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