From 025b8701c09813c40339bd8fa1c75e0e5eaf7120 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 26 Sep 2022 18:06:12 +1300 Subject: Add several new methods for getting and setting buffer contents. (#6434) --- io_buffer.c | 509 ++++++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 406 insertions(+), 103 deletions(-) (limited to 'io_buffer.c') diff --git a/io_buffer.c b/io_buffer.c index 98f1da1fc1..8ec5dc984b 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -6,6 +6,7 @@ **********************************************************************/ +#include "internal/array.h" #include "ruby/io.h" #include "ruby/io/buffer.h" #include "ruby/fiber/scheduler.h" @@ -461,7 +462,6 @@ rb_io_buffer_map(VALUE io, size_t size, rb_off_t offset, enum rb_io_buffer_flags * * Note that some operating systems may not have cache coherency between mapped * buffers and file reads. - * */ static VALUE io_buffer_map(int argc, VALUE *argv, VALUE klass) @@ -543,7 +543,6 @@ io_flags_for_size(size_t size) * # => * # # * # 0x00000000 74 65 73 74 test - * */ VALUE rb_io_buffer_initialize(int argc, VALUE *argv, VALUE self) @@ -628,7 +627,6 @@ io_buffer_validate(struct rb_io_buffer *data) * * puts IO::Buffer.new(4) # uses to_s internally * # # - * */ VALUE rb_io_buffer_to_s(VALUE self) @@ -756,7 +754,6 @@ rb_io_buffer_inspect(VALUE self) * * Returns the size of the buffer that was explicitly set (on creation with ::new * or on #resize), or deduced on buffer's creation from string or file. - * */ VALUE rb_io_buffer_size(VALUE self) @@ -774,7 +771,6 @@ rb_io_buffer_size(VALUE self) * * A buffer becomes invalid if it is a slice of another buffer which has been * freed. - * */ static VALUE rb_io_buffer_valid_p(VALUE self) @@ -790,7 +786,6 @@ rb_io_buffer_valid_p(VALUE self) * * If the buffer was freed with #free or was never allocated in the first * place. - * */ static VALUE rb_io_buffer_null_p(VALUE self) @@ -807,7 +802,6 @@ rb_io_buffer_null_p(VALUE self) * If the buffer has 0 size: it is created by ::new with size 0, or with ::for * from an empty string. (Note that empty files can't be mapped, so the buffer * created with ::map will never be empty.) - * */ static VALUE rb_io_buffer_empty_p(VALUE self) @@ -828,7 +822,6 @@ rb_io_buffer_empty_p(VALUE self) * memory. * * External buffer can't be resized. - * */ static VALUE rb_io_buffer_external_p(VALUE self) @@ -854,7 +847,6 @@ rb_io_buffer_external_p(VALUE self) * * Internal buffers can be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_internal_p(VALUE self) @@ -877,7 +869,6 @@ rb_io_buffer_internal_p(VALUE self) * * Mapped buffers can usually be resized, and such an operation will typically * invalidate all slices, but not always. - * */ static VALUE rb_io_buffer_mapped_p(VALUE self) @@ -901,7 +892,6 @@ rb_io_buffer_mapped_p(VALUE self) * buffer.locked do * buffer.write(io) # theoretical system call interface * end - * */ static VALUE rb_io_buffer_locked_p(VALUE self) @@ -928,7 +918,6 @@ rb_io_buffer_readonly_p(VALUE self) * #set_value, #set_string or #copy and similar. * * Frozen strings and read-only files create read-only buffers. - * */ static VALUE io_buffer_readonly_p(VALUE self) @@ -1053,7 +1042,6 @@ rb_io_buffer_locked(VALUE self) * # => true * * You can resize a freed buffer to re-allocate it. - * */ VALUE rb_io_buffer_free(VALUE self) @@ -1115,7 +1103,6 @@ io_buffer_validate_range(struct rb_io_buffer *data, size_t offset, size_t length * # ...and original string * string * # => tost - * */ VALUE rb_io_buffer_slice(VALUE self, VALUE _offset, VALUE _length) @@ -1166,7 +1153,7 @@ rb_io_buffer_get_bytes(VALUE self, void **base, size_t *size) return 0; } -static void +inline static void io_buffer_get_bytes_for_writing(struct rb_io_buffer *data, void **base, size_t *size) { if (data->flags & RB_IO_BUFFER_READONLY) { @@ -1238,7 +1225,6 @@ rb_io_buffer_get_bytes_for_reading(VALUE self, const void **base, size_t *size) * # # * buffer.null? * # => true - * */ VALUE rb_io_buffer_transfer(VALUE self) @@ -1358,7 +1344,6 @@ rb_io_buffer_resize(VALUE self, size_t size) * * External buffer (created with ::for), and locked buffer * can not be resized. - * */ static VALUE io_buffer_resize(VALUE self, VALUE size) @@ -1373,7 +1358,6 @@ io_buffer_resize(VALUE self, VALUE size) * * Buffers are compared by size and exact contents of the memory they are * referencing using +memcmp+. - * */ static VALUE rb_io_buffer_compare(VALUE self, VALUE other) @@ -1399,7 +1383,7 @@ static void io_buffer_validate_type(size_t size, size_t offset) { if (offset > size) { - rb_raise(rb_eArgError, "Type extends beyond end of buffer!"); + rb_raise(rb_eArgError, "Type extends beyond end of buffer! (offset=%ld > size=%ld)", offset, size); } } @@ -1449,8 +1433,8 @@ ruby_swapf64(double value) return swap.value; } -#define DECLARE_TYPE(name, type, endian, wrap, unwrap, swap) \ -static ID RB_IO_BUFFER_TYPE_##name; \ +#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) \ @@ -1471,67 +1455,123 @@ io_buffer_write_##name(const void* base, size_t size, size_t *offset, VALUE _val if (endian != RB_IO_BUFFER_HOST_ENDIAN) value = swap(value); \ memcpy((char*)base + *offset, &value, sizeof(type)); \ *offset += sizeof(type); \ -} - -DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) -DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) - -DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) -DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) -DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +} \ +\ +enum { \ + RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = sizeof(type) \ +}; -DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) -DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) -DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U8, uint8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap8) +IO_BUFFER_DECLARE_TYPE(S8, int8_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap8) + +IO_BUFFER_DECLARE_TYPE(u16, uint16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(U16, uint16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(s16, int16_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) +IO_BUFFER_DECLARE_TYPE(S16, int16_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap16) + +IO_BUFFER_DECLARE_TYPE(u32, uint32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(U32, uint32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_UINT2NUM, RB_NUM2UINT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(s32, int32_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) +IO_BUFFER_DECLARE_TYPE(S32, int32_t, RB_IO_BUFFER_BIG_ENDIAN, RB_INT2NUM, RB_NUM2INT, ruby_swap32) + +IO_BUFFER_DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +IO_BUFFER_DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) +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(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) +IO_BUFFER_DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) +#undef IO_BUFFER_DECLARE_TYPE + +static inline size_t +io_buffer_data_type_size(ID data_type) { +#define IO_BUFFER_DATA_TYPE_SIZE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return RB_IO_BUFFER_DATA_TYPE_##name##_SIZE; + IO_BUFFER_DATA_TYPE_SIZE(U8) + IO_BUFFER_DATA_TYPE_SIZE(S8) + IO_BUFFER_DATA_TYPE_SIZE(u16) + IO_BUFFER_DATA_TYPE_SIZE(U16) + IO_BUFFER_DATA_TYPE_SIZE(s16) + IO_BUFFER_DATA_TYPE_SIZE(S16) + IO_BUFFER_DATA_TYPE_SIZE(u32) + IO_BUFFER_DATA_TYPE_SIZE(U32) + IO_BUFFER_DATA_TYPE_SIZE(s32) + IO_BUFFER_DATA_TYPE_SIZE(S32) + IO_BUFFER_DATA_TYPE_SIZE(u64) + IO_BUFFER_DATA_TYPE_SIZE(U64) + IO_BUFFER_DATA_TYPE_SIZE(s64) + IO_BUFFER_DATA_TYPE_SIZE(S64) + IO_BUFFER_DATA_TYPE_SIZE(f32) + IO_BUFFER_DATA_TYPE_SIZE(F32) + IO_BUFFER_DATA_TYPE_SIZE(f64) + IO_BUFFER_DATA_TYPE_SIZE(F64) +#undef IO_BUFFER_DATA_TYPE_SIZE -DECLARE_TYPE(u64, uint64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(U64, uint64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_ULL2NUM, RB_NUM2ULL, ruby_swap64) -DECLARE_TYPE(s64, int64_t, RB_IO_BUFFER_LITTLE_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) -DECLARE_TYPE(S64, int64_t, RB_IO_BUFFER_BIG_ENDIAN, RB_LL2NUM, RB_NUM2LL, ruby_swap64) + rb_raise(rb_eArgError, "Invalid type name!"); +} -DECLARE_TYPE(f32, float, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(F32, float, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf32) -DECLARE_TYPE(f64, double, RB_IO_BUFFER_LITTLE_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -DECLARE_TYPE(F64, double, RB_IO_BUFFER_BIG_ENDIAN, DBL2NUM, NUM2DBL, ruby_swapf64) -#undef DECLARE_TYPE +/* + * call-seq: + * size_of(data_type) -> byte size + * size_of(array of data_type) -> byte size + * + * Returns the size of the given data type(s) in bytes. + * + * Example: + * + * IO::Buffer.size_of(:u32) # => 4 + * IO::Buffer.size_of([:u32, :u32]) # => 8 + */ +static VALUE +io_buffer_size_of(VALUE klass, VALUE data_type) +{ + if (RB_TYPE_P(data_type, T_ARRAY)) { + size_t total = 0; + for (long i = 0; i < RARRAY_LEN(data_type); i++) { + total += io_buffer_data_type_size(RB_SYM2ID(RARRAY_AREF(data_type, i))); + } + return SIZET2NUM(total); + } else { + return SIZET2NUM(io_buffer_data_type_size(RB_SYM2ID(data_type))); + } +} -VALUE -rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) +static inline VALUE +rb_io_buffer_get_value(const void* base, size_t size, ID data_type, size_t *offset) { -#define READ_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) return io_buffer_read_##name(base, size, &offset); - READ_TYPE(U8); - READ_TYPE(S8); +#define IO_BUFFER_GET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) return io_buffer_read_##name(base, size, offset); + IO_BUFFER_GET_VALUE(U8) + IO_BUFFER_GET_VALUE(S8) - READ_TYPE(u16); - READ_TYPE(U16); - READ_TYPE(s16); - READ_TYPE(S16); + IO_BUFFER_GET_VALUE(u16) + IO_BUFFER_GET_VALUE(U16) + IO_BUFFER_GET_VALUE(s16) + IO_BUFFER_GET_VALUE(S16) - READ_TYPE(u32); - READ_TYPE(U32); - READ_TYPE(s32); - READ_TYPE(S32); + IO_BUFFER_GET_VALUE(u32) + IO_BUFFER_GET_VALUE(U32) + IO_BUFFER_GET_VALUE(s32) + IO_BUFFER_GET_VALUE(S32) - READ_TYPE(u64); - READ_TYPE(U64); - READ_TYPE(s64); - READ_TYPE(S64); + IO_BUFFER_GET_VALUE(u64) + IO_BUFFER_GET_VALUE(U64) + IO_BUFFER_GET_VALUE(s64) + IO_BUFFER_GET_VALUE(S64) - READ_TYPE(f32); - READ_TYPE(F32); - READ_TYPE(f64); - READ_TYPE(F64); -#undef READ_TYPE + IO_BUFFER_GET_VALUE(f32) + IO_BUFFER_GET_VALUE(F32) + IO_BUFFER_GET_VALUE(f64) + IO_BUFFER_GET_VALUE(F64) +#undef IO_BUFFER_GET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } /* - * call-seq: get_value(type, offset) -> numeric + * call-seq: get_value(data_type, offset) -> numeric * - * Read from buffer a value of +type+ at +offset+. +type+ should be one + * Read from buffer a value of +type+ at +offset+. +data_type+ should be one * of symbols: * * * +:U8+: unsigned integer, 1 byte @@ -1553,13 +1593,16 @@ rb_io_buffer_get_value(const void* base, size_t size, ID type, size_t offset) * * +:f64+: double, 8 bytes, little-endian * * +:F64+: double, 8 bytes, big-endian * + * A data type refers specifically to the type of binary data that is stored + * in the buffer. For example, a +:u32+ data type is a 32-bit unsigned + * integer in little-endian format. + * * Example: * * string = [1.5].pack('f') * # => "\x00\x00\xC0?" * IO::Buffer.for(string).get_value(:f32, 0) * # => 1.5 - * */ static VALUE io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) @@ -1570,36 +1613,230 @@ io_buffer_get_value(VALUE self, VALUE type, VALUE _offset) rb_io_buffer_get_bytes_for_reading(self, &base, &size); - return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), offset); + return rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); } -void -rb_io_buffer_set_value(const void* base, size_t size, ID type, size_t offset, VALUE value) +/* + * call-seq: get_values(data_types, offset) -> array + * + * Similar to #get_value, except that it can handle multiple data types and + * returns an array of values. + * + * Example: + * + * string = [1.5, 2.5].pack('ff') + * IO::Buffer.for(string).get_values([:f32, :f32], 0) + * # => [1.5, 2.5] + */ +static VALUE +io_buffer_get_values(VALUE self, VALUE data_types, VALUE _offset) +{ + size_t offset = NUM2SIZET(_offset); + + const void *base; + size_t size; + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + VALUE array = rb_ary_new_capa(RARRAY_LEN(data_types)); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_io_buffer_get_value(base, size, RB_SYM2ID(type), &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each(data_type, [offset, [count]]) {|offset, value| ...} -> self + * each(data_type, [offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each +value+ of +data_type+ starting + * from +offset+. + * + * If +count+ is given, only +count+ values will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each(:U8, 2, 2) do |offset, value| + * puts "#{offset}: #{value}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each(int argc, VALUE *argv, VALUE self) +{ + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_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_data_type_size(data_type); + } + + for (size_t i = 0; i < count; i++) { + size_t current_offset = offset; + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_yield_values(2, SIZET2NUM(current_offset), value); + } + + return self; +} + +/* + * call-seq: values(data_type, [offset, [count]]) -> array + * + * Returns an array of values of +data_type+ starting from +offset+. + * + * If +count+ is given, only +count+ values will be returned. + * + * Example: + * + * IO::Buffer.for("Hello World").values(:U8, 2, 2) + * # => [108, 108] + */ +static VALUE +io_buffer_values(int argc, VALUE *argv, VALUE self) +{ + const void *base; + size_t size; + + rb_io_buffer_get_bytes_for_reading(self, &base, &size); + + ID data_type; + if (argc >= 1) { + data_type = RB_SYM2ID(argv[0]); + } else { + data_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_data_type_size(data_type); + } + + VALUE array = rb_ary_new_capa(count); + + for (size_t i = 0; i < count; i++) { + VALUE value = rb_io_buffer_get_value(base, size, data_type, &offset); + rb_ary_push(array, value); + } + + return array; +} + +/* + * call-seq: + * each_byte([offset, [count]]) {|offset, byte| ...} -> self + * each_byte([offset, [count]]) -> enumerator + * + * Iterates over the buffer, yielding each byte starting from +offset+. + * + * If +count+ is given, only +count+ bytes will be yielded. + * + * Example: + * + * IO::Buffer.for("Hello World").each_byte(2, 2) do |offset, byte| + * puts "#{offset}: #{byte}" + * end + * # 2: 108 + * # 3: 108 + */ +static VALUE +io_buffer_each_byte(int argc, VALUE *argv, VALUE self) +{ + RETURN_ENUMERATOR_KW(self, argc, argv, RB_NO_KEYWORDS); + + const void *base; + size_t size; + + 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); + } + + for (size_t i = 0; i < count; i++) { + unsigned char *value = (unsigned char *)base + i + offset; + rb_yield(RB_INT2FIX(*value)); + } + + return self; +} + +inline static void +rb_io_buffer_set_value(const void* base, size_t size, ID data_type, size_t *offset, VALUE value) { -#define WRITE_TYPE(name) if (type == RB_IO_BUFFER_TYPE_##name) {io_buffer_write_##name(base, size, &offset, value); return;} - WRITE_TYPE(U8); - WRITE_TYPE(S8); +#define IO_BUFFER_SET_VALUE(name) if (data_type == RB_IO_BUFFER_DATA_TYPE_##name) {io_buffer_write_##name(base, size, offset, value); return;} + IO_BUFFER_SET_VALUE(U8); + IO_BUFFER_SET_VALUE(S8); - WRITE_TYPE(u16); - WRITE_TYPE(U16); - WRITE_TYPE(s16); - WRITE_TYPE(S16); + IO_BUFFER_SET_VALUE(u16); + IO_BUFFER_SET_VALUE(U16); + IO_BUFFER_SET_VALUE(s16); + IO_BUFFER_SET_VALUE(S16); - WRITE_TYPE(u32); - WRITE_TYPE(U32); - WRITE_TYPE(s32); - WRITE_TYPE(S32); + IO_BUFFER_SET_VALUE(u32); + IO_BUFFER_SET_VALUE(U32); + IO_BUFFER_SET_VALUE(s32); + IO_BUFFER_SET_VALUE(S32); - WRITE_TYPE(u64); - WRITE_TYPE(U64); - WRITE_TYPE(s64); - WRITE_TYPE(S64); + IO_BUFFER_SET_VALUE(u64); + IO_BUFFER_SET_VALUE(U64); + IO_BUFFER_SET_VALUE(s64); + IO_BUFFER_SET_VALUE(S64); - WRITE_TYPE(f32); - WRITE_TYPE(F32); - WRITE_TYPE(f64); - WRITE_TYPE(F64); -#undef WRITE_TYPE + IO_BUFFER_SET_VALUE(f32); + IO_BUFFER_SET_VALUE(F32); + IO_BUFFER_SET_VALUE(f64); + IO_BUFFER_SET_VALUE(F64); +#undef IO_BUFFER_SET_VALUE rb_raise(rb_eArgError, "Invalid type name!"); } @@ -1640,7 +1877,53 @@ io_buffer_set_value(VALUE self, VALUE type, VALUE _offset, VALUE value) rb_io_buffer_get_bytes_for_writing(self, &base, &size); - rb_io_buffer_set_value(base, size, RB_SYM2ID(type), offset, value); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + + return SIZET2NUM(offset); +} + +/* + * call-seq: set_values(data_types, offset, values) -> offset + * + * Write +values+ of +data_types+ at +offset+ to the buffer. +data_types+ + * should be an array of symbols as described in #get_value. +values+ should + * be an array of values to write. + * + * Example: + * + * buffer = IO::Buffer.new(8) + * buffer.set_values([:U8, :U16], 0, [1, 2]) + * buffer + * # => + * # # + * # 0x00000000 01 00 02 00 00 00 00 00 ........ + */ +static VALUE +io_buffer_set_values(VALUE self, VALUE data_types, VALUE _offset, VALUE values) +{ + if (!RB_TYPE_P(data_types, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument data_types should be an array!"); + } + + if (!RB_TYPE_P(values, T_ARRAY)) { + rb_raise(rb_eArgError, "Argument values should be an array!"); + } + + if (RARRAY_LEN(data_types) != RARRAY_LEN(values)) { + rb_raise(rb_eArgError, "Argument data_types and values should have the same length!"); + } + + size_t offset = NUM2SIZET(_offset); + + void *base; + size_t size; + rb_io_buffer_get_bytes_for_writing(self, &base, &size); + + for (long i = 0; i < RARRAY_LEN(data_types); i++) { + VALUE type = rb_ary_entry(data_types, i); + VALUE value = rb_ary_entry(values, i); + rb_io_buffer_set_value(base, size, RB_SYM2ID(type), &offset, value); + } return SIZET2NUM(offset); } @@ -1719,7 +2002,6 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s * # => * # # * # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World - * */ static VALUE rb_io_buffer_initialize_copy(VALUE self, VALUE source) @@ -1790,7 +2072,6 @@ 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) - * */ static VALUE io_buffer_copy(int argc, VALUE *argv, VALUE self) @@ -1822,7 +2103,6 @@ io_buffer_copy(int argc, VALUE *argv, VALUE self) * # => "st" * buffer.get_string(2, 1) * # => "s" - * */ static VALUE io_buffer_get_string(int argc, VALUE *argv, VALUE self) @@ -1944,7 +2224,6 @@ rb_io_buffer_clear(VALUE self, uint8_t value, size_t offset, size_t length) * # => * # * # 0x00000000 01 02 02 02 .... - * */ static VALUE io_buffer_clear(int argc, VALUE *argv, VALUE self) @@ -2685,17 +2964,41 @@ Init_IO_Buffer(void) rb_include_module(rb_cIOBuffer, rb_mComparable); -#define DEFINE_TYPE(name) RB_IO_BUFFER_TYPE_##name = rb_intern_const(#name) - DEFINE_TYPE(U8); DEFINE_TYPE(S8); - DEFINE_TYPE(u16); DEFINE_TYPE(U16); DEFINE_TYPE(s16); DEFINE_TYPE(S16); - DEFINE_TYPE(u32); DEFINE_TYPE(U32); DEFINE_TYPE(s32); DEFINE_TYPE(S32); - DEFINE_TYPE(u64); DEFINE_TYPE(U64); DEFINE_TYPE(s64); DEFINE_TYPE(S64); - DEFINE_TYPE(f32); DEFINE_TYPE(F32); DEFINE_TYPE(f64); DEFINE_TYPE(F64); -#undef DEFINE_TYPE +#define IO_BUFFER_DEFINE_DATA_TYPE(name) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name) + IO_BUFFER_DEFINE_DATA_TYPE(U8); + IO_BUFFER_DEFINE_DATA_TYPE(S8); + + IO_BUFFER_DEFINE_DATA_TYPE(u16); + IO_BUFFER_DEFINE_DATA_TYPE(U16); + IO_BUFFER_DEFINE_DATA_TYPE(s16); + IO_BUFFER_DEFINE_DATA_TYPE(S16); + + IO_BUFFER_DEFINE_DATA_TYPE(u32); + IO_BUFFER_DEFINE_DATA_TYPE(U32); + IO_BUFFER_DEFINE_DATA_TYPE(s32); + IO_BUFFER_DEFINE_DATA_TYPE(S32); + + IO_BUFFER_DEFINE_DATA_TYPE(u64); + IO_BUFFER_DEFINE_DATA_TYPE(U64); + IO_BUFFER_DEFINE_DATA_TYPE(s64); + IO_BUFFER_DEFINE_DATA_TYPE(S64); + + IO_BUFFER_DEFINE_DATA_TYPE(f32); + IO_BUFFER_DEFINE_DATA_TYPE(F32); + IO_BUFFER_DEFINE_DATA_TYPE(f64); + IO_BUFFER_DEFINE_DATA_TYPE(F64); +#undef IO_BUFFER_DEFINE_DATA_TYPE + + rb_define_singleton_method(rb_cIOBuffer, "size_of", io_buffer_size_of, 1); // Data access: rb_define_method(rb_cIOBuffer, "get_value", io_buffer_get_value, 2); + rb_define_method(rb_cIOBuffer, "get_values", io_buffer_get_values, 2); + rb_define_method(rb_cIOBuffer, "each", io_buffer_each, -1); + rb_define_method(rb_cIOBuffer, "values", io_buffer_values, -1); + rb_define_method(rb_cIOBuffer, "each_byte", io_buffer_each_byte, -1); rb_define_method(rb_cIOBuffer, "set_value", io_buffer_set_value, 3); + rb_define_method(rb_cIOBuffer, "set_values", io_buffer_set_values, 3); rb_define_method(rb_cIOBuffer, "copy", io_buffer_copy, -1); -- cgit v1.2.3