Skip to content
On this page

Preview Ruby 4.1 is a preview. It can change before the final release. Switch to Ruby 4.0 →

class · Ruby 4.1

IO::Buffer

< Object include Comparable 38 instance methods 5 class methods

IO::Buffer is the common byte-view interface. Its concrete subclasses are IO::Buffer::Storage, which manages backing storage, and IO::Buffer::Slice, which represents a parent-relative view. Both are IO::Buffer instances.

IO::Buffer.new, IO::Buffer.for, and IO::Buffer.map create Storage objects. The abstract base itself cannot be allocated directly. Each factory creates one storage-bearing object, with no additional view needed for byte access.

Storage#resize may reallocate; Storage#free and Storage#transfer manage the backing storage. Slice#resize and Slice#advance only change a view, and Slice never acquires its own storage. Duplicating Storage copies its bytes; duplicating a Slice copies its range and source reference without copying bytes.

IO::Buffer is an efficient zero-copy buffer for input/output. There are typical use cases:

  • Create an empty buffer with ::new, fill it with buffer using copy or set_value, set_string, get buffer with get_string or write it directly to some file with write.

  • Create a buffer mapped to some string with ::for, then it could be used both for reading with get_string or get_value, and writing (writing will change the source string, too).

  • Create a buffer mapped to some file with ::map, then it could be used for reading and writing the underlying file.

  • Create a string of a fixed size with ::string, then read into it, or modify it using set_value.

Interaction with string and file memory is performed by efficient low-level C mechanisms like memcpy.

The class is meant to be an utility for implementing more high-level mechanisms like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary protocols.

MemoryView Support

IO::Buffer supports the C-level MemoryView protocol, so C extensions can use +rb_memory_view_get()+ to access the buffer’s memory directly (zero-copy) as a 1-dimensional contiguous array of bytes. The memory view is writable if the buffer is not readonly? and RUBY_MEMORY_VIEW_WRITABLE is specified.

While a MemoryView is exported, the buffer is locked.

Examples of Usage

Empty buffer:

Example
buffer = IO::Buffer.new(8)  # create empty 8-byte buffer
# =>
# #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
# ...
buffer
# =>
# <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
# 0x00000000  00 00 00 00 00 00 00 00
buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
# => 4
buffer.get_string  # get the result
# => "\x00\x00test\x00\x00"

Buffer from string:

Example
string = 'data'
IO::Buffer.for(string) do |buffer|
  buffer
  # =>
  # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
  # 0x00000000  64 61 74 61                                     data

  buffer.get_string(2)  # read content starting from offset 2
  # => "ta"
  buffer.set_string('---', 1) # write content, starting from offset 1
  # => 3
  buffer
  # =>
  # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
  # 0x00000000  64 2d 2d 2d                                     d---
  string  # original string changed, too
  # => "d---"
end

Buffer from file:

Example
File.write('test.txt', 'test data')
# => 9
buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
# =>
# #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY>
# ...
buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
# => "da"
buffer.set_string('---', 1) # attempt to write
# in `set_string': Buffer is not writable! (IO::Buffer::AccessError)

# To create writable file-mapped buffer
# Open file for read-write, pass size, offset, and flags=0
buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
buffer.set_string('---', 1)
# => 3 -- bytes written
File.read('test.txt')
# => "t--- data"

The class is experimental and the interface is subject to change, this is especially true of file mappings which may be removed entirely in the future.

Class methods 5

Instance methods 38

Constants

Refers to big endian byte order, where the most significant byte is stored first. See get_value for more details.

The default buffer size, typically a (small) multiple of the PAGE_SIZE. Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE environment variable.

Indicates that the memory in the buffer is owned by someone else. See external? for more details.

Refers to the byte order of the host machine. See get_value for more details.

Indicates that the memory in the buffer is owned by the buffer. See internal? for more details.

Refers to little endian byte order, where the least significant byte is stored first. See get_value for more details.

The alignment required for file mapping offsets. Mapping sizes do not need to be aligned.

Indicates that the memory in the buffer is mapped by the operating system. See mapped? for more details.

Refers to network byte order, which is the same as big endian. See get_value for more details.

The operating system page size. Used for efficient page-aligned memory allocations.

Indicates that the memory in the buffer is mapped privately and changes won’t be replicated to the underlying file. See private? for more details.

Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See readonly? for more details.

Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See shared? for more details.

The IO::Buffer interface version.

Ruby version changes 3

  1. 4.1
    • read, write, pread, and pwrite now perform one IO operation using (offset, length), where length is the maximum transfer size. Short transfers are returned directly.

    • The corresponding fiber scheduler hooks are no longer experimental and now use the same (offset, length) argument order and single-operation semantics.

  2. 3.4