Skip to content

Commit

Permalink
Add support for Timestamp type
Browse files Browse the repository at this point in the history
Fixes #17.
  • Loading branch information
rybakit committed Feb 15, 2022
1 parent 1e9eda5 commit d3d3f61
Show file tree
Hide file tree
Showing 12 changed files with 661 additions and 382 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015-2021 Eugene Leonovich
Copyright (c) 2015-2022 Eugene Leonovich

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
740 changes: 367 additions & 373 deletions README.md

Large diffs are not rendered by default.

82 changes: 82 additions & 0 deletions src/Extension/TimestampExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

/**
* This file is part of the rybakit/msgpack.php package.
*
* (c) Eugene Leonovich <gen.work@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace MessagePack\Extension;

use MessagePack\BufferUnpacker;
use MessagePack\Extension;
use MessagePack\Packer;
use MessagePack\Type\Timestamp;

final class TimestampExtension implements Extension
{
private const TYPE = -1;

public function getType() : int
{
return self::TYPE;
}

public function pack(Packer $packer, $value) : ?string
{
if (!$value instanceof Timestamp) {
return null;
}

$sec = $value->getSeconds();
$nsec = $value->getNanoseconds();

if ($sec >> 34) {
return $packer->packExt(self::TYPE, \pack('NJ', $nsec, $sec));
}

return (0 === $nsec && $sec <= 0xffffffff)
? $packer->packExt(self::TYPE, \pack('N', $sec))
: $packer->packExt(self::TYPE, \pack('J', ($nsec << 34) | $sec));
}

/**
* @return Timestamp
*/
public function unpackExt(BufferUnpacker $unpacker, int $extLength)
{
if (4 === $extLength) {
$data = $unpacker->read(4);

$sec = \ord($data[0]) << 24
| \ord($data[1]) << 16
| \ord($data[2]) << 8
| \ord($data[3]);

return new Timestamp($sec);
}
if (8 === $extLength) {
$data = $unpacker->read(8);

$num = \unpack('J', $data)[1];
$nsec = $num >> 34;
if ($nsec < 0) {
$nsec += 0x40000000;
}

return new Timestamp($num & 0x3ffffffff, $nsec);
}

$data = $unpacker->read(12);

$nsec = \ord($data[0]) << 24
| \ord($data[1]) << 16
| \ord($data[2]) << 8
| \ord($data[3]);

return new Timestamp(\unpack('J', $data, 4)[1], $nsec);
}
}
15 changes: 13 additions & 2 deletions src/MessagePack.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,13 @@
use MessagePack\Exception\InvalidOptionException;
use MessagePack\Exception\PackingFailedException;
use MessagePack\Exception\UnpackingFailedException;
use MessagePack\Extension\TimestampExtension;

final class MessagePack
{
/** @var Extension[]|null */
private static $extensions;

/**
* @codeCoverageIgnore
*/
Expand All @@ -33,7 +37,7 @@ private function __construct()
*/
public static function pack($value, $options = null) : string
{
return (new Packer($options))->pack($value);
return (new Packer($options, self::getBuiltInExtensions()))->pack($value);
}

/**
Expand All @@ -46,6 +50,13 @@ public static function pack($value, $options = null) : string
*/
public static function unpack(string $data, $options = null)
{
return (new BufferUnpacker($data, $options))->unpack();
return (new BufferUnpacker($data, $options, self::getBuiltInExtensions()))->unpack();
}

private static function getBuiltInExtensions() : array
{
return self::$extensions ?? self::$extensions = [
new TimestampExtension(),
];
}
}
46 changes: 46 additions & 0 deletions src/Type/Timestamp.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<?php

/**
* This file is part of the rybakit/msgpack.php package.
*
* (c) Eugene Leonovich <gen.work@gmail.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace MessagePack\Type;

final class Timestamp
{
private $seconds;
private $nanoseconds;

public function __construct(int $seconds, int $nanoseconds = 0)
{
$this->seconds = $seconds;
$this->nanoseconds = $nanoseconds;
}

public static function now() : self
{
$date = new \DateTime();

return new self($date->getTimestamp(), (int) $date->format('u') * 1000);
}

public static function fromDateTime(\DateTimeInterface $date) : self
{
return new self($date->getTimestamp(), (int) $date->format('u') * 1000);
}

public function getSeconds() : int
{
return $this->seconds;
}

public function getNanoseconds() : int
{
return $this->nanoseconds;
}
}
85 changes: 83 additions & 2 deletions tests/DataProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,13 @@
namespace MessagePack\Tests;

use MessagePack\Type\Ext;
use MessagePack\Type\Timestamp;

class DataProvider
{
/**
* @return array<string, array{mixed, string}>
*/
public static function provideData() : array
{
return array_merge(
Expand All @@ -31,10 +35,14 @@ public static function provideData() : array
self::provideBinData(),
self::provideArrayData(),
self::provideMapData(),
self::provideExtData()
self::provideExtData(),
self::provideExtTimestampData()
);
}

/**
* @return array<string, array{mixed, string}>
*/
public static function provideUnpackData() : array
{
return array_merge(
Expand All @@ -46,17 +54,24 @@ public static function provideUnpackData() : array
self::provideBinData(),
self::provideArrayData(),
self::provideMapUnpackData(),
self::provideExtData()
self::provideExtData(),
self::provideExtTimestampData()
);
}

/**
* @return array<string, array{null, string}>
*/
public static function provideNilData() : array
{
return [
'nil' => [null, "\xc0"],
];
}

/**
* @return array<string, array{bool, string}>
*/
public static function provideBoolData() : array
{
return [
Expand All @@ -65,6 +80,9 @@ public static function provideBoolData() : array
];
}

/**
* @return array<string, array{int, string}>
*/
public static function provideIntData() : array
{
return [
Expand Down Expand Up @@ -112,6 +130,9 @@ public static function provideIntData() : array
];
}

/**
* @return array<string, array{int, string}>
*/
public static function provideIntUnpackData() : array
{
return array_merge(self::provideIntData(), [
Expand All @@ -129,6 +150,9 @@ public static function provideIntUnpackData() : array
]);
}

/**
* @return array<string, array{float, string}>
*/
public static function provideFloat32Data() : array
{
return [
Expand All @@ -137,6 +161,9 @@ public static function provideFloat32Data() : array
];
}

/**
* @return array<string, array{float, string}>
*/
public static function provideFloat64Data() : array
{
return [
Expand All @@ -146,6 +173,9 @@ public static function provideFloat64Data() : array
];
}

/**
* @return array<string, array{float, string}>
*/
public static function provideFloatUnpackData() : array
{
return array_merge(
Expand All @@ -154,6 +184,9 @@ public static function provideFloatUnpackData() : array
);
}

/**
* @return array<string, array{string, string}>
*/
public static function provideStrData() : array
{
return [
Expand All @@ -174,6 +207,9 @@ public static function provideStrData() : array
];
}

/**
* @return array<string, array{string, string}>
*/
public static function provideBinData() : array
{
return [
Expand All @@ -185,6 +221,9 @@ public static function provideBinData() : array
];
}

/**
* @return array<string, {array<int, mixed>, string}>
*/
public static function provideArrayData() : array
{
return [
Expand All @@ -198,6 +237,9 @@ public static function provideArrayData() : array
];
}

/**
* @return array<string, {array, string}>
*/
public static function provideMapData() : array
{
return [
Expand All @@ -212,6 +254,9 @@ public static function provideMapData() : array
];
}

/**
* @return array<string, {array, string}>
*/
public static function provideMapUnpackData() : array
{
return array_merge(self::provideMapData(), [
Expand All @@ -221,6 +266,9 @@ public static function provideMapUnpackData() : array
]);
}

/**
* @return array<string, array{Ext, string}>
*/
public static function provideExtData() : array
{
return [
Expand All @@ -235,6 +283,28 @@ public static function provideExtData() : array
];
}

/**
* @return array<string, array{Timestamp, string}>
*/
public static function provideExtTimestampData() : array
{
return [
'32-bit timestamp #1' => [new Timestamp(0), "\xd6\xff\x00\x00\x00\x00"],
'32-bit timestamp #2' => [new Timestamp(0xffffffff), "\xd6\xff\xff\xff\xff\xff"],

'64-bit timestamp #1' => [new Timestamp(0, 1), "\xd7\xff\x00\x00\x00\x04\x00\x00\x00\x00"],
'64-bit timestamp #2' => [new Timestamp(0xffffffff + 1), "\xd7\xff\x00\x00\x00\x01\x00\x00\x00\x00"],
'64-bit timestamp #3' => [new Timestamp(0x3ffffffff, 999999999), "\xd7\xff\xee\x6b\x27\xff\xff\xff\xff\xff"],

'96-bit timestamp #1' => [new Timestamp(PHP_INT_MIN), "\xc7\x0c\xff\x00\x00\x00\x00\x80\x00\x00\x00\x00\x00\x00\x00"],
'96-bit timestamp #2' => [new Timestamp(0x3ffffffff + 1, 0), "\xc7\x0c\xff\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00"],
'96-bit timestamp #3' => [new Timestamp(PHP_INT_MAX, 999999999), "\xc7\x0c\xff\x3b\x9a\xc9\xff\x7f\xff\xff\xff\xff\xff\xff\xff"],
];
}

/**
* @return list<string>
*/
public static function getSlowTestNames() : array
{
return [
Expand All @@ -245,6 +315,9 @@ public static function getSlowTestNames() : array
];
}

/**
* @return list<string>
*/
public static function getPeclIncompatibleTestNames() : array
{
return [
Expand All @@ -265,6 +338,14 @@ public static function getPeclIncompatibleTestNames() : array
'8-bit ext',
'16-bit ext',
'32-bit ext',
'32-bit timestamp #1',
'32-bit timestamp #2',
'64-bit timestamp #1',
'64-bit timestamp #2',
'64-bit timestamp #3',
'96-bit timestamp #1',
'96-bit timestamp #2',
'96-bit timestamp #3',
];
}
}
Loading

0 comments on commit d3d3f61

Please sign in to comment.