xref: /PHP-7.0/Zend/zend_string.c (revision 478f119a)
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