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