Permalink
Browse files

Merge pull request #466 from atambo/copy_stream_to_path

Bunch of fixes for IO.copy_stream

I will address Wayne's concerns after I merge this.
  • Loading branch information...
2 parents 8e20824 + 639473c commit 5fae6858b1aadfcfcad0c33e7b40b9fd6d331edc @enebo enebo committed Jan 21, 2013
Showing with 34 additions and 54 deletions.
  1. +0 −30 spec/tags/1.9/ruby/core/io/copy_stream_tags.txt
  2. +34 −24 src/org/jruby/RubyIO.java
@@ -1,32 +1,2 @@
-fails:IO.copy_stream from an IO raises an IOError if the source IO is not open for reading
-fails:IO.copy_stream from an IO does not close the source IO
-fails:IO.copy_stream from an IO does not change the IO offset when an offset is specified
-fails:IO.copy_stream from an IO does change the IO offset when an offset is not specified
-fails:IO.copy_stream from an IO to a file name copies the entire IO contents to the file
-fails:IO.copy_stream from an IO to a file name returns the number of bytes copied
-fails:IO.copy_stream from an IO to a file name copies only length bytes when specified
-fails:IO.copy_stream from an IO to a file name copies only length bytes from the offset
-fails:IO.copy_stream from an IO to a file name calls #to_path to convert on object to a file name
-fails:IO.copy_stream from an IO to a file name raises a TypeError if #to_path does not return a String
-fails:IO.copy_stream from an IO to an IO copies the entire IO contents to the IO
-fails:IO.copy_stream from an IO to an IO returns the number of bytes copied
fails:IO.copy_stream from an IO to an IO starts writing at the destination IO's current position
-fails:IO.copy_stream from an IO to an IO leaves the destination IO position at the last write
-fails:IO.copy_stream from an IO to an IO raises an IOError if the destination IO is not open for writing
-fails:IO.copy_stream from an IO to an IO does not close the destination IO
-fails:IO.copy_stream from an IO to an IO copies only length bytes when specified
-fails:IO.copy_stream from an IO to an IO copies only length bytes from the offset
-fails:IO.copy_stream from a file name calls #to_path to convert on object to a file name
-fails:IO.copy_stream from a file name raises a TypeError if #to_path does not return a String
-fails:IO.copy_stream from a file name to a file name copies only length bytes when specified
-fails:IO.copy_stream from a file name to a file name copies only length bytes from the offset
-fails:IO.copy_stream from a file name to a file name calls #to_path to convert on object to a file name
-fails:IO.copy_stream from a file name to a file name raises a TypeError if #to_path does not return a String
-fails:IO.copy_stream from a file name to an IO copies the entire IO contents to the IO
-fails:IO.copy_stream from a file name to an IO returns the number of bytes copied
fails:IO.copy_stream from a file name to an IO starts writing at the destination IO's current position
-fails:IO.copy_stream from a file name to an IO leaves the destination IO position at the last write
-fails:IO.copy_stream from a file name to an IO raises an IOError if the destination IO is not open for writing
-fails:IO.copy_stream from a file name to an IO does not close the destination IO
-fails:IO.copy_stream from a file name to an IO copies only length bytes when specified
-fails:IO.copy_stream from a file name to an IO copies only length bytes from the offset
View
@@ -4136,37 +4136,56 @@ public static IRubyObject pipe19(ThreadContext context, IRubyObject recv, IRubyO
return pipe19(context, recv, modes);
}
- @JRubyMethod(name = "copy_stream", meta = true, compat = RUBY1_9)
+ @JRubyMethod(name = "copy_stream", required = 2, optional = 2, meta = true, compat = RUBY1_9)
public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv,
- IRubyObject arg1, IRubyObject arg2) {
+ IRubyObject[] args) {
Ruby runtime = context.runtime;
+ IRubyObject arg1 = args[0];
+ IRubyObject arg2 = args[1];
+
+ RubyInteger length = null;
+ RubyInteger offset = null;
+
RubyIO io1 = null;
RubyIO io2 = null;
+ if (args.length >= 3) {
+ length = args[2].convertToInteger();
+ if (args.length == 4) {
+ offset = args[3].convertToInteger();
+ }
+ }
+
try {
if (arg1 instanceof RubyString) {
io1 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[] {arg1}, Block.NULL_BLOCK);
} else if (arg1 instanceof RubyIO) {
io1 = (RubyIO) arg1;
+ } else if (arg1.respondsTo("to_path")) {
+ RubyString path = (RubyString) TypeConverter.convertToType19(arg1, runtime.getString(), "to_path");
+ io1 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[] {path}, Block.NULL_BLOCK);
} else {
- throw runtime.newTypeError("Should be String or IO");
+ throw runtime.newArgumentError("Should be String or IO");
}
if (arg2 instanceof RubyString) {
io2 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[] {arg2, runtime.newString("w")}, Block.NULL_BLOCK);
} else if (arg2 instanceof RubyIO) {
io2 = (RubyIO) arg2;
+ } else if (arg2.respondsTo("to_path")) {
+ RubyString path = (RubyString) TypeConverter.convertToType19(arg2, runtime.getString(), "to_path");
+ io2 = (RubyIO) RubyFile.open(context, runtime.getFile(), new IRubyObject[] {path, runtime.newString("w")}, Block.NULL_BLOCK);
} else {
- throw runtime.newTypeError("Should be String or IO");
+ throw runtime.newArgumentError("Should be String or IO");
}
+ if (!io1.openFile.isReadable()) throw runtime.newIOError("from IO is not readable");
+ if (!io2.openFile.isWritable()) throw runtime.newIOError("to IO is not writable");
+
ChannelDescriptor d1 = io1.openFile.getMainStreamSafe().getDescriptor();
ChannelDescriptor d2 = io2.openFile.getMainStreamSafe().getDescriptor();
- if (!d1.isReadable()) throw runtime.newIOError("from IO is not readable");
- if (!d2.isWritable()) throw runtime.newIOError("from IO is not writable");
-
try {
long size = 0;
if (!d1.isSeekable()) {
@@ -4182,7 +4201,7 @@ public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv,
FileChannel from = (FileChannel)d1.getChannel();
WritableByteChannel to = (WritableByteChannel)d2.getChannel();
- size = transfer(from, to);
+ size = transfer(from, to, length, offset);
}
return context.runtime.newFixnum(size);
@@ -4191,16 +4210,6 @@ public static IRubyObject copy_stream(ThreadContext context, IRubyObject recv,
}
} catch (BadDescriptorException e) {
throw runtime.newErrnoEBADFError();
- } finally {
- try {
- if (io1 != null) {
- io1.close();
- }
- } finally {
- if (io2 != null) {
- io2.close();
- }
- }
}
}
@@ -4214,14 +4223,11 @@ private static long transfer(ReadableByteChannel from, FileChannel to) throws IO
return transferred;
}
- private static long transfer(FileChannel from, WritableByteChannel to) throws IOException {
-
-
+ private static long transfer(FileChannel from, WritableByteChannel to, RubyInteger length, RubyInteger offset) throws IOException {
// handle large files on 32-bit JVMs
long chunkSize = 128 * 1024 * 1024;
- long size = from.size();
- long remaining = size;
- long position = from.position();
+ long remaining = length == null ? from.size() : length.getLongValue();
+ long position = offset == null? from.position() : offset.getLongValue();
long transferred = 0;
while (remaining > 0) {
@@ -4235,6 +4241,10 @@ private static long transfer(FileChannel from, WritableByteChannel to) throws IO
remaining -= n;
transferred += n;
}
+
+ if (offset == null) {
+ from.position(from.position() + transferred);
+ }
return transferred;
}

0 comments on commit 5fae685

Please sign in to comment.