Spec-Zone.ru › Ruby 3.2

класс 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(string)
# =>
# #<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
ЗАКРЕПЛЁННЫЙ
СОПОСТАВЛЕННЫЙ
NETWORK_ENDIAN
РАЗМЕР_СТРАНИЦЫ
ЧАСТНЫЙ
ТОЛЬКО_ДЛЯ_ЧТЕНИЯ
ОБЩИЙ

Публичные методы класса

IO::Buffer.for(string) → readonly io_buffer Показать исходный код
IO::Buffer.for(string) {|io_buffer| ... чтение/запись io_buffer ...}
VALUE
rb_io_buffer_type_for(VALUE klass, VALUE string)
{
    StringValue(string);

    // If the string is frozen, both code paths are okay.
    // If the string is not frozen, if a block is not given, it must be frozen.
    if (rb_block_given_p()) {
        struct io_buffer_for_yield_instance_arguments arguments = {
            .klass = klass,
            .string = string,
            .instance = Qnil,
        };

        return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
    }
    else {
        // This internally returns the source string if it's already frozen.
        string = rb_str_tmp_frozen_acquire(string);
        return io_buffer_for_make_instance(klass, string);
    }
}

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

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

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

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

    rb_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();

    rb_check_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 размер превышает специфичный для ОС размер 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
size_of(data_type) → размер в байтах Показать исходный код
size_of(массив из data_type) → размер в байтах
static VALUE
io_buffer_size_of(VALUE klass, VALUE data_type)
{
    if (RB_TYPE_P(data_type, T_ARRAY)) {
        size_t total = 0;
        for (long i = 0; i < RARRAY_LEN(data_type); i++) {
            total += io_buffer_data_type_size(RB_SYM2ID(RARRAY_AREF(data_type, i)));
        }
        return SIZET2NUM(total);
    } else {
        return SIZET2NUM(io_buffer_data_type_size(RB_SYM2ID(data_type)));
    }
}

Возвращает размер заданного типа данных (ов) в байтах.

Пример:

IO::Buffer.size_of(:u32) # => 4
IO::Buffer.size_of([:u32, :u32]) # => 8

Методы публичного экземпляра

source & mask → io_buffer Показать исходный код
static VALUE
io_buffer_and(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);

    VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
    struct rb_io_buffer *output_data = NULL;
    TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);

    memory_and(output_data->base, data->base, data->size, mask_data->base, mask_data->size);

    return output;
}

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

IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
# =>
# #<IO::Buffer 0x00005589b2758480+4 INTERNAL>
# 0x00000000  31 00 00 34 35 00 00 38 39 00                   1..45..89.
<=>(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.

source ^ mask → io_buffer Показать исходный код
static VALUE
io_buffer_xor(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);

    VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
    struct rb_io_buffer *output_data = NULL;
    TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);

    memory_xor(output_data->base, data->base, data->size, mask_data->base, mask_data->size);

    return output;
}

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

IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
# =>
# #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
# 0x00000000  ce 32 33 cb ca 36 37 c7 c6 30                   .23..67..0
and!(mask) → io_buffer Показать исходный код
static VALUE
io_buffer_and_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);
    io_buffer_check_overlaps(data, mask_data);

    void *base;
    size_t size;
    io_buffer_get_bytes_for_writing(data, &base, &size);

    memory_and_inplace(base, size, mask_data->base, mask_data->size);

    return self;
}

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

source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
# =>
# #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
# 0x00000000  31 32 33 34 35 36 37 38 39 30                   1234567890

source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
# =>
# #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
# 0x00000000  31 00 00 34 35 00 00 38 39 00                   1..45..89.
clear(value = 0, [offset, [length]]) → self Показать исходный код
static VALUE
io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
    rb_check_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)
{
    rb_check_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(string)
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(IO::Buffer.for("boom"), 0)
# => 4
File.read('test.txt')
# => "boom"

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

buffer = IO::Buffer.new(2)
buffer.copy(IO::Buffer.for('test'), 0)
# in `copy': Specified offset+length exceeds source size! (ArgumentError)
each(data_type, [offset, [count]]) {|offset, value| ...} → self Показать исходный код
each(data_type, [offset, [count]]) → enumerator
static VALUE
io_buffer_each(int argc, VALUE *argv, VALUE self)
{
    RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);

    const void *base;
    size_t size;

    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

    ID data_type;
    if (argc >= 1) {
        data_type = RB_SYM2ID(argv[0]);
    } else {
        data_type = RB_IO_BUFFER_DATA_TYPE_U8;
    }

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

    size_t count;
    if (argc >= 3) {
        count = NUM2SIZET(argv[2]);
    } else {
        count = (size - offset) / io_buffer_data_type_size(data_type);
    }

    for (size_t i = 0; i < count; i++) {
        size_t current_offset = offset;
        VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset);
        rb_yield_values(2, SIZET2NUM(current_offset), value);
    }

    return self;
}

Итерируется по буферу, возвращая каждый value типа data_type, начиная с offset.

Если count задано, будут возвращаться только count значения.

Пример:

IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
  puts "#{offset}: #{value}"
end
# 2: 108
# 3: 108
each_byte([offset, [count]]) {|offset, byte| ...} → self Показать исходный код
each_byte([offset, [count]]) → enumerator
static VALUE
io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
{
    RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);

    const void *base;
    size_t size;

    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

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

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

    for (size_t i = 0; i < count; i++) {
        unsigned char *value = (unsigned char *)base + i + offset;
        rb_yield(RB_INT2FIX(*value));
    }

    return self;
}

Итерируется по буферу, возвращая каждый байт, начиная с offset.

Если count задано, будут возвращены только count байта.

Пример:

IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
  puts "#{offset}: #{byte}"
end
# 2: 108
# 3: 108
empty? → 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);
}

Если размер буфера равен 0: он создан с помощью ::new размером 0 или с помощью ::for из пустой строки. (Обратите внимание, что пустые файлы не могут быть отображены, поэтому буфер, созданный с помощью ::map, никогда не будет пустым.)

external? → true или false Показать исходный код
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);
}

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

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

Внешний буфер не может быть изменен.

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)
{
    rb_check_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(data_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. data_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

Тип данных относится к типу двоичных данных, хранящихся в буфере. Например, тип данных :u32 представляет собой 32-битовое беззнаковое целое число в формате little-endian.

Пример:

string = [1.5].pack('f')
# => "\x00\x00\xC0?"
IO::Buffer.for(string).get_value(:f32, 0)
# => 1.5
get_values(data_types, offset) → array Показать исходный код
static VALUE
io_buffer_get_values(VALUE self, VALUE data_types, VALUE _offset)
{
    size_t offset = NUM2SIZET(_offset);

    const void *base;
    size_t size;
    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

    if (!RB_TYPE_P(data_types, T_ARRAY)) {
        rb_raise(rb_eArgError, "Argument data_types should be an array!");
    }

    VALUE array = rb_ary_new_capa(RARRAY_LEN(data_types));

    for (long i = 0; i < RARRAY_LEN(data_types); i++) {
        VALUE type = rb_ary_entry(data_types, i);
        VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
        rb_ary_push(array, value);
    }

    return array;
}

Аналогично get_value, но может обрабатывать несколько типов данных и возвращает массив значений.

Пример:

string = [1.5, 2.5].pack('ff')
IO::Buffer.for(string).get_values([:f32, :f32], 0)
# => [1.5, 2.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;
}
dup → io_buffer Show source
clone → io_buffer
static VALUE
rb_io_buffer_initialize_copy(VALUE self, VALUE source)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    const void *source_base;
    size_t source_size;

    rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);

    io_buffer_initialize(data, NULL, source_size, io_flags_for_size(source_size), Qnil);

    return io_buffer_copy_from(data, source_base, source_size, 0, NULL);
}

Создать внутреннюю копию исходного буфера. Изменения копии не повлияют на исходный буфер.

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
inspect() Show source
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 or false Show source
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 { ... } Show source
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.

Следующие операции приобретают блокировку: 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("test", 0)
  end
end
locked? → true or false Show source
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 or false Show source
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.

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

not! → io_buffer Show source
static VALUE
io_buffer_not_inplace(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    void *base;
    size_t size;
    io_buffer_get_bytes_for_writing(data, &base, &size);

    memory_not_inplace(base, size);

    return self;
}

Изменить исходный буфер на месте, применив побитовую операцию NOT к источнику.

source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
# =>
# #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
# 0x00000000  31 32 33 34 35 36 37 38 39 30                   1234567890

source.not!
# =>
# #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
# 0x00000000  ce cd cc cb ca c9 c8 c7 c6 cf                   ..........
null? → true or false Show source
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 или никогда не выделялся.

or!(mask) → io_buffer Show source
static VALUE
io_buffer_or_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);
    io_buffer_check_overlaps(data, mask_data);

    void *base;
    size_t size;
    io_buffer_get_bytes_for_writing(data, &base, &size);

    memory_or_inplace(base, size, mask_data->base, mask_data->size);

    return self;
}

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

source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
# =>
# #<IO::Buffer 0x000056307a272350+10 INTERNAL>
# 0x00000000  31 32 33 34 35 36 37 38 39 30                   1234567890

source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
# =>
# #<IO::Buffer 0x000056307a272350+10 INTERNAL>
# 0x00000000  ff 32 33 ff ff 36 37 ff ff 30                   .23..67..0
pread(io, from, length, [offset]) → read length or -errno Show source
static VALUE
io_buffer_pread(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 3, 4);

    VALUE io = argv[0];
    rb_off_t from = NUM2OFFT(argv[1]);

    size_t length;
    if (rb_int_negative_p(argv[2])) {
        rb_raise(rb_eArgError, "Length can't be negative!");
    }
    length = NUM2SIZET(argv[2]);

    size_t offset = 0;
    if (argc >= 4) {
        if (rb_int_negative_p(argv[3])) {
            rb_raise(rb_eArgError, "Offset can't be negative!");
        }

        offset = NUM2SIZET(argv[3]);
    }

    return rb_io_buffer_pread(self, io, from, length, offset);
}

Прочитать не более length байт из io в буфер, начиная с from, и поместить их в буфер, начиная с указанного offset. В случае ошибки вернуть -errno.

Если offset не указан, поместить в начало буфера.

Пример:

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
pwrite(io, from, length, [offset]) → written length or -errno Show source
static VALUE
io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 3, 4);

    VALUE io = argv[0];
    rb_off_t from = NUM2OFFT(argv[1]);

    size_t length;
    if (rb_int_negative_p(argv[2])) {
        rb_raise(rb_eArgError, "Length can't be negative!");
    }
    length = NUM2SIZET(argv[2]);

    size_t offset = 0;
    if (argc >= 4) {
        if (rb_int_negative_p(argv[3])) {
            rb_raise(rb_eArgError, "Offset can't be negative!");
        }

        offset = NUM2SIZET(argv[3]);
    }

    return rb_io_buffer_pwrite(self, io, from, length, offset);
}

Записать length байт из буфера в io, начиная с offset в буфере. В случае ошибки вернуть -errno.

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

out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)

Это приводит к записи 234 (3 байта, начиная с позиции 1) в output.txt, начиная с позиции файла 2.

read(io, [length, [offset]]) → read length or -errno Show source
static VALUE
io_buffer_read(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 2, 3);

    VALUE io = argv[0];

    size_t length;
    if (argc >= 2) {
        if (rb_int_negative_p(argv[1])) {
            rb_raise(rb_eArgError, "Length can't be negative!");
        }

        length = NUM2SIZET(argv[1]);
    }

    size_t offset = 0;
    if (argc >= 3) {
        if (rb_int_negative_p(argv[2])) {
            rb_raise(rb_eArgError, "Offset can't be negative!");
        }

        offset = NUM2SIZET(argv[2]);
    }

    return rb_io_buffer_read(self, io, length, offset);
}

Прочитать не более length байт из io в буфер, начиная с offset. В случае ошибки вернуть -errno.

Если length не указан, читать до конца буфера.

Если offset не указан, читать с начала буфера.

Если length равно 0, ничего не читать.

Пример:

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
readonly? → true or false Show source
static VALUE
io_buffer_readonly_p(VALUE self)
{
    return RBOOL(rb_io_buffer_readonly_p(self));
}

Если буфер только для чтения, это означает, что буфер не может быть изменен с помощью set_value, set_string или copy и подобных.

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

resize(new_size) → self Show source
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(string, [offset, [length, [source_offset]]]) → size Show source
static VALUE
io_buffer_set_string(int argc, VALUE *argv, VALUE self)
{
    rb_check_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);
}

Эффективно копирует данные из исходного String в буфер, в offset используя memcpy.

buf = IO::Buffer.new(8)
# =>
# #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
# 0x00000000  00 00 00 00 00 00 00 00                         ........

# set data 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.....

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

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
set_values(data_types, offset, values) → offset Показать исходный код
static VALUE
io_buffer_set_values(VALUE self, VALUE data_types, VALUE _offset, VALUE values)
{
    if (!RB_TYPE_P(data_types, T_ARRAY)) {
        rb_raise(rb_eArgError, "Argument data_types should be an array!");
    }

    if (!RB_TYPE_P(values, T_ARRAY)) {
        rb_raise(rb_eArgError, "Argument values should be an array!");
    }

    if (RARRAY_LEN(data_types) != RARRAY_LEN(values)) {
        rb_raise(rb_eArgError, "Argument data_types and values should have the same length!");
    }

    size_t offset = NUM2SIZET(_offset);

    void *base;
    size_t size;
    rb_io_buffer_get_bytes_for_writing(self, &base, &size);

    for (long i = 0; i < RARRAY_LEN(data_types); i++) {
        VALUE type = rb_ary_entry(data_types, i);
        VALUE value = rb_ary_entry(values, i);
        rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
    }

    return SIZET2NUM(offset);
}

Записать values значений data_types по адресу offset в буфер. data_types должен быть массивом символов, как описано в get_value. values — массив значений для записи.

Пример:

buffer = IO::Buffer.new(8)
buffer.set_values([:U8, :U16], 0, [1, 2])
buffer
# =>
# #<IO::Buffer 0x696f717561746978+8 INTERNAL>
# 0x00000000  01 00 02 00 00 00 00 00                         ........
shared? → true or false Показать исходный код
static VALUE
rb_io_buffer_shared_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_SHARED);
}

Если буфер является разделяемым, то есть он ссылается на память, которая может быть разделена с другими процессами (и, следовательно, может изменяться без локального изменения).

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 = 0, [length]]) → io_buffer Показать исходный код
static VALUE
io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
    rb_check_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 offset = 0, length = 0;

    if (argc > 0) {
        if (rb_int_negative_p(argv[0])) {
            rb_raise(rb_eArgError, "Offset can't be negative!");
        }

        offset = NUM2SIZET(argv[0]);
    }

    if (argc > 1) {
        if (rb_int_negative_p(argv[1])) {
            rb_raise(rb_eArgError, "Length can't be negative!");
        }

        length = NUM2SIZET(argv[1]);
    } else {
        length = data->size - offset;
    }

    return rb_io_buffer_slice(data, self, offset, length);
}

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

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

Если смещение не указано, оно будет нулевым. Если смещение отрицательное, будет возбуждено исключение ArgumentError.

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

Возбуждает исключение RuntimeError, если offset+length выходит за пределы текущего буфера.

Пример:

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

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 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_SHARED) {
        rb_str_cat2(result, " SHARED");
    }

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

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

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

values(data_type, [offset, [count]]) → array Показать исходный код
static VALUE
io_buffer_values(int argc, VALUE *argv, VALUE self)
{
    const void *base;
    size_t size;

    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

    ID data_type;
    if (argc >= 1) {
        data_type = RB_SYM2ID(argv[0]);
    } else {
        data_type = RB_IO_BUFFER_DATA_TYPE_U8;
    }

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

    size_t count;
    if (argc >= 3) {
        count = NUM2SIZET(argv[2]);
    } else {
        count = (size - offset) / io_buffer_data_type_size(data_type);
    }

    VALUE array = rb_ary_new_capa(count);

    for (size_t i = 0; i < count; i++) {
        VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset);
        rb_ary_push(array, value);
    }

    return array;
}

Возвращает массив значений типа data_type, начиная с offset.

Если count задано, будут возвращены только count значений.

Пример:

IO::Buffer.for("Hello World").values(:U8, 2, 2)
# => [108, 108]
write(io, length, [offset]) → written length or -errno Показать исходный код
static VALUE
io_buffer_write(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 2, 3);

    VALUE io = argv[0];

    size_t length;
    if (argc >= 2) {
        if (rb_int_negative_p(argv[1])) {
            rb_raise(rb_eArgError, "Length can't be negative!");
        }

        length = NUM2SIZET(argv[1]);
    }

    size_t offset = 0;
    if (argc >= 3) {
        if (rb_int_negative_p(argv[2])) {
            rb_raise(rb_eArgError, "Offset can't be negative!");
        }

        offset = NUM2SIZET(argv[2]);
    }

    return rb_io_buffer_write(self, io, length, offset);
}

Записать length байт из буфера в io, начиная с offset байта в буфере. В случае ошибки вернуть -errno.

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

out = File.open('output.txt', 'wb')
IO::Buffer.for('1234567').write(out, 3)

Это приведет к записи 123 в output.txt

xor!(mask) → io_buffer Показать исходный код
static VALUE
io_buffer_xor_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);
    io_buffer_check_overlaps(data, mask_data);

    void *base;
    size_t size;
    io_buffer_get_bytes_for_writing(data, &base, &size);

    memory_xor_inplace(base, size, mask_data->base, mask_data->size);

    return self;
}

Изменить исходный буфер на месте, применив побитовое XOR-исключающее ИЛИ к исходному, используя маску, повторяя по необходимости.

source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
# =>
# #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
# 0x00000000  31 32 33 34 35 36 37 38 39 30                   1234567890

source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
# =>
# #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
# 0x00000000  ce 32 33 cb ca 36 37 c7 c6 30                   .23..67..0
source | mask → io_buffer Показать исходный код
static VALUE
io_buffer_or(VALUE self, VALUE mask)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    struct rb_io_buffer *mask_data = NULL;
    TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);

    io_buffer_check_mask(mask_data);

    VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
    struct rb_io_buffer *output_data = NULL;
    TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);

    memory_or(output_data->base, data->base, data->size, mask_data->base, mask_data->size);

    return output;
}

Сгенерировать новый буфер того же размера, что и исходный, применив побитовое OR-логическое ИЛИ к исходному, используя маску, повторяя по необходимости.

IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
# =>
# #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
# 0x00000000  ff 32 33 ff ff 36 37 ff ff 30                   .23..67..0
~source → io_buffer Показать исходный код
static VALUE
io_buffer_not(VALUE self)
{
    struct rb_io_buffer *data = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);

    VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
    struct rb_io_buffer *output_data = NULL;
    TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);

    memory_not(output_data->base, data->base, data->size);

    return output;
}

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

~IO::Buffer.for("1234567890")
# =>
# #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
# 0x00000000  ce cd cc cb ca c9 c8 c7 c6 cf                   ..........

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