| | $state = 0; |
| | $buffer = ''; |
| | - $done = false; |
| | - $isFile = is_resource( $base ); |
| | - |
| | - while( !$done && !feof( $handle ) ) { |
| | - $chunk = fread( $handle, 8192 ); |
| | - $done = $chunk === false || $chunk === ''; |
| | - |
| | - if( !$done ) { |
| | - $data = $stream->pump( $chunk ); |
| | - |
| | - if( $data !== '' ) { |
| | - $buffer .= $data; |
| | - $doneBuffer = false; |
| | - |
| | - while( !$doneBuffer ) { |
| | - $len = strlen( $buffer ); |
| | - |
| | - if( $len === 0 ) { |
| | - $doneBuffer = true; |
| | - } |
| | - |
| | - if( !$doneBuffer ) { |
| | - if( $state < 2 ) { |
| | - $pos = 0; |
| | - |
| | - while( $pos < $len && (ord( $buffer[$pos] ) & 128) ) { |
| | - $pos++; |
| | - } |
| | - |
| | - if( $pos === $len && (ord( $buffer[$pos - 1] ) & 128) ) { |
| | - $doneBuffer = true; |
| | - } |
| | - |
| | - if( !$doneBuffer ) { |
| | - $buffer = substr( $buffer, $pos + 1 ); |
| | - $state++; |
| | - } |
| | - } else { |
| | - $op = ord( $buffer[0] ); |
| | - |
| | - if( $op & 128 ) { |
| | - $need = $this->getCopyInstructionSize( $op ); |
| | - |
| | - if( $len < 1 + $need ) { |
| | - $doneBuffer = true; |
| | - } |
| | - |
| | - if( !$doneBuffer ) { |
| | - $info = $this->parseCopyInstruction( $op, $buffer, 1 ); |
| | - |
| | - if( $isFile ) { |
| | - fseek( $base, $info['off'] ); |
| | - $rem = $info['len']; |
| | - |
| | - while( $rem > 0 ) { |
| | - $slc = fread( $base, min( 65536, $rem ) ); |
| | - |
| | - if( $slc === false || $slc === '' ) { |
| | - $rem = 0; |
| | - } else { |
| | - yield $slc; |
| | - $rem -= strlen( $slc ); |
| | - } |
| | - } |
| | - } else { |
| | - yield substr( $base, $info['off'], $info['len'] ); |
| | - } |
| | - |
| | - $buffer = substr( $buffer, 1 + $need ); |
| | - } |
| | - } else { |
| | - $ln = $op & 127; |
| | - |
| | - if( $len < 1 + $ln ) { |
| | - $doneBuffer = true; |
| | - } |
| | - |
| | - if( !$doneBuffer ) { |
| | - yield substr( $buffer, 1, $ln ); |
| | - $buffer = substr( $buffer, 1 + $ln ); |
| | - } |
| | - } |
| | - } |
| | - } |
| | - } |
| | - } |
| | - |
| | - $done = $stream->finished(); |
| | - } |
| | - } |
| | - } |
| | - |
| | - private function decompressToString( |
| | - $handle, |
| | - int $cap = 0 |
| | - ): string { |
| | - $stream = CompressionStream::createInflater(); |
| | - $res = ''; |
| | - $done = false; |
| | - |
| | - while( !$done && !feof( $handle ) ) { |
| | - $chunk = fread( $handle, 8192 ); |
| | - $done = $chunk === false || $chunk === ''; |
| | - |
| | - if( !$done ) { |
| | - $data = $stream->pump( $chunk ); |
| | - |
| | - if( $data !== '' ) { |
| | - $res .= $data; |
| | - } |
| | - |
| | - if( $cap > 0 && strlen( $res ) >= $cap ) { |
| | - $res = substr( $res, 0, $cap ); |
| | - $done = true; |
| | - } |
| | - |
| | - if( !$done ) { |
| | - $done = $stream->finished(); |
| | - } |
| | - } |
| | - } |
| | - |
| | - return $res; |
| | - } |
| | - |
| | - private function extractPackedSize( $packPathOrHandle, int $offset ): int { |
| | - $handle = is_resource( $packPathOrHandle ) |
| | - ? $packPathOrHandle |
| | - : $this->getHandle( $packPathOrHandle ); |
| | - $size = 0; |
| | - |
| | - if( $handle ) { |
| | - fseek( $handle, $offset ); |
| | - $header = $this->readVarInt( $handle ); |
| | - $size = $header['value']; |
| | - $type = ($header['byte'] >> 4) & 7; |
| | - |
| | - if( $type === 6 || $type === 7 ) { |
| | - $size = $this->readDeltaTargetSize( $handle, $type ); |
| | - } |
| | - } |
| | - |
| | - return $size; |
| | - } |
| | - |
| | - private function handleOfsDelta( |
| | - $handle, |
| | - int $offset, |
| | - int $size, |
| | - int $cap |
| | - ): string { |
| | - $neg = $this->readOffsetDelta( $handle ); |
| | - $cur = ftell( $handle ); |
| | - $base = $offset - $neg; |
| | - |
| | - fseek( $handle, $base ); |
| | - $bHead = $this->readVarInt( $handle ); |
| | - |
| | - fseek( $handle, $base ); |
| | - $bData = $this->readPackEntry( $handle, $base, $bHead['value'], $cap ); |
| | - |
| | - fseek( $handle, $cur ); |
| | - $rem = min( self::MAX_READ, max( $size * 2, 1048576 ) ); |
| | - $comp = fread( $handle, $rem ); |
| | - $delta = @gzuncompress( $comp ) ?: ''; |
| | - |
| | - return $this->applyDelta( $bData, $delta, $cap ); |
| | - } |
| | - |
| | - private function handleRefDelta( $handle, int $size, int $cap ): string { |
| | - $sha = bin2hex( fread( $handle, 20 ) ); |
| | - $bas = $cap > 0 ? $this->peek( $sha, $cap ) : $this->read( $sha ); |
| | - $rem = min( self::MAX_READ, max( $size * 2, 1048576 ) ); |
| | - $cmp = fread( $handle, $rem ); |
| | - $del = @gzuncompress( $cmp ) ?: ''; |
| | - |
| | - return $this->applyDelta( $bas, $del, $cap ); |
| | - } |
| | - |
| | - private function applyDelta( string $base, string $delta, int $cap ): string { |
| | - $pos = 0; |
| | - $res = $this->readDeltaSize( $delta, $pos ); |
| | - $pos += $res['used']; |
| | - $res = $this->readDeltaSize( $delta, $pos ); |
| | - $pos += $res['used']; |
| | - |
| | - $out = ''; |
| | - $len = strlen( $delta ); |
| | - $done = false; |
| | - |
| | - while( !$done && $pos < $len ) { |
| | - if( $cap > 0 && strlen( $out ) >= $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']; |
| | - } else { |
| | - $ln = $op & 127; |
| | - $out .= substr( $delta, $pos, $ln ); |
| | - $pos += $ln; |
| | - } |
| | - } |
| | - } |
| | - |
| | - return $out; |
| | - } |
| | - |
| | - private function parseCopyInstruction( |
| | - int $op, |
| | - string $data, |
| | - int $pos |
| | - ): array { |
| | - $off = 0; |
| | - $len = 0; |
| | - $ptr = $pos; |
| | - |
| | - if( $op & 0x01 ) { |
| | - $off |= ord( $data[$ptr++] ); |
| | - } |
| | - |
| | - if( $op & 0x02 ) { |
| | - $off |= ord( $data[$ptr++] ) << 8; |
| | - } |
| | - |
| | - if( $op & 0x04 ) { |
| | - $off |= ord( $data[$ptr++] ) << 16; |
| | - } |
| | - |
| | - if( $op & 0x08 ) { |
| | - $off |= ord( $data[$ptr++] ) << 24; |
| | - } |
| | - |
| | - if( $op & 0x10 ) { |
| | - $len |= ord( $data[$ptr++] ); |
| | - } |
| | - |
| | - if( $op & 0x20 ) { |
| | - $len |= ord( $data[$ptr++] ) << 8; |
| | - } |
| | - |
| | - if( $op & 0x40 ) { |
| | - $len |= ord( $data[$ptr++] ) << 16; |
| | - } |
| | - |
| | - return [ |
| | - 'off' => $off, |
| | - 'len' => $len === 0 ? 0x10000 : $len, |
| | - 'used' => $ptr - $pos |
| | - ]; |
| | - } |
| | - |
| | - private function getCopyInstructionSize( int $op ): int { |
| | - $c = $op & 0x7F; |
| | - $c = $c - (($c >> 1) & 0x55); |
| | - $c = (($c >> 2) & 0x33) + ($c & 0x33); |
| | - $c = (($c >> 4) + $c) & 0x0F; |
| | - |
| | - return $c; |
| | - } |
| | - |
| | - private function readVarInt( $handle ): array { |
| | - $byte = ord( fread( $handle, 1 ) ); |
| | - $val = $byte & 15; |
| | - $shft = 4; |
| | - $fst = $byte; |
| | - |
| | - while( $byte & 128 ) { |
| | - $byte = ord( fread( $handle, 1 ) ); |
| | - $val |= (($byte & 127) << $shft); |
| | - $shft += 7; |
| | - } |
| | - |
| | - return [ 'value' => $val, 'byte' => $fst ]; |
| | - } |
| | - |
| | - private function readOffsetDelta( $handle ): int { |
| | - $byte = ord( fread( $handle, 1 ) ); |
| | - $neg = $byte & 127; |
| | - |
| | - while( $byte & 128 ) { |
| | - $byte = ord( fread( $handle, 1 ) ); |
| | - $neg = (($neg + 1) << 7) | ($byte & 127); |
| | - } |
| | - |
| | - return $neg; |
| | - } |
| | - |
| | - private function readDeltaTargetSize( $handle, int $type ): int { |
| | - if( $type === 6 ) { |
| | - $b = ord( fread( $handle, 1 ) ); |
| | - |
| | - while( $b & 128 ) { |
| | - $b = ord( fread( $handle, 1 ) ); |
| | - } |
| | - } else { |
| | - fseek( $handle, 20, SEEK_CUR ); |
| | - } |
| | - |
| | - $stream = CompressionStream::createInflater(); |
| | - $head = ''; |
| | - $try = 0; |
| | - $done = false; |
| | - |
| | - while( !$done && !feof( $handle ) && strlen( $head ) < 32 && $try < 64 ) { |
| | - $chunk = fread( $handle, 512 ); |
| | - $done = $chunk === false || $chunk === ''; |
| | - |
| | - if( !$done ) { |
| | - $out = $stream->pump( $chunk ); |
| | - |
| | - if( $out !== '' ) { |
| | - $head .= $out; |
| | - } |
| | - |
| | - $done = $stream->finished(); |
| | - $try++; |
| | - } |
| | - } |
| | - |
| | - $pos = 0; |
| | - $result = 0; |
| | - |
| | - if( strlen( $head ) > 0 ) { |
| | - $res = $this->readDeltaSize( $head, $pos ); |
| | - $pos += $res['used']; |
| | - $res = $this->readDeltaSize( $head, $pos ); |
| | - |
| | - $result = $res['val']; |
| | - } |
| | - |
| | - return $result; |
| | + $isFile = is_resource( $base ); |
| | + |
| | + foreach( $stream->stream( $handle ) as $data ) { |
| | + $buffer .= $data; |
| | + $doneBuffer = false; |
| | + |
| | + while( !$doneBuffer ) { |
| | + $len = strlen( $buffer ); |
| | + |
| | + if( $len === 0 ) { |
| | + $doneBuffer = true; |
| | + } |
| | + |
| | + if( !$doneBuffer ) { |
| | + if( $state < 2 ) { |
| | + $pos = 0; |
| | + |
| | + while( $pos < $len && (ord( $buffer[$pos] ) & 128) ) { |
| | + $pos++; |
| | + } |
| | + |
| | + if( $pos === $len && (ord( $buffer[$pos - 1] ) & 128) ) { |
| | + $doneBuffer = true; |
| | + } |
| | + |
| | + if( !$doneBuffer ) { |
| | + $buffer = substr( $buffer, $pos + 1 ); |
| | + $state++; |
| | + } |
| | + } else { |
| | + $op = ord( $buffer[0] ); |
| | + |
| | + if( $op & 128 ) { |
| | + $need = $this->getCopyInstructionSize( $op ); |
| | + |
| | + if( $len < 1 + $need ) { |
| | + $doneBuffer = true; |
| | + } |
| | + |
| | + if( !$doneBuffer ) { |
| | + $info = $this->parseCopyInstruction( $op, $buffer, 1 ); |
| | + |
| | + if( $isFile ) { |
| | + fseek( $base, $info['off'] ); |
| | + $rem = $info['len']; |
| | + |
| | + while( $rem > 0 ) { |
| | + $slc = fread( $base, min( 65536, $rem ) ); |
| | + |
| | + if( $slc === false || $slc === '' ) { |
| | + $rem = 0; |
| | + } else { |
| | + yield $slc; |
| | + $rem -= strlen( $slc ); |
| | + } |
| | + } |
| | + } else { |
| | + yield substr( $base, $info['off'], $info['len'] ); |
| | + } |
| | + |
| | + $buffer = substr( $buffer, 1 + $need ); |
| | + } |
| | + } else { |
| | + $ln = $op & 127; |
| | + |
| | + if( $len < 1 + $ln ) { |
| | + $doneBuffer = true; |
| | + } |
| | + |
| | + if( !$doneBuffer ) { |
| | + yield substr( $buffer, 1, $ln ); |
| | + $buffer = substr( $buffer, 1 + $ln ); |
| | + } |
| | + } |
| | + } |
| | + } |
| | + } |
| | + } |
| | + } |
| | + |
| | + private function decompressToString( |
| | + $handle, |
| | + int $cap = 0 |
| | + ): string { |
| | + $stream = CompressionStream::createInflater(); |
| | + $res = ''; |
| | + |
| | + foreach( $stream->stream( $handle ) as $data ) { |
| | + $res .= $data; |
| | + |
| | + if( $cap > 0 && strlen( $res ) >= $cap ) { |
| | + $res = substr( $res, 0, $cap ); |
| | + |
| | + break; |
| | + } |
| | + } |
| | + |
| | + return $res; |
| | + } |
| | + |
| | + private function readDeltaTargetSize( $handle, int $type ): int { |
| | + if( $type === 6 ) { |
| | + $b = ord( fread( $handle, 1 ) ); |
| | + |
| | + while( $b & 128 ) { |
| | + $b = ord( fread( $handle, 1 ) ); |
| | + } |
| | + } else { |
| | + fseek( $handle, 20, SEEK_CUR ); |
| | + } |
| | + |
| | + $stream = CompressionStream::createInflater(); |
| | + $head = ''; |
| | + $try = 0; |
| | + |
| | + foreach( $stream->stream( $handle, 512 ) as $out ) { |
| | + $head .= $out; |
| | + $try++; |
| | + |
| | + if( strlen( $head ) >= 32 || $try >= 64 ) { |
| | + break; |
| | + } |
| | + } |
| | + |
| | + $pos = 0; |
| | + $result = 0; |
| | + |
| | + if( strlen( $head ) > 0 ) { |
| | + $res = $this->readDeltaSize( $head, $pos ); |
| | + $pos += $res['used']; |
| | + $res = $this->readDeltaSize( $head, $pos ); |
| | + |
| | + $result = $res['val']; |
| | + } |
| | + |
| | + return $result; |
| | + } |
| | + |
| | + private function extractPackedSize( $packPathOrHandle, int $offset ): int { |
| | + $handle = is_resource( $packPathOrHandle ) |
| | + ? $packPathOrHandle |
| | + : $this->getHandle( $packPathOrHandle ); |
| | + $size = 0; |
| | + |
| | + if( $handle ) { |
| | + fseek( $handle, $offset ); |
| | + $header = $this->readVarInt( $handle ); |
| | + $size = $header['value']; |
| | + $type = ($header['byte'] >> 4) & 7; |
| | + |
| | + if( $type === 6 || $type === 7 ) { |
| | + $size = $this->readDeltaTargetSize( $handle, $type ); |
| | + } |
| | + } |
| | + |
| | + return $size; |
| | + } |
| | + |
| | + private function handleOfsDelta( |
| | + $handle, |
| | + int $offset, |
| | + int $size, |
| | + int $cap |
| | + ): string { |
| | + $neg = $this->readOffsetDelta( $handle ); |
| | + $cur = ftell( $handle ); |
| | + $base = $offset - $neg; |
| | + |
| | + fseek( $handle, $base ); |
| | + $bHead = $this->readVarInt( $handle ); |
| | + |
| | + fseek( $handle, $base ); |
| | + $bData = $this->readPackEntry( $handle, $base, $bHead['value'], $cap ); |
| | + |
| | + fseek( $handle, $cur ); |
| | + $rem = min( self::MAX_READ, max( $size * 2, 1048576 ) ); |
| | + $comp = fread( $handle, $rem ); |
| | + $delta = @gzuncompress( $comp ) ?: ''; |
| | + |
| | + return $this->applyDelta( $bData, $delta, $cap ); |
| | + } |
| | + |
| | + private function handleRefDelta( $handle, int $size, int $cap ): string { |
| | + $sha = bin2hex( fread( $handle, 20 ) ); |
| | + $bas = $cap > 0 ? $this->peek( $sha, $cap ) : $this->read( $sha ); |
| | + $rem = min( self::MAX_READ, max( $size * 2, 1048576 ) ); |
| | + $cmp = fread( $handle, $rem ); |
| | + $del = @gzuncompress( $cmp ) ?: ''; |
| | + |
| | + return $this->applyDelta( $bas, $del, $cap ); |
| | + } |
| | + |
| | + private function applyDelta( string $base, string $delta, int $cap ): string { |
| | + $pos = 0; |
| | + $res = $this->readDeltaSize( $delta, $pos ); |
| | + $pos += $res['used']; |
| | + $res = $this->readDeltaSize( $delta, $pos ); |
| | + $pos += $res['used']; |
| | + |
| | + $out = ''; |
| | + $len = strlen( $delta ); |
| | + $done = false; |
| | + |
| | + while( !$done && $pos < $len ) { |
| | + if( $cap > 0 && strlen( $out ) >= $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']; |
| | + } else { |
| | + $ln = $op & 127; |
| | + $out .= substr( $delta, $pos, $ln ); |
| | + $pos += $ln; |
| | + } |
| | + } |
| | + } |
| | + |
| | + return $out; |
| | + } |
| | + |
| | + private function parseCopyInstruction( |
| | + int $op, |
| | + string $data, |
| | + int $pos |
| | + ): array { |
| | + $off = 0; |
| | + $len = 0; |
| | + $ptr = $pos; |
| | + |
| | + if( $op & 0x01 ) { |
| | + $off |= ord( $data[$ptr++] ); |
| | + } |
| | + |
| | + if( $op & 0x02 ) { |
| | + $off |= ord( $data[$ptr++] ) << 8; |
| | + } |
| | + |
| | + if( $op & 0x04 ) { |
| | + $off |= ord( $data[$ptr++] ) << 16; |
| | + } |
| | + |
| | + if( $op & 0x08 ) { |
| | + $off |= ord( $data[$ptr++] ) << 24; |
| | + } |
| | + |
| | + if( $op & 0x10 ) { |
| | + $len |= ord( $data[$ptr++] ); |
| | + } |
| | + |
| | + if( $op & 0x20 ) { |
| | + $len |= ord( $data[$ptr++] ) << 8; |
| | + } |
| | + |
| | + if( $op & 0x40 ) { |
| | + $len |= ord( $data[$ptr++] ) << 16; |
| | + } |
| | + |
| | + return [ |
| | + 'off' => $off, |
| | + 'len' => $len === 0 ? 0x10000 : $len, |
| | + 'used' => $ptr - $pos |
| | + ]; |
| | + } |
| | + |
| | + private function getCopyInstructionSize( int $op ): int { |
| | + $c = $op & 0x7F; |
| | + $c = $c - (($c >> 1) & 0x55); |
| | + $c = (($c >> 2) & 0x33) + ($c & 0x33); |
| | + $c = (($c >> 4) + $c) & 0x0F; |
| | + |
| | + return $c; |
| | + } |
| | + |
| | + private function readVarInt( $handle ): array { |
| | + $byte = ord( fread( $handle, 1 ) ); |
| | + $val = $byte & 15; |
| | + $shft = 4; |
| | + $fst = $byte; |
| | + |
| | + while( $byte & 128 ) { |
| | + $byte = ord( fread( $handle, 1 ) ); |
| | + $val |= (($byte & 127) << $shft); |
| | + $shft += 7; |
| | + } |
| | + |
| | + return [ 'value' => $val, 'byte' => $fst ]; |
| | + } |
| | + |
| | + private function readOffsetDelta( $handle ): int { |
| | + $byte = ord( fread( $handle, 1 ) ); |
| | + $neg = $byte & 127; |
| | + |
| | + while( $byte & 128 ) { |
| | + $byte = ord( fread( $handle, 1 ) ); |
| | + $neg = (($neg + 1) << 7) | ($byte & 127); |
| | + } |
| | + |
| | + return $neg; |
| | } |
| | |