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