import base64 import enum import random import re import time from typing import overload from ..coding import base32_sortable LAST_GENERATED_TID = 0 CLOCK_IDENTIFIER = random.randint(0, ~-(1 << 10)) class TID: __lexicon_string_format__ = "tid" PATTERN = re.compile(r"^[234567abcdefghij][234567abcdefghijklmnopqrstuvwxyz]{12}$") __value: int def __init__(self, tid: bytes | int | str, /): if isinstance(tid, bytes): tid = int.from_bytes(tid, "big", signed=False) if isinstance(tid, str): if not self.PATTERN.match(tid): raise ValueError("Invalid TID: does not match pattern") tid = base32_sortable.decode_int(tid) if not isinstance(tid, int): raise TypeError( f"Invalid TID: cannot construct from '{type(tid).__name__}'" ) if tid >> 63: raise ValueError("Invalid TID: first bytes high bit must be 0") self.__value = tid @property def clock_identifier(self) -> int: return self.__value & ~-(1 << 10) @property def timestamp(self) -> int: return self.__value >> 10 @property def value(self) -> int: return self.__value def as_bytes(self) -> bytes: return self.__value.to_bytes(8, "big", signed=False) def __str__(self) -> str: return f"{base32_sortable.encode_int(self.__value, 64)}" def __repr__(self) -> str: return f"TID({self.__value})" def __lt__(self, other) -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value < other.__value def __le__(self, other) -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value <= other.__value def __gt__(self, other) -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value > other.__value def __ge__(self, other) -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value >= other.__value def __eq__(self, other) -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value == other.__value def __neq__(self, other: "TID") -> bool: if not isinstance(other, TID): raise TypeError( f"Cannot compare '{type(self).__name__}' to '{type(other).__name__}'" ) return self.__value != other.__value @classmethod def now(cls: "type[TID]") -> "TID": global LAST_GENERATED_TID timestamp = time.monotonic_ns() // 1000 if timestamp <= LAST_GENERATED_TID: LAST_GENERATED_TID += 1 timestamp = LAST_GENERATED_TID value = ((timestamp & ~-(1 << 53)) << 10) + CLOCK_IDENTIFIER return cls(value)