xref: /php-src/ext/json/json_scanner.re (revision c8955c07)
1/*
2  +----------------------------------------------------------------------+
3  | Copyright (c) The PHP Group                                          |
4  +----------------------------------------------------------------------+
5  | This source file is subject to version 3.01 of the PHP license,      |
6  | that is bundled with this package in the file LICENSE, and is        |
7  | available through the world-wide-web at the following url:           |
8  | https://www.php.net/license/3_01.txt                                 |
9  | If you did not receive a copy of the PHP license and are unable to   |
10  | obtain it through the world-wide-web, please send a note to          |
11  | license@php.net so we can mail you a copy immediately.               |
12  +----------------------------------------------------------------------+
13  | Author: Jakub Zelenka <bukka@php.net>                                |
14  +----------------------------------------------------------------------+
15*/
16
17#include "php.h"
18#include "php_json_scanner.h"
19#include "php_json_scanner_defs.h"
20#include "php_json_parser.h"
21#include "json_parser.tab.h"
22
23#define	YYCTYPE     php_json_ctype
24#define	YYCURSOR    s->cursor
25#define	YYLIMIT     s->limit
26#define	YYMARKER    s->marker
27#define	YYCTXMARKER s->ctxmarker
28
29#define YYGETCONDITION()        s->state
30#define YYSETCONDITION(yystate) s->state = yystate
31
32#define	YYFILL(n)
33
34#define PHP_JSON_CONDITION_SET(condition) YYSETCONDITION(yyc##condition)
35#define PHP_JSON_CONDITION_GOTO(condition) goto yyc_##condition
36#define PHP_JSON_CONDITION_SET_AND_GOTO(condition) \
37	PHP_JSON_CONDITION_SET(condition); \
38	PHP_JSON_CONDITION_GOTO(condition)
39#define PHP_JSON_CONDITION_GOTO_STR_P2() \
40	do { \
41		if (s->utf8_invalid) { \
42			PHP_JSON_CONDITION_GOTO(STR_P2_BIN); \
43		} else { \
44			PHP_JSON_CONDITION_GOTO(STR_P2_UTF); \
45		} \
46	} while(0)
47
48
49#define PHP_JSON_SCANNER_COPY_ESC() php_json_scanner_copy_string(s, 0)
50#define PHP_JSON_SCANNER_COPY_UTF() php_json_scanner_copy_string(s, 5)
51#define PHP_JSON_SCANNER_COPY_UTF_SP() php_json_scanner_copy_string(s, 11)
52
53#define PHP_JSON_INT_MAX_LENGTH (MAX_LENGTH_OF_LONG - 1)
54
55
56static void php_json_scanner_copy_string(php_json_scanner *s, int esc_size)
57{
58	size_t len = s->cursor - s->str_start - esc_size - 1;
59	if (len) {
60		memcpy(s->pstr, s->str_start, len);
61		s->pstr += len;
62	}
63}
64
65static int php_json_hex_to_int(char code)
66{
67	if (code >= '0' && code <= '9') {
68		return code - '0';
69	} else if (code >= 'A' && code <= 'F') {
70		return code - ('A' - 10);
71	} else if (code >= 'a' && code <= 'f') {
72		return code - ('a' - 10);
73	} else {
74		/* this should never happened (just to suppress compiler warning) */
75		return -1;
76	}
77}
78
79static int php_json_ucs2_to_int_ex(php_json_scanner *s, int size, int start)
80{
81	int i, code = 0;
82	php_json_ctype *pc = s->cursor - start;
83	for (i = 0; i < size; i++) {
84		code |= php_json_hex_to_int(*(pc--)) << (i * 4);
85	}
86	return code;
87}
88
89static int php_json_ucs2_to_int(php_json_scanner *s, int size)
90{
91	return php_json_ucs2_to_int_ex(s, size, 1);
92}
93
94void php_json_scanner_init(php_json_scanner *s, const char *str, size_t str_len, int options)
95{
96	s->cursor = (php_json_ctype *) str;
97	s->limit = (php_json_ctype *) str + str_len;
98	s->options = options;
99	PHP_JSON_CONDITION_SET(JS);
100}
101
102int php_json_scan(php_json_scanner *s)
103{
104	ZVAL_NULL(&s->value);
105
106std:
107	s->token = s->cursor;
108
109/*!re2c
110	re2c:indent:top = 1;
111	re2c:yyfill:enable = 0;
112
113	DIGIT   = [0-9] ;
114	DIGITNZ = [1-9] ;
115	UINT    = "0" | ( DIGITNZ DIGIT* ) ;
116	INT     = "-"? UINT ;
117	HEX     = DIGIT | [a-fA-F] ;
118	HEXNZ   = DIGITNZ | [a-fA-F] ;
119	HEX7    = [0-7] ;
120	HEXC    = DIGIT | [a-cA-C] ;
121	FLOAT   = INT "." DIGIT+ ;
122	EXP     = ( INT | FLOAT ) [eE] [+-]? DIGIT+ ;
123	NL      = "\r"? "\n" ;
124	WS      = [ \t\r]+ ;
125	EOI     = "\000";
126	CTRL    = [\x00-\x1F] ;
127	UTF8T   = [\x80-\xBF] ;
128	UTF8_1  = [\x00-\x7F] ;
129	UTF8_2  = [\xC2-\xDF] UTF8T ;
130	UTF8_3A = "\xE0" [\xA0-\xBF] UTF8T ;
131	UTF8_3B = [\xE1-\xEC] UTF8T{2} ;
132	UTF8_3C = "\xED" [\x80-\x9F] UTF8T ;
133	UTF8_3D = [\xEE-\xEF] UTF8T{2} ;
134	UTF8_3  = UTF8_3A | UTF8_3B | UTF8_3C | UTF8_3D ;
135	UTF8_4A = "\xF0"[\x90-\xBF] UTF8T{2} ;
136	UTF8_4B = [\xF1-\xF3] UTF8T{3} ;
137	UTF8_4C = "\xF4" [\x80-\x8F] UTF8T{2} ;
138	UTF8_4  = UTF8_4A | UTF8_4B | UTF8_4C ;
139	UTF8    = UTF8_1 | UTF8_2 | UTF8_3 | UTF8_4 ;
140	ANY     = [^] ;
141	ESCPREF = "\\" ;
142	ESCSYM  = ( "\"" | "\\" | "/" | [bfnrt] ) ;
143	ESC     = ESCPREF ESCSYM ;
144	UTFSYM  = "u" ;
145	UTFPREF = ESCPREF UTFSYM ;
146	UCS2    = UTFPREF HEX{4} ;
147	UTF16_1 = UTFPREF "00" HEX7 HEX ;
148	UTF16_2 = UTFPREF "0" HEX7 HEX{2} ;
149	UTF16_3 = UTFPREF ( ( ( HEXC | [efEF] ) HEX ) | ( [dD] HEX7 ) ) HEX{2} ;
150	UTF16_4 = UTFPREF [dD] [89abAB] HEX{2} UTFPREF [dD] [c-fC-F] HEX{2} ;
151
152	<JS>"{"                  { return '{'; }
153	<JS>"}"                  { return '}'; }
154	<JS>"["                  { return '['; }
155	<JS>"]"                  { return ']'; }
156	<JS>":"                  { return ':'; }
157	<JS>","                  { return ','; }
158	<JS>"null"               {
159		ZVAL_NULL(&s->value);
160		return PHP_JSON_T_NUL;
161	}
162	<JS>"true"               {
163		ZVAL_TRUE(&s->value);
164		return PHP_JSON_T_TRUE;
165	}
166	<JS>"false"              {
167		ZVAL_FALSE(&s->value);
168		return PHP_JSON_T_FALSE;
169	}
170	<JS>INT                  {
171		bool bigint = 0, negative = s->token[0] == '-';
172		size_t digits = (size_t) (s->cursor - s->token - negative);
173		if (digits >= PHP_JSON_INT_MAX_LENGTH) {
174			if (digits == PHP_JSON_INT_MAX_LENGTH) {
175				int cmp = strncmp((char *) (s->token + negative), LONG_MIN_DIGITS, PHP_JSON_INT_MAX_LENGTH);
176				if (!(cmp < 0 || (cmp == 0 && negative))) {
177					bigint = 1;
178				}
179			} else {
180				bigint = 1;
181			}
182		}
183		if (!bigint) {
184			ZVAL_LONG(&s->value, ZEND_STRTOL((char *) s->token, NULL, 10));
185			return PHP_JSON_T_INT;
186		} else if (s->options & PHP_JSON_BIGINT_AS_STRING) {
187			ZVAL_STRINGL(&s->value, (char *) s->token, s->cursor - s->token);
188			return PHP_JSON_T_STRING;
189		} else {
190			ZVAL_DOUBLE(&s->value, zend_strtod((char *) s->token, NULL));
191			return PHP_JSON_T_DOUBLE;
192		}
193	}
194	<JS>FLOAT|EXP            {
195		ZVAL_DOUBLE(&s->value, zend_strtod((char *) s->token, NULL));
196		return PHP_JSON_T_DOUBLE;
197	}
198	<JS>NL|WS                { goto std; }
199	<JS>EOI                  {
200		if (s->limit < s->cursor) {
201			return PHP_JSON_T_EOI;
202		} else {
203			s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
204			return PHP_JSON_T_ERROR;
205		}
206	}
207	<JS>["]                  {
208		s->str_start = s->cursor;
209		s->str_esc = 0;
210		s->utf8_invalid = 0;
211		s->utf8_invalid_count = 0;
212		PHP_JSON_CONDITION_SET_AND_GOTO(STR_P1);
213	}
214	<JS>CTRL                 {
215		s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
216		return PHP_JSON_T_ERROR;
217	}
218	<JS>UTF8                 {
219		s->errcode = PHP_JSON_ERROR_SYNTAX;
220		return PHP_JSON_T_ERROR;
221	}
222	<JS>ANY                  {
223		s->errcode = PHP_JSON_ERROR_UTF8;
224		return PHP_JSON_T_ERROR;
225	}
226
227	<STR_P1>CTRL             {
228		s->errcode = PHP_JSON_ERROR_CTRL_CHAR;
229		return PHP_JSON_T_ERROR;
230	}
231	<STR_P1>UTF16_1          {
232		s->str_esc += 5;
233		PHP_JSON_CONDITION_GOTO(STR_P1);
234	}
235	<STR_P1>UTF16_2          {
236		s->str_esc += 4;
237		PHP_JSON_CONDITION_GOTO(STR_P1);
238	}
239	<STR_P1>UTF16_3          {
240		s->str_esc += 3;
241		PHP_JSON_CONDITION_GOTO(STR_P1);
242	}
243	<STR_P1>UTF16_4          {
244		s->str_esc += 8;
245		PHP_JSON_CONDITION_GOTO(STR_P1);
246	}
247	<STR_P1>UCS2             {
248		s->errcode = PHP_JSON_ERROR_UTF16;
249		return PHP_JSON_T_ERROR;
250	}
251	<STR_P1>ESC              {
252		s->str_esc++;
253		PHP_JSON_CONDITION_GOTO(STR_P1);
254	}
255	<STR_P1>ESCPREF           {
256		s->errcode = PHP_JSON_ERROR_SYNTAX;
257		return PHP_JSON_T_ERROR;
258	}
259	<STR_P1>["]              {
260		zend_string *str;
261		size_t len = s->cursor - s->str_start - s->str_esc - 1 + s->utf8_invalid_count;
262		if (len == 0) {
263			PHP_JSON_CONDITION_SET(JS);
264			ZVAL_EMPTY_STRING(&s->value);
265			return PHP_JSON_T_ESTRING;
266		}
267		str = zend_string_alloc(len, 0);
268		ZSTR_VAL(str)[len] = '\0';
269		ZVAL_STR(&s->value, str);
270		if (s->str_esc || s->utf8_invalid) {
271			s->pstr = (php_json_ctype *) Z_STRVAL(s->value);
272			s->cursor = s->str_start;
273			PHP_JSON_CONDITION_GOTO_STR_P2();
274		} else {
275			memcpy(Z_STRVAL(s->value), s->str_start, len);
276			PHP_JSON_CONDITION_SET(JS);
277			return PHP_JSON_T_STRING;
278		}
279	}
280	<STR_P1>UTF8             { PHP_JSON_CONDITION_GOTO(STR_P1); }
281	<STR_P1>ANY              {
282		if (s->options & (PHP_JSON_INVALID_UTF8_IGNORE | PHP_JSON_INVALID_UTF8_SUBSTITUTE)) {
283			if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
284				if (s->utf8_invalid_count > INT_MAX - 2) {
285					s->errcode = PHP_JSON_ERROR_UTF8;
286					return PHP_JSON_T_ERROR;
287				}
288				s->utf8_invalid_count += 2;
289			} else {
290				s->utf8_invalid_count--;
291			}
292			s->utf8_invalid = 1;
293			PHP_JSON_CONDITION_GOTO(STR_P1);
294		}
295		s->errcode = PHP_JSON_ERROR_UTF8;
296		return PHP_JSON_T_ERROR;
297	}
298
299	<STR_P2_UTF,STR_P2_BIN>UTF16_1             {
300		int utf16 = php_json_ucs2_to_int(s, 2);
301		PHP_JSON_SCANNER_COPY_UTF();
302		*(s->pstr++) = (char) utf16;
303		s->str_start = s->cursor;
304		PHP_JSON_CONDITION_GOTO_STR_P2();
305	}
306	<STR_P2_UTF,STR_P2_BIN>UTF16_2             {
307		int utf16 = php_json_ucs2_to_int(s, 3);
308		PHP_JSON_SCANNER_COPY_UTF();
309		*(s->pstr++) = (char) (0xc0 | (utf16 >> 6));
310		*(s->pstr++) = (char) (0x80 | (utf16 & 0x3f));
311		s->str_start = s->cursor;
312		PHP_JSON_CONDITION_GOTO_STR_P2();
313	}
314	<STR_P2_UTF,STR_P2_BIN>UTF16_3             {
315		int utf16 = php_json_ucs2_to_int(s, 4);
316		PHP_JSON_SCANNER_COPY_UTF();
317		*(s->pstr++) = (char) (0xe0 | (utf16 >> 12));
318		*(s->pstr++) = (char) (0x80 | ((utf16 >> 6) & 0x3f));
319		*(s->pstr++) = (char) (0x80 | (utf16 & 0x3f));
320		s->str_start = s->cursor;
321		PHP_JSON_CONDITION_GOTO_STR_P2();
322	}
323	<STR_P2_UTF,STR_P2_BIN>UTF16_4             {
324		int utf32, utf16_hi, utf16_lo;
325		utf16_hi = php_json_ucs2_to_int(s, 4);
326		utf16_lo = php_json_ucs2_to_int_ex(s, 4, 7);
327		utf32 = ((utf16_lo & 0x3FF) << 10) + (utf16_hi & 0x3FF) + 0x10000;
328		PHP_JSON_SCANNER_COPY_UTF_SP();
329		*(s->pstr++) = (char) (0xf0 | (utf32 >> 18));
330		*(s->pstr++) = (char) (0x80 | ((utf32 >> 12) & 0x3f));
331		*(s->pstr++) = (char) (0x80 | ((utf32 >> 6) & 0x3f));
332		*(s->pstr++) = (char) (0x80 | (utf32 & 0x3f));
333		s->str_start = s->cursor;
334		PHP_JSON_CONDITION_GOTO_STR_P2();
335	}
336	<STR_P2_UTF,STR_P2_BIN>ESCPREF          {
337		char esc;
338		PHP_JSON_SCANNER_COPY_ESC();
339		switch (*s->cursor) {
340			case 'b':
341				esc = '\b';
342				break;
343			case 'f':
344				esc = '\f';				break;
345			case 'n':
346				esc = '\n';
347				break;
348			case 'r':
349				esc = '\r';
350				break;
351			case 't':
352				esc = '\t';
353				break;
354			case '\\':
355			case '/':
356			case '"':
357				esc = *s->cursor;
358				break;
359			default:
360				s->errcode = PHP_JSON_ERROR_SYNTAX;
361				return PHP_JSON_T_ERROR;
362		}
363		*(s->pstr++) = esc;
364		++YYCURSOR;
365		s->str_start = s->cursor;
366		PHP_JSON_CONDITION_GOTO_STR_P2();
367	}
368	<STR_P2_UTF,STR_P2_BIN>["] => JS        {
369		PHP_JSON_SCANNER_COPY_ESC();
370		return PHP_JSON_T_STRING;
371	}
372	<STR_P2_BIN>UTF8         { PHP_JSON_CONDITION_GOTO(STR_P2_BIN); }
373	<STR_P2_BIN>ANY          {
374		if (s->utf8_invalid) {
375			PHP_JSON_SCANNER_COPY_ESC();
376			if (s->options & PHP_JSON_INVALID_UTF8_SUBSTITUTE) {
377				*(s->pstr++) = (char) (0xe0 | (0xfffd >> 12));
378				*(s->pstr++) = (char) (0x80 | ((0xfffd >> 6) & 0x3f));
379				*(s->pstr++) = (char) (0x80 | (0xfffd & 0x3f));
380			}
381			s->str_start = s->cursor;
382		}
383		PHP_JSON_CONDITION_GOTO(STR_P2_BIN);
384	}
385	<STR_P2_UTF>ANY          { PHP_JSON_CONDITION_GOTO(STR_P2_UTF); }
386
387	<*>ANY                   {
388		s->errcode = PHP_JSON_ERROR_SYNTAX;
389		return PHP_JSON_T_ERROR;
390	}
391*/
392
393}
394