summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2021-10-22 15:05:00 +1300
committerSamuel Williams <samuel.williams@oriontransfer.co.nz>2021-11-12 16:46:08 +1300
commitc833ece5f78b8c2e43263e08ccbd3ce1628bf610 (patch)
treef813cd893a32991422cd5e833b09236dd897bced
parent98b442e013afbb450f1c946d86ed625c39ab3233 (diff)
downloadruby-c833ece5f78b8c2e43263e08ccbd3ce1628bf610.tar.gz
Rework implementation of `IO::Buffer.for(string)` to use string locking.
-rw-r--r--io_buffer.c20
-rw-r--r--test/ruby/test_io_buffer.rb19
2 files changed, 39 insertions, 0 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 1acc942987..9b8f85ed4c 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -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)
@@ -253,6 +257,21 @@ rb_io_buffer_type_allocate(VALUE self)
}
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);
+
+ 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)
{
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
@@ -1029,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;
diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb
index 1bd839e163..12937729f4 100644
--- a/test/ruby/test_io_buffer.rb
+++ b/test/ruby/test_io_buffer.rb
@@ -62,6 +62,25 @@ class TestIOBuffer < Test::Unit::TestCase
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)