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
24#ifndef ZEND_DEBUG_INTERNED_STRINGS
25# define ZEND_DEBUG_INTERNED_STRINGS 0
26#endif
27
28#if ZEND_DEBUG_INTERNED_STRINGS
29# include <sys/mman.h>
30#endif
31
32ZEND_API const char *(*zend_new_interned_string)(const char *str, int len, int free_src TSRMLS_DC);
33ZEND_API void (*zend_interned_strings_snapshot)(TSRMLS_D);
34ZEND_API void (*zend_interned_strings_restore)(TSRMLS_D);
35
36static const char *zend_new_interned_string_int(const char *str, int len, int free_src TSRMLS_DC);
37static void zend_interned_strings_snapshot_int(TSRMLS_D);
38static void zend_interned_strings_restore_int(TSRMLS_D);
39
40void zend_interned_strings_init(TSRMLS_D)
41{
42#ifndef ZTS
43    size_t size = 1024 * 1024;
44
45#if ZEND_DEBUG_INTERNED_STRINGS
46    CG(interned_strings_start) = valloc(size);
47#else
48    CG(interned_strings_start) = malloc(size);
49#endif
50
51    CG(interned_strings_top) = CG(interned_strings_start);
52    CG(interned_strings_snapshot_top) = CG(interned_strings_start);
53    CG(interned_strings_end) = CG(interned_strings_start) + size;
54
55    zend_hash_init(&CG(interned_strings), 0, NULL, NULL, 1);
56
57    CG(interned_strings).nTableMask = CG(interned_strings).nTableSize - 1;
58    CG(interned_strings).arBuckets = (Bucket **) pecalloc(CG(interned_strings).nTableSize, sizeof(Bucket *), CG(interned_strings).persistent);
59
60#if ZEND_DEBUG_INTERNED_STRINGS
61    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_READ);
62#endif
63
64    /* interned empty string */
65    CG(interned_empty_string) = zend_new_interned_string_int("", sizeof(""), 0 TSRMLS_CC);
66#endif
67
68    zend_new_interned_string = zend_new_interned_string_int;
69    zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
70    zend_interned_strings_restore = zend_interned_strings_restore_int;
71}
72
73void zend_interned_strings_dtor(TSRMLS_D)
74{
75#ifndef ZTS
76#if ZEND_DEBUG_INTERNED_STRINGS
77    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_WRITE | PROT_READ);
78#endif
79    free(CG(interned_strings).arBuckets);
80    free(CG(interned_strings_start));
81#endif
82}
83
84static const char *zend_new_interned_string_int(const char *arKey, int nKeyLength, int free_src TSRMLS_DC)
85{
86#ifndef ZTS
87    ulong h;
88    uint nIndex;
89    Bucket *p;
90
91    if (IS_INTERNED(arKey)) {
92        return arKey;
93    }
94
95    h = zend_inline_hash_func(arKey, nKeyLength);
96    nIndex = h & CG(interned_strings).nTableMask;
97    p = CG(interned_strings).arBuckets[nIndex];
98    while (p != NULL) {
99        if ((p->h == h) && (p->nKeyLength == nKeyLength)) {
100            if (!memcmp(p->arKey, arKey, nKeyLength)) {
101                if (free_src) {
102                    efree((void *)arKey);
103                }
104                return p->arKey;
105            }
106        }
107        p = p->pNext;
108    }
109
110    if (CG(interned_strings_top) + ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength) >=
111        CG(interned_strings_end)) {
112        /* no memory */
113        return arKey;
114    }
115
116    p = (Bucket *) CG(interned_strings_top);
117    CG(interned_strings_top) += ZEND_MM_ALIGNED_SIZE(sizeof(Bucket) + nKeyLength);
118
119#if ZEND_DEBUG_INTERNED_STRINGS
120    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_READ | PROT_WRITE);
121#endif
122
123    p->arKey = (char*)(p+1);
124    memcpy((char*)p->arKey, arKey, nKeyLength);
125    if (free_src) {
126        efree((void *)arKey);
127    }
128    p->nKeyLength = nKeyLength;
129    p->h = h;
130    p->pData = &p->pDataPtr;
131    p->pDataPtr = p;
132
133    p->pNext = CG(interned_strings).arBuckets[nIndex];
134    p->pLast = NULL;
135    if (p->pNext) {
136        p->pNext->pLast = p;
137    }
138
139    HANDLE_BLOCK_INTERRUPTIONS();
140
141    p->pListLast = CG(interned_strings).pListTail;
142    CG(interned_strings).pListTail = p;
143    p->pListNext = NULL;
144    if (p->pListLast != NULL) {
145        p->pListLast->pListNext = p;
146    }
147    if (!CG(interned_strings).pListHead) {
148        CG(interned_strings).pListHead = p;
149    }
150
151    CG(interned_strings).arBuckets[nIndex] = p;
152
153    HANDLE_UNBLOCK_INTERRUPTIONS();
154
155    CG(interned_strings).nNumOfElements++;
156
157    if (CG(interned_strings).nNumOfElements > CG(interned_strings).nTableSize) {
158        if ((CG(interned_strings).nTableSize << 1) > 0) {   /* Let's double the table size */
159            Bucket **t = (Bucket **) perealloc_recoverable(CG(interned_strings).arBuckets, (CG(interned_strings).nTableSize << 1) * sizeof(Bucket *), CG(interned_strings).persistent);
160
161            if (t) {
162                HANDLE_BLOCK_INTERRUPTIONS();
163                CG(interned_strings).arBuckets = t;
164                CG(interned_strings).nTableSize = (CG(interned_strings).nTableSize << 1);
165                CG(interned_strings).nTableMask = CG(interned_strings).nTableSize - 1;
166                zend_hash_rehash(&CG(interned_strings));
167                HANDLE_UNBLOCK_INTERRUPTIONS();
168            }
169        }
170    }
171
172#if ZEND_DEBUG_INTERNED_STRINGS
173    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_READ);
174#endif
175
176    return p->arKey;
177#else
178    return arKey;
179#endif
180}
181
182static void zend_interned_strings_snapshot_int(TSRMLS_D)
183{
184    CG(interned_strings_snapshot_top) = CG(interned_strings_top);
185}
186
187static void zend_interned_strings_restore_int(TSRMLS_D)
188{
189#ifndef ZTS
190    Bucket *p;
191    int i;
192#endif
193
194    CG(interned_strings_top) = CG(interned_strings_snapshot_top);
195
196#ifndef ZTS
197#if ZEND_DEBUG_INTERNED_STRINGS
198    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_WRITE | PROT_READ);
199#endif
200
201    for (i = 0; i < CG(interned_strings).nTableSize; i++) {
202        p = CG(interned_strings).arBuckets[i];
203        while (p && p->arKey > CG(interned_strings_top)) {
204            CG(interned_strings).nNumOfElements--;
205            if (p->pListLast != NULL) {
206                p->pListLast->pListNext = p->pListNext;
207            } else {
208                CG(interned_strings).pListHead = p->pListNext;
209            }
210            if (p->pListNext != NULL) {
211                p->pListNext->pListLast = p->pListLast;
212            } else {
213                CG(interned_strings).pListTail = p->pListLast;
214            }
215            p = p->pNext;
216        }
217        if (p) {
218            p->pLast = NULL;
219        }
220        CG(interned_strings).arBuckets[i] = p;
221    }
222
223#if ZEND_DEBUG_INTERNED_STRINGS
224    mprotect(CG(interned_strings_start), CG(interned_strings_end) - CG(interned_strings_start), PROT_READ);
225#endif
226#endif
227}
228
229/*
230 * Local variables:
231 * tab-width: 4
232 * c-basic-offset: 4
233 * indent-tabs-mode: t
234 * End:
235 */
236