diff --git a/Zend/zend_alloc.c b/Zend/zend_alloc.c index 1157dc98fa615..16338310e6acd 100644 --- a/Zend/zend_alloc.c +++ b/Zend/zend_alloc.c @@ -1671,7 +1671,10 @@ static zend_never_inline void *zend_mm_realloc_huge(zend_mm_heap *heap, void *pt return zend_mm_realloc_slow(heap, ptr, size, MIN(old_size, copy_size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } -static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, bool use_copy_size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +#define EREALLOC_DEFAULT 0 +#define EREALLOC_COPY 1 +#define EREALLOC_NOSHRINK 2 +static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *ptr, size_t size, int mode, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { size_t page_offset; size_t old_size; @@ -1686,6 +1689,12 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p if (EXPECTED(ptr == NULL)) { return _zend_mm_alloc(heap, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } else { + if (mode == EREALLOC_NOSHRINK) { + old_size = zend_mm_get_huge_block_size(heap, ptr ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + if (EXPECTED(size <= old_size)) { + return ptr; + } + } return zend_mm_realloc_huge(heap, ptr, size, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } } else { @@ -1713,10 +1722,10 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p /* Check if requested size fits into current bin */ if (size <= old_size) { /* Check if truncation is necessary */ - if (old_bin_num > 0 && size < bin_data_size[old_bin_num - 1]) { + if (mode != EREALLOC_NOSHRINK && old_bin_num > 0 && size < bin_data_size[old_bin_num - 1]) { /* truncation */ ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - copy_size = use_copy_size ? MIN(size, copy_size) : size; + copy_size = mode != EREALLOC_DEFAULT ? MIN(size, copy_size) : size; memcpy(ret, ptr, copy_size); zend_mm_free_small(heap, ptr, old_bin_num); } else { @@ -1731,7 +1740,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p size_t orig_peak = heap->peak; #endif ret = zend_mm_alloc_small(heap, ZEND_MM_SMALL_SIZE_TO_BIN(size) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); - copy_size = use_copy_size ? MIN(old_size, copy_size) : old_size; + copy_size = mode != EREALLOC_DEFAULT ? MIN(old_size, copy_size) : old_size; memcpy(ret, ptr, copy_size); zend_mm_free_small(heap, ptr, old_bin_num); #if ZEND_MM_STAT @@ -1759,7 +1768,7 @@ static zend_always_inline void *zend_mm_realloc_heap(zend_mm_heap *heap, void *p old_size = ZEND_MM_LRUN_PAGES(info) * ZEND_MM_PAGE_SIZE; if (size > ZEND_MM_MAX_SMALL_SIZE && size <= ZEND_MM_MAX_LARGE_SIZE) { new_size = ZEND_MM_ALIGNED_SIZE_EX(size, ZEND_MM_PAGE_SIZE); - if (new_size == old_size) { + if (mode == EREALLOC_NOSHRINK ? new_size <= old_size : (new_size == old_size)) { #if ZEND_DEBUG dbg = zend_mm_get_debug_info(heap, ptr); dbg->size = real_size; @@ -2579,12 +2588,17 @@ ZEND_API void ZEND_FASTCALL _zend_mm_free(zend_mm_heap *heap, void *ptr ZEND_FIL void* ZEND_FASTCALL _zend_mm_realloc(zend_mm_heap *heap, void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - return zend_mm_realloc_heap(heap, ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + return zend_mm_realloc_heap(heap, ptr, size, EREALLOC_DEFAULT, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } void* ZEND_FASTCALL _zend_mm_realloc2(zend_mm_heap *heap, void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { - return zend_mm_realloc_heap(heap, ptr, size, 1, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + return zend_mm_realloc_heap(heap, ptr, size, EREALLOC_COPY, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); +} + +void* ZEND_FASTCALL _zend_mm_realloc3(zend_mm_heap *heap, void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ + return zend_mm_realloc_heap(heap, ptr, size, EREALLOC_NOSHRINK, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } ZEND_API size_t ZEND_FASTCALL _zend_mm_block_size(zend_mm_heap *heap, void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -2801,7 +2815,7 @@ ZEND_API void* ZEND_FASTCALL _erealloc(void *ptr, size_t size ZEND_FILE_LINE_DC return AG(mm_heap)->custom_heap._realloc(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } #endif - return zend_mm_realloc_heap(AG(mm_heap), ptr, size, 0, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + return zend_mm_realloc_heap(AG(mm_heap), ptr, size, EREALLOC_DEFAULT, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } ZEND_API void* ZEND_FASTCALL _erealloc2(void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) @@ -2811,7 +2825,17 @@ ZEND_API void* ZEND_FASTCALL _erealloc2(void *ptr, size_t size, size_t copy_size return AG(mm_heap)->custom_heap._realloc(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } #endif - return zend_mm_realloc_heap(AG(mm_heap), ptr, size, 1, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + return zend_mm_realloc_heap(AG(mm_heap), ptr, size, EREALLOC_COPY, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); +} + +ZEND_API void* ZEND_FASTCALL _erealloc3(void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) +{ +#if ZEND_MM_CUSTOM + if (UNEXPECTED(AG(mm_heap)->use_custom_heap)) { + return AG(mm_heap)->custom_heap._realloc(ptr, size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); + } +#endif + return zend_mm_realloc_heap(AG(mm_heap), ptr, size, EREALLOC_NOSHRINK, copy_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } ZEND_API size_t ZEND_FASTCALL _zend_mem_block_size(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) diff --git a/Zend/zend_alloc.h b/Zend/zend_alloc.h index 264e13848d1b7..bbc0e834da65d 100644 --- a/Zend/zend_alloc.h +++ b/Zend/zend_alloc.h @@ -71,6 +71,7 @@ ZEND_API void ZEND_FASTCALL _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ ZEND_API ZEND_ATTRIBUTE_MALLOC void* ZEND_FASTCALL _ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE2(1,2); ZEND_API void* ZEND_FASTCALL _erealloc(void *ptr, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE(2); ZEND_API void* ZEND_FASTCALL _erealloc2(void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE(2); +ZEND_API void* ZEND_FASTCALL _erealloc3(void *ptr, size_t size, size_t copy_size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) ZEND_ATTRIBUTE_ALLOC_SIZE(2); ZEND_API void* ZEND_FASTCALL _safe_erealloc(void *ptr, size_t nmemb, size_t size, size_t offset ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); ZEND_API void* ZEND_FASTCALL _safe_realloc(void *ptr, size_t nmemb, size_t size, size_t offset); ZEND_API ZEND_ATTRIBUTE_MALLOC char* ZEND_FASTCALL _estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC); @@ -158,6 +159,7 @@ ZEND_API void ZEND_FASTCALL _efree_huge(void *, size_t size); #define ecalloc(nmemb, size) _ecalloc((nmemb), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) #define erealloc(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) #define erealloc2(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) +#define erealloc3(ptr, size, copy_size) _erealloc3((ptr), (size), (copy_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) #define safe_erealloc(ptr, nmemb, size, offset) _safe_erealloc((ptr), (nmemb), (size), (offset) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) #define erealloc_recoverable(ptr, size) _erealloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) #define erealloc2_recoverable(ptr, size, copy_size) _erealloc2((ptr), (size), (copy_size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC) @@ -200,6 +202,7 @@ ZEND_API ZEND_ATTRIBUTE_MALLOC char * __zend_strdup(const char *s); #define pecalloc(nmemb, size, persistent) ((persistent)?__zend_calloc((nmemb), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC):ecalloc((nmemb), (size))) #define perealloc(ptr, size, persistent) ((persistent)?__zend_realloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC):erealloc((ptr), (size))) #define perealloc2(ptr, size, copy_size, persistent) ((persistent)?__zend_realloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC):erealloc2((ptr), (size), (copy_size))) +#define perealloc3(ptr, size, copy_size, persistent) ((persistent)?__zend_realloc((ptr), (size) ZEND_FILE_LINE_CC ZEND_FILE_LINE_EMPTY_CC):erealloc3((ptr), (size), (copy_size))) #define safe_perealloc(ptr, nmemb, size, offset, persistent) ((persistent)?_safe_realloc((ptr), (nmemb), (size), (offset)):safe_erealloc((ptr), (nmemb), (size), (offset))) #define perealloc_recoverable(ptr, size, persistent) ((persistent)?realloc((ptr), (size)):erealloc_recoverable((ptr), (size))) #define perealloc2_recoverable(ptr, size, persistent) ((persistent)?realloc((ptr), (size)):erealloc2_recoverable((ptr), (size), (copy_size))) diff --git a/Zend/zend_string.h b/Zend/zend_string.h index fc7705ff78650..eb2d0ad8bae90 100644 --- a/Zend/zend_string.h +++ b/Zend/zend_string.h @@ -261,7 +261,7 @@ static zend_always_inline zend_string *zend_string_extend(zend_string *s, size_t ZEND_ASSERT(len >= ZSTR_LEN(s)); if (!ZSTR_IS_INTERNED(s)) { if (EXPECTED(GC_REFCOUNT(s) == 1)) { - ret = (zend_string *)perealloc(s, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), persistent); + ret = (zend_string *)perealloc3(s, ZEND_MM_ALIGNED_SIZE(_ZSTR_STRUCT_SIZE(len)), _ZSTR_STRUCT_SIZE(ZSTR_LEN(s)), persistent); ZSTR_LEN(ret) = len; zend_string_forget_hash_val(ret); return ret; diff --git a/ext/standard/basic_functions.stub.php b/ext/standard/basic_functions.stub.php index 7913ca0e00194..6ad8fdac99a7c 100644 --- a/ext/standard/basic_functions.stub.php +++ b/ext/standard/basic_functions.stub.php @@ -2448,6 +2448,11 @@ function substr_replace(array|string $string, array|string $replace, array|int $ */ function quotemeta(string $string): string {} +/** + * @compile-time-eval + */ +function str_extend(string $string, int $size): string {} + /** @compile-time-eval */ function ord(string $character): int {} diff --git a/ext/standard/basic_functions_arginfo.h b/ext/standard/basic_functions_arginfo.h index 0a21d7d76426c..4bdb871254cf2 100644 --- a/ext/standard/basic_functions_arginfo.h +++ b/ext/standard/basic_functions_arginfo.h @@ -1,5 +1,5 @@ /* This is a generated file, edit the .stub.php file instead. - * Stub hash: f6bf6cdd07080c01d3a0cb08d71409d05b1084f9 */ + * Stub hash: e5708214c093e9a4ee017b62a55f6b040448b445 */ ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_set_time_limit, 0, 1, _IS_BOOL, 0) ZEND_ARG_TYPE_INFO(0, seconds, IS_LONG, 0) @@ -943,6 +943,11 @@ ZEND_END_ARG_INFO() #define arginfo_quotemeta arginfo_base64_encode +ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_str_extend, 0, 2, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, string, IS_STRING, 0) + ZEND_ARG_TYPE_INFO(0, size, IS_LONG, 0) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_WITH_RETURN_TYPE_INFO_EX(arginfo_ord, 0, 1, IS_LONG, 0) ZEND_ARG_TYPE_INFO(0, character, IS_STRING, 0) ZEND_END_ARG_INFO() @@ -2551,6 +2556,7 @@ ZEND_FUNCTION(chunk_split); ZEND_FUNCTION(substr); ZEND_FUNCTION(substr_replace); ZEND_FUNCTION(quotemeta); +ZEND_FUNCTION(str_extend); ZEND_FUNCTION(ord); ZEND_FUNCTION(chr); ZEND_FUNCTION(ucfirst); @@ -3152,6 +3158,7 @@ static const zend_function_entry ext_functions[] = { ZEND_RAW_FENTRY("substr", zif_substr, arginfo_substr, ZEND_ACC_COMPILE_TIME_EVAL, frameless_function_infos_substr, NULL) ZEND_RAW_FENTRY("substr_replace", zif_substr_replace, arginfo_substr_replace, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("quotemeta", zif_quotemeta, arginfo_quotemeta, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) + ZEND_RAW_FENTRY("str_extend", zif_str_extend, arginfo_str_extend, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("ord", zif_ord, arginfo_ord, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("chr", zif_chr, arginfo_chr, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) ZEND_RAW_FENTRY("ucfirst", zif_ucfirst, arginfo_ucfirst, ZEND_ACC_COMPILE_TIME_EVAL, NULL, NULL) diff --git a/ext/standard/string.c b/ext/standard/string.c index 029c7a29bb478..ae762217b28c1 100644 --- a/ext/standard/string.c +++ b/ext/standard/string.c @@ -2643,6 +2643,28 @@ PHP_FUNCTION(quotemeta) } /* }}} */ +PHP_FUNCTION(str_extend) +{ + zend_string *str; + zend_long size; + + ZEND_PARSE_PARAMETERS_START(2, 2) + Z_PARAM_STR(str) + Z_PARAM_LONG(size) + ZEND_PARSE_PARAMETERS_END(); + + size_t len = ZSTR_LEN(str); + if (len > size) { + zend_argument_value_error(2, "must not be smaller than the input string"); + RETURN_THROWS(); + } + + ZVAL_UNDEF(ZEND_CALL_ARG(execute_data, 0)); // avoid copies, so that we may benefit from the RC=1 optimization + RETVAL_STR(zend_string_extend(str, size, 0)); + Z_STRLEN_P(return_value) = len; +} +/* }}} */ + /* {{{ Returns ASCII value of character Warning: This function is special-cased by zend_compile.c and so is bypassed for constant string argument */ PHP_FUNCTION(ord) diff --git a/ext/standard/tests/strings/str_extend.phpt b/ext/standard/tests/strings/str_extend.phpt new file mode 100644 index 0000000000000..07988ff87e243 --- /dev/null +++ b/ext/standard/tests/strings/str_extend.phpt @@ -0,0 +1,18 @@ +--TEST-- +Append to string allocated with str_extend() +--FILE-- + +--EXPECT-- +array(1) { + [97]=> + int(2097153) +} \ No newline at end of file