File Coverage

blib/lib/Convert/Binary/C/Cached.pm
Criterion Covered Total %
statement 172 197 87.3
branch 100 134 74.6
condition 26 36 72.2
subroutine 17 19 89.4
pod 7 7 100.0
total 322 393 81.9


line stmt bran cond sub pod time code
1             ################################################################################
2             #
3             # MODULE: Convert::Binary::C::Cached
4             #
5             ################################################################################
6             #
7             # DESCRIPTION: Cached version of Convert::Binary::C module
8             #
9             ################################################################################
10             #
11             # Copyright (c) 2002-2020 Marcus Holland-Moritz. All rights reserved.
12             # This program is free software; you can redistribute it and/or modify
13             # it under the same terms as Perl itself.
14             #
15             ################################################################################
16              
17             package Convert::Binary::C::Cached;
18              
19 4     4   1330 use strict;
  4         7  
  4         115  
20 4     4   28 use Convert::Binary::C;
  4         8  
  4         96  
21 4     4   21 use Carp;
  4         7  
  4         234  
22 4     4   27 use vars qw( @ISA $VERSION );
  4         7  
  4         9253  
23              
24             @ISA = qw(Convert::Binary::C);
25              
26             $VERSION = '0.82';
27              
28             sub new
29             {
30 930     930 1 4724835 my $class = shift;
31 930         10241 my $self = $class->SUPER::new;
32              
33 930         2523 $self->{cache} = undef;
34 930         1696 $self->{parsed} = 0;
35 930         1690 $self->{uses_cache} = 0;
36              
37 930 100       3013 @_ % 2 and croak "Number of configuration arguments to new must be even";
38              
39 929 100       3368 @_ and $self->configure(@_);
40              
41 926         28437 return $self;
42             }
43              
44             sub configure
45             {
46 1957     1957 1 5592 my $self = shift;
47              
48 1957 100 100     8452 if (@_ < 2 and not defined wantarray) {
49 3 100       354 $^W and carp "Useless use of configure in void context";
50 3         83 return;
51             }
52              
53 1954 100 33     5941 if (@_ == 0) {
    50          
54 952         20066 my $cfg = $self->SUPER::configure;
55 952         2717 $cfg->{Cache} = $self->{cache};
56 952         11298 return $cfg;
57             }
58             elsif (@_ == 1 and $_[0] eq 'Cache') {
59 0         0 return $self->{cache};
60             }
61              
62 1002         1548 my @args;
63              
64 1002 50       2829 if (@_ == 1) {
    50          
65 0         0 @args = @_;
66             }
67             elsif (@_ % 2 == 0) {
68 1002         2042 while (@_) {
69 1237         3891 my %arg = splice @_, 0, 2;
70 1237 100       2623 if (exists $arg{Cache}) {
71 922 50       2562 if ($self->{parsed}) {
    100          
72 0         0 croak 'Cache cannot be configured after parsing';
73             }
74             elsif (ref $arg{Cache}) {
75 1         156 croak 'Cache must be a string value, not a reference';
76             }
77             else {
78 921 50       1960 if (defined $arg{Cache}) {
79 921         1275 my @missing;
80 921         1734 eval { require Data::Dumper };
  921         6420  
81 921 100       8826 $@ and push @missing, 'Data::Dumper';
82 921         1218 eval { require IO::File };
  921         3530  
83 921 100       10702 $@ and push @missing, 'IO::File';
84 921 100       2234 if (@missing) {
85 2 50       309 $^W and carp "Cannot load ", join(' and ', @missing), ", disabling cache";
86 2         24 undef $arg{Cache};
87             }
88             }
89 921         3694 $self->{cache} = $arg{Cache};
90             }
91             }
92 315         990 else { push @args, %arg }
93             }
94             }
95              
96 1001         1772 my $opt = $self;
97              
98 1001 100       2253 if (@args) {
99 109         145 $opt = eval { $self->SUPER::configure(@args) };
  109         1404  
100 109 100       4248 $@ =~ s/\s+at.*?Cached\.pm.*//s, croak $@ if $@;
101             }
102              
103 957         1866 $opt;
104             }
105              
106             sub clean
107             {
108 46     46 1 104 my $self = shift;
109              
110 46         871 delete $self->{$_} for grep !/^(?:|cache|parsed|uses_cache)$/, keys %$self;
111              
112 46         102 $self->{parsed} = 0;
113 46         72 $self->{uses_cache} = 0;
114              
115 46         880 $self->SUPER::clean;
116             }
117              
118             sub clone
119             {
120 3     3 1 169 my $self = shift;
121              
122 3 50       11 unless (defined wantarray) {
123 3 100       166 $^W and carp "Useless use of clone in void context";
124 3         65 return;
125             }
126              
127 0         0 my $clone = $self->SUPER::clone;
128              
129 0         0 for (keys %$self) {
130 0 0       0 if ($_) {
131 0 0       0 $clone->{$_} = ref $_ eq 'ARRAY' ? [@{$self->{$_}}] : $self->{$_};
  0         0  
132             }
133             }
134              
135 0         0 $clone;
136             }
137              
138             sub parse_file
139             {
140 39     39 1 2495 my $self = shift;
141 39         197 my($warn,$error) = $self->__parse('file', $_[0]);
142 38         120 for my $w ( @$warn ) { carp $w }
  0         0  
143 38 100       720 defined $error and croak $error;
144 32 100       149 defined wantarray and return $self;
145             }
146              
147             sub parse
148             {
149 976     976 1 12285 my $self = shift;
150 976         2591 my($warn,$error) = $self->__parse('code', $_[0]);
151 975         2250 for my $w ( @$warn ) { carp $w }
  4         351  
152 975 100       7160 defined $error and croak $error;
153 918 100       3617 defined wantarray and return $self;
154             }
155              
156             sub dependencies
157             {
158 20     20 1 3743 my $self = shift;
159              
160 20 100       439 $self->{parsed} or croak "Call to dependencies without parse data";
161              
162 17 100       43 unless (defined wantarray) {
163 3 100       177 $^W and carp "Useless use of dependencies in void context";
164 3         65 return;
165             }
166              
167 14 100       79 $self->{files} || $self->SUPER::dependencies;
168             }
169              
170             sub __uses_cache
171             {
172 904     904   11107 my $self = shift;
173 904         2513 $self->{uses_cache};
174             }
175              
176             sub __parse
177             {
178 1015     1015   1518 my $self = shift;
179              
180 1015 100       2495 if (defined $self->{cache}) {
181 919 100       2324 $self->{parsed} and croak "Cannot parse more than once for cached objects";
182              
183 918         2595 $self->{$_[0]} = $_[1];
184              
185 918 100       2113 if ($self->__can_use_cache) {
186 35         97 my @WARN;
187             {
188 35     0   67 local $SIG{__WARN__} = sub { push @WARN, $_[0] };
  35         302  
  0         0  
189 35         90 eval { $self->SUPER::parse_file($self->{cache}) };
  35         100988  
190             }
191 35 100 66     269 unless ($@ or @WARN) {
192 18         53 $self->{parsed} = 1;
193 18         34 $self->{uses_cache} = 1;
194 18         85 return;
195             }
196 17         50 $self->clean;
197             }
198             }
199              
200 996         3406 $self->{parsed} = 1;
201              
202 996         2015 my(@warnings, $error);
203             {
204 996     4   1394 local $SIG{__WARN__} = sub { push @warnings, $_[0] };
  996         6193  
  4         37  
205              
206 996 100       3079 if ($_[0] eq 'file') {
207 23         47 eval { $self->SUPER::parse_file($_[1]) };
  23         493902  
208             }
209             else {
210 973         1662 eval { $self->SUPER::parse($_[1]) };
  973         158007  
211             }
212             }
213              
214 996 100       4095 if ($@) {
215 63         86 $error = $@;
216 63         441 $error =~ s/\s+at.*?Cached\.pm.*//s;
217             }
218             else {
219 933 100       4341 defined $self->{cache} and $self->__save_cache;
220             }
221              
222 995         4894 for (@warnings) { s/\s+at.*?Cached\.pm.*//s }
  4         22  
223              
224 995         4264 (\@warnings, $error);
225             }
226              
227             sub __can_use_cache
228             {
229 918     918   1515 my $self = shift;
230 918         4016 my $fh = IO::File->new;
231              
232 918 100 66     47314 unless (-e $self->{cache} and -s _) {
233 5 50       19 $ENV{CBCC_DEBUG} and print STDERR "CBCC: cache file '$self->{cache}' doesn't exist or is empty\n";
234 5         31 return 0;
235             }
236              
237 913 50       4805 unless ($fh->open($self->{cache})) {
238 0 0       0 $^W and carp "Cannot open '$self->{cache}': $!";
239 0 0       0 $ENV{CBCC_DEBUG} and print STDERR "CBCC: cannot open cache file '$self->{cache}'\n";
240 0         0 return 0;
241             }
242              
243 913         40060 my @warnings;
244 913         1604 my @config = do {
245 913         1532 my $config;
246 913 50       16172 unless (defined($config = <$fh>)) {
247 0 0       0 $ENV{CBCC_DEBUG} and print STDERR "CBCC: cannot read configuration\n";
248 0         0 return 0;
249             }
250 913 100       7155 unless ($config =~ /^#if\s+0/) {
251 5 50       18 $ENV{CBCC_DEBUG} and print STDERR "CBCC: invalid configuration\n";
252 5         97 return 0;
253             }
254 908         6038 local $/ = $/.'#endif';
255 908         5832 chomp($config = <$fh>);
256 908         35379 $config =~ s/^\*//gms;
257 908     0   7624 local $SIG{__WARN__} = sub { push @warnings, $_[0] };
  0         0  
258 908         126100 eval $config;
259             };
260              
261             # corrupt config
262 908 50 66     3991 if ($@ or @warnings or @config % 2) {
      66        
263 867 50       2918 $ENV{CBCC_DEBUG} and print STDERR "CBCC: broken configuration\n";
264 867         18371 return 0;
265             }
266              
267 41         165 my %config = @config;
268              
269 41 100       146 my $what = exists $self->{code} ? 'code' : 'file';
270              
271 41 100 100     304 unless (exists $config{$what} and
      100        
272             $config{$what} eq $self->{$what} and
273             __reccmp($config{cfg}, $self->configure)) {
274 3 50       9 if ($ENV{CBCC_DEBUG}) {
275 0         0 print STDERR "CBCC: configuration has changed\n";
276 0         0 print STDERR "CBCC: what='$what', \$config{$what}='$config{$what}' \$self->{$what}='$self->{$what}'\n";
277 0         0 my $dump = Data::Dumper->Dump([$config{cfg}, $self->configure], ['config', 'self']);
278 0         0 $dump =~ s/^/CBCC: /mg;
279 0         0 print STDERR $dump;
280             }
281 3         84 return 0;
282             }
283              
284 38         272 while (my($file, $spec) = each %{$config{files}}) {
  7737         24378  
285 7702 50       93951 unless (-e $file) {
286 0 0       0 $ENV{CBCC_DEBUG} and print STDERR "CBCC: file '$file' deleted\n";
287 0         0 return 0;
288             }
289 7702         27931 my($size, $mtime, $ctime) = (stat(_))[7,9,10];
290 7702 100 100     45875 unless ($spec->{size} == $size and
      66        
291             $spec->{mtime} == $mtime and
292             $spec->{ctime} == $ctime) {
293 3 50       10 $ENV{CBCC_DEBUG} and print STDERR "CBCC: size/mtime/ctime of '$file' changed\n";
294 3         90 return 0;
295             }
296             }
297              
298 35         94 $self->{files} = $config{files};
299              
300 35 50       129 $ENV{CBCC_DEBUG} and print STDERR "CBCC: '$self->{cache}' is usable\n";
301 35         1143 return 1;
302             }
303              
304             sub __save_cache
305             {
306 900     900   2131 my $self = shift;
307 900         5195 my $fh = IO::File->new;
308              
309 900 100       33494 $fh->open(">$self->{cache}") or croak "Cannot open '$self->{cache}': $!";
310              
311 899 100       91595 my $what = exists $self->{code} ? 'code' : 'file';
312              
313 899         3565 my $config = Data::Dumper->new([{ $what => $self->{$what},
314             cfg => $self->configure,
315             files => scalar $self->SUPER::dependencies,
316             }], ['*'])->Indent(1)->Dump;
317 899         156141 $config =~ s/[^(]*//;
318 899         22287 $config =~ s/^/*/gms;
319              
320             print $fh "#if 0\n", $config, "#endif\n\n",
321 899         2369 do { local $^W; $self->sourcify({ Context => 1 }) };
  899         3662  
  899         76887  
322             }
323              
324             sub __reccmp
325             {
326 2402     2402   3735 my($ref, $val) = @_;
327              
328 2402 50 66     3993 !defined($ref) && !defined($val) and return 1;
329 2392 50 33     6248 !defined($ref) || !defined($val) and return 0;
330              
331 2392 100       6756 ref $ref or return $ref eq $val;
332              
333 271 100       588 if (ref $ref eq 'ARRAY') {
    50          
334 156 100       321 @$ref == @$val or return 0;
335 155         420 for (0..$#$ref) {
336 1178 50       1982 __reccmp($ref->[$_], $val->[$_]) or return 0;
337             }
338             }
339             elsif (ref $ref eq 'HASH') {
340 115 50       352 keys %$ref == keys %$val or return 0;
341 115         381 for (keys %$ref) {
342 1185 100       2150 __reccmp($ref->{$_}, $val->{$_}) or return 0;
343             }
344             }
345 0         0 else { return 0 }
346              
347 269         695 return 1;
348             }
349              
350             1;
351              
352             __END__