class 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 = 'data' IO::Buffer.for(string) do |buffer| buffer # => # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE> # 0x00000000 64 61 74 61 data buffer.get_string(2) # read content starting from offset 2 # => "ta" buffer.set_string('---', 1) # write content, starting from offset 1 # => 3 buffer # => # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE> # 0x00000000 64 2d 2d 2d d--- string # original string changed, too # => "d---" end
从文件创建缓冲区
File.write('test.txt', 'test data') # => 9 buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY) # => # #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY> # ... buffer.get_string(5, 2) # read 2 bytes, starting from offset 5 # => "da" buffer.set_string('---', 1) # attempt to write # in `set_string': Buffer is not writable! (IO::Buffer::AccessError) # To create writable file-mapped buffer # Open file for read-write, pass size, offset, and flags=0 buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0) buffer.set_string('---', 1) # => 3 -- bytes written File.read('test.txt') # => "t--- data"
该类尚处于实验阶段,接口可能会发生变化,尤其是在文件映射方面,未来可能会被完全移除。
Constants
- 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?。
Public Class Methods
Source
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。如果不带块,则会高效地创建一个字符串的内部冻结副本并用作缓冲区源。当提供块时,缓冲区将直接与字符串的内部缓冲区关联,更新缓冲区将更新字符串。
直到缓冲区被 free(显式调用或通过垃圾回收器),源字符串将被锁定且无法修改。
如果字符串是冻结的,它将创建一个只读缓冲区,无法修改。如果字符串是共享的,使用块形式时可能会触发写时复制。
string = 'test' buffer = IO::Buffer.for(string) buffer.external? #=> true buffer.get_string(0, 1) # => "t" string # => "test" 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
Source
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];
rb_off_t file_size = rb_file_size(io);
// Compiler can confirm that we handled file_size <= 0 case:
if (UNLIKELY(file_size <= 0)) {
rb_raise(rb_eArgError, "Invalid negative or zero file size!");
}
// Here, we assume that file_size is positive:
else if (UNLIKELY((uintmax_t)file_size > SIZE_MAX)) {
rb_raise(rb_eArgError, "File larger than address space!");
}
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
size = io_buffer_extract_size(argv[1]);
if (UNLIKELY(size == 0)) {
rb_raise(rb_eArgError, "Size can't be zero!");
}
if (UNLIKELY(size > (size_t)file_size)) {
rb_raise(rb_eArgError, "Size can't be larger than file size!");
}
}
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]);
if (UNLIKELY(offset < 0)) {
rb_raise(rb_eArgError, "Offset can't be negative!");
}
if (UNLIKELY(offset >= file_size)) {
rb_raise(rb_eArgError, "Offset too large!");
}
if (RB_NIL_P(argv[1])) {
// Decrease size if it's set from the actual file size:
size = (size_t)(file_size - offset);
}
else if (UNLIKELY((size_t)(file_size - offset) < size)) {
rb_raise(rb_eArgError, "Offset too large!");
}
}
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);
}
通过内存映射文件来创建一个用于从 file 读取的 IO::Buffer。file 应该是一个 File 实例,并以读取或读写模式打开。
可以指定可选的 size 和 offset。尝试映射空文件或指定 size 为 0 将引发错误。offset 的有效值取决于系统。
默认情况下,缓冲区是可写的,并且期望文件也是可写的。它也是共享的,因此多个进程可以使用相同的映射。
可以在 flags 参数中传递 IO::Buffer::READONLY 来创建一个只读缓冲区;这允许与仅以读取模式打开的文件一起工作。在 flags 中指定 IO::Buffer::PRIVATE 创建一个私有映射,它不会影响其他进程或底层文件。它也允许更新从只读文件创建的缓冲区。
File.write('test.txt', 'test') buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY) # => #<IO::Buffer 0x00000001014a0000+4 EXTERNAL MAPPED FILE SHARED READONLY> buffer.readonly? # => true buffer.get_string # => "test" buffer.set_string('b', 0) # 'IO::Buffer#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"
请注意,某些操作系统可能无法保证映射缓冲区与文件读取之间的缓存一致性。
Source
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
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 字节的 IO::Buffer。默认情况下,缓冲区将是内部的:直接分配的内存块。但如果请求的 size 大于 OS 特定的 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
Source
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.size_of(:u32) # => 4 IO::Buffer.size_of([:u32, :u32]) # => 8
Source
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;
}
创建一个给定长度的新字符串,并向块屈服一个使用字符串作为源的零拷贝 IO::Buffer 实例。块应写入缓冲区,然后返回字符串。
IO::Buffer.string(4) do |buffer| buffer.set_string("Ruby") end # => "Ruby"
Public Instance Methods
Source
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;
}
生成一个与源缓冲区大小相同的新缓冲区,通过将源与掩码进行二进制 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.
Source
static VALUE
rb_io_buffer_compare(VALUE self, VALUE other)
{
const void *ptr1, *ptr2;
size_t size1, size2;
rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
if (size1 < size2) {
return RB_INT2NUM(-1);
}
if (size1 > size2) {
return RB_INT2NUM(1);
}
return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
}
缓冲区通过大小和它们引用的内存的确切内容使用 memcmp 进行比较。
Source
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;
}
生成一个与源缓冲区大小相同的新缓冲区,通过将源与掩码进行二进制 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
Source
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;
}
生成一个与源缓冲区大小相同的新缓冲区,通过将源与掩码进行二进制 OR 操作来生成,必要时会重复。
IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF") # => # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL> # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
Source
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;
}
生成一个与源缓冲区大小相同的新缓冲区,通过对源进行二进制 NOT 操作来生成。
~IO::Buffer.for("1234567890") # => # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL> # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
Source
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;
}
通过将源与掩码进行二进制 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.
Source
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;
}
用 value 填充缓冲区,从 offset 开始,持续 length 字节。
buffer = IO::Buffer.for('test').dup # => # <IO::Buffer 0x00007fca40087c38+4 INTERNAL> # 0x00000000 74 65 73 74 test buffer.clear # => # <IO::Buffer 0x00007fca40087c38+4 INTERNAL> # 0x00000000 00 00 00 00 .... buf.clear(1) # fill with 1 # => # <IO::Buffer 0x00007fca40087c38+4 INTERNAL> # 0x00000000 01 01 01 01 .... buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes # => # <IO::Buffer 0x00007fca40087c38+4 INTERNAL> # 0x00000000 01 02 02 01 .... buffer.clear(2, 1) # fill with 2, starting from offset 1 # => # <IO::Buffer 0x00007fca40087c38+4 INTERNAL> # 0x00000000 01 02 02 02 ....
Source
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);
VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
RB_GC_GUARD(source);
return result;
}
高效地使用 memmove 将源 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 = "data: " # => "data: " buffer = IO::Buffer.for(string) do |buffer| buffer.copy(IO::Buffer.for("test"), 5) end # => 4 string # => "data:test"
尝试复制到只读缓冲区将失败
File.write('test.txt', 'test') buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY) buffer.copy(IO::Buffer.for("test"), 8) # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
有关创建可变文件映射的详细信息,请参见 ::map,此操作将有效
buffer = IO::Buffer.map(File.open('test.txt', 'r+')) buffer.copy(IO::Buffer.for("boom"), 0) # => 4 File.read('test.txt') # => "boom"
尝试复制超出缓冲区范围的缓冲区将失败
buffer = IO::Buffer.new(2) buffer.copy(IO::Buffer.for('test'), 0) # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
在重叠的内存区域之间复制是安全的。在这种情况下,数据将像先从源缓冲区复制到临时缓冲区,然后再从临时缓冲区复制到目标缓冲区一样被复制。
buffer = IO::Buffer.new(10) buffer.set_string("0123456789") buffer.copy(buffer, 3, 7) # => 7 buffer # => # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL> # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
Source
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 开始屈服每个 buffer_type 的 value。
如果指定了 count,则只屈服 count 个值。
IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value| puts "#{offset}: #{value}" end # 2: 108 # 3: 108
Source
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, argv, &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;
}
遍历缓冲区,从 offset 开始屈服每个字节。
如果指定了 count,则只屈服 count 个字节。
IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte| puts "#{offset}: #{byte}" end # 2: 108 # 3: 108
Source
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);
}
Source
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);
}
Source
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;
}
如果缓冲区引用内存,则将其释放回操作系统。
-
对于映射的缓冲区(例如来自文件的):取消映射。
-
对于从头创建的缓冲区:释放内存。
-
对于从字符串创建的缓冲区:取消关联。
在缓冲区被释放后,无法对其执行任何进一步的操作。
您可以调整已释放缓冲区的大小以重新分配它。
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
Source
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);
}
以指定的 encoding 将缓冲区的一部分或全部读入字符串。如果没有提供编码,则使用 Encoding::BINARY。
buffer = IO::Buffer.for('test') buffer.get_string # => "test" buffer.get_string(2) # => "st" buffer.get_string(2, 1) # => "s"
Source
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);
}
从缓冲区中在 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 字节,大端 -
:u128: 无符号整数,16 字节,小端 -
:U128: 无符号整数,16 字节,大端 -
:s128: 有符号整数,16 字节,小端 -
:S128: 有符号整数,16 字节,大端 -
: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
Source
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;
}
与 get_value 类似,但可以处理多种缓冲区类型并返回一个值数组。
string = [1.5, 2.5].pack('ff') IO::Buffer.for(string).get_values([:f32, :f32], 0) # => [1.5, 2.5]
Source
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;
}
返回缓冲区的可读字符串表示形式。确切格式可能会更改。
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
Source
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);
VALUE result = io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
RB_GC_GUARD(source);
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
Source
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;
}
检查缓冲区并报告其内部状态的有用信息。缓冲区只有一小部分会以十六进制转储格式显示。
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
Source
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);
}
如果缓冲区是内部的,则表示它引用的是缓冲区自身分配的内存。
内部缓冲区不与任何外部内存(例如字符串)或文件映射关联。
内部缓冲区使用 ::new 创建,当请求大小小于 IO::Buffer::PAGE_SIZE 且创建时未请求映射时,这是默认行为。
内部缓冲区可以调整大小,此操作通常会使所有切片失效,但并非总是如此。
Source
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;
}
允许以独占方式处理缓冲区,以实现并发安全。在块执行期间,缓冲区被视为已锁定,其他代码无法进入锁定。此外,锁定的缓冲区无法通过 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
Source
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);
}
如果缓冲区已锁定,则表示它在 locked 块执行中。锁定的缓冲区不能调整大小或释放,也不能在上面获取另一个锁定。
锁定不是线程安全的,但它是一种用于确保缓冲区在被系统调用使用时不会移动的语义。
buffer.locked do buffer.write(io) # theoretical system call interface end
Source
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);
}
如果缓冲区是映射的,则表示它引用的是缓冲区映射的内存。
映射缓冲区要么是匿名的(如果由 ::new 并带 IO::Buffer::MAPPED 标志创建,或者如果大小至少为 IO::Buffer::PAGE_SIZE),要么由文件支持(如果使用 ::map 创建)。
映射缓冲区通常可以调整大小,此操作通常会使所有切片失效,但并非总是如此。
Source
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;
}
通过对源进行二进制 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 ..........
Source
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);
}
Source
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;
}
通过将源与掩码进行二进制 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
Source
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);
}
从 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
Source
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);
}
如果缓冲区是私有的,则表示对缓冲区的修改不会复制到底层文件映射。
# 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"
Source
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);
}
从缓冲区中从 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 开始。
Source
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);
}
从 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
Source
static VALUE
io_buffer_readonly_p(VALUE self)
{
return RBOOL(rb_io_buffer_readonly_p(self));
}
如果缓冲区是只读的,则表示缓冲区不能通过 set_value、set_string 或 copy 和类似方法进行修改。
冻结的字符串和只读文件会创建只读缓冲区。
Source
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
将缓冲区的大小调整为 new_size 字节,同时保留其内容。根据旧大小和新大小,与缓冲区关联的内存区域可能会被扩展,或者在内容被复制到不同地址进行重新分配。
buffer = IO::Buffer.new(4) buffer.set_string("test", 0) buffer.resize(8) # resize to 8 bytes # => # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL> # 0x00000000 74 65 73 74 00 00 00 00 test....
外部缓冲区(使用 ::for 创建)和锁定的缓冲区不能调整大小。
Source
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);
VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
RB_GC_GUARD(string);
return result;
}
使用 memmove 高效地将源 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,其中包含有关缓冲区写入如何用于更改关联字符串和文件的示例。
Source
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);
}
将 type 类型的 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 是整数而 value 是 Float,则会执行隐式截断。
buffer = IO::Buffer.new(8) buffer.set_value(:U32, 0, 2.5) buffer # => # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL> # 0x00000000 00 00 00 02 00 00 00 00 # ^^ the same as if we'd pass just integer 2
Source
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);
}
将 buffer_types 的 values 写入缓冲区,在 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 ........
Source
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);
}
Source
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);
}
生成另一个 IO::Buffer,它是一个切片(或视图),从当前缓冲区的 offset 字节开始,持续 length 字节。
切片操作不进行内存复制,并且切片如果存在,则继续与原始缓冲区的源(字符串或文件)关联。
如果未给出偏移量,则为零。如果偏移量为负数,则会引发 ArgumentError。
如果未给出长度,则切片将等于原始缓冲区减去指定偏移量。如果长度为负数,则会引发 ArgumentError。
如果 offset+length 超出当前缓冲区范围,则引发 RuntimeError。
string = 'test' buffer = IO::Buffer.for(string).dup 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 INTERNAL> # 0x00000000 74 6f 73 74 tost
Source
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, ">");
}
缓冲区的简短表示。它包括地址、大小和符号标志。此格式可能会更改。
puts IO::Buffer.new(4) # uses to_s internally # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
Source
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 = 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
Source
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));
}
返回缓冲区是否可访问。
当缓冲区是另一个已被释放或重新分配到不同地址的缓冲区(或字符串)的切片时,该缓冲区会变得无效。
Source
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;
}
返回 buffer_type 类型的值数组,从 offset 开始。
如果指定了 count,则只返回 count 个值。
IO::Buffer.for("Hello World").values(:U8, 2, 2) # => [108, 108]
Source
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);
}
从缓冲区中从 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
Source
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;
}
通过将源与掩码进行二进制 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