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