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