Add sub-controls for Hack array compat runtime checks
[hiphop-php.git] / hphp / runtime / base / zend-pack.cpp
blob3d2ebfb884d76055b410d6991df6e9d9fbabe95e
1 /*
2 +----------------------------------------------------------------------+
3 | HipHop for PHP |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
6 | Copyright (c) 1998-2010 Zend Technologies Ltd. (http://www.zend.com) |
7 +----------------------------------------------------------------------+
8 | This source file is subject to version 2.00 of the Zend license, |
9 | that is bundled with this package in the file LICENSE, and is |
10 | available through the world-wide-web at the following url: |
11 | http://www.zend.com/license/2_00.txt. |
12 | If you did not receive a copy of the Zend license and are unable to |
13 | obtain it through the world-wide-web, please send a note to |
14 | license@zend.com so we can mail you a copy immediately. |
15 +----------------------------------------------------------------------+
18 #include "hphp/runtime/base/zend-pack.h"
19 #include "hphp/runtime/base/builtin-functions.h"
20 #include "hphp/util/tiny-vector.h"
22 #include <algorithm>
24 namespace HPHP {
26 #define INC_OUTPUTPOS(a,b) \
27 if ((a) < 0 || ((INT_MAX - outputpos)/((int)b)) < (a)) { \
28 throw_invalid_argument \
29 ("Type %c: integer overflow in format string", code); \
30 return false; \
31 } \
32 outputpos += (a)*(b);
34 ///////////////////////////////////////////////////////////////////////////////
36 ZendPack::ZendPack() {
37 int machine_endian_check = 1;
38 int64_t i;
40 machine_little_endian = ((char *)&machine_endian_check)[0];
42 if (machine_little_endian) {
43 /* Where to get lo to hi bytes from */
44 byte_map[0] = 0;
46 for (i = 0; i < (int)sizeof(int); i++) {
47 int_map[i] = i;
50 machine_endian_short_map[0] = 0;
51 machine_endian_short_map[1] = 1;
52 big_endian_short_map[0] = 1;
53 big_endian_short_map[1] = 0;
54 little_endian_short_map[0] = 0;
55 little_endian_short_map[1] = 1;
57 machine_endian_int32_map[0] = 0;
58 machine_endian_int32_map[1] = 1;
59 machine_endian_int32_map[2] = 2;
60 machine_endian_int32_map[3] = 3;
61 big_endian_int32_map[0] = 3;
62 big_endian_int32_map[1] = 2;
63 big_endian_int32_map[2] = 1;
64 big_endian_int32_map[3] = 0;
65 little_endian_int32_map[0] = 0;
66 little_endian_int32_map[1] = 1;
67 little_endian_int32_map[2] = 2;
68 little_endian_int32_map[3] = 3;
70 machine_endian_int64_map[0] = 0;
71 machine_endian_int64_map[1] = 1;
72 machine_endian_int64_map[2] = 2;
73 machine_endian_int64_map[3] = 3;
74 machine_endian_int64_map[4] = 4;
75 machine_endian_int64_map[5] = 5;
76 machine_endian_int64_map[6] = 6;
77 machine_endian_int64_map[7] = 7;
78 big_endian_int64_map[0] = 7;
79 big_endian_int64_map[1] = 6;
80 big_endian_int64_map[2] = 5;
81 big_endian_int64_map[3] = 4;
82 big_endian_int64_map[4] = 3;
83 big_endian_int64_map[5] = 2;
84 big_endian_int64_map[6] = 1;
85 big_endian_int64_map[7] = 0;
86 little_endian_int64_map[0] = 0;
87 little_endian_int64_map[1] = 1;
88 little_endian_int64_map[2] = 2;
89 little_endian_int64_map[3] = 3;
90 little_endian_int64_map[4] = 4;
91 little_endian_int64_map[5] = 5;
92 little_endian_int64_map[6] = 6;
93 little_endian_int64_map[7] = 7;
94 } else {
95 int64_t size = sizeof(int64_t);
97 /* Where to get hi to lo bytes from */
98 byte_map[0] = size - 1;
100 for (i = 0; i < (int)sizeof(int); i++) {
101 int_map[i] = size - (sizeof(int64_t) - i);
104 machine_endian_short_map[0] = size - 2;
105 machine_endian_short_map[1] = size - 1;
106 big_endian_short_map[0] = size - 2;
107 big_endian_short_map[1] = size - 1;
108 little_endian_short_map[0] = size - 1;
109 little_endian_short_map[1] = size - 2;
111 machine_endian_int32_map[0] = size - 4;
112 machine_endian_int32_map[1] = size - 3;
113 machine_endian_int32_map[2] = size - 2;
114 machine_endian_int32_map[3] = size - 1;
115 big_endian_int32_map[0] = size - 4;
116 big_endian_int32_map[1] = size - 3;
117 big_endian_int32_map[2] = size - 2;
118 big_endian_int32_map[3] = size - 1;
119 little_endian_int32_map[0] = size - 1;
120 little_endian_int32_map[1] = size - 2;
121 little_endian_int32_map[2] = size - 3;
122 little_endian_int32_map[3] = size - 4;
124 machine_endian_int64_map[0] = size - 8;
125 machine_endian_int64_map[1] = size - 7;
126 machine_endian_int64_map[2] = size - 6;
127 machine_endian_int64_map[3] = size - 5;
128 machine_endian_int64_map[4] = size - 4;
129 machine_endian_int64_map[5] = size - 3;
130 machine_endian_int64_map[6] = size - 2;
131 machine_endian_int64_map[7] = size - 1;
132 big_endian_int64_map[0] = size - 8;
133 big_endian_int64_map[1] = size - 7;
134 big_endian_int64_map[2] = size - 6;
135 big_endian_int64_map[3] = size - 5;
136 big_endian_int64_map[4] = size - 4;
137 big_endian_int64_map[5] = size - 3;
138 big_endian_int64_map[6] = size - 2;
139 big_endian_int64_map[7] = size - 1;
140 little_endian_int64_map[0] = size - 1;
141 little_endian_int64_map[1] = size - 2;
142 little_endian_int64_map[2] = size - 3;
143 little_endian_int64_map[3] = size - 4;
144 little_endian_int64_map[4] = size - 5;
145 little_endian_int64_map[5] = size - 6;
146 little_endian_int64_map[6] = size - 7;
147 little_endian_int64_map[7] = size - 8;
151 void ZendPack::pack(const Variant& val, int64_t size, int64_t *map,
152 char *output) {
153 int64_t n = val.toInt64();
154 char *v = (char*)&n;
155 for (int64_t i = 0; i < size; i++) {
156 *output++ = v[map[i]];
160 Variant ZendPack::pack(const String& fmt, const Array& argv) {
161 /* Preprocess format into formatcodes and formatargs */
162 req::TinyVector<char, 64> formatcodes; // up to 64 codes on the stack
163 req::TinyVector<int, 64> formatargs;
164 int argc = argv.size();
166 const char *format = fmt.c_str();
167 int formatlen = fmt.size();
168 int currentarg = 0;
169 for (int i = 0; i < formatlen; ) {
170 char code = format[i++];
171 int arg = 1;
173 /* Handle format arguments if any */
174 if (i < formatlen) {
175 char c = format[i];
177 if (c == '*') {
178 arg = -1;
179 i++;
181 else if (c >= '0' && c <= '9') {
182 arg = atoi(&format[i]);
184 while (format[i] >= '0' && format[i] <= '9' && i < formatlen) {
185 i++;
190 /* Handle special arg '*' for all codes and check argv overflows */
191 switch ((int) code) {
192 /* Never uses any args */
193 case 'x':
194 case 'X':
195 case '@':
196 if (arg < 0) {
197 throw_invalid_argument("Type %c: '*' ignored", code);
198 arg = 1;
200 break;
202 /* Always uses one arg */
203 case 'a':
204 case 'A':
205 case 'h':
206 case 'H':
207 case 'Z':
208 if (currentarg >= argc) {
209 throw_invalid_argument("Type %c: not enough arguments", code);
210 return false;
213 if (arg < 0) {
214 arg = argv[currentarg].toString().size();
215 //add one, because Z is always NUL-terminated
216 if (code == 'Z') {
217 arg++;
221 currentarg++;
222 break;
224 /* Use as many args as specified */
225 case 'q':
226 case 'Q':
227 case 'J':
228 case 'P':
229 case 'c':
230 case 'C':
231 case 's':
232 case 'S':
233 case 'i':
234 case 'I':
235 case 'l':
236 case 'L':
237 case 'n':
238 case 'N':
239 case 'v':
240 case 'V':
241 case 'f':
242 case 'd':
243 if (arg < 0) {
244 arg = argc - currentarg;
247 currentarg += arg;
249 if (currentarg > argc) {
250 throw_invalid_argument("Type %c: too few arguments", code);
251 return false;
253 break;
255 default:
256 throw_invalid_argument("Type %c: unknown format code", code);
257 return false;
260 formatcodes.push_back(code);
261 formatargs.push_back(arg);
264 if (currentarg < argc) {
265 throw_invalid_argument("%d arguments unused", (argc - currentarg));
268 int outputpos = 0, outputsize = 0;
269 /* Calculate output length and upper bound while processing*/
270 for (int i = 0; i < (int)formatcodes.size(); i++) {
271 int code = (int) formatcodes[i];
272 int arg = formatargs[i];
274 switch ((int) code) {
275 case 'h':
276 case 'H':
277 INC_OUTPUTPOS((arg + (arg % 2)) / 2,1); /* 4 bit per arg */
278 break;
280 case 'a':
281 case 'A':
282 case 'c':
283 case 'C':
284 case 'x':
285 case 'Z':
286 INC_OUTPUTPOS(arg,1); /* 8 bit per arg */
287 break;
289 case 's':
290 case 'S':
291 case 'n':
292 case 'v':
293 INC_OUTPUTPOS(arg,2); /* 16 bit per arg */
294 break;
296 case 'i':
297 case 'I':
298 INC_OUTPUTPOS(arg,sizeof(int));
299 break;
301 case 'l':
302 case 'L':
303 case 'N':
304 case 'V':
305 INC_OUTPUTPOS(arg,4); /* 32 bit per arg */
306 break;
307 case 'q':
308 case 'Q':
309 case 'J':
310 case 'P':
311 INC_OUTPUTPOS(arg,8); /* 64 bit per arg */
312 break;
314 case 'f':
315 INC_OUTPUTPOS(arg,sizeof(float));
316 break;
318 case 'd':
319 INC_OUTPUTPOS(arg,sizeof(double));
320 break;
322 case 'X':
323 outputpos -= arg;
325 if (outputpos < 0) {
326 throw_invalid_argument("Type %c: outside of string", code);
327 outputpos = 0;
329 break;
331 case '@':
332 outputpos = arg;
333 break;
336 if (outputsize < outputpos) {
337 outputsize = outputpos;
341 String str = String(outputsize, ReserveString);
342 char *output = str.mutableData();
343 outputpos = 0;
344 currentarg = 0;
346 /* Do actual packing */
347 for (int i = 0; i < (int)formatcodes.size(); i++) {
348 int code = (int) formatcodes[i];
349 int arg = formatargs[i];
350 String val;
351 const char *s;
352 int slen;
354 switch ((int) code) {
355 case 'a':
356 case 'A':
357 case 'Z': {
358 int arg_cp = (code != 'Z') ? arg : std::max(0, arg - 1);
359 memset(&output[outputpos], (code != 'A') ? '\0' : ' ', arg);
360 val = argv[currentarg++].toString();
361 s = val.c_str();
362 slen = val.size();
363 memcpy(&output[outputpos], s, (slen < arg_cp) ? slen : arg_cp);
364 outputpos += arg;
366 break;
368 case 'h':
369 case 'H': {
370 int nibbleshift = (code == 'h') ? 0 : 4;
371 int first = 1;
372 const char *v;
374 val = argv[currentarg++].toString();
375 v = val.data();
376 slen = val.size();
377 outputpos--;
378 if (arg > slen) {
379 throw_invalid_argument
380 ("Type %c: not enough characters in string", code);
381 arg = slen;
384 while (arg-- > 0) {
385 char n = *v++;
387 if (n >= '0' && n <= '9') {
388 n -= '0';
389 } else if (n >= 'A' && n <= 'F') {
390 n -= ('A' - 10);
391 } else if (n >= 'a' && n <= 'f') {
392 n -= ('a' - 10);
393 } else {
394 throw_invalid_argument("Type %c: illegal hex digit %c", code, n);
395 n = 0;
398 if (first--) {
399 output[++outputpos] = 0;
400 } else {
401 first = 1;
404 output[outputpos] |= (n << nibbleshift);
405 nibbleshift = (nibbleshift + 4) & 7;
408 outputpos++;
409 break;
412 case 'c':
413 case 'C':
414 while (arg-- > 0) {
415 pack(argv[currentarg++], 1, byte_map, &output[outputpos]);
416 outputpos++;
418 break;
420 case 's':
421 case 'S':
422 case 'n':
423 case 'v': {
424 int64_t *map = machine_endian_short_map;
426 if (code == 'n') {
427 map = big_endian_short_map;
428 } else if (code == 'v') {
429 map = little_endian_short_map;
432 while (arg-- > 0) {
433 pack(argv[currentarg++], 2, map, &output[outputpos]);
434 outputpos += 2;
436 break;
439 case 'i':
440 case 'I':
441 while (arg-- > 0) {
442 pack(argv[currentarg++], sizeof(int), int_map, &output[outputpos]);
443 outputpos += sizeof(int);
445 break;
447 case 'l':
448 case 'L':
449 case 'N':
450 case 'V': {
451 int64_t *map = machine_endian_int32_map;
453 if (code == 'N') {
454 map = big_endian_int32_map;
455 } else if (code == 'V') {
456 map = little_endian_int32_map;
459 while (arg-- > 0) {
460 pack(argv[currentarg++], 4, map, &output[outputpos]);
461 outputpos += 4;
463 break;
466 case 'q':
467 case 'Q':
468 case 'J':
469 case 'P': {
470 int64_t *map = machine_endian_int64_map;
471 if (code == 'J') {
472 map = big_endian_int64_map;
473 } else if (code == 'P') {
474 map = little_endian_int64_map;
477 while (arg-- > 0) {
478 pack(argv[currentarg++], 8, map, &output[outputpos]);
479 outputpos += 8;
481 break;
484 case 'f': {
485 float v;
487 while (arg-- > 0) {
488 v = argv[currentarg++].toDouble();
489 memcpy(&output[outputpos], &v, sizeof(v));
490 outputpos += sizeof(v);
492 break;
495 case 'd': {
496 double v;
498 while (arg-- > 0) {
499 v = argv[currentarg++].toDouble();
500 memcpy(&output[outputpos], &v, sizeof(v));
501 outputpos += sizeof(v);
503 break;
506 case 'x':
507 memset(&output[outputpos], '\0', arg);
508 outputpos += arg;
509 break;
511 case 'X':
512 outputpos -= arg;
514 if (outputpos < 0) {
515 outputpos = 0;
517 break;
519 case '@':
520 if (arg > outputpos) {
521 memset(&output[outputpos], '\0', arg - outputpos);
523 outputpos = arg;
524 break;
528 str.setSize(outputpos);
529 return str;
532 int64_t ZendPack::unpack(const char *data, int64_t size, int issigned,
533 int64_t *map) {
534 int64_t result;
535 char *cresult = (char *) &result;
536 int i;
538 result = issigned ? -1 : 0;
540 for (i = 0; i < size; i++) {
541 cresult[map[i]] = *data++;
544 return result;
547 Variant ZendPack::unpack(const String& fmt, const String& data) {
548 const char *format = fmt.c_str();
549 int formatlen = fmt.size();
550 const char *input = data.c_str();
551 int inputlen = data.size();
552 int inputpos = 0;
554 Array ret = Array::Create();
555 while (formatlen-- > 0) {
556 char type = *(format++);
557 int arg = 1, argb;
558 const char *name;
559 int namelen;
560 int size=0;
562 /* Handle format arguments if any */
563 if (formatlen > 0) {
564 char c = *format;
566 if (c >= '0' && c <= '9') {
567 arg = atoi(format);
569 while (formatlen > 0 && *format >= '0' && *format <= '9') {
570 format++;
571 formatlen--;
573 } else if (c == '*') {
574 arg = -1;
575 format++;
576 formatlen--;
580 /* Get of new value in array */
581 name = format;
582 argb = arg;
584 while (formatlen > 0 && *format != '/') {
585 formatlen--;
586 format++;
589 namelen = format - name;
591 if (namelen > 200)
592 namelen = 200;
594 switch ((int) type) {
595 /* Never use any input */
596 case 'X':
597 size = -1;
598 if (arg < 0) {
599 throw_invalid_argument("Type %c: '*' ignored", type);
600 arg = 1;
602 break;
604 case '@':
605 size = 0;
606 break;
608 case 'a':
609 case 'A':
610 case 'Z':
611 size = arg;
612 arg = 1;
613 break;
615 case 'h':
616 case 'H':
617 size = (arg > 0) ? (arg + (arg % 2)) / 2 : arg;
618 arg = 1;
619 break;
621 /* Use 1 byte of input */
622 case 'c':
623 case 'C':
624 case 'x':
625 size = 1;
626 break;
628 /* Use 2 bytes of input */
629 case 's':
630 case 'S':
631 case 'n':
632 case 'v':
633 size = 2;
634 break;
636 /* Use machine dependent bytes of input */
637 case 'i':
638 case 'I':
639 size = sizeof(int);
640 break;
642 /* Use 4 bytes of input */
643 case 'l':
644 case 'L':
645 case 'N':
646 case 'V':
647 size = 4;
648 break;
650 /* Use 8 bytes of input */
651 case 'q':
652 case 'Q':
653 case 'J':
654 case 'P':
655 size = 8;
656 break;
657 /* Use sizeof(float) bytes of input */
658 case 'f':
659 size = sizeof(float);
660 break;
662 /* Use sizeof(double) bytes of input */
663 case 'd':
664 size = sizeof(double);
665 break;
667 default:
668 throw_invalid_argument("Invalid format type %c", type);
669 return false;
672 /* Do actual unpacking */
673 for (int i = 0; i != arg; i++ ) {
674 /* Space for name + number, safe as namelen is ensured <= 200 */
675 char n[256];
677 if (arg != 1 || namelen == 0) {
678 /* Need to add element number to name */
679 snprintf(n, sizeof(n), "%.*s%d", namelen, name, i + 1);
680 } else {
681 /* Truncate name to next format code or end of string */
682 snprintf(n, sizeof(n), "%.*s", namelen, name);
685 if (size != 0 && size != -1 && INT_MAX - size + 1 < inputpos) {
686 throw_invalid_argument("Type %c: integer overflow", type);
687 inputpos = 0;
690 if ((inputpos + size) <= inputlen) {
691 switch ((int) type) {
692 case 'a':
693 case 'A':
694 case 'Z': {
695 int len = inputlen - inputpos; /* Remaining string */
697 /* If size was given take minimum of len and size */
698 if ((size >= 0) && (len > size)) {
699 len = size;
702 size = len;
704 /* A will strip any trailing whitespace */
705 if (type == 'A')
707 char padn = '\0'; char pads = ' '; char padt = '\t';
708 char padc = '\r'; char padl = '\n';
709 while (--len >= 0) {
710 if (input[inputpos + len] != padn
711 && input[inputpos + len] != pads
712 && input[inputpos + len] != padt
713 && input[inputpos + len] != padc
714 && input[inputpos + len] != padl
716 break;
719 /* Remove everything after the first null */
720 if (type=='Z') {
721 int s;
722 for (s=0 ; s < len ; s++) {
723 if (input[inputpos + s] == '\0')
724 break;
726 len = s;
729 /*only A is \0 terminated*/
730 if (type=='A')
731 len++;
733 ret.set(String(n, CopyString),
734 String(input + inputpos, len, CopyString));
735 break;
738 case 'h':
739 case 'H': {
740 int len = (inputlen - inputpos) * 2; /* Remaining */
741 int nibbleshift = (type == 'h') ? 0 : 4;
742 int first = 1;
743 char *buf;
744 int ipos, opos;
746 /* If size was given take minimum of len and size */
747 if (size >= 0 && len > (size * 2)) {
748 len = size * 2;
751 if (argb > 0) {
752 len -= argb % 2;
755 String s = String(len, ReserveString);
756 buf = s.mutableData();
758 for (ipos = opos = 0; opos < len; opos++) {
759 char c = (input[inputpos + ipos] >> nibbleshift) & 0xf;
761 if (c < 10) {
762 c += '0';
763 } else {
764 c += 'a' - 10;
767 buf[opos] = c;
768 nibbleshift = (nibbleshift + 4) & 7;
770 if (first-- == 0) {
771 ipos++;
772 first = 1;
776 s.setSize(len);
777 ret.set(String(n, CopyString), s);
778 break;
781 case 'c':
782 case 'C': {
783 int issigned = (type == 'c') ? (input[inputpos] & 0x80) : 0;
784 ret.set(String(n, CopyString),
785 unpack(&input[inputpos], 1, issigned, byte_map));
786 break;
789 case 's':
790 case 'S':
791 case 'n':
792 case 'v': {
793 int issigned = 0;
794 int64_t *map = machine_endian_short_map;
796 if (type == 's') {
797 issigned = input[inputpos + (machine_little_endian ? 1 : 0)] &
798 0x80;
799 } else if (type == 'n') {
800 map = big_endian_short_map;
801 } else if (type == 'v') {
802 map = little_endian_short_map;
805 ret.set(String(n, CopyString),
806 unpack(&input[inputpos], 2, issigned, map));
807 break;
810 case 'i':
811 case 'I': {
812 int32_t v = 0;
813 int issigned = 0;
815 if (type == 'i') {
816 issigned = input[inputpos + (machine_little_endian ?
817 (sizeof(int) - 1) : 0)] & 0x80;
818 } else if (sizeof(int32_t) > 4 &&
819 (input[inputpos + machine_endian_int32_map[3]]
820 & 0x80) == 0x80) {
821 v = ~INT_MAX;
824 v |= unpack(&input[inputpos], sizeof(int), issigned, int_map);
825 if (type == 'i') {
826 ret.set(String(n, CopyString), v);
827 } else {
828 uint64_t u64 = uint32_t(v);
829 ret.set(String(n, CopyString), u64);
831 break;
834 case 'l':
835 case 'L':
836 case 'N':
837 case 'V': {
838 int issigned = 0;
839 int64_t *map = machine_endian_int32_map;
840 int64_t v = 0;
842 if (type == 'l' || type == 'L') {
843 issigned = input[inputpos + (machine_little_endian ? 3 : 0)]
844 & 0x80;
845 } else if (type == 'N') {
846 issigned = input[inputpos] & 0x80;
847 map = big_endian_int32_map;
848 } else if (type == 'V') {
849 issigned = input[inputpos + 3] & 0x80;
850 map = little_endian_int32_map;
853 if (sizeof(int32_t) > 4 && issigned) {
854 v = ~INT_MAX;
857 v |= unpack(&input[inputpos], 4, issigned, map);
858 if (type == 'l') {
859 ret.set(String(n, CopyString), v);
860 } else {
861 uint64_t u64 = uint32_t(v);
862 ret.set(String(n, CopyString), u64);
864 break;
867 case 'q':
868 case 'Q':
869 case 'J':
870 case 'P': {
871 int issigned = 0;
872 int64_t *map = machine_endian_int64_map;
873 int64_t v = 0;
874 if (type == 'q' || type == 'Q') {
875 issigned = input[inputpos + (machine_little_endian ? 7 : 0)] & 0x80;
876 } else if (type == 'J') {
877 issigned = input[inputpos] & 0x80;
878 map = big_endian_int64_map;
879 } else if (type == 'P') {
880 issigned = input[inputpos + 7] & 0x80;
881 map = little_endian_int64_map;
884 v = unpack(&input[inputpos], 8, issigned, map);
886 if (type == 'q') {
887 ret.set(String(n, CopyString), v);
888 } else {
889 uint64_t u64 = uint64_t(v);
890 ret.set(String(n, CopyString), u64);
893 break;
896 case 'f': {
897 float v;
899 memcpy(&v, &input[inputpos], sizeof(float));
900 ret.set(String(n, CopyString), (double)v);
901 break;
904 case 'd': {
905 double v;
907 memcpy(&v, &input[inputpos], sizeof(double));
908 ret.set(String(n, CopyString), v);
909 break;
912 case 'x':
913 /* Do nothing with input, just skip it */
914 break;
916 case 'X':
917 if (inputpos < size) {
918 inputpos = -size;
919 i = arg - 1; /* Break out of for loop */
921 if (arg >= 0) {
922 throw_invalid_argument("Type %c: outside of string", type);
925 break;
927 case '@':
928 if (arg <= inputlen) {
929 inputpos = arg;
930 } else {
931 throw_invalid_argument("Type %c: outside of string", type);
934 i = arg - 1; /* Done, break out of for loop */
935 break;
938 inputpos += size;
939 if (inputpos < 0) {
940 if (size != -1) { /* only print warning if not working with * */
941 throw_invalid_argument("Type %c: outside of string", type);
943 inputpos = 0;
945 } else if (arg < 0) {
946 /* Reached end of input for '*' repeater */
947 break;
948 } else {
949 throw_invalid_argument
950 ("Type %c: not enough input, need %d, have %d",
951 type, size, inputlen - inputpos);
952 return false;
956 formatlen--; /* Skip '/' separator, does no harm if inputlen == 0 */
957 format++;
960 return ret;
963 ///////////////////////////////////////////////////////////////////////////////