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