diff --git a/TODO.md b/TODO.md index 4d73b55..880d3af 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,6 @@ Things We Need To Do ==================== -* `unpack.py` like golang's `encoding/binary` * documentation * remove lingering py2-isms * more logical way to chain together dispatcher, tcp resequencer diff --git a/netarch/unpack.py b/netarch/unpack.py new file mode 100644 index 0000000..7a5b80b --- /dev/null +++ b/netarch/unpack.py @@ -0,0 +1,73 @@ +#! /usr/bin/python3 + +ENDIAN_LITTLE = 1 +ENDIAN_BIG = 2 +ENDIAN_MIDDLE = 3 +ENDIAN_NETWORK = ENDIAN_BIG + +class Unpacker: + """Class that lets you peel values off + + >>> u = Unpacker(bytes((1, 0,2, 0,0,0,3, 0,0,0,0,0,0,0,4))) + >>> u.uint8() + 1 + >>> u.uint16() + 2 + >>> u.uint32() + 3 + >>> u.uint64() + 4 + + >>> u = Unpacker(bytes((1,0, 104,105)), ENDIAN_LITTLE) + >>> u.uint16() + 1 + >>> u.buf + b'hi' + + >>> u = Unpacker(bytes((1,0, 0,2))) + >>> u.uint16(ENDIAN_LITTLE) + 1 + >>> u.uint(16, ENDIAN_BIG) + 2 + + >>> u = Unpacker(bytes((0,1,2,3)), ENDIAN_MIDDLE) + >>> '%08x' % u.uint32() + '01000302' + """ + + def __init__(self, buf, endian=ENDIAN_NETWORK): + self.endian = endian + self.buf = buf + + def uint(self, size, endian=None): + endian = endian or self.endian + if size not in (8, 16, 32, 64): + # XXX: I'm pretty sure this can be done, but I don't want to code it up right now. + raise ValueError("Can't do weird sizes") + noctets = size // 8 + if endian == ENDIAN_BIG: + r = range(0, noctets) + elif endian == ENDIAN_LITTLE: + r = range(noctets-1, -1, -1) + elif endian == ENDIAN_MIDDLE: + r = (1, 0, 3, 2, 5, 4, 7, 6)[:noctets] + else: + raise ValueError("Unsupported byte order") + pull, self.buf = self.buf[:noctets], self.buf[noctets:] + acc = 0 + for i in r: + acc = (acc << 8) | pull[i] + return acc + + def uint8(self): + return self.uint(8) + def uint16(self, endian=None): + return self.uint(16, endian) + def uint32(self, endian=None): + return self.uint(32, endian) + def uint64(self, endian=None): + return self.uint(64, endian) + +if __name__ == "__main__": + import doctest + doctest.testmod()