2 +----------------------------------------------------------------------+
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-2014 Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 2.00 of the Zend license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.zend.com/license/2_00.txt. |
12 | If you did not receive a copy of the Zend license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@zend.com so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/zend-pack.h"
19 #include "hphp/runtime/base/complex-types.h"
20 #include "hphp/runtime/base/type-conversions.h"
21 #include "hphp/runtime/base/builtin-functions.h"
25 #define INC_OUTPUTPOS(a,b) \
26 if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
27 throw_invalid_argument \
28 ("Type %c: integer overflow in format string", code); \
33 ///////////////////////////////////////////////////////////////////////////////
35 ZendPack::ZendPack() {
36 int machine_endian_check
= 1;
39 machine_little_endian
= ((char *)&machine_endian_check
)[0];
41 if (machine_little_endian
) {
42 /* Where to get lo to hi bytes from */
45 for (i
= 0; i
< (int)sizeof(int); i
++) {
49 machine_endian_short_map
[0] = 0;
50 machine_endian_short_map
[1] = 1;
51 big_endian_short_map
[0] = 1;
52 big_endian_short_map
[1] = 0;
53 little_endian_short_map
[0] = 0;
54 little_endian_short_map
[1] = 1;
56 machine_endian_int32_map
[0] = 0;
57 machine_endian_int32_map
[1] = 1;
58 machine_endian_int32_map
[2] = 2;
59 machine_endian_int32_map
[3] = 3;
60 big_endian_int32_map
[0] = 3;
61 big_endian_int32_map
[1] = 2;
62 big_endian_int32_map
[2] = 1;
63 big_endian_int32_map
[3] = 0;
64 little_endian_int32_map
[0] = 0;
65 little_endian_int32_map
[1] = 1;
66 little_endian_int32_map
[2] = 2;
67 little_endian_int32_map
[3] = 3;
69 int size
= sizeof(int32_t);
71 /* Where to get hi to lo bytes from */
72 byte_map
[0] = size
- 1;
74 for (i
= 0; i
< (int)sizeof(int); i
++) {
75 int_map
[i
] = size
- (sizeof(int) - i
);
78 machine_endian_short_map
[0] = size
- 2;
79 machine_endian_short_map
[1] = size
- 1;
80 big_endian_short_map
[0] = size
- 2;
81 big_endian_short_map
[1] = size
- 1;
82 little_endian_short_map
[0] = size
- 1;
83 little_endian_short_map
[1] = size
- 2;
85 machine_endian_int32_map
[0] = size
- 4;
86 machine_endian_int32_map
[1] = size
- 3;
87 machine_endian_int32_map
[2] = size
- 2;
88 machine_endian_int32_map
[3] = size
- 1;
89 big_endian_int32_map
[0] = size
- 4;
90 big_endian_int32_map
[1] = size
- 3;
91 big_endian_int32_map
[2] = size
- 2;
92 big_endian_int32_map
[3] = size
- 1;
93 little_endian_int32_map
[0] = size
- 1;
94 little_endian_int32_map
[1] = size
- 2;
95 little_endian_int32_map
[2] = size
- 3;
96 little_endian_int32_map
[3] = size
- 4;
100 void ZendPack::pack(CVarRef val
, int size
, int *map
, char *output
) {
101 int32_t n
= val
.toInt32();
103 for (int i
= 0; i
< size
; i
++) {
104 *output
++ = v
[map
[i
]];
108 Variant
ZendPack::pack(const String
& fmt
, CArrRef argv
) {
109 /* Preprocess format into formatcodes and formatargs */
110 std::vector
<char> formatcodes
;
111 std::vector
<int> formatargs
;
112 int argc
= argv
.size();
114 const char *format
= fmt
.c_str();
115 int formatlen
= fmt
.size();
117 for (int i
= 0; i
< formatlen
; ) {
118 char code
= format
[i
++];
121 /* Handle format arguments if any */
129 else if (c
>= '0' && c
<= '9') {
130 arg
= atoi(&format
[i
]);
132 while (format
[i
] >= '0' && format
[i
] <= '9' && i
< formatlen
) {
138 /* Handle special arg '*' for all codes and check argv overflows */
139 switch ((int) code
) {
140 /* Never uses any args */
145 throw_invalid_argument("Type %c: '*' ignored", code
);
150 /* Always uses one arg */
155 if (currentarg
>= argc
) {
156 throw_invalid_argument("Type %c: not enough arguments", code
);
161 arg
= argv
[currentarg
].toString().size();
167 /* Use as many args as specified */
183 arg
= argc
- currentarg
;
188 if (currentarg
> argc
) {
189 throw_invalid_argument("Type %c: too few arguments", code
);
195 throw_invalid_argument("Type %c: unknown format code", code
);
199 formatcodes
.push_back(code
);
200 formatargs
.push_back(arg
);
203 if (currentarg
< argc
) {
204 throw_invalid_argument("%d arguments unused", (argc
- currentarg
));
207 int outputpos
= 0, outputsize
= 0;
208 /* Calculate output length and upper bound while processing*/
209 for (int i
= 0; i
< (int)formatcodes
.size(); i
++) {
210 int code
= (int) formatcodes
[i
];
211 int arg
= formatargs
[i
];
213 switch ((int) code
) {
216 INC_OUTPUTPOS((arg
+ (arg
% 2)) / 2,1); /* 4 bit per arg */
224 INC_OUTPUTPOS(arg
,1); /* 8 bit per arg */
231 INC_OUTPUTPOS(arg
,2); /* 16 bit per arg */
236 INC_OUTPUTPOS(arg
,sizeof(int));
243 INC_OUTPUTPOS(arg
,4); /* 32 bit per arg */
247 INC_OUTPUTPOS(arg
,sizeof(float));
251 INC_OUTPUTPOS(arg
,sizeof(double));
258 throw_invalid_argument("Type %c: outside of string", code
);
268 if (outputsize
< outputpos
) {
269 outputsize
= outputpos
;
273 String s
= String(outputsize
, ReserveString
);
274 char *output
= s
.bufferSlice().ptr
;
278 /* Do actual packing */
279 for (int i
= 0; i
< (int)formatcodes
.size(); i
++) {
280 int code
= (int) formatcodes
[i
];
281 int arg
= formatargs
[i
];
286 switch ((int) code
) {
289 memset(&output
[outputpos
], (code
== 'a') ? '\0' : ' ', arg
);
290 val
= argv
[currentarg
++].toString();
293 memcpy(&output
[outputpos
], s
, (slen
< arg
) ? slen
: arg
);
299 int nibbleshift
= (code
== 'h') ? 0 : 4;
303 val
= argv
[currentarg
++].toString();
308 throw_invalid_argument
309 ("Type %c: not enough characters in string", code
);
316 if (n
>= '0' && n
<= '9') {
318 } else if (n
>= 'A' && n
<= 'F') {
320 } else if (n
>= 'a' && n
<= 'f') {
323 throw_invalid_argument("Type %c: illegal hex digit %c", code
, n
);
328 output
[++outputpos
] = 0;
333 output
[outputpos
] |= (n
<< nibbleshift
);
334 nibbleshift
= (nibbleshift
+ 4) & 7;
344 pack(argv
[currentarg
++], 1, byte_map
, &output
[outputpos
]);
353 int *map
= machine_endian_short_map
;
356 map
= big_endian_short_map
;
357 } else if (code
== 'v') {
358 map
= little_endian_short_map
;
362 pack(argv
[currentarg
++], 2, map
, &output
[outputpos
]);
371 pack(argv
[currentarg
++], sizeof(int), int_map
, &output
[outputpos
]);
372 outputpos
+= sizeof(int);
380 int *map
= machine_endian_int32_map
;
383 map
= big_endian_int32_map
;
384 } else if (code
== 'V') {
385 map
= little_endian_int32_map
;
389 pack(argv
[currentarg
++], 4, map
, &output
[outputpos
]);
399 v
= argv
[currentarg
++].toDouble();
400 memcpy(&output
[outputpos
], &v
, sizeof(v
));
401 outputpos
+= sizeof(v
);
410 v
= argv
[currentarg
++].toDouble();
411 memcpy(&output
[outputpos
], &v
, sizeof(v
));
412 outputpos
+= sizeof(v
);
418 memset(&output
[outputpos
], '\0', arg
);
431 if (arg
> outputpos
) {
432 memset(&output
[outputpos
], '\0', arg
- outputpos
);
439 return s
.setSize(outputpos
);
442 int32_t ZendPack::unpack(const char *data
, int size
, int issigned
, int *map
) {
444 char *cresult
= (char *) &result
;
447 result
= issigned
? -1 : 0;
449 for (i
= 0; i
< size
; i
++) {
450 cresult
[map
[i
]] = *data
++;
456 Variant
ZendPack::unpack(const String
& fmt
, const String
& data
) {
457 const char *format
= fmt
.c_str();
458 int formatlen
= fmt
.size();
459 const char *input
= data
.c_str();
460 int inputlen
= data
.size();
464 while (formatlen
-- > 0) {
465 char type
= *(format
++);
472 /* Handle format arguments if any */
476 if (c
>= '0' && c
<= '9') {
479 while (formatlen
> 0 && *format
>= '0' && *format
<= '9') {
483 } else if (c
== '*') {
490 /* Get of new value in array */
494 while (formatlen
> 0 && *format
!= '/') {
499 namelen
= format
- name
;
504 switch ((int) type
) {
505 /* Never use any input */
522 size
= (arg
> 0) ? (arg
+ (arg
% 2)) / 2 : arg
;
526 /* Use 1 byte of input */
533 /* Use 2 bytes of input */
541 /* Use sizeof(int) bytes of input */
547 /* Use 4 bytes of input */
555 /* Use sizeof(float) bytes of input */
557 size
= sizeof(float);
560 /* Use sizeof(double) bytes of input */
562 size
= sizeof(double);
566 throw_invalid_argument("Invalid format type %c", type
);
570 /* Do actual unpacking */
571 for (int i
= 0; i
!= arg
; i
++ ) {
572 /* Space for name + number, safe as namelen is ensured <= 200 */
575 if (arg
!= 1 || namelen
== 0) {
576 /* Need to add element number to name */
577 snprintf(n
, sizeof(n
), "%.*s%d", namelen
, name
, i
+ 1);
579 /* Truncate name to next format code or end of string */
580 snprintf(n
, sizeof(n
), "%.*s", namelen
, name
);
583 if (size
!= 0 && size
!= -1 && INT_MAX
- size
+ 1 < inputpos
) {
584 throw_invalid_argument("Type %c: integer overflow", type
);
588 if ((inputpos
+ size
) <= inputlen
) {
589 switch ((int) type
) {
592 char pad
= (type
== 'a') ? '\0' : ' ';
593 int len
= inputlen
- inputpos
; /* Remaining string */
595 /* If size was given take minimum of len and size */
596 if ((size
>= 0) && (len
> size
)) {
602 /* Remove padding chars from unpacked data */
604 if (input
[inputpos
+ len
] != pad
)
608 ret
.set(String(n
, CopyString
),
609 String(input
+ inputpos
, len
+ 1, CopyString
));
615 int len
= (inputlen
- inputpos
) * 2; /* Remaining */
616 int nibbleshift
= (type
== 'h') ? 0 : 4;
621 /* If size was given take minimum of len and size */
622 if (size
>= 0 && len
> (size
* 2)) {
630 String s
= String(len
, ReserveString
);
631 buf
= s
.bufferSlice().ptr
;
633 for (ipos
= opos
= 0; opos
< len
; opos
++) {
634 char c
= (input
[inputpos
+ ipos
] >> nibbleshift
) & 0xf;
643 nibbleshift
= (nibbleshift
+ 4) & 7;
652 ret
.set(String(n
, CopyString
), s
);
658 int issigned
= (type
== 'c') ? (input
[inputpos
] & 0x80) : 0;
659 ret
.set(String(n
, CopyString
),
660 unpack(&input
[inputpos
], 1, issigned
, byte_map
));
669 int *map
= machine_endian_short_map
;
672 issigned
= input
[inputpos
+ (machine_little_endian
? 1 : 0)] &
674 } else if (type
== 'n') {
675 map
= big_endian_short_map
;
676 } else if (type
== 'v') {
677 map
= little_endian_short_map
;
680 ret
.set(String(n
, CopyString
),
681 unpack(&input
[inputpos
], 2, issigned
, map
));
691 issigned
= input
[inputpos
+ (machine_little_endian
?
692 (sizeof(int) - 1) : 0)] & 0x80;
693 } else if (sizeof(int32_t) > 4 &&
694 (input
[inputpos
+ machine_endian_int32_map
[3]]
699 v
|= unpack(&input
[inputpos
], sizeof(int), issigned
, int_map
);
701 ret
.set(String(n
, CopyString
), v
);
703 uint64_t u64
= uint32_t(v
);
704 ret
.set(String(n
, CopyString
), u64
);
714 int *map
= machine_endian_int32_map
;
717 if (type
== 'l' || type
== 'L') {
718 issigned
= input
[inputpos
+ (machine_little_endian
? 3 : 0)]
720 } else if (type
== 'N') {
721 issigned
= input
[inputpos
] & 0x80;
722 map
= big_endian_int32_map
;
723 } else if (type
== 'V') {
724 issigned
= input
[inputpos
+ 3] & 0x80;
725 map
= little_endian_int32_map
;
728 if (sizeof(int32_t) > 4 && issigned
) {
732 v
|= unpack(&input
[inputpos
], 4, issigned
, map
);
734 ret
.set(String(n
, CopyString
), v
);
736 uint64_t u64
= uint32_t(v
);
737 ret
.set(String(n
, CopyString
), u64
);
745 memcpy(&v
, &input
[inputpos
], sizeof(float));
746 ret
.set(String(n
, CopyString
), (double)v
);
753 memcpy(&v
, &input
[inputpos
], sizeof(double));
754 ret
.set(String(n
, CopyString
), v
);
759 /* Do nothing with input, just skip it */
763 if (inputpos
< size
) {
765 i
= arg
- 1; /* Break out of for loop */
768 throw_invalid_argument("Type %c: outside of string", type
);
774 if (arg
<= inputlen
) {
777 throw_invalid_argument("Type %c: outside of string", type
);
780 i
= arg
- 1; /* Done, break out of for loop */
786 if (size
!= -1) { /* only print warning if not working with * */
787 throw_invalid_argument("Type %c: outside of string", type
);
791 } else if (arg
< 0) {
792 /* Reached end of input for '*' repeater */
795 throw_invalid_argument
796 ("Type %c: not enough input, need %d, have %d",
797 type
, size
, inputlen
- inputpos
);
802 formatlen
--; /* Skip '/' separator, does no harm if inputlen == 0 */
809 ///////////////////////////////////////////////////////////////////////////////