From dd9053d0cf82440f2df42e27a46ea49b7ed0305f Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sat, 16 Aug 2025 12:25:42 +0900 Subject: [PATCH] mruby-io: implement puts in c for improved performance Moved IO#puts from Ruby to C implementation to reduce boundary crossing overhead. Maintains full compatibility including array recursion and newline handling. Co-authored-by: Claude --- mrbgems/mruby-io/mrblib/io.rb | 35 ---------------- mrbgems/mruby-io/src/io.c | 77 +++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 35 deletions(-) diff --git a/mrbgems/mruby-io/mrblib/io.rb b/mrbgems/mruby-io/mrblib/io.rb index 6d058e5d8..7a2e36b41 100644 --- a/mrbgems/mruby-io/mrblib/io.rb +++ b/mrbgems/mruby-io/mrblib/io.rb @@ -304,41 +304,6 @@ class IO self end - # - # call-seq: - # ios.puts(obj, ...) -> nil - # - # Writes the given objects to ios as strings, each followed by a newline - # character unless the string already ends with a newline. If called with - # an array argument, writes each element on a new line. If called without - # arguments, outputs a single newline. - # - # $stdout.puts("this", "is", "a", "test") - # this - # is - # a - # test - # - def puts(*args) - i = 0 - len = args.size - if len == 0 - write "\n" - return - end - while i < len - s = args[i] - if s.kind_of?(Array) - puts(*s) if s.size > 0 - else - s = s.to_s - write s - write "\n" if (s[-1] != "\n") - end - i += 1 - end - nil - end # # call-seq: diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c index c83d95235..22fb255d4 100644 --- a/mrbgems/mruby-io/src/io.c +++ b/mrbgems/mruby-io/src/io.c @@ -1142,6 +1142,82 @@ io_write(mrb_state *mrb, mrb_value io) return mrb_int_value(mrb, len); } +/* Helper function to write a string followed by newline if needed */ +static void +io_puts_str(mrb_state *mrb, int fd, mrb_value str) +{ + str = mrb_obj_as_string(mrb, str); + const char *ptr = RSTRING_PTR(str); + mrb_int len = RSTRING_LEN(str); + + /* Write the original string */ + fd_write(mrb, fd, 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); + } +} + +/* Recursive helper for puts with arrays */ +static void +io_puts_ary(mrb_state *mrb, int fd, mrb_value ary) +{ + mrb_int len = RARRAY_LEN(ary); + + if (len == 0) { + /* Empty array - write a single newline */ + mrb_value newline = mrb_str_new_lit(mrb, "\n"); + fd_write(mrb, fd, newline); + return; + } + + for (mrb_int i = 0; i < len; i++) { + mrb_value elem = RARRAY_PTR(ary)[i]; + if (mrb_array_p(elem)) { + io_puts_ary(mrb, fd, elem); /* Recursive call for nested arrays */ + } + else { + io_puts_str(mrb, fd, elem); + } + } +} + +static mrb_value +io_puts(mrb_state *mrb, mrb_value io) +{ + struct mrb_io *fptr = io_get_write_fptr(mrb, io); + int fd = io_get_write_fd(fptr); + + /* Prepare IO for writing (handle read buffer adjustment) */ + io_prepare_write(mrb, fptr); + + mrb_value *argv; + mrb_int argc; + mrb_get_args(mrb, "*", &argv, &argc); + + if (argc == 0) { + /* No arguments - just write a newline */ + mrb_value newline = mrb_str_new_lit(mrb, "\n"); + fd_write(mrb, fd, newline); + return mrb_nil_value(); + } + + /* Process each argument */ + for (mrb_int i = 0; i < argc; i++) { + mrb_value arg = argv[i]; + if (mrb_array_p(arg)) { + io_puts_ary(mrb, fd, arg); + } + else { + io_puts_str(mrb, fd, arg); + } + } + + return mrb_nil_value(); +} + static mrb_value io_close(mrb_state *mrb, mrb_value io) { @@ -2149,6 +2225,7 @@ mrb_init_io(mrb_state *mrb) mrb_define_method_id(mrb, io, MRB_SYM(pid), io_pid, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(fileno), io_fileno, MRB_ARGS_NONE()); mrb_define_method_id(mrb, io, MRB_SYM(write), io_write, MRB_ARGS_ANY()); /* 15.2.20.5.20 */ + mrb_define_method_id(mrb, io, MRB_SYM(puts), io_puts, MRB_ARGS_ANY()); mrb_define_method_id(mrb, io, MRB_SYM(pread), io_pread, MRB_ARGS_ANY()); /* Ruby 2.5 feature */ mrb_define_method_id(mrb, io, MRB_SYM(pwrite), io_pwrite, MRB_ARGS_ANY()); /* Ruby 2.5 feature */ mrb_define_method_id(mrb, io, MRB_SYM(getbyte), io_getbyte, MRB_ARGS_NONE());