diff options
-rw-r--r-- | io_buffer.c | 25 | ||||
-rw-r--r-- | test/ruby/test_io_buffer.rb | 50 |
2 files changed, 70 insertions, 5 deletions
diff --git a/io_buffer.c b/io_buffer.c index 51ddde8c29..53733c8ff4 100644 --- a/io_buffer.c +++ b/io_buffer.c @@ -2328,7 +2328,7 @@ io_buffer_set_values(VALUE self, VALUE buffer_types, VALUE _offset, VALUE values } static void -io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length) +io_buffer_memmove(struct rb_io_buffer *buffer, size_t offset, const void *source_base, size_t source_offset, size_t source_size, size_t length) { void *base; size_t size; @@ -2340,7 +2340,9 @@ io_buffer_memcpy(struct rb_io_buffer *buffer, size_t offset, const void *source_ rb_raise(rb_eArgError, "The computed source range exceeds the size of the source buffer!"); } - memcpy((unsigned char*)base+offset, (unsigned char*)source_base+source_offset, length); + if (length != 0) { + memmove((unsigned char*)base+offset, (const unsigned char*)source_base+source_offset, length); + } } // (offset, length, source_offset) -> length @@ -2377,7 +2379,7 @@ io_buffer_copy_from(struct rb_io_buffer *buffer, const void *source_base, size_t length = source_size - source_offset; } - io_buffer_memcpy(buffer, offset, source_base, source_offset, source_size, length); + io_buffer_memmove(buffer, offset, source_base, source_offset, source_size, length); return SIZET2NUM(length); } @@ -2420,7 +2422,7 @@ rb_io_buffer_initialize_copy(VALUE self, VALUE source) * copy(source, [offset, [length, [source_offset]]]) -> size * * Efficiently copy from a source IO::Buffer into the buffer, at +offset+ - * using +memcpy+. For copying String instances, see #set_string. + * using +memmove+. For copying String instances, see #set_string. * * buffer = IO::Buffer.new(32) * # => @@ -2469,6 +2471,19 @@ 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 is bigger than the buffer size! (ArgumentError) + * + * It is safe to copy between memory regions that overlaps each other. + * In such case, the data is copied as if the data was first copied from the source buffer to + * a temporary buffer, and then copied from the temporary buffer to the destination buffer. + * + * buffer = IO::Buffer.new(10) + * buffer.set_string("0123456789") + * buffer.copy(buffer, 3, 7) + * # => 7 + * buffer + * # => + * # #<IO::Buffer 0x000056494f8ce440+10 INTERNAL> + * # 0x00000000 30 31 32 30 31 32 33 34 35 36 0120123456 */ static VALUE io_buffer_copy(int argc, VALUE *argv, VALUE self) @@ -2530,7 +2545,7 @@ io_buffer_get_string(int argc, VALUE *argv, VALUE self) * call-seq: set_string(string, [offset, [length, [source_offset]]]) -> size * * Efficiently copy from a source String into the buffer, at +offset+ using - * +memcpy+. + * +memmove+. * * buf = IO::Buffer.new(8) * # => diff --git a/test/ruby/test_io_buffer.rb b/test/ruby/test_io_buffer.rb index 178ed05003..55296c1f23 100644 --- a/test/ruby/test_io_buffer.rb +++ b/test/ruby/test_io_buffer.rb @@ -633,4 +633,54 @@ class TestIOBuffer < Test::Unit::TestCase end end end + + def test_copy_overlapped_fwd + buf = IO::Buffer.for('0123456789').dup + buf.copy(buf, 3, 7) + assert_equal '0120123456', buf.get_string + end + + def test_copy_overlapped_bwd + buf = IO::Buffer.for('0123456789').dup + buf.copy(buf, 0, 7, 3) + assert_equal '3456789789', buf.get_string + end + + def test_copy_null_destination + buf = IO::Buffer.new(0) + assert_predicate buf, :null? + buf.copy(IO::Buffer.for('a'), 0, 0) + assert_predicate buf, :empty? + end + + def test_copy_null_source + buf = IO::Buffer.for('a').dup + src = IO::Buffer.new(0) + assert_predicate src, :null? + buf.copy(src, 0, 0) + assert_equal 'a', buf.get_string + end + + def test_set_string_overlapped_fwd + str = +'0123456789' + IO::Buffer.for(str) do |buf| + buf.set_string(str, 3, 7) + end + assert_equal '0120123456', str + end + + def test_set_string_overlapped_bwd + str = +'0123456789' + IO::Buffer.for(str) do |buf| + buf.set_string(str, 0, 7, 3) + end + assert_equal '3456789789', str + end + + def test_set_string_null_destination + buf = IO::Buffer.new(0) + assert_predicate buf, :null? + buf.set_string('a', 0, 0) + assert_predicate buf, :empty? + end end |