1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 /** 6 * Internet utilities. 7 * 8 * Copyright: Eugene Wissner 2016-2020. 9 * License: $(LINK2 https://www.mozilla.org/en-US/MPL/2.0/, 10 * Mozilla Public License, v. 2.0). 11 * Authors: $(LINK2 mailto:info@caraus.de, Eugene Wissner) 12 * Source: $(LINK2 https://github.com/caraus-ecms/tanya/blob/master/source/tanya/net/inet.d, 13 * tanya/net/inet.d) 14 */ 15 module tanya.net.inet; 16 17 import tanya.meta.trait; 18 import tanya.meta.transform; 19 import tanya.range; 20 21 /** 22 * Represents an unsigned integer as an $(D_KEYWORD ubyte) range. 23 * 24 * The range is bidirectional. The byte order is always big-endian. 25 * 26 * It can accept any unsigned integral type but the value should fit 27 * in $(D_PARAM L) bytes. 28 * 29 * Params: 30 * L = Desired range length. 31 */ 32 struct NetworkOrder(uint L) 33 if (L > ubyte.sizeof && L <= ulong.sizeof) 34 { 35 static if (L > uint.sizeof) 36 { 37 private alias StorageType = ulong; 38 } 39 else static if (L > ushort.sizeof) 40 { 41 private alias StorageType = uint; 42 } 43 else static if (L > ubyte.sizeof) 44 { 45 private alias StorageType = ushort; 46 } 47 else 48 { 49 private alias StorageType = ubyte; 50 } 51 52 private StorageType value; 53 private size_t size = L; 54 55 invariant 56 { 57 assert(this.size <= L); 58 } 59 60 /** 61 * Constructs a new range. 62 * 63 * $(D_PARAM T) can be any unsigned type but $(D_PARAM value) cannot be 64 * larger than the maximum can be stored in $(D_PARAM L) bytes. Otherwise 65 * an assertion failure will be caused. 66 * 67 * Params: 68 * T = Value type. 69 * value = The value should be represented by this range. 70 * 71 * Precondition: $(D_INLINECODE value <= (2 ^^ (L * 8)) - 1). 72 */ 73 this(T)(T value) 74 if (isUnsigned!T) 75 in 76 { 77 assert(value <= (2 ^^ (L * 8)) - 1); 78 } 79 do 80 { 81 this.value = value & StorageType.max; 82 } 83 84 /** 85 * Returns: LSB. 86 * 87 * Precondition: $(D_INLINECODE length > 0). 88 */ 89 @property ubyte back() const 90 in 91 { 92 assert(this.length > 0); 93 } 94 do 95 { 96 return this.value & 0xff; 97 } 98 99 /** 100 * Returns: MSB. 101 * 102 * Precondition: $(D_INLINECODE length > 0). 103 */ 104 @property ubyte front() const 105 in 106 { 107 assert(this.length > 0); 108 } 109 do 110 { 111 return (this.value >> ((this.length - 1) * 8)) & 0xff; 112 } 113 114 /** 115 * Eliminates the LSB. 116 * 117 * Precondition: $(D_INLINECODE length > 0). 118 */ 119 void popBack() 120 in 121 { 122 assert(this.length > 0); 123 } 124 do 125 { 126 this.value >>= 8; 127 --this.size; 128 } 129 130 /** 131 * Eliminates the MSB. 132 * 133 * Precondition: $(D_INLINECODE length > 0). 134 */ 135 void popFront() 136 in 137 { 138 assert(this.length > 0); 139 } 140 do 141 { 142 this.value &= StorageType.max >> ((StorageType.sizeof - this.length) * 8); 143 --this.size; 144 } 145 146 /** 147 * Returns: Copy of this range. 148 */ 149 typeof(this) save() const 150 { 151 return this; 152 } 153 154 /** 155 * Returns: Whether the range is empty. 156 */ 157 @property bool empty() const 158 { 159 return this.length == 0; 160 } 161 162 /** 163 * Returns: Byte length. 164 */ 165 @property size_t length() const 166 { 167 return this.size; 168 } 169 } 170 171 /// 172 @nogc nothrow pure @safe unittest 173 { 174 auto networkOrder = NetworkOrder!3(0xae34e2u); 175 assert(!networkOrder.empty); 176 assert(networkOrder.front == 0xae); 177 178 networkOrder.popFront(); 179 assert(networkOrder.length == 2); 180 assert(networkOrder.front == 0x34); 181 assert(networkOrder.back == 0xe2); 182 183 networkOrder.popBack(); 184 assert(networkOrder.length == 1); 185 assert(networkOrder.front == 0x34); 186 assert(networkOrder.front == 0x34); 187 188 networkOrder.popFront(); 189 assert(networkOrder.empty); 190 } 191 192 /** 193 * Converts the $(D_KEYWORD ubyte) input range $(D_PARAM range) to 194 * $(D_PARAM T). 195 * 196 * The byte order of $(D_PARAM r) is assumed to be big-endian. The length 197 * cannot be larger than $(D_INLINECODE T.sizeof). Otherwise an assertion 198 * failure will be caused. 199 * 200 * Params: 201 * T = Desired return type. 202 * R = Range type. 203 * range = Input range. 204 * 205 * Returns: Integral representation of $(D_PARAM range) with the host byte 206 * order. 207 */ 208 T toHostOrder(T = size_t, R)(R range) 209 if (isInputRange!R 210 && !isInfinite!R 211 && is(Unqual!(ElementType!R) == ubyte) 212 && isUnsigned!T) 213 { 214 T ret; 215 ushort pos = T.sizeof * 8; 216 217 for (; !range.empty && range.front == 0; pos -= 8, range.popFront()) 218 { 219 } 220 for (; !range.empty; range.popFront()) 221 { 222 assert(pos != 0); 223 pos -= 8; 224 ret |= (cast(T) range.front) << pos; 225 } 226 227 return ret >> pos; 228 } 229 230 /// 231 @nogc nothrow pure @safe unittest 232 { 233 const value = 0xae34e2u; 234 auto networkOrder = NetworkOrder!4(value); 235 assert(networkOrder.toHostOrder() == value); 236 }