summaryrefslogtreecommitdiff
path: root/io_buffer.c
diff options
context:
space:
mode:
authorSamuel Williams <samuel.williams@oriontransfer.co.nz>2022-09-26 18:06:12 +1300
committerGitHub <noreply@github.com>2022-09-26 18:06:12 +1300
commit025b8701c09813c40339bd8fa1c75e0e5eaf7120 (patch)
tree95bb45a27aeb14c9e224baa6c5e6d0ef75a49697 /io_buffer.c
parentecffc6a203822a828d00afece9c9cd782ff60a66 (diff)
downloadruby-025b8701c09813c40339bd8fa1c75e0e5eaf7120.tar.gz
Add several new methods for getting and setting buffer contents. (#6434)
Diffstat (limited to 'io_buffer.c')
-rw-r--r--io_buffer.c509
1 files changed, 406 insertions, 103 deletions
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)
* # =>
* # #<IO::Buffer 0x000055b34497ea10+4 INTERNAL>
* # 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
* # #<IO::Buffer 0x000055769f41b1a0+4 INTERNAL>
- *
*/
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)
* # #<IO::Buffer 0x0000000000000000+0 NULL>
* 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
+ * # =>
+ * # #<IO::Buffer 0x696f717561746978+8 INTERNAL>
+ * # 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
* # =>
* # #<IO::Buffer 0x0000558cbec03320+11 INTERNAL>
* # 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)
* # =>
* # <IO::Buffer 0x00007fca40087c38+4 SLICE>
* # 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);