mirror of
https://github.com/dlang/phobos.git
synced 2025-04-28 06:00:35 +03:00
Merge pull request #2631 from nordlow/inout-array-range
Fix constness of array.d
This commit is contained in:
commit
224238a854
1 changed files with 244 additions and 138 deletions
|
@ -22,9 +22,161 @@ module std.container.array;
|
||||||
|
|
||||||
import std.range.primitives;
|
import std.range.primitives;
|
||||||
import std.traits;
|
import std.traits;
|
||||||
|
import core.exception : RangeError;
|
||||||
|
import std.algorithm : move;
|
||||||
|
|
||||||
public import std.container.util;
|
public import std.container.util;
|
||||||
|
|
||||||
|
private struct RangeT(A)
|
||||||
|
{
|
||||||
|
/* Workaround for Issue 13629 at https://issues.dlang.org/show_bug.cgi?id=13629
|
||||||
|
See also: http://forum.dlang.org/thread/vbmwhzvawhnkoxrhbnyb@forum.dlang.org?page=1
|
||||||
|
*/
|
||||||
|
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
|
||||||
|
{
|
||||||
|
version (assert) if (empty) throw new RangeError();
|
||||||
|
return _outer[_a];
|
||||||
|
}
|
||||||
|
@property ref inout(E) back() inout
|
||||||
|
{
|
||||||
|
version (assert) if (empty) throw new RangeError();
|
||||||
|
return _outer[_b - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
void popFront() @safe pure nothrow
|
||||||
|
{
|
||||||
|
version (assert) if (empty) throw new RangeError();
|
||||||
|
++_a;
|
||||||
|
}
|
||||||
|
|
||||||
|
void popBack() @safe pure nothrow
|
||||||
|
{
|
||||||
|
version (assert) if (empty) throw new RangeError();
|
||||||
|
--_b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (isMutable!A)
|
||||||
|
{
|
||||||
|
E moveFront()
|
||||||
|
{
|
||||||
|
version (assert) if (empty || _a >= _outer.length) throw new RangeError();
|
||||||
|
return move(_outer._data._payload[_a]);
|
||||||
|
}
|
||||||
|
|
||||||
|
E moveBack()
|
||||||
|
{
|
||||||
|
version (assert) if (empty || _b > _outer.length) throw new RangeError();
|
||||||
|
return move(_outer._data._payload[_b - 1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
E moveAt(size_t i)
|
||||||
|
{
|
||||||
|
version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError();
|
||||||
|
return move(_outer._data._payload[_a + i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ref inout(E) opIndex(size_t i) inout
|
||||||
|
{
|
||||||
|
version (assert) if (_a + i >= _b) throw new RangeError();
|
||||||
|
return _outer[_a + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
RangeT opSlice()
|
||||||
|
{
|
||||||
|
return typeof(return)(_outer, _a, _b);
|
||||||
|
}
|
||||||
|
|
||||||
|
RangeT opSlice(size_t i, size_t j)
|
||||||
|
{
|
||||||
|
version (assert) if (i > j || _a + j > _b) throw new RangeError();
|
||||||
|
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
|
||||||
|
{
|
||||||
|
version (assert) if (i > j || _a + j > _b) throw new RangeError();
|
||||||
|
return typeof(return)(_outer, _a + i, _a + j);
|
||||||
|
}
|
||||||
|
|
||||||
|
static if (isMutable!A)
|
||||||
|
{
|
||||||
|
void opSliceAssign(E value)
|
||||||
|
{
|
||||||
|
version (assert) if (_b > _outer.length) throw new RangeError();
|
||||||
|
_outer[_a .. _b] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opSliceAssign(E value, size_t i, size_t j)
|
||||||
|
{
|
||||||
|
version (assert) if (_a + j > _b) throw new RangeError();
|
||||||
|
_outer[_a + i .. _a + j] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
void opSliceUnary(string op)()
|
||||||
|
if (op == "++" || op == "--")
|
||||||
|
{
|
||||||
|
version (assert) if (_b > _outer.length) throw new RangeError();
|
||||||
|
mixin(op~"_outer[_a .. _b];");
|
||||||
|
}
|
||||||
|
|
||||||
|
void opSliceUnary(string op)(size_t i, size_t j)
|
||||||
|
if (op == "++" || op == "--")
|
||||||
|
{
|
||||||
|
version (assert) if (_a + j > _b) throw new RangeError();
|
||||||
|
mixin(op~"_outer[_a + i .. _a + j];");
|
||||||
|
}
|
||||||
|
|
||||||
|
void opSliceOpAssign(string op)(E value)
|
||||||
|
{
|
||||||
|
version (assert) if (_b > _outer.length) throw new RangeError();
|
||||||
|
mixin("_outer[_a .. _b] "~op~"= value;");
|
||||||
|
}
|
||||||
|
|
||||||
|
void opSliceOpAssign(string op)(E value, size_t i, size_t j)
|
||||||
|
{
|
||||||
|
version (assert) if (_a + j > _b) throw new RangeError();
|
||||||
|
mixin("_outer[_a + i .. _a + j] "~op~"= value;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Array type with deterministic control of memory. The memory allocated
|
Array type with deterministic control of memory. The memory allocated
|
||||||
|
@ -50,9 +202,8 @@ if (!is(Unqual!T == bool))
|
||||||
import core.stdc.string;
|
import core.stdc.string;
|
||||||
|
|
||||||
import core.memory;
|
import core.memory;
|
||||||
import core.exception : RangeError;
|
|
||||||
|
|
||||||
import std.algorithm : initializeAll, move, copy;
|
import std.algorithm : initializeAll, copy;
|
||||||
import std.exception : enforce;
|
import std.exception : enforce;
|
||||||
import std.typecons : RefCounted, RefCountedAutoInitialize;
|
import std.typecons : RefCounted, RefCountedAutoInitialize;
|
||||||
|
|
||||||
|
@ -259,133 +410,14 @@ Comparison for equality.
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Defines the container's primary range, which is a random-access range.
|
Defines the container's primary range, which is a random-access range.
|
||||||
*/
|
|
||||||
static struct Range
|
|
||||||
{
|
|
||||||
private Array _outer;
|
|
||||||
private size_t _a, _b;
|
|
||||||
|
|
||||||
private this(ref Array data, size_t a, size_t b)
|
ConstRange is a variant with const elements.
|
||||||
{
|
ImmutableRange is a variant with immutable elements.
|
||||||
_outer = data;
|
*/
|
||||||
_a = a;
|
alias Range = RangeT!Array;
|
||||||
_b = b;
|
alias ConstRange = RangeT!(const Array); /// ditto
|
||||||
}
|
alias ImmutableRange = RangeT!(immutable Array); /// ditto
|
||||||
|
|
||||||
@property Range 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 T front()
|
|
||||||
{
|
|
||||||
version (assert) if (empty) throw new RangeError();
|
|
||||||
return _outer[_a];
|
|
||||||
}
|
|
||||||
|
|
||||||
@property ref T back()
|
|
||||||
{
|
|
||||||
version (assert) if (empty) throw new RangeError();
|
|
||||||
return _outer[_b - 1];
|
|
||||||
}
|
|
||||||
|
|
||||||
void popFront() @safe pure nothrow
|
|
||||||
{
|
|
||||||
version (assert) if (empty) throw new RangeError();
|
|
||||||
++_a;
|
|
||||||
}
|
|
||||||
|
|
||||||
void popBack() @safe pure nothrow
|
|
||||||
{
|
|
||||||
version (assert) if (empty) throw new RangeError();
|
|
||||||
--_b;
|
|
||||||
}
|
|
||||||
|
|
||||||
T moveFront()
|
|
||||||
{
|
|
||||||
version (assert) if (empty || _a >= _outer.length) throw new RangeError();
|
|
||||||
return move(_outer._data._payload[_a]);
|
|
||||||
}
|
|
||||||
|
|
||||||
T moveBack()
|
|
||||||
{
|
|
||||||
version (assert) if (empty || _b > _outer.length) throw new RangeError();
|
|
||||||
return move(_outer._data._payload[_b - 1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
T moveAt(size_t i)
|
|
||||||
{
|
|
||||||
version (assert) if (_a + i >= _b || _a + i >= _outer.length) throw new RangeError();
|
|
||||||
return move(_outer._data._payload[_a + i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
ref T opIndex(size_t i)
|
|
||||||
{
|
|
||||||
version (assert) if (_a + i >= _b) throw new RangeError();
|
|
||||||
return _outer[_a + i];
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(this) opSlice()
|
|
||||||
{
|
|
||||||
return typeof(this)(_outer, _a, _b);
|
|
||||||
}
|
|
||||||
|
|
||||||
typeof(this) opSlice(size_t i, size_t j)
|
|
||||||
{
|
|
||||||
version (assert) if (i > j || _a + j > _b) throw new RangeError();
|
|
||||||
return typeof(this)(_outer, _a + i, _a + j);
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceAssign(T value)
|
|
||||||
{
|
|
||||||
version (assert) if (_b > _outer.length) throw new RangeError();
|
|
||||||
_outer[_a .. _b] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceAssign(T value, size_t i, size_t j)
|
|
||||||
{
|
|
||||||
version (assert) if (_a + j > _b) throw new RangeError();
|
|
||||||
_outer[_a + i .. _a + j] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceUnary(string op)()
|
|
||||||
if(op == "++" || op == "--")
|
|
||||||
{
|
|
||||||
version (assert) if (_b > _outer.length) throw new RangeError();
|
|
||||||
mixin(op~"_outer[_a .. _b];");
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceUnary(string op)(size_t i, size_t j)
|
|
||||||
if(op == "++" || op == "--")
|
|
||||||
{
|
|
||||||
version (assert) if (_a + j > _b) throw new RangeError();
|
|
||||||
mixin(op~"_outer[_a + i .. _a + j];");
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceOpAssign(string op)(T value)
|
|
||||||
{
|
|
||||||
version (assert) if (_b > _outer.length) throw new RangeError();
|
|
||||||
mixin("_outer[_a .. _b] "~op~"= value;");
|
|
||||||
}
|
|
||||||
|
|
||||||
void opSliceOpAssign(string op)(T value, size_t i, size_t j)
|
|
||||||
{
|
|
||||||
version (assert) if (_a + j > _b) throw new RangeError();
|
|
||||||
mixin("_outer[_a + i .. _a + j] "~op~"= value;");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Duplicates the container. The elements themselves are not transitively
|
Duplicates the container. The elements themselves are not transitively
|
||||||
|
@ -472,7 +504,15 @@ Complexity: $(BIGOH 1)
|
||||||
*/
|
*/
|
||||||
Range opSlice()
|
Range opSlice()
|
||||||
{
|
{
|
||||||
return Range(this, 0, length);
|
return typeof(return)(this, 0, length);
|
||||||
|
}
|
||||||
|
ConstRange opSlice() const
|
||||||
|
{
|
||||||
|
return typeof(return)(this, 0, length);
|
||||||
|
}
|
||||||
|
ImmutableRange opSlice() immutable
|
||||||
|
{
|
||||||
|
return typeof(return)(this, 0, length);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -482,11 +522,21 @@ index $(D a) up to (excluding) index $(D b).
|
||||||
Precondition: $(D a <= b && b <= length)
|
Precondition: $(D a <= b && b <= length)
|
||||||
|
|
||||||
Complexity: $(BIGOH 1)
|
Complexity: $(BIGOH 1)
|
||||||
*/
|
*/
|
||||||
Range opSlice(size_t i, size_t j)
|
Range opSlice(size_t i, size_t j)
|
||||||
{
|
{
|
||||||
version (assert) if (i > j || j > length) throw new RangeError();
|
version (assert) if (i > j || j > length) throw new RangeError();
|
||||||
return Range(this, i, j);
|
return typeof(return)(this, i, j);
|
||||||
|
}
|
||||||
|
ConstRange opSlice(size_t i, size_t j) const
|
||||||
|
{
|
||||||
|
version (assert) if (i > j || j > length) throw new RangeError();
|
||||||
|
return typeof(return)(this, i, j);
|
||||||
|
}
|
||||||
|
ImmutableRange opSlice(size_t i, size_t j) immutable
|
||||||
|
{
|
||||||
|
version (assert) if (i > j || j > length) throw new RangeError();
|
||||||
|
return typeof(return)(this, i, j);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -496,14 +546,14 @@ Precondition: $(D !empty)
|
||||||
|
|
||||||
Complexity: $(BIGOH 1)
|
Complexity: $(BIGOH 1)
|
||||||
*/
|
*/
|
||||||
@property ref T front()
|
@property ref inout(T) front() inout
|
||||||
{
|
{
|
||||||
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
||||||
return _data._payload[0];
|
return _data._payload[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
@property ref T back()
|
@property ref inout(T) back() inout
|
||||||
{
|
{
|
||||||
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
||||||
return _data._payload[$ - 1];
|
return _data._payload[$ - 1];
|
||||||
|
@ -516,7 +566,7 @@ Precondition: $(D i < length)
|
||||||
|
|
||||||
Complexity: $(BIGOH 1)
|
Complexity: $(BIGOH 1)
|
||||||
*/
|
*/
|
||||||
ref T opIndex(size_t i)
|
ref inout(T) opIndex(size_t i) inout
|
||||||
{
|
{
|
||||||
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
||||||
return _data._payload[i];
|
return _data._payload[i];
|
||||||
|
@ -546,15 +596,15 @@ Complexity: $(BIGOH slice.length)
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
void opSliceUnary(string op)()
|
void opSliceUnary(string op)()
|
||||||
if(op == "++" || op == "--")
|
if (op == "++" || op == "--")
|
||||||
{
|
{
|
||||||
if(!_data.refCountedStore.isInitialized) return;
|
if (!_data.refCountedStore.isInitialized) return;
|
||||||
mixin(op~"_data._payload[];");
|
mixin(op~"_data._payload[];");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ditto
|
/// ditto
|
||||||
void opSliceUnary(string op)(size_t i, size_t j)
|
void opSliceUnary(string op)(size_t i, size_t j)
|
||||||
if(op == "++" || op == "--")
|
if (op == "++" || op == "--")
|
||||||
{
|
{
|
||||||
auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
|
auto slice = _data.refCountedStore.isInitialized ? _data._payload : T[].init;
|
||||||
mixin(op~"slice[i .. j];");
|
mixin(op~"slice[i .. j];");
|
||||||
|
@ -563,7 +613,7 @@ Complexity: $(BIGOH slice.length)
|
||||||
/// ditto
|
/// ditto
|
||||||
void opSliceOpAssign(string op)(T value)
|
void opSliceOpAssign(string op)(T value)
|
||||||
{
|
{
|
||||||
if(!_data.refCountedStore.isInitialized) return;
|
if (!_data.refCountedStore.isInitialized) return;
|
||||||
mixin("_data._payload[] "~op~"= value;");
|
mixin("_data._payload[] "~op~"= value;");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -898,6 +948,27 @@ unittest
|
||||||
assert(a.length == 3);
|
assert(a.length == 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unittest
|
unittest
|
||||||
{
|
{
|
||||||
// REG https://issues.dlang.org/show_bug.cgi?id=13621
|
// REG https://issues.dlang.org/show_bug.cgi?id=13621
|
||||||
|
@ -1242,6 +1313,41 @@ unittest //6998-2
|
||||||
assert(c.i == 42); //fails
|
assert(c.i == 42); //fails
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unittest
|
||||||
|
{
|
||||||
|
static assert(is(Array!int.Range));
|
||||||
|
static assert(is(Array!int.ConstRange));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// Array!bool
|
// Array!bool
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue