summaryrefslogtreecommitdiff
path: root/io_buffer.c
diff options
context:
space:
mode:
Diffstat (limited to 'io_buffer.c')
-rw-r--r--io_buffer.c555
1 files changed, 424 insertions, 131 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 7715aa0d37..faa5304248 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -6,17 +6,20 @@
**********************************************************************/
-#include "ruby/io.h"
#include "ruby/io/buffer.h"
#include "ruby/fiber/scheduler.h"
+// For `rb_nogvl`.
+#include "ruby/thread.h"
+
#include "internal.h"
#include "internal/array.h"
#include "internal/bits.h"
#include "internal/error.h"
+#include "internal/gc.h"
#include "internal/numeric.h"
#include "internal/string.h"
-#include "internal/thread.h"
+#include "internal/io.h"
VALUE rb_cIOBuffer;
VALUE rb_eIOBufferLockedError;
@@ -36,6 +39,7 @@ size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
enum {
RB_IO_BUFFER_HEXDUMP_DEFAULT_WIDTH = 16,
+ RB_IO_BUFFER_HEXDUMP_MAXIMUM_WIDTH = 1024,
RB_IO_BUFFER_INSPECT_HEXDUMP_MAXIMUM_SIZE = 256,
RB_IO_BUFFER_INSPECT_HEXDUMP_WIDTH = 16,
@@ -81,6 +85,8 @@ io_buffer_map_memory(size_t size, int flags)
if (base == MAP_FAILED) {
rb_sys_fail("io_buffer_map_memory:mmap");
}
+
+ ruby_annotate_mmap(base, size, "Ruby:io_buffer_map_memory");
#endif
return base;
@@ -260,21 +266,43 @@ io_buffer_free(struct rb_io_buffer *buffer)
if (buffer->mapping) {
if (RB_IO_BUFFER_DEBUG) fprintf(stderr, "io_buffer_free:CloseHandle -> %p\n", buffer->mapping);
if (!CloseHandle(buffer->mapping)) {
- fprintf(stderr, "io_buffer_free:GetLastError -> %d\n", GetLastError());
+ fprintf(stderr, "io_buffer_free:GetLastError -> %lu\n", GetLastError());
}
buffer->mapping = NULL;
}
#endif
}
-void
+static void
rb_io_buffer_type_mark(void *_buffer)
{
struct rb_io_buffer *buffer = _buffer;
- rb_gc_mark(buffer->source);
+ if (buffer->source != Qnil) {
+ if (RB_TYPE_P(buffer->source, T_STRING)) {
+ // The `source` String has to be pinned, because the `base` may point to the embedded String content,
+ // which can be otherwise moved by GC compaction.
+ rb_gc_mark(buffer->source);
+ } else {
+ rb_gc_mark_movable(buffer->source);
+ }
+ }
}
-void
+static void
+rb_io_buffer_type_compact(void *_buffer)
+{
+ struct rb_io_buffer *buffer = _buffer;
+ if (buffer->source != Qnil) {
+ if (RB_TYPE_P(buffer->source, T_STRING)) {
+ // The `source` String has to be pinned, because the `base` may point to the embedded String content,
+ // which can be otherwise moved by GC compaction.
+ } else {
+ buffer->source = rb_gc_location(buffer->source);
+ }
+ }
+}
+
+static void
rb_io_buffer_type_free(void *_buffer)
{
struct rb_io_buffer *buffer = _buffer;
@@ -282,7 +310,7 @@ rb_io_buffer_type_free(void *_buffer)
io_buffer_free(buffer);
}
-size_t
+static size_t
rb_io_buffer_type_size(const void *_buffer)
{
const struct rb_io_buffer *buffer = _buffer;
@@ -301,6 +329,7 @@ static const rb_data_type_t rb_io_buffer_type = {
.dmark = rb_io_buffer_type_mark,
.dfree = rb_io_buffer_type_free,
.dsize = rb_io_buffer_type_size,
+ .dcompact = rb_io_buffer_type_compact,
},
.data = NULL,
.flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE,
@@ -356,7 +385,7 @@ io_buffer_extract_size(VALUE argument)
}
// Extract a width argument, which must be a non-negative integer, and must be
-// at least the given minimum.
+// at least the given minimum and at most RB_IO_BUFFER_HEXDUMP_MAXIMUM_WIDTH.
static inline size_t
io_buffer_extract_width(VALUE argument, size_t minimum)
{
@@ -370,6 +399,10 @@ io_buffer_extract_width(VALUE argument, size_t minimum)
rb_raise(rb_eArgError, "Width must be at least %" PRIuSIZE "!", minimum);
}
+ if (width > RB_IO_BUFFER_HEXDUMP_MAXIMUM_WIDTH) {
+ rb_raise(rb_eArgError, "Width must be at most %" PRIuSIZE "!", (size_t)RB_IO_BUFFER_HEXDUMP_MAXIMUM_WIDTH);
+ }
+
return width;
}
@@ -451,6 +484,8 @@ io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offs
VALUE
rb_io_buffer_type_allocate(VALUE self)
{
+ io_buffer_experimental();
+
struct rb_io_buffer *buffer = NULL;
VALUE instance = TypedData_Make_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
@@ -493,7 +528,9 @@ io_buffer_for_yield_instance(VALUE _arguments)
arguments->instance = io_buffer_for_make_instance(arguments->klass, arguments->string, arguments->flags);
- rb_str_locktmp(arguments->string);
+ if (!RB_OBJ_FROZEN(arguments->string)) {
+ rb_str_locktmp(arguments->string);
+ }
return rb_yield(arguments->instance);
}
@@ -507,7 +544,9 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
rb_io_buffer_free(arguments->instance);
}
- rb_str_unlocktmp(arguments->string);
+ if (!RB_OBJ_FROZEN(arguments->string)) {
+ rb_str_unlocktmp(arguments->string);
+ }
return Qnil;
}
@@ -537,7 +576,7 @@ io_buffer_for_yield_instance_ensure(VALUE _arguments)
* buffer.get_string(0, 1)
* # => "t"
* string
- * # => "best"
+ * # => "test"
*
* buffer.resize(100)
* # in `resize': Cannot resize external buffer! (IO::Buffer::AccessError)
@@ -617,8 +656,6 @@ rb_io_buffer_new(void *base, size_t size, enum rb_io_buffer_flags flags)
VALUE
rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags flags)
{
- io_buffer_experimental();
-
VALUE instance = rb_io_buffer_type_allocate(rb_cIOBuffer);
struct rb_io_buffer *buffer = NULL;
@@ -635,18 +672,25 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags
* call-seq: IO::Buffer.map(file, [size, [offset, [flags]]]) -> io_buffer
*
* Create an IO::Buffer for reading from +file+ by memory-mapping the file.
- * +file_io+ should be a +File+ instance, opened for reading.
+ * +file+ should be a +File+ instance, opened for reading or reading and writing.
*
* Optional +size+ and +offset+ of mapping can be specified.
+ * Trying to map an empty file or specify +size+ of 0 will raise an error.
+ * Valid values for +offset+ are system-dependent.
+ *
+ * By default, the buffer is writable and expects the file to be writable.
+ * It is also shared, so several processes can use the same mapping.
*
- * By default, the buffer would be immutable (read only); to create a writable
- * mapping, you need to open a file in read-write mode, and explicitly pass
- * +flags+ argument without IO::Buffer::IMMUTABLE.
+ * You can pass IO::Buffer::READONLY in +flags+ argument to make a read-only buffer;
+ * this allows to work with files opened only for reading.
+ * Specifying IO::Buffer::PRIVATE in +flags+ creates a private mapping,
+ * which will not impact other processes or the underlying file.
+ * It also allows updating a buffer created from a read-only file.
*
* File.write('test.txt', 'test')
*
* buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
- * # => #<IO::Buffer 0x00000001014a0000+4 MAPPED READONLY>
+ * # => #<IO::Buffer 0x00000001014a0000+4 EXTERNAL MAPPED FILE SHARED READONLY>
*
* buffer.readonly? # => true
*
@@ -654,7 +698,7 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags
* # => "test"
*
* buffer.set_string('b', 0)
- * # `set_string': Buffer is not writable! (IO::Buffer::AccessError)
+ * # 'IO::Buffer#set_string': Buffer is not writable! (IO::Buffer::AccessError)
*
* # create read/write mapping: length 4 bytes, offset 0, flags 0
* buffer = IO::Buffer.map(File.open('test.txt', 'r+'), 4, 0)
@@ -676,31 +720,48 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
// We might like to handle a string path?
VALUE io = argv[0];
+ rb_off_t file_size = rb_file_size(io);
+ // Compiler can confirm that we handled file_size <= 0 case:
+ if (UNLIKELY(file_size <= 0)) {
+ rb_raise(rb_eArgError, "Invalid negative or zero file size!");
+ }
+ // Here, we assume that file_size is positive:
+ else if (UNLIKELY((uintmax_t)file_size > SIZE_MAX)) {
+ rb_raise(rb_eArgError, "File larger than address space!");
+ }
+
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
size = io_buffer_extract_size(argv[1]);
- }
- else {
- rb_off_t file_size = rb_file_size(io);
-
- // Compiler can confirm that we handled file_size < 0 case:
- if (file_size < 0) {
- rb_raise(rb_eArgError, "Invalid negative file size!");
+ if (UNLIKELY(size == 0)) {
+ rb_raise(rb_eArgError, "Size can't be zero!");
}
- // Here, we assume that file_size is positive:
- else if ((uintmax_t)file_size > SIZE_MAX) {
- rb_raise(rb_eArgError, "File larger than address space!");
- }
- else {
- // This conversion should be safe:
- size = (size_t)file_size;
+ if (UNLIKELY(size > (size_t)file_size)) {
+ rb_raise(rb_eArgError, "Size can't be larger than file size!");
}
}
+ else {
+ // This conversion should be safe:
+ size = (size_t)file_size;
+ }
// This is the file offset, not the buffer offset:
rb_off_t offset = 0;
if (argc >= 3) {
offset = NUM2OFFT(argv[2]);
+ if (UNLIKELY(offset < 0)) {
+ rb_raise(rb_eArgError, "Offset can't be negative!");
+ }
+ if (UNLIKELY(offset >= file_size)) {
+ rb_raise(rb_eArgError, "Offset too large!");
+ }
+ if (RB_NIL_P(argv[1])) {
+ // Decrease size if it's set from the actual file size:
+ size = (size_t)(file_size - offset);
+ }
+ else if (UNLIKELY((size_t)(file_size - offset) < size)) {
+ rb_raise(rb_eArgError, "Offset too large!");
+ }
}
enum rb_io_buffer_flags flags = 0;
@@ -749,8 +810,6 @@ io_flags_for_size(size_t size)
VALUE
rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
{
- io_buffer_experimental();
-
rb_check_arity(argc, 0, 2);
struct rb_io_buffer *buffer = NULL;
@@ -843,7 +902,8 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size)
static inline void
io_buffer_get_bytes_for_writing(struct rb_io_buffer *buffer, void **base, size_t *size)
{
- if (buffer->flags & RB_IO_BUFFER_READONLY) {
+ if (buffer->flags & RB_IO_BUFFER_READONLY ||
+ (!NIL_P(buffer->source) && OBJ_FROZEN(buffer->source))) {
rb_raise(rb_eIOBufferAccessError, "Buffer is not writable!");
}
@@ -1369,6 +1429,17 @@ rb_io_buffer_try_unlock(VALUE self)
return 0;
}
+static VALUE
+rb_io_buffer_locked_ensure(VALUE self)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ buffer->flags &= ~RB_IO_BUFFER_LOCKED;
+
+ return Qnil;
+}
+
/*
* call-seq: locked { ... }
*
@@ -1411,11 +1482,7 @@ rb_io_buffer_locked(VALUE self)
buffer->flags |= RB_IO_BUFFER_LOCKED;
- VALUE result = rb_yield(self);
-
- buffer->flags &= ~RB_IO_BUFFER_LOCKED;
-
- return result;
+ return rb_ensure(rb_yield, self, rb_io_buffer_locked_ensure, self);
}
/*
@@ -1426,7 +1493,7 @@ rb_io_buffer_locked(VALUE self)
* * for a buffer created from scratch: free memory.
* * for a buffer created from string: undo the association.
*
- * After the buffer is freed, no further operations can't be performed on it.
+ * After the buffer is freed, no further operations can be performed on it.
*
* You can resize a freed buffer to re-allocate it.
*
@@ -1469,13 +1536,19 @@ VALUE rb_io_buffer_free_locked(VALUE self)
return self;
}
+static bool
+size_sum_is_bigger_than(size_t a, size_t b, size_t x)
+{
+ struct rbimpl_size_overflow_tag size = rbimpl_size_add_overflow(a, b);
+ return size.overflowed || size.result > x;
+}
+
// Validate that access to the buffer is within bounds, assuming you want to
// access length bytes from the specified offset.
static inline void
io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
- // We assume here that offset + length won't overflow:
- if (offset + length > buffer->size) {
+ if (size_sum_is_bigger_than(offset, length, buffer->size)) {
rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
}
}
@@ -1532,6 +1605,7 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
struct rb_io_buffer *slice = NULL;
TypedData_Get_Struct(instance, struct rb_io_buffer, &rb_io_buffer_type, slice);
+ slice->flags |= (buffer->flags & RB_IO_BUFFER_READONLY);
slice->base = (char*)buffer->base + offset;
slice->size = length;
@@ -1566,7 +1640,7 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
* buffer's bounds.
*
* string = 'test'
- * buffer = IO::Buffer.for(string)
+ * buffer = IO::Buffer.for(string).dup
*
* slice = buffer.slice
* # =>
@@ -1593,12 +1667,8 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
* # it is also visible at position 1 of the original buffer
* buffer
* # =>
- * # #<IO::Buffer 0x00007fc3d31e2d80+4 SLICE>
+ * # #<IO::Buffer 0x00007fc3d31e2d80+4 INTERNAL>
* # 0x00000000 74 6f 73 74 tost
- *
- * # ...and original string
- * string
- * # => tost
*/
static VALUE
io_buffer_slice(int argc, VALUE *argv, VALUE self)
@@ -1788,9 +1858,9 @@ rb_io_buffer_compare(VALUE self, VALUE other)
}
static void
-io_buffer_validate_type(size_t size, size_t offset)
+io_buffer_validate_type(size_t size, size_t offset, size_t extend)
{
- if (offset > size) {
+ if (size_sum_is_bigger_than(offset, extend, size)) {
rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%"PRIdSIZE" > size=%"PRIdSIZE")", offset, size);
}
}
@@ -1810,6 +1880,9 @@ io_buffer_validate_type(size_t size, size_t offset)
// :u64, :U64 | unsigned 64-bit integer.
// :s64, :S64 | signed 64-bit integer.
//
+// :u128, :U128 | unsigned 128-bit integer.
+// :s128, :S128 | signed 128-bit integer.
+//
// :f32, :F32 | 32-bit floating point number.
// :f64, :F64 | 64-bit floating point number.
@@ -1841,13 +1914,53 @@ ruby_swapf64(double value)
return swap.value;
}
+// Structures and conversion functions are now in numeric.h/numeric.c
+// Unified swap function for 128-bit integers (works with both signed and unsigned)
+// Since both rb_uint128_t and rb_int128_t have the same memory layout,
+// we can use a union to make the swap function work with both types
+static inline rb_uint128_t
+ruby_swap128_uint(rb_uint128_t x)
+{
+ rb_uint128_t result;
+#ifdef HAVE_UINT128_T
+#if __has_builtin(__builtin_bswap128)
+ result.value = __builtin_bswap128(x.value);
+#else
+ // Manual byte swap for 128-bit integers
+ uint64_t low = (uint64_t)x.value;
+ uint64_t high = (uint64_t)(x.value >> 64);
+ low = ruby_swap64(low);
+ high = ruby_swap64(high);
+ result.value = ((uint128_t)low << 64) | high;
+#endif
+#else
+ // Fallback swap function using two 64-bit integers
+ // For big-endian data on little-endian host (or vice versa):
+ // 1. Swap bytes within each 64-bit part
+ // 2. Swap the order of the parts (since big-endian stores high first, little-endian stores low first)
+ result.parts.low = ruby_swap64(x.parts.high);
+ result.parts.high = ruby_swap64(x.parts.low);
+#endif
+ return result;
+}
+
+static inline rb_int128_t
+ruby_swap128_int(rb_int128_t x)
+{
+ union uint128_int128_conversion conversion = {
+ .int128 = x
+ };
+ conversion.uint128 = ruby_swap128_uint(conversion.uint128);
+ return conversion.int128;
+}
+
#define IO_BUFFER_DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \
static ID RB_IO_BUFFER_DATA_TYPE_##name; \
\
static VALUE \
io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
{ \
- io_buffer_validate_type(size, *offset + sizeof(type)); \
+ io_buffer_validate_type(size, *offset, sizeof(type)); \
type value; \
memcpy(&value, (char*)base + *offset, sizeof(type)); \
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
@@ -1858,7 +1971,7 @@ io_buffer_read_##name(const void* base, size_t size, size_t *offset) \
static void \
io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _value) \
{ \
- io_buffer_validate_type(size, *offset + sizeof(type)); \
+ io_buffer_validate_type(size, *offset, sizeof(type)); \
type value = unwrap(_value); \
if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \
memcpy((char*)base + *offset, &value, sizeof(type)); \
@@ -1887,6 +2000,11 @@ IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NU
IO_BUFFER_DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
IO_BUFFER_DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64)
+IO_BUFFER_DECLARE_TYPE(u128, rb_uint128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
+IO_BUFFER_DECLARE_TYPE(U128, rb_uint128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_uint128_to_numeric, rb_numeric_to_uint128, ruby_swap128_uint)
+IO_BUFFER_DECLARE_TYPE(s128, rb_int128_t, RB_IO_BUFFER_LITTLE_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
+IO_BUFFER_DECLARE_TYPE(S128, rb_int128_t, RB_IO_BUFFER_BIG_ENDIAN, rb_int128_to_numeric, rb_numeric_to_int128, ruby_swap128_int)
+
IO_BUFFER_DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
IO_BUFFER_DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32)
IO_BUFFER_DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64)
@@ -1911,6 +2029,10 @@ io_buffer_buffer_type_size(ID buffer_type)
IO_BUFFER_DATA_TYPE_SIZE(U64)
IO_BUFFER_DATA_TYPE_SIZE(s64)
IO_BUFFER_DATA_TYPE_SIZE(S64)
+ IO_BUFFER_DATA_TYPE_SIZE(u128)
+ IO_BUFFER_DATA_TYPE_SIZE(U128)
+ IO_BUFFER_DATA_TYPE_SIZE(s128)
+ IO_BUFFER_DATA_TYPE_SIZE(S128)
IO_BUFFER_DATA_TYPE_SIZE(f32)
IO_BUFFER_DATA_TYPE_SIZE(F32)
IO_BUFFER_DATA_TYPE_SIZE(f64)
@@ -1967,6 +2089,11 @@ rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *of
IO_BUFFER_GET_VALUE(s64)
IO_BUFFER_GET_VALUE(S64)
+ IO_BUFFER_GET_VALUE(u128)
+ IO_BUFFER_GET_VALUE(U128)
+ IO_BUFFER_GET_VALUE(s128)
+ IO_BUFFER_GET_VALUE(S128)
+
IO_BUFFER_GET_VALUE(f32)
IO_BUFFER_GET_VALUE(F32)
IO_BUFFER_GET_VALUE(f64)
@@ -1996,6 +2123,10 @@ rb_io_buffer_get_value(const void* base, size_t size, ID buffer_type, size_t *of
* * +:U64+: unsigned integer, 8 bytes, big-endian
* * +:s64+: signed integer, 8 bytes, little-endian
* * +:S64+: signed integer, 8 bytes, big-endian
+ * * +:u128+: unsigned integer, 16 bytes, little-endian
+ * * +:U128+: unsigned integer, 16 bytes, big-endian
+ * * +:s128+: signed integer, 16 bytes, little-endian
+ * * +:S128+: signed integer, 16 bytes, big-endian
* * +:f32+: float, 4 bytes, little-endian
* * +:F32+: float, 4 bytes, big-endian
* * +:f64+: double, 8 bytes, little-endian
@@ -2177,7 +2308,7 @@ io_buffer_values(int argc, VALUE *argv, VALUE self)
/*
* call-seq:
- * each_byte([offset, [count]]) {|offset, byte| ...} -> self
+ * each_byte([offset, [count]]) {|byte| ...} -> self
* each_byte([offset, [count]]) -> enumerator
*
* Iterates over the buffer, yielding each byte starting from +offset+.
@@ -2201,7 +2332,11 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
size_t offset, count;
- io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
+ io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc, argv, &offset, &count);
+
+ if (size_sum_is_bigger_than(offset, count, size)) {
+ rb_raise(rb_eArgError, "Specified offset+count is bigger than the buffer size!");
+ }
for (size_t i = 0; i < count; i++) {
unsigned char *value = (unsigned char *)base + i + offset;
@@ -2233,6 +2368,11 @@ rb_io_buffer_set_value(const void* base, size_t size, ID buffer_type, size_t *of
IO_BUFFER_SET_VALUE(s64);
IO_BUFFER_SET_VALUE(S64);
+ IO_BUFFER_SET_VALUE(u128);
+ IO_BUFFER_SET_VALUE(U128);
+ IO_BUFFER_SET_VALUE(s128);
+ IO_BUFFER_SET_VALUE(S128);
+
IO_BUFFER_SET_VALUE(f32);
IO_BUFFER_SET_VALUE(F32);
IO_BUFFER_SET_VALUE(f64);
@@ -2330,8 +2470,32 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values
return SIZET2NUM(offset);
}
+static size_t IO_BUFFER_BLOCKING_SIZE = 1024*1024;
+
+struct io_buffer_memmove_arguments {
+ unsigned char * destination;
+ const unsigned char * source;
+ size_t length;
+};
+
+static void *
+io_buffer_memmove_blocking(void *data)
+{
+ struct io_buffer_memmove_arguments *arguments = (struct io_buffer_memmove_arguments *)data;
+
+ memmove(arguments->destination, arguments->source, arguments->length);
+
+ return NULL;
+}
+
+static void
+io_buffer_memmove_unblock(void *data)
+{
+ // No safe way to interrupt.
+}
+
static void
-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)
+io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length)
{
void *base;
size_t size;
@@ -2339,11 +2503,21 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_
io_buffer_validate_range(buffer, offset, length);
- if (source_offset + length > source_size) {
+ if (size_sum_is_bigger_than(source_offset, length, source_size)) {
rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!");
}
- memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length);
+ struct io_buffer_memmove_arguments arguments = {
+ .destination = (unsigned char*)base+offset,
+ .source = (unsigned char*)source_base+source_offset,
+ .length = length
+ };
+
+ if (arguments.length >= IO_BUFFER_BLOCKING_SIZE) {
+ rb_nogvl(io_buffer_memmove_blocking, &arguments, io_buffer_memmove_unblock, &arguments, RB_NOGVL_OFFLOAD_SAFE);
+ } else if (arguments.length != 0) {
+ memmove(arguments.destination, arguments.source, arguments.length);
+ }
}
// (offset, length, source_offset) -> length
@@ -2380,7 +2554,7 @@ io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t
length = source_size - source_offset;
}
- io_buffer_memcpy(buffer, offset, source_base, source_offset, source_size, length);
+ io_buffer_memmove(buffer, offset, source_base, source_offset, source_size, length);
return SIZET2NUM(length);
}
@@ -2415,7 +2589,9 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
io_buffer_initialize(self, buffer, NULL, source_size, io_flags_for_size(source_size), Qnil);
- return io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
+ VALUE result = io_buffer_copy_from(buffer, source_base, source_size, 0, NULL);
+ RB_GC_GUARD(source);
+ return result;
}
/*
@@ -2423,7 +2599,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* copy(source, [offset, [length, [source_offset]]]) -> size
*
* Efficiently copy from a source IO::Buffer into the buffer, at +offset+
- * using +memcpy+. For copying String instances, see #set_string.
+ * using +memmove+. For copying String instances, see #set_string.
*
* buffer = IO::Buffer.new(32)
* # =>
@@ -2441,13 +2617,14 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
*
* #copy can be used to put buffer into strings associated with buffer:
*
- * string= "buffer: "
- * # => "buffer: "
- * buffer = IO::Buffer.for(string)
- * buffer.copy(IO::Buffer.for("test"), 5)
+ * string = "data: "
+ * # => "data: "
+ * buffer = IO::Buffer.for(string) do |buffer|
+ * buffer.copy(IO::Buffer.for("test"), 5)
+ * end
* # => 4
* string
- * # => "buffer:test"
+ * # => "data:test"
*
* Attempt to copy into a read-only buffer will fail:
*
@@ -2471,6 +2648,19 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source)
* buffer = IO::Buffer.new(2)
* buffer.copy(IO::Buffer.for('test'), 0)
* # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
+ *
+ * It is safe to copy between memory regions that overlaps each other.
+ * In such case, the data is copied as if the data was first copied from the source buffer to
+ * a temporary buffer, and then copied from the temporary buffer to the destination buffer.
+ *
+ * buffer = IO::Buffer.new(10)
+ * buffer.set_string("0123456789")
+ * buffer.copy(buffer, 3, 7)
+ * # => 7
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL>
+ * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
@@ -2486,7 +2676,9 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
- return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
+ VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
+ RB_GC_GUARD(source);
+ return result;
}
/*
@@ -2532,7 +2724,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
* call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size
*
* Efficiently copy from a source String into the buffer, at +offset+ using
- * +memcpy+.
+ * +memmove+.
*
* buf = IO::Buffer.new(8)
* # =>
@@ -2564,7 +2756,9 @@ io_buffer_set_string(int argc, VALUE *argv, VALUE self)
const void *source_base = RSTRING_PTR(string);
size_t source_size = RSTRING_LEN(string);
- return io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
+ VALUE result = io_buffer_copy_from(buffer, source_base, source_size, argc-1, argv+1);
+ RB_GC_GUARD(string);
+ return result;
}
void
@@ -2588,29 +2782,29 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
* Fill buffer with +value+, starting with +offset+ and going for +length+
* bytes.
*
- * buffer = IO::Buffer.for('test')
+ * buffer = IO::Buffer.for('test').dup
* # =>
- * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
* # 0x00000000 74 65 73 74 test
*
* buffer.clear
* # =>
- * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
* # 0x00000000 00 00 00 00 ....
*
* buf.clear(1) # fill with 1
* # =>
- * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
* # 0x00000000 01 01 01 01 ....
*
* buffer.clear(2, 1, 2) # fill with 2, starting from offset 1, for 2 bytes
* # =>
- * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
* # 0x00000000 01 02 02 01 ....
*
* buffer.clear(2, 1) # fill with 2, starting from offset 1
* # =>
- * # <IO::Buffer 0x00007fca40087c38+4 SLICE>
+ * # <IO::Buffer 0x00007fca40087c38+4 INTERNAL>
* # 0x00000000 01 02 02 02 ....
*/
static VALUE
@@ -2657,10 +2851,10 @@ io_buffer_default_size(size_t page_size)
}
struct io_buffer_blocking_region_argument {
+ struct rb_io *io;
struct rb_io_buffer *buffer;
rb_blocking_function_t *function;
void *data;
- int descriptor;
};
static VALUE
@@ -2668,7 +2862,7 @@ io_buffer_blocking_region_begin(VALUE _argument)
{
struct io_buffer_blocking_region_argument *argument = (void*)_argument;
- return rb_thread_io_blocking_region(argument->function, argument->data, argument->descriptor);
+ return rb_io_blocking_region(argument->io, argument->function, argument->data);
}
static VALUE
@@ -2682,13 +2876,16 @@ io_buffer_blocking_region_ensure(VALUE _argument)
}
static VALUE
-io_buffer_blocking_region(struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data, int descriptor)
+io_buffer_blocking_region(VALUE io, struct rb_io_buffer *buffer, rb_blocking_function_t *function, void *data)
{
+ struct rb_io *ioptr;
+ RB_IO_POINTER(io, ioptr);
+
struct io_buffer_blocking_region_argument argument = {
+ .io = ioptr,
.buffer = buffer,
.function = function,
.data = data,
- .descriptor = descriptor,
};
// If the buffer is already locked, we can skip the ensure (unlock):
@@ -2745,6 +2942,8 @@ io_buffer_read_internal(void *_argument)
VALUE
rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
{
+ io = rb_io_get_io(io);
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_read(scheduler, io, self, length, offset);
@@ -2775,7 +2974,7 @@ rb_io_buffer_read(VALUE self, VALUE io, size_t length, size_t offset)
.length = length,
};
- return io_buffer_blocking_region(buffer, io_buffer_read_internal, &argument, descriptor);
+ return io_buffer_blocking_region(io, buffer, io_buffer_read_internal, &argument);
}
/*
@@ -2862,6 +3061,8 @@ io_buffer_pread_internal(void *_argument)
VALUE
rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
{
+ io = rb_io_get_io(io);
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_pread(scheduler, io, from, self, length, offset);
@@ -2893,7 +3094,7 @@ rb_io_buffer_pread(VALUE self, VALUE io, rb_off_t from, size_t length, size_t of
.offset = from,
};
- return io_buffer_blocking_region(buffer, io_buffer_pread_internal, &argument, descriptor);
+ return io_buffer_blocking_region(io, buffer, io_buffer_pread_internal, &argument);
}
/*
@@ -2982,6 +3183,8 @@ io_buffer_write_internal(void *_argument)
VALUE
rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
{
+ io = rb_io_get_write_io(rb_io_get_io(io));
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_write(scheduler, io, self, length, offset);
@@ -3012,7 +3215,7 @@ rb_io_buffer_write(VALUE self, VALUE io, size_t length, size_t offset)
.length = length,
};
- return io_buffer_blocking_region(buffer, io_buffer_write_internal, &argument, descriptor);
+ return io_buffer_blocking_region(io, buffer, io_buffer_write_internal, &argument);
}
/*
@@ -3046,6 +3249,7 @@ io_buffer_write(int argc, VALUE *argv, VALUE self)
return rb_io_buffer_write(self, io, length, offset);
}
+
struct io_buffer_pwrite_internal_argument {
// The file descriptor to write to:
int descriptor;
@@ -3091,6 +3295,8 @@ io_buffer_pwrite_internal(void *_argument)
VALUE
rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t offset)
{
+ io = rb_io_get_write_io(rb_io_get_io(io));
+
VALUE scheduler = rb_fiber_scheduler_current();
if (scheduler != Qnil) {
VALUE result = rb_fiber_scheduler_io_pwrite(scheduler, io, from, self, length, offset);
@@ -3130,7 +3336,7 @@ rb_io_buffer_pwrite(VALUE self, VALUE io, rb_off_t from, size_t length, size_t o
.offset = from,
};
- return io_buffer_blocking_region(buffer, io_buffer_pwrite_internal, &argument, descriptor);
+ return io_buffer_blocking_region(io, buffer, io_buffer_pwrite_internal, &argument);
}
/*
@@ -3172,14 +3378,14 @@ io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
}
static inline void
-io_buffer_check_mask(const struct rb_io_buffer *buffer)
+io_buffer_check_mask_size(size_t size)
{
- if (buffer->size == 0)
+ if (size == 0)
rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
}
static void
-memory_and(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
+memory_and(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
output[offset] = base[offset] & mask[offset % mask_size];
@@ -3207,19 +3413,27 @@ io_buffer_and(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ const void *base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ const void *mask_base;
+ size_t mask_size;
+ io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
+
+ io_buffer_check_mask_size(mask_size);
- VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
struct rb_io_buffer *output_buffer = NULL;
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_and(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
+ memory_and(output_buffer->base, base, size, mask_base, mask_size);
return output;
}
static void
-memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
+memory_or(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
output[offset] = base[offset] | mask[offset % mask_size];
@@ -3247,19 +3461,27 @@ io_buffer_or(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ const void *base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ const void *mask_base;
+ size_t mask_size;
+ io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
- VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ io_buffer_check_mask_size(mask_size);
+
+ VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
struct rb_io_buffer *output_buffer = NULL;
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_or(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
+ memory_or(output_buffer->base, base, size, mask_base, mask_size);
return output;
}
static void
-memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
+memory_xor(unsigned char * restrict output, const unsigned char * restrict base, size_t size, const unsigned char * restrict mask, size_t mask_size)
{
for (size_t offset = 0; offset < size; offset += 1) {
output[offset] = base[offset] ^ mask[offset % mask_size];
@@ -3287,19 +3509,27 @@ io_buffer_xor(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ const void *base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ const void *mask_base;
+ size_t mask_size;
+ io_buffer_get_bytes_for_reading(mask_buffer, &mask_base, &mask_size);
+
+ io_buffer_check_mask_size(mask_size);
- VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
struct rb_io_buffer *output_buffer = NULL;
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_xor(output_buffer->base, buffer->base, buffer->size, mask_buffer->base, mask_buffer->size);
+ memory_xor(output_buffer->base, base, size, mask_base, mask_size);
return output;
}
static void
-memory_not(unsigned char * restrict output, unsigned char * restrict base, size_t size)
+memory_not(unsigned char * restrict output, const unsigned char * restrict base, size_t size)
{
for (size_t offset = 0; offset < size; offset += 1) {
output[offset] = ~base[offset];
@@ -3310,7 +3540,7 @@ memory_not(unsigned char * restrict output, unsigned char * restrict base, size_
* call-seq:
* ~source -> io_buffer
*
- * Generate a new buffer the same size as the source by applying the binary NOT
+ * Generate a new buffer the same size as the source by applying the unary NOT
* operation to the source.
*
* ~IO::Buffer.for("1234567890")
@@ -3324,11 +3554,15 @@ io_buffer_not(VALUE self)
struct rb_io_buffer *buffer = NULL;
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
- VALUE output = rb_io_buffer_new(NULL, buffer->size, io_flags_for_size(buffer->size));
+ const void *base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ VALUE output = rb_io_buffer_new(NULL, size, io_flags_for_size(size));
struct rb_io_buffer *output_buffer = NULL;
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_buffer);
- memory_not(output_buffer->base, buffer->base, buffer->size);
+ memory_not(output_buffer->base, base, size);
return output;
}
@@ -3340,7 +3574,7 @@ io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
return io_buffer_overlaps(b, a);
}
- return (b->base >= a->base) && (b->base <= (void*)((unsigned char *)a->base + a->size));
+ return (b->base >= a->base) && (b->base < (void*)((unsigned char *)a->base + a->size));
}
static inline void
@@ -3384,7 +3618,7 @@ io_buffer_and_inplace(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ io_buffer_check_mask_size(mask_buffer->size);
io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
@@ -3430,7 +3664,7 @@ io_buffer_or_inplace(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ io_buffer_check_mask_size(mask_buffer->size);
io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
@@ -3476,7 +3710,7 @@ io_buffer_xor_inplace(VALUE self, VALUE mask)
struct rb_io_buffer *mask_buffer = NULL;
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_buffer);
- io_buffer_check_mask(mask_buffer);
+ io_buffer_check_mask_size(mask_buffer->size);
io_buffer_check_overlaps(buffer, mask_buffer);
void *base;
@@ -3500,7 +3734,7 @@ memory_not_inplace(unsigned char * restrict base, size_t size)
* call-seq:
* source.not! -> io_buffer
*
- * Modify the source buffer in place by applying the binary NOT
+ * Modify the source buffer in place by applying the unary NOT
* operation to the source.
*
* source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
@@ -3528,6 +3762,60 @@ io_buffer_not_inplace(VALUE self)
return self;
}
+static size_t
+memory_bit_count(const unsigned char *base, size_t size)
+{
+ size_t count = 0;
+
+ // Process 8 bytes at a time for efficiency:
+ const uint64_t *base64 = (const uint64_t *)base;
+ size_t count64 = size / 8;
+ for (size_t i = 0; i < count64; i += 1) {
+ count += rb_popcount64(base64[i]);
+ }
+
+ // Process any remaining bytes:
+ size_t remaining = size % 8;
+ const unsigned char *tail = base + (count64 * 8);
+ for (size_t i = 0; i < remaining; i += 1) {
+ count += rb_popcount32(tail[i]);
+ }
+
+ return count;
+}
+
+/*
+ * call-seq: bit_count([offset, [length]]) -> integer
+ *
+ * Returns the number of set bits (1s) in the buffer, also known as the
+ * Hamming weight or population count. An optional +offset+ and +length+
+ * can be provided to count bits in a subrange of the buffer.
+ *
+ * IO::Buffer.for("\xFF\x00\x0F").bit_count
+ * # => 12
+ *
+ * IO::Buffer.for("\xFF\x00\x0F").bit_count(1, 2)
+ * # => 4
+ */
+static VALUE
+io_buffer_bit_count(int argc, VALUE *argv, VALUE self)
+{
+ rb_check_arity(argc, 0, 2);
+
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
+
+ io_buffer_validate_range(buffer, offset, length);
+
+ const void *base;
+ size_t size;
+ io_buffer_get_bytes_for_reading(buffer, &base, &size);
+
+ size_t count = memory_bit_count((const unsigned char *)base + offset, length);
+
+ return SIZET2NUM(count);
+}
+
/*
* Document-class: IO::Buffer
*
@@ -3571,34 +3859,32 @@ io_buffer_not_inplace(VALUE self)
*
* \Buffer from string:
*
- * string = 'buffer'
- * buffer = IO::Buffer.for(string)
- * # =>
- * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
- * # ...
- * buffer
- * # =>
- * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
- * # 0x00000000 64 61 74 61 buffer
- *
- * buffer.get_string(2) # read content starting from offset 2
- * # => "ta"
- * buffer.set_string('---', 1) # write content, starting from offset 1
- * # => 3
- * buffer
- * # =>
- * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
- * # 0x00000000 64 2d 2d 2d d---
- * string # original string changed, too
- * # => "d---"
+ * string = 'data'
+ * IO::Buffer.for(string) do |buffer|
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
+ * # 0x00000000 64 61 74 61 data
+ *
+ * buffer.get_string(2) # read content starting from offset 2
+ * # => "ta"
+ * buffer.set_string('---', 1) # write content, starting from offset 1
+ * # => 3
+ * buffer
+ * # =>
+ * # #<IO::Buffer 0x00007f3f02be9b18+4 SLICE>
+ * # 0x00000000 64 2d 2d 2d d---
+ * string # original string changed, too
+ * # => "d---"
+ * end
*
* \Buffer from file:
*
- * File.write('test.txt', 'test buffer')
+ * File.write('test.txt', 'test data')
* # => 9
- * buffer = IO::Buffer.map(File.open('test.txt'))
+ * buffer = IO::Buffer.map(File.open('test.txt'), nil, 0, IO::Buffer::READONLY)
* # =>
- * # #<IO::Buffer 0x00007f3f0768c000+9 MAPPED IMMUTABLE>
+ * # #<IO::Buffer 0x00007f3f0768c000+9 EXTERNAL MAPPED FILE SHARED READONLY>
* # ...
* buffer.get_string(5, 2) # read 2 bytes, starting from offset 5
* # => "da"
@@ -3611,7 +3897,7 @@ io_buffer_not_inplace(VALUE self)
* buffer.set_string('---', 1)
* # => 3 -- bytes written
* File.read('test.txt')
- * # => "t--- buffer"
+ * # => "t--- data"
*
* <b>The class is experimental and the interface is subject to change, this
* is especially true of file mappings which may be removed entirely in
@@ -3747,6 +4033,11 @@ Init_IO_Buffer(void)
IO_BUFFER_DEFINE_DATA_TYPE(s64);
IO_BUFFER_DEFINE_DATA_TYPE(S64);
+ IO_BUFFER_DEFINE_DATA_TYPE(u128);
+ IO_BUFFER_DEFINE_DATA_TYPE(U128);
+ IO_BUFFER_DEFINE_DATA_TYPE(s128);
+ IO_BUFFER_DEFINE_DATA_TYPE(S128);
+
IO_BUFFER_DEFINE_DATA_TYPE(f32);
IO_BUFFER_DEFINE_DATA_TYPE(F32);
IO_BUFFER_DEFINE_DATA_TYPE(f64);
@@ -3780,6 +4071,8 @@ Init_IO_Buffer(void)
rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
+ rb_define_method(rb_cIOBuffer, "bit_count", io_buffer_bit_count, -1);
+
// IO operations:
rb_define_method(rb_cIOBuffer, "read", io_buffer_read, -1);
rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, -1);