Skip to content

Commit

Permalink
minor #36077 [Uid] Add support for UUIDv6 (nicolas-grekas)
Browse files Browse the repository at this point in the history
This PR was merged into the 5.1-dev branch.

Discussion
----------

[Uid] Add support for UUIDv6

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

See https://github.com/uuid6/uuid6-ietf-draft/blob/master/draft-peabody-dispatch-new-uuid-format-00.txt

A v6 UUID is a lexicographically-sortable-v1.
This makes it db-index friendly (same as ULIDs).

For reference:
- v1 has no benefits over v6 except being in the current official RFC
- v6 is order-friendly and leaks time data + stable entropy (potentially bound to a MAC address or equivalent)
- ULID is also order-friendly and leaks time data, with high entropy (crypto random source)
- v4 is pure crypto random source, aka no order and no leak of anything.

Commits
-------

b705ee1 [Uid] Add support for UUIDv6
  • Loading branch information
fabpot committed Mar 15, 2020
2 parents 00aab67 + b705ee1 commit 68dec1a
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 1 deletion.
13 changes: 13 additions & 0 deletions src/Symfony/Component/Uid/Tests/UuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
use Symfony\Component\Uid\UuidV3;
use Symfony\Component\Uid\UuidV4;
use Symfony\Component\Uid\UuidV5;
use Symfony\Component\Uid\UuidV6;

class UuidTest extends TestCase
{
Expand Down Expand Up @@ -73,6 +74,18 @@ public function testV5()
$this->assertInstanceOf(UuidV5::class, $uuid);
}

public function testV6()
{
$uuid = Uuid::v6();

$this->assertInstanceOf(UuidV6::class, $uuid);

$uuid = new UuidV6(substr_replace(self::A_UUID_V1, '6', 14, 1));

$this->assertSame(85916308548.27832, $uuid->getTime());
$this->assertSame('3499710062d0', $uuid->getNode());
}

public function testBinary()
{
$uuid = new UuidV4(self::A_UUID_V4);
Expand Down
2 changes: 2 additions & 0 deletions src/Symfony/Component/Uid/Ulid.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
namespace Symfony\Component\Uid;

/**
* A ULID is lexicographically sortable and contains a 48-bit timestamp and 80-bit of crypto-random entropy.
*
* @see https://github.com/ulid/spec
*
* @experimental in 5.1
Expand Down
6 changes: 6 additions & 0 deletions src/Symfony/Component/Uid/Uuid.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public static function fromString(string $uuid): self
case UuidV3::TYPE: return new UuidV3($uuid);
case UuidV4::TYPE: return new UuidV4($uuid);
case UuidV5::TYPE: return new UuidV5($uuid);
case UuidV6::TYPE: return new UuidV6($uuid);
case NullUuid::TYPE: return new NullUuid();
case self::TYPE: return new self($uuid);
}
Expand Down Expand Up @@ -76,6 +77,11 @@ final public static function v5(self $namespace, string $name): UuidV5
return new UuidV5(uuid_generate_sha1($namespace->uuid, $name));
}

final public static function v6(): UuidV6
{
return new UuidV6();
}

public static function isValid(string $uuid): bool
{
if (__CLASS__ === static::class) {
Expand Down
2 changes: 1 addition & 1 deletion src/Symfony/Component/Uid/UuidV1.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
namespace Symfony\Component\Uid;

/**
* A v1 UUID contains a 60-bit timestamp and ~60 extra unique bits.
* A v1 UUID contains a 60-bit timestamp and 63 extra unique bits.
*
* @experimental in 5.1
*
Expand Down
60 changes: 60 additions & 0 deletions src/Symfony/Component/Uid/UuidV6.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<?php

/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Symfony\Component\Uid;

/**
* A v6 UUID is lexicographically sortable and contains a 60-bit timestamp and 63 extra unique bits.
*
* @experimental in 5.1
*
* @author Nicolas Grekas <p@tchwork.com>
*/
class UuidV6 extends Uuid
{
protected const TYPE = 6;

// https://tools.ietf.org/html/rfc4122#section-4.1.4
// 0x01b21dd213814000 is the number of 100-ns intervals between the
// UUID epoch 1582-10-15 00:00:00 and the Unix epoch 1970-01-01 00:00:00.
private const TIME_OFFSET_INT = 0x01b21dd213814000;
private const TIME_OFFSET_COM = "\xfe\x4d\xe2\x2d\xec\x7e\xc0\x00";

public function __construct(string $uuid = null)
{
if (null === $uuid) {
$uuid = uuid_create(UUID_TYPE_TIME);
$this->uuid = substr($uuid, 15, 3).substr($uuid, 9, 4).$uuid[0].'-'.substr($uuid, 1, 4).'-6'.substr($uuid, 5, 3).substr($uuid, 18);
} else {
parent::__construct($uuid);
}
}

public function getTime(): float
{
$time = '0'.substr($this->uuid, 0, 8).substr($this->uuid, 9, 4).substr($this->uuid, 15, 3);

if (\PHP_INT_SIZE >= 8) {
return (hexdec($time) - self::TIME_OFFSET_INT) / 10000000;
}

$time = str_pad(hex2bin($time), 8, "\0", STR_PAD_LEFT);
$time = BinaryUtil::add($time, self::TIME_OFFSET_COM);
$time[0] = $time[0] & "\x7F";

return BinaryUtil::toBase($time, BinaryUtil::BASE10) / 10000000;
}

public function getNode(): string
{
return substr($this->uuid, 24);
}
}

0 comments on commit 68dec1a

Please sign in to comment.