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 <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2025-08-16 12:25:42 +09:00
parent 857a1b3a0d
commit dd9053d0cf
2 changed files with 77 additions and 35 deletions
-35
View File
@@ -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:
+77
View File
@@ -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());