From 0b64d6d995c73dd391bcf5a3692f5d132ccbef7c Mon Sep 17 00:00:00 2001 From: Ben Ramsey Date: Mon, 13 Jan 2020 16:33:28 -0600 Subject: [PATCH] Optimize how OrderedTimeCodec rearranges bytes and validates instances --- src/Codec/OrderedTimeCodec.php | 33 +++++++++++----------------- tests/Codec/OrderedTimeCodecTest.php | 24 +++++++++++--------- 2 files changed, 27 insertions(+), 30 deletions(-) diff --git a/src/Codec/OrderedTimeCodec.php b/src/Codec/OrderedTimeCodec.php index aff910d..f30cf58 100644 --- a/src/Codec/OrderedTimeCodec.php +++ b/src/Codec/OrderedTimeCodec.php @@ -16,7 +16,7 @@ namespace Ramsey\Uuid\Codec; use Ramsey\Uuid\Exception\InvalidArgumentException; use Ramsey\Uuid\Exception\UnsupportedOperationException; -use Ramsey\Uuid\Rfc4122\FieldsInterface; +use Ramsey\Uuid\Rfc4122\FieldsInterface as Rfc4122FieldsInterface; use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidInterface; @@ -50,16 +50,15 @@ class OrderedTimeCodec extends StringCodec public function encodeBinary(UuidInterface $uuid): string { if ( - $uuid->getVariant() !== Uuid::RFC_4122 - || $uuid->getVersion() !== Uuid::UUID_TYPE_TIME - || !($uuid->getFields() instanceof FieldsInterface) + !($uuid->getFields() instanceof Rfc4122FieldsInterface) + || $uuid->getFields()->getVersion() !== Uuid::UUID_TYPE_TIME ) { throw new InvalidArgumentException( 'Expected RFC 4122 version 1 (time-based) UUID' ); } - /** @var FieldsInterface $fields */ + /** @var Rfc4122FieldsInterface $fields */ $fields = $uuid->getFields(); $optimized = [ @@ -92,24 +91,18 @@ class OrderedTimeCodec extends StringCodec ); } - $unpacked = unpack('H*', $bytes); + // Rearrange the bytes to their original order. + $rearrangedBytes = substr($bytes, 4, 2) + . substr($bytes, 6, 2) + . substr($bytes, 2, 2) + . substr($bytes, 0, 2) + . substr($bytes, 8); - assert(is_string($unpacked[1])); - - $hex = $unpacked[1]; - - // Rearrange the fields to their original order - $hex = substr($hex, 8, 4) - . substr($hex, 12, 4) - . substr($hex, 4, 4) - . substr($hex, 0, 4) - . substr($hex, 16); - - $uuid = $this->decode($hex); + $uuid = parent::decodeBytes($rearrangedBytes); if ( - $uuid->getVariant() !== Uuid::RFC_4122 - || $uuid->getVersion() !== Uuid::UUID_TYPE_TIME + !($uuid->getFields() instanceof Rfc4122FieldsInterface) + || $uuid->getFields()->getVersion() !== Uuid::UUID_TYPE_TIME ) { throw new UnsupportedOperationException( 'Attempting to decode a non-time-based UUID using ' diff --git a/tests/Codec/OrderedTimeCodecTest.php b/tests/Codec/OrderedTimeCodecTest.php index b492506..614e7bb 100644 --- a/tests/Codec/OrderedTimeCodecTest.php +++ b/tests/Codec/OrderedTimeCodecTest.php @@ -9,13 +9,17 @@ use PHPUnit\Framework\MockObject\MockObject; use Ramsey\Uuid\Builder\DefaultUuidBuilder; use Ramsey\Uuid\Builder\UuidBuilderInterface; use Ramsey\Uuid\Codec\OrderedTimeCodec; +use Ramsey\Uuid\Converter\Number\GenericNumberConverter; use Ramsey\Uuid\Converter\NumberConverterInterface; +use Ramsey\Uuid\Converter\Time\GenericTimeConverter; use Ramsey\Uuid\Converter\TimeConverterInterface; use Ramsey\Uuid\Exception\InvalidArgumentException; use Ramsey\Uuid\Exception\UnsupportedOperationException; +use Ramsey\Uuid\Math\BrickMathCalculator; +use Ramsey\Uuid\Nonstandard\Fields as NonstandardFields; +use Ramsey\Uuid\Nonstandard\UuidBuilder; use Ramsey\Uuid\Rfc4122\Fields; use Ramsey\Uuid\Test\TestCase; -use Ramsey\Uuid\Uuid; use Ramsey\Uuid\UuidFactory; use Ramsey\Uuid\UuidInterface; @@ -120,7 +124,8 @@ class OrderedTimeCodecTest extends TestCase { $bytes = (string) hex2bin($this->optimizedHex); - $numberConverter = Mockery::mock(NumberConverterInterface::class); + $calculator = new BrickMathCalculator(); + $numberConverter = new GenericNumberConverter($calculator); $timeConverter = Mockery::mock(TimeConverterInterface::class); $builder = new DefaultUuidBuilder($numberConverter, $timeConverter); $codec = new OrderedTimeCodec($builder); @@ -138,6 +143,7 @@ class OrderedTimeCodecTest extends TestCase { $nonRfc4122Uuid = '58e0a7d7-eebc-11d8-d669-0800200c9a66'; + $fields = new NonstandardFields((string) hex2bin(str_replace('-', '', $nonRfc4122Uuid))); $numberConverter = Mockery::mock(NumberConverterInterface::class); $timeConverter = Mockery::mock(TimeConverterInterface::class); $builder = new DefaultUuidBuilder($numberConverter, $timeConverter); @@ -146,6 +152,7 @@ class OrderedTimeCodecTest extends TestCase $uuid = Mockery::mock(UuidInterface::class, [ 'getVariant' => 0, 'toString' => $nonRfc4122Uuid, + 'getFields' => $fields, ]); $this->expectException(InvalidArgumentException::class); @@ -183,15 +190,12 @@ class OrderedTimeCodecTest extends TestCase $nonRfc4122OptimizedHex = '11d8eebc58e0a7d716690800200c9a66'; $bytes = (string) hex2bin($nonRfc4122OptimizedHex); - $uuid = Mockery::mock(UuidInterface::class, [ - 'getVariant' => Uuid::RESERVED_NCS, - ]); + $calculator = new BrickMathCalculator(); + $numberConverter = new GenericNumberConverter($calculator); + $timeConverter = new GenericTimeConverter($calculator); + $builder = new UuidBuilder($numberConverter, $timeConverter); - $codec = Mockery::mock(OrderedTimeCodec::class, [ - 'decode' => $uuid, - ]); - - $codec->shouldReceive('decodeBytes')->passthru(); + $codec = new OrderedTimeCodec($builder); $this->expectException(UnsupportedOperationException::class); $this->expectExceptionMessage(