Coverage Report

Created: 2026-10-28 06:20

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
0
  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
0
  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
0
# 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
0
{
203
0
  zval *argv = NULL;
204
0
  int num_args = 0;
205
0
  size_t i;
206
0
  int currentarg;
207
0
  char *format;
208
0
  size_t formatlen;
209
0
  char *formatcodes;
210
0
  int *formatargs;
211
0
  size_t formatcount = 0;
212
0
  int outputpos = 0, outputsize = 0;
213
0
  zend_string *output;
214
215
0
  ZEND_PARSE_PARAMETERS_START(1, -1)
216
0
    Z_PARAM_STRING(format, formatlen)
217
0
    Z_PARAM_VARIADIC('*', argv, num_args)
218
0
  ZEND_PARSE_PARAMETERS_END();
219
220
  /* We have a maximum of <formatlen> format codes to deal with */
221
0
  formatcodes = safe_emalloc(formatlen, sizeof(*formatcodes), 0);
222
0
  formatargs = safe_emalloc(formatlen, sizeof(*formatargs), 0);
223
0
  php_pack_endianness *formatendian = safe_emalloc(formatlen, sizeof(*formatendian), 0);
224
0
  currentarg = 0;
225
226
  /* Preprocess format into formatcodes and formatargs */
227
0
  for (i = 0; i < formatlen; formatcount++) {
228
0
    char code = format[i++];
229
0
    int arg = 1;
230
0
    php_pack_endianness endian = PHP_NO_ENDIAN_MODIFIER;
231
232
    /* Handle endianness modifier if any */
233
0
    if (i < formatlen) {
234
0
      char c = format[i];
235
236
0
      if (c == '<') {
237
0
        endian = PHP_LITTLE_ENDIAN;
238
0
        i++;
239
0
      } else if (c == '>') {
240
0
        endian = PHP_BIG_ENDIAN;
241
0
        i++;
242
0
      }
243
0
    }
244
245
    /* Handle format arguments if any */
246
0
    if (i < formatlen) {
247
0
      char c = format[i];
248
249
0
      if (c == '*') {
250
0
        arg = -1;
251
0
        i++;
252
0
      }
253
0
      else if (c >= '0' && c <= '9') {
254
0
        arg = atoi(&format[i]);
255
256
0
        while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
257
0
          i++;
258
0
        }
259
0
      }
260
0
    }
261
262
    /* Handle special arg '*' for all codes and check argv overflows */
263
0
    switch (code) {
264
      /* Never uses any args */
265
0
      case 'x':
266
0
      case 'X':
267
0
      case '@':
268
0
        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
0
        if (arg < 0) {
276
0
          php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", code);
277
0
          arg = 1;
278
0
        }
279
0
        break;
280
281
      /* Always uses one arg */
282
0
      case 'a':
283
0
      case 'A':
284
0
      case 'Z':
285
0
      case 'h':
286
0
      case 'H':
287
0
        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
0
        if (currentarg >= num_args) {
295
0
          efree(formatcodes);
296
0
          efree(formatargs);
297
0
          efree(formatendian);
298
0
          zend_value_error("Type %c: not enough arguments", code);
299
0
          RETURN_THROWS();
300
0
        }
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
0
      case 'J':
324
0
      case 'P':
325
0
        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
0
        ZEND_FALLTHROUGH;
333
334
      /* 64-bit codes that support endianness modifiers */
335
0
      case 'q':
336
0
      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
0
        if (arg < 0) {
345
0
          arg = num_args - currentarg;
346
0
        }
347
0
        if (currentarg > INT_MAX - arg) {
348
0
          goto too_few_args;
349
0
        }
350
0
        currentarg += arg;
351
352
0
        if (currentarg > num_args) {
353
0
          goto too_few_args;
354
0
        }
355
0
        break;
356
0
#endif
357
358
      /* Codes with explicit endianness, endianness modifiers not allowed */
359
0
      case 'n':
360
0
      case 'N':
361
0
      case 'v':
362
0
      case 'V':
363
0
        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
0
        ZEND_FALLTHROUGH;
371
372
      /* Codes that support endianness modifiers */
373
0
      case 's':
374
0
      case 'S':
375
0
      case 'l':
376
0
      case 'L':
377
0
        if (arg < 0) {
378
0
          arg = num_args - currentarg;
379
0
        }
380
0
        if (currentarg > INT_MAX - arg) {
381
0
          goto too_few_args;
382
0
        }
383
0
        currentarg += arg;
384
385
0
        if (currentarg > num_args) {
386
0
          goto too_few_args;
387
0
        }
388
0
        break;
389
390
0
      case 'c':
391
0
      case 'C':
392
0
      case 'i':
393
0
      case 'I':
394
0
        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
0
        if (arg < 0) {
402
0
          arg = num_args - currentarg;
403
0
        }
404
0
        if (currentarg > INT_MAX - arg) {
405
0
          goto too_few_args;
406
0
        }
407
0
        currentarg += arg;
408
409
0
        if (currentarg > num_args) {
410
0
          goto too_few_args;
411
0
        }
412
0
        break;
413
414
      /* Codes with explicit endianness, endianness modifiers not allowed */
415
0
      case 'g': /* little endian float */
416
0
      case 'G': /* big endian float */
417
0
      case 'e': /* little endian double */
418
0
      case 'E': /* big endian double */
419
0
        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
0
        ZEND_FALLTHROUGH;
427
428
      /* Codes that support endianness modifiers */
429
0
      case 'f': /* float */
430
0
      case 'd': /* double */
431
0
        if (arg < 0) {
432
0
          arg = num_args - currentarg;
433
0
        }
434
0
        if (currentarg > INT_MAX - arg) {
435
0
          goto too_few_args;
436
0
        }
437
0
        currentarg += arg;
438
439
0
        if (currentarg > num_args) {
440
0
too_few_args:
441
0
          efree(formatcodes);
442
0
          efree(formatargs);
443
0
          efree(formatendian);
444
0
          zend_value_error("Type %c: too few arguments", code);
445
0
          RETURN_THROWS();
446
0
        }
447
0
        break;
448
449
0
      default:
450
0
        efree(formatcodes);
451
0
        efree(formatargs);
452
0
        efree(formatendian);
453
0
        zend_value_error("Type %c: unknown format code", code);
454
0
        RETURN_THROWS();
455
0
    }
456
457
0
    formatcodes[formatcount] = code;
458
0
    formatargs[formatcount] = arg;
459
0
    formatendian[formatcount] = endian;
460
0
  }
461
462
0
  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
0
  for (i = 0; i < formatcount; i++) {
468
0
    char code = formatcodes[i];
469
0
    int arg = formatargs[i];
470
471
0
    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
0
      case 'x':
483
0
        INC_OUTPUTPOS(arg,1)   /* 8 bit per arg */
484
0
        break;
485
486
0
      case 's':
487
0
      case 'S':
488
0
      case 'n':
489
0
      case 'v':
490
0
        INC_OUTPUTPOS(arg,2)   /* 16 bit per arg */
491
0
        break;
492
493
0
      case 'i':
494
0
      case 'I':
495
0
        INC_OUTPUTPOS(arg,sizeof(int))
496
0
        break;
497
498
0
      case 'l':
499
0
      case 'L':
500
0
      case 'N':
501
0
      case 'V':
502
0
        INC_OUTPUTPOS(arg,4)   /* 32 bit per arg */
503
0
        break;
504
505
0
#if SIZEOF_ZEND_LONG > 4
506
0
      case 'q':
507
0
      case 'Q':
508
0
      case 'J':
509
0
      case 'P':
510
0
        INC_OUTPUTPOS(arg,8)   /* 32 bit per arg */
511
0
        break;
512
0
#endif
513
514
0
      case 'f': /* float */
515
0
      case 'g': /* little endian float */
516
0
      case 'G': /* big endian float */
517
0
        INC_OUTPUTPOS(arg,sizeof(float))
518
0
        break;
519
520
0
      case 'd': /* double */
521
0
      case 'e': /* little endian double */
522
0
      case 'E': /* big endian double */
523
0
        INC_OUTPUTPOS(arg,sizeof(double))
524
0
        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
0
      case '@':
536
0
        outputpos = arg;
537
0
        break;
538
0
    }
539
540
0
    if (outputsize < outputpos) {
541
0
      outputsize = outputpos;
542
0
    }
543
0
  }
544
545
0
  output = zend_string_alloc(outputsize, 0);
546
0
  outputpos = 0;
547
0
  currentarg = 0;
548
549
  /* Do actual packing */
550
0
  for (i = 0; i < formatcount; i++) {
551
0
    char code = formatcodes[i];
552
0
    int arg = formatargs[i];
553
554
0
    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
        arg_cp = MIN(ZSTR_LEN(str), arg_cp);
563
0
        if (arg_cp < arg) {
564
0
          memset(&ZSTR_VAL(output)[outputpos + arg_cp],
565
0
            (code == 'a' || code == 'Z') ? '\0' : ' ', arg - arg_cp);
566
0
        }
567
0
        memcpy(&ZSTR_VAL(output)[outputpos], ZSTR_VAL(str), arg_cp);
568
569
0
        outputpos += arg;
570
0
        zend_tmp_string_release(tmp_str);
571
0
        break;
572
0
      }
573
574
0
      case 'h':
575
0
      case 'H': {
576
0
        int nibbleshift = (code == 'h') ? 0 : 4;
577
0
        int first = 1;
578
0
        zend_string *tmp_str;
579
0
        zend_string *str = zval_get_tmp_string(&argv[currentarg++], &tmp_str);
580
0
        char *v = ZSTR_VAL(str);
581
582
0
        outputpos--;
583
0
        if ((size_t)arg > ZSTR_LEN(str)) {
584
0
          php_error_docref(NULL, E_WARNING, "Type %c: not enough characters in string", code);
585
0
          arg = ZSTR_LEN(str);
586
0
        }
587
588
0
        while (arg-- > 0) {
589
0
          char n = *v++;
590
591
0
          if (n >= '0' && n <= '9') {
592
0
            n -= '0';
593
0
          } else if (n >= 'A' && n <= 'F') {
594
0
            n -= ('A' - 10);
595
0
          } else if (n >= 'a' && n <= 'f') {
596
0
            n -= ('a' - 10);
597
0
          } else {
598
0
            php_error_docref(NULL, E_WARNING, "Type %c: illegal hex digit %c", code, n);
599
0
            n = 0;
600
0
          }
601
602
0
          if (first--) {
603
0
            ZSTR_VAL(output)[++outputpos] = 0;
604
0
          } else {
605
0
            first = 1;
606
0
          }
607
608
0
          ZSTR_VAL(output)[outputpos] |= (n << nibbleshift);
609
0
          nibbleshift = (nibbleshift + 4) & 7;
610
0
        }
611
612
0
        outputpos++;
613
0
        zend_tmp_string_release(tmp_str);
614
0
        break;
615
0
      }
616
617
0
      case 'c':
618
0
      case 'C':
619
0
        while (arg-- > 0) {
620
0
          php_pack(&argv[currentarg++], 1, PHP_MACHINE_ENDIAN, &ZSTR_VAL(output)[outputpos]);
621
0
          outputpos++;
622
0
        }
623
0
        break;
624
625
0
      case 's':
626
0
      case 'S':
627
0
      case 'n':
628
0
      case 'v': {
629
0
        php_pack_endianness endianness;
630
631
0
        if (code == 'n') {
632
0
          endianness = PHP_BIG_ENDIAN;
633
0
        } else if (code == 'v') {
634
0
          endianness = PHP_LITTLE_ENDIAN;
635
0
        } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) {
636
0
          endianness = formatendian[i];
637
0
        } else {
638
0
          endianness = PHP_MACHINE_ENDIAN;
639
0
        }
640
641
0
        while (arg-- > 0) {
642
0
          php_pack(&argv[currentarg++], 2, endianness, &ZSTR_VAL(output)[outputpos]);
643
0
          outputpos += 2;
644
0
        }
645
0
        break;
646
0
      }
647
648
0
      case 'i':
649
0
      case 'I':
650
0
        while (arg-- > 0) {
651
0
          php_pack(&argv[currentarg++], sizeof(int), PHP_MACHINE_ENDIAN, &ZSTR_VAL(output)[outputpos]);
652
0
          outputpos += sizeof(int);
653
0
        }
654
0
        break;
655
656
0
      case 'l':
657
0
      case 'L':
658
0
      case 'N':
659
0
      case 'V': {
660
0
        php_pack_endianness endianness;
661
662
0
        if (code == 'N') {
663
0
          endianness = PHP_BIG_ENDIAN;
664
0
        } else if (code == 'V') {
665
0
          endianness = PHP_LITTLE_ENDIAN;
666
0
        } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) {
667
0
          endianness = formatendian[i];
668
0
        } else {
669
0
          endianness = PHP_MACHINE_ENDIAN;
670
0
        }
671
672
0
        while (arg-- > 0) {
673
0
          php_pack(&argv[currentarg++], 4, endianness, &ZSTR_VAL(output)[outputpos]);
674
0
          outputpos += 4;
675
0
        }
676
0
        break;
677
0
      }
678
679
0
#if SIZEOF_ZEND_LONG > 4
680
0
      case 'q':
681
0
      case 'Q':
682
0
      case 'J':
683
0
      case 'P': {
684
0
        php_pack_endianness endianness;
685
686
0
        if (code == 'J') {
687
0
          endianness = PHP_BIG_ENDIAN;
688
0
        } else if (code == 'P') {
689
0
          endianness = PHP_LITTLE_ENDIAN;
690
0
        } else if (formatendian[i] != PHP_NO_ENDIAN_MODIFIER) {
691
0
          endianness = formatendian[i];
692
0
        } else {
693
0
          endianness = PHP_MACHINE_ENDIAN;
694
0
        }
695
696
0
        while (arg-- > 0) {
697
0
          php_pack(&argv[currentarg++], 8, endianness, &ZSTR_VAL(output)[outputpos]);
698
0
          outputpos += 8;
699
0
        }
700
0
        break;
701
0
      }
702
0
#endif
703
704
0
      case 'f':
705
0
      case 'g':
706
0
      case 'G': {
707
0
        while (arg-- > 0) {
708
0
          float v = (float) zval_get_double(&argv[currentarg++]);
709
0
          if (code == 'g' || formatendian[i] == PHP_LITTLE_ENDIAN) {
710
0
            php_pack_copy_float(1, &ZSTR_VAL(output)[outputpos], v);
711
0
          } else if (code == 'G' || formatendian[i] == PHP_BIG_ENDIAN) {
712
0
            php_pack_copy_float(0, &ZSTR_VAL(output)[outputpos], v);
713
0
          } else {
714
0
            memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
715
0
          }
716
0
          outputpos += sizeof(v);
717
0
        }
718
0
        break;
719
0
      }
720
721
0
      case 'd':
722
0
      case 'e':
723
0
      case 'E': {
724
0
        while (arg-- > 0) {
725
0
          double v = zval_get_double(&argv[currentarg++]);
726
0
          if (code == 'e' || formatendian[i] == PHP_LITTLE_ENDIAN) {
727
0
            php_pack_copy_double(1, &ZSTR_VAL(output)[outputpos], v);
728
0
          } else if (code == 'E' || formatendian[i] == PHP_BIG_ENDIAN) {
729
0
            php_pack_copy_double(0, &ZSTR_VAL(output)[outputpos], v);
730
0
          } else {
731
0
            memcpy(&ZSTR_VAL(output)[outputpos], &v, sizeof(v));
732
0
          }
733
0
          outputpos += sizeof(v);
734
0
        }
735
0
        break;
736
0
      }
737
738
0
      case 'x':
739
0
        memset(&ZSTR_VAL(output)[outputpos], '\0', arg);
740
0
        outputpos += arg;
741
0
        break;
742
743
0
      case 'X':
744
0
        outputpos -= arg;
745
746
0
        if (outputpos < 0) {
747
0
          outputpos = 0;
748
0
        }
749
0
        break;
750
751
0
      case '@':
752
0
        if (arg > outputpos) {
753
0
          memset(&ZSTR_VAL(output)[outputpos], '\0', arg - outputpos);
754
0
        }
755
0
        outputpos = arg;
756
0
        break;
757
0
    }
758
0
  }
759
760
0
  efree(formatcodes);
761
0
  efree(formatargs);
762
0
  efree(formatendian);
763
0
  ZSTR_VAL(output)[outputpos] = '\0';
764
0
  ZSTR_LEN(output) = outputpos;
765
0
  RETURN_NEW_STR(output);
766
0
}
767
/* }}} */
768
769
/* unpack() is based on Perl's unpack(), but is modified a bit from there.
770
 * Rather than depending on error-prone ordered lists or syntactically
771
 * unpleasant pass-by-reference, we return an object with named parameters
772
 * (like *_fetch_object()). Syntax is "f[repeat]name/...", where "f" is the
773
 * formatter char (like pack()), "[repeat]" is the optional repeater argument,
774
 * and "name" is the name of the variable to use.
775
 * Example: "c2chars/nints" will return an object with fields
776
 * chars1, chars2, and ints.
777
 * Numeric pack types will return numbers, a and A will return strings,
778
 * f and d will return doubles.
779
 * 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, @.
780
 * Added g, G for little endian float and big endian float, added e, E for little endian double and big endian double.
781
 */
782
/* {{{ Unpack binary string into named array elements according to format argument */
783
PHP_FUNCTION(unpack)
784
0
{
785
0
  char *format, *input;
786
0
  zend_string *formatarg, *inputarg;
787
0
  zend_long formatlen, inputpos, inputlen;
788
0
  int i;
789
0
  zend_long offset = 0;
790
791
0
  ZEND_PARSE_PARAMETERS_START(2, 3)
792
0
    Z_PARAM_STR(formatarg)
793
0
    Z_PARAM_STR(inputarg)
794
0
    Z_PARAM_OPTIONAL
795
0
    Z_PARAM_LONG(offset)
796
0
  ZEND_PARSE_PARAMETERS_END();
797
798
0
  format = ZSTR_VAL(formatarg);
799
0
  formatlen = ZSTR_LEN(formatarg);
800
0
  input = ZSTR_VAL(inputarg);
801
0
  inputlen = ZSTR_LEN(inputarg);
802
0
  inputpos = 0;
803
804
805
0
  if (offset < 0 || offset > inputlen) {
806
0
    zend_argument_value_error(3, "must be contained in argument #2 ($string)");
807
0
    RETURN_THROWS();
808
0
  }
809
810
0
  input += offset;
811
0
  inputlen -= offset;
812
813
0
  array_init(return_value);
814
815
0
  while (formatlen-- > 0) {
816
0
    char type = *(format++);
817
0
    int repetitions = 1, argb;
818
0
    char *name;
819
0
    int namelen;
820
0
    int size = 0;
821
0
    php_pack_endianness endian = PHP_NO_ENDIAN_MODIFIER;
822
823
0
    if (formatlen > 0) {
824
0
      char c = *format;
825
826
0
      if (c == '<') {
827
0
        endian = PHP_LITTLE_ENDIAN;
828
0
        format++;
829
0
        formatlen--;
830
0
      } else if (c == '>') {
831
0
        endian = PHP_BIG_ENDIAN;
832
0
        format++;
833
0
        formatlen--;
834
0
      }
835
0
    }
836
837
    /* Handle format arguments if any */
838
0
    if (formatlen > 0) {
839
0
      char c = *format;
840
841
0
      if (c >= '0' && c <= '9') {
842
0
        errno = 0;
843
0
        long tmp = strtol(format, NULL, 10);
844
        /* There is not strtoi. We have to check the range ourselves.
845
         * With 32-bit long the INT_{MIN,MAX} are useless because long == int, but with 64-bit they do limit us to 32-bit. */
846
0
        if (errno || tmp < INT_MIN || tmp > INT_MAX) {
847
0
          php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
848
0
          zend_array_destroy(Z_ARR_P(return_value));
849
0
          RETURN_FALSE;
850
0
        }
851
0
        repetitions = tmp;
852
853
0
        while (formatlen > 0 && *format >= '0' && *format <= '9') {
854
0
          format++;
855
0
          formatlen--;
856
0
        }
857
0
      } else if (c == '*') {
858
0
        repetitions = -1;
859
0
        format++;
860
0
        formatlen--;
861
0
      }
862
0
    }
863
864
    /* Get of new value in array */
865
0
    name = format;
866
0
    argb = repetitions;
867
868
0
    while (formatlen > 0 && *format != '/') {
869
0
      formatlen--;
870
0
      format++;
871
0
    }
872
873
0
    namelen = format - name;
874
875
0
    if (namelen > 200)
876
0
      namelen = 200;
877
878
0
    switch (type) {
879
      /* Never use any input */
880
0
      case 'X':
881
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
882
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
883
0
          RETURN_THROWS();
884
0
        }
885
0
        size = -1;
886
0
        if (repetitions < 0) {
887
0
          php_error_docref(NULL, E_WARNING, "Type %c: '*' ignored", type);
888
0
          repetitions = 1;
889
0
        }
890
0
        break;
891
892
0
      case '@':
893
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
894
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
895
0
          RETURN_THROWS();
896
0
        }
897
0
        size = 0;
898
0
        break;
899
900
0
      case 'a':
901
0
      case 'A':
902
0
      case 'Z':
903
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
904
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
905
0
          RETURN_THROWS();
906
0
        }
907
0
        size = repetitions;
908
0
        repetitions = 1;
909
0
        break;
910
911
0
      case 'h':
912
0
      case 'H':
913
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
914
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
915
0
          RETURN_THROWS();
916
0
        }
917
0
        size = (repetitions > 0) ? ((unsigned int) repetitions + 1) / 2 : repetitions;
918
0
        repetitions = 1;
919
0
        break;
920
921
      /* Use 1 byte of input */
922
0
      case 'c':
923
0
      case 'C':
924
0
      case 'x':
925
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
926
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
927
0
          RETURN_THROWS();
928
0
        }
929
0
        size = 1;
930
0
        break;
931
932
      /* Use 2 bytes of input, endianness modifiers allowed */
933
0
      case 's':
934
0
      case 'S':
935
0
        size = 2;
936
0
        break;
937
938
      /* Use 2 bytes of input with inherent endianness */
939
0
      case 'n':
940
0
      case 'v':
941
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
942
0
          zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type);
943
0
          RETURN_THROWS();
944
0
        }
945
0
        size = 2;
946
0
        break;
947
948
      /* Use sizeof(int) bytes of input */
949
0
      case 'i':
950
0
      case 'I':
951
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
952
0
          zend_value_error("Endianness modifier is not supported for format code '%c'", type);
953
0
          RETURN_THROWS();
954
0
        }
955
0
        size = sizeof(int);
956
0
        break;
957
958
      /* Use 4 bytes of input, endianness modifiers allowed */
959
0
      case 'l':
960
0
      case 'L':
961
0
        size = 4;
962
0
        break;
963
964
      /* Use 4 bytes of input with inherent endianness */
965
0
      case 'N':
966
0
      case 'V':
967
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
968
0
          zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type);
969
0
          RETURN_THROWS();
970
0
        }
971
0
        size = 4;
972
0
        break;
973
974
      /* Use 8 bytes of input, endianness modifiers allowed */
975
0
      case 'q':
976
0
      case 'Q':
977
0
#if SIZEOF_ZEND_LONG > 4
978
0
        size = 8;
979
0
        break;
980
#else
981
        zend_value_error("64-bit format codes are not available for 32-bit versions of PHP");
982
        RETURN_THROWS();
983
#endif
984
985
      /* Use 8 bytes of input with inherent endianness */
986
0
      case 'J':
987
0
      case 'P':
988
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
989
0
          zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type);
990
0
          RETURN_THROWS();
991
0
        }
992
0
#if SIZEOF_ZEND_LONG > 4
993
0
        size = 8;
994
0
        break;
995
#else
996
        zend_value_error("64-bit format codes are not available for 32-bit versions of PHP");
997
        RETURN_THROWS();
998
#endif
999
1000
      /* Use sizeof(float) bytes of input, endianness modifiers allowed */
1001
0
      case 'f':
1002
0
        size = sizeof(float);
1003
0
        break;
1004
1005
      /* Use sizeof(float) bytes of input with inherent endianness */
1006
0
      case 'g':
1007
0
      case 'G':
1008
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
1009
0
          zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type);
1010
0
          RETURN_THROWS();
1011
0
        }
1012
0
        size = sizeof(float);
1013
0
        break;
1014
1015
      /* Use sizeof(double) bytes of input, endianness modifiers allowed */
1016
0
      case 'd':
1017
0
        size = sizeof(double);
1018
0
        break;
1019
1020
      /* Use sizeof(double) bytes of input with inherent endianness */
1021
0
      case 'e':
1022
0
      case 'E':
1023
0
        if (endian != PHP_NO_ENDIAN_MODIFIER) {
1024
0
          zend_value_error("Endianness modifier '%c' cannot be applied to format code '%c' which already has inherent endianness", (endian == PHP_LITTLE_ENDIAN) ? '<' : '>', type);
1025
0
          RETURN_THROWS();
1026
0
        }
1027
0
        size = sizeof(double);
1028
0
        break;
1029
1030
0
      default:
1031
0
        zend_value_error("Invalid format type %c", type);
1032
0
        RETURN_THROWS();
1033
0
    }
1034
1035
1036
    /* Do actual unpacking */
1037
0
    for (i = 0; i != repetitions; i++ ) {
1038
1039
      /* Each repetition costs a name and a value, so stop once over the limit. */
1040
0
      if (ZEND_HAS_OOM()) {
1041
0
        RETURN_OOM();
1042
0
      }
1043
1044
0
      if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
1045
0
        php_error_docref(NULL, E_WARNING, "Type %c: integer overflow", type);
1046
0
        zend_array_destroy(Z_ARR_P(return_value));
1047
0
        RETURN_FALSE;
1048
0
      }
1049
1050
0
      if ((inputpos + size) <= inputlen) {
1051
1052
0
        zend_string* real_name;
1053
0
        zend_long long_key = 0;
1054
0
        zval val;
1055
1056
0
        if (namelen == 0) {
1057
0
          real_name = NULL;
1058
0
          long_key = i + 1;
1059
0
        } else if (repetitions == 1) {
1060
          /* Use a part of the formatarg argument directly as the name. */
1061
0
          real_name = zend_string_init_fast(name, namelen);
1062
0
        } else {
1063
          /* Need to add the 1-based element number to the name */
1064
0
          char buf[MAX_LENGTH_OF_LONG + 1];
1065
0
          char *res = zend_print_ulong_to_buf(buf + sizeof(buf) - 1, i+1);
1066
0
          size_t digits = buf + sizeof(buf) - 1 - res;
1067
0
          real_name = zend_string_concat2(name, namelen, res, digits);
1068
0
        }
1069
1070
0
        switch (type) {
1071
0
          case 'a': {
1072
            /* a will not strip any trailing whitespace or null padding */
1073
0
            zend_long len = inputlen - inputpos;  /* Remaining string */
1074
1075
            /* If size was given take minimum of len and size */
1076
0
            if ((size >= 0) && (len > size)) {
1077
0
              len = size;
1078
0
            }
1079
1080
0
            size = len;
1081
1082
0
            ZVAL_STRINGL(&val, &input[inputpos], len);
1083
0
            break;
1084
0
          }
1085
0
          case 'A': {
1086
            /* A will strip any trailing whitespace */
1087
0
            zend_long len = inputlen - inputpos;  /* Remaining string */
1088
1089
            /* If size was given take minimum of len and size */
1090
0
            if ((size >= 0) && (len > size)) {
1091
0
              len = size;
1092
0
            }
1093
1094
0
            size = len;
1095
1096
            /* Remove trailing white space and nulls chars from unpacked data */
1097
0
            while (--len >= 0) {
1098
0
              if (input[inputpos + len] != '\0'
1099
0
                && input[inputpos + len] != ' '
1100
0
                && input[inputpos + len] != '\t'
1101
0
                && input[inputpos + len] != '\r'
1102
0
                && input[inputpos + len] != '\n'
1103
0
              )
1104
0
                break;
1105
0
            }
1106
1107
0
            ZVAL_STRINGL(&val, &input[inputpos], len + 1);
1108
0
            break;
1109
0
          }
1110
          /* New option added for Z to remain in-line with the Perl implementation */
1111
0
          case 'Z': {
1112
            /* Z will strip everything after the first null character */
1113
0
            zend_long s,
1114
0
               len = inputlen - inputpos; /* Remaining string */
1115
1116
            /* If size was given take minimum of len and size */
1117
0
            if ((size >= 0) && (len > size)) {
1118
0
              len = size;
1119
0
            }
1120
1121
0
            size = len;
1122
1123
            /* Remove everything after the first null */
1124
0
            for (s=0 ; s < len ; s++) {
1125
0
              if (input[inputpos + s] == '\0')
1126
0
                break;
1127
0
            }
1128
0
            len = s;
1129
1130
0
            ZVAL_STRINGL(&val, &input[inputpos], len);
1131
0
            break;
1132
0
          }
1133
1134
1135
0
          case 'h':
1136
0
          case 'H': {
1137
0
            zend_long len = (inputlen - inputpos) * 2;  /* Remaining */
1138
0
            int nibbleshift = (type == 'h') ? 0 : 4;
1139
0
            int first = 1;
1140
0
            zend_string *buf;
1141
0
            zend_long ipos, opos;
1142
1143
1144
0
            if (size > INT_MAX / 2) {
1145
0
              if (real_name) {
1146
0
                zend_string_release_ex(real_name, false);
1147
0
              }
1148
0
              zend_argument_value_error(1, "repeater must be less than or equal to %d", INT_MAX / 2);
1149
0
              RETURN_THROWS();
1150
0
            }
1151
1152
            /* If size was given take minimum of len and size */
1153
0
            if (size >= 0 && len > (size * 2)) {
1154
0
              len = size * 2;
1155
0
            }
1156
1157
0
            if (len > 0 && argb > 0) {
1158
0
              len -= argb % 2;
1159
0
            }
1160
1161
0
            buf = zend_string_alloc(len, 0);
1162
1163
0
            for (ipos = opos = 0; opos < len; opos++) {
1164
0
              char cc = (input[inputpos + ipos] >> nibbleshift) & 0xf;
1165
1166
0
              if (cc < 10) {
1167
0
                cc += '0';
1168
0
              } else {
1169
0
                cc += 'a' - 10;
1170
0
              }
1171
1172
0
              ZSTR_VAL(buf)[opos] = cc;
1173
0
              nibbleshift = (nibbleshift + 4) & 7;
1174
1175
0
              if (first-- == 0) {
1176
0
                ipos++;
1177
0
                first = 1;
1178
0
              }
1179
0
            }
1180
1181
0
            ZSTR_VAL(buf)[len] = '\0';
1182
1183
0
            ZVAL_STR(&val, buf);
1184
0
            break;
1185
0
          }
1186
1187
0
          case 'c':   /* signed */
1188
0
          case 'C': { /* unsigned */
1189
0
            uint8_t x = input[inputpos];
1190
0
            zend_long v = (type == 'c') ? (int8_t) x : x;
1191
1192
0
            ZVAL_LONG(&val, v);
1193
0
            break;
1194
0
          }
1195
1196
0
          case 's':   /* signed, machine endian or explicit */
1197
0
          case 'S':   /* unsigned, machine endian or explicit */
1198
0
          case 'n':   /* unsigned big endian     */
1199
0
          case 'v': { /* unsigned little endian  */
1200
0
            zend_long v = 0;
1201
0
            uint16_t x = *((unaligned_uint16_t*) &input[inputpos]);
1202
1203
0
            bool need_swap = false;
1204
0
            if (type == 'n') {
1205
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1206
0
            } else if (type == 'v') {
1207
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1208
0
            } else if (endian == PHP_LITTLE_ENDIAN) {
1209
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1210
0
            } else if (endian == PHP_BIG_ENDIAN) {
1211
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1212
0
            }
1213
1214
0
            if (need_swap) {
1215
0
              x = php_pack_reverse_int16(x);
1216
0
            }
1217
1218
0
            if (type == 's') {
1219
0
              v = (int16_t) x;
1220
0
            } else {
1221
0
              v = x;
1222
0
            }
1223
1224
0
            ZVAL_LONG(&val, v);
1225
0
            break;
1226
0
          }
1227
1228
0
          case 'i':   /* signed integer, machine size, machine endian */
1229
0
          case 'I': { /* unsigned integer, machine size, machine endian */
1230
0
            zend_long v;
1231
0
            if (type == 'i') {
1232
0
              int x = *((unaligned_int*) &input[inputpos]);
1233
0
              v = x;
1234
0
            } else {
1235
0
              unsigned int x = *((unaligned_uint*) &input[inputpos]);
1236
0
              v = x;
1237
0
            }
1238
1239
0
            ZVAL_LONG(&val, v);
1240
0
            break;
1241
0
          }
1242
1243
0
          case 'l':   /* signed, machine endian or explicit */
1244
0
          case 'L':   /* unsigned, machine endian or explicit */
1245
0
          case 'N':   /* unsigned big endian     */
1246
0
          case 'V': { /* unsigned little endian  */
1247
0
            zend_long v = 0;
1248
0
            uint32_t x = *((unaligned_uint32_t*) &input[inputpos]);
1249
1250
0
            bool need_swap = false;
1251
0
            if (type == 'N') {
1252
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1253
0
            } else if (type == 'V') {
1254
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1255
0
            } else if (endian == PHP_LITTLE_ENDIAN) {
1256
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1257
0
            } else if (endian == PHP_BIG_ENDIAN) {
1258
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1259
0
            }
1260
1261
0
            if (need_swap) {
1262
0
              x = php_pack_reverse_int32(x);
1263
0
            }
1264
1265
0
            if (type == 'l') {
1266
0
              v = (int32_t) x;
1267
0
            } else {
1268
0
              v = (zend_long) x;
1269
0
            }
1270
1271
0
            ZVAL_LONG(&val, v);
1272
0
            break;
1273
0
          }
1274
1275
0
#if SIZEOF_ZEND_LONG > 4
1276
0
          case 'q':   /* signed, machine endian or explicit */
1277
0
          case 'Q':   /* unsigned, machine endian or explicit */
1278
0
          case 'J':   /* unsigned big endian     */
1279
0
          case 'P': { /* unsigned little endian  */
1280
0
            zend_long v = 0;
1281
0
            uint64_t x = *((unaligned_uint64_t*) &input[inputpos]);
1282
1283
0
            bool need_swap = false;
1284
0
            if (type == 'J') {
1285
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1286
0
            } else if (type == 'P') {
1287
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1288
0
            } else if (endian == PHP_LITTLE_ENDIAN) {
1289
0
              need_swap = !MACHINE_LITTLE_ENDIAN;
1290
0
            } else if (endian == PHP_BIG_ENDIAN) {
1291
0
              need_swap = MACHINE_LITTLE_ENDIAN;
1292
0
            }
1293
1294
0
            if (need_swap) {
1295
0
              x = php_pack_reverse_int64(x);
1296
0
            }
1297
1298
0
            if (type == 'q') {
1299
0
              v = (int64_t) x;
1300
0
            } else {
1301
0
              v = x;
1302
0
            }
1303
1304
0
            ZVAL_LONG(&val, v);
1305
0
            break;
1306
0
          }
1307
0
#endif
1308
1309
0
          case 'f': /* float */
1310
0
          case 'g': /* little endian float*/
1311
0
          case 'G': /* big endian float*/
1312
0
          {
1313
0
            float v;
1314
1315
0
            if (type == 'g' || endian == PHP_LITTLE_ENDIAN) {
1316
0
              v = php_pack_parse_float(1, &input[inputpos]);
1317
0
            } else if (type == 'G' || endian == PHP_BIG_ENDIAN) {
1318
0
              v = php_pack_parse_float(0, &input[inputpos]);
1319
0
            } else {
1320
0
              memcpy(&v, &input[inputpos], sizeof(float));
1321
0
            }
1322
1323
0
            ZVAL_DOUBLE(&val, v);
1324
0
            break;
1325
0
          }
1326
1327
1328
0
          case 'd': /* double */
1329
0
          case 'e': /* little endian float */
1330
0
          case 'E': /* big endian float */
1331
0
          {
1332
0
            double v;
1333
0
            if (type == 'e' || endian == PHP_LITTLE_ENDIAN) {
1334
0
              v = php_pack_parse_double(1, &input[inputpos]);
1335
0
            } else if (type == 'E' || endian == PHP_BIG_ENDIAN) {
1336
0
              v = php_pack_parse_double(0, &input[inputpos]);
1337
0
            } else {
1338
0
              memcpy(&v, &input[inputpos], sizeof(double));
1339
0
            }
1340
1341
0
            ZVAL_DOUBLE(&val, v);
1342
0
            break;
1343
0
          }
1344
1345
0
          case 'x':
1346
            /* Do nothing with input, just skip it */
1347
0
            goto no_output;
1348
1349
0
          case 'X':
1350
0
            if (inputpos < size) {
1351
0
              inputpos = -size;
1352
0
              i = repetitions - 1;    /* Break out of for loop */
1353
1354
0
              if (repetitions >= 0) {
1355
0
                php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1356
0
              }
1357
0
            }
1358
0
            goto no_output;
1359
1360
0
          case '@':
1361
0
            if (repetitions <= inputlen) {
1362
0
              inputpos = repetitions;
1363
0
            } else {
1364
0
              php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1365
0
            }
1366
1367
0
            i = repetitions - 1;  /* Done, break out of for loop */
1368
0
            goto no_output;
1369
0
        }
1370
1371
0
        if (real_name) {
1372
0
          zend_symtable_update(Z_ARRVAL_P(return_value), real_name, &val);
1373
0
        } else {
1374
0
          zend_hash_index_update(Z_ARRVAL_P(return_value), long_key, &val);
1375
0
        }
1376
1377
0
no_output:
1378
0
        if (real_name) {
1379
0
          zend_string_release_ex(real_name, false);
1380
0
        }
1381
1382
0
        inputpos += size;
1383
0
        if (inputpos < 0) {
1384
0
          if (size != -1) { /* only print warning if not working with * */
1385
0
            php_error_docref(NULL, E_WARNING, "Type %c: outside of string", type);
1386
0
          }
1387
0
          inputpos = 0;
1388
0
        }
1389
0
      } else if (repetitions < 0) {
1390
        /* Reached end of input for '*' repeater */
1391
0
        break;
1392
0
      } else {
1393
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");
1394
0
        zend_array_destroy(Z_ARR_P(return_value));
1395
0
        RETURN_FALSE;
1396
0
      }
1397
0
    }
1398
1399
0
    if (formatlen > 0) {
1400
0
      formatlen--;  /* Skip '/' separator, does no harm if inputlen == 0 */
1401
0
      format++;
1402
0
    }
1403
0
  }
1404
0
}
1405
/* }}} */