mirror of
				https://github.com/ruby/ruby.git
				synced 2022-11-09 12:17:21 -05:00 
			
		
		
		
	
		
			
				
	
	
		
			341 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
			
		
		
	
	
			341 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			Ruby
		
	
	
	
	
	
| require "-test-/memory_view"
 | |
| require "rbconfig/sizeof"
 | |
| 
 | |
| class TestMemoryView < Test::Unit::TestCase
 | |
|   NATIVE_ENDIAN = MemoryViewTestUtils::NATIVE_ENDIAN
 | |
|   LITTLE_ENDIAN = :little_endian
 | |
|   BIG_ENDIAN    = :big_endian
 | |
| 
 | |
|   %I(SHORT INT INT16 INT32 INT64 INTPTR LONG LONG_LONG FLOAT DOUBLE).each do |type|
 | |
|     name = :"#{type}_ALIGNMENT"
 | |
|     const_set(name, MemoryViewTestUtils.const_get(name))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_register_duplicated
 | |
|     assert_warning(/Duplicated registration of memory view to/) do
 | |
|       MemoryViewTestUtils.register(MemoryViewTestUtils::ExportableString)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_register_nonclass
 | |
|     assert_raise(TypeError) do
 | |
|       MemoryViewTestUtils.register(Object.new)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def sizeof(type)
 | |
|     RbConfig::SIZEOF[type.to_s]
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_item_size_from_format
 | |
|     [
 | |
|       [nil, 1], ['c', 1], ['C', 1],
 | |
|       ['n', 2], ['v', 2],
 | |
|       ['l', 4], ['L', 4], ['N', 4], ['V', 4], ['f', 4], ['e', 4], ['g', 4],
 | |
|       ['q', 8], ['Q', 8], ['d', 8], ['E', 8], ['G', 8],
 | |
|       ['s', sizeof(:short)], ['S', sizeof(:short)], ['s!', sizeof(:short)], ['S!', sizeof(:short)],
 | |
|       ['i', sizeof(:int)], ['I', sizeof(:int)], ['i!', sizeof(:int)], ['I!', sizeof(:int)],
 | |
|       ['l!', sizeof(:long)], ['L!', sizeof(:long)],
 | |
|       ['q!', sizeof('long long')], ['Q!', sizeof('long long')],
 | |
|       ['j', sizeof(:intptr_t)], ['J', sizeof(:intptr_t)],
 | |
|     ].each do |format, expected|
 | |
|       actual, err = MemoryViewTestUtils.item_size_from_format(format)
 | |
|       assert_nil(err)
 | |
|       assert_equal(expected, actual, "rb_memory_view_item_size_from_format(#{format || 'NULL'}) == #{expected}")
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_item_size_from_format_composed
 | |
|     actual, = MemoryViewTestUtils.item_size_from_format("ccc")
 | |
|     assert_equal(3, actual)
 | |
| 
 | |
|     actual, = MemoryViewTestUtils.item_size_from_format("c3")
 | |
|     assert_equal(3, actual)
 | |
| 
 | |
|     actual, = MemoryViewTestUtils.item_size_from_format("fd")
 | |
|     assert_equal(12, actual)
 | |
| 
 | |
|     actual, = MemoryViewTestUtils.item_size_from_format("fx2d")
 | |
|     assert_equal(14, actual)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_item_size_from_format_with_spaces
 | |
|     # spaces should be ignored
 | |
|     actual, = MemoryViewTestUtils.item_size_from_format("f x2 d")
 | |
|     assert_equal(14, actual)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_item_size_from_format_error
 | |
|     assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccca"))
 | |
|     assert_equal([-1, "a"], MemoryViewTestUtils.item_size_from_format("ccc4a"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_parse_item_format
 | |
|     total_size, members, err = MemoryViewTestUtils.parse_item_format("ccc2f3x2d4q!<")
 | |
|     assert_equal(58, total_size)
 | |
|     assert_nil(err)
 | |
|     assert_equal([
 | |
|                    {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset:  0, size: 1, repeat: 1},
 | |
|                    {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset:  1, size: 1, repeat: 1},
 | |
|                    {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN, offset:  2, size: 1, repeat: 2},
 | |
|                    {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset:  4, size: 4, repeat: 3},
 | |
|                    {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: 18, size: 8, repeat: 4},
 | |
|                    {format: 'q', native_size_p: true,  endianness: :little_endian, offset: 50, size: sizeof('long long'), repeat: 1}
 | |
|                  ],
 | |
|                  members)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_parse_item_format_with_alignment_signle
 | |
|     [
 | |
|       ["c",  false, NATIVE_ENDIAN,  1,               1,              1],
 | |
|       ["C",  false, NATIVE_ENDIAN,  1,               1,              1],
 | |
|       ["s",  false, NATIVE_ENDIAN,  SHORT_ALIGNMENT, sizeof(:short), 1],
 | |
|       ["S",  false, NATIVE_ENDIAN,  SHORT_ALIGNMENT, sizeof(:short), 1],
 | |
|       ["s!", true,  NATIVE_ENDIAN,  SHORT_ALIGNMENT, sizeof(:short), 1],
 | |
|       ["S!", true,  NATIVE_ENDIAN,  SHORT_ALIGNMENT, sizeof(:short), 1],
 | |
|       ["n",  false, :big_endian,    INT16_ALIGNMENT, sizeof(:int16_t), 1],
 | |
|       ["v",  false, :little_endian, INT16_ALIGNMENT, sizeof(:int16_t), 1],
 | |
|       ["i",  false, NATIVE_ENDIAN,  INT_ALIGNMENT, sizeof(:int), 1],
 | |
|       ["I",  false, NATIVE_ENDIAN,  INT_ALIGNMENT, sizeof(:int), 1],
 | |
|       ["i!", true,  NATIVE_ENDIAN,  INT_ALIGNMENT, sizeof(:int), 1],
 | |
|       ["I!", true,  NATIVE_ENDIAN,  INT_ALIGNMENT, sizeof(:int), 1],
 | |
|       ["l",  false, NATIVE_ENDIAN,  INT32_ALIGNMENT, sizeof(:int32_t), 1],
 | |
|       ["L",  false, NATIVE_ENDIAN,  INT32_ALIGNMENT, sizeof(:int32_t), 1],
 | |
|       ["l!", true,  NATIVE_ENDIAN,  LONG_ALIGNMENT, sizeof(:long), 1],
 | |
|       ["L!", true,  NATIVE_ENDIAN,  LONG_ALIGNMENT, sizeof(:long), 1],
 | |
|       ["N",  false, :big_endian,    INT32_ALIGNMENT, sizeof(:int32_t), 1],
 | |
|       ["V",  false, :little_endian, INT32_ALIGNMENT, sizeof(:int32_t), 1],
 | |
|       ["f",  false, NATIVE_ENDIAN,  FLOAT_ALIGNMENT, sizeof(:float), 1],
 | |
|       ["e",  false, :little_endian, FLOAT_ALIGNMENT, sizeof(:float), 1],
 | |
|       ["g",  false, :big_endian,    FLOAT_ALIGNMENT, sizeof(:float), 1],
 | |
|       ["q",  false, NATIVE_ENDIAN,  INT64_ALIGNMENT, sizeof(:int64_t), 1],
 | |
|       ["Q",  false, NATIVE_ENDIAN,  INT64_ALIGNMENT, sizeof(:int64_t), 1],
 | |
|       ["q!", true,  NATIVE_ENDIAN,  LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
 | |
|       ["Q!", true,  NATIVE_ENDIAN,  LONG_LONG_ALIGNMENT, sizeof("long long"), 1],
 | |
|       ["d",  false, NATIVE_ENDIAN,  DOUBLE_ALIGNMENT, sizeof(:double), 1],
 | |
|       ["E",  false, :little_endian, DOUBLE_ALIGNMENT, sizeof(:double), 1],
 | |
|       ["G",  false, :big_endian,    DOUBLE_ALIGNMENT, sizeof(:double), 1],
 | |
|       ["j",  false, NATIVE_ENDIAN,  INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
 | |
|       ["J",  false, NATIVE_ENDIAN,  INTPTR_ALIGNMENT, sizeof(:intptr_t), 1],
 | |
|     ].each do |type, native_size_p, endianness, alignment, size, repeat, total_size|
 | |
|       total_size, members, err = MemoryViewTestUtils.parse_item_format("|c#{type}")
 | |
|       assert_nil(err)
 | |
| 
 | |
|       padding_size = alignment - 1
 | |
|       expected_total_size = 1 + padding_size + size
 | |
|       assert_equal(expected_total_size, total_size)
 | |
| 
 | |
|       expected_result = [
 | |
|         {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN,  offset: 0, size: 1, repeat: 1},
 | |
|         {format: type[0], native_size_p: native_size_p, endianness: endianness,  offset: alignment, size: size, repeat: repeat},
 | |
|       ]
 | |
|       assert_equal(expected_result, members)
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def alignment_padding(total_size, alignment)
 | |
|     res = total_size % alignment
 | |
|     if res > 0
 | |
|       alignment - res
 | |
|     else
 | |
|       0
 | |
|     end
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_parse_item_format_with_alignment_total_size_with_tail_padding
 | |
|     total_size, _members, err = MemoryViewTestUtils.parse_item_format("|lqc")
 | |
|     assert_nil(err)
 | |
| 
 | |
|     expected_total_size = sizeof(:int32_t)
 | |
|     expected_total_size += alignment_padding(expected_total_size, INT32_ALIGNMENT)
 | |
|     expected_total_size += sizeof(:int64_t)
 | |
|     expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
 | |
|     expected_total_size += 1
 | |
|     expected_total_size += alignment_padding(expected_total_size, INT64_ALIGNMENT)
 | |
|     assert_equal(expected_total_size, total_size)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_parse_item_format_with_alignment_compound
 | |
|     total_size, members, err = MemoryViewTestUtils.parse_item_format("|ccc2f3x2d4cq!<")
 | |
|     assert_nil(err)
 | |
| 
 | |
|     expected_total_size = 1 + 1 + 1*2
 | |
|     expected_total_size += alignment_padding(expected_total_size, FLOAT_ALIGNMENT)
 | |
|     expected_total_size += sizeof(:float)*3 + 1*2
 | |
|     expected_total_size += alignment_padding(expected_total_size, DOUBLE_ALIGNMENT)
 | |
|     expected_total_size += sizeof(:double)*4 + 1
 | |
|     expected_total_size += alignment_padding(expected_total_size, LONG_LONG_ALIGNMENT)
 | |
|     expected_total_size += sizeof("long long")
 | |
|     assert_equal(expected_total_size, total_size)
 | |
| 
 | |
|     expected_result = [
 | |
|       {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN,  offset:  0, size: 1, repeat: 1},
 | |
|       {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN,  offset:  1, size: 1, repeat: 1},
 | |
|       {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN,  offset:  2, size: 1, repeat: 2},
 | |
|     ]
 | |
|     offset = 4
 | |
| 
 | |
|     res = offset % FLOAT_ALIGNMENT
 | |
|     offset += FLOAT_ALIGNMENT - res if res > 0
 | |
|     expected_result << {format: 'f', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 4, repeat: 3}
 | |
|     offset += 12
 | |
| 
 | |
|     offset += 2 # 2x
 | |
| 
 | |
|     res = offset % DOUBLE_ALIGNMENT
 | |
|     offset += DOUBLE_ALIGNMENT - res if res > 0
 | |
|     expected_result << {format: 'd', native_size_p: false, endianness: NATIVE_ENDIAN, offset: offset, size: 8, repeat: 4}
 | |
|     offset += 32
 | |
| 
 | |
|     expected_result << {format: 'c', native_size_p: false, endianness: NATIVE_ENDIAN,  offset: offset, size: 1, repeat: 1}
 | |
|     offset += 1
 | |
| 
 | |
|     res = offset % LONG_LONG_ALIGNMENT
 | |
|     offset += LONG_LONG_ALIGNMENT - res if res > 0
 | |
|     expected_result << {format: 'q', native_size_p: true, endianness: :little_endian, offset: offset, size: 8, repeat: 1}
 | |
| 
 | |
|     assert_equal(expected_result, members)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members
 | |
|     m = MemoryViewTestUtils
 | |
|     assert_equal(1, m.extract_item_members([1].pack("c"), "c"))
 | |
|     assert_equal([1, 2], m.extract_item_members([1, 2].pack("ii"), "ii"))
 | |
|     assert_equal([1, 2, 3], m.extract_item_members([1, 2, 3].pack("cls"), "cls"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members_endianness
 | |
|     m = MemoryViewTestUtils
 | |
|     assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S>2"))
 | |
|     assert_equal([0x0102, 0x0304], m.extract_item_members([1, 2, 3, 4].pack("c*"), "n2"))
 | |
|     assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "S<2"))
 | |
|     assert_equal([0x0201, 0x0403], m.extract_item_members([1, 2, 3, 4].pack("c*"), "v2"))
 | |
|     assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L>"))
 | |
|     assert_equal(0x01020304, m.extract_item_members([1, 2, 3, 4].pack("c*"), "N"))
 | |
|     assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "L<"))
 | |
|     assert_equal(0x04030201, m.extract_item_members([1, 2, 3, 4].pack("c*"), "V"))
 | |
|     assert_equal(0x0102030405060708, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q>"))
 | |
|     assert_equal(0x0807060504030201, m.extract_item_members([1, 2, 3, 4, 5, 6, 7, 8].pack("c*"), "Q<"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members_float
 | |
|     m = MemoryViewTestUtils
 | |
|     packed = [1.23].pack("f")
 | |
|     assert_equal(packed.unpack("f")[0], m.extract_item_members(packed, "f"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members_float_endianness
 | |
|     m = MemoryViewTestUtils
 | |
|     hi, lo = [1.23].pack("f").unpack("L")[0].divmod(0x10000)
 | |
|     packed = [lo, hi].pack("S*")
 | |
|     assert_equal(packed.unpack("e")[0], m.extract_item_members(packed, "e"))
 | |
|     packed = [hi, lo].pack("S*")
 | |
|     assert_equal(packed.unpack("g")[0], m.extract_item_members(packed, "g"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members_doble
 | |
|     m = MemoryViewTestUtils
 | |
|     packed = [1.23].pack("d")
 | |
|     assert_equal(1.23, m.extract_item_members(packed, "d"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_extract_item_members_doble_endianness
 | |
|     m = MemoryViewTestUtils
 | |
|     hi, lo = [1.23].pack("d").unpack("Q")[0].divmod(0x10000)
 | |
|     packed = [lo, hi].pack("L*")
 | |
|     assert_equal(packed.unpack("E")[0], m.extract_item_members(packed, "E"))
 | |
|     packed = [hi, lo].pack("L*")
 | |
|     assert_equal(packed.unpack("G")[0], m.extract_item_members(packed, "G"))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_available_p
 | |
|     es = MemoryViewTestUtils::ExportableString.new("ruby")
 | |
|     assert_equal(true, MemoryViewTestUtils.available?(es))
 | |
|     es = MemoryViewTestUtils::ExportableString.new(nil)
 | |
|     assert_equal(false, MemoryViewTestUtils.available?(es))
 | |
|   end
 | |
| 
 | |
|   def test_ref_count_with_exported_object
 | |
|     es = MemoryViewTestUtils::ExportableString.new("ruby")
 | |
|     assert_equal(1, MemoryViewTestUtils.ref_count_while_exporting(es, 1))
 | |
|     assert_equal(2, MemoryViewTestUtils.ref_count_while_exporting(es, 2))
 | |
|     assert_equal(10, MemoryViewTestUtils.ref_count_while_exporting(es, 10))
 | |
|     assert_nil(MemoryViewTestUtils.ref_count_while_exporting(es, 0))
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_init_as_byte_array
 | |
|     # ExportableString's memory view is initialized by rb_memory_view_init_as_byte_array
 | |
|     es = MemoryViewTestUtils::ExportableString.new("ruby")
 | |
|     memory_view_info = MemoryViewTestUtils.get_memory_view_info(es)
 | |
|     assert_equal({
 | |
|                    obj: es,
 | |
|                    byte_size: 4,
 | |
|                    readonly: true,
 | |
|                    format: nil,
 | |
|                    item_size: 1,
 | |
|                    ndim: 1,
 | |
|                    shape: nil,
 | |
|                    strides: nil,
 | |
|                    sub_offsets: nil
 | |
|                  },
 | |
|                  memory_view_info)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_get_with_memory_view_unavailable_object
 | |
|     es = MemoryViewTestUtils::ExportableString.new(nil)
 | |
|     memory_view_info = MemoryViewTestUtils.get_memory_view_info(es)
 | |
|     assert_nil(memory_view_info)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_fill_contiguous_strides
 | |
|     row_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], true)
 | |
|     assert_equal([96, 32, 8],
 | |
|                  row_major_strides)
 | |
| 
 | |
|     column_major_strides = MemoryViewTestUtils.fill_contiguous_strides(3, 8, [2, 3, 4], false)
 | |
|     assert_equal([8, 16, 48],
 | |
|                  column_major_strides)
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_get_item_pointer_single_member
 | |
|     buf = [ 1, 2, 3, 4,
 | |
|             5, 6, 7, 8,
 | |
|             9, 10, 11, 12 ].pack("l!*")
 | |
|     shape = [3, 4]
 | |
|     mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "l!", shape, nil)
 | |
|     assert_equal(1, mv[[0, 0]])
 | |
|     assert_equal(4, mv[[0, 3]])
 | |
|     assert_equal(6, mv[[1, 1]])
 | |
|     assert_equal(10, mv[[2, 1]])
 | |
|   end
 | |
| 
 | |
|   def test_rb_memory_view_get_item_pointer_multiple_members
 | |
|     buf = [ 1, 2,  3,  4,  5,  6,  7,  8,
 | |
|             -1, -2, -3, -4, -5, -6, -7, -8].pack("s*")
 | |
|     shape = [2, 4]
 | |
|     strides = [4*sizeof(:short)*2, sizeof(:short)*2]
 | |
|     mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides)
 | |
|     assert_equal([1, 2], mv[[0, 0]])
 | |
|     assert_equal([5, 6], mv[[0, 2]])
 | |
|     assert_equal([-1, -2], mv[[1, 0]])
 | |
|     assert_equal([-7, -8], mv[[1, 3]])
 | |
|   end
 | |
| 
 | |
|   def test_ractor
 | |
|     assert_in_out_err([], <<-"end;", ["[5, 6]", "[-7, -8]"], [])
 | |
|       require "-test-/memory_view"
 | |
|       require "rbconfig/sizeof"
 | |
|       $VERBOSE = nil
 | |
|       r = Ractor.new RbConfig::SIZEOF["short"] do |sizeof_short|
 | |
|         buf = [ 1, 2,  3,  4,  5,  6,  7,  8,
 | |
|                 -1, -2, -3, -4, -5, -6, -7, -8].pack("s*")
 | |
|         shape = [2, 4]
 | |
|         strides = [4*sizeof_short*2, sizeof_short*2]
 | |
|         mv = MemoryViewTestUtils::MultiDimensionalView.new(buf, "ss", shape, strides)
 | |
|         p mv[[0, 2]]
 | |
|         mv[[1, 3]]
 | |
|       end
 | |
|       p r.take
 | |
|     end;
 | |
|   end
 | |
| end
 | 
