## # # bencode.py python3 compatable bencode / bdecode # ## def _decode_int(data): """ decode integer from bytearray return int, remaining data """ data = data[1:] end = data.index(b'e') return int(data[:end],10), data[end+1:] def _decode_str(data): """ decode string from bytearray return string, remaining data """ start = data.index(b':') l = int(data[:start].decode(),10) if l <= 0: raise Exception('invalid string size: %d'%d) start += 1 ret = bytes(data[start:start+l]) data = data[start+l:] return ret, data def _decode_list(data): """ decode list from bytearray return list, remaining data """ ls = [] data = data[1:] while data[0] != ord(b'e'): elem, data = _decode(data) ls.append(elem) return ls, data[1:] def _decode_dict(data): """ decode dict from bytearray return dict, remaining data """ d = {} data = data[1:] while data[0] != ord(b'e'): k, data = _decode_str(data) v, data = _decode(data) d[k.decode()] = v return d, data[1:] def _decode(data): """ decode a bytearray return deserialized object, remaining data """ ch = chr(data[0]) if ch == 'l': return _decode_list(data) elif ch == 'i': return _decode_int(data) elif ch == 'd': return _decode_dict(data) elif ch.isdigit(): return _decode_str(data) else: raise Exception('could not deserialize data: %s'%data) def bdecode(data): """ decode a bytearray return deserialized object """ obj , data = _decode(data) if len(data) > 0: raise Exception('failed to deserialize, extra data: %s'%data) return obj def _encode_str(s,buff): """ encode string to a buffer """ s = bytearray(s) l = len(s) buff.append(bytearray(str(l)+':','utf-8')) buff.append(s) def _encode_int(i,buff): """ encode integer to a buffer """ buff.append(b'i') buff.append(bytearray(str(i),'ascii')) buff.append(b'e') def _encode_list(l,buff): """ encode list of elements to a buffer """ buff.append(b'l') for i in l: _encode(i,buff) buff.append(b'e') def _encode_dict(d,buff): """ encode dict """ buff.append(b'd') l = list(d.keys()) l.sort() for k in l: _encode(str(k),buff) _encode(d[k],buff) buff.append(b'e') def _encode(obj,buff): """ encode element obj to a buffer buff """ if isinstance(obj,str): _encode_str(bytearray(obj,'utf-8'),buff) elif isinstance(obj,bytes): _encode_str(bytearray(obj),buff) elif isinstance(obj,bytearray): _encode_str(obj,buff) elif str(obj).isdigit(): _encode_int(obj,buff) elif isinstance(obj,list): _encode_list(obj,buff) elif hasattr(obj,'keys') and hasattr(obj,'values'): _encode_dict(obj,buff) elif str(obj) in ['True','False']: _encode_int(int(obj and '1' or '0'),buff) else: raise Exception('non serializable object: %s'%obj) def bencode(obj): """ bencode element, return bytearray """ buff = [] _encode(obj,buff) ret = bytearray() for ba in buff: ret += ba return bytes(ret)