from binary import encode_bin, decode_bin


class BitStreamReader(object):
    __slots__ = ["substream", "buffer", "total_size"]
    def __init__(self, substream):
        self.substream = substream
        self.total_size = 0
        self.buffer = ""
    def close(self):
        if self.total_size % 8 != 0:
            raise ValueError("total size of read data must be a multiple of 8",
                self.total_size)
    def tell(self):
        return self.substream.tell()
    def seek(self, pos, whence = 0):
        self.buffer = ""
        self.total_size = 0
        self.substream.seek(pos, whence)
    def read(self, count):
        assert count >= 0
        l = len(self.buffer)
        if count == 0:
            data = ""
        elif count <= l:
            data = self.buffer[:count]
            self.buffer = self.buffer[count:]
        else:
            data = self.buffer
            count -= l
            bytes = count // 8
            if count & 7: 
                bytes += 1
            buf = encode_bin(self.substream.read(bytes))
            data += buf[:count]
            self.buffer = buf[count:]
        self.total_size += len(data)
        return data


class BitStreamWriter(object):
    __slots__ = ["substream", "buffer", "pos"]
    def __init__(self, substream):
        self.substream = substream
        self.buffer = []
        self.pos = 0
    def close(self):
        self.flush()
    def flush(self):
        bytes = decode_bin("".join(self.buffer))
        self.substream.write(bytes)
        self.buffer = []
        self.pos = 0
    def tell(self):
        return self.substream.tell() + self.pos // 8
    def seek(self, pos, whence = 0):
        self.flush()
        self.substream.seek(pos, whence)
    def write(self, data):
        if not data:
            return
        if type(data) is not str:
            raise TypeError("data must be a string, not %r" % (type(data),))
        self.buffer.append(data)
















