File Coverage

blib/lib/DataStore/CAS/FS/DirCodec.pm
Criterion Covered Total %
statement 46 57 80.7
branch 18 36 50.0
condition 6 17 35.2
subroutine 11 14 78.5
pod 5 5 100.0
total 86 129 66.6


line stmt bran cond sub pod time code
1             package DataStore::CAS::FS::DirCodec;
2 8     8   2710 use 5.008;
  8         17  
3 8     8   26 use strict;
  8         8  
  8         112  
4 8     8   20 use warnings;
  8         6  
  8         133  
5 8     8   23 use Carp;
  8         7  
  8         341  
6 8     8   374 use Try::Tiny;
  8         1504  
  8         5305  
7              
8             our $VERSION= '0.010000';
9              
10             our %_Formats= ();
11              
12             # ABSTRACT: Abstract base class for directory encoder/decoders
13              
14              
15             sub load {
16 59     59 1 15923 my $class= shift;
17 59 100       223 my %p= (@_ == 1)? ((ref $_[0] eq 'HASH')? %{$_[0]} : ( file => $_[0] )) : @_;
  1 50       4  
18              
19 59 50       104 defined $p{file} or croak "Missing required attribute 'file'";
20 59 50       158 defined $p{format} or $p{format}= $class->_read_format(\%p);
21              
22             # Once we get the name of the format, we can jump over to the constructor
23             # for the appropriate class
24             my $codec= $_Formats{$p{format}}
25             or croak "Unknown directory format '$p{format}' in ".$p{file}->hash
26 59 50       158 ."\n(be sure to load relevant modules)\n";
27 59         267 return $codec->decode(\%p);
28             }
29              
30              
31             sub put {
32 21     21 1 49142 my ($class, $cas, $format, $entries, $metadata)= @_;
33 21 50 33     111 defined $entries and ref $entries eq 'ARRAY' or croak "entries must be an arrayref";
34 21 50       48 my $codec= $_Formats{$format}
35             or croak "Unknown directory format '$format'"
36             ."\n(be sure to load relevant modules)\n";
37 21         65 my $scalar= $codec->encode($entries, $metadata);
38 21         61 return $cas->put_scalar($scalar);
39             }
40              
41              
42             sub decode {
43 0     0 1 0 (shift)->load(@_);
44             }
45              
46              
47             sub encode {
48 0     0 1 0 croak "Only implemented in subclasses";
49             }
50              
51              
52             sub register_format {
53 21     21 1 37 my ($class, $format, $codec)= @_;
54 21         98 my $dec= $codec->can('decode');
55 21 50 33     151 defined $dec && $dec ne \&decode
56             or croak ref($codec)." must implement 'decode'";
57 21         61 $_Formats{$format}= $codec;
58             }
59              
60              
61             my $_MagicNumber= 'CAS_Dir ';
62              
63 0     0   0 sub _magic_number { $_MagicNumber }
64              
65             sub _calc_header_length {
66 60     60   100 my ($class, $format)= @_;
67             # Length of sprintf("CAS_Dir %02X %s\n", length($format), $format)
68 60         205 return length($format)+length($_MagicNumber)+4;
69             }
70              
71              
72             sub _read_format {
73 62     62   62 my ($class, $params)= @_;
74              
75             # The caller is allowed to pre-load the data so that we don't need to read it here.
76 62         68 my $buf= $params->{data};
77             # If they didn't, we need to load it.
78 62 100       104 if (!defined $params->{data}) {
79             $params->{handle}= $params->{file}->open
80 59 50       175 unless defined $params->{handle};
81 59 50       5473 seek($params->{handle}, 0, 0) or croak "seek: $!";
82 59         233 $class->_readall($params->{handle}, $buf, length($_MagicNumber)+2);
83             }
84              
85             # first 8 bytes are "CAS_Dir "
86             # Next 2 bytes are the length of the format in uppercase ascii hex (limiting format id to 255 characters)
87             substr($buf, 0, length($_MagicNumber)) eq $_MagicNumber
88 62 50       188 or croak "Bad magic number in directory ".$params->{file}->hash;
89 62         144 my $format_len= hex substr($buf, length($_MagicNumber), 2);
90              
91             # Now we know how many additional bytes we need
92 62 100       129 if (!defined $params->{data}) {
93 59         179 $class->_readall($params->{handle}, $buf, 1+$format_len+1, length($buf));
94             }
95              
96             # The byte after that is a space character.
97             # The format id string follows, in exactly $format_len bytes
98             # There is a newline (\n) at the end of the format string which is not part of that count.
99             substr($buf, length($_MagicNumber)+2, 1) eq ' '
100             and substr($buf, length($_MagicNumber)+3+$format_len, 1) eq "\n"
101 62 50 33     355 or croak "Invalid directory encoding in ".$params->{file}->hash;
102 62         200 return substr($buf, length($_MagicNumber)+3, $format_len);
103             }
104              
105              
106             sub _readall {
107 148   100 148   587 my $got= read($_[1], $_[2], $_[3], $_[4]||0);
108 148 50 33     628 return $got if defined $got and $got == $_[3];
109 0           my $count= $_[3];
110 0           while (1) {
111 0 0         if (defined $got) {
112 0 0         croak "unexpected EOF"
113             unless $got > 0;
114 0           $count -= $got;
115             }
116             else {
117             croak "read: $!"
118 0 0 0       unless $!{EINTR} || $!{EAGAIN};
119             }
120 0           $got= read($_[1], $_[2], $count, length $_[2]);
121             }
122 0           1;
123             }
124              
125             1;
126              
127             __END__