mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
Add basic binary operators (and, or, xor, not) to IO::Buffer
. (#5893)
This commit is contained in:
parent
df0bcb3385
commit
cea34bd808
Notes:
git
2022-05-09 14:19:24 +09:00
Merged-By: ioquatix <samuel@codeotaku.com>
2 changed files with 427 additions and 0 deletions
405
io_buffer.c
405
io_buffer.c
|
@ -20,6 +20,7 @@ VALUE rb_eIOBufferLockedError;
|
|||
VALUE rb_eIOBufferAllocationError;
|
||||
VALUE rb_eIOBufferAccessError;
|
||||
VALUE rb_eIOBufferInvalidatedError;
|
||||
VALUE rb_eIOBufferMaskError;
|
||||
|
||||
size_t RUBY_IO_BUFFER_PAGE_SIZE;
|
||||
size_t RUBY_IO_BUFFER_DEFAULT_SIZE;
|
||||
|
@ -1692,6 +1693,40 @@ io_buffer_copy_from(struct rb_io_buffer *data, const void *source_base, size_t s
|
|||
return SIZET2NUM(length);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* dup -> io_buffer
|
||||
* clone -> io_buffer
|
||||
*
|
||||
* Make an internal copy of the source buffer. Updates to the copy will not
|
||||
* affect the source buffer.
|
||||
*
|
||||
* source = IO::Buffer.for("Hello World")
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x00007fd598466830+11 EXTERNAL READONLY SLICE>
|
||||
* # 0x00000000 48 65 6c 6c 6f 20 57 6f 72 6c 64 Hello World
|
||||
* buffer = source.dup
|
||||
* # =>
|
||||
* # #<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)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
const void *source_base;
|
||||
size_t source_size;
|
||||
|
||||
rb_io_buffer_get_bytes_for_reading(source, &source_base, &source_size);
|
||||
|
||||
io_buffer_initialize(data, NULL, source_size, io_flags_for_size(source_size), Qnil);
|
||||
|
||||
return io_buffer_copy_from(data, source_base, source_size, 0, NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* copy(source, [offset, [length, [source_offset]]]) -> size
|
||||
|
@ -2120,6 +2155,363 @@ io_buffer_pwrite(VALUE self, VALUE io, VALUE length, VALUE offset)
|
|||
return rb_io_buffer_pwrite(self, io, RB_NUM2SIZE(length), NUM2OFFT(offset));
|
||||
}
|
||||
|
||||
static inline void
|
||||
io_buffer_check_mask(const struct rb_io_buffer *buffer)
|
||||
{
|
||||
if (buffer->size == 0)
|
||||
rb_raise(rb_eIOBufferMaskError, "Zero-length mask given!");
|
||||
}
|
||||
|
||||
static void
|
||||
memory_and(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
output[offset] = base[offset] & mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source & mask -> io_buffer
|
||||
*
|
||||
* Generate a new buffer the same size as the source by applying the binary AND
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* IO::Buffer.for("1234567890") & IO::Buffer.for("\xFF\x00\x00\xFF")
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x00005589b2758480+4 INTERNAL>
|
||||
* # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_and(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
|
||||
VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
|
||||
struct rb_io_buffer *output_data = NULL;
|
||||
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
|
||||
|
||||
memory_and(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_or(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
output[offset] = base[offset] | mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source | mask -> io_buffer
|
||||
*
|
||||
* Generate a new buffer the same size as the source by applying the binary OR
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* IO::Buffer.for("1234567890") | IO::Buffer.for("\xFF\x00\x00\xFF")
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x0000561785ae3480+10 INTERNAL>
|
||||
* # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_or(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
|
||||
VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
|
||||
struct rb_io_buffer *output_data = NULL;
|
||||
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
|
||||
|
||||
memory_or(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_xor(unsigned char * restrict output, unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
output[offset] = base[offset] ^ mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source ^ mask -> io_buffer
|
||||
*
|
||||
* Generate a new buffer the same size as the source by applying the binary XOR
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* IO::Buffer.for("1234567890") ^ IO::Buffer.for("\xFF\x00\x00\xFF")
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000055a2d5d10480+10 INTERNAL>
|
||||
* # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_xor(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
|
||||
VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
|
||||
struct rb_io_buffer *output_data = NULL;
|
||||
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
|
||||
|
||||
memory_xor(output_data->base, data->base, data->size, mask_data->base, mask_data->size);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_not(unsigned char * restrict output, unsigned char * restrict base, size_t size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
output[offset] = ~base[offset];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* ~source -> io_buffer
|
||||
*
|
||||
* Generate a new buffer the same size as the source by applying the binary NOT
|
||||
* operation to the source.
|
||||
*
|
||||
* ~IO::Buffer.for("1234567890")
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000055a5ac42f120+10 INTERNAL>
|
||||
* # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_not(VALUE self)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
VALUE output = rb_io_buffer_new(NULL, data->size, io_flags_for_size(data->size));
|
||||
struct rb_io_buffer *output_data = NULL;
|
||||
TypedData_Get_Struct(output, struct rb_io_buffer, &rb_io_buffer_type, output_data);
|
||||
|
||||
memory_not(output_data->base, data->base, data->size);
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
static inline int
|
||||
io_buffer_overlaps(const struct rb_io_buffer *a, const struct rb_io_buffer *b)
|
||||
{
|
||||
if (a->base > b->base) {
|
||||
return io_buffer_overlaps(b, a);
|
||||
}
|
||||
|
||||
return (b->base >= a->base) && (b->base <= (void*)((unsigned char *)a->base + a->size));
|
||||
}
|
||||
|
||||
static inline void
|
||||
io_buffer_check_overlaps(struct rb_io_buffer *a, struct rb_io_buffer *b)
|
||||
{
|
||||
if (io_buffer_overlaps(a, b))
|
||||
rb_raise(rb_eIOBufferMaskError, "Mask overlaps source data!");
|
||||
}
|
||||
|
||||
static void
|
||||
memory_and_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
base[offset] &= mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source.and!(mask) -> io_buffer
|
||||
*
|
||||
* Modify the source buffer in place by applying the binary AND
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
|
||||
* # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
|
||||
*
|
||||
* source.and!(IO::Buffer.for("\xFF\x00\x00\xFF"))
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a0d0c20+10 INTERNAL>
|
||||
* # 0x00000000 31 00 00 34 35 00 00 38 39 00 1..45..89.
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_and_inplace(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
io_buffer_check_overlaps(data, mask_data);
|
||||
|
||||
void *base;
|
||||
size_t size;
|
||||
io_buffer_get_bytes_for_writing(data, &base, &size);
|
||||
|
||||
memory_and_inplace(base, size, mask_data->base, mask_data->size);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_or_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
base[offset] |= mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source.or!(mask) -> io_buffer
|
||||
*
|
||||
* Modify the source buffer in place by applying the binary OR
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
|
||||
* # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
|
||||
*
|
||||
* source.or!(IO::Buffer.for("\xFF\x00\x00\xFF"))
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a272350+10 INTERNAL>
|
||||
* # 0x00000000 ff 32 33 ff ff 36 37 ff ff 30 .23..67..0
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_or_inplace(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
io_buffer_check_overlaps(data, mask_data);
|
||||
|
||||
void *base;
|
||||
size_t size;
|
||||
io_buffer_get_bytes_for_writing(data, &base, &size);
|
||||
|
||||
memory_or_inplace(base, size, mask_data->base, mask_data->size);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_xor_inplace(unsigned char * restrict base, size_t size, unsigned char * restrict mask, size_t mask_size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
base[offset] ^= mask[offset % mask_size];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source.xor!(mask) -> io_buffer
|
||||
*
|
||||
* Modify the source buffer in place by applying the binary XOR
|
||||
* operation to the source, using the mask, repeating as necessary.
|
||||
*
|
||||
* source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
|
||||
* # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
|
||||
*
|
||||
* source.xor!(IO::Buffer.for("\xFF\x00\x00\xFF"))
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a25b3e0+10 INTERNAL>
|
||||
* # 0x00000000 ce 32 33 cb ca 36 37 c7 c6 30 .23..67..0
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_xor_inplace(VALUE self, VALUE mask)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
struct rb_io_buffer *mask_data = NULL;
|
||||
TypedData_Get_Struct(mask, struct rb_io_buffer, &rb_io_buffer_type, mask_data);
|
||||
|
||||
io_buffer_check_mask(mask_data);
|
||||
io_buffer_check_overlaps(data, mask_data);
|
||||
|
||||
void *base;
|
||||
size_t size;
|
||||
io_buffer_get_bytes_for_writing(data, &base, &size);
|
||||
|
||||
memory_xor_inplace(base, size, mask_data->base, mask_data->size);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
static void
|
||||
memory_not_inplace(unsigned char * restrict base, size_t size)
|
||||
{
|
||||
for (size_t offset = 0; offset < size; offset += 1) {
|
||||
base[offset] = ~base[offset];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* call-seq:
|
||||
* source.not! -> io_buffer
|
||||
*
|
||||
* Modify the source buffer in place by applying the binary NOT
|
||||
* operation to the source.
|
||||
*
|
||||
* source = IO::Buffer.for("1234567890").dup # Make a read/write copy.
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
|
||||
* # 0x00000000 31 32 33 34 35 36 37 38 39 30 1234567890
|
||||
*
|
||||
* source.not!
|
||||
* # =>
|
||||
* # #<IO::Buffer 0x000056307a33a450+10 INTERNAL>
|
||||
* # 0x00000000 ce cd cc cb ca c9 c8 c7 c6 cf ..........
|
||||
*/
|
||||
static VALUE
|
||||
io_buffer_not_inplace(VALUE self)
|
||||
{
|
||||
struct rb_io_buffer *data = NULL;
|
||||
TypedData_Get_Struct(self, struct rb_io_buffer, &rb_io_buffer_type, data);
|
||||
|
||||
void *base;
|
||||
size_t size;
|
||||
io_buffer_get_bytes_for_writing(data, &base, &size);
|
||||
|
||||
memory_not_inplace(base, size);
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
/*
|
||||
* Document-class: IO::Buffer
|
||||
*
|
||||
|
@ -2211,6 +2603,7 @@ Init_IO_Buffer(void)
|
|||
rb_eIOBufferAllocationError = rb_define_class_under(rb_cIOBuffer, "AllocationError", rb_eRuntimeError);
|
||||
rb_eIOBufferAccessError = rb_define_class_under(rb_cIOBuffer, "AccessError", rb_eRuntimeError);
|
||||
rb_eIOBufferInvalidatedError = rb_define_class_under(rb_cIOBuffer, "InvalidatedError", rb_eRuntimeError);
|
||||
rb_eIOBufferMaskError = rb_define_class_under(rb_cIOBuffer, "MaskError", rb_eArgError);
|
||||
|
||||
rb_define_alloc_func(rb_cIOBuffer, rb_io_buffer_type_allocate);
|
||||
rb_define_singleton_method(rb_cIOBuffer, "for", rb_io_buffer_type_for, 1);
|
||||
|
@ -2233,6 +2626,7 @@ Init_IO_Buffer(void)
|
|||
|
||||
// General use:
|
||||
rb_define_method(rb_cIOBuffer, "initialize", rb_io_buffer_initialize, -1);
|
||||
rb_define_method(rb_cIOBuffer, "initialize_copy", rb_io_buffer_initialize_copy, 1);
|
||||
rb_define_method(rb_cIOBuffer, "inspect", rb_io_buffer_inspect, 0);
|
||||
rb_define_method(rb_cIOBuffer, "hexdump", rb_io_buffer_hexdump, 0);
|
||||
rb_define_method(rb_cIOBuffer, "to_s", rb_io_buffer_to_s, 0);
|
||||
|
@ -2295,6 +2689,17 @@ Init_IO_Buffer(void)
|
|||
rb_define_method(rb_cIOBuffer, "get_string", io_buffer_get_string, -1);
|
||||
rb_define_method(rb_cIOBuffer, "set_string", io_buffer_set_string, -1);
|
||||
|
||||
// Binary data manipulations:
|
||||
rb_define_method(rb_cIOBuffer, "&", io_buffer_and, 1);
|
||||
rb_define_method(rb_cIOBuffer, "|", io_buffer_or, 1);
|
||||
rb_define_method(rb_cIOBuffer, "^", io_buffer_xor, 1);
|
||||
rb_define_method(rb_cIOBuffer, "~", io_buffer_not, 0);
|
||||
|
||||
rb_define_method(rb_cIOBuffer, "and!", io_buffer_and_inplace, 1);
|
||||
rb_define_method(rb_cIOBuffer, "or!", io_buffer_or_inplace, 1);
|
||||
rb_define_method(rb_cIOBuffer, "xor!", io_buffer_xor_inplace, 1);
|
||||
rb_define_method(rb_cIOBuffer, "not!", io_buffer_not_inplace, 0);
|
||||
|
||||
// IO operations:
|
||||
rb_define_method(rb_cIOBuffer, "read", io_buffer_read, 2);
|
||||
rb_define_method(rb_cIOBuffer, "pread", io_buffer_pread, 3);
|
||||
|
|
|
@ -331,4 +331,26 @@ class TestIOBuffer < Test::Unit::TestCase
|
|||
ensure
|
||||
io.close!
|
||||
end
|
||||
|
||||
def test_operators
|
||||
source = IO::Buffer.for("1234123412")
|
||||
mask = IO::Buffer.for("133\x00")
|
||||
result = source & mask
|
||||
|
||||
assert_equal (source & mask), IO::Buffer.for("123\x00123\x0012")
|
||||
assert_equal (source | mask), IO::Buffer.for("1334133413")
|
||||
assert_equal (source ^ mask), IO::Buffer.for("\x00\x01\x004\x00\x01\x004\x00\x01")
|
||||
assert_equal ~source, IO::Buffer.for("\xce\xcd\xcc\xcb\xce\xcd\xcc\xcb\xce\xcd")
|
||||
end
|
||||
|
||||
def test_inplace_operators
|
||||
source = IO::Buffer.for("1234123412")
|
||||
mask = IO::Buffer.for("133\x00")
|
||||
result = source & mask
|
||||
|
||||
assert_equal source.dup.and!(mask), IO::Buffer.for("123\x00123\x0012")
|
||||
assert_equal source.dup.or!(mask), IO::Buffer.for("1334133413")
|
||||
assert_equal source.dup.xor!(mask), IO::Buffer.for("\x00\x01\x004\x00\x01\x004\x00\x01")
|
||||
assert_equal source.dup.not!, IO::Buffer.for("\xce\xcd\xcc\xcb\xce\xcd\xcc\xcb\xce\xcd")
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Reference in a new issue