1 // Written in the D programming language. 2 3 module msgpack.common; 4 5 import msgpack.attribute; 6 7 import std.typetuple; // will use std.meta 8 import std.traits; 9 10 11 // for Converting Endian using ntohs and ntohl; 12 version(Windows) 13 { 14 package import core.sys.windows.winsock2; 15 } 16 else 17 { 18 package import core.sys.posix.arpa.inet; 19 } 20 21 22 version(EnableReal) 23 { 24 package enum EnableReal = true; 25 } 26 else 27 { 28 package enum EnableReal = false; 29 } 30 31 32 static if (real.sizeof == double.sizeof) { 33 // for 80bit real inter-operation on non-x86 CPU 34 version = NonX86; 35 36 package import std.numeric; 37 } 38 39 40 @trusted: 41 public: 42 43 44 /** 45 * $(D ExtValue) is a $(D MessagePack) Extended value representation. 46 * The application is responsible for correctly interpreting $(D data) according 47 * to the type described by $(D type). 48 */ 49 struct ExtValue 50 { 51 byte type; /// An integer 0-127 with application-defined meaning 52 ubyte[] data; /// The raw bytes 53 } 54 55 56 /** 57 * MessagePack type-information format 58 * 59 * See_Also: 60 * $(LINK2 http://redmine.msgpack.org/projects/msgpack/wiki/FormatSpec, MessagePack Specificaton) 61 */ 62 enum Format : ubyte 63 { 64 // unsinged integer 65 UINT8 = 0xcc, // ubyte 66 UINT16 = 0xcd, // ushort 67 UINT32 = 0xce, // uint 68 UINT64 = 0xcf, // ulong 69 70 // signed integer 71 INT8 = 0xd0, // byte 72 INT16 = 0xd1, // short 73 INT32 = 0xd2, // int 74 INT64 = 0xd3, // long 75 76 // floating point 77 FLOAT = 0xca, // float 78 DOUBLE = 0xcb, // double 79 80 // raw byte 81 RAW = 0xa0, 82 RAW16 = 0xda, 83 RAW32 = 0xdb, 84 85 // bin type 86 BIN8 = 0xc4, 87 BIN16 = 0xc5, 88 BIN32 = 0xc6, 89 90 // ext type 91 EXT = 0xd4, // fixext 1/2/4/8/16 92 EXT8 = 0xc7, 93 EXT16 = 0xc8, 94 EXT32 = 0xc9, 95 96 // str type 97 STR8 = 0xd9, 98 //STR16 = 0xda, 99 //STR32 = 0xdb, 100 101 // array 102 ARRAY = 0x90, 103 ARRAY16 = 0xdc, 104 ARRAY32 = 0xdd, 105 106 // map 107 MAP = 0x80, 108 MAP16 = 0xde, 109 MAP32 = 0xdf, 110 111 // other 112 NIL = 0xc0, // null 113 TRUE = 0xc3, 114 FALSE = 0xc2, 115 116 // real (This format is D only!) 117 REAL = 0xd4 118 } 119 120 121 package: 122 123 124 /** 125 * For float type serialization / deserialization 126 */ 127 union _f 128 { 129 float f; 130 uint i; 131 } 132 133 134 /** 135 * For double type serialization / deserialization 136 */ 137 union _d 138 { 139 double f; 140 ulong i; 141 } 142 143 144 /** 145 * For real type serialization / deserialization 146 * 147 * 80-bit real is padded to 12 bytes(Linux) and 16 bytes(Mac). 148 * http://lists.puremagic.com/pipermail/digitalmars-d/2010-June/077394.html 149 */ 150 union _r 151 { 152 real f; 153 154 struct 155 { 156 ulong fraction; 157 ushort exponent; // includes sign 158 } 159 } 160 161 enum RealSize = 10; // Real size is 80bit 162 163 164 /** 165 * Detects whether $(D_PARAM T) is a built-in byte type. 166 */ 167 template isByte(T) 168 { 169 enum isByte = staticIndexOf!(Unqual!T, byte, ubyte) >= 0; 170 } 171 172 173 unittest 174 { 175 static assert(isByte!(byte)); 176 static assert(isByte!(const(byte))); 177 static assert(isByte!(ubyte)); 178 static assert(isByte!(immutable(ubyte))); 179 static assert(!isByte!(short)); 180 static assert(!isByte!(char)); 181 static assert(!isByte!(string)); 182 } 183 184 185 /** 186 * Gets asterisk string from pointer type 187 */ 188 template AsteriskOf(T) 189 { 190 static if (is(T P == U*, U)) 191 enum AsteriskOf = "*" ~ AsteriskOf!U; 192 else 193 enum AsteriskOf = ""; 194 } 195 196 197 /** 198 * Get the number of member to serialize. 199 */ 200 template SerializingMemberNumbers(Classes...) 201 { 202 static if (Classes.length == 0) 203 enum SerializingMemberNumbers = 0; 204 else 205 enum SerializingMemberNumbers = Filter!(isPackedField, Classes[0].tupleof).length + SerializingMemberNumbers!(Classes[1..$]); 206 } 207 208 209 /** 210 * Get derived classes with serialization-order 211 */ 212 template SerializingClasses(T) 213 { 214 // There is no information in Object type. Currently disable Object serialization. 215 static if (is(T == Object)) 216 static assert(false, "Object type serialization doesn't support yet. Please define toMsgpack/fromMsgpack and use cast"); 217 else 218 alias TypeTuple!(Reverse!(Erase!(Object, BaseClassesTuple!(T))), T) SerializingClasses; 219 } 220 221 222 /** 223 * Get a field name of class or struct. 224 */ 225 template getFieldName(Type, size_t i) 226 { 227 import std.conv : text; 228 229 static assert((is(Unqual!Type == class) || is(Unqual!Type == struct)), "Type must be class or struct: type = " ~ Type.stringof); 230 static assert(i < Type.tupleof.length, text(Type.stringof, " has ", Type.tupleof.length, " attributes: given index = ", i)); 231 232 enum getFieldName = __traits(identifier, Type.tupleof[i]); 233 } 234 235 236 version (LittleEndian) 237 { 238 /* 239 * Converts $(value) to different Endian. 240 * 241 * Params: 242 * value = the LittleEndian value to convert. 243 * 244 * Returns: 245 * the converted value. 246 */ 247 @trusted 248 ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16) 249 { 250 return ntohs(cast(ushort)value); 251 } 252 253 254 // ditto 255 @trusted 256 uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32) 257 { 258 return ntohl(cast(uint)value); 259 } 260 261 262 // ditto 263 @trusted 264 ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64) 265 { 266 // dmd has convert function? 267 return ((((cast(ulong)value) << 56) & 0xff00000000000000UL) | 268 (((cast(ulong)value) << 40) & 0x00ff000000000000UL) | 269 (((cast(ulong)value) << 24) & 0x0000ff0000000000UL) | 270 (((cast(ulong)value) << 8) & 0x000000ff00000000UL) | 271 (((cast(ulong)value) >> 8) & 0x00000000ff000000UL) | 272 (((cast(ulong)value) >> 24) & 0x0000000000ff0000UL) | 273 (((cast(ulong)value) >> 40) & 0x000000000000ff00UL) | 274 (((cast(ulong)value) >> 56) & 0x00000000000000ffUL)); 275 } 276 277 278 unittest 279 { 280 assert(convertEndianTo!16(0x0123) == 0x2301); 281 assert(convertEndianTo!32(0x01234567) == 0x67452301); 282 assert(convertEndianTo!64(0x0123456789abcdef) == 0xefcdab8967452301); 283 } 284 285 286 /* 287 * Comapatible for BigEndian environment. 288 */ 289 ubyte take8from(size_t bit = 8, T)(T value) 290 { 291 static if (bit == 8 || bit == 16 || bit == 32 || bit == 64) 292 return (cast(ubyte*)&value)[0]; 293 else 294 static assert(false, bit.stringof ~ " is not support bit width."); 295 } 296 297 298 unittest 299 { 300 foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) { 301 assert(take8from!8 (cast(Integer)0x01) == 0x01); 302 assert(take8from!16(cast(Integer)0x0123) == 0x23); 303 assert(take8from!32(cast(Integer)0x01234567) == 0x67); 304 assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef); 305 } 306 } 307 } 308 else 309 { 310 /* 311 * Comapatible for LittleEndian environment. 312 */ 313 @safe 314 ushort convertEndianTo(size_t Bit, T)(in T value) if (Bit == 16) 315 { 316 return cast(ushort)value; 317 } 318 319 320 // ditto 321 @safe 322 uint convertEndianTo(size_t Bit, T)(in T value) if (Bit == 32) 323 { 324 return cast(uint)value; 325 } 326 327 328 // ditto 329 @safe 330 ulong convertEndianTo(size_t Bit, T)(in T value) if (Bit == 64) 331 { 332 return cast(ulong)value; 333 } 334 335 336 unittest 337 { 338 assert(convertEndianTo!16(0x0123) == 0x0123); 339 assert(convertEndianTo!32(0x01234567) == 0x01234567); 340 assert(convertEndianTo!64(0x0123456789) == 0x0123456789); 341 } 342 343 344 /* 345 * Takes 8bit from $(D_PARAM value) 346 * 347 * Params: 348 * value = the content to take. 349 * 350 * Returns: 351 * the 8bit value corresponding $(D_PARAM bit) width. 352 */ 353 ubyte take8from(size_t bit = 8, T)(T value) 354 { 355 static if (bit == 8) 356 return (cast(ubyte*)&value)[0]; 357 else static if (bit == 16) 358 return (cast(ubyte*)&value)[1]; 359 else static if (bit == 32) 360 return (cast(ubyte*)&value)[3]; 361 else static if (bit == 64) 362 return (cast(ubyte*)&value)[7]; 363 else 364 static assert(false, bit.stringof ~ " is not support bit width."); 365 } 366 367 368 unittest 369 { 370 foreach (Integer; TypeTuple!(ubyte, ushort, uint, ulong)) { 371 assert(take8from!8 (cast(Integer)0x01) == 0x01); 372 assert(take8from!16(cast(Integer)0x0123) == 0x23); 373 assert(take8from!32(cast(Integer)0x01234567) == 0x67); 374 assert(take8from!64(cast(Integer)0x0123456789abcdef) == 0xef); 375 } 376 } 377 } 378 379 380 /* 381 * Loads $(D_PARAM T) type value from $(D_PARAM buffer). 382 * 383 * Params: 384 * buffer = the serialized contents. 385 * 386 * Returns: 387 * the Endian-converted value. 388 */ 389 T load16To(T)(ubyte[] buffer) 390 { 391 return cast(T)(convertEndianTo!16(*cast(ushort*)buffer.ptr)); 392 } 393 394 395 // ditto 396 T load32To(T)(ubyte[] buffer) 397 { 398 return cast(T)(convertEndianTo!32(*cast(uint*)buffer.ptr)); 399 } 400 401 402 // ditto 403 T load64To(T)(ubyte[] buffer) 404 { 405 return cast(T)(convertEndianTo!64(*cast(ulong*)buffer.ptr)); 406 } 407 408 409 version (D_Ddoc) 410 { 411 /** 412 * Internal buffer and related operations for Unpacker 413 * 414 * Following Unpackers mixin this template. So, Unpacker can use following methods. 415 * 416 * ----- 417 * //buffer image: 418 * +-------------------------------------------+ 419 * | [object] | [obj | unparsed... | unused... | 420 * +-------------------------------------------+ 421 * ^ offset 422 * ^ current 423 * ^ used 424 * ^ buffer.length 425 * ----- 426 * 427 * This mixin template is a private. 428 */ 429 mixin template InternalBuffer() 430 { 431 private: 432 ubyte[] buffer_; // internal buffer 433 size_t used_; // index that buffer cosumed 434 size_t offset_; // index that buffer parsed 435 size_t parsed_; // total size of parsed message 436 bool hasRaw_; // indicates whether Raw object has been deserialized 437 438 439 public: 440 /** 441 * Forwards to internal buffer. 442 * 443 * Returns: 444 * the reference of internal buffer. 445 */ 446 @property @safe 447 nothrow ubyte[] buffer(); 448 449 450 /** 451 * Fills internal buffer with $(D_PARAM target). 452 * 453 * Params: 454 * target = new serialized buffer to deserialize. 455 */ 456 @safe void feed(in ubyte[] target); 457 458 459 /** 460 * Consumes buffer. This method is helper for buffer property. 461 * You must use this method if you write bytes to buffer directly. 462 * 463 * Params: 464 * size = the number of consuming. 465 */ 466 @safe 467 nothrow void bufferConsumed(in size_t size); 468 469 470 /** 471 * Removes unparsed buffer. 472 */ 473 @safe 474 nothrow void removeUnparsed(); 475 476 477 /** 478 * Returns: 479 * the total size including unparsed buffer size. 480 */ 481 @property @safe 482 nothrow size_t size() const; 483 484 485 /** 486 * Returns: 487 * the parsed size of buffer. 488 */ 489 @property @safe 490 nothrow size_t parsedSize() const; 491 492 493 /** 494 * Returns: 495 * the unparsed size of buffer. 496 */ 497 @property @safe 498 nothrow size_t unparsedSize() const; 499 500 501 private: 502 @safe 503 void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192); 504 } 505 } 506 else 507 { 508 mixin template InternalBuffer() 509 { 510 private: 511 ubyte[] buffer_; // internal buffer 512 size_t used_; // index that buffer cosumed 513 size_t offset_; // index that buffer parsed 514 size_t parsed_; // total size of parsed message 515 bool hasRaw_; // indicates whether Raw object has been deserialized 516 517 518 public: 519 @property @safe 520 nothrow ubyte[] buffer() 521 { 522 return buffer_; 523 } 524 525 526 @safe 527 void feed(in ubyte[] target) 528 in 529 { 530 assert(target.length); 531 } 532 body 533 { 534 /* 535 * Expands internal buffer. 536 * 537 * Params: 538 * size = new buffer size to append. 539 */ 540 void expandBuffer(in size_t size) 541 { 542 // rewinds buffer(completed deserialization) 543 if (used_ == offset_ && !hasRaw_) { 544 used_ = offset_ = 0; 545 546 if (buffer_.length < size) 547 buffer_.length = size; 548 549 return; 550 } 551 552 // deserializing state is mid-flow(buffer has non-parsed data yet) 553 auto unparsed = buffer_[offset_..used_]; 554 auto restSize = buffer_.length - used_ + offset_; 555 auto newSize = size > restSize ? unparsedSize + size : buffer_.length; 556 557 if (hasRaw_) { 558 hasRaw_ = false; 559 buffer_ = new ubyte[](newSize); 560 } else { 561 buffer_.length = newSize; 562 563 // avoids overlapping copy 564 auto area = buffer_[0..unparsedSize]; 565 unparsed = area.overlap(unparsed) ? unparsed.dup : unparsed; 566 } 567 568 buffer_[0..unparsedSize] = unparsed[]; 569 used_ = unparsedSize; 570 offset_ = 0; 571 } 572 573 const size = target.length; 574 575 // lacks current buffer? 576 if (buffer_.length - used_ < size) 577 expandBuffer(size); 578 579 buffer_[used_..used_ + size] = target[]; 580 used_ += size; 581 } 582 583 584 @safe 585 nothrow void bufferConsumed(in size_t size) 586 { 587 if (used_ + size > buffer_.length) 588 used_ = buffer_.length; 589 else 590 used_ += size; 591 } 592 593 594 @safe 595 nothrow void removeUnparsed() 596 { 597 used_ = offset_; 598 } 599 600 601 @property @safe 602 nothrow size_t size() const 603 { 604 return parsed_ - offset_ + used_; 605 } 606 607 608 @property @safe 609 nothrow size_t parsedSize() const 610 { 611 return parsed_; 612 } 613 614 615 @property @safe 616 nothrow size_t unparsedSize() const 617 { 618 return used_ - offset_; 619 } 620 621 622 private: 623 @safe 624 nothrow void initializeBuffer(in ubyte[] target, in size_t bufferSize = 8192) 625 { 626 const size = target.length; 627 628 buffer_ = new ubyte[](size > bufferSize ? size : bufferSize); 629 used_ = size; 630 buffer_[0..size] = target[]; 631 } 632 } 633 }