Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/treetrek.git

Eliminates O(N²) concatenations

AuthorDave Jarvis <email>
Date2026-02-21 11:31:56 GMT-0800
Commit6de003a6a891dec281891454ab8cb43f1ac447b2
Parentfbed27e
git/DeltaDecoder.php
$pos += $res['used'];
- $out = '';
- $len = strlen( $delta );
- $done = false;
+ $chunks = [];
+ $len = strlen( $delta );
+ $outLen = 0;
+ $done = false;
+ $result = '';
while( !$done && $pos < $len ) {
- if( $cap > 0 && strlen( $out ) >= $cap ) {
+ if( $cap > 0 && $outLen >= $cap ) {
$done = true;
}
if( !$done ) {
$op = ord( $delta[$pos++] );
if( $op & 128 ) {
- $info = $this->parseCopyInstruction( $op, $delta, $pos );
- $out .= substr( $base, $info['off'], $info['len'] );
- $pos += $info['used'];
+ $info = $this->parseCopyInstruction( $op, $delta, $pos );
+ $extracted = substr( $base, $info['off'], $info['len'] );
+ $chunks[] = $extracted;
+ $outLen += strlen( $extracted );
+ $pos += $info['used'];
} else {
- $ln = $op & 127;
- $out .= substr( $delta, $pos, $ln );
- $pos += $ln;
+ $ln = $op & 127;
+ $extracted = substr( $delta, $pos, $ln );
+ $chunks[] = $extracted;
+ $outLen += strlen( $extracted );
+ $pos += $ln;
}
}
}
- return $out;
+ $result = implode( '', $chunks );
+
+ if( $cap > 0 && strlen( $result ) > $cap ) {
+ $result = substr( $result, 0, $cap );
+ }
+
+ return $result;
}
if( !$doneBuffer ) {
if( $state < 2 ) {
- $pos = 0;
+ $pos = 0;
+ $found = false;
- while( $pos < $len && (ord( $buffer[$pos] ) & 128) ) {
- $pos++;
- }
+ while( !$found && $pos < $len ) {
+ if( !(ord( $buffer[$pos] ) & 128) ) {
+ $found = true;
+ }
- if( $pos === $len && (ord( $buffer[$pos - 1] ) & 128) ) {
- $doneBuffer = true;
+ $pos++;
}
- if( !$doneBuffer ) {
- $buffer = substr( $buffer, $pos + 1 );
+ if( $found ) {
+ $buffer = substr( $buffer, $pos );
$state++;
+ } else {
+ $doneBuffer = true;
}
} else {
git/FileHandlePool.php
int $fallback = 0
): int {
- $result = $this->withHandle( $path, $action );
- return is_int( $result ) ? $result : $fallback;
+ $computed = $this->withHandle( $path, $action );
+
+ return is_int( $computed ) ? $computed : $fallback;
}
public function computeString(
string $path,
callable $action,
string $fallback = ''
): string {
- $result = $this->withHandle( $path, $action );
- return is_string( $result ) ? $result : $fallback;
+ $computed = $this->withHandle( $path, $action );
+
+ return is_string( $computed ) ? $computed : $fallback;
}
}
- private function withHandle( string $path, callable $action ) {
+ public function computeStringDedicated(
+ string $path,
+ callable $action,
+ string $fallback = ''
+ ): string {
+ $computed = $this->withDedicatedHandle( $path, $action );
+
+ return is_string( $computed ) ? $computed : $fallback;
+ }
+
+ public function streamGeneratorDedicated(
+ string $path,
+ callable $action
+ ): Generator {
+ $resultGenerator = $this->withDedicatedHandle( $path, $action );
+
+ if( $resultGenerator instanceof Generator ) {
+ yield from $resultGenerator;
+ }
+ }
+
+ private function withHandle( string $path, callable $action ): mixed {
+ $result = false;
+
if( !array_key_exists( $path, $this->handles ) ) {
- $this->handles[$path] = @fopen( $path, 'rb' ) ?: null;
+ $handle = @fopen( $path, 'rb' );
+
+ if( is_resource( $handle ) ) {
+ $this->handles[$path] = $handle;
+ }
}
- $handle = $this->handles[$path] ?? null;
+ if( array_key_exists( $path, $this->handles ) ) {
+ $result = $action( $this->handles[$path] );
+ }
- return is_resource( $handle ) ? $action( $handle ) : null;
+ return $result;
+ }
+
+ private function withDedicatedHandle(
+ string $path,
+ callable $action
+ ): mixed {
+ $handle = @fopen( $path, 'rb' );
+ $result = false;
+
+ if( is_resource( $handle ) ) {
+ $result = $action( $handle );
+
+ fclose( $handle );
+ }
+
+ return $result;
}
}
git/PackEntryReader.php
callable $readShaBaseFn
): string {
- return $pool->computeString(
+ return $pool->computeStringDedicated(
$packFile,
function( mixed $handle ) use (
callable $streamShaFn
): Generator {
- yield from $pool->streamGenerator(
+ yield from $pool->streamGeneratorDedicated(
$packFile,
function( mixed $handle ) use (
}
} else {
- $base = '';
+ $chunks = [];
foreach( $this->streamEntryGenerator(
$pool,
$packFile,
$offset - $neg,
$depth + 1,
$getSizeShaFn,
$streamShaFn
) as $chunk ) {
- $base .= $chunk;
+ $chunks[] = $chunk;
}
+
+ $base = implode( '', $chunks );
fseek( $handle, $deltaPos );
}
} else {
- $base = '';
+ $chunks = [];
$written = false;
foreach( $streamShaFn( $baseSha, $depth + 1 ) as $chunk ) {
- $base .= $chunk;
+ $chunks[] = $chunk;
$written = true;
}
if( $written ) {
+ $base = implode( '', $chunks );
+
fseek( $handle, $deltaPos );
private function inflate( mixed $handle, int $cap = 0 ): string {
$stream = CompressionStream::createInflater();
+ $chunks = [];
+ $len = 0;
$result = '';
foreach( $stream->stream( $handle ) as $data ) {
- $result .= $data;
-
- if( $cap > 0 && strlen( $result ) >= $cap ) {
- $result = substr( $result, 0, $cap );
+ $chunks[] = $data;
+ $len += strlen( $data );
+ if( $cap > 0 && $len >= $cap ) {
break;
}
+ }
+
+ $result = implode( '', $chunks );
+
+ if( $cap > 0 && strlen( $result ) > $cap ) {
+ $result = substr( $result, 0, $cap );
}
git/PackIndex.php
callable $onFound
): void {
- $byte = ord( $sha[0] );
- $start = $byte === 0 ? 0 : $this->fanoutCache[$byte - 1];
- $end = $this->fanoutCache[$byte];
- $result = 0;
+ $byte = ord( $sha[0] );
+ $start = $byte === 0 ? 0 : $this->fanoutCache[$byte - 1];
+ $end = $this->fanoutCache[$byte];
if( $end > $start ) {
- $low = $start;
- $high = $end - 1;
+ $low = $start;
+ $high = $end - 1;
+ $result = 0;
while( $result === 0 && $low <= $high ) {
}
}
- }
- if( $result !== 0 ) {
- $onFound( $this->packFile, $result );
+ if( $result !== 0 ) {
+ $onFound( $this->packFile, $result );
+ }
}
}
private function readOffset( mixed $handle, int $mid ): int {
$total = $this->fanoutCache[255];
$pos = 1032 + $total * 24 + $mid * 4;
- $result = 0;
fseek( $handle, $pos );
git/PackLocator.php
class PackLocator {
private array $indexes;
+ private array $cache;
public function __construct( string $objectsPath ) {
$this->indexes = [];
+ $this->cache = [];
$packFiles = glob( "{$objectsPath}/pack/*.idx" ) ?: [];
if( strlen( $sha ) === 40 && ctype_xdigit( $sha ) ) {
$binarySha = hex2bin( $sha );
- $found = false;
- $count = count( $this->indexes );
- $index = 0;
- while( !$found && $index < $count ) {
- $this->indexes[$index]->search(
- $pool,
- $binarySha,
- function(
- string $packFile,
- int $offset
- ) use (
- &$found,
- $index,
- $action
- ): void {
- $found = true;
+ if( array_key_exists( $binarySha, $this->cache ) ) {
+ $cached = $this->cache[$binarySha];
- if( $index > 0 ) {
- $temp = $this->indexes[0];
- $this->indexes[0] = $this->indexes[$index];
- $this->indexes[$index] = $temp;
- }
+ unset( $this->cache[$binarySha] );
- $action( $packFile, $offset );
- }
- );
+ $this->cache[$binarySha] = $cached;
- $index++;
+ $action( $cached['pack'], $cached['offset'] );
+ } else {
+ $found = false;
+ $count = count( $this->indexes );
+ $index = 0;
+
+ while( !$found && $index < $count ) {
+ $this->indexes[$index]->search(
+ $pool,
+ $binarySha,
+ function(
+ string $packFile,
+ int $offset
+ ) use (
+ &$found,
+ $index,
+ $action,
+ $binarySha
+ ): void {
+ $found = true;
+
+ if( $index > 0 ) {
+ $temp = $this->indexes[0];
+ $this->indexes[0] = $this->indexes[$index];
+ $this->indexes[$index] = $temp;
+ }
+
+ $this->cache[$binarySha] = [
+ 'pack' => $packFile,
+ 'offset' => $offset
+ ];
+
+ if( count( $this->cache ) > 512 ) {
+ $keyToDrop = array_key_first( $this->cache );
+
+ unset( $this->cache[$keyToDrop] );
+ }
+
+ $action( $packFile, $offset );
+ }
+ );
+
+ $index++;
+ }
}
}
Delta169 lines added, 72 lines removed, 97-line increase