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