1/*
2   +----------------------------------------------------------------------+
3   | Zend Engine                                                          |
4   +----------------------------------------------------------------------+
5   | Copyright (c) 1998-2014 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 TSRMLS_DC);
25ZEND_API void (*zend_interned_strings_snapshot)(TSRMLS_D);
26ZEND_API void (*zend_interned_strings_restore)(TSRMLS_D);
27
28static zend_string *zend_new_interned_string_int(zend_string *str TSRMLS_DC);
29static void zend_interned_strings_snapshot_int(TSRMLS_D);
30static void zend_interned_strings_restore_int(TSRMLS_D);
31
32ZEND_API zend_uint_t zend_hash_func(const char *str, zend_size_t len)
33{
34    return zend_inline_hash_func(str, len);
35}
36
37static void _str_dtor(zval *zv)
38{
39    zend_string *str = Z_STR_P(zv);
40    GC_FLAGS(str) &= ~IS_STR_INTERNED;
41    GC_REFCOUNT(str) = 1;
42}
43
44void zend_interned_strings_init(TSRMLS_D)
45{
46    zend_string *str;
47
48#ifndef ZTS
49    zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1);
50
51    CG(interned_strings).nTableMask = CG(interned_strings).nTableSize - 1;
52    CG(interned_strings).arData = (Bucket*) pecalloc(CG(interned_strings).nTableSize, sizeof(Bucket), 1);
53    CG(interned_strings).arHash = (zend_uint*) pecalloc(CG(interned_strings).nTableSize, sizeof(zend_uint), 1);
54    memset(CG(interned_strings).arHash, INVALID_IDX, CG(interned_strings).nTableSize * sizeof(zend_uint));
55
56    /* interned empty string */
57    str = STR_ALLOC(sizeof("")-1, 1);
58    str->val[0] = '\000';
59    CG(empty_string) = zend_new_interned_string_int(str TSRMLS_CC);
60#else
61    str = STR_ALLOC(sizeof("")-1, 1);
62    str->val[0] = '\000';
63    STR_HASH_VAL(str);
64    str->gc.u.v.flags |= IS_STR_INTERNED;
65    CG(empty_string) = str;
66#endif
67
68    /* one char strings (the actual interned strings are going to be created by ext/opcache) */
69    memset(CG(one_char_string), 0, sizeof(CG(one_char_string)));
70
71    zend_new_interned_string = zend_new_interned_string_int;
72    zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
73    zend_interned_strings_restore = zend_interned_strings_restore_int;
74}
75
76void zend_interned_strings_dtor(TSRMLS_D)
77{
78#ifndef ZTS
79    zend_hash_destroy(&CG(interned_strings));
80#else
81    free(CG(empty_string));
82#endif
83}
84
85static zend_string *zend_new_interned_string_int(zend_string *str TSRMLS_DC)
86{
87#ifndef ZTS
88    zend_uint_t h;
89    uint nIndex;
90    uint idx;
91    Bucket *p;
92
93    if (IS_INTERNED(str)) {
94        return str;
95    }
96
97    h = STR_HASH_VAL(str);
98    nIndex = h & CG(interned_strings).nTableMask;
99    idx = CG(interned_strings).arHash[nIndex];
100    while (idx != INVALID_IDX) {
101        p = CG(interned_strings).arData + idx;
102        if ((p->h == h) && (p->key->len == str->len)) {
103            if (!memcmp(p->key->val, str->val, str->len)) {
104                STR_RELEASE(str);
105                return p->key;
106            }
107        }
108        idx = Z_NEXT(p->val);
109    }
110
111    GC_REFCOUNT(str) = 1;
112    GC_FLAGS(str) |= IS_STR_INTERNED;
113
114    if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) {
115        if ((CG(interned_strings).nTableSize << 1) > 0) {   /* Let's double the table size */
116            Bucket *d = (Bucket *) perealloc_recoverable(CG(interned_strings).arData, (CG(interned_strings).nTableSize << 1) * sizeof(Bucket), 1);
117            zend_uint *h = (zend_uint *) perealloc_recoverable(CG(interned_strings).arHash, (CG(interned_strings).nTableSize << 1) * sizeof(zend_uint), 1);
118
119            if (d && h) {
120                HANDLE_BLOCK_INTERRUPTIONS();
121                CG(interned_strings).arData = d;
122                CG(interned_strings).arHash = h;
123                CG(interned_strings).nTableSize = (CG(interned_strings).nTableSize << 1);
124                CG(interned_strings).nTableMask = CG(interned_strings).nTableSize - 1;
125                zend_hash_rehash(&CG(interned_strings));
126                HANDLE_UNBLOCK_INTERRUPTIONS();
127            }
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) = CG(interned_strings).arHash[nIndex];
142    CG(interned_strings).arHash[nIndex] = 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(TSRMLS_D)
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(TSRMLS_D)
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        STR_FREE(p->key);
186
187        nIndex = p->h & CG(interned_strings).nTableMask;
188        if (CG(interned_strings).arHash[nIndex] == idx) {
189            CG(interned_strings).arHash[nIndex] = Z_NEXT(p->val);
190        } else {
191            uint prev = CG(interned_strings).arHash[nIndex];
192            while (Z_NEXT(CG(interned_strings).arData[prev].val) != idx) {
193                prev = Z_NEXT(CG(interned_strings).arData[prev].val);
194            }
195            Z_NEXT(CG(interned_strings).arData[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