From 40039d93abe3a23e77a8b627d8597fc763c813a8 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Thu, 5 Jan 2023 17:19:50 +0900 Subject: [PATCH] mruby-io/io.c: implement `IO#getline` in C --- mrbgems/mruby-io/mrblib/io.rb | 48 ------------ mrbgems/mruby-io/src/io.c | 134 +++++++++++++++++++++++++++++++--- 2 files changed, 124 insertions(+), 58 deletions(-) diff --git a/mrbgems/mruby-io/mrblib/io.rb b/mrbgems/mruby-io/mrblib/io.rb index 921a273d9..b7aba6498 100644 --- a/mrbgems/mruby-io/mrblib/io.rb +++ b/mrbgems/mruby-io/mrblib/io.rb @@ -107,54 +107,6 @@ class IO ungetc s end - def readline(arg = "\n", limit = nil) - case arg - when String - rs = arg - when Integer - rs = "\n" - limit = arg - else - raise ArgumentError - end - - if rs.nil? - return read - end - - if rs == "" - rs = "\n\n" - end - - array = [] - while true - begin - _read_buf - rescue EOFError - array = nil if array.empty? - break - end - - if limit && limit <= @buf.size - array.push @buf[0, limit] - @buf[0, limit] = "" - break - elsif idx = @buf.index(rs) - len = idx + rs.size - array.push @buf[0, len] - @buf[0, len] = "" - break - else - array.push @buf - @buf = '' - end - end - - raise EOFError.new "end of file reached" if array.nil? - - array.join - end - def gets(*args) begin readline(*args) diff --git a/mrbgems/mruby-io/src/io.c b/mrbgems/mruby-io/src/io.c index 3cb416e13..1a3e706de 100644 --- a/mrbgems/mruby-io/src/io.c +++ b/mrbgems/mruby-io/src/io.c @@ -1617,6 +1617,21 @@ io_eof(mrb_state *mrb, mrb_value io) return mrb_bool_value(fptr->eof); } +static mrb_value +io_read_all(mrb_state *mrb, mrb_value io, mrb_value outbuf) +{ + struct mrb_io *fptr = io_get_read_fptr(mrb, io); + for (;;) { + io_read_buf_noraise(mrb, io); + if (fptr->eof) { + return outbuf; + } + mrb_value buf = io_buf(mrb, io); + mrb_str_cat_str(mrb, outbuf, buf); + RSTR_SET_LEN(RSTRING(buf), 0); + } +} + static mrb_value io_read(mrb_state *mrb, mrb_value io) { @@ -1646,15 +1661,7 @@ io_read(mrb_state *mrb, mrb_value io) outbuf = mrb_str_new_capa(mrb, BUF_SIZE); } if (!length_given) { /* read as much as possible */ - for (;;) { - io_read_buf_noraise(mrb, io); - if (fptr->eof) { - return outbuf; - } - mrb_value buf = io_buf(mrb, io); - mrb_str_cat_str(mrb, outbuf, buf); - RSTR_SET_LEN(RSTRING(buf), 0); - } + return io_read_all(mrb, io, outbuf); } for (;;) { io_read_buf_noraise(mrb, io); @@ -1677,6 +1684,113 @@ io_read(mrb_state *mrb, mrb_value io) } } +static mrb_value +io_readline(mrb_state *mrb, mrb_value io) +{ + mrb_value rs = mrb_nil_value(); + mrb_int limit; + mrb_bool rs_given = FALSE; /* newline break */ + mrb_bool limit_given = FALSE; /* no limit */ + mrb_value outbuf; + + mrb_get_args(mrb, "|o?i?", &rs, &rs_given, &limit, &limit_given); + + if (limit_given == FALSE) { + if (rs_given) { + if (mrb_nil_p(rs)) { + rs_given = FALSE; + } + else if (mrb_integer_p(rs)) { + limit = mrb_integer(rs); + limit_given = TRUE; + rs = mrb_nil_value(); + } + else if (!mrb_string_p(rs)) { + mrb_ensure_int_type(mrb, rs); + } + } + } + if (rs_given) { + if (mrb_nil_p(rs)) { + rs_given = FALSE; + } + else { + mrb_ensure_string_type(mrb, rs); + if (RSTRING_LEN(rs) == 0) { /* paragraph mode */ + rs = mrb_str_new_lit(mrb, "\n\n"); + } + } + } + else { + rs = mrb_str_new_lit(mrb, "\n"); + rs_given = TRUE; + } + /* from now on rs_given==FALSE means no RS */ + if (mrb_nil_p(rs) && !limit_given) { + return io_read_all(mrb, io, mrb_str_new_capa(mrb, BUF_SIZE)); + } + + if (limit_given) { + if (limit == 0) return mrb_str_new(mrb, NULL, 0); + outbuf = mrb_str_new_capa(mrb, limit); + } + else { + outbuf = mrb_str_new(mrb, NULL, 0); + } + + struct mrb_io *fptr = io_get_read_fptr(mrb, io); + mrb_value buf = io_buf(mrb, io); + io_read_buf(mrb, io); + + if (!rs_given) { /* no RS; only limit */ + mrb_assert(limit_given); + for (;;) { + if (RSTRING_LEN(buf) >= limit) { + mrb_str_cat(mrb, outbuf, RSTRING_PTR(buf), limit); + io_bufshift(mrb, RSTRING(buf), limit); + return outbuf; + } + mrb_str_cat(mrb, outbuf, RSTRING_PTR(buf), RSTRING_LEN(buf)); + limit -= RSTRING_LEN(buf); + RSTR_SET_LEN(RSTRING(buf), 0); + + io_read_buf_noraise(mrb, io); + if (fptr->eof) { + if (RSTRING_LEN(outbuf) == 0) + mrb_raise(mrb, E_EOF_ERROR, "end of file reached"); + return outbuf; + } + } + } + + for (;;) { /* with RS */ + mrb_int idx = mrb_str_index(mrb, buf, RSTRING_PTR(rs), RSTRING_LEN(rs), 0); + if (idx >= 0) { /* found */ + mrb_int n = idx+RSTRING_LEN(rs); + if (limit_given && limit < n) { + n = limit; + } + mrb_str_cat(mrb, outbuf, RSTRING_PTR(buf), n); + io_bufshift(mrb, RSTRING(buf), n); + return outbuf; + } + if (limit_given && RSTRING_LEN(buf) < limit) { + mrb_str_cat(mrb, outbuf, RSTRING_PTR(buf), limit); + io_bufshift(mrb, RSTRING(buf), limit); + return outbuf; + } + mrb_str_cat(mrb, outbuf, RSTRING_PTR(buf), RSTRING_LEN(buf)); + RSTR_SET_LEN(RSTRING(buf), 0); + + io_read_buf_noraise(mrb, io); + if (fptr->eof) { + if (RSTRING_LEN(outbuf) == 0) + mrb_raise(mrb, E_EOF_ERROR, "end of file reached"); + return outbuf; + } + } +} + static mrb_value io_readchar(mrb_state *mrb, mrb_value io) { @@ -1751,6 +1865,7 @@ mrb_init_io(mrb_state *mrb) mrb_define_method(mrb, io, "isatty", io_isatty, MRB_ARGS_NONE()); mrb_define_method(mrb, io, "eof?", io_eof, MRB_ARGS_NONE()); /* 15.2.20.5.6 */ mrb_define_method(mrb, io, "read", io_read, MRB_ARGS_OPT(2)); /* 15.2.20.5.14 */ + mrb_define_method(mrb, io, "readline", io_readline, MRB_ARGS_OPT(2)); /* 15.2.20.5.16 */ mrb_define_method(mrb, io, "sync", io_sync, MRB_ARGS_NONE()); /* 15.2.20.5.18 */ mrb_define_method(mrb, io, "sync=", io_set_sync, MRB_ARGS_REQ(1)); /* 15.2.20.5.19 */ mrb_define_method(mrb, io, "sysread", io_sysread, MRB_ARGS_ARG(1,1)); @@ -1777,6 +1892,5 @@ mrb_init_io(mrb_state *mrb) mrb_define_const_id(mrb, io, MRB_SYM(SEEK_CUR), mrb_fixnum_value(SEEK_CUR)); mrb_define_const_id(mrb, io, MRB_SYM(SEEK_END), mrb_fixnum_value(SEEK_END)); - mrb_define_method(mrb, io, "_read_buf", io_read_buf, MRB_ARGS_NONE()); mrb_define_class_method(mrb, io, "_bufread", io_bufread_m, MRB_ARGS_REQ(2)); }