diff --git a/std/algorithm/searching.d b/std/algorithm/searching.d index d26a20150..8aba54852 100644 --- a/std/algorithm/searching.d +++ b/std/algorithm/searching.d @@ -2748,6 +2748,21 @@ if (isForwardRange!R1 && isForwardRange!R2 assert(findSkip(s, "def") && s.empty); } +@safe unittest // issue 19020 +{ + 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)) @@ -2821,7 +2836,7 @@ Returns: A sub-type of `Tuple!()` of the split portions of `haystack` (see above for details). This sub-type of `Tuple!()` has `opCast` defined for `bool`. This `opCast` returns `true` when the separating `needle` was found -(`!result[1].empty`) and `false` otherwise. +and `false` otherwise. See_Also: $(LREF find) */ @@ -2881,6 +2896,10 @@ if (isForwardRange!R1 && isForwardRange!R2) pos2 = ++pos1; } } + if (!n.empty) // incomplete match at the end of haystack + { + pos1 = pos2; + } return Result!(typeof(takeExactly(original, pos1)), typeof(h))(takeExactly(original, pos1), takeExactly(haystack, pos2 - pos1), @@ -2906,7 +2925,7 @@ if (isForwardRange!R1 && isForwardRange!R2) Tuple!(S1, S2) asTuple; bool opCast(T : bool)() { - return !asTuple[0].empty; + return !asTuple[1].empty; } alias asTuple this; } @@ -2926,24 +2945,30 @@ if (isForwardRange!R1 && isForwardRange!R2) auto original = haystack.save; auto h = haystack.save; auto n = needle.save; - size_t pos; + 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; - ++pos; + pos2 = ++pos1; } } - return Result!(typeof(takeExactly(original, pos)), - typeof(haystack))(takeExactly(original, pos), + if (!n.empty) // incomplete match at the end of haystack + { + pos1 = pos2; + haystack = h; + } + return Result!(typeof(takeExactly(original, pos1)), + typeof(haystack))(takeExactly(original, pos1), haystack); } } @@ -2966,7 +2991,7 @@ if (isForwardRange!R1 && isForwardRange!R2) Tuple!(S1, S2) asTuple; bool opCast(T : bool)() { - return !asTuple[1].empty; + return !asTuple[0].empty; } alias asTuple this; } @@ -3077,7 +3102,7 @@ if (isForwardRange!R1 && isForwardRange!R2) assert(r[2] == a[3 .. $]); auto r1 = findSplitBefore(a, [9, 1]); - assert(r1); + assert(!r1); assert(r1[0] == a); assert(r1[1].empty); r1 = findSplitBefore(a, [3, 4]); @@ -3086,7 +3111,7 @@ if (isForwardRange!R1 && isForwardRange!R2) assert(r1[1] == a[2 .. $]); auto r2 = findSplitAfter(a, [9, 1]); - assert(r2); + assert(!r2); assert(r2[0].empty); assert(r2[1] == a); r2 = findSplitAfter(a, [3, 4]); @@ -3112,24 +3137,37 @@ if (isForwardRange!R1 && isForwardRange!R2) 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 r1 = findSplitBefore(fwd, [9, 1]); - assert(r1); + assert(!r1); assert(equal(r1[0], a)); assert(r1[1].empty); r1 = findSplitBefore(fwd, [3, 4]); assert(r1); assert(equal(r1[0], a[0 .. 2])); assert(equal(r1[1], a[2 .. $])); + 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); assert(r2[0].empty); assert(equal(r2[1], a)); r2 = findSplitAfter(fwd, [3, 4]); assert(r2); assert(equal(r2[0], a[0 .. 4])); assert(equal(r2[1], a[4 .. $])); + r2 = findSplitAfter(fwd, [8, 9]); + assert(!r2); + assert(r2[0].empty); + assert(equal(r2[1], a)); } @safe pure nothrow @nogc unittest diff --git a/std/experimental/allocator/common.d b/std/experimental/allocator/common.d index 70ab5448a..199ce1cb7 100644 --- a/std/experimental/allocator/common.d +++ b/std/experimental/allocator/common.d @@ -759,6 +759,63 @@ version(unittest) } } +/* Basically the `is` operator, but handles static arrays for which `is` is +deprecated. For use in CTFE. */ +private bool bitwiseIdentical(T)(T a, T b) +{ + static if (isStaticArray!T) + { + foreach (i, e; a) + { + if (!.bitwiseIdentical(e, b[i])) return false; + } + return true; + } + else return a is b; +} + +@nogc nothrow pure @safe unittest +{ + import std.meta : AliasSeq; + + static struct NeverEq + { + int x; + bool opEquals(NeverEq other) const { return false; } + } + + static struct AlwaysEq + { + int x; + bool opEquals(AlwaysEq other) const { return true; } + } + + static foreach (x; AliasSeq!(-1, 0, 1, 2, "foo", NeverEq(0))) + { + assert(bitwiseIdentical(x, x)); + static assert(bitwiseIdentical(x, x)); + } + + static foreach (pair; AliasSeq!([0, 1], [-1, 1], [2, 3], ["foo", "bar"], + [AlwaysEq(0), AlwaysEq(1)])) + { + assert(!bitwiseIdentical(pair[0], pair[1])); + static assert(!bitwiseIdentical(pair[0], pair[1])); + } + + { + int[2][2][2] x = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + int[2][2][2] y = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + assert(bitwiseIdentical(x, y)); + } + + { + enum int[2][2][2] x = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + enum int[2][2][2] y = [[[1, 2], [3, 4]], [[5, 6], [7, 8]]]; + static assert(bitwiseIdentical(x, y)); + } +} + /+ Can the representation be determined at compile time to consist of nothing but zero bits? Padding between a struct's fields is not considered. @@ -774,15 +831,17 @@ private template isAllZeroBits(T, T value) else static if (isStaticArray!(typeof(value))) enum isAllZeroBits = () { - bool b = true; - // Use index so this works when T.length is 0. - static foreach (i; 0 .. T.length) - { - b &= isAllZeroBits!(typeof(value[i]), value[i]); - if (b == false) return b; - } - return b; + static if (value.length == 0) return true; + else static if (.isAllZeroBits!(typeof(value[0]), value[0])) + { + foreach (e; value[1 .. $]) + { + if (!bitwiseIdentical(e, value[0])) return false; + } + return true; + } + else return false; }(); else static if (is(typeof(value) == struct) || is(typeof(value) == union)) enum isAllZeroBits = () @@ -822,6 +881,53 @@ private template isAllZeroBits(T, T value) static assert(isAllZeroBits!(Object, null)); } +@nogc nothrow pure @safe unittest // large static arrays +{ + import std.meta : Repeat; + enum n = 16 * 1024; + + static assert(isAllZeroBits!(ubyte[n], (ubyte[n]).init)); + static assert(!isAllZeroBits!(ubyte[n], [Repeat!(n, 1)])); + static assert(!isAllZeroBits!(ubyte[n], [1, Repeat!(n - 1, 0)])); + static assert(!isAllZeroBits!(ubyte[n], [Repeat!(n - 1, 0), 1])); + + static assert(!isAllZeroBits!(char[n], (char[n]).init)); + static assert(isAllZeroBits!(char[n], [Repeat!(n, 0)])); +} + +@nogc nothrow pure @safe unittest // nested static arrays +{ + static assert(isAllZeroBits!(int[2][2], [[0, 0], [0, 0]])); + static assert(!isAllZeroBits!(int[2][2], [[0, 0], [1, 0]])); +} + +@nogc nothrow pure @safe unittest // funky opEquals +{ + static struct AlwaysEq + { + int x; + bool opEquals(AlwaysEq other) const { return true; } + } + static assert(AlwaysEq(0) == AlwaysEq(0)); + static assert(AlwaysEq(0) == AlwaysEq(1)); + static assert(isAllZeroBits!(AlwaysEq, AlwaysEq(0))); + static assert(!isAllZeroBits!(AlwaysEq, AlwaysEq(1))); + static assert(isAllZeroBits!(AlwaysEq[1], [AlwaysEq(0)])); + static assert(!isAllZeroBits!(AlwaysEq[2], [AlwaysEq(0), AlwaysEq(1)])); + + static struct NeverEq + { + int x; + bool opEquals(NeverEq other) const { return false; } + } + static assert(NeverEq(0) != NeverEq(1)); + static assert(NeverEq(0) != NeverEq(0)); + static assert(isAllZeroBits!(NeverEq, NeverEq(0))); + static assert(!isAllZeroBits!(NeverEq, NeverEq(1))); + static assert(isAllZeroBits!(NeverEq[1], [NeverEq(0)])); + static assert(!isAllZeroBits!(NeverEq[2], [NeverEq(0), NeverEq(1)])); +} + /+ Is the representation of T.init known at compile time to consist of nothing but zero bits? Padding between a struct's fields is not considered. diff --git a/std/experimental/allocator/package.d b/std/experimental/allocator/package.d index 58a14d76b..056ff15b2 100644 --- a/std/experimental/allocator/package.d +++ b/std/experimental/allocator/package.d @@ -1410,6 +1410,22 @@ nothrow @safe @nogc unittest "Don't allow zero-ctor-args `make` for structs with `@disable this();`"); } +@safe unittest // issue 18937 +{ + 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) {