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