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