summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorEugen Wissner <belka@caraus.de>2017-04-02 09:29:54 +0200
committerEugen Wissner <belka@caraus.de>2017-04-02 09:29:54 +0200
commit445b872e91e0256bce27fc9c7f271f728ed5bbee (patch)
tree8e8473d7c0f89334ce0d5346eb5581a02c340ab5 /source
parent5e16fe98d6d36c97e46809169ba16d03a0f72e75 (diff)
downloadtanya-445b872e91e0256bce27fc9c7f271f728ed5bbee.tar.gz
Add tanya.network.inet.NetworkOrder
NetworkOrder converts an integral type into a bidirectional range with big-endian byte order.
Diffstat (limited to 'source')
-rw-r--r--source/tanya/network/inet.d292
-rw-r--r--source/tanya/network/package.d1
2 files changed, 293 insertions, 0 deletions
diff --git a/source/tanya/network/inet.d b/source/tanya/network/inet.d
new file mode 100644
index 0000000..97da907
--- /dev/null
+++ b/source/tanya/network/inet.d
@@ -0,0 +1,292 @@
+/* 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 utilities.
+ *
+ * Copyright: Eugene Wissner 2016-2017.
+ * 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)
+ */
+module tanya.network.inet;
+
+import std.algorithm.comparison;
+import std.traits;
+
+version (assert)
+{
+ import std.math;
+}
+
+version (unittest)
+{
+ import std.math;
+ import std.range.primitives;
+
+ version (Windows)
+ {
+ import core.sys.windows.winsock2;
+ version = PlattformUnittest;
+ }
+ else version (Posix)
+ {
+ import core.sys.posix.arpa.inet;
+ version = PlattformUnittest;
+ }
+}
+
+/**
+ * Represents an unsigned integer as an $(D_KEYWORD ubyte) range.
+ *
+ * The range is bidirectional. The byte order is always big-endian.
+ */
+struct NetworkOrder
+{
+ private uint value;
+ private size_t size;
+
+ const pure nothrow @safe @nogc invariant
+ {
+ assert(this.size <= uint.sizeof);
+ }
+
+ /**
+ * Constructs a new range.
+ *
+ * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) shouldn't be
+ * larger than the maximum can be stored in $(D_PARAM length) bytes.
+ * Otherwise an assertion failure will be caused.
+ *
+ * If $(D_PARAM length) isn't specified, it is inferred from the
+ * $(D_INLINECODE T.sizeof).
+ *
+ * If $(D_PARAM T) is $(D_KEYWORD ulong), $(D_PARAM value) should be less
+ * than or equal to $(D_INLINECODE uint.max).
+ *
+ * Params:
+ * T = Value type.
+ * value = The value should be iterated over.
+ * length = $(D_PARAM value) size in bytes.
+ *
+ * Precondition: $(D_INLINECODE length < uint.sizeof
+ * && value <= 2 ^ (length * 8) - 1).
+ */
+ this(T)(const T value, const size_t length)
+ if (isIntegral!T)
+ in
+ {
+ assert(length <= uint.sizeof);
+ assert(value >= 0);
+ assert(value <= pow(2, length * 8) - 1);
+ }
+ body
+ {
+ this.value = value & uint.max;
+ this.size = length;
+ }
+
+ /// Ditto.
+ this(T)(const T value)
+ if (isIntegral!T)
+ {
+ this(value, min(T.sizeof, uint.sizeof));
+ }
+
+ /**
+ * Returns: LSB.
+ *
+ * Precondition: $(D_INLINECODE length > 0).
+ */
+ @property ubyte back() const pure nothrow @safe @nogc
+ in
+ {
+ assert(this.length > 0);
+ }
+ body
+ {
+ return this.value & 0xff;
+ }
+
+ /**
+ * Returns: MSB.
+ *
+ * Precondition: $(D_INLINECODE length > 0).
+ */
+ @property ubyte front() const pure nothrow @safe @nogc
+ in
+ {
+ assert(this.length > 0);
+ }
+ body
+ {
+ return (this.value >> ((this.length - 1) * 8)) & 0xff;
+ }
+
+ /**
+ * Eliminates the LSB.
+ *
+ * Precondition: $(D_INLINECODE length > 0).
+ */
+ void popBack() pure nothrow @safe @nogc
+ in
+ {
+ assert(this.length > 0);
+ }
+ body
+ {
+ this.value >>= 8;
+ --this.size;
+ }
+
+ /**
+ * Eliminates the MSB.
+ *
+ * Precondition: $(D_INLINECODE length > 0).
+ */
+ void popFront() pure nothrow @safe @nogc
+ in
+ {
+ assert(this.length > 0);
+ }
+ body
+ {
+ this.value &= uint.max >> ((4 - this.length) * 8);
+ --this.size;
+ }
+
+ /**
+ * Returns: Copy of this range.
+ */
+ typeof(this) save() const pure nothrow @safe @nogc
+ {
+ return this;
+ }
+
+ /**
+ * Returns: Whether the range is empty.
+ */
+ @property bool empty() const pure nothrow @safe @nogc
+ {
+ return this.length == 0;
+ }
+
+ /**
+ * Returns: Byte length.
+ */
+ @property size_t length() const pure nothrow @safe @nogc
+ {
+ return this.size;
+ }
+}
+
+///
+pure nothrow @safe @nogc unittest
+{
+ auto networkOrder = NetworkOrder(0xae34e2u, 3);
+ assert(!networkOrder.empty);
+ assert(networkOrder.front == 0xae);
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 2);
+ assert(networkOrder.front == 0x34);
+ assert(networkOrder.back == 0xe2);
+
+ networkOrder.popBack();
+ assert(networkOrder.length == 1);
+ assert(networkOrder.front == 0x34);
+ assert(networkOrder.front == 0x34);
+
+ networkOrder.popFront();
+ assert(networkOrder.empty);
+}
+
+// Static.
+private unittest
+{
+ static assert(isBidirectionalRange!NetworkOrder);
+}
+
+// Tests against the system's htonl, htons.
+version (PlattformUnittest)
+{
+ private unittest
+ {
+ for (uint counter; counter <= 8 * uint.sizeof; ++counter)
+ {
+ const value = pow(2, counter) - 1;
+ const inNetworkOrder = htonl(value);
+ const p = cast(ubyte*) &inNetworkOrder;
+ auto networkOrder = NetworkOrder(value);
+
+ assert(networkOrder.length == 4);
+ assert(!networkOrder.empty);
+ assert(networkOrder.front == *p);
+ assert(networkOrder.back == *(p + 3));
+
+ networkOrder.popBack();
+ assert(networkOrder.length == 3);
+ assert(networkOrder.front == *p);
+ assert(networkOrder.back == *(p + 2));
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 2);
+ assert(networkOrder.front == *(p + 1));
+ assert(networkOrder.back == *(p + 2));
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 1);
+ assert(networkOrder.front == *(p + 2));
+ assert(networkOrder.back == *(p + 2));
+
+ networkOrder.popBack();
+ assert(networkOrder.length == 0);
+ assert(networkOrder.empty);
+ }
+
+ for (ushort counter; counter <= 8 * ushort.sizeof; ++counter)
+ {
+ const value = cast(ushort) (pow(2, counter) - 1);
+ const inNetworkOrder = htons(value);
+ const p = cast(ubyte*) &inNetworkOrder;
+
+ auto networkOrder = NetworkOrder(value);
+
+ assert(networkOrder.length == 2);
+ assert(!networkOrder.empty);
+ assert(networkOrder.front == *p);
+ assert(networkOrder.back == *(p + 1));
+
+ networkOrder.popBack();
+ assert(networkOrder.length == 1);
+ assert(networkOrder.front == *p);
+ assert(networkOrder.back == *p);
+
+ networkOrder.popBack();
+ assert(networkOrder.length == 0);
+ assert(networkOrder.empty);
+
+ networkOrder = NetworkOrder(value);
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 1);
+ assert(networkOrder.front == *(p + 1));
+ assert(networkOrder.back == *(p + 1));
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 0);
+ assert(networkOrder.empty);
+ }
+
+ auto networkOrder = NetworkOrder(255u, 1);
+ assert(networkOrder.length == 1);
+ assert(!networkOrder.empty);
+ assert(networkOrder.front == 0xff);
+ assert(networkOrder.back == 0xff);
+
+ networkOrder.popFront();
+ assert(networkOrder.length == 0);
+ assert(networkOrder.empty);
+ }
+}
diff --git a/source/tanya/network/package.d b/source/tanya/network/package.d
index 96b987c..14a6752 100644
--- a/source/tanya/network/package.d
+++ b/source/tanya/network/package.d
@@ -12,5 +12,6 @@
*/
module tanya.network;
+public import tanya.network.inet;
public import tanya.network.socket;
public import tanya.network.url;