| | |
| | fseek($pack, $offset); |
| | - |
| | $byte = ord(fread($pack, 1)); |
| | $type = ($byte >> 4) & 0x07; |
| | $size = $byte & 0x0f; |
| | $shift = 4; |
| | |
| | while ($byte & 0x80) { |
| | $byte = ord(fread($pack, 1)); |
| | $size |= ($byte & 0x7f) << $shift; |
| | $shift += 7; |
| | - } |
| | - |
| | - $types = ['', 'commit', 'tree', 'blob', 'tag', '', 'ofs_delta', 'ref_delta']; |
| | - $typeName = $types[$type] ?? 'unknown'; |
| | - |
| | - if ($type === 6 || $type === 7) { |
| | - fclose($pack); |
| | - return false; |
| | } |
| | - |
| | - $compressed = ''; |
| | - $context = inflate_init(ZLIB_ENCODING_DEFLATE); |
| | - $uncompressed = ''; |
| | - |
| | - while (!feof($pack)) { |
| | - $chunk = fread($pack, 8192); |
| | - $uncompressed .= inflate_add($context, $chunk); |
| | |
| | - if (strlen($uncompressed) >= $size) { |
| | - break; |
| | + // Handle Offset Deltas (Type 6) |
| | + if ($type === 6) { |
| | + $byte = ord(fread($pack, 1)); |
| | + $baseOffset = $byte & 0x7f; |
| | + while ($byte & 0x80) { |
| | + $byte = ord(fread($pack, 1)); |
| | + $baseOffset = (($baseOffset + 1) << 7) | ($byte & 0x7f); |
| | } |
| | + $deltaData = gzuncompress(stream_get_contents($pack)); // Simplified for example |
| | + $baseObj = readPackObject($packFile, $offset - $baseOffset); |
| | + fclose($pack); |
| | + return [ |
| | + 'type' => $baseObj['type'], |
| | + 'content' => applyGitDelta($baseObj['content'], $deltaData) |
| | + ]; |
| | } |
| | |
| | + // Standard Objects (Commit, Tree, Blob) |
| | + $compressed = stream_get_contents($pack); |
| | fclose($pack); |
| | - |
| | - if (strlen($uncompressed) !== $size) { |
| | - return false; |
| | - } |
| | + $uncompressed = @zlib_decode($compressed); |
| | |
| | + $types = ['', 'commit', 'tree', 'blob', 'tag']; |
| | return [ |
| | - 'type' => $typeName, |
| | - 'size' => $size, |
| | + 'type' => $types[$type] ?? 'unknown', |
| | 'content' => $uncompressed |
| | ]; |
| | +} |
| | + |
| | +function applyGitDelta($base, $delta) { |
| | + $pos = 0; |
| | + $readVarInt = function() use (&$delta, &$pos) { |
| | + $res = 0; $shift = 0; |
| | + do { |
| | + $b = ord($delta[$pos++]); |
| | + $res |= ($b & 0x7f) << $shift; |
| | + $shift += 7; |
| | + } while ($b & 0x80); |
| | + return $res; |
| | + }; |
| | + |
| | + $baseSize = $readVarInt(); |
| | + $targetSize = $readVarInt(); |
| | + $res = ''; |
| | + |
| | + while ($pos < strlen($delta)) { |
| | + $opcode = ord($delta[$pos++]); |
| | + if ($opcode & 0x80) { // Copy from base |
| | + $off = 0; $sz = 0; |
| | + if ($opcode & 0x01) $off |= ord($delta[$pos++]); |
| | + if ($opcode & 0x02) $off |= ord($delta[$pos++] ) << 8; |
| | + if ($opcode & 0x04) $off |= ord($delta[$pos++] ) << 16; |
| | + if ($opcode & 0x08) $off |= ord($delta[$pos++] ) << 24; |
| | + if ($opcode & 0x10) $sz |= ord($delta[$pos++]); |
| | + if ($opcode & 0x20) $sz |= ord($delta[$pos++] ) << 8; |
| | + if ($opcode & 0x40) $sz |= ord($delta[$pos++] ) << 16; |
| | + if ($sz === 0) $sz = 0x10000; |
| | + $res .= substr($base, $off, $sz); |
| | + } else { // Insert new data |
| | + $res .= substr($delta, $pos, $opcode); |
| | + $pos += $opcode; |
| | + } |
| | + } |
| | + return $res; |
| | } |
| | |