1 /**
2 Copyright: Copyright (c) 2014-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 
6 Concise Binary Object Representation (CBOR) for D lang.
7 
8 The Concise Binary Object Representation (CBOR) is a data format
9 whose design goals include the possibility of extremely small code
10 size, fairly small message size, and extensibility without the need
11 for version negotiation.  These design goals make it different from
12 earlier binary serializations such as ASN.1 and MessagePack.
13 
14 Standards: Conforms to RFC 7049.
15 */
16 
17 module cbor;
18 
19 private import std..string : format;
20 private import std.traits : Unqual, hasUDA, isArray, isAssociativeArray, isBoolean, isDynamicArray,
21 	isExpressionTuple, isFloatingPoint, isIntegral, isSomeChar, isStaticArray, isUnsigned;
22 public import std.typecons : Flag, Yes, No;
23 private import std.range : ElementEncodingType, hasLength, save, tee;
24 private import std.conv : to;
25 private import std.utf : byChar;
26 
27 version(unittest) {
28 	version = Cbor_Debug;
29 }
30 
31 //------------------------------------------------------------------------------
32 //		EEEEEEE NN   NN  CCCCC   OOOOO  DDDDD   IIIII NN   NN   GGGG
33 //		EE      NNN  NN CC    C OO   OO DD  DD   III  NNN  NN  GG  GG
34 //		EEEEE   NN N NN CC      OO   OO DD   DD  III  NN N NN GG
35 //		EE      NN  NNN CC    C OO   OO DD   DD  III  NN  NNN GG   GG
36 //		EEEEEEE NN   NN  CCCCC   OOOO0  DDDDDD  IIIII NN   NN  GGGGGG
37 //------------------------------------------------------------------------------
38 
39 private import std.range : isInputRange, isOutputRange, ElementType;
40 private import std.typecons : isTuple;
41 
42 //------------------------------------------------------------------------------
43 // Simple types (int, float, bool, null, undefined, break, simple)
44 //------------------------------------------------------------------------------
45 
46 // Encode integer types as separate type or as part of arrays or map.
47 private size_t encodeLongType(R)(auto ref R sink, ubyte majorType, ulong length)
48 	if(isOutputRange!(R, ubyte))
49 {
50 	import std.bitmanip : nativeToBigEndian;
51 
52 	majorType <<= 5;
53 	if (length < 24) {
54 		putChecked(sink, cast(ubyte)(majorType | length));
55 		return 1;
56 	} else if (length <= ubyte.max) {
57 		putChecked(sink, cast(ubyte)(majorType | 24));
58 		putChecked(sink, cast(ubyte)length);
59 		return 2;
60 	} else if (length <= ushort.max) {
61 		putChecked(sink, cast(ubyte)(majorType | 25));
62 		putChecked(sink, nativeToBigEndian!ushort(cast(ushort)length)[]);
63 		return 3;
64 	} else if (length <= uint.max) {
65 		putChecked(sink, cast(ubyte)(majorType | 26));
66 		putChecked(sink, nativeToBigEndian!uint(cast(uint)length)[]);
67 		return 5;
68 	} else { // if (length <= ulong.max)
69 		putChecked(sink, cast(ubyte)(majorType | 27));
70 		putChecked(sink, nativeToBigEndian!ulong(cast(ulong)length)[]);
71 		return 9;
72 	}
73 }
74 
75 /// Encodes integer.
76 size_t encodeCborInt(R, E)(auto ref R sink, E value)
77 	if(isOutputRange!(R, ubyte) && isIntegral!E)
78 {
79 	ulong val;
80 	ubyte majorType;
81 
82 	if (value < 0) {
83 		val = -cast(long)value - 1;
84 		majorType = 1;
85 	} else {
86 		val = value;
87 		majorType = 0;
88 	}
89 
90 	return encodeLongType(sink, majorType, val);
91 }
92 
93 /// Encodes floating.
94 size_t encodeCborFloat(R, E)(auto ref R sink, E value)
95 	if(isOutputRange!(R, ubyte) && isFloatingPoint!E)
96 {
97 	import std.bitmanip : nativeToBigEndian;
98 	enum majorType = 7 << 5;
99 
100 	static if (is(Unqual!E == float))
101 	{
102 		__FloatRep flt;
103 		flt.f = value;
104 		putChecked(sink, cast(ubyte)(majorType | 26));
105 		putChecked(sink, nativeToBigEndian!uint(flt.u)[]);
106 		return 5;
107 	}
108 	else static if (is(Unqual!E == double) || is(Unqual!E == real))
109 	{
110 		__DoubleRep dbl;
111 		dbl.d = cast(double)value;
112 		putChecked(sink, cast(ubyte)(majorType | 27));
113 		putChecked(sink, nativeToBigEndian!ulong(dbl.u)[]);
114 		return 9;
115 	}
116 }
117 
118 /// Encodes boolean.
119 size_t encodeCborBool(R, E)(auto ref R sink, E value)
120 	if(isOutputRange!(R, ubyte) && isBoolean!E)
121 {
122 	if (value)
123 		putChecked(sink, cast(ubyte)0xf5);
124 	else
125 		putChecked(sink, cast(ubyte)0xf4);
126 	return 1;
127 }
128 
129 /// Encodes null.
130 size_t encodeCborNull(R)(auto ref R sink)
131 	if(isOutputRange!(R, ubyte))
132 {
133 	putChecked(sink, cast(ubyte)0xf6);
134 	return 1;
135 }
136 
137 /// Encodes undefined.
138 size_t encodeCborUndefined(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
139 {
140 	putChecked(sink, cast(ubyte)0xf7);
141 	return 1;
142 }
143 
144 /// Encodes break. Use after all items of indefinite-length sequence were encoded.
145 size_t encodeCborBreak(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
146 {
147 	putChecked(sink, cast(ubyte)0xff);
148 	return 1;
149 }
150 
151 /// Encodes simple. Simple data type is essentially a number that has special meaning.
152 /// Value must lie in range [0..23] or [32..255].
153 size_t encodeCborSimple(R)(auto ref R sink, ubyte simple) if(isOutputRange!(R, ubyte))
154 {
155 	enum ubyte majorType = 7 << 5;
156 	if (simple < 24) {
157 		putChecked(sink, cast(ubyte)(majorType | simple));
158 		return 1;
159 	} else if (simple > 31) {
160 		putChecked(sink, cast(ubyte)0xf8);
161 		putChecked(sink, simple);
162 		return 2;
163 	} else {
164 		assert(false, format("Invalid simple value %s", simple));
165 	}
166 }
167 
168 //------------------------------------------------------------------------------
169 // Complex data (byte-string, text-string, array, map)
170 //------------------------------------------------------------------------------
171 // Headers
172 //------------------------------------------------------------------------------
173 
174 size_t encodeCborBytesHeader(R)(auto ref R sink, ulong bytesLength) if(isOutputRange!(R, ubyte))
175 {
176 	return encodeLongType(sink, 2, bytesLength);
177 }
178 
179 size_t encodeCborBytesHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
180 {
181 	putChecked(sink, cast(ubyte)0x5f);
182 	return 1;
183 }
184 
185 size_t encodeCborStringHeader(R)(auto ref R sink, ulong textLength) if(isOutputRange!(R, ubyte))
186 {
187 	return encodeLongType(sink, 3, textLength);
188 }
189 
190 size_t encodeCborStringHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
191 {
192 	putChecked(sink, cast(ubyte)0x7f);
193 	return 1;
194 }
195 
196 size_t encodeCborArrayHeader(R)(auto ref R sink, ulong arrayLength) if(isOutputRange!(R, ubyte))
197 {
198 	return encodeLongType(sink, 4, arrayLength);
199 }
200 
201 size_t encodeCborArrayHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
202 {
203 	putChecked(sink, cast(ubyte)0x9f);
204 	return 1;
205 }
206 
207 size_t encodeCborMapHeader(R)(auto ref R sink, ulong mapLength) if(isOutputRange!(R, ubyte))
208 {
209 	return encodeLongType(sink, 5, mapLength);
210 }
211 
212 size_t encodeCborMapHeader(R)(auto ref R sink) if(isOutputRange!(R, ubyte))
213 {
214 	putChecked(sink, cast(ubyte)0xbf);
215 	return 1;
216 }
217 
218 //------------------------------------------------------------------------------
219 // Items
220 //------------------------------------------------------------------------------
221 
222 /// Writes range of ubyte to the sink. Needs to go after a call to encodeCborBytesHeader.
223 /// The length of supplied range must be equal to one provided to encodeCborBytesHeader.
224 size_t encodeCborBytesItems(R, D)(auto ref R sink, D bytes)
225 	if(isOutputRange!(R, ubyte) &&
226 		(isArray!D || isInputRange!D) && is(Unqual!(ElementType!D) == ubyte))
227 {
228 	static if (hasLength!D) {
229 		static if (isStaticArray!D)
230 			putChecked(sink, bytes[]);
231 		else
232 			putChecked(sink, bytes);
233 		return bytes.length;
234 	} else {
235 		size_t count;
236 		putChecked(sink, tee!((a){++count;})(bytes));
237 		return count;
238 	}
239 }
240 
241 //------------------------------------------------------------------------------
242 // Tag
243 //------------------------------------------------------------------------------
244 
245 ///
246 size_t encodeCborTag(R)(auto ref R sink, ulong value) if(isOutputRange!(R, ubyte))
247 {
248 	return encodeLongType(sink, 6, value);
249 }
250 
251 //------------------------------------------------------------------------------
252 //	 SSSSS  EEEEEEE RRRRRR  IIIII   AAA   LL      IIIII ZZZZZ   AAA   TTTTTTT IIIII  OOOOO  NN   NN
253 //	SS      EE      RR   RR  III   AAAAA  LL       III     ZZ  AAAAA    TTT    III  OO   OO NNN  NN
254 //	 SSSSS  EEEEE   RRRRRR   III  AA   AA LL       III    ZZ  AA   AA   TTT    III  OO   OO NN N NN
255 //	     SS EE      RR  RR   III  AAAAAAA LL       III   ZZ   AAAAAAA   TTT    III  OO   OO NN  NNN
256 //	 SSSSS  EEEEEEE RR   RR IIIII AA   AA LLLLLLL IIIII ZZZZZ AA   AA   TTT   IIIII  OOOO0  NN   NN
257 //------------------------------------------------------------------------------
258 
259 /// Encodes value E into output range sink.
260 /// Returns number of bytes written to sink.
261 /// If flatten flag is yes then static arrays and structs will be encoded in place without headers.
262 size_t encodeCbor(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, auto ref const E value)
263 	if(isOutputRange!(R, ubyte))
264 {
265 	import std.typecons : isTuple;
266 
267 	static if (isIntegral!E)
268 	{
269 		return encodeCborInt(sink, value);
270 	}
271 	else static if (isSomeChar!E)
272 	{
273 		return encodeCborInt(sink, cast(ulong)value);
274 	}
275 	else static if (isFloatingPoint!E)
276 	{
277 		return encodeCborFloat(sink, value);
278 	}
279 	else static if (isBoolean!E)
280 	{
281 		return encodeCborBool(sink, value);
282 	}
283 	else static if (is(Unqual!E == typeof(null)))
284 	{
285 		return encodeCborNull(sink);
286 	}
287 	else static if ((isArray!E || isInputRange!E) && is(Unqual!(ElementType!E) == ubyte))
288 	{
289 		return encodeCborBytes!(flatten)(sink, value);
290 	}
291 	else static if ((isArray!E || isInputRange!E) && isSomeChar!(Unqual!(ElementEncodingType!E)))
292 	{
293 		return encodeCborString!(flatten)(sink, value);
294 	}
295 	else static if (isInputRange!E || isArray!E || isTuple!E ||
296 		is(E == class) || is(E == struct))
297 	{
298 		return encodeCborArray!(flatten)(sink, value);
299 	}
300 	else static if (isAssociativeArray!E)
301 	{
302 		return encodeCborMap(sink, value);
303 	}
304 	else
305 	{
306 		static assert(false, "Unable to encode " ~ E.stringof);
307 	}
308 }
309 
310 /// Encodes range of ubytes.
311 size_t encodeCborBytes(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value)
312 	if(isOutputRange!(R, ubyte) &&
313 		(isArray!E || isInputRange!E) && is(Unqual!(ElementType!E) == ubyte))
314 {
315 	size_t size = 0;
316 	// drop type and length for static arrays in flatten mode
317 	static if (!needsFlattening!(E, flatten))
318 	{
319 		size = encodeCborBytesHeader(sink, value.length);
320 	}
321 	size += encodeCborBytesItems(sink, value);
322 	return size;
323 }
324 
325 /// Encodes string.
326 size_t encodeCborString(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value)
327 	if(isOutputRange!(R, ubyte) && isSomeChar!(Unqual!(ElementEncodingType!E)))
328 {
329 	size_t size = 0;
330 	// drop type and length for static arrays in flatten mode
331 	static if (!needsFlattening!(E, flatten))
332 	{
333 		size = encodeCborStringHeader(sink, value.length);
334 	}
335 	size += value.length;
336 
337 	static if (is(Unqual!(ElementEncodingType!E) == char))
338 	{
339 		putChecked(sink, cast(ubyte[])value);
340 	}
341 	else
342 	{
343 		foreach(char elem; value[].byChar)
344 		{
345 			putChecked(sink, cast(ubyte)elem);
346 		}
347 	}
348 	return size;
349 }
350 
351 /// Encodes array of any items or a tuple as cbor array.
352 size_t encodeCborArray(Flag!"Flatten" flatten = No.Flatten, R, E)(auto ref R sink, E value)
353 	if(isOutputRange!(R, ubyte) &&
354 	(isInputRange!E || isArray!E || isTuple!E))
355 {
356 	static if (isArray!E && is(Unqual!(ElementType!E) == void)) // accept []
357 	{
358 		return encodeCborArrayHeader(sink, 0);
359 	}
360 	else
361 	{
362 		size_t size = 0;
363 		// drop type and length for static arrays and expression tuples in flatten mode
364 		static if (!needsFlattening!(E, flatten))
365 			size = encodeCborArrayHeader(sink, value.length);
366 
367 		foreach(item; value)
368 			size += encodeCbor!(flatten)(sink, item);
369 		return size;
370 	}
371 }
372 
373 /// Encodes structs and classes as cbor array.
374 size_t encodeCborArray(Flag!"Flatten" flatten = No.Flatten, R, A)(auto ref R sink, A aggregate)
375 	if(isOutputRange!(R, ubyte) &&
376 		(is(A == struct) || is(A == class)) &&
377 		!isTuple!A)
378 {
379 	return encodeCborAggregate!(Flag!"WithFieldName".no, flatten)(sink, aggregate);
380 }
381 
382 /// Encodes asociative array as cbor map.
383 size_t encodeCborMap(R, E)(auto ref R sink, E value)
384 	if(isOutputRange!(R, ubyte) && isAssociativeArray!E)
385 {
386 	size_t size = encodeCborMapHeader(sink, value.length);
387 	foreach(key, element; value)
388 	{
389 		size += encodeCbor(sink, key);
390 		size += encodeCbor(sink, element);
391 	}
392 	return size;
393 }
394 
395 /// Encodes structs and classes as cbor map.
396 /// Note, that decoding of structs and classes from maps is not supported (yet).
397 size_t encodeCborMap(Flag!"Flatten" flatten = Flag!"Flatten".no, R, A)(auto ref R sink, A aggregate)
398 	if(isOutputRange!(R, ubyte) && (is(A == struct) || is(A == class)) && !isTuple!A)
399 {
400 	return encodeCborAggregate!(Flag!"WithFieldName".yes, flatten)(sink, aggregate);
401 }
402 
403 /// Encodes classes and structs. If withFieldName is yes, than value is encoded as map.
404 /// If withFieldName is no, then value is encoded as an array.
405 /// If flatten flag is yes then static arrays and structs will be encoded in place without headers.
406 size_t encodeCborAggregate(
407 	Flag!"WithFieldName" withFieldName,
408 	Flag!"Flatten" flatten = Flag!"Flatten".no,
409 	R,
410 	A)(
411 	auto ref R sink,
412 	auto ref A aggregate)
413 	if (isOutputRange!(R, ubyte) && (is(A == struct) || is(A == class)))
414 {
415 	size_t size;
416 	static if (is(A == class))
417 		if (aggregate is null)
418 			return encodeCbor(sink, null);
419 
420 	// flatten structs only
421 	static if (!needsFlattening!(A, flatten))
422 	{
423 		static if (withFieldName)
424 			size += encodeCborMapHeader(sink, numEncodableMembers!A);
425 		else
426 			size += encodeCborArrayHeader(sink, numEncodableMembers!A);
427 	}
428 
429 	foreach(i, member; aggregate.tupleof)
430 	{
431 		static if (isEncodedField!(aggregate.tupleof[i]))
432 		{
433 			// when encoded as map order is unspecified and flatten is not possible
434 			static if (withFieldName)
435 			{
436 				size += encodeCborString(sink, __traits(identifier, aggregate.tupleof[i]));
437 				size += encodeCbor(sink, member);
438 			}
439 			else
440 				size += encodeCbor!flatten(sink, member);
441 		}
442 	}
443 	return size;
444 }
445 
446 //------------------------------------------------------------------------------
447 //		 SSSSS  TTTTTTT  OOOOO  RRRRRR    AAA     GGGG  EEEEEEE
448 //		SS        TTT   OO   OO RR   RR  AAAAA   GG  GG EE
449 //		 SSSSS    TTT   OO   OO RRRRRR  AA   AA GG      EEEEE
450 //		     SS   TTT   OO   OO RR  RR  AAAAAAA GG   GG EE
451 //		 SSSSS    TTT    OOOO0  RR   RR AA   AA  GGGGGG EEEEEEE
452 //------------------------------------------------------------------------------
453 
454 ///
455 enum CborTokenType : ubyte
456 {
457 	/// CborToken.uinteger stores array length.
458 	/// Indefinite-length array has no length.
459 	/// Header is followed by zero or more data items, terminated by break.
460 	arrayHeader = 0b000,
461 	arrayIndefiniteHeader = 0b001, /// ditto
462 
463 	/// CborToken.uinteger stores map's pair count.
464 	/// Indefinite-length map has no length.
465 	/// Header is followed by zero or more pairs, terminated by break.
466 	mapHeader = 0b010,
467 	mapIndefiniteHeader = 0b011, /// ditto
468 
469 	/// CborToken.uinteger stores byte string length.
470 	/// Indefinite-length byte string has no length.
471 	/// Header is follower by zero or more definite-length byte strings terminated by break.
472 	bytesHeader = 0b100,
473 	bytesIndefiniteHeader = 0b101, /// ditto
474 
475 	/// CborToken.uinteger stores text string length.
476 	/// Indefinite-length text string has no length.
477 	/// Header is follower by zero or more definite-length text strings terminated by break.
478 	textHeader = 0b110,
479 	textIndefiniteHeader = 0b111, /// ditto
480 
481 	undefined, /// CborToken value is undefined.
482 	nil, /// CborToken value is null.
483 	boolean, /// CborToken.boolean stores actual value.
484 	tag, /// uinteger stores tag value.
485 	simple, /// Simple value is integer in range [0-255], stored in uinteger.
486 	breakCode, /// "break" stop code, used to terminate indefinite-length sequences.
487 
488 	posinteger, /// CborToken.uinteger stores actual value.
489 	neginteger, /// CborToken.integer stores actual value.
490 	floating, /// CborToken.floating stores actual value.
491 }
492 
493 ///
494 struct CborToken
495 {
496 	CborTokenType type;
497 	union
498 	{
499 		bool boolean;
500 		long integer;
501 		double floating;
502 		// used for storing positive integers, array, map, raw and text size
503 		ulong uinteger;
504 	}
505 
506 	this(CborTokenType t) @nogc  {
507 		type = t;
508 	}
509 	this(CborTokenType t, bool v) @nogc {
510 		type = t;
511 		boolean = v;
512 	}
513 	this(CborTokenType t, long v) @nogc {
514 		type = t;
515 		integer = v;
516 	}
517 	this(CborTokenType t, double v) @nogc {
518 		type = t;
519 		floating = v;
520 	}
521 
522 	bool opEquals(const CborToken other) const @nogc {
523 		ubyte[] thisRep = (cast(ubyte*)(&this))[0..this.sizeof];
524 		ubyte[] otherRep = (cast(ubyte*)(&other))[0..other.sizeof];
525 		return thisRep == otherRep;
526 	}
527 
528 	string toString() const
529 	{
530 		import std..string : format;
531 		final switch(type) with(CborTokenType)
532 		{
533 			case boolean: return format("CborToken(%s)", boolean);
534 			case nil: return "CborToken(null)";
535 			case undefined: return "CborToken(undefined)";
536 			case tag: return format("CborToken(tag, %s)", type, uinteger);
537 			case simple: return format("CborToken(simple, %s)", uinteger);
538 			case breakCode: return format("CborToken(break)");
539 			case posinteger: return format("CborToken(%s, %s)", type, uinteger);
540 			case neginteger: return format("CborToken(%s, %s)", type, integer);
541 			case floating: return format("CborToken(%s, %s)", type, floating);
542 			case arrayHeader: return format("CborToken(array, %s)", uinteger);
543 			case arrayIndefiniteHeader: return format("CborToken(arrayIndefiniteLength, _)");
544 			case mapHeader: return format("CborToken(map, %s)", uinteger);
545 			case mapIndefiniteHeader: return format("CborToken(mapIndefiniteLength, _)");
546 			case bytesHeader: return format("CborToken(byteString, %s)", uinteger);
547 			case bytesIndefiniteHeader: return format("CborToken(byteStringIndefiniteLength, _)");
548 			case textHeader: return format("CborToken(textString, %s)", uinteger);
549 			case textIndefiniteHeader: return format("CborToken(textStringIndefiniteLength, _)");
550 		}
551 	}
552 }
553 
554 //------------------------------------------------------------------------------
555 //		DDDDD   EEEEEEE  CCCCC   OOOOO  DDDDD   IIIII NN   NN   GGGG
556 //		DD  DD  EE      CC    C OO   OO DD  DD   III  NNN  NN  GG  GG
557 //		DD   DD EEEEE   CC      OO   OO DD   DD  III  NN N NN GG
558 //		DD   DD EE      CC    C OO   OO DD   DD  III  NN  NNN GG   GG
559 //		DDDDDD  EEEEEEE  CCCCC   OOOO0  DDDDDD  IIIII NN   NN  GGGGGG
560 //------------------------------------------------------------------------------
561 
562 CborToken decodeCborToken(R)(auto ref R input)
563 	if(isInputRange!R && is(ElementType!R == ubyte))
564 {
565 	import std.array;
566 
567 	if (input.empty) onInsufficientInput();
568 
569 	ubyte item = input.front;
570 	input.popFront;
571 
572 	switch(item)
573 	{
574 		case 0x00: .. case 0x17: // Integer 0x00..0x17 (0..23)
575 			return CborToken(CborTokenType.posinteger, item);
576 		case 0x18: // Unsigned integer (one-byte uint8_t follows)
577 			return CborToken(CborTokenType.posinteger, readInteger!ubyte(input));
578 		case 0x19: // Unsigned integer (two-byte uint16_t follows)
579 			return CborToken(CborTokenType.posinteger, readInteger!ushort(input));
580 		case 0x1a: // Unsigned integer (four-byte uint32_t follows)
581 			return CborToken(CborTokenType.posinteger, readInteger!uint(input));
582 		case 0x1b: // Unsigned integer (eight-byte uint64_t follows)
583 			return CborToken(CborTokenType.posinteger, readInteger!ulong(input));
584 		case 0x20: .. case 0x37: // Negative integer -1-0x00..-1-0x17 (-1..-24)
585 			return CborToken(CborTokenType.neginteger, cast(byte)(-1 - item + 0x20));
586 		case 0x38: // Negative integer -1-n (one-byte uint8_t for n follows)
587 			return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ubyte(input));
588 		case 0x39: // Negative integer -1-n (two-byte uint16_t for n follows)
589 			return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ushort(input));
590 		case 0x3a: // Negative integer -1-n (four-byte uint32_t for n follows)
591 			return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!uint(input));
592 		case 0x3b: // Negative integer -1-n (eight-byte uint64_t for n follows)
593 			return CborToken(CborTokenType.neginteger, -1 - cast(long)readInteger!ulong(input));
594 		case 0x40: .. case 0x57: // byte string (0x00..0x17 bytes follow)
595 			return CborToken(CborTokenType.bytesHeader, item - 0x40);
596 		case 0x58: // byte string (one-byte uint8_t for n, and then n bytes follow)
597 			return CborToken(CborTokenType.bytesHeader, readInteger!ubyte(input));
598 		case 0x59: // byte string (two-byte uint16_t for n, and then n bytes follow)
599 			return CborToken(CborTokenType.bytesHeader, readInteger!ushort(input));
600 		case 0x5a: // byte string (four-byte uint_t for n, and then n bytes follow)
601 			return CborToken(CborTokenType.bytesHeader, readInteger!uint(input));
602 		case 0x5b: // byte string (eight-byte uint64_t for n, and then n bytes follow)
603 			return CborToken(CborTokenType.bytesHeader, readInteger!ulong(input));
604 		case 0x5f: // indefinite-length byte string, byte strings follow, terminated by "break"
605 			return CborToken(CborTokenType.bytesIndefiniteHeader);
606 		case 0x60: .. case 0x77: // UTF-8 string (0x00..0x17 bytes follow)
607 			return CborToken(CborTokenType.textHeader, item - 0x60);
608 		case 0x78: // UTF-8 string (one-byte uint8_t for n, and then n bytes follow)
609 			return CborToken(CborTokenType.textHeader, readInteger!ubyte(input));
610 		case 0x79: // UTF-8 string (two-byte uint16_t for n, and then n bytes follow)
611 			return CborToken(CborTokenType.textHeader, readInteger!ushort(input));
612 		case 0x7a: // UTF-8 string (four-byte uint_t for n, and then n bytes follow)
613 			return CborToken(CborTokenType.textHeader, readInteger!uint(input));
614 		case 0x7b: // UTF-8 string (eight-byte uint64_t for n, and then n bytes follow)
615 			return CborToken(CborTokenType.textHeader, readInteger!ulong(input));
616 		case 0x7f: // indefinite-length UTF-8 string, UTF-8 strings follow, terminated by "break"
617 			return CborToken(CborTokenType.textIndefiniteHeader);
618 		case 0x80: .. case 0x97: // array (0x00..0x17 data items follow)
619 			return CborToken(CborTokenType.arrayHeader, item - 0x80);
620 		case 0x98: // array (one-byte uint8_t for n, and then n data items follow)
621 			return CborToken(CborTokenType.arrayHeader, readInteger!ubyte(input));
622 		case 0x99: // array (two-byte uint16_t for n, and then n data items follow)
623 			return CborToken(CborTokenType.arrayHeader, readInteger!ushort(input));
624 		case 0x9a: // array (four-byte uint_t for n, and then n data items follow)
625 			return CborToken(CborTokenType.arrayHeader, readInteger!uint(input));
626 		case 0x9b: // array (eight-byte uint64_t for n, and then n data items follow)
627 			return CborToken(CborTokenType.arrayHeader, readInteger!ulong(input));
628 		case 0x9f: // indefinite-length array, data items follow, terminated by "break"
629 			return CborToken(CborTokenType.arrayIndefiniteHeader);
630 		case 0xa0: .. case 0xb7: // map (0x00..0x17 pairs of data items follow)
631 			return CborToken(CborTokenType.mapHeader, item - 0xa0);
632 		case 0xb8: // map (one-byte uint8_t for n, and then n pairs of data items follow)
633 			return CborToken(CborTokenType.mapHeader, readInteger!ubyte(input));
634 		case 0xb9: // map (two-byte uint16_t for n, and then n pairs of data items follow)
635 			return CborToken(CborTokenType.mapHeader, readInteger!ushort(input));
636 		case 0xba: // map (four-byte uint_t for n, and then n pairs of data items follow)
637 			return CborToken(CborTokenType.mapHeader, readInteger!uint(input));
638 		case 0xbb: // map (eight-byte uint64_t for n, and then n pairs of data items follow)
639 			return CborToken(CborTokenType.mapHeader, readInteger!ulong(input));
640 		case 0xbf: // indefinite-length map, pairs of data items follow, terminated by "break"
641 			return CborToken(CborTokenType.mapIndefiniteHeader);
642 		case 0xc0: .. case 0xd7: // (tagged item)
643 			return CborToken(CborTokenType.tag, item - 0xc0);
644 		case 0xd8: // 1-byte tag
645 			return CborToken(CborTokenType.tag, readInteger!ubyte(input));
646 		case 0xd9: // 2-byte tag
647 			return CborToken(CborTokenType.tag, readInteger!ushort(input));
648 		case 0xda: // 4-byte tag
649 			return CborToken(CborTokenType.tag, readInteger!uint(input));
650 		case 0xdb: // 8-byte tag
651 			return CborToken(CborTokenType.tag, readInteger!ulong(input));
652 		case 0xe0: .. case 0xf3: // (simple value)
653 			return CborToken(CborTokenType.simple, item - 0xe0);
654 		case 0xf4: // False
655 			return CborToken(CborTokenType.boolean, false);
656 		case 0xf5: // True
657 			return CborToken(CborTokenType.boolean, true);
658 		case 0xf6: // Null
659 			return CborToken(CborTokenType.nil);
660 		case 0xf7: // Undefined
661 			return CborToken(CborTokenType.undefined);
662 		case 0xf8: // (simple value, one byte follows)
663 			return CborToken(CborTokenType.simple, readInteger!ubyte(input));
664 		case 0xf9: // Half-Precision Float (two-byte IEEE 754)
665 			ushort u = readInteger!ushort(input);
666 			return CborToken(CborTokenType.floating, halfToFloat(u));
667 		case 0xfa: // Single-Precision Float (four-byte IEEE 754)
668 			__FloatRep fr = {u : readInteger!uint(input)};
669 			return CborToken(CborTokenType.floating, fr.f);
670 		case 0xfb: // Double-Precision Float (eight-byte IEEE 754)
671 			__DoubleRep dr = {u : readInteger!ulong(input)};
672 			return CborToken(CborTokenType.floating, dr.d);
673 		case 0xff: // "break" stop code
674 			return CborToken(CborTokenType.breakCode);
675 		default:
676 			onUnsupportedTag(item);
677 	}
678 
679 	assert(false);
680 }
681 
682 //------------------------------------------------------------------------------
683 //	DDDDD   EEEEEEE  SSSSS  EEEEEEE RRRRRR  IIIII   AAA   LL      IIIII ZZZZZ EEEEEEE
684 //	DD  DD  EE      SS      EE      RR   RR  III   AAAAA  LL       III     ZZ EE
685 //	DD   DD EEEEE    SSSSS  EEEEE   RRRRRR   III  AA   AA LL       III    ZZ  EEEEE
686 //	DD   DD EE           SS EE      RR  RR   III  AAAAAAA LL       III   ZZ   EE
687 //	DDDDDD  EEEEEEE  SSSSS  EEEEEEE RR   RR IIIII AA   AA LLLLLLL IIIII ZZZZZ EEEEEEE
688 //------------------------------------------------------------------------------
689 
690 // If duplicate is true then dups incoming byte arrays and utf-8 string.
691 // If duplicate is false and input is array, then resulting byte arrays (ubyte[]) and
692 // utf-8 strings (char[], string) are slices of the input range.
693 void decodeCbor(
694 	Flag!"Duplicate" duplicate = Yes.Duplicate,
695 	Flag!"Flatten" flatten = No.Flatten,
696 	R,
697 	T)
698 	(auto ref R input, ref T outValue)
699 		if(isInputRange!R && is(ElementType!R == ubyte))
700 {
701 	import std.typecons : isTuple;
702 	CborToken token;
703 
704 	static if (isIntegral!T) {
705 		token = decodeCborToken(input);
706 		if (token.type == CborTokenType.neginteger) {
707 			outValue = cast(T)token.integer;
708 			return;
709 		} else if (token.type == CborTokenType.posinteger) {
710 			outValue = cast(T)token.uinteger;
711 			return;
712 		}
713 		onCastErrorToFrom!T(token.type);
714 	} else static if (isSomeChar!T) {
715 		token = decodeCborToken(input);
716 		if (token.type == CborTokenType.posinteger) {
717 			outValue = cast(T)token.uinteger;
718 			return;
719 		}
720 		onCastErrorToFrom!T(token.type);
721 	} else static if (isFloatingPoint!T) {
722 		token = decodeCborToken(input);
723 		if (token.type == CborTokenType.floating) {
724 			outValue = cast(T)token.floating;
725 			return;
726 		}
727 		onCastErrorToFrom!T(token.type);
728 	} else static if (isBoolean!T) {
729 		token = decodeCborToken(input);
730 		if (token.type == CborTokenType.boolean) {
731 			outValue = token.boolean;
732 			return;
733 		}
734 		onCastErrorToFrom!T(token.type);
735 	} else static if ((isArray!T || isOutputRange!(T, ubyte)) && is(Unqual!(ElementType!T) == ubyte)) {
736 		decodeCborExactByteArray!(duplicate, flatten)(input, outValue);
737 		return;
738 	} else static if (isArray!T && isSomeChar!(Unqual!(ElementEncodingType!T))) {
739 		decodeString!(duplicate, flatten)(input, outValue);
740 		return;
741 	} else static if (isArray!T) {
742 		decodeSequence!(duplicate, flatten)(input, outValue);
743 		return;
744 	} else static if (is(T == class) || is(T == struct) || isTuple!T) {
745 		decodeAggregate!(duplicate, flatten)(input, outValue);
746 		return;
747 	} else static if (isAssociativeArray!T) {
748 		token = decodeCborToken(input);
749 
750 		if (token.type == CborTokenType.nil) {
751 			outValue = null;
752 			return;
753 		}
754 
755 		alias K = typeof(T.init.keys[0]);
756 		alias V = typeof(T.init.values[0]);
757 
758 		if (token.type == CborTokenType.mapHeader) {
759 			foreach (_; 0..token.uinteger) {
760 				K key;
761 				V value;
762 				decodeCbor!(duplicate, flatten)(input, key);
763 				decodeCbor!(duplicate, flatten)(input, value);
764 				outValue[key] = value;
765 			}
766 		} else if (token.type == CborTokenType.mapIndefiniteHeader) {
767 			while (true) {
768 				token = decodeCborToken(input.save);
769 				if (token.type == CborTokenType.breakCode) {
770 					break;
771 				} else {
772 					K key;
773 					V value;
774 					decodeCbor!(duplicate, flatten)(input, key);
775 					decodeCbor!(duplicate, flatten)(input, value);
776 					outValue[key] = value;
777 				}
778 			}
779 		}
780 		return;
781 	} else {
782 		static assert(false, "Unable to decode " ~ T.stringof);
783 	}
784 
785 	assert(false);
786 }
787 
788 private void decodeString(
789 	Flag!"Duplicate" duplicate = Yes.Duplicate,
790 	Flag!"Flatten" flatten = No.Flatten,
791 	R,
792 	T)
793 	(auto ref R input, ref T outValue)
794 		if(isInputRange!R && is(ElementType!R == ubyte))
795 {
796 	enum bool flat = needsFlattening!(T, flatten);
797 	static if (flat)
798 	{
799 		static assert(isStaticArray!T, "Only static arrays can be flat");
800 		ubyte[] bytes = readBytes(input, T.length);
801 		readStaticString(bytes, outValue);
802 		return;
803 	}
804 	else
805 	{
806 		CborToken token = decodeCborToken(input);
807 		bool definite = !(token.type & 0b001);
808 
809 		if (definite) {
810 			static if (isDynamicArray!T)
811 			{
812 				ubyte[] bytes = readBytes(input, cast(size_t)token.uinteger);
813 				outValue = to!T(cast(char[])bytes);
814 				static if (is(Unqual!(ElementEncodingType!T) == ubyte) && duplicate)
815 					outValue = outValue.dup; // TODO allocation
816 				return;
817 			}
818 			else static if (isStaticArray!T)
819 			{
820 				ubyte[] bytes = readBytes(input, cast(size_t)token.uinteger);
821 				if (bytes.length != T.length) onCastErrorToFrom!T(token.type);
822 				readStaticString(bytes, outValue);
823 				return;
824 			}
825 		}
826 		else
827 		{
828 			static if (isDynamicArray!T)
829 			{
830 				alias Unqualified = Unqual!(ElementEncodingType!T)[];
831 				Unqualified output;
832 				size_t i;
833 
834 				while (true) {
835 					token = decodeCborToken(input.save);
836 					if (token.type == CborTokenType.breakCode) {
837 						import std.range : dropExactly;
838 						dropExactly(input, 1);
839 						break;
840 					} else {
841 						decodeCborToken(input);
842 						if (token.type != CborTokenType.textHeader)
843 							throw new CborException(format("Expected textHeader but got %s", token.type));
844 						output.length += cast(size_t)token.uinteger - (output.length - i); // TODO allocation
845 						ubyte[] innerBytes = readBytes(input, token.uinteger);
846 						i += readStaticString(innerBytes, output[i..$]);
847 					}
848 				}
849 				output.length = i;
850 				outValue = cast(T)output;
851 				return;
852 			}
853 			else static if (isStaticArray!T)
854 				assert(false, "TODO");
855 		}
856 		assert(false, "TODO");
857 	}
858 }
859 
860 private size_t readStaticString(T)(ubyte[] bytes, auto ref T outValue)
861 {
862 	static if (is(Unqual!(ElementEncodingType!T) == char))
863 	{
864 		outValue[] = cast(ElementEncodingType!T[])(bytes[]);
865 		return bytes.length;
866 	}
867 	else
868 	{
869 		import std.utf : byChar, byWchar, byDchar;
870 		alias V = Unqual!(ElementEncodingType!T);
871 
872 		static if (is(V == char))
873 			alias byElem = byChar;
874 		else static if (is(V == wchar))
875 			alias byElem = byWchar;
876 		else static if (is(V == dchar))
877 			alias byElem = byDchar;
878 
879 		size_t i;
880 		foreach(c; byElem(cast(char[])bytes)) {
881 			outValue[i] = c;
882 			++i;
883 		}
884 		return i;
885 	}
886 }
887 
888 private void decodeSequence(
889 	Flag!"Duplicate" duplicate = Yes.Duplicate,
890 	Flag!"Flatten" flatten = No.Flatten,
891 	R,
892 	T)
893 	(auto ref R input, ref T outValue)
894 		if(isInputRange!R && is(ElementType!R == ubyte))
895 {
896 	enum bool flat = needsFlattening!(T, flatten);
897 	enum bool dynamicArray = isDynamicArray!T;
898 	enum bool staticArray = isStaticArray!T;
899 	enum bool range = !(staticArray || dynamicArray);
900 
901 	//------------------------------------------------------------------------------
902 	// Flat. Without header. Read only elements.
903 	static if (flat)
904 	{
905 		static assert(isStaticArray!T, "Only static arrays can be flat");
906 		foreach (ref elem; outValue)
907 			decodeCbor!(duplicate, flatten)(input, elem);
908 		return;
909 	}
910 	else
911 	{
912 		//------------------------------------------------------------------------------
913 		// non-flat. Read header.
914 		CborToken token = decodeCborToken(input);
915 		bool definite = !(token.type & 0b001);
916 
917 		if (definite) {
918 			static if (dynamicArray)
919 			{
920 				ulong lengthToRead = token.uinteger;
921 				checkArraySize(lengthToRead);
922 				if (outValue.length != cast(size_t)lengthToRead)
923 					outValue.length = cast(size_t)token.uinteger;
924 				foreach (ref elem; outValue)
925 					decodeCbor!(duplicate, flatten)(input, elem);
926 				return;
927 			}
928 			else static if (staticArray)
929 			{
930 				foreach (ref elem; outValue)
931 					decodeCbor!(duplicate, flatten)(input, elem);
932 				return;
933 			}
934 		}
935 		else // indefinite
936 		{
937 			static if (dynamicArray)
938 			{
939 				alias Unqualified = Unqual!(ElementEncodingType!T)[];
940 				Unqualified output;
941 				output.length = 1;
942 				size_t i;
943 
944 				while (true) {
945 					token = decodeCborToken(input.save);
946 					if (token.type == CborTokenType.breakCode) {
947 						import std.range : dropExactly;
948 						dropExactly(input, 1);
949 						break;
950 					} else {
951 						if (output.length == i)
952 							output.length = output.length * 2; // TODO allocation
953 						decodeCbor!(No.Duplicate, flatten)(input, output[i]);
954 					}
955 					++i;
956 				}
957 				output.length = i;
958 				outValue = cast(T)output;
959 				return;
960 			}
961 			else static if (staticArray)
962 				assert(false, "TODO");
963 		}
964 		assert(false, "TODO");
965 	}
966 }
967 
968 private void decodeCborExactByteArray(
969 	Flag!"Duplicate" duplicate = Yes.Duplicate,
970 	Flag!"Flatten" flatten = No.Flatten,
971 	R,
972 	T)
973 	(auto ref R input, ref T outValue)
974 		if(isInputRange!R && is(ElementType!R == ubyte))
975 {
976 	static if (needsFlattening!(T, flatten))
977 	{
978 		outValue[] = cast(ElementEncodingType!T[])(readBytes(input, T.length));
979 		return;
980 	}
981 	else
982 	{
983 		CborToken token = decodeCborToken(input);
984 		if (token.type == CborTokenType.bytesHeader)
985 		{
986 			ubyte[] data = readBytes(input, token.uinteger);
987 			static if (isDynamicArray!T) {
988 				outValue = cast(T)data;
989 				static if (duplicate)
990 					outValue = outValue.dup;
991 			} else static if (isStaticArray!T) {
992 				if (data.length != T.length)
993 					onCastErrorToFrom!T(token.type);
994 				outValue[] = data;
995 			} else {
996 				static assert(false);
997 			}
998 			return;
999 		}
1000 		else if (token.type == CborTokenType.bytesIndefiniteHeader)
1001 		{
1002 			import std.algorithm : copy;
1003 			import std.array : Appender, appender;
1004 
1005 			static if (isArray!T) {
1006 				alias unqualified = Unqual!(ElementEncodingType!T)[];
1007 				Appender!(unqualified) sink = appender(cast(unqualified)outValue);
1008 				sink.clear();
1009 			} else
1010 				alias sink = outValue;
1011 
1012 			while (true) {
1013 				token = decodeCborToken(input);
1014 				if (token.type == CborTokenType.breakCode) {
1015 					break;
1016 				} else if (token.type == CborTokenType.bytesHeader) {
1017 					copy(readBytes(input, token.uinteger), sink);
1018 				} else {
1019 					throw new CborException(
1020 						format("Unexpected token inside indefinite-length byte array: %s; "~
1021 							"Expected definite-length byteString", token));
1022 				}
1023 			}
1024 
1025 			static if (isDynamicArray!T) {
1026 				outValue = cast(T)sink.data;
1027 			} else static if (isStaticArray!T) {
1028 				outValue[] = sink.data;
1029 			} else
1030 				static assert(false);
1031 
1032 			return;
1033 		}
1034 	}
1035 }
1036 
1037 private void decodeAggregate(
1038 	Flag!"Duplicate" duplicate = Yes.Duplicate,
1039 	Flag!"Flatten" flatten = No.Flatten, R, T)
1040 	(auto ref R input, ref T outValue)
1041 		if(isInputRange!R && is(ElementType!R == ubyte))
1042 {
1043 	static if (!needsFlattening!(T, flatten)) {
1044 		CborToken token = decodeCborToken(input);
1045 	}
1046 
1047 	static if (is(T == class))
1048 	if (token.type == CborTokenType.nil) {
1049 		outValue = null;
1050 		return;
1051 	}
1052 
1053 	static if (!needsFlattening!(T, flatten))
1054 	{
1055 		if (token.type != CborTokenType.arrayHeader)
1056 			throw new CborException(format("Can not decode %s from %s", T.stringof, token.type));
1057 
1058 		size_t numMembers;
1059 
1060 		static if (isTuple!T)
1061 			numMembers = T.Types.length;
1062 		else
1063 			numMembers = numEncodableMembers!T;
1064 
1065 		if (token.uinteger != numMembers)
1066 		{
1067 			throw new CborException(
1068 				format("The number of deserialized members of %s is mismatched."~
1069 					" Got %s, while expected %s members",
1070 						T.stringof, token.uinteger, numMembers));
1071 		}
1072 	}
1073 
1074 	static if (isTuple!T)
1075 	{
1076 		foreach (i, Type; T.Types)
1077 			decodeCbor!(duplicate, flatten)(input, outValue.field[i]);
1078 	}
1079 	else
1080 	{
1081 		static if (is(T == class))
1082 		if (outValue is null)
1083 			outValue = newClassInstance!T();
1084 
1085 		foreach(i, member; outValue.tupleof)
1086 		{
1087 			static if (isEncodedField!(outValue.tupleof[i]))
1088 				decodeCbor!(duplicate, flatten)(input, outValue.tupleof[i]);
1089 		}
1090 	}
1091 }
1092 
1093 /// Decodes single cbor value and tries to convert it to requested type.
1094 /// If types don't match CborException is thrown.
1095 /// Note, that ubyte[] and string types are slices of input range if ubyte[] was provided.
1096 /// Will consume input range, decoding all the elements of T.
1097 T decodeCborSingle(T, Flag!"Flatten" flatten = No.Flatten, R)(auto ref R input)
1098 	if(isInputRange!R && is(ElementType!R == ubyte))
1099 {
1100 	Unqual!T value;
1101 	decodeCbor!(No.Duplicate, flatten)(input, value);
1102 	return cast(T)value;
1103 }
1104 
1105 /// Decodes single cbor value and tries to convert it to requested type.
1106 /// If types don't match CborException is thrown.
1107 /// Note, that this version will dup all array slices for you.
1108 /// Will consume input range, decoding all the elements of T.
1109 T decodeCborSingleDup(T, Flag!"Flatten" flatten = No.Flatten, R)(auto ref R input)
1110 	if(isInputRange!R && is(ElementType!R == ubyte))
1111 {
1112 	Unqual!T value;
1113 	decodeCbor!(Yes.Duplicate, flatten)(input, value);
1114 	return cast(T)value;
1115 }
1116 
1117 //------------------------------------------------------------------------------
1118 //		HH   HH EEEEEEE LL      PPPPPP  EEEEEEE RRRRRR   SSSSS
1119 //		HH   HH EE      LL      PP   PP EE      RR   RR SS
1120 //		HHHHHHH EEEEE   LL      PPPPPP  EEEEE   RRRRRR   SSSSS
1121 //		HH   HH EE      LL      PP      EE      RR  RR       SS
1122 //		HH   HH EEEEEEE LLLLLLL PP      EEEEEEE RR   RR  SSSSS
1123 //------------------------------------------------------------------------------
1124 
1125 private void putChecked(R, E)(ref R sink, auto ref E e)
1126 {
1127 	import std.range : put, hasLength;
1128 	version(Cbor_Debug)
1129 	static if (hasLength!R)
1130 	{
1131 		size_t elemSize;
1132 
1133 		static if (hasLength!E)
1134 			elemSize = e.length * ElementType!E.sizeof;
1135 		else
1136 			elemSize = ElementType!E.sizeof;
1137 
1138 		assert(sink.length >= elemSize, "Provided sink length is to small");
1139 	}
1140 	put(sink, e);
1141 }
1142 
1143 private T readInteger(T, R)(auto ref R input)
1144 	if(isInputRange!R && is(ElementType!R == ubyte))
1145 {
1146 	enum ubyte size = T.sizeof;
1147 	import std.algorithm : copy;
1148 	import std.bitmanip : bigEndianToNative;
1149 	import std.range : dropExactly, take;
1150 
1151 	static assert(T.sizeof == size);
1152 	static assert(size > 0);
1153 	if (input.length < size) onInsufficientInput();
1154 
1155 	ubyte[size] data;
1156 
1157 	copy(take(input, size), data[]);
1158 	input = input.dropExactly(size);
1159 	T result = bigEndianToNative!(T, size)(data);
1160 
1161 	return result;
1162 }
1163 
1164 // Reads byte array from input range. On 32-bit can read up to uint.max bytes.
1165 // If ubyte[] is passed as input, a slice will be returned.
1166 // Make sure to dup array when input buffer is reused.
1167 ubyte[] readBytes(R)(auto ref R input, ulong length)
1168 	if(isInputRange!R && is(ElementType!R == ubyte))
1169 {
1170 	import std.array;
1171 	import std.range : take;
1172 	if (input.length < length) onInsufficientInput();
1173 
1174 	static if (size_t.sizeof < ulong.sizeof)
1175 		if (length > size_t.max)
1176 			throw new CborException(format("Array size is too big %s", length));
1177 
1178 	size_t dataLength = cast(size_t)length;
1179 	ubyte[] result;
1180 	static if (is(R == ubyte[]))
1181 	{
1182 		result = input[0..dataLength];
1183 		input = input[dataLength..$];
1184 	}
1185 	else
1186 	{
1187 		result = take(input, dataLength).array; // TODO allocation
1188 	}
1189 
1190 	return result;
1191 }
1192 
1193 private void checkArraySize(T)(T length)
1194 {
1195 	static if (size_t.sizeof < ulong.sizeof)
1196 		if (length > size_t.max)
1197 			throw new CborException(format("Array size is too big: %s > size_t.max", length));
1198 }
1199 
1200 private union __FloatRep { float f; uint u;}
1201 private union __DoubleRep { double d; ulong u; }
1202 // from http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java
1203 private float halfToFloat(ushort half) @nogc
1204 {
1205 	int mant = half & 0x03ff;             // 10 bits mantissa
1206 	int exp =  half & 0x7c00;             // 5 bits exponent
1207 	if( exp == 0x7c00 )                   // NaN/Inf
1208 		exp = 0x3fc00;                    // -> NaN/Inf
1209 	else if( exp != 0 )                   // normalized value
1210 	{
1211 		exp += 0x1c000;                   // exp - 15 + 127
1212 		if( mant == 0 && exp > 0x1c400 ){ // smooth transition
1213 			__FloatRep f = {u : (( half & 0x8000 ) << 16 | exp << 13 | 0x3ff) };
1214 			return f.f;
1215 		}
1216 	}
1217 	else if( mant != 0 )                  // && exp==0 -> subnormal
1218 	{
1219 		exp = 0x1c400;                    // make it normal
1220 		do {
1221 			mant <<= 1;                   // mantissa * 2
1222 			exp -= 0x400;                 // decrease exp by 1
1223 		} while( ( mant & 0x400 ) == 0 ); // while not normal
1224 		mant &= 0x3ff;                    // discard subnormal bit
1225 	}                                     // else +/-0 -> +/-0
1226 	__FloatRep f = {u : (                 // combine all parts
1227 		( half & 0x8000 ) << 16           // sign  << ( 31 - 15 )
1228 		| ( exp | mant ) << 13 )          // value << ( 23 - 10 )
1229 	};
1230 	return f.f;
1231 }
1232 
1233 /// Outputs textual representation of cbor stream into sink or stdout if not provided.
1234 void printCborStream(string singleIndent="  ", R)(auto ref R input, ulong numItems = ulong.max)
1235 {
1236 	import std.stdio : stdout;
1237 	auto writer = stdout.lockingTextWriter;
1238 	printCborStream!singleIndent(input, writer, numItems);
1239 }
1240 
1241 /// ditto
1242 void printCborStream(string singleIndent="  ", Sink, R)(
1243 		auto ref R input,
1244 		auto ref Sink sink,
1245 		ulong numItems = ulong.max,
1246 		string indent = ""
1247 	)
1248 	if(isInputRange!R && is(ElementType!R == ubyte) && isOutputRange!(Sink, char))
1249 {
1250 	import std.format : formattedWrite;
1251 
1252 	while(input.length > 0 && numItems > 0)
1253 	{
1254 		CborToken token = decodeCborToken(input);
1255 		auto type = token.type;
1256 		final switch(type) with(CborTokenType)
1257 		{
1258 			case boolean: formattedWrite(sink, "%s(%s)\n", indent, token.boolean); break;
1259 			case nil: formattedWrite(sink, "%s(null)\n", indent); break;
1260 			case undefined: formattedWrite(sink, "%s(undefined)\n", indent); break;
1261 			case tag:
1262 				formattedWrite(sink, "%s(tag, %s)\n", indent, token.uinteger);
1263 				printCborStream!singleIndent(input, sink, 1, indent~singleIndent);
1264 				break;
1265 			case simple: formattedWrite(sink, "%s(simple, %s)\n", indent, token.uinteger); break;
1266 			case breakCode:
1267 				formattedWrite(sink, "%s(break)\n", indent, token.uinteger);
1268 				return;
1269 			case posinteger: formattedWrite(sink, "%s(posinteger, %s)\n", indent, token.uinteger); break;
1270 			case neginteger: formattedWrite(sink, "%s(neginteger, %s)\n", indent, token.integer); break;
1271 			case floating: formattedWrite(sink, "%s(floating, %s)\n", indent, token.floating); break;
1272 			case arrayHeader:
1273 				formattedWrite(sink, "%s(array, %s)\n", indent, token.uinteger);
1274 				printCborStream!singleIndent(input, sink, token.uinteger, indent~singleIndent);
1275 				break;
1276 			case arrayIndefiniteHeader:
1277 				formattedWrite(sink, "%s(array, _)\n", indent);
1278 				printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent);
1279 				break;
1280 			case mapHeader:
1281 				formattedWrite(sink, "%s(map, %s)\n", indent, token.uinteger);
1282 				printCborStream!singleIndent(input, sink, token.uinteger*2, indent~singleIndent);
1283 				break;
1284 			case mapIndefiniteHeader:
1285 				formattedWrite(sink, "%s(map, _)\n", indent);
1286 				printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent);
1287 				break;
1288 			case bytesHeader:
1289 				formattedWrite(sink, "%s(bytes, %s)\n%s%s(%(%02x%))\n",
1290 					indent, token.uinteger, indent, singleIndent, readBytes(input, token.uinteger));
1291 				break;
1292 			case bytesIndefiniteHeader:
1293 				formattedWrite(sink, "%s(bytes, _)\n", indent);
1294 				printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent);
1295 				break;
1296 			case textHeader:
1297 				formattedWrite(sink, "%s(text, %s)\n%s%s\"%s\"\n",
1298 					indent, token.uinteger, indent, singleIndent, cast(string)readBytes(input, token.uinteger));
1299 				break;
1300 			case textIndefiniteHeader:
1301 				formattedWrite(sink, "%s(text, _)\n", indent);
1302 				printCborStream!singleIndent(input, sink, ulong.max, indent~singleIndent);
1303 				break;
1304 		}
1305 		--numItems;
1306 	}
1307 }
1308 
1309 /// Variables marked with this attribute wont be (de)serialized.
1310 enum ignore;
1311 
1312 private enum bool isEncodedField(Member) = isEncodedType!(Member);
1313 
1314 private enum bool isEncodedField(alias member) = isEncodedType!(typeof(member)) && !hasUDA!(member, ignore);
1315 
1316 unittest
1317 {
1318 	static class C{}
1319 	static struct S {
1320 		void* pointer;
1321 	}
1322 
1323 	int v1;
1324 	float v2;
1325 	bool v3;
1326 	int[] v4;
1327 	C v5;
1328 	S v6;
1329 	int[int] v7;
1330 	int* v8;
1331 	void* v9;
1332 	static assert(isEncodedField!v1);
1333 	static assert(isEncodedField!v2);
1334 	static assert(isEncodedField!v3);
1335 	static assert(isEncodedField!v4);
1336 	static assert(isEncodedField!v5);
1337 	static assert(isEncodedField!v6);
1338 	static assert(isEncodedField!v7);
1339 	static assert(!isEncodedField!v8);
1340 	static assert(!isEncodedField!v9);
1341 
1342 	foreach(i, member; v6.tupleof)
1343 	{
1344 		static assert(!isEncodedField!(v6.tupleof[i]));
1345 		static assert(!isEncodedType!(typeof(v6.tupleof[i])));
1346 	}
1347 }
1348 
1349 private enum bool isEncodedType(T) = isIntegral!T || isFloatingPoint!T || isBoolean!T ||
1350 	is(Unqual!T == typeof(null)) || isArray!T || isInputRange!T || isAssociativeArray!T ||
1351 	isTuple!T || is(T == string) || is(T == class) || is(T == struct);
1352 
1353 unittest
1354 {
1355 	import std.meta : allSatisfy, templateNot, AliasSeq;
1356 	static class C{}
1357 	static struct S{}
1358 
1359 	alias encodedTypes = AliasSeq!(int, float, bool, typeof(null), int[], C, S, int[int]);
1360 	alias nonEncodedTypes = AliasSeq!(int*, void*);
1361 
1362 	static assert(allSatisfy!(isEncodedType, encodedTypes));
1363 	static assert(allSatisfy!(templateNot!isEncodedType, nonEncodedTypes));
1364 
1365 	static assert(allSatisfy!(isEncodedField, encodedTypes));
1366 	static assert(allSatisfy!(templateNot!isEncodedField, nonEncodedTypes));
1367 }
1368 
1369 /// Tests if type can be encoded in flat mode, i.e. without header
1370 private enum bool canBeFlattened(T) = isStaticArray!T || (isTuple!T && isExpressionTuple!T) ||	is(T == struct);
1371 
1372 private enum bool needsFlattening(T, Flag!"Flatten" flatten) = canBeFlattened!T && flatten;
1373 
1374 /// Returns a number of aggregate members that will be encoded by cbor-d.
1375 enum size_t numEncodableMembers(A) = numEncodableMembersImpl!(A.tupleof);
1376 
1377 private template numEncodableMembersImpl(members ...)
1378 {
1379 	static if (members.length == 0)
1380 		enum numEncodableMembersImpl = 0;
1381 	else
1382 		enum numEncodableMembersImpl =
1383 			isEncodedField!(members[0]) +
1384 			numEncodableMembersImpl!(members[1..$]);
1385 }
1386 
1387 private C newClassInstance(C)() if (is(C == class))
1388 {
1389 	import core.memory : GC;
1390 	// .init is deprecated since 2.072. Replaced with initializer.
1391 	static if (__VERSION__ < 2072L) {
1392 		void* memory = GC.malloc(typeid(C).init.length);
1393 		memory[0 .. typeid(C).init.length] = typeid(C).init[];
1394 	} else {
1395 		void* memory = GC.malloc(typeid(C).initializer.length);
1396 		memory[0 .. typeid(C).initializer.length] = typeid(C).initializer[];
1397 	}
1398 
1399 	return cast(C) memory;
1400 }
1401 
1402 //------------------------------------------------------------------------------
1403 //		TTTTTTT EEEEEEE  SSSSS  TTTTTTT  SSSSS
1404 //		  TTT   EE      SS        TTT   SS
1405 //		  TTT   EEEEE    SSSSS    TTT    SSSSS
1406 //		  TTT   EE           SS   TTT        SS
1407 //		  TTT   EEEEEEE  SSSSS    TTT    SSSSS
1408 //------------------------------------------------------------------------------
1409 
1410 // Testing helpers
1411 version(unittest)
1412 {
1413 	import std.array : Appender, appender;
1414 	import std.stdio;
1415 	import std.exception : assertThrown;
1416 	import core.exception : AssertError;
1417 	auto testBuf = appender!(ubyte[])();
1418 	auto strBuf = appender!(char[])();
1419 
1420 	void cmpStreamStringAndReset(string file = __MODULE__, size_t line = __LINE__)(string expected){
1421 		printCborStream(testBuf.data, strBuf);
1422 		assertEqual!(file, line)(strBuf.data, expected);
1423 		testBuf.clear();
1424 		strBuf.clear();
1425 	}
1426 
1427 	CborToken getTokenAndReset()
1428 	{
1429 		CborToken res = decodeCborToken(testBuf.data);
1430 		testBuf.clear();
1431 		return res;
1432 	}
1433 
1434 	void assertToken(CborToken expected)
1435 	{
1436 		CborToken decoded = getTokenAndReset();
1437 		ubyte[] decodedRep = (cast(ubyte*)(&decoded))[0..decoded.sizeof];
1438 		ubyte[] expectedRep = (cast(ubyte*)(&expected))[0..expected.sizeof];
1439 		//writefln("%s %s", decodedRep, expectedRep);
1440 		assert(decoded == expected, format("decoded %s, %s, expected %s, %s",
1441 				decoded, decodedRep, expected, expectedRep));
1442 	}
1443 
1444 	void assertHexAndClear(string file = __MODULE__, size_t line = __LINE__)(string hex)
1445 	{
1446 		assertEqual!(file, line)(toHexString(testBuf.data), hex);
1447 		testBuf.clear();
1448 	}
1449 
1450 	void printBufferAndReset()
1451 	{
1452 		import std.stdio : stdout;
1453 		printCborStream(testBuf.data);
1454 		testBuf.clear();
1455 	}
1456 
1457 	void assertEqual(string file = __MODULE__, size_t line = __LINE__, A, B)(A a, B b)
1458 	{
1459 		assert(a == b, format("%s(%s) != %s(%s) at %s:%s", typeid(a), a, typeid(b), b, file, line));
1460 	}
1461 
1462 	void assertf(Args...)(bool condition, string formatStr, Args args)
1463 	{
1464 		assert(condition, format(formatStr, args));
1465 	}
1466 
1467 	private ubyte[1024] buf;
1468 
1469 	private ubyte[] getEncoded(T)(T value)
1470 	{
1471 		return buf[0..encodeCbor(buf[], value)];
1472 	}
1473 
1474 	private string toHexString(ubyte[] arr)
1475 	{
1476 		return format("0x%(%02x%)", arr);
1477 	}
1478 	private string encodedString(T)(T value)
1479 	{
1480 		return toHexString(getEncoded(value));
1481 	}
1482 	private void printEncoded(T)(T value)
1483 	{
1484 		import std.stdio : writeln;
1485 		encodedString(value).writeln;
1486 	}
1487 	private void cmpEncoded(T)(T value, string encodedStr)
1488 	{
1489 		auto encoded = encodedString(value);
1490 		assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr));
1491 	}
1492 	private void cmpEncodedConst(T)(T value, string encodedStr)
1493 	{
1494 		auto encoded = encodedString(cast(const)value);
1495 		assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr));
1496 	}
1497 	private void cmpEncodedImmutable(T)(T value, string encodedStr)
1498 	{
1499 		auto encoded = encodedString(cast(immutable)value);
1500 		assert(encoded == encodedStr, format("%s != %s", encoded, encodedStr));
1501 	}
1502 }
1503 // Basic encoding/decoding
1504 unittest
1505 {
1506 	// positive integer
1507 	encodeCborInt(testBuf, 1);
1508 	assertToken(CborToken(CborTokenType.posinteger, 1));
1509 	encodeCborInt(testBuf, ubyte.max + 10);
1510 	assertToken(CborToken(CborTokenType.posinteger, ubyte.max + 10));
1511 	encodeCborInt(testBuf, ushort.max + 10);
1512 	assertToken(CborToken(CborTokenType.posinteger, ushort.max + 10));
1513 	encodeCborInt(testBuf, ulong.max);
1514 	assertToken(CborToken(CborTokenType.posinteger, ulong.max));
1515 	// negative integer
1516 	encodeCborInt(testBuf, -10);
1517 	assertToken(CborToken(CborTokenType.neginteger, -10));
1518 	encodeCborInt(testBuf, -long.max);
1519 	assertToken(CborToken(CborTokenType.neginteger, -long.max));
1520 	// bool true
1521 	encodeCborBool(testBuf, true);
1522 	assertToken(CborToken(CborTokenType.boolean, true));
1523 	// bool false
1524 	encodeCborBool(testBuf, false);
1525 	assertToken(CborToken(CborTokenType.boolean, false));
1526 	// null
1527 	encodeCborNull(testBuf);
1528 	assertToken(CborToken(CborTokenType.nil));
1529 	// undefined
1530 	encodeCborUndefined(testBuf);
1531 	assertToken(CborToken(CborTokenType.undefined));
1532 	// break
1533 	encodeCborBreak(testBuf);
1534 	assertToken(CborToken(CborTokenType.breakCode));
1535 	// simple
1536 	encodeCborSimple(testBuf, 10);
1537 	assertToken(CborToken(CborTokenType.simple, 10));
1538 	encodeCborSimple(testBuf, ubyte.max);
1539 	assertToken(CborToken(CborTokenType.simple, ubyte.max));
1540 	foreach(ubyte invalidValue; 25..32) {
1541 		assertThrown!AssertError(encodeCborSimple(testBuf, invalidValue));
1542 	}
1543 	// bytes
1544 	encodeCborBytesHeader(testBuf, 10);
1545 	assertToken(CborToken(CborTokenType.bytesHeader, 10));
1546 	encodeCborBytesHeader(testBuf, ubyte.max);
1547 	assertToken(CborToken(CborTokenType.bytesHeader, ubyte.max));
1548 	encodeCborBytesHeader(testBuf, ushort.max);
1549 	assertToken(CborToken(CborTokenType.bytesHeader, ushort.max));
1550 	encodeCborBytesHeader(testBuf, uint.max);
1551 	assertToken(CborToken(CborTokenType.bytesHeader, uint.max));
1552 	encodeCborBytesHeader(testBuf, ulong.max);
1553 	assertToken(CborToken(CborTokenType.bytesHeader, ulong.max));
1554 	// bytes
1555 	encodeCborBytesHeader(testBuf);
1556 	assertToken(CborToken(CborTokenType.bytesIndefiniteHeader));
1557 	// text
1558 	encodeCborStringHeader(testBuf, 10);
1559 	assertToken(CborToken(CborTokenType.textHeader, 10));
1560 	encodeCborStringHeader(testBuf, ubyte.max);
1561 	assertToken(CborToken(CborTokenType.textHeader, ubyte.max));
1562 	encodeCborStringHeader(testBuf, ushort.max);
1563 	assertToken(CborToken(CborTokenType.textHeader, ushort.max));
1564 	encodeCborStringHeader(testBuf, uint.max);
1565 	assertToken(CborToken(CborTokenType.textHeader, uint.max));
1566 	encodeCborStringHeader(testBuf, ulong.max);
1567 	assertToken(CborToken(CborTokenType.textHeader, ulong.max));
1568 	// text
1569 	encodeCborStringHeader(testBuf);
1570 	assertToken(CborToken(CborTokenType.textIndefiniteHeader));
1571 	// array
1572 	encodeCborArrayHeader(testBuf, 10);
1573 	assertToken(CborToken(CborTokenType.arrayHeader, 10));
1574 	encodeCborArrayHeader(testBuf, ubyte.max);
1575 	assertToken(CborToken(CborTokenType.arrayHeader, ubyte.max));
1576 	encodeCborArrayHeader(testBuf, ushort.max);
1577 	assertToken(CborToken(CborTokenType.arrayHeader, ushort.max));
1578 	encodeCborArrayHeader(testBuf, uint.max);
1579 	assertToken(CborToken(CborTokenType.arrayHeader, uint.max));
1580 	encodeCborArrayHeader(testBuf, ulong.max);
1581 	assertToken(CborToken(CborTokenType.arrayHeader, ulong.max));
1582 	// array
1583 	encodeCborArrayHeader(testBuf);
1584 	assertToken(CborToken(CborTokenType.arrayIndefiniteHeader));
1585 	// map
1586 	encodeCborMapHeader(testBuf, 10);
1587 	assertToken(CborToken(CborTokenType.mapHeader, 10));
1588 	encodeCborMapHeader(testBuf, ubyte.max);
1589 	assertToken(CborToken(CborTokenType.mapHeader, ubyte.max));
1590 	encodeCborMapHeader(testBuf, ushort.max);
1591 	assertToken(CborToken(CborTokenType.mapHeader, ushort.max));
1592 	encodeCborMapHeader(testBuf, uint.max);
1593 	assertToken(CborToken(CborTokenType.mapHeader, uint.max));
1594 	encodeCborMapHeader(testBuf, ulong.max);
1595 	assertToken(CborToken(CborTokenType.mapHeader, ulong.max));
1596 	// map
1597 	encodeCborMapHeader(testBuf);
1598 	assertToken(CborToken(CborTokenType.mapIndefiniteHeader));
1599 	// tag
1600 	encodeCborTag(testBuf, 1);
1601 	assertToken(CborToken(CborTokenType.tag, 1));
1602 	encodeCborTag(testBuf, ubyte.max);
1603 	assertToken(CborToken(CborTokenType.tag, ubyte.max));
1604 	encodeCborTag(testBuf, ushort.max);
1605 	assertToken(CborToken(CborTokenType.tag, ushort.max));
1606 	encodeCborTag(testBuf, uint.max);
1607 	assertToken(CborToken(CborTokenType.tag, uint.max));
1608 	encodeCborTag(testBuf, ulong.max);
1609 	assertToken(CborToken(CborTokenType.tag, ulong.max));
1610 }
1611 
1612 unittest // encoding
1613 {
1614 	testEncoding!cmpEncoded();
1615 	testEncoding!cmpEncodedConst();
1616 	testEncoding!cmpEncodedImmutable();
1617 }
1618 
1619 version(unittest)
1620 private void testEncoding(alias cmpEncoded)()
1621 {
1622 	// Test vectors
1623 	cmpEncoded(0, "0x00");
1624 	cmpEncoded(1, "0x01");
1625 	cmpEncoded(10, "0x0a");
1626 	cmpEncoded(23, "0x17");
1627 	cmpEncoded(24, "0x1818");
1628 	cmpEncoded(25, "0x1819");
1629 	cmpEncoded(100, "0x1864");
1630 	cmpEncoded(1000, "0x1903e8");
1631 	cmpEncoded(1000000, "0x1a000f4240");
1632 	cmpEncoded(1000000000000, "0x1b000000e8d4a51000");
1633 
1634 	//bignums
1635 	//cmpEncoded(18446744073709551615, "0x1bffffffffffffffff");
1636 	//cmpEncoded(18446744073709551616, "0xc249010000000000000000");
1637 	//cmpEncoded(-18446744073709551616, "0x3bffffffffffffffff");
1638 	//cmpEncoded(-18446744073709551617, "0xc349010000000000000000");
1639 	cmpEncoded(-1, "0x20");
1640 	cmpEncoded(-10, "0x29");
1641 	cmpEncoded(-100, "0x3863");
1642 	cmpEncoded(-1000, "0x3903e7");
1643 
1644 	//printEncoded(0.0f, "0xf90000"); // half-float
1645 	//printEncoded(-0.0f, "0xf98000"); // half-float
1646 	//printEncoded(1.0f, "0xf93c00"); // half-float
1647 	cmpEncoded(1.1, "0xfb3ff199999999999a");
1648 	//printEncoded(1.5f, "0xf93e00"); // half-float
1649 	//printEncoded(65504.0f, "0xf97bff"); // half-float
1650 	cmpEncoded(100000.0f, "0xfa47c35000");
1651 	cmpEncoded(3.4028234663852886e+38f, "0xfa7f7fffff");
1652 	cmpEncoded(1.0e+300, "0xfb7e37e43c8800759c");
1653 	//printEncoded(5.960464477539063e-8f, "0xf90001"); // half-float
1654 	//printEncoded(0.00006103515625f, "0xf90400"); // half-float
1655 	//printEncoded(-4.0f, "0xf9c400"); // half-float
1656 	cmpEncoded(-4.1, "0xfbc010666666666666");
1657 	cmpEncoded(1.0f/0, "0xfa7f800000"); // Infinity
1658 	//cmpEncoded(NaN, "0xfa7fc00000"); // produces 0xfa7fe00000
1659 	cmpEncoded(-1.0f/0, "0xfaff800000"); // -Infinity
1660 	cmpEncoded(1.0/0, "0xfb7ff0000000000000"); // Infinity
1661 	//cmpEncoded(NaN, "0xfb7ff8000000000000"); // produces 0xfb7ffc000000000000
1662 	cmpEncoded(-1.0/0, "0xfbfff0000000000000");// -Infinity
1663 
1664 	cmpEncoded(false, "0xf4");
1665 	cmpEncoded(true, "0xf5");
1666 	cmpEncoded(null, "0xf6");
1667 
1668 	// raw arrays, ubyte[]
1669 	cmpEncoded(cast(ubyte[])[], "0x40");
1670 	cmpEncoded(cast(ubyte[])[1, 2, 3, 4], "0x4401020304");
1671 
1672 	// strings
1673 	cmpEncoded("", "0x60");
1674 	cmpEncoded("a", "0x6161");
1675 	cmpEncoded("IETF", "0x6449455446");
1676 	cmpEncoded("\"\\", "0x62225c");
1677 	cmpEncoded("\u00fc", "0x62c3bc");
1678 	cmpEncoded("\u6c34", "0x63e6b0b4");
1679 	//cmpEncoded("\ud800\udd51", "0x64f0908591"); // invalid character
1680 
1681 	import std.typecons : tuple;
1682 	// arrays
1683 	cmpEncoded([], "0x80");
1684 	cmpEncoded([1, 2, 3], "0x83010203");
1685 	cmpEncoded(tuple(1, [2, 3], [4, 5]), "0x8301820203820405");
1686 	enum arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
1687 				15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25];
1688 	cmpEncoded(arr, "0x98190102030405060708090a0b0c0d0e0f101112131415161718181819");
1689 
1690 	// AA
1691 	uint[uint] emptyAA;
1692 	cmpEncoded(emptyAA, "0xa0");
1693 	cmpEncoded([1:2], "0xa10102");
1694 
1695 	cmpEncoded(cast(ubyte)42, "0x182a");
1696 	cmpEncoded(cast(ushort)42, "0x182a");
1697 	cmpEncoded(cast(uint)42, "0x182a");
1698 	cmpEncoded(cast(ulong)42, "0x182a");
1699 	cmpEncoded(cast(byte)42, "0x182a");
1700 	cmpEncoded(cast(short)42, "0x182a");
1701 	cmpEncoded(cast(int)42, "0x182a");
1702 	cmpEncoded(cast(long)42, "0x182a");
1703 }
1704 
1705 unittest // decoding
1706 {
1707 	ubyte[1024] buf;
1708 	size_t size;
1709 
1710 	int[string] maptest1;
1711 	// decode map from empty map
1712 	size = encodeCbor(buf[], maptest1);
1713 	assertEqual(decodeCborSingle!(int[string])(buf[0..size]), maptest1);
1714 	// decode map from null
1715 	size = encodeCbor(buf[], null);
1716 	assertEqual(decodeCborSingle!(int[string])(buf[0..size]), maptest1);
1717 }
1718 
1719 // indefinite-length encoding
1720 unittest
1721 {
1722 	// {"a": 1, "b": [2, 3]}
1723 	encodeCborMapHeader(testBuf, 2);
1724 		encodeCborString(testBuf, "a");
1725 		encodeCborInt(testBuf, 1);
1726 		encodeCborString(testBuf, "b");
1727 		encodeCborArrayHeader(testBuf, 2);
1728 			encodeCborInt(testBuf, 2);
1729 			encodeCborInt(testBuf, 3);
1730 	assertHexAndClear("0xa26161016162820203");
1731 
1732 	// ["a", {"b": "c"}]
1733 	encodeCborArrayHeader(testBuf, 2);
1734 		encodeCborString(testBuf, "a");
1735 		encodeCborMapHeader(testBuf, 1);
1736 			encodeCborString(testBuf, "b");
1737 			encodeCborString(testBuf, "c");
1738 	assertHexAndClear("0x826161a161626163");
1739 
1740 	// ["a": "A", "b": "B", "c":"C", "d": "D", "e": "E"]
1741 	encodeCborMapHeader(testBuf, 5);
1742 		foreach(chr; "aAbBcCdDeE")
1743 			encodeCborString(testBuf, (&chr)[0..1]);
1744 	assertHexAndClear("0xa56161614161626142616361436164614461656145");
1745 
1746 	// (_ h'0102', h'030405')
1747 	encodeCborBytesHeader(testBuf);
1748 		encodeCborBytes(testBuf, cast(ubyte[])[1, 2]);
1749 		encodeCborBytes(testBuf, cast(ubyte[])[3, 4, 5]);
1750 		encodeCborBreak(testBuf);
1751 	assertHexAndClear("0x5f42010243030405ff");
1752 
1753 	// (_ "strea", "ming")
1754 	encodeCborStringHeader(testBuf);
1755 		encodeCborString(testBuf, "strea");
1756 		encodeCborString(testBuf, "ming");
1757 		encodeCborBreak(testBuf);
1758 	assertHexAndClear("0x7f657374726561646d696e67ff");
1759 
1760 	// [_ ]
1761 	encodeCborArrayHeader(testBuf);
1762 		encodeCborBreak(testBuf);
1763 	assertHexAndClear("0x9fff");
1764 
1765 	// [_ 1, [2, 3], [_ 4, 5]]
1766 	encodeCborArrayHeader(testBuf);
1767 		encodeCborInt(testBuf, 1);
1768 		encodeCborArrayHeader(testBuf, 2);
1769 			encodeCborInt(testBuf, 2);
1770 			encodeCborInt(testBuf, 3);
1771 		encodeCborArrayHeader(testBuf);
1772 			encodeCborInt(testBuf, 4);
1773 			encodeCborInt(testBuf, 5);
1774 			encodeCborBreak(testBuf);
1775 		encodeCborBreak(testBuf);
1776 	assertHexAndClear("0x9f018202039f0405ffff");
1777 
1778 	// [_ 1, [2, 3], [4, 5]]
1779 	encodeCborArrayHeader(testBuf);
1780 		encodeCborInt(testBuf, 1);
1781 		encodeCborArrayHeader(testBuf, 2);
1782 			encodeCborInt(testBuf, 2);
1783 			encodeCborInt(testBuf, 3);
1784 		encodeCborArrayHeader(testBuf, 2);
1785 			encodeCborInt(testBuf, 4);
1786 			encodeCborInt(testBuf, 5);
1787 		encodeCborBreak(testBuf);
1788 	assertHexAndClear("0x9f01820203820405ff");
1789 
1790 	// [1, [2, 3], [_ 4, 5]]
1791 	encodeCborArrayHeader(testBuf, 3);
1792 		encodeCborInt(testBuf, 1);
1793 		encodeCborArrayHeader(testBuf, 2);
1794 			encodeCborInt(testBuf, 2);
1795 			encodeCborInt(testBuf, 3);
1796 		encodeCborArrayHeader(testBuf);
1797 			encodeCborInt(testBuf, 4);
1798 			encodeCborInt(testBuf, 5);
1799 			encodeCborBreak(testBuf);
1800 	assertHexAndClear("0x83018202039f0405ff");
1801 
1802 	// [1, [_ 2, 3], [4, 5]]
1803 	encodeCborArrayHeader(testBuf, 3);
1804 		encodeCborInt(testBuf, 1);
1805 		encodeCborArrayHeader(testBuf);
1806 			encodeCborInt(testBuf, 2);
1807 			encodeCborInt(testBuf, 3);
1808 			encodeCborBreak(testBuf);
1809 		encodeCborArrayHeader(testBuf, 2);
1810 			encodeCborInt(testBuf, 4);
1811 			encodeCborInt(testBuf, 5);
1812 	assertHexAndClear("0x83019f0203ff820405");
1813 
1814 	import std.range : iota;
1815 	import std.algorithm : each;
1816 	// [_ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25],
1817 	encodeCborArrayHeader(testBuf);
1818 		iota(1, 26).each!(a => encodeCborInt(testBuf, a));
1819 		encodeCborBreak(testBuf);
1820 	assertHexAndClear("0x9f0102030405060708090a0b0c0d0e0f101112131415161718181819ff");
1821 
1822 	// {_ "a": 1, "b": [_ 2, 3]}
1823 	encodeCborMapHeader(testBuf);
1824 		encodeCborString(testBuf, "a");
1825 		encodeCborInt(testBuf, 1);
1826 		encodeCborString(testBuf, "b");
1827 		encodeCborArrayHeader(testBuf);
1828 			encodeCborInt(testBuf, 2);
1829 			encodeCborInt(testBuf, 3);
1830 			encodeCborBreak(testBuf);
1831 		encodeCborBreak(testBuf);
1832 	assertHexAndClear("0xbf61610161629f0203ffff");
1833 
1834 	// ["a", {_ "b": "c"}]
1835 	encodeCborArrayHeader(testBuf, 2);
1836 		encodeCborString(testBuf, "a");
1837 		encodeCborMapHeader(testBuf);
1838 			encodeCborString(testBuf, "b");
1839 			encodeCborString(testBuf, "c");
1840 			encodeCborBreak(testBuf);
1841 	assertHexAndClear("0x826161bf61626163ff");
1842 
1843 	// {_ "Fun": true, "Amt": -2}
1844 	encodeCborMapHeader(testBuf);
1845 		encodeCborString(testBuf, "Fun");
1846 		encodeCborBool(testBuf, true);
1847 		encodeCborString(testBuf, "Amt");
1848 		encodeCborInt(testBuf, -2);
1849 		encodeCborBreak(testBuf);
1850 	assertHexAndClear("0xbf6346756ef563416d7421ff");
1851 }
1852 
1853 // indefinite-length decoding
1854 unittest
1855 {
1856 	encodeCborBytesHeader(testBuf);
1857 		encodeCborBytes(testBuf, cast(ubyte[])[1, 2]);
1858 		encodeCborBytes(testBuf, cast(ubyte[])[1, 2]);
1859 		encodeCborBreak(testBuf);
1860 	ubyte[] output1;
1861 	decodeCbor(testBuf.data, output1);
1862 	assertEqual(output1, [1,2,1,2]);
1863 	testBuf.clear();
1864 
1865 	encodeCborStringHeader(testBuf);
1866 		encodeCborString(testBuf, "zzz");
1867 		encodeCborString(testBuf, "zzz");
1868 		encodeCborString(testBuf, "zzz");
1869 		encodeCborBreak(testBuf);
1870 	string output2;
1871 	decodeCbor(testBuf.data, output2);
1872 	assertEqual(output2, "zzzzzzzzz");
1873 	testBuf.clear();
1874 
1875 	encodeCborArrayHeader(testBuf);
1876 		encodeCborInt(testBuf, 1);
1877 		encodeCborInt(testBuf, 2);
1878 		encodeCborInt(testBuf, 3);
1879 		encodeCborInt(testBuf, 4);
1880 		encodeCborInt(testBuf, 5);
1881 		encodeCborBreak(testBuf);
1882 	int[] output3;
1883 	decodeCbor(testBuf.data, output3);
1884 	assertEqual(output3, [1,2,3,4,5]);
1885 	testBuf.clear();
1886 
1887 	encodeCborMapHeader(testBuf);
1888 		encodeCborInt(testBuf, 1);
1889 		encodeCborString(testBuf, "abc");
1890 		encodeCborInt(testBuf, 2);
1891 		encodeCborString(testBuf, "def");
1892 		encodeCborBreak(testBuf);
1893 	string[int] output4;
1894 	decodeCbor(testBuf.data, output4);
1895 	assertEqual(output4, [1:"abc", 2:"def"]);
1896 	testBuf.clear();
1897 }
1898 
1899 // Printing
1900 unittest
1901 {
1902 	import std.range : cycle, take, takeExactly, repeat, Repeat, iota;
1903 
1904 	encodeCborInt(testBuf, 1);
1905 	cmpStreamStringAndReset("(posinteger, 1)\n");
1906 
1907 	encodeCborArrayHeader(testBuf);
1908 		encodeCborInt(testBuf, 1);
1909 		encodeCborArrayHeader(testBuf, 2);
1910 			encodeCborInt(testBuf, 2);
1911 			encodeCborInt(testBuf, 3);
1912 		encodeCborArrayHeader(testBuf);
1913 			encodeCborInt(testBuf, 4);
1914 			encodeCborInt(testBuf, 5);
1915 			encodeCborBreak(testBuf);
1916 		encodeCborBreak(testBuf);
1917 
1918 	cmpStreamStringAndReset(
1919 	"(array, _)\n"~
1920 	"  (posinteger, 1)\n"~
1921 	"  (array, 2)\n"~
1922 	"    (posinteger, 2)\n"~
1923 	"    (posinteger, 3)\n"~
1924 	"  (array, _)\n"~
1925 	"    (posinteger, 4)\n"~
1926 	"    (posinteger, 5)\n"~
1927 	"    (break)\n"~
1928 	"  (break)\n");
1929 
1930 	encodeCborTag(testBuf, 100);
1931 		encodeCborBytesHeader(testBuf, 16);
1932 			auto bytes = iota!ubyte(10).cycle.take(16);
1933 			encodeCborBytesItems(testBuf, bytes);
1934 
1935 	cmpStreamStringAndReset(
1936 	"(tag, 100)\n"~
1937 	"  (bytes, 16)\n"~
1938 	"    (00010203040506070809000102030405)\n");
1939 
1940 	encodeCbor(testBuf, true);
1941 	cmpStreamStringAndReset("(true)\n");
1942 
1943 	encodeCbor(testBuf, false);
1944 	cmpStreamStringAndReset("(false)\n");
1945 
1946 	encodeCbor(testBuf, null);
1947 	cmpStreamStringAndReset("(null)\n");
1948 
1949 	encodeCborUndefined(testBuf);
1950 	cmpStreamStringAndReset("(undefined)\n");
1951 
1952 	encodeCborSimple(testBuf, 4);
1953 	cmpStreamStringAndReset("(simple, 4)\n");
1954 
1955 	encodeCborInt(testBuf, -4);
1956 	cmpStreamStringAndReset("(neginteger, -4)\n");
1957 
1958 	encodeCborFloat(testBuf, 0.0);
1959 	cmpStreamStringAndReset("(floating, 0)\n");
1960 
1961 	encodeCborMapHeader(testBuf, 1);
1962 		encodeCborInt(testBuf, 1);
1963 		encodeCborInt(testBuf, 2);
1964 	cmpStreamStringAndReset(
1965 	"(map, 1)\n"~
1966 	"  (posinteger, 1)\n"~
1967 	"  (posinteger, 2)\n");
1968 
1969 	encodeCborMapHeader(testBuf);
1970 		encodeCborBreak(testBuf);
1971 		cmpStreamStringAndReset(
1972 	"(map, _)\n"~
1973 	"  (break)\n");
1974 
1975 	encodeCborBytesHeader(testBuf);
1976 		encodeCborBreak(testBuf);
1977 		cmpStreamStringAndReset(
1978 	"(bytes, _)\n"~
1979 	"  (break)\n");
1980 
1981 	encodeCborStringHeader(testBuf, 0);
1982 		cmpStreamStringAndReset(
1983 	"(text, 0)\n"~
1984 	`  ""`~"\n");
1985 
1986 	encodeCborStringHeader(testBuf);
1987 		encodeCborBreak(testBuf);
1988 		cmpStreamStringAndReset(
1989 	"(text, _)\n"~
1990 	"  (break)\n");
1991 
1992 	struct NoLengthRange(int val)
1993 	{
1994 		private int itemsLeft = 5;
1995 		ubyte front() { return val; }
1996 		bool empty() { return itemsLeft == 0; }
1997 		void popFront() { --itemsLeft; }
1998 	}
1999 	// test byte range withou length
2000 	encodeCborBytesHeader(testBuf, 5);
2001 		encodeCborBytesItems(testBuf, NoLengthRange!1());
2002 
2003 	cmpStreamStringAndReset(
2004 	"(bytes, 5)\n"~
2005 	"  (0101010101)\n");
2006 }
2007 
2008 /// structs, classes, tuples
2009 unittest // decoding exact
2010 {
2011 	static struct Inner
2012 	{
2013 		int[] array;
2014 		string someText;
2015 	}
2016 
2017 	static struct Test1
2018 	{
2019 		ubyte b;
2020 		short s;
2021 		uint i;
2022 		long l;
2023 		float f;
2024 		double d;
2025 		ubyte[] arr;
2026 		int[] intarr;
2027 		string str;
2028 		Inner inner;
2029 
2030 		void fun(){} // not encoded
2031 		void* pointer; // not encoded
2032 	}
2033 
2034 	ubyte[1024] buf1;
2035 	size_t size;
2036 
2037 	Test1 test = Test1(42, -120, 111111, -123456789, 0.1234, -0.987654,
2038 		cast(ubyte[])[1,2,3,4,5,6,7,8], [42, 41], "It is a test string",
2039 		Inner([1,2,3,4,5], "Test of inner struct"));
2040 
2041 	size = encodeCborArray(buf1[], test);
2042 	Test1 result = decodeCborSingle!Test1(buf1[0..size]);
2043 	assertEqual(test, result);
2044 
2045 	import std.typecons : Tuple;
2046 
2047 	alias TupleVal = Tuple!(int, string, byte, string);
2048 	auto testTuple = TupleVal(1, "hello", 56, "there");
2049 
2050 	size = encodeCborArray(buf1[], testTuple);
2051 	TupleVal resultTuple = decodeCborSingle!TupleVal(buf1[0..size]);
2052 	assertEqual(testTuple, resultTuple);
2053 
2054 	static class Inner2
2055 	{
2056 		int val;
2057 		ulong u;
2058 	}
2059 
2060 	static class Test2
2061 	{
2062 		ubyte b;
2063 		short s;
2064 		uint i;
2065 		long l;
2066 		float f;
2067 		double d;
2068 		ubyte[] arr;
2069 		string str;
2070 		Inner2 inner;
2071 	}
2072 
2073 	Test2 testClass = new Test2();
2074 	testClass.b = 42;
2075 	testClass.s = -120;
2076 	testClass.i = 111111;
2077 	testClass.l = -123456789;
2078 	testClass.f = 0.1234;
2079 	testClass.d = -0.987654;
2080 	testClass.arr = cast(ubyte[])[1,2,3,4,5,6,7,8];
2081 	testClass.str = "It is a test string";
2082 	testClass.inner = null;
2083 
2084 	size = encodeCborArray(buf1[], testClass);
2085 	ubyte[] encodedBuf = buf1[0..size];
2086 	Test2 resultClass = decodeCborSingle!Test2(encodedBuf);
2087 	assertEqual(encodedBuf.length, 0);
2088 
2089 	foreach(i, m; resultClass.tupleof)
2090 		assertEqual(testClass.tupleof[i], m);
2091 
2092 	testClass.inner = new Inner2;
2093 	testClass.inner.val = -555;
2094 	testClass.inner.u = 123456789;
2095 
2096 	size = encodeCborArray(buf1[], testClass);
2097 	resultClass = decodeCborSingle!Test2(buf1[0..size]);
2098 
2099 	foreach(i, m; resultClass.inner.tupleof)
2100 		assertEqual(testClass.inner.tupleof[i], m);
2101 }
2102 
2103 unittest // decoding with dup
2104 {
2105 	ubyte[128] buf1;
2106 	size_t size;
2107 
2108 	// with dup
2109 	size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]);
2110 	ubyte[] data = decodeCborSingleDup!(ubyte[])(buf1[0..size]);
2111 	buf1[] = 0; // zero-out initial data, to check that result was duped.
2112 
2113 	assertEqual(data, [0, 1, 2, 3, 4, 5]);
2114 
2115 	// without dup
2116 	size = encodeCbor(buf1[], cast(ubyte[])[0, 1, 2, 3, 4, 5]);
2117 	data = decodeCborSingle!(ubyte[])(buf1[0..size]);
2118 	buf1[] = 0;
2119 
2120 	assertEqual(data, [0, 0, 0, 0, 0, 0]);
2121 
2122 	// dup is only needed for ubyte[] and string types,
2123 	// because they can be sliced from ubyte[] input range
2124 	size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array
2125 	int[] intData = decodeCborSingle!(int[])(buf1[0..size]); // no dup
2126 	buf1[] = 0;
2127 
2128 	assertEqual(intData, [0, 1, 2, 3, 4, 5]);
2129 
2130 	// also no slicing will occur if data was encoded as regular array and than
2131 	// ubyte[] retreived. integer[] -> ubyte[] cast would occur causing possible data loss.
2132 	// size = encodeCbor(buf1[], [0, 1, 2, 3, 4, 5]); // int array
2133 	// integer array cannot be decoded as ubyte[], only raw arrays can
2134 	// data = decodeCborSingle!(ubyte[])(buf1[0..size]); // CborException: Attempt to cast array to ubyte[]
2135 }
2136 
2137 unittest // static arrays
2138 {
2139 	ubyte[128] buf;
2140 	size_t size;
2141 
2142 	// raw static array
2143 	size = encodeCbor(buf[], cast(ubyte[6])[0, 1, 2, 3, 4, 5]);
2144 	ubyte[6] data1 = decodeCborSingle!(ubyte[6])(buf[0..size]);
2145 	assertEqual(data1, [0, 1, 2, 3, 4, 5]);
2146 
2147 	// regular static array
2148 	size = encodeCbor(buf[], cast(int[6])[0, 1, 2, 3, 4, 5]);
2149 	int[6] data2 = decodeCborSingle!(int[6])(buf[0..size]);
2150 	assertEqual(data2, [0, 1, 2, 3, 4, 5]);
2151 }
2152 
2153 unittest // const
2154 {
2155 	ubyte[1024] buf;
2156 	size_t size = encodeCbor(buf[], cast(const)0.0);
2157 
2158 	double cam2 = decodeCborSingle!double(buf[0..size]);
2159 }
2160 
2161 unittest // using output range
2162 {
2163 	import std.array : Appender;
2164 
2165 	Appender!(ubyte[]) buffer;
2166 	ubyte[] testData = [0, 1, 2, 3, 4, 5];
2167 
2168 	size_t size = encodeCbor(buffer, testData);
2169 
2170 	assertEqual(testData, decodeCborSingle!(ubyte[])(buffer.data));
2171 }
2172 
2173 unittest // recursive type
2174 {
2175 	import std.array : Appender;
2176 
2177 	Appender!(ubyte[]) a;
2178 	class Class
2179 	{
2180 		Class[] groups;
2181 	}
2182 	encodeCborArray(a, Class[].init);
2183 }
2184 
2185 unittest // char arrays
2186 {
2187 	ubyte[1024] buf;
2188 
2189 	size_t size = encodeCbor(buf[], cast(char[])"abc");
2190 	char[] str1 = decodeCborSingle!(char[])(buf[0..size]);
2191 	assertEqual(str1, "abc");
2192 
2193 	size = encodeCbor(buf[], cast(const char[])"abc");
2194 	const char[] str2 = decodeCborSingle!(const char[])(buf[0..size]);
2195 	assertEqual(str2, "abc");
2196 
2197 	size = encodeCbor(buf[], cast(immutable char[])"abc");
2198 	immutable char[] str3 = decodeCborSingle!(immutable char[])(buf[0..size]);
2199 	assertEqual(str3, "abc");
2200 }
2201 
2202 unittest // char wchar dchar
2203 {
2204 	ubyte[1024] buf;
2205 	char testChar = 'c';
2206 
2207 	size_t size = encodeCbor(buf[], cast(char)testChar);
2208 	char chr = decodeCborSingle!(char)(buf[0..size]);
2209 	assertEqual(chr, testChar);
2210 
2211 	size = encodeCbor(buf[], cast(wchar)testChar);
2212 	wchar wchr = decodeCborSingle!(wchar)(buf[0..size]);
2213 	assertEqual(wchr, testChar);
2214 
2215 	size = encodeCbor(buf[], cast(dchar)testChar);
2216 	dchar dchr = decodeCborSingle!(dchar)(buf[0..size]);
2217 	assertEqual(dchr, testChar);
2218 
2219 	size = encodeCbor(buf[], cast(const char)testChar);
2220 	const char constchr = decodeCborSingle!(const char)(buf[0..size]);
2221 	assertEqual(constchr, testChar);
2222 
2223 	size = encodeCbor(buf[], cast(const wchar)testChar);
2224 	const wchar constwchr = decodeCborSingle!(const wchar)(buf[0..size]);
2225 	assertEqual(constwchr, testChar);
2226 
2227 	size = encodeCbor(buf[], cast(immutable dchar)testChar);
2228 	immutable dchar immdchr = decodeCborSingle!(immutable dchar)(buf[0..size]);
2229 	assertEqual(immdchr, testChar);
2230 }
2231 
2232 unittest // wstring dstring; static char wchar dchar arrays
2233 {
2234 	ubyte[1024] buf;
2235 
2236 	size_t size = encodeCbor(buf[], "hello w"w);
2237 	wstring wstr = decodeCborSingle!(wstring)(buf[0..size]);
2238 	assertEqual(wstr, "hello w"w);
2239 
2240 	size = encodeCbor(buf[], "hello d"d);
2241 	dstring dstr = decodeCborSingle!(dstring)(buf[0..size]);
2242 	assertEqual(dstr, "hello d"d);
2243 
2244 	size = encodeCbor(buf[], cast(char[7])"hello c");
2245 	char[7] str1 = decodeCborSingle!(char[7])(buf[0..size]);
2246 	assertEqual(str1, "hello c");
2247 
2248 	size = encodeCbor(buf[], cast(wchar[7])"hello w");
2249 	wchar[7] wstr1 = decodeCborSingle!(wchar[7])(buf[0..size]);
2250 	assertEqual(wstr1, "hello w"w);
2251 
2252 	size = encodeCbor(buf[], cast(dchar[7])"hello d");
2253 	dchar[7] dstr1 = decodeCborSingle!(dchar[7])(buf[0..size]);
2254 	assertEqual(dstr1, "hello d"d);
2255 }
2256 
2257 unittest // char[] wchar[] dchar[]
2258 {
2259 	ubyte[1024] buf;
2260 
2261 	size_t size = encodeCbor(buf[], cast(char[])"hello");
2262 	char[] str1 = decodeCborSingle!(char[])(buf[0..size]);
2263 	assertEqual(str1, "hello");
2264 
2265 	size = encodeCbor(buf[], cast(wchar[])"hello");
2266 	wchar[] wstr1 = decodeCborSingle!(wchar[])(buf[0..size]);
2267 	assertEqual(wstr1, "hello"w);
2268 
2269 	size = encodeCbor(buf[], cast(dchar[])"hello");
2270 	dchar[] dstr1 = decodeCborSingle!(dchar[])(buf[0..size]);
2271 	assertEqual(dstr1, "hello"d);
2272 }
2273 
2274 unittest // flatten mode
2275 {
2276 	ubyte[1024] buf;
2277 	size_t size;
2278 
2279 	size = encodeCbor(buf[], cast(ubyte[2])[1, 2]);
2280 	assertEqual(toHexString(buf[0..size]), "0x420102");
2281 
2282 	// Raw
2283 	size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[2])[1, 2]);
2284 	assertEqual(toHexString(buf[0..size]), "0x0102");
2285 	auto arr1 = decodeCborSingle!(ubyte[2], Yes.Flatten)(buf[0..size]);
2286 	assertEqual(arr1, cast(ubyte[2])[1, 2]);
2287 
2288 	// Array
2289 	size = encodeCbor!(Yes.Flatten)(buf[], cast(int[2])[1, 2]);
2290 	assertEqual(toHexString(buf[0..size]), "0x0102");
2291 	auto arr2 = decodeCborSingle!(int[2], Yes.Flatten)(buf[0..size]);
2292 	assertEqual(arr2, cast(int[2])[1, 2]);
2293 
2294 	// String
2295 	size = encodeCbor!(Yes.Flatten)(buf[], cast(immutable(char)[1])"a");
2296 	assertEqual(toHexString(buf[0..size]), "0x61");
2297 	auto str1 = decodeCborSingle!(immutable(char)[1], Yes.Flatten)(buf[0..size]);
2298 	assertEqual(str1, "a");
2299 
2300 	// Tuple
2301 	import std.typecons : tuple;
2302 	size = encodeCbor!(Yes.Flatten)(buf[], tuple(1, 2));
2303 	assertEqual(toHexString(buf[0..size]), "0x0102");
2304 
2305 	// Struct
2306 	static struct A {
2307 		int a, b;
2308 	}
2309 	static struct B {
2310 		A a;
2311 	}
2312 	static struct C {
2313 		B b;
2314 	}
2315 	static struct D {
2316 		C c;
2317 	}
2318 	size = encodeCbor!(Yes.Flatten)(buf[], D(C(B(A(1, 2)))));
2319 	assertEqual(toHexString(buf[0..size]), "0x0102");
2320 
2321 	size = encodeCbor!(No.Flatten)(buf[], D(C(B(A(1, 2)))));
2322 	assertEqual(toHexString(buf[0..size]), "0x818181820102");
2323 
2324 	size = encodeCbor!(Yes.Flatten)(buf[], cast(int)1);
2325 	assertEqual(decodeCborSingle!(int, Yes.Flatten)(buf[0..size]), 1);
2326 
2327 	size = encodeCbor!(Yes.Flatten)(buf[], cast(float)1);
2328 	assertEqual(decodeCborSingle!(float, Yes.Flatten)(buf[0..size]), 1);
2329 
2330 	size = encodeCbor!(Yes.Flatten)(buf[], true);
2331 	assertEqual(decodeCborSingle!(bool, Yes.Flatten)(buf[0..size]), true);
2332 
2333 	size = encodeCbor!(Yes.Flatten)(buf[], 'a');
2334 	assertEqual(decodeCborSingle!(char, Yes.Flatten)(buf[0..size]), 'a');
2335 
2336 	size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[])[1,2]);
2337 	assertEqual(decodeCborSingle!(ubyte[], Yes.Flatten)(buf[0..size]), cast(ubyte[])[1,2]);
2338 
2339 	size = encodeCbor!(Yes.Flatten)(buf[], cast(ubyte[2])[1,2]);
2340 	assertEqual(decodeCborSingle!(ubyte[2], Yes.Flatten)(buf[0..size]), cast(ubyte[2])[1,2]);
2341 
2342 	size = encodeCborAggregate!(Yes.WithFieldName, Yes.Flatten)(buf[], new class{int a, b;});
2343 	assertEqual(decodeCborSingle!(int[string], Yes.Flatten)(buf[0..size]), ["a":0,"b":0]);
2344 
2345 	static struct ivec4 {int x,y,z,w;}
2346 	static struct Transform
2347 	{
2348 		ivec4 pos;
2349 	}
2350 
2351 	size = encodeCbor!(Yes.Flatten)(buf[], Transform( ivec4(1,2,3,4) ) );
2352 	assertEqual(decodeCborSingle!(Transform, Yes.Flatten)(buf[0..size]), Transform( ivec4(1,2,3,4) ));
2353 }
2354 
2355 @nogc unittest // @nogc
2356 {
2357 	ubyte[1024] buf;
2358 	size_t size;
2359 
2360 	size = encodeCbor(buf[], 42);
2361 	int num = decodeCborSingle!int(buf[0..size]);
2362 }
2363 
2364 unittest // @ignore
2365 {
2366 	ubyte[1024] buf;
2367 	size_t size;
2368 
2369 	// Struct
2370 	static struct Test {
2371 		int a;
2372 		@ignore int b;
2373 	}
2374 
2375 	encodeCbor(testBuf, Test(42, 84));
2376 	cmpStreamStringAndReset(
2377 		"(array, 1)\n"~
2378 		"  (posinteger, 42)\n");
2379 
2380 	size = encodeCbor(buf[], Test(42, 84));
2381 	Test decoded = decodeCborSingle!Test(buf[0..size]);
2382 	assert(decoded == Test(42, 0));
2383 }
2384 
2385 unittest // readme example
2386 {
2387 	//import cbor;
2388 
2389 	static struct Inner
2390 	{
2391 		int[] array;
2392 		string someText;
2393 	}
2394 
2395 	static struct Test
2396 	{
2397 		ubyte b;
2398 		short s;
2399 		uint i;
2400 		long l;
2401 		float f;
2402 		double d;
2403 		ubyte[] arr;
2404 		string str;
2405 		Inner inner;
2406 		@ignore long ignored; // not encoded
2407 
2408 		void fun(){} // not encoded
2409 		void* pointer; // not encoded
2410 		int* numPointer; // not encoded
2411 	}
2412 
2413 	import std.array : appender;
2414 	auto buffer = appender!(ubyte[])();
2415 
2416 	Test test = Test(42, -120, 111111, -123456789, 0.1234, -0.987654,
2417 		cast(ubyte[])[1,2,3,4,5,6,7,8], "It is a test string",
2418 		Inner([1,2,3,4,5], "Test of inner struct"), 88);
2419 
2420 	encodeCbor(buffer, test);
2421 
2422 	// ubyte[] and string types are slices of input ubyte[].
2423 	Test result = decodeCborSingle!Test(buffer.data);
2424 	// or
2425 	// Test result;
2426 	// decodeCbor(buffer.data, result);
2427 
2428 	// decodeCborSingleDup can be used to auto-dup those types.
2429 
2430 	assert(result.ignored == 0); // ignored was not (de)serialized.
2431 	result.ignored = test.ignored;
2432 	assert(test == result);
2433 
2434 
2435 	struct Vector4f
2436 	{
2437 		float x, y, z, w;
2438 	}
2439 
2440 	// encoded as array of 4 floats
2441 	encodeCbor(buffer, Vector4f(1,2,3,4));
2442 	buffer.clear();
2443 
2444 	// Flat encoding
2445 	// encoded as 4 floats
2446 	encodeCbor!(Yes.Flatten)(buffer, Vector4f(1,2,3,4));
2447 
2448 	// You need to use Yes.Flatten on both sides.
2449 	decodeCborSingle!(Vector4f, Yes.Flatten)(buffer.data);
2450 
2451 	// Printing cbor data (by default into stdout)
2452 	printCborStream(buffer.data);
2453 	// prints
2454 	// (floating, 1)
2455 	// (floating, 2)
2456 	// (floating, 3)
2457 	// (floating, 4)
2458 
2459 	// you can also use other sink
2460 	auto textbuffer = appender!(char[])();
2461 	printCborStream(buffer.data, textbuffer);
2462 }
2463 
2464 //------------------------------------------------------------------------------
2465 //	EEEEEEE XX    XX  CCCCC  EEEEEEE PPPPPP  TTTTTTT IIIII  OOOOO  NN   NN
2466 //	EE       XX  XX  CC    C EE      PP   PP   TTT    III  OO   OO NNN  NN
2467 //	EEEEE     XXXX   CC      EEEEE   PPPPPP    TTT    III  OO   OO NN N NN
2468 //	EE       XX  XX  CC    C EE      PP        TTT    III  OO   OO NN  NNN
2469 //	EEEEEEE XX    XX  CCCCC  EEEEEEE PP        TTT   IIIII  OOOO0  NN   NN
2470 //------------------------------------------------------------------------------
2471 
2472 class CborException : Exception
2473 {
2474 	@trusted pure @nogc this(string message, string file = __FILE__, size_t line = __LINE__)
2475 	{
2476 		super(message, file, line);
2477 	}
2478 }
2479 
2480 private:
2481 
2482 auto customEmplace(T, A...)(void[] buffer, A args) @nogc
2483 {
2484 	// .init is deprecated since 2.072. Replaced with initializer.
2485 	static if (__VERSION__ < 2072L)
2486 		buffer[] = typeid(T).init;
2487 	else
2488 		buffer[] = typeid(T).initializer;
2489 	return (cast(T)buffer.ptr).__ctor(args);
2490 }
2491 
2492 CborException getException(A...)(string file, size_t line, string fmt, A args) @nogc
2493 {
2494 	static ubyte[__traits(classInstanceSize, CborException)] exceptionBuffer;
2495 	static char[512] charBuffer;
2496 	import core.stdc.stdio : snprintf;
2497 	int written = snprintf(charBuffer.ptr, charBuffer.length, fmt.ptr, args);
2498 	return customEmplace!CborException(exceptionBuffer, cast(string)charBuffer[0..written], file, line);
2499 }
2500 
2501 void onCastErrorToFrom(To)(CborTokenType from, string file = __FILE__, size_t line = __LINE__) @nogc
2502 {
2503 	throw getException(file, line, "Attempt to cast %s to %s", from, typeid(To));
2504 }
2505 
2506 void onInsufficientInput(string file = __FILE__, size_t line = __LINE__) @nogc
2507 {
2508 	throw getException(file, line, "Input range is too short");
2509 }
2510 
2511 void onUnsupportedTag(ubyte tag, string file = __FILE__, size_t line = __LINE__) @nogc
2512 {
2513 	throw getException(file, line, "Unsupported tag found: %02x", tag);
2514 }