類別 IO::Buffer
IO::Buffer
是輸入/輸出的有效率零複製緩衝區。以下是常見的用例
-
使用
::new
建立一個空的緩衝區,使用copy
或set_value
、set_string
填入緩衝區,使用get_string
取得緩衝區或直接使用write
將其寫入某些檔案。 -
使用
::for
建立一個對應到某個字串的緩衝區,然後可以使用get_string
或get_value
進行讀取,也可以進行寫入(寫入也會變更原始字串)。 -
使用
::map
建立一個對應到某個檔案的緩衝區,然後可以使用它來讀取和寫入基礎檔案。
與字串和檔案記憶體的互動是透過有效率的低階 C 機制(例如「memcpy`」)來執行。
此類別旨在作為實作較高階機制的公用程式,例如 Fiber::Scheduler#io_read
和 Fiber::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
- MAPPED
表示緩衝區中的記憶體由作業系統對應。有關更多詳細資訊,請參閱
mapped?
。- NETWORK_ENDIAN
指網路位元組順序,與大端序相同。有關更多詳細資訊,請參閱
get_value
。- PAGE_SIZE
作業系統頁面大小。用於有效率的頁面對齊記憶體配置。
- PRIVATE
表示緩衝區中的記憶體以私人方式對應,變更不會複製到基礎檔案。有關更多詳細資訊,請參閱
private?
。- READONLY
表示緩衝區中的記憶體為唯讀,嘗試修改它會失敗。有關更多詳細資訊,請參閱
readonly?
。- SHARED
表示緩衝區中的記憶體也已對應,以便可以與其他處理程序共用。有關更多詳細資訊,請參閱
shared?
。
公開類別方法
從指定的字串記憶體建立一個零複製 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
以從 檔案
讀取。檔案_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
,大小為 大小
位元組。預設情況下,緩衝區會是內部:直接配置的記憶體區塊。但如果要求的 大小
大於作業系統特定的 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; }
傳回指定緩衝區類型的大小(以位元組為單位)。
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
實例傳遞給區塊,該區塊使用字串作為來源。區塊預期會寫入緩衝區,而字串會被傳回。
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; }
公開實例方法
透過將二進制 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; }
緩衝區會根據大小和它們所參照的記憶體的確切內容來進行比較,方法是使用 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)); }
透過將二進制 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 運算套用至來源(使用遮罩,必要時重複)來修改來源緩衝區。
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; }
使用 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; }
有效率地從來源 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); }
反覆運算緩衝區,產生從 offset
開始的每個 value
的 buffer_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; }
反覆運算緩衝區,產生從 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; }
如果緩衝區參照的記憶體並未由緩衝區本身配置或對應,則緩衝區為外部。
使用 ::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); }
如果緩衝區參照記憶體,請將其釋放回作業系統。
-
對於對應的緩衝區(例如來自檔案):取消對應。
-
對於從頭建立的緩衝區:釋放記憶體。
-
對於從字串建立的緩衝區:取消關聯。
在釋放緩衝區後,無法對其執行進一步的操作。
您可以調整已釋放緩衝區的大小以重新配置它。
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; }
將緩衝區中的一段或全部讀取到字串中,使用指定的 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); }
從緩衝區中讀取 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_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; }
傳回緩衝區的人類可讀字串表示形式。確切格式可能會變更。
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; }
製作原始緩衝區的內部副本。對副本的更新不會影響原始緩衝區。
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); }
檢查緩衝區並報告有關其內部狀態的有用資訊。只有緩衝區的一小部分會以十六進位轉儲樣式格式顯示。
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; }
如果緩衝區是內部,表示它參照緩衝區本身配置的記憶體。
內部緩衝區與任何外部記憶體(例如字串)或檔案對應無關。
使用 ::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); }
允許以獨佔方式處理緩衝區,以確保執行緒安全性。在執行區塊時,緩衝區會被視為已鎖定,且沒有其他程式碼可以進入鎖定。此外,已鎖定的緩衝區無法使用 resize
或 free
進行變更。
鎖定並非執行緒安全。其設計為非封鎖系統呼叫周圍的安全網。您只能使用適當的同步技術在執行緒之間共用緩衝區。
buffer = IO::Buffer.new(4) buffer.locked? #=> false Fiber.schedule do buffer.locked do buffer.write(io) # theoretical system call interface end end Fiber.schedule do # in `locked': Buffer already locked! (IO::Buffer::LockedError) buffer.locked do buffer.set_string("test", 0) end end
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
區塊執行中。已鎖定的緩衝區無法調整大小或釋放,且無法取得另一個鎖定。
鎖定並非執行緒安全,但是一種語意,用於確保緩衝區在系統呼叫使用時不會移動。
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); }
如果緩衝區已對應,表示它參照緩衝區對應的記憶體。
對應緩衝區可能是匿名的(如果使用 ::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 作業套用至來源,就地修改來源緩衝區。
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; }
如果緩衝區已使用 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 作業套用至來源,就地修改來源緩衝區,並視需要重複。
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; }
從指定的 from
位置開始,從 io
讀取至少 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); }
如果緩衝區是私人,表示對緩衝區的修改不會複製到底層檔案對應。
# 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); }
從從 offset
開始的緩衝區寫入至少 length
位元組,到從指定的 from
位置開始的 io
。如果發生錯誤,傳回 -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); }
從 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); }
如果緩衝區是唯讀,表示緩衝區無法使用 set_value
、set_string
或 copy
等修改。
凍結字串和唯讀檔案會建立唯讀緩衝區。
static VALUE io_buffer_readonly_p(VALUE self) { return RBOOL(rb_io_buffer_readonly_p(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; }
有效率地從來源 字串
複製到緩衝區,在 偏移量
使用 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); }
在 偏移量
寫入 類型
的 值
到緩衝區。類型
應為 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); }
在 偏移量
寫入 緩衝區類型
的 值
到緩衝區。緩衝區類型
應為 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); }
產生另一個 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); }
緩衝區的簡短表示形式。它包含位址、大小和符號旗標。此格式可能會變更。
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, ">"); }
將底層記憶體的所有權轉移到新的緩衝區,導致目前的緩衝區變成未初始化。
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; }
傳回緩衝區 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)); }
傳回從 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; }
從緩衝區的 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 運算,並重複使用遮罩,就地修改來源緩衝區。
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; }
透過對來源套用二進位 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; }
透過對來源套用二進位 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; }