summaryrefslogtreecommitdiff
path: root/python/bitstring
diff options
context:
space:
mode:
Diffstat (limited to 'python/bitstring')
-rw-r--r--python/bitstring/PKG-INFO122
-rw-r--r--python/bitstring/README.txt99
-rw-r--r--python/bitstring/bitstring.py4234
-rw-r--r--python/bitstring/doc/bitstring_manual.pdfbin0 -> 439327 bytes
-rw-r--r--python/bitstring/release_notes.txt1523
-rw-r--r--python/bitstring/setup.py44
-rw-r--r--python/bitstring/test/smalltestfile1
-rw-r--r--python/bitstring/test/test.m1vbin0 -> 125300 bytes
-rw-r--r--python/bitstring/test/test_bitarray.py310
-rw-r--r--python/bitstring/test/test_bits.py378
-rw-r--r--python/bitstring/test/test_bitstore.py37
-rw-r--r--python/bitstring/test/test_bitstream.py3940
-rw-r--r--python/bitstring/test/test_bitstring.py97
-rw-r--r--python/bitstring/test/test_constbitstream.py121
14 files changed, 10906 insertions, 0 deletions
diff --git a/python/bitstring/PKG-INFO b/python/bitstring/PKG-INFO
new file mode 100644
index 0000000000..1036c45d79
--- /dev/null
+++ b/python/bitstring/PKG-INFO
@@ -0,0 +1,122 @@
+Metadata-Version: 1.1
+Name: bitstring
+Version: 3.1.3
+Summary: Simple construction, analysis and modification of binary data.
+Home-page: http://python-bitstring.googlecode.com
+Author: Scott Griffiths
+Author-email: scott@griffiths.name
+License: The MIT License: http://www.opensource.org/licenses/mit-license.php
+Download-URL: http://python-bitstring.googlecode.com
+Description: ================
+ bitstring module
+ ================
+
+ **bitstring** is a pure Python module designed to help make
+ the creation and analysis of binary data as simple and natural as possible.
+
+ Bitstrings can be constructed from integers (big and little endian), hex,
+ octal, binary, strings or files. They can be sliced, joined, reversed,
+ inserted into, overwritten, etc. with simple functions or slice notation.
+ They can also be read from, searched and replaced, and navigated in,
+ similar to a file or stream.
+
+ bitstring is open source software, and has been released under the MIT
+ licence.
+
+ This version supports Python 2.6 and later (including Python 3).
+ For Python 2.4 and 2.5 you should instead download version 1.0.
+
+ Documentation
+ -------------
+ The manual for the bitstring module is available here
+ <http://packages.python.org/bitstring>. It contains a walk-through of all
+ the features and a complete reference section.
+
+ It is also available as a PDF as part of the source download.
+
+ Installation
+ ------------
+ If you have downloaded and unzipped the package then you need to run the
+ ``setup.py`` script with the 'install' argument::
+
+ python setup.py install
+
+ You may need to run this with root privileges on Unix-like systems.
+
+
+ If you haven't yet downloaded the package then you can just try::
+
+ easy_install bitstring
+
+ or ::
+
+ pip install bitstring
+
+
+ Simple Examples
+ ---------------
+ Creation::
+
+ >>> a = BitArray(bin='00101')
+ >>> b = Bits(a_file_object)
+ >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22')
+ >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f')
+ >>> e = pack('<16h', *range(16))
+
+ Different interpretations, slicing and concatenation::
+
+ >>> a = BitArray('0x1af')
+ >>> a.hex, a.bin, a.uint
+ ('1af', '000110101111', 431)
+ >>> a[10:3:-1].bin
+ '1110101'
+ >>> 3*a + '0b100'
+ BitArray('0o0657056705674')
+
+ Reading data sequentially::
+
+ >>> b = BitStream('0x160120f')
+ >>> b.read(12).hex
+ '160'
+ >>> b.pos = 0
+ >>> b.read('uint:12')
+ 352
+ >>> b.readlist('uint:12, bin:3')
+ [288, '111']
+
+ Searching, inserting and deleting::
+
+ >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f'
+ >>> c.find('0x48')
+ (8,)
+ >>> c.replace('0b001', '0xabc')
+ >>> c.insert('0b0000')
+ >>> del c[12:16]
+
+ Unit Tests
+ ----------
+
+ The 400+ unit tests should all pass for Python 2.6 and later.
+
+ ----
+
+ The bitstring module has been released as open source under the MIT License.
+ Copyright (c) 2014 Scott Griffiths
+
+ For more information see the project's homepage on Google Code:
+ <http://python-bitstring.googlecode.com>
+
+
+Platform: all
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Intended Audience :: Developers
+Classifier: Operating System :: OS Independent
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Programming Language :: Python :: 2.6
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.0
+Classifier: Programming Language :: Python :: 3.1
+Classifier: Programming Language :: Python :: 3.2
+Classifier: Programming Language :: Python :: 3.3
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
diff --git a/python/bitstring/README.txt b/python/bitstring/README.txt
new file mode 100644
index 0000000000..491c2f8cf8
--- /dev/null
+++ b/python/bitstring/README.txt
@@ -0,0 +1,99 @@
+================
+bitstring module
+================
+
+**bitstring** is a pure Python module designed to help make
+the creation and analysis of binary data as simple and natural as possible.
+
+Bitstrings can be constructed from integers (big and little endian), hex,
+octal, binary, strings or files. They can be sliced, joined, reversed,
+inserted into, overwritten, etc. with simple functions or slice notation.
+They can also be read from, searched and replaced, and navigated in,
+similar to a file or stream.
+
+bitstring is open source software, and has been released under the MIT
+licence.
+
+This version supports Python 2.6 and later (including Python 3).
+For Python 2.4 and 2.5 you should instead download version 1.0.
+
+Documentation
+-------------
+The manual for the bitstring module is available here
+<http://packages.python.org/bitstring>. It contains a walk-through of all
+the features and a complete reference section.
+
+It is also available as a PDF as part of the source download.
+
+Installation
+------------
+If you have downloaded and unzipped the package then you need to run the
+``setup.py`` script with the 'install' argument::
+
+ python setup.py install
+
+You may need to run this with root privileges on Unix-like systems.
+
+
+If you haven't yet downloaded the package then you can just try::
+
+ easy_install bitstring
+
+or ::
+
+ pip install bitstring
+
+
+Simple Examples
+---------------
+Creation::
+
+ >>> a = BitArray(bin='00101')
+ >>> b = Bits(a_file_object)
+ >>> c = BitArray('0xff, 0b101, 0o65, uint:6=22')
+ >>> d = pack('intle:16, hex=a, 0b1', 100, a='0x34f')
+ >>> e = pack('<16h', *range(16))
+
+Different interpretations, slicing and concatenation::
+
+ >>> a = BitArray('0x1af')
+ >>> a.hex, a.bin, a.uint
+ ('1af', '000110101111', 431)
+ >>> a[10:3:-1].bin
+ '1110101'
+ >>> 3*a + '0b100'
+ BitArray('0o0657056705674')
+
+Reading data sequentially::
+
+ >>> b = BitStream('0x160120f')
+ >>> b.read(12).hex
+ '160'
+ >>> b.pos = 0
+ >>> b.read('uint:12')
+ 352
+ >>> b.readlist('uint:12, bin:3')
+ [288, '111']
+
+Searching, inserting and deleting::
+
+ >>> c = BitArray('0b00010010010010001111') # c.hex == '0x1248f'
+ >>> c.find('0x48')
+ (8,)
+ >>> c.replace('0b001', '0xabc')
+ >>> c.insert('0b0000')
+ >>> del c[12:16]
+
+Unit Tests
+----------
+
+The 400+ unit tests should all pass for Python 2.6 and later.
+
+----
+
+The bitstring module has been released as open source under the MIT License.
+Copyright (c) 2014 Scott Griffiths
+
+For more information see the project's homepage on Google Code:
+<http://python-bitstring.googlecode.com>
+
diff --git a/python/bitstring/bitstring.py b/python/bitstring/bitstring.py
new file mode 100644
index 0000000000..86f969c7f2
--- /dev/null
+++ b/python/bitstring/bitstring.py
@@ -0,0 +1,4234 @@
+#!/usr/bin/env python
+# cython: profile=True
+"""
+This package defines classes that simplify bit-wise creation, manipulation and
+interpretation of data.
+
+Classes:
+
+Bits -- An immutable container for binary data.
+BitArray -- A mutable container for binary data.
+ConstBitStream -- An immutable container with streaming methods.
+BitStream -- A mutable container with streaming methods.
+
+ Bits (base class)
+ / \
+ + mutating methods / \ + streaming methods
+ / \
+ BitArray ConstBitStream
+ \ /
+ \ /
+ \ /
+ BitStream
+
+Functions:
+
+pack -- Create a BitStream from a format string.
+
+Exceptions:
+
+Error -- Module exception base class.
+CreationError -- Error during creation.
+InterpretError -- Inappropriate interpretation of binary data.
+ByteAlignError -- Whole byte position or length needed.
+ReadError -- Reading or peeking past the end of a bitstring.
+
+http://python-bitstring.googlecode.com
+"""
+
+__licence__ = """
+The MIT License
+
+Copyright (c) 2006-2014 Scott Griffiths (scott@griffiths.name)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
+"""
+
+__version__ = "3.1.3"
+
+__author__ = "Scott Griffiths"
+
+import numbers
+import copy
+import sys
+import re
+import binascii
+import mmap
+import os
+import struct
+import operator
+import collections
+
+byteorder = sys.byteorder
+
+bytealigned = False
+"""Determines whether a number of methods default to working only on byte boundaries."""
+
+# Maximum number of digits to use in __str__ and __repr__.
+MAX_CHARS = 250
+
+# Maximum size of caches used for speed optimisations.
+CACHE_SIZE = 1000
+
+class Error(Exception):
+ """Base class for errors in the bitstring module."""
+
+ def __init__(self, *params):
+ self.msg = params[0] if params else ''
+ self.params = params[1:]
+
+ def __str__(self):
+ if self.params:
+ return self.msg.format(*self.params)
+ return self.msg
+
+
+class ReadError(Error, IndexError):
+ """Reading or peeking past the end of a bitstring."""
+
+ def __init__(self, *params):
+ Error.__init__(self, *params)
+
+
+class InterpretError(Error, ValueError):
+ """Inappropriate interpretation of binary data."""
+
+ def __init__(self, *params):
+ Error.__init__(self, *params)
+
+
+class ByteAlignError(Error):
+ """Whole-byte position or length needed."""
+
+ def __init__(self, *params):
+ Error.__init__(self, *params)
+
+
+class CreationError(Error, ValueError):
+ """Inappropriate argument during bitstring creation."""
+
+ def __init__(self, *params):
+ Error.__init__(self, *params)
+
+
+class ConstByteStore(object):
+ """Stores raw bytes together with a bit offset and length.
+
+ Used internally - not part of public interface.
+ """
+
+ __slots__ = ('offset', '_rawarray', 'bitlength')
+
+ def __init__(self, data, bitlength=None, offset=None):
+ """data is either a bytearray or a MmapByteArray"""
+ self._rawarray = data
+ if offset is None:
+ offset = 0
+ if bitlength is None:
+ bitlength = 8 * len(data) - offset
+ self.offset = offset
+ self.bitlength = bitlength
+
+ def getbit(self, pos):
+ assert 0 <= pos < self.bitlength
+ byte, bit = divmod(self.offset + pos, 8)
+ return bool(self._rawarray[byte] & (128 >> bit))
+
+ def getbyte(self, pos):
+ """Direct access to byte data."""
+ return self._rawarray[pos]
+
+ def getbyteslice(self, start, end):
+ """Direct access to byte data."""
+ c = self._rawarray[start:end]
+ return c
+
+ @property
+ def bytelength(self):
+ if not self.bitlength:
+ return 0
+ sb = self.offset // 8
+ eb = (self.offset + self.bitlength - 1) // 8
+ return eb - sb + 1
+
+ def __copy__(self):
+ return ByteStore(self._rawarray[:], self.bitlength, self.offset)
+
+ def _appendstore(self, store):
+ """Join another store on to the end of this one."""
+ if not store.bitlength:
+ return
+ # Set new array offset to the number of bits in the final byte of current array.
+ store = offsetcopy(store, (self.offset + self.bitlength) % 8)
+ if store.offset:
+ # first do the byte with the join.
+ joinval = (self._rawarray.pop() & (255 ^ (255 >> store.offset)) |
+ (store.getbyte(0) & (255 >> store.offset)))
+ self._rawarray.append(joinval)
+ self._rawarray.extend(store._rawarray[1:])
+ else:
+ self._rawarray.extend(store._rawarray)
+ self.bitlength += store.bitlength
+
+ def _prependstore(self, store):
+ """Join another store on to the start of this one."""
+ if not store.bitlength:
+ return
+ # Set the offset of copy of store so that it's final byte
+ # ends in a position that matches the offset of self,
+ # then join self on to the end of it.
+ store = offsetcopy(store, (self.offset - store.bitlength) % 8)
+ assert (store.offset + store.bitlength) % 8 == self.offset % 8
+ bit_offset = self.offset % 8
+ if bit_offset:
+ # first do the byte with the join.
+ store.setbyte(-1, (store.getbyte(-1) & (255 ^ (255 >> bit_offset)) | \
+ (self._rawarray[self.byteoffset] & (255 >> bit_offset))))
+ store._rawarray.extend(self._rawarray[self.byteoffset + 1: self.byteoffset + self.bytelength])
+ else:
+ store._rawarray.extend(self._rawarray[self.byteoffset: self.byteoffset + self.bytelength])
+ self._rawarray = store._rawarray
+ self.offset = store.offset
+ self.bitlength += store.bitlength
+
+ @property
+ def byteoffset(self):
+ return self.offset // 8
+
+ @property
+ def rawbytes(self):
+ return self._rawarray
+
+
+class ByteStore(ConstByteStore):
+ """Adding mutating methods to ConstByteStore
+
+ Used internally - not part of public interface.
+ """
+ __slots__ = ()
+
+ def setbit(self, pos):
+ assert 0 <= pos < self.bitlength
+ byte, bit = divmod(self.offset + pos, 8)
+ self._rawarray[byte] |= (128 >> bit)
+
+ def unsetbit(self, pos):
+ assert 0 <= pos < self.bitlength
+ byte, bit = divmod(self.offset + pos, 8)
+ self._rawarray[byte] &= ~(128 >> bit)
+
+ def invertbit(self, pos):
+ assert 0 <= pos < self.bitlength
+ byte, bit = divmod(self.offset + pos, 8)
+ self._rawarray[byte] ^= (128 >> bit)
+
+ def setbyte(self, pos, value):
+ self._rawarray[pos] = value
+
+ def setbyteslice(self, start, end, value):
+ self._rawarray[start:end] = value
+
+
+def offsetcopy(s, newoffset):
+ """Return a copy of a ByteStore with the newoffset.
+
+ Not part of public interface.
+ """
+ assert 0 <= newoffset < 8
+ if not s.bitlength:
+ return copy.copy(s)
+ else:
+ if newoffset == s.offset % 8:
+ return ByteStore(s.getbyteslice(s.byteoffset, s.byteoffset + s.bytelength), s.bitlength, newoffset)
+ newdata = []
+ d = s._rawarray
+ assert newoffset != s.offset % 8
+ if newoffset < s.offset % 8:
+ # We need to shift everything left
+ shiftleft = s.offset % 8 - newoffset
+ # First deal with everything except for the final byte
+ for x in range(s.byteoffset, s.byteoffset + s.bytelength - 1):
+ newdata.append(((d[x] << shiftleft) & 0xff) +\
+ (d[x + 1] >> (8 - shiftleft)))
+ bits_in_last_byte = (s.offset + s.bitlength) % 8
+ if not bits_in_last_byte:
+ bits_in_last_byte = 8
+ if bits_in_last_byte > shiftleft:
+ newdata.append((d[s.byteoffset + s.bytelength - 1] << shiftleft) & 0xff)
+ else: # newoffset > s._offset % 8
+ shiftright = newoffset - s.offset % 8
+ newdata.append(s.getbyte(0) >> shiftright)
+ for x in range(s.byteoffset + 1, s.byteoffset + s.bytelength):
+ newdata.append(((d[x - 1] << (8 - shiftright)) & 0xff) +\
+ (d[x] >> shiftright))
+ bits_in_last_byte = (s.offset + s.bitlength) % 8
+ if not bits_in_last_byte:
+ bits_in_last_byte = 8
+ if bits_in_last_byte + shiftright > 8:
+ newdata.append((d[s.byteoffset + s.bytelength - 1] << (8 - shiftright)) & 0xff)
+ new_s = ByteStore(bytearray(newdata), s.bitlength, newoffset)
+ assert new_s.offset == newoffset
+ return new_s
+
+
+def equal(a, b):
+ """Return True if ByteStores a == b.
+
+ Not part of public interface.
+ """
+ # We want to return False for inequality as soon as possible, which
+ # means we get lots of special cases.
+ # First the easy one - compare lengths:
+ a_bitlength = a.bitlength
+ b_bitlength = b.bitlength
+ if a_bitlength != b_bitlength:
+ return False
+ if not a_bitlength:
+ assert b_bitlength == 0
+ return True
+ # Make 'a' the one with the smaller offset
+ if (a.offset % 8) > (b.offset % 8):
+ a, b = b, a
+ # and create some aliases
+ a_bitoff = a.offset % 8
+ b_bitoff = b.offset % 8
+ a_byteoffset = a.byteoffset
+ b_byteoffset = b.byteoffset
+ a_bytelength = a.bytelength
+ b_bytelength = b.bytelength
+ da = a._rawarray
+ db = b._rawarray
+
+ # If they are pointing to the same data, they must be equal
+ if da is db and a.offset == b.offset:
+ return True
+
+ if a_bitoff == b_bitoff:
+ bits_spare_in_last_byte = 8 - (a_bitoff + a_bitlength) % 8
+ if bits_spare_in_last_byte == 8:
+ bits_spare_in_last_byte = 0
+ # Special case for a, b contained in a single byte
+ if a_bytelength == 1:
+ a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength)
+ b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength)
+ return a_val == b_val
+ # Otherwise check first byte
+ if da[a_byteoffset] & (0xff >> a_bitoff) != db[b_byteoffset] & (0xff >> b_bitoff):
+ return False
+ # then everything up to the last
+ b_a_offset = b_byteoffset - a_byteoffset
+ for x in range(1 + a_byteoffset, a_byteoffset + a_bytelength - 1):
+ if da[x] != db[b_a_offset + x]:
+ return False
+ # and finally the last byte
+ return (da[a_byteoffset + a_bytelength - 1] >> bits_spare_in_last_byte ==
+ db[b_byteoffset + b_bytelength - 1] >> bits_spare_in_last_byte)
+
+ assert a_bitoff != b_bitoff
+ # This is how much we need to shift a to the right to compare with b:
+ shift = b_bitoff - a_bitoff
+ # Special case for b only one byte long
+ if b_bytelength == 1:
+ assert a_bytelength == 1
+ a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength)
+ b_val = ((db[b_byteoffset] << b_bitoff) & 0xff) >> (8 - b_bitlength)
+ return a_val == b_val
+ # Special case for a only one byte long
+ if a_bytelength == 1:
+ assert b_bytelength == 2
+ a_val = ((da[a_byteoffset] << a_bitoff) & 0xff) >> (8 - a_bitlength)
+ b_val = ((db[b_byteoffset] << 8) + db[b_byteoffset + 1]) << b_bitoff
+ b_val &= 0xffff
+ b_val >>= 16 - b_bitlength
+ return a_val == b_val
+
+ # Compare first byte of b with bits from first byte of a
+ if (da[a_byteoffset] & (0xff >> a_bitoff)) >> shift != db[b_byteoffset] & (0xff >> b_bitoff):
+ return False
+ # Now compare every full byte of b with bits from 2 bytes of a
+ for x in range(1, b_bytelength - 1):
+ # Construct byte from 2 bytes in a to compare to byte in b
+ b_val = db[b_byteoffset + x]
+ a_val = ((da[a_byteoffset + x - 1] << 8) + da[a_byteoffset + x]) >> shift
+ a_val &= 0xff
+ if a_val != b_val:
+ return False
+
+ # Now check bits in final byte of b
+ final_b_bits = (b.offset + b_bitlength) % 8
+ if not final_b_bits:
+ final_b_bits = 8
+ b_val = db[b_byteoffset + b_bytelength - 1] >> (8 - final_b_bits)
+ final_a_bits = (a.offset + a_bitlength) % 8
+ if not final_a_bits:
+ final_a_bits = 8
+ if b.bytelength > a_bytelength:
+ assert b_bytelength == a_bytelength + 1
+ a_val = da[a_byteoffset + a_bytelength - 1] >> (8 - final_a_bits)
+ a_val &= 0xff >> (8 - final_b_bits)
+ return a_val == b_val
+ assert a_bytelength == b_bytelength
+ a_val = da[a_byteoffset + a_bytelength - 2] << 8
+ a_val += da[a_byteoffset + a_bytelength - 1]
+ a_val >>= (8 - final_a_bits)
+ a_val &= 0xff >> (8 - final_b_bits)
+ return a_val == b_val
+
+
+class MmapByteArray(object):
+ """Looks like a bytearray, but from an mmap.
+
+ Not part of public interface.
+ """
+
+ __slots__ = ('filemap', 'filelength', 'source', 'byteoffset', 'bytelength')
+
+ def __init__(self, source, bytelength=None, byteoffset=None):
+ self.source = source
+ source.seek(0, os.SEEK_END)
+ self.filelength = source.tell()
+ if byteoffset is None:
+ byteoffset = 0
+ if bytelength is None:
+ bytelength = self.filelength - byteoffset
+ self.byteoffset = byteoffset
+ self.bytelength = bytelength
+ self.filemap = mmap.mmap(source.fileno(), 0, access=mmap.ACCESS_READ)
+
+ def __getitem__(self, key):
+ try:
+ start = key.start
+ stop = key.stop
+ except AttributeError:
+ try:
+ assert 0 <= key < self.bytelength
+ return ord(self.filemap[key + self.byteoffset])
+ except TypeError:
+ # for Python 3
+ return self.filemap[key + self.byteoffset]
+ else:
+ if start is None:
+ start = 0
+ if stop is None:
+ stop = self.bytelength
+ assert key.step is None
+ assert 0 <= start < self.bytelength
+ assert 0 <= stop <= self.bytelength
+ s = slice(start + self.byteoffset, stop + self.byteoffset)
+ return bytearray(self.filemap.__getitem__(s))
+
+ def __len__(self):
+ return self.bytelength
+
+
+# This creates a dictionary for every possible byte with the value being
+# the key with its bits reversed.
+BYTE_REVERSAL_DICT = dict()
+
+# For Python 2.x/ 3.x coexistence
+# Yes this is very very hacky.
+try:
+ xrange
+ for i in range(256):
+ BYTE_REVERSAL_DICT[i] = chr(int("{0:08b}".format(i)[::-1], 2))
+except NameError:
+ for i in range(256):
+ BYTE_REVERSAL_DICT[i] = bytes([int("{0:08b}".format(i)[::-1], 2)])
+ from io import IOBase as file
+ xrange = range
+ basestring = str
+
+# Python 2.x octals start with '0', in Python 3 it's '0o'
+LEADING_OCT_CHARS = len(oct(1)) - 1
+
+def tidy_input_string(s):
+ """Return string made lowercase and with all whitespace removed."""
+ s = ''.join(s.split()).lower()
+ return s
+
+INIT_NAMES = ('uint', 'int', 'ue', 'se', 'sie', 'uie', 'hex', 'oct', 'bin', 'bits',
+ 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne',
+ 'float', 'floatbe', 'floatle', 'floatne', 'bytes', 'bool', 'pad')
+
+TOKEN_RE = re.compile(r'(?P<name>' + '|'.join(INIT_NAMES) +
+ r')((:(?P<len>[^=]+)))?(=(?P<value>.*))?$', re.IGNORECASE)
+DEFAULT_UINT = re.compile(r'(?P<len>[^=]+)?(=(?P<value>.*))?$', re.IGNORECASE)
+
+MULTIPLICATIVE_RE = re.compile(r'(?P<factor>.*)\*(?P<token>.+)')
+
+# Hex, oct or binary literals
+LITERAL_RE = re.compile(r'(?P<name>0(x|o|b))(?P<value>.+)', re.IGNORECASE)
+
+# An endianness indicator followed by one or more struct.pack codes
+STRUCT_PACK_RE = re.compile(r'(?P<endian><|>|@)?(?P<fmt>(?:\d*[bBhHlLqQfd])+)$')
+
+# A number followed by a single character struct.pack code
+STRUCT_SPLIT_RE = re.compile(r'\d*[bBhHlLqQfd]')
+
+# These replicate the struct.pack codes
+# Big-endian
+REPLACEMENTS_BE = {'b': 'intbe:8', 'B': 'uintbe:8',
+ 'h': 'intbe:16', 'H': 'uintbe:16',
+ 'l': 'intbe:32', 'L': 'uintbe:32',
+ 'q': 'intbe:64', 'Q': 'uintbe:64',
+ 'f': 'floatbe:32', 'd': 'floatbe:64'}
+# Little-endian
+REPLACEMENTS_LE = {'b': 'intle:8', 'B': 'uintle:8',
+ 'h': 'intle:16', 'H': 'uintle:16',
+ 'l': 'intle:32', 'L': 'uintle:32',
+ 'q': 'intle:64', 'Q': 'uintle:64',
+ 'f': 'floatle:32', 'd': 'floatle:64'}
+
+# Size in bytes of all the pack codes.
+PACK_CODE_SIZE = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4, 'L': 4,
+ 'q': 8, 'Q': 8, 'f': 4, 'd': 8}
+
+_tokenname_to_initialiser = {'hex': 'hex', '0x': 'hex', '0X': 'hex', 'oct': 'oct',
+ '0o': 'oct', '0O': 'oct', 'bin': 'bin', '0b': 'bin',
+ '0B': 'bin', 'bits': 'auto', 'bytes': 'bytes', 'pad': 'pad'}
+
+def structparser(token):
+ """Parse struct-like format string token into sub-token list."""
+ m = STRUCT_PACK_RE.match(token)
+ if not m:
+ return [token]
+ else:
+ endian = m.group('endian')
+ if endian is None:
+ return [token]
+ # Split the format string into a list of 'q', '4h' etc.
+ formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt'))
+ # Now deal with mulitiplicative factors, 4h -> hhhh etc.
+ fmt = ''.join([f[-1] * int(f[:-1]) if len(f) != 1 else
+ f for f in formatlist])
+ if endian == '@':
+ # Native endianness
+ if byteorder == 'little':
+ endian = '<'
+ else:
+ assert byteorder == 'big'
+ endian = '>'
+ if endian == '<':
+ tokens = [REPLACEMENTS_LE[c] for c in fmt]
+ else:
+ assert endian == '>'
+ tokens = [REPLACEMENTS_BE[c] for c in fmt]
+ return tokens
+
+def tokenparser(fmt, keys=None, token_cache={}):
+ """Divide the format string into tokens and parse them.
+
+ Return stretchy token and list of [initialiser, length, value]
+ initialiser is one of: hex, oct, bin, uint, int, se, ue, 0x, 0o, 0b etc.
+ length is None if not known, as is value.
+
+ If the token is in the keyword dictionary (keys) then it counts as a
+ special case and isn't messed with.
+
+ tokens must be of the form: [factor*][initialiser][:][length][=value]
+
+ """
+ try:
+ return token_cache[(fmt, keys)]
+ except KeyError:
+ token_key = (fmt, keys)
+ # Very inefficient expanding of brackets.
+ fmt = expand_brackets(fmt)
+ # Split tokens by ',' and remove whitespace
+ # The meta_tokens can either be ordinary single tokens or multiple
+ # struct-format token strings.
+ meta_tokens = (''.join(f.split()) for f in fmt.split(','))
+ return_values = []
+ stretchy_token = False
+ for meta_token in meta_tokens:
+ # See if it has a multiplicative factor
+ m = MULTIPLICATIVE_RE.match(meta_token)
+ if not m:
+ factor = 1
+ else:
+ factor = int(m.group('factor'))
+ meta_token = m.group('token')
+ # See if it's a struct-like format
+ tokens = structparser(meta_token)
+ ret_vals = []
+ for token in tokens:
+ if keys and token in keys:
+ # Don't bother parsing it, it's a keyword argument
+ ret_vals.append([token, None, None])
+ continue
+ value = length = None
+ if token == '':
+ continue
+ # Match literal tokens of the form 0x... 0o... and 0b...
+ m = LITERAL_RE.match(token)
+ if m:
+ name = m.group('name')
+ value = m.group('value')
+ ret_vals.append([name, length, value])
+ continue
+ # Match everything else:
+ m1 = TOKEN_RE.match(token)
+ if not m1:
+ # and if you don't specify a 'name' then the default is 'uint':
+ m2 = DEFAULT_UINT.match(token)
+ if not m2:
+ raise ValueError("Don't understand token '{0}'.".format(token))
+ if m1:
+ name = m1.group('name')
+ length = m1.group('len')
+ if m1.group('value'):
+ value = m1.group('value')
+ else:
+ assert m2
+ name = 'uint'
+ length = m2.group('len')
+ if m2.group('value'):
+ value = m2.group('value')
+ if name == 'bool':
+ if length is not None:
+ raise ValueError("You can't specify a length with bool tokens - they are always one bit.")
+ length = 1
+ if length is None and name not in ('se', 'ue', 'sie', 'uie'):
+ stretchy_token = True
+ if length is not None:
+ # Try converting length to int, otherwise check it's a key.
+ try:
+ length = int(length)
+ if length < 0:
+ raise Error
+ # For the 'bytes' token convert length to bits.
+ if name == 'bytes':
+ length *= 8
+ except Error:
+ raise ValueError("Can't read a token with a negative length.")
+ except ValueError:
+ if not keys or length not in keys:
+ raise ValueError("Don't understand length '{0}' of token.".format(length))
+ ret_vals.append([name, length, value])
+ # This multiplies by the multiplicative factor, but this means that
+ # we can't allow keyword values as multipliers (e.g. n*uint:8).
+ # The only way to do this would be to return the factor in some fashion
+ # (we can't use the key's value here as it would mean that we couldn't
+ # sensibly continue to cache the function's results. (TODO).
+ return_values.extend(ret_vals * factor)
+ return_values = [tuple(x) for x in return_values]
+ if len(token_cache) < CACHE_SIZE:
+ token_cache[token_key] = stretchy_token, return_values
+ return stretchy_token, return_values
+
+# Looks for first number*(
+BRACKET_RE = re.compile(r'(?P<factor>\d+)\*\(')
+
+def expand_brackets(s):
+ """Remove whitespace and expand all brackets."""
+ s = ''.join(s.split())
+ while True:
+ start = s.find('(')
+ if start == -1:
+ break
+ count = 1 # Number of hanging open brackets
+ p = start + 1
+ while p < len(s):
+ if s[p] == '(':
+ count += 1
+ if s[p] == ')':
+ count -= 1
+ if not count:
+ break
+ p += 1
+ if count:
+ raise ValueError("Unbalanced parenthesis in '{0}'.".format(s))
+ if start == 0 or s[start - 1] != '*':
+ s = s[0:start] + s[start + 1:p] + s[p + 1:]
+ else:
+ m = BRACKET_RE.search(s)
+ if m:
+ factor = int(m.group('factor'))
+ matchstart = m.start('factor')
+ s = s[0:matchstart] + (factor - 1) * (s[start + 1:p] + ',') + s[start + 1:p] + s[p + 1:]
+ else:
+ raise ValueError("Failed to parse '{0}'.".format(s))
+ return s
+
+
+# This converts a single octal digit to 3 bits.
+OCT_TO_BITS = ['{0:03b}'.format(i) for i in xrange(8)]
+
+# A dictionary of number of 1 bits contained in binary representation of any byte
+BIT_COUNT = dict(zip(xrange(256), [bin(i).count('1') for i in xrange(256)]))
+
+
+class Bits(object):
+ """A container holding an immutable sequence of bits.
+
+ For a mutable container use the BitArray class instead.
+
+ Methods:
+
+ all() -- Check if all specified bits are set to 1 or 0.
+ any() -- Check if any of specified bits are set to 1 or 0.
+ count() -- Count the number of bits set to 1 or 0.
+ cut() -- Create generator of constant sized chunks.
+ endswith() -- Return whether the bitstring ends with a sub-string.
+ find() -- Find a sub-bitstring in the current bitstring.
+ findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
+ join() -- Join bitstrings together using current bitstring.
+ rfind() -- Seek backwards to find a sub-bitstring.
+ split() -- Create generator of chunks split by a delimiter.
+ startswith() -- Return whether the bitstring starts with a sub-bitstring.
+ tobytes() -- Return bitstring as bytes, padding if needed.
+ tofile() -- Write bitstring to file, padding if needed.
+ unpack() -- Interpret bits using format string.
+
+ Special methods:
+
+ Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^.
+
+ Properties:
+
+ bin -- The bitstring as a binary string.
+ bool -- For single bit bitstrings, interpret as True or False.
+ bytes -- The bitstring as a bytes object.
+ float -- Interpret as a floating point number.
+ floatbe -- Interpret as a big-endian floating point number.
+ floatle -- Interpret as a little-endian floating point number.
+ floatne -- Interpret as a native-endian floating point number.
+ hex -- The bitstring as a hexadecimal string.
+ int -- Interpret as a two's complement signed integer.
+ intbe -- Interpret as a big-endian signed integer.
+ intle -- Interpret as a little-endian signed integer.
+ intne -- Interpret as a native-endian signed integer.
+ len -- Length of the bitstring in bits.
+ oct -- The bitstring as an octal string.
+ se -- Interpret as a signed exponential-Golomb code.
+ ue -- Interpret as an unsigned exponential-Golomb code.
+ sie -- Interpret as a signed interleaved exponential-Golomb code.
+ uie -- Interpret as an unsigned interleaved exponential-Golomb code.
+ uint -- Interpret as a two's complement unsigned integer.
+ uintbe -- Interpret as a big-endian unsigned integer.
+ uintle -- Interpret as a little-endian unsigned integer.
+ uintne -- Interpret as a native-endian unsigned integer.
+
+ """
+
+ __slots__ = ('_datastore')
+
+ def __init__(self, auto=None, length=None, offset=None, **kwargs):
+ """Either specify an 'auto' initialiser:
+ auto -- a string of comma separated tokens, an integer, a file object,
+ a bytearray, a boolean iterable or another bitstring.
+
+ Or initialise via **kwargs with one (and only one) of:
+ bytes -- raw data as a string, for example read from a binary file.
+ bin -- binary string representation, e.g. '0b001010'.
+ hex -- hexadecimal string representation, e.g. '0x2ef'
+ oct -- octal string representation, e.g. '0o777'.
+ uint -- an unsigned integer.
+ int -- a signed integer.
+ float -- a floating point number.
+ uintbe -- an unsigned big-endian whole byte integer.
+ intbe -- a signed big-endian whole byte integer.
+ floatbe - a big-endian floating point number.
+ uintle -- an unsigned little-endian whole byte integer.
+ intle -- a signed little-endian whole byte integer.
+ floatle -- a little-endian floating point number.
+ uintne -- an unsigned native-endian whole byte integer.
+ intne -- a signed native-endian whole byte integer.
+ floatne -- a native-endian floating point number.
+ se -- a signed exponential-Golomb code.
+ ue -- an unsigned exponential-Golomb code.
+ sie -- a signed interleaved exponential-Golomb code.
+ uie -- an unsigned interleaved exponential-Golomb code.
+ bool -- a boolean (True or False).
+ filename -- a file which will be opened in binary read-only mode.
+
+ Other keyword arguments:
+ length -- length of the bitstring in bits, if needed and appropriate.
+ It must be supplied for all integer and float initialisers.
+ offset -- bit offset to the data. These offset bits are
+ ignored and this is mainly intended for use when
+ initialising using 'bytes' or 'filename'.
+
+ """
+ pass
+
+ def __new__(cls, auto=None, length=None, offset=None, _cache={}, **kwargs):
+ # For instances auto-initialised with a string we intern the
+ # instance for re-use.
+ try:
+ if isinstance(auto, basestring):
+ try:
+ return _cache[auto]
+ except KeyError:
+ x = object.__new__(Bits)
+ try:
+ _, tokens = tokenparser(auto)
+ except ValueError as e:
+ raise CreationError(*e.args)
+ x._datastore = ConstByteStore(bytearray(0), 0, 0)
+ for token in tokens:
+ x._datastore._appendstore(Bits._init_with_token(*token)._datastore)
+ assert x._assertsanity()
+ if len(_cache) < CACHE_SIZE:
+ _cache[auto] = x
+ return x
+ if isinstance(auto, Bits):
+ return auto
+ except TypeError:
+ pass
+ x = super(Bits, cls).__new__(cls)
+ x._initialise(auto, length, offset, **kwargs)
+ return x
+
+ def _initialise(self, auto, length, offset, **kwargs):
+ if length is not None and length < 0:
+ raise CreationError("bitstring length cannot be negative.")
+ if offset is not None and offset < 0:
+ raise CreationError("offset must be >= 0.")
+ if auto is not None:
+ self._initialise_from_auto(auto, length, offset)
+ return
+ if not kwargs:
+ # No initialisers, so initialise with nothing or zero bits
+ if length is not None and length != 0:
+ data = bytearray((length + 7) // 8)
+ self._setbytes_unsafe(data, length, 0)
+ return
+ self._setbytes_unsafe(bytearray(0), 0, 0)
+ return
+ k, v = kwargs.popitem()
+ try:
+ init_without_length_or_offset[k](self, v)
+ if length is not None or offset is not None:
+ raise CreationError("Cannot use length or offset with this initialiser.")
+ except KeyError:
+ try:
+ init_with_length_only[k](self, v, length)
+ if offset is not None:
+ raise CreationError("Cannot use offset with this initialiser.")
+ except KeyError:
+ if offset is None:
+ offset = 0
+ try:
+ init_with_length_and_offset[k](self, v, length, offset)
+ except KeyError:
+ raise CreationError("Unrecognised keyword '{0}' used to initialise.", k)
+
+ def _initialise_from_auto(self, auto, length, offset):
+ if offset is None:
+ offset = 0
+ self._setauto(auto, length, offset)
+ return
+
+ def __copy__(self):
+ """Return a new copy of the Bits for the copy module."""
+ # Note that if you want a new copy (different ID), use _copy instead.
+ # The copy can return self as it's immutable.
+ return self
+
+ def __lt__(self, other):
+ raise TypeError("unorderable type: {0}".format(type(self).__name__))
+
+ def __gt__(self, other):
+ raise TypeError("unorderable type: {0}".format(type(self).__name__))
+
+ def __le__(self, other):
+ raise TypeError("unorderable type: {0}".format(type(self).__name__))
+
+ def __ge__(self, other):
+ raise TypeError("unorderable type: {0}".format(type(self).__name__))
+
+ def __add__(self, bs):
+ """Concatenate bitstrings and return new bitstring.
+
+ bs -- the bitstring to append.
+
+ """
+ bs = Bits(bs)
+ if bs.len <= self.len:
+ s = self._copy()
+ s._append(bs)
+ else:
+ s = bs._copy()
+ s = self.__class__(s)
+ s._prepend(self)
+ return s
+
+ def __radd__(self, bs):
+ """Append current bitstring to bs and return new bitstring.
+
+ bs -- the string for the 'auto' initialiser that will be appended to.
+
+ """
+ bs = self._converttobitstring(bs)
+ return bs.__add__(self)
+
+ def __getitem__(self, key):
+ """Return a new bitstring representing a slice of the current bitstring.
+
+ Indices are in units of the step parameter (default 1 bit).
+ Stepping is used to specify the number of bits in each item.
+
+ >>> print BitArray('0b00110')[1:4]
+ '0b011'
+ >>> print BitArray('0x00112233')[1:3:8]
+ '0x1122'
+
+ """
+ length = self.len
+ try:
+ step = key.step if key.step is not None else 1
+ except AttributeError:
+ # single element
+ if key < 0:
+ key += length
+ if not 0 <= key < length:
+ raise IndexError("Slice index out of range.")
+ # Single bit, return True or False
+ return self._datastore.getbit(key)
+ else:
+ if step != 1:
+ # convert to binary string and use string slicing
+ bs = self.__class__()
+ bs._setbin_unsafe(self._getbin().__getitem__(key))
+ return bs
+ start, stop = 0, length
+ if key.start is not None:
+ start = key.start
+ if key.start < 0:
+ start += stop
+ if key.stop is not None:
+ stop = key.stop
+ if key.stop < 0:
+ stop += length
+ start = max(start, 0)
+ stop = min(stop, length)
+ if start < stop:
+ return self._slice(start, stop)
+ else:
+ return self.__class__()
+
+ def __len__(self):
+ """Return the length of the bitstring in bits."""
+ return self._getlength()
+
+ def __str__(self):
+ """Return approximate string representation of bitstring for printing.
+
+ Short strings will be given wholly in hexadecimal or binary. Longer
+ strings may be part hexadecimal and part binary. Very long strings will
+ be truncated with '...'.
+
+ """
+ length = self.len
+ if not length:
+ return ''
+ if length > MAX_CHARS * 4:
+ # Too long for hex. Truncate...
+ return ''.join(('0x', self._readhex(MAX_CHARS * 4, 0), '...'))
+ # If it's quite short and we can't do hex then use bin
+ if length < 32 and length % 4 != 0:
+ return '0b' + self.bin
+ # If we can use hex then do so
+ if not length % 4:
+ return '0x' + self.hex
+ # Otherwise first we do as much as we can in hex
+ # then add on 1, 2 or 3 bits on at the end
+ bits_at_end = length % 4
+ return ''.join(('0x', self._readhex(length - bits_at_end, 0),
+ ', ', '0b',
+ self._readbin(bits_at_end, length - bits_at_end)))
+
+ def __repr__(self):
+ """Return representation that could be used to recreate the bitstring.
+
+ If the returned string is too long it will be truncated. See __str__().
+
+ """
+ length = self.len
+ if isinstance(self._datastore._rawarray, MmapByteArray):
+ offsetstring = ''
+ if self._datastore.byteoffset or self._offset:
+ offsetstring = ", offset=%d" % (self._datastore._rawarray.byteoffset * 8 + self._offset)
+ lengthstring = ", length=%d" % length
+ return "{0}(filename='{1}'{2}{3})".format(self.__class__.__name__,
+ self._datastore._rawarray.source.name, lengthstring, offsetstring)
+ else:
+ s = self.__str__()
+ lengthstring = ''
+ if s.endswith('...'):
+ lengthstring = " # length={0}".format(length)
+ return "{0}('{1}'){2}".format(self.__class__.__name__, s, lengthstring)
+
+ def __eq__(self, bs):
+ """Return True if two bitstrings have the same binary representation.
+
+ >>> BitArray('0b1110') == '0xe'
+ True
+
+ """
+ try:
+ bs = Bits(bs)
+ except TypeError:
+ return False
+ return equal(self._datastore, bs._datastore)
+
+ def __ne__(self, bs):
+ """Return False if two bitstrings have the same binary representation.
+
+ >>> BitArray('0b111') == '0x7'
+ False
+
+ """
+ return not self.__eq__(bs)
+
+ def __invert__(self):
+ """Return bitstring with every bit inverted.
+
+ Raises Error if the bitstring is empty.
+
+ """
+ if not self.len:
+ raise Error("Cannot invert empty bitstring.")
+ s = self._copy()
+ s._invert_all()
+ return s
+
+ def __lshift__(self, n):
+ """Return bitstring with bits shifted by n to the left.
+
+ n -- the number of bits to shift. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot shift by a negative amount.")
+ if not self.len:
+ raise ValueError("Cannot shift an empty bitstring.")
+ n = min(n, self.len)
+ s = self._slice(n, self.len)
+ s._append(Bits(n))
+ return s
+
+ def __rshift__(self, n):
+ """Return bitstring with bits shifted by n to the right.
+
+ n -- the number of bits to shift. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot shift by a negative amount.")
+ if not self.len:
+ raise ValueError("Cannot shift an empty bitstring.")
+ if not n:
+ return self._copy()
+ s = self.__class__(length=min(n, self.len))
+ s._append(self[:-n])
+ return s
+
+ def __mul__(self, n):
+ """Return bitstring consisting of n concatenations of self.
+
+ Called for expression of the form 'a = b*3'.
+ n -- The number of concatenations. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot multiply by a negative integer.")
+ if not n:
+ return self.__class__()
+ s = self._copy()
+ s._imul(n)
+ return s
+
+ def __rmul__(self, n):
+ """Return bitstring consisting of n concatenations of self.
+
+ Called for expressions of the form 'a = 3*b'.
+ n -- The number of concatenations. Must be >= 0.
+
+ """
+ return self.__mul__(n)
+
+ def __and__(self, bs):
+ """Bit-wise 'and' between two bitstrings. Returns new bitstring.
+
+ bs -- The bitstring to '&' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for & operator.")
+ s = self._copy()
+ s._iand(bs)
+ return s
+
+ def __rand__(self, bs):
+ """Bit-wise 'and' between two bitstrings. Returns new bitstring.
+
+ bs -- the bitstring to '&' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ return self.__and__(bs)
+
+ def __or__(self, bs):
+ """Bit-wise 'or' between two bitstrings. Returns new bitstring.
+
+ bs -- The bitstring to '|' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for | operator.")
+ s = self._copy()
+ s._ior(bs)
+ return s
+
+ def __ror__(self, bs):
+ """Bit-wise 'or' between two bitstrings. Returns new bitstring.
+
+ bs -- The bitstring to '|' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ return self.__or__(bs)
+
+ def __xor__(self, bs):
+ """Bit-wise 'xor' between two bitstrings. Returns new bitstring.
+
+ bs -- The bitstring to '^' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for ^ operator.")
+ s = self._copy()
+ s._ixor(bs)
+ return s
+
+ def __rxor__(self, bs):
+ """Bit-wise 'xor' between two bitstrings. Returns new bitstring.
+
+ bs -- The bitstring to '^' with.
+
+ Raises ValueError if the two bitstrings have differing lengths.
+
+ """
+ return self.__xor__(bs)
+
+ def __contains__(self, bs):
+ """Return whether bs is contained in the current bitstring.
+
+ bs -- The bitstring to search for.
+
+ """
+ # Don't want to change pos
+ try:
+ pos = self._pos
+ except AttributeError:
+ pass
+ found = Bits.find(self, bs, bytealigned=False)
+ try:
+ self._pos = pos
+ except AttributeError:
+ pass
+ return bool(found)
+
+ def __hash__(self):
+ """Return an integer hash of the object."""
+ # We can't in general hash the whole bitstring (it could take hours!)
+ # So instead take some bits from the start and end.
+ if self.len <= 160:
+ # Use the whole bitstring.
+ shorter = self
+ else:
+ # Take 10 bytes from start and end
+ shorter = self[:80] + self[-80:]
+ h = 0
+ for byte in shorter.tobytes():
+ try:
+ h = (h << 4) + ord(byte)
+ except TypeError:
+ # Python 3
+ h = (h << 4) + byte
+ g = h & 0xf0000000
+ if g & (1 << 31):
+ h ^= (g >> 24)
+ h ^= g
+ return h % 1442968193
+
+ # This is only used in Python 2.x...
+ def __nonzero__(self):
+ """Return True if any bits are set to 1, otherwise return False."""
+ return self.any(True)
+
+ # ...whereas this is used in Python 3.x
+ __bool__ = __nonzero__
+
+ def _assertsanity(self):
+ """Check internal self consistency as a debugging aid."""
+ assert self.len >= 0
+ assert 0 <= self._offset, "offset={0}".format(self._offset)
+ assert (self.len + self._offset + 7) // 8 == self._datastore.bytelength + self._datastore.byteoffset
+ return True
+
+ @classmethod
+ def _init_with_token(cls, name, token_length, value):
+ if token_length is not None:
+ token_length = int(token_length)
+ if token_length == 0:
+ return cls()
+ # For pad token just return the length in zero bits
+ if name == 'pad':
+ return cls(token_length)
+
+ if value is None:
+ if token_length is None:
+ error = "Token has no value ({0}=???).".format(name)
+ else:
+ error = "Token has no value ({0}:{1}=???).".format(name, token_length)
+ raise ValueError(error)
+ try:
+ b = cls(**{_tokenname_to_initialiser[name]: value})
+ except KeyError:
+ if name in ('se', 'ue', 'sie', 'uie'):
+ b = cls(**{name: int(value)})
+ elif name in ('uint', 'int', 'uintbe', 'intbe', 'uintle', 'intle', 'uintne', 'intne'):
+ b = cls(**{name: int(value), 'length': token_length})
+ elif name in ('float', 'floatbe', 'floatle', 'floatne'):
+ b = cls(**{name: float(value), 'length': token_length})
+ elif name == 'bool':
+ if value in (1, 'True', '1'):
+ b = cls(bool=True)
+ elif value in (0, 'False', '0'):
+ b = cls(bool=False)
+ else:
+ raise CreationError("bool token can only be 'True' or 'False'.")
+ else:
+ raise CreationError("Can't parse token name {0}.", name)
+ if token_length is not None and b.len != token_length:
+ msg = "Token with length {0} packed with value of length {1} ({2}:{3}={4})."
+ raise CreationError(msg, token_length, b.len, name, token_length, value)
+ return b
+
+ def _clear(self):
+ """Reset the bitstring to an empty state."""
+ self._datastore = ByteStore(bytearray(0))
+
+ def _setauto(self, s, length, offset):
+ """Set bitstring from a bitstring, file, bool, integer, iterable or string."""
+ # As s can be so many different things it's important to do the checks
+ # in the correct order, as some types are also other allowed types.
+ # So basestring must be checked before Iterable
+ # and bytes/bytearray before Iterable but after basestring!
+ if isinstance(s, Bits):
+ if length is None:
+ length = s.len - offset
+ self._setbytes_unsafe(s._datastore.rawbytes, length, s._offset + offset)
+ return
+ if isinstance(s, file):
+ if offset is None:
+ offset = 0
+ if length is None:
+ length = os.path.getsize(s.name) * 8 - offset
+ byteoffset, offset = divmod(offset, 8)
+ bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset
+ m = MmapByteArray(s, bytelength, byteoffset)
+ if length + byteoffset * 8 + offset > m.filelength * 8:
+ raise CreationError("File is not long enough for specified "
+ "length and offset.")
+ self._datastore = ConstByteStore(m, length, offset)
+ return
+ if length is not None:
+ raise CreationError("The length keyword isn't applicable to this initialiser.")
+ if offset:
+ raise CreationError("The offset keyword isn't applicable to this initialiser.")
+ if isinstance(s, basestring):
+ bs = self._converttobitstring(s)
+ assert bs._offset == 0
+ self._setbytes_unsafe(bs._datastore.rawbytes, bs.length, 0)
+ return
+ if isinstance(s, (bytes, bytearray)):
+ self._setbytes_unsafe(bytearray(s), len(s) * 8, 0)
+ return
+ if isinstance(s, numbers.Integral):
+ # Initialise with s zero bits.
+ if s < 0:
+ msg = "Can't create bitstring of negative length {0}."
+ raise CreationError(msg, s)
+ data = bytearray((s + 7) // 8)
+ self._datastore = ByteStore(data, s, 0)
+ return
+ if isinstance(s, collections.Iterable):
+ # Evaluate each item as True or False and set bits to 1 or 0.
+ self._setbin_unsafe(''.join(str(int(bool(x))) for x in s))
+ return
+ raise TypeError("Cannot initialise bitstring from {0}.".format(type(s)))
+
+ def _setfile(self, filename, length, offset):
+ """Use file as source of bits."""
+ source = open(filename, 'rb')
+ if offset is None:
+ offset = 0
+ if length is None:
+ length = os.path.getsize(source.name) * 8 - offset
+ byteoffset, offset = divmod(offset, 8)
+ bytelength = (length + byteoffset * 8 + offset + 7) // 8 - byteoffset
+ m = MmapByteArray(source, bytelength, byteoffset)
+ if length + byteoffset * 8 + offset > m.filelength * 8:
+ raise CreationError("File is not long enough for specified "
+ "length and offset.")
+ self._datastore = ConstByteStore(m, length, offset)
+
+ def _setbytes_safe(self, data, length=None, offset=0):
+ """Set the data from a string."""
+ data = bytearray(data)
+ if length is None:
+ # Use to the end of the data
+ length = len(data)*8 - offset
+ self._datastore = ByteStore(data, length, offset)
+ else:
+ if length + offset > len(data) * 8:
+ msg = "Not enough data present. Need {0} bits, have {1}."
+ raise CreationError(msg, length + offset, len(data) * 8)
+ if length == 0:
+ self._datastore = ByteStore(bytearray(0))
+ else:
+ self._datastore = ByteStore(data, length, offset)
+
+ def _setbytes_unsafe(self, data, length, offset):
+ """Unchecked version of _setbytes_safe."""
+ self._datastore = ByteStore(data[:], length, offset)
+ assert self._assertsanity()
+
+ def _readbytes(self, length, start):
+ """Read bytes and return them. Note that length is in bits."""
+ assert length % 8 == 0
+ assert start + length <= self.len
+ if not (start + self._offset) % 8:
+ return bytes(self._datastore.getbyteslice((start + self._offset) // 8,
+ (start + self._offset + length) // 8))
+ return self._slice(start, start + length).tobytes()
+
+ def _getbytes(self):
+ """Return the data as an ordinary string."""
+ if self.len % 8:
+ raise InterpretError("Cannot interpret as bytes unambiguously - "
+ "not multiple of 8 bits.")
+ return self._readbytes(self.len, 0)
+
+ def _setuint(self, uint, length=None):
+ """Reset the bitstring to have given unsigned int interpretation."""
+ try:
+ if length is None:
+ # Use the whole length. Deliberately not using .len here.
+ length = self._datastore.bitlength
+ except AttributeError:
+ # bitstring doesn't have a _datastore as it hasn't been created!
+ pass
+ # TODO: All this checking code should be hoisted out of here!
+ if length is None or length == 0:
+ raise CreationError("A non-zero length must be specified with a "
+ "uint initialiser.")
+ if uint >= (1 << length):
+ msg = "{0} is too large an unsigned integer for a bitstring of length {1}. "\
+ "The allowed range is [0, {2}]."
+ raise CreationError(msg, uint, length, (1 << length) - 1)
+ if uint < 0:
+ raise CreationError("uint cannot be initialsed by a negative number.")
+ s = hex(uint)[2:]
+ s = s.rstrip('L')
+ if len(s) & 1:
+ s = '0' + s
+ try:
+ data = bytes.fromhex(s)
+ except AttributeError:
+ # the Python 2.x way
+ data = binascii.unhexlify(s)
+ # Now add bytes as needed to get the right length.
+ extrabytes = ((length + 7) // 8) - len(data)
+ if extrabytes > 0:
+ data = b'\x00' * extrabytes + data
+ offset = 8 - (length % 8)
+ if offset == 8:
+ offset = 0
+ self._setbytes_unsafe(bytearray(data), length, offset)
+
+ def _readuint(self, length, start):
+ """Read bits and interpret as an unsigned int."""
+ if not length:
+ raise InterpretError("Cannot interpret a zero length bitstring "
+ "as an integer.")
+ offset = self._offset
+ startbyte = (start + offset) // 8
+ endbyte = (start + offset + length - 1) // 8
+
+ b = binascii.hexlify(bytes(self._datastore.getbyteslice(startbyte, endbyte + 1)))
+ assert b
+ i = int(b, 16)
+ final_bits = 8 - ((start + offset + length) % 8)
+ if final_bits != 8:
+ i >>= final_bits
+ i &= (1 << length) - 1
+ return i
+
+ def _getuint(self):
+ """Return data as an unsigned int."""
+ return self._readuint(self.len, 0)
+
+ def _setint(self, int_, length=None):
+ """Reset the bitstring to have given signed int interpretation."""
+ # If no length given, and we've previously been given a length, use it.
+ if length is None and hasattr(self, 'len') and self.len != 0:
+ length = self.len
+ if length is None or length == 0:
+ raise CreationError("A non-zero length must be specified with an int initialiser.")
+ if int_ >= (1 << (length - 1)) or int_ < -(1 << (length - 1)):
+ raise CreationError("{0} is too large a signed integer for a bitstring of length {1}. "
+ "The allowed range is [{2}, {3}].", int_, length, -(1 << (length - 1)),
+ (1 << (length - 1)) - 1)
+ if int_ >= 0:
+ self._setuint(int_, length)
+ return
+ # TODO: We should decide whether to just use the _setuint, or to do the bit flipping,
+ # based upon which will be quicker. If the -ive number is less than half the maximum
+ # possible then it's probably quicker to do the bit flipping...
+
+ # Do the 2's complement thing. Add one, set to minus number, then flip bits.
+ int_ += 1
+ self._setuint(-int_, length)
+ self._invert_all()
+
+ def _readint(self, length, start):
+ """Read bits and interpret as a signed int"""
+ ui = self._readuint(length, start)
+ if not ui >> (length - 1):
+ # Top bit not set, number is positive
+ return ui
+ # Top bit is set, so number is negative
+ tmp = (~(ui - 1)) & ((1 << length) - 1)
+ return -tmp
+
+ def _getint(self):
+ """Return data as a two's complement signed int."""
+ return self._readint(self.len, 0)
+
+ def _setuintbe(self, uintbe, length=None):
+ """Set the bitstring to a big-endian unsigned int interpretation."""
+ if length is not None and length % 8 != 0:
+ raise CreationError("Big-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ self._setuint(uintbe, length)
+
+ def _readuintbe(self, length, start):
+ """Read bits and interpret as a big-endian unsigned int."""
+ if length % 8:
+ raise InterpretError("Big-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ return self._readuint(length, start)
+
+ def _getuintbe(self):
+ """Return data as a big-endian two's complement unsigned int."""
+ return self._readuintbe(self.len, 0)
+
+ def _setintbe(self, intbe, length=None):
+ """Set bitstring to a big-endian signed int interpretation."""
+ if length is not None and length % 8 != 0:
+ raise CreationError("Big-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ self._setint(intbe, length)
+
+ def _readintbe(self, length, start):
+ """Read bits and interpret as a big-endian signed int."""
+ if length % 8:
+ raise InterpretError("Big-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ return self._readint(length, start)
+
+ def _getintbe(self):
+ """Return data as a big-endian two's complement signed int."""
+ return self._readintbe(self.len, 0)
+
+ def _setuintle(self, uintle, length=None):
+ if length is not None and length % 8 != 0:
+ raise CreationError("Little-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ self._setuint(uintle, length)
+ self._reversebytes(0, self.len)
+
+ def _readuintle(self, length, start):
+ """Read bits and interpret as a little-endian unsigned int."""
+ if length % 8:
+ raise InterpretError("Little-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ assert start + length <= self.len
+ absolute_pos = start + self._offset
+ startbyte, offset = divmod(absolute_pos, 8)
+ val = 0
+ if not offset:
+ endbyte = (absolute_pos + length - 1) // 8
+ chunksize = 4 # for 'L' format
+ while endbyte - chunksize + 1 >= startbyte:
+ val <<= 8 * chunksize
+ val += struct.unpack('<L', bytes(self._datastore.getbyteslice(endbyte + 1 - chunksize, endbyte + 1)))[0]
+ endbyte -= chunksize
+ for b in xrange(endbyte, startbyte - 1, -1):
+ val <<= 8
+ val += self._datastore.getbyte(b)
+ else:
+ data = self._slice(start, start + length)
+ assert data.len % 8 == 0
+ data._reversebytes(0, self.len)
+ for b in bytearray(data.bytes):
+ val <<= 8
+ val += b
+ return val
+
+ def _getuintle(self):
+ return self._readuintle(self.len, 0)
+
+ def _setintle(self, intle, length=None):
+ if length is not None and length % 8 != 0:
+ raise CreationError("Little-endian integers must be whole-byte. "
+ "Length = {0} bits.", length)
+ self._setint(intle, length)
+ self._reversebytes(0, self.len)
+
+ def _readintle(self, length, start):
+ """Read bits and interpret as a little-endian signed int."""
+ ui = self._readuintle(length, start)
+ if not ui >> (length - 1):
+ # Top bit not set, number is positive
+ return ui
+ # Top bit is set, so number is negative
+ tmp = (~(ui - 1)) & ((1 << length) - 1)
+ return -tmp
+
+ def _getintle(self):
+ return self._readintle(self.len, 0)
+
+ def _setfloat(self, f, length=None):
+ # If no length given, and we've previously been given a length, use it.
+ if length is None and hasattr(self, 'len') and self.len != 0:
+ length = self.len
+ if length is None or length == 0:
+ raise CreationError("A non-zero length must be specified with a "
+ "float initialiser.")
+ if length == 32:
+ b = struct.pack('>f', f)
+ elif length == 64:
+ b = struct.pack('>d', f)
+ else:
+ raise CreationError("floats can only be 32 or 64 bits long, "
+ "not {0} bits", length)
+ self._setbytes_unsafe(bytearray(b), length, 0)
+
+ def _readfloat(self, length, start):
+ """Read bits and interpret as a float."""
+ if not (start + self._offset) % 8:
+ startbyte = (start + self._offset) // 8
+ if length == 32:
+ f, = struct.unpack('>f', bytes(self._datastore.getbyteslice(startbyte, startbyte + 4)))
+ elif length == 64:
+ f, = struct.unpack('>d', bytes(self._datastore.getbyteslice(startbyte, startbyte + 8)))
+ else:
+ if length == 32:
+ f, = struct.unpack('>f', self._readbytes(32, start))
+ elif length == 64:
+ f, = struct.unpack('>d', self._readbytes(64, start))
+ try:
+ return f
+ except NameError:
+ raise InterpretError("floats can only be 32 or 64 bits long, not {0} bits", length)
+
+ def _getfloat(self):
+ """Interpret the whole bitstring as a float."""
+ return self._readfloat(self.len, 0)
+
+ def _setfloatle(self, f, length=None):
+ # If no length given, and we've previously been given a length, use it.
+ if length is None and hasattr(self, 'len') and self.len != 0:
+ length = self.len
+ if length is None or length == 0:
+ raise CreationError("A non-zero length must be specified with a "
+ "float initialiser.")
+ if length == 32:
+ b = struct.pack('<f', f)
+ elif length == 64:
+ b = struct.pack('<d', f)
+ else:
+ raise CreationError("floats can only be 32 or 64 bits long, "
+ "not {0} bits", length)
+ self._setbytes_unsafe(bytearray(b), length, 0)
+
+ def _readfloatle(self, length, start):
+ """Read bits and interpret as a little-endian float."""
+ startbyte, offset = divmod(start + self._offset, 8)
+ if not offset:
+ if length == 32:
+ f, = struct.unpack('<f', bytes(self._datastore.getbyteslice(startbyte, startbyte + 4)))
+ elif length == 64:
+ f, = struct.unpack('<d', bytes(self._datastore.getbyteslice(startbyte, startbyte + 8)))
+ else:
+ if length == 32:
+ f, = struct.unpack('<f', self._readbytes(32, start))
+ elif length == 64:
+ f, = struct.unpack('<d', self._readbytes(64, start))
+ try:
+ return f
+ except NameError:
+ raise InterpretError("floats can only be 32 or 64 bits long, "
+ "not {0} bits", length)
+
+ def _getfloatle(self):
+ """Interpret the whole bitstring as a little-endian float."""
+ return self._readfloatle(self.len, 0)
+
+ def _setue(self, i):
+ """Initialise bitstring with unsigned exponential-Golomb code for integer i.
+
+ Raises CreationError if i < 0.
+
+ """
+ if i < 0:
+ raise CreationError("Cannot use negative initialiser for unsigned "
+ "exponential-Golomb.")
+ if not i:
+ self._setbin_unsafe('1')
+ return
+ tmp = i + 1
+ leadingzeros = -1
+ while tmp > 0:
+ tmp >>= 1
+ leadingzeros += 1
+ remainingpart = i + 1 - (1 << leadingzeros)
+ binstring = '0' * leadingzeros + '1' + Bits(uint=remainingpart,
+ length=leadingzeros).bin
+ self._setbin_unsafe(binstring)
+
+ def _readue(self, pos):
+ """Return interpretation of next bits as unsigned exponential-Golomb code.
+
+ Raises ReadError if the end of the bitstring is encountered while
+ reading the code.
+
+ """
+ oldpos = pos
+ try:
+ while not self[pos]:
+ pos += 1
+ except IndexError:
+ raise ReadError("Read off end of bitstring trying to read code.")
+ leadingzeros = pos - oldpos
+ codenum = (1 << leadingzeros) - 1
+ if leadingzeros > 0:
+ if pos + leadingzeros + 1 > self.len:
+ raise ReadError("Read off end of bitstring trying to read code.")
+ codenum += self._readuint(leadingzeros, pos + 1)
+ pos += leadingzeros + 1
+ else:
+ assert codenum == 0
+ pos += 1
+ return codenum, pos
+
+ def _getue(self):
+ """Return data as unsigned exponential-Golomb code.
+
+ Raises InterpretError if bitstring is not a single exponential-Golomb code.
+
+ """
+ try:
+ value, newpos = self._readue(0)
+ if value is None or newpos != self.len:
+ raise ReadError
+ except ReadError:
+ raise InterpretError("Bitstring is not a single exponential-Golomb code.")
+ return value
+
+ def _setse(self, i):
+ """Initialise bitstring with signed exponential-Golomb code for integer i."""
+ if i > 0:
+ u = (i * 2) - 1
+ else:
+ u = -2 * i
+ self._setue(u)
+
+ def _getse(self):
+ """Return data as signed exponential-Golomb code.
+
+ Raises InterpretError if bitstring is not a single exponential-Golomb code.
+
+ """
+ try:
+ value, newpos = self._readse(0)
+ if value is None or newpos != self.len:
+ raise ReadError
+ except ReadError:
+ raise InterpretError("Bitstring is not a single exponential-Golomb code.")
+ return value
+
+ def _readse(self, pos):
+ """Return interpretation of next bits as a signed exponential-Golomb code.
+
+ Advances position to after the read code.
+
+ Raises ReadError if the end of the bitstring is encountered while
+ reading the code.
+
+ """
+ codenum, pos = self._readue(pos)
+ m = (codenum + 1) // 2
+ if not codenum % 2:
+ return -m, pos
+ else:
+ return m, pos
+
+ def _setuie(self, i):
+ """Initialise bitstring with unsigned interleaved exponential-Golomb code for integer i.
+
+ Raises CreationError if i < 0.
+
+ """
+ if i < 0:
+ raise CreationError("Cannot use negative initialiser for unsigned "
+ "interleaved exponential-Golomb.")
+ self._setbin_unsafe('1' if i == 0 else '0' + '0'.join(bin(i + 1)[3:]) + '1')
+
+ def _readuie(self, pos):
+ """Return interpretation of next bits as unsigned interleaved exponential-Golomb code.
+
+ Raises ReadError if the end of the bitstring is encountered while
+ reading the code.
+
+ """
+ try:
+ codenum = 1
+ while not self[pos]:
+ pos += 1
+ codenum <<= 1
+ codenum += self[pos]
+ pos += 1
+ pos += 1
+ except IndexError:
+ raise ReadError("Read off end of bitstring trying to read code.")
+ codenum -= 1
+ return codenum, pos
+
+ def _getuie(self):
+ """Return data as unsigned interleaved exponential-Golomb code.
+
+ Raises InterpretError if bitstring is not a single exponential-Golomb code.
+
+ """
+ try:
+ value, newpos = self._readuie(0)
+ if value is None or newpos != self.len:
+ raise ReadError
+ except ReadError:
+ raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.")
+ return value
+
+ def _setsie(self, i):
+ """Initialise bitstring with signed interleaved exponential-Golomb code for integer i."""
+ if not i:
+ self._setbin_unsafe('1')
+ else:
+ self._setuie(abs(i))
+ self._append(Bits([i < 0]))
+
+ def _getsie(self):
+ """Return data as signed interleaved exponential-Golomb code.
+
+ Raises InterpretError if bitstring is not a single exponential-Golomb code.
+
+ """
+ try:
+ value, newpos = self._readsie(0)
+ if value is None or newpos != self.len:
+ raise ReadError
+ except ReadError:
+ raise InterpretError("Bitstring is not a single interleaved exponential-Golomb code.")
+ return value
+
+ def _readsie(self, pos):
+ """Return interpretation of next bits as a signed interleaved exponential-Golomb code.
+
+ Advances position to after the read code.
+
+ Raises ReadError if the end of the bitstring is encountered while
+ reading the code.
+
+ """
+ codenum, pos = self._readuie(pos)
+ if not codenum:
+ return 0, pos
+ try:
+ if self[pos]:
+ return -codenum, pos + 1
+ else:
+ return codenum, pos + 1
+ except IndexError:
+ raise ReadError("Read off end of bitstring trying to read code.")
+
+ def _setbool(self, value):
+ # We deliberately don't want to have implicit conversions to bool here.
+ # If we did then it would be difficult to deal with the 'False' string.
+ if value in (1, 'True'):
+ self._setbytes_unsafe(bytearray(b'\x80'), 1, 0)
+ elif value in (0, 'False'):
+ self._setbytes_unsafe(bytearray(b'\x00'), 1, 0)
+ else:
+ raise CreationError('Cannot initialise boolean with {0}.', value)
+
+ def _getbool(self):
+ if self.length != 1:
+ msg = "For a bool interpretation a bitstring must be 1 bit long, not {0} bits."
+ raise InterpretError(msg, self.length)
+ return self[0]
+
+ def _readbool(self, pos):
+ return self[pos], pos + 1
+
+ def _setbin_safe(self, binstring):
+ """Reset the bitstring to the value given in binstring."""
+ binstring = tidy_input_string(binstring)
+ # remove any 0b if present
+ binstring = binstring.replace('0b', '')
+ self._setbin_unsafe(binstring)
+
+ def _setbin_unsafe(self, binstring):
+ """Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'."""
+ length = len(binstring)
+ # pad with zeros up to byte boundary if needed
+ boundary = ((length + 7) // 8) * 8
+ padded_binstring = binstring + '0' * (boundary - length)\
+ if len(binstring) < boundary else binstring
+ try:
+ bytelist = [int(padded_binstring[x:x + 8], 2)
+ for x in xrange(0, len(padded_binstring), 8)]
+ except ValueError:
+ raise CreationError("Invalid character in bin initialiser {0}.", binstring)
+ self._setbytes_unsafe(bytearray(bytelist), length, 0)
+
+ def _readbin(self, length, start):
+ """Read bits and interpret as a binary string."""
+ if not length:
+ return ''
+ # Get the byte slice containing our bit slice
+ startbyte, startoffset = divmod(start + self._offset, 8)
+ endbyte = (start + self._offset + length - 1) // 8
+ b = self._datastore.getbyteslice(startbyte, endbyte + 1)
+ # Convert to a string of '0' and '1's (via a hex string an and int!)
+ try:
+ c = "{:0{}b}".format(int(binascii.hexlify(b), 16), 8*len(b))
+ except TypeError:
+ # Hack to get Python 2.6 working
+ c = "{0:0{1}b}".format(int(binascii.hexlify(str(b)), 16), 8*len(b))
+ # Finally chop off any extra bits.
+ return c[startoffset:startoffset + length]
+
+ def _getbin(self):
+ """Return interpretation as a binary string."""
+ return self._readbin(self.len, 0)
+
+ def _setoct(self, octstring):
+ """Reset the bitstring to have the value given in octstring."""
+ octstring = tidy_input_string(octstring)
+ # remove any 0o if present
+ octstring = octstring.replace('0o', '')
+ binlist = []
+ for i in octstring:
+ try:
+ if not 0 <= int(i) < 8:
+ raise ValueError
+ binlist.append(OCT_TO_BITS[int(i)])
+ except ValueError:
+ raise CreationError("Invalid symbol '{0}' in oct initialiser.", i)
+ self._setbin_unsafe(''.join(binlist))
+
+ def _readoct(self, length, start):
+ """Read bits and interpret as an octal string."""
+ if length % 3:
+ raise InterpretError("Cannot convert to octal unambiguously - "
+ "not multiple of 3 bits.")
+ if not length:
+ return ''
+ # Get main octal bit by converting from int.
+ # Strip starting 0 or 0o depending on Python version.
+ end = oct(self._readuint(length, start))[LEADING_OCT_CHARS:]
+ if end.endswith('L'):
+ end = end[:-1]
+ middle = '0' * (length // 3 - len(end))
+ return middle + end
+
+ def _getoct(self):
+ """Return interpretation as an octal string."""
+ return self._readoct(self.len, 0)
+
+ def _sethex(self, hexstring):
+ """Reset the bitstring to have the value given in hexstring."""
+ hexstring = tidy_input_string(hexstring)
+ # remove any 0x if present
+ hexstring = hexstring.replace('0x', '')
+ length = len(hexstring)
+ if length % 2:
+ hexstring += '0'
+ try:
+ try:
+ data = bytearray.fromhex(hexstring)
+ except TypeError:
+ # Python 2.6 needs a unicode string (a bug). 2.7 and 3.x work fine.
+ data = bytearray.fromhex(unicode(hexstring))
+ except ValueError:
+ raise CreationError("Invalid symbol in hex initialiser.")
+ self._setbytes_unsafe(data, length * 4, 0)
+
+ def _readhex(self, length, start):
+ """Read bits and interpret as a hex string."""
+ if length % 4:
+ raise InterpretError("Cannot convert to hex unambiguously - "
+ "not multiple of 4 bits.")
+ if not length:
+ return ''
+ # This monstrosity is the only thing I could get to work for both 2.6 and 3.1.
+ # TODO: Is utf-8 really what we mean here?
+ s = str(binascii.hexlify(self._slice(start, start + length).tobytes()).decode('utf-8'))
+ # If there's one nibble too many then cut it off
+ return s[:-1] if (length // 4) % 2 else s
+
+ def _gethex(self):
+ """Return the hexadecimal representation as a string prefixed with '0x'.
+
+ Raises an InterpretError if the bitstring's length is not a multiple of 4.
+
+ """
+ return self._readhex(self.len, 0)
+
+ def _getoffset(self):
+ return self._datastore.offset
+
+ def _getlength(self):
+ """Return the length of the bitstring in bits."""
+ return self._datastore.bitlength
+
+ def _ensureinmemory(self):
+ """Ensure the data is held in memory, not in a file."""
+ self._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength),
+ self.len, self._offset)
+
+ @classmethod
+ def _converttobitstring(cls, bs, offset=0, cache={}):
+ """Convert bs to a bitstring and return it.
+
+ offset gives the suggested bit offset of first significant
+ bit, to optimise append etc.
+
+ """
+ if isinstance(bs, Bits):
+ return bs
+ try:
+ return cache[(bs, offset)]
+ except KeyError:
+ if isinstance(bs, basestring):
+ b = cls()
+ try:
+ _, tokens = tokenparser(bs)
+ except ValueError as e:
+ raise CreationError(*e.args)
+ if tokens:
+ b._append(Bits._init_with_token(*tokens[0]))
+ b._datastore = offsetcopy(b._datastore, offset)
+ for token in tokens[1:]:
+ b._append(Bits._init_with_token(*token))
+ assert b._assertsanity()
+ assert b.len == 0 or b._offset == offset
+ if len(cache) < CACHE_SIZE:
+ cache[(bs, offset)] = b
+ return b
+ except TypeError:
+ # Unhashable type
+ pass
+ return cls(bs)
+
+ def _copy(self):
+ """Create and return a new copy of the Bits (always in memory)."""
+ s_copy = self.__class__()
+ s_copy._setbytes_unsafe(self._datastore.getbyteslice(0, self._datastore.bytelength),
+ self.len, self._offset)
+ return s_copy
+
+ def _slice(self, start, end):
+ """Used internally to get a slice, without error checking."""
+ if end == start:
+ return self.__class__()
+ offset = self._offset
+ startbyte, newoffset = divmod(start + offset, 8)
+ endbyte = (end + offset - 1) // 8
+ bs = self.__class__()
+ bs._setbytes_unsafe(self._datastore.getbyteslice(startbyte, endbyte + 1), end - start, newoffset)
+ return bs
+
+ def _readtoken(self, name, pos, length):
+ """Reads a token from the bitstring and returns the result."""
+ if length is not None and int(length) > self.length - pos:
+ raise ReadError("Reading off the end of the data. "
+ "Tried to read {0} bits when only {1} available.".format(int(length), self.length - pos))
+ try:
+ val = name_to_read[name](self, length, pos)
+ return val, pos + length
+ except KeyError:
+ if name == 'pad':
+ return None, pos + length
+ raise ValueError("Can't parse token {0}:{1}".format(name, length))
+ except TypeError:
+ # This is for the 'ue', 'se' and 'bool' tokens. They will also return the new pos.
+ return name_to_read[name](self, pos)
+
+ def _append(self, bs):
+ """Append a bitstring to the current bitstring."""
+ self._datastore._appendstore(bs._datastore)
+
+ def _prepend(self, bs):
+ """Prepend a bitstring to the current bitstring."""
+ self._datastore._prependstore(bs._datastore)
+
+ def _reverse(self):
+ """Reverse all bits in-place."""
+ # Reverse the contents of each byte
+ n = [BYTE_REVERSAL_DICT[b] for b in self._datastore.rawbytes]
+ # Then reverse the order of the bytes
+ n.reverse()
+ # The new offset is the number of bits that were unused at the end.
+ newoffset = 8 - (self._offset + self.len) % 8
+ if newoffset == 8:
+ newoffset = 0
+ self._setbytes_unsafe(bytearray().join(n), self.length, newoffset)
+
+ def _truncatestart(self, bits):
+ """Truncate bits from the start of the bitstring."""
+ assert 0 <= bits <= self.len
+ if not bits:
+ return
+ if bits == self.len:
+ self._clear()
+ return
+ bytepos, offset = divmod(self._offset + bits, 8)
+ self._setbytes_unsafe(self._datastore.getbyteslice(bytepos, self._datastore.bytelength), self.len - bits,
+ offset)
+ assert self._assertsanity()
+
+ def _truncateend(self, bits):
+ """Truncate bits from the end of the bitstring."""
+ assert 0 <= bits <= self.len
+ if not bits:
+ return
+ if bits == self.len:
+ self._clear()
+ return
+ newlength_in_bytes = (self._offset + self.len - bits + 7) // 8
+ self._setbytes_unsafe(self._datastore.getbyteslice(0, newlength_in_bytes), self.len - bits,
+ self._offset)
+ assert self._assertsanity()
+
+ def _insert(self, bs, pos):
+ """Insert bs at pos."""
+ assert 0 <= pos <= self.len
+ if pos > self.len // 2:
+ # Inserting nearer end, so cut off end.
+ end = self._slice(pos, self.len)
+ self._truncateend(self.len - pos)
+ self._append(bs)
+ self._append(end)
+ else:
+ # Inserting nearer start, so cut off start.
+ start = self._slice(0, pos)
+ self._truncatestart(pos)
+ self._prepend(bs)
+ self._prepend(start)
+ try:
+ self._pos = pos + bs.len
+ except AttributeError:
+ pass
+ assert self._assertsanity()
+
+ def _overwrite(self, bs, pos):
+ """Overwrite with bs at pos."""
+ assert 0 <= pos < self.len
+ if bs is self:
+ # Just overwriting with self, so do nothing.
+ assert pos == 0
+ return
+ firstbytepos = (self._offset + pos) // 8
+ lastbytepos = (self._offset + pos + bs.len - 1) // 8
+ bytepos, bitoffset = divmod(self._offset + pos, 8)
+ if firstbytepos == lastbytepos:
+ mask = ((1 << bs.len) - 1) << (8 - bs.len - bitoffset)
+ self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask))
+ d = offsetcopy(bs._datastore, bitoffset)
+ self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask))
+ else:
+ # Do first byte
+ mask = (1 << (8 - bitoffset)) - 1
+ self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) & (~mask))
+ d = offsetcopy(bs._datastore, bitoffset)
+ self._datastore.setbyte(bytepos, self._datastore.getbyte(bytepos) | (d.getbyte(0) & mask))
+ # Now do all the full bytes
+ self._datastore.setbyteslice(firstbytepos + 1, lastbytepos, d.getbyteslice(1, lastbytepos - firstbytepos))
+ # and finally the last byte
+ bitsleft = (self._offset + pos + bs.len) % 8
+ if not bitsleft:
+ bitsleft = 8
+ mask = (1 << (8 - bitsleft)) - 1
+ self._datastore.setbyte(lastbytepos, self._datastore.getbyte(lastbytepos) & mask)
+ self._datastore.setbyte(lastbytepos,
+ self._datastore.getbyte(lastbytepos) | (d.getbyte(d.bytelength - 1) & ~mask))
+ assert self._assertsanity()
+
+ def _delete(self, bits, pos):
+ """Delete bits at pos."""
+ assert 0 <= pos <= self.len
+ assert pos + bits <= self.len
+ if not pos:
+ # Cutting bits off at the start.
+ self._truncatestart(bits)
+ return
+ if pos + bits == self.len:
+ # Cutting bits off at the end.
+ self._truncateend(bits)
+ return
+ if pos > self.len - pos - bits:
+ # More bits before cut point than after it, so do bit shifting
+ # on the final bits.
+ end = self._slice(pos + bits, self.len)
+ assert self.len - pos > 0
+ self._truncateend(self.len - pos)
+ self._append(end)
+ return
+ # More bits after the cut point than before it.
+ start = self._slice(0, pos)
+ self._truncatestart(pos + bits)
+ self._prepend(start)
+ return
+
+ def _reversebytes(self, start, end):
+ """Reverse bytes in-place."""
+ # Make the start occur on a byte boundary
+ # TODO: We could be cleverer here to avoid changing the offset.
+ newoffset = 8 - (start % 8)
+ if newoffset == 8:
+ newoffset = 0
+ self._datastore = offsetcopy(self._datastore, newoffset)
+ # Now just reverse the byte data
+ toreverse = bytearray(self._datastore.getbyteslice((newoffset + start) // 8, (newoffset + end) // 8))
+ toreverse.reverse()
+ self._datastore.setbyteslice((newoffset + start) // 8, (newoffset + end) // 8, toreverse)
+
+ def _set(self, pos):
+ """Set bit at pos to 1."""
+ assert 0 <= pos < self.len
+ self._datastore.setbit(pos)
+
+ def _unset(self, pos):
+ """Set bit at pos to 0."""
+ assert 0 <= pos < self.len
+ self._datastore.unsetbit(pos)
+
+ def _invert(self, pos):
+ """Flip bit at pos 1<->0."""
+ assert 0 <= pos < self.len
+ self._datastore.invertbit(pos)
+
+ def _invert_all(self):
+ """Invert every bit."""
+ set = self._datastore.setbyte
+ get = self._datastore.getbyte
+ for p in xrange(self._datastore.byteoffset, self._datastore.byteoffset + self._datastore.bytelength):
+ set(p, 256 + ~get(p))
+
+ def _ilshift(self, n):
+ """Shift bits by n to the left in place. Return self."""
+ assert 0 < n <= self.len
+ self._append(Bits(n))
+ self._truncatestart(n)
+ return self
+
+ def _irshift(self, n):
+ """Shift bits by n to the right in place. Return self."""
+ assert 0 < n <= self.len
+ self._prepend(Bits(n))
+ self._truncateend(n)
+ return self
+
+ def _imul(self, n):
+ """Concatenate n copies of self in place. Return self."""
+ assert n >= 0
+ if not n:
+ self._clear()
+ return self
+ m = 1
+ old_len = self.len
+ while m * 2 < n:
+ self._append(self)
+ m *= 2
+ self._append(self[0:(n - m) * old_len])
+ return self
+
+ def _inplace_logical_helper(self, bs, f):
+ """Helper function containing most of the __ior__, __iand__, __ixor__ code."""
+ # Give the two bitstrings the same offset (modulo 8)
+ self_byteoffset, self_bitoffset = divmod(self._offset, 8)
+ bs_byteoffset, bs_bitoffset = divmod(bs._offset, 8)
+ if bs_bitoffset != self_bitoffset:
+ if not self_bitoffset:
+ bs._datastore = offsetcopy(bs._datastore, 0)
+ else:
+ self._datastore = offsetcopy(self._datastore, bs_bitoffset)
+ a = self._datastore.rawbytes
+ b = bs._datastore.rawbytes
+ for i in xrange(len(a)):
+ a[i] = f(a[i + self_byteoffset], b[i + bs_byteoffset])
+ return self
+
+ def _ior(self, bs):
+ return self._inplace_logical_helper(bs, operator.ior)
+
+ def _iand(self, bs):
+ return self._inplace_logical_helper(bs, operator.iand)
+
+ def _ixor(self, bs):
+ return self._inplace_logical_helper(bs, operator.xor)
+
+ def _readbits(self, length, start):
+ """Read some bits from the bitstring and return newly constructed bitstring."""
+ return self._slice(start, start + length)
+
+ def _validate_slice(self, start, end):
+ """Validate start and end and return them as positive bit positions."""
+ if start is None:
+ start = 0
+ elif start < 0:
+ start += self.len
+ if end is None:
+ end = self.len
+ elif end < 0:
+ end += self.len
+ if not 0 <= end <= self.len:
+ raise ValueError("end is not a valid position in the bitstring.")
+ if not 0 <= start <= self.len:
+ raise ValueError("start is not a valid position in the bitstring.")
+ if end < start:
+ raise ValueError("end must not be less than start.")
+ return start, end
+
+ def unpack(self, fmt, **kwargs):
+ """Interpret the whole bitstring using fmt and return list.
+
+ fmt -- A single string or a list of strings with comma separated tokens
+ describing how to interpret the bits in the bitstring. Items
+ can also be integers, for reading new bitstring of the given length.
+ kwargs -- A dictionary or keyword-value pairs - the keywords used in the
+ format string will be replaced with their given value.
+
+ Raises ValueError if the format is not understood. If not enough bits
+ are available then all bits to the end of the bitstring will be used.
+
+ See the docstring for 'read' for token examples.
+
+ """
+ return self._readlist(fmt, 0, **kwargs)[0]
+
+ def _readlist(self, fmt, pos, **kwargs):
+ tokens = []
+ stretchy_token = None
+ if isinstance(fmt, basestring):
+ fmt = [fmt]
+ # Not very optimal this, but replace integers with 'bits' tokens
+ # TODO: optimise
+ for i, f in enumerate(fmt):
+ if isinstance(f, numbers.Integral):
+ fmt[i] = "bits:{0}".format(f)
+ for f_item in fmt:
+ stretchy, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys())))
+ if stretchy:
+ if stretchy_token:
+ raise Error("It's not possible to have more than one 'filler' token.")
+ stretchy_token = stretchy
+ tokens.extend(tkns)
+ if not stretchy_token:
+ lst = []
+ for name, length, _ in tokens:
+ if length in kwargs:
+ length = kwargs[length]
+ if name == 'bytes':
+ length *= 8
+ if name in kwargs and length is None:
+ # Using default 'uint' - the name is really the length.
+ value, pos = self._readtoken('uint', pos, kwargs[name])
+ lst.append(value)
+ continue
+ value, pos = self._readtoken(name, pos, length)
+ if value is not None: # Don't append pad tokens
+ lst.append(value)
+ return lst, pos
+ stretchy_token = False
+ bits_after_stretchy_token = 0
+ for token in tokens:
+ name, length, _ = token
+ if length in kwargs:
+ length = kwargs[length]
+ if name == 'bytes':
+ length *= 8
+ if name in kwargs and length is None:
+ # Default 'uint'.
+ length = kwargs[name]
+ if stretchy_token:
+ if name in ('se', 'ue', 'sie', 'uie'):
+ raise Error("It's not possible to parse a variable"
+ "length token after a 'filler' token.")
+ else:
+ if length is None:
+ raise Error("It's not possible to have more than "
+ "one 'filler' token.")
+ bits_after_stretchy_token += length
+ if length is None and name not in ('se', 'ue', 'sie', 'uie'):
+ assert not stretchy_token
+ stretchy_token = token
+ bits_left = self.len - pos
+ return_values = []
+ for token in tokens:
+ name, length, _ = token
+ if token is stretchy_token:
+ # Set length to the remaining bits
+ length = max(bits_left - bits_after_stretchy_token, 0)
+ if length in kwargs:
+ length = kwargs[length]
+ if name == 'bytes':
+ length *= 8
+ if name in kwargs and length is None:
+ # Default 'uint'
+ length = kwargs[name]
+ if length is not None:
+ bits_left -= length
+ value, pos = self._readtoken(name, pos, length)
+ if value is not None:
+ return_values.append(value)
+ return return_values, pos
+
+ def _findbytes(self, bytes_, start, end, bytealigned):
+ """Quicker version of find when everything's whole byte
+ and byte aligned.
+
+ """
+ assert self._datastore.offset == 0
+ assert bytealigned is True
+ # Extract data bytes from bitstring to be found.
+ bytepos = (start + 7) // 8
+ found = False
+ p = bytepos
+ finalpos = end // 8
+ increment = max(1024, len(bytes_) * 10)
+ buffersize = increment + len(bytes_)
+ while p < finalpos:
+ # Read in file or from memory in overlapping chunks and search the chunks.
+ buf = bytearray(self._datastore.getbyteslice(p, min(p + buffersize, finalpos)))
+ pos = buf.find(bytes_)
+ if pos != -1:
+ found = True
+ p += pos
+ break
+ p += increment
+ if not found:
+ return ()
+ return (p * 8,)
+
+ def _findregex(self, reg_ex, start, end, bytealigned):
+ """Find first occurrence of a compiled regular expression.
+
+ Note that this doesn't support arbitrary regexes, in particular they
+ must match a known length.
+
+ """
+ p = start
+ length = len(reg_ex.pattern)
+ # We grab overlapping chunks of the binary representation and
+ # do an ordinary string search within that.
+ increment = max(4096, length * 10)
+ buffersize = increment + length
+ while p < end:
+ buf = self._readbin(min(buffersize, end - p), p)
+ # Test using regular expressions...
+ m = reg_ex.search(buf)
+ if m:
+ pos = m.start()
+ # pos = buf.find(targetbin)
+ # if pos != -1:
+ # if bytealigned then we only accept byte aligned positions.
+ if not bytealigned or (p + pos) % 8 == 0:
+ return (p + pos,)
+ if bytealigned:
+ # Advance to just beyond the non-byte-aligned match and try again...
+ p += pos + 1
+ continue
+ p += increment
+ # Not found, return empty tuple
+ return ()
+
+ def find(self, bs, start=None, end=None, bytealigned=None):
+ """Find first occurrence of substring bs.
+
+ Returns a single item tuple with the bit position if found, or an
+ empty tuple if not found. The bit position (pos property) will
+ also be set to the start of the substring if it is found.
+
+ bs -- The bitstring to find.
+ start -- The bit position to start the search. Defaults to 0.
+ end -- The bit position one past the last bit to search.
+ Defaults to self.len.
+ bytealigned -- If True the bitstring will only be
+ found on byte boundaries.
+
+ Raises ValueError if bs is empty, if start < 0, if end > self.len or
+ if end < start.
+
+ >>> BitArray('0xc3e').find('0b1111')
+ (6,)
+
+ """
+ bs = Bits(bs)
+ if not bs.len:
+ raise ValueError("Cannot find an empty bitstring.")
+ start, end = self._validate_slice(start, end)
+ if bytealigned is None:
+ bytealigned = globals()['bytealigned']
+ if bytealigned and not bs.len % 8 and not self._datastore.offset:
+ p = self._findbytes(bs.bytes, start, end, bytealigned)
+ else:
+ p = self._findregex(re.compile(bs._getbin()), start, end, bytealigned)
+ # If called from a class that has a pos, set it
+ try:
+ self._pos = p[0]
+ except (AttributeError, IndexError):
+ pass
+ return p
+
+ def findall(self, bs, start=None, end=None, count=None, bytealigned=None):
+ """Find all occurrences of bs. Return generator of bit positions.
+
+ bs -- The bitstring to find.
+ start -- The bit position to start the search. Defaults to 0.
+ end -- The bit position one past the last bit to search.
+ Defaults to self.len.
+ count -- The maximum number of occurrences to find.
+ bytealigned -- If True the bitstring will only be found on
+ byte boundaries.
+
+ Raises ValueError if bs is empty, if start < 0, if end > self.len or
+ if end < start.
+
+ Note that all occurrences of bs are found, even if they overlap.
+
+ """
+ if count is not None and count < 0:
+ raise ValueError("In findall, count must be >= 0.")
+ bs = Bits(bs)
+ start, end = self._validate_slice(start, end)
+ if bytealigned is None:
+ bytealigned = globals()['bytealigned']
+ c = 0
+ if bytealigned and not bs.len % 8 and not self._datastore.offset:
+ # Use the quick find method
+ f = self._findbytes
+ x = bs._getbytes()
+ else:
+ f = self._findregex
+ x = re.compile(bs._getbin())
+ while True:
+
+ p = f(x, start, end, bytealigned)
+ if not p:
+ break
+ if count is not None and c >= count:
+ return
+ c += 1
+ try:
+ self._pos = p[0]
+ except AttributeError:
+ pass
+ yield p[0]
+ if bytealigned:
+ start = p[0] + 8
+ else:
+ start = p[0] + 1
+ if start >= end:
+ break
+ return
+
+ def rfind(self, bs, start=None, end=None, bytealigned=None):
+ """Find final occurrence of substring bs.
+
+ Returns a single item tuple with the bit position if found, or an
+ empty tuple if not found. The bit position (pos property) will
+ also be set to the start of the substring if it is found.
+
+ bs -- The bitstring to find.
+ start -- The bit position to end the reverse search. Defaults to 0.
+ end -- The bit position one past the first bit to reverse search.
+ Defaults to self.len.
+ bytealigned -- If True the bitstring will only be found on byte
+ boundaries.
+
+ Raises ValueError if bs is empty, if start < 0, if end > self.len or
+ if end < start.
+
+ """
+ bs = Bits(bs)
+ start, end = self._validate_slice(start, end)
+ if bytealigned is None:
+ bytealigned = globals()['bytealigned']
+ if not bs.len:
+ raise ValueError("Cannot find an empty bitstring.")
+ # Search chunks starting near the end and then moving back
+ # until we find bs.
+ increment = max(8192, bs.len * 80)
+ buffersize = min(increment + bs.len, end - start)
+ pos = max(start, end - buffersize)
+ while True:
+ found = list(self.findall(bs, start=pos, end=pos + buffersize,
+ bytealigned=bytealigned))
+ if not found:
+ if pos == start:
+ return ()
+ pos = max(start, pos - increment)
+ continue
+ return (found[-1],)
+
+ def cut(self, bits, start=None, end=None, count=None):
+ """Return bitstring generator by cutting into bits sized chunks.
+
+ bits -- The size in bits of the bitstring chunks to generate.
+ start -- The bit position to start the first cut. Defaults to 0.
+ end -- The bit position one past the last bit to use in the cut.
+ Defaults to self.len.
+ count -- If specified then at most count items are generated.
+ Default is to cut as many times as possible.
+
+ """
+ start, end = self._validate_slice(start, end)
+ if count is not None and count < 0:
+ raise ValueError("Cannot cut - count must be >= 0.")
+ if bits <= 0:
+ raise ValueError("Cannot cut - bits must be >= 0.")
+ c = 0
+ while count is None or c < count:
+ c += 1
+ nextchunk = self._slice(start, min(start + bits, end))
+ if nextchunk.len != bits:
+ return
+ assert nextchunk._assertsanity()
+ yield nextchunk
+ start += bits
+ return
+
+ def split(self, delimiter, start=None, end=None, count=None,
+ bytealigned=None):
+ """Return bitstring generator by splittling using a delimiter.
+
+ The first item returned is the initial bitstring before the delimiter,
+ which may be an empty bitstring.
+
+ delimiter -- The bitstring used as the divider.
+ start -- The bit position to start the split. Defaults to 0.
+ end -- The bit position one past the last bit to use in the split.
+ Defaults to self.len.
+ count -- If specified then at most count items are generated.
+ Default is to split as many times as possible.
+ bytealigned -- If True splits will only occur on byte boundaries.
+
+ Raises ValueError if the delimiter is empty.
+
+ """
+ delimiter = Bits(delimiter)
+ if not delimiter.len:
+ raise ValueError("split delimiter cannot be empty.")
+ start, end = self._validate_slice(start, end)
+ if bytealigned is None:
+ bytealigned = globals()['bytealigned']
+ if count is not None and count < 0:
+ raise ValueError("Cannot split - count must be >= 0.")
+ if count == 0:
+ return
+ if bytealigned and not delimiter.len % 8 and not self._datastore.offset:
+ # Use the quick find method
+ f = self._findbytes
+ x = delimiter._getbytes()
+ else:
+ f = self._findregex
+ x = re.compile(delimiter._getbin())
+ found = f(x, start, end, bytealigned)
+ if not found:
+ # Initial bits are the whole bitstring being searched
+ yield self._slice(start, end)
+ return
+ # yield the bytes before the first occurrence of the delimiter, even if empty
+ yield self._slice(start, found[0])
+ startpos = pos = found[0]
+ c = 1
+ while count is None or c < count:
+ pos += delimiter.len
+ found = f(x, pos, end, bytealigned)
+ if not found:
+ # No more occurrences, so return the rest of the bitstring
+ yield self._slice(startpos, end)
+ return
+ c += 1
+ yield self._slice(startpos, found[0])
+ startpos = pos = found[0]
+ # Have generated count bitstrings, so time to quit.
+ return
+
+ def join(self, sequence):
+ """Return concatenation of bitstrings joined by self.
+
+ sequence -- A sequence of bitstrings.
+
+ """
+ s = self.__class__()
+ i = iter(sequence)
+ try:
+ s._append(Bits(next(i)))
+ while True:
+ n = next(i)
+ s._append(self)
+ s._append(Bits(n))
+ except StopIteration:
+ pass
+ return s
+
+ def tobytes(self):
+ """Return the bitstring as bytes, padding with zero bits if needed.
+
+ Up to seven zero bits will be added at the end to byte align.
+
+ """
+ d = offsetcopy(self._datastore, 0).rawbytes
+ # Need to ensure that unused bits at end are set to zero
+ unusedbits = 8 - self.len % 8
+ if unusedbits != 8:
+ d[-1] &= (0xff << unusedbits)
+ return bytes(d)
+
+ def tofile(self, f):
+ """Write the bitstring to a file object, padding with zero bits if needed.
+
+ Up to seven zero bits will be added at the end to byte align.
+
+ """
+ # If the bitstring is file based then we don't want to read it all
+ # in to memory.
+ chunksize = 1024 * 1024 # 1 MB chunks
+ if not self._offset:
+ a = 0
+ bytelen = self._datastore.bytelength
+ p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1))
+ while len(p) == chunksize:
+ f.write(p)
+ a += chunksize
+ p = self._datastore.getbyteslice(a, min(a + chunksize, bytelen - 1))
+ f.write(p)
+ # Now the final byte, ensuring that unused bits at end are set to 0.
+ bits_in_final_byte = self.len % 8
+ if not bits_in_final_byte:
+ bits_in_final_byte = 8
+ f.write(self[-bits_in_final_byte:].tobytes())
+ else:
+ # Really quite inefficient...
+ a = 0
+ b = a + chunksize * 8
+ while b <= self.len:
+ f.write(self._slice(a, b)._getbytes())
+ a += chunksize * 8
+ b += chunksize * 8
+ if a != self.len:
+ f.write(self._slice(a, self.len).tobytes())
+
+ def startswith(self, prefix, start=None, end=None):
+ """Return whether the current bitstring starts with prefix.
+
+ prefix -- The bitstring to search for.
+ start -- The bit position to start from. Defaults to 0.
+ end -- The bit position to end at. Defaults to self.len.
+
+ """
+ prefix = Bits(prefix)
+ start, end = self._validate_slice(start, end)
+ if end < start + prefix.len:
+ return False
+ end = start + prefix.len
+ return self._slice(start, end) == prefix
+
+ def endswith(self, suffix, start=None, end=None):
+ """Return whether the current bitstring ends with suffix.
+
+ suffix -- The bitstring to search for.
+ start -- The bit position to start from. Defaults to 0.
+ end -- The bit position to end at. Defaults to self.len.
+
+ """
+ suffix = Bits(suffix)
+ start, end = self._validate_slice(start, end)
+ if start + suffix.len > end:
+ return False
+ start = end - suffix.len
+ return self._slice(start, end) == suffix
+
+ def all(self, value, pos=None):
+ """Return True if one or many bits are all set to value.
+
+ value -- If value is True then checks for bits set to 1, otherwise
+ checks for bits set to 0.
+ pos -- An iterable of bit positions. Negative numbers are treated in
+ the same way as slice indices. Defaults to the whole bitstring.
+
+ """
+ value = bool(value)
+ length = self.len
+ if pos is None:
+ pos = xrange(self.len)
+ for p in pos:
+ if p < 0:
+ p += length
+ if not 0 <= p < length:
+ raise IndexError("Bit position {0} out of range.".format(p))
+ if not self._datastore.getbit(p) is value:
+ return False
+ return True
+
+ def any(self, value, pos=None):
+ """Return True if any of one or many bits are set to value.
+
+ value -- If value is True then checks for bits set to 1, otherwise
+ checks for bits set to 0.
+ pos -- An iterable of bit positions. Negative numbers are treated in
+ the same way as slice indices. Defaults to the whole bitstring.
+
+ """
+ value = bool(value)
+ length = self.len
+ if pos is None:
+ pos = xrange(self.len)
+ for p in pos:
+ if p < 0:
+ p += length
+ if not 0 <= p < length:
+ raise IndexError("Bit position {0} out of range.".format(p))
+ if self._datastore.getbit(p) is value:
+ return True
+ return False
+
+ def count(self, value):
+ """Return count of total number of either zero or one bits.
+
+ value -- If True then bits set to 1 are counted, otherwise bits set
+ to 0 are counted.
+
+ >>> Bits('0xef').count(1)
+ 7
+
+ """
+ if not self.len:
+ return 0
+ # count the number of 1s (from which it's easy to work out the 0s).
+ # Don't count the final byte yet.
+ count = sum(BIT_COUNT[self._datastore.getbyte(i)] for i in xrange(self._datastore.bytelength - 1))
+ # adjust for bits at start that aren't part of the bitstring
+ if self._offset:
+ count -= BIT_COUNT[self._datastore.getbyte(0) >> (8 - self._offset)]
+ # and count the last 1 - 8 bits at the end.
+ endbits = self._datastore.bytelength * 8 - (self._offset + self.len)
+ count += BIT_COUNT[self._datastore.getbyte(self._datastore.bytelength - 1) >> endbits]
+ return count if value else self.len - count
+
+ # Create native-endian functions as aliases depending on the byteorder
+ if byteorder == 'little':
+ _setfloatne = _setfloatle
+ _readfloatne = _readfloatle
+ _getfloatne = _getfloatle
+ _setuintne = _setuintle
+ _readuintne = _readuintle
+ _getuintne = _getuintle
+ _setintne = _setintle
+ _readintne = _readintle
+ _getintne = _getintle
+ else:
+ _setfloatne = _setfloat
+ _readfloatne = _readfloat
+ _getfloatne = _getfloat
+ _setuintne = _setuintbe
+ _readuintne = _readuintbe
+ _getuintne = _getuintbe
+ _setintne = _setintbe
+ _readintne = _readintbe
+ _getintne = _getintbe
+
+ _offset = property(_getoffset)
+
+ len = property(_getlength,
+ doc="""The length of the bitstring in bits. Read only.
+ """)
+ length = property(_getlength,
+ doc="""The length of the bitstring in bits. Read only.
+ """)
+ bool = property(_getbool,
+ doc="""The bitstring as a bool (True or False). Read only.
+ """)
+ hex = property(_gethex,
+ doc="""The bitstring as a hexadecimal string. Read only.
+ """)
+ bin = property(_getbin,
+ doc="""The bitstring as a binary string. Read only.
+ """)
+ oct = property(_getoct,
+ doc="""The bitstring as an octal string. Read only.
+ """)
+ bytes = property(_getbytes,
+ doc="""The bitstring as a bytes object. Read only.
+ """)
+ int = property(_getint,
+ doc="""The bitstring as a two's complement signed int. Read only.
+ """)
+ uint = property(_getuint,
+ doc="""The bitstring as a two's complement unsigned int. Read only.
+ """)
+ float = property(_getfloat,
+ doc="""The bitstring as a floating point number. Read only.
+ """)
+ intbe = property(_getintbe,
+ doc="""The bitstring as a two's complement big-endian signed int. Read only.
+ """)
+ uintbe = property(_getuintbe,
+ doc="""The bitstring as a two's complement big-endian unsigned int. Read only.
+ """)
+ floatbe = property(_getfloat,
+ doc="""The bitstring as a big-endian floating point number. Read only.
+ """)
+ intle = property(_getintle,
+ doc="""The bitstring as a two's complement little-endian signed int. Read only.
+ """)
+ uintle = property(_getuintle,
+ doc="""The bitstring as a two's complement little-endian unsigned int. Read only.
+ """)
+ floatle = property(_getfloatle,
+ doc="""The bitstring as a little-endian floating point number. Read only.
+ """)
+ intne = property(_getintne,
+ doc="""The bitstring as a two's complement native-endian signed int. Read only.
+ """)
+ uintne = property(_getuintne,
+ doc="""The bitstring as a two's complement native-endian unsigned int. Read only.
+ """)
+ floatne = property(_getfloatne,
+ doc="""The bitstring as a native-endian floating point number. Read only.
+ """)
+ ue = property(_getue,
+ doc="""The bitstring as an unsigned exponential-Golomb code. Read only.
+ """)
+ se = property(_getse,
+ doc="""The bitstring as a signed exponential-Golomb code. Read only.
+ """)
+ uie = property(_getuie,
+ doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read only.
+ """)
+ sie = property(_getsie,
+ doc="""The bitstring as a signed interleaved exponential-Golomb code. Read only.
+ """)
+
+
+# Dictionary that maps token names to the function that reads them.
+name_to_read = {'uint': Bits._readuint,
+ 'uintle': Bits._readuintle,
+ 'uintbe': Bits._readuintbe,
+ 'uintne': Bits._readuintne,
+ 'int': Bits._readint,
+ 'intle': Bits._readintle,
+ 'intbe': Bits._readintbe,
+ 'intne': Bits._readintne,
+ 'float': Bits._readfloat,
+ 'floatbe': Bits._readfloat, # floatbe is a synonym for float
+ 'floatle': Bits._readfloatle,
+ 'floatne': Bits._readfloatne,
+ 'hex': Bits._readhex,
+ 'oct': Bits._readoct,
+ 'bin': Bits._readbin,
+ 'bits': Bits._readbits,
+ 'bytes': Bits._readbytes,
+ 'ue': Bits._readue,
+ 'se': Bits._readse,
+ 'uie': Bits._readuie,
+ 'sie': Bits._readsie,
+ 'bool': Bits._readbool,
+ }
+
+# Dictionaries for mapping init keywords with init functions.
+init_with_length_and_offset = {'bytes': Bits._setbytes_safe,
+ 'filename': Bits._setfile,
+ }
+
+init_with_length_only = {'uint': Bits._setuint,
+ 'int': Bits._setint,
+ 'float': Bits._setfloat,
+ 'uintbe': Bits._setuintbe,
+ 'intbe': Bits._setintbe,
+ 'floatbe': Bits._setfloat,
+ 'uintle': Bits._setuintle,
+ 'intle': Bits._setintle,
+ 'floatle': Bits._setfloatle,
+ 'uintne': Bits._setuintne,
+ 'intne': Bits._setintne,
+ 'floatne': Bits._setfloatne,
+ }
+
+init_without_length_or_offset = {'bin': Bits._setbin_safe,
+ 'hex': Bits._sethex,
+ 'oct': Bits._setoct,
+ 'ue': Bits._setue,
+ 'se': Bits._setse,
+ 'uie': Bits._setuie,
+ 'sie': Bits._setsie,
+ 'bool': Bits._setbool,
+ }
+
+
+class BitArray(Bits):
+ """A container holding a mutable sequence of bits.
+
+ Subclass of the immutable Bits class. Inherits all of its
+ methods (except __hash__) and adds mutating methods.
+
+ Mutating methods:
+
+ append() -- Append a bitstring.
+ byteswap() -- Change byte endianness in-place.
+ insert() -- Insert a bitstring.
+ invert() -- Flip bit(s) between one and zero.
+ overwrite() -- Overwrite a section with a new bitstring.
+ prepend() -- Prepend a bitstring.
+ replace() -- Replace occurrences of one bitstring with another.
+ reverse() -- Reverse bits in-place.
+ rol() -- Rotate bits to the left.
+ ror() -- Rotate bits to the right.
+ set() -- Set bit(s) to 1 or 0.
+
+ Methods inherited from Bits:
+
+ all() -- Check if all specified bits are set to 1 or 0.
+ any() -- Check if any of specified bits are set to 1 or 0.
+ count() -- Count the number of bits set to 1 or 0.
+ cut() -- Create generator of constant sized chunks.
+ endswith() -- Return whether the bitstring ends with a sub-string.
+ find() -- Find a sub-bitstring in the current bitstring.
+ findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
+ join() -- Join bitstrings together using current bitstring.
+ rfind() -- Seek backwards to find a sub-bitstring.
+ split() -- Create generator of chunks split by a delimiter.
+ startswith() -- Return whether the bitstring starts with a sub-bitstring.
+ tobytes() -- Return bitstring as bytes, padding if needed.
+ tofile() -- Write bitstring to file, padding if needed.
+ unpack() -- Interpret bits using format string.
+
+ Special methods:
+
+ Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^=
+ in addition to the inherited [], ==, !=, +, *, ~, <<, >>, &, | and ^.
+
+ Properties:
+
+ bin -- The bitstring as a binary string.
+ bool -- For single bit bitstrings, interpret as True or False.
+ bytepos -- The current byte position in the bitstring.
+ bytes -- The bitstring as a bytes object.
+ float -- Interpret as a floating point number.
+ floatbe -- Interpret as a big-endian floating point number.
+ floatle -- Interpret as a little-endian floating point number.
+ floatne -- Interpret as a native-endian floating point number.
+ hex -- The bitstring as a hexadecimal string.
+ int -- Interpret as a two's complement signed integer.
+ intbe -- Interpret as a big-endian signed integer.
+ intle -- Interpret as a little-endian signed integer.
+ intne -- Interpret as a native-endian signed integer.
+ len -- Length of the bitstring in bits.
+ oct -- The bitstring as an octal string.
+ pos -- The current bit position in the bitstring.
+ se -- Interpret as a signed exponential-Golomb code.
+ ue -- Interpret as an unsigned exponential-Golomb code.
+ sie -- Interpret as a signed interleaved exponential-Golomb code.
+ uie -- Interpret as an unsigned interleaved exponential-Golomb code.
+ uint -- Interpret as a two's complement unsigned integer.
+ uintbe -- Interpret as a big-endian unsigned integer.
+ uintle -- Interpret as a little-endian unsigned integer.
+ uintne -- Interpret as a native-endian unsigned integer.
+
+ """
+
+ __slots__ = ()
+
+ # As BitArray objects are mutable, we shouldn't allow them to be hashed.
+ __hash__ = None
+
+ def __init__(self, auto=None, length=None, offset=None, **kwargs):
+ """Either specify an 'auto' initialiser:
+ auto -- a string of comma separated tokens, an integer, a file object,
+ a bytearray, a boolean iterable or another bitstring.
+
+ Or initialise via **kwargs with one (and only one) of:
+ bytes -- raw data as a string, for example read from a binary file.
+ bin -- binary string representation, e.g. '0b001010'.
+ hex -- hexadecimal string representation, e.g. '0x2ef'
+ oct -- octal string representation, e.g. '0o777'.
+ uint -- an unsigned integer.
+ int -- a signed integer.
+ float -- a floating point number.
+ uintbe -- an unsigned big-endian whole byte integer.
+ intbe -- a signed big-endian whole byte integer.
+ floatbe - a big-endian floating point number.
+ uintle -- an unsigned little-endian whole byte integer.
+ intle -- a signed little-endian whole byte integer.
+ floatle -- a little-endian floating point number.
+ uintne -- an unsigned native-endian whole byte integer.
+ intne -- a signed native-endian whole byte integer.
+ floatne -- a native-endian floating point number.
+ se -- a signed exponential-Golomb code.
+ ue -- an unsigned exponential-Golomb code.
+ sie -- a signed interleaved exponential-Golomb code.
+ uie -- an unsigned interleaved exponential-Golomb code.
+ bool -- a boolean (True or False).
+ filename -- a file which will be opened in binary read-only mode.
+
+ Other keyword arguments:
+ length -- length of the bitstring in bits, if needed and appropriate.
+ It must be supplied for all integer and float initialisers.
+ offset -- bit offset to the data. These offset bits are
+ ignored and this is intended for use when
+ initialising using 'bytes' or 'filename'.
+
+ """
+ # For mutable BitArrays we always read in files to memory:
+ if not isinstance(self._datastore, ByteStore):
+ self._ensureinmemory()
+
+ def __new__(cls, auto=None, length=None, offset=None, **kwargs):
+ x = super(BitArray, cls).__new__(cls)
+ y = Bits.__new__(BitArray, auto, length, offset, **kwargs)
+ x._datastore = y._datastore
+ return x
+
+ def __iadd__(self, bs):
+ """Append bs to current bitstring. Return self.
+
+ bs -- the bitstring to append.
+
+ """
+ self.append(bs)
+ return self
+
+ def __copy__(self):
+ """Return a new copy of the BitArray."""
+ s_copy = BitArray()
+ if not isinstance(self._datastore, ByteStore):
+ # Let them both point to the same (invariant) array.
+ # If either gets modified then at that point they'll be read into memory.
+ s_copy._datastore = self._datastore
+ else:
+ s_copy._datastore = copy.copy(self._datastore)
+ return s_copy
+
+ def __setitem__(self, key, value):
+ """Set item or range to new value.
+
+ Indices are in units of the step parameter (default 1 bit).
+ Stepping is used to specify the number of bits in each item.
+
+ If the length of the bitstring is changed then pos will be moved
+ to after the inserted section, otherwise it will remain unchanged.
+
+ >>> s = BitArray('0xff')
+ >>> s[0:1:4] = '0xe'
+ >>> print s
+ '0xef'
+ >>> s[4:4] = '0x00'
+ >>> print s
+ '0xe00f'
+
+ """
+ try:
+ # A slice
+ start, step = 0, 1
+ if key.step is not None:
+ step = key.step
+ except AttributeError:
+ # single element
+ if key < 0:
+ key += self.len
+ if not 0 <= key < self.len:
+ raise IndexError("Slice index out of range.")
+ if isinstance(value, numbers.Integral):
+ if not value:
+ self._unset(key)
+ return
+ if value in (1, -1):
+ self._set(key)
+ return
+ raise ValueError("Cannot set a single bit with integer {0}.".format(value))
+ value = Bits(value)
+ if value.len == 1:
+ # TODO: this can't be optimal
+ if value[0]:
+ self._set(key)
+ else:
+ self._unset(key)
+ else:
+ self._delete(1, key)
+ self._insert(value, key)
+ return
+ else:
+ if step != 1:
+ # convert to binary string and use string slicing
+ # TODO: Horribly inefficent
+ temp = list(self._getbin())
+ v = list(Bits(value)._getbin())
+ temp.__setitem__(key, v)
+ self._setbin_unsafe(''.join(temp))
+ return
+
+ # If value is an integer then we want to set the slice to that
+ # value rather than initialise a new bitstring of that length.
+ if not isinstance(value, numbers.Integral):
+ try:
+ # TODO: Better way than calling constructor here?
+ value = Bits(value)
+ except TypeError:
+ raise TypeError("Bitstring, integer or string expected. "
+ "Got {0}.".format(type(value)))
+ if key.start is not None:
+ start = key.start
+ if key.start < 0:
+ start += self.len
+ if start < 0:
+ start = 0
+ stop = self.len
+ if key.stop is not None:
+ stop = key.stop
+ if key.stop < 0:
+ stop += self.len
+ if start > stop:
+ # The standard behaviour for lists is to just insert at the
+ # start position if stop < start and step == 1.
+ stop = start
+ if isinstance(value, numbers.Integral):
+ if value >= 0:
+ value = self.__class__(uint=value, length=stop - start)
+ else:
+ value = self.__class__(int=value, length=stop - start)
+ stop = min(stop, self.len)
+ start = max(start, 0)
+ start = min(start, stop)
+ if (stop - start) == value.len:
+ if not value.len:
+ return
+ if step >= 0:
+ self._overwrite(value, start)
+ else:
+ self._overwrite(value.__getitem__(slice(None, None, 1)), start)
+ else:
+ # TODO: A delete then insert is wasteful - it could do unneeded shifts.
+ # Could be either overwrite + insert or overwrite + delete.
+ self._delete(stop - start, start)
+ if step >= 0:
+ self._insert(value, start)
+ else:
+ self._insert(value.__getitem__(slice(None, None, 1)), start)
+ # pos is now after the inserted piece.
+ return
+
+ def __delitem__(self, key):
+ """Delete item or range.
+
+ Indices are in units of the step parameter (default 1 bit).
+ Stepping is used to specify the number of bits in each item.
+
+ >>> a = BitArray('0x001122')
+ >>> del a[1:2:8]
+ >>> print a
+ 0x0022
+
+ """
+ try:
+ # A slice
+ start = 0
+ step = key.step if key.step is not None else 1
+ except AttributeError:
+ # single element
+ if key < 0:
+ key += self.len
+ if not 0 <= key < self.len:
+ raise IndexError("Slice index out of range.")
+ self._delete(1, key)
+ return
+ else:
+ if step != 1:
+ # convert to binary string and use string slicing
+ # TODO: Horribly inefficent
+ temp = list(self._getbin())
+ temp.__delitem__(key)
+ self._setbin_unsafe(''.join(temp))
+ return
+ stop = key.stop
+ if key.start is not None:
+ start = key.start
+ if key.start < 0 and stop is None:
+ start += self.len
+ if start < 0:
+ start = 0
+ if stop is None:
+ stop = self.len
+ if start > stop:
+ return
+ stop = min(stop, self.len)
+ start = max(start, 0)
+ start = min(start, stop)
+ self._delete(stop - start, start)
+ return
+
+ def __ilshift__(self, n):
+ """Shift bits by n to the left in place. Return self.
+
+ n -- the number of bits to shift. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot shift by a negative amount.")
+ if not self.len:
+ raise ValueError("Cannot shift an empty bitstring.")
+ if not n:
+ return self
+ n = min(n, self.len)
+ return self._ilshift(n)
+
+ def __irshift__(self, n):
+ """Shift bits by n to the right in place. Return self.
+
+ n -- the number of bits to shift. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot shift by a negative amount.")
+ if not self.len:
+ raise ValueError("Cannot shift an empty bitstring.")
+ if not n:
+ return self
+ n = min(n, self.len)
+ return self._irshift(n)
+
+ def __imul__(self, n):
+ """Concatenate n copies of self in place. Return self.
+
+ Called for expressions of the form 'a *= 3'.
+ n -- The number of concatenations. Must be >= 0.
+
+ """
+ if n < 0:
+ raise ValueError("Cannot multiply by a negative integer.")
+ return self._imul(n)
+
+ def __ior__(self, bs):
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for |= operator.")
+ return self._ior(bs)
+
+ def __iand__(self, bs):
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for &= operator.")
+ return self._iand(bs)
+
+ def __ixor__(self, bs):
+ bs = Bits(bs)
+ if self.len != bs.len:
+ raise ValueError("Bitstrings must have the same length "
+ "for ^= operator.")
+ return self._ixor(bs)
+
+ def replace(self, old, new, start=None, end=None, count=None,
+ bytealigned=None):
+ """Replace all occurrences of old with new in place.
+
+ Returns number of replacements made.
+
+ old -- The bitstring to replace.
+ new -- The replacement bitstring.
+ start -- Any occurrences that start before this will not be replaced.
+ Defaults to 0.
+ end -- Any occurrences that finish after this will not be replaced.
+ Defaults to self.len.
+ count -- The maximum number of replacements to make. Defaults to
+ replace all occurrences.
+ bytealigned -- If True replacements will only be made on byte
+ boundaries.
+
+ Raises ValueError if old is empty or if start or end are
+ out of range.
+
+ """
+ old = Bits(old)
+ new = Bits(new)
+ if not old.len:
+ raise ValueError("Empty bitstring cannot be replaced.")
+ start, end = self._validate_slice(start, end)
+ if bytealigned is None:
+ bytealigned = globals()['bytealigned']
+ # Adjust count for use in split()
+ if count is not None:
+ count += 1
+ sections = self.split(old, start, end, count, bytealigned)
+ lengths = [s.len for s in sections]
+ if len(lengths) == 1:
+ # Didn't find anything to replace.
+ return 0 # no replacements done
+ if new is self:
+ # Prevent self assignment woes
+ new = copy.copy(self)
+ positions = [lengths[0] + start]
+ for l in lengths[1:-1]:
+ # Next position is the previous one plus the length of the next section.
+ positions.append(positions[-1] + l)
+ # We have all the positions that need replacements. We do them
+ # in reverse order so that they won't move around as we replace.
+ positions.reverse()
+ try:
+ # Need to calculate new pos, if this is a bitstream
+ newpos = self._pos
+ for p in positions:
+ self[p:p + old.len] = new
+ if old.len != new.len:
+ diff = new.len - old.len
+ for p in positions:
+ if p >= newpos:
+ continue
+ if p + old.len <= newpos:
+ newpos += diff
+ else:
+ newpos = p
+ self._pos = newpos
+ except AttributeError:
+ for p in positions:
+ self[p:p + old.len] = new
+ assert self._assertsanity()
+ return len(lengths) - 1
+
+ def insert(self, bs, pos=None):
+ """Insert bs at bit position pos.
+
+ bs -- The bitstring to insert.
+ pos -- The bit position to insert at.
+
+ Raises ValueError if pos < 0 or pos > self.len.
+
+ """
+ bs = Bits(bs)
+ if not bs.len:
+ return self
+ if bs is self:
+ bs = self.__copy__()
+ if pos is None:
+ try:
+ pos = self._pos
+ except AttributeError:
+ raise TypeError("insert require a bit position for this type.")
+ if pos < 0:
+ pos += self.len
+ if not 0 <= pos <= self.len:
+ raise ValueError("Invalid insert position.")
+ self._insert(bs, pos)
+
+ def overwrite(self, bs, pos=None):
+ """Overwrite with bs at bit position pos.
+
+ bs -- The bitstring to overwrite with.
+ pos -- The bit position to begin overwriting from.
+
+ Raises ValueError if pos < 0 or pos + bs.len > self.len
+
+ """
+ bs = Bits(bs)
+ if not bs.len:
+ return
+ if pos is None:
+ try:
+ pos = self._pos
+ except AttributeError:
+ raise TypeError("overwrite require a bit position for this type.")
+ if pos < 0:
+ pos += self.len
+ if pos < 0 or pos + bs.len > self.len:
+ raise ValueError("Overwrite exceeds boundary of bitstring.")
+ self._overwrite(bs, pos)
+ try:
+ self._pos = pos + bs.len
+ except AttributeError:
+ pass
+
+ def append(self, bs):
+ """Append a bitstring to the current bitstring.
+
+ bs -- The bitstring to append.
+
+ """
+ # The offset is a hint to make bs easily appendable.
+ bs = self._converttobitstring(bs, offset=(self.len + self._offset) % 8)
+ self._append(bs)
+
+ def prepend(self, bs):
+ """Prepend a bitstring to the current bitstring.
+
+ bs -- The bitstring to prepend.
+
+ """
+ bs = Bits(bs)
+ self._prepend(bs)
+
+ def reverse(self, start=None, end=None):
+ """Reverse bits in-place.
+
+ start -- Position of first bit to reverse. Defaults to 0.
+ end -- One past the position of the last bit to reverse.
+ Defaults to self.len.
+
+ Using on an empty bitstring will have no effect.
+
+ Raises ValueError if start < 0, end > self.len or end < start.
+
+ """
+ start, end = self._validate_slice(start, end)
+ if start == 0 and end == self.len:
+ self._reverse()
+ return
+ s = self._slice(start, end)
+ s._reverse()
+ self[start:end] = s
+
+ def set(self, value, pos=None):
+ """Set one or many bits to 1 or 0.
+
+ value -- If True bits are set to 1, otherwise they are set to 0.
+ pos -- Either a single bit position or an iterable of bit positions.
+ Negative numbers are treated in the same way as slice indices.
+ Defaults to the entire bitstring.
+
+ Raises IndexError if pos < -self.len or pos >= self.len.
+
+ """
+ f = self._set if value else self._unset
+ if pos is None:
+ pos = xrange(self.len)
+ try:
+ length = self.len
+ for p in pos:
+ if p < 0:
+ p += length
+ if not 0 <= p < length:
+ raise IndexError("Bit position {0} out of range.".format(p))
+ f(p)
+ except TypeError:
+ # Single pos
+ if pos < 0:
+ pos += self.len
+ if not 0 <= pos < length:
+ raise IndexError("Bit position {0} out of range.".format(pos))
+ f(pos)
+
+ def invert(self, pos=None):
+ """Invert one or many bits from 0 to 1 or vice versa.
+
+ pos -- Either a single bit position or an iterable of bit positions.
+ Negative numbers are treated in the same way as slice indices.
+
+ Raises IndexError if pos < -self.len or pos >= self.len.
+
+ """
+ if pos is None:
+ self._invert_all()
+ return
+ if not isinstance(pos, collections.Iterable):
+ pos = (pos,)
+ length = self.len
+
+ for p in pos:
+ if p < 0:
+ p += length
+ if not 0 <= p < length:
+ raise IndexError("Bit position {0} out of range.".format(p))
+ self._invert(p)
+
+ def ror(self, bits, start=None, end=None):
+ """Rotate bits to the right in-place.
+
+ bits -- The number of bits to rotate by.
+ start -- Start of slice to rotate. Defaults to 0.
+ end -- End of slice to rotate. Defaults to self.len.
+
+ Raises ValueError if bits < 0.
+
+ """
+ if not self.len:
+ raise Error("Cannot rotate an empty bitstring.")
+ if bits < 0:
+ raise ValueError("Cannot rotate right by negative amount.")
+ start, end = self._validate_slice(start, end)
+ bits %= (end - start)
+ if not bits:
+ return
+ rhs = self._slice(end - bits, end)
+ self._delete(bits, end - bits)
+ self._insert(rhs, start)
+
+ def rol(self, bits, start=None, end=None):
+ """Rotate bits to the left in-place.
+
+ bits -- The number of bits to rotate by.
+ start -- Start of slice to rotate. Defaults to 0.
+ end -- End of slice to rotate. Defaults to self.len.
+
+ Raises ValueError if bits < 0.
+
+ """
+ if not self.len:
+ raise Error("Cannot rotate an empty bitstring.")
+ if bits < 0:
+ raise ValueError("Cannot rotate left by negative amount.")
+ start, end = self._validate_slice(start, end)
+ bits %= (end - start)
+ if not bits:
+ return
+ lhs = self._slice(start, start + bits)
+ self._delete(bits, start)
+ self._insert(lhs, end - bits)
+
+ def byteswap(self, fmt=None, start=None, end=None, repeat=True):
+ """Change the endianness in-place. Return number of repeats of fmt done.
+
+ fmt -- A compact structure string, an integer number of bytes or
+ an iterable of integers. Defaults to 0, which byte reverses the
+ whole bitstring.
+ start -- Start bit position, defaults to 0.
+ end -- End bit position, defaults to self.len.
+ repeat -- If True (the default) the byte swapping pattern is repeated
+ as much as possible.
+
+ """
+ start, end = self._validate_slice(start, end)
+ if fmt is None or fmt == 0:
+ # reverse all of the whole bytes.
+ bytesizes = [(end - start) // 8]
+ elif isinstance(fmt, numbers.Integral):
+ if fmt < 0:
+ raise ValueError("Improper byte length {0}.".format(fmt))
+ bytesizes = [fmt]
+ elif isinstance(fmt, basestring):
+ m = STRUCT_PACK_RE.match(fmt)
+ if not m:
+ raise ValueError("Cannot parse format string {0}.".format(fmt))
+ # Split the format string into a list of 'q', '4h' etc.
+ formatlist = re.findall(STRUCT_SPLIT_RE, m.group('fmt'))
+ # Now deal with multiplicative factors, 4h -> hhhh etc.
+ bytesizes = []
+ for f in formatlist:
+ if len(f) == 1:
+ bytesizes.append(PACK_CODE_SIZE[f])
+ else:
+ bytesizes.extend([PACK_CODE_SIZE[f[-1]]] * int(f[:-1]))
+ elif isinstance(fmt, collections.Iterable):
+ bytesizes = fmt
+ for bytesize in bytesizes:
+ if not isinstance(bytesize, numbers.Integral) or bytesize < 0:
+ raise ValueError("Improper byte length {0}.".format(bytesize))
+ else:
+ raise TypeError("Format must be an integer, string or iterable.")
+
+ repeats = 0
+ totalbitsize = 8 * sum(bytesizes)
+ if not totalbitsize:
+ return 0
+ if repeat:
+ # Try to repeat up to the end of the bitstring.
+ finalbit = end
+ else:
+ # Just try one (set of) byteswap(s).
+ finalbit = start + totalbitsize
+ for patternend in xrange(start + totalbitsize, finalbit + 1, totalbitsize):
+ bytestart = patternend - totalbitsize
+ for bytesize in bytesizes:
+ byteend = bytestart + bytesize * 8
+ self._reversebytes(bytestart, byteend)
+ bytestart += bytesize * 8
+ repeats += 1
+ return repeats
+
+ def clear(self):
+ """Remove all bits, reset to zero length."""
+ self._clear()
+
+ def copy(self):
+ """Return a copy of the bitstring."""
+ return self._copy()
+
+ int = property(Bits._getint, Bits._setint,
+ doc="""The bitstring as a two's complement signed int. Read and write.
+ """)
+ uint = property(Bits._getuint, Bits._setuint,
+ doc="""The bitstring as a two's complement unsigned int. Read and write.
+ """)
+ float = property(Bits._getfloat, Bits._setfloat,
+ doc="""The bitstring as a floating point number. Read and write.
+ """)
+ intbe = property(Bits._getintbe, Bits._setintbe,
+ doc="""The bitstring as a two's complement big-endian signed int. Read and write.
+ """)
+ uintbe = property(Bits._getuintbe, Bits._setuintbe,
+ doc="""The bitstring as a two's complement big-endian unsigned int. Read and write.
+ """)
+ floatbe = property(Bits._getfloat, Bits._setfloat,
+ doc="""The bitstring as a big-endian floating point number. Read and write.
+ """)
+ intle = property(Bits._getintle, Bits._setintle,
+ doc="""The bitstring as a two's complement little-endian signed int. Read and write.
+ """)
+ uintle = property(Bits._getuintle, Bits._setuintle,
+ doc="""The bitstring as a two's complement little-endian unsigned int. Read and write.
+ """)
+ floatle = property(Bits._getfloatle, Bits._setfloatle,
+ doc="""The bitstring as a little-endian floating point number. Read and write.
+ """)
+ intne = property(Bits._getintne, Bits._setintne,
+ doc="""The bitstring as a two's complement native-endian signed int. Read and write.
+ """)
+ uintne = property(Bits._getuintne, Bits._setuintne,
+ doc="""The bitstring as a two's complement native-endian unsigned int. Read and write.
+ """)
+ floatne = property(Bits._getfloatne, Bits._setfloatne,
+ doc="""The bitstring as a native-endian floating point number. Read and write.
+ """)
+ ue = property(Bits._getue, Bits._setue,
+ doc="""The bitstring as an unsigned exponential-Golomb code. Read and write.
+ """)
+ se = property(Bits._getse, Bits._setse,
+ doc="""The bitstring as a signed exponential-Golomb code. Read and write.
+ """)
+ uie = property(Bits._getuie, Bits._setuie,
+ doc="""The bitstring as an unsigned interleaved exponential-Golomb code. Read and write.
+ """)
+ sie = property(Bits._getsie, Bits._setsie,
+ doc="""The bitstring as a signed interleaved exponential-Golomb code. Read and write.
+ """)
+ hex = property(Bits._gethex, Bits._sethex,
+ doc="""The bitstring as a hexadecimal string. Read and write.
+ """)
+ bin = property(Bits._getbin, Bits._setbin_safe,
+ doc="""The bitstring as a binary string. Read and write.
+ """)
+ oct = property(Bits._getoct, Bits._setoct,
+ doc="""The bitstring as an octal string. Read and write.
+ """)
+ bool = property(Bits._getbool, Bits._setbool,
+ doc="""The bitstring as a bool (True or False). Read and write.
+ """)
+ bytes = property(Bits._getbytes, Bits._setbytes_safe,
+ doc="""The bitstring as a ordinary string. Read and write.
+ """)
+
+
+
+class ConstBitStream(Bits):
+ """A container or stream holding an immutable sequence of bits.
+
+ For a mutable container use the BitStream class instead.
+
+ Methods inherited from Bits:
+
+ all() -- Check if all specified bits are set to 1 or 0.
+ any() -- Check if any of specified bits are set to 1 or 0.
+ count() -- Count the number of bits set to 1 or 0.
+ cut() -- Create generator of constant sized chunks.
+ endswith() -- Return whether the bitstring ends with a sub-string.
+ find() -- Find a sub-bitstring in the current bitstring.
+ findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
+ join() -- Join bitstrings together using current bitstring.
+ rfind() -- Seek backwards to find a sub-bitstring.
+ split() -- Create generator of chunks split by a delimiter.
+ startswith() -- Return whether the bitstring starts with a sub-bitstring.
+ tobytes() -- Return bitstring as bytes, padding if needed.
+ tofile() -- Write bitstring to file, padding if needed.
+ unpack() -- Interpret bits using format string.
+
+ Other methods:
+
+ bytealign() -- Align to next byte boundary.
+ peek() -- Peek at and interpret next bits as a single item.
+ peeklist() -- Peek at and interpret next bits as a list of items.
+ read() -- Read and interpret next bits as a single item.
+ readlist() -- Read and interpret next bits as a list of items.
+
+ Special methods:
+
+ Also available are the operators [], ==, !=, +, *, ~, <<, >>, &, |, ^.
+
+ Properties:
+
+ bin -- The bitstring as a binary string.
+ bool -- For single bit bitstrings, interpret as True or False.
+ bytepos -- The current byte position in the bitstring.
+ bytes -- The bitstring as a bytes object.
+ float -- Interpret as a floating point number.
+ floatbe -- Interpret as a big-endian floating point number.
+ floatle -- Interpret as a little-endian floating point number.
+ floatne -- Interpret as a native-endian floating point number.
+ hex -- The bitstring as a hexadecimal string.
+ int -- Interpret as a two's complement signed integer.
+ intbe -- Interpret as a big-endian signed integer.
+ intle -- Interpret as a little-endian signed integer.
+ intne -- Interpret as a native-endian signed integer.
+ len -- Length of the bitstring in bits.
+ oct -- The bitstring as an octal string.
+ pos -- The current bit position in the bitstring.
+ se -- Interpret as a signed exponential-Golomb code.
+ ue -- Interpret as an unsigned exponential-Golomb code.
+ sie -- Interpret as a signed interleaved exponential-Golomb code.
+ uie -- Interpret as an unsigned interleaved exponential-Golomb code.
+ uint -- Interpret as a two's complement unsigned integer.
+ uintbe -- Interpret as a big-endian unsigned integer.
+ uintle -- Interpret as a little-endian unsigned integer.
+ uintne -- Interpret as a native-endian unsigned integer.
+
+ """
+
+ __slots__ = ('_pos')
+
+ def __init__(self, auto=None, length=None, offset=None, **kwargs):
+ """Either specify an 'auto' initialiser:
+ auto -- a string of comma separated tokens, an integer, a file object,
+ a bytearray, a boolean iterable or another bitstring.
+
+ Or initialise via **kwargs with one (and only one) of:
+ bytes -- raw data as a string, for example read from a binary file.
+ bin -- binary string representation, e.g. '0b001010'.
+ hex -- hexadecimal string representation, e.g. '0x2ef'
+ oct -- octal string representation, e.g. '0o777'.
+ uint -- an unsigned integer.
+ int -- a signed integer.
+ float -- a floating point number.
+ uintbe -- an unsigned big-endian whole byte integer.
+ intbe -- a signed big-endian whole byte integer.
+ floatbe - a big-endian floating point number.
+ uintle -- an unsigned little-endian whole byte integer.
+ intle -- a signed little-endian whole byte integer.
+ floatle -- a little-endian floating point number.
+ uintne -- an unsigned native-endian whole byte integer.
+ intne -- a signed native-endian whole byte integer.
+ floatne -- a native-endian floating point number.
+ se -- a signed exponential-Golomb code.
+ ue -- an unsigned exponential-Golomb code.
+ sie -- a signed interleaved exponential-Golomb code.
+ uie -- an unsigned interleaved exponential-Golomb code.
+ bool -- a boolean (True or False).
+ filename -- a file which will be opened in binary read-only mode.
+
+ Other keyword arguments:
+ length -- length of the bitstring in bits, if needed and appropriate.
+ It must be supplied for all integer and float initialisers.
+ offset -- bit offset to the data. These offset bits are
+ ignored and this is intended for use when
+ initialising using 'bytes' or 'filename'.
+
+ """
+ self._pos = 0
+
+ def __new__(cls, auto=None, length=None, offset=None, **kwargs):
+ x = super(ConstBitStream, cls).__new__(cls)
+ x._initialise(auto, length, offset, **kwargs)
+ return x
+
+ def _setbytepos(self, bytepos):
+ """Move to absolute byte-aligned position in stream."""
+ self._setbitpos(bytepos * 8)
+
+ def _getbytepos(self):
+ """Return the current position in the stream in bytes. Must be byte aligned."""
+ if self._pos % 8:
+ raise ByteAlignError("Not byte aligned in _getbytepos().")
+ return self._pos // 8
+
+ def _setbitpos(self, pos):
+ """Move to absolute postion bit in bitstream."""
+ if pos < 0:
+ raise ValueError("Bit position cannot be negative.")
+ if pos > self.len:
+ raise ValueError("Cannot seek past the end of the data.")
+ self._pos = pos
+
+ def _getbitpos(self):
+ """Return the current position in the stream in bits."""
+ return self._pos
+
+ def _clear(self):
+ Bits._clear(self)
+ self._pos = 0
+
+ def __copy__(self):
+ """Return a new copy of the ConstBitStream for the copy module."""
+ # Note that if you want a new copy (different ID), use _copy instead.
+ # The copy can use the same datastore as it's immutable.
+ s = ConstBitStream()
+ s._datastore = self._datastore
+ # Reset the bit position, don't copy it.
+ s._pos = 0
+ return s
+
+ def __add__(self, bs):
+ """Concatenate bitstrings and return new bitstring.
+
+ bs -- the bitstring to append.
+
+ """
+ s = Bits.__add__(self, bs)
+ s._pos = 0
+ return s
+
+ def read(self, fmt):
+ """Interpret next bits according to the format string and return result.
+
+ fmt -- Token string describing how to interpret the next bits.
+
+ Token examples: 'int:12' : 12 bits as a signed integer
+ 'uint:8' : 8 bits as an unsigned integer
+ 'float:64' : 8 bytes as a big-endian float
+ 'intbe:16' : 2 bytes as a big-endian signed integer
+ 'uintbe:16' : 2 bytes as a big-endian unsigned integer
+ 'intle:32' : 4 bytes as a little-endian signed integer
+ 'uintle:32' : 4 bytes as a little-endian unsigned integer
+ 'floatle:64': 8 bytes as a little-endian float
+ 'intne:24' : 3 bytes as a native-endian signed integer
+ 'uintne:24' : 3 bytes as a native-endian unsigned integer
+ 'floatne:32': 4 bytes as a native-endian float
+ 'hex:80' : 80 bits as a hex string
+ 'oct:9' : 9 bits as an octal string
+ 'bin:1' : single bit binary string
+ 'ue' : next bits as unsigned exp-Golomb code
+ 'se' : next bits as signed exp-Golomb code
+ 'uie' : next bits as unsigned interleaved exp-Golomb code
+ 'sie' : next bits as signed interleaved exp-Golomb code
+ 'bits:5' : 5 bits as a bitstring
+ 'bytes:10' : 10 bytes as a bytes object
+ 'bool' : 1 bit as a bool
+ 'pad:3' : 3 bits of padding to ignore - returns None
+
+ fmt may also be an integer, which will be treated like the 'bits' token.
+
+ The position in the bitstring is advanced to after the read items.
+
+ Raises ReadError if not enough bits are available.
+ Raises ValueError if the format is not understood.
+
+ """
+ if isinstance(fmt, numbers.Integral):
+ if fmt < 0:
+ raise ValueError("Cannot read negative amount.")
+ if fmt > self.len - self._pos:
+ raise ReadError("Cannot read {0} bits, only {1} available.",
+ fmt, self.len - self._pos)
+ bs = self._slice(self._pos, self._pos + fmt)
+ self._pos += fmt
+ return bs
+ p = self._pos
+ _, token = tokenparser(fmt)
+ if len(token) != 1:
+ self._pos = p
+ raise ValueError("Format string should be a single token, not {0} "
+ "tokens - use readlist() instead.".format(len(token)))
+ name, length, _ = token[0]
+ if length is None:
+ length = self.len - self._pos
+ value, self._pos = self._readtoken(name, self._pos, length)
+ return value
+
+ def readlist(self, fmt, **kwargs):
+ """Interpret next bits according to format string(s) and return list.
+
+ fmt -- A single string or list of strings with comma separated tokens
+ describing how to interpret the next bits in the bitstring. Items
+ can also be integers, for reading new bitstring of the given length.
+ kwargs -- A dictionary or keyword-value pairs - the keywords used in the
+ format string will be replaced with their given value.
+
+ The position in the bitstring is advanced to after the read items.
+
+ Raises ReadError is not enough bits are available.
+ Raises ValueError if the format is not understood.
+
+ See the docstring for 'read' for token examples. 'pad' tokens are skipped
+ and not added to the returned list.
+
+ >>> h, b1, b2 = s.readlist('hex:20, bin:5, bin:3')
+ >>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10])
+
+ """
+ value, self._pos = self._readlist(fmt, self._pos, **kwargs)
+ return value
+
+ def readto(self, bs, bytealigned=None):
+ """Read up to and including next occurrence of bs and return result.
+
+ bs -- The bitstring to find. An integer is not permitted.
+ bytealigned -- If True the bitstring will only be
+ found on byte boundaries.
+
+ Raises ValueError if bs is empty.
+ Raises ReadError if bs is not found.
+
+ """
+ if isinstance(bs, numbers.Integral):
+ raise ValueError("Integers cannot be searched for")
+ bs = Bits(bs)
+ oldpos = self._pos
+ p = self.find(bs, self._pos, bytealigned=bytealigned)
+ if not p:
+ raise ReadError("Substring not found")
+ self._pos += bs.len
+ return self._slice(oldpos, self._pos)
+
+ def peek(self, fmt):
+ """Interpret next bits according to format string and return result.
+
+ fmt -- Token string describing how to interpret the next bits.
+
+ The position in the bitstring is not changed. If not enough bits are
+ available then all bits to the end of the bitstring will be used.
+
+ Raises ReadError if not enough bits are available.
+ Raises ValueError if the format is not understood.
+
+ See the docstring for 'read' for token examples.
+
+ """
+ pos_before = self._pos
+ value = self.read(fmt)
+ self._pos = pos_before
+ return value
+
+ def peeklist(self, fmt, **kwargs):
+ """Interpret next bits according to format string(s) and return list.
+
+ fmt -- One or more strings with comma separated tokens describing
+ how to interpret the next bits in the bitstring.
+ kwargs -- A dictionary or keyword-value pairs - the keywords used in the
+ format string will be replaced with their given value.
+
+ The position in the bitstring is not changed. If not enough bits are
+ available then all bits to the end of the bitstring will be used.
+
+ Raises ReadError if not enough bits are available.
+ Raises ValueError if the format is not understood.
+
+ See the docstring for 'read' for token examples.
+
+ """
+ pos = self._pos
+ return_values = self.readlist(fmt, **kwargs)
+ self._pos = pos
+ return return_values
+
+ def bytealign(self):
+ """Align to next byte and return number of skipped bits.
+
+ Raises ValueError if the end of the bitstring is reached before
+ aligning to the next byte.
+
+ """
+ skipped = (8 - (self._pos % 8)) % 8
+ self.pos += self._offset + skipped
+ assert self._assertsanity()
+ return skipped
+
+ pos = property(_getbitpos, _setbitpos,
+ doc="""The position in the bitstring in bits. Read and write.
+ """)
+ bitpos = property(_getbitpos, _setbitpos,
+ doc="""The position in the bitstring in bits. Read and write.
+ """)
+ bytepos = property(_getbytepos, _setbytepos,
+ doc="""The position in the bitstring in bytes. Read and write.
+ """)
+
+
+
+
+
+class BitStream(ConstBitStream, BitArray):
+ """A container or stream holding a mutable sequence of bits
+
+ Subclass of the ConstBitStream and BitArray classes. Inherits all of
+ their methods.
+
+ Methods:
+
+ all() -- Check if all specified bits are set to 1 or 0.
+ any() -- Check if any of specified bits are set to 1 or 0.
+ append() -- Append a bitstring.
+ bytealign() -- Align to next byte boundary.
+ byteswap() -- Change byte endianness in-place.
+ count() -- Count the number of bits set to 1 or 0.
+ cut() -- Create generator of constant sized chunks.
+ endswith() -- Return whether the bitstring ends with a sub-string.
+ find() -- Find a sub-bitstring in the current bitstring.
+ findall() -- Find all occurrences of a sub-bitstring in the current bitstring.
+ insert() -- Insert a bitstring.
+ invert() -- Flip bit(s) between one and zero.
+ join() -- Join bitstrings together using current bitstring.
+ overwrite() -- Overwrite a section with a new bitstring.
+ peek() -- Peek at and interpret next bits as a single item.
+ peeklist() -- Peek at and interpret next bits as a list of items.
+ prepend() -- Prepend a bitstring.
+ read() -- Read and interpret next bits as a single item.
+ readlist() -- Read and interpret next bits as a list of items.
+ replace() -- Replace occurrences of one bitstring with another.
+ reverse() -- Reverse bits in-place.
+ rfind() -- Seek backwards to find a sub-bitstring.
+ rol() -- Rotate bits to the left.
+ ror() -- Rotate bits to the right.
+ set() -- Set bit(s) to 1 or 0.
+ split() -- Create generator of chunks split by a delimiter.
+ startswith() -- Return whether the bitstring starts with a sub-bitstring.
+ tobytes() -- Return bitstring as bytes, padding if needed.
+ tofile() -- Write bitstring to file, padding if needed.
+ unpack() -- Interpret bits using format string.
+
+ Special methods:
+
+ Mutating operators are available: [], <<=, >>=, +=, *=, &=, |= and ^=
+ in addition to [], ==, !=, +, *, ~, <<, >>, &, | and ^.
+
+ Properties:
+
+ bin -- The bitstring as a binary string.
+ bool -- For single bit bitstrings, interpret as True or False.
+ bytepos -- The current byte position in the bitstring.
+ bytes -- The bitstring as a bytes object.
+ float -- Interpret as a floating point number.
+ floatbe -- Interpret as a big-endian floating point number.
+ floatle -- Interpret as a little-endian floating point number.
+ floatne -- Interpret as a native-endian floating point number.
+ hex -- The bitstring as a hexadecimal string.
+ int -- Interpret as a two's complement signed integer.
+ intbe -- Interpret as a big-endian signed integer.
+ intle -- Interpret as a little-endian signed integer.
+ intne -- Interpret as a native-endian signed integer.
+ len -- Length of the bitstring in bits.
+ oct -- The bitstring as an octal string.
+ pos -- The current bit position in the bitstring.
+ se -- Interpret as a signed exponential-Golomb code.
+ ue -- Interpret as an unsigned exponential-Golomb code.
+ sie -- Interpret as a signed interleaved exponential-Golomb code.
+ uie -- Interpret as an unsigned interleaved exponential-Golomb code.
+ uint -- Interpret as a two's complement unsigned integer.
+ uintbe -- Interpret as a big-endian unsigned integer.
+ uintle -- Interpret as a little-endian unsigned integer.
+ uintne -- Interpret as a native-endian unsigned integer.
+
+ """
+
+ __slots__ = ()
+
+ # As BitStream objects are mutable, we shouldn't allow them to be hashed.
+ __hash__ = None
+
+ def __init__(self, auto=None, length=None, offset=None, **kwargs):
+ """Either specify an 'auto' initialiser:
+ auto -- a string of comma separated tokens, an integer, a file object,
+ a bytearray, a boolean iterable or another bitstring.
+
+ Or initialise via **kwargs with one (and only one) of:
+ bytes -- raw data as a string, for example read from a binary file.
+ bin -- binary string representation, e.g. '0b001010'.
+ hex -- hexadecimal string representation, e.g. '0x2ef'
+ oct -- octal string representation, e.g. '0o777'.
+ uint -- an unsigned integer.
+ int -- a signed integer.
+ float -- a floating point number.
+ uintbe -- an unsigned big-endian whole byte integer.
+ intbe -- a signed big-endian whole byte integer.
+ floatbe - a big-endian floating point number.
+ uintle -- an unsigned little-endian whole byte integer.
+ intle -- a signed little-endian whole byte integer.
+ floatle -- a little-endian floating point number.
+ uintne -- an unsigned native-endian whole byte integer.
+ intne -- a signed native-endian whole byte integer.
+ floatne -- a native-endian floating point number.
+ se -- a signed exponential-Golomb code.
+ ue -- an unsigned exponential-Golomb code.
+ sie -- a signed interleaved exponential-Golomb code.
+ uie -- an unsigned interleaved exponential-Golomb code.
+ bool -- a boolean (True or False).
+ filename -- a file which will be opened in binary read-only mode.
+
+ Other keyword arguments:
+ length -- length of the bitstring in bits, if needed and appropriate.
+ It must be supplied for all integer and float initialisers.
+ offset -- bit offset to the data. These offset bits are
+ ignored and this is intended for use when
+ initialising using 'bytes' or 'filename'.
+
+ """
+ self._pos = 0
+ # For mutable BitStreams we always read in files to memory:
+ if not isinstance(self._datastore, ByteStore):
+ self._ensureinmemory()
+
+ def __new__(cls, auto=None, length=None, offset=None, **kwargs):
+ x = super(BitStream, cls).__new__(cls)
+ x._initialise(auto, length, offset, **kwargs)
+ return x
+
+ def __copy__(self):
+ """Return a new copy of the BitStream."""
+ s_copy = BitStream()
+ s_copy._pos = 0
+ if not isinstance(self._datastore, ByteStore):
+ # Let them both point to the same (invariant) array.
+ # If either gets modified then at that point they'll be read into memory.
+ s_copy._datastore = self._datastore
+ else:
+ s_copy._datastore = ByteStore(self._datastore._rawarray[:],
+ self._datastore.bitlength,
+ self._datastore.offset)
+ return s_copy
+
+ def prepend(self, bs):
+ """Prepend a bitstring to the current bitstring.
+
+ bs -- The bitstring to prepend.
+
+ """
+ bs = self._converttobitstring(bs)
+ self._prepend(bs)
+ self._pos += bs.len
+
+
+def pack(fmt, *values, **kwargs):
+ """Pack the values according to the format string and return a new BitStream.
+
+ fmt -- A single string or a list of strings with comma separated tokens
+ describing how to create the BitStream.
+ values -- Zero or more values to pack according to the format.
+ kwargs -- A dictionary or keyword-value pairs - the keywords used in the
+ format string will be replaced with their given value.
+
+ Token examples: 'int:12' : 12 bits as a signed integer
+ 'uint:8' : 8 bits as an unsigned integer
+ 'float:64' : 8 bytes as a big-endian float
+ 'intbe:16' : 2 bytes as a big-endian signed integer
+ 'uintbe:16' : 2 bytes as a big-endian unsigned integer
+ 'intle:32' : 4 bytes as a little-endian signed integer
+ 'uintle:32' : 4 bytes as a little-endian unsigned integer
+ 'floatle:64': 8 bytes as a little-endian float
+ 'intne:24' : 3 bytes as a native-endian signed integer
+ 'uintne:24' : 3 bytes as a native-endian unsigned integer
+ 'floatne:32': 4 bytes as a native-endian float
+ 'hex:80' : 80 bits as a hex string
+ 'oct:9' : 9 bits as an octal string
+ 'bin:1' : single bit binary string
+ 'ue' / 'uie': next bits as unsigned exp-Golomb code
+ 'se' / 'sie': next bits as signed exp-Golomb code
+ 'bits:5' : 5 bits as a bitstring object
+ 'bytes:10' : 10 bytes as a bytes object
+ 'bool' : 1 bit as a bool
+ 'pad:3' : 3 zero bits as padding
+
+ >>> s = pack('uint:12, bits', 100, '0xffe')
+ >>> t = pack(['bits', 'bin:3'], s, '111')
+ >>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44)
+
+ """
+ tokens = []
+ if isinstance(fmt, basestring):
+ fmt = [fmt]
+ try:
+ for f_item in fmt:
+ _, tkns = tokenparser(f_item, tuple(sorted(kwargs.keys())))
+ tokens.extend(tkns)
+ except ValueError as e:
+ raise CreationError(*e.args)
+ value_iter = iter(values)
+ s = BitStream()
+ try:
+ for name, length, value in tokens:
+ # If the value is in the kwd dictionary then it takes precedence.
+ if value in kwargs:
+ value = kwargs[value]
+ # If the length is in the kwd dictionary then use that too.
+ if length in kwargs:
+ length = kwargs[length]
+ # Also if we just have a dictionary name then we want to use it
+ if name in kwargs and length is None and value is None:
+ s.append(kwargs[name])
+ continue
+ if length is not None:
+ length = int(length)
+ if value is None and name != 'pad':
+ # Take the next value from the ones provided
+ value = next(value_iter)
+ s._append(BitStream._init_with_token(name, length, value))
+ except StopIteration:
+ raise CreationError("Not enough parameters present to pack according to the "
+ "format. {0} values are needed.", len(tokens))
+ try:
+ next(value_iter)
+ except StopIteration:
+ # Good, we've used up all the *values.
+ return s
+ raise CreationError("Too many parameters present to pack according to the format.")
+
+
+# Aliases for backward compatibility
+ConstBitArray = Bits
+BitString = BitStream
+
+__all__ = ['ConstBitArray', 'ConstBitStream', 'BitStream', 'BitArray',
+ 'Bits', 'BitString', 'pack', 'Error', 'ReadError',
+ 'InterpretError', 'ByteAlignError', 'CreationError', 'bytealigned']
diff --git a/python/bitstring/doc/bitstring_manual.pdf b/python/bitstring/doc/bitstring_manual.pdf
new file mode 100644
index 0000000000..dc17385b7c
--- /dev/null
+++ b/python/bitstring/doc/bitstring_manual.pdf
Binary files differ
diff --git a/python/bitstring/release_notes.txt b/python/bitstring/release_notes.txt
new file mode 100644
index 0000000000..8cad4ca9d1
--- /dev/null
+++ b/python/bitstring/release_notes.txt
@@ -0,0 +1,1523 @@
+--------------------------------
+bitstring module version history
+--------------------------------
+
+---------------------------------------
+March 4th 2014: version 3.1.3 released
+---------------------------------------
+This is another bug fix release.
+
+* Fix for problem with prepend for bitstrings with byte offsets in their data store.
+
+---------------------------------------
+April 18th 2013: version 3.1.2 released
+---------------------------------------
+This is another bug fix release.
+
+* Fix for problem where unpacking bytes would by eight times too long
+
+---------------------------------------
+March 21st 2013: version 3.1.1 released
+---------------------------------------
+This is a bug fix release.
+
+* Fix for problem where concatenating bitstrings sometimes modified method's arguments
+
+------------------------------------------
+February 26th 2013: version 3.1.0 released
+------------------------------------------
+This is a minor release with a couple of new features and some bug fixes.
+
+New 'pad' token
+---------------
+
+This token can be used in reads and when packing/unpacking to indicate that
+you don't care about the contents of these bits. Any padding bits will just
+be skipped over when reading/unpacking or zero-filled when packing.
+
+ >>> a, b = s.readlist('pad:5, uint:3, pad:1, uint:3')
+
+Here only two items are returned in the list - the padding bits are ignored.
+
+New clear and copy convenience methods
+--------------------------------------
+
+These methods have been introduced in Python 3.3 for lists and bytearrays,
+as more obvious ways of clearing and copying, and we mirror that change here.
+
+t = s.copy() is equivalent to t = s[:], and s.clear() is equivalent to del s[:].
+
+Other changes
+-------------
+
+* Some bug fixes.
+
+-----------------------------------------
+February 7th 2012: version 3.0.2 released
+-----------------------------------------
+This is a minor update that fixes a few bugs.
+
+* Fix for subclasses of bitstring classes behaving strangely (Issue 121).
+* Fix for excessive memory usage in rare cases (Issue 120).
+* Fixes for slicing edge cases.
+
+There has also been a reorganisation of the code to return it to a single
+'bitstring.py' file rather than the package that has been used for the past
+several releases. This change shouldn't affect users directly.
+
+------------------------------------------
+November 21st 2011: version 3.0.1 released
+------------------------------------------
+This release fixed a small but very visible bug in bitstring printing.
+
+------------------------------------------
+November 21st 2011: version 3.0.0 released
+------------------------------------------
+This is a major release which breaks backward compatibility in a few places.
+
+Backwardly incompatible changes
+===============================
+
+Hex, oct and bin properties don't have leading 0x, 0o and 0b
+------------------------------------------------------------
+
+If you ask for the hex, octal or binary representations of a bitstring then
+they will no longer be prefixed with '0x', 0o' or '0b'. This was done as it
+was noticed that the first thing a lot of user code does after getting these
+representations was to cut off the first two characters before further
+processing.
+
+ >>> a = BitArray('0x123')
+ >>> a.hex, a.oct, a.bin
+ ('123', '0443', '000100100011')
+
+Previously this would have returned ('0x123', '0o0443', '0b000100100011')
+
+This change might require some recoding, but it should all be simplifications.
+
+ConstBitArray renamed to Bits
+-----------------------------
+
+Previously Bits was an alias for ConstBitStream (for backward compatibility).
+This has now changed so that Bits and BitArray loosely correspond to the
+built-in types bytes and bytearray.
+
+If you were using streaming/reading methods on a Bits object then you will
+have to change it to a ConstBitStream.
+
+The ConstBitArray name is kept as an alias for Bits.
+
+Stepping in slices has conventional meaning
+-------------------------------------------
+
+The step parameter in __getitem__, __setitem__ and __delitem__ used to act
+as a multiplier for the start and stop parameters. No one seemed to use it
+though and so it has now reverted to the convential meaning for containers.
+
+If you are using step then recoding is simple: s[a:b:c] becomes s[a*c:b*c].
+
+Some examples of the new usage:
+
+ >>> s = BitArray('0x0000')
+ s[::4] = [1, 1, 1, 1]
+ >>> s.hex
+ '8888'
+ >>> del s[8::2]
+ >>> s.hex
+ '880'
+
+
+New features
+============
+
+New readto method
+-----------------
+
+This method is a mix between a find and a read - it searches for a bitstring
+and then reads up to and including it. For example:
+
+ >>> s = ConstBitStream('0x47000102034704050647')
+ >>> s.readto('0x47', bytealigned=True)
+ BitStream('0x47')
+ >>> s.readto('0x47', bytealigned=True)
+ BitStream('0x0001020347')
+ >>> s.readto('0x47', bytealigned=True)
+ BitStream('0x04050647')
+
+pack function accepts an iterable as its format
+-----------------------------------------------
+
+Previously only a string was accepted as the format in the pack function.
+This was an oversight as it broke the symmetry between pack and unpack.
+Now you can use formats like this:
+
+ fmt = ['hex:8', 'bin:3']
+ a = pack(fmt, '47', '001')
+ a.unpack(fmt)
+
+
+--------------------------------------
+June 18th 2011: version 2.2.0 released
+--------------------------------------
+This is a minor upgrade with a couple of new features.
+
+New interleaved exponential-Golomb interpretations
+--------------------------------------------------
+
+New bit interpretations for interleaved exponential-Golomb (as used in the
+Dirac video codec) are supplied via 'uie' and 'sie':
+
+ >>> s = BitArray(uie=41)
+ >>> s.uie
+ 41
+ >>> s.bin
+ '0b00010001001'
+
+These are pretty similar to the non-interleaved versions - see the manual
+for more details. Credit goes to Paul Sargent for the patch.
+
+New package-level bytealigned variable
+--------------------------------------
+
+A number of methods take a 'bytealigned' parameter to indicate that they
+should only work on byte boundaries (e.g. find, replace, split). Previously
+this parameter defaulted to 'False'. Instead it now defaults to
+'bitstring.bytealigned', which itself defaults to 'False', but can be changed
+to modify the default behaviour of the methods. For example:
+
+ >>> a = BitArray('0x00 ff 0f ff')
+ >>> a.find('0x0f')
+ (4,) # found first not on a byte boundary
+ >>> a.find('0x0f', bytealigned=True)
+ (16,) # forced looking only on byte boundaries
+ >>> bitstring.bytealigned = True # Change default behaviour
+ >>> a.find('0x0f')
+ (16,)
+ >>> a.find('0x0f', bytealigned=False)
+ (4,)
+
+If you're only working with bytes then this can help avoid some errors and
+save some typing!
+
+Other changes
+-------------
+
+* Fix for Python 3.2, correcting for a change to the binascii module.
+* Fix for bool initialisation from 0 or 1.
+* Efficiency improvements, including interning strategy.
+
+------------------------------------------
+February 23rd 2011: version 2.1.1 released
+------------------------------------------
+This is a release to fix a couple of bugs that were introduced in 2.1.0.
+
+* Bug fix: Reading using the 'bytes' token had been broken (Issue 102).
+* Fixed problem using some methods on ConstBitArrays.
+* Better exception handling for tokens missing values.
+* Some performance improvements.
+
+-----------------------------------------
+January 23rd 2011: version 2.1.0 released
+-----------------------------------------
+
+New class hierarchy introduced with simpler classes
+---------------------------------------------------
+Previously there were just two classes, the immutable Bits which was the base
+class for the mutable BitString class. Both of these classes have the concept
+of a bit position, from which reads etc. take place so that the bitstring could
+be treated as if it were a file or stream.
+
+Two simpler classes have now been added which are purely bit containers and
+don't have a bit position. These are called ConstBitArray and BitArray. As you
+can guess the former is an immutable version of the latter.
+
+The other classes have also been renamed to better reflect their capabilities.
+Instead of BitString you can use BitStream, and instead of Bits you can use
+ConstBitStream. The old names are kept as aliases for backward compatibility.
+
+The classes hierarchy is:
+
+ ConstBitArray
+ / \
+ / \
+ BitArray ConstBitStream (formerly Bits)
+ \ /
+ \ /
+ BitStream (formerly BitString)
+
+
+Other changes
+-------------
+A lot of internal reorganisation has taken place since the previous version,
+most of which won't be noticed by the end user. Some things you might see are:
+
+* New package structure. Previous versions have been a single file for the
+ module and another for the unit tests. The module is now split into many
+ more files so it can't be used just by copying bitstring.py any more.
+* To run the unit tests there is now a script called runtests.py in the test
+ directory.
+* File based bitstring are now implemented in terms of an mmap. This should
+ be just an implementation detail, but unfortunately for 32-bit versions of
+ Python this creates a limit of 4GB on the files that can be used. The work
+ around is either to get a 64-bit Python, or just stick with version 2.0.
+* The ConstBitArray and ConstBitStream classes no longer copy byte data when
+ a slice or a read takes place, they just take a reference. This is mostly
+ a very nice optimisation, but there are occassions where it could have an
+ adverse effect. For example if a very large bitstring is created, a small
+ slice taken and the original deleted. The byte data from the large
+ bitstring would still be retained in memory.
+* Optimisations. Once again this version should be faster than the last.
+ The module is still pure Python but some of the reorganisation was to make
+ it more feasible to put some of the code into Cython or similar, so
+ hopefully more speed will be on the way.
+
+--------------------------------------
+July 26th 2010: version 2.0.3 released
+--------------------------------------
+* Bug fix: Using peek and read for a single bit now returns a new bitstring
+ as was intended, rather than the old behaviour of returning a bool.
+* Removed HTML docs from source archive - better to use the online version.
+
+--------------------------------------
+July 25th 2010: version 2.0.2 released
+--------------------------------------
+This is a major release, with a number of backwardly incompatible changes.
+The main change is the removal of many methods, all of which have simple
+alternatives. Other changes are quite minor but may need some recoding.
+
+There are a few new features, most of which have been made to help the
+stream-lining of the API. As always there are performance improvements and
+some API changes were made purely with future performance in mind.
+
+The backwardly incompatible changes are:
+-----------------------------------------
+* Methods removed.
+
+About half of the class methods have been removed from the API. They all have
+simple alternatives, so what remains is more powerful and easier to remember.
+The removed methods are listed here on the left, with their equivalent
+replacements on the right:
+
+s.advancebit() -> s.pos += 1
+s.advancebits(bits) -> s.pos += bits
+s.advancebyte() -> s.pos += 8
+s.advancebytes(bytes) -> s.pos += 8*bytes
+s.allunset([a, b]) -> s.all(False, [a, b])
+s.anyunset([a, b]) -> s.any(False, [a, b])
+s.delete(bits, pos) -> del s[pos:pos+bits]
+s.peekbit() -> s.peek(1)
+s.peekbitlist(a, b) -> s.peeklist([a, b])
+s.peekbits(bits) -> s.peek(bits)
+s.peekbyte() -> s.peek(8)
+s.peekbytelist(a, b) -> s.peeklist([8*a, 8*b])
+s.peekbytes(bytes) -> s.peek(8*bytes)
+s.readbit() -> s.read(1)
+s.readbitlist(a, b) -> s.readlist([a, b])
+s.readbits(bits) -> s.read(bits)
+s.readbyte() -> s.read(8)
+s.readbytelist(a, b) -> s.readlist([8*a, 8*b])
+s.readbytes(bytes) -> s.read(8*bytes)
+s.retreatbit() -> s.pos -= 1
+s.retreatbits(bits) -> s.pos -= bits
+s.retreatbyte() -> s.pos -= 8
+s.retreatbytes(bytes) -> s.pos -= 8*bytes
+s.reversebytes(start, end) -> s.byteswap(0, start, end)
+s.seek(pos) -> s.pos = pos
+s.seekbyte(bytepos) -> s.bytepos = bytepos
+s.slice(start, end, step) -> s[start:end:step]
+s.tell() -> s.pos
+s.tellbyte() -> s.bytepos
+s.truncateend(bits) -> del s[-bits:]
+s.truncatestart(bits) -> del s[:bits]
+s.unset([a, b]) -> s.set(False, [a, b])
+
+Many of these methods have been deprecated for the last few releases, but
+there are some new removals too. Any recoding needed should be quite
+straightforward, so while I apologise for the hassle, I had to take the
+opportunity to streamline and rationalise what was becoming a bit of an
+overblown API.
+
+* set / unset methods combined.
+
+The set/unset methods have been combined in a single method, which now
+takes a boolean as its first argument:
+
+s.set([a, b]) -> s.set(1, [a, b])
+s.unset([a, b]) -> s.set(0, [a, b])
+s.allset([a, b]) -> s.all(1, [a, b])
+s.allunset([a, b]) -> s.all(0, [a, b])
+s.anyset([a, b]) -> s.any(1, [a, b])
+s.anyunset([a, b]) -> s.any(0, [a, b])
+
+* all / any only accept iterables.
+
+The all and any methods (previously called allset, allunset, anyset and
+anyunset) no longer accept a single bit position. The recommended way of
+testing a single bit is just to index it, for example instead of:
+
+>>> if s.all(True, i):
+
+just use
+
+>>> if s[i]:
+
+If you really want to you can of course use an iterable with a single
+element, such as 's.any(False, [i])', but it's clearer just to write
+'not s[i]'.
+
+* Exception raised on reading off end of bitstring.
+
+If a read or peek goes beyond the end of the bitstring then a ReadError
+will be raised. The previous behaviour was that the rest of the bitstring
+would be returned and no exception raised.
+
+* BitStringError renamed to Error.
+
+The base class for errors in the bitstring module is now just Error, so
+it will likely appears in your code as bitstring.Error instead of
+the rather repetitive bitstring.BitStringError.
+
+* Single bit slices and reads return a bool.
+
+A single index slice (such as s[5]) will now return a bool (i.e. True or
+False) rather than a single bit bitstring. This is partly to reflect the
+style of the bytearray type, which returns an integer for single items, but
+mostly to avoid common errors like:
+
+>>> if s[0]:
+... do_something()
+
+While the intent of this code snippet is quite clear (i.e. do_something if
+the first bit of s is set) under the old rules s[0] would be true as long
+as s wasn't empty. That's because any one-bit bitstring was true as it was a
+non-empty container. Under the new rule s[0] is True if s starts with a '1'
+bit and False if s starts with a '0' bit.
+
+The change does not affect reads and peeks, so s.peek(1) will still return
+a single bit bitstring, which leads on to the next item...
+
+* Empty bitstrings or bitstrings with only zero bits are considered False.
+
+Previously a bitstring was False if it had no elements, otherwise it was True.
+This is standard behaviour for containers, but wasn't very useful for a container
+of just 0s and 1s. The new behaviour means that the bitstring is False if it
+has no 1 bits. This means that code like this:
+
+>>> if s.peek(1):
+... do_something()
+
+should work as you'd expect. It also means that Bits(1000), Bits(0x00) and
+Bits('uint:12=0') are all also False. If you need to check for the emptiness of
+a bitstring then instead check the len property:
+
+if s -> if s.len
+if not s -> if not s.len
+
+* Length and offset disallowed for some initialisers.
+
+Previously you could create bitstring using expressions like:
+
+>>> s = Bits(hex='0xabcde', offset=4, length=13)
+
+This has now been disallowed, and the offset and length parameters may only
+be used when initialising with bytes or a file. To replace the old behaviour
+you could instead use
+
+>>> s = Bits(hex='0xabcde')[4:17]
+
+* Renamed 'format' parameter 'fmt'.
+
+Methods with a 'format' parameter have had it renamed to 'fmt', to prevent
+hiding the built-in 'format'. Affects methods unpack, read, peek, readlist,
+peeklist and byteswap and the pack function.
+
+* Iterables instead of *format accepted for some methods.
+
+This means that for the affected methods (unpack, readlist and peeklist) you
+will need to use an iterable to specify multiple items. This is easier to
+show than to describe, so instead of
+
+>>> a, b, c, d = s.readlist('uint:12', 'hex:4', 'bin:7')
+
+you would instead write
+
+>>> a, b, c, d = s.readlist(['uint:12', 'hex:4', 'bin:7'])
+
+Note that you could still use the single string 'uint:12, hex:4, bin:7' if
+you preferred.
+
+* Bool auto-initialisation removed.
+
+You can no longer use True and False to initialise single bit bitstrings.
+The reasoning behind this is that as bool is a subclass of int, it really is
+bad practice to have Bits(False) be different to Bits(0) and to have Bits(True)
+different to Bits(1).
+
+If you have used bool auto-initialisation then you will have to be careful to
+replace it as the bools will now be interpreted as ints, so Bits(False) will
+be empty (a bitstring of length 0), and Bits(True) will be a single zero bit
+(a bitstring of length 1). Sorry for the confusion, but I think this will
+prevent bigger problems in the future.
+
+There are a few alternatives for creating a single bit bitstring. My favourite
+it to use a list with a single item:
+
+Bits(False) -> Bits([0])
+Bits(True) -> Bits([1])
+
+* New creation from file strategy
+
+Previously if you created a bitstring from a file, either by auto-initialising
+with a file object or using the filename parameter, the file would not be read
+into memory unless you tried to modify it, at which point the whole file would
+be read.
+
+The new behaviour depends on whether you create a Bits or a BitString from the
+file. If you create a Bits (which is immutable) then the file will never be
+read into memory. This allows very large files to be opened for examination
+even if they could never fit in memory.
+
+If however you create a BitString, the whole of the referenced file will be read
+to store in memory. If the file is very big this could take a long time, or fail,
+but the idea is that in saying you want the mutable BitString you are implicitly
+saying that you want to make changes and so (for now) we need to load it into
+memory.
+
+The new strategy is a bit more predictable in terms of performance than the old.
+The main point to remember is that if you want to open a file and don't plan to
+alter the bitstring then use the Bits class rather than BitString.
+
+Just to be clear, in neither case will the contents of the file ever be changed -
+if you want to output the modified BitString then use the tofile method, for
+example.
+
+* find and rfind return a tuple instead of a bool.
+
+If a find is unsuccessful then an empty tuple is returned (which is False in a
+boolean sense) otherwise a single item tuple with the bit position is returned
+(which is True in a boolean sense). You shouldn't need to recode unless you
+explicitly compared the result of a find to True or False, for example this
+snippet doesn't need to be altered:
+
+>>> if s.find('0x23'):
+... print(s.bitpos)
+
+but you could now instead use
+
+>>> found = s.find('0x23')
+>>> if found:
+... print(found[0])
+
+The reason for returning the bit position in a tuple is so that finding at
+position zero can still be True - it's the tuple (0,) - whereas not found can
+be False - the empty tuple ().
+
+The new features in this release are:
+-------------------------------------
+* New count method.
+
+This method just counts the number of 1 or 0 bits in the bitstring.
+
+>>> s = Bits('0x31fff4')
+>>> s.count(1)
+16
+
+* read and peek methods accept integers.
+
+The read, readlist, peek and peeklist methods now accept integers as parameters
+to mean "read this many bits and return a bitstring". This has allowed a number
+of methods to be removed from this release, so for example instead of:
+
+>>> a, b, c = s.readbits(5, 6, 7)
+>>> if s.peekbit():
+... do_something()
+
+you should write:
+
+>>> a, b, c = s.readlist([5, 6, 7])
+>>> if s.peek(1):
+... do_something()
+
+* byteswap used to reverse all bytes.
+
+The byteswap method now allows a format specifier of 0 (the default) to signify
+that all of the whole bytes should be reversed. This means that calling just
+byteswap() is almost equivalent to the now removed bytereverse() method (a small
+difference is that byteswap won't raise an exception if the bitstring isn't a
+whole number of bytes long).
+
+* Auto initialise with bytearray or (for Python 3 only) bytes.
+
+So rather than writing:
+
+>>> a = Bits(bytes=some_bytearray)
+
+you can just write
+
+>>> a = Bits(some_bytearray)
+
+This also works for the bytes type, but only if you're using Python 3.
+For Python 2 it's not possible to distinguish between a bytes object and a
+str. For this reason this method should be used with some caution as it will
+make you code behave differently with the different major Python versions.
+
+>>> b = Bits(b'abcd\x23\x00') # Only Python 3!
+
+* set, invert, all and any default to whole bitstring.
+
+This means that you can for example write:
+
+>>> a = BitString(100) # 100 zero bits
+>>> a.set(1) # set all bits to 1
+>>> a.all(1) # are all bits set to 1?
+True
+>>> a.any(0) # are any set to 0?
+False
+>>> a.invert() # invert every bit
+
+* New exception types.
+
+As well as renaming BitStringError to just Error
+there are also new exceptions which use Error as a base class.
+
+These can be caught in preference to Error if you need finer control.
+The new exceptions sometimes also derive from built-in exceptions:
+
+ByteAlignError(Error) - whole byte position or length needed.
+
+ReadError(Error, IndexError) - reading or peeking off the end of
+the bitstring.
+
+CreationError(Error, ValueError) - inappropriate argument during
+bitstring creation.
+
+InterpretError(Error, ValueError) - inappropriate interpretation of
+binary data.
+
+
+--------------------------------------------------------------
+March 18th 2010: version 1.3.0 for Python 2.6 and 3.x released
+--------------------------------------------------------------
+New features:
+
+* byteswap method for changing endianness.
+
+Changes the endianness in-place according to a format string or
+integer(s) giving the byte pattern. See the manual for details.
+
+>>> s = BitString('0x00112233445566')
+>>> s.byteswap(2)
+3
+>>> s
+BitString('0x11003322554466')
+>>> s.byteswap('h')
+3
+>>> s
+BitString('0x00112233445566')
+>>> s.byteswap([2, 5])
+1
+>>> s
+BitString('0x11006655443322')
+
+* Multiplicative factors in bitstring creation and reading.
+
+For example:
+
+>>> s = Bits('100*0x123')
+
+* Token grouping using parenthesis.
+
+For example:
+
+>>> s = Bits('3*(uint:6=3, 0b1)')
+
+* Negative slice indices allowed.
+
+The start and end parameters of many methods may now be negative, with the
+same meaning as for negative slice indices. Affects all methods with these
+parameters.
+
+* Sequence ABCs used.
+
+The Bits class now derives from collections.Sequence, while the BitString
+class derives from collections.MutableSequence.
+
+* Keywords allowed in readlist, peeklist and unpack.
+
+Keywords for token lengths are now permitted when reading. So for example,
+you can write
+
+>>> s = bitstring.pack('4*(uint:n)', 2, 3, 4, 5, n=7)
+>>> s.unpack('4*(uint:n)', n=7)
+[2, 3, 4, 5]
+
+* start and end parameters added to rol and ror.
+
+* join function accepts other iterables.
+
+Also its parameter has changed from 'bitstringlist' to 'sequence'. This is
+technically a backward incompatibility in the unlikely event that you are
+referring to the parameter by name.
+
+* __init__ method accepts keywords.
+
+Rather than a long list of initialisers the __init__ methods now use a
+**kwargs dictionary for all initialisers except 'auto'. This should have no
+effect, except that this is a small backward incompatibility if you use
+positional arguments when initialising with anything other than auto
+(which would be rather unusual).
+
+* More optimisations.
+
+* Bug fixed in replace method (it could fail if start != 0).
+
+----------------------------------------------------------------
+January 19th 2010: version 1.2.0 for Python 2.6 and 3.x released
+----------------------------------------------------------------
+
+* New 'Bits' class.
+
+Introducing a brand new class, Bits, representing an immutable sequence of
+bits.
+
+The Bits class is the base class for the mutable BitString. The differences
+between Bits and BitStrings are:
+
+1) Bits are immutable, so once they have been created their value cannot change.
+This of course means that mutating methods (append, replace, del etc.) are not
+available for Bits.
+
+2) Bits are hashable, so they can be used in sets and as keys in dictionaries.
+
+3) Bits are potentially more efficient than BitStrings, both in terms of
+computation and memory. The current implementation is only marginally
+more efficient though - this should improve in future versions.
+
+You can switch from Bits to a BitString or vice versa by constructing a new
+object from the old.
+
+>>> s = Bits('0xabcd')
+>>> t = BitString(s)
+>>> t.append('0xe')
+>>> u = Bits(t)
+
+The relationship between Bits and BitString is supposed to loosely mirror that
+between bytes and bytearray in Python 3.
+
+* Deprecation messages turned on.
+
+A number of methods have been flagged for removal in version 2. Deprecation
+warnings will now be given, which include an alternative way to do the same
+thing. All of the deprecated methods have simpler equivalent alternatives.
+
+>>> t = s.slice(0, 2)
+__main__:1: DeprecationWarning: Call to deprecated function slice.
+Instead of 's.slice(a, b, c)' use 's[a:b:c]'.
+
+The deprecated methods are: advancebit, advancebits, advancebyte, advancebytes,
+retreatbit, retreatbits, retreatbyte, retreatbytes, tell, seek, slice, delete,
+tellbyte, seekbyte, truncatestart and truncateend.
+
+* Initialise from bool.
+
+Booleans have been added to the list of types that can 'auto'
+initialise a bitstring.
+
+>>> zerobit = BitString(False)
+>>> onebit = BitString(True)
+
+* Improved efficiency.
+
+More methods have been speeded up, in particular some deletions and insertions.
+
+* Bug fixes.
+
+A rare problem with truncating the start of bitstrings was fixed.
+
+A possible problem outputting the final byte in tofile() was fixed.
+
+-----------------------------------------------------------------
+December 22nd 2009: version 1.1.3 for Python 2.6 and 3.x released
+-----------------------------------------------------------------
+
+This version hopefully fixes an installation problem for platforms with
+case-sensitive file systems. There are no new features or other bug fixes.
+
+-----------------------------------------------------------------
+December 18th 2009: version 1.1.2 for Python 2.6 and 3.x released
+-----------------------------------------------------------------
+
+This is a minor update with (almost) no new features.
+
+* Improved efficiency.
+
+The speed of many typical operations has been increased, some substantially.
+
+* Initialise from integer.
+
+A BitString of '0' bits can be created using just an integer to give the length
+in bits. So instead of
+
+>>> s = BitString(length=100)
+
+you can write just
+
+>>> s = BitString(100)
+
+This matches the behaviour of bytearrays and (in Python 3) bytes.
+
+* A defect related to using the set / unset functions on BitStrings initialised
+from a file has been fixed.
+
+-----------------------------------------------------------------
+November 24th 2009: version 1.1.0 for Python 2.6 and 3.x released
+-----------------------------------------------------------------
+Note that this version will not work for Python 2.4 or 2.5. There may be an
+update for these Python versions some time next year, but it's not a priorty
+quite yet. Also note that only one version is now provided, which works for
+Python 2.6 and 3.x (done with the minimum of hackery!)
+
+* Improved efficiency.
+
+A fair number of functions have improved efficiency, some quite dramatically.
+
+* New bit setting and checking functions.
+
+Although these functions don't do anything that couldn't be done before, they
+do make some common use cases much more efficient. If you need to set or check
+single bits then these are the functions you need.
+
+set / unset : Set bit(s) to 1 or 0 respectively.
+allset / allunset : Check if all bits are 1 or all 0.
+anyset / anyunset : Check if any bits are 1 or any 0.
+
+>>> s = BitString(length=1000)
+>>> s.set((10, 100, 44, 12, 1))
+>>> s.allunset((2, 22, 222))
+True
+>>> s.anyset(range(7, 77))
+True
+
+* New rotate functions.
+
+ror / rol : Rotate bits to the right or left respectively.
+
+>>> s = BitString('0b100000000')
+>>> s.ror(2)
+>>> s.bin
+'0b001000000'
+>>> s.rol(5)
+>>> s.bin
+'0b000000100'
+
+* Floating point interpretations.
+
+New float initialisations and interpretations are available. These only work
+for BitStrings of length 32 or 64 bits.
+
+>>> s = BitString(float=0.2, length=64)
+>>> s.float
+0.200000000000000001
+>>> t = bitstring.pack('<3f', -0.4, 1e34, 17.0)
+>>> t.hex
+'0xcdccccbedf84f67700008841'
+
+* 'bytes' token reintroduced.
+
+This token returns a bytes object (equivalent to a str in Python 2.6).
+
+>>> s = BitString('0x010203')
+>>> s.unpack('bytes:2, bytes:1')
+['\x01\x02', '\x03']
+
+* 'uint' is now the default token type.
+
+So for example these are equivalent:
+
+a, b = s.readlist('uint:12, uint:12')
+a, b = s.readlist('12, 12')
+
+--------------------------------------------------------
+October 10th 2009: version 1.0.1 for Python 3.x released
+--------------------------------------------------------
+This is a straight port of version 1.0.0 to Python 3.
+
+For changes since the last Python 3 release read all the way down in this
+document to version 0.4.3.
+
+This version will also work for Python 2.6, but there's no advantage to using
+it over the 1.0.0 release. It won't work for anything before 2.6.
+
+-------------------------------------------------------
+October 9th 2009: version 1.0.0 for Python 2.x released
+-------------------------------------------------------
+Version 1 is here!
+
+This is the first release not to carry the 'beta' tag. It contains a couple of
+minor new features but is principally a release to fix the API. If you've been
+using an older version then you almost certainly will have to recode a bit. If
+you're not ready to do that then you may wish to delay updating.
+
+So the bad news is that there are lots of small changes to the API. The good
+news is that all the changes are pretty trivial, the new API is cleaner and
+more 'Pythonic', and that by making it version 1.0 I'm promising not to
+tweak it again for some time.
+
+** API Changes **
+
+* New read / peek functions for returning multiple items.
+
+The functions read, readbits, readbytes, peek, peekbits and peekbytes now only
+ever return a single item, never a list.
+
+The new functions readlist, readbitlist, readbytelist, peeklist, peekbitlist
+and peekbytelist can be used to read multiple items and will always return a
+list.
+
+So a line like:
+
+>>> a, b = s.read('uint:12, hex:32')
+
+becomes
+
+>>> a, b = s.readlist('uint:12, hex:32')
+
+* Renaming / removing functions.
+
+Functions have been renamed as follows:
+
+seekbit -> seek
+tellbit -> tell
+reversebits -> reverse
+deletebits -> delete
+tostring -> tobytes
+
+and a couple have been removed altogether:
+
+deletebytes - use delete instead.
+empty - use 'not s' rather than 's.empty()'.
+
+* Renaming parameters.
+
+The parameters 'startbit' and 'endbit' have been renamed 'start' and 'end'.
+This affects the functions slice, find, findall, rfind, reverse, cut and split.
+
+The parameter 'bitpos' has been renamed to 'pos'. The affects the functions
+seek, tell, insert, overwrite and delete.
+
+* Mutating methods return None rather than self.
+
+This means that you can't chain functions together so
+
+>>> s.append('0x00').prepend('0xff')
+>>> t = s.reverse()
+
+Needs to be rewritten
+
+>>> s.append('0x00')
+>>> s.prepend('0xff)
+>>> s.reverse()
+>>> t = s
+
+Affects truncatestart, truncateend, insert, overwrite, delete, append,
+prepend, reverse and reversebytes.
+
+* Properties renamed.
+
+The 'data' property has been renamed to 'bytes'. Also if the BitString is not a
+whole number of bytes then a ValueError exception will be raised when using
+'bytes' as a 'getter'.
+
+Properties 'len' and 'pos' have been added to replace 'length' and 'bitpos',
+although the longer names have not been removed so you can continue to use them
+if you prefer.
+
+* Other changes.
+
+The unpack function now always returns a list, never a single item.
+
+BitStrings are now 'unhashable', so calling hash on one or making a set will
+fail.
+
+The colon separating the token name from its length is now mandatory. So for
+example BitString('uint12=100') becomes BitString('uint:12=100').
+
+Removed support for the 'bytes' token in format strings. Instead of
+s.read('bytes:4') use s.read('bits:32').
+
+** New features **
+
+* Added endswith and startswith functions.
+
+These do much as you'd expect; they return True or False depending on whether
+the BitString starts or ends with the parameter.
+
+>>> BitString('0xef342').startswith('0b11101')
+True
+
+----------------------------------------------------------
+September 11th 2009: version 0.5.2 for Python 2.x released
+----------------------------------------------------------
+Finally some tools for dealing with endianness!
+
+* New interpretations are now available for whole-byte BitStrings that treat
+them as big, little, or native-endian.
+
+>>> big = BitString(intbe=1, length=16) # or BitString('intbe:16=1') if you prefer.
+>>> little = BitString(intle=1, length=16)
+>>> print big.hex, little.hex
+0x0001 0x0100
+>>> print big.intbe, little.intle
+1 1
+
+* 'Struct'-like compact format codes
+
+To save some typing when using pack, unpack, read and peek, compact format
+codes based on those used in the struct and array modules have been added.
+These must start with a character indicating the endianness (>, < or @ for
+big, little and native-endian), followed by characters giving the format:
+
+b 1-byte signed int
+B 1-byte unsigned int
+h 2-byte signed int
+H 2-byte unsigned int
+l 4-byte signed int
+L 4-byte unsigned int
+q 8-byte signed int
+Q 8-byte unsigned int
+
+For example:
+
+>>> s = bitstring.pack('<4h', 0, 1, 2, 3)
+
+creates a BitString with four little-endian 2-byte integers. While
+
+>>> x, y, z = s.read('>hhl')
+
+reads them back as two big-endian two-byte integers and one four-byte big
+endian integer.
+
+Of course you can combine this new format with the old ones however you like:
+
+>>> s.unpack('<h, intle:24, uint:5, bin')
+[0, 131073, 0, '0b0000000001100000000']
+
+-------------------------------------------------------
+August 26th 2009: version 0.5.1 for Python 2.x released
+-------------------------------------------------------
+
+This update introduces pack and unpack functions for creating and dissembling
+BitStrings.
+
+* New pack() and unpack() functions.
+
+The module level pack function provides a flexible new method for creating
+BitStrings. Tokens for BitString 'literals' can be used in the same way as in
+the constructor.
+
+>>> from bitstring import BitString, pack
+>>> a = pack('0b11, 0xff, 0o77, int:5=-1, se=33')
+
+You can also leave placeholders in the format, which will be filled in by
+the values provided.
+
+>>> b = pack('uint:10, hex:4', 33, 'f')
+
+Finally you can use a dictionary or keywords.
+
+>>> c = pack('bin=a, hex=b, bin=a', a='010', b='ef')
+
+The unpack function is similar to the read function except that it always
+unpacks from the start of the BitString.
+
+>>> x, y = b.unpack('uint:10, hex')
+
+If a token is given without a length (as above) then it will expand to fill the
+remaining bits in the BitString. This also now works with read() and peek().
+
+* New tostring() and tofile() functions.
+
+The tostring() function just returns the data as a string, with up to seven
+zero bits appended to byte align. The tofile() function does the same except
+writes to a file object.
+
+>>> f = open('myfile', 'wb')
+>>> BitString('0x1234ff').tofile(f)
+
+* Other changes.
+
+The use of '=' is now mandatory in 'auto' initialisers. Tokens like 'uint12 100' will
+no longer work. Also the use of a ':' before the length is encouraged, but not yet
+mandated. So the previous example should be written as 'uint:12=100'.
+
+The 'auto' initialiser will now take a file object.
+
+>>> f = open('myfile', 'rb')
+>>> s = BitString(f)
+
+-----------------------------------------------------
+July 19th 2009: version 0.5.0 for Python 2.x released
+-----------------------------------------------------
+
+This update breaks backward compatibility in a couple of areas. The only one
+you probably need to be concerned about is the change to the default for
+bytealigned in find, replace, split, etc.
+
+See the user manual for more details on each of these items.
+
+* Expanded abilities of 'auto' initialiser.
+
+More types can be initialised through the 'auto' initialiser. For example
+instead of
+
+>>> a = BitString(uint=44, length=16)
+
+you can write
+
+>>> a = BitString('uint16=44')
+
+Also, different comma-separated tokens will be joined together, e.g.
+
+>>> b = BitString('0xff') + 'int8=-5'
+
+can be written
+
+>>> b = BitString('0xff, int8=-5')
+
+* New formatted read() and peek() functions.
+
+These takes a format string similar to that used in the auto initialiser.
+If only one token is provided then a single value is returned, otherwise a
+list of values is returned.
+
+>>> start_code, width, height = s.read('hex32, uint12, uint12')
+
+is equivalent to
+
+>>> start_code = s.readbits(32).hex
+>>> width = s.readbits(12).uint
+>>> height = s.readbits(12).uint
+
+The tokens are:
+
+ int n : n bits as an unsigned integer.
+ uint n : n bits as a signed integer.
+ hex n : n bits as a hexadecimal string.
+ oct n : n bits as an octal string.
+ bin n : n bits as a binary string.
+ ue : next bits as an unsigned exp-Golomb.
+ se : next bits as a signed exp-Golomb.
+ bits n : n bits as a new BitString.
+ bytes n : n bytes as a new BitString.
+
+See the user manual for more details.
+
+* hex() and oct() functions removed.
+
+The special functions for hex() and oct() have been removed. Please use the
+hex and oct properties instead.
+
+>>> hex(s)
+
+becomes
+
+>>> s.hex
+
+* join made a member function.
+
+The join function must now be called on a BitString object, which will be
+used to join the list together. You may need to recode slightly:
+
+>>> s = bitstring.join('0x34', '0b1001', '0b1')
+
+becomes
+
+>>> s = BitString().join('0x34', '0b1001', '0b1')
+
+* More than one value allowed in readbits, readbytes, peekbits and peekbytes
+
+If you specify more than one bit or byte length then a list of BitStrings will
+be returned.
+
+>>> a, b, c = s.readbits(10, 5, 5)
+
+is equivalent to
+
+>>> a = readbits(10)
+>>> b = readbits(5)
+>>> c = readbits(5)
+
+* bytealigned defaults to False, and is at the end of the parameter list
+
+Functions that have a bytealigned paramater have changed so that it now
+defaults to False rather than True. Also its position in the parameter list
+has changed to be at the end. You may need to recode slightly (sorry!)
+
+* readue and readse functions have been removed
+
+Instead you should use the new read function with a 'ue' or 'se' token:
+
+>>> i = s.readue()
+
+becomes
+
+>>> i = s.read('ue')
+
+This is more flexible as you can read multiple items in one go, plus you can
+now also use the peek function with ue and se.
+
+* Minor bugs fixed.
+
+See the issue tracker for more details.
+
+-----------------------------------------------------
+June 15th 2009: version 0.4.3 for Python 2.x released
+-----------------------------------------------------
+
+This is a minor update. This release is the first to bundle the bitstring
+manual. This is a PDF and you can find it in the docs directory.
+
+Changes in version 0.4.3
+
+* New 'cut' function
+
+This function returns a generator for constant sized chunks of a BitString.
+
+>>> for byte in s.cut(8):
+... do_something_with(byte)
+
+You can also specify a startbit and endbit, as well as a count, which limits
+the number of items generated:
+
+>>> first100TSPackets = list(s.cut(188*8, count=100))
+
+* 'slice' function now equivalent to __getitem__.
+
+This means that a step can also be given to the slice function so that the
+following are now the same thing, and it's just a personal preference which
+to use:
+
+>>> s1 = s[a:b:c]
+>>> s2 = s.slice(a, b, c)
+
+* findall gets a 'count' parameter.
+
+So now
+
+>>> list(a.findall(s, count=n))
+
+is equivalent to
+
+>>> list(a.findall(s))[:n]
+
+except that it won't need to generate the whole list and so is much more
+efficient.
+
+* Changes to 'split'.
+
+The split function now has a 'count' parameter rather than 'maxsplit'. This
+makes the interface closer to that for cut, replace and findall. The final item
+generated is now no longer the whole of the rest of the BitString.
+
+* A couple of minor bugs were fixed. See the issue tracker for details.
+
+----------------------------------------------------
+May 25th 2009: version 0.4.2 for Python 2.x released
+----------------------------------------------------
+
+This is a minor update, and almost doesn't break compatibility with version
+0.4.0, but with the slight exception of findall() returning a generator,
+detailed below.
+
+Changes in version 0.4.2
+
+* Stepping in slices
+
+The use of the step parameter (also known as the stride) in slices has been
+added. Its use is a little non-standard as it effectively gives a multiplicative
+factor to apply to the start and stop parameters, rather than skipping over
+bits.
+
+For example this makes it much more convenient if you want to give slices in
+terms of bytes instead of bits. Instead of writing s[a*8:b*8] you can use
+s[a:b:8].
+
+When using a step the BitString is effectively truncated to a multiple of the
+step, so s[::8] is equal to s if s is an integer number of bytes, otherwise it
+is truncated by up to 7 bits. So the final seven complete 16-bit words could be
+written as s[-7::16]
+
+Negative slices are also allowed, and should do what you'd expect. So for
+example s[::-1] returns a bit-reversed copy of s (which is similar to
+s.reversebits(), which does the same operation on s in-place). As another
+example, to get the first 10 bytes in reverse byte order you could use
+s_bytereversed = s[0:10:-8].
+
+* Removed restrictions on offset
+
+You can now specify an offset of greater than 7 bits when creating a BitString,
+and the use of offset is also now permitted when using the filename initialiser.
+This is useful when you want to create a BitString from the middle of a file
+without having to read the file into memory.
+
+>>> f = BitString(filename='reallybigfile', offset=8000000, length=32)
+
+* Integers can be assigned to slices
+
+You can now assign an integer to a slice of a BitString. If the integer doesn't
+fit in the size of slice given then a ValueError exception is raised. So this
+is now allowed and works as expected:
+
+>>> s[8:16] = 106
+
+and is equivalent to
+
+>>> s[8:16] = BitString(uint=106, length=8)
+
+* Less exceptions raised
+
+Some changes have been made to slicing so that less exceptions are raised,
+bringing the interface closer to that for lists. So for example trying to delete
+past the end of the BitString will now just delete to the end, rather than
+raising a ValueError.
+
+* Initialisation from lists and tuples
+
+A new option for the auto initialiser is to pass it a list or tuple. The items
+in the list or tuple are evaluated as booleans and the bits in the BitString are
+set to 1 for True items and 0 for False items. This can be used anywhere the
+auto initialiser can currently be used. For example:
+
+>>> a = BitString([True, 7, False, 0, ()]) # 0b11000
+>>> b = a + ['Yes', ''] # Adds '0b10'
+>>> (True, True, False) in a
+True
+
+* Miscellany
+
+reversebits() now has optional startbit and endbit parameters.
+
+As an optimisation findall() will return a generator, rather than a list. If you
+still want the whole list then of course you can just call list() on the
+generator.
+
+Improved efficiency of rfind().
+
+A couple of minor bugs were fixed. See the issue tracker for details.
+
+-----------------------------------------------------
+April 23rd 2009: Python 3 only version 0.4.1 released
+-----------------------------------------------------
+
+This version is just a port of version 0.4.0 to Python 3. All the unit tests
+pass, but beyond that only limited ad hoc testing has been done and so it
+should be considered an experimental release. That said, the unit test
+coverage is very good - I'm just not sure if anyone even wants a Python 3
+version!
+
+---------------------------------------
+April 11th 2009: version 0.4.0 released
+---------------------------------------
+Changes in version 0.4.0
+
+* New functions
+
+Added rfind(), findall(), replace(). These do pretty much what you'd expect -
+see the docstrings or the wiki for more information.
+
+* More special functions
+
+Some missing functions were added: __repr__, __contains__, __rand__,
+__ror__, _rxor__ and __delitem__.
+
+* Miscellany
+
+A couple of small bugs were fixed (see the issue tracker).
+
+----
+
+There are some small backward incompatibilities relative to version 0.3.2:
+
+* Combined find() and findbytealigned()
+
+findbytealigned() has been removed, and becomes part of find(). The default
+start position has changed on both find() and split() to be the start of the
+BitString. You may need to recode:
+
+>>> s1.find(bs)
+>>> s2.findbytealigned(bs)
+>>> s2.split(bs)
+
+becomes
+
+>>> s1.find(bs, bytealigned=False, startbit=s1.bitpos)
+>>> s2.find(bs, startbit=s1.bitpos) # bytealigned defaults to True
+>>> s2.split(bs, startbit=s2.bitpos)
+
+* Reading off end of BitString no longer raises exception.
+
+Previously a read or peek function that encountered the end of the BitString
+would raise a ValueError. It will now instead return the remainder of the
+BitString, which could be an empty BitString. This is closer to the file
+object interface.
+
+* Removed visibility of offset.
+
+The offset property was previously read-only, and has now been removed from
+public view altogether. As it is used internally for efficiency reasons you
+shouldn't really have needed to use it. If you do then use the _offset parameter
+instead (with caution).
+
+---------------------------------------
+March 11th 2009: version 0.3.2 released
+---------------------------------------
+Changes in version 0.3.2
+
+* Better performance
+
+A number of functions (especially find() and findbytealigned()) have been sped
+up considerably.
+
+* Bit-wise operations
+
+Added support for bit-wise AND (&), OR (|) and XOR (^). For example:
+
+>>> a = BitString('0b00111')
+>>> print a & '0b10101'
+0b00101
+
+* Miscellany
+
+Added seekbit() and seekbyte() functions. These complement the 'advance' and
+'retreat' functions, although you can still just use bitpos and bytepos
+properties directly.
+
+>>> a.seekbit(100) # Equivalent to a.bitpos = 100
+
+Allowed comparisons between BitString objects and strings. For example this
+will now work:
+
+>>> a = BitString('0b00001111')
+>>> a == '0x0f'
+True
+
+------------------------------------------
+February 26th 2009: version 0.3.1 released
+------------------------------------------
+Changes in version 0.3.1
+
+This version only adds features and fixes bugs relative to 0.3.0, and doesn't
+break backwards compatibility.
+
+* Octal interpretation and initialisation
+
+The oct property now joins bin and hex. Just prefix octal numbers with '0o'.
+
+>>> a = BitString('0o755')
+>>> print a.bin
+0b111101101
+
+* Simpler copying
+
+Rather than using b = copy.copy(a) to create a copy of a BitString, now you
+can just use b = BitString(a).
+
+* More special methods
+
+Lots of new special methods added, for example bit-shifting via << and >>,
+equality testing via == and !=, bit inversion (~) and concatenation using *.
+
+Also __setitem__ is now supported so BitString objects can be modified using
+standard index notation.
+
+* Proper installer
+
+Finally got round to writing the distutils script. To install just
+python setup.py install.
+
+------------------------------------------
+February 15th 2009: version 0.3.0 released
+------------------------------------------
+Changes in version 0.3.0
+
+* Simpler initialisation from binary and hexadecimal
+
+The first argument in the BitString constructor is now called auto and will
+attempt to interpret the type of a string. Prefix binary numbers with '0b'
+and hexadecimals with '0x'.
+
+>>> a = BitString('0b0') # single zero bit
+>>> b = BitString('0xffff') # two bytes
+
+Previously the first argument was data, so if you relied on this then you
+will need to recode:
+
+>>> a = BitString('\x00\x00\x01\xb3') # Don't do this any more!
+
+becomes
+
+>>> a = BitString(data='\x00\x00\x01\xb3')
+
+or just
+
+>>> a = BitString('0x000001b3')
+
+This new notation can also be used in functions that take a BitString as an
+argument. For example:
+
+>>> a = BitString('0x0011') + '0xff'
+>>> a.insert('0b001', 6)
+>>> a.find('0b1111')
+
+* BitString made more mutable
+
+The functions append, deletebits, insert, overwrite, truncatestart and
+truncateend now modify the BitString that they act upon. This allows for
+cleaner and more efficient code, but you may need to rewrite slightly if you
+depended upon the old behaviour:
+
+>>> a = BitString(hex='0xffff')
+>>> a = a.append(BitString(hex='0x00'))
+>>> b = a.deletebits(10, 10)
+
+becomes:
+
+>>> a = BitString('0xffff')
+>>> a.append('0x00')
+>>> b = copy.copy(a)
+>>> b.deletebits(10, 10)
+
+Thanks to Frank Aune for suggestions in this and other areas.
+
+* Changes to printing
+
+The binary interpretation of a BitString is now prepended with '0b'. This is
+in keeping with the Python 2.6 (and 3.0) bin function. The prefix is optional
+when initialising using 'bin='.
+
+Also, if you just print a BitString with no interpretation it will pick
+something appropriate - hex if it is an integer number of bytes, otherwise
+binary. If the BitString representation is very long it will be truncated
+by '...' so it is only an approximate interpretation.
+
+>>> a = BitString('0b0011111')
+>>> print a
+0b0011111
+>>> a += '0b0'
+>>> print a
+0x3e
+
+* More convenience functions
+
+Some missing functions such as advancebit and deletebytes have been added. Also
+a number of peek functions make an appearance as have prepend and reversebits.
+See the Tutorial for more details.
+
+-----------------------------------------
+January 13th 2009: version 0.2.0 released
+-----------------------------------------
+Some fairly minor updates, not really deserving of a whole version point update.
+------------------------------------------
+December 29th 2008: version 0.1.0 released
+------------------------------------------
+First release!
diff --git a/python/bitstring/setup.py b/python/bitstring/setup.py
new file mode 100644
index 0000000000..9f088dda9b
--- /dev/null
+++ b/python/bitstring/setup.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python
+from distutils.core import setup
+# from distutils.extension import Extension
+# from Cython.Distutils import build_ext
+import sys
+
+kwds = {'long_description': open('README.txt').read()}
+
+if sys.version_info[:2] < (2, 6):
+ raise Exception('This version of bitstring needs Python 2.6 or later. '
+ 'For Python 2.4 / 2.5 please use bitstring version 1.0 instead.')
+
+# macros = [('PYREX_WITHOUT_ASSERTIONS', None)]
+# ext_modules = [Extension('bitstring', ["bitstring.pyx"], define_macros=macros)]
+
+setup(name='bitstring',
+ version='3.1.3',
+ description='Simple construction, analysis and modification of binary data.',
+ author='Scott Griffiths',
+ author_email='scott@griffiths.name',
+ url='http://python-bitstring.googlecode.com',
+ download_url='http://python-bitstring.googlecode.com',
+ license='The MIT License: http://www.opensource.org/licenses/mit-license.php',
+ # cmdclass = {'build_ext': build_ext},
+ # ext_modules = ext_modules,
+ py_modules=['bitstring'],
+ platforms='all',
+ classifiers = [
+ 'Development Status :: 5 - Production/Stable',
+ 'Intended Audience :: Developers',
+ 'Operating System :: OS Independent',
+ 'License :: OSI Approved :: MIT License',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Programming Language :: Python :: 3.0',
+ 'Programming Language :: Python :: 3.1',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
+ 'Topic :: Software Development :: Libraries :: Python Modules',
+ ],
+ **kwds
+ )
+
diff --git a/python/bitstring/test/smalltestfile b/python/bitstring/test/smalltestfile
new file mode 100644
index 0000000000..be687ec351
--- /dev/null
+++ b/python/bitstring/test/smalltestfile
@@ -0,0 +1 @@
+#Eg‰«Íï \ No newline at end of file
diff --git a/python/bitstring/test/test.m1v b/python/bitstring/test/test.m1v
new file mode 100644
index 0000000000..2da3ece11a
--- /dev/null
+++ b/python/bitstring/test/test.m1v
Binary files differ
diff --git a/python/bitstring/test/test_bitarray.py b/python/bitstring/test/test_bitarray.py
new file mode 100644
index 0000000000..b80f90617b
--- /dev/null
+++ b/python/bitstring/test/test_bitarray.py
@@ -0,0 +1,310 @@
+#!/usr/bin/env python
+"""
+Unit tests for the bitarray module.
+"""
+
+import unittest
+import sys
+
+sys.path.insert(0, '..')
+import bitstring
+from bitstring import BitArray
+
+class All(unittest.TestCase):
+ def testCreationFromUint(self):
+ s = BitArray(uint=15, length=6)
+ self.assertEqual(s.bin, '001111')
+ s = BitArray(uint=0, length=1)
+ self.assertEqual(s.bin, '0')
+ s.uint = 1
+ self.assertEqual(s.uint, 1)
+ s = BitArray(length=8)
+ s.uint = 0
+ self.assertEqual(s.uint, 0)
+ s.uint = 255
+ self.assertEqual(s.uint, 255)
+ self.assertEqual(s.len, 8)
+ self.assertRaises(bitstring.CreationError, s._setuint, 256)
+
+ def testCreationFromOct(self):
+ s = BitArray(oct='7')
+ self.assertEqual(s.oct, '7')
+ self.assertEqual(s.bin, '111')
+ s.append('0o1')
+ self.assertEqual(s.bin, '111001')
+ s.oct = '12345670'
+ self.assertEqual(s.length, 24)
+ self.assertEqual(s.bin, '001010011100101110111000')
+ s = BitArray('0o123')
+ self.assertEqual(s.oct, '123')
+
+
+class NoPosAttribute(unittest.TestCase):
+ def testReplace(self):
+ s = BitArray('0b01')
+ s.replace('0b1', '0b11')
+ self.assertEqual(s, '0b011')
+
+ def testDelete(self):
+ s = BitArray('0b000000001')
+ del s[-1:]
+ self.assertEqual(s, '0b00000000')
+
+ def testInsert(self):
+ s = BitArray('0b00')
+ s.insert('0xf', 1)
+ self.assertEqual(s, '0b011110')
+
+ def testInsertParameters(self):
+ s = BitArray('0b111')
+ self.assertRaises(TypeError, s.insert, '0x4')
+
+ def testOverwrite(self):
+ s = BitArray('0b01110')
+ s.overwrite('0b000', 1)
+ self.assertEqual(s, '0b00000')
+
+ def testOverwriteParameters(self):
+ s = BitArray('0b0000')
+ self.assertRaises(TypeError, s.overwrite, '0b111')
+
+ def testPrepend(self):
+ s = BitArray('0b0')
+ s.prepend([1])
+ self.assertEqual(s, [1, 0])
+
+ def testRol(self):
+ s = BitArray('0b0001')
+ s.rol(1)
+ self.assertEqual(s, '0b0010')
+
+ def testRor(self):
+ s = BitArray('0b1000')
+ s.ror(1)
+ self.assertEqual(s, '0b0100')
+
+ def testSetItem(self):
+ s = BitArray('0b000100')
+ s[4:5] = '0xf'
+ self.assertEqual(s, '0b000111110')
+ s[0:1] = [1]
+ self.assertEqual(s, '0b100111110')
+
+
+class Bugs(unittest.TestCase):
+ def testAddingNonsense(self):
+ a = BitArray([0])
+ a += '0' # a uint of length 0 - so nothing gets added.
+ self.assertEqual(a, [0])
+ self.assertRaises(ValueError, a.__iadd__, '3')
+ self.assertRaises(ValueError, a.__iadd__, 'se')
+ self.assertRaises(ValueError, a.__iadd__, 'float:32')
+
+ def testPrependAfterCreationFromDataWithOffset(self):
+ s1 = BitArray(bytes=b'\x00\x00\x07\xff\xf0\x00', offset=21, length=15)
+ self.assertFalse(s1.any(0))
+ s1.prepend('0b0')
+ self.assertEqual(s1.bin, '0111111111111111')
+ s1.prepend('0b0')
+ self.assertEqual(s1.bin, '00111111111111111')
+
+
+class ByteAligned(unittest.TestCase):
+ def testDefault(self, defaultbytealigned=bitstring.bytealigned):
+ self.assertFalse(defaultbytealigned)
+
+ def testChangingIt(self):
+ bitstring.bytealigned = True
+ self.assertTrue(bitstring.bytealigned)
+ bitstring.bytealigned = False
+
+ def testNotByteAligned(self):
+ bitstring.bytealigned = False
+ a = BitArray('0x00 ff 0f f')
+ l = list(a.findall('0xff'))
+ self.assertEqual(l, [8, 20])
+ p = a.find('0x0f')[0]
+ self.assertEqual(p, 4)
+ p = a.rfind('0xff')[0]
+ self.assertEqual(p, 20)
+ s = list(a.split('0xff'))
+ self.assertEqual(s, ['0x00', '0xff0', '0xff'])
+ a.replace('0xff', '')
+ self.assertEqual(a, '0x000')
+
+ def testByteAligned(self):
+ bitstring.bytealigned = True
+ a = BitArray('0x00 ff 0f f')
+ l = list(a.findall('0xff'))
+ self.assertEqual(l, [8])
+ p = a.find('0x0f')[0]
+ self.assertEqual(p, 16)
+ p = a.rfind('0xff')[0]
+ self.assertEqual(p, 8)
+ s = list(a.split('0xff'))
+ self.assertEqual(s, ['0x00', '0xff0ff'])
+ a.replace('0xff', '')
+ self.assertEqual(a, '0x000ff')
+
+
+class SliceAssignment(unittest.TestCase):
+
+ def testSliceAssignmentSingleBit(self):
+ a = BitArray('0b000')
+ a[2] = '0b1'
+ self.assertEqual(a.bin, '001')
+ a[0] = BitArray(bin='1')
+ self.assertEqual(a.bin, '101')
+ a[-1] = '0b0'
+ self.assertEqual(a.bin, '100')
+ a[-3] = '0b0'
+ self.assertEqual(a.bin, '000')
+
+ def testSliceAssignmentSingleBitErrors(self):
+ a = BitArray('0b000')
+ self.assertRaises(IndexError, a.__setitem__, -4, '0b1')
+ self.assertRaises(IndexError, a.__setitem__, 3, '0b1')
+ self.assertRaises(TypeError, a.__setitem__, 1, 1.3)
+
+ def testSliceAssignmentMulipleBits(self):
+ a = BitArray('0b0')
+ a[0] = '0b110'
+ self.assertEqual(a.bin, '110')
+ a[0] = '0b000'
+ self.assertEqual(a.bin, '00010')
+ a[0:3] = '0b111'
+ self.assertEqual(a.bin, '11110')
+ a[-2:] = '0b011'
+ self.assertEqual(a.bin, '111011')
+ a[:] = '0x12345'
+ self.assertEqual(a.hex, '12345')
+ a[:] = ''
+ self.assertFalse(a)
+
+ def testSliceAssignmentMultipleBitsErrors(self):
+ a = BitArray()
+ self.assertRaises(IndexError, a.__setitem__, 0, '0b00')
+ a += '0b1'
+ a[0:2] = '0b11'
+ self.assertEqual(a, '0b11')
+
+ def testDelSliceStep(self):
+ a = BitArray(bin='100111101001001110110100101')
+ del a[::2]
+ self.assertEqual(a.bin, '0110010101100')
+ del a[3:9:3]
+ self.assertEqual(a.bin, '01101101100')
+ del a[2:7:1]
+ self.assertEqual(a.bin, '011100')
+ del a[::99]
+ self.assertEqual(a.bin, '11100')
+ del a[::1]
+ self.assertEqual(a.bin, '')
+
+ def testDelSliceNegativeStep(self):
+ a = BitArray('0b0001011101101100100110000001')
+ del a[5:23:-3]
+ self.assertEqual(a.bin, '0001011101101100100110000001')
+ del a[25:3:-3]
+ self.assertEqual(a.bin, '00011101010000100001')
+ del a[:6:-7]
+ self.assertEqual(a.bin, '000111010100010000')
+ del a[15::-2]
+ self.assertEqual(a.bin, '0010000000')
+ del a[::-1]
+ self.assertEqual(a.bin, '')
+
+ def testDelSliceErrors(self):
+ a = BitArray(10)
+ del a[5:3]
+ self.assertEqual(a, 10)
+ del a[3:5:-1]
+ self.assertEqual(a, 10)
+
+ def testDelSingleElement(self):
+ a = BitArray('0b0010011')
+ del a[-1]
+ self.assertEqual(a.bin, '001001')
+ del a[2]
+ self.assertEqual(a.bin, '00001')
+ try:
+ del a[5]
+ self.assertTrue(False)
+ except IndexError:
+ pass
+
+ def testSetSliceStep(self):
+ a = BitArray(bin='0000000000')
+ a[::2] = '0b11111'
+ self.assertEqual(a.bin, '1010101010')
+ a[4:9:3] = [0, 0]
+ self.assertEqual(a.bin, '1010001010')
+ a[7:3:-1] = [1, 1, 1, 0]
+ self.assertEqual(a.bin, '1010011110')
+ a[7:1:-2] = [0, 0, 1]
+ self.assertEqual(a.bin, '1011001010')
+ a[::-5] = [1, 1]
+ self.assertEqual(a.bin, '1011101011')
+ a[::-1] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 1]
+ self.assertEqual(a.bin, '1000000000')
+
+ def testSetSliceErrors(self):
+ a = BitArray(8)
+ try:
+ a[::3] = [1]
+ self.assertTrue(False)
+ except ValueError:
+ pass
+ class A(object): pass
+ try:
+ a[1:2] = A()
+ self.assertTrue(False)
+ except TypeError:
+ pass
+ try:
+ a[1:4:-1] = [1, 2]
+ self.assertTrue(False)
+ except ValueError:
+ pass
+
+
+class Subclassing(unittest.TestCase):
+
+ def testIsInstance(self):
+ class SubBits(BitArray): pass
+ a = SubBits()
+ self.assertTrue(isinstance(a, SubBits))
+
+ def testClassType(self):
+ class SubBits(BitArray): pass
+ self.assertEqual(SubBits().__class__, SubBits)
+
+
+class Clear(unittest.TestCase):
+
+ def testClear(self):
+ s = BitArray('0xfff')
+ s.clear()
+ self.assertEqual(s.len, 0)
+
+
+class Copy(unittest.TestCase):
+
+ def testCopyMethod(self):
+ s = BitArray(9)
+ t = s.copy()
+ self.assertEqual(s, t)
+ t[0] = True
+ self.assertEqual(t.bin, '100000000')
+ self.assertEqual(s.bin, '000000000')
+
+
+class ModifiedByAddingBug(unittest.TestCase):
+
+ def testAdding(self):
+ a = BitArray('0b0')
+ b = BitArray('0b11')
+ c = a + b
+ self.assertEqual(c, '0b011')
+ self.assertEqual(a, '0b0')
+ self.assertEqual(b, '0b11') \ No newline at end of file
diff --git a/python/bitstring/test/test_bits.py b/python/bitstring/test/test_bits.py
new file mode 100644
index 0000000000..402c038996
--- /dev/null
+++ b/python/bitstring/test/test_bits.py
@@ -0,0 +1,378 @@
+#!/usr/bin/env python
+
+import unittest
+import sys
+
+sys.path.insert(0, '..')
+import bitstring
+from bitstring import MmapByteArray
+from bitstring import Bits, BitArray, ConstByteStore, ByteStore
+
+class Creation(unittest.TestCase):
+ def testCreationFromBytes(self):
+ s = Bits(bytes=b'\xa0\xff')
+ self.assertEqual((s.len, s.hex), (16, 'a0ff'))
+ s = Bits(bytes=b'abc', length=0)
+ self.assertEqual(s, '')
+
+ def testCreationFromBytesErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, bytes=b'abc', length=25)
+
+ def testCreationFromDataWithOffset(self):
+ s1 = Bits(bytes=b'\x0b\x1c\x2f', offset=0, length=20)
+ s2 = Bits(bytes=b'\xa0\xb1\xC2', offset=4)
+ self.assertEqual((s2.len, s2.hex), (20, '0b1c2'))
+ self.assertEqual((s1.len, s1.hex), (20, '0b1c2'))
+ self.assertTrue(s1 == s2)
+
+ def testCreationFromHex(self):
+ s = Bits(hex='0xA0ff')
+ self.assertEqual((s.len, s.hex), (16, 'a0ff'))
+ s = Bits(hex='0x0x0X')
+ self.assertEqual((s.length, s.hex), (0, ''))
+
+ def testCreationFromHexWithWhitespace(self):
+ s = Bits(hex=' \n0 X a 4e \r3 \n')
+ self.assertEqual(s.hex, 'a4e3')
+
+ def testCreationFromHexErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, hex='0xx0')
+ self.assertRaises(bitstring.CreationError, Bits, hex='0xX0')
+ self.assertRaises(bitstring.CreationError, Bits, hex='0Xx0')
+ self.assertRaises(bitstring.CreationError, Bits, hex='-2e')
+ # These really should fail, but it's awkward and not a big deal...
+# self.assertRaises(bitstring.CreationError, Bits, '0x2', length=2)
+# self.assertRaises(bitstring.CreationError, Bits, '0x3', offset=1)
+
+ def testCreationFromBin(self):
+ s = Bits(bin='1010000011111111')
+ self.assertEqual((s.length, s.hex), (16, 'a0ff'))
+ s = Bits(bin='00')[:1]
+ self.assertEqual(s.bin, '0')
+ s = Bits(bin=' 0000 \n 0001\r ')
+ self.assertEqual(s.bin, '00000001')
+
+ def testCreationFromBinWithWhitespace(self):
+ s = Bits(bin=' \r\r\n0 B 00 1 1 \t0 ')
+ self.assertEqual(s.bin, '00110')
+
+ def testCreationFromOctErrors(self):
+ s = Bits('0b00011')
+ self.assertRaises(bitstring.InterpretError, s._getoct)
+ self.assertRaises(bitstring.CreationError, s._setoct, '8')
+
+ def testCreationFromUintWithOffset(self):
+ self.assertRaises(bitstring.Error, Bits, uint=12, length=8, offset=1)
+
+ def testCreationFromUintErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, uint=-1, length=10)
+ self.assertRaises(bitstring.CreationError, Bits, uint=12)
+ self.assertRaises(bitstring.CreationError, Bits, uint=4, length=2)
+ self.assertRaises(bitstring.CreationError, Bits, uint=0, length=0)
+ self.assertRaises(bitstring.CreationError, Bits, uint=12, length=-12)
+
+ def testCreationFromInt(self):
+ s = Bits(int=0, length=4)
+ self.assertEqual(s.bin, '0000')
+ s = Bits(int=1, length=2)
+ self.assertEqual(s.bin, '01')
+ s = Bits(int=-1, length=11)
+ self.assertEqual(s.bin, '11111111111')
+ s = Bits(int=12, length=7)
+ self.assertEqual(s.int, 12)
+ s = Bits(int=-243, length=108)
+ self.assertEqual((s.int, s.length), (-243, 108))
+ for length in range(6, 10):
+ for value in range(-17, 17):
+ s = Bits(int=value, length=length)
+ self.assertEqual((s.int, s.length), (value, length))
+ s = Bits(int=10, length=8)
+
+ def testCreationFromIntErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, int=-1, length=0)
+ self.assertRaises(bitstring.CreationError, Bits, int=12)
+ self.assertRaises(bitstring.CreationError, Bits, int=4, length=3)
+ self.assertRaises(bitstring.CreationError, Bits, int=-5, length=3)
+
+ def testCreationFromSe(self):
+ for i in range(-100, 10):
+ s = Bits(se=i)
+ self.assertEqual(s.se, i)
+
+ def testCreationFromSeWithOffset(self):
+ self.assertRaises(bitstring.CreationError, Bits, se=-13, offset=1)
+
+ def testCreationFromSeErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, se=-5, length=33)
+ s = Bits(bin='001000')
+ self.assertRaises(bitstring.InterpretError, s._getse)
+
+ def testCreationFromUe(self):
+ [self.assertEqual(Bits(ue=i).ue, i) for i in range(0, 20)]
+
+ def testCreationFromUeWithOffset(self):
+ self.assertRaises(bitstring.CreationError, Bits, ue=104, offset=2)
+
+ def testCreationFromUeErrors(self):
+ self.assertRaises(bitstring.CreationError, Bits, ue=-1)
+ self.assertRaises(bitstring.CreationError, Bits, ue=1, length=12)
+ s = Bits(bin='10')
+ self.assertRaises(bitstring.InterpretError, s._getue)
+
+ def testCreationFromBool(self):
+ a = Bits('bool=1')
+ self.assertEqual(a, 'bool=1')
+ b = Bits('bool=0')
+ self.assertEqual(b, [0])
+ c = bitstring.pack('2*bool', 0, 1)
+ self.assertEqual(c, '0b01')
+
+ def testCreationKeywordError(self):
+ self.assertRaises(bitstring.CreationError, Bits, squirrel=5)
+
+ def testDataStoreType(self):
+ a = Bits('0xf')
+ self.assertEqual(type(a._datastore), bitstring.ConstByteStore)
+
+
+class Initialisation(unittest.TestCase):
+ def testEmptyInit(self):
+ a = Bits()
+ self.assertEqual(a, '')
+
+ def testNoPos(self):
+ a = Bits('0xabcdef')
+ try:
+ a.pos
+ except AttributeError:
+ pass
+ else:
+ assert False
+
+ def testFind(self):
+ a = Bits('0xabcd')
+ r = a.find('0xbc')
+ self.assertEqual(r[0], 4)
+ r = a.find('0x23462346246', bytealigned=True)
+ self.assertFalse(r)
+
+ def testRfind(self):
+ a = Bits('0b11101010010010')
+ b = a.rfind('0b010')
+ self.assertEqual(b[0], 11)
+
+ def testFindAll(self):
+ a = Bits('0b0010011')
+ b = list(a.findall([1]))
+ self.assertEqual(b, [2, 5, 6])
+
+
+class Cut(unittest.TestCase):
+ def testCut(self):
+ s = Bits(30)
+ for t in s.cut(3):
+ self.assertEqual(t, [0] * 3)
+
+
+class InterleavedExpGolomb(unittest.TestCase):
+ def testCreation(self):
+ s1 = Bits(uie=0)
+ s2 = Bits(uie=1)
+ self.assertEqual(s1, [1])
+ self.assertEqual(s2, [0, 0, 1])
+ s1 = Bits(sie=0)
+ s2 = Bits(sie=-1)
+ s3 = Bits(sie=1)
+ self.assertEqual(s1, [1])
+ self.assertEqual(s2, [0, 0, 1, 1])
+ self.assertEqual(s3, [0, 0, 1, 0])
+
+ def testCreationFromProperty(self):
+ s = BitArray()
+ s.uie = 45
+ self.assertEqual(s.uie, 45)
+ s.sie = -45
+ self.assertEqual(s.sie, -45)
+
+ def testInterpretation(self):
+ for x in range(101):
+ self.assertEqual(Bits(uie=x).uie, x)
+ for x in range(-100, 100):
+ self.assertEqual(Bits(sie=x).sie, x)
+
+ def testErrors(self):
+ for f in ['sie=100, 0b1001', '0b00', 'uie=100, 0b1001']:
+ s = Bits(f)
+ self.assertRaises(bitstring.InterpretError, s._getsie)
+ self.assertRaises(bitstring.InterpretError, s._getuie)
+ self.assertRaises(ValueError, Bits, 'uie=-10')
+
+
+class FileBased(unittest.TestCase):
+ def setUp(self):
+ self.a = Bits(filename='smalltestfile')
+ self.b = Bits(filename='smalltestfile', offset=16)
+ self.c = Bits(filename='smalltestfile', offset=20, length=16)
+ self.d = Bits(filename='smalltestfile', offset=20, length=4)
+
+ def testCreationWithOffset(self):
+ self.assertEqual(self.a, '0x0123456789abcdef')
+ self.assertEqual(self.b, '0x456789abcdef')
+ self.assertEqual(self.c, '0x5678')
+
+ def testBitOperators(self):
+ x = self.b[4:20]
+ self.assertEqual(x, '0x5678')
+ self.assertEqual((x & self.c).hex, self.c.hex)
+ self.assertEqual(self.c ^ self.b[4:20], 16)
+ self.assertEqual(self.a[23:36] | self.c[3:], self.c[3:])
+
+ def testAddition(self):
+ h = self.d + '0x1'
+ x = self.a[20:24] + self.c[-4:] + self.c[8:12]
+ self.assertEqual(x, '0x587')
+ x = self.b + x
+ self.assertEqual(x.hex, '456789abcdef587')
+ x = BitArray(x)
+ del x[12:24]
+ self.assertEqual(x, '0x456abcdef587')
+
+class Mmap(unittest.TestCase):
+ def setUp(self):
+ self.f = open('smalltestfile', 'rb')
+
+ def tearDown(self):
+ self.f.close()
+
+ def testByteArrayEquivalence(self):
+ a = MmapByteArray(self.f)
+ self.assertEqual(a.bytelength, 8)
+ self.assertEqual(len(a), 8)
+ self.assertEqual(a[0], 0x01)
+ self.assertEqual(a[1], 0x23)
+ self.assertEqual(a[7], 0xef)
+ self.assertEqual(a[0:1], bytearray([1]))
+ self.assertEqual(a[:], bytearray([0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]))
+ self.assertEqual(a[2:4], bytearray([0x45, 0x67]))
+
+ def testWithLength(self):
+ a = MmapByteArray(self.f, 3)
+ self.assertEqual(a[0], 0x01)
+ self.assertEqual(len(a), 3)
+
+ def testWithOffset(self):
+ a = MmapByteArray(self.f, None, 5)
+ self.assertEqual(len(a), 3)
+ self.assertEqual(a[0], 0xab)
+
+ def testWithLengthAndOffset(self):
+ a = MmapByteArray(self.f, 3, 3)
+ self.assertEqual(len(a), 3)
+ self.assertEqual(a[0], 0x67)
+ self.assertEqual(a[:], bytearray([0x67, 0x89, 0xab]))
+
+
+class Comparisons(unittest.TestCase):
+ def testUnorderable(self):
+ a = Bits(5)
+ b = Bits(5)
+ self.assertRaises(TypeError, a.__lt__, b)
+ self.assertRaises(TypeError, a.__gt__, b)
+ self.assertRaises(TypeError, a.__le__, b)
+ self.assertRaises(TypeError, a.__ge__, b)
+
+
+class Subclassing(unittest.TestCase):
+
+ def testIsInstance(self):
+ class SubBits(bitstring.Bits): pass
+ a = SubBits()
+ self.assertTrue(isinstance(a, SubBits))
+
+ def testClassType(self):
+ class SubBits(bitstring.Bits): pass
+ self.assertEqual(SubBits().__class__, SubBits)
+
+
+class LongBoolConversion(unittest.TestCase):
+
+ def testLongBool(self):
+ a = Bits(1000)
+ b = bool(a)
+ self.assertTrue(b is False)
+
+
+# Some basic tests for the private ByteStore classes
+
+class ConstByteStoreCreation(unittest.TestCase):
+
+ def testProperties(self):
+ a = ConstByteStore(bytearray(b'abc'))
+ self.assertEqual(a.bytelength, 3)
+ self.assertEqual(a.offset, 0)
+ self.assertEqual(a.bitlength, 24)
+ self.assertEqual(a._rawarray, b'abc')
+
+ def testGetBit(self):
+ a = ConstByteStore(bytearray([0x0f]))
+ self.assertEqual(a.getbit(0), False)
+ self.assertEqual(a.getbit(3), False)
+ self.assertEqual(a.getbit(4), True)
+ self.assertEqual(a.getbit(7), True)
+
+ b = ConstByteStore(bytearray([0x0f]), 7, 1)
+ self.assertEqual(b.getbit(2), False)
+ self.assertEqual(b.getbit(3), True)
+
+ def testGetByte(self):
+ a = ConstByteStore(bytearray(b'abcde'), 1, 13)
+ self.assertEqual(a.getbyte(0), 97)
+ self.assertEqual(a.getbyte(1), 98)
+ self.assertEqual(a.getbyte(4), 101)
+
+
+class PadToken(unittest.TestCase):
+
+ def testCreation(self):
+ a = Bits('pad:10')
+ self.assertEqual(a, Bits(10))
+ b = Bits('pad:0')
+ self.assertEqual(b, Bits())
+ c = Bits('0b11, pad:1, 0b111')
+ self.assertEqual(c, Bits('0b110111'))
+
+ def testPack(self):
+ s = bitstring.pack('0b11, pad:3=5, 0b1')
+ self.assertEqual(s.bin, '110001')
+ d = bitstring.pack('pad:c', c=12)
+ self.assertEqual(d, Bits(12))
+ e = bitstring.pack('0xf, uint:12, pad:1, bin, pad:4, 0b10', 0, '111')
+ self.assertEqual(e.bin, '11110000000000000111000010')
+
+ def testUnpack(self):
+ s = Bits('0b111000111')
+ x, y = s.unpack('3, pad:3, 3')
+ self.assertEqual((x, y), (7, 7))
+ x, y = s.unpack('2, pad:2, bin')
+ self.assertEqual((x, y), (3, '00111'))
+ x = s.unpack('pad:1, pad:2, pad:3')
+ self.assertEqual(x, [])
+
+
+class ModifiedByAddingBug(unittest.TestCase):
+
+ def testAdding(self):
+ a = Bits('0b0')
+ b = Bits('0b11')
+ c = a + b
+ self.assertEqual(c, '0b011')
+ self.assertEqual(a, '0b0')
+ self.assertEqual(b, '0b11')
+
+ def testAdding2(self):
+ a = Bits(100)
+ b = Bits(101)
+ c = a + b
+ self.assertEqual(a, 100)
+ self.assertEqual(b, 101)
+ self.assertEqual(c, 201)
diff --git a/python/bitstring/test/test_bitstore.py b/python/bitstring/test/test_bitstore.py
new file mode 100644
index 0000000000..9f5c9036ee
--- /dev/null
+++ b/python/bitstring/test/test_bitstore.py
@@ -0,0 +1,37 @@
+#!/usr/bin/env python
+
+import unittest
+import sys
+sys.path.insert(0, '..')
+from bitstring import ByteStore, ConstByteStore, equal, offsetcopy
+
+
+class OffsetCopy(unittest.TestCase):
+ def testStraightCopy(self):
+ s = ByteStore(bytearray([10, 5, 1]), 24, 0)
+ t = offsetcopy(s, 0)
+ self.assertEqual(t._rawarray, bytearray([10, 5, 1]))
+
+ def testOffsetIncrease(self):
+ s = ByteStore(bytearray([1, 1, 1]), 24, 0)
+ t = offsetcopy(s, 4)
+ self.assertEqual(t.bitlength, 24)
+ self.assertEqual(t.offset, 4)
+ self.assertEqual(t._rawarray, bytearray([0, 16, 16, 16]))
+
+
+class Equals(unittest.TestCase):
+
+ def testBothSingleByte(self):
+ s = ByteStore(bytearray([128]), 3, 0)
+ t = ByteStore(bytearray([64]), 3, 1)
+ u = ByteStore(bytearray([32]), 3, 2)
+ self.assertTrue(equal(s, t))
+ self.assertTrue(equal(s, u))
+ self.assertTrue(equal(u, t))
+
+ def testOneSingleByte(self):
+ s = ByteStore(bytearray([1, 0]), 2, 7)
+ t = ByteStore(bytearray([64]), 2, 1)
+ self.assertTrue(equal(s, t))
+ self.assertTrue(equal(t, s)) \ No newline at end of file
diff --git a/python/bitstring/test/test_bitstream.py b/python/bitstring/test/test_bitstream.py
new file mode 100644
index 0000000000..f94193d324
--- /dev/null
+++ b/python/bitstring/test/test_bitstream.py
@@ -0,0 +1,3940 @@
+#!/usr/bin/env python
+
+import unittest
+import sys
+sys.path.insert(0, '..')
+import bitstring
+import copy
+import os
+import collections
+from bitstring import BitStream, ConstBitStream, pack
+from bitstring import ByteStore, offsetcopy
+
+
+class FlexibleInitialisation(unittest.TestCase):
+ def testFlexibleInitialisation(self):
+ a = BitStream('uint:8=12')
+ c = BitStream(' uint : 8 = 12')
+ self.assertTrue(a == c == BitStream(uint=12, length=8))
+ self.assertEqual(a.uint, 12)
+ a = BitStream(' int:2= -1')
+ b = BitStream('int :2 = -1')
+ c = BitStream(' int: 2 =-1 ')
+ self.assertTrue(a == b == c == BitStream(int=-1, length=2))
+
+ def testFlexibleInitialisation2(self):
+ h = BitStream('hex=12')
+ o = BitStream('oct=33')
+ b = BitStream('bin=10')
+ self.assertEqual(h, '0x12')
+ self.assertEqual(o, '0o33')
+ self.assertEqual(b, '0b10')
+
+ def testFlexibleInitialisation3(self):
+ for s in ['se=-1', ' se = -1 ', 'se = -1']:
+ a = BitStream(s)
+ self.assertEqual(a.se, -1)
+ for s in ['ue=23', 'ue =23', 'ue = 23']:
+ a = BitStream(s)
+ self.assertEqual(a.ue, 23)
+
+ def testMultipleStringInitialisation(self):
+ a = BitStream('0b1 , 0x1')
+ self.assertEqual(a, '0b10001')
+ a = BitStream('ue=5, ue=1, se=-2')
+ self.assertEqual(a.read('ue'), 5)
+ self.assertEqual(a.read('ue'), 1)
+ self.assertEqual(a.read('se'), -2)
+ b = BitStream('uint:32 = 12, 0b11') + 'int:100=-100, 0o44'
+ self.assertEqual(b.read(32).uint, 12)
+ self.assertEqual(b.read(2).bin, '11')
+ self.assertEqual(b.read(100).int, -100)
+
+
+class Reading(unittest.TestCase):
+ def testReadBits(self):
+ s = BitStream(bytes=b'\x4d\x55')
+ self.assertEqual(s.read(4).hex, '4')
+ self.assertEqual(s.read(8).hex, 'd5')
+ self.assertEqual(s.read(1), [0])
+ self.assertEqual(s.read(3).bin, '101')
+ self.assertFalse(s.read(0))
+
+ def testReadByte(self):
+ s = BitStream(hex='4d55')
+ self.assertEqual(s.read(8).hex, '4d')
+ self.assertEqual(s.read(8).hex, '55')
+
+ def testReadBytes(self):
+ s = BitStream(hex='0x112233448811')
+ self.assertEqual(s.read(3 * 8).hex, '112233')
+ self.assertRaises(ValueError, s.read, -2 * 8)
+ s.bitpos += 1
+ self.assertEqual(s.read(2 * 8).bin, '1000100100010000')
+
+ def testReadUE(self):
+ self.assertRaises(bitstring.InterpretError, BitStream('')._getue)
+ # The numbers 0 to 8 as unsigned Exponential-Golomb codes
+ s = BitStream(bin='1 010 011 00100 00101 00110 00111 0001000 0001001')
+ self.assertEqual(s.pos, 0)
+ for i in range(9):
+ self.assertEqual(s.read('ue'), i)
+ self.assertRaises(bitstring.ReadError, s.read, 'ue')
+
+ def testReadSE(self):
+ s = BitStream(bin='010 00110 0001010 0001000 00111')
+ self.assertEqual(s.read('se'), 1)
+ self.assertEqual(s.read('se'), 3)
+ self.assertEqual(s.readlist(3 * ['se']), [5, 4, -3])
+
+
+class Find(unittest.TestCase):
+ def testFind1(self):
+ s = ConstBitStream(bin='0b0000110110000')
+ self.assertTrue(s.find(BitStream(bin='11011'), False))
+ self.assertEqual(s.bitpos, 4)
+ self.assertEqual(s.read(5).bin, '11011')
+ s.bitpos = 0
+ self.assertFalse(s.find('0b11001', False))
+
+ def testFind2(self):
+ s = BitStream(bin='0')
+ self.assertTrue(s.find(s, False))
+ self.assertEqual(s.pos, 0)
+ self.assertFalse(s.find('0b00', False))
+ self.assertRaises(ValueError, s.find, BitStream(), False)
+
+ def testFindWithOffset(self):
+ s = BitStream(hex='0x112233')[4:]
+ self.assertTrue(s.find('0x23', False))
+ self.assertEqual(s.pos, 8)
+
+ def testFindCornerCases(self):
+ s = BitStream(bin='000111000111')
+ self.assertTrue(s.find('0b000'))
+ self.assertEqual(s.pos, 0)
+ self.assertTrue(s.find('0b000'))
+ self.assertEqual(s.pos, 0)
+ self.assertTrue(s.find('0b0111000111'))
+ self.assertEqual(s.pos, 2)
+ self.assertTrue(s.find('0b000', start=2))
+ self.assertEqual(s.pos, 6)
+ self.assertTrue(s.find('0b111', start=6))
+ self.assertEqual(s.pos, 9)
+ s.pos += 2
+ self.assertTrue(s.find('0b1', start=s.pos))
+
+ def testFindBytes(self):
+ s = BitStream('0x010203040102ff')
+ self.assertFalse(s.find('0x05', bytealigned=True))
+ self.assertTrue(s.find('0x02', bytealigned=True))
+ self.assertEqual(s.read(16).hex, '0203')
+ self.assertTrue(s.find('0x02', start=s.bitpos, bytealigned=True))
+ s.read(1)
+ self.assertFalse(s.find('0x02', start=s.bitpos, bytealigned=True))
+
+ def testFindBytesAlignedCornerCases(self):
+ s = BitStream('0xff')
+ self.assertTrue(s.find(s))
+ self.assertFalse(s.find(BitStream(hex='0x12')))
+ self.assertFalse(s.find(BitStream(hex='0xffff')))
+
+ def testFindBytesBitpos(self):
+ s = BitStream(hex='0x1122334455')
+ s.pos = 2
+ s.find('0x66', bytealigned=True)
+ self.assertEqual(s.pos, 2)
+ s.pos = 38
+ s.find('0x66', bytealigned=True)
+ self.assertEqual(s.pos, 38)
+
+ def testFindByteAligned(self):
+ s = BitStream(hex='0x12345678')
+ self.assertTrue(s.find(BitStream(hex='0x56'), bytealigned=True))
+ self.assertEqual(s.bytepos, 2)
+ s.pos = 0
+ self.assertFalse(s.find(BitStream(hex='0x45'), bytealigned=True))
+ s = BitStream('0x1234')
+ s.find('0x1234')
+ self.assertTrue(s.find('0x1234'))
+ s += '0b111'
+ s.pos = 3
+ s.find('0b1', start=17, bytealigned=True)
+ self.assertFalse(s.find('0b1', start=17, bytealigned=True))
+ self.assertEqual(s.pos, 3)
+
+ def testFindByteAlignedWithOffset(self):
+ s = BitStream(hex='0x112233')[4:]
+ self.assertTrue(s.find(BitStream(hex='0x23')))
+
+ def testFindByteAlignedErrors(self):
+ s = BitStream(hex='0xffff')
+ self.assertRaises(ValueError, s.find, '')
+ self.assertRaises(ValueError, s.find, BitStream())
+
+
+class Rfind(unittest.TestCase):
+ def testRfind(self):
+ a = BitStream('0b001001001')
+ b = a.rfind('0b001')
+ self.assertEqual(b, (6,))
+ self.assertEqual(a.pos, 6)
+ big = BitStream(length=100000) + '0x12' + BitStream(length=10000)
+ found = big.rfind('0x12', bytealigned=True)
+ self.assertEqual(found, (100000,))
+ self.assertEqual(big.pos, 100000)
+
+ def testRfindByteAligned(self):
+ a = BitStream('0x8888')
+ b = a.rfind('0b1', bytealigned=True)
+ self.assertEqual(b, (8,))
+ self.assertEqual(a.pos, 8)
+
+ def testRfindStartbit(self):
+ a = BitStream('0x0000ffffff')
+ b = a.rfind('0x0000', start=1, bytealigned=True)
+ self.assertEqual(b, ())
+ self.assertEqual(a.pos, 0)
+ b = a.rfind('0x00', start=1, bytealigned=True)
+ self.assertEqual(b, (8,))
+ self.assertEqual(a.pos, 8)
+
+ def testRfindEndbit(self):
+ a = BitStream('0x000fff')
+ b = a.rfind('0b011', bytealigned=False, start=0, end=14)
+ self.assertEqual(bool(b), True)
+ b = a.rfind('0b011', False, 0, 13)
+ self.assertEqual(b, ())
+
+ def testRfindErrors(self):
+ a = BitStream('0x43234234')
+ self.assertRaises(ValueError, a.rfind, '', bytealigned=True)
+ self.assertRaises(ValueError, a.rfind, '0b1', start=-99, bytealigned=True)
+ self.assertRaises(ValueError, a.rfind, '0b1', end=33, bytealigned=True)
+ self.assertRaises(ValueError, a.rfind, '0b1', start=10, end=9, bytealigned=True)
+
+
+class Shift(unittest.TestCase):
+ def testShiftLeft(self):
+ s = BitStream('0b1010')
+ t = s << 1
+ self.assertEqual(s.bin, '1010')
+ self.assertEqual(t.bin, '0100')
+ t = t << 0
+ self.assertEqual(t, '0b0100')
+ t = t << 100
+ self.assertEqual(t.bin, '0000')
+
+ def testShiftLeftErrors(self):
+ s = BitStream()
+ self.assertRaises(ValueError, s.__lshift__, 1)
+ s = BitStream('0xf')
+ self.assertRaises(ValueError, s.__lshift__, -1)
+
+ def testShiftRight(self):
+ s = BitStream('0b1010')
+ t = s >> 1
+ self.assertEqual(s.bin, '1010')
+ self.assertEqual(t.bin, '0101')
+ q = s >> 0
+ self.assertEqual(q, '0b1010')
+ q.replace('0b1010', '')
+ s = s >> 100
+ self.assertEqual(s.bin, '0000')
+
+ def testShiftRightErrors(self):
+ s = BitStream()
+ self.assertRaises(ValueError, s.__rshift__, 1)
+ s = BitStream('0xf')
+ self.assertRaises(ValueError, s.__rshift__, -1)
+
+ def testShiftRightInPlace(self):
+ s = BitStream('0xffff')[4:12]
+ s >>= 1
+ self.assertEqual(s, '0b01111111')
+ s = BitStream('0b11011')
+ s >>= 2
+ self.assertEqual(s.bin, '00110')
+ s >>= 100000000000000
+ self.assertEqual(s.bin, '00000')
+ s = BitStream('0xff')
+ s >>= 1
+ self.assertEqual(s, '0x7f')
+ s >>= 0
+ self.assertEqual(s, '0x7f')
+
+ def testShiftRightInPlaceErrors(self):
+ s = BitStream()
+ self.assertRaises(ValueError, s.__irshift__, 1)
+ s += '0b11'
+ self.assertRaises(ValueError, s.__irshift__, -1)
+
+ def testShiftLeftInPlace(self):
+ s = BitStream('0xffff')
+ t = s[4:12]
+ t <<= 2
+ self.assertEqual(t, '0b11111100')
+ s = BitStream('0b11011')
+ s <<= 2
+ self.assertEqual(s.bin, '01100')
+ s <<= 100000000000000000000
+ self.assertEqual(s.bin, '00000')
+ s = BitStream('0xff')
+ s <<= 1
+ self.assertEqual(s, '0xfe')
+ s <<= 0
+ self.assertEqual(s, '0xfe')
+
+ def testShiftLeftInPlaceErrors(self):
+ s = BitStream()
+ self.assertRaises(ValueError, s.__ilshift__, 1)
+ s += '0b11'
+ self.assertRaises(ValueError, s.__ilshift__, -1)
+
+
+class Replace(unittest.TestCase):
+ def testReplace1(self):
+ a = BitStream('0b1')
+ n = a.replace('0b1', '0b0', bytealigned=True)
+ self.assertEqual(a.bin, '0')
+ self.assertEqual(n, 1)
+ n = a.replace('0b1', '0b0', bytealigned=True)
+ self.assertEqual(n, 0)
+
+ def testReplace2(self):
+ a = BitStream('0b00001111111')
+ n = a.replace('0b1', '0b0', bytealigned=True)
+ self.assertEqual(a.bin, '00001111011')
+ self.assertEqual(n, 1)
+ n = a.replace('0b1', '0b0', bytealigned=False)
+ self.assertEqual(a.bin, '00000000000')
+ self.assertEqual(n, 6)
+
+ def testReplace3(self):
+ a = BitStream('0b0')
+ n = a.replace('0b0', '0b110011111', bytealigned=True)
+ self.assertEqual(n, 1)
+ self.assertEqual(a.bin, '110011111')
+ n = a.replace('0b11', '', bytealigned=False)
+ self.assertEqual(n, 3)
+ self.assertEqual(a.bin, '001')
+
+ def testReplace4(self):
+ a = BitStream('0x00114723ef4732344700')
+ n = a.replace('0x47', '0x00', bytealigned=True)
+ self.assertEqual(n, 3)
+ self.assertEqual(a.hex, '00110023ef0032340000')
+ a.replace('0x00', '', bytealigned=True)
+ self.assertEqual(a.hex, '1123ef3234')
+ a.replace('0x11', '', start=1, bytealigned=True)
+ self.assertEqual(a.hex, '1123ef3234')
+ a.replace('0x11', '0xfff', end=7, bytealigned=True)
+ self.assertEqual(a.hex, '1123ef3234')
+ a.replace('0x11', '0xfff', end=8, bytealigned=True)
+ self.assertEqual(a.hex, 'fff23ef3234')
+
+ def testReplace5(self):
+ a = BitStream('0xab')
+ b = BitStream('0xcd')
+ c = BitStream('0xabef')
+ c.replace(a, b)
+ self.assertEqual(c, '0xcdef')
+ self.assertEqual(a, '0xab')
+ self.assertEqual(b, '0xcd')
+ a = BitStream('0x0011223344')
+ a.pos = 12
+ a.replace('0x11', '0xfff', bytealigned=True)
+ self.assertEqual(a.pos, 8)
+ self.assertEqual(a, '0x00fff223344')
+
+ def testReplaceWithSelf(self):
+ a = BitStream('0b11')
+ a.replace('0b1', a)
+ self.assertEqual(a, '0xf')
+ a.replace(a, a)
+ self.assertEqual(a, '0xf')
+
+ def testReplaceCount(self):
+ a = BitStream('0x223344223344223344')
+ n = a.replace('0x2', '0x0', count=0, bytealigned=True)
+ self.assertEqual(n, 0)
+ self.assertEqual(a.hex, '223344223344223344')
+ n = a.replace('0x2', '0x0', count=1, bytealigned=True)
+ self.assertEqual(n, 1)
+ self.assertEqual(a.hex, '023344223344223344')
+ n = a.replace('0x33', '', count=2, bytealigned=True)
+ self.assertEqual(n, 2)
+ self.assertEqual(a.hex, '02442244223344')
+ n = a.replace('0x44', '0x4444', count=1435, bytealigned=True)
+ self.assertEqual(n, 3)
+ self.assertEqual(a.hex, '02444422444422334444')
+
+ def testReplaceBitpos(self):
+ a = BitStream('0xff')
+ a.bitpos = 8
+ a.replace('0xff', '', bytealigned=True)
+ self.assertEqual(a.bitpos, 0)
+ a = BitStream('0b0011110001')
+ a.bitpos = 4
+ a.replace('0b1', '0b000')
+ self.assertEqual(a.bitpos, 8)
+ a = BitStream('0b1')
+ a.bitpos = 1
+ a.replace('0b1', '0b11111', bytealigned=True)
+ self.assertEqual(a.bitpos, 5)
+ a.replace('0b11', '0b0', False)
+ self.assertEqual(a.bitpos, 3)
+ a.append('0b00')
+ a.replace('0b00', '0xffff')
+ self.assertEqual(a.bitpos, 17)
+
+ def testReplaceErrors(self):
+ a = BitStream('0o123415')
+ self.assertRaises(ValueError, a.replace, '', '0o7', bytealigned=True)
+ self.assertRaises(ValueError, a.replace, '0b1', '0b1', start=-100, bytealigned=True)
+ self.assertRaises(ValueError, a.replace, '0b1', '0b1', end=19, bytealigned=True)
+
+
+class SliceAssignment(unittest.TestCase):
+
+ # TODO: Move this to another class
+ def testSetSlice(self):
+ a = BitStream()
+ a[0:0] = '0xabcdef'
+ self.assertEqual(a.bytepos, 3)
+ a[4:16] = ''
+ self.assertEqual(a, '0xaef')
+ self.assertEqual(a.bitpos, 4)
+ a[8:] = '0x00'
+ self.assertEqual(a, '0xae00')
+ self.assertEqual(a.bytepos, 2)
+ a += '0xf'
+ a[8:] = '0xe'
+ self.assertEqual(a, '0xaee')
+ self.assertEqual(a.bitpos, 12)
+ b = BitStream()
+ b[0:800] = '0xffee'
+ self.assertEqual(b, '0xffee')
+ b[4:48] = '0xeed123'
+ self.assertEqual(b, '0xfeed123')
+ b[-800:8] = '0x0000'
+ self.assertEqual(b, '0x0000ed123')
+ a = BitStream('0xabcde')
+ self.assertEqual(a[-100:-90], '')
+ self.assertEqual(a[-100:-16], '0xa')
+ a[-100:-16] = '0x0'
+ self.assertEqual(a, '0x0bcde')
+
+ def testInsertingUsingSetItem(self):
+ a = BitStream()
+ a[0:0] = '0xdeadbeef'
+ self.assertEqual(a, '0xdeadbeef')
+ self.assertEqual(a.bytepos, 4)
+ a[16:16] = '0xfeed'
+ self.assertEqual(a, '0xdeadfeedbeef')
+ self.assertEqual(a.bytepos, 4)
+ a[0:0] = '0xa'
+ self.assertEqual(a, '0xadeadfeedbeef')
+ self.assertEqual(a.bitpos, 4)
+ a.bytepos = 6
+ a[0:0] = '0xff'
+ self.assertEqual(a.bytepos, 1)
+ a[8:0] = '0x00000'
+ self.assertTrue(a.startswith('0xff00000adead'))
+
+ def testSliceAssignmentBitPos(self):
+ a = BitStream('int:64=-1')
+ a.pos = 64
+ a[0:8] = ''
+ self.assertEqual(a.pos, 0)
+ a.pos = 52
+ a[48:56] = '0x0000'
+ self.assertEqual(a.pos, 64)
+ a[10:10] = '0x0'
+ self.assertEqual(a.pos, 14)
+ a[56:68] = '0x000'
+ self.assertEqual(a.pos, 14)
+
+
+class Pack(unittest.TestCase):
+ def testPack1(self):
+ s = bitstring.pack('uint:6, bin, hex, int:6, se, ue, oct', 10, '0b110', 'ff', -1, -6, 6, '54')
+ t = BitStream('uint:6=10, 0b110, 0xff, int:6=-1, se=-6, ue=6, oct=54')
+ self.assertEqual(s, t)
+ self.assertRaises(bitstring.CreationError, pack, 'tomato', '0')
+ self.assertRaises(bitstring.CreationError, pack, 'uint', 12)
+ self.assertRaises(bitstring.CreationError, pack, 'hex', 'penguin')
+ self.assertRaises(bitstring.CreationError, pack, 'hex12', '0x12')
+
+ def testPackWithLiterals(self):
+ s = bitstring.pack('0xf')
+ self.assertEqual(s, '0xf')
+ self.assertTrue(type(s), BitStream)
+ s = pack('0b1')
+ self.assertEqual(s, '0b1')
+ s = pack('0o7')
+ self.assertEqual(s, '0o7')
+ s = pack('int:10=-1')
+ self.assertEqual(s, '0b1111111111')
+ s = pack('uint:10=1')
+ self.assertEqual(s, '0b0000000001')
+ s = pack('ue=12')
+ self.assertEqual(s.ue, 12)
+ s = pack('se=-12')
+ self.assertEqual(s.se, -12)
+ s = pack('bin=01')
+ self.assertEqual(s.bin, '01')
+ s = pack('hex=01')
+ self.assertEqual(s.hex, '01')
+ s = pack('oct=01')
+ self.assertEqual(s.oct, '01')
+
+ def testPackWithDict(self):
+ a = pack('uint:6=width, se=height', height=100, width=12)
+ w, h = a.unpack('uint:6, se')
+ self.assertEqual(w, 12)
+ self.assertEqual(h, 100)
+ d = {}
+ d['w'] = '0xf'
+ d['300'] = 423
+ d['e'] = '0b1101'
+ a = pack('int:100=300, bin=e, uint:12=300', **d)
+ x, y, z = a.unpack('int:100, bin, uint:12')
+ self.assertEqual(x, 423)
+ self.assertEqual(y, '1101')
+ self.assertEqual(z, 423)
+
+ def testPackWithDict2(self):
+ a = pack('int:5, bin:3=b, 0x3, bin=c, se=12', 10, b='0b111', c='0b1')
+ b = BitStream('int:5=10, 0b111, 0x3, 0b1, se=12')
+ self.assertEqual(a, b)
+ a = pack('bits:3=b', b=BitStream('0b101'))
+ self.assertEqual(a, '0b101')
+ a = pack('bits:24=b', b=BitStream('0x001122'))
+ self.assertEqual(a, '0x001122')
+
+ def testPackWithDict3(self):
+ s = pack('hex:4=e, hex:4=0xe, hex:4=e', e='f')
+ self.assertEqual(s, '0xfef')
+ s = pack('sep', sep='0b00')
+ self.assertEqual(s, '0b00')
+
+ def testPackWithDict4(self):
+ s = pack('hello', hello='0xf')
+ self.assertEqual(s, '0xf')
+ s = pack('x, y, x, y, x', x='0b10', y='uint:12=100')
+ t = BitStream('0b10, uint:12=100, 0b10, uint:12=100, 0b10')
+ self.assertEqual(s, t)
+ a = [1, 2, 3, 4, 5]
+ s = pack('int:8, div,' * 5, *a, **{'div': '0b1'})
+ t = BitStream('int:8=1, 0b1, int:8=2, 0b1, int:8=3, 0b1, int:8=4, 0b1, int:8=5, 0b1')
+ self.assertEqual(s, t)
+
+ def testPackWithLocals(self):
+ width = 352
+ height = 288
+ s = pack('uint:12=width, uint:12=height', **locals())
+ self.assertEqual(s, '0x160120')
+
+ def testPackWithLengthRestriction(self):
+ s = pack('bin:3', '0b000')
+ self.assertRaises(bitstring.CreationError, pack, 'bin:3', '0b0011')
+ self.assertRaises(bitstring.CreationError, pack, 'bin:3', '0b11')
+ self.assertRaises(bitstring.CreationError, pack, 'bin:3=0b0011')
+ self.assertRaises(bitstring.CreationError, pack, 'bin:3=0b11')
+
+ s = pack('hex:4', '0xf')
+ self.assertRaises(bitstring.CreationError, pack, 'hex:4', '0b111')
+ self.assertRaises(bitstring.CreationError, pack, 'hex:4', '0b11111')
+ self.assertRaises(bitstring.CreationError, pack, 'hex:8=0xf')
+
+ s = pack('oct:6', '0o77')
+ self.assertRaises(bitstring.CreationError, pack, 'oct:6', '0o1')
+ self.assertRaises(bitstring.CreationError, pack, 'oct:6', '0o111')
+ self.assertRaises(bitstring.CreationError, pack, 'oct:3', '0b1')
+ self.assertRaises(bitstring.CreationError, pack, 'oct:3=hello', hello='0o12')
+
+ s = pack('bits:3', BitStream('0b111'))
+ self.assertRaises(bitstring.CreationError, pack, 'bits:3', BitStream('0b11'))
+ self.assertRaises(bitstring.CreationError, pack, 'bits:3', BitStream('0b1111'))
+ self.assertRaises(bitstring.CreationError, pack, 'bits:12=b', b=BitStream('0b11'))
+
+ def testPackNull(self):
+ s = pack('')
+ self.assertFalse(s)
+ s = pack(',')
+ self.assertFalse(s)
+ s = pack(',,,,,0b1,,,,,,,,,,,,,0b1,,,,,,,,,,')
+ self.assertEqual(s, '0b11')
+ s = pack(',,uint:12,,bin:3,', 100, '100')
+ a, b = s.unpack(',,,uint:12,,,,bin:3,,,')
+ self.assertEqual(a, 100)
+ self.assertEqual(b, '100')
+
+ def testPackDefaultUint(self):
+ s = pack('10, 5', 1, 2)
+ a, b = s.unpack('10, 5')
+ self.assertEqual((a, b), (1, 2))
+ s = pack('10=150, 12=qee', qee=3)
+ self.assertEqual(s, 'uint:10=150, uint:12=3')
+ t = BitStream('100=5')
+ self.assertEqual(t, 'uint:100=5')
+
+ def testPackDefualtUintErrors(self):
+ self.assertRaises(bitstring.CreationError, BitStream, '5=-1')
+
+ def testPackingLongKeywordBitstring(self):
+ s = pack('bits=b', b=BitStream(128000))
+ self.assertEqual(s, BitStream(128000))
+
+ def testPackingWithListFormat(self):
+ f = ['bin', 'hex', 'uint:10']
+ a = pack(','.join(f), '00', '234', 100)
+ b = pack(f, '00', '234', 100)
+ self.assertEqual(a, b)
+
+
+class Unpack(unittest.TestCase):
+ def testUnpack1(self):
+ s = BitStream('uint:13=23, hex=e, bin=010, int:41=-554, 0o44332, se=-12, ue=4')
+ s.pos = 11
+ a, b, c, d, e, f, g = s.unpack('uint:13, hex:4, bin:3, int:41, oct:15, se, ue')
+ self.assertEqual(a, 23)
+ self.assertEqual(b, 'e')
+ self.assertEqual(c, '010')
+ self.assertEqual(d, -554)
+ self.assertEqual(e, '44332')
+ self.assertEqual(f, -12)
+ self.assertEqual(g, 4)
+ self.assertEqual(s.pos, 11)
+
+ def testUnpack2(self):
+ s = BitStream('0xff, 0b000, uint:12=100')
+ a, b, c = s.unpack('bits:8, bits, uint:12')
+ self.assertEqual(type(s), BitStream)
+ self.assertEqual(a, '0xff')
+ self.assertEqual(type(s), BitStream)
+ self.assertEqual(b, '0b000')
+ self.assertEqual(c, 100)
+ a, b = s.unpack(['bits:11', 'uint'])
+ self.assertEqual(a, '0xff, 0b000')
+ self.assertEqual(b, 100)
+
+ def testUnpackNull(self):
+ s = pack('0b1, , , 0xf,')
+ a, b = s.unpack('bin:1,,,hex:4,')
+ self.assertEqual(a, '1')
+ self.assertEqual(b, 'f')
+
+
+class FromFile(unittest.TestCase):
+ def testCreationFromFileOperations(self):
+ s = BitStream(filename='smalltestfile')
+ s.append('0xff')
+ self.assertEqual(s.hex, '0123456789abcdefff')
+
+ s = ConstBitStream(filename='smalltestfile')
+ t = BitStream('0xff') + s
+ self.assertEqual(t.hex, 'ff0123456789abcdef')
+
+ s = BitStream(filename='smalltestfile')
+ del s[:1]
+ self.assertEqual((BitStream('0b0') + s).hex, '0123456789abcdef')
+
+ s = BitStream(filename='smalltestfile')
+ del s[:7 * 8]
+ self.assertEqual(s.hex, 'ef')
+
+ s = BitStream(filename='smalltestfile')
+ s.insert('0xc', 4)
+ self.assertEqual(s.hex, '0c123456789abcdef')
+
+ s = BitStream(filename='smalltestfile')
+ s.prepend('0xf')
+ self.assertEqual(s.hex, 'f0123456789abcdef')
+
+ s = BitStream(filename='smalltestfile')
+ s.overwrite('0xaaa', 12)
+ self.assertEqual(s.hex, '012aaa6789abcdef')
+
+ s = BitStream(filename='smalltestfile')
+ s.reverse()
+ self.assertEqual(s.hex, 'f7b3d591e6a2c480')
+
+ s = BitStream(filename='smalltestfile')
+ del s[-60:]
+ self.assertEqual(s.hex, '0')
+
+ s = BitStream(filename='smalltestfile')
+ del s[:60]
+ self.assertEqual(s.hex, 'f')
+
+ def testFileProperties(self):
+ s = ConstBitStream(filename='smalltestfile')
+ self.assertEqual(s.hex, '0123456789abcdef')
+ self.assertEqual(s.uint, 81985529216486895)
+ self.assertEqual(s.int, 81985529216486895)
+ self.assertEqual(s.bin, '0000000100100011010001010110011110001001101010111100110111101111')
+ self.assertEqual(s[:-1].oct, '002215053170465363367')
+ s.bitpos = 0
+ self.assertEqual(s.read('se'), -72)
+ s.bitpos = 0
+ self.assertEqual(s.read('ue'), 144)
+ self.assertEqual(s.bytes, b'\x01\x23\x45\x67\x89\xab\xcd\xef')
+ self.assertEqual(s.tobytes(), b'\x01\x23\x45\x67\x89\xab\xcd\xef')
+
+ def testCreationFromFileWithLength(self):
+ s = ConstBitStream(filename='test.m1v', length=32)
+ self.assertEqual(s.length, 32)
+ self.assertEqual(s.hex, '000001b3')
+ s = ConstBitStream(filename='test.m1v', length=0)
+ self.assertFalse(s)
+ self.assertRaises(bitstring.CreationError, BitStream, filename='smalltestfile', length=65)
+ self.assertRaises(bitstring.CreationError, ConstBitStream, filename='smalltestfile', length=64, offset=1)
+ # self.assertRaises(bitstring.CreationError, ConstBitStream, filename='smalltestfile', offset=65)
+ f = open('smalltestfile', 'rb')
+ # self.assertRaises(bitstring.CreationError, ConstBitStream, auto=f, offset=65)
+ self.assertRaises(bitstring.CreationError, ConstBitStream, auto=f, length=65)
+ self.assertRaises(bitstring.CreationError, ConstBitStream, auto=f, offset=60, length=5)
+
+ def testCreationFromFileWithOffset(self):
+ a = BitStream(filename='test.m1v', offset=4)
+ self.assertEqual(a.peek(4 * 8).hex, '00001b31')
+ b = BitStream(filename='test.m1v', offset=28)
+ self.assertEqual(b.peek(8).hex, '31')
+
+ def testFileSlices(self):
+ s = BitStream(filename='smalltestfile')
+ self.assertEqual(s[-16:].hex, 'cdef')
+
+ def testCreataionFromFileErrors(self):
+ self.assertRaises(IOError, BitStream, filename='Idonotexist')
+
+ def testFindInFile(self):
+ s = BitStream(filename='test.m1v')
+ self.assertTrue(s.find('0x160120'))
+ self.assertEqual(s.bytepos, 4)
+ s3 = s.read(3 * 8)
+ self.assertEqual(s3.hex, '160120')
+ s.bytepos = 0
+ self.assertTrue(s._pos == 0)
+ self.assertTrue(s.find('0x0001b2'))
+ self.assertEqual(s.bytepos, 13)
+
+ def testHexFromFile(self):
+ s = BitStream(filename='test.m1v')
+ self.assertEqual(s[0:32].hex, '000001b3')
+ self.assertEqual(s[-32:].hex, '000001b7')
+ s.hex = '0x11'
+ self.assertEqual(s.hex, '11')
+
+ def testFileOperations(self):
+ s1 = BitStream(filename='test.m1v')
+ s2 = BitStream(filename='test.m1v')
+ self.assertEqual(s1.read(32).hex, '000001b3')
+ self.assertEqual(s2.read(32).hex, '000001b3')
+ s1.bytepos += 4
+ self.assertEqual(s1.read(8).hex, '02')
+ self.assertEqual(s2.read(5 * 8).hex, '1601208302')
+ s1.pos = s1.len
+ try:
+ s1.pos += 1
+ self.assertTrue(False)
+ except ValueError:
+ pass
+
+ def testFileBitGetting(self):
+ s = ConstBitStream(filename='smalltestfile', offset=16, length=8) # 0x45
+ b = s[1]
+ self.assertTrue(b)
+ b = s.any(0, [-1, -2, -3])
+ self.assertTrue(b)
+ b = s.all(0, [0, 1, 2])
+ self.assertFalse(b)
+
+ def testVeryLargeFiles(self):
+ # This uses an 11GB file which isn't distributed for obvious reasons
+ # and so this test won't work for anyone except me!
+ try:
+ s = ConstBitStream(filename='11GB.mkv')
+ except IOError:
+ return
+ self.assertEqual(s.len, 11743020505 * 8)
+ self.assertEqual(s[1000000000:1000000100].hex, 'bdef7335d4545f680d669ce24')
+ self.assertEqual(s[-4::8].hex, 'bbebf7a1')
+
+
+class CreationErrors(unittest.TestCase):
+ def testIncorrectBinAssignment(self):
+ s = BitStream()
+ self.assertRaises(bitstring.CreationError, s._setbin_safe, '0010020')
+
+ def testIncorrectHexAssignment(self):
+ s = BitStream()
+ self.assertRaises(bitstring.CreationError, s._sethex, '0xabcdefg')
+
+
+class Length(unittest.TestCase):
+ def testLengthZero(self):
+ self.assertEqual(BitStream('').len, 0)
+
+ def testLength(self):
+ self.assertEqual(BitStream('0x80').len, 8)
+
+ def testLengthErrors(self):
+ #TODO: Lots of new checks, for various inits which now disallow length and offset
+ pass
+ #self.assertRaises(ValueError, BitStream, bin='111', length=-1)
+ #self.assertRaises(ValueError, BitStream, bin='111', length=4)
+
+ def testOffsetLengthError(self):
+ self.assertRaises(bitstring.CreationError, BitStream, hex='0xffff', offset=-1)
+
+
+class SimpleConversions(unittest.TestCase):
+ def testConvertToUint(self):
+ self.assertEqual(BitStream('0x10').uint, 16)
+ self.assertEqual(BitStream('0b000111').uint, 7)
+
+ def testConvertToInt(self):
+ self.assertEqual(BitStream('0x10').int, 16)
+ self.assertEqual(BitStream('0b11110').int, -2)
+
+ def testConvertToHex(self):
+ self.assertEqual(BitStream(bytes=b'\x00\x12\x23\xff').hex, '001223ff')
+ s = BitStream('0b11111')
+ self.assertRaises(bitstring.InterpretError, s._gethex)
+
+
+class Empty(unittest.TestCase):
+ def testEmptyBitstring(self):
+ s = BitStream()
+ self.assertRaises(bitstring.ReadError, s.read, 1)
+ self.assertEqual(s.bin, '')
+ self.assertEqual(s.hex, '')
+ self.assertRaises(bitstring.InterpretError, s._getint)
+ self.assertRaises(bitstring.InterpretError, s._getuint)
+ self.assertFalse(s)
+
+ def testNonEmptyBitStream(self):
+ s = BitStream(bin='0')
+ self.assertFalse(not s.len)
+
+
+class Position(unittest.TestCase):
+ def testBitPosition(self):
+ s = BitStream(bytes=b'\x00\x00\x00')
+ self.assertEqual(s.bitpos, 0)
+ s.read(5)
+ self.assertEqual(s.pos, 5)
+ s.pos = s.len
+ self.assertRaises(bitstring.ReadError, s.read, 1)
+
+ def testBytePosition(self):
+ s = BitStream(bytes=b'\x00\x00\x00')
+ self.assertEqual(s.bytepos, 0)
+ s.read(10)
+ self.assertRaises(bitstring.ByteAlignError, s._getbytepos)
+ s.read(6)
+ self.assertEqual(s.bytepos, 2)
+
+ def testSeekToBit(self):
+ s = BitStream(bytes=b'\x00\x00\x00\x00\x00\x00')
+ s.bitpos = 0
+ self.assertEqual(s.bitpos, 0)
+ self.assertRaises(ValueError, s._setbitpos, -1)
+ self.assertRaises(ValueError, s._setbitpos, 6 * 8 + 1)
+ s.bitpos = 6 * 8
+ self.assertEqual(s.bitpos, 6 * 8)
+
+ def testSeekToByte(self):
+ s = BitStream(bytes=b'\x00\x00\x00\x00\x00\xab')
+ s.bytepos = 5
+ self.assertEqual(s.read(8).hex, 'ab')
+
+ def testAdvanceBitsAndBytes(self):
+ s = BitStream(bytes=b'\x00\x00\x00\x00\x00\x00\x00\x00')
+ s.pos += 5
+ self.assertEqual(s.pos, 5)
+ s.bitpos += 16
+ self.assertEqual(s.pos, 2 * 8 + 5)
+ s.pos -= 8
+ self.assertEqual(s.pos, 8 + 5)
+
+ def testRetreatBitsAndBytes(self):
+ a = BitStream(length=100)
+ a.pos = 80
+ a.bytepos -= 5
+ self.assertEqual(a.bytepos, 5)
+ a.pos -= 5
+ self.assertEqual(a.pos, 35)
+
+
+class Offset(unittest.TestCase):
+ def testOffset1(self):
+ s = BitStream(bytes=b'\x00\x1b\x3f', offset=4)
+ self.assertEqual(s.read(8).bin, '00000001')
+ self.assertEqual(s.length, 20)
+
+ def testOffset2(self):
+ s1 = BitStream(bytes=b'\xf1\x02\x04')
+ s2 = BitStream(bytes=b'\xf1\x02\x04', length=23)
+ for i in [1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1, 0, 7, 3, 5, 1, 4]:
+ s1._datastore = offsetcopy(s1._datastore, i)
+ self.assertEqual(s1.hex, 'f10204')
+ s2._datastore = offsetcopy(s2._datastore, i)
+ self.assertEqual(s2.bin, '11110001000000100000010')
+
+
+class Append(unittest.TestCase):
+ def testAppend(self):
+ s1 = BitStream('0b00000')
+ s1.append(BitStream(bool=True))
+ self.assertEqual(s1.bin, '000001')
+ self.assertEqual((BitStream('0x0102') + BitStream('0x0304')).hex, '01020304')
+
+ def testAppendSameBitstring(self):
+ s1 = BitStream('0xf0')[:6]
+ s1.append(s1)
+ self.assertEqual(s1.bin, '111100111100')
+
+ def testAppendWithOffset(self):
+ s = BitStream(bytes=b'\x28\x28', offset=1)
+ s.append('0b0')
+ self.assertEqual(s.hex, '5050')
+
+
+class ByteAlign(unittest.TestCase):
+ def testByteAlign(self):
+ s = BitStream(hex='0001ff23')
+ s.bytealign()
+ self.assertEqual(s.bytepos, 0)
+ s.pos += 11
+ s.bytealign()
+ self.assertEqual(s.bytepos, 2)
+ s.pos -= 10
+ s.bytealign()
+ self.assertEqual(s.bytepos, 1)
+
+ def testByteAlignWithOffset(self):
+ s = BitStream(hex='0112233')
+ s._datastore = offsetcopy(s._datastore, 3)
+ bitstoalign = s.bytealign()
+ self.assertEqual(bitstoalign, 0)
+ self.assertEqual(s.read(5).bin, '00001')
+
+ def testInsertByteAligned(self):
+ s = BitStream('0x0011')
+ s.insert(BitStream('0x22'), 8)
+ self.assertEqual(s.hex, '002211')
+ s = BitStream(0)
+ s.insert(BitStream(bin='101'), 0)
+ self.assertEqual(s.bin, '101')
+
+
+class Truncate(unittest.TestCase):
+ def testTruncateStart(self):
+ s = BitStream('0b1')
+ del s[:1]
+ self.assertFalse(s)
+ s = BitStream(hex='1234')
+ self.assertEqual(s.hex, '1234')
+ del s[:4]
+ self.assertEqual(s.hex, '234')
+ del s[:9]
+ self.assertEqual(s.bin, '100')
+ del s[:2]
+ self.assertEqual(s.bin, '0')
+ self.assertEqual(s.len, 1)
+ del s[:1]
+ self.assertFalse(s)
+
+ def testTruncateEnd(self):
+ s = BitStream('0b1')
+ del s[-1:]
+ self.assertFalse(s)
+ s = BitStream(bytes=b'\x12\x34')
+ self.assertEqual(s.hex, '1234')
+ del s[-4:]
+ self.assertEqual(s.hex, '123')
+ del s[-9:]
+ self.assertEqual(s.bin, '000')
+ del s[-3:]
+ self.assertFalse(s)
+ s = BitStream('0b001')
+ del s[:2]
+ del s[-1:]
+ self.assertFalse(s)
+
+
+class Slice(unittest.TestCase):
+ def testByteAlignedSlice(self):
+ s = BitStream(hex='0x123456')
+ self.assertEqual(s[8:16].hex, '34')
+ s = s[8:24]
+ self.assertEqual(s.len, 16)
+ self.assertEqual(s.hex, '3456')
+ s = s[0:8]
+ self.assertEqual(s.hex, '34')
+ s.hex = '0x123456'
+ self.assertEqual(s[8:24][0:8].hex, '34')
+
+ def testSlice(self):
+ s = BitStream(bin='000001111100000')
+ s1 = s[0:5]
+ s2 = s[5:10]
+ s3 = s[10:15]
+ self.assertEqual(s1.bin, '00000')
+ self.assertEqual(s2.bin, '11111')
+ self.assertEqual(s3.bin, '00000')
+
+
+class Insert(unittest.TestCase):
+ def testInsert(self):
+ s1 = BitStream(hex='0x123456')
+ s2 = BitStream(hex='0xff')
+ s1.bytepos = 1
+ s1.insert(s2)
+ self.assertEqual(s1.bytepos, 2)
+ self.assertEqual(s1.hex, '12ff3456')
+ s1.insert('0xee', 24)
+ self.assertEqual(s1.hex, '12ff34ee56')
+ self.assertEqual(s1.bitpos, 32)
+ self.assertRaises(ValueError, s1.insert, '0b1', -1000)
+ self.assertRaises(ValueError, s1.insert, '0b1', 1000)
+
+ def testInsertNull(self):
+ s = BitStream(hex='0x123').insert(BitStream(), 3)
+ self.assertEqual(s.hex, '123')
+
+ def testInsertBits(self):
+ one = BitStream(bin='1')
+ zero = BitStream(bin='0')
+ s = BitStream(bin='00')
+ s.insert(one, 0)
+ self.assertEqual(s.bin, '100')
+ s.insert(zero, 0)
+ self.assertEqual(s.bin, '0100')
+ s.insert(one, s.len)
+ self.assertEqual(s.bin, '01001')
+ s.insert(s, 2)
+ self.assertEqual(s.bin, '0101001001')
+
+
+class Resetting(unittest.TestCase):
+ def testSetHex(self):
+ s = BitStream()
+ s.hex = '0'
+ self.assertEqual(s.hex, '0')
+ s.hex = '0x010203045'
+ self.assertEqual(s.hex, '010203045')
+ self.assertRaises(bitstring.CreationError, s._sethex, '0x002g')
+
+ def testSetBin(self):
+ s = BitStream(bin="000101101")
+ self.assertEqual(s.bin, '000101101')
+ self.assertEqual(s.len, 9)
+ s.bin = '0'
+ self.assertEqual(s.bin, '0')
+ self.assertEqual(s.len, 1)
+
+ def testSetEmptyBin(self):
+ s = BitStream(hex='0x000001b3')
+ s.bin = ''
+ self.assertEqual(s.len, 0)
+ self.assertEqual(s.bin, '')
+
+ def testSetInvalidBin(self):
+ s = BitStream()
+ self.assertRaises(bitstring.CreationError, s._setbin_safe, '00102')
+
+
+class Overwriting(unittest.TestCase):
+ def testOverwriteBit(self):
+ s = BitStream(bin='0')
+ s.overwrite(BitStream(bin='1'), 0)
+ self.assertEqual(s.bin, '1')
+
+ def testOverwriteLimits(self):
+ s = BitStream(bin='0b11111')
+ s.overwrite(BitStream(bin='000'), 0)
+ self.assertEqual(s.bin, '00011')
+ s.overwrite('0b000', 2)
+ self.assertEqual(s.bin, '00000')
+
+ def testOverwriteNull(self):
+ s = BitStream(hex='342563fedec')
+ s2 = BitStream(s)
+ s.overwrite(BitStream(bin=''), 23)
+ self.assertEqual(s.bin, s2.bin)
+
+ def testOverwritePosition(self):
+ s1 = BitStream(hex='0123456')
+ s2 = BitStream(hex='ff')
+ s1.bytepos = 1
+ s1.overwrite(s2)
+ self.assertEqual((s1.hex, s1.bytepos), ('01ff456', 2))
+ s1.overwrite('0xff', 0)
+ self.assertEqual((s1.hex, s1.bytepos), ('ffff456', 1))
+
+ def testOverwriteWithSelf(self):
+ s = BitStream('0x123')
+ s.overwrite(s)
+ self.assertEqual(s, '0x123')
+
+
+class Split(unittest.TestCase):
+ def testSplitByteAlignedCornerCases(self):
+ s = BitStream()
+ bsl = s.split(BitStream(hex='0xff'))
+ self.assertEqual(next(bsl).hex, '')
+ self.assertRaises(StopIteration, next, bsl)
+ s = BitStream(hex='aabbcceeddff')
+ delimiter = BitStream()
+ bsl = s.split(delimiter)
+ self.assertRaises(ValueError, next, bsl)
+ delimiter = BitStream(hex='11')
+ bsl = s.split(delimiter)
+ self.assertEqual(next(bsl).hex, s.hex)
+
+ def testSplitByteAligned(self):
+ s = BitStream(hex='0x1234aa1234bbcc1234ffff')
+ delimiter = BitStream(hex='1234')
+ bsl = s.split(delimiter)
+ self.assertEqual([b.hex for b in bsl], ['', '1234aa', '1234bbcc', '1234ffff'])
+ self.assertEqual(s.pos, 0)
+
+ def testSplitByteAlignedWithIntialBytes(self):
+ s = BitStream(hex='aa471234fedc43 47112233 47 4723 472314')
+ delimiter = BitStream(hex='47')
+ s.find(delimiter)
+ self.assertEqual(s.bytepos, 1)
+ bsl = s.split(delimiter, start=0)
+ self.assertEqual([b.hex for b in bsl], ['aa', '471234fedc43', '47112233',
+ '47', '4723', '472314'])
+ self.assertEqual(s.bytepos, 1)
+
+ def testSplitByteAlignedWithOverlappingDelimiter(self):
+ s = BitStream(hex='aaffaaffaaffaaffaaff')
+ bsl = s.split(BitStream(hex='aaffaa'))
+ self.assertEqual([b.hex for b in bsl], ['', 'aaffaaff', 'aaffaaffaaff'])
+
+
+class Adding(unittest.TestCase):
+ def testAdding(self):
+ s1 = BitStream(hex='0x0102')
+ s2 = BitStream(hex='0x0304')
+ s3 = s1 + s2
+ self.assertEqual(s1.hex, '0102')
+ self.assertEqual(s2.hex, '0304')
+ self.assertEqual(s3.hex, '01020304')
+ s3 += s1
+ self.assertEqual(s3.hex, '010203040102')
+ self.assertEqual(s2[9:16].bin, '0000100')
+ self.assertEqual(s1[0:9].bin, '000000010')
+ s4 = BitStream(bin='000000010') +\
+ BitStream(bin='0000100')
+ self.assertEqual(s4.bin, '0000000100000100')
+ s2p = s2[9:16]
+ s1p = s1[0:9]
+ s5p = s1p + s2p
+ s5 = s1[0:9] + s2[9:16]
+ self.assertEqual(s5.bin, '0000000100000100')
+
+ def testMoreAdding(self):
+ s = BitStream(bin='00') + BitStream(bin='') + BitStream(bin='11')
+ self.assertEqual(s.bin, '0011')
+ s = '0b01'
+ s += BitStream('0b11')
+ self.assertEqual(s.bin, '0111')
+ s = BitStream('0x00')
+ t = BitStream('0x11')
+ s += t
+ self.assertEqual(s.hex, '0011')
+ self.assertEqual(t.hex, '11')
+ s += s
+ self.assertEqual(s.hex, '00110011')
+
+ def testRadd(self):
+ s = '0xff' + BitStream('0xee')
+ self.assertEqual(s.hex, 'ffee')
+
+
+ def testTruncateAsserts(self):
+ s = BitStream('0x001122')
+ s.bytepos = 2
+ del s[-s.len:]
+ self.assertEqual(s.bytepos, 0)
+ s.append('0x00')
+ s.append('0x1122')
+ s.bytepos = 2
+ del s[:s.len]
+ self.assertEqual(s.bytepos, 0)
+ s.append('0x00')
+
+ def testOverwriteErrors(self):
+ s = BitStream(bin='11111')
+ self.assertRaises(ValueError, s.overwrite, BitStream(bin='1'), -10)
+ self.assertRaises(ValueError, s.overwrite, BitStream(bin='1'), 6)
+ self.assertRaises(ValueError, s.overwrite, BitStream(bin='11111'), 1)
+
+ def testDeleteBits(self):
+ s = BitStream(bin='000111100000')
+ s.bitpos = 4
+ del s[4:8]
+ self.assertEqual(s.bin, '00010000')
+ del s[4:1004]
+ self.assertTrue(s.bin, '0001')
+
+ def testDeleteBitsWithPosition(self):
+ s = BitStream(bin='000111100000')
+ del s[4:8]
+ self.assertEqual(s.bin, '00010000')
+
+ def testDeleteBytes(self):
+ s = BitStream('0x00112233')
+ del s[8:8]
+ self.assertEqual(s.hex, '00112233')
+ self.assertEqual(s.pos, 0)
+ del s[8:16]
+ self.assertEqual(s.hex, '002233')
+ self.assertEqual(s.bytepos, 0)
+ del s[:24]
+ self.assertFalse(s)
+ self.assertEqual(s.pos, 0)
+
+ def testGetItemWithPositivePosition(self):
+ s = BitStream(bin='0b1011')
+ self.assertEqual(s[0], True)
+ self.assertEqual(s[1], False)
+ self.assertEqual(s[2], True)
+ self.assertEqual(s[3], True)
+ self.assertRaises(IndexError, s.__getitem__, 4)
+
+ def testGetItemWithNegativePosition(self):
+ s = BitStream(bin='1011')
+ self.assertEqual(s[-1], True)
+ self.assertEqual(s[-2], True)
+ self.assertEqual(s[-3], False)
+ self.assertEqual(s[-4], True)
+ self.assertRaises(IndexError, s.__getitem__, -5)
+
+ def testSlicing(self):
+ s = ConstBitStream(hex='0123456789')
+ self.assertEqual(s[0:8].hex, '01')
+ self.assertFalse(s[0:0])
+ self.assertFalse(s[23:20])
+ self.assertEqual(s[8:12].bin, '0010')
+ self.assertEqual(s[32:80], '0x89')
+
+ def testNegativeSlicing(self):
+ s = ConstBitStream(hex='012345678')
+ self.assertEqual(s[:-8].hex, '0123456')
+ self.assertEqual(s[-16:-8].hex, '56')
+ self.assertEqual(s[-24:].hex, '345678')
+ self.assertEqual(s[-1000:-24], '0x012')
+
+ def testLen(self):
+ s = BitStream()
+ self.assertEqual(len(s), 0)
+ s.append(BitStream(bin='001'))
+ self.assertEqual(len(s), 3)
+
+ def testJoin(self):
+ s1 = BitStream(bin='0')
+ s2 = BitStream(bin='1')
+ s3 = BitStream(bin='000')
+ s4 = BitStream(bin='111')
+ strings = [s1, s2, s1, s3, s4]
+ s = BitStream().join(strings)
+ self.assertEqual(s.bin, '010000111')
+
+ def testJoin2(self):
+ s1 = BitStream(hex='00112233445566778899aabbccddeeff')
+ s2 = BitStream(bin='0b000011')
+ bsl = [s1[0:32], s1[4:12], s2, s2, s2, s2]
+ s = ConstBitStream().join(bsl)
+ self.assertEqual(s.hex, '00112233010c30c3')
+
+ bsl = [BitStream(uint=j, length=12) for j in range(10) for i in range(10)]
+ s = BitStream().join(bsl)
+ self.assertEqual(s.length, 1200)
+
+
+ def testPos(self):
+ s = BitStream(bin='1')
+ self.assertEqual(s.bitpos, 0)
+ s.read(1)
+ self.assertEqual(s.bitpos, 1)
+
+ def testWritingData(self):
+ strings = [BitStream(bin=x) for x in ['0', '001', '0011010010', '010010', '1011']]
+ s = BitStream().join(strings)
+ s2 = BitStream(bytes=s.bytes)
+ self.assertEqual(s2.bin, '000100110100100100101011')
+ s2.append(BitStream(bin='1'))
+ s3 = BitStream(bytes=s2.tobytes())
+ self.assertEqual(s3.bin, '00010011010010010010101110000000')
+
+ def testWritingDataWithOffsets(self):
+ s1 = BitStream(bytes=b'\x10')
+ s2 = BitStream(bytes=b'\x08\x00', length=8, offset=1)
+ s3 = BitStream(bytes=b'\x04\x00', length=8, offset=2)
+ self.assertTrue(s1 == s2)
+ self.assertTrue(s2 == s3)
+ self.assertTrue(s1.bytes == s2.bytes)
+ self.assertTrue(s2.bytes == s3.bytes)
+
+ def testVariousThings1(self):
+ hexes = ['12345678', '87654321', 'ffffffffff', 'ed', '12ec']
+ bins = ['001010', '1101011', '0010000100101110110110', '11', '011']
+ bsl = []
+ for (hex, bin) in list(zip(hexes, bins)) * 5:
+ bsl.append(BitStream(hex=hex))
+ bsl.append(BitStream(bin=bin))
+ s = BitStream().join(bsl)
+ for (hex, bin) in list(zip(hexes, bins)) * 5:
+ h = s.read(4 * len(hex))
+ b = s.read(len(bin))
+ self.assertEqual(h.hex, hex)
+ self.assertEqual(b.bin, bin)
+
+ def testVariousThings2(self):
+ s1 = BitStream(hex="0x1f08")[:13]
+ self.assertEqual(s1.bin, '0001111100001')
+ s2 = BitStream(bin='0101')
+ self.assertEqual(s2.bin, '0101')
+ s1.append(s2)
+ self.assertEqual(s1.length, 17)
+ self.assertEqual(s1.bin, '00011111000010101')
+ s1 = s1[3:8]
+ self.assertEqual(s1.bin, '11111')
+
+ def testVariousThings3(self):
+ s1 = BitStream(hex='0x012480ff')[2:27]
+ s2 = s1 + s1
+ self.assertEqual(s2.length, 50)
+ s3 = s2[0:25]
+ s4 = s2[25:50]
+ self.assertEqual(s3.bin, s4.bin)
+
+ def testPeekBit(self):
+ s = BitStream(bin='01')
+ self.assertEqual(s.peek(1), [0])
+ self.assertEqual(s.peek(1), [0])
+ self.assertEqual(s.read(1), [0])
+ self.assertEqual(s.peek(1), [1])
+ self.assertEqual(s.peek(1), [1])
+
+ s = BitStream(bytes=b'\x1f', offset=3)
+ self.assertEqual(s.len, 5)
+ self.assertEqual(s.peek(5).bin, '11111')
+ self.assertEqual(s.peek(5).bin, '11111')
+ s.pos += 1
+ self.assertRaises(bitstring.ReadError, s.peek, 5)
+
+ s = BitStream(hex='001122334455')
+ self.assertEqual(s.peek(8).hex, '00')
+ self.assertEqual(s.read(8).hex, '00')
+ s.pos += 33
+ self.assertRaises(bitstring.ReadError, s.peek, 8)
+
+ s = BitStream(hex='001122334455')
+ self.assertEqual(s.peek(8 * 2).hex, '0011')
+ self.assertEqual(s.read(8 * 3).hex, '001122')
+ self.assertEqual(s.peek(8 * 3).hex, '334455')
+ self.assertRaises(bitstring.ReadError, s.peek, 25)
+
+ def testAdvanceBit(self):
+ s = BitStream(hex='0xff')
+ s.bitpos = 6
+ s.pos += 1
+ self.assertEqual(s.bitpos, 7)
+ s.bitpos += 1
+ try:
+ s.pos += 1
+ self.assertTrue(False)
+ except ValueError:
+ pass
+
+ def testAdvanceByte(self):
+ s = BitStream(hex='0x010203')
+ s.bytepos += 1
+ self.assertEqual(s.bytepos, 1)
+ s.bytepos += 1
+ self.assertEqual(s.bytepos, 2)
+ s.bytepos += 1
+ try:
+ s.bytepos += 1
+ self.assertTrue(False)
+ except ValueError:
+ pass
+
+ def testRetreatBit(self):
+ s = BitStream(hex='0xff')
+ try:
+ s.pos -= 1
+ self.assertTrue(False)
+ except ValueError:
+ pass
+ s.pos = 5
+ s.pos -= 1
+ self.assertEqual(s.pos, 4)
+
+ def testRetreatByte(self):
+ s = BitStream(hex='0x010203')
+ try:
+ s.bytepos -= 1
+ self.assertTrue(False)
+ except ValueError:
+ pass
+ s.bytepos = 3
+ s.bytepos -= 1
+ self.assertEqual(s.bytepos, 2)
+ self.assertEqual(s.read(8).hex, '03')
+
+ def testCreationByAuto(self):
+ s = BitStream('0xff')
+ self.assertEqual(s.hex, 'ff')
+ s = BitStream('0b00011')
+ self.assertEqual(s.bin, '00011')
+ self.assertRaises(bitstring.CreationError, BitStream, 'hello')
+ s1 = BitStream(bytes=b'\xf5', length=3, offset=5)
+ s2 = BitStream(s1, length=1, offset=1)
+ self.assertEqual(s2, '0b0')
+ s = BitStream(bytes=b'\xff', offset=2)
+ t = BitStream(s, offset=2)
+ self.assertEqual(t, '0b1111')
+ self.assertRaises(TypeError, BitStream, auto=1.2)
+
+ def testCreationByAuto2(self):
+ s = BitStream('bin=001')
+ self.assertEqual(s.bin, '001')
+ s = BitStream('oct=0o007')
+ self.assertEqual(s.oct, '007')
+ s = BitStream('hex=123abc')
+ self.assertEqual(s, '0x123abc')
+
+ s = BitStream('bin:2=01')
+ self.assertEqual(s, '0b01')
+ for s in ['bin:1=01', 'bits:4=0b1', 'oct:3=000', 'hex:4=0x1234']:
+ self.assertRaises(bitstring.CreationError, BitStream, s)
+
+ def testInsertUsingAuto(self):
+ s = BitStream('0xff')
+ s.insert('0x00', 4)
+ self.assertEqual(s.hex, 'f00f')
+ self.assertRaises(ValueError, s.insert, 'ff')
+
+ def testOverwriteUsingAuto(self):
+ s = BitStream('0x0110')
+ s.overwrite('0b1')
+ self.assertEqual(s.hex, '8110')
+ s.overwrite('')
+ self.assertEqual(s.hex, '8110')
+ self.assertRaises(ValueError, s.overwrite, '0bf')
+
+ def testFindUsingAuto(self):
+ s = BitStream('0b000000010100011000')
+ self.assertTrue(s.find('0b101'))
+ self.assertEqual(s.pos, 7)
+
+ def testFindbytealignedUsingAuto(self):
+ s = BitStream('0x00004700')
+ self.assertTrue(s.find('0b01000111', bytealigned=True))
+ self.assertEqual(s.bytepos, 2)
+
+ def testAppendUsingAuto(self):
+ s = BitStream('0b000')
+ s.append('0b111')
+ self.assertEqual(s.bin, '000111')
+ s.append('0b0')
+ self.assertEqual(s.bin, '0001110')
+
+ def testSplitByteAlignedUsingAuto(self):
+ s = BitStream('0x000143563200015533000123')
+ sections = s.split('0x0001')
+ self.assertEqual(next(sections).hex, '')
+ self.assertEqual(next(sections).hex, '0001435632')
+ self.assertEqual(next(sections).hex, '00015533')
+ self.assertEqual(next(sections).hex, '000123')
+ self.assertRaises(StopIteration, next, sections)
+
+ def testSplitByteAlignedWithSelf(self):
+ s = BitStream('0x1234')
+ sections = s.split(s)
+ self.assertEqual(next(sections).hex, '')
+ self.assertEqual(next(sections).hex, '1234')
+ self.assertRaises(StopIteration, next, sections)
+
+ def testPrepend(self):
+ s = BitStream('0b000')
+ s.prepend('0b11')
+ self.assertEqual(s.bin, '11000')
+ s.prepend(s)
+ self.assertEqual(s.bin, '1100011000')
+ s.prepend('')
+ self.assertEqual(s.bin, '1100011000')
+
+ def testNullSlice(self):
+ s = BitStream('0x111')
+ t = s[1:1]
+ self.assertEqual(t._datastore.bytelength, 0)
+
+ def testMultipleAutos(self):
+ s = BitStream('0xa')
+ s.prepend('0xf')
+ s.append('0xb')
+ self.assertEqual(s, '0xfab')
+ s.prepend(s)
+ s.append('0x100')
+ s.overwrite('0x5', 4)
+ self.assertEqual(s, '0xf5bfab100')
+
+ def testReverse(self):
+ s = BitStream('0b0011')
+ s.reverse()
+ self.assertEqual(s.bin, '1100')
+ s = BitStream('0b10')
+ s.reverse()
+ self.assertEqual(s.bin, '01')
+ s = BitStream()
+ s.reverse()
+ self.assertEqual(s.bin, '')
+
+ def testInitWithConcatenatedStrings(self):
+ s = BitStream('0xff 0Xee 0xd 0xcc')
+ self.assertEqual(s.hex, 'ffeedcc')
+ s = BitStream('0b0 0B111 0b001')
+ self.assertEqual(s.bin, '0111001')
+ s += '0b1' + '0B1'
+ self.assertEqual(s.bin, '011100111')
+ s = BitStream(hex='ff0xee')
+ self.assertEqual(s.hex, 'ffee')
+ s = BitStream(bin='000b0b11')
+ self.assertEqual(s.bin, '0011')
+ s = BitStream(' 0o123 0O 7 0 o1')
+ self.assertEqual(s.oct, '12371')
+ s += ' 0 o 332'
+ self.assertEqual(s.oct, '12371332')
+
+ def testEquals(self):
+ s1 = BitStream('0b01010101')
+ s2 = BitStream('0b01010101')
+ self.assertTrue(s1 == s2)
+ s3 = BitStream()
+ s4 = BitStream()
+ self.assertTrue(s3 == s4)
+ self.assertFalse(s3 != s4)
+ s5 = BitStream(bytes=b'\xff', offset=2, length=3)
+ s6 = BitStream('0b111')
+ self.assertTrue(s5 == s6)
+ class A(object):
+ pass
+ self.assertFalse(s5 == A())
+
+ def testLargeEquals(self):
+ s1 = BitStream(1000000)
+ s2 = BitStream(1000000)
+ s1.set(True, [-1, 55, 53214, 534211, 999999])
+ s2.set(True, [-1, 55, 53214, 534211, 999999])
+ self.assertEqual(s1, s2)
+ s1.set(True, 800000)
+ self.assertNotEqual(s1, s2)
+
+ def testNotEquals(self):
+ s1 = BitStream('0b0')
+ s2 = BitStream('0b1')
+ self.assertTrue(s1 != s2)
+ self.assertFalse(s1 != BitStream('0b0'))
+
+ def testEqualityWithAutoInitialised(self):
+ a = BitStream('0b00110111')
+ self.assertTrue(a == '0b00110111')
+ self.assertTrue(a == '0x37')
+ self.assertTrue('0b0011 0111' == a)
+ self.assertTrue('0x3 0x7' == a)
+ self.assertFalse(a == '0b11001000')
+ self.assertFalse('0x3737' == a)
+
+ def testInvertSpecialMethod(self):
+ s = BitStream('0b00011001')
+ self.assertEqual((~s).bin, '11100110')
+ self.assertEqual((~BitStream('0b0')).bin, '1')
+ self.assertEqual((~BitStream('0b1')).bin, '0')
+ self.assertTrue(~~s == s)
+
+ def testInvertBitPosition(self):
+ s = ConstBitStream('0xefef')
+ s.pos = 8
+ t = ~s
+ self.assertEqual(s.pos, 8)
+ self.assertEqual(t.pos, 0)
+
+ def testInvertSpecialMethodErrors(self):
+ s = BitStream()
+ self.assertRaises(bitstring.Error, s.__invert__)
+
+ def testJoinWithAuto(self):
+ s = BitStream().join(['0xf', '0b00', BitStream(bin='11')])
+ self.assertEqual(s, '0b11110011')
+
+ def testAutoBitStringCopy(self):
+ s = BitStream('0xabcdef')
+ t = BitStream(s)
+ self.assertEqual(t.hex, 'abcdef')
+ del s[-8:]
+ self.assertEqual(t.hex, 'abcdef')
+
+class Multiplication(unittest.TestCase):
+
+ def testMultiplication(self):
+ a = BitStream('0xff')
+ b = a * 8
+ self.assertEqual(b, '0xffffffffffffffff')
+ b = 4 * a
+ self.assertEqual(b, '0xffffffff')
+ self.assertTrue(1 * a == a * 1 == a)
+ c = a * 0
+ self.assertFalse(c)
+ a *= 3
+ self.assertEqual(a, '0xffffff')
+ a *= 0
+ self.assertFalse(a)
+ one = BitStream('0b1')
+ zero = BitStream('0b0')
+ mix = one * 2 + 3 * zero + 2 * one * 2
+ self.assertEqual(mix, '0b110001111')
+ q = BitStream()
+ q *= 143
+ self.assertFalse(q)
+ q += [True, True, False]
+ q.pos += 2
+ q *= 0
+ self.assertFalse(q)
+ self.assertEqual(q.bitpos, 0)
+
+ def testMultiplicationWithFiles(self):
+ a = BitStream(filename='test.m1v')
+ b = a.len
+ a *= 3
+ self.assertEqual(a.len, 3 * b)
+
+ def testMultiplicationErrors(self):
+ a = BitStream('0b1')
+ b = BitStream('0b0')
+ self.assertRaises(ValueError, a.__mul__, -1)
+ self.assertRaises(ValueError, a.__imul__, -1)
+ self.assertRaises(ValueError, a.__rmul__, -1)
+ self.assertRaises(TypeError, a.__mul__, 1.2)
+ self.assertRaises(TypeError, a.__rmul__, b)
+ self.assertRaises(TypeError, a.__imul__, b)
+
+ def testFileAndMemEquivalence(self):
+ a = ConstBitStream(filename='smalltestfile')
+ b = BitStream(filename='smalltestfile')
+ self.assertTrue(isinstance(a._datastore._rawarray, bitstring.MmapByteArray))
+ self.assertTrue(isinstance(b._datastore._rawarray, bytearray))
+ self.assertEqual(a._datastore.getbyte(0), b._datastore.getbyte(0))
+ self.assertEqual(a._datastore.getbyteslice(1, 5), bytearray(b._datastore.getbyteslice(1, 5)))
+
+
+class BitWise(unittest.TestCase):
+
+ def testBitwiseAnd(self):
+ a = BitStream('0b01101')
+ b = BitStream('0b00110')
+ self.assertEqual((a & b).bin, '00100')
+ self.assertEqual((a & '0b11111'), a)
+ self.assertRaises(ValueError, a.__and__, '0b1')
+ self.assertRaises(ValueError, b.__and__, '0b110111111')
+ c = BitStream('0b0011011')
+ c.pos = 4
+ d = c & '0b1111000'
+ self.assertEqual(d.pos, 0)
+ self.assertEqual(d.bin, '0011000')
+ d = '0b1111000' & c
+ self.assertEqual(d.bin, '0011000')
+
+ def testBitwiseOr(self):
+ a = BitStream('0b111001001')
+ b = BitStream('0b011100011')
+ self.assertEqual((a | b).bin, '111101011')
+ self.assertEqual((a | '0b000000000'), a)
+ self.assertRaises(ValueError, a.__or__, '0b0000')
+ self.assertRaises(ValueError, b.__or__, a + '0b1')
+ a = '0xff00' | BitStream('0x00f0')
+ self.assertEqual(a.hex, 'fff0')
+
+ def testBitwiseXor(self):
+ a = BitStream('0b111001001')
+ b = BitStream('0b011100011')
+ self.assertEqual((a ^ b).bin, '100101010')
+ self.assertEqual((a ^ '0b111100000').bin, '000101001')
+ self.assertRaises(ValueError, a.__xor__, '0b0000')
+ self.assertRaises(ValueError, b.__xor__, a + '0b1')
+ a = '0o707' ^ BitStream('0o777')
+ self.assertEqual(a.oct, '070')
+
+class Split(unittest.TestCase):
+
+ def testSplit(self):
+ a = BitStream('0b0 010100111 010100 0101 010')
+ a.pos = 20
+ subs = [i.bin for i in a.split('0b010')]
+ self.assertEqual(subs, ['0', '010100111', '010100', '0101', '010'])
+ self.assertEqual(a.pos, 20)
+
+ def testSplitCornerCases(self):
+ a = BitStream('0b000000')
+ bsl = a.split('0b1', False)
+ self.assertEqual(next(bsl), a)
+ self.assertRaises(StopIteration, next, bsl)
+ b = BitStream()
+ bsl = b.split('0b001', False)
+ self.assertFalse(next(bsl))
+ self.assertRaises(StopIteration, next, bsl)
+
+ def testSplitErrors(self):
+ a = BitStream('0b0')
+ b = a.split('', False)
+ self.assertRaises(ValueError, next, b)
+
+ def testSliceWithOffset(self):
+ a = BitStream(bytes=b'\x00\xff\x00', offset=7)
+ b = a[7:12]
+ self.assertEqual(b.bin, '11000')
+
+ def testSplitWithMaxsplit(self):
+ a = BitStream('0xaabbccbbccddbbccddee')
+ self.assertEqual(len(list(a.split('0xbb', bytealigned=True))), 4)
+ bsl = list(a.split('0xbb', count=1, bytealigned=True))
+ self.assertEqual((len(bsl), bsl[0]), (1, '0xaa'))
+ bsl = list(a.split('0xbb', count=2, bytealigned=True))
+ self.assertEqual(len(bsl), 2)
+ self.assertEqual(bsl[0], '0xaa')
+ self.assertEqual(bsl[1], '0xbbcc')
+
+ def testSplitMore(self):
+ s = BitStream('0b1100011001110110')
+ for i in range(10):
+ a = list(s.split('0b11', False, count=i))
+ b = list(s.split('0b11', False))[:i]
+ self.assertEqual(a, b)
+ b = s.split('0b11', count=-1)
+ self.assertRaises(ValueError, next, b)
+
+ def testSplitStartbit(self):
+ a = BitStream('0b0010101001000000001111')
+ bsl = a.split('0b001', bytealigned=False, start=1)
+ self.assertEqual([x.bin for x in bsl], ['010101', '001000000', '001111'])
+ b = a.split('0b001', start=-100)
+ self.assertRaises(ValueError, next, b)
+ b = a.split('0b001', start=23)
+ self.assertRaises(ValueError, next, b)
+ b = a.split('0b1', start=10, end=9)
+ self.assertRaises(ValueError, next, b)
+
+ def testSplitStartbitByteAligned(self):
+ a = BitStream('0x00ffffee')
+ bsl = list(a.split('0b111', start=9, bytealigned=True))
+ self.assertEqual([x.bin for x in bsl], ['1111111', '11111111', '11101110'])
+
+ def testSplitEndbit(self):
+ a = BitStream('0b000010001001011')
+ bsl = list(a.split('0b1', bytealigned=False, end=14))
+ self.assertEqual([x.bin for x in bsl], ['0000', '1000', '100', '10', '1'])
+ self.assertEqual(list(a[4:12].split('0b0', False)), list(a.split('0b0', start=4, end=12)))
+ # Shouldn't raise ValueError
+ bsl = list(a.split('0xffee', end=15))
+ # Whereas this one will when we call next()
+ bsl = a.split('0xffee', end=16)
+ self.assertRaises(ValueError, next, bsl)
+
+ def testSplitEndbitByteAligned(self):
+ a = BitStream('0xff00ff')[:22]
+ bsl = list(a.split('0b 0000 0000 111', end=19))
+ self.assertEqual([x.bin for x in bsl], ['11111111', '00000000111'])
+ bsl = list(a.split('0b 0000 0000 111', end=18))
+ self.assertEqual([x.bin for x in bsl], ['111111110000000011'])
+
+ def testSplitMaxSplit(self):
+ a = BitStream('0b1' * 20)
+ for i in range(10):
+ bsl = list(a.split('0b1', count=i))
+ self.assertEqual(len(bsl), i)
+
+ #######################
+
+ def testPositionInSlice(self):
+ a = BitStream('0x00ffff00')
+ a.bytepos = 2
+ b = a[8:24]
+ self.assertEqual(b.bytepos, 0)
+
+ def testFindByteAlignedWithBits(self):
+ a = BitStream('0x00112233445566778899')
+ a.find('0b0001', bytealigned=True)
+ self.assertEqual(a.bitpos, 8)
+
+ def testFindStartbitNotByteAligned(self):
+ a = BitStream('0b0010000100')
+ found = a.find('0b1', start=4)
+ self.assertEqual((found, a.bitpos), ((7,), 7))
+ found = a.find('0b1', start=2)
+ self.assertEqual((found, a.bitpos), ((2,), 2))
+ found = a.find('0b1', bytealigned=False, start=8)
+ self.assertEqual((found, a.bitpos), ((), 2))
+
+ def testFindEndbitNotByteAligned(self):
+ a = BitStream('0b0010010000')
+ found = a.find('0b1', bytealigned=False, end=2)
+ self.assertEqual((found, a.bitpos), ((), 0))
+ found = a.find('0b1', end=3)
+ self.assertEqual((found, a.bitpos), ((2,), 2))
+ found = a.find('0b1', bytealigned=False, start=3, end=5)
+ self.assertEqual((found, a.bitpos), ((), 2))
+ found = a.find('0b1', start=3, end=6)
+ self.assertEqual((found[0], a.bitpos), (5, 5))
+
+ def testFindStartbitByteAligned(self):
+ a = BitStream('0xff001122ff0011ff')
+ a.pos = 40
+ found = a.find('0x22', start=23, bytealigned=True)
+ self.assertEqual((found, a.bytepos), ((24,), 3))
+ a.bytepos = 4
+ found = a.find('0x22', start=24, bytealigned=True)
+ self.assertEqual((found, a.bytepos), ((24,), 3))
+ found = a.find('0x22', start=25, bytealigned=True)
+ self.assertEqual((found, a.pos), ((), 24))
+ found = a.find('0b111', start=40, bytealigned=True)
+ self.assertEqual((found, a.pos), ((56,), 56))
+
+ def testFindEndbitByteAligned(self):
+ a = BitStream('0xff001122ff0011ff')
+ found = a.find('0x22', end=31, bytealigned=True)
+ self.assertFalse(found)
+ self.assertEqual(a.pos, 0)
+ found = a.find('0x22', end=32, bytealigned=True)
+ self.assertTrue(found)
+ self.assertEqual(a.pos, 24)
+ self.assertEqual(found[0], 24)
+
+ def testFindStartEndbitErrors(self):
+ a = BitStream('0b00100')
+ self.assertRaises(ValueError, a.find, '0b1', bytealigned=False, start=-100)
+ self.assertRaises(ValueError, a.find, '0b1', end=6)
+ self.assertRaises(ValueError, a.find, '0b1', start=4, end=3)
+ b = BitStream('0x0011223344')
+ self.assertRaises(ValueError, a.find, '0x22', bytealigned=True, start=-100)
+ self.assertRaises(ValueError, a.find, '0x22', end=41, bytealigned=True)
+
+ def testPrependAndAppendAgain(self):
+ c = BitStream('0x1122334455667788')
+ c.bitpos = 40
+ c.prepend('0b1')
+ self.assertEqual(c.bitpos, 41)
+ c = BitStream()
+ c.prepend('0x1234')
+ self.assertEqual(c.bytepos, 2)
+ c = BitStream()
+ c.append('0x1234')
+ self.assertEqual(c.bytepos, 0)
+ s = BitStream(bytes=b'\xff\xff', offset=2)
+ self.assertEqual(s.length, 14)
+ t = BitStream(bytes=b'\x80', offset=1, length=2)
+ s.prepend(t)
+ self.assertEqual(s, '0x3fff')
+
+ def testFindAll(self):
+ a = BitStream('0b11111')
+ p = a.findall('0b1')
+ self.assertEqual(list(p), [0, 1, 2, 3, 4])
+ p = a.findall('0b11')
+ self.assertEqual(list(p), [0, 1, 2, 3])
+ p = a.findall('0b10')
+ self.assertEqual(list(p), [])
+ a = BitStream('0x4733eeff66554747335832434547')
+ p = a.findall('0x47', bytealigned=True)
+ self.assertEqual(list(p), [0, 6 * 8, 7 * 8, 13 * 8])
+ p = a.findall('0x4733', bytealigned=True)
+ self.assertEqual(list(p), [0, 7 * 8])
+ a = BitStream('0b1001001001001001001')
+ p = a.findall('0b1001', bytealigned=False)
+ self.assertEqual(list(p), [0, 3, 6, 9, 12, 15])
+ self.assertEqual(a.pos, 15)
+
+ def testFindAllGenerator(self):
+ a = BitStream('0xff1234512345ff1234ff12ff')
+ p = a.findall('0xff', bytealigned=True)
+ self.assertEqual(next(p), 0)
+ self.assertEqual(next(p), 6 * 8)
+ self.assertEqual(next(p), 9 * 8)
+ self.assertEqual(next(p), 11 * 8)
+ self.assertRaises(StopIteration, next, p)
+
+ def testFindAllCount(self):
+ s = BitStream('0b1') * 100
+ for i in [0, 1, 23]:
+ self.assertEqual(len(list(s.findall('0b1', count=i))), i)
+ b = s.findall('0b1', bytealigned=True, count=-1)
+ self.assertRaises(ValueError, next, b)
+
+ def testContains(self):
+ a = BitStream('0b1') + '0x0001dead0001'
+ self.assertTrue('0xdead' in a)
+ self.assertEqual(a.pos, 0)
+ self.assertFalse('0xfeed' in a)
+
+ def testRepr(self):
+ max = bitstring.MAX_CHARS
+ bls = ['', '0b1', '0o5', '0x43412424f41', '0b00101001010101']
+ for bs in bls:
+ a = BitStream(bs)
+ b = eval(a.__repr__())
+ self.assertTrue(a == b)
+ for f in [ConstBitStream(filename='test.m1v'),
+ ConstBitStream(filename='test.m1v', length=17),
+ ConstBitStream(filename='test.m1v', length=23, offset=23102)]:
+ f2 = eval(f.__repr__())
+ self.assertEqual(f._datastore._rawarray.source.name, f2._datastore._rawarray.source.name)
+ self.assertTrue(f2.tobytes() == f.tobytes())
+ a = BitStream('0b1')
+ self.assertEqual(repr(a), "BitStream('0b1')")
+ a += '0b11'
+ self.assertEqual(repr(a), "BitStream('0b111')")
+ a += '0b1'
+ self.assertEqual(repr(a), "BitStream('0xf')")
+ a *= max
+ self.assertEqual(repr(a), "BitStream('0x" + "f" * max + "')")
+ a += '0xf'
+ self.assertEqual(repr(a), "BitStream('0x" + "f" * max + "...') # length=%d" % (max * 4 + 4))
+
+ def testPrint(self):
+ s = BitStream(hex='0x00')
+ self.assertEqual('0x' + s.hex, s.__str__())
+ s = BitStream(filename='test.m1v')
+ self.assertEqual('0x' + s[0:bitstring.MAX_CHARS * 4].hex + '...', s.__str__())
+ self.assertEqual(BitStream().__str__(), '')
+ s = BitStream('0b11010')
+ self.assertEqual('0b' + s.bin, s.__str__())
+ s = BitStream('0x12345678901234567890,0b1')
+ self.assertEqual('0x12345678901234567890, 0b1', s.__str__())
+
+ def testIter(self):
+ a = BitStream('0b001010')
+ b = BitStream()
+ for bit in a:
+ b.append(ConstBitStream(bool=bit))
+ self.assertEqual(a, b)
+
+ def testDelitem(self):
+ a = BitStream('0xffee')
+ del a[0:8]
+ self.assertEqual(a.hex, 'ee')
+ del a[0:8]
+ self.assertFalse(a)
+ del a[10:12]
+ self.assertFalse(a)
+
+ def testNonZeroBitsAtStart(self):
+ a = BitStream(bytes=b'\xff', offset=2)
+ b = BitStream('0b00')
+ b += a
+ self.assertTrue(b == '0b0011 1111')
+ #self.assertEqual(a._datastore.rawbytes, b'\xff')
+ self.assertEqual(a.tobytes(), b'\xfc')
+
+ def testNonZeroBitsAtEnd(self):
+ a = BitStream(bytes=b'\xff', length=5)
+ #self.assertEqual(a._datastore.rawbytes, b'\xff')
+ b = BitStream('0b00')
+ a += b
+ self.assertTrue(a == '0b1111100')
+ self.assertEqual(a.tobytes(), b'\xf8')
+ self.assertRaises(ValueError, a._getbytes)
+
+ def testNewOffsetErrors(self):
+ self.assertRaises(bitstring.CreationError, BitStream, hex='ff', offset=-1)
+ self.assertRaises(bitstring.CreationError, BitStream, '0xffffffff', offset=33)
+
+ def testSliceStep(self):
+ a = BitStream('0x3')
+ b = a[::1]
+ self.assertEqual(a, b)
+ self.assertEqual(a[2:4:1], '0b11')
+ self.assertEqual(a[0:2:1], '0b00')
+ self.assertEqual(a[:3], '0o1')
+
+ a = BitStream('0x0011223344556677')
+ self.assertEqual(a[-8:], '0x77')
+ self.assertEqual(a[:-24], '0x0011223344')
+ self.assertEqual(a[-1000:-24], '0x0011223344')
+
+ def testInterestingSliceStep(self):
+ a = BitStream('0b0011000111')
+ self.assertEqual(a[7:3:-1], '0b1000')
+ self.assertEqual(a[9:2:-1], '0b1110001')
+ self.assertEqual(a[8:2:-2], '0b100')
+ self.assertEqual(a[100:-20:-3], '0b1010')
+ self.assertEqual(a[100:-20:-1], '0b1110001100')
+ self.assertEqual(a[10:2:-1], '0b1110001')
+ self.assertEqual(a[100:2:-1], '0b1110001')
+
+ def testInsertionOrderAndBitpos(self):
+ b = BitStream()
+ b[0:0] = '0b0'
+ b[0:0] = '0b1'
+ self.assertEqual(b, '0b10')
+ self.assertEqual(b.bitpos, 1)
+ a = BitStream()
+ a.insert('0b0')
+ a.insert('0b1')
+ self.assertEqual(a, '0b01')
+ self.assertEqual(a.bitpos, 2)
+
+ def testOverwriteOrderAndBitpos(self):
+ a = BitStream('0xff')
+ a.overwrite('0xa')
+ self.assertEqual(a, '0xaf')
+ self.assertEqual(a.bitpos, 4)
+ a.overwrite('0xb')
+ self.assertEqual(a, '0xab')
+ self.assertEqual(a.bitpos, 8)
+ self.assertRaises(ValueError, a.overwrite, '0b1')
+ a.overwrite('0xa', 4)
+ self.assertEqual(a, '0xaa')
+ self.assertEqual(a.bitpos, 8)
+ a.overwrite(a, 0)
+ self.assertEqual(a, '0xaa')
+
+ def testInitSliceWithInt(self):
+ a = BitStream(length=8)
+ a[:] = 100
+ self.assertEqual(a.uint, 100)
+ a[0] = 1
+ self.assertEqual(a.bin, '11100100')
+ a[1] = 0
+ self.assertEqual(a.bin, '10100100')
+ a[-1] = -1
+ self.assertEqual(a.bin, '10100101')
+ a[-3:] = -2
+ self.assertEqual(a.bin, '10100110')
+
+ def testInitSliceWithIntErrors(self):
+ a = BitStream('0b0000')
+ self.assertRaises(ValueError, a.__setitem__, slice(0, 4), 16)
+ self.assertRaises(ValueError, a.__setitem__, slice(0, 4), -9)
+ self.assertRaises(ValueError, a.__setitem__, 0, 2)
+ self.assertRaises(ValueError, a.__setitem__, 0, -2)
+
+ def testReverseWithSlice(self):
+ a = BitStream('0x0012ff')
+ a.reverse()
+ self.assertEqual(a, '0xff4800')
+ a.reverse(8, 16)
+ self.assertEqual(a, '0xff1200')
+ b = a[8:16]
+ b.reverse()
+ a[8:16] = b
+ self.assertEqual(a, '0xff4800')
+
+ def testReverseWithSliceErrors(self):
+ a = BitStream('0x123')
+ self.assertRaises(ValueError, a.reverse, -1, 4)
+ self.assertRaises(ValueError, a.reverse, 10, 9)
+ self.assertRaises(ValueError, a.reverse, 1, 10000)
+
+ def testInitialiseFromList(self):
+ a = BitStream([])
+ self.assertFalse(a)
+ a = BitStream([True, False, [], [0], 'hello'])
+ self.assertEqual(a, '0b10011')
+ a += []
+ self.assertEqual(a, '0b10011')
+ a += [True, False, True]
+ self.assertEqual(a, '0b10011101')
+ a.find([12, 23])
+ self.assertEqual(a.pos, 3)
+ self.assertEqual([1, 0, False, True], BitStream('0b1001'))
+ a = [True] + BitStream('0b1')
+ self.assertEqual(a, '0b11')
+
+ def testInitialiseFromTuple(self):
+ a = BitStream(())
+ self.assertFalse(a)
+ a = BitStream((0, 1, '0', '1'))
+ self.assertEqual('0b0111', a)
+ a.replace((True, True), [])
+ self.assertEqual(a, (False, True))
+
+ def testCut(self):
+ a = BitStream('0x00112233445')
+ b = list(a.cut(8))
+ self.assertEqual(b, ['0x00', '0x11', '0x22', '0x33', '0x44'])
+ b = list(a.cut(4, 8, 16))
+ self.assertEqual(b, ['0x1', '0x1'])
+ b = list(a.cut(4, 0, 44, 4))
+ self.assertEqual(b, ['0x0', '0x0', '0x1', '0x1'])
+ a = BitStream()
+ b = list(a.cut(10))
+ self.assertTrue(not b)
+
+ def testCutErrors(self):
+ a = BitStream('0b1')
+ b = a.cut(1, 1, 2)
+ self.assertRaises(ValueError, next, b)
+ b = a.cut(1, -2, 1)
+ self.assertRaises(ValueError, next, b)
+ b = a.cut(0)
+ self.assertRaises(ValueError, next, b)
+ b = a.cut(1, count=-1)
+ self.assertRaises(ValueError, next, b)
+
+ def testCutProblem(self):
+ s = BitStream('0x1234')
+ for n in list(s.cut(4)):
+ s.prepend(n)
+ self.assertEqual(s, '0x43211234')
+
+ def testJoinFunctions(self):
+ a = BitStream().join(['0xa', '0xb', '0b1111'])
+ self.assertEqual(a, '0xabf')
+ a = BitStream('0b1').join(['0b0' for i in range(10)])
+ self.assertEqual(a, '0b0101010101010101010')
+ a = BitStream('0xff').join([])
+ self.assertFalse(a)
+
+ def testAddingBitpos(self):
+ a = BitStream('0xff')
+ b = BitStream('0x00')
+ a.bitpos = b.bitpos = 8
+ c = a + b
+ self.assertEqual(c.bitpos, 0)
+
+ def testIntelligentRead1(self):
+ a = BitStream(uint=123, length=23)
+ u = a.read('uint:23')
+ self.assertEqual(u, 123)
+ self.assertEqual(a.pos, a.len)
+ b = BitStream(int=-12, length=44)
+ i = b.read('int:44')
+ self.assertEqual(i, -12)
+ self.assertEqual(b.pos, b.len)
+ u2, i2 = (a + b).readlist('uint:23, int:44')
+ self.assertEqual((u2, i2), (123, -12))
+
+ def testIntelligentRead2(self):
+ a = BitStream(ue=822)
+ u = a.read('ue')
+ self.assertEqual(u, 822)
+ self.assertEqual(a.pos, a.len)
+ b = BitStream(se=-1001)
+ s = b.read('se')
+ self.assertEqual(s, -1001)
+ self.assertEqual(b.pos, b.len)
+ s, u1, u2 = (b + 2 * a).readlist('se, ue, ue')
+ self.assertEqual((s, u1, u2), (-1001, 822, 822))
+
+ def testIntelligentRead3(self):
+ a = BitStream('0x123') + '0b11101'
+ h = a.read('hex:12')
+ self.assertEqual(h, '123')
+ b = a.read('bin: 5')
+ self.assertEqual(b, '11101')
+ c = '0b' + b + a
+ b, h = c.readlist('bin:5, hex:12')
+ self.assertEqual((b, h), ('11101', '123'))
+
+ def testIntelligentRead4(self):
+ a = BitStream('0o007')
+ o = a.read('oct:9')
+ self.assertEqual(o, '007')
+ self.assertEqual(a.pos, a.len)
+
+ def testIntelligentRead5(self):
+ a = BitStream('0x00112233')
+ c0, c1, c2 = a.readlist('bits:8, bits:8, bits:16')
+ self.assertEqual((c0, c1, c2), (BitStream('0x00'), BitStream('0x11'), BitStream('0x2233')))
+ a.pos = 0
+ c = a.read('bits:16')
+ self.assertEqual(c, BitStream('0x0011'))
+
+ def testIntelligentRead6(self):
+ a = BitStream('0b000111000')
+ b1, b2, b3 = a.readlist('bin :3, int: 3, int:3')
+ self.assertEqual(b1, '000')
+ self.assertEqual(b2, -1)
+ self.assertEqual(b3, 0)
+
+ def testIntelligentRead7(self):
+ a = BitStream('0x1234')
+ a1, a2, a3, a4 = a.readlist('bin:0, oct:0, hex:0, bits:0')
+ self.assertTrue(a1 == a2 == a3 == '')
+ self.assertFalse(a4)
+ self.assertRaises(ValueError, a.read, 'int:0')
+ self.assertRaises(ValueError, a.read, 'uint:0')
+ self.assertEqual(a.pos, 0)
+
+ def testIntelligentRead8(self):
+ a = BitStream('0x123456')
+ for t in ['hex:1', 'oct:1', 'hex4', '-5', 'fred', 'bin:-2',
+ 'uint:p', 'uint:-2', 'int:u', 'int:-3', 'ses', 'uee', '-14']:
+ self.assertRaises(ValueError, a.read, t)
+
+ def testIntelligentRead9(self):
+ a = BitStream('0xff')
+ self.assertEqual(a.read('intle'), -1)
+
+ def testFillerReads1(self):
+ s = BitStream('0x012345')
+ t = s.read('bits')
+ self.assertEqual(s, t)
+ s.pos = 0
+ a, b = s.readlist('hex:8, hex')
+ self.assertEqual(a, '01')
+ self.assertEqual(b, '2345')
+ self.assertTrue(isinstance(b, str))
+ s.bytepos = 0
+ a, b = s.readlist('bin, hex:20')
+ self.assertEqual(a, '0000')
+ self.assertEqual(b, '12345')
+ self.assertTrue(isinstance(a, str))
+
+ def testFillerReads2(self):
+ s = BitStream('0xabcdef')
+ self.assertRaises(bitstring.Error, s.readlist, 'bits, se')
+ self.assertRaises(bitstring.Error, s.readlist, 'hex:4, bits, ue, bin:4')
+ s.pos = 0
+ self.assertRaises(bitstring.Error, s.readlist, 'bin, bin')
+
+ def testIntelligentPeek(self):
+ a = BitStream('0b01, 0x43, 0o4, uint:23=2, se=5, ue=3')
+ b, c, e = a.peeklist('bin:2, hex:8, oct:3')
+ self.assertEqual((b, c, e), ('01', '43', '4'))
+ self.assertEqual(a.pos, 0)
+ a.pos = 13
+ f, g, h = a.peeklist('uint:23, se, ue')
+ self.assertEqual((f, g, h), (2, 5, 3))
+ self.assertEqual(a.pos, 13)
+
+ def testReadMultipleBits(self):
+ s = BitStream('0x123456789abcdef')
+ a, b = s.readlist([4, 4])
+ self.assertEqual(a, '0x1')
+ self.assertEqual(b, '0x2')
+ c, d, e = s.readlist([8, 16, 8])
+ self.assertEqual(c, '0x34')
+ self.assertEqual(d, '0x5678')
+ self.assertEqual(e, '0x9a')
+
+ def testPeekMultipleBits(self):
+ s = BitStream('0b1101, 0o721, 0x2234567')
+ a, b, c, d = s.peeklist([2, 1, 1, 9])
+ self.assertEqual(a, '0b11')
+ self.assertEqual(bool(b), False)
+ self.assertEqual(bool(c), True)
+ self.assertEqual(d, '0o721')
+ self.assertEqual(s.pos, 0)
+ a, b = s.peeklist([4, 9])
+ self.assertEqual(a, '0b1101')
+ self.assertEqual(b, '0o721')
+ s.pos = 13
+ a, b = s.peeklist([16, 8])
+ self.assertEqual(a, '0x2234')
+ self.assertEqual(b, '0x56')
+ self.assertEqual(s.pos, 13)
+
+ def testDifficultPrepends(self):
+ a = BitStream('0b1101011')
+ b = BitStream()
+ for i in range(10):
+ b.prepend(a)
+ self.assertEqual(b, a * 10)
+
+ def testPackingWrongNumberOfThings(self):
+ self.assertRaises(bitstring.CreationError, pack, 'bin:1')
+ self.assertRaises(bitstring.CreationError, pack, '', 100)
+
+ def testPackWithVariousKeys(self):
+ a = pack('uint10', uint10='0b1')
+ self.assertEqual(a, '0b1')
+ b = pack('0b110', **{'0b110': '0xfff'})
+ self.assertEqual(b, '0xfff')
+
+ def testPackWithVariableLength(self):
+ for i in range(1, 11):
+ a = pack('uint:n', 0, n=i)
+ self.assertEqual(a.bin, '0' * i)
+
+ def testToBytes(self):
+ a = BitStream(bytes=b'\xab\x00')
+ b = a.tobytes()
+ self.assertEqual(a.bytes, b)
+ for i in range(7):
+ del a[-1:]
+ self.assertEqual(a.tobytes(), b'\xab\x00')
+ del a[-1:]
+ self.assertEqual(a.tobytes(), b'\xab')
+
+ def testToFile(self):
+ a = BitStream('0x0000ff')[:17]
+ f = open('temp_bitstring_unit_testing_file', 'wb')
+ a.tofile(f)
+ f.close()
+ b = BitStream(filename='temp_bitstring_unit_testing_file')
+ self.assertEqual(b, '0x000080')
+
+ a = BitStream('0x911111')
+ del a[:1]
+ self.assertEqual(a + '0b0', '0x222222')
+ f = open('temp_bitstring_unit_testing_file', 'wb')
+ a.tofile(f)
+ f.close()
+ b = BitStream(filename='temp_bitstring_unit_testing_file')
+ self.assertEqual(b, '0x222222')
+ os.remove('temp_bitstring_unit_testing_file')
+
+ #def testToFileWithLargerFile(self):
+ # a = BitStream(length=16000000)
+ # a[1] = '0b1'
+ # a[-2] = '0b1'
+ # f = open('temp_bitstring_unit_testing_file' ,'wb')
+ # a.tofile(f)
+ # f.close()
+ # b = BitStream(filename='temp_bitstring_unit_testing_file')
+ # self.assertEqual(b.len, 16000000)
+ # self.assertEqual(b[1], True)
+ #
+ # f = open('temp_bitstring_unit_testing_file' ,'wb')
+ # a[1:].tofile(f)
+ # f.close()
+ # b = BitStream(filename='temp_bitstring_unit_testing_file')
+ # self.assertEqual(b.len, 16000000)
+ # self.assertEqual(b[0], True)
+ # os.remove('temp_bitstring_unit_testing_file')
+
+ def testTokenParser(self):
+ tp = bitstring.tokenparser
+ self.assertEqual(tp('hex'), (True, [('hex', None, None)]))
+ self.assertEqual(tp('hex=14'), (True, [('hex', None, '14')]))
+ self.assertEqual(tp('se'), (False, [('se', None, None)]))
+ self.assertEqual(tp('ue=12'), (False, [('ue', None, '12')]))
+ self.assertEqual(tp('0xef'), (False, [('0x', None, 'ef')]))
+ self.assertEqual(tp('uint:12'), (False, [('uint', 12, None)]))
+ self.assertEqual(tp('int:30=-1'), (False, [('int', 30, '-1')]))
+ self.assertEqual(tp('bits:10'), (False, [('bits', 10, None)]))
+ self.assertEqual(tp('bits:10'), (False, [('bits', 10, None)]))
+ self.assertEqual(tp('123'), (False, [('uint', 123, None)]))
+ self.assertEqual(tp('123'), (False, [('uint', 123, None)]))
+ self.assertRaises(ValueError, tp, 'hex12')
+ self.assertEqual(tp('hex12', ('hex12',)), (False, [('hex12', None, None)]))
+ self.assertEqual(tp('2*bits:6'), (False, [('bits', 6, None), ('bits', 6, None)]))
+
+ def testAutoFromFileObject(self):
+ with open('test.m1v', 'rb') as f:
+ s = ConstBitStream(f, offset=32, length=12)
+ self.assertEqual(s.uint, 352)
+ t = ConstBitStream('0xf') + f
+ self.assertTrue(t.startswith('0xf000001b3160'))
+ s2 = ConstBitStream(f)
+ t2 = BitStream('0xc')
+ t2.prepend(s2)
+ self.assertTrue(t2.startswith('0x000001b3'))
+ self.assertTrue(t2.endswith('0xc'))
+ with open('test.m1v', 'rb') as b:
+ u = BitStream(bytes=b.read())
+ # TODO: u == s2 is much slower than u.bytes == s2.bytes
+ self.assertEqual(u.bytes, s2.bytes)
+
+ def testFileBasedCopy(self):
+ with open('smalltestfile', 'rb') as f:
+ s = BitStream(f)
+ t = BitStream(s)
+ s.prepend('0b1')
+ self.assertEqual(s[1:], t)
+ s = BitStream(f)
+ t = copy.copy(s)
+ t.append('0b1')
+ self.assertEqual(s, t[:-1])
+
+ def testBigEndianSynonyms(self):
+ s = BitStream('0x12318276ef')
+ self.assertEqual(s.int, s.intbe)
+ self.assertEqual(s.uint, s.uintbe)
+ s = BitStream(intbe=-100, length=16)
+ self.assertEqual(s, 'int:16=-100')
+ s = BitStream(uintbe=13, length=24)
+ self.assertEqual(s, 'int:24=13')
+ s = BitStream('uintbe:32=1000')
+ self.assertEqual(s, 'uint:32=1000')
+ s = BitStream('intbe:8=2')
+ self.assertEqual(s, 'int:8=2')
+ self.assertEqual(s.read('intbe'), 2)
+ s.pos = 0
+ self.assertEqual(s.read('uintbe'), 2)
+
+ def testBigEndianSynonymErrors(self):
+ self.assertRaises(bitstring.CreationError, BitStream, uintbe=100, length=15)
+ self.assertRaises(bitstring.CreationError, BitStream, intbe=100, length=15)
+ self.assertRaises(bitstring.CreationError, BitStream, 'uintbe:17=100')
+ self.assertRaises(bitstring.CreationError, BitStream, 'intbe:7=2')
+ s = BitStream('0b1')
+ self.assertRaises(bitstring.InterpretError, s._getintbe)
+ self.assertRaises(bitstring.InterpretError, s._getuintbe)
+ self.assertRaises(ValueError, s.read, 'uintbe')
+ self.assertRaises(ValueError, s.read, 'intbe')
+
+ def testLittleEndianUint(self):
+ s = BitStream(uint=100, length=16)
+ self.assertEqual(s.uintle, 25600)
+ s = BitStream(uintle=100, length=16)
+ self.assertEqual(s.uint, 25600)
+ self.assertEqual(s.uintle, 100)
+ s.uintle += 5
+ self.assertEqual(s.uintle, 105)
+ s = BitStream('uintle:32=999')
+ self.assertEqual(s.uintle, 999)
+ s.byteswap()
+ self.assertEqual(s.uint, 999)
+ s = pack('uintle:24', 1001)
+ self.assertEqual(s.uintle, 1001)
+ self.assertEqual(s.length, 24)
+ self.assertEqual(s.read('uintle'), 1001)
+
+ def testLittleEndianInt(self):
+ s = BitStream(int=100, length=16)
+ self.assertEqual(s.intle, 25600)
+ s = BitStream(intle=100, length=16)
+ self.assertEqual(s.int, 25600)
+ self.assertEqual(s.intle, 100)
+ s.intle += 5
+ self.assertEqual(s.intle, 105)
+ s = BitStream('intle:32=999')
+ self.assertEqual(s.intle, 999)
+ s.byteswap()
+ self.assertEqual(s.int, 999)
+ s = pack('intle:24', 1001)
+ self.assertEqual(s.intle, 1001)
+ self.assertEqual(s.length, 24)
+ self.assertEqual(s.read('intle'), 1001)
+
+ def testLittleEndianErrors(self):
+ self.assertRaises(bitstring.CreationError, BitStream, 'uintle:15=10')
+ self.assertRaises(bitstring.CreationError, BitStream, 'intle:31=-999')
+ self.assertRaises(bitstring.CreationError, BitStream, uintle=100, length=15)
+ self.assertRaises(bitstring.CreationError, BitStream, intle=100, length=15)
+ s = BitStream('0xfff')
+ self.assertRaises(bitstring.InterpretError, s._getintle)
+ self.assertRaises(bitstring.InterpretError, s._getuintle)
+ self.assertRaises(ValueError, s.read, 'uintle')
+ self.assertRaises(ValueError, s.read, 'intle')
+
+ def testStructTokens1(self):
+ self.assertEqual(pack('<b', 23), BitStream('intle:8=23'))
+ self.assertEqual(pack('<B', 23), BitStream('uintle:8=23'))
+ self.assertEqual(pack('<h', 23), BitStream('intle:16=23'))
+ self.assertEqual(pack('<H', 23), BitStream('uintle:16=23'))
+ self.assertEqual(pack('<l', 23), BitStream('intle:32=23'))
+ self.assertEqual(pack('<L', 23), BitStream('uintle:32=23'))
+ self.assertEqual(pack('<q', 23), BitStream('intle:64=23'))
+ self.assertEqual(pack('<Q', 23), BitStream('uintle:64=23'))
+ self.assertEqual(pack('>b', 23), BitStream('intbe:8=23'))
+ self.assertEqual(pack('>B', 23), BitStream('uintbe:8=23'))
+ self.assertEqual(pack('>h', 23), BitStream('intbe:16=23'))
+ self.assertEqual(pack('>H', 23), BitStream('uintbe:16=23'))
+ self.assertEqual(pack('>l', 23), BitStream('intbe:32=23'))
+ self.assertEqual(pack('>L', 23), BitStream('uintbe:32=23'))
+ self.assertEqual(pack('>q', 23), BitStream('intbe:64=23'))
+ self.assertEqual(pack('>Q', 23), BitStream('uintbe:64=23'))
+ self.assertRaises(bitstring.CreationError, pack, '<B', -1)
+ self.assertRaises(bitstring.CreationError, pack, '<H', -1)
+ self.assertRaises(bitstring.CreationError, pack, '<L', -1)
+ self.assertRaises(bitstring.CreationError, pack, '<Q', -1)
+
+ def testStructTokens2(self):
+ endianness = sys.byteorder
+ sys.byteorder = 'little'
+ self.assertEqual(pack('@b', 23), BitStream('intle:8=23'))
+ self.assertEqual(pack('@B', 23), BitStream('uintle:8=23'))
+ self.assertEqual(pack('@h', 23), BitStream('intle:16=23'))
+ self.assertEqual(pack('@H', 23), BitStream('uintle:16=23'))
+ self.assertEqual(pack('@l', 23), BitStream('intle:32=23'))
+ self.assertEqual(pack('@L', 23), BitStream('uintle:32=23'))
+ self.assertEqual(pack('@q', 23), BitStream('intle:64=23'))
+ self.assertEqual(pack('@Q', 23), BitStream('uintle:64=23'))
+ sys.byteorder = 'big'
+ self.assertEqual(pack('@b', 23), BitStream('intbe:8=23'))
+ self.assertEqual(pack('@B', 23), BitStream('uintbe:8=23'))
+ self.assertEqual(pack('@h', 23), BitStream('intbe:16=23'))
+ self.assertEqual(pack('@H', 23), BitStream('uintbe:16=23'))
+ self.assertEqual(pack('@l', 23), BitStream('intbe:32=23'))
+ self.assertEqual(pack('@L', 23), BitStream('uintbe:32=23'))
+ self.assertEqual(pack('@q', 23), BitStream('intbe:64=23'))
+ self.assertEqual(pack('@Q', 23), BitStream('uintbe:64=23'))
+ sys.byteorder = endianness
+
+ def testNativeEndianness(self):
+ s = pack('@2L', 40, 40)
+ if sys.byteorder == 'little':
+ self.assertEqual(s, pack('<2L', 40, 40))
+ else:
+ self.assertEqual(sys.byteorder, 'big')
+ self.assertEqual(s, pack('>2L', 40, 40))
+
+ def testStructTokens2(self):
+ s = pack('>hhl', 1, 2, 3)
+ a, b, c = s.unpack('>hhl')
+ self.assertEqual((a, b, c), (1, 2, 3))
+ s = pack('<QL, >Q \tL', 1001, 43, 21, 9999)
+ self.assertEqual(s.unpack('<QL, >QL'), [1001, 43, 21, 9999])
+
+ def testStructTokensMultiplicativeFactors(self):
+ s = pack('<2h', 1, 2)
+ a, b = s.unpack('<2h')
+ self.assertEqual((a, b), (1, 2))
+ s = pack('<100q', *range(100))
+ self.assertEqual(s.len, 100 * 64)
+ self.assertEqual(s[44*64:45*64].uintle, 44)
+ s = pack('@L0B2h', 5, 5, 5)
+ self.assertEqual(s.unpack('@Lhh'), [5, 5, 5])
+
+ def testStructTokensErrors(self):
+ for f in ['>>q', '<>q', 'q>', '2q', 'q', '>-2q', '@a', '>int:8', '>q2']:
+ self.assertRaises(bitstring.CreationError, pack, f, 100)
+
+ def testImmutableBitStreams(self):
+ a = ConstBitStream('0x012345')
+ self.assertEqual(a, '0x012345')
+ b = BitStream('0xf') + a
+ self.assertEqual(b, '0xf012345')
+ try:
+ a.append(b)
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.prepend(b)
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a[0] = '0b1'
+ self.assertTrue(False)
+ except TypeError:
+ pass
+ try:
+ del a[5]
+ self.assertTrue(False)
+ except TypeError:
+ pass
+ try:
+ a.replace('0b1', '0b0')
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.insert('0b11', 4)
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.reverse()
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.reversebytes()
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ self.assertEqual(a, '0x012345')
+ self.assertTrue(isinstance(a, ConstBitStream))
+
+ def testReverseBytes(self):
+ a = BitStream('0x123456')
+ a.byteswap()
+ self.assertEqual(a, '0x563412')
+ b = a + '0b1'
+ b.byteswap()
+ self.assertEqual('0x123456, 0b1', b)
+ a = BitStream('0x54')
+ a.byteswap()
+ self.assertEqual(a, '0x54')
+ a = BitStream()
+ a.byteswap()
+ self.assertFalse(a)
+
+ def testReverseBytes2(self):
+ a = BitStream()
+ a.byteswap()
+ self.assertFalse(a)
+ a = BitStream('0x00112233')
+ a.byteswap(0, 0, 16)
+ self.assertEqual(a, '0x11002233')
+ a.byteswap(0, 4, 28)
+ self.assertEqual(a, '0x12302103')
+ a.byteswap(start=0, end=18)
+ self.assertEqual(a, '0x30122103')
+ self.assertRaises(ValueError, a.byteswap, 0, 10, 2)
+ self.assertRaises(ValueError, a.byteswap, 0, -4, 4)
+ self.assertRaises(ValueError, a.byteswap, 0, 24, 48)
+ a.byteswap(0, 24)
+ self.assertEqual(a, '0x30122103')
+ a.byteswap(0, 11, 11)
+ self.assertEqual(a, '0x30122103')
+
+ def testCapitalsInPack(self):
+ a = pack('A', A='0b1')
+ self.assertEqual(a, '0b1')
+ format = 'bits:4=BL_OFFT, uint:12=width, uint:12=height'
+ d = {'BL_OFFT': '0b1011', 'width': 352, 'height': 288}
+ s = bitstring.pack(format, **d)
+ self.assertEqual(s, '0b1011, uint:12=352, uint:12=288')
+ a = pack('0X0, uint:8, hex', 45, '0XABcD')
+ self.assertEqual(a, '0x0, uint:8=45, 0xabCD')
+
+ def testOtherCapitals(self):
+ a = ConstBitStream('0XABC, 0O0, 0B11')
+ self.assertEqual(a, 'hex=0Xabc, oct=0, bin=0B11')
+
+ def testEfficientOverwrite(self):
+ a = BitStream(1000000000)
+ a.overwrite([1], 123456)
+ self.assertEqual(a[123456], True)
+ a.overwrite('0xff', 1)
+ self.assertEqual(a[0:32:1], '0x7f800000')
+ b = BitStream('0xffff')
+ b.overwrite('0x0000')
+ self.assertEqual(b, '0x0000')
+ self.assertEqual(b.pos, 16)
+ c = BitStream(length=1000)
+ c.overwrite('0xaaaaaaaaaaaa', 81)
+ self.assertEqual(c[81:81 + 6 * 8], '0xaaaaaaaaaaaa')
+ self.assertEqual(len(list(c.findall('0b1'))), 24)
+ s = BitStream(length=1000)
+ s = s[5:]
+ s.overwrite('0xffffff', 500)
+ s.pos = 500
+ self.assertEqual(s.read(4 * 8), '0xffffff00')
+ s.overwrite('0xff', 502)
+ self.assertEqual(s[502:518], '0xffff')
+
+ def testPeekAndReadListErrors(self):
+ a = BitStream('0x123456')
+ self.assertRaises(ValueError, a.read, 'hex:8, hex:8')
+ self.assertRaises(ValueError, a.peek, 'hex:8, hex:8')
+ self.assertRaises(TypeError, a.read, 10, 12)
+ self.assertRaises(TypeError, a.peek, 12, 14)
+ self.assertRaises(TypeError, a.read, 8, 8)
+ self.assertRaises(TypeError, a.peek, 80, 80)
+
+ def testStartswith(self):
+ a = BitStream()
+ self.assertTrue(a.startswith(BitStream()))
+ self.assertFalse(a.startswith('0b0'))
+ a = BitStream('0x12ff')
+ self.assertTrue(a.startswith('0x1'))
+ self.assertTrue(a.startswith('0b0001001'))
+ self.assertTrue(a.startswith('0x12ff'))
+ self.assertFalse(a.startswith('0x12ff, 0b1'))
+ self.assertFalse(a.startswith('0x2'))
+
+ def testStartswithStartEnd(self):
+ s = BitStream('0x123456')
+ self.assertTrue(s.startswith('0x234', 4))
+ self.assertFalse(s.startswith('0x123', end=11))
+ self.assertTrue(s.startswith('0x123', end=12))
+ self.assertTrue(s.startswith('0x34', 8, 16))
+ self.assertFalse(s.startswith('0x34', 7, 16))
+ self.assertFalse(s.startswith('0x34', 9, 16))
+ self.assertFalse(s.startswith('0x34', 8, 15))
+
+ def testEndswith(self):
+ a = BitStream()
+ self.assertTrue(a.endswith(''))
+ self.assertFalse(a.endswith(BitStream('0b1')))
+ a = BitStream('0xf2341')
+ self.assertTrue(a.endswith('0x41'))
+ self.assertTrue(a.endswith('0b001'))
+ self.assertTrue(a.endswith('0xf2341'))
+ self.assertFalse(a.endswith('0x1f2341'))
+ self.assertFalse(a.endswith('0o34'))
+
+ def testEndswithStartEnd(self):
+ s = BitStream('0x123456')
+ self.assertTrue(s.endswith('0x234', end=16))
+ self.assertFalse(s.endswith('0x456', start=13))
+ self.assertTrue(s.endswith('0x456', start=12))
+ self.assertTrue(s.endswith('0x34', 8, 16))
+ self.assertTrue(s.endswith('0x34', 7, 16))
+ self.assertFalse(s.endswith('0x34', 9, 16))
+ self.assertFalse(s.endswith('0x34', 8, 15))
+
+ def testUnhashability(self):
+ s = BitStream('0xf')
+ self.assertRaises(TypeError, set, [s])
+ self.assertRaises(TypeError, hash, [s])
+
+ def testConstBitStreamSetCreation(self):
+ sl = [ConstBitStream(uint=i, length=7) for i in range(15)]
+ s = set(sl)
+ self.assertEqual(len(s), 15)
+ s.add(ConstBitStream('0b0000011'))
+ self.assertEqual(len(s), 15)
+ self.assertRaises(TypeError, s.add, BitStream('0b0000011'))
+
+ def testConstBitStreamFunctions(self):
+ s = ConstBitStream('0xf, 0b1')
+ self.assertEqual(type(s), ConstBitStream)
+ t = copy.copy(s)
+ self.assertEqual(type(t), ConstBitStream)
+ a = s + '0o3'
+ self.assertEqual(type(a), ConstBitStream)
+ b = a[0:4]
+ self.assertEqual(type(b), ConstBitStream)
+ b = a[4:3]
+ self.assertEqual(type(b), ConstBitStream)
+ b = a[5:2:-1]
+ self.assertEqual(type(b), ConstBitStream)
+ b = ~a
+ self.assertEqual(type(b), ConstBitStream)
+ b = a << 2
+ self.assertEqual(type(b), ConstBitStream)
+ b = a >> 2
+ self.assertEqual(type(b), ConstBitStream)
+ b = a * 2
+ self.assertEqual(type(b), ConstBitStream)
+ b = a * 0
+ self.assertEqual(type(b), ConstBitStream)
+ b = a & ~a
+ self.assertEqual(type(b), ConstBitStream)
+ b = a | ~a
+ self.assertEqual(type(b), ConstBitStream)
+ b = a ^ ~a
+ self.assertEqual(type(b), ConstBitStream)
+ b = a._slice(4, 4)
+ self.assertEqual(type(b), ConstBitStream)
+ b = a.read(4)
+ self.assertEqual(type(b), ConstBitStream)
+
+ def testConstBitStreamProperties(self):
+ a = ConstBitStream('0x123123')
+ try:
+ a.hex = '0x234'
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.oct = '0o234'
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.bin = '0b101'
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.ue = 3453
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.se = -123
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.int = 432
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.uint = 4412
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.intle = 123
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.uintle = 4412
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.intbe = 123
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.uintbe = 4412
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.intne = 123
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.uintne = 4412
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+ try:
+ a.bytes = b'hello'
+ self.assertTrue(False)
+ except AttributeError:
+ pass
+
+ def testConstBitStreamMisc(self):
+ a = ConstBitStream('0xf')
+ b = a
+ a += '0xe'
+ self.assertEqual(b, '0xf')
+ self.assertEqual(a, '0xfe')
+ c = BitStream(a)
+ self.assertEqual(a, c)
+ a = ConstBitStream('0b1')
+ a._append(a)
+ self.assertEqual(a, '0b11')
+ self.assertEqual(type(a), ConstBitStream)
+ a._prepend(a)
+ self.assertEqual(a, '0b1111')
+ self.assertEqual(type(a), ConstBitStream)
+
+ def testConstBitStreamHashibility(self):
+ a = ConstBitStream('0x1')
+ b = ConstBitStream('0x2')
+ c = ConstBitStream('0x1')
+ c.pos = 3
+ s = set((a, b, c))
+ self.assertEqual(len(s), 2)
+ self.assertEqual(hash(a), hash(c))
+
+ def testConstBitStreamCopy(self):
+ a = ConstBitStream('0xabc')
+ a.pos = 11
+ b = copy.copy(a)
+ b.pos = 4
+ self.assertEqual(id(a._datastore), id(b._datastore))
+ self.assertEqual(a.pos, 11)
+ self.assertEqual(b.pos, 4)
+
+ def testPython26stuff(self):
+ s = BitStream('0xff')
+ self.assertTrue(isinstance(s.tobytes(), bytes))
+ self.assertTrue(isinstance(s.bytes, bytes))
+
+ def testReadFromBits(self):
+ a = ConstBitStream('0xaabbccdd')
+ b = a.read(8)
+ self.assertEqual(b, '0xaa')
+ self.assertEqual(a[0:8], '0xaa')
+ self.assertEqual(a[-1], True)
+ a.pos = 0
+ self.assertEqual(a.read(4).uint, 10)
+
+
+class Set(unittest.TestCase):
+ def testSet(self):
+ a = BitStream(length=16)
+ a.set(True, 0)
+ self.assertEqual(a, '0b10000000 00000000')
+ a.set(1, 15)
+ self.assertEqual(a, '0b10000000 00000001')
+ b = a[4:12]
+ b.set(True, 1)
+ self.assertEqual(b, '0b01000000')
+ b.set(True, -1)
+ self.assertEqual(b, '0b01000001')
+ b.set(1, -8)
+ self.assertEqual(b, '0b11000001')
+ self.assertRaises(IndexError, b.set, True, -9)
+ self.assertRaises(IndexError, b.set, True, 8)
+
+ def testSetNegativeIndex(self):
+ a = BitStream(10)
+ a.set(1, -1)
+ self.assertEqual(a.bin, '0000000001')
+ a.set(1, [-1, -10])
+ self.assertEqual(a.bin, '1000000001')
+ self.assertRaises(IndexError, a.set, 1, [-11])
+
+ def testFileBasedSetUnset(self):
+ a = BitStream(filename='test.m1v')
+ a.set(True, (0, 1, 2, 3, 4))
+ self.assertEqual(a[0:32], '0xf80001b3')
+ a = BitStream(filename='test.m1v')
+ a.set(False, (28, 29, 30, 31))
+ self.assertTrue(a.startswith('0x000001b0'))
+
+ def testSetList(self):
+ a = BitStream(length=18)
+ a.set(True, range(18))
+ self.assertEqual(a.int, -1)
+ a.set(False, range(18))
+ self.assertEqual(a.int, 0)
+
+ def testUnset(self):
+ a = BitStream(length=16, int=-1)
+ a.set(False, 0)
+ self.assertEqual(~a, '0b10000000 00000000')
+ a.set(0, 15)
+ self.assertEqual(~a, '0b10000000 00000001')
+ b = a[4:12]
+ b.set(False, 1)
+ self.assertEqual(~b, '0b01000000')
+ b.set(False, -1)
+ self.assertEqual(~b, '0b01000001')
+ b.set(False, -8)
+ self.assertEqual(~b, '0b11000001')
+ self.assertRaises(IndexError, b.set, False, -9)
+ self.assertRaises(IndexError, b.set, False, 8)
+
+ def testSetWholeBitStream(self):
+ a = BitStream(14)
+ a.set(1)
+ self.assertTrue(a.all(1))
+ a.set(0)
+ self.assertTrue(a.all(0))
+
+
+class Invert(unittest.TestCase):
+ def testInvertBits(self):
+ a = BitStream('0b111000')
+ a.invert(range(a.len))
+ self.assertEqual(a, '0b000111')
+ a.invert([0, 1, -1])
+ self.assertEqual(a, '0b110110')
+
+ def testInvertWholeBitStream(self):
+ a = BitStream('0b11011')
+ a.invert()
+ self.assertEqual(a, '0b00100')
+
+ def testInvertSingleBit(self):
+ a = BitStream('0b000001')
+ a.invert(0)
+ self.assertEqual(a.bin, '100001')
+ a.invert(-1)
+ self.assertEqual(a.bin, '100000')
+
+ def testInvertErrors(self):
+ a = BitStream(10)
+ self.assertRaises(IndexError, a.invert, 10)
+ self.assertRaises(IndexError, a.invert, -11)
+ self.assertRaises(IndexError, a.invert, [1, 2, 10])
+
+
+ #######################
+
+ def testIor(self):
+ a = BitStream('0b1101001')
+ a |= '0b1110000'
+ self.assertEqual(a, '0b1111001')
+ b = a[2:]
+ c = a[1:-1]
+ b |= c
+ self.assertEqual(c, '0b11100')
+ self.assertEqual(b, '0b11101')
+
+ def testIand(self):
+ a = BitStream('0b0101010101000')
+ a &= '0b1111110000000'
+ self.assertEqual(a, '0b0101010000000')
+ s = BitStream(filename='test.m1v', offset=26, length=24)
+ s &= '0xff00ff'
+ self.assertEqual(s, '0xcc0004')
+
+ def testIxor(self):
+ a = BitStream('0b11001100110011')
+ a ^= '0b11111100000010'
+ self.assertEqual(a, '0b00110000110001')
+
+ def testLogicalInplaceErrors(self):
+ a = BitStream(4)
+ self.assertRaises(ValueError, a.__ior__, '0b111')
+ self.assertRaises(ValueError, a.__iand__, '0b111')
+ self.assertRaises(ValueError, a.__ixor__, '0b111')
+
+
+class AllAndAny(unittest.TestCase):
+ def testAll(self):
+ a = BitStream('0b0111')
+ self.assertTrue(a.all(True, (1, 3)))
+ self.assertFalse(a.all(True, (0, 1, 2)))
+ self.assertTrue(a.all(True, [-1]))
+ self.assertFalse(a.all(True, [0]))
+
+ def testFileBasedAll(self):
+ a = BitStream(filename='test.m1v')
+ self.assertTrue(a.all(True, [31]))
+ a = BitStream(filename='test.m1v')
+ self.assertTrue(a.all(False, (0, 1, 2, 3, 4)))
+
+ def testFileBasedAny(self):
+ a = BitStream(filename='test.m1v')
+ self.assertTrue(a.any(True, (31, 12)))
+ a = BitStream(filename='test.m1v')
+ self.assertTrue(a.any(False, (0, 1, 2, 3, 4)))
+
+ def testAny(self):
+ a = BitStream('0b10011011')
+ self.assertTrue(a.any(True, (1, 2, 3, 5)))
+ self.assertFalse(a.any(True, (1, 2, 5)))
+ self.assertTrue(a.any(True, (-1,)))
+ self.assertFalse(a.any(True, (1,)))
+
+ def testAllFalse(self):
+ a = BitStream('0b0010011101')
+ self.assertTrue(a.all(False, (0, 1, 3, 4)))
+ self.assertFalse(a.all(False, (0, 1, 2, 3, 4)))
+
+ def testAnyFalse(self):
+ a = BitStream('0b01001110110111111111111111111')
+ self.assertTrue(a.any(False, (4, 5, 6, 2)))
+ self.assertFalse(a.any(False, (1, 15, 20)))
+
+ def testAnyEmptyBitstring(self):
+ a = ConstBitStream()
+ self.assertFalse(a.any(True))
+ self.assertFalse(a.any(False))
+
+ def testAllEmptyBitStream(self):
+ a = ConstBitStream()
+ self.assertTrue(a.all(True))
+ self.assertTrue(a.all(False))
+
+ def testAnyWholeBitstring(self):
+ a = ConstBitStream('0xfff')
+ self.assertTrue(a.any(True))
+ self.assertFalse(a.any(False))
+
+ def testAllWholeBitstring(self):
+ a = ConstBitStream('0xfff')
+ self.assertTrue(a.all(True))
+ self.assertFalse(a.all(False))
+
+ def testErrors(self):
+ a = BitStream('0xf')
+ self.assertRaises(IndexError, a.all, True, [5])
+ self.assertRaises(IndexError, a.all, True, [-5])
+ self.assertRaises(IndexError, a.any, True, [5])
+ self.assertRaises(IndexError, a.any, True, [-5])
+
+ ###################
+
+ def testFloatInitialisation(self):
+ for f in (0.0000001, -1.0, 1.0, 0.2, -3.1415265, 1.331e32):
+ a = BitStream(float=f, length=64)
+ a.pos = 6
+ self.assertEqual(a.float, f)
+ a = BitStream('float:64=%s' % str(f))
+ a.pos = 6
+ self.assertEqual(a.float, f)
+ a = BitStream('floatbe:64=%s' % str(f))
+ a.pos = 6
+ self.assertEqual(a.floatbe, f)
+ a = BitStream('floatle:64=%s' % str(f))
+ a.pos = 6
+ self.assertEqual(a.floatle, f)
+ a = BitStream('floatne:64=%s' % str(f))
+ a.pos = 6
+ self.assertEqual(a.floatne, f)
+ b = BitStream(float=f, length=32)
+ b.pos = 6
+ self.assertAlmostEqual(b.float / f, 1.0)
+ b = BitStream('float:32=%s' % str(f))
+ b.pos = 6
+ self.assertAlmostEqual(b.float / f, 1.0)
+ b = BitStream('floatbe:32=%s' % str(f))
+ b.pos = 6
+ self.assertAlmostEqual(b.floatbe / f, 1.0)
+ b = BitStream('floatle:32=%s' % str(f))
+ b.pos = 6
+ self.assertAlmostEqual(b.floatle / f, 1.0)
+ b = BitStream('floatne:32=%s' % str(f))
+ b.pos = 6
+ self.assertAlmostEqual(b.floatne / f, 1.0)
+ a = BitStream('0x12345678')
+ a.pos = 6
+ a.float = 23
+ self.assertEqual(a.float, 23.0)
+
+ def testFloatInitStrings(self):
+ for s in ('5', '+0.0001', '-1e101', '4.', '.2', '-.65', '43.21E+32'):
+ a = BitStream('float:64=%s' % s)
+ self.assertEqual(a.float, float(s))
+
+ def testFloatPacking(self):
+ a = pack('>d', 0.01)
+ self.assertEqual(a.float, 0.01)
+ self.assertEqual(a.floatbe, 0.01)
+ a.byteswap()
+ self.assertEqual(a.floatle, 0.01)
+ b = pack('>f', 1e10)
+ self.assertAlmostEqual(b.float / 1e10, 1.0)
+ c = pack('<f', 10.3)
+ self.assertAlmostEqual(c.floatle / 10.3, 1.0)
+ d = pack('>5d', 10.0, 5.0, 2.5, 1.25, 0.1)
+ self.assertEqual(d.unpack('>5d'), [10.0, 5.0, 2.5, 1.25, 0.1])
+
+ def testFloatReading(self):
+ a = BitStream('floatle:64=12, floatbe:64=-0.01, floatne:64=3e33')
+ x, y, z = a.readlist('floatle:64, floatbe:64, floatne:64')
+ self.assertEqual(x, 12.0)
+ self.assertEqual(y, -0.01)
+ self.assertEqual(z, 3e33)
+ a = BitStream('floatle:32=12, floatbe:32=-0.01, floatne:32=3e33')
+ x, y, z = a.readlist('floatle:32, floatbe:32, floatne:32')
+ self.assertAlmostEqual(x / 12.0, 1.0)
+ self.assertAlmostEqual(y / -0.01, 1.0)
+ self.assertAlmostEqual(z / 3e33, 1.0)
+ a = BitStream('0b11, floatle:64=12, 0xfffff')
+ a.pos = 2
+ self.assertEqual(a.read('floatle:64'), 12.0)
+ b = BitStream(floatle=20, length=32)
+ b.floatle = 10.0
+ b = [0] + b
+ self.assertEqual(b[1:].floatle, 10.0)
+
+ def testNonAlignedFloatReading(self):
+ s = BitStream('0b1, float:32 = 10.0')
+ x, y = s.readlist('1, float:32')
+ self.assertEqual(y, 10.0)
+ s[1:] = 'floatle:32=20.0'
+ x, y = s.unpack('1, floatle:32')
+ self.assertEqual(y, 20.0)
+
+ def testFloatErrors(self):
+ a = BitStream('0x3')
+ self.assertRaises(bitstring.InterpretError, a._getfloat)
+ self.assertRaises(bitstring.CreationError, a._setfloat, -0.2)
+ for l in (8, 10, 12, 16, 30, 128, 200):
+ self.assertRaises(ValueError, BitStream, float=1.0, length=l)
+ self.assertRaises(bitstring.CreationError, BitStream, floatle=0.3, length=0)
+ self.assertRaises(bitstring.CreationError, BitStream, floatle=0.3, length=1)
+ self.assertRaises(bitstring.CreationError, BitStream, float=2)
+ self.assertRaises(bitstring.InterpretError, a.read, 'floatle:2')
+
+ def testReadErrorChangesPos(self):
+ a = BitStream('0x123123')
+ try:
+ a.read('10, 5')
+ except ValueError:
+ pass
+ self.assertEqual(a.pos, 0)
+
+ def testRor(self):
+ a = BitStream('0b11001')
+ a.ror(0)
+ self.assertEqual(a, '0b11001')
+ a.ror(1)
+ self.assertEqual(a, '0b11100')
+ a.ror(5)
+ self.assertEqual(a, '0b11100')
+ a.ror(101)
+ self.assertEqual(a, '0b01110')
+ a = BitStream('0b1')
+ a.ror(1000000)
+ self.assertEqual(a, '0b1')
+
+ def testRorErrors(self):
+ a = BitStream()
+ self.assertRaises(bitstring.Error, a.ror, 0)
+ a += '0b001'
+ self.assertRaises(ValueError, a.ror, -1)
+
+ def testRol(self):
+ a = BitStream('0b11001')
+ a.rol(0)
+ self.assertEqual(a, '0b11001')
+ a.rol(1)
+ self.assertEqual(a, '0b10011')
+ a.rol(5)
+ self.assertEqual(a, '0b10011')
+ a.rol(101)
+ self.assertEqual(a, '0b00111')
+ a = BitStream('0b1')
+ a.rol(1000000)
+ self.assertEqual(a, '0b1')
+
+ def testRolFromFile(self):
+ a = BitStream(filename='test.m1v')
+ l = a.len
+ a.rol(1)
+ self.assertTrue(a.startswith('0x000003'))
+ self.assertEqual(a.len, l)
+ self.assertTrue(a.endswith('0x0036e'))
+
+ def testRorFromFile(self):
+ a = BitStream(filename='test.m1v')
+ l = a.len
+ a.ror(1)
+ self.assertTrue(a.startswith('0x800000'))
+ self.assertEqual(a.len, l)
+ self.assertTrue(a.endswith('0x000db'))
+
+ def testRolErrors(self):
+ a = BitStream()
+ self.assertRaises(bitstring.Error, a.rol, 0)
+ a += '0b001'
+ self.assertRaises(ValueError, a.rol, -1)
+
+ def testBytesToken(self):
+ a = BitStream('0x010203')
+ b = a.read('bytes:1')
+ self.assertTrue(isinstance(b, bytes))
+ self.assertEqual(b, b'\x01')
+ x, y, z = a.unpack('4, bytes:2, uint')
+ self.assertEqual(x, 0)
+ self.assertEqual(y, b'\x10\x20')
+ self.assertEqual(z, 3)
+ s = pack('bytes:4', b'abcd')
+ self.assertEqual(s.bytes, b'abcd')
+
+ def testBytesTokenMoreThoroughly(self):
+ a = BitStream('0x0123456789abcdef')
+ a.pos += 16
+ self.assertEqual(a.read('bytes:1'), b'\x45')
+ self.assertEqual(a.read('bytes:3'), b'\x67\x89\xab')
+ x, y, z = a.unpack('bits:28, bytes, bits:12')
+ self.assertEqual(y, b'\x78\x9a\xbc')
+
+ def testDedicatedReadFunctions(self):
+ a = BitStream('0b11, uint:43=98798798172, 0b11111')
+ x = a._readuint(43, 2)
+ self.assertEqual(x, 98798798172)
+ self.assertEqual(a.pos, 0)
+ x = a._readint(43, 2)
+ self.assertEqual(x, 98798798172)
+ self.assertEqual(a.pos, 0)
+
+ a = BitStream('0b11, uintbe:48=98798798172, 0b11111')
+ x = a._readuintbe(48, 2)
+ self.assertEqual(x, 98798798172)
+ self.assertEqual(a.pos, 0)
+ x = a._readintbe(48, 2)
+ self.assertEqual(x, 98798798172)
+ self.assertEqual(a.pos, 0)
+
+ a = BitStream('0b111, uintle:40=123516, 0b111')
+ self.assertEqual(a._readuintle(40, 3), 123516)
+ b = BitStream('0xff, uintle:800=999, 0xffff')
+ self.assertEqual(b._readuintle(800, 8), 999)
+
+ a = BitStream('0b111, intle:48=999999999, 0b111111111111')
+ self.assertEqual(a._readintle(48, 3), 999999999)
+ b = BitStream('0xff, intle:200=918019283740918263512351235, 0xfffffff')
+ self.assertEqual(b._readintle(200, 8), 918019283740918263512351235)
+
+ a = BitStream('0b111, floatbe:64=-5.32, 0xffffffff')
+ self.assertEqual(a._readfloat(64, 3), -5.32)
+
+ a = BitStream('0b111, floatle:64=9.9998, 0b111')
+ self.assertEqual(a._readfloatle(64, 3), 9.9998)
+
+ def testAutoInitWithInt(self):
+ a = BitStream(0)
+ self.assertFalse(a)
+ a = BitStream(1)
+ self.assertEqual(a, '0b0')
+ a = BitStream(1007)
+ self.assertEqual(a, BitStream(length=1007))
+ self.assertRaises(bitstring.CreationError, BitStream, -1)
+
+ a = 6 + ConstBitStream('0b1') + 3
+ self.assertEqual(a, '0b0000001000')
+ a += 1
+ self.assertEqual(a, '0b00000010000')
+ self.assertEqual(ConstBitStream(13), 13)
+
+ def testReadingProblems(self):
+ a = BitStream('0x000001')
+ b = a.read('uint:24')
+ self.assertEqual(b, 1)
+ a.pos = 0
+ self.assertRaises(bitstring.ReadError, a.read, 'bytes:4')
+
+ def testAddVersesInPlaceAdd(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 += '0xdef'
+ self.assertEqual(a1, '0xabcdef')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 + '0x0'
+ a2 += '0xdef'
+ self.assertEqual(a2, '0xabcdef')
+ self.assertEqual(b2, '0xabcdef')
+ self.assertEqual(c2, '0xabc0')
+
+ def testAndVersesInPlaceAnd(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 &= '0xf0f'
+ self.assertEqual(a1, '0xa0c')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 & '0x00f'
+ a2 &= '0xf0f'
+ self.assertEqual(a2, '0xa0c')
+ self.assertEqual(b2, '0xa0c')
+ self.assertEqual(c2, '0x00c')
+
+ def testOrVersesInPlaceOr(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 |= '0xf0f'
+ self.assertEqual(a1, '0xfbf')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 | '0x00f'
+ a2 |= '0xf0f'
+ self.assertEqual(a2, '0xfbf')
+ self.assertEqual(b2, '0xfbf')
+ self.assertEqual(c2, '0xabf')
+
+ def testXorVersesInPlaceXor(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 ^= '0xf0f'
+ self.assertEqual(a1, '0x5b3')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 ^ '0x00f'
+ a2 ^= '0xf0f'
+ self.assertEqual(a2, '0x5b3')
+ self.assertEqual(b2, '0x5b3')
+ self.assertEqual(c2, '0xab3')
+
+ def testMulVersesInPlaceMul(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 *= 3
+ self.assertEqual(a1, '0xabcabcabc')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 * 2
+ a2 *= 3
+ self.assertEqual(a2, '0xabcabcabc')
+ self.assertEqual(b2, '0xabcabcabc')
+ self.assertEqual(c2, '0xabcabc')
+
+ def testLshiftVersesInPlaceLshift(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 <<= 4
+ self.assertEqual(a1, '0xbc0')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 << 8
+ a2 <<= 4
+ self.assertEqual(a2, '0xbc0')
+ self.assertEqual(b2, '0xbc0')
+ self.assertEqual(c2, '0xc00')
+
+ def testRshiftVersesInPlaceRshift(self):
+ a1 = ConstBitStream('0xabc')
+ b1 = a1
+ a1 >>= 4
+ self.assertEqual(a1, '0x0ab')
+ self.assertEqual(b1, '0xabc')
+
+ a2 = BitStream('0xabc')
+ b2 = a2
+ c2 = a2 >> 8
+ a2 >>= 4
+ self.assertEqual(a2, '0x0ab')
+ self.assertEqual(b2, '0x0ab')
+ self.assertEqual(c2, '0x00a')
+
+ def testAutoFromBool(self):
+ a = ConstBitStream() + True + False + True
+ self.assertEqual(a, '0b00')
+ # self.assertEqual(a, '0b101')
+ # b = ConstBitStream(False)
+ # self.assertEqual(b, '0b0')
+ # c = ConstBitStream(True)
+ # self.assertEqual(c, '0b1')
+ # self.assertEqual(b, False)
+ # self.assertEqual(c, True)
+ # self.assertEqual(b & True, False)
+
+
+class Bugs(unittest.TestCase):
+ def testBugInReplace(self):
+ s = BitStream('0x00112233')
+ l = list(s.split('0x22', start=8, bytealigned=True))
+ self.assertEqual(l, ['0x11', '0x2233'])
+ s = BitStream('0x00112233')
+ s.replace('0x22', '0xffff', start=8, bytealigned=True)
+ self.assertEqual(s, '0x0011ffff33')
+ s = BitStream('0x0123412341234')
+ s.replace('0x23', '0xf', start=9, bytealigned=True)
+ self.assertEqual(s, '0x012341f41f4')
+
+ def testTruncateStartBug(self):
+ a = BitStream('0b000000111')[2:]
+ a._truncatestart(6)
+ self.assertEqual(a, '0b1')
+
+ def testNullBits(self):
+ s = ConstBitStream(bin='')
+ t = ConstBitStream(oct='')
+ u = ConstBitStream(hex='')
+ v = ConstBitStream(bytes=b'')
+ self.assertFalse(s)
+ self.assertFalse(t)
+ self.assertFalse(u)
+ self.assertFalse(v)
+
+ def testMultiplicativeFactorsCreation(self):
+ s = BitStream('1*0b1')
+ self.assertEqual(s, '0b1')
+ s = BitStream('4*0xc')
+ self.assertEqual(s, '0xcccc')
+ s = BitStream('0b1, 0*0b0')
+ self.assertEqual(s, '0b1')
+ s = BitStream('0b1, 3*uint:8=34, 2*0o755')
+ self.assertEqual(s, '0b1, uint:8=34, uint:8=34, uint:8=34, 0o755755')
+ s = BitStream('0*0b1001010')
+ self.assertFalse(s)
+
+ def testMultiplicativeFactorsReading(self):
+ s = BitStream('0xc') * 5
+ a, b, c, d, e = s.readlist('5*4')
+ self.assertTrue(a == b == c == d == e == 12)
+ s = ConstBitStream('2*0b101, 4*uint:7=3')
+ a, b, c, d, e = s.readlist('2*bin:3, 3*uint:7')
+ self.assertTrue(a == b == '101')
+ self.assertTrue(c == d == e == 3)
+
+ def testMultiplicativeFactorsPacking(self):
+ s = pack('3*bin', '1', '001', '101')
+ self.assertEqual(s, '0b1001101')
+ s = pack('hex, 2*se=-56, 3*uint:37', '34', 1, 2, 3)
+ a, b, c, d, e, f = s.unpack('hex:8, 2*se, 3*uint:37')
+ self.assertEqual(a, '34')
+ self.assertEqual(b, -56)
+ self.assertEqual(c, -56)
+ self.assertEqual((d, e, f), (1, 2, 3))
+ # This isn't allowed yet. See comment in tokenparser.
+ #s = pack('fluffy*uint:8', *range(3), fluffy=3)
+ #a, b, c = s.readlist('2*uint:8, 1*uint:8, 0*uint:8')
+ #self.assertEqual((a, b, c), (0, 1, 2))
+
+ def testMultiplicativeFactorsUnpacking(self):
+ s = ConstBitStream('0b10111')
+ a, b, c, d = s.unpack('3*bool, bin')
+ self.assertEqual((a, b, c), (True, False, True))
+ self.assertEqual(d, '11')
+
+
+ def testPackingDefaultIntWithKeyword(self):
+ s = pack('12', 100)
+ self.assertEqual(s.unpack('12')[0], 100)
+ s = pack('oh_no_not_the_eyes=33', oh_no_not_the_eyes=17)
+ self.assertEqual(s.uint, 33)
+ self.assertEqual(s.len, 17)
+
+ def testInitFromIterable(self):
+ self.assertTrue(isinstance(range(10), collections.Iterable))
+ s = ConstBitStream(range(12))
+ self.assertEqual(s, '0x7ff')
+
+ def testFunctionNegativeIndices(self):
+ # insert
+ s = BitStream('0b0111')
+ s.insert('0b0', -1)
+ self.assertEqual(s, '0b01101')
+ self.assertRaises(ValueError, s.insert, '0b0', -1000)
+
+ # reverse
+ s.reverse(-2)
+ self.assertEqual(s, '0b01110')
+ t = BitStream('0x778899abcdef')
+ t.reverse(-12, -4)
+ self.assertEqual(t, '0x778899abc7bf')
+
+ # reversebytes
+ t.byteswap(0, -40, -16)
+ self.assertEqual(t, '0x77ab9988c7bf')
+
+ # overwrite
+ t.overwrite('0x666', -20)
+ self.assertEqual(t, '0x77ab998666bf')
+
+ # find
+ found = t.find('0x998', bytealigned=True, start=-31)
+ self.assertFalse(found)
+ found = t.find('0x998', bytealigned=True, start=-32)
+ self.assertTrue(found)
+ self.assertEqual(t.pos, 16)
+ t.pos = 0
+ found = t.find('0x988', bytealigned=True, end=-21)
+ self.assertFalse(found)
+ found = t.find('0x998', bytealigned=True, end=-20)
+ self.assertTrue(found)
+ self.assertEqual(t.pos, 16)
+
+ #findall
+ s = BitStream('0x1234151f')
+ l = list(s.findall('0x1', bytealigned=True, start=-15))
+ self.assertEqual(l, [24])
+ l = list(s.findall('0x1', bytealigned=True, start=-16))
+ self.assertEqual(l, [16, 24])
+ l = list(s.findall('0x1', bytealigned=True, end=-5))
+ self.assertEqual(l, [0, 16])
+ l = list(s.findall('0x1', bytealigned=True, end=-4))
+ self.assertEqual(l, [0, 16, 24])
+
+ # rfind
+ found = s.rfind('0x1f', end=-1)
+ self.assertFalse(found)
+ found = s.rfind('0x12', start=-31)
+ self.assertFalse(found)
+
+ # cut
+ s = BitStream('0x12345')
+ l = list(s.cut(4, start=-12, end=-4))
+ self.assertEqual(l, ['0x3', '0x4'])
+
+ # split
+ s = BitStream('0xfe0012fe1200fe')
+ l = list(s.split('0xfe', bytealigned=True, end=-1))
+ self.assertEqual(l, ['', '0xfe0012', '0xfe1200f, 0b111'])
+ l = list(s.split('0xfe', bytealigned=True, start=-8))
+ self.assertEqual(l, ['', '0xfe'])
+
+ # startswith
+ self.assertTrue(s.startswith('0x00f', start=-16))
+ self.assertTrue(s.startswith('0xfe00', end=-40))
+ self.assertFalse(s.startswith('0xfe00', end=-41))
+
+ # endswith
+ self.assertTrue(s.endswith('0x00fe', start=-16))
+ self.assertFalse(s.endswith('0x00fe', start=-15))
+ self.assertFalse(s.endswith('0x00fe', end=-1))
+ self.assertTrue(s.endswith('0x00f', end=-4))
+
+ # replace
+ s.replace('0xfe', '', end=-1)
+ self.assertEqual(s, '0x00121200fe')
+ s.replace('0x00', '', start=-24)
+ self.assertEqual(s, '0x001212fe')
+
+ def testRotateStartAndEnd(self):
+ a = BitStream('0b110100001')
+ a.rol(1, 3, 6)
+ self.assertEqual(a, '0b110001001')
+ a.ror(1, start=-4)
+ self.assertEqual(a, '0b110001100')
+ a.rol(202, end=-5)
+ self.assertEqual(a, '0b001101100')
+ a.ror(3, end=4)
+ self.assertEqual(a, '0b011001100')
+ self.assertRaises(ValueError, a.rol, 5, start=-4, end=-6)
+
+ def testByteSwapInt(self):
+ s = pack('5*uintle:16', *range(10, 15))
+ self.assertEqual(list(range(10, 15)), s.unpack('5*uintle:16'))
+ swaps = s.byteswap(2)
+ self.assertEqual(list(range(10, 15)), s.unpack('5*uintbe:16'))
+ self.assertEqual(swaps, 5)
+ s = BitStream('0xf234567f')
+ swaps = s.byteswap(1, start=4)
+ self.assertEqual(swaps, 3)
+ self.assertEqual(s, '0xf234567f')
+ s.byteswap(2, start=4)
+ self.assertEqual(s, '0xf452367f')
+ s.byteswap(2, start=4, end=-4)
+ self.assertEqual(s, '0xf234567f')
+ s.byteswap(3)
+ self.assertEqual(s, '0x5634f27f')
+ s.byteswap(2, repeat=False)
+ self.assertEqual(s, '0x3456f27f')
+ swaps = s.byteswap(5)
+ self.assertEqual(swaps, 0)
+ swaps = s.byteswap(4, repeat=False)
+ self.assertEqual(swaps, 1)
+ self.assertEqual(s, '0x7ff25634')
+
+ def testByteSwapPackCode(self):
+ s = BitStream('0x0011223344556677')
+ swaps = s.byteswap('b')
+ self.assertEqual(s, '0x0011223344556677')
+ self.assertEqual(swaps, 8)
+ swaps = s.byteswap('>3h', repeat=False)
+ self.assertEqual(s, '0x1100332255446677')
+ self.assertEqual(swaps, 1)
+
+ def testByteSwapIterable(self):
+ s = BitStream('0x0011223344556677')
+ swaps = s.byteswap(range(1, 4), repeat=False)
+ self.assertEqual(swaps, 1)
+ self.assertEqual(s, '0x0022115544336677')
+ swaps = s.byteswap([2], start=8)
+ self.assertEqual(s, '0x0011224455663377')
+ self.assertEqual(3, swaps)
+ swaps = s.byteswap([2, 3], start=4)
+ self.assertEqual(swaps, 1)
+ self.assertEqual(s, '0x0120156452463377')
+
+ def testByteSwapErrors(self):
+ s = BitStream('0x0011223344556677')
+ self.assertRaises(ValueError, s.byteswap, 'z')
+ self.assertRaises(ValueError, s.byteswap, -1)
+ self.assertRaises(ValueError, s.byteswap, [-1])
+ self.assertRaises(ValueError, s.byteswap, [1, 'e'])
+ self.assertRaises(ValueError, s.byteswap, '!h')
+ self.assertRaises(ValueError, s.byteswap, 2, start=-1000)
+ self.assertRaises(TypeError, s.byteswap, 5.4)
+
+ def testByteSwapFromFile(self):
+ s = BitStream(filename='smalltestfile')
+ swaps = s.byteswap('2bh')
+ self.assertEqual(s, '0x0123674589abefcd')
+ self.assertEqual(swaps, 2)
+
+ def testBracketExpander(self):
+ be = bitstring.expand_brackets
+ self.assertEqual(be('hello'), 'hello')
+ self.assertEqual(be('(hello)'), 'hello')
+ self.assertEqual(be('1*(hello)'), 'hello')
+ self.assertEqual(be('2*(hello)'), 'hello,hello')
+ self.assertEqual(be('1*(a, b)'), 'a,b')
+ self.assertEqual(be('2*(a, b)'), 'a,b,a,b')
+ self.assertEqual(be('2*(a), 3*(b)'), 'a,a,b,b,b')
+ self.assertEqual(be('2*(a, b, 3*(c, d), e)'), 'a,b,c,d,c,d,c,d,e,a,b,c,d,c,d,c,d,e')
+
+ def testBracketTokens(self):
+ s = BitStream('3*(0x0, 0b1)')
+ self.assertEqual(s, '0x0, 0b1, 0x0, 0b1, 0x0, 0b1')
+ s = pack('2*(uint:12, 3*(7, 6))', *range(3, 17))
+ a = s.unpack('12, 7, 6, 7, 6, 7, 6, 12, 7, 6, 7, 6, 7, 6')
+ self.assertEqual(a, list(range(3, 17)))
+ b = s.unpack('2*(12,3*(7,6))')
+ self.assertEqual(a, b)
+
+ def testPackCodeDicts(self):
+ self.assertEqual(sorted(bitstring.REPLACEMENTS_BE.keys()),
+ sorted(bitstring.REPLACEMENTS_LE.keys()))
+ self.assertEqual(sorted(bitstring.REPLACEMENTS_BE.keys()),
+ sorted(bitstring.PACK_CODE_SIZE.keys()))
+ for key in bitstring.PACK_CODE_SIZE:
+ be = pack(bitstring.REPLACEMENTS_BE[key], 0)
+ le = pack(bitstring.REPLACEMENTS_LE[key], 0)
+ self.assertEqual(be.len, bitstring.PACK_CODE_SIZE[key] * 8)
+ self.assertEqual(le.len, be.len)
+
+ # These tests don't compile for Python 3, so they're commented out to save me stress.
+ #def testUnicode(self):
+ #a = ConstBitStream(u'uint:12=34')
+ #self.assertEqual(a.uint, 34)
+ #a += u'0xfe'
+ #self.assertEqual(a[12:], '0xfe')
+ #a = BitStream('0x1122')
+ #c = a.byteswap(u'h')
+ #self.assertEqual(c, 1)
+ #self.assertEqual(a, u'0x2211')
+
+ #def testLongInt(self):
+ #a = BitStream(4L)
+ #self.assertEqual(a, '0b0000')
+ #a[1:3] = -1L
+ #self.assertEqual(a, '0b0110')
+ #a[0] = 1L
+ #self.assertEqual(a, '0b1110')
+ #a *= 4L
+ #self.assertEqual(a, '0xeeee')
+ #c = a.byteswap(2L)
+ #self.assertEqual(c, 1)
+ #a = BitStream('0x11223344')
+ #a.byteswap([1, 2L])
+ #self.assertEqual(a, '0x11332244')
+ #b = a*2L
+ #self.assertEqual(b, '0x1133224411332244')
+ #s = pack('uint:12', 46L)
+ #self.assertEqual(s.uint, 46)
+
+
+class UnpackWithDict(unittest.TestCase):
+ def testLengthKeywords(self):
+ a = ConstBitStream('2*13=100, 0b111')
+ x, y, z = a.unpack('n, uint:m, bin:q', n=13, m=13, q=3)
+ self.assertEqual(x, 100)
+ self.assertEqual(y, 100)
+ self.assertEqual(z, '111')
+
+ def testLengthKeywordsWithStretch(self):
+ a = ConstBitStream('0xff, 0b000, 0xf')
+ x, y, z = a.unpack('hex:a, bin, hex:b', a=8, b=4)
+ self.assertEqual(y, '000')
+
+ def testUnusedKeyword(self):
+ a = ConstBitStream('0b110')
+ x, = a.unpack('bin:3', notused=33)
+ self.assertEqual(x, '110')
+
+ def testLengthKeywordErrors(self):
+ a = pack('uint:p=33', p=12)
+ self.assertRaises(ValueError, a.unpack, 'uint:p')
+ self.assertRaises(ValueError, a.unpack, 'uint:p', p='a_string')
+
+
+class ReadWithDict(unittest.TestCase):
+ def testLengthKeywords(self):
+ s = BitStream('0x0102')
+ x, y = s.readlist('a, hex:b', a=8, b=4)
+ self.assertEqual((x, y), (1, '0'))
+ self.assertEqual(s.pos, 12)
+
+ def testBytesKeywordProblem(self):
+ s = BitStream('0x01')
+ x, = s.unpack('bytes:a', a=1)
+ self.assertEqual(x, b'\x01')
+
+ s = BitStream('0x000ff00a')
+ x, y, z = s.unpack('12, bytes:x, bits', x=2)
+ self.assertEqual((x, y, z), (0, b'\xff\x00', '0xa'))
+
+
+
+class PeekWithDict(unittest.TestCase):
+ def testLengthKeywords(self):
+ s = BitStream('0x0102')
+ x, y = s.peeklist('a, hex:b', a=8, b=4)
+ self.assertEqual((x, y), (1, '0'))
+ self.assertEqual(s.pos, 0)
+
+##class Miscellany(unittest.TestCase):
+##
+## def testNumpyInt(self):
+## try:
+## import numpy
+## a = ConstBitStream(uint=numpy.uint8(5), length=3)
+## self.assertEqual(a.uint, 5)
+## except ImportError:
+## # Not to worry
+## pass
+
+class BoolToken(unittest.TestCase):
+ def testInterpretation(self):
+ a = ConstBitStream('0b1')
+ self.assertEqual(a.bool, True)
+ self.assertEqual(a.read('bool'), True)
+ self.assertEqual(a.unpack('bool')[0], True)
+ b = ConstBitStream('0b0')
+ self.assertEqual(b.bool, False)
+ self.assertEqual(b.peek('bool'), False)
+ self.assertEqual(b.unpack('bool')[0], False)
+
+ def testPack(self):
+ a = pack('bool=True')
+ b = pack('bool=False')
+ self.assertEqual(a.bool, True)
+ self.assertEqual(b.bool, False)
+ c = pack('4*bool', False, True, 'False', 'True')
+ self.assertEqual(c, '0b0101')
+
+ def testAssignment(self):
+ a = BitStream()
+ a.bool = True
+ self.assertEqual(a.bool, True)
+ a.hex = 'ee'
+ a.bool = False
+ self.assertEqual(a.bool, False)
+ a.bool = 'False'
+ self.assertEqual(a.bool, False)
+ a.bool = 'True'
+ self.assertEqual(a.bool, True)
+ a.bool = 0
+ self.assertEqual(a.bool, False)
+ a.bool = 1
+ self.assertEqual(a.bool, True)
+
+ def testErrors(self):
+ self.assertRaises(bitstring.CreationError, pack, 'bool', 'hello')
+ self.assertRaises(bitstring.CreationError, pack, 'bool=true')
+ self.assertRaises(bitstring.CreationError, pack, 'True')
+ self.assertRaises(bitstring.CreationError, pack, 'bool', 2)
+ a = BitStream('0b11')
+ self.assertRaises(bitstring.InterpretError, a._getbool)
+ b = BitStream()
+ self.assertRaises(bitstring.InterpretError, a._getbool)
+ self.assertRaises(bitstring.CreationError, a._setbool, 'false')
+
+ def testLengthWithBoolRead(self):
+ a = ConstBitStream('0xf')
+ self.assertRaises(ValueError, a.read, 'bool:0')
+ self.assertRaises(ValueError, a.read, 'bool:1')
+ self.assertRaises(ValueError, a.read, 'bool:2')
+
+
+class ReadWithIntegers(unittest.TestCase):
+ def testReadInt(self):
+ a = ConstBitStream('0xffeedd')
+ b = a.read(8)
+ self.assertEqual(b.hex, 'ff')
+ self.assertEqual(a.pos, 8)
+ b = a.peek(8)
+ self.assertEqual(b.hex, 'ee')
+ self.assertEqual(a.pos, 8)
+ b = a.peek(1)
+ self.assertEqual(b, '0b1')
+ b = a.read(1)
+ self.assertEqual(b, '0b1')
+
+ def testReadIntList(self):
+ a = ConstBitStream('0xab, 0b110')
+ b, c = a.readlist([8, 3])
+ self.assertEqual(b.hex, 'ab')
+ self.assertEqual(c.bin, '110')
+
+
+class FileReadingStrategy(unittest.TestCase):
+ def testBitStreamIsAlwaysRead(self):
+ a = BitStream(filename='smalltestfile')
+ self.assertTrue(isinstance(a._datastore, bitstring.ByteStore))
+ f = open('smalltestfile', 'rb')
+ b = BitStream(f)
+ self.assertTrue(isinstance(b._datastore, bitstring.ByteStore))
+
+ def testBitsIsNeverRead(self):
+ a = ConstBitStream(filename='smalltestfile')
+ self.assertTrue(isinstance(a._datastore._rawarray, bitstring.MmapByteArray))
+ f = open('smalltestfile', 'rb')
+ b = ConstBitStream(f)
+ self.assertTrue(isinstance(b._datastore._rawarray, bitstring.MmapByteArray))
+
+
+class Count(unittest.TestCase):
+ def testCount(self):
+ a = ConstBitStream('0xf0f')
+ self.assertEqual(a.count(True), 8)
+ self.assertEqual(a.count(False), 4)
+
+ b = BitStream()
+ self.assertEqual(b.count(True), 0)
+ self.assertEqual(b.count(False), 0)
+
+ def testCountWithOffsetData(self):
+ a = ConstBitStream('0xff0120ff')
+ b = a[1:-1]
+ self.assertEqual(b.count(1), 16)
+ self.assertEqual(b.count(0), 14)
+
+
+class ZeroBitReads(unittest.TestCase):
+ def testInteger(self):
+ a = ConstBitStream('0x123456')
+ self.assertRaises(bitstring.InterpretError, a.read, 'uint:0')
+ self.assertRaises(bitstring.InterpretError, a.read, 'float:0')
+
+#class EfficientBitsCopies(unittest.TestCase):
+#
+# def testBitsCopy(self):
+# a = ConstBitStream('0xff')
+# b = ConstBitStream(a)
+# c = a[:]
+# d = copy.copy(a)
+# self.assertTrue(a._datastore is b._datastore)
+# self.assertTrue(a._datastore is c._datastore)
+# self.assertTrue(a._datastore is d._datastore)
+
+class InitialiseFromBytes(unittest.TestCase):
+ def testBytesBehaviour(self):
+ a = ConstBitStream(b'uint:5=2')
+ b = ConstBitStream(b'')
+ c = ConstBitStream(bytes=b'uint:5=2')
+ if b'' == '':
+ # Python 2
+ self.assertEqual(a, 'uint:5=2')
+ self.assertFalse(b)
+ self.assertEqual(c.bytes, b'uint:5=2')
+ else:
+ self.assertEqual(a.bytes, b'uint:5=2')
+ self.assertFalse(b)
+ self.assertEqual(c, b'uint:5=2')
+
+ def testBytearrayBehaviour(self):
+ a = ConstBitStream(bytearray(b'uint:5=2'))
+ b = ConstBitStream(bytearray(4))
+ c = ConstBitStream(bytes=bytearray(b'uint:5=2'))
+ self.assertEqual(a.bytes, b'uint:5=2')
+ self.assertEqual(b, '0x00000000')
+ self.assertEqual(c.bytes, b'uint:5=2')
+
+
+class CoverageCompletionTests(unittest.TestCase):
+ def testUeReadError(self):
+ s = ConstBitStream('0b000000001')
+ self.assertRaises(bitstring.ReadError, s.read, 'ue')
+
+ def testOverwriteWithSelf(self):
+ s = BitStream('0b1101')
+ s.overwrite(s)
+ self.assertEqual(s, '0b1101')
+
+
+class Subclassing(unittest.TestCase):
+
+ def testIsInstance(self):
+ class SubBits(BitStream): pass
+ a = SubBits()
+ self.assertTrue(isinstance(a, SubBits))
+
+ def testClassType(self):
+ class SubBits(BitStream): pass
+ self.assertEqual(SubBits().__class__, SubBits)
+
+
+class BytesProblems(unittest.TestCase):
+
+ def testOffsetButNoLength(self):
+ b = BitStream(bytes=b'\x00\xaa', offset=8)
+ self.assertEqual(b.hex, 'aa')
+ b = BitStream(bytes=b'\x00\xaa', offset=4)
+ self.assertEqual(b.hex, '0aa')
+
+ def testInvert(self):
+ b = BitStream(bytes=b'\x00\xaa', offset=8, length=8)
+ self.assertEqual(b.hex, 'aa')
+ b.invert()
+ self.assertEqual(b.hex, '55')
+
+ def testPrepend(self):
+ b = BitStream(bytes=b'\xaa\xbb', offset=8, length=4)
+ self.assertEqual(b.hex, 'b')
+ b.prepend('0xe')
+ self.assertEqual(b.hex, 'eb')
+ b = BitStream(bytes=b'\x00\xaa', offset=8, length=8)
+ b.prepend('0xee')
+ self.assertEqual(b.hex, 'eeaa')
+
+ def testByteSwap(self):
+ b = BitStream(bytes=b'\x01\x02\x03\x04', offset=8)
+ b.byteswap()
+ self.assertEqual(b, '0x040302')
+
+ def testBinProperty(self):
+ b = BitStream(bytes=b'\x00\xaa', offset=8, length=4)
+ self.assertEqual(b.bin, '1010') \ No newline at end of file
diff --git a/python/bitstring/test/test_bitstring.py b/python/bitstring/test/test_bitstring.py
new file mode 100644
index 0000000000..1b52b7b808
--- /dev/null
+++ b/python/bitstring/test/test_bitstring.py
@@ -0,0 +1,97 @@
+#!/usr/bin/env python
+"""
+Module-level unit tests.
+"""
+
+import unittest
+import sys
+sys.path.insert(0, '..')
+import bitstring
+import copy
+
+
+class ModuleData(unittest.TestCase):
+ def testVersion(self):
+ self.assertEqual(bitstring.__version__, '3.1.3')
+
+ def testAll(self):
+ exported = ['ConstBitArray', 'ConstBitStream', 'BitStream', 'BitArray',
+ 'Bits', 'BitString', 'pack', 'Error', 'ReadError',
+ 'InterpretError', 'ByteAlignError', 'CreationError', 'bytealigned']
+ self.assertEqual(set(bitstring.__all__), set(exported))
+
+ def testReverseDict(self):
+ d = bitstring.BYTE_REVERSAL_DICT
+ for i in range(256):
+ a = bitstring.Bits(uint=i, length=8)
+ b = d[i]
+ self.assertEqual(a.bin[::-1], bitstring.Bits(bytes=b).bin)
+
+ def testAliases(self):
+ self.assertTrue(bitstring.Bits is bitstring.ConstBitArray)
+ self.assertTrue(bitstring.BitStream is bitstring.BitString)
+
+
+class MemoryUsage(unittest.TestCase):
+ def testBaselineMemory(self):
+ try:
+ import pympler.asizeof.asizeof as size
+ except ImportError:
+ return
+ # These values might be platform dependent, so don't fret too much.
+ self.assertEqual(size(bitstring.ConstBitStream([0])), 64)
+ self.assertEqual(size(bitstring.Bits([0])), 64)
+ self.assertEqual(size(bitstring.BitStream([0])), 64)
+ self.assertEqual(size(bitstring.BitArray([0])), 64)
+ from bitstring.bitstore import ByteStore
+ self.assertEqual(size(ByteStore(bytearray())), 100)
+
+
+class Copy(unittest.TestCase):
+ def testConstBitArrayCopy(self):
+ import copy
+ cba = bitstring.Bits(100)
+ cba_copy = copy.copy(cba)
+ self.assertTrue(cba is cba_copy)
+
+ def testBitArrayCopy(self):
+ ba = bitstring.BitArray(100)
+ ba_copy = copy.copy(ba)
+ self.assertFalse(ba is ba_copy)
+ self.assertFalse(ba._datastore is ba_copy._datastore)
+ self.assertTrue(ba == ba_copy)
+
+ def testConstBitStreamCopy(self):
+ cbs = bitstring.ConstBitStream(100)
+ cbs.pos = 50
+ cbs_copy = copy.copy(cbs)
+ self.assertEqual(cbs_copy.pos, 0)
+ self.assertTrue(cbs._datastore is cbs_copy._datastore)
+ self.assertTrue(cbs == cbs_copy)
+
+ def testBitStreamCopy(self):
+ bs = bitstring.BitStream(100)
+ bs.pos = 50
+ bs_copy = copy.copy(bs)
+ self.assertEqual(bs_copy.pos, 0)
+ self.assertFalse(bs._datastore is bs_copy._datastore)
+ self.assertTrue(bs == bs_copy)
+
+
+class Interning(unittest.TestCase):
+ def testBits(self):
+ a = bitstring.Bits('0xf')
+ b = bitstring.Bits('0xf')
+ self.assertTrue(a is b)
+ c = bitstring.Bits('0b1111')
+ self.assertFalse(a is c)
+
+ def testCBS(self):
+ a = bitstring.ConstBitStream('0b11000')
+ b = bitstring.ConstBitStream('0b11000')
+ self.assertFalse(a is b)
+ # self.assertTrue(a._datastore is b._datastore)
+
+
+
+ \ No newline at end of file
diff --git a/python/bitstring/test/test_constbitstream.py b/python/bitstring/test/test_constbitstream.py
new file mode 100644
index 0000000000..a1bef743f6
--- /dev/null
+++ b/python/bitstring/test/test_constbitstream.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python
+
+import unittest
+import sys
+sys.path.insert(0, '..')
+import bitstring
+from bitstring import ConstBitStream as CBS
+
+class All(unittest.TestCase):
+ def testFromFile(self):
+ s = CBS(filename='test.m1v')
+ self.assertEqual(s[0:32].hex, '000001b3')
+ self.assertEqual(s.read(8 * 4).hex, '000001b3')
+ width = s.read(12).uint
+ height = s.read(12).uint
+ self.assertEqual((width, height), (352, 288))
+
+
+class InterleavedExpGolomb(unittest.TestCase):
+ def testReading(self):
+ s = CBS(uie=333)
+ a = s.read('uie')
+ self.assertEqual(a, 333)
+ s = CBS('uie=12, sie=-9, sie=9, uie=1000000')
+ u = s.unpack('uie, 2*sie, uie')
+ self.assertEqual(u, [12, -9, 9, 1000000])
+
+ def testReadingErrors(self):
+ s = CBS(10)
+ self.assertRaises(bitstring.ReadError, s.read, 'uie')
+ self.assertEqual(s.pos, 0)
+ self.assertRaises(bitstring.ReadError, s.read, 'sie')
+ self.assertEqual(s.pos, 0)
+
+
+class ReadTo(unittest.TestCase):
+ def testByteAligned(self):
+ a = CBS('0xaabb00aa00bb')
+ b = a.readto('0x00', bytealigned=True)
+ self.assertEqual(b, '0xaabb00')
+ self.assertEqual(a.bytepos, 3)
+ b = a.readto('0xaa', bytealigned=True)
+ self.assertEqual(b, '0xaa')
+ self.assertRaises(bitstring.ReadError, a.readto, '0xcc', bytealigned=True)
+
+ def testNotAligned(self):
+ a = CBS('0b00111001001010011011')
+ a.pos = 1
+ self.assertEqual(a.readto('0b00'), '0b011100')
+ self.assertEqual(a.readto('0b110'), '0b10010100110')
+ self.assertRaises(ValueError, a.readto, '')
+
+ def testDisallowIntegers(self):
+ a = CBS('0x0f')
+ self.assertRaises(ValueError, a.readto, 4)
+
+ def testReadingLines(self):
+ s = b"This is a test\nof reading lines\nof text\n"
+ b = CBS(bytes=s)
+ n = bitstring.Bits(bytes=b'\n')
+ self.assertEqual(b.readto(n).bytes, b'This is a test\n')
+ self.assertEqual(b.readto(n).bytes, b'of reading lines\n')
+ self.assertEqual(b.readto(n).bytes, b'of text\n')
+
+
+class Subclassing(unittest.TestCase):
+
+ def testIsInstance(self):
+ class SubBits(CBS): pass
+ a = SubBits()
+ self.assertTrue(isinstance(a, SubBits))
+
+ def testClassType(self):
+ class SubBits(CBS): pass
+ self.assertEqual(SubBits().__class__, SubBits)
+
+
+class PadToken(unittest.TestCase):
+
+ def testRead(self):
+ s = CBS('0b100011110001')
+ a = s.read('pad:1')
+ self.assertEqual(a, None)
+ self.assertEqual(s.pos, 1)
+ a = s.read(3)
+ self.assertEqual(a, CBS('0b000'))
+ a = s.read('pad:0')
+ self.assertEqual(a, None)
+ self.assertEqual(s.pos, 4)
+
+ def testReadList(self):
+ s = CBS('0b10001111001')
+ t = s.readlist('pad:1, uint:3, pad:4, uint:3')
+ self.assertEqual(t, [0, 1])
+ s.pos = 0
+ t = s.readlist('pad:1, pad:5')
+ self.assertEqual(t, [])
+ self.assertEqual(s.pos, 6)
+ s.pos = 0
+ t = s.readlist('pad:1, bin, pad:4, uint:3')
+ self.assertEqual(t, ['000', 1])
+ s.pos = 0
+ t = s.readlist('pad, bin:3, pad:4, uint:3')
+ self.assertEqual(t, ['000', 1])
+
+class ReadingBytes(unittest.TestCase):
+
+ def testUnpackingBytes(self):
+ s = CBS(80)
+ t = s.unpack('bytes:1')
+ self.assertEqual(t[0], b'\x00')
+ a, b, c = s.unpack('bytes:1, bytes, bytes:2')
+ self.assertEqual(a, b'\x00')
+ self.assertEqual(b, b'\x00'*7)
+ self.assertEqual(c, b'\x00'*2)
+
+ def testUnpackingBytesWithKeywords(self):
+ s = CBS('0x55'*10)
+ t = s.unpack('pad:a, bytes:b, bytes, pad:a', a=4, b=6)
+ self.assertEqual(t, [b'\x55'*6, b'\x55'*3])
+