Skip to content

Commit d0eaa9c

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 9f2a262 commit d0eaa9c

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
@@ -100,6 +100,8 @@ php_file_globals file_globals;
100100

101101
#include "file_arginfo.h"
102102

103+
#include <sys/uio.h> // for writev()
104+
103105
/* }}} */
104106

105107
#define PHP_STREAM_FROM_ZVAL(stream, arg) \
@@ -440,6 +442,17 @@ PHP_FUNCTION(file_get_contents)
440442
}
441443
/* }}} */
442444

445+
ZEND_ATTRIBUTE_PURE
446+
static bool is_string_array(HashTable *ht)
447+
{
448+
zval *tmp;
449+
ZEND_HASH_FOREACH_VAL(ht, tmp) {
450+
if (Z_TYPE_P(tmp) != IS_STRING)
451+
return false;
452+
} ZEND_HASH_FOREACH_END();
453+
return true;
454+
}
455+
443456
/* {{{ Write/Create a file with contents data and return the number of bytes written */
444457
PHP_FUNCTION(file_put_contents)
445458
{
@@ -529,6 +542,41 @@ PHP_FUNCTION(file_put_contents)
529542
break;
530543

531544
case IS_ARRAY:
545+
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))) {
546+
/* if we're writing a string array to
547+
a regular file, use one writev()
548+
system call instead of many
549+
write() calls */
550+
551+
int fd;
552+
if (php_stream_cast(stream, PHP_STREAM_AS_FD, (void *)&fd, 0) == SUCCESS) {
553+
struct iovec v[IOV_MAX];
554+
size_t n = 0, total = 0;
555+
556+
zval *tmp;
557+
ZEND_HASH_FOREACH_VAL(Z_ARRVAL_P(data), tmp) {
558+
const size_t length = Z_STRLEN_P(tmp);
559+
if (length > 0) {
560+
total += length;
561+
v[n].iov_len = length;
562+
v[n].iov_base = Z_STRVAL_P(tmp);
563+
++n;
564+
}
565+
} ZEND_HASH_FOREACH_END();
566+
567+
if (n == 0)
568+
break;
569+
570+
numbytes = writev(fd, v, n);
571+
if (numbytes != -1 && (size_t)numbytes != total) {
572+
php_error_docref(NULL, E_WARNING, "Failed to write %zu bytes to %s", total, filename);
573+
numbytes = -1;
574+
}
575+
576+
break;
577+
}
578+
}
579+
532580
if (zend_hash_num_elements(Z_ARRVAL_P(data))) {
533581
ssize_t bytes_written;
534582
zval *tmp;

0 commit comments

Comments
 (0)