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 }