Skip to content

Commit 20db46f

Browse files
committed
streams/userspace: Make function calls faster
1 parent 76b53af commit 20db46f

File tree

1 file changed

+61
-63
lines changed

1 file changed

+61
-63
lines changed

main/streams/userspace.c

Lines changed: 61 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ PHP_MINIT_FUNCTION(user_streams)
8989
struct _php_userstream_data {
9090
struct php_user_stream_wrapper * wrapper;
9191
zend_object *object;
92+
/* Caches for most frequently used methods */
93+
zend_function *fn_cache_read;
94+
zend_function *fn_cache_eof;
95+
zend_function *fn_cache_write;
9296
};
9397
typedef struct _php_userstream_data php_userstream_data_t;
9498

@@ -249,6 +253,38 @@ typedef struct _php_userstream_data php_userstream_data_t;
249253
250254
}}} **/
251255

256+
static zend_function *php_userstream_get_fn(php_userstream_data_t *us, zend_function **cache, const char *name, size_t len)
257+
{
258+
if (cache && *cache) {
259+
return *cache;
260+
}
261+
262+
zend_function *fn = zend_hash_str_find_ptr(&us->object->ce->function_table, name, len);
263+
264+
if (cache) {
265+
*cache = fn;
266+
}
267+
268+
return fn;
269+
}
270+
271+
static zend_result php_userstream_call(php_userstream_data_t *us, zend_function **cache, const char *name, size_t len, zval *retval, uint32_t param_count, zval *params)
272+
{
273+
zend_function *fn = php_userstream_get_fn(us, cache, name, len);
274+
if (UNEXPECTED(!fn || !(fn->common.fn_flags & ZEND_ACC_PUBLIC))) { // TODO: this path should be tested with magic methods via a .phpt file
275+
/* Slow fallback */
276+
zend_string *tmp;
277+
ALLOCA_FLAG(use_heap);
278+
ZSTR_ALLOCA_INIT(tmp, name, len, use_heap);
279+
zend_result result = zend_call_method_if_exists(us->object, tmp, retval, param_count, params);
280+
ZSTR_ALLOCA_FREE(tmp, use_heap);
281+
return result;
282+
} else {
283+
zend_call_known_instance_method(fn, us->object, retval, param_count, params);
284+
return SUCCESS;
285+
}
286+
}
287+
252288
static zend_object *user_stream_create_object(struct php_user_stream_wrapper *uwrap, php_stream_context *context)
253289
{
254290
if (uwrap->ce->ce_flags & (ZEND_ACC_INTERFACE|ZEND_ACC_TRAIT|ZEND_ACC_IMPLICIT_ABSTRACT_CLASS|ZEND_ACC_EXPLICIT_ABSTRACT_CLASS)) {
@@ -309,9 +345,9 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
309345
PG(in_user_include) = 1;
310346
}
311347

312-
us = emalloc(sizeof(*us));
348+
us = ecalloc(1, sizeof(*us));
313349
us->wrapper = uwrap;
314-
/* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
350+
/* method call may unregister the stream wrapper. Hold on to it. */
315351
GC_ADDREF(us->wrapper->resource);
316352

317353
us->object = user_stream_create_object(uwrap, context);
@@ -325,9 +361,7 @@ static php_stream *user_wrapper_opener(php_stream_wrapper *wrapper, const char *
325361
ZVAL_LONG(&args[2], options);
326362
ZVAL_NEW_REF(&args[3], &EG(uninitialized_zval));
327363

328-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_OPEN, false);
329-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &zretval, 4, args);
330-
zend_string_release_ex(func_name, false);
364+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_OPEN), &zretval, 4, args);
331365

332366
/* Keep arg3 alive if it has assigned the reference */
333367
zval_ptr_dtor(&args[1]);
@@ -401,9 +435,9 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
401435
}
402436
FG(user_stream_current_filename) = filename;
403437

404-
us = emalloc(sizeof(*us));
438+
us = ecalloc(1, sizeof(*us));
405439
us->wrapper = uwrap;
406-
/* zend_call_method_if_exists() may unregister the stream wrapper. Hold on to it. */
440+
/* method call may unregister the stream wrapper. Hold on to it. */
407441
GC_ADDREF(us->wrapper->resource);
408442

409443
us->object = user_stream_create_object(uwrap, context);
@@ -415,9 +449,7 @@ static php_stream *user_wrapper_opendir(php_stream_wrapper *wrapper, const char
415449
ZVAL_STRING(&args[0], filename);
416450
ZVAL_LONG(&args[1], options);
417451

418-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_OPEN, false);
419-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &zretval, 2, args);
420-
zend_string_release_ex(func_name, false);
452+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_DIR_OPEN), &zretval, 2, args);
421453
zval_ptr_dtor(&args[0]);
422454

423455
if (UNEXPECTED(call_result == FAILURE)) {
@@ -570,9 +602,7 @@ static ssize_t php_userstreamop_write(php_stream *stream, const char *buf, size_
570602
uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
571603
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
572604

573-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_WRITE, false);
574-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 1, args);
575-
zend_string_release_ex(func_name, false);
605+
zend_result call_result = php_userstream_call(us, &us->fn_cache_write, ZEND_STRL(USERSTREAM_WRITE), &retval, 1, args);
576606
zval_ptr_dtor(&args[0]);
577607

578608
if (UNEXPECTED(call_result == FAILURE)) {
@@ -619,9 +649,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
619649
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
620650

621651
ZVAL_LONG(&args[0], count);
622-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_READ, false);
623-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 1, args);
624-
zend_string_release_ex(func_name, false);
652+
zend_result call_result = php_userstream_call(us, &us->fn_cache_read, ZEND_STRL(USERSTREAM_READ), &retval, 1, args);
625653

626654
if (UNEXPECTED(Z_ISUNDEF(retval))) {
627655
goto err;
@@ -656,11 +684,7 @@ static ssize_t php_userstreamop_read(php_stream *stream, char *buf, size_t count
656684
ZVAL_UNDEF(&retval);
657685

658686
/* since the user stream has no way of setting the eof flag directly, we need to ask it if we hit eof */
659-
660-
func_name = ZSTR_INIT_LITERAL(USERSTREAM_EOF, false);
661-
call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
662-
zend_string_release_ex(func_name, false);
663-
687+
call_result = php_userstream_call(us, &us->fn_cache_eof, ZEND_STRL(USERSTREAM_EOF), &retval, 0, NULL);
664688
if (UNEXPECTED(call_result == FAILURE)) {
665689
php_error_docref(NULL, E_WARNING,
666690
"%s::" USERSTREAM_EOF " is not implemented! Assuming EOF",
@@ -696,9 +720,7 @@ static int php_userstreamop_close(php_stream *stream, int close_handle)
696720

697721
ZEND_ASSERT(us != NULL);
698722

699-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_CLOSE, false);
700-
zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
701-
zend_string_release_ex(func_name, false);
723+
php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_CLOSE), &retval, 0, NULL);
702724

703725
zval_ptr_dtor(&retval);
704726

@@ -716,11 +738,9 @@ static int php_userstreamop_flush(php_stream *stream)
716738

717739
ZEND_ASSERT(us != NULL);
718740

719-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_FLUSH, false);
720-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
721-
zend_string_release_ex(func_name, false);
741+
php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_FLUSH), &retval, 0, NULL);
722742

723-
int ret = call_result == SUCCESS && Z_TYPE(retval) != IS_UNDEF && zend_is_true(&retval) ? 0 : -1;
743+
int ret = Z_TYPE(retval) != IS_UNDEF && zend_is_true(&retval) ? 0 : -1;
724744

725745
zval_ptr_dtor(&retval);
726746

@@ -742,10 +762,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
742762
uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
743763
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
744764

745-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_SEEK, false);
746-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 2, args);
747-
zend_string_release_ex(func_name, false);
748-
765+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_SEEK), &retval, 2, args);
749766
if (call_result == FAILURE) {
750767
/* stream_seek is not implemented, so disable seeks for this stream */
751768
stream->flags |= PHP_STREAM_FLAG_NO_SEEK;
@@ -769,9 +786,7 @@ static int php_userstreamop_seek(php_stream *stream, zend_off_t offset, int when
769786
}
770787

771788
/* now determine where we are */
772-
func_name = ZSTR_INIT_LITERAL(USERSTREAM_TELL, false);
773-
call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
774-
zend_string_release_ex(func_name, false);
789+
call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_TELL), &retval, 0, NULL);
775790

776791
if (call_result == SUCCESS && Z_TYPE(retval) == IS_LONG) {
777792
*newoffs = Z_LVAL(retval);
@@ -836,10 +851,7 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
836851
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
837852
int ret = -1;
838853

839-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_STAT, false);
840-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
841-
zend_string_release_ex(func_name, false);
842-
854+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_STAT), &retval, 0, NULL);
843855
if (UNEXPECTED(call_result == FAILURE)) {
844856
php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_STAT " is not implemented!",
845857
ZSTR_VAL(us->wrapper->ce->name));
@@ -860,13 +872,11 @@ static int php_userstreamop_stat(php_stream *stream, php_stream_statbuf *ssb)
860872
return ret;
861873
}
862874

863-
static int user_stream_set_check_liveliness(const php_userstream_data_t *us)
875+
static int user_stream_set_check_liveliness(php_userstream_data_t *us)
864876
{
865877
zval retval;
866878

867-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_EOF, false);
868-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
869-
zend_string_release_ex(func_name, false);
879+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_EOF), &retval, 0, NULL);
870880

871881
if (UNEXPECTED(call_result == FAILURE)) {
872882
php_error_docref(NULL, E_WARNING,
@@ -888,7 +898,7 @@ static int user_stream_set_check_liveliness(const php_userstream_data_t *us)
888898
}
889899
}
890900

891-
static int user_stream_set_locking(const php_userstream_data_t *us, int value)
901+
static int user_stream_set_locking(php_userstream_data_t *us, int value)
892902
{
893903
zval retval;
894904
zval zlock;
@@ -914,9 +924,7 @@ static int user_stream_set_locking(const php_userstream_data_t *us, int value)
914924
ZVAL_LONG(&zlock, lock);
915925

916926
/* TODO wouldblock */
917-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_LOCK, false);
918-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 1, &zlock);
919-
zend_string_release_ex(func_name, false);
927+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_LOCK), &retval, 1, &zlock);
920928

921929
if (UNEXPECTED(call_result == FAILURE)) {
922930
if (value == 0) {
@@ -991,7 +999,7 @@ static int user_stream_set_truncation(const php_userstream_data_t *us, int value
991999
}
9921000
}
9931001

994-
static int user_stream_set_option(const php_userstream_data_t *us, int option, int value, void *ptrparam)
1002+
static int user_stream_set_option(php_userstream_data_t *us, int option, int value, void *ptrparam)
9951003
{
9961004
zval args[3];
9971005
ZVAL_LONG(&args[0], option);
@@ -1011,9 +1019,7 @@ static int user_stream_set_option(const php_userstream_data_t *us, int option, i
10111019
}
10121020

10131021
zval retval;
1014-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_SET_OPTION, false);
1015-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 3, args);
1016-
zend_string_release_ex(func_name, false);
1022+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_SET_OPTION), &retval, 3, args);
10171023

10181024
if (UNEXPECTED(call_result == FAILURE)) {
10191025
php_error_docref(NULL, E_WARNING,
@@ -1324,9 +1330,7 @@ static ssize_t php_userstreamop_readdir(php_stream *stream, char *buf, size_t co
13241330
return -1;
13251331
}
13261332

1327-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_READ, false);
1328-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
1329-
zend_string_release_ex(func_name, false);
1333+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_DIR_READ), &retval, 0, NULL);
13301334

13311335
if (UNEXPECTED(call_result == FAILURE)) {
13321336
php_error_docref(NULL, E_WARNING, "%s::" USERSTREAM_DIR_READ " is not implemented!",
@@ -1360,9 +1364,7 @@ static int php_userstreamop_closedir(php_stream *stream, int close_handle)
13601364

13611365
ZEND_ASSERT(us != NULL);
13621366

1363-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_CLOSE, false);
1364-
zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
1365-
zend_string_release_ex(func_name, false);
1367+
php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_DIR_CLOSE), &retval, 0, NULL);
13661368

13671369
zval_ptr_dtor(&retval);
13681370
OBJ_RELEASE(us->object);
@@ -1376,9 +1378,7 @@ static int php_userstreamop_rewinddir(php_stream *stream, zend_off_t offset, int
13761378
zval retval;
13771379
php_userstream_data_t *us = (php_userstream_data_t *)stream->abstract;
13781380

1379-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_DIR_REWIND, false);
1380-
zend_call_method_if_exists(us->object, func_name, &retval, 0, NULL);
1381-
zend_string_release_ex(func_name, false);
1381+
php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_DIR_REWIND), &retval, 0, NULL);
13821382

13831383
zval_ptr_dtor(&retval);
13841384

@@ -1408,9 +1408,7 @@ static int php_userstreamop_cast(php_stream *stream, int castas, void **retptr)
14081408
uint32_t orig_no_fclose = stream->flags & PHP_STREAM_FLAG_NO_FCLOSE;
14091409
stream->flags |= PHP_STREAM_FLAG_NO_FCLOSE;
14101410

1411-
zend_string *func_name = ZSTR_INIT_LITERAL(USERSTREAM_CAST, false);
1412-
zend_result call_result = zend_call_method_if_exists(us->object, func_name, &retval, 1, args);
1413-
zend_string_release_ex(func_name, false);
1411+
zend_result call_result = php_userstream_call(us, NULL, ZEND_STRL(USERSTREAM_CAST), &retval, 1, args);
14141412

14151413
if (UNEXPECTED(call_result == FAILURE)) {
14161414
if (report_errors) {

0 commit comments

Comments
 (0)