1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
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:
.. code-block:: python
>>> 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``:
.. code-block:: python
>>> a[6] = 1
>>> v.tobytes()
b'C\xffC'
However, as ``a``'s buffer is shared, it is not possible to resize it:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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:
.. code-block:: python
>>> 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 <../examples/mmapped-file.py>`__
example.
|