diff options
| -rw-r--r-- | arch/x64/linux/memory/cmp.S | 7 | ||||
| -rw-r--r-- | arch/x64/linux/syscall.S | 12 | ||||
| -rw-r--r-- | source/tanya/algorithm/mutation.d | 60 | ||||
| -rw-r--r-- | source/tanya/container/string.d | 2 | ||||
| -rw-r--r-- | source/tanya/conv.d | 13 | ||||
| -rw-r--r-- | source/tanya/math/mp.d | 8 | ||||
| -rw-r--r-- | source/tanya/memory/package.d | 2 | ||||
| -rw-r--r-- | source/tanya/net/ip.d | 374 | ||||
| -rw-r--r-- | source/tanya/range/primitive.d | 19 |
9 files changed, 473 insertions, 24 deletions
diff --git a/arch/x64/linux/memory/cmp.S b/arch/x64/linux/memory/cmp.S index 64d3ca6..bd9f02e 100644 --- a/arch/x64/linux/memory/cmp.S +++ b/arch/x64/linux/memory/cmp.S @@ -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 diff --git a/arch/x64/linux/syscall.S b/arch/x64/linux/syscall.S index 7c00036..3d7f0b8 100644 --- a/arch/x64/linux/syscall.S +++ b/arch/x64/linux/syscall.S @@ -23,10 +23,10 @@ syscall1: // 2 parameters. - .globl _D5tanya3sys5linux7syscall7syscallFNbNilllZl - .type _D5tanya3sys5linux7syscall7syscallFNbNilllZl, @function + .globl _D5tanya3sys5linux7syscallQiFNbNilllZl + .type _D5tanya3sys5linux7syscallQiFNbNilllZl, @function -_D5tanya3sys5linux7syscall7syscallFNbNilllZl: +_D5tanya3sys5linux7syscallQiFNbNilllZl: movq %rdx, %rax syscall @@ -35,10 +35,10 @@ _D5tanya3sys5linux7syscall7syscallFNbNilllZl: // 6 parameters. - .globl _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl - .type _D5tanya3sys5linux7syscall7syscallFNbNilllllllZl, @function + .globl _D5tanya3sys5linux7syscallQiFNbNilllllllZl + .type _D5tanya3sys5linux7syscallQiFNbNilllllllZl, @function -_D5tanya3sys5linux7syscall7syscallFNbNilllllllZl: +_D5tanya3sys5linux7syscallQiFNbNilllllllZl: pushq %rbp movq %rsp, %rbp diff --git a/source/tanya/algorithm/mutation.d b/source/tanya/algorithm/mutation.d index df741cc..bf39b22 100644 --- a/source/tanya/algorithm/mutation.d +++ b/source/tanya/algorithm/mutation.d @@ -14,8 +14,9 @@ */ module tanya.algorithm.mutation; -import tanya.memory.op; +static import tanya.memory.op; import tanya.meta.trait; +import tanya.range; private void deinitialize(bool zero, T)(ref T value) { @@ -39,11 +40,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 +83,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 +275,53 @@ 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. + * + * Precondition: $(D_PARAM target) should be large enough to accept all + * $(D_PARAM source) elements. + */ +void 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(Source == Target) + && !hasElaborateCopyConstructor!E + && !hasElaborateAssign!E) + { + tanya.memory.op.copy(source, target); + } + else + { + for (; !source.empty; source.popFront()) + { + put(target, source.front); + } + } +} + +/// +@nogc nothrow pure @safe unittest +{ + int[2] actual; + int[2] expected = [2, 3]; + + copy(actual[], expected[]); + assert(actual == expected); +} diff --git a/source/tanya/container/string.d b/source/tanya/container/string.d index a523d68..c6bc4e7 100644 --- a/source/tanya/container/string.d +++ b/source/tanya/container/string.d @@ -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; diff --git a/source/tanya/conv.d b/source/tanya/conv.d index 561ccce..bce5401 100644 --- a/source/tanya/conv.d +++ b/source/tanya/conv.d @@ -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 { diff --git a/source/tanya/math/mp.d b/source/tanya/math/mp.d index ae365de..5a1e49a 100644 --- a/source/tanya/math/mp.d +++ b/source/tanya/math/mp.d @@ -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; diff --git a/source/tanya/memory/package.d b/source/tanya/memory/package.d index 7cf49e4..a98cf4a 100644 --- a/source/tanya/memory/package.d +++ b/source/tanya/memory/package.d @@ -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; diff --git a/source/tanya/net/ip.d b/source/tanya/net/ip.d new file mode 100644 index 0000000..5c375ba --- /dev/null +++ b/source/tanya/net/ip.d @@ -0,0 +1,374 @@ +/* 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.format; +import tanya.meta.trait; +import tanya.meta.transform; +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 isMulticast). + */ + 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 (isInputRange!R && isSomeChar!(ElementType!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()) + { + result.address |= readIntegral!ubyte(range) << shift; + if (range.empty || range.front != '.') + { + return typeof(return)(); + } + } + + result.address |= readIntegral!ubyte(range) << shift; + return range.empty ? typeof(return)(result) : typeof(return)(); +} + +/// +@nogc nothrow pure @safe unittest +{ + assert(address4("256.0.0.1").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); +} diff --git a/source/tanya/range/primitive.d b/source/tanya/range/primitive.d index c9134de..72824a5 100644 --- a/source/tanya/range/primitive.d +++ b/source/tanya/range/primitive.d @@ -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); @@ -963,7 +965,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 |
