[Uid] improve base convertion logic
This commit is contained in:
parent
42c76d7683
commit
0e05c6de80
97
src/Symfony/Component/Uid/BinaryUtil.php
Normal file
97
src/Symfony/Component/Uid/BinaryUtil.php
Normal file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class BinaryUtil
|
||||
{
|
||||
public const BASE10 = [
|
||||
'' => '0123456789',
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
];
|
||||
|
||||
public static function toBase(string $bytes, array $map): string
|
||||
{
|
||||
$base = \strlen($alphabet = $map['']);
|
||||
$bytes = array_values(unpack(\PHP_INT_SIZE >= 8 ? 'n*' : 'C*', $bytes));
|
||||
$digits = '';
|
||||
|
||||
while ($count = \count($bytes)) {
|
||||
$quotient = [];
|
||||
$remainder = 0;
|
||||
|
||||
for ($i = 0; $i !== $count; ++$i) {
|
||||
$carry = $bytes[$i] + ($remainder << (\PHP_INT_SIZE >= 8 ? 16 : 8));
|
||||
$digit = intdiv($carry, $base);
|
||||
$remainder = $carry % $base;
|
||||
|
||||
if ($digit || $quotient) {
|
||||
$quotient[] = $digit;
|
||||
}
|
||||
}
|
||||
|
||||
$digits = $alphabet[$remainder].$digits;
|
||||
$bytes = $quotient;
|
||||
}
|
||||
|
||||
return $digits;
|
||||
}
|
||||
|
||||
public static function fromBase(string $digits, array $map): string
|
||||
{
|
||||
$base = \strlen($map['']);
|
||||
$count = \strlen($digits);
|
||||
$bytes = [];
|
||||
|
||||
while ($count) {
|
||||
$quotient = [];
|
||||
$remainder = 0;
|
||||
|
||||
for ($i = 0; $i !== $count; ++$i) {
|
||||
$carry = ($bytes ? $digits[$i] : $map[$digits[$i]]) + $remainder * $base;
|
||||
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
$digit = $carry >> 16;
|
||||
$remainder = $carry & 0xFFFF;
|
||||
} else {
|
||||
$digit = $carry >> 8;
|
||||
$remainder = $carry & 0xFF;
|
||||
}
|
||||
|
||||
if ($digit || $quotient) {
|
||||
$quotient[] = $digit;
|
||||
}
|
||||
}
|
||||
|
||||
$bytes[] = $remainder;
|
||||
$count = \count($digits = $quotient);
|
||||
}
|
||||
|
||||
return pack(\PHP_INT_SIZE >= 8 ? 'n*' : 'C*', ...array_reverse($bytes));
|
||||
}
|
||||
|
||||
public static function add(string $a, string $b): string
|
||||
{
|
||||
$carry = 0;
|
||||
for ($i = 7; 0 <= $i; --$i) {
|
||||
$carry += \ord($a[$i]) + \ord($b[$i]);
|
||||
$a[$i] = \chr($carry & 0xFF);
|
||||
$carry >>= 8;
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
}
|
@ -1,85 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the Symfony package.
|
||||
*
|
||||
* (c) Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Symfony\Component\Uid;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @author Nicolas Grekas <p@tchwork.com>
|
||||
*/
|
||||
class InternalUtil
|
||||
{
|
||||
public static function toBinary(string $digits): string
|
||||
{
|
||||
$bytes = '';
|
||||
$len = \strlen($digits);
|
||||
|
||||
while ($len > $i = strspn($digits, '0')) {
|
||||
for ($j = 2, $r = 0; $i < $len; $i += $j, $j = 0) {
|
||||
do {
|
||||
$r *= 10;
|
||||
$d = (int) substr($digits, $i, ++$j);
|
||||
} while ($i + $j < $len && $r + $d < 256);
|
||||
|
||||
$j = \strlen((string) $d);
|
||||
$q = str_pad(($d += $r) >> 8, $j, '0', STR_PAD_LEFT);
|
||||
$digits = substr_replace($digits, $q, $i, $j);
|
||||
$r = $d % 256;
|
||||
}
|
||||
|
||||
$bytes .= \chr($r);
|
||||
}
|
||||
|
||||
return strrev($bytes);
|
||||
}
|
||||
|
||||
public static function toDecimal(string $bytes): string
|
||||
{
|
||||
$digits = '';
|
||||
$len = \strlen($bytes);
|
||||
|
||||
while ($len > $i = strspn($bytes, "\0")) {
|
||||
for ($r = 0; $i < $len; $i += $j) {
|
||||
$j = $d = 0;
|
||||
do {
|
||||
$r <<= 8;
|
||||
$d = ($d << 8) + \ord($bytes[$i + $j]);
|
||||
} while ($i + ++$j < $len && $r + $d < 10);
|
||||
|
||||
if (256 < $d) {
|
||||
$q = intdiv($d += $r, 10);
|
||||
$bytes[$i] = \chr($q >> 8);
|
||||
$bytes[1 + $i] = \chr($q & 0xFF);
|
||||
} else {
|
||||
$bytes[$i] = \chr(intdiv($d += $r, 10));
|
||||
}
|
||||
$r = $d % 10;
|
||||
}
|
||||
|
||||
$digits .= (string) $r;
|
||||
}
|
||||
|
||||
return strrev($digits);
|
||||
}
|
||||
|
||||
public static function binaryAdd(string $a, string $b): string
|
||||
{
|
||||
$sum = 0;
|
||||
for ($i = 7; 0 <= $i; --$i) {
|
||||
$sum += \ord($a[$i]) + \ord($b[$i]);
|
||||
$a[$i] = \chr($sum & 0xFF);
|
||||
$sum >>= 8;
|
||||
}
|
||||
|
||||
return $a;
|
||||
}
|
||||
}
|
@ -121,7 +121,7 @@ class Ulid implements \JsonSerializable
|
||||
base_convert(substr($time, 6, 4), 32, 16)
|
||||
);
|
||||
|
||||
return InternalUtil::toDecimal(hex2bin($time)) / 1000;
|
||||
return BinaryUtil::toBase(hex2bin($time), BinaryUtil::BASE10) / 1000;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
@ -163,7 +163,7 @@ class Ulid implements \JsonSerializable
|
||||
if (\PHP_INT_SIZE >= 8) {
|
||||
$time = base_convert($time, 10, 32);
|
||||
} else {
|
||||
$time = bin2hex(InternalUtil::toBinary($time));
|
||||
$time = bin2hex(BinaryUtil::fromBase($time, BinaryUtil::BASE10));
|
||||
$time = sprintf('%s%04s%04s',
|
||||
base_convert(substr($time, 0, 2), 16, 32),
|
||||
base_convert(substr($time, 2, 5), 16, 32),
|
||||
|
@ -121,10 +121,10 @@ class Uuid implements \JsonSerializable
|
||||
}
|
||||
|
||||
$time = str_pad(hex2bin($time), 8, "\0", STR_PAD_LEFT);
|
||||
$time = InternalUtil::binaryAdd($time, self::TIME_OFFSET_COM);
|
||||
$time = BinaryUtil::add($time, self::TIME_OFFSET_COM);
|
||||
$time[0] = $time[0] & "\x7F";
|
||||
|
||||
return InternalUtil::toDecimal($time) / 10000000;
|
||||
return BinaryUtil::toBase($time, BinaryUtil::BASE10) / 10000000;
|
||||
}
|
||||
|
||||
public function getMac(): string
|
||||
|
Reference in New Issue
Block a user