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