Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Lib/test/test_memoryview.py
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,55 @@ def test_array_assign(self):
m[:] = new_a
self.assertEqual(a, new_a)

def test_compare_equal(self):
# A memoryview is equal to itself: there is no need to compare
# individual values. This is not true for float values since they can
# be NaN, and NaN is not equal to itself.

# Test integer formats
for int_format in 'bBhHiIlLqQ':
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can "?" be tested? Can format starting with "@" be tested? Can the null format be tested?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know how to test these formats. array.array doesn't support "P" and "?" formats and it doesn't support "@" byte order. Do you have an idea how to test these cases?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

memoryview.cast() supports them.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprisingly:

>>> memoryview(b'\0\1').cast('?') == memoryview(b'\0\2').cast('?')
False

even if

>>> list(memoryview(b'\0\1').cast('?')) == list(memoryview(b'\0\2').cast('?'))
True

But this may be platform depending, so I would not test values different than 0 and 1. Or 1 is also not safe?

It may be undefined behavior to interpret random values except 0 as void* (even if it works on x86). Maybe there is a way to create an array of pointers in ctypes? Or it is not worth to bother?

with self.subTest(format=int_format):
a = array.array(int_format, [1, 2, 3])
m = memoryview(a)
self.assertTrue(m == m)
Comment thread
vstinner marked this conversation as resolved.
Outdated
self.assertFalse(m != m)

if int_format in 'bB':
m2 = m.cast('@' + m.format)
self.assertTrue(m2 == m2)
self.assertFalse(m2 != m2)

# Test '?' format
m = memoryview(b'\0\1\2').cast('?')
self.assertTrue(m == m)
self.assertFalse(m != m)

# Test 'P' format
if struct.calcsize('L') == struct.calcsize('P'):
int_format = 'L'
elif struct.calcsize('Q') == struct.calcsize('P'):
int_format = 'Q'
else:
raise ValueError('unable to get void* format in struct')
a = array.array(int_format, [1, 2, 3])
m = memoryview(a.tobytes()).cast('P')
self.assertTrue(m == m)
self.assertFalse(m != m)

# Test float formats
for float_format in 'fd':
with self.subTest(format=float_format):
a = array.array(float_format, [1.0, 2.0, float('nan')])
m = memoryview(a)
# nan is not equal to nan
self.assertFalse(m == m)
Comment thread
vstinner marked this conversation as resolved.
Outdated
self.assertTrue(m != m)

a = array.array(float_format, [1.0, 2.0, 3.0])
m = memoryview(a)
self.assertTrue(m == m)
self.assertFalse(m != m)


class BytesMemorySliceTest(unittest.TestCase,
BaseMemorySliceTests, BaseBytesMemoryTests):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Optimize :class:`memoryview` comparison: a :class:`memoryview` is equal to
itself, there is no need to compare values. Patch by Victor Stinner.
26 changes: 26 additions & 0 deletions Objects/memoryobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -3122,6 +3122,32 @@ memory_richcompare(PyObject *v, PyObject *w, int op)
}
vv = VIEW_ADDR(v);

// For formats supported by the struct module a memoryview is equal to
// itself: there is no need to compare individual values.
// This is not true for float values since they can be NaN, and NaN
// is not equal to itself. So only use this optimization on format known to
// not use floats.
if (v == w) {
int can_compare_ptr;
const char *format = vv->format;
if (format != NULL) {
if (*format == '@') {
format++;
}
// Include only formats known by struct, exclude formats "d" (double),
// "f" (float), "e" (16-bit float)
can_compare_ptr = (strchr("bBchHiIlLnNPqQ?", format[0]) != NULL
Comment thread
vstinner marked this conversation as resolved.
Outdated
&& format[1] == 0);
}
else {
can_compare_ptr = 1;
}
if (can_compare_ptr) {
equal = 1;
goto result;
Comment thread
vstinner marked this conversation as resolved.
Outdated
}
}

if (PyMemoryView_Check(w)) {
if (BASE_INACCESSIBLE(w)) {
equal = (v == w);
Expand Down
Loading