类 IO::Buffer

IO::Buffer 是一个高效的零拷贝缓冲区,用于输入/输出。一些典型的用例:

  • 使用 ::new 创建一个空缓冲区,使用 copyset_valueset_string 填充缓冲区,使用 get_string 获取缓冲区,或使用 write 直接写入某个文件。

  • 使用 ::for 创建一个映射到某个字符串的缓冲区,然后它可以用于读取(使用 get_stringget_value)和写入(写入也会改变源字符串)。

  • 使用 ::map 创建一个映射到某个文件的缓冲区,然后它可以用于读取和写入底层文件。

  • 使用 ::string 创建一个固定大小的字符串,然后使用 read 读取到其中,或使用 set_value 修改它。

与字符串和文件内存的交互通过高效的底层 C 机制(如 `memcpy`)执行。

该类旨在作为实现更高级机制(如 Fiber::Scheduler#io_readFiber::Scheduler#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 = 'buffer'
buffer = IO::Buffer.for(string)
# =>
# #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
# ...
buffer
# =>
# #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
# 0x00000000  64 61 74 61                                     buffer

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 buffer')
# => 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--- buffer"

该类是实验性的,接口可能会发生变化,这在文件映射中尤其如此,文件映射可能在将来完全删除。

常量

BIG_ENDIAN

指大端字节序,其中最高有效字节存储在最前面。有关更多详细信息,请参阅 get_value

DEFAULT_SIZE

默认缓冲区大小,通常是 PAGE_SIZE 的(小)倍数。可以通过设置 RUBY_IO_BUFFER_DEFAULT_SIZE 环境变量来显式指定。

EXTERNAL

表示缓冲区中的内存由其他人拥有。有关更多详细信息,请参阅 external?

HOST_ENDIAN

指主机系统的字节序。有关更多详细信息,请参阅 get_value

INTERNAL

表示缓冲区中的内存由缓冲区拥有。有关更多详细信息,请参阅 internal?

LITTLE_ENDIAN

指小端字节序,其中最低有效字节存储在最前面。有关更多详细信息,请参阅 get_value

LOCKED

表示缓冲区中的内存已锁定,无法调整大小或释放。有关更多详细信息,请参阅 locked?locked

MAPPED

表示缓冲区中的内存由操作系统映射。有关更多详细信息,请参阅 mapped?

NETWORK_ENDIAN

指网络字节序,与大端相同。有关更多详细信息,请参阅 get_value

PAGE_SIZE

操作系统页面大小。用于高效的页面对齐内存分配。

PRIVATE

表示缓冲区中的内存私有映射,更改不会复制到基础文件。有关更多详细信息,请参阅 private?

READONLY

表示缓冲区中的内存为只读,尝试修改它将失败。有关更多详细信息,请参阅 readonly?

SHARED

表示缓冲区中的内存也被映射,以便可以与其他进程共享。有关更多详细信息,请参见 shared?

公共类方法

IO::Buffer.for(string) → readonly io_buffer 点击切换源代码
IO::Buffer.for(string) {|io_buffer| ... 读取/写入 io_buffer ...}

从给定字符串的内存中创建一个零拷贝 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
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,
            .flags = 0,
        };

        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, RB_IO_BUFFER_READONLY);
    }
}
IO::Buffer.map(file, [size, [offset, [flags]]]) → io_buffer 点击切换源代码

通过内存映射文件,为从 file 读取创建 IO::Bufferfile_io 应该是一个以读取模式打开的 File 实例。

可以指定可选的映射 sizeoffset

默认情况下,缓冲区将是不可变的(只读);要创建可写映射,您需要以读写模式打开文件,并显式传递 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"

请注意,某些操作系统可能在映射缓冲区和文件读取之间没有缓存一致性。

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 = io_buffer_extract_size(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;
        }
    }

    // This is the file offset, not the buffer offset:
    rb_off_t offset = 0;
    if (argc >= 3) {
        offset = NUM2OFFT(argv[2]);
    }

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

    return rb_io_buffer_map(io, size, offset, flags);
}
IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) → io_buffer 点击切换源代码

创建一个新的零填充 IO::Buffer,大小为 size 字节。默认情况下,缓冲区将是内部的:直接分配的内存块。但是,如果请求的 size 大于特定于操作系统的 IO::Buffer::PAGE_SIZE,则缓冲区将使用虚拟内存机制(在 Unix 上为匿名 mmap,在 Windows 上为 VirtualAlloc)分配。可以通过将 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
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
    io_buffer_experimental();

    rb_check_arity(argc, 0, 2);

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

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

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

    io_buffer_initialize(self, buffer, NULL, size, flags, Qnil);

    return self;
}
size_of(buffer_type) → 字节大小 点击切换源代码
size_of(buffer_type 数组) → 字节大小

返回给定缓冲区类型(s)的大小(以字节为单位)。

IO::Buffer.size_of(:u32) # => 4
IO::Buffer.size_of([:u32, :u32]) # => 8
static VALUE
io_buffer_size_of(VALUE klass, VALUE buffer_type)
{
    if (RB_TYPE_P(buffer_type, T_ARRAY)) {
        size_t total = 0;
        for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
            total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
        }
        return SIZET2NUM(total);
    }
    else {
        return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
    }
}
IO::Buffer.string(length) {|io_buffer| ... 读取/写入 io_buffer ...} → string 点击切换源代码

创建一个给定长度的新字符串,并将一个零拷贝 IO::Buffer 实例传递给使用该字符串作为源的块。该块应该写入缓冲区,并将返回字符串。

IO::Buffer.string(4) do |buffer|
  buffer.set_string("Ruby")
end
# => "Ruby"
VALUE
rb_io_buffer_type_string(VALUE klass, VALUE length)
{
    VALUE string = rb_str_new(NULL, RB_NUM2LONG(length));

    struct io_buffer_for_yield_instance_arguments arguments = {
        .klass = klass,
        .string = string,
        .instance = Qnil,
    };

    rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);

    return string;
}

公共实例方法

source & mask → io_buffer 点击切换源代码

使用掩码对源进行二进制 AND 操作,生成一个与源大小相同的缓冲区,必要时重复。

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.
static VALUE
io_buffer_and(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);

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

    memory_and(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);

    return output;
}
<=>(other) → true or false 点击切换源代码

缓冲区通过大小和使用 memcmp 引用内存的精确内容进行比较。

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));
}
source ^ mask → io_buffer 点击切换源代码

使用掩码对源进行二进制 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
static VALUE
io_buffer_xor(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);

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

    memory_xor(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);

    return output;
}
and!(mask) → io_buffer 点击切换源代码

使用掩码对源进行二进制 AND 操作,在原位修改源缓冲区,必要时重复。

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.
static VALUE
io_buffer_and_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);
    io_buffer_check_overlaps(buffer, mask_buffer);

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

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

    return self;
}
clear(value = 0, [offset, [length]]) → 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_clear(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 0, 3);

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

    size_t offset, length;
    io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);

    rb_io_buffer_clear(self, value, offset, length);

    return self;
}
copy(source, [offset, [length, [source_offset]]]) → size 点击切换源代码

使用 memcpy,高效地将源 IO::Buffer 复制到缓冲区中,从 offset 开始。对于复制 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 buffer copied
buffer
# =>
# #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
# 0x00000000  00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
# 0x00000010  00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *

copy 可用于将缓冲区放入与缓冲区关联的字符串中

string= "buffer:    "
# => "buffer:    "
buffer = IO::Buffer.for(string)
buffer.copy(IO::Buffer.for("test"), 5)
# => 4
string
# => "buffer: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 is bigger than the buffer size! (ArgumentError)
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 1, 4);

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

    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(buffer, source_base, source_size, argc-1, argv+1);
}
each(buffer_type, [offset, [count]]) {|offset, value| ...} → self 点击切换源代码
each(buffer_type, [offset, [count]]) → enumerator

迭代缓冲区,从 offset 开始,生成每个 buffer_typevalue

如果给出了 count,则只生成 count 个值。

IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
  puts "#{offset}: #{value}"
end
# 2: 108
# 3: 108
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 buffer_type;
    if (argc >= 1) {
        buffer_type = RB_SYM2ID(argv[0]);
    }
    else {
        buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
    }

    size_t offset, count;
    io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);

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

    return self;
}
each_byte([offset, [count]]) {|offset, byte| ...} → self 点击切换源代码
each_byte([offset, [count]]) → enumerator

迭代缓冲区,从 offset 开始,生成每个字节。

如果给出了 count,则只生成 count 个字节。

IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
  puts "#{offset}: #{byte}"
end
# 2: 108
# 3: 108
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, count;
    io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);

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

    return self;
}
empty? → true or false 点击切换源代码

如果缓冲区大小为 0:它是通过 ::new 创建的,大小为 0,或者通过 ::for 从空字符串创建的。(请注意,空文件无法映射,因此使用 ::map 创建的缓冲区永远不会为空。)

static VALUE
rb_io_buffer_empty_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->size == 0);
}
external? → true 或 false 点击切换源代码

如果缓冲区引用了非缓冲区本身分配或映射的内存,则该缓冲区为外部缓冲区。

使用 ::for 创建的缓冲区对字符串的内存具有外部引用。

外部缓冲区无法调整大小。

static VALUE
rb_io_buffer_external_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
}
free → 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
VALUE
rb_io_buffer_free(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_free(buffer);

    return self;
}
get_string([offset, [length, [encoding]]]) → string 点击切换源代码

将缓冲区的一部分或全部读入字符串,使用指定的 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_string(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 0, 3);

    size_t offset, length;
    struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);

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

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

    io_buffer_validate_range(buffer, offset, length);

    return rb_enc_str_new((const char*)base + offset, length, encoding);
}
get_value(buffer_type, offset) → numeric 点击切换源代码

从缓冲区中读取 offset 处的 type 值。buffer_type 应为以下符号之一

  • :U8: 无符号整数,1 字节

  • :S8: 有符号整数,1 字节

  • :u16: 无符号整数,2 字节,小端序

  • :U16: 无符号整数,2 字节,大端序

  • :s16: 有符号整数,2 字节,小端序

  • :S16: 有符号整数,2 字节,大端序

  • :u32: 无符号整数,4 字节,小端序

  • :U32: 无符号整数,4 字节,大端序

  • :s32: 有符号整数,4 字节,小端序

  • :S32: 有符号整数,4 字节,大端序

  • :u64: 无符号整数,8 字节,小端序

  • :U64: 无符号整数,8 字节,大端序

  • :s64: 有符号整数,8 字节,小端序

  • :S64: 有符号整数,8 字节,大端序

  • :f32: 浮点数,4 字节,小端序

  • :F32: 浮点数,4 字节,大端序

  • :f64: 双精度浮点数,8 字节,小端序

  • :F64: 双精度浮点数,8 字节,大端序

缓冲区类型专门指存储在缓冲区中的二进制缓冲区的类型。例如,:u32 缓冲区类型是 32 位无符号整数,采用小端序格式。

string = [1.5].pack('f')
# => "\x00\x00\xC0?"
IO::Buffer.for(string).get_value(:f32, 0)
# => 1.5
static VALUE
io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
    const void *base;
    size_t size;
    size_t offset = io_buffer_extract_offset(_offset);

    rb_io_buffer_get_bytes_for_reading(self, &base, &size);

    return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
}
get_values(buffer_types, offset) → array 点击切换源代码

类似于 get_value,但它可以处理多种缓冲区类型并返回一个值数组。

string = [1.5, 2.5].pack('ff')
IO::Buffer.for(string).get_values([:f32, :f32], 0)
# => [1.5, 2.5]
static VALUE
io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
{
    size_t offset = io_buffer_extract_offset(_offset);

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

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

    VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));

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

    return array;
}
hexdump([offset, [length, [width]]]) → string 点击切换源代码

返回缓冲区的可读字符串表示。确切的格式可能会发生变化。

buffer = IO::Buffer.for("Hello World")
puts buffer.hexdump
# 0x00000000  48 65 6c 6c 6f 20 57 6f 72 6c 64                Hello World

由于缓冲区通常相当大,您可能希望通过指定偏移量和长度来限制输出。

puts buffer.hexdump(6, 5)
# 0x00000006  57 6f 72 6c 64                                  World
static VALUE
rb_io_buffer_hexdump(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 0, 3);

    size_t offset, length;
    struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);

    size_t width = RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH;
    if (argc >= 3) {
        width = io_buffer_extract_width(argv[2], 1);
    }

    // This may raise an exception if the offset/length is invalid:
    io_buffer_validate_range(buffer, offset, length);

    VALUE result = Qnil;

    if (io_buffer_validate(buffer) && buffer->base) {
        result = rb_str_buf_new(io_buffer_hexdump_output_size(width, length, 1));

        io_buffer_hexdump(result, width, buffer->base, offset+length, offset, 1);
    }

    return result;
}
dup → io_buffer 点击切换源代码
clone → io_buffer

对源缓冲区进行内部复制。对副本的更新不会影响源缓冲区。

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
static VALUE
rb_io_buffer_initialize_copy(VALUE self, VALUE source)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    const void *source_base;
    size_t source_size;

    rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);

    io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);

    return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
}
inspect → string 点击切换源代码

检查缓冲区并报告有关其内部状态的有用信息。仅一小部分缓冲区将以十六进制转储格式显示。

buffer = IO::Buffer.for("Hello World")
puts buffer.inspect
# #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
# 0x00000000  48 65 6c 6c 6f 20 57 6f 72 6c 64                Hello World
VALUE
rb_io_buffer_inspect(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    VALUE result = rb_io_buffer_to_s(self);

    if (io_buffer_validate(buffer)) {
        // Limit the maximum size generated by inspect:
        size_t size = buffer->size;
        int clamped = 0;

        if (size > RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE) {
            size = RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE;
            clamped = 1;
        }

        io_buffer_hexdump(result, RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH, buffer->base, size, 0, 0);

        if (clamped) {
            rb_str_catf(result, "\n(and %" PRIuSIZE " more bytes not printed)", buffer->size - size);
        }
    }

    return result;
}
internal? → true or false 点击切换源代码

如果缓冲区是内部的,则表示它引用了缓冲区本身分配的内存。

内部缓冲区不与任何外部内存(例如字符串)或文件映射相关联。

内部缓冲区是使用 ::new 创建的,当请求的大小小于 IO::Buffer::PAGE_SIZE 且未在创建时请求映射时,它是默认值。

内部缓冲区可以调整大小,此类操作通常会使所有切片失效,但并非总是如此。

static VALUE
rb_io_buffer_internal_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
}
locked { ... } 点击切换源代码

允许以独占方式处理缓冲区,以确保并发安全。在执行块期间,缓冲区被视为已锁定,并且任何其他代码都无法进入锁定。此外,已锁定的缓冲区不能使用 resizefree 进行更改。

以下操作会获取锁:resizefree

锁定不是线程安全的。它被设计为非阻塞系统调用的安全网。您只能使用适当的同步技术在线程之间共享缓冲区。

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
VALUE
rb_io_buffer_locked(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    buffer->flags |= RB_IO_BUFFER_LOCKED;

    VALUE result = rb_yield(self);

    buffer->flags &= ~RB_IO_BUFFER_LOCKED;

    return result;
}
locked? → true 或 false 点击切换源代码

如果缓冲区被锁定,意味着它在 locked 块执行中。锁定的缓冲区不能被调整大小或释放,也不能在它上面获取另一个锁。

锁定不是线程安全的,但它是一种语义,用于确保缓冲区在被系统调用使用时不会移动。

buffer.locked do
  buffer.write(io) # theoretical system call interface
end
static VALUE
rb_io_buffer_locked_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_LOCKED);
}
mapped? → true 或 false 点击切换源代码

如果缓冲区被映射,意味着它引用了由缓冲区映射的内存。

映射缓冲区要么是匿名的,如果它们是由 ::new 使用 IO::Buffer::MAPPED 标志创建的,或者如果它们的大小至少为 IO::Buffer::PAGE_SIZE,要么由文件支持,如果它们是由 ::map 创建的。

映射缓冲区通常可以调整大小,并且这种操作通常会使所有切片失效,但并非总是如此。

static VALUE
rb_io_buffer_mapped_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
}
not! → io_buffer 点击切换源代码

通过对源应用二进制 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                   ..........
static VALUE
io_buffer_not_inplace(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    memory_not_inplace(base, size);

    return self;
}
null? → true 或 false 点击切换源代码

如果缓冲区使用 free 释放,使用 transfer 传输,或者根本没有分配。

buffer = IO::Buffer.new(0)
buffer.null? #=> true

buffer = IO::Buffer.new(4)
buffer.null? #=> false
buffer.free
buffer.null? #=> true
static VALUE
rb_io_buffer_null_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->base == NULL);
}
or!(mask) → io_buffer 点击切换源代码

通过对源应用二进制 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
static VALUE
io_buffer_or_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);
    io_buffer_check_overlaps(buffer, mask_buffer);

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

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

    return self;
}
pread(io, from, [length, [offset]]) → 读取长度或 -errno 点击切换源代码

io 中从指定的 from 位置开始读取至少 length 字节,写入缓冲区中从 offset 开始的位置。如果发生错误,则返回 -errno

如果 length 未给出或为 nil,则默认为缓冲区大小减去偏移量,即整个缓冲区。

如果 length 为零,则将执行一个 pread 操作。

如果 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
static VALUE
io_buffer_pread(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 2, 4);

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

    size_t length, offset;
    io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);

    return rb_io_buffer_pread(self, io, from, length, offset);
}
private? → true 或 false 点击切换源代码

如果缓冲区是私有的,这意味着对缓冲区的修改不会复制到底层文件映射。

# Create a test file:
File.write('test.txt', 'test')

# Create a private mapping from the given file. Note that the file here
# is opened in read-only mode, but it doesn't matter due to the private
# mapping:
buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE)
# => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE>

# Write to the buffer (invoking CoW of the underlying file buffer):
buffer.set_string('b', 0)
# => 1

# The file itself is not modified:
File.read('test.txt')
# => "test"
static VALUE
rb_io_buffer_private_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_PRIVATE);
}
pwrite(io, from, [length, [offset]]) → 写入长度或 -errno 点击切换源代码

从缓冲区中从 offset 开始的 length 字节写入 io,从指定的 from 位置开始。如果发生错误,则返回 -errno

如果 length 未给出或为 nil,则默认为缓冲区大小减去偏移量,即整个缓冲区。

如果 length 为零,则将执行一次 pwrite 操作。

如果 offset 未给出,则默认为零,即缓冲区的开头。

如果 from 位置超出文件末尾,则间隙将用空(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 开始。

static VALUE
io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 2, 4);

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

    size_t length, offset;
    io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);

    return rb_io_buffer_pwrite(self, io, from, length, offset);
}
read(io, [length, [offset]]) → 读取长度或 -errno 点击切换源代码

io 中读取至少 length 字节,写入从 offset 开始的缓冲区。如果发生错误,则返回 -errno

如果 length 未给出或为 nil,则默认为缓冲区大小减去偏移量,即整个缓冲区。

如果 length 为零,则将执行一次 read 操作。

如果 offset 未给出,则默认为零,即缓冲区的开头。

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
static VALUE
io_buffer_read(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 1, 3);

    VALUE io = argv[0];

    size_t length, offset;
    io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);

    return rb_io_buffer_read(self, io, length, offset);
}
readonly? → true 或 false 点击切换源代码

如果缓冲区是只读的,这意味着不能使用 set_valueset_stringcopy 等方法修改缓冲区。

冻结的字符串和只读文件会创建只读缓冲区。

static VALUE
io_buffer_readonly_p(VALUE self)
{
    return RBOOL(rb_io_buffer_readonly_p(self));
}
resize(new_size) → 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_resize(VALUE self, VALUE size)
{
    rb_io_buffer_resize(self, io_buffer_extract_size(size));

    return self;
}
set_string(string, [offset, [length, [source_offset]]]) → size 点击切换源代码

使用 memcpy 从源 String 复制到缓冲区中,在 offset 位置。

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

# set buffer starting from offset 1, take 2 bytes starting from string's
# second
buf.set_string('test', 1, 2, 1)
# => 2
buf
# =>
# #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
# 0x00000000  00 65 73 00 00 00 00 00                         .es.....

有关缓冲区写入如何用于更改关联字符串和文件的示例,请参见 copy

static VALUE
io_buffer_set_string(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 1, 4);

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

    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(buffer, source_base, source_size, argc-1, argv+1);
}
set_value(type, offset, value) → offset 点击切换源代码

value的值写入缓冲区中的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是整数,而valueFloat,则会执行隐式截断。

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
static VALUE
io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
    void *base;
    size_t size;
    size_t offset = io_buffer_extract_offset(_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);
}
set_values(buffer_types, offset, values) → offset click to toggle source

buffer_typesvalues写入缓冲区中的offset处。buffer_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                         ........
static VALUE
io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
{
    if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
        rb_raise(rb_eArgError, "Argument buffer_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(buffer_types) != RARRAY_LEN(values)) {
        rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
    }

    size_t offset = io_buffer_extract_offset(_offset);

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

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

    return SIZET2NUM(offset);
}
shared? → true or false click to toggle source

如果缓冲区是共享的,这意味着它引用了可以与其他进程共享的内存(因此可能会在没有本地修改的情况下发生更改)。

# Create a test file:
File.write('test.txt', 'test')

# Create a shared mapping from the given file, the file must be opened in
# read-write mode unless we also specify IO::Buffer::READONLY:
buffer = IO::Buffer.map(File.open('test.txt', 'r+'), nil, 0)
# => #<IO::Buffer 0x00007f1bffd5e000+4 EXTERNAL MAPPED SHARED>

# Write to the buffer, which will modify the mapped file:
buffer.set_string('b', 0)
# => 1

# The file itself is modified:
File.read('test.txt')
# => "best"
static VALUE
rb_io_buffer_shared_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(buffer->flags & RB_IO_BUFFER_SHARED);
}
size → integer click to toggle source

返回显式设置的缓冲区大小(在使用::new创建时或在resize时),或在缓冲区创建时从字符串或文件中推断出的缓冲区大小。

VALUE
rb_io_buffer_size(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return SIZET2NUM(buffer->size);
}
slice([offset, [length]]) → io_buffer click to toggle source

生成另一个IO::Buffer,它是当前缓冲区从offset字节开始,持续length字节的切片(或视图)。

切片操作不会复制内存,并且切片将继续与原始缓冲区的源(字符串或文件)相关联(如果有)。

如果没有给出偏移量,则偏移量将为零。如果偏移量为负数,则会引发ArgumentError

如果没有给出长度,则切片将与原始缓冲区减去指定偏移量的长度一样长。如果长度为负数,则会引发ArgumentError

如果offset+length超出当前缓冲区的范围,则会引发RuntimeError

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
static VALUE
io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 0, 2);

    size_t offset, length;
    struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);

    return rb_io_buffer_slice(buffer, self, offset, length);
}
to_s → string click to toggle source

缓冲区的简短表示形式。它包括地址、大小和符号标志。此格式可能会发生变化。

puts IO::Buffer.new(4) # uses to_s internally
# #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
VALUE
rb_io_buffer_to_s(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    VALUE result = rb_str_new_cstr("#<");

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

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

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

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

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

    if (buffer->flags & RB_IO_BUFFER_FILE) {
        rb_str_cat2(result, " FILE");
    }

    if (buffer->flags & RB_IO_BUFFER_SHARED) {
        rb_str_cat2(result, " SHARED");
    }

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

    if (buffer->flags & RB_IO_BUFFER_PRIVATE) {
        rb_str_cat2(result, " PRIVATE");
    }

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

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

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

    return rb_str_cat2(result, ">");
}
transfer → new_io_buffer click to toggle source

将底层内存的所有权转移到一个新的缓冲区,导致当前缓冲区变为未初始化状态。

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
VALUE
rb_io_buffer_transfer(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    if (buffer->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 = *buffer;
    io_buffer_zero(buffer);

    return instance;
}
valid? → true 或 false 点击切换源代码

返回缓冲区是否可访问。

如果缓冲区是另一个已释放或在不同地址重新分配的缓冲区(或字符串)的切片,则它将变为无效。

static VALUE
rb_io_buffer_valid_p(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

    return RBOOL(io_buffer_validate(buffer));
}
values(buffer_type, [offset, [count]]) → 数组 点击切换源代码

返回从 offset 开始的 buffer_type 类型的数组。

如果给出了 count,则只返回 count 个值。

IO::Buffer.for("Hello World").values(:U8, 2, 2)
# => [108, 108]
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 buffer_type;
    if (argc >= 1) {
        buffer_type = RB_SYM2ID(argv[0]);
    }
    else {
        buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
    }

    size_t offset, count;
    io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);

    VALUE array = rb_ary_new_capa(count);

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

    return array;
}
write(io, [length, [offset]]) → 写入长度或 -errno 点击切换源代码

从缓冲区中从 offset 开始写入至少 length 个字节到 io 中。如果发生错误,则返回 -errno

如果 length 未给出或为 nil,则默认为缓冲区大小减去偏移量,即整个缓冲区。

如果 length 为零,则将执行一次 write 操作。

如果 offset 未给出,则默认为零,即缓冲区的开头。

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

这会导致将 123 写入 output.txt

static VALUE
io_buffer_write(int argc, VALUE *argv, VALUE self)
{
    rb_check_arity(argc, 1, 3);

    VALUE io = argv[0];

    size_t length, offset;
    io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);

    return rb_io_buffer_write(self, io, length, offset);
}
xor!(mask) → io_buffer 点击切换源代码

通过对源使用掩码应用二进制异或运算来修改源缓冲区,并根据需要重复。

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
static VALUE
io_buffer_xor_inplace(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);
    io_buffer_check_overlaps(buffer, mask_buffer);

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

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

    return self;
}
source | mask → io_buffer 点击切换源代码

通过对源使用掩码应用二进制或运算来生成一个与源大小相同的缓冲区,并根据需要重复。

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
static VALUE
io_buffer_or(VALUE self, VALUE mask)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    io_buffer_check_mask(mask_buffer);

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

    memory_or(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);

    return output;
}
~source → io_buffer 点击切换源代码

通过对源应用二进制非运算来生成一个与源大小相同的缓冲区。

~IO::Buffer.for("1234567890")
# =>
# #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
# 0x00000000  ce cd cc cb ca c9 c8 c7 c6 cf                   ..........
static VALUE
io_buffer_not(VALUE self)
{
    struct rb_io_buffer *buffer = NULL;
    TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);

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

    memory_not(output_buffer->base, buffer->base, buffer->size);

    return output;
}