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 }