%a{annotate:rdoc:skip}
class IO
  # <!-- rdoc-file=io_buffer.c -->
  # IO::Buffer is a 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.
  #
  # ## Examples of Usage
  #
  # Empty buffer:
  #
  #     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:
  #
  #     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:
  #
  #     File.write('test.txt', 'test data')
  #     # => 9
  #     buffer = IO::Buffer.map(File.open('test.txt'))
  #     # =>
  #     # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
  #     # ...
  #     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 Buffer
    include Comparable

    # <!--
    #   rdoc-file=io_buffer.c
    #   - IO::Buffer.for(string) -> readonly io_buffer
    #   - IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
    # -->
    # Creates a zero-copy IO::Buffer from the given string's memory. Without a block
    # a frozen internal copy of the string is created efficiently and used as the
    # buffer source. When a block is provided, the buffer is associated directly
    # with the string's internal buffer and updating the buffer will update the
    # string.
    #
    # 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. If the string is shared, it may trigger a copy-on-write when using
    # the block form.
    #
    #     string = 'test'
    #     buffer = IO::Buffer.for(string)
    #     buffer.external? #=> true
    #
    #     buffer.get_string(0, 1)
    #     # => "t"
    #     string
    #     # => "best"
    #
    #     buffer.resize(100)
    #     # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
    #
    #     IO::Buffer.for(string) do |buffer|
    #       buffer.set_string("T")
    #       string
    #       # => "Test"
    #     end
    #
    def self.for: (String) -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - 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.
    #
    def self.map: (File file, ?Integer? size, ?Integer offset, ?Integer flags) -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - IO::Buffer.string(length) {|io_buffer| ... read/write io_buffer ...} -> 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. The block is expected
    # to write to the buffer and the string will be returned.
    #
    #     IO::Buffer.string(4) do |buffer|
    #       buffer.set_string("Ruby")
    #     end
    #     # => "Ruby"
    #
    def self.string: (int) { (Buffer) -> void } -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - <=>(other) -> true or false
    # -->
    # Buffers are compared by size and exact contents of the memory they are
    # referencing using `memcmp`.
    #
    def <=>: (Buffer) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - clear(value = 0, [offset, [length]]) -> self
    # -->
    # Fill buffer with `value`, starting with `offset` and going for `length` bytes.
    #
    #     buffer = IO::Buffer.for('test').dup
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
    #     #   0x00000000  74 65 73 74         test
    #
    #     buffer.clear
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
    #     #   0x00000000  00 00 00 00         ....
    #
    #     buf.clear(1) # fill with 1
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
    #     #   0x00000000  01 01 01 01         ....
    #
    #     buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
    #     #   0x00000000  01 02 02 01         ....
    #
    #     buffer.clear(2, 1) # fill with 2, starting from offset 1
    #     # =>
    #     #   <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
    #     #   0x00000000  01 02 02 02         ....
    #
    def clear: (?Integer value, ?Integer offset, ?Integer length) -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - copy(source, [offset, [length, [source_offset]]]) -> size
    # -->
    # Efficiently copy from a source IO::Buffer into the buffer, at `offset` using
    # `memmove`. For copying String instances, see #set_string.
    #
    #     buffer = IO::Buffer.new(32)
    #     # =>
    #     # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
    #     # 0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................  *
    #
    #     buffer.copy(IO::Buffer.for("test"), 8)
    #     # => 4 -- size of buffer copied
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
    #     # 0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
    #
    # #copy can be used to put buffer into strings associated with buffer:
    #
    #     string = "data:    "
    #     # => "data:    "
    #     buffer = IO::Buffer.for(string) do |buffer|
    #       buffer.copy(IO::Buffer.for("test"), 5)
    #     end
    #     # => 4
    #     string
    #     # => "data:test"
    #
    # Attempt to copy into a read-only buffer will fail:
    #
    #     File.write('test.txt', 'test')
    #     buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
    #     buffer.copy(IO::Buffer.for("test"), 8)
    #     # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
    #
    # See ::map for details of creation of mutable file mappings, this will work:
    #
    #     buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
    #     buffer.copy(IO::Buffer.for("boom"), 0)
    #     # => 4
    #     File.read('test.txt')
    #     # => "boom"
    #
    # Attempt to copy the buffer which will need place outside of buffer's bounds
    # will fail:
    #
    #     buffer = IO::Buffer.new(2)
    #     buffer.copy(IO::Buffer.for('test'), 0)
    #     # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
    #
    # It is safe to copy between memory regions that overlaps each other. In such
    # case, the data is copied as if the data was first copied from the source
    # buffer to a temporary buffer, and then copied from the temporary buffer to the
    # destination buffer.
    #
    #     buffer = IO::Buffer.new(10)
    #     buffer.set_string("0123456789")
    #     buffer.copy(buffer, 3, 7)
    #     # => 7
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
    #     # 0x00000000  30 31 32 30 31 32 33 34 35 36                   0120123456
    #
    def copy: (Buffer source, ?Integer offset, ?Integer length, ?Integer source_offset) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - empty? -> true or false
    # -->
    # If the buffer has 0 size: it is created by ::new with size 0, or with ::for
    # from an empty string. (Note that empty files can't be mapped, so the buffer
    # created with ::map will never be empty.)
    #
    def empty?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - external? -> true or false
    # -->
    # 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.
    #
    def external?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - 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 the buffer is freed, no further operations can't be performed on it.
    #
    # You can resize a freed buffer to re-allocate it.
    #
    #     buffer = IO::Buffer.for('test')
    #     buffer.free
    #     # => #<IO::Buffer 0x0000000000000000+0 NULL>
    #
    #     buffer.get_value(:U8, 0)
    #     # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
    #
    #     buffer.get_string
    #     # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
    #
    #     buffer.null?
    #     # => true
    #
    def free: () -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - get_string([offset, [length, [encoding]]]) -> string
    # -->
    # Read a chunk or all of the buffer into a string, in the specified `encoding`.
    # If no encoding is provided `Encoding::BINARY` is used.
    #
    #     buffer = IO::Buffer.for('test')
    #     buffer.get_string
    #     # => "test"
    #     buffer.get_string(2)
    #     # => "st"
    #     buffer.get_string(2, 1)
    #     # => "s"
    #
    def get_string: (?Integer offset, ?Integer length, ?Encoding encoding) -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - get_value(buffer_type, offset) -> numeric
    # -->
    # Read from buffer a value of `type` at `offset`. `buffer_type` should be one of
    # symbols:
    #
    # *   `:U8`: unsigned integer, 1 byte
    # *   `:S8`: signed integer, 1 byte
    # *   `:u16`: unsigned integer, 2 bytes, little-endian
    # *   `:U16`: unsigned integer, 2 bytes, big-endian
    # *   `:s16`: signed integer, 2 bytes, little-endian
    # *   `:S16`: signed integer, 2 bytes, big-endian
    # *   `:u32`: unsigned integer, 4 bytes, little-endian
    # *   `:U32`: unsigned integer, 4 bytes, big-endian
    # *   `:s32`: signed integer, 4 bytes, little-endian
    # *   `:S32`: signed integer, 4 bytes, big-endian
    # *   `:u64`: unsigned integer, 8 bytes, little-endian
    # *   `:U64`: unsigned integer, 8 bytes, big-endian
    # *   `:s64`: signed integer, 8 bytes, little-endian
    # *   `:S64`: signed integer, 8 bytes, big-endian
    # *   `:f32`: float, 4 bytes, little-endian
    # *   `:F32`: float, 4 bytes, big-endian
    # *   `:f64`: double, 8 bytes, little-endian
    # *   `:F64`: double, 8 bytes, big-endian
    #
    # A buffer type refers specifically to the type of binary buffer that is stored
    # in the buffer. For example, a `:u32` buffer type is a 32-bit unsigned integer
    # in little-endian format.
    #
    #     string = [1.5].pack('f')
    #     # => "\x00\x00\xC0?"
    #     IO::Buffer.for(string).get_value(:f32, 0)
    #     # => 1.5
    #
    def get_value: (int_get_type, Integer offset) -> Integer
                 | (float_get_type, Integer offset) -> Float

    type int_get_type = :U8 | :S8
                      | :u16 | :U16 | :s16 | :S16
                      | :u32 | :U32 | :s32 | :S32
                      | :u64 | :U64 | :s64 | :S64

    type float_get_type = :f32 | :F32 | :f64 | :F64

    # <!--
    #   rdoc-file=io_buffer.c
    #   - hexdump([offset, [length, [width]]]) -> string
    # -->
    # Returns a human-readable string representation of the buffer. The exact format
    # is subject to change.
    #
    #     buffer = IO::Buffer.for("Hello World")
    #     puts buffer.hexdump
    #     # 0x00000000  48 65 6c 6c 6f 20 57 6f 72 6c 64                Hello World
    #
    # As buffers are usually fairly big, you may want to limit the output by
    # specifying the offset and length:
    #
    #     puts buffer.hexdump(6, 5)
    #     # 0x00000006  57 6f 72 6c 64                                  World
    #
    def hexdump: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - inspect -> string
    # -->
    # Inspect the buffer and report useful information about it's internal state.
    # Only a limited portion of the buffer will be displayed in a hexdump style
    # format.
    #
    #     buffer = IO::Buffer.for("Hello World")
    #     puts buffer.inspect
    #     # #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
    #     # 0x00000000  48 65 6c 6c 6f 20 57 6f 72 6c 64                Hello World
    #
    def inspect: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - internal? -> true or false
    # -->
    # 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, and such an operation will typically
    # invalidate all slices, but not always.
    #
    def internal?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - locked { ... }
    # -->
    # Allows to process a buffer in exclusive way, for concurrency-safety. While the
    # block is performed, the buffer is considered locked, and no other code can
    # enter the lock. Also, locked buffer can't be changed with #resize or #free.
    #
    # The following operations acquire a lock: #resize, #free.
    #
    # Locking is not thread safe. It is designed as a safety net around non-blocking
    # system calls. You can only share a buffer between threads with appropriate
    # synchronisation techniques.
    #
    #     buffer = IO::Buffer.new(4)
    #     buffer.locked? #=> false
    #
    #     Fiber.schedule do
    #       buffer.locked do
    #         buffer.write(io) # theoretical system call interface
    #       end
    #     end
    #
    #     Fiber.schedule do
    #       # in `locked': Buffer already locked! (IO::Buffer::LockedError)
    #       buffer.locked do
    #         buffer.set_string("test", 0)
    #       end
    #     end
    #
    def locked: [A] () { (IO::Buffer) -> A } -> A

    # <!--
    #   rdoc-file=io_buffer.c
    #   - locked? -> true or false
    # -->
    # If the buffer is *locked*, meaning it is inside #locked block execution.
    # Locked buffer can't be resized or freed, and another lock can't be acquired on
    # it.
    #
    # Locking is not thread safe, but is a semantic used to ensure buffers don't
    # move while being used by a system call.
    #
    #     buffer.locked do
    #       buffer.write(io) # theoretical system call interface
    #     end
    #
    def locked?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - mapped? -> true or false
    # -->
    # 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, and such an operation will typically
    # invalidate all slices, but not always.
    #
    def mapped?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - null? -> true or false
    # -->
    # If the buffer was freed with #free, transferred with #transfer, or was never
    # allocated in the first place.
    #
    #     buffer = IO::Buffer.new(0)
    #     buffer.null? #=> true
    #
    #     buffer = IO::Buffer.new(4)
    #     buffer.null? #=> false
    #     buffer.free
    #     buffer.null? #=> true
    #
    def null?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - pread(io, from, [length, [offset]]) -> read length or -errno
    # -->
    # Read at least `length` bytes from the `io` starting at the specified `from`
    # position, into the buffer starting at `offset`. If an error occurs, return
    # `-errno`.
    #
    # If `length` is not given or `nil`, it defaults to the size of the buffer minus
    # the offset, i.e. the entire buffer.
    #
    # If `length` is zero, exactly one `pread` operation will occur.
    #
    # If `offset` is not given, it defaults to zero, i.e. the beginning of the
    # buffer.
    #
    #     IO::Buffer.for('test') do |buffer|
    #       p buffer
    #       # =>
    #       # <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #       # 0x00000000  74 65 73 74         test
    #
    #       # take 2 bytes from the beginning of urandom,
    #       # put them in buffer starting from position 2
    #       buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
    #       p buffer
    #       # =>
    #       # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
    #       # 0x00000000  05 35 73 74         te.5
    #     end
    #
    def pread: (untyped, untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - pwrite(io, from, [length, [offset]]) -> written length or -errno
    # -->
    # Write at least `length` bytes from the buffer starting at `offset`, into the
    # `io` starting at the specified `from` position. If an error occurs, return
    # `-errno`.
    #
    # If `length` is not given or `nil`, it defaults to the size of the buffer minus
    # the offset, i.e. the entire buffer.
    #
    # If `length` is zero, exactly one `pwrite` operation will occur.
    #
    # If `offset` is not given, it defaults to zero, i.e. the beginning of the
    # buffer.
    #
    # If the `from` position is beyond the end of the file, the gap will be filled
    # with null (0 value) bytes.
    #
    #     out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
    #     IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
    #
    # This leads to `234` (3 bytes, starting from position 1) being written into
    # `output.txt`, starting from file position 2.
    #
    def pwrite: (untyped, untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - read(io, [length, [offset]]) -> read length or -errno
    # -->
    # Read at least `length` bytes from the `io`, into the buffer starting at
    # `offset`. If an error occurs, return `-errno`.
    #
    # If `length` is not given or `nil`, it defaults to the size of the buffer minus
    # the offset, i.e. the entire buffer.
    #
    # If `length` is zero, exactly one `read` operation will occur.
    #
    # If `offset` is not given, it defaults to zero, i.e. the beginning of the
    # buffer.
    #
    #     IO::Buffer.for('test') do |buffer|
    #       p buffer
    #       # =>
    #       # <IO::Buffer 0x00007fca40087c38+4 SLICE>
    #       # 0x00000000  74 65 73 74         test
    #       buffer.read(File.open('/dev/urandom', 'rb'), 2)
    #       p buffer
    #       # =>
    #       # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
    #       # 0x00000000  05 35 73 74         .5st
    #     end
    #
    def read: (untyped, untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - readonly? -> true or false
    # -->
    # If the buffer is *read only*, meaning the buffer cannot be modified using
    # #set_value, #set_string or #copy and similar.
    #
    # Frozen strings and read-only files create read-only buffers.
    #
    def readonly?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - 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....
    #
    # External buffer (created with ::for), and locked buffer can not be resized.
    #
    def resize: (Integer) -> self

    # <!--
    #   rdoc-file=io_buffer.c
    #   - set_string(string, [offset, [length, [source_offset]]]) -> size
    # -->
    # Efficiently copy from a source String into the buffer, at `offset` using
    # `memmove`.
    #
    #     buf = IO::Buffer.new(8)
    #     # =>
    #     # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00                         ........
    #
    #     # set buffer starting from offset 1, take 2 bytes starting from string's
    #     # second
    #     buf.set_string('test', 1, 2, 1)
    #     # => 2
    #     buf
    #     # =>
    #     # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
    #     # 0x00000000  00 65 73 00 00 00 00 00                         .es.....
    #
    # See also #copy for examples of how buffer writing might be used for changing
    # associated strings and files.
    #
    def set_string: (*untyped) -> untyped

    # <!--
    #   rdoc-file=io_buffer.c
    #   - set_value(type, offset, value) -> offset
    # -->
    # Write to a buffer a `value` of `type` at `offset`. `type` should be one of
    # symbols described in #get_value.
    #
    #     buffer = IO::Buffer.new(8)
    #     # =>
    #     # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     # 0x00000000  00 00 00 00 00 00 00 00
    #
    #     buffer.set_value(:U8, 1, 111)
    #     # => 1
    #
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     # 0x00000000  00 6f 00 00 00 00 00 00                         .o......
    #
    # Note that if the `type` is integer and `value` is Float, the implicit
    # truncation is performed:
    #
    #     buffer = IO::Buffer.new(8)
    #     buffer.set_value(:U32, 0, 2.5)
    #
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
    #     # 0x00000000  00 00 00 02 00 00 00 00
    #     #                      ^^ the same as if we'd pass just integer 2
    #
    def set_value: (int_get_type | float_get_type, Integer offset, Float | Integer value) -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - size -> integer
    # -->
    # Returns the size of the buffer that was explicitly set (on creation with ::new
    # or on #resize), or deduced on buffer's creation from string or file.
    #
    def size: () -> Integer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - slice([offset, [length]]) -> io_buffer
    # -->
    # Produce another IO::Buffer which is a slice (or view into) the current one
    # starting at `offset` bytes and going for `length` bytes.
    #
    # The slicing happens without copying of memory, and the slice keeps being
    # associated with the original buffer's source (string, or file), if any.
    #
    # If the offset is not given, it will be zero. If the offset is negative, it
    # will raise an ArgumentError.
    #
    # If the length is not given, the slice will be as long as the original buffer
    # minus the specified offset. If the length is negative, it will raise an
    # ArgumentError.
    #
    # Raises RuntimeError if the `offset+length` is out of the current buffer's
    # bounds.
    #
    #     string = 'test'
    #     buffer = IO::Buffer.for(string).dup
    #
    #     slice = buffer.slice
    #     # =>
    #     # #<IO::Buffer 0x0000000108338e68+4 SLICE>
    #     # 0x00000000  74 65 73 74                                     test
    #
    #     buffer.slice(2)
    #     # =>
    #     # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
    #     # 0x00000000  73 74                                           st
    #
    #     slice = buffer.slice(1, 2)
    #     # =>
    #     # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
    #     # 0x00000000  65 73                                           es
    #
    #     # Put "o" into 0s position of the slice
    #     slice.set_string('o', 0)
    #     slice
    #     # =>
    #     # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
    #     # 0x00000000  6f 73                                           os
    #
    #     # it is also visible at position 1 of the original buffer
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x00007fc3d31e2d80+4 INTERNAL>
    #     # 0x00000000  74 6f 73 74                                     tost
    #
    def slice: (Integer offset, Integer length) -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - to_s -> string
    # -->
    # Short representation of the buffer. It includes the address, size and symbolic
    # flags. This format is subject to change.
    #
    #     puts IO::Buffer.new(4) # uses to_s internally
    #     # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
    #
    def to_s: () -> String

    # <!--
    #   rdoc-file=io_buffer.c
    #   - transfer -> new_io_buffer
    # -->
    # Transfers ownership of the underlying memory to a new buffer, causing the
    # current buffer to become uninitialized.
    #
    #     buffer = IO::Buffer.new('test')
    #     other = buffer.transfer
    #     other
    #     # =>
    #     # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
    #     # 0x00000000  74 65 73 74                                     test
    #     buffer
    #     # =>
    #     # #<IO::Buffer 0x0000000000000000+0 NULL>
    #     buffer.null?
    #     # => true
    #
    def transfer: () -> Buffer

    # <!--
    #   rdoc-file=io_buffer.c
    #   - valid? -> true or false
    # -->
    # Returns whether the buffer buffer is accessible.
    #
    # A buffer becomes invalid if it is a slice of another buffer (or string) which
    # has been freed or re-allocated at a different address.
    #
    def valid?: () -> bool

    # <!--
    #   rdoc-file=io_buffer.c
    #   - write(io, [length, [offset]]) -> written length or -errno
    # -->
    # Write at least `length` bytes from the buffer starting at `offset`, into the
    # `io`. If an error occurs, return `-errno`.
    #
    # If `length` is not given or `nil`, it defaults to the size of the buffer minus
    # the offset, i.e. the entire buffer.
    #
    # If `length` is zero, exactly one `write` operation will occur.
    #
    # If `offset` is not given, it defaults to zero, i.e. the beginning of the
    # buffer.
    #
    #     out = File.open('output.txt', 'wb')
    #     IO::Buffer.for('1234567').write(out, 3)
    #
    # This leads to `123` being written into `output.txt`
    #
    def write: (untyped, untyped) -> untyped

    private

    # <!--
    #   rdoc-file=io_buffer.c
    #   - 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::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.
    #
    #     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
    #
    def initialize: (?Integer size, ?Integer flags) -> void

    # <!-- rdoc-file=io_buffer.c -->
    # Refers to big endian byte order, where the most significant byte is stored
    # first. See #get_value for more details.
    #
    BIG_ENDIAN: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # 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.
    #
    DEFAULT_SIZE: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Indicates that the memory in the buffer is owned by someone else. See
    # #external? for more details.
    #
    EXTERNAL: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Refers to the byte order of the host machine. See #get_value for more details.
    #
    HOST_ENDIAN: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Indicates that the memory in the buffer is owned by the buffer. See #internal?
    # for more details.
    #
    INTERNAL: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Refers to little endian byte order, where the least significant byte is stored
    # first. See #get_value for more details.
    #
    LITTLE_ENDIAN: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Indicates that the memory in the buffer is locked and cannot be resized or
    # freed. See #locked? and #locked for more details.
    #
    LOCKED: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Indicates that the memory in the buffer is mapped by the operating system. See
    # #mapped? for more details.
    #
    MAPPED: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Refers to network byte order, which is the same as big endian. See #get_value
    # for more details.
    #
    NETWORK_ENDIAN: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # The operating system page size. Used for efficient page-aligned memory
    # allocations.
    #
    PAGE_SIZE: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # 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.
    #
    PRIVATE: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Indicates that the memory in the buffer is read only, and attempts to modify
    # it will fail. See #readonly? for more details.
    #
    READONLY: Integer

    # <!-- rdoc-file=io_buffer.c -->
    # Raised when an operation would resize or re-allocate a locked buffer.
    #
    class LockedError < RuntimeError
    end

    # <!-- rdoc-file=io_buffer.c -->
    # Raised when the buffer cannot be allocated for some reason, or you try to use
    # a buffer that's not allocated.
    #
    class AllocationError < RuntimeError
    end

    # <!-- rdoc-file=io_buffer.c -->
    # Raised when you try to write to a read-only buffer, or resize an external
    # buffer.
    #
    class AccessError < RuntimeError
    end

    # <!-- rdoc-file=io_buffer.c -->
    # Raised if you try to access a buffer slice which no longer references a valid
    # memory range of the underlying source.
    #
    class InvalidatedError < RuntimeError
    end

    # <!-- rdoc-file=io_buffer.c -->
    # Raised if the mask given to a binary operation is invalid, e.g. zero length or
    # overlaps the target buffer.
    #
    class MaskError < ArgumentError
    end
  end
end
