From 80cf8fb6295b72026eb41c43d9678ab57388f664 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Wed, 5 Mar 2025 10:23:36 +0600 Subject: [PATCH 1/2] uid: sequence --- src/GetSequence.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/GetSequence.php b/src/GetSequence.php index b79da56..e6a434e 100644 --- a/src/GetSequence.php +++ b/src/GetSequence.php @@ -17,17 +17,17 @@ trait GetSequence * @param int $dateTime The current time. * @param string $machineId The machine ID. * @param string $type The type identifier. - * @param int $maxSequence The maximum sequence number. + * @param int $maxSequenceLength The maximum length of the sequence number. * @return int The generated sequence number, or `0` if lock was not acquired. */ - private static function sequence(int $dateTime, string $machineId, string $type, int $maxSequence = 0): int + private static function sequence(int $dateTime, string $machineId, string $type, int $maxSequenceLength = 0): int { self::$fileLocation ??= sys_get_temp_dir() . DIRECTORY_SEPARATOR . "uid-$type-$machineId.seq"; // Attempt to acquire a lock $handle = self::acquireLock(); if (!$handle) { - return getmypid() % ($maxSequence + 1); + return getmypid() % ((-1 ^ (-1 << $maxSequenceLength)) + 1); } // Update sequence From 9aa9eead063a6aff48036e6489aa4ff06262fe93 Mon Sep 17 00:00:00 2001 From: "A. B. M. Mahmudul Hasan" Date: Wed, 5 Mar 2025 12:25:10 +0600 Subject: [PATCH 2/2] uid: sequence --- src/GetSequence.php | 4 ++-- src/TBSL.php | 6 +++--- tests/SnowflakeTest.php | 44 ++++++++++++++++++++++++++++++++++++++++- tests/SonyflakeTest.php | 42 ++++++++++++++++++++++++++++++++++++++- tests/TBSLTest.php | 33 ++++++++++++++++++++++++++++--- 5 files changed, 119 insertions(+), 10 deletions(-) diff --git a/src/GetSequence.php b/src/GetSequence.php index e6a434e..231a161 100644 --- a/src/GetSequence.php +++ b/src/GetSequence.php @@ -15,12 +15,12 @@ trait GetSequence * Generates a sequence number based on the current time. * * @param int $dateTime The current time. - * @param string $machineId The machine ID. + * @param int $machineId The machine ID. * @param string $type The type identifier. * @param int $maxSequenceLength The maximum length of the sequence number. * @return int The generated sequence number, or `0` if lock was not acquired. */ - private static function sequence(int $dateTime, string $machineId, string $type, int $maxSequenceLength = 0): int + private static function sequence(int $dateTime, int $machineId, string $type, int $maxSequenceLength = 0): int { self::$fileLocation ??= sys_get_temp_dir() . DIRECTORY_SEPARATOR . "uid-$type-$machineId.seq"; diff --git a/src/TBSL.php b/src/TBSL.php index 1bd7857..5d94deb 100644 --- a/src/TBSL.php +++ b/src/TBSL.php @@ -31,7 +31,7 @@ public static function generate(int $machineId = 0, bool $sequenced = false): st return strtoupper(sprintf( '%015s%05s', $storeData, - substr(self::sequencedGenerate($machineId, $sequenced, (int)$timeSequence), -1, 5) + substr(self::sequencedGenerate($machineId, $sequenced, (int)$timeSequence), 0, 5) )); } @@ -62,7 +62,7 @@ private static function sequencedGenerate(int $machineId, bool $enableSequence, public static function parse(string $tbsl): array { $data = [ - 'isValid' => preg_match('/^[0-9A-F]{20}$/', $tbsl), + 'isValid' => (bool)preg_match('/^[0-9A-F]{20}$/', $tbsl), 'time' => null, 'machineId' => null, ]; @@ -73,7 +73,7 @@ public static function parse(string $tbsl): array $storeData = base_convert(substr($tbsl, 0, 15), 16, 10); $data['time'] = new DateTimeImmutable('@' . substr($storeData, 0, 10) . '.' . substr($storeData, 10, 6)); - $data['machineId'] = substr($storeData, -2); + $data['machineId'] = (int)substr($storeData, -2); return $data; } diff --git a/tests/SnowflakeTest.php b/tests/SnowflakeTest.php index 20b848e..0ff3b18 100644 --- a/tests/SnowflakeTest.php +++ b/tests/SnowflakeTest.php @@ -2,11 +2,53 @@ use Infocyph\UID\Snowflake; -test('Basic', function () { +test('Snowflake Basic Functionality', function () { $sf = Snowflake::generate(); $parsed = Snowflake::parse($sf); + expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) ->and($parsed['worker_id'])->toBe(0) ->and($parsed['datacenter_id'])->toBe(0); }); +test('Snowflake ID Uniqueness', function () { + $id1 = Snowflake::generate(); + usleep(10); + $id2 = Snowflake::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('Snowflake Sequential Order', function () { + $id1 = Snowflake::generate(); + usleep(10); + $id2 = Snowflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + +test('Snowflake Datacenter and Worker Differentiation', function () { + $id1 = Snowflake::generate(1, 1); + $id2 = Snowflake::generate(2, 2); + + $parsed1 = Snowflake::parse($id1); + $parsed2 = Snowflake::parse($id2); + + expect($parsed1['worker_id'])->not->toBe($parsed2['worker_id']) + ->and($parsed1['datacenter_id'])->not->toBe($parsed2['datacenter_id']); +}); + +test('Snowflake Max Sequence Handling', function () { + $maxSeq = (-1 ^ (-1 << 12)); + + $id1 = Snowflake::generate(); + for ($i = 0; $i <= $maxSeq; $i++) { + $id1 = Snowflake::generate(); + } + usleep(10); + $id2 = Snowflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + + diff --git a/tests/SonyflakeTest.php b/tests/SonyflakeTest.php index ede1091..0734047 100644 --- a/tests/SonyflakeTest.php +++ b/tests/SonyflakeTest.php @@ -2,10 +2,50 @@ use Infocyph\UID\Sonyflake; -test('Basic', function () { +test('Sonyflake Basic Functionality', function () { $sf = Sonyflake::generate(); $parsed = Sonyflake::parse($sf); + expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) ->and($parsed['machine_id'])->toBe(0); }); +test('Sonyflake ID Uniqueness', function () { + $id1 = Sonyflake::generate(); + usleep(10); + $id2 = Sonyflake::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('Sonyflake Sequential Order', function () { + $id1 = Sonyflake::generate(); + usleep(10); + $id2 = Sonyflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + +test('Sonyflake Machine ID Differentiation', function () { + $id1 = Sonyflake::generate(1); + $id2 = Sonyflake::generate(2); + + $parsed1 = Sonyflake::parse($id1); + $parsed2 = Sonyflake::parse($id2); + + expect($parsed1['machine_id'])->not->toBe($parsed2['machine_id']); +}); + +test('Sonyflake Max Sequence Handling', function () { + $maxSeq = (-1 ^ (-1 << 8)); + + $id1 = Sonyflake::generate(); + for ($i = 0; $i <= $maxSeq; $i++) { + $id1 = Sonyflake::generate(); + } + usleep(10); + $id2 = Sonyflake::generate(); + + expect((int) $id2)->toBeGreaterThan((int) $id1); +}); + diff --git a/tests/TBSLTest.php b/tests/TBSLTest.php index d5aeb1f..8441a60 100644 --- a/tests/TBSLTest.php +++ b/tests/TBSLTest.php @@ -2,10 +2,37 @@ use Infocyph\UID\TBSL; -test('Basic', function () { +test('TBSL Basic Functionality', function () { $sf = TBSL::generate(); $parsed = TBSL::parse($sf); - expect($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) - ->and($parsed['machineId'])->toBe('00'); + expect($parsed['isValid'])->toBeTrue() + ->and($parsed['time']->getTimestamp())->toBeBetween(time() - 1, time()) + ->and($parsed['machineId'])->toBe(0); +}); + +test('TBSL ID Uniqueness', function () { + $id1 = TBSL::generate(); + usleep(10); // Ensure slight time difference + $id2 = TBSL::generate(); + + expect($id1)->not->toBe($id2); +}); + +test('TBSL Sequential Order', function () { + $id1 = TBSL::generate(); + usleep(10); + $id2 = TBSL::generate(); + + expect(hexdec($id2))->toBeGreaterThan(hexdec($id1)); +}); + +test('TBSL Machine ID Differentiation', function () { + $id1 = TBSL::generate(1); + $id2 = TBSL::generate(2); + + $parsed1 = TBSL::parse($id1); + $parsed2 = TBSL::parse($id2); + + expect($parsed1['machineId'])->not->toBe($parsed2['machineId']); });