Dave Jarvis' Repositories

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

Adds missing class

AuthorDave Jarvis <email>
Date2026-02-22 22:38:15 GMT-0800
Commit85b3400ee3eeafaa0413d58e923a208e9523d61b
Parent0103b15
git/DeltaDecoder.php
];
- public function apply( string $base, string $delta, int $cap ): string {
+ public function apply(
+ string $base,
+ string $delta,
+ int $cap
+ ): string {
$pos = 0;
+
$this->readDeltaSize( $delta, $pos );
$this->readDeltaSize( $delta, $pos );
$chunks = [];
- $len = strlen( $delta );
+ $len = \strlen( $delta );
$outLen = 0;
-
- while( $pos < $len ) {
- if( $cap > 0 && $outLen >= $cap ) break;
- $op = ord( $delta[$pos++] );
+ while( $pos < $len && ( $cap === 0 || $outLen < $cap ) ) {
+ $op = \ord( $delta[$pos++] );
if( $op & 128 ) {
- $off = $ln = 0;
+ $off = 0;
+ $ln = 0;
- $this->parseCopyInstruction( $op, $delta, $pos, $off, $ln );
+ $this->parseCopyInstruction(
+ $op, $delta, $pos, $off, $ln
+ );
- $chunks[] = substr( $base, $off, $ln );
+ $chunks[] = \substr( $base, $off, $ln );
$outLen += $ln;
} else {
$ln = $op & 127;
- $chunks[] = substr( $delta, $pos, $ln );
+ $chunks[] = \substr( $delta, $pos, $ln );
$outLen += $ln;
$pos += $ln;
}
}
- $result = implode( '', $chunks );
+ $result = \implode( '', $chunks );
- return $cap > 0 && strlen( $result ) > $cap
- ? substr( $result, 0, $cap )
+ return $cap > 0 && \strlen( $result ) > $cap
+ ? \substr( $result, 0, $cap )
: $result;
}
public function applyStreamGenerator(
StreamReader $handle,
mixed $base
): Generator {
- $stream = CompressionStream::createInflater();
+ $stream = new ZlibInflaterStream();
$state = 0;
$buffer = '';
while( $pos < $bufLen ) {
- if( !(ord( $buffer[$pos] ) & 128) ) {
+ if( !(\ord( $buffer[$pos] ) & 128) ) {
$found = true;
$pos++;
}
} else {
- $op = ord( $buffer[$offset] );
+ $op = \ord( $buffer[$offset] );
if( $op & 128 ) {
$need = self::COPY_INSTRUCTION_SIZES[$op & 0x7F];
if( $len < 1 + $need ) {
break;
}
- $off = $ln = 0;
+ $off = 0;
+ $ln = 0;
$ptr = $offset + 1;
- $this->parseCopyInstruction( $op, $buffer, $ptr, $off, $ln );
+ $this->parseCopyInstruction(
+ $op, $buffer, $ptr, $off, $ln
+ );
if( $isStream ) {
$base->seek( $off );
$rem = $ln;
while( $rem > 0 ) {
- $slc = $base->read( min( self::CHUNK_SIZE, $rem ) );
+ $slc = $base->read(
+ \min( self::CHUNK_SIZE, $rem )
+ );
if( $slc === '' ) {
$rem = 0;
} else {
- $slcLen = strlen( $slc );
+ $slcLen = \strlen( $slc );
$yieldBuffer .= $slc;
$yieldBufLen += $slcLen;
}
- $yieldBuffer .= substr( $buffer, $offset + 1, $ln );
+ $yieldBuffer .= \substr( $buffer, $offset + 1, $ln );
$yieldBufLen += $ln;
$offset += 1 + $ln;
}
- public function readDeltaTargetSize( StreamReader $handle, int $type ): int {
+ public function readDeltaTargetSize(
+ StreamReader $handle,
+ int $type
+ ): int {
if( $type === 6 ) {
- $byte = ord( $handle->read( 1 ) );
+ $byte = \ord( $handle->read( 1 ) );
while( $byte & 128 ) {
- $byte = ord( $handle->read( 1 ) );
+ $byte = \ord( $handle->read( 1 ) );
}
} else {
$handle->seek( 20, SEEK_CUR );
}
$head = $this->readInflatedHead( $handle );
-
- if( strlen( $head ) === 0 ) return 0;
+ $pos = 0;
- $pos = 0;
- $this->readDeltaSize( $head, $pos );
+ if( \strlen( $head ) > 0 ) {
+ $this->readDeltaSize( $head, $pos );
+ }
- return $this->readDeltaSize( $head, $pos );
+ return \strlen( $head ) > 0
+ ? $this->readDeltaSize( $head, $pos )
+ : 0;
}
- public function readDeltaBaseSize( StreamReader $handle ): int {
+ public function readDeltaBaseSize(
+ StreamReader $handle
+ ): int {
$head = $this->readInflatedHead( $handle );
-
- if( strlen( $head ) === 0 ) return 0;
-
- $pos = 0;
+ $pos = 0;
- return $this->readDeltaSize( $head, $pos );
+ return \strlen( $head ) > 0
+ ? $this->readDeltaSize( $head, $pos )
+ : 0;
}
- private function readInflatedHead( StreamReader $handle ): string {
- $stream = CompressionStream::createInflater();
+ private function readInflatedHead(
+ StreamReader $handle
+ ): string {
+ $stream = new ZlibInflaterStream();
$head = '';
$try = 0;
foreach( $stream->stream( $handle, 512 ) as $out ) {
$head .= $out;
$try++;
- if( strlen( $head ) >= 32 || $try >= 64 ) {
+ if( \strlen( $head ) >= 32 || $try >= 64 ) {
break;
}
$len = 0;
- ($op & 0x01) ? $off |= ord( $data[$pos++] ) : null;
- ($op & 0x02) ? $off |= ord( $data[$pos++] ) << 8 : null;
- ($op & 0x04) ? $off |= ord( $data[$pos++] ) << 16 : null;
- ($op & 0x08) ? $off |= ord( $data[$pos++] ) << 24 : null;
+ $off |= ($op & 0x01) ? \ord( $data[$pos++] ) : 0;
+ $off |= ($op & 0x02) ? \ord( $data[$pos++] ) << 8 : 0;
+ $off |= ($op & 0x04) ? \ord( $data[$pos++] ) << 16 : 0;
+ $off |= ($op & 0x08) ? \ord( $data[$pos++] ) << 24 : 0;
- ($op & 0x10) ? $len |= ord( $data[$pos++] ) : null;
- ($op & 0x20) ? $len |= ord( $data[$pos++] ) << 8 : null;
- ($op & 0x40) ? $len |= ord( $data[$pos++] ) << 16 : null;
+ $len |= ($op & 0x10) ? \ord( $data[$pos++] ) : 0;
+ $len |= ($op & 0x20) ? \ord( $data[$pos++] ) << 8 : 0;
+ $len |= ($op & 0x40) ? \ord( $data[$pos++] ) << 16 : 0;
$len = $len === 0 ? 0x10000 : $len;
}
- private function readDeltaSize( string $data, int &$pos ): int {
- $len = strlen( $data );
+ private function readDeltaSize(
+ string $data,
+ int &$pos
+ ): int {
+ $len = \strlen( $data );
$val = 0;
$shift = 0;
$done = false;
while( !$done && $pos < $len ) {
- $byte = ord( $data[$pos++] );
+ $byte = \ord( $data[$pos++] );
$val |= ($byte & 0x7F) << $shift;
$done = !($byte & 0x80);
git/GitPackStream.php
+<?php
+require_once __DIR__ . '/StreamReader.php';
+
+class GitPackStream implements StreamReader {
+ private StreamReader $stream;
+
+ public function __construct( StreamReader $stream ) {
+ $this->stream = $stream;
+ }
+
+ public function isOpen(): bool {
+ return $this->stream->isOpen();
+ }
+
+ public function read( int $length ): string {
+ return $this->stream->read( $length );
+ }
+
+ public function seek( int $offset, int $whence = SEEK_SET ): bool {
+ return $this->stream->seek( $offset, $whence );
+ }
+
+ public function tell(): int {
+ return $this->stream->tell();
+ }
+
+ public function eof(): bool {
+ return $this->stream->eof();
+ }
+
+ public function rewind(): void {
+ $this->stream->rewind();
+ }
+
+ public function readVarInt(): array {
+ $data = $this->stream->read( 12 );
+ $byte = isset( $data[0] ) ? \ord( $data[0] ) : 0;
+ $val = $byte & 15;
+ $shft = 4;
+ $fst = $byte;
+ $pos = 1;
+
+ while( $byte & 128 ) {
+ $byte = isset( $data[$pos] )
+ ? \ord( $data[$pos++] )
+ : 0;
+ $val |= ($byte & 127) << $shft;
+ $shft += 7;
+ }
+
+ $rem = \strlen( $data ) - $pos;
+
+ if( $rem > 0 ) {
+ $this->stream->seek( -$rem, SEEK_CUR );
+ }
+
+ return [
+ 'type' => $fst >> 4 & 7,
+ 'size' => $val
+ ];
+ }
+
+ public function readOffsetDelta(): int {
+ $data = $this->stream->read( 12 );
+ $byte = isset( $data[0] ) ? \ord( $data[0] ) : 0;
+ $result = $byte & 127;
+ $pos = 1;
+
+ while( $byte & 128 ) {
+ $byte = isset( $data[$pos] )
+ ? \ord( $data[$pos++] )
+ : 0;
+ $result = ($result + 1) << 7 | $byte & 127;
+ }
+
+ $rem = \strlen( $data ) - $pos;
+
+ if( $rem > 0 ) {
+ $this->stream->seek( -$rem, SEEK_CUR );
+ }
+
+ return $result;
+ }
+}
git/PackEntryReader.php
require_once __DIR__ . '/BufferedReader.php';
require_once __DIR__ . '/PackContext.php';
-
-class PackEntryReader {
- private const MAX_DEPTH = 200;
- private const MAX_BASE_RAM = 8388608;
- private const MAX_CACHE = 1024;
-
- private DeltaDecoder $decoder;
- private array $cache;
-
- public function __construct( DeltaDecoder $decoder ) {
- $this->decoder = $decoder;
- $this->cache = [];
- }
-
- public function getEntryMeta(
- PackContext $context
- ): array {
- return $context->computeArray(
- function(
- StreamReader $stream,
- int $offset
- ): array {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
- $result = [
- 'type' => $hdr['type'],
- 'size' => $hdr['size'],
- ];
-
- if( $hdr['type'] === 6 ) {
- $neg = $this->readOffsetDelta(
- $stream
- );
- $result['baseOffset'] = $offset - $neg;
- } elseif( $hdr['type'] === 7 ) {
- $result['baseSha'] = \bin2hex(
- $stream->read( 20 )
- );
- }
-
- return $result;
- },
- [ 'type' => 0, 'size' => 0 ]
- );
- }
-
- public function getSize( PackContext $context ): int {
- return $context->computeIntDedicated(
- function(
- StreamReader $stream,
- int $offset
- ): int {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
-
- return $hdr['type'] === 6 || $hdr['type'] === 7
- ? $this->decoder->readDeltaTargetSize(
- $stream, $hdr['type']
- )
- : $hdr['size'];
- },
- 0
- );
- }
-
- public function read(
- PackContext $context,
- int $cap,
- callable $readShaBaseFn
- ): string {
- return $context->computeStringDedicated(
- function(
- StreamReader $s,
- int $o
- ) use ( $cap, $readShaBaseFn ): string {
- return $this->readWithStream(
- $s, $o, $cap, $readShaBaseFn
- );
- },
- ''
- );
- }
-
- private function readWithStream(
- StreamReader $stream,
- int $offset,
- int $cap,
- callable $readShaBaseFn
- ): string {
- $result = '';
-
- if( isset( $this->cache[$offset] ) ) {
- $result = $cap > 0
- && \strlen( $this->cache[$offset] ) > $cap
- ? \substr( $this->cache[$offset], 0, $cap )
- : $this->cache[$offset];
- } else {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
- $type = $hdr['type'];
-
- if( $type === 6 ) {
- $neg = $this->readOffsetDelta( $stream );
- $cur = $stream->tell();
- $bData = $this->readWithStream(
- $stream,
- $offset - $neg,
- $cap,
- $readShaBaseFn
- );
-
- $stream->seek( $cur );
-
- $result = $this->decoder->apply(
- $bData,
- $this->inflate( $stream ),
- $cap
- );
- } elseif( $type === 7 ) {
- $sha = \bin2hex( $stream->read( 20 ) );
- $cur = $stream->tell();
- $bas = $readShaBaseFn( $sha, $cap );
-
- $stream->seek( $cur );
-
- $result = $this->decoder->apply(
- $bas,
- $this->inflate( $stream ),
- $cap
- );
- } else {
- $result = $this->inflate( $stream, $cap );
- }
-
- if( $cap === 0 ) {
- $this->cache[$offset] = $result;
-
- if( \count( $this->cache ) > self::MAX_CACHE ) {
- unset(
- $this->cache[
- \array_key_first( $this->cache )
- ]
- );
- }
- }
- }
-
- return $result;
- }
-
- public function streamRawCompressed(
- PackContext $context
- ): Generator {
- yield from $context->streamGenerator(
- function(
- StreamReader $stream,
- int $offset
- ): Generator {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
-
- yield from $hdr['type'] !== 6
- && $hdr['type'] !== 7
- ? CompressionStream::createExtractor()->stream(
- $stream
- )
- : [];
- }
- );
- }
-
- public function streamRawDelta(
- PackContext $context
- ): Generator {
- yield from $context->streamGenerator(
- function(
- StreamReader $stream,
- int $offset
- ): Generator {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
-
- if( $hdr['type'] === 6 ) {
- $this->readOffsetDelta( $stream );
- } elseif( $hdr['type'] === 7 ) {
- $stream->read( 20 );
- }
-
- yield from CompressionStream::createExtractor()
- ->stream( $stream );
- }
- );
- }
-
- public function streamEntryGenerator(
- PackContext $context
- ): Generator {
- yield from $context->streamGeneratorDedicated(
- function(
- StreamReader $stream,
- int $offset
- ) use ( $context ): Generator {
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
-
- yield from $hdr['type'] === 6
- || $hdr['type'] === 7
- ? $this->streamDeltaObjectGenerator(
- $stream,
- $context,
- $hdr['type'],
- $offset
- )
- : CompressionStream::createInflater()->stream(
- $stream
- );
- }
- );
- }
-
- private function readEntryHeader(
- StreamReader $stream,
- int $offset
- ): array {
- $stream->seek( $offset );
-
- $header = $this->readVarInt( $stream );
-
- return [
- 'type' => $header['byte'] >> 4 & 7,
- 'size' => $header['value']
- ];
- }
-
- private function streamDeltaObjectGenerator(
- StreamReader $stream,
- PackContext $context,
- int $type,
- int $offset
- ): Generator {
- $gen = $context->isWithinDepth( self::MAX_DEPTH )
- ? ( $type === 6
- ? $this->processOffsetDelta(
- $stream, $context, $offset
- )
- : $this->processRefDelta( $stream, $context )
- )
- : [];
-
- yield from $gen;
- }
-
- private function readSizeWithStream(
- StreamReader $stream,
- int $offset
- ): int {
- $result = 0;
-
- if( isset( $this->cache[$offset] ) ) {
- $result = \strlen( $this->cache[$offset] );
- } else {
- $cur = $stream->tell();
- $hdr = $this->readEntryHeader(
- $stream, $offset
- );
-
- $result = $hdr['type'] === 6
- || $hdr['type'] === 7
- ? $this->decoder->readDeltaTargetSize(
- $stream, $hdr['type']
- )
- : $hdr['size'];
-
- $stream->seek( $cur );
- }
-
- return $result;
- }
-
- private function processOffsetDelta(
- StreamReader $stream,
- PackContext $context,
- int $offset
- ): Generator {
- $neg = $this->readOffsetDelta( $stream );
- $cur = $stream->tell();
- $baseOff = $offset - $neg;
- $baseSrc = '';
-
- if( isset( $this->cache[$baseOff] ) ) {
- $baseSrc = $this->cache[$baseOff];
- } elseif(
- $this->readSizeWithStream(
- $stream, $baseOff
- ) <= self::MAX_BASE_RAM
- ) {
- $baseSrc = $this->readWithStream(
- $stream,
- $baseOff,
- 0,
- function(
- string $sha,
- int $cap
- ) use ( $context ): string {
- return $this->resolveBaseSha(
- $sha, $cap, $context
- );
- }
- );
- } else {
- $baseCtx = $context->deriveOffsetContext(
- $neg
- );
- [$b, $tmp] = $this->collectBase(
- $this->streamEntryGenerator( $baseCtx )
- );
- $baseSrc = $tmp instanceof BufferedReader
- ? $tmp
- : $b;
- }
-
- $stream->seek( $cur );
-
- yield from $this->decoder->applyStreamGenerator(
- $stream, $baseSrc
- );
- }
-
- private function processRefDelta(
- StreamReader $stream,
- PackContext $context
- ): Generator {
- $baseSha = \bin2hex( $stream->read( 20 ) );
- $cur = $stream->tell();
- $size = $context->resolveBaseSize( $baseSha );
- $baseSrc = '';
-
- if( $size <= self::MAX_BASE_RAM ) {
- $baseSrc = $this->resolveBaseSha(
- $baseSha, 0, $context
- );
- } else {
- [$b, $tmp] = $this->collectBase(
- $context->resolveBaseStream( $baseSha )
- );
- $baseSrc = $tmp instanceof BufferedReader
- ? $tmp
- : $b;
- }
-
- $stream->seek( $cur );
-
- yield from $this->decoder->applyStreamGenerator(
- $stream, $baseSrc
- );
- }
-
- private function collectBase(
- iterable $chunks
- ): array {
- $parts = [];
- $total = 0;
- $tmp = false;
-
- foreach( $chunks as $chunk ) {
- $total += \strlen( $chunk );
-
- if( $tmp instanceof BufferedReader ) {
- $tmp->write( $chunk );
- } elseif( $total > self::MAX_BASE_RAM ) {
- $tmp = new BufferedReader(
- 'php://temp/maxmemory:65536', 'w+b'
- );
-
- foreach( $parts as $part ) {
- $tmp->write( $part );
- }
-
- $tmp->write( $chunk );
- $parts = [];
- } else {
- $parts[] = $chunk;
- }
- }
-
- if( $tmp instanceof BufferedReader ) {
- $tmp->rewind();
- }
-
- return [
- $tmp === false ? \implode( '', $parts ) : '',
- $tmp
- ];
- }
-
- private function resolveBaseSha(
- string $sha,
- int $cap,
- PackContext $context
- ): string {
- $chunks = [];
-
- foreach(
- $context->resolveBaseStream( $sha ) as $chunk
- ) {
- $chunks[] = $chunk;
- }
-
- $result = \implode( '', $chunks );
-
- return $cap > 0 && \strlen( $result ) > $cap
- ? \substr( $result, 0, $cap )
- : $result;
- }
-
- private function readVarInt(
- StreamReader $stream
- ): array {
- $data = $stream->read( 12 );
- $byte = isset( $data[0] ) ? \ord( $data[0] ) : 0;
- $val = $byte & 15;
- $shft = 4;
- $fst = $byte;
- $pos = 1;
-
- while( $byte & 128 ) {
- $byte = isset( $data[$pos] )
- ? \ord( $data[$pos++] )
- : 0;
- $val |= ( $byte & 127 ) << $shft;
- $shft += 7;
- }
-
- $rem = \strlen( $data ) - $pos;
-
- if( $rem > 0 ) {
- $stream->seek( -$rem, SEEK_CUR );
- }
-
- return [ 'value' => $val, 'byte' => $fst ];
- }
-
- private function readOffsetDelta(
- StreamReader $stream
- ): int {
- $data = $stream->read( 12 );
- $byte = isset( $data[0] ) ? \ord( $data[0] ) : 0;
- $result = $byte & 127;
- $pos = 1;
-
- while( $byte & 128 ) {
- $byte = isset( $data[$pos] )
- ? \ord( $data[$pos++] )
- : 0;
- $result = ( $result + 1 ) << 7 | $byte & 127;
- }
-
- $rem = \strlen( $data ) - $pos;
-
- if( $rem > 0 ) {
- $stream->seek( -$rem, SEEK_CUR );
- }
-
- return $result;
- }
-
- private function inflate(
- StreamReader $stream,
- int $cap = 0
- ): string {
- $inflater = CompressionStream::createInflater();
+require_once __DIR__ . '/GitPackStream.php';
+
+class PackEntryReader {
+ private const MAX_DEPTH = 200;
+ private const MAX_BASE_RAM = 8388608;
+ private const MAX_CACHE = 1024;
+
+ private DeltaDecoder $decoder;
+ private array $cache;
+
+ public function __construct( DeltaDecoder $decoder ) {
+ $this->decoder = $decoder;
+ $this->cache = [];
+ }
+
+ public function getEntryMeta( PackContext $context ): array {
+ return $context->computeArray(
+ function( StreamReader $stream, int $offset ): array {
+ $packStream = new GitPackStream( $stream );
+ $packStream->seek( $offset );
+ $hdr = $packStream->readVarInt();
+
+ return [
+ 'type' => $hdr['type'],
+ 'size' => $hdr['size'],
+ 'baseOffset' => $hdr['type'] === 6
+ ? $offset - $packStream->readOffsetDelta()
+ : 0,
+ 'baseSha' => $hdr['type'] === 7
+ ? \bin2hex( $packStream->read( 20 ) )
+ : ''
+ ];
+ },
+ [ 'type' => 0, 'size' => 0 ]
+ );
+ }
+
+ public function getSize( PackContext $context ): int {
+ return $context->computeIntDedicated(
+ function( StreamReader $stream, int $offset ): int {
+ $packStream = new GitPackStream( $stream );
+ $packStream->seek( $offset );
+ $hdr = $packStream->readVarInt();
+
+ return $hdr['type'] === 6 || $hdr['type'] === 7
+ ? $this->decoder->readDeltaTargetSize(
+ $stream, $hdr['type']
+ )
+ : $hdr['size'];
+ },
+ 0
+ );
+ }
+
+ public function read(
+ PackContext $context,
+ int $cap,
+ callable $readShaBaseFn
+ ): string {
+ return $context->computeStringDedicated(
+ function(
+ StreamReader $s,
+ int $o
+ ) use ( $cap, $readShaBaseFn ): string {
+ return $this->readWithStream(
+ new GitPackStream( $s ),
+ $o,
+ $cap,
+ $readShaBaseFn
+ );
+ },
+ ''
+ );
+ }
+
+ private function readWithStream(
+ GitPackStream $stream,
+ int $offset,
+ int $cap,
+ callable $readShaBaseFn
+ ): string {
+ $stream->seek( $offset );
+ $hdr = $stream->readVarInt();
+ $type = $hdr['type'];
+
+ $result = isset( $this->cache[$offset] )
+ ? ( $cap > 0 && \strlen( $this->cache[$offset] ) > $cap
+ ? \substr( $this->cache[$offset], 0, $cap )
+ : $this->cache[$offset] )
+ : ( $type === 6
+ ? $this->readOffsetDeltaContent(
+ $stream, $offset, $cap, $readShaBaseFn
+ )
+ : ( $type === 7
+ ? $this->readRefDeltaContent(
+ $stream, $cap, $readShaBaseFn
+ )
+ : $this->inflate( $stream, $cap ) ) );
+
+ if( $cap === 0 && !isset( $this->cache[$offset] ) ) {
+ $this->cache[$offset] = $result;
+
+ if( \count( $this->cache ) > self::MAX_CACHE ) {
+ unset(
+ $this->cache[\array_key_first( $this->cache )]
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ private function readOffsetDeltaContent(
+ GitPackStream $stream,
+ int $offset,
+ int $cap,
+ callable $readShaBaseFn
+ ): string {
+ $neg = $stream->readOffsetDelta();
+ $cur = $stream->tell();
+ $bData = $this->readWithStream(
+ $stream,
+ $offset - $neg,
+ $cap,
+ $readShaBaseFn
+ );
+
+ $stream->seek( $cur );
+
+ return $this->decoder->apply(
+ $bData,
+ $this->inflate( $stream ),
+ $cap
+ );
+ }
+
+ private function readRefDeltaContent(
+ GitPackStream $stream,
+ int $cap,
+ callable $readShaBaseFn
+ ): string {
+ $sha = \bin2hex( $stream->read( 20 ) );
+ $cur = $stream->tell();
+ $bas = $readShaBaseFn( $sha, $cap );
+
+ $stream->seek( $cur );
+
+ return $this->decoder->apply(
+ $bas,
+ $this->inflate( $stream ),
+ $cap
+ );
+ }
+
+ public function streamRawCompressed(
+ PackContext $context
+ ): Generator {
+ yield from $context->streamGenerator(
+ function( StreamReader $stream, int $offset ): Generator {
+ $packStream = new GitPackStream( $stream );
+ $packStream->seek( $offset );
+ $hdr = $packStream->readVarInt();
+
+ yield from $hdr['type'] !== 6 && $hdr['type'] !== 7
+ ? (new ZlibExtractorStream())->stream( $stream )
+ : [];
+ }
+ );
+ }
+
+ public function streamRawDelta( PackContext $context ): Generator {
+ yield from $context->streamGenerator(
+ function( StreamReader $stream, int $offset ): Generator {
+ $packStream = new GitPackStream( $stream );
+ $packStream->seek( $offset );
+ $hdr = $packStream->readVarInt();
+
+ if( $hdr['type'] === 6 ) {
+ $packStream->readOffsetDelta();
+ } elseif( $hdr['type'] === 7 ) {
+ $packStream->read( 20 );
+ }
+
+ yield from (new ZlibExtractorStream())->stream( $stream );
+ }
+ );
+ }
+
+ public function streamEntryGenerator(
+ PackContext $context
+ ): Generator {
+ yield from $context->streamGeneratorDedicated(
+ function(
+ StreamReader $stream,
+ int $offset
+ ) use ( $context ): Generator {
+ $packStream = new GitPackStream( $stream );
+ $packStream->seek( $offset );
+ $hdr = $packStream->readVarInt();
+
+ yield from $hdr['type'] === 6 || $hdr['type'] === 7
+ ? $this->streamDeltaObjectGenerator(
+ $packStream,
+ $context,
+ $hdr['type'],
+ $offset
+ )
+ : (new ZlibInflaterStream())->stream( $stream );
+ }
+ );
+ }
+
+ private function streamDeltaObjectGenerator(
+ GitPackStream $stream,
+ PackContext $context,
+ int $type,
+ int $offset
+ ): Generator {
+ yield from $context->isWithinDepth( self::MAX_DEPTH )
+ ? ( $type === 6
+ ? $this->processOffsetDelta(
+ $stream, $context, $offset
+ )
+ : $this->processRefDelta( $stream, $context ) )
+ : [];
+ }
+
+ private function readSizeWithStream(
+ GitPackStream $stream,
+ int $offset
+ ): int {
+ $cur = $stream->tell();
+ $stream->seek( $offset );
+ $hdr = $stream->readVarInt();
+
+ $result = isset( $this->cache[$offset] )
+ ? \strlen( $this->cache[$offset] )
+ : ( $hdr['type'] === 6 || $hdr['type'] === 7
+ ? $this->decoder->readDeltaTargetSize(
+ $stream, $hdr['type']
+ )
+ : $hdr['size'] );
+
+ if( !isset( $this->cache[$offset] ) ) {
+ $stream->seek( $cur );
+ }
+
+ return $result;
+ }
+
+ private function processOffsetDelta(
+ GitPackStream $stream,
+ PackContext $context,
+ int $offset
+ ): Generator {
+ $neg = $stream->readOffsetDelta();
+ $cur = $stream->tell();
+ $baseOff = $offset - $neg;
+
+ $baseSrc = isset( $this->cache[$baseOff] )
+ ? $this->cache[$baseOff]
+ : ( $this->readSizeWithStream( $stream, $baseOff )
+ <= self::MAX_BASE_RAM
+ ? $this->readWithStream(
+ $stream,
+ $baseOff,
+ 0,
+ function(
+ string $sha,
+ int $cap
+ ) use ( $context ): string {
+ return $this->resolveBaseSha(
+ $sha, $cap, $context
+ );
+ }
+ )
+ : $this->collectBase(
+ $this->streamEntryGenerator(
+ $context->deriveOffsetContext( $neg )
+ )
+ ) );
+
+ $stream->seek( $cur );
+
+ yield from $this->decoder->applyStreamGenerator(
+ $stream, $baseSrc
+ );
+ }
+
+ private function processRefDelta(
+ GitPackStream $stream,
+ PackContext $context
+ ): Generator {
+ $baseSha = \bin2hex( $stream->read( 20 ) );
+ $cur = $stream->tell();
+ $size = $context->resolveBaseSize( $baseSha );
+
+ $baseSrc = $size <= self::MAX_BASE_RAM
+ ? $this->resolveBaseSha( $baseSha, 0, $context )
+ : $this->collectBase(
+ $context->resolveBaseStream( $baseSha )
+ );
+
+ $stream->seek( $cur );
+
+ yield from $this->decoder->applyStreamGenerator(
+ $stream, $baseSrc
+ );
+ }
+
+ private function collectBase(
+ iterable $chunks
+ ): BufferedReader|string {
+ $parts = [];
+ $total = 0;
+ $tmp = false;
+
+ foreach( $chunks as $chunk ) {
+ $total += \strlen( $chunk );
+
+ if( $tmp instanceof BufferedReader ) {
+ $tmp->write( $chunk );
+ } elseif( $total > self::MAX_BASE_RAM ) {
+ $tmp = new BufferedReader(
+ 'php://temp/maxmemory:65536', 'w+b'
+ );
+
+ foreach( $parts as $part ) {
+ $tmp->write( $part );
+ }
+
+ $tmp->write( $chunk );
+ $parts = [];
+ } else {
+ $parts[] = $chunk;
+ }
+ }
+
+ if( $tmp instanceof BufferedReader ) {
+ $tmp->rewind();
+ }
+
+ return $tmp === false ? \implode( '', $parts ) : $tmp;
+ }
+
+ private function resolveBaseSha(
+ string $sha,
+ int $cap,
+ PackContext $context
+ ): string {
+ $chunks = [];
+
+ foreach(
+ $context->resolveBaseStream( $sha ) as $chunk
+ ) {
+ $chunks[] = $chunk;
+ }
+
+ $result = \implode( '', $chunks );
+
+ return $cap > 0 && \strlen( $result ) > $cap
+ ? \substr( $result, 0, $cap )
+ : $result;
+ }
+
+ private function inflate(
+ StreamReader $stream,
+ int $cap = 0
+ ): string {
+ $inflater = new ZlibInflaterStream();
$chunks = [];
$len = 0;
Delta524 lines added, 525 lines removed, 1-line decrease