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