File Coverage

blib/lib/File/Rsync/Mirror/Recentfile/Done.pm
Criterion Covered Total %
statement 142 165 86.0
branch 64 86 74.4
condition 32 36 88.8
subroutine 16 16 100.0
pod 5 5 100.0
total 259 308 84.0


line stmt bran cond sub pod time code
1             package File::Rsync::Mirror::Recentfile::Done;
2              
3             # use warnings;
4 7     7   628 use strict;
  7         15  
  7         247  
5              
6 7     7   42 use File::Rsync::Mirror::Recentfile::FakeBigFloat qw(:all);
  7         14  
  7         1223  
7              
8             =encoding utf-8
9              
10             =head1 NAME
11              
12             File::Rsync::Mirror::Recentfile::Done - intervals of already rsynced timespans
13              
14             =cut
15              
16 7     7   46 use version; our $VERSION = qv('0.0.8');
  7         16  
  7         48  
17              
18             =head1 SYNOPSIS
19              
20             my $done = File::Rsync::Mirror::Recentfile::Done->new;
21             $done->register ( $recent_events, [3,4,5,9] ); # registers elements 3-5 and 9
22             my $boolean = $done->covered ( $epoch );
23              
24             =head1 DESCRIPTION
25              
26             Keeping track of already rsynced timespans.
27              
28             =head1 EXPORT
29              
30             No exports.
31              
32             =head1 CONSTRUCTORS
33              
34             =head2 my $obj = CLASS->new(%hash)
35              
36             Constructor. On every argument pair the key is a method name and the
37             value is an argument to that method name.
38              
39             =cut
40              
41             sub new {
42 35     35 1 12353 my($class, @args) = @_;
43 35         155 my $self = bless {}, $class;
44 35         190 while (@args) {
45 0         0 my($method,$arg) = splice @args, 0, 2;
46 0         0 $self->$method($arg);
47             }
48 35         405 return $self;
49             }
50              
51             =head1 ACCESSORS
52              
53             =cut
54              
55             my @accessors;
56              
57             BEGIN {
58 7     7   1908 @accessors = (
59             "__intervals",
60             "_logfile", # undocced: a small yaml dump appended on every change
61             "_rfinterval", # undocced: the interval of the holding rf
62             );
63              
64 7         62 my @pod_lines =
65 7         30 split /\n/, <<'=cut'; push @accessors, grep {s/^=item\s+//} @pod_lines; }
  56         284  
66              
67             =over 4
68              
69             =item verbose
70              
71             Boolean to turn on a bit verbosity.
72              
73             =back
74              
75             =cut
76              
77 7     7   49 use accessors @accessors;
  7         14  
  7         56  
78              
79             =head1 METHODS
80              
81             =head2 $boolean = $obj->covered ( $epoch1, $epoch2 )
82              
83             =head2 $boolean = $obj->covered ( $epoch )
84              
85             The first form returns true if both timestamps $epoch1 and $epoch2 in
86             floating point notation have been registered within one interval,
87             otherwise false.
88              
89             The second form returns true if this timestamp has been registered.
90              
91             =cut
92             sub _is_sorted {
93 3707     3707   5584 my($self,$ivs) = @_;
94 3707         4308 my $Lup;
95 3707         4701 my $is_sorted = 1;
96 3707         7537 for my $i (0..$#$ivs) {
97 4322 100       7383 if (defined $Lup) {
98 615 50       1893 if (_bigfloatge ($ivs->[$i][0],$Lup)) {
99 0         0 warn "Warning (may be harmless): F:R:M:R:Done object contains unsorted internal data";
100 0         0 $DB::single++;
101 0         0 return 0;
102             }
103             }
104 4322         8656 $Lup = $ivs->[$i][0];
105             }
106 3707         6135 return $is_sorted;
107             }
108             sub covered {
109 4351     4351 1 8252 my($self, $epoch_high, $epoch_low) = @_;
110 4351 50       8372 die "Alert: covered() called without or with undefined first argument" unless defined $epoch_high;
111 4351         8612 my $intervals = $self->_intervals;
112 4351 100       9147 return unless @$intervals;
113 3707 100       6633 if (defined $epoch_low) {
114 47 50       314 ($epoch_high,$epoch_low) = ($epoch_low,$epoch_high) if _bigfloatgt($epoch_low,$epoch_high);
115             }
116 3707         6304 my $is_sorted = $self->_is_sorted($intervals);
117 3707         5962 for my $iv (@$intervals) {
118 4318         7272 my($upper,$lower) = @$iv; # may be the same
119 4318 100       6905 if (defined $epoch_low) {
120 57         151 my $goodbound = 0;
121 57         170 for my $e ($epoch_high,$epoch_low) {
122 114 100 100     1266 $goodbound++ if
      100        
      100        
123             $e eq $upper || $e eq $lower || (_bigfloatlt($e,$upper) && _bigfloatgt($e,$lower));
124             }
125 57 100       285 return 1 if $goodbound > 1;
126             } else {
127 4261 100       9410 if ( _bigfloatle ( $epoch_high, $upper ) ) {
    50          
128 4028 100       8420 if ( _bigfloatge ( $epoch_high, $lower )) {
129 1250         3911 return 1; # "between"
130             }
131             } elsif ($is_sorted) {
132 233         960 return 0; # no chance anymore
133             }
134             }
135             }
136 2199         5539 return 0;
137             }
138              
139             =head2 (void) $obj1->merge ( $obj2 )
140              
141             Integrates all intervals in $obj2 into $obj1. Overlapping intervals
142             are conflated/folded/consolidated. Sort order is preserved as decreasing.
143              
144             =cut
145             sub merge {
146 64     64 1 3809 my($self, $other) = @_;
147 64         815 my $intervals = $self->_intervals;
148 64         282 my $ointervals = $other->_intervals;
149 64         356 OTHER: for my $oiv (@$ointervals) {
150 85         190 my $splicepos;
151 85 100       289 if (@$intervals) {
152 77         258 SELF: for my $i (0..$#$intervals) {
153 121         242 my $iv = $intervals->[$i];
154 121 100       381 if ( _bigfloatlt ($oiv->[0],$iv->[1]) ) {
155             # both oiv lower than iv => next
156 78         174 next SELF;
157             }
158 43 100       182 if ( _bigfloatgt ($oiv->[1],$iv->[0]) ) {
159             # both oiv greater than iv => insert
160 33         54 $splicepos = $i;
161 33         61 last SELF;
162             }
163             # larger(left-iv,left-oiv) becomes left, smaller(right-iv,right-oiv) becomes right
164 10         118 $iv->[0] = _bigfloatmax ($oiv->[0],$iv->[0]);
165 10         107 $iv->[1] = _bigfloatmin ($oiv->[1],$iv->[1]);
166 10         71 next OTHER;
167             }
168 67 100       143 unless (defined $splicepos) {
169 34 50       79 if ( _bigfloatlt ($oiv->[0], $intervals->[-1][1]) ) {
170 34         66 $splicepos = @$intervals;
171             } else {
172 0         0 die "Panic: left-oiv[$oiv->[0]] should be smaller than smallest[$intervals->[-1][1]]";
173             }
174             }
175 67         218 splice @$intervals, $splicepos, 0, [@$oiv];
176             } else {
177 8         131 $intervals->[0] = [@$oiv];
178             }
179             }
180             }
181              
182             =head2 (void) $obj->register ( $recent_events_arrayref, $register_arrayref )
183              
184             =head2 (void) $obj->register ( $recent_events_arrayref )
185              
186             The first arrayref is a list of hashes that contain a key called
187             C which is a string looking like a number. The second arrayref
188             is a list if integers which point to elements in the first arrayref to
189             be registered.
190              
191             The second form registers all events in $recent_events_arrayref.
192              
193             =cut
194              
195             sub register {
196 77     77 1 12421 my($self, $re, $reg) = @_;
197 77         708 my $intervals = $self->_intervals;
198 77 50       574 unless ($reg) {
199 0         0 $reg = [0..$#$re];
200             }
201 77         588 REGISTRANT: for my $i (@$reg) {
202 1572         4219 my $logfile = $self->_logfile;
203 1572 50       8283 if ($logfile) {
204 0         0 require YAML::Syck;
205 0 0       0 open my $fh, ">>", $logfile or die "Could not open '$logfile': $!";
206 0 0       0 print $fh YAML::Syck::Dump({
    0          
207             At => "before",
208             Brfinterval => $self->_rfinterval,
209             Ci => $i,
210             ($i>0 ? ("Dre-1" => $re->[$i-1]) : ()),
211             "Dre-0" => $re->[$i],
212             ($i<$#$re ? ("Dre+1" => $re->[$i+1]) : ()),
213             Eintervals => $intervals,
214             });
215             }
216             $self->_register_one
217             ({
218 1572         7300 i => $i,
219             re => $re,
220             intervals => $intervals,
221             });
222 1572 50       4977 if ($logfile) {
223 0         0 require YAML::Syck;
224 0 0       0 open my $fh, ">>", $logfile or die "Could not open '$logfile': $!";
225 0         0 print $fh YAML::Syck::Dump({
226             At => "after",
227             intervals => $intervals,
228             });
229             }
230             }
231             }
232              
233             sub _register_one {
234 1572     1572   2634 my($self, $one) = @_;
235 1572         2201 my($i,$re,$intervals) = @{$one}{qw(i re intervals)};
  1572         3025  
236 1572 50       3677 die sprintf "Panic: illegal i[%d] larger than number of events[%d]", $i, $#$re
237             if $i > $#$re;
238 1572         2946 my $epoch = $re->[$i]{epoch};
239 1572 100       3013 return if $self->covered ( $epoch );
240 1568 100       3178 if (@$intervals) {
241 1548         2178 my $registered = 0;
242 1548         2919 IV: for my $iv (@$intervals) {
243 1782         3161 my($ivhi,$ivlo) = @$iv; # may be the same
244 1782 100 100     6502 if ($i > 0
      100        
      66        
245             && _bigfloatge($re->[$i-1]{epoch}, $ivlo)
246             && _bigfloatle($re->[$i-1]{epoch}, $ivhi)
247             && _bigfloatge($iv->[1],$epoch)
248             ) {
249             # if left neighbor in re belongs to this interval,
250             # then I belong to it too; let us lower the ivlo
251 1506         2459 $iv->[1] = $epoch;
252 1506         2675 $registered++;
253             }
254 1782 100 100     8023 if ($i < $#$re
      100        
      66        
255             && _bigfloatle($re->[$i+1]{epoch}, $ivhi)
256             && _bigfloatge($re->[$i+1]{epoch}, $ivlo)
257             && _bigfloatle($iv->[0],$epoch)
258             ) {
259             # ditto for right neighbor; increase the ivhi
260 42         206 $iv->[0] = $epoch;
261 42         168 $registered++;
262             }
263 1782 100       5467 last IV if $registered>=2;
264             }
265 1548 100       3660 if ($registered == 2) {
    100          
266 21         280 $self->_register_one_fold2
267             (
268             $intervals,
269             $epoch,
270             );
271             } elsif ($registered == 1) {
272 1506         3701 $self->_register_one_fold1 ($intervals);
273             } else {
274 21         214 $self->_register_one_fold0
275             (
276             $intervals,
277             $epoch,
278             );
279             }
280             } else {
281 20         197 $intervals->[0] = [($epoch)x2];
282             }
283             }
284              
285             sub _register_one_fold0 {
286 21     21   148 my($self,
287             $intervals,
288             $epoch,
289             ) = @_;
290 21         106 my $splicepos;
291 21         193 for my $i (0..$#$intervals) {
292 27 100       208 if (_bigfloatgt ($epoch, $intervals->[$i][0])) {
293 5         8 $splicepos = $i;
294 5         9 last;
295             }
296             }
297 21 100       238 unless (defined $splicepos) {
298 16 50       304 if (_bigfloatlt ($epoch, $intervals->[-1][1])) {
299 16         117 $splicepos = @$intervals;
300             } else {
301 0         0 die "Panic: epoch[$epoch] should be smaller than smallest[$intervals->[-1][1]]";
302             }
303             }
304 21         227 splice @$intervals, $splicepos, 0, [($epoch)x2];
305             }
306              
307             # conflate: eliminate overlapping intervals
308             sub _register_one_fold1 {
309 1506     1506   2764 my($self,$intervals) = @_;
310 1506         1819 LOOP: while () {
311 1506         1928 my $splicepos;
312 1506         3311 for my $i (0..$#$intervals-1) {
313 203 50       759 if (_bigfloatle ($intervals->[$i][1],
314             $intervals->[$i+1][0])) {
315 0         0 $intervals->[$i+1][0] = $intervals->[$i][0];
316 0         0 $splicepos = $i;
317 0         0 last;
318             }
319             }
320 1506 50       2686 if (defined $splicepos) {
321 0         0 splice @$intervals, $splicepos, 1;
322             } else {
323 1506         3259 last LOOP;
324             }
325             }
326             }
327              
328             sub _register_one_fold2 {
329 33     33   180 my($self,
330             $intervals,
331             $epoch,
332             ) = @_;
333             # we know we have hit twice, like in
334             # 40:[45,40], [40,35]
335             # 40:[45,40],[42,37],[40,35]
336             # 45:[45,40], [45,35]
337             # 45:[45,40],[42,37],[45,35]
338             # 35:[45,35], [40,35]
339             # 35:[45,35],[42,37],[40,35]
340 33         134 my($splicepos, $splicelen, %assert_between);
341 33         208 INTERVAL: for my $i (0..$#$intervals) {
342 40 100 100     450 if ( $epoch eq $intervals->[$i][0]
343             or $epoch eq $intervals->[$i][1]
344             ) {
345 33         204 for (my $j = 1; $i+$j <= $#$intervals; $j++) {
346 42 100 100     432 if ( $epoch eq $intervals->[$i+$j][0]
347             or $epoch eq $intervals->[$i+$j][1]) {
348 33         268 $intervals->[$i+$j][0] = _bigfloatmax($intervals->[$i][0],$intervals->[$i+$j][0]);
349 33         255 $intervals->[$i+$j][1] = _bigfloatmin($intervals->[$i][1],$intervals->[$i+$j][1]);
350 33         139 $splicepos = $i;
351 33         139 $splicelen = $j;
352 33         161 last INTERVAL;
353             } else {
354 9         19 for my $k (0,1) {
355 18         57 $assert_between{$intervals->[$i+$j][$k]}++;
356             }
357             }
358             }
359             }
360             }
361 33 50       171 if (defined $splicepos) {
362 33         211 for my $k (keys %assert_between) {
363 18 50 33     66 if (_bigfloatgt($k,$intervals->[$splicepos+$splicelen][0])
364             or _bigfloatlt($k,$intervals->[$splicepos+$splicelen][1])){
365 0         0 $DB::single=1;
366 0         0 require Data::Dumper;
367 0         0 die "Panic: broken intervals:".Data::Dumper::Dumper($intervals);
368             }
369             }
370 33         251 splice @$intervals, $splicepos, $splicelen;
371             } else {
372 0         0 $DB::single=1;
373 0         0 die "Panic: Could not find an interval position to insert '$epoch'";
374             }
375             }
376              
377             =head2 reset
378              
379             Forgets everything ever done and gives way for a new round of
380             mirroring. Usually called when the dirtymark on upstream has changed.
381              
382             =cut
383              
384             sub reset {
385 5     5 1 66 my($self) = @_;
386 5         79 $self->_intervals(undef);
387             }
388              
389             =head1 PRIVATE METHODS
390              
391             =head2 _intervals
392              
393             =cut
394             sub _intervals {
395 4561     4561   6924 my($self,$set) = @_;
396 4561 100       9670 if (@_ >= 2) {
397 5         80 $self->__intervals($set);
398             }
399 4561         10039 my $x = $self->__intervals;
400 4561 100       18357 unless (defined $x) {
401 28         85 $x = [];
402 28         1914 $self->__intervals ($x);
403             }
404 4561         7418 return $x;
405             }
406              
407             =head1 COPYRIGHT & LICENSE
408              
409             Copyright 2008, 2009 Andreas König.
410              
411             This program is free software; you can redistribute it and/or modify it
412             under the same terms as Perl itself.
413              
414             =cut
415              
416             1; # End of File::Rsync::Mirror::Recentfile
417              
418             # Local Variables:
419             # mode: cperl
420             # cperl-indent-level: 4
421             # End: