| | |
| | foreach( $objs as $sha => $type ) { |
| | - $data = ''; |
| | - |
| | - $this->slurp( $sha, function( $chunk ) use ( &$data ) { |
| | - $data .= $chunk; |
| | - } ); |
| | - |
| | - $size = strlen( $data ); |
| | + // Obtain uncompressed size from index/header without loading object data. |
| | + $size = $this->getObjectSize( $sha ); |
| | $byte = $type << 4 | $size & 0x0f; |
| | - $size >>= 4; |
| | + $sz = $size >> 4; |
| | $hdr = ''; |
| | |
| | - while( $size > 0 ) { |
| | + while( $sz > 0 ) { |
| | $hdr .= chr( $byte | 0x80 ); |
| | - $byte = $size & 0x7f; |
| | - $size >>= 7; |
| | + $byte = $sz & 0x7f; |
| | + $sz >>= 7; |
| | } |
| | |
| | $hdr .= chr( $byte ); |
| | hash_update( $ctx, $hdr ); |
| | yield $hdr; |
| | |
| | - $body = gzcompress( $data ); |
| | - hash_update( $ctx, $body ); |
| | - yield $body; |
| | + // Stream-compress the object data without ever holding the full raw |
| | + // content in memory. deflate_add() with ZLIB_NO_FLUSH produces |
| | + // compressed output incrementally; we accumulate only the (much |
| | + // smaller) compressed bytes. |
| | + $deflate = deflate_init( ZLIB_ENCODING_DEFLATE ); |
| | + $compBuf = ''; |
| | + |
| | + $this->slurp( $sha, function( $chunk ) use ( $deflate, $ctx, &$compBuf ) { |
| | + $compressed = deflate_add( $deflate, $chunk, ZLIB_NO_FLUSH ); |
| | + |
| | + if( $compressed !== '' ) { |
| | + hash_update( $ctx, $compressed ); |
| | + $compBuf .= $compressed; |
| | + } |
| | + } ); |
| | + |
| | + $final = deflate_add( $deflate, '', ZLIB_FINISH ); |
| | + |
| | + if( $final !== '' ) { |
| | + hash_update( $ctx, $final ); |
| | + $compBuf .= $final; |
| | + } |
| | + |
| | + // Yield the compressed body in 64 KB chunks. |
| | + $pos = 0; |
| | + $len = strlen( $compBuf ); |
| | + |
| | + while( $pos < $len ) { |
| | + yield substr( $compBuf, $pos, 65536 ); |
| | + $pos += 65536; |
| | + } |
| | } |
| | |