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 }