php-src/Zend/zend_string.c
Dmitry Stogov 4042f543c4 HANDLE_BLOCK/UNBLOCK_INTERRUPTIONS() protection on inter-process data is completely useless now.
Historicaly, these macros were introduced as a protection from execution timeout handling, but we don't need them anymore after "safe execution timeout" implementation.
These macros are still useful to protect from termination during inner process data modification, because of OS signals (e.g. SIGTERM during OPcache SHM update).
2016-06-20 15:50:41 +03:00

253 lines
7.6 KiB
C

/*
+----------------------------------------------------------------------+
| Zend Engine |
+----------------------------------------------------------------------+
| Copyright (c) 1998-2016 Zend Technologies Ltd. (http://www.zend.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 2.00 of the Zend license, |
| that is bundled with this package in the file LICENSE, and is |
| 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"
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);
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);
ZEND_API zend_ulong zend_hash_func(const char *str, size_t len)
{
return zend_inline_hash_func(str, len);
}
#ifndef ZTS
static void _str_dtor(zval *zv)
{
zend_string *str = Z_STR_P(zv);
pefree(str, GC_FLAGS(str) & IS_STR_PERSISTENT);
}
#endif
/* Readonly, so assigned also per thread. */
static const zend_string **known_interned_strings = NULL;
static uint32_t known_interned_strings_count = 0;
ZEND_API uint32_t zend_intern_known_strings(const char **strings, uint32_t count)
{
uint32_t i, old_count = known_interned_strings_count;
known_interned_strings = perealloc(known_interned_strings, sizeof(char*) * (old_count + count), 1);
for (i = 0; i < count; i++) {
#ifndef ZTS
zend_string *str = zend_string_init(strings[i], strlen(strings[i]), 1);
known_interned_strings[known_interned_strings_count + i] =
zend_new_interned_string_int(str);
#else
known_interned_strings[known_interned_strings_count + i] =
zend_zts_interned_string_init(strings[i], strlen(strings[i]));
#endif
}
known_interned_strings_count = old_count + count;
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
};
void zend_known_interned_strings_init(zend_string ***strings, uint32_t *count)
{
*strings = (zend_string **)known_interned_strings;
*count = known_interned_strings_count;
}
void zend_interned_strings_init(void)
{
#ifndef ZTS
zend_string *str;
zend_hash_init(&CG(interned_strings), 1024, NULL, _str_dtor, 1);
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));
CG(interned_strings).u.flags |= HASH_FLAG_INITIALIZED;
/* interned empty string */
str = zend_string_alloc(sizeof("")-1, 1);
ZSTR_VAL(str)[0] = '\000';
CG(empty_string) = zend_new_interned_string_int(str);
#endif
/* 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)));
/* known strings */
zend_intern_known_strings(known_strings, (sizeof(known_strings) / sizeof(known_strings[0])) - 1);
zend_known_interned_strings_init(&CG(known_strings), &CG(known_strings_count));
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;
}
void zend_interned_strings_dtor(void)
{
#ifndef ZTS
zend_hash_destroy(&CG(interned_strings));
#else
uint32_t i;
for (i = 0; i < CG(known_strings_count); i++) {
zend_zts_interned_string_free(&CG(known_strings)[i]);
}
#endif
free(CG(known_strings));
CG(known_strings) = NULL;
CG(known_strings_count) = 0;
known_interned_strings = NULL;
known_interned_strings_count = 0;
}
static zend_string *zend_new_interned_string_int(zend_string *str)
{
#ifndef ZTS
zend_ulong h;
uint nIndex;
uint idx;
Bucket *p;
if (ZSTR_IS_INTERNED(str)) {
return str;
}
h = zend_string_hash_val(str);
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);
if ((p->h == h) && (ZSTR_LEN(p->key) == ZSTR_LEN(str))) {
if (!memcmp(ZSTR_VAL(p->key), ZSTR_VAL(str), ZSTR_LEN(str))) {
zend_string_release(str);
return p->key;
}
}
idx = Z_NEXT(p->val);
}
GC_REFCOUNT(str) = 1;
GC_FLAGS(str) |= IS_STR_INTERNED;
if (CG(interned_strings).nNumUsed >= CG(interned_strings).nTableSize) {
if (CG(interned_strings).nTableSize < HT_MAX_SIZE) { /* Let's double the table size */
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);
zend_hash_rehash(&CG(interned_strings));
} else {
CG(interned_strings).nTableSize = CG(interned_strings).nTableSize >> 1;
CG(interned_strings).nTableMask = -CG(interned_strings).nTableSize;
}
}
}
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;
Z_TYPE_INFO(p->val) = IS_INTERNED_STRING_EX;
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);
return str;
#else
return str;
#endif
}
static void zend_interned_strings_snapshot_int(void)
{
#ifndef ZTS
uint idx;
Bucket *p;
idx = CG(interned_strings).nNumUsed;
while (idx > 0) {
idx--;
p = CG(interned_strings).arData + idx;
ZEND_ASSERT(GC_FLAGS(p->key) & IS_STR_PERSISTENT);
GC_FLAGS(p->key) |= IS_STR_PERMANENT;
}
#endif
}
static void zend_interned_strings_restore_int(void)
{
#ifndef ZTS
uint nIndex;
uint idx;
Bucket *p;
idx = CG(interned_strings).nNumUsed;
while (idx > 0) {
idx--;
p = CG(interned_strings).arData + idx;
if (GC_FLAGS(p->key) & IS_STR_PERMANENT) break;
CG(interned_strings).nNumUsed--;
CG(interned_strings).nNumOfElements--;
GC_FLAGS(p->key) &= ~IS_STR_INTERNED;
GC_REFCOUNT(p->key) = 1;
zend_string_free(p->key);
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);
} else {
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);
}
Z_NEXT(HT_HASH_TO_BUCKET(&CG(interned_strings), prev)->val) = Z_NEXT(p->val);
}
}
#endif
}
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* indent-tabs-mode: t
* End:
*/