Skip to content

Commit

Permalink
[GR-18163] Fix IO#write when passed multiple arguments
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/3625
  • Loading branch information
andrykonchin committed Jan 27, 2023
2 parents 1911a20 + 010cc76 commit 360ec34
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 18 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ Compatibility:
* Record the source location in the constant for the `module`/`class` keywords (#2833, @eregon).
* Fix `File.open` and support `flags` option (#2820, @andrykonchin).
* Support writing to `RData.dfree` for native extensions (#2830, #2732, #2165, @eregon).
* Fix `IO#write` and support multiple arguments with different encodings (#2829, @andrykonchin).

Performance:

Expand Down
47 changes: 43 additions & 4 deletions spec/ruby/core/io/write_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@
-> { @readonly_file.write("") }.should_not raise_error
end

it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end

before :each do
@external = Encoding.default_external
@internal = Encoding.default_internal
Expand All @@ -40,6 +36,18 @@
Encoding.default_internal = @internal
end

it "returns a length of 0 when writing a blank string" do
@file.write('').should == 0
end

it "returns a length of 0 when writing blank strings" do
@file.write('', '', '').should == 0
end

it "returns a length of 0 when passed no arguments" do
@file.write().should == 0
end

it "returns the number of bytes written" do
@file.write("hellø").should == 6
end
Expand All @@ -54,13 +62,32 @@
File.binread(@filename).bytes.should == [159]
end

it "does not modify arguments when passed multiple arguments and external encoding not set" do
a, b = "a".freeze, "b".freeze

File.open(@filename, "w") do |f|
f.write(a, b)
end

File.binread(@filename).bytes.should == [97, 98]
a.encoding.should == Encoding::UTF_8
b.encoding.should == Encoding::UTF_8
end

it "uses the encoding from the given option for non-ascii encoding" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("hi").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end

it "uses the encoding from the given option for non-ascii encoding when multiple arguments passes" do
File.open(@filename, "w", encoding: Encoding::UTF_32LE) do |file|
file.write("h", "i").should == 8
end
File.binread(@filename).should == "h\u0000\u0000\u0000i\u0000\u0000\u0000"
end

it "raises a invalid byte sequence error if invalid bytes are being written" do
# pack "\xFEhi" to avoid utf-8 conflict
xFEhi = ([254].pack('C*') + 'hi').force_encoding('utf-8')
Expand All @@ -78,6 +105,18 @@
res = "H#{ë}ll#{ö}"
File.binread(@filename).should == res.force_encoding(Encoding::BINARY)
end

it "writes binary data if no encoding is given and multiple arguments passed" do
File.open(@filename, "w") do |file|
file.write("\x87".b, "ą") # 0x87 isn't a valid UTF-8 binary representation of a character
end
File.binread(@filename).bytes.should == [0x87, 0xC4, 0x85]

File.open(@filename, "w") do |file|
file.write("\x61".encode("utf-32le"), "ą")
end
File.binread(@filename).bytes.should == [0x61, 0x00, 0x00, 0x00, 0xC4, 0x85]
end
end

describe "IO.write" do
Expand Down
29 changes: 15 additions & 14 deletions src/main/ruby/truffleruby/core/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2310,25 +2310,26 @@ def ungetc(obj)
nil
end

def write(*data)
data = if data.size > 1
data.map { |d| Truffle::Type.rb_obj_as_string(d) }.join
else
Truffle::Type.rb_obj_as_string(data[0])
end
return 0 if data.empty?
def write(*objects)
bytes_written = 0

ensure_open_and_writable
objects.each do |object|
string = Truffle::Type.rb_obj_as_string(object)
next if string.empty?

ensure_open_and_writable

if !binmode? && external_encoding &&
external_encoding != data.encoding &&
external_encoding != Encoding::BINARY
unless data.ascii_only? && external_encoding.ascii_compatible?
data = data.encode(external_encoding)
if !binmode? && external_encoding && external_encoding != string.encoding && external_encoding != Encoding::BINARY
unless string.ascii_only? && external_encoding.ascii_compatible?
string = string.encode(external_encoding)
end
end

count = Truffle::POSIX.write_string self, string, true
bytes_written += count
end

Truffle::POSIX.write_string self, data, true
bytes_written
end

def write_nonblock(data, exception: true)
Expand Down

0 comments on commit 360ec34

Please sign in to comment.