# -*- coding: utf-8 -*- import struct class InvalidVarInt(Exception): pass class VarInt: """Implement the varint type used in mumble""" def __init__(self, value=0): self.value = value def encode(self): """Encode an integer in the VarInt format, returning a binary string""" result = bytearray() value = abs(self.value) if self.value < 0: if self.value >= -3: return struct.pack("!B", (0b11111100 | value)) else: result = struct.pack("!B", 0b11111000) if value <= 0x7f: return result + struct.pack("!B", value) elif value <= 0x3fff: return result + struct.pack("!H", 0x8000 | value) elif value <= 0x1fffff: return result + struct.pack("!BH", 0xc0 | (value >> 16), 0xffff & value) elif value <= 0xfffffff: return result + struct.pack("!L", 0xe0000000 | value) elif value <= 0xffffffff: return result + struct.pack("!BL", 0b11110000, value) else: return result + struct.pack("!BQ", 0b11110100, value) def decode(self, value): """Decode a VarInt contained in a binary string, returning an integer""" varint = value is_negative = False result = None size = 0 if len(varint) <= 0: raise InvalidVarInt("length can't be 0") (first, ) = struct.unpack("!B", varint[0:1]) if first & 0b11111100 == 0b11111000: is_negative = True size += 1 if len(varint) < 2: raise InvalidVarInt("Too short negative varint") varint = varint[1:] (first, ) = struct.unpack("!B", varint[0:1]) if first & 0b10000000 == 0b00000000: (result, ) = struct.unpack("!B", varint[0:1]) size += 1 elif first & 0b11111100 == 0b11111100: (result, ) = struct.unpack("!B", varint[0:1]) result &= 0b00000011 is_negative = True size += 1 elif first & 0b11000000 == 0b10000000: if len(varint) < 2: raise InvalidVarInt("Too short 2 bytes varint") (result, ) = struct.unpack("!H", varint[:2]) result &= 0b0011111111111111 size += 2 elif first & 0b11100000 == 0b11000000: if len(varint) < 3: raise InvalidVarInt("Too short 3 bytes varint") (result, ) = struct.unpack("!B", varint[0:1]) result &= 0b00011111 (tmp, ) = struct.unpack("!H", varint[1:3]) result = (result << 16) + tmp size += 3 elif first & 0b11110000 == 0b11100000: if len(varint) < 4: raise InvalidVarInt("Too short 4 bytes varint") (result, ) = struct.unpack("!L", varint[:4]) result &= 0x0fffffff size += 4 elif first & 0b11111100 == 0b11110000: if len(varint) < 5: raise InvalidVarInt("Too short 5 bytes varint") (result, ) = struct.unpack("!L", varint[1:5]) size += 5 elif first & 0b11111100 == 0b11110100: if len(varint) < 9: raise InvalidVarInt("Too short 9 bytes varint") (result, ) = struct.unpack("!Q", varint[1:9]) size += 9 if is_negative: self.value = - result else: self.value = result return size def tohex(buffer): """Used for debugging. Output a sting in hex format""" result = "\n" cpt1 = 0 cpt2 = 0 for byte in buffer: result += hex(ord(byte))[2:].zfill(2) cpt1 += 1 if cpt1 >= 4: result += " " cpt1 = 0 cpt2 += 1 if cpt2 >= 10: result += "\n" cpt2 = 0 return result