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