2010-04-20 19:05:54 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Zend Engine |
|
|
|
|
+----------------------------------------------------------------------+
|
2016-01-02 17:56:11 +08:00
|
|
|
| Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
|
2010-04-20 19:05:54 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| This source file is subject to version 2.00 of the Zend license, |
|
2015-01-03 17:22:58 +08:00
|
|
|
| that is bundled with this package in the file LICENSE, and is |
|
2010-04-20 19:05:54 +08:00
|
|
|
| available through the world-wide-web at the following url: |
|
|
|
|
| http://www.zend.com/license/2_00.txt. |
|
|
|
|
| If you did not receive a copy of the Zend license and are unable to |
|
|
|
|
| obtain it through the world-wide-web, please send a note to |
|
|
|
|
| license@zend.com so we can mail you a copy immediately. |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| Authors: Dmitry Stogov <dmitry@zend.com> |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* $Id: $ */
|
|
|
|
|
|
|
|
#include "zend.h"
|
|
|
|
#include "zend_globals.h"
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
ZEND_API zend_string *(*zend_new_interned_string)(zend_string *str);
|
|
|
|
ZEND_API void (*zend_interned_strings_snapshot)(void);
|
|
|
|
ZEND_API void (*zend_interned_strings_restore)(void);
|
2010-05-25 17:00:20 +08:00
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static zend_string *zend_new_interned_string_int(zend_string *str);
|
|
|
|
static void zend_interned_strings_snapshot_int(void);
|
|
|
|
static void zend_interned_strings_restore_int(void);
|
2010-04-20 19:05:54 +08:00
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
ZEND_API zend_ulong zend_hash_func(const char *str, size_t len)
|
2014-02-10 14:04:30 +08:00
|
|
|
{
|
|
|
|
return zend_inline_hash_func(str, len);
|
|
|
|
}
|
|
|
|
|
2014-08-26 05:05:05 +08:00
|
|
|
#ifndef ZTS
|
2014-02-17 21:59:18 +08:00
|
|
|
static void _str_dtor(zval *zv)
|
|
|
|
{
|
|
|
|
zend_string *str = Z_STR_P(zv);
|
2015-04-28 11:14:21 +08:00
|
|
|
pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT);
|
2014-02-17 21:59:18 +08:00
|
|
|
}
|
2014-08-26 05:05:05 +08:00
|
|
|
#endif
|
2014-02-17 21:59:18 +08:00
|
|
|
|
2016-06-02 23:55:48 +08:00
|
|
|
/* Readonly, so assigned also per thread. */
|
|
|
|
static const zend_string **known_interned_strings = NULL;
|
|
|
|
static uint32_t known_interned_strings_count = 0;
|
|
|
|
|
2016-05-12 18:47:22 +08:00
|
|
|
ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count)
|
|
|
|
{
|
2016-06-02 23:55:48 +08:00
|
|
|
uint32_t i, old_count = known_interned_strings_count;
|
2016-05-12 18:47:22 +08:00
|
|
|
|
2016-06-02 23:55:48 +08:00
|
|
|
known_interned_strings = perealloc(known_interned_strings, sizeof(char*) * (old_count + count), 1);
|
2016-05-12 18:47:22 +08:00
|
|
|
for (i = 0; i < count; i++) {
|
|
|
|
#ifndef ZTS
|
|
|
|
zend_string *str = zend_string_init(strings[i], strlen(strings[i]), 1);
|
2016-06-02 23:55:48 +08:00
|
|
|
known_interned_strings[known_interned_strings_count + i] =
|
2016-05-12 18:47:22 +08:00
|
|
|
zend_new_interned_string_int(str);
|
|
|
|
#else
|
2016-06-02 23:55:48 +08:00
|
|
|
known_interned_strings[known_interned_strings_count + i] =
|
2016-05-12 18:47:22 +08:00
|
|
|
zend_zts_interned_string_init(strings[i], strlen(strings[i]));
|
|
|
|
#endif
|
|
|
|
}
|
2016-06-02 23:55:48 +08:00
|
|
|
known_interned_strings_count = old_count + count;
|
2016-05-12 18:47:22 +08:00
|
|
|
return old_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char *known_strings[] = {
|
|
|
|
#define _ZEND_STR_DSC(id, str) str,
|
|
|
|
ZEND_KNOWN_STRINGS(_ZEND_STR_DSC)
|
|
|
|
#undef _ZEND_STR_DSC
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
2016-06-02 23:55:48 +08:00
|
|
|
void zend_known_interned_strings_init(zend_string ***strings, uint32_t *count)
|
|
|
|
{
|
2016-06-11 16:11:40 +08:00
|
|
|
*strings = (zend_string **)known_interned_strings;
|
2016-06-02 23:55:48 +08:00
|
|
|
*count = known_interned_strings_count;
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
void zend_interned_strings_init(void)
|
2010-04-20 19:05:54 +08:00
|
|
|
{
|
2014-10-06 21:44:43 +08:00
|
|
|
#ifndef ZTS
|
2014-02-10 14:04:30 +08:00
|
|
|
zend_string *str;
|
2010-04-20 19:05:54 +08:00
|
|
|
|
2014-04-21 22:25:34 +08:00
|
|
|
zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1);
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2015-03-13 22:13:19 +08:00
|
|
|
CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
|
|
|
|
HT_SET_DATA_ADDR(&CG(interned_strings), pemalloc(HT_SIZE(&CG(interned_strings)), 1));
|
|
|
|
HT_HASH_RESET(&CG(interned_strings));
|
2015-04-22 04:26:34 +08:00
|
|
|
CG(interned_strings).u.flags |= HASH_FLAG_INITIALIZED;
|
2014-02-10 14:04:30 +08:00
|
|
|
|
|
|
|
/* interned empty string */
|
2014-08-26 01:24:55 +08:00
|
|
|
str = zend_string_alloc(sizeof("")-1, 1);
|
2015-06-30 18:59:27 +08:00
|
|
|
ZSTR_VAL(str)[0] = '\000';
|
2014-12-14 06:06:14 +08:00
|
|
|
CG(empty_string) = zend_new_interned_string_int(str);
|
2010-04-20 19:05:54 +08:00
|
|
|
#endif
|
|
|
|
|
2014-05-06 03:16:49 +08:00
|
|
|
/* one char strings (the actual interned strings are going to be created by ext/opcache) */
|
|
|
|
memset(CG(one_char_string), 0, sizeof(CG(one_char_string)));
|
|
|
|
|
2016-05-12 18:47:22 +08:00
|
|
|
/* known strings */
|
|
|
|
zend_intern_known_strings(known_strings, (sizeof(known_strings) / sizeof(known_strings[0])) - 1);
|
2016-06-02 23:55:48 +08:00
|
|
|
zend_known_interned_strings_init(&CG(known_strings), &CG(known_strings_count));
|
2016-05-12 18:47:22 +08:00
|
|
|
|
2010-05-25 17:00:20 +08:00
|
|
|
zend_new_interned_string = zend_new_interned_string_int;
|
|
|
|
zend_interned_strings_snapshot = zend_interned_strings_snapshot_int;
|
|
|
|
zend_interned_strings_restore = zend_interned_strings_restore_int;
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
void zend_interned_strings_dtor(void)
|
2010-04-20 19:05:54 +08:00
|
|
|
{
|
|
|
|
#ifndef ZTS
|
2014-02-17 21:59:18 +08:00
|
|
|
zend_hash_destroy(&CG(interned_strings));
|
2016-05-12 18:47:22 +08:00
|
|
|
#else
|
|
|
|
uint32_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < CG(known_strings_count); i++) {
|
|
|
|
zend_zts_interned_string_free(&CG(known_strings)[i]);
|
|
|
|
}
|
2010-04-20 19:05:54 +08:00
|
|
|
#endif
|
2016-05-12 18:47:22 +08:00
|
|
|
free(CG(known_strings));
|
|
|
|
CG(known_strings) = NULL;
|
|
|
|
CG(known_strings_count) = 0;
|
2016-06-03 01:42:50 +08:00
|
|
|
known_interned_strings = NULL;
|
|
|
|
known_interned_strings_count = 0;
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static zend_string *zend_new_interned_string_int(zend_string *str)
|
2010-04-20 19:05:54 +08:00
|
|
|
{
|
|
|
|
#ifndef ZTS
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_ulong h;
|
2010-04-20 19:05:54 +08:00
|
|
|
uint nIndex;
|
2014-02-10 14:04:30 +08:00
|
|
|
uint idx;
|
2010-04-20 19:05:54 +08:00
|
|
|
Bucket *p;
|
|
|
|
|
2015-06-29 21:44:54 +08:00
|
|
|
if (ZSTR_IS_INTERNED(str)) {
|
2014-02-10 14:04:30 +08:00
|
|
|
return str;
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
|
2014-08-26 01:24:55 +08:00
|
|
|
h = zend_string_hash_val(str);
|
2015-03-13 22:13:19 +08:00
|
|
|
nIndex = h | CG(interned_strings).nTableMask;
|
|
|
|
idx = HT_HASH(&CG(interned_strings), nIndex);
|
|
|
|
while (idx != HT_INVALID_IDX) {
|
|
|
|
p = HT_HASH_TO_BUCKET(&CG(interned_strings), idx);
|
2015-06-30 18:59:27 +08:00
|
|
|
if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) {
|
|
|
|
if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) {
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_string_release(str);
|
2014-02-10 14:04:30 +08:00
|
|
|
return p->key;
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
2014-04-02 18:34:44 +08:00
|
|
|
idx = Z_NEXT(p->val);
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2014-04-02 18:34:44 +08:00
|
|
|
GC_REFCOUNT(str) = 1;
|
|
|
|
GC_FLAGS(str) |= IS_STR_INTERNED;
|
2014-02-10 14:04:30 +08:00
|
|
|
|
|
|
|
if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) {
|
2015-02-13 07:12:42 +08:00
|
|
|
if (CG(interned_strings).nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
|
2015-03-13 22:13:19 +08:00
|
|
|
void *new_data;
|
|
|
|
void *old_data = HT_GET_DATA_ADDR(&CG(interned_strings));
|
|
|
|
Bucket *old_buckets = CG(interned_strings).arData;
|
|
|
|
|
|
|
|
CG(interned_strings).nTableSize += CG(interned_strings).nTableSize;
|
|
|
|
CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
|
|
|
|
new_data = malloc(HT_SIZE(&CG(interned_strings)));
|
|
|
|
|
|
|
|
if (new_data) {
|
|
|
|
HT_SET_DATA_ADDR(&CG(interned_strings), new_data);
|
|
|
|
memcpy(CG(interned_strings).arData, old_buckets, sizeof(Bucket) * CG(interned_strings).nNumUsed);
|
|
|
|
free(old_data);
|
2010-04-20 19:05:54 +08:00
|
|
|
zend_hash_rehash(&CG(interned_strings));
|
2015-03-13 22:13:19 +08:00
|
|
|
} else {
|
|
|
|
CG(interned_strings).nTableSize = CG(interned_strings).nTableSize >> 1;
|
|
|
|
CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-10 14:04:30 +08:00
|
|
|
idx = CG(interned_strings).nNumUsed++;
|
|
|
|
CG(interned_strings).nNumOfElements++;
|
|
|
|
p = CG(interned_strings).arData + idx;
|
|
|
|
p->h = h;
|
|
|
|
p->key = str;
|
|
|
|
Z_STR(p->val) = str;
|
2014-04-03 19:26:23 +08:00
|
|
|
Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX;
|
2015-03-13 22:13:19 +08:00
|
|
|
nIndex = h | CG(interned_strings).nTableMask;
|
|
|
|
Z_NEXT(p->val) = HT_HASH(&CG(interned_strings), nIndex);
|
|
|
|
HT_HASH(&CG(interned_strings), nIndex) = HT_IDX_TO_HASH(idx);
|
2015-01-03 17:22:58 +08:00
|
|
|
|
2014-02-10 14:04:30 +08:00
|
|
|
return str;
|
2010-04-20 19:05:54 +08:00
|
|
|
#else
|
2014-02-10 14:04:30 +08:00
|
|
|
return str;
|
2010-04-20 19:05:54 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static void zend_interned_strings_snapshot_int(void)
|
2010-04-20 19:05:54 +08:00
|
|
|
{
|
2014-02-10 14:04:30 +08:00
|
|
|
#ifndef ZTS
|
|
|
|
uint idx;
|
|
|
|
Bucket *p;
|
|
|
|
|
|
|
|
idx = CG(interned_strings).nNumUsed;
|
|
|
|
while (idx > 0) {
|
|
|
|
idx--;
|
|
|
|
p = CG(interned_strings).arData + idx;
|
2014-04-02 18:34:44 +08:00
|
|
|
ZEND_ASSERT(GC_FLAGS(p->key) & IS_STR_PERSISTENT);
|
|
|
|
GC_FLAGS(p->key) |= IS_STR_PERMANENT;
|
2014-02-10 14:04:30 +08:00
|
|
|
}
|
|
|
|
#endif
|
2010-04-20 19:05:54 +08:00
|
|
|
}
|
|
|
|
|
2014-12-14 06:06:14 +08:00
|
|
|
static void zend_interned_strings_restore_int(void)
|
2010-04-20 19:05:54 +08:00
|
|
|
{
|
|
|
|
#ifndef ZTS
|
2014-02-10 14:04:30 +08:00
|
|
|
uint nIndex;
|
|
|
|
uint idx;
|
2010-04-20 19:05:54 +08:00
|
|
|
Bucket *p;
|
|
|
|
|
2014-02-10 14:04:30 +08:00
|
|
|
idx = CG(interned_strings).nNumUsed;
|
|
|
|
while (idx > 0) {
|
|
|
|
idx--;
|
|
|
|
p = CG(interned_strings).arData + idx;
|
2014-04-02 18:34:44 +08:00
|
|
|
if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break;
|
2014-02-10 14:04:30 +08:00
|
|
|
CG(interned_strings).nNumUsed--;
|
|
|
|
CG(interned_strings).nNumOfElements--;
|
|
|
|
|
2014-04-02 18:34:44 +08:00
|
|
|
GC_FLAGS(p->key) &= ~IS_STR_INTERNED;
|
|
|
|
GC_REFCOUNT(p->key) = 1;
|
2014-08-26 01:24:55 +08:00
|
|
|
zend_string_free(p->key);
|
2014-02-18 02:30:35 +08:00
|
|
|
|
2015-03-13 22:13:19 +08:00
|
|
|
nIndex = p->h | CG(interned_strings).nTableMask;
|
|
|
|
if (HT_HASH(&CG(interned_strings), nIndex) == HT_IDX_TO_HASH(idx)) {
|
|
|
|
HT_HASH(&CG(interned_strings), nIndex) = Z_NEXT(p->val);
|
2014-02-10 14:04:30 +08:00
|
|
|
} else {
|
2015-03-13 22:13:19 +08:00
|
|
|
uint32_t prev = HT_HASH(&CG(interned_strings), nIndex);
|
|
|
|
while (Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) != idx) {
|
|
|
|
prev = Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val);
|
2014-02-10 14:04:30 +08:00
|
|
|
}
|
2015-03-13 22:13:19 +08:00
|
|
|
Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) = Z_NEXT(p->val);
|
2014-02-10 14:04:30 +08:00
|
|
|
}
|
|
|
|
}
|
2010-04-20 19:05:54 +08:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* tab-width: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* indent-tabs-mode: t
|
|
|
|
* End:
|
|
|
|
*/
|