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