1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
|
package Git::PurePerl::Pack::WithoutIndex;
use Moose;
use MooseX::StrictConstructor;
use namespace::autoclean;
extends 'Git::PurePerl::Pack';
has 'offsets' => ( is => 'rw', isa => 'HashRef', required => 0 );
my @TYPES = ( 'none', 'commit', 'tree', 'blob', 'tag', '', 'ofs_delta',
'ref_delta' );
sub create_index {
my ($self) = @_;
my $index_filename = $self->filename;
$index_filename =~ s/\.pack/.idx/;
my $index_fh = IO::File->new("> $index_filename") || die $!;
my $iod = IO::Digest->new( $index_fh, 'SHA' );
my $offsets = $self->create_index_offsets;
my @fan_out_table;
foreach my $sha1 ( sort keys %$offsets ) {
my $offset = $offsets->{$sha1};
my $slot = unpack( 'C', pack( 'H*', $sha1 ) );
$fan_out_table[$slot]++;
}
foreach my $i ( 0 .. 255 ) {
$index_fh->print( pack( 'N', $fan_out_table[$i] || 0 ) ) || die $!;
$fan_out_table[ $i + 1 ] += $fan_out_table[$i] || 0;
}
foreach my $sha1 ( sort keys %$offsets ) {
my $offset = $offsets->{$sha1};
$index_fh->print( pack( 'N', $offset ) ) || die $!;
$index_fh->print( pack( 'H*', $sha1 ) ) || die $!;
}
# read the pack checksum from the end of the pack file
my $size = -s $self->filename;
my $fh = $self->fh;
$fh->seek( $size - 20, 0 ) || die $!;
my $read = $fh->read( my $pack_sha1, 20 ) || die $!;
$index_fh->print($pack_sha1) || die $!;
$index_fh->print( $iod->digest ) || die $!;
$index_fh->close() || die $!;
}
sub create_index_offsets {
my ($self) = @_;
my $fh = $self->fh;
$fh->read( my $signature, 4 );
$fh->read( my $version, 4 );
$version = unpack( 'N', $version );
$fh->read( my $objects, 4 );
$objects = unpack( 'N', $objects );
my %offsets;
$self->offsets( \%offsets );
foreach my $i ( 1 .. $objects ) {
my $offset = $fh->tell || die "Error telling filehandle: $!";
my $obj_offset = $offset;
$fh->read( my $c, 1 ) || die "Error reading from pack: $!";
$c = unpack( 'C', $c ) || die $!;
$offset++;
my $size = ( $c & 0xf );
my $type_number = ( $c >> 4 ) & 7;
my $type = $TYPES[$type_number]
|| confess
"invalid type $type_number at offset $offset, size $size";
my $shift = 4;
while ( ( $c & 0x80 ) != 0 ) {
$fh->read( $c, 1 ) || die $!;
$c = unpack( 'C', $c ) || die $!;
$offset++;
$size |= ( ( $c & 0x7f ) << $shift );
$shift += 7;
}
my $content;
if ( $type eq 'ofs_delta' || $type eq 'ref_delta' ) {
( $type, $size, $content )
= $self->unpack_deltified( $type, $offset, $obj_offset, $size,
\%offsets );
} elsif ( $type eq 'commit'
|| $type eq 'tree'
|| $type eq 'blob'
|| $type eq 'tag' )
{
$content = $self->read_compressed( $offset, $size );
} else {
confess "invalid type $type";
}
my $raw = $type . ' ' . $size . "\0" . $content;
my $sha1 = Digest::SHA->new;
$sha1->add($raw);
my $sha1_hex = $sha1->hexdigest;
$offsets{$sha1_hex} = $obj_offset;
}
return \%offsets;
}
sub get_object {
my ( $self, $want_sha1 ) = @_;
my $offset = $self->offsets->{$want_sha1};
return unless $offset;
return $self->unpack_object($offset);
}
__PACKAGE__->meta->make_immutable;
|