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