mirror of
https://github.com/dlang/phobos.git
synced 2025-04-27 05:30:33 +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.traits;
|
||||
import core.exception : RangeError;
|
||||
import std.algorithm : move;
|
||||
|
||||
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
|
||||
|
@ -50,9 +202,8 @@ if (!is(Unqual!T == bool))
|
|||
import core.stdc.string;
|
||||
|
||||
import core.memory;
|
||||
import core.exception : RangeError;
|
||||
|
||||
import std.algorithm : initializeAll, move, copy;
|
||||
import std.algorithm : initializeAll, copy;
|
||||
import std.exception : enforce;
|
||||
import std.typecons : RefCounted, RefCountedAutoInitialize;
|
||||
|
||||
|
@ -259,133 +410,14 @@ Comparison for equality.
|
|||
}
|
||||
|
||||
/**
|
||||
Defines the container's primary range, which is a random-access range.
|
||||
*/
|
||||
static struct Range
|
||||
{
|
||||
private Array _outer;
|
||||
private size_t _a, _b;
|
||||
Defines the container's primary range, which is a random-access range.
|
||||
|
||||
private this(ref Array data, size_t a, size_t b)
|
||||
{
|
||||
_outer = data;
|
||||
_a = a;
|
||||
_b = b;
|
||||
}
|
||||
|
||||
@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;");
|
||||
}
|
||||
}
|
||||
ConstRange is a variant with const elements.
|
||||
ImmutableRange is a variant with immutable elements.
|
||||
*/
|
||||
alias Range = RangeT!Array;
|
||||
alias ConstRange = RangeT!(const Array); /// ditto
|
||||
alias ImmutableRange = RangeT!(immutable Array); /// ditto
|
||||
|
||||
/**
|
||||
Duplicates the container. The elements themselves are not transitively
|
||||
|
@ -472,7 +504,15 @@ Complexity: $(BIGOH 1)
|
|||
*/
|
||||
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)
|
||||
|
||||
Complexity: $(BIGOH 1)
|
||||
*/
|
||||
*/
|
||||
Range opSlice(size_t i, size_t j)
|
||||
{
|
||||
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)
|
||||
*/
|
||||
@property ref T front()
|
||||
@property ref inout(T) front() inout
|
||||
{
|
||||
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
||||
return _data._payload[0];
|
||||
}
|
||||
|
||||
/// ditto
|
||||
@property ref T back()
|
||||
@property ref inout(T) back() inout
|
||||
{
|
||||
version (assert) if (!_data.refCountedStore.isInitialized) throw new RangeError();
|
||||
return _data._payload[$ - 1];
|
||||
|
@ -516,7 +566,7 @@ Precondition: $(D i < length)
|
|||
|
||||
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();
|
||||
return _data._payload[i];
|
||||
|
@ -546,15 +596,15 @@ Complexity: $(BIGOH slice.length)
|
|||
|
||||
/// ditto
|
||||
void opSliceUnary(string op)()
|
||||
if(op == "++" || op == "--")
|
||||
if (op == "++" || op == "--")
|
||||
{
|
||||
if(!_data.refCountedStore.isInitialized) return;
|
||||
if (!_data.refCountedStore.isInitialized) return;
|
||||
mixin(op~"_data._payload[];");
|
||||
}
|
||||
|
||||
/// ditto
|
||||
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;
|
||||
mixin(op~"slice[i .. j];");
|
||||
|
@ -563,7 +613,7 @@ Complexity: $(BIGOH slice.length)
|
|||
/// ditto
|
||||
void opSliceOpAssign(string op)(T value)
|
||||
{
|
||||
if(!_data.refCountedStore.isInitialized) return;
|
||||
if (!_data.refCountedStore.isInitialized) return;
|
||||
mixin("_data._payload[] "~op~"= value;");
|
||||
}
|
||||
|
||||
|
@ -898,6 +948,27 @@ unittest
|
|||
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
|
||||
{
|
||||
// REG https://issues.dlang.org/show_bug.cgi?id=13621
|
||||
|
@ -1242,6 +1313,41 @@ unittest //6998-2
|
|||
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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue