Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO#pread and pwrite missing #5816

Open
headius opened this issue Aug 6, 2019 · 7 comments

Comments

@headius
Copy link
Member

commented Aug 6, 2019

This is yanked out of #5118

Ruby 2.5 added pread and pwrite, which allow reading and writing without advancing the file position. We have never added these. May be easy, I have not looked into it.

@headius headius referenced this issue Aug 6, 2019
3 of 12 tasks complete

@headius headius added the help wanted label Aug 6, 2019

@FuncGuy

This comment has been minimized.

Copy link
Contributor

commented Aug 7, 2019

something like this

File.write("testfile", "This is line one\nThis is line two\n")
File.open("testfile") do |f|
p f.read # => "This is line one\nThis is line two\n"
p f.pread(12, 0) # => "This is line"
p f.pread(9, 8) # => "line one\n"

using RandomAccessFile's seek() we can achieve this.. @headius could you tell me in which class i have to make changes as i'm new to this.

@headius

This comment has been minimized.

Copy link
Member Author

commented Aug 12, 2019

@FuncGuy I would worry about atomicity with manually seeking. This might be something we can only support on actual native streams, since I don't think there's a way in NIO to read without advancing the pointer.

@headius

This comment has been minimized.

Copy link
Member Author

commented Aug 12, 2019

Ok I stand corrected! It appears that FileChannel#read and #write when given an absolute position will read from or write to that position without changing the position of the channel itself:

https://docs.oracle.com/javase/8/docs/api/java/nio/channels/FileChannel.html#write-java.nio.ByteBuffer-long-

And I have confirmed that this logic is actually using pread/pwrite under the covers:

https://github.com/JetBrains/jdk8u_jdk/blob/master/src/solaris/classes/sun/nio/ch/FileDispatcherImpl.java#L49-L53

So if we can have either a FileChannel or a NativeDeviceChannel, we should be able to support this!

(A first pass that gets FileChannel working would be a good start; we usually don't use the native logic for files).

@FuncGuy

This comment has been minimized.

Copy link
Contributor

commented Aug 14, 2019

using FileChannel read goes like this :
Path path = Paths.get("file.txt");
FileChannel fileChannel = FileChannel.open(path);
ByteBuffer buffer = ByteBuffer.allocate(1000);
fileChannel.read(buffer,1); //1 is position

And write:
RandomAccessFile stream = new RandomAccessFile("file.txt", "rw");
FileChannel channel = stream.getChannel();
String text = "sample text";
byte[] strBytes = text.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(strBytes.length);
buffer.put(strBytes);
channel.write(buffer,10); // 10 is position

@headius i hope this is what we need.

@headius

This comment has been minimized.

Copy link
Member Author

commented Aug 19, 2019

@FuncGuy Yeah that looks like the right logic! We would need to rig this up in RubyIO.pread/pwrite by grabbing the FileChannel from getOpenFile().fd().chFile. If that's not null, we can use the above logic. Otherwise we would just fall back on a normal read or write (since non-files don't seek).

@FuncGuy

This comment has been minimized.

Copy link
Contributor

commented Aug 21, 2019

@headius i've implemented pread and pwrite like this:

@JRubyMethod(name = "pread",required = 1)
public String pread(int offset) throws IOException {
FileChannel fileChannel = getOpenFile().fd().chFile;
ByteBuffer buffer = ByteBuffer.allocate(1000);
fileChannel.read(buffer,offset);
byte actual[] = trimNullAtEnd(buffer.array());
return new String(actual);
}

@JRubyMethod(name = "pwrite",required = 2)
public void pwrite(String content,int offset) throws IOException {
    FileChannel fileChannel = getOpenFile().fd().chFile;
    byte[] bytes = content.getBytes();
    ByteBuffer buffer = ByteBuffer.allocate(bytes.length);
    buffer.put(bytes);
    fileChannel.write(buffer,offset);
}

private byte[] trimNullAtEnd(byte[] bytes) {
    return new String(bytes).replaceAll("\0", "").getBytes();
}

but not sure what to return from pread i tried to return a java string but it is failed.. need help here..

@headius

This comment has been minimized.

Copy link
Member Author

commented Aug 21, 2019

@FuncGuy Ok so these methods will want to use Ruby types. You might look at the implementations of "sysread" and "syswrite" in RubyIO. They'll accept at least ThreadContext (per-thread Ruby state) and an IRubyObject for the size or buffer to write.

I can take over from what you have any time, but take a look at sysread/syswrite and how they take in arguments and produce a Ruby object return value.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
2 participants
You can’t perform that action at this time.