Skip to content

Commit 4c6d524

Browse files
committed
Implement IO#pread and IO#pwrite
It is available by default in environments where `__unix__` is defined. Other environments are enabled by defining `MRB_WITH_IO_PREAD_PWRITE` (requires an implementation of `pread()` and `pwrite()` functions). In any case, you can disable it by defining `MRB_WITHOUT_IO_PREAD_PWRITE`.
1 parent 3c67d9b commit 4c6d524

File tree

4 files changed

+150
-8
lines changed

4 files changed

+150
-8
lines changed

mrbgems/mruby-io/include/mruby/ext/io.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,14 @@
99
extern "C" {
1010
#endif
1111

12+
#if defined(MRB_WITHOUT_IO_PREAD_PWRITE)
13+
# undef MRB_WITH_IO_PREAD_PWRITE
14+
#elif !defined(MRB_WITH_IO_PREAD_PWRITE)
15+
# if defined(__unix__)
16+
# define MRB_WITH_IO_PREAD_PWRITE
17+
# endif
18+
#endif
19+
1220
struct mrb_io {
1321
int fd; /* file descriptor, or -1 */
1422
int fd2; /* file descriptor to write if it's different from fd, or -1 */

mrbgems/mruby-io/src/io.c

Lines changed: 105 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
typedef long ftime_t;
3232
typedef long fsuseconds_t;
3333
typedef int fmode_t;
34+
typedef int mrb_io_read_write_size;
3435

3536
#ifndef O_TMPFILE
3637
#define O_TMPFILE O_TEMPORARY
@@ -43,6 +44,7 @@
4344
typedef time_t ftime_t;
4445
typedef suseconds_t fsuseconds_t;
4546
typedef mode_t fmode_t;
47+
typedef ssize_t mrb_io_read_write_size;
4648
#endif
4749

4850
#ifdef _MSC_VER
@@ -845,15 +847,35 @@ mrb_io_s_sysopen(mrb_state *mrb, mrb_value klass)
845847
return mrb_fixnum_value(fd);
846848
}
847849

850+
static mrb_value mrb_io_sysread_common(mrb_state *mrb,
851+
mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
852+
mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset);
853+
854+
static mrb_io_read_write_size
855+
mrb_sysread_dummy(int fd, void *buf, fsize_t nbytes, off_t offset)
856+
{
857+
return (mrb_io_read_write_size)read(fd, buf, nbytes);
858+
}
859+
848860
mrb_value
849861
mrb_io_sysread(mrb_state *mrb, mrb_value io)
850862
{
851-
struct mrb_io *fptr;
852863
mrb_value buf = mrb_nil_value();
853864
mrb_int maxlen;
854-
int ret;
855865

856866
mrb_get_args(mrb, "i|S", &maxlen, &buf);
867+
868+
return mrb_io_sysread_common(mrb, mrb_sysread_dummy, io, buf, maxlen, 0);
869+
}
870+
871+
static mrb_value
872+
mrb_io_sysread_common(mrb_state *mrb,
873+
mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
874+
mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
875+
{
876+
struct mrb_io *fptr;
877+
int ret;
878+
857879
if (maxlen < 0) {
858880
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
859881
}
@@ -875,7 +897,7 @@ mrb_io_sysread(mrb_state *mrb, mrb_value io)
875897
if (!fptr->readable) {
876898
mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
877899
}
878-
ret = read(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen);
900+
ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
879901
switch (ret) {
880902
case 0: /* EOF */
881903
if (maxlen == 0) {
@@ -925,27 +947,44 @@ mrb_io_sysseek(mrb_state *mrb, mrb_value io)
925947
}
926948
}
927949

950+
static mrb_value mrb_io_syswrite_common(mrb_state *mrb,
951+
mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
952+
mrb_value io, mrb_value buf, off_t offset);
953+
954+
static mrb_io_read_write_size
955+
mrb_syswrite_dummy(int fd, const void *buf, fsize_t nbytes, off_t offset)
956+
{
957+
return (mrb_io_read_write_size)write(fd, buf, nbytes);
958+
}
959+
928960
mrb_value
929961
mrb_io_syswrite(mrb_state *mrb, mrb_value io)
962+
{
963+
mrb_value buf;
964+
965+
mrb_get_args(mrb, "S", &buf);
966+
967+
return mrb_io_syswrite_common(mrb, mrb_syswrite_dummy, io, buf, 0);
968+
}
969+
970+
static mrb_value mrb_io_syswrite_common(mrb_state *mrb,
971+
mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
972+
mrb_value io, mrb_value buf, off_t offset)
930973
{
931974
struct mrb_io *fptr;
932-
mrb_value str, buf;
933975
int fd, length;
934976

935977
fptr = io_get_open_fptr(mrb, io);
936978
if (! fptr->writable) {
937979
mrb_raise(mrb, E_IO_ERROR, "not opened for writing");
938980
}
939981

940-
mrb_get_args(mrb, "S", &str);
941-
buf = str;
942-
943982
if (fptr->fd2 == -1) {
944983
fd = fptr->fd;
945984
} else {
946985
fd = fptr->fd2;
947986
}
948-
length = write(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf));
987+
length = writefunc(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf), offset);
949988
if (length == -1) {
950989
mrb_sys_fail(mrb, 0);
951990
}
@@ -1328,6 +1367,62 @@ mrb_io_sync(mrb_state *mrb, mrb_value self)
13281367
return mrb_bool_value(fptr->sync);
13291368
}
13301369

1370+
#ifndef MRB_WITH_IO_PREAD_PWRITE
1371+
# define mrb_io_pread mrb_notimplement_m
1372+
# define mrb_io_pwrite mrb_notimplement_m
1373+
#else
1374+
static off_t
1375+
value2off(mrb_state *mrb, mrb_value offv)
1376+
{
1377+
switch (mrb_type(offv)) {
1378+
#ifndef MRB_WITHOUT_FLOAT
1379+
case MRB_TT_FLOAT:
1380+
{
1381+
mrb_float tmp = mrb_float(offv);
1382+
if (tmp < INT64_MIN || tmp > INT64_MAX) {
1383+
/* fall through to use convert by `mrb_int()` (and raise error if out of range) */
1384+
} else {
1385+
return (off_t)tmp;
1386+
}
1387+
}
1388+
/* fall through */
1389+
#endif /* MRB_WITHOUT_FLOAT */
1390+
default:
1391+
return (off_t)mrb_int(mrb, offv);
1392+
}
1393+
}
1394+
1395+
/*
1396+
* call-seq:
1397+
* pread(maxlen, offset, outbuf = "") -> outbuf
1398+
*/
1399+
static mrb_value
1400+
mrb_io_pread(mrb_state *mrb, mrb_value io)
1401+
{
1402+
mrb_value buf = mrb_nil_value();
1403+
mrb_value off;
1404+
mrb_int maxlen;
1405+
1406+
mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);
1407+
1408+
return mrb_io_sysread_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
1409+
}
1410+
1411+
/*
1412+
* call-seq:
1413+
* pwrite(buffer, offset) -> wrote_bytes
1414+
*/
1415+
static mrb_value
1416+
mrb_io_pwrite(mrb_state *mrb, mrb_value io)
1417+
{
1418+
mrb_value buf, off;
1419+
1420+
mrb_get_args(mrb, "So", &buf, &off);
1421+
1422+
return mrb_io_syswrite_common(mrb, pwrite, io, buf, value2off(mrb, off));
1423+
}
1424+
#endif /* MRB_WITH_IO_PREAD_PWRITE */
1425+
13311426
static mrb_value
13321427
io_bufread(mrb_state *mrb, mrb_value self)
13331428
{
@@ -1383,6 +1478,8 @@ mrb_init_io(mrb_state *mrb)
13831478
mrb_define_method(mrb, io, "closed?", mrb_io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
13841479
mrb_define_method(mrb, io, "pid", mrb_io_pid, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
13851480
mrb_define_method(mrb, io, "fileno", mrb_io_fileno_m, MRB_ARGS_NONE());
1481+
mrb_define_method(mrb, io, "pread", mrb_io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */
1482+
mrb_define_method(mrb, io, "pwrite", mrb_io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */
13861483

13871484
mrb_define_class_method(mrb, io, "_bufread", io_bufread, MRB_ARGS_REQ(2));
13881485
}

mrbgems/mruby-io/test/io.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -564,6 +564,34 @@ def io._buf
564564
end
565565
end
566566

567+
assert('IO#pread') do
568+
skip "IO#pread is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
569+
570+
IO.open(IO.sysopen($mrbtest_io_rfname, 'r'), 'r') do |io|
571+
assert_equal $mrbtest_io_msg.byteslice(5, 8), io.pread(8, 5)
572+
assert_equal 0, io.pos
573+
assert_equal $mrbtest_io_msg.byteslice(1, 5), io.pread(5, 1)
574+
assert_equal 0, io.pos
575+
assert_raise(RuntimeError) { io.pread(20, -9) }
576+
end
577+
end
578+
579+
assert('IO#pwrite') do
580+
skip "IO#pwrite is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE
581+
582+
IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') do |io|
583+
assert_equal 6, io.pwrite("Warld!", 7)
584+
assert_equal 0, io.pos
585+
assert_equal 7, io.pwrite("Hello, ", 0)
586+
assert_equal 0, io.pos
587+
assert_equal "Hello, Warld!", io.read
588+
assert_equal 6, io.pwrite("world!", 7)
589+
assert_equal 13, io.pos
590+
io.pos = 0
591+
assert_equal "Hello, world!", io.read
592+
end
593+
end
594+
567595
assert('IO.pipe') do
568596
begin
569597
called = false

mrbgems/mruby-io/test/mruby_io_test.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ mkdtemp(char *temp)
6464
#include "mruby/error.h"
6565
#include "mruby/string.h"
6666
#include "mruby/variable.h"
67+
#include <mruby/ext/io.h>
6768

6869
static mrb_value
6970
mrb_io_test_io_setup(mrb_state *mrb, mrb_value self)
@@ -219,6 +220,12 @@ mrb_io_win_p(mrb_state *mrb, mrb_value klass)
219220
#endif
220221
}
221222

223+
#ifdef MRB_WITH_IO_PREAD_PWRITE
224+
# define MRB_WITH_IO_PREAD_PWRITE_ENABLED TRUE
225+
#else
226+
# define MRB_WITH_IO_PREAD_PWRITE_ENABLED FALSE
227+
#endif
228+
222229
void
223230
mrb_mruby_io_gem_test(mrb_state* mrb)
224231
{
@@ -229,4 +236,6 @@ mrb_mruby_io_gem_test(mrb_state* mrb)
229236
mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1));
230237
mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1));
231238
mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE());
239+
240+
mrb_define_const(mrb, io_test, "MRB_WITH_IO_PREAD_PWRITE", mrb_bool_value(MRB_WITH_IO_PREAD_PWRITE_ENABLED));
232241
}

0 commit comments

Comments
 (0)