xref: /PHP-7.1/Zend/zend_string.c (revision ccd4716e)
1 /*
2    +----------------------------------------------------------------------+
3    | Zend Engine                                                          |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1998-2018 Zend Technologies Ltd. (http://www.zend.com) |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 2.00 of the Zend 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.zend.com/license/2_00.txt.                                |
11    | If you did not receive a copy of the Zend license and are unable to  |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@zend.com so we can mail you a copy immediately.              |
14    +----------------------------------------------------------------------+
15    | Authors: Dmitry Stogov <dmitry@zend.com>                             |
16    +----------------------------------------------------------------------+
17 */
18 
19 /* $Id: $ */
20 
21 #include "zend.h"
22 #include "zend_globals.h"
23 
24 ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str);
25 ZEND_API void (*zend_interned_strings_snapshot)(void);
26 ZEND_API void (*zend_interned_strings_restore)(void);
27 
28 static zend_string *zend_new_interned_string_int(zend_string *str);
29 static void zend_interned_strings_snapshot_int(void);
30 static void zend_interned_strings_restore_int(void);
31 
zend_hash_func(const char * str,size_t len)32 ZEND_API zend_ulong zend_hash_func(const char *str, size_t len)
33 {
34 	return zend_inline_hash_func(str, len);
35 }
36 
37 #ifndef ZTS
_str_dtor(zval * zv)38 static void _str_dtor(zval *zv)
39 {
40 	zend_string *str = Z_STR_P(zv);
41 	pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT);
42 }
43 #endif
44 
45 /* Readonly, so assigned also per thread. */
46 static const zend_string **known_interned_strings = NULL;
47 static uint32_t known_interned_strings_count = 0;
48 
zend_intern_known_strings(const char ** strings,uint32_t count)49 ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count)
50 {
51 	uint32_t i, old_count = known_interned_strings_count;
52 
53 	known_interned_strings = perealloc(known_interned_strings, sizeof(char*) * (old_count + count), 1);
54 	for (i = 0; i < count; i++) {
55 #ifndef ZTS
56 		zend_string *str = zend_string_init(strings[i], strlen(strings[i]), 1);
57 		known_interned_strings[known_interned_strings_count + i] =
58 			zend_new_interned_string_int(str);
59 #else
60 		known_interned_strings[known_interned_strings_count + i] =
61 			zend_zts_interned_string_init(strings[i], strlen(strings[i]));
62 #endif
63 	}
64 	known_interned_strings_count = old_count + count;
65 	return old_count;
66 }
67 
68 static const char *known_strings[] = {
69 #define _ZEND_STR_DSC(id, str) str,
70 ZEND_KNOWN_STRINGS(_ZEND_STR_DSC)
71 #undef _ZEND_STR_DSC
72 	NULL
73 };
74 
zend_known_interned_strings_init(zend_string *** strings,uint32_t * count)75 void zend_known_interned_strings_init(zend_string ***strings, uint32_t *count)
76 {
77 	*strings = (zend_string **)known_interned_strings;
78 	*count   = known_interned_strings_count;
79 }
80 
zend_interned_strings_init(void)81 void zend_interned_strings_init(void)
82 {
83 #ifndef ZTS
84 	zend_string *str;
85 
86 	zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1);
87 
88 	CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
89 	HT_SET_DATA_ADDR(&CG(interned_strings), pemalloc(HT_SIZE(&CG(interned_strings)), 1));
90 	HT_HASH_RESET(&CG(interned_strings));
91 	CG(interned_strings).u.flags |= HASH_FLAG_INITIALIZED;
92 
93 	/* interned empty string */
94 	str = zend_string_alloc(sizeof("")-1, 1);
95 	ZSTR_VAL(str)[0] = '\000';
96 	CG(empty_string) = zend_new_interned_string_int(str);
97 #endif
98 
99 	/* one char strings (the actual interned strings are going to be created by ext/opcache) */
100 	memset(CG(one_char_string), 0, sizeof(CG(one_char_string)));
101 
102 	/* known strings */
103 	zend_intern_known_strings(known_strings, (sizeof(known_strings) / sizeof(known_strings[0])) - 1);
104 	zend_known_interned_strings_init(&CG(known_strings), &CG(known_strings_count));
105 
106 	zend_new_interned_string = zend_new_interned_string_int;
107 	zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
108 	zend_interned_strings_restore = zend_interned_strings_restore_int;
109 }
110 
zend_interned_strings_dtor(void)111 void zend_interned_strings_dtor(void)
112 {
113 #ifndef ZTS
114 	zend_hash_destroy(&CG(interned_strings));
115 #else
116 	uint32_t i;
117 
118 	for (i = 0; i < CG(known_strings_count); i++) {
119 		zend_zts_interned_string_free(&CG(known_strings)[i]);
120 	}
121 #endif
122 	free(CG(known_strings));
123 	CG(known_strings) = NULL;
124 	CG(known_strings_count) = 0;
125 	known_interned_strings = NULL;
126 	known_interned_strings_count = 0;
127 }
128 
zend_new_interned_string_int(zend_string * str)129 static zend_string *zend_new_interned_string_int(zend_string *str)
130 {
131 #ifndef ZTS
132 	zend_ulong h;
133 	uint nIndex;
134 	uint idx;
135 	Bucket *p;
136 
137 	if (ZSTR_IS_INTERNED(str)) {
138 		return str;
139 	}
140 
141 	h = zend_string_hash_val(str);
142 	nIndex = h | CG(interned_strings).nTableMask;
143 	idx = HT_HASH(&CG(interned_strings), nIndex);
144 	while (idx != HT_INVALID_IDX) {
145 		p = HT_HASH_TO_BUCKET(&CG(interned_strings), idx);
146 		if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) {
147 			if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) {
148 				zend_string_release(str);
149 				return p->key;
150 			}
151 		}
152 		idx = Z_NEXT(p->val);
153 	}
154 
155 	GC_REFCOUNT(str) = 1;
156 	GC_FLAGS(str) |= IS_STR_INTERNED;
157 
158 	if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) {
159 		if (CG(interned_strings).nTableSize < HT_MAX_SIZE) {	/* Let's double the table size */
160 			void *new_data;
161 			void *old_data = HT_GET_DATA_ADDR(&CG(interned_strings));
162 			Bucket *old_buckets = CG(interned_strings).arData;
163 
164 			CG(interned_strings).nTableSize += CG(interned_strings).nTableSize;
165 			CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
166 			new_data = malloc(HT_SIZE(&CG(interned_strings)));
167 
168 			if (new_data) {
169 				HT_SET_DATA_ADDR(&CG(interned_strings), new_data);
170 				memcpy(CG(interned_strings).arData, old_buckets, sizeof(Bucket) * CG(interned_strings).nNumUsed);
171 				free(old_data);
172 				zend_hash_rehash(&CG(interned_strings));
173 			} else {
174 				CG(interned_strings).nTableSize = CG(interned_strings).nTableSize >> 1;
175 				CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
176 			}
177 		}
178 	}
179 
180 	idx = CG(interned_strings).nNumUsed++;
181 	CG(interned_strings).nNumOfElements++;
182 	p = CG(interned_strings).arData + idx;
183 	p->h = h;
184 	p->key = str;
185 	Z_STR(p->val) = str;
186 	Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX;
187 	nIndex = h | CG(interned_strings).nTableMask;
188 	Z_NEXT(p->val) = HT_HASH(&CG(interned_strings), nIndex);
189 	HT_HASH(&CG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx);
190 
191 	return str;
192 #else
193 	return str;
194 #endif
195 }
196 
zend_interned_strings_snapshot_int(void)197 static void zend_interned_strings_snapshot_int(void)
198 {
199 #ifndef ZTS
200 	uint idx;
201 	Bucket *p;
202 
203 	idx = CG(interned_strings).nNumUsed;
204 	while (idx > 0) {
205 		idx--;
206 		p = CG(interned_strings).arData + idx;
207 		ZEND_ASSERT(GC_FLAGS(p->key) & IS_STR_PERSISTENT);
208 		GC_FLAGS(p->key) |= IS_STR_PERMANENT;
209 	}
210 #endif
211 }
212 
zend_interned_strings_restore_int(void)213 static void zend_interned_strings_restore_int(void)
214 {
215 #ifndef ZTS
216 	uint nIndex;
217 	uint idx;
218 	Bucket *p;
219 
220 	idx = CG(interned_strings).nNumUsed;
221 	while (idx > 0) {
222 		idx--;
223 		p = CG(interned_strings).arData + idx;
224 		if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break;
225 		CG(interned_strings).nNumUsed--;
226 		CG(interned_strings).nNumOfElements--;
227 
228 		GC_FLAGS(p->key) &= ~IS_STR_INTERNED;
229 		GC_REFCOUNT(p->key) = 1;
230 		zend_string_free(p->key);
231 
232 		nIndex = p->h | CG(interned_strings).nTableMask;
233 		if (HT_HASH(&CG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) {
234 			HT_HASH(&CG(interned_strings), nIndex) = Z_NEXT(p->val);
235 		} else {
236 			uint32_t prev = HT_HASH(&CG(interned_strings), nIndex);
237 			while (Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) != idx) {
238 				prev = Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val);
239  			}
240 			Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) = Z_NEXT(p->val);
241  		}
242  	}
243 #endif
244 }
245 
246 /*
247  * Local variables:
248  * tab-width: 4
249  * c-basic-offset: 4
250  * indent-tabs-mode: t
251  * End:
252  */
253