Skip to content

Commit 9390a85

Browse files
authored
Merge pull request #18 from GromNaN/optim-string
Performance optimizations
2 parents 8cbfcfc + 66d5ae8 commit 9390a85

File tree

1 file changed

+39
-46
lines changed

1 file changed

+39
-46
lines changed

src/Sqids.php

Lines changed: 39 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@
1717
use InvalidArgumentException;
1818
use RuntimeException;
1919

20+
use function strlen;
21+
use function ord;
22+
use function in_array;
23+
use function count;
24+
use function array_key_exists;
25+
2026
class Sqids implements SqidsInterface
2127
{
2228
final public const DEFAULT_ALPHABET = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
@@ -606,16 +612,12 @@ public function __construct(
606612
throw new InvalidArgumentException('Alphabet length must be at least 3');
607613
}
608614

609-
if (count(array_unique(str_split($alphabet))) !== strlen($alphabet)) {
615+
if (preg_match('/(.).*\1/', $alphabet)) {
610616
throw new InvalidArgumentException('Alphabet must contain unique characters');
611617
}
612618

613619
$minLengthLimit = 255;
614-
if (
615-
!is_int($minLength) ||
616-
$minLength < 0 ||
617-
$minLength > $minLengthLimit
618-
) {
620+
if ($minLength < 0 || $minLength > $minLengthLimit) {
619621
throw new InvalidArgumentException(
620622
'Minimum length has to be between 0 and ' . $minLengthLimit,
621623
);
@@ -635,7 +637,6 @@ public function __construct(
635637
}
636638

637639
$this->alphabet = $this->shuffle($alphabet);
638-
$this->minLength = $minLength;
639640
$this->blocklist = $filteredBlocklist;
640641
}
641642

@@ -655,11 +656,12 @@ public function encode(array $numbers): string
655656
return '';
656657
}
657658

658-
$inRangeNumbers = array_filter($numbers, fn($n) => $n >= 0 && $n <= self::maxValue());
659-
if (count($inRangeNumbers) != count($numbers)) {
660-
throw new InvalidArgumentException(
661-
'Encoding supports numbers between 0 and ' . self::maxValue(),
662-
);
659+
foreach ($numbers as $n) {
660+
if ($n < 0 || $n > self::maxValue()) {
661+
throw new InvalidArgumentException(
662+
'Encoding supports numbers between 0 and ' . self::maxValue(),
663+
);
664+
}
663665
}
664666

665667
return $this->encodeNumbers($numbers);
@@ -688,26 +690,24 @@ protected function encodeNumbers(array $numbers, int $increment = 0): string
688690
$alphabet = substr($this->alphabet, $offset) . substr($this->alphabet, 0, $offset);
689691
$prefix = $alphabet[0];
690692
$alphabet = strrev($alphabet);
691-
$ret = [$prefix];
693+
$id = $prefix;
692694

693695
for ($i = 0; $i != count($numbers); $i++) {
694696
$num = $numbers[$i];
695697

696-
$ret[] = $this->toId($num, substr($alphabet, 1));
698+
$id .= $this->toId($num, substr($alphabet, 1));
697699
if ($i < count($numbers) - 1) {
698-
$ret[] = $alphabet[0];
700+
$id .= $alphabet[0];
699701
$alphabet = $this->shuffle($alphabet);
700702
}
701703
}
702704

703-
$id = implode('', $ret);
704-
705705
if ($this->minLength > strlen($id)) {
706706
$id .= $alphabet[0];
707707

708708
while ($this->minLength - strlen($id) > 0) {
709709
$alphabet = $this->shuffle($alphabet);
710-
$id .= substr($alphabet, 0, min($this->minLength - strlen($id), strlen($alphabet)));
710+
$id .= substr($alphabet, 0, min($this->minLength - strlen($id), strlen($this->alphabet)));
711711
}
712712
}
713713

@@ -736,11 +736,8 @@ public function decode(string $id): array
736736
return $ret;
737737
}
738738

739-
$alphabetChars = str_split($this->alphabet);
740-
foreach (str_split($id) as $c) {
741-
if (!in_array($c, $alphabetChars)) {
742-
return $ret;
743-
}
739+
if (!preg_match('/^[' . preg_quote($this->alphabet, '/') . ']+$/', $id)) {
740+
return $ret;
744741
}
745742

746743
$prefix = $id[0];
@@ -753,13 +750,13 @@ public function decode(string $id): array
753750
$separator = $alphabet[0];
754751

755752
$chunks = explode($separator, $id, 2);
756-
if (!empty($chunks)) {
753+
if (array_key_exists(0, $chunks)) {
757754
if ($chunks[0] == '') {
758755
return $ret;
759756
}
760757

761758
$ret[] = $this->toNumber($chunks[0], substr($alphabet, 1));
762-
if (count($chunks) > 1) {
759+
if (array_key_exists(1, $chunks)) {
763760
$alphabet = $this->shuffle($alphabet);
764761
}
765762
}
@@ -772,40 +769,36 @@ public function decode(string $id): array
772769

773770
protected function shuffle(string $alphabet): string
774771
{
775-
$chars = str_split($alphabet);
776-
777-
for ($i = 0, $j = count($chars) - 1; $j > 0; $i++, $j--) {
778-
$r = ($i * $j + ord($chars[$i]) + ord($chars[$j])) % count($chars);
779-
[$chars[$i], $chars[$r]] = [$chars[$r], $chars[$i]];
772+
for ($i = 0, $j = strlen($alphabet) - 1; $j > 0; $i++, $j--) {
773+
$r = ($i * $j + ord($alphabet[$i]) + ord($alphabet[$j])) % strlen($alphabet);
774+
[$alphabet[$i], $alphabet[$r]] = [$alphabet[$r], $alphabet[$i]];
780775
}
781776

782-
return implode('', $chars);
777+
return $alphabet;
783778
}
784779

785780
protected function toId(int $num, string $alphabet): string
786781
{
787-
$id = [];
788-
$chars = str_split($alphabet);
789-
790-
$result = $num;
791-
782+
$id = '';
792783
do {
793-
array_unshift($id, $chars[$this->math->intval($this->math->mod($result, count($chars)))]);
794-
$result = $this->math->divide($result, count($chars));
795-
} while ($this->math->greaterThan($result, 0));
784+
$id = $alphabet[$this->math->intval($this->math->mod($num, strlen($alphabet)))] . $id;
785+
$num = $this->math->divide($num, strlen($alphabet));
786+
} while ($this->math->greaterThan($num, 0));
796787

797-
return implode('', $id);
788+
return $id;
798789
}
799790

800791
protected function toNumber(string $id, string $alphabet): int
801792
{
802-
$chars = str_split($alphabet);
803-
return $this->math->intval(array_reduce(str_split($id), function ($a, $v) use ($chars) {
804-
$number = $this->math->multiply($a, count($chars));
805-
$number = $this->math->add($number, array_search($v, $chars));
793+
$number = 0;
794+
for ($i = 0; $i < strlen($id); $i++) {
795+
$number = $this->math->add(
796+
$this->math->multiply($number, strlen($alphabet)),
797+
strpos($alphabet, $id[$i]),
798+
);
799+
}
806800

807-
return $number;
808-
}, 0));
801+
return $this->math->intval($number);
809802
}
810803

811804
protected function isBlockedId(string $id): bool

0 commit comments

Comments
 (0)