Skip to content

Commit

Permalink
[GR-59866] Add IO#pread and IO#pwrite methods
Browse files Browse the repository at this point in the history
PullRequest: truffleruby/4405
  • Loading branch information
andrykonchin committed Nov 19, 2024
2 parents 3f96be0 + 2a07541 commit 49a4475
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 31 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Compatibility:
* Add `rb_category_warn` function (#3710, @andrykonchin).
* Add `rb_gc_mark_locations()` (#3704, @andrykonchin).
* Implement `rb_str_format()` (#3716, @andrykonchin).
* Add `IO#{pread, pwrite}` methods (#3718, @andrykonchin).

Performance:

Expand Down
2 changes: 1 addition & 1 deletion lib/cext/include/truffleruby/truffleruby-abi-version.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,6 @@
// $RUBY_VERSION must be the same as TruffleRuby.LANGUAGE_VERSION.
// $ABI_NUMBER starts at 1 and is incremented for every ABI-incompatible change.

#define TRUFFLERUBY_ABI_VERSION "3.3.5.3"
#define TRUFFLERUBY_ABI_VERSION "3.3.5.4"

#endif
6 changes: 6 additions & 0 deletions spec/ruby/core/io/pread_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@
@file.pread(0, 4).should == ""
end

it "returns a buffer for maxlen = 0 when buffer specified" do
buffer = +"foo"
@file.pread(0, 4, buffer).should.equal?(buffer)
buffer.should == "foo"
end

it "ignores the offset for maxlen = 0, even if it is out of file bounds" do
@file.pread(0, 400).should == ""
end
Expand Down
21 changes: 0 additions & 21 deletions spec/tags/core/io/pread_tags.txt

This file was deleted.

9 changes: 0 additions & 9 deletions spec/tags/core/io/pwrite_tags.txt

This file was deleted.

35 changes: 35 additions & 0 deletions src/main/ruby/truffleruby/core/io.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,32 @@ def pos=(offset)
seek Primitive.rb_num2long(offset), SEEK_SET
end

def pread(length, offset, buffer = nil)
ensure_open_and_readable

length = Primitive.rb_to_int(length)
offset = Primitive.rb_to_int(offset)

raise ArgumentError, 'negative string size (or size too big)' if length < 0
raise Errno::EINVAL, 'offset must not be negative' if offset < 0

if length == 0
return buffer ? buffer : +''
end

str, errno = Truffle::POSIX.pread_string(self, length, offset)
Errno.handle_errno(errno) unless errno == 0

raise EOFError if Primitive.nil? str

if buffer
buffer = StringValue(buffer)
buffer.replace str.force_encoding(buffer.encoding)
else
str
end
end

##
# Writes each given argument.to_s to the stream or $_ (the result of last
# IO#gets) if called without arguments. Appends $\.to_s to output. Returns
Expand Down Expand Up @@ -1668,6 +1694,15 @@ def printf(fmt, *args)
end
Truffle::Graal.always_split(instance_method(:printf))

def pwrite(object, offset)
string = Truffle::Type.rb_obj_as_string(object)
offset = Primitive.rb_to_int(offset)

ensure_open_and_writable

Truffle::POSIX.pwrite_string(self, string, offset)
end

def read(length = nil, buffer = nil)
ensure_open_and_readable
buffer = StringValue(buffer) if buffer
Expand Down
50 changes: 50 additions & 0 deletions src/main/ruby/truffleruby/core/posix.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
attach_function :truffleposix_poll_single_fd, [:int, :int, :int], :int, LIBTRUFFLEPOSIX
attach_function :poll, [:pointer, :nfds_t, :int], :int
attach_function :read, [:int, :pointer, :size_t], :ssize_t, LIBC, true
attach_function :pread, [:int, :pointer, :size_t, :off_t], :ssize_t, LIBC, true
attach_function :readlink, [:string, :pointer, :size_t], :ssize_t
attach_function :realpath, [:string, :pointer], :pointer
attach_function :truffleposix_readdir_multiple, [:pointer, :int, :int, :int, :pointer], :int, LIBTRUFFLEPOSIX
Expand All @@ -236,6 +237,7 @@ def self.attach_function_eagerly(native_name, argument_types, return_type,
attach_function :unlink, [:string], :int
attach_function :truffleposix_utimes, [:string, :long, :int, :long, :int], :int, LIBTRUFFLEPOSIX
attach_function :write, [:int, :pointer, :size_t], :ssize_t, LIBC, true
attach_function :pwrite, [:int, :pointer, :size_t, :off_t], :ssize_t, LIBC, true

Truffle::Boot.delay do
if NATIVE
Expand Down Expand Up @@ -485,6 +487,29 @@ def self.read_string_polyglot(io, length)
end
end

def self.pread_string_native(io, length, offset)
fd = io.fileno
buffer = Primitive.io_thread_buffer_allocate(length)

begin
bytes_read = Truffle::POSIX.pread(fd, buffer, length, offset)

if bytes_read < 0 # error
[nil, Errno.errno]
elsif bytes_read == 0 # EOF
[nil, 0]
else
[buffer.read_string(bytes_read), 0]
end
ensure
Primitive.io_thread_buffer_free(buffer)
end
end

def self.pread_string_polyglot(io, length, offset)
raise 'Not implemented' # there is not way to read starting from a specific position
end

# #write_string (either #write_string_native or #write_string_polyglot) is
# called by IO#syswrite, IO#write, and IO::InternalBuffer#empty_to

Expand Down Expand Up @@ -577,22 +602,47 @@ def self.write_string_nonblock_polyglot(io, string)
end
end

def self.pwrite_string_native(io, string, offset)
fd = io.fileno
length = string.bytesize
buffer = Primitive.io_thread_buffer_allocate(length)

begin
buffer.write_bytes string

written = Truffle::POSIX.pwrite(fd, buffer, length, offset)
Errno.handle_errno(Errno.errno) if written < 0

written
ensure
Primitive.io_thread_buffer_free(buffer)
end
end

def self.pwrite_string_polyglot(io, length, offset)
raise 'Not implemented' # there is not way to write starting from a specific position
end

# Select between native and polyglot variants

Truffle::Boot.delay do
if Truffle::Boot.get_option('polyglot-stdio')
class << self
alias_method :read_string, :read_string_polyglot
alias_method :read_to_buffer, :read_to_buffer_polyglot
alias_method :pread_string, :pread_string_polyglot
alias_method :write_string, :write_string_polyglot
alias_method :write_string_nonblock, :write_string_nonblock_polyglot
alias_method :pwrite_string, :pwrite_string_polyglot
end
else
class << self
alias_method :read_string, :read_string_native
alias_method :read_to_buffer, :read_to_buffer_native
alias_method :pread_string, :pread_string_native
alias_method :write_string, :write_string_native
alias_method :write_string_nonblock, :write_string_nonblock_native
alias_method :pwrite_string, :pwrite_string_native
end
end
end
Expand Down

0 comments on commit 49a4475

Please sign in to comment.