Ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e0ba6b95ab71a441357ed5484e33498)
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/string.h"
15#include "internal/bits.h"
16#include "internal/error.h"
17
18VALUE rb_cIOBuffer;
19VALUE rb_eIOBufferLockedError;
20VALUE rb_eIOBufferAllocationError;
21VALUE rb_eIOBufferAccessError;
22VALUE rb_eIOBufferInvalidatedError;
23
24size_t RUBY_IO_BUFFER_PAGE_SIZE;
25size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
26
27#ifdef _WIN32
28#else
29#include <unistd.h>
30#include <sys/mman.h>
31#endif
32
34 void *base;
35 size_t size;
36 enum rb_io_buffer_flags flags;
37
38#if defined(_WIN32)
39 HANDLE mapping;
40#endif
41
42 VALUE source;
43};
44
45static inline void *
46io_buffer_map_memory(size_t size)
47{
48#if defined(_WIN32)
49 void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
50
51 if (!base) {
52 rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
53 }
54#else
55 void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
56
57 if (base == MAP_FAILED) {
58 rb_sys_fail("io_buffer_map_memory:mmap");
59 }
60#endif
61
62 return base;
63}
64
65static void
66io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags)
67{
68#if defined(_WIN32)
69 HANDLE file = (HANDLE)_get_osfhandle(descriptor);
70 if (!file) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
71
72 DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
73
74 if (flags & RB_IO_BUFFER_READONLY) {
75 data->flags |= RB_IO_BUFFER_READONLY;
76 }
77 else {
78 protect = PAGE_READWRITE;
79 access = FILE_MAP_WRITE;
80 }
81
82 HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
83 if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
84
85 if (flags & RB_IO_BUFFER_PRIVATE) {
86 access |= FILE_MAP_COPY;
87 data->flags |= RB_IO_BUFFER_PRIVATE;
88 } else {
89 // This buffer refers to external data.
90 data->flags |= RB_IO_BUFFER_EXTERNAL;
91 }
92
93 void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
94
95 if (!base) {
96 CloseHandle(mapping);
97 rb_sys_fail("io_buffer_map_file:MapViewOfFile");
98 }
99
100 data->mapping = mapping;
101#else
102 int protect = PROT_READ, access = 0;
103
104 if (flags & RB_IO_BUFFER_READONLY) {
105 data->flags |= RB_IO_BUFFER_READONLY;
106 }
107 else {
108 protect |= PROT_WRITE;
109 }
110
111 if (flags & RB_IO_BUFFER_PRIVATE) {
112 data->flags |= RB_IO_BUFFER_PRIVATE;
113 }
114 else {
115 // This buffer refers to external data.
116 data->flags |= RB_IO_BUFFER_EXTERNAL;
117 access |= MAP_SHARED;
118 }
119
120 void *base = mmap(NULL, size, protect, access, descriptor, offset);
121
122 if (base == MAP_FAILED) {
123 rb_sys_fail("io_buffer_map_file:mmap");
124 }
125#endif
126
127 data->base = base;
128 data->size = size;
129
130 data->flags |= RB_IO_BUFFER_MAPPED;
131}
132
133static inline void
134io_buffer_unmap(void* base, size_t size)
135{
136#ifdef _WIN32
137 VirtualFree(base, 0, MEM_RELEASE);
138#else
139 munmap(base, size);
140#endif
141}
142
143static void
144io_buffer_experimental(void)
145{
146 static int warned = 0;
147
148 if (warned) return;
149
150 warned = 1;
151
152 if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
154 "IO::Buffer is experimental and both the Ruby and C interface may change in the future!"
155 );
156 }
157}
158
159static void
160io_buffer_zero(struct rb_io_buffer *data)
161{
162 data->base = NULL;
163 data->size = 0;
164#if defined(_WIN32)
165 data->mapping = NULL;
166#endif
167 data->source = Qnil;
168}
169
170static void
171io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
172{
173 if (base) {
174 // If we are provided a pointer, we use it.
175 }
176 else if (size) {
177 // If we are provided a non-zero size, we allocate it:
178 if (flags & RB_IO_BUFFER_INTERNAL) {
179 base = calloc(size, 1);
180 }
181 else if (flags & RB_IO_BUFFER_MAPPED) {
182 base = io_buffer_map_memory(size);
183 }
184
185 if (!base) {
186 rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
187 }
188 } else {
189 // Otherwise we don't do anything.
190 return;
191 }
192
193 data->base = base;
194 data->size = size;
195 data->flags = flags;
196 data->source = source;
197}
198
199static int
200io_buffer_free(struct rb_io_buffer *data)
201{
202 if (data->base) {
203 if (data->flags & RB_IO_BUFFER_INTERNAL) {
204 free(data->base);
205 }
206
207 if (data->flags & RB_IO_BUFFER_MAPPED) {
208 io_buffer_unmap(data->base, data->size);
209 }
210
211 // Previously we had this, but we found out due to the way GC works, we
212 // can't refer to any other Ruby objects here.
213 // if (RB_TYPE_P(data->source, T_STRING)) {
214 // rb_str_unlocktmp(data->source);
215 // }
216
217 data->base = NULL;
218
219#if defined(_WIN32)
220 if (data->mapping) {
221 CloseHandle(data->mapping);
222 data->mapping = NULL;
223 }
224#endif
225 data->size = 0;
226 data->flags = 0;
227 data->source = Qnil;
228
229 return 1;
230 }
231
232 return 0;
233}
234
235void
236rb_io_buffer_type_mark(void *_data)
237{
238 struct rb_io_buffer *data = _data;
239 rb_gc_mark(data->source);
240}
241
242void
243rb_io_buffer_type_free(void *_data)
244{
245 struct rb_io_buffer *data = _data;
246
247 io_buffer_free(data);
248
249 free(data);
250}
251
252size_t
253rb_io_buffer_type_size(const void *_data)
254{
255 const struct rb_io_buffer *data = _data;
256 size_t total = sizeof(struct rb_io_buffer);
257
258 if (data->flags) {
259 total += data->size;
260 }
261
262 return total;
263}
264
265static const rb_data_type_t rb_io_buffer_type = {
266 .wrap_struct_name = "IO::Buffer",
267 .function = {
268 .dmark = rb_io_buffer_type_mark,
269 .dfree = rb_io_buffer_type_free,
270 .dsize = rb_io_buffer_type_size,
271 },
272 .data = NULL,
273 .flags = RUBY_TYPED_FREE_IMMEDIATELY,
274};
275
276VALUE
277rb_io_buffer_type_allocate(VALUE self)
278{
279 struct rb_io_buffer *data = NULL;
280 VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
281
282 io_buffer_zero(data);
283
284 return instance;
285}
286
287static VALUE
288io_buffer_for_make_instance(VALUE klass, VALUE string)
289{
290 VALUE instance = rb_io_buffer_type_allocate(klass);
291
292 struct rb_io_buffer *data = NULL;
293 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
294
295 enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
296
297 if (RB_OBJ_FROZEN(string))
298 flags |= RB_IO_BUFFER_READONLY;
299
300 io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
301
302 return instance;
303}
304
306 VALUE klass;
307 VALUE string;
308 VALUE instance;
309};
310
311static VALUE
312io_buffer_for_yield_instance(VALUE _arguments) {
314
315 rb_str_locktmp(arguments->string);
316
317 arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string);
318
319 return rb_yield(arguments->instance);
320}
321
322static VALUE
323io_buffer_for_yield_instance_ensure(VALUE _arguments)
324{
326
327 if (arguments->instance != Qnil) {
328 rb_io_buffer_free(arguments->instance);
329 }
330
331 rb_str_unlocktmp(arguments->string);
332
333 return Qnil;
334}
335
336/*
337 * call-seq:
338 * IO::Buffer.for(string) -> readonly io_buffer
339 * IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
340 *
341 * Creates a IO::Buffer from the given string's memory. Without a block a
342 * frozen internal copy of the string is created efficiently and used as the
343 * buffer source. When a block is provided, the buffer is associated directly
344 * with the string's internal data and updating the buffer will update the
345 * string.
346 *
347 * Until #free is invoked on the buffer, either explicitly or via the garbage
348 * collector, the source string will be locked and cannot be modified.
349 *
350 * If the string is frozen, it will create a read-only buffer which cannot be
351 * modified.
352 *
353 * string = 'test'
354 * buffer = IO::Buffer.for(string)
355 * buffer.external? #=> true
356 *
357 * buffer.get_string(0, 1)
358 * # => "t"
359 * string
360 * # => "best"
361 *
362 * buffer.resize(100)
363 * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
364 *
365 * IO::Buffer.for(string) do |buffer|
366 * buffer.set_string("T")
367 * string
368 * # => "Test"
369 * end
370 */
371VALUE
372rb_io_buffer_type_for(VALUE klass, VALUE string)
373{
374 StringValue(string);
375
376 // If the string is frozen, both code paths are okay.
377 // If the string is not frozen, if a block is not given, it must be frozen.
378 if (rb_block_given_p()) {
379 struct io_buffer_for_yield_instance_arguments arguments = {
380 .klass = klass,
381 .string = string,
382 .instance = Qnil,
383 };
384
385 return rb_ensure(io_buffer_for_yield_instance, (VALUE)&arguments, io_buffer_for_yield_instance_ensure, (VALUE)&arguments);
386 } else {
387 // This internally returns the source string if it's already frozen.
388 string = rb_str_tmp_frozen_acquire(string);
389 return io_buffer_for_make_instance(klass, string);
390 }
391}
392
393VALUE
394rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
395{
396 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
397
398 struct rb_io_buffer *data = NULL;
399 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
400
401 io_buffer_initialize(data, base, size, flags, Qnil);
402
403 return instance;
404}
405
406VALUE
407rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags)
408{
409 io_buffer_experimental();
410
411 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
412
413 struct rb_io_buffer *data = NULL;
414 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
415
416 int descriptor = rb_io_descriptor(io);
417
418 io_buffer_map_file(data, descriptor, size, offset, flags);
419
420 return instance;
421}
422
423/*
424 * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
425 *
426 * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
427 * +file_io+ should be a +File+ instance, opened for reading.
428 *
429 * Optional +size+ and +offset+ of mapping can be specified.
430 *
431 * By default, the buffer would be immutable (read only); to create a writable
432 * mapping, you need to open a file in read-write mode, and explicitly pass
433 * +flags+ argument without IO::Buffer::IMMUTABLE.
434 *
435 * File.write('test.txt', 'test')
436 *
437 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
438 * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
439 *
440 * buffer.readonly? # => true
441 *
442 * buffer.get_string
443 * # => "test"
444 *
445 * buffer.set_string('b', 0)
446 * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
447 *
448 * # create read/write mapping: length 4 bytes, offset 0, flags 0
449 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
450 * buffer.set_string('b', 0)
451 * # => 1
452 *
453 * # Check it
454 * File.read('test.txt')
455 * # => "best"
456 *
457 * Note that some operating systems may not have cache coherency between mapped
458 * buffers and file reads.
459 *
460 */
461static VALUE
462io_buffer_map(int argc, VALUE *argv, VALUE klass)
463{
464 if (argc < 1 || argc > 4) {
465 rb_error_arity(argc, 2, 4);
466 }
467
468 // We might like to handle a string path?
469 VALUE io = argv[0];
470
471 size_t size;
472 if (argc >= 2 && !RB_NIL_P(argv[1])) {
473 size = RB_NUM2SIZE(argv[1]);
474 }
475 else {
476 off_t file_size = rb_file_size(io);
477
478 // Compiler can confirm that we handled file_size < 0 case:
479 if (file_size < 0) {
480 rb_raise(rb_eArgError, "Invalid negative file size!");
481 }
482 // Here, we assume that file_size is positive:
483 else if ((uintmax_t)file_size > SIZE_MAX) {
484 rb_raise(rb_eArgError, "File larger than address space!");
485 }
486 else {
487 // This conversion should be safe:
488 size = (size_t)file_size;
489 }
490 }
491
492 off_t offset = 0;
493 if (argc >= 3) {
494 offset = NUM2OFFT(argv[2]);
495 }
496
497 enum rb_io_buffer_flags flags = 0;
498 if (argc >= 4) {
499 flags = RB_NUM2UINT(argv[3]);
500 }
501
502 return rb_io_buffer_map(io, size, offset, flags);
503}
504
505// Compute the optimal allocation flags for a buffer of the given size.
506static inline enum rb_io_buffer_flags
507io_flags_for_size(size_t size)
508{
509 if (size >= RUBY_IO_BUFFER_PAGE_SIZE) {
510 return RB_IO_BUFFER_MAPPED;
511 }
512
513 return RB_IO_BUFFER_INTERNAL;
514}
515
516/*
517 * call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
518 *
519 * Create a new zero-filled IO::Buffer of +size+ bytes.
520 * By default, the buffer will be _internal_: directly allocated chunk
521 * of the memory. But if the requested +size+ is more than OS-specific
522 * IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
523 * virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
524 * on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
525 * as a second parameter.
526 *
527 * Examples
528 *
529 * buffer = IO::Buffer.new(4)
530 * # =>
531 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
532 * # 0x00000000 00 00 00 00 ....
533 *
534 * buffer.get_string(0, 1) # => "\x00"
535 *
536 * buffer.set_string("test")
537 * buffer
538 * # =>
539 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
540 * # 0x00000000 74 65 73 74 test
541 *
542 */
543VALUE
544rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
545{
546 io_buffer_experimental();
547
548 if (argc < 0 || argc > 2) {
549 rb_error_arity(argc, 0, 2);
550 }
551
552 struct rb_io_buffer *data = NULL;
553 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
554
555 size_t size;
556
557 if (argc > 0) {
558 size = RB_NUM2SIZE(argv[0]);
559 } else {
560 size = RUBY_IO_BUFFER_DEFAULT_SIZE;
561 }
562
563 enum rb_io_buffer_flags flags = 0;
564 if (argc >= 2) {
565 flags = RB_NUM2UINT(argv[1]);
566 }
567 else {
568 flags |= io_flags_for_size(size);
569 }
570
571 io_buffer_initialize(data, NULL, size, flags, Qnil);
572
573 return self;
574}
575
576static int
577io_buffer_validate_slice(VALUE source, void *base, size_t size)
578{
579 void *source_base = NULL;
580 size_t source_size = 0;
581
582 if (RB_TYPE_P(source, T_STRING)) {
583 RSTRING_GETMEM(source, source_base, source_size);
584 }
585 else {
586 rb_io_buffer_get_bytes(source, &source_base, &source_size);
587 }
588
589 // Source is invalid:
590 if (source_base == NULL) return 0;
591
592 // Base is out of range:
593 if (base < source_base) return 0;
594
595 const void *source_end = (char*)source_base + source_size;
596 const void *end = (char*)base + size;
597
598 // End is out of range:
599 if (end > source_end) return 0;
600
601 // It seems okay:
602 return 1;
603}
604
605static int
606io_buffer_validate(struct rb_io_buffer *data)
607{
608 if (data->source != Qnil) {
609 // Only slices incur this overhead, unfortunately... better safe than sorry!
610 return io_buffer_validate_slice(data->source, data->base, data->size);
611 }
612 else {
613 return 1;
614 }
615}
616
617/*
618 * call-seq: to_s -> string
619 *
620 * Short representation of the buffer. It includes the address, size and
621 * symbolic flags. This format is subject to change.
622 *
623 * puts IO::Buffer.new(4) # uses to_s internally
624 * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
625 *
626 */
627VALUE
628rb_io_buffer_to_s(VALUE self)
629{
630 struct rb_io_buffer *data = NULL;
631 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
632
633 VALUE result = rb_str_new_cstr("#<");
634
635 rb_str_append(result, rb_class_name(CLASS_OF(self)));
636 rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);
637
638 if (data->base == NULL) {
639 rb_str_cat2(result, " NULL");
640 }
641
642 if (data->flags & RB_IO_BUFFER_EXTERNAL) {
643 rb_str_cat2(result, " EXTERNAL");
644 }
645
646 if (data->flags & RB_IO_BUFFER_INTERNAL) {
647 rb_str_cat2(result, " INTERNAL");
648 }
649
650 if (data->flags & RB_IO_BUFFER_MAPPED) {
651 rb_str_cat2(result, " MAPPED");
652 }
653
654 if (data->flags & RB_IO_BUFFER_LOCKED) {
655 rb_str_cat2(result, " LOCKED");
656 }
657
658 if (data->flags & RB_IO_BUFFER_READONLY) {
659 rb_str_cat2(result, " READONLY");
660 }
661
662 if (data->source != Qnil) {
663 rb_str_cat2(result, " SLICE");
664 }
665
666 if (!io_buffer_validate(data)) {
667 rb_str_cat2(result, " INVALID");
668 }
669
670 return rb_str_cat2(result, ">");
671}
672
673static VALUE
674io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first)
675{
676 char *text = alloca(width+1);
677 text[width] = '\0';
678
679 for (size_t offset = 0; offset < size; offset += width) {
680 memset(text, '\0', width);
681 if (first) {
682 rb_str_catf(string, "0x%08zx ", offset);
683 first = 0;
684 } else {
685 rb_str_catf(string, "\n0x%08zx ", offset);
686 }
687
688 for (size_t i = 0; i < width; i += 1) {
689 if (offset+i < size) {
690 unsigned char value = ((unsigned char*)base)[offset+i];
691
692 if (value < 127 && isprint(value)) {
693 text[i] = (char)value;
694 }
695 else {
696 text[i] = '.';
697 }
698
699 rb_str_catf(string, " %02x", value);
700 }
701 else {
702 rb_str_cat2(string, " ");
703 }
704 }
705
706 rb_str_catf(string, " %s", text);
707 }
708
709 return string;
710}
711
712static VALUE
713rb_io_buffer_hexdump(VALUE self)
714{
715 struct rb_io_buffer *data = NULL;
716 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
717
718 VALUE result = Qnil;
719
720 if (io_buffer_validate(data) && data->base) {
721 result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);
722
723 io_buffer_hexdump(result, 16, data->base, data->size, 1);
724 }
725
726 return result;
727}
728
729VALUE
730rb_io_buffer_inspect(VALUE self)
731{
732 struct rb_io_buffer *data = NULL;
733 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
734
735 VALUE result = rb_io_buffer_to_s(self);
736
737 if (io_buffer_validate(data)) {
738 // Limit the maximum size genearted by inspect.
739 if (data->size <= 256) {
740 io_buffer_hexdump(result, 16, data->base, data->size, 0);
741 }
742 }
743
744 return result;
745}
746
747/*
748 * call-seq: size -> integer
749 *
750 * Returns the size of the buffer that was explicitly set (on creation with ::new
751 * or on #resize), or deduced on buffer's creation from string or file.
752 *
753 */
754VALUE
755rb_io_buffer_size(VALUE self)
756{
757 struct rb_io_buffer *data = NULL;
758 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
759
760 return SIZET2NUM(data->size);
761}
762
763/*
764 * call-seq: valid? -> true or false
765 *
766 * Returns whether the buffer data is accessible.
767 *
768 * A buffer becomes invalid if it is a slice of another buffer which has been
769 * freed.
770 *
771 */
772static VALUE
773rb_io_buffer_valid_p(VALUE self)
774{
775 struct rb_io_buffer *data = NULL;
776 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
777
778 return RBOOL(io_buffer_validate(data));
779}
780
781/*
782 * call-seq: null? -> true or false
783 *
784 * If the buffer was freed with #free or was never allocated in the first
785 * place.
786 *
787 */
788static VALUE
789rb_io_buffer_null_p(VALUE self)
790{
791 struct rb_io_buffer *data = NULL;
792 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
793
794 return RBOOL(data->base == NULL);
795}
796
797/*
798 * call-seq: external? -> true or false
799 *
800 * If the buffer is _external_, meaning it references from memory which is not
801 * allocated or mapped by the buffer itself.
802 *
803 * A buffer created using ::for has an external reference to the string's
804 * memory.
805 *
806 * External buffer can't be resized.
807 *
808 */
809static VALUE
810rb_io_buffer_empty_p(VALUE self)
811{
812 struct rb_io_buffer *data = NULL;
813 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
814
815 return RBOOL(data->size == 0);
816}
817
818static VALUE
819rb_io_buffer_external_p(VALUE self)
820{
821 struct rb_io_buffer *data = NULL;
822 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
823
824 return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
825}
826
827/*
828 * call-seq: internal? -> true or false
829 *
830 * If the buffer is _internal_, meaning it references memory allocated by the
831 * buffer itself.
832 *
833 * An internal buffer is not associated with any external memory (e.g. string)
834 * or file mapping.
835 *
836 * Internal buffers are created using ::new and is the default when the
837 * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
838 * requested to be mapped on creation.
839 *
840 * Internal buffers can be resized, and such an operation will typically
841 * invalidate all slices, but not always.
842 *
843 */
844static VALUE
845rb_io_buffer_internal_p(VALUE self)
846{
847 struct rb_io_buffer *data = NULL;
848 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
849
850 return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
851}
852
853/*
854 * call-seq: mapped? -> true or false
855 *
856 * If the buffer is _mapped_, meaning it references memory mapped by the
857 * buffer.
858 *
859 * Mapped buffers are either anonymous, if created by ::new with the
860 * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
861 * or backed by a file if created with ::map.
862 *
863 * Mapped buffers can usually be resized, and such an operation will typically
864 * invalidate all slices, but not always.
865 *
866 */
867static VALUE
868rb_io_buffer_mapped_p(VALUE self)
869{
870 struct rb_io_buffer *data = NULL;
871 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
872
873 return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
874}
875
876/*
877 * call-seq: locked? -> true or false
878 *
879 * If the buffer is _locked_, meaning it is inside #locked block execution.
880 * Locked buffer can't be resized or freed, and another lock can't be acquired
881 * on it.
882 *
883 * Locking is not thread safe, but is a semantic used to ensure buffers don't
884 * move while being used by a system call.
885 *
886 * buffer.locked do
887 * buffer.write(io) # theoretical system call interface
888 * end
889 *
890 */
891static VALUE
892rb_io_buffer_locked_p(VALUE self)
893{
894 struct rb_io_buffer *data = NULL;
895 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
896
897 return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
898}
899
900/*
901 * call-seq: readonly? -> true or false
902 *
903 * If the buffer is _read only_, meaning the buffer cannot be modified using
904 * #set_value, #set_string or #copy and similar.
905 *
906 * Frozen strings and read-only files create read-only buffers.
907 *
908 */
909int
910rb_io_buffer_readonly_p(VALUE self)
911{
912 struct rb_io_buffer *data = NULL;
913 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
914
915 return data->flags & RB_IO_BUFFER_READONLY;
916}
917
918static VALUE
919io_buffer_readonly_p(VALUE self)
920{
921 return RBOOL(rb_io_buffer_readonly_p(self));
922}
923
924VALUE
925rb_io_buffer_lock(VALUE self)
926{
927 struct rb_io_buffer *data = NULL;
928 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
929
930 if (data->flags & RB_IO_BUFFER_LOCKED) {
931 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
932 }
933
934 data->flags |= RB_IO_BUFFER_LOCKED;
935
936 return self;
937}
938
939VALUE
940rb_io_buffer_unlock(VALUE self)
941{
942 struct rb_io_buffer *data = NULL;
943 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
944
945 if (!(data->flags & RB_IO_BUFFER_LOCKED)) {
946 rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
947 }
948
949 data->flags &= ~RB_IO_BUFFER_LOCKED;
950
951 return self;
952}
953
954int
955rb_io_buffer_try_unlock(VALUE self)
956{
957 struct rb_io_buffer *data = NULL;
958 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
959
960 if (data->flags & RB_IO_BUFFER_LOCKED) {
961 data->flags &= ~RB_IO_BUFFER_LOCKED;
962 return 1;
963 }
964
965 return 0;
966}
967
968/*
969 * call-seq: locked { ... }
970 *
971 * Allows to process a buffer in exclusive way, for concurrency-safety. While
972 * the block is performed, the buffer is considered locked, and no other code
973 * can enter the lock. Also, locked buffer can't be changed with #resize or
974 * #free.
975 *
976 * buffer = IO::Buffer.new(4)
977 * buffer.locked? #=> false
978 *
979 * Fiber.schedule do
980 * buffer.locked do
981 * buffer.write(io) # theoretical system call interface
982 * end
983 * end
984 *
985 * Fiber.schedule do
986 * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
987 * buffer.locked do
988 * buffer.set_string(...)
989 * end
990 * end
991 *
992 * The following operations acquire a lock: #resize, #free.
993 *
994 * Locking is not thread safe. It is designed as a safety net around
995 * non-blocking system calls. You can only share a buffer between threads with
996 * appropriate synchronisation techniques.
997 */
998VALUE
999rb_io_buffer_locked(VALUE self)
1000{
1001 struct rb_io_buffer *data = NULL;
1002 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1003
1004 if (data->flags & RB_IO_BUFFER_LOCKED) {
1005 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
1006 }
1007
1008 data->flags |= RB_IO_BUFFER_LOCKED;
1009
1010 VALUE result = rb_yield(self);
1011
1012 data->flags &= ~RB_IO_BUFFER_LOCKED;
1013
1014 return result;
1015}
1016
1017/*
1018 * call-seq: free -> self
1019 *
1020 * If the buffer references memory, release it back to the operating system.
1021 * * for a _mapped_ buffer (e.g. from file): unmap.
1022 * * for a buffer created from scratch: free memory.
1023 * * for a buffer created from string: undo the association.
1024 *
1025 * After the buffer is freed, no further operations can't be performed on it.
1026 *
1027 * buffer = IO::Buffer.for('test')
1028 * buffer.free
1029 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
1030 *
1031 * buffer.get_value(:U8, 0)
1032 * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
1033 *
1034 * buffer.get_string
1035 * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
1036 *
1037 * buffer.null?
1038 * # => true
1039 *
1040 * You can resize a freed buffer to re-allocate it.
1041 *
1042 */
1043VALUE
1044rb_io_buffer_free(VALUE self)
1045{
1046 struct rb_io_buffer *data = NULL;
1047 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1048
1049 if (data->flags & RB_IO_BUFFER_LOCKED) {
1050 rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
1051 }
1052
1053 io_buffer_free(data);
1054
1055 return self;
1056}
1057
1058static inline void
1059io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length)
1060{
1061 if (offset + length > data->size) {
1062 rb_raise(rb_eArgError, "Specified offset+length exceeds data size!");
1063 }
1064}
1065
1066/*
1067 * call-seq: slice(offset, length) -> io_buffer
1068 *
1069 * Produce another IO::Buffer which is a slice (or view into) the current one
1070 * starting at +offset+ bytes and going for +length+ bytes.
1071 *
1072 * The slicing happens without copying of memory, and the slice keeps being
1073 * associated with the original buffer's source (string, or file), if any.
1074 *
1075 * Raises RuntimeError if the <tt>offset+length<tt> is out of the current
1076 * buffer's bounds.
1077 *
1078 * string = 'test'
1079 * buffer = IO::Buffer.for(string)
1080 *
1081 * slice = buffer.slice(1, 2)
1082 * # =>
1083 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1084 * # 0x00000000 65 73 es
1085 *
1086 * # Put "o" into 0s position of the slice
1087 * slice.set_string('o', 0)
1088 * slice
1089 * # =>
1090 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1091 * # 0x00000000 6f 73 os
1092 *
1093 *
1094 * # it is also visible at position 1 of the original buffer
1095 * buffer
1096 * # =>
1097 * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
1098 * # 0x00000000 74 6f 73 74 tost
1099 *
1100 * # ...and original string
1101 * string
1102 * # => tost
1103 *
1104 */
1105VALUE
1106rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
1107{
1108 // TODO fail on negative offets/lengths.
1109 size_t offset = NUM2SIZET(_offset);
1110 size_t length = NUM2SIZET(_length);
1111
1112 struct rb_io_buffer *data = NULL;
1113 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1114
1115 io_buffer_validate_range(data, offset, length);
1116
1117 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1118 struct rb_io_buffer *slice = NULL;
1119 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
1120
1121 slice->base = (char*)data->base + offset;
1122 slice->size = length;
1123
1124 // The source should be the root buffer:
1125 if (data->source != Qnil)
1126 slice->source = data->source;
1127 else
1128 slice->source = self;
1129
1130 return instance;
1131}
1132
1133int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
1134{
1135 struct rb_io_buffer *data = NULL;
1136 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1137
1138 if (io_buffer_validate(data)) {
1139 if (data->base) {
1140 *base = data->base;
1141 *size = data->size;
1142
1143 return data->flags;
1144 }
1145 }
1146
1147 *base = NULL;
1148 *size = 0;
1149
1150 return 0;
1151}
1152
1153static void
1154io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size)
1155{
1156 if (data->flags & RB_IO_BUFFER_READONLY) {
1157 rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
1158 }
1159
1160 if (!io_buffer_validate(data)) {
1161 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
1162 }
1163
1164 if (data->base) {
1165 *base = data->base;
1166 *size = data->size;
1167
1168 return;
1169 }
1170
1171 rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1172}
1173
1174void
1175rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
1176{
1177 struct rb_io_buffer *data = NULL;
1178 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1179
1180 io_buffer_get_bytes_for_writing(data, base, size);
1181}
1182
1183static void
1184io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, size_t *size)
1185{
1186 if (!io_buffer_validate(data)) {
1187 rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
1188 }
1189
1190 if (data->base) {
1191 *base = data->base;
1192 *size = data->size;
1193
1194 return;
1195 }
1196
1197 rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1198}
1199
1200void
1201rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
1202{
1203 struct rb_io_buffer *data = NULL;
1204 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1205
1206 io_buffer_get_bytes_for_reading(data, base, size);
1207}
1208
1209/*
1210 * call-seq: transfer -> new_io_buffer
1211 *
1212 * Transfers ownership to a new buffer, deallocating the current one.
1213 *
1214 * buffer = IO::Buffer.new('test')
1215 * other = buffer.transfer
1216 * other
1217 * # =>
1218 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1219 * # 0x00000000 74 65 73 74 test
1220 * buffer
1221 * # =>
1222 * # #<IO::Buffer 0x0000000000000000+0 NULL>
1223 * buffer.null?
1224 * # => true
1225 *
1226 */
1227VALUE
1228rb_io_buffer_transfer(VALUE self)
1229{
1230 struct rb_io_buffer *data = NULL;
1231 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1232
1233 if (data->flags & RB_IO_BUFFER_LOCKED) {
1234 rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
1235 }
1236
1237 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1238 struct rb_io_buffer *transferred;
1239 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
1240
1241 *transferred = *data;
1242 io_buffer_zero(data);
1243
1244 return instance;
1245}
1246
1247static void
1248io_buffer_resize_clear(struct rb_io_buffer *data, void* base, size_t size)
1249{
1250 if (size > data->size) {
1251 memset((unsigned char*)base+data->size, 0, size - data->size);
1252 }
1253}
1254
1255static void
1256io_buffer_resize_copy(struct rb_io_buffer *data, size_t size)
1257{
1258 // Slow path:
1259 struct rb_io_buffer resized;
1260 io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
1261
1262 if (data->base) {
1263 size_t preserve = data->size;
1264 if (preserve > size) preserve = size;
1265 memcpy(resized.base, data->base, preserve);
1266
1267 io_buffer_resize_clear(data, resized.base, size);
1268 }
1269
1270 io_buffer_free(data);
1271 *data = resized;
1272}
1273
1274void
1275rb_io_buffer_resize(VALUE self, size_t size)
1276{
1277 struct rb_io_buffer *data = NULL;
1278 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1279
1280 if (data->flags & RB_IO_BUFFER_LOCKED) {
1281 rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
1282 }
1283
1284 if (data->base == NULL) {
1285 io_buffer_initialize(data, NULL, size, io_flags_for_size(size), Qnil);
1286 return;
1287 }
1288
1289 if (data->flags & RB_IO_BUFFER_EXTERNAL) {
1290 rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
1291 }
1292
1293#ifdef MREMAP_MAYMOVE
1294 if (data->flags & RB_IO_BUFFER_MAPPED) {
1295 void *base = mremap(data->base, data->size, size, MREMAP_MAYMOVE);
1296
1297 if (base == MAP_FAILED) {
1298 rb_sys_fail("rb_io_buffer_resize:mremap");
1299 }
1300
1301 io_buffer_resize_clear(data, base, size);
1302
1303 data->base = base;
1304 data->size = size;
1305
1306 return;
1307 }
1308#endif
1309
1310 if (data->flags & RB_IO_BUFFER_INTERNAL) {
1311 void *base = realloc(data->base, size);
1312
1313 if (!base) {
1314 rb_sys_fail("rb_io_buffer_resize:realloc");
1315 }
1316
1317 io_buffer_resize_clear(data, base, size);
1318
1319 data->base = base;
1320 data->size = size;
1321
1322 return;
1323 }
1324
1325 io_buffer_resize_copy(data, size);
1326}
1327
1328/*
1329 * call-seq: resize(new_size) -> self
1330 *
1331 * Resizes a buffer to a +new_size+ bytes, preserving its content.
1332 * Depending on the old and new size, the memory area associated with
1333 * the buffer might be either extended, or rellocated at different
1334 * address with content being copied.
1335 *
1336 * buffer = IO::Buffer.new(4)
1337 * buffer.set_string("test", 0)
1338 * buffer.resize(8) # resize to 8 bytes
1339 * # =>
1340 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1341 * # 0x00000000 74 65 73 74 00 00 00 00 test....
1342 *
1343 * External buffer (created with ::for), and locked buffer
1344 * can not be resized.
1345 *
1346 */
1347static VALUE
1348io_buffer_resize(VALUE self, VALUE size)
1349{
1350 rb_io_buffer_resize(self, NUM2SIZET(size));
1351
1352 return self;
1353}
1354
1355/*
1356 * call-seq: <=>(other) -> true or false
1357 *
1358 * Buffers are compared by size and exact contents of the memory they are
1359 * referencing using +memcmp+.
1360 *
1361 */
1362static VALUE
1363rb_io_buffer_compare(VALUE self, VALUE other)
1364{
1365 const void *ptr1, *ptr2;
1366 size_t size1, size2;
1367
1368 rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
1369 rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
1370
1371 if (size1 < size2) {
1372 return RB_INT2NUM(-1);
1373 }
1374
1375 if (size1 > size2) {
1376 return RB_INT2NUM(1);
1377 }
1378
1379 return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
1380}
1381
1382static void
1383io_buffer_validate_type(size_t size, size_t offset)
1384{
1385 if (offset > size) {
1386 rb_raise(rb_eArgError, "Type extends beyond end of buffer!");
1387 }
1388}
1389
1390// Lower case: little endian.
1391// Upper case: big endian (network endian).
1392//
1393// :U8 | unsigned 8-bit integer.
1394// :S8 | signed 8-bit integer.
1395//
1396// :u16, :U16 | unsigned 16-bit integer.
1397// :s16, :S16 | signed 16-bit integer.
1398//
1399// :u32, :U32 | unsigned 32-bit integer.
1400// :s32, :S32 | signed 32-bit integer.
1401//
1402// :u64, :U64 | unsigned 64-bit integer.
1403// :s64, :S64 | signed 64-bit integer.
1404//
1405// :f32, :F32 | 32-bit floating point number.
1406// :f64, :F64 | 64-bit floating point number.
1407
1408#define ruby_swap8(value) value
1409
1410union swapf32 {
1411 uint32_t integral;
1412 float value;
1413};
1414
1415static float
1416ruby_swapf32(float value)
1417{
1418 union swapf32 swap = {.value = value};
1419 swap.integral = ruby_swap32(swap.integral);
1420 return swap.value;
1421}
1422
1423union swapf64 {
1424 uint64_t integral;
1425 double value;
1426};
1427
1428static double
1429ruby_swapf64(double value)
1430{
1431 union swapf64 swap = {.value = value};
1432 swap.integral = ruby_swap64(swap.integral);
1433 return swap.value;
1434}
1435
1436#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1437static ID RB_IO_BUFFER_TYPE_##name; \
1438\
1439static VALUE \
1440io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1441{ \
1442 io_buffer_validate_type(size, *offset + sizeof(type)); \
1443 type value; \
1444 memcpy(&value, (char*)base + *offset, sizeof(type)); \
1445 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1446 *offset += sizeof(type); \
1447 return wrap(value); \
1448} \
1449\
1450static void \
1451io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
1452{ \
1453 io_buffer_validate_type(size, *offset + sizeof(type)); \
1454 type value = unwrap(_value); \
1455 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1456 memcpy((char*)base + *offset, &value, sizeof(type)); \
1457 *offset += sizeof(type); \
1458}
1459
1460DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
1461DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
1462
1463DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1464DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1465DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1466DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1467
1468DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1469DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1470DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1471DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1472
1473DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1474DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1475DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1476DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1477
1478DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1479DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1480DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1481DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1482#undef DECLARE_TYPE
1483
1484VALUE
1485rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
1486{
1487#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset);
1488 READ_TYPE(U8)
1489 READ_TYPE(S8)
1490
1491 READ_TYPE(u16)
1492 READ_TYPE(U16)
1493 READ_TYPE(s16)
1494 READ_TYPE(S16)
1495
1496 READ_TYPE(u32)
1497 READ_TYPE(U32)
1498 READ_TYPE(s32)
1499 READ_TYPE(S32)
1500
1501 READ_TYPE(u64)
1502 READ_TYPE(U64)
1503 READ_TYPE(s64)
1504 READ_TYPE(S64)
1505
1506 READ_TYPE(f32)
1507 READ_TYPE(F32)
1508 READ_TYPE(f64)
1509 READ_TYPE(F64)
1510#undef READ_TYPE
1511
1512 rb_raise(rb_eArgError, "Invalid type name!");
1513}
1514
1515/*
1516 * call-seq: get_value(type, offset) -> numeric
1517 *
1518 * Read from buffer a value of +type+ at +offset+. +type+ should be one
1519 * of symbols:
1520 *
1521 * * +:U8+: unsigned integer, 1 byte
1522 * * +:S8+: signed integer, 1 byte
1523 * * +:u16+: unsigned integer, 2 bytes, little-endian
1524 * * +:U16+: unsigned integer, 2 bytes, big-endian
1525 * * +:s16+: signed integer, 2 bytes, little-endian
1526 * * +:S16+: signed integer, 2 bytes, big-endian
1527 * * +:u32+: unsigned integer, 4 bytes, little-endian
1528 * * +:U32+: unsigned integer, 4 bytes, big-endian
1529 * * +:s32+: signed integer, 4 bytes, little-endian
1530 * * +:S32+: signed integer, 4 bytes, big-endian
1531 * * +:u64+: unsigned integer, 8 bytes, little-endian
1532 * * +:U64+: unsigned integer, 8 bytes, big-endian
1533 * * +:s64+: signed integer, 8 bytes, little-endian
1534 * * +:S64+: signed integer, 8 bytes, big-endian
1535 * * +:f32+: float, 4 bytes, little-endian
1536 * * +:F32+: float, 4 bytes, big-endian
1537 * * +:f64+: double, 8 bytes, little-endian
1538 * * +:F64+: double, 8 bytes, big-endian
1539 *
1540 * Example:
1541 *
1542 * string = [1.5].pack('f')
1543 * # => "\x00\x00\xC0?"
1544 * IO::Buffer.for(string).get_value(:f32, 0)
1545 * # => 1.5
1546 *
1547 */
1548static VALUE
1549io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
1550{
1551 const void *base;
1552 size_t size;
1553 size_t offset = NUM2SIZET(_offset);
1554
1555 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
1556
1557 return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
1558}
1559
1560void
1561rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value)
1562{
1563#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;}
1564 WRITE_TYPE(U8)
1565 WRITE_TYPE(S8)
1566
1567 WRITE_TYPE(u16)
1568 WRITE_TYPE(U16)
1569 WRITE_TYPE(s16)
1570 WRITE_TYPE(S16)
1571
1572 WRITE_TYPE(u32)
1573 WRITE_TYPE(U32)
1574 WRITE_TYPE(s32)
1575 WRITE_TYPE(S32)
1576
1577 WRITE_TYPE(u64)
1578 WRITE_TYPE(U64)
1579 WRITE_TYPE(s64)
1580 WRITE_TYPE(S64)
1581
1582 WRITE_TYPE(f32)
1583 WRITE_TYPE(F32)
1584 WRITE_TYPE(f64)
1585 WRITE_TYPE(F64)
1586#undef WRITE_TYPE
1587
1588 rb_raise(rb_eArgError, "Invalid type name!");
1589}
1590
1591/*
1592 * call-seq: set_value(type, offset, value) -> offset
1593 *
1594 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
1595 * symbols described in #get_value.
1596 *
1597 * buffer = IO::Buffer.new(8)
1598 * # =>
1599 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1600 * # 0x00000000 00 00 00 00 00 00 00 00
1601 * buffer.set_value(:U8, 1, 111)
1602 * # => 1
1603 * buffer
1604 * # =>
1605 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1606 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
1607 *
1608 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
1609 *
1610 * buffer = IO::Buffer.new(8)
1611 * buffer.set_value(:U32, 0, 2.5)
1612 * buffer
1613 * # =>
1614 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1615 * # 0x00000000 00 00 00 02 00 00 00 00
1616 * # ^^ the same as if we'd pass just integer 2
1617 */
1618static VALUE
1619io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
1620{
1621 void *base;
1622 size_t size;
1623 size_t offset = NUM2SIZET(_offset);
1624
1625 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1626
1627 rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);
1628
1629 return SIZET2NUM(offset);
1630}
1631
1632static void
1633io_buffer_memcpy(struct rb_io_buffer *data, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
1634{
1635 void *base;
1636 size_t size;
1637 io_buffer_get_bytes_for_writing(data, &base, &size);
1638
1639 io_buffer_validate_range(data, offset, length);
1640
1641 if (source_offset + length > source_size) {
1642 rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
1643 }
1644
1645 memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
1646}
1647
1648// (offset, length, source_offset) -> length
1649static VALUE
1650io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t source_size, int argc, VALUE *argv)
1651{
1652 size_t offset;
1653 size_t length;
1654 size_t source_offset;
1655
1656 // The offset we copy into the buffer:
1657 if (argc >= 1) {
1658 offset = NUM2SIZET(argv[0]);
1659 } else {
1660 offset = 0;
1661 }
1662
1663 // The offset we start from within the string:
1664 if (argc >= 3) {
1665 source_offset = NUM2SIZET(argv[2]);
1666
1667 if (source_offset > source_size) {
1668 rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
1669 }
1670 } else {
1671 source_offset = 0;
1672 }
1673
1674 // The length we are going to copy:
1675 if (argc >= 2 && !RB_NIL_P(argv[1])) {
1676 length = NUM2SIZET(argv[1]);
1677 } else {
1678 // Default to the source offset -> source size:
1679 length = source_size - source_offset;
1680 }
1681
1682 io_buffer_memcpy(data, offset, source_base, source_offset, source_size, length);
1683
1684 return SIZET2NUM(length);
1685}
1686
1687/*
1688 * call-seq:
1689 * copy(source, [offset, [length, [source_offset]]]) -> size
1690 *
1691 * Efficiently copy data from a source IO::Buffer into the buffer,
1692 * at +offset+ using +memcpy+. For copying String instances, see #set_string.
1693 *
1694 * buffer = IO::Buffer.new(32)
1695 * # =>
1696 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
1697 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1698 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1699 *
1700 * buffer.copy(IO::Buffer.for("test"), 8)
1701 * # => 4 -- size of data copied
1702 * buffer
1703 * # =>
1704 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
1705 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
1706 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1707 *
1708 * #copy can be used to put data into strings associated with buffer:
1709 *
1710 * string= "data: "
1711 * # => "data: "
1712 * buffer = IO::Buffer.for(str)
1713 * buffer.copy(IO::Buffer.for("test"), 5)
1714 * # => 4
1715 * string
1716 * # => "data:test"
1717 *
1718 * Attempt to copy into a read-only buffer will fail:
1719 *
1720 * File.write('test.txt', 'test')
1721 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
1722 * buffer.copy(IO::Buffer.for("test"), 8)
1723 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
1724 *
1725 * See ::map for details of creation of mutable file mappings, this will
1726 * work:
1727 *
1728 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
1729 * buffer.copy("boom", 0)
1730 * # => 4
1731 * File.read('test.txt')
1732 * # => "boom"
1733 *
1734 * Attempt to copy the data which will need place outside of buffer's
1735 * bounds will fail:
1736 *
1737 * buffer = IO::Buffer.new(2)
1738 * buffer.copy('test', 0)
1739 * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
1740 *
1741 */
1742static VALUE
1743io_buffer_copy(int argc, VALUE *argv, VALUE self)
1744{
1745 if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1746
1747 struct rb_io_buffer *data = NULL;
1748 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1749
1750 VALUE source = argv[0];
1751 const void *source_base;
1752 size_t source_size;
1753
1754 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
1755
1756 return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1757}
1758
1759/*
1760 * call-seq: get_string([offset, [length, [encoding]]]) -> string
1761 *
1762 * Read a chunk or all of the buffer into a string, in the specified
1763 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
1764 *
1765 *
1766 * buffer = IO::Buffer.for('test')
1767 * buffer.get_string
1768 * # => "test"
1769 * buffer.get_string(2)
1770 * # => "st"
1771 * buffer.get_string(2, 1)
1772 * # => "s"
1773 *
1774 */
1775static VALUE
1776io_buffer_get_string(int argc, VALUE *argv, VALUE self)
1777{
1778 if (argc > 3) rb_error_arity(argc, 0, 3);
1779
1780 struct rb_io_buffer *data = NULL;
1781 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1782
1783 const void *base;
1784 size_t size;
1785 io_buffer_get_bytes_for_reading(data, &base, &size);
1786
1787 size_t offset = 0;
1788 size_t length = size;
1789 rb_encoding *encoding = rb_ascii8bit_encoding();
1790
1791 if (argc >= 1) {
1792 offset = NUM2SIZET(argv[0]);
1793 }
1794
1795 if (argc >= 2 && !RB_NIL_P(argv[1])) {
1796 length = NUM2SIZET(argv[1]);
1797 } else {
1798 length = size - offset;
1799 }
1800
1801 if (argc >= 3) {
1802 encoding = rb_find_encoding(argv[2]);
1803 }
1804
1805 io_buffer_validate_range(data, offset, length);
1806
1807 return rb_enc_str_new((const char*)base + offset, length, encoding);
1808}
1809
1810static VALUE
1811io_buffer_set_string(int argc, VALUE *argv, VALUE self)
1812{
1813 if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1814
1815 struct rb_io_buffer *data = NULL;
1816 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1817
1818 VALUE string = rb_str_to_str(argv[0]);
1819
1820 const void *source_base = RSTRING_PTR(string);
1821 size_t source_size = RSTRING_LEN(string);
1822
1823 return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1824}
1825
1826void
1827rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
1828{
1829 void *base;
1830 size_t size;
1831
1832 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1833
1834 if (offset + length > size) {
1835 rb_raise(rb_eArgError, "The given offset + length out of bounds!");
1836 }
1837
1838 memset((char*)base + offset, value, length);
1839}
1840
1841/*
1842 * call-seq: clear(value = 0, [offset, [length]]) -> self
1843 *
1844 * Fill buffer with +value+, starting with +offset+ and going for +length+
1845 * bytes.
1846 *
1847 * buffer = IO::Buffer.for('test')
1848 * # =>
1849 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1850 * # 0x00000000 74 65 73 74 test
1851 *
1852 * buffer.clear
1853 * # =>
1854 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1855 * # 0x00000000 00 00 00 00 ....
1856 *
1857 * buf.clear(1) # fill with 1
1858 * # =>
1859 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1860 * # 0x00000000 01 01 01 01 ....
1861 *
1862 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
1863 * # =>
1864 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1865 * # 0x00000000 01 02 02 01 ....
1866 *
1867 * buffer.clear(2, 1) # fill with 2, starting from offset 1
1868 * # =>
1869 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1870 * # 0x00000000 01 02 02 02 ....
1871 *
1872 */
1873static VALUE
1874io_buffer_clear(int argc, VALUE *argv, VALUE self)
1875{
1876 if (argc > 3) rb_error_arity(argc, 0, 3);
1877
1878 struct rb_io_buffer *data = NULL;
1879 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1880
1881 uint8_t value = 0;
1882 if (argc >= 1) {
1883 value = NUM2UINT(argv[0]);
1884 }
1885
1886 size_t offset = 0;
1887 if (argc >= 2) {
1888 offset = NUM2SIZET(argv[1]);
1889 }
1890
1891 size_t length;
1892 if (argc >= 3) {
1893 length = NUM2SIZET(argv[2]);
1894 } else {
1895 length = data->size - offset;
1896 }
1897
1898 rb_io_buffer_clear(self, value, offset, length);
1899
1900 return self;
1901}
1902
1903static
1904size_t io_buffer_default_size(size_t page_size) {
1905 // Platform agnostic default size, based on empirical performance observation:
1906 const size_t platform_agnostic_default_size = 64*1024;
1907
1908 // Allow user to specify custom default buffer size:
1909 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
1910 if (default_size) {
1911 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
1912 int value = atoi(default_size);
1913
1914 // assuming sizeof(int) <= sizeof(size_t)
1915 if (value > 0) {
1916 return value;
1917 }
1918 }
1919
1920 if (platform_agnostic_default_size < page_size) {
1921 return page_size;
1922 }
1923
1924 return platform_agnostic_default_size;
1925}
1926
1927VALUE
1928rb_io_buffer_read(VALUE self, VALUE io, size_t length)
1929{
1930 VALUE scheduler = rb_fiber_scheduler_current();
1931 if (scheduler != Qnil) {
1932 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length);
1933
1934 if (result != Qundef) {
1935 return result;
1936 }
1937 }
1938
1939 struct rb_io_buffer *data = NULL;
1940 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1941
1942 io_buffer_validate_range(data, 0, length);
1943
1944 int descriptor = rb_io_descriptor(io);
1945
1946 void * base;
1947 size_t size;
1948 io_buffer_get_bytes_for_writing(data, &base, &size);
1949
1950 ssize_t result = read(descriptor, base, size);
1951
1952 return rb_fiber_scheduler_io_result(result, errno);
1953}
1954
1955static VALUE
1956io_buffer_read(VALUE self, VALUE io, VALUE length)
1957{
1958 return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
1959}
1960
1961VALUE
1962rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset)
1963{
1964 VALUE scheduler = rb_fiber_scheduler_current();
1965 if (scheduler != Qnil) {
1966 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset);
1967
1968 if (result != Qundef) {
1969 return result;
1970 }
1971 }
1972
1973 struct rb_io_buffer *data = NULL;
1974 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1975
1976 io_buffer_validate_range(data, 0, length);
1977
1978 int descriptor = rb_io_descriptor(io);
1979
1980 void * base;
1981 size_t size;
1982 io_buffer_get_bytes_for_writing(data, &base, &size);
1983
1984#if defined(HAVE_PREAD)
1985 ssize_t result = pread(descriptor, base, size, offset);
1986#else
1987 // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
1988 off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
1989 if (current_offset == (off_t)-1)
1990 return rb_fiber_scheduler_io_result(-1, errno);
1991
1992 if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
1993 return rb_fiber_scheduler_io_result(-1, errno);
1994
1995 ssize_t result = read(descriptor, base, size);
1996
1997 if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
1998 return rb_fiber_scheduler_io_result(-1, errno);
1999#endif
2000
2001 return rb_fiber_scheduler_io_result(result, errno);
2002}
2003
2004static VALUE
2005io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
2006{
2007 return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
2008}
2009
2010VALUE
2011rb_io_buffer_write(VALUE self, VALUE io, size_t length)
2012{
2013 VALUE scheduler = rb_fiber_scheduler_current();
2014 if (scheduler != Qnil) {
2015 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length);
2016
2017 if (result != Qundef) {
2018 return result;
2019 }
2020 }
2021
2022 struct rb_io_buffer *data = NULL;
2023 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
2024
2025 io_buffer_validate_range(data, 0, length);
2026
2027 int descriptor = rb_io_descriptor(io);
2028
2029 const void * base;
2030 size_t size;
2031 io_buffer_get_bytes_for_reading(data, &base, &size);
2032
2033 ssize_t result = write(descriptor, base, length);
2034
2035 return rb_fiber_scheduler_io_result(result, errno);
2036}
2037
2038static VALUE
2039io_buffer_write(VALUE self, VALUE io, VALUE length)
2040{
2041 return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
2042}
2043
2044VALUE
2045rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset)
2046{
2047 VALUE scheduler = rb_fiber_scheduler_current();
2048 if (scheduler != Qnil) {
2049 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset));
2050
2051 if (result != Qundef) {
2052 return result;
2053 }
2054 }
2055
2056 struct rb_io_buffer *data = NULL;
2057 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
2058
2059 io_buffer_validate_range(data, 0, length);
2060
2061 int descriptor = rb_io_descriptor(io);
2062
2063 const void * base;
2064 size_t size;
2065 io_buffer_get_bytes_for_reading(data, &base, &size);
2066
2067#if defined(HAVE_PWRITE)
2068 ssize_t result = pwrite(descriptor, base, length, offset);
2069#else
2070 // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
2071 off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
2072 if (current_offset == (off_t)-1)
2073 return rb_fiber_scheduler_io_result(-1, errno);
2074
2075 if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
2076 return rb_fiber_scheduler_io_result(-1, errno);
2077
2078 ssize_t result = write(descriptor, base, length);
2079
2080 if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
2081 return rb_fiber_scheduler_io_result(-1, errno);
2082#endif
2083
2084 return rb_fiber_scheduler_io_result(result, errno);
2085}
2086
2087static VALUE
2088io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
2089{
2090 return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
2091}
2092
2093/*
2094 * Document-class: IO::Buffer
2095 *
2096 * IO::Buffer is a low-level efficient buffer for input/output. There are three
2097 * ways of using buffer:
2098 *
2099 * * Create an empty buffer with ::new, fill it with data using #copy or
2100 * #set_value, #set_string, get data with #get_string;
2101 * * Create a buffer mapped to some string with ::for, then it could be used
2102 * both for reading with #get_string or #get_value, and writing (writing will
2103 * change the source string, too);
2104 * * Create a buffer mapped to some file with ::map, then it could be used for
2105 * reading and writing the underlying file.
2106 *
2107 * Interaction with string and file memory is performed by efficient low-level
2108 * C mechanisms like `memcpy`.
2109 *
2110 * The class is meant to be an utility for implementing more high-level mechanisms
2111 * like Fiber::SchedulerInterface#io_read and Fiber::SchedulerInterface#io_write.
2112 *
2113 * <b>Examples of usage:</b>
2114 *
2115 * Empty buffer:
2116 *
2117 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
2118 * # =>
2119 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
2120 * # ...
2121 * buffer
2122 * # =>
2123 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
2124 * # 0x00000000 00 00 00 00 00 00 00 00
2125 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
2126 * # => 4
2127 * buffer.get_string # get the result
2128 * # => "\x00\x00test\x00\x00"
2129 *
2130 * \Buffer from string:
2131 *
2132 * string = 'data'
2133 * buffer = IO::Buffer.for(str)
2134 * # =>
2135 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2136 * # ...
2137 * buffer
2138 * # =>
2139 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2140 * # 0x00000000 64 61 74 61 data
2141 *
2142 * buffer.get_string(2) # read content starting from offset 2
2143 * # => "ta"
2144 * buffer.set_string('---', 1) # write content, starting from offset 1
2145 * # => 3
2146 * buffer
2147 * # =>
2148 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2149 * # 0x00000000 64 2d 2d 2d d---
2150 * string # original string changed, too
2151 * # => "d---"
2152 *
2153 * \Buffer from file:
2154 *
2155 * File.write('test.txt', 'test data')
2156 * # => 9
2157 * buffer = IO::Buffer.map(File.open('test.txt'))
2158 * # =>
2159 * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
2160 * # ...
2161 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
2162 * # => "da"
2163 * buffer.set_string('---', 1) # attempt to write
2164 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
2165 *
2166 * # To create writable file-mapped buffer
2167 * # Open file for read-write, pass size, offset, and flags=0
2168 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
2169 * buffer.set_string('---', 1)
2170 * # => 3 -- bytes written
2171 * File.read('test.txt')
2172 * # => "t--- data"
2173 *
2174 * <b>The class is experimental and the interface is subject to change.</b>
2175 */
2176void
2177Init_IO_Buffer(void)
2178{
2179 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
2180 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
2181 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
2182 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
2183 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
2184
2185 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
2186 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
2187
2188#ifdef _WIN32
2189 SYSTEM_INFO info;
2190 GetSystemInfo(&info);
2191 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
2192#else /* not WIN32 */
2193 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
2194#endif
2195
2196 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
2197
2198 // Efficient sizing of mapped buffers:
2199 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
2200 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
2201
2202 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
2203
2204 // General use:
2205 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
2206 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
2207 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, 0);
2208 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
2209 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
2210 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
2211
2212 // Ownership:
2213 rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
2214
2215 // Flags:
2216 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
2217 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
2218 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
2219 rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
2220 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
2221 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
2222
2223 // Endian:
2224 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
2225 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
2226 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
2227 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
2228
2229 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
2230 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
2231 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
2232 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
2233 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
2234 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
2235 rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
2236
2237 // Locking to prevent changes while using pointer:
2238 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
2239 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
2240 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
2241
2242 // Manipulation:
2243 rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2);
2244 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
2245 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
2246 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
2247 rb_define_method(rb_cIOBuffer, "free", rb_io_buffer_free, 0);
2248
2249 rb_include_module(rb_cIOBuffer, rb_mComparable);
2250
2251#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name)
2252 DEFINE_TYPE(U8); DEFINE_TYPE(S8);
2253 DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16);
2254 DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32);
2255 DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64);
2256 DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64);
2257#undef DEFINE_TYPE
2258
2259 // Data access:
2260 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
2261 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
2262
2263 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
2264
2265 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
2266 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
2267
2268 // IO operations:
2269 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
2270 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
2271 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2);
2272 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3);
2273}
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition: cxxanyargs.hpp:685
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
Definition: fl_type.h:927
void rb_include_module(VALUE klass, VALUE module)
Includes a module to a class.
Definition: class.c:1043
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:869
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:1914
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition: eval.c:850
#define T_STRING
Old name of RUBY_T_STRING.
Definition: value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#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 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 cat, const char *fmt,...)
Identical to rb_category_warning(), except it reports always regardless of runtime -W flag.
Definition: error.c:428
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition: error.c:3021
void rb_sys_fail(const char *mesg)
Converts a C errno into a Ruby exception, then raises it.
Definition: error.c:3145
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:979
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
Definition: error.h:51
VALUE rb_cIO
IO class.
Definition: io.c:185
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
rb_encoding * rb_ascii8bit_encoding(void)
Queries the encoding that represents ASCII-8BIT a.k.a.
Definition: encoding.c:1515
rb_encoding * rb_find_encoding(VALUE obj)
Identical to rb_to_encoding_index(), except the return type.
Definition: encoding.c:336
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:940
off_t rb_file_size(VALUE file)
Queries the file size of the given file.
Definition: file.c:2559
void rb_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:6774
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:3317
VALUE rb_str_cat2(VALUE, const char *)
Just another name of rb_str_cat_cstr.
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:3022
VALUE rb_str_new_cstr(const char *ptr)
Identical to rb_str_new(), except it assumes the passed pointer is a pointer to a C string.
Definition: string.c:952
VALUE rb_str_buf_new(long capa)
Allocates a "string buffer".
Definition: string.c:1506
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition: variable.c:294
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:3253
int rb_io_descriptor(VALUE io)
Returns an integer representing the numeric file descriptor for io.
Definition: io.c:2650
#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:1241
#define RB_LL2NUM
Just another name of rb_ll2num_inline.
Definition: long_long.h:28
#define RB_ULL2NUM
Just another name of rb_ull2num_inline.
Definition: long_long.h:29
#define RB_NUM2ULL
Just another name of rb_num2ull_inline.
Definition: long_long.h:33
#define RB_NUM2LL
Just another name of rb_num2ll_inline.
Definition: long_long.h:32
VALUE rb_yield(VALUE val)
Yields the block.
Definition: vm_eval.c:1357
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
#define OFFT2NUM
Converts a C's off_t into an instance of rb_cInteger.
Definition: off_t.h:33
#define NUM2OFFT
Converts an instance of rb_cNumeric into C's off_t.
Definition: off_t.h:44
#define 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:573
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
Definition: rstring.h:483
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:1584
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:497
#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
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:126
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:46
VALUE rb_fiber_scheduler_io_read(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
Nonblocking read from the passed IO.
Definition: scheduler.c:235
VALUE rb_fiber_scheduler_io_pread(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
Nonblocking read from the passed IO at the specified offset.
Definition: scheduler.c:245
VALUE rb_fiber_scheduler_io_write(VALUE scheduler, VALUE io, VALUE buffer, size_t length)
Nonblocking write to the passed IO.
Definition: scheduler.c:255
VALUE rb_fiber_scheduler_io_pwrite(VALUE scheduler, VALUE io, VALUE buffer, size_t length, off_t offset)
Nonblocking write to the passed IO at the specified offset.
Definition: scheduler.c:265
#define RB_NUM2SIZE
Converts an instance of rb_cInteger into C's size_t.
Definition: size_t.h:47
static bool RB_NIL_P(VALUE obj)
Checks if the given object is nil.
This is the struct that holds necessary info for a struct.
const char * wrap_struct_name
Name of structs of this kind.
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