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