[ruby/etc] bump up to 1.3.1
[ruby-80x24.org.git] / io_buffer.c
blob5227a2a5b8bf24cb53432699468a127c6e651457
1 /**********************************************************************
3 io_buffer.c
5 Copyright (C) 2021 Samuel Grant Dawson Williams
7 **********************************************************************/
9 #include "ruby/io.h"
10 #include "ruby/io/buffer.h"
11 #include "ruby/fiber/scheduler.h"
13 #include "internal.h"
14 #include "internal/string.h"
15 #include "internal/bits.h"
16 #include "internal/error.h"
18 VALUE rb_cIOBuffer;
19 VALUE rb_eIOBufferLockedError;
20 VALUE rb_eIOBufferAllocationError;
21 VALUE rb_eIOBufferAccessError;
22 VALUE rb_eIOBufferInvalidatedError;
24 size_t RUBY_IO_BUFFER_PAGE_SIZE;
25 size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
27 #ifdef _WIN32
28 #else
29 #include <unistd.h>
30 #include <sys/mman.h>
31 #endif
33 struct rb_io_buffer {
34 void *base;
35 size_t size;
36 enum rb_io_buffer_flags flags;
38 #if defined(_WIN32)
39 HANDLE mapping;
40 #endif
42 VALUE source;
45 static inline void *
46 io_buffer_map_memory(size_t size)
48 #if defined(_WIN32)
49 void * base = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE);
51 if (!base) {
52 rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
54 #else
55 void * base = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_ANON | MAP_PRIVATE, -1, 0);
57 if (base == MAP_FAILED) {
58 rb_sys_fail("io_buffer_map_memory:mmap");
60 #endif
62 return base;
65 static void
66 io_buffer_map_file(struct rb_io_buffer *data, int descriptor, size_t size, off_t offset, enum rb_io_buffer_flags flags)
68 #if defined(_WIN32)
69 HANDLE file = (HANDLE)_get_osfhandle(descriptor);
70 if (!file) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
72 DWORD protect = PAGE_READONLY, access = FILE_MAP_READ;
74 if (flags & RB_IO_BUFFER_READONLY) {
75 data->flags |= RB_IO_BUFFER_READONLY;
77 else {
78 protect = PAGE_READWRITE;
79 access = FILE_MAP_WRITE;
82 HANDLE mapping = CreateFileMapping(file, NULL, protect, 0, 0, NULL);
83 if (!mapping) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
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;
93 void *base = MapViewOfFile(mapping, access, (DWORD)(offset >> 32), (DWORD)(offset & 0xFFFFFFFF), size);
95 if (!base) {
96 CloseHandle(mapping);
97 rb_sys_fail("io_buffer_map_file:MapViewOfFile");
100 data->mapping = mapping;
101 #else
102 int protect = PROT_READ, access = 0;
104 if (flags & RB_IO_BUFFER_READONLY) {
105 data->flags |= RB_IO_BUFFER_READONLY;
107 else {
108 protect |= PROT_WRITE;
111 if (flags & RB_IO_BUFFER_PRIVATE) {
112 data->flags |= RB_IO_BUFFER_PRIVATE;
114 else {
115 // This buffer refers to external data.
116 data->flags |= RB_IO_BUFFER_EXTERNAL;
117 access |= MAP_SHARED;
120 void *base = mmap(NULL, size, protect, access, descriptor, offset);
122 if (base == MAP_FAILED) {
123 rb_sys_fail("io_buffer_map_file:mmap");
125 #endif
127 data->base = base;
128 data->size = size;
130 data->flags |= RB_IO_BUFFER_MAPPED;
133 static inline void
134 io_buffer_unmap(void* base, size_t size)
136 #ifdef _WIN32
137 VirtualFree(base, 0, MEM_RELEASE);
138 #else
139 munmap(base, size);
140 #endif
143 static void
144 io_buffer_experimental(void)
146 static int warned = 0;
148 if (warned) return;
150 warned = 1;
152 if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL)) {
153 rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL,
154 "IO::Buffer is experimental and both the Ruby and C interface may change in the future!"
159 static void
160 io_buffer_zero(struct rb_io_buffer *data)
162 data->base = NULL;
163 data->size = 0;
164 #if defined(_WIN32)
165 data->mapping = NULL;
166 #endif
167 data->source = Qnil;
170 static void
171 io_buffer_initialize(struct rb_io_buffer *data, void *base, size_t size, enum rb_io_buffer_flags flags, VALUE source)
173 if (base) {
174 // If we are provided a pointer, we use it.
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);
181 else if (flags & RB_IO_BUFFER_MAPPED) {
182 base = io_buffer_map_memory(size);
185 if (!base) {
186 rb_raise(rb_eIOBufferAllocationError, "Could not allocate buffer!");
188 } else {
189 // Otherwise we don't do anything.
190 return;
193 data->base = base;
194 data->size = size;
195 data->flags = flags;
196 data->source = source;
199 static int
200 io_buffer_free(struct rb_io_buffer *data)
202 if (data->base) {
203 if (data->flags & RB_IO_BUFFER_INTERNAL) {
204 free(data->base);
207 if (data->flags & RB_IO_BUFFER_MAPPED) {
208 io_buffer_unmap(data->base, data->size);
211 if (RB_TYPE_P(data->source, T_STRING)) {
212 rb_str_unlocktmp(data->source);
215 data->base = NULL;
217 #if defined(_WIN32)
218 if (data->mapping) {
219 CloseHandle(data->mapping);
220 data->mapping = NULL;
222 #endif
223 data->size = 0;
224 data->flags = 0;
225 data->source = Qnil;
227 return 1;
230 return 0;
233 void
234 rb_io_buffer_type_mark(void *_data)
236 struct rb_io_buffer *data = _data;
237 rb_gc_mark(data->source);
240 void
241 rb_io_buffer_type_free(void *_data)
243 struct rb_io_buffer *data = _data;
245 io_buffer_free(data);
247 free(data);
250 size_t
251 rb_io_buffer_type_size(const void *_data)
253 const struct rb_io_buffer *data = _data;
254 size_t total = sizeof(struct rb_io_buffer);
256 if (data->flags) {
257 total += data->size;
260 return total;
263 static const rb_data_type_t rb_io_buffer_type = {
264 .wrap_struct_name = "IO::Buffer",
265 .function = {
266 .dmark = rb_io_buffer_type_mark,
267 .dfree = rb_io_buffer_type_free,
268 .dsize = rb_io_buffer_type_size,
270 .data = NULL,
271 .flags = RUBY_TYPED_FREE_IMMEDIATELY,
274 VALUE
275 rb_io_buffer_type_allocate(VALUE self)
277 struct rb_io_buffer *data = NULL;
278 VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
280 io_buffer_zero(data);
282 return instance;
286 * call-seq: IO::Buffer.for(string) -> io_buffer
288 * Creates a IO::Buffer from the given string's memory. The buffer remains
289 * associated with the string, and writing to a buffer will update the string's
290 * contents.
292 * Until #free is invoked on the buffer, either explicitly or via the garbage
293 * collector, the source string will be locked and cannot be modified.
295 * If the string is frozen, it will create a read-only buffer which cannot be
296 * modified.
298 * string = 'test'
299 * buffer = IO::Buffer.for(str)
300 * buffer.external? #=> true
302 * buffer.get_string(0, 1)
303 * # => "t"
304 * string
305 * # => "best"
307 * buffer.resize(100)
308 * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
310 VALUE
311 rb_io_buffer_type_for(VALUE klass, VALUE string)
313 io_buffer_experimental();
315 StringValue(string);
317 VALUE instance = rb_io_buffer_type_allocate(klass);
319 struct rb_io_buffer *data = NULL;
320 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
322 rb_str_locktmp(string);
324 enum rb_io_buffer_flags flags = RB_IO_BUFFER_EXTERNAL;
326 if (RB_OBJ_FROZEN(string))
327 flags |= RB_IO_BUFFER_READONLY;
329 io_buffer_initialize(data, RSTRING_PTR(string), RSTRING_LEN(string), flags, string);
331 return instance;
334 VALUE
335 rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
337 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
339 struct rb_io_buffer *data = NULL;
340 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
342 io_buffer_initialize(data, base, size, flags, Qnil);
344 return instance;
347 VALUE
348 rb_io_buffer_map(VALUE io, size_t size, off_t offset, enum rb_io_buffer_flags flags)
350 io_buffer_experimental();
352 VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
354 struct rb_io_buffer *data = NULL;
355 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, data);
357 int descriptor = rb_io_descriptor(io);
359 io_buffer_map_file(data, descriptor, size, offset, flags);
361 return instance;
365 * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
367 * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
368 * +file_io+ should be a +File+ instance, opened for reading.
370 * Optional +size+ and +offset+ of mapping can be specified.
372 * By default, the buffer would be immutable (read only); to create a writable
373 * mapping, you need to open a file in read-write mode, and explicitly pass
374 * +flags+ argument without IO::Buffer::IMMUTABLE.
376 * File.write('test.txt', 'test')
378 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
379 * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
381 * buffer.readonly? # => true
383 * buffer.get_string
384 * # => "test"
386 * buffer.set_string('b', 0)
387 * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
389 * # create read/write mapping: length 4 bytes, offset 0, flags 0
390 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
391 * buffer.set_string('b', 0)
392 * # => 1
394 * # Check it
395 * File.read('test.txt')
396 * # => "best"
398 * Note that some operating systems may not have cache coherency between mapped
399 * buffers and file reads.
402 static VALUE
403 io_buffer_map(int argc, VALUE *argv, VALUE klass)
405 if (argc < 1 || argc > 4) {
406 rb_error_arity(argc, 2, 4);
409 // We might like to handle a string path?
410 VALUE io = argv[0];
412 size_t size;
413 if (argc >= 2 && !RB_NIL_P(argv[1])) {
414 size = RB_NUM2SIZE(argv[1]);
416 else {
417 off_t file_size = rb_file_size(io);
419 // Compiler can confirm that we handled file_size < 0 case:
420 if (file_size < 0) {
421 rb_raise(rb_eArgError, "Invalid negative file size!");
423 // Here, we assume that file_size is positive:
424 else if ((uintmax_t)file_size > SIZE_MAX) {
425 rb_raise(rb_eArgError, "File larger than address space!");
427 else {
428 // This conversion should be safe:
429 size = (size_t)file_size;
433 off_t offset = 0;
434 if (argc >= 3) {
435 offset = NUM2OFFT(argv[2]);
438 enum rb_io_buffer_flags flags = 0;
439 if (argc >= 4) {
440 flags = RB_NUM2UINT(argv[3]);
443 return rb_io_buffer_map(io, size, offset, flags);
446 // Compute the optimal allocation flags for a buffer of the given size.
447 static inline enum rb_io_buffer_flags
448 io_flags_for_size(size_t size)
450 if (size >= RUBY_IO_BUFFER_PAGE_SIZE) {
451 return RB_IO_BUFFER_MAPPED;
454 return RB_IO_BUFFER_INTERNAL;
458 * call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
460 * Create a new zero-filled IO::Buffer of +size+ bytes.
461 * By default, the buffer will be _internal_: directly allocated chunk
462 * of the memory. But if the requested +size+ is more than OS-specific
463 * IO::Bufer::PAGE_SIZE, the buffer would be allocated using the
464 * virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
465 * on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
466 * as a second parameter.
468 * Examples
470 * buffer = IO::Buffer.new(4)
471 * # =>
472 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
473 * # 0x00000000 00 00 00 00 ....
475 * buffer.get_string(0, 1) # => "\x00"
477 * buffer.set_string("test")
478 * buffer
479 * # =>
480 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
481 * # 0x00000000 74 65 73 74 test
484 VALUE
485 rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
487 io_buffer_experimental();
489 if (argc < 0 || argc > 2) {
490 rb_error_arity(argc, 0, 2);
493 struct rb_io_buffer *data = NULL;
494 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
496 size_t size;
498 if (argc > 0) {
499 size = RB_NUM2SIZE(argv[0]);
500 } else {
501 size = RUBY_IO_BUFFER_DEFAULT_SIZE;
504 enum rb_io_buffer_flags flags = 0;
505 if (argc >= 2) {
506 flags = RB_NUM2UINT(argv[1]);
508 else {
509 flags |= io_flags_for_size(size);
512 io_buffer_initialize(data, NULL, size, flags, Qnil);
514 return self;
517 static int
518 io_buffer_validate_slice(VALUE source, void *base, size_t size)
520 void *source_base = NULL;
521 size_t source_size = 0;
523 if (RB_TYPE_P(source, T_STRING)) {
524 RSTRING_GETMEM(source, source_base, source_size);
526 else {
527 rb_io_buffer_get_bytes(source, &source_base, &source_size);
530 // Source is invalid:
531 if (source_base == NULL) return 0;
533 // Base is out of range:
534 if (base < source_base) return 0;
536 const void *source_end = (char*)source_base + source_size;
537 const void *end = (char*)base + size;
539 // End is out of range:
540 if (end > source_end) return 0;
542 // It seems okay:
543 return 1;
546 static int
547 io_buffer_validate(struct rb_io_buffer *data)
549 if (data->source != Qnil) {
550 // Only slices incur this overhead, unfortunately... better safe than sorry!
551 return io_buffer_validate_slice(data->source, data->base, data->size);
553 else {
554 return 1;
559 * call-seq: to_s -> string
561 * Short representation of the buffer. It includes the address, size and
562 * symbolic flags. This format is subject to change.
564 * puts IO::Buffer.new(4) # uses to_s internally
565 * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
568 VALUE
569 rb_io_buffer_to_s(VALUE self)
571 struct rb_io_buffer *data = NULL;
572 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
574 VALUE result = rb_str_new_cstr("#<");
576 rb_str_append(result, rb_class_name(CLASS_OF(self)));
577 rb_str_catf(result, " %p+%"PRIdSIZE, data->base, data->size);
579 if (data->base == NULL) {
580 rb_str_cat2(result, " NULL");
583 if (data->flags & RB_IO_BUFFER_EXTERNAL) {
584 rb_str_cat2(result, " EXTERNAL");
587 if (data->flags & RB_IO_BUFFER_INTERNAL) {
588 rb_str_cat2(result, " INTERNAL");
591 if (data->flags & RB_IO_BUFFER_MAPPED) {
592 rb_str_cat2(result, " MAPPED");
595 if (data->flags & RB_IO_BUFFER_LOCKED) {
596 rb_str_cat2(result, " LOCKED");
599 if (data->flags & RB_IO_BUFFER_READONLY) {
600 rb_str_cat2(result, " READONLY");
603 if (data->source != Qnil) {
604 rb_str_cat2(result, " SLICE");
607 if (!io_buffer_validate(data)) {
608 rb_str_cat2(result, " INVALID");
611 return rb_str_cat2(result, ">");
614 static VALUE
615 io_buffer_hexdump(VALUE string, size_t width, char *base, size_t size, int first)
617 char *text = alloca(width+1);
618 text[width] = '\0';
620 for (size_t offset = 0; offset < size; offset += width) {
621 memset(text, '\0', width);
622 if (first) {
623 rb_str_catf(string, "0x%08zx ", offset);
624 first = 0;
625 } else {
626 rb_str_catf(string, "\n0x%08zx ", offset);
629 for (size_t i = 0; i < width; i += 1) {
630 if (offset+i < size) {
631 unsigned char value = ((unsigned char*)base)[offset+i];
633 if (value < 127 && isprint(value)) {
634 text[i] = (char)value;
636 else {
637 text[i] = '.';
640 rb_str_catf(string, " %02x", value);
642 else {
643 rb_str_cat2(string, " ");
647 rb_str_catf(string, " %s", text);
650 return string;
653 static VALUE
654 rb_io_buffer_hexdump(VALUE self)
656 struct rb_io_buffer *data = NULL;
657 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
659 VALUE result = Qnil;
661 if (io_buffer_validate(data) && data->base) {
662 result = rb_str_buf_new(data->size*3 + (data->size/16)*12 + 1);
664 io_buffer_hexdump(result, 16, data->base, data->size, 1);
667 return result;
670 VALUE
671 rb_io_buffer_inspect(VALUE self)
673 struct rb_io_buffer *data = NULL;
674 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
676 VALUE result = rb_io_buffer_to_s(self);
678 if (io_buffer_validate(data)) {
679 // Limit the maximum size genearted by inspect.
680 if (data->size <= 256) {
681 io_buffer_hexdump(result, 16, data->base, data->size, 0);
685 return result;
689 * call-seq: size -> integer
691 * Returns the size of the buffer that was explicitly set (on creation with ::new
692 * or on #resize), or deduced on buffer's creation from string or file.
695 VALUE
696 rb_io_buffer_size(VALUE self)
698 struct rb_io_buffer *data = NULL;
699 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
701 return SIZET2NUM(data->size);
705 * call-seq: valid? -> true or false
707 * Returns whether the buffer data is accessible.
709 * A buffer becomes invalid if it is a slice of another buffer which has been
710 * freed.
713 static VALUE
714 rb_io_buffer_valid_p(VALUE self)
716 struct rb_io_buffer *data = NULL;
717 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
719 return RBOOL(io_buffer_validate(data));
723 * call-seq: null? -> true or false
725 * If the buffer was freed with #free or was never allocated in the first
726 * place.
729 static VALUE
730 rb_io_buffer_null_p(VALUE self)
732 struct rb_io_buffer *data = NULL;
733 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
735 return RBOOL(data->base == NULL);
739 * call-seq: external? -> true or false
741 * If the buffer is _external_, meaning it references from memory which is not
742 * allocated or mapped by the buffer itself.
744 * A buffer created using ::for has an external reference to the string's
745 * memory.
747 * External buffer can't be resized.
750 static VALUE
751 rb_io_buffer_empty_p(VALUE self)
753 struct rb_io_buffer *data = NULL;
754 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
756 return RBOOL(data->size == 0);
759 static VALUE
760 rb_io_buffer_external_p(VALUE self)
762 struct rb_io_buffer *data = NULL;
763 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
765 return RBOOL(data->flags & RB_IO_BUFFER_EXTERNAL);
769 * call-seq: internal? -> true or false
771 * If the buffer is _internal_, meaning it references memory allocated by the
772 * buffer itself.
774 * An internal buffer is not associated with any external memory (e.g. string)
775 * or file mapping.
777 * Internal buffers are created using ::new and is the default when the
778 * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
779 * requested to be mapped on creation.
781 * Internal buffers can be resized, and such an operation will typically
782 * invalidate all slices, but not always.
785 static VALUE
786 rb_io_buffer_internal_p(VALUE self)
788 struct rb_io_buffer *data = NULL;
789 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
791 return RBOOL(data->flags & RB_IO_BUFFER_INTERNAL);
795 * call-seq: mapped? -> true or false
797 * If the buffer is _mapped_, meaning it references memory mapped by the
798 * buffer.
800 * Mapped buffers are either anonymous, if created by ::new with the
801 * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
802 * or backed by a file if created with ::map.
804 * Mapped buffers can usually be resized, and such an operation will typically
805 * invalidate all slices, but not always.
808 static VALUE
809 rb_io_buffer_mapped_p(VALUE self)
811 struct rb_io_buffer *data = NULL;
812 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
814 return RBOOL(data->flags & RB_IO_BUFFER_MAPPED);
818 * call-seq: locked? -> true or false
820 * If the buffer is _locked_, meaning it is inside #locked block execution.
821 * Locked buffer can't be resized or freed, and another lock can't be acquired
822 * on it.
824 * Locking is not thread safe, but is a semantic used to ensure buffers don't
825 * move while being used by a system call.
827 * buffer.locked do
828 * buffer.write(io) # theoretical system call interface
829 * end
832 static VALUE
833 rb_io_buffer_locked_p(VALUE self)
835 struct rb_io_buffer *data = NULL;
836 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
838 return RBOOL(data->flags & RB_IO_BUFFER_LOCKED);
842 * call-seq: readonly? -> true or false
844 * If the buffer is _read only_, meaning the buffer cannot be modified using
845 * #set_value, #set_string or #copy and similar.
847 * Frozen strings and read-only files create read-only buffers.
851 rb_io_buffer_readonly_p(VALUE self)
853 struct rb_io_buffer *data = NULL;
854 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
856 return data->flags & RB_IO_BUFFER_READONLY;
859 static VALUE
860 io_buffer_readonly_p(VALUE self)
862 return RBOOL(rb_io_buffer_readonly_p(self));
865 VALUE
866 rb_io_buffer_lock(VALUE self)
868 struct rb_io_buffer *data = NULL;
869 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
871 if (data->flags & RB_IO_BUFFER_LOCKED) {
872 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
875 data->flags |= RB_IO_BUFFER_LOCKED;
877 return self;
880 VALUE
881 rb_io_buffer_unlock(VALUE self)
883 struct rb_io_buffer *data = NULL;
884 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
886 if (!(data->flags & RB_IO_BUFFER_LOCKED)) {
887 rb_raise(rb_eIOBufferLockedError, "Buffer not locked!");
890 data->flags &= ~RB_IO_BUFFER_LOCKED;
892 return self;
896 rb_io_buffer_try_unlock(VALUE self)
898 struct rb_io_buffer *data = NULL;
899 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
901 if (data->flags & RB_IO_BUFFER_LOCKED) {
902 data->flags &= ~RB_IO_BUFFER_LOCKED;
903 return 1;
906 return 0;
910 * call-seq: locked { ... }
912 * Allows to process a buffer in exclusive way, for concurrency-safety. While
913 * the block is performed, the buffer is considered locked, and no other code
914 * can enter the lock. Also, locked buffer can't be changed with #resize or
915 * #free.
917 * buffer = IO::Buffer.new(4)
918 * buffer.locked? #=> false
920 * Fiber.schedule do
921 * buffer.locked do
922 * buffer.write(io) # theoretical system call interface
923 * end
924 * end
926 * Fiber.schedule do
927 * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
928 * buffer.locked do
929 * buffer.set_string(...)
930 * end
931 * end
933 * The following operations acquire a lock: #resize, #free.
935 * Locking is not thread safe. It is designed as a safety net around
936 * non-blocking system calls. You can only share a buffer between threads with
937 * appropriate synchronisation techniques.
939 VALUE
940 rb_io_buffer_locked(VALUE self)
942 struct rb_io_buffer *data = NULL;
943 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
945 if (data->flags & RB_IO_BUFFER_LOCKED) {
946 rb_raise(rb_eIOBufferLockedError, "Buffer already locked!");
949 data->flags |= RB_IO_BUFFER_LOCKED;
951 VALUE result = rb_yield(self);
953 data->flags &= ~RB_IO_BUFFER_LOCKED;
955 return result;
959 * call-seq: free -> self
961 * If the buffer references memory, release it back to the operating system.
962 * * for a _mapped_ buffer (e.g. from file): unmap.
963 * * for a buffer created from scratch: free memory.
964 * * for a buffer created from string: undo the association.
966 * After the buffer is freed, no further operations can't be performed on it.
968 * buffer = IO::Buffer.for('test')
969 * buffer.free
970 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
972 * buffer.get_value(:U8, 0)
973 * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
975 * buffer.get_string
976 * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
978 * buffer.null?
979 * # => true
981 * You can resize a freed buffer to re-allocate it.
984 VALUE
985 rb_io_buffer_free(VALUE self)
987 struct rb_io_buffer *data = NULL;
988 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
990 if (data->flags & RB_IO_BUFFER_LOCKED) {
991 rb_raise(rb_eIOBufferLockedError, "Buffer is locked!");
994 io_buffer_free(data);
996 return self;
999 static inline void
1000 io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length)
1002 if (offset + length > data->size) {
1003 rb_raise(rb_eArgError, "Specified offset+length exceeds data size!");
1008 * call-seq: slice(offset, length) -> io_buffer
1010 * Produce another IO::Buffer which is a slice (or view into) the current one
1011 * starting at +offset+ bytes and going for +length+ bytes.
1013 * The slicing happens without copying of memory, and the slice keeps being
1014 * associated with the original buffer's source (string, or file), if any.
1016 * Raises RuntimeError if the <tt>offset+length<tt> is out of the current
1017 * buffer's bounds.
1019 * string = 'test'
1020 * buffer = IO::Buffer.for(string)
1022 * slice = buffer.slice(1, 2)
1023 * # =>
1024 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1025 * # 0x00000000 65 73 es
1027 * # Put "o" into 0s position of the slice
1028 * slice.set_string('o', 0)
1029 * slice
1030 * # =>
1031 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1032 * # 0x00000000 6f 73 os
1035 * # it is also visible at position 1 of the original buffer
1036 * buffer
1037 * # =>
1038 * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
1039 * # 0x00000000 74 6f 73 74 tost
1041 * # ...and original string
1042 * string
1043 * # => tost
1046 VALUE
1047 rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length)
1049 // TODO fail on negative offets/lengths.
1050 size_t offset = NUM2SIZET(_offset);
1051 size_t length = NUM2SIZET(_length);
1053 struct rb_io_buffer *data = NULL;
1054 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1056 io_buffer_validate_range(data, offset, length);
1058 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1059 struct rb_io_buffer *slice = NULL;
1060 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
1062 slice->base = (char*)data->base + offset;
1063 slice->size = length;
1065 // The source should be the root buffer:
1066 if (data->source != Qnil)
1067 slice->source = data->source;
1068 else
1069 slice->source = self;
1071 return instance;
1074 int rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
1076 struct rb_io_buffer *data = NULL;
1077 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1079 if (io_buffer_validate(data)) {
1080 if (data->base) {
1081 *base = data->base;
1082 *size = data->size;
1084 return data->flags;
1088 *base = NULL;
1089 *size = 0;
1091 return 0;
1094 static void
1095 io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size)
1097 if (data->flags & RB_IO_BUFFER_READONLY) {
1098 rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
1101 if (!io_buffer_validate(data)) {
1102 rb_raise(rb_eIOBufferInvalidatedError, "Buffer is invalid!");
1105 if (data->base) {
1106 *base = data->base;
1107 *size = data->size;
1109 return;
1112 rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1115 void
1116 rb_io_buffer_get_bytes_for_writing(VALUE self, void **base, size_t *size)
1118 struct rb_io_buffer *data = NULL;
1119 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1121 io_buffer_get_bytes_for_writing(data, base, size);
1124 static void
1125 io_buffer_get_bytes_for_reading(struct rb_io_buffer *data, const void **base, size_t *size)
1127 if (!io_buffer_validate(data)) {
1128 rb_raise(rb_eIOBufferInvalidatedError, "Buffer has been invalidated!");
1131 if (data->base) {
1132 *base = data->base;
1133 *size = data->size;
1135 return;
1138 rb_raise(rb_eIOBufferAllocationError, "The buffer is not allocated!");
1141 void
1142 rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size)
1144 struct rb_io_buffer *data = NULL;
1145 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1147 io_buffer_get_bytes_for_reading(data, base, size);
1151 * call-seq: transfer -> new_io_buffer
1153 * Transfers ownership to a new buffer, deallocating the current one.
1155 * buffer = IO::Buffer.new('test')
1156 * other = buffer.transfer
1157 * other
1158 * # =>
1159 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1160 * # 0x00000000 74 65 73 74 test
1161 * buffer
1162 * # =>
1163 * # #<IO::Buffer 0x0000000000000000+0 NULL>
1164 * buffer.null?
1165 * # => true
1168 VALUE
1169 rb_io_buffer_transfer(VALUE self)
1171 struct rb_io_buffer *data = NULL;
1172 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1174 if (data->flags & RB_IO_BUFFER_LOCKED) {
1175 rb_raise(rb_eIOBufferLockedError, "Cannot transfer ownership of locked buffer!");
1178 VALUE instance = rb_io_buffer_type_allocate(rb_class_of(self));
1179 struct rb_io_buffer *transferred;
1180 TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, transferred);
1182 *transferred = *data;
1183 io_buffer_zero(data);
1185 return instance;
1188 static void
1189 io_buffer_resize_clear(struct rb_io_buffer *data, void* base, size_t size)
1191 if (size > data->size) {
1192 memset((unsigned char*)base+data->size, 0, size - data->size);
1196 static void
1197 io_buffer_resize_copy(struct rb_io_buffer *data, size_t size)
1199 // Slow path:
1200 struct rb_io_buffer resized;
1201 io_buffer_initialize(&resized, NULL, size, io_flags_for_size(size), Qnil);
1203 if (data->base) {
1204 size_t preserve = data->size;
1205 if (preserve > size) preserve = size;
1206 memcpy(resized.base, data->base, preserve);
1208 io_buffer_resize_clear(data, resized.base, size);
1211 io_buffer_free(data);
1212 *data = resized;
1215 void
1216 rb_io_buffer_resize(VALUE self, size_t size)
1218 struct rb_io_buffer *data = NULL;
1219 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1221 if (data->flags & RB_IO_BUFFER_LOCKED) {
1222 rb_raise(rb_eIOBufferLockedError, "Cannot resize locked buffer!");
1225 if (data->base == NULL) {
1226 io_buffer_initialize(data, NULL, size, io_flags_for_size(size), Qnil);
1227 return;
1230 if (data->flags & RB_IO_BUFFER_EXTERNAL) {
1231 rb_raise(rb_eIOBufferAccessError, "Cannot resize external buffer!");
1234 #ifdef MREMAP_MAYMOVE
1235 if (data->flags & RB_IO_BUFFER_MAPPED) {
1236 void *base = mremap(data->base, data->size, size, MREMAP_MAYMOVE);
1238 if (base == MAP_FAILED) {
1239 rb_sys_fail("rb_io_buffer_resize:mremap");
1242 io_buffer_resize_clear(data, base, size);
1244 data->base = base;
1245 data->size = size;
1247 return;
1249 #endif
1251 if (data->flags & RB_IO_BUFFER_INTERNAL) {
1252 void *base = realloc(data->base, size);
1254 if (!base) {
1255 rb_sys_fail("rb_io_buffer_resize:realloc");
1258 io_buffer_resize_clear(data, base, size);
1260 data->base = base;
1261 data->size = size;
1263 return;
1266 io_buffer_resize_copy(data, size);
1270 * call-seq: resize(new_size) -> self
1272 * Resizes a buffer to a +new_size+ bytes, preserving its content.
1273 * Depending on the old and new size, the memory area associated with
1274 * the buffer might be either extended, or rellocated at different
1275 * address with content being copied.
1277 * buffer = IO::Buffer.new(4)
1278 * buffer.set_string("test", 0)
1279 * buffer.resize(8) # resize to 8 bytes
1280 * # =>
1281 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1282 * # 0x00000000 74 65 73 74 00 00 00 00 test....
1284 * External buffer (created with ::for), and locked buffer
1285 * can not be resized.
1288 static VALUE
1289 io_buffer_resize(VALUE self, VALUE size)
1291 rb_io_buffer_resize(self, NUM2SIZET(size));
1293 return self;
1297 * call-seq: <=>(other) -> true or false
1299 * Buffers are compared by size and exact contents of the memory they are
1300 * referencing using +memcmp+.
1303 static VALUE
1304 rb_io_buffer_compare(VALUE self, VALUE other)
1306 const void *ptr1, *ptr2;
1307 size_t size1, size2;
1309 rb_io_buffer_get_bytes_for_reading(self, &ptr1, &size1);
1310 rb_io_buffer_get_bytes_for_reading(other, &ptr2, &size2);
1312 if (size1 < size2) {
1313 return RB_INT2NUM(-1);
1316 if (size1 > size2) {
1317 return RB_INT2NUM(1);
1320 return RB_INT2NUM(memcmp(ptr1, ptr2, size1));
1323 static void
1324 io_buffer_validate_type(size_t size, size_t offset)
1326 if (offset > size) {
1327 rb_raise(rb_eArgError, "Type extends beyond end of buffer!");
1331 // Lower case: little endian.
1332 // Upper case: big endian (network endian).
1334 // :U8 | unsigned 8-bit integer.
1335 // :S8 | signed 8-bit integer.
1337 // :u16, :U16 | unsigned 16-bit integer.
1338 // :s16, :S16 | signed 16-bit integer.
1340 // :u32, :U32 | unsigned 32-bit integer.
1341 // :s32, :S32 | signed 32-bit integer.
1343 // :u64, :U64 | unsigned 64-bit integer.
1344 // :s64, :S64 | signed 64-bit integer.
1346 // :f32, :F32 | 32-bit floating point number.
1347 // :f64, :F64 | 64-bit floating point number.
1349 #define ruby_swap8(value) value
1351 union swapf32 {
1352 uint32_t integral;
1353 float value;
1356 static float
1357 ruby_swapf32(float value)
1359 union swapf32 swap = {.value = value};
1360 swap.integral = ruby_swap32(swap.integral);
1361 return swap.value;
1364 union swapf64 {
1365 uint64_t integral;
1366 double value;
1369 static double
1370 ruby_swapf64(double value)
1372 union swapf64 swap = {.value = value};
1373 swap.integral = ruby_swap64(swap.integral);
1374 return swap.value;
1377 #define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1378 static ID RB_IO_BUFFER_TYPE_##name; \
1380 static VALUE \
1381 io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1383 io_buffer_validate_type(size, *offset + sizeof(type)); \
1384 type value; \
1385 memcpy(&value, (char*)base + *offset, sizeof(type)); \
1386 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1387 *offset += sizeof(type); \
1388 return wrap(value); \
1391 static void \
1392 io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
1394 io_buffer_validate_type(size, *offset + sizeof(type)); \
1395 type value = unwrap(_value); \
1396 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1397 memcpy((char*)base + *offset, &value, sizeof(type)); \
1398 *offset += sizeof(type); \
1401 DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8)
1402 DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8)
1404 DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1405 DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16)
1406 DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1407 DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16)
1409 DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1410 DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32)
1411 DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1412 DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32)
1414 DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1415 DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64)
1416 DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1417 DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
1419 DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1420 DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
1421 DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1422 DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
1423 #undef DECLARE_TYPE
1425 VALUE
1426 rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset)
1428 #define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset);
1429 READ_TYPE(U8)
1430 READ_TYPE(S8)
1432 READ_TYPE(u16)
1433 READ_TYPE(U16)
1434 READ_TYPE(s16)
1435 READ_TYPE(S16)
1437 READ_TYPE(u32)
1438 READ_TYPE(U32)
1439 READ_TYPE(s32)
1440 READ_TYPE(S32)
1442 READ_TYPE(u64)
1443 READ_TYPE(U64)
1444 READ_TYPE(s64)
1445 READ_TYPE(S64)
1447 READ_TYPE(f32)
1448 READ_TYPE(F32)
1449 READ_TYPE(f64)
1450 READ_TYPE(F64)
1451 #undef READ_TYPE
1453 rb_raise(rb_eArgError, "Invalid type name!");
1457 * call-seq: get_value(type, offset) -> numeric
1459 * Read from buffer a value of +type+ at +offset+. +type+ should be one
1460 * of symbols:
1462 * * +:U8+: unsigned integer, 1 byte
1463 * * +:S8+: signed integer, 1 byte
1464 * * +:u16+: unsigned integer, 2 bytes, little-endian
1465 * * +:U16+: unsigned integer, 2 bytes, big-endian
1466 * * +:s16+: signed integer, 2 bytes, little-endian
1467 * * +:S16+: signed integer, 2 bytes, big-endian
1468 * * +:u32+: unsigned integer, 4 bytes, little-endian
1469 * * +:U32+: unsigned integer, 4 bytes, big-endian
1470 * * +:s32+: signed integer, 4 bytes, little-endian
1471 * * +:S32+: signed integer, 4 bytes, big-endian
1472 * * +:u64+: unsigned integer, 8 bytes, little-endian
1473 * * +:U64+: unsigned integer, 8 bytes, big-endian
1474 * * +:s64+: signed integer, 8 bytes, little-endian
1475 * * +:S64+: signed integer, 8 bytes, big-endian
1476 * * +:f32+: float, 4 bytes, little-endian
1477 * * +:F32+: float, 4 bytes, big-endian
1478 * * +:f64+: double, 8 bytes, little-endian
1479 * * +:F64+: double, 8 bytes, big-endian
1481 * Example:
1483 * string = [1.5].pack('f')
1484 * # => "\x00\x00\xC0?"
1485 * IO::Buffer.for(string).get_value(:f32, 0)
1486 * # => 1.5
1489 static VALUE
1490 io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
1492 const void *base;
1493 size_t size;
1494 size_t offset = NUM2SIZET(_offset);
1496 rb_io_buffer_get_bytes_for_reading(self, &base, &size);
1498 return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset);
1501 void
1502 rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value)
1504 #define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;}
1505 WRITE_TYPE(U8)
1506 WRITE_TYPE(S8)
1508 WRITE_TYPE(u16)
1509 WRITE_TYPE(U16)
1510 WRITE_TYPE(s16)
1511 WRITE_TYPE(S16)
1513 WRITE_TYPE(u32)
1514 WRITE_TYPE(U32)
1515 WRITE_TYPE(s32)
1516 WRITE_TYPE(S32)
1518 WRITE_TYPE(u64)
1519 WRITE_TYPE(U64)
1520 WRITE_TYPE(s64)
1521 WRITE_TYPE(S64)
1523 WRITE_TYPE(f32)
1524 WRITE_TYPE(F32)
1525 WRITE_TYPE(f64)
1526 WRITE_TYPE(F64)
1527 #undef WRITE_TYPE
1529 rb_raise(rb_eArgError, "Invalid type name!");
1533 * call-seq: set_value(type, offset, value) -> offset
1535 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
1536 * symbols described in #get_value.
1538 * buffer = IO::Buffer.new(8)
1539 * # =>
1540 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1541 * # 0x00000000 00 00 00 00 00 00 00 00
1542 * buffer.set_value(:U8, 1, 111)
1543 * # => 1
1544 * buffer
1545 * # =>
1546 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1547 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
1549 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
1551 * buffer = IO::Buffer.new(8)
1552 * buffer.set_value(:U32, 0, 2.5)
1553 * buffer
1554 * # =>
1555 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1556 * # 0x00000000 00 00 00 02 00 00 00 00
1557 * # ^^ the same as if we'd pass just integer 2
1559 static VALUE
1560 io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
1562 void *base;
1563 size_t size;
1564 size_t offset = NUM2SIZET(_offset);
1566 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1568 rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value);
1570 return SIZET2NUM(offset);
1573 static void
1574 io_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)
1576 void *base;
1577 size_t size;
1578 io_buffer_get_bytes_for_writing(data, &base, &size);
1580 io_buffer_validate_range(data, offset, length);
1582 if (source_offset + length > source_size) {
1583 rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
1586 memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
1589 // (offset, length, source_offset) -> length
1590 static VALUE
1591 io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t source_size, int argc, VALUE *argv)
1593 size_t offset;
1594 size_t length;
1595 size_t source_offset;
1597 // The offset we copy into the buffer:
1598 if (argc >= 1) {
1599 offset = NUM2SIZET(argv[0]);
1600 } else {
1601 offset = 0;
1604 // The offset we start from within the string:
1605 if (argc >= 3) {
1606 source_offset = NUM2SIZET(argv[2]);
1608 if (source_offset > source_size) {
1609 rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
1611 } else {
1612 source_offset = 0;
1615 // The length we are going to copy:
1616 if (argc >= 2 && !RB_NIL_P(argv[1])) {
1617 length = NUM2SIZET(argv[1]);
1618 } else {
1619 // Default to the source offset -> source size:
1620 length = source_size - source_offset;
1623 io_buffer_memcpy(data, offset, source_base, source_offset, source_size, length);
1625 return SIZET2NUM(length);
1629 * call-seq:
1630 * copy(source, [offset, [length, [source_offset]]]) -> size
1632 * Efficiently copy data from a source IO::Buffer into the buffer,
1633 * at +offset+ using +memcpy+. For copying String instances, see #set_string.
1635 * buffer = IO::Buffer.new(32)
1636 * # =>
1637 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
1638 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
1639 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1641 * buffer.copy(IO::Buffer.for("test"), 8)
1642 * # => 4 -- size of data copied
1643 * buffer
1644 * # =>
1645 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
1646 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
1647 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
1649 * #copy can be used to put data into strings associated with buffer:
1651 * string= "data: "
1652 * # => "data: "
1653 * buffer = IO::Buffer.for(str)
1654 * buffer.copy(IO::Buffer.for("test"), 5)
1655 * # => 4
1656 * string
1657 * # => "data:test"
1659 * Attempt to copy into a read-only buffer will fail:
1661 * File.write('test.txt', 'test')
1662 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
1663 * buffer.copy(IO::Buffer.for("test"), 8)
1664 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
1666 * See ::map for details of creation of mutable file mappings, this will
1667 * work:
1669 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
1670 * buffer.copy("boom", 0)
1671 * # => 4
1672 * File.read('test.txt')
1673 * # => "boom"
1675 * Attempt to copy the data which will need place outside of buffer's
1676 * bounds will fail:
1678 * buffer = IO::Buffer.new(2)
1679 * buffer.copy('test', 0)
1680 * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
1683 static VALUE
1684 io_buffer_copy(int argc, VALUE *argv, VALUE self)
1686 if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1688 struct rb_io_buffer *data = NULL;
1689 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1691 VALUE source = argv[0];
1692 const void *source_base;
1693 size_t source_size;
1695 rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
1697 return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1701 * call-seq: get_string([offset, [length, [encoding]]]) -> string
1703 * Read a chunk or all of the buffer into a string, in the specified
1704 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
1707 * buffer = IO::Buffer.for('test')
1708 * buffer.get_string
1709 * # => "test"
1710 * buffer.get_string(2)
1711 * # => "st"
1712 * buffer.get_string(2, 1)
1713 * # => "s"
1716 static VALUE
1717 io_buffer_get_string(int argc, VALUE *argv, VALUE self)
1719 if (argc > 3) rb_error_arity(argc, 0, 3);
1721 struct rb_io_buffer *data = NULL;
1722 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1724 const void *base;
1725 size_t size;
1726 io_buffer_get_bytes_for_reading(data, &base, &size);
1728 size_t offset = 0;
1729 size_t length = size;
1730 rb_encoding *encoding = rb_ascii8bit_encoding();
1732 if (argc >= 1) {
1733 offset = NUM2SIZET(argv[0]);
1736 if (argc >= 2 && !RB_NIL_P(argv[1])) {
1737 length = NUM2SIZET(argv[1]);
1738 } else {
1739 length = size - offset;
1742 if (argc >= 3) {
1743 encoding = rb_find_encoding(argv[2]);
1746 io_buffer_validate_range(data, offset, length);
1748 return rb_enc_str_new((const char*)base + offset, length, encoding);
1751 static VALUE
1752 io_buffer_set_string(int argc, VALUE *argv, VALUE self)
1754 if (argc < 1 || argc > 4) rb_error_arity(argc, 1, 4);
1756 struct rb_io_buffer *data = NULL;
1757 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1759 VALUE string = rb_str_to_str(argv[0]);
1761 const void *source_base = RSTRING_PTR(string);
1762 size_t source_size = RSTRING_LEN(string);
1764 return io_buffer_copy_from(data, source_base, source_size, argc-1, argv+1);
1767 void
1768 rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
1770 void *base;
1771 size_t size;
1773 rb_io_buffer_get_bytes_for_writing(self, &base, &size);
1775 if (offset + length > size) {
1776 rb_raise(rb_eArgError, "The given offset + length out of bounds!");
1779 memset((char*)base + offset, value, length);
1783 * call-seq: clear(value = 0, [offset, [length]]) -> self
1785 * Fill buffer with +value+, starting with +offset+ and going for +length+
1786 * bytes.
1788 * buffer = IO::Buffer.for('test')
1789 * # =>
1790 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1791 * # 0x00000000 74 65 73 74 test
1793 * buffer.clear
1794 * # =>
1795 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1796 * # 0x00000000 00 00 00 00 ....
1798 * buf.clear(1) # fill with 1
1799 * # =>
1800 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1801 * # 0x00000000 01 01 01 01 ....
1803 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
1804 * # =>
1805 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1806 * # 0x00000000 01 02 02 01 ....
1808 * buffer.clear(2, 1) # fill with 2, starting from offset 1
1809 * # =>
1810 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1811 * # 0x00000000 01 02 02 02 ....
1814 static VALUE
1815 io_buffer_clear(int argc, VALUE *argv, VALUE self)
1817 if (argc > 3) rb_error_arity(argc, 0, 3);
1819 struct rb_io_buffer *data = NULL;
1820 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1822 uint8_t value = 0;
1823 if (argc >= 1) {
1824 value = NUM2UINT(argv[0]);
1827 size_t offset = 0;
1828 if (argc >= 2) {
1829 offset = NUM2SIZET(argv[1]);
1832 size_t length;
1833 if (argc >= 3) {
1834 length = NUM2SIZET(argv[2]);
1835 } else {
1836 length = data->size - offset;
1839 rb_io_buffer_clear(self, value, offset, length);
1841 return self;
1844 static
1845 size_t io_buffer_default_size(size_t page_size) {
1846 // Platform agnostic default size, based on empirical performance observation:
1847 const size_t platform_agnostic_default_size = 64*1024;
1849 // Allow user to specify custom default buffer size:
1850 const char *default_size = getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
1851 if (default_size) {
1852 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
1853 int value = atoi(default_size);
1855 // assuming sizeof(int) <= sizeof(size_t)
1856 if (value > 0) {
1857 return value;
1861 if (platform_agnostic_default_size < page_size) {
1862 return page_size;
1865 return platform_agnostic_default_size;
1868 VALUE
1869 rb_io_buffer_read(VALUE self, VALUE io, size_t length)
1871 VALUE scheduler = rb_fiber_scheduler_current();
1872 if (scheduler != Qnil) {
1873 VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length);
1875 if (result != Qundef) {
1876 return result;
1880 struct rb_io_buffer *data = NULL;
1881 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1883 io_buffer_validate_range(data, 0, length);
1885 int descriptor = rb_io_descriptor(io);
1887 void * base;
1888 size_t size;
1889 io_buffer_get_bytes_for_writing(data, &base, &size);
1891 ssize_t result = read(descriptor, base, size);
1893 return rb_fiber_scheduler_io_result(result, errno);
1896 static VALUE
1897 io_buffer_read(VALUE self, VALUE io, VALUE length)
1899 return rb_io_buffer_read(self, io, RB_NUM2SIZE(length));
1902 VALUE
1903 rb_io_buffer_pread(VALUE self, VALUE io, size_t length, off_t offset)
1905 VALUE scheduler = rb_fiber_scheduler_current();
1906 if (scheduler != Qnil) {
1907 VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, self, length, offset);
1909 if (result != Qundef) {
1910 return result;
1914 struct rb_io_buffer *data = NULL;
1915 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1917 io_buffer_validate_range(data, 0, length);
1919 int descriptor = rb_io_descriptor(io);
1921 void * base;
1922 size_t size;
1923 io_buffer_get_bytes_for_writing(data, &base, &size);
1925 #if defined(HAVE_PREAD)
1926 ssize_t result = pread(descriptor, base, size, offset);
1927 #else
1928 // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
1929 off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
1930 if (current_offset == (off_t)-1)
1931 return rb_fiber_scheduler_io_result(-1, errno);
1933 if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
1934 return rb_fiber_scheduler_io_result(-1, errno);
1936 ssize_t result = read(descriptor, base, size);
1938 if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
1939 return rb_fiber_scheduler_io_result(-1, errno);
1940 #endif
1942 return rb_fiber_scheduler_io_result(result, errno);
1945 static VALUE
1946 io_buffer_pread(VALUE self, VALUE io, VALUE length, VALUE offset)
1948 return rb_io_buffer_pread(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
1951 VALUE
1952 rb_io_buffer_write(VALUE self, VALUE io, size_t length)
1954 VALUE scheduler = rb_fiber_scheduler_current();
1955 if (scheduler != Qnil) {
1956 VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length);
1958 if (result != Qundef) {
1959 return result;
1963 struct rb_io_buffer *data = NULL;
1964 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
1966 io_buffer_validate_range(data, 0, length);
1968 int descriptor = rb_io_descriptor(io);
1970 const void * base;
1971 size_t size;
1972 io_buffer_get_bytes_for_reading(data, &base, &size);
1974 ssize_t result = write(descriptor, base, length);
1976 return rb_fiber_scheduler_io_result(result, errno);
1979 static VALUE
1980 io_buffer_write(VALUE self, VALUE io, VALUE length)
1982 return rb_io_buffer_write(self, io, RB_NUM2SIZE(length));
1985 VALUE
1986 rb_io_buffer_pwrite(VALUE self, VALUE io, size_t length, off_t offset)
1988 VALUE scheduler = rb_fiber_scheduler_current();
1989 if (scheduler != Qnil) {
1990 VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, self, length, OFFT2NUM(offset));
1992 if (result != Qundef) {
1993 return result;
1997 struct rb_io_buffer *data = NULL;
1998 TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
2000 io_buffer_validate_range(data, 0, length);
2002 int descriptor = rb_io_descriptor(io);
2004 const void * base;
2005 size_t size;
2006 io_buffer_get_bytes_for_reading(data, &base, &size);
2008 #if defined(HAVE_PWRITE)
2009 ssize_t result = pwrite(descriptor, base, length, offset);
2010 #else
2011 // This emulation is not thread safe, but the GVL means it's unlikely to be a problem.
2012 off_t current_offset = lseek(descriptor, 0, SEEK_CUR);
2013 if (current_offset == (off_t)-1)
2014 return rb_fiber_scheduler_io_result(-1, errno);
2016 if (lseek(descriptor, offset, SEEK_SET) == (off_t)-1)
2017 return rb_fiber_scheduler_io_result(-1, errno);
2019 ssize_t result = write(descriptor, base, length);
2021 if (lseek(descriptor, current_offset, SEEK_SET) == (off_t)-1)
2022 return rb_fiber_scheduler_io_result(-1, errno);
2023 #endif
2025 return rb_fiber_scheduler_io_result(result, errno);
2028 static VALUE
2029 io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
2031 return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
2035 * Document-class: IO::Buffer
2037 * IO::Buffer is a low-level efficient buffer for input/output. There are three
2038 * ways of using buffer:
2040 * * Create an empty buffer with ::new, fill it with data using #copy or
2041 * #set_value, #set_string, get data with #get_string;
2042 * * Create a buffer mapped to some string with ::for, then it could be used
2043 * both for reading with #get_string or #get_value, and writing (writing will
2044 * change the source string, too);
2045 * * Create a buffer mapped to some file with ::map, then it could be used for
2046 * reading and writing the underlying file.
2048 * Interaction with string and file memory is performed by efficient low-level
2049 * C mechanisms like `memcpy`.
2051 * The class is meant to be an utility for implementing more high-level mechanisms
2052 * like Fiber::SchedulerInterface#io_read and Fiber::SchedulerInterface#io_write.
2054 * <b>Examples of usage:</b>
2056 * Empty buffer:
2058 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
2059 * # =>
2060 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
2061 * # ...
2062 * buffer
2063 * # =>
2064 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
2065 * # 0x00000000 00 00 00 00 00 00 00 00
2066 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
2067 * # => 4
2068 * buffer.get_string # get the result
2069 * # => "\x00\x00test\x00\x00"
2071 * \Buffer from string:
2073 * string = 'data'
2074 * buffer = IO::Buffer.for(str)
2075 * # =>
2076 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2077 * # ...
2078 * buffer
2079 * # =>
2080 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2081 * # 0x00000000 64 61 74 61 data
2083 * buffer.get_string(2) # read content starting from offset 2
2084 * # => "ta"
2085 * buffer.set_string('---', 1) # write content, starting from offset 1
2086 * # => 3
2087 * buffer
2088 * # =>
2089 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2090 * # 0x00000000 64 2d 2d 2d d---
2091 * string # original string changed, too
2092 * # => "d---"
2094 * \Buffer from file:
2096 * File.write('test.txt', 'test data')
2097 * # => 9
2098 * buffer = IO::Buffer.map(File.open('test.txt'))
2099 * # =>
2100 * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
2101 * # ...
2102 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
2103 * # => "da"
2104 * buffer.set_string('---', 1) # attempt to write
2105 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
2107 * # To create writable file-mapped buffer
2108 * # Open file for read-write, pass size, offset, and flags=0
2109 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
2110 * buffer.set_string('---', 1)
2111 * # => 3 -- bytes written
2112 * File.read('test.txt')
2113 * # => "t--- data"
2115 * <b>The class is experimental and the interface is subject to change.</b>
2117 void
2118 Init_IO_Buffer(void)
2120 rb_cIOBuffer = rb_define_class_under(rb_cIO, "Buffer", rb_cObject);
2121 rb_eIOBufferLockedError = rb_define_class_under(rb_cIOBuffer, "LockedError", rb_eRuntimeError);
2122 rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
2123 rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
2124 rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
2126 rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
2127 rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
2129 #ifdef _WIN32
2130 SYSTEM_INFO info;
2131 GetSystemInfo(&info);
2132 RUBY_IO_BUFFER_PAGE_SIZE = info.dwPageSize;
2133 #else /* not WIN32 */
2134 RUBY_IO_BUFFER_PAGE_SIZE = sysconf(_SC_PAGESIZE);
2135 #endif
2137 RUBY_IO_BUFFER_DEFAULT_SIZE = io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE);
2139 // Efficient sizing of mapped buffers:
2140 rb_define_const(rb_cIOBuffer, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE));
2141 rb_define_const(rb_cIOBuffer, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE));
2143 rb_define_singleton_method(rb_cIOBuffer, "map", io_buffer_map, -1);
2145 // General use:
2146 rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
2147 rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
2148 rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, 0);
2149 rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
2150 rb_define_method(rb_cIOBuffer, "size", rb_io_buffer_size, 0);
2151 rb_define_method(rb_cIOBuffer, "valid?", rb_io_buffer_valid_p, 0);
2153 // Ownership:
2154 rb_define_method(rb_cIOBuffer, "transfer", rb_io_buffer_transfer, 0);
2156 // Flags:
2157 rb_define_const(rb_cIOBuffer, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL));
2158 rb_define_const(rb_cIOBuffer, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL));
2159 rb_define_const(rb_cIOBuffer, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED));
2160 rb_define_const(rb_cIOBuffer, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED));
2161 rb_define_const(rb_cIOBuffer, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE));
2162 rb_define_const(rb_cIOBuffer, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY));
2164 // Endian:
2165 rb_define_const(rb_cIOBuffer, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN));
2166 rb_define_const(rb_cIOBuffer, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN));
2167 rb_define_const(rb_cIOBuffer, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN));
2168 rb_define_const(rb_cIOBuffer, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN));
2170 rb_define_method(rb_cIOBuffer, "null?", rb_io_buffer_null_p, 0);
2171 rb_define_method(rb_cIOBuffer, "empty?", rb_io_buffer_empty_p, 0);
2172 rb_define_method(rb_cIOBuffer, "external?", rb_io_buffer_external_p, 0);
2173 rb_define_method(rb_cIOBuffer, "internal?", rb_io_buffer_internal_p, 0);
2174 rb_define_method(rb_cIOBuffer, "mapped?", rb_io_buffer_mapped_p, 0);
2175 rb_define_method(rb_cIOBuffer, "locked?", rb_io_buffer_locked_p, 0);
2176 rb_define_method(rb_cIOBuffer, "readonly?", io_buffer_readonly_p, 0);
2178 // Locking to prevent changes while using pointer:
2179 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
2180 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
2181 rb_define_method(rb_cIOBuffer, "locked", rb_io_buffer_locked, 0);
2183 // Manipulation:
2184 rb_define_method(rb_cIOBuffer, "slice", rb_io_buffer_slice, 2);
2185 rb_define_method(rb_cIOBuffer, "<=>", rb_io_buffer_compare, 1);
2186 rb_define_method(rb_cIOBuffer, "resize", io_buffer_resize, 1);
2187 rb_define_method(rb_cIOBuffer, "clear", io_buffer_clear, -1);
2188 rb_define_method(rb_cIOBuffer, "free", rb_io_buffer_free, 0);
2190 rb_include_module(rb_cIOBuffer, rb_mComparable);
2192 #define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name)
2193 DEFINE_TYPE(U8); DEFINE_TYPE(S8);
2194 DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16);
2195 DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32);
2196 DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64);
2197 DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64);
2198 #undef DEFINE_TYPE
2200 // Data access:
2201 rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2);
2202 rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3);
2204 rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1);
2206 rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
2207 rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
2209 // IO operations:
2210 rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
2211 rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
2212 rb_define_method(rb_cIOBuffer, "write", io_buffer_write, 2);
2213 rb_define_method(rb_cIOBuffer, "pwrite", io_buffer_pwrite, 3);