1 module dutils.data.bson;
2 
3 // TODO: support nested structs and arrays
4 void populateFromBSON(T)(ref T object, ref BSON data) {
5   import std.conv : to;
6   import std.traits : isSomeFunction, hasMember;
7   import dutils.validation.validate : validate, ValidationError, ValidationErrors;
8   import dutils.validation.constraints : ValidationErrorTypes;
9 
10   ValidationError[] errors;
11 
12   static foreach (memberName; __traits(allMembers, T)) {
13     static if (!isSomeFunction!(__traits(getMember, T, memberName))) {
14       try {
15         // static if (is(typeof(__traits(getMember, T, memberName)) == EventMeta)) {
16         static if (hasMember!(typeof(__traits(getMember, T, memberName)),
17             "fromBSON") && isSomeFunction!(__traits(getMember,
18             __traits(getMember, T, memberName), "fromBSON"))) {
19           __traits(getMember, object, memberName) = __traits(getMember,
20               __traits(getMember, T, memberName), "fromBSON")(data[memberName]);
21         } else {
22           if (data[memberName].type != BSON.Type.null_) {
23             __traits(getMember, object, memberName) = data[memberName].get!(typeof(__traits(getMember,
24                 T, memberName)));
25           }
26         }
27       } catch (Exception error) {
28         errors ~= ValidationError(memberName, ValidationErrorTypes.type,
29             "Value must be convertable to type " ~ typeof(__traits(getMember,
30               T, memberName)).stringof ~ " but got " ~ data[memberName].type.to!string);
31       }
32     }
33   }
34 
35   try {
36     validate(object);
37   } catch (ValidationErrors validation) {
38     errors ~= validation.errors;
39   }
40 
41   if (errors.length > 0) {
42     throw new ValidationErrors(errors);
43   }
44 }
45 
46 /**
47  * populateFromBSON - ensure that population works with valid BSON data
48  */
49 unittest {
50   import dutils.validation.constraints : ValidateMinimumLength,
51     ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired;
52 
53   struct Person {
54     @ValidateMinimumLength(2)
55     @ValidateMaximumLength(100)
56     string name;
57 
58     @ValidateMinimum!double(20) double height;
59 
60     @ValidateEmail()
61     @ValidateRequired()
62     string email;
63 
64     @ValidateRequired()
65     bool member;
66   }
67 
68   auto data = BSON([
69       "does not exists": BSON(true),
70       "name": BSON("Anna"),
71       "height": BSON(170.1),
72       "email": BSON("anna@example.com"),
73       "member": BSON(true)
74       ]);
75 
76   Person person;
77   populateFromBSON(person, data);
78 
79   assert(person.name == "Anna", "expected name Anna");
80   assert(person.height == 170.1, "expected height 170");
81   assert(person.email == "anna@example.com", "expected email anna@example.com");
82   assert(person.member == true, "expected member true");
83 }
84 
85 /**
86  * populateFromBSON - ensure that validation errors are thrown with invalid BSON data
87  */
88 unittest {
89   import dutils.validation.validate : ValidationError, ValidationErrors;
90   import dutils.validation.constraints : ValidationErrorTypes, ValidateMinimumLength,
91     ValidateMaximumLength, ValidateMinimum, ValidateEmail, ValidateRequired;
92 
93   struct Person {
94     @ValidateMinimumLength(2)
95     @ValidateMaximumLength(100)
96     string name;
97 
98     @ValidateMinimum!double(20) double height;
99 
100     @ValidateEmail()
101     @ValidateRequired()
102     string email;
103 
104     @ValidateRequired()
105     bool member;
106   }
107 
108   auto data = BSON([
109       "does not exists": BSON(true),
110       "name": BSON("Anna"),
111       "height": BSON("not a number")
112       ]);
113 
114   Person person;
115 
116   auto catched = false;
117   try {
118     populateFromBSON(person, data);
119   } catch (ValidationErrors validation) {
120     import std.conv : to;
121 
122     catched = true;
123     assert(validation.errors.length == 4,
124         "expected 4 errors, got " ~ validation.errors.length.to!string
125         ~ " with message: " ~ validation.msg);
126     assert(validation.errors[0].type == "type", "expected minimumLength error");
127     assert(validation.errors[0].path == "height", "expected error path to be height");
128     assert(validation.errors[1].type == "minimum", "expected minimum error");
129     assert(validation.errors[1].path == "height", "expected error path to be height");
130     assert(validation.errors[2].type == "required", "expected required error");
131     assert(validation.errors[2].path == "email", "expected error path to be email");
132     assert(validation.errors[3].type == "required", "expected required error");
133     assert(validation.errors[3].path == "member", "expected error path to be member");
134   }
135 
136   assert(catched == true, "did not catch the expected errors");
137 }
138 
139 /**
140  * populateFromBSON - should populate
141  */
142 unittest {
143   import dutils.validation.constraints : ValidateRequired, ValidateEmail;
144 
145   struct Email {
146     @ValidateRequired()
147     @ValidateEmail()
148     string to;
149 
150     @ValidateEmail()
151     string from;
152 
153     string subject;
154 
155     @ValidateRequired()
156     string body;
157   }
158 
159   auto data = BSON([
160       "does not exists": BSON(true),
161       "to": BSON("anna@example.com"),
162       "body": BSON("Some text")
163       ]);
164 
165   Email email;
166   populateFromBSON(email, data);
167 
168   assert(email.to == "anna@example.com", "expected to to be anna@example.com");
169   assert(email.from == "", "expected from to be \"\"");
170   assert(email.subject == "", "expected from to be \"\"");
171   assert(email..body == "Some text", "expected from to be \"Some text\"");
172 }
173 
174 /**
175 	BSON serialization and value handling.
176 
177 	Copyright: © 2012-2015 Sönke Ludwig
178 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
179 	Authors: Sönke Ludwig
180 */
181 
182 import dutils.data.json;
183 
184 ///
185 unittest {
186   void manipulateBSON(BSON b) {
187     import std.stdio;
188 
189     // retrieving the values is done using get()
190     assert(b["name"].get!string == "Example");
191     assert(b["id"].get!int == 1);
192 
193     // semantic conversions can be done using to()
194     assert(b["id"].to!string == "1");
195 
196     // prints:
197     // name: "Example"
198     // id: 1
199     foreach (string key, value; b)
200       writefln("%s: %s", key, value);
201 
202     // print out with JSON syntax: {"name": "Example", "id": 1}
203     writefln("BSON: %s", b.toString());
204   }
205 }
206 
207 /// Constructing `BSON` objects
208 unittest {
209   // construct a BSON object {"field1": "foo", "field2": 42, "field3": true}
210 
211   // using the constructor
212   BSON b1 = BSON([
213       "field1": BSON("foo"),
214       "field2": BSON(42),
215       "field3": BSON(true)
216       ]);
217 
218   // using piecewise construction
219   BSON b2 = BSON.emptyObject;
220   b2["field1"] = "foo";
221   b2["field2"] = 42;
222   b2["field3"] = true;
223 
224   // using serialization
225   struct S {
226     string field1;
227     int field2;
228     bool field3;
229   }
230 
231   BSON b3 = S("foo", 42, true).serializeToBSON();
232 }
233 
234 import std.algorithm;
235 import std.array;
236 import std.base64;
237 import std.bitmanip;
238 import std.conv;
239 import std.datetime;
240 import std.uuid : UUID;
241 import std.exception;
242 import std.range;
243 import std.traits;
244 import std.typecons : Tuple, tuple;
245 
246 alias bdata_t = immutable(ubyte)[];
247 
248 /**
249 	Represents a BSON value.
250 
251 
252 */
253 struct BSON {
254 @safe:
255 
256   /// Represents the type of a BSON value
257   enum Type : ubyte {
258     end = 0x00, /// End marker - should never occur explicitly
259     double_ = 0x01, /// A 64-bit floating point value
260     string = 0x02, /// A UTF-8 string
261     object = 0x03, /// An object aka. dictionary of string to BSON
262     array = 0x04, /// An array of BSON values
263     binData = 0x05, /// Raw binary data (ubyte[])
264     undefined = 0x06, /// Deprecated
265     objectID = 0x07, /// BSON Object ID (96-bit)
266     bool_ = 0x08, /// Boolean value
267     date = 0x09, /// Date value (UTC)
268     null_ = 0x0A, /// Null value
269     regex = 0x0B, /// Regular expression
270     dbRef = 0x0C, /// Deprecated
271     code = 0x0D, /// JaveScript code
272     symbol = 0x0E, /// Symbol/variable name
273     codeWScope = 0x0F, /// JavaScript code with scope
274     int_ = 0x10, /// 32-bit integer
275     timestamp = 0x11, /// Timestamp value
276     long_ = 0x12, /// 64-bit integer
277     minKey = 0xff, /// Internal value
278     maxKey = 0x7f, /// Internal value
279   }
280 
281   /// Returns a new, empty BSON value of type Object.
282   static @property BSON emptyObject() {
283     return BSON(cast(BSON[string]) null);
284   }
285 
286   /// Returns a new, empty BSON value of type Array.
287   static @property BSON emptyArray() {
288     return BSON(cast(BSON[]) null);
289   }
290 
291   private {
292     Type m_type = Type.undefined;
293     bdata_t m_data;
294   }
295 
296   /**
297 		Creates a new BSON value using raw data.
298 
299 		A slice of the first bytes of `data` is stored, containg the data related to the value. An
300 		exception is thrown if `data` is too short.
301 	*/
302   this(Type type, bdata_t data) {
303     m_type = type;
304     m_data = data;
305     final switch (type) {
306     case Type.end:
307       m_data = null;
308       break;
309     case Type.double_:
310       m_data = m_data[0 .. 8];
311       break;
312     case Type..string:
313       m_data = m_data[0 .. 4 + fromBSONData!int(m_data)];
314       break;
315     case Type.object:
316       m_data = m_data[0 .. fromBSONData!int(m_data)];
317       break;
318     case Type.array:
319       m_data = m_data[0 .. fromBSONData!int(m_data)];
320       break;
321     case Type.binData:
322       m_data = m_data[0 .. 5 + fromBSONData!int(m_data)];
323       break;
324     case Type.undefined:
325       m_data = null;
326       break;
327     case Type.objectID:
328       m_data = m_data[0 .. 12];
329       break;
330     case Type.bool_:
331       m_data = m_data[0 .. 1];
332       break;
333     case Type.date:
334       m_data = m_data[0 .. 8];
335       break;
336     case Type.null_:
337       m_data = null;
338       break;
339     case Type.regex:
340       auto tmp = m_data;
341       tmp.skipCString();
342       tmp.skipCString();
343       m_data = m_data[0 .. $ - tmp.length];
344       break;
345     case Type.dbRef:
346       m_data = m_data[0 .. 0];
347       assert(false, "Not implemented.");
348     case Type.code:
349       m_data = m_data[0 .. 4 + fromBSONData!int(m_data)];
350       break;
351     case Type.symbol:
352       m_data = m_data[0 .. 4 + fromBSONData!int(m_data)];
353       break;
354     case Type.codeWScope:
355       m_data = m_data[0 .. 0];
356       assert(false, "Not implemented.");
357     case Type.int_:
358       m_data = m_data[0 .. 4];
359       break;
360     case Type.timestamp:
361       m_data = m_data[0 .. 8];
362       break;
363     case Type.long_:
364       m_data = m_data[0 .. 8];
365       break;
366     case Type.minKey:
367       m_data = null;
368       break;
369     case Type.maxKey:
370       m_data = null;
371       break;
372     }
373   }
374 
375   /**
376 		Initializes a new BSON value from the given D type.
377 	*/
378   this(double value) {
379     opAssign(value);
380   }
381   /// ditto
382   this(string value, Type type = Type..string) {
383     assert(type == Type..string || type == Type.code || type == Type.symbol);
384     opAssign(value);
385     m_type = type;
386   }
387   /// ditto
388   this(in BSON[string] value) {
389     opAssign(value);
390   }
391   /// ditto
392   this(in BSON[] value) {
393     opAssign(value);
394   }
395   /// ditto
396   this(in BSONBinData value) {
397     opAssign(value);
398   }
399   /// ditto
400   this(in BSONObjectID value) {
401     opAssign(value);
402   }
403   /// ditto
404   this(bool value) {
405     opAssign(value);
406   }
407   /// ditto
408   this(in BSONDate value) {
409     opAssign(value);
410   }
411   /// ditto
412   this(typeof(null)) {
413     opAssign(null);
414   }
415   /// ditto
416   this(in BSONRegex value) {
417     opAssign(value);
418   }
419   /// ditto
420   this(int value) {
421     opAssign(value);
422   }
423   /// ditto
424   this(in BSONTimestamp value) {
425     opAssign(value);
426   }
427   /// ditto
428   this(long value) {
429     opAssign(value);
430   }
431   /// ditto
432   this(in JSON value) {
433     opAssign(value);
434   }
435   /// ditto
436   this(in UUID value) {
437     opAssign(value);
438   }
439 
440   /**
441 		Assigns a D type to a BSON value.
442 	*/
443   void opAssign(in BSON other) {
444     m_data = other.m_data;
445     m_type = other.m_type;
446   }
447   /// ditto
448   void opAssign(double value) {
449     m_data = toBSONData(value).idup;
450     m_type = Type.double_;
451   }
452   /// ditto
453   void opAssign(string value) {
454     import std.utf;
455 
456     debug std.utf.validate(value);
457     auto app = appender!bdata_t();
458     app.put(toBSONData(cast(int) value.length + 1));
459     app.put(cast(bdata_t) value);
460     app.put(cast(ubyte) 0);
461     m_data = app.data;
462     m_type = Type..string;
463   }
464   /// ditto
465   void opAssign(in BSON[string] value) {
466     auto app = appender!bdata_t();
467     foreach (k, ref v; value) {
468       app.put(cast(ubyte) v.type);
469       putCString(app, k);
470       app.put(v.data);
471     }
472 
473     auto dapp = appender!bdata_t();
474     dapp.put(toBSONData(cast(int) app.data.length + 5));
475     dapp.put(app.data);
476     dapp.put(cast(ubyte) 0);
477     m_data = dapp.data;
478     m_type = Type.object;
479   }
480   /// ditto
481   void opAssign(in BSON[] value) {
482     auto app = appender!bdata_t();
483     foreach (i, ref v; value) {
484       app.put(v.type);
485       putCString(app, to!string(i));
486       app.put(v.data);
487     }
488 
489     auto dapp = appender!bdata_t();
490     dapp.put(toBSONData(cast(int) app.data.length + 5));
491     dapp.put(app.data);
492     dapp.put(cast(ubyte) 0);
493     m_data = dapp.data;
494     m_type = Type.array;
495   }
496   /// ditto
497   void opAssign(in BSONBinData value) {
498     auto app = appender!bdata_t();
499     app.put(toBSONData(cast(int) value.rawData.length));
500     app.put(value.type);
501     app.put(value.rawData);
502 
503     m_data = app.data;
504     m_type = Type.binData;
505   }
506   /// ditto
507   void opAssign(in BSONObjectID value) {
508     m_data = value.m_bytes.idup;
509     m_type = Type.objectID;
510   }
511   /// ditto
512   void opAssign(bool value) {
513     m_data = [value ? 0x01 : 0x00];
514     m_type = Type.bool_;
515   }
516   /// ditto
517   void opAssign(in BSONDate value) {
518     m_data = toBSONData(value.m_time).idup;
519     m_type = Type.date;
520   }
521   /// ditto
522   void opAssign(typeof(null)) {
523     m_data = null;
524     m_type = Type.null_;
525   }
526   /// ditto
527   void opAssign(in BSONRegex value) {
528     auto app = appender!bdata_t();
529     putCString(app, value.expression);
530     putCString(app, value.options);
531     m_data = app.data;
532     m_type = type.regex;
533   }
534   /// ditto
535   void opAssign(int value) {
536     m_data = toBSONData(value).idup;
537     m_type = Type.int_;
538   }
539   /// ditto
540   void opAssign(in BSONTimestamp value) {
541     m_data = toBSONData(value.m_time).idup;
542     m_type = Type.timestamp;
543   }
544   /// ditto
545   void opAssign(long value) {
546     m_data = toBSONData(value).idup;
547     m_type = Type.long_;
548   }
549   /// ditto
550   void opAssign(in JSON value) @trusted {
551     auto app = appender!bdata_t();
552     m_type = writeBSON(app, value);
553     m_data = app.data;
554   }
555   /// ditto
556   void opAssign(in UUID value) {
557     opAssign(BSONBinData(BSONBinData.Type.uuid, value.data.idup));
558   }
559 
560   /**
561 		Returns the BSON type of this value.
562 	*/
563   @property Type type() const {
564     return m_type;
565   }
566 
567   bool isNull() const {
568     return m_type == Type.null_;
569   }
570 
571   /**
572 		Returns the raw data representing this BSON value (not including the field name and type).
573 	*/
574   @property bdata_t data() const {
575     return m_data;
576   }
577 
578   /**
579 		Converts the BSON value to a D value.
580 
581 		If the BSON type of the value does not match the D type, an exception is thrown.
582 
583 		See_Also: `deserializeBSON`, `opt`
584 	*/
585   T opCast(T)() const {
586     return get!T();
587   }
588   /// ditto
589   @property T get(T)() const {
590     static if (is(T == double)) {
591       checkType(Type.double_);
592       return fromBSONData!double(m_data);
593     } else static if (is(T == string)) {
594       checkType(Type..string, Type.code, Type.symbol);
595       return cast(string) m_data[4 .. 4 + fromBSONData!int(m_data) - 1];
596     } else static if (is(Unqual!T == BSON[string]) || is(Unqual!T == const(BSON)[string])) {
597       checkType(Type.object);
598       BSON[string] ret;
599       auto d = m_data[4 .. $];
600       while (d.length > 0) {
601         auto tp = cast(Type) d[0];
602         if (tp == Type.end)
603           break;
604         d = d[1 .. $];
605         auto key = skipCString(d);
606         auto value = BSON(tp, d);
607         d = d[value.data.length .. $];
608 
609         ret[key] = value;
610       }
611       return cast(T) ret;
612     } else static if (is(Unqual!T == BSON[]) || is(Unqual!T == const(BSON)[])) {
613       checkType(Type.array);
614       BSON[] ret;
615       auto d = m_data[4 .. $];
616       while (d.length > 0) {
617         auto tp = cast(Type) d[0];
618         if (tp == Type.end)
619           break;
620         /*auto key = */
621         skipCString(d); // should be '0', '1', ...
622         auto value = BSON(tp, d);
623         d = d[value.data.length .. $];
624 
625         ret ~= value;
626       }
627       return cast(T) ret;
628     } else static if (is(T == BSONBinData)) {
629       checkType(Type.binData);
630       auto size = fromBSONData!int(m_data);
631       auto type = cast(BSONBinData.Type) m_data[4];
632       return BSONBinData(type, m_data[5 .. 5 + size]);
633     } else static if (is(T == BSONObjectID)) {
634       checkType(Type.objectID);
635       return BSONObjectID(m_data[0 .. 12]);
636     } else static if (is(T == bool)) {
637       checkType(Type.bool_);
638       return m_data[0] != 0;
639     } else static if (is(T == BSONDate)) {
640       checkType(Type.date);
641       return BSONDate(fromBSONData!long(m_data));
642     } else static if (is(T == BSONRegex)) {
643       checkType(Type.regex);
644       auto d = m_data[0 .. $];
645       auto expr = skipCString(d);
646       auto options = skipCString(d);
647       return BSONRegex(expr, options);
648     } else static if (is(T == int)) {
649       checkType(Type.int_);
650       return fromBSONData!int(m_data);
651     } else static if (is(T == BSONTimestamp)) {
652       checkType(Type.timestamp);
653       return BSONTimestamp(fromBSONData!long(m_data));
654     } else static if (is(T == long)) {
655       checkType(Type.long_);
656       return fromBSONData!long(m_data);
657     } else static if (is(T == JSON)) {
658       pragma(msg,
659           "BSON.get!JSON() and BSON.opCast!JSON() will soon be removed. Please use BSON.toJSON() instead.");
660       return this.toJSON();
661     } else static if (is(T == UUID)) {
662       checkType(Type.binData);
663       auto bbd = this.get!BSONBinData();
664       enforce(bbd.type == BSONBinData.Type.uuid,
665           "BSONBinData value is type '" ~ to!string(bbd.type) ~ "', expected to be uuid");
666       const ubyte[16] b = bbd.rawData;
667       return UUID(b);
668     } else static if (is(T == SysTime)) {
669       checkType(Type.date);
670       return BSONDate(fromBSONData!long(m_data)).toSysTime();
671     } else
672       static assert(false, "Cannot cast " ~ typeof(this).stringof ~ " to '" ~ T.stringof ~ "'.");
673   }
674 
675   /** Returns the native type for this BSON if it matches the current runtime type.
676 
677 		If the runtime type does not match the given native type, the 'def' parameter is returned
678 		instead.
679 	*/
680   T opt(T)(T def = T.init) {
681     if (isNull())
682       return def;
683     try
684       return cast(T) this;
685     catch (Exception e)
686       return def;
687   }
688   /// ditto
689   const(T) opt(T)(const(T) def = const(T).init) const {
690     if (isNull())
691       return def;
692     try
693       return cast(T) this;
694     catch (Exception e)
695       return def;
696   }
697 
698   /** Returns the length of a BSON value of type String, Array, Object or BinData.
699 	*/
700   @property size_t length() const {
701     switch (m_type) {
702     default:
703       enforce(false, "BSON objects of type " ~ to!string(m_type) ~ " do not have a length field.");
704       break;
705     case Type..string, Type.code, Type.symbol:
706       return (cast(string) this).length;
707     case Type.array:
708       return byValue.walkLength;
709     case Type.object:
710       return byValue.walkLength;
711     case Type.binData:
712       assert(false); //return (cast(BSONBinData)this).length; break;
713     }
714     assert(false);
715   }
716 
717   /** Converts a given JSON value to the corresponding BSON value.
718 	*/
719   static BSON fromJSON(in JSON value) @trusted {
720     auto app = appender!bdata_t();
721     auto tp = writeBSON(app, value);
722     return BSON(tp, app.data);
723   }
724 
725   /** Converts a BSON value to a JSON value.
726 
727 		All BSON types that cannot be exactly represented as JSON, will
728 		be converted to a string.
729 	*/
730   JSON toJSON() const {
731     switch (this.type) {
732     default:
733       assert(false);
734     case BSON.Type.double_:
735       return JSON(get!double());
736     case BSON.Type..string:
737       return JSON(get!string());
738     case BSON.Type.object:
739       JSON[string] ret;
740       foreach (k, v; this.byKeyValue)
741         ret[k] = v.toJSON();
742       return JSON(ret);
743     case BSON.Type.array:
744       auto ret = new JSON[this.length];
745       foreach (i, v; this.byIndexValue)
746         ret[i] = v.toJSON();
747       return JSON(ret);
748     case BSON.Type.binData:
749       return JSON(() @trusted {
750         return cast(string) Base64.encode(get!BSONBinData.rawData);
751       }());
752     case BSON.Type.objectID:
753       return JSON(get!BSONObjectID().toString());
754     case BSON.Type.bool_:
755       return JSON(get!bool());
756     case BSON.Type.date:
757       return JSON(get!BSONDate.toString());
758     case BSON.Type.null_:
759       return JSON(null);
760     case BSON.Type.regex:
761       assert(false, "TODO");
762     case BSON.Type.dbRef:
763       assert(false, "TODO");
764     case BSON.Type.code:
765       return JSON(get!string());
766     case BSON.Type.symbol:
767       return JSON(get!string());
768     case BSON.Type.codeWScope:
769       assert(false, "TODO");
770     case BSON.Type.int_:
771       return JSON(get!int());
772     case BSON.Type.timestamp:
773       return JSON(get!BSONTimestamp().m_time);
774     case BSON.Type.long_:
775       return JSON(get!long());
776     case BSON.Type.undefined:
777       return JSON();
778     }
779   }
780 
781   /** Returns a string representation of this BSON value in JSON format.
782 	*/
783   string toString() const {
784     return toJSON().toString();
785   }
786 
787   import std.typecons : Nullable;
788 
789   /**
790 		Check whether the BSON object contains the given key.
791 	*/
792   Nullable!BSON tryIndex(string key) const {
793     checkType(Type.object);
794     foreach (string idx, v; this.byKeyValue)
795       if (idx == key)
796         return Nullable!BSON(v);
797     return Nullable!BSON.init;
798   }
799 
800   /** Allows accessing fields of a BSON object using `[]`.
801 
802 		Returns a null value if the specified field does not exist.
803 	*/
804   inout(BSON) opIndex(string idx) inout {
805     foreach (string key, v; this.byKeyValue)
806       if (key == idx)
807         return v;
808     return BSON(null);
809   }
810   /// ditto
811   void opIndexAssign(T)(in T value, string idx) {
812     auto newcont = appender!bdata_t();
813     checkType(Type.object);
814     auto d = m_data[4 .. $];
815     while (d.length > 0) {
816       auto tp = cast(Type) d[0];
817       if (tp == Type.end)
818         break;
819       d = d[1 .. $];
820       auto key = skipCString(d);
821       auto val = BSON(tp, d);
822       d = d[val.data.length .. $];
823 
824       if (key != idx) {
825         // copy to new array
826         newcont.put(cast(ubyte) tp);
827         putCString(newcont, key);
828         newcont.put(val.data);
829       }
830     }
831 
832     static if (is(T == BSON))
833       alias bval = value;
834     else
835       auto bval = BSON(value);
836 
837     newcont.put(cast(ubyte) bval.type);
838     putCString(newcont, idx);
839     newcont.put(bval.data);
840 
841     auto newdata = appender!bdata_t();
842     newdata.put(toBSONData(cast(uint)(newcont.data.length + 5)));
843     newdata.put(newcont.data);
844     newdata.put(cast(ubyte) 0);
845     m_data = newdata.data;
846   }
847 
848   ///
849   unittest {
850     BSON value = BSON.emptyObject;
851     value["a"] = 1;
852     value["b"] = true;
853     value["c"] = "foo";
854     assert(value["a"] == BSON(1));
855     assert(value["b"] == BSON(true));
856     assert(value["c"] == BSON("foo"));
857   }
858 
859   ///
860   unittest {
861     auto srcUuid = UUID("00010203-0405-0607-0809-0a0b0c0d0e0f");
862 
863     BSON b = srcUuid;
864     auto u = b.get!UUID();
865 
866     assert(b.type == BSON.Type.binData);
867     assert(b.get!BSONBinData().type == BSONBinData.Type.uuid);
868     assert(u == srcUuid);
869   }
870 
871   /** Allows index based access of a BSON array value.
872 
873 		Returns a null value if the index is out of bounds.
874 	*/
875   inout(BSON) opIndex(size_t idx) inout {
876     foreach (size_t i, v; this.byIndexValue)
877       if (i == idx)
878         return v;
879     return BSON(null);
880   }
881 
882   ///
883   unittest {
884     BSON[] entries;
885     entries ~= BSON(1);
886     entries ~= BSON(true);
887     entries ~= BSON("foo");
888 
889     BSON value = BSON(entries);
890     assert(value[0] == BSON(1));
891     assert(value[1] == BSON(true));
892     assert(value[2] == BSON("foo"));
893   }
894 
895   /** Removes an entry from a BSON obect.
896 
897 		If the key doesn't exit, this function will be a no-op.
898 	*/
899   void remove(string key) {
900     checkType(Type.object);
901     auto d = m_data[4 .. $];
902     while (d.length > 0) {
903       size_t start_remainder = d.length;
904       auto tp = cast(Type) d[0];
905       if (tp == Type.end)
906         break;
907       d = d[1 .. $];
908       auto ekey = skipCString(d);
909       auto evalue = BSON(tp, d);
910       d = d[evalue.data.length .. $];
911 
912       if (ekey == key) {
913         m_data = m_data[0 .. $ - start_remainder] ~ d;
914         break;
915       }
916     }
917   }
918 
919   unittest {
920     auto o = BSON.emptyObject;
921     o["a"] = BSON(1);
922     o["b"] = BSON(2);
923     o["c"] = BSON(3);
924     assert(o.length == 3);
925     o.remove("b");
926     assert(o.length == 2);
927     assert(o["a"] == BSON(1));
928     assert(o["c"] == BSON(3));
929     o.remove("c");
930     assert(o.length == 1);
931     assert(o["a"] == BSON(1));
932     o.remove("c");
933     assert(o.length == 1);
934     assert(o["a"] == BSON(1));
935     o.remove("a");
936     assert(o.length == 0);
937   }
938 
939   /**
940 		Allows foreach iterating over BSON objects and arrays.
941 	*/
942   int opApply(scope int delegate(BSON obj) del) const @system {
943     foreach (value; byValue)
944       if (auto ret = del(value))
945         return ret;
946     return 0;
947   }
948   /// ditto
949   int opApply(scope int delegate(size_t idx, BSON obj) del) const @system {
950     foreach (index, value; byIndexValue)
951       if (auto ret = del(index, value))
952         return ret;
953     return 0;
954   }
955   /// ditto
956   int opApply(scope int delegate(string idx, BSON obj) del) const @system {
957     foreach (key, value; byKeyValue)
958       if (auto ret = del(key, value))
959         return ret;
960     return 0;
961   }
962 
963   /// Iterates over all values of an object or array.
964   auto byValue() const {
965     checkType(Type.array, Type.object);
966     return byKeyValueImpl().map!(t => t[1]);
967   }
968   /// Iterates over all index/value pairs of an array.
969   auto byIndexValue() const {
970     checkType(Type.array);
971     return byKeyValueImpl().map!(t => Tuple!(size_t, "key", BSON, "value")(t[0].to!size_t, t[1]));
972   }
973   /// Iterates over all key/value pairs of an object.
974   auto byKeyValue() const {
975     checkType(Type.object);
976     return byKeyValueImpl();
977   }
978 
979   private auto byKeyValueImpl() const {
980     checkType(Type.object, Type.array);
981 
982     alias T = Tuple!(string, "key", BSON, "value");
983 
984     static struct Rng {
985       private {
986         immutable(ubyte)[] data;
987         string key;
988         BSON value;
989       }
990 
991       @property bool empty() const {
992         return data.length == 0;
993       }
994 
995       @property T front() {
996         return T(key, value);
997       }
998 
999       @property Rng save() const {
1000         return this;
1001       }
1002 
1003       void popFront() {
1004         auto tp = cast(Type) data[0];
1005         data = data[1 .. $];
1006         if (tp == Type.end)
1007           return;
1008         key = skipCString(data);
1009         value = BSON(tp, data);
1010         data = data[value.data.length .. $];
1011       }
1012     }
1013 
1014     auto ret = Rng(m_data[4 .. $]);
1015     ret.popFront();
1016     return ret;
1017   }
1018 
1019   ///
1020   bool opEquals(ref const BSON other) const {
1021     if (m_type != other.m_type)
1022       return false;
1023     if (m_type != Type.object)
1024       return m_data == other.m_data;
1025 
1026     if (m_data == other.m_data)
1027       return true;
1028     // Similar objects can have a different key order, but they must have a same length
1029     if (m_data.length != other.m_data.length)
1030       return false;
1031 
1032     foreach (k, ref v; this.byKeyValue) {
1033       if (other[k] != v)
1034         return false;
1035     }
1036 
1037     return true;
1038   }
1039   /// ditto
1040   bool opEquals(const BSON other) const {
1041     if (m_type != other.m_type)
1042       return false;
1043 
1044     return opEquals(other);
1045   }
1046 
1047   private void checkType(in Type[] valid_types...) const {
1048     foreach (t; valid_types)
1049       if (m_type == t)
1050         return;
1051     throw new Exception("BSON value is type '" ~ to!string(
1052         m_type) ~ "', expected to be one of " ~ to!string(valid_types));
1053   }
1054 }
1055 
1056 /**
1057 	Represents a BSON binary data value (BSON.Type.binData).
1058 */
1059 struct BSONBinData {
1060 @safe:
1061 
1062   enum Type : ubyte {
1063     generic = 0x00,
1064     function_ = 0x01,
1065     binaryOld = 0x02,
1066     uuid = 0x04,
1067     md5 = 0x05,
1068     userDefined = 0x80,
1069 
1070     Generic = generic, /// Compatibility alias - will be deprecated soon
1071     Function = function_, /// Compatibility alias - will be deprecated soon
1072     BinaryOld = binaryOld, /// Compatibility alias - will be deprecated soon
1073     UUID = uuid, /// Compatibility alias - will be deprecated soon
1074     MD5 = md5, /// Compatibility alias - will be deprecated soon
1075     UserDefined = userDefined, /// Compatibility alias - will be deprecated soon
1076   }
1077 
1078   private {
1079     Type m_type;
1080     bdata_t m_data;
1081   }
1082 
1083   this(Type type, immutable(ubyte)[] data) {
1084     m_type = type;
1085     m_data = data;
1086   }
1087 
1088   @property Type type() const {
1089     return m_type;
1090   }
1091 
1092   @property bdata_t rawData() const {
1093     return m_data;
1094   }
1095 }
1096 
1097 /**
1098 	Represents a BSON object id (BSON.Type.binData).
1099 */
1100 struct BSONObjectID {
1101 @safe:
1102 
1103   private {
1104     ubyte[12] m_bytes;
1105     static immutable uint MACHINE_ID;
1106     static immutable int ms_pid;
1107     static uint ms_inc = 0;
1108   }
1109 
1110   shared static this() {
1111     import std.process;
1112     import std.random;
1113 
1114     MACHINE_ID = uniform(0, 0xffffff);
1115     ms_pid = thisProcessID;
1116   }
1117 
1118   static this() {
1119     import std.random;
1120 
1121     ms_inc = uniform(0, 0xffffff);
1122   }
1123 
1124   /** Constructs a new object ID from the given raw byte array.
1125 	*/
1126   this(in ubyte[] bytes) {
1127     assert(bytes.length == 12);
1128     m_bytes[] = bytes[];
1129   }
1130 
1131   /** Creates an on object ID from a string in standard hexa-decimal form.
1132 	*/
1133   static BSONObjectID fromString(string str) {
1134     import std.conv : ConvException;
1135 
1136     static const lengthex = new ConvException("BSON Object ID string must be 24 characters.");
1137     static const charex = new ConvException("Not a valid hex string.");
1138 
1139     if (str.length != 24)
1140       throw lengthex;
1141     BSONObjectID ret = void;
1142     uint b = 0;
1143     foreach (i, ch; str) {
1144       ubyte n;
1145       if (ch >= '0' && ch <= '9')
1146         n = cast(ubyte)(ch - '0');
1147       else if (ch >= 'a' && ch <= 'f')
1148         n = cast(ubyte)(ch - 'a' + 10);
1149       else if (ch >= 'A' && ch <= 'F')
1150         n = cast(ubyte)(ch - 'F' + 10);
1151       else
1152         throw charex;
1153       b <<= 4;
1154       b += n;
1155       if (i % 8 == 7) {
1156         auto j = i / 8;
1157         ret.m_bytes[j * 4 .. (j + 1) * 4] = toBigEndianData(b)[];
1158         b = 0;
1159       }
1160     }
1161     return ret;
1162   }
1163   /// ditto
1164   alias fromHexString = fromString;
1165 
1166   /** Generates a unique object ID.
1167 	 *
1168 	 *   By default it will use `Clock.currTime(UTC())` as the timestamp
1169 	 *   which guarantees that `BSONObjectID`s are chronologically
1170 	 *   sorted.
1171 	*/
1172   static BSONObjectID generate(in SysTime time = Clock.currTime(UTC())) {
1173     import std.datetime;
1174 
1175     BSONObjectID ret = void;
1176     ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint) time.toUnixTime())[];
1177     ret.m_bytes[4 .. 7] = toBSONData(MACHINE_ID)[0 .. 3];
1178     ret.m_bytes[7 .. 9] = toBSONData(cast(ushort) ms_pid)[];
1179     ret.m_bytes[9 .. 12] = toBigEndianData(ms_inc++)[1 .. 4];
1180     return ret;
1181   }
1182 
1183   /** Creates a pseudo object ID that matches the given date.
1184 
1185 		This kind of ID can be useful to query a database for items in a certain
1186 		date interval using their ID. This works using the property of standard BSON
1187 		object IDs that they store their creation date as part of the ID. Note that
1188 		this date part is only 32-bit wide and is limited to the same timespan as a
1189 		32-bit Unix timestamp.
1190 	*/
1191   static BSONObjectID createDateID(in SysTime time) {
1192     BSONObjectID ret;
1193     ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint) time.toUnixTime())[];
1194     return ret;
1195   }
1196 
1197   /** Returns true for any non-zero ID.
1198 	*/
1199   @property bool valid() const {
1200     foreach (b; m_bytes)
1201       if (b != 0)
1202         return true;
1203     return false;
1204   }
1205 
1206   /** Extracts the time/date portion of the object ID.
1207 
1208 		For IDs created using the standard generation algorithm or using createDateID
1209 		this will return the associated time stamp.
1210 	*/
1211   @property SysTime timeStamp() const {
1212     ubyte[4] tm = m_bytes[0 .. 4];
1213     return SysTime(unixTimeToStdTime(bigEndianToNative!uint(tm)));
1214   }
1215 
1216   /** Allows for relational comparison of different IDs.
1217 	*/
1218   int opCmp(ref const BSONObjectID other) const {
1219     import core.stdc.string;
1220 
1221     return () @trusted {
1222       return memcmp(m_bytes.ptr, other.m_bytes.ptr, m_bytes.length);
1223     }();
1224   }
1225 
1226   /** Converts the ID to its standard hexa-decimal string representation.
1227 	*/
1228   string toString() const pure {
1229     enum hexdigits = "0123456789abcdef";
1230     auto ret = new char[24];
1231     foreach (i, b; m_bytes) {
1232       ret[i * 2 + 0] = hexdigits[(b >> 4) & 0x0F];
1233       ret[i * 2 + 1] = hexdigits[b & 0x0F];
1234     }
1235     return ret;
1236   }
1237 
1238   inout(ubyte)[] opCast() inout {
1239     return m_bytes;
1240   }
1241 }
1242 
1243 unittest {
1244   auto t0 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime);
1245   auto id = BSONObjectID.generate();
1246   auto t1 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime);
1247   assert(t0 <= id.timeStamp);
1248   assert(id.timeStamp <= t1);
1249 
1250   id = BSONObjectID.generate(t0);
1251   assert(id.timeStamp == t0);
1252 
1253   id = BSONObjectID.generate(t1);
1254   assert(id.timeStamp == t1);
1255 
1256   immutable dt = DateTime(2014, 07, 31, 19, 14, 55);
1257   id = BSONObjectID.generate(SysTime(dt, UTC()));
1258   assert(id.timeStamp == SysTime(dt, UTC()));
1259 }
1260 
1261 unittest {
1262   auto b = BSON(true);
1263   assert(b.opt!bool(false) == true);
1264   assert(b.opt!int(12) == 12);
1265   assert(b.opt!(BSON[])(null).length == 0);
1266 
1267   const c = b;
1268   assert(c.opt!bool(false) == true);
1269   assert(c.opt!int(12) == 12);
1270   assert(c.opt!(BSON[])(null).length == 0);
1271 }
1272 
1273 /**
1274 	Represents a BSON date value (`BSON.Type.date`).
1275 
1276 	BSON date values are stored in UNIX time format, counting the number of
1277 	milliseconds from 1970/01/01.
1278 */
1279 struct BSONDate {
1280 @safe:
1281 
1282   private long m_time; // milliseconds since UTC unix epoch
1283 
1284   /** Constructs a BSONDate from the given date value.
1285 
1286 		The time-zone independent Date and DateTime types are assumed to be in
1287 		the local time zone and converted to UTC if tz is left to null.
1288 	*/
1289   this(in Date date, immutable TimeZone tz = null) {
1290     this(SysTime(date, tz));
1291   }
1292   /// ditto
1293   this(in DateTime date, immutable TimeZone tz = null) {
1294     this(SysTime(date, tz));
1295   }
1296   /// ditto
1297   this(in SysTime date) {
1298     this(fromStdTime(date.stdTime()).m_time);
1299   }
1300 
1301   /** Constructs a BSONDate from the given UNIX time.
1302 
1303 		unix_time needs to be given in milliseconds from 1970/01/01. This is
1304 		the native storage format for BSONDate.
1305 	*/
1306   this(long unix_time) {
1307     m_time = unix_time;
1308   }
1309 
1310   /** Constructs a BSONDate from the given date/time string in ISO extended format.
1311 	*/
1312   static BSONDate fromString(string iso_ext_string) {
1313     return BSONDate(SysTime.fromISOExtString(iso_ext_string));
1314   }
1315 
1316   /** Constructs a BSONDate from the given date/time in standard time as defined in `std.datetime`.
1317 	*/
1318   static BSONDate fromStdTime(long std_time) {
1319     enum zero = unixTimeToStdTime(0);
1320     return BSONDate((std_time - zero) / 10_000L);
1321   }
1322 
1323   /** The raw unix time value.
1324 
1325 		This is the native storage/transfer format of a BSONDate.
1326 	*/
1327   @property long value() const {
1328     return m_time;
1329   }
1330   /// ditto
1331   @property void value(long v) {
1332     m_time = v;
1333   }
1334 
1335   /** Returns the date formatted as ISO extended format.
1336 	*/
1337   string toString() const {
1338     return toSysTime().toISOExtString();
1339   }
1340 
1341   /* Converts to a SysTime using UTC timezone.
1342 	*/
1343   SysTime toSysTime() const {
1344     return toSysTime(UTC());
1345   }
1346 
1347   /* Converts to a SysTime with a given timezone.
1348 	*/
1349   SysTime toSysTime(immutable TimeZone tz) const {
1350     auto zero = unixTimeToStdTime(0);
1351     return SysTime(zero + m_time * 10_000L, tz);
1352   }
1353 
1354   /** Allows relational and equality comparisons.
1355 	*/
1356   bool opEquals(ref const BSONDate other) const {
1357     return m_time == other.m_time;
1358   }
1359   /// ditto
1360   int opCmp(ref const BSONDate other) const {
1361     if (m_time == other.m_time)
1362       return 0;
1363     if (m_time < other.m_time)
1364       return -1;
1365     else
1366       return 1;
1367   }
1368 }
1369 
1370 /**
1371 	Represents a BSON timestamp value `(BSON.Type.timestamp)`.
1372 */
1373 struct BSONTimestamp {
1374 @safe:
1375 
1376   private long m_time;
1377 
1378   this(long time) {
1379     m_time = time;
1380   }
1381 }
1382 
1383 /**
1384 	Represents a BSON regular expression value `(BSON.Type.regex)`.
1385 */
1386 struct BSONRegex {
1387 @safe:
1388 
1389   private {
1390     string m_expr;
1391     string m_options;
1392   }
1393 
1394   this(string expr, string options) {
1395     m_expr = expr;
1396     m_options = options;
1397   }
1398 
1399   @property string expression() const {
1400     return m_expr;
1401   }
1402 
1403   @property string options() const {
1404     return m_options;
1405   }
1406 }
1407 
1408 /**
1409 	Serializes the given value to BSON.
1410 
1411 	The following types of values are supported:
1412 
1413 	$(DL
1414 		$(DT `BSON`)            $(DD Used as-is)
1415 		$(DT `JSON`)            $(DD Converted to BSON)
1416 		$(DT `BSONBinData`)     $(DD Converted to `BSON.Type.binData`)
1417 		$(DT `BSONObjectID`)    $(DD Converted to `BSON.Type.objectID`)
1418 		$(DT `BSONDate`)        $(DD Converted to `BSON.Type.date`)
1419 		$(DT `BSONTimestamp`)   $(DD Converted to `BSON.Type.timestamp`)
1420 		$(DT `BSONRegex`)       $(DD Converted to `BSON.Type.regex`)
1421 		$(DT `null`)            $(DD Converted to `BSON.Type.null_`)
1422 		$(DT `bool`)            $(DD Converted to `BSON.Type.bool_`)
1423 		$(DT `float`, `double`)   $(DD Converted to `BSON.Type.double_`)
1424 		$(DT `short`, `ushort`, `int`, `uint`, `long`, `ulong`) $(DD Converted to `BSON.Type.long_`)
1425 		$(DT `string`)          $(DD Converted to `BSON.Type.string`)
1426 		$(DT `ubyte[]`)         $(DD Converted to `BSON.Type.binData`)
1427 		$(DT `T[]`)             $(DD Converted to `BSON.Type.array`)
1428 		$(DT `T[string]`)       $(DD Converted to `BSON.Type.object`)
1429 		$(DT `struct`)          $(DD Converted to `BSON.Type.object`)
1430 		$(DT `class`)           $(DD Converted to `BSON.Type.object` or `BSON.Type.null_`)
1431 	)
1432 
1433 	All entries of an array or an associative array, as well as all R/W properties and
1434 	all fields of a struct/class are recursively serialized using the same rules.
1435 
1436 	Fields ending with an underscore will have the last underscore stripped in the
1437 	serialized output. This makes it possible to use fields with D keywords as their name
1438 	by simply appending an underscore.
1439 
1440 	The following methods can be used to customize the serialization of structs/classes:
1441 
1442 	---
1443 	BSON toBSON() const;
1444 	static T fromBSON(BSON src);
1445 
1446 	JSON toJSON() const;
1447 	static T fromJSON(JSON src);
1448 
1449 	string toString() const;
1450 	static T fromString(string src);
1451 	---
1452 
1453 	The methods will have to be defined in pairs. The first pair that is implemented by
1454 	the type will be used for serialization (i.e. `toBSON` overrides `toJSON`).
1455 
1456 	See_Also: `deserializeBSON`
1457 */
1458 import dutils.data.utils.serialization;
1459 
1460 BSON serializeToBSON(T)(auto ref T value, ubyte[] buffer = null) {
1461   return serialize!BSONSerializer(value, buffer);
1462 }
1463 
1464 template deserializeBSON(T) {
1465   /**
1466 		Deserializes a BSON value into the destination variable.
1467 
1468 		The same types as for `serializeToBSON()` are supported and handled inversely.
1469 
1470 		See_Also: `serializeToBSON`
1471 	*/
1472   void deserializeBSON(ref T dst, BSON src) {
1473     dst = deserializeBSON!T(src);
1474   }
1475   /// ditto
1476   T deserializeBSON(BSON src) {
1477     return deserialize!(BSONSerializer, T)(src);
1478   }
1479 }
1480 
1481 unittest {
1482   import std.stdio;
1483 
1484   enum Foo : string {
1485     k = "test"
1486   }
1487 
1488   enum Boo : int {
1489     l = 5
1490   }
1491 
1492   static struct S {
1493     float a;
1494     double b;
1495     bool c;
1496     int d;
1497     string e;
1498     byte f;
1499     ubyte g;
1500     long h;
1501     ulong i;
1502     float[] j;
1503     Foo k;
1504     Boo l;
1505   }
1506 
1507   immutable S t = {
1508     1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [
1509       1.1, 1.2, 1.3
1510     ], Foo.k, Boo.l,};
1511     S u;
1512     deserializeBSON(u, serializeToBSON(t));
1513     assert(t.a == u.a);
1514     assert(t.b == u.b);
1515     assert(t.c == u.c);
1516     assert(t.d == u.d);
1517     assert(t.e == u.e);
1518     assert(t.f == u.f);
1519     assert(t.g == u.g);
1520     assert(t.h == u.h);
1521     assert(t.i == u.i);
1522     assert(t.j == u.j);
1523     assert(t.k == u.k);
1524     assert(t.l == u.l);
1525   }
1526 
1527   unittest {
1528     assert(uint.max == serializeToBSON(uint.max).deserializeBSON!uint);
1529     assert(ulong.max == serializeToBSON(ulong.max).deserializeBSON!ulong);
1530   }
1531 
1532   unittest {
1533     assert(deserializeBSON!SysTime(serializeToBSON(SysTime(0))) == SysTime(0));
1534     assert(deserializeBSON!SysTime(serializeToBSON(SysTime(0, UTC()))) == SysTime(0, UTC()));
1535     assert(deserializeBSON!Date(serializeToBSON(Date.init)) == Date.init);
1536     assert(deserializeBSON!Date(serializeToBSON(Date(2001, 1, 1))) == Date(2001, 1, 1));
1537   }
1538 
1539   @safe unittest {
1540     static struct A {
1541       int value;
1542       static A fromJSON(JSON val) @safe {
1543         return A(val.get!int);
1544       }
1545 
1546       JSON toJSON() const @safe {
1547         return JSON(value);
1548       }
1549 
1550       BSON toBSON() {
1551         return BSON();
1552       }
1553     }
1554 
1555     static assert(!isStringSerializable!A && isJSONSerializable!A && !isBSONSerializable!A);
1556     static assert(!isStringSerializable!(const(A))
1557         && isJSONSerializable!(const(A)) && !isBSONSerializable!(const(A)));
1558     //	assert(serializeToBSON(const A(123)) == BSON(123));
1559     //	assert(serializeToBSON(A(123))       == BSON(123));
1560 
1561     static struct B {
1562       int value;
1563       static B fromBSON(BSON val) @safe {
1564         return B(val.get!int);
1565       }
1566 
1567       BSON toBSON() const @safe {
1568         return BSON(value);
1569       }
1570 
1571       JSON toJSON() {
1572         return JSON();
1573       }
1574     }
1575 
1576     static assert(!isStringSerializable!B && !isJSONSerializable!B && isBSONSerializable!B);
1577     static assert(!isStringSerializable!(const(B))
1578         && !isJSONSerializable!(const(B)) && isBSONSerializable!(const(B)));
1579     assert(serializeToBSON(const B(123)) == BSON(123));
1580     assert(serializeToBSON(B(123)) == BSON(123));
1581 
1582     static struct C {
1583       int value;
1584       static C fromString(string val) @safe {
1585         return C(val.to!int);
1586       }
1587 
1588       string toString() const @safe {
1589         return value.to!string;
1590       }
1591 
1592       JSON toJSON() {
1593         return JSON();
1594       }
1595     }
1596 
1597     static assert(isStringSerializable!C && !isJSONSerializable!C && !isBSONSerializable!C);
1598     static assert(isStringSerializable!(const(C))
1599         && !isJSONSerializable!(const(C)) && !isBSONSerializable!(const(C)));
1600     assert(serializeToBSON(const C(123)) == BSON("123"));
1601     assert(serializeToBSON(C(123)) == BSON("123"));
1602 
1603     static struct D {
1604       int value;
1605       string toString() const {
1606         return "";
1607       }
1608     }
1609 
1610     static assert(!isStringSerializable!D && !isJSONSerializable!D && !isBSONSerializable!D);
1611     static assert(!isStringSerializable!(const(D))
1612         && !isJSONSerializable!(const(D)) && !isBSONSerializable!(const(D)));
1613     assert(serializeToBSON(const D(123)) == serializeToBSON(["value": 123]));
1614     assert(serializeToBSON(D(123)) == serializeToBSON(["value": 123]));
1615 
1616     // test if const(class) is serializable
1617     static class E {
1618       int value;
1619       this(int v) @safe {
1620         value = v;
1621       }
1622 
1623       static E fromBSON(BSON val) @safe {
1624         return new E(val.get!int);
1625       }
1626 
1627       BSON toBSON() const @safe {
1628         return BSON(value);
1629       }
1630 
1631       JSON toJSON() {
1632         return JSON();
1633       }
1634     }
1635 
1636     static assert(!isStringSerializable!E && !isJSONSerializable!E && isBSONSerializable!E);
1637     static assert(!isStringSerializable!(const(E))
1638         && !isJSONSerializable!(const(E)) && isBSONSerializable!(const(E)));
1639     assert(serializeToBSON(new const E(123)) == BSON(123));
1640     assert(serializeToBSON(new E(123)) == BSON(123));
1641   }
1642 
1643   @safe unittest {
1644     static struct E {
1645       ubyte[4] bytes;
1646       ubyte[] more;
1647     }
1648 
1649     auto e = E([1, 2, 3, 4], [5, 6]);
1650     auto eb = serializeToBSON(e);
1651     assert(eb["bytes"].type == BSON.Type.binData);
1652     assert(eb["more"].type == BSON.Type.binData);
1653     assert(e == deserializeBSON!E(eb));
1654   }
1655 
1656   @safe unittest {
1657     static class C {
1658     @safe:
1659       int a;
1660       private int _b;
1661       @property int b() const {
1662         return _b;
1663       }
1664 
1665       @property void b(int v) {
1666         _b = v;
1667       }
1668 
1669       @property int test() const @safe {
1670         return 10;
1671       }
1672 
1673       void test2() {
1674       }
1675     }
1676 
1677     C c = new C;
1678     c.a = 1;
1679     c.b = 2;
1680 
1681     C d;
1682     deserializeBSON(d, serializeToBSON(c));
1683     assert(c.a == d.a);
1684     assert(c.b == d.b);
1685 
1686     const(C) e = c; // serialize const class instances (issue #653)
1687     deserializeBSON(d, serializeToBSON(e));
1688     assert(e.a == d.a);
1689     assert(e.b == d.b);
1690   }
1691 
1692   unittest {
1693     static struct C {
1694     @safe:
1695       int value;
1696       static C fromString(string val) {
1697         return C(val.to!int);
1698       }
1699 
1700       string toString() const {
1701         return value.to!string;
1702       }
1703     }
1704 
1705     enum Color {
1706       Red,
1707       Green,
1708       Blue
1709     }
1710 
1711     {
1712       static class T {
1713       @safe:
1714         string[Color] enumIndexedMap;
1715         string[C] stringableIndexedMap;
1716         this() {
1717           enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"];
1718           stringableIndexedMap = [C(42): "forty-two"];
1719         }
1720       }
1721 
1722       T original = new T;
1723       original.enumIndexedMap[Color.Green] = "olive";
1724       T other;
1725       deserializeBSON(other, serializeToBSON(original));
1726       assert(serializeToBSON(other) == serializeToBSON(original));
1727     }
1728     {
1729       static struct S {
1730         string[Color] enumIndexedMap;
1731         string[C] stringableIndexedMap;
1732       }
1733 
1734       S original;
1735       original.enumIndexedMap = [Color.Red: "magenta", Color.Blue: "deep blue"];
1736       original.enumIndexedMap[Color.Green] = "olive";
1737       original.stringableIndexedMap = [C(42): "forty-two"];
1738       S other;
1739       deserializeBSON(other, serializeToBSON(original));
1740       assert(serializeToBSON(other) == serializeToBSON(original));
1741     }
1742   }
1743 
1744   unittest {
1745     ubyte[] data = [1, 2, 3];
1746     auto BSON = serializeToBSON(data);
1747     assert(BSON.type == BSON.Type.binData);
1748     assert(deserializeBSON!(ubyte[])(BSON) == data);
1749   }
1750 
1751   unittest { // issue #709
1752     ulong[] data = [2354877787627192443, 1, 2354877787627192442];
1753     auto BSON = BSON.fromJSON(serializeToBSON(data).toJSON);
1754     assert(deserializeBSON!(ulong[])(BSON) == data);
1755   }
1756 
1757   unittest { // issue #709
1758     uint[] data = [1, 2, 3, 4];
1759     auto BSON = BSON.fromJSON(serializeToBSON(data).toJSON);
1760     //	assert(deserializeBSON!(uint[])(BSON) == data);
1761     assert(deserializeBSON!(ulong[])(BSON).equal(data));
1762   }
1763 
1764   unittest {
1765     import std.typecons;
1766 
1767     Nullable!bool x;
1768     auto BSON = serializeToBSON(x);
1769     assert(BSON.type == BSON.Type.null_);
1770     deserializeBSON(x, BSON);
1771     assert(x.isNull);
1772     x = true;
1773     BSON = serializeToBSON(x);
1774     assert(BSON.type == BSON.Type.bool_ && BSON.get!bool == true);
1775     deserializeBSON(x, BSON);
1776     assert(x == true);
1777   }
1778 
1779   unittest { // issue #793
1780     char[] test = "test".dup;
1781     auto BSON = serializeToBSON(test);
1782     //assert(BSON.type == BSON.Type.string);
1783     //assert(BSON.get!string == "test");
1784     assert(BSON.type == BSON.Type.array);
1785     assert(BSON[0].type == BSON.Type..string && BSON[0].get!string == "t");
1786   }
1787 
1788   @safe unittest { // issue #2212
1789     auto bsonRegex = BSON(BSONRegex(".*", "i"));
1790     auto parsedRegex = bsonRegex.get!BSONRegex;
1791     assert(bsonRegex.type == BSON.Type.regex);
1792     assert(parsedRegex.expression == ".*");
1793     assert(parsedRegex.options == "i");
1794   }
1795 
1796   unittest {
1797     UUID uuid = UUID("35399104-fbc9-4c08-bbaf-65a5efe6f5f2");
1798 
1799     auto bson = BSON(uuid);
1800     assert(bson.get!UUID == uuid);
1801     assert(bson.deserializeBSON!UUID == uuid);
1802 
1803     bson = BSON([BSON(uuid)]);
1804     assert(bson.deserializeBSON!(UUID[]) == [uuid]);
1805 
1806     bson = [uuid].serializeToBSON();
1807     assert(bson.deserializeBSON!(UUID[]) == [uuid]);
1808   }
1809 
1810   /**
1811 	Serializes to an in-memory BSON representation.
1812 
1813 	See_Also: `vibe.data.serialization.serialize`, `vibe.data.serialization.deserialize`, `serializeToBSON`, `deserializeBSON`
1814 */
1815   struct BSONSerializer {
1816     import dutils.data.utils.array : AllocAppender;
1817 
1818     private {
1819       AllocAppender!(ubyte[]) m_dst;
1820       size_t[] m_compositeStack;
1821       BSON.Type m_type = BSON.Type.null_;
1822       BSON m_inputData;
1823       string m_entryName;
1824       size_t m_entryIndex = size_t.max;
1825     }
1826 
1827     this(BSON input) @safe {
1828       m_inputData = input;
1829     }
1830 
1831     this(ubyte[] buffer) @safe {
1832       import dutils.data.utils.utilallocator;
1833 
1834       m_dst = () @trusted {
1835         return AllocAppender!(ubyte[])(vibeThreadAllocator(), buffer);
1836       }();
1837     }
1838 
1839     @disable this(this);
1840 
1841     template isSupportedValueType(T) {
1842       enum isSupportedValueType = is(typeof(getBSONTypeID(T.init)));
1843     }
1844 
1845     //
1846     // serialization
1847     //
1848     BSON getSerializedResult() @safe {
1849       auto ret = BSON(m_type, () @trusted { return cast(immutable) m_dst.data; }());
1850       () @trusted { m_dst.reset(); }();
1851       m_type = BSON.Type.null_;
1852       return ret;
1853     }
1854 
1855     void beginWriteDictionary(Traits)() {
1856       writeCompositeEntryHeader(BSON.Type.object);
1857       m_compositeStack ~= m_dst.data.length;
1858       m_dst.put(toBSONData(cast(int) 0));
1859     }
1860 
1861     void endWriteDictionary(Traits)() {
1862       m_dst.put(BSON.Type.end);
1863       auto sh = m_compositeStack[$ - 1];
1864       m_compositeStack.length--;
1865       m_dst.data[sh .. sh + 4] = toBSONData(cast(uint)(m_dst.data.length - sh))[];
1866     }
1867 
1868     void beginWriteDictionaryEntry(Traits)(string name) {
1869       m_entryName = name;
1870     }
1871 
1872     void endWriteDictionaryEntry(Traits)(string name) {
1873     }
1874 
1875     void beginWriteArray(Traits)(size_t) {
1876       writeCompositeEntryHeader(BSON.Type.array);
1877       m_compositeStack ~= m_dst.data.length;
1878       m_dst.put(toBSONData(cast(int) 0));
1879     }
1880 
1881     void endWriteArray(Traits)() {
1882       endWriteDictionary!Traits();
1883     }
1884 
1885     void beginWriteArrayEntry(Traits)(size_t idx) {
1886       m_entryIndex = idx;
1887     }
1888 
1889     void endWriteArrayEntry(Traits)(size_t idx) {
1890     }
1891 
1892     void writeValue(Traits, T)(auto ref T value) {
1893       writeValueH!(T, true)(value);
1894     }
1895 
1896     private void writeValueH(T, bool write_header)(auto ref T value) {
1897       alias UT = Unqual!T;
1898       static if (write_header)
1899         writeCompositeEntryHeader(getBSONTypeID(value));
1900 
1901       static if (is(UT == BSON)) {
1902         m_dst.put(value.data);
1903       } else static if (is(UT == JSON)) {
1904         m_dst.put(BSON(value).data);
1905       }  // FIXME: use .writeBSONValue
1906       else static if (is(UT == typeof(null))) {
1907       } else static if (is(UT == string)) {
1908         m_dst.put(toBSONData(cast(uint) value.length + 1));
1909         m_dst.putCString(value);
1910       } else static if (is(UT == BSONBinData)) {
1911         m_dst.put(toBSONData(cast(int) value.rawData.length));
1912         m_dst.put(value.type);
1913         m_dst.put(value.rawData);
1914       } else static if (is(UT == BSONObjectID)) {
1915         m_dst.put(value.m_bytes[]);
1916       } else static if (is(UT == BSONDate)) {
1917         m_dst.put(toBSONData(value.m_time));
1918       } else static if (is(UT == SysTime)) {
1919         m_dst.put(toBSONData(BSONDate(value).m_time));
1920       } else static if (is(UT == BSONRegex)) {
1921         m_dst.putCString(value.expression);
1922         m_dst.putCString(value.options);
1923       } else static if (is(UT == BSONTimestamp)) {
1924         m_dst.put(toBSONData(value.m_time));
1925       } else static if (is(UT == bool)) {
1926         m_dst.put(cast(ubyte)(value ? 0x01 : 0x00));
1927       } else static if (is(UT : int) && isIntegral!UT) {
1928         m_dst.put(toBSONData(cast(int) value));
1929       } else static if (is(UT : long) && isIntegral!UT) {
1930         m_dst.put(toBSONData(value));
1931       } else static if (is(UT : double) && isFloatingPoint!UT) {
1932         m_dst.put(toBSONData(cast(double) value));
1933       } else static if (is(UT == UUID)) {
1934         m_dst.put(BSON(value).data);
1935       } else static if (isBSONSerializable!UT) {
1936         static if (!__traits(compiles, ()@safe { return value.toBSON(); }()))
1937           pragma(msg,
1938               "Non-@safe toBSON/fromBSON methods are deprecated - annotate "
1939               ~ T.stringof ~ ".toBSON() with @safe.");
1940         m_dst.put(() @trusted { return value.toBSON(); }().data);
1941       } else static if (isJSONSerializable!UT) {
1942         static if (!__traits(compiles, ()@safe { return value.toJSON(); }()))
1943           pragma(msg,
1944               "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
1945               ~ UT.stringof ~ ".toJSON() with @safe.");
1946         m_dst.put(BSON(() @trusted { return value.toJSON(); }()).data);
1947       } else static if (is(UT : const(ubyte)[])) {
1948         writeValueH!(BSONBinData, false)(BSONBinData(BSONBinData.Type.generic, value.idup));
1949       } else
1950         static assert(false, "Unsupported type: " ~ UT.stringof);
1951     }
1952 
1953     private void writeCompositeEntryHeader(BSON.Type tp) @safe {
1954       if (!m_compositeStack.length) {
1955         assert(m_type == BSON.Type.null_, "Overwriting root item.");
1956         m_type = tp;
1957       }
1958 
1959       if (m_entryName !is null) {
1960         m_dst.put(tp);
1961         m_dst.putCString(m_entryName);
1962         m_entryName = null;
1963       } else if (m_entryIndex != size_t.max) {
1964         import std.format;
1965 
1966         m_dst.put(tp);
1967         static struct Wrapper {
1968         @trusted:
1969           AllocAppender!(ubyte[])* app;
1970           void put(char ch) {
1971             (*app).put(ch);
1972           }
1973 
1974           void put(in char[] str) {
1975             (*app).put(cast(const(ubyte)[]) str);
1976           }
1977         }
1978 
1979         auto wr = Wrapper(&m_dst);
1980         wr.formattedWrite("%d\0", m_entryIndex);
1981         m_entryIndex = size_t.max;
1982       }
1983     }
1984 
1985     //
1986     // deserialization
1987     //
1988     void readDictionary(Traits)(scope void delegate(string) @safe entry_callback) {
1989       enforce(m_inputData.type == BSON.Type.object,
1990           "Expected object instead of " ~ m_inputData.type.to!string());
1991       auto old = m_inputData;
1992       foreach (string name, value; old.byKeyValue) {
1993         m_inputData = value;
1994         entry_callback(name);
1995       }
1996       m_inputData = old;
1997     }
1998 
1999     void beginReadDictionaryEntry(Traits)(string name) {
2000     }
2001 
2002     void endReadDictionaryEntry(Traits)(string name) {
2003     }
2004 
2005     void readArray(Traits)(scope void delegate(size_t) @safe size_callback,
2006         scope void delegate() @safe entry_callback) {
2007       enforce(m_inputData.type == BSON.Type.array,
2008           "Expected array instead of " ~ m_inputData.type.to!string());
2009       auto old = m_inputData;
2010       foreach (value; old.byValue) {
2011         m_inputData = value;
2012         entry_callback();
2013       }
2014       m_inputData = old;
2015     }
2016 
2017     void beginReadArrayEntry(Traits)(size_t index) {
2018     }
2019 
2020     void endReadArrayEntry(Traits)(size_t index) {
2021     }
2022 
2023     T readValue(Traits, T)() {
2024       static if (is(T == BSON))
2025         return m_inputData;
2026       else static if (is(T == JSON))
2027         return m_inputData.toJSON();
2028       else static if (is(T == bool))
2029         return m_inputData.get!bool();
2030       else static if (is(T == uint))
2031         return cast(T) m_inputData.get!int();
2032       else static if (is(T : int)) {
2033         if (m_inputData.type == BSON.Type.long_) {
2034           enforce((m_inputData.get!long() >= int.min) && (m_inputData.get!long() <= int.max),
2035               "Long out of range while attempting to deserialize to int: " ~ m_inputData.get!long
2036               .to!string);
2037           return cast(T) m_inputData.get!long();
2038         } else
2039           return m_inputData.get!int().to!T;
2040       } else static if (is(T : long)) {
2041         if (m_inputData.type == BSON.Type.int_)
2042           return cast(T) m_inputData.get!int();
2043         else
2044           return cast(T) m_inputData.get!long();
2045       } else static if (is(T : double))
2046         return cast(T) m_inputData.get!double();
2047       else static if (is(T == SysTime)) {
2048         // support legacy behavior to serialize as string
2049         if (m_inputData.type == BSON.Type..string)
2050           return SysTime.fromISOExtString(m_inputData.get!string);
2051         else
2052           return m_inputData.get!BSONDate().toSysTime();
2053       } else static if (isBSONSerializable!T) {
2054         static if (!__traits(compiles, ()@safe { return T.fromBSON(BSON.init); }()))
2055           pragma(msg,
2056               "Non-@safe toBSON/fromBSON methods are deprecated - annotate "
2057               ~ T.stringof ~ ".fromBSON() with @safe.");
2058         auto bval = readValue!(Traits, BSON);
2059         return () @trusted { return T.fromBSON(bval); }();
2060       } else static if (isJSONSerializable!T) {
2061         static if (!__traits(compiles, ()@safe { return T.fromJSON(JSON.init); }()))
2062           pragma(msg,
2063               "Non-@safe toJSON/fromJSON methods are deprecated - annotate "
2064               ~ T.stringof ~ ".fromJSON() with @safe.");
2065         auto jval = readValue!(Traits, BSON).toJSON();
2066         return () @trusted { return T.fromJSON(jval); }();
2067       } else static if (is(T : const(ubyte)[])) {
2068         auto ret = m_inputData.get!BSONBinData.rawData;
2069         static if (isStaticArray!T)
2070           return cast(T) ret[0 .. T.length];
2071         else static if (is(T : immutable(char)[]))
2072           return ret;
2073         else
2074           return cast(T) ret.dup;
2075       } else
2076         return m_inputData.get!T();
2077     }
2078 
2079     bool tryReadNull(Traits)() {
2080       if (m_inputData.type == BSON.Type.null_)
2081         return true;
2082       return false;
2083     }
2084 
2085     private static BSON.Type getBSONTypeID(T, bool accept_ao = false)(auto ref T value) @safe {
2086       alias UT = Unqual!T;
2087       BSON.Type tp;
2088       static if (is(T == BSON))
2089         tp = value.type;
2090       else static if (is(UT == JSON))
2091         tp = JSONTypeToBSONType(value.type);
2092       else static if (is(UT == typeof(null)))
2093         tp = BSON.Type.null_;
2094       else static if (is(UT == string))
2095         tp = BSON.Type..string;
2096       else static if (is(UT == BSONBinData))
2097         tp = BSON.Type.binData;
2098       else static if (is(UT == BSONObjectID))
2099         tp = BSON.Type.objectID;
2100       else static if (is(UT == BSONDate))
2101         tp = BSON.Type.date;
2102       else static if (is(UT == SysTime))
2103         tp = BSON.Type.date;
2104       else static if (is(UT == BSONRegex))
2105         tp = BSON.Type.regex;
2106       else static if (is(UT == BSONTimestamp))
2107         tp = BSON.Type.timestamp;
2108       else static if (is(UT == bool))
2109         tp = BSON.Type.bool_;
2110       else static if (isIntegral!UT && is(UT : int))
2111         tp = BSON.Type.int_;
2112       else static if (isIntegral!UT && is(UT : long))
2113         tp = BSON.Type.long_;
2114       else static if (isFloatingPoint!UT && is(UT : double))
2115         tp = BSON.Type.double_;
2116       else static if (isBSONSerializable!UT)
2117         tp = value.toBSON().type; // FIXME: this is highly inefficient
2118       else static if (isJSONSerializable!UT)
2119         tp = JSONTypeToBSONType(value.toJSON().type); // FIXME: this is highly inefficient
2120       else static if (is(UT == UUID))
2121         tp = BSON.Type.binData;
2122       else static if (is(UT : const(ubyte)[]))
2123         tp = BSON.Type.binData;
2124       else static if (accept_ao && isArray!UT)
2125         tp = BSON.Type.array;
2126       else static if (accept_ao && isAssociativeArray!UT)
2127         tp = BSON.Type.object;
2128       else static if (accept_ao && (is(UT == class) || is(UT == struct)))
2129         tp = BSON.Type.object;
2130       else
2131         static assert(false, "Unsupported type: " ~ UT.stringof);
2132       return tp;
2133     }
2134   }
2135 
2136   private BSON.Type JSONTypeToBSONType(JSON.Type tp) @safe {
2137     static immutable BSON.Type[JSON.Type.max + 1] JSONIDToBSONID = [
2138       BSON.Type.undefined, BSON.Type.null_, BSON.Type.bool_, BSON.Type.long_,
2139       BSON.Type.long_, BSON.Type.double_, BSON.Type..string, BSON.Type.array,
2140       BSON.Type.object
2141     ];
2142     return JSONIDToBSONID[tp];
2143   }
2144 
2145   private BSON.Type writeBSON(R)(ref R dst, in JSON value)
2146       if (isOutputRange!(R, ubyte)) {
2147     final switch (value.type) {
2148     case JSON.Type.undefined:
2149       return BSON.Type.undefined;
2150     case JSON.Type.null_:
2151       return BSON.Type.null_;
2152     case JSON.Type.bool_:
2153       dst.put(cast(ubyte)(cast(bool) value ? 0x01 : 0x00));
2154       return BSON.Type.bool_;
2155     case JSON.Type.int_:
2156       dst.put(toBSONData(cast(long) value));
2157       return BSON.Type.long_;
2158     case JSON.Type.bigInt:
2159       dst.put(toBSONData(cast(long) value));
2160       return BSON.Type.long_;
2161     case JSON.Type.float_:
2162       dst.put(toBSONData(cast(double) value));
2163       return BSON.Type.double_;
2164     case JSON.Type..string:
2165       dst.put(toBSONData(cast(uint) value.length + 1));
2166       dst.put(cast(bdata_t) cast(string) value);
2167       dst.put(cast(ubyte) 0);
2168       return BSON.Type..string;
2169     case JSON.Type.array:
2170       auto app = appender!bdata_t();
2171       foreach (size_t i, ref const JSON v; value) {
2172         app.put(cast(ubyte)(JSONTypeToBSONType(v.type)));
2173         putCString(app, to!string(i));
2174         writeBSON(app, v);
2175       }
2176 
2177       dst.put(toBSONData(cast(int)(app.data.length + int.sizeof + 1)));
2178       dst.put(app.data);
2179       dst.put(cast(ubyte) 0);
2180       return BSON.Type.array;
2181     case JSON.Type.object:
2182       auto app = appender!bdata_t();
2183       foreach (string k, ref const JSON v; value) {
2184         app.put(cast(ubyte)(JSONTypeToBSONType(v.type)));
2185         putCString(app, k);
2186         writeBSON(app, v);
2187       }
2188 
2189       dst.put(toBSONData(cast(int)(app.data.length + int.sizeof + 1)));
2190       dst.put(app.data);
2191       dst.put(cast(ubyte) 0);
2192       return BSON.Type.object;
2193     }
2194   }
2195 
2196   unittest {
2197     JSON jsvalue = parseJSONString("{\"key\" : \"Value\"}");
2198     assert(serializeToBSON(jsvalue).toJSON() == jsvalue);
2199 
2200     jsvalue = parseJSONString("{\"key\" : [{\"key\" : \"Value\"}, {\"key2\" : \"Value2\"}] }");
2201     assert(serializeToBSON(jsvalue).toJSON() == jsvalue);
2202 
2203     jsvalue = parseJSONString("[ 1 , 2 , 3]");
2204     assert(serializeToBSON(jsvalue).toJSON() == jsvalue);
2205   }
2206 
2207   unittest {
2208     static struct Pipeline(ARGS...) {
2209       @asArray ARGS pipeline;
2210     }
2211 
2212     auto getPipeline(ARGS...)(ARGS args) {
2213       return Pipeline!ARGS(args);
2214     }
2215 
2216     string[string] a = ["foo" : "bar"];
2217     int b = 42;
2218 
2219     auto fields = getPipeline(a, b).serializeToBSON()["pipeline"].get!(BSON[]);
2220     assert(fields[0]["foo"].get!string == "bar");
2221     assert(fields[1].get!int == 42);
2222   }
2223 
2224   private string skipCString(ref bdata_t data) @safe {
2225     auto idx = data.countUntil(0);
2226     enforce(idx >= 0, "Unterminated BSON C-string.");
2227     auto ret = data[0 .. idx];
2228     data = data[idx + 1 .. $];
2229     return cast(string) ret;
2230   }
2231 
2232   private void putCString(R)(ref R dst, string str) {
2233     dst.put(cast(bdata_t) str);
2234     dst.put(cast(ubyte) 0);
2235   }
2236 
2237   ubyte[] toBSONData(T)(T v) {
2238     if (__ctfe)
2239       return nativeToLittleEndian(v).dup;
2240     else {
2241       static ubyte[T.sizeof] ret;
2242       ret = nativeToLittleEndian(v);
2243       return ret;
2244     }
2245   }
2246 
2247   T fromBSONData(T)(in ubyte[] v) {
2248     assert(v.length >= T.sizeof);
2249 
2250     ubyte[T.sizeof] vu = v[0 .. T.sizeof];
2251     return littleEndianToNative!T(vu);
2252   }
2253 
2254   ubyte[] toBigEndianData(T)(T v) {
2255     if (__ctfe)
2256       return nativeToBigEndian(v).dup;
2257     else {
2258       static ubyte[T.sizeof] ret;
2259       ret = nativeToBigEndian(v);
2260       return ret;
2261     }
2262   }
2263 
2264   private string underscoreStrip(string field_name) pure @safe {
2265     if (field_name.length < 1 || field_name[$ - 1] != '_')
2266       return field_name;
2267     else
2268       return field_name[0 .. $ - 1];
2269   }
2270 
2271   /// private
2272   package template isBSONSerializable(T) {
2273     enum isBSONSerializable = is(typeof(T.init.toBSON()) : BSON)
2274       && is(typeof(T.fromBSON(BSON())) : T);
2275   }