def _float_adjust(v, encode): if encode and six.indexbytes(v, 0) & 0x80 != 0x00: return b''.join(map(lambda x: six.int2byte(x ^ 0xff), six.iterbytes(v))) elif not encode and six.indexbytes(v, 0) & 0x80 != 0x80: return b''.join(map(lambda x: six.int2byte(x ^ 0xff), six.iterbytes(v))) else: return six.int2byte(six.indexbytes(v, 0) ^ 0x80) + v[1:]
def _encode(value, nested=False): # returns [code][data] (code != 0xFF) # encoded values are self-terminating # sorting need to work too! if value == None: # ==, not is, because some fdb.impl.Value are equal to None if nested: return b''.join([six.int2byte(NULL_CODE), six.int2byte(0xff)]), -1 else: return b''.join([six.int2byte(NULL_CODE)]), -1 elif isinstance(value, bytes): # also gets non-None fdb.impl.Value return six.int2byte(BYTES_CODE) + value.replace( b'\x00', b'\x00\xFF') + b'\x00', -1 elif isinstance(value, six.text_type): return six.int2byte(STRING_CODE) + value.encode('utf-8').replace( b'\x00', b'\x00\xFF') + b'\x00', -1 elif isinstance(value, six.integer_types) and ( not isinstance(value, bool) or (hasattr(fdb, '_version') and fdb._version < 500)): if value == 0: return b''.join([six.int2byte(INT_ZERO_CODE)]), -1 elif value > 0: if value >= _size_limits[-1]: length = (_bit_length(value) + 7) // 8 data = [six.int2byte(POS_INT_END), six.int2byte(length)] for i in _range(length - 1, -1, -1): data.append(six.int2byte((value >> (8 * i)) & 0xff)) return b''.join(data), -1 n = bisect_left(_size_limits, value) return six.int2byte(INT_ZERO_CODE + n) + struct.pack( ">Q", value)[-n:], -1 else: if -value >= _size_limits[-1]: length = (_bit_length(value) + 7) // 8 value += (1 << (length * 8)) - 1 data = [ six.int2byte(NEG_INT_START), six.int2byte(length ^ 0xff) ] for i in _range(length - 1, -1, -1): data.append(six.int2byte((value >> (8 * i)) & 0xff)) return b''.join(data), -1 n = bisect_left(_size_limits, -value) maxv = _size_limits[n] return six.int2byte(INT_ZERO_CODE - n) + struct.pack( ">Q", maxv + value)[-n:], -1 elif isinstance(value, ctypes.c_float) or isinstance(value, SingleFloat): return six.int2byte(FLOAT_CODE) + _float_adjust( struct.pack(">f", value.value), True), -1 elif isinstance(value, ctypes.c_double): return six.int2byte(DOUBLE_CODE) + _float_adjust( struct.pack(">d", value.value), True), -1 elif isinstance(value, float): return six.int2byte(DOUBLE_CODE) + _float_adjust( struct.pack(">d", value), True), -1 elif isinstance(value, uuid.UUID): return six.int2byte(UUID_CODE) + value.bytes, -1 elif isinstance(value, bool): if value: return b''.join([six.int2byte(TRUE_CODE)]), -1 else: return b''.join([six.int2byte(FALSE_CODE)]), -1 elif isinstance(value, Versionstamp): version_pos = -1 if value.is_complete() else 1 return six.int2byte(VERSIONSTAMP_CODE) + value.to_bytes(), version_pos elif isinstance(value, tuple) or isinstance(value, list): child_bytes, version_pos = _reduce_children( map(lambda x: _encode(x, True), value)) new_version_pos = -1 if version_pos < 0 else version_pos + 1 return b''.join([six.int2byte(NESTED_CODE)] + child_bytes + [six.int2byte(0x00)]), new_version_pos else: raise ValueError("Unsupported data type: " + str(type(value)))
class Versionstamp(object): LENGTH = 12 _TR_VERSION_LEN = 10 _MAX_USER_VERSION = (1 << 16) - 1 _UNSET_TR_VERSION = 10 * six.int2byte(0xff) _STRUCT_FORMAT_STRING = '>' + str(_TR_VERSION_LEN) + 'sH' @classmethod def validate_tr_version(cls, tr_version): if tr_version is None: return if not isinstance(tr_version, bytes): raise TypeError("Global version has illegal type " + str(type(tr_version)) + " (requires bytes)") elif len(tr_version) != cls._TR_VERSION_LEN: raise ValueError("Global version has incorrect length " + str(len(tr_version)) + " (requires " + str(cls._TR_VERSION_LEN) + ")") @classmethod def validate_user_version(cls, user_version): if not isinstance(user_version, six.integer_types): raise TypeError("Local version has illegal type " + str(type(user_version)) + " (requires integer type)") elif user_version < 0 or user_version > cls._MAX_USER_VERSION: raise ValueError("Local version has value " + str(user_version) + " which is out of range") def __init__(self, tr_version=None, user_version=0): Versionstamp.validate_tr_version(tr_version) Versionstamp.validate_user_version(user_version) self.tr_version = tr_version self.user_version = user_version @staticmethod def incomplete(user_version=0): return Versionstamp(user_version=user_version) @classmethod def from_bytes(cls, v, start=0): if not isinstance(v, bytes): raise TypeError("Cannot parse versionstamp from non-byte string") elif len(v) - start < cls.LENGTH: raise ValueError("Versionstamp byte string is too short (only " + str(len(v) - start) + " bytes to read from") else: tr_version = v[start:start + cls._TR_VERSION_LEN] if tr_version == cls._UNSET_TR_VERSION: tr_version = None user_version = six.indexbytes(v, start + cls._TR_VERSION_LEN) * ( 1 << 8) + six.indexbytes(v, start + cls._TR_VERSION_LEN + 1) return Versionstamp(tr_version, user_version) def is_complete(self): return self.tr_version is not None def __repr__(self): return "fdb.tuple.Versionstamp(" + repr(self.tr_version) + ", " + repr( self.user_version) + ")" def __str__(self): return "Versionstamp(" + repr(self.tr_version) + ", " + str( self.user_version) + ")" def to_bytes(self): return struct.pack( self._STRUCT_FORMAT_STRING, self.tr_version if self.is_complete() else self._UNSET_TR_VERSION, self.user_version) def completed(self, new_tr_version): if self.is_complete(): raise RuntimeError("Versionstamp already completed") else: return Versionstamp(new_tr_version, self.user_version) # Comparisons def __eq__(self, other): if isinstance(other, Versionstamp): return self.tr_version == other.tr_version and self.user_version == other.user_version else: return False def __ne__(self, other): return not (self == other) def __cmp__(self, other): if self.is_complete(): if other.is_complete(): if self.tr_version == other.tr_version: return cmp(self.user_version, other.user_version) else: return cmp(self.tr_version, other.tr_version) else: # All complete are less than all incomplete. return -1 else: if other.is_complete(): # All incomplete are greater than all complete return 1 else: return cmp(self.user_version, other.user_version) def __hash__(self): if self.tr_version is None: return hash(self.user_version) else: return hash(self.tr_version) * 37 ^ hash(self.user_version) def __nonzero__(self): return self.is_complete()
def randomElement(): r = random.randint(0, 9) if r == 0: if random.random() < 0.5: chars = [b'\x00', b'\x01', b'a', b'7', b'\xfe', b'\ff'] return b''.join( [random.choice(chars) for c in _range(random.randint(0, 5))]) else: return b''.join([ six.int2byte(random.randint(0, 255)) for _ in _range(random.randint(0, 10)) ]) elif r == 1: if random.random() < 0.5: chars = [ u('\x00'), u('\x01'), u('a'), u('7'), u('\xfe'), u('\ff'), u('\u0000'), u('\u0001'), u('\uffff'), u('\uff00'), u('\U0001f4a9') ] return u('').join( [random.choice(chars) for c in _range(random.randint(0, 10))]) else: return u('').join( [randomUnicode() for _ in _range(random.randint(0, 10))]) elif r == 2: return random.choice([-1, 1]) * min( 2**random.randint(0, 2040) + random.randint(-10, 10), 2**2040 - 1) elif r == 3: return random.choice([-1, 1]) * 2**random.randint( 0, 64) + random.randint(-10, 10) elif r == 4: return None elif r == 5: ret = random.choice([ float('-nan'), float('-inf'), -0.0, 0.0, float('inf'), float('nan') ]) if random.random() < 0.5: return SingleFloat(ret) else: return ret elif r == 6: is_double = random.random() < 0.5 byte_str = b''.join([ six.int2byte(random.randint(0, 255)) for _ in _range(8 if is_double else 4) ]) if is_double: return struct.unpack(">d", byte_str)[0] else: return SingleFloat(struct.unpack(">f", byte_str)[0]) elif r == 7: return random.random() < 0.5 elif r == 8: return uuid.uuid4() elif r == 9: return [randomElement() for _ in _range(random.randint(0, 5))]