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