Skip to content

Latest commit

 

History

History
131 lines (98 loc) · 3.71 KB

buffer.rst

File metadata and controls

131 lines (98 loc) · 3.71 KB

Buffer protocol

Bitarray objects support the buffer protocol. They can both export their own buffer, as well as import another object's buffer.

Exporting buffers

Here is an example where the bitarray's buffer is exported:

>>> from bitarray import bitarray
>>> a = bitarray('01000001 01000010 01000011', endian='big')
>>> v = memoryview(a)
>>> v.tobytes()
b'ABC'
>>> v[1] = 255
>>> a
bitarray('010000011111111101000011')

Note that it is possible to change the shared buffer from both a and v:

>>> a[6] = 1
>>> v.tobytes()
b'C\xffC'

However, as a's buffer is shared, it is not possible to resize it:

>>> a.append(0)
Traceback (most recent call last):
    ...
BufferError: cannot resize bitarray that is exporting buffers

When exporting the buffer of a frozenbitarray, it is not possible to change its memoryview either:

>>> from bitarray import frozenbitarray
>>> a = frozenbitarray('01000001 01000010')
>>> v = memoryview(a)
>>> v.readonly
True
>>> v[0] = 15
Traceback (most recent call last):
    ...
TypeError: cannot modify read-only memory

Importing buffers

As of bitarray version 2.3, it is also possible to import the buffer from an object which exposes its buffer. Here the bytearray:

>>> c = bytearray([0x41, 0xff, 0x01])
>>> a = bitarray(buffer=c, endian='big')
>>> a
bitarray('010000011111111100000001')
>>> a <<= 3  # shift all bits by 3 to the left
>>> c
bytearray(b'\x0f\xf8\x08')
>>> a[20:] = 1
>>> a
bitarray('000011111111100000001111')

Again, the shared buffer can be represented and modify by either object a and c. When importing a buffer into a bitarray, the length of the bitarray will always be multiple of 8 bits, as buffers are bases on bytes. Also, we may specify the endianness of the bitarray:

>>> b = bitarray(buffer=c, endian='little')
>>> b
bitarray('111100000001111111110000')

The bytearray c is now exporting its buffer twice: to big-endian bitarray a, and a little-endian bitarray b. At this point all three object a, b and c share the same buffer. Using the .buffer_info() method, we can actually verify that the bitarrays a and b point to the same buffer address:

>>> assert a.buffer_info()[0] == b.buffer_info()[0]

As bitarray's expose their buffer, we can also directly create a bitarray which imports the buffer from another bitarray:

>>> a = bitarray(32)
>>> b = bitarray(buffer=a)
>>> # the buffer address is the same
>>> assert a.buffer_info()[0] == b.buffer_info()[0]
>>> a.setall(0)
>>> assert a == b
>>> b[::7] = 1
>>> assert a == b
>>> a
bitarray('10000001000000100000010000001000')

We can also create bitarrays which share part of the buffer. Let's create a large bitarray a, and then have b and c share different portions of a's buffer:

>>> a = bitarray(1 << 23)
>>> a.setall(0)
>>> b = bitarray(buffer=memoryview(a)[0x10000:0x30000])
>>> assert a.buffer_info()[0] + 0x10000 == b.buffer_info()[0]
>>> c = bitarray(buffer=memoryview(a)[0x20000:0x50000])
>>> assert a.buffer_info()[0] + 0x20000 == c.buffer_info()[0]
>>> c[0] = 1
>>> assert b[8 * 0x10000] == 1
>>> assert a[8 * 0x20000] == 1

Finally, importing buffers allows creating bitarrays that are memory mapped to a file. Please see the mmapped-file.py example.