1 /**
2 	BSON serialization and value handling.
3 
4 	Copyright: © 2012 RejectedSoftware e.K.
5 	License: Subject to the terms of the MIT license, as written in the included LICENSE.txt file.
6 	Authors: Sönke Ludwig
7 */
8 module vson.bson;
9 
10 public import vson.json;
11 
12 import std.algorithm;
13 import std.array;
14 import std.base64;
15 import std.bitmanip;
16 import std.conv;
17 import std.datetime;
18 import std.exception;
19 import std.range;
20 import std.traits;
21 
22 
23 alias bdata_t = immutable(ubyte)[];
24 
25 /**
26 	Represents a BSON value.
27 
28 
29 */
30 struct Bson {
31 	/// Represents the type of a BSON value
32 	enum Type : ubyte {
33 		end        = 0x00,  /// End marker - should never occur explicitly
34 		double_    = 0x01,  /// A 64-bit floating point value
35 		string     = 0x02,  /// A UTF-8 string
36 		object     = 0x03,  /// An object aka. dictionary of string to Bson
37 		array      = 0x04,  /// An array of BSON values
38 		binData    = 0x05,  /// Raw binary data (ubyte[])
39 		undefined  = 0x06,  /// Deprecated
40 		objectID   = 0x07,  /// BSON Object ID (96-bit)
41 		bool_      = 0x08,  /// Boolean value
42 		date       = 0x09,  /// Date value (UTC)
43 		null_      = 0x0A,  /// Null value
44 		regex      = 0x0B,  /// Regular expression
45 		dbRef      = 0x0C,  /// Deprecated
46 		code       = 0x0D,  /// JaveScript code
47 		symbol     = 0x0E,  /// Symbol/variable name
48 		codeWScope = 0x0F,  /// JavaScript code with scope
49 		int_       = 0x10,  /// 32-bit integer
50 		timestamp  = 0x11,  /// Timestamp value
51 		long_      = 0x12,  /// 64-bit integer
52 		minKey     = 0xff,  /// Internal value
53 		maxKey     = 0x7f,  /// Internal value
54 
55 		End = end,                /// Compatibility alias - will be deprecated soon.
56 		Double = double_,         /// Compatibility alias - will be deprecated soon.
57 		String = string,          /// Compatibility alias - will be deprecated soon.
58 		Object = object,          /// Compatibility alias - will be deprecated soon.
59 		Array = array,            /// Compatibility alias - will be deprecated soon.
60 		BinData = binData,        /// Compatibility alias - will be deprecated soon.
61 		Undefined = undefined,    /// Compatibility alias - will be deprecated soon.
62 		ObjectID = objectID,      /// Compatibility alias - will be deprecated soon.
63 		Bool = bool_,             /// Compatibility alias - will be deprecated soon.
64 		Date = date,              /// Compatibility alias - will be deprecated soon.
65 		Null = null_,             /// Compatibility alias - will be deprecated soon.
66 		Regex = regex,            /// Compatibility alias - will be deprecated soon.
67 		DBRef = dbRef,            /// Compatibility alias - will be deprecated soon.
68 		Code = code,              /// Compatibility alias - will be deprecated soon.
69 		Symbol = symbol,          /// Compatibility alias - will be deprecated soon.
70 		CodeWScope = codeWScope,  /// Compatibility alias - will be deprecated soon.
71 		Int = int_,               /// Compatibility alias - will be deprecated soon.
72 		Timestamp = timestamp,    /// Compatibility alias - will be deprecated soon.
73 		Long = long_,             /// Compatibility alias - will be deprecated soon.
74 		MinKey = minKey,          /// Compatibility alias - will be deprecated soon.
75 		MaxKey = maxKey           /// Compatibility alias - will be deprecated soon.
76 	}
77 
78 	/// Returns a new, empty Bson value of type Object.
79 	static @property Bson emptyObject() { return Bson(cast(Bson[string])null); }
80 
81 	/// Returns a new, empty Bson value of type Array.
82 	static @property Bson emptyArray() { return Bson(cast(Bson[])null); }
83 
84 	/// Deprecated compatibility alias.
85 	deprecated("Please use emptyObject instead.")
86 	alias EmptyObject = emptyObject;
87 	/// Deprecated compatibility alias.
88 	deprecated("Please use emptyArray instead.")
89 	alias EmptyArray = emptyArray;
90 
91 	private {
92 		Type m_type = Type.undefined;
93 		bdata_t m_data;
94 	}
95 
96 	/**
97 		Creates a new BSON value using raw data.
98 
99 		A slice of the first bytes of 'data' is stored, containg the data related to the value. An
100 		exception is thrown if 'data' is too short.
101 	*/
102 	this(Type type, bdata_t data)
103 	{
104 		m_type = type;
105 		m_data = data;
106 		final switch(type){
107 			case Type.end: m_data = null; break;
108 			case Type.double_: m_data = m_data[0 .. 8]; break;
109 			case Type..string: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break;
110 			case Type.object: m_data = m_data[0 .. fromBsonData!int(m_data)]; break;
111 			case Type.array: m_data = m_data[0 .. fromBsonData!int(m_data)]; break;
112 			case Type.binData: m_data = m_data[0 .. 5 + fromBsonData!int(m_data)]; break;
113 			case Type.undefined: m_data = null; break;
114 			case Type.objectID: m_data = m_data[0 .. 12]; break;
115 			case Type.bool_: m_data = m_data[0 .. 1]; break;
116 			case Type.date: m_data = m_data[0 .. 8]; break;
117 			case Type.null_: m_data = null; break;
118 			case Type.regex:
119 				auto tmp = m_data;
120     			tmp.skipCString();
121     			tmp.skipCString();
122     			m_data = m_data[0 .. $ - tmp.length];
123     			break;
124 			case Type.dbRef: m_data = m_data[0 .. 0]; assert(false, "Not implemented.");
125 			case Type.code: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break;
126 			case Type.symbol: m_data = m_data[0 .. 4 + fromBsonData!int(m_data)]; break;
127 			case Type.codeWScope: m_data = m_data[0 .. 0]; assert(false, "Not implemented.");
128 			case Type.int_: m_data = m_data[0 .. 4]; break;
129 			case Type.timestamp: m_data = m_data[0 .. 8]; break;
130 			case Type.long_: m_data = m_data[0 .. 8]; break;
131 			case Type.minKey: m_data = null; break;
132 			case Type.maxKey: m_data = null; break;
133 		}
134 	}
135 
136 	/**
137 		Initializes a new BSON value from the given D type.
138 	*/
139 	this(double value) { opAssign(value); }
140 	/// ditto
141 	this(string value, Type type = Type..string)
142 	{
143 		assert(type == Type..string || type == Type.code || type == Type.symbol);
144 		opAssign(value);
145 		m_type = type;
146 	}
147 	/// ditto
148 	this(in Bson[string] value) { opAssign(value); }
149 	/// ditto
150 	this(in Bson[] value) { opAssign(value); }
151 	/// ditto
152 	this(in BsonBinData value) { opAssign(value); }
153 	/// ditto
154 	this(in BsonObjectID value) { opAssign(value); }
155 	/// ditto
156 	this(bool value) { opAssign(value); }
157 	/// ditto
158 	this(in BsonDate value) { opAssign(value); }
159 	/// ditto
160 	this(typeof(null)) { opAssign(null); }
161 	/// ditto
162 	this(in BsonRegex value) { opAssign(value); }
163 	/// ditto
164 	this(int value) { opAssign(value); }
165 	/// ditto
166 	this(in BsonTimestamp value) { opAssign(value); }
167 	/// ditto
168 	this(long value) { opAssign(value); }
169 	/// ditto
170 	this(in Json value) { opAssign(value); }
171 
172 	/**
173 		Assigns a D type to a BSON value.
174 	*/
175 	void opAssign(in Bson other)
176 	{
177 		m_data = other.m_data;
178 		m_type = other.m_type;
179 	}
180 	/// ditto
181 	void opAssign(double value)
182 	{
183 		m_data = toBsonData(value).idup;
184 		m_type = Type.double_;
185 	}
186 	/// ditto
187 	void opAssign(string value)
188 	{
189 		import std.utf;
190 		debug std.utf.validate(value);
191 		auto app = appender!bdata_t();
192 		app.put(toBsonData(cast(int)value.length+1));
193 		app.put(cast(bdata_t)value);
194 		app.put(cast(ubyte)0);
195 		m_data = app.data;
196 		m_type = Type..string;
197 	}
198 	/// ditto
199 	void opAssign(in Bson[string] value)
200 	{
201 		auto app = appender!bdata_t();
202 		foreach( k, ref v; value ){
203 			app.put(cast(ubyte)v.type);
204 			putCString(app, k);
205 			app.put(v.data);
206 		}
207 
208 		auto dapp = appender!bdata_t();
209 		dapp.put(toBsonData(cast(int)app.data.length+5));
210 		dapp.put(app.data);
211 		dapp.put(cast(ubyte)0);
212 		m_data = dapp.data;
213 		m_type = Type.object;
214 	}
215 	/// ditto
216 	void opAssign(in Bson[] value)
217 	{
218 		auto app = appender!bdata_t();
219 		foreach( i, ref v; value ){
220 			app.put(v.type);
221 			putCString(app, to!string(i));
222 			app.put(v.data);
223 		}
224 
225 		auto dapp = appender!bdata_t();
226 		dapp.put(toBsonData(cast(int)app.data.length+5));
227 		dapp.put(app.data);
228 		dapp.put(cast(ubyte)0);
229 		m_data = dapp.data;
230 		m_type = Type.array;
231 	}
232 	/// ditto
233 	void opAssign(in BsonBinData value)
234 	{
235 		auto app = appender!bdata_t();
236 		app.put(toBsonData(cast(int)value.rawData.length));
237 		app.put(value.type);
238 		app.put(value.rawData);
239 
240 		m_data = app.data;
241 		m_type = Type.binData;
242 	}
243 	/// ditto
244 	void opAssign(in BsonObjectID value)
245 	{
246 		m_data = value.m_bytes.idup;
247 		m_type = Type.objectID;
248 	}
249 	/// ditto
250 	void opAssign(bool value)
251 	{
252 		m_data = [value ? 0x01 : 0x00];
253 		m_type = Type.bool_;
254 	}
255 	/// ditto
256 	void opAssign(in BsonDate value)
257 	{
258 		m_data = toBsonData(value.m_time).idup;
259 		m_type = Type.date;
260 	}
261 	/// ditto
262 	void opAssign(typeof(null))
263 	{
264 		m_data = null;
265 		m_type = Type.null_;
266 	}
267 	/// ditto
268 	void opAssign(in BsonRegex value)
269 	{
270 		auto app = appender!bdata_t();
271 		putCString(app, value.expression);
272 		putCString(app, value.options);
273 		m_data = app.data;
274 		m_type = type.regex;
275 	}
276 	/// ditto
277 	void opAssign(int value)
278 	{
279 		m_data = toBsonData(value).idup;
280 		m_type = Type.int_;
281 	}
282 	/// ditto
283 	void opAssign(in BsonTimestamp value)
284 	{
285 		m_data = toBsonData(value.m_time).idup;
286 		m_type = Type.timestamp;
287 	}
288 	/// ditto
289 	void opAssign(long value)
290 	{
291 		m_data = toBsonData(value).idup;
292 		m_type = Type.long_;
293 	}
294 	/// ditto
295 	void opAssign(in Json value)
296 	{
297 		auto app = appender!bdata_t();
298 		m_type = writeBson(app, value);
299 		m_data = app.data;
300 	}
301 
302 	/**
303 		Returns the BSON type of this value.
304 	*/
305 	@property Type type() const { return m_type; }
306 
307 	bool isNull() const { return m_type == Type.null_; }
308 
309 	/**
310 		Returns the raw data representing this BSON value (not including the field name and type).
311 	*/
312 	@property bdata_t data() const { return m_data; }
313 
314 	/**
315 		Converts the BSON value to a D value.
316 
317 		If the BSON type of the value does not match the D type, an exception is thrown.
318 	*/
319 	T opCast(T)() const { return get!T(); }
320 	/// ditto
321 	@property T get(T)()
322 	const {
323 		static if( is(T == double) ){ checkType(Type.double_); return fromBsonData!double(m_data); }
324 		else static if( is(T == string) ){
325 			checkType(Type..string, Type.code, Type.symbol);
326 			return cast(string)m_data[4 .. 4+fromBsonData!int(m_data)-1];
327 		}
328 		else static if( is(Unqual!T == Bson[string]) || is(Unqual!T == const(Bson)[string]) ){
329 			checkType(Type.object);
330 			Bson[string] ret;
331 			auto d = m_data[4 .. $];
332 			while( d.length > 0 ){
333 				auto tp = cast(Type)d[0];
334 				if( tp == Type.end ) break;
335 				d = d[1 .. $];
336 				auto key = skipCString(d);
337 				auto value = Bson(tp, d);
338 				d = d[value.data.length .. $];
339 
340 				ret[key] = value;
341 			}
342 			return cast(T)ret;
343 		}
344 		else static if( is(Unqual!T == Bson[]) || is(Unqual!T == const(Bson)[]) ){
345 			checkType(Type.array);
346 			Bson[] ret;
347 			auto d = m_data[4 .. $];
348 			while( d.length > 0 ){
349 				auto tp = cast(Type)d[0];
350 				if( tp == Type.end ) break;
351 				/*auto key = */skipCString(d); // should be '0', '1', ...
352 				auto value = Bson(tp, d);
353 				d = d[value.data.length .. $];
354 
355 				ret ~= value;
356 			}
357 			return cast(T)ret;
358 		}
359 		else static if( is(T == BsonBinData) ){
360 			checkType(Type.binData);
361 			auto size = fromBsonData!int(m_data);
362 			auto type = cast(BsonBinData.Type)m_data[4];
363 			return BsonBinData(type, m_data[5 .. 5+size]);
364 		}
365 		else static if( is(T == BsonObjectID) ){ checkType(Type.objectID); return BsonObjectID(m_data[0 .. 12]); }
366 		else static if( is(T == bool) ){ checkType(Type.bool_); return m_data[0] != 0; }
367 		else static if( is(T == BsonDate) ){ checkType(Type.date); return BsonDate(fromBsonData!long(m_data)); }
368 		else static if( is(T == BsonRegex) ){
369 			checkType(Type.regex);
370 			auto d = m_data;
371 			auto expr = skipCString(d);
372 			auto options = skipCString(d);
373 			return BsonRegex(expr, options);
374 		}
375 		else static if( is(T == int) ){ checkType(Type.int_); return fromBsonData!int(m_data); }
376 		else static if( is(T == BsonTimestamp) ){ checkType(Type.timestamp); return BsonTimestamp(fromBsonData!long(m_data)); }
377 		else static if( is(T == long) ){ checkType(Type.long_); return fromBsonData!long(m_data); }
378 		else static if( is(T == Json) ){
379 			pragma(msg, "Bson.get!Json() and Bson.opCast!Json() will soon be removed. Please use Bson.toJson() instead.");
380 			return this.toJson();
381 		}
382 		else static assert(false, "Cannot cast "~typeof(this).stringof~" to '"~T.stringof~"'.");
383 	}
384 
385 	/** Returns the native type for this BSON if it matches the current runtime type.
386 
387 		If the runtime type does not match the given native type, the 'def' parameter is returned
388 		instead.
389 	*/
390 	inout(T) opt(T)(T def = T.init) inout {
391 		if( isNull() ) return def;
392 		try def = cast(T)this;
393 		catch( Exception e ) {}
394 		return def;
395 	}
396 
397 	/** Returns the length of a BSON value of type String, Array, Object or BinData.
398 	*/
399 	@property size_t length() const {
400 		switch( m_type ){
401 			default: enforce(false, "Bson objects of type "~to!string(m_type)~" do not have a length field."); break;
402 			case Type..string, Type.code, Type.symbol: return (cast(string)this).length;
403 			case Type.array: return (cast(const(Bson)[])this).length; // TODO: optimize!
404 			case Type.object: return (cast(const(Bson)[string])this).length; // TODO: optimize!
405 			case Type.binData: assert(false); //return (cast(BsonBinData)this).length; break;
406 		}
407 		assert(false);
408 	}
409 
410 	/** Converts a given JSON value to the corresponding BSON value.
411 	*/
412 	static Bson fromJson(in Json value)
413 	{
414 		auto app = appender!bdata_t();
415 		auto tp = writeBson(app, value);
416 		return Bson(tp, app.data);
417 	}
418 
419 	/** Converts a BSON value to a JSON value.
420 
421 		All BSON types that cannot be exactly represented as JSON, will
422 		be converted to a string.
423 	*/
424 	Json toJson()
425 	const {
426 		switch( this.type ){
427 			default: assert(false);
428 			case Bson.Type.double_: return Json(get!double());
429 			case Bson.Type..string: return Json(get!string());
430 			case Bson.Type.object:
431 				Json[string] ret;
432 				foreach( k, v; get!(Bson[string])() )
433 					ret[k] = v.toJson();
434 				return Json(ret);
435 			case Bson.Type.array:
436 				auto ret = new Json[this.length];
437 				foreach( i, v; get!(Bson[])() )
438 					ret[i] = v.toJson();
439 				return Json(ret);
440 			case Bson.Type.binData: return Json(cast(string)Base64.encode(get!BsonBinData.rawData));
441 			case Bson.Type.objectID: return Json(get!BsonObjectID().toString());
442 			case Bson.Type.bool_: return Json(get!bool());
443 			case Bson.Type.date: return Json(get!BsonDate.toString());
444 			case Bson.Type.null_: return Json(null);
445 			case Bson.Type.regex: assert(false, "TODO");
446 			case Bson.Type.dbRef: assert(false, "TODO");
447 			case Bson.Type.code: return Json(get!string());
448 			case Bson.Type.symbol: return Json(get!string());
449 			case Bson.Type.codeWScope: assert(false, "TODO");
450 			case Bson.Type.int_: return Json(get!int());
451 			case Bson.Type.timestamp: return Json(get!BsonTimestamp().m_time);
452 			case Bson.Type.long_: return Json(get!long());
453 			case Bson.Type.undefined: return Json();
454 		}
455 	}
456 
457 	/** Returns a string representation of this BSON value in JSON format.
458 	*/
459 	string toString()
460 	const {
461 		return toJson().toString();
462 	}
463 
464 	/** Allows accessing fields of a BSON object using [].
465 
466 		Returns a null value if the specified field does not exist.
467 	*/
468 	inout(Bson) opIndex(string idx) inout {
469 		foreach( string key, v; this )
470 			if( key == idx )
471 				return v;
472 		return Bson(null);
473 	}
474 	/// ditto
475 	void opIndexAssign(T)(T value, string idx){
476 		auto newcont = appender!bdata_t();
477 		checkType(Type.object);
478 		auto d = m_data[4 .. $];
479 		while( d.length > 0 ){
480 			auto tp = cast(Type)d[0];
481 			if( tp == Type.end ) break;
482 			d = d[1 .. $];
483 			auto key = skipCString(d);
484 			auto val = Bson(tp, d);
485 			d = d[val.data.length .. $];
486 
487 			if( key != idx ){
488 				// copy to new array
489 				newcont.put(cast(ubyte)tp);
490 				putCString(newcont, key);
491 				newcont.put(val.data);
492 			}
493 		}
494 
495 		static if( is(T == Bson) )
496 			alias bval = value;
497 		else
498 			auto bval = Bson(value);
499 
500 		newcont.put(cast(ubyte)bval.type);
501 		putCString(newcont, idx);
502 		newcont.put(bval.data);
503 
504 		auto newdata = appender!bdata_t();
505 		newdata.put(toBsonData(cast(uint)(newcont.data.length + 5)));
506 		newdata.put(newcont.data);
507 		newdata.put(cast(ubyte)0);
508 		m_data = newdata.data;
509 	}
510 
511 	/** Allows index based access of a BSON array value.
512 
513 		Returns a null value if the index is out of bounds.
514 	*/
515 	inout(Bson) opIndex(size_t idx) inout {
516 		foreach( size_t i, v; this )
517 			if( i == idx )
518 				return v;
519 		return Bson(null);
520 	}
521 
522 	/**
523 		Allows foreach iterating over BSON objects and arrays.
524 
525 		Note that although D requires to provide a 'ref' argument for
526 		opApply, in-place editing of the array/object fields is not possible.
527 		Any modification attempty will work on a temporary, even if the
528 		loop variable is declared 'ref'.
529 	*/
530 	int opApply(int delegate(ref Bson obj) del)
531 	const {
532 		checkType(Type.array, Type.object);
533 		if( m_type == Type.array ){
534 			foreach( size_t idx, v; this )
535 				if( auto ret = del(v) )
536 					return ret;
537 			return 0;
538 		} else {
539 			foreach( string idx, v; this )
540 				if( auto ret = del(v) )
541 					return ret;
542 			return 0;
543 		}
544 	}
545 	/// ditto
546 	int opApply(int delegate(ref size_t idx, ref Bson obj) del)
547 	const {
548 		checkType(Type.array);
549 		auto d = m_data[4 .. $];
550 		size_t i = 0;
551 		while( d.length > 0 ){
552 			auto tp = cast(Type)d[0];
553 			if( tp == Type.end ) break;
554 			d = d[1 .. $];
555 			skipCString(d);
556 			auto value = Bson(tp, d);
557 			d = d[value.data.length .. $];
558 
559 			auto icopy = i;
560 			if( auto ret = del(icopy, value) )
561 				return ret;
562 
563 			i++;
564 		}
565 		return 0;
566 	}
567 	/// ditto
568 	int opApply(int delegate(ref string idx, ref Bson obj) del)
569 	const {
570 		checkType(Type.object);
571 		auto d = m_data[4 .. $];
572 		while( d.length > 0 ){
573 			auto tp = cast(Type)d[0];
574 			if( tp == Type.end ) break;
575 			d = d[1 .. $];
576 			auto key = skipCString(d);
577 			auto value = Bson(tp, d);
578 			d = d[value.data.length .. $];
579 
580 			if( auto ret = del(key, value) )
581 				return ret;
582 		}
583 		return 0;
584 	}
585 
586 	/** Allows to access existing fields of a JSON object using dot syntax.
587 
588 		Returns a null value for non-existent fields.
589 	*/
590 	@property inout(Bson) opDispatch(string prop)() inout { return opIndex(prop); }
591 	/// ditto
592 	@property void opDispatch(string prop, T)(T val) { opIndexAssign(val, prop); }
593 
594 	///
595 	bool opEquals(ref const Bson other) const {
596 		if( m_type != other.m_type ) return false;
597 		return m_data == other.m_data;
598 	}
599 	/// ditto
600 	bool opEquals(const Bson other) const {
601 		if( m_type != other.m_type ) return false;
602 		return m_data == other.m_data;
603 	}
604 
605 	private void checkType(in Type[] valid_types...)
606 	const {
607 		foreach( t; valid_types )
608 			if( m_type == t )
609 				return;
610 		throw new Exception("BSON value is type '"~to!string(m_type)~"', expected to be one of "~to!string(valid_types));
611 	}
612 }
613 
614 
615 /**
616 	Represents a BSON binary data value (Bson.Type.binData).
617 */
618 struct BsonBinData {
619 	enum Type : ubyte {
620 		generic = 0x00,
621 		function_ = 0x01,
622 		binaryOld = 0x02,
623 		uuid = 0x03,
624 		md5 = 0x05,
625 		userDefined = 0x80,
626 
627 		Generic = generic,          /// Compatibility alias - will be deprecated soon
628 		Function = function_,       /// Compatibility alias - will be deprecated soon
629 		BinaryOld = binaryOld,      /// Compatibility alias - will be deprecated soon
630 		UUID = uuid,                /// Compatibility alias - will be deprecated soon
631 		MD5 = md5,                  /// Compatibility alias - will be deprecated soon
632 		UserDefined	= userDefined,  /// Compatibility alias - will be deprecated soon
633 	}
634 
635 	private {
636 		Type m_type;
637 		bdata_t m_data;
638 	}
639 
640 	this(Type type, immutable(ubyte)[] data)
641 	{
642 		m_type = type;
643 		m_data = data;
644 	}
645 
646 	@property Type type() const { return m_type; }
647 	@property bdata_t rawData() const { return m_data; }
648 }
649 
650 
651 /**
652 	Represents a BSON object id (Bson.Type.binData).
653 */
654 struct BsonObjectID {
655 	private {
656 		ubyte[12] m_bytes;
657 		static int ms_pid = -1;
658 		static uint ms_inc = 0;
659 		static uint MACHINE_ID = 0;
660 	}
661 
662 	/** Constructs a new object ID from the given raw byte array.
663 	*/
664 	this( in ubyte[] bytes ){
665 		assert(bytes.length == 12);
666 		m_bytes[] = bytes[];
667 	}
668 
669 	/** Creates an on object ID from a string in standard hexa-decimal form.
670 	*/
671 	static BsonObjectID fromString(string str)
672 	{
673 		static const lengthex = new Exception("BSON Object ID string must be 24 characters.");
674 		static const charex = new Exception("Not a valid hex string.");
675 
676 		if (str.length != 24) throw lengthex;
677 		BsonObjectID ret = void;
678 		uint b = 0;
679 		foreach( i, ch; str ){
680 			ubyte n;
681 			if( ch >= '0' && ch <= '9' ) n = cast(ubyte)(ch - '0');
682 			else if( ch >= 'a' && ch <= 'f' ) n = cast(ubyte)(ch - 'a' + 10);
683 			else if( ch >= 'A' && ch <= 'F' ) n = cast(ubyte)(ch - 'F' + 10);
684 			else throw charex;
685 			b <<= 4;
686 			b += n;
687 			if( i % 8 == 7 ){
688 				auto j = i / 8;
689 				ret.m_bytes[j*4 .. (j+1)*4] = toBigEndianData(b)[];
690 				b = 0;
691 			}
692 		}
693 		return ret;
694 	}
695 	/// ditto
696 	alias fromHexString = fromString;
697 
698 	/** Generates a unique object ID.
699 	 *
700 	 *   By default it will use the Clock.currTime(UTC()) as timestamp
701 	 *   which guarantees that BsonObjectIDs are chronologically
702 	 *   sorted.
703 	*/
704 	static BsonObjectID generate(in SysTime time = Clock.currTime(UTC()))
705 	{
706 		import std.datetime;
707 		import std.process;
708 		import std.random;
709 
710 		if( ms_pid == -1 ) ms_pid = thisProcessID;
711 		if( MACHINE_ID == 0 ) MACHINE_ID = uniform(0, 0xffffff);
712 
713 		BsonObjectID ret = void;
714 		ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint)time.toUnixTime())[];
715 		ret.m_bytes[4 .. 7] = toBsonData(MACHINE_ID)[0 .. 3];
716 		ret.m_bytes[7 .. 9] = toBsonData(cast(ushort)ms_pid)[];
717 		ret.m_bytes[9 .. 12] = toBigEndianData(ms_inc++)[1 .. 4];
718 		return ret;
719 	}
720 
721 	/** Creates a pseudo object ID that matches the given date.
722 
723 		This kind of ID can be useful to query a database for items in a certain
724 		date interval using their ID. This works using the property of standard BSON
725 		object IDs that they store their creation date as part of the ID. Note that
726 		this date part is only 32-bit wide and is limited to the same timespan as a
727 		32-bit Unix timestamp.
728 	*/
729 	static BsonObjectID createDateID(in SysTime time)
730 	{
731 		BsonObjectID ret;
732 		ret.m_bytes[0 .. 4] = toBigEndianData(cast(uint)time.toUnixTime())[];
733 		return ret;
734 	}
735 
736 	/** Returns true for any non-zero ID.
737 	*/
738 	@property bool valid() const {
739 		foreach( b; m_bytes )
740 			if( b != 0 )
741 				return true;
742 		return false;
743 	}
744 
745 	/** Extracts the time/date portion of the object ID.
746 
747 		For IDs created using the standard generation algorithm or using createDateID
748 		this will return the associated time stamp.
749 	*/
750 	@property SysTime timeStamp()
751 	{
752 		ubyte[4] tm = m_bytes[0 .. 4];
753 		return SysTime(unixTimeToStdTime(bigEndianToNative!uint(tm)));
754 	}
755 
756 	/** Allows for relational comparison of different IDs.
757 	*/
758 	int opCmp(ref const BsonObjectID other)
759 	const {
760 		import core.stdc..string;
761 		return memcmp(m_bytes.ptr, other.m_bytes.ptr, m_bytes.length);
762 	}
763 
764 	/** Converts the ID to its standard hexa-decimal string representation.
765 	*/
766 	string toString() const {
767 		enum hexdigits = "0123456789abcdef";
768 		auto ret = new char[24];
769 		foreach( i, b; m_bytes ){
770 			ret[i*2+0] = hexdigits[(b >> 4) & 0x0F];
771 			ret[i*2+1] = hexdigits[b & 0x0F];
772 		}
773 		return cast(immutable)ret;
774 	}
775 
776 	ubyte[] opCast() {
777 		return m_bytes;
778 	}
779 }
780 
781 unittest {
782 	auto t0 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime);
783 	auto id = BsonObjectID.generate();
784 	auto t1 = SysTime(Clock.currTime(UTC()).toUnixTime.unixTimeToStdTime);
785 	assert(t0 <= id.timeStamp);
786 	assert(id.timeStamp <= t1);
787 
788 	id = BsonObjectID.generate(t0);
789 	assert(id.timeStamp == t0);
790 
791 	id = BsonObjectID.generate(t1);
792 	assert(id.timeStamp == t1);
793 
794 	immutable dt = DateTime(2014, 07, 31, 19, 14, 55);
795 	id = BsonObjectID.generate(SysTime(dt, UTC()));
796 	assert(id.timeStamp == SysTime(dt, UTC()));
797 }
798 
799 /**
800 	Represents a BSON date value (Bson.Type.date).
801 
802 	BSON date values are stored in UNIX time format, counting the number of
803 	milliseconds from 1970/01/01.
804 */
805 struct BsonDate {
806 	private long m_time; // milliseconds since UTC unix epoch
807 
808 	/** Constructs a BsonDate from the given date value.
809 
810 		The time-zone independent Date and DateTime types are assumed to be in
811 		the local time zone and converted to UTC if tz is left to null.
812 	*/
813 	this(in Date date, immutable TimeZone tz = null) { this(SysTime(date, tz)); }
814 	/// ditto
815 	this(in DateTime date, immutable TimeZone tz = null) { this(SysTime(date, tz)); }
816 	/// ditto
817 	this(in SysTime date) { this(fromStdTime(date.stdTime()).m_time); }
818 
819 	/** Constructs a BsonDate from the given UNIX time.
820 
821 		unix_time needs to be given in milliseconds from 1970/01/01. This is
822 		the native storage format for BsonDate.
823 	*/
824 	this(long unix_time)
825 	{
826 		m_time = unix_time;
827 	}
828 
829 	/** Constructs a BsonDate from the given date/time string in ISO extended format.
830 	*/
831 	static BsonDate fromString(string iso_ext_string) { return BsonDate(SysTime.fromISOExtString(iso_ext_string)); }
832 
833 	/** Constructs a BsonDate from the given date/time in standard time as defined in std.datetime.
834 	*/
835 	static BsonDate fromStdTime(long std_time)
836 	{
837 		enum zero = unixTimeToStdTime(0);
838 		return BsonDate((std_time - zero) / 10_000L);
839 	}
840 
841 	/** The raw unix time value.
842 
843 		This is the native storage/transfer format of a BsonDate.
844 	*/
845 	@property long value() const { return m_time; }
846 	/// ditto
847 	@property void value(long v) { m_time = v; }
848 
849 	/** Returns the date formatted as ISO extended format.
850 	*/
851 	string toString() const { return toSysTime().toISOExtString(); }
852 
853 	/* Converts to a SysTime.
854 	*/
855 	SysTime toSysTime() const {
856 		auto zero = unixTimeToStdTime(0);
857 		return SysTime(zero + m_time * 10_000L, UTC());
858 	}
859 
860 	/** Allows relational and equality comparisons.
861 	*/
862 	bool opEquals(ref const BsonDate other) const { return m_time == other.m_time; }
863 	/// ditto
864 	int opCmp(ref const BsonDate other) const {
865 		if( m_time == other.m_time ) return 0;
866 		if( m_time < other.m_time ) return -1;
867 		else return 1;
868 	}
869 }
870 
871 
872 /**
873 	Represents a BSON timestamp value (Bson.Type.timestamp)
874 */
875 struct BsonTimestamp {
876 	private long m_time;
877 
878 	this( long time ){
879 		m_time = time;
880 	}
881 }
882 
883 
884 /**
885 	Represents a BSON regular expression value (Bson.Type.regex).
886 */
887 struct BsonRegex {
888 	private {
889 		string m_expr;
890 		string m_options;
891 	}
892 
893 	this(string expr, string options)
894 	{
895 		m_expr = expr;
896 		m_options = options;
897 	}
898 
899 	@property string expression() const { return m_expr; }
900 	@property string options() const { return m_options; }
901 }
902 
903 
904 /**
905 	Serializes the given value to BSON.
906 
907 	The following types of values are supported:
908 
909 	$(DL
910 		$(DT Bson)            $(DD Used as-is)
911 		$(DT Json)            $(DD Converted to BSON)
912 		$(DT BsonBinData)     $(DD Converted to Bson.Type.binData)
913 		$(DT BsonObjectID)    $(DD Converted to Bson.Type.objectID)
914 		$(DT BsonDate)        $(DD Converted to Bson.Type.date)
915 		$(DT BsonTimestamp)   $(DD Converted to Bson.Type.timestamp)
916 		$(DT BsonRegex)       $(DD Converted to Bson.Type.regex)
917 		$(DT null)            $(DD Converted to Bson.Type.null_)
918 		$(DT bool)            $(DD Converted to Bson.Type.bool_)
919 		$(DT float, double)   $(DD Converted to Bson.Type.double_)
920 		$(DT short, ushort, int, uint, long, ulong) $(DD Converted to Bson.Type.long_)
921 		$(DT string)          $(DD Converted to Bson.Type.string)
922 		$(DT ubyte[])         $(DD Converted to Bson.Type.binData)
923 		$(DT T[])             $(DD Converted to Bson.Type.array)
924 		$(DT T[string])       $(DD Converted to Bson.Type.object)
925 		$(DT struct)          $(DD Converted to Bson.Type.object)
926 		$(DT class)           $(DD Converted to Bson.Type.object or Bson.Type.null_)
927 	)
928 
929 	All entries of an array or an associative array, as well as all R/W properties and
930 	all fields of a struct/class are recursively serialized using the same rules.
931 
932 	Fields ending with an underscore will have the last underscore stripped in the
933 	serialized output. This makes it possible to use fields with D keywords as their name
934 	by simply appending an underscore.
935 
936 	The following methods can be used to customize the serialization of structs/classes:
937 
938 	---
939 	Bson toBson() const;
940 	static T fromBson(Bson src);
941 
942 	Json toJson() const;
943 	static T fromJson(Json src);
944 
945 	string toString() const;
946 	static T fromString(string src);
947 	---
948 
949 	The methods will have to be defined in pairs. The first pair that is implemented by
950 	the type will be used for serialization (i.e. toBson overrides toJson).
951 */
952 Bson serializeToBson(T)(T value, ubyte[] buffer = null)
953 {
954 	version (VibeOldSerialization) {
955 		return serializeToBsonOld(value);
956 	} else {
957 		return serialize!BsonSerializer(value, buffer);
958 	}
959 }
960 
961 /// private
962 Bson serializeToBsonOld(T)(T value)
963 {
964 	import vson.meta.traits;
965 
966     alias Unqualified = Unqual!T;
967 	static if (is(Unqualified == Bson)) return value;
968 	else static if (is(Unqualified == Json)) return Bson.fromJson(value);
969 	else static if (is(Unqualified == BsonBinData)) return Bson(value);
970 	else static if (is(Unqualified == BsonObjectID)) return Bson(value);
971 	else static if (is(Unqualified == BsonDate)) return Bson(value);
972 	else static if (is(Unqualified == BsonTimestamp)) return Bson(value);
973 	else static if (is(Unqualified == BsonRegex)) return Bson(value);
974 	else static if (is(Unqualified == DateTime)) return Bson(BsonDate(value, UTC()));
975 	else static if (is(Unqualified == SysTime)) return Bson(BsonDate(value));
976 	else static if (is(Unqualified == Date)) return Bson(BsonDate(value, UTC()));
977 	else static if (is(Unqualified == typeof(null))) return Bson(null);
978 	else static if (is(Unqualified == bool)) return Bson(value);
979 	else static if (is(Unqualified == float)) return Bson(cast(double)value);
980 	else static if (is(Unqualified == double)) return Bson(value);
981 	else static if (is(Unqualified : int)) return Bson(cast(int)value);
982 	else static if (is(Unqualified : long)) return Bson(cast(long)value);
983 	else static if (is(Unqualified : string)) return Bson(value);
984 	else static if (is(Unqualified : const(ubyte)[])) return Bson(BsonBinData(BsonBinData.Type.generic, value.idup));
985 	else static if (isArray!T) {
986 		auto ret = new Bson[value.length];
987 		foreach (i; 0 .. value.length)
988 			ret[i] = serializeToBson(value[i]);
989 		return Bson(ret);
990 	} else static if (isAssociativeArray!T) {
991 		Bson[string] ret;
992 		alias TK = KeyType!T;
993 		foreach (key, value; value) {
994 			static if(is(TK == string)) {
995 				ret[key] = serializeToBson(value);
996 			} else static if (is(TK == enum)) {
997 				ret[to!string(key)] = serializeToBson(value);
998 			} else static if (isStringSerializable!(TK)) {
999 				ret[key.toString()] = serializeToBson(value);
1000 			} else static assert("AA key type %s not supported for BSON serialization.");
1001 		}
1002 		return Bson(ret);
1003 	} else static if (isBsonSerializable!Unqualified) {
1004 		return value.toBson();
1005 	} else static if (isJsonSerializable!Unqualified) {
1006 		return Bson.fromJson(value.toJson());
1007 	} else static if (isStringSerializable!Unqualified) {
1008 		return Bson(value.toString());
1009 	} else static if (is(Unqualified == struct)) {
1010 		Bson[string] ret;
1011 		foreach (m; __traits(allMembers, T)) {
1012 			static if (isRWField!(Unqualified, m)) {
1013 				auto mv = __traits(getMember, value, m);
1014 				ret[underscoreStrip(m)] = serializeToBson(mv);
1015 			}
1016 		}
1017 		return Bson(ret);
1018 	} else static if (is(Unqualified == class)) {
1019 		if (value is null) return Bson(null);
1020 		Bson[string] ret;
1021 		foreach (m; __traits(allMembers, T)) {
1022 			static if (isRWField!(Unqualified, m)) {
1023 				auto mv = __traits(getMember, value, m);
1024 				ret[underscoreStrip(m)] = serializeToBson(mv);
1025 			}
1026 		}
1027 		return Bson(ret);
1028 	} else {
1029 		static assert(false, "Unsupported type '"~T.stringof~"' for BSON serialization.");
1030 	}
1031 }
1032 
1033 
1034 
1035 template deserializeBson(T)
1036 {
1037     /**
1038         Deserializes a BSON value into the destination variable.
1039 
1040         The same types as for serializeToBson() are supported and handled inversely.
1041     */
1042     void deserializeBson(ref T dst, Bson src)
1043     {
1044         dst = deserializeBson!T(src);
1045     }
1046     /// ditto
1047     T deserializeBson(Bson src)
1048     {
1049         version (VibeOldSerialization) {
1050             return deserializeBsonOld!T(src);
1051         } else {
1052             return deserialize!(BsonSerializer, T)(src);
1053         }
1054     }
1055 }
1056 
1057 /// private
1058 T deserializeBsonOld(T)(Bson src)
1059 {
1060 	import vson.meta.traits;
1061 
1062 	static if (is(T == Bson)) return src;
1063 	else static if (is(T == Json)) return src.toJson();
1064 	else static if (is(T == BsonBinData)) return cast(T)src;
1065 	else static if (is(T == BsonObjectID)) return cast(T)src;
1066 	else static if (is(T == BsonDate)) return cast(T)src;
1067 	else static if (is(T == BsonTimestamp)) return cast(T)src;
1068 	else static if (is(T == BsonRegex)) return cast(T)src;
1069 	else static if (is(T == SysTime)) return src.get!BsonDate().toSysTime();
1070 	else static if (is(T == DateTime)) return cast(DateTime)src.get!BsonDate().toSysTime();
1071 	else static if (is(T == Date)) return cast(Date)src.get!BsonDate().toSysTime();
1072 	else static if (is(T == typeof(null))) return null;
1073 	else static if (is(T == bool)) return cast(bool)src;
1074 	else static if (is(T == float)) return cast(double)src;
1075 	else static if (is(T == double)) return cast(double)src;
1076 	else static if (is(T : int)) return cast(T)cast(int)src;
1077 	else static if (is(T : long)) return cast(T)cast(long)src;
1078 	else static if (is(T : string)) return cast(T)(cast(string)src);
1079 	else static if (is(T : const(ubyte)[])) return cast(T)src.get!BsonBinData.rawData.dup;
1080 	else static if (isArray!T) {
1081 		alias TV = typeof(T.init[0]) ;
1082 		auto ret = new Unqual!TV[src.length];
1083 		foreach (size_t i, v; cast(Bson[])src)
1084 			ret[i] = deserializeBson!(Unqual!TV)(v);
1085 		return ret;
1086 	} else static if (isAssociativeArray!T) {
1087 		alias TV = typeof(T.init.values[0]) ;
1088 		alias TK = KeyType!T;
1089 		Unqual!TV[TK] dst;
1090 		foreach (string key, value; src) {
1091 			static if (is(TK == string)) {
1092 				dst[key] = deserializeBson!(Unqual!TV)(value);
1093 			} else static if (is(TK == enum)) {
1094 				dst[to!(TK)(key)] = deserializeBson!(Unqual!TV)(value);
1095 			} else static if (isStringSerializable!TK) {
1096 				auto dsk = TK.fromString(key);
1097 				dst[dsk] = deserializeBson!(Unqual!TV)(value);
1098 			} else static assert("AA key type %s not supported for BSON serialization.");
1099 		}
1100 		return dst;
1101 	} else static if (isBsonSerializable!T) {
1102 		return T.fromBson(src);
1103 	} else static if (isJsonSerializable!T) {
1104 		return T.fromJson(src.toJson());
1105 	} else static if (isStringSerializable!T) {
1106 		return T.fromString(cast(string)src);
1107 	} else static if (is(T == struct)) {
1108 		T dst;
1109 		foreach (m; __traits(allMembers, T)) {
1110 			static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
1111 				alias TM = typeof(__traits(getMember, dst, m)) ;
1112 				debug enforce(!src[underscoreStrip(m)].isNull() || is(TM == class) || isPointer!TM || is(TM == typeof(null)),
1113 					"Missing field '"~underscoreStrip(m)~"'.");
1114 				__traits(getMember, dst, m) = deserializeBson!TM(src[underscoreStrip(m)]);
1115 			}
1116 		}
1117 		return dst;
1118 	} else static if (is(T == class)) {
1119 		if (src.isNull()) return null;
1120 		auto dst = new T;
1121 		foreach (m; __traits(allMembers, T)) {
1122 			static if (isRWPlainField!(T, m) || isRWField!(T, m)) {
1123 				alias TM = typeof(__traits(getMember, dst, m)) ;
1124 				__traits(getMember, dst, m) = deserializeBson!TM(src[underscoreStrip(m)]);
1125 			}
1126 		}
1127 		return dst;
1128 	} else static if (isPointer!T) {
1129 		if (src.type == Bson.Type.null_) return null;
1130 		alias TD = typeof(*T.init) ;
1131 		dst = new TD;
1132 		*dst = deserializeBson!TD(src);
1133 		return dst;
1134 	} else {
1135 		static assert(false, "Unsupported type '"~T.stringof~"' for BSON serialization.");
1136 	}
1137 }
1138 
1139 unittest {
1140 	import std.stdio;
1141 	enum Foo : string { k = "test" }
1142 	enum Boo : int { l = 5 }
1143 	static struct S { float a; double b; bool c; int d; string e; byte f; ubyte g; long h; ulong i; float[] j; Foo k; Boo l;}
1144 	immutable S t = {1.5, -3.0, true, int.min, "Test", -128, 255, long.min, ulong.max, [1.1, 1.2, 1.3], Foo.k, Boo.l,};
1145 	S u;
1146 	deserializeBson(u, serializeToBson(t));
1147 	assert(t.a == u.a);
1148 	assert(t.b == u.b);
1149 	assert(t.c == u.c);
1150 	assert(t.d == u.d);
1151 	assert(t.e == u.e);
1152 	assert(t.f == u.f);
1153 	assert(t.g == u.g);
1154 	assert(t.h == u.h);
1155 	assert(t.i == u.i);
1156 	assert(t.j == u.j);
1157 	assert(t.k == u.k);
1158 	assert(t.l == u.l);
1159 }
1160 
1161 unittest
1162 {
1163     assert(uint.max == serializeToBson(uint.max).deserializeBson!uint);
1164     assert(ulong.max == serializeToBson(ulong.max).deserializeBson!ulong);
1165 }
1166 
1167 unittest {
1168 	assert(deserializeBson!SysTime(serializeToBson(SysTime(0))) == SysTime(0));
1169 	assert(deserializeBson!SysTime(serializeToBson(SysTime(0, UTC()))) == SysTime(0, UTC()));
1170 	assert(deserializeBson!Date(serializeToBson(Date.init)) == Date.init);
1171 	assert(deserializeBson!Date(serializeToBson(Date(2001, 1, 1))) == Date(2001, 1, 1));
1172 }
1173 
1174 unittest {
1175 	static struct A { int value; static A fromJson(Json val) { return A(val.get!int); } Json toJson() const { return Json(value); } Bson toBson() { return Bson(); } }
1176 	static assert(!isStringSerializable!A && isJsonSerializable!A && !isBsonSerializable!A);
1177 	static assert(!isStringSerializable!(const(A)) && !isJsonSerializable!(const(A)) && !isBsonSerializable!(const(A)));
1178 //	assert(serializeToBson(const A(123)) == Bson(123));
1179 //	assert(serializeToBson(A(123))       == Bson(123));
1180 
1181 	static struct B { int value; static B fromBson(Bson val) { return B(val.get!int); } Bson toBson() const { return Bson(value); } Json toJson() { return Json(); } }
1182 	static assert(!isStringSerializable!B && !isJsonSerializable!B && isBsonSerializable!B);
1183 	static assert(!isStringSerializable!(const(B)) && !isJsonSerializable!(const(B)) && !isBsonSerializable!(const(B)));
1184 	assert(serializeToBson(const B(123)) == Bson(123));
1185 	assert(serializeToBson(B(123))       == Bson(123));
1186 
1187 	static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } Json toJson() { return Json(); } }
1188 	static assert(isStringSerializable!C && !isJsonSerializable!C && !isBsonSerializable!C);
1189 	static assert(!isStringSerializable!(const(C)) && !isJsonSerializable!(const(C)) && !isBsonSerializable!(const(C)));
1190 	assert(serializeToBson(const C(123)) == Bson("123"));
1191 	assert(serializeToBson(C(123))       == Bson("123"));
1192 
1193 	static struct D { int value; string toString() const { return ""; } }
1194 	static assert(!isStringSerializable!D && !isJsonSerializable!D && !isBsonSerializable!D);
1195 	static assert(!isStringSerializable!(const(D)) && !isJsonSerializable!(const(D)) && !isBsonSerializable!(const(D)));
1196 	assert(serializeToBson(const D(123)) == serializeToBson(["value": 123]));
1197 	assert(serializeToBson(D(123))       == serializeToBson(["value": 123]));
1198 
1199 	// test if const(class) is serializable
1200 	static class E { int value; this(int v) { value = v; } static E fromBson(Bson val) { return new E(val.get!int); } Bson toBson() const { return Bson(value); } Json toJson() { return Json(); } }
1201 	static assert(!isStringSerializable!E && !isJsonSerializable!E && isBsonSerializable!E);
1202 	static assert(!isStringSerializable!(const(E)) && !isJsonSerializable!(const(E)) && !isBsonSerializable!(const(E)));
1203 	assert(serializeToBson(new const E(123)) == Bson(123));
1204 	assert(serializeToBson(new E(123))       == Bson(123));
1205 }
1206 
1207 unittest {
1208 	static struct E { ubyte[4] bytes; ubyte[] more; }
1209 	auto e = E([1, 2, 3, 4], [5, 6]);
1210 	auto eb = serializeToBson(e);
1211 	assert(eb.bytes.type == Bson.Type.binData);
1212 	assert(eb.more.type == Bson.Type.binData);
1213 	assert(e == deserializeBson!E(eb));
1214 }
1215 
1216 unittest {
1217 	static class C {
1218 		int a;
1219 		private int _b;
1220 		@property int b() const { return _b; }
1221 		@property void b(int v) { _b = v; }
1222 
1223 		@property int test() const { return 10; }
1224 
1225 		void test2() {}
1226 	}
1227 	C c = new C;
1228 	c.a = 1;
1229 	c.b = 2;
1230 
1231 	C d;
1232 	deserializeBson(d, serializeToBson(c));
1233 	assert(c.a == d.a);
1234 	assert(c.b == d.b);
1235 
1236 	const(C) e = c; // serialize const class instances (issue #653)
1237 	deserializeBson(d, serializeToBson(e));
1238 	assert(e.a == d.a);
1239 	assert(e.b == d.b);
1240 }
1241 
1242 unittest {
1243 	static struct C { int value; static C fromString(string val) { return C(val.to!int); } string toString() const { return value.to!string; } }
1244 	enum Color { Red, Green, Blue }
1245 	{
1246 		static class T {
1247 			string[Color] enumIndexedMap;
1248 			string[C] stringableIndexedMap;
1249 			this() {
1250 				enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
1251                                 stringableIndexedMap = [ C(42) : "forty-two" ];
1252 			}
1253 		}
1254 
1255 		T original = new T;
1256 		original.enumIndexedMap[Color.Green] = "olive";
1257 		T other;
1258 		deserializeBson(other, serializeToBson(original));
1259 		assert(serializeToBson(other) == serializeToBson(original));
1260 	}
1261 	{
1262 		static struct S {
1263 			string[Color] enumIndexedMap;
1264 			string[C] stringableIndexedMap;
1265 		}
1266 
1267 		S original;
1268 		original.enumIndexedMap = [ Color.Red : "magenta", Color.Blue : "deep blue" ];
1269 		original.enumIndexedMap[Color.Green] = "olive";
1270                 original.stringableIndexedMap = [ C(42) : "forty-two" ];
1271 		S other;
1272 		deserializeBson(other, serializeToBson(original));
1273 		assert(serializeToBson(other) == serializeToBson(original));
1274 	}
1275 }
1276 
1277 unittest {
1278 	ubyte[] data = [1, 2, 3];
1279 	auto bson = serializeToBson(data);
1280 	assert(bson.type == Bson.Type.binData);
1281 	assert(deserializeBson!(ubyte[])(bson) == data);
1282 }
1283 
1284 unittest { // issue #709
1285  	ulong[] data = [2354877787627192443, 1, 2354877787627192442];
1286 	auto bson = Bson.fromJson(serializeToBson(data).toJson);
1287 	assert(deserializeBson!(ulong[])(bson) == data);
1288 }
1289 
1290 unittest { // issue #709
1291  	uint[] data = [1, 2, 3, 4];
1292 	auto bson = Bson.fromJson(serializeToBson(data).toJson);
1293 //	assert(deserializeBson!(uint[])(bson) == data);
1294 	assert(deserializeBson!(ulong[])(bson).equal(data));
1295 }
1296 
1297 unittest {
1298 	import std.typecons;
1299 	Nullable!bool x;
1300 	auto bson = serializeToBson(x);
1301 	assert(bson.type == Bson.Type.null_);
1302 	deserializeBson(x, bson);
1303 	assert(x.isNull);
1304 	x = true;
1305 	bson = serializeToBson(x);
1306 	assert(bson.type == Bson.Type.bool_ && bson.get!bool == true);
1307 	deserializeBson(x, bson);
1308 	assert(x == true);
1309 }
1310 
1311 unittest { // issue #793
1312 	char[] test = "test".dup;
1313 	auto bson = serializeToBson(test);
1314 	//assert(bson.type == Bson.Type.string);
1315 	//assert(bson.get!string == "test");
1316 	assert(bson.type == Bson.Type.array);
1317 	assert(bson[0].type == Bson.Type..string && bson[0].get!string == "t");
1318 }
1319 
1320 
1321 /**
1322 	Serializes to an in-memory BSON representation.
1323 
1324 	See_Also: vson.serialization.serialize, vson.serialization.deserialize, serializeToBson, deserializeBson
1325 */
1326 struct BsonSerializer {
1327 	import vutil.array : AllocAppender;
1328 
1329 	private {
1330 		AllocAppender!(ubyte[]) m_dst;
1331 		size_t[] m_compositeStack;
1332 		Bson.Type m_type = Bson.Type.null_;
1333 		Bson m_inputData;
1334 		string m_entryName;
1335 		size_t m_entryIndex = size_t.max;
1336 	}
1337 
1338 	this(Bson input)
1339 	{
1340 		m_inputData = input;
1341 	}
1342 
1343 	this(ubyte[] buffer)
1344 	{
1345 		import vutil.memory;
1346 		m_dst = AllocAppender!(ubyte[])(defaultAllocator(), buffer);
1347 	}
1348 
1349 	@disable this(this);
1350 
1351 	template isSupportedValueType(T) { enum isSupportedValueType = is(typeof(getBsonTypeID(T.init))); }
1352 
1353 	//
1354 	// serialization
1355 	//
1356 	Bson getSerializedResult()
1357 	{
1358 		auto ret = Bson(m_type, cast(immutable)m_dst.data);
1359 		m_dst.reset();
1360 		m_type = Bson.Type.null_;
1361 		return ret;
1362 	}
1363 
1364 	void beginWriteDictionary(T)()
1365 	{
1366 		writeCompositeEntryHeader(Bson.Type.object);
1367 		m_compositeStack ~= m_dst.data.length;
1368 		m_dst.put(toBsonData(cast(int)0));
1369 	}
1370 	void endWriteDictionary(T)()
1371 	{
1372 		m_dst.put(Bson.Type.end);
1373 		auto sh = m_compositeStack[$-1];
1374 		m_compositeStack.length--;
1375 		m_dst.data[sh .. sh + 4] = toBsonData(cast(uint)(m_dst.data.length - sh))[];
1376 	}
1377 	void beginWriteDictionaryEntry(T)(string name) { m_entryName = name; }
1378 	void endWriteDictionaryEntry(T)(string name) {}
1379 
1380 	void beginWriteArray(T)(size_t)
1381 	{
1382 		writeCompositeEntryHeader(Bson.Type.array);
1383 		m_compositeStack ~= m_dst.data.length;
1384 		m_dst.put(toBsonData(cast(int)0));
1385 	}
1386 	void endWriteArray(T)() { endWriteDictionary!T(); }
1387 	void beginWriteArrayEntry(T)(size_t idx) { m_entryIndex = idx; }
1388 	void endWriteArrayEntry(T)(size_t idx) {}
1389 
1390 	// auto ref does't work for DMD 2.064
1391 	void writeValue(T)(/*auto ref const*/ in T value) { writeValueH!(T, true)(value); }
1392 
1393 	private void writeValueH(T, bool write_header)(/*auto ref const*/ in T value)
1394 	{
1395 		static if (write_header) writeCompositeEntryHeader(getBsonTypeID(value));
1396 
1397 		static if (is(T == Bson)) { m_dst.put(value.data); }
1398 		else static if (is(T == Json)) { m_dst.put(Bson(value).data); } // FIXME: use .writeBsonValue
1399 		else static if (is(T == typeof(null))) {}
1400 		else static if (is(T == string)) { m_dst.put(toBsonData(cast(uint)value.length+1)); m_dst.putCString(value); }
1401 		else static if (is(T == BsonBinData)) { m_dst.put(toBsonData(cast(int)value.rawData.length)); m_dst.put(value.type); m_dst.put(value.rawData); }
1402 		else static if (is(T == BsonObjectID)) { m_dst.put(value.m_bytes[]); }
1403 		else static if (is(T == BsonDate)) { m_dst.put(toBsonData(value.m_time)); }
1404 		else static if (is(T == SysTime)) { m_dst.put(toBsonData(BsonDate(value).m_time)); }
1405 		else static if (is(T == BsonRegex)) { m_dst.putCString(value.expression); m_dst.putCString(value.options); }
1406 		else static if (is(T == BsonTimestamp)) { m_dst.put(toBsonData(value.m_time)); }
1407 		else static if (is(T == bool)) { m_dst.put(cast(ubyte)(value ? 0x01 : 0x00)); }
1408 		else static if (is(T : int) && isIntegral!T) { m_dst.put(toBsonData(cast(int)value)); }
1409 		else static if (is(T : long) && isIntegral!T) { m_dst.put(toBsonData(value)); }
1410 		else static if (is(T : double) && isFloatingPoint!T) { m_dst.put(toBsonData(cast(double)value)); }
1411 		else static if (isBsonSerializable!T) m_dst.put(value.toBson().data);
1412 		else static if (isJsonSerializable!T) m_dst.put(Bson(value.toJson()).data);
1413 		else static if (is(T : const(ubyte)[])) { writeValueH!(BsonBinData, false)(BsonBinData(BsonBinData.Type.generic, value.idup)); }
1414 		else static assert(false, "Unsupported type: " ~ T.stringof);
1415 	}
1416 
1417 	private void writeCompositeEntryHeader(Bson.Type tp)
1418 	{
1419 		if (!m_compositeStack.length) {
1420 			assert(m_type == Bson.Type.null_, "Overwriting root item.");
1421 			m_type = tp;
1422 		}
1423 
1424 		if (m_entryName) {
1425 			m_dst.put(tp);
1426 			m_dst.putCString(m_entryName);
1427 			m_entryName = null;
1428 		} else if (m_entryIndex != size_t.max) {
1429 			import std.format;
1430 			m_dst.put(tp);
1431 			static struct Wrapper {
1432 				AllocAppender!(ubyte[])* app;
1433 				void put(char ch) { (*app).put(ch); }
1434 				void put(in char[] str) { (*app).put(cast(ubyte[])str); }
1435 			}
1436 			auto wr = Wrapper(&m_dst);
1437 			wr.formattedWrite("%d\0", m_entryIndex);
1438 			m_entryIndex = size_t.max;
1439 		}
1440 	}
1441 
1442 	//
1443 	// deserialization
1444 	//
1445 	void readDictionary(T)(scope void delegate(string) entry_callback)
1446 	{
1447 		enforce(m_inputData.type == Bson.Type.object, "Expected object instead of "~m_inputData.type.to!string());
1448 		auto old = m_inputData;
1449 		foreach (string name, value; old) {
1450 			m_inputData = value;
1451 			entry_callback(name);
1452 		}
1453 		m_inputData = old;
1454 	}
1455 
1456 	void readArray(T)(scope void delegate(size_t) size_callback, scope void delegate() entry_callback)
1457 	{
1458 		enforce(m_inputData.type == Bson.Type.array, "Expected array instead of "~m_inputData.type.to!string());
1459 		auto old = m_inputData;
1460 		foreach (value; old) {
1461 			m_inputData = value;
1462 			entry_callback();
1463 		}
1464 		m_inputData = old;
1465 	}
1466 
1467 	T readValue(T)()
1468 	{
1469 		static if (is(T == Bson)) return m_inputData;
1470 		else static if (is(T == Json)) return m_inputData.toJson();
1471 		else static if (is(T == bool)) return m_inputData.get!bool();
1472 		else static if (is(T == uint)) return cast(T)m_inputData.get!int();
1473 		else static if (is(T : int)) {
1474 			if(m_inputData.type == Bson.Type.long_) {
1475 				enforce((m_inputData.get!long() >= int.min) && (m_inputData.get!long() <= int.max), "Long out of range while attempting to deserialize to int: " ~ m_inputData.get!long.to!string);
1476 				return cast(T)m_inputData.get!long();
1477 			}
1478 			else return m_inputData.get!int().to!T;
1479 		}
1480 		else static if (is(T : long)) {
1481 			if(m_inputData.type == Bson.Type.int_) return cast(T)m_inputData.get!int();
1482 			else return cast(T)m_inputData.get!long();
1483 		}
1484 		else static if (is(T : double)) return cast(T)m_inputData.get!double();
1485 		else static if (is(T == SysTime)) {
1486 			// support legacy behavior to serialize as string
1487 			if (m_inputData.type == Bson.Type..string) return SysTime.fromISOExtString(m_inputData.get!string);
1488 			else return m_inputData.get!BsonDate().toSysTime();
1489 		}
1490 		else static if (isBsonSerializable!T) return T.fromBson(readValue!Bson);
1491 		else static if (isJsonSerializable!T) return T.fromJson(readValue!Bson.toJson());
1492 		else static if (is(T : const(ubyte)[])) {
1493 			auto ret = m_inputData.get!BsonBinData.rawData;
1494 			static if (isStaticArray!T) return cast(T)ret[0 .. T.length];
1495 			else static if (is(T : immutable(char)[])) return ret;
1496 			else return cast(T)ret.dup;
1497 		} else return m_inputData.get!T();
1498 	}
1499 
1500 	bool tryReadNull()
1501 	{
1502 		if (m_inputData.type == Bson.Type.null_) return true;
1503 		return false;
1504 	}
1505 
1506 	private static Bson.Type getBsonTypeID(T, bool accept_ao = false)(/*auto ref const*/ in T value)
1507 	{
1508 		Bson.Type tp;
1509 		static if (is(T == Bson)) tp = value.type;
1510 		else static if (is(T == Json)) tp = jsonTypeToBsonType(value.type);
1511 		else static if (is(T == typeof(null))) tp = Bson.Type.null_;
1512 		else static if (is(T == string)) tp = Bson.Type..string;
1513 		else static if (is(T == BsonBinData)) tp = Bson.Type.binData;
1514 		else static if (is(T == BsonObjectID)) tp = Bson.Type.objectID;
1515 		else static if (is(T == BsonDate)) tp = Bson.Type.date;
1516 		else static if (is(T == SysTime)) tp = Bson.Type.date;
1517 		else static if (is(T == BsonRegex)) tp = Bson.Type.regex;
1518 		else static if (is(T == BsonTimestamp)) tp = Bson.Type.timestamp;
1519 		else static if (is(T == bool)) tp = Bson.Type.bool_;
1520 		else static if (isIntegral!T && is(T : int)) tp = Bson.Type.int_;
1521 		else static if (isIntegral!T && is(T : long)) tp = Bson.Type.long_;
1522 		else static if (isFloatingPoint!T && is(T : double)) tp = Bson.Type.double_;
1523 		else static if (isBsonSerializable!T) tp = value.toBson().type; // FIXME: this is highly inefficient
1524 		else static if (isJsonSerializable!T) tp = jsonTypeToBsonType(value.toJson().type); // FIXME: this is highly inefficient
1525 		else static if (is(T : const(ubyte)[])) tp = Bson.Type.binData;
1526 		else static if (accept_ao && isArray!T) tp = Bson.Type.array;
1527 		else static if (accept_ao && isAssociativeArray!T) tp = Bson.Type.object;
1528 		else static if (accept_ao && (is(T == class) || is(T == struct))) tp = Bson.Type.object;
1529 		else static assert(false, "Unsupported type: " ~ T.stringof);
1530 		return tp;
1531 	}
1532 }
1533 
1534 private Bson.Type jsonTypeToBsonType(Json.Type tp)
1535 {
1536 	static immutable Bson.Type[Json.Type.max+1] JsonIDToBsonID = [
1537 		Bson.Type.undefined,
1538 		Bson.Type.null_,
1539 		Bson.Type.bool_,
1540 		Bson.Type.long_,
1541 		Bson.Type.double_,
1542 		Bson.Type..string,
1543 		Bson.Type.array,
1544 		Bson.Type.object
1545 	];
1546 	return JsonIDToBsonID[tp];
1547 }
1548 
1549 private Bson.Type writeBson(R)(ref R dst, in Json value)
1550 	if( isOutputRange!(R, ubyte) )
1551 {
1552 	final switch(value.type){
1553 		case Json.Type.undefined:
1554 			return Bson.Type.undefined;
1555 		case Json.Type.null_:
1556 			return Bson.Type.null_;
1557 		case Json.Type.bool_:
1558 			dst.put(cast(ubyte)(cast(bool)value ? 0x01 : 0x00));
1559 			return Bson.Type.bool_;
1560 		case Json.Type.int_:
1561 			dst.put(toBsonData(cast(long)value));
1562 			return Bson.Type.long_;
1563 		case Json.Type.float_:
1564 			dst.put(toBsonData(cast(double)value));
1565 			return Bson.Type.double_;
1566 		case Json.Type..string:
1567 			dst.put(toBsonData(cast(uint)value.length+1));
1568 			dst.put(cast(bdata_t)cast(string)value);
1569 			dst.put(cast(ubyte)0);
1570 			return Bson.Type..string;
1571 		case Json.Type.array:
1572 			auto app = appender!bdata_t();
1573 			foreach( size_t i, ref const Json v; value ){
1574 				app.put(cast(ubyte)(jsonTypeToBsonType(v.type)));
1575 				putCString(app, to!string(i));
1576 				writeBson(app, v);
1577 			}
1578 
1579             dst.put(toBsonData(cast(int)(app.data.length + int.sizeof + 1)));
1580 			dst.put(app.data);
1581 			dst.put(cast(ubyte)0);
1582 			return Bson.Type.array;
1583 		case Json.Type.object:
1584 			auto app = appender!bdata_t();
1585 			foreach( string k, ref const Json v; value ){
1586 				app.put(cast(ubyte)(jsonTypeToBsonType(v.type)));
1587 				putCString(app, k);
1588 				writeBson(app, v);
1589 			}
1590 
1591 			dst.put(toBsonData(cast(int)(app.data.length + int.sizeof + 1)));
1592 			dst.put(app.data);
1593 			dst.put(cast(ubyte)0);
1594 			return Bson.Type.object;
1595 	}
1596 }
1597 
1598 unittest
1599 {
1600     Json jsvalue = parseJsonString("{\"key\" : \"Value\"}");
1601     assert(serializeToBson(jsvalue).toJson() == jsvalue);
1602 
1603     jsvalue = parseJsonString("{\"key\" : [{\"key\" : \"Value\"}, {\"key2\" : \"Value2\"}] }");
1604     assert(serializeToBson(jsvalue).toJson() == jsvalue);
1605 
1606     jsvalue = parseJsonString("[ 1 , 2 , 3]");
1607     assert(serializeToBson(jsvalue).toJson() == jsvalue);
1608 }
1609 
1610 private string skipCString(ref bdata_t data)
1611 {
1612 	auto idx = data.countUntil(0);
1613 	enforce(idx >= 0, "Unterminated BSON C-string.");
1614 	auto ret = data[0 .. idx];
1615 	data = data[idx+1 .. $];
1616 	return cast(string)ret;
1617 }
1618 
1619 private void putCString(R)(ref R dst, string str)
1620 {
1621 	dst.put(cast(bdata_t)str);
1622 	dst.put(cast(ubyte)0);
1623 }
1624 
1625 ubyte[] toBsonData(T)(T v)
1626 {
1627 	/*static T tmp;
1628 	tmp = nativeToLittleEndian(v);
1629 	return cast(ubyte[])((&tmp)[0 .. 1]);*/
1630 	if (__ctfe) return nativeToLittleEndian(v).dup;
1631 	else {
1632 		static ubyte[T.sizeof] ret;
1633 		ret = nativeToLittleEndian(v);
1634 		return ret;
1635 	}
1636 }
1637 
1638 T fromBsonData(T)(in ubyte[] v)
1639 {
1640 	assert(v.length >= T.sizeof);
1641 	//return (cast(T[])v[0 .. T.sizeof])[0];
1642 	ubyte[T.sizeof] vu = v[0 .. T.sizeof];
1643 	return littleEndianToNative!T(vu);
1644 }
1645 
1646 ubyte[] toBigEndianData(T)(T v)
1647 {
1648 	if (__ctfe) return nativeToBigEndian(v).dup;
1649 	else {
1650 		static ubyte[T.sizeof] ret;
1651 		ret = nativeToBigEndian(v);
1652 		return ret;
1653 	}
1654 }
1655 
1656 private string underscoreStrip(string field_name)
1657 pure {
1658 	if( field_name.length < 1 || field_name[$-1] != '_' ) return field_name;
1659 	else return field_name[0 .. $-1];
1660 }
1661 
1662 /// private
1663 package template isBsonSerializable(T) { enum isBsonSerializable = is(typeof(T.init.toBson()) == Bson) && is(typeof(T.fromBson(Bson())) == T); }