File Coverage

blib/lib/File/BetweenTree.pm
Criterion Covered Total %
statement 135 146 92.4
branch 53 70 75.7
condition 72 128 56.2
subroutine 8 10 80.0
pod 2 5 40.0
total 270 359 75.2


line stmt bran cond sub pod time code
1             package File::BetweenTree;
2              
3             # Copyright (C) 2013 by Mitsuru Yasuda. All rights reserved.
4             # mail bugs, comments and feedback to dsyrtm@gmail.com
5              
6 6     6   183882 use strict;
  6         16  
  6         270  
7 6     6   32 use warnings;
  6         14  
  6         198  
8 6     6   31 use Carp ':DEFAULT', 'confess';
  6         17  
  6         1337  
9 6     6   37 use Fcntl qw(:seek O_RDONLY);
  6         9  
  6         19892  
10              
11             our $VERSION = '1.02';
12              
13             sub new {
14 5     5 1 4327062 my ($class, $file, $recsep) = @_;
15 5         14 my $self;
16              
17 5 50 33     137 $self->{_sep_} = $recsep || ($^O =~ /win32/i) ? "\015\012"
    50          
18             :($^O =~ /mac/i) ? "\015" : "\012";
19 5         21 bless($self, $class);
20 5 50       674 sysopen $self->{fh}, $file, O_RDONLY or return;
21 5         27 binmode $self->{fh};
22              
23 5         30 return $self;
24             }
25             sub search {
26 5     5 1 94 my($self,
27             $my_min,
28             $my_max,
29             $mode,
30             $result_limit,
31             $result_offset,
32             $order_by,
33             $col_sep,
34             $col_num,
35             ) = @_;
36 5         12 $my_max = '' ? $my_min : $my_max;
37 5   50     38 $result_limit ||= 1000;
38 5   50     36 $result_offset ||= 0;
39 5   100     30 $order_by ||= 'ASC';
40 5   50     33 $col_sep ||= ',';
41 5   100     29 $col_num ||= 0;
42 5   100     33 $mode ||= 0;
43              
44 5 50 33     63 croak "0 mode, only the number" if
      66        
45             !$mode && ($my_min =~ /[^\-\.\d]/ || $my_max =~ /[^\-\.\d]/);
46              
47 5         16 my $read_size = 1 << 12;
48              
49             # debug reverse
50 5 100 100     70 if (!$mode && ( $my_min > $my_max )) {
    50 66        
51 1         4 ($my_min, $my_max) = ($my_max, $my_min);
52             }
53             elsif ($mode && ($my_min cmp $my_max) eq 1 ) {
54 0         0 ($my_min, $my_max) = ($my_max, $my_min);
55             }
56              
57 5         53 seek $self->{fh}, 0, SEEK_END;
58 5         20 my $size = tell $self->{fh};
59              
60 5         18 $self->{col_sep} = $col_sep;
61 5         12 $self->{col_num} = $col_num;
62 5         9 my($row,$tip,$str);
63              
64 5         25 my $_var = int ($size / 2);
65 5         12 my $_pos = $_var;
66 5         11 my $_min = 0;
67 5         9 my $_top = 0;# minimum of _max
68 5         11 my $_max = $size;
69 5         9 while (1) {
70 21         70 ($str,$tip,$row)=$self->scoop($_pos);
71 21         50 $_var = int ($_var / 2);
72             # want to approach the my_max
73 21 100       141 if ($mode) {
74 3 100 66     26 if ( ($my_max cmp $str) eq -1 && ( $_max > $_pos ) ) {
    50 33        
75 2         3 $_max = $_pos; $self->{_mon_} .= "<=|";
  2         5  
76             } elsif (($my_max cmp $str) eq 1 && ($_top < $_pos)) {
77 1         2 $_top = $_pos; $self->{_mon_} .= "=>|";
  1         4  
78 0         0 } else { $self->{_mon_} .= " |" }
79             }
80             else {
81 18 100 66     136 if ( ($my_max-$str) < 0 && ( $_max > $_pos ) ) {
    100 66        
82 1         2 $_max = $_pos; $self->{_mon_} .= "<=|";
  1         4  
83             } elsif (($my_max-$str) > 0 && ($_top < $_pos)) {
84 5         8 $_top = $_pos; $self->{_mon_} .= "=>|";
  5         15  
85 12         27 } else { $self->{_mon_} .= " |" }
86             }
87            
88             # adjust the minimum value
89 21 100 100     165 if (
      100        
      66        
90             ( $mode && ($my_min cmp $str) < 1 ) ||
91             (!$mode && $my_min <= $str)
92             ) {
93 10         61 $self->{_mon_} .= "<- $_pos $_var\n";
94 10         34 $_pos -= $_var;
95             }
96             else {
97 11         38 $self->{_mon_} .= "-> $_pos $_var | min\n";
98 11         99 $_min = $_pos; $_pos += $_var;
  11         18  
99              
100             }
101              
102 21 100       64 last if( $_var < $read_size );#
103             }
104              
105             # adjust the maximum value
106 5         14 $_var = int(($_max - $_top) / 2);
107 5         10 $_pos = $_top + $_var;
108 5         10 while (1) {
109 15         41 ($str,$tip,$row)=$self->scoop($_pos);
110 15         98 $_var = int ($_var / 2);
111 15 100 66     157 if (($mode && ($my_max cmp $str) >= 0
      66        
      66        
112             || (!$mode && $my_max >= $str))){
113 10         14 $_pos += $_var;
114 10         32 $self->{_mon_} .= "=>| $_pos $_var\n";
115             }
116             else {
117              
118 5         35 $_max = $_pos + $tip; $_pos -= $_var;
  5         8  
119 5         20 $self->{_mon_} .= "<=| $_pos $_var | max\n";
120             }
121 15 100       42 last if( $_var < $read_size );#
122             }
123              
124             # Locate The Data From Block
125 5         12 my (@z, @_add);
126 0         0 my ($dat, $ll, $_z, $_s, $count, $spare);
127 5         10 my $t = '';
128 5         11 my $_sep = '';
129 5         33 $self->{_mon_} = "roughly_offset_addr:$_min "
130             ."search_length:".($_max-$_min)."\n\n"
131             . $self->{_mon_};
132              
133 5 100       27 if ($order_by =~ /DESC/i) {
134 1         4 my $read_pos = $_max;
135 1         2 while (1) {
136              
137 1         3 $count++;
138              
139 1         1 $read_pos -= $read_size;
140 1 50       5 if ($read_pos < 0) { $read_size += $read_pos; $read_pos = 0 }
  0         0  
  0         0  
141              
142 1         10 seek $self->{fh}, $read_pos, 0;
143 1         16 read $self->{fh}, $dat, $read_size;
144              
145 1         2 $dat .= $t;
146 1         343 @z = reverse split $self->{_sep_}, $dat;
147 1 50 33     34 shift @z if ($count eq 1 && $_max ne $size );
148              
149 1         4 for $ll ( 1 .. $#z ){
150              
151 294         978 $_z = shift @z;
152 294         656 $_s = (split $col_sep, $_z)[$col_num];
153              
154 294 50 33     671 unless (!$mode && $_s =~ /[^\-\.\d]/) {
155 294 50 33     2370 push @_add, $_z if ((
      66        
      66        
156             (!$mode && $my_min <= $_s && $_s <= $my_max ) ||
157             ( $mode && ($my_min cmp $_s) < 1 && ($_s cmp $my_max) < 1 ))
158             && (--$result_offset < 0)
159             && (--$result_limit >= 0));
160              
161 294 50 33     2528 $spare = $_z if(
      33        
      33        
      33        
      33        
162             (!$spare && !$mode && $my_min >= $_s) ||
163             (!$spare && $mode && ($my_min cmp $_s) >= 0) );
164             }
165              
166 294 100       588 last if $result_limit <= 0;
167             }
168 1         4 $t = shift @z;
169 1   50     22 $_s = (split $col_sep, $t)[$col_num] || '';
170              
171 1 0 0     6 push @_add, $t if(($read_pos eq 0)
      33        
172             &&((!$mode && $my_min <= $_s && $_s <= $my_max )
173             ||( $mode && ($my_min cmp $_s) < 1 && ($_s cmp $my_max) < 1 )));
174              
175 1 50 33     30 last if ($read_pos <= $_min || $result_limit <= 0);
176             }
177             }
178              
179             else { # AEC
180              
181 4         16 my $read_pos = $_min;
182 4         7 my $eof = 1;
183 4         6 while (1) {
184              
185 5         12 $count++;
186              
187 5         663 seek $self->{fh}, $read_pos, 0;
188 5         81 read $self->{fh}, $dat, $read_size;
189              
190 5         29 $dat = $t . $_sep . $dat;
191             #$_sep = $dat =~ /($self->{_sep_})$/ ? $1 : '';
192 5 100       106 $_sep = $dat =~ s/($self->{_sep_})$// ? $1 : '';
193              
194 5         643 @z = split $self->{_sep_}, $dat;
195 5 100 100     1284 shift @z if ($count eq 1 && $_min ne 0);
196              
197 5         12 $read_pos += $read_size;
198 5 100       16 $eof = 0 if ($read_pos >= $_max);
199              
200 5         20 for $ll ( $eof .. $#z ){
201              
202 783         1050 $_z = shift @z;
203 783         11698 $_s = (split $col_sep, $_z)[$col_num];
204              
205 783 50 33     3441 unless (!$mode && $_s =~ /[^\-\.\d]/) {
206 783 50 33     5516 push @_add, $_z if ((
      66        
      66        
207             ( $mode && ($my_min cmp $_s) < 1 && ($_s cmp $my_max) < 1) ||
208             (!$mode && $my_min <= $_s && $_s <= $my_max ))
209             && (--$result_offset < 0)
210             && (--$result_limit >= 0));
211              
212 783 100 33     4676 $spare = $_z if( # && $#z >= 0
      66        
      33        
213             ( $mode && ($my_min cmp $_s) >= 0) ) ||
214             (!$mode && $my_min >= $_s);
215             }
216              
217 783 100       1604 last if $result_limit <= 0;
218             }
219 5         14 $t = shift @z;
220              
221             last if
222 5 100 66     44 ($read_pos >= $_max || $read_pos > $size || $result_limit <= 0);
      100        
223             }
224             }
225              
226 5         31 $self->{_mon_} = "file_read:$count " . $self->{_mon_};
227              
228 5 100       20 $spare = '' unless defined $spare;
229 5 100       132 return @_add ? \@_add : ['NULL', $spare];
230              
231             }
232             sub scoop {
233 36     36 0 64 my ($self, $_pos) = @_;
234 36         45 my ($row, $tip);
235 36         284 seek $self->{fh}, $_pos, 0; read $self->{fh}, $row, 1024;
  36         530  
236 36         331 ($tip, $row) = split /$self->{_sep_}/, $row; $tip = length($tip)+1;
  36         96  
237 36         142 my $str = (split($self->{col_sep}, $row))[$self->{col_num}];
238 36         139 return ($str,$tip,$row);
239             }
240             sub mon {
241 0     0 0 0 "Process:\n-----------------------\n".
242             shift->{_mon_};
243             }
244             sub DESTROY {
245 5     5   3418 my $self = shift;
246 5 50       24177 defined $self->{fh} && close $self->{fh};
247             }
248             sub view {
249             #
250             # viewing by specifying a pointer
251             #
252             # Example:
253             # print $bt->view(0, 1024);
254             #
255 0     0 0   my ($self, $offset_byts, $length) = @_;
256 0           my $dat;
257 0           seek $self->{fh}, $offset_byts, 0;
258 0           read $self->{fh}, $dat, $length;
259 0           join "\n", split $self->{_sep_}, $dat;
260             }
261              
262             __END__