class IO::Buffer::Storage
A buffer that manages backing storage, including internally allocated, mapped, String-backed, and borrowed memory. Storage provides Buffer’s byte-access interface together with allocation resizing, free, transfer, and storage-specific predicates. It need not own the bytes it references.
IO::Buffer.new, IO::Buffer.for, and IO::Buffer.map create Storage objects. The Storage object is directly usable as a buffer; no separate view object is needed unless a caller requests a slice. Duplicating Storage copies its bytes into independent storage, unlike duplicating an IO::Buffer::Slice.
Public Class Methods
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
rb_check_frozen(self);
rb_check_arity(argc, 0, 2);
size_t size;
if (argc > 0) {
size = io_buffer_extract_size(argv[0]);
}
else {
size = RUBY_IO_BUFFER_DEFAULT_SIZE;
}
enum rb_io_buffer_flags flags = 0;
if (argc >= 2) {
flags = io_buffer_extract_flags(argv[1]);
}
flags = io_buffer_flags_for_new(flags, size);
struct rb_io_buffer_storage *buffer = get_io_buffer_storage(self);
if (buffer->lock_count) rb_raise(rb_eIOBufferLockedError, "Cannot initialize locked buffer!");
io_buffer_storage_release(buffer);
io_buffer_storage_initialize(self, buffer, NULL, size, flags, Qnil);
return self;
}
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::Buffer::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.
IO::Buffer::SHARED and IO::Buffer::PRIVATE imply IO::Buffer::MAPPED and are mutually exclusive. Otherwise, if flags do not include an allocation mode, IO::Buffer::INTERNAL or IO::Buffer::MAPPED is inferred from the requested size. The two allocation modes are mutually exclusive.
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
Public Instance Methods
Source
static VALUE
rb_io_buffer_external_p(VALUE self)
{
struct rb_io_buffer_view *buffer = get_io_buffer_view(self);
return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
}
The buffer is external if it references the memory which is not allocated or mapped by the buffer itself.
A buffer created using ::for has an external reference to the string’s memory.
External buffer can’t be resized.
Source
static VALUE
io_buffer_free(VALUE self)
{
rb_check_frozen(self);
return rb_io_buffer_free(self);
}
If the buffer references memory, release it back to the operating system.
-
for a mapped buffer (e.g. from file): unmap.
-
for a buffer created from scratch: free memory.
-
for a buffer created from string: undo the association.
After releasing any referenced memory, the buffer is reset to a valid, empty, null state. It has no backing storage and its size is zero. Zero-length operations remain valid, while operations requiring bytes fail normal bounds checking.
Repeated calls on an unlocked, unfrozen Storage are harmless and return self. You can resize the buffer to allocate new storage. This method is not available on Slice, which never manages storage.
buffer = IO::Buffer.for('test') buffer.free # => #<IO::Buffer 0x0000000000000000+0 NULL> buffer.null? # => true buffer.empty? # => true buffer.valid? # => true buffer.get_string # => "" buffer.get_value(:U8, 0) # raises ArgumentError
A frozen buffer cannot be freed, as that would release the memory its contents live in:
buffer = IO::Buffer.for('test').freeze buffer.free # in `free': can't modify frozen IO::Buffer (FrozenError)
Source
static VALUE
rb_io_buffer_initialize_copy(VALUE self, VALUE source)
{
if (self == source) return self;
rb_check_frozen(self);
return rb_io_buffer_locked_for_reading(source, io_buffer_initialize_copy_from, self);
}
Make an internal copy of the source buffer. Updates to the copy will not affect the source buffer.
source = IO::Buffer.for("Hello World") # => # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE> # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World buffer = source.dup # => # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL> # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
Source
static VALUE
rb_io_buffer_internal_p(VALUE self)
{
struct rb_io_buffer_view *buffer = get_io_buffer_view(self);
return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
}
If the buffer is internal, meaning it references memory allocated by the buffer itself.
An internal buffer is not associated with any external memory (e.g. string) or file mapping.
Internal buffers are created using ::new and is the default when the requested size is less than the IO::Buffer::PAGE_SIZE and it was not requested to be mapped on creation.
Internal buffers can be resized. Slices remain valid if their ranges still fit within the resized buffer, including when its storage is relocated.
Source
static VALUE
rb_io_buffer_mapped_p(VALUE self)
{
struct rb_io_buffer_view *buffer = get_io_buffer_view(self);
return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
}
If the buffer is mapped, meaning it references memory mapped by the buffer.
Mapped buffers are either anonymous, if created by ::new with the IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE, or backed by a file if created with ::map.
Mapped buffers can usually be resized. Slices remain valid if their ranges still fit within the resized buffer, including when its mapping is moved.
Source
static VALUE
rb_io_buffer_private_p(VALUE self)
{
struct rb_io_buffer_view *buffer = get_io_buffer_view(self);
return RBOOL(buffer->flags & RB_IO_BUFFER_PRIVATE);
}
If the buffer is private, meaning modifications to the buffer will not be replicated to the underlying file mapping.
# Create a test file: File.write('test.txt', 'test') # Create a private mapping from the given file. Note that the file here # is opened in read-only mode, but it doesn't matter due to the private # mapping: buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE) # => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE> # Write to the buffer (invoking CoW of the underlying file buffer): buffer.set_string('b', 0) # => 1 # The file itself is not modified: File.read('test.txt') # => "test"
Source
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
rb_check_frozen(self);
rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
Resizes a buffer to a new_size bytes, preserving its content. Depending on the old and new size, the memory area associated with the buffer might be either extended, or rellocated at different address with content being copied.
buffer = IO::Buffer.new(4) buffer.set_string("test", 0) buffer.resize(8) # resize to 8 bytes # => # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL> # 0x00000000 74 65 73 74 00 00 00 00 test....
When the buffer is a slice, resizing changes the size of the view without modifying the source buffer or allocating new storage. The resized view must remain within the source buffer. Growing the view exposes the existing bytes in the source; they are not cleared. Because the source allocation does not change, a slice can be resized while its source is locked.
External owning buffers (created with ::for), and locked owning buffers cannot be resized. Frozen buffers cannot be resized.
Source
static VALUE
io_buffer_transfer(VALUE self)
{
rb_check_frozen(self);
return rb_io_buffer_transfer(self);
}
Transfers ownership of the underlying memory to a new buffer, causing the current buffer to become uninitialized.
buffer = IO::Buffer.for('test') other = buffer.transfer other # => # #<IO::Buffer 0x00007f136a15f7b0+4 EXTERNAL READONLY SLICE> # 0x00000000 74 65 73 74 test buffer # => # #<IO::Buffer 0x0000000000000000+0 NULL EXTERNAL READONLY> buffer.null? # => true
A frozen buffer cannot transfer ownership, as that would leave it uninitialized:
buffer = IO::Buffer.for('test').freeze buffer.transfer # in `transfer': can't modify frozen IO::Buffer (FrozenError)