[前][次][番号順一覧][スレッド一覧]

ruby-changes:70428

From: Samuel <ko1@a...>
Date: Wed, 22 Dec 2021 06:58:03 +0900 (JST)
Subject: [ruby-changes:70428] e30920354f (master): Extended interface for IO::Buffer & documentation.

https://git.ruby-lang.org/ruby.git/commit/?id=e30920354f

From e30920354f8c4513150c61385220a8e75448d833 Mon Sep 17 00:00:00 2001
From: Samuel Williams <samuel.williams@o...>
Date: Wed, 22 Dec 2021 10:57:34 +1300
Subject: Extended interface for IO::Buffer & documentation.

---
 common.mk                   |   1 +
 include/ruby/io/buffer.h    |   8 +-
 io_buffer.c                 | 812 +++++++++++++++++++++++++++++++++++++++-----
 test/ruby/test_io_buffer.rb |   6 +-
 update.rb                   |  20 ++
 5 files changed, 763 insertions(+), 84 deletions(-)
 create mode 100755 update.rb

diff --git a/common.mk b/common.mk
index 14ef9cb4ec3..8fc0a590f8a 100644
--- a/common.mk
+++ b/common.mk
@@ -7586,6 +7586,7 @@ io_buffer.$(OBJEXT): {$(VPATH)}config.h https://github.com/ruby/ruby/blob/trunk/common.mk#L7586
 io_buffer.$(OBJEXT): {$(VPATH)}defines.h
 io_buffer.$(OBJEXT): {$(VPATH)}encoding.h
 io_buffer.$(OBJEXT): {$(VPATH)}intern.h
+io_buffer.$(OBJEXT): {$(VPATH)}internal.h
 io_buffer.$(OBJEXT): {$(VPATH)}internal/anyargs.h
 io_buffer.$(OBJEXT): {$(VPATH)}internal/arithmetic.h
 io_buffer.$(OBJEXT): {$(VPATH)}internal/arithmetic/char.h
diff --git a/include/ruby/io/buffer.h b/include/ruby/io/buffer.h
index 1154b98bdd8..4826a7a76f6 100644
--- a/include/ruby/io/buffer.h
+++ b/include/ruby/io/buffer.h
@@ -69,12 +69,12 @@ VALUE rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_fl https://github.com/ruby/ruby/blob/trunk/include/ruby/io/buffer.h#L69
 
 VALUE rb_io_buffer_lock(VALUE self);
 VALUE rb_io_buffer_unlock(VALUE self);
+int rb_io_buffer_try_unlock(VALUE self);
 VALUE rb_io_buffer_free(VALUE self);
 
-int rb_io_buffer_readonly_p(VALUE self);
-
-void rb_io_buffer_get(VALUE self, void **base, size_t *size);
-void rb_io_buffer_get_readonly(VALUE self, const void **base, size_t *size);
+int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size);
+void rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size);
+void rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size);
 
 VALUE rb_io_buffer_transfer(VALUE self);
 void rb_io_buffer_resize(VALUE self, size_t size);
diff --git a/io_buffer.c b/io_buffer.c
index a9aaa8d6267..4487cab7736 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -9,6 +9,7 @@ https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L9
 #include "ruby/io.h"
 #include "ruby/io/buffer.h"
 
+#include "internal.h"
 #include "internal/string.h"
 #include "internal/bits.h"
 #include "internal/error.h"
@@ -16,7 +17,7 @@ https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L17
 VALUE rb_cIOBuffer;
 VALUE rb_eIOBufferLockedError;
 VALUE rb_eIOBufferAllocationError;
-VALUE rb_eIOBufferMutationError;
+VALUE rb_eIOBufferAccessError;
 VALUE rb_eIOBufferInvalidatedError;
 
 size_t RUBY_IO_BUFFER_PAGE_SIZE;
@@ -155,41 +156,45 @@ io_buffer_experimental(void) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L156
 }
 
 static void
-io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
+io_buffer_zero(struct rb_io_buffer *data)
 {
-    data->flags = flags;
-    data->size = size;
+    data->base = NULL;
+    data->size = 0;
+#if defined(_WIN32)
+    data->mapping = NULL;
+#endif
+    data->source = Qnil;
+}
 
+static void
+io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
+{
     if (base) {
-        data->base = base;
+        // If we are provided a pointer, we use it.
     }
-    else {
-        if (data->flags & RB_IO_BUFFER_INTERNAL) {
-            data->base = calloc(data->size, 1);
+    else if (size) {
+        // If we are provided a non-zero size, we allocate it:
+        if (flags & RB_IO_BUFFER_INTERNAL) {
+            base = calloc(size, 1);
         }
-        else if (data->flags & RB_IO_BUFFER_MAPPED) {
-            data->base = io_buffer_map_memory(data->size);
+        else if (flags & RB_IO_BUFFER_MAPPED) {
+            base = io_buffer_map_memory(size);
         }
-    }
 
-    if (!data->base) {
-        rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
+        if (!base) {
+            rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
+        }
+    } else {
+        // Otherwise we don't do anything.
+        return;
     }
 
+    data->base = base;
+    data->size = size;
+    data->flags = flags;
     data->source = source;
 }
 
-static void
-io_buffer_zero(struct rb_io_buffer *data)
-{
-    data->base = NULL;
-    data->size = 0;
-#if defined(_WIN32)
-    data->mapping = NULL;
-#endif
-    data->source = Qnil;
-}
-
 static int
 io_buffer_free(struct rb_io_buffer *data)
 {
@@ -214,8 +219,9 @@ io_buffer_free(struct rb_io_buffer *data) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L219
             data->mapping = NULL;
         }
 #endif
-
         data->size = 0;
+        data->flags = 0;
+        data->source = Qnil;
 
         return 1;
     }
@@ -275,6 +281,31 @@ rb_io_buffer_type_allocate(VALUE self) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L281
     return instance;
 }
 
+/*
+ *  call-seq: IO::Buffer.for(string) -> io_buffer
+ *
+ *  Creates a IO::Buffer from the given string's memory. The buffer remains
+ *  associated with the string, and writing to a buffer will update the string's
+ *  contents.
+ *
+ *  Until #free is invoked on the buffer, either explicitly or via the garbage
+ *  collector, the source string will be locked and cannot be modified.
+ *
+ *  If the string is frozen, it will create a read-only buffer which cannot be
+ *  modified.
+ *
+ *    string = 'test'
+ *    buffer = IO::Buffer.for(str)
+ *    buffer.external? #=> true
+ *
+ *    buffer.get_string(0, 1)
+ *    # => "t"
+ *    string
+ *    # => "best"
+ *
+ *    buffer.resize(100)
+ *    # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
+ */
 VALUE
 rb_io_buffer_type_for(VALUE klass, VALUE string)
 {
@@ -329,6 +360,44 @@ rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags fl https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L360
     return instance;
 }
 
+/*
+ *  call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
+ *
+ *  Create an IO::Buffer for reading from +file+ by memory-mapping the file.
+ *  +file_io+ should be a +File+ instance, opened for reading.
+ *
+ *  Optional +size+ and +offset+ of mapping can be specified.
+ *
+ *  By default, the buffer would be immutable (read only); to create a writable
+ *  mapping, you need to open a file in read-write mode, and explicitly pass
+ *  +flags+ argument without IO::Buffer::IMMUTABLE.
+ *
+ *     File.write('test.txt', 'test')
+ *
+ *     buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
+ *     # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
+ *
+ *     buffer.readonly?   # => true
+ *
+ *     buffer.get_string
+ *     # => "test"
+ *
+ *     buffer.set_string('b', 0)
+ *     # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
+ *
+ *     # create read/write mapping: length 4 bytes, offset 0, flags 0
+ *     buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
+ *     buffer.set_string('b', 0)
+ *     # => 1
+ *
+ *     # Check it
+ *     File.read('test.txt')
+ *     # => "best"
+ *
+ *  Note that some operating systems may not have cache coherency between mapped
+ *  buffers and file reads.
+ *
+ */
 static VALUE
 io_buffer_map(int argc, VALUE *argv, VALUE klass)
 {
@@ -336,10 +405,11 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L405
         rb_error_arity(argc, 2, 4);
     }
 
+    // We might like to handle a string path?
     VALUE io = argv[0];
 
     size_t size;
-    if (argc >= 2) {
+    if (argc >= 2 && !RB_NIL_P(argv[1])) {
         size = RB_NUM2SIZE(argv[1]);
     }
     else {
@@ -354,7 +424,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L424
             rb_raise(rb_eArgError, "File larger than address space!");
         }
         else {
-            // This conversion shoud be safe:
+            // This conversion should be safe:
             size = (size_t)file_size;
         }
     }
@@ -364,7 +434,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L434
         offset = NUM2OFFT(argv[2]);
     }
 
-    enum rb_io_buffer_flags flags = RB_IO_BUFFER_READONLY;
+    enum rb_io_buffer_flags flags = 0;
     if (argc >= 4) {
         flags = RB_NUM2UINT(argv[3]);
     }
@@ -376,13 +446,40 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass) https://github.com/ruby/ruby/blob/trunk/io_buffer.c#L446
 static inline enum rb_io_buffer_flags
 io_flags_for_size(size_t size)
 {
-    if (size > RUBY_IO_BUFFER_PAGE_SIZE) {
+    if (size >= RUBY_IO_BUFFER_PAGE_SIZE) {
         return RB_IO_BUFFER_MAPPED;
     }
 
     return RB_IO_BUFFER_INTERNAL;
 }
 
+/*
+ *  call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
+ *
+ *  Create a new zero-filled IO::Buffer of +size+ bytes.
+ *  By default, the buffer will be _internal_: directly allocated chunk
+ *  of the memory. But if the requested +size+ is more than OS-specific
+ *  IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
+ *  virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
+ *  on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
+ *  as a second parameter.
+ *
+ *  Examples
+ *
+ *    buffer = IO::Buffer.new(4)
+ *    # =>
+ *    #  #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
+ *    #  0x00000000  00 00 00 00                                     ....
+ *
+ *    buffer.get_string(0, 1) # => "\x00"
+ *
+ *    buffer.set_string("test")
+ *    buffer
+ *    #  =>
+ *    # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
+ *    # 0x00000000  74 65 73 74                                     test
+ *
+ */
 VALUE
 rb_io_buffer_initialize(int argc, VALUE *arg (... truncated)

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]