From ca0cc642aab2bef61e4a7761a49c6095f01fc644 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 27 Apr 2016 21:11:29 +0200 Subject: [PATCH 1/2] Add OrderedTimeCodec + test --- src/Codec/OrderedTimeCodec.php | 67 ++++++++++++++ tests/src/Codec/OrderedTimeCodecTest.php | 106 +++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 src/Codec/OrderedTimeCodec.php create mode 100644 tests/src/Codec/OrderedTimeCodecTest.php diff --git a/src/Codec/OrderedTimeCodec.php b/src/Codec/OrderedTimeCodec.php new file mode 100644 index 0000000..cbbb75e --- /dev/null +++ b/src/Codec/OrderedTimeCodec.php @@ -0,0 +1,67 @@ + + * @license http://opensource.org/licenses/MIT MIT + * @link https://benramsey.com/projects/ramsey-uuid/ Documentation + * @link https://packagist.org/packages/ramsey/uuid Packagist + * @link https://github.com/ramsey/uuid GitHub + */ +namespace Ramsey\Uuid\Codec; + +use InvalidArgumentException; +use Ramsey\Uuid\UuidInterface; + +/** + * OrderedTimeCodec optimizes the bytes to increment UUIDs when time goes by, to improve database INSERTs. + * The string value will be unchanged from StringCodec. Only works for UUID type 1. + */ +class OrderedTimeCodec extends StringCodec +{ + + /** + * Encodes a UuidInterface as an optimized binary representation of a UUID + * + * @param UuidInterface $uuid + * @return string Binary string representation of a UUID + */ + public function encodeBinary(UuidInterface $uuid) + { + $fields = $uuid->getFieldsHex(); + + $optimized = [ + $fields['time_hi_and_version'], + $fields['time_mid'], + $fields['time_low'], + $fields['clock_seq_hi_and_reserved'], + $fields['clock_seq_low'], + $fields['node'], + ]; + + return hex2bin(implode('', $optimized)); + } + + /** + * Decodes an optimized binary representation of a UUID into a UuidInterface object instance + * + * @param string $bytes + * @return UuidInterface + */ + public function decodeBytes($bytes) + { + if (strlen($bytes) !== 16) { + throw new InvalidArgumentException('$bytes string should contain 16 characters.'); + } + + $hex = unpack('H*', $bytes)[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); + + return $this->decode($hex); + } +} diff --git a/tests/src/Codec/OrderedTimeCodecTest.php b/tests/src/Codec/OrderedTimeCodecTest.php new file mode 100644 index 0000000..455b926 --- /dev/null +++ b/tests/src/Codec/OrderedTimeCodecTest.php @@ -0,0 +1,106 @@ +builder = $this->getMock('Ramsey\Uuid\Builder\UuidBuilderInterface'); + $this->uuid = $this->getMock('Ramsey\Uuid\UuidInterface'); + $this->fields = ['time_low' => '58e0a7d7', + 'time_mid' => 'eebc', + 'time_hi_and_version' => '11d8', + 'clock_seq_hi_and_reserved' => '96', + 'clock_seq_low' => '69', + 'node' => '0800200c9a66']; + } + + public function tearDown() + { + parent::tearDown(); + $this->builder = null; + $this->uuid = null; + $this->fields = null; + } + + public function testEncodeUsesFieldsArray() + { + $this->uuid->expects($this->once()) + ->method('getFieldsHex') + ->willReturn($this->fields); + $codec = new OrderedTimeCodec($this->builder); + $codec->encode($this->uuid); + } + + public function testEncodeReturnsFormattedString() + { + $this->uuid->method('getFieldsHex') + ->willReturn($this->fields); + $codec = new OrderedTimeCodec($this->builder); + $result = $codec->encode($this->uuid); + $this->assertEquals($this->uuidString, $result); + } + + public function testEncodeBinaryUsesFieldsHex() + { + $this->uuid->expects($this->once()) + ->method('getFieldsHex') + ->willReturn($this->fields); + $codec = new OrderedTimeCodec($this->builder); + $codec->encodeBinary($this->uuid); + } + + public function testEncodeBinaryReturnsBinaryString() + { + $expected = hex2bin($this->optimizedHex); + $this->uuid->method('getFieldsHex') + ->willReturn($this->fields); + $codec = new OrderedTimeCodec($this->builder); + $result = $codec->encodeBinary($this->uuid); + $this->assertEquals($expected, $result); + } + + public function testDecodeReturnsUuidFromBuilder() + { + $string = 'uuid:58e0a7d7-eebc-11d8-9669-0800200c9a66'; + $this->builder->method('build') + ->willReturn($this->uuid); + $codec = new OrderedTimeCodec($this->builder); + $result = $codec->decode($string); + $this->assertEquals($this->uuid, $result); + } + + public function testDecodeBytesRearrangesFields() + { + $bytes = pack('H*', $this->optimizedHex); + $codec = new OrderedTimeCodec($this->builder); + $this->builder->method('build')->with($this->anything(), $this->equalTo($this->fields)) + ->willReturn($this->uuid); + $result = $codec->decodeBytes($bytes); + $this->assertEquals($this->uuid, $result); + } +} From 9112b2bd958f52308723c00aa55f5f81a73ec830 Mon Sep 17 00:00:00 2001 From: "Barry vd. Heuvel" Date: Wed, 27 Apr 2016 22:32:15 +0200 Subject: [PATCH 2/2] Test OrderedTimeCodecTest exception --- tests/src/Codec/OrderedTimeCodecTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/src/Codec/OrderedTimeCodecTest.php b/tests/src/Codec/OrderedTimeCodecTest.php index 455b926..44a8b3c 100644 --- a/tests/src/Codec/OrderedTimeCodecTest.php +++ b/tests/src/Codec/OrderedTimeCodecTest.php @@ -84,6 +84,15 @@ class OrderedTimeCodecTest extends TestCase $this->assertEquals($expected, $result); } + public function testDecodeBytesThrowsExceptionWhenBytesStringNotSixteenCharacters() + { + $string = '61'; + $bytes = pack('H*', $string); + $codec = new OrderedTimeCodec($this->builder); + $this->setExpectedException('InvalidArgumentException', '$bytes string should contain 16 characters.'); + $codec->decodeBytes($bytes); + } + public function testDecodeReturnsUuidFromBuilder() { $string = 'uuid:58e0a7d7-eebc-11d8-9669-0800200c9a66';