summaryrefslogtreecommitdiff
path: root/io_buffer.c
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2023-09-14 20:37:43 +1200
committerGitHub <noreply@github.com>2023-09-14 20:37:43 +1200
commita095740fed2a05a04806a1d3827bcaba02e45720 (patch)
treead2b2e3ca8d24a876f594ff085d4303d983742c0 /io_buffer.c
parent998ae7c3f36b65f04471cd680b4c7ac78b3d9f30 (diff)
Fix `io_buffer_get_string` default length computation. (#8427)
* Fix `io_buffer_get_string` default length computation. When an offset bigger than the size is given, the resulting length will be computed incorrectly. Raise an argument error in this case. * Validate all arguments.
Notes
Notes: Merged-By: ioquatix <samuel@codeotaku.com>
Diffstat (limited to 'io_buffer.c')
-rw-r--r--io_buffer.c341
1 files changed, 181 insertions, 160 deletions
diff --git a/io_buffer.c b/io_buffer.c
index 6b5d5ee714..942ab268ae 100644
--- a/io_buffer.c
+++ b/io_buffer.c
@@ -289,6 +289,111 @@ static const rb_data_type_t rb_io_buffer_type = {
.flags = RUBY_TYPED_FREE_IMMEDIATELY,
};
+// Extract an offset argument, which must be a positive integer.
+static inline size_t
+io_buffer_extract_offset(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Offset can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a length argument, which must be a positive integer.
+// Length is generally considered a mutable property of an object and
+// semantically should be considered a subset of "size" as a concept.
+static inline size_t
+io_buffer_extract_length(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Length can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Extract a size argument, which must be a positive integer.
+// Size is generally considered an immutable property of an object.
+static inline size_t
+io_buffer_extract_size(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Size can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+// Compute the default length for a buffer, given an offset into that buffer.
+// The default length is the size of the buffer minus the offset. The offset
+// must be less than the size of the buffer otherwise the length will be
+// invalid; in that case, an ArgumentError exception will be raised.
+static inline size_t
+io_buffer_default_length(const struct rb_io_buffer *buffer, size_t offset)
+{
+ if (offset > buffer->size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ // Note that the "length" is computed by the size the offset.
+ return buffer->size - offset;
+}
+
+// Extract the optional length and offset arguments, returning the buffer.
+// The length and offset are optional, but if they are provided, they must be
+// positive integers. If the length is not provided, the default length is
+// computed from the buffer size and offset. If the offset is not provided, it
+// defaults to zero.
+static inline struct rb_io_buffer *
+io_buffer_extract_length_offset(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 2) {
+ *offset = io_buffer_extract_offset(argv[1]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 1 && !NIL_P(argv[0])) {
+ *length = io_buffer_extract_length(argv[0]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
+// Extract the optional offset and length arguments, returning the buffer.
+// Similar to `io_buffer_extract_length_offset` but with the order of
+// arguments reversed.
+static inline struct rb_io_buffer *
+io_buffer_extract_offset_length(VALUE self, int argc, VALUE argv[], size_t *offset, size_t *length)
+{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *length = io_buffer_extract_length(argv[1]);
+ }
+ else {
+ *length = io_buffer_default_length(buffer, *offset);
+ }
+
+ return buffer;
+}
+
VALUE
rb_io_buffer_type_allocate(VALUE self)
{
@@ -521,7 +626,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
size_t size;
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- size = RB_NUM2SIZE(argv[1]);
+ size = io_buffer_extract_size(argv[1]);
}
else {
rb_off_t file_size = rb_file_size(io);
@@ -540,6 +645,7 @@ io_buffer_map(int argc, VALUE *argv, VALUE klass)
}
}
+ // This is the file offset, not the buffer offset:
rb_off_t offset = 0;
if (argc >= 3) {
offset = NUM2OFFT(argv[2]);
@@ -601,9 +707,8 @@ rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self)
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
size_t size;
-
if (argc > 0) {
- size = RB_NUM2SIZE(argv[0]);
+ size = io_buffer_extract_size(argv[0]);
}
else {
size = RUBY_IO_BUFFER_DEFAULT_SIZE;
@@ -1156,11 +1261,9 @@ VALUE rb_io_buffer_free_locked(VALUE self)
static inline void
io_buffer_validate_range(struct rb_io_buffer *buffer, size_t offset, size_t length)
{
- if (offset > buffer->size) {
- rb_raise(rb_eArgError, "Specified offset exceeds buffer size!");
- }
+ // We assume here that offset + length won't overflow:
if (offset + length > buffer->size) {
- rb_raise(rb_eArgError, "Specified offset+length exceeds buffer size!");
+ rb_raise(rb_eArgError, "Specified offset+length is bigger than the buffer size!");
}
}
@@ -1186,7 +1289,7 @@ rb_io_buffer_slice(struct rb_io_buffer *buffer, VALUE self, size_t offset, size_
}
/*
- * call-seq: slice([offset = 0, [length]]) -> io_buffer
+ * call-seq: slice([offset, [length]]) -> io_buffer
*
* Produce another IO::Buffer which is a slice (or view into) the current one
* starting at +offset+ bytes and going for +length+ bytes.
@@ -1246,29 +1349,8 @@ io_buffer_slice(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 2);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- size_t offset = 0, length = 0;
-
- if (argc > 0) {
- if (rb_int_negative_p(argv[0])) {
- rb_raise(rb_eArgError, "Offset can't be negative!");
- }
-
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc > 1) {
- if (rb_int_negative_p(argv[1])) {
- rb_raise(rb_eArgError, "Length can't be negative!");
- }
-
- length = NUM2SIZET(argv[1]);
- }
- else {
- length = buffer->size - offset;
- }
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
return rb_io_buffer_slice(buffer, self, offset, length);
}
@@ -1496,7 +1578,7 @@ rb_io_buffer_resize(VALUE self, size_t size)
static VALUE
io_buffer_resize(VALUE self, VALUE size)
{
- rb_io_buffer_resize(self, NUM2SIZET(size));
+ rb_io_buffer_resize(self, io_buffer_extract_size(size));
return self;
}
@@ -1759,7 +1841,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
{
const void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
@@ -1781,7 +1863,7 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset)
static VALUE
io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
{
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
const void *base;
size_t size;
@@ -1802,6 +1884,40 @@ io_buffer_get_values(VALUE self, VALUE buffer_types, VALUE _offset)
return array;
}
+// Extract a count argument, which must be a positive integer.
+// Count is generally considered relative to the number of things.
+static inline size_t
+io_buffer_extract_count(VALUE argument)
+{
+ if (rb_int_negative_p(argument)) {
+ rb_raise(rb_eArgError, "Count can't be negative!");
+ }
+
+ return NUM2SIZET(argument);
+}
+
+static inline void
+io_buffer_extract_offset_count(ID buffer_type, size_t size, int argc, VALUE *argv, size_t *offset, size_t *count)
+{
+ if (argc >= 1) {
+ *offset = io_buffer_extract_offset(argv[0]);
+ }
+ else {
+ *offset = 0;
+ }
+
+ if (argc >= 2) {
+ *count = io_buffer_extract_count(argv[1]);
+ }
+ else {
+ if (*offset > size) {
+ rb_raise(rb_eArgError, "The given offset is bigger than the buffer size!");
+ }
+
+ *count = (size - *offset) / io_buffer_buffer_type_size(buffer_type);
+ }
+}
+
/*
* call-seq:
* each(buffer_type, [offset, [count]]) {|offset, value| ...} -> self
@@ -1838,21 +1954,8 @@ io_buffer_each(int argc, VALUE *argv, VALUE self)
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
}
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset) / io_buffer_buffer_type_size(buffer_type);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
for (size_t i = 0; i < count; i++) {
size_t current_offset = offset;
@@ -1891,21 +1994,8 @@ io_buffer_values(int argc, VALUE *argv, VALUE self)
buffer_type = RB_IO_BUFFER_DATA_TYPE_U8;
}
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset) / io_buffer_buffer_type_size(buffer_type);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(buffer_type, size, argc-1, argv+1, &offset, &count);
VALUE array = rb_ary_new_capa(count);
@@ -1944,21 +2034,8 @@ io_buffer_each_byte(int argc, VALUE *argv, VALUE self)
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
- size_t offset;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
- else {
- offset = 0;
- }
-
- size_t count;
- if (argc >= 3) {
- count = NUM2SIZET(argv[2]);
- }
- else {
- count = (size - offset);
- }
+ size_t offset, count;
+ io_buffer_extract_offset_count(RB_IO_BUFFER_DATA_TYPE_U8, size, argc-1, argv+1, &offset, &count);
for (size_t i = 0; i < count; i++) {
unsigned char *value = (unsigned char *)base + i + offset;
@@ -2034,7 +2111,7 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value)
{
void *base;
size_t size;
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
rb_io_buffer_get_bytes_for_writing(self, &base, &size);
@@ -2074,7 +2151,7 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values
rb_raise(rb_eArgError, "Argument buffer_types and values should have the same length!");
}
- size_t offset = NUM2SIZET(_offset);
+ size_t offset = io_buffer_extract_offset(_offset);
void *base;
size_t size;
@@ -2099,7 +2176,7 @@ 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) {
- rb_raise(rb_eArgError, "The computed source range exceeds the size of the source!");
+ 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);
@@ -2109,21 +2186,18 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_
static VALUE
io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t source_size, int argc, VALUE *argv)
{
- size_t offset;
+ size_t offset = 0;
size_t length;
size_t source_offset;
// The offset we copy into the buffer:
if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
- else {
- offset = 0;
+ offset = io_buffer_extract_offset(argv[0]);
}
// The offset we start from within the string:
if (argc >= 3) {
- source_offset = NUM2SIZET(argv[2]);
+ source_offset = io_buffer_extract_offset(argv[2]);
if (source_offset > source_size) {
rb_raise(rb_eArgError, "The given source offset is bigger than the source itself!");
@@ -2135,7 +2209,7 @@ io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t
// The length we are going to copy:
if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
+ length = io_buffer_extract_length(argv[1]);
}
else {
// Default to the source offset -> source size:
@@ -2232,7 +2306,7 @@ 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 exceeds source size! (ArgumentError)
+ * # in `copy': Specified offset+length is bigger than the buffer size! (ArgumentError)
*/
static VALUE
io_buffer_copy(int argc, VALUE *argv, VALUE self)
@@ -2270,31 +2344,20 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+ size_t offset, length;
+ struct rb_io_buffer *buffer = io_buffer_extract_offset_length(self, argc, argv, &offset, &length);
const void *base;
size_t size;
io_buffer_get_bytes_for_reading(buffer, &base, &size);
- size_t offset = 0;
- size_t length = size;
- rb_encoding *encoding = rb_ascii8bit_encoding();
-
- if (argc >= 1) {
- offset = NUM2SIZET(argv[0]);
- }
-
- if (argc >= 2 && !RB_NIL_P(argv[1])) {
- length = NUM2SIZET(argv[1]);
- }
- else {
- length = size - offset;
- }
-
+ rb_encoding *encoding;
if (argc >= 3) {
encoding = rb_find_encoding(argv[2]);
}
+ else {
+ encoding = rb_ascii8bit_encoding();
+ }
io_buffer_validate_range(buffer, offset, length);
@@ -2343,14 +2406,14 @@ io_buffer_set_string(int argc, VALUE *argv, VALUE self)
void
rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length)
{
+ struct rb_io_buffer *buffer = NULL;
+ TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
+
void *base;
size_t size;
+ io_buffer_get_bytes_for_writing(buffer, &base, &size);
- rb_io_buffer_get_bytes_for_writing(self, &base, &size);
-
- if (offset + length > size) {
- rb_raise(rb_eArgError, "The given offset + length out of bounds!");
- }
+ io_buffer_validate_range(buffer, offset, length);
memset((char*)base + offset, value, length);
}
@@ -2391,26 +2454,13 @@ io_buffer_clear(int argc, VALUE *argv, VALUE self)
{
rb_check_arity(argc, 0, 3);
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
uint8_t value = 0;
if (argc >= 1) {
value = NUM2UINT(argv[0]);
}
- size_t offset = 0;
- if (argc >= 2) {
- offset = NUM2SIZET(argv[1]);
- }
-
- size_t length;
- if (argc >= 3) {
- length = NUM2SIZET(argv[2]);
- }
- else {
- length = buffer->size - offset;
- }
+ size_t offset, length;
+ io_buffer_extract_offset_length(self, argc-1, argv+1, &offset, &length);
rb_io_buffer_clear(self, value, offset, length);
@@ -2489,35 +2539,6 @@ io_buffer_blocking_region(struct rb_io_buffer *buffer, rb_blocking_function_t *f
}
}
-static inline struct rb_io_buffer *
-io_buffer_extract_arguments(VALUE self, int argc, VALUE argv[], size_t *length, size_t *offset)
-{
- struct rb_io_buffer *buffer = NULL;
- TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, buffer);
-
- *offset = 0;
- if (argc >= 2) {
- if (rb_int_negative_p(argv[1])) {
- rb_raise(rb_eArgError, "Offset can't be negative!");
- }
-
- *offset = NUM2SIZET(argv[1]);
- }
-
- if (argc >= 1 && !NIL_P(argv[0])) {
- if (rb_int_negative_p(argv[0])) {
- rb_raise(rb_eArgError, "Length can't be negative!");
- }
-
- *length = NUM2SIZET(argv[0]);
- }
- else {
- *length = buffer->size - *offset;
- }
-
- return buffer;
-}
-
struct io_buffer_read_internal_argument {
// The file descriptor to read from:
int descriptor;
@@ -2629,7 +2650,7 @@ io_buffer_read(int argc, VALUE *argv, VALUE self)
VALUE io = argv[0];
size_t length, offset;
- io_buffer_extract_arguments(self, argc-1, argv+1, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
return rb_io_buffer_read(self, io, length, offset);
}
@@ -2754,7 +2775,7 @@ io_buffer_pread(int argc, VALUE *argv, VALUE self)
rb_off_t from = NUM2OFFT(argv[1]);
size_t length, offset;
- io_buffer_extract_arguments(self, argc-2, argv+2, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
return rb_io_buffer_pread(self, io, from, length, offset);
}
@@ -2863,7 +2884,7 @@ io_buffer_write(int argc, VALUE *argv, VALUE self)
VALUE io = argv[0];
size_t length, offset;
- io_buffer_extract_arguments(self, argc-1, argv+1, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-1, argv+1, &length, &offset);
return rb_io_buffer_write(self, io, length, offset);
}
@@ -2989,7 +3010,7 @@ io_buffer_pwrite(int argc, VALUE *argv, VALUE self)
rb_off_t from = NUM2OFFT(argv[1]);
size_t length, offset;
- io_buffer_extract_arguments(self, argc-2, argv+2, &length, &offset);
+ io_buffer_extract_length_offset(self, argc-2, argv+2, &length, &offset);
return rb_io_buffer_pwrite(self, io, from, length, offset);
}