Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IO::Buffer restore string locking and zero-copy string backed buffers. #5109

Merged
merged 2 commits into from Nov 12, 2021
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
31 changes: 27 additions & 4 deletions io_buffer.c
Expand Up @@ -182,6 +182,10 @@ io_buffer_free(struct rb_io_buffer *data)
io_buffer_unmap(data->base, data->size);
}

if (RB_TYPE_P(data->source, T_STRING)) {
rb_str_unlocktmp(data->source);
}

data->base = NULL;

#if defined(_WIN32)
Expand Down Expand Up @@ -252,6 +256,21 @@ rb_io_buffer_type_allocate(VALUE self)
return instance;
}

VALUE
rb_io_buffer_type_for(VALUE klass, VALUE string)
{
VALUE instance = rb_io_buffer_type_allocate(klass);

struct rb_io_buffer *data = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);

rb_str_locktmp(string);
ioquatix marked this conversation as resolved.
Show resolved Hide resolved

io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), RB_IO_BUFFER_EXTERNAL, string);

return instance;
}

VALUE
rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
{
Expand Down Expand Up @@ -417,7 +436,6 @@ rb_io_buffer_to_s(VALUE self)
}

return rb_str_cat2(result, ">");

}

static VALUE
Expand Down Expand Up @@ -701,16 +719,20 @@ size_t rb_io_buffer_copy(VALUE self, VALUE source, size_t offset)
const void *source_base = NULL;
size_t source_size = 0;

struct rb_io_buffer *data = NULL;
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

if (data->flags & RB_IO_BUFFER_IMMUTABLE) {
rb_raise(rb_eRuntimeError, "Buffer is immutable!");
}

if (RB_TYPE_P(source, T_STRING)) {
RSTRING_GETMEM(source, source_base, source_size);
}
else {
rb_io_buffer_get_immutable(source, &source_base, &source_size);
}

struct rb_io_buffer *data = NULL;
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

rb_io_buffer_validate(data, offset, source_size);

memcpy((char*)data->base + offset, source_base, source_size);
Expand Down Expand Up @@ -1026,6 +1048,7 @@ Init_IO_Buffer(void)
rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);

rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);

#ifdef _WIN32
SYSTEM_INFO info;
Expand Down
48 changes: 48 additions & 0 deletions test/ruby/test_io_buffer.rb
Expand Up @@ -44,11 +44,43 @@ def test_new_mapped
assert buffer.mapped?
end

def test_new_immutable
buffer = IO::Buffer.new(128, IO::Buffer::INTERNAL|IO::Buffer::IMMUTABLE)
assert buffer.immutable?

assert_raise RuntimeError do
buffer.copy("", 0)
end

assert_raise RuntimeError do
buffer.copy("!", 1)
end
end

def test_file_mapped
buffer = File.open(__FILE__) {|file| IO::Buffer.map(file)}
assert_include buffer.to_str, "Hello World"
end

def test_string_mapped
string = "Hello World"
buffer = IO::Buffer.for(string)

# Cannot modify string as it's locked by the buffer:
assert_raise RuntimeError do
string[0] = "h"
end

buffer.set(:U8, 0, "h".ord)

# Buffer releases it's ownership of the string:
buffer.free

assert_equal "hello World", string
string[0] = "H"
assert_equal "Hello World", string
end

def test_resize
buffer = IO::Buffer.new(1024, IO::Buffer::MAPPED)
buffer.resize(2048, 0)
Expand Down Expand Up @@ -103,6 +135,22 @@ def test_slice_bounds
# end
end

def test_locked
buffer = IO::Buffer.new(128, IO::Buffer::INTERNAL|IO::Buffer::LOCKED)

assert_raise RuntimeError do
buffer.resize(256, 0)
end

assert_equal 128, buffer.size

assert_raise RuntimeError do
buffer.free
end

assert_equal 128, buffer.size
end

def test_invalidation
input, output = IO.pipe

Expand Down