/src/php-src/Zend/zend_smart_str.c
Line | Count | Source |
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: Dmitry Stogov <dmitry@php.net> | |
14 | | +----------------------------------------------------------------------+ |
15 | | */ |
16 | | |
17 | | #include <zend.h> |
18 | | #include "zend_smart_str.h" |
19 | | #include "zend_smart_string.h" |
20 | | #include "zend_enum.h" |
21 | | |
22 | 8.46M | #define SMART_STR_OVERHEAD (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE + 1) |
23 | 8.25M | #define SMART_STR_START_SIZE 256 |
24 | 8.25M | #define SMART_STR_START_LEN (SMART_STR_START_SIZE - SMART_STR_OVERHEAD) |
25 | | #define SMART_STR_PAGE 4096 |
26 | | |
27 | | #define SMART_STR_NEW_LEN(len) \ |
28 | 4.33M | (ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STR_OVERHEAD, SMART_STR_PAGE) - SMART_STR_OVERHEAD) |
29 | | |
30 | | ZEND_API void ZEND_FASTCALL smart_str_erealloc(smart_str *str, size_t len) |
31 | 4.33M | { |
32 | 4.33M | if (UNEXPECTED(!str->s)) { |
33 | 4.12M | str->a = len <= SMART_STR_START_LEN |
34 | 4.12M | ? SMART_STR_START_LEN |
35 | 4.12M | : SMART_STR_NEW_LEN(len); |
36 | 4.12M | str->s = zend_string_alloc(str->a, 0); |
37 | 4.12M | ZSTR_LEN(str->s) = 0; |
38 | 4.12M | } else { |
39 | 201k | str->a = SMART_STR_NEW_LEN(len); |
40 | 201k | str->s = (zend_string *) erealloc2(str->s, str->a + _ZSTR_HEADER_SIZE + 1, _ZSTR_HEADER_SIZE + ZSTR_LEN(str->s)); |
41 | 201k | } |
42 | 4.33M | } |
43 | | |
44 | | ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len) |
45 | 0 | { |
46 | 0 | if (UNEXPECTED(!str->s)) { |
47 | 0 | str->a = len <= SMART_STR_START_LEN |
48 | 0 | ? SMART_STR_START_LEN |
49 | 0 | : SMART_STR_NEW_LEN(len); |
50 | 0 | str->s = zend_string_alloc(str->a, 1); |
51 | 0 | ZSTR_LEN(str->s) = 0; |
52 | 0 | } else { |
53 | 0 | str->a = SMART_STR_NEW_LEN(len); |
54 | 0 | str->s = (zend_string *) perealloc(str->s, str->a + _ZSTR_HEADER_SIZE + 1, 1); |
55 | 0 | } |
56 | 0 | } |
57 | | |
58 | | /* Windows uses VK_ESCAPE instead of \e */ |
59 | | #ifndef VK_ESCAPE |
60 | 376k | #define VK_ESCAPE '\e' |
61 | | #endif |
62 | | |
63 | 21.1k | static size_t zend_compute_escaped_string_len(const char *s, size_t l) { |
64 | 21.1k | size_t i, len = l; |
65 | 400k | for (i = 0; i < l; ++i) { |
66 | 378k | char c = s[i]; |
67 | 378k | if (c == '\n' || c == '\r' || c == '\t' || |
68 | 376k | c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) { |
69 | 4.30k | len += 1; |
70 | 374k | } else if (c < 32 || c > 126) { |
71 | 57.7k | len += 3; |
72 | 57.7k | } |
73 | 378k | } |
74 | 21.1k | return len; |
75 | 21.1k | } |
76 | | |
77 | 21.1k | ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) { |
78 | 21.1k | char *res; |
79 | 21.1k | size_t i, len = zend_compute_escaped_string_len(s, l); |
80 | | |
81 | 21.1k | smart_str_alloc(str, len, 0); |
82 | 21.1k | res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)]; |
83 | 21.1k | ZSTR_LEN(str->s) += len; |
84 | | |
85 | 400k | for (i = 0; i < l; ++i) { |
86 | 378k | unsigned char c = s[i]; |
87 | 378k | if (c < 32 || c == '\\' || c > 126) { |
88 | 62.0k | *res++ = '\\'; |
89 | 62.0k | switch (c) { |
90 | 927 | case '\n': *res++ = 'n'; break; |
91 | 735 | case '\r': *res++ = 'r'; break; |
92 | 653 | case '\t': *res++ = 't'; break; |
93 | 221 | case '\f': *res++ = 'f'; break; |
94 | 367 | case '\v': *res++ = 'v'; break; |
95 | 483 | case '\\': *res++ = '\\'; break; |
96 | 919 | case VK_ESCAPE: *res++ = 'e'; break; |
97 | 57.7k | default: |
98 | 57.7k | *res++ = 'x'; |
99 | 57.7k | if ((c >> 4) < 10) { |
100 | 39.7k | *res++ = (c >> 4) + '0'; |
101 | 39.7k | } else { |
102 | 18.0k | *res++ = (c >> 4) + 'A' - 10; |
103 | 18.0k | } |
104 | 57.7k | if ((c & 0xf) < 10) { |
105 | 40.9k | *res++ = (c & 0xf) + '0'; |
106 | 40.9k | } else { |
107 | 16.7k | *res++ = (c & 0xf) + 'A' - 10; |
108 | 16.7k | } |
109 | 62.0k | } |
110 | 316k | } else { |
111 | 316k | *res++ = c; |
112 | 316k | } |
113 | 378k | } |
114 | 21.1k | } |
115 | | |
116 | | ZEND_API void ZEND_FASTCALL smart_str_append_double( |
117 | 63.8k | smart_str *str, double num, int precision, bool zero_fraction) { |
118 | 63.8k | char buf[ZEND_DOUBLE_MAX_LENGTH]; |
119 | | /* Model snprintf precision behavior. */ |
120 | 63.8k | zend_gcvt(num, precision ? precision : 1, '.', 'E', buf); |
121 | 63.8k | smart_str_appends(str, buf); |
122 | 63.8k | if (zero_fraction && zend_finite(num) && !strchr(buf, '.')) { |
123 | 35.1k | smart_str_appendl(str, ".0", 2); |
124 | 35.1k | } |
125 | 63.8k | } |
126 | | |
127 | 4.61k | ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) { |
128 | 4.61k | va_list arg; |
129 | 4.61k | va_start(arg, format); |
130 | 4.61k | zend_printf_to_smart_str(dest, format, arg); |
131 | 4.61k | va_end(arg); |
132 | 4.61k | } |
133 | | |
134 | 0 | ZEND_API void smart_string_append_printf(smart_string *dest, const char *format, ...) { |
135 | 0 | va_list arg; |
136 | 0 | va_start(arg, format); |
137 | 0 | zend_printf_to_smart_string(dest, format, arg); |
138 | 0 | va_end(arg); |
139 | 0 | } |
140 | | |
141 | 31.1M | #define SMART_STRING_OVERHEAD (ZEND_MM_OVERHEAD + 1) |
142 | 31.1M | #define SMART_STRING_START_SIZE 256 |
143 | 31.1M | #define SMART_STRING_START_LEN (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD) |
144 | | #define SMART_STRING_PAGE 4096 |
145 | | |
146 | | ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len) |
147 | 0 | { |
148 | 0 | if (!str->c) { |
149 | 0 | str->len = 0; |
150 | 0 | if (len <= SMART_STRING_START_LEN) { |
151 | 0 | str->a = SMART_STRING_START_LEN; |
152 | 0 | } else { |
153 | 0 | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
154 | 0 | } |
155 | 0 | str->c = pemalloc(str->a + 1, 1); |
156 | 0 | } else { |
157 | 0 | if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) { |
158 | 0 | zend_error_noreturn(E_ERROR, "String size overflow"); |
159 | 0 | } |
160 | 0 | len += str->len; |
161 | 0 | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
162 | 0 | str->c = perealloc(str->c, str->a + 1, 1); |
163 | 0 | } |
164 | 0 | } |
165 | | |
166 | | ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len) |
167 | 15.5M | { |
168 | 15.5M | if (!str->c) { |
169 | 15.5M | str->len = 0; |
170 | 15.5M | if (len <= SMART_STRING_START_LEN) { |
171 | 15.5M | str->a = SMART_STRING_START_LEN; |
172 | 15.5M | str->c = emalloc(SMART_STRING_START_LEN + 1); |
173 | 15.5M | } else { |
174 | 679 | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
175 | 679 | if (EXPECTED(str->a < (ZEND_MM_CHUNK_SIZE - SMART_STRING_OVERHEAD))) { |
176 | 679 | str->c = emalloc_large(str->a + 1); |
177 | 679 | } else { |
178 | | /* allocate a huge chunk */ |
179 | 0 | str->c = emalloc(str->a + 1); |
180 | 0 | } |
181 | 679 | } |
182 | 15.5M | } else { |
183 | 8.57k | if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) { |
184 | 0 | zend_error_noreturn(E_ERROR, "String size overflow"); |
185 | 0 | } |
186 | 8.57k | len += str->len; |
187 | 8.57k | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
188 | 8.57k | str->c = erealloc2(str->c, str->a + 1, str->len); |
189 | 8.57k | } |
190 | 15.5M | } |
191 | | |
192 | | ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, const zend_string *value, size_t length) |
193 | 20.8k | { |
194 | 20.8k | smart_str_append_escaped(str, ZSTR_VAL(value), MIN(length, ZSTR_LEN(value))); |
195 | | |
196 | 20.8k | if (ZSTR_LEN(value) > length) { |
197 | 2.91k | smart_str_appendl(str, "...", sizeof("...")-1); |
198 | 2.91k | } |
199 | 20.8k | } |
200 | | |
201 | 23.2k | ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *dest, const zval *value, size_t truncate) { |
202 | 23.2k | ZEND_ASSERT(Z_TYPE_P(value) <= IS_STRING); |
203 | | |
204 | 23.2k | switch (Z_TYPE_P(value)) { |
205 | 5 | case IS_UNDEF: |
206 | 2.05k | case IS_NULL: |
207 | 2.05k | smart_str_appendl(dest, "NULL", sizeof("NULL")-1); |
208 | 2.05k | break; |
209 | | |
210 | 28 | case IS_TRUE: |
211 | 28 | smart_str_appendl(dest, "true", sizeof("true")-1); |
212 | 28 | break; |
213 | | |
214 | 30 | case IS_FALSE: |
215 | 30 | smart_str_appendl(dest, "false", sizeof("false")-1); |
216 | 30 | break; |
217 | | |
218 | 21 | case IS_DOUBLE: |
219 | 21 | smart_str_append_double(dest, Z_DVAL_P(value), (int) EG(precision), true); |
220 | 21 | break; |
221 | | |
222 | 262 | case IS_LONG: |
223 | 262 | smart_str_append_long(dest, Z_LVAL_P(value)); |
224 | 262 | break; |
225 | | |
226 | 20.8k | case IS_STRING: |
227 | 20.8k | smart_str_appendc(dest, '\''); |
228 | 20.8k | smart_str_append_escaped_truncated(dest, Z_STR_P(value), truncate); |
229 | 20.8k | smart_str_appendc(dest, '\''); |
230 | 20.8k | break; |
231 | | |
232 | 0 | EMPTY_SWITCH_DEFAULT_CASE(); |
233 | 23.2k | } |
234 | 23.2k | } |
235 | | |
236 | | ZEND_API zend_result ZEND_FASTCALL smart_str_append_zval(smart_str *dest, const zval *value, size_t truncate) |
237 | 25.1k | { |
238 | 25.1k | if (Z_TYPE_P(value) <= IS_STRING) { |
239 | 23.2k | smart_str_append_scalar(dest, value, truncate); |
240 | 23.2k | } else if (Z_TYPE_P(value) == IS_OBJECT && (Z_OBJCE_P(value)->ce_flags & ZEND_ACC_ENUM)) { |
241 | 6 | smart_str_append(dest, Z_OBJCE_P(value)->name); |
242 | 6 | smart_str_appends(dest, "::"); |
243 | 6 | smart_str_append(dest, Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(value)))); |
244 | 1.94k | } else { |
245 | 1.94k | return FAILURE; |
246 | 1.94k | } |
247 | 23.2k | return SUCCESS; |
248 | 25.1k | } |