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