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