19 Commits

Author SHA1 Message Date
7f080831c5 Implement IPv6 parser, fix #49 2018-09-08 07:20:23 +02:00
94c7fd2231 Move range.adapter to algorithms + take() bugfixes
A lot of algorithms like lazy sort() can be also classified as adapters
since it wraps the original range and allows to access the elements of
the range in a particular order. The only reason why take() was in
range.adapter is that take() is trivial - it doesn't change the order of
elements but can turn an infinite range into finite one. This
distinction between trivial and non-trivial algorithms isn't absolutely
clear. So let us put all algorithms and any adapters that change the
range iteration in some way into "algorithm" package to avoid any
confusion later.

- range.adapter is renamed into algorithm.iteration
- range.adapter is deprecated
- Added missing imports for take() and takeExactly()
- takeExactly() doesn't wrap ranges that have slicing anymore
- Voldemort structs for take() takeExactly() are now static
2018-09-06 12:50:42 +02:00
5ba6d35a1b Use fixed dscanner version 2018-09-03 09:55:19 +02:00
09f434f631 net.iface: Add indexToName 2018-09-02 10:00:52 +02:00
1f615301e5 memory.op: Add findNullTerminated 2018-09-02 08:27:26 +02:00
131675d0a8 Parse for the main part of an IPv6 address 2018-09-01 11:02:10 +02:00
aa12aa9014 Add module for network interfaces 2018-09-01 10:15:23 +02:00
41878cde50 Fix #60: Copying overlapping array slices 2018-08-30 07:12:38 +02:00
0fc0aa23f7 Add constants and syscall for if_nametoindex 2018-08-28 20:39:45 +02:00
c205c087a4 Switch to COFF on x86 Windows 2018-08-26 00:10:17 +02:00
8ca88d1f01 net.ip.Address4: Reject malformed addresses 2018-08-22 06:51:20 +02:00
fa4cbb7e59 Update to 2.081.2. Remove old compilers 2018-08-17 05:44:58 +02:00
4653e94fa1 Merge remote-tracking branch 'n8sh/relax-hasher-reqs' 2018-08-12 06:17:12 +02:00
ba5833318b conv: Fix taking out of range chars for hex values 2018-08-11 14:42:09 +02:00
918d8f5450 Deprecated putting an input into an output range
Use copy instead.
2018-08-10 15:34:07 +02:00
2862cc6f50 Update asm mangling to match GDC's D frontend 2018-08-08 10:27:23 +02:00
aa4ccddf47 Add net.ip. Fix #48 2018-08-07 22:27:09 +02:00
22cffe9d6e Set: allow hasher to take arg by ref 2018-08-06 14:41:47 -04:00
abd286064b Add algorithm.mutation.copy 2018-08-05 07:19:30 +02:00
27 changed files with 2168 additions and 199 deletions

View File

@ -7,10 +7,8 @@ os:
language: d
d:
- dmd-2.081.0
- dmd-2.080.1
- dmd-2.079.1
- dmd-2.078.3
- dmd-2.082.0
- dmd-2.081.2
env:
matrix:
@ -23,7 +21,7 @@ addons:
- gcc-multilib
before_script:
- if [ "`$DC --version | head -n 1 | grep 'v2.081.0'`" ]; then
- if [ "`$DC --version | head -n 1 | grep 'v2.082.0'`" ]; then
export UNITTEST="unittest-cov";
fi
@ -31,7 +29,7 @@ script:
- dub test -b ${UNITTEST:-unittest} --arch=$ARCH --compiler=$DC
- if [ "$UNITTEST" ] && [ "$ARCH" = "x86_64" ] && [ "$TRAVIS_OS_NAME" = "linux" ];
then
dub fetch dscanner;
dub fetch dscanner --version=0.5.10;
dub run dscanner -- --styleCheck ./source/;
fi

View File

@ -172,12 +172,10 @@ parameter is used)
### Supported compilers
| DMD | GCC |
|:-------:|:---------:|
| 2.081.1 | *master* |
| 2.080.1 | |
| 2.079.1 | |
| 2.078.3 | |
| DMD | GCC |
|:-------:|:------:|
| 2.082.0 | gdc-8 |
| 2.081.2 | gdc-7 |
### Release management

View File

@ -4,28 +4,16 @@ os: Visual Studio 2015
environment:
matrix:
- DC: dmd
DVersion: 2.081.1
DVersion: 2.082.0
arch: x64
- DC: dmd
DVersion: 2.081.1
DVersion: 2.082.0
arch: x86
- DC: dmd
DVersion: 2.080.1
DVersion: 2.081.2
arch: x64
- DC: dmd
DVersion: 2.080.1
arch: x86
- DC: dmd
DVersion: 2.079.1
arch: x64
- DC: dmd
DVersion: 2.079.1
arch: x86
- DC: dmd
DVersion: 2.078.3
arch: x64
- DC: dmd
DVersion: 2.078.3
DVersion: 2.081.2
arch: x86
skip_tags: true
@ -46,7 +34,7 @@ install:
before_build:
- ps: if($env:arch -eq "x86"){
$env:compilersetupargs = "x86";
$env:Darch = "x86";
$env:Darch = "x86_mscoff";
}
elseif($env:arch -eq "x64"){
$env:compilersetupargs = "amd64";

View File

@ -8,10 +8,9 @@
* rdx - r2 length.
* rcx - r2 data.
*/
.globl _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi
.type _D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi, @function
_D5tanya6memory2op9cmpMemoryFNaNbNixAvxAvZi:
.globl _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi
.type _D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi, @function
_D5tanya6memory2op9cmpMemoryFNaNbNixAvxQdZi:
// Compare the lengths
cmp %rdx, %rdi
jl less

View File

@ -11,10 +11,11 @@ The returned value is placed in %rax.
*/
.text
.globl syscall1
.type syscall1, @function
// 1 parameter.
.globl _D5tanya3sys5linux7syscallQiFNbNillZl
.type _D5tanya3sys5linux7syscallQiFNbNillZl, @function
syscall1:
_D5tanya3sys5linux7syscallQiFNbNillZl:
movq %rsi, %rax // Syscall number.
syscall
@ -23,10 +24,10 @@ syscall1:
// 2 parameters.
.globl _D5tanya3sys5linux7syscall7syscallFNbNilllZl
.type _D5tanya3sys5linux7syscall7syscallFNbNilllZl, @function
.globl _D5tanya3sys5linux7syscallQiFNbNilllZl
.type _D5tanya3sys5linux7syscallQiFNbNilllZl, @function
_D5tanya3sys5linux7syscall7syscallFNbNilllZl:
_D5tanya3sys5linux7syscallQiFNbNilllZl:
movq %rdx, %rax
syscall
@ -34,11 +35,23 @@ _D5tanya3sys5linux7syscall7syscallFNbNilllZl:
ret
// 6 parameters.
.globl _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl
.type _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl, @function
// 3 parameters.
.globl _D5tanya3sys5linux7syscallQiFNbNillllZl
.type _D5tanya3sys5linux7syscallQiFNbNillllZl, @function
_D5tanya3sys5linux7syscall7syscallFNbNilllllllZl:
_D5tanya3sys5linux7syscallQiFNbNillllZl:
movq %rcx, %rax
syscall
ret
// 6 parameters.
.globl _D5tanya3sys5linux7syscallQiFNbNilllllllZl
.type _D5tanya3sys5linux7syscallQiFNbNilllllllZl, @function
_D5tanya3sys5linux7syscallQiFNbNilllllllZl:
pushq %rbp
movq %rsp, %rbp

View File

@ -34,5 +34,7 @@
}
],
"libs-windows": ["advapi32"]
"libs-windows": ["advapi32"],
"libs-windows-x86_mscoff": ["iphlpapi"],
"libs-windows-x86_64": ["iphlpapi"]
}

View File

@ -0,0 +1,409 @@
/* 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.
*
* A range adapter wraps another range and modifies the way, how the original
* range is iterated, or the order in which its elements are accessed.
*
* All adapters are lazy algorithms, they request the next element of the
* adapted range on demand.
*
* Copyright: Eugene Wissner 2018.
* 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/algorithm/iteration.d,
* tanya/algorithm/iteration.d)
*/
module tanya.algorithm.iteration;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.range;
private mixin template Take(R, bool exactly)
{
private R source;
size_t length_;
@disable this();
private this(R source, size_t length)
{
this.source = source;
static if (!exactly && hasLength!R)
{
this.length_ = min(source.length, length);
}
else
{
this.length_ = length;
}
}
@property auto ref front()
in
{
assert(!empty);
}
do
{
return this.source.front;
}
void popFront()
in
{
assert(!empty);
}
do
{
this.source.popFront();
--this.length_;
}
@property bool empty()
{
static if (exactly || isInfinite!R)
{
return length == 0;
}
else
{
return length == 0 || this.source.empty;
}
}
@property size_t length()
{
return this.length_;
}
static if (hasAssignableElements!R)
{
@property void front(ref ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source.front = value;
}
@property void front(ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source.front = move(value);
}
}
static if (isForwardRange!R)
{
typeof(this) save()
{
return typeof(this)(this.source.save(), length);
}
}
static if (isRandomAccessRange!R)
{
@property auto ref back()
in
{
assert(!empty);
}
do
{
return this.source[this.length - 1];
}
void popBack()
in
{
assert(!empty);
}
do
{
--this.length_;
}
auto ref opIndex(size_t i)
in
{
assert(i < length);
}
do
{
return this.source[i];
}
static if (hasAssignableElements!R)
{
@property void back(ref ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source[length - 1] = value;
}
@property void back(ElementType!R value)
in
{
assert(!empty);
}
do
{
this.source[length - 1] = move(value);
}
void opIndexAssign(ref ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
{
this.source[i] = value;
}
void opIndexAssign(ElementType!R value, size_t i)
in
{
assert(i < length);
}
do
{
this.source[i] = move(value);
}
}
}
}
/**
* Takes $(D_PARAM n) elements from $(D_PARAM range).
*
* If $(D_PARAM range) doesn't have $(D_PARAM n) elements, the resulting range
* spans all elements of $(D_PARAM range).
*
* $(D_PSYMBOL take) is particulary useful with infinite ranges. You can take
` $(B n) elements from such range and pass the result to an algorithm which
* expects a finit range.
*
* Params:
* R = Type of the adapted range.
* range = The range to take the elements from.
* n = The number of elements to take.
*
* Returns: A range containing maximum $(D_PARAM n) first elements of
* $(D_PARAM range).
*
* See_Also: $(D_PSYMBOL takeExactly).
*/
auto take(R)(R range, size_t n)
if (isInputRange!R)
{
static struct Take
{
mixin .Take!(R, false);
static if (hasSlicing!R)
{
auto opSlice(size_t i, size_t j)
in
{
assert(i <= j);
assert(j <= length);
}
do
{
return typeof(this)(this.source[i .. j], length);
}
}
}
return Take(range, n);
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().take(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}
/**
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
*
* $(D_PARAM range) must have at least $(D_PARAM n) elements.
*
* $(D_PSYMBOL takeExactly) is particulary useful with infinite ranges. You can
` take $(B n) elements from such range and pass the result to an algorithm
* which expects a finit range.
*
* Params:
* R = Type of the adapted range.
* range = The range to take the elements from.
* n = The number of elements to take.
*
* Returns: A range containing $(D_PARAM n) first elements of $(D_PARAM range).
*
* See_Also: $(D_PSYMBOL take).
*/
auto takeExactly(R)(R range, size_t n)
if (isInputRange!R)
{
static if (hasSlicing!R)
{
return range[0 .. n];
}
else
{
static struct TakeExactly
{
mixin Take!(R, true);
}
return TakeExactly(range, n);
}
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().takeExactly(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}
// Takes minimum length if the range length > n
@nogc nothrow pure @safe unittest
{
auto range = take(cast(int[]) null, 8);
assert(range.length == 0);
}
@nogc nothrow pure @safe unittest
{
const int[9] range = [1, 2, 3, 4, 5, 6, 7, 8, 9];
{
auto slice = take(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
{
auto slice = takeExactly(range[], 8)[1 .. 3];
assert(slice.length == 2);
assert(slice.front == 2);
assert(slice.back == 3);
}
}

View File

@ -14,8 +14,10 @@
*/
module tanya.algorithm.mutation;
import tanya.memory.op;
static import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
private void deinitialize(bool zero, T)(ref T value)
{
@ -39,11 +41,12 @@ private void deinitialize(bool zero, T)(ref T value)
}
static if (zero)
{
fill!0((cast(void*) &value)[0 .. size]);
tanya.memory.op.fill!0((cast(void*) &value)[0 .. size]);
}
else
{
copy(typeid(T).initializer()[0 .. size], (&value)[0 .. 1]);
tanya.memory.op.copy(typeid(T).initializer()[0 .. size],
(&value)[0 .. 1]);
}
}
}
@ -81,7 +84,7 @@ do
{
static if (is(T == struct) || isStaticArray!T)
{
copy((&source)[0 .. 1], (&target)[0 .. 1]);
tanya.memory.op.copy((&source)[0 .. 1], (&target)[0 .. 1]);
static if (hasElaborateCopyConstructor!T || hasElaborateDestructor!T)
{
@ -273,3 +276,115 @@ void swap(T)(ref T a, ref T b) @trusted
assert(a == 5);
assert(b == 3);
}
/**
* Copies the $(D_PARAM source) range into the $(D_PARAM target) range.
*
* Params:
* Source = Input range type.
* Target = Output range type.
* source = Source input range.
* target = Target output range.
*
* Returns: $(D_PARAM target) range, whose front element is the one past the
* last element copied.
*
* Precondition: $(D_PARAM target) should be large enough to accept all
* $(D_PARAM source) elements.
*/
Target copy(Source, Target)(Source source, Target target)
if (isInputRange!Source && isOutputRange!(Target, Source))
in
{
static if (hasLength!Source && hasLength!Target)
{
assert(target.length >= source.length);
}
}
do
{
alias E = ElementType!Source;
static if (isDynamicArray!Source
&& is(Unqual!E == ElementType!Target)
&& !hasElaborateCopyConstructor!E
&& !hasElaborateAssign!E
&& !hasElaborateDestructor!E)
{
if (source.ptr < target.ptr
&& (() @trusted => (target.ptr - source.ptr) < source.length)())
{
tanya.memory.op.copyBackward(source, target);
}
else if (source.ptr !is target.ptr)
{
tanya.memory.op.copy(source, target);
}
return target[source.length .. $];
}
else
{
for (; !source.empty; source.popFront())
{
put(target, source.front);
}
return target;
}
}
///
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
const int[2] source = [1, 2];
int[2] target = [3, 4];
copy(source[], target[]);
assert(equal(source[], target[]));
}
// Returns advanced target
@nogc nothrow pure @safe unittest
{
int[5] input = [1, 2, 3, 4, 5];
assert(copy(input[3 .. 5], input[]).front == 3);
}
// Copies overlapping arrays
@nogc nothrow pure @safe unittest
{
import tanya.algorithm.comparison : equal;
int[6] actual = [1, 2, 3, 4, 5, 6];
const int[6] expected = [1, 2, 1, 2, 3, 4];
copy(actual[0 .. 4], actual[2 .. 6]);
assert(equal(actual[], expected[]));
}
@nogc nothrow pure @safe unittest
{
static assert(is(typeof(copy((ubyte[]).init, (ushort[]).init))));
static assert(!is(typeof(copy((ushort[]).init, (ubyte[]).init))));
}
@nogc nothrow pure @safe unittest
{
static struct OutPutRange
{
int value;
void put(int value) @nogc nothrow pure @safe
in
{
assert(this.value == 0);
}
do
{
this.value = value;
}
}
int[1] source = [5];
OutPutRange target;
assert(copy(source[], target).value == 5);
}

View File

@ -15,4 +15,5 @@
module tanya.algorithm;
public import tanya.algorithm.comparison;
public import tanya.algorithm.iteration;
public import tanya.algorithm.mutation;

View File

@ -154,7 +154,7 @@ struct Range(T)
* hasher = Hash function for $(D_PARAM T).
*/
struct Set(T, alias hasher = hash)
if (is(typeof(hasher(T.init)) == size_t))
if (is(typeof(((T x) => hasher(x))(T.init)) == size_t))
{
private alias HashArray = .HashArray!(hasher, T);
private alias Buckets = HashArray.Buckets;
@ -767,3 +767,9 @@ if (is(typeof(hasher(T.init)) == size_t))
}
testFunc(set);
}
@nogc nothrow pure @safe unittest
{
// Using hasher that takes argument by ref.
Set!(int, (const ref x) => cast(size_t)x) set;
}

View File

@ -27,7 +27,7 @@
module tanya.container.string;
import std.algorithm.comparison : cmp;
import std.algorithm.mutation : bringToFront, copy;
import std.algorithm.mutation : bringToFront;
import std.algorithm.searching;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;

View File

@ -308,11 +308,11 @@ do
{
digit = range.front - 'W';
}
else if (range.front >= 'A')
else if (range.front >= 'A' && range.front <= 'Z')
{
digit = range.front - '7';
}
else if (range.front >= '0')
else if (range.front >= '0' && range.front <= '9')
{
digit = range.front - '0';
}
@ -360,6 +360,15 @@ do
return n;
}
// ':' is not a hex value
@nogc nothrow pure @safe unittest
{
string colon = ":";
auto actual = readIntegral!ubyte(colon, 16);
assert(actual == 0);
assert(colon.length == 1);
}
// reads ubyte.max
@nogc nothrow pure @safe unittest
{

View File

@ -15,14 +15,13 @@
module tanya.math.mp;
import std.algorithm.comparison : cmp;
import std.algorithm.mutation : copy, fill, reverse;
import std.algorithm.mutation : fill, reverse;
import std.range;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.container.array;
import tanya.encoding.ascii;
import tanya.memory;
static import tanya.memory.op;
import tanya.meta.trait;
import tanya.meta.transform;
@ -211,7 +210,7 @@ struct Integer
this(this) @nogc nothrow pure @safe
{
auto tmp = allocator.resize!digit(null, this.size);
tanya.memory.op.copy(this.rep[0 .. this.size], tmp);
copy(this.rep[0 .. this.size], tmp);
this.rep = tmp;
}
@ -344,8 +343,7 @@ struct Integer
if (is(Unqual!T == Integer))
{
this.rep = allocator.resize(this.rep, value.size);
tanya.memory.op.copy(value.rep[0 .. value.size],
this.rep[0 .. value.size]);
copy(value.rep[0 .. value.size], this.rep[0 .. value.size]);
this.size = value.size;
this.sign = value.sign;

View File

@ -305,7 +305,7 @@ do
* first occurrence of $(D_PARAM needle). If $(D_PARAM needle)
* couldn't be found, an empty `inout void[]` is returned.
*/
inout(void[]) find(return inout void[] haystack, const ubyte needle)
inout(void[]) find(return inout void[] haystack, ubyte needle)
@nogc nothrow pure @trusted
in
{
@ -326,19 +326,19 @@ do
{
return bytes[0 .. length];
}
bytes++;
length--;
++bytes;
--length;
}
// Check if some of the words has the needle
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words ^ needleWord) - highBits) & (~*words) & mask)
if ((((*words ^ needleWord) - highBits) & (~*words) & mask) != 0)
{
break;
}
words++;
++words;
length -= size_t.sizeof;
}
@ -350,8 +350,8 @@ do
{
return bytes[0 .. length];
}
bytes++;
length--;
++bytes;
--length;
}
return haystack[$ .. $];
@ -362,14 +362,89 @@ do
{
const ubyte[9] haystack = ['a', 'b', 'c', 'd', 'e', 'f', 'b', 'g', 'h'];
assert(find(haystack, 'a') == haystack[]);
assert(find(haystack, 'b') == haystack[1 .. $]);
assert(find(haystack, 'c') == haystack[2 .. $]);
assert(find(haystack, 'd') == haystack[3 .. $]);
assert(find(haystack, 'e') == haystack[4 .. $]);
assert(find(haystack, 'f') == haystack[5 .. $]);
assert(find(haystack, 'h') == haystack[8 .. $]);
assert(cmp(find(haystack, 'a'), haystack[]) == 0);
assert(cmp(find(haystack, 'b'), haystack[1 .. $]) == 0);
assert(cmp(find(haystack, 'c'), haystack[2 .. $]) == 0);
assert(cmp(find(haystack, 'd'), haystack[3 .. $]) == 0);
assert(cmp(find(haystack, 'e'), haystack[4 .. $]) == 0);
assert(cmp(find(haystack, 'f'), haystack[5 .. $]) == 0);
assert(cmp(find(haystack, 'h'), haystack[8 .. $]) == 0);
assert(find(haystack, 'i').length == 0);
assert(find(null, 'a').length == 0);
}
/**
* Looks for `\0` in the $(D_PARAM haystack) and returns the part of the
* $(D_PARAM haystack) ahead of it.
*
* Returns $(D_KEYWORD null) if $(D_PARAM haystack) doesn't contain a null
* character.
*
* Params:
* haystack = Memory block.
*
* Returns: The subrange that spans all bytes before the null character or
* $(D_KEYWORD null) if the $(D_PARAM haystack) doesn't contain any.
*/
inout(char[]) findNullTerminated(return inout char[] haystack)
@nogc nothrow pure @trusted
in
{
assert(haystack.length == 0 || haystack.ptr !is null);
}
do
{
auto length = haystack.length;
enum size_t highBits = filledBytes!(0x01, 0);
enum size_t mask = filledBytes!(0x80, 0);
// Align
auto bytes = cast(inout(ubyte)*) haystack;
while (length > 0 && ((cast(size_t) bytes) & 3) != 0)
{
if (*bytes == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
// Check if some of the words contains 0
auto words = cast(inout(size_t)*) bytes;
while (length >= size_t.sizeof)
{
if (((*words - highBits) & (~*words) & mask) != 0)
{
break;
}
++words;
length -= size_t.sizeof;
}
// Find the exact 0 position in the word
bytes = cast(inout(ubyte)*) words;
while (length > 0)
{
if (*bytes == '\0')
{
return haystack[0 .. haystack.length - length];
}
++bytes;
--length;
}
return null;
}
///
@nogc nothrow pure @safe unittest
{
assert(cmp(findNullTerminated("abcdef\0gh"), "abcdef") == 0);
assert(cmp(findNullTerminated("\0garbage"), "") == 0);
assert(cmp(findNullTerminated("\0"), "") == 0);
assert(cmp(findNullTerminated("cstring\0"), "cstring") == 0);
assert(findNullTerminated(null) is null);
assert(findNullTerminated("abcdef") is null);
}

View File

@ -14,7 +14,7 @@
*/
module tanya.memory;
import std.algorithm.mutation;
import std.algorithm.mutation : uninitializedFill;
import tanya.conv;
import tanya.exception;
public import tanya.memory.allocator;

228
source/tanya/net/iface.d Normal file
View File

@ -0,0 +1,228 @@
/* 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/. */
/**
* Network interfaces.
*
* Copyright: Eugene Wissner 2018.
* 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/net/iface.d,
* tanya/net/iface.d)
*/
module tanya.net.iface;
import tanya.algorithm.comparison;
import tanya.algorithm.mutation;
import tanya.container.string;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.range;
version (TanyaNative)
{
import mir.linux._asm.unistd;
import tanya.sys.linux.syscall;
import tanya.sys.posix.ioctl;
import tanya.sys.posix.net.if_;
import tanya.sys.posix.socket;
}
else version (Windows)
{
import tanya.sys.windows.ifdef;
import tanya.sys.windows.iphlpapi;
}
else version (Posix)
{
import core.sys.posix.net.if_;
}
/**
* Converts the name of a network interface to its index.
*
* If an interface with the name $(D_PARAM name) cannot be found or another
* error occurres, returns 0.
*
* Params:
* name = Interface name.
*
* Returns: Returns interface index or 0.
*/
uint nameToIndex(R)(R name) @trusted
if (isInputRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
{
version (TanyaNative)
{
if (name.length >= IF_NAMESIZE)
{
return 0;
}
ifreq ifreq_ = void;
copy(name, ifreq_.ifr_name[]);
ifreq_.ifr_name[name.length] = '\0';
auto socket = syscall(AF_INET,
SOCK_DGRAM | SOCK_CLOEXEC,
0,
NR_socket);
if (socket <= 0)
{
return 0;
}
scope (exit)
{
syscall(socket, NR_close);
}
if (syscall(socket,
SIOCGIFINDEX,
cast(ptrdiff_t) &ifreq_,
NR_ioctl) == 0)
{
return ifreq_.ifr_ifindex;
}
return 0;
}
else version (Windows)
{
if (name.length > IF_MAX_STRING_SIZE)
{
return 0;
}
char[IF_MAX_STRING_SIZE + 1] buffer;
NET_LUID luid;
copy(name, buffer[]);
buffer[name.length] = '\0';
if (ConvertInterfaceNameToLuidA(buffer.ptr, &luid) != 0)
{
return 0;
}
NET_IFINDEX index;
if (ConvertInterfaceLuidToIndex(&luid, &index) == 0)
{
return index;
}
return 0;
}
else version (Posix)
{
if (name.length >= IF_NAMESIZE)
{
return 0;
}
char[IF_NAMESIZE] buffer;
copy(name, buffer[]);
buffer[name.length] = '\0';
return if_nametoindex(buffer.ptr);
}
}
///
@nogc nothrow @safe unittest
{
version (linux)
{
assert(nameToIndex("lo") == 1);
}
else version (Windows)
{
assert(nameToIndex("loopback_0") == 1);
}
else
{
assert(nameToIndex("lo0") == 1);
}
assert(nameToIndex("ecafretni") == 0);
}
/**
* Converts the index of a network interface to its name.
*
* If an interface with the $(D_PARAM index) cannot be found or another
* error occurres, returns an empty $(D_PSYMBOL String).
*
* Params:
* index = Interface index.
*
* Returns: Returns interface name or an empty $(D_PSYMBOL String).
*/
String indexToName(uint index) @nogc nothrow @trusted
{
import tanya.memory.op : findNullTerminated;
version (TanyaNative)
{
ifreq ifreq_ = void;
ifreq_.ifr_ifindex = index;
auto socket = syscall(AF_INET,
SOCK_DGRAM | SOCK_CLOEXEC,
0,
NR_socket);
if (socket <= 0)
{
return String();
}
scope (exit)
{
syscall(socket, NR_close);
}
if (syscall(socket,
SIOCGIFNAME,
cast(ptrdiff_t) &ifreq_,
NR_ioctl) == 0)
{
return String(findNullTerminated(ifreq_.ifr_name));
}
return String();
}
else version (Windows)
{
NET_LUID luid;
if (ConvertInterfaceIndexToLuid(index, &luid) != 0)
{
return String();
}
char[IF_MAX_STRING_SIZE + 1] buffer;
if (ConvertInterfaceLuidToNameA(&luid,
buffer.ptr,
IF_MAX_STRING_SIZE + 1) != 0)
{
return String();
}
return String(findNullTerminated(buffer));
}
else version (Posix)
{
char[IF_NAMESIZE] buffer;
if (if_indextoname(index, buffer.ptr) is null)
{
return String();
}
return String(findNullTerminated(buffer));
}
}
@nogc nothrow @safe unittest
{
version (linux)
{
assert(equal(indexToName(1)[], "lo"));
}
else version (Windows)
{
assert(equal(indexToName(1)[], "loopback_0"));
}
else
{
assert(equal(indexToName(1)[], "lo0"));
}
assert(indexToName(uint.max).empty);
}

890
source/tanya/net/ip.d Normal file
View File

@ -0,0 +1,890 @@
/* 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/. */
/**
* Internet Protocol implementation.
*
* Copyright: Eugene Wissner 2018.
* 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/net/ip.d,
* tanya/net/ip.d)
*/
module tanya.net.ip;
import tanya.algorithm.mutation;
import tanya.container.string;
import tanya.conv;
import tanya.encoding.ascii;
import tanya.format;
import tanya.meta.trait;
import tanya.meta.transform;
import tanya.net.iface;
import tanya.net.inet;
import tanya.range;
import tanya.typecons;
/**
* IPv4 internet address.
*/
struct Address4
{
// In network byte order.
private uint address;
version (LittleEndian)
{
private enum uint loopback_ = 0x0100007fU;
enum byte step = 8;
}
else
{
private enum uint loopback_ = 0x7f000001U;
enum byte step = -8;
}
private enum uint any_ = 0U;
private enum uint broadcast = uint.max;
/**
* Constructs an $(D_PSYMBOL Address4) from an unsigned integer in host
* byte order.
*
* Params:
* address = The address as an unsigned integer in host byte order.
*/
this(uint address) @nogc nothrow pure @safe
{
copy(NetworkOrder!4(address),
(() @trusted => (cast(ubyte*) &this.address)[0 .. 4])());
}
///
@nogc nothrow pure @safe unittest
{
assert(Address4(0x00202000U).toUInt() == 0x00202000U);
}
/**
* Returns object that represents 127.0.0.1.
*
* Returns: Object that represents the Loopback address.
*/
static Address4 loopback() @nogc nothrow pure @safe
{
typeof(return) address;
address.address = Address4.loopback_;
return address;
}
///
@nogc nothrow pure @safe unittest
{
assert(Address4.loopback().isLoopback());
}
/**
* Returns object that represents 0.0.0.0.
*
* Returns: Object that represents any address.
*/
static Address4 any() @nogc nothrow pure @safe
{
typeof(return) address;
address.address = Address4.any_;
return address;
}
///
@nogc nothrow pure @safe unittest
{
assert(Address4.any().isAny());
}
/**
* Loopback address is 127.0.0.1.
*
* Returns: $(D_KEYWORD true) if this is a loopback address,
* $(D_KEYWORD false) otherwise.
*/
bool isLoopback() const @nogc nothrow pure @safe
{
return this.address == loopback_;
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("127.0.0.1").isLoopback());
}
/**
* 0.0.0.0 can represent any address. This function checks whether this
* address is 0.0.0.0.
*
* Returns: $(D_KEYWORD true) if this is an unspecified address,
* $(D_KEYWORD false) otherwise.
*/
bool isAny() const @nogc nothrow pure @safe
{
return this.address == any_;
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("0.0.0.0").isAny());
}
/**
* Broadcast address is 255.255.255.255.
*
* Returns: $(D_KEYWORD true) if this is a broadcast address,
* $(D_KEYWORD false) otherwise.
*/
bool isBroadcast() const @nogc nothrow pure @safe
{
return this.address == broadcast;
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("255.255.255.255").isBroadcast());
}
/**
* Determines whether this address' destination is a group of endpoints.
*
* Returns: $(D_KEYWORD true) if this is a multicast address,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isUnicast).
*/
bool isMulticast() const @nogc nothrow pure @safe
{
version (LittleEndian)
{
enum uint mask = 0xe0;
}
else
{
enum uint mask = 0xe0000000U;
}
return (this.address & mask) == mask;
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("224.0.0.3").isMulticast());
}
/**
* Determines whether this address' destination is a single endpoint.
*
* Returns: $(D_KEYWORD true) if this is a multicast address,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isMulticast).
*/
bool isUnicast() const @nogc nothrow pure @safe
{
return !isMulticast();
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("192.168.0.1").isUnicast());
}
/**
* Produces a string containing an IPv4 address in dotted-decimal notation.
*
* Returns: This address in dotted-decimal notation.
*/
String stringify() const @nogc nothrow pure @safe
{
const octets = (() @trusted => (cast(ubyte*) &this.address)[0 .. 4])();
enum string fmt = "{}.{}.{}.{}";
version (LittleEndian)
{
return format!fmt(octets[0], octets[1], octets[2], octets[3]);
}
else
{
return format!fmt(octets[3], octets[2], octets[1], octets[0]);
}
}
///
@nogc nothrow pure @safe unittest
{
const dottedDecimal = "192.168.0.1";
const address = address4(dottedDecimal);
assert(address.get.stringify() == dottedDecimal);
}
/**
* Produces a byte array containing this address in network byte order.
*
* Returns: This address as raw bytes in network byte order.
*/
ubyte[4] toBytes() const @nogc nothrow pure @safe
{
ubyte[4] bytes;
copy((() @trusted => (cast(ubyte*) &this.address)[0 .. 4])(), bytes[]);
return bytes;
}
///
@nogc nothrow pure @safe unittest
{
const actual = address4("192.168.0.1");
const ubyte[4] expected = [192, 168, 0, 1];
assert(actual.toBytes() == expected);
}
/**
* Converts this address to an unsigned integer in host byte order.
*
* Returns: This address as an unsigned integer in host byte order.
*/
uint toUInt() const @nogc nothrow pure @safe
{
alias slice = () @trusted => (cast(ubyte*) &this.address)[0 .. 4];
return toHostOrder!uint(slice());
}
///
@nogc nothrow pure @safe unittest
{
assert(address4("127.0.0.1").toUInt() == 0x7f000001U);
}
}
/**
* Parses a string containing an IPv4 address in dotted-decimal notation.
*
* Params:
* R = Input range type.
* range = Stringish range containing the address.
*
* Returns: $(D_PSYMBOL Option) containing the address if the parsing was
* successful, or nothing otherwise.
*/
Option!Address4 address4(R)(R range)
if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
{
Address4 result;
version (LittleEndian)
{
ubyte shift;
enum ubyte cond = 24;
}
else
{
ubyte shift = 24;
enum ubyte cond = 0;
}
for (; shift != cond; shift += Address4.step, range.popFront())
{
if (range.empty || range.front == '.')
{
return typeof(return)();
}
result.address |= readIntegral!ubyte(range) << shift;
if (range.empty || range.front != '.')
{
return typeof(return)();
}
}
if (range.empty || range.front == '.')
{
return typeof(return)();
}
result.address |= readIntegral!ubyte(range) << shift;
return range.empty ? typeof(return)(result) : typeof(return)();
}
// Rejects malformed addresses
@nogc nothrow pure @safe unittest
{
assert(address4("256.0.0.1").isNothing);
assert(address4(".0.0.1").isNothing);
assert(address4("0..0.1").isNothing);
assert(address4("0.0.0.").isNothing);
assert(address4("0.0.").isNothing);
assert(address4("").isNothing);
}
/**
* Constructs an $(D_PSYMBOL Address4) from raw bytes in network byte order.
*
* Params:
* R = Input range type.
* range = $(D_KEYWORD ubyte) range containing the address.
*
* Returns: $(D_PSYMBOL Option) containing the address if the $(D_PARAM range)
* contains exactly 4 bytes, or nothing otherwise.
*/
Option!Address4 address4(R)(R range)
if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte))
{
Address4 result;
version (LittleEndian)
{
ubyte shift;
}
else
{
ubyte shift = 24;
}
for (; shift <= 24; shift += Address4.step, range.popFront())
{
if (range.empty)
{
return typeof(return)();
}
result.address |= range.front << shift;
}
return range.empty ? typeof(return)(result) : typeof(return)();
}
///
@nogc nothrow pure @safe unittest
{
{
ubyte[4] actual = [127, 0, 0, 1];
assert(address4(actual[]).isLoopback());
}
{
ubyte[3] actual = [127, 0, 0];
assert(address4(actual[]).isNothing);
}
{
ubyte[5] actual = [127, 0, 0, 0, 1];
assert(address4(actual[]).isNothing);
}
}
@nogc nothrow pure @safe unittest
{
assert(address4(cast(ubyte[]) []).isNothing);
}
// Assignment and comparison works
@nogc nothrow pure @safe unittest
{
auto address1 = Address4.loopback();
auto address2 = Address4.any();
address1 = address2;
assert(address1 == address2);
}
/**
* IPv6 internet address.
*/
struct Address6
{
// Raw bytes
private ubyte[16] address;
/// Scope ID.
uint scopeID;
/**
* Constructs an $(D_PSYMBOL Address6) from an array containing raw bytes
* in network byte order and scope ID.
*
* Params:
* address = The address as an unsigned integer in host byte order.
* scopeID = Scope ID.
*/
this(ubyte[16] address, uint scopeID = 0) @nogc nothrow pure @safe
{
copy(address[], this.address[]);
this.scopeID = scopeID;
}
///
@nogc nothrow pure @safe unittest
{
const ubyte[16] expected = [ 0, 1, 0, 2, 0, 3, 0, 4,
0, 5, 0, 6, 0, 7, 0, 8 ];
auto actual = Address6(expected, 1);
assert(actual.toBytes() == expected);
assert(actual.scopeID == 1);
}
/**
* Returns object that represents ::.
*
* Returns: Object that represents any address.
*/
static Address6 any() @nogc nothrow pure @safe
{
return Address6();
}
///
@nogc nothrow pure @safe unittest
{
assert(Address6.any().isAny());
}
/**
* Returns object that represents ::1.
*
* Returns: Object that represents the Loopback address.
*/
static Address6 loopback() @nogc nothrow pure @safe
{
typeof(return) address;
address.address[$ - 1] = 1;
return address;
}
///
@nogc nothrow pure @safe unittest
{
assert(Address6.loopback().isLoopback());
}
/**
* :: can represent any address. This function checks whether this
* address is ::.
*
* Returns: $(D_KEYWORD true) if this is an unspecified address,
* $(D_KEYWORD false) otherwise.
*/
bool isAny() const @nogc nothrow pure @safe
{
return this.address == any.address;
}
///
@nogc nothrow @safe unittest
{
assert(address6("::").isAny());
}
/**
* Loopback address is ::1.
*
* Returns: $(D_KEYWORD true) if this is a loopback address,
* $(D_KEYWORD false) otherwise.
*/
bool isLoopback() const @nogc nothrow pure @safe
{
return this.address == loopback.address;
}
///
@nogc nothrow @safe unittest
{
assert(address6("::1").isLoopback());
}
/**
* Determines whether this address' destination is a group of endpoints.
*
* Returns: $(D_KEYWORD true) if this is a multicast address,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isUnicast).
*/
bool isMulticast() const @nogc nothrow pure @safe
{
return this.address[0] == 0xff;
}
///
@nogc nothrow @safe unittest
{
assert(address6("ff00::").isMulticast());
}
/**
* Determines whether this address' destination is a single endpoint.
*
* Returns: $(D_KEYWORD true) if this is a multicast address,
* $(D_KEYWORD false) otherwise.
*
* See_Also: $(D_PSYMBOL isMulticast).
*/
bool isUnicast() const @nogc nothrow pure @safe
{
return !isMulticast();
}
///
@nogc nothrow @safe unittest
{
assert(address6("::1").isUnicast());
}
/**
* Determines whether this address is a link-local unicast address.
*
* Returns: $(D_KEYWORD true) if this is a link-local address,
* $(D_KEYWORD false) otherwise.
*/
bool isLinkLocal() const @nogc nothrow pure @safe
{
return this.address[0] == 0xfe && (this.address[1] & 0xc0) == 0x80;
}
///
@nogc nothrow @safe unittest
{
assert(address6("fe80::1").isLinkLocal());
}
/**
* Determines whether this address is an Unique Local Address (ULA).
*
* Returns: $(D_KEYWORD true) if this is an Unique Local Address,
* $(D_KEYWORD false) otherwise.
*/
bool isUniqueLocal() const @nogc nothrow pure @safe
{
return this.address[0] == 0xfc || this.address[0] == 0xfd;
}
///
@nogc nothrow @safe unittest
{
assert(address6("fd80:124e:34f3::1").isUniqueLocal());
}
/**
* Returns text representation of this address.
*
* Returns: text representation of this address.
*/
String stringify() const @nogc nothrow pure @safe
{
String output;
foreach (i, b; this.address)
{
ubyte low = b & 0xf;
ubyte high = b >> 4;
if (high < 10)
{
output.insertBack(cast(char) (high + '0'));
}
else
{
output.insertBack(cast(char) (high - 10 + 'a'));
}
if (low < 10)
{
output.insertBack(cast(char) (low + '0'));
}
else
{
output.insertBack(cast(char) (low - 10 + 'a'));
}
if (i % 2 != 0 && i != (this.address.length - 1))
{
output.insertBack(':');
}
}
return output;
}
///
@nogc nothrow @safe unittest
{
import tanya.algorithm.comparison : equal;
assert(equal(address6("1:2:3:4:5:6:7:8").stringify()[],
"0001:0002:0003:0004:0005:0006:0007:0008"));
}
/**
* Produces a byte array containing this address in network byte order.
*
* Returns: This address as raw bytes in network byte order.
*/
ubyte[16] toBytes() const @nogc nothrow pure @safe
{
return this.address;
}
///
@nogc nothrow @safe unittest
{
auto actual = address6("1:2:3:4:5:6:7:8");
ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8];
assert(actual.toBytes() == expected);
}
}
private void write2Bytes(R)(ref R range, ubyte[] address)
{
ushort group = readIntegral!ushort(range, 16);
address[0] = cast(ubyte) (group >> 8);
address[1] = group & 0xff;
}
/**
* Parses a string containing an IPv6 address.
*
* This function isn't pure since an IPv6 address can contain interface name
* or interface ID (separated from the address by `%`). If an interface name
* is specified (i.e. first character after `%` is not a digit), the parser
* tries to convert it to the ID of that interface. If the interface with the
* given name can't be found, the parser doesn't fail, but just ignores the
* invalid interface name.
*
* If an ID is given (i.e. first character after `%` is a digit),
* $(D_PSYMBOL address6) just stores it in $(D_PSYMBOL Address6.scopeID) without
* checking whether an interface with this ID really exists. If the ID is
* invalid (if it is too long or contains non decimal characters), parsing
* and nothing is returned.
*
* If neither an ID nor a name is given, $(D_PSYMBOL Address6.scopeID) is set
* to `0`.
*
* The parser doesn't support notation with an embedded IPv4 address (e.g.
* ::1.2.3.4).
*
* Params:
* R = Input range type.
* range = Stringish range containing the address.
*
* Returns: $(D_PSYMBOL Option) containing the address if the parsing was
* successful, or nothing otherwise.
*/
Option!Address6 address6(R)(R range)
if (isForwardRange!R && is(Unqual!(ElementType!R) == char) && hasLength!R)
{
if (range.empty)
{
return typeof(return)();
}
Address6 result;
ubyte[12] tail;
size_t i;
size_t j;
// An address begins with a number, not ':'. But there is a special case
// if the address begins with '::'.
if (range.front == ':')
{
range.popFront();
if (range.empty || range.front != ':')
{
return typeof(return)();
}
range.popFront();
goto ParseTail;
}
// Parse the address before '::'.
// This loop parses the whole address if it doesn't contain '::'.
for (; i < 13; i += 2)
{
write2Bytes(range, result.address[i .. $]);
if (range.empty || range.front != ':')
{
return typeof(return)();
}
range.popFront();
if (range.empty)
{
return typeof(return)();
}
if (range.front == ':')
{
range.popFront();
goto ParseTail;
}
}
write2Bytes(range, result.address[14 .. $]);
if (range.empty)
{
return typeof(return)(result);
}
else if (range.front == '%')
{
goto ParseIface;
}
else
{
return typeof(return)();
}
ParseTail: // after ::
// Normally the address can't end with ':', but a special case is if the
// address ends with '::'. So the first iteration of the loop below is
// unrolled to check whether the address contains something after '::' at
// all.
if (range.empty)
{
return typeof(return)(result); // ends with ::
}
if (range.front == ':')
{
return typeof(return)();
}
write2Bytes(range, tail[j .. $]);
if (range.empty)
{
goto CopyTail;
}
else if (range.front == '%')
{
goto ParseIface;
}
else if (range.front != ':')
{
return typeof(return)();
}
range.popFront();
for (i = 2, j = 2; i <= 11; i += 2, j += 2, range.popFront())
{
if (range.empty || range.front == ':')
{
return typeof(return)();
}
write2Bytes(range, tail[j .. $]);
if (range.empty)
{
goto CopyTail;
}
else if (range.front == '%')
{
goto ParseIface;
}
else if (range.front != ':')
{
return typeof(return)();
}
}
ParseIface: // Scope name or ID
range.popFront();
if (range.empty)
{
return typeof(return)();
}
else if (isDigit(range.front))
{
const scopeID = readIntegral!uint(range);
if (range.empty)
{
result.scopeID = scopeID;
}
else
{
return typeof(return)();
}
}
else
{
result.scopeID = nameToIndex(range);
}
CopyTail:
copy(tail[0 .. j + 2], result.address[$ - j - 2 .. $]);
return typeof(return)(result);
}
@nogc nothrow @safe unittest
{
{
ubyte[16] expected = [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8];
auto actual = address6("1:2:3:4:5:6:7:8");
assert(actual.address == expected);
}
{
ubyte[16] expected;
auto actual = address6("::");
assert(actual.address == expected);
}
{
ubyte[16] expected = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1];
auto actual = address6("::1");
assert(actual.address == expected);
}
{
ubyte[16] expected = [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
auto actual = address6("1::");
assert(actual.address == expected);
}
}
// Rejects malformed addresses
@nogc nothrow @safe unittest
{
assert(address6("").isNothing);
assert(address6(":").isNothing);
assert(address6(":a").isNothing);
assert(address6("a:").isNothing);
assert(address6("1:2:3:4::6:").isNothing);
assert(address6("1:2:3:4::6:7:8%").isNothing);
}
/**
* Constructs an $(D_PSYMBOL Address6) from raw bytes in network byte order and
* the scope ID.
*
* Params:
* R = Input range type.
* range = $(D_KEYWORD ubyte) range containing the address.
* scopeID = Scope ID.
*
* Returns: $(D_PSYMBOL Option) containing the address if the $(D_PARAM range)
* contains exactly 16 bytes, or nothing otherwise.
*/
Option!Address6 address6(R)(R range, uint scopeID = 0)
if (isInputRange!R && is(Unqual!(ElementType!R) == ubyte))
{
Address6 result;
int i;
for (; i < 16 && !range.empty; ++i, range.popFront())
{
result.address[i] = range.front;
}
result.scopeID = scopeID;
return range.empty && i == 16 ? typeof(return)(result) : typeof(return)();
}
///
@nogc nothrow pure @safe unittest
{
{
ubyte[16] actual = [ 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15, 16 ];
assert(!address6(actual[]).isNothing);
}
{
ubyte[15] actual = [ 1, 2, 3, 4, 5, 6, 7, 8,
9, 10, 11, 12, 13, 14, 15 ];
assert(address6(actual[]).isNothing);
}
{
ubyte[17] actual = [ 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17 ];
assert(address6(actual[]).isNothing);
}
{
assert(address6(cast(ubyte[]) []).isNothing);
}
}

View File

@ -18,6 +18,7 @@
* Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/range/adapter.d,
* tanya/range/adapter.d)
*/
deprecated("Use tanya.algorithm.iteration instead")
module tanya.range.adapter;
import tanya.algorithm.mutation;
@ -232,63 +233,6 @@ if (isInputRange!R)
return Take(range, n);
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().take(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}
/**
* Takes exactly $(D_PARAM n) elements from $(D_PARAM range).
*
@ -316,60 +260,3 @@ if (isInputRange!R)
}
return TakeExactly(range, n);
}
///
@nogc nothrow pure @safe unittest
{
static struct InfiniteRange
{
private size_t front_ = 1;
enum bool empty = false;
@property size_t front() @nogc nothrow pure @safe
{
return this.front_;
}
@property void front(size_t i) @nogc nothrow pure @safe
{
this.front_ = i;
}
void popFront() @nogc nothrow pure @safe
{
++this.front_;
}
size_t opIndex(size_t i) @nogc nothrow pure @safe
{
return this.front_ + i;
}
void opIndexAssign(size_t value, size_t i) @nogc nothrow pure @safe
{
this.front = i + value;
}
InfiniteRange save() @nogc nothrow pure @safe
{
return this;
}
}
auto t = InfiniteRange().takeExactly(3);
assert(t.length == 3);
assert(t.front == 1);
assert(t.back == 3);
t.popFront();
assert(t.front == 2);
assert(t.back == 3);
t.popBack();
assert(t.front == 2);
assert(t.back == 2);
t.popFront();
assert(t.empty);
}

View File

@ -851,6 +851,8 @@ void put(R, E)(ref R range, auto ref E e)
}
else static if (isInputRange!E)
{
pragma(msg, "Putting an input range into an output range is "
~ "deprecated. Use tanya.algorithm.mutation.copy instead");
for (; !e.empty; e.popFront())
{
put(range, e.front);
@ -907,17 +909,6 @@ void put(R, E)(ref R range, auto ref E e)
assert(oc.e == 2);
}
///
@nogc nothrow pure @safe unittest
{
int[2] actual;
int[2] expected = [2, 3];
auto slice = actual[];
put(slice, expected[]);
assert(actual == expected);
}
/**
* Determines whether $(D_PARAM R) is an output range for the elemens of type
* $(D_PARAM E).
@ -963,7 +954,22 @@ void put(R, E)(ref R range, auto ref E e)
* Returns: $(D_KEYWORD true) if $(D_PARAM R) is an output range for the
* elements of the type $(D_PARAM E), $(D_KEYWORD false) otherwise.
*/
enum bool isOutputRange(R, E) = is(typeof((ref R r, ref E e) => put(r, e)));
template isOutputRange(R, E)
{
static if (is(typeof((R r, E e) => put(r, e))))
{
enum bool isOutputRange = true;
}
else static if (isInputRange!E)
{
alias ET = ElementType!E;
enum bool isOutputRange = is(typeof((R r, ET e) => put(r, e)));
}
else
{
enum bool isOutputRange = false;
}
}
///
@nogc nothrow pure @safe unittest

View File

@ -14,9 +14,15 @@ module tanya.sys.linux.syscall;
version (TanyaNative):
extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t)
@nogc nothrow @system;
extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow @system;
extern ptrdiff_t syscall(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow @system;
extern ptrdiff_t syscall(ptrdiff_t,
ptrdiff_t,
ptrdiff_t,
@ -34,10 +40,18 @@ private template getOverloadMangling(size_t n)
}
pragma(mangle, getOverloadMangling!0)
extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t)
extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t)
@nogc nothrow pure @system;
pragma(mangle, getOverloadMangling!1)
extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow pure @system;
pragma(mangle, getOverloadMangling!2)
extern ptrdiff_t syscall_(ptrdiff_t, ptrdiff_t, ptrdiff_t, ptrdiff_t)
@nogc nothrow pure @system;
pragma(mangle, getOverloadMangling!3)
extern ptrdiff_t syscall_(ptrdiff_t,
ptrdiff_t,
ptrdiff_t,

View File

@ -0,0 +1,78 @@
/* 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/. */
/*
* Copyright: Eugene Wissner 2018.
* 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/sys/posix/ioctl.d,
* tanya/sys/posix/ioctl.d)
*/
module tanya.sys.posix.ioctl;
version (TanyaNative):
enum
{
SIOCADDRT = 0x890B, // Add routing table entry.
SIOCDELRT = 0x890C, // Delete routing table entry.
SIOCRTMSG = 0x890D, // Call to routing system.
SIOCGIFNAME = 0x8910, // Get iface name.
SIOCSIFLINK = 0x8911, // Set iface channel.
SIOCGIFCONF = 0x8912, // Get iface list.
SIOCGIFFLAGS = 0x8913, // Get flags.
SIOCSIFFLAGS = 0x8914, // Set flags.
SIOCGIFADDR = 0x8915, // Get PA address.
SIOCSIFADDR = 0x8916, // Set PA address.
SIOCGIFDSTADDR = 0x8917, // Get remote PA address.
SIOCSIFDSTADDR = 0x8918, // Set remote PA address.
SIOCGIFBRDADDR = 0x8919, // Get broadcast PA address.
SIOCSIFBRDADDR = 0x891a, // Set broadcast PA address.
SIOCGIFNETMASK = 0x891b, // Get network PA mask.
SIOCSIFNETMASK = 0x891c, // Set network PA mask.
SIOCGIFMETRIC = 0x891d, // Get metric.
SIOCSIFMETRIC = 0x891e, // Set metric.
SIOCGIFMEM = 0x891f, // Get memory address (BSD).
SIOCSIFMEM = 0x8920, // Set memory address (BSD).
SIOCGIFMTU = 0x8921, // Get MTU size.
SIOCSIFMTU = 0x8922, // Set MTU size.
SIOCSIFNAME = 0x8923, // Set interface name.
SIOCSIFHWADDR = 0x8924, // Set hardware address.
SIOCGIFENCAP = 0x8925, // Get/set encapsulations.
SIOCSIFENCAP = 0x8926,
SIOCGIFHWADDR = 0x8927, // Get hardware address.
SIOCGIFSLAVE = 0x8929, // Driver slaving support.
SIOCSIFSLAVE = 0x8930,
SIOCADDMULTI = 0x8931, // Multicast address lists.
SIOCDELMULTI = 0x8932,
SIOCGIFINDEX = 0x8933, // Name -> if_index mapping.
SIOGIFINDEX = SIOCGIFINDEX, // Misprint compatibility.
SIOCSIFPFLAGS = 0x8934, // Set/get extended flags set.
SIOCGIFPFLAGS = 0x8935,
SIOCDIFADDR = 0x8936, // Delete PA address.
SIOCSIFHWBROADCAST = 0x8937, // Set hardware broadcast address.
SIOCGIFCOUNT = 0x8938, // Get number of devices.
SIOCGIFBR = 0x8940, // Bridging support.
SIOCSIFBR = 0x8941, // Set bridging options.
SIOCGIFTXQLEN = 0x8942, // Get the tx queue length.
SIOCSIFTXQLEN = 0x8943, // Set the tx queue length.
SIOCDARP = 0x8953, // Delete ARP table entry.
SIOCGARP = 0x8954, // Get ARP table entry.
SIOCSARP = 0x8955, // Set ARP table entry.
SIOCDRARP = 0x8960, // Delete RARP table entry.
SIOCGRARP = 0x8961, // Get RARP table entry.
SIOCSRARP = 0x8962, // Set RARP table entry.
SIOCGIFMAP = 0x8970, // Get device parameters.
SIOCSIFMAP = 0x8971, // Set device parameters.
SIOCADDDLCI = 0x8980, // Create new DLCI device.
SIOCDELDLCI = 0x8981, // Delete DLCI device.
}

View File

@ -0,0 +1,27 @@
/* 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/. */
/*
* Copyright: Eugene Wissner 2018.
* 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/sys/posix/net/if_.d,
* tanya/sys/posix/net/if_.d)
*/
module tanya.sys.posix.net.if_;
version (TanyaNative):
enum size_t IF_NAMESIZE = 16;
struct ifreq
{
char[IF_NAMESIZE] ifr_name;
union
{
int ifr_ifindex;
}
}

View File

@ -0,0 +1,152 @@
/* 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/. */
/*
* Copyright: Eugene Wissner 2018.
* 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/sys/posix/socket.d,
* tanya/sys/posix/socket.d)
*/
module tanya.sys.posix.socket;
version (TanyaNative):
/*
* Protocol families.
*/
enum
{
PF_UNSPEC = 0, // Unspecified.
PF_LOCAL = 1, // Local to host (pipes and file-domain).
PF_UNIX = PF_LOCAL, // POSIX name for PF_LOCAL.
PF_FILE = PF_LOCAL, // Another non-standard name for PF_LOCAL.
PF_INET = 2, // IP protocol family.
PF_AX25 = 3, // Amateur Radio AX.25.
PF_IPX = 4, // Novell Internet Protocol.
PF_APPLETALK = 5, // Appletalk DDP.
PF_NETROM = 6, // Amateur radio NetROM.
PF_BRIDGE = 7, // Multiprotocol bridge.
PF_ATMPVC = 8, // ATM PVCs.
PF_X25 = 9, // Reserved for X.25 project.
PF_INET6 = 10, // IP version 6.
PF_ROSE = 11, // Amateur Radio X.25 PLP.
PF_DECnet = 12, // Reserved for DECnet project.
PF_NETBEUI = 13, // Reserved for 802.2LLC project.
PF_SECURITY = 14, // Security callback pseudo AF.
PF_KEY = 15, // PF_KEY key management API.
PF_NETLINK = 16, // Kernel user interface device.
PF_ROUTE = PF_NETLINK, // Alias to emulate 4.4BSD.
PF_PACKET = 17, // Packet family.
PF_ASH = 18, // Ash.
PF_ECONET = 19, // Acorn Econet.
PF_ATMSVC = 20, // ATM SVCs.
PF_RDS = 21, // RDS sockets.
PF_SNA = 22, // Linux SNA Project.
PF_IRDA = 23, // IRDA sockets.
PF_PPPOX = 24, // PPPoX sockets.
PF_WANPIPE = 25, // Wanpipe API sockets.
PF_LLC = 26, // Linux LLC.
PF_IB = 27, // Native InfiniBand address.
PF_MPLS = 28, // MPLS.
PF_CAN = 29, // Controller Area Network.
PF_TIPC = 30, // TIPC sockets.
PF_BLUETOOTH = 31, // Bluetooth sockets.
PF_IUCV = 32, // IUCV sockets.
PF_RXRPC = 33, // RxRPC sockets.
PF_ISDN = 34, // mISDN sockets.
PF_PHONET = 35, // Phonet sockets.
PF_IEEE802154 = 36, // IEEE 802.15.4 sockets.
PF_CAIF = 37, // CAIF sockets.
PF_ALG = 38, // Algorithm sockets.
PF_NFC = 39, // NFC sockets.
PF_VSOCK = 40, // vSockets.
PF_MAX = 41, // For now.
}
/*
* Address families.
*/
enum
{
AF_UNSPEC = PF_UNSPEC,
AF_LOCAL = PF_LOCAL,
AF_UNIX = PF_UNIX,
AF_FILE = PF_FILE,
AF_INET = PF_INET,
AF_AX25 = PF_AX25,
AF_IPX = PF_IPX,
AF_APPLETALK = PF_APPLETALK,
AF_NETROM = PF_NETROM,
AF_BRIDGE = PF_BRIDGE,
AF_ATMPVC = PF_ATMPVC,
AF_X25 = PF_X25,
AF_INET6 = PF_INET6,
AF_ROSE = PF_ROSE,
AF_DECnet = PF_DECnet,
AF_NETBEUI = PF_NETBEUI,
AF_SECURITY = PF_SECURITY,
AF_KEY = PF_KEY,
AF_NETLINK = PF_NETLINK,
AF_ROUTE = PF_ROUTE,
AF_PACKET = PF_PACKET,
AF_ASH = PF_ASH,
AF_ECONET = PF_ECONET,
AF_ATMSVC = PF_ATMSVC,
AF_RDS = PF_RDS,
AF_SNA = PF_SNA,
AF_IRDA = PF_IRDA,
AF_PPPOX = PF_PPPOX,
AF_WANPIPE = PF_WANPIPE,
AF_LLC = PF_LLC,
AF_IB = PF_IB,
AF_MPLS = PF_MPLS,
AF_CAN = PF_CAN,
AF_TIPC = PF_TIPC,
AF_BLUETOOTH = PF_BLUETOOTH,
AF_IUCV = PF_IUCV,
AF_RXRPC = PF_RXRPC,
AF_ISDN = PF_ISDN,
AF_PHONET = PF_PHONET,
AF_IEEE802154 = PF_IEEE802154,
AF_CAIF = PF_CAIF,
AF_ALG = PF_ALG,
AF_NFC = PF_NFC,
AF_VSOCK = PF_VSOCK,
AF_MAX = PF_MAX,
}
/*
* Types of sockets.
*/
enum
{
// Sequenced, reliable, connection-based byte streams.
SOCK_STREAM = 1,
// Connectionless, unreliable datagrams of fixed maximum length.
SOCK_DGRAM = 2,
// Raw protocol interface.
SOCK_RAW = 3,
// Reliably-delivered messages.
SOCK_RDM = 4,
// Sequenced, reliable, connection-based, datagrams of fixed maximum
// length.
SOCK_SEQPACKET = 5,
// Datagram Congestion Control Protocol.
SOCK_DCCP = 6,
// Linux specific way of getting packets at the dev level. For writing rarp
// and other similar things on the user level.
SOCK_PACKET = 10,
}
/*
* Flags to be ORed into the type parameter of socket and socketpair and used
* for the flags parameter of paccept.
*/
enum
{
SOCK_CLOEXEC = 0x80000, // Atomically set close-on-exec flag for the new descriptor(s).
SOCK_NONBLOCK = 0x800, // Atomically mark descriptor(s) as non-blocking.
}

View File

@ -30,6 +30,7 @@ version (Windows):
alias BYTE = ubyte;
alias TBYTE = wchar; // If Unicode, otherwise char.
alias CHAR = char; // Signed or unsigned char.
alias WCHAR = wchar;
alias TCHAR = wchar; // If Unicode, otherwise char.
alias SHORT = short;
alias USHORT = ushort;
@ -52,6 +53,10 @@ enum HANDLE INVALID_HANDLE_VALUE = cast(HANDLE) -1;
enum TRUE = 1;
enum FALSE = 0;
alias PSTR = CHAR*;
alias PWSTR = WCHAR*;
alias PTSTR = TCHAR*;
align(1) struct GUID
{
uint Data1;

View File

@ -0,0 +1,30 @@
/* 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/. */
/**
* Copyright: Eugene Wissner 2018.
* 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/sys/windows/ifdef.d,
* tanya/sys/windows/ifdef.d)
*/
module tanya.sys.windows.ifdef;
version (Windows):
import tanya.sys.windows.def;
union NET_LUID_LH
{
ulong Value;
ulong Info;
}
alias NET_LUID = NET_LUID_LH;
alias IF_LUID = NET_LUID_LH;
alias NET_IFINDEX = ULONG;
enum size_t IF_MAX_STRING_SIZE = 256;

View File

@ -0,0 +1,39 @@
/* 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/. */
/**
* Copyright: Eugene Wissner 2018.
* 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/sys/windows/iphlpapi.d,
* tanya/sys/windows/iphlpapi.d)
*/
module tanya.sys.windows.iphlpapi;
version (Windows):
import tanya.sys.windows.def;
import tanya.sys.windows.ifdef;
extern(Windows)
DWORD ConvertInterfaceNameToLuidA(const(CHAR)* InterfaceName,
NET_LUID* InterfaceLuid)
@nogc nothrow @system;
extern(Windows)
DWORD ConvertInterfaceLuidToIndex(const(NET_LUID)* InterfaceLuid,
NET_IFINDEX* InterfaceIndex)
@nogc nothrow @system;
extern(Windows)
DWORD ConvertInterfaceIndexToLuid(NET_IFINDEX InterfaceIndex,
NET_LUID* InterfaceLuid)
@nogc nothrow @system;
extern(Windows)
DWORD ConvertInterfaceLuidToNameA(const(NET_LUID)* InterfaceLuid,
PSTR InterfaceName,
size_t Length)
@nogc nothrow @system;

View File

@ -15,5 +15,7 @@ module tanya.sys.windows;
version (Windows):
public import tanya.sys.windows.def;
public import tanya.sys.windows.ifdef;
public import tanya.sys.windows.iphlpapi;
public import tanya.sys.windows.winbase;
public import tanya.sys.windows.winsock2;