123456789_123456789_123456789_123456789_123456789_

Class: IO::Buffer::Storage

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::IO::Buffer
Instance Chain:
Inherits: IO::Buffer
Defined in: io_buffer.c

Overview

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.

new, for, and 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 Slice.

Constant Summary

::IO::Buffer - Inherited

BIG_ENDIAN, DEFAULT_SIZE, EXTERNAL, HOST_ENDIAN, INTERNAL, LITTLE_ENDIAN, MAPPED, MAP_ALIGNMENT, NETWORK_ENDIAN, PAGE_SIZE, PRIVATE, READONLY, SHARED, VERSION

Class Method Summary

::IO::Buffer - Inherited

.for

Creates a zero-copy ::IO::Buffer from the given string's memory.

.map

Create an ::IO::Buffer for reading from file by memory-mapping the file.

.new

Creates an Storage with the given size and flags.

.size_of

Returns the size of the given buffer type(s) in bytes.

.string

Creates a new string of the given length and yields a zero-copy ::IO::Buffer instance to the block which uses the string as a source.

Instance Attribute Summary

::IO::Buffer - Inherited

#empty?,
#locked

Prevents the buffer or its buffer source from being moved or freed while the block is executing.

#locked?, #null?, #readonly?, #valid?

Instance Method Summary

::IO::Buffer - Inherited

#&

Generate a new buffer the same size as the source by applying the binary AND operation to the source, using the mask, repeating as necessary.

#<=>

Returns a negative integer, zero, or a positive integer if the receiver is less than, equal to, or greater than other, respectively.

#^

Generate a new buffer the same size as the source by applying the binary XOR operation to the source, using the mask, repeating as necessary.

#advance

Advances the beginning of a non-owning buffer view by amount bytes, reducing its size by the same amount.

#and!

Modify the source buffer in place by applying the binary AND operation to the source, using the mask, repeating as necessary.

#bit_count

Returns the number of set bits (1s) in the buffer, also known as the Hamming weight or population count.

#clear

Fill buffer with value, starting with offset and going for length bytes.

#copy

Efficiently copy from a source ::IO::Buffer into the buffer, at offset using memmove.

#get_string

Read a chunk or all of the buffer into a string, in the specified encoding.

#get_value

Read from buffer a value of type at offset.

#get_values

Similar to #get_value, except that it can handle multiple buffer types and returns an array of values.

#hexdump

Returns a human-readable string representation of the buffer.

#not!

Modify the source buffer in place by applying the unary NOT operation to the source.

#or!

Modify the source buffer in place by applying the binary OR operation to the source, using the mask, repeating as necessary.

#read

Perform one read operation of at most length bytes from io into the buffer starting at offset.

#set_string

Efficiently copy from a source ::String into the buffer, at offset using memmove.

#set_value,
#set_values

Write #values of buffer_types at offset to the buffer.

#size,
#slice

Produce another ::IO::Buffer which is a slice (or view into) the current one starting at offset bytes and going for length bytes.

#source

Returns the object backing this view, or nil for a source-less buffer.

#to_s,
#values

Returns an array of values of buffer_type starting from offset.

#write

Perform one write operation of at most length bytes to io from the buffer starting at offset.

#xor!

Modify the source buffer in place by applying the binary XOR operation to the source, using the mask, repeating as necessary.

#|

Generate a new buffer the same size as the source by applying the binary OR operation to the source, using the mask, repeating as necessary.

#~

Generate a new buffer the same size as the source by applying the unary NOT operation to the source.

::Comparable - Included

#<

Returns whether self is "less than" other; equivalent to (self <=> other) < 0:

#<=

Returns whether self is "less than or equal to" other; equivalent to (self <=> other) <= 0:

#==

Compares two objects based on the receiver's #<=> method, returning true if it returns 0.

#>

Returns whether self is "greater than" other; equivalent to (self <=> other) > 0:

#>=

Returns whether self is "greater than or equal to" other; equivalent to (self <=> other) >= 0:

#between?

Returns false if obj #<=> min is less than zero or if obj #<=> max is greater than zero, true otherwise.

#clamp

In (min, max) form, returns min if obj #<=> min is less than zero, max if obj #<=> max is greater than zero, and obj otherwise.

Constructor Details

IO::Buffer.new([size = DEFAULT_SIZE, [flags]]) ⇒ io_buffer

Create a new zero-filled ::IO::Buffer of IO::Buffer#size bytes. By default, the buffer will be internal: directly allocated chunk of the memory. But if the requested IO::Buffer#size is more than OS-specific 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 MAPPED as a second parameter.

SHARED and PRIVATE imply MAPPED and are mutually exclusive. Otherwise, if flags do not include an allocation mode, INTERNAL or 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
[ GitHub ]

  
# File 'io_buffer.c', line 1179

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;
}

Instance Attribute Details

#external? ⇒ Boolean (readonly)

[ GitHub ]

#internal? ⇒ Boolean (readonly)

[ GitHub ]

#mapped? ⇒ Boolean (readonly)

[ GitHub ]

#private? ⇒ Boolean (readonly)

[ GitHub ]

#shared? ⇒ Boolean (readonly)

[ GitHub ]

Instance Method Details

#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)
[ GitHub ]

  
# File 'io_buffer.c', line 2037

static VALUE
io_buffer_free(VALUE self)
{
    rb_check_frozen(self);

    return rb_io_buffer_free(self);
}

#dup ⇒ io_buffer #clone ⇒ io_buffer

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
[ GitHub ]

  
# File 'io_buffer.c', line 3492

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);
}

#resize(new_size) ⇒ 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 IO::Buffer.for), and locked owning buffers cannot be resized. Frozen buffers cannot be resized.

[ GitHub ]

  
# File 'io_buffer.c', line 2546

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;
}

#transfer ⇒ new_io_buffer

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)
[ GitHub ]

  
# File 'io_buffer.c', line 2353

static VALUE
io_buffer_transfer(VALUE self)
{
    rb_check_frozen(self);

    return rb_io_buffer_transfer(self);
}