Ruby: io_buffer.c Source File

Ruby 4.0.6p0 (2026-07-14 revision 03b6d3f8898a28604fe6cb00eae3226b821168f4)

io_buffer.c
1/**********************************************************************
2
3 io_buffer.c
4
5 Copyright (C) 2021 Samuel Grant Dawson Williams
6
7**********************************************************************/
8
9#include "ruby/io/buffer.h"
10#include "ruby/fiber/scheduler.h"
11
12// For `rb_nogvl`.
13#include "ruby/thread.h"
14
15#include "internal.h"
16#include "internal/array.h"
17#include "internal/bits.h"
18#include "internal/error.h"
19#include "internal/gc.h"
20#include "internal/numeric.h"
21#include "internal/string.h"
22#include "internal/io.h"
23
24 VALUE rb_cIOBuffer;
25 VALUE rb_eIOBufferLockedError;
26 VALUE rb_eIOBufferAllocationError;
27 VALUE rb_eIOBufferAccessError;
28 VALUE rb_eIOBufferInvalidatedError;
29 VALUE rb_eIOBufferMaskError;
30
31size_t RUBY_IO_BUFFER_PAGE_SIZE;
32size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
33
34#ifdef _WIN32
35#else
36#include <unistd.h>
37#include <sys/mman.h>
38#endif
39
40enum {
41 RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH = 16,
42
43 RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE = 256,
44 RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH = 16,
45
46 // This is used to validate the flags given by the user.
47 RB_IO_BUFFER_FLAGS_MASK = RB_IO_BUFFER_EXTERNAL | RB_IO_BUFFER_INTERNAL | RB_IO_BUFFER_MAPPED | RB_IO_BUFFER_SHARED | RB_IO_BUFFER_LOCKED | RB_IO_BUFFER_PRIVATE | RB_IO_BUFFER_READONLY,
48
49 RB_IO_BUFFER_DEBUG = 0,
50};
51
52 struct rb_io_buffer {
53 void *base;
54 size_t size;
55 enum rb_io_buffer_flags flags;
56
57#if defined(_WIN32)
58 HANDLE mapping;
59#endif
60
61 VALUE source;
62};
63
64static inline void *
65io_buffer_map_memory(size_t size, int flags)
66{
67#if defined(_WIN32)
68 void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
69
70 if (!base) {
71 rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
72 }
73#else
74 int mmap_flags = MAP_ANONYMOUS;
75 if (flags & RB_IO_BUFFER_SHARED) {
76 mmap_flags |= MAP_SHARED;
77 }
78 else {
79 mmap_flags |= MAP_PRIVATE;
80 }
81
82 void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, mmap_flags, -1, 0);
83
84 if (base == MAP_FAILED) {
85 rb_sys_fail("io_buffer_map_memory:mmap");
86 }
87
88 ruby_annotate_mmap(base, size, "Ruby:io_buffer_map_memory");
89#endif
90
91 return base;
92}
93
94static void
95io_buffer_map_file(struct rb_io_buffer *buffer, int descriptor, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
96{
97#if defined(_WIN32)
98 HANDLE file = (HANDLE)_get_osfhandle(descriptor);
99 if (!file) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
100
101 DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
102
103 if (flags & RB_IO_BUFFER_READONLY) {
104 buffer->flags |= RB_IO_BUFFER_READONLY;
105 }
106 else {
107 protect = PAGE_READWRITE;
108 access = FILE_MAP_WRITE;
109 }
110
111 if (flags & RB_IO_BUFFER_PRIVATE) {
112 protect = PAGE_WRITECOPY;
113 access = FILE_MAP_COPY;
114 buffer->flags |= RB_IO_BUFFER_PRIVATE;
115 }
116 else {
117 // This buffer refers to external buffer.
118 buffer->flags |= RB_IO_BUFFER_EXTERNAL;
119 buffer->flags |= RB_IO_BUFFER_SHARED;
120 }
121
122 HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
123 if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_map_file:CreateFileMapping -> %p\n", mapping);
124 if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
125
126 void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
127
128 if (!base) {
129 CloseHandle(mapping);
130 rb_sys_fail("io_buffer_map_file:MapViewOfFile");
131 }
132
133 buffer->mapping = mapping;
134#else
135 int protect = PROT_READ, access = 0;
136
137 if (flags & RB_IO_BUFFER_READONLY) {
138 buffer->flags |= RB_IO_BUFFER_READONLY;
139 }
140 else {
141 protect |= PROT_WRITE;
142 }
143
144 if (flags & RB_IO_BUFFER_PRIVATE) {
145 buffer->flags |= RB_IO_BUFFER_PRIVATE;
146 access |= MAP_PRIVATE;
147 }
148 else {
149 // This buffer refers to external buffer.
150 buffer->flags |= RB_IO_BUFFER_EXTERNAL;
151 buffer->flags |= RB_IO_BUFFER_SHARED;
152 access |= MAP_SHARED;
153 }
154
155 void *base = mmap(NULL, size, protect, access, descriptor, offset);
156
157 if (base == MAP_FAILED) {
158 rb_sys_fail("io_buffer_map_file:mmap");
159 }
160#endif
161
162 buffer->base = base;
163 buffer->size = size;
164
165 buffer->flags |= RB_IO_BUFFER_MAPPED;
166 buffer->flags |= RB_IO_BUFFER_FILE;
167}
168
169static void
170io_buffer_experimental(void)
171{
172 static int warned = 0;
173
174 if (warned) return;
175
176 warned = 1;
177
178 if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
179 rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
180 "IO::Buffer is experimental and both the Ruby and C interface may change in the future!"
181 );
182 }
183}
184
185static void
186io_buffer_zero(struct rb_io_buffer *buffer)
187{
188 buffer->base = NULL;
189 buffer->size = 0;
190#if defined(_WIN32)
191 buffer->mapping = NULL;
192#endif
193 buffer->source = Qnil;
194}
195
196static void
197io_buffer_initialize(VALUE self, struct rb_io_buffer *buffer, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
198{
199 if (base) {
200 // If we are provided a pointer, we use it.
201 }
202 else if (size) {
203 // If we are provided a non-zero size, we allocate it:
204 if (flags & RB_IO_BUFFER_INTERNAL) {
205 base = calloc(size, 1);
206 }
207 else if (flags & RB_IO_BUFFER_MAPPED) {
208 base = io_buffer_map_memory(size, flags);
209 }
210
211 if (!base) {
212 rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
213 }
214 }
215 else {
216 // Otherwise we don't do anything.
217 return;
218 }
219
220 buffer->base = base;
221 buffer->size = size;
222 buffer->flags = flags;
223 RB_OBJ_WRITE(self, &buffer->source, source);
224
225#if defined(_WIN32)
226 buffer->mapping = NULL;
227#endif
228}
229
230static void
231io_buffer_free(struct rb_io_buffer *buffer)
232{
233 if (buffer->base) {
234 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
235 free(buffer->base);
236 }
237
238 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
239#ifdef _WIN32
240 if (buffer->flags & RB_IO_BUFFER_FILE) {
241 UnmapViewOfFile(buffer->base);
242 }
243 else {
244 VirtualFree(buffer->base, 0, MEM_RELEASE);
245 }
246#else
247 munmap(buffer->base, buffer->size);
248#endif
249 }
250
251 // Previously we had this, but we found out due to the way GC works, we
252 // can't refer to any other Ruby objects here.
253 // if (RB_TYPE_P(buffer->source, T_STRING)) {
254 // rb_str_unlocktmp(buffer->source);
255 // }
256
257 buffer->base = NULL;
258
259 buffer->size = 0;
260 buffer->flags = 0;
261 buffer->source = Qnil;
262 }
263
264#if defined(_WIN32)
265 if (buffer->mapping) {
266 if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_free:CloseHandle -> %p\n", buffer->mapping);
267 if (!CloseHandle(buffer->mapping)) {
268 fprintf(stderr, "io_buffer_free:GetLastError -> %lu\n", GetLastError());
269 }
270 buffer->mapping = NULL;
271 }
272#endif
273}
274
275static void
276rb_io_buffer_type_mark(void *_buffer)
277{
278 struct rb_io_buffer *buffer = _buffer;
279 if (buffer->source != Qnil) {
280 if (RB_TYPE_P(buffer->source, T_STRING)) {
281 // The `source` String has to be pinned, because the `base` may point to the embedded String content,
282 // which can be otherwise moved by GC compaction.
283 rb_gc_mark(buffer->source);
284 } else {
285 rb_gc_mark_movable(buffer->source);
286 }
287 }
288}
289
290static void
291rb_io_buffer_type_compact(void *_buffer)
292{
293 struct rb_io_buffer *buffer = _buffer;
294 if (buffer->source != Qnil) {
295 if (RB_TYPE_P(buffer->source, T_STRING)) {
296 // The `source` String has to be pinned, because the `base` may point to the embedded String content,
297 // which can be otherwise moved by GC compaction.
298 } else {
299 buffer->source = rb_gc_location(buffer->source);
300 }
301 }
302}
303
304static void
305rb_io_buffer_type_free(void *_buffer)
306{
307 struct rb_io_buffer *buffer = _buffer;
308
309 io_buffer_free(buffer);
310}
311
312static size_t
313rb_io_buffer_type_size(const void *_buffer)
314{
315 const struct rb_io_buffer *buffer = _buffer;
316 size_t total = sizeof(struct rb_io_buffer);
317
318 if (buffer->flags) {
319 total += buffer->size;
320 }
321
322 return total;
323}
324
325static const rb_data_type_t rb_io_buffer_type = {
326 .wrap_struct_name = "IO::Buffer",
327 .function = {
328 .dmark = rb_io_buffer_type_mark,
329 .dfree = rb_io_buffer_type_free,
330 .dsize = rb_io_buffer_type_size,
331 .dcompact = rb_io_buffer_type_compact,
332 },
333 .data = NULL,
334 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
335};
336
337static inline enum rb_io_buffer_flags
338io_buffer_extract_flags(VALUE argument)
339{
340 if (rb_int_negative_p(argument)) {
341 rb_raise(rb_eArgError, "Flags can't be negative!");
342 }
343
344 enum rb_io_buffer_flags flags = RB_NUM2UINT(argument);
345
346 // We deliberately ignore unknown flags. Any future flags which are exposed this way should be safe to ignore.
347 return flags & RB_IO_BUFFER_FLAGS_MASK;
348}
349
350// Extract an offset argument, which must be a non-negative integer.
351static inline size_t
352io_buffer_extract_offset(VALUE argument)
353{
354 if (rb_int_negative_p(argument)) {
355 rb_raise(rb_eArgError, "Offset can't be negative!");
356 }
357
358 return NUM2SIZET(argument);
359}
360
361// Extract a length argument, which must be a non-negative integer.
362// Length is generally considered a mutable property of an object and
363// semantically should be considered a subset of "size" as a concept.
364static inline size_t
365io_buffer_extract_length(VALUE argument)
366{
367 if (rb_int_negative_p(argument)) {
368 rb_raise(rb_eArgError, "Length can't be negative!");
369 }
370
371 return NUM2SIZET(argument);
372}
373
374// Extract a size argument, which must be a non-negative integer.
375// Size is generally considered an immutable property of an object.
376static inline size_t
377io_buffer_extract_size(VALUE argument)
378{
379 if (rb_int_negative_p(argument)) {
380 rb_raise(rb_eArgError, "Size can't be negative!");
381 }
382
383 return NUM2SIZET(argument);
384}
385
386// Extract a width argument, which must be a non-negative integer, and must be
387// at least the given minimum.
388static inline size_t
389io_buffer_extract_width(VALUE argument, size_t minimum)
390{
391 if (rb_int_negative_p(argument)) {
392 rb_raise(rb_eArgError, "Width can't be negative!");
393 }
394
395 size_t width = NUM2SIZET(argument);
396
397 if (width < minimum) {
398 rb_raise(rb_eArgError, "Width must be at least %" PRIuSIZE "!", minimum);
399 }
400
401 return width;
402}
403
404// Compute the default length for a buffer, given an offset into that buffer.
405// The default length is the size of the buffer minus the offset. The offset
406// must be less than the size of the buffer otherwise the length will be
407// invalid; in that case, an ArgumentError exception will be raised.
408static inline size_t
409io_buffer_default_length(const struct rb_io_buffer *buffer, size_t offset)
410{
411 if (offset > buffer->size) {
412 rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
413 }
414
415 // Note that the "length" is computed by the size the offset.
416 return buffer->size - offset;
417}
418
419// Extract the optional length and offset arguments, returning the buffer.
420// The length and offset are optional, but if they are provided, they must be
421// positive integers. If the length is not provided, the default length is
422// computed from the buffer size and offset. If the offset is not provided, it
423// defaults to zero.
424static inline struct rb_io_buffer *
425io_buffer_extract_length_offset(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
426{
427 struct rb_io_buffer *buffer = NULL;
428 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
429
430 if (argc >= 2 && !NIL_P(argv[1])) {
431 *offset = io_buffer_extract_offset(argv[1]);
432 }
433 else {
434 *offset = 0;
435 }
436
437 if (argc >= 1 && !NIL_P(argv[0])) {
438 *length = io_buffer_extract_length(argv[0]);
439 }
440 else {
441 *length = io_buffer_default_length(buffer, *offset);
442 }
443
444 return buffer;
445}
446
447// Extract the optional offset and length arguments, returning the buffer.
448// Similar to `io_buffer_extract_length_offset` but with the order of arguments
449// reversed.
450//
451// After much consideration, I decided to accept both forms.
452// The `(offset, length)` order is more natural when referring about data,
453// while the `(length, offset)` order is more natural when referring to
454// read/write operations. In many cases, with the latter form, `offset`
455// is usually not supplied.
456static inline struct rb_io_buffer *
457io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offset, size_t *length)
458{
459 struct rb_io_buffer *buffer = NULL;
460 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
461
462 if (argc >= 1 && !NIL_P(argv[0])) {
463 *offset = io_buffer_extract_offset(argv[0]);
464 }
465 else {
466 *offset = 0;
467 }
468
469 if (argc >= 2 && !NIL_P(argv[1])) {
470 *length = io_buffer_extract_length(argv[1]);
471 }
472 else {
473 *length = io_buffer_default_length(buffer, *offset);
474 }
475
476 return buffer;
477}
478
479 VALUE
480rb_io_buffer_type_allocate(VALUE self)
481{
482 io_buffer_experimental();
483
484 struct rb_io_buffer *buffer = NULL;
485 VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
486
487 io_buffer_zero(buffer);
488
489 return instance;
490}
491
492static VALUE io_buffer_for_make_instance(VALUE klass, VALUE string, enum rb_io_buffer_flags flags)
493{
494 VALUE instance = rb_io_buffer_type_allocate(klass);
495
496 struct rb_io_buffer *buffer = NULL;
497 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
498
499 flags |= RB_IO_BUFFER_EXTERNAL;
500
501 if (RB_OBJ_FROZEN(string))
502 flags |= RB_IO_BUFFER_READONLY;
503
504 if (!(flags & RB_IO_BUFFER_READONLY))
505 rb_str_modify(string);
506
507 io_buffer_initialize(instance, buffer, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
508
509 return instance;
510}
511
512 struct io_buffer_for_yield_instance_arguments {
513 VALUE klass;
514 VALUE string;
515 VALUE instance;
516 enum rb_io_buffer_flags flags;
517};
518
519static VALUE
520io_buffer_for_yield_instance(VALUE _arguments)
521{
522 struct io_buffer_for_yield_instance_arguments *arguments = (struct io_buffer_for_yield_instance_arguments *)_arguments;
523
524 arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
525
526 if (!RB_OBJ_FROZEN(arguments->string)) {
527 rb_str_locktmp(arguments->string);
528 }
529
530 return rb_yield(arguments->instance);
531}
532
533static VALUE
534io_buffer_for_yield_instance_ensure(VALUE _arguments)
535{
536 struct io_buffer_for_yield_instance_arguments *arguments = (struct io_buffer_for_yield_instance_arguments *)_arguments;
537
538 if (arguments->instance != Qnil) {
539 rb_io_buffer_free(arguments->instance);
540 }
541
542 if (!RB_OBJ_FROZEN(arguments->string)) {
543 rb_str_unlocktmp(arguments->string);
544 }
545
546 return Qnil;
547}
548
549/*
550 * call-seq:
551 * IO::Buffer.for(string) -> readonly io_buffer
552 * IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
553 *
554 * Creates a zero-copy IO::Buffer from the given string's memory. Without a
555 * block a frozen internal copy of the string is created efficiently and used
556 * as the buffer source. When a block is provided, the buffer is associated
557 * directly with the string's internal buffer and updating the buffer will
558 * update the string.
559 *
560 * Until #free is invoked on the buffer, either explicitly or via the garbage
561 * collector, the source string will be locked and cannot be modified.
562 *
563 * If the string is frozen, it will create a read-only buffer which cannot be
564 * modified. If the string is shared, it may trigger a copy-on-write when
565 * using the block form.
566 *
567 * string = 'test'
568 * buffer = IO::Buffer.for(string)
569 * buffer.external? #=> true
570 *
571 * buffer.get_string(0, 1)
572 * # => "t"
573 * string
574 * # => "test"
575 *
576 * buffer.resize(100)
577 * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
578 *
579 * IO::Buffer.for(string) do |buffer|
580 * buffer.set_string("T")
581 * string
582 * # => "Test"
583 * end
584 */
585 VALUE
586rb_io_buffer_type_for(VALUE klass, VALUE string)
587{
588 StringValue(string);
589
590 // If the string is frozen, both code paths are okay.
591 // If the string is not frozen, if a block is not given, it must be frozen.
592 if (rb_block_given_p()) {
593 struct io_buffer_for_yield_instance_arguments arguments = {
594 .klass = klass,
595 .string = string,
596 .instance = Qnil,
597 .flags = 0,
598 };
599
600 return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
601 }
602 else {
603 // This internally returns the source string if it's already frozen.
604 string = rb_str_tmp_frozen_acquire(string);
605 return io_buffer_for_make_instance(klass, string, RB_IO_BUFFER_READONLY);
606 }
607}
608
609/*
610 * call-seq:
611 * IO::Buffer.string(length) {|io_buffer| ... read/write io_buffer ...} -> string
612 *
613 * Creates a new string of the given length and yields a zero-copy IO::Buffer
614 * instance to the block which uses the string as a source. The block is
615 * expected to write to the buffer and the string will be returned.
616 *
617 * IO::Buffer.string(4) do |buffer|
618 * buffer.set_string("Ruby")
619 * end
620 * # => "Ruby"
621 */
622 VALUE
623rb_io_buffer_type_string(VALUE klass, VALUE length)
624{
625 VALUE string = rb_str_new(NULL, RB_NUM2LONG(length));
626
627 struct io_buffer_for_yield_instance_arguments arguments = {
628 .klass = klass,
629 .string = string,
630 .instance = Qnil,
631 };
632
633 rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
634
635 return string;
636}
637
638 VALUE
639rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
640{
641 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
642
643 struct rb_io_buffer *buffer = NULL;
644 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
645
646 io_buffer_initialize(instance, buffer, base, size, flags, Qnil);
647
648 return instance;
649}
650
651 VALUE
652rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
653{
654 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
655
656 struct rb_io_buffer *buffer = NULL;
657 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, buffer);
658
659 int descriptor = rb_io_descriptor(io);
660
661 io_buffer_map_file(buffer, descriptor, size, offset, flags);
662
663 return instance;
664}
665
666/*
667 * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
668 *
669 * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
670 * +file+ should be a +File+ instance, opened for reading or reading and writing.
671 *
672 * Optional +size+ and +offset+ of mapping can be specified.
673 * Trying to map an empty file or specify +size+ of 0 will raise an error.
674 * Valid values for +offset+ are system-dependent.
675 *
676 * By default, the buffer is writable and expects the file to be writable.
677 * It is also shared, so several processes can use the same mapping.
678 *
679 * You can pass IO::Buffer::READONLY in +flags+ argument to make a read-only buffer;
680 * this allows to work with files opened only for reading.
681 * Specifying IO::Buffer::PRIVATE in +flags+ creates a private mapping,
682 * which will not impact other processes or the underlying file.
683 * It also allows updating a buffer created from a read-only file.
684 *
685 * File.write('test.txt', 'test')
686 *
687 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
688 * # => #<IO::Buffer 0x00000001014a0000+4 EXTERNAL MAPPED FILE SHARED READONLY>
689 *
690 * buffer.readonly? # => true
691 *
692 * buffer.get_string
693 * # => "test"
694 *
695 * buffer.set_string('b', 0)
696 * # 'IO::Buffer#set_string': Buffer is not writable! (IO::Buffer::AccessError)
697 *
698 * # create read/write mapping: length 4 bytes, offset 0, flags 0
699 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
700 * buffer.set_string('b', 0)
701 * # => 1
702 *
703 * # Check it
704 * File.read('test.txt')
705 * # => "best"
706 *
707 * Note that some operating systems may not have cache coherency between mapped
708 * buffers and file reads.
709 */
710static VALUE
711io_buffer_map(int argc, VALUE *argv, VALUE klass)
712{
713 rb_check_arity(argc, 1, 4);
714
715 // We might like to handle a string path?
716 VALUE io = argv[0];
717
718 rb_off_t file_size = rb_file_size(io);
719 // Compiler can confirm that we handled file_size <= 0 case:
720 if (UNLIKELY(file_size <= 0)) {
721 rb_raise(rb_eArgError, "Invalid negative or zero file size!");
722 }
723 // Here, we assume that file_size is positive:
724 else if (UNLIKELY((uintmax_t)file_size > SIZE_MAX)) {
725 rb_raise(rb_eArgError, "File larger than address space!");
726 }
727
728 size_t size;
729 if (argc >= 2 && !RB_NIL_P(argv[1])) {
730 size = io_buffer_extract_size(argv[1]);
731 if (UNLIKELY(size == 0)) {
732 rb_raise(rb_eArgError, "Size can't be zero!");
733 }
734 if (UNLIKELY(size > (size_t)file_size)) {
735 rb_raise(rb_eArgError, "Size can't be larger than file size!");
736 }
737 }
738 else {
739 // This conversion should be safe:
740 size = (size_t)file_size;
741 }
742
743 // This is the file offset, not the buffer offset:
744 rb_off_t offset = 0;
745 if (argc >= 3) {
746 offset = NUM2OFFT(argv[2]);
747 if (UNLIKELY(offset < 0)) {
748 rb_raise(rb_eArgError, "Offset can't be negative!");
749 }
750 if (UNLIKELY(offset >= file_size)) {
751 rb_raise(rb_eArgError, "Offset too large!");
752 }
753 if (RB_NIL_P(argv[1])) {
754 // Decrease size if it's set from the actual file size:
755 size = (size_t)(file_size - offset);
756 }
757 else if (UNLIKELY((size_t)(file_size - offset) < size)) {
758 rb_raise(rb_eArgError, "Offset too large!");
759 }
760 }
761
762 enum rb_io_buffer_flags flags = 0;
763 if (argc >= 4) {
764 flags = io_buffer_extract_flags(argv[3]);
765 }
766
767 return rb_io_buffer_map(io, size, offset, flags);
768}
769
770// Compute the optimal allocation flags for a buffer of the given size.
771static inline enum rb_io_buffer_flags
772io_flags_for_size(size_t size)
773{
774 if (size >= RUBY_IO_BUFFER_PAGE_SIZE) {
775 return RB_IO_BUFFER_MAPPED;
776 }
777
778 return RB_IO_BUFFER_INTERNAL;
779}
780
781/*
782 * call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
783 *
784 * Create a new zero-filled IO::Buffer of +size+ bytes.
785 * By default, the buffer will be _internal_: directly allocated chunk
786 * of the memory. But if the requested +size+ is more than OS-specific
787 * IO::Buffer::PAGE_SIZE, the buffer would be allocated using the
788 * virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
789 * on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
790 * as a second parameter.
791 *
792 * buffer = IO::Buffer.new(4)
793 * # =>
794 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
795 * # 0x00000000 00 00 00 00 ....
796 *
797 * buffer.get_string(0, 1) # => "\x00"
798 *
799 * buffer.set_string("test")
800 * buffer
801 * # =>
802 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
803 * # 0x00000000 74 65 73 74 test
804 */
805 VALUE
806rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
807{
808 rb_check_arity(argc, 0, 2);
809
810 struct rb_io_buffer *buffer = NULL;
811 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
812
813 size_t size;
814 if (argc > 0) {
815 size = io_buffer_extract_size(argv[0]);
816 }
817 else {
818 size = RUBY_IO_BUFFER_DEFAULT_SIZE;
819 }
820
821 enum rb_io_buffer_flags flags = 0;
822 if (argc >= 2) {
823 flags = io_buffer_extract_flags(argv[1]);
824 }
825 else {
826 flags |= io_flags_for_size(size);
827 }
828
829 io_buffer_initialize(self, buffer, NULL, size, flags, Qnil);
830
831 return self;
832}
833
834static int
835io_buffer_validate_slice(VALUE source, void *base, size_t size)
836{
837 void *source_base = NULL;
838 size_t source_size = 0;
839
840 if (RB_TYPE_P(source, T_STRING)) {
841 RSTRING_GETMEM(source, source_base, source_size);
842 }
843 else {
844 rb_io_buffer_get_bytes(source, &source_base, &source_size);
845 }
846
847 // Source is invalid:
848 if (source_base == NULL) return 0;
849
850 // Base is out of range:
851 if (base < source_base) return 0;
852
853 const void *source_end = (char*)source_base + source_size;
854 const void *end = (char*)base + size;
855
856 // End is out of range:
857 if (end > source_end) return 0;
858
859 // It seems okay:
860 return 1;
861}
862
863static int
864io_buffer_validate(struct rb_io_buffer *buffer)
865{
866 if (buffer->source != Qnil) {
867 // Only slices incur this overhead, unfortunately... better safe than sorry!
868 return io_buffer_validate_slice(buffer->source, buffer->base, buffer->size);
869 }
870 else {
871 return 1;
872 }
873}
874
875enum rb_io_buffer_flags
876rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
877{
878 struct rb_io_buffer *buffer = NULL;
879 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
880
881 if (io_buffer_validate(buffer)) {
882 if (buffer->base) {
883 *base = buffer->base;
884 *size = buffer->size;
885
886 return buffer->flags;
887 }
888 }
889
890 *base = NULL;
891 *size = 0;
892
893 return 0;
894}
895
896// Internal function for accessing bytes for writing, wil
897static void
898io_buffer_validate_for_writing(struct rb_io_buffer *buffer)
899{
900 if (buffer->flags & RB_IO_BUFFER_READONLY ||
901 (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
902 rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
903 }
904
905 if (!io_buffer_validate(buffer)) {
906 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
907 }
908}
909
910static struct rb_io_buffer *
911get_io_buffer_for_writing(VALUE self)
912{
913 struct rb_io_buffer *buffer = NULL;
914 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
915 io_buffer_validate_for_writing(buffer);
916 return buffer;
917}
918
919static inline void
920io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
921{
922 io_buffer_validate_for_writing(buffer);
923
924 if (buffer->base) {
925 *base = buffer->base;
926 *size = buffer->size;
927 } else {
928 *base = NULL;
929 *size = 0;
930 }
931}
932
933void
934rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
935{
936 struct rb_io_buffer *buffer = NULL;
937 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
938
939 io_buffer_get_bytes_for_writing(buffer, base, size);
940}
941
942static void
943io_buffer_validate_for_reading(struct rb_io_buffer *buffer)
944{
945 if (!io_buffer_validate(buffer)) {
946 rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
947 }
948}
949
950static void
951io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
952{
953 io_buffer_validate_for_reading(buffer);
954
955 if (buffer->base) {
956 *base = buffer->base;
957 *size = buffer->size;
958 } else {
959 *base = NULL;
960 *size = 0;
961 }
962}
963
964void
965rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
966{
967 struct rb_io_buffer *buffer = NULL;
968 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
969
970 io_buffer_get_bytes_for_reading(buffer, base, size);
971}
972
973/*
974 * call-seq: to_s -> string
975 *
976 * Short representation of the buffer. It includes the address, size and
977 * symbolic flags. This format is subject to change.
978 *
979 * puts IO::Buffer.new(4) # uses to_s internally
980 * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
981 */
982 VALUE
983rb_io_buffer_to_s(VALUE self)
984{
985 struct rb_io_buffer *buffer = NULL;
986 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
987
988 VALUE result = rb_str_new_cstr("#<");
989
990 rb_str_append(result, rb_class_name(CLASS_OF(self)));
991 rb_str_catf(result, " %p+%"PRIdSIZE, buffer->base, buffer->size);
992
993 if (buffer->base == NULL) {
994 rb_str_cat2(result, " NULL");
995 }
996
997 if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
998 rb_str_cat2(result, " EXTERNAL");
999 }
1000
1001 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
1002 rb_str_cat2(result, " INTERNAL");
1003 }
1004
1005 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
1006 rb_str_cat2(result, " MAPPED");
1007 }
1008
1009 if (buffer->flags & RB_IO_BUFFER_FILE) {
1010 rb_str_cat2(result, " FILE");
1011 }
1012
1013 if (buffer->flags & RB_IO_BUFFER_SHARED) {
1014 rb_str_cat2(result, " SHARED");
1015 }
1016
1017 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1018 rb_str_cat2(result, " LOCKED");
1019 }
1020
1021 if (buffer->flags & RB_IO_BUFFER_PRIVATE) {
1022 rb_str_cat2(result, " PRIVATE");
1023 }
1024
1025 if (buffer->flags & RB_IO_BUFFER_READONLY) {
1026 rb_str_cat2(result, " READONLY");
1027 }
1028
1029 if (buffer->source != Qnil) {
1030 rb_str_cat2(result, " SLICE");
1031 }
1032
1033 if (!io_buffer_validate(buffer)) {
1034 rb_str_cat2(result, " INVALID");
1035 }
1036
1037 return rb_str_cat2(result, ">");
1038}
1039
1040// Compute the output size of a hexdump of the given width (bytes per line), total size, and whether it is the first line in the output.
1041// This is used to preallocate the output string.
1042inline static size_t
1043io_buffer_hexdump_output_size(size_t width, size_t size, int first)
1044{
1045 // The preview on the right hand side is 1:1:
1046 size_t total = size;
1047
1048 size_t whole_lines = (size / width);
1049 size_t partial_line = (size % width) ? 1 : 0;
1050
1051 // For each line:
1052 // 1 byte 10 bytes 1 byte width*3 bytes 1 byte size bytes
1053 // (newline) (address) (space) (hexdump ) (space) (preview)
1054 total += (whole_lines + partial_line) * (1 + 10 + width*3 + 1 + 1);
1055
1056 // If the hexdump is the first line, one less newline will be emitted:
1057 if (size && first) total -= 1;
1058
1059 return total;
1060}
1061
1062// Append a hexdump of the given width (bytes per line), base address, size, and whether it is the first line in the output.
1063// If the hexdump is not the first line, it will prepend a newline if there is any output at all.
1064// If formatting here is adjusted, please update io_buffer_hexdump_output_size accordingly.
1065static VALUE
1066io_buffer_hexdump(VALUE string, size_t width, const char *base, size_t length, size_t offset, int first)
1067{
1068 char *text = alloca(width+1);
1069 text[width] = '\0';
1070
1071 for (; offset < length; offset += width) {
1072 memset(text, '\0', width);
1073 if (first) {
1074 rb_str_catf(string, "0x%08" PRIxSIZE " ", offset);
1075 first = 0;
1076 }
1077 else {
1078 rb_str_catf(string, "\n0x%08" PRIxSIZE " ", offset);
1079 }
1080
1081 for (size_t i = 0; i < width; i += 1) {
1082 if (offset+i < length) {
1083 unsigned char value = ((unsigned char*)base)[offset+i];
1084
1085 if (value < 127 && isprint(value)) {
1086 text[i] = (char)value;
1087 }
1088 else {
1089 text[i] = '.';
1090 }
1091
1092 rb_str_catf(string, " %02x", value);
1093 }
1094 else {
1095 rb_str_cat2(string, " ");
1096 }
1097 }
1098
1099 rb_str_catf(string, " %s", text);
1100 }
1101
1102 return string;
1103}
1104
1105/*
1106 * call-seq: inspect -> string
1107 *
1108 * Inspect the buffer and report useful information about it's internal state.
1109 * Only a limited portion of the buffer will be displayed in a hexdump style
1110 * format.
1111 *
1112 * buffer = IO::Buffer.for("Hello World")
1113 * puts buffer.inspect
1114 * # #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
1115 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1116 */
1117 VALUE
1118rb_io_buffer_inspect(VALUE self)
1119{
1120 struct rb_io_buffer *buffer = NULL;
1121 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1122
1123 VALUE result = rb_io_buffer_to_s(self);
1124
1125 if (io_buffer_validate(buffer)) {
1126 // Limit the maximum size generated by inspect:
1127 size_t size = buffer->size;
1128 int clamped = 0;
1129
1130 if (size > RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE) {
1131 size = RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE;
1132 clamped = 1;
1133 }
1134
1135 io_buffer_hexdump(result, RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH, buffer->base, size, 0, 0);
1136
1137 if (clamped) {
1138 rb_str_catf(result, "\n(and %" PRIuSIZE " more bytes not printed)", buffer->size - size);
1139 }
1140 }
1141
1142 return result;
1143}
1144
1145/*
1146 * call-seq: size -> integer
1147 *
1148 * Returns the size of the buffer that was explicitly set (on creation with ::new
1149 * or on #resize), or deduced on buffer's creation from string or file.
1150 */
1151 VALUE
1152rb_io_buffer_size(VALUE self)
1153{
1154 struct rb_io_buffer *buffer = NULL;
1155 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1156
1157 return SIZET2NUM(buffer->size);
1158}
1159
1160/*
1161 * call-seq: valid? -> true or false
1162 *
1163 * Returns whether the buffer buffer is accessible.
1164 *
1165 * A buffer becomes invalid if it is a slice of another buffer (or string)
1166 * which has been freed or re-allocated at a different address.
1167 */
1168static VALUE
1169rb_io_buffer_valid_p(VALUE self)
1170{
1171 struct rb_io_buffer *buffer = NULL;
1172 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1173
1174 return RBOOL(io_buffer_validate(buffer));
1175}
1176
1177/*
1178 * call-seq: null? -> true or false
1179 *
1180 * If the buffer was freed with #free, transferred with #transfer, or was
1181 * never allocated in the first place.
1182 *
1183 * buffer = IO::Buffer.new(0)
1184 * buffer.null? #=> true
1185 *
1186 * buffer = IO::Buffer.new(4)
1187 * buffer.null? #=> false
1188 * buffer.free
1189 * buffer.null? #=> true
1190 */
1191static VALUE
1192rb_io_buffer_null_p(VALUE self)
1193{
1194 struct rb_io_buffer *buffer = NULL;
1195 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1196
1197 return RBOOL(buffer->base == NULL);
1198}
1199
1200/*
1201 * call-seq: empty? -> true or false
1202 *
1203 * If the buffer has 0 size: it is created by ::new with size 0, or with ::for
1204 * from an empty string. (Note that empty files can't be mapped, so the buffer
1205 * created with ::map will never be empty.)
1206 */
1207static VALUE
1208rb_io_buffer_empty_p(VALUE self)
1209{
1210 struct rb_io_buffer *buffer = NULL;
1211 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1212
1213 return RBOOL(buffer->size == 0);
1214}
1215
1216/*
1217 * call-seq: external? -> true or false
1218 *
1219 * The buffer is _external_ if it references the memory which is not
1220 * allocated or mapped by the buffer itself.
1221 *
1222 * A buffer created using ::for has an external reference to the string's
1223 * memory.
1224 *
1225 * External buffer can't be resized.
1226 */
1227static VALUE
1228rb_io_buffer_external_p(VALUE self)
1229{
1230 struct rb_io_buffer *buffer = NULL;
1231 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1232
1233 return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
1234}
1235
1236/*
1237 * call-seq: internal? -> true or false
1238 *
1239 * If the buffer is _internal_, meaning it references memory allocated by the
1240 * buffer itself.
1241 *
1242 * An internal buffer is not associated with any external memory (e.g. string)
1243 * or file mapping.
1244 *
1245 * Internal buffers are created using ::new and is the default when the
1246 * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
1247 * requested to be mapped on creation.
1248 *
1249 * Internal buffers can be resized, and such an operation will typically
1250 * invalidate all slices, but not always.
1251 */
1252static VALUE
1253rb_io_buffer_internal_p(VALUE self)
1254{
1255 struct rb_io_buffer *buffer = NULL;
1256 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1257
1258 return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
1259}
1260
1261/*
1262 * call-seq: mapped? -> true or false
1263 *
1264 * If the buffer is _mapped_, meaning it references memory mapped by the
1265 * buffer.
1266 *
1267 * Mapped buffers are either anonymous, if created by ::new with the
1268 * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
1269 * or backed by a file if created with ::map.
1270 *
1271 * Mapped buffers can usually be resized, and such an operation will typically
1272 * invalidate all slices, but not always.
1273 */
1274static VALUE
1275rb_io_buffer_mapped_p(VALUE self)
1276{
1277 struct rb_io_buffer *buffer = NULL;
1278 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1279
1280 return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
1281}
1282
1283/*
1284 * call-seq: shared? -> true or false
1285 *
1286 * If the buffer is _shared_, meaning it references memory that can be shared
1287 * with other processes (and thus might change without being modified
1288 * locally).
1289 *
1290 * # Create a test file:
1291 * File.write('test.txt', 'test')
1292 *
1293 * # Create a shared mapping from the given file, the file must be opened in
1294 * # read-write mode unless we also specify IO::Buffer::READONLY:
1295 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), nil, 0)
1296 * # => #<IO::Buffer 0x00007f1bffd5e000+4 EXTERNAL MAPPED SHARED>
1297 *
1298 * # Write to the buffer, which will modify the mapped file:
1299 * buffer.set_string('b', 0)
1300 * # => 1
1301 *
1302 * # The file itself is modified:
1303 * File.read('test.txt')
1304 * # => "best"
1305 */
1306static VALUE
1307rb_io_buffer_shared_p(VALUE self)
1308{
1309 struct rb_io_buffer *buffer = NULL;
1310 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1311
1312 return RBOOL(buffer->flags & RB_IO_BUFFER_SHARED);
1313}
1314
1315/*
1316 * call-seq: locked? -> true or false
1317 *
1318 * If the buffer is _locked_, meaning it is inside #locked block execution.
1319 * Locked buffer can't be resized or freed, and another lock can't be acquired
1320 * on it.
1321 *
1322 * Locking is not thread safe, but is a semantic used to ensure buffers don't
1323 * move while being used by a system call.
1324 *
1325 * buffer.locked do
1326 * buffer.write(io) # theoretical system call interface
1327 * end
1328 */
1329static VALUE
1330rb_io_buffer_locked_p(VALUE self)
1331{
1332 struct rb_io_buffer *buffer = NULL;
1333 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1334
1335 return RBOOL(buffer->flags & RB_IO_BUFFER_LOCKED);
1336}
1337
1338/* call-seq: private? -> true or false
1339 *
1340 * If the buffer is _private_, meaning modifications to the buffer will not
1341 * be replicated to the underlying file mapping.
1342 *
1343 * # Create a test file:
1344 * File.write('test.txt', 'test')
1345 *
1346 * # Create a private mapping from the given file. Note that the file here
1347 * # is opened in read-only mode, but it doesn't matter due to the private
1348 * # mapping:
1349 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE)
1350 * # => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE>
1351 *
1352 * # Write to the buffer (invoking CoW of the underlying file buffer):
1353 * buffer.set_string('b', 0)
1354 * # => 1
1355 *
1356 * # The file itself is not modified:
1357 * File.read('test.txt')
1358 * # => "test"
1359 */
1360static VALUE
1361rb_io_buffer_private_p(VALUE self)
1362{
1363 struct rb_io_buffer *buffer = NULL;
1364 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1365
1366 return RBOOL(buffer->flags & RB_IO_BUFFER_PRIVATE);
1367}
1368
1369int
1370rb_io_buffer_readonly_p(VALUE self)
1371{
1372 struct rb_io_buffer *buffer = NULL;
1373 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1374
1375 return buffer->flags & RB_IO_BUFFER_READONLY;
1376}
1377
1378/*
1379 * call-seq: readonly? -> true or false
1380 *
1381 * If the buffer is <i>read only</i>, meaning the buffer cannot be modified using
1382 * #set_value, #set_string or #copy and similar.
1383 *
1384 * Frozen strings and read-only files create read-only buffers.
1385 */
1386static VALUE
1387io_buffer_readonly_p(VALUE self)
1388{
1389 return RBOOL(rb_io_buffer_readonly_p(self));
1390}
1391
1392static int
1393io_buffer_try_lock(struct rb_io_buffer *buffer)
1394{
1395 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1396 return 0;
1397 }
1398
1399 buffer->flags |= RB_IO_BUFFER_LOCKED;
1400 return 1;
1401}
1402
1403static void
1404io_buffer_lock(struct rb_io_buffer *buffer)
1405{
1406 if (!io_buffer_try_lock(buffer)) {
1407 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
1408 }
1409}
1410
1411 VALUE
1412rb_io_buffer_lock(VALUE self)
1413{
1414 struct rb_io_buffer *buffer = NULL;
1415 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1416
1417 io_buffer_lock(buffer);
1418
1419 return self;
1420}
1421
1422static void
1423io_buffer_unlock(struct rb_io_buffer *buffer)
1424{
1425 if (!(buffer->flags & RB_IO_BUFFER_LOCKED)) {
1426 rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
1427 }
1428
1429 buffer->flags &= ~RB_IO_BUFFER_LOCKED;
1430}
1431
1432 VALUE
1433rb_io_buffer_unlock(VALUE self)
1434{
1435 struct rb_io_buffer *buffer = NULL;
1436 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1437
1438 io_buffer_unlock(buffer);
1439
1440 return self;
1441}
1442
1443int
1444rb_io_buffer_try_unlock(VALUE self)
1445{
1446 struct rb_io_buffer *buffer = NULL;
1447 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1448
1449 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1450 buffer->flags &= ~RB_IO_BUFFER_LOCKED;
1451 return 1;
1452 }
1453
1454 return 0;
1455}
1456
1457static VALUE
1458rb_io_buffer_locked_ensure(VALUE self)
1459{
1460 struct rb_io_buffer *buffer = NULL;
1461 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1462
1463 buffer->flags &= ~RB_IO_BUFFER_LOCKED;
1464
1465 return Qnil;
1466}
1467
1468/*
1469 * call-seq: locked { ... }
1470 *
1471 * Allows to process a buffer in exclusive way, for concurrency-safety. While
1472 * the block is performed, the buffer is considered locked, and no other code
1473 * can enter the lock. Also, locked buffer can't be changed with #resize or
1474 * #free.
1475 *
1476 * The following operations acquire a lock: #resize, #free.
1477 *
1478 * Locking is not thread safe. It is designed as a safety net around
1479 * non-blocking system calls. You can only share a buffer between threads with
1480 * appropriate synchronisation techniques.
1481 *
1482 * buffer = IO::Buffer.new(4)
1483 * buffer.locked? #=> false
1484 *
1485 * Fiber.schedule do
1486 * buffer.locked do
1487 * buffer.write(io) # theoretical system call interface
1488 * end
1489 * end
1490 *
1491 * Fiber.schedule do
1492 * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
1493 * buffer.locked do
1494 * buffer.set_string("test", 0)
1495 * end
1496 * end
1497 */
1498 VALUE
1499rb_io_buffer_locked(VALUE self)
1500{
1501 struct rb_io_buffer *buffer = NULL;
1502 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1503
1504 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1505 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
1506 }
1507
1508 buffer->flags |= RB_IO_BUFFER_LOCKED;
1509
1510 return rb_ensure(rb_yield, self, rb_io_buffer_locked_ensure, self);
1511}
1512
1513/*
1514 * call-seq: free -> self
1515 *
1516 * If the buffer references memory, release it back to the operating system.
1517 * * for a _mapped_ buffer (e.g. from file): unmap.
1518 * * for a buffer created from scratch: free memory.
1519 * * for a buffer created from string: undo the association.
1520 *
1521 * After the buffer is freed, no further operations can't be performed on it.
1522 *
1523 * You can resize a freed buffer to re-allocate it.
1524 *
1525 * buffer = IO::Buffer.for('test')
1526 * buffer.free
1527 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
1528 *
1529 * buffer.get_value(:U8, 0)
1530 * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
1531 *
1532 * buffer.get_string
1533 * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
1534 *
1535 * buffer.null?
1536 * # => true
1537 */
1538 VALUE
1539rb_io_buffer_free(VALUE self)
1540{
1541 struct rb_io_buffer *buffer = NULL;
1542 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1543
1544 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1545 rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
1546 }
1547
1548 io_buffer_free(buffer);
1549
1550 return self;
1551}
1552
1553 VALUE rb_io_buffer_free_locked(VALUE self)
1554{
1555 struct rb_io_buffer *buffer = NULL;
1556 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1557
1558 io_buffer_unlock(buffer);
1559 io_buffer_free(buffer);
1560
1561 return self;
1562}
1563
1564static bool
1565size_sum_is_bigger_than(size_t a, size_t b, size_t x)
1566{
1567 struct rbimpl_size_overflow_tag size = rbimpl_size_add_overflow(a, b);
1568 return size.overflowed || size.result > x;
1569}
1570
1571// Validate that access to the buffer is within bounds, assuming you want to
1572// access length bytes from the specified offset.
1573static inline void
1574io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
1575{
1576 io_buffer_validate_for_reading(buffer);
1577
1578 if (size_sum_is_bigger_than(offset, length, buffer->size)) {
1579 rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
1580 }
1581}
1582
1583/*
1584 * call-seq: hexdump([offset, [length, [width]]]) -> string
1585 *
1586 * Returns a human-readable string representation of the buffer. The exact
1587 * format is subject to change.
1588 *
1589 * buffer = IO::Buffer.for("Hello World")
1590 * puts buffer.hexdump
1591 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1592 *
1593 * As buffers are usually fairly big, you may want to limit the output by
1594 * specifying the offset and length:
1595 *
1596 * puts buffer.hexdump(6, 5)
1597 * # 0x00000006 57 6f 72 6c 64 World
1598 */
1599static VALUE
1600rb_io_buffer_hexdump(int argc, VALUE *argv, VALUE self)
1601{
1602 rb_check_arity(argc, 0, 3);
1603
1604 size_t offset, length;
1605 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
1606
1607 size_t width = RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH;
1608 if (argc >= 3) {
1609 width = io_buffer_extract_width(argv[2], 1);
1610 }
1611
1612 // This may raise an exception if the offset/length is invalid:
1613 io_buffer_validate_range(buffer, offset, length);
1614
1615 VALUE result = Qnil;
1616
1617 if (io_buffer_validate(buffer) && buffer->base) {
1618 result = rb_str_buf_new(io_buffer_hexdump_output_size(width, length, 1));
1619
1620 io_buffer_hexdump(result, width, buffer->base, offset+length, offset, 1);
1621 }
1622
1623 return result;
1624}
1625
1626static VALUE
1627rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_t length)
1628{
1629 io_buffer_validate_range(buffer, offset, length);
1630
1631 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1632 struct rb_io_buffer *slice = NULL;
1633 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
1634
1635 slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
1636 slice->base = (char*)buffer->base + offset;
1637 slice->size = length;
1638
1639 // The source should be the root buffer:
1640 if (buffer->source != Qnil) {
1641 RB_OBJ_WRITE(instance, &slice->source, buffer->source);
1642 }
1643 else {
1644 RB_OBJ_WRITE(instance, &slice->source, self);
1645 }
1646
1647 return instance;
1648}
1649
1650/*
1651 * call-seq: slice([offset, [length]]) -> io_buffer
1652 *
1653 * Produce another IO::Buffer which is a slice (or view into) the current one
1654 * starting at +offset+ bytes and going for +length+ bytes.
1655 *
1656 * The slicing happens without copying of memory, and the slice keeps being
1657 * associated with the original buffer's source (string, or file), if any.
1658 *
1659 * If the offset is not given, it will be zero. If the offset is negative, it
1660 * will raise an ArgumentError.
1661 *
1662 * If the length is not given, the slice will be as long as the original
1663 * buffer minus the specified offset. If the length is negative, it will raise
1664 * an ArgumentError.
1665 *
1666 * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
1667 * buffer's bounds.
1668 *
1669 * string = 'test'
1670 * buffer = IO::Buffer.for(string).dup
1671 *
1672 * slice = buffer.slice
1673 * # =>
1674 * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
1675 * # 0x00000000 74 65 73 74 test
1676 *
1677 * buffer.slice(2)
1678 * # =>
1679 * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
1680 * # 0x00000000 73 74 st
1681 *
1682 * slice = buffer.slice(1, 2)
1683 * # =>
1684 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1685 * # 0x00000000 65 73 es
1686 *
1687 * # Put "o" into 0s position of the slice
1688 * slice.set_string('o', 0)
1689 * slice
1690 * # =>
1691 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1692 * # 0x00000000 6f 73 os
1693 *
1694 * # it is also visible at position 1 of the original buffer
1695 * buffer
1696 * # =>
1697 * # #<IO::Buffer 0x00007fc3d31e2d80+4 INTERNAL>
1698 * # 0x00000000 74 6f 73 74 tost
1699 */
1700static VALUE
1701io_buffer_slice(int argc, VALUE *argv, VALUE self)
1702{
1703 rb_check_arity(argc, 0, 2);
1704
1705 size_t offset, length;
1706 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
1707
1708 return rb_io_buffer_slice(buffer, self, offset, length);
1709}
1710
1711/*
1712 * call-seq: transfer -> new_io_buffer
1713 *
1714 * Transfers ownership of the underlying memory to a new buffer, causing the
1715 * current buffer to become uninitialized.
1716 *
1717 * buffer = IO::Buffer.new('test')
1718 * other = buffer.transfer
1719 * other
1720 * # =>
1721 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1722 * # 0x00000000 74 65 73 74 test
1723 * buffer
1724 * # =>
1725 * # #<IO::Buffer 0x0000000000000000+0 NULL>
1726 * buffer.null?
1727 * # => true
1728 */
1729 VALUE
1730rb_io_buffer_transfer(VALUE self)
1731{
1732 struct rb_io_buffer *buffer = NULL;
1733 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1734
1735 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1736 rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
1737 }
1738
1739 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1740 struct rb_io_buffer *transferred;
1741 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
1742
1743 *transferred = *buffer;
1744 io_buffer_zero(buffer);
1745
1746 return instance;
1747}
1748
1749static void
1750io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
1751{
1752 if (size > buffer->size) {
1753 memset((unsigned char*)base+buffer->size, 0, size - buffer->size);
1754 }
1755}
1756
1757static void
1758io_buffer_resize_copy(VALUE self, struct rb_io_buffer *buffer, size_t size)
1759{
1760 // Slow path:
1761 struct rb_io_buffer resized;
1762 io_buffer_initialize(self, &resized, NULL, size, io_flags_for_size(size), Qnil);
1763
1764 if (buffer->base) {
1765 size_t preserve = buffer->size;
1766 if (preserve > size) preserve = size;
1767 memcpy(resized.base, buffer->base, preserve);
1768
1769 io_buffer_resize_clear(buffer, resized.base, size);
1770 }
1771
1772 io_buffer_free(buffer);
1773 *buffer = resized;
1774}
1775
1776void
1777rb_io_buffer_resize(VALUE self, size_t size)
1778{
1779 struct rb_io_buffer *buffer = NULL;
1780 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1781
1782 io_buffer_validate_for_reading(buffer);
1783
1784 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1785 rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
1786 }
1787
1788 if (buffer->base == NULL) {
1789 io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
1790 return;
1791 }
1792
1793 if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
1794 rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
1795 }
1796
1797#if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
1798 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
1799 void *base = mremap(buffer->base, buffer->size, size, MREMAP_MAYMOVE);
1800
1801 if (base == MAP_FAILED) {
1802 rb_sys_fail("rb_io_buffer_resize:mremap");
1803 }
1804
1805 io_buffer_resize_clear(buffer, base, size);
1806
1807 buffer->base = base;
1808 buffer->size = size;
1809
1810 return;
1811 }
1812#endif
1813
1814 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
1815 if (size == 0) {
1816 io_buffer_free(buffer);
1817 return;
1818 }
1819
1820 void *base = realloc(buffer->base, size);
1821
1822 if (!base) {
1823 rb_sys_fail("rb_io_buffer_resize:realloc");
1824 }
1825
1826 io_buffer_resize_clear(buffer, base, size);
1827
1828 buffer->base = base;
1829 buffer->size = size;
1830
1831 return;
1832 }
1833
1834 io_buffer_resize_copy(self, buffer, size);
1835}
1836
1837/*
1838 * call-seq: resize(new_size) -> self
1839 *
1840 * Resizes a buffer to a +new_size+ bytes, preserving its content.
1841 * Depending on the old and new size, the memory area associated with
1842 * the buffer might be either extended, or rellocated at different
1843 * address with content being copied.
1844 *
1845 * buffer = IO::Buffer.new(4)
1846 * buffer.set_string("test", 0)
1847 * buffer.resize(8) # resize to 8 bytes
1848 * # =>
1849 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1850 * # 0x00000000 74 65 73 74 00 00 00 00 test....
1851 *
1852 * External buffer (created with ::for), and locked buffer
1853 * can not be resized.
1854 */
1855static VALUE
1856io_buffer_resize(VALUE self, VALUE size)
1857{
1858 rb_io_buffer_resize(self, io_buffer_extract_size(size));
1859
1860 return self;
1861}
1862
1863/*
1864 * call-seq: <=>(other) -> true or false
1865 *
1866 * Buffers are compared by size and exact contents of the memory they are
1867 * referencing using +memcmp+.
1868 */
1869static VALUE
1870rb_io_buffer_compare(VALUE self, VALUE other)
1871{
1872 const void *ptr1, *ptr2;
1873 size_t size1, size2;
1874
1875 rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
1876 rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
1877
1878 if (size1 < size2) {
1879 return RB_INT2NUM(-1);
1880 }
1881
1882 if (size1 > size2) {
1883 return RB_INT2NUM(1);
1884 }
1885
1886 return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
1887}
1888
1889static void
1890io_buffer_validate_type(size_t size, size_t offset, size_t extend)
1891{
1892 if (size_sum_is_bigger_than(offset, extend, size)) {
1893 rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
1894 }
1895}
1896
1897// Lower case: little endian.
1898// Upper case: big endian (network endian).
1899//
1900// :U8 | unsigned 8-bit integer.
1901// :S8 | signed 8-bit integer.
1902//
1903// :u16, :U16 | unsigned 16-bit integer.
1904// :s16, :S16 | signed 16-bit integer.
1905//
1906// :u32, :U32 | unsigned 32-bit integer.
1907// :s32, :S32 | signed 32-bit integer.
1908//
1909// :u64, :U64 | unsigned 64-bit integer.
1910// :s64, :S64 | signed 64-bit integer.
1911//
1912// :u128, :U128 | unsigned 128-bit integer.
1913// :s128, :S128 | signed 128-bit integer.
1914//
1915// :f32, :F32 | 32-bit floating point number.
1916// :f64, :F64 | 64-bit floating point number.
1917
1918#define ruby_swap8(value) value
1919
1920 union swapf32 {
1921 uint32_t integral;
1922 float value;
1923};
1924
1925static float
1926ruby_swapf32(float value)
1927{
1928 union swapf32 swap = {.value = value};
1929 swap.integral = ruby_swap32(swap.integral);
1930 return swap.value;
1931}
1932
1933 union swapf64 {
1934 uint64_t integral;
1935 double value;
1936};
1937
1938static double
1939ruby_swapf64(double value)
1940{
1941 union swapf64 swap = {.value = value};
1942 swap.integral = ruby_swap64(swap.integral);
1943 return swap.value;
1944}
1945
1946// Structures and conversion functions are now in numeric.h/numeric.c
1947// Unified swap function for 128-bit integers (works with both signed and unsigned)
1948// Since both rb_uint128_t and rb_int128_t have the same memory layout,
1949// we can use a union to make the swap function work with both types
1950static inline rb_uint128_t
1951ruby_swap128_uint(rb_uint128_t x)
1952{
1953 rb_uint128_t result;
1954#ifdef HAVE_UINT128_T
1955#if __has_builtin(__builtin_bswap128)
1956 result.value = __builtin_bswap128(x.value);
1957#else
1958 // Manual byte swap for 128-bit integers
1959 uint64_t low = (uint64_t)x.value;
1960 uint64_t high = (uint64_t)(x.value >> 64);
1961 low = ruby_swap64(low);
1962 high = ruby_swap64(high);
1963 result.value = ((uint128_t)low << 64) | high;
1964#endif
1965#else
1966 // Fallback swap function using two 64-bit integers
1967 // For big-endian data on little-endian host (or vice versa):
1968 // 1. Swap bytes within each 64-bit part
1969 // 2. Swap the order of the parts (since big-endian stores high first, little-endian stores low first)
1970 result.parts.low = ruby_swap64(x.parts.high);
1971 result.parts.high = ruby_swap64(x.parts.low);
1972#endif
1973 return result;
1974}
1975
1976static inline rb_int128_t
1977ruby_swap128_int(rb_int128_t x)
1978{
1979 union uint128_int128_conversion conversion = {
1980 .int128 = x
1981 };
1982 conversion.uint128 = ruby_swap128_uint(conversion.uint128);
1983 return conversion.int128;
1984}
1985
1986#define IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, offset, type) \
1987 (io_buffer_get_bytes_for_writing(buffer, &(base), &(size)), \
1988 io_buffer_validate_type(size, offset, sizeof(type)))
1989
1990#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1991static ID RB_IO_BUFFER_DATA_TYPE_##name; \
1992\
1993static VALUE \
1994io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1995{ \
1996 io_buffer_validate_type(size, *offset, sizeof(type)); \
1997 type value; \
1998 memcpy(&value, (char*)base + *offset, sizeof(type)); \
1999 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
2000 *offset += sizeof(type); \
2001 return wrap(value); \
2002} \
2003\
2004static void \
2005io_buffer_write_##name(struct rb_io_buffer* buffer, size_t *offset, VALUE _value) \
2006{ \
2007 void* base; size_t size; \
2008 IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
2009 type value = unwrap(_value); \
2010 IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
2011 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
2012 memcpy((char*)base + *offset, &value, sizeof(type)); \
2013 *offset += sizeof(type); \
2014} \
2015\
2016enum { \
2017 RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
2018};
2019
2020IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
2021IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
2022
2023IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
2024IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
2025IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
2026IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
2027
2028IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
2029IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
2030IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
2031IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
2032
2033IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
2034IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
2035IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
2036IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
2037
2038IO_BUFFER_DECLARE_TYPE(u128, rb_uint128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
2039IO_BUFFER_DECLARE_TYPE(U128, rb_uint128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
2040IO_BUFFER_DECLARE_TYPE(s128, rb_int128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
2041IO_BUFFER_DECLARE_TYPE(S128, rb_int128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
2042
2043IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
2044IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
2045IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
2046IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
2047#undef IO_BUFFER_DECLARE_TYPE
2048
2049static inline size_t
2050io_buffer_buffer_type_size(ID buffer_type)
2051{
2052#define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
2053 IO_BUFFER_DATA_TYPE_SIZE(U8)
2054 IO_BUFFER_DATA_TYPE_SIZE(S8)
2055 IO_BUFFER_DATA_TYPE_SIZE(u16)
2056 IO_BUFFER_DATA_TYPE_SIZE(U16)
2057 IO_BUFFER_DATA_TYPE_SIZE(s16)
2058 IO_BUFFER_DATA_TYPE_SIZE(S16)
2059 IO_BUFFER_DATA_TYPE_SIZE(u32)
2060 IO_BUFFER_DATA_TYPE_SIZE(U32)
2061 IO_BUFFER_DATA_TYPE_SIZE(s32)
2062 IO_BUFFER_DATA_TYPE_SIZE(S32)
2063 IO_BUFFER_DATA_TYPE_SIZE(u64)
2064 IO_BUFFER_DATA_TYPE_SIZE(U64)
2065 IO_BUFFER_DATA_TYPE_SIZE(s64)
2066 IO_BUFFER_DATA_TYPE_SIZE(S64)
2067 IO_BUFFER_DATA_TYPE_SIZE(u128)
2068 IO_BUFFER_DATA_TYPE_SIZE(U128)
2069 IO_BUFFER_DATA_TYPE_SIZE(s128)
2070 IO_BUFFER_DATA_TYPE_SIZE(S128)
2071 IO_BUFFER_DATA_TYPE_SIZE(f32)
2072 IO_BUFFER_DATA_TYPE_SIZE(F32)
2073 IO_BUFFER_DATA_TYPE_SIZE(f64)
2074 IO_BUFFER_DATA_TYPE_SIZE(F64)
2075#undef IO_BUFFER_DATA_TYPE_SIZE
2076
2077 rb_raise(rb_eArgError, "Invalid type name!");
2078}
2079
2080/*
2081 * call-seq:
2082 * size_of(buffer_type) -> byte size
2083 * size_of(array of buffer_type) -> byte size
2084 *
2085 * Returns the size of the given buffer type(s) in bytes.
2086 *
2087 * IO::Buffer.size_of(:u32) # => 4
2088 * IO::Buffer.size_of([:u32, :u32]) # => 8
2089 */
2090static VALUE
2091io_buffer_size_of(VALUE klass, VALUE buffer_type)
2092{
2093 if (RB_TYPE_P(buffer_type, T_ARRAY)) {
2094 size_t total = 0;
2095 for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
2096 total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
2097 }
2098 return SIZET2NUM(total);
2099 }
2100 else {
2101 return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
2102 }
2103}
2104
2105static inline VALUE
2106rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *offset)
2107{
2108#define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
2109 IO_BUFFER_GET_VALUE(U8)
2110 IO_BUFFER_GET_VALUE(S8)
2111
2112 IO_BUFFER_GET_VALUE(u16)
2113 IO_BUFFER_GET_VALUE(U16)
2114 IO_BUFFER_GET_VALUE(s16)
2115 IO_BUFFER_GET_VALUE(S16)
2116
2117 IO_BUFFER_GET_VALUE(u32)
2118 IO_BUFFER_GET_VALUE(U32)
2119 IO_BUFFER_GET_VALUE(s32)
2120 IO_BUFFER_GET_VALUE(S32)
2121
2122 IO_BUFFER_GET_VALUE(u64)
2123 IO_BUFFER_GET_VALUE(U64)
2124 IO_BUFFER_GET_VALUE(s64)
2125 IO_BUFFER_GET_VALUE(S64)
2126
2127 IO_BUFFER_GET_VALUE(u128)
2128 IO_BUFFER_GET_VALUE(U128)
2129 IO_BUFFER_GET_VALUE(s128)
2130 IO_BUFFER_GET_VALUE(S128)
2131
2132 IO_BUFFER_GET_VALUE(f32)
2133 IO_BUFFER_GET_VALUE(F32)
2134 IO_BUFFER_GET_VALUE(f64)
2135 IO_BUFFER_GET_VALUE(F64)
2136#undef IO_BUFFER_GET_VALUE
2137
2138 rb_raise(rb_eArgError, "Invalid type name!");
2139}
2140
2141/*
2142 * call-seq: get_value(buffer_type, offset) -> numeric
2143 *
2144 * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
2145 * of symbols:
2146 *
2147 * * +:U8+: unsigned integer, 1 byte
2148 * * +:S8+: signed integer, 1 byte
2149 * * +:u16+: unsigned integer, 2 bytes, little-endian
2150 * * +:U16+: unsigned integer, 2 bytes, big-endian
2151 * * +:s16+: signed integer, 2 bytes, little-endian
2152 * * +:S16+: signed integer, 2 bytes, big-endian
2153 * * +:u32+: unsigned integer, 4 bytes, little-endian
2154 * * +:U32+: unsigned integer, 4 bytes, big-endian
2155 * * +:s32+: signed integer, 4 bytes, little-endian
2156 * * +:S32+: signed integer, 4 bytes, big-endian
2157 * * +:u64+: unsigned integer, 8 bytes, little-endian
2158 * * +:U64+: unsigned integer, 8 bytes, big-endian
2159 * * +:s64+: signed integer, 8 bytes, little-endian
2160 * * +:S64+: signed integer, 8 bytes, big-endian
2161 * * +:u128+: unsigned integer, 16 bytes, little-endian
2162 * * +:U128+: unsigned integer, 16 bytes, big-endian
2163 * * +:s128+: signed integer, 16 bytes, little-endian
2164 * * +:S128+: signed integer, 16 bytes, big-endian
2165 * * +:f32+: float, 4 bytes, little-endian
2166 * * +:F32+: float, 4 bytes, big-endian
2167 * * +:f64+: double, 8 bytes, little-endian
2168 * * +:F64+: double, 8 bytes, big-endian
2169 *
2170 * A buffer type refers specifically to the type of binary buffer that is stored
2171 * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
2172 * integer in little-endian format.
2173 *
2174 * string = [1.5].pack('f')
2175 * # => "\x00\x00\xC0?"
2176 * IO::Buffer.for(string).get_value(:f32, 0)
2177 * # => 1.5
2178 */
2179static VALUE
2180io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
2181{
2182 const void *base;
2183 size_t size;
2184 size_t offset = io_buffer_extract_offset(_offset);
2185
2186 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2187
2188 return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
2189}
2190
2191/*
2192 * call-seq: get_values(buffer_types, offset) -> array
2193 *
2194 * Similar to #get_value, except that it can handle multiple buffer types and
2195 * returns an array of values.
2196 *
2197 * string = [1.5, 2.5].pack('ff')
2198 * IO::Buffer.for(string).get_values([:f32, :f32], 0)
2199 * # => [1.5, 2.5]
2200 */
2201static VALUE
2202io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
2203{
2204 size_t offset = io_buffer_extract_offset(_offset);
2205
2206 const void *base;
2207 size_t size;
2208 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2209
2210 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2211 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2212 }
2213
2214 VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));
2215
2216 for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
2217 VALUE type = rb_ary_entry(buffer_types, i);
2218 VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
2219 rb_ary_push(array, value);
2220 }
2221
2222 return array;
2223}
2224
2225// Extract a count argument, which must be a positive integer.
2226// Count is generally considered relative to the number of things.
2227static inline size_t
2228io_buffer_extract_count(VALUE argument)
2229{
2230 if (rb_int_negative_p(argument)) {
2231 rb_raise(rb_eArgError, "Count can't be negative!");
2232 }
2233
2234 return NUM2SIZET(argument);
2235}
2236
2237static inline void
2238io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
2239{
2240 if (argc >= 1) {
2241 *offset = io_buffer_extract_offset(argv[0]);
2242 }
2243 else {
2244 *offset = 0;
2245 }
2246
2247 if (argc >= 2) {
2248 *count = io_buffer_extract_count(argv[1]);
2249 }
2250 else {
2251 if (*offset > size) {
2252 rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
2253 }
2254
2255 *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
2256 }
2257}
2258
2259/*
2260 * call-seq:
2261 * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
2262 * each(buffer_type, [offset, [count]]) -> enumerator
2263 *
2264 * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
2265 * from +offset+.
2266 *
2267 * If +count+ is given, only +count+ values will be yielded.
2268 *
2269 * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
2270 * puts "#{offset}: #{value}"
2271 * end
2272 * # 2: 108
2273 * # 3: 108
2274 */
2275static VALUE
2276io_buffer_each(int argc, VALUE *argv, VALUE self)
2277{
2278 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2279
2280 const void *base;
2281 size_t size;
2282
2283 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2284
2285 ID buffer_type;
2286 if (argc >= 1) {
2287 buffer_type = RB_SYM2ID(argv[0]);
2288 }
2289 else {
2290 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2291 }
2292
2293 size_t offset, count;
2294 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2295
2296 for (size_t i = 0; i < count; i++) {
2297 size_t current_offset = offset;
2298 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2299 rb_yield_values(2, SIZET2NUM(current_offset), value);
2300 }
2301
2302 return self;
2303}
2304
2305/*
2306 * call-seq: values(buffer_type, [offset, [count]]) -> array
2307 *
2308 * Returns an array of values of +buffer_type+ starting from +offset+.
2309 *
2310 * If +count+ is given, only +count+ values will be returned.
2311 *
2312 * IO::Buffer.for("Hello World").values(:U8, 2, 2)
2313 * # => [108, 108]
2314 */
2315static VALUE
2316io_buffer_values(int argc, VALUE *argv, VALUE self)
2317{
2318 const void *base;
2319 size_t size;
2320
2321 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2322
2323 ID buffer_type;
2324 if (argc >= 1) {
2325 buffer_type = RB_SYM2ID(argv[0]);
2326 }
2327 else {
2328 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2329 }
2330
2331 size_t offset, count;
2332 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2333
2334 VALUE array = rb_ary_new_capa(count);
2335
2336 for (size_t i = 0; i < count; i++) {
2337 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2338 rb_ary_push(array, value);
2339 }
2340
2341 return array;
2342}
2343
2344/*
2345 * call-seq:
2346 * each_byte([offset, [count]]) {|byte| ...} -> self
2347 * each_byte([offset, [count]]) -> enumerator
2348 *
2349 * Iterates over the buffer, yielding each byte starting from +offset+.
2350 *
2351 * If +count+ is given, only +count+ bytes will be yielded.
2352 *
2353 * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
2354 * puts "#{offset}: #{byte}"
2355 * end
2356 * # 2: 108
2357 * # 3: 108
2358 */
2359static VALUE
2360io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
2361{
2362 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2363
2364 const void *base;
2365 size_t size;
2366
2367 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2368
2369 size_t offset, count;
2370 io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc, argv, &offset, &count);
2371
2372 for (size_t i = 0; i < count; i++) {
2373 unsigned char *value = (unsigned char *)base + i + offset;
2374 rb_yield(RB_INT2FIX(*value));
2375 }
2376
2377 return self;
2378}
2379
2380static inline void
2381rb_io_buffer_set_value(struct rb_io_buffer *buffer, VALUE buffer_type, size_t *offset, VALUE value)
2382{
2383 ID type = RB_SYM2ID(buffer_type);
2384#define IO_BUFFER_SET_VALUE(name) if (type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(buffer, offset, value); return;}
2385 IO_BUFFER_SET_VALUE(U8);
2386 IO_BUFFER_SET_VALUE(S8);
2387
2388 IO_BUFFER_SET_VALUE(u16);
2389 IO_BUFFER_SET_VALUE(U16);
2390 IO_BUFFER_SET_VALUE(s16);
2391 IO_BUFFER_SET_VALUE(S16);
2392
2393 IO_BUFFER_SET_VALUE(u32);
2394 IO_BUFFER_SET_VALUE(U32);
2395 IO_BUFFER_SET_VALUE(s32);
2396 IO_BUFFER_SET_VALUE(S32);
2397
2398 IO_BUFFER_SET_VALUE(u64);
2399 IO_BUFFER_SET_VALUE(U64);
2400 IO_BUFFER_SET_VALUE(s64);
2401 IO_BUFFER_SET_VALUE(S64);
2402
2403 IO_BUFFER_SET_VALUE(u128);
2404 IO_BUFFER_SET_VALUE(U128);
2405 IO_BUFFER_SET_VALUE(s128);
2406 IO_BUFFER_SET_VALUE(S128);
2407
2408 IO_BUFFER_SET_VALUE(f32);
2409 IO_BUFFER_SET_VALUE(F32);
2410 IO_BUFFER_SET_VALUE(f64);
2411 IO_BUFFER_SET_VALUE(F64);
2412#undef IO_BUFFER_SET_VALUE
2413
2414 rb_raise(rb_eArgError, "Invalid type name!");
2415}
2416
2417 struct io_buffer_set_value_arguments {
2418 struct rb_io_buffer *buffer;
2419 size_t offset;
2420 VALUE type, value;
2421};
2422
2423static VALUE
2424io_buffer_set_value_try(VALUE arguments)
2425{
2426 struct io_buffer_set_value_arguments *args = (void *)arguments;
2427 size_t offset = args->offset;
2428 rb_io_buffer_set_value(args->buffer, args->type, &offset, args->value);
2429 return SIZET2NUM(offset);
2430}
2431
2432/*
2433 * call-seq: set_value(type, offset, value) -> offset
2434 *
2435 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
2436 * symbols described in #get_value.
2437 *
2438 * buffer = IO::Buffer.new(8)
2439 * # =>
2440 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2441 * # 0x00000000 00 00 00 00 00 00 00 00
2442 *
2443 * buffer.set_value(:U8, 1, 111)
2444 * # => 1
2445 *
2446 * buffer
2447 * # =>
2448 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2449 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
2450 *
2451 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
2452 *
2453 * buffer = IO::Buffer.new(8)
2454 * buffer.set_value(:U32, 0, 2.5)
2455 *
2456 * buffer
2457 * # =>
2458 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2459 * # 0x00000000 00 00 00 02 00 00 00 00
2460 * # ^^ the same as if we'd pass just integer 2
2461 */
2462static VALUE
2463io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
2464{
2465 struct io_buffer_set_value_arguments arguments = {
2466 .buffer = get_io_buffer_for_writing(self),
2467 .offset = io_buffer_extract_offset(_offset),
2468 .type = type,
2469 .value = value,
2470 };
2471
2472 if (!io_buffer_try_lock(arguments.buffer)) {
2473 return io_buffer_set_value_try((VALUE)&arguments);
2474 }
2475 return rb_ensure(io_buffer_set_value_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
2476}
2477
2478static VALUE
2479io_buffer_set_values_try(VALUE arguments)
2480{
2481 struct io_buffer_set_value_arguments *args = (void *)arguments;
2482 size_t offset = args->offset;
2483
2484 for (long i = 0; i < RARRAY_LEN(args->type); i++) {
2485 VALUE type = rb_ary_entry(args->type, i);
2486 VALUE value = rb_ary_entry(args->value, i);
2487 rb_io_buffer_set_value(args->buffer, type, &offset, value);
2488 }
2489
2490 return SIZET2NUM(offset);
2491}
2492
2493/*
2494 * call-seq: set_values(buffer_types, offset, values) -> offset
2495 *
2496 * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
2497 * should be an array of symbols as described in #get_value. +values+ should
2498 * be an array of values to write.
2499 *
2500 * buffer = IO::Buffer.new(8)
2501 * buffer.set_values([:U8, :U16], 0, [1, 2])
2502 * buffer
2503 * # =>
2504 * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
2505 * # 0x00000000 01 00 02 00 00 00 00 00 ........
2506 */
2507static VALUE
2508io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
2509{
2510 struct io_buffer_set_value_arguments arguments = {
2511 .buffer = get_io_buffer_for_writing(self),
2512 };
2513
2514 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2515 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2516 }
2517 arguments.type = buffer_types;
2518
2519 arguments.offset = io_buffer_extract_offset(_offset);
2520
2521 if (!RB_TYPE_P(values, T_ARRAY)) {
2522 rb_raise(rb_eArgError, "Argument values should be an array!");
2523 }
2524 arguments.value = values;
2525
2526 if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
2527 rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
2528 }
2529
2530 if (!io_buffer_try_lock(arguments.buffer)) {
2531 return io_buffer_set_values_try((VALUE)&arguments);
2532 }
2533 return rb_ensure(io_buffer_set_values_try, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
2534
2535}
2536
2537static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
2538
2539 struct io_buffer_memmove_arguments {
2540 unsigned char * destination;
2541 const unsigned char * source;
2542 size_t length;
2543};
2544
2545static void *
2546io_buffer_memmove_blocking(void *data)
2547{
2548 struct io_buffer_memmove_arguments *arguments = (struct io_buffer_memmove_arguments *)data;
2549
2550 memmove(arguments->destination, arguments->source, arguments->length);
2551
2552 return NULL;
2553}
2554
2555static void
2556io_buffer_memmove_unblock(void *data)
2557{
2558 // No safe way to interrupt.
2559}
2560
2561static void
2562io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
2563{
2564 void *base;
2565 size_t size;
2566 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2567
2568 io_buffer_validate_range(buffer, offset, length);
2569
2570 if (size_sum_is_bigger_than(source_offset, length, source_size)) {
2571 rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
2572 }
2573
2574 struct io_buffer_memmove_arguments arguments = {
2575 .destination = (unsigned char*)base+offset,
2576 .source = (unsigned char*)source_base+source_offset,
2577 .length = length
2578 };
2579
2580 if (arguments.length >= IO_BUFFER_BLOCKING_SIZE) {
2581 rb_nogvl(io_buffer_memmove_blocking, &arguments, io_buffer_memmove_unblock, &arguments, RB_NOGVL_OFFLOAD_SAFE);
2582 } else if (arguments.length != 0) {
2583 memmove(arguments.destination, arguments.source, arguments.length);
2584 }
2585}
2586
2587// (offset, length, source_offset) -> length
2588static VALUE
2589io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
2590{
2591 size_t offset = 0;
2592 size_t length;
2593 size_t source_offset;
2594
2595 // The offset we copy into the buffer:
2596 if (argc >= 1) {
2597 offset = io_buffer_extract_offset(argv[0]);
2598 }
2599
2600 // The offset we start from within the string:
2601 if (argc >= 3) {
2602 source_offset = io_buffer_extract_offset(argv[2]);
2603
2604 if (source_offset > source_size) {
2605 rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
2606 }
2607 }
2608 else {
2609 source_offset = 0;
2610 }
2611
2612 // The length we are going to copy:
2613 if (argc >= 2 && !RB_NIL_P(argv[1])) {
2614 length = io_buffer_extract_length(argv[1]);
2615 }
2616 else {
2617 // Default to the source offset -> source size:
2618 length = source_size - source_offset;
2619 }
2620
2621 io_buffer_memmove(buffer, offset, source_base, source_offset, source_size, length);
2622
2623 return SIZET2NUM(length);
2624}
2625
2626/*
2627 * call-seq:
2628 * dup -> io_buffer
2629 * clone -> io_buffer
2630 *
2631 * Make an internal copy of the source buffer. Updates to the copy will not
2632 * affect the source buffer.
2633 *
2634 * source = IO::Buffer.for("Hello World")
2635 * # =>
2636 * # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
2637 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2638 * buffer = source.dup
2639 * # =>
2640 * # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
2641 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2642 */
2643static VALUE
2644rb_io_buffer_initialize_copy(VALUE self, VALUE source)
2645{
2646 struct rb_io_buffer *buffer = NULL;
2647 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2648
2649 const void *source_base;
2650 size_t source_size;
2651
2652 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
2653
2654 io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
2655
2656 VALUE result = io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
2657 RB_GC_GUARD(source);
2658 return result;
2659}
2660
2661/*
2662 * call-seq:
2663 * copy(source, [offset, [length, [source_offset]]]) -> size
2664 *
2665 * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
2666 * using +memmove+. For copying String instances, see #set_string.
2667 *
2668 * buffer = IO::Buffer.new(32)
2669 * # =>
2670 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
2671 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2672 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2673 *
2674 * buffer.copy(IO::Buffer.for("test"), 8)
2675 * # => 4 -- size of buffer copied
2676 * buffer
2677 * # =>
2678 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
2679 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
2680 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2681 *
2682 * #copy can be used to put buffer into strings associated with buffer:
2683 *
2684 * string = "data: "
2685 * # => "data: "
2686 * buffer = IO::Buffer.for(string) do |buffer|
2687 * buffer.copy(IO::Buffer.for("test"), 5)
2688 * end
2689 * # => 4
2690 * string
2691 * # => "data:test"
2692 *
2693 * Attempt to copy into a read-only buffer will fail:
2694 *
2695 * File.write('test.txt', 'test')
2696 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
2697 * buffer.copy(IO::Buffer.for("test"), 8)
2698 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
2699 *
2700 * See ::map for details of creation of mutable file mappings, this will
2701 * work:
2702 *
2703 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
2704 * buffer.copy(IO::Buffer.for("boom"), 0)
2705 * # => 4
2706 * File.read('test.txt')
2707 * # => "boom"
2708 *
2709 * Attempt to copy the buffer which will need place outside of buffer's
2710 * bounds will fail:
2711 *
2712 * buffer = IO::Buffer.new(2)
2713 * buffer.copy(IO::Buffer.for('test'), 0)
2714 * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
2715 *
2716 * It is safe to copy between memory regions that overlaps each other.
2717 * In such case, the data is copied as if the data was first copied from the source buffer to
2718 * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
2719 *
2720 * buffer = IO::Buffer.new(10)
2721 * buffer.set_string("0123456789")
2722 * buffer.copy(buffer, 3, 7)
2723 * # => 7
2724 * buffer
2725 * # =>
2726 * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
2727 * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
2728 */
2729static VALUE
2730io_buffer_copy(int argc, VALUE *argv, VALUE self)
2731{
2732 rb_check_arity(argc, 1, 4);
2733
2734 struct rb_io_buffer *buffer = NULL;
2735 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2736
2737 VALUE source = argv[0];
2738 const void *source_base;
2739 size_t source_size;
2740
2741 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
2742
2743 VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
2744 RB_GC_GUARD(source);
2745 return result;
2746}
2747
2748/*
2749 * call-seq: get_string([offset, [length, [encoding]]]) -> string
2750 *
2751 * Read a chunk or all of the buffer into a string, in the specified
2752 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
2753 *
2754 * buffer = IO::Buffer.for('test')
2755 * buffer.get_string
2756 * # => "test"
2757 * buffer.get_string(2)
2758 * # => "st"
2759 * buffer.get_string(2, 1)
2760 * # => "s"
2761 */
2762static VALUE
2763io_buffer_get_string(int argc, VALUE *argv, VALUE self)
2764{
2765 rb_check_arity(argc, 0, 3);
2766
2767 size_t offset, length;
2768 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
2769
2770 const void *base;
2771 size_t size;
2772 io_buffer_get_bytes_for_reading(buffer, &base, &size);
2773
2774 rb_encoding *encoding;
2775 if (argc >= 3) {
2776 encoding = rb_find_encoding(argv[2]);
2777 }
2778 else {
2779 encoding = rb_ascii8bit_encoding();
2780 }
2781
2782 io_buffer_validate_range(buffer, offset, length);
2783
2784 return rb_enc_str_new((const char*)base + offset, length, encoding);
2785}
2786
2787/*
2788 * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
2789 *
2790 * Efficiently copy from a source String into the buffer, at +offset+ using
2791 * +memmove+.
2792 *
2793 * buf = IO::Buffer.new(8)
2794 * # =>
2795 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2796 * # 0x00000000 00 00 00 00 00 00 00 00 ........
2797 *
2798 * # set buffer starting from offset 1, take 2 bytes starting from string's
2799 * # second
2800 * buf.set_string('test', 1, 2, 1)
2801 * # => 2
2802 * buf
2803 * # =>
2804 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2805 * # 0x00000000 00 65 73 00 00 00 00 00 .es.....
2806 *
2807 * See also #copy for examples of how buffer writing might be used for changing
2808 * associated strings and files.
2809 */
2810static VALUE
2811io_buffer_set_string(int argc, VALUE *argv, VALUE self)
2812{
2813 rb_check_arity(argc, 1, 4);
2814
2815 struct rb_io_buffer *buffer = NULL;
2816 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2817
2818 VALUE string = rb_str_to_str(argv[0]);
2819
2820 const void *source_base = RSTRING_PTR(string);
2821 size_t source_size = RSTRING_LEN(string);
2822
2823 VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
2824 RB_GC_GUARD(string);
2825 return result;
2826}
2827
2828void
2829rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
2830{
2831 struct rb_io_buffer *buffer = NULL;
2832 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2833
2834 void *base;
2835 size_t size;
2836 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2837
2838 io_buffer_validate_range(buffer, offset, length);
2839
2840 memset((char*)base + offset, value, length);
2841}
2842
2843/*
2844 * call-seq: clear(value = 0, [offset, [length]]) -> self
2845 *
2846 * Fill buffer with +value+, starting with +offset+ and going for +length+
2847 * bytes.
2848 *
2849 * buffer = IO::Buffer.for('test').dup
2850 * # =>
2851 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2852 * # 0x00000000 74 65 73 74 test
2853 *
2854 * buffer.clear
2855 * # =>
2856 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2857 * # 0x00000000 00 00 00 00 ....
2858 *
2859 * buf.clear(1) # fill with 1
2860 * # =>
2861 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2862 * # 0x00000000 01 01 01 01 ....
2863 *
2864 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
2865 * # =>
2866 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2867 * # 0x00000000 01 02 02 01 ....
2868 *
2869 * buffer.clear(2, 1) # fill with 2, starting from offset 1
2870 * # =>
2871 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2872 * # 0x00000000 01 02 02 02 ....
2873 */
2874static VALUE
2875io_buffer_clear(int argc, VALUE *argv, VALUE self)
2876{
2877 rb_check_arity(argc, 0, 3);
2878
2879 uint8_t value = 0;
2880 if (argc >= 1) {
2881 value = NUM2UINT(argv[0]);
2882 }
2883
2884 size_t offset, length;
2885 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
2886
2887 rb_io_buffer_clear(self, value, offset, length);
2888
2889 return self;
2890}
2891
2892static size_t
2893io_buffer_default_size(size_t page_size)
2894{
2895 // Platform agnostic default size, based on empirical performance observation:
2896 const size_t platform_agnostic_default_size = 64*1024;
2897
2898 // Allow user to specify custom default buffer size:
2899 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
2900 if (default_size) {
2901 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
2902 int value = atoi(default_size);
2903
2904 // assuming sizeof(int) <= sizeof(size_t)
2905 if (value > 0) {
2906 return value;
2907 }
2908 }
2909
2910 if (platform_agnostic_default_size < page_size) {
2911 return page_size;
2912 }
2913
2914 return platform_agnostic_default_size;
2915}
2916
2917 struct io_buffer_blocking_region_argument {
2918 struct rb_io *io;
2919 struct rb_io_buffer *buffer;
2920 rb_blocking_function_t *function;
2921 void *data;
2922};
2923
2924static VALUE
2925io_buffer_blocking_region_begin(VALUE _argument)
2926{
2927 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
2928
2929 return rb_io_blocking_region(argument->io, argument->function, argument->data);
2930}
2931
2932static VALUE
2933io_buffer_blocking_region_ensure(VALUE _argument)
2934{
2935 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
2936
2937 io_buffer_unlock(argument->buffer);
2938
2939 return Qnil;
2940}
2941
2942static VALUE
2943io_buffer_blocking_region(VALUE io, struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data)
2944{
2945 struct rb_io *ioptr;
2946 RB_IO_POINTER(io, ioptr);
2947
2948 struct io_buffer_blocking_region_argument argument = {
2949 .io = ioptr,
2950 .buffer = buffer,
2951 .function = function,
2952 .data = data,
2953 };
2954
2955 // If the buffer is already locked, we can skip the ensure (unlock):
2956 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
2957 return io_buffer_blocking_region_begin((VALUE)&argument);
2958 }
2959 else {
2960 // The buffer should be locked for the duration of the blocking region:
2961 io_buffer_lock(buffer);
2962
2963 return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
2964 }
2965}
2966
2967 struct io_buffer_read_internal_argument {
2968 // The file descriptor to read from:
2969 int descriptor;
2970 // The base pointer to read from:
2971 char *base;
2972 // The size of the buffer:
2973 size_t size;
2974 // The minimum number of bytes to read:
2975 size_t length;
2976};
2977
2978static VALUE
2979io_buffer_read_internal(void *_argument)
2980{
2981 size_t total = 0;
2982 struct io_buffer_read_internal_argument *argument = _argument;
2983
2984 while (true) {
2985 ssize_t result = read(argument->descriptor, argument->base, argument->size);
2986
2987 if (result < 0) {
2988 return rb_fiber_scheduler_io_result(result, errno);
2989 }
2990 else if (result == 0) {
2991 return rb_fiber_scheduler_io_result(total, 0);
2992 }
2993 else {
2994 total += result;
2995
2996 if (total >= argument->length) {
2997 return rb_fiber_scheduler_io_result(total, 0);
2998 }
2999
3000 argument->base = argument->base + result;
3001 argument->size = argument->size - result;
3002 }
3003 }
3004}
3005
3006 VALUE
3007rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
3008{
3009 io = rb_io_get_io(io);
3010
3011 VALUE scheduler = rb_fiber_scheduler_current();
3012 if (scheduler != Qnil) {
3013 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length, offset);
3014
3015 if (!UNDEF_P(result)) {
3016 return result;
3017 }
3018 }
3019
3020 struct rb_io_buffer *buffer = NULL;
3021 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3022
3023 io_buffer_validate_range(buffer, offset, length);
3024
3025 int descriptor = rb_io_descriptor(io);
3026
3027 void * base;
3028 size_t size;
3029 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3030
3031 base = (unsigned char*)base + offset;
3032 size = size - offset;
3033
3034 struct io_buffer_read_internal_argument argument = {
3035 .descriptor = descriptor,
3036 .base = base,
3037 .size = size,
3038 .length = length,
3039 };
3040
3041 return io_buffer_blocking_region(io, buffer, io_buffer_read_internal, &argument);
3042}
3043
3044/*
3045 * call-seq: read(io, [length, [offset]]) -> read length or -errno
3046 *
3047 * Read at least +length+ bytes from the +io+, into the buffer starting at
3048 * +offset+. If an error occurs, return <tt>-errno</tt>.
3049 *
3050 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3051 * minus the offset, i.e. the entire buffer.
3052 *
3053 * If +length+ is zero, exactly one <tt>read</tt> operation will occur.
3054 *
3055 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3056 * buffer.
3057 *
3058 * IO::Buffer.for('test') do |buffer|
3059 * p buffer
3060 * # =>
3061 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3062 * # 0x00000000 74 65 73 74 test
3063 * buffer.read(File.open('/dev/urandom', 'rb'), 2)
3064 * p buffer
3065 * # =>
3066 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3067 * # 0x00000000 05 35 73 74 .5st
3068 * end
3069 */
3070static VALUE
3071io_buffer_read(int argc, VALUE *argv, VALUE self)
3072{
3073 rb_check_arity(argc, 1, 3);
3074
3075 VALUE io = argv[0];
3076
3077 size_t length, offset;
3078 io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
3079
3080 return rb_io_buffer_read(self, io, length, offset);
3081}
3082
3083 struct io_buffer_pread_internal_argument {
3084 // The file descriptor to read from:
3085 int descriptor;
3086 // The base pointer to read from:
3087 char *base;
3088 // The size of the buffer:
3089 size_t size;
3090 // The minimum number of bytes to read:
3091 size_t length;
3092 // The offset to read from:
3093 off_t offset;
3094};
3095
3096static VALUE
3097io_buffer_pread_internal(void *_argument)
3098{
3099 size_t total = 0;
3100 struct io_buffer_pread_internal_argument *argument = _argument;
3101
3102 while (true) {
3103 ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset);
3104
3105 if (result < 0) {
3106 return rb_fiber_scheduler_io_result(result, errno);
3107 }
3108 else if (result == 0) {
3109 return rb_fiber_scheduler_io_result(total, 0);
3110 }
3111 else {
3112 total += result;
3113
3114 if (total >= argument->length) {
3115 return rb_fiber_scheduler_io_result(total, 0);
3116 }
3117
3118 argument->base = argument->base + result;
3119 argument->size = argument->size - result;
3120 argument->offset = argument->offset + result;
3121 }
3122 }
3123}
3124
3125 VALUE
3126rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
3127{
3128 io = rb_io_get_io(io);
3129
3130 VALUE scheduler = rb_fiber_scheduler_current();
3131 if (scheduler != Qnil) {
3132 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, length, offset);
3133
3134 if (!UNDEF_P(result)) {
3135 return result;
3136 }
3137 }
3138
3139 struct rb_io_buffer *buffer = NULL;
3140 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3141
3142 io_buffer_validate_range(buffer, offset, length);
3143
3144 int descriptor = rb_io_descriptor(io);
3145
3146 void * base;
3147 size_t size;
3148 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3149
3150 base = (unsigned char*)base + offset;
3151 size = size - offset;
3152
3153 struct io_buffer_pread_internal_argument argument = {
3154 .descriptor = descriptor,
3155 .base = base,
3156 .size = size,
3157 .length = length,
3158 .offset = from,
3159 };
3160
3161 return io_buffer_blocking_region(io, buffer, io_buffer_pread_internal, &argument);
3162}
3163
3164/*
3165 * call-seq: pread(io, from, [length, [offset]]) -> read length or -errno
3166 *
3167 * Read at least +length+ bytes from the +io+ starting at the specified +from+
3168 * position, into the buffer starting at +offset+. If an error occurs,
3169 * return <tt>-errno</tt>.
3170 *
3171 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3172 * minus the offset, i.e. the entire buffer.
3173 *
3174 * If +length+ is zero, exactly one <tt>pread</tt> operation will occur.
3175 *
3176 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3177 * buffer.
3178 *
3179 * IO::Buffer.for('test') do |buffer|
3180 * p buffer
3181 * # =>
3182 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3183 * # 0x00000000 74 65 73 74 test
3184 *
3185 * # take 2 bytes from the beginning of urandom,
3186 * # put them in buffer starting from position 2
3187 * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
3188 * p buffer
3189 * # =>
3190 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3191 * # 0x00000000 05 35 73 74 te.5
3192 * end
3193 */
3194static VALUE
3195io_buffer_pread(int argc, VALUE *argv, VALUE self)
3196{
3197 rb_check_arity(argc, 2, 4);
3198
3199 VALUE io = argv[0];
3200 rb_off_t from = NUM2OFFT(argv[1]);
3201
3202 size_t length, offset;
3203 io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
3204
3205 return rb_io_buffer_pread(self, io, from, length, offset);
3206}
3207
3208 struct io_buffer_write_internal_argument {
3209 // The file descriptor to write to:
3210 int descriptor;
3211 // The base pointer to write from:
3212 const char *base;
3213 // The size of the buffer:
3214 size_t size;
3215 // The minimum length to write:
3216 size_t length;
3217};
3218
3219static VALUE
3220io_buffer_write_internal(void *_argument)
3221{
3222 size_t total = 0;
3223 struct io_buffer_write_internal_argument *argument = _argument;
3224
3225 while (true) {
3226 ssize_t result = write(argument->descriptor, argument->base, argument->size);
3227
3228 if (result < 0) {
3229 return rb_fiber_scheduler_io_result(result, errno);
3230 }
3231 else if (result == 0) {
3232 return rb_fiber_scheduler_io_result(total, 0);
3233 }
3234 else {
3235 total += result;
3236
3237 if (total >= argument->length) {
3238 return rb_fiber_scheduler_io_result(total, 0);
3239 }
3240
3241 argument->base = argument->base + result;
3242 argument->size = argument->size - result;
3243 }
3244 }
3245}
3246
3247 VALUE
3248rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
3249{
3250 io = rb_io_get_write_io(rb_io_get_io(io));
3251
3252 VALUE scheduler = rb_fiber_scheduler_current();
3253 if (scheduler != Qnil) {
3254 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length, offset);
3255
3256 if (!UNDEF_P(result)) {
3257 return result;
3258 }
3259 }
3260
3261 struct rb_io_buffer *buffer = NULL;
3262 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3263
3264 io_buffer_validate_range(buffer, offset, length);
3265
3266 int descriptor = rb_io_descriptor(io);
3267
3268 const void * base;
3269 size_t size;
3270 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3271
3272 base = (unsigned char*)base + offset;
3273 size = size - offset;
3274
3275 struct io_buffer_write_internal_argument argument = {
3276 .descriptor = descriptor,
3277 .base = base,
3278 .size = size,
3279 .length = length,
3280 };
3281
3282 return io_buffer_blocking_region(io, buffer, io_buffer_write_internal, &argument);
3283}
3284
3285/*
3286 * call-seq: write(io, [length, [offset]]) -> written length or -errno
3287 *
3288 * Write at least +length+ bytes from the buffer starting at +offset+, into the +io+.
3289 * If an error occurs, return <tt>-errno</tt>.
3290 *
3291 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3292 * minus the offset, i.e. the entire buffer.
3293 *
3294 * If +length+ is zero, exactly one <tt>write</tt> operation will occur.
3295 *
3296 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3297 * buffer.
3298 *
3299 * out = File.open('output.txt', 'wb')
3300 * IO::Buffer.for('1234567').write(out, 3)
3301 *
3302 * This leads to +123+ being written into <tt>output.txt</tt>
3303 */
3304static VALUE
3305io_buffer_write(int argc, VALUE *argv, VALUE self)
3306{
3307 rb_check_arity(argc, 1, 3);
3308
3309 VALUE io = argv[0];
3310
3311 size_t length, offset;
3312 io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
3313
3314 return rb_io_buffer_write(self, io, length, offset);
3315}
3316
3317 struct io_buffer_pwrite_internal_argument {
3318 // The file descriptor to write to:
3319 int descriptor;
3320 // The base pointer to write from:
3321 const char *base;
3322 // The size of the buffer:
3323 size_t size;
3324 // The minimum length to write:
3325 size_t length;
3326 // The offset to write to:
3327 off_t offset;
3328};
3329
3330static VALUE
3331io_buffer_pwrite_internal(void *_argument)
3332{
3333 size_t total = 0;
3334 struct io_buffer_pwrite_internal_argument *argument = _argument;
3335
3336 while (true) {
3337 ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset);
3338
3339 if (result < 0) {
3340 return rb_fiber_scheduler_io_result(result, errno);
3341 }
3342 else if (result == 0) {
3343 return rb_fiber_scheduler_io_result(total, 0);
3344 }
3345 else {
3346 total += result;
3347
3348 if (total >= argument->length) {
3349 return rb_fiber_scheduler_io_result(total, 0);
3350 }
3351
3352 argument->base = argument->base + result;
3353 argument->size = argument->size - result;
3354 argument->offset = argument->offset + result;
3355 }
3356 }
3357}
3358
3359 VALUE
3360rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
3361{
3362 io = rb_io_get_write_io(rb_io_get_io(io));
3363
3364 VALUE scheduler = rb_fiber_scheduler_current();
3365 if (scheduler != Qnil) {
3366 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, length, offset);
3367
3368 if (!UNDEF_P(result)) {
3369 return result;
3370 }
3371 }
3372
3373 struct rb_io_buffer *buffer = NULL;
3374 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3375
3376 io_buffer_validate_range(buffer, offset, length);
3377
3378 int descriptor = rb_io_descriptor(io);
3379
3380 const void * base;
3381 size_t size;
3382 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3383
3384 base = (unsigned char*)base + offset;
3385 size = size - offset;
3386
3387 struct io_buffer_pwrite_internal_argument argument = {
3388 .descriptor = descriptor,
3389
3390 // Move the base pointer to the offset:
3391 .base = base,
3392
3393 // And the size to the length of buffer we want to read:
3394 .size = size,
3395
3396 // And the length of the buffer we want to write:
3397 .length = length,
3398
3399 // And the offset in the file we want to write from:
3400 .offset = from,
3401 };
3402
3403 return io_buffer_blocking_region(io, buffer, io_buffer_pwrite_internal, &argument);
3404}
3405
3406/*
3407 * call-seq: pwrite(io, from, [length, [offset]]) -> written length or -errno
3408 *
3409 * Write at least +length+ bytes from the buffer starting at +offset+, into
3410 * the +io+ starting at the specified +from+ position. If an error occurs,
3411 * return <tt>-errno</tt>.
3412 *
3413 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3414 * minus the offset, i.e. the entire buffer.
3415 *
3416 * If +length+ is zero, exactly one <tt>pwrite</tt> operation will occur.
3417 *
3418 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3419 * buffer.
3420 *
3421 * If the +from+ position is beyond the end of the file, the gap will be
3422 * filled with null (0 value) bytes.
3423 *
3424 * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
3425 * IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
3426 *
3427 * This leads to +234+ (3 bytes, starting from position 1) being written into
3428 * <tt>output.txt</tt>, starting from file position 2.
3429 */
3430static VALUE
3431io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
3432{
3433 rb_check_arity(argc, 2, 4);
3434
3435 VALUE io = argv[0];
3436 rb_off_t from = NUM2OFFT(argv[1]);
3437
3438 size_t length, offset;
3439 io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
3440
3441 return rb_io_buffer_pwrite(self, io, from, length, offset);
3442}
3443
3444static inline void
3445io_buffer_check_mask_size(size_t size)
3446{
3447 if (size == 0)
3448 rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
3449}
3450
3451static void
3452memory_and(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3453{
3454 for (size_t offset = 0; offset < size; offset += 1) {
3455 output[offset] = base[offset] & mask[offset % mask_size];
3456 }
3457}
3458
3459/*
3460 * call-seq:
3461 * source & mask -> io_buffer
3462 *
3463 * Generate a new buffer the same size as the source by applying the binary AND
3464 * operation to the source, using the mask, repeating as necessary.
3465 *
3466 * IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
3467 * # =>
3468 * # #<IO::Buffer 0x00005589b2758480+4 INTERNAL>
3469 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3470 */
3471static VALUE
3472io_buffer_and(VALUE self, VALUE mask)
3473{
3474 struct rb_io_buffer *buffer = NULL;
3475 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3476
3477 struct rb_io_buffer *mask_buffer = NULL;
3478 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3479
3480 const void *base;
3481 size_t size;
3482 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3483
3484 const void *mask_base;
3485 size_t mask_size;
3486 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3487
3488 io_buffer_check_mask_size(mask_size);
3489
3490 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3491 struct rb_io_buffer *output_buffer = NULL;
3492 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3493
3494 memory_and(output_buffer->base, base, size, mask_base, mask_size);
3495
3496 return output;
3497}
3498
3499static void
3500memory_or(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3501{
3502 for (size_t offset = 0; offset < size; offset += 1) {
3503 output[offset] = base[offset] | mask[offset % mask_size];
3504 }
3505}
3506
3507/*
3508 * call-seq:
3509 * source | mask -> io_buffer
3510 *
3511 * Generate a new buffer the same size as the source by applying the binary OR
3512 * operation to the source, using the mask, repeating as necessary.
3513 *
3514 * IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
3515 * # =>
3516 * # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
3517 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3518 */
3519static VALUE
3520io_buffer_or(VALUE self, VALUE mask)
3521{
3522 struct rb_io_buffer *buffer = NULL;
3523 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3524
3525 struct rb_io_buffer *mask_buffer = NULL;
3526 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3527
3528 const void *base;
3529 size_t size;
3530 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3531
3532 const void *mask_base;
3533 size_t mask_size;
3534 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3535
3536 io_buffer_check_mask_size(mask_size);
3537
3538 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3539 struct rb_io_buffer *output_buffer = NULL;
3540 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3541
3542 memory_or(output_buffer->base, base, size, mask_base, mask_size);
3543
3544 return output;
3545}
3546
3547static void
3548memory_xor(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3549{
3550 for (size_t offset = 0; offset < size; offset += 1) {
3551 output[offset] = base[offset] ^ mask[offset % mask_size];
3552 }
3553}
3554
3555/*
3556 * call-seq:
3557 * source ^ mask -> io_buffer
3558 *
3559 * Generate a new buffer the same size as the source by applying the binary XOR
3560 * operation to the source, using the mask, repeating as necessary.
3561 *
3562 * IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
3563 * # =>
3564 * # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
3565 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3566 */
3567static VALUE
3568io_buffer_xor(VALUE self, VALUE mask)
3569{
3570 struct rb_io_buffer *buffer = NULL;
3571 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3572
3573 struct rb_io_buffer *mask_buffer = NULL;
3574 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3575
3576 const void *base;
3577 size_t size;
3578 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3579
3580 const void *mask_base;
3581 size_t mask_size;
3582 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3583
3584 io_buffer_check_mask_size(mask_size);
3585
3586 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3587 struct rb_io_buffer *output_buffer = NULL;
3588 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3589
3590 memory_xor(output_buffer->base, base, size, mask_base, mask_size);
3591
3592 return output;
3593}
3594
3595static void
3596memory_not(unsigned char * restrict output, const unsigned char * restrict base, size_t size)
3597{
3598 for (size_t offset = 0; offset < size; offset += 1) {
3599 output[offset] = ~base[offset];
3600 }
3601}
3602
3603/*
3604 * call-seq:
3605 * ~source -> io_buffer
3606 *
3607 * Generate a new buffer the same size as the source by applying the binary NOT
3608 * operation to the source.
3609 *
3610 * ~IO::Buffer.for("1234567890")
3611 * # =>
3612 * # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
3613 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3614 */
3615static VALUE
3616io_buffer_not(VALUE self)
3617{
3618 struct rb_io_buffer *buffer = NULL;
3619 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3620
3621 const void *base;
3622 size_t size;
3623 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3624
3625 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3626 struct rb_io_buffer *output_buffer = NULL;
3627 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3628
3629 memory_not(output_buffer->base, base, size);
3630
3631 return output;
3632}
3633
3634static inline int
3635io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
3636{
3637 if (a->base > b->base) {
3638 return io_buffer_overlaps(b, a);
3639 }
3640
3641 return (b->base >= a->base) && (b->base < (void*)((unsigned char *)a->base + a->size));
3642}
3643
3644static inline void
3645io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
3646{
3647 if (io_buffer_overlaps(a, b))
3648 rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
3649}
3650
3651static void
3652memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3653{
3654 for (size_t offset = 0; offset < size; offset += 1) {
3655 base[offset] &= mask[offset % mask_size];
3656 }
3657}
3658
3659/*
3660 * call-seq:
3661 * source.and!(mask) -> io_buffer
3662 *
3663 * Modify the source buffer in place by applying the binary AND
3664 * operation to the source, using the mask, repeating as necessary.
3665 *
3666 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3667 * # =>
3668 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3669 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3670 *
3671 * source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3672 * # =>
3673 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3674 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3675 */
3676static VALUE
3677io_buffer_and_inplace(VALUE self, VALUE mask)
3678{
3679 struct rb_io_buffer *buffer = NULL;
3680 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3681
3682 struct rb_io_buffer *mask_buffer = NULL;
3683 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3684
3685 io_buffer_check_mask_size(mask_buffer->size);
3686 io_buffer_check_overlaps(buffer, mask_buffer);
3687
3688 void *base;
3689 size_t size;
3690 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3691
3692 const void *mask_base;
3693 size_t mask_size;
3694 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3695
3696 memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
3697
3698 return self;
3699}
3700
3701static void
3702memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3703{
3704 for (size_t offset = 0; offset < size; offset += 1) {
3705 base[offset] |= mask[offset % mask_size];
3706 }
3707}
3708
3709/*
3710 * call-seq:
3711 * source.or!(mask) -> io_buffer
3712 *
3713 * Modify the source buffer in place by applying the binary OR
3714 * operation to the source, using the mask, repeating as necessary.
3715 *
3716 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3717 * # =>
3718 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3719 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3720 *
3721 * source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3722 * # =>
3723 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3724 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3725 */
3726static VALUE
3727io_buffer_or_inplace(VALUE self, VALUE mask)
3728{
3729 struct rb_io_buffer *buffer = NULL;
3730 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3731
3732 struct rb_io_buffer *mask_buffer = NULL;
3733 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3734
3735 io_buffer_check_mask_size(mask_buffer->size);
3736 io_buffer_check_overlaps(buffer, mask_buffer);
3737
3738 void *base;
3739 size_t size;
3740 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3741
3742 const void *mask_base;
3743 size_t mask_size;
3744 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3745
3746 memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
3747
3748 return self;
3749}
3750
3751static void
3752memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3753{
3754 for (size_t offset = 0; offset < size; offset += 1) {
3755 base[offset] ^= mask[offset % mask_size];
3756 }
3757}
3758
3759/*
3760 * call-seq:
3761 * source.xor!(mask) -> io_buffer
3762 *
3763 * Modify the source buffer in place by applying the binary XOR
3764 * operation to the source, using the mask, repeating as necessary.
3765 *
3766 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3767 * # =>
3768 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3769 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3770 *
3771 * source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3772 * # =>
3773 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3774 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3775 */
3776static VALUE
3777io_buffer_xor_inplace(VALUE self, VALUE mask)
3778{
3779 struct rb_io_buffer *buffer = NULL;
3780 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3781
3782 struct rb_io_buffer *mask_buffer = NULL;
3783 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3784
3785 io_buffer_check_mask_size(mask_buffer->size);
3786 io_buffer_check_overlaps(buffer, mask_buffer);
3787
3788 void *base;
3789 size_t size;
3790 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3791
3792 const void *mask_base;
3793 size_t mask_size;
3794 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3795
3796 memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
3797
3798 return self;
3799}
3800
3801static void
3802memory_not_inplace(unsigned char * restrict base, size_t size)
3803{
3804 for (size_t offset = 0; offset < size; offset += 1) {
3805 base[offset] = ~base[offset];
3806 }
3807}
3808
3809/*
3810 * call-seq:
3811 * source.not! -> io_buffer
3812 *
3813 * Modify the source buffer in place by applying the binary NOT
3814 * operation to the source.
3815 *
3816 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3817 * # =>
3818 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3819 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3820 *
3821 * source.not!
3822 * # =>
3823 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3824 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3825 */
3826static VALUE
3827io_buffer_not_inplace(VALUE self)
3828{
3829 struct rb_io_buffer *buffer = NULL;
3830 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3831
3832 void *base;
3833 size_t size;
3834 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3835
3836 memory_not_inplace(base, size);
3837
3838 return self;
3839}
3840
3841/*
3842 * Document-class: IO::Buffer
3843 *
3844 * IO::Buffer is a efficient zero-copy buffer for input/output. There are
3845 * typical use cases:
3846 *
3847 * * Create an empty buffer with ::new, fill it with buffer using #copy or
3848 * #set_value, #set_string, get buffer with #get_string or write it directly
3849 * to some file with #write.
3850 * * Create a buffer mapped to some string with ::for, then it could be used
3851 * both for reading with #get_string or #get_value, and writing (writing will
3852 * change the source string, too).
3853 * * Create a buffer mapped to some file with ::map, then it could be used for
3854 * reading and writing the underlying file.
3855 * * Create a string of a fixed size with ::string, then #read into it, or
3856 * modify it using #set_value.
3857 *
3858 * Interaction with string and file memory is performed by efficient low-level
3859 * C mechanisms like `memcpy`.
3860 *
3861 * The class is meant to be an utility for implementing more high-level mechanisms
3862 * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
3863 * protocols.
3864 *
3865 * == Examples of Usage
3866 *
3867 * Empty buffer:
3868 *
3869 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
3870 * # =>
3871 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
3872 * # ...
3873 * buffer
3874 * # =>
3875 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
3876 * # 0x00000000 00 00 00 00 00 00 00 00
3877 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
3878 * # => 4
3879 * buffer.get_string # get the result
3880 * # => "\x00\x00test\x00\x00"
3881 *
3882 * \Buffer from string:
3883 *
3884 * string = 'data'
3885 * IO::Buffer.for(string) do |buffer|
3886 * buffer
3887 * # =>
3888 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3889 * # 0x00000000 64 61 74 61 data
3890 *
3891 * buffer.get_string(2) # read content starting from offset 2
3892 * # => "ta"
3893 * buffer.set_string('---', 1) # write content, starting from offset 1
3894 * # => 3
3895 * buffer
3896 * # =>
3897 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3898 * # 0x00000000 64 2d 2d 2d d---
3899 * string # original string changed, too
3900 * # => "d---"
3901 * end
3902 *
3903 * \Buffer from file:
3904 *
3905 * File.write('test.txt', 'test data')
3906 * # => 9
3907 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
3908 * # =>
3909 * # #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY>
3910 * # ...
3911 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
3912 * # => "da"
3913 * buffer.set_string('---', 1) # attempt to write
3914 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
3915 *
3916 * # To create writable file-mapped buffer
3917 * # Open file for read-write, pass size, offset, and flags=0
3918 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
3919 * buffer.set_string('---', 1)
3920 * # => 3 -- bytes written
3921 * File.read('test.txt')
3922 * # => "t--- data"
3923 *
3924 * <b>The class is experimental and the interface is subject to change, this
3925 * is especially true of file mappings which may be removed entirely in
3926 * the future.</b>
3927 */
3928void
3929Init_IO_Buffer(void)
3930{
3931 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
3932
3933 /* Raised when an operation would resize or re-allocate a locked buffer. */
3934 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
3935
3936 /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
3937 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
3938
3939 /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
3940 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
3941
3942 /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
3943 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
3944
3945 /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
3946 rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
3947
3948 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
3949 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
3950 rb_define_singleton_method(rb_cIOBuffer, "string", rb_io_buffer_type_string, 1);
3951
3952#ifdef _WIN32
3953 SYSTEM_INFO info;
3954 GetSystemInfo(&info);
3955 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
3956#else /* not WIN32 */
3957 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
3958#endif
3959
3960 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
3961
3962 /* The operating system page size. Used for efficient page-aligned memory allocations. */
3963 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
3964
3965 /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
3966 Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
3967 environment variable. */
3968 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
3969
3970 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
3971
3972 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
3973 rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
3974 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
3975 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, -1);
3976 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
3977 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
3978 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
3979
3980 rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
3981
3982 /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
3983 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
3984
3985 /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
3986 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
3987
3988 /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
3989 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
3990
3991 /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
3992 rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
3993
3994 /* Indicates that the memory in the buffer is locked and cannot be resized or freed. See #locked? and #locked for more details. */
3995 rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
3996
3997 /* Indicates that the memory in the buffer is mapped privately and changes won't be replicated to the underlying file. See #private? for more details. */
3998 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
3999
4000 /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
4001 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
4002
4003 /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
4004 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
4005
4006 /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
4007 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
4008
4009 /* Refers to the byte order of the host machine. See #get_value for more details. */
4010 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
4011
4012 /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
4013 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
4014
4015 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
4016 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
4017 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
4018 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
4019 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
4020 rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
4021 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
4022 rb_define_method(rb_cIOBuffer, "private?", rb_io_buffer_private_p, 0);
4023 rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
4024
4025 // Locking to prevent changes while using pointer:
4026 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
4027 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
4028 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
4029
4030 // Manipulation:
4031 rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
4032 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
4033 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
4034 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
4035 rb_define_method(rb_cIOBuffer, "free", rb_io_buffer_free, 0);
4036
4037 rb_include_module(rb_cIOBuffer, rb_mComparable);
4038
4039#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
4040 IO_BUFFER_DEFINE_DATA_TYPE(U8);
4041 IO_BUFFER_DEFINE_DATA_TYPE(S8);
4042
4043 IO_BUFFER_DEFINE_DATA_TYPE(u16);
4044 IO_BUFFER_DEFINE_DATA_TYPE(U16);
4045 IO_BUFFER_DEFINE_DATA_TYPE(s16);
4046 IO_BUFFER_DEFINE_DATA_TYPE(S16);
4047
4048 IO_BUFFER_DEFINE_DATA_TYPE(u32);
4049 IO_BUFFER_DEFINE_DATA_TYPE(U32);
4050 IO_BUFFER_DEFINE_DATA_TYPE(s32);
4051 IO_BUFFER_DEFINE_DATA_TYPE(S32);
4052
4053 IO_BUFFER_DEFINE_DATA_TYPE(u64);
4054 IO_BUFFER_DEFINE_DATA_TYPE(U64);
4055 IO_BUFFER_DEFINE_DATA_TYPE(s64);
4056 IO_BUFFER_DEFINE_DATA_TYPE(S64);
4057
4058 IO_BUFFER_DEFINE_DATA_TYPE(u128);
4059 IO_BUFFER_DEFINE_DATA_TYPE(U128);
4060 IO_BUFFER_DEFINE_DATA_TYPE(s128);
4061 IO_BUFFER_DEFINE_DATA_TYPE(S128);
4062
4063 IO_BUFFER_DEFINE_DATA_TYPE(f32);
4064 IO_BUFFER_DEFINE_DATA_TYPE(F32);
4065 IO_BUFFER_DEFINE_DATA_TYPE(f64);
4066 IO_BUFFER_DEFINE_DATA_TYPE(F64);
4067#undef IO_BUFFER_DEFINE_DATA_TYPE
4068
4069 rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
4070
4071 // Data access:
4072 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
4073 rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
4074 rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
4075 rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
4076 rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
4077 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
4078 rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
4079
4080 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
4081
4082 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
4083 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
4084
4085 // Binary buffer manipulations:
4086 rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
4087 rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
4088 rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
4089 rb_define_method(rb_cIOBuffer, "~", io_buffer_not, 0);
4090
4091 rb_define_method(rb_cIOBuffer, "and!", io_buffer_and_inplace, 1);
4092 rb_define_method(rb_cIOBuffer, "or!", io_buffer_or_inplace, 1);
4093 rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
4094 rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
4095
4096 // IO operations:
4097 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
4098 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
4099 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
4100 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
4101}
buffer.h
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition cxxanyargs.hpp:670
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition cxxanyargs.hpp:685
RB_OBJ_FROZEN
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition fl_type.h:892
rb_include_module
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1684
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1508
rb_block_given_p
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1021
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
OBJ_FROZEN
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:136
rb_str_cat2
#define rb_str_cat2
Old name of rb_str_cat_cstr.
Definition string.h:1684
CLASS_OF
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
SIZET2NUM
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition size_t.h:62
NUM2UINT
#define NUM2UINT
Old name of RB_NUM2UINT.
Definition int.h:45
NUM2DBL
#define NUM2DBL
Old name of rb_num2dbl.
Definition double.h:27
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
DBL2NUM
#define DBL2NUM
Old name of rb_float_new.
Definition double.h:29
NUM2SIZET
#define NUM2SIZET
Old name of RB_NUM2SIZE.
Definition size_t.h:61
rb_category_warn
void rb_category_warn(rb_warning_category_t category, const char *fmt,...)
Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:476
rb_eRuntimeError
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
RB_WARN_CATEGORY_EXPERIMENTAL
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
Definition error.h:51
rb_cIO
VALUE rb_cIO
IO class.
Definition io.c:187
rb_class_of
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
rb_mComparable
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
RB_OBJ_WRITE
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
rb_ascii8bit_encoding
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
Definition encoding.c:1523
rb_ary_new_capa
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
rb_ary_entry
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
RETURN_ENUMERATOR_KW
#define RETURN_ENUMERATOR_KW(obj, argc, argv, kw_splat)
Identical to RETURN_SIZED_ENUMERATOR_KW(), except its size is unknown.
Definition enumerator.h:260
rb_check_arity
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
rb_str_append
VALUE rb_str_append(VALUE dst, VALUE src)
Identical to rb_str_buf_append(), except it converts the right hand side before concatenating.
Definition string.c:3799
rb_str_new
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
rb_str_locktmp
VALUE rb_str_locktmp(VALUE str)
Obtains a "temporary lock" of the string.
rb_str_unlocktmp
VALUE rb_str_unlocktmp(VALUE str)
Releases a lock formerly obtained by rb_str_locktmp().
Definition string.c:3371
rb_str_buf_new
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1718
rb_str_new_cstr
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
rb_class_name
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:500
rb_define_alloc_func
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
RB_SYM2ID
#define RB_SYM2ID
Just another name of rb_sym2id.
Definition symbol.h:43
rb_io_get_io
VALUE rb_io_get_io(VALUE io)
Identical to rb_io_check_io(), except it raises exceptions on conversion failures.
Definition io.c:811
rb_io_descriptor
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition io.c:2927
RB_IO_POINTER
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
Definition io.h:436
rb_io_get_write_io
VALUE rb_io_get_write_io(VALUE io)
Queries the tied IO for writing.
Definition io.c:823
thread.h
rb_nogvl
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
Identical to rb_thread_call_without_gvl(), except it additionally takes "flags" that change the behav...
Definition thread.c:1593
RB_NOGVL_OFFLOAD_SAFE
#define RB_NOGVL_OFFLOAD_SAFE
Passing this flag to rb_nogvl() indicates that the passed function is safe to offload to a background...
Definition thread.h:73
RB_NUM2INT
#define RB_NUM2INT
Just another name of rb_num2int_inline.
Definition int.h:38
RB_UINT2NUM
#define RB_UINT2NUM
Just another name of rb_uint2num_inline.
Definition int.h:39
RB_INT2NUM
#define RB_INT2NUM
Just another name of rb_int2num_inline.
Definition int.h:37
RB_NUM2UINT
static unsigned int RB_NUM2UINT(VALUE x)
Converts an instance of rb_cNumeric into C's unsigned int.
Definition int.h:185
RB_LL2NUM
#define RB_LL2NUM
Just another name of rb_ll2num_inline.
Definition long_long.h:28
RB_ULL2NUM
#define RB_ULL2NUM
Just another name of rb_ull2num_inline.
Definition long_long.h:29
RB_NUM2ULL
#define RB_NUM2ULL
Just another name of rb_num2ull_inline.
Definition long_long.h:33
RB_NUM2LL
#define RB_NUM2LL
Just another name of rb_num2ll_inline.
Definition long_long.h:32
rb_yield_values
VALUE rb_yield_values(int n,...)
Identical to rb_yield(), except it takes variadic number of parameters and pass them to the block.
Definition vm_eval.c:1395
rb_yield
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
RB_INT2FIX
static VALUE RB_INT2FIX(long i)
Converts a C's long into an instance of rb_cInteger.
Definition long.h:111
RB_NUM2LONG
#define RB_NUM2LONG
Just another name of rb_num2long_inline.
Definition long.h:57
RB_GC_GUARD
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition cxxanyargs.hpp:56
ruby::backward::cxxanyargs::rb_ensure
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
Definition cxxanyargs.hpp:310
NUM2OFFT
#define NUM2OFFT
Converts an instance of rb_cNumeric into C's off_t.
Definition off_t.h:44
RARRAY_LEN
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition rarray.h:403
StringValue
#define StringValue(v)
Ensures that the parameter object is a String.
Definition rstring.h:66
RSTRING_GETMEM
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
Definition rstring.h:450
rb_str_to_str
VALUE rb_str_to_str(VALUE obj)
Identical to rb_check_string_type(), except it raises exceptions in case of conversion failures.
Definition string.c:1779
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:649
rb_data_type_t
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:205
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:508
errno
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
RB_NO_KEYWORDS
#define RB_NO_KEYWORDS
Do not pass keywords.
Definition scan_args.h:69
scheduler.h
Scheduler APIs.
rb_fiber_scheduler_current
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:471
rb_fiber_scheduler_io_pwrite
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
Non-blocking write to the passed IO at the specified offset.
Definition scheduler.c:962
rb_fiber_scheduler_io_read
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
Non-blocking read from the passed IO.
Definition scheduler.c:830
rb_fiber_scheduler_io_result
static VALUE rb_fiber_scheduler_io_result(ssize_t result, int error)
Wrap a ssize_t and int errno into a single VALUE.
Definition scheduler.h:50
rb_fiber_scheduler_io_pread
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, rb_off_t from, VALUE buffer, size_t length, size_t offset)
Non-blocking read from the passed IO at the specified offset.
Definition scheduler.c:869
rb_fiber_scheduler_io_write
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length, size_t offset)
Non-blocking write to the passed IO.
Definition scheduler.c:922
RB_NIL_P
static bool RB_NIL_P(VALUE obj)
Checks if the given object is nil.
Definition special_consts.h:173
io_buffer_blocking_region_argument
Definition io_buffer.c:2917
io_buffer_for_yield_instance_arguments
Definition io_buffer.c:512
io_buffer_memmove_arguments
Definition io_buffer.c:2539
io_buffer_pread_internal_argument
Definition io_buffer.c:3083
io_buffer_pwrite_internal_argument
Definition io_buffer.c:3317
io_buffer_read_internal_argument
Definition io_buffer.c:2967
io_buffer_set_value_arguments
Definition io_buffer.c:2417
io_buffer_write_internal_argument
Definition io_buffer.c:3208
rb_io_buffer
Definition io_buffer.c:52
rb_io
Ruby's IO, metadata and buffers.
Definition io.h:295
swapf32
Definition io_buffer.c:1920
swapf64
Definition io_buffer.c:1933
uint128_int128_conversion
Definition numeric.h:167
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_TYPE_P
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376