mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add several new methods for getting and setting buffer contents. (#6434)
This commit is contained in:
parent
ecffc6a203
commit
025b8701c0
Notes:
git
2022-09-26 14:06:43 +09:00
Merged-By: ioquatix <samuel@codeotaku.com>
5 changed files with 503 additions and 115 deletions
27
benchmark/buffer_each.yml
Normal file
27
benchmark/buffer_each.yml
Normal file
|
@ -0,0 +1,27 @@
|
|||
prelude: |
|
||||
# frozen_string_literal: true
|
||||
Warning[:experimental] = false
|
||||
string = "The quick brown fox jumped over the lazy dog."
|
||||
array = string.bytes
|
||||
buffer = IO::Buffer.for(string)
|
||||
benchmark:
|
||||
string.each_byte: |
|
||||
upcased = String.new
|
||||
string.each_byte do |byte|
|
||||
upcased << (byte ^ 32)
|
||||
end
|
||||
array.each: |
|
||||
upcased = String.new
|
||||
array.each do |byte|
|
||||
upcased << (byte ^ 32)
|
||||
end
|
||||
buffer.each: |
|
||||
upcased = String.new
|
||||
buffer.each(:U8) do |offset, byte|
|
||||
upcased << (byte ^ 32)
|
||||
end
|
||||
buffer.each_byte: |
|
||||
upcased = String.new
|
||||
buffer.each_byte do |byte|
|
||||
upcased << (byte ^ 32)
|
||||
end
|
|
@ -1,10 +1,25 @@
|
|||
prelude: |
|
||||
# frozen_string_literal: true
|
||||
Warning[:experimental] = false
|
||||
buffer = IO::Buffer.new(32, IO::Buffer::MAPPED)
|
||||
string = "\0" * 32
|
||||
string = "The quick brown fox jumped over the lazy dog."
|
||||
buffer = IO::Buffer.for(string)
|
||||
format = [:U32, :U32, :U32, :U32]
|
||||
benchmark:
|
||||
buffer.get_value: |
|
||||
buffer.get_value(:U32, 0)
|
||||
string.unpack1: |
|
||||
string.unpack1("N")
|
||||
[
|
||||
string.unpack1("N"),
|
||||
string.unpack1("N", offset: 4),
|
||||
string.unpack1("N", offset: 8),
|
||||
string.unpack1("N", offset: 12),
|
||||
]
|
||||
buffer.get_value: |
|
||||
[
|
||||
buffer.get_value(:U32, 0),
|
||||
buffer.get_value(:U32, 4),
|
||||
buffer.get_value(:U32, 8),
|
||||
buffer.get_value(:U32, 12),
|
||||
]
|
||||
buffer.get_values: |
|
||||
buffer.get_values(format, 0)
|
||||
string.unpack: |
|
||||
string.unpack("NNNN")
|
||||
|
|
|
@ -7669,6 +7669,7 @@ io.$(OBJEXT): {$(VPATH)}util.h
|
|||
io.$(OBJEXT): {$(VPATH)}vm_core.h
|
||||
io.$(OBJEXT): {$(VPATH)}vm_opts.h
|
||||
io_buffer.$(OBJEXT): $(hdrdir)/ruby/ruby.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/array.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/bits.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/compilers.h
|
||||
io_buffer.$(OBJEXT): $(top_srcdir)/internal/error.h
|
||||
|
|
515
io_buffer.c
515
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); \
|
||||
}
|
||||
} \
|
||||
\
|
||||
enum { \
|
||||
RB_IO_BUFFER_DATA_TYPE_##name##_SIZE = 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)
|
||||
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)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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(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)
|
||||
|
||||
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)
|
||||
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)
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
VALUE
|
||||
rb_io_buffer_get_value(const void* base, size_t size, ID 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);
|
||||
|
||||
READ_TYPE(u16);
|
||||
READ_TYPE(U16);
|
||||
READ_TYPE(s16);
|
||||
READ_TYPE(S16);
|
||||
|
||||
READ_TYPE(u32);
|
||||
READ_TYPE(U32);
|
||||
READ_TYPE(s32);
|
||||
READ_TYPE(S32);
|
||||
|
||||
READ_TYPE(u64);
|
||||
READ_TYPE(U64);
|
||||
READ_TYPE(s64);
|
||||
READ_TYPE(S64);
|
||||
|
||||
READ_TYPE(f32);
|
||||
READ_TYPE(F32);
|
||||
READ_TYPE(f64);
|
||||
READ_TYPE(F64);
|
||||
#undef READ_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
|
||||
|
||||
rb_raise(rb_eArgError, "Invalid type name!");
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq: get_value(type, offset) -> numeric
|
||||
* call-seq:
|
||||
* size_of(data_type) -> byte size
|
||||
* size_of(array of data_type) -> byte size
|
||||
*
|
||||
* Read from buffer a value of +type+ at +offset+. +type+ should be one
|
||||
* 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)));
|
||||
}
|
||||
}
|
||||
|
||||
static inline VALUE
|
||||
rb_io_buffer_get_value(const void* base, size_t size, ID data_type, size_t *offset)
|
||||
{
|
||||
#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)
|
||||
|
||||
IO_BUFFER_GET_VALUE(u16)
|
||||
IO_BUFFER_GET_VALUE(U16)
|
||||
IO_BUFFER_GET_VALUE(s16)
|
||||
IO_BUFFER_GET_VALUE(S16)
|
||||
|
||||
IO_BUFFER_GET_VALUE(u32)
|
||||
IO_BUFFER_GET_VALUE(U32)
|
||||
IO_BUFFER_GET_VALUE(s32)
|
||||
IO_BUFFER_GET_VALUE(S32)
|
||||
|
||||
IO_BUFFER_GET_VALUE(u64)
|
||||
IO_BUFFER_GET_VALUE(U64)
|
||||
IO_BUFFER_GET_VALUE(s64)
|
||||
IO_BUFFER_GET_VALUE(S64)
|
||||
|
||||
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(data_type, offset) -> numeric
|
||||
*
|
||||
* 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)
|
||||
{
|
||||
#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);
|
||||
size_t offset = NUM2SIZET(_offset);
|
||||
|
||||
WRITE_TYPE(u16);
|
||||
WRITE_TYPE(U16);
|
||||
WRITE_TYPE(s16);
|
||||
WRITE_TYPE(S16);
|
||||
const void *base;
|
||||
size_t size;
|
||||
rb_io_buffer_get_bytes_for_reading(self, &base, &size);
|
||||
|
||||
WRITE_TYPE(u32);
|
||||
WRITE_TYPE(U32);
|
||||
WRITE_TYPE(s32);
|
||||
WRITE_TYPE(S32);
|
||||
if (!RB_TYPE_P(data_types, T_ARRAY)) {
|
||||
rb_raise(rb_eArgError, "Argument data_types should be an array!");
|
||||
}
|
||||
|
||||
WRITE_TYPE(u64);
|
||||
WRITE_TYPE(U64);
|
||||
WRITE_TYPE(s64);
|
||||
WRITE_TYPE(S64);
|
||||
VALUE array = rb_ary_new_capa(RARRAY_LEN(data_types));
|
||||
|
||||
WRITE_TYPE(f32);
|
||||
WRITE_TYPE(F32);
|
||||
WRITE_TYPE(f64);
|
||||
WRITE_TYPE(F64);
|
||||
#undef WRITE_TYPE
|
||||
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 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);
|
||||
|
||||
IO_BUFFER_SET_VALUE(u16);
|
||||
IO_BUFFER_SET_VALUE(U16);
|
||||
IO_BUFFER_SET_VALUE(s16);
|
||||
IO_BUFFER_SET_VALUE(S16);
|
||||
|
||||
IO_BUFFER_SET_VALUE(u32);
|
||||
IO_BUFFER_SET_VALUE(U32);
|
||||
IO_BUFFER_SET_VALUE(s32);
|
||||
IO_BUFFER_SET_VALUE(S32);
|
||||
|
||||
IO_BUFFER_SET_VALUE(u64);
|
||||
IO_BUFFER_SET_VALUE(U64);
|
||||
IO_BUFFER_SET_VALUE(s64);
|
||||
IO_BUFFER_SET_VALUE(S64);
|
||||
|
||||
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);
|
||||
|
||||
|
|
|
@ -235,17 +235,59 @@ class TestIOBuffer < Test::Unit::TestCase
|
|||
:F64 => [-1.0, 0.0, 0.5, 1.0, 128.0],
|
||||
}
|
||||
|
||||
def test_get_set_primitives
|
||||
def test_get_set_value
|
||||
buffer = IO::Buffer.new(128)
|
||||
|
||||
RANGES.each do |type, values|
|
||||
RANGES.each do |data_type, values|
|
||||
values.each do |value|
|
||||
buffer.set_value(type, 0, value)
|
||||
assert_equal value, buffer.get_value(type, 0), "Converting #{value} as #{type}."
|
||||
buffer.set_value(data_type, 0, value)
|
||||
assert_equal value, buffer.get_value(data_type, 0), "Converting #{value} as #{data_type}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def test_get_set_values
|
||||
buffer = IO::Buffer.new(128)
|
||||
|
||||
RANGES.each do |data_type, values|
|
||||
format = [data_type] * values.size
|
||||
|
||||
buffer.set_values(format, 0, values)
|
||||
assert_equal values, buffer.get_values(format, 0), "Converting #{values} as #{format}."
|
||||
end
|
||||
end
|
||||
|
||||
def test_values
|
||||
buffer = IO::Buffer.new(128)
|
||||
|
||||
RANGES.each do |data_type, values|
|
||||
format = [data_type] * values.size
|
||||
|
||||
buffer.set_values(format, 0, values)
|
||||
assert_equal values, buffer.values(data_type, 0, values.size), "Reading #{values} as #{format}."
|
||||
end
|
||||
end
|
||||
|
||||
def test_each
|
||||
buffer = IO::Buffer.new(128)
|
||||
|
||||
RANGES.each do |data_type, values|
|
||||
format = [data_type] * values.size
|
||||
data_type_size = IO::Buffer.size_of(data_type)
|
||||
values_with_offsets = values.map.with_index{|value, index| [index * data_type_size, value]}
|
||||
|
||||
buffer.set_values(format, 0, values)
|
||||
assert_equal values_with_offsets, buffer.each(data_type, 0, values.size).to_a, "Reading #{values} as #{data_type}."
|
||||
end
|
||||
end
|
||||
|
||||
def test_each_byte
|
||||
string = "The quick brown fox jumped over the lazy dog."
|
||||
buffer = IO::Buffer.for(string)
|
||||
|
||||
assert_equal string.bytes, buffer.each_byte.to_a
|
||||
end
|
||||
|
||||
def test_clear
|
||||
buffer = IO::Buffer.new(16)
|
||||
buffer.set_string("Hello World!")
|
||||
|
|
Loading…
Reference in a new issue