Ruby 4.1.0dev (2026-09-27 revision f6ff9e7d02e46360f8930b280a3dd921cccbda29)
io_buffer.c (f6ff9e7d02e46360f8930b280a3dd921cccbda29)
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 rb_gc_update_moved(&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 ((size_t)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 rb_check_frozen(self);
1162
1163 struct rb_io_buffer *buffer = get_io_buffer(self);
1164 io_buffer_validate_for_writing(buffer);
1165 return buffer;
1166}
1167
1168static inline void
1169io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
1170{
1171 io_buffer_validate_for_writing(buffer);
1172
1173 if (buffer->base) {
1174 *base = buffer->base;
1175 *size = buffer->size;
1176 } else {
1177 *base = NULL;
1178 *size = 0;
1179 }
1180}
1181
1182void
1183rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
1184{
1185 struct rb_io_buffer *buffer = get_io_buffer(self);
1186
1187 io_buffer_get_bytes_for_writing(buffer, base, size);
1188}
1189
1190static void
1191io_buffer_validate_for_reading(struct rb_io_buffer *buffer)
1192{
1193 if (!io_buffer_validate(buffer)) {
1194 rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
1195 }
1196}
1197
1198static void
1199io_buffer_get_bytes_for_reading(struct rb_io_buffer *buffer, const void **base, size_t *size)
1200{
1201 io_buffer_validate_for_reading(buffer);
1202
1203 if (buffer->base) {
1204 *base = buffer->base;
1205 *size = buffer->size;
1206 } else {
1207 *base = NULL;
1208 *size = 0;
1209 }
1210}
1211
1212void
1213rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
1214{
1215 struct rb_io_buffer *buffer = get_io_buffer(self);
1216
1217 io_buffer_get_bytes_for_reading(buffer, base, size);
1218}
1219
1220/*
1221 * call-seq: to_s -> string
1222 *
1223 * Short representation of the buffer. It includes the address, size and
1224 * symbolic flags. This format is subject to change.
1225 *
1226 * puts IO::Buffer.new(4) # uses to_s internally
1227 * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
1228 */
1229VALUE
1230rb_io_buffer_to_s(VALUE self)
1231{
1232 struct rb_io_buffer *buffer = get_io_buffer(self);
1233
1234 VALUE result = rb_str_new_cstr("#<");
1235
1236 rb_str_append(result, rb_class_name(CLASS_OF(self)));
1237 rb_str_catf(result, " %p+%"PRIdSIZE, buffer->base, buffer->size);
1238
1239 if (buffer->base == NULL) {
1240 rb_str_cat2(result, " NULL");
1241 }
1242
1243 if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
1244 rb_str_cat2(result, " EXTERNAL");
1245 }
1246
1247 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
1248 rb_str_cat2(result, " INTERNAL");
1249 }
1250
1251 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
1252 rb_str_cat2(result, " MAPPED");
1253 }
1254
1255 if (buffer->flags & RB_IO_BUFFER_FILE) {
1256 rb_str_cat2(result, " FILE");
1257 }
1258
1259 if (buffer->flags & RB_IO_BUFFER_SHARED) {
1260 rb_str_cat2(result, " SHARED");
1261 }
1262
1263 if (io_buffer_locked(buffer)) {
1264 rb_str_cat2(result, " LOCKED");
1265 }
1266
1267 if (buffer->flags & RB_IO_BUFFER_PRIVATE) {
1268 rb_str_cat2(result, " PRIVATE");
1269 }
1270
1271 if (buffer->flags & RB_IO_BUFFER_READONLY) {
1272 rb_str_cat2(result, " READONLY");
1273 }
1274
1275 if (buffer->source != Qnil) {
1276 rb_str_cat2(result, " SLICE");
1277 }
1278
1279 if (!io_buffer_validate(buffer)) {
1280 rb_str_cat2(result, " INVALID");
1281 }
1282
1283 return rb_str_cat2(result, ">");
1284}
1285
1286// 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.
1287// This is used to preallocate the output string.
1288inline static size_t
1289io_buffer_hexdump_output_size(size_t width, size_t size, int first)
1290{
1291 // The preview on the right hand side is 1:1:
1292 size_t total = size;
1293
1294 size_t whole_lines = (size / width);
1295 size_t partial_line = (size % width) ? 1 : 0;
1296
1297 // For each line:
1298 // 1 byte 10 bytes 1 byte width*3 bytes 1 byte size bytes
1299 // (newline) (address) (space) (hexdump ) (space) (preview)
1300 total += (whole_lines + partial_line) * (1 + 10 + width*3 + 1 + 1);
1301
1302 // If the hexdump is the first line, one less newline will be emitted:
1303 if (size && first) total -= 1;
1304
1305 return total;
1306}
1307
1308// Append a hexdump of the given width (bytes per line), base address, size, and whether it is the first line in the output.
1309// If the hexdump is not the first line, it will prepend a newline if there is any output at all.
1310// If formatting here is adjusted, please update io_buffer_hexdump_output_size accordingly.
1311static VALUE
1312io_buffer_hexdump(VALUE string, size_t width, const char *base, size_t length, size_t offset, int first)
1313{
1314 char *text = alloca(width+1);
1315 text[width] = '\0';
1316
1317 for (; offset < length; offset += width) {
1318 memset(text, '\0', width);
1319 if (first) {
1320 rb_str_catf(string, "0x%08" PRIxSIZE " ", offset);
1321 first = 0;
1322 }
1323 else {
1324 rb_str_catf(string, "\n0x%08" PRIxSIZE " ", offset);
1325 }
1326
1327 for (size_t i = 0; i < width; i += 1) {
1328 if (offset+i < length) {
1329 unsigned char value = ((unsigned char*)base)[offset+i];
1330
1331 if (value < 127 && isprint(value)) {
1332 text[i] = (char)value;
1333 }
1334 else {
1335 text[i] = '.';
1336 }
1337
1338 rb_str_catf(string, " %02x", value);
1339 }
1340 else {
1341 rb_str_cat2(string, " ");
1342 }
1343 }
1344
1345 rb_str_catf(string, " %s", text);
1346 }
1347
1348 return string;
1349}
1350
1351/*
1352 * call-seq: inspect -> string
1353 *
1354 * Inspect the buffer and report useful information about it's internal state.
1355 * Only a limited portion of the buffer will be displayed in a hexdump style
1356 * format.
1357 *
1358 * buffer = IO::Buffer.for("Hello World")
1359 * puts buffer.inspect
1360 * # #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
1361 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1362 */
1363VALUE
1364rb_io_buffer_inspect(VALUE self)
1365{
1366 struct rb_io_buffer *buffer = get_io_buffer(self);
1367
1368 VALUE result = rb_io_buffer_to_s(self);
1369
1370 if (io_buffer_validate(buffer)) {
1371 // Limit the maximum size generated by inspect:
1372 size_t size = buffer->size;
1373 int clamped = 0;
1374
1375 if (size > RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE) {
1376 size = RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE;
1377 clamped = 1;
1378 }
1379
1380 io_buffer_hexdump(result, RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH, buffer->base, size, 0, 0);
1381
1382 if (clamped) {
1383 rb_str_catf(result, "\n(and %" PRIuSIZE " more bytes not printed)", buffer->size - size);
1384 }
1385 }
1386
1387 return result;
1388}
1389
1390/*
1391 * call-seq: size -> integer
1392 *
1393 * Returns the size of the buffer that was explicitly set (on creation with ::new
1394 * or on #resize), or deduced on buffer's creation from string or file.
1395 */
1396VALUE
1397rb_io_buffer_size(VALUE self)
1398{
1399 struct rb_io_buffer *buffer = get_io_buffer(self);
1400
1401 return SIZET2NUM(buffer->size);
1402}
1403
1404/*
1405 * call-seq: valid? -> true or false
1406 *
1407 * A buffer which is not a slice is always valid, including a null buffer.
1408 * Only slices can become invalid.
1409 *
1410 * A slice is valid when its entire recorded memory range is contained within
1411 * its source's current memory range. It can become invalid if its source is
1412 * freed, transferred, shrunk past the slice, or reallocated at a different
1413 * address. Validity is dynamic: if the source later contains the same address
1414 * range again, the slice becomes valid again.
1415 *
1416 * #valid?, #null? and #empty? describe independent properties. For example,
1417 * an invalid slice can still have a non-null address and a non-zero size.
1418 */
1419static VALUE
1420rb_io_buffer_valid_p(VALUE self)
1421{
1422 struct rb_io_buffer *buffer = get_io_buffer(self);
1423
1424 return RBOOL(io_buffer_validate(buffer));
1425}
1426
1427/*
1428 * call-seq: null? -> true or false
1429 *
1430 * Returns whether the buffer has no recorded base address.
1431 *
1432 * A buffer is null if it was freed with #free, transferred with #transfer, or
1433 * was never allocated in the first place. A zero-sized buffer or slice may
1434 * have a non-null address, so #null? and #empty? are distinct properties.
1435 *
1436 * buffer = IO::Buffer.new(0)
1437 * buffer.null? #=> true
1438 *
1439 * buffer = IO::Buffer.new(4)
1440 * buffer.null? #=> false
1441 * buffer.free
1442 * buffer.null? #=> true
1443 */
1444static VALUE
1445rb_io_buffer_null_p(VALUE self)
1446{
1447 struct rb_io_buffer *buffer = get_io_buffer(self);
1448
1449 return RBOOL(buffer->base == NULL);
1450}
1451
1452/*
1453 * call-seq: empty? -> true or false
1454 *
1455 * Returns whether the buffer has zero size.
1456 *
1457 * A buffer can be empty but have a non-null address, for example a zero-sized
1458 * slice or a buffer created with ::for from an empty string. Therefore
1459 * #empty? does not imply #null?.
1460 */
1461static VALUE
1462rb_io_buffer_empty_p(VALUE self)
1463{
1464 struct rb_io_buffer *buffer = get_io_buffer(self);
1465
1466 return RBOOL(buffer->size == 0);
1467}
1468
1469/*
1470 * call-seq: external? -> true or false
1471 *
1472 * The buffer is _external_ if it references the memory which is not
1473 * allocated or mapped by the buffer itself.
1474 *
1475 * A buffer created using ::for has an external reference to the string's
1476 * memory.
1477 *
1478 * External buffer can't be resized.
1479 */
1480static VALUE
1481rb_io_buffer_external_p(VALUE self)
1482{
1483 struct rb_io_buffer *buffer = get_io_buffer(self);
1484
1485 return RBOOL(buffer->flags & RB_IO_BUFFER_EXTERNAL);
1486}
1487
1488/*
1489 * call-seq: internal? -> true or false
1490 *
1491 * If the buffer is _internal_, meaning it references memory allocated by the
1492 * buffer itself.
1493 *
1494 * An internal buffer is not associated with any external memory (e.g. string)
1495 * or file mapping.
1496 *
1497 * Internal buffers are created using ::new and is the default when the
1498 * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
1499 * requested to be mapped on creation.
1500 *
1501 * Internal buffers can be resized, and such an operation will typically
1502 * invalidate all slices, but not always.
1503 */
1504static VALUE
1505rb_io_buffer_internal_p(VALUE self)
1506{
1507 struct rb_io_buffer *buffer = get_io_buffer(self);
1508
1509 return RBOOL(buffer->flags & RB_IO_BUFFER_INTERNAL);
1510}
1511
1512/*
1513 * call-seq: mapped? -> true or false
1514 *
1515 * If the buffer is _mapped_, meaning it references memory mapped by the
1516 * buffer.
1517 *
1518 * Mapped buffers are either anonymous, if created by ::new with the
1519 * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
1520 * or backed by a file if created with ::map.
1521 *
1522 * Mapped buffers can usually be resized, and such an operation will typically
1523 * invalidate all slices, but not always.
1524 */
1525static VALUE
1526rb_io_buffer_mapped_p(VALUE self)
1527{
1528 struct rb_io_buffer *buffer = get_io_buffer(self);
1529
1530 return RBOOL(buffer->flags & RB_IO_BUFFER_MAPPED);
1531}
1532
1533/*
1534 * call-seq: shared? -> true or false
1535 *
1536 * If the buffer is _shared_, meaning it references memory that can be shared
1537 * with other processes (and thus might change without being modified
1538 * locally).
1539 *
1540 * # Create a test file:
1541 * File.write('test.txt', 'test')
1542 *
1543 * # Create a shared mapping from the given file, the file must be opened in
1544 * # read-write mode unless we also specify IO::Buffer::READONLY:
1545 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), nil, 0)
1546 * # => #<IO::Buffer 0x00007f1bffd5e000+4 EXTERNAL MAPPED SHARED>
1547 *
1548 * # Write to the buffer, which will modify the mapped file:
1549 * buffer.set_string('b', 0)
1550 * # => 1
1551 *
1552 * # The file itself is modified:
1553 * File.read('test.txt')
1554 * # => "best"
1555 */
1556static VALUE
1557rb_io_buffer_shared_p(VALUE self)
1558{
1559 struct rb_io_buffer *buffer = get_io_buffer(self);
1560
1561 return RBOOL(buffer->flags & RB_IO_BUFFER_SHARED);
1562}
1563
1564/*
1565 * call-seq: locked? -> true or false
1566 *
1567 * If the buffer is _locked_, its underlying allocation cannot be resized,
1568 * freed or transferred. Locks are shared with slices and may be nested.
1569 *
1570 * Locking is a lifetime mechanism used to ensure buffers don't move while
1571 * being used by a system call or other native operation.
1572 *
1573 * buffer.locked do
1574 * buffer.write(io) # theoretical system call interface
1575 * end
1576 */
1577static VALUE
1578rb_io_buffer_locked_p(VALUE self)
1579{
1580 struct rb_io_buffer *buffer = get_io_buffer(self);
1581
1582 return RBOOL(io_buffer_locked(buffer));
1583}
1584
1585/* call-seq: private? -> true or false
1586 *
1587 * If the buffer is _private_, meaning modifications to the buffer will not
1588 * be replicated to the underlying file mapping.
1589 *
1590 * # Create a test file:
1591 * File.write('test.txt', 'test')
1592 *
1593 * # Create a private mapping from the given file. Note that the file here
1594 * # is opened in read-only mode, but it doesn't matter due to the private
1595 * # mapping:
1596 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE)
1597 * # => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE>
1598 *
1599 * # Write to the buffer (invoking CoW of the underlying file buffer):
1600 * buffer.set_string('b', 0)
1601 * # => 1
1602 *
1603 * # The file itself is not modified:
1604 * File.read('test.txt')
1605 * # => "test"
1606 */
1607static VALUE
1608rb_io_buffer_private_p(VALUE self)
1609{
1610 struct rb_io_buffer *buffer = get_io_buffer(self);
1611
1612 return RBOOL(buffer->flags & RB_IO_BUFFER_PRIVATE);
1613}
1614
1615static int
1616io_buffer_readonly_p(struct rb_io_buffer *buffer)
1617{
1618 return buffer->flags & RB_IO_BUFFER_READONLY;
1619}
1620
1621/*
1622 * call-seq: readonly? -> true or false
1623 *
1624 * If the buffer is <i>read only</i>, meaning the buffer cannot be modified using
1625 * #set_value, #set_string or #copy and similar.
1626 *
1627 * A buffer created by IO::Buffer.for without a block is read-only, as is one
1628 * backed by a frozen string or a read-only file.
1629 */
1630static VALUE
1631rb_io_buffer_readonly_p(VALUE self)
1632{
1633 struct rb_io_buffer *buffer = get_io_buffer(self);
1634
1635 return RBOOL(io_buffer_readonly_p(buffer));
1636}
1637
1638static void
1639io_buffer_lock(struct rb_io_buffer *buffer)
1640{
1641 struct rb_io_buffer *owner = io_buffer_lock_owner(buffer);
1642
1643 if (owner->lock_count == SIZE_MAX) {
1644 rb_raise(rb_eIOBufferLockedError, "It's locks all the way down!");
1645 }
1646
1647 owner->lock_count += 1;
1648}
1649
1650VALUE
1651rb_io_buffer_lock(VALUE self)
1652{
1653 struct rb_io_buffer *buffer = get_io_buffer(self);
1654
1655 io_buffer_lock(buffer);
1656
1657 return self;
1658}
1659
1660static void
1661io_buffer_unlock(struct rb_io_buffer *buffer)
1662{
1663 struct rb_io_buffer *owner = io_buffer_lock_owner(buffer);
1664
1665 if (owner->lock_count == 0) {
1666 rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
1667 }
1668
1669 owner->lock_count -= 1;
1670}
1671
1672VALUE
1673rb_io_buffer_unlock(VALUE self)
1674{
1675 struct rb_io_buffer *buffer = get_io_buffer(self);
1676
1677 io_buffer_unlock(buffer);
1678
1679 return self;
1680}
1681
1682int
1683rb_io_buffer_try_unlock(VALUE self)
1684{
1685 struct rb_io_buffer *buffer = get_io_buffer(self);
1686 struct rb_io_buffer *owner = io_buffer_lock_owner(buffer);
1687
1688 if (owner->lock_count > 0) {
1689 owner->lock_count -= 1;
1690
1691 return 1;
1692 }
1693
1694 return 0;
1695}
1696
1697static VALUE
1698rb_io_buffer_locked_ensure(VALUE self)
1699{
1700 struct rb_io_buffer *buffer = get_io_buffer(self);
1701
1702 io_buffer_unlock(buffer);
1703
1704 return Qnil;
1705}
1706
1708 VALUE self;
1709 VALUE (*callback)(const void *base, size_t size, VALUE argument);
1710 VALUE argument;
1711};
1712
1713static VALUE
1714io_buffer_readable_bytes_call(VALUE _arguments)
1715{
1716 struct io_buffer_readable_bytes_arguments *arguments = (void *)_arguments;
1717
1718 const void *base;
1719 size_t size;
1720 rb_io_buffer_get_bytes_for_reading(arguments->self, &base, &size);
1721
1722 return arguments->callback(base, size, arguments->argument);
1723}
1724
1725VALUE
1726rb_io_buffer_locked_for_reading(VALUE self, VALUE (*callback)(const void *base, size_t size, VALUE argument), VALUE argument)
1727{
1728 struct rb_io_buffer *buffer = get_io_buffer(self);
1729 io_buffer_validate_for_reading(buffer);
1730
1731 struct io_buffer_readable_bytes_arguments arguments = {
1732 .self = self,
1733 .callback = callback,
1734 .argument = argument,
1735 };
1736
1737 rb_io_buffer_lock(self);
1738 return rb_ensure(io_buffer_readable_bytes_call, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
1739}
1740
1742 VALUE self;
1743 VALUE (*callback)(void *base, size_t size, VALUE argument);
1744 VALUE argument;
1745};
1746
1747static VALUE
1748io_buffer_writable_bytes_call(VALUE _arguments)
1749{
1750 struct io_buffer_writable_bytes_arguments *arguments = (void *)_arguments;
1751
1752 void *base;
1753 size_t size;
1754 rb_io_buffer_get_bytes_for_writing(arguments->self, &base, &size);
1755
1756 return arguments->callback(base, size, arguments->argument);
1757}
1758
1759VALUE
1760rb_io_buffer_locked_for_writing(VALUE self, VALUE (*callback)(void *base, size_t size, VALUE argument), VALUE argument)
1761{
1762 get_io_buffer_for_writing(self);
1763
1764 struct io_buffer_writable_bytes_arguments arguments = {
1765 .self = self,
1766 .callback = callback,
1767 .argument = argument,
1768 };
1769
1770 rb_io_buffer_lock(self);
1771 return rb_ensure(io_buffer_writable_bytes_call, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
1772}
1773
1774/*
1775 * call-seq: locked { ... }
1776 *
1777 * Prevents the buffer or its buffer source from being moved or freed while
1778 * the block is executing. Locks are nested and shared with slices backed by
1779 * the same buffer source. The source remains locked until every nested lock
1780 * has been released.
1781 *
1782 * Locking protects allocation lifetime; it does not serialize access to the
1783 * bytes. Code that shares mutable buffer contents between threads must still
1784 * use appropriate synchronization.
1785 *
1786 * buffer = IO::Buffer.new(4)
1787 * buffer.locked? #=> false
1788 *
1789 * Fiber.schedule do
1790 * buffer.locked do
1791 * buffer.write(io) # theoretical system call interface
1792 * end
1793 * end
1794 *
1795 * Fiber.schedule do
1796 * buffer.locked do
1797 * buffer.set_string("test", 0) # Nested locking is allowed.
1798 * end
1799 * end
1800 */
1801VALUE
1802rb_io_buffer_locked(VALUE self)
1803{
1804 struct rb_io_buffer *buffer = get_io_buffer(self);
1805
1806 // Only yield the block for a currently valid view. In particular, an
1807 // invalid slice should not lock its source.
1808 io_buffer_validate_for_reading(buffer);
1809
1810 io_buffer_lock(buffer);
1811
1812 return rb_ensure(rb_yield, self, rb_io_buffer_locked_ensure, self);
1813}
1814
1815VALUE
1816rb_io_buffer_free(VALUE self)
1817{
1818 struct rb_io_buffer *buffer = get_io_buffer(self);
1819
1820 if (io_buffer_locked(buffer)) {
1821 rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
1822 }
1823
1824 io_buffer_release(buffer);
1825
1826 return self;
1827}
1828
1829/*
1830 * call-seq: free -> self
1831 *
1832 * If the buffer references memory, release it back to the operating system.
1833 * * for a _mapped_ buffer (e.g. from file): unmap.
1834 * * for a buffer created from scratch: free memory.
1835 * * for a buffer created from string: undo the association.
1836 *
1837 * After releasing any referenced memory, the buffer is reset to a valid,
1838 * empty, null state. It has no backing storage and its size is zero.
1839 * Zero-length operations remain valid, while operations requiring bytes fail
1840 * normal bounds checking.
1841 *
1842 * You can resize the buffer to allocate new storage.
1843 *
1844 * buffer = IO::Buffer.for('test')
1845 * buffer.free
1846 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
1847 *
1848 * buffer.null? # => true
1849 * buffer.empty? # => true
1850 * buffer.valid? # => true
1851 * buffer.get_string # => ""
1852 *
1853 * buffer.get_value(:U8, 0) # raises ArgumentError
1854 *
1855 * A frozen buffer cannot be freed, as that would release the memory its
1856 * contents live in:
1857 *
1858 * buffer = IO::Buffer.for('test').freeze
1859 * buffer.free
1860 * # in `free': can't modify frozen IO::Buffer (FrozenError)
1861 */
1862static VALUE
1863io_buffer_free(VALUE self)
1864{
1865 rb_check_frozen(self);
1866
1867 return rb_io_buffer_free(self);
1868}
1869
1870VALUE rb_io_buffer_free_locked(VALUE self)
1871{
1872 struct rb_io_buffer *buffer = get_io_buffer(self);
1873 struct rb_io_buffer *owner = io_buffer_lock_owner(buffer);
1874
1875 // This function is used to invalidate temporary wrappers around borrowed
1876 // memory. If another lock remains, the owner cannot safely end the
1877 // lifetime of that memory while another operation still retains it.
1878 if (owner->lock_count != 1) {
1879 rb_bug("rb_io_buffer_free_locked: expected lock count 1, got %" PRIuSIZE, owner->lock_count);
1880 }
1881
1882 io_buffer_unlock(buffer);
1883 io_buffer_release(buffer);
1884
1885 return self;
1886}
1887
1888static bool
1889size_sum_is_bigger_than(size_t a, size_t b, size_t x)
1890{
1891 struct rbimpl_size_overflow_tag size = rbimpl_size_add_overflow(a, b);
1892 return size.overflowed || size.result > x;
1893}
1894
1895// Validate that access to the buffer is within bounds, assuming you want to
1896// access length bytes from the specified offset.
1897static inline void
1898io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
1899{
1900 io_buffer_validate_for_reading(buffer);
1901
1902 if (size_sum_is_bigger_than(offset, length, buffer->size)) {
1903 rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
1904 }
1905}
1906
1907/*
1908 * call-seq: hexdump([offset, [length, [width]]]) -> string or nil
1909 *
1910 * Returns a human-readable string representation of the buffer. The exact
1911 * format is subject to change.
1912 *
1913 * Returns +nil+ if the buffer does not reference any memory, that is, if
1914 * #null? returns +true+ (for example after #free or #transfer).
1915 *
1916 * buffer = IO::Buffer.for("Hello World")
1917 * puts buffer.hexdump
1918 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1919 *
1920 * As buffers are usually fairly big, you may want to limit the output by
1921 * specifying the offset and length:
1922 *
1923 * puts buffer.hexdump(6, 5)
1924 * # 0x00000006 57 6f 72 6c 64 World
1925 */
1926static VALUE
1927rb_io_buffer_hexdump(int argc, VALUE *argv, VALUE self)
1928{
1929 rb_check_arity(argc, 0, 3);
1930
1931 size_t offset, length;
1932 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
1933
1934 size_t width = RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH;
1935 if (argc >= 3) {
1936 width = io_buffer_extract_width(argv[2], 1);
1937 }
1938
1939 // This may raise an exception if the offset/length is invalid:
1940 io_buffer_validate_range(buffer, offset, length);
1941
1942 VALUE result = Qnil;
1943
1944 if (io_buffer_validate(buffer) && buffer->base) {
1945 result = rb_str_buf_new(io_buffer_hexdump_output_size(width, length, 1));
1946
1947 io_buffer_hexdump(result, width, buffer->base, offset+length, offset, 1);
1948 }
1949
1950 return result;
1951}
1952
1953static VALUE
1954rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_t length)
1955{
1956 io_buffer_validate_range(buffer, offset, length);
1957
1958 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1959 struct rb_io_buffer *slice = get_io_buffer(instance);
1960
1961 slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
1962 slice->base = buffer->base ? (char*)buffer->base + offset : NULL;
1963 slice->size = length;
1964
1965 // Slices retain their root buffer. If this buffer is already a slice,
1966 // retain its root directly rather than building a chain of slices:
1967 if (io_buffer_slice_p(buffer)) {
1968 RB_OBJ_WRITE(instance, &slice->source, buffer->source);
1969 }
1970 else {
1971 RB_OBJ_WRITE(instance, &slice->source, self);
1972 }
1973
1974 return instance;
1975}
1976
1977/*
1978 * call-seq: slice([offset, [length]]) -> io_buffer
1979 *
1980 * Produce another IO::Buffer which is a slice (or view into) the current one
1981 * starting at +offset+ bytes and going for +length+ bytes.
1982 *
1983 * The slicing happens without copying memory. The slice retains its root
1984 * buffer and becomes invalid if that root is freed, transferred, resized so
1985 * that the slice is outside its bounds, or otherwise invalidated.
1986 *
1987 * If the offset is not given, it will be zero. If the offset is negative, it
1988 * will raise an ArgumentError.
1989 *
1990 * If the length is not given, the slice will be as long as the original
1991 * buffer minus the specified offset. If the length is negative, it will raise
1992 * an ArgumentError.
1993 *
1994 * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
1995 * buffer's bounds.
1996 *
1997 * string = 'test'
1998 * buffer = IO::Buffer.for(string).dup
1999 *
2000 * slice = buffer.slice
2001 * # =>
2002 * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
2003 * # 0x00000000 74 65 73 74 test
2004 *
2005 * buffer.slice(2)
2006 * # =>
2007 * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
2008 * # 0x00000000 73 74 st
2009 *
2010 * slice = buffer.slice(1, 2)
2011 * # =>
2012 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
2013 * # 0x00000000 65 73 es
2014 *
2015 * # Put "o" into 0s position of the slice
2016 * slice.set_string('o', 0)
2017 * slice
2018 * # =>
2019 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
2020 * # 0x00000000 6f 73 os
2021 *
2022 * # it is also visible at position 1 of the original buffer
2023 * buffer
2024 * # =>
2025 * # #<IO::Buffer 0x00007fc3d31e2d80+4 INTERNAL>
2026 * # 0x00000000 74 6f 73 74 tost
2027 */
2028static VALUE
2029io_buffer_slice(int argc, VALUE *argv, VALUE self)
2030{
2031 rb_check_arity(argc, 0, 2);
2032
2033 size_t offset, length;
2034 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
2035
2036 return rb_io_buffer_slice(buffer, self, offset, length);
2037}
2038
2039VALUE
2040rb_io_buffer_transfer(VALUE self)
2041{
2042 struct rb_io_buffer *buffer = get_io_buffer(self);
2043
2044 if (io_buffer_locked(buffer)) {
2045 rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
2046 }
2047
2048 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
2049 struct rb_io_buffer *transferred;
2050 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
2051
2052 *transferred = *buffer;
2053 io_buffer_zero(buffer);
2054
2055 return instance;
2056}
2057
2058/*
2059 * call-seq: transfer -> new_io_buffer
2060 *
2061 * Transfers ownership of the underlying memory to a new buffer, causing the
2062 * current buffer to become uninitialized.
2063 *
2064 * buffer = IO::Buffer.for('test')
2065 * other = buffer.transfer
2066 * other
2067 * # =>
2068 * # #<IO::Buffer 0x00007f136a15f7b0+4 EXTERNAL READONLY SLICE>
2069 * # 0x00000000 74 65 73 74 test
2070 * buffer
2071 * # =>
2072 * # #<IO::Buffer 0x0000000000000000+0 NULL EXTERNAL READONLY>
2073 * buffer.null?
2074 * # => true
2075 *
2076 * A frozen buffer cannot transfer ownership, as that would leave it
2077 * uninitialized:
2078 *
2079 * buffer = IO::Buffer.for('test').freeze
2080 * buffer.transfer
2081 * # in `transfer': can't modify frozen IO::Buffer (FrozenError)
2082 */
2083static VALUE
2084io_buffer_transfer(VALUE self)
2085{
2086 rb_check_frozen(self);
2087
2088 return rb_io_buffer_transfer(self);
2089}
2090
2091static void
2092io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
2093{
2094 if (size > buffer->size) {
2095 memset((unsigned char*)base+buffer->size, 0, size - buffer->size);
2096 }
2097}
2098
2099static void
2100io_buffer_resize_copy(VALUE self, struct rb_io_buffer *buffer, size_t size)
2101{
2102 // Slow path:
2103 struct rb_io_buffer resized;
2104 enum rb_io_buffer_flags flags = io_flags_for_size(size) | (buffer->flags & RB_IO_BUFFER_READONLY);
2105 io_buffer_initialize(self, &resized, NULL, size, flags, Qnil);
2106
2107 if (buffer->base) {
2108 size_t preserve = buffer->size;
2109 if (preserve > size) preserve = size;
2110 memcpy(resized.base, buffer->base, preserve);
2111
2112 io_buffer_resize_clear(buffer, resized.base, size);
2113 }
2114
2115 io_buffer_release(buffer);
2116 *buffer = resized;
2117}
2118
2119static void
2120io_buffer_resize_slice(struct rb_io_buffer *slice, size_t size)
2121{
2122 struct rb_io_buffer *source = get_io_buffer(slice->source);
2123
2124 if (!io_buffer_validate(source)) {
2125 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
2126 }
2127
2128 if (source->base == NULL || slice->base == NULL) {
2129 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
2130 }
2131
2132 uintptr_t source_address = (uintptr_t)source->base;
2133 uintptr_t slice_address = (uintptr_t)slice->base;
2134
2135 if (slice_address < source_address) {
2136 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
2137 }
2138
2139 uintptr_t offset = slice_address - source_address;
2140
2141 if (offset > source->size) {
2142 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
2143 }
2144
2145 if (size > source->size - (size_t)offset) {
2146 rb_raise(rb_eArgError, "Resized slice exceeds its source buffer!");
2147 }
2148
2149 // Validate the requested range rather than the current range so that
2150 // shrinking a slice can restore its validity after the source shrinks.
2151 slice->size = size;
2152}
2153
2154void
2155rb_io_buffer_resize(VALUE self, size_t size)
2156{
2157 struct rb_io_buffer *buffer = get_io_buffer(self);
2158
2159 if (io_buffer_slice_p(buffer)) {
2160 // Resizing a slice only changes the view, not the locked allocation.
2161 io_buffer_resize_slice(buffer, size);
2162 return;
2163 }
2164
2165 io_buffer_validate_for_reading(buffer);
2166
2167 if (io_buffer_locked(buffer)) {
2168 rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
2169 }
2170
2171 if (buffer->base == NULL) {
2172 io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
2173 return;
2174 }
2175
2176 if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
2177 rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
2178 }
2179
2180 if (size == 0) {
2181 io_buffer_release(buffer);
2182 return;
2183 }
2184
2185#if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
2186 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
2187 void *base = mremap(buffer->base, buffer->size, size, MREMAP_MAYMOVE);
2188
2189 if (base == MAP_FAILED) {
2190 rb_sys_fail("rb_io_buffer_resize:mremap");
2191 }
2192
2193 io_buffer_resize_clear(buffer, base, size);
2194
2195 buffer->base = base;
2196 buffer->size = size;
2197
2198 return;
2199 }
2200#endif
2201
2202 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
2203 void *base = realloc(buffer->base, size);
2204
2205 if (!base) {
2206 rb_sys_fail("rb_io_buffer_resize:realloc");
2207 }
2208
2209 io_buffer_resize_clear(buffer, base, size);
2210
2211 buffer->base = base;
2212 buffer->size = size;
2213
2214 return;
2215 }
2216
2217 io_buffer_resize_copy(self, buffer, size);
2218}
2219
2220/*
2221 * call-seq: resize(new_size) -> self
2222 *
2223 * Resizes a buffer to a +new_size+ bytes, preserving its content.
2224 * Depending on the old and new size, the memory area associated with
2225 * the buffer might be either extended, or rellocated at different
2226 * address with content being copied.
2227 *
2228 * buffer = IO::Buffer.new(4)
2229 * buffer.set_string("test", 0)
2230 * buffer.resize(8) # resize to 8 bytes
2231 * # =>
2232 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
2233 * # 0x00000000 74 65 73 74 00 00 00 00 test....
2234 *
2235 * When the buffer is a slice, resizing changes the size of the view without
2236 * modifying the source buffer or allocating new storage. The resized view
2237 * must remain within the source buffer. Growing the view exposes the existing
2238 * bytes in the source; they are not cleared. Because the source allocation
2239 * does not change, a slice can be resized while its source is locked.
2240 *
2241 * External owning buffers (created with ::for), and locked owning buffers
2242 * cannot be resized. Frozen buffers cannot be resized.
2243 */
2244static VALUE
2245io_buffer_resize(VALUE self, VALUE size)
2246{
2247 rb_check_frozen(self);
2248
2249 rb_io_buffer_resize(self, io_buffer_extract_size(size));
2250
2251 return self;
2252}
2253
2254/*
2255 * call-seq: <=>(other) -> integer
2256 *
2257 * Returns a negative integer, zero, or a positive integer if the receiver is
2258 * less than, equal to, or greater than +other+, respectively.
2259 *
2260 * Buffers are compared by size first, and if the sizes are equal, by the exact
2261 * contents of the memory they are referencing using +memcmp+. Only the sign of
2262 * the returned integer is meaningful; the result of +memcmp+ is returned as is.
2263 *
2264 * IO::Buffer.for("abc") <=> IO::Buffer.for("abc") # => 0
2265 * IO::Buffer.for("abc") <=> IO::Buffer.for("ab") # => 1
2266 * IO::Buffer.for("abc") <=> IO::Buffer.for("abd") # => -1
2267 */
2268static VALUE
2269rb_io_buffer_compare(VALUE self, VALUE other)
2270{
2271 const void *ptr1, *ptr2;
2272 size_t size1, size2;
2273
2274 rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
2275 rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
2276
2277 if (size1 < size2) {
2278 return RB_INT2NUM(-1);
2279 }
2280
2281 if (size1 > size2) {
2282 return RB_INT2NUM(1);
2283 }
2284
2285 if (size1 == 0) {
2286 return RB_INT2NUM(0);
2287 }
2288
2289 RUBY_ASSERT(ptr1 != NULL);
2290 RUBY_ASSERT(ptr2 != NULL);
2291 return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
2292}
2293
2294static void
2295io_buffer_validate_type(size_t size, size_t offset, size_t extend)
2296{
2297 if (size_sum_is_bigger_than(offset, extend, size)) {
2298 rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
2299 }
2300}
2301
2302// Lower case: little endian.
2303// Upper case: big endian (network endian).
2304//
2305// :U8 | unsigned 8-bit integer.
2306// :S8 | signed 8-bit integer.
2307//
2308// :u16, :U16 | unsigned 16-bit integer.
2309// :s16, :S16 | signed 16-bit integer.
2310//
2311// :u32, :U32 | unsigned 32-bit integer.
2312// :s32, :S32 | signed 32-bit integer.
2313//
2314// :u64, :U64 | unsigned 64-bit integer.
2315// :s64, :S64 | signed 64-bit integer.
2316//
2317// :u128, :U128 | unsigned 128-bit integer.
2318// :s128, :S128 | signed 128-bit integer.
2319//
2320// :f32, :F32 | 32-bit floating point number.
2321// :f64, :F64 | 64-bit floating point number.
2322
2323#define ruby_swap8(value) value
2324
2325union swapf32 {
2326 uint32_t integral;
2327 float value;
2328};
2329
2330static float
2331ruby_swapf32(float value)
2332{
2333 union swapf32 swap = {.value = value};
2334 swap.integral = ruby_swap32(swap.integral);
2335 return swap.value;
2336}
2337
2338union swapf64 {
2339 uint64_t integral;
2340 double value;
2341};
2342
2343static double
2344ruby_swapf64(double value)
2345{
2346 union swapf64 swap = {.value = value};
2347 swap.integral = ruby_swap64(swap.integral);
2348 return swap.value;
2349}
2350
2351// Structures and conversion functions are now in numeric.h/numeric.c
2352// Unified swap function for 128-bit integers (works with both signed and unsigned)
2353// Since both rb_uint128_t and rb_int128_t have the same memory layout,
2354// we can use a union to make the swap function work with both types
2355static inline rb_uint128_t
2356ruby_swap128_uint(rb_uint128_t x)
2357{
2358 rb_uint128_t result;
2359#ifdef HAVE_UINT128_T
2360#if __has_builtin(__builtin_bswap128)
2361 result.value = __builtin_bswap128(x.value);
2362#else
2363 // Manual byte swap for 128-bit integers
2364 uint64_t low = (uint64_t)x.value;
2365 uint64_t high = (uint64_t)(x.value >> 64);
2366 low = ruby_swap64(low);
2367 high = ruby_swap64(high);
2368 result.value = ((uint128_t)low << 64) | high;
2369#endif
2370#else
2371 // Fallback swap function using two 64-bit integers
2372 // For big-endian data on little-endian host (or vice versa):
2373 // 1. Swap bytes within each 64-bit part
2374 // 2. Swap the order of the parts (since big-endian stores high first, little-endian stores low first)
2375 result.parts.low = ruby_swap64(x.parts.high);
2376 result.parts.high = ruby_swap64(x.parts.low);
2377#endif
2378 return result;
2379}
2380
2381static inline rb_int128_t
2382ruby_swap128_int(rb_int128_t x)
2383{
2384 union uint128_int128_conversion conversion = {
2385 .int128 = x
2386 };
2387 conversion.uint128 = ruby_swap128_uint(conversion.uint128);
2388 return conversion.int128;
2389}
2390
2391#define IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, offset, type) \
2392 (io_buffer_get_bytes_for_writing(buffer, &(base), &(size)), \
2393 io_buffer_validate_type(size, offset, sizeof(type)))
2394
2395#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
2396static ID RB_IO_BUFFER_DATA_TYPE_##name; \
2397\
2398static VALUE \
2399io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
2400{ \
2401 io_buffer_validate_type(size, *offset, sizeof(type)); \
2402 type value; \
2403 memcpy(&value, (char*)base + *offset, sizeof(type)); \
2404 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
2405 *offset += sizeof(type); \
2406 return wrap(value); \
2407} \
2408\
2409static void \
2410io_buffer_write_##name(struct rb_io_buffer* buffer, size_t *offset, VALUE _value) \
2411{ \
2412 void* base; size_t size; \
2413 IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
2414 type value = unwrap(_value); \
2415 IO_BUFFER_VALIDATE_TYPE_FOR_WRITING(buffer, base, size, *offset, type); \
2416 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
2417 memcpy((char*)base + *offset, &value, sizeof(type)); \
2418 *offset += sizeof(type); \
2419} \
2420\
2421enum { \
2422 RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
2423};
2424
2425IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
2426IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
2427
2428IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
2429IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
2430IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
2431IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
2432
2433IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
2434IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
2435IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
2436IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
2437
2438IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
2439IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
2440IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
2441IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
2442
2443IO_BUFFER_DECLARE_TYPE(u128, rb_uint128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
2444IO_BUFFER_DECLARE_TYPE(U128, rb_uint128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
2445IO_BUFFER_DECLARE_TYPE(s128, rb_int128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
2446IO_BUFFER_DECLARE_TYPE(S128, rb_int128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
2447
2448IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
2449IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
2450IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
2451IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
2452#undef IO_BUFFER_DECLARE_TYPE
2453
2454static inline size_t
2455io_buffer_buffer_type_size(ID buffer_type)
2456{
2457#define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
2458 IO_BUFFER_DATA_TYPE_SIZE(U8)
2459 IO_BUFFER_DATA_TYPE_SIZE(S8)
2460 IO_BUFFER_DATA_TYPE_SIZE(u16)
2461 IO_BUFFER_DATA_TYPE_SIZE(U16)
2462 IO_BUFFER_DATA_TYPE_SIZE(s16)
2463 IO_BUFFER_DATA_TYPE_SIZE(S16)
2464 IO_BUFFER_DATA_TYPE_SIZE(u32)
2465 IO_BUFFER_DATA_TYPE_SIZE(U32)
2466 IO_BUFFER_DATA_TYPE_SIZE(s32)
2467 IO_BUFFER_DATA_TYPE_SIZE(S32)
2468 IO_BUFFER_DATA_TYPE_SIZE(u64)
2469 IO_BUFFER_DATA_TYPE_SIZE(U64)
2470 IO_BUFFER_DATA_TYPE_SIZE(s64)
2471 IO_BUFFER_DATA_TYPE_SIZE(S64)
2472 IO_BUFFER_DATA_TYPE_SIZE(u128)
2473 IO_BUFFER_DATA_TYPE_SIZE(U128)
2474 IO_BUFFER_DATA_TYPE_SIZE(s128)
2475 IO_BUFFER_DATA_TYPE_SIZE(S128)
2476 IO_BUFFER_DATA_TYPE_SIZE(f32)
2477 IO_BUFFER_DATA_TYPE_SIZE(F32)
2478 IO_BUFFER_DATA_TYPE_SIZE(f64)
2479 IO_BUFFER_DATA_TYPE_SIZE(F64)
2480#undef IO_BUFFER_DATA_TYPE_SIZE
2481
2482 rb_raise(rb_eArgError, "Invalid type name!");
2483}
2484
2485static inline ID
2486io_buffer_type_id(VALUE name)
2487{
2488 Check_Type(name, T_SYMBOL);
2489 if (!STATIC_SYM_P(name)) return 0;
2490 return rb_sym2id(name);
2491}
2492#define TYPE_ID(name) io_buffer_type_id(name)
2493
2494/*
2495 * call-seq:
2496 * size_of(buffer_type) -> byte size
2497 * size_of(array of buffer_type) -> byte size
2498 *
2499 * Returns the size of the given buffer type(s) in bytes.
2500 *
2501 * IO::Buffer.size_of(:u32) # => 4
2502 * IO::Buffer.size_of([:u32, :u32]) # => 8
2503 */
2504static VALUE
2505io_buffer_size_of(VALUE klass, VALUE buffer_type)
2506{
2507 if (RB_TYPE_P(buffer_type, T_ARRAY)) {
2508 size_t total = 0;
2509 for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
2510 total += io_buffer_buffer_type_size(TYPE_ID(RARRAY_AREF(buffer_type, i)));
2511 }
2512 return SIZET2NUM(total);
2513 }
2514 else {
2515 return SIZET2NUM(io_buffer_buffer_type_size(TYPE_ID(buffer_type)));
2516 }
2517}
2518
2519static inline VALUE
2520rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *offset)
2521{
2522#define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
2523 IO_BUFFER_GET_VALUE(U8)
2524 IO_BUFFER_GET_VALUE(S8)
2525
2526 IO_BUFFER_GET_VALUE(u16)
2527 IO_BUFFER_GET_VALUE(U16)
2528 IO_BUFFER_GET_VALUE(s16)
2529 IO_BUFFER_GET_VALUE(S16)
2530
2531 IO_BUFFER_GET_VALUE(u32)
2532 IO_BUFFER_GET_VALUE(U32)
2533 IO_BUFFER_GET_VALUE(s32)
2534 IO_BUFFER_GET_VALUE(S32)
2535
2536 IO_BUFFER_GET_VALUE(u64)
2537 IO_BUFFER_GET_VALUE(U64)
2538 IO_BUFFER_GET_VALUE(s64)
2539 IO_BUFFER_GET_VALUE(S64)
2540
2541 IO_BUFFER_GET_VALUE(u128)
2542 IO_BUFFER_GET_VALUE(U128)
2543 IO_BUFFER_GET_VALUE(s128)
2544 IO_BUFFER_GET_VALUE(S128)
2545
2546 IO_BUFFER_GET_VALUE(f32)
2547 IO_BUFFER_GET_VALUE(F32)
2548 IO_BUFFER_GET_VALUE(f64)
2549 IO_BUFFER_GET_VALUE(F64)
2550#undef IO_BUFFER_GET_VALUE
2551
2552 rb_raise(rb_eArgError, "Invalid type name!");
2553}
2554
2555/*
2556 * call-seq: get_value(buffer_type, offset) -> numeric
2557 *
2558 * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
2559 * of symbols:
2560 *
2561 * * +:U8+: unsigned integer, 1 byte
2562 * * +:S8+: signed integer, 1 byte
2563 * * +:u16+: unsigned integer, 2 bytes, little-endian
2564 * * +:U16+: unsigned integer, 2 bytes, big-endian
2565 * * +:s16+: signed integer, 2 bytes, little-endian
2566 * * +:S16+: signed integer, 2 bytes, big-endian
2567 * * +:u32+: unsigned integer, 4 bytes, little-endian
2568 * * +:U32+: unsigned integer, 4 bytes, big-endian
2569 * * +:s32+: signed integer, 4 bytes, little-endian
2570 * * +:S32+: signed integer, 4 bytes, big-endian
2571 * * +:u64+: unsigned integer, 8 bytes, little-endian
2572 * * +:U64+: unsigned integer, 8 bytes, big-endian
2573 * * +:s64+: signed integer, 8 bytes, little-endian
2574 * * +:S64+: signed integer, 8 bytes, big-endian
2575 * * +:u128+: unsigned integer, 16 bytes, little-endian
2576 * * +:U128+: unsigned integer, 16 bytes, big-endian
2577 * * +:s128+: signed integer, 16 bytes, little-endian
2578 * * +:S128+: signed integer, 16 bytes, big-endian
2579 * * +:f32+: float, 4 bytes, little-endian
2580 * * +:F32+: float, 4 bytes, big-endian
2581 * * +:f64+: double, 8 bytes, little-endian
2582 * * +:F64+: double, 8 bytes, big-endian
2583 *
2584 * A buffer type refers specifically to the type of binary buffer that is stored
2585 * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
2586 * integer in little-endian format.
2587 *
2588 * string = [1.5].pack('f')
2589 * # => "\x00\x00\xC0?"
2590 * IO::Buffer.for(string).get_value(:f32, 0)
2591 * # => 1.5
2592 */
2593static VALUE
2594io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
2595{
2596 const void *base;
2597 size_t size;
2598 size_t offset = io_buffer_extract_offset(_offset);
2599
2600 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2601
2602 return rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset);
2603}
2604
2605/*
2606 * call-seq: get_values(buffer_types, offset) -> array
2607 *
2608 * Similar to #get_value, except that it can handle multiple buffer types and
2609 * returns an array of values.
2610 *
2611 * string = [1.5, 2.5].pack('ff')
2612 * IO::Buffer.for(string).get_values([:f32, :f32], 0)
2613 * # => [1.5, 2.5]
2614 */
2615static VALUE
2616io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
2617{
2618 size_t offset = io_buffer_extract_offset(_offset);
2619
2620 const void *base;
2621 size_t size;
2622 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2623
2624 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2625 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2626 }
2627
2628 VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));
2629
2630 for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
2631 VALUE type = rb_ary_entry(buffer_types, i);
2632 VALUE value = rb_io_buffer_get_value(base, size, TYPE_ID(type), &offset);
2633 rb_ary_push(array, value);
2634 }
2635
2636 return array;
2637}
2638
2639// Extract a count argument, which must be a positive integer.
2640// Count is generally considered relative to the number of things.
2641static inline size_t
2642io_buffer_extract_count(VALUE argument)
2643{
2644 if (rb_int_negative_p(argument)) {
2645 rb_raise(rb_eArgError, "Count can't be negative!");
2646 }
2647
2648 return NUM2SIZET(argument);
2649}
2650
2651static inline void
2652io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
2653{
2654 if (argc >= 1) {
2655 *offset = io_buffer_extract_offset(argv[0]);
2656 }
2657 else {
2658 *offset = 0;
2659 }
2660
2661 if (argc >= 2) {
2662 *count = io_buffer_extract_count(argv[1]);
2663 }
2664 else {
2665 if (*offset > size) {
2666 rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
2667 }
2668
2669 *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
2670 }
2671}
2672
2674 VALUE self;
2675 int argc;
2676 VALUE *argv;
2677};
2678
2679static VALUE
2680io_buffer_each_locked(VALUE _arguments)
2681{
2682 struct io_buffer_each_arguments *arguments = (void *)_arguments;
2683 VALUE self = arguments->self;
2684 int argc = arguments->argc;
2685 VALUE *argv = arguments->argv;
2686
2687 const void *base;
2688 size_t size;
2689
2690 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2691
2692 ID buffer_type;
2693 if (argc >= 1) {
2694 buffer_type = TYPE_ID(argv[0]);
2695 }
2696 else {
2697 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2698 }
2699
2700 size_t offset, count;
2701 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2702
2703 for (size_t i = 0; i < count; i++) {
2704 size_t current_offset = offset;
2705 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2706 rb_yield_values(2, SIZET2NUM(current_offset), value);
2707 }
2708
2709 return self;
2710}
2711
2712/*
2713 * call-seq:
2714 * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
2715 * each(buffer_type, [offset, [count]]) -> enumerator
2716 *
2717 * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
2718 * from +offset+.
2719 *
2720 * If +count+ is given, only +count+ values will be yielded.
2721 *
2722 * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
2723 * puts "#{offset}: #{value}"
2724 * end
2725 * # 2: 108
2726 * # 3: 108
2727 */
2728static VALUE
2729io_buffer_each(int argc, VALUE *argv, VALUE self)
2730{
2731 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2732
2733 struct io_buffer_each_arguments arguments = {
2734 .self = self,
2735 .argc = argc,
2736 .argv = argv,
2737 };
2738
2739 rb_io_buffer_lock(self);
2740 return rb_ensure(io_buffer_each_locked, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
2741}
2742
2743/*
2744 * call-seq: values(buffer_type, [offset, [count]]) -> array
2745 *
2746 * Returns an array of values of +buffer_type+ starting from +offset+.
2747 *
2748 * If +count+ is given, only +count+ values will be returned.
2749 *
2750 * IO::Buffer.for("Hello World").values(:U8, 2, 2)
2751 * # => [108, 108]
2752 */
2753static VALUE
2754io_buffer_values(int argc, VALUE *argv, VALUE self)
2755{
2756 const void *base;
2757 size_t size;
2758
2759 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2760
2761 ID buffer_type;
2762 if (argc >= 1) {
2763 buffer_type = TYPE_ID(argv[0]);
2764 }
2765 else {
2766 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2767 }
2768
2769 size_t offset, count;
2770 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2771
2772 VALUE array = rb_ary_new_capa(count);
2773
2774 for (size_t i = 0; i < count; i++) {
2775 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2776 rb_ary_push(array, value);
2777 }
2778
2779 return array;
2780}
2781
2782static VALUE
2783io_buffer_each_byte_locked(VALUE _arguments)
2784{
2785 struct io_buffer_each_arguments *arguments = (void *)_arguments;
2786 VALUE self = arguments->self;
2787 int argc = arguments->argc;
2788 VALUE *argv = arguments->argv;
2789
2790 const void *base;
2791 size_t size;
2792
2793 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2794
2795 size_t offset, count;
2796 io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc, argv, &offset, &count);
2797
2798 if (size_sum_is_bigger_than(offset, count, size)) {
2799 rb_raise(rb_eArgError, "Specified offset+count is bigger than the buffer size!");
2800 }
2801
2802 for (size_t i = 0; i < count; i++) {
2803 unsigned char *value = (unsigned char *)base + i + offset;
2804 rb_yield(RB_INT2FIX(*value));
2805 }
2806
2807 return self;
2808}
2809
2810/*
2811 * call-seq:
2812 * each_byte([offset, [count]]) {|byte| ...} -> self
2813 * each_byte([offset, [count]]) -> enumerator
2814 *
2815 * Iterates over the buffer, yielding each byte starting from +offset+.
2816 *
2817 * If +count+ is given, only +count+ bytes will be yielded.
2818 *
2819 * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
2820 * puts "#{offset}: #{byte}"
2821 * end
2822 * # 2: 108
2823 * # 3: 108
2824 */
2825static VALUE
2826io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
2827{
2828 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2829
2830 struct io_buffer_each_arguments arguments = {
2831 .self = self,
2832 .argc = argc,
2833 .argv = argv,
2834 };
2835
2836 rb_io_buffer_lock(self);
2837 return rb_ensure(io_buffer_each_byte_locked, (VALUE)&arguments, rb_io_buffer_locked_ensure, self);
2838}
2839
2840static inline void
2841rb_io_buffer_set_value(struct rb_io_buffer *buffer, VALUE buffer_type, size_t *offset, VALUE value)
2842{
2843 ID type = TYPE_ID(buffer_type);
2844#define IO_BUFFER_SET_VALUE(name) if (type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(buffer, offset, value); return;}
2845 IO_BUFFER_SET_VALUE(U8);
2846 IO_BUFFER_SET_VALUE(S8);
2847
2848 IO_BUFFER_SET_VALUE(u16);
2849 IO_BUFFER_SET_VALUE(U16);
2850 IO_BUFFER_SET_VALUE(s16);
2851 IO_BUFFER_SET_VALUE(S16);
2852
2853 IO_BUFFER_SET_VALUE(u32);
2854 IO_BUFFER_SET_VALUE(U32);
2855 IO_BUFFER_SET_VALUE(s32);
2856 IO_BUFFER_SET_VALUE(S32);
2857
2858 IO_BUFFER_SET_VALUE(u64);
2859 IO_BUFFER_SET_VALUE(U64);
2860 IO_BUFFER_SET_VALUE(s64);
2861 IO_BUFFER_SET_VALUE(S64);
2862
2863 IO_BUFFER_SET_VALUE(u128);
2864 IO_BUFFER_SET_VALUE(U128);
2865 IO_BUFFER_SET_VALUE(s128);
2866 IO_BUFFER_SET_VALUE(S128);
2867
2868 IO_BUFFER_SET_VALUE(f32);
2869 IO_BUFFER_SET_VALUE(F32);
2870 IO_BUFFER_SET_VALUE(f64);
2871 IO_BUFFER_SET_VALUE(F64);
2872#undef IO_BUFFER_SET_VALUE
2873
2874 rb_raise(rb_eArgError, "Invalid type name!");
2875}
2876
2878 struct rb_io_buffer *buffer;
2879 size_t offset;
2880 VALUE type, value;
2881};
2882
2883/*
2884 * call-seq: set_value(type, offset, value) -> offset
2885 *
2886 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
2887 * symbols described in #get_value. Returns the offset just after the written
2888 * value.
2889 *
2890 * buffer = IO::Buffer.new(8)
2891 * # =>
2892 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2893 * # 0x00000000 00 00 00 00 00 00 00 00
2894 *
2895 * buffer.set_value(:U8, 1, 111)
2896 * # => 2
2897 *
2898 * buffer
2899 * # =>
2900 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2901 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
2902 *
2903 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
2904 *
2905 * buffer = IO::Buffer.new(8)
2906 * buffer.set_value(:U32, 0, 2.5)
2907 *
2908 * buffer
2909 * # =>
2910 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2911 * # 0x00000000 00 00 00 02 00 00 00 00
2912 * # ^^ the same as if we'd pass just integer 2
2913 */
2914static VALUE
2915io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
2916{
2917 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
2918 size_t offset = io_buffer_extract_offset(_offset);
2919 rb_io_buffer_set_value(buffer, type, &offset, value);
2920 return SIZET2NUM(offset);
2921}
2922
2923/*
2924 * call-seq: set_values(buffer_types, offset, values) -> offset
2925 *
2926 * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
2927 * should be an array of symbols as described in #get_value. +values+ should
2928 * be an array of values to write. Returns the offset just after the last
2929 * written value.
2930 *
2931 * buffer = IO::Buffer.new(8)
2932 * buffer.set_values([:U8, :U16], 0, [1, 2])
2933 * # => 3
2934 * buffer
2935 * # =>
2936 * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
2937 * # 0x00000000 01 00 02 00 00 00 00 00 ........
2938 */
2939static VALUE
2940io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
2941{
2942 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
2943
2944 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2945 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2946 }
2947
2948 size_t offset = io_buffer_extract_offset(_offset);
2949
2950 if (!RB_TYPE_P(values, T_ARRAY)) {
2951 rb_raise(rb_eArgError, "Argument values should be an array!");
2952 }
2953
2954 if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
2955 rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
2956 }
2957
2958 for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
2959 VALUE type = rb_ary_entry(buffer_types, i);
2960 VALUE value = rb_ary_entry(values, i);
2961 rb_io_buffer_set_value(buffer, type, &offset, value);
2962 }
2963
2964 return SIZET2NUM(offset);
2965}
2966
2967static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
2968
2970 unsigned char * destination;
2971 const unsigned char * source;
2972 size_t length;
2973};
2974
2975static void *
2976io_buffer_memmove_blocking(void *data)
2977{
2978 struct io_buffer_memmove_arguments *arguments = (struct io_buffer_memmove_arguments *)data;
2979
2980 memmove(arguments->destination, arguments->source, arguments->length);
2981
2982 return NULL;
2983}
2984
2985static void
2986io_buffer_memmove_unblock(void *data)
2987{
2988 // No safe way to interrupt.
2989}
2990
2991static void
2992io_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)
2993{
2994 if (size_sum_is_bigger_than(offset, length, size)) {
2995 rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
2996 }
2997
2998 if (size_sum_is_bigger_than(source_offset, length, source_size)) {
2999 rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
3000 }
3001
3002 if (length == 0) return;
3003
3004 RUBY_ASSERT(base != NULL);
3005 RUBY_ASSERT(source_base != NULL);
3006 struct io_buffer_memmove_arguments arguments = {
3007 .destination = (unsigned char*)base+offset,
3008 .source = (unsigned char*)source_base+source_offset,
3009 .length = length
3010 };
3011
3012 if (arguments.length >= IO_BUFFER_BLOCKING_SIZE) {
3013 rb_nogvl(io_buffer_memmove_blocking, &arguments, io_buffer_memmove_unblock, &arguments, RB_NOGVL_OFFLOAD_SAFE);
3014 } else if (arguments.length != 0) {
3015 memmove(arguments.destination, arguments.source, arguments.length);
3016 }
3017}
3018
3019static void
3020io_buffer_extract_copy_arguments(size_t source_size, int argc, VALUE *argv, size_t *offset, size_t *length, size_t *source_offset)
3021{
3022 // The offset we copy into the buffer:
3023 if (argc >= 1) {
3024 *offset = io_buffer_extract_offset(argv[0]);
3025 }
3026 else {
3027 *offset = 0;
3028 }
3029
3030 // The offset we start from within the string:
3031 if (argc >= 3) {
3032 *source_offset = io_buffer_extract_offset(argv[2]);
3033
3034 if (*source_offset > source_size) {
3035 rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
3036 }
3037 }
3038 else {
3039 *source_offset = 0;
3040 }
3041
3042 // The length we are going to copy:
3043 if (argc >= 2 && !RB_NIL_P(argv[1])) {
3044 *length = io_buffer_extract_length(argv[1]);
3045 }
3046 else {
3047 // Default to the source offset -> source size:
3048 *length = source_size - *source_offset;
3049 }
3050}
3051
3052// (offset, length, source_offset) -> length
3053static VALUE
3054io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
3055{
3056 size_t offset, length, source_offset;
3057 io_buffer_extract_copy_arguments(source_size, argc, argv, &offset, &length, &source_offset);
3058
3059 void *base;
3060 size_t size;
3061 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3062
3063 if (length >= IO_BUFFER_BLOCKING_SIZE) {
3064 io_buffer_lock(buffer);
3065 }
3066
3067 io_buffer_memmove(base, size, offset, source_base, source_offset, source_size, length);
3068
3069 if (length >= IO_BUFFER_BLOCKING_SIZE) {
3070 io_buffer_unlock(buffer);
3071 }
3072
3073 return SIZET2NUM(length);
3074}
3075
3077 VALUE destination;
3078 const void *source_base;
3079 size_t source_size;
3080 int argc;
3081 VALUE *argv;
3082};
3083
3084// This is the innermost callback for IO::Buffer#copy. At this point the source
3085// is locked for reading and the destination is locked for writing, so both
3086// pointers and sizes remain valid while arguments are extracted, ranges are
3087// validated, and memmove potentially releases the GVL.
3088static VALUE
3089io_buffer_copy_to(void *base, size_t size, VALUE _arguments)
3090{
3091 struct io_buffer_copy_arguments *arguments = (void *)_arguments;
3092
3093 size_t offset, length, source_offset;
3094 io_buffer_extract_copy_arguments(arguments->source_size, arguments->argc, arguments->argv, &offset, &length, &source_offset);
3095
3096 io_buffer_memmove(base, size, offset, arguments->source_base, source_offset, arguments->source_size, length);
3097
3098 return SIZET2NUM(length);
3099}
3100
3101// This callback runs while the source is locked for reading. Retain its bytes
3102// in the callback arguments, then enter the destination's writable scope. The
3103// source scope remains active until that nested scope returns.
3104static VALUE
3105io_buffer_copy_from_readable(const void *base, size_t size, VALUE _arguments)
3106{
3107 struct io_buffer_copy_arguments *arguments = (void *)_arguments;
3108
3109 arguments->source_base = base;
3110 arguments->source_size = size;
3111
3112 return rb_io_buffer_locked_for_writing(arguments->destination, io_buffer_copy_to, _arguments);
3113}
3114
3115static VALUE
3116io_buffer_initialize_copy_from(const void *base, size_t size, VALUE self)
3117{
3118 struct rb_io_buffer *buffer = get_io_buffer(self);
3119
3120 io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
3121
3122 struct io_buffer_copy_arguments arguments = {
3123 .destination = self,
3124 .source_base = base,
3125 .source_size = size,
3126 .argc = 0,
3127 .argv = NULL,
3128 };
3129
3130 // The source remains locked by the outer readable scope while the newly
3131 // initialized destination is locked and populated by io_buffer_copy_to.
3132 return rb_io_buffer_locked_for_writing(self, io_buffer_copy_to, (VALUE)&arguments);
3133}
3134
3135/*
3136 * call-seq:
3137 * dup -> io_buffer
3138 * clone -> io_buffer
3139 *
3140 * Make an internal copy of the source buffer. Updates to the copy will not
3141 * affect the source buffer.
3142 *
3143 * source = IO::Buffer.for("Hello World")
3144 * # =>
3145 * # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
3146 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
3147 * buffer = source.dup
3148 * # =>
3149 * # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
3150 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
3151 */
3152static VALUE
3153rb_io_buffer_initialize_copy(VALUE self, VALUE source)
3154{
3155 return rb_io_buffer_locked_for_reading(source, io_buffer_initialize_copy_from, self);
3156}
3157
3158/*
3159 * call-seq:
3160 * copy(source, [offset, [length, [source_offset]]]) -> size
3161 *
3162 * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
3163 * using +memmove+. For copying String instances, see #set_string.
3164 *
3165 * buffer = IO::Buffer.new(32)
3166 * # =>
3167 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
3168 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
3169 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
3170 *
3171 * buffer.copy(IO::Buffer.for("test"), 8)
3172 * # => 4 -- size of buffer copied
3173 * buffer
3174 * # =>
3175 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
3176 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
3177 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
3178 *
3179 * #copy can be used to put buffer into strings associated with buffer:
3180 *
3181 * string = "data: "
3182 * # => "data: "
3183 * buffer = IO::Buffer.for(string) do |buffer|
3184 * buffer.copy(IO::Buffer.for("test"), 5)
3185 * end
3186 * # => 4
3187 * string
3188 * # => "data:test"
3189 *
3190 * Attempt to copy into a read-only buffer will fail:
3191 *
3192 * File.write('test.txt', 'test')
3193 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
3194 * buffer.copy(IO::Buffer.for("test"), 8)
3195 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
3196 *
3197 * See ::map for details of creation of mutable file mappings, this will
3198 * work:
3199 *
3200 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
3201 * buffer.copy(IO::Buffer.for("boom"), 0)
3202 * # => 4
3203 * File.read('test.txt')
3204 * # => "boom"
3205 *
3206 * Attempt to copy the buffer which will need place outside of buffer's
3207 * bounds will fail:
3208 *
3209 * buffer = IO::Buffer.new(2)
3210 * buffer.copy(IO::Buffer.for('test'), 0)
3211 * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
3212 *
3213 * It is safe to copy between memory regions that overlaps each other.
3214 * In such case, the data is copied as if the data was first copied from the source buffer to
3215 * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
3216 *
3217 * buffer = IO::Buffer.new(10)
3218 * buffer.set_string("0123456789")
3219 * buffer.copy(buffer, 3, 7)
3220 * # => 7
3221 * buffer
3222 * # =>
3223 * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
3224 * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
3225 */
3226static VALUE
3227io_buffer_copy(int argc, VALUE *argv, VALUE self)
3228{
3229 rb_check_arity(argc, 1, 4);
3230
3231 VALUE source = argv[0];
3232 struct io_buffer_copy_arguments arguments = {
3233 .destination = self,
3234 .argc = argc-1,
3235 .argv = argv+1,
3236 };
3237
3238 // Lock the source first, then io_buffer_copy_from_readable nests the
3239 // destination lock. The scoped helpers use rb_ensure, so the destination
3240 // is unlocked before the source on both normal and exceptional returns.
3241 // If both buffers share an allocation, its reference-counted lock is
3242 // acquired and released twice.
3243 return rb_io_buffer_locked_for_reading(source, io_buffer_copy_from_readable, (VALUE)&arguments);
3244}
3245
3246/*
3247 * call-seq: get_string([offset, [length, [encoding]]]) -> string
3248 *
3249 * Read a chunk or all of the buffer into a string, in the specified
3250 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
3251 *
3252 * buffer = IO::Buffer.for('test')
3253 * buffer.get_string
3254 * # => "test"
3255 * buffer.get_string(2)
3256 * # => "st"
3257 * buffer.get_string(2, 1)
3258 * # => "s"
3259 */
3260static VALUE
3261io_buffer_get_string(int argc, VALUE *argv, VALUE self)
3262{
3263 rb_check_arity(argc, 0, 3);
3264
3265 size_t offset, length;
3266 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
3267
3268 rb_encoding *encoding;
3269 if (argc >= 3) {
3270 encoding = rb_find_encoding(argv[2]);
3271 }
3272 else {
3273 encoding = rb_ascii8bit_encoding();
3274 }
3275
3276 const void *base;
3277 size_t size;
3278 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3279
3280 io_buffer_validate_range(buffer, offset, length);
3281
3282 const char *data = base ? (const char*)base + offset : NULL;
3283
3284 return rb_enc_str_new(data, length, encoding);
3285}
3286
3287/*
3288 * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
3289 *
3290 * Efficiently copy from a source String into the buffer, at +offset+ using
3291 * +memmove+.
3292 *
3293 * buf = IO::Buffer.new(8)
3294 * # =>
3295 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
3296 * # 0x00000000 00 00 00 00 00 00 00 00 ........
3297 *
3298 * # set buffer starting from offset 1, take 2 bytes starting from string's
3299 * # second
3300 * buf.set_string('test', 1, 2, 1)
3301 * # => 2
3302 * buf
3303 * # =>
3304 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
3305 * # 0x00000000 00 65 73 00 00 00 00 00 .es.....
3306 *
3307 * See also #copy for examples of how buffer writing might be used for changing
3308 * associated strings and files.
3309 */
3310static VALUE
3311io_buffer_set_string(int argc, VALUE *argv, VALUE self)
3312{
3313 rb_check_arity(argc, 1, 4);
3314
3315 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3316
3317 VALUE string = rb_str_to_str(argv[0]);
3318
3319 const void *source_base = RSTRING_PTR(string);
3320 size_t source_size = RSTRING_LEN(string);
3321
3322 VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
3323 RB_GC_GUARD(string);
3324 return result;
3325}
3326
3327void
3328rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
3329{
3330 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3331
3332 void *base;
3333 size_t size;
3334 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3335
3336 io_buffer_validate_range(buffer, offset, length);
3337
3338 if (length == 0) return;
3339
3340 RUBY_ASSERT(base != NULL);
3341 memset((char*)base + offset, value, length);
3342}
3343
3344/*
3345 * call-seq: clear(value = 0, [offset, [length]]) -> self
3346 *
3347 * Fill buffer with +value+, starting with +offset+ and going for +length+
3348 * bytes.
3349 *
3350 * buffer = IO::Buffer.for('test').dup
3351 * # =>
3352 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3353 * # 0x00000000 74 65 73 74 test
3354 *
3355 * buffer.clear
3356 * # =>
3357 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3358 * # 0x00000000 00 00 00 00 ....
3359 *
3360 * buf.clear(1) # fill with 1
3361 * # =>
3362 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3363 * # 0x00000000 01 01 01 01 ....
3364 *
3365 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
3366 * # =>
3367 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3368 * # 0x00000000 01 02 02 01 ....
3369 *
3370 * buffer.clear(2, 1) # fill with 2, starting from offset 1
3371 * # =>
3372 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3373 * # 0x00000000 01 02 02 02 ....
3374 */
3375static VALUE
3376io_buffer_clear(int argc, VALUE *argv, VALUE self)
3377{
3378 rb_check_arity(argc, 0, 3);
3379
3380 uint8_t value = 0;
3381 if (argc >= 1) {
3382 value = NUM2UINT(argv[0]);
3383 }
3384
3385 size_t offset, length;
3386 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3387
3388 rb_io_buffer_clear(self, value, offset, length);
3389
3390 return self;
3391}
3392
3393static size_t
3394io_buffer_default_size(size_t page_size)
3395{
3396 // Platform agnostic default size, based on empirical performance observation:
3397 const size_t platform_agnostic_default_size = 64*1024;
3398
3399 // Allow user to specify custom default buffer size:
3400 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
3401 if (default_size) {
3402 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
3403 int value = atoi(default_size);
3404
3405 // assuming sizeof(int) <= sizeof(size_t)
3406 if (value > 0) {
3407 return value;
3408 }
3409 }
3410
3411 if (platform_agnostic_default_size < page_size) {
3412 return page_size;
3413 }
3414
3415 return platform_agnostic_default_size;
3416}
3417
3419 struct rb_io *io;
3420 struct rb_io_buffer *buffer;
3421 rb_blocking_function_t *function;
3422 void *data;
3423};
3424
3425static VALUE
3426io_buffer_blocking_region_begin(VALUE _argument)
3427{
3428 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
3429
3430 return rb_io_blocking_region(argument->io, argument->function, argument->data);
3431}
3432
3433static VALUE
3434io_buffer_blocking_region_ensure(VALUE _argument)
3435{
3436 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
3437
3438 io_buffer_unlock(argument->buffer);
3439
3440 return Qnil;
3441}
3442
3443static VALUE
3444io_buffer_blocking_region(VALUE io, struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data)
3445{
3446 struct rb_io *ioptr;
3447 RB_IO_POINTER(io, ioptr);
3448
3449 struct io_buffer_blocking_region_argument argument = {
3450 .io = ioptr,
3451 .buffer = buffer,
3452 .function = function,
3453 .data = data,
3454 };
3455
3456 // The buffer should be locked for the duration of the blocking region. We
3457 // always acquire our own reference so another operation cannot release the
3458 // allocation while this operation is still using it:
3459 io_buffer_lock(buffer);
3460
3461 return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
3462}
3463
3465 // The file descriptor to read from:
3466 int descriptor;
3467 // The base pointer to read into:
3468 char *base;
3469 // The maximum number of bytes to read:
3470 size_t length;
3471};
3472
3473static VALUE
3474io_buffer_read_internal(void *_argument)
3475{
3476 struct io_buffer_read_internal_argument *argument = _argument;
3477 ssize_t result = read(argument->descriptor, argument->base, argument->length);
3478
3479 return rb_fiber_scheduler_io_result(result, errno);
3480}
3481
3482VALUE
3483rb_io_buffer_read(VALUE self, VALUE io, size_t offset, size_t length)
3484{
3485 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3486
3487 io = rb_io_get_io(io);
3488
3489 io_buffer_validate_range(buffer, offset, length);
3490
3491 if (length == 0) return SIZET2NUM(0);
3492
3493 VALUE scheduler = rb_fiber_scheduler_current();
3494 if (scheduler != Qnil) {
3495 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, offset, length);
3496
3497 if (!UNDEF_P(result)) {
3498 return result;
3499 }
3500 }
3501
3502 void *base;
3503 size_t size;
3504 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3505
3506 RUBY_ASSERT(base != NULL);
3507
3508 struct io_buffer_read_internal_argument argument = {
3509 .descriptor = rb_io_descriptor(io),
3510 .base = (char*)base + offset,
3511 .length = length,
3512 };
3513
3514 return io_buffer_blocking_region(io, buffer, io_buffer_read_internal, &argument);
3515}
3516
3517/*
3518 * call-seq: read(io, [offset, [length]]) -> read length or -errno
3519 *
3520 * Perform one read operation of at most +length+ bytes from +io+ into the
3521 * buffer starting at +offset+. A short read is a normal result. If an error
3522 * occurs, return <tt>-errno</tt>.
3523 *
3524 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3525 * buffer. If +length+ is not given, it defaults to the size of the buffer
3526 * minus the offset. A zero length is a no-op.
3527 *
3528 * IO::Buffer.for('test') do |buffer|
3529 * p buffer
3530 * # =>
3531 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3532 * # 0x00000000 74 65 73 74 test
3533 * buffer.read(File.open('/dev/urandom', 'rb'), 0, 2)
3534 * p buffer
3535 * # =>
3536 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3537 * # 0x00000000 05 35 73 74 .5st
3538 * end
3539 */
3540static VALUE
3541io_buffer_read(int argc, VALUE *argv, VALUE self)
3542{
3543 rb_check_arity(argc, 1, 3);
3544
3545 VALUE io = argv[0];
3546
3547 size_t offset, length;
3548 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3549
3550 return rb_io_buffer_read(self, io, offset, length);
3551}
3552
3554 // The file descriptor to read from:
3555 int descriptor;
3556 // The base pointer to read into:
3557 char *base;
3558 // The maximum number of bytes to read:
3559 size_t length;
3560 // The position to read from:
3561 off_t from;
3562};
3563
3564static VALUE
3565io_buffer_pread_internal(void *_argument)
3566{
3567 struct io_buffer_pread_internal_argument *argument = _argument;
3568 ssize_t result = pread(argument->descriptor, argument->base, argument->length, argument->from);
3569
3570 return rb_fiber_scheduler_io_result(result, errno);
3571}
3572
3573VALUE
3574rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length)
3575{
3576 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3577
3578 io = rb_io_get_io(io);
3579
3580 io_buffer_validate_range(buffer, offset, length);
3581
3582 if (length == 0) return SIZET2NUM(0);
3583
3584 VALUE scheduler = rb_fiber_scheduler_current();
3585 if (scheduler != Qnil) {
3586 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, offset, length);
3587
3588 if (!UNDEF_P(result)) {
3589 return result;
3590 }
3591 }
3592
3593 void *base;
3594 size_t size;
3595 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3596
3597 RUBY_ASSERT(base != NULL);
3598
3599 struct io_buffer_pread_internal_argument argument = {
3600 .descriptor = rb_io_descriptor(io),
3601 .base = (char*)base + offset,
3602 .length = length,
3603 .from = from,
3604 };
3605
3606 return io_buffer_blocking_region(io, buffer, io_buffer_pread_internal, &argument);
3607}
3608
3609/*
3610 * call-seq: pread(io, from, [offset, [length]]) -> read length or -errno
3611 *
3612 * Perform one read operation of at most +length+ bytes from +io+ at +from+
3613 * into the buffer starting at +offset+. A short read is a normal result and
3614 * the IO's current position is not modified. If an error occurs, return
3615 * <tt>-errno</tt>.
3616 *
3617 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3618 * buffer. If +length+ is not given, it defaults to the size of the buffer
3619 * minus the offset. A zero length is a no-op.
3620 *
3621 * IO::Buffer.for('test') do |buffer|
3622 * p buffer
3623 * # =>
3624 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3625 * # 0x00000000 74 65 73 74 test
3626 *
3627 * # take 2 bytes from the beginning of urandom,
3628 * # put them in buffer starting from position 2
3629 * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
3630 * p buffer
3631 * # =>
3632 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3633 * # 0x00000000 05 35 73 74 te.5
3634 * end
3635 */
3636static VALUE
3637io_buffer_pread(int argc, VALUE *argv, VALUE self)
3638{
3639 rb_check_arity(argc, 2, 4);
3640
3641 VALUE io = argv[0];
3642 rb_off_t from = NUM2OFFT(argv[1]);
3643
3644 size_t offset, length;
3645 io_buffer_extract_offset_length(self, argc-2, argv+2, &offset, &length);
3646
3647 return rb_io_buffer_pread(self, io, from, offset, length);
3648}
3649
3651 // The file descriptor to write to:
3652 int descriptor;
3653 // The base pointer to write from:
3654 const char *base;
3655 // The maximum number of bytes to write:
3656 size_t length;
3657};
3658
3659static VALUE
3660io_buffer_write_internal(void *_argument)
3661{
3662 struct io_buffer_write_internal_argument *argument = _argument;
3663 ssize_t result = write(argument->descriptor, argument->base, argument->length);
3664
3665 return rb_fiber_scheduler_io_result(result, errno);
3666}
3667
3668VALUE
3669rb_io_buffer_write(VALUE self, VALUE io, size_t offset, size_t length)
3670{
3672
3673 struct rb_io_buffer *buffer = get_io_buffer(self);
3674 io_buffer_validate_range(buffer, offset, length);
3675
3676 if (length == 0) return SIZET2NUM(0);
3677
3678 VALUE scheduler = rb_fiber_scheduler_current();
3679 if (scheduler != Qnil) {
3680 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, offset, length);
3681
3682 if (!UNDEF_P(result)) {
3683 return result;
3684 }
3685 }
3686
3687 const void *base;
3688 size_t size;
3689 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3690
3691 RUBY_ASSERT(base != NULL);
3692
3693 struct io_buffer_write_internal_argument argument = {
3694 .descriptor = rb_io_descriptor(io),
3695 .base = (const char*)base + offset,
3696 .length = length,
3697 };
3698
3699 return io_buffer_blocking_region(io, buffer, io_buffer_write_internal, &argument);
3700}
3701
3702/*
3703 * call-seq: write(io, [offset, [length]]) -> written length or -errno
3704 *
3705 * Perform one write operation of at most +length+ bytes to +io+ from the
3706 * buffer starting at +offset+. A short write is a normal result. If an error
3707 * occurs, return <tt>-errno</tt>.
3708 *
3709 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3710 * buffer. If +length+ is not given, it defaults to the size of the buffer
3711 * minus the offset. A zero length is a no-op.
3712 *
3713 * out = File.open('output.txt', 'wb')
3714 * IO::Buffer.for('1234567').write(out, 0, 3)
3715 *
3716 * This leads to +123+ being written into <tt>output.txt</tt>
3717 */
3718static VALUE
3719io_buffer_write(int argc, VALUE *argv, VALUE self)
3720{
3721 rb_check_arity(argc, 1, 3);
3722
3723 VALUE io = argv[0];
3724
3725 size_t offset, length;
3726 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3727
3728 return rb_io_buffer_write(self, io, offset, length);
3729}
3730
3732 // The file descriptor to write to:
3733 int descriptor;
3734 // The base pointer to write from:
3735 const char *base;
3736 // The maximum number of bytes to write:
3737 size_t length;
3738 // The position to write to:
3739 off_t from;
3740};
3741
3742static VALUE
3743io_buffer_pwrite_internal(void *_argument)
3744{
3745 struct io_buffer_pwrite_internal_argument *argument = _argument;
3746 ssize_t result = pwrite(argument->descriptor, argument->base, argument->length, argument->from);
3747
3748 return rb_fiber_scheduler_io_result(result, errno);
3749}
3750
3751VALUE
3752rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length)
3753{
3755
3756 struct rb_io_buffer *buffer = get_io_buffer(self);
3757 io_buffer_validate_range(buffer, offset, length);
3758
3759 if (length == 0) return SIZET2NUM(0);
3760
3761 VALUE scheduler = rb_fiber_scheduler_current();
3762 if (scheduler != Qnil) {
3763 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, offset, length);
3764
3765 if (!UNDEF_P(result)) {
3766 return result;
3767 }
3768 }
3769
3770 const void *base;
3771 size_t size;
3772 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3773
3774 RUBY_ASSERT(base != NULL);
3775
3776 struct io_buffer_pwrite_internal_argument argument = {
3777 .descriptor = rb_io_descriptor(io),
3778 .base = (const char*)base + offset,
3779 .length = length,
3780 .from = from,
3781 };
3782
3783 return io_buffer_blocking_region(io, buffer, io_buffer_pwrite_internal, &argument);
3784}
3785
3786/*
3787 * call-seq: pwrite(io, from, [offset, [length]]) -> written length or -errno
3788 *
3789 * Perform one write operation of at most +length+ bytes to +io+ at +from+
3790 * from the buffer starting at +offset+. A short write is a normal result and
3791 * the IO's current position is not modified. If an error occurs, return
3792 * <tt>-errno</tt>.
3793 *
3794 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3795 * buffer. If +length+ is not given, it defaults to the size of the buffer
3796 * minus the offset. A zero length is a no-op.
3797 *
3798 * If the +from+ position is beyond the end of the file, the gap will be
3799 * filled with null (0 value) bytes.
3800 *
3801 * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
3802 * IO::Buffer.for('1234567').pwrite(out, 2, 1, 3)
3803 *
3804 * This leads to +234+ (3 bytes, starting from position 1) being written into
3805 * <tt>output.txt</tt>, starting from file position 2.
3806 */
3807static VALUE
3808io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
3809{
3810 rb_check_arity(argc, 2, 4);
3811
3812 VALUE io = argv[0];
3813 rb_off_t from = NUM2OFFT(argv[1]);
3814
3815 size_t offset, length;
3816 io_buffer_extract_offset_length(self, argc-2, argv+2, &offset, &length);
3817
3818 return rb_io_buffer_pwrite(self, io, from, offset, length);
3819}
3820
3821static inline void
3822io_buffer_check_mask_size(size_t size)
3823{
3824 if (size == 0)
3825 rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
3826}
3827
3828static void
3829memory_and(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3830{
3831 for (size_t offset = 0; offset < size; offset += 1) {
3832 output[offset] = base[offset] & mask[offset % mask_size];
3833 }
3834}
3835
3836/*
3837 * call-seq:
3838 * source & mask -> io_buffer
3839 *
3840 * Generate a new buffer the same size as the source by applying the binary AND
3841 * operation to the source, using the mask, repeating as necessary.
3842 *
3843 * IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
3844 * # =>
3845 * # #<IO::Buffer 0x00005589b2758480+10 INTERNAL>
3846 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3847 */
3848static VALUE
3849io_buffer_and(VALUE self, VALUE mask)
3850{
3851 struct rb_io_buffer *buffer = get_io_buffer(self);
3852
3853 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3854
3855 const void *base;
3856 size_t size;
3857 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3858
3859 const void *mask_base;
3860 size_t mask_size;
3861 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3862
3863 io_buffer_check_mask_size(mask_size);
3864
3865 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3866 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3867
3868 memory_and(output_buffer->base, base, size, mask_base, mask_size);
3869
3870 return output;
3871}
3872
3873static void
3874memory_or(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3875{
3876 for (size_t offset = 0; offset < size; offset += 1) {
3877 output[offset] = base[offset] | mask[offset % mask_size];
3878 }
3879}
3880
3881/*
3882 * call-seq:
3883 * source | mask -> io_buffer
3884 *
3885 * Generate a new buffer the same size as the source by applying the binary OR
3886 * operation to the source, using the mask, repeating as necessary.
3887 *
3888 * IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
3889 * # =>
3890 * # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
3891 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3892 */
3893static VALUE
3894io_buffer_or(VALUE self, VALUE mask)
3895{
3896 struct rb_io_buffer *buffer = get_io_buffer(self);
3897
3898 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3899
3900 const void *base;
3901 size_t size;
3902 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3903
3904 const void *mask_base;
3905 size_t mask_size;
3906 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3907
3908 io_buffer_check_mask_size(mask_size);
3909
3910 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3911 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3912
3913 memory_or(output_buffer->base, base, size, mask_base, mask_size);
3914
3915 return output;
3916}
3917
3918static void
3919memory_xor(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3920{
3921 for (size_t offset = 0; offset < size; offset += 1) {
3922 output[offset] = base[offset] ^ mask[offset % mask_size];
3923 }
3924}
3925
3926/*
3927 * call-seq:
3928 * source ^ mask -> io_buffer
3929 *
3930 * Generate a new buffer the same size as the source by applying the binary XOR
3931 * operation to the source, using the mask, repeating as necessary.
3932 *
3933 * IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
3934 * # =>
3935 * # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
3936 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3937 */
3938static VALUE
3939io_buffer_xor(VALUE self, VALUE mask)
3940{
3941 struct rb_io_buffer *buffer = get_io_buffer(self);
3942
3943 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3944
3945 const void *base;
3946 size_t size;
3947 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3948
3949 const void *mask_base;
3950 size_t mask_size;
3951 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3952
3953 io_buffer_check_mask_size(mask_size);
3954
3955 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3956 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3957
3958 memory_xor(output_buffer->base, base, size, mask_base, mask_size);
3959
3960 return output;
3961}
3962
3963static void
3964memory_not(unsigned char * restrict output, const unsigned char * restrict base, size_t size)
3965{
3966 for (size_t offset = 0; offset < size; offset += 1) {
3967 output[offset] = ~base[offset];
3968 }
3969}
3970
3971/*
3972 * call-seq:
3973 * ~source -> io_buffer
3974 *
3975 * Generate a new buffer the same size as the source by applying the unary NOT
3976 * operation to the source.
3977 *
3978 * ~IO::Buffer.for("1234567890")
3979 * # =>
3980 * # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
3981 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3982 */
3983static VALUE
3984io_buffer_not(VALUE self)
3985{
3986 struct rb_io_buffer *buffer = get_io_buffer(self);
3987
3988 const void *base;
3989 size_t size;
3990 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3991
3992 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3993 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3994
3995 memory_not(output_buffer->base, base, size);
3996
3997 return output;
3998}
3999
4000static inline int
4001io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
4002{
4003 if (a->base > b->base) {
4004 return io_buffer_overlaps(b, a);
4005 }
4006
4007 return (b->base >= a->base) && (b->base < (void*)((unsigned char *)a->base + a->size));
4008}
4009
4010static inline void
4011io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
4012{
4013 if (io_buffer_overlaps(a, b))
4014 rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
4015}
4016
4017static void
4018memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4019{
4020 for (size_t offset = 0; offset < size; offset += 1) {
4021 base[offset] &= mask[offset % mask_size];
4022 }
4023}
4024
4025/*
4026 * call-seq:
4027 * source.and!(mask) -> io_buffer
4028 *
4029 * Modify the source buffer in place by applying the binary AND
4030 * operation to the source, using the mask, repeating as necessary.
4031 *
4032 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4033 * # =>
4034 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
4035 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4036 *
4037 * source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4038 * # =>
4039 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
4040 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
4041 */
4042static VALUE
4043io_buffer_and_inplace(VALUE self, VALUE mask)
4044{
4045 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4046
4047 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4048
4049 io_buffer_check_mask_size(mask_buffer->size);
4050 io_buffer_check_overlaps(buffer, mask_buffer);
4051
4052 void *base;
4053 size_t size;
4054 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4055
4056 const void *mask_base;
4057 size_t mask_size;
4058 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4059
4060 memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
4061
4062 return self;
4063}
4064
4065static void
4066memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4067{
4068 for (size_t offset = 0; offset < size; offset += 1) {
4069 base[offset] |= mask[offset % mask_size];
4070 }
4071}
4072
4073/*
4074 * call-seq:
4075 * source.or!(mask) -> io_buffer
4076 *
4077 * Modify the source buffer in place by applying the binary OR
4078 * operation to the source, using the mask, repeating as necessary.
4079 *
4080 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4081 * # =>
4082 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
4083 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4084 *
4085 * source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4086 * # =>
4087 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
4088 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
4089 */
4090static VALUE
4091io_buffer_or_inplace(VALUE self, VALUE mask)
4092{
4093 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4094
4095 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4096
4097 io_buffer_check_mask_size(mask_buffer->size);
4098 io_buffer_check_overlaps(buffer, mask_buffer);
4099
4100 void *base;
4101 size_t size;
4102 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4103
4104 const void *mask_base;
4105 size_t mask_size;
4106 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4107
4108 memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
4109
4110 return self;
4111}
4112
4113static void
4114memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4115{
4116 for (size_t offset = 0; offset < size; offset += 1) {
4117 base[offset] ^= mask[offset % mask_size];
4118 }
4119}
4120
4121/*
4122 * call-seq:
4123 * source.xor!(mask) -> io_buffer
4124 *
4125 * Modify the source buffer in place by applying the binary XOR
4126 * operation to the source, using the mask, repeating as necessary.
4127 *
4128 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4129 * # =>
4130 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
4131 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4132 *
4133 * source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4134 * # =>
4135 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
4136 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
4137 */
4138static VALUE
4139io_buffer_xor_inplace(VALUE self, VALUE mask)
4140{
4141 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4142
4143 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4144
4145 io_buffer_check_mask_size(mask_buffer->size);
4146 io_buffer_check_overlaps(buffer, mask_buffer);
4147
4148 void *base;
4149 size_t size;
4150 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4151
4152 const void *mask_base;
4153 size_t mask_size;
4154 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4155
4156 memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
4157
4158 return self;
4159}
4160
4161static void
4162memory_not_inplace(unsigned char * restrict base, size_t size)
4163{
4164 for (size_t offset = 0; offset < size; offset += 1) {
4165 base[offset] = ~base[offset];
4166 }
4167}
4168
4169/*
4170 * call-seq:
4171 * source.not! -> io_buffer
4172 *
4173 * Modify the source buffer in place by applying the unary NOT
4174 * operation to the source.
4175 *
4176 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4177 * # =>
4178 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
4179 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4180 *
4181 * source.not!
4182 * # =>
4183 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
4184 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
4185 */
4186static VALUE
4187io_buffer_not_inplace(VALUE self)
4188{
4189 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4190
4191 void *base;
4192 size_t size;
4193 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4194
4195 memory_not_inplace(base, size);
4196
4197 return self;
4198}
4199
4200static size_t
4201memory_bit_count(const unsigned char *base, size_t size)
4202{
4203 size_t count = 0;
4204
4205 // Process 8 bytes at a time for efficiency:
4206 const uint64_t *base64 = (const uint64_t *)base;
4207 size_t count64 = size / 8;
4208 for (size_t i = 0; i < count64; i += 1) {
4209 count += rb_popcount64(base64[i]);
4210 }
4211
4212 // Process any remaining bytes:
4213 size_t remaining = size % 8;
4214 const unsigned char *tail = base + (count64 * 8);
4215 for (size_t i = 0; i < remaining; i += 1) {
4216 count += rb_popcount32(tail[i]);
4217 }
4218
4219 return count;
4220}
4221
4222/*
4223 * call-seq: bit_count([offset, [length]]) -> integer
4224 *
4225 * Returns the number of set bits (1s) in the buffer, also known as the
4226 * Hamming weight or population count. An optional +offset+ and +length+
4227 * can be provided to count bits in a subrange of the buffer.
4228 *
4229 * IO::Buffer.for("\xFF\x00\x0F").bit_count
4230 * # => 12
4231 *
4232 * IO::Buffer.for("\xFF\x00\x0F").bit_count(1, 2)
4233 * # => 4
4234 */
4235static VALUE
4236io_buffer_bit_count(int argc, VALUE *argv, VALUE self)
4237{
4238 rb_check_arity(argc, 0, 2);
4239
4240 size_t offset, length;
4241 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
4242
4243 io_buffer_validate_range(buffer, offset, length);
4244
4245 const void *base;
4246 size_t size;
4247 io_buffer_get_bytes_for_reading(buffer, &base, &size);
4248
4249 if (length == 0) return SIZET2NUM(0);
4250
4251 RUBY_ASSERT(base != NULL);
4252 size_t count = memory_bit_count((const unsigned char *)base + offset, length);
4253
4254 return SIZET2NUM(count);
4255}
4256
4257static bool
4258io_buffer_memory_view_get(VALUE self, rb_memory_view_t *view, int flags)
4259{
4260 struct rb_io_buffer *buffer = get_io_buffer(self);
4261
4262 if (buffer->base == NULL || !io_buffer_validate(buffer)) {
4263 return false;
4264 }
4265
4266 bool readonly = true;
4267 if (flags & RUBY_MEMORY_VIEW_WRITABLE) {
4268 if (io_buffer_readonly_p(buffer)) {
4269 return false;
4270 } else {
4271 readonly = false;
4272 }
4273 }
4274 rb_memory_view_init_as_byte_array(view, self, buffer->base, buffer->size, readonly);
4275 if (flags & RUBY_MEMORY_VIEW_FORMAT) {
4276 view->format = "C";
4277 }
4278 bool request_multi_dimensional = flags & RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL;
4279 bool request_strides =
4280 (flags & RUBY_MEMORY_VIEW_STRIDES) == RUBY_MEMORY_VIEW_STRIDES;
4281 if (request_multi_dimensional || request_strides) {
4282 size_t n_metadata = 0;
4283 if (request_multi_dimensional)
4284 n_metadata++;
4285 if (request_strides)
4286 n_metadata++;
4287 ssize_t *metadata_buffer = ALLOC_N(ssize_t, n_metadata);
4288 size_t i = 0;
4289 if (request_multi_dimensional) {
4290 ssize_t *shape = &metadata_buffer[i];
4291 shape[0] = buffer->size;
4292 view->shape = shape;
4293 i++;
4294 }
4295 if (request_strides) {
4296 ssize_t *strides = &metadata_buffer[i];
4297 strides[0] = 1;
4298 view->strides = strides;
4299 i++;
4300 }
4301 view->private_data = metadata_buffer;
4302 }
4303 io_buffer_lock(buffer);
4304
4305 return true;
4306}
4307
4308static bool
4309io_buffer_memory_view_release(VALUE self, rb_memory_view_t *view)
4310{
4311 rb_io_buffer_unlock(self);
4312 if (view->private_data) {
4313 xfree(view->private_data);
4314 }
4315 return true;
4316}
4317
4318static bool
4319io_buffer_memory_view_available_p(VALUE self)
4320{
4321 struct rb_io_buffer *buffer = get_io_buffer(self);
4322
4323 return buffer->base != NULL && io_buffer_validate(buffer);
4324}
4325
4326static const rb_memory_view_entry_t io_buffer_memory_view_entry = {
4327 .get_func = io_buffer_memory_view_get,
4328 .release_func = io_buffer_memory_view_release,
4329 .available_p_func = io_buffer_memory_view_available_p,
4330};
4331
4332/*
4333 * Document-class: IO::Buffer
4334 *
4335 * IO::Buffer is a efficient zero-copy buffer for input/output. There are
4336 * typical use cases:
4337 *
4338 * * Create an empty buffer with ::new, fill it with buffer using #copy or
4339 * #set_value, #set_string, get buffer with #get_string or write it directly
4340 * to some file with #write.
4341 * * Create a buffer mapped to some string with ::for, then it could be used
4342 * both for reading with #get_string or #get_value, and writing (writing will
4343 * change the source string, too).
4344 * * Create a buffer mapped to some file with ::map, then it could be used for
4345 * reading and writing the underlying file.
4346 * * Create a string of a fixed size with ::string, then #read into it, or
4347 * modify it using #set_value.
4348 *
4349 * Interaction with string and file memory is performed by efficient low-level
4350 * C mechanisms like `memcpy`.
4351 *
4352 * The class is meant to be an utility for implementing more high-level mechanisms
4353 * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
4354 * protocols.
4355 *
4356 * == MemoryView Support
4357 *
4358 * IO::Buffer supports the C-level MemoryView protocol, so C
4359 * extensions can use +rb_memory_view_get()+ to access the buffer's
4360 * memory directly (zero-copy) as a 1-dimensional contiguous array of
4361 * bytes. The memory view is writable if the buffer is not
4362 * #readonly? and +RUBY_MEMORY_VIEW_WRITABLE+ is specified.
4363 *
4364 * While a MemoryView is exported, the buffer is locked.
4365 *
4366 * == Examples of Usage
4367 *
4368 * Empty buffer:
4369 *
4370 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
4371 * # =>
4372 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
4373 * # ...
4374 * buffer
4375 * # =>
4376 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
4377 * # 0x00000000 00 00 00 00 00 00 00 00
4378 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
4379 * # => 4
4380 * buffer.get_string # get the result
4381 * # => "\x00\x00test\x00\x00"
4382 *
4383 * \Buffer from string:
4384 *
4385 * string = 'data'
4386 * IO::Buffer.for(string) do |buffer|
4387 * buffer
4388 * # =>
4389 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
4390 * # 0x00000000 64 61 74 61 data
4391 *
4392 * buffer.get_string(2) # read content starting from offset 2
4393 * # => "ta"
4394 * buffer.set_string('---', 1) # write content, starting from offset 1
4395 * # => 3
4396 * buffer
4397 * # =>
4398 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
4399 * # 0x00000000 64 2d 2d 2d d---
4400 * string # original string changed, too
4401 * # => "d---"
4402 * end
4403 *
4404 * \Buffer from file:
4405 *
4406 * File.write('test.txt', 'test data')
4407 * # => 9
4408 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
4409 * # =>
4410 * # #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY>
4411 * # ...
4412 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
4413 * # => "da"
4414 * buffer.set_string('---', 1) # attempt to write
4415 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
4416 *
4417 * # To create writable file-mapped buffer
4418 * # Open file for read-write, pass size, offset, and flags=0
4419 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
4420 * buffer.set_string('---', 1)
4421 * # => 3 -- bytes written
4422 * File.read('test.txt')
4423 * # => "t--- data"
4424 *
4425 * <b>The class is experimental and the interface is subject to change, this
4426 * is especially true of file mappings which may be removed entirely in
4427 * the future.</b>
4428 */
4429void
4430Init_IO_Buffer(void)
4431{
4432 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
4433
4434 /* Raised when an operation would resize or re-allocate a locked buffer. */
4435 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
4436
4437 /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
4438 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
4439
4440 /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
4441 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
4442
4443 /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
4444 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
4445
4446 /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
4447 rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
4448
4449 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
4450 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
4451 rb_define_singleton_method(rb_cIOBuffer, "string", rb_io_buffer_type_string, 1);
4452
4453#ifdef _WIN32
4454 SYSTEM_INFO info;
4455 GetSystemInfo(&info);
4456 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
4457 RUBY_IO_BUFFER_MAP_ALIGNMENT = info.dwAllocationGranularity;
4458#else /* not WIN32 */
4459 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
4460 RUBY_IO_BUFFER_MAP_ALIGNMENT = RUBY_IO_BUFFER_PAGE_SIZE;
4461#endif
4462
4463 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
4464
4465 /* The IO::Buffer interface version. */
4466 rb_define_const(rb_cIOBuffer, "VERSION", INT2NUM(RUBY_IO_BUFFER_VERSION));
4467
4468 /* The operating system page size. Used for efficient page-aligned memory allocations. */
4469 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
4470
4471 /* The alignment required for file mapping offsets. Mapping sizes do not need to be aligned. */
4472 rb_define_const(rb_cIOBuffer, "MAP_ALIGNMENT", SIZET2NUM(RUBY_IO_BUFFER_MAP_ALIGNMENT));
4473
4474 /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
4475 Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
4476 environment variable. */
4477 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
4478
4479 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
4480
4481 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
4482 rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
4483 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
4484 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, -1);
4485 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
4486 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
4487 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
4488
4489 rb_define_method(rb_cIOBuffer, "transfer", io_buffer_transfer, 0);
4490
4491 /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
4492 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
4493
4494 /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
4495 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
4496
4497 /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
4498 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
4499
4500 /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
4501 rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
4502
4503 /* 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. */
4504 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
4505
4506 /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
4507 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
4508
4509 /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
4510 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
4511
4512 /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
4513 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
4514
4515 /* Refers to the byte order of the host machine. See #get_value for more details. */
4516 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
4517
4518 /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
4519 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
4520
4521 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
4522 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
4523 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
4524 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
4525 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
4526 rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
4527 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
4528 rb_define_method(rb_cIOBuffer, "private?", rb_io_buffer_private_p, 0);
4529 rb_define_method(rb_cIOBuffer, "readonly?", rb_io_buffer_readonly_p, 0);
4530
4531 // Locking to prevent changes while using pointer:
4532 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
4533 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
4534 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
4535
4536 // Manipulation:
4537 rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
4538 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
4539 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
4540 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
4541 rb_define_method(rb_cIOBuffer, "free", io_buffer_free, 0);
4542
4543 rb_include_module(rb_cIOBuffer, rb_mComparable);
4544
4545#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
4546 IO_BUFFER_DEFINE_DATA_TYPE(U8);
4547 IO_BUFFER_DEFINE_DATA_TYPE(S8);
4548
4549 IO_BUFFER_DEFINE_DATA_TYPE(u16);
4550 IO_BUFFER_DEFINE_DATA_TYPE(U16);
4551 IO_BUFFER_DEFINE_DATA_TYPE(s16);
4552 IO_BUFFER_DEFINE_DATA_TYPE(S16);
4553
4554 IO_BUFFER_DEFINE_DATA_TYPE(u32);
4555 IO_BUFFER_DEFINE_DATA_TYPE(U32);
4556 IO_BUFFER_DEFINE_DATA_TYPE(s32);
4557 IO_BUFFER_DEFINE_DATA_TYPE(S32);
4558
4559 IO_BUFFER_DEFINE_DATA_TYPE(u64);
4560 IO_BUFFER_DEFINE_DATA_TYPE(U64);
4561 IO_BUFFER_DEFINE_DATA_TYPE(s64);
4562 IO_BUFFER_DEFINE_DATA_TYPE(S64);
4563
4564 IO_BUFFER_DEFINE_DATA_TYPE(u128);
4565 IO_BUFFER_DEFINE_DATA_TYPE(U128);
4566 IO_BUFFER_DEFINE_DATA_TYPE(s128);
4567 IO_BUFFER_DEFINE_DATA_TYPE(S128);
4568
4569 IO_BUFFER_DEFINE_DATA_TYPE(f32);
4570 IO_BUFFER_DEFINE_DATA_TYPE(F32);
4571 IO_BUFFER_DEFINE_DATA_TYPE(f64);
4572 IO_BUFFER_DEFINE_DATA_TYPE(F64);
4573#undef IO_BUFFER_DEFINE_DATA_TYPE
4574
4575 rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
4576
4577 // Data access:
4578 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
4579 rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
4580 rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
4581 rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
4582 rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
4583 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
4584 rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
4585
4586 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
4587
4588 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
4589 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
4590
4591 // Binary buffer manipulations:
4592 rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
4593 rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
4594 rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
4595 rb_define_method(rb_cIOBuffer, "~", io_buffer_not, 0);
4596
4597 rb_define_method(rb_cIOBuffer, "and!", io_buffer_and_inplace, 1);
4598 rb_define_method(rb_cIOBuffer, "or!", io_buffer_or_inplace, 1);
4599 rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
4600 rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
4601
4602 rb_define_method(rb_cIOBuffer, "bit_count", io_buffer_bit_count, -1);
4603
4604 // IO operations:
4605 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
4606 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
4607 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
4608 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
4609
4610 // MemoryView:
4611 rb_memory_view_register(rb_cIOBuffer, &io_buffer_memory_view_entry);
4612}
#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:714
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1769
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1035
#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:1473
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1471
@ 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:191
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:906
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:492
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:581
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:935
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:1046
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:52
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:969
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:1012
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:3906
#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:3475
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1763
#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:518
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:873
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition io.c:3018
#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:885
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:1773
#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:1824
#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
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:242
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:249
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