類別 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(字串) → 唯讀 io_buffer 按一下以切換來源
IO::Buffer.for(字串) {|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(檔案, [大小, [位移, [旗標]]]) → io_buffer 按一下以切換來源

透過記憶體對應檔案來建立一個 IO::Buffer 以從 檔案 讀取。檔案_io 應為 檔案 實例,已開啟為讀取模式。

可以指定對應的選用 大小位移

預設情況下,緩衝區會是不可變的(唯讀);若要建立可寫入對應,您需要以讀寫模式開啟檔案,並明確傳遞不含 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([大小 = DEFAULT_SIZE, [旗標 = 0]]) → io_buffer 按一下以切換來源

建立一個新的零填滿 IO::Buffer,大小為 大小 位元組。預設情況下,緩衝區會是內部:直接配置的記憶體區塊。但如果要求的 大小 大於作業系統特定的 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(緩衝區類型) → 位元組大小 按一下以切換來源
size_of(緩衝區類型陣列) → 位元組大小

傳回指定緩衝區類型的大小(以位元組為單位)。

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(長度) {|io_buffer| ... 讀取/寫入 io_buffer ...} → 字串 按一下以切換來源

建立一個指定長度的字串,並將一個零複製 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 或 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 按一下以切換來源

有效率地從來源 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 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 開始的每個 valuebuffer_type

如果指定 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 或 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 或 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 按一下以切換來源

從指定的 from 位置開始,從 io 讀取至少 length 位元組,到從 offset 開始的緩衝區。如果發生錯誤,傳回 -errno

如果未提供 lengthnil,它預設為緩衝區大小減去偏移量,即整個緩衝區。

如果 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 位元組,到從指定的 from 位置開始的 io。如果發生錯誤,傳回 -errno

如果未提供 lengthnil,它預設為緩衝區大小減去偏移量,即整個緩衝區。

如果 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

如果未提供 lengthnil,它預設為緩衝區大小減去偏移量,即整個緩衝區。

如果 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(字串, [偏移量, [長度, [來源偏移量]]]) → 大小 按一下以切換來源

有效率地從來源 字串 複製到緩衝區,在 偏移量 使用 memcpy

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

另請參閱 複製,了解緩衝區寫入如何用於變更關聯字串和檔案的範例。

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(類型, 偏移量, 值) → 偏移量 按一下以切換來源

偏移量 寫入 類型 到緩衝區。類型 應為 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......

請注意,如果 類型 是整數,而 浮點數,則會執行隱式截斷

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(緩衝區類型, 偏移量, 值) → 偏移量 按一下以切換來源

偏移量 寫入 緩衝區類型 到緩衝區。緩衝區類型 應為 get_value 中所述符號的陣列。 應為要寫入值的陣列。

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 或 false 按一下以切換來源

如果緩衝區為共用,表示它參照可以與其他程序共用的記憶體(因此可能會在未在本地修改的情況下變更)。

# 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);
}
大小 → 整數 按一下以切換來源

傳回緩衝區的大小,該大小已明確設定(在使用 ::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([偏移量, [長度]]) → io_buffer 按一下以切換來源

產生另一個 IO::Buffer,該 IO::Buffer 是從 偏移量 位元組開始並執行 長度 位元組的切片(或檢視)。

切片會在不複製記憶體的情況下發生,而且切片會持續與原始緩衝區的來源(字串或檔案,如果有的話)關聯。

如果未提供偏移量,則會為零。如果偏移量為負數,則會引發 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 按一下以切換來源

緩衝區的簡短表示形式。它包含位址、大小和符號旗標。此格式可能會變更。

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 按一下以切換來源

將底層記憶體的所有權轉移到新的緩衝區,導致目前的緩衝區變成未初始化。

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 按一下以切換來源

傳回緩衝區 buffer 是否可存取。

如果緩衝區是已釋放或在不同位址重新配置的另一個緩衝區 (或字串) 的切片,則該緩衝區會變成無效。

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]]) → array 按一下以切換來源

傳回從 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

如果未提供 lengthnil,它預設為緩衝區大小減去偏移量,即整個緩衝區。

如果 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 按一下以切換來源

透過對來源套用二進位 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
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 按一下以切換來源

透過對來源套用二進位 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
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 按一下以切換來源

透過對來源套用二進位 NOT 運算,產生一個與來源大小相同的緩衝區。

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