1 /*
2 +----------------------------------------------------------------------+
3 | Zend Engine |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1998-2017 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
zend_interned_strings_init(void)45 void zend_interned_strings_init(void)
46 {
47 #ifndef ZTS
48 zend_string *str;
49
50 zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1);
51
52 CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
53 HT_SET_DATA_ADDR(&CG(interned_strings), pemalloc(HT_SIZE(&CG(interned_strings)), 1));
54 HT_HASH_RESET(&CG(interned_strings));
55 CG(interned_strings).u.flags |= HASH_FLAG_INITIALIZED;
56
57 /* interned empty string */
58 str = zend_string_alloc(sizeof("")-1, 1);
59 ZSTR_VAL(str)[0] = '\000';
60 CG(empty_string) = zend_new_interned_string_int(str);
61 #endif
62
63 /* one char strings (the actual interned strings are going to be created by ext/opcache) */
64 memset(CG(one_char_string), 0, sizeof(CG(one_char_string)));
65
66 zend_new_interned_string = zend_new_interned_string_int;
67 zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
68 zend_interned_strings_restore = zend_interned_strings_restore_int;
69 }
70
zend_interned_strings_dtor(void)71 void zend_interned_strings_dtor(void)
72 {
73 #ifndef ZTS
74 zend_hash_destroy(&CG(interned_strings));
75 #endif
76 }
77
zend_new_interned_string_int(zend_string * str)78 static zend_string *zend_new_interned_string_int(zend_string *str)
79 {
80 #ifndef ZTS
81 zend_ulong h;
82 uint nIndex;
83 uint idx;
84 Bucket *p;
85
86 if (ZSTR_IS_INTERNED(str)) {
87 return str;
88 }
89
90 h = zend_string_hash_val(str);
91 nIndex = h | CG(interned_strings).nTableMask;
92 idx = HT_HASH(&CG(interned_strings), nIndex);
93 while (idx != HT_INVALID_IDX) {
94 p = HT_HASH_TO_BUCKET(&CG(interned_strings), idx);
95 if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) {
96 if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) {
97 zend_string_release(str);
98 return p->key;
99 }
100 }
101 idx = Z_NEXT(p->val);
102 }
103
104 GC_REFCOUNT(str) = 1;
105 GC_FLAGS(str) |= IS_STR_INTERNED;
106
107 if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) {
108 if (CG(interned_strings).nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
109 void *new_data;
110 void *old_data = HT_GET_DATA_ADDR(&CG(interned_strings));
111 Bucket *old_buckets = CG(interned_strings).arData;
112
113 HANDLE_BLOCK_INTERRUPTIONS();
114 CG(interned_strings).nTableSize += CG(interned_strings).nTableSize;
115 CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
116 new_data = malloc(HT_SIZE(&CG(interned_strings)));
117
118 if (new_data) {
119 HT_SET_DATA_ADDR(&CG(interned_strings), new_data);
120 memcpy(CG(interned_strings).arData, old_buckets, sizeof(Bucket) * CG(interned_strings).nNumUsed);
121 free(old_data);
122 zend_hash_rehash(&CG(interned_strings));
123 } else {
124 CG(interned_strings).nTableSize = CG(interned_strings).nTableSize >> 1;
125 CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
126 }
127 HANDLE_UNBLOCK_INTERRUPTIONS();
128 }
129 }
130
131 HANDLE_BLOCK_INTERRUPTIONS();
132
133 idx = CG(interned_strings).nNumUsed++;
134 CG(interned_strings).nNumOfElements++;
135 p = CG(interned_strings).arData + idx;
136 p->h = h;
137 p->key = str;
138 Z_STR(p->val) = str;
139 Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX;
140 nIndex = h | CG(interned_strings).nTableMask;
141 Z_NEXT(p->val) = HT_HASH(&CG(interned_strings), nIndex);
142 HT_HASH(&CG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx);
143
144 HANDLE_UNBLOCK_INTERRUPTIONS();
145
146 return str;
147 #else
148 return str;
149 #endif
150 }
151
zend_interned_strings_snapshot_int(void)152 static void zend_interned_strings_snapshot_int(void)
153 {
154 #ifndef ZTS
155 uint idx;
156 Bucket *p;
157
158 idx = CG(interned_strings).nNumUsed;
159 while (idx > 0) {
160 idx--;
161 p = CG(interned_strings).arData + idx;
162 ZEND_ASSERT(GC_FLAGS(p->key) & IS_STR_PERSISTENT);
163 GC_FLAGS(p->key) |= IS_STR_PERMANENT;
164 }
165 #endif
166 }
167
zend_interned_strings_restore_int(void)168 static void zend_interned_strings_restore_int(void)
169 {
170 #ifndef ZTS
171 uint nIndex;
172 uint idx;
173 Bucket *p;
174
175 idx = CG(interned_strings).nNumUsed;
176 while (idx > 0) {
177 idx--;
178 p = CG(interned_strings).arData + idx;
179 if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break;
180 CG(interned_strings).nNumUsed--;
181 CG(interned_strings).nNumOfElements--;
182
183 GC_FLAGS(p->key) &= ~IS_STR_INTERNED;
184 GC_REFCOUNT(p->key) = 1;
185 zend_string_free(p->key);
186
187 nIndex = p->h | CG(interned_strings).nTableMask;
188 if (HT_HASH(&CG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) {
189 HT_HASH(&CG(interned_strings), nIndex) = Z_NEXT(p->val);
190 } else {
191 uint32_t prev = HT_HASH(&CG(interned_strings), nIndex);
192 while (Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) != idx) {
193 prev = Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val);
194 }
195 Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) = Z_NEXT(p->val);
196 }
197 }
198 #endif
199 }
200
201 /*
202 * Local variables:
203 * tab-width: 4
204 * c-basic-offset: 4
205 * indent-tabs-mode: t
206 * End:
207 */
208