1 /**********************************************************************
5 Copyright (C) 2021 Samuel Grant Dawson Williams
7 **********************************************************************/
10 #include "ruby/io/buffer.h"
11 #include "ruby/fiber/scheduler.h"
14 #include "internal/string.h"
15 #include "internal/bits.h"
16 #include "internal/error.h"
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
;
36 enum rb_io_buffer_flags flags
;
46 io_buffer_map_memory(size_t size
)
49 void * base
= VirtualAlloc(0, size
, MEM_COMMIT
, PAGE_READWRITE
);
52 rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
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");
66 io_buffer_map_file(struct rb_io_buffer
*data
, int descriptor
, size_t size
, off_t offset
, enum rb_io_buffer_flags flags
)
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
;
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
;
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
);
97 rb_sys_fail("io_buffer_map_file:MapViewOfFile");
100 data
->mapping
= mapping
;
102 int protect
= PROT_READ
, access
= 0;
104 if (flags
& RB_IO_BUFFER_READONLY
) {
105 data
->flags
|= RB_IO_BUFFER_READONLY
;
108 protect
|= PROT_WRITE
;
111 if (flags
& RB_IO_BUFFER_PRIVATE
) {
112 data
->flags
|= RB_IO_BUFFER_PRIVATE
;
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");
130 data
->flags
|= RB_IO_BUFFER_MAPPED
;
134 io_buffer_unmap(void* base
, size_t size
)
137 VirtualFree(base
, 0, MEM_RELEASE
);
144 io_buffer_experimental(void)
146 static int warned
= 0;
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!"
160 io_buffer_zero(struct rb_io_buffer
*data
)
165 data
->mapping
= NULL
;
171 io_buffer_initialize(struct rb_io_buffer
*data
, void *base
, size_t size
, enum rb_io_buffer_flags flags
, VALUE source
)
174 // If we are provided a pointer, we use it.
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
);
186 rb_raise(rb_eIOBufferAllocationError
, "Could not allocate buffer!");
189 // Otherwise we don't do anything.
196 data
->source
= source
;
200 io_buffer_free(struct rb_io_buffer
*data
)
203 if (data
->flags
& RB_IO_BUFFER_INTERNAL
) {
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
);
219 CloseHandle(data
->mapping
);
220 data
->mapping
= NULL
;
234 rb_io_buffer_type_mark(void *_data
)
236 struct rb_io_buffer
*data
= _data
;
237 rb_gc_mark(data
->source
);
241 rb_io_buffer_type_free(void *_data
)
243 struct rb_io_buffer
*data
= _data
;
245 io_buffer_free(data
);
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
);
263 static const rb_data_type_t rb_io_buffer_type
= {
264 .wrap_struct_name
= "IO::Buffer",
266 .dmark
= rb_io_buffer_type_mark
,
267 .dfree
= rb_io_buffer_type_free
,
268 .dsize
= rb_io_buffer_type_size
,
271 .flags
= RUBY_TYPED_FREE_IMMEDIATELY
,
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
);
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
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
299 * buffer = IO::Buffer.for(str)
300 * buffer.external? #=> true
302 * buffer.get_string(0, 1)
308 * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
311 rb_io_buffer_type_for(VALUE klass
, VALUE string
)
313 io_buffer_experimental();
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
);
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
);
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
);
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
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)
395 * File.read('test.txt')
398 * Note that some operating systems may not have cache coherency between mapped
399 * buffers and file reads.
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?
413 if (argc
>= 2 && !RB_NIL_P(argv
[1])) {
414 size
= RB_NUM2SIZE(argv
[1]);
417 off_t file_size
= rb_file_size(io
);
419 // Compiler can confirm that we handled file_size < 0 case:
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!");
428 // This conversion should be safe:
429 size
= (size_t)file_size
;
435 offset
= NUM2OFFT(argv
[2]);
438 enum rb_io_buffer_flags flags
= 0;
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.
470 * buffer = IO::Buffer.new(4)
472 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
473 * # 0x00000000 00 00 00 00 ....
475 * buffer.get_string(0, 1) # => "\x00"
477 * buffer.set_string("test")
480 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
481 * # 0x00000000 74 65 73 74 test
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
);
499 size
= RB_NUM2SIZE(argv
[0]);
501 size
= RUBY_IO_BUFFER_DEFAULT_SIZE
;
504 enum rb_io_buffer_flags flags
= 0;
506 flags
= RB_NUM2UINT(argv
[1]);
509 flags
|= io_flags_for_size(size
);
512 io_buffer_initialize(data
, NULL
, size
, flags
, Qnil
);
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
);
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;
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
);
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>
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
, ">");
615 io_buffer_hexdump(VALUE string
, size_t width
, char *base
, size_t size
, int first
)
617 char *text
= alloca(width
+1);
620 for (size_t offset
= 0; offset
< size
; offset
+= width
) {
621 memset(text
, '\0', width
);
623 rb_str_catf(string
, "0x%08zx ", offset
);
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
;
640 rb_str_catf(string
, " %02x", value
);
643 rb_str_cat2(string
, " ");
647 rb_str_catf(string
, " %s", text
);
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
);
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);
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);
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.
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
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
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
747 * External buffer can't be resized.
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);
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
774 * An internal buffer is not associated with any external memory (e.g. string)
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.
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
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.
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
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.
828 * buffer.write(io) # theoretical system call interface
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
;
860 io_buffer_readonly_p(VALUE self
)
862 return RBOOL(rb_io_buffer_readonly_p(self
));
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
;
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
;
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
;
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
917 * buffer = IO::Buffer.new(4)
918 * buffer.locked? #=> false
922 * buffer.write(io) # theoretical system call interface
927 * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
929 * buffer.set_string(...)
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.
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
;
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')
970 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
972 * buffer.get_value(:U8, 0)
973 * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
976 * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
981 * You can resize a freed buffer to re-allocate it.
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
);
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
1020 * buffer = IO::Buffer.for(string)
1022 * slice = buffer.slice(1, 2)
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)
1031 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1032 * # 0x00000000 6f 73 os
1035 * # it is also visible at position 1 of the original buffer
1038 * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
1039 * # 0x00000000 74 6f 73 74 tost
1041 * # ...and original string
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
;
1069 slice
->source
= self
;
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
)) {
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!");
1112 rb_raise(rb_eIOBufferAllocationError
, "The buffer is not allocated!");
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
);
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!");
1138 rb_raise(rb_eIOBufferAllocationError
, "The buffer is not allocated!");
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
1159 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1160 * # 0x00000000 74 65 73 74 test
1163 * # #<IO::Buffer 0x0000000000000000+0 NULL>
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
);
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
);
1197 io_buffer_resize_copy(struct rb_io_buffer
*data
, size_t size
)
1200 struct rb_io_buffer resized
;
1201 io_buffer_initialize(&resized
, NULL
, size
, io_flags_for_size(size
), Qnil
);
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
);
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
);
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
);
1251 if (data
->flags
& RB_IO_BUFFER_INTERNAL
) {
1252 void *base
= realloc(data
->base
, size
);
1255 rb_sys_fail("rb_io_buffer_resize:realloc");
1258 io_buffer_resize_clear(data
, base
, size
);
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
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.
1289 io_buffer_resize(VALUE self
, VALUE size
)
1291 rb_io_buffer_resize(self
, NUM2SIZET(size
));
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+.
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
));
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
1357 ruby_swapf32(float value
)
1359 union swapf32 swap
= {.value
= value
};
1360 swap
.integral
= ruby_swap32(swap
.integral
);
1370 ruby_swapf64(double value
)
1372 union swapf64 swap
= {.value
= value
};
1373 swap
.integral
= ruby_swap64(swap
.integral
);
1377 #define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1378 static ID RB_IO_BUFFER_TYPE_##name; \
1381 io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1383 io_buffer_validate_type(size, *offset + sizeof(type)); \
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); \
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
)
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);
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
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
1483 * string = [1.5].pack('f')
1484 * # => "\x00\x00\xC0?"
1485 * IO::Buffer.for(string).get_value(:f32, 0)
1490 io_buffer_get_value(VALUE self
, VALUE type
, VALUE _offset
)
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
);
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;}
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)
1540 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
1541 * # 0x00000000 00 00 00 00 00 00 00 00
1542 * buffer.set_value(:U8, 1, 111)
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)
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
1560 io_buffer_set_value(VALUE self
, VALUE type
, VALUE _offset
, VALUE value
)
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
);
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
)
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
1591 io_buffer_copy_from(struct rb_io_buffer
*data
, const void *source_base
, size_t source_size
, int argc
, VALUE
*argv
)
1595 size_t source_offset
;
1597 // The offset we copy into the buffer:
1599 offset
= NUM2SIZET(argv
[0]);
1604 // The offset we start from within the string:
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!");
1615 // The length we are going to copy:
1616 if (argc
>= 2 && !RB_NIL_P(argv
[1])) {
1617 length
= NUM2SIZET(argv
[1]);
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
);
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)
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
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:
1653 * buffer = IO::Buffer.for(str)
1654 * buffer.copy(IO::Buffer.for("test"), 5)
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
1669 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
1670 * buffer.copy("boom", 0)
1672 * File.read('test.txt')
1675 * Attempt to copy the data which will need place outside of buffer's
1678 * buffer = IO::Buffer.new(2)
1679 * buffer.copy('test', 0)
1680 * # in `copy': Specified offset+length exceeds source size! (ArgumentError)
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
;
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')
1710 * buffer.get_string(2)
1712 * buffer.get_string(2, 1)
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
);
1726 io_buffer_get_bytes_for_reading(data
, &base
, &size
);
1729 size_t length
= size
;
1730 rb_encoding
*encoding
= rb_ascii8bit_encoding();
1733 offset
= NUM2SIZET(argv
[0]);
1736 if (argc
>= 2 && !RB_NIL_P(argv
[1])) {
1737 length
= NUM2SIZET(argv
[1]);
1739 length
= size
- offset
;
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
);
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);
1768 rb_io_buffer_clear(VALUE self
, uint8_t value
, size_t offset
, size_t length
)
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+
1788 * buffer = IO::Buffer.for('test')
1790 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1791 * # 0x00000000 74 65 73 74 test
1795 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1796 * # 0x00000000 00 00 00 00 ....
1798 * buf.clear(1) # fill with 1
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
1805 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1806 * # 0x00000000 01 02 02 01 ....
1808 * buffer.clear(2, 1) # fill with 2, starting from offset 1
1810 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
1811 * # 0x00000000 01 02 02 02 ....
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
);
1824 value
= NUM2UINT(argv
[0]);
1829 offset
= NUM2SIZET(argv
[1]);
1834 length
= NUM2SIZET(argv
[2]);
1836 length
= data
->size
- offset
;
1839 rb_io_buffer_clear(self
, value
, offset
, length
);
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");
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)
1861 if (platform_agnostic_default_size
< page_size
) {
1865 return platform_agnostic_default_size
;
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
) {
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
);
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
);
1897 io_buffer_read(VALUE self
, VALUE io
, VALUE length
)
1899 return rb_io_buffer_read(self
, io
, RB_NUM2SIZE(length
));
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
) {
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
);
1923 io_buffer_get_bytes_for_writing(data
, &base
, &size
);
1925 #if defined(HAVE_PREAD)
1926 ssize_t result
= pread(descriptor
, base
, size
, offset
);
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
);
1942 return rb_fiber_scheduler_io_result(result
, errno
);
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
));
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
) {
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
);
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
);
1980 io_buffer_write(VALUE self
, VALUE io
, VALUE length
)
1982 return rb_io_buffer_write(self
, io
, RB_NUM2SIZE(length
));
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
) {
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
);
2006 io_buffer_get_bytes_for_reading(data
, &base
, &size
);
2008 #if defined(HAVE_PWRITE)
2009 ssize_t result
= pwrite(descriptor
, base
, length
, offset
);
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
);
2025 return rb_fiber_scheduler_io_result(result
, errno
);
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>
2058 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
2060 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
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
2068 * buffer.get_string # get the result
2069 * # => "\x00\x00test\x00\x00"
2071 * \Buffer from string:
2074 * buffer = IO::Buffer.for(str)
2076 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2080 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2081 * # 0x00000000 64 61 74 61 data
2083 * buffer.get_string(2) # read content starting from offset 2
2085 * buffer.set_string('---', 1) # write content, starting from offset 1
2089 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
2090 * # 0x00000000 64 2d 2d 2d d---
2091 * string # original string changed, too
2094 * \Buffer from file:
2096 * File.write('test.txt', 'test data')
2098 * buffer = IO::Buffer.map(File.open('test.txt'))
2100 * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
2102 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
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')
2115 * <b>The class is experimental and the interface is subject to change.</b>
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);
2131 GetSystemInfo(&info
);
2132 RUBY_IO_BUFFER_PAGE_SIZE
= info
.dwPageSize
;
2133 #else /* not WIN32 */
2134 RUBY_IO_BUFFER_PAGE_SIZE
= sysconf(_SC_PAGESIZE
);
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);
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);
2154 rb_define_method(rb_cIOBuffer
, "transfer", rb_io_buffer_transfer
, 0);
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
));
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);
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
);
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);
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);