diff --git a/ext/zlib/zlib.c b/ext/zlib/zlib.c index 982933b..87087fa 100644 --- a/ext/zlib/zlib.c +++ b/ext/zlib/zlib.c @@ -4496,6 +4496,12 @@ gzreader_gets(int argc, VALUE *argv, VALUE obj) else { n += (rb_len_t)(res - p); p = res; + if (maxlen > 1 && + rb_enc_left_char_head(RSTRING_PTR(gz->z.buf), p, RSTRING_PTR(gz->z.buf) + ZSTREAM_BUF_FILLED(&gz->z), enc) != p) { + /* the match starts inside a multibyte character: keep searching */ + p++, n++; + continue; + } if (rslen == 1 || memcmp(p, rsptr, rslen) == 0) break; p++, n++; } diff --git a/test/zlib/test_zlib.rb b/test/zlib/test_zlib.rb index 1f9cb81..7e706f7 100644 --- a/test/zlib/test_zlib.rb +++ b/test/zlib/test_zlib.rb @@ -1367,6 +1367,20 @@ def test_reader_wrap } end + def test_gets_separator_inside_multibyte_char + pend "rb_io_extract_encoding_option ignores the encoding options on TruffleRuby" if RUBY_ENGINE == 'truffleruby' + # In Shift_JIS the second byte of a character can be an ASCII byte such + # as "@"; a separator must only match on a character boundary. + content = "\x81\x40?\x81\x40@".b + r = Zlib::GzipReader.new(StringIO.new(Zlib.gzip(content)), external_encoding: "Shift_JIS") + assert_equal(content.dup.force_encoding("Shift_JIS"), r.gets("@")) + assert_nil(r.gets("@")) + r = Zlib::GzipReader.new(StringIO.new(Zlib.gzip(content)), external_encoding: "Shift_JIS") + assert_equal("\x81\x40?".b.force_encoding("Shift_JIS"), r.gets("?")) + assert_equal("\x81\x40@".b.force_encoding("Shift_JIS"), r.gets("?")) + assert_nil(r.gets("?")) + end + def test_corrupted_header gz = Zlib::GzipWriter.new(StringIO.new(s = "".dup)) gz.orig_name = "X"