php-internal-docs 8.4.8
Unofficial docs for php/php-src
Loading...
Searching...
No Matches
pack.c
Go to the documentation of this file.
1/*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: Chris Schneider <cschneid@relog.ch> |
14 +----------------------------------------------------------------------+
15 */
16
17#include "php.h"
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <errno.h>
22#include <sys/types.h>
23#include <sys/stat.h>
24#include <fcntl.h>
25#ifdef PHP_WIN32
26#define O_RDONLY _O_RDONLY
27#include "win32/param.h"
28#else
29#include <sys/param.h>
30#endif
31#include "pack.h"
32#ifdef HAVE_PWD_H
33#ifdef PHP_WIN32
34#include "win32/pwd.h"
35#else
36#include <pwd.h>
37#endif
38#endif
39#include "fsock.h"
40#ifdef HAVE_NETINET_IN_H
41#include <netinet/in.h>
42#endif
43
44#define INC_OUTPUTPOS(a,b) \
45 if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
46 efree(formatcodes); \
47 efree(formatargs); \
48 zend_value_error("Type %c: integer overflow in format string", code); \
49 RETURN_THROWS(); \
50 } \
51 outputpos += (a)*(b);
52
53#ifdef WORDS_BIGENDIAN
54#define MACHINE_LITTLE_ENDIAN 0
55#else
56#define MACHINE_LITTLE_ENDIAN 1
57#endif
58
59typedef ZEND_SET_ALIGNED(1, uint16_t unaligned_uint16_t);
60typedef ZEND_SET_ALIGNED(1, uint32_t unaligned_uint32_t);
61typedef ZEND_SET_ALIGNED(1, uint64_t unaligned_uint64_t);
62typedef ZEND_SET_ALIGNED(1, unsigned int unaligned_uint);
63typedef ZEND_SET_ALIGNED(1, int unaligned_int);
64
65/* Mapping of byte from char (8bit) to long for machine endian */
66static int byte_map[1];
67
68/* Mappings of bytes from int (machine dependent) to int for machine endian */
69static int int_map[sizeof(int)];
70
71/* Mappings of bytes from shorts (16bit) for all endian environments */
72static int machine_endian_short_map[2];
73static int big_endian_short_map[2];
74static int little_endian_short_map[2];
75
76/* Mappings of bytes from longs (32bit) for all endian environments */
77static int machine_endian_long_map[4];
78static int big_endian_long_map[4];
79static int little_endian_long_map[4];
80
81#if SIZEOF_ZEND_LONG > 4
82/* Mappings of bytes from quads (64bit) for all endian environments */
83static int machine_endian_longlong_map[8];
84static int big_endian_longlong_map[8];
85static int little_endian_longlong_map[8];
86#endif
87
88/* {{{ php_pack */
89static void php_pack(zval *val, size_t size, int *map, char *output)
90{
91 size_t i;
92 char *v;
93
95 v = (char *) &Z_LVAL_P(val);
96
97 for (i = 0; i < size; i++) {
98 *output++ = v[map[i]];
99 }
100}
101/* }}} */
102
103ZEND_ATTRIBUTE_CONST static inline uint16_t php_pack_reverse_int16(uint16_t arg)
104{
105 return ((arg & 0xFF) << 8) | ((arg >> 8) & 0xFF);
106}
107
108/* {{{ php_pack_reverse_int32 */
109ZEND_ATTRIBUTE_CONST static inline uint32_t php_pack_reverse_int32(uint32_t arg)
110{
111 uint32_t result;
112 result = ((arg & 0xFF) << 24) | ((arg & 0xFF00) << 8) | ((arg >> 8) & 0xFF00) | ((arg >> 24) & 0xFF);
113
114 return result;
115}
116/* }}} */
117
118/* {{{ php_pack */
119static inline uint64_t php_pack_reverse_int64(uint64_t arg)
120{
121 union Swap64 {
122 uint64_t i;
123 uint32_t ul[2];
124 } tmp, result;
125 tmp.i = arg;
126 result.ul[0] = php_pack_reverse_int32(tmp.ul[1]);
127 result.ul[1] = php_pack_reverse_int32(tmp.ul[0]);
128
129 return result.i;
130}
131/* }}} */
132
133/* {{{ php_pack_copy_float */
134static void php_pack_copy_float(int is_little_endian, void * dst, float f)
135{
136 union Copy32 {
137 float f;
138 uint32_t i;
139 } m;
140 m.f = f;
141
142#ifdef WORDS_BIGENDIAN
143 if (is_little_endian) {
144 m.i = php_pack_reverse_int32(m.i);
145 }
146#else /* WORDS_BIGENDIAN */
147 if (!is_little_endian) {
148 m.i = php_pack_reverse_int32(m.i);
149 }
150#endif /* WORDS_BIGENDIAN */
151
152 memcpy(dst, &m.f, sizeof(float));
153}
154/* }}} */
155
156/* {{{ php_pack_copy_double */
157static void php_pack_copy_double(int is_little_endian, void * dst, double d)
158{
159 union Copy64 {
160 double d;
161 uint64_t i;
162 } m;
163 m.d = d;
164
165#ifdef WORDS_BIGENDIAN
166 if (is_little_endian) {
167 m.i = php_pack_reverse_int64(m.i);
168 }
169#else /* WORDS_BIGENDIAN */
170 if (!is_little_endian) {
171 m.i = php_pack_reverse_int64(m.i);
172 }
173#endif /* WORDS_BIGENDIAN */
174
175 memcpy(dst, &m.d, sizeof(double));
176}
177/* }}} */
178
179/* {{{ php_pack_parse_float */
180static float php_pack_parse_float(int is_little_endian, void * src)
181{
182 union Copy32 {
183 float f;
184 uint32_t i;
185 } m;
186 memcpy(&m.i, src, sizeof(float));
187
188#ifdef WORDS_BIGENDIAN
189 if (is_little_endian) {
190 m.i = php_pack_reverse_int32(m.i);
191 }
192#else /* WORDS_BIGENDIAN */
193 if (!is_little_endian) {
194 m.i = php_pack_reverse_int32(m.i);
195 }
196#endif /* WORDS_BIGENDIAN */
197
198 return m.f;
199}
200/* }}} */
201
202/* {{{ php_pack_parse_double */
203static double php_pack_parse_double(int is_little_endian, void * src)
204{
205 union Copy64 {
206 double d;
207 uint64_t i;
208 } m;
209 memcpy(&m.i, src, sizeof(double));
210
211#ifdef WORDS_BIGENDIAN
212 if (is_little_endian) {
213 m.i = php_pack_reverse_int64(m.i);
214 }
215#else /* WORDS_BIGENDIAN */
216 if (!is_little_endian) {
217 m.i = php_pack_reverse_int64(m.i);
218 }
219#endif /* WORDS_BIGENDIAN */
220
221 return m.d;
222}
223/* }}} */
224
225/* pack() idea stolen from Perl (implemented formats behave the same as there except J and P)
226 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
227 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
228 */
229/* {{{ Takes one or more arguments and packs them into a binary string according to the format argument */
231{
232 zval *argv = NULL;
233 int num_args = 0;
234 size_t i;
235 int currentarg;
236 char *format;
237 size_t formatlen;
238 char *formatcodes;
239 int *formatargs;
240 size_t formatcount = 0;
241 int outputpos = 0, outputsize = 0;
242 zend_string *output;
243
245 Z_PARAM_STRING(format, formatlen)
246 Z_PARAM_VARIADIC('*', argv, num_args)
248
249 /* We have a maximum of <formatlen> format codes to deal with */
250 formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
251 formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
252 currentarg = 0;
253
254 /* Preprocess format into formatcodes and formatargs */
255 for (i = 0; i < formatlen; formatcount++) {
256 char code = format[i++];
257 int arg = 1;
258
259 /* Handle format arguments if any */
260 if (i < formatlen) {
261 char c = format[i];
262
263 if (c == '*') {
264 arg = -1;
265 i++;
266 }
267 else if (c >= '0' && c <= '9') {
268 arg = atoi(&format[i]);
269
270 while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
271 i++;
272 }
273 }
274 }
275
276 /* Handle special arg '*' for all codes and check argv overflows */
277 switch ((int) code) {
278 /* Never uses any args */
279 case 'x':
280 case 'X':
281 case '@':
282 if (arg < 0) {
283 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code);
284 arg = 1;
285 }
286 break;
287
288 /* Always uses one arg */
289 case 'a':
290 case 'A':
291 case 'Z':
292 case 'h':
293 case 'H':
294 if (currentarg >= num_args) {
295 efree(formatcodes);
296 efree(formatargs);
297 zend_value_error("Type %c: not enough arguments", code);
299 }
300
301 if (arg < 0) {
302 if (!try_convert_to_string(&argv[currentarg])) {
303 efree(formatcodes);
304 efree(formatargs);
306 }
307
308 arg = Z_STRLEN(argv[currentarg]);
309 if (code == 'Z') {
310 /* add one because Z is always NUL-terminated:
311 * pack("Z*", "aa") === "aa\0"
312 * pack("Z2", "aa") === "a\0" */
313 arg++;
314 }
315 }
316
317 currentarg++;
318 break;
319
320 /* Use as many args as specified */
321 case 'q':
322 case 'Q':
323 case 'J':
324 case 'P':
325#if SIZEOF_ZEND_LONG < 8
326 efree(formatcodes);
327 efree(formatargs);
328 zend_value_error("64-bit format codes are not available for 32-bit versions of PHP");
330#endif
331 case 'c':
332 case 'C':
333 case 's':
334 case 'S':
335 case 'i':
336 case 'I':
337 case 'l':
338 case 'L':
339 case 'n':
340 case 'N':
341 case 'v':
342 case 'V':
343 case 'f': /* float */
344 case 'g': /* little endian float */
345 case 'G': /* big endian float */
346 case 'd': /* double */
347 case 'e': /* little endian double */
348 case 'E': /* big endian double */
349 if (arg < 0) {
350 arg = num_args - currentarg;
351 }
352 if (currentarg > INT_MAX - arg) {
353 goto too_few_args;
354 }
355 currentarg += arg;
356
357 if (currentarg > num_args) {
358too_few_args:
359 efree(formatcodes);
360 efree(formatargs);
361 zend_value_error("Type %c: too few arguments", code);
363 }
364 break;
365
366 default:
367 efree(formatcodes);
368 efree(formatargs);
369 zend_value_error("Type %c: unknown format code", code);
371 }
372
373 formatcodes[formatcount] = code;
374 formatargs[formatcount] = arg;
375 }
376
377 if (currentarg < num_args) {
378 php_error_docref(NULL, E_WARNING, "%d arguments unused", (num_args - currentarg));
379 }
380
381 /* Calculate output length and upper bound while processing*/
382 for (i = 0; i < formatcount; i++) {
383 int code = (int) formatcodes[i];
384 int arg = formatargs[i];
385
386 switch ((int) code) {
387 case 'h':
388 case 'H':
389 INC_OUTPUTPOS((arg + (arg % 2)) / 2,1) /* 4 bit per arg */
390 break;
391
392 case 'a':
393 case 'A':
394 case 'Z':
395 case 'c':
396 case 'C':
397 case 'x':
398 INC_OUTPUTPOS(arg,1) /* 8 bit per arg */
399 break;
400
401 case 's':
402 case 'S':
403 case 'n':
404 case 'v':
405 INC_OUTPUTPOS(arg,2) /* 16 bit per arg */
406 break;
407
408 case 'i':
409 case 'I':
410 INC_OUTPUTPOS(arg,sizeof(int))
411 break;
412
413 case 'l':
414 case 'L':
415 case 'N':
416 case 'V':
417 INC_OUTPUTPOS(arg,4) /* 32 bit per arg */
418 break;
419
420#if SIZEOF_ZEND_LONG > 4
421 case 'q':
422 case 'Q':
423 case 'J':
424 case 'P':
425 INC_OUTPUTPOS(arg,8) /* 32 bit per arg */
426 break;
427#endif
428
429 case 'f': /* float */
430 case 'g': /* little endian float */
431 case 'G': /* big endian float */
432 INC_OUTPUTPOS(arg,sizeof(float))
433 break;
434
435 case 'd': /* double */
436 case 'e': /* little endian double */
437 case 'E': /* big endian double */
438 INC_OUTPUTPOS(arg,sizeof(double))
439 break;
440
441 case 'X':
442 outputpos -= arg;
443
444 if (outputpos < 0) {
445 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", code);
446 outputpos = 0;
447 }
448 break;
449
450 case '@':
451 outputpos = arg;
452 break;
453 }
454
455 if (outputsize < outputpos) {
456 outputsize = outputpos;
457 }
458 }
459
460 output = zend_string_alloc(outputsize, 0);
461 outputpos = 0;
462 currentarg = 0;
463
464 /* Do actual packing */
465 for (i = 0; i < formatcount; i++) {
466 int code = (int) formatcodes[i];
467 int arg = formatargs[i];
468
469 switch ((int) code) {
470 case 'a':
471 case 'A':
472 case 'Z': {
473 size_t arg_cp = (code != 'Z') ? arg : MAX(0, arg - 1);
474 zend_string *tmp_str;
475 zend_string *str = zval_get_tmp_string(&argv[currentarg++], &tmp_str);
476
477 memset(&ZSTR_VAL(output)[outputpos], (code == 'a' || code == 'Z') ? '\0' : ' ', arg);
478 memcpy(&ZSTR_VAL(output)[outputpos], ZSTR_VAL(str),
479 (ZSTR_LEN(str) < arg_cp) ? ZSTR_LEN(str) : arg_cp);
480
481 outputpos += arg;
482 zend_tmp_string_release(tmp_str);
483 break;
484 }
485
486 case 'h':
487 case 'H': {
488 int nibbleshift = (code == 'h') ? 0 : 4;
489 int first = 1;
490 zend_string *tmp_str;
491 zend_string *str = zval_get_tmp_string(&argv[currentarg++], &tmp_str);
492 char *v = ZSTR_VAL(str);
493
494 outputpos--;
495 if ((size_t)arg > ZSTR_LEN(str)) {
496 php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
497 arg = ZSTR_LEN(str);
498 }
499
500 while (arg-- > 0) {
501 char n = *v++;
502
503 if (n >= '0' && n <= '9') {
504 n -= '0';
505 } else if (n >= 'A' && n <= 'F') {
506 n -= ('A' - 10);
507 } else if (n >= 'a' && n <= 'f') {
508 n -= ('a' - 10);
509 } else {
510 php_error_docref(NULL, E_WARNING, "Type %c: illegal hex digit %c", code, n);
511 n = 0;
512 }
513
514 if (first--) {
515 ZSTR_VAL(output)[++outputpos] = 0;
516 } else {
517 first = 1;
518 }
519
520 ZSTR_VAL(output)[outputpos] |= (n << nibbleshift);
521 nibbleshift = (nibbleshift + 4) & 7;
522 }
523
524 outputpos++;
525 zend_tmp_string_release(tmp_str);
526 break;
527 }
528
529 case 'c':
530 case 'C':
531 while (arg-- > 0) {
532 php_pack(&argv[currentarg++], 1, byte_map, &ZSTR_VAL(output)[outputpos]);
533 outputpos++;
534 }
535 break;
536
537 case 's':
538 case 'S':
539 case 'n':
540 case 'v': {
541 int *map = machine_endian_short_map;
542
543 if (code == 'n') {
544 map = big_endian_short_map;
545 } else if (code == 'v') {
546 map = little_endian_short_map;
547 }
548
549 while (arg-- > 0) {
550 php_pack(&argv[currentarg++], 2, map, &ZSTR_VAL(output)[outputpos]);
551 outputpos += 2;
552 }
553 break;
554 }
555
556 case 'i':
557 case 'I':
558 while (arg-- > 0) {
559 php_pack(&argv[currentarg++], sizeof(int), int_map, &ZSTR_VAL(output)[outputpos]);
560 outputpos += sizeof(int);
561 }
562 break;
563
564 case 'l':
565 case 'L':
566 case 'N':
567 case 'V': {
568 int *map = machine_endian_long_map;
569
570 if (code == 'N') {
571 map = big_endian_long_map;
572 } else if (code == 'V') {
573 map = little_endian_long_map;
574 }
575
576 while (arg-- > 0) {
577 php_pack(&argv[currentarg++], 4, map, &ZSTR_VAL(output)[outputpos]);
578 outputpos += 4;
579 }
580 break;
581 }
582
583#if SIZEOF_ZEND_LONG > 4
584 case 'q':
585 case 'Q':
586 case 'J':
587 case 'P': {
588 int *map = machine_endian_longlong_map;
589
590 if (code == 'J') {
591 map = big_endian_longlong_map;
592 } else if (code == 'P') {
593 map = little_endian_longlong_map;
594 }
595
596 while (arg-- > 0) {
597 php_pack(&argv[currentarg++], 8, map, &ZSTR_VAL(output)[outputpos]);
598 outputpos += 8;
599 }
600 break;
601 }
602#endif
603
604 case 'f': {
605 while (arg-- > 0) {
606 float v = (float) zval_get_double(&argv[currentarg++]);
607 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
608 outputpos += sizeof(v);
609 }
610 break;
611 }
612
613 case 'g': {
614 /* pack little endian float */
615 while (arg-- > 0) {
616 float v = (float) zval_get_double(&argv[currentarg++]);
617 php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
618 outputpos += sizeof(v);
619 }
620
621 break;
622 }
623 case 'G': {
624 /* pack big endian float */
625 while (arg-- > 0) {
626 float v = (float) zval_get_double(&argv[currentarg++]);
627 php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
628 outputpos += sizeof(v);
629 }
630 break;
631 }
632
633 case 'd': {
634 while (arg-- > 0) {
635 double v = (double) zval_get_double(&argv[currentarg++]);
636 memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
637 outputpos += sizeof(v);
638 }
639 break;
640 }
641
642 case 'e': {
643 /* pack little endian double */
644 while (arg-- > 0) {
645 double v = (double) zval_get_double(&argv[currentarg++]);
646 php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
647 outputpos += sizeof(v);
648 }
649 break;
650 }
651
652 case 'E': {
653 /* pack big endian double */
654 while (arg-- > 0) {
655 double v = (double) zval_get_double(&argv[currentarg++]);
656 php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
657 outputpos += sizeof(v);
658 }
659 break;
660 }
661
662 case 'x':
663 memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
664 outputpos += arg;
665 break;
666
667 case 'X':
668 outputpos -= arg;
669
670 if (outputpos < 0) {
671 outputpos = 0;
672 }
673 break;
674
675 case '@':
676 if (arg > outputpos) {
677 memset(&ZSTR_VAL(output)[outputpos], '\0', arg - outputpos);
678 }
679 outputpos = arg;
680 break;
681 }
682 }
683
684 efree(formatcodes);
685 efree(formatargs);
686 ZSTR_VAL(output)[outputpos] = '\0';
687 ZSTR_LEN(output) = outputpos;
688 RETURN_NEW_STR(output);
689}
690/* }}} */
691
692/* unpack() is based on Perl's unpack(), but is modified a bit from there.
693 * Rather than depending on error-prone ordered lists or syntactically
694 * unpleasant pass-by-reference, we return an object with named parameters
695 * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
696 * formatter char (like pack()), "[repeat]" is the optional repeater argument,
697 * and "name" is the name of the variable to use.
698 * Example: "c2chars/nints" will return an object with fields
699 * chars1, chars2, and ints.
700 * Numeric pack types will return numbers, a and A will return strings,
701 * f and d will return doubles.
702 * Implemented formats are Z, A, a, h, H, c, C, s, S, i, I, l, L, n, N, q, Q, J, P, f, d, x, X, @.
703 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
704 */
705/* {{{ Unpack binary string into named array elements according to format argument */
707{
708 char *format, *input;
709 zend_string *formatarg, *inputarg;
710 zend_long formatlen, inputpos, inputlen;
711 int i;
712 zend_long offset = 0;
713
715 Z_PARAM_STR(formatarg)
716 Z_PARAM_STR(inputarg)
720
721 format = ZSTR_VAL(formatarg);
722 formatlen = ZSTR_LEN(formatarg);
723 input = ZSTR_VAL(inputarg);
724 inputlen = ZSTR_LEN(inputarg);
725 inputpos = 0;
726
727
728 if (offset < 0 || offset > inputlen) {
729 zend_argument_value_error(3, "must be contained in argument #2 ($data)");
731 }
732
733 input += offset;
734 inputlen -= offset;
735
737
738 while (formatlen-- > 0) {
739 char type = *(format++);
740 char c;
741 int repetitions = 1, argb;
742 char *name;
743 int namelen;
744 int size = 0;
745
746 /* Handle format arguments if any */
747 if (formatlen > 0) {
748 c = *format;
749
750 if (c >= '0' && c <= '9') {
751 errno = 0;
752 long tmp = strtol(format, NULL, 10);
753 /* There is not strtoi. We have to check the range ourselves.
754 * With 32-bit long the INT_{MIN,MAX} are useless because long == int, but with 64-bit they do limit us to 32-bit. */
755 if (errno || tmp < INT_MIN || tmp > INT_MAX) {
756 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
759 }
760 repetitions = tmp;
761
762 while (formatlen > 0 && *format >= '0' && *format <= '9') {
763 format++;
764 formatlen--;
765 }
766 } else if (c == '*') {
767 repetitions = -1;
768 format++;
769 formatlen--;
770 }
771 }
772
773 /* Get of new value in array */
774 name = format;
775 argb = repetitions;
776
777 while (formatlen > 0 && *format != '/') {
778 formatlen--;
779 format++;
780 }
781
782 namelen = format - name;
783
784 if (namelen > 200)
785 namelen = 200;
786
787 switch ((int) type) {
788 /* Never use any input */
789 case 'X':
790 size = -1;
791 if (repetitions < 0) {
792 php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
793 repetitions = 1;
794 }
795 break;
796
797 case '@':
798 size = 0;
799 break;
800
801 case 'a':
802 case 'A':
803 case 'Z':
804 size = repetitions;
805 repetitions = 1;
806 break;
807
808 case 'h':
809 case 'H':
810 size = (repetitions > 0) ? ((unsigned int) repetitions + 1) / 2 : repetitions;
811 repetitions = 1;
812 break;
813
814 /* Use 1 byte of input */
815 case 'c':
816 case 'C':
817 case 'x':
818 size = 1;
819 break;
820
821 /* Use 2 bytes of input */
822 case 's':
823 case 'S':
824 case 'n':
825 case 'v':
826 size = 2;
827 break;
828
829 /* Use sizeof(int) bytes of input */
830 case 'i':
831 case 'I':
832 size = sizeof(int);
833 break;
834
835 /* Use 4 bytes of input */
836 case 'l':
837 case 'L':
838 case 'N':
839 case 'V':
840 size = 4;
841 break;
842
843 /* Use 8 bytes of input */
844 case 'q':
845 case 'Q':
846 case 'J':
847 case 'P':
848#if SIZEOF_ZEND_LONG > 4
849 size = 8;
850 break;
851#else
852 zend_value_error("64-bit format codes are not available for 32-bit versions of PHP");
854#endif
855
856 /* Use sizeof(float) bytes of input */
857 case 'f':
858 case 'g':
859 case 'G':
860 size = sizeof(float);
861 break;
862
863 /* Use sizeof(double) bytes of input */
864 case 'd':
865 case 'e':
866 case 'E':
867 size = sizeof(double);
868 break;
869
870 default:
871 zend_value_error("Invalid format type %c", type);
873 }
874
875
876 /* Do actual unpacking */
877 for (i = 0; i != repetitions; i++ ) {
878
879 if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
880 php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
883 }
884
885 if ((inputpos + size) <= inputlen) {
886
887 zend_string* real_name;
888 zval val;
889
890 if (repetitions == 1 && namelen > 0) {
891 /* Use a part of the formatarg argument directly as the name. */
892 real_name = zend_string_init_fast(name, namelen);
893
894 } else {
895 /* Need to add the 1-based element number to the name */
896 char buf[MAX_LENGTH_OF_LONG + 1];
897 char *res = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, i+1);
898 size_t digits = buf + sizeof(buf) - 1 - res;
899 real_name = zend_string_concat2(name, namelen, res, digits);
900 }
901
902 switch ((int) type) {
903 case 'a': {
904 /* a will not strip any trailing whitespace or null padding */
905 zend_long len = inputlen - inputpos; /* Remaining string */
906
907 /* If size was given take minimum of len and size */
908 if ((size >= 0) && (len > size)) {
909 len = size;
910 }
911
912 size = len;
913
914 ZVAL_STRINGL(&val, &input[inputpos], len);
915 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
916 break;
917 }
918 case 'A': {
919 /* A will strip any trailing whitespace */
920 char padn = '\0'; char pads = ' '; char padt = '\t'; char padc = '\r'; char padl = '\n';
921 zend_long len = inputlen - inputpos; /* Remaining string */
922
923 /* If size was given take minimum of len and size */
924 if ((size >= 0) && (len > size)) {
925 len = size;
926 }
927
928 size = len;
929
930 /* Remove trailing white space and nulls chars from unpacked data */
931 while (--len >= 0) {
932 if (input[inputpos + len] != padn
933 && input[inputpos + len] != pads
934 && input[inputpos + len] != padt
935 && input[inputpos + len] != padc
936 && input[inputpos + len] != padl
937 )
938 break;
939 }
940
941 ZVAL_STRINGL(&val, &input[inputpos], len + 1);
942 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
943 break;
944 }
945 /* New option added for Z to remain in-line with the Perl implementation */
946 case 'Z': {
947 /* Z will strip everything after the first null character */
948 char pad = '\0';
949 zend_long s,
950 len = inputlen - inputpos; /* Remaining string */
951
952 /* If size was given take minimum of len and size */
953 if ((size >= 0) && (len > size)) {
954 len = size;
955 }
956
957 size = len;
958
959 /* Remove everything after the first null */
960 for (s=0 ; s < len ; s++) {
961 if (input[inputpos + s] == pad)
962 break;
963 }
964 len = s;
965
966 ZVAL_STRINGL(&val, &input[inputpos], len);
967 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
968 break;
969 }
970
971
972 case 'h':
973 case 'H': {
974 zend_long len = (inputlen - inputpos) * 2; /* Remaining */
975 int nibbleshift = (type == 'h') ? 0 : 4;
976 int first = 1;
978 zend_long ipos, opos;
979
980
981 if (size > INT_MAX / 2) {
982 zend_string_release(real_name);
983 zend_argument_value_error(1, "repeater must be less than or equal to %d", INT_MAX / 2);
985 }
986
987 /* If size was given take minimum of len and size */
988 if (size >= 0 && len > (size * 2)) {
989 len = size * 2;
990 }
991
992 if (len > 0 && argb > 0) {
993 len -= argb % 2;
994 }
995
996 buf = zend_string_alloc(len, 0);
997
998 for (ipos = opos = 0; opos < len; opos++) {
999 char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
1000
1001 if (cc < 10) {
1002 cc += '0';
1003 } else {
1004 cc += 'a' - 10;
1005 }
1006
1007 ZSTR_VAL(buf)[opos] = cc;
1008 nibbleshift = (nibbleshift + 4) & 7;
1009
1010 if (first-- == 0) {
1011 ipos++;
1012 first = 1;
1013 }
1014 }
1015
1016 ZSTR_VAL(buf)[len] = '\0';
1017
1018 ZVAL_STR(&val, buf);
1019 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1020 break;
1021 }
1022
1023 case 'c': /* signed */
1024 case 'C': { /* unsigned */
1025 uint8_t x = input[inputpos];
1026 zend_long v = (type == 'c') ? (int8_t) x : x;
1027
1028 ZVAL_LONG(&val, v);
1029 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1030 break;
1031 }
1032
1033 case 's': /* signed machine endian */
1034 case 'S': /* unsigned machine endian */
1035 case 'n': /* unsigned big endian */
1036 case 'v': { /* unsigned little endian */
1037 zend_long v = 0;
1038 uint16_t x = *((unaligned_uint16_t*) &input[inputpos]);
1039
1040 if (type == 's') {
1041 v = (int16_t) x;
1042 } else if ((type == 'n' && MACHINE_LITTLE_ENDIAN) || (type == 'v' && !MACHINE_LITTLE_ENDIAN)) {
1043 v = php_pack_reverse_int16(x);
1044 } else {
1045 v = x;
1046 }
1047
1048 ZVAL_LONG(&val, v);
1049 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1050 break;
1051 }
1052
1053 case 'i': /* signed integer, machine size, machine endian */
1054 case 'I': { /* unsigned integer, machine size, machine endian */
1055 zend_long v;
1056 if (type == 'i') {
1057 int x = *((unaligned_int*) &input[inputpos]);
1058 v = x;
1059 } else {
1060 unsigned int x = *((unaligned_uint*) &input[inputpos]);
1061 v = x;
1062 }
1063
1064 ZVAL_LONG(&val, v);
1065 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1066 break;
1067 }
1068
1069 case 'l': /* signed machine endian */
1070 case 'L': /* unsigned machine endian */
1071 case 'N': /* unsigned big endian */
1072 case 'V': { /* unsigned little endian */
1073 zend_long v = 0;
1074 uint32_t x = *((unaligned_uint32_t*) &input[inputpos]);
1075
1076 if (type == 'l') {
1077 v = (int32_t) x;
1078 } else if ((type == 'N' && MACHINE_LITTLE_ENDIAN) || (type == 'V' && !MACHINE_LITTLE_ENDIAN)) {
1079 v = php_pack_reverse_int32(x);
1080 } else {
1081 v = x;
1082 }
1083
1084 ZVAL_LONG(&val, v);
1085 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1086
1087 break;
1088 }
1089
1090#if SIZEOF_ZEND_LONG > 4
1091 case 'q': /* signed machine endian */
1092 case 'Q': /* unsigned machine endian */
1093 case 'J': /* unsigned big endian */
1094 case 'P': { /* unsigned little endian */
1095 zend_long v = 0;
1096 uint64_t x = *((unaligned_uint64_t*) &input[inputpos]);
1097
1098 if (type == 'q') {
1099 v = (int64_t) x;
1100 } else if ((type == 'J' && MACHINE_LITTLE_ENDIAN) || (type == 'P' && !MACHINE_LITTLE_ENDIAN)) {
1101 v = php_pack_reverse_int64(x);
1102 } else {
1103 v = x;
1104 }
1105
1106 ZVAL_LONG(&val, v);
1107 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1108 break;
1109 }
1110#endif
1111
1112 case 'f': /* float */
1113 case 'g': /* little endian float*/
1114 case 'G': /* big endian float*/
1115 {
1116 float v;
1117
1118 if (type == 'g') {
1119 v = php_pack_parse_float(1, &input[inputpos]);
1120 } else if (type == 'G') {
1121 v = php_pack_parse_float(0, &input[inputpos]);
1122 } else {
1123 memcpy(&v, &input[inputpos], sizeof(float));
1124 }
1125
1126 ZVAL_DOUBLE(&val, v);
1127 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1128 break;
1129 }
1130
1131
1132 case 'd': /* double */
1133 case 'e': /* little endian float */
1134 case 'E': /* big endian float */
1135 {
1136 double v;
1137 if (type == 'e') {
1138 v = php_pack_parse_double(1, &input[inputpos]);
1139 } else if (type == 'E') {
1140 v = php_pack_parse_double(0, &input[inputpos]);
1141 } else {
1142 memcpy(&v, &input[inputpos], sizeof(double));
1143 }
1144
1145 ZVAL_DOUBLE(&val, v);
1146 zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1147 break;
1148 }
1149
1150 case 'x':
1151 /* Do nothing with input, just skip it */
1152 break;
1153
1154 case 'X':
1155 if (inputpos < size) {
1156 inputpos = -size;
1157 i = repetitions - 1; /* Break out of for loop */
1158
1159 if (repetitions >= 0) {
1160 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1161 }
1162 }
1163 break;
1164
1165 case '@':
1166 if (repetitions <= inputlen) {
1167 inputpos = repetitions;
1168 } else {
1169 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1170 }
1171
1172 i = repetitions - 1; /* Done, break out of for loop */
1173 break;
1174 }
1175
1176 zend_string_release(real_name);
1177
1178 inputpos += size;
1179 if (inputpos < 0) {
1180 if (size != -1) { /* only print warning if not working with * */
1181 php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1182 }
1183 inputpos = 0;
1184 }
1185 } else if (repetitions < 0) {
1186 /* Reached end of input for '*' repeater */
1187 break;
1188 } else {
1189 php_error_docref(NULL, E_WARNING, "Type %c: not enough input values, need %d values but only " ZEND_LONG_FMT " %s provided", type, size, inputlen - inputpos, inputlen - inputpos == 1 ? "was" : "were");
1192 }
1193 }
1194
1195 if (formatlen > 0) {
1196 formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
1197 format++;
1198 }
1199 }
1200}
1201/* }}} */
1202
1203/* {{{ PHP_MINIT_FUNCTION */
1205{
1206 int i;
1207
1209 /* Where to get lo to hi bytes from */
1210 byte_map[0] = 0;
1211
1212 for (i = 0; i < (int)sizeof(int); i++) {
1213 int_map[i] = i;
1214 }
1215
1216 machine_endian_short_map[0] = 0;
1217 machine_endian_short_map[1] = 1;
1218 big_endian_short_map[0] = 1;
1219 big_endian_short_map[1] = 0;
1220 little_endian_short_map[0] = 0;
1221 little_endian_short_map[1] = 1;
1222
1223 machine_endian_long_map[0] = 0;
1224 machine_endian_long_map[1] = 1;
1225 machine_endian_long_map[2] = 2;
1226 machine_endian_long_map[3] = 3;
1227 big_endian_long_map[0] = 3;
1228 big_endian_long_map[1] = 2;
1229 big_endian_long_map[2] = 1;
1230 big_endian_long_map[3] = 0;
1231 little_endian_long_map[0] = 0;
1232 little_endian_long_map[1] = 1;
1233 little_endian_long_map[2] = 2;
1234 little_endian_long_map[3] = 3;
1235
1236#if SIZEOF_ZEND_LONG > 4
1237 machine_endian_longlong_map[0] = 0;
1238 machine_endian_longlong_map[1] = 1;
1239 machine_endian_longlong_map[2] = 2;
1240 machine_endian_longlong_map[3] = 3;
1241 machine_endian_longlong_map[4] = 4;
1242 machine_endian_longlong_map[5] = 5;
1243 machine_endian_longlong_map[6] = 6;
1244 machine_endian_longlong_map[7] = 7;
1245 big_endian_longlong_map[0] = 7;
1246 big_endian_longlong_map[1] = 6;
1247 big_endian_longlong_map[2] = 5;
1248 big_endian_longlong_map[3] = 4;
1249 big_endian_longlong_map[4] = 3;
1250 big_endian_longlong_map[5] = 2;
1251 big_endian_longlong_map[6] = 1;
1252 big_endian_longlong_map[7] = 0;
1253 little_endian_longlong_map[0] = 0;
1254 little_endian_longlong_map[1] = 1;
1255 little_endian_longlong_map[2] = 2;
1256 little_endian_longlong_map[3] = 3;
1257 little_endian_longlong_map[4] = 4;
1258 little_endian_longlong_map[5] = 5;
1259 little_endian_longlong_map[6] = 6;
1260 little_endian_longlong_map[7] = 7;
1261#endif
1262 }
1263 else {
1264 zval val;
1265 int size = sizeof(Z_LVAL(val));
1266 Z_LVAL(val)=0; /*silence a warning*/
1267
1268 /* Where to get hi to lo bytes from */
1269 byte_map[0] = size - 1;
1270
1271 for (i = 0; i < (int)sizeof(int); i++) {
1272 int_map[i] = size - (sizeof(int) - i);
1273 }
1274
1275 machine_endian_short_map[0] = size - 2;
1276 machine_endian_short_map[1] = size - 1;
1277 big_endian_short_map[0] = size - 2;
1278 big_endian_short_map[1] = size - 1;
1279 little_endian_short_map[0] = size - 1;
1280 little_endian_short_map[1] = size - 2;
1281
1282 machine_endian_long_map[0] = size - 4;
1283 machine_endian_long_map[1] = size - 3;
1284 machine_endian_long_map[2] = size - 2;
1285 machine_endian_long_map[3] = size - 1;
1286 big_endian_long_map[0] = size - 4;
1287 big_endian_long_map[1] = size - 3;
1288 big_endian_long_map[2] = size - 2;
1289 big_endian_long_map[3] = size - 1;
1290 little_endian_long_map[0] = size - 1;
1291 little_endian_long_map[1] = size - 2;
1292 little_endian_long_map[2] = size - 3;
1293 little_endian_long_map[3] = size - 4;
1294
1295#if SIZEOF_ZEND_LONG > 4
1296 machine_endian_longlong_map[0] = size - 8;
1297 machine_endian_longlong_map[1] = size - 7;
1298 machine_endian_longlong_map[2] = size - 6;
1299 machine_endian_longlong_map[3] = size - 5;
1300 machine_endian_longlong_map[4] = size - 4;
1301 machine_endian_longlong_map[5] = size - 3;
1302 machine_endian_longlong_map[6] = size - 2;
1303 machine_endian_longlong_map[7] = size - 1;
1304 big_endian_longlong_map[0] = size - 8;
1305 big_endian_longlong_map[1] = size - 7;
1306 big_endian_longlong_map[2] = size - 6;
1307 big_endian_longlong_map[3] = size - 5;
1308 big_endian_longlong_map[4] = size - 4;
1309 big_endian_longlong_map[5] = size - 3;
1310 big_endian_longlong_map[6] = size - 2;
1311 big_endian_longlong_map[7] = size - 1;
1312 little_endian_longlong_map[0] = size - 1;
1313 little_endian_longlong_map[1] = size - 2;
1314 little_endian_longlong_map[2] = size - 3;
1315 little_endian_longlong_map[3] = size - 4;
1316 little_endian_longlong_map[4] = size - 5;
1317 little_endian_longlong_map[5] = size - 6;
1318 little_endian_longlong_map[6] = size - 7;
1319 little_endian_longlong_map[7] = size - 8;
1320#endif
1321 }
1322
1323 return SUCCESS;
1324}
1325/* }}} */
size_t len
Definition apprentice.c:174
pack(string $format, mixed ... $values)
unpack(string $format, string $string, int $offset=0)
char s[4]
Definition cdf.c:77
uint32_t v
Definition cdf.c:1237
zend_ffi_type * type
Definition ffi.c:3812
zend_long n
Definition ffi.c:4979
new_type size
Definition ffi.c:4365
zend_string * res
Definition ffi.c:4692
memcpy(ptr1, ptr2, size)
zval * arg
Definition ffi.c:3975
memset(ptr, 0, type->size)
zval * val
Definition ffi.c:4262
zend_ffi_ctype_name_buf buf
Definition ffi.c:4685
zend_long offset
#define NULL
Definition gdcache.h:45
#define SUCCESS
Definition hash_sha3.c:261
PHPAPI ZEND_COLD void php_error_docref(const char *docref, int type, const char *format,...)
Definition main.c:1173
#define MACHINE_LITTLE_ENDIAN
Definition pack.c:56
#define INC_OUTPUTPOS(a, b)
Definition pack.c:44
#define PHP_FUNCTION
Definition php.h:364
#define PHP_MINIT_FUNCTION
Definition php.h:400
#define INT_MAX
Definition php.h:237
ptrdiff_t namelen
Definition session.c:1097
#define errno
ZEND_API ZEND_COLD void zend_value_error(const char *format,...)
Definition zend.c:1849
ZEND_API ZEND_COLD void zend_argument_value_error(uint32_t arg_num, const char *format,...)
Definition zend_API.c:433
#define ZEND_PARSE_PARAMETERS_END()
Definition zend_API.h:1641
#define RETURN_FALSE
Definition zend_API.h:1058
#define Z_PARAM_OPTIONAL
Definition zend_API.h:1667
#define Z_PARAM_STRING(dest, dest_len)
Definition zend_API.h:2071
#define Z_PARAM_STR(dest)
Definition zend_API.h:2086
#define ZEND_PARSE_PARAMETERS_START(min_num_args, max_num_args)
Definition zend_API.h:1620
#define Z_PARAM_LONG(dest)
Definition zend_API.h:1896
#define Z_PARAM_VARIADIC(spec, dest, dest_num)
Definition zend_API.h:2124
#define RETURN_NEW_STR(s)
Definition zend_API.h:1041
#define RETURN_THROWS()
Definition zend_API.h:1060
#define ZVAL_STRINGL(z, s, l)
Definition zend_API.h:952
#define array_init(arg)
Definition zend_API.h:537
#define efree(ptr)
Definition zend_alloc.h:155
#define safe_emalloc(nmemb, size, offset)
Definition zend_alloc.h:154
struct _zval_struct zval
uint32_t num_args
#define E_WARNING
Definition zend_errors.h:24
ZEND_API void ZEND_FASTCALL zend_array_destroy(HashTable *ht)
Definition zend_hash.c:1808
int32_t zend_long
Definition zend_long.h:42
#define MAX_LENGTH_OF_LONG
Definition zend_long.h:109
#define ZEND_LONG_FMT
Definition zend_long.h:87
struct _zend_string zend_string
ZEND_API void ZEND_FASTCALL convert_to_long(zval *op)
#define ZEND_ATTRIBUTE_CONST
#define ZEND_SET_ALIGNED(alignment, decl)
#define MAX(a, b)
ZEND_API zend_string * zend_string_concat2(const char *str1, size_t str1_len, const char *str2, size_t str2_len)
#define ZSTR_VAL(zstr)
Definition zend_string.h:68
#define ZSTR_LEN(zstr)
Definition zend_string.h:69
#define ZVAL_STR(z, s)
#define Z_ARRVAL_P(zval_p)
Definition zend_types.h:987
#define ZVAL_LONG(z, l)
#define Z_STRLEN(zval)
Definition zend_types.h:977
#define ZVAL_DOUBLE(z, d)
#define Z_ARR_P(zval_p)
Definition zend_types.h:984
#define Z_LVAL_P(zval_p)
Definition zend_types.h:966
#define Z_LVAL(zval)
Definition zend_types.h:965
zval * return_value
zend_string * name
bool result