Skip to content

Commit 660c00f

Browse files
committed
Reduce string manipulation
1 parent e793c96 commit 660c00f

File tree

1 file changed

+49
-33
lines changed

1 file changed

+49
-33
lines changed

src/Internal/ArrayParser.php

Lines changed: 49 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ final class ArrayParser
1414
use ForbidCloning;
1515
use ForbidSerialization;
1616

17+
private const WHITESPACE_CHARS = " \n\r\t\v\0";
18+
1719
/**
1820
* @param string $data String representation of PostgresSQL array.
1921
* @param \Closure(string):mixed $cast Callback to cast parsed values.
@@ -25,12 +27,10 @@ final class ArrayParser
2527
*/
2628
public static function parse(string $data, \Closure $cast, string $delimiter = ','): array
2729
{
28-
$data = \trim($data);
29-
3030
$parser = new self($data, $cast, $delimiter);
3131
$result = $parser->parseToArray();
3232

33-
if ($parser->data !== '') {
33+
if (isset($parser->data[$parser->position])) {
3434
throw new PostgresParseException("Data left in buffer after parsing");
3535
}
3636

@@ -43,9 +43,10 @@ public static function parse(string $data, \Closure $cast, string $delimiter = '
4343
* @param string $delimiter Delimiter used to separate values.
4444
*/
4545
private function __construct(
46-
private string $data,
46+
private readonly string $data,
4747
private readonly \Closure $cast,
48-
private readonly string $delimiter = ',',
48+
private readonly string $delimiter,
49+
private int $position = 0,
4950
) {
5051
}
5152

@@ -58,36 +59,39 @@ private function parseToArray(): array
5859
{
5960
$result = [];
6061

61-
if ($this->data === '') {
62+
$this->position = $this->skipWhitespace($this->position);
63+
64+
if (!isset($this->data[$this->position])) {
6265
throw new PostgresParseException("Unexpected end of data");
6366
}
6467

65-
if ($this->data[0] !== '{') {
68+
if ($this->data[$this->position] !== '{') {
6669
throw new PostgresParseException("Missing opening bracket");
6770
}
6871

69-
$this->data = \ltrim(\substr($this->data, 1));
72+
$this->position = $this->skipWhitespace($this->position + 1);
7073

7174
do {
72-
if ($this->data === '') {
75+
if (!isset($this->data[$this->position])) {
7376
throw new PostgresParseException("Unexpected end of data");
7477
}
7578

76-
if ($this->data[0] === '}') { // Empty array
77-
$this->data = \ltrim(\substr($this->data, 1));
79+
if ($this->data[$this->position] === '}') { // Empty array
80+
$this->position = $this->skipWhitespace($this->position + 1);
7881
break;
7982
}
8083

81-
if ($this->data[0] === '{') { // Array
82-
$parser = new self($this->data, $this->cast, $this->delimiter);
84+
if ($this->data[$this->position] === '{') { // Array
85+
$parser = new self($this->data, $this->cast, $this->delimiter, $this->position);
8386
$result[] = $parser->parseToArray();
84-
$this->data = $parser->data;
85-
$end = $this->trim(0);
87+
$this->position = $parser->position;
88+
$delimiter = $this->moveToNextDelimiter($this->position);
8689
continue;
8790
}
8891

89-
if ($this->data[0] === '"') { // Quoted value
90-
for ($position = 1; isset($this->data[$position]); ++$position) {
92+
if ($this->data[$this->position] === '"') { // Quoted value
93+
++$this->position;
94+
for ($position = $this->position; isset($this->data[$position]); ++$position) {
9195
if ($this->data[$position] === '\\') {
9296
++$position; // Skip next character
9397
continue;
@@ -102,27 +106,30 @@ private function parseToArray(): array
102106
throw new PostgresParseException("Could not find matching quote in quoted value");
103107
}
104108

105-
$yield = \stripslashes(\substr($this->data, 1, $position - 1));
109+
$entry = \stripslashes(\substr($this->data, $this->position, $position - $this->position));
106110

107-
$end = $this->trim($position + 1);
111+
$delimiter = $this->moveToNextDelimiter($position + 1);
108112
} else { // Unquoted value
109-
$position = 0;
110-
while (isset($this->data[$position]) && $this->data[$position] !== $this->delimiter && $this->data[$position] !== '}') {
113+
$position = $this->position;
114+
while (isset($this->data[$position])
115+
&& $this->data[$position] !== $this->delimiter
116+
&& $this->data[$position] !== '}'
117+
) {
111118
++$position;
112119
}
113120

114-
$yield = \trim(\substr($this->data, 0, $position));
121+
$entry = \trim(\substr($this->data, $this->position, $position - $this->position));
115122

116-
$end = $this->trim($position);
123+
$delimiter = $this->moveToNextDelimiter($position);
117124

118-
if (\strcasecmp($yield, "NULL") === 0) { // Literal NULL is always unquoted.
125+
if (\strcasecmp($entry, "NULL") === 0) { // Literal NULL is always unquoted.
119126
$result[] = null;
120127
continue;
121128
}
122129
}
123130

124-
$result[] = ($this->cast)($yield);
125-
} while ($end !== '}');
131+
$result[] = ($this->cast)($entry);
132+
} while ($delimiter !== '}');
126133

127134
return $result;
128135
}
@@ -134,22 +141,31 @@ private function parseToArray(): array
134141
*
135142
* @throws PostgresParseException
136143
*/
137-
private function trim(int $position): string
144+
private function moveToNextDelimiter(int $position): string
138145
{
139-
$this->data = \ltrim(\substr($this->data, $position));
146+
$position = $this->skipWhitespace($position);
140147

141-
if ($this->data === '') {
148+
if (!isset($this->data[$position])) {
142149
throw new PostgresParseException("Unexpected end of data");
143150
}
144151

145-
$end = $this->data[0];
152+
$delimiter = $this->data[$position];
146153

147-
if ($end !== $this->delimiter && $end !== '}') {
154+
if ($delimiter !== $this->delimiter && $delimiter !== '}') {
148155
throw new PostgresParseException("Invalid delimiter");
149156
}
150157

151-
$this->data = \ltrim(\substr($this->data, 1));
158+
$this->position = $this->skipWhitespace($position + 1);
159+
160+
return $delimiter;
161+
}
162+
163+
private function skipWhitespace(int $position): int
164+
{
165+
while (isset($this->data[$position]) && \str_contains(self::WHITESPACE_CHARS, $this->data[$position])) {
166+
++$position;
167+
}
152168

153-
return $end;
169+
return $position;
154170
}
155171
}

0 commit comments

Comments
 (0)