File Coverage

blib/lib/Mogstored/FIDStatter.pm
Criterion Covered Total %
statement 66 67 98.5
branch 20 26 76.9
condition 4 7 57.1
subroutine 14 16 87.5
pod 0 8 0.0
total 104 124 83.8


line stmt bran cond sub pod time code
1             package Mogstored::FIDStatter;
2 1     1   52492 use strict;
  1         7  
  1         21  
3 1     1   4 use warnings;
  1         1  
  1         20  
4 1     1   4 use Carp qw(croak);
  1         1  
  1         828  
5              
6             # on_fid => sub { my ($fidid, $size) = @_; ... }
7             # t_stat => sub { my $fid = shift }
8             sub new {
9 3     3 0 4067 my ($class, %opts) = @_;
10 3         8 my $self = bless {}, $class;
11 3         7 foreach (qw(dir from to on_fid t_stat)) {
12 15         29 $self->{$_} = delete $opts{$_};
13             }
14 3 50       7 croak("unknown opts") if %opts;
15 3   50 0   7 $self->{on_fid} ||= sub {};
16 3   50 0   6 $self->{t_stat} ||= sub {};
17 3         20 return $self;
18             }
19              
20             sub run {
21 4     4 0 40260 my $self = shift;
22              
23             # min/max dirs we could possibly care about format: "n/nnn/nnn/"
24 4         11 my $min_dir = dir($self->{from});
25 4         7 my $max_dir = dir($self->{to});
26              
27             # our start/end fid ranges, zero-padded to 25 or so digits, to be
28             # string-comparable, avoiding integer math (this might be a 32-bit
29             # machine, with a 64-bit mogilefsd/clients)
30 4         21 my $min_zpad = zeropad($self->{from});
31 4         8 my $max_zpad = zeropad($self->{to});
32              
33             my $dir_in_range = sub {
34 15     15   20 my $dir = shift; # "n/[nnn/[nnnn/]]"
35 15 100       24 return 0 if max_subdir($dir) lt $min_dir;
36 13 100       22 return 0 if min_subdir($dir) gt $max_dir;
37 12         26 return 1;
38 4         13 };
39              
40             my $file_in_range = sub {
41 508     508   663 my $fid = zeropad(shift);
42 508   66     1586 return $fid ge $min_zpad && $fid le $max_zpad;
43 4         35 };
44              
45             foreach_dentry($self->{dir}, qr/^\d$/, sub {
46 6     6   56 my ($bdir, $dir) = @_;
47 6 100       12 return unless $dir_in_range->("$bdir/");
48              
49             foreach_dentry($dir, qr/^\d{3}$/, sub {
50 3         7 my ($mdir, $dir) = @_;
51 3 50       9 return unless $dir_in_range->("$bdir/$mdir/");
52              
53             foreach_dentry($dir, qr/^\d{3}$/, sub {
54 6         76 my ($tdir, $dir) = @_;
55 6 50       17 return unless $dir_in_range->("$bdir/$mdir/$tdir/");
56              
57             foreach_dentry($dir, qr/^\d+\.fid$/, sub {
58 508         2842 my ($file, $fullfile) = @_;
59 508         1622 my ($fid) = ($file =~ /^0*(\d+)\.fid$/);
60 508 100       788 return unless $file_in_range->($fid);
61              
62 507         1013 $self->{t_stat}->($fid);
63 507         5236 my $size = (stat($fullfile))[9];
64 507 50       1705 $self->{on_fid}->($fid, $size) if $size;
65 6         38 });
66 3         21 });
67 3         20 });
68 4         30 });
69             }
70              
71             sub zeropad {
72 516     516 0 577 my $fid = shift;
73 516         947 return "0"x(25-length($fid)) . $fid;
74             }
75              
76             sub foreach_dentry {
77 16     16 0 29 my ($dir, $re, $code) = @_;
78 16 50       382 opendir(my $dh, $dir) or die "Failed to open $dir: $!";
79 16         557 $code->($_, "$dir/$_") foreach sort grep { /$re/ } readdir($dh);
  555         1603  
80             }
81              
82             # returns directory that a fid will be in
83             # $fid may or may not have leading zeroes.
84             sub dir {
85 8     8 0 10 my $fid = shift;
86 8         28 $fid =~ s!^0*!!;
87 8 100       22 $fid = "0"x(10-length($fid)) . $fid if length($fid) < 10;
88 8         26 my ($b, $mmm, $ttt) = $fid =~ m{^(\d)(\d{3})(\d{3})};
89 8         22 return "$b/$mmm/$ttt/";
90             }
91              
92 15     15 0 26 sub max_subdir { pad_dir($_[0], "999"); }
93 13     13 0 18 sub min_subdir { pad_dir($_[0], "000"); }
94              
95             sub pad_dir {
96 28     28 0 37 my ($dir, $pad) = @_;
97 28 100       47 if (length($dir) == 2) { return "$dir$pad/$pad/" }
  10         43  
98 18 100       25 if (length($dir) == 6) { return "$dir$pad/" }
  6         14  
99 12 50       18 if (length($dir) == 10) { return $dir }
  12         22  
100 0           Carp::confess("how do I pad '$dir' ?");
101             }
102              
103             1;