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