File Coverage

blib/lib/Brackup/PositionedChunk.pm
Criterion Covered Total %
statement 79 79 100.0
branch 16 24 66.6
condition 2 3 66.6
subroutine 21 21 100.0
pod 0 12 0.0
total 118 139 84.8


line stmt bran cond sub pod time code
1             package Brackup::PositionedChunk;
2              
3 13     13   200 use strict;
  13         22  
  13         653  
4 13     13   69 use warnings;
  13         56  
  13         432  
5 13     13   68 use Carp qw(croak);
  13         24  
  13         629  
6 13     13   496 use Brackup::Util qw(io_sha1);
  13         28  
  13         566  
7 13     13   17180 use IO::File;
  13         19492  
  13         2610  
8 13     13   15058 use IO::InnerFile;
  13         23717  
  13         2483  
9 13     13   101 use Fcntl qw(SEEK_SET);
  13         30  
  13         1479  
10              
11             use fields (
12 13         97 'file', # the Brackup::File object
13             'offset', # offset within said file
14             'length', # length of data
15             '_raw_digest',
16             '_raw_chunkref',
17 13     13   16112 );
  13         45809  
18              
19             sub new {
20 115     115 0 1486 my ($class, %opts) = @_;
21 115 50       1395 my $self = ref $class ? $class : fields::new($class);
22              
23 115         49353 $self->{file} = delete $opts{'file'}; # Brackup::File object
24 115         432 $self->{offset} = delete $opts{'offset'};
25 115         331 $self->{length} = delete $opts{'length'};
26              
27 115 50       1059 croak("Unknown options: " . join(', ', keys %opts)) if %opts;
28 115 50       1585 croak("offset not numeric") unless $self->{offset} =~ /^\d+$/;
29 115 50       1740 croak("length not numeric") unless $self->{length} =~ /^\d+$/;
30 115         451 return $self;
31             }
32              
33             sub as_string {
34 81     81 0 189 my $self = shift;
35 81         570 return $self->{file}->as_string . "{off=$self->{offset},len=$self->{length}}";
36             }
37              
38             # the original length, pre-encryption
39             sub length {
40 406     406 0 1096 my $self = shift;
41 406         3671 return $self->{length};
42             }
43              
44             sub offset {
45 193     193 0 1302 my $self = shift;
46 193         1836 return $self->{offset};
47             }
48              
49             sub file {
50 917     917 0 2647 my $self = shift;
51 917         10966 return $self->{file};
52             }
53              
54             sub root {
55 342     342 0 2023 my $self = shift;
56 342         1365 return $self->file->root;
57             }
58              
59             sub raw_digest {
60 321     321 0 771 my $self = shift;
61 321   66     7520 return $self->{_raw_digest} ||= $self->_calc_raw_digest;
62             }
63              
64             sub _calc_raw_digest {
65 108     108   214 my $self = shift;
66              
67 108 50       4142 my $n_chunks = $self->{file}->chunks
68             or die "zero chunks?";
69 108 100       496 if ($n_chunks == 1) {
70             # don't calculate this chunk's digest.. it's the same as our
71             # file's digest, since this chunk spans the entire file.
72 77 50       2221 die "ASSERT" unless $self->length == $self->{file}->size;
73 77         1307 return $self->{file}->full_digest;
74             }
75              
76 31         200 my $cache = $self->root->digest_cache;
77 31         202 my $key = $self->cachekey;
78 31         1401 my $dig;
79              
80 31 100       500 if ($dig = $cache->get($key)) {
81 3         42 return $self->{_raw_digest} = $dig;
82             }
83              
84 28         230 $dig = "sha1:" . io_sha1($self->raw_chunkref);
85              
86 28         207 $cache->set($key => $dig);
87              
88 28         590 return $self->{_raw_digest} = $dig;
89             }
90              
91             sub raw_chunkref {
92 138     138 0 249 my $self = shift;
93 138 100       581 if ($self->{_raw_chunkref}) {
94 72         507 $self->{_raw_chunkref}->seek(0, SEEK_SET);
95 72         2252 return $self->{_raw_chunkref};
96             }
97              
98 66         380 my $fullpath = $self->{file}->fullpath;
99 66 50       1138 my $fh = IO::File->new($fullpath, 'r') or die "Failed to open $fullpath: $!";
100 66         16021 binmode($fh);
101              
102 66 50       808 my $ifh = IO::InnerFile->new($fh, $self->{offset}, $self->{length})
103             or die "Failed to create inner file handle for $fullpath: $!\n";
104 66         5720 return $self->{_raw_chunkref} = $ifh;
105             }
106              
107             # useful string for targets to key on. of one of the forms:
108             # ";to="
109             # ";raw"
110             # ";gz" (future)
111             sub inventory_key {
112 311     311 0 1355 my $self = shift;
113 311         5279 my $key = $self->raw_digest;
114 311 100       2649 if (my @rcpts = $self->root->gpg_rcpts) {
115 155         2062 $key .= ";to=@rcpts";
116             } else {
117 156         1174 $key .= ";raw";
118             }
119 311         3110 return $key;
120             }
121              
122             sub forget_chunkref {
123 108     108 0 256 my $self = shift;
124 108         21432 delete $self->{_raw_chunkref};
125             }
126              
127             sub cachekey {
128 88     88 0 182 my $self = shift;
129 88         415 return $self->{file}->cachekey . ";o=$self->{offset};l=$self->{length}";
130             }
131              
132             sub is_entire_file {
133 11     11 0 21 my $self = shift;
134 11         263 return $self->{file}->chunks == 1;
135             }
136              
137             1;