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); }