Skip to content
Permalink
Browse files
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`.
  • Loading branch information
dearblue committed Feb 2, 2020
1 parent 3c67d9b commit 4c6d524c473ebb9174d0183dc1d1ac0530337314
Showing 4 changed files with 150 additions and 8 deletions.
@@ -9,6 +9,14 @@
extern "C" {
#endif

#if defined(MRB_WITHOUT_IO_PREAD_PWRITE)
# undef MRB_WITH_IO_PREAD_PWRITE
#elif !defined(MRB_WITH_IO_PREAD_PWRITE)
# if defined(__unix__)
# define MRB_WITH_IO_PREAD_PWRITE
# endif
#endif

struct mrb_io {
int fd; /* file descriptor, or -1 */
int fd2; /* file descriptor to write if it's different from fd, or -1 */
@@ -31,6 +31,7 @@
typedef long ftime_t;
typedef long fsuseconds_t;
typedef int fmode_t;
typedef int mrb_io_read_write_size;

#ifndef O_TMPFILE
#define O_TMPFILE O_TEMPORARY
@@ -43,6 +44,7 @@
typedef time_t ftime_t;
typedef suseconds_t fsuseconds_t;
typedef mode_t fmode_t;
typedef ssize_t mrb_io_read_write_size;
#endif

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

static mrb_value mrb_io_sysread_common(mrb_state *mrb,
mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset);

static mrb_io_read_write_size
mrb_sysread_dummy(int fd, void *buf, fsize_t nbytes, off_t offset)
{
return (mrb_io_read_write_size)read(fd, buf, nbytes);
}

mrb_value
mrb_io_sysread(mrb_state *mrb, mrb_value io)
{
struct mrb_io *fptr;
mrb_value buf = mrb_nil_value();
mrb_int maxlen;
int ret;

mrb_get_args(mrb, "i|S", &maxlen, &buf);

return mrb_io_sysread_common(mrb, mrb_sysread_dummy, io, buf, maxlen, 0);
}

static mrb_value
mrb_io_sysread_common(mrb_state *mrb,
mrb_io_read_write_size (*readfunc)(int, void *, fsize_t, off_t),
mrb_value io, mrb_value buf, mrb_int maxlen, off_t offset)
{
struct mrb_io *fptr;
int ret;

if (maxlen < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "negative expanding string size");
}
@@ -875,7 +897,7 @@ mrb_io_sysread(mrb_state *mrb, mrb_value io)
if (!fptr->readable) {
mrb_raise(mrb, E_IO_ERROR, "not opened for reading");
}
ret = read(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen);
ret = readfunc(fptr->fd, RSTRING_PTR(buf), (fsize_t)maxlen, offset);
switch (ret) {
case 0: /* EOF */
if (maxlen == 0) {
@@ -925,27 +947,44 @@ mrb_io_sysseek(mrb_state *mrb, mrb_value io)
}
}

static mrb_value mrb_io_syswrite_common(mrb_state *mrb,
mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
mrb_value io, mrb_value buf, off_t offset);

static mrb_io_read_write_size
mrb_syswrite_dummy(int fd, const void *buf, fsize_t nbytes, off_t offset)
{
return (mrb_io_read_write_size)write(fd, buf, nbytes);
}

mrb_value
mrb_io_syswrite(mrb_state *mrb, mrb_value io)
{
mrb_value buf;

mrb_get_args(mrb, "S", &buf);

return mrb_io_syswrite_common(mrb, mrb_syswrite_dummy, io, buf, 0);
}

static mrb_value mrb_io_syswrite_common(mrb_state *mrb,
mrb_io_read_write_size (*writefunc)(int, const void *, fsize_t, off_t),
mrb_value io, mrb_value buf, off_t offset)
{
struct mrb_io *fptr;
mrb_value str, buf;
int fd, length;

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

mrb_get_args(mrb, "S", &str);
buf = str;

if (fptr->fd2 == -1) {
fd = fptr->fd;
} else {
fd = fptr->fd2;
}
length = write(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf));
length = writefunc(fd, RSTRING_PTR(buf), (fsize_t)RSTRING_LEN(buf), offset);
if (length == -1) {
mrb_sys_fail(mrb, 0);
}
@@ -1328,6 +1367,62 @@ mrb_io_sync(mrb_state *mrb, mrb_value self)
return mrb_bool_value(fptr->sync);
}

#ifndef MRB_WITH_IO_PREAD_PWRITE
# define mrb_io_pread mrb_notimplement_m
# define mrb_io_pwrite mrb_notimplement_m
#else
static off_t
value2off(mrb_state *mrb, mrb_value offv)
{
switch (mrb_type(offv)) {
#ifndef MRB_WITHOUT_FLOAT
case MRB_TT_FLOAT:
{
mrb_float tmp = mrb_float(offv);
if (tmp < INT64_MIN || tmp > INT64_MAX) {
/* fall through to use convert by `mrb_int()` (and raise error if out of range) */
} else {
return (off_t)tmp;
}
}
/* fall through */
#endif /* MRB_WITHOUT_FLOAT */
default:
return (off_t)mrb_int(mrb, offv);
}
}

/*
* call-seq:
* pread(maxlen, offset, outbuf = "") -> outbuf
*/
static mrb_value
mrb_io_pread(mrb_state *mrb, mrb_value io)
{
mrb_value buf = mrb_nil_value();
mrb_value off;
mrb_int maxlen;

mrb_get_args(mrb, "io|S!", &maxlen, &off, &buf);

return mrb_io_sysread_common(mrb, pread, io, buf, maxlen, value2off(mrb, off));
}

/*
* call-seq:
* pwrite(buffer, offset) -> wrote_bytes
*/
static mrb_value
mrb_io_pwrite(mrb_state *mrb, mrb_value io)
{
mrb_value buf, off;

mrb_get_args(mrb, "So", &buf, &off);

return mrb_io_syswrite_common(mrb, pwrite, io, buf, value2off(mrb, off));
}
#endif /* MRB_WITH_IO_PREAD_PWRITE */

static mrb_value
io_bufread(mrb_state *mrb, mrb_value self)
{
@@ -1383,6 +1478,8 @@ mrb_init_io(mrb_state *mrb)
mrb_define_method(mrb, io, "closed?", mrb_io_closed, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
mrb_define_method(mrb, io, "pid", mrb_io_pid, MRB_ARGS_NONE()); /* 15.2.20.5.2 */
mrb_define_method(mrb, io, "fileno", mrb_io_fileno_m, MRB_ARGS_NONE());
mrb_define_method(mrb, io, "pread", mrb_io_pread, MRB_ARGS_ANY()); /* ruby 2.5 feature */
mrb_define_method(mrb, io, "pwrite", mrb_io_pwrite, MRB_ARGS_ANY()); /* ruby 2.5 feature */

mrb_define_class_method(mrb, io, "_bufread", io_bufread, MRB_ARGS_REQ(2));
}
@@ -564,6 +564,34 @@ def io._buf
end
end

assert('IO#pread') do
skip "IO#pread is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE

IO.open(IO.sysopen($mrbtest_io_rfname, 'r'), 'r') do |io|
assert_equal $mrbtest_io_msg.byteslice(5, 8), io.pread(8, 5)
assert_equal 0, io.pos
assert_equal $mrbtest_io_msg.byteslice(1, 5), io.pread(5, 1)
assert_equal 0, io.pos
assert_raise(RuntimeError) { io.pread(20, -9) }
end
end

assert('IO#pwrite') do
skip "IO#pwrite is not implemented on this configuration" unless MRubyIOTestUtil::MRB_WITH_IO_PREAD_PWRITE

IO.open(IO.sysopen($mrbtest_io_wfname, 'w+'), 'w+') do |io|
assert_equal 6, io.pwrite("Warld!", 7)
assert_equal 0, io.pos
assert_equal 7, io.pwrite("Hello, ", 0)
assert_equal 0, io.pos
assert_equal "Hello, Warld!", io.read
assert_equal 6, io.pwrite("world!", 7)
assert_equal 13, io.pos
io.pos = 0
assert_equal "Hello, world!", io.read
end
end

assert('IO.pipe') do
begin
called = false
@@ -64,6 +64,7 @@ mkdtemp(char *temp)
#include "mruby/error.h"
#include "mruby/string.h"
#include "mruby/variable.h"
#include <mruby/ext/io.h>

static mrb_value
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)
#endif
}

#ifdef MRB_WITH_IO_PREAD_PWRITE
# define MRB_WITH_IO_PREAD_PWRITE_ENABLED TRUE
#else
# define MRB_WITH_IO_PREAD_PWRITE_ENABLED FALSE
#endif

void
mrb_mruby_io_gem_test(mrb_state* mrb)
{
@@ -229,4 +236,6 @@ mrb_mruby_io_gem_test(mrb_state* mrb)
mrb_define_class_method(mrb, io_test, "mkdtemp", mrb_io_test_mkdtemp, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, io_test, "rmdir", mrb_io_test_rmdir, MRB_ARGS_REQ(1));
mrb_define_class_method(mrb, io_test, "win?", mrb_io_win_p, MRB_ARGS_NONE());

mrb_define_const(mrb, io_test, "MRB_WITH_IO_PREAD_PWRITE", mrb_bool_value(MRB_WITH_IO_PREAD_PWRITE_ENABLED));
}

0 comments on commit 4c6d524

Please sign in to comment.