Skip to content

Commit 6d8b27e

Browse files
committed
Implement StringIO#pread
Both for being closer to real IOs and also because it's a convenient API in multithreaded scenarios.
1 parent 1587d36 commit 6d8b27e

File tree

3 files changed

+124
-0
lines changed

3 files changed

+124
-0
lines changed

ext/java/org/jruby/ext/stringio/StringIO.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,68 @@ public IRubyObject read(ThreadContext context, IRubyObject[] args) {
907907
return string;
908908
}
909909

910+
@JRubyMethod(name = "pread", required = 2, optional = 1)
911+
public IRubyObject pread(ThreadContext context, IRubyObject[] args) {
912+
checkReadable();
913+
914+
final Ruby runtime = context.runtime;
915+
IRubyObject str = context.nil;
916+
int len;
917+
int offset;
918+
919+
StringIOData ptr = this.ptr;
920+
final RubyString string;
921+
922+
switch (args.length) {
923+
case 3:
924+
str = args[2];
925+
if (!str.isNil()) {
926+
str = str.convertToString();
927+
((RubyString) str).modify();
928+
}
929+
case 2:
930+
len = RubyNumeric.fix2int(args[0]);
931+
offset = RubyNumeric.fix2int(args[1]);
932+
if (!args[0].isNil()) {
933+
len = RubyNumeric.fix2int(args[0]);
934+
935+
if (len < 0) {
936+
throw runtime.newArgumentError("negative length " + len + " given");
937+
}
938+
939+
if (offset < 0) {
940+
throw runtime.newErrnoEINVALError("pread: Invalid offset argument");
941+
}
942+
}
943+
break;
944+
default:
945+
throw runtime.newArgumentError(args.length, 0, 2);
946+
}
947+
948+
synchronized (ptr) {
949+
if (offset >= ptr.string.size()) {
950+
throw context.runtime.newEOFError();
951+
}
952+
953+
if (str.isNil()) {
954+
return strioSubstr(runtime, offset, len, ASCIIEncoding.INSTANCE);
955+
}
956+
957+
string = (RubyString) str;
958+
int rest = ptr.string.size() - offset;
959+
if (len > rest) len = rest;
960+
string.resize(len);
961+
ByteList strByteList = string.getByteList();
962+
byte[] strBytes = strByteList.getUnsafeBytes();
963+
ByteList dataByteList = ptr.string.getByteList();
964+
byte[] dataBytes = dataByteList.getUnsafeBytes();
965+
System.arraycopy(dataBytes, dataByteList.getBegin() + offset, strBytes, strByteList.getBegin(), len);
966+
string.setEncoding(ASCIIEncoding.INSTANCE);
967+
}
968+
969+
return string;
970+
}
971+
910972
@JRubyMethod(name = "readlines")
911973
public IRubyObject readlines(ThreadContext context) {
912974
return Getline.getlineCall(context, GETLINE_ARY, this, getEncoding());

ext/stringio/stringio.c

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1583,6 +1583,48 @@ strio_read(int argc, VALUE *argv, VALUE self)
15831583
return str;
15841584
}
15851585

1586+
/*
1587+
* call-seq:
1588+
* pread(maxlen, offset) -> string
1589+
* pread(maxlen, offset, out_string) -> string
1590+
*
1591+
* See IO#pread.
1592+
*/
1593+
static VALUE
1594+
strio_pread(int argc, VALUE *argv, VALUE self)
1595+
{
1596+
VALUE rb_len, rb_offset, rb_buf;
1597+
rb_scan_args(argc, argv, "21", &rb_len, &rb_offset, &rb_buf);
1598+
long len = NUM2LONG(rb_len);
1599+
long offset = NUM2LONG(rb_offset);
1600+
1601+
if (len < 0) {
1602+
rb_raise(rb_eArgError, "negative string size (or size too big): %ld", len);
1603+
}
1604+
1605+
if (offset < 0) {
1606+
rb_exc_raise(rb_syserr_new_str(EINVAL, rb_sprintf("pread: Invalid offset argument: %ld", offset)));
1607+
}
1608+
1609+
struct StringIO *ptr = readable(self);
1610+
1611+
if (offset >= RSTRING_LEN(ptr->string)) {
1612+
rb_eof_error();
1613+
}
1614+
1615+
if (NIL_P(rb_buf)) {
1616+
return strio_substr(ptr, offset, len, rb_ascii8bit_encoding());
1617+
}
1618+
1619+
long rest = RSTRING_LEN(ptr->string) - offset;
1620+
if (len > rest) len = rest;
1621+
rb_str_resize(rb_buf, len);
1622+
rb_enc_associate(rb_buf, rb_ascii8bit_encoding());
1623+
MEMCPY(RSTRING_PTR(rb_buf), RSTRING_PTR(ptr->string) + offset, char, len);
1624+
return rb_buf;
1625+
}
1626+
1627+
15861628
/*
15871629
* call-seq:
15881630
* strio.sysread(integer[, outbuf]) -> string
@@ -1843,6 +1885,7 @@ Init_stringio(void)
18431885
rb_define_method(StringIO, "gets", strio_gets, -1);
18441886
rb_define_method(StringIO, "readlines", strio_readlines, -1);
18451887
rb_define_method(StringIO, "read", strio_read, -1);
1888+
rb_define_method(StringIO, "pread", strio_pread, -1);
18461889

18471890
rb_define_method(StringIO, "write", strio_write_m, -1);
18481891
rb_define_method(StringIO, "putc", strio_putc, 1);

test/stringio/test_stringio.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,25 @@ def test_sysread
729729
assert_equal Encoding::ASCII_8BIT, f.sysread(3).encoding
730730
end
731731

732+
def test_pread
733+
f = StringIO.new("pread")
734+
f.read
735+
736+
assert_equal "pre".b, f.pread(3, 0)
737+
assert_equal "read".b, f.pread(4, 1)
738+
assert_equal Encoding::ASCII_8BIT, f.pread(4, 1).encoding
739+
740+
buf = "".b
741+
f.pread(3, 0, buf)
742+
assert_equal "pre".b, buf
743+
f.pread(4, 1, buf)
744+
assert_equal "read".b, buf
745+
746+
assert_raise(EOFError) { f.pread(1, 5) }
747+
assert_raise(ArgumentError) { f.pread(-1, 0) }
748+
assert_raise(Errno::EINVAL) { f.pread(3, -1) }
749+
end
750+
732751
def test_size
733752
f = StringIO.new("1234")
734753
assert_equal(4, f.size)

0 commit comments

Comments
 (0)