Skip to content

Commit bae37e4

Browse files
committed
Fix IO#read_nonblock, IO#readpartial, IO#sysread and preserve buffer's encoding
1 parent 9e28eca commit bae37e4

File tree

10 files changed

+51
-15
lines changed

10 files changed

+51
-15
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Bug fixes:
99
* Fix `IO#{wait,wait_readable,wait_writable}` methods and switch the current thread into a sleep state (@andrykonchin).
1010
* Fix `rb_global_variable()` for `Float` and bignum values during the `Init_` function (#3478, @eregon).
1111
* Fix parsing literal floats when the locale does not use `.` for the decimal separator (e.g. `LANG=fr_FR.UTF-8`) (#3512, @eregon).
12+
* Fix `IO#{read_nonblock,readpartial,sysread}` and preserve a provided buffer's encoding (#3506, @andrykonchyn).
1213

1314
Compatibility:
1415
* Move `IO#wait_readable`, `IO#wait_writable`, `IO#wait_priority` and `IO#wait` into core library (@andrykonchin).

spec/ruby/core/io/pread_spec.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222

2323
it "accepts a length, an offset, and an output buffer" do
2424
buffer = +"foo"
25-
@file.pread(3, 4, buffer)
25+
@file.pread(3, 4, buffer).should.equal?(buffer)
2626
buffer.should == "567"
2727
end
2828

@@ -38,6 +38,13 @@
3838
buffer.should == "12345"
3939
end
4040

41+
it "preserves the encoding of the given buffer" do
42+
buffer = ''.encode(Encoding::ISO_8859_1)
43+
@file.pread(10, 0, buffer)
44+
45+
buffer.encoding.should == Encoding::ISO_8859_1
46+
end
47+
4148
it "does not advance the file pointer" do
4249
@file.pread(4, 0).should == "1234"
4350
@file.read.should == "1234567890"

spec/ruby/core/io/read_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,21 @@
376376
buf.should == @contents[0..4]
377377
end
378378

379+
it "preserves the encoding of the given buffer" do
380+
buffer = ''.encode(Encoding::ISO_8859_1)
381+
@io.read(10, buffer)
382+
383+
buffer.encoding.should == Encoding::ISO_8859_1
384+
end
385+
386+
# https://bugs.ruby-lang.org/issues/20416
387+
it "does not preserve the encoding of the given buffer when max length is not provided" do
388+
buffer = ''.encode(Encoding::ISO_8859_1)
389+
@io.read(nil, buffer)
390+
391+
buffer.encoding.should_not == Encoding::ISO_8859_1
392+
end
393+
379394
it "returns the given buffer" do
380395
buf = +""
381396

spec/ruby/core/io/readpartial_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@
6262
buffer = +"existing content"
6363
@wr.write("hello world")
6464
@wr.close
65-
@rd.readpartial(11, buffer)
65+
@rd.readpartial(11, buffer).should.equal?(buffer)
6666
buffer.should == "hello world"
6767
end
6868

@@ -106,6 +106,7 @@
106106
@wr.write("abc")
107107
@wr.close
108108
@rd.readpartial(10, buffer)
109+
109110
buffer.encoding.should == Encoding::ISO_8859_1
110111
end
111112
end

spec/ruby/core/io/sysread_spec.rb

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@
9797

9898
it "discards the existing buffer content upon successful read" do
9999
buffer = +"existing content"
100-
@file.sysread(11, buffer)
100+
@file.sysread(11, buffer).should.equal?(buffer)
101101
buffer.should == "01234567890"
102102
end
103103

@@ -107,6 +107,13 @@
107107
-> { @file.sysread(1, buffer) }.should raise_error(EOFError)
108108
buffer.should be_empty
109109
end
110+
111+
it "preserves the encoding of the given buffer" do
112+
buffer = ''.encode(Encoding::ISO_8859_1)
113+
string = @file.sysread(10, buffer)
114+
115+
buffer.encoding.should == Encoding::ISO_8859_1
116+
end
110117
end
111118

112119
describe "IO#sysread" do

spec/tags/core/io/pread_tags.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,3 +18,4 @@ fails:IO#pread raises TypeError if offset is not an Integer and cannot be coerce
1818
fails:IO#pread raises ArgumentError for negative values of maxlen
1919
fails:IO#pread raised Errno::EINVAL for negative values of offset
2020
fails:IO#pread raises TypeError if the buffer is not a String and cannot be coerced into String
21+
fails:IO#pread preserves the encoding of the given buffer
Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
fails:IO#read_nonblock raises an exception after ungetc with data in the buffer and character conversion enabled
22
fails:IO#read_nonblock discards the existing buffer content upon error
3-
fails:IO#read_nonblock preserves the encoding of the given buffer

spec/tags/core/io/readpartial_tags.txt

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,2 @@
11
fails:BasicSocket#read_nonblock using IPv4 does not set the IO in nonblock mode
22
fails:BasicSocket#read_nonblock using IPv6 does not set the IO in nonblock mode
3-
fails:BasicSocket#read_nonblock using IPv4 replaces the content of the provided buffer without changing its encoding
4-
fails:BasicSocket#read_nonblock using IPv6 replaces the content of the provided buffer without changing its encoding

src/main/ruby/truffleruby/core/io.rb

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1673,6 +1673,8 @@ def read(length = nil, buffer = nil)
16731673
str = IO.read_encode self, read_all
16741674
return str unless buffer
16751675

1676+
# intentionally don't preserve buffer encoding
1677+
# see https://bugs.ruby-lang.org/issues/20416
16761678
return buffer.replace(str)
16771679
end
16781680

@@ -1753,10 +1755,14 @@ def read_nonblock(size, buffer = nil, exception: true)
17531755
str = Truffle::POSIX.read_string_nonblock(self, size, exception)
17541756

17551757
case str
1756-
when Symbol
1758+
when Symbol # :wait_readable in case of EAGAIN_ERRNO
17571759
str
17581760
when String
1759-
buffer ? buffer.replace(str) : str
1761+
if buffer
1762+
buffer.replace str.force_encoding(buffer.encoding)
1763+
else
1764+
str
1765+
end
17601766
else # EOF
17611767
if exception
17621768
raise EOFError, 'end of file reached'
@@ -1871,8 +1877,7 @@ def readpartial(size, buffer = nil)
18711877
raise EOFError if Primitive.nil? data
18721878
end
18731879

1874-
buffer.replace(data)
1875-
buffer
1880+
buffer.replace data.force_encoding(buffer.encoding)
18761881
else
18771882
return +'' if size == 0
18781883

@@ -2196,20 +2201,23 @@ def sync=(v)
21962201
#
21972202
# @todo Improve reading into provided buffer.
21982203
#
2199-
def sysread(number_of_bytes, buffer = undefined)
2204+
def sysread(number_of_bytes, buffer = nil)
22002205
ensure_open_and_readable
22012206
flush
22022207
raise IOError unless @ibuffer.empty?
22032208

2209+
buffer = StringValue(buffer) if buffer
2210+
22042211
str, errno = Truffle::POSIX.read_string(self, number_of_bytes)
22052212
Errno.handle_errno(errno) unless errno == 0
22062213

22072214
raise EOFError if Primitive.nil? str
22082215

2209-
unless Primitive.undefined? buffer
2210-
StringValue(buffer).replace str
2216+
if buffer
2217+
buffer.replace str.force_encoding(buffer.encoding)
2218+
else
2219+
str
22112220
end
2212-
str
22132221
end
22142222

22152223
##

0 commit comments

Comments
 (0)