Skip to content

Commit

Permalink
Merge pull request #43 from aztech-forks/comb
Browse files Browse the repository at this point in the history
Add generation of COMB UUID's
  • Loading branch information
ramsey committed Mar 10, 2015
2 parents db357ed + 8bece7c commit 12c9227
Show file tree
Hide file tree
Showing 4 changed files with 159 additions and 0 deletions.
31 changes: 31 additions & 0 deletions src/Console/Command/GenerateCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@
use Symfony\Component\Console\Output\OutputInterface;
use Rhumsaa\Uuid\Console\Exception;
use Rhumsaa\Uuid\Uuid;
use Rhumsaa\Uuid\Generator\CombGenerator;
use Rhumsaa\Uuid\Codec\GuidStringCodec;
use Rhumsaa\Uuid\FeatureSet;
use Rhumsaa\Uuid\UuidFactory;

/**
* Provides the console command to generate UUIDs
Expand Down Expand Up @@ -60,6 +64,18 @@ protected function configure()
InputOption::VALUE_REQUIRED,
'Generate count UUIDs instead of just a single one.',
1
)
->addOption(
'comb',
null,
InputOption::VALUE_NONE,
'For version 4 UUIDs, uses the COMB strategy to generate the random data.'
)
->addOption(
'guid',
'g',
InputOption::VALUE_NONE,
'Returns a GUID formatted UUID.'
);
}

Expand All @@ -82,6 +98,21 @@ protected function execute(InputInterface $input, OutputInterface $output)
)
);

if (((bool) $input->getOption('guid')) == true) {
$features = new FeatureSet(true);

Uuid::setFactory(new UuidFactory($features));
}

if (((bool) $input->getOption('comb')) === true) {
Uuid::getFactory()->setRandomGenerator(
new CombGenerator(
Uuid::getFactory()->getRandomGenerator(),
Uuid::getFactory()->getNumberConverter()
)
);
}

for ($i = 0; $i < $count; $i++) {
$uuids[] = $this->createUuid(
$input->getArgument('version'),
Expand Down
68 changes: 68 additions & 0 deletions src/Generator/CombGenerator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

namespace Rhumsaa\Uuid\Generator;

use Rhumsaa\Uuid\RandomGeneratorInterface;
use Rhumsaa\Uuid\Converter\NumberConverterInterface;

/**
* Generator to be used for COMB sequential UUID's.
*
* @author thibaud
*
*/
class CombGenerator implements RandomGeneratorInterface
{

private $randomGenerator;

private $converter;

private $timestampBytes;

/**
* @param RandomGeneratorInterface $generator RNG for the non-time part.
* @param NumberConverterInterface $numberConverter Instance of number converter.
*/
public function __construct(RandomGeneratorInterface $generator, NumberConverterInterface $numberConverter)
{
$this->converter = $numberConverter;
$this->randomGenerator = $generator;
$this->timestampBytes = 6;
}

/**
* (non-PHPdoc) @see \Rhumsaa\Uuid\RandomGeneratorInterface::generate()
*/
public function generate($length)
{
if ($length < $this->timestampBytes || $length < 0) {
throw new \InvalidArgumentException('Length must be a positive integer.');
}

$hash = '';

if ($this->timestampBytes > 0 && $length > $this->timestampBytes) {
$hash = $this->randomGenerator->generate($length - $this->timestampBytes);
}

$lsbTime = str_pad($this->converter->toHex($this->timestamp()), $this->timestampBytes * 2, '0', STR_PAD_LEFT);

if ($this->timestampBytes > 0 && strlen($lsbTime) > $this->timestampBytes * 2) {
$lsbTime = substr($lsbTime, 0 - ($this->timestampBytes * 2));
}

return hex2bin(str_pad(bin2hex($hash), $length - $this->timestampBytes, '0')) . hex2bin($lsbTime);
}

/**
* Returns current timestamp as integer, precise to 0.00001 seconds
* @return number
*/
private function timestamp()
{
$time = explode(' ', microtime(false));

return $time[1] . substr($time[0], 2, 5);
}
}
15 changes: 15 additions & 0 deletions src/UuidFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ public function getCodec()
return $this->codec;
}

public function getRandomGenerator()
{
return $this->randomGenerator;
}

public function getNumberConverter()
{
return $this->numberConverter;
}

public function getTimeConverter()
{
return $this->timeConverter;
}

public function setTimeConverter(TimeConverterInterface $converter)
{
$this->timeConverter = $converter;
Expand Down
45 changes: 45 additions & 0 deletions tests/UuidTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Rhumsaa\Uuid\Provider\Time\SystemTimeProvider;
use Rhumsaa\Uuid\Provider\Time\FixedTimeProvider;
use Rhumsaa\Uuid\Generator\CombGenerator;

class UuidTest extends TestCase
{
Expand Down Expand Up @@ -785,6 +786,50 @@ public function testUuid4WithoutOpensslRandomPseudoBytes()
$this->assertEquals(4, $uuid->getVersion());
}

/**
* Tests that generated UUID's using COMB are sequential
* @return string
*/
public function testUuid4Comb()
{
$mock = $this->getMock('Rhumsaa\Uuid\RandomGeneratorInterface');
$mock->expects($this->any())
->method('generate')
->willReturnCallback(function ($length)
{
// Makes first fields of UUIDs equal
return str_pad('', $length, '0');
});

$factory = new UuidFactory();
$generator = new CombGenerator($mock, $factory->getNumberConverter());
$factory->setRandomGenerator($generator);

$previous = $factory->uuid4();

for ($i = 0; $i < 1000; $i ++) {
$uuid = $factory->uuid4();
$this->assertGreaterThan($previous->toString(), $uuid->toString());

$previous = $uuid;
}
}

/**
* Test that COMB UUID's have a version 4 flag
*/
public function testUuid4CombVersion()
{
$factory = new UuidFactory();
$generator = new CombGenerator(RandomGeneratorFactory::getGenerator(), $factory->getNumberConverter());

$factory->setRandomGenerator($generator);

$uuid = $factory->uuid4();

$this->assertEquals(4, $uuid->getVersion());
}

/**
* The "python.org" UUID is a known entity, so we're testing that this
* library generates a matching UUID for the same name.
Expand Down

0 comments on commit 12c9227

Please sign in to comment.