xref: /PHP-7.4/Zend/zend_smart_str.c (revision 549cb440)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) The PHP Group                                          |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Dmitry Stogov <dmitry@php.net>                               |
16    +----------------------------------------------------------------------+
17  */
18 
19 #include <zend.h>
20 #include "zend_smart_str.h"
21 #include "zend_smart_string.h"
22 
23 #define SMART_STR_OVERHEAD   (ZEND_MM_OVERHEAD + _ZSTR_HEADER_SIZE + 1)
24 #define SMART_STR_START_SIZE 256
25 #define SMART_STR_START_LEN  (SMART_STR_START_SIZE - SMART_STR_OVERHEAD)
26 #define SMART_STR_PAGE       4096
27 
28 #define SMART_STR_NEW_LEN(len) \
29 	(ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STR_OVERHEAD, SMART_STR_PAGE) - SMART_STR_OVERHEAD)
30 
smart_str_erealloc(smart_str * str,size_t len)31 ZEND_API void ZEND_FASTCALL smart_str_erealloc(smart_str *str, size_t len)
32 {
33 	if (UNEXPECTED(!str->s)) {
34 		str->a = len <= SMART_STR_START_LEN
35 				? SMART_STR_START_LEN
36 				: SMART_STR_NEW_LEN(len);
37 		str->s = zend_string_alloc(str->a, 0);
38 		ZSTR_LEN(str->s) = 0;
39 	} else {
40 		str->a = SMART_STR_NEW_LEN(len);
41 		str->s = (zend_string *) erealloc2(str->s, str->a + _ZSTR_HEADER_SIZE + 1, _ZSTR_HEADER_SIZE + ZSTR_LEN(str->s));
42 	}
43 }
44 
smart_str_realloc(smart_str * str,size_t len)45 ZEND_API void ZEND_FASTCALL smart_str_realloc(smart_str *str, size_t len)
46 {
47 	if (UNEXPECTED(!str->s)) {
48 		str->a = len <= SMART_STR_START_LEN
49 				? SMART_STR_START_LEN
50 				: SMART_STR_NEW_LEN(len);
51 		str->s = zend_string_alloc(str->a, 1);
52 		ZSTR_LEN(str->s) = 0;
53 	} else {
54 		str->a = SMART_STR_NEW_LEN(len);
55 		str->s = (zend_string *) perealloc(str->s, str->a + _ZSTR_HEADER_SIZE + 1, 1);
56 	}
57 }
58 
59 /* Windows uses VK_ESCAPE instead of \e */
60 #ifndef VK_ESCAPE
61 #define VK_ESCAPE '\e'
62 #endif
63 
zend_compute_escaped_string_len(const char * s,size_t l)64 static size_t zend_compute_escaped_string_len(const char *s, size_t l) {
65 	size_t i, len = l;
66 	for (i = 0; i < l; ++i) {
67 		char c = s[i];
68 		if (c == '\n' || c == '\r' || c == '\t' ||
69 			c == '\f' || c == '\v' || c == '\\' || c == VK_ESCAPE) {
70 			len += 1;
71 		} else if (c < 32 || c > 126) {
72 			len += 3;
73 		}
74 	}
75 	return len;
76 }
77 
smart_str_append_escaped(smart_str * str,const char * s,size_t l)78 ZEND_API void ZEND_FASTCALL smart_str_append_escaped(smart_str *str, const char *s, size_t l) {
79 	char *res;
80 	size_t i, len = zend_compute_escaped_string_len(s, l);
81 
82 	smart_str_alloc(str, len, 0);
83 	res = &ZSTR_VAL(str->s)[ZSTR_LEN(str->s)];
84 	ZSTR_LEN(str->s) += len;
85 
86 	for (i = 0; i < l; ++i) {
87 		unsigned char c = s[i];
88 		if (c < 32 || c == '\\' || c > 126) {
89 			*res++ = '\\';
90 			switch (c) {
91 				case '\n': *res++ = 'n'; break;
92 				case '\r': *res++ = 'r'; break;
93 				case '\t': *res++ = 't'; break;
94 				case '\f': *res++ = 'f'; break;
95 				case '\v': *res++ = 'v'; break;
96 				case '\\': *res++ = '\\'; break;
97 				case VK_ESCAPE: *res++ = 'e'; break;
98 				default:
99 					*res++ = 'x';
100 					if ((c >> 4) < 10) {
101 						*res++ = (c >> 4) + '0';
102 					} else {
103 						*res++ = (c >> 4) + 'A' - 10;
104 					}
105 					if ((c & 0xf) < 10) {
106 						*res++ = (c & 0xf) + '0';
107 					} else {
108 						*res++ = (c & 0xf) + 'A' - 10;
109 					}
110 			}
111 		} else {
112 			*res++ = c;
113 		}
114 	}
115 }
116 
smart_str_append_printf(smart_str * dest,const char * format,...)117 ZEND_API void smart_str_append_printf(smart_str *dest, const char *format, ...) {
118 	va_list arg;
119 	va_start(arg, format);
120 	zend_printf_to_smart_str(dest, format, arg);
121 	va_end(arg);
122 }
123 
124 #define SMART_STRING_OVERHEAD   (ZEND_MM_OVERHEAD + 1)
125 #define SMART_STRING_START_SIZE 256
126 #define SMART_STRING_START_LEN  (SMART_STRING_START_SIZE - SMART_STRING_OVERHEAD)
127 #define SMART_STRING_PAGE       4096
128 
_smart_string_alloc_persistent(smart_string * str,size_t len)129 ZEND_API void ZEND_FASTCALL _smart_string_alloc_persistent(smart_string *str, size_t len)
130 {
131 	if (!str->c) {
132 		str->len = 0;
133 		if (len <= SMART_STRING_START_LEN) {
134 			str->a = SMART_STRING_START_LEN;
135 		} else {
136 			str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
137 		}
138 		str->c = pemalloc(str->a + 1, 1);
139 	} else {
140 		if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
141 			zend_error(E_ERROR, "String size overflow");
142 		}
143 		len += str->len;
144 		str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
145 		str->c = perealloc(str->c, str->a + 1, 1);
146 	}
147 }
148 
_smart_string_alloc(smart_string * str,size_t len)149 ZEND_API void ZEND_FASTCALL _smart_string_alloc(smart_string *str, size_t len)
150 {
151 	if (!str->c) {
152 		str->len = 0;
153 		if (len <= SMART_STRING_START_LEN) {
154 			str->a = SMART_STRING_START_LEN;
155 			str->c = emalloc(SMART_STRING_START_LEN + 1);
156 		} else {
157 			str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
158 			if (EXPECTED(str->a < (ZEND_MM_CHUNK_SIZE - SMART_STRING_OVERHEAD))) {
159 				str->c = emalloc_large(str->a + 1);
160 			} else {
161 				/* allocate a huge chunk */
162 				str->c = emalloc(str->a + 1);
163 			}
164 		}
165 	} else {
166 		if (UNEXPECTED((size_t) len > SIZE_MAX - str->len)) {
167 			zend_error(E_ERROR, "String size overflow");
168 		}
169 		len += str->len;
170 		str->a = ZEND_MM_ALIGNED_SIZE_EX(len + SMART_STRING_OVERHEAD, SMART_STRING_PAGE) - SMART_STRING_OVERHEAD;
171 		str->c = erealloc2(str->c, str->a + 1, str->len);
172 	}
173 }
174