Skip to content

Commit 83a7701

Browse files
twosenikic
authored andcommitted
Add helper APIs for maybe-interned string creation
Add ZVAL_CHAR/RETVAL_CHAR/RETURN_CHAR as a shortcut for using ZVAL_INTERNED_STRING and ZSTR_CHAR. Add zend_string_init_fast() as a helper for the empty string / one char interned string / zend_string_init() pattern. Also add corresponding ZVAL_STRINGL_FAST etc macros. Closes GH-5684.
1 parent 543684e commit 83a7701

16 files changed

+107
-126
lines changed

Zend/zend_API.h

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,20 @@ END_EXTERN_C()
636636
ZVAL_PSTRINGL(z, "", 0); \
637637
} while (0)
638638

639+
#define ZVAL_CHAR(z, c) do { \
640+
char _c = (c); \
641+
ZVAL_INTERNED_STR(z, ZSTR_CHAR((zend_uchar) _c)); \
642+
} while (0)
643+
644+
#define ZVAL_STRINGL_FAST(z, s, l) do { \
645+
ZVAL_STR(z, zend_string_init_fast(s, l)); \
646+
} while (0)
647+
648+
#define ZVAL_STRING_FAST(z, s) do { \
649+
const char *_s = (s); \
650+
ZVAL_STRINGL_FAST(z, _s, strlen(_s)); \
651+
} while (0)
652+
639653
#define ZVAL_ZVAL(z, zv, copy, dtor) do { \
640654
zval *__z = (z); \
641655
zval *__zv = (zv); \
@@ -654,46 +668,52 @@ END_EXTERN_C()
654668
} while (0)
655669

656670
#define RETVAL_BOOL(b) ZVAL_BOOL(return_value, b)
657-
#define RETVAL_NULL() ZVAL_NULL(return_value)
658-
#define RETVAL_LONG(l) ZVAL_LONG(return_value, l)
659-
#define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d)
660-
#define RETVAL_STR(s) ZVAL_STR(return_value, s)
661-
#define RETVAL_INTERNED_STR(s) ZVAL_INTERNED_STR(return_value, s)
662-
#define RETVAL_NEW_STR(s) ZVAL_NEW_STR(return_value, s)
663-
#define RETVAL_STR_COPY(s) ZVAL_STR_COPY(return_value, s)
664-
#define RETVAL_STRING(s) ZVAL_STRING(return_value, s)
665-
#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l)
666-
#define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
667-
#define RETVAL_RES(r) ZVAL_RES(return_value, r)
668-
#define RETVAL_ARR(r) ZVAL_ARR(return_value, r)
671+
#define RETVAL_NULL() ZVAL_NULL(return_value)
672+
#define RETVAL_LONG(l) ZVAL_LONG(return_value, l)
673+
#define RETVAL_DOUBLE(d) ZVAL_DOUBLE(return_value, d)
674+
#define RETVAL_STR(s) ZVAL_STR(return_value, s)
675+
#define RETVAL_INTERNED_STR(s) ZVAL_INTERNED_STR(return_value, s)
676+
#define RETVAL_NEW_STR(s) ZVAL_NEW_STR(return_value, s)
677+
#define RETVAL_STR_COPY(s) ZVAL_STR_COPY(return_value, s)
678+
#define RETVAL_STRING(s) ZVAL_STRING(return_value, s)
679+
#define RETVAL_STRINGL(s, l) ZVAL_STRINGL(return_value, s, l)
680+
#define RETVAL_STRING_FAST(s) ZVAL_STRING_FAST(return_value, s)
681+
#define RETVAL_STRINGL_FAST(s, l) ZVAL_STRINGL_FAST(return_value, s, l)
682+
#define RETVAL_EMPTY_STRING() ZVAL_EMPTY_STRING(return_value)
683+
#define RETVAL_CHAR(c) ZVAL_CHAR(return_value, c)
684+
#define RETVAL_RES(r) ZVAL_RES(return_value, r)
685+
#define RETVAL_ARR(r) ZVAL_ARR(return_value, r)
669686
#define RETVAL_EMPTY_ARRAY() ZVAL_EMPTY_ARRAY(return_value)
670-
#define RETVAL_OBJ(r) ZVAL_OBJ(return_value, r)
687+
#define RETVAL_OBJ(r) ZVAL_OBJ(return_value, r)
671688
#define RETVAL_COPY(zv) ZVAL_COPY(return_value, zv)
672689
#define RETVAL_COPY_VALUE(zv) ZVAL_COPY_VALUE(return_value, zv)
673690
#define RETVAL_ZVAL(zv, copy, dtor) ZVAL_ZVAL(return_value, zv, copy, dtor)
674-
#define RETVAL_FALSE ZVAL_FALSE(return_value)
675-
#define RETVAL_TRUE ZVAL_TRUE(return_value)
691+
#define RETVAL_FALSE ZVAL_FALSE(return_value)
692+
#define RETVAL_TRUE ZVAL_TRUE(return_value)
676693

677-
#define RETURN_BOOL(b) do { RETVAL_BOOL(b); return; } while (0)
678-
#define RETURN_NULL() do { RETVAL_NULL(); return;} while (0)
679-
#define RETURN_LONG(l) do { RETVAL_LONG(l); return; } while (0)
680-
#define RETURN_DOUBLE(d) do { RETVAL_DOUBLE(d); return; } while (0)
694+
#define RETURN_BOOL(b) do { RETVAL_BOOL(b); return; } while (0)
695+
#define RETURN_NULL() do { RETVAL_NULL(); return;} while (0)
696+
#define RETURN_LONG(l) do { RETVAL_LONG(l); return; } while (0)
697+
#define RETURN_DOUBLE(d) do { RETVAL_DOUBLE(d); return; } while (0)
681698
#define RETURN_STR(s) do { RETVAL_STR(s); return; } while (0)
682699
#define RETURN_INTERNED_STR(s) do { RETVAL_INTERNED_STR(s); return; } while (0)
683700
#define RETURN_NEW_STR(s) do { RETVAL_NEW_STR(s); return; } while (0)
684701
#define RETURN_STR_COPY(s) do { RETVAL_STR_COPY(s); return; } while (0)
685702
#define RETURN_STRING(s) do { RETVAL_STRING(s); return; } while (0)
686703
#define RETURN_STRINGL(s, l) do { RETVAL_STRINGL(s, l); return; } while (0)
704+
#define RETURN_STRING_FAST(s) do { RETVAL_STRING_FAST(s); return; } while (0)
705+
#define RETURN_STRINGL_FAST(s, l) do { RETVAL_STRINGL_FAST(s, l); return; } while (0)
687706
#define RETURN_EMPTY_STRING() do { RETVAL_EMPTY_STRING(); return; } while (0)
688-
#define RETURN_RES(r) do { RETVAL_RES(r); return; } while (0)
689-
#define RETURN_ARR(r) do { RETVAL_ARR(r); return; } while (0)
707+
#define RETURN_CHAR(c) do { RETVAL_CHAR(c); return; } while (0)
708+
#define RETURN_RES(r) do { RETVAL_RES(r); return; } while (0)
709+
#define RETURN_ARR(r) do { RETVAL_ARR(r); return; } while (0)
690710
#define RETURN_EMPTY_ARRAY() do { RETVAL_EMPTY_ARRAY(); return; } while (0)
691-
#define RETURN_OBJ(r) do { RETVAL_OBJ(r); return; } while (0)
711+
#define RETURN_OBJ(r) do { RETVAL_OBJ(r); return; } while (0)
692712
#define RETURN_COPY(zv) do { RETVAL_COPY(zv); return; } while (0)
693713
#define RETURN_COPY_VALUE(zv) do { RETVAL_COPY_VALUE(zv); return; } while (0)
694714
#define RETURN_ZVAL(zv, copy, dtor) do { RETVAL_ZVAL(zv, copy, dtor); return; } while (0)
695-
#define RETURN_FALSE do { RETVAL_FALSE; return; } while (0)
696-
#define RETURN_TRUE do { RETVAL_TRUE; return; } while (0)
715+
#define RETURN_FALSE do { RETVAL_FALSE; return; } while (0)
716+
#define RETURN_TRUE do { RETVAL_TRUE; return; } while (0)
697717
#define RETURN_THROWS() do { ZEND_ASSERT(EG(exception)); (void) return_value; return; } while (0)
698718

699719
#define HASH_OF(p) (Z_TYPE_P(p)==IS_ARRAY ? Z_ARRVAL_P(p) : ((Z_TYPE_P(p)==IS_OBJECT ? Z_OBJ_HT_P(p)->get_properties(Z_OBJ_P(p)) : NULL)))

Zend/zend_compile.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,7 +3525,7 @@ int zend_compile_func_chr(znode *result, zend_ast_list *args) /* {{{ */
35253525
zend_long c = Z_LVAL_P(zend_ast_get_zval(args->child[0])) & 0xff;
35263526

35273527
result->op_type = IS_CONST;
3528-
ZVAL_INTERNED_STR(&result->u.constant, ZSTR_CHAR(c));
3528+
ZVAL_CHAR(&result->u.constant, c);
35293529
return SUCCESS;
35303530
} else {
35313531
return FAILURE;
@@ -9383,7 +9383,7 @@ void zend_eval_const_expr(zend_ast **ast_ptr) /* {{{ */
93839383
return;
93849384
}
93859385
c = (zend_uchar) Z_STRVAL_P(container)[offset];
9386-
ZVAL_INTERNED_STR(&result, ZSTR_CHAR(c));
9386+
ZVAL_CHAR(&result, c);
93879387
} else if (Z_TYPE_P(container) <= IS_FALSE) {
93889388
ZVAL_NULL(&result);
93899389
} else {

Zend/zend_execute.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1584,7 +1584,7 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
15841584

15851585
if (UNEXPECTED(RETURN_VALUE_USED(opline))) {
15861586
/* Return the new character */
1587-
ZVAL_INTERNED_STR(EX_VAR(opline->result.var), ZSTR_CHAR(c));
1587+
ZVAL_CHAR(EX_VAR(opline->result.var), c);
15881588
}
15891589
}
15901590

@@ -2332,7 +2332,7 @@ static zend_always_inline void zend_fetch_dimension_address_read(zval *result, z
23322332
? (zend_long)Z_STRLEN_P(container) + offset : offset;
23332333
c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
23342334

2335-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
2335+
ZVAL_CHAR(result, c);
23362336
}
23372337
} else if (EXPECTED(Z_TYPE_P(container) == IS_OBJECT)) {
23382338
if (ZEND_CONST_COND(dim_type == IS_CV, 1) && UNEXPECTED(Z_TYPE_P(dim) == IS_UNDEF)) {

Zend/zend_operators.c

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,7 @@ ZEND_API void ZEND_FASTCALL _convert_to_string(zval *op) /* {{{ */
616616
break;
617617
}
618618
case IS_TRUE:
619-
ZVAL_INTERNED_STR(op, ZSTR_CHAR('1'));
619+
ZVAL_CHAR(op, '1');
620620
break;
621621
case IS_STRING:
622622
break;
@@ -1449,7 +1449,7 @@ ZEND_API int ZEND_FASTCALL bitwise_not_function(zval *result, zval *op1) /* {{{
14491449

14501450
if (Z_STRLEN_P(op1) == 1) {
14511451
zend_uchar not = (zend_uchar) ~*Z_STRVAL_P(op1);
1452-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(not));
1452+
ZVAL_CHAR(result, not);
14531453
} else {
14541454
ZVAL_NEW_STR(result, zend_string_alloc(Z_STRLEN_P(op1), 0));
14551455
for (i = 0; i < Z_STRLEN_P(op1); i++) {
@@ -1497,7 +1497,7 @@ ZEND_API int ZEND_FASTCALL bitwise_or_function(zval *result, zval *op1, zval *op
14971497
if (result==op1) {
14981498
zval_ptr_dtor_str(result);
14991499
}
1500-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(or));
1500+
ZVAL_CHAR(result, or);
15011501
return SUCCESS;
15021502
}
15031503
longer = op1;
@@ -1579,7 +1579,7 @@ ZEND_API int ZEND_FASTCALL bitwise_and_function(zval *result, zval *op1, zval *o
15791579
if (result==op1) {
15801580
zval_ptr_dtor_str(result);
15811581
}
1582-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(and));
1582+
ZVAL_CHAR(result, and);
15831583
return SUCCESS;
15841584
}
15851585
longer = op1;
@@ -1661,7 +1661,7 @@ ZEND_API int ZEND_FASTCALL bitwise_xor_function(zval *result, zval *op1, zval *o
16611661
if (result==op1) {
16621662
zval_ptr_dtor_str(result);
16631663
}
1664-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(xor));
1664+
ZVAL_CHAR(result, xor);
16651665
return SUCCESS;
16661666
}
16671667
longer = op1;
@@ -2244,7 +2244,7 @@ static void ZEND_FASTCALL increment_string(zval *str) /* {{{ */
22442244

22452245
if (Z_STRLEN_P(str) == 0) {
22462246
zval_ptr_dtor_str(str);
2247-
ZVAL_INTERNED_STR(str, ZSTR_CHAR('1'));
2247+
ZVAL_CHAR(str, '1');
22482248
return;
22492249
}
22502250

Zend/zend_string.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ static zend_always_inline zend_string *zend_string_init(const char *str, size_t
167167
return ret;
168168
}
169169

170+
static zend_always_inline zend_string *zend_string_init_fast(const char *str, size_t len)
171+
{
172+
if (len > 1) {
173+
return zend_string_init(str, len, 0);
174+
} else if (len == 0) {
175+
return zend_empty_string;
176+
} else /* if (len == 1) */ {
177+
return ZSTR_CHAR((zend_uchar) *str);
178+
}
179+
}
180+
170181
static zend_always_inline zend_string *zend_string_copy(zend_string *s)
171182
{
172183
if (!ZSTR_IS_INTERNED(s)) {

ext/ffi/ffi.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ static zend_always_inline void zend_ffi_cdata_to_zval(zend_ffi_cdata *cdata, voi
504504
ZVAL_BOOL(rv, *(uint8_t*)ptr);
505505
return;
506506
case ZEND_FFI_TYPE_CHAR:
507-
ZVAL_INTERNED_STR(rv, ZSTR_CHAR(*(unsigned char*)ptr));
507+
ZVAL_CHAR(rv, *(char*)ptr);
508508
return;
509509
case ZEND_FFI_TYPE_ENUM:
510510
kind = type->enumeration.kind;
@@ -1077,7 +1077,7 @@ static int zend_ffi_cdata_cast_object(zend_object *readobj, zval *writeobj, int
10771077
ZVAL_BOOL(writeobj, *(uint8_t*)ptr);
10781078
break;
10791079
case ZEND_FFI_TYPE_CHAR:
1080-
ZVAL_INTERNED_STR(writeobj, ZSTR_CHAR(*(unsigned char*)ptr));
1080+
ZVAL_CHAR(writeobj, *(char*)ptr);
10811081
return SUCCESS;
10821082
case ZEND_FFI_TYPE_ENUM:
10831083
kind = ctype->enumeration.kind;

ext/mysqlnd/mysqlnd_wireprotocol.c

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1666,12 +1666,8 @@ php_mysqlnd_rowp_read_text_protocol_aux(MYSQLND_ROW_BUFFER * row_buffer, zval *
16661666
} else if (Z_TYPE_P(current_field) == IS_STRING) {
16671667
/* nothing to do here, as we want a string and ps_fetch_from_1_to_8_bytes() has given us one */
16681668
}
1669-
} else if (len == 0) {
1670-
ZVAL_EMPTY_STRING(current_field);
1671-
} else if (len == 1) {
1672-
ZVAL_INTERNED_STR(current_field, ZSTR_CHAR((zend_uchar)*(char *)p));
16731669
} else {
1674-
ZVAL_STRINGL(current_field, (char *)p, len);
1670+
ZVAL_STRINGL_FAST(current_field, (char *)p, len);
16751671
}
16761672
p += len;
16771673
}

ext/opcache/Optimizer/sccp.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,7 @@ static inline int ct_eval_func_call(
801801
}
802802

803803
c = Z_LVAL_P(args[0]) & 0xff;
804-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
804+
ZVAL_CHAR(result, c);
805805
return SUCCESS;
806806
} else if (zend_string_equals_literal(name, "count")) {
807807
if (Z_TYPE_P(args[0]) != IS_ARRAY) {

ext/opcache/jit/zend_jit_helpers.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -638,7 +638,7 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_r_helper(zval *container, zval
638638
real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
639639
? (zend_long)Z_STRLEN_P(container) + offset : offset;
640640
c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
641-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
641+
ZVAL_CHAR(result, c);
642642
}
643643
}
644644

@@ -685,7 +685,7 @@ static void ZEND_FASTCALL zend_jit_fetch_dim_str_is_helper(zval *container, zval
685685
real_offset = (UNEXPECTED(offset < 0)) /* Handle negative offset */
686686
? (zend_long)Z_STRLEN_P(container) + offset : offset;
687687
c = (zend_uchar)Z_STRVAL_P(container)[real_offset];
688-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
688+
ZVAL_CHAR(result, c);
689689
}
690690
}
691691

@@ -952,7 +952,7 @@ static zend_never_inline void zend_assign_to_string_offset(zval *str, zval *dim,
952952

953953
if (result) {
954954
/* Return the new character */
955-
ZVAL_INTERNED_STR(result, ZSTR_CHAR(c));
955+
ZVAL_CHAR(result, c);
956956
}
957957
}
958958

ext/pcre/php_pcre.c

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -966,13 +966,7 @@ static void init_unmatched_empty_pair() {
966966

967967
static zend_always_inline void populate_match_value_str(
968968
zval *val, const char *subject, PCRE2_SIZE start_offset, PCRE2_SIZE end_offset) {
969-
if (start_offset == end_offset) {
970-
ZVAL_EMPTY_STRING(val);
971-
} else if (start_offset + 1 == end_offset) {
972-
ZVAL_INTERNED_STR(val, ZSTR_CHAR((unsigned char) subject[start_offset]));
973-
} else {
974-
ZVAL_STRINGL(val, subject + start_offset, end_offset - start_offset);
975-
}
969+
ZVAL_STRINGL_FAST(val, subject + start_offset, end_offset - start_offset);
976970
}
977971

978972
static inline void populate_match_value(

ext/standard/array.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2739,7 +2739,7 @@ PHP_FUNCTION(range)
27392739
} ZEND_HASH_FILL_END();
27402740
} else {
27412741
array_init(return_value);
2742-
ZVAL_INTERNED_STR(&tmp, ZSTR_CHAR(low));
2742+
ZVAL_CHAR(&tmp, low);
27432743
zend_hash_next_index_insert_new(Z_ARRVAL_P(return_value), &tmp);
27442744
}
27452745
} else if (Z_TYPE_P(zlow) == IS_DOUBLE || Z_TYPE_P(zhigh) == IS_DOUBLE || is_step_double) {

ext/standard/basic_functions.c

Lines changed: 19 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2041,6 +2041,21 @@ PHP_FUNCTION(highlight_string)
20412041
}
20422042
/* }}} */
20432043

2044+
#define INI_RETVAL_STR(val) do { \
2045+
/* copy to return value here, because alter might free it! */ \
2046+
if (ZSTR_IS_INTERNED(val)) { \
2047+
RETVAL_INTERNED_STR(val); \
2048+
} else if (ZSTR_LEN(val) == 0) { \
2049+
RETVAL_EMPTY_STRING(); \
2050+
} else if (ZSTR_LEN(val) == 1) { \
2051+
RETVAL_CHAR(ZSTR_VAL(val)[0]); \
2052+
} else if (!(GC_FLAGS(val) & GC_PERSISTENT)) { \
2053+
ZVAL_NEW_STR(return_value, zend_string_copy(val)); \
2054+
} else { \
2055+
ZVAL_NEW_STR(return_value, zend_string_init(ZSTR_VAL(val), ZSTR_LEN(val), 0)); \
2056+
} \
2057+
} while (0)
2058+
20442059
/* {{{ proto string|false ini_get(string varname)
20452060
Get a configuration option */
20462061
PHP_FUNCTION(ini_get)
@@ -2057,17 +2072,7 @@ PHP_FUNCTION(ini_get)
20572072
RETURN_FALSE;
20582073
}
20592074

2060-
if (ZSTR_IS_INTERNED(val)) {
2061-
RETVAL_INTERNED_STR(val);
2062-
} else if (ZSTR_LEN(val) == 0) {
2063-
RETVAL_EMPTY_STRING();
2064-
} else if (ZSTR_LEN(val) == 1) {
2065-
RETVAL_INTERNED_STR(ZSTR_CHAR((zend_uchar)ZSTR_VAL(val)[0]));
2066-
} else if (!(GC_FLAGS(val) & GC_PERSISTENT)) {
2067-
ZVAL_NEW_STR(return_value, zend_string_copy(val));
2068-
} else {
2069-
ZVAL_NEW_STR(return_value, zend_string_init(ZSTR_VAL(val), ZSTR_LEN(val), 0));
2070-
}
2075+
INI_RETVAL_STR(val);
20712076
}
20722077
/* }}} */
20732078

@@ -2168,19 +2173,8 @@ PHP_FUNCTION(ini_set)
21682173

21692174
val = zend_ini_get_value(varname);
21702175

2171-
/* copy to return here, because alter might free it! */
21722176
if (val) {
2173-
if (ZSTR_IS_INTERNED(val)) {
2174-
RETVAL_INTERNED_STR(val);
2175-
} else if (ZSTR_LEN(val) == 0) {
2176-
RETVAL_EMPTY_STRING();
2177-
} else if (ZSTR_LEN(val) == 1) {
2178-
RETVAL_INTERNED_STR(ZSTR_CHAR((zend_uchar)ZSTR_VAL(val)[0]));
2179-
} else if (!(GC_FLAGS(val) & GC_PERSISTENT)) {
2180-
ZVAL_NEW_STR(return_value, zend_string_copy(val));
2181-
} else {
2182-
ZVAL_NEW_STR(return_value, zend_string_init(ZSTR_VAL(val), ZSTR_LEN(val), 0));
2183-
}
2177+
INI_RETVAL_STR(val);
21842178
} else {
21852179
RETVAL_FALSE;
21862180
}
@@ -2209,6 +2203,8 @@ PHP_FUNCTION(ini_set)
22092203
}
22102204
/* }}} */
22112205

2206+
#undef INI_RETVAL_STR
2207+
22122208
/* {{{ proto void ini_restore(string varname)
22132209
Restore the value of a configuration option specified by varname */
22142210
PHP_FUNCTION(ini_restore)

0 commit comments

Comments
 (0)