Skip to content

Commit 3e67cb3

Browse files
committed
ext/standard/file: use writev() for file_put_contents() with string array
Combine all write() system calls to just one writev() which is more efficient.
1 parent 73ceaa5 commit 3e67cb3

File tree

1 file changed

+48
-0
lines changed

1 file changed

+48
-0
lines changed

ext/standard/file.c

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,8 @@ php_file_globals file_globals;
112112

113113
#include "file_arginfo.h"
114114

115+
#include <sys/uio.h> // for writev()
116+
115117
/* }}} */
116118

117119
#define PHP_STREAM_FROM_ZVAL(stream, arg) \
@@ -452,6 +454,17 @@ PHP_FUNCTION(file_get_contents)
452454
}
453455
/* }}} */
454456

457+
ZEND_ATTRIBUTE_PURE
458+
static bool is_string_array(HashTable *ht)
459+
{
460+
zval *tmp;
461+
ZEND_HASH_FOREACH_VAL(ht, tmp) {
462+
if (Z_TYPE_P(tmp) != IS_STRING)
463+
return false;
464+
} ZEND_HASH_FOREACH_END();
465+
return true;
466+
}
467+
455468
/* {{{ Write/Create a file with contents data and return the number of bytes written */
456469
PHP_FUNCTION(file_put_contents)
457470
{
@@ -541,6 +554,41 @@ PHP_FUNCTION(file_put_contents)
541554
break;
542555

543556
case IS_ARRAY:
557+
if (php_stream_is(stream, PHP_STREAM_IS_STDIO) && zend_hash_num_elements(Z_ARRVAL_P(data)) <= IOV_MAX && is_string_array(Z_ARRVAL_P(data))) {
558+
/* if we're writing a string array to
559+
a regular file, use one writev()
560+
system call instead of many
561+
write() calls */
562+
563+
int fd;
564+
if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void *)&fd, 0) == SUCCESS) {
565+
struct iovec v[IOV_MAX];
566+
size_t n = 0, total = 0;
567+
568+
zval *tmp;
569+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), tmp) {
570+
const size_t length = Z_STRLEN_P(tmp);
571+
if (length > 0) {
572+
total += length;
573+
v[n].iov_len = length;
574+
v[n].iov_base = Z_STRVAL_P(tmp);
575+
++n;
576+
}
577+
} ZEND_HASH_FOREACH_END();
578+
579+
if (n == 0)
580+
break;
581+
582+
numbytes = writev(fd, v, n);
583+
if (numbytes != -1 && (size_t)numbytes != total) {
584+
php_error_docref(NULL, E_WARNING, "Failed to write %zu bytes to %s", total, filename);
585+
numbytes = -1;
586+
}
587+
588+
break;
589+
}
590+
}
591+
544592
if (zend_hash_num_elements(Z_ARRVAL_P(data))) {
545593
ssize_t bytes_written;
546594
zval *tmp;

0 commit comments

Comments
 (0)