From 5d51d763e2947e1c528fadba120bb88d00a688d7 Mon Sep 17 00:00:00 2001 From: Vova Date: Fri, 25 Sep 2026 19:03:08 +0300 Subject: [PATCH 1/2] Update constants.py --- src/part1/constants.py | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/src/part1/constants.py b/src/part1/constants.py index 9abfbba2..4aa78ccf 100644 --- a/src/part1/constants.py +++ b/src/part1/constants.py @@ -1,38 +1,17 @@ -"""Bit layout and default epoch for the Twitter Snowflake ID generator. - -A Snowflake identifier is a 63-bit positive integer packed as follows, from -the most significant bit downwards: - - 1 bit unused sign bit, always zero - 41 bits milliseconds elapsed since a custom epoch - 10 bits node identifier - 12 bits per-millisecond sequence counter - -The shift and mask constants should be derived from the bit widths so that -changing a width keeps everything else consistent. -""" - from typing import Final - -# TODO: замените заглушки (0) на корректные значения, см. TASK.md. -# Все константы, зависящие от ёмкостей *_BITS, должны вычисляться из них. - # Twitter's original Snowflake epoch: 2010-11-04 01:42:54.657 UTC. -EPOCH_MS_DEFAULT: Final[int] = 0 +EPOCH_MS_DEFAULT: Final[int] = 1288834974657 # Node identifier used when the caller does not supply one. -NODE_ID_DEFAULT: Final[int] = 0 +NODE_ID_DEFAULT: Final[int] = 1 # Width of each field, in bits. -TIMESTAMP_BITS: Final[int] = 0 -NODE_ID_BITS: Final[int] = 0 -SEQUENCE_ID_BITS: Final[int] = 0 +TIMESTAMP_BITS: Final[int] = 41 +NODE_ID_BITS: Final[int] = 10 +SEQUENCE_ID_BITS: Final[int] = 12 # Largest value each field can hold. -TIMESTAMP_MS_MAX: Final[int] = 0 -NODE_ID_MAX: Final[int] = 0 -SEQUENCE_ID_MAX: Final[int] = 0 - -# Здесь можно добавить собственные вспомогательные константы -# (например, сдвиги полей при сборке идентификатора). +TIMESTAMP_MS_MAX: Final[int] = (1 << TIMESTAMP_BITS) - 1 +NODE_ID_MAX: Final[int] = (1 << NODE_ID_BITS) - 1 +SEQUENCE_ID_MAX: Final[int] = (1 << SEQUENCE_ID_BITS) - 1 From f0dd66ababa6483c32231a0420db6cafae024b0b Mon Sep 17 00:00:00 2001 From: Vova Date: Fri, 25 Sep 2026 19:03:29 +0300 Subject: [PATCH 2/2] Update snowflake.py --- src/part1/snowflake.py | 121 ++++++++++++----------------------------- 1 file changed, 35 insertions(+), 86 deletions(-) diff --git a/src/part1/snowflake.py b/src/part1/snowflake.py index d3abf050..7be71625 100644 --- a/src/part1/snowflake.py +++ b/src/part1/snowflake.py @@ -1,18 +1,3 @@ -"""Twitter Snowflake identifier generator. - -Snowflake produces roughly time-ordered 64-bit integer identifiers without -coordination between nodes. Each identifier packs the milliseconds elapsed -since a custom epoch, a node identifier and a per-millisecond sequence counter -into a single 63-bit positive integer (see ``constants`` for the layout). - -The public entry point is :func:`generate_snowflake_id`. It is stateless: the -caller passes the sequence counter on every call and is responsible for -advancing it within a millisecond and resetting it when the clock ticks over. - -Each packed field can be read back on its own with :func:`decode_timestamp_ms`, -:func:`decode_node_id` and :func:`decode_sequence_id`. -""" - import time # noqa: F401 from .constants import ( # noqa: F401 @@ -21,65 +6,27 @@ NODE_ID_MAX, SEQUENCE_ID_MAX, TIMESTAMP_MS_MAX, + TIMESTAMP_BITS, + NODE_ID_BITS, + SEQUENCE_ID_BITS, + ) def read_current_millis(epoch_ms: int) -> int: - """Read the number of milliseconds elapsed since the custom epoch. - - Args: - epoch_ms: The custom epoch expressed as Unix milliseconds. - - Returns: - The count of whole milliseconds between ``epoch_ms`` and now. May be - negative if ``epoch_ms`` lies in the future. - """ - # TODO: реализуйте функцию - return 0 - + return time.time_ns()//10**6 - epoch_ms def decode_timestamp_ms(snowflake_id: int, epoch_ms: int = EPOCH_MS_DEFAULT) -> int: - """Read the timestamp field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced against the same epoch. - epoch_ms: The epoch the identifier was generated against. Defaults to - the original Twitter epoch (2010-11-04 01:42:54.657 UTC). - - Returns: - The absolute Unix time in milliseconds at which the identifier was - generated. - """ - # TODO: реализуйте функцию - return 0 + stored_time = snowflake_id >> (NODE_ID_BITS + SEQUENCE_ID_BITS) + return stored_time + epoch_ms def decode_node_id(snowflake_id: int) -> int: - """Read the node identifier field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced by :func:`generate_snowflake_id`. - - Returns: - The node identifier packed into ``snowflake_id``, in the range - ``[0, NODE_ID_MAX]``. - """ - # TODO: реализуйте функцию - return 0 + return (snowflake_id >> SEQUENCE_ID_BITS) & NODE_ID_MAX def decode_sequence_id(snowflake_id: int) -> int: - """Read the sequence counter field out of a Snowflake identifier. - - Args: - snowflake_id: An identifier produced by :func:`generate_snowflake_id`. - - Returns: - The per-millisecond sequence counter packed into ``snowflake_id``, in - the range ``[0, SEQUENCE_ID_MAX]``. - """ - # TODO: реализуйте функцию - return 0 + return snowflake_id & SEQUENCE_ID_MAX def generate_snowflake_id( @@ -87,28 +34,30 @@ def generate_snowflake_id( node_id: int = NODE_ID_DEFAULT, epoch_ms: int = EPOCH_MS_DEFAULT, ) -> int | None: - """Build and return a Snowflake identifier for the current millisecond. - - The function is stateless: the caller passes the per-millisecond sequence - counter explicitly. It is the caller's responsibility to increment - ``sequence_id`` for identifiers minted within the same millisecond and to - reset it once the clock advances. - - Args: - sequence_id: The per-millisecond sequence counter, in the range - ``[0, SEQUENCE_ID_MAX]``. - node_id: The identifier of this node, in the range ``[0, NODE_ID_MAX]``. - Optional; defaults to ``NODE_ID_DEFAULT``. - epoch_ms: The start of the epoch as Unix milliseconds. Optional; - defaults to the original Twitter epoch (2010-11-04 01:42:54.657 - UTC). - Returns: - The Snowflake identifier as a positive 63-bit integer, or ``None`` if - ``node_id`` is outside ``[0, NODE_ID_MAX]``, ``sequence_id`` is outside - ``[0, SEQUENCE_ID_MAX]``, or the elapsed time no longer fits in the - timestamp field (roughly 69 years after ``epoch_ms``). In each of those - cases an explanatory message is printed to stdout first. - """ - # TODO: реализуйте функцию - return 0 + if not (0 <= node_id <= NODE_ID_MAX): + print( + f"node_id must be in [0, {NODE_ID_MAX}], got {node_id}" + ) + return None + + if not (0 <= sequence_id <= SEQUENCE_ID_MAX): + print( + f"sequence_id must be in [0, {SEQUENCE_ID_MAX}], got {sequence_id}" + ) + return None + + timestamp_ms = read_current_millis(epoch_ms) + if not (0 <= timestamp_ms <= TIMESTAMP_MS_MAX): + print( + f"timestamp_ms value is overflows {TIMESTAMP_BITS} bits:" + f"{timestamp_ms} > {TIMESTAMP_MS_MAX}" + ) + return None + + snowflake_id = ( + (timestamp_ms << (NODE_ID_BITS + SEQUENCE_ID_BITS)) + | (node_id << SEQUENCE_ID_BITS) + | sequence_id + ) + return snowflake_id