Ruby 4.1.0dev (2026-09-15 revision 135e0f844ab316ea5258b11672bd331269ab1460)
io_buffer.c (135e0f844ab316ea5258b11672bd331269ab1460)
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 (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 io_buffer_memmove(base, size, offset, source_base, source_offset, source_size, length);
3064
3065 return SIZET2NUM(length);
3066}
3067
3069 VALUE destination;
3070 const void *source_base;
3071 size_t source_size;
3072 int argc;
3073 VALUE *argv;
3074};
3075
3076// This is the innermost callback for IO::Buffer#copy. At this point the source
3077// is locked for reading and the destination is locked for writing, so both
3078// pointers and sizes remain valid while arguments are extracted, ranges are
3079// validated, and memmove potentially releases the GVL.
3080static VALUE
3081io_buffer_copy_to(void *base, size_t size, VALUE _arguments)
3082{
3083 struct io_buffer_copy_arguments *arguments = (void *)_arguments;
3084
3085 size_t offset, length, source_offset;
3086 io_buffer_extract_copy_arguments(arguments->source_size, arguments->argc, arguments->argv, &offset, &length, &source_offset);
3087
3088 io_buffer_memmove(base, size, offset, arguments->source_base, source_offset, arguments->source_size, length);
3089
3090 return SIZET2NUM(length);
3091}
3092
3093// This callback runs while the source is locked for reading. Retain its bytes
3094// in the callback arguments, then enter the destination's writable scope. The
3095// source scope remains active until that nested scope returns.
3096static VALUE
3097io_buffer_copy_from_readable(const void *base, size_t size, VALUE _arguments)
3098{
3099 struct io_buffer_copy_arguments *arguments = (void *)_arguments;
3100
3101 arguments->source_base = base;
3102 arguments->source_size = size;
3103
3104 return rb_io_buffer_locked_for_writing(arguments->destination, io_buffer_copy_to, _arguments);
3105}
3106
3107static VALUE
3108io_buffer_initialize_copy_from(const void *base, size_t size, VALUE self)
3109{
3110 struct rb_io_buffer *buffer = get_io_buffer(self);
3111
3112 io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
3113
3114 struct io_buffer_copy_arguments arguments = {
3115 .destination = self,
3116 .source_base = base,
3117 .source_size = size,
3118 .argc = 0,
3119 .argv = NULL,
3120 };
3121
3122 // The source remains locked by the outer readable scope while the newly
3123 // initialized destination is locked and populated by io_buffer_copy_to.
3124 return rb_io_buffer_locked_for_writing(self, io_buffer_copy_to, (VALUE)&arguments);
3125}
3126
3127/*
3128 * call-seq:
3129 * dup -> io_buffer
3130 * clone -> io_buffer
3131 *
3132 * Make an internal copy of the source buffer. Updates to the copy will not
3133 * affect the source buffer.
3134 *
3135 * source = IO::Buffer.for("Hello World")
3136 * # =>
3137 * # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
3138 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
3139 * buffer = source.dup
3140 * # =>
3141 * # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
3142 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
3143 */
3144static VALUE
3145rb_io_buffer_initialize_copy(VALUE self, VALUE source)
3146{
3147 return rb_io_buffer_locked_for_reading(source, io_buffer_initialize_copy_from, self);
3148}
3149
3150/*
3151 * call-seq:
3152 * copy(source, [offset, [length, [source_offset]]]) -> size
3153 *
3154 * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
3155 * using +memmove+. For copying String instances, see #set_string.
3156 *
3157 * buffer = IO::Buffer.new(32)
3158 * # =>
3159 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
3160 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
3161 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
3162 *
3163 * buffer.copy(IO::Buffer.for("test"), 8)
3164 * # => 4 -- size of buffer copied
3165 * buffer
3166 * # =>
3167 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
3168 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
3169 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
3170 *
3171 * #copy can be used to put buffer into strings associated with buffer:
3172 *
3173 * string = "data: "
3174 * # => "data: "
3175 * buffer = IO::Buffer.for(string) do |buffer|
3176 * buffer.copy(IO::Buffer.for("test"), 5)
3177 * end
3178 * # => 4
3179 * string
3180 * # => "data:test"
3181 *
3182 * Attempt to copy into a read-only buffer will fail:
3183 *
3184 * File.write('test.txt', 'test')
3185 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
3186 * buffer.copy(IO::Buffer.for("test"), 8)
3187 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
3188 *
3189 * See ::map for details of creation of mutable file mappings, this will
3190 * work:
3191 *
3192 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
3193 * buffer.copy(IO::Buffer.for("boom"), 0)
3194 * # => 4
3195 * File.read('test.txt')
3196 * # => "boom"
3197 *
3198 * Attempt to copy the buffer which will need place outside of buffer's
3199 * bounds will fail:
3200 *
3201 * buffer = IO::Buffer.new(2)
3202 * buffer.copy(IO::Buffer.for('test'), 0)
3203 * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
3204 *
3205 * It is safe to copy between memory regions that overlaps each other.
3206 * In such case, the data is copied as if the data was first copied from the source buffer to
3207 * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
3208 *
3209 * buffer = IO::Buffer.new(10)
3210 * buffer.set_string("0123456789")
3211 * buffer.copy(buffer, 3, 7)
3212 * # => 7
3213 * buffer
3214 * # =>
3215 * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
3216 * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
3217 */
3218static VALUE
3219io_buffer_copy(int argc, VALUE *argv, VALUE self)
3220{
3221 rb_check_arity(argc, 1, 4);
3222
3223 VALUE source = argv[0];
3224 struct io_buffer_copy_arguments arguments = {
3225 .destination = self,
3226 .argc = argc-1,
3227 .argv = argv+1,
3228 };
3229
3230 // Lock the source first, then io_buffer_copy_from_readable nests the
3231 // destination lock. The scoped helpers use rb_ensure, so the destination
3232 // is unlocked before the source on both normal and exceptional returns.
3233 // If both buffers share an allocation, its reference-counted lock is
3234 // acquired and released twice.
3235 return rb_io_buffer_locked_for_reading(source, io_buffer_copy_from_readable, (VALUE)&arguments);
3236}
3237
3238/*
3239 * call-seq: get_string([offset, [length, [encoding]]]) -> string
3240 *
3241 * Read a chunk or all of the buffer into a string, in the specified
3242 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
3243 *
3244 * buffer = IO::Buffer.for('test')
3245 * buffer.get_string
3246 * # => "test"
3247 * buffer.get_string(2)
3248 * # => "st"
3249 * buffer.get_string(2, 1)
3250 * # => "s"
3251 */
3252static VALUE
3253io_buffer_get_string(int argc, VALUE *argv, VALUE self)
3254{
3255 rb_check_arity(argc, 0, 3);
3256
3257 size_t offset, length;
3258 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
3259
3260 const void *base;
3261 size_t size;
3262 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3263
3264 rb_encoding *encoding;
3265 if (argc >= 3) {
3266 encoding = rb_find_encoding(argv[2]);
3267 }
3268 else {
3269 encoding = rb_ascii8bit_encoding();
3270 }
3271
3272 io_buffer_validate_range(buffer, offset, length);
3273
3274 const char *data = base ? (const char*)base + offset : NULL;
3275
3276 return rb_enc_str_new(data, length, encoding);
3277}
3278
3279/*
3280 * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
3281 *
3282 * Efficiently copy from a source String into the buffer, at +offset+ using
3283 * +memmove+.
3284 *
3285 * buf = IO::Buffer.new(8)
3286 * # =>
3287 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
3288 * # 0x00000000 00 00 00 00 00 00 00 00 ........
3289 *
3290 * # set buffer starting from offset 1, take 2 bytes starting from string's
3291 * # second
3292 * buf.set_string('test', 1, 2, 1)
3293 * # => 2
3294 * buf
3295 * # =>
3296 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
3297 * # 0x00000000 00 65 73 00 00 00 00 00 .es.....
3298 *
3299 * See also #copy for examples of how buffer writing might be used for changing
3300 * associated strings and files.
3301 */
3302static VALUE
3303io_buffer_set_string(int argc, VALUE *argv, VALUE self)
3304{
3305 rb_check_arity(argc, 1, 4);
3306
3307 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3308
3309 VALUE string = rb_str_to_str(argv[0]);
3310
3311 const void *source_base = RSTRING_PTR(string);
3312 size_t source_size = RSTRING_LEN(string);
3313
3314 VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
3315 RB_GC_GUARD(string);
3316 return result;
3317}
3318
3319void
3320rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
3321{
3322 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3323
3324 void *base;
3325 size_t size;
3326 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3327
3328 io_buffer_validate_range(buffer, offset, length);
3329
3330 if (length == 0) return;
3331
3332 RUBY_ASSERT(base != NULL);
3333 memset((char*)base + offset, value, length);
3334}
3335
3336/*
3337 * call-seq: clear(value = 0, [offset, [length]]) -> self
3338 *
3339 * Fill buffer with +value+, starting with +offset+ and going for +length+
3340 * bytes.
3341 *
3342 * buffer = IO::Buffer.for('test').dup
3343 * # =>
3344 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3345 * # 0x00000000 74 65 73 74 test
3346 *
3347 * buffer.clear
3348 * # =>
3349 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3350 * # 0x00000000 00 00 00 00 ....
3351 *
3352 * buf.clear(1) # fill with 1
3353 * # =>
3354 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3355 * # 0x00000000 01 01 01 01 ....
3356 *
3357 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
3358 * # =>
3359 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3360 * # 0x00000000 01 02 02 01 ....
3361 *
3362 * buffer.clear(2, 1) # fill with 2, starting from offset 1
3363 * # =>
3364 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
3365 * # 0x00000000 01 02 02 02 ....
3366 */
3367static VALUE
3368io_buffer_clear(int argc, VALUE *argv, VALUE self)
3369{
3370 rb_check_arity(argc, 0, 3);
3371
3372 uint8_t value = 0;
3373 if (argc >= 1) {
3374 value = NUM2UINT(argv[0]);
3375 }
3376
3377 size_t offset, length;
3378 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3379
3380 rb_io_buffer_clear(self, value, offset, length);
3381
3382 return self;
3383}
3384
3385static size_t
3386io_buffer_default_size(size_t page_size)
3387{
3388 // Platform agnostic default size, based on empirical performance observation:
3389 const size_t platform_agnostic_default_size = 64*1024;
3390
3391 // Allow user to specify custom default buffer size:
3392 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
3393 if (default_size) {
3394 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
3395 int value = atoi(default_size);
3396
3397 // assuming sizeof(int) <= sizeof(size_t)
3398 if (value > 0) {
3399 return value;
3400 }
3401 }
3402
3403 if (platform_agnostic_default_size < page_size) {
3404 return page_size;
3405 }
3406
3407 return platform_agnostic_default_size;
3408}
3409
3411 struct rb_io *io;
3412 struct rb_io_buffer *buffer;
3413 rb_blocking_function_t *function;
3414 void *data;
3415};
3416
3417static VALUE
3418io_buffer_blocking_region_begin(VALUE _argument)
3419{
3420 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
3421
3422 return rb_io_blocking_region(argument->io, argument->function, argument->data);
3423}
3424
3425static VALUE
3426io_buffer_blocking_region_ensure(VALUE _argument)
3427{
3428 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
3429
3430 io_buffer_unlock(argument->buffer);
3431
3432 return Qnil;
3433}
3434
3435static VALUE
3436io_buffer_blocking_region(VALUE io, struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data)
3437{
3438 struct rb_io *ioptr;
3439 RB_IO_POINTER(io, ioptr);
3440
3441 struct io_buffer_blocking_region_argument argument = {
3442 .io = ioptr,
3443 .buffer = buffer,
3444 .function = function,
3445 .data = data,
3446 };
3447
3448 // The buffer should be locked for the duration of the blocking region. We
3449 // always acquire our own reference so another operation cannot release the
3450 // allocation while this operation is still using it:
3451 io_buffer_lock(buffer);
3452
3453 return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
3454}
3455
3457 // The file descriptor to read from:
3458 int descriptor;
3459 // The base pointer to read into:
3460 char *base;
3461 // The maximum number of bytes to read:
3462 size_t length;
3463};
3464
3465static VALUE
3466io_buffer_read_internal(void *_argument)
3467{
3468 struct io_buffer_read_internal_argument *argument = _argument;
3469 ssize_t result = read(argument->descriptor, argument->base, argument->length);
3470
3471 return rb_fiber_scheduler_io_result(result, errno);
3472}
3473
3474VALUE
3475rb_io_buffer_read(VALUE self, VALUE io, size_t offset, size_t length)
3476{
3477 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3478
3479 io = rb_io_get_io(io);
3480
3481 io_buffer_validate_range(buffer, offset, length);
3482
3483 if (length == 0) return SIZET2NUM(0);
3484
3485 VALUE scheduler = rb_fiber_scheduler_current();
3486 if (scheduler != Qnil) {
3487 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, offset, length);
3488
3489 if (!UNDEF_P(result)) {
3490 return result;
3491 }
3492 }
3493
3494 void *base;
3495 size_t size;
3496 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3497
3498 RUBY_ASSERT(base != NULL);
3499
3500 struct io_buffer_read_internal_argument argument = {
3501 .descriptor = rb_io_descriptor(io),
3502 .base = (char*)base + offset,
3503 .length = length,
3504 };
3505
3506 return io_buffer_blocking_region(io, buffer, io_buffer_read_internal, &argument);
3507}
3508
3509/*
3510 * call-seq: read(io, [offset, [length]]) -> read length or -errno
3511 *
3512 * Perform one read operation of at most +length+ bytes from +io+ into the
3513 * buffer starting at +offset+. A short read is a normal result. If an error
3514 * occurs, return <tt>-errno</tt>.
3515 *
3516 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3517 * buffer. If +length+ is not given, it defaults to the size of the buffer
3518 * minus the offset. A zero length is a no-op.
3519 *
3520 * IO::Buffer.for('test') do |buffer|
3521 * p buffer
3522 * # =>
3523 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3524 * # 0x00000000 74 65 73 74 test
3525 * buffer.read(File.open('/dev/urandom', 'rb'), 0, 2)
3526 * p buffer
3527 * # =>
3528 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3529 * # 0x00000000 05 35 73 74 .5st
3530 * end
3531 */
3532static VALUE
3533io_buffer_read(int argc, VALUE *argv, VALUE self)
3534{
3535 rb_check_arity(argc, 1, 3);
3536
3537 VALUE io = argv[0];
3538
3539 size_t offset, length;
3540 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3541
3542 return rb_io_buffer_read(self, io, offset, length);
3543}
3544
3546 // The file descriptor to read from:
3547 int descriptor;
3548 // The base pointer to read into:
3549 char *base;
3550 // The maximum number of bytes to read:
3551 size_t length;
3552 // The position to read from:
3553 off_t from;
3554};
3555
3556static VALUE
3557io_buffer_pread_internal(void *_argument)
3558{
3559 struct io_buffer_pread_internal_argument *argument = _argument;
3560 ssize_t result = pread(argument->descriptor, argument->base, argument->length, argument->from);
3561
3562 return rb_fiber_scheduler_io_result(result, errno);
3563}
3564
3565VALUE
3566rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length)
3567{
3568 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
3569
3570 io = rb_io_get_io(io);
3571
3572 io_buffer_validate_range(buffer, offset, length);
3573
3574 if (length == 0) return SIZET2NUM(0);
3575
3576 VALUE scheduler = rb_fiber_scheduler_current();
3577 if (scheduler != Qnil) {
3578 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, offset, length);
3579
3580 if (!UNDEF_P(result)) {
3581 return result;
3582 }
3583 }
3584
3585 void *base;
3586 size_t size;
3587 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3588
3589 RUBY_ASSERT(base != NULL);
3590
3591 struct io_buffer_pread_internal_argument argument = {
3592 .descriptor = rb_io_descriptor(io),
3593 .base = (char*)base + offset,
3594 .length = length,
3595 .from = from,
3596 };
3597
3598 return io_buffer_blocking_region(io, buffer, io_buffer_pread_internal, &argument);
3599}
3600
3601/*
3602 * call-seq: pread(io, from, [offset, [length]]) -> read length or -errno
3603 *
3604 * Perform one read operation of at most +length+ bytes from +io+ at +from+
3605 * into the buffer starting at +offset+. A short read is a normal result and
3606 * the IO's current position is not modified. If an error occurs, return
3607 * <tt>-errno</tt>.
3608 *
3609 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3610 * buffer. If +length+ is not given, it defaults to the size of the buffer
3611 * minus the offset. A zero length is a no-op.
3612 *
3613 * IO::Buffer.for('test') do |buffer|
3614 * p buffer
3615 * # =>
3616 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
3617 * # 0x00000000 74 65 73 74 test
3618 *
3619 * # take 2 bytes from the beginning of urandom,
3620 * # put them in buffer starting from position 2
3621 * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
3622 * p buffer
3623 * # =>
3624 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
3625 * # 0x00000000 05 35 73 74 te.5
3626 * end
3627 */
3628static VALUE
3629io_buffer_pread(int argc, VALUE *argv, VALUE self)
3630{
3631 rb_check_arity(argc, 2, 4);
3632
3633 VALUE io = argv[0];
3634 rb_off_t from = NUM2OFFT(argv[1]);
3635
3636 size_t offset, length;
3637 io_buffer_extract_offset_length(self, argc-2, argv+2, &offset, &length);
3638
3639 return rb_io_buffer_pread(self, io, from, offset, length);
3640}
3641
3643 // The file descriptor to write to:
3644 int descriptor;
3645 // The base pointer to write from:
3646 const char *base;
3647 // The maximum number of bytes to write:
3648 size_t length;
3649};
3650
3651static VALUE
3652io_buffer_write_internal(void *_argument)
3653{
3654 struct io_buffer_write_internal_argument *argument = _argument;
3655 ssize_t result = write(argument->descriptor, argument->base, argument->length);
3656
3657 return rb_fiber_scheduler_io_result(result, errno);
3658}
3659
3660VALUE
3661rb_io_buffer_write(VALUE self, VALUE io, size_t offset, size_t length)
3662{
3664
3665 struct rb_io_buffer *buffer = get_io_buffer(self);
3666 io_buffer_validate_range(buffer, offset, length);
3667
3668 if (length == 0) return SIZET2NUM(0);
3669
3670 VALUE scheduler = rb_fiber_scheduler_current();
3671 if (scheduler != Qnil) {
3672 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, offset, length);
3673
3674 if (!UNDEF_P(result)) {
3675 return result;
3676 }
3677 }
3678
3679 const void *base;
3680 size_t size;
3681 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3682
3683 RUBY_ASSERT(base != NULL);
3684
3685 struct io_buffer_write_internal_argument argument = {
3686 .descriptor = rb_io_descriptor(io),
3687 .base = (const char*)base + offset,
3688 .length = length,
3689 };
3690
3691 return io_buffer_blocking_region(io, buffer, io_buffer_write_internal, &argument);
3692}
3693
3694/*
3695 * call-seq: write(io, [offset, [length]]) -> written length or -errno
3696 *
3697 * Perform one write operation of at most +length+ bytes to +io+ from the
3698 * buffer starting at +offset+. A short write is a normal result. If an error
3699 * occurs, return <tt>-errno</tt>.
3700 *
3701 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3702 * buffer. If +length+ is not given, it defaults to the size of the buffer
3703 * minus the offset. A zero length is a no-op.
3704 *
3705 * out = File.open('output.txt', 'wb')
3706 * IO::Buffer.for('1234567').write(out, 0, 3)
3707 *
3708 * This leads to +123+ being written into <tt>output.txt</tt>
3709 */
3710static VALUE
3711io_buffer_write(int argc, VALUE *argv, VALUE self)
3712{
3713 rb_check_arity(argc, 1, 3);
3714
3715 VALUE io = argv[0];
3716
3717 size_t offset, length;
3718 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
3719
3720 return rb_io_buffer_write(self, io, offset, length);
3721}
3722
3724 // The file descriptor to write to:
3725 int descriptor;
3726 // The base pointer to write from:
3727 const char *base;
3728 // The maximum number of bytes to write:
3729 size_t length;
3730 // The position to write to:
3731 off_t from;
3732};
3733
3734static VALUE
3735io_buffer_pwrite_internal(void *_argument)
3736{
3737 struct io_buffer_pwrite_internal_argument *argument = _argument;
3738 ssize_t result = pwrite(argument->descriptor, argument->base, argument->length, argument->from);
3739
3740 return rb_fiber_scheduler_io_result(result, errno);
3741}
3742
3743VALUE
3744rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t offset, size_t length)
3745{
3747
3748 struct rb_io_buffer *buffer = get_io_buffer(self);
3749 io_buffer_validate_range(buffer, offset, length);
3750
3751 if (length == 0) return SIZET2NUM(0);
3752
3753 VALUE scheduler = rb_fiber_scheduler_current();
3754 if (scheduler != Qnil) {
3755 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, offset, length);
3756
3757 if (!UNDEF_P(result)) {
3758 return result;
3759 }
3760 }
3761
3762 const void *base;
3763 size_t size;
3764 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3765
3766 RUBY_ASSERT(base != NULL);
3767
3768 struct io_buffer_pwrite_internal_argument argument = {
3769 .descriptor = rb_io_descriptor(io),
3770 .base = (const char*)base + offset,
3771 .length = length,
3772 .from = from,
3773 };
3774
3775 return io_buffer_blocking_region(io, buffer, io_buffer_pwrite_internal, &argument);
3776}
3777
3778/*
3779 * call-seq: pwrite(io, from, [offset, [length]]) -> written length or -errno
3780 *
3781 * Perform one write operation of at most +length+ bytes to +io+ at +from+
3782 * from the buffer starting at +offset+. A short write is a normal result and
3783 * the IO's current position is not modified. If an error occurs, return
3784 * <tt>-errno</tt>.
3785 *
3786 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3787 * buffer. If +length+ is not given, it defaults to the size of the buffer
3788 * minus the offset. A zero length is a no-op.
3789 *
3790 * If the +from+ position is beyond the end of the file, the gap will be
3791 * filled with null (0 value) bytes.
3792 *
3793 * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
3794 * IO::Buffer.for('1234567').pwrite(out, 2, 1, 3)
3795 *
3796 * This leads to +234+ (3 bytes, starting from position 1) being written into
3797 * <tt>output.txt</tt>, starting from file position 2.
3798 */
3799static VALUE
3800io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
3801{
3802 rb_check_arity(argc, 2, 4);
3803
3804 VALUE io = argv[0];
3805 rb_off_t from = NUM2OFFT(argv[1]);
3806
3807 size_t offset, length;
3808 io_buffer_extract_offset_length(self, argc-2, argv+2, &offset, &length);
3809
3810 return rb_io_buffer_pwrite(self, io, from, offset, length);
3811}
3812
3813static inline void
3814io_buffer_check_mask_size(size_t size)
3815{
3816 if (size == 0)
3817 rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
3818}
3819
3820static void
3821memory_and(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3822{
3823 for (size_t offset = 0; offset < size; offset += 1) {
3824 output[offset] = base[offset] & mask[offset % mask_size];
3825 }
3826}
3827
3828/*
3829 * call-seq:
3830 * source & mask -> io_buffer
3831 *
3832 * Generate a new buffer the same size as the source by applying the binary AND
3833 * operation to the source, using the mask, repeating as necessary.
3834 *
3835 * IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
3836 * # =>
3837 * # #<IO::Buffer 0x00005589b2758480+10 INTERNAL>
3838 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3839 */
3840static VALUE
3841io_buffer_and(VALUE self, VALUE mask)
3842{
3843 struct rb_io_buffer *buffer = get_io_buffer(self);
3844
3845 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3846
3847 const void *base;
3848 size_t size;
3849 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3850
3851 const void *mask_base;
3852 size_t mask_size;
3853 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3854
3855 io_buffer_check_mask_size(mask_size);
3856
3857 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3858 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3859
3860 memory_and(output_buffer->base, base, size, mask_base, mask_size);
3861
3862 return output;
3863}
3864
3865static void
3866memory_or(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3867{
3868 for (size_t offset = 0; offset < size; offset += 1) {
3869 output[offset] = base[offset] | mask[offset % mask_size];
3870 }
3871}
3872
3873/*
3874 * call-seq:
3875 * source | mask -> io_buffer
3876 *
3877 * Generate a new buffer the same size as the source by applying the binary OR
3878 * operation to the source, using the mask, repeating as necessary.
3879 *
3880 * IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
3881 * # =>
3882 * # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
3883 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3884 */
3885static VALUE
3886io_buffer_or(VALUE self, VALUE mask)
3887{
3888 struct rb_io_buffer *buffer = get_io_buffer(self);
3889
3890 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3891
3892 const void *base;
3893 size_t size;
3894 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3895
3896 const void *mask_base;
3897 size_t mask_size;
3898 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3899
3900 io_buffer_check_mask_size(mask_size);
3901
3902 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3903 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3904
3905 memory_or(output_buffer->base, base, size, mask_base, mask_size);
3906
3907 return output;
3908}
3909
3910static void
3911memory_xor(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
3912{
3913 for (size_t offset = 0; offset < size; offset += 1) {
3914 output[offset] = base[offset] ^ mask[offset % mask_size];
3915 }
3916}
3917
3918/*
3919 * call-seq:
3920 * source ^ mask -> io_buffer
3921 *
3922 * Generate a new buffer the same size as the source by applying the binary XOR
3923 * operation to the source, using the mask, repeating as necessary.
3924 *
3925 * IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
3926 * # =>
3927 * # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
3928 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3929 */
3930static VALUE
3931io_buffer_xor(VALUE self, VALUE mask)
3932{
3933 struct rb_io_buffer *buffer = get_io_buffer(self);
3934
3935 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
3936
3937 const void *base;
3938 size_t size;
3939 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3940
3941 const void *mask_base;
3942 size_t mask_size;
3943 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
3944
3945 io_buffer_check_mask_size(mask_size);
3946
3947 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3948 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3949
3950 memory_xor(output_buffer->base, base, size, mask_base, mask_size);
3951
3952 return output;
3953}
3954
3955static void
3956memory_not(unsigned char * restrict output, const unsigned char * restrict base, size_t size)
3957{
3958 for (size_t offset = 0; offset < size; offset += 1) {
3959 output[offset] = ~base[offset];
3960 }
3961}
3962
3963/*
3964 * call-seq:
3965 * ~source -> io_buffer
3966 *
3967 * Generate a new buffer the same size as the source by applying the unary NOT
3968 * operation to the source.
3969 *
3970 * ~IO::Buffer.for("1234567890")
3971 * # =>
3972 * # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
3973 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3974 */
3975static VALUE
3976io_buffer_not(VALUE self)
3977{
3978 struct rb_io_buffer *buffer = get_io_buffer(self);
3979
3980 const void *base;
3981 size_t size;
3982 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3983
3984 VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
3985 struct rb_io_buffer *output_buffer = get_io_buffer(output);
3986
3987 memory_not(output_buffer->base, base, size);
3988
3989 return output;
3990}
3991
3992static inline int
3993io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
3994{
3995 if (a->base > b->base) {
3996 return io_buffer_overlaps(b, a);
3997 }
3998
3999 return (b->base >= a->base) && (b->base < (void*)((unsigned char *)a->base + a->size));
4000}
4001
4002static inline void
4003io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
4004{
4005 if (io_buffer_overlaps(a, b))
4006 rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
4007}
4008
4009static void
4010memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4011{
4012 for (size_t offset = 0; offset < size; offset += 1) {
4013 base[offset] &= mask[offset % mask_size];
4014 }
4015}
4016
4017/*
4018 * call-seq:
4019 * source.and!(mask) -> io_buffer
4020 *
4021 * Modify the source buffer in place by applying the binary AND
4022 * operation to the source, using the mask, repeating as necessary.
4023 *
4024 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4025 * # =>
4026 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
4027 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4028 *
4029 * source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4030 * # =>
4031 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
4032 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
4033 */
4034static VALUE
4035io_buffer_and_inplace(VALUE self, VALUE mask)
4036{
4037 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4038
4039 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4040
4041 io_buffer_check_mask_size(mask_buffer->size);
4042 io_buffer_check_overlaps(buffer, mask_buffer);
4043
4044 void *base;
4045 size_t size;
4046 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4047
4048 const void *mask_base;
4049 size_t mask_size;
4050 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4051
4052 memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
4053
4054 return self;
4055}
4056
4057static void
4058memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4059{
4060 for (size_t offset = 0; offset < size; offset += 1) {
4061 base[offset] |= mask[offset % mask_size];
4062 }
4063}
4064
4065/*
4066 * call-seq:
4067 * source.or!(mask) -> io_buffer
4068 *
4069 * Modify the source buffer in place by applying the binary OR
4070 * operation to the source, using the mask, repeating as necessary.
4071 *
4072 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4073 * # =>
4074 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
4075 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4076 *
4077 * source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4078 * # =>
4079 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
4080 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
4081 */
4082static VALUE
4083io_buffer_or_inplace(VALUE self, VALUE mask)
4084{
4085 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4086
4087 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4088
4089 io_buffer_check_mask_size(mask_buffer->size);
4090 io_buffer_check_overlaps(buffer, mask_buffer);
4091
4092 void *base;
4093 size_t size;
4094 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4095
4096 const void *mask_base;
4097 size_t mask_size;
4098 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4099
4100 memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
4101
4102 return self;
4103}
4104
4105static void
4106memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
4107{
4108 for (size_t offset = 0; offset < size; offset += 1) {
4109 base[offset] ^= mask[offset % mask_size];
4110 }
4111}
4112
4113/*
4114 * call-seq:
4115 * source.xor!(mask) -> io_buffer
4116 *
4117 * Modify the source buffer in place by applying the binary XOR
4118 * operation to the source, using the mask, repeating as necessary.
4119 *
4120 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4121 * # =>
4122 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
4123 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4124 *
4125 * source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
4126 * # =>
4127 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
4128 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
4129 */
4130static VALUE
4131io_buffer_xor_inplace(VALUE self, VALUE mask)
4132{
4133 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4134
4135 struct rb_io_buffer *mask_buffer = get_io_buffer(mask);
4136
4137 io_buffer_check_mask_size(mask_buffer->size);
4138 io_buffer_check_overlaps(buffer, mask_buffer);
4139
4140 void *base;
4141 size_t size;
4142 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4143
4144 const void *mask_base;
4145 size_t mask_size;
4146 io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
4147
4148 memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
4149
4150 return self;
4151}
4152
4153static void
4154memory_not_inplace(unsigned char * restrict base, size_t size)
4155{
4156 for (size_t offset = 0; offset < size; offset += 1) {
4157 base[offset] = ~base[offset];
4158 }
4159}
4160
4161/*
4162 * call-seq:
4163 * source.not! -> io_buffer
4164 *
4165 * Modify the source buffer in place by applying the unary NOT
4166 * operation to the source.
4167 *
4168 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
4169 * # =>
4170 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
4171 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
4172 *
4173 * source.not!
4174 * # =>
4175 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
4176 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
4177 */
4178static VALUE
4179io_buffer_not_inplace(VALUE self)
4180{
4181 struct rb_io_buffer *buffer = get_io_buffer_for_writing(self);
4182
4183 void *base;
4184 size_t size;
4185 io_buffer_get_bytes_for_writing(buffer, &base, &size);
4186
4187 memory_not_inplace(base, size);
4188
4189 return self;
4190}
4191
4192static size_t
4193memory_bit_count(const unsigned char *base, size_t size)
4194{
4195 size_t count = 0;
4196
4197 // Process 8 bytes at a time for efficiency:
4198 const uint64_t *base64 = (const uint64_t *)base;
4199 size_t count64 = size / 8;
4200 for (size_t i = 0; i < count64; i += 1) {
4201 count += rb_popcount64(base64[i]);
4202 }
4203
4204 // Process any remaining bytes:
4205 size_t remaining = size % 8;
4206 const unsigned char *tail = base + (count64 * 8);
4207 for (size_t i = 0; i < remaining; i += 1) {
4208 count += rb_popcount32(tail[i]);
4209 }
4210
4211 return count;
4212}
4213
4214/*
4215 * call-seq: bit_count([offset, [length]]) -> integer
4216 *
4217 * Returns the number of set bits (1s) in the buffer, also known as the
4218 * Hamming weight or population count. An optional +offset+ and +length+
4219 * can be provided to count bits in a subrange of the buffer.
4220 *
4221 * IO::Buffer.for("\xFF\x00\x0F").bit_count
4222 * # => 12
4223 *
4224 * IO::Buffer.for("\xFF\x00\x0F").bit_count(1, 2)
4225 * # => 4
4226 */
4227static VALUE
4228io_buffer_bit_count(int argc, VALUE *argv, VALUE self)
4229{
4230 rb_check_arity(argc, 0, 2);
4231
4232 size_t offset, length;
4233 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
4234
4235 io_buffer_validate_range(buffer, offset, length);
4236
4237 const void *base;
4238 size_t size;
4239 io_buffer_get_bytes_for_reading(buffer, &base, &size);
4240
4241 if (length == 0) return SIZET2NUM(0);
4242
4243 RUBY_ASSERT(base != NULL);
4244 size_t count = memory_bit_count((const unsigned char *)base + offset, length);
4245
4246 return SIZET2NUM(count);
4247}
4248
4249static bool
4250io_buffer_memory_view_get(VALUE self, rb_memory_view_t *view, int flags)
4251{
4252 struct rb_io_buffer *buffer = get_io_buffer(self);
4253
4254 if (buffer->base == NULL || !io_buffer_validate(buffer)) {
4255 return false;
4256 }
4257
4258 bool readonly = true;
4259 if (flags & RUBY_MEMORY_VIEW_WRITABLE) {
4260 if (io_buffer_readonly_p(buffer)) {
4261 return false;
4262 } else {
4263 readonly = false;
4264 }
4265 }
4266 rb_memory_view_init_as_byte_array(view, self, buffer->base, buffer->size, readonly);
4267 if (flags & RUBY_MEMORY_VIEW_FORMAT) {
4268 view->format = "C";
4269 }
4270 bool request_multi_dimensional = flags & RUBY_MEMORY_VIEW_MULTI_DIMENSIONAL;
4271 bool request_strides =
4272 (flags & RUBY_MEMORY_VIEW_STRIDES) == RUBY_MEMORY_VIEW_STRIDES;
4273 if (request_multi_dimensional || request_strides) {
4274 size_t n_metadata = 0;
4275 if (request_multi_dimensional)
4276 n_metadata++;
4277 if (request_strides)
4278 n_metadata++;
4279 ssize_t *metadata_buffer = ALLOC_N(ssize_t, n_metadata);
4280 size_t i = 0;
4281 if (request_multi_dimensional) {
4282 ssize_t *shape = &metadata_buffer[i];
4283 shape[0] = buffer->size;
4284 view->shape = shape;
4285 i++;
4286 }
4287 if (request_strides) {
4288 ssize_t *strides = &metadata_buffer[i];
4289 strides[0] = 1;
4290 view->strides = strides;
4291 i++;
4292 }
4293 view->private_data = metadata_buffer;
4294 }
4295 io_buffer_lock(buffer);
4296
4297 return true;
4298}
4299
4300static bool
4301io_buffer_memory_view_release(VALUE self, rb_memory_view_t *view)
4302{
4303 rb_io_buffer_unlock(self);
4304 if (view->private_data) {
4305 xfree(view->private_data);
4306 }
4307 return true;
4308}
4309
4310static bool
4311io_buffer_memory_view_available_p(VALUE self)
4312{
4313 struct rb_io_buffer *buffer = get_io_buffer(self);
4314
4315 return buffer->base != NULL && io_buffer_validate(buffer);
4316}
4317
4318static const rb_memory_view_entry_t io_buffer_memory_view_entry = {
4319 .get_func = io_buffer_memory_view_get,
4320 .release_func = io_buffer_memory_view_release,
4321 .available_p_func = io_buffer_memory_view_available_p,
4322};
4323
4324/*
4325 * Document-class: IO::Buffer
4326 *
4327 * IO::Buffer is a efficient zero-copy buffer for input/output. There are
4328 * typical use cases:
4329 *
4330 * * Create an empty buffer with ::new, fill it with buffer using #copy or
4331 * #set_value, #set_string, get buffer with #get_string or write it directly
4332 * to some file with #write.
4333 * * Create a buffer mapped to some string with ::for, then it could be used
4334 * both for reading with #get_string or #get_value, and writing (writing will
4335 * change the source string, too).
4336 * * Create a buffer mapped to some file with ::map, then it could be used for
4337 * reading and writing the underlying file.
4338 * * Create a string of a fixed size with ::string, then #read into it, or
4339 * modify it using #set_value.
4340 *
4341 * Interaction with string and file memory is performed by efficient low-level
4342 * C mechanisms like `memcpy`.
4343 *
4344 * The class is meant to be an utility for implementing more high-level mechanisms
4345 * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
4346 * protocols.
4347 *
4348 * == MemoryView Support
4349 *
4350 * IO::Buffer supports the C-level MemoryView protocol, so C
4351 * extensions can use +rb_memory_view_get()+ to access the buffer's
4352 * memory directly (zero-copy) as a 1-dimensional contiguous array of
4353 * bytes. The memory view is writable if the buffer is not
4354 * #readonly? and +RUBY_MEMORY_VIEW_WRITABLE+ is specified.
4355 *
4356 * While a MemoryView is exported, the buffer is locked.
4357 *
4358 * == Examples of Usage
4359 *
4360 * Empty buffer:
4361 *
4362 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
4363 * # =>
4364 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
4365 * # ...
4366 * buffer
4367 * # =>
4368 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
4369 * # 0x00000000 00 00 00 00 00 00 00 00
4370 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
4371 * # => 4
4372 * buffer.get_string # get the result
4373 * # => "\x00\x00test\x00\x00"
4374 *
4375 * \Buffer from string:
4376 *
4377 * string = 'data'
4378 * IO::Buffer.for(string) do |buffer|
4379 * buffer
4380 * # =>
4381 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
4382 * # 0x00000000 64 61 74 61 data
4383 *
4384 * buffer.get_string(2) # read content starting from offset 2
4385 * # => "ta"
4386 * buffer.set_string('---', 1) # write content, starting from offset 1
4387 * # => 3
4388 * buffer
4389 * # =>
4390 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
4391 * # 0x00000000 64 2d 2d 2d d---
4392 * string # original string changed, too
4393 * # => "d---"
4394 * end
4395 *
4396 * \Buffer from file:
4397 *
4398 * File.write('test.txt', 'test data')
4399 * # => 9
4400 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
4401 * # =>
4402 * # #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY>
4403 * # ...
4404 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
4405 * # => "da"
4406 * buffer.set_string('---', 1) # attempt to write
4407 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
4408 *
4409 * # To create writable file-mapped buffer
4410 * # Open file for read-write, pass size, offset, and flags=0
4411 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
4412 * buffer.set_string('---', 1)
4413 * # => 3 -- bytes written
4414 * File.read('test.txt')
4415 * # => "t--- data"
4416 *
4417 * <b>The class is experimental and the interface is subject to change, this
4418 * is especially true of file mappings which may be removed entirely in
4419 * the future.</b>
4420 */
4421void
4422Init_IO_Buffer(void)
4423{
4424 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
4425
4426 /* Raised when an operation would resize or re-allocate a locked buffer. */
4427 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
4428
4429 /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
4430 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
4431
4432 /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
4433 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
4434
4435 /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
4436 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
4437
4438 /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
4439 rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
4440
4441 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
4442 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
4443 rb_define_singleton_method(rb_cIOBuffer, "string", rb_io_buffer_type_string, 1);
4444
4445#ifdef _WIN32
4446 SYSTEM_INFO info;
4447 GetSystemInfo(&info);
4448 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
4449 RUBY_IO_BUFFER_MAP_ALIGNMENT = info.dwAllocationGranularity;
4450#else /* not WIN32 */
4451 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
4452 RUBY_IO_BUFFER_MAP_ALIGNMENT = RUBY_IO_BUFFER_PAGE_SIZE;
4453#endif
4454
4455 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
4456
4457 /* The IO::Buffer interface version. */
4458 rb_define_const(rb_cIOBuffer, "VERSION", INT2NUM(RUBY_IO_BUFFER_VERSION));
4459
4460 /* The operating system page size. Used for efficient page-aligned memory allocations. */
4461 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
4462
4463 /* The alignment required for file mapping offsets. Mapping sizes do not need to be aligned. */
4464 rb_define_const(rb_cIOBuffer, "MAP_ALIGNMENT", SIZET2NUM(RUBY_IO_BUFFER_MAP_ALIGNMENT));
4465
4466 /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
4467 Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
4468 environment variable. */
4469 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
4470
4471 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
4472
4473 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
4474 rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
4475 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
4476 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, -1);
4477 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
4478 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
4479 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
4480
4481 rb_define_method(rb_cIOBuffer, "transfer", io_buffer_transfer, 0);
4482
4483 /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
4484 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
4485
4486 /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
4487 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
4488
4489 /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
4490 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
4491
4492 /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
4493 rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
4494
4495 /* 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. */
4496 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
4497
4498 /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
4499 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
4500
4501 /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
4502 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
4503
4504 /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
4505 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
4506
4507 /* Refers to the byte order of the host machine. See #get_value for more details. */
4508 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
4509
4510 /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
4511 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
4512
4513 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
4514 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
4515 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
4516 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
4517 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
4518 rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
4519 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
4520 rb_define_method(rb_cIOBuffer, "private?", rb_io_buffer_private_p, 0);
4521 rb_define_method(rb_cIOBuffer, "readonly?", rb_io_buffer_readonly_p, 0);
4522
4523 // Locking to prevent changes while using pointer:
4524 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
4525 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
4526 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
4527
4528 // Manipulation:
4529 rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
4530 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
4531 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
4532 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
4533 rb_define_method(rb_cIOBuffer, "free", io_buffer_free, 0);
4534
4535 rb_include_module(rb_cIOBuffer, rb_mComparable);
4536
4537#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
4538 IO_BUFFER_DEFINE_DATA_TYPE(U8);
4539 IO_BUFFER_DEFINE_DATA_TYPE(S8);
4540
4541 IO_BUFFER_DEFINE_DATA_TYPE(u16);
4542 IO_BUFFER_DEFINE_DATA_TYPE(U16);
4543 IO_BUFFER_DEFINE_DATA_TYPE(s16);
4544 IO_BUFFER_DEFINE_DATA_TYPE(S16);
4545
4546 IO_BUFFER_DEFINE_DATA_TYPE(u32);
4547 IO_BUFFER_DEFINE_DATA_TYPE(U32);
4548 IO_BUFFER_DEFINE_DATA_TYPE(s32);
4549 IO_BUFFER_DEFINE_DATA_TYPE(S32);
4550
4551 IO_BUFFER_DEFINE_DATA_TYPE(u64);
4552 IO_BUFFER_DEFINE_DATA_TYPE(U64);
4553 IO_BUFFER_DEFINE_DATA_TYPE(s64);
4554 IO_BUFFER_DEFINE_DATA_TYPE(S64);
4555
4556 IO_BUFFER_DEFINE_DATA_TYPE(u128);
4557 IO_BUFFER_DEFINE_DATA_TYPE(U128);
4558 IO_BUFFER_DEFINE_DATA_TYPE(s128);
4559 IO_BUFFER_DEFINE_DATA_TYPE(S128);
4560
4561 IO_BUFFER_DEFINE_DATA_TYPE(f32);
4562 IO_BUFFER_DEFINE_DATA_TYPE(F32);
4563 IO_BUFFER_DEFINE_DATA_TYPE(f64);
4564 IO_BUFFER_DEFINE_DATA_TYPE(F64);
4565#undef IO_BUFFER_DEFINE_DATA_TYPE
4566
4567 rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
4568
4569 // Data access:
4570 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
4571 rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
4572 rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
4573 rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
4574 rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
4575 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
4576 rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
4577
4578 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
4579
4580 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
4581 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
4582
4583 // Binary buffer manipulations:
4584 rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
4585 rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
4586 rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
4587 rb_define_method(rb_cIOBuffer, "~", io_buffer_not, 0);
4588
4589 rb_define_method(rb_cIOBuffer, "and!", io_buffer_and_inplace, 1);
4590 rb_define_method(rb_cIOBuffer, "or!", io_buffer_or_inplace, 1);
4591 rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
4592 rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
4593
4594 rb_define_method(rb_cIOBuffer, "bit_count", io_buffer_bit_count, -1);
4595
4596 // IO operations:
4597 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
4598 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
4599 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
4600 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
4601
4602 // MemoryView:
4603 rb_memory_view_register(rb_cIOBuffer, &io_buffer_memory_view_entry);
4604}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition fl_type.h:711
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition class.c:1764
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1034
#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:1463
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1461
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
Definition error.h:51
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_cIO
IO class.
Definition io.c:187
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:234
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:905
VALUE rb_mComparable
Comparable module.
Definition compar.c:19
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:469
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:3898
#define rb_str_new(str, len)
Allocates an instance of rb_cString.
Definition string.h:1499
VALUE rb_str_locktmp(VALUE str)
Obtains a "temporary lock" of the string.
VALUE rb_str_unlocktmp(VALUE str)
Releases a lock formerly obtained by rb_str_locktmp().
Definition string.c:3467
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1755
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:517
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
ID rb_sym2id(VALUE obj)
Converts an instance of rb_cSymbol into an ID.
Definition symbol.c:1091
VALUE rb_io_get_io(VALUE io)
Identical to rb_io_check_io(), except it raises exceptions on conversion failures.
Definition io.c:815
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition io.c:2931
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
Definition io.h:436
VALUE rb_io_get_write_io(VALUE io)
Queries the tied IO for writing.
Definition io.c:827
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
Identical to rb_thread_call_without_gvl(), except it additionally takes "flags" that change the behav...
Definition thread.c: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:1816
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:604
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define RB_NO_KEYWORDS
Do not pass keywords.
Definition scan_args.h:69
static bool RB_NIL_P(VALUE obj)
Checks if the given object is nil.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:238
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:245
Ruby's IO, metadata and buffers.
Definition io.h:295
Operations applied to a specific kind of a memory view.
rb_memory_view_get_func_t get_func
Exports a memory view from a Ruby object.
A MemoryView structure, rb_memory_view_t, is used for exporting objects' MemoryView.
Definition memory_view.h:77
const ssize_t * strides
ndim size array indicating the number of bytes to skip to go to the next element in each dimension.
const ssize_t * shape
ndim size array indicating the number of elements in each dimension.
void * private_data
The private data for managing this exported memory.
const char * format
A string to describe the format of an element, or NULL for unsigned bytes.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
Definition value_type.h:425
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376