1/*
2   +----------------------------------------------------------------------+
3   | Zend Engine                                                          |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2016 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
24ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str);
25ZEND_API void (*zend_interned_strings_snapshot)(void);
26ZEND_API void (*zend_interned_strings_restore)(void);
27
28static zend_string *zend_new_interned_string_int(zend_string *str);
29static void zend_interned_strings_snapshot_int(void);
30static void zend_interned_strings_restore_int(void);
31
32ZEND_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
38static 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
45void 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
71void zend_interned_strings_dtor(void)
72{
73#ifndef ZTS
74	zend_hash_destroy(&CG(interned_strings));
75#endif
76}
77
78static 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
152static 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
168static 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