aboutsummaryrefslogtreecommitdiffstats
path: root/bits.py
diff options
context:
space:
mode:
Diffstat (limited to 'bits.py')
-rw-r--r--bits.py158
1 files changed, 88 insertions, 70 deletions
diff --git a/bits.py b/bits.py
index b8cf441..02c24df 100644
--- a/bits.py
+++ b/bits.py
@@ -1,12 +1,86 @@
-empty = (0, 0)
+class Bitfield(object):
+ def __init__(self, val, size):
+ self.val = val
+ self.size = size
+
+ def __repr__(self):
+ return "Bitfield(0x{:x}, {})".format(self.val, self.size)
+
+ def __str__(self):
+ return "0x{:x}'{}".format(self.val, self.size)
+
+ def __getitem__(self, range):
+ """ slice a bitfield given hi and lo bit indices.
+
+ >>> Bitfield(0x7f, 8)[7:4]
+ Bitfield(0x7, 4)
+ >>> Bitfield(0b100, 3)[2:2]
+ Bitfield(0x1, 1)
+ >>> Bitfield(0x12345678, 32)[7:0]
+ Bitfield(0x78, 8)
+ >>> Bitfield(0x12345678, 32)[15:8]
+ Bitfield(0x56, 8)
+ >>> Bitfield(0x12345678, 32)[23:16]
+ Bitfield(0x34, 8)
+
+ >>> Bitfield(0xf, 4)[4:0]
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [4:0] out of range of 0xf'4
+ >>> Bitfield(0xf, 4)[2:3]
+ Traceback (most recent call last):
+ ...
+ ValueError: cant slice reverse range
+ >>> Bitfield(0xf, 4)[3:-1]
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [3:-1] out of range of 0xf'4
+ """
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ if hi < lo:
+ raise ValueError("cant slice reverse range")
+ elif lo < 0 or hi >= self.size:
+ raise ValueError("slice [{0.start}:{0.stop}] out of range of {1}".format(range, self))
+
+ size = hi - lo + 1
+ val = (self.val >> lo) & ((1 << size) - 1)
+ return Bitfield(val, size)
+
+ def slice_allowempty(self, range):
+ if not isinstance(range, slice):
+ range = slice(range, range)
+ hi, lo = range.start, range.stop
+
+ if hi < lo:
+ return empty
+
+ return self[range]
+
+ def __and__(self, other):
+ """ concatenate multiple bitfields.
+
+ >>> Bitfield(0b00, 2) & Bitfield(0b10, 2)
+ Bitfield(0x2, 4)
+ >>> Bitfield(0b110, 3) & Bitfield(0b0110, 4) & Bitfield(0b1, 1)
+ Bitfield(0xcd, 8)
+ """
+ if not isinstance(other, Bitfield):
+ raise NotImplementedError()
+
+ return Bitfield(self.val << other.size | other.val, self.size + other.size)
+
+empty = Bitfield(0, 0)
def u(num, bits):
""" parse an unsigned integer into a bitfield.
>>> u(0x08, 8)
- (8, 8)
+ Bitfield(0x8, 8)
>>> u(0xff, 8)
- (255, 8)
+ Bitfield(0xff, 8)
>>> u(0xf0, 7)
Traceback (most recent call last):
@@ -24,19 +98,19 @@ def u(num, bits):
raise ValueError("value {} (u{}) too large for u{} field"
.format(num, num.bit_length(), bits))
- return (num, bits)
+ return Bitfield(num, bits)
def i(num, bits):
""" parse a signed integer into a bitfield.
>>> i(8, 8)
- (8, 8)
+ Bitfield(0x8, 8)
>>> i(-4, 8)
- (252, 8)
- >>> i(127, 8)
- (127, 8)
+ Bitfield(0xfc, 8)
+ >>> i(0x7f, 8)
+ Bitfield(0x7f, 8)
>>> i(-128, 8)
- (128, 8)
+ Bitfield(0x80, 8)
>>> i(128, 8)
Traceback (most recent call last):
@@ -62,67 +136,11 @@ def i(num, bits):
def from_part(part):
""" parse a size-tagged subv part into a bit.
- >>> from_part((34, 2))
- (34, 2)
- >>> from_part((34, 2, 'extra'))
- (34, 2)
+ >>> from_part((0x12, 2))
+ Bitfield(0x12, 2)
+ >>> from_part((0x12, 2, 'extra'))
+ Bitfield(0x12, 2)
"""
val = int(part[0])
size = part[1]
- return (val, int(size))
-
-def concat(*parts):
- """ concatenate multiple bitfields.
-
- >>> concat((0b10, 2), (0b00, 2))
- (2, 4)
- >>> concat((0b1, 1), (0b0110, 4), (0b110, 3))
- (205, 8)
- """
- val, size = 0, 0
- for (pval, psize) in parts:
- val = val | pval << size
- size += psize
- return (val, size)
-
-def slice(bits, hi, lo, allow_empty=False):
- """ slice a bitfield given hi and lo bit indices.
-
- >>> slice((0x7f, 8), 7, 4)
- (7, 4)
- >>> slice((0b100, 3), 2, 2)
- (1, 1)
- >>> slice((0x12345678, 32), 7, 0)
- (120, 8)
- >>> slice((0x12345678, 32), 15, 8)
- (86, 8)
- >>> slice((0x12345678, 32), 23, 16)
- (52, 8)
-
- >>> slice((0xf, 4), 4, 0)
- Traceback (most recent call last):
- ...
- ValueError: slice [4:0] out of range (4 bit value)
- >>> slice((0xf, 4), 2, 3)
- Traceback (most recent call last):
- ...
- ValueError: cant slice reverse range
- >>> slice((0xf, 4), 2, 3, allow_empty=True)
- (0, 0)
- >>> slice((0xf, 4), 3, -1)
- Traceback (most recent call last):
- ...
- ValueError: slice [3:-1] out of range (4 bit value)
- """
- (val, size) = bits
-
- if hi < lo:
- if allow_empty:
- return empty
- raise ValueError("cant slice reverse range")
- elif lo < 0 or hi >= size:
- raise ValueError("slice [{}:{}] out of range ({} bit value)".format(hi, lo, size))
-
- size = hi - lo + 1
- val = (val >> lo) & ((1 << size) - 1)
- return (val, size)
+ return Bitfield(val, int(size))