summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-25 02:02:55 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2010-12-25 02:02:55 +0000
commit91c6ba23336789f340878f69b5fee1defe0fa5d5 (patch)
tree0f6d71a98c547ef6b7886a99d242f3080aed1780
parentaeb8d7f5e591915d7c4e290a3bee3185c84f0630 (diff)
* ext/zlib/zlib.c (gzreader_gets): support optional length
parameter. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@30341 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog5
-rw-r--r--ext/zlib/zlib.c102
-rw-r--r--test/zlib/test_zlib.rb34
3 files changed, 125 insertions, 16 deletions
diff --git a/ChangeLog b/ChangeLog
index e927b61af5..a610161939 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,7 @@
-Sat Dec 25 10:59:12 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
+Sat Dec 25 11:02:52 2010 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * ext/zlib/zlib.c (gzreader_gets): support optional length
+ parameter.
* ext/zlib/zlib.c (gzfile_read, gzfile_readpartial): length should
be long.
diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c
index 458a7009e0..9a04c61bf3 100644
--- a/ext/zlib/zlib.c
+++ b/ext/zlib/zlib.c
@@ -2204,15 +2204,13 @@ gzfile_newstr(struct gzfile *gz, VALUE str)
gz->ecflags, gz->ecopts);
}
-static VALUE
-gzfile_read(struct gzfile *gz, long len)
+static long
+gzfile_fill(struct gzfile *gz, long len)
{
- VALUE dst;
-
if (len < 0)
rb_raise(rb_eArgError, "negative length %ld given", len);
if (len == 0)
- return rb_str_new(0, 0);
+ return 0;
while (!ZSTREAM_IS_FINISHED(&gz->z) && gz->z.buf_filled < len) {
gzfile_read_more(gz);
}
@@ -2220,9 +2218,19 @@ gzfile_read(struct gzfile *gz, long len)
if (!(gz->z.flags & GZFILE_FLAG_FOOTER_FINISHED)) {
gzfile_check_footer(gz);
}
- return Qnil;
+ return -1;
}
+ return len < gz->z.buf_filled ? len : gz->z.buf_filled;
+}
+static VALUE
+gzfile_read(struct gzfile *gz, long len)
+{
+ VALUE dst;
+
+ len = gzfile_fill(gz, len);
+ if (len == 0) return rb_str_new(0, 0);
+ if (len < 0) return Qnil;
dst = zstream_shift_buffer(&gz->z, len);
gzfile_calc_crc(gz, dst);
return dst;
@@ -3351,6 +3359,27 @@ rscheck(const char *rsptr, long rslen, VALUE rs)
rb_raise(rb_eRuntimeError, "rs modified");
}
+static long
+gzreader_charboundary(struct gzfile *gz, long n)
+{
+ char *s = RSTRING_PTR(gz->z.buf);
+ char *e = s + gz->z.buf_filled;
+ char *p = rb_enc_left_char_head(s, s + n, e, gz->enc);
+ long l = p - s;
+ if (l < n) {
+ n = rb_enc_precise_mbclen(p, e, gz->enc);
+ if (MBCLEN_NEEDMORE_P(n)) {
+ if ((l = gzfile_fill(gz, l + MBCLEN_NEEDMORE_LEN(n))) > 0) {
+ return l;
+ }
+ }
+ else if (MBCLEN_CHARFOUND_P(n)) {
+ return l + MBCLEN_CHARFOUND_LEN(n);
+ }
+ }
+ return n;
+}
+
static VALUE
gzreader_gets(int argc, VALUE *argv, VALUE obj)
{
@@ -3359,24 +3388,57 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj)
VALUE dst;
const char *rsptr;
char *p, *res;
- long rslen, n;
+ long rslen, n, limit = -1;
int rspara;
+ rb_encoding *enc = gz->enc;
+ int maxlen = rb_enc_mbmaxlen(enc);
if (argc == 0) {
rs = rb_rs;
}
else {
- rb_scan_args(argc, argv, "1", &rs);
- if (!NIL_P(rs)) {
- Check_Type(rs, T_STRING);
+ VALUE lim, tmp;
+
+ rb_scan_args(argc, argv, "11", &rs, &lim);
+ if (!NIL_P(lim)) {
+ if (!NIL_P(rs)) StringValue(rs);
+ }
+ else if (!NIL_P(rs)) {
+ tmp = rb_check_string_type(rs);
+ if (NIL_P(tmp)) {
+ lim = rs;
+ rs = rb_rs;
+ }
+ else {
+ rs = tmp;
+ }
+ }
+ if (!NIL_P(lim)) {
+ limit = NUM2LONG(lim);
+ if (limit == 0) return rb_str_new(0,0);
}
}
if (NIL_P(rs)) {
- dst = gzfile_read_all(gz);
- if (RSTRING_LEN(dst) != 0) gz->lineno++;
- else
+ if (limit < 0) {
+ dst = gzfile_read_all(gz);
+ if (RSTRING_LEN(dst) == 0) return Qnil;
+ }
+ else if ((n = gzfile_fill(gz, limit)) <= 0) {
return Qnil;
+ }
+ else {
+ if (maxlen > 1 && n >= limit && !GZFILE_IS_FINISHED(gz)) {
+ n = gzreader_charboundary(gz, n);
+ }
+ else {
+ n = limit;
+ }
+ dst = zstream_shift_buffer(&gz->z, n);
+ gzfile_calc_crc(gz, dst);
+ dst = gzfile_newstr(gz, dst);
+ }
+ gz->lineno++;
return dst;
}
@@ -3405,15 +3467,22 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj)
p = RSTRING_PTR(gz->z.buf);
n = rslen;
for (;;) {
+ long filled;
if (n > gz->z.buf_filled) {
if (ZSTREAM_IS_FINISHED(&gz->z)) break;
gzfile_read_more(gz);
p = RSTRING_PTR(gz->z.buf) + n - rslen;
}
if (!rspara) rscheck(rsptr, rslen, rs);
- res = memchr(p, rsptr[0], (gz->z.buf_filled - n + 1));
+ filled = gz->z.buf_filled;
+ if (limit > 0 && filled >= limit) {
+ filled = limit;
+ }
+ res = memchr(p, rsptr[0], (filled - n + 1));
if (!res) {
- n = gz->z.buf_filled + 1;
+ n = filled;
+ if (limit > 0 && filled >= limit) break;
+ n++;
} else {
n += (long)(res - p);
p = res;
@@ -3421,6 +3490,9 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj)
p++, n++;
}
}
+ if (maxlen > 1 && n == limit && (gz->z.buf_filled > n || !ZSTREAM_IS_FINISHED(&gz->z))) {
+ n = gzreader_charboundary(gz, n);
+ }
gz->lineno++;
dst = gzfile_read(gz, n);
diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb
index 746dfba844..68b44a1c32 100644
--- a/test/zlib/test_zlib.rb
+++ b/test/zlib/test_zlib.rb
@@ -588,6 +588,40 @@ if defined? Zlib
Zlib::GzipReader.open(t.path) do |f|
assert_equal("foo\nbar\nbaz\n", f.gets(nil))
end
+
+ Zlib::GzipReader.open(t.path) do |f|
+ assert_equal("foo\n", f.gets(10))
+ assert_equal("ba", f.gets(2))
+ assert_equal("r\nb", f.gets(nil, 3))
+ assert_equal("az\n", f.gets(nil, 10))
+ assert_nil(f.gets)
+ end
+ end
+
+ def test_gets2
+ t = Tempfile.new("test_zlib_gzip_reader_gets2")
+ t.close
+ ustrs = %W"\u{3042 3044 3046}\n \u{304b 304d 304f}\n \u{3055 3057 3059}\n"
+ Zlib::GzipWriter.open(t.path) {|gz| gz.print(*ustrs) }
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs[0], f.gets)
+ assert_equal(ustrs[1], f.gets)
+ assert_equal(ustrs[2], f.gets)
+ assert_nil(f.gets)
+ end
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs.join(''), f.gets(nil))
+ end
+
+ Zlib::GzipReader.open(t.path, encoding: "UTF-8") do |f|
+ assert_equal(ustrs[0], f.gets(20))
+ assert_equal(ustrs[1][0,2], f.gets(5))
+ assert_equal(ustrs[1][2..-1]+ustrs[2][0,1], f.gets(nil, 5))
+ assert_equal(ustrs[2][1..-1], f.gets(nil, 20))
+ assert_nil(f.gets)
+ end
end
def test_readline