back to Claude Sonnet 3.5 - Fill-in summary
Claude Sonnet 3.5 - Fill-in: bitstring
Failed to run pytests for test tests
Pytest collection failure.
Patch diff
diff --git a/bitstring/array_.py b/bitstring/array_.py
index 5df26f0..cb5e869 100644
--- a/bitstring/array_.py
+++ b/bitstring/array_.py
@@ -95,7 +95,7 @@ class Array:
def _create_element(self, value: ElementType) ->Bits:
"""Create Bits from value according to the token_name and token_length"""
- pass
+ return self._dtype.set_fn(value)
def __len__(self) ->int:
return len(self.data) // self._dtype.length
@@ -206,13 +206,20 @@ class Array:
def astype(self, dtype: Union[str, Dtype]) ->Array:
"""Return Array with elements of new dtype, initialised from current Array."""
- pass
+ new_array = Array(dtype)
+ new_array.extend(self)
+ return new_array
def insert(self, i: int, x: ElementType) ->None:
"""Insert a new element into the Array at position i.
"""
- pass
+ if i < 0:
+ i += len(self)
+ if i < 0 or i > len(self):
+ raise IndexError("Array index out of range")
+ element = self._create_element(x)
+ self.data.insert(i * self._dtype.length, element)
def pop(self, i: int=-1) ->ElementType:
"""Return and remove an element of the Array.
@@ -220,7 +227,15 @@ class Array:
Default is to return and remove the final element.
"""
- pass
+ if i < 0:
+ i += len(self)
+ if i < 0 or i >= len(self):
+ raise IndexError("Array index out of range")
+ start = i * self._dtype.length
+ end = start + self._dtype.length
+ element = self._dtype.read_fn(self.data, start=start)
+ del self.data[start:end]
+ return element
def byteswap(self) ->None:
"""Change the endianness in-place of all items in the Array.
@@ -228,7 +243,14 @@ class Array:
If the Array format is not a whole number of bytes a ValueError will be raised.
"""
- pass
+ if self._dtype.length % 8 != 0:
+ raise ValueError("Array format is not a whole number of bytes")
+ bytes_per_item = self._dtype.length // 8
+ for i in range(0, len(self.data), self._dtype.length):
+ item = self.data[i:i + self._dtype.length]
+ swapped = BitArray(item)
+ swapped.byteswap()
+ self.data.overwrite(swapped, i)
def count(self, value: ElementType) ->int:
"""Return count of Array items that equal value.
@@ -238,7 +260,13 @@ class Array:
For floating point types using a value of float('nan') will count the number of elements that are NaN.
"""
- pass
+ count = 0
+ for item in self:
+ if math.isnan(value) and math.isnan(item):
+ count += 1
+ elif item == value:
+ count += 1
+ return count
def tobytes(self) ->bytes:
"""Return the Array data as a bytes object, padding with zero bits if needed.
@@ -246,7 +274,7 @@ class Array:
Up to seven zero bits will be added at the end to byte align.
"""
- pass
+ return self.data.tobytes()
def tofile(self, f: BinaryIO) ->None:
"""Write the Array data to a file object, padding with zero bits if needed.
@@ -254,7 +282,7 @@ class Array:
Up to seven zero bits will be added at the end to byte align.
"""
- pass
+ f.write(self.tobytes())
def pp(self, fmt: Optional[str]=None, width: int=120, show_offset: bool
=True, stream: TextIO=sys.stdout) ->None:
diff --git a/bitstring/bitarray_.py b/bitstring/bitarray_.py
index 5d6204f..f9b9ee7 100644
--- a/bitstring/bitarray_.py
+++ b/bitstring/bitarray_.py
@@ -113,7 +113,7 @@ class BitArray(Bits):
def copy(self: TBits) ->TBits:
"""Return a copy of the bitstring."""
- pass
+ return BitArray(self)
def __setattr__(self, attribute, value) ->None:
try:
@@ -245,7 +245,34 @@ class BitArray(Bits):
out of range.
"""
- pass
+ if not old:
+ raise ValueError("Empty bitstring to replace")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+ count = -1 if count is None else count
+
+ if start < 0 or end > len(self) or start > end:
+ raise ValueError("Invalid start or end values")
+
+ old_bits = Bits(old)
+ new_bits = Bits(new)
+ replacements = 0
+ pos = start
+
+ while pos <= end - len(old_bits) and replacements != count:
+ if bytealigned and pos % 8 != 0:
+ pos += 1
+ continue
+
+ if self[pos:pos+len(old_bits)] == old_bits:
+ self[pos:pos+len(old_bits)] = new_bits
+ pos += len(new_bits)
+ replacements += 1
+ else:
+ pos += 1
+
+ return replacements
def insert(self, bs: BitsType, pos: int) ->None:
"""Insert bs at bit position pos.
@@ -256,7 +283,11 @@ class BitArray(Bits):
Raises ValueError if pos < 0 or pos > len(self).
"""
- pass
+ if pos < 0 or pos > len(self):
+ raise ValueError("Invalid insertion position")
+
+ bs_to_insert = Bits(bs)
+ self._bitstore = self._bitstore[:pos] + bs_to_insert._bitstore + self._bitstore[pos:]
def overwrite(self, bs: BitsType, pos: int) ->None:
"""Overwrite with bs at bit position pos.
@@ -267,7 +298,15 @@ class BitArray(Bits):
Raises ValueError if pos < 0 or pos > len(self).
"""
- pass
+ if pos < 0 or pos > len(self):
+ raise ValueError("Invalid overwrite position")
+
+ bs_to_write = Bits(bs)
+ end = pos + len(bs_to_write)
+ if end > len(self):
+ self._bitstore = self._bitstore[:pos] + bs_to_write._bitstore
+ else:
+ self._bitstore = self._bitstore[:pos] + bs_to_write._bitstore + self._bitstore[end:]
def append(self, bs: BitsType) ->None:
"""Append a bitstring to the current bitstring.
@@ -275,7 +314,8 @@ class BitArray(Bits):
bs -- The bitstring to append.
"""
- pass
+ bs_to_append = Bits(bs)
+ self._bitstore += bs_to_append._bitstore
def prepend(self, bs: BitsType) ->None:
"""Prepend a bitstring to the current bitstring.
@@ -283,7 +323,8 @@ class BitArray(Bits):
bs -- The bitstring to prepend.
"""
- pass
+ bs_to_prepend = Bits(bs)
+ self._bitstore = bs_to_prepend._bitstore + self._bitstore
def reverse(self, start: Optional[int]=None, end: Optional[int]=None
) ->None:
@@ -298,7 +339,17 @@ class BitArray(Bits):
Raises ValueError if start < 0, end > len(self) or end < start.
"""
- pass
+ if not self:
+ return
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or end < start:
+ raise ValueError("Invalid start or end values")
+
+ reversed_section = self._bitstore[start:end][::-1]
+ self._bitstore = self._bitstore[:start] + reversed_section + self._bitstore[end:]
def set(self, value: Any, pos: Optional[Union[int, Iterable[int]]]=None
) ->None:
@@ -312,7 +363,23 @@ class BitArray(Bits):
Raises IndexError if pos < -len(self) or pos >= len(self).
"""
- pass
+ bit_value = 1 if bool(value) else 0
+
+ if pos is None:
+ self._bitstore = BitArray([bit_value] * len(self))._bitstore
+ elif isinstance(pos, int):
+ if pos < -len(self) or pos >= len(self):
+ raise IndexError("Bit position out of range")
+ if pos < 0:
+ pos = len(self) + pos
+ self._bitstore[pos] = bit_value
+ else:
+ for p in pos:
+ if p < -len(self) or p >= len(self):
+ raise IndexError("Bit position out of range")
+ if p < 0:
+ p = len(self) + p
+ self._bitstore[p] = bit_value
def invert(self, pos: Optional[Union[Iterable[int], int]]=None) ->None:
"""Invert one or many bits from 0 to 1 or vice versa.
@@ -323,7 +390,21 @@ class BitArray(Bits):
Raises IndexError if pos < -len(self) or pos >= len(self).
"""
- pass
+ if pos is None:
+ self._bitstore = ~self._bitstore
+ elif isinstance(pos, int):
+ if pos < -len(self) or pos >= len(self):
+ raise IndexError("Bit position out of range")
+ if pos < 0:
+ pos = len(self) + pos
+ self._bitstore[pos] = not self._bitstore[pos]
+ else:
+ for p in pos:
+ if p < -len(self) or p >= len(self):
+ raise IndexError("Bit position out of range")
+ if p < 0:
+ p = len(self) + p
+ self._bitstore[p] = not self._bitstore[p]
def ror(self, bits: int, start: Optional[int]=None, end: Optional[int]=None
) ->None:
@@ -336,7 +417,26 @@ class BitArray(Bits):
Raises ValueError if bits < 0.
"""
- pass
+ if bits < 0:
+ raise ValueError("Cannot rotate by a negative amount")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or start >= end:
+ raise ValueError("Invalid start or end values")
+
+ if bits == 0 or start == end:
+ return
+
+ slice_len = end - start
+ bits = bits % slice_len # Normalize rotation amount
+
+ if bits == 0:
+ return
+
+ rotated = self._bitstore[end-bits:end] + self._bitstore[start:end-bits]
+ self._bitstore = self._bitstore[:start] + rotated + self._bitstore[end:]
def rol(self, bits: int, start: Optional[int]=None, end: Optional[int]=None
) ->None:
@@ -349,7 +449,26 @@ class BitArray(Bits):
Raises ValueError if bits < 0.
"""
- pass
+ if bits < 0:
+ raise ValueError("Cannot rotate by a negative amount")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or start >= end:
+ raise ValueError("Invalid start or end values")
+
+ if bits == 0 or start == end:
+ return
+
+ slice_len = end - start
+ bits = bits % slice_len # Normalize rotation amount
+
+ if bits == 0:
+ return
+
+ rotated = self._bitstore[start+bits:end] + self._bitstore[start:start+bits]
+ self._bitstore = self._bitstore[:start] + rotated + self._bitstore[end:]
def byteswap(self, fmt: Optional[Union[int, Iterable[int], str]]=None,
start: Optional[int]=None, end: Optional[int]=None, repeat: bool=True
@@ -365,8 +484,42 @@ class BitArray(Bits):
as much as possible.
"""
- pass
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or start > end:
+ raise ValueError("Invalid start or end values")
+
+ if fmt is None or fmt == 0:
+ fmt = [8] * ((end - start) // 8)
+ elif isinstance(fmt, int):
+ fmt = [8] * fmt
+ elif isinstance(fmt, str):
+ fmt = [int(x) * 8 for x in fmt.split(',') if x]
+
+ fmt = [f for f in fmt if f % 8 == 0] # Ensure all chunks are byte-aligned
+ if not fmt:
+ return 0
+
+ chunk_size = sum(fmt)
+ if chunk_size == 0:
+ return 0
+
+ repeats = 0
+ pos = start
+ while pos + chunk_size <= end:
+ for size in fmt:
+ byte_size = size // 8
+ byte_data = self._bitstore[pos:pos+size].tobytes()
+ swapped_data = byte_data[::-1]
+ self._bitstore[pos:pos+size] = BitArray(bytes=swapped_data)
+ pos += size
+ repeats += 1
+ if not repeat:
+ break
+
+ return repeats
def clear(self) ->None:
"""Remove all bits, reset to zero length."""
- pass
+ self._bitstore = BitArray()
diff --git a/bitstring/bits.py b/bitstring/bits.py
index f6d7d08..6a8c668 100644
--- a/bitstring/bits.py
+++ b/bitstring/bits.py
@@ -421,16 +421,33 @@ class Bits:
def _clear(self) ->None:
"""Reset the bitstring to an empty state."""
- pass
+ self._bitstore = BitStore()
def _setauto_no_length_or_offset(self, s: BitsType, /) ->None:
"""Set bitstring from a bitstring, file, bool, array, iterable or string."""
pass
- def _setauto(self, s: BitsType, length: Optional[int], offset: Optional
- [int], /) ->None:
+ def _setauto(self, s: BitsType, length: Optional[int], offset: Optional[int], /) ->None:
"""Set bitstring from a bitstring, file, bool, array, iterable or string."""
- pass
+ if isinstance(s, Bits):
+ self._bitstore = s._bitstore.copy()
+ elif isinstance(s, (bytes, bytearray)):
+ self._setbytes_with_truncation(s, length, offset)
+ elif isinstance(s, str):
+ if s.startswith('0b'):
+ self._setbin_safe(s[2:], length)
+ elif s.startswith('0x'):
+ self._sethex(s[2:], length)
+ elif s.startswith('0o'):
+ self._setoct(s[2:], length)
+ else:
+ self._setfile(s, length, offset)
+ elif isinstance(s, (int, bool)):
+ self._setuint(int(s), length)
+ elif isinstance(s, (array.array, list, tuple)):
+ self._setbytes(bytes(s), length)
+ else:
+ raise TypeError(f"Cannot initialise bitstring from {type(s)}")
def _setfile(self, filename: str, length: Optional[int]=None, offset:
Optional[int]=None) ->None:
@@ -445,11 +462,23 @@ class Bits:
def _setbytes_with_truncation(self, data: Union[bytearray, bytes],
length: Optional[int]=None, offset: Optional[int]=None) ->None:
"""Set the data from a bytes or bytearray object, with optional offset and length truncations."""
- pass
+ if offset is None:
+ offset = 0
+ if length is None:
+ length = len(data) * 8 - offset
+ start_byte, start_bit = divmod(offset, 8)
+ end_byte = (offset + length + 7) // 8
+ truncated_data = data[start_byte:end_byte]
+ self._bitstore = BitStore(truncated_data)
+ if start_bit:
+ self._bitstore.lshift(start_bit)
+ if (offset + length) % 8:
+ self._bitstore.rshift(8 - ((offset + length) % 8))
+ self._bitstore.truncate(length)
def _getbytes(self) ->bytes:
"""Return the data as an ordinary bytes object."""
- pass
+ return self._bitstore.tobytes()
_unprintable = list(range(0, 32))
_unprintable.extend(range(127, 255))
@@ -571,7 +600,13 @@ class Bits:
def _setbin_safe(self, binstring: str, length: None=None) ->None:
"""Reset the bitstring to the value given in binstring."""
- pass
+ if set(binstring) - set('01'):
+ raise ValueError("binstring must contain only '0' and '1'")
+ if length is not None and len(binstring) > length:
+ raise ValueError("binstring is too long")
+ self._bitstore = BitStore.frombinstr(binstring)
+ if length is not None:
+ self._bitstore.prepend(BitStore(length - len(binstring)))
def _setbin_unsafe(self, binstring: str, length: None=None) ->None:
"""Same as _setbin_safe, but input isn't sanity checked. binstring mustn't start with '0b'."""
@@ -591,7 +626,15 @@ class Bits:
def _sethex(self, hexstring: str, length: None=None) ->None:
"""Reset the bitstring to have the value given in hexstring."""
- pass
+ try:
+ byte_data = bytes.fromhex(hexstring)
+ except ValueError:
+ raise ValueError("Invalid hexadecimal string")
+ self._bitstore = BitStore(byte_data)
+ if length is not None:
+ if len(self._bitstore) > length:
+ raise ValueError("hexstring is too long")
+ self._bitstore.prepend(BitStore(length - len(self._bitstore)))
def _gethex(self) ->str:
"""Return the hexadecimal representation as a string.
@@ -603,15 +646,19 @@ class Bits:
def _getlength(self) ->int:
"""Return the length of the bitstring in bits."""
- pass
+ return len(self._bitstore)
def _copy(self: TBits) ->TBits:
"""Create and return a new copy of the Bits (always in memory)."""
- pass
+ new_bits = object.__new__(self.__class__)
+ new_bits._bitstore = self._bitstore.copy()
+ return new_bits
def _slice(self: TBits, start: int, end: int) ->TBits:
"""Used internally to get a slice, without error checking."""
- pass
+ new_bits = object.__new__(self.__class__)
+ new_bits._bitstore = self._bitstore[start:end]
+ return new_bits
def _absolute_slice(self: TBits, start: int, end: int) ->TBits:
"""Used internally to get a slice, without error checking.
@@ -625,11 +672,13 @@ class Bits:
def _addright(self, bs: Bits, /) ->None:
"""Add a bitstring to the RHS of the current bitstring."""
- pass
+ self._bitstore.extend(bs._bitstore)
def _addleft(self, bs: Bits, /) ->None:
"""Prepend a bitstring to the current bitstring."""
- pass
+ new_bitstore = bs._bitstore.copy()
+ new_bitstore.extend(self._bitstore)
+ self._bitstore = new_bitstore
def _truncateleft(self: TBits, bits: int, /) ->TBits:
"""Truncate bits from the start of the bitstring. Return the truncated bits."""
@@ -657,11 +706,11 @@ class Bits:
def _invert(self, pos: int, /) ->None:
"""Flip bit at pos 1<->0."""
- pass
+ self._bitstore.invert(pos)
def _invert_all(self) ->None:
"""Invert every bit."""
- pass
+ self._bitstore.invert_all()
def _ilshift(self: TBits, n: int, /) ->TBits:
"""Shift bits by n to the left in place. Return self."""
@@ -825,7 +874,7 @@ class Bits:
Up to seven zero bits will be added at the end to byte align.
"""
- pass
+ return self._bitstore.tobytes()
def tobitarray(self) ->bitarray.bitarray:
"""Convert the bitstring to a bitarray object."""
diff --git a/bitstring/bitstore.py b/bitstring/bitstore.py
index d01a2d3..85a6d59 100644
--- a/bitstring/bitstore.py
+++ b/bitstring/bitstore.py
@@ -53,10 +53,29 @@ class BitStore:
def _copy(self) ->BitStore:
"""Always creates a copy, even if instance is immutable."""
- pass
+ new_bitstore = BitStore()
+ new_bitstore._bitarray = self._bitarray.copy()
+ new_bitstore.modified_length = self.modified_length
+ new_bitstore.immutable = False # The copy is always mutable
+ return new_bitstore
def __getitem__(self, item: Union[int, slice], /) ->Union[int, BitStore]:
- raise NotImplementedError
+ if isinstance(item, int):
+ return self.getindex(item)
+ elif isinstance(item, slice):
+ new_bitstore = BitStore()
+ new_bitstore._bitarray = self._bitarray[item]
+ return new_bitstore
+ else:
+ raise TypeError("Invalid argument type.")
+
+ def getindex(self, i: int) ->int:
+ """Get the bit at index i (LSB0 order)."""
+ if i < 0:
+ i += len(self)
+ if i < 0 or i >= len(self):
+ raise IndexError("Bit index out of range")
+ return self._bitarray[len(self) - 1 - i]
def __len__(self) ->int:
return (self.modified_length if self.modified_length is not None else
diff --git a/bitstring/bitstore_helpers.py b/bitstring/bitstore_helpers.py
index 87a9787..6edc23c 100644
--- a/bitstring/bitstore_helpers.py
+++ b/bitstring/bitstore_helpers.py
@@ -11,9 +11,9 @@ from bitstring.mxfp import e3m2mxfp_fmt, e2m3mxfp_fmt, e2m1mxfp_fmt, e4m3mxfp_sa
CACHE_SIZE = 256
-def tidy_input_string(s: str) ->str:
+def tidy_input_string(s: str) -> str:
"""Return string made lowercase and with all whitespace and underscores removed."""
- pass
+ return ''.join(char.lower() for char in s if char not in (' ', '\t', '\n', '\r', '_'))
e8m0mxfp_allowed_values = [float(2 ** x) for x in range(-127, 128)]
diff --git a/bitstring/bitstream.py b/bitstring/bitstream.py
index 9a65360..fb32043 100644
--- a/bitstring/bitstream.py
+++ b/bitstring/bitstream.py
@@ -109,19 +109,25 @@ class ConstBitStream(Bits):
def _setbytepos(self, bytepos: int) ->None:
"""Move to absolute byte-aligned position in stream."""
- pass
+ if bytepos * 8 > len(self):
+ raise ValueError("Byte position out of range")
+ self._pos = bytepos * 8
def _getbytepos(self) ->int:
"""Return the current position in the stream in bytes. Must be byte aligned."""
- pass
+ if self._pos % 8:
+ raise ValueError("Current position is not byte aligned")
+ return self._pos // 8
def _setbitpos(self, pos: int) ->None:
"""Move to absolute position bit in bitstream."""
- pass
+ if pos < 0 or pos > len(self):
+ raise ValueError("Bit position out of range")
+ self._pos = pos
def _getbitpos(self) ->int:
"""Return the current position in the stream in bits."""
- pass
+ return self._pos
def __copy__(self: TConstBitStream) ->TConstBitStream:
"""Return a new copy of the ConstBitStream for the copy module."""
@@ -184,7 +190,8 @@ class ConstBitStream(Bits):
The current bit position will be moved to the end of the BitStream.
"""
- pass
+ self._bitstore.append(Bits(bs)._bitstore)
+ self._pos = len(self)
def __repr__(self) ->str:
"""Return representation that could be used to recreate the bitstring.
@@ -204,7 +211,18 @@ class ConstBitStream(Bits):
Raises ValueError if pos < 0 or pos > len(self).
"""
- pass
+ if pos is None:
+ pos = self._pos
+ if pos < 0 or pos > len(self):
+ raise ValueError("pos must be between 0 and len(self)")
+
+ bs = Bits(bs)
+ end = pos + len(bs)
+ if end > len(self):
+ self._bitstore.append(bs._bitstore)
+ else:
+ self._bitstore[pos:end] = bs._bitstore
+ self._pos = end
def find(self, bs: BitsType, /, start: Optional[int]=None, end:
Optional[int]=None, bytealigned: Optional[bool]=None) ->Union[Tuple
@@ -229,7 +247,24 @@ class ConstBitStream(Bits):
(6,)
"""
- pass
+ bs = Bits(bs)
+ if not bs:
+ raise ValueError("Cannot find an empty bitstring")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or end < start:
+ raise ValueError("Invalid start or end values")
+
+ if bytealigned:
+ start = (start + 7) // 8 * 8
+
+ pos = self._bitstore.find(bs._bitstore, start, end, bytealigned)
+ if pos >= 0:
+ self._pos = pos
+ return (pos,)
+ return ()
def rfind(self, bs: BitsType, /, start: Optional[int]=None, end:
Optional[int]=None, bytealigned: Optional[bool]=None) ->Union[Tuple
@@ -251,7 +286,24 @@ class ConstBitStream(Bits):
if end < start.
"""
- pass
+ bs = Bits(bs)
+ if not bs:
+ raise ValueError("Cannot find an empty bitstring")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+
+ if start < 0 or end > len(self) or end < start:
+ raise ValueError("Invalid start or end values")
+
+ if bytealigned:
+ start = (start + 7) // 8 * 8
+
+ pos = self._bitstore.rfind(bs._bitstore, start, end, bytealigned)
+ if pos >= 0:
+ self._pos = pos
+ return (pos,)
+ return ()
def read(self, fmt: Union[int, str, Dtype]) ->Union[int, float, str,
Bits, bool, bytes, None]:
@@ -290,7 +342,19 @@ class ConstBitStream(Bits):
Raises ValueError if the format is not understood.
"""
- pass
+ if isinstance(fmt, int):
+ fmt = f'bits:{fmt}'
+
+ dtype = Dtype(fmt)
+ length = dtype.length
+
+ if self._pos + length > len(self):
+ raise bitstring.ReadError("Not enough bits available")
+
+ value = dtype.get_fn(self[self._pos:self._pos + length])
+ self._pos += length
+
+ return value
def readlist(self, fmt: Union[str, List[Union[int, str, Dtype]]], **kwargs
) ->List[Union[int, float, str, Bits, bool, bytes, None]]:
@@ -314,7 +378,23 @@ class ConstBitStream(Bits):
>>> i, bs1, bs2 = s.readlist(['uint:12', 10, 10])
"""
- pass
+ tokens = []
+ if isinstance(fmt, str):
+ fmt = fmt.replace(' ', '')
+ tokens = fmt.split(',')
+ elif isinstance(fmt, list):
+ tokens = fmt
+ else:
+ raise ValueError("fmt must be either a string or a list")
+
+ return_values = []
+ for token in tokens:
+ if isinstance(token, int):
+ token = f'bits:{token}'
+ value = self.read(token)
+ if not token.startswith('pad:'):
+ return_values.append(value)
+ return return_values
def readto(self: TConstBitStream, bs: BitsType, /, bytealigned:
Optional[bool]=None) ->TConstBitStream:
@@ -328,7 +408,18 @@ class ConstBitStream(Bits):
Raises ReadError if bs is not found.
"""
- pass
+ bs = Bits(bs)
+ if not bs:
+ raise ValueError("Cannot find an empty bitstring")
+
+ found = self.find(bs, start=self._pos, bytealigned=bytealigned)
+ if not found:
+ raise bitstring.ReadError("Substring not found")
+
+ end = found[0] + len(bs)
+ return_value = self[self._pos:end]
+ self._pos = end
+ return return_value
def peek(self: TConstBitStream, fmt: Union[int, str]) ->Union[int,
float, str, TConstBitStream, bool, bytes, None]:
@@ -345,7 +436,11 @@ class ConstBitStream(Bits):
See the docstring for 'read' for token examples.
"""
- pass
+ original_pos = self._pos
+ try:
+ return self.read(fmt)
+ finally:
+ self._pos = original_pos
def peeklist(self, fmt: Union[str, List[Union[int, str]]], **kwargs
) ->List[Union[int, float, str, Bits, None]]:
@@ -537,7 +632,9 @@ class BitStream(ConstBitStream, bitstring.BitArray):
bs -- The bitstring to prepend.
"""
- pass
+ bs = Bits(bs)
+ self._bitstore = bs._bitstore + self._bitstore
+ self._pos += len(bs)
def __setitem__(self, /, key: Union[slice, int], value: BitsType) ->None:
length_before = len(self)
@@ -570,7 +667,14 @@ class BitStream(ConstBitStream, bitstring.BitArray):
Raises ValueError if pos < 0 or pos > len(self).
"""
- pass
+ if pos is None:
+ pos = self._pos
+ if pos < 0 or pos > len(self):
+ raise ValueError("pos must be between 0 and len(self)")
+
+ bs = Bits(bs)
+ self._bitstore = self._bitstore[:pos] + bs._bitstore + self._bitstore[pos:]
+ self._pos = pos + len(bs)
def replace(self, old: BitsType, new: BitsType, start: Optional[int]=
None, end: Optional[int]=None, count: Optional[int]=None,
@@ -594,4 +698,29 @@ class BitStream(ConstBitStream, bitstring.BitArray):
out of range.
"""
- pass
+ old = Bits(old)
+ new = Bits(new)
+ if not old:
+ raise ValueError("Cannot replace an empty bitstring")
+
+ start = 0 if start is None else start
+ end = len(self) if end is None else end
+ count = -1 if count is None else count
+
+ if start < 0 or end > len(self) or start > end:
+ raise ValueError("Invalid start or end values")
+
+ replacements = 0
+ pos = start
+ while count != 0:
+ found = self.find(old, start=pos, end=end, bytealigned=bytealigned)
+ if not found:
+ break
+ pos = found[0]
+ self._bitstore = self._bitstore[:pos] + new._bitstore + self._bitstore[pos + len(old):]
+ pos += len(new)
+ end += len(new) - len(old)
+ replacements += 1
+ count -= 1
+
+ return replacements
diff --git a/bitstring/dtypes.py b/bitstring/dtypes.py
index 775063f..09100af 100644
--- a/bitstring/dtypes.py
+++ b/bitstring/dtypes.py
@@ -44,57 +44,57 @@ class Dtype:
@property
def scale(self) ->Union[int, float, None]:
"""The multiplicative scale applied when interpreting the data."""
- pass
+ return self._scale
@property
def name(self) ->str:
"""A string giving the name of the data type."""
- pass
+ return self._name
@property
def length(self) ->int:
"""The length of the data type in units of bits_per_item. Set to None for variable length dtypes."""
- pass
+ return self._length
@property
def bitlength(self) ->Optional[int]:
"""The number of bits needed to represent a single instance of the data type. Set to None for variable length dtypes."""
- pass
+ return self._bitlength
@property
def bits_per_item(self) ->int:
"""The number of bits for each unit of length. Usually 1, but equals 8 for bytes type."""
- pass
+ return self._bits_per_item
@property
def variable_length(self) ->bool:
"""If True then the length of the data type depends on the data being interpreted, and must not be specified."""
- pass
+ return self._variable_length
@property
def return_type(self) ->Any:
"""The type of the value returned by the parse method, such as int, float or str."""
- pass
+ return self._return_type
@property
def is_signed(self) ->bool:
"""If True then the data type represents a signed quantity."""
- pass
+ return self._is_signed
@property
def set_fn(self) ->Optional[Callable]:
"""A function to set the value of the data type."""
- pass
+ return self._set_fn
@property
def get_fn(self) ->Callable:
"""A function to get the value of the data type."""
- pass
+ return self._get_fn
@property
def read_fn(self) ->Callable:
"""A function to read the value of the data type."""
- pass
+ return self._read_fn
def __hash__(self) ->int:
return hash((self._name, self._length))
@@ -104,13 +104,31 @@ class Dtype:
The value parameter should be of a type appropriate to the dtype.
"""
- pass
+ if self._set_fn is None:
+ raise NotImplementedError(f"Cannot build {self._name} dtype")
+
+ if self._set_fn_needs_length:
+ return self._set_fn(value, self._length)
+ else:
+ return self._set_fn(value)
def parse(self, b: BitsType, /) ->Any:
"""Parse a bitstring to find its value.
The b parameter should be a bitstring of the appropriate length, or an object that can be converted to a bitstring."""
- pass
+ if isinstance(b, bitstring.Bits):
+ bs = b
+ else:
+ bs = bitstring.Bits(b)
+
+ if not self._variable_length and self._bitlength is not None:
+ if len(bs) != self._bitlength:
+ raise ValueError(f"Expected {self._bitlength} bits, got {len(bs)}")
+
+ result = self._get_fn(bs)
+ if self._scale is not None:
+ return result * self._scale
+ return result
def __str__(self) ->str:
if self._scale is not None:
diff --git a/bitstring/fp8.py b/bitstring/fp8.py
index 4cf9431..53453e9 100644
--- a/bitstring/fp8.py
+++ b/bitstring/fp8.py
@@ -25,11 +25,61 @@ class Binary8Format:
def float_to_int8(self, f: float) ->int:
"""Given a Python float convert to the best float8 (expressed as an integer in 0-255 range)."""
- pass
+ if math.isnan(f):
+ return 0 # NaN is represented as 0 in this format
+
+ if f == 0:
+ return 0 if math.copysign(1, f) == 1 else 128 # Handle +0 and -0
+
+ if f > 0 and f >= 2 ** (self.pos_clamp_value - self.bias):
+ return self.pos_clamp_value # Positive infinity or too large positive number
+
+ if f < 0 and f <= -2 ** (self.neg_clamp_value - 128 - self.bias):
+ return self.neg_clamp_value # Negative infinity or too large negative number
+
+ sign = 0 if f > 0 else 128
+ f = abs(f)
+
+ exponent = math.floor(math.log2(f)) + self.bias
+ mantissa = round((f / (2 ** (exponent - self.bias)) - 1) * (2 ** (8 - self.exp_bits)))
+
+ if mantissa == 2 ** (8 - self.exp_bits):
+ exponent += 1
+ mantissa = 0
+
+ if exponent < 0:
+ exponent = 0
+ mantissa = 1
+ elif exponent >= 2 ** self.exp_bits - 1:
+ exponent = 2 ** self.exp_bits - 1
+ mantissa = 0
+
+ return sign | (exponent << (8 - self.exp_bits)) | mantissa
def createLUT_for_binary8_to_float(self):
"""Create a LUT to convert an int in range 0-255 representing a float8 into a Python float"""
- pass
+ lut = []
+ for i in range(256):
+ sign = -1 if i & 128 else 1
+ exponent = (i >> (8 - self.exp_bits)) & ((1 << self.exp_bits) - 1)
+ mantissa = i & ((1 << (8 - self.exp_bits)) - 1)
+
+ if exponent == 0:
+ if mantissa == 0:
+ value = 0.0
+ else:
+ value = sign * (mantissa / (2 ** (8 - self.exp_bits))) * (2 ** (1 - self.bias))
+ elif exponent == (2 ** self.exp_bits) - 1:
+ if mantissa == 0:
+ value = float('inf') if sign == 1 else float('-inf')
+ else:
+ value = float('nan')
+ else:
+ value = sign * (1 + mantissa / (2 ** (8 - self.exp_bits))) * (2 ** (exponent - self.bias))
+
+ lut.append(value)
+
+ return lut
p4binary_fmt = Binary8Format(exp_bits=4, bias=8)
diff --git a/bitstring/methods.py b/bitstring/methods.py
index e6be89b..db7277d 100644
--- a/bitstring/methods.py
+++ b/bitstring/methods.py
@@ -43,4 +43,37 @@ def pack(fmt: Union[str, List[str]], *values, **kwargs) ->BitStream:
>>> u = pack('uint:8=a, uint:8=b, uint:55=a', a=6, b=44)
"""
- pass
+ if isinstance(fmt, list):
+ fmt = ','.join(fmt)
+
+ tokens, _ = tokenparser(fmt)
+ bitstring_list = []
+ value_index = 0
+
+ for token in tokens:
+ if '=' in token[1]:
+ name, token_str = token[1].split('=')
+ value = kwargs.get(name.strip())
+ if value is None:
+ raise CreationError(f"Keyword '{name.strip()}' not provided")
+ else:
+ if value_index >= len(values):
+ raise CreationError("Not enough values provided")
+ value = values[value_index]
+ value_index += 1
+ token_str = token[1]
+
+ try:
+ bs = bitstore_from_token(token_str, value)
+ bitstring_list.append(bs)
+ except ValueError as e:
+ raise CreationError(str(e))
+
+ if value_index < len(values):
+ raise CreationError("Too many values provided")
+
+ result = BitStream()
+ for bs in bitstring_list:
+ result.append(bs)
+
+ return result
diff --git a/bitstring/mxfp.py b/bitstring/mxfp.py
index be90cd1..9afdd75 100644
--- a/bitstring/mxfp.py
+++ b/bitstring/mxfp.py
@@ -42,15 +42,66 @@ class MXFPFormat:
def float_to_int(self, f: float) ->int:
"""Given a Python float convert to the best mxfp float (expressed as an int) that represents it."""
- pass
+ if math.isnan(f):
+ return (1 << (self.exp_bits + self.mantissa_bits + 1)) - 1 # All ones for NaN
+
+ if f == 0:
+ return 0 # Zero is represented as all zeros
+
+ sign = 1 if f < 0 else 0
+ f = abs(f)
+
+ # Handle infinity and large numbers
+ if math.isinf(f) or f >= 2 ** (2 ** self.exp_bits - self.bias):
+ if self.mxfp_overflow == 'saturate':
+ return self.neg_clamp_value if sign else self.pos_clamp_value
+ else: # overflow
+ return self.neg_clamp_value if sign else self.pos_clamp_value
+
+ # Find the exponent
+ exp = math.floor(math.log2(f))
+ exp = max(exp, 1 - self.bias) # Handle subnormals
+
+ # Calculate mantissa
+ mantissa = int(round((f / (2 ** exp) - 1) * (2 ** self.mantissa_bits)))
+
+ # Adjust for bias
+ exp += self.bias
+
+ # Combine sign, exponent, and mantissa
+ result = (sign << (self.exp_bits + self.mantissa_bits)) | (exp << self.mantissa_bits) | mantissa
+
+ return result
def createLUT_for_int_to_float(self) ->array.array:
"""Create a LUT to convert an int in representing a MXFP float into a Python float"""
- pass
+ lut = array.array('f')
+ for i in range(1 << (self.exp_bits + self.mantissa_bits + 1)):
+ sign = -1 if i & (1 << (self.exp_bits + self.mantissa_bits)) else 1
+ exp = (i >> self.mantissa_bits) & ((1 << self.exp_bits) - 1)
+ mantissa = i & ((1 << self.mantissa_bits) - 1)
+
+ if exp == 0 and mantissa == 0:
+ lut.append(0.0)
+ elif exp == (1 << self.exp_bits) - 1:
+ if mantissa == 0:
+ lut.append(float('inf') if sign > 0 else float('-inf'))
+ else:
+ lut.append(float('nan'))
+ else:
+ value = sign * (1 + mantissa / (2 ** self.mantissa_bits)) * (2 ** (exp - self.bias))
+ lut.append(value)
+
+ return lut
def createLUT_for_float16_to_mxfp(self) ->bytes:
"""Create a LUT to convert a float16 into a MXFP format"""
- pass
+ lut = bytearray(65536)
+ for i in range(65536):
+ f16 = struct.unpack('!e', struct.pack('!H', i))[0]
+ mxfp = self.float_to_int(f16)
+ lut[i] = mxfp
+ return bytes(lut)
e2m1mxfp_fmt = MXFPFormat(exp_bits=2, mantissa_bits=1, bias=1,
diff --git a/bitstring/utils.py b/bitstring/utils.py
index 623d69b..dd9fa5a 100644
--- a/bitstring/utils.py
+++ b/bitstring/utils.py
@@ -36,7 +36,27 @@ PACK_CODE_SIZE: Dict[str, int] = {'b': 1, 'B': 1, 'h': 2, 'H': 2, 'l': 4,
def structparser(m: Match[str]) ->List[str]:
"""Parse struct-like format string token into sub-token list."""
- pass
+ endian = m.group('endian')
+ fmt = m.group('fmt')
+ tokens = []
+
+ if endian == '>':
+ replacements = REPLACEMENTS_BE
+ elif endian == '<':
+ replacements = REPLACEMENTS_LE
+ else:
+ replacements = REPLACEMENTS_NE
+
+ for match in STRUCT_SPLIT_RE.finditer(fmt):
+ code = match.group()
+ if code[0] in '0123456789':
+ count = int(code[:-1])
+ token = replacements[code[-1]]
+ tokens.extend([token] * count)
+ else:
+ tokens.append(replacements[code])
+
+ return tokens
@functools.lru_cache(CACHE_SIZE)
@@ -54,7 +74,61 @@ def tokenparser(fmt: str, keys: Tuple[str, ...]=()) ->Tuple[bool, List[
tokens must be of the form: [factor*][initialiser][:][length][=value]
"""
- pass
+ tokens = []
+ stretchy_token = False
+ fmt = expand_brackets(fmt)
+
+ for token in fmt.split(','):
+ token = token.strip()
+ if token in keys:
+ tokens.append((token, None, None))
+ continue
+
+ mobj = MULTIPLICATIVE_RE.match(token)
+ if mobj:
+ factor = int(mobj.group('factor'))
+ token = mobj.group('token')
+ else:
+ factor = 1
+
+ mobj = NAME_INT_RE.match(token)
+ if mobj:
+ name, length = mobj.group(1), mobj.group(2)
+ if length:
+ length = int(length)
+ else:
+ length = None
+ if name == 'pad':
+ if length is None:
+ stretchy_token = True
+ tokens.extend([(name, length, None)] * factor)
+ continue
+
+ mobj = DEFAULT_BITS.match(token)
+ if mobj:
+ name = 'bits'
+ length = mobj.group('len')
+ if length:
+ length = int(length)
+ else:
+ length = None
+ value = mobj.group('value')
+ if length is None and value is None:
+ stretchy_token = True
+ tokens.extend([(name, length, value)] * factor)
+ continue
+
+ mobj = LITERAL_RE.match(token)
+ if mobj:
+ name = mobj.group('name').lower()
+ value = mobj.group('value')
+ length = len(value) * {'0b': 1, '0o': 3, '0x': 4}[name]
+ tokens.extend([(name, length, value)] * factor)
+ continue
+
+ raise ValueError(f"Don't understand token '{token}' in format string")
+
+ return stretchy_token, tokens
BRACKET_RE = re.compile('(?P<factor>\\d+)\\*\\(')
@@ -62,4 +136,13 @@ BRACKET_RE = re.compile('(?P<factor>\\d+)\\*\\(')
def expand_brackets(s: str) ->str:
"""Expand all brackets."""
- pass
+ while True:
+ match = BRACKET_RE.search(s)
+ if not match:
+ break
+ factor = int(match.group('factor'))
+ start = match.start()
+ end = s.index(')', start)
+ sub = s[start + len(match.group()):end]
+ s = s[:start] + ','.join([sub] * factor) + s[end + 1:]
+ return s