summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-07-07 07:31:09 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2016-07-07 07:31:09 +0000
commit415059abf14c63979621be79512713772f9e9970 (patch)
tree193320aa64f741d8469857aac414521a2d6da9b8
parent8667e8b1864379bab0bfe4ac97d4c5eeb61d684f (diff)
io.c: convert arguments just once
* io.c (rb_io_s_foreach, rb_io_s_readlines): convert arguments just once before reading, instead of conversions for each lines. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@55603 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--io.c61
-rw-r--r--test/ruby/test_io.rb68
3 files changed, 123 insertions, 11 deletions
diff --git a/ChangeLog b/ChangeLog
index 0886597aa1..5dbd688284 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Thu Jul 7 16:31:07 2016 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * io.c (rb_io_s_foreach, rb_io_s_readlines): convert arguments
+ just once before reading, instead of conversions for each lines.
+
Wed Jul 6 19:54:17 2016 Martin Duerst <duerst@it.aoyama.ac.jp>
* enc/iso_8859_14.c, test/ruby/enc/test_case_comprehensive.rb:
diff --git a/io.c b/io.c
index 10cdeb699c..2f09957470 100644
--- a/io.c
+++ b/io.c
@@ -3011,11 +3011,16 @@ rb_io_getline_fast(rb_io_t *fptr, rb_encoding *enc)
return str;
}
+struct getline_arg {
+ VALUE io;
+ VALUE rs;
+ long limit;
+};
+
static void
-prepare_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit, VALUE io)
+extract_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit)
{
VALUE rs = rb_rs, lim = Qnil;
- rb_io_t *fptr;
rb_check_arity(argc, 0, 2);
if (argc == 1) {
@@ -3033,6 +3038,16 @@ prepare_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit, VALUE io)
if (!NIL_P(rs))
StringValue(rs);
}
+ *rsp = rs;
+ *limit = NIL_P(lim) ? -1L : NUM2LONG(lim);
+}
+
+static void
+check_getline_args(VALUE *rsp, long *limit, VALUE io)
+{
+ rb_io_t *fptr;
+ VALUE rs = *rsp;
+
if (!NIL_P(rs)) {
rb_encoding *enc_rs, *enc_io;
@@ -3045,6 +3060,7 @@ prepare_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit, VALUE io)
if (rs == rb_default_rs) {
rs = rb_enc_str_new(0, 0, enc_io);
rb_str_buf_cat_ascii(rs, "\n");
+ *rsp = rs;
}
else {
rb_raise(rb_eArgError, "encoding mismatch: %s IO with %s RS",
@@ -3053,8 +3069,13 @@ prepare_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit, VALUE io)
}
}
}
- *rsp = rs;
- *limit = NIL_P(lim) ? -1L : NUM2LONG(lim);
+}
+
+static void
+prepare_getline_args(int argc, VALUE *argv, VALUE *rsp, long *limit, VALUE io)
+{
+ extract_getline_args(argc, argv, rsp, limit);
+ check_getline_args(rsp, limit, io);
}
static VALUE
@@ -3326,6 +3347,8 @@ rb_io_readline(int argc, VALUE *argv, VALUE io)
return line;
}
+static VALUE io_readlines(VALUE rs, long limit, VALUE io);
+
/*
* call-seq:
* ios.readlines(sep=$/) -> array
@@ -3347,10 +3370,18 @@ rb_io_readline(int argc, VALUE *argv, VALUE io)
static VALUE
rb_io_readlines(int argc, VALUE *argv, VALUE io)
{
- VALUE line, ary, rs;
+ VALUE rs;
long limit;
prepare_getline_args(argc, argv, &rs, &limit, io);
+ return io_readlines(rs, limit, io);
+}
+
+static VALUE
+io_readlines(VALUE rs, long limit, VALUE io)
+{
+ VALUE line, ary;
+
if (limit == 0)
rb_raise(rb_eArgError, "invalid limit: 0 for readlines");
ary = rb_ary_new();
@@ -9695,13 +9726,15 @@ open_key_args(int argc, VALUE *argv, VALUE opt, struct foreach_arg *arg)
}
static VALUE
-io_s_foreach(struct foreach_arg *arg)
+io_s_foreach(struct getline_arg *arg)
{
VALUE str;
- while (!NIL_P(str = rb_io_gets_m(arg->argc, arg->argv, arg->io))) {
+ while (!NIL_P(str = rb_io_getline_1(arg->rs, arg->limit, arg->io))) {
+ rb_lastline_set(str);
rb_yield(str);
}
+ rb_lastline_set(Qnil);
return Qnil;
}
@@ -9737,18 +9770,21 @@ rb_io_s_foreach(int argc, VALUE *argv, VALUE self)
VALUE opt;
int orig_argc = argc;
struct foreach_arg arg;
+ struct getline_arg garg;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
RETURN_ENUMERATOR(self, orig_argc, argv);
+ extract_getline_args(argc-1, argv+1, &garg.rs, &garg.limit);
open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
- return rb_ensure(io_s_foreach, (VALUE)&arg, rb_io_close, arg.io);
+ check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
+ return rb_ensure(io_s_foreach, (VALUE)&garg, rb_io_close, arg.io);
}
static VALUE
-io_s_readlines(struct foreach_arg *arg)
+io_s_readlines(struct getline_arg *arg)
{
- return rb_io_readlines(arg->argc, arg->argv, arg->io);
+ return io_readlines(arg->rs, arg->limit, arg->io);
}
/*
@@ -9774,11 +9810,14 @@ rb_io_s_readlines(int argc, VALUE *argv, VALUE io)
{
VALUE opt;
struct foreach_arg arg;
+ struct getline_arg garg;
argc = rb_scan_args(argc, argv, "13:", NULL, NULL, NULL, NULL, &opt);
+ extract_getline_args(argc-1, argv+1, &garg.rs, &garg.limit);
open_key_args(argc, argv, opt, &arg);
if (NIL_P(arg.io)) return Qnil;
- return rb_ensure(io_s_readlines, (VALUE)&arg, rb_io_close, arg.io);
+ check_getline_args(&garg.rs, &garg.limit, garg.io = arg.io);
+ return rb_ensure(io_s_readlines, (VALUE)&garg, rb_io_close, arg.io);
}
static VALUE
diff --git a/test/ruby/test_io.rb b/test/ruby/test_io.rb
index 300217be07..505525d95e 100644
--- a/test/ruby/test_io.rb
+++ b/test/ruby/test_io.rb
@@ -3276,4 +3276,72 @@ End
end
end
end if File::BINARY != 0
+
+ if RUBY_ENGINE == "ruby" # implementation details
+ def test_foreach_rs_conversion
+ make_tempfile {|t|
+ a = []
+ rs = Struct.new(:count).new(0)
+ def rs.to_str; self.count += 1; "\n"; end
+ IO.foreach(t.path, rs) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ assert_equal(1, rs.count)
+ }
+ end
+
+ def test_foreach_rs_invalid
+ make_tempfile {|t|
+ rs = Object.new
+ def rs.to_str; raise "invalid rs"; end
+ assert_raise(RuntimeError) do
+ IO.foreach(t.path, rs, mode:"w") {}
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
+ }
+ end
+
+ def test_foreach_limit_conversion
+ make_tempfile {|t|
+ a = []
+ lim = Struct.new(:count).new(0)
+ def lim.to_int; self.count += 1; -1; end
+ IO.foreach(t.path, lim) {|x| a << x }
+ assert_equal(["foo\n", "bar\n", "baz\n"], a)
+ assert_equal(1, lim.count)
+ }
+ end
+
+ def test_foreach_limit_invalid
+ make_tempfile {|t|
+ lim = Object.new
+ def lim.to_int; raise "invalid limit"; end
+ assert_raise(RuntimeError) do
+ IO.foreach(t.path, lim, mode:"w") {}
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.foreach(t.path).to_a)
+ }
+ end
+
+ def test_readlines_rs_invalid
+ make_tempfile {|t|
+ rs = Object.new
+ def rs.to_str; raise "invalid rs"; end
+ assert_raise(RuntimeError) do
+ IO.readlines(t.path, rs, mode:"w")
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ }
+ end
+
+ def test_readlines_limit_invalid
+ make_tempfile {|t|
+ lim = Object.new
+ def lim.to_int; raise "invalid limit"; end
+ assert_raise(RuntimeError) do
+ IO.readlines(t.path, lim, mode:"w")
+ end
+ assert_equal(["foo\n", "bar\n", "baz\n"], IO.readlines(t.path))
+ }
+ end
+ end
end