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