Coverage Report

Created: 2026-09-14 06:25

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