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