Update website

This commit is contained in:
Guilhem Lavaux 2024-11-23 20:45:29 +01:00
parent 41ce1aa076
commit ea0eb1c6e0
4222 changed files with 721797 additions and 14 deletions

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use function chr;
abstract class AbstractCBORObject implements CBORObject
{
/**
* @var int
*/
protected $additionalInformation;
/**
* @var int
*/
private $majorType;
public function __construct(int $majorType, int $additionalInformation)
{
$this->majorType = $majorType;
$this->additionalInformation = $additionalInformation;
}
public function __toString(): string
{
return chr($this->majorType << 5 | $this->additionalInformation);
}
public function getMajorType(): int
{
return $this->majorType;
}
public function getAdditionalInformation(): int
{
return $this->additionalInformation;
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
final class ByteStringObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b010;
/**
* @var string
*/
private $value;
/**
* @var int|null
*/
private $length;
public function __construct(string $data)
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfString($data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->length = $length;
$this->value = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
$result .= $this->value;
return $result;
}
public function getValue(): string
{
return $this->value;
}
public function getLength(): int
{
return mb_strlen($this->value, '8bit');
}
public function getNormalizedData(bool $ignoreTags = false): string
{
return $this->value;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
final class ByteStringWithChunkObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b010;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var ByteStringObject[]
*/
private $chunks = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->chunks as $chunk) {
$result .= (string) $chunk;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function add(ByteStringObject $chunk): void
{
$this->chunks[] = $chunk;
}
public function append(string $chunk): void
{
$this->add(new ByteStringObject($chunk));
}
public function getValue(): string
{
$result = '';
foreach ($this->chunks as $chunk) {
$result .= $chunk->getValue();
}
return $result;
}
public function getLength(): int
{
$length = 0;
foreach ($this->chunks as $chunk) {
$length += $chunk->getLength();
}
return $length;
}
public function getNormalizedData(bool $ignoreTags = false): string
{
$result = '';
foreach ($this->chunks as $chunk) {
$result .= $chunk->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,28 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
interface CBORObject
{
public function __toString(): string;
public function getMajorType(): int;
public function getAdditionalInformation(): int;
/**
* @return mixed|null
*/
public function getNormalizedData(bool $ignoreTags = false);
}

View file

@ -0,0 +1,160 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use CBOR\OtherObject\BreakObject;
use CBOR\OtherObject\OtherObjectManager;
use CBOR\Tag\TagObjectManager;
use InvalidArgumentException;
use function ord;
use RuntimeException;
final class Decoder
{
/**
* @var TagObjectManager
*/
private $tagObjectManager;
/**
* @var OtherObjectManager
*/
private $otherTypeManager;
public function __construct(TagObjectManager $tagObjectManager, OtherObjectManager $otherTypeManager)
{
$this->tagObjectManager = $tagObjectManager;
$this->otherTypeManager = $otherTypeManager;
}
public function decode(Stream $stream): CBORObject
{
return $this->process($stream);
}
private function process(Stream $stream, bool $breakable = false): CBORObject
{
$ib = ord($stream->read(1));
$mt = $ib >> 5;
$ai = $ib & 0b00011111;
$val = null;
switch ($ai) {
case 0b00011000: //24
case 0b00011001: //25
case 0b00011010: //26
case 0b00011011: //27
$val = $stream->read(2 ** ($ai & 0b00000111));
break;
case 0b00011100: //28
case 0b00011101: //29
case 0b00011110: //30
throw new InvalidArgumentException(sprintf('Cannot parse the data. Found invalid Additional Information "%s" (%d).', str_pad(decbin($ai), 5, '0', STR_PAD_LEFT), $ai));
case 0b00011111: //31
return $this->processInfinite($stream, $mt, $breakable);
}
return $this->processFinite($stream, $mt, $ai, $val);
}
private function processFinite(Stream $stream, int $mt, int $ai, ?string $val): CBORObject
{
switch ($mt) {
case 0b000: //0
return UnsignedIntegerObject::createObjectForValue($ai, $val);
case 0b001: //1
return SignedIntegerObject::createObjectForValue($ai, $val);
case 0b010: //2
$length = null === $val ? $ai : Utils::binToInt($val);
return new ByteStringObject($stream->read($length));
case 0b011: //3
$length = null === $val ? $ai : Utils::binToInt($val);
return new TextStringObject($stream->read($length));
case 0b100: //4
$object = new ListObject();
$nbItems = null === $val ? $ai : Utils::binToInt($val);
for ($i = 0; $i < $nbItems; ++$i) {
$object->add($this->process($stream));
}
return $object;
case 0b101: //5
$object = new MapObject();
$nbItems = null === $val ? $ai : Utils::binToInt($val);
for ($i = 0; $i < $nbItems; ++$i) {
$object->add($this->process($stream), $this->process($stream));
}
return $object;
case 0b110: //6
return $this->tagObjectManager->createObjectForValue($ai, $val, $this->process($stream));
case 0b111: //7
return $this->otherTypeManager->createObjectForValue($ai, $val);
default:
throw new RuntimeException(sprintf('Unsupported major type "%s" (%d).', str_pad(decbin($mt), 5, '0', STR_PAD_LEFT), $mt)); // Should never append
}
}
private function processInfinite(Stream $stream, int $mt, bool $breakable): CBORObject
{
switch ($mt) {
case 0b010: //2
$object = new ByteStringWithChunkObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
if (!$it instanceof ByteStringObject) {
throw new RuntimeException('Unable to parse the data. Infinite Byte String object can only get Byte String objects.');
}
$object->add($it);
}
return $object;
case 0b011: //3
$object = new TextStringWithChunkObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
if (!$it instanceof TextStringObject) {
throw new RuntimeException('Unable to parse the data. Infinite Text String object can only get Text String objects.');
}
$object->add($it);
}
return $object;
case 0b100: //4
$object = new InfiniteListObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
$object->add($it);
}
return $object;
case 0b101: //5
$object = new InfiniteMapObject();
while (!($it = $this->process($stream, true)) instanceof BreakObject) {
$object->append($it, $this->process($stream));
}
return $object;
case 0b111: //7
if (!$breakable) {
throw new InvalidArgumentException('Cannot parse the data. No enclosing indefinite.');
}
return new BreakObject();
case 0b000: //0
case 0b001: //1
case 0b110: //6
default:
throw new InvalidArgumentException(sprintf('Cannot parse the data. Found infinite length for Major Type "%s" (%d).', str_pad(decbin($mt), 5, '0', STR_PAD_LEFT), $mt));
}
}
}

View file

@ -0,0 +1,74 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class InfiniteListObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b100;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var CBORObject[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function getNormalizedData(bool $ignoreTags = false): array
{
return array_map(function (CBORObject $item) use ($ignoreTags) {
return $item->getNormalizedData($ignoreTags);
}, $this->data);
}
public function add(CBORObject $item): void
{
$this->data[] = $item;
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
}

View file

@ -0,0 +1,78 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class InfiniteMapObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b101;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var MapItem[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object->getKey();
$result .= (string) $object->getValue();
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function append(CBORObject $key, CBORObject $value): void
{
$this->data[] = new MapItem($key, $value);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
public function getNormalizedData(bool $ignoreTags = false): array
{
$result = [];
foreach ($this->data as $object) {
$result[$object->getKey()->getNormalizedData($ignoreTags)] = $object->getValue()->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,69 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use function chr;
use function count;
use InvalidArgumentException;
final class LengthCalculator
{
public static function getLengthOfString(string $data): array
{
$length = mb_strlen($data, '8bit');
return self::computeLength($length);
}
public static function getLengthOfArray(array $data): array
{
$length = count($data);
return self::computeLength($length);
}
private static function computeLength(int $length): array
{
switch (true) {
case $length < 24:
return [$length, null];
case $length < 0xFF:
return [24, chr($length)];
case $length < 0xFFFF:
return [25, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
case $length < 0xFFFFFFFF:
return [26, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
case BigInteger::of($length)->isLessThan(BigInteger::fromBase('FFFFFFFFFFFFFFFF', 16)):
return [27, self::hex2bin(static::fixHexLength(Utils::intToHex($length)))];
default:
return [31, null];
}
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
private static function fixHexLength(string $data): string
{
return str_pad($data, (int) (2 ** ceil(log(mb_strlen($data, '8bit'), 2))), '0', STR_PAD_LEFT);
}
}

View file

@ -0,0 +1,99 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use function array_key_exists;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
class ListObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b100;
/**
* @var CBORObject[]
*/
private $data = [];
/**
* @var int|null
*/
private $length;
/**
* @param CBORObject[] $data
*/
public function __construct(array $data = [])
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfArray($data);
array_map(static function ($item): void {
if (!$item instanceof CBORObject) {
throw new InvalidArgumentException('The list must contain only CBORObject objects.');
}
}, $data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
foreach ($this->data as $object) {
$result .= (string) $object;
}
return $result;
}
public function add(CBORObject $object): void
{
$this->data[] = $object;
list($this->additionalInformation, $this->length) = LengthCalculator::getLengthOfArray($this->data);
}
public function get(int $index): CBORObject
{
if (!array_key_exists($index, $this->data)) {
throw new InvalidArgumentException('Index not found.');
}
return $this->data[$index];
}
public function getNormalizedData(bool $ignoreTags = false): array
{
return array_map(function (CBORObject $item) use ($ignoreTags) {
return $item->getNormalizedData($ignoreTags);
}, $this->data);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
}

View file

@ -0,0 +1,43 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
class MapItem
{
/**
* @var CBORObject
*/
private $key;
/**
* @var CBORObject
*/
private $value;
public function __construct(CBORObject $key, CBORObject $value)
{
$this->key = $key;
$this->value = $value;
}
public function getKey(): CBORObject
{
return $this->key;
}
public function getValue(): CBORObject
{
return $this->value;
}
}

View file

@ -0,0 +1,93 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use ArrayIterator;
use function count;
use Countable;
use InvalidArgumentException;
use Iterator;
use IteratorAggregate;
final class MapObject extends AbstractCBORObject implements Countable, IteratorAggregate
{
private const MAJOR_TYPE = 0b101;
/**
* @var MapItem[]
*/
private $data = [];
/**
* @var int|null
*/
private $length;
/**
* @param MapItem[] $data
*/
public function __construct(array $data = [])
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfArray($data);
array_map(static function ($item): void {
if (!$item instanceof MapItem) {
throw new InvalidArgumentException('The list must contain only MapItem objects.');
}
}, $data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
foreach ($this->data as $object) {
$result .= (string) $object->getKey();
$result .= (string) $object->getValue();
}
return $result;
}
public function add(CBORObject $key, CBORObject $value): void
{
$this->data[] = new MapItem($key, $value);
list($this->additionalInformation, $this->length) = LengthCalculator::getLengthOfArray($this->data);
}
public function count(): int
{
return count($this->data);
}
public function getIterator(): Iterator
{
return new ArrayIterator($this->data);
}
public function getNormalizedData(bool $ignoreTags = false): array
{
$result = [];
foreach ($this->data as $object) {
$result[$object->getKey()->getNormalizedData($ignoreTags)] = $object->getValue()->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
abstract class OtherObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b111;
/**
* @var string|null
*/
protected $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
/**
* @return int[]
*/
abstract public static function supportedAdditionalInformation(): array;
abstract public static function createFromLoadedData(int $additionalInformation, ?string $data): self;
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class BreakObject extends Base
{
public function __construct()
{
parent::__construct(0b00011111, null);
}
public static function supportedAdditionalInformation(): array
{
return [0b00011111];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return false;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class DoublePrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [27];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return DoublePrecisionFloatObject
*/
public static function create(string $value): self
{
if (8 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid double precision floating point');
}
return new self(27, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-(1022 + 52));
} elseif (0b11111111111 !== $exp) {
$val = ($mant + (1 << 52)) * 2 ** ($exp - (1023 + 52));
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(52)->and(Utils::hexToBigInteger('7ff'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('fffffffffffff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(63);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class FalseObject extends Base
{
public function __construct()
{
parent::__construct(20, null);
}
public static function supportedAdditionalInformation(): array
{
return [20];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return false;
}
}

View file

@ -0,0 +1,34 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class GenericObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
public function getNormalizedData(bool $ignoreTags = false)
{
return $this->data;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class HalfPrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [25];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return HalfPrecisionFloatObject
*/
public static function create(string $value): self
{
if (4 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid half precision floating point');
}
return new self(25, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-24);
} elseif (0b11111 !== $exp) {
$val = ($mant + (1 << 10)) * 2 ** ($exp - 25);
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(10)->and(Utils::hexToBigInteger('1f'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('3ff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(15);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,38 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class NullObject extends Base
{
public function __construct()
{
parent::__construct(22, null);
}
public static function supportedAdditionalInformation(): array
{
return [22];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false)
{
}
}

View file

@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use function array_key_exists;
use CBOR\OtherObject;
use InvalidArgumentException;
class OtherObjectManager
{
/**
* @var string[]
*/
private $classes = [];
public function add(string $class): void
{
foreach ($class::supportedAdditionalInformation() as $ai) {
if ($ai < 0) {
throw new InvalidArgumentException('Invalid additional information.');
}
$this->classes[$ai] = $class;
}
}
public function getClassForValue(int $value): string
{
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericObject::class;
}
public function createObjectForValue(int $value, ?string $data): OtherObject
{
/** @var OtherObject $class */
$class = $this->getClassForValue($value);
return $class::createFromLoadedData($value, $data);
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use function chr;
use InvalidArgumentException;
final class SimpleObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 24];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if (null === $this->data) {
return $this->getAdditionalInformation();
}
return Utils::binToInt($this->data);
}
/**
* @return SimpleObject
*/
public static function create(int $value): self
{
switch (true) {
case $value < 24:
return new self($value, null);
case $value < 256:
return new self(24, chr($value));
default:
throw new InvalidArgumentException('The value is not a valid simple value');
}
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use Assert\Assertion;
use Brick\Math\BigInteger;
use CBOR\OtherObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class SinglePrecisionFloatObject extends Base
{
public static function supportedAdditionalInformation(): array
{
return [26];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self($additionalInformation, $data);
}
/**
* @return SinglePrecisionFloatObject
*/
public static function create(string $value): self
{
if (4 !== mb_strlen($value, '8bit')) {
throw new InvalidArgumentException('The value is not a valid single precision floating point');
}
return new self(26, $value);
}
public function getNormalizedData(bool $ignoreTags = false)
{
$exp = $this->getExponent();
$mant = $this->getMantissa();
$sign = $this->getSign();
if (0 === $exp) {
$val = $mant * 2 ** (-(126 + 23));
} elseif (0b11111111 !== $exp) {
$val = ($mant + (1 << 23)) * 2 ** ($exp - (127 + 23));
} else {
$val = 0 === $mant ? INF : NAN;
}
return $sign * $val;
}
public function getExponent(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->shiftedRight(23)->and(Utils::hexToBigInteger('ff'))->toInt();
}
public function getMantissa(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
return Utils::binToBigInteger($data)->and(Utils::hexToBigInteger('7fffff'))->toInt();
}
public function getSign(): int
{
$data = $this->data;
Assertion::string($data, 'Invalid data');
$sign = Utils::binToBigInteger($data)->shiftedRight(32);
return $sign->isEqualTo(BigInteger::one()) ? -1 : 1;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class TrueObject extends Base
{
public function __construct()
{
parent::__construct(21, null);
}
public static function supportedAdditionalInformation(): array
{
return [21];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false): bool
{
return true;
}
}

View file

@ -0,0 +1,39 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\OtherObject;
use CBOR\OtherObject as Base;
final class UndefinedObject extends Base
{
public function __construct()
{
parent::__construct(23, null);
}
public static function supportedAdditionalInformation(): array
{
return [23];
}
public static function createFromLoadedData(int $additionalInformation, ?string $data): Base
{
return new self();
}
public function getNormalizedData(bool $ignoreTags = false)
{
return 'undefined';
}
}

View file

@ -0,0 +1,157 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use GMP;
use InvalidArgumentException;
final class SignedIntegerObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b001;
/**
* @var string|null
*/
private $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
public static function createObjectForValue(int $additionalInformation, ?string $data): self
{
return new self($additionalInformation, $data);
}
public static function create(int $value): self
{
return self::createFromString((string) $value);
}
public static function createFromString(string $value): self
{
$integer = BigInteger::of($value);
return self::createBigInteger($integer);
}
/**
* @deprecated Deprecated since v1.1 and will be removed in v2.0. Please use "create" or "createFromString" instead
*/
public static function createFromGmpValue(GMP $value): self
{
if (gmp_cmp($value, gmp_init(0)) >= 0) {
throw new InvalidArgumentException('The value must be a negative integer.');
}
$minusOne = gmp_init(-1);
$computed_value = gmp_sub($minusOne, $value);
switch (true) {
case gmp_intval($computed_value) < 24:
$ai = gmp_intval($computed_value);
$data = null;
break;
case gmp_cmp($computed_value, gmp_init('FF', 16)) < 0:
$ai = 24;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 2, '0', STR_PAD_LEFT));
break;
case gmp_cmp($computed_value, gmp_init('FFFF', 16)) < 0:
$ai = 25;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 4, '0', STR_PAD_LEFT));
break;
case gmp_cmp($computed_value, gmp_init('FFFFFFFF', 16)) < 0:
$ai = 26;
$data = self::hex2bin(str_pad(gmp_strval($computed_value, 16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use NegativeBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
public function getValue(): string
{
return $this->getNormalizedData();
}
public function getNormalizedData(bool $ignoreTags = false): string
{
if (null === $this->data) {
return (string) (-1 - $this->additionalInformation);
}
$result = Utils::binToBigInteger($this->data);
$minusOne = BigInteger::of(-1);
return $minusOne->minus($result)->toBase(10);
}
private static function createBigInteger(BigInteger $integer): self
{
if ($integer->isGreaterThanOrEqualTo(BigInteger::zero())) {
throw new InvalidArgumentException('The value must be a negative integer.');
}
$minusOne = BigInteger::of(-1);
$computed_value = $minusOne->minus($integer);
switch (true) {
case $computed_value->isLessThan(BigInteger::of(24)):
$ai = $computed_value->toInt();
$data = null;
break;
case $computed_value->isLessThan(BigInteger::fromBase('FF', 16)):
$ai = 24;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 2, '0', STR_PAD_LEFT));
break;
case $computed_value->isLessThan(BigInteger::fromBase('FFFF', 16)):
$ai = 25;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 4, '0', STR_PAD_LEFT));
break;
case $computed_value->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
$ai = 26;
$data = self::hex2bin(str_pad($computed_value->toBase(16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use NegativeBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
}

View file

@ -0,0 +1,19 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
interface Stream
{
public function read(int $length): string;
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
use RuntimeException;
final class StringStream implements Stream
{
/**
* @var resource
*/
private $resource;
public function __construct(string $data)
{
$resource = fopen('php://memory', 'rb+');
if (false === $resource) {
throw new RuntimeException('Unable to open the memory');
}
$result = fwrite($resource, $data);
if (false === $result) {
throw new RuntimeException('Unable to write the memory');
}
$result = rewind($resource);
if (false === $result) {
throw new RuntimeException('Unable to rewind the memory');
}
$this->resource = $resource;
}
public function read(int $length): string
{
if (0 === $length) {
return '';
}
$data = fread($this->resource, $length);
if (false === $data) {
throw new RuntimeException('Unable to read the memory');
}
if (mb_strlen($data, '8bit') !== $length) {
throw new InvalidArgumentException(sprintf('Out of range. Expected: %d, read: %d.', $length, mb_strlen($data, '8bit')));
}
return $data;
}
}

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base16EncodingTag extends Base
{
public static function getTagId(): int
{
return 23;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(23, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return bin2hex($this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,62 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base64EncodingTag extends Base
{
public static function getTagId(): int
{
return 22;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(22, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
$result = base64_decode($this->object->getNormalizedData($ignoreTags), true);
if (false === $result) {
throw new InvalidArgumentException('Unable to decode the data');
}
return $result;
}
}

View file

@ -0,0 +1,58 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use Base64Url\Base64Url;
use CBOR\ByteStringObject;
use CBOR\ByteStringWithChunkObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\TextStringObject;
use CBOR\TextStringWithChunkObject;
use InvalidArgumentException;
final class Base64UrlEncodingTag extends Base
{
public static function getTagId(): int
{
return 21;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject && !$object instanceof ByteStringWithChunkObject && !$object instanceof TextStringObject && !$object instanceof TextStringWithChunkObject) {
throw new InvalidArgumentException('This tag only accepts Byte String, Infinite Byte String, Text String or Infinite Text String objects.');
}
return new self(21, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject && !$this->object instanceof ByteStringWithChunkObject && !$this->object instanceof TextStringObject && !$this->object instanceof TextStringWithChunkObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return Base64Url::decode($this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,102 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\ListObject;
use CBOR\SignedIntegerObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use function count;
use function extension_loaded;
use InvalidArgumentException;
use RuntimeException;
final class BigFloatTag extends Base
{
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
{
if (!extension_loaded('bcmath')) {
throw new RuntimeException('The extension "bcmath" is required to use this tag');
}
parent::__construct($additionalInformation, $data, $object);
}
public static function getTagId(): int
{
return 5;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ListObject || 2 !== count($object)) {
throw new InvalidArgumentException('This tag only accepts a ListObject object that contains an exponent and a mantissa.');
}
$e = $object->get(0);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
}
$m = $object->get(1);
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
throw new InvalidArgumentException('The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.');
}
return new self(5, null, $object);
}
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Base
{
$object = new ListObject();
$object->add($e);
$object->add($m);
return self::create($object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ListObject || 2 !== count($this->object)) {
return $this->object->getNormalizedData($ignoreTags);
}
$e = $this->object->get(0);
$m = $this->object->get(1);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
return $this->object->getNormalizedData($ignoreTags);
}
return rtrim(
bcmul(
$m->getNormalizedData($ignoreTags),
bcpow(
'2',
$e->getNormalizedData($ignoreTags),
100),
100),
'0'
);
}
}

View file

@ -0,0 +1,97 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\ListObject;
use CBOR\SignedIntegerObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use function count;
use function extension_loaded;
use InvalidArgumentException;
use RuntimeException;
final class DecimalFractionTag extends Base
{
public function __construct(CBORObject $object)
{
if (!extension_loaded('bcmath')) {
throw new RuntimeException('The extension "bcmath" is required to use this tag');
}
if (!$object instanceof ListObject || 2 !== count($object)) {
throw new InvalidArgumentException('This tag only accepts a ListObject object that contains an exponent and a mantissa.');
}
$e = $object->get(0);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
throw new InvalidArgumentException('The exponent must be a Signed Integer or an Unsigned Integer object.');
}
$m = $object->get(1);
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
throw new InvalidArgumentException('The mantissa must be a Positive or Negative Signed Integer or an Unsigned Integer object.');
}
parent::__construct(4, null, $object);
}
public static function getTagId(): int
{
return 4;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($object);
}
public static function createFromExponentAndMantissa(CBORObject $e, CBORObject $m): Base
{
$object = new ListObject();
$object->add($e);
$object->add($m);
return new self($object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ListObject || 2 !== count($this->object)) {
return $this->object->getNormalizedData($ignoreTags);
}
$e = $this->object->get(0);
$m = $this->object->get(1);
if (!$e instanceof UnsignedIntegerObject && !$e instanceof SignedIntegerObject) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$m instanceof UnsignedIntegerObject && !$m instanceof SignedIntegerObject && !$m instanceof NegativeBigIntegerTag && !$m instanceof PositiveBigIntegerTag) {
return $this->object->getNormalizedData($ignoreTags);
}
return rtrim(
bcmul(
$m->getNormalizedData($ignoreTags),
bcpow(
'10',
$e->getNormalizedData($ignoreTags),
100),
100),
'0'
);
}
}

View file

@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use DateTimeImmutable;
final class EpochTag extends Base
{
public static function getTagId(): int
{
return 0;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
return new self(0, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
return DateTimeImmutable::createFromFormat(DATE_RFC3339, $this->object->getNormalizedData($ignoreTags));
}
}

View file

@ -0,0 +1,35 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
final class GenericTag extends Base
{
public static function getTagId(): int
{
return -1;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
return $this->object;
}
}

View file

@ -0,0 +1,57 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use Brick\Math\BigInteger;
use CBOR\ByteStringObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use InvalidArgumentException;
final class NegativeBigIntegerTag extends Base
{
public static function getTagId(): int
{
return 3;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(3, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject) {
return $this->object->getNormalizedData($ignoreTags);
}
$integer = BigInteger::fromBase(bin2hex($this->object->getValue()), 16);
$minusOne = BigInteger::of(-1);
return $minusOne->minus($integer)->toBase(10);
}
}

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\ByteStringObject;
use CBOR\CBORObject;
use CBOR\TagObject as Base;
use CBOR\Utils;
use InvalidArgumentException;
final class PositiveBigIntegerTag extends Base
{
public static function getTagId(): int
{
return 2;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof ByteStringObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(2, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
if (!$this->object instanceof ByteStringObject) {
return $this->object->getNormalizedData($ignoreTags);
}
return Utils::hexToString($this->object->getValue());
}
}

View file

@ -0,0 +1,55 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use function array_key_exists;
use Assert\Assertion;
use CBOR\CBORObject;
use CBOR\TagObject;
use CBOR\Utils;
use InvalidArgumentException;
class TagObjectManager
{
/**
* @var string[]
*/
private $classes = [];
public function add(string $class): void
{
if ($class::getTagId() < 0) {
throw new InvalidArgumentException('Invalid tag ID.');
}
$this->classes[$class::getTagId()] = $class;
}
public function getClassForValue(int $value): string
{
return array_key_exists($value, $this->classes) ? $this->classes[$value] : GenericTag::class;
}
public function createObjectForValue(int $additionalInformation, ?string $data, CBORObject $object): TagObject
{
$value = $additionalInformation;
if ($additionalInformation >= 24) {
Assertion::string($data, 'Invalid data');
$value = Utils::binToInt($data);
}
/** @var TagObject $class */
$class = $this->getClassForValue($value);
return $class::createFromLoadedData($additionalInformation, $data, $object);
}
}

View file

@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR\Tag;
use CBOR\CBORObject;
use CBOR\OtherObject\DoublePrecisionFloatObject;
use CBOR\OtherObject\HalfPrecisionFloatObject;
use CBOR\OtherObject\SinglePrecisionFloatObject;
use CBOR\TagObject as Base;
use CBOR\UnsignedIntegerObject;
use DateTimeImmutable;
use InvalidArgumentException;
use function strval;
final class TimestampTag extends Base
{
public static function getTagId(): int
{
return 1;
}
public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): Base
{
return new self($additionalInformation, $data, $object);
}
public static function create(CBORObject $object): Base
{
if (!$object instanceof UnsignedIntegerObject && !$object instanceof HalfPrecisionFloatObject && !$object instanceof SinglePrecisionFloatObject && !$object instanceof DoublePrecisionFloatObject) {
throw new InvalidArgumentException('This tag only accepts a Byte String object.');
}
return new self(1, null, $object);
}
public function getNormalizedData(bool $ignoreTags = false)
{
if ($ignoreTags) {
return $this->object->getNormalizedData($ignoreTags);
}
switch (true) {
case $this->object instanceof UnsignedIntegerObject:
return DateTimeImmutable::createFromFormat('U', strval($this->object->getNormalizedData($ignoreTags)));
case $this->object instanceof HalfPrecisionFloatObject:
case $this->object instanceof SinglePrecisionFloatObject:
case $this->object instanceof DoublePrecisionFloatObject:
return DateTimeImmutable::createFromFormat('U.u', strval($this->object->getNormalizedData($ignoreTags)));
default:
return $this->object->getNormalizedData($ignoreTags);
}
}
}

View file

@ -0,0 +1,56 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
abstract class TagObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b110;
/**
* @var string|null
*/
protected $data;
/**
* @var CBORObject
*/
protected $object;
public function __construct(int $additionalInformation, ?string $data, CBORObject $object)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->object = $object;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
$result .= (string) $this->object;
return $result;
}
abstract public static function getTagId(): int;
abstract public static function createFromLoadedData(int $additionalInformation, ?string $data, CBORObject $object): self;
public function getValue(): CBORObject
{
return $this->object;
}
}

View file

@ -0,0 +1,64 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
final class TextStringObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b011;
/**
* @var int|null
*/
private $length;
/**
* @var string
*/
private $data;
public function __construct(string $data)
{
list($additionalInformation, $length) = LengthCalculator::getLengthOfString($data);
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
$this->length = $length;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->length) {
$result .= $this->length;
}
$result .= $this->data;
return $result;
}
public function getValue(): string
{
return $this->data;
}
public function getLength(): int
{
return mb_strlen($this->data, 'utf8');
}
public function getNormalizedData(bool $ignoreTags = false): string
{
return $this->data;
}
}

View file

@ -0,0 +1,87 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use InvalidArgumentException;
final class TextStringWithChunkObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b011;
private const ADDITIONAL_INFORMATION = 0b00011111;
/**
* @var TextStringObject[]
*/
private $data = [];
public function __construct()
{
parent::__construct(self::MAJOR_TYPE, self::ADDITIONAL_INFORMATION);
}
public function __toString(): string
{
$result = parent::__toString();
foreach ($this->data as $object) {
$result .= (string) $object;
}
$bin = hex2bin('FF');
if (false === $bin) {
throw new InvalidArgumentException('Unable to convert the data');
}
$result .= $bin;
return $result;
}
public function add(TextStringObject $chunk): void
{
$this->data[] = $chunk;
}
public function append(string $chunk): void
{
$this->add(new TextStringObject($chunk));
}
public function getValue(): string
{
$result = '';
foreach ($this->data as $object) {
$result .= $object->getValue();
}
return $result;
}
public function getLength(): int
{
$length = 0;
foreach ($this->data as $object) {
$length += $object->getLength();
}
return $length;
}
public function getNormalizedData(bool $ignoreTags = false): string
{
$result = '';
foreach ($this->data as $object) {
$result .= $object->getNormalizedData($ignoreTags);
}
return $result;
}
}

View file

@ -0,0 +1,167 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
use GMP;
use InvalidArgumentException;
final class UnsignedIntegerObject extends AbstractCBORObject
{
private const MAJOR_TYPE = 0b000;
/**
* @var string|null
*/
private $data;
public function __construct(int $additionalInformation, ?string $data)
{
parent::__construct(self::MAJOR_TYPE, $additionalInformation);
$this->data = $data;
}
public function __toString(): string
{
$result = parent::__toString();
if (null !== $this->data) {
$result .= $this->data;
}
return $result;
}
public static function createObjectForValue(int $additionalInformation, ?string $data): self
{
return new self($additionalInformation, $data);
}
public static function create(int $value): self
{
return self::createFromString((string) $value);
}
public static function createFromHex(string $value): self
{
$integer = BigInteger::fromBase($value, 16);
return self::createBigInteger($integer);
}
public static function createFromString(string $value): self
{
$integer = BigInteger::of($value);
return self::createBigInteger($integer);
}
/**
* @deprecated Deprecated since v1.1 and will be removed in v2.0. Please use "create" or "createFromString" instead
*/
public static function createFromGmpValue(GMP $value): self
{
if (gmp_cmp($value, gmp_init(0)) < 0) {
throw new InvalidArgumentException('The value must be a positive integer.');
}
switch (true) {
case gmp_cmp($value, gmp_init(24)) < 0:
$ai = gmp_intval($value);
$data = null;
break;
case gmp_cmp($value, gmp_init('FF', 16)) < 0:
$ai = 24;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 2, '0', STR_PAD_LEFT));
break;
case gmp_cmp($value, gmp_init('FFFF', 16)) < 0:
$ai = 25;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 4, '0', STR_PAD_LEFT));
break;
case gmp_cmp($value, gmp_init('FFFFFFFF', 16)) < 0:
$ai = 26;
$data = self::hex2bin(str_pad(gmp_strval($value, 16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
public function getMajorType(): int
{
return self::MAJOR_TYPE;
}
public function getAdditionalInformation(): int
{
return $this->additionalInformation;
}
public function getValue(): string
{
return $this->getNormalizedData();
}
public function getNormalizedData(bool $ignoreTags = false): string
{
if (null === $this->data) {
return (string) $this->additionalInformation;
}
$integer = BigInteger::fromBase(bin2hex($this->data), 16);
return $integer->toBase(10);
}
private static function createBigInteger(BigInteger $integer): self
{
if ($integer->isLessThan(BigInteger::zero())) {
throw new InvalidArgumentException('The value must be a positive integer.');
}
switch (true) {
case $integer->isLessThan(BigInteger::of(24)):
$ai = $integer->toInt();
$data = null;
break;
case $integer->isLessThan(BigInteger::fromBase('FF', 16)):
$ai = 24;
$data = self::hex2bin(str_pad($integer->toBase(16), 2, '0', STR_PAD_LEFT));
break;
case $integer->isLessThan(BigInteger::fromBase('FFFF', 16)):
$ai = 25;
$data = self::hex2bin(str_pad($integer->toBase(16), 4, '0', STR_PAD_LEFT));
break;
case $integer->isLessThan(BigInteger::fromBase('FFFFFFFF', 16)):
$ai = 26;
$data = self::hex2bin(str_pad($integer->toBase(16), 8, '0', STR_PAD_LEFT));
break;
default:
throw new InvalidArgumentException('Out of range. Please use PositiveBigIntegerTag tag with ByteStringObject object instead.');
}
return new self($ai, $data);
}
private static function hex2bin(string $data): string
{
$result = hex2bin($data);
if (false === $result) {
throw new InvalidArgumentException('Unable to convert the data');
}
return $result;
}
}

View file

@ -0,0 +1,52 @@
<?php
declare(strict_types=1);
/*
* The MIT License (MIT)
*
* Copyright (c) 2018-2020 Spomky-Labs
*
* This software may be modified and distributed under the terms
* of the MIT license. See the LICENSE file for details.
*/
namespace CBOR;
use Brick\Math\BigInteger;
/**
* @internal
*/
abstract class Utils
{
public static function binToInt(string $value): int
{
return self::binToBigInteger($value)->toInt();
}
public static function binToBigInteger(string $value): BigInteger
{
return self::hexToBigInteger(bin2hex($value));
}
public static function hexToInt(string $value): int
{
return self::hexToBigInteger($value)->toInt();
}
public static function hexToBigInteger(string $value): BigInteger
{
return BigInteger::fromBase($value, 16);
}
public static function hexToString(string $value): string
{
return BigInteger::fromBase(bin2hex($value), 16)->toBase(10);
}
public static function intToHex(int $value): string
{
return BigInteger::of($value)->toBase(16);
}
}