File Coverage

blib/lib/Brackup/CompositeChunk.pm
Criterion Covered Total %
statement 67 68 98.5
branch 9 16 56.2
condition 2 3 66.6
subroutine 15 16 93.7
pod 0 10 0.0
total 93 113 82.3


line stmt bran cond sub pod time code
1             package Brackup::CompositeChunk;
2              
3 13     13   74 use strict;
  13         27  
  13         611  
4 13     13   78 use warnings;
  13         25  
  13         351  
5 13     13   208 use Carp qw(croak);
  13         26  
  13         702  
6 13     13   75 use Fcntl qw(SEEK_SET);
  13         26  
  13         730  
7 13     13   76 use Brackup::Util qw(tempfile_obj io_sha1 io_print_to_fh);
  13         24  
  13         1356  
8              
9             use fields (
10 13         294 'used_up',
11             'max_size',
12             'target',
13             'digest', # memoized
14             'finalized', # if we've written ourselves to the target yet
15             'subchunks', # the chunks this composite chunk is made of
16             'sha1', # Digest::SHA1 object
17             '_chunk_fh', # tempfile file containing the whole composite chunk
18 13     13   72 );
  13         22  
19              
20             sub new {
21 2     2 0 31 my ($class, $root, $target) = @_;
22 2 50       39 my $self = ref $class ? $class : fields::new($class);
23 2         357 $self->{used_up} = 0; # bytes
24 2         5 $self->{finalized} = 0; # false
25 2         14 $self->{max_size} = $root->max_composite_size;
26 2         5 $self->{target} = $target;
27 2         5 $self->{subchunks} = [];
28 2         16 $self->{sha1} = Digest::SHA1->new;
29 2         22 $self->{_chunk_fh} = tempfile_obj();
30 2         3467 return $self;
31             }
32              
33             sub append_little_chunk {
34 11     11 0 21 my ($self, $schunk) = @_;
35 11 50       54 die "ASSERT" if $self->{digest}; # its digest was already requested?
36              
37 11         25 my $from = $self->{used_up};
38 11         50 $self->{used_up} += $schunk->backup_length;
39 11         73 io_print_to_fh($schunk->chunkref, $self->{_chunk_fh}, $self->{sha1});
40 11         29 my $to = $self->{used_up};
41              
42 11         92 $schunk->set_composite_chunk($self, $from, $to);
43 11         20 push @{$self->{subchunks}}, $schunk;
  11         54  
44             }
45              
46             sub digest {
47 17     17 0 37 my $self = shift;
48 17   66     1579 return $self->{digest} ||= "sha1:" . $self->{sha1}->hexdigest;
49             }
50              
51             sub can_fit {
52 9     9 0 45 my ($self, $len) = @_;
53 9         70 return $len <= ($self->{max_size} - $self->{used_up});
54             }
55              
56             # return on success; die on any failure
57             sub finalize {
58 2     2 0 5 my $self = shift;
59 2 50       10 die "ASSERT" if $self->{finalized}++;
60              
61 2 50       16 $self->{target}->store_chunk($self)
62             or die "chunk storage of composite chunk failed.\n";
63              
64 2         5 foreach my $schunk (@{$self->{subchunks}}) {
  2         15  
65 11         163 $self->{target}->add_to_inventory($schunk->pchunk => $schunk);
66             }
67              
68 2         17 $self->forget_chunkref;
69              
70 2         553 return 1;
71             }
72              
73             sub stored_chunk_from_dup_internal_raw {
74 22     22 0 53 my ($self, $pchunk) = @_;
75 22         102 my $ikey = $pchunk->inventory_key;
76 22         53 foreach my $schunk (@{$self->{subchunks}}) {
  22         138  
77 64 100       485 next unless $schunk->pchunk->inventory_key eq $ikey;
78             # match! found a duplicate within ourselves
79 4         54 return $schunk->clone_but_for_pchunk($pchunk);
80             }
81 18         132 return undef;
82             }
83              
84             #
85             # make this duck-typed like a StoredChunk, so targets can store it
86             *backup_digest = \&digest;
87             sub backup_length {
88 19     19 0 75 my $self = shift;
89 19         78 return $self->{used_up};
90             }
91             # return handle to data
92             sub chunkref {
93 2     2 0 6 my $self = shift;
94 2 50       32 croak "ASSERT: _chunk_fh not opened" unless $self->{_chunk_fh}->opened;
95 2         113 seek($self->{_chunk_fh}, 0, SEEK_SET);
96 2         16 return $self->{_chunk_fh};
97             }
98             sub inventory_value {
99 0     0 0 0 die "ASSERT: don't expect this to be called";
100             }
101             # called when chunk data not needed anymore
102             sub forget_chunkref {
103 2     2 0 8 my $self = shift;
104 2 50       27 if ($self->{_chunk_fh}) {
105 2 50       46 die "ASSERT: used_up: $self->{used_up}, size: " . -s $self->{_chunk_fh}->filename
106             unless -s $self->{_chunk_fh}->filename == $self->{used_up};
107 2         110 close $self->{_chunk_fh};
108 2         17 delete $self->{_chunk_fh}; # also deletes the temp file
109             }
110             }
111             #
112              
113             1;