Bitarray objects support the buffer protocol. They can both export their own buffer, as well as import another object's buffer.
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
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.