Coverage for faststream / _internal / utils / nuid.py: 87%
35 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-05-08 01:48 +0000
1# Copyright 2016-2018 The NATS Authors
2# Licensed under the Apache License, Version 2.0 (the "License");
3# you may not use this file except in compliance with the License.
4# You may obtain a copy of the License at
5#
6# http://www.apache.org/licenses/LICENSE-2.0
7#
8# Unless required by applicable law or agreed to in writing, software
9# distributed under the License is distributed on an "AS IS" BASIS,
10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11# See the License for the specific language governing permissions and
12# limitations under the License.
14from __future__ import annotations
16from random import Random
17from secrets import randbelow, token_bytes
18from sys import maxsize as max_int
20DIGITS = b"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
21BASE = 62
22PREFIX_LENGTH = 12
23SEQ_LENGTH = 10
24MAX_SEQ = BASE**10
25MIN_INC = 33
26MAX_INC = 333
27INC = MAX_INC - MIN_INC
28TOTAL_LENGTH = PREFIX_LENGTH + SEQ_LENGTH
31class NUID:
32 """NUID created is a utility to create a new id.
34 NUID is an implementation of the approach for fast generation
35 of unique identifiers used for inboxes in NATS.
36 """
38 def __init__(self) -> None:
39 self._prand = Random(randbelow(max_int)) # nosec B311 # noqa: S311
40 self._seq = self._prand.randint(0, MAX_SEQ)
41 self._inc = MIN_INC + self._prand.randint(BASE + 1, INC)
42 self._prefix = bytearray()
43 self.randomize_prefix()
45 def next(self) -> bytearray:
46 """Next returns the next unique identifier."""
47 self._seq += self._inc
48 if self._seq >= MAX_SEQ: 48 ↛ 49line 48 didn't jump to line 49 because the condition on line 48 was never true
49 self.randomize_prefix()
50 self.reset_sequential()
52 l_seq = self._seq
53 prefix = self._prefix[:]
54 suffix = bytearray(SEQ_LENGTH)
55 for i in reversed(range(SEQ_LENGTH)):
56 suffix[i] = DIGITS[int(l_seq) % BASE]
57 l_seq //= BASE
59 prefix.extend(suffix)
60 return prefix
62 def randomize_prefix(self) -> None:
63 random_bytes = token_bytes(PREFIX_LENGTH)
64 self._prefix = bytearray(DIGITS[c % BASE] for c in random_bytes)
66 def reset_sequential(self) -> None:
67 self._seq = self._prand.randint(0, MAX_SEQ)
68 self._inc = MIN_INC + self._prand.randint(0, INC)