1 /***************************************************************************************************
2  * 
3  * A fast JSON parser implementing RFC 7159.
4  * 
5  * The most prominent change compared to the initial revision is the allowance of all data types as
6  * root values, not just objects and arrays.
7  * 
8  * Usage_Hints:
9  *   $(UL
10  *     $(LI This parser only supports UTF-8 without BOM.)
11  *     $(LI When a JSON object has duplicate keys, the last one in the set will determine the value
12  *          of associative-array entries or struct fields.)
13  *     $(LI `BigInt` and large number parsing are not implemented currently, but all integral types
14  *          as well as minimal exact representations of many `double` values are supported.)
15  *   )
16  * 
17  * Authors:
18  *   $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise)
19  * 
20  * Copyright:
21  *   © 2017 $(LINK2 mailto:Marco.Leise@gmx.de, Marco Leise)
22  * 
23  * License:
24  *   $(LINK2 http://www.gnu.org/licenses/gpl-3.0, GNU General Public License 3.0)
25  * 
26  **************************************************************************************************/
27 module fast.json;
28 
29 import core.stdc..string;
30 
31 import std.ascii;
32 import std.conv;
33 import std.exception;
34 import std.file;
35 import std.json;
36 import std.range;
37 import std..string : representation, format;
38 import std.traits;
39 import std.uni;
40 
41 import fast.buffer;
42 import fast.cstring;
43 import fast.internal.sysdef;
44 import fast.parsing;
45 
46 
47 /*******************************************************************************
48  * 
49  * Loads a file as JSON text and validates the used parts. This includes a UTF-8
50  * validation on strings.
51  *
52  * Params:
53  *   fname = The file name to load.
54  *
55  * Returns:
56  *   A JSON file object exposing the `Json` API.
57  *
58  **************************************/
59 auto parseJSONFile(uint vl = validateUsed)(in char[] fname)
60 { return Json!vl.File(fname); }
61 
62 /// ditto
63 auto parseJSONFile(uint vl = validateUsed)(in Filename fname)
64 { return Json!vl.File(fname); }
65 
66 
67 /*******************************************************************************
68  * 
69  * Loads a JSON string and validates the used parts. This includes a UTF-8
70  * validation on strings.
71  *
72  * Params:
73  *   text = The string to load.
74  *
75  * Returns:
76  *   A `Json` struct.
77  *
78  **************************************/
79 auto parseJSON(uint vl = validateUsed, T : const(char)[])(T text) nothrow
80 { return Json!(vl, false)(text); }
81 
82 
83 /*******************************************************************************
84  * 
85  * Load a file as JSON text that is considered 100% correct. No checks will be
86  * performed, not even if you try to read a number as a string.
87  *
88  * Params:
89  *   fname = The file name to load.
90  *
91  * Returns:
92  *   A JSON file object exposing the `Json` API.
93  *
94  **************************************/
95 Json!trustedSource.File parseTrustedJSONFile(in char[] fname)
96 { return Json!trustedSource.File(fname); }
97 
98 /// ditto
99 Json!trustedSource.File parseTrustedJSONFile(in Filename fname)
100 { return Json!trustedSource.File(fname); }
101 
102 
103 /*******************************************************************************
104  * 
105  * Load a JSON string that is considered 100% correct. No checks will be
106  * performed, not even if you try to read a number as a string.
107  *
108  * Params:
109  *   text = The string to load.
110  *
111  * Returns:
112  *   A `Json` struct.
113  *
114  **************************************/
115 auto parseTrustedJSON(T : const(char)[])(T text) nothrow
116 { return Json!(trustedSource, false)(text); }
117 
118 
119 /*******************************************************************************
120  *
121  * Validates a JSON text file.
122  *
123  * Params:
124  *   fname = The file name to load.
125  *
126  * Throws:
127  *   JSONException on validation errors.
128  *
129  **************************************/
130 void validateJSONFile(in char[] fname)
131 { Json!(validateAll, true).File(fname).skipValue(); }
132 
133 /// ditto
134 void validateJSONFile(in Filename fname)
135 { Json!(validateAll, true).File(fname).skipValue(); }
136 
137 
138 /*******************************************************************************
139  *
140  * Validates a JSON string.
141  *
142  * Params:
143  *   text = The string to load.
144  *
145  * Throws:
146  *   JSONException on validation errors.
147  *
148  **************************************/
149 void validateJSON(T : const(char)[])(T text)
150 { Json!(validateAll, true)(text).skipValue(); }
151 
152 
153 /// JSON data types returned by `peek`.
154 enum DataType : ubyte
155 {
156 	string, number, object, array, boolean, null_
157 }
158 
159 
160 /// Validation strength of JSON parser
161 enum
162 {
163 	trustedSource,  /// Assume 100% correct JSON and speed up parsing.
164 	validateUsed,   /// Ignore errors in skipped portions.
165 	validateAll,    /// Do a complete validation of the JSON data.
166 }
167 
168 
169 /// A UDA used to remap enum members or struct field names to JSON strings.
170 struct JsonMapping { string[string] map; }
171 
172 
173 /// JSON parser state returned by the `state` property.
174 struct JsonParserState {
175 	const(char)*    text;
176 	size_t          nesting;
177 }
178 
179 
180 /*******************************************************************************
181  * 
182  * This is a forward JSON parser for picking off items of interest on the go.
183  * It neither produces a node structure, nor does it produce events. Instead you
184  * can peek at the value type that lies ahead and/or directly consume a JSON
185  * value from the parser. Objects and arrays can be iterated over via `foreach`,
186  * while you can also directly ask for one or multiple keys of an object.
187  * 
188  * Prams:
189  *   vl = Validation level. Any of `trustedSource`, `validateUsed` or
190  *        `validateAll`.
191  *   validateUtf8 = If validation is enabled, this also checks UTF-8 encoding
192  *                  of JSON strings.
193  * 
194  **************************************/
195 struct Json(uint vl = validateUsed, bool validateUtf8 = vl > trustedSource)
196 	if (vl > trustedSource || !validateUtf8)
197 {
198 private:
199 
200 	enum isTrusted     = vl == trustedSource;
201 	enum skipAllInter  = vl == trustedSource;
202 	enum isValidating  = vl >= validateUsed;
203 	enum isValidateAll = vl == validateAll;
204 
205 	const(char*)    m_start     = void;
206 	const(char)*    m_text      = void;
207 	size_t          m_nesting   = 0;
208 	RaiiArray!char  m_mem;
209 	bool            m_isString  = false;
210 
211 
212 public:
213 
214 	@disable this();
215 	@disable this(this);
216 
217 
218 	/*******************************************************************************
219 	 * 
220 	 * Constructor taking a `string` for fast slicing.
221 	 * 
222 	 * JSON strings without escape sequences can be returned as slices.
223 	 *
224 	 * Params:
225 	 *   text = The JSON text to parse.
226 	 *   simdPrep = Set this to `No.simdPrep` to indicate that `text` is already
227 	 *     suffixed by 16 zero bytes as required for SIMD processing.
228 	 *
229 	 **************************************/
230 	nothrow
231 	this(string text, Flag!"simdPrep" simdPrep = Yes.simdPrep)
232 	{
233 		import core.memory;
234 		m_isString = GC.query(text.ptr) !is ReturnType!(GC.query).init;
235 		this(cast(const(char)[]) text, simdPrep);
236 	}
237 
238 
239 	/*******************************************************************************
240 	 * 
241 	 * Constructor taking a `const char[]`.
242 	 * 
243 	 * JSON strings allocate on the GC heap when returned.
244 	 *
245 	 * Params:
246 	 *   text = The JSON text to parse.
247 	 *   simdPrep = Set this to `No.simdPrep` to indicate that `text` is already
248 	 *     suffixed by 16 zero bytes as required for SIMD processing.
249 	 *
250 	 **************************************/
251 	pure nothrow
252 	this(const(char)[] text, Flag!"simdPrep" simdPrep = Yes.simdPrep)
253 	{
254 		if (simdPrep)
255 		{
256 			// We need to append 16 zero bytes for SSE to work, and if that reallocates the char[]
257 			// we can declare it unique/immutable and don't need to allocate when returning JSON strings.
258 			auto oldPtr = text.ptr;
259 			text ~= "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
260 			m_isString |= oldPtr !is text.ptr;
261 		}
262 		m_start = m_text = text.ptr;
263 		skipWhitespace!false();
264 	}
265 
266 
267 	/+
268 	 ╔══════════════════════════════════════════════════════════════════════════════
269 	 ║ ⚑ String
270 	 ╚══════════════════════════════════════════════════════════════════════════════
271 	 +/
272 
273 	/*******************************************************************************
274 	 * 
275 	 * Reads a string off the JSON text.
276 	 *
277 	 * Params:
278 	 *   allowNull = Allow `null` as a valid option for the string.
279 	 *
280 	 * Returns:
281 	 *   A GC managed string.
282 	 *
283 	 **************************************/
284 	string read(T)(bool allowNull = true) if (is(T == string))
285 	{
286 		if (!allowNull || peek == DataType..string)
287 		{
288 			auto borrowed = borrowString();
289 			return m_isString ? borrowed.assumeUnique() : borrowed.idup;
290 		}
291 		return readNull();
292 	}
293 
294 
295 	/*******************************************************************************
296 	 * 
297 	 * Reads an enumeration off the JSON text.
298 	 *
299 	 **************************************/
300 	T read(T)() if (is(T == enum))
301 	{
302 		enum mapping = buildRemapTable!T;
303 		auto oldPos = m_text;
304 		auto text = borrowString();
305 		foreach (m; mapping)
306 			if (text.length == m.json.length && memcmp(text.ptr, m.json.ptr, m.json.length) == 0)
307 				return m.d;
308 		m_text = oldPos;
309 		static if (isValidating)
310 			handleError(format("Could not find enum member `%s` in `%s`", text, T.stringof));
311 		assert(0);
312 	}
313 
314 
315 	/*******************************************************************************
316 	 * 
317 	 * Reads a string off the JSON text with limited lifetime.
318 	 * 
319 	 * The reference to this slice is not guaranteed to be valid after the JSON
320 	 * parser has been destroyed or another object key or string value has been
321 	 * parsed. So make a copy before you continue parsing.
322 	 *
323 	 * Returns:
324 	 *   If the string had no escape sequences in it, the returned array is a
325 	 *   slice of the JSON text buffer, otherwise temporary copy.
326 	 *
327 	 **************************************/
328 	const(char)[] borrowString()
329 	{
330 		expect('"', "at start of string");
331 		auto escFreeStart = m_text;
332 
333 		if (scanString!validateUtf8())
334 		{
335 			// Fast path here is to return a slice of the JSON if it doesn't contain escapes.
336 			size_t length = m_text - escFreeStart;
337 			skipOnePlusWhitespace!skipAllInter();
338 			return escFreeStart[0 .. length];
339 		}
340 		else
341 		{
342 			// Otherwise we copy to a separate memory area managed by this parser instance.
343 			size_t length = 0;
344 			bool eos = false;
345 			goto CopyToBuffer;
346 			do
347 			{
348 				do
349 				{
350 					m_mem.capacityNeeded( length + 4 );
351 					uint decoded = decodeEscape( &m_mem[length] );
352 					length += decoded;
353 				}
354 				while (*m_text == '\\');
355 
356 				escFreeStart = m_text;
357 				eos = scanString!validateUtf8();
358 			CopyToBuffer:
359 				size_t escFreeLength = m_text - escFreeStart;
360 				m_mem.capacityNeeded( length + escFreeLength );
361 				memcpy( m_mem.ptr + length, escFreeStart, escFreeLength );
362 				length += escFreeLength;
363 			}
364 			while (!eos);
365 			skipOnePlusWhitespace!skipAllInter();
366 			return m_mem[0 .. length];
367 		}
368 	}
369 
370 
371 	private bool scanString(bool validate)()
372 	{
373 		static if (validate)
374 		{
375 			import core.bitop;
376 
377 			while (true)
378 			{
379 				// Stop for control-characters, \, " and anything non-ASCII.
380 				m_text.seekToRanges!"\0\x1F\"\"\\\\\x7F\xFF";
381 				
382 				// Handle printable ASCII range
383 				if (*m_text == '"')
384 					return true;
385 				if (*m_text == '\\')
386 					return false;
387 				
388 				// Anything else better be UTF-8
389 				uint u = *cast(uint*) m_text;
390 				version (LittleEndian) u = bswap(u);
391 				
392 				// Filter overlong ASCII and missing follow byte.
393 				if (
394 					(u & 0b111_00000_11_000000_00000000_00000000) == 0b110_00000_10_000000_00000000_00000000 &&
395 					(u > 0b110_00001_10_111111_11111111_11111111))
396 					m_text += 2;
397 				// Handle overlong representation, UTF-16 surrogate pairs and missing follow bytes.
398 				else if (
399 					(u & 0b1111_0000_11_000000_11_000000_00000000) == 0b1110_0000_10_000000_10_000000_00000000 &&
400 					(u & 0b0000_1111_00_100000_00_000000_00000000) != 0b0000_1101_00_100000_00_000000_00000000 &&
401 					(u > 0b1110_0000_10_011111_10_111111_11111111))
402 					m_text += 3;
403 				// Handle missing follow bytes, Handle overlong representation and out of valid range (max. 0x10FFFF)
404 				else if (
405 					(u & 0b11111_000_11_000000_11_000000_11_000000) == 0b11110_000_10_000000_10_000000_10_000000 &&
406 					(u > 0b11110_000_10_001111_10_111111_10_111111) && (u < 0b11110_100_10_010000_10_000000_10_000000))
407 					m_text += 4;
408 				// Handle invalid code units.
409 				else if (*m_text < ' ' || *m_text == 0x7F)
410 					expectNot("is a disallowed control character in strings");
411 				else if (*m_text >= 0x80 && *m_text <= 0xBF)
412 					expectNot("is a UTF-8 follow byte and cannot start a sequence");
413 				else
414 					expectNot("is not a valid UTF-8 sequence start");
415 			}
416 		}
417 		else
418 		{
419 			m_text.seekToAnyOf!("\\\"\0");
420 			return *m_text == '"';
421 		}
422 	}
423 
424 
425 	private int matchString(string key)()
426 	{
427 		return m_text.fixedTermStrCmp!(char, key, "\"\0", "\\")(&stringCompareCallback);
428 	}
429 
430 
431 	private bool stringCompareCallback(ref immutable(char)* key, ref const(char)* str)
432 	{
433 		do
434 		{
435 			auto key4 = cast(char[4]*) key;
436 			char[4] buf = *key4;
437 			uint bytes = decodeEscape(buf.ptr);
438 			if (buf != *key4)
439 				return false;
440 			key += bytes;
441 		}
442 		while (str[0] == '\\');
443 		return true;
444 	}
445 
446 
447 	private static immutable escapes = {
448 		char[256] result = '\0';
449 		result['"'] = '"';
450 		result['\\'] = '\\';
451 		result['/'] = '/';
452 		result['b'] = '\b';
453 		result['f'] = '\f';
454 		result['n'] = '\n';
455 		result['r'] = '\r';
456 		result['t'] = '\t';
457 		return result;
458 	}();
459 
460 
461 	private void skipEscape()
462 	{
463 		static if (isValidateAll)
464 		{
465 			if (m_text[1] != 'u')
466 			{
467 				// Normal escape sequence. 2 bytes removed.
468 				if (!escapes[*++m_text])
469 					expectNot("in escape sequence");
470 				m_text++;
471 			}
472 			else
473 			{
474 				// UTF-16
475 				m_text += 2;
476 				decodeUtf16HexToCodepoint();
477 			}
478 		}
479 		else m_text += 2;
480 	}
481 
482 
483 	private uint decodeEscape(scope char* dst)
484 	{
485 		if (m_text[1] != 'u')
486 		{
487 			// Normal escape sequence. 2 bytes removed.
488 			dst[0] = escapes[m_text[1]];
489 			static if (isValidating)
490 				if (!dst[0])
491 					handleError("Invalid escape sequence");
492 			m_text += 2;
493 			return 1;
494 		}
495 		else
496 		{
497 			// UTF-16
498 			m_text += 2;
499 			uint cp = decodeUtf16HexToCodepoint();
500 
501 			if (cp >= 0xD800 && cp <= 0xDBFF)
502 			{
503 				dst[0] = cast(char)(0b11110_000 | cp >> 18);
504 				dst[1] = cast(char)(0b10_000000 | cp >> 12 & 0b00_111111);
505 				dst[2] = cast(char)(0b10_000000 | cp >> 6  & 0b00_111111);
506 				dst[3] = cast(char)(0b10_000000 | cp       & 0b00_111111);
507 				return 4;
508 			}
509 			else if (cp >= 0x800)
510 			{
511 				dst[0] = cast(char)(0b1110_0000 | cp >> 12);
512 				dst[1] = cast(char)(0b10_000000 | cp >> 6  & 0b00_111111);
513 				dst[2] = cast(char)(0b10_000000 | cp       & 0b00_111111);
514 				return 3;
515 			}
516 			else if (cp >= 0x80)
517 			{
518 				dst[0] = cast(char)(0b110_00000 | cp >> 6);
519 				dst[1] = cast(char)(0b10_000000 | cp       & 0b00_111111);
520 				return 2;
521 			}
522 			else
523 			{
524 				dst[0] = cast(char)(cp);
525 				return 1;
526 			}
527 		}
528 	}
529 
530 
531 	private dchar decodeUtf16HexToCodepoint()
532 	{
533 		import fast.internal.helpers;
534 
535 		uint cp, hi;
536 		foreach (i; staticIota!(0, 2))
537 		{
538 			static if (isValidating)
539 			{
540 				if (auto badByte = hexDecode4(m_text, cp))
541 				{
542 					m_text = badByte;
543 					expectNot("is not a hex digit");
544 				}
545 			}
546 			else
547 			{
548 				cp = hexDecode4(m_text);
549 			}
550 			
551 			static if (i == 0)
552 			{
553 				// Is this a high surrogate (followed by a low surrogate) or not ?
554 				if (cp < 0xD800 || cp > 0xDBFF)
555 					break;
556 				hi = cp - 0xD800 + 0x40 << 10;
557 			}
558 			else static if (i == 1)
559 			{
560 				static if (isValidating)
561 				{
562 					if (cp < 0xDC00 || cp > 0xDFFF)
563 						handleError("The UTF-16 escape produced an invalid code point.");
564 					cp -= 0xDC00;
565 				}
566 				cp |= hi;
567 			}
568 		}
569 
570 		static if (isValidating)
571 			if (cp > 0x10FFFF || cp >= 0xD800 && cp <= 0xDFFF)
572 				handleError("The UTF-16 escape produced an invalid code point.");
573 
574 		return cp;
575 	}
576 
577 
578 	private void skipString(bool skipInter)()
579 	{
580 		m_text++;
581 		skipRestOfString!skipInter();
582 	}
583 
584 
585 	private void skipRestOfString(bool skipInter)()
586 	{
587 		while (!scanString!isValidateAll())
588 			skipEscape();
589 		skipOnePlusWhitespace!skipInter();
590 	}
591 
592 
593 	/+
594 	 ╔══════════════════════════════════════════════════════════════════════════════
595 	 ║ ⚑ Number
596 	 ╚══════════════════════════════════════════════════════════════════════════════
597 	 +/
598 
599 	/*******************************************************************************
600 	 * 
601 	 * Reads a number off the JSON text.
602 	 * 
603 	 * If you ask for an unsigned value, no minus sign will be accepted in the JSON,
604 	 * otherwise all features of JSON numbers will be available. In particular large
605 	 * integers can be given in scientific notation.
606 	 *
607 	 * Params:
608 	 *   N = Built-in numerical type that should be returned.
609 	 *
610 	 * Returns:
611 	 *   The parsed number.
612 	 *
613 	 * Throws:
614 	 *   JSONException, on invalid JSON or integer overflow.
615 	 *
616 	 **************************************/
617 	N read(N)() if (isNumeric!N && !is(N == enum))
618 	{
619 		N n = void;
620 		static if (isUnsigned!N)
621 			enum NumberOptions opt = {};
622 		else
623 			enum NumberOptions opt = { minus:true };
624 		if (parseNumber!opt(m_text, n))
625 			skipWhitespace!skipAllInter();
626 		else static if (isValidating)
627 			handleError(format("Could not convert JSON number to `%s`", N.stringof));
628 		return n;
629 	}
630 
631 
632 	private void skipNumber(bool skipInter)()
633 	{
634 		static if (isValidateAll)
635 		{
636 			if (*m_text == '-')
637 				m_text++;
638 			if (*m_text == '0')
639 				m_text++;
640 			else
641 				trySkipDigits();
642 			if (*m_text == '.')
643 			{
644 				m_text++;
645 				trySkipDigits();
646 			}
647 			if ((*m_text | 0x20) == 'e')
648 			{
649 				m_text++;
650 				if (*m_text == '+' || *m_text == '-')
651 					m_text++;
652 				trySkipDigits();
653 			}
654 			skipWhitespace!false();
655 		}
656 		else
657 		{
658 			m_text.skipCharRanges!"\t\n\r\r  ++-.09EEee";
659 			static if (skipInter)
660 				m_text.skipAllOf!"\t\n\r ,";
661 		}
662 	}
663 
664 
665 	static if (isValidateAll)
666 	{
667 		private void trySkipDigits()
668 		{
669 			if (*m_text - '0' > 9)
670 				expectNot("in number literal");
671 			m_text.skipAllOf!"0123456789";
672 		}
673 	}
674 
675 
676 	/+
677 	 ╔══════════════════════════════════════════════════════════════════════════════
678 	 ║ ⚑ Object
679 	 ╚══════════════════════════════════════════════════════════════════════════════
680 	 +/
681 
682 	/*******************************************************************************
683 	 * 
684 	 * Reads a plain old data struct off the JSON text.
685 	 *
686 	 * Params:
687 	 *   T = Type of struct that should be returned.
688 	 *
689 	 * Returns:
690 	 *   A struct of type `T`.
691 	 *
692 	 **************************************/
693 	T read(T)() if (is(T == struct) && __traits(isPOD, T))
694 	{
695 		nest('{', "on start of object");
696 
697 		T t;
698 		if (*m_text != '}') while (true)
699 		{
700 			auto key = borrowString();
701 			static if (!skipAllInter)
702 			{
703 				expect(':', "between key and value");
704 				skipWhitespace!false();
705 			}
706 
707 			enum mapping = buildRemapTable!T;
708 			foreach (m; mapping)
709 			{
710 				if (key.length == m.json.length && memcmp(key.ptr, m.json.ptr, m.json.length) == 0)
711 				{
712 					mixin("alias keyT = typeof(T." ~ m.d ~ ");");
713 					mixin("t." ~ m.d ~ " = read!keyT;");
714 					goto Success;
715 				}
716 			}
717 			skipValue();
718 
719 		Success:
720 			if (*m_text == '}')
721 				break;
722 
723 			static if (!skipAllInter)
724 			{
725 				expect(',', "between key-value pairs");
726 				skipWhitespace!false();
727 			}
728 		}
729 		
730 		unnest();
731 		return t;
732 	}
733 
734 
735 	/*******************************************************************************
736 	 * 
737 	 * Reads a plain old data struct or `null` off the JSON text.
738 	 * 
739 	 * Params:
740 	 *   T = Type of struct pointer that should be returned.
741 	 *
742 	 * Returns:
743 	 *   A pointer to a newly filled struct of type `T` on the GC heap.
744 	 *
745 	 **************************************/
746 	T read(T)() if (is(PointerTarget!T == struct) && __traits(isPOD, PointerTarget!T))
747 	{
748 		if (peek == DataType.null_)
749 			return readNull();
750 		T tp = new PointerTarget!T;
751 		*tp = read!(PointerTarget!T)();
752 		return tp;
753 	}
754 
755 
756 	/*******************************************************************************
757 	 * 
758 	 * Reads an associative-array off a JSON text.
759 	 * 
760 	 * The key type must be `string`, the value type can be any type otherwise
761 	 * supported by the parser.
762 	 *
763 	 * Params:
764 	 *   T = The type of AA to return.
765 	 *
766 	 * Returns:
767 	 *   A newly filled associative array.
768 	 *
769 	 **************************************/
770 	T read(T)() if (is(KeyType!T == string))
771 	{
772 		T aa;
773 		foreach (key; byKey)
774 			aa[m_isString ? cast(immutable)key : key.idup] = read!(ValueType!T)();
775 		return aa;
776 	}
777 
778 
779 	/*******************************************************************************
780 	 * 
781 	 * An alias to the `singleKey` method. Instead of `json.singleKey!"something"`
782 	 * you can write `json.something`. Read the notes on `singleKey`.
783 	 *
784 	 **************************************/
785 	alias opDispatch = singleKey;
786 
787 
788 	/*******************************************************************************
789 	 * 
790 	 * Skips all keys of an object except the first occurence with the given key
791 	 * name.
792 	 *
793 	 * Params:
794 	 *   name = the key name of interest
795 	 *
796 	 * Returns:
797 	 *   A temporary struct, a proxy to the parser, that will automatically seek to
798 	 *   the end of the current JSON object on destruction.
799 	 *
800 	 * Throws:
801 	 *   JSONException when the key is not found in the object or parsing errors
802 	 *   occur.
803 	 * 
804 	 * Note:
805 	 *   Since this is an on the fly parser, you can only get one key from an
806 	 *   object with this method. Use `keySwitch` or `foreach(key; json)` to get
807 	 *   values from multiple keys.
808 	 * 
809 	 * See_Also:
810 	 *   keySwitch
811 	 *
812 	 **************************************/
813 	@property SingleKey singleKey(string name)()
814 	{
815 		nest('{', "on start of object");
816 		
817 		if (*m_text != '}') while (true)
818 		{
819 			auto key = borrowString();
820 			static if (!skipAllInter)
821 			{
822 				expect(':', "between key and value");
823 				skipWhitespace!false();
824 			}
825 			
826 			if (key.length == name.length && memcmp(key.ptr, name.ptr, name.length) == 0)
827 				return SingleKey(this);
828 
829 			skipValueImpl!skipAllInter();
830 			
831 			if (*m_text == '}')
832 				break;
833 			
834 			static if (!skipAllInter)
835 			{
836 				expect(',', "between key-value pairs");
837 				skipWhitespace!false();
838 			}
839 		}
840 		
841 		unnest();
842 		static if (isValidating)
843 			handleError("Key not found.");
844 		assert(0);
845 	}
846 
847 
848 	/*******************************************************************************
849 	 * 
850 	 * Selects from a set of given keys in an object and calls the corresponding
851 	 * delegate. The difference to `singleKey` when invoked with a single key is
852 	 * that `keySwitch` will not error out if the key is non-existent and may
853 	 * trigger the delegate multiple times, if the JSON object has duplicate keys.
854 	 *
855 	 * Params:
856 	 *   Args = the names of the keys
857 	 *   dlg = the delegates corresponding to the keys
858 	 *
859 	 * Throws:
860 	 *   JSONException when the key is not found in the object or parsing errors
861 	 *   occur.
862 	 * 
863 	 **************************************/
864 	void keySwitch(Args...)(scope void delegate()[Args.length] dlg...)
865 	{
866 		nest('{', "on start of object");
867 		
868 		if (*m_text != '}') while (true)
869 		{
870 			auto key = borrowString();
871 			static if (!skipAllInter)
872 			{
873 				expect(':', "between key and value");
874 				skipWhitespace!false();
875 			}
876 			
877 			auto oldPos = m_text;
878 			foreach (i, arg; Args)
879 			{
880 				if (key.length == arg.length && memcmp(key.ptr, arg.ptr, arg.length) == 0)
881 				{
882 					dlg[i]();
883 					goto Next;
884 				}
885 			}
886 			skipValue();
887 			
888 		Next:
889 			if (*m_text == '}')
890 				break;
891 			
892 			static if (!skipAllInter) if (oldPos !is m_text)
893 			{
894 				expect(',', "after key-value pair");
895 				skipWhitespace!false();
896 			}
897 		}
898 		
899 		unnest();
900 	}
901 	
902 	
903 	private int byKeyImpl(scope int delegate(ref const char[]) foreachBody)
904 	{
905 		nest('{', "at start of foreach over object");
906 
907 		int result = 0;
908 		if (*m_text != '}') while (true)
909 		{
910 			auto key = borrowString();
911 			static if (!skipAllInter)
912 			{
913 				expect(':', "between key and value");
914 				skipWhitespace!false;
915 			}
916 
917 			if (iterationGuts!"{}"(result, key, foreachBody, "after key-value pair"))
918 				break;
919 		}
920 
921 		unnest();
922 		return result;
923 	}
924 
925 
926 	/*******************************************************************************
927 	 * 
928 	 * Iterate the keys of a JSON object with `foreach`.
929 	 * 
930 	 * Notes:
931 	 *   $(UL
932 	 *     $(LI If you want to store the key, you need to duplicate it.)
933 	 *   )
934 	 * 
935 	 * Example:
936 	 * ---
937 	 * uint id;
938 	 * foreach (key; json.byKey)
939 	 *     if (key == "id")
940 	 *         id = json.read!uint;
941 	 * ---
942 	 **************************************/
943 	@safe @nogc pure nothrow
944 	@property int delegate(scope int delegate(ref const char[])) byKey()
945 	{
946 		return &byKeyImpl;
947 	}
948 
949 
950 	/+
951 	 ╔══════════════════════════════════════════════════════════════════════════════
952 	 ║ ⚑ Array handling
953 	 ╚══════════════════════════════════════════════════════════════════════════════
954 	 +/
955 
956 	/*******************************************************************************
957 	 * 
958 	 * Reads a dynamic array off the JSON text.
959 	 * 
960 	 **************************************/
961 	T read(T)() if (isDynamicArray!T && !isSomeString!T)
962 	{
963 		import std.array;
964 		Appender!T app;
965 		foreach (i; this)
966 			app.put(read!(typeof(T.init[0])));
967 		return app.data;
968 	}
969 
970 
971 	/*******************************************************************************
972 	 * 
973 	 * Reads a static array off the JSON text.
974 	 * 
975 	 * When validation is enabled, it is an error if the JSON array has a different
976 	 * length lengths don't match up. Otherwise unset elements receive their initial
977 	 * value.
978 	 *
979 	 **************************************/
980 	T read(T)() if (isStaticArray!T)
981 	{
982 		T sa = void;
983 		size_t cnt;
984 		foreach (i; this)
985 		{
986 			if (i < T.length)
987 				sa[i] = read!(typeof(T.init[0]));
988 			cnt = i + 1;
989 		}
990 		static if (isValidating)
991 		{
992 			if (cnt != T.length)
993 				handleError(format("Static array size mismatch. Expected %s, got %s", T.length, cnt));
994 		}
995 		else
996 		{
997 			foreach (i; cnt .. T.length)
998 				sa[i] = T.init;
999 		}
1000 		return sa;
1001 	}
1002 
1003 
1004 	/*******************************************************************************
1005 	 * 
1006 	 * Iterate over a JSON array via `foreach`.
1007 	 *
1008 	 **************************************/
1009 	int opApply(scope int delegate(const size_t) foreachBody)
1010 	{
1011 		nest('[', "at start of foreach over array");
1012 
1013 		int result = 0;
1014 		if (*m_text != ']') for (size_t idx = 0; true; idx++)
1015 			if (iterationGuts!"[]"(result, idx, foreachBody, "after array element"))
1016 				break;
1017 
1018 		unnest();
1019 		return result;
1020 	}
1021 
1022 
1023 	/+
1024 	 ╔══════════════════════════════════════════════════════════════════════════════
1025 	 ║ ⚑ Boolean
1026 	 ╚══════════════════════════════════════════════════════════════════════════════
1027 	 +/
1028 
1029 	/*******************************************************************************
1030 	 * 
1031 	 * Reads a boolean value off the JSON text.
1032 	 *
1033 	 **************************************/
1034 	bool read(T)() if (is(T == bool))
1035 	{
1036 		return skipBoolean!(skipAllInter, isValidating)();
1037 	}
1038 
1039 
1040 	private bool skipBoolean(bool skipInter, bool validate = isValidateAll)()
1041 	{
1042 		static immutable char[4][2] keywords = [ "true", "alse" ];
1043 		auto isFalse = *m_text == 'f';
1044 		static if (validate)
1045 			if (*cast(char[4]*) &m_text[isFalse] != keywords[isFalse])
1046 				handleError("`true` or `false` expected.");
1047 		m_text += isFalse ? 5 : 4;
1048 		skipWhitespace!skipInter();
1049 		return !isFalse;
1050 	}
1051 
1052 
1053 	/+
1054 	 ╔══════════════════════════════════════════════════════════════════════════════
1055 	 ║ ⚑ Null
1056 	 ╚══════════════════════════════════════════════════════════════════════════════
1057 	 +/
1058 
1059 	/*******************************************************************************
1060 	 * 
1061 	 * Reads `null` off the JSON text.
1062 	 *
1063 	 **************************************/
1064 	typeof(null) readNull()
1065 	{
1066 		skipNull!(skipAllInter, isValidating)();
1067 		return null;
1068 	}
1069 
1070 
1071 	private void skipNull(bool skipInter, bool validate = isValidateAll)()
1072 	{
1073 		static if (validate)
1074 			if (*cast(const uint*) m_text != *cast(const uint*) "null".ptr)
1075 				handleError("`null` expected.");
1076 		m_text += 4;
1077 		skipWhitespace!skipInter();
1078 	}
1079 
1080 
1081 	/+
1082 	 ╔══════════════════════════════════════════════════════════════════════════════
1083 	 ║ ⚑ Helpers and Error Handling
1084 	 ╚══════════════════════════════════════════════════════════════════════════════
1085 	 +/
1086 
1087 	/*******************************************************************************
1088 	 * 
1089 	 * Skips the next JSON value if you are not interested.
1090 	 *
1091 	 **************************************/
1092 	void skipValue()
1093 	{
1094 		skipValueImpl!skipAllInter();
1095 	}
1096 
1097 
1098 	private void skipValueImpl(bool skipInter)()
1099 	{
1100 		with (DataType) final switch (peek)
1101 		{
1102 			case string:
1103 				skipString!skipInter();
1104 				break;
1105 			case number:
1106 				skipNumber!skipInter();
1107 				break;
1108 			case object:
1109 				static if (isValidateAll)
1110 				{
1111 					foreach (_; this.byKey)
1112 						break;
1113 				}
1114 				else
1115 				{
1116 					m_text++;
1117 					seekObjectEnd();
1118 					skipOnePlusWhitespace!skipInter();
1119 				}
1120 				break;
1121 			case array:
1122 				static if (isValidateAll)
1123 				{
1124 					foreach (_; this)
1125 						break;
1126 				}
1127 				else
1128 				{
1129 					m_text++;
1130 					seekArrayEnd();
1131 					skipOnePlusWhitespace!skipInter();
1132 				}
1133 				break;
1134 			case boolean:
1135 				skipBoolean!skipInter();
1136 				break;
1137 			case null_:
1138 				skipNull!skipInter();
1139 				break;
1140 		}
1141 	}
1142 
1143 
1144 	/*******************************************************************************
1145 	 * 
1146 	 * Returns the type of data that is up next in the JSON text.
1147 	 *
1148 	 **************************************/
1149 	@property DataType peek()
1150 	{
1151 		static immutable trans = {
1152 			DataType[256] result = cast(DataType) ubyte.max;
1153 			result['{'] = DataType.object;
1154 			result['['] = DataType.array;
1155 			result['-'] = DataType.number;
1156 			foreach (i; '0' .. '9'+1)
1157 				result[i] = DataType.number;
1158 			result['"'] = DataType..string;
1159 			result['t'] = DataType.boolean;
1160 			result['f'] = DataType.boolean;
1161 			result['n'] = DataType.null_;
1162 			return result;
1163 		}();
1164 		
1165 		DataType vt = trans[*m_text];
1166 		static if (isValidating)
1167 			if (vt == ubyte.max)
1168 				expectNot("while peeking at next value type");
1169 		return vt;
1170 	}
1171 
1172 
1173 	/*******************************************************************************
1174 	 *
1175 	 * Save or restore the parser's internal state.
1176 	 *
1177 	 * If you want to read only a certain object from the JSON, but exactly which
1178 	 * depends on the value of some key, this is where saving and restoring the
1179 	 * parser state helps.
1180 	 *
1181 	 * Before each candidate you save the parser state. Then you perform just the
1182 	 * minimal work to test if the candidate matches some criteria. If it does,
1183 	 * restore the parser state and read the elements in full. Of it doesn't, just
1184 	 * skip to the next.
1185 	 *
1186 	 **************************************/
1187 	@property const(JsonParserState) state() const
1188 	{
1189 		return JsonParserState(m_text, m_nesting);
1190 	}
1191 
1192 	@property void state(const JsonParserState oldState)
1193 	{
1194 		m_text    = oldState.text;
1195 		m_nesting = oldState.nesting;
1196 	}
1197 
1198 
1199 	private void nest(char c, string msg)
1200 	{
1201 		expect(c, msg);
1202 		skipWhitespace!false();
1203 		m_nesting++;
1204 	}
1205 
1206 
1207 	private void unnest()
1208 	in { assert(m_nesting > 0); }
1209 	body
1210 	{
1211 		if (--m_nesting == 0)
1212 		{
1213 			skipOnePlusWhitespace!false();
1214 			static if (isValidating)
1215 				if (*m_text != '\0')
1216 					handleError("Expected end of JSON.");
1217 		}
1218 		else skipOnePlusWhitespace!skipAllInter();
1219 	}
1220 
1221 
1222 	private bool iterationGuts(char[2] braces, T, D)(ref int result, T idx, scope D dlg,
1223 		string missingCommaMsg)
1224 	{
1225 		auto oldPos = m_text;
1226 		static if (isValidateAll)
1227 		{
1228 			if (result)
1229 			{
1230 				skipValueImpl!(!isValidateAll)();
1231 				goto PastValue;
1232 			}
1233 		}
1234 		result = dlg(idx);
1235 		if (oldPos is m_text)
1236 			skipValueImpl!(!isValidateAll)();
1237 		
1238 	PastValue:
1239 		if (*m_text == braces[1])
1240 			return true;
1241 		
1242 		static if (!isValidateAll) if (result)
1243 		{
1244 			seekAggregateEnd!braces();
1245 			return true;
1246 		}
1247 		
1248 		static if (!skipAllInter) if (oldPos !is m_text)
1249 		{
1250 			expect(',', missingCommaMsg);
1251 			skipWhitespace!false();
1252 		}
1253 		return false;
1254 	}
1255 
1256 
1257 	static if (!isValidateAll)
1258 	{
1259 		private void seekObjectEnd()
1260 		{
1261 			seekAggregateEnd!"{}"();
1262 		}
1263 
1264 
1265 		private void seekArrayEnd()
1266 		{
1267 			seekAggregateEnd!"[]"();
1268 		}
1269 
1270 
1271 		private void seekAggregateEnd(immutable char[2] parenthesis)()
1272 		{
1273 			size_t nesting = 1;
1274 			while (true)
1275 			{
1276 				m_text.seekToAnyOf!(parenthesis ~ "\"\0");
1277 				final switch (*m_text)
1278 				{
1279 					case parenthesis[0]:
1280 						m_text++;
1281 						nesting++;
1282 						break;
1283 					case parenthesis[1]:
1284 						if (--nesting == 0)
1285 							return;
1286 						m_text++;
1287 						break;
1288 					case '"':
1289 						// Could skip ':' or ',' here by passing `true`, but we skip it above anyways.
1290 						skipString!false();
1291 				}
1292 			}
1293 		}
1294 	}
1295 
1296 
1297 	/// This also increments the JSON read pointer.
1298 	private void expect(char c, string msg)
1299 	{
1300 		static if (isValidating)
1301 			if (*m_text != c)
1302 				expectImpl(c, msg);
1303 		m_text++;
1304 	}
1305 
1306 
1307 	private void expectNot(char c, string msg)
1308 	{
1309 		static if (isValidating)
1310 			if (*m_text == c)
1311 				expectNot(msg);
1312 	}
1313 
1314 
1315 	static if (isValidating)
1316 	{
1317 		@noinline
1318 		private void expectNot(string msg)
1319 		{
1320 			string tmpl = isPrintable(*m_text)
1321 				? "Character '%s' %s."
1322 				: "Byte 0x%02x %s.";
1323 			handleError(format(tmpl, *m_text, msg));
1324 		}
1325 
1326 
1327 		@noinline
1328 		private void expectImpl(char c, string msg)
1329 		{
1330 			string tmpl = isPrintable(*m_text)
1331 				? "Expected '%s', but found '%s' %s."
1332 				: "Expected '%s', but found byte 0x%02x %s.";
1333 			handleError(format(tmpl, c, *m_text, msg));
1334 		}
1335 
1336 
1337 		@noinline
1338 		private void handleError(string msg)
1339 		{
1340 			import fast.unicode;
1341 
1342 			size_t line;
1343 			const(char)* p    = m_start;
1344 			const(char)* last = m_start;
1345 			while (p < m_text)
1346 			{
1347 				last = p;
1348 				p.skipToNextLine();
1349 				line++;
1350 			}
1351 			line += p is m_text;
1352 			size_t column = last[0 .. m_text - last].countGraphemes() + 1;
1353 			
1354 			throw new JSONException(msg, line.to!int, column.to!int);
1355 		}
1356 	}
1357 
1358 
1359 	@forceinline @nogc pure nothrow
1360 	private void skipOnePlusWhitespace(bool skipInter)()
1361 	{
1362 		m_text++;
1363 		skipWhitespace!skipInter();
1364 	}
1365 
1366 
1367 	@forceinline @nogc pure nothrow
1368 	private void skipWhitespace(bool skipInter)()
1369 	{
1370 		static if (skipInter)
1371 			m_text.skipAllOf!"\t\n\r ,:";
1372 		else
1373 			m_text.skipAsciiWhitespace();
1374 	}
1375 
1376 
1377 	private static struct SingleKey
1378 	{
1379 		alias json this;
1380 
1381 		private Json* m_pjson;
1382 		private const(char*) m_oldPos;
1383 
1384 		@safe @nogc pure nothrow
1385 		@property ref Json json()
1386 		{
1387 			return *m_pjson;
1388 		}
1389 
1390 		this(ref Json json)
1391 		{
1392 			m_pjson = &json;
1393 			m_oldPos = json.m_text;
1394 		}
1395 
1396 		~this()
1397 		{
1398 			static if (isValidateAll)
1399 			{
1400 				if (*json.m_text != '}')
1401 				{
1402 					if (m_oldPos !is json.m_text)
1403 					{
1404 						json.expect(',', "after key-value pair");
1405 						json.skipWhitespace!false();
1406 					}
1407 					while (true)
1408 					{
1409 						json.skipString!false();
1410 						json.expect(':', "between key and value");
1411 						json.skipWhitespace!false();
1412 						json.skipValueImpl!false();
1413 
1414 						if (*json.m_text == '}')
1415 							break;
1416 
1417 						json.expect(',', "after key-value pair");
1418 						json.skipWhitespace!false();
1419 					}
1420 				}
1421 			}
1422 			else
1423 			{
1424 				json.seekObjectEnd();
1425 			}
1426 			json.unnest();
1427 		}
1428 	}
1429 
1430 
1431 	private static struct File
1432 	{
1433 		alias m_json this;
1434 		
1435 		Json m_json;
1436 		private size_t m_len;
1437 		private bool m_isMapping;
1438 		
1439 		@disable this();
1440 		@disable this(this);
1441 		
1442 		this(const Filename fname)
1443 		{
1444 			version (Posix)
1445 			{
1446 				import core.sys.posix.fcntl;
1447 				import core.sys.posix.sys.mman;
1448 				import core.sys.posix.unistd;
1449 
1450 				version (CRuntime_Glibc)
1451 					enum O_CLOEXEC = octal!2000000;
1452 				else version (OSX)  // Requires at least OS X 10.7 Lion
1453 					enum O_CLOEXEC = 0x1000000;
1454 				else static assert(0, "Not implemented");
1455 				
1456 				int fd = { return open(charPtr!fname, O_RDONLY | O_NOCTTY | O_CLOEXEC); }();
1457 				assert(fcntl(fd, F_GETFD) & FD_CLOEXEC, "Could not set O_CLOEXEC.");
1458 				
1459 				if (fd == -1)
1460 					throw new ErrnoException("Could not open JSON file for reading.");
1461 				scope(exit) close(fd);
1462 				
1463 				// Get the file size
1464 				stat_t info;
1465 				if (fstat(fd, &info) == -1)
1466 					throw new ErrnoException("Could not get JSON file size.");
1467 
1468 				// Ensure we have 16 extra bytes
1469 				size_t pagesize = sysconf(_SC_PAGESIZE);
1470 				ulong fsize = ulong(info.st_size + pagesize - 1) / pagesize * pagesize;
1471 				bool zeroPage = fsize < info.st_size + 16;
1472 				if (zeroPage)
1473 					fsize += pagesize;
1474 				if (fsize > size_t.max)
1475 					throw new Exception("JSON file too large to be mapped in RAM.");
1476 				m_len = cast(size_t) fsize;
1477 				
1478 				// Map the file
1479 				void* mapping = mmap(null, m_len, PROT_READ, MAP_PRIVATE, fd, 0);
1480 				if (mapping == MAP_FAILED)
1481 					throw new ErrnoException("Could not map JSON file.");
1482 				scope(failure)
1483 					munmap(mapping, m_len);
1484 
1485 				// Get a zero-page up behind the JSON text
1486 				if (zeroPage)
1487 				{
1488 					void* offs = mapping + m_len - pagesize;
1489 					if (mmap(offs, pagesize, PROT_READ, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0) == MAP_FAILED)
1490 						throw new ErrnoException("Could not map zero-page behind JSON text.");
1491 				}
1492 
1493 				// Initialize the parser on the JSON text
1494 				m_json = Json((cast(char*) mapping)[0 .. cast(size_t) info.st_size], No.simdPrep);
1495 			}
1496 			else version (Windows)
1497 			{
1498 				import core.sys.windows.winnt;
1499 				import core.sys.windows.winbase;
1500 
1501 				HANDLE hnd = { return CreateFileW( wcharPtr!fname, GENERIC_READ, FILE_SHARE_READ, null,
1502 						OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, null ); }();
1503 
1504 				if (hnd == INVALID_HANDLE_VALUE)
1505 					throw new FileException("Could not open JSON file for reading.");
1506 				scope(exit)
1507 					CloseHandle( hnd );
1508 
1509 				// Get the file size
1510 				LARGE_INTEGER fileSize = void;
1511 				if (!GetFileSizeEx( hnd, &fileSize ))
1512 					throw new Exception("Could not get JSON file size.");
1513 
1514 				// Map the file
1515 				HANDLE mapping = CreateFileMapping( hnd, null, PAGE_READONLY, fileSize.HighPart, fileSize.LowPart, null );
1516 				if (mapping == INVALID_HANDLE_VALUE)
1517 					throw new Exception("Could not create file mapping for JSON file.");
1518 				scope(exit) CloseHandle( mapping );
1519 
1520 				// View the mapping
1521 				void* view = MapViewOfFile( mapping, FILE_MAP_READ, 0, 0, 0 );
1522 				if (view is null)
1523 					throw new Exception("Could not map view of JSON file.");
1524 				scope(failure)
1525 					UnmapViewOfFile( view );
1526 				
1527 				// Missing 64-bit version in druntime (2.071)
1528 				version (X86_64) struct MEMORY_BASIC_INFORMATION {
1529 					PVOID     BaseAddress;
1530 					PVOID     AllocationBase;
1531 					DWORD     AllocationProtect;
1532 					DWORD     __alignment1;
1533 					ULONGLONG RegionSize;
1534 					DWORD     State;
1535 					DWORD     Protect;
1536 					DWORD     Type;
1537 					DWORD     __alignment2;
1538 				}
1539 				
1540 				// Check if the view is 16 bytes larger than the file
1541 				MEMORY_BASIC_INFORMATION query = void;
1542 				if (!VirtualQuery( view, cast(PMEMORY_BASIC_INFORMATION)&query, query.sizeof ))
1543 					throw new Exception("VirtualQuery failed.");
1544 				
1545 				// Initialize the parser on the JSON text
1546 				char[] slice = (cast(char*) view)[0 .. cast(size_t)fileSize.QuadPart];
1547 				if (query.RegionSize >= fileSize.QuadPart + 16)
1548 				{
1549 					m_json = Json(slice, No.simdPrep);
1550 					m_isMapping = true;
1551 				}
1552 				else
1553 				{
1554 					m_json = Json(slice, Yes.simdPrep);
1555 					UnmapViewOfFile( view );
1556 				}
1557 			}
1558 			else static assert(0, "Not implemented");
1559 		}
1560 
1561 
1562 		this(const(char)[] fname)
1563 		{
1564 			import std..string;
1565 
1566 			version (Posix)
1567 				this( fname.representation );
1568 			else version (Windows)
1569 			{
1570 				import core.stdc.stdlib;
1571 				auto buf = cast(wchar*)alloca(string2wstringSize(fname));
1572 				auto fnameW = string2wstring(fname, buf);
1573 				this( fnameW.representation );
1574 			}
1575 			else static assert(0, "Not implemented");
1576 		}
1577 
1578 
1579 		nothrow
1580 		~this()
1581 		{
1582 			version (Posix)
1583 			{
1584 				import core.sys.posix.sys.mman;
1585 				munmap(cast(void*)m_json.m_start, m_len);
1586 			}
1587 			else version (Windows)
1588 			{
1589 				import core.sys.windows.winnt;
1590 				import core.sys.windows.winbase;
1591 				if (m_isMapping)
1592 					UnmapViewOfFile( cast(LPCVOID)m_json.m_start );
1593 			}
1594 			else static assert(0, "Not implemented");
1595 		}
1596 	}
1597 }
1598 
1599 
1600 private template buildRemapTable(T)
1601 {
1602 	import std.typetuple;
1603 	import fast.internal.helpers;
1604 
1605 	static if (is(T == enum))
1606 	{
1607 		struct Remap { T d; string json; }
1608 		enum members = EnumMembers!T;
1609 	}
1610 	else
1611 	{
1612 		struct Remap { string d; string json; }
1613 		enum members = FieldNameTuple!T;
1614 	}
1615 	enum mapping = getUDA!(T, JsonMapping).map;
1616 
1617 	template Impl(size_t a, size_t b)
1618 	{
1619 		static if (b - a > 1)
1620 		{
1621 			alias Impl = TypeTuple!(Impl!(a, (b + a) / 2), Impl!((b + a) / 2, b));
1622 		}
1623 		else static if (b - a == 1)
1624 		{
1625 			static if (is(T == enum))
1626 				enum key = members[a].to!string;
1627 			else
1628 				alias key = members[a];
1629 			static if ((key in mapping) !is null)
1630 				enum mapped = mapping[key];
1631 			else
1632 				alias mapped = key;
1633 			alias Impl = TypeTuple!(Remap(members[a], mapped));
1634 		}
1635 		else alias Impl = TypeTuple!();
1636 	}
1637 
1638 	alias buildRemapTable = Impl!(0, members.length);
1639 }
1640 
1641 
1642 unittest
1643 {
1644 	struct Counter
1645 	{
1646 		size_t array, object, key, string, number, boolean, null_;
1647 	}
1648 
1649 	void valueHandler(ref Json!validateAll.File json, ref Counter ctr)
1650 	{
1651 		with (DataType) final switch (json.peek)
1652 		{
1653 			case array:
1654 				ctr.array++;
1655 				foreach (_; json)
1656 					valueHandler(json, ctr);
1657 				break;
1658 			case object:
1659 				ctr.object++;
1660 				foreach(key; json.byKey)
1661 				{
1662 					ctr.key++;
1663 					valueHandler(json, ctr);
1664 				}
1665 				break;
1666 			case string:
1667 				ctr..string++;
1668 				json.skipValue();
1669 				break;
1670 			case number:
1671 				ctr.number++;
1672 				json.skipValue();
1673 				break;
1674 			case boolean:
1675 				ctr.boolean++;
1676 				json.skipValue();
1677 				break;
1678 			case null_:
1679 				ctr.null_++;
1680 				json.skipValue();
1681 				break;
1682 		}
1683 	}
1684 
1685 	void passFile(string fname, Counter valid)
1686 	{
1687 		auto json = parseJSONFile!validateAll(fname);
1688 		Counter ctr;
1689 		valueHandler(json, ctr);
1690 		assert(ctr == valid, fname);
1691 	}
1692 
1693 	void failFile(string fname)
1694 	{
1695 		auto json = parseJSONFile!validateAll(fname);
1696 		Counter ctr;
1697 		assertThrown!JSONException(valueHandler(json, ctr), fname);
1698 	}
1699 
1700 	// Tests that need to pass according to RFC 7159
1701 	passFile("test/pass1.json",  Counter( 6,  4, 33, 21, 32,  4,  2));
1702 	passFile("test/pass2.json",  Counter(19,  0,  0,  1,  0,  0,  0));
1703 	passFile("test/pass3.json",  Counter( 0,  2,  3,  2,  0,  0,  0));
1704 	passFile("test/fail1.json",  Counter( 0,  0,  0,  1,  0,  0,  0));
1705 	passFile("test/fail18.json", Counter(20,  0,  0,  1,  0,  0,  0));
1706 
1707 	// Tests that need to fail
1708 	foreach (i; chain(iota(2, 18), iota(19, 34)))
1709 		failFile("test/fail" ~ i.to!string ~ ".json");
1710 
1711 	// Deserialization
1712 	struct Test
1713 	{
1714 		string text1;
1715 		string text2;
1716 		string text3;
1717 		double dbl = 0;
1718 		float flt = 0;
1719 		ulong ul;
1720 		uint ui;
1721 		ushort us;
1722 		ubyte ub;
1723 		long lm, lp;
1724 		int im, ip;
1725 		short sm, sp;
1726 		byte bm, bp;
1727 		bool t, f;
1728 		Test* tp1, tp2;
1729 		int[2] sa;
1730 		int[] da;
1731 		Test[string] aa;
1732 		SearchPolicy e;
1733 	}
1734 
1735 	Test t1 = {
1736 		text1 : "abcde",
1737 		text2 : "",
1738 		text3 : null,
1739 		dbl   : 1.1,
1740 		flt   : -1.1,
1741 		ul    : ulong.max,
1742 		ui    : uint.max,
1743 		us    : ushort.max,
1744 		ub    : ubyte.max,
1745 		lm    : long.min,
1746 		lp    : long.max,
1747 		im    : int.min,
1748 		ip    : int.max,
1749 		sm    : short.min,
1750 		sp    : short.max,
1751 		bm    : byte.min,
1752 		bp    : byte.max,
1753 		t     : true,
1754 		f     : false,
1755 		tp1   : null,
1756 		tp2   : new Test("This is", "a", "test."),
1757 		sa    : [ 33, 44 ],
1758 		da    : [ 5, 6, 7 ],
1759 		aa    : [ "hash" : Test("x", "y", "z") ],
1760 		e     : SearchPolicy.linear
1761 	};
1762 	Test t2 = parseJSON(`{
1763 		"text1" : "abcde",
1764 		"text2" : "",
1765 		"text3" : null,
1766 		"dbl"   : 1.1,
1767 		"flt"   : -1.1,
1768 		"ul"    : ` ~ ulong.max.to!string ~ `,
1769 		"ui"    : ` ~ uint.max.to!string ~ `,
1770 		"us"    : ` ~ ushort.max.to!string ~ `,
1771 		"ub"    : ` ~ ubyte.max.to!string ~ `,
1772 		"lm"    : ` ~ long.min.to!string ~ `,
1773 		"lp"    : ` ~ long.max.to!string ~ `,
1774 		"im"    : ` ~ int.min.to!string ~ `,
1775 		"ip"    : ` ~ int.max.to!string ~ `,
1776 		"sm"    : ` ~ short.min.to!string ~ `,
1777 		"sp"    : ` ~ short.max.to!string ~ `,
1778 		"bm"    : ` ~ byte.min.to!string ~ `,
1779 		"bp"    : ` ~ byte.max.to!string ~ `,
1780 		"t"     : true,
1781 		"f"     : false,
1782 		"tp1"   : null,
1783 		"tp2"   : { "text1": "This is", "text2": "a", "text3": "test." },
1784 		"sa"    : [ 33, 44 ],
1785 		"da"    : [ 5, 6, 7 ],
1786 		"aa"    : { "hash" : { "text1":"x", "text2":"y", "text3":"z" } },
1787 		"e"     : "linear"
1788 	}`).read!Test;
1789 
1790 	assert(t2.tp2 && *t1.tp2 == *t2.tp2);
1791 	assert(t1.da == t2.da);
1792 	assert(t1.aa == t2.aa);
1793 	t2.tp2 = t1.tp2;
1794 	t2.da = t1.da;
1795 	t2.aa = t1.aa;
1796 	assert(t1 == t2);
1797 }
1798 
1799 // Test case for Issue #4
1800 unittest
1801 {
1802 	auto str = `{"initiator_carrier_code":null,"a":"b"}`;
1803 	auto js = parseTrustedJSON(str);
1804 	foreach(key; js.byKey)
1805 	{
1806 		if(key == "initiator_carrier_code")
1807 		{
1808 			auto t = js.read!string;
1809 			assert(t is null);
1810 		}
1811 	}
1812 }
1813 
1814 // Test case for Issue #5
1815 unittest
1816 {
1817 	import std.utf;
1818 	auto str = `{"a":"SΛNNO𐍈€한"}`;
1819 	str.validate;
1820 	validateJSON(str);
1821 }