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.
