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