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