1 // Copyright 2000 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS-IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 16 // 17 // 18 // This holds the encoding/decoding routines that used to live in netutil 19 20 module s2.util.coding.coder; 21 22 import std.array; 23 import std.bitmanip : append, read; 24 import std.range; 25 import std.traits; 26 27 /// Constructs a new Encoder using an `Appender!(ubyte[])` as the buffer. 28 auto makeEncoder() { 29 return new Encoder!(Appender!(ubyte[]))(appender(new ubyte[0])); 30 } 31 32 /// Constructs a new Encoder with a given output range as the buffer. 33 auto makeEncoder(ORangeT)(ORangeT r) { 34 return new Encoder!ORangeT(r); 35 } 36 37 /** 38 * Class for encoding data into a memory buffer 39 */ 40 class Encoder(ORangeT) 41 if (isOutputRange!(ORangeT, ubyte) && isOutputRange!(ORangeT, ubyte[])) { 42 public: 43 // Initialize encoder to encode into "buf" 44 this(ORangeT buf, size_t maxn = size_t.max) { 45 _buf = buf; 46 _limit = maxn; 47 } 48 49 final void reset(ORangeT buf, size_t maxn) { 50 _buf = buf; 51 _limit = maxn; 52 } 53 54 // Encoding routines. Note that these do not check bounds 55 final void put8(ubyte v) 56 in (avail() >= v.sizeof) { 57 _pos += v.sizeof; 58 _buf.append!ubyte(v); 59 } 60 61 final void put16(ushort v) 62 in (avail() >= v.sizeof) { 63 _pos += v.sizeof; 64 _buf.append!ushort(v); 65 } 66 67 final void put32(uint v) 68 in (avail() >= v.sizeof) { 69 _pos += v.sizeof; 70 _buf.append!uint(v); 71 } 72 73 final void put64(ulong v) 74 in (avail() >= v.sizeof) { 75 _pos += v.sizeof; 76 _buf.append!ulong(v); 77 } 78 79 final void putDouble(double v) 80 in (avail() >= v.sizeof) { 81 _pos += v.sizeof; 82 _buf.append!double(v); 83 } 84 85 /** 86 * Encodes into the OutputRange a raw object of a type with no indirections, 87 * e.g. no arrays, no classes, no pointers, etc. 88 * 89 * Note: The encoded byte order is machine dependent. 90 */ 91 final void putRaw(T)(T item) @trusted 92 if (!hasIndirections!T) { 93 ubyte[] itemBytes = *(cast(ubyte[typeof(item).sizeof]*) &item); 94 put(_buf, itemBytes); 95 _pos += item.sizeof; 96 } 97 98 /** 99 * Encodes into the OutputRange a range of objects in raw form. 100 * 101 * Note: The encoded byte order is machine dependent. 102 */ 103 final void putRaw(R)(R items) 104 if (isInputRange!R) { 105 foreach (ref item; items) { 106 ubyte[] itemBytes = *(cast(ubyte[typeof(item).sizeof]*) &item); 107 put(_buf, itemBytes); 108 _pos += item.sizeof; 109 } 110 } 111 112 /// Return number of bytes encoded so far 113 final size_t length() const { 114 return _pos; 115 } 116 117 /// Return number of bytes of space remaining in buffer 118 final size_t avail() const { 119 return _limit - _pos; 120 } 121 122 /** 123 * This interface ensures that at least "N" more bytes are available 124 * in the underlying buffer by resizing the buffer (if necessary). 125 * 126 * Note that no bounds checking is done on any of the put routines, 127 * so it is the client's responsibility to call Ensure() at 128 * appropriate intervals to ensure that enough space is available 129 * for the data being added. 130 */ 131 void ensure(size_t n) { 132 static if (hasMember!(ORangeT, "reserve")) { 133 _buf.reserve(n); 134 } 135 } 136 137 // Return ptr to start of encoded data. This pointer remains valid 138 // until reset or Ensure is called. 139 final ORangeT buffer() { 140 return _buf; 141 } 142 143 private: 144 // buf_ points into the orig_ buffer, just past the last encoded byte. 145 ORangeT _buf; 146 147 // limits_ points just past the last allocated byte in the orig_ buffer. 148 size_t _limit; 149 size_t _pos; 150 } 151 152 @("Encoder.put") 153 unittest { 154 auto enc = makeEncoder(); 155 // Use hex just to make the bytes easier to identify. 156 enc.put8(0x0a); 157 assert(enc.buffer().data == [0x0a]); 158 assert(enc.length() == 1); 159 160 enc.put16(0x0b0c); 161 assert(enc.buffer().data == [0x0a, 0x0b, 0x0c]); 162 assert(enc.length() == 3); 163 } 164 165 @("Encoder.putRaw") 166 unittest { 167 auto enc = makeEncoder(); 168 struct Thing { 169 int a; 170 double b; 171 } 172 173 enc.putRaw(Thing(1, 4.1)); 174 assert(enc.length() == Thing.sizeof); 175 176 auto things = [Thing(3, 2.1), Thing(2, 3.1)]; 177 enc.putRaw(things); 178 179 assert(enc.length() == (things.length + 1) * Thing.sizeof); 180 } 181 182 /// Constructs a new Decoder with the given input range as the buffer to read from. 183 auto makeDecoder(IRangeT)(IRangeT r) { 184 static if (hasLength!IRangeT) { 185 return new Decoder!IRangeT(r, r.length); 186 } else { 187 return new Decoder!IRangeT(r, size_t.max); 188 } 189 } 190 191 /// Constructs a new Decoder with the given input range and limit of byte to be read. 192 auto makeDecoder(IRangeT)(IRangeT r, size_t maxn) { 193 return new Decoder!IRangeT(r, maxn); 194 } 195 196 /* Class for decoding data from a memory buffer */ 197 class Decoder(IRangeT) 198 if (isInputRange!IRangeT && is(ElementType!IRangeT == ubyte)) { 199 public: 200 // Initialize decoder to decode from "buf" 201 this(IRangeT buf, size_t maxn = size_t.max) { 202 reset(buf, maxn); 203 } 204 205 void reset(IRangeT buf, size_t maxn) { 206 _buf = buf; 207 _limit = maxn; 208 } 209 210 // Decoding routines. Note that these do not check bounds 211 ubyte get8() 212 in (avail() >= ubyte.sizeof) { 213 _pos += ubyte.sizeof; 214 return _buf.read!ubyte(); 215 } 216 217 ushort get16() 218 in (avail() >= ushort.sizeof) { 219 _pos += ushort.sizeof; 220 return _buf.read!ushort(); 221 } 222 223 uint get32() 224 in (avail() >= uint.sizeof) { 225 _pos += uint.sizeof; 226 return _buf.read!uint(); 227 } 228 229 ulong get64() 230 in (avail() >= ulong.sizeof) { 231 _pos += ulong.sizeof; 232 return _buf.read!ulong(); 233 } 234 235 double getDouble() 236 in (avail() >= double.sizeof) { 237 _pos += double.sizeof; 238 return _buf.read!double(); 239 } 240 241 /** 242 * Decodes an object without indirections from raw bytes in the InputRange. 243 * 244 * Note: The decoding byte order is machine dependent. 245 */ 246 T getRaw(T)() @trusted 247 if (!hasIndirections!T) { 248 ubyte[T.sizeof] itemBytes = _buf.takeExactly(T.sizeof); 249 _buf = _buf.dropExactly(T.sizeof); 250 _pos += T.sizeof; 251 return *(cast(T*) &itemBytes); 252 } 253 254 T[] getRaw(T)(size_t n) { 255 T[] output; 256 output.reserve(n); 257 foreach (i; 0 .. n) { 258 output ~= getRaw!T(); 259 } 260 return output; 261 } 262 263 void skip(size_t n) 264 in (avail() >= n) { 265 _buf = _buf.dropExactly(n); 266 _pos += n; 267 } 268 269 /// Returns number of bytes decoded so far. 270 size_t pos() const { 271 return _pos; 272 } 273 274 /// Returns number of available bytes to read. 275 size_t avail() const { 276 return _limit - _pos; 277 } 278 279 private: 280 IRangeT _buf; 281 282 size_t _pos; 283 size_t _limit; 284 } 285 286 version(unittest) { 287 auto toNativeBytes(T)(T t) { 288 import std.system : endian, Endian; 289 import std.bitmanip : nativeToLittleEndian, nativeToBigEndian; 290 291 if (endian == Endian.littleEndian) { 292 return nativeToLittleEndian(t); 293 } else { 294 return nativeToBigEndian(t); 295 } 296 } 297 } 298 299 @("Decoder.get") 300 unittest { 301 auto dec = makeDecoder(cast(ubyte[]) [0x0a, 0x0b, 0x0c]); 302 assert(dec.avail() == 3); 303 assert(dec.get8() == 0x0a); 304 assert(dec.avail() == 2); 305 assert(dec.get16() == 0x0b0c); 306 assert(dec.avail() == 0); 307 assert(dec.pos() == 3); 308 } 309 310 @("Decoder.getRaw") 311 unittest { 312 struct Thing { 313 short a; 314 byte b; 315 short c; 316 } 317 318 // Pick some test data with clearly written bytes in big-endian order (most significant first). 319 short testA = 0x0102; 320 byte testB = 0x03; 321 short testC = 0x0405; 322 323 auto dec = makeDecoder(chain( 324 toNativeBytes(testA)[], 325 toNativeBytes(testB)[], [cast(ubyte) 0x00], 326 toNativeBytes(testC)[]) 327 .array); 328 329 assert(dec.avail() == Thing.sizeof); 330 assert(dec.getRaw!Thing() == Thing(testA, testB, testC)); 331 assert(dec.avail() == 0); 332 assert(dec.pos() == Thing.sizeof); 333 } 334 335 @("Decoder.getRaw") 336 unittest { 337 short[] testData = [0x0102, 0x0304, 0x0506]; 338 ubyte[] buffer; 339 foreach (data; testData) { 340 buffer ~= toNativeBytes(data); 341 } 342 auto dec = makeDecoder(buffer); 343 short[] result = dec.getRaw!short(3); 344 assert(result == testData); 345 }