aboutsummaryrefslogtreecommitdiffstats
path: root/bits.py
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-05-29 11:50:25 +0000
committers-ol <s-ol@users.noreply.github.com>2020-05-29 11:50:25 +0000
commit517f3e141c3e3bede4e7b0c0ea7237544f28ce90 (patch)
treed8e12207c006bc5e03a0fea3ddbab261ac09e6c7 /bits.py
parentnew label-slice syntax in format.py; switch to doctest (diff)
downloadsubv-517f3e141c3e3bede4e7b0c0ea7237544f28ce90.tar.gz
subv-517f3e141c3e3bede4e7b0c0ea7237544f28ce90.zip
switch bit.py, subv.py to doctest
Diffstat (limited to 'bits.py')
-rw-r--r--bits.py156
1 files changed, 99 insertions, 57 deletions
diff --git a/bits.py b/bits.py
index 162bcb0..b3409da 100644
--- a/bits.py
+++ b/bits.py
@@ -1,80 +1,122 @@
def u(num, bits):
+ """ parse an unsigned integer into a bitfield.
+
+ >>> u(0x08, 8)
+ (8, 8)
+ >>> u(0xff, 8)
+ (255, 8)
+
+ >>> u(0xf0, 7)
+ Traceback (most recent call last):
+ ...
+ ValueError: value 240 (u8) too large for u7 field
+ >>> u(-1, 8)
+ Traceback (most recent call last):
+ ...
+ ValueError: negative value not allowed: -1
+ """
if num < 0:
raise ValueError("negative value not allowed: {}".format(num))
if num.bit_length() > bits:
- raise ValueError("value too large for u{} field: {} ({} bits)",
- bits, num, num.bit_length())
+ raise ValueError("value {} (u{}) too large for u{} field"
+ .format(num, num.bit_length(), bits))
return (num, bits)
def i(num, bits):
- if num < 0:
- num = (1 << bits) + num
+ """ parse a signed integer into a bitfield.
+
+ >>> i(8, 8)
+ (8, 8)
+ >>> i(-4, 8)
+ (252, 8)
+ >>> i(127, 8)
+ (127, 8)
+ >>> i(-128, 8)
+ (128, 8)
+
+ >>> i(128, 8)
+ Traceback (most recent call last):
+ ...
+ ValueError: value 128 (i9) too large for i8 field [-128;127]
+ >>> i(-129, 8)
+ Traceback (most recent call last):
+ ...
+ ValueError: value -129 (i9) too large for i8 field [-128;127]
+ """
+ min = -(1 << (bits - 1))
+ max = -1 - min
+
+ if num > max or num < min:
+ raise ValueError("value {} (i{}) too large for i{} field [{};{}]"
+ .format(num, num.bit_length()+1, bits, min, max))
+
+ if num < 0:
+ num = (1 << bits) + num
- return u(num, bits)
+ return u(num, bits)
def from_part(part):
- val, size = part
- return (val, int(size))
+ """ parse a size-tagged subv part into a bit.
+
+ >>> from_part((34, 2))
+ (34, 2)
+ >>> from_part((34, 2, 'extra'))
+ (34, 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, top, bottom):
+def slice(bits, hi, lo):
+ """ 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), 3, -1)
+ Traceback (most recent call last):
+ ...
+ ValueError: slice [3:-1] out of range (4 bit value)
+ """
(val, size) = bits
- if top < bottom:
+ if hi < lo:
raise ValueError("cant slice reverse range")
- elif bottom < 0:
- raise ValueError("negative slice index")
- elif top >= size:
- raise ValueError("cant slice [{}:{}] from {} bit value".format(top, bottom, size))
-
- width = top - bottom + 1
- val = (val >> bottom) & ((1 << width) - 1)
- return (val, width)
-
-import unittest
-class TestHelpers(unittest.TestCase):
- def test_concat(self):
- self.assertEqual(
- concat((0b10, 2), (0b00, 2)),
- (0b0010, 4)
- )
- self.assertEqual(
- concat((0b1, 1), (0b0110, 4), (0b110, 3)),
- (0b11001101, 8)
- )
-
- def test_slice(self):
- self.assertEqual(
- slice((0x7f, 8), 7, 4),
- (0x7, 4)
- )
- self.assertEqual(
- slice((0b100, 3), 2, 2),
- (0b1, 1)
- )
- with self.assertRaises(ValueError):
- slice((0xf, 4), 4, 0)
- with self.assertRaises(ValueError):
- slice((0xf, 4), 2, 3)
- with self.assertRaises(ValueError):
- slice((0xf, 4), 3, -1)
- self.assertEqual(
- slice((0x12345678, 32), 7, 0),
- (0x78, 8)
- )
- self.assertEqual(
- slice((0x12345678, 32), 15, 8),
- (0x56, 8)
- )
- self.assertEqual(
- slice((0x12345678, 32), 23, 16),
- (0x34, 8)
- )
+ 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)