Dave Jarvis' Repositories

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

Extracts PackContext

AuthorDave Jarvis <email>
Date2026-02-21 20:26:34 GMT-0800
Commit615160a0f6f0f3819edb6f6d477aff96715a93a9
Parentb1ae5b6
git/DeltaDecoder.php
if( !$doneBuffer ) {
if( $state < 2 ) {
- $pos = $offset;
- $found = false;
-
- while( !$found && $pos < $offset + $len ) {
- if( !(ord( $buffer[$pos] ) & 128) ) {
- $found = true;
- }
-
- $pos++;
- }
-
- if( $found ) {
- $offset = $pos;
- $state++;
- } else {
- $doneBuffer = true;
- }
+ $this->advanceToInstructions(
+ $buffer,
+ $offset,
+ $len,
+ $state,
+ $doneBuffer
+ );
} else {
- $op = ord( $buffer[$offset] );
+ yield from $this->processInstruction(
+ $buffer,
+ $offset,
+ $len,
+ $doneBuffer,
+ $isStream,
+ $base,
+ $yieldBuffer
+ );
+ }
+ }
+ }
+ }
- if( $op & 128 ) {
- $need = $this->calculateCopyInstructionSize( $op );
+ if( $yieldBuffer !== '' ) {
+ yield $yieldBuffer;
+ }
+ }
- if( $len < 1 + $need ) {
- $doneBuffer = true;
- }
+ private function advanceToInstructions(
+ string $buffer,
+ int &$offset,
+ int $len,
+ int &$state,
+ bool &$doneBuffer
+ ): void {
+ $pos = $offset;
+ $found = false;
- if( !$doneBuffer ) {
- $off = 0;
- $ln = 0;
- $ptr = $offset + 1;
+ while( !$found && $pos < $offset + $len ) {
+ if( !(ord( $buffer[$pos] ) & 128) ) {
+ $found = true;
+ }
- $this->parseCopyInstruction(
- $op,
- $buffer,
- $ptr,
- $off,
- $ln
- );
+ $pos++;
+ }
- if( $isStream ) {
- $base->seek( $off );
+ if( $found ) {
+ $offset = $pos;
+ $state++;
+ } else {
+ $doneBuffer = true;
+ }
+ }
- $rem = $ln;
+ private function processInstruction(
+ string $buffer,
+ int &$offset,
+ int $len,
+ bool &$doneBuffer,
+ bool $isStream,
+ mixed $base,
+ string &$yieldBuffer
+ ): Generator {
+ $op = ord( $buffer[$offset] );
- while( $rem > 0 ) {
- $slc = $base->read( min( self::CHUNK_SIZE, $rem ) );
+ if( $op & 128 ) {
+ yield from $this->processCopyInstruction(
+ $op,
+ $buffer,
+ $offset,
+ $len,
+ $doneBuffer,
+ $isStream,
+ $base,
+ $yieldBuffer
+ );
+ } else {
+ yield from $this->processInsertInstruction(
+ $op,
+ $buffer,
+ $offset,
+ $len,
+ $doneBuffer,
+ $yieldBuffer
+ );
+ }
+ }
- if( $slc === '' ) {
- $rem = 0;
- } else {
- $yieldBuffer .= $slc;
- $rem -= strlen( $slc );
+ private function processCopyInstruction(
+ int $op,
+ string $buffer,
+ int &$offset,
+ int $len,
+ bool &$doneBuffer,
+ bool $isStream,
+ mixed $base,
+ string &$yieldBuffer
+ ): Generator {
+ $need = $this->calculateCopyInstructionSize( $op );
- if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
- yield $yieldBuffer;
+ if( $len < 1 + $need ) {
+ $doneBuffer = true;
+ }
- $yieldBuffer = '';
- }
- }
- }
- } else {
- $yieldBuffer .= substr( $base, $off, $ln );
+ if( !$doneBuffer ) {
+ $off = 0;
+ $ln = 0;
+ $ptr = $offset + 1;
- if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
- yield $yieldBuffer;
+ $this->parseCopyInstruction( $op, $buffer, $ptr, $off, $ln );
- $yieldBuffer = '';
- }
- }
+ if( $isStream ) {
+ $base->seek( $off );
- $offset += 1 + $need;
- }
- } else {
- $ln = $op & 127;
+ $rem = $ln;
- if( $len < 1 + $ln ) {
- $doneBuffer = true;
- }
+ while( $rem > 0 ) {
+ $slc = $base->read( min( self::CHUNK_SIZE, $rem ) );
- if( !$doneBuffer ) {
- $yieldBuffer .= substr( $buffer, $offset + 1, $ln );
- $offset += 1 + $ln;
+ if( $slc === '' ) {
+ $rem = 0;
+ } else {
+ $yieldBuffer .= $slc;
+ $rem -= strlen( $slc );
- if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
- yield $yieldBuffer;
+ if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
+ yield $yieldBuffer;
- $yieldBuffer = '';
- }
- }
+ $yieldBuffer = '';
}
}
+ }
+ } else {
+ $yieldBuffer .= substr( $base, $off, $ln );
+
+ if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
+ yield $yieldBuffer;
+
+ $yieldBuffer = '';
}
}
+
+ $offset += 1 + $need;
}
+ }
- if( $yieldBuffer !== '' ) {
- yield $yieldBuffer;
+ private function processInsertInstruction(
+ int $op,
+ string $buffer,
+ int &$offset,
+ int $len,
+ bool &$doneBuffer,
+ string &$yieldBuffer
+ ): Generator {
+ $ln = $op & 127;
+
+ if( $len < 1 + $ln ) {
+ $doneBuffer = true;
+ }
+
+ if( !$doneBuffer ) {
+ $yieldBuffer .= substr( $buffer, $offset + 1, $ln );
+ $offset += 1 + $ln;
+
+ if( strlen( $yieldBuffer ) >= self::CHUNK_SIZE ) {
+ yield $yieldBuffer;
+
+ $yieldBuffer = '';
+ }
}
}
$pos = 0;
$this->readDeltaSize( $head, $pos );
+
$result = $this->readDeltaSize( $head, $pos );
}
while( !$done && $pos < $len ) {
- $byte = ord( $data[$pos++] );
- $val |= ($byte & 0x7F) << $shift;
- $done = !($byte & 0x80);
+ $byte = ord( $data[$pos++] );
+ $val |= ($byte & 0x7F) << $shift;
+ $done = !($byte & 0x80);
$shift += 7;
}
git/GitDiff.php
}
+ $stream = $this->buildFullStream(
+ $oldLines,
+ $newLines,
+ $m,
+ $n,
+ $start,
+ $end
+ );
+
+ return $this->formatDiffOutput( $stream );
+ }
+
+ private function buildFullStream(
+ array $oldLines,
+ array $newLines,
+ int $m,
+ int $n,
+ int $start,
+ int $end
+ ): array {
$context = 2;
$limit = 100000;
$stream = [];
-
- $pStart = max( 0, $start - $context );
+ $pStart = max( 0, $start - $context );
for( $i = $pStart; $i < $start; $i++ ) {
$oldSlice = array_slice( $oldLines, $start, $m - $start - $end );
$newSlice = array_slice( $newLines, $start, $n - $start - $end );
- $mid = [];
-
- if( (count( $oldSlice ) * count( $newSlice )) > $limit ) {
- $mid = $this->buildFallbackDiff( $oldSlice, $newSlice, $start );
- } else {
- $ops = $this->computeLCS( $oldSlice, $newSlice );
- $mid = $this->buildDiffStream( $ops, $start );
- }
+ $mid = (count( $oldSlice ) * count( $newSlice )) > $limit
+ ? $this->buildFallbackDiff( $oldSlice, $newSlice, $start )
+ : $this->buildDiffStream(
+ $this->computeLCS( $oldSlice, $newSlice ),
+ $start
+ );
foreach( $mid as $line ) {
$idxO = $m - $end + $i;
$idxN = $n - $end + $i;
+
$stream[] = [
't' => ' ',
'l' => $oldLines[$idxO],
'no' => $idxO + 1,
'nn' => $idxN + 1
];
}
- return $this->formatDiffOutput( $stream );
+ return $stream;
}
for( $i = 0; $i < $n; $i++ ) {
if( $keep[$i] ) {
- $cnt = count( $buffer );
-
- if( $cnt > 0 ) {
- if( $cnt > 5 ) {
- $result[] = [ 't' => 'gap' ];
- } else {
- foreach( $buffer as $bufLine ) {
- $result[] = $bufLine;
- }
- }
-
- $buffer = [];
- }
+ $this->flushBuffer( $result, $buffer );
$result[] = $stream[$i];
} else {
$buffer[] = $stream[$i];
}
}
+
+ $this->flushBuffer( $result, $buffer );
+
+ return $result;
+ }
+ private function flushBuffer( array &$result, array &$buffer ): void {
$cnt = count( $buffer );
}
}
- }
- return $result;
+ $buffer = [];
+ }
}
git/GitPacks.php
require_once __DIR__ . '/DeltaDecoder.php';
require_once __DIR__ . '/PackEntryReader.php';
+require_once __DIR__ . '/PackContext.php';
class GitPacks {
public function __construct( string $objectsPath ) {
$this->manager = new PackStreamManager();
- $this->locator = new PackLocator( $objectsPath );
+ $this->locator = new PackLocator( $this->manager, $objectsPath );
$this->reader = new PackEntryReader( new DeltaDecoder() );
}
public function peek( string $sha, int $len = 12 ): string {
$result = '';
$this->locator->locate(
- $this->manager,
$sha,
function( string $packFile, int $offset ) use ( &$result, $len ): void {
- $result = $this->reader->read(
- $this->manager,
- $packFile,
- $offset,
+ $context = $this->createContext( $packFile, $offset, 0 );
+ $result = $this->reader->read(
+ $context,
$len,
function( string $baseSha, int $cap ): string {
$this->locator->locate(
- $this->manager,
$sha,
function( string $packFile, int $offset ) use ( &$result ): void {
- $size = $this->reader->getSize(
- $this->manager,
- $packFile,
- $offset
- );
+ $context = $this->createContext( $packFile, $offset, 0 );
+ $size = $this->reader->getSize( $context );
if( $size <= self::MAX_RAM ) {
$result = $this->reader->read(
- $this->manager,
- $packFile,
- $offset,
+ $context,
0,
function( string $baseSha, int $cap ): string {
$this->locator->locate(
- $this->manager,
$sha,
function( string $packFile, int $offset ) use (
if( $found ) {
- yield from $this->reader->streamRawCompressed(
- $this->manager,
- $file,
- $off
- );
+ $context = $this->createContext( $file, $off, 0 );
+
+ yield from $this->reader->streamRawCompressed( $context );
}
}
private function streamShaGenerator( string $sha, int $depth ): Generator {
$found = false;
$file = '';
$off = 0;
$this->locator->locate(
- $this->manager,
$sha,
function( string $packFile, int $offset ) use (
if( $found ) {
- yield from $this->reader->streamEntryGenerator(
- $this->manager,
- $file,
- $off,
- $depth,
- function( string $baseSha ): int {
- return $this->getSize( $baseSha );
- },
- function( string $baseSha, int $baseDepth ): Generator {
- yield from $this->streamShaGenerator( $baseSha, $baseDepth );
- }
- );
+ $context = $this->createContext( $file, $off, $depth );
+
+ yield from $this->reader->streamEntryGenerator( $context );
}
}
public function getSize( string $sha ): int {
$result = 0;
$this->locator->locate(
- $this->manager,
$sha,
function( string $packFile, int $offset ) use ( &$result ): void {
- $result = $this->reader->getSize(
- $this->manager,
- $packFile,
- $offset
- );
+ $context = $this->createContext( $packFile, $offset, 0 );
+ $result = $this->reader->getSize( $context );
}
);
return $result;
+ }
+
+ private function createContext(
+ string $packFile,
+ int $offset,
+ int $depth
+ ): PackContext {
+ return new PackContext(
+ $this->manager,
+ $packFile,
+ $offset,
+ $depth,
+ function( string $baseSha ): int {
+ return $this->getSize( $baseSha );
+ },
+ function( string $baseSha, int $baseDepth ): Generator {
+ yield from $this->streamShaGenerator( $baseSha, $baseDepth );
+ }
+ );
}
}
git/PackContext.php
+<?php
+require_once __DIR__ . '/StreamReader.php';
+require_once __DIR__ . '/PackStreamManager.php';
+
+class PackContext {
+ private PackStreamManager $manager;
+ private string $packFile;
+ private int $offset;
+ private int $depth;
+ private Closure $sizeResolver;
+ private Closure $streamResolver;
+
+ public function __construct(
+ PackStreamManager $manager,
+ string $packFile,
+ int $offset,
+ int $depth,
+ Closure $sizeResolver,
+ Closure $streamResolver
+ ) {
+ $this->manager = $manager;
+ $this->packFile = $packFile;
+ $this->offset = $offset;
+ $this->depth = $depth;
+ $this->sizeResolver = $sizeResolver;
+ $this->streamResolver = $streamResolver;
+ }
+
+ public function deriveOffsetContext( int $negativeOffset ): self {
+ return new self(
+ $this->manager,
+ $this->packFile,
+ $this->offset - $negativeOffset,
+ $this->depth,
+ $this->sizeResolver,
+ $this->streamResolver
+ );
+ }
+
+ public function computeInt( callable $callback, int $default ): int {
+ return $this->manager->computeInt(
+ $this->packFile,
+ function( StreamReader $stream ) use ( $callback ): int {
+ return $callback( $stream, $this->offset );
+ },
+ $default
+ );
+ }
+
+ public function computeStringDedicated(
+ callable $callback,
+ string $default
+ ): string {
+ return $this->manager->computeStringDedicated(
+ $this->packFile,
+ function( StreamReader $stream ) use ( $callback ): string {
+ return $callback( $stream, $this->offset );
+ },
+ $default
+ );
+ }
+
+ public function streamGenerator( callable $callback ): Generator {
+ yield from $this->manager->streamGenerator(
+ $this->packFile,
+ function( StreamReader $stream ) use ( $callback ): Generator {
+ yield from $callback( $stream, $this->offset );
+ }
+ );
+ }
+
+ public function streamGeneratorDedicated( callable $callback ): Generator {
+ yield from $this->manager->streamGeneratorDedicated(
+ $this->packFile,
+ function( StreamReader $stream ) use ( $callback ): Generator {
+ yield from $callback( $stream, $this->offset );
+ }
+ );
+ }
+
+ public function resolveBaseSize( string $sha ): int {
+ return ($this->sizeResolver)( $sha );
+ }
+
+ public function resolveBaseStream( string $sha ): Generator {
+ yield from ($this->streamResolver)( $sha, $this->depth + 1 );
+ }
+
+ public function isWithinDepth( int $maxDepth ): bool {
+ return $this->depth < $maxDepth;
+ }
+}
git/PackEntryReader.php
require_once __DIR__ . '/CompressionStream.php';
require_once __DIR__ . '/BufferedReader.php';
+require_once __DIR__ . '/PackContext.php';
class PackEntryReader {
}
- public function getSize(
- PackStreamManager $manager,
- string $packFile,
- int $offset
- ): int {
- $result = $manager->computeInt(
- $packFile,
- function( StreamReader $stream ) use ( $offset ): int {
+ public function getSize( PackContext $context ): int {
+ return $context->computeInt(
+ function( StreamReader $stream, int $offset ): int {
$stream->seek( $offset );
0
);
-
- return $result;
}
public function read(
- PackStreamManager $manager,
- string $packFile,
- int $offset,
+ PackContext $context,
int $cap,
callable $readShaBaseFn
): string {
- $result = $manager->computeStringDedicated(
- $packFile,
- function( StreamReader $stream ) use (
- $offset,
+ return $context->computeStringDedicated(
+ function( StreamReader $stream, int $offset ) use (
$cap,
$readShaBaseFn
): string {
- $result = $this->readWithStream(
+ return $this->readWithStream(
$stream,
$offset,
$cap,
$readShaBaseFn
);
-
- return $result;
},
''
);
-
- return $result;
}
}
- public function streamRawCompressed(
- PackStreamManager $manager,
- string $packFile,
- int $offset
- ): Generator {
- yield from $manager->streamGenerator(
- $packFile,
- function( StreamReader $stream ) use ( $offset ): Generator {
+ public function streamRawCompressed( PackContext $context ): Generator {
+ yield from $context->streamGenerator(
+ function( StreamReader $stream, int $offset ): Generator {
$stream->seek( $offset );
}
- public function streamEntryGenerator(
- PackStreamManager $manager,
- string $packFile,
- int $offset,
- int $depth,
- callable $getSizeShaFn,
- callable $streamShaFn
- ): Generator {
- yield from $manager->streamGeneratorDedicated(
- $packFile,
- function( StreamReader $stream ) use (
- $manager,
- $packFile,
- $offset,
- $depth,
- $getSizeShaFn,
- $streamShaFn
+ public function streamEntryGenerator( PackContext $context ): Generator {
+ yield from $context->streamGeneratorDedicated(
+ function( StreamReader $stream, int $offset ) use (
+ $context
): Generator {
$stream->seek( $offset );
$header = $this->readVarInt( $stream );
$type = $header['byte'] >> 4 & 7;
if( $type === 6 || $type === 7 ) {
yield from $this->streamDeltaObjectGenerator(
$stream,
- $manager,
- $packFile,
- $offset,
- $type,
- $depth,
- $getSizeShaFn,
- $streamShaFn
+ $context,
+ $type
);
} else {
private function streamDeltaObjectGenerator(
StreamReader $stream,
- PackStreamManager $manager,
- string $packFile,
- int $offset,
- int $type,
- int $depth,
- callable $getSizeShaFn,
- callable $streamShaFn
+ PackContext $context,
+ int $type
): Generator {
- if( $depth < self::MAX_DEPTH ) {
+ if( $context->isWithinDepth( self::MAX_DEPTH ) ) {
if( $type === 6 ) {
- $neg = $this->readOffsetDelta( $stream );
- $baseSize = $this->getSize(
- $manager,
- $packFile,
- $offset - $neg
- );
-
- if( $baseSize > self::MAX_BASE_RAM ) {
- $tmpStream = $this->resolveBaseToTempFile(
- $manager,
- $packFile,
- $offset - $neg,
- $depth,
- $getSizeShaFn,
- $streamShaFn
- );
+ yield from $this->processOffsetDelta( $stream, $context );
+ } else {
+ yield from $this->processRefDelta( $stream, $context );
+ }
+ }
+ }
- yield from $this->decoder->applyStreamGenerator(
- $stream,
- $tmpStream
- );
- } else {
- $readShaBaseFn = function(
- string $sha,
- int $cap
- ) use (
- $streamShaFn,
- $depth
- ): string {
- $chunks = [];
+ private function processOffsetDelta(
+ StreamReader $stream,
+ PackContext $context
+ ): Generator {
+ $neg = $this->readOffsetDelta( $stream );
+ $baseCtx = $context->deriveOffsetContext( $neg );
+ $baseSz = $this->getSize( $baseCtx );
- foreach( $streamShaFn( $sha, $depth + 1 ) as $chunk ) {
- $chunks[] = $chunk;
- }
+ if( $baseSz > self::MAX_BASE_RAM ) {
+ $tmp = BufferedReader::createTemp();
- $result = implode( '', $chunks );
+ foreach( $this->streamEntryGenerator( $baseCtx ) as $chunk ) {
+ $tmp->write( $chunk );
+ }
- if( $cap > 0 && strlen( $result ) > $cap ) {
- $result = substr( $result, 0, $cap );
- }
+ $tmp->rewind();
- return $result;
- };
+ yield from $this->decoder->applyStreamGenerator( $stream, $tmp );
+ } else {
+ $base = $this->read(
+ $baseCtx,
+ 0,
+ function( string $sha, int $cap ) use ( $context ): string {
+ return $this->resolveBaseSha( $sha, $cap, $context );
+ }
+ );
- $base = $this->read(
- $manager,
- $packFile,
- $offset - $neg,
- 0,
- $readShaBaseFn
- );
+ yield from $this->decoder->applyStreamGenerator( $stream, $base );
+ }
+ }
- yield from $this->decoder->applyStreamGenerator(
- $stream,
- $base
- );
- }
- } else {
- $baseSha = bin2hex( $stream->read( 20 ) );
- $baseSize = $getSizeShaFn( $baseSha );
+ private function processRefDelta(
+ StreamReader $stream,
+ PackContext $context
+ ): Generator {
+ $baseSha = bin2hex( $stream->read( 20 ) );
+ $baseSize = $context->resolveBaseSize( $baseSha );
- if( $baseSize > self::MAX_BASE_RAM ) {
- $tmpStream = BufferedReader::createTemp();
- $written = false;
+ if( $baseSize > self::MAX_BASE_RAM ) {
+ $tmp = BufferedReader::createTemp();
+ $add = false;
- foreach( $streamShaFn( $baseSha, $depth + 1 ) as $chunk ) {
- $tmpStream->write( $chunk );
+ foreach( $context->resolveBaseStream( $baseSha ) as $chunk ) {
+ $tmp->write( $chunk );
- $written = true;
- }
+ $add = true;
+ }
- if( $written ) {
- $tmpStream->rewind();
+ if( $add ) {
+ $tmp->rewind();
- yield from $this->decoder->applyStreamGenerator(
- $stream,
- $tmpStream
- );
- }
- } else {
- $chunks = [];
- $written = false;
+ yield from $this->decoder->applyStreamGenerator( $stream, $tmp );
+ }
+ } else {
+ $chunks = [];
+ $add = false;
- foreach( $streamShaFn( $baseSha, $depth + 1 ) as $chunk ) {
- $chunks[] = $chunk;
- $written = true;
- }
+ foreach( $context->resolveBaseStream( $baseSha ) as $chunk ) {
+ $chunks[] = $chunk;
+ $add = true;
+ }
- if( $written ) {
- $base = implode( '', $chunks );
+ if( $add ) {
+ $base = implode( '', $chunks );
- yield from $this->decoder->applyStreamGenerator(
- $stream,
- $base
- );
- }
- }
+ yield from $this->decoder->applyStreamGenerator( $stream, $base );
}
}
}
- private function resolveBaseToTempFile(
- PackStreamManager $manager,
- string $packFile,
- int $baseOffset,
- int $depth,
- callable $getSizeShaFn,
- callable $streamShaFn
- ): StreamReader {
- $result = BufferedReader::createTemp();
+ private function resolveBaseSha(
+ string $sha,
+ int $cap,
+ PackContext $context
+ ): string {
+ $chunks = [];
- foreach( $this->streamEntryGenerator(
- $manager,
- $packFile,
- $baseOffset,
- $depth + 1,
- $getSizeShaFn,
- $streamShaFn
- ) as $chunk ) {
- $result->write( $chunk );
+ foreach( $context->resolveBaseStream( $sha ) as $chunk ) {
+ $chunks[] = $chunk;
}
- $result->rewind();
+ $result = implode( '', $chunks );
+
+ if( $cap > 0 && strlen( $result ) > $cap ) {
+ $result = substr( $result, 0, $cap );
+ }
return $result;
git/PackLocator.php
class PackLocator {
- private array $indexes;
- private array $cache;
+ private PackStreamManager $manager;
+ private array $indexes;
+ private array $cache;
- public function __construct( string $objectsPath ) {
+ public function __construct(
+ PackStreamManager $manager,
+ string $objectsPath
+ ) {
+ $this->manager = $manager;
$this->indexes = [];
$this->cache = [];
}
- public function locate(
- PackStreamManager $manager,
- string $sha,
- callable $action
- ): void {
+ public function locate( string $sha, callable $action ): void {
if( strlen( $sha ) === 40 && ctype_xdigit( $sha ) ) {
$binarySha = hex2bin( $sha );
while( !$found && $index < $count ) {
$this->indexes[$index]->search(
- $manager,
+ $this->manager,
$binarySha,
function(
Delta414 lines added, 315 lines removed, 99-line increase