253 lines
5.8 KiB
D
253 lines
5.8 KiB
D
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/**
|
|
* This module defines primitives for working with ranges.
|
|
*
|
|
* Copyright: Eugen Wissner 2017-2026.
|
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
|
* Mozilla Public License, v. 2.0).
|
|
*/
|
|
module tanya.range.primitive;
|
|
|
|
import std.algorithm.comparison;
|
|
import std.range : isInfinite, hasSlicing, hasLvalueElements, isInputRange, isBidirectionalRange;
|
|
import std.traits;
|
|
import tanya.memory.lifetime;
|
|
import tanya.meta;
|
|
import tanya.range.array;
|
|
|
|
/**
|
|
* Returns the element type of the range $(D_PARAM R).
|
|
*
|
|
* Element type is the return type of such primitives like
|
|
* $(D_INLINECODE R.front) and (D_INLINECODE R.back) or the array base type.
|
|
* If $(D_PARAM R) is not a range, its element type is $(D_KEYWORD void).
|
|
*
|
|
* If $(D_PARAM R) is a string, $(D_PSYMBOL ElementType) doesn't distinguish
|
|
* between narrow and wide strings, it just returns the base type of the
|
|
* underlying array ($(D_KEYWORD char), $(D_KEYWORD wchar) or
|
|
* $(D_KEYWORD dchar)).
|
|
*
|
|
* Params:
|
|
* R = Range type.
|
|
*
|
|
* Returns: Element type of the range $(D_PARAM R).
|
|
*/
|
|
template ElementType(R)
|
|
{
|
|
static if (is(R U : U[]))
|
|
{
|
|
alias ElementType = U;
|
|
}
|
|
else static if (isInputRange!R)
|
|
{
|
|
alias ElementType = ReturnType!((R r) => r.front());
|
|
}
|
|
else
|
|
{
|
|
alias ElementType = void;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detects whether $(D_PARAM R) has a length property.
|
|
*
|
|
* $(D_PARAM R) does not have to be a range to support the length.
|
|
*
|
|
* Length mustn't be a $(D_KEYWORD @property) or a function, it can be a member
|
|
* variable or $(D_KEYWORD enum). But its type (or the type returned by the
|
|
* appropriate function) should be $(D_KEYWORD size_t), otherwise
|
|
* $(D_PSYMBOL hasLength) is $(D_KEYWORD false).
|
|
*
|
|
* All dynamic arrays except $(D_KEYWORD void)-arrays have length.
|
|
*
|
|
* Params:
|
|
* R = A type.
|
|
*
|
|
* Returns: $(D_KEYWORD true) if $(D_PARAM R) has a length property,
|
|
* $(D_KEYWORD false) otherwise.
|
|
*
|
|
* See_Also: $(D_PSYMBOL isInfinite).
|
|
*/
|
|
enum bool hasLength(R) = is(ReturnType!((R r) => r.length) == size_t);
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static assert(hasLength!(char[]));
|
|
static assert(hasLength!(int[]));
|
|
static assert(hasLength!(const(int)[]));
|
|
|
|
struct A
|
|
{
|
|
enum size_t length = 1;
|
|
}
|
|
static assert(hasLength!(A));
|
|
|
|
struct B
|
|
{
|
|
@property size_t length() const @nogc nothrow pure @safe
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
static assert(hasLength!(B));
|
|
|
|
struct C
|
|
{
|
|
@property const(size_t) length() const @nogc nothrow pure @safe
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
static assert(!hasLength!C);
|
|
}
|
|
|
|
private template isDynamicArrayRange(R)
|
|
{
|
|
static if (is(R E : E[]))
|
|
{
|
|
enum bool isDynamicArrayRange = !is(E == void);
|
|
}
|
|
else
|
|
{
|
|
enum bool isDynamicArrayRange = false;
|
|
}
|
|
}
|
|
|
|
private struct Primitive(Candidate, string primitive)
|
|
{
|
|
auto ref returnType(ref Candidate candidate)
|
|
{
|
|
mixin("return candidate." ~ primitive ~ ";");
|
|
}
|
|
|
|
alias ReturnType = .ReturnType!returnType;
|
|
static assert(!is(ReturnType == void));
|
|
|
|
enum uint attributes = functionAttributes!returnType
|
|
& FunctionAttribute.ref_;
|
|
|
|
bool opEquals(That)(That) const
|
|
{
|
|
return is(ReturnType == That.ReturnType)
|
|
&& attributes == That.attributes;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the first element and advances the range.
|
|
*
|
|
* If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopFront)
|
|
* returns by reference, otherwise the returned element is copied.
|
|
*
|
|
* Params:
|
|
* R = Input range type.
|
|
* range = Input range.
|
|
*
|
|
* Returns: Front range element.
|
|
*
|
|
* See_Also: $(D_PSYMBOL getAndPopBack).
|
|
*/
|
|
ElementType!R getAndPopFront(R)(ref R range)
|
|
if (isInputRange!R)
|
|
in
|
|
{
|
|
assert(!range.empty);
|
|
}
|
|
do
|
|
{
|
|
static if (hasLvalueElements!R)
|
|
{
|
|
if (false)
|
|
{
|
|
// This code is removed by the compiler but ensures that
|
|
// this function isn't @safe if range.front isn't @safe.
|
|
auto _ = range.front();
|
|
}
|
|
auto el = (() @trusted => &range.front())();
|
|
}
|
|
else
|
|
{
|
|
auto el = range.front;
|
|
}
|
|
range.popFront();
|
|
static if (hasLvalueElements!R)
|
|
{
|
|
return *el;
|
|
}
|
|
else
|
|
{
|
|
return el;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
int[3] array = [1, 2, 3];
|
|
auto slice = array[];
|
|
|
|
assert(getAndPopFront(slice) == 1);
|
|
assert(slice.length == 2);
|
|
}
|
|
|
|
/**
|
|
* Returns the last element and removes it from the range.
|
|
*
|
|
* If $(D_PARAM range) has lvalue elements, then $(D_PSYMBOL getAndPopBack)
|
|
* returns by reference, otherwise the returned element is copied.
|
|
*
|
|
* Params:
|
|
* R = Bidirectional range type.
|
|
* range = Bidirectional range.
|
|
*
|
|
* Returns: Last range element.
|
|
*
|
|
* See_Also: $(D_PSYMBOL getAndPopFront).
|
|
*/
|
|
auto ref getAndPopBack(R)(ref R range)
|
|
if (isBidirectionalRange!R)
|
|
in
|
|
{
|
|
assert(!range.empty);
|
|
}
|
|
do
|
|
{
|
|
static if (hasLvalueElements!R)
|
|
{
|
|
if (false)
|
|
{
|
|
// This code is removed by the compiler but ensures that
|
|
// this function isn't @safe if range.back isn't @safe.
|
|
auto _ = range.back();
|
|
}
|
|
auto el = (() @trusted => &range.back())();
|
|
}
|
|
else
|
|
{
|
|
auto el = range.back;
|
|
}
|
|
range.popBack();
|
|
static if (hasLvalueElements!R)
|
|
{
|
|
return *el;
|
|
}
|
|
else
|
|
{
|
|
return el;
|
|
}
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @trusted unittest
|
|
{
|
|
int[3] array = [1, 2, 3];
|
|
auto slice = array[];
|
|
|
|
assert(getAndPopBack(slice) == 3);
|
|
assert(slice.length == 2);
|
|
}
|