/src/php-src/Zend/zend_smart_str.c
Line | Count | Source (jump to first uncovered line) |
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 | 9.43M | #define SMART_STR_OVERHEAD (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE + 1) |
23 | 9.33M | #define SMART_STR_START_SIZE 256 |
24 | 9.33M | #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.76M | (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.76M | { |
32 | 4.76M | if (UNEXPECTED(!str->s)) { |
33 | 4.66M | str->a = len <= SMART_STR_START_LEN |
34 | 4.66M | ? SMART_STR_START_LEN |
35 | 4.66M | : SMART_STR_NEW_LEN(len); |
36 | 4.66M | str->s = zend_string_alloc(str->a, 0); |
37 | 4.66M | ZSTR_LEN(str->s) = 0; |
38 | 4.66M | } else { |
39 | 94.7k | str->a = SMART_STR_NEW_LEN(len); |
40 | 94.7k | str->s = (zend_string *) erealloc2(str->s, str->a + _ZSTR_HEADER_SIZE + 1, _ZSTR_HEADER_SIZE + ZSTR_LEN(str->s)); |
41 | 94.7k | } |
42 | 4.76M | } |
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 | 530k | #define VK_ESCAPE '\e' |
61 | | #endif |
62 | | |
63 | 29.9k | static size_t zend_compute_escaped_string_len(const char *s, size_t l) { |
64 | 29.9k | size_t i, len = l; |
65 | 563k | for (i = 0; i < l; ++i) { |
66 | 533k | char c = s[i]; |
67 | 533k | if (c == '\n' || c == '\r' || c == '\t' || |
68 | 533k | c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) { |
69 | 3.75k | len += 1; |
70 | 529k | } else if (c < 32 || c > 126) { |
71 | 84.0k | len += 3; |
72 | 84.0k | } |
73 | 533k | } |
74 | 29.9k | return len; |
75 | 29.9k | } |
76 | | |
77 | 29.9k | ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) { |
78 | 29.9k | char *res; |
79 | 29.9k | size_t i, len = zend_compute_escaped_string_len(s, l); |
80 | | |
81 | 29.9k | smart_str_alloc(str, len, 0); |
82 | 29.9k | res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)]; |
83 | 29.9k | ZSTR_LEN(str->s) += len; |
84 | | |
85 | 563k | for (i = 0; i < l; ++i) { |
86 | 533k | unsigned char c = s[i]; |
87 | 533k | if (c < 32 || c == '\\' || c > 126) { |
88 | 87.8k | *res++ = '\\'; |
89 | 87.8k | switch (c) { |
90 | 670 | case '\n': *res++ = 'n'; break; |
91 | 816 | case '\r': *res++ = 'r'; break; |
92 | 507 | case '\t': *res++ = 't'; break; |
93 | 248 | case '\f': *res++ = 'f'; break; |
94 | 244 | case '\v': *res++ = 'v'; break; |
95 | 822 | case '\\': *res++ = '\\'; break; |
96 | 446 | case VK_ESCAPE: *res++ = 'e'; break; |
97 | 84.0k | default: |
98 | 84.0k | *res++ = 'x'; |
99 | 84.0k | if ((c >> 4) < 10) { |
100 | 55.7k | *res++ = (c >> 4) + '0'; |
101 | 55.7k | } else { |
102 | 28.3k | *res++ = (c >> 4) + 'A' - 10; |
103 | 28.3k | } |
104 | 84.0k | if ((c & 0xf) < 10) { |
105 | 54.6k | *res++ = (c & 0xf) + '0'; |
106 | 54.6k | } else { |
107 | 29.4k | *res++ = (c & 0xf) + 'A' - 10; |
108 | 29.4k | } |
109 | 87.8k | } |
110 | 445k | } else { |
111 | 445k | *res++ = c; |
112 | 445k | } |
113 | 533k | } |
114 | 29.9k | } |
115 | | |
116 | | ZEND_API void ZEND_FASTCALL smart_str_append_double( |
117 | 27.7k | smart_str *str, double num, int precision, bool zero_fraction) { |
118 | 27.7k | char buf[ZEND_DOUBLE_MAX_LENGTH]; |
119 | | /* Model snprintf precision behavior. */ |
120 | 27.7k | zend_gcvt(num, precision ? precision : 1, '.', 'E', buf); |
121 | 27.7k | smart_str_appends(str, buf); |
122 | 27.7k | if (zero_fraction && zend_finite(num) && !strchr(buf, '.')) { |
123 | 4.24k | smart_str_appendl(str, ".0", 2); |
124 | 4.24k | } |
125 | 27.7k | } |
126 | | |
127 | 2.66k | ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) { |
128 | 2.66k | va_list arg; |
129 | 2.66k | va_start(arg, format); |
130 | 2.66k | zend_printf_to_smart_str(dest, format, arg); |
131 | 2.66k | va_end(arg); |
132 | 2.66k | } |
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 | 22.8M | #define SMART_STRING_OVERHEAD (ZEND_MM_OVERHEAD + 1) |
142 | 22.8M | #define SMART_STRING_START_SIZE 256 |
143 | 22.8M | #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 | 11.4M | { |
168 | 11.4M | if (!str->c) { |
169 | 11.4M | str->len = 0; |
170 | 11.4M | if (len <= SMART_STRING_START_LEN) { |
171 | 11.4M | str->a = SMART_STRING_START_LEN; |
172 | 11.4M | str->c = emalloc(SMART_STRING_START_LEN + 1); |
173 | 11.4M | } else { |
174 | 526 | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
175 | 526 | if (EXPECTED(str->a < (ZEND_MM_CHUNK_SIZE - SMART_STRING_OVERHEAD))) { |
176 | 526 | str->c = emalloc_large(str->a + 1); |
177 | 526 | } else { |
178 | | /* allocate a huge chunk */ |
179 | 0 | str->c = emalloc(str->a + 1); |
180 | 0 | } |
181 | 526 | } |
182 | 11.4M | } else { |
183 | 13.3k | if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) { |
184 | 0 | zend_error_noreturn(E_ERROR, "String size overflow"); |
185 | 0 | } |
186 | 13.3k | len += str->len; |
187 | 13.3k | str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD; |
188 | 13.3k | str->c = erealloc2(str->c, str->a + 1, str->len); |
189 | 13.3k | } |
190 | 11.4M | } |
191 | | |
192 | | ZEND_API void ZEND_FASTCALL smart_str_append_escaped_truncated(smart_str *str, const zend_string *value, size_t length) |
193 | 29.7k | { |
194 | 29.7k | smart_str_append_escaped(str, ZSTR_VAL(value), MIN(length, ZSTR_LEN(value))); |
195 | | |
196 | 29.7k | if (ZSTR_LEN(value) > length) { |
197 | 4.56k | smart_str_appendl(str, "...", sizeof("...")-1); |
198 | 4.56k | } |
199 | 29.7k | } |
200 | | |
201 | 33.1k | ZEND_API void ZEND_FASTCALL smart_str_append_scalar(smart_str *dest, const zval *value, size_t truncate) { |
202 | 33.1k | ZEND_ASSERT(Z_TYPE_P(value) <= IS_STRING); |
203 | | |
204 | 33.1k | switch (Z_TYPE_P(value)) { |
205 | 5 | case IS_UNDEF: |
206 | 3.03k | case IS_NULL: |
207 | 3.03k | smart_str_appendl(dest, "NULL", sizeof("NULL")-1); |
208 | 3.03k | break; |
209 | | |
210 | 27 | case IS_TRUE: |
211 | 52 | case IS_FALSE: |
212 | 52 | smart_str_appends(dest, Z_TYPE_P(value) == IS_TRUE ? "true" : "false"); |
213 | 52 | break; |
214 | | |
215 | 20 | case IS_DOUBLE: |
216 | 20 | smart_str_append_double(dest, Z_DVAL_P(value), (int) EG(precision), true); |
217 | 20 | break; |
218 | | |
219 | 259 | case IS_LONG: |
220 | 259 | smart_str_append_long(dest, Z_LVAL_P(value)); |
221 | 259 | break; |
222 | | |
223 | 29.7k | case IS_STRING: |
224 | 29.7k | smart_str_appendc(dest, '\''); |
225 | 29.7k | smart_str_append_escaped_truncated(dest, Z_STR_P(value), truncate); |
226 | 29.7k | smart_str_appendc(dest, '\''); |
227 | 29.7k | break; |
228 | | |
229 | 0 | EMPTY_SWITCH_DEFAULT_CASE(); |
230 | 33.1k | } |
231 | 33.1k | } |
232 | | |
233 | | ZEND_API zend_result ZEND_FASTCALL smart_str_append_zval(smart_str *dest, const zval *value, size_t truncate) |
234 | 34.9k | { |
235 | 34.9k | if (Z_TYPE_P(value) <= IS_STRING) { |
236 | 33.1k | smart_str_append_scalar(dest, value, truncate); |
237 | 33.1k | } else if (Z_TYPE_P(value) == IS_OBJECT && (Z_OBJCE_P(value)->ce_flags & ZEND_ACC_ENUM)) { |
238 | 5 | smart_str_append(dest, Z_OBJCE_P(value)->name); |
239 | 5 | smart_str_appends(dest, "::"); |
240 | 5 | smart_str_append(dest, Z_STR_P(zend_enum_fetch_case_name(Z_OBJ_P(value)))); |
241 | 1.78k | } else { |
242 | 1.78k | return FAILURE; |
243 | 1.78k | } |
244 | 33.1k | return SUCCESS; |
245 | 34.9k | } |