Skip to content

Commit

Permalink
[TASK] Avoid poor scaling of array_search() with very long arrays (#628)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
JakeQZ and bartbutler authored Jun 29, 2024
1 parent ab0baf7 commit 9b91918
Show file tree
Hide file tree
Showing 2 changed files with 15 additions and 6 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ This project adheres to [Semantic Versioning](https://semver.org/).

### Changed

- Improve performance of Value::parseValue with many delimiters by refactoring to remove array_search() (#413)

### Deprecated

### Removed
Expand Down
19 changes: 13 additions & 6 deletions src/Value/Value.php
Original file line number Diff line number Diff line change
Expand Up @@ -67,23 +67,30 @@ public static function parseValue(ParserState $oParserState, array $aListDelimit
}
// Convert the list to list objects
foreach ($aListDelimiters as $sDelimiter) {
if (count($aStack) === 1) {
$iStackLength = count($aStack);
if ($iStackLength === 1) {
return $aStack[0];
}
$iStartPosition = null;
while (($iStartPosition = array_search($sDelimiter, $aStack, true)) !== false) {
$aNewStack = [];
for ($iStartPosition = 0; $iStartPosition < $iStackLength; ++$iStartPosition) {
if ($iStartPosition === ($iStackLength - 1) || $sDelimiter !== $aStack[$iStartPosition + 1]) {
$aNewStack[] = $aStack[$iStartPosition];
continue;
}
$iLength = 2; //Number of elements to be joined
for ($i = $iStartPosition + 2; $i < count($aStack); $i += 2, ++$iLength) {
for ($i = $iStartPosition + 3; $i < $iStackLength; $i += 2, ++$iLength) {
if ($sDelimiter !== $aStack[$i]) {
break;
}
}
$oList = new RuleValueList($sDelimiter, $oParserState->currentLine());
for ($i = $iStartPosition - 1; $i - $iStartPosition + 1 < $iLength * 2; $i += 2) {
for ($i = $iStartPosition; $i - $iStartPosition < $iLength * 2; $i += 2) {
$oList->addListComponent($aStack[$i]);
}
array_splice($aStack, $iStartPosition - 1, $iLength * 2 - 1, [$oList]);
$aNewStack[] = $oList;
$iStartPosition += $iLength * 2 - 2;
}
$aStack = $aNewStack;
}
if (!isset($aStack[0])) {
throw new UnexpectedTokenException(
Expand Down

0 comments on commit 9b91918

Please sign in to comment.