Ruby 3.5.0dev (2025-01-10 revision 5fab31b15e32622c4b71d1d347a41937e9f9c212)
io_buffer.c (5fab31b15e32622c4b71d1d347a41937e9f9c212)
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
1478// Validate that access to the buffer is within bounds, assuming you want to
1479// access length bytes from the specified offset.
1480static inline void
1481io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
1482{
1483 // We assume here that offset + length won't overflow:
1484 if (offset + length > buffer->size) {
1485 rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
1486 }
1487}
1488
1489/*
1490 * call-seq: hexdump([offset, [length, [width]]]) -> string
1491 *
1492 * Returns a human-readable string representation of the buffer. The exact
1493 * format is subject to change.
1494 *
1495 * buffer = IO::Buffer.for("Hello World")
1496 * puts buffer.hexdump
1497 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1498 *
1499 * As buffers are usually fairly big, you may want to limit the output by
1500 * specifying the offset and length:
1501 *
1502 * puts buffer.hexdump(6, 5)
1503 * # 0x00000006 57 6f 72 6c 64 World
1504 */
1505static VALUE
1506rb_io_buffer_hexdump(int argc, VALUE *argv, VALUE self)
1507{
1508 rb_check_arity(argc, 0, 3);
1509
1510 size_t offset, length;
1511 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
1512
1513 size_t width = RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH;
1514 if (argc >= 3) {
1515 width = io_buffer_extract_width(argv[2], 1);
1516 }
1517
1518 // This may raise an exception if the offset/length is invalid:
1519 io_buffer_validate_range(buffer, offset, length);
1520
1521 VALUE result = Qnil;
1522
1523 if (io_buffer_validate(buffer) && buffer->base) {
1524 result = rb_str_buf_new(io_buffer_hexdump_output_size(width, length, 1));
1525
1526 io_buffer_hexdump(result, width, buffer->base, offset+length, offset, 1);
1527 }
1528
1529 return result;
1530}
1531
1532static VALUE
1533rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_t length)
1534{
1535 io_buffer_validate_range(buffer, offset, length);
1536
1537 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1538 struct rb_io_buffer *slice = NULL;
1539 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
1540
1541 slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
1542 slice->base = (char*)buffer->base + offset;
1543 slice->size = length;
1544
1545 // The source should be the root buffer:
1546 if (buffer->source != Qnil) {
1547 RB_OBJ_WRITE(instance, &slice->source, buffer->source);
1548 }
1549 else {
1550 RB_OBJ_WRITE(instance, &slice->source, self);
1551 }
1552
1553 return instance;
1554}
1555
1556/*
1557 * call-seq: slice([offset, [length]]) -> io_buffer
1558 *
1559 * Produce another IO::Buffer which is a slice (or view into) the current one
1560 * starting at +offset+ bytes and going for +length+ bytes.
1561 *
1562 * The slicing happens without copying of memory, and the slice keeps being
1563 * associated with the original buffer's source (string, or file), if any.
1564 *
1565 * If the offset is not given, it will be zero. If the offset is negative, it
1566 * will raise an ArgumentError.
1567 *
1568 * If the length is not given, the slice will be as long as the original
1569 * buffer minus the specified offset. If the length is negative, it will raise
1570 * an ArgumentError.
1571 *
1572 * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
1573 * buffer's bounds.
1574 *
1575 * string = 'test'
1576 * buffer = IO::Buffer.for(string).dup
1577 *
1578 * slice = buffer.slice
1579 * # =>
1580 * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
1581 * # 0x00000000 74 65 73 74 test
1582 *
1583 * buffer.slice(2)
1584 * # =>
1585 * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
1586 * # 0x00000000 73 74 st
1587 *
1588 * slice = buffer.slice(1, 2)
1589 * # =>
1590 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1591 * # 0x00000000 65 73 es
1592 *
1593 * # Put "o" into 0s position of the slice
1594 * slice.set_string('o', 0)
1595 * slice
1596 * # =>
1597 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1598 * # 0x00000000 6f 73 os
1599 *
1600 * # it is also visible at position 1 of the original buffer
1601 * buffer
1602 * # =>
1603 * # #<IO::Buffer 0x00007fc3d31e2d80+4 INTERNAL>
1604 * # 0x00000000 74 6f 73 74 tost
1605 */
1606static VALUE
1607io_buffer_slice(int argc, VALUE *argv, VALUE self)
1608{
1609 rb_check_arity(argc, 0, 2);
1610
1611 size_t offset, length;
1612 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
1613
1614 return rb_io_buffer_slice(buffer, self, offset, length);
1615}
1616
1617/*
1618 * call-seq: transfer -> new_io_buffer
1619 *
1620 * Transfers ownership of the underlying memory to a new buffer, causing the
1621 * current buffer to become uninitialized.
1622 *
1623 * buffer = IO::Buffer.new('test')
1624 * other = buffer.transfer
1625 * other
1626 * # =>
1627 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1628 * # 0x00000000 74 65 73 74 test
1629 * buffer
1630 * # =>
1631 * # #<IO::Buffer 0x0000000000000000+0 NULL>
1632 * buffer.null?
1633 * # => true
1634 */
1635VALUE
1636rb_io_buffer_transfer(VALUE self)
1637{
1638 struct rb_io_buffer *buffer = NULL;
1639 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1640
1641 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1642 rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
1643 }
1644
1645 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1646 struct rb_io_buffer *transferred;
1647 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
1648
1649 *transferred = *buffer;
1650 io_buffer_zero(buffer);
1651
1652 return instance;
1653}
1654
1655static void
1656io_buffer_resize_clear(struct rb_io_buffer *buffer, void* base, size_t size)
1657{
1658 if (size > buffer->size) {
1659 memset((unsigned char*)base+buffer->size, 0, size - buffer->size);
1660 }
1661}
1662
1663static void
1664io_buffer_resize_copy(VALUE self, struct rb_io_buffer *buffer, size_t size)
1665{
1666 // Slow path:
1667 struct rb_io_buffer resized;
1668 io_buffer_initialize(self, &resized, NULL, size, io_flags_for_size(size), Qnil);
1669
1670 if (buffer->base) {
1671 size_t preserve = buffer->size;
1672 if (preserve > size) preserve = size;
1673 memcpy(resized.base, buffer->base, preserve);
1674
1675 io_buffer_resize_clear(buffer, resized.base, size);
1676 }
1677
1678 io_buffer_free(buffer);
1679 *buffer = resized;
1680}
1681
1682void
1683rb_io_buffer_resize(VALUE self, size_t size)
1684{
1685 struct rb_io_buffer *buffer = NULL;
1686 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
1687
1688 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
1689 rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
1690 }
1691
1692 if (buffer->base == NULL) {
1693 io_buffer_initialize(self, buffer, NULL, size, io_flags_for_size(size), Qnil);
1694 return;
1695 }
1696
1697 if (buffer->flags & RB_IO_BUFFER_EXTERNAL) {
1698 rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
1699 }
1700
1701#if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
1702 if (buffer->flags & RB_IO_BUFFER_MAPPED) {
1703 void *base = mremap(buffer->base, buffer->size, size, MREMAP_MAYMOVE);
1704
1705 if (base == MAP_FAILED) {
1706 rb_sys_fail("rb_io_buffer_resize:mremap");
1707 }
1708
1709 io_buffer_resize_clear(buffer, base, size);
1710
1711 buffer->base = base;
1712 buffer->size = size;
1713
1714 return;
1715 }
1716#endif
1717
1718 if (buffer->flags & RB_IO_BUFFER_INTERNAL) {
1719 if (size == 0) {
1720 io_buffer_free(buffer);
1721 return;
1722 }
1723
1724 void *base = realloc(buffer->base, size);
1725
1726 if (!base) {
1727 rb_sys_fail("rb_io_buffer_resize:realloc");
1728 }
1729
1730 io_buffer_resize_clear(buffer, base, size);
1731
1732 buffer->base = base;
1733 buffer->size = size;
1734
1735 return;
1736 }
1737
1738 io_buffer_resize_copy(self, buffer, size);
1739}
1740
1741/*
1742 * call-seq: resize(new_size) -> self
1743 *
1744 * Resizes a buffer to a +new_size+ bytes, preserving its content.
1745 * Depending on the old and new size, the memory area associated with
1746 * the buffer might be either extended, or rellocated at different
1747 * address with content being copied.
1748 *
1749 * buffer = IO::Buffer.new(4)
1750 * buffer.set_string("test", 0)
1751 * buffer.resize(8) # resize to 8 bytes
1752 * # =>
1753 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1754 * # 0x00000000 74 65 73 74 00 00 00 00 test....
1755 *
1756 * External buffer (created with ::for), and locked buffer
1757 * can not be resized.
1758 */
1759static VALUE
1760io_buffer_resize(VALUE self, VALUE size)
1761{
1762 rb_io_buffer_resize(self, io_buffer_extract_size(size));
1763
1764 return self;
1765}
1766
1767/*
1768 * call-seq: <=>(other) -> true or false
1769 *
1770 * Buffers are compared by size and exact contents of the memory they are
1771 * referencing using +memcmp+.
1772 */
1773static VALUE
1774rb_io_buffer_compare(VALUE self, VALUE other)
1775{
1776 const void *ptr1, *ptr2;
1777 size_t size1, size2;
1778
1779 rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
1780 rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
1781
1782 if (size1 < size2) {
1783 return RB_INT2NUM(-1);
1784 }
1785
1786 if (size1 > size2) {
1787 return RB_INT2NUM(1);
1788 }
1789
1790 return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
1791}
1792
1793static void
1794io_buffer_validate_type(size_t size, size_t offset)
1795{
1796 if (offset > size) {
1797 rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
1798 }
1799}
1800
1801// Lower case: little endian.
1802// Upper case: big endian (network endian).
1803//
1804// :U8 | unsigned 8-bit integer.
1805// :S8 | signed 8-bit integer.
1806//
1807// :u16, :U16 | unsigned 16-bit integer.
1808// :s16, :S16 | signed 16-bit integer.
1809//
1810// :u32, :U32 | unsigned 32-bit integer.
1811// :s32, :S32 | signed 32-bit integer.
1812//
1813// :u64, :U64 | unsigned 64-bit integer.
1814// :s64, :S64 | signed 64-bit integer.
1815//
1816// :f32, :F32 | 32-bit floating point number.
1817// :f64, :F64 | 64-bit floating point number.
1818
1819#define ruby_swap8(value) value
1820
1821union swapf32 {
1822 uint32_t integral;
1823 float value;
1824};
1825
1826static float
1827ruby_swapf32(float value)
1828{
1829 union swapf32 swap = {.value = value};
1830 swap.integral = ruby_swap32(swap.integral);
1831 return swap.value;
1832}
1833
1834union swapf64 {
1835 uint64_t integral;
1836 double value;
1837};
1838
1839static double
1840ruby_swapf64(double value)
1841{
1842 union swapf64 swap = {.value = value};
1843 swap.integral = ruby_swap64(swap.integral);
1844 return swap.value;
1845}
1846
1847#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1848static ID RB_IO_BUFFER_DATA_TYPE_##name; \
1849\
1850static VALUE \
1851io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1852{ \
1853 io_buffer_validate_type(size, *offset + sizeof(type)); \
1854 type value; \
1855 memcpy(&value, (char*)base + *offset, sizeof(type)); \
1856 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1857 *offset += sizeof(type); \
1858 return wrap(value); \
1859} \
1860\
1861static void \
1862io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
1863{ \
1864 io_buffer_validate_type(size, *offset + sizeof(type)); \
1865 type value = unwrap(_value); \
1866 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1867 memcpy((char*)base + *offset, &value, sizeof(type)); \
1868 *offset += sizeof(type); \
1869} \
1870\
1871enum { \
1872 RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
1873};
1874
1875IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
1876IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
1877
1878IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1879IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1880IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1881IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1882
1883IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1884IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1885IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1886IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1887
1888IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1889IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1890IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1891IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1892
1893IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1894IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1895IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1896IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1897#undef IO_BUFFER_DECLARE_TYPE
1898
1899static inline size_t
1900io_buffer_buffer_type_size(ID buffer_type)
1901{
1902#define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
1903 IO_BUFFER_DATA_TYPE_SIZE(U8)
1904 IO_BUFFER_DATA_TYPE_SIZE(S8)
1905 IO_BUFFER_DATA_TYPE_SIZE(u16)
1906 IO_BUFFER_DATA_TYPE_SIZE(U16)
1907 IO_BUFFER_DATA_TYPE_SIZE(s16)
1908 IO_BUFFER_DATA_TYPE_SIZE(S16)
1909 IO_BUFFER_DATA_TYPE_SIZE(u32)
1910 IO_BUFFER_DATA_TYPE_SIZE(U32)
1911 IO_BUFFER_DATA_TYPE_SIZE(s32)
1912 IO_BUFFER_DATA_TYPE_SIZE(S32)
1913 IO_BUFFER_DATA_TYPE_SIZE(u64)
1914 IO_BUFFER_DATA_TYPE_SIZE(U64)
1915 IO_BUFFER_DATA_TYPE_SIZE(s64)
1916 IO_BUFFER_DATA_TYPE_SIZE(S64)
1917 IO_BUFFER_DATA_TYPE_SIZE(f32)
1918 IO_BUFFER_DATA_TYPE_SIZE(F32)
1919 IO_BUFFER_DATA_TYPE_SIZE(f64)
1920 IO_BUFFER_DATA_TYPE_SIZE(F64)
1921#undef IO_BUFFER_DATA_TYPE_SIZE
1922
1923 rb_raise(rb_eArgError, "Invalid type name!");
1924}
1925
1926/*
1927 * call-seq:
1928 * size_of(buffer_type) -> byte size
1929 * size_of(array of buffer_type) -> byte size
1930 *
1931 * Returns the size of the given buffer type(s) in bytes.
1932 *
1933 * IO::Buffer.size_of(:u32) # => 4
1934 * IO::Buffer.size_of([:u32, :u32]) # => 8
1935 */
1936static VALUE
1937io_buffer_size_of(VALUE klass, VALUE buffer_type)
1938{
1939 if (RB_TYPE_P(buffer_type, T_ARRAY)) {
1940 size_t total = 0;
1941 for (long i = 0; i < RARRAY_LEN(buffer_type); i++) {
1942 total += io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type, i)));
1943 }
1944 return SIZET2NUM(total);
1945 }
1946 else {
1947 return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type)));
1948 }
1949}
1950
1951static inline VALUE
1952rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *offset)
1953{
1954#define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
1955 IO_BUFFER_GET_VALUE(U8)
1956 IO_BUFFER_GET_VALUE(S8)
1957
1958 IO_BUFFER_GET_VALUE(u16)
1959 IO_BUFFER_GET_VALUE(U16)
1960 IO_BUFFER_GET_VALUE(s16)
1961 IO_BUFFER_GET_VALUE(S16)
1962
1963 IO_BUFFER_GET_VALUE(u32)
1964 IO_BUFFER_GET_VALUE(U32)
1965 IO_BUFFER_GET_VALUE(s32)
1966 IO_BUFFER_GET_VALUE(S32)
1967
1968 IO_BUFFER_GET_VALUE(u64)
1969 IO_BUFFER_GET_VALUE(U64)
1970 IO_BUFFER_GET_VALUE(s64)
1971 IO_BUFFER_GET_VALUE(S64)
1972
1973 IO_BUFFER_GET_VALUE(f32)
1974 IO_BUFFER_GET_VALUE(F32)
1975 IO_BUFFER_GET_VALUE(f64)
1976 IO_BUFFER_GET_VALUE(F64)
1977#undef IO_BUFFER_GET_VALUE
1978
1979 rb_raise(rb_eArgError, "Invalid type name!");
1980}
1981
1982/*
1983 * call-seq: get_value(buffer_type, offset) -> numeric
1984 *
1985 * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
1986 * of symbols:
1987 *
1988 * * +:U8+: unsigned integer, 1 byte
1989 * * +:S8+: signed integer, 1 byte
1990 * * +:u16+: unsigned integer, 2 bytes, little-endian
1991 * * +:U16+: unsigned integer, 2 bytes, big-endian
1992 * * +:s16+: signed integer, 2 bytes, little-endian
1993 * * +:S16+: signed integer, 2 bytes, big-endian
1994 * * +:u32+: unsigned integer, 4 bytes, little-endian
1995 * * +:U32+: unsigned integer, 4 bytes, big-endian
1996 * * +:s32+: signed integer, 4 bytes, little-endian
1997 * * +:S32+: signed integer, 4 bytes, big-endian
1998 * * +:u64+: unsigned integer, 8 bytes, little-endian
1999 * * +:U64+: unsigned integer, 8 bytes, big-endian
2000 * * +:s64+: signed integer, 8 bytes, little-endian
2001 * * +:S64+: signed integer, 8 bytes, big-endian
2002 * * +:f32+: float, 4 bytes, little-endian
2003 * * +:F32+: float, 4 bytes, big-endian
2004 * * +:f64+: double, 8 bytes, little-endian
2005 * * +:F64+: double, 8 bytes, big-endian
2006 *
2007 * A buffer type refers specifically to the type of binary buffer that is stored
2008 * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
2009 * integer in little-endian format.
2010 *
2011 * string = [1.5].pack('f')
2012 * # => "\x00\x00\xC0?"
2013 * IO::Buffer.for(string).get_value(:f32, 0)
2014 * # => 1.5
2015 */
2016static VALUE
2017io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
2018{
2019 const void *base;
2020 size_t size;
2021 size_t offset = io_buffer_extract_offset(_offset);
2022
2023 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2024
2025 return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
2026}
2027
2028/*
2029 * call-seq: get_values(buffer_types, offset) -> array
2030 *
2031 * Similar to #get_value, except that it can handle multiple buffer types and
2032 * returns an array of values.
2033 *
2034 * string = [1.5, 2.5].pack('ff')
2035 * IO::Buffer.for(string).get_values([:f32, :f32], 0)
2036 * # => [1.5, 2.5]
2037 */
2038static VALUE
2039io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
2040{
2041 size_t offset = io_buffer_extract_offset(_offset);
2042
2043 const void *base;
2044 size_t size;
2045 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2046
2047 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2048 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2049 }
2050
2051 VALUE array = rb_ary_new_capa(RARRAY_LEN(buffer_types));
2052
2053 for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
2054 VALUE type = rb_ary_entry(buffer_types, i);
2055 VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset);
2056 rb_ary_push(array, value);
2057 }
2058
2059 return array;
2060}
2061
2062// Extract a count argument, which must be a positive integer.
2063// Count is generally considered relative to the number of things.
2064static inline size_t
2065io_buffer_extract_count(VALUE argument)
2066{
2067 if (rb_int_negative_p(argument)) {
2068 rb_raise(rb_eArgError, "Count can't be negative!");
2069 }
2070
2071 return NUM2SIZET(argument);
2072}
2073
2074static inline void
2075io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
2076{
2077 if (argc >= 1) {
2078 *offset = io_buffer_extract_offset(argv[0]);
2079 }
2080 else {
2081 *offset = 0;
2082 }
2083
2084 if (argc >= 2) {
2085 *count = io_buffer_extract_count(argv[1]);
2086 }
2087 else {
2088 if (*offset > size) {
2089 rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
2090 }
2091
2092 *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
2093 }
2094}
2095
2096/*
2097 * call-seq:
2098 * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
2099 * each(buffer_type, [offset, [count]]) -> enumerator
2100 *
2101 * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
2102 * from +offset+.
2103 *
2104 * If +count+ is given, only +count+ values will be yielded.
2105 *
2106 * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
2107 * puts "#{offset}: #{value}"
2108 * end
2109 * # 2: 108
2110 * # 3: 108
2111 */
2112static VALUE
2113io_buffer_each(int argc, VALUE *argv, VALUE self)
2114{
2115 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2116
2117 const void *base;
2118 size_t size;
2119
2120 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2121
2122 ID buffer_type;
2123 if (argc >= 1) {
2124 buffer_type = RB_SYM2ID(argv[0]);
2125 }
2126 else {
2127 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2128 }
2129
2130 size_t offset, count;
2131 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2132
2133 for (size_t i = 0; i < count; i++) {
2134 size_t current_offset = offset;
2135 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2136 rb_yield_values(2, SIZET2NUM(current_offset), value);
2137 }
2138
2139 return self;
2140}
2141
2142/*
2143 * call-seq: values(buffer_type, [offset, [count]]) -> array
2144 *
2145 * Returns an array of values of +buffer_type+ starting from +offset+.
2146 *
2147 * If +count+ is given, only +count+ values will be returned.
2148 *
2149 * IO::Buffer.for("Hello World").values(:U8, 2, 2)
2150 * # => [108, 108]
2151 */
2152static VALUE
2153io_buffer_values(int argc, VALUE *argv, VALUE self)
2154{
2155 const void *base;
2156 size_t size;
2157
2158 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2159
2160 ID buffer_type;
2161 if (argc >= 1) {
2162 buffer_type = RB_SYM2ID(argv[0]);
2163 }
2164 else {
2165 buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
2166 }
2167
2168 size_t offset, count;
2169 io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
2170
2171 VALUE array = rb_ary_new_capa(count);
2172
2173 for (size_t i = 0; i < count; i++) {
2174 VALUE value = rb_io_buffer_get_value(base, size, buffer_type, &offset);
2175 rb_ary_push(array, value);
2176 }
2177
2178 return array;
2179}
2180
2181/*
2182 * call-seq:
2183 * each_byte([offset, [count]]) {|offset, byte| ...} -> self
2184 * each_byte([offset, [count]]) -> enumerator
2185 *
2186 * Iterates over the buffer, yielding each byte starting from +offset+.
2187 *
2188 * If +count+ is given, only +count+ bytes will be yielded.
2189 *
2190 * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
2191 * puts "#{offset}: #{byte}"
2192 * end
2193 * # 2: 108
2194 * # 3: 108
2195 */
2196static VALUE
2197io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
2198{
2199 RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS);
2200
2201 const void *base;
2202 size_t size;
2203
2204 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
2205
2206 size_t offset, count;
2207 io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
2208
2209 for (size_t i = 0; i < count; i++) {
2210 unsigned char *value = (unsigned char *)base + i + offset;
2211 rb_yield(RB_INT2FIX(*value));
2212 }
2213
2214 return self;
2215}
2216
2217static inline void
2218rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *offset, VALUE value)
2219{
2220#define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;}
2221 IO_BUFFER_SET_VALUE(U8);
2222 IO_BUFFER_SET_VALUE(S8);
2223
2224 IO_BUFFER_SET_VALUE(u16);
2225 IO_BUFFER_SET_VALUE(U16);
2226 IO_BUFFER_SET_VALUE(s16);
2227 IO_BUFFER_SET_VALUE(S16);
2228
2229 IO_BUFFER_SET_VALUE(u32);
2230 IO_BUFFER_SET_VALUE(U32);
2231 IO_BUFFER_SET_VALUE(s32);
2232 IO_BUFFER_SET_VALUE(S32);
2233
2234 IO_BUFFER_SET_VALUE(u64);
2235 IO_BUFFER_SET_VALUE(U64);
2236 IO_BUFFER_SET_VALUE(s64);
2237 IO_BUFFER_SET_VALUE(S64);
2238
2239 IO_BUFFER_SET_VALUE(f32);
2240 IO_BUFFER_SET_VALUE(F32);
2241 IO_BUFFER_SET_VALUE(f64);
2242 IO_BUFFER_SET_VALUE(F64);
2243#undef IO_BUFFER_SET_VALUE
2244
2245 rb_raise(rb_eArgError, "Invalid type name!");
2246}
2247
2248/*
2249 * call-seq: set_value(type, offset, value) -> offset
2250 *
2251 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
2252 * symbols described in #get_value.
2253 *
2254 * buffer = IO::Buffer.new(8)
2255 * # =>
2256 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2257 * # 0x00000000 00 00 00 00 00 00 00 00
2258 *
2259 * buffer.set_value(:U8, 1, 111)
2260 * # => 1
2261 *
2262 * buffer
2263 * # =>
2264 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2265 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
2266 *
2267 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
2268 *
2269 * buffer = IO::Buffer.new(8)
2270 * buffer.set_value(:U32, 0, 2.5)
2271 *
2272 * buffer
2273 * # =>
2274 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2275 * # 0x00000000 00 00 00 02 00 00 00 00
2276 * # ^^ the same as if we'd pass just integer 2
2277 */
2278static VALUE
2279io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
2280{
2281 void *base;
2282 size_t size;
2283 size_t offset = io_buffer_extract_offset(_offset);
2284
2285 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
2286
2287 rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
2288
2289 return SIZET2NUM(offset);
2290}
2291
2292/*
2293 * call-seq: set_values(buffer_types, offset, values) -> offset
2294 *
2295 * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
2296 * should be an array of symbols as described in #get_value. +values+ should
2297 * be an array of values to write.
2298 *
2299 * buffer = IO::Buffer.new(8)
2300 * buffer.set_values([:U8, :U16], 0, [1, 2])
2301 * buffer
2302 * # =>
2303 * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
2304 * # 0x00000000 01 00 02 00 00 00 00 00 ........
2305 */
2306static VALUE
2307io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values)
2308{
2309 if (!RB_TYPE_P(buffer_types, T_ARRAY)) {
2310 rb_raise(rb_eArgError, "Argument buffer_types should be an array!");
2311 }
2312
2313 if (!RB_TYPE_P(values, T_ARRAY)) {
2314 rb_raise(rb_eArgError, "Argument values should be an array!");
2315 }
2316
2317 if (RARRAY_LEN(buffer_types) != RARRAY_LEN(values)) {
2318 rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
2319 }
2320
2321 size_t offset = io_buffer_extract_offset(_offset);
2322
2323 void *base;
2324 size_t size;
2325 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
2326
2327 for (long i = 0; i < RARRAY_LEN(buffer_types); i++) {
2328 VALUE type = rb_ary_entry(buffer_types, i);
2329 VALUE value = rb_ary_entry(values, i);
2330 rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value);
2331 }
2332
2333 return SIZET2NUM(offset);
2334}
2335
2336static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
2337
2339 unsigned char * destination;
2340 const unsigned char * source;
2341 size_t length;
2342};
2343
2344static void *
2345io_buffer_memmove_blocking(void *data)
2346{
2347 struct io_buffer_memmove_arguments *arguments = (struct io_buffer_memmove_arguments *)data;
2348
2349 memmove(arguments->destination, arguments->source, arguments->length);
2350
2351 return NULL;
2352}
2353
2354static void
2355io_buffer_memmove_unblock(void *data)
2356{
2357 // No safe way to interrupt.
2358}
2359
2360static void
2361io_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)
2362{
2363 void *base;
2364 size_t size;
2365 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2366
2367 io_buffer_validate_range(buffer, offset, length);
2368
2369 if (source_offset + length > source_size) {
2370 rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
2371 }
2372
2373 struct io_buffer_memmove_arguments arguments = {
2374 .destination = (unsigned char*)base+offset,
2375 .source = (unsigned char*)source_base+source_offset,
2376 .length = length
2377 };
2378
2379 if (arguments.length >= IO_BUFFER_BLOCKING_SIZE) {
2380 rb_nogvl(io_buffer_memmove_blocking, &arguments, io_buffer_memmove_unblock, &arguments, RB_NOGVL_OFFLOAD_SAFE);
2381 } else if (arguments.length != 0) {
2382 memmove(arguments.destination, arguments.source, arguments.length);
2383 }
2384}
2385
2386// (offset, length, source_offset) -> length
2387static VALUE
2388io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
2389{
2390 size_t offset = 0;
2391 size_t length;
2392 size_t source_offset;
2393
2394 // The offset we copy into the buffer:
2395 if (argc >= 1) {
2396 offset = io_buffer_extract_offset(argv[0]);
2397 }
2398
2399 // The offset we start from within the string:
2400 if (argc >= 3) {
2401 source_offset = io_buffer_extract_offset(argv[2]);
2402
2403 if (source_offset > source_size) {
2404 rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
2405 }
2406 }
2407 else {
2408 source_offset = 0;
2409 }
2410
2411 // The length we are going to copy:
2412 if (argc >= 2 && !RB_NIL_P(argv[1])) {
2413 length = io_buffer_extract_length(argv[1]);
2414 }
2415 else {
2416 // Default to the source offset -> source size:
2417 length = source_size - source_offset;
2418 }
2419
2420 io_buffer_memmove(buffer, offset, source_base, source_offset, source_size, length);
2421
2422 return SIZET2NUM(length);
2423}
2424
2425/*
2426 * call-seq:
2427 * dup -> io_buffer
2428 * clone -> io_buffer
2429 *
2430 * Make an internal copy of the source buffer. Updates to the copy will not
2431 * affect the source buffer.
2432 *
2433 * source = IO::Buffer.for("Hello World")
2434 * # =>
2435 * # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
2436 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2437 * buffer = source.dup
2438 * # =>
2439 * # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
2440 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2441 */
2442static VALUE
2443rb_io_buffer_initialize_copy(VALUE self, VALUE source)
2444{
2445 struct rb_io_buffer *buffer = NULL;
2446 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2447
2448 const void *source_base;
2449 size_t source_size;
2450
2451 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
2452
2453 io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
2454
2455 return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
2456}
2457
2458/*
2459 * call-seq:
2460 * copy(source, [offset, [length, [source_offset]]]) -> size
2461 *
2462 * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
2463 * using +memmove+. For copying String instances, see #set_string.
2464 *
2465 * buffer = IO::Buffer.new(32)
2466 * # =>
2467 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
2468 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2469 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2470 *
2471 * buffer.copy(IO::Buffer.for("test"), 8)
2472 * # => 4 -- size of buffer copied
2473 * buffer
2474 * # =>
2475 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
2476 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
2477 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2478 *
2479 * #copy can be used to put buffer into strings associated with buffer:
2480 *
2481 * string = "data: "
2482 * # => "data: "
2483 * buffer = IO::Buffer.for(string) do |buffer|
2484 * buffer.copy(IO::Buffer.for("test"), 5)
2485 * end
2486 * # => 4
2487 * string
2488 * # => "data:test"
2489 *
2490 * Attempt to copy into a read-only buffer will fail:
2491 *
2492 * File.write('test.txt', 'test')
2493 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
2494 * buffer.copy(IO::Buffer.for("test"), 8)
2495 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
2496 *
2497 * See ::map for details of creation of mutable file mappings, this will
2498 * work:
2499 *
2500 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
2501 * buffer.copy(IO::Buffer.for("boom"), 0)
2502 * # => 4
2503 * File.read('test.txt')
2504 * # => "boom"
2505 *
2506 * Attempt to copy the buffer which will need place outside of buffer's
2507 * bounds will fail:
2508 *
2509 * buffer = IO::Buffer.new(2)
2510 * buffer.copy(IO::Buffer.for('test'), 0)
2511 * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
2512 *
2513 * It is safe to copy between memory regions that overlaps each other.
2514 * In such case, the data is copied as if the data was first copied from the source buffer to
2515 * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
2516 *
2517 * buffer = IO::Buffer.new(10)
2518 * buffer.set_string("0123456789")
2519 * buffer.copy(buffer, 3, 7)
2520 * # => 7
2521 * buffer
2522 * # =>
2523 * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
2524 * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
2525 */
2526static VALUE
2527io_buffer_copy(int argc, VALUE *argv, VALUE self)
2528{
2529 rb_check_arity(argc, 1, 4);
2530
2531 struct rb_io_buffer *buffer = NULL;
2532 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2533
2534 VALUE source = argv[0];
2535 const void *source_base;
2536 size_t source_size;
2537
2538 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
2539
2540 return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
2541}
2542
2543/*
2544 * call-seq: get_string([offset, [length, [encoding]]]) -> string
2545 *
2546 * Read a chunk or all of the buffer into a string, in the specified
2547 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
2548 *
2549 * buffer = IO::Buffer.for('test')
2550 * buffer.get_string
2551 * # => "test"
2552 * buffer.get_string(2)
2553 * # => "st"
2554 * buffer.get_string(2, 1)
2555 * # => "s"
2556 */
2557static VALUE
2558io_buffer_get_string(int argc, VALUE *argv, VALUE self)
2559{
2560 rb_check_arity(argc, 0, 3);
2561
2562 size_t offset, length;
2563 struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
2564
2565 const void *base;
2566 size_t size;
2567 io_buffer_get_bytes_for_reading(buffer, &base, &size);
2568
2569 rb_encoding *encoding;
2570 if (argc >= 3) {
2571 encoding = rb_find_encoding(argv[2]);
2572 }
2573 else {
2574 encoding = rb_ascii8bit_encoding();
2575 }
2576
2577 io_buffer_validate_range(buffer, offset, length);
2578
2579 return rb_enc_str_new((const char*)base + offset, length, encoding);
2580}
2581
2582/*
2583 * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
2584 *
2585 * Efficiently copy from a source String into the buffer, at +offset+ using
2586 * +memmove+.
2587 *
2588 * buf = IO::Buffer.new(8)
2589 * # =>
2590 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2591 * # 0x00000000 00 00 00 00 00 00 00 00 ........
2592 *
2593 * # set buffer starting from offset 1, take 2 bytes starting from string's
2594 * # second
2595 * buf.set_string('test', 1, 2, 1)
2596 * # => 2
2597 * buf
2598 * # =>
2599 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2600 * # 0x00000000 00 65 73 00 00 00 00 00 .es.....
2601 *
2602 * See also #copy for examples of how buffer writing might be used for changing
2603 * associated strings and files.
2604 */
2605static VALUE
2606io_buffer_set_string(int argc, VALUE *argv, VALUE self)
2607{
2608 rb_check_arity(argc, 1, 4);
2609
2610 struct rb_io_buffer *buffer = NULL;
2611 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2612
2613 VALUE string = rb_str_to_str(argv[0]);
2614
2615 const void *source_base = RSTRING_PTR(string);
2616 size_t source_size = RSTRING_LEN(string);
2617
2618 return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
2619}
2620
2621void
2622rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
2623{
2624 struct rb_io_buffer *buffer = NULL;
2625 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2626
2627 void *base;
2628 size_t size;
2629 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2630
2631 io_buffer_validate_range(buffer, offset, length);
2632
2633 memset((char*)base + offset, value, length);
2634}
2635
2636/*
2637 * call-seq: clear(value = 0, [offset, [length]]) -> self
2638 *
2639 * Fill buffer with +value+, starting with +offset+ and going for +length+
2640 * bytes.
2641 *
2642 * buffer = IO::Buffer.for('test').dup
2643 * # =>
2644 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2645 * # 0x00000000 74 65 73 74 test
2646 *
2647 * buffer.clear
2648 * # =>
2649 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2650 * # 0x00000000 00 00 00 00 ....
2651 *
2652 * buf.clear(1) # fill with 1
2653 * # =>
2654 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2655 * # 0x00000000 01 01 01 01 ....
2656 *
2657 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
2658 * # =>
2659 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2660 * # 0x00000000 01 02 02 01 ....
2661 *
2662 * buffer.clear(2, 1) # fill with 2, starting from offset 1
2663 * # =>
2664 * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
2665 * # 0x00000000 01 02 02 02 ....
2666 */
2667static VALUE
2668io_buffer_clear(int argc, VALUE *argv, VALUE self)
2669{
2670 rb_check_arity(argc, 0, 3);
2671
2672 uint8_t value = 0;
2673 if (argc >= 1) {
2674 value = NUM2UINT(argv[0]);
2675 }
2676
2677 size_t offset, length;
2678 io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
2679
2680 rb_io_buffer_clear(self, value, offset, length);
2681
2682 return self;
2683}
2684
2685static size_t
2686io_buffer_default_size(size_t page_size)
2687{
2688 // Platform agnostic default size, based on empirical performance observation:
2689 const size_t platform_agnostic_default_size = 64*1024;
2690
2691 // Allow user to specify custom default buffer size:
2692 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
2693 if (default_size) {
2694 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
2695 int value = atoi(default_size);
2696
2697 // assuming sizeof(int) <= sizeof(size_t)
2698 if (value > 0) {
2699 return value;
2700 }
2701 }
2702
2703 if (platform_agnostic_default_size < page_size) {
2704 return page_size;
2705 }
2706
2707 return platform_agnostic_default_size;
2708}
2709
2711 struct rb_io *io;
2712 struct rb_io_buffer *buffer;
2713 rb_blocking_function_t *function;
2714 void *data;
2715};
2716
2717static VALUE
2718io_buffer_blocking_region_begin(VALUE _argument)
2719{
2720 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
2721
2722 return rb_io_blocking_region(argument->io, argument->function, argument->data);
2723}
2724
2725static VALUE
2726io_buffer_blocking_region_ensure(VALUE _argument)
2727{
2728 struct io_buffer_blocking_region_argument *argument = (void*)_argument;
2729
2730 io_buffer_unlock(argument->buffer);
2731
2732 return Qnil;
2733}
2734
2735static VALUE
2736io_buffer_blocking_region(VALUE io, struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data)
2737{
2738 io = rb_io_get_io(io);
2739 struct rb_io *ioptr;
2740 RB_IO_POINTER(io, ioptr);
2741
2742 struct io_buffer_blocking_region_argument argument = {
2743 .io = ioptr,
2744 .buffer = buffer,
2745 .function = function,
2746 .data = data,
2747 };
2748
2749 // If the buffer is already locked, we can skip the ensure (unlock):
2750 if (buffer->flags & RB_IO_BUFFER_LOCKED) {
2751 return io_buffer_blocking_region_begin((VALUE)&argument);
2752 }
2753 else {
2754 // The buffer should be locked for the duration of the blocking region:
2755 io_buffer_lock(buffer);
2756
2757 return rb_ensure(io_buffer_blocking_region_begin, (VALUE)&argument, io_buffer_blocking_region_ensure, (VALUE)&argument);
2758 }
2759}
2760
2762 // The file descriptor to read from:
2763 int descriptor;
2764 // The base pointer to read from:
2765 char *base;
2766 // The size of the buffer:
2767 size_t size;
2768 // The minimum number of bytes to read:
2769 size_t length;
2770};
2771
2772static VALUE
2773io_buffer_read_internal(void *_argument)
2774{
2775 size_t total = 0;
2776 struct io_buffer_read_internal_argument *argument = _argument;
2777
2778 while (true) {
2779 ssize_t result = read(argument->descriptor, argument->base, argument->size);
2780
2781 if (result < 0) {
2782 return rb_fiber_scheduler_io_result(result, errno);
2783 }
2784 else if (result == 0) {
2785 return rb_fiber_scheduler_io_result(total, 0);
2786 }
2787 else {
2788 total += result;
2789
2790 if (total >= argument->length) {
2791 return rb_fiber_scheduler_io_result(total, 0);
2792 }
2793
2794 argument->base = argument->base + result;
2795 argument->size = argument->size - result;
2796 }
2797 }
2798}
2799
2800VALUE
2801rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
2802{
2803 VALUE scheduler = rb_fiber_scheduler_current();
2804 if (scheduler != Qnil) {
2805 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length, offset);
2806
2807 if (!UNDEF_P(result)) {
2808 return result;
2809 }
2810 }
2811
2812 struct rb_io_buffer *buffer = NULL;
2813 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2814
2815 io_buffer_validate_range(buffer, offset, length);
2816
2817 int descriptor = rb_io_descriptor(io);
2818
2819 void * base;
2820 size_t size;
2821 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2822
2823 base = (unsigned char*)base + offset;
2824 size = size - offset;
2825
2826 struct io_buffer_read_internal_argument argument = {
2827 .descriptor = descriptor,
2828 .base = base,
2829 .size = size,
2830 .length = length,
2831 };
2832
2833 return io_buffer_blocking_region(io, buffer, io_buffer_read_internal, &argument);
2834}
2835
2836/*
2837 * call-seq: read(io, [length, [offset]]) -> read length or -errno
2838 *
2839 * Read at least +length+ bytes from the +io+, into the buffer starting at
2840 * +offset+. If an error occurs, return <tt>-errno</tt>.
2841 *
2842 * If +length+ is not given or +nil+, it defaults to the size of the buffer
2843 * minus the offset, i.e. the entire buffer.
2844 *
2845 * If +length+ is zero, exactly one <tt>read</tt> operation will occur.
2846 *
2847 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
2848 * buffer.
2849 *
2850 * IO::Buffer.for('test') do |buffer|
2851 * p buffer
2852 * # =>
2853 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2854 * # 0x00000000 74 65 73 74 test
2855 * buffer.read(File.open('/dev/urandom', 'rb'), 2)
2856 * p buffer
2857 * # =>
2858 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
2859 * # 0x00000000 05 35 73 74 .5st
2860 * end
2861 */
2862static VALUE
2863io_buffer_read(int argc, VALUE *argv, VALUE self)
2864{
2865 rb_check_arity(argc, 1, 3);
2866
2867 VALUE io = argv[0];
2868
2869 size_t length, offset;
2870 io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
2871
2872 return rb_io_buffer_read(self, io, length, offset);
2873}
2874
2876 // The file descriptor to read from:
2877 int descriptor;
2878 // The base pointer to read from:
2879 char *base;
2880 // The size of the buffer:
2881 size_t size;
2882 // The minimum number of bytes to read:
2883 size_t length;
2884 // The offset to read from:
2885 off_t offset;
2886};
2887
2888static VALUE
2889io_buffer_pread_internal(void *_argument)
2890{
2891 size_t total = 0;
2892 struct io_buffer_pread_internal_argument *argument = _argument;
2893
2894 while (true) {
2895 ssize_t result = pread(argument->descriptor, argument->base, argument->size, argument->offset);
2896
2897 if (result < 0) {
2898 return rb_fiber_scheduler_io_result(result, errno);
2899 }
2900 else if (result == 0) {
2901 return rb_fiber_scheduler_io_result(total, 0);
2902 }
2903 else {
2904 total += result;
2905
2906 if (total >= argument->length) {
2907 return rb_fiber_scheduler_io_result(total, 0);
2908 }
2909
2910 argument->base = argument->base + result;
2911 argument->size = argument->size - result;
2912 argument->offset = argument->offset + result;
2913 }
2914 }
2915}
2916
2917VALUE
2918rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
2919{
2920 VALUE scheduler = rb_fiber_scheduler_current();
2921 if (scheduler != Qnil) {
2922 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, length, offset);
2923
2924 if (!UNDEF_P(result)) {
2925 return result;
2926 }
2927 }
2928
2929 struct rb_io_buffer *buffer = NULL;
2930 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
2931
2932 io_buffer_validate_range(buffer, offset, length);
2933
2934 int descriptor = rb_io_descriptor(io);
2935
2936 void * base;
2937 size_t size;
2938 io_buffer_get_bytes_for_writing(buffer, &base, &size);
2939
2940 base = (unsigned char*)base + offset;
2941 size = size - offset;
2942
2943 struct io_buffer_pread_internal_argument argument = {
2944 .descriptor = descriptor,
2945 .base = base,
2946 .size = size,
2947 .length = length,
2948 .offset = from,
2949 };
2950
2951 return io_buffer_blocking_region(io, buffer, io_buffer_pread_internal, &argument);
2952}
2953
2954/*
2955 * call-seq: pread(io, from, [length, [offset]]) -> read length or -errno
2956 *
2957 * Read at least +length+ bytes from the +io+ starting at the specified +from+
2958 * position, into the buffer starting at +offset+. If an error occurs,
2959 * return <tt>-errno</tt>.
2960 *
2961 * If +length+ is not given or +nil+, it defaults to the size of the buffer
2962 * minus the offset, i.e. the entire buffer.
2963 *
2964 * If +length+ is zero, exactly one <tt>pread</tt> operation will occur.
2965 *
2966 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
2967 * buffer.
2968 *
2969 * IO::Buffer.for('test') do |buffer|
2970 * p buffer
2971 * # =>
2972 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2973 * # 0x00000000 74 65 73 74 test
2974 *
2975 * # take 2 bytes from the beginning of urandom,
2976 * # put them in buffer starting from position 2
2977 * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
2978 * p buffer
2979 * # =>
2980 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
2981 * # 0x00000000 05 35 73 74 te.5
2982 * end
2983 */
2984static VALUE
2985io_buffer_pread(int argc, VALUE *argv, VALUE self)
2986{
2987 rb_check_arity(argc, 2, 4);
2988
2989 VALUE io = argv[0];
2990 rb_off_t from = NUM2OFFT(argv[1]);
2991
2992 size_t length, offset;
2993 io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
2994
2995 return rb_io_buffer_pread(self, io, from, length, offset);
2996}
2997
2999 // The file descriptor to write to:
3000 int descriptor;
3001 // The base pointer to write from:
3002 const char *base;
3003 // The size of the buffer:
3004 size_t size;
3005 // The minimum length to write:
3006 size_t length;
3007};
3008
3009static VALUE
3010io_buffer_write_internal(void *_argument)
3011{
3012 size_t total = 0;
3013 struct io_buffer_write_internal_argument *argument = _argument;
3014
3015 while (true) {
3016 ssize_t result = write(argument->descriptor, argument->base, argument->size);
3017
3018 if (result < 0) {
3019 return rb_fiber_scheduler_io_result(result, errno);
3020 }
3021 else if (result == 0) {
3022 return rb_fiber_scheduler_io_result(total, 0);
3023 }
3024 else {
3025 total += result;
3026
3027 if (total >= argument->length) {
3028 return rb_fiber_scheduler_io_result(total, 0);
3029 }
3030
3031 argument->base = argument->base + result;
3032 argument->size = argument->size - result;
3033 }
3034 }
3035}
3036
3037VALUE
3038rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
3039{
3040 VALUE scheduler = rb_fiber_scheduler_current();
3041 if (scheduler != Qnil) {
3042 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length, offset);
3043
3044 if (!UNDEF_P(result)) {
3045 return result;
3046 }
3047 }
3048
3049 struct rb_io_buffer *buffer = NULL;
3050 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3051
3052 io_buffer_validate_range(buffer, offset, length);
3053
3054 int descriptor = rb_io_descriptor(io);
3055
3056 const void * base;
3057 size_t size;
3058 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3059
3060 base = (unsigned char*)base + offset;
3061 size = size - offset;
3062
3063 struct io_buffer_write_internal_argument argument = {
3064 .descriptor = descriptor,
3065 .base = base,
3066 .size = size,
3067 .length = length,
3068 };
3069
3070 return io_buffer_blocking_region(io, buffer, io_buffer_write_internal, &argument);
3071}
3072
3073/*
3074 * call-seq: write(io, [length, [offset]]) -> written length or -errno
3075 *
3076 * Write at least +length+ bytes from the buffer starting at +offset+, into the +io+.
3077 * If an error occurs, return <tt>-errno</tt>.
3078 *
3079 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3080 * minus the offset, i.e. the entire buffer.
3081 *
3082 * If +length+ is zero, exactly one <tt>write</tt> operation will occur.
3083 *
3084 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3085 * buffer.
3086 *
3087 * out = File.open('output.txt', 'wb')
3088 * IO::Buffer.for('1234567').write(out, 3)
3089 *
3090 * This leads to +123+ being written into <tt>output.txt</tt>
3091 */
3092static VALUE
3093io_buffer_write(int argc, VALUE *argv, VALUE self)
3094{
3095 rb_check_arity(argc, 1, 3);
3096
3097 VALUE io = argv[0];
3098
3099 size_t length, offset;
3100 io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
3101
3102 return rb_io_buffer_write(self, io, length, offset);
3103}
3105 // The file descriptor to write to:
3106 int descriptor;
3107 // The base pointer to write from:
3108 const char *base;
3109 // The size of the buffer:
3110 size_t size;
3111 // The minimum length to write:
3112 size_t length;
3113 // The offset to write to:
3114 off_t offset;
3115};
3116
3117static VALUE
3118io_buffer_pwrite_internal(void *_argument)
3119{
3120 size_t total = 0;
3121 struct io_buffer_pwrite_internal_argument *argument = _argument;
3122
3123 while (true) {
3124 ssize_t result = pwrite(argument->descriptor, argument->base, argument->size, argument->offset);
3125
3126 if (result < 0) {
3127 return rb_fiber_scheduler_io_result(result, errno);
3128 }
3129 else if (result == 0) {
3130 return rb_fiber_scheduler_io_result(total, 0);
3131 }
3132 else {
3133 total += result;
3134
3135 if (total >= argument->length) {
3136 return rb_fiber_scheduler_io_result(total, 0);
3137 }
3138
3139 argument->base = argument->base + result;
3140 argument->size = argument->size - result;
3141 argument->offset = argument->offset + result;
3142 }
3143 }
3144}
3145
3146VALUE
3147rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
3148{
3149 VALUE scheduler = rb_fiber_scheduler_current();
3150 if (scheduler != Qnil) {
3151 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, length, offset);
3152
3153 if (!UNDEF_P(result)) {
3154 return result;
3155 }
3156 }
3157
3158 struct rb_io_buffer *buffer = NULL;
3159 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3160
3161 io_buffer_validate_range(buffer, offset, length);
3162
3163 int descriptor = rb_io_descriptor(io);
3164
3165 const void * base;
3166 size_t size;
3167 io_buffer_get_bytes_for_reading(buffer, &base, &size);
3168
3169 base = (unsigned char*)base + offset;
3170 size = size - offset;
3171
3172 struct io_buffer_pwrite_internal_argument argument = {
3173 .descriptor = descriptor,
3174
3175 // Move the base pointer to the offset:
3176 .base = base,
3177
3178 // And the size to the length of buffer we want to read:
3179 .size = size,
3180
3181 // And the length of the buffer we want to write:
3182 .length = length,
3183
3184 // And the offset in the file we want to write from:
3185 .offset = from,
3186 };
3187
3188 return io_buffer_blocking_region(io, buffer, io_buffer_pwrite_internal, &argument);
3189}
3190
3191/*
3192 * call-seq: pwrite(io, from, [length, [offset]]) -> written length or -errno
3193 *
3194 * Write at least +length+ bytes from the buffer starting at +offset+, into
3195 * the +io+ starting at the specified +from+ position. If an error occurs,
3196 * return <tt>-errno</tt>.
3197 *
3198 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3199 * minus the offset, i.e. the entire buffer.
3200 *
3201 * If +length+ is zero, exactly one <tt>pwrite</tt> operation will occur.
3202 *
3203 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3204 * buffer.
3205 *
3206 * If the +from+ position is beyond the end of the file, the gap will be
3207 * filled with null (0 value) bytes.
3208 *
3209 * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
3210 * IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
3211 *
3212 * This leads to +234+ (3 bytes, starting from position 1) being written into
3213 * <tt>output.txt</tt>, starting from file position 2.
3214 */
3215static VALUE
3216io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
3217{
3218 rb_check_arity(argc, 2, 4);
3219
3220 VALUE io = argv[0];
3221 rb_off_t from = NUM2OFFT(argv[1]);
3222
3223 size_t length, offset;
3224 io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
3225
3226 return rb_io_buffer_pwrite(self, io, from, length, offset);
3227}
3228
3229static inline void
3230io_buffer_check_mask(const struct rb_io_buffer *buffer)
3231{
3232 if (buffer->size == 0)
3233 rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
3234}
3235
3236static void
3237memory_and(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3238{
3239 for (size_t offset = 0; offset < size; offset += 1) {
3240 output[offset] = base[offset] & mask[offset % mask_size];
3241 }
3242}
3243
3244/*
3245 * call-seq:
3246 * source & mask -> io_buffer
3247 *
3248 * Generate a new buffer the same size as the source by applying the binary AND
3249 * operation to the source, using the mask, repeating as necessary.
3250 *
3251 * IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
3252 * # =>
3253 * # #<IO::Buffer 0x00005589b2758480+4 INTERNAL>
3254 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3255 */
3256static VALUE
3257io_buffer_and(VALUE self, VALUE mask)
3258{
3259 struct rb_io_buffer *buffer = NULL;
3260 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3261
3262 struct rb_io_buffer *mask_buffer = NULL;
3263 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3264
3265 io_buffer_check_mask(mask_buffer);
3266
3267 VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
3268 struct rb_io_buffer *output_buffer = NULL;
3269 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3270
3271 memory_and(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
3272
3273 return output;
3274}
3275
3276static void
3277memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3278{
3279 for (size_t offset = 0; offset < size; offset += 1) {
3280 output[offset] = base[offset] | mask[offset % mask_size];
3281 }
3282}
3283
3284/*
3285 * call-seq:
3286 * source | mask -> io_buffer
3287 *
3288 * Generate a new buffer the same size as the source by applying the binary OR
3289 * operation to the source, using the mask, repeating as necessary.
3290 *
3291 * IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
3292 * # =>
3293 * # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
3294 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3295 */
3296static VALUE
3297io_buffer_or(VALUE self, VALUE mask)
3298{
3299 struct rb_io_buffer *buffer = NULL;
3300 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3301
3302 struct rb_io_buffer *mask_buffer = NULL;
3303 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3304
3305 io_buffer_check_mask(mask_buffer);
3306
3307 VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
3308 struct rb_io_buffer *output_buffer = NULL;
3309 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3310
3311 memory_or(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
3312
3313 return output;
3314}
3315
3316static void
3317memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3318{
3319 for (size_t offset = 0; offset < size; offset += 1) {
3320 output[offset] = base[offset] ^ mask[offset % mask_size];
3321 }
3322}
3323
3324/*
3325 * call-seq:
3326 * source ^ mask -> io_buffer
3327 *
3328 * Generate a new buffer the same size as the source by applying the binary XOR
3329 * operation to the source, using the mask, repeating as necessary.
3330 *
3331 * IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
3332 * # =>
3333 * # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
3334 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3335 */
3336static VALUE
3337io_buffer_xor(VALUE self, VALUE mask)
3338{
3339 struct rb_io_buffer *buffer = NULL;
3340 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3341
3342 struct rb_io_buffer *mask_buffer = NULL;
3343 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3344
3345 io_buffer_check_mask(mask_buffer);
3346
3347 VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
3348 struct rb_io_buffer *output_buffer = NULL;
3349 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3350
3351 memory_xor(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
3352
3353 return output;
3354}
3355
3356static void
3357memory_not(unsigned char * restrict output, unsigned char * restrict base, size_t size)
3358{
3359 for (size_t offset = 0; offset < size; offset += 1) {
3360 output[offset] = ~base[offset];
3361 }
3362}
3363
3364/*
3365 * call-seq:
3366 * ~source -> io_buffer
3367 *
3368 * Generate a new buffer the same size as the source by applying the binary NOT
3369 * operation to the source.
3370 *
3371 * ~IO::Buffer.for("1234567890")
3372 * # =>
3373 * # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
3374 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3375 */
3376static VALUE
3377io_buffer_not(VALUE self)
3378{
3379 struct rb_io_buffer *buffer = NULL;
3380 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3381
3382 VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
3383 struct rb_io_buffer *output_buffer = NULL;
3384 TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
3385
3386 memory_not(output_buffer->base, buffer->base, buffer->size);
3387
3388 return output;
3389}
3390
3391static inline int
3392io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
3393{
3394 if (a->base > b->base) {
3395 return io_buffer_overlaps(b, a);
3396 }
3397
3398 return (b->base >= a->base) && (b->base < (void*)((unsigned char *)a->base + a->size));
3399}
3400
3401static inline void
3402io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
3403{
3404 if (io_buffer_overlaps(a, b))
3405 rb_raise(rb_eIOBufferMaskError, "Mask overlaps source buffer!");
3406}
3407
3408static void
3409memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3410{
3411 for (size_t offset = 0; offset < size; offset += 1) {
3412 base[offset] &= mask[offset % mask_size];
3413 }
3414}
3415
3416/*
3417 * call-seq:
3418 * source.and!(mask) -> io_buffer
3419 *
3420 * Modify the source buffer in place by applying the binary AND
3421 * operation to the source, using the mask, repeating as necessary.
3422 *
3423 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3424 * # =>
3425 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3426 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3427 *
3428 * source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3429 * # =>
3430 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3431 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3432 */
3433static VALUE
3434io_buffer_and_inplace(VALUE self, VALUE mask)
3435{
3436 struct rb_io_buffer *buffer = NULL;
3437 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3438
3439 struct rb_io_buffer *mask_buffer = NULL;
3440 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3441
3442 io_buffer_check_mask(mask_buffer);
3443 io_buffer_check_overlaps(buffer, mask_buffer);
3444
3445 void *base;
3446 size_t size;
3447 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3448
3449 memory_and_inplace(base, size, mask_buffer->base, mask_buffer->size);
3450
3451 return self;
3452}
3453
3454static void
3455memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3456{
3457 for (size_t offset = 0; offset < size; offset += 1) {
3458 base[offset] |= mask[offset % mask_size];
3459 }
3460}
3461
3462/*
3463 * call-seq:
3464 * source.or!(mask) -> io_buffer
3465 *
3466 * Modify the source buffer in place by applying the binary OR
3467 * operation to the source, using the mask, repeating as necessary.
3468 *
3469 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3470 * # =>
3471 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3472 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3473 *
3474 * source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3475 * # =>
3476 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3477 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3478 */
3479static VALUE
3480io_buffer_or_inplace(VALUE self, VALUE mask)
3481{
3482 struct rb_io_buffer *buffer = NULL;
3483 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3484
3485 struct rb_io_buffer *mask_buffer = NULL;
3486 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3487
3488 io_buffer_check_mask(mask_buffer);
3489 io_buffer_check_overlaps(buffer, mask_buffer);
3490
3491 void *base;
3492 size_t size;
3493 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3494
3495 memory_or_inplace(base, size, mask_buffer->base, mask_buffer->size);
3496
3497 return self;
3498}
3499
3500static void
3501memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
3502{
3503 for (size_t offset = 0; offset < size; offset += 1) {
3504 base[offset] ^= mask[offset % mask_size];
3505 }
3506}
3507
3508/*
3509 * call-seq:
3510 * source.xor!(mask) -> io_buffer
3511 *
3512 * Modify the source buffer in place by applying the binary XOR
3513 * operation to the source, using the mask, repeating as necessary.
3514 *
3515 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3516 * # =>
3517 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3518 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3519 *
3520 * source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3521 * # =>
3522 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3523 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3524 */
3525static VALUE
3526io_buffer_xor_inplace(VALUE self, VALUE mask)
3527{
3528 struct rb_io_buffer *buffer = NULL;
3529 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3530
3531 struct rb_io_buffer *mask_buffer = NULL;
3532 TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
3533
3534 io_buffer_check_mask(mask_buffer);
3535 io_buffer_check_overlaps(buffer, mask_buffer);
3536
3537 void *base;
3538 size_t size;
3539 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3540
3541 memory_xor_inplace(base, size, mask_buffer->base, mask_buffer->size);
3542
3543 return self;
3544}
3545
3546static void
3547memory_not_inplace(unsigned char * restrict base, size_t size)
3548{
3549 for (size_t offset = 0; offset < size; offset += 1) {
3550 base[offset] = ~base[offset];
3551 }
3552}
3553
3554/*
3555 * call-seq:
3556 * source.not! -> io_buffer
3557 *
3558 * Modify the source buffer in place by applying the binary NOT
3559 * operation to the source.
3560 *
3561 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3562 * # =>
3563 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3564 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3565 *
3566 * source.not!
3567 * # =>
3568 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3569 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3570 */
3571static VALUE
3572io_buffer_not_inplace(VALUE self)
3573{
3574 struct rb_io_buffer *buffer = NULL;
3575 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
3576
3577 void *base;
3578 size_t size;
3579 io_buffer_get_bytes_for_writing(buffer, &base, &size);
3580
3581 memory_not_inplace(base, size);
3582
3583 return self;
3584}
3585
3586/*
3587 * Document-class: IO::Buffer
3588 *
3589 * IO::Buffer is a efficient zero-copy buffer for input/output. There are
3590 * typical use cases:
3591 *
3592 * * Create an empty buffer with ::new, fill it with buffer using #copy or
3593 * #set_value, #set_string, get buffer with #get_string or write it directly
3594 * to some file with #write.
3595 * * Create a buffer mapped to some string with ::for, then it could be used
3596 * both for reading with #get_string or #get_value, and writing (writing will
3597 * change the source string, too).
3598 * * Create a buffer mapped to some file with ::map, then it could be used for
3599 * reading and writing the underlying file.
3600 * * Create a string of a fixed size with ::string, then #read into it, or
3601 * modify it using #set_value.
3602 *
3603 * Interaction with string and file memory is performed by efficient low-level
3604 * C mechanisms like `memcpy`.
3605 *
3606 * The class is meant to be an utility for implementing more high-level mechanisms
3607 * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
3608 * protocols.
3609 *
3610 * == Examples of Usage
3611 *
3612 * Empty buffer:
3613 *
3614 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
3615 * # =>
3616 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
3617 * # ...
3618 * buffer
3619 * # =>
3620 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
3621 * # 0x00000000 00 00 00 00 00 00 00 00
3622 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
3623 * # => 4
3624 * buffer.get_string # get the result
3625 * # => "\x00\x00test\x00\x00"
3626 *
3627 * \Buffer from string:
3628 *
3629 * string = 'data'
3630 * IO::Buffer.for(string) do |buffer|
3631 * buffer
3632 * # =>
3633 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3634 * # 0x00000000 64 61 74 61 data
3635 *
3636 * buffer.get_string(2) # read content starting from offset 2
3637 * # => "ta"
3638 * buffer.set_string('---', 1) # write content, starting from offset 1
3639 * # => 3
3640 * buffer
3641 * # =>
3642 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3643 * # 0x00000000 64 2d 2d 2d d---
3644 * string # original string changed, too
3645 * # => "d---"
3646 * end
3647 *
3648 * \Buffer from file:
3649 *
3650 * File.write('test.txt', 'test data')
3651 * # => 9
3652 * buffer = IO::Buffer.map(File.open('test.txt'))
3653 * # =>
3654 * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
3655 * # ...
3656 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
3657 * # => "da"
3658 * buffer.set_string('---', 1) # attempt to write
3659 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
3660 *
3661 * # To create writable file-mapped buffer
3662 * # Open file for read-write, pass size, offset, and flags=0
3663 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
3664 * buffer.set_string('---', 1)
3665 * # => 3 -- bytes written
3666 * File.read('test.txt')
3667 * # => "t--- data"
3668 *
3669 * <b>The class is experimental and the interface is subject to change, this
3670 * is especially true of file mappings which may be removed entirely in
3671 * the future.</b>
3672 */
3673void
3674Init_IO_Buffer(void)
3675{
3676 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
3677
3678 /* Raised when an operation would resize or re-allocate a locked buffer. */
3679 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
3680
3681 /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
3682 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
3683
3684 /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
3685 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
3686
3687 /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
3688 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
3689
3690 /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
3691 rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
3692
3693 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
3694 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
3695 rb_define_singleton_method(rb_cIOBuffer, "string", rb_io_buffer_type_string, 1);
3696
3697#ifdef _WIN32
3698 SYSTEM_INFO info;
3699 GetSystemInfo(&info);
3700 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
3701#else /* not WIN32 */
3702 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
3703#endif
3704
3705 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
3706
3707 /* The operating system page size. Used for efficient page-aligned memory allocations. */
3708 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
3709
3710 /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
3711 Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
3712 environment variable. */
3713 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
3714
3715 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
3716
3717 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
3718 rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
3719 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
3720 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, -1);
3721 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
3722 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
3723 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
3724
3725 rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
3726
3727 /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
3728 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
3729
3730 /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
3731 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
3732
3733 /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
3734 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
3735
3736 /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
3737 rb_define_const(rb_cIOBuffer, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED));
3738
3739 /* Indicates that the memory in the buffer is locked and cannot be resized or freed. See #locked? and #locked for more details. */
3740 rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
3741
3742 /* 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. */
3743 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
3744
3745 /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
3746 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
3747
3748 /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
3749 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
3750
3751 /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
3752 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
3753
3754 /* Refers to the byte order of the host machine. See #get_value for more details. */
3755 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
3756
3757 /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
3758 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
3759
3760 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
3761 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
3762 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
3763 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
3764 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
3765 rb_define_method(rb_cIOBuffer, "shared?", rb_io_buffer_shared_p, 0);
3766 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
3767 rb_define_method(rb_cIOBuffer, "private?", rb_io_buffer_private_p, 0);
3768 rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
3769
3770 // Locking to prevent changes while using pointer:
3771 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
3772 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
3773 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
3774
3775 // Manipulation:
3776 rb_define_method(rb_cIOBuffer, "slice", io_buffer_slice, -1);
3777 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
3778 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
3779 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
3780 rb_define_method(rb_cIOBuffer, "free", rb_io_buffer_free, 0);
3781
3782 rb_include_module(rb_cIOBuffer, rb_mComparable);
3783
3784#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
3785 IO_BUFFER_DEFINE_DATA_TYPE(U8);
3786 IO_BUFFER_DEFINE_DATA_TYPE(S8);
3787
3788 IO_BUFFER_DEFINE_DATA_TYPE(u16);
3789 IO_BUFFER_DEFINE_DATA_TYPE(U16);
3790 IO_BUFFER_DEFINE_DATA_TYPE(s16);
3791 IO_BUFFER_DEFINE_DATA_TYPE(S16);
3792
3793 IO_BUFFER_DEFINE_DATA_TYPE(u32);
3794 IO_BUFFER_DEFINE_DATA_TYPE(U32);
3795 IO_BUFFER_DEFINE_DATA_TYPE(s32);
3796 IO_BUFFER_DEFINE_DATA_TYPE(S32);
3797
3798 IO_BUFFER_DEFINE_DATA_TYPE(u64);
3799 IO_BUFFER_DEFINE_DATA_TYPE(U64);
3800 IO_BUFFER_DEFINE_DATA_TYPE(s64);
3801 IO_BUFFER_DEFINE_DATA_TYPE(S64);
3802
3803 IO_BUFFER_DEFINE_DATA_TYPE(f32);
3804 IO_BUFFER_DEFINE_DATA_TYPE(F32);
3805 IO_BUFFER_DEFINE_DATA_TYPE(f64);
3806 IO_BUFFER_DEFINE_DATA_TYPE(F64);
3807#undef IO_BUFFER_DEFINE_DATA_TYPE
3808
3809 rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1);
3810
3811 // Data access:
3812 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
3813 rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2);
3814 rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1);
3815 rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1);
3816 rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1);
3817 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
3818 rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3);
3819
3820 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
3821
3822 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
3823 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
3824
3825 // Binary buffer manipulations:
3826 rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
3827 rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
3828 rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
3829 rb_define_method(rb_cIOBuffer, "~", io_buffer_not, 0);
3830
3831 rb_define_method(rb_cIOBuffer, "and!", io_buffer_and_inplace, 1);
3832 rb_define_method(rb_cIOBuffer, "or!", io_buffer_or_inplace, 1);
3833 rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
3834 rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
3835
3836 // IO operations:
3837 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
3838 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);
3839 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, -1);
3840 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, -1);
3841}
#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
#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:3676
#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:3251
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition string.c:1643
#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:412
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
#define RB_SYM2ID
Just another name of rb_sym2id.
Definition symbol.h:43
VALUE rb_io_get_io(VALUE io)
Identical to rb_io_check_io(), except it raises exceptions on conversion failures.
Definition io.c:811
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition io.c:2892
#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:488
VALUE rb_str_to_str(VALUE obj)
Identical to rb_check_string_type(), except it raises exceptions in case of conversion failures.
Definition string.c:1704
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:515
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:497
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
#define RB_NO_KEYWORDS
Do not pass keywords.
Definition scan_args.h:69
Scheduler APIs.
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:228
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:598
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:511
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:535
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:573
static bool RB_NIL_P(VALUE obj)
Checks if the given object is nil.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:200
const char * wrap_struct_name
Name of structs of this kind.
Definition rtypeddata.h:207
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