класс IO::Buffer
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
Открытые методы класса
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)
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"
Обратите внимание, что на некоторых операционных системах может отсутствовать согласованность кэша между сопоставленными буферами и файловыми операциями чтения.
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
Публичные методы экземпляров
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.
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 ....
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)
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.
Внешний буфер не может быть изменен в размере.
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);
} 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
Вы можете изменить размер освобождённого буфера, чтобы повторно выделить его.
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"
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
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;
} 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;
} 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 и не было запрошено отображение при создании.
Внутренние буферы могут быть изменены в размере, и такая операция обычно делает недействительными все срезы, но не всегда.
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.
Блокировка не является потокобезопасной. Она предназначена как страховочная сетка вокруг асинхронных системных вызовов. Вы можете обмениваться буфером между потоками только с помощью соответствующих методов синхронизации.
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
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.
Отображённые буферы обычно могут быть изменены в размере, и такая операция обычно делает недействительными все срезы, но не всегда.
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 или не был выделен в первую очередь.
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));
} 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));
} static VALUE
io_buffer_read(VALUE self, VALUE io, VALUE length)
{
return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
} static VALUE
io_buffer_readonly_p(VALUE self)
{
return RBOOL(rb_io_buffer_readonly_p(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), и заблокированный буфер не могут быть изменены в размере.
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);
} 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
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), или определён при создании буфера из строки или файла.
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
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>
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
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));
} Возвращает, доступны ли данные буфера.
Буфер становится недоступным, если он является слайсом другого буфера, который был освобождён.
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.