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