Spec-Zone.ru › Ruby 3.1

класс IO::Buffer

Родитель:
Объект
Включенные модули:
Comparable

IO::Buffer — это эффективный буфер низкого уровня для ввода/вывода. Есть три способа использования буфера:

  • Создать пустой буфер с помощью ::new, заполнить его данными с помощью copy или set_value, set_string, получить данные с помощью get_string;

  • Создать буфер, сопоставленный с какой-то строкой с помощью ::for, затем его можно использовать для чтения с помощью get_string или get_value, и для записи (запись также изменит исходную строку);

  • Создать буфер, сопоставленный с каким-то файлом с помощью ::map, затем его можно использовать для чтения и записи в подлежащий файл.

Взаимодействие с памятью строк и файлов выполняется с помощью эффективных механизмов C низкого уровня, таких как ‘memcpy`.

Класс предназначен для реализации более высокоуровневых механизмов, таких как Fiber::SchedulerInterface#io_read и Fiber::SchedulerInterface#io_write.

Примеры использования:

Пустой буфер:

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"

Буфер из строки:

string = 'data'
buffer = IO::Buffer.for(str)
#  =>
# #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
# ...
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---"

Буфер из файла:

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"

Класс является экспериментальным, и интерфейс может быть изменён.

Константы

BIG_ENDIAN
DEFAULT_SIZE
EXTERNAL
HOST_ENDIAN
INTERNAL
LITTLE_ENDIAN
LOCKED
MAPPED
NETWORK_ENDIAN
PAGE_SIZE
PRIVATE
READONLY

Открытые методы класса

IO::Buffer.for(string) → io_buffer Показать исходный код
VALUE
rb_io_buffer_type_for(VALUE klass, VALUE string)
{
    io_buffer_experimental();

    StringValue(string);

    VALUE instance = rb_io_buffer_type_allocate(klass);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);

    rb_str_locktmp(string);

    enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;

    if (RB_OBJ_FROZEN(string))
        flags |= RB_IO_BUFFER_READONLY;

    io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);

    return instance;
}

Создаёт IO::Buffer из памяти данной строки. Буфер остаётся связанным со строкой, и запись в буфер обновит содержимое строки.

До тех пор, пока free не будет вызван для буфера, явно или через сборщик мусора, исходная строка будет заблокирована и не может быть изменена.

Если строка заморожена, будет создан только для чтения буфер, который нельзя изменить.

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)
IO::Buffer.map(file, [size, [offset, [flags]]]) → io_buffer Показать исходный код
static VALUE
io_buffer_map(int argc, VALUE *argv, VALUE klass)
{
    if (argc < 1 || argc > 4) {
        rb_error_arity(argc, 2, 4);
    }

    // We might like to handle a string path?
    VALUE io = argv[0];

    size_t size;
    if (argc >= 2 && !RB_NIL_P(argv[1])) {
        size = RB_NUM2SIZE(argv[1]);
    }
    else {
        off_t file_size = rb_file_size(io);

        // Compiler can confirm that we handled file_size < 0 case:
        if (file_size < 0) {
            rb_raise(rb_eArgError, "Invalid negative file size!");
        }
        // Here, we assume that file_size is positive:
        else if ((uintmax_t)file_size > SIZE_MAX) {
            rb_raise(rb_eArgError, "File larger than address space!");
        }
        else {
            // This conversion should be safe:
            size = (size_t)file_size;
        }
    }

    off_t offset = 0;
    if (argc >= 3) {
        offset = NUM2OFFT(argv[2]);
    }

    enum rb_io_buffer_flags flags = 0;
    if (argc >= 4) {
        flags = RB_NUM2UINT(argv[3]);
    }

    return rb_io_buffer_map(io, size, offset, flags);
}

Создаёт IO::Buffer для чтения из file путём сопоставления файла с памятью. file должен быть экземпляром File , открытым для чтения.

Можно указать необязательные size и offset сопоставления.

По умолчанию буфер будет неизменяемым (только для чтения); для создания изменяемого сопоставления необходимо открыть файл в режиме чтения/записи и явно передать flags аргумент без 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"

Обратите внимание, что на некоторых операционных системах может отсутствовать согласованность кэша между сопоставленными буферами и файловыми операциями чтения.

IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) → io_buffer Показать исходный код
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
    io_buffer_experimental();

    if (argc < 0 || argc > 2) {
        rb_error_arity(argc, 0, 2);
    }

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    size_t size;

    if (argc > 0) {
        size = RB_NUM2SIZE(argv[0]);
    } else {
        size = RUBY_IO_BUFFER_DEFAULT_SIZE;
    }

    enum rb_io_buffer_flags flags = 0;
    if (argc >= 2) {
        flags = RB_NUM2UINT(argv[1]);
    }
    else {
        flags |= io_flags_for_size(size);
    }

    io_buffer_initialize(data, NULL, size, flags, Qnil);

    return self;
}

Создаёт новый нулевой IO::Buffer размером size байт. По умолчанию буфер будет внутренним: непосредственно выделенный блок памяти. Но если запрашиваемый size больше, чем OS-специфичный IO::Buffer::PAGE_SIZE, буфер будет выделен с помощью механизма виртуальной памяти (анонимный mmap на Unix, VirtualAlloc на Windows). Поведение можно заставить при передаче IO::Buffer::MAPPED как второго параметра.

Примеры

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

Публичные методы экземпляров

<=>(other) → true или false Показать исходный код
static VALUE
rb_io_buffer_compare(VALUE self, VALUE other)
{
    const void *ptr1, *ptr2;
    size_t size1, size2;

    rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
    rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);

    if (size1 < size2) {
        return RB_INT2NUM(-1);
    }

    if (size1 > size2) {
        return RB_INT2NUM(1);
    }

    return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
}

Буферы сравниваются по размеру и точному содержимому памяти, на которую они ссылаются, используя memcmp.

clear(value = 0, [offset, [length]]) → self Показать исходный код
static VALUE
io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
    if (argc > 3) rb_error_arity(argc, 0, 3);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    uint8_t value = 0;
    if (argc >= 1) {
        value = NUM2UINT(argv[0]);
    }

    size_t offset = 0;
    if (argc >= 2) {
        offset = NUM2SIZET(argv[1]);
    }

    size_t length;
    if (argc >= 3) {
        length = NUM2SIZET(argv[2]);
    } else {
        length = data->size - offset;
    }

    rb_io_buffer_clear(self, value, offset, length);

    return self;
}

Заполнить буфер значением value, начиная с offset и проходя length байт.

buffer = IO::Buffer.for('test')
# =>
#   <IO::Buffer 0x00007fca40087c38+4 SLICE>
#   0x00000000  74 65 73 74         test

buffer.clear
# =>
#   <IO::Buffer 0x00007fca40087c38+4 SLICE>
#   0x00000000  00 00 00 00         ....

buf.clear(1) # fill with 1
# =>
#   <IO::Buffer 0x00007fca40087c38+4 SLICE>
#   0x00000000  01 01 01 01         ....

buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
# =>
#   <IO::Buffer 0x00007fca40087c38+4 SLICE>
#   0x00000000  01 02 02 01         ....

buffer.clear(2, 1) # fill with 2, starting from offset 1
# =>
#   <IO::Buffer 0x00007fca40087c38+4 SLICE>
#   0x00000000  01 02 02 02         ....
copy(source, [offset, [length, [source_offset]]]) → size Показать исходный код
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
{
    if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE source = argv[0];
    const void *source_base;
    size_t source_size;

    rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);

    return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
}

Эффективно копировать данные из исходного IO::Buffer в буфер по адресу offset с помощью memcpy. Для копирования экземпляров String, см. 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 data 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 можно использовать для размещения данных в строках, связанных с буфером:

string= "data:    "
# => "data:    "
buffer = IO::Buffer.for(str)
buffer.copy(IO::Buffer.for("test"), 5)
# => 4
string
# => "data:test"

Попытка скопировать данные в неизменяемый буфер завершится ошибкой:

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)

См. ::map для получения подробностей о создании изменяемых отображений файлов, это сработает:

buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
buffer.copy("boom", 0)
# => 4
File.read('test.txt')
# => "boom"

Попытка скопировать данные, которые потребуют размещения за пределами границ буфера, завершится ошибкой:

buffer = IO::Buffer.new(2)
buffer.copy('test', 0)
# in `copy': Specified offset+length exceeds source size! (ArgumentError)
external? → true или false Показать исходный код
static VALUE
rb_io_buffer_empty_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->size == 0);
}
If the buffer is _external_, meaning it references from 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?() Показать исходный код
static VALUE
rb_io_buffer_external_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
}
free → self Показать исходный код
VALUE
rb_io_buffer_free(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    if (data->flags & RB_IO_BUFFER_LOCKED) {
        rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
    }

    io_buffer_free(data);

    return self;
}

Если буфер ссылается на память, освободите её обратно операционной системе.

  • для отображённого буфера (например, из файла): размапить.

  • для буфера, созданного с нуля: освободить память.

  • для буфера, созданного из строки: отменить ассоциацию.

После освобождения буфера, на нём больше нельзя выполнять операции.

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

Вы можете изменить размер освобождённого буфера, чтобы повторно выделить его.

get_string([offset, [length, [encoding]]]) → string Показать исходный код
static VALUE
io_buffer_get_string(int argc, VALUE *argv, VALUE self)
{
    if (argc > 3) rb_error_arity(argc, 0, 3);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    const void *base;
    size_t size;
    io_buffer_get_bytes_for_reading(data, &base, &size);

    size_t offset = 0;
    size_t length = size;
    rb_encoding *encoding = rb_ascii8bit_encoding();

    if (argc >= 1) {
        offset = NUM2SIZET(argv[0]);
    }

    if (argc >= 2 && !RB_NIL_P(argv[1])) {
        length = NUM2SIZET(argv[1]);
    } else {
        length = size - offset;
    }

    if (argc >= 3) {
        encoding = rb_find_encoding(argv[2]);
    }

    io_buffer_validate_range(data, offset, length);

    return rb_enc_str_new((const char*)base + offset, length, encoding);
}

Прочитать часть или весь буфер в строку в указанной encoding. Если кодировка не указана, используется Encoding::BINARY.

buffer = IO::Buffer.for('test')
buffer.get_string
# => "test"
buffer.get_string(2)
# => "st"
buffer.get_string(2, 1)
# => "s"
get_value(type, offset) → numeric Показать исходный код
static VALUE
io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
    const void *base;
    size_t size;
    size_t offset = NUM2SIZET(_offset);

    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

    return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
}

Прочитать из буфера значение типа type по адресу offset. type должно быть одним из символов:

  • :U8: беззнаковое целое число, 1 байт

  • :S8: со знаком целое число, 1 байт

  • :u16: беззнаковое целое число, 2 байта, little-endian

  • :U16: беззнаковое целое число, 2 байта, big-endian

  • :s16: со знаком целое число, 2 байта, little-endian

  • :S16: со знаком целое число, 2 байта, big-endian

  • :u32: беззнаковое целое число, 4 байта, little-endian

  • :U32: беззнаковое целое число, 4 байта, big-endian

  • :s32: со знаком целое число, 4 байта, little-endian

  • :S32: со знаком целое число, 4 байта, big-endian

  • :u64: беззнаковое целое число, 8 байт, little-endian

  • :U64: беззнаковое целое число, 8 байт, big-endian

  • :s64: со знаком целое число, 8 байт, little-endian

  • :S64: со знаком целое число, 8 байт, big-endian

  • :f32: float, 4 байта, little-endian

  • :F32: float, 4 байта, big-endian

  • :f64: double, 8 байт, little-endian

  • :F64: double, 8 байт, big-endian

Пример:

string = [1.5].pack('f')
# => "\x00\x00\xC0?"
IO::Buffer.for(string).get_value(:f32, 0)
# => 1.5
hexdump() Показать исходный код
static VALUE
rb_io_buffer_hexdump(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE result = Qnil;

    if (io_buffer_validate(data) && data->base) {
        result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);

        io_buffer_hexdump(result, 16, data->base, data->size, 1);
    }

    return result;
}
inspect() Показать исходный код
VALUE
rb_io_buffer_inspect(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE result = rb_io_buffer_to_s(self);

    if (io_buffer_validate(data)) {
        // Limit the maximum size genearted by inspect.
        if (data->size <= 256) {
            io_buffer_hexdump(result, 16, data->base, data->size, 0);
        }
    }

    return result;
}
internal? → true или false Показать исходный код
static VALUE
rb_io_buffer_internal_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
}

Если буфер является внутренним, что означает, что он ссылается на память, выделенную самим буфером.

Внутренний буфер не связан с какой-либо внешней памятью (например, строкой) или отображением файла.

Внутренние буферы создаются с помощью ::new и являются по умолчанию, когда запрашиваемый размер меньше IO::Buffer::PAGE_SIZE и не было запрошено отображение при создании.

Внутренние буферы могут быть изменены в размере, и такая операция обычно делает недействительными все срезы, но не всегда.

locked { ... } Показать исходный код
VALUE
rb_io_buffer_locked(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    if (data->flags & RB_IO_BUFFER_LOCKED) {
        rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
    }

    data->flags |= RB_IO_BUFFER_LOCKED;

    VALUE result = rb_yield(self);

    data->flags &= ~RB_IO_BUFFER_LOCKED;

    return result;
}

Позволяет обрабатывать буфер в эксклюзивном режиме для обеспечения сохранности в условиях параллельного выполнения. Пока выполняется блок, буфер считается заблокированным, и другой код не может получить доступ к блокировке. Также заблокированный буфер не может быть изменён с помощью resize или free.

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(...)
  end
end

Следующие операции получают доступ к блокировке: resize, free.

Блокировка не является потокобезопасной. Она предназначена как страховочная сетка вокруг асинхронных системных вызовов. Вы можете обмениваться буфером между потоками только с помощью соответствующих методов синхронизации.

locked? → true или false Показать исходный код
static VALUE
rb_io_buffer_locked_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
}

Если буфер заблокирован, что означает, что он находится внутри блока locked исполнения. Заблокированный буфер не может быть изменен в размере или освобожден, и на него не может быть получена другая блокировка.

Блокировка не является потокобезопасной, но является семантикой, используемой для обеспечения того, что буферы не перемещаются во время использования системным вызовом.

buffer.locked do
  buffer.write(io) # theoretical system call interface
end
mapped? → true или false Показать исходный код
static VALUE
rb_io_buffer_mapped_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
}

Если буфер отображён, что означает, что он ссылается на память, отображенную буфером.

Отображённые буферы либо анонимные, если созданы с помощью ::new с флагом IO::Buffer::MAPPED или если размер был как минимум IO::Buffer::PAGE_SIZE, либо поддерживаются файлом, если созданы с помощью ::map.

Отображённые буферы обычно могут быть изменены в размере, и такая операция обычно делает недействительными все срезы, но не всегда.

null? → true или false Показать исходный код
static VALUE
rb_io_buffer_null_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(data->base == NULL);
}

Если буфер был освобождён с помощью free или не был выделен в первую очередь.

pread(p1, p2, p3) Показать исходный код
static VALUE
io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
{
    return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
}
pwrite(p1, p2, p3) Показать исходный код
static VALUE
io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
{
    return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
}
read(p1, p2) Показать исходный код
static VALUE
io_buffer_read(VALUE self, VALUE io, VALUE length)
{
    return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
}
readonly?() Показать исходный код
static VALUE
io_buffer_readonly_p(VALUE self)
{
    return RBOOL(rb_io_buffer_readonly_p(self));
}
resize(new_size) → self Показать исходный код
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
    rb_io_buffer_resize(self, NUM2SIZET(size));

    return self;
}

Изменяет размер буфера на new_size байт, сохраняя содержимое. В зависимости от старого и нового размера, область памяти, связанная с буфером, может быть расширена или перераспределена по новому адресу с копированием содержимого.

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....

Внешний буфер (созданный с помощью ::for), и заблокированный буфер не могут быть изменены в размере.

set_string(*args) Показать исходный код
static VALUE
io_buffer_set_string(int argc, VALUE *argv, VALUE self)
{
    if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE string = rb_str_to_str(argv[0]);

    const void *source_base = RSTRING_PTR(string);
    size_t source_size = RSTRING_LEN(string);

    return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
}
set_value(type, offset, value) → offset Показать исходный код
static VALUE
io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
    void *base;
    size_t size;
    size_t offset = NUM2SIZET(_offset);

    rb_io_buffer_get_bytes_for_writing(self, &base, &size);

    rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);

    return SIZET2NUM(offset);
}

Записывает в буфер value типа type по адресу offset. type должен быть одним из символов, описанных в 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......

Обратите внимание, что если type является целым числом, а value является Float, выполняется неявное усечение:

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
size → integer Показать исходный код
VALUE
rb_io_buffer_size(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return SIZET2NUM(data->size);
}

Возвращает размер буфера, который был явно задан (при создании с помощью ::new или при resize), или определён при создании буфера из строки или файла.

slice(offset, length) → io_buffer Показать исходный код
VALUE
rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
{
    // TODO fail on negative offets/lengths.
    size_t offset = NUM2SIZET(_offset);
    size_t length = NUM2SIZET(_length);

    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    io_buffer_validate_range(data, offset, length);

    VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
    struct rb_io_buffer *slice = NULL;
    TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);

    slice->base = (char*)data->base + offset;
    slice->size = length;

    // The source should be the root buffer:
    if (data->source != Qnil)
        slice->source = data->source;
    else
        slice->source = self;

    return instance;
}

Создаёт другой IO::Buffer, который является слайсом (или представлением) текущего, начиная с offset байт и охватывая length байт.

Срез выполняется без копирования памяти, и срез сохраняет связь с исходным источником буфера (строка или файл), если таковой имеется.

Вызывает RuntimeError, если <tt>offset+length<tt> выходит за пределы текущего буфера.

string = 'test'
buffer = IO::Buffer.for(string)

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 SLICE>
#  0x00000000  74 6f 73 74                                     tost

# ...and original string
string
# => tost
to_s → string Показать исходный код
VALUE
rb_io_buffer_to_s(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE result = rb_str_new_cstr("#<");

    rb_str_append(result, rb_class_name(CLASS_OF(self)));
    rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);

    if (data->base == NULL) {
        rb_str_cat2(result, " NULL");
    }

    if (data->flags & RB_IO_BUFFER_EXTERNAL) {
        rb_str_cat2(result, " EXTERNAL");
    }

    if (data->flags & RB_IO_BUFFER_INTERNAL) {
        rb_str_cat2(result, " INTERNAL");
    }

    if (data->flags & RB_IO_BUFFER_MAPPED) {
        rb_str_cat2(result, " MAPPED");
    }

    if (data->flags & RB_IO_BUFFER_LOCKED) {
        rb_str_cat2(result, " LOCKED");
    }

    if (data->flags & RB_IO_BUFFER_READONLY) {
        rb_str_cat2(result, " READONLY");
    }

    if (data->source != Qnil) {
        rb_str_cat2(result, " SLICE");
    }

    if (!io_buffer_validate(data)) {
        rb_str_cat2(result, " INVALID");
    }

    return rb_str_cat2(result, ">");
}

Краткое представление буфера. Включает адрес, размер и символьные флаги. Этот формат может быть изменён.

puts IO::Buffer.new(4) # uses to_s internally
# #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
transfer → new_io_buffer Показать исходный код
VALUE
rb_io_buffer_transfer(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    if (data->flags & RB_IO_BUFFER_LOCKED) {
        rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
    }

    VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
    struct rb_io_buffer *transferred;
    TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);

    *transferred = *data;
    io_buffer_zero(data);

    return instance;
}

Переносит владение новому буферу, освобождая текущий.

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
valid? → true or false Показать исходный код
static VALUE
rb_io_buffer_valid_p(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    return RBOOL(io_buffer_validate(data));
}

Возвращает, доступны ли данные буфера.

Буфер становится недоступным, если он является слайсом другого буфера, который был освобождён.

write(p1, p2) Показать исходный код
static VALUE
io_buffer_write(VALUE self, VALUE io, VALUE length)
{
    return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
}

Ruby Core © 1993–2022 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API