From 9d084b09b7f18230f89452c5cc0861353cb9a967 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 31 May 2026 09:09:20 +0900 Subject: [PATCH] mruby-io: write literal strings directly via fd_write_buf io_puts_str, io_puts_ary, and io_puts allocated a fresh mruby String for every "\n", empty-array marker, "[...]" overflow marker, and no-arg newline, only for fd_write to unpack it back to ptr/len. The allocations also stayed on the GC arena across the recursive walk, scaling pressure with array length. Split fd_write into fd_write_buf (the EINTR-resilient write loop over ptr/len) plus the existing mrb_value wrapper, and add a FD_WRITE_LIT macro for compile-time-known literals. Replace the four mrb_str_new_lit + fd_write pairs with FD_WRITE_LIT. The "" s "" inside the macro enforces that the argument is a string literal so sizeof(s) - 1 is the correct length. Co-authored-by: Claude --- mrbgems/mruby-io/src/io.c | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c index e38545c2c..87d6f9a46 100644 --- a/mrbgems/mruby-io/src/io.c +++ b/mrbgems/mruby-io/src/io.c @@ -911,18 +911,12 @@ io_syswrite(mrb_state *mrb, mrb_value io) /* end */ static mrb_int -fd_write(mrb_state *mrb, int fd, mrb_value str) +fd_write_buf(mrb_state *mrb, int fd, const char *ptr, mrb_int len) { - fssize_t n; - - str = mrb_obj_as_string(mrb, str); - fssize_t len = (fssize_t)RSTRING_LEN(str); if (len == 0) return 0; - - const char *ptr = RSTRING_PTR(str); fssize_t sum = 0; - while (sum < len) { - n = write(fd, ptr + sum, len - sum); + while (sum < (fssize_t)len) { + fssize_t n = write(fd, ptr + sum, (size_t)(len - sum)); if (n == -1) { if (errno == EINTR) continue; mrb_sys_fail(mrb, "syswrite"); @@ -932,6 +926,15 @@ fd_write(mrb_state *mrb, int fd, mrb_value str) return len; } +static mrb_int +fd_write(mrb_state *mrb, int fd, mrb_value str) +{ + str = mrb_obj_as_string(mrb, str); + return fd_write_buf(mrb, fd, RSTRING_PTR(str), RSTRING_LEN(str)); +} + +#define FD_WRITE_LIT(mrb, fd, s) fd_write_buf(mrb, fd, "" s "", sizeof(s) - 1) + /* Helper function to prepare IO object for writing by adjusting buffer state */ static void io_prepare_write(mrb_state *mrb, struct mrb_io *fptr) @@ -987,8 +990,7 @@ io_puts_str(mrb_state *mrb, int fd, mrb_value str) /* Add newline if string doesn't end with one */ if (len == 0 || ptr[len-1] != '\n') { - mrb_value newline = mrb_str_new_lit(mrb, "\n"); - fd_write(mrb, fd, newline); + FD_WRITE_LIT(mrb, fd, "\n"); } } @@ -1001,8 +1003,7 @@ static void io_puts_ary(mrb_state *mrb, int fd, mrb_value ary, int depth) { if (depth >= IO_PUTS_MAX_DEPTH) { - mrb_value mark = mrb_str_new_lit(mrb, "[...]\n"); - fd_write(mrb, fd, mark); + FD_WRITE_LIT(mrb, fd, "[...]\n"); return; } @@ -1010,8 +1011,7 @@ io_puts_ary(mrb_state *mrb, int fd, mrb_value ary, int depth) if (len == 0) { /* Empty array - write a single newline */ - mrb_value newline = mrb_str_new_lit(mrb, "\n"); - fd_write(mrb, fd, newline); + FD_WRITE_LIT(mrb, fd, "\n"); return; } @@ -1041,8 +1041,7 @@ io_puts(mrb_state *mrb, mrb_value io) if (argc == 0) { /* No arguments - just write a newline */ - mrb_value newline = mrb_str_new_lit(mrb, "\n"); - fd_write(mrb, fd, newline); + FD_WRITE_LIT(mrb, fd, "\n"); return mrb_nil_value(); }