def pack(fmt, *values): tfmt, fmt = __get_type(fmt) if not fmt: return () if tfmt != '.': raise ValueError('Only variable-length encoding is currently supported') result = empty_pack i = 0 for offset, havesize, size, f, val in __pack_iter_fmt(fmt, values): if f == 'x': if not havesize: result += x00 else: result += x00 * size # Note: no value, don't increment i elif f in 'SsUu': if f == 'S' and '\0' in val: l = val.find('\0') else: l = len(val) if havesize or f == 's': if l > size: l = size elif (f == 'u' and offset != len(fmt) - 1) or f == 'U': result += pack_int(l) if _is_string(val) and f in 'Ss': result += str(val[:l]).encode() else: result += val[:l].encode() if f == 'S' and not havesize: result += x00 elif size > l and havesize: result += x00 * (size - l) elif f in 't': # bit type, size is number of bits if not havesize: size = 1 if size > 8: raise ValueError("bit count cannot be greater than 8 for 't' encoding") mask = (1 << size) - 1 if (mask & val) != val: raise ValueError("value out of range for 't' encoding") result += _chr(val) elif f in 'Bb': # byte type if not havesize: size = 1 for i in range(size): if f == 'B': v = val else: # Translate to maintain ordering with the sign bit. v = val + 0x80 if v > 255 or v < 0: raise ValueError("value out of range for 'B' encoding") result += _chr(v) else: # integral type result += pack_int(val) return result
def pack_int(x): if x < NEG_2BYTE_MIN: packed = struct.pack('>Q', x & UINT64_MASK) while packed and packed[0] == xff_entry: packed = packed[1:] return _chr(NEG_MULTI_MARKER | getbits(8 - len(packed), 4)) + packed elif x < NEG_1BYTE_MIN: x -= NEG_2BYTE_MIN return _chr(NEG_2BYTE_MARKER | getbits(x, 13, 8), getbits(x, 8)) elif x < 0: x -= NEG_1BYTE_MIN return _chr(NEG_1BYTE_MARKER | getbits(x, 6)) elif x <= POS_1BYTE_MAX: return _chr(POS_1BYTE_MARKER | getbits(x, 6)) elif x <= POS_2BYTE_MAX: x -= (POS_1BYTE_MAX + 1) return _chr(POS_2BYTE_MARKER | getbits(x, 13, 8), getbits(x, 8)) elif x == POS_2BYTE_MAX + 1: # This is a special case where we could store the value with # just a single byte, but we append a zero byte so that the # encoding doesn't get shorter for this one value. return _chr(POS_MULTI_MARKER | 0x1, 0) else: packed = struct.pack('>Q', x - (POS_2BYTE_MAX + 1)) while packed and packed[0] == x00_entry: packed = packed[1:] return _chr(POS_MULTI_MARKER | getbits(len(packed), 4)) + packed