209 lines
5.2 KiB
D
209 lines
5.2 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/. */
|
|
|
|
/**
|
|
* Range adapters transform some data structures into ranges.
|
|
*
|
|
* Copyright: Eugene Wissner 2018-2020.
|
|
* License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/,
|
|
* Mozilla Public License, v. 2.0).
|
|
* Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner)
|
|
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d,
|
|
* tanya/range/adapter.d)
|
|
*/
|
|
module tanya.range.adapter;
|
|
|
|
import tanya.algorithm.mutation;
|
|
import tanya.memory.lifetime;
|
|
import tanya.meta.trait;
|
|
import tanya.range;
|
|
|
|
private mixin template InserterCtor()
|
|
{
|
|
private Container* container;
|
|
|
|
private this(return scope ref Container container) @trusted
|
|
{
|
|
this.container = &container;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* If $(D_PARAM container) is a container with `insertBack`-support,
|
|
* $(D_PSYMBOL backInserter) returns an output range that puts the elements
|
|
* into the container with `insertBack`.
|
|
*
|
|
* The resulting output range supports all types `insertBack` supports.
|
|
*
|
|
* The range keeps a reference to the container passed to it, it doesn't use
|
|
* any other storage. So there is no method to get the written data out of the
|
|
* range - the container passed to $(D_PSYMBOL backInserter) contains that data
|
|
* and can be used directly after all operations on the output range are
|
|
* completed. It also means that the result range is not allowed to outlive its
|
|
* container.
|
|
*
|
|
* Params:
|
|
* Container = Container type.
|
|
* container = Container used as an output range.
|
|
*
|
|
* Returns: `insertBack`-based output range.
|
|
*/
|
|
auto backInserter(Container)(return scope ref Container container)
|
|
if (hasMember!(Container, "insertBack"))
|
|
{
|
|
static struct Inserter
|
|
{
|
|
void opCall(T)(auto ref T data)
|
|
{
|
|
this.container.insertBack(forward!data);
|
|
}
|
|
|
|
mixin InserterCtor;
|
|
}
|
|
return Inserter(container);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static struct Container
|
|
{
|
|
int element;
|
|
|
|
void insertBack(int element)
|
|
{
|
|
this.element = element;
|
|
}
|
|
}
|
|
Container container;
|
|
backInserter(container)(5);
|
|
|
|
assert(container.element == 5);
|
|
}
|
|
|
|
/**
|
|
* If $(D_PARAM container) is a container with `insertFront`-support,
|
|
* $(D_PSYMBOL frontInserter) returns an output range that puts the elements
|
|
* into the container with `insertFront`.
|
|
*
|
|
* The resulting output range supports all types `insertFront` supports.
|
|
*
|
|
* The range keeps a reference to the container passed to it, it doesn't use
|
|
* any other storage. So there is no method to get the written data out of the
|
|
* range - the container passed to $(D_PSYMBOL frontInserter) contains that data
|
|
* and can be used directly after all operations on the output range are
|
|
* completed. It also means that the result range is not allowed to outlive its
|
|
* container.
|
|
*
|
|
* Params:
|
|
* Container = Container type.
|
|
* container = Container used as an output range.
|
|
*
|
|
* Returns: `insertFront`-based output range.
|
|
*/
|
|
auto frontInserter(Container)(return scope ref Container container)
|
|
if (hasMember!(Container, "insertFront"))
|
|
{
|
|
static struct Inserter
|
|
{
|
|
void opCall(T)(auto ref T data)
|
|
{
|
|
this.container.insertFront(forward!data);
|
|
}
|
|
|
|
mixin InserterCtor;
|
|
}
|
|
return Inserter(container);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
static struct Container
|
|
{
|
|
int element;
|
|
|
|
void insertFront(int element)
|
|
{
|
|
this.element = element;
|
|
}
|
|
}
|
|
Container container;
|
|
frontInserter(container)(5);
|
|
|
|
assert(container.element == 5);
|
|
}
|
|
|
|
/**
|
|
* $(D_PSYMBOL arrayInserter) makes an output range out of an array.
|
|
*
|
|
* The returned output range accepts single values as well as input ranges that
|
|
* can be copied into the target array.
|
|
*
|
|
* Params:
|
|
* Array = Array type.
|
|
* array = Array.
|
|
*
|
|
* Returns: An output range writing into $(D_PARAM array).
|
|
*/
|
|
auto arrayInserter(Array)(return scope ref Array array)
|
|
if (isArray!Array)
|
|
{
|
|
static if (is(Array ArrayT : ArrayT[size], size_t size))
|
|
{
|
|
alias E = ArrayT;
|
|
}
|
|
else
|
|
{
|
|
alias E = ElementType!Array;
|
|
}
|
|
|
|
static struct ArrayInserter
|
|
{
|
|
private E[] data;
|
|
|
|
private this(return scope ref Array data) @trusted
|
|
{
|
|
this.data = data[];
|
|
}
|
|
|
|
void opCall(T)(auto ref T data)
|
|
if (is(T : E))
|
|
in
|
|
{
|
|
assert(!this.data.empty);
|
|
}
|
|
do
|
|
{
|
|
put(this.data, data);
|
|
}
|
|
|
|
void opCall(R)(auto ref R data)
|
|
if (isInputRange!R && isOutputRange!(E[], ElementType!R))
|
|
{
|
|
this.data = copy(data, this.data);
|
|
}
|
|
}
|
|
return ArrayInserter(array);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
int[1] array;
|
|
|
|
arrayInserter(array)(5);
|
|
assert(array[0] == 5);
|
|
}
|
|
|
|
///
|
|
@nogc nothrow pure @safe unittest
|
|
{
|
|
char[1] array;
|
|
alias Actual = typeof(arrayInserter(array));
|
|
|
|
static assert(isOutputRange!(Actual, char));
|
|
static assert(isOutputRange!(Actual, char[]));
|
|
}
|