29
29
#include "zend_enum.h"
30
30
#include "zend_property_hooks.h"
31
31
#include "zend_lazy_objects.h"
32
+ #include "zend_bitset.h"
33
+
34
+ #if defined(ZEND_INTRIN_SSE4_2_NATIVE ) || defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO )
35
+ # include <nmmintrin.h>
36
+ #endif
37
+ #ifdef ZEND_INTRIN_SSE4_2_FUNC_PROTO
38
+ # include "zend_cpuinfo.h"
39
+ #endif
40
+
41
+ #ifdef __SSE2__
42
+ # define JSON_USE_SIMD
43
+ #endif
44
+
45
+ typedef enum php_json_simd_result {
46
+ PHP_JSON_STOP ,
47
+ PHP_JSON_SLOW ,
48
+ PHP_JSON_NON_ASCII ,
49
+ } php_json_simd_result ;
50
+
51
+ /* Specialization of smart_str_appendl() to avoid performance loss due to code bloat */
52
+ static zend_always_inline void php_json_append (smart_str * dest , const char * src , size_t len )
53
+ {
54
+ /* dest has a minimum size of the input length,
55
+ * this avoids generating initial allocation code */
56
+ ZEND_ASSERT (dest -> s );
57
+
58
+ smart_str_appendl (dest , src , len );
59
+ }
60
+
61
+ static zend_always_inline bool php_json_printable_ascii_escape (smart_str * buf , unsigned char us , int options )
62
+ {
63
+ ZEND_ASSERT (buf -> s );
64
+
65
+ switch (us ) {
66
+ case '"' :
67
+ if (options & PHP_JSON_HEX_QUOT ) {
68
+ php_json_append (buf , "\\u0022" , 6 );
69
+ } else {
70
+ php_json_append (buf , "\\\"" , 2 );
71
+ }
72
+ break ;
73
+
74
+ case '\\' :
75
+ php_json_append (buf , "\\\\" , 2 );
76
+ break ;
77
+
78
+ case '/' :
79
+ if (options & PHP_JSON_UNESCAPED_SLASHES ) {
80
+ smart_str_appendc (buf , '/' );
81
+ } else {
82
+ php_json_append (buf , "\\/" , 2 );
83
+ }
84
+ break ;
85
+
86
+ case '<' :
87
+ if (options & PHP_JSON_HEX_TAG ) {
88
+ php_json_append (buf , "\\u003C" , 6 );
89
+ } else {
90
+ smart_str_appendc (buf , '<' );
91
+ }
92
+ break ;
93
+
94
+ case '>' :
95
+ if (options & PHP_JSON_HEX_TAG ) {
96
+ php_json_append (buf , "\\u003E" , 6 );
97
+ } else {
98
+ smart_str_appendc (buf , '>' );
99
+ }
100
+ break ;
101
+
102
+ case '&' :
103
+ if (options & PHP_JSON_HEX_AMP ) {
104
+ php_json_append (buf , "\\u0026" , 6 );
105
+ } else {
106
+ smart_str_appendc (buf , '&' );
107
+ }
108
+ break ;
109
+
110
+ case '\'' :
111
+ if (options & PHP_JSON_HEX_APOS ) {
112
+ php_json_append (buf , "\\u0027" , 6 );
113
+ } else {
114
+ smart_str_appendc (buf , '\'' );
115
+ }
116
+ break ;
117
+
118
+ default :
119
+ return false;
120
+ }
121
+
122
+ return true;
123
+ }
124
+
125
+ #ifdef JSON_USE_SIMD
126
+ static zend_always_inline int php_json_sse2_compute_escape_intersection (const __m128i mask , const __m128i input )
127
+ {
128
+ (void ) mask ;
129
+
130
+ const __m128i result_34 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('"' ));
131
+ const __m128i result_38 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('&' ));
132
+ const __m128i result_39 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('\'' ));
133
+ const __m128i result_47 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('/' ));
134
+ const __m128i result_60 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('<' ));
135
+ const __m128i result_62 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('>' ));
136
+ const __m128i result_92 = _mm_cmpeq_epi8 (input , _mm_set1_epi8 ('\\' ));
137
+
138
+ const __m128i result_34_38 = _mm_or_si128 (result_34 , result_38 );
139
+ const __m128i result_39_47 = _mm_or_si128 (result_39 , result_47 );
140
+ const __m128i result_60_62 = _mm_or_si128 (result_60 , result_62 );
141
+
142
+ const __m128i result_34_38_39_47 = _mm_or_si128 (result_34_38 , result_39_47 );
143
+ const __m128i result_60_62_92 = _mm_or_si128 (result_60_62 , result_92 );
144
+
145
+ const __m128i result_individual_bytes = _mm_or_si128 (result_34_38_39_47 , result_60_62_92 );
146
+ return _mm_movemask_epi8 (result_individual_bytes );
147
+ }
148
+
149
+ #if defined(ZEND_INTRIN_SSE4_2_NATIVE ) || defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO )
150
+ static const char php_json_escape_noslashes_lut [2 ][8 ][16 ] = {
151
+ /* !PHP_JSON_UNESCAPED_SLASHES */
152
+ {
153
+ [0 ] = {'"' , '\\' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
154
+ [PHP_JSON_HEX_AMP ] = {'"' , '\\' , '&' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
155
+ [PHP_JSON_HEX_APOS ] = {'"' , '\\' , '\'' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
156
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_APOS ] = {'"' , '\\' , '&' , '\'' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
157
+ [PHP_JSON_HEX_TAG ] = {'"' , '\\' , '<' , '>' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
158
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '&' , '<' , '>' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
159
+ [PHP_JSON_HEX_APOS |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '\'' , '<' , '>' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
160
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_APOS |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '&' , '\'' , '<' , '>' , '/' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
161
+ },
162
+
163
+ /* PHP_JSON_UNESCAPED_SLASHES */
164
+ {
165
+ [0 ] = {'"' , '\\' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
166
+ [PHP_JSON_HEX_AMP ] = {'"' , '\\' , '&' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
167
+ [PHP_JSON_HEX_APOS ] = {'"' , '\\' , '\'' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
168
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_APOS ] = {'"' , '\\' , '&' , '\'' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
169
+ [PHP_JSON_HEX_TAG ] = {'"' , '\\' , '<' , '>' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
170
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '&' , '<' , '>' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
171
+ [PHP_JSON_HEX_APOS |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '\'' , '<' , '>' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 },
172
+ [PHP_JSON_HEX_AMP |PHP_JSON_HEX_APOS |PHP_JSON_HEX_TAG ] = {'"' , '\\' , '&' , '\'' , '<' , '>' , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 }
173
+ }
174
+ };
175
+
176
+ static zend_always_inline __m128i php_json_create_sse_escape_mask (int options )
177
+ {
178
+ const int slashes = (options & PHP_JSON_UNESCAPED_SLASHES ) ? 1 : 0 ;
179
+ const int masked = options & (PHP_JSON_HEX_AMP |PHP_JSON_HEX_APOS |PHP_JSON_HEX_TAG );
180
+ return * (const __m128i * ) & php_json_escape_noslashes_lut [slashes ][masked ];
181
+ }
182
+
183
+ ZEND_INTRIN_SSE4_2_FUNC_DECL (int php_json_sse42_compute_escape_intersection_real (const __m128i mask , const __m128i input ));
184
+ zend_always_inline int php_json_sse42_compute_escape_intersection_real (const __m128i mask , const __m128i input )
185
+ {
186
+ const __m128i result_individual_bytes = _mm_cmpistrm (mask , input , _SIDD_SBYTE_OPS | _SIDD_CMP_EQUAL_ANY | _SIDD_BIT_MASK );
187
+ return _mm_cvtsi128_si32 (result_individual_bytes );
188
+ }
189
+ #endif
190
+
191
+ #ifdef ZEND_INTRIN_SSE4_2_FUNC_PROTO
192
+ static int php_json_sse42_compute_escape_intersection (const __m128i mask , const __m128i input ) __attribute__((ifunc ("php_json_resolve_escape_intersection" )));
193
+
194
+ typedef int (* php_json_compute_escape_intersection_t )(const __m128i mask , const __m128i input );
195
+
196
+ ZEND_NO_SANITIZE_ADDRESS
197
+ ZEND_ATTRIBUTE_UNUSED /* clang mistakenly warns about this */
198
+ static php_json_compute_escape_intersection_t php_json_resolve_escape_intersection (void ) {
199
+ if (zend_cpu_supports_sse42 ()) {
200
+ return php_json_sse42_compute_escape_intersection_real ;
201
+ }
202
+ return php_json_sse2_compute_escape_intersection ;
203
+ }
204
+ #endif
205
+
206
+ static zend_always_inline php_json_simd_result php_json_process_simd_block (
207
+ smart_str * buf ,
208
+ const __m128i sse_escape_mask ,
209
+ const char * * restrict s ,
210
+ size_t * restrict pos ,
211
+ size_t * restrict len ,
212
+ int options
213
+ )
214
+ {
215
+ while (* len >= sizeof (__m128i )) {
216
+ const __m128i input = _mm_loadu_si128 ((const __m128i * ) (* s + * pos ));
217
+ /* signed compare, so checks for unsigned bytes >= 0x80 as well */
218
+ const __m128i input_range = _mm_cmplt_epi8 (input , _mm_set1_epi8 (32 ));
219
+
220
+ int max_shift = sizeof (__m128i );
221
+
222
+ int input_range_mask = _mm_movemask_epi8 (input_range );
223
+ if (input_range_mask != 0 ) {
224
+ if (UNEXPECTED (input_range_mask & 1 )) {
225
+ /* not worth it */
226
+ return PHP_JSON_NON_ASCII ;
227
+ }
228
+ max_shift = zend_ulong_ntz (input_range_mask );
229
+ }
230
+
231
+ #ifdef ZEND_INTRIN_SSE4_2_NATIVE
232
+ int mask = php_json_sse42_compute_escape_intersection_real (sse_escape_mask , input );
233
+ #elif defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO )
234
+ int mask = php_json_sse42_compute_escape_intersection (sse_escape_mask , input );
235
+ #else
236
+ int mask = php_json_sse2_compute_escape_intersection (_mm_setzero_si128 (), input );
237
+ #endif
238
+ if (mask != 0 ) {
239
+ if (UNEXPECTED (max_shift < sizeof (__m128i ))) {
240
+ int shift = zend_ulong_ntz (mask ); /* first offending character */
241
+ * pos += MIN (max_shift , shift );
242
+ * len -= MIN (max_shift , shift );
243
+ return PHP_JSON_SLOW ;
244
+ }
245
+
246
+ php_json_append (buf , * s , * pos );
247
+ * s += * pos ;
248
+ const char * s_backup = * s ;
249
+
250
+ /* It's more important to keep this loop tight than to optimize this with
251
+ * a trailing zero count. */
252
+ for (; mask ; mask >>= 1 , * s += 1 ) {
253
+ if (UNEXPECTED (mask & 1 )) {
254
+ bool handled = php_json_printable_ascii_escape (buf , (* s )[0 ], options );
255
+ ZEND_ASSERT (handled );
256
+ } else {
257
+ ZEND_ASSERT (buf -> s );
258
+ smart_str_appendc (buf , (* s )[0 ]);
259
+ }
260
+ }
261
+
262
+ * pos = sizeof (__m128i ) - (* s - s_backup );
263
+ } else {
264
+ if (max_shift < sizeof (__m128i )) {
265
+ * pos += max_shift ;
266
+ * len -= max_shift ;
267
+ return PHP_JSON_SLOW ;
268
+ }
269
+ * pos += sizeof (__m128i );
270
+ }
271
+
272
+ * len -= sizeof (__m128i );
273
+ }
274
+
275
+ return UNEXPECTED (!* len ) ? PHP_JSON_STOP : PHP_JSON_SLOW ;
276
+ }
277
+
278
+ # if defined(ZEND_INTRIN_SSE4_2_NATIVE ) || defined(ZEND_INTRIN_SSE4_2_FUNC_PROTO )
279
+ # define JSON_DEFINE_ESCAPE_MASK (name , options ) const __m128i name = php_json_create_sse_escape_mask(options)
280
+ # else
281
+ # define JSON_DEFINE_ESCAPE_MASK (name , options ) const __m128i name = _mm_setzero_si128()
282
+ # endif
283
+ #else
284
+ # define JSON_DEFINE_ESCAPE_MASK (name , options )
285
+ #endif
32
286
33
287
static const char digits [] = "0123456789abcdef" ;
34
288
@@ -394,54 +648,64 @@ zend_result php_json_escape_string(
394
648
}
395
649
396
650
}
397
- checkpoint = buf -> s ? ZSTR_LEN (buf -> s ) : 0 ;
398
651
399
652
/* pre-allocate for string length plus 2 quotes */
400
653
smart_str_alloc (buf , len + 2 , 0 );
654
+ checkpoint = ZSTR_LEN (buf -> s );
401
655
smart_str_appendc (buf , '"' );
402
656
403
657
pos = 0 ;
404
658
659
+ JSON_DEFINE_ESCAPE_MASK (sse_escape_mask , options );
660
+
405
661
do {
406
662
static const uint32_t charmap [8 ] = {
407
663
0xffffffff , 0x500080c4 , 0x10000000 , 0x00000000 ,
408
664
0xffffffff , 0xffffffff , 0xffffffff , 0xffffffff };
409
665
666
+ php_json_simd_result result = PHP_JSON_SLOW ;
667
+ #ifdef JSON_USE_SIMD
668
+ result = php_json_process_simd_block (buf , sse_escape_mask , & s , & pos , & len , options );
669
+ if (UNEXPECTED (result == PHP_JSON_STOP )) {
670
+ break ;
671
+ }
672
+ #endif
673
+
410
674
us = (unsigned char )s [pos ];
411
- if (EXPECTED (!ZEND_BIT_TEST (charmap , us ))) {
675
+ if (EXPECTED (result != PHP_JSON_NON_ASCII && !ZEND_BIT_TEST (charmap , us ))) {
412
676
pos ++ ;
413
677
len -- ;
414
- if (len == 0 ) {
415
- smart_str_appendl (buf , s , pos );
416
- break ;
417
- }
418
678
} else {
419
- if (pos ) {
420
- smart_str_appendl (buf , s , pos );
421
- s += pos ;
422
- pos = 0 ;
423
- }
424
- us = (unsigned char )s [0 ];
425
679
if (UNEXPECTED (us >= 0x80 )) {
426
- zend_result status ;
427
- us = php_next_utf8_char ((unsigned char * )s , len , & pos , & status );
680
+ size_t pos_old = pos ;
681
+ const char * cur = s + pos ;
682
+ pos = 0 ;
683
+ us = php_next_utf8_char_mb ((unsigned char * )cur , us , len , & pos );
684
+ len -= pos ;
685
+ pos += pos_old ;
428
686
429
687
/* check whether UTF8 character is correct */
430
- if (UNEXPECTED (status != SUCCESS )) {
688
+ if (UNEXPECTED (!us )) {
689
+ if (pos_old && (options & (PHP_JSON_INVALID_UTF8_IGNORE |PHP_JSON_INVALID_UTF8_SUBSTITUTE ))) {
690
+ php_json_append (buf , s , pos_old );
691
+ }
692
+ s += pos ;
693
+ pos = 0 ;
694
+
431
695
if (options & PHP_JSON_INVALID_UTF8_IGNORE ) {
432
696
/* ignore invalid UTF8 character */
433
697
} else if (options & PHP_JSON_INVALID_UTF8_SUBSTITUTE ) {
434
698
/* Use Unicode character 'REPLACEMENT CHARACTER' (U+FFFD) */
435
699
if (options & PHP_JSON_UNESCAPED_UNICODE ) {
436
- smart_str_appendl (buf , "\xef\xbf\xbd" , 3 );
700
+ php_json_append (buf , "\xef\xbf\xbd" , 3 );
437
701
} else {
438
- smart_str_appendl (buf , "\\ufffd" , 6 );
702
+ php_json_append (buf , "\\ufffd" , 6 );
439
703
}
440
704
} else {
441
705
ZSTR_LEN (buf -> s ) = checkpoint ;
442
706
encoder -> error_code = PHP_JSON_ERROR_UTF8 ;
443
707
if (options & PHP_JSON_PARTIAL_OUTPUT_ON_ERROR ) {
444
- smart_str_appendl (buf , "null" , 4 );
708
+ php_json_append (buf , "null" , 4 );
445
709
}
446
710
return FAILURE ;
447
711
}
@@ -452,126 +716,87 @@ zend_result php_json_escape_string(
452
716
} else if ((options & PHP_JSON_UNESCAPED_UNICODE )
453
717
&& ((options & PHP_JSON_UNESCAPED_LINE_TERMINATORS )
454
718
|| us < 0x2028 || us > 0x2029 )) {
455
- smart_str_appendl ( buf , s , pos );
719
+ /* No need to emit any bytes, just move the cursor. */
456
720
} else {
721
+ php_json_append (buf , s , pos_old );
722
+ s += pos ;
723
+ pos = 0 ;
724
+
725
+ ZEND_ASSERT (buf -> s );
726
+
457
727
/* From http://en.wikipedia.org/wiki/UTF16 */
728
+ dst = smart_str_extend (buf , 6 + ((us >= 0x10000 ) ? 6 : 0 ));
458
729
if (us >= 0x10000 ) {
459
730
unsigned int next_us ;
460
731
461
732
us -= 0x10000 ;
462
733
next_us = (unsigned short )((us & 0x3ff ) | 0xdc00 );
463
734
us = (unsigned short )((us >> 10 ) | 0xd800 );
464
- dst = smart_str_extend (buf , 6 );
465
735
dst [0 ] = '\\' ;
466
736
dst [1 ] = 'u' ;
467
737
dst [2 ] = digits [(us >> 12 ) & 0xf ];
468
738
dst [3 ] = digits [(us >> 8 ) & 0xf ];
469
739
dst [4 ] = digits [(us >> 4 ) & 0xf ];
470
740
dst [5 ] = digits [us & 0xf ];
471
741
us = next_us ;
742
+ dst += 6 ;
472
743
}
473
- dst = smart_str_extend (buf , 6 );
474
744
dst [0 ] = '\\' ;
475
745
dst [1 ] = 'u' ;
476
746
dst [2 ] = digits [(us >> 12 ) & 0xf ];
477
747
dst [3 ] = digits [(us >> 8 ) & 0xf ];
478
748
dst [4 ] = digits [(us >> 4 ) & 0xf ];
479
749
dst [5 ] = digits [us & 0xf ];
480
750
}
481
- s += pos ;
482
- len -= pos ;
483
- pos = 0 ;
484
751
} else {
752
+ if (pos ) {
753
+ php_json_append (buf , s , pos );
754
+ s += pos ;
755
+ pos = 0 ;
756
+ }
485
757
s ++ ;
486
758
switch (us ) {
487
- case '"' :
488
- if (options & PHP_JSON_HEX_QUOT ) {
489
- smart_str_appendl (buf , "\\u0022" , 6 );
490
- } else {
491
- smart_str_appendl (buf , "\\\"" , 2 );
492
- }
493
- break ;
494
-
495
- case '\\' :
496
- smart_str_appendl (buf , "\\\\" , 2 );
497
- break ;
498
-
499
- case '/' :
500
- if (options & PHP_JSON_UNESCAPED_SLASHES ) {
501
- smart_str_appendc (buf , '/' );
502
- } else {
503
- smart_str_appendl (buf , "\\/" , 2 );
504
- }
505
- break ;
506
-
507
759
case '\b' :
508
- smart_str_appendl (buf , "\\b" , 2 );
760
+ php_json_append (buf , "\\b" , 2 );
509
761
break ;
510
762
511
763
case '\f' :
512
- smart_str_appendl (buf , "\\f" , 2 );
764
+ php_json_append (buf , "\\f" , 2 );
513
765
break ;
514
766
515
767
case '\n' :
516
- smart_str_appendl (buf , "\\n" , 2 );
768
+ php_json_append (buf , "\\n" , 2 );
517
769
break ;
518
770
519
771
case '\r' :
520
- smart_str_appendl (buf , "\\r" , 2 );
772
+ php_json_append (buf , "\\r" , 2 );
521
773
break ;
522
774
523
775
case '\t' :
524
- smart_str_appendl (buf , "\\t" , 2 );
525
- break ;
526
-
527
- case '<' :
528
- if (options & PHP_JSON_HEX_TAG ) {
529
- smart_str_appendl (buf , "\\u003C" , 6 );
530
- } else {
531
- smart_str_appendc (buf , '<' );
532
- }
533
- break ;
534
-
535
- case '>' :
536
- if (options & PHP_JSON_HEX_TAG ) {
537
- smart_str_appendl (buf , "\\u003E" , 6 );
538
- } else {
539
- smart_str_appendc (buf , '>' );
540
- }
541
- break ;
542
-
543
- case '&' :
544
- if (options & PHP_JSON_HEX_AMP ) {
545
- smart_str_appendl (buf , "\\u0026" , 6 );
546
- } else {
547
- smart_str_appendc (buf , '&' );
548
- }
549
- break ;
550
-
551
- case '\'' :
552
- if (options & PHP_JSON_HEX_APOS ) {
553
- smart_str_appendl (buf , "\\u0027" , 6 );
554
- } else {
555
- smart_str_appendc (buf , '\'' );
556
- }
776
+ php_json_append (buf , "\\t" , 2 );
557
777
break ;
558
778
559
779
default :
560
- ZEND_ASSERT (us < ' ' );
561
- dst = smart_str_extend (buf , 6 );
562
- dst [0 ] = '\\' ;
563
- dst [1 ] = 'u' ;
564
- dst [2 ] = '0' ;
565
- dst [3 ] = '0' ;
566
- dst [4 ] = digits [(us >> 4 ) & 0xf ];
567
- dst [5 ] = digits [us & 0xf ];
780
+ if (!php_json_printable_ascii_escape (buf , us , options )) {
781
+ ZEND_ASSERT (us < ' ' );
782
+ dst = smart_str_extend (buf , 6 );
783
+ dst [0 ] = '\\' ;
784
+ dst [1 ] = 'u' ;
785
+ dst [2 ] = '0' ;
786
+ dst [3 ] = '0' ;
787
+ dst [4 ] = digits [(us >> 4 ) & 0xf ];
788
+ dst [5 ] = digits [us & 0xf ];
789
+ }
568
790
break ;
569
791
}
570
792
len -- ;
571
793
}
572
794
}
573
795
} while (len );
574
796
797
+ php_json_append (buf , s , pos );
798
+
799
+ ZEND_ASSERT (buf -> s );
575
800
smart_str_appendc (buf , '"' );
576
801
577
802
return SUCCESS ;
0 commit comments