Skip to content

Commit 9b91918

Browse files
JakeQZbartbutler
andauthored
[TASK] Avoid poor scaling of array_search() with very long arrays (#628)
When there were many many elements in `$aStack`, starting the delimiter search from the beginning for each loop iteration was very slow. This is addressed by building a new array, rather than modifying `$aStack` in place, and iterating over it in a single pass. A particular 1.6M style string is now parsed in 5 seconds rather than 4 minutes. Co-authored-by: Bart Butler <bart+github@pm.me>
1 parent ab0baf7 commit 9b91918

File tree

2 files changed

+15
-6
lines changed

2 files changed

+15
-6
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).
1212

1313
### Changed
1414

15+
- Improve performance of Value::parseValue with many delimiters by refactoring to remove array_search() (#413)
16+
1517
### Deprecated
1618

1719
### Removed

src/Value/Value.php

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -67,23 +67,30 @@ public static function parseValue(ParserState $oParserState, array $aListDelimit
6767
}
6868
// Convert the list to list objects
6969
foreach ($aListDelimiters as $sDelimiter) {
70-
if (count($aStack) === 1) {
70+
$iStackLength = count($aStack);
71+
if ($iStackLength === 1) {
7172
return $aStack[0];
7273
}
73-
$iStartPosition = null;
74-
while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) {
74+
$aNewStack = [];
75+
for ($iStartPosition = 0; $iStartPosition < $iStackLength; ++$iStartPosition) {
76+
if ($iStartPosition === ($iStackLength - 1) || $sDelimiter !== $aStack[$iStartPosition + 1]) {
77+
$aNewStack[] = $aStack[$iStartPosition];
78+
continue;
79+
}
7580
$iLength = 2; //Number of elements to be joined
76-
for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) {
81+
for ($i = $iStartPosition + 3; $i < $iStackLength; $i += 2, ++$iLength) {
7782
if ($sDelimiter !== $aStack[$i]) {
7883
break;
7984
}
8085
}
8186
$oList = new RuleValueList($sDelimiter, $oParserState->currentLine());
82-
for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) {
87+
for ($i = $iStartPosition; $i - $iStartPosition < $iLength * 2; $i += 2) {
8388
$oList->addListComponent($aStack[$i]);
8489
}
85-
array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]);
90+
$aNewStack[] = $oList;
91+
$iStartPosition += $iLength * 2 - 2;
8692
}
93+
$aStack = $aNewStack;
8794
}
8895
if (!isset($aStack[0])) {
8996
throw new UnexpectedTokenException(

0 commit comments

Comments
 (0)