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/array.h"
15 #include "internal/bits.h"
16 #include "internal/error.h"
17 #include "internal/numeric.h"
18 #include "internal/string.h"
19 #include "internal/thread.h"
22 VALUE rb_eIOBufferLockedError
;
23 VALUE rb_eIOBufferAllocationError
;
24 VALUE rb_eIOBufferAccessError
;
25 VALUE rb_eIOBufferInvalidatedError
;
26 VALUE rb_eIOBufferMaskError
;
28 size_t RUBY_IO_BUFFER_PAGE_SIZE
;
29 size_t RUBY_IO_BUFFER_DEFAULT_SIZE
;
38 RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH
= 16,
40 RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE
= 256,
41 RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH
= 16,
43 // This is used to validate the flags given by the user.
44 RB_IO_BUFFER_FLAGS_MASK
= RB_IO_BUFFER_EXTERNAL
| RB_IO_BUFFER_INTERNAL
| RB_IO_BUFFER_MAPPED
| RB_IO_BUFFER_SHARED
| RB_IO_BUFFER_LOCKED
| RB_IO_BUFFER_PRIVATE
| RB_IO_BUFFER_READONLY
,
46 RB_IO_BUFFER_DEBUG
= 0,
52 enum rb_io_buffer_flags flags
;
62 io_buffer_map_memory(size_t size
, int flags
)
65 void * base
= VirtualAlloc(0, size
, MEM_COMMIT
, PAGE_READWRITE
);
68 rb_sys_fail("io_buffer_map_memory:VirtualAlloc");
71 int mmap_flags
= MAP_ANONYMOUS
;
72 if (flags
& RB_IO_BUFFER_SHARED
) {
73 mmap_flags
|= MAP_SHARED
;
76 mmap_flags
|= MAP_PRIVATE
;
79 void * base
= mmap(NULL
, size
, PROT_READ
| PROT_WRITE
, mmap_flags
, -1, 0);
81 if (base
== MAP_FAILED
) {
82 rb_sys_fail("io_buffer_map_memory:mmap");
90 io_buffer_map_file(struct rb_io_buffer
*buffer
, int descriptor
, size_t size
, rb_off_t offset
, enum rb_io_buffer_flags flags
)
93 HANDLE file
= (HANDLE
)_get_osfhandle(descriptor
);
94 if (!file
) rb_sys_fail("io_buffer_map_descriptor:_get_osfhandle");
96 DWORD protect
= PAGE_READONLY
, access
= FILE_MAP_READ
;
98 if (flags
& RB_IO_BUFFER_READONLY
) {
99 buffer
->flags
|= RB_IO_BUFFER_READONLY
;
102 protect
= PAGE_READWRITE
;
103 access
= FILE_MAP_WRITE
;
106 if (flags
& RB_IO_BUFFER_PRIVATE
) {
107 protect
= PAGE_WRITECOPY
;
108 access
= FILE_MAP_COPY
;
109 buffer
->flags
|= RB_IO_BUFFER_PRIVATE
;
112 // This buffer refers to external buffer.
113 buffer
->flags
|= RB_IO_BUFFER_EXTERNAL
;
114 buffer
->flags
|= RB_IO_BUFFER_SHARED
;
117 HANDLE mapping
= CreateFileMapping(file
, NULL
, protect
, 0, 0, NULL
);
118 if (RB_IO_BUFFER_DEBUG
) fprintf(stderr
, "io_buffer_map_file:CreateFileMapping -> %p\n", mapping
);
119 if (!mapping
) rb_sys_fail("io_buffer_map_descriptor:CreateFileMapping");
121 void *base
= MapViewOfFile(mapping
, access
, (DWORD
)(offset
>> 32), (DWORD
)(offset
& 0xFFFFFFFF), size
);
124 CloseHandle(mapping
);
125 rb_sys_fail("io_buffer_map_file:MapViewOfFile");
128 buffer
->mapping
= mapping
;
130 int protect
= PROT_READ
, access
= 0;
132 if (flags
& RB_IO_BUFFER_READONLY
) {
133 buffer
->flags
|= RB_IO_BUFFER_READONLY
;
136 protect
|= PROT_WRITE
;
139 if (flags
& RB_IO_BUFFER_PRIVATE
) {
140 buffer
->flags
|= RB_IO_BUFFER_PRIVATE
;
141 access
|= MAP_PRIVATE
;
144 // This buffer refers to external buffer.
145 buffer
->flags
|= RB_IO_BUFFER_EXTERNAL
;
146 buffer
->flags
|= RB_IO_BUFFER_SHARED
;
147 access
|= MAP_SHARED
;
150 void *base
= mmap(NULL
, size
, protect
, access
, descriptor
, offset
);
152 if (base
== MAP_FAILED
) {
153 rb_sys_fail("io_buffer_map_file:mmap");
160 buffer
->flags
|= RB_IO_BUFFER_MAPPED
;
161 buffer
->flags
|= RB_IO_BUFFER_FILE
;
165 io_buffer_experimental(void)
167 static int warned
= 0;
173 if (rb_warning_category_enabled_p(RB_WARN_CATEGORY_EXPERIMENTAL
)) {
174 rb_category_warn(RB_WARN_CATEGORY_EXPERIMENTAL
,
175 "IO::Buffer is experimental and both the Ruby and C interface may change in the future!"
181 io_buffer_zero(struct rb_io_buffer
*buffer
)
186 buffer
->mapping
= NULL
;
188 buffer
->source
= Qnil
;
192 io_buffer_initialize(VALUE self
, struct rb_io_buffer
*buffer
, void *base
, size_t size
, enum rb_io_buffer_flags flags
, VALUE source
)
195 // If we are provided a pointer, we use it.
198 // If we are provided a non-zero size, we allocate it:
199 if (flags
& RB_IO_BUFFER_INTERNAL
) {
200 base
= calloc(size
, 1);
202 else if (flags
& RB_IO_BUFFER_MAPPED
) {
203 base
= io_buffer_map_memory(size
, flags
);
207 rb_raise(rb_eIOBufferAllocationError
, "Could not allocate buffer!");
211 // Otherwise we don't do anything.
217 buffer
->flags
= flags
;
218 RB_OBJ_WRITE(self
, &buffer
->source
, source
);
221 buffer
->mapping
= NULL
;
226 io_buffer_free(struct rb_io_buffer
*buffer
)
229 if (buffer
->flags
& RB_IO_BUFFER_INTERNAL
) {
233 if (buffer
->flags
& RB_IO_BUFFER_MAPPED
) {
235 if (buffer
->flags
& RB_IO_BUFFER_FILE
) {
236 UnmapViewOfFile(buffer
->base
);
239 VirtualFree(buffer
->base
, 0, MEM_RELEASE
);
242 munmap(buffer
->base
, buffer
->size
);
246 // Previously we had this, but we found out due to the way GC works, we
247 // can't refer to any other Ruby objects here.
248 // if (RB_TYPE_P(buffer->source, T_STRING)) {
249 // rb_str_unlocktmp(buffer->source);
256 buffer
->source
= Qnil
;
260 if (buffer
->mapping
) {
261 if (RB_IO_BUFFER_DEBUG
) fprintf(stderr
, "io_buffer_free:CloseHandle -> %p\n", buffer
->mapping
);
262 if (!CloseHandle(buffer
->mapping
)) {
263 fprintf(stderr
, "io_buffer_free:GetLastError -> %lu\n", GetLastError());
265 buffer
->mapping
= NULL
;
271 rb_io_buffer_type_mark(void *_buffer
)
273 struct rb_io_buffer
*buffer
= _buffer
;
274 rb_gc_mark(buffer
->source
);
278 rb_io_buffer_type_free(void *_buffer
)
280 struct rb_io_buffer
*buffer
= _buffer
;
282 io_buffer_free(buffer
);
286 rb_io_buffer_type_size(const void *_buffer
)
288 const struct rb_io_buffer
*buffer
= _buffer
;
289 size_t total
= sizeof(struct rb_io_buffer
);
292 total
+= buffer
->size
;
298 static const rb_data_type_t rb_io_buffer_type
= {
299 .wrap_struct_name
= "IO::Buffer",
301 .dmark
= rb_io_buffer_type_mark
,
302 .dfree
= rb_io_buffer_type_free
,
303 .dsize
= rb_io_buffer_type_size
,
306 .flags
= RUBY_TYPED_FREE_IMMEDIATELY
| RUBY_TYPED_WB_PROTECTED
| RUBY_TYPED_EMBEDDABLE
,
309 static inline enum rb_io_buffer_flags
310 io_buffer_extract_flags(VALUE argument
)
312 if (rb_int_negative_p(argument
)) {
313 rb_raise(rb_eArgError
, "Flags can't be negative!");
316 enum rb_io_buffer_flags flags
= RB_NUM2UINT(argument
);
318 // We deliberately ignore unknown flags. Any future flags which are exposed this way should be safe to ignore.
319 return flags
& RB_IO_BUFFER_FLAGS_MASK
;
322 // Extract an offset argument, which must be a non-negative integer.
324 io_buffer_extract_offset(VALUE argument
)
326 if (rb_int_negative_p(argument
)) {
327 rb_raise(rb_eArgError
, "Offset can't be negative!");
330 return NUM2SIZET(argument
);
333 // Extract a length argument, which must be a non-negative integer.
334 // Length is generally considered a mutable property of an object and
335 // semantically should be considered a subset of "size" as a concept.
337 io_buffer_extract_length(VALUE argument
)
339 if (rb_int_negative_p(argument
)) {
340 rb_raise(rb_eArgError
, "Length can't be negative!");
343 return NUM2SIZET(argument
);
346 // Extract a size argument, which must be a non-negative integer.
347 // Size is generally considered an immutable property of an object.
349 io_buffer_extract_size(VALUE argument
)
351 if (rb_int_negative_p(argument
)) {
352 rb_raise(rb_eArgError
, "Size can't be negative!");
355 return NUM2SIZET(argument
);
358 // Extract a width argument, which must be a non-negative integer, and must be
359 // at least the given minimum.
361 io_buffer_extract_width(VALUE argument
, size_t minimum
)
363 if (rb_int_negative_p(argument
)) {
364 rb_raise(rb_eArgError
, "Width can't be negative!");
367 size_t width
= NUM2SIZET(argument
);
369 if (width
< minimum
) {
370 rb_raise(rb_eArgError
, "Width must be at least %" PRIuSIZE
"!", minimum
);
376 // Compute the default length for a buffer, given an offset into that buffer.
377 // The default length is the size of the buffer minus the offset. The offset
378 // must be less than the size of the buffer otherwise the length will be
379 // invalid; in that case, an ArgumentError exception will be raised.
381 io_buffer_default_length(const struct rb_io_buffer
*buffer
, size_t offset
)
383 if (offset
> buffer
->size
) {
384 rb_raise(rb_eArgError
, "The given offset is bigger than the buffer size!");
387 // Note that the "length" is computed by the size the offset.
388 return buffer
->size
- offset
;
391 // Extract the optional length and offset arguments, returning the buffer.
392 // The length and offset are optional, but if they are provided, they must be
393 // positive integers. If the length is not provided, the default length is
394 // computed from the buffer size and offset. If the offset is not provided, it
396 static inline struct rb_io_buffer
*
397 io_buffer_extract_length_offset(VALUE self
, int argc
, VALUE argv
[], size_t *length
, size_t *offset
)
399 struct rb_io_buffer
*buffer
= NULL
;
400 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
402 if (argc
>= 2 && !NIL_P(argv
[1])) {
403 *offset
= io_buffer_extract_offset(argv
[1]);
409 if (argc
>= 1 && !NIL_P(argv
[0])) {
410 *length
= io_buffer_extract_length(argv
[0]);
413 *length
= io_buffer_default_length(buffer
, *offset
);
419 // Extract the optional offset and length arguments, returning the buffer.
420 // Similar to `io_buffer_extract_length_offset` but with the order of arguments
423 // After much consideration, I decided to accept both forms.
424 // The `(offset, length)` order is more natural when referring about data,
425 // while the `(length, offset)` order is more natural when referring to
426 // read/write operations. In many cases, with the latter form, `offset`
427 // is usually not supplied.
428 static inline struct rb_io_buffer
*
429 io_buffer_extract_offset_length(VALUE self
, int argc
, VALUE argv
[], size_t *offset
, size_t *length
)
431 struct rb_io_buffer
*buffer
= NULL
;
432 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
434 if (argc
>= 1 && !NIL_P(argv
[0])) {
435 *offset
= io_buffer_extract_offset(argv
[0]);
441 if (argc
>= 2 && !NIL_P(argv
[1])) {
442 *length
= io_buffer_extract_length(argv
[1]);
445 *length
= io_buffer_default_length(buffer
, *offset
);
452 rb_io_buffer_type_allocate(VALUE self
)
454 struct rb_io_buffer
*buffer
= NULL
;
455 VALUE instance
= TypedData_Make_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
457 io_buffer_zero(buffer
);
462 static VALUE
io_buffer_for_make_instance(VALUE klass
, VALUE string
, enum rb_io_buffer_flags flags
)
464 VALUE instance
= rb_io_buffer_type_allocate(klass
);
466 struct rb_io_buffer
*buffer
= NULL
;
467 TypedData_Get_Struct(instance
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
469 flags
|= RB_IO_BUFFER_EXTERNAL
;
471 if (RB_OBJ_FROZEN(string
))
472 flags
|= RB_IO_BUFFER_READONLY
;
474 if (!(flags
& RB_IO_BUFFER_READONLY
))
475 rb_str_modify(string
);
477 io_buffer_initialize(instance
, buffer
, RSTRING_PTR(string
), RSTRING_LEN(string
), flags
, string
);
482 struct io_buffer_for_yield_instance_arguments
{
486 enum rb_io_buffer_flags flags
;
490 io_buffer_for_yield_instance(VALUE _arguments
)
492 struct io_buffer_for_yield_instance_arguments
*arguments
= (struct io_buffer_for_yield_instance_arguments
*)_arguments
;
494 arguments
->instance
= io_buffer_for_make_instance(arguments
->klass
, arguments
->string
, arguments
->flags
);
496 rb_str_locktmp(arguments
->string
);
498 return rb_yield(arguments
->instance
);
502 io_buffer_for_yield_instance_ensure(VALUE _arguments
)
504 struct io_buffer_for_yield_instance_arguments
*arguments
= (struct io_buffer_for_yield_instance_arguments
*)_arguments
;
506 if (arguments
->instance
!= Qnil
) {
507 rb_io_buffer_free(arguments
->instance
);
510 rb_str_unlocktmp(arguments
->string
);
517 * IO::Buffer.for(string) -> readonly io_buffer
518 * IO::Buffer.for(string) {|io_buffer| ... read/write io_buffer ...}
520 * Creates a zero-copy IO::Buffer from the given string's memory. Without a
521 * block a frozen internal copy of the string is created efficiently and used
522 * as the buffer source. When a block is provided, the buffer is associated
523 * directly with the string's internal buffer and updating the buffer will
526 * Until #free is invoked on the buffer, either explicitly or via the garbage
527 * collector, the source string will be locked and cannot be modified.
529 * If the string is frozen, it will create a read-only buffer which cannot be
530 * modified. If the string is shared, it may trigger a copy-on-write when
531 * using the block form.
534 * buffer = IO::Buffer.for(string)
535 * buffer.external? #=> true
537 * buffer.get_string(0, 1)
543 * # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
545 * IO::Buffer.for(string) do |buffer|
546 * buffer.set_string("T")
552 rb_io_buffer_type_for(VALUE klass
, VALUE string
)
556 // If the string is frozen, both code paths are okay.
557 // If the string is not frozen, if a block is not given, it must be frozen.
558 if (rb_block_given_p()) {
559 struct io_buffer_for_yield_instance_arguments arguments
= {
566 return rb_ensure(io_buffer_for_yield_instance
, (VALUE
)&arguments
, io_buffer_for_yield_instance_ensure
, (VALUE
)&arguments
);
569 // This internally returns the source string if it's already frozen.
570 string
= rb_str_tmp_frozen_acquire(string
);
571 return io_buffer_for_make_instance(klass
, string
, RB_IO_BUFFER_READONLY
);
577 * IO::Buffer.string(length) {|io_buffer| ... read/write io_buffer ...} -> string
579 * Creates a new string of the given length and yields a zero-copy IO::Buffer
580 * instance to the block which uses the string as a source. The block is
581 * expected to write to the buffer and the string will be returned.
583 * IO::Buffer.string(4) do |buffer|
584 * buffer.set_string("Ruby")
589 rb_io_buffer_type_string(VALUE klass
, VALUE length
)
591 VALUE string
= rb_str_new(NULL
, RB_NUM2LONG(length
));
593 struct io_buffer_for_yield_instance_arguments arguments
= {
599 rb_ensure(io_buffer_for_yield_instance
, (VALUE
)&arguments
, io_buffer_for_yield_instance_ensure
, (VALUE
)&arguments
);
605 rb_io_buffer_new(void *base
, size_t size
, enum rb_io_buffer_flags flags
)
607 VALUE instance
= rb_io_buffer_type_allocate(rb_cIOBuffer
);
609 struct rb_io_buffer
*buffer
= NULL
;
610 TypedData_Get_Struct(instance
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
612 io_buffer_initialize(instance
, buffer
, base
, size
, flags
, Qnil
);
618 rb_io_buffer_map(VALUE io
, size_t size
, rb_off_t offset
, enum rb_io_buffer_flags flags
)
620 io_buffer_experimental();
622 VALUE instance
= rb_io_buffer_type_allocate(rb_cIOBuffer
);
624 struct rb_io_buffer
*buffer
= NULL
;
625 TypedData_Get_Struct(instance
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
627 int descriptor
= rb_io_descriptor(io
);
629 io_buffer_map_file(buffer
, descriptor
, size
, offset
, flags
);
635 * call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
637 * Create an IO::Buffer for reading from +file+ by memory-mapping the file.
638 * +file_io+ should be a +File+ instance, opened for reading.
640 * Optional +size+ and +offset+ of mapping can be specified.
642 * By default, the buffer would be immutable (read only); to create a writable
643 * mapping, you need to open a file in read-write mode, and explicitly pass
644 * +flags+ argument without IO::Buffer::IMMUTABLE.
646 * File.write('test.txt', 'test')
648 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
649 * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
651 * buffer.readonly? # => true
656 * buffer.set_string('b', 0)
657 * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
659 * # create read/write mapping: length 4 bytes, offset 0, flags 0
660 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
661 * buffer.set_string('b', 0)
665 * File.read('test.txt')
668 * Note that some operating systems may not have cache coherency between mapped
669 * buffers and file reads.
672 io_buffer_map(int argc
, VALUE
*argv
, VALUE klass
)
674 rb_check_arity(argc
, 1, 4);
676 // We might like to handle a string path?
680 if (argc
>= 2 && !RB_NIL_P(argv
[1])) {
681 size
= io_buffer_extract_size(argv
[1]);
684 rb_off_t file_size
= rb_file_size(io
);
686 // Compiler can confirm that we handled file_size < 0 case:
688 rb_raise(rb_eArgError
, "Invalid negative file size!");
690 // Here, we assume that file_size is positive:
691 else if ((uintmax_t)file_size
> SIZE_MAX
) {
692 rb_raise(rb_eArgError
, "File larger than address space!");
695 // This conversion should be safe:
696 size
= (size_t)file_size
;
700 // This is the file offset, not the buffer offset:
703 offset
= NUM2OFFT(argv
[2]);
706 enum rb_io_buffer_flags flags
= 0;
708 flags
= io_buffer_extract_flags(argv
[3]);
711 return rb_io_buffer_map(io
, size
, offset
, flags
);
714 // Compute the optimal allocation flags for a buffer of the given size.
715 static inline enum rb_io_buffer_flags
716 io_flags_for_size(size_t size
)
718 if (size
>= RUBY_IO_BUFFER_PAGE_SIZE
) {
719 return RB_IO_BUFFER_MAPPED
;
722 return RB_IO_BUFFER_INTERNAL
;
726 * call-seq: IO::Buffer.new([size = DEFAULT_SIZE, [flags = 0]]) -> io_buffer
728 * Create a new zero-filled IO::Buffer of +size+ bytes.
729 * By default, the buffer will be _internal_: directly allocated chunk
730 * of the memory. But if the requested +size+ is more than OS-specific
731 * IO::Buffer::PAGE_SIZE, the buffer would be allocated using the
732 * virtual memory mechanism (anonymous +mmap+ on Unix, +VirtualAlloc+
733 * on Windows). The behavior can be forced by passing IO::Buffer::MAPPED
734 * as a second parameter.
736 * buffer = IO::Buffer.new(4)
738 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
739 * # 0x00000000 00 00 00 00 ....
741 * buffer.get_string(0, 1) # => "\x00"
743 * buffer.set_string("test")
746 * # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
747 * # 0x00000000 74 65 73 74 test
750 rb_io_buffer_initialize(int argc
, VALUE
*argv
, VALUE self
)
752 io_buffer_experimental();
754 rb_check_arity(argc
, 0, 2);
756 struct rb_io_buffer
*buffer
= NULL
;
757 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
761 size
= io_buffer_extract_size(argv
[0]);
764 size
= RUBY_IO_BUFFER_DEFAULT_SIZE
;
767 enum rb_io_buffer_flags flags
= 0;
769 flags
= io_buffer_extract_flags(argv
[1]);
772 flags
|= io_flags_for_size(size
);
775 io_buffer_initialize(self
, buffer
, NULL
, size
, flags
, Qnil
);
781 io_buffer_validate_slice(VALUE source
, void *base
, size_t size
)
783 void *source_base
= NULL
;
784 size_t source_size
= 0;
786 if (RB_TYPE_P(source
, T_STRING
)) {
787 RSTRING_GETMEM(source
, source_base
, source_size
);
790 rb_io_buffer_get_bytes(source
, &source_base
, &source_size
);
793 // Source is invalid:
794 if (source_base
== NULL
) return 0;
796 // Base is out of range:
797 if (base
< source_base
) return 0;
799 const void *source_end
= (char*)source_base
+ source_size
;
800 const void *end
= (char*)base
+ size
;
802 // End is out of range:
803 if (end
> source_end
) return 0;
810 io_buffer_validate(struct rb_io_buffer
*buffer
)
812 if (buffer
->source
!= Qnil
) {
813 // Only slices incur this overhead, unfortunately... better safe than sorry!
814 return io_buffer_validate_slice(buffer
->source
, buffer
->base
, buffer
->size
);
821 enum rb_io_buffer_flags
822 rb_io_buffer_get_bytes(VALUE self
, void **base
, size_t *size
)
824 struct rb_io_buffer
*buffer
= NULL
;
825 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
827 if (io_buffer_validate(buffer
)) {
829 *base
= buffer
->base
;
830 *size
= buffer
->size
;
832 return buffer
->flags
;
842 // Internal function for accessing bytes for writing, wil
844 io_buffer_get_bytes_for_writing(struct rb_io_buffer
*buffer
, void **base
, size_t *size
)
846 if (buffer
->flags
& RB_IO_BUFFER_READONLY
) {
847 rb_raise(rb_eIOBufferAccessError
, "Buffer is not writable!");
850 if (!io_buffer_validate(buffer
)) {
851 rb_raise(rb_eIOBufferInvalidatedError
, "Buffer is invalid!");
855 *base
= buffer
->base
;
856 *size
= buffer
->size
;
864 rb_io_buffer_get_bytes_for_writing(VALUE self
, void **base
, size_t *size
)
866 struct rb_io_buffer
*buffer
= NULL
;
867 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
869 io_buffer_get_bytes_for_writing(buffer
, base
, size
);
873 io_buffer_get_bytes_for_reading(struct rb_io_buffer
*buffer
, const void **base
, size_t *size
)
875 if (!io_buffer_validate(buffer
)) {
876 rb_raise(rb_eIOBufferInvalidatedError
, "Buffer has been invalidated!");
880 *base
= buffer
->base
;
881 *size
= buffer
->size
;
889 rb_io_buffer_get_bytes_for_reading(VALUE self
, const void **base
, size_t *size
)
891 struct rb_io_buffer
*buffer
= NULL
;
892 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
894 io_buffer_get_bytes_for_reading(buffer
, base
, size
);
898 * call-seq: to_s -> string
900 * Short representation of the buffer. It includes the address, size and
901 * symbolic flags. This format is subject to change.
903 * puts IO::Buffer.new(4) # uses to_s internally
904 * # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
907 rb_io_buffer_to_s(VALUE self
)
909 struct rb_io_buffer
*buffer
= NULL
;
910 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
912 VALUE result
= rb_str_new_cstr("#<");
914 rb_str_append(result
, rb_class_name(CLASS_OF(self
)));
915 rb_str_catf(result
, " %p+%"PRIdSIZE
, buffer
->base
, buffer
->size
);
917 if (buffer
->base
== NULL
) {
918 rb_str_cat2(result
, " NULL");
921 if (buffer
->flags
& RB_IO_BUFFER_EXTERNAL
) {
922 rb_str_cat2(result
, " EXTERNAL");
925 if (buffer
->flags
& RB_IO_BUFFER_INTERNAL
) {
926 rb_str_cat2(result
, " INTERNAL");
929 if (buffer
->flags
& RB_IO_BUFFER_MAPPED
) {
930 rb_str_cat2(result
, " MAPPED");
933 if (buffer
->flags
& RB_IO_BUFFER_FILE
) {
934 rb_str_cat2(result
, " FILE");
937 if (buffer
->flags
& RB_IO_BUFFER_SHARED
) {
938 rb_str_cat2(result
, " SHARED");
941 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
942 rb_str_cat2(result
, " LOCKED");
945 if (buffer
->flags
& RB_IO_BUFFER_PRIVATE
) {
946 rb_str_cat2(result
, " PRIVATE");
949 if (buffer
->flags
& RB_IO_BUFFER_READONLY
) {
950 rb_str_cat2(result
, " READONLY");
953 if (buffer
->source
!= Qnil
) {
954 rb_str_cat2(result
, " SLICE");
957 if (!io_buffer_validate(buffer
)) {
958 rb_str_cat2(result
, " INVALID");
961 return rb_str_cat2(result
, ">");
964 // Compute the output size of a hexdump of the given width (bytes per line), total size, and whether it is the first line in the output.
965 // This is used to preallocate the output string.
967 io_buffer_hexdump_output_size(size_t width
, size_t size
, int first
)
969 // The preview on the right hand side is 1:1:
972 size_t whole_lines
= (size
/ width
);
973 size_t partial_line
= (size
% width
) ? 1 : 0;
976 // 1 byte 10 bytes 1 byte width*3 bytes 1 byte size bytes
977 // (newline) (address) (space) (hexdump ) (space) (preview)
978 total
+= (whole_lines
+ partial_line
) * (1 + 10 + width
*3 + 1 + 1);
980 // If the hexdump is the first line, one less newline will be emitted:
981 if (size
&& first
) total
-= 1;
986 // Append a hexdump of the given width (bytes per line), base address, size, and whether it is the first line in the output.
987 // If the hexdump is not the first line, it will prepend a newline if there is any output at all.
988 // If formatting here is adjusted, please update io_buffer_hexdump_output_size accordingly.
990 io_buffer_hexdump(VALUE string
, size_t width
, const char *base
, size_t length
, size_t offset
, int first
)
992 char *text
= alloca(width
+1);
995 for (; offset
< length
; offset
+= width
) {
996 memset(text
, '\0', width
);
998 rb_str_catf(string
, "0x%08" PRIxSIZE
" ", offset
);
1002 rb_str_catf(string
, "\n0x%08" PRIxSIZE
" ", offset
);
1005 for (size_t i
= 0; i
< width
; i
+= 1) {
1006 if (offset
+i
< length
) {
1007 unsigned char value
= ((unsigned char*)base
)[offset
+i
];
1009 if (value
< 127 && isprint(value
)) {
1010 text
[i
] = (char)value
;
1016 rb_str_catf(string
, " %02x", value
);
1019 rb_str_cat2(string
, " ");
1023 rb_str_catf(string
, " %s", text
);
1030 * call-seq: inspect -> string
1032 * Inspect the buffer and report useful information about it's internal state.
1033 * Only a limited portion of the buffer will be displayed in a hexdump style
1036 * buffer = IO::Buffer.for("Hello World")
1037 * puts buffer.inspect
1038 * # #<IO::Buffer 0x000000010198ccd8+11 EXTERNAL READONLY SLICE>
1039 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1042 rb_io_buffer_inspect(VALUE self
)
1044 struct rb_io_buffer
*buffer
= NULL
;
1045 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1047 VALUE result
= rb_io_buffer_to_s(self
);
1049 if (io_buffer_validate(buffer
)) {
1050 // Limit the maximum size generated by inspect:
1051 size_t size
= buffer
->size
;
1054 if (size
> RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE
) {
1055 size
= RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE
;
1059 io_buffer_hexdump(result
, RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH
, buffer
->base
, size
, 0, 0);
1062 rb_str_catf(result
, "\n(and %" PRIuSIZE
" more bytes not printed)", buffer
->size
- size
);
1070 * call-seq: size -> integer
1072 * Returns the size of the buffer that was explicitly set (on creation with ::new
1073 * or on #resize), or deduced on buffer's creation from string or file.
1076 rb_io_buffer_size(VALUE self
)
1078 struct rb_io_buffer
*buffer
= NULL
;
1079 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1081 return SIZET2NUM(buffer
->size
);
1085 * call-seq: valid? -> true or false
1087 * Returns whether the buffer buffer is accessible.
1089 * A buffer becomes invalid if it is a slice of another buffer (or string)
1090 * which has been freed or re-allocated at a different address.
1093 rb_io_buffer_valid_p(VALUE self
)
1095 struct rb_io_buffer
*buffer
= NULL
;
1096 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1098 return RBOOL(io_buffer_validate(buffer
));
1102 * call-seq: null? -> true or false
1104 * If the buffer was freed with #free, transferred with #transfer, or was
1105 * never allocated in the first place.
1107 * buffer = IO::Buffer.new(0)
1108 * buffer.null? #=> true
1110 * buffer = IO::Buffer.new(4)
1111 * buffer.null? #=> false
1113 * buffer.null? #=> true
1116 rb_io_buffer_null_p(VALUE self
)
1118 struct rb_io_buffer
*buffer
= NULL
;
1119 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1121 return RBOOL(buffer
->base
== NULL
);
1125 * call-seq: empty? -> true or false
1127 * If the buffer has 0 size: it is created by ::new with size 0, or with ::for
1128 * from an empty string. (Note that empty files can't be mapped, so the buffer
1129 * created with ::map will never be empty.)
1132 rb_io_buffer_empty_p(VALUE self
)
1134 struct rb_io_buffer
*buffer
= NULL
;
1135 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1137 return RBOOL(buffer
->size
== 0);
1141 * call-seq: external? -> true or false
1143 * The buffer is _external_ if it references the memory which is not
1144 * allocated or mapped by the buffer itself.
1146 * A buffer created using ::for has an external reference to the string's
1149 * External buffer can't be resized.
1152 rb_io_buffer_external_p(VALUE self
)
1154 struct rb_io_buffer
*buffer
= NULL
;
1155 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1157 return RBOOL(buffer
->flags
& RB_IO_BUFFER_EXTERNAL
);
1161 * call-seq: internal? -> true or false
1163 * If the buffer is _internal_, meaning it references memory allocated by the
1166 * An internal buffer is not associated with any external memory (e.g. string)
1169 * Internal buffers are created using ::new and is the default when the
1170 * requested size is less than the IO::Buffer::PAGE_SIZE and it was not
1171 * requested to be mapped on creation.
1173 * Internal buffers can be resized, and such an operation will typically
1174 * invalidate all slices, but not always.
1177 rb_io_buffer_internal_p(VALUE self
)
1179 struct rb_io_buffer
*buffer
= NULL
;
1180 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1182 return RBOOL(buffer
->flags
& RB_IO_BUFFER_INTERNAL
);
1186 * call-seq: mapped? -> true or false
1188 * If the buffer is _mapped_, meaning it references memory mapped by the
1191 * Mapped buffers are either anonymous, if created by ::new with the
1192 * IO::Buffer::MAPPED flag or if the size was at least IO::Buffer::PAGE_SIZE,
1193 * or backed by a file if created with ::map.
1195 * Mapped buffers can usually be resized, and such an operation will typically
1196 * invalidate all slices, but not always.
1199 rb_io_buffer_mapped_p(VALUE self
)
1201 struct rb_io_buffer
*buffer
= NULL
;
1202 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1204 return RBOOL(buffer
->flags
& RB_IO_BUFFER_MAPPED
);
1208 * call-seq: shared? -> true or false
1210 * If the buffer is _shared_, meaning it references memory that can be shared
1211 * with other processes (and thus might change without being modified
1214 * # Create a test file:
1215 * File.write('test.txt', 'test')
1217 * # Create a shared mapping from the given file, the file must be opened in
1218 * # read-write mode unless we also specify IO::Buffer::READONLY:
1219 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), nil, 0)
1220 * # => #<IO::Buffer 0x00007f1bffd5e000+4 EXTERNAL MAPPED SHARED>
1222 * # Write to the buffer, which will modify the mapped file:
1223 * buffer.set_string('b', 0)
1226 * # The file itself is modified:
1227 * File.read('test.txt')
1231 rb_io_buffer_shared_p(VALUE self
)
1233 struct rb_io_buffer
*buffer
= NULL
;
1234 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1236 return RBOOL(buffer
->flags
& RB_IO_BUFFER_SHARED
);
1240 * call-seq: locked? -> true or false
1242 * If the buffer is _locked_, meaning it is inside #locked block execution.
1243 * Locked buffer can't be resized or freed, and another lock can't be acquired
1246 * Locking is not thread safe, but is a semantic used to ensure buffers don't
1247 * move while being used by a system call.
1250 * buffer.write(io) # theoretical system call interface
1254 rb_io_buffer_locked_p(VALUE self
)
1256 struct rb_io_buffer
*buffer
= NULL
;
1257 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1259 return RBOOL(buffer
->flags
& RB_IO_BUFFER_LOCKED
);
1262 /* call-seq: private? -> true or false
1264 * If the buffer is _private_, meaning modifications to the buffer will not
1265 * be replicated to the underlying file mapping.
1267 * # Create a test file:
1268 * File.write('test.txt', 'test')
1270 * # Create a private mapping from the given file. Note that the file here
1271 * # is opened in read-only mode, but it doesn't matter due to the private
1273 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::PRIVATE)
1274 * # => #<IO::Buffer 0x00007fce63f11000+4 MAPPED PRIVATE>
1276 * # Write to the buffer (invoking CoW of the underlying file buffer):
1277 * buffer.set_string('b', 0)
1280 * # The file itself is not modified:
1281 * File.read('test.txt')
1285 rb_io_buffer_private_p(VALUE self
)
1287 struct rb_io_buffer
*buffer
= NULL
;
1288 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1290 return RBOOL(buffer
->flags
& RB_IO_BUFFER_PRIVATE
);
1294 rb_io_buffer_readonly_p(VALUE self
)
1296 struct rb_io_buffer
*buffer
= NULL
;
1297 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1299 return buffer
->flags
& RB_IO_BUFFER_READONLY
;
1303 * call-seq: readonly? -> true or false
1305 * If the buffer is <i>read only</i>, meaning the buffer cannot be modified using
1306 * #set_value, #set_string or #copy and similar.
1308 * Frozen strings and read-only files create read-only buffers.
1311 io_buffer_readonly_p(VALUE self
)
1313 return RBOOL(rb_io_buffer_readonly_p(self
));
1317 io_buffer_lock(struct rb_io_buffer
*buffer
)
1319 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1320 rb_raise(rb_eIOBufferLockedError
, "Buffer already locked!");
1323 buffer
->flags
|= RB_IO_BUFFER_LOCKED
;
1327 rb_io_buffer_lock(VALUE self
)
1329 struct rb_io_buffer
*buffer
= NULL
;
1330 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1332 io_buffer_lock(buffer
);
1338 io_buffer_unlock(struct rb_io_buffer
*buffer
)
1340 if (!(buffer
->flags
& RB_IO_BUFFER_LOCKED
)) {
1341 rb_raise(rb_eIOBufferLockedError
, "Buffer not locked!");
1344 buffer
->flags
&= ~RB_IO_BUFFER_LOCKED
;
1348 rb_io_buffer_unlock(VALUE self
)
1350 struct rb_io_buffer
*buffer
= NULL
;
1351 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1353 io_buffer_unlock(buffer
);
1359 rb_io_buffer_try_unlock(VALUE self
)
1361 struct rb_io_buffer
*buffer
= NULL
;
1362 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1364 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1365 buffer
->flags
&= ~RB_IO_BUFFER_LOCKED
;
1373 * call-seq: locked { ... }
1375 * Allows to process a buffer in exclusive way, for concurrency-safety. While
1376 * the block is performed, the buffer is considered locked, and no other code
1377 * can enter the lock. Also, locked buffer can't be changed with #resize or
1380 * The following operations acquire a lock: #resize, #free.
1382 * Locking is not thread safe. It is designed as a safety net around
1383 * non-blocking system calls. You can only share a buffer between threads with
1384 * appropriate synchronisation techniques.
1386 * buffer = IO::Buffer.new(4)
1387 * buffer.locked? #=> false
1391 * buffer.write(io) # theoretical system call interface
1396 * # in `locked': Buffer already locked! (IO::Buffer::LockedError)
1398 * buffer.set_string("test", 0)
1403 rb_io_buffer_locked(VALUE self
)
1405 struct rb_io_buffer
*buffer
= NULL
;
1406 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1408 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1409 rb_raise(rb_eIOBufferLockedError
, "Buffer already locked!");
1412 buffer
->flags
|= RB_IO_BUFFER_LOCKED
;
1414 VALUE result
= rb_yield(self
);
1416 buffer
->flags
&= ~RB_IO_BUFFER_LOCKED
;
1422 * call-seq: free -> self
1424 * If the buffer references memory, release it back to the operating system.
1425 * * for a _mapped_ buffer (e.g. from file): unmap.
1426 * * for a buffer created from scratch: free memory.
1427 * * for a buffer created from string: undo the association.
1429 * After the buffer is freed, no further operations can't be performed on it.
1431 * You can resize a freed buffer to re-allocate it.
1433 * buffer = IO::Buffer.for('test')
1435 * # => #<IO::Buffer 0x0000000000000000+0 NULL>
1437 * buffer.get_value(:U8, 0)
1438 * # in `get_value': The buffer is not allocated! (IO::Buffer::AllocationError)
1441 * # in `get_string': The buffer is not allocated! (IO::Buffer::AllocationError)
1447 rb_io_buffer_free(VALUE self
)
1449 struct rb_io_buffer
*buffer
= NULL
;
1450 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1452 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1453 rb_raise(rb_eIOBufferLockedError
, "Buffer is locked!");
1456 io_buffer_free(buffer
);
1461 VALUE
rb_io_buffer_free_locked(VALUE self
)
1463 struct rb_io_buffer
*buffer
= NULL
;
1464 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1466 io_buffer_unlock(buffer
);
1467 io_buffer_free(buffer
);
1472 // Validate that access to the buffer is within bounds, assuming you want to
1473 // access length bytes from the specified offset.
1475 io_buffer_validate_range(struct rb_io_buffer
*buffer
, size_t offset
, size_t length
)
1477 // We assume here that offset + length won't overflow:
1478 if (offset
+ length
> buffer
->size
) {
1479 rb_raise(rb_eArgError
, "Specified offset+length is bigger than the buffer size!");
1484 * call-seq: hexdump([offset, [length, [width]]]) -> string
1486 * Returns a human-readable string representation of the buffer. The exact
1487 * format is subject to change.
1489 * buffer = IO::Buffer.for("Hello World")
1490 * puts buffer.hexdump
1491 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
1493 * As buffers are usually fairly big, you may want to limit the output by
1494 * specifying the offset and length:
1496 * puts buffer.hexdump(6, 5)
1497 * # 0x00000006 57 6f 72 6c 64 World
1500 rb_io_buffer_hexdump(int argc
, VALUE
*argv
, VALUE self
)
1502 rb_check_arity(argc
, 0, 3);
1504 size_t offset
, length
;
1505 struct rb_io_buffer
*buffer
= io_buffer_extract_offset_length(self
, argc
, argv
, &offset
, &length
);
1507 size_t width
= RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH
;
1509 width
= io_buffer_extract_width(argv
[2], 1);
1512 // This may raise an exception if the offset/length is invalid:
1513 io_buffer_validate_range(buffer
, offset
, length
);
1515 VALUE result
= Qnil
;
1517 if (io_buffer_validate(buffer
) && buffer
->base
) {
1518 result
= rb_str_buf_new(io_buffer_hexdump_output_size(width
, length
, 1));
1520 io_buffer_hexdump(result
, width
, buffer
->base
, offset
+length
, offset
, 1);
1527 rb_io_buffer_slice(struct rb_io_buffer
*buffer
, VALUE self
, size_t offset
, size_t length
)
1529 io_buffer_validate_range(buffer
, offset
, length
);
1531 VALUE instance
= rb_io_buffer_type_allocate(rb_class_of(self
));
1532 struct rb_io_buffer
*slice
= NULL
;
1533 TypedData_Get_Struct(instance
, struct rb_io_buffer
, &rb_io_buffer_type
, slice
);
1535 slice
->base
= (char*)buffer
->base
+ offset
;
1536 slice
->size
= length
;
1538 // The source should be the root buffer:
1539 if (buffer
->source
!= Qnil
) {
1540 RB_OBJ_WRITE(instance
, &slice
->source
, buffer
->source
);
1543 RB_OBJ_WRITE(instance
, &slice
->source
, self
);
1550 * call-seq: slice([offset, [length]]) -> io_buffer
1552 * Produce another IO::Buffer which is a slice (or view into) the current one
1553 * starting at +offset+ bytes and going for +length+ bytes.
1555 * The slicing happens without copying of memory, and the slice keeps being
1556 * associated with the original buffer's source (string, or file), if any.
1558 * If the offset is not given, it will be zero. If the offset is negative, it
1559 * will raise an ArgumentError.
1561 * If the length is not given, the slice will be as long as the original
1562 * buffer minus the specified offset. If the length is negative, it will raise
1565 * Raises RuntimeError if the <tt>offset+length</tt> is out of the current
1569 * buffer = IO::Buffer.for(string)
1571 * slice = buffer.slice
1573 * # #<IO::Buffer 0x0000000108338e68+4 SLICE>
1574 * # 0x00000000 74 65 73 74 test
1578 * # #<IO::Buffer 0x0000000108338e6a+2 SLICE>
1579 * # 0x00000000 73 74 st
1581 * slice = buffer.slice(1, 2)
1583 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1584 * # 0x00000000 65 73 es
1586 * # Put "o" into 0s position of the slice
1587 * slice.set_string('o', 0)
1590 * # #<IO::Buffer 0x00007fc3d34ebc49+2 SLICE>
1591 * # 0x00000000 6f 73 os
1593 * # it is also visible at position 1 of the original buffer
1596 * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
1597 * # 0x00000000 74 6f 73 74 tost
1599 * # ...and original string
1604 io_buffer_slice(int argc
, VALUE
*argv
, VALUE self
)
1606 rb_check_arity(argc
, 0, 2);
1608 size_t offset
, length
;
1609 struct rb_io_buffer
*buffer
= io_buffer_extract_offset_length(self
, argc
, argv
, &offset
, &length
);
1611 return rb_io_buffer_slice(buffer
, self
, offset
, length
);
1615 * call-seq: transfer -> new_io_buffer
1617 * Transfers ownership of the underlying memory to a new buffer, causing the
1618 * current buffer to become uninitialized.
1620 * buffer = IO::Buffer.new('test')
1621 * other = buffer.transfer
1624 * # #<IO::Buffer 0x00007f136a15f7b0+4 SLICE>
1625 * # 0x00000000 74 65 73 74 test
1628 * # #<IO::Buffer 0x0000000000000000+0 NULL>
1633 rb_io_buffer_transfer(VALUE self
)
1635 struct rb_io_buffer
*buffer
= NULL
;
1636 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1638 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1639 rb_raise(rb_eIOBufferLockedError
, "Cannot transfer ownership of locked buffer!");
1642 VALUE instance
= rb_io_buffer_type_allocate(rb_class_of(self
));
1643 struct rb_io_buffer
*transferred
;
1644 TypedData_Get_Struct(instance
, struct rb_io_buffer
, &rb_io_buffer_type
, transferred
);
1646 *transferred
= *buffer
;
1647 io_buffer_zero(buffer
);
1653 io_buffer_resize_clear(struct rb_io_buffer
*buffer
, void* base
, size_t size
)
1655 if (size
> buffer
->size
) {
1656 memset((unsigned char*)base
+buffer
->size
, 0, size
- buffer
->size
);
1661 io_buffer_resize_copy(VALUE self
, struct rb_io_buffer
*buffer
, size_t size
)
1664 struct rb_io_buffer resized
;
1665 io_buffer_initialize(self
, &resized
, NULL
, size
, io_flags_for_size(size
), Qnil
);
1668 size_t preserve
= buffer
->size
;
1669 if (preserve
> size
) preserve
= size
;
1670 memcpy(resized
.base
, buffer
->base
, preserve
);
1672 io_buffer_resize_clear(buffer
, resized
.base
, size
);
1675 io_buffer_free(buffer
);
1680 rb_io_buffer_resize(VALUE self
, size_t size
)
1682 struct rb_io_buffer
*buffer
= NULL
;
1683 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
1685 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
1686 rb_raise(rb_eIOBufferLockedError
, "Cannot resize locked buffer!");
1689 if (buffer
->base
== NULL
) {
1690 io_buffer_initialize(self
, buffer
, NULL
, size
, io_flags_for_size(size
), Qnil
);
1694 if (buffer
->flags
& RB_IO_BUFFER_EXTERNAL
) {
1695 rb_raise(rb_eIOBufferAccessError
, "Cannot resize external buffer!");
1698 #if defined(HAVE_MREMAP) && defined(MREMAP_MAYMOVE)
1699 if (buffer
->flags
& RB_IO_BUFFER_MAPPED
) {
1700 void *base
= mremap(buffer
->base
, buffer
->size
, size
, MREMAP_MAYMOVE
);
1702 if (base
== MAP_FAILED
) {
1703 rb_sys_fail("rb_io_buffer_resize:mremap");
1706 io_buffer_resize_clear(buffer
, base
, size
);
1708 buffer
->base
= base
;
1709 buffer
->size
= size
;
1715 if (buffer
->flags
& RB_IO_BUFFER_INTERNAL
) {
1717 io_buffer_free(buffer
);
1721 void *base
= realloc(buffer
->base
, size
);
1724 rb_sys_fail("rb_io_buffer_resize:realloc");
1727 io_buffer_resize_clear(buffer
, base
, size
);
1729 buffer
->base
= base
;
1730 buffer
->size
= size
;
1735 io_buffer_resize_copy(self
, buffer
, size
);
1739 * call-seq: resize(new_size) -> self
1741 * Resizes a buffer to a +new_size+ bytes, preserving its content.
1742 * Depending on the old and new size, the memory area associated with
1743 * the buffer might be either extended, or rellocated at different
1744 * address with content being copied.
1746 * buffer = IO::Buffer.new(4)
1747 * buffer.set_string("test", 0)
1748 * buffer.resize(8) # resize to 8 bytes
1750 * # #<IO::Buffer 0x0000555f5d1a1630+8 INTERNAL>
1751 * # 0x00000000 74 65 73 74 00 00 00 00 test....
1753 * External buffer (created with ::for), and locked buffer
1754 * can not be resized.
1757 io_buffer_resize(VALUE self
, VALUE size
)
1759 rb_io_buffer_resize(self
, io_buffer_extract_size(size
));
1765 * call-seq: <=>(other) -> true or false
1767 * Buffers are compared by size and exact contents of the memory they are
1768 * referencing using +memcmp+.
1771 rb_io_buffer_compare(VALUE self
, VALUE other
)
1773 const void *ptr1
, *ptr2
;
1774 size_t size1
, size2
;
1776 rb_io_buffer_get_bytes_for_reading(self
, &ptr1
, &size1
);
1777 rb_io_buffer_get_bytes_for_reading(other
, &ptr2
, &size2
);
1779 if (size1
< size2
) {
1780 return RB_INT2NUM(-1);
1783 if (size1
> size2
) {
1784 return RB_INT2NUM(1);
1787 return RB_INT2NUM(memcmp(ptr1
, ptr2
, size1
));
1791 io_buffer_validate_type(size_t size
, size_t offset
)
1793 if (offset
> size
) {
1794 rb_raise(rb_eArgError
, "Type extends beyond end of buffer! (offset=%"PRIdSIZE
" > size=%"PRIdSIZE
")", offset
, size
);
1798 // Lower case: little endian.
1799 // Upper case: big endian (network endian).
1801 // :U8 | unsigned 8-bit integer.
1802 // :S8 | signed 8-bit integer.
1804 // :u16, :U16 | unsigned 16-bit integer.
1805 // :s16, :S16 | signed 16-bit integer.
1807 // :u32, :U32 | unsigned 32-bit integer.
1808 // :s32, :S32 | signed 32-bit integer.
1810 // :u64, :U64 | unsigned 64-bit integer.
1811 // :s64, :S64 | signed 64-bit integer.
1813 // :f32, :F32 | 32-bit floating point number.
1814 // :f64, :F64 | 64-bit floating point number.
1816 #define ruby_swap8(value) value
1824 ruby_swapf32(float value
)
1826 union swapf32 swap
= {.value
= value
};
1827 swap
.integral
= ruby_swap32(swap
.integral
);
1837 ruby_swapf64(double value
)
1839 union swapf64 swap
= {.value
= value
};
1840 swap
.integral
= ruby_swap64(swap
.integral
);
1844 #define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
1845 static ID RB_IO_BUFFER_DATA_TYPE_##name; \
1848 io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
1850 io_buffer_validate_type(size, *offset + sizeof(type)); \
1852 memcpy(&value, (char*)base + *offset, sizeof(type)); \
1853 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1854 *offset += sizeof(type); \
1855 return wrap(value); \
1859 io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
1861 io_buffer_validate_type(size, *offset + sizeof(type)); \
1862 type value = unwrap(_value); \
1863 if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
1864 memcpy((char*)base + *offset, &value, sizeof(type)); \
1865 *offset += sizeof(type); \
1869 RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \
1872 IO_BUFFER_DECLARE_TYPE(U8
, uint8_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_UINT2NUM
, RB_NUM2UINT
, ruby_swap8
)
1873 IO_BUFFER_DECLARE_TYPE(S8
, int8_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_INT2NUM
, RB_NUM2INT
, ruby_swap8
)
1875 IO_BUFFER_DECLARE_TYPE(u16
, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_UINT2NUM
, RB_NUM2UINT
, ruby_swap16
)
1876 IO_BUFFER_DECLARE_TYPE(U16
, uint16_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_UINT2NUM
, RB_NUM2UINT
, ruby_swap16
)
1877 IO_BUFFER_DECLARE_TYPE(s16
, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_INT2NUM
, RB_NUM2INT
, ruby_swap16
)
1878 IO_BUFFER_DECLARE_TYPE(S16
, int16_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_INT2NUM
, RB_NUM2INT
, ruby_swap16
)
1880 IO_BUFFER_DECLARE_TYPE(u32
, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_UINT2NUM
, RB_NUM2UINT
, ruby_swap32
)
1881 IO_BUFFER_DECLARE_TYPE(U32
, uint32_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_UINT2NUM
, RB_NUM2UINT
, ruby_swap32
)
1882 IO_BUFFER_DECLARE_TYPE(s32
, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_INT2NUM
, RB_NUM2INT
, ruby_swap32
)
1883 IO_BUFFER_DECLARE_TYPE(S32
, int32_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_INT2NUM
, RB_NUM2INT
, ruby_swap32
)
1885 IO_BUFFER_DECLARE_TYPE(u64
, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_ULL2NUM
, RB_NUM2ULL
, ruby_swap64
)
1886 IO_BUFFER_DECLARE_TYPE(U64
, uint64_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_ULL2NUM
, RB_NUM2ULL
, ruby_swap64
)
1887 IO_BUFFER_DECLARE_TYPE(s64
, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN
, RB_LL2NUM
, RB_NUM2LL
, ruby_swap64
)
1888 IO_BUFFER_DECLARE_TYPE(S64
, int64_t, RB_IO_BUFFER_BIG_ENDIAN
, RB_LL2NUM
, RB_NUM2LL
, ruby_swap64
)
1890 IO_BUFFER_DECLARE_TYPE(f32
, float, RB_IO_BUFFER_LITTLE_ENDIAN
, DBL2NUM
, NUM2DBL
, ruby_swapf32
)
1891 IO_BUFFER_DECLARE_TYPE(F32
, float, RB_IO_BUFFER_BIG_ENDIAN
, DBL2NUM
, NUM2DBL
, ruby_swapf32
)
1892 IO_BUFFER_DECLARE_TYPE(f64
, double, RB_IO_BUFFER_LITTLE_ENDIAN
, DBL2NUM
, NUM2DBL
, ruby_swapf64
)
1893 IO_BUFFER_DECLARE_TYPE(F64
, double, RB_IO_BUFFER_BIG_ENDIAN
, DBL2NUM
, NUM2DBL
, ruby_swapf64
)
1894 #undef IO_BUFFER_DECLARE_TYPE
1896 static inline size_t
1897 io_buffer_buffer_type_size(ID buffer_type
)
1899 #define IO_BUFFER_DATA_TYPE_SIZE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE;
1900 IO_BUFFER_DATA_TYPE_SIZE(U8
)
1901 IO_BUFFER_DATA_TYPE_SIZE(S8
)
1902 IO_BUFFER_DATA_TYPE_SIZE(u16
)
1903 IO_BUFFER_DATA_TYPE_SIZE(U16
)
1904 IO_BUFFER_DATA_TYPE_SIZE(s16
)
1905 IO_BUFFER_DATA_TYPE_SIZE(S16
)
1906 IO_BUFFER_DATA_TYPE_SIZE(u32
)
1907 IO_BUFFER_DATA_TYPE_SIZE(U32
)
1908 IO_BUFFER_DATA_TYPE_SIZE(s32
)
1909 IO_BUFFER_DATA_TYPE_SIZE(S32
)
1910 IO_BUFFER_DATA_TYPE_SIZE(u64
)
1911 IO_BUFFER_DATA_TYPE_SIZE(U64
)
1912 IO_BUFFER_DATA_TYPE_SIZE(s64
)
1913 IO_BUFFER_DATA_TYPE_SIZE(S64
)
1914 IO_BUFFER_DATA_TYPE_SIZE(f32
)
1915 IO_BUFFER_DATA_TYPE_SIZE(F32
)
1916 IO_BUFFER_DATA_TYPE_SIZE(f64
)
1917 IO_BUFFER_DATA_TYPE_SIZE(F64
)
1918 #undef IO_BUFFER_DATA_TYPE_SIZE
1920 rb_raise(rb_eArgError
, "Invalid type name!");
1925 * size_of(buffer_type) -> byte size
1926 * size_of(array of buffer_type) -> byte size
1928 * Returns the size of the given buffer type(s) in bytes.
1930 * IO::Buffer.size_of(:u32) # => 4
1931 * IO::Buffer.size_of([:u32, :u32]) # => 8
1934 io_buffer_size_of(VALUE klass
, VALUE buffer_type
)
1936 if (RB_TYPE_P(buffer_type
, T_ARRAY
)) {
1938 for (long i
= 0; i
< RARRAY_LEN(buffer_type
); i
++) {
1939 total
+= io_buffer_buffer_type_size(RB_SYM2ID(RARRAY_AREF(buffer_type
, i
)));
1941 return SIZET2NUM(total
);
1944 return SIZET2NUM(io_buffer_buffer_type_size(RB_SYM2ID(buffer_type
)));
1949 rb_io_buffer_get_value(const void* base
, size_t size
, ID buffer_type
, size_t *offset
)
1951 #define IO_BUFFER_GET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset);
1952 IO_BUFFER_GET_VALUE(U8
)
1953 IO_BUFFER_GET_VALUE(S8
)
1955 IO_BUFFER_GET_VALUE(u16
)
1956 IO_BUFFER_GET_VALUE(U16
)
1957 IO_BUFFER_GET_VALUE(s16
)
1958 IO_BUFFER_GET_VALUE(S16
)
1960 IO_BUFFER_GET_VALUE(u32
)
1961 IO_BUFFER_GET_VALUE(U32
)
1962 IO_BUFFER_GET_VALUE(s32
)
1963 IO_BUFFER_GET_VALUE(S32
)
1965 IO_BUFFER_GET_VALUE(u64
)
1966 IO_BUFFER_GET_VALUE(U64
)
1967 IO_BUFFER_GET_VALUE(s64
)
1968 IO_BUFFER_GET_VALUE(S64
)
1970 IO_BUFFER_GET_VALUE(f32
)
1971 IO_BUFFER_GET_VALUE(F32
)
1972 IO_BUFFER_GET_VALUE(f64
)
1973 IO_BUFFER_GET_VALUE(F64
)
1974 #undef IO_BUFFER_GET_VALUE
1976 rb_raise(rb_eArgError
, "Invalid type name!");
1980 * call-seq: get_value(buffer_type, offset) -> numeric
1982 * Read from buffer a value of +type+ at +offset+. +buffer_type+ should be one
1985 * * +:U8+: unsigned integer, 1 byte
1986 * * +:S8+: signed integer, 1 byte
1987 * * +:u16+: unsigned integer, 2 bytes, little-endian
1988 * * +:U16+: unsigned integer, 2 bytes, big-endian
1989 * * +:s16+: signed integer, 2 bytes, little-endian
1990 * * +:S16+: signed integer, 2 bytes, big-endian
1991 * * +:u32+: unsigned integer, 4 bytes, little-endian
1992 * * +:U32+: unsigned integer, 4 bytes, big-endian
1993 * * +:s32+: signed integer, 4 bytes, little-endian
1994 * * +:S32+: signed integer, 4 bytes, big-endian
1995 * * +:u64+: unsigned integer, 8 bytes, little-endian
1996 * * +:U64+: unsigned integer, 8 bytes, big-endian
1997 * * +:s64+: signed integer, 8 bytes, little-endian
1998 * * +:S64+: signed integer, 8 bytes, big-endian
1999 * * +:f32+: float, 4 bytes, little-endian
2000 * * +:F32+: float, 4 bytes, big-endian
2001 * * +:f64+: double, 8 bytes, little-endian
2002 * * +:F64+: double, 8 bytes, big-endian
2004 * A buffer type refers specifically to the type of binary buffer that is stored
2005 * in the buffer. For example, a +:u32+ buffer type is a 32-bit unsigned
2006 * integer in little-endian format.
2008 * string = [1.5].pack('f')
2009 * # => "\x00\x00\xC0?"
2010 * IO::Buffer.for(string).get_value(:f32, 0)
2014 io_buffer_get_value(VALUE self
, VALUE type
, VALUE _offset
)
2018 size_t offset
= io_buffer_extract_offset(_offset
);
2020 rb_io_buffer_get_bytes_for_reading(self
, &base
, &size
);
2022 return rb_io_buffer_get_value(base
, size
, RB_SYM2ID(type
), &offset
);
2026 * call-seq: get_values(buffer_types, offset) -> array
2028 * Similar to #get_value, except that it can handle multiple buffer types and
2029 * returns an array of values.
2031 * string = [1.5, 2.5].pack('ff')
2032 * IO::Buffer.for(string).get_values([:f32, :f32], 0)
2036 io_buffer_get_values(VALUE self
, VALUE buffer_types
, VALUE _offset
)
2038 size_t offset
= io_buffer_extract_offset(_offset
);
2042 rb_io_buffer_get_bytes_for_reading(self
, &base
, &size
);
2044 if (!RB_TYPE_P(buffer_types
, T_ARRAY
)) {
2045 rb_raise(rb_eArgError
, "Argument buffer_types should be an array!");
2048 VALUE array
= rb_ary_new_capa(RARRAY_LEN(buffer_types
));
2050 for (long i
= 0; i
< RARRAY_LEN(buffer_types
); i
++) {
2051 VALUE type
= rb_ary_entry(buffer_types
, i
);
2052 VALUE value
= rb_io_buffer_get_value(base
, size
, RB_SYM2ID(type
), &offset
);
2053 rb_ary_push(array
, value
);
2059 // Extract a count argument, which must be a positive integer.
2060 // Count is generally considered relative to the number of things.
2061 static inline size_t
2062 io_buffer_extract_count(VALUE argument
)
2064 if (rb_int_negative_p(argument
)) {
2065 rb_raise(rb_eArgError
, "Count can't be negative!");
2068 return NUM2SIZET(argument
);
2072 io_buffer_extract_offset_count(ID buffer_type
, size_t size
, int argc
, VALUE
*argv
, size_t *offset
, size_t *count
)
2075 *offset
= io_buffer_extract_offset(argv
[0]);
2082 *count
= io_buffer_extract_count(argv
[1]);
2085 if (*offset
> size
) {
2086 rb_raise(rb_eArgError
, "The given offset is bigger than the buffer size!");
2089 *count
= (size
- *offset
) / io_buffer_buffer_type_size(buffer_type
);
2095 * each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
2096 * each(buffer_type, [offset, [count]]) -> enumerator
2098 * Iterates over the buffer, yielding each +value+ of +buffer_type+ starting
2101 * If +count+ is given, only +count+ values will be yielded.
2103 * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value|
2104 * puts "#{offset}: #{value}"
2110 io_buffer_each(int argc
, VALUE
*argv
, VALUE self
)
2112 RETURN_ENUMERATOR_KW(self
, argc
, argv
, RB_NO_KEYWORDS
);
2117 rb_io_buffer_get_bytes_for_reading(self
, &base
, &size
);
2121 buffer_type
= RB_SYM2ID(argv
[0]);
2124 buffer_type
= RB_IO_BUFFER_DATA_TYPE_U8
;
2127 size_t offset
, count
;
2128 io_buffer_extract_offset_count(buffer_type
, size
, argc
-1, argv
+1, &offset
, &count
);
2130 for (size_t i
= 0; i
< count
; i
++) {
2131 size_t current_offset
= offset
;
2132 VALUE value
= rb_io_buffer_get_value(base
, size
, buffer_type
, &offset
);
2133 rb_yield_values(2, SIZET2NUM(current_offset
), value
);
2140 * call-seq: values(buffer_type, [offset, [count]]) -> array
2142 * Returns an array of values of +buffer_type+ starting from +offset+.
2144 * If +count+ is given, only +count+ values will be returned.
2146 * IO::Buffer.for("Hello World").values(:U8, 2, 2)
2150 io_buffer_values(int argc
, VALUE
*argv
, VALUE self
)
2155 rb_io_buffer_get_bytes_for_reading(self
, &base
, &size
);
2159 buffer_type
= RB_SYM2ID(argv
[0]);
2162 buffer_type
= RB_IO_BUFFER_DATA_TYPE_U8
;
2165 size_t offset
, count
;
2166 io_buffer_extract_offset_count(buffer_type
, size
, argc
-1, argv
+1, &offset
, &count
);
2168 VALUE array
= rb_ary_new_capa(count
);
2170 for (size_t i
= 0; i
< count
; i
++) {
2171 VALUE value
= rb_io_buffer_get_value(base
, size
, buffer_type
, &offset
);
2172 rb_ary_push(array
, value
);
2180 * each_byte([offset, [count]]) {|offset, byte| ...} -> self
2181 * each_byte([offset, [count]]) -> enumerator
2183 * Iterates over the buffer, yielding each byte starting from +offset+.
2185 * If +count+ is given, only +count+ bytes will be yielded.
2187 * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte|
2188 * puts "#{offset}: #{byte}"
2194 io_buffer_each_byte(int argc
, VALUE
*argv
, VALUE self
)
2196 RETURN_ENUMERATOR_KW(self
, argc
, argv
, RB_NO_KEYWORDS
);
2201 rb_io_buffer_get_bytes_for_reading(self
, &base
, &size
);
2203 size_t offset
, count
;
2204 io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8
, size
, argc
-1, argv
+1, &offset
, &count
);
2206 for (size_t i
= 0; i
< count
; i
++) {
2207 unsigned char *value
= (unsigned char *)base
+ i
+ offset
;
2208 rb_yield(RB_INT2FIX(*value
));
2215 rb_io_buffer_set_value(const void* base
, size_t size
, ID buffer_type
, size_t *offset
, VALUE value
)
2217 #define IO_BUFFER_SET_VALUE(name) if (buffer_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;}
2218 IO_BUFFER_SET_VALUE(U8
);
2219 IO_BUFFER_SET_VALUE(S8
);
2221 IO_BUFFER_SET_VALUE(u16
);
2222 IO_BUFFER_SET_VALUE(U16
);
2223 IO_BUFFER_SET_VALUE(s16
);
2224 IO_BUFFER_SET_VALUE(S16
);
2226 IO_BUFFER_SET_VALUE(u32
);
2227 IO_BUFFER_SET_VALUE(U32
);
2228 IO_BUFFER_SET_VALUE(s32
);
2229 IO_BUFFER_SET_VALUE(S32
);
2231 IO_BUFFER_SET_VALUE(u64
);
2232 IO_BUFFER_SET_VALUE(U64
);
2233 IO_BUFFER_SET_VALUE(s64
);
2234 IO_BUFFER_SET_VALUE(S64
);
2236 IO_BUFFER_SET_VALUE(f32
);
2237 IO_BUFFER_SET_VALUE(F32
);
2238 IO_BUFFER_SET_VALUE(f64
);
2239 IO_BUFFER_SET_VALUE(F64
);
2240 #undef IO_BUFFER_SET_VALUE
2242 rb_raise(rb_eArgError
, "Invalid type name!");
2246 * call-seq: set_value(type, offset, value) -> offset
2248 * Write to a buffer a +value+ of +type+ at +offset+. +type+ should be one of
2249 * symbols described in #get_value.
2251 * buffer = IO::Buffer.new(8)
2253 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2254 * # 0x00000000 00 00 00 00 00 00 00 00
2256 * buffer.set_value(:U8, 1, 111)
2261 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2262 * # 0x00000000 00 6f 00 00 00 00 00 00 .o......
2264 * Note that if the +type+ is integer and +value+ is Float, the implicit truncation is performed:
2266 * buffer = IO::Buffer.new(8)
2267 * buffer.set_value(:U32, 0, 2.5)
2271 * # #<IO::Buffer 0x0000555f5c9a2d50+8 INTERNAL>
2272 * # 0x00000000 00 00 00 02 00 00 00 00
2273 * # ^^ the same as if we'd pass just integer 2
2276 io_buffer_set_value(VALUE self
, VALUE type
, VALUE _offset
, VALUE value
)
2280 size_t offset
= io_buffer_extract_offset(_offset
);
2282 rb_io_buffer_get_bytes_for_writing(self
, &base
, &size
);
2284 rb_io_buffer_set_value(base
, size
, RB_SYM2ID(type
), &offset
, value
);
2286 return SIZET2NUM(offset
);
2290 * call-seq: set_values(buffer_types, offset, values) -> offset
2292 * Write +values+ of +buffer_types+ at +offset+ to the buffer. +buffer_types+
2293 * should be an array of symbols as described in #get_value. +values+ should
2294 * be an array of values to write.
2296 * buffer = IO::Buffer.new(8)
2297 * buffer.set_values([:U8, :U16], 0, [1, 2])
2300 * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
2301 * # 0x00000000 01 00 02 00 00 00 00 00 ........
2304 io_buffer_set_values(VALUE self
, VALUE buffer_types
, VALUE _offset
, VALUE values
)
2306 if (!RB_TYPE_P(buffer_types
, T_ARRAY
)) {
2307 rb_raise(rb_eArgError
, "Argument buffer_types should be an array!");
2310 if (!RB_TYPE_P(values
, T_ARRAY
)) {
2311 rb_raise(rb_eArgError
, "Argument values should be an array!");
2314 if (RARRAY_LEN(buffer_types
) != RARRAY_LEN(values
)) {
2315 rb_raise(rb_eArgError
, "Argument buffer_types and values should have the same length!");
2318 size_t offset
= io_buffer_extract_offset(_offset
);
2322 rb_io_buffer_get_bytes_for_writing(self
, &base
, &size
);
2324 for (long i
= 0; i
< RARRAY_LEN(buffer_types
); i
++) {
2325 VALUE type
= rb_ary_entry(buffer_types
, i
);
2326 VALUE value
= rb_ary_entry(values
, i
);
2327 rb_io_buffer_set_value(base
, size
, RB_SYM2ID(type
), &offset
, value
);
2330 return SIZET2NUM(offset
);
2334 io_buffer_memcpy(struct rb_io_buffer
*buffer
, size_t offset
, const void *source_base
, size_t source_offset
, size_t source_size
, size_t length
)
2338 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
2340 io_buffer_validate_range(buffer
, offset
, length
);
2342 if (source_offset
+ length
> source_size
) {
2343 rb_raise(rb_eArgError
, "The computed source range exceeds the size of the source buffer!");
2346 memcpy((unsigned char*)base
+offset
, (unsigned char*)source_base
+source_offset
, length
);
2349 // (offset, length, source_offset) -> length
2351 io_buffer_copy_from(struct rb_io_buffer
*buffer
, const void *source_base
, size_t source_size
, int argc
, VALUE
*argv
)
2355 size_t source_offset
;
2357 // The offset we copy into the buffer:
2359 offset
= io_buffer_extract_offset(argv
[0]);
2362 // The offset we start from within the string:
2364 source_offset
= io_buffer_extract_offset(argv
[2]);
2366 if (source_offset
> source_size
) {
2367 rb_raise(rb_eArgError
, "The given source offset is bigger than the source itself!");
2374 // The length we are going to copy:
2375 if (argc
>= 2 && !RB_NIL_P(argv
[1])) {
2376 length
= io_buffer_extract_length(argv
[1]);
2379 // Default to the source offset -> source size:
2380 length
= source_size
- source_offset
;
2383 io_buffer_memcpy(buffer
, offset
, source_base
, source_offset
, source_size
, length
);
2385 return SIZET2NUM(length
);
2391 * clone -> io_buffer
2393 * Make an internal copy of the source buffer. Updates to the copy will not
2394 * affect the source buffer.
2396 * source = IO::Buffer.for("Hello World")
2398 * # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
2399 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2400 * buffer = source.dup
2402 * # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
2403 * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
2406 rb_io_buffer_initialize_copy(VALUE self
, VALUE source
)
2408 struct rb_io_buffer
*buffer
= NULL
;
2409 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2411 const void *source_base
;
2414 rb_io_buffer_get_bytes_for_reading(source
, &source_base
, &source_size
);
2416 io_buffer_initialize(self
, buffer
, NULL
, source_size
, io_flags_for_size(source_size
), Qnil
);
2418 return io_buffer_copy_from(buffer
, source_base
, source_size
, 0, NULL
);
2423 * copy(source, [offset, [length, [source_offset]]]) -> size
2425 * Efficiently copy from a source IO::Buffer into the buffer, at +offset+
2426 * using +memcpy+. For copying String instances, see #set_string.
2428 * buffer = IO::Buffer.new(32)
2430 * # #<IO::Buffer 0x0000555f5ca22520+32 INTERNAL>
2431 * # 0x00000000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
2432 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2434 * buffer.copy(IO::Buffer.for("test"), 8)
2435 * # => 4 -- size of buffer copied
2438 * # #<IO::Buffer 0x0000555f5cf8fe40+32 INTERNAL>
2439 * # 0x00000000 00 00 00 00 00 00 00 00 74 65 73 74 00 00 00 00 ........test....
2440 * # 0x00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ *
2442 * #copy can be used to put buffer into strings associated with buffer:
2446 * buffer = IO::Buffer.for(string)
2447 * buffer.copy(IO::Buffer.for("test"), 5)
2452 * Attempt to copy into a read-only buffer will fail:
2454 * File.write('test.txt', 'test')
2455 * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
2456 * buffer.copy(IO::Buffer.for("test"), 8)
2457 * # in `copy': Buffer is not writable! (IO::Buffer::AccessError)
2459 * See ::map for details of creation of mutable file mappings, this will
2462 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'))
2463 * buffer.copy(IO::Buffer.for("boom"), 0)
2465 * File.read('test.txt')
2468 * Attempt to copy the buffer which will need place outside of buffer's
2471 * buffer = IO::Buffer.new(2)
2472 * buffer.copy(IO::Buffer.for('test'), 0)
2473 * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
2476 io_buffer_copy(int argc
, VALUE
*argv
, VALUE self
)
2478 rb_check_arity(argc
, 1, 4);
2480 struct rb_io_buffer
*buffer
= NULL
;
2481 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2483 VALUE source
= argv
[0];
2484 const void *source_base
;
2487 rb_io_buffer_get_bytes_for_reading(source
, &source_base
, &source_size
);
2489 return io_buffer_copy_from(buffer
, source_base
, source_size
, argc
-1, argv
+1);
2493 * call-seq: get_string([offset, [length, [encoding]]]) -> string
2495 * Read a chunk or all of the buffer into a string, in the specified
2496 * +encoding+. If no encoding is provided +Encoding::BINARY+ is used.
2498 * buffer = IO::Buffer.for('test')
2501 * buffer.get_string(2)
2503 * buffer.get_string(2, 1)
2507 io_buffer_get_string(int argc
, VALUE
*argv
, VALUE self
)
2509 rb_check_arity(argc
, 0, 3);
2511 size_t offset
, length
;
2512 struct rb_io_buffer
*buffer
= io_buffer_extract_offset_length(self
, argc
, argv
, &offset
, &length
);
2516 io_buffer_get_bytes_for_reading(buffer
, &base
, &size
);
2518 rb_encoding
*encoding
;
2520 encoding
= rb_find_encoding(argv
[2]);
2523 encoding
= rb_ascii8bit_encoding();
2526 io_buffer_validate_range(buffer
, offset
, length
);
2528 return rb_enc_str_new((const char*)base
+ offset
, length
, encoding
);
2532 * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
2534 * Efficiently copy from a source String into the buffer, at +offset+ using
2537 * buf = IO::Buffer.new(8)
2539 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2540 * # 0x00000000 00 00 00 00 00 00 00 00 ........
2542 * # set buffer starting from offset 1, take 2 bytes starting from string's
2544 * buf.set_string('test', 1, 2, 1)
2548 * # #<IO::Buffer 0x0000557412714a20+8 INTERNAL>
2549 * # 0x00000000 00 65 73 00 00 00 00 00 .es.....
2551 * See also #copy for examples of how buffer writing might be used for changing
2552 * associated strings and files.
2555 io_buffer_set_string(int argc
, VALUE
*argv
, VALUE self
)
2557 rb_check_arity(argc
, 1, 4);
2559 struct rb_io_buffer
*buffer
= NULL
;
2560 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2562 VALUE string
= rb_str_to_str(argv
[0]);
2564 const void *source_base
= RSTRING_PTR(string
);
2565 size_t source_size
= RSTRING_LEN(string
);
2567 return io_buffer_copy_from(buffer
, source_base
, source_size
, argc
-1, argv
+1);
2571 rb_io_buffer_clear(VALUE self
, uint8_t value
, size_t offset
, size_t length
)
2573 struct rb_io_buffer
*buffer
= NULL
;
2574 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2578 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
2580 io_buffer_validate_range(buffer
, offset
, length
);
2582 memset((char*)base
+ offset
, value
, length
);
2586 * call-seq: clear(value = 0, [offset, [length]]) -> self
2588 * Fill buffer with +value+, starting with +offset+ and going for +length+
2591 * buffer = IO::Buffer.for('test')
2593 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2594 * # 0x00000000 74 65 73 74 test
2598 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2599 * # 0x00000000 00 00 00 00 ....
2601 * buf.clear(1) # fill with 1
2603 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2604 * # 0x00000000 01 01 01 01 ....
2606 * buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
2608 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2609 * # 0x00000000 01 02 02 01 ....
2611 * buffer.clear(2, 1) # fill with 2, starting from offset 1
2613 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2614 * # 0x00000000 01 02 02 02 ....
2617 io_buffer_clear(int argc
, VALUE
*argv
, VALUE self
)
2619 rb_check_arity(argc
, 0, 3);
2623 value
= NUM2UINT(argv
[0]);
2626 size_t offset
, length
;
2627 io_buffer_extract_offset_length(self
, argc
-1, argv
+1, &offset
, &length
);
2629 rb_io_buffer_clear(self
, value
, offset
, length
);
2635 io_buffer_default_size(size_t page_size
)
2637 // Platform agnostic default size, based on empirical performance observation:
2638 const size_t platform_agnostic_default_size
= 64*1024;
2640 // Allow user to specify custom default buffer size:
2641 const char *default_size
= getenv("RUBY_IO_BUFFER_DEFAULT_SIZE");
2643 // For the purpose of setting a default size, 2^31 is an acceptable maximum:
2644 int value
= atoi(default_size
);
2646 // assuming sizeof(int) <= sizeof(size_t)
2652 if (platform_agnostic_default_size
< page_size
) {
2656 return platform_agnostic_default_size
;
2659 struct io_buffer_blocking_region_argument
{
2660 struct rb_io_buffer
*buffer
;
2661 rb_blocking_function_t
*function
;
2667 io_buffer_blocking_region_begin(VALUE _argument
)
2669 struct io_buffer_blocking_region_argument
*argument
= (void*)_argument
;
2671 return rb_thread_io_blocking_region(argument
->function
, argument
->data
, argument
->descriptor
);
2675 io_buffer_blocking_region_ensure(VALUE _argument
)
2677 struct io_buffer_blocking_region_argument
*argument
= (void*)_argument
;
2679 io_buffer_unlock(argument
->buffer
);
2685 io_buffer_blocking_region(struct rb_io_buffer
*buffer
, rb_blocking_function_t
*function
, void *data
, int descriptor
)
2687 struct io_buffer_blocking_region_argument argument
= {
2689 .function
= function
,
2691 .descriptor
= descriptor
,
2694 // If the buffer is already locked, we can skip the ensure (unlock):
2695 if (buffer
->flags
& RB_IO_BUFFER_LOCKED
) {
2696 return io_buffer_blocking_region_begin((VALUE
)&argument
);
2699 // The buffer should be locked for the duration of the blocking region:
2700 io_buffer_lock(buffer
);
2702 return rb_ensure(io_buffer_blocking_region_begin
, (VALUE
)&argument
, io_buffer_blocking_region_ensure
, (VALUE
)&argument
);
2706 struct io_buffer_read_internal_argument
{
2707 // The file descriptor to read from:
2709 // The base pointer to read from:
2711 // The size of the buffer:
2713 // The minimum number of bytes to read:
2718 io_buffer_read_internal(void *_argument
)
2721 struct io_buffer_read_internal_argument
*argument
= _argument
;
2724 ssize_t result
= read(argument
->descriptor
, argument
->base
, argument
->size
);
2727 return rb_fiber_scheduler_io_result(result
, errno
);
2729 else if (result
== 0) {
2730 return rb_fiber_scheduler_io_result(total
, 0);
2735 if (total
>= argument
->length
) {
2736 return rb_fiber_scheduler_io_result(total
, 0);
2739 argument
->base
= argument
->base
+ result
;
2740 argument
->size
= argument
->size
- result
;
2746 rb_io_buffer_read(VALUE self
, VALUE io
, size_t length
, size_t offset
)
2748 VALUE scheduler
= rb_fiber_scheduler_current();
2749 if (scheduler
!= Qnil
) {
2750 VALUE result
= rb_fiber_scheduler_io_read(scheduler
, io
, self
, length
, offset
);
2752 if (!UNDEF_P(result
)) {
2757 struct rb_io_buffer
*buffer
= NULL
;
2758 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2760 io_buffer_validate_range(buffer
, offset
, length
);
2762 int descriptor
= rb_io_descriptor(io
);
2766 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
2768 base
= (unsigned char*)base
+ offset
;
2769 size
= size
- offset
;
2771 struct io_buffer_read_internal_argument argument
= {
2772 .descriptor
= descriptor
,
2778 return io_buffer_blocking_region(buffer
, io_buffer_read_internal
, &argument
, descriptor
);
2782 * call-seq: read(io, [length, [offset]]) -> read length or -errno
2784 * Read at least +length+ bytes from the +io+, into the buffer starting at
2785 * +offset+. If an error occurs, return <tt>-errno</tt>.
2787 * If +length+ is not given or +nil+, it defaults to the size of the buffer
2788 * minus the offset, i.e. the entire buffer.
2790 * If +length+ is zero, exactly one <tt>read</tt> operation will occur.
2792 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
2795 * IO::Buffer.for('test') do |buffer|
2798 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2799 * # 0x00000000 74 65 73 74 test
2800 * buffer.read(File.open('/dev/urandom', 'rb'), 2)
2803 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
2804 * # 0x00000000 05 35 73 74 .5st
2808 io_buffer_read(int argc
, VALUE
*argv
, VALUE self
)
2810 rb_check_arity(argc
, 1, 3);
2814 size_t length
, offset
;
2815 io_buffer_extract_length_offset(self
, argc
-1, argv
+1, &length
, &offset
);
2817 return rb_io_buffer_read(self
, io
, length
, offset
);
2820 struct io_buffer_pread_internal_argument
{
2821 // The file descriptor to read from:
2823 // The base pointer to read from:
2825 // The size of the buffer:
2827 // The minimum number of bytes to read:
2829 // The offset to read from:
2834 io_buffer_pread_internal(void *_argument
)
2837 struct io_buffer_pread_internal_argument
*argument
= _argument
;
2840 ssize_t result
= pread(argument
->descriptor
, argument
->base
, argument
->size
, argument
->offset
);
2843 return rb_fiber_scheduler_io_result(result
, errno
);
2845 else if (result
== 0) {
2846 return rb_fiber_scheduler_io_result(total
, 0);
2851 if (total
>= argument
->length
) {
2852 return rb_fiber_scheduler_io_result(total
, 0);
2855 argument
->base
= argument
->base
+ result
;
2856 argument
->size
= argument
->size
- result
;
2857 argument
->offset
= argument
->offset
+ result
;
2863 rb_io_buffer_pread(VALUE self
, VALUE io
, rb_off_t from
, size_t length
, size_t offset
)
2865 VALUE scheduler
= rb_fiber_scheduler_current();
2866 if (scheduler
!= Qnil
) {
2867 VALUE result
= rb_fiber_scheduler_io_pread(scheduler
, io
, from
, self
, length
, offset
);
2869 if (!UNDEF_P(result
)) {
2874 struct rb_io_buffer
*buffer
= NULL
;
2875 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2877 io_buffer_validate_range(buffer
, offset
, length
);
2879 int descriptor
= rb_io_descriptor(io
);
2883 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
2885 base
= (unsigned char*)base
+ offset
;
2886 size
= size
- offset
;
2888 struct io_buffer_pread_internal_argument argument
= {
2889 .descriptor
= descriptor
,
2896 return io_buffer_blocking_region(buffer
, io_buffer_pread_internal
, &argument
, descriptor
);
2900 * call-seq: pread(io, from, [length, [offset]]) -> read length or -errno
2902 * Read at least +length+ bytes from the +io+ starting at the specified +from+
2903 * position, into the buffer starting at +offset+. If an error occurs,
2904 * return <tt>-errno</tt>.
2906 * If +length+ is not given or +nil+, it defaults to the size of the buffer
2907 * minus the offset, i.e. the entire buffer.
2909 * If +length+ is zero, exactly one <tt>pread</tt> operation will occur.
2911 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
2914 * IO::Buffer.for('test') do |buffer|
2917 * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
2918 * # 0x00000000 74 65 73 74 test
2920 * # take 2 bytes from the beginning of urandom,
2921 * # put them in buffer starting from position 2
2922 * buffer.pread(File.open('/dev/urandom', 'rb'), 0, 2, 2)
2925 * # <IO::Buffer 0x00007f3bc65f2a58+4 EXTERNAL SLICE>
2926 * # 0x00000000 05 35 73 74 te.5
2930 io_buffer_pread(int argc
, VALUE
*argv
, VALUE self
)
2932 rb_check_arity(argc
, 2, 4);
2935 rb_off_t from
= NUM2OFFT(argv
[1]);
2937 size_t length
, offset
;
2938 io_buffer_extract_length_offset(self
, argc
-2, argv
+2, &length
, &offset
);
2940 return rb_io_buffer_pread(self
, io
, from
, length
, offset
);
2943 struct io_buffer_write_internal_argument
{
2944 // The file descriptor to write to:
2946 // The base pointer to write from:
2948 // The size of the buffer:
2950 // The minimum length to write:
2955 io_buffer_write_internal(void *_argument
)
2958 struct io_buffer_write_internal_argument
*argument
= _argument
;
2961 ssize_t result
= write(argument
->descriptor
, argument
->base
, argument
->size
);
2964 return rb_fiber_scheduler_io_result(result
, errno
);
2966 else if (result
== 0) {
2967 return rb_fiber_scheduler_io_result(total
, 0);
2972 if (total
>= argument
->length
) {
2973 return rb_fiber_scheduler_io_result(total
, 0);
2976 argument
->base
= argument
->base
+ result
;
2977 argument
->size
= argument
->size
- result
;
2983 rb_io_buffer_write(VALUE self
, VALUE io
, size_t length
, size_t offset
)
2985 VALUE scheduler
= rb_fiber_scheduler_current();
2986 if (scheduler
!= Qnil
) {
2987 VALUE result
= rb_fiber_scheduler_io_write(scheduler
, io
, self
, length
, offset
);
2989 if (!UNDEF_P(result
)) {
2994 struct rb_io_buffer
*buffer
= NULL
;
2995 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
2997 io_buffer_validate_range(buffer
, offset
, length
);
2999 int descriptor
= rb_io_descriptor(io
);
3003 io_buffer_get_bytes_for_reading(buffer
, &base
, &size
);
3005 base
= (unsigned char*)base
+ offset
;
3006 size
= size
- offset
;
3008 struct io_buffer_write_internal_argument argument
= {
3009 .descriptor
= descriptor
,
3015 return io_buffer_blocking_region(buffer
, io_buffer_write_internal
, &argument
, descriptor
);
3019 * call-seq: write(io, [length, [offset]]) -> written length or -errno
3021 * Write at least +length+ bytes from the buffer starting at +offset+, into the +io+.
3022 * If an error occurs, return <tt>-errno</tt>.
3024 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3025 * minus the offset, i.e. the entire buffer.
3027 * If +length+ is zero, exactly one <tt>write</tt> operation will occur.
3029 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3032 * out = File.open('output.txt', 'wb')
3033 * IO::Buffer.for('1234567').write(out, 3)
3035 * This leads to +123+ being written into <tt>output.txt</tt>
3038 io_buffer_write(int argc
, VALUE
*argv
, VALUE self
)
3040 rb_check_arity(argc
, 1, 3);
3044 size_t length
, offset
;
3045 io_buffer_extract_length_offset(self
, argc
-1, argv
+1, &length
, &offset
);
3047 return rb_io_buffer_write(self
, io
, length
, offset
);
3049 struct io_buffer_pwrite_internal_argument
{
3050 // The file descriptor to write to:
3052 // The base pointer to write from:
3054 // The size of the buffer:
3056 // The minimum length to write:
3058 // The offset to write to:
3063 io_buffer_pwrite_internal(void *_argument
)
3066 struct io_buffer_pwrite_internal_argument
*argument
= _argument
;
3069 ssize_t result
= pwrite(argument
->descriptor
, argument
->base
, argument
->size
, argument
->offset
);
3072 return rb_fiber_scheduler_io_result(result
, errno
);
3074 else if (result
== 0) {
3075 return rb_fiber_scheduler_io_result(total
, 0);
3080 if (total
>= argument
->length
) {
3081 return rb_fiber_scheduler_io_result(total
, 0);
3084 argument
->base
= argument
->base
+ result
;
3085 argument
->size
= argument
->size
- result
;
3086 argument
->offset
= argument
->offset
+ result
;
3092 rb_io_buffer_pwrite(VALUE self
, VALUE io
, rb_off_t from
, size_t length
, size_t offset
)
3094 VALUE scheduler
= rb_fiber_scheduler_current();
3095 if (scheduler
!= Qnil
) {
3096 VALUE result
= rb_fiber_scheduler_io_pwrite(scheduler
, io
, from
, self
, length
, offset
);
3098 if (!UNDEF_P(result
)) {
3103 struct rb_io_buffer
*buffer
= NULL
;
3104 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3106 io_buffer_validate_range(buffer
, offset
, length
);
3108 int descriptor
= rb_io_descriptor(io
);
3112 io_buffer_get_bytes_for_reading(buffer
, &base
, &size
);
3114 base
= (unsigned char*)base
+ offset
;
3115 size
= size
- offset
;
3117 struct io_buffer_pwrite_internal_argument argument
= {
3118 .descriptor
= descriptor
,
3120 // Move the base pointer to the offset:
3123 // And the size to the length of buffer we want to read:
3126 // And the length of the buffer we want to write:
3129 // And the offset in the file we want to write from:
3133 return io_buffer_blocking_region(buffer
, io_buffer_pwrite_internal
, &argument
, descriptor
);
3137 * call-seq: pwrite(io, from, [length, [offset]]) -> written length or -errno
3139 * Write at least +length+ bytes from the buffer starting at +offset+, into
3140 * the +io+ starting at the specified +from+ position. If an error occurs,
3141 * return <tt>-errno</tt>.
3143 * If +length+ is not given or +nil+, it defaults to the size of the buffer
3144 * minus the offset, i.e. the entire buffer.
3146 * If +length+ is zero, exactly one <tt>pwrite</tt> operation will occur.
3148 * If +offset+ is not given, it defaults to zero, i.e. the beginning of the
3151 * If the +from+ position is beyond the end of the file, the gap will be
3152 * filled with null (0 value) bytes.
3154 * out = File.open('output.txt', File::RDWR) # open for read/write, no truncation
3155 * IO::Buffer.for('1234567').pwrite(out, 2, 3, 1)
3157 * This leads to +234+ (3 bytes, starting from position 1) being written into
3158 * <tt>output.txt</tt>, starting from file position 2.
3161 io_buffer_pwrite(int argc
, VALUE
*argv
, VALUE self
)
3163 rb_check_arity(argc
, 2, 4);
3166 rb_off_t from
= NUM2OFFT(argv
[1]);
3168 size_t length
, offset
;
3169 io_buffer_extract_length_offset(self
, argc
-2, argv
+2, &length
, &offset
);
3171 return rb_io_buffer_pwrite(self
, io
, from
, length
, offset
);
3175 io_buffer_check_mask(const struct rb_io_buffer
*buffer
)
3177 if (buffer
->size
== 0)
3178 rb_raise(rb_eIOBufferMaskError
, "Zero-length mask given!");
3182 memory_and(unsigned char * restrict output
, unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3184 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3185 output
[offset
] = base
[offset
] & mask
[offset
% mask_size
];
3191 * source & mask -> io_buffer
3193 * Generate a new buffer the same size as the source by applying the binary AND
3194 * operation to the source, using the mask, repeating as necessary.
3196 * IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
3198 * # #<IO::Buffer 0x00005589b2758480+4 INTERNAL>
3199 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3202 io_buffer_and(VALUE self
, VALUE mask
)
3204 struct rb_io_buffer
*buffer
= NULL
;
3205 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3207 struct rb_io_buffer
*mask_buffer
= NULL
;
3208 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3210 io_buffer_check_mask(mask_buffer
);
3212 VALUE output
= rb_io_buffer_new(NULL
, buffer
->size
, io_flags_for_size(buffer
->size
));
3213 struct rb_io_buffer
*output_buffer
= NULL
;
3214 TypedData_Get_Struct(output
, struct rb_io_buffer
, &rb_io_buffer_type
, output_buffer
);
3216 memory_and(output_buffer
->base
, buffer
->base
, buffer
->size
, mask_buffer
->base
, mask_buffer
->size
);
3222 memory_or(unsigned char * restrict output
, unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3224 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3225 output
[offset
] = base
[offset
] | mask
[offset
% mask_size
];
3231 * source | mask -> io_buffer
3233 * Generate a new buffer the same size as the source by applying the binary OR
3234 * operation to the source, using the mask, repeating as necessary.
3236 * IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
3238 * # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
3239 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3242 io_buffer_or(VALUE self
, VALUE mask
)
3244 struct rb_io_buffer
*buffer
= NULL
;
3245 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3247 struct rb_io_buffer
*mask_buffer
= NULL
;
3248 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3250 io_buffer_check_mask(mask_buffer
);
3252 VALUE output
= rb_io_buffer_new(NULL
, buffer
->size
, io_flags_for_size(buffer
->size
));
3253 struct rb_io_buffer
*output_buffer
= NULL
;
3254 TypedData_Get_Struct(output
, struct rb_io_buffer
, &rb_io_buffer_type
, output_buffer
);
3256 memory_or(output_buffer
->base
, buffer
->base
, buffer
->size
, mask_buffer
->base
, mask_buffer
->size
);
3262 memory_xor(unsigned char * restrict output
, unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3264 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3265 output
[offset
] = base
[offset
] ^ mask
[offset
% mask_size
];
3271 * source ^ mask -> io_buffer
3273 * Generate a new buffer the same size as the source by applying the binary XOR
3274 * operation to the source, using the mask, repeating as necessary.
3276 * IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
3278 * # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
3279 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3282 io_buffer_xor(VALUE self
, VALUE mask
)
3284 struct rb_io_buffer
*buffer
= NULL
;
3285 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3287 struct rb_io_buffer
*mask_buffer
= NULL
;
3288 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3290 io_buffer_check_mask(mask_buffer
);
3292 VALUE output
= rb_io_buffer_new(NULL
, buffer
->size
, io_flags_for_size(buffer
->size
));
3293 struct rb_io_buffer
*output_buffer
= NULL
;
3294 TypedData_Get_Struct(output
, struct rb_io_buffer
, &rb_io_buffer_type
, output_buffer
);
3296 memory_xor(output_buffer
->base
, buffer
->base
, buffer
->size
, mask_buffer
->base
, mask_buffer
->size
);
3302 memory_not(unsigned char * restrict output
, unsigned char * restrict base
, size_t size
)
3304 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3305 output
[offset
] = ~base
[offset
];
3311 * ~source -> io_buffer
3313 * Generate a new buffer the same size as the source by applying the binary NOT
3314 * operation to the source.
3316 * ~IO::Buffer.for("1234567890")
3318 * # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
3319 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3322 io_buffer_not(VALUE self
)
3324 struct rb_io_buffer
*buffer
= NULL
;
3325 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3327 VALUE output
= rb_io_buffer_new(NULL
, buffer
->size
, io_flags_for_size(buffer
->size
));
3328 struct rb_io_buffer
*output_buffer
= NULL
;
3329 TypedData_Get_Struct(output
, struct rb_io_buffer
, &rb_io_buffer_type
, output_buffer
);
3331 memory_not(output_buffer
->base
, buffer
->base
, buffer
->size
);
3337 io_buffer_overlaps(const struct rb_io_buffer
*a
, const struct rb_io_buffer
*b
)
3339 if (a
->base
> b
->base
) {
3340 return io_buffer_overlaps(b
, a
);
3343 return (b
->base
>= a
->base
) && (b
->base
<= (void*)((unsigned char *)a
->base
+ a
->size
));
3347 io_buffer_check_overlaps(struct rb_io_buffer
*a
, struct rb_io_buffer
*b
)
3349 if (io_buffer_overlaps(a
, b
))
3350 rb_raise(rb_eIOBufferMaskError
, "Mask overlaps source buffer!");
3354 memory_and_inplace(unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3356 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3357 base
[offset
] &= mask
[offset
% mask_size
];
3363 * source.and!(mask) -> io_buffer
3365 * Modify the source buffer in place by applying the binary AND
3366 * operation to the source, using the mask, repeating as necessary.
3368 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3370 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3371 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3373 * source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3375 * # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
3376 * # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
3379 io_buffer_and_inplace(VALUE self
, VALUE mask
)
3381 struct rb_io_buffer
*buffer
= NULL
;
3382 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3384 struct rb_io_buffer
*mask_buffer
= NULL
;
3385 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3387 io_buffer_check_mask(mask_buffer
);
3388 io_buffer_check_overlaps(buffer
, mask_buffer
);
3392 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
3394 memory_and_inplace(base
, size
, mask_buffer
->base
, mask_buffer
->size
);
3400 memory_or_inplace(unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3402 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3403 base
[offset
] |= mask
[offset
% mask_size
];
3409 * source.or!(mask) -> io_buffer
3411 * Modify the source buffer in place by applying the binary OR
3412 * operation to the source, using the mask, repeating as necessary.
3414 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3416 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3417 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3419 * source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3421 * # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
3422 * # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
3425 io_buffer_or_inplace(VALUE self
, VALUE mask
)
3427 struct rb_io_buffer
*buffer
= NULL
;
3428 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3430 struct rb_io_buffer
*mask_buffer
= NULL
;
3431 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3433 io_buffer_check_mask(mask_buffer
);
3434 io_buffer_check_overlaps(buffer
, mask_buffer
);
3438 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
3440 memory_or_inplace(base
, size
, mask_buffer
->base
, mask_buffer
->size
);
3446 memory_xor_inplace(unsigned char * restrict base
, size_t size
, unsigned char * restrict mask
, size_t mask_size
)
3448 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3449 base
[offset
] ^= mask
[offset
% mask_size
];
3455 * source.xor!(mask) -> io_buffer
3457 * Modify the source buffer in place by applying the binary XOR
3458 * operation to the source, using the mask, repeating as necessary.
3460 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3462 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3463 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3465 * source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
3467 * # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
3468 * # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
3471 io_buffer_xor_inplace(VALUE self
, VALUE mask
)
3473 struct rb_io_buffer
*buffer
= NULL
;
3474 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3476 struct rb_io_buffer
*mask_buffer
= NULL
;
3477 TypedData_Get_Struct(mask
, struct rb_io_buffer
, &rb_io_buffer_type
, mask_buffer
);
3479 io_buffer_check_mask(mask_buffer
);
3480 io_buffer_check_overlaps(buffer
, mask_buffer
);
3484 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
3486 memory_xor_inplace(base
, size
, mask_buffer
->base
, mask_buffer
->size
);
3492 memory_not_inplace(unsigned char * restrict base
, size_t size
)
3494 for (size_t offset
= 0; offset
< size
; offset
+= 1) {
3495 base
[offset
] = ~base
[offset
];
3501 * source.not! -> io_buffer
3503 * Modify the source buffer in place by applying the binary NOT
3504 * operation to the source.
3506 * source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
3508 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3509 * # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
3513 * # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
3514 * # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
3517 io_buffer_not_inplace(VALUE self
)
3519 struct rb_io_buffer
*buffer
= NULL
;
3520 TypedData_Get_Struct(self
, struct rb_io_buffer
, &rb_io_buffer_type
, buffer
);
3524 io_buffer_get_bytes_for_writing(buffer
, &base
, &size
);
3526 memory_not_inplace(base
, size
);
3532 * Document-class: IO::Buffer
3534 * IO::Buffer is a efficient zero-copy buffer for input/output. There are
3535 * typical use cases:
3537 * * Create an empty buffer with ::new, fill it with buffer using #copy or
3538 * #set_value, #set_string, get buffer with #get_string or write it directly
3539 * to some file with #write.
3540 * * Create a buffer mapped to some string with ::for, then it could be used
3541 * both for reading with #get_string or #get_value, and writing (writing will
3542 * change the source string, too).
3543 * * Create a buffer mapped to some file with ::map, then it could be used for
3544 * reading and writing the underlying file.
3545 * * Create a string of a fixed size with ::string, then #read into it, or
3546 * modify it using #set_value.
3548 * Interaction with string and file memory is performed by efficient low-level
3549 * C mechanisms like `memcpy`.
3551 * The class is meant to be an utility for implementing more high-level mechanisms
3552 * like Fiber::Scheduler#io_read and Fiber::Scheduler#io_write and parsing binary
3555 * == Examples of Usage
3559 * buffer = IO::Buffer.new(8) # create empty 8-byte buffer
3561 * # #<IO::Buffer 0x0000555f5d1a5c50+8 INTERNAL>
3565 * # <IO::Buffer 0x0000555f5d156ab0+8 INTERNAL>
3566 * # 0x00000000 00 00 00 00 00 00 00 00
3567 * buffer.set_string('test', 2) # put there bytes of the "test" string, starting from offset 2
3569 * buffer.get_string # get the result
3570 * # => "\x00\x00test\x00\x00"
3572 * \Buffer from string:
3575 * IO::Buffer.for(string) do |buffer|
3578 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3579 * # 0x00000000 64 61 74 61 data
3581 * buffer.get_string(2) # read content starting from offset 2
3583 * buffer.set_string('---', 1) # write content, starting from offset 1
3587 * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
3588 * # 0x00000000 64 2d 2d 2d d---
3589 * string # original string changed, too
3593 * \Buffer from file:
3595 * File.write('test.txt', 'test data')
3597 * buffer = IO::Buffer.map(File.open('test.txt'))
3599 * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
3601 * buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
3603 * buffer.set_string('---', 1) # attempt to write
3604 * # in `set_string': Buffer is not writable! (IO::Buffer::AccessError)
3606 * # To create writable file-mapped buffer
3607 * # Open file for read-write, pass size, offset, and flags=0
3608 * buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 9, 0, 0)
3609 * buffer.set_string('---', 1)
3610 * # => 3 -- bytes written
3611 * File.read('test.txt')
3614 * <b>The class is experimental and the interface is subject to change, this
3615 * is especially true of file mappings which may be removed entirely in
3619 Init_IO_Buffer(void)
3621 rb_cIOBuffer
= rb_define_class_under(rb_cIO
, "Buffer", rb_cObject
);
3623 /* Raised when an operation would resize or re-allocate a locked buffer. */
3624 rb_eIOBufferLockedError
= rb_define_class_under(rb_cIOBuffer
, "LockedError", rb_eRuntimeError
);
3626 /* Raised when the buffer cannot be allocated for some reason, or you try to use a buffer that's not allocated. */
3627 rb_eIOBufferAllocationError
= rb_define_class_under(rb_cIOBuffer
, "AllocationError", rb_eRuntimeError
);
3629 /* Raised when you try to write to a read-only buffer, or resize an external buffer. */
3630 rb_eIOBufferAccessError
= rb_define_class_under(rb_cIOBuffer
, "AccessError", rb_eRuntimeError
);
3632 /* Raised if you try to access a buffer slice which no longer references a valid memory range of the underlying source. */
3633 rb_eIOBufferInvalidatedError
= rb_define_class_under(rb_cIOBuffer
, "InvalidatedError", rb_eRuntimeError
);
3635 /* Raised if the mask given to a binary operation is invalid, e.g. zero length or overlaps the target buffer. */
3636 rb_eIOBufferMaskError
= rb_define_class_under(rb_cIOBuffer
, "MaskError", rb_eArgError
);
3638 rb_define_alloc_func(rb_cIOBuffer
, rb_io_buffer_type_allocate
);
3639 rb_define_singleton_method(rb_cIOBuffer
, "for", rb_io_buffer_type_for
, 1);
3640 rb_define_singleton_method(rb_cIOBuffer
, "string", rb_io_buffer_type_string
, 1);
3644 GetSystemInfo(&info
);
3645 RUBY_IO_BUFFER_PAGE_SIZE
= info
.dwPageSize
;
3646 #else /* not WIN32 */
3647 RUBY_IO_BUFFER_PAGE_SIZE
= sysconf(_SC_PAGESIZE
);
3650 RUBY_IO_BUFFER_DEFAULT_SIZE
= io_buffer_default_size(RUBY_IO_BUFFER_PAGE_SIZE
);
3652 /* The operating system page size. Used for efficient page-aligned memory allocations. */
3653 rb_define_const(rb_cIOBuffer
, "PAGE_SIZE", SIZET2NUM(RUBY_IO_BUFFER_PAGE_SIZE
));
3655 /* The default buffer size, typically a (small) multiple of the PAGE_SIZE.
3656 Can be explicitly specified by setting the RUBY_IO_BUFFER_DEFAULT_SIZE
3657 environment variable. */
3658 rb_define_const(rb_cIOBuffer
, "DEFAULT_SIZE", SIZET2NUM(RUBY_IO_BUFFER_DEFAULT_SIZE
));
3660 rb_define_singleton_method(rb_cIOBuffer
, "map", io_buffer_map
, -1);
3662 rb_define_method(rb_cIOBuffer
, "initialize", rb_io_buffer_initialize
, -1);
3663 rb_define_method(rb_cIOBuffer
, "initialize_copy", rb_io_buffer_initialize_copy
, 1);
3664 rb_define_method(rb_cIOBuffer
, "inspect", rb_io_buffer_inspect
, 0);
3665 rb_define_method(rb_cIOBuffer
, "hexdump", rb_io_buffer_hexdump
, -1);
3666 rb_define_method(rb_cIOBuffer
, "to_s", rb_io_buffer_to_s
, 0);
3667 rb_define_method(rb_cIOBuffer
, "size", rb_io_buffer_size
, 0);
3668 rb_define_method(rb_cIOBuffer
, "valid?", rb_io_buffer_valid_p
, 0);
3670 rb_define_method(rb_cIOBuffer
, "transfer", rb_io_buffer_transfer
, 0);
3672 /* Indicates that the memory in the buffer is owned by someone else. See #external? for more details. */
3673 rb_define_const(rb_cIOBuffer
, "EXTERNAL", RB_INT2NUM(RB_IO_BUFFER_EXTERNAL
));
3675 /* Indicates that the memory in the buffer is owned by the buffer. See #internal? for more details. */
3676 rb_define_const(rb_cIOBuffer
, "INTERNAL", RB_INT2NUM(RB_IO_BUFFER_INTERNAL
));
3678 /* Indicates that the memory in the buffer is mapped by the operating system. See #mapped? for more details. */
3679 rb_define_const(rb_cIOBuffer
, "MAPPED", RB_INT2NUM(RB_IO_BUFFER_MAPPED
));
3681 /* Indicates that the memory in the buffer is also mapped such that it can be shared with other processes. See #shared? for more details. */
3682 rb_define_const(rb_cIOBuffer
, "SHARED", RB_INT2NUM(RB_IO_BUFFER_SHARED
));
3684 /* Indicates that the memory in the buffer is locked and cannot be resized or freed. See #locked? and #locked for more details. */
3685 rb_define_const(rb_cIOBuffer
, "LOCKED", RB_INT2NUM(RB_IO_BUFFER_LOCKED
));
3687 /* Indicates that the memory in the buffer is mapped privately and changes won't be replicated to the underlying file. See #private? for more details. */
3688 rb_define_const(rb_cIOBuffer
, "PRIVATE", RB_INT2NUM(RB_IO_BUFFER_PRIVATE
));
3690 /* Indicates that the memory in the buffer is read only, and attempts to modify it will fail. See #readonly? for more details.*/
3691 rb_define_const(rb_cIOBuffer
, "READONLY", RB_INT2NUM(RB_IO_BUFFER_READONLY
));
3693 /* Refers to little endian byte order, where the least significant byte is stored first. See #get_value for more details. */
3694 rb_define_const(rb_cIOBuffer
, "LITTLE_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_LITTLE_ENDIAN
));
3696 /* Refers to big endian byte order, where the most significant byte is stored first. See #get_value for more details. */
3697 rb_define_const(rb_cIOBuffer
, "BIG_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_BIG_ENDIAN
));
3699 /* Refers to the byte order of the host machine. See #get_value for more details. */
3700 rb_define_const(rb_cIOBuffer
, "HOST_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_HOST_ENDIAN
));
3702 /* Refers to network byte order, which is the same as big endian. See #get_value for more details. */
3703 rb_define_const(rb_cIOBuffer
, "NETWORK_ENDIAN", RB_INT2NUM(RB_IO_BUFFER_NETWORK_ENDIAN
));
3705 rb_define_method(rb_cIOBuffer
, "null?", rb_io_buffer_null_p
, 0);
3706 rb_define_method(rb_cIOBuffer
, "empty?", rb_io_buffer_empty_p
, 0);
3707 rb_define_method(rb_cIOBuffer
, "external?", rb_io_buffer_external_p
, 0);
3708 rb_define_method(rb_cIOBuffer
, "internal?", rb_io_buffer_internal_p
, 0);
3709 rb_define_method(rb_cIOBuffer
, "mapped?", rb_io_buffer_mapped_p
, 0);
3710 rb_define_method(rb_cIOBuffer
, "shared?", rb_io_buffer_shared_p
, 0);
3711 rb_define_method(rb_cIOBuffer
, "locked?", rb_io_buffer_locked_p
, 0);
3712 rb_define_method(rb_cIOBuffer
, "private?", rb_io_buffer_private_p
, 0);
3713 rb_define_method(rb_cIOBuffer
, "readonly?", io_buffer_readonly_p
, 0);
3715 // Locking to prevent changes while using pointer:
3716 // rb_define_method(rb_cIOBuffer, "lock", rb_io_buffer_lock, 0);
3717 // rb_define_method(rb_cIOBuffer, "unlock", rb_io_buffer_unlock, 0);
3718 rb_define_method(rb_cIOBuffer
, "locked", rb_io_buffer_locked
, 0);
3721 rb_define_method(rb_cIOBuffer
, "slice", io_buffer_slice
, -1);
3722 rb_define_method(rb_cIOBuffer
, "<=>", rb_io_buffer_compare
, 1);
3723 rb_define_method(rb_cIOBuffer
, "resize", io_buffer_resize
, 1);
3724 rb_define_method(rb_cIOBuffer
, "clear", io_buffer_clear
, -1);
3725 rb_define_method(rb_cIOBuffer
, "free", rb_io_buffer_free
, 0);
3727 rb_include_module(rb_cIOBuffer
, rb_mComparable
);
3729 #define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
3730 IO_BUFFER_DEFINE_DATA_TYPE(U8
);
3731 IO_BUFFER_DEFINE_DATA_TYPE(S8
);
3733 IO_BUFFER_DEFINE_DATA_TYPE(u16
);
3734 IO_BUFFER_DEFINE_DATA_TYPE(U16
);
3735 IO_BUFFER_DEFINE_DATA_TYPE(s16
);
3736 IO_BUFFER_DEFINE_DATA_TYPE(S16
);
3738 IO_BUFFER_DEFINE_DATA_TYPE(u32
);
3739 IO_BUFFER_DEFINE_DATA_TYPE(U32
);
3740 IO_BUFFER_DEFINE_DATA_TYPE(s32
);
3741 IO_BUFFER_DEFINE_DATA_TYPE(S32
);
3743 IO_BUFFER_DEFINE_DATA_TYPE(u64
);
3744 IO_BUFFER_DEFINE_DATA_TYPE(U64
);
3745 IO_BUFFER_DEFINE_DATA_TYPE(s64
);
3746 IO_BUFFER_DEFINE_DATA_TYPE(S64
);
3748 IO_BUFFER_DEFINE_DATA_TYPE(f32
);
3749 IO_BUFFER_DEFINE_DATA_TYPE(F32
);
3750 IO_BUFFER_DEFINE_DATA_TYPE(f64
);
3751 IO_BUFFER_DEFINE_DATA_TYPE(F64
);
3752 #undef IO_BUFFER_DEFINE_DATA_TYPE
3754 rb_define_singleton_method(rb_cIOBuffer
, "size_of", io_buffer_size_of
, 1);
3757 rb_define_method(rb_cIOBuffer
, "get_value", io_buffer_get_value
, 2);
3758 rb_define_method(rb_cIOBuffer
, "get_values", io_buffer_get_values
, 2);
3759 rb_define_method(rb_cIOBuffer
, "each", io_buffer_each
, -1);
3760 rb_define_method(rb_cIOBuffer
, "values", io_buffer_values
, -1);
3761 rb_define_method(rb_cIOBuffer
, "each_byte", io_buffer_each_byte
, -1);
3762 rb_define_method(rb_cIOBuffer
, "set_value", io_buffer_set_value
, 3);
3763 rb_define_method(rb_cIOBuffer
, "set_values", io_buffer_set_values
, 3);
3765 rb_define_method(rb_cIOBuffer
, "copy", io_buffer_copy
, -1);
3767 rb_define_method(rb_cIOBuffer
, "get_string", io_buffer_get_string
, -1);
3768 rb_define_method(rb_cIOBuffer
, "set_string", io_buffer_set_string
, -1);
3770 // Binary buffer manipulations:
3771 rb_define_method(rb_cIOBuffer
, "&", io_buffer_and
, 1);
3772 rb_define_method(rb_cIOBuffer
, "|", io_buffer_or
, 1);
3773 rb_define_method(rb_cIOBuffer
, "^", io_buffer_xor
, 1);
3774 rb_define_method(rb_cIOBuffer
, "~", io_buffer_not
, 0);
3776 rb_define_method(rb_cIOBuffer
, "and!", io_buffer_and_inplace
, 1);
3777 rb_define_method(rb_cIOBuffer
, "or!", io_buffer_or_inplace
, 1);
3778 rb_define_method(rb_cIOBuffer
, "xor!", io_buffer_xor_inplace
, 1);
3779 rb_define_method(rb_cIOBuffer
, "not!", io_buffer_not_inplace
, 0);
3782 rb_define_method(rb_cIOBuffer
, "read", io_buffer_read
, -1);
3783 rb_define_method(rb_cIOBuffer
, "pread", io_buffer_pread
, -1);
3784 rb_define_method(rb_cIOBuffer
, "write", io_buffer_write
, -1);
3785 rb_define_method(rb_cIOBuffer
, "pwrite", io_buffer_pwrite
, -1);