File Coverage

blib/lib/Tie/Hash/MinPerfHashTwoLevel/OnDisk.pm
Criterion Covered Total %
statement 97 114 85.0
branch 35 54 64.8
condition 11 17 64.7
subroutine 18 24 75.0
pod 5 6 83.3
total 166 215 77.2


line stmt bran cond sub pod time code
1             package Tie::Hash::MinPerfHashTwoLevel::OnDisk;
2 4     4   439265 use strict;
  4         18  
  4         110  
3 4     4   21 use warnings;
  4         8  
  4         178  
4             our $VERSION = '0.14';
5              
6             # this also installs the XS routines we use into our namespace.
7 4     4   1712 use Algorithm::MinPerfHashTwoLevel ( 'hash_with_state', '$DEFAULT_VARIANT', ':flags', 'MAX_VARIANT' );
  4         12  
  4         659  
8 4     4   30 use Exporter qw(import);
  4         8  
  4         177  
9             my %constants;
10             BEGIN {
11 4     4   92 %constants= (
12             MAGIC_STR => "PH2L",
13             #MPH_F_FILTER_UNDEF => (1<<0),
14             #MPH_F_DETERMINISTIC => (1<<1),
15             MPH_F_NO_DEDUPE => (1<<2),
16             MPH_F_VALIDATE => (1<<3),
17             );
18             }
19              
20 4     4   21 use constant \%constants;
  4         7  
  4         252  
21 4     4   24 use Carp;
  4         6  
  4         5720  
22              
23             our %EXPORT_TAGS = (
24             'all' => [ qw(mph2l_tied_hashref mph2l_make_file MAX_VARIANT), sort keys %constants ],
25             'flags' => ['MPH_F_DETERMINISTIC', grep /MPH_F_/, sort keys %constants],
26             'magic' => [grep /MAGIC/, sort keys %constants],
27             );
28              
29             my $scalar_has_slash= scalar(%EXPORT_TAGS)=~m!/!;
30             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
31              
32             our @EXPORT = qw();
33              
34             sub mph2l_tied_hashref {
35 201     201 1 118210 my ($file, %opts)= @_;
36 201         725 tie my %tied, __PACKAGE__, $file, %opts;
37 1         3 return \%tied;
38             }
39              
40             sub mph2l_make_file {
41 1     1 1 621 my ($file,%opts)= @_;
42 1         7 return __PACKAGE__->make_file(file => $file, %opts);
43             }
44              
45             sub mph2l_validate_file {
46 0     0 1 0 my ($file, %opts)= @_;
47 0         0 return __PACKAGE__->validate_file(file => $file, %opts);
48             }
49              
50             sub new {
51 330     330 0 885 my ($class, %opts)= @_;
52              
53 330   100     1387 $opts{flags} ||= 0;
54 330 100       766 $opts{flags} |= MPH_F_VALIDATE if $opts{validate};
55 330         434 my $error;
56 330         23240 my $mount= mount_file($opts{file},$error,$opts{flags});
57 330         1089 my $error_rsv= delete $opts{error_rsv};
58 330 100       756 if ($error_rsv) {
59 63         157 $$error_rsv= $error;
60             }
61 330 100       737 if (!defined($mount)) {
62 203 50       370 if ($error_rsv) {
63 0         0 return;
64             } else {
65 203         1936 die "Failed to mount file '$opts{file}': $error";
66             }
67             }
68 127         290 $opts{mount}= $mount;
69 127         612 return bless \%opts, $class;
70             }
71              
72             sub TIEHASH {
73 267     267   85797 my ($class, $file, %opts)= @_;
74 267         687 return $class->new( file => $file, %opts );
75             }
76              
77             sub FETCH {
78 1804968     1804968   10114638 my ($self, $key)= @_;
79 1804968         2265212 my $value;
80 1804968 50       4533204 fetch_by_key($self->{mount},$key,$value)
81             or return;
82 1804968         4932552 return $value;
83             }
84              
85             sub EXISTS {
86 0     0   0 my ($self, $key)= @_;
87 0         0 return fetch_by_key($self->{mount},$key);
88             }
89              
90             sub FIRSTKEY {
91 189     189   1631753 my ($self)= @_;
92 189         453 $self->{iter_idx}= 0;
93 189         447 return $self->NEXTKEY();
94             }
95              
96             sub NEXTKEY {
97 1353915     1353915   3831387 my ($self, $lastkey)= @_;
98 1353915         3157114 fetch_by_index($self->{mount},$self->{iter_idx}++,my $key);
99 1353915         4746911 return $key;
100             }
101              
102             sub SCALAR {
103 63     63   426 my ($self)= @_;
104 63         196 my $buckets= $self->get_hdr_num_buckets();
105 63 50       186 if ($scalar_has_slash) {
106 0         0 $buckets .= "/" . $buckets;
107             }
108 63         161 return $buckets;
109             }
110              
111             sub UNTIE {
112 0     0   0 my ($self)= @_;
113             }
114              
115             sub DESTROY {
116 127     127   22706659 my ($self)= @_;
117 127 50       3978 unmount_file($self->{mount}) if $self->{mount};
118             }
119              
120             sub STORE {
121 0     0   0 my ($self, $key, $value)= @_;
122 0         0 confess __PACKAGE__ . " is readonly, STORE operations are not supported";
123             }
124              
125             sub DELETE {
126 0     0   0 my ($self, $key)= @_;
127 0         0 confess __PACKAGE__ . " is readonly, DELETE operations are not supported";
128             }
129              
130             sub CLEAR {
131 0     0   0 my ($self)= @_;
132 0         0 confess __PACKAGE__ . " is readonly, CLEAR operations are not supported";
133             }
134              
135             sub make_file {
136 64     64 1 277294 my ($class, %opts)= @_;
137              
138             my $ofile= $opts{file}
139 64 50       265 or die "file is a mandatory option to make_file";
140             my $source_hash= $opts{source_hash}
141 64 50       199 or die "source_hash is a mandatory option to make_file";
142 64 100       180 $opts{comment}= "" unless defined $opts{comment};
143 64 100       204 $opts{variant}= $DEFAULT_VARIANT unless defined $opts{variant};
144            
145 64   100     177 my $comment= $opts{comment}||"";
146 64   50     310 my $debug= $opts{debug} || 0;
147 64         147 my $variant= int($opts{variant});
148 64   66     240 my $deterministic= $opts{canonical} || $opts{deterministic};
149 64         120 delete $opts{canonical};
150 64         109 delete $opts{deterministic};
151              
152             #1234567812345678
153             $opts{seed} = "MinPerfHash2Levl"
154 64 100 66     202 if !defined($opts{seed}) and $deterministic;
155              
156 64   50     217 my $compute_flags= int($opts{compute_flags}||0);
157 64 50       154 $compute_flags += MPH_F_NO_DEDUPE if delete $opts{no_dedupe};
158 64 100       146 $compute_flags += MPH_F_DETERMINISTIC
159             if $deterministic;
160             $compute_flags += MPH_F_FILTER_UNDEF
161 64 50       154 if delete $opts{filter_undef};
162              
163 64 50 33     275 die "Unknown file variant $variant"
164             if $variant > MAX_VARIANT or $variant < 0;
165              
166 64 50       2162 die "comment cannot contain null"
167             if index($comment,"\0") >= 0;
168              
169 64         1232 my $seed= $opts{seed};
170             my $hasher= Algorithm::MinPerfHashTwoLevel->new(
171             debug => $debug,
172             seed => (ref $seed ? $$seed : $seed),
173             variant => $variant,
174             compute_flags => $compute_flags,
175             max_tries => $opts{max_tries},
176 64 100       1548 );
177 64         263 my $buckets= $hasher->compute($source_hash);
178 64         265 my $buf_length= $hasher->{buf_length};
179 64         150 my $state= $hasher->{state};
180 64         1453363 my $buf= packed_xs($variant, $buf_length, $state, $comment, $compute_flags, @$buckets);
181 64 100       87228 $$seed= $hasher->get_seed if ref $seed;
182              
183 64         319 my $tmp_file= "$ofile.$$";
184 64 50       20567 open my $ofh, ">", $tmp_file
185             or die "Failed to open $tmp_file for output";
186 64 50       94847 print $ofh $buf
187             or die "failed to print to '$tmp_file': $!";
188 64 50       2300 close $ofh
189             or die "failed to close '$tmp_file': $!";
190 64 50       2347 rename $tmp_file, $ofile
191             or die "failed to rename '$tmp_file' to '$ofile': $!";
192 64         1257102 return $ofile;
193             }
194              
195             sub validate_file {
196 63     63 1 284525 my ($class, %opts)= @_;
197             my $file= $opts{file}
198 63 50       260 or die "file is a mandatory option to validate_file";
199 63         153 my $verbose= $opts{verbose};
200 63         179 my ($variant,$msg);
201              
202 63         0 my $error_sv;
203 63         232 my $self= $class->new(file => $file, flags => MPH_F_VALIDATE, error_rsv => \$error_sv);
204 63 50       213 if ($self) {
205 63         1081 $msg= sprintf "file '%s' is a valid '%s' file\n"
206             . " variant: %d\n"
207             . " keys: %d\n"
208             . " hash-state: %s\n"
209             . " table checksum: %016x\n"
210             . " string checksum: %016x\n"
211             . " comment: %s"
212             , $file,
213             MAGIC_STR,
214             $self->get_hdr_variant,
215             $self->get_hdr_num_buckets,
216             unpack("H*", $self->get_state),
217             $self->get_hdr_table_checksum,
218             $self->get_hdr_str_buf_checksum,
219             $self->get_comment,
220             ;
221 63         217 $variant = $self->get_hdr_variant;
222             } else {
223 0         0 $msg= $error_sv;
224             }
225 63 50       163 if ($verbose) {
226 0 0       0 if (defined $variant) {
227 0         0 print $msg;
228             } else {
229 0         0 die $msg."\n";
230             }
231             }
232 63         233 return ($variant, $msg);
233             }
234              
235              
236              
237             1;
238             __END__