class IO::Buffer
IO::Buffer ist ein effizienter Zero-Copy-Puffer für Ein- und Ausgabe. Typische Anwendungsfälle sind:
-
Erstellen Sie einen leeren Puffer mit
::new, füllen Sie ihn mit Pufferinhalt mitcopyoderset_value,set_string, rufen Sie den Pufferinhalt mitget_stringab oder schreiben Sie ihn direkt in eine Datei mitwrite. -
Erstellen Sie einen Puffer, der auf einen String abgebildet wird, mit
::for. Dieser kann dann sowohl zum Lesen mitget_stringoderget_valueals auch zum Schreiben verwendet werden (Schreiben ändert auch den Quell-String). -
Erstellen Sie einen Puffer, der auf eine Datei abgebildet wird, mit
::map. Dieser kann dann zum Lesen und Schreiben der zugrunde liegenden Datei verwendet werden. -
Erstellen Sie einen String fester Größe mit
::string, lesen Sie dann mitreadhinein oder ändern Sie ihn mitset_value.
Die Interaktion mit String- und Dateispeicher wird durch effiziente Low-Level-C-Mechanismen wie `memcpy` durchgeführt.
Die Klasse dient als Hilfsmittel zur Implementierung höherwertiger Mechanismen wie Fiber::Scheduler#io_read und Fiber::Scheduler#io_write sowie zum Parsen binärer Protokolle.
Anwendungsbeispiele
Leerer Puffer
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"
Puffer aus String
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
Puffer aus Datei
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"
Die Klasse ist experimentell und die Schnittstelle kann sich ändern. Dies gilt insbesondere für Dateizuordnungen, die möglicherweise in Zukunft vollständig entfernt werden.
Constants
- BIG_ENDIAN
-
Bezieht sich auf die Big-Endian-Byte-Reihenfolge, bei der das höchstwertige Byte zuerst gespeichert wird. Weitere Informationen finden Sie unter
get_value. - DEFAULT_SIZE
-
Die Standardgröße des Puffers, typischerweise ein (kleines) Vielfaches der
PAGE_SIZE. Kann explizit durch Setzen der Umgebungsvariablen RUBY_IO_BUFFER_DEFAULT_SIZE angegeben werden. - EXTERNAL
-
Zeigt an, dass der Speicher im Puffer von jemand anderem besessen wird. Weitere Informationen finden Sie unter
external?. - HOST_ENDIAN
-
Bezieht sich auf die Byte-Reihenfolge des Host-Rechners. Weitere Informationen finden Sie unter
get_value. - INTERNAL
-
Zeigt an, dass der Speicher im Puffer vom Puffer besessen wird. Weitere Informationen finden Sie unter
internal?. - LITTLE_ENDIAN
-
Bezieht sich auf die Little-Endian-Byte-Reihenfolge, bei der das niederwertigste Byte zuerst gespeichert wird. Weitere Informationen finden Sie unter
get_value. - LOCKED
-
Zeigt an, dass der Speicher im Puffer gesperrt ist und nicht vergrößert oder freigegeben werden kann. Weitere Informationen finden Sie unter
locked?undlocked. - MAPPED
-
Zeigt an, dass der Speicher im Puffer vom Betriebssystem zugeordnet wurde. Weitere Informationen finden Sie unter
mapped?. - NETWORK_ENDIAN
-
Bezieht sich auf die Netzwerk-Byte-Reihenfolge, die mit Big Endian identisch ist. Weitere Informationen finden Sie unter
get_value. - PAGE_SIZE
-
Die Seitengröße des Betriebssystems. Wird für effiziente seitenbasierte Speicherzuordnungen verwendet.
- PRIVATE
-
Zeigt an, dass der Speicher im Puffer privat zugeordnet ist und Änderungen nicht in die zugrunde liegende Datei repliziert werden. Weitere Informationen finden Sie unter
private?. - READONLY
-
Zeigt an, dass der Speicher im Puffer schreibgeschützt ist und jeder Versuch, ihn zu ändern, fehlschlägt. Weitere Informationen finden Sie unter
readonly?. - SHARED
-
Zeigt an, dass der Speicher im Puffer auch so zugeordnet ist, dass er mit anderen Prozessen geteilt werden kann. Weitere Informationen finden Sie unter
shared?.
Öffentliche Klassenmethoden
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);
}
}
Erstellt einen Zero-Copy-IO::Buffer aus dem Speicher des angegebenen Strings. Ohne Block wird eine eingefrorene interne Kopie des Strings erstellt und effizient als Pufferquelle verwendet. Wenn ein Block bereitgestellt wird, wird der Puffer direkt dem internen Puffer des Strings zugeordnet und die Aktualisierung des Puffers aktualisiert auch den String.
Bis free auf dem Puffer aufgerufen wird, entweder explizit oder durch den Garbage Collector, wird der Quell-String gesperrt und kann nicht geändert werden.
Wenn der String eingefroren ist, wird ein schreibgeschützter Puffer erstellt, der nicht geändert werden kann. Wenn der String geteilt wird, kann dies bei Verwendung der Blockform zu einem Copy-on-Write führen.
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);
}
Erstellt einen IO::Buffer zum Lesen aus file durch Memory-Mapping der Datei. file sollte eine File-Instanz sein, die zum Lesen oder zum Lesen und Schreiben geöffnet ist.
Optionale size und offset der Abbildung können angegeben werden. Der Versuch, eine leere Datei abzubilden oder size von 0 anzugeben, löst einen Fehler aus. Gültige Werte für offset sind systemabhängig.
Standardmäßig ist der Puffer beschreibbar und erwartet, dass die Datei beschreibbar ist. Er ist auch geteilt, sodass mehrere Prozesse dieselbe Zuordnung verwenden können.
Sie können IO::Buffer::READONLY im flags-Argument übergeben, um einen schreibgeschützten Puffer zu erstellen. Dies ermöglicht die Arbeit mit Dateien, die nur zum Lesen geöffnet sind. Die Angabe von IO::Buffer::PRIVATE in flags erstellt eine private Zuordnung, die andere Prozesse oder die zugrunde liegende Datei nicht beeinflusst. Sie ermöglicht auch die Aktualisierung eines aus einer schreibgeschützten Datei erstellten Puffers.
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"
Beachten Sie, dass einige Betriebssysteme möglicherweise keine Cache-Kohärenz zwischen zugeordneten Puffern und Datei-Lesevorgängen haben.
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;
}
Erstellt einen neuen, null-gefüllten IO::Buffer von size Bytes. Standardmäßig ist der Puffer *intern*: ein direkt zugeordneter Speicherbereich. Wenn die angeforderte size jedoch größer als die OS-spezifische IO::Buffer::PAGE_SIZE ist, wird der Puffer über den virtuellen Speckhanismus (anonymes mmap unter Unix, VirtualAlloc unter Windows) zugeordnet. Das Verhalten kann durch Übergabe von IO::Buffer::MAPPED als zweites Argument erzwungen werden.
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)));
}
}
Gibt die Größe der angegebenen Puffertypen in Bytes zurück.
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;
}
Erstellt einen neuen String der angegebenen Länge und übergibt eine Zero-Copy IO::Buffer-Instanz an den Block, der den String als Quelle verwendet. Es wird erwartet, dass der Block in den Puffer schreibt und der String zurückgegeben wird.
IO::Buffer.string(4) do |buffer| buffer.set_string("Ruby") end # => "Ruby"
Öffentliche Instanzmethoden
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;
}
Generiert einen neuen Puffer, der die gleiche Größe wie die Quelle hat, indem die binäre AND-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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));
}
Puffer werden nach Größe und exaktem Inhalt des Speichers, auf den sie verweisen, mit memcmp verglichen.
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;
}
Generiert einen neuen Puffer, der die gleiche Größe wie die Quelle hat, indem die binäre XOR-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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;
}
Generiert einen neuen Puffer, der die gleiche Größe wie die Quelle hat, indem die binäre OR-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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;
}
Generiert einen neuen Puffer, der die gleiche Größe wie die Quelle hat, indem die binäre NOT-Operation auf die Quelle angewendet wird.
~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;
}
Modifiziert den Quellpuffer direkt, indem die binäre AND-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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;
}
Füllt den Puffer mit value, beginnend mit offset und für length Bytes.
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;
}
Kopiert effizient von einer Quell- IO::Buffer in den Puffer, an offset unter Verwendung von memmove. Zum Kopieren von String-Instanzen siehe 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 kann verwendet werden, um Puffer in Strings einzufügen, die mit dem Puffer verknüpft sind.
string = "data: " # => "data: " buffer = IO::Buffer.for(string) do |buffer| buffer.copy(IO::Buffer.for("test"), 5) end # => 4 string # => "data:test"
Der Versuch, in einen schreibgeschützten Puffer zu kopieren, schlägt fehl.
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)
Siehe ::map für Details zur Erstellung von beschreibbaren Dateizuordnungen; dies wird funktionieren.
buffer = IO::Buffer.map(File.open('test.txt', 'r+')) buffer.copy(IO::Buffer.for("boom"), 0) # => 4 File.read('test.txt') # => "boom"
Der Versuch, den Puffer zu kopieren, der Platz außerhalb der Grenzen des Puffers benötigt, schlägt fehl.
buffer = IO::Buffer.new(2) buffer.copy(IO::Buffer.for('test'), 0) # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
Es ist sicher, zwischen Speicherbereichen zu kopieren, die sich überlappen. In diesem Fall werden die Daten kopiert, als ob die Daten zuerst vom Quellpuffer in einen temporären Puffer kopiert und dann vom temporären Puffer in den Zielpuffer kopiert würden.
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;
}
Iteriert über den Puffer und gibt jeden value vom buffer_type ab, beginnend bei offset.
Wenn count angegeben ist, werden nur count Werte ausgegeben.
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;
}
Iteriert über den Puffer und gibt jedes Byte ab, beginnend bei offset.
Wenn count angegeben ist, werden nur count Bytes ausgegeben.
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);
}
Der Puffer ist *extern*, wenn er auf Speicher verweist, der nicht vom Puffer selbst zugeordnet oder abgebildet wurde.
Ein Puffer, der mit ::for erstellt wurde, hat einen externen Verweis auf den Speicher des Strings.
Externe Puffer können nicht vergrößert werden.
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;
}
Wenn der Puffer auf Speicher verweist, geben Sie ihn an das Betriebssystem zurück.
-
für einen *zugeordneten* Puffer (z.B. aus einer Datei): zuordnen aufheben.
-
für einen von Grund auf neu erstellten Puffer: Speicher freigeben.
-
für einen aus einem String erstellten Puffer: die Zuordnung aufheben.
Nachdem der Puffer freigegeben wurde, können keine weiteren Operationen darauf ausgeführt werden.
Sie können einen freigegebenen Puffer vergrößern, um ihn neu zuzuordnen.
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);
}
Liest einen Teil oder den gesamten Puffer in einen String, in der angegebenen encoding. Wenn keine Codierung angegeben ist, wird Encoding::BINARY verwendet.
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);
}
Liest einen Wert vom type an der offset-Position aus dem Puffer. buffer_type sollte eines der Symbole sein
-
:U8: vorzeichenlose Ganzzahl, 1 Byte -
:S8: vorzeichenbehaftete Ganzzahl, 1 Byte -
:u16: vorzeichenlose Ganzzahl, 2 Bytes, Little-Endian -
:U16: vorzeichenlose Ganzzahl, 2 Bytes, Big-Endian -
:s16: vorzeichenbehaftete Ganzzahl, 2 Bytes, Little-Endian -
:S16: vorzeichenbehaftete Ganzzahl, 2 Bytes, Big-Endian -
:u32: vorzeichenlose Ganzzahl, 4 Bytes, Little-Endian -
:U32: vorzeichenlose Ganzzahl, 4 Bytes, Big-Endian -
:s32: vorzeichenbehaftete Ganzzahl, 4 Bytes, Little-Endian -
:S32: vorzeichenbehaftete Ganzzahl, 4 Bytes, Big-Endian -
:u64: vorzeichenlose Ganzzahl, 8 Bytes, Little-Endian -
:U64: vorzeichenlose Ganzzahl, 8 Bytes, Big-Endian -
:s64: vorzeichenbehaftete Ganzzahl, 8 Bytes, Little-Endian -
:S64: vorzeichenbehaftete Ganzzahl, 8 Bytes, Big-Endian -
:u128: vorzeichenlose Ganzzahl, 16 Bytes, Little-Endian -
:U128: vorzeichenlose Ganzzahl, 16 Bytes, Big-Endian -
:s128: vorzeichenbehaftete Ganzzahl, 16 Bytes, Little-Endian -
:S128: vorzeichenbehaftete Ganzzahl, 16 Bytes, Big-Endian -
:f32: Gleitkommazahl, 4 Bytes, Little-Endian -
:F32: Gleitkommazahl, 4 Bytes, Big-Endian -
:f64: Doppel, 8 Bytes, Little-Endian -
:F64: Doppel, 8 Bytes, Big-Endian
Ein Puffertyp bezieht sich spezifisch auf den Typ des im Puffer gespeicherten Binärpuffers. Zum Beispiel ist ein :u32-Puffertyp eine 32-Bit-vorzeichenlose Ganzzahl im Little-Endian-Format.
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;
}
Ähnlich wie get_value, jedoch kann es mehrere Puffertypen verarbeiten und gibt ein Array von Werten zurück.
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;
}
Gibt eine menschenlesbare String-Darstellung des Puffers zurück. Das genaue Format kann sich ändern.
buffer = IO::Buffer.for("Hello World") puts buffer.hexdump # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
Da Puffer normalerweise ziemlich groß sind, möchten Sie die Ausgabe möglicherweise einschränken, indem Sie den Offset und die Länge angeben.
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;
}
Erstellt eine interne Kopie des Quellpuffers. Änderungen an der Kopie wirken sich nicht auf den Quellpuffer aus.
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;
}
Untersucht den Puffer und meldet nützliche Informationen über seinen internen Zustand. Nur ein begrenzter Teil des Puffers wird im Hexdump-Stil angezeigt.
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);
}
Wenn der Puffer *intern* ist, d.h. er verweist auf Speicher, der vom Puffer selbst zugeordnet wurde.
Ein interner Puffer ist nicht mit externem Speicher (z.B. String) oder Dateizuordnungen verknüpft.
Interne Puffer werden mit ::new erstellt und sind der Standard, wenn die angeforderte Größe kleiner als die IO::Buffer::PAGE_SIZE ist und bei der Erstellung keine Zuordnung angefordert wurde.
Interne Puffer können vergrößert werden, und ein solcher Vorgang macht normalerweise alle Slices ungültig, aber nicht immer.
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;
}
Ermöglicht die exklusive Verarbeitung eines Puffers zur Nebenläufigkeitssicherheit. Während der Ausführung des Blocks gilt der Puffer als gesperrt, und kein anderer Code kann die Sperre erwerben. Außerdem kann ein gesperrter Puffer nicht mit resize oder free geändert werden.
Die folgenden Operationen erwerben eine Sperre: resize, free.
Sperren ist nicht threadsicher. Es dient als Sicherheitsnetz für nicht-blockierende Systemaufrufe. Sie können einen Puffer nur zwischen Threads mit geeigneten Synchronisationstechniken teilen.
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);
}
Wenn der Puffer *gesperrt* ist, d.h. er befindet sich innerhalb der Ausführung eines locked-Blocks. Ein gesperrter Puffer kann nicht vergrößert oder freigegeben werden, und eine weitere Sperre kann darauf nicht erworben werden.
Sperren ist nicht threadsicher, dient aber als semantische Sicherung, um sicherzustellen, dass Puffer nicht bewegt werden, während sie von einem Systemaufruf verwendet werden.
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);
}
Wenn der Puffer *zugeordnet* ist, d.h. er verweist auf Speicher, der vom Puffer zugeordnet wurde.
Zugeordnete Puffer sind entweder anonym, wenn sie mit ::new mit dem Flag IO::Buffer::MAPPED erstellt wurden oder wenn die Größe mindestens IO::Buffer::PAGE_SIZE betrug, oder sie basieren auf einer Datei, wenn sie mit ::map erstellt wurden.
Zugeordnete Puffer können normalerweise vergrößert werden, und ein solcher Vorgang macht normalerweise alle Slices ungültig, aber nicht immer.
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;
}
Modifiziert den Quellpuffer direkt, indem die binäre NOT-Operation auf die Quelle angewendet wird.
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;
}
Modifiziert den Quellpuffer direkt, indem die binäre OR-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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);
}
Liest mindestens length Bytes aus io ab der angegebenen Position from in den Puffer ab der offset-Position. Wenn ein Fehler auftritt, wird -errno zurückgegeben.
Wenn length nicht angegeben ist oder nil ist, wird standardmäßig die Größe des Puffers abzüglich des Offsets verwendet, d.h. der gesamte Puffer.
Wenn length null ist, wird genau eine pread-Operation durchgeführt.
Wenn offset nicht angegeben ist, wird standardmäßig null verwendet, d.h. der Anfang des Puffers.
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);
}
Wenn der Puffer *privat* ist, d.h. Änderungen am Puffer werden nicht in die zugrunde liegende Dateizuordnung repliziert.
# 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);
}
Schreibt mindestens length Bytes aus dem Puffer ab der offset-Position in io ab der angegebenen Position from. Wenn ein Fehler auftritt, wird -errno zurückgegeben.
Wenn length nicht angegeben ist oder nil ist, wird standardmäßig die Größe des Puffers abzüglich des Offsets verwendet, d.h. der gesamte Puffer.
Wenn length null ist, wird genau eine pwrite-Operation durchgeführt.
Wenn offset nicht angegeben ist, wird standardmäßig null verwendet, d.h. der Anfang des Puffers.
Wenn die Position from über das Ende der Datei hinausgeht, wird die Lücke mit Nullbytes (Wert 0) gefüllt.
out = File.open('output.txt', File::RDWR) # open for read/write, no truncation IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
Dies führt dazu, dass 234 (3 Bytes, beginnend an Position 1) in output.txt geschrieben werden, beginnend an Dateiposition 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);
}
Liest mindestens length Bytes aus io in den Puffer ab der offset-Position. Wenn ein Fehler auftritt, wird -errno zurückgegeben.
Wenn length nicht angegeben ist oder nil ist, wird standardmäßig die Größe des Puffers abzüglich des Offsets verwendet, d.h. der gesamte Puffer.
Wenn length null ist, wird genau eine read-Operation durchgeführt.
Wenn offset nicht angegeben ist, wird standardmäßig null verwendet, d.h. der Anfang des Puffers.
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));
}
Wenn der Puffer *schreibgeschützt* ist, d.h. der Puffer kann nicht mit set_value, set_string oder copy und ähnlichen geändert werden.
Eingefrorene Strings und schreibgeschützte Dateien erstellen schreibgeschützte Puffer.
Source
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
Ändert die Größe eines Puffers auf new_size Bytes und behält dessen Inhalt bei. Abhängig von der alten und neuen Größe kann der dem Puffer zugeordnete Speicherbereich entweder erweitert oder an einer anderen Adresse neu zugeordnet und der Inhalt kopiert werden.
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....
Externe Puffer (erstellt mit ::for) und gesperrte Puffer können nicht vergrößert werden.
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;
}
Kopiert effizient von einem Quell- String in den Puffer, an offset unter Verwendung von memmove.
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.....
Siehe auch copy für Beispiele, wie Puffer-Schreibvorgänge zum Ändern zugeordneter Strings und Dateien verwendet werden können.
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);
}
Schreibt einen value vom type an der offset-Position in den Puffer. type sollte eines der in get_value beschriebenen Symbole sein.
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......
Beachten Sie, dass bei einem Ganzzahl-type und einem value als Float eine implizite Trunkierung erfolgt.
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);
}
Schreibt values von buffer_types an der offset-Position in den Puffer. buffer_types sollte ein Array von Symbolen sein, wie in get_value beschrieben. values sollte ein Array von zu schreibenden Werten sein.
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);
}
Erzeugt einen weiteren IO::Buffer, der ein Slice (oder eine Ansicht) des aktuellen ist, beginnend bei offset Bytes und für length Bytes.
Das Slicing geschieht ohne Kopieren von Speicher, und das Slice bleibt mit der Quelle des ursprünglichen Puffers (String oder Datei) verbunden, falls vorhanden.
Wenn der Offset nicht angegeben ist, ist er null. Wenn der Offset negativ ist, wird ein ArgumentError ausgelöst.
Wenn die Länge nicht angegeben ist, ist das Slice so lang wie der ursprüngliche Puffer abzüglich des angegebenen Offsets. Wenn die Länge negativ ist, wird ein ArgumentError ausgelöst.
Löst einen RuntimeError aus, wenn offset+length außerhalb der Grenzen des aktuellen Puffers liegt.
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, ">");
}
Kurze Darstellung des Puffers. Sie enthält die Adresse, Größe und symbolische Flags. Dieses Format kann sich ändern.
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;
}
Überträgt den Besitz des zugrunde liegenden Speichers auf einen neuen Puffer, wodurch der aktuelle Puffer als uninitialisiert gekennzeichnet wird.
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));
}
Gibt zurück, ob der Puffer zugänglich ist.
Ein Puffer wird ungültig, wenn er ein Slice eines anderen Puffers (oder Strings) ist, der freigegeben oder an eine andere Adresse neu zugeordnet wurde.
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;
}
Gibt ein Array von Werten vom buffer_type zurück, beginnend bei offset.
Wenn count angegeben ist, werden nur count Werte zurückgegeben.
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);
}
Schreibt mindestens length Bytes aus dem Puffer ab der offset-Position in io. Wenn ein Fehler auftritt, wird -errno zurückgegeben.
Wenn length nicht angegeben ist oder nil ist, wird standardmäßig die Größe des Puffers abzüglich des Offsets verwendet, d.h. der gesamte Puffer.
Wenn length null ist, wird genau eine write-Operation durchgeführt.
Wenn offset nicht angegeben ist, wird standardmäßig null verwendet, d.h. der Anfang des Puffers.
out = File.open('output.txt', 'wb') IO::Buffer.for('1234567').write(out, 3)
Dies führt dazu, dass 123 in output.txt geschrieben wird.
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;
}
Modifiziert den Quellpuffer direkt, indem die binäre XOR-Operation auf die Quelle angewendet wird, wobei die Maske wiederholt wird, falls erforderlich.
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