def test_ops(self):
        a = (b'01234567'
             b'456789ab'
             b'89abcdef')
        v = memoryview(a).cast('b', shape=[3, 8])

        b = containers.StridedArrayView2D(v).transposed(0, 1).flipped(0)
        self.assertEqual(b.size, (8, 3))
        self.assertEqual(b.stride, (-1, 8))
        self.assertEqual(bytes(b), b'7bf6ae59d48c37b26a159048')

        c = containers.StridedArrayView2D(v).transposed(1, 0).flipped(1)
        self.assertEqual(c.size, (8, 3))
        self.assertEqual(c.stride, (1, -8))
        self.assertEqual(bytes(c), b'840951a62b73c84d95ea6fb7')

        d = containers.StridedArrayView2D(v).transposed(0, 1)[3:4].broadcasted(0, 5)
        self.assertEqual(d.size, (5, 3))
        self.assertEqual(d.stride, (0, 8))
        self.assertEqual(bytes(d), b'37b37b37b37b37b')

        d = containers.StridedArrayView2D(v)[:, 3:4].broadcasted(1, 2)
        self.assertEqual(d.size, (3, 2))
        self.assertEqual(d.stride, (8, 0))
        self.assertEqual(bytes(d), b'3377bb')
    def test_slice_multidimensional(self):
        a = memoryview(b'01234567'
                       b'456789ab'
                       b'89abcdef').cast('b', shape=[3, 8])
        a_refcount = sys.getrefcount(a)

        b = containers.StridedArrayView2D(a)
        b_refcount = sys.getrefcount(b)
        # memoryview's buffer protocol returns itself, not the underlying
        # bytes, as it manages the Py_buffer instance. So this is expected.
        self.assertIs(b.owner, a)
        self.assertEqual(sys.getrefcount(a), a_refcount + 1)

        # When slicing, b's refcount should not change but a's refcount should
        # increase
        c = b[1:3, 4:7]
        self.assertIsInstance(c, containers.StridedArrayView2D)
        self.assertEqual(c.size, (2, 3))
        self.assertEqual(c.stride, (8, 1))
        self.assertEqual(bytes(c[0]), b'89a')
        self.assertEqual(bytes(c[1]), b'cde')
        self.assertEqual(sys.getrefcount(b), b_refcount)
        self.assertEqual(sys.getrefcount(a), a_refcount + 2)

        # Deleting a slice should reduce a's refcount again, keep b's unchanged
        del c
        self.assertEqual(sys.getrefcount(b), b_refcount)
        self.assertEqual(sys.getrefcount(a), a_refcount + 1)
    def test_init_buffer(self):
        a = (b'01234567' b'456789ab' b'89abcdef')
        a_refcount = sys.getrefcount(a)

        v = memoryview(a).cast('b', shape=[3, 8])
        b = containers.StridedArrayView2D(v)
        self.assertEqual(len(b), 3)
        self.assertEqual(bytes(b), b'01234567' b'456789ab' b'89abcdef')
        self.assertEqual(b.size, (3, 8))
        self.assertEqual(b.stride, (8, 1))
        self.assertIsInstance(b[1], containers.StridedArrayView1D)
        self.assertEqual(bytes(b[1]), b'456789ab')
        self.assertEqual(b[1, 2], '6')
        self.assertEqual(b[1][2], '6')
        self.assertEqual(sys.getrefcount(a), a_refcount + 1)

        # Not mutable
        with self.assertRaisesRegex(TypeError,
                                    "object does not support item assignment"):
            b[1, 2] = '!'

        # b should keep a reference to a, so deleting the local reference
        # shouldn't affect it
        del a
        self.assertTrue(sys.getrefcount(b.owner), a_refcount)
        self.assertEqual(b[1][2], '6')

        # Now, if we delete b, a should not be referenced by anything anymore
        a = b.owner
        del b
        self.assertTrue(sys.getrefcount(a), a_refcount)
    def test_slice_multidimensional_empty(self):
        a = memoryview(b'01234567'
                       b'456789ab'
                       b'89abcdef').cast('b', shape=[3, 8])
        a_refcount = sys.getrefcount(a)

        b = containers.StridedArrayView2D(a)[1:1, 2:2]
        self.assertEqual(b.size, (0, 0))

        # Empty view, original data not referenced at all
        self.assertIs(b.owner, None)
        self.assertEqual(sys.getrefcount(a), a_refcount)
 def test_init_buffer_stride(self):
     a = memoryview(b'01234567'
                    b'456789ab'
                    b'89abcdef').cast('b', shape=[3, 8])[::2]
     self.assertEqual(bytes(a), b'0123456789abcdef')
     b = containers.StridedArrayView2D(a)
     self.assertEqual(len(b), 2)
     self.assertEqual(bytes(b), b'0123456789abcdef')
     self.assertEqual(b.size, (2, 8))
     self.assertEqual(b.stride, (16, 1))
     self.assertEqual(bytes(b[1]), b'89abcdef')
     self.assertEqual(b[1][3], 'b')
 def test_init(self):
     a = containers.StridedArrayView2D()
     b = containers.MutableStridedArrayView2D()
     self.assertIs(a.owner, None)
     self.assertIs(b.owner, None)
     self.assertEqual(len(a), 0)
     self.assertEqual(len(b), 0)
     self.assertEqual(bytes(a), b'')
     self.assertEqual(bytes(b), b'')
     self.assertEqual(a.size, (0, 0))
     self.assertEqual(b.size, (0, 0))
     self.assertEqual(a.stride, (0, 0))
     self.assertEqual(b.stride, (0, 0))
     self.assertEqual(a.dimensions, 2)
     self.assertEqual(b.dimensions, 2)
    def test_slice_stride(self):
        a = (b'01234567' b'456789ab' b'89abcdef')
        v = memoryview(a).cast('b', shape=[3, 8])
        b = containers.StridedArrayView2D(v)

        # Check consistency with slices on memoryview
        c1 = v[0:3:2]
        c2 = b[0:3:2]
        self.assertEqual(len(c1), 2)
        self.assertEqual(len(c2), 2)
        self.assertIsInstance(c2, containers.StridedArrayView2D)
        self.assertEqual(bytes(c1), b'0123456789abcdef')
        self.assertEqual(bytes(c2), b'0123456789abcdef')
        self.assertEqual(c2.size, (2, 8))
        self.assertEqual(c2.stride, (16, 1))
        self.assertEqual(bytes(c2[1]), b'89abcdef')
    def test_slice_stride_negative_multidimensional(self):
        a = (b'01234567' b'456789ab' b'89abcdef')
        v = memoryview(a).cast('b', shape=[3, 8])
        b = containers.StridedArrayView2D(v)

        # Check consistency with slices on memoryview
        self.assertEqual(v.shape, (3, 8))
        self.assertEqual(b.size, (3, 8))
        self.assertEqual(v.strides, (8, 1))
        self.assertEqual(b.stride, (8, 1))

        with self.assertRaises(NotImplementedError):
            c1 = v[-1:None:-2, -2:2:-3]  # HAH!

        c2 = b[-1:None:-2, -2:2:-3]
        self.assertEqual(len(c2), 2)
        self.assertEqual(bytes(c2), b'eb63')
        self.assertEqual(c2.size, (2, 2))
        self.assertEqual(c2.stride, (-16, -3))
    def test_convert_memoryview(self):
        a = memoryview(b'01234567'
                       b'456789ab'
                       b'89abcdef').cast('b', shape=[3, 8])
        a_refcount = sys.getrefcount(a)

        b = containers.StridedArrayView2D(a)
        b_refcount = sys.getrefcount(b)
        # memoryview's buffer protocol returns itself, not the underlying
        # bytes, as it manages the Py_buffer instance. So this is expected.
        self.assertIs(b.owner, a)
        self.assertEqual(sys.getrefcount(a), a_refcount + 1)

        c = memoryview(b)
        self.assertEqual(c.ndim, 2)
        self.assertEqual(c.shape, (3, 8))
        self.assertEqual(c.strides, (8, 1))
        self.assertIs(c.obj, b)
        self.assertEqual(sys.getrefcount(a), a_refcount + 1)
        self.assertEqual(sys.getrefcount(b), b_refcount + 1)
    def test_slice_stride_negative(self):
        a = (b'01234567' b'456789ab' b'89abcdef')
        v = memoryview(a).cast('b', shape=[3, 8])
        b = containers.StridedArrayView2D(v)

        # Check consistency with slices on memoryview
        self.assertEqual(v.shape, (3, 8))
        self.assertEqual(b.size, (3, 8))
        self.assertEqual(v.strides, (8, 1))
        self.assertEqual(b.stride, (8, 1))
        c1 = v[-1:None:-2]  # like [0:3:2] above, but reverted
        c2 = b[-1:None:-2]
        self.assertEqual(len(c1), 2)
        self.assertEqual(len(c2), 2)
        self.assertEqual(bytes(c1),
                         b'89abcdef01234567')  # like above but reverted
        self.assertEqual(bytes(c2), b'89abcdef01234567')
        self.assertEqual(c1.shape, (2, 8))
        self.assertEqual(c2.size, (2, 8))
        self.assertEqual(c1.strides, (-16, 1))
        self.assertEqual(c2.stride, (-16, 1))
 def test_init_buffer_unexpected_dimensions(self):
     a = b'123456'
     with self.assertRaisesRegex(BufferError,
                                 "expected 2 dimensions but got 1"):
         b = containers.StridedArrayView2D(a)