Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,16 @@ Note: We're only listing outstanding class updates.
binary representation of a non-negative integer (its population count).
[[Feature #20163]]

* IO::Buffer

* `read`, `write`, `pread`, and `pwrite` now perform one IO operation using
`(offset, length)`, where `length` is the maximum transfer size. Short
transfers are returned directly.

* The corresponding fiber scheduler hooks are no longer experimental and
now use the same `(offset, length)` argument order and single-operation
semantics.

* Kernel

* `Kernel#autoload_relative` and `Module#autoload_relative` are added.
Expand Down
36 changes: 17 additions & 19 deletions array.c
Original file line number Diff line number Diff line change
Expand Up @@ -2307,10 +2307,9 @@ rb_ary_to_ary(VALUE obj)
}

static void
rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen, int self_insert)
{
long olen;
long rofs;

if (len < 0) rb_raise(rb_eIndexError, "negative length (%ld)", len);
olen = RARRAY_LEN(ary);
Expand All @@ -2325,11 +2324,6 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
len = olen - beg;
}

{
const VALUE *optr = RARRAY_CONST_PTR(ary);
rofs = (rptr >= optr && rptr < optr + olen) ? rptr - optr : -1;
}

if (beg >= olen) {
VALUE target_ary;
if (beg > ARY_MAX_SIZE - rlen) {
Expand All @@ -2339,7 +2333,8 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
len = beg + rlen;
ary_mem_clear(ary, olen, beg - olen);
if (rlen > 0) {
if (rofs != -1) rptr = RARRAY_CONST_PTR(ary) + rofs;
/* ary's storage may have moved; only ary itself needs re-deriving. */
if (self_insert) rptr = RARRAY_CONST_PTR(ary);
ary_memcpy0(ary, beg, rlen, rptr, target_ary);
}
ARY_SET_LEN(ary, len);
Expand All @@ -2363,13 +2358,13 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
ARY_SET_LEN(ary, alen);
}
if (rlen > 0) {
if (rofs == -1) {
if (!self_insert) {
rb_gc_writebarrier_remember(ary);
}
else {
/* In this case, we're copying from a region in this array, so
* we don't need to fire the write barrier. */
rptr = RARRAY_CONST_PTR(ary) + rofs;
rptr = RARRAY_CONST_PTR(ary);
}

/* do not use RARRAY_PTR() because it can causes GC.
Expand All @@ -2381,6 +2376,13 @@ rb_ary_splice(VALUE ary, long beg, long len, const VALUE *rptr, long rlen)
}
}

static void
rb_ary_splice(VALUE ary, long beg, long len, VALUE rpl)
{
ary_splice(ary, beg, len, RARRAY_CONST_PTR(rpl), RARRAY_LEN(rpl), rpl == ary);
RB_GC_GUARD(rpl);
}

void
rb_ary_set_len(VALUE ary, long len)
{
Expand Down Expand Up @@ -2468,9 +2470,7 @@ ary_aset_by_rb_ary_store(VALUE ary, long key, VALUE val)
static VALUE
ary_aset_by_rb_ary_splice(VALUE ary, long beg, long len, VALUE val)
{
VALUE rpl = rb_ary_to_ary(val);
rb_ary_splice(ary, beg, len, RARRAY_CONST_PTR(rpl), RARRAY_LEN(rpl));
RB_GC_GUARD(rpl);
rb_ary_splice(ary, beg, len, rb_ary_to_ary(val));
return val;
}

Expand Down Expand Up @@ -2699,7 +2699,7 @@ rb_ary_insert(int argc, VALUE *argv, VALUE ary)
}
pos++;
}
rb_ary_splice(ary, pos, 0, argv + 1, argc - 1);
ary_splice(ary, pos, 0, argv + 1, argc - 1, FALSE);
return ary;
}

Expand Down Expand Up @@ -4423,7 +4423,7 @@ ary_slice_bang_by_rb_ary_splice(VALUE ary, long pos, long len)
}
else {
VALUE arg2 = rb_ary_new4(len, RARRAY_CONST_PTR(ary)+pos);
rb_ary_splice(ary, pos, len, 0, 0);
ary_splice(ary, pos, len, 0, 0, FALSE);
return arg2;
}
}
Expand Down Expand Up @@ -5268,11 +5268,9 @@ rb_ary_plus(VALUE x, VALUE y)
static VALUE
ary_append(VALUE x, VALUE y)
{
long n = RARRAY_LEN(y);
if (n > 0) {
rb_ary_splice(x, RARRAY_LEN(x), 0, RARRAY_CONST_PTR(y), n);
if (RARRAY_LEN(y) > 0) {
rb_ary_splice(x, RARRAY_LEN(x), 0, y);
}
RB_GC_GUARD(y);
return x;
}

Expand Down
30 changes: 24 additions & 6 deletions doc/language/fiber.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,19 +83,37 @@ class Scheduler
end

# Read from the given io into the specified buffer.
# WARNING: Experimental hook! Do not use in production code!
# @parameter io [IO] The io to read from.
# @parameter buffer [IO::Buffer] The buffer to read into.
# @parameter length [Integer] The minimum amount to read.
def io_read(io, buffer, length)
# @parameter offset [Integer] The offset in the buffer to read to.
# @parameter length [Integer] The maximum amount to read in one operation.
def io_read(io, buffer, offset, length)
end

# Read from the given io at the specified position into the specified buffer.
# @parameter io [IO] The io to read from.
# @parameter buffer [IO::Buffer] The buffer to read into.
# @parameter from [Integer] The position in the io to read from.
# @parameter offset [Integer] The offset in the buffer to read to.
# @parameter length [Integer] The maximum amount to read in one operation.
def io_pread(io, buffer, from, offset, length)
end

# Write from the given buffer into the specified IO.
# WARNING: Experimental hook! Do not use in production code!
# @parameter io [IO] The io to write to.
# @parameter buffer [IO::Buffer] The buffer to write from.
# @parameter length [Integer] The minimum amount to write.
def io_write(io, buffer, length)
# @parameter offset [Integer] The offset in the buffer to write from.
# @parameter length [Integer] The maximum amount to write in one operation.
def io_write(io, buffer, offset, length)
end

# Write from the given buffer to the specified position in the given io.
# @parameter io [IO] The io to write to.
# @parameter buffer [IO::Buffer] The buffer to write from.
# @parameter from [Integer] The position in the io to write to.
# @parameter offset [Integer] The offset in the buffer to write from.
# @parameter length [Integer] The maximum amount to write in one operation.
def io_pwrite(io, buffer, from, offset, length)
end

# Sleep the current task for the specified duration, or forever if not
Expand Down
20 changes: 17 additions & 3 deletions gc/mmtk/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ pub fn parse_capacity(input: &str) -> Option<usize> {
};

match suffix {
"GiB" => Some(v * GIBIBYTE),
"MiB" => Some(v * MEBIBYTE),
"KiB" => Some(v * KIBIBYTE),
"GiB" => v.checked_mul(GIBIBYTE),
"MiB" => v.checked_mul(MEBIBYTE),
"KiB" => v.checked_mul(KIBIBYTE),
"" => Some(v),
_ => None,
}
Expand Down Expand Up @@ -150,6 +150,20 @@ mod tests {
assert_eq!(Some(10737418240), parse_capacity("10GiB"))
}

#[test]
fn test_parse_capacity_rejects_overflowing_values() {
assert_eq!(None, parse_capacity("99999999999GiB"));
assert_eq!(None, parse_capacity("99999999999999MiB"));
assert_eq!(None, parse_capacity("99999999999999999KiB"));

const GIBIBYTE: usize = 1024 * 1024 * 1024;
let max_gib = usize::MAX / GIBIBYTE;
assert_eq!(
Some(max_gib * GIBIBYTE),
parse_capacity(&format!("{max_gib}GiB"))
);
}

#[test]
fn test_parse_capacity_parses_nonsense_values() {
assert_eq!(None, parse_capacity("notanumber"));
Expand Down
41 changes: 19 additions & 22 deletions include/ruby/fiber/scheduler.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
RBIMPL_SYMBOL_EXPORT_BEGIN()

// Version 3: Adds support for `fiber_interrupt`.
#define RUBY_FIBER_SCHEDULER_VERSION 3
// Version 4: IO hooks use single-transfer `(offset, length)` semantics.
#define RUBY_FIBER_SCHEDULER_VERSION 4

struct timeval;
struct rb_thread_struct;
Expand Down Expand Up @@ -292,25 +293,25 @@ VALUE rb_fiber_scheduler_io_selectv(VALUE scheduler, int argc, VALUE *argv);
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to read from.
* @param[in] buffer The buffer to read to.
* @param[in] length The minimum number of bytes to read.
* @param[in] offset The offset in the buffer to read from.
* @param[in] offset The offset in the buffer to read to.
* @param[in] length The maximum number of bytes to read in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns `[-errno, size]`.
*/
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length);

/**
* Non-blocking write to the passed IO.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to write to.
* @param[in] buffer The buffer to write from.
* @param[in] length The minimum number of bytes to write.
* @param[in] offset The offset in the buffer to write from.
* @param[in] length The maximum number of bytes to write in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns `[-errno, size]`.
*/
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset);
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t offset, size_t length);

/**
* Non-blocking read from the passed IO at the specified offset.
Expand All @@ -319,12 +320,12 @@ VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_
* @param[in] io An io object to read from.
* @param[in] from The offset to read from.
* @param[in] buffer The buffer to read to.
* @param[in] length The minimum number of bytes to read.
* @param[in] offset The offset in the buffer to read to.
* @param[in] length The maximum number of bytes to read in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t offset, size_t length);

/**
* Non-blocking write to the passed IO at the specified offset.
Expand All @@ -333,38 +334,36 @@ VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALU
* @param[in] io An io object to write to.
* @param[in] from The offset to write to.
* @param[in] buffer The buffer to write from.
* @param[in] length The minimum number of bytes to write.
* @param[in] offset The offset in the buffer to write from.
* @param[in] length The maximum number of bytes to write in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset);
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t offset, size_t length);

/**
* Non-blocking read from the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to read from.
* @param[in] base The memory to read to.
* @param[in] size Size of the memory.
* @param[in] length The minimum number of bytes to read.
* @param[in] size The maximum number of bytes to read in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t size, size_t length);
VALUE rb_fiber_scheduler_io_read_memory(VALUE scheduler, VALUE io, void *base, size_t size);

/**
* Non-blocking write to the passed IO using a native buffer.
*
* @param[in] scheduler Target scheduler.
* @param[in] io An io object to write to.
* @param[in] base The memory to write from.
* @param[in] size Size of the memory.
* @param[in] length The minimum number of bytes to write.
* @param[in] size The maximum number of bytes to write in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base, size_t size, size_t length);
VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *base, size_t size);

/**
* Non-blocking pread from the passed IO using a native buffer.
Expand All @@ -373,12 +372,11 @@ VALUE rb_fiber_scheduler_io_write_memory(VALUE scheduler, VALUE io, const void *
* @param[in] io An io object to read from.
* @param[in] from The offset to read from.
* @param[in] base The memory to read to.
* @param[in] size Size of the memory.
* @param[in] length The minimum number of bytes to read.
* @param[in] size The maximum number of bytes to read in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_read`.
* @return otherwise What `scheduler.io_read` returns.
*/
VALUE rb_fiber_scheduler_io_pread_memory(VALUE scheduler, VALUE io, rb_off_t from, void *base, size_t size, size_t length);
VALUE rb_fiber_scheduler_io_pread_memory(VALUE scheduler, VALUE io, rb_off_t from, void *base, size_t size);

/**
* Non-blocking pwrite to the passed IO using a native buffer.
Expand All @@ -387,12 +385,11 @@ VALUE rb_fiber_scheduler_io_pread_memory(VALUE scheduler, VALUE io, rb_off_t fro
* @param[in] io An io object to write to.
* @param[in] from The offset to write from.
* @param[in] base The memory to write from.
* @param[in] size Size of the memory.
* @param[in] length The minimum number of bytes to write.
* @param[in] size The maximum number of bytes to write in one operation.
* @retval RUBY_Qundef `scheduler` doesn't have `#io_write`.
* @return otherwise What `scheduler.io_write` returns.
*/
VALUE rb_fiber_scheduler_io_pwrite_memory(VALUE scheduler, VALUE io, rb_off_t from, const void *base, size_t size, size_t length);
VALUE rb_fiber_scheduler_io_pwrite_memory(VALUE scheduler, VALUE io, rb_off_t from, const void *base, size_t size);

/**
* Non-blocking close the given IO.
Expand Down
14 changes: 8 additions & 6 deletions include/ruby/io/buffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ RBIMPL_SYMBOL_EXPORT_BEGIN()
// WARNING: This entire interface is experimental and may change in the future!
#define RB_IO_BUFFER_EXPERIMENTAL 1

#define RUBY_IO_BUFFER_VERSION 2
// Version 3: IO operations use single-transfer `(offset, length)` semantics.
#define RUBY_IO_BUFFER_VERSION 3

// The `IO::Buffer` class.
RUBY_EXTERN VALUE rb_cIOBuffer;
Expand Down Expand Up @@ -107,11 +108,12 @@ VALUE rb_io_buffer_transfer(VALUE self);
void rb_io_buffer_resize(VALUE self, size_t size);
void rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length);

// The length is the minimum required length.
VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset);
VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset);
VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset);
// The length is the maximum transfer length. Each function performs one
// logical IO operation and may return a short result.
VALUE rb_io_buffer_read(VALUE self, VALUE io, size_t offset, size_t length);
VALUE rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length);
VALUE rb_io_buffer_write(VALUE self, VALUE io, size_t offset, size_t length);
VALUE rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length);

RBIMPL_SYMBOL_EXPORT_END()

Expand Down
Loading