File Coverage

blib/lib/Data/TemporaryBag.pm
Criterion Covered Total %
statement 200 242 82.6
branch 55 76 72.3
condition 10 18 55.5
subroutine 25 29 86.2
pod 9 9 100.0
total 299 374 79.9


line stmt bran cond sub pod time code
1             package Data::TemporaryBag;
2              
3 1     1   2557 use strict;
  1         3  
  1         63  
4              
5 1     1   7 use Fcntl qw/:DEFAULT :seek/;
  1         2  
  1         1004  
6 1     1   153 use Carp;
  1         7  
  1         117  
7 1     1   1788 use File::Temp 'tempfile';
  1         35109  
  1         136  
8              
9 1     1   10 use overload '""' => \&value, '.=' => \&add, '=' => \&clone, fallback => 1;
  1         2  
  1         11  
10 1     1   91 use constant BUFFER => 0;
  1         2  
  1         67  
11 1     1   5 use constant FILENAME => 1;
  1         2  
  1         45  
12 1     1   5 use constant FILEHANDLE => 2;
  1         2  
  1         41  
13 1     1   5 use constant STARTPOS => 3;
  1         2  
  1         40  
14 1     1   5 use constant RECENTNESS => 4;
  1         2  
  1         41  
15 1     1   5 use constant FINGERPRINT => 4;
  1         11  
  1         131  
16 1     1   10 use constant LENGTH => 5;
  1         3  
  1         4740  
17              
18             our ($VERSION, $Threshold, $TempPath, $MaxOpen);
19              
20             $VERSION = '0.09';
21              
22             $Threshold = 10; # KB
23             #$TempPath = $::ENV{'TEMP'}||$::ENV{'TMP'}||'.';
24             $TempPath = '';
25             $MaxOpen = 10;
26              
27             my %OpenFiles;
28              
29             sub new {
30 195     195 1 602 my $class = shift;
31 195         676 my $self = [''];
32            
33 195   66     1442 bless $self, ref($class)||$class;
34            
35 195         759 $self->[LENGTH] = 0;
36 195 100       671 $self->add(@_) if @_;
37 195         379 $self;
38             }
39              
40             sub clear {
41 0     0 1 0 my $self = $_[0];
42              
43 0         0 &_clear_buffer;
44 0         0 $self->[LENGTH] = 0;
45             }
46              
47             sub _clear_buffer {
48 0     0   0 my $self = shift;
49 0         0 my $fn = $self->[FILENAME];
50              
51 0 0       0 if ($fn) {
52 0 0       0 $self->_close if $self->[FILEHANDLE];
53 0         0 unlink $fn;
54 0         0 @{$self}[FILENAME..FINGERPRINT] = ();
  0         0  
55             }
56 0         0 $self->[BUFFER] = '';
57             }
58              
59             sub add {
60 21084     21084 1 43306 my ($self, $data) = @_;
61 21084         33191 my $buf = \$$self[BUFFER];
62              
63 21084 50       44735 $data = '' unless defined $data;
64 21084         28888 $self->[LENGTH] += CORE::length($data);
65              
66 21084 100       40350 if ($self->[FILENAME]) {
67 18359         31189 my $fh = $self->_open;
68 18359         328730 seek $fh, 0, SEEK_END;
69 18359         37177 print $fh $data;
70             } else {
71 2725 100       5166 if (CORE::length($data) + CORE::length($$buf) > $Threshold * 1024) {
72 194         337 my $fh = $self->_open;
73 194         1122 seek $fh, 0, SEEK_END;
74 194         9225 print $fh $$buf, $data;
75             } else {
76 2531         6605 $$buf .= $data;
77             }
78             }
79 21084         32570 $self;
80             }
81              
82             sub substr {
83 19495     19495 1 28492 my ($self, $pos, $size, $replace) = @_;
84 19495         23714 my $len = $self->[LENGTH];
85            
86 19495 100       34739 $pos = $len + $pos if $pos < 0;
87 19495 100 100     92739 if (not defined $size or $size+$pos > $len) {
    50          
88 198         236 $size = $len - $pos;
89             } elsif ($size < 0) {
90 0         0 $size = $len + $size;
91             }
92 19495 100       33888 my $rsize = defined($replace) ? CORE::length($replace) : 0;
93 19495         21281 my $offset = $size - $rsize;
94 19495         22524 my $newlen = $len - $offset;
95              
96 19495 100       32214 if ($self->[FILENAME]) {
97 19487         18912 my $data;
98 19487         33171 my $fh = $self->_open;
99 19487         31772 my $startpos = $self->[STARTPOS];
100              
101 19487 50       33481 return '' if $pos >= $len;
102 19487         151506 seek($fh, $startpos+$pos, SEEK_SET);
103 19487         141710 read($fh, $data, $size);
104 19487 100       36674 if (defined $replace) {
105              
106 5 50 66     49 if ($offset == 0) {
    50          
    100          
    100          
    100          
107 0         0 my $fh = $self->_open;
108 0         0 seek($fh, $pos + $startpos, SEEK_SET);
109 0         0 print $fh $replace;
110             } elsif ($newlen < $Threshold * 800) {
111 0         0 my $data1 = $self->substr(0, $pos);
112 0         0 my $data2 = $self->substr($pos + $size);
113 0         0 $self->_clear_buffer;
114 0         0 $self->[BUFFER] = $data1.$replace.$data2;
115 0         0 $self->[LENGTH] = $newlen;
116             } elsif ($pos == 0 and $startpos >= -$offset) {
117 2         3 $self->[STARTPOS] += $offset;
118 2 100       7 if ($rsize>0) {
119 1         6 seek($fh, $self->[STARTPOS], SEEK_SET);
120 1         3 print $fh $replace;
121             }
122             } elsif ($pos+$size == $len) {
123 1         6 seek($fh, $startpos+$pos, SEEK_SET);
124 1         4 print $fh $replace;
125 1 50       5 truncate($fh, $startpos+$newlen) if $newlen<$len;
126             } elsif ($offset > 0) {
127 1         3 my ($data, $pos2);
128              
129 1 50       4 if ($pos < $len - $pos - $size) {
130 1         8 seek($fh, $startpos+$pos+$offset, SEEK_SET);
131 1         3 print $fh $replace;
132 1         5 _blktf_fw($fh, $startpos, $pos, $offset);
133 1         2 $self->[STARTPOS] += $offset;
134             } else {
135 0         0 seek($fh, $startpos+$pos, SEEK_SET);
136 0         0 print $fh $replace;
137 0         0 my $start = $startpos+$pos+$size;
138 0         0 _blktf_bw($fh, $startpos+$pos+$size, $len-$pos-$size, $offset);
139 0         0 truncate($fh, $startpos+$newlen);
140             }
141             } else {
142 1         2 my $offset = $rsize-$size;
143 1         2 my ($data, $pos2);
144              
145 1 50       3 if ($startpos >= $offset) {
146 0         0 _blktf_bw($fh, $startpos, $pos, $offset);
147 0         0 seek($fh, $startpos+$pos-$offset, SEEK_SET);
148 0         0 print $fh $replace;
149 0         0 $self->[STARTPOS] -= $offset;
150             } else {
151 1         10 _blktf_fw($fh, $startpos+$pos+$size, $len-$pos-$size, $offset);
152 1         13 seek($fh, $startpos+$pos, SEEK_SET);
153 1         5 print $fh $replace;
154             }
155             }
156 5         12 $self->[LENGTH] = $newlen;
157             }
158 19487         71031 return $data;
159             } else {
160 8 100       12 if (defined $replace) {
161 1         3 $self->[LENGTH] = $newlen;
162 1         5 substr($self->[BUFFER], $pos, $size, $replace);
163             } else {
164 7         29 substr($self->[BUFFER], $pos, $size);
165             }
166             }
167             }
168              
169             sub _blktf_fw {
170 2     2   4 my ($fh, $start, $size, $offset) = @_;
171 2         3 my ($pos2, $data);
172              
173 2         8 for ($pos2 = $start + $size-1024; $pos2 > $start; $pos2-=1024) {
174 195         3173 seek($fh, $pos2, SEEK_SET);
175 195         7482 read($fh, $data, 1024);
176 195         1470 seek($fh, $pos2+$offset, SEEK_SET);
177 195         583 print $fh $data;
178             }
179 2         28 seek($fh, $start, SEEK_SET);
180 2         14 read($fh, $data, $pos2 - $start+1024);
181 2         15 seek($fh, $start+$offset, SEEK_SET);
182 2         6 print $fh $data;
183             }
184              
185             sub _blktf_bw {
186 0     0   0 my ($fh, $start, $size, $offset) = @_;
187 0         0 my ($pos2, $data);
188              
189 0         0 for($pos2 = $start; $pos2 < $start+$size-1024; $pos2+=1024) {
190 0         0 seek($fh, $pos2, SEEK_SET);
191 0         0 read($fh, $data, 1024);
192 0         0 seek($fh, $pos2-$offset, SEEK_SET);
193 0         0 print $fh $data;
194             }
195 0         0 seek($fh, $pos2, SEEK_SET);
196 0         0 read($fh, $data, $start+$size-$pos2);
197 0         0 seek($fh, $pos2-$offset, SEEK_SET);
198 0         0 print $fh $data;
199             }
200              
201              
202             sub clone {
203 190     190 1 1188 my ($self, $stream)=@_;
204 190         320 my $size = $self->[LENGTH];
205 190         210 my $pos = 0;
206 190         684 my $new = $self->new;
207              
208 190         365 while ($size > $pos) {
209 19475         38829 $new->add($self->substr($pos, 1024));
210 19475         50520 $pos += 1024;
211             }
212 190         260 $new->[LENGTH] = $size;
213 190         977 $new;
214             }
215              
216             sub value {
217 3     3 1 55 my ($self, $stream)=@_;
218 3         10 my $size = $self->length;
219 3         4 my $pos = 0;
220 3         7 my $data = '';
221              
222 3         8 while ($size > $pos) {
223 3         7 $data .= $self->substr($pos, 1024);
224 3         9 $pos += 1024;
225             }
226 3         9 $data;
227             }
228              
229             sub length {
230 3     3 1 7 shift->[LENGTH];
231              
232             =pod
233              
234             my $self = shift;
235             my $fn = $self->[FILENAME];
236             my $fh = $self->[FILEHANDLE];
237              
238             if ($fh) {
239             seek $fh, 0, SEEK_END;
240             return tell($fh)- $self->[STARTPOS];
241             } elsif ($fn) {
242             return (-s $fn) - $self->[STARTPOS];
243             } else {
244             return length($self->[BUFFER]);
245             }
246              
247             =cut
248              
249             }
250              
251             sub defined {
252 0     0 1 0 defined shift->[BUFFER];
253             }
254              
255             sub _open {
256 38040     38040   54036 my ($self, $mode) = @_;
257 38040         35829 my ($fh, $fn);
258              
259 38040 100       81340 if (defined ($fh = $self->[FILEHANDLE])) {
260 37256         40466 my $recent = $self->[RECENTNESS];
261 37256 100       68227 return $fh if $recent == 1;
262 34790         49031 $self->[RECENTNESS] = 0;
263 34790         89093 while(my (undef, $obj) = each %OpenFiles) {
264 69580 50       124666 if ($obj->[RECENTNESS] <= $recent) {
265 69580         185787 $obj->[RECENTNESS]++;
266             }
267             }
268 34790         69642 return $fh;
269             }
270 784 100       1616 if (defined ($fn = $self->[FILENAME])) {
271 590 50 33     21250 croak "TemporaryBag object seems to be collapsed " if (!-e $fn) or (!-f _);
272 590 50       21414 sysopen($fh, $fn, O_RDWR) or croak "TemporaryBag object seems to be collapsed OP";
273 590 50       10514 croak "TemporaryBag object seems to be collapsed " if (-l $fn);
274 590         1106 binmode $fh;
275 590         876 $self->[FILEHANDLE] = $fh;
276 590 50       1261 $self->_check_fingerprint or croak "TemporaryBag object seems to be collapsed CH";
277             } else {
278 194         883 ($fh, $fn) = tempfile();
279 194         90398 $self->[STARTPOS] = 0;
280 194 50       575 croak "TemporaryBag object seems to be collapsed CR" unless defined $fh;
281 194         538 binmode $fh;
282 194         342 $self->[FILEHANDLE] = $fh;
283 194         444 $self->[FILENAME] = $fn;
284             }
285              
286 784         4131 while(my (undef, $obj) = each %OpenFiles) {
287 1565         4672 ++$obj->[RECENTNESS];
288             }
289            
290 784 100       2065 if (keys %OpenFiles >= $MaxOpen) {
291 782         800 my $to_close;
292 782         1841 while(my (undef, $obj) = each %OpenFiles) {
293 1228 100       3784 if ($obj->[RECENTNESS] > $MaxOpen) {
294 782         773 $to_close = $obj;
295 782         1161 last;
296             }
297             }
298 782         1678 $to_close->_close;
299             }
300              
301 784         1406 $self->[RECENTNESS] = 1;
302 784         2199 $OpenFiles{overload::StrVal($self)} = $self;
303 784         4958 return $fh;
304             }
305              
306             sub _close {
307 782     782   957 my $self = shift;
308 782         1006 my $recent = $self->[RECENTNESS];
309 782         1094 my $fh = $self->[FILEHANDLE];
310 782         768 my $i;
311              
312 782         2184 delete $OpenFiles{overload::StrVal($self)};
313              
314 782         6109 while(my (undef, $obj) = each %OpenFiles) {
315 336 50 33     2324 if (defined $obj and $obj->[RECENTNESS] > $recent) {
316 0         0 $obj->[RECENTNESS]--;
317             }
318             }
319 782         1619 $self->_set_fingerprint;
320 782         1078 undef $self->[FILEHANDLE];
321 782 50       15170 close $fh or croak "TemporaryBag object seems to be collapsed CL";
322             }
323              
324              
325             sub is_saved {
326 2     2 1 24 return shift->[FILENAME];
327             }
328              
329             sub _set_fingerprint {
330 782     782   919 my $self = shift;
331 782         831 my $fingerprint;
332 782         953 my $fh = $self->[FILEHANDLE];
333 782         17096 seek $fh, 0, SEEK_END;
334 782         2001 my $range = tell($fh) - $self->[STARTPOS] - 1024;
335              
336 782         1495 for (1..3) {
337 2346         4074 my $r = int(rand($range))+1024;
338 2346         2152 my $data;
339 2346         15129 seek $fh, -$r, SEEK_END;
340 2346         17504 read($fh, $data, 1024);
341 2346         21615 $fingerprint .= "[$r]".unpack('%32C*',$data);
342             }
343 782         2002 $self->[FINGERPRINT] = $fingerprint;
344             }
345              
346             sub _check_fingerprint {
347 590     590   642 my $self = shift;
348 590         662 my $fh = $self->[FILEHANDLE];
349 590         673 my $fingerprint = $self->[FINGERPRINT];
350 590         573 my $flag = 1;
351              
352 590         3294 while($fingerprint=~/\[([^]]+)\]([^[]+)/g) {
353 1770         2940 my $pos = $1;
354 1770         2143 my $sum = $2;
355 1770         1726 my $data;
356              
357 1770         14232 seek $fh, -$pos, SEEK_END;
358 1770         14381 read($fh, $data, 1024);
359 1770   33     21759 $flag &&= (unpack('%32C*',$data) == $sum);
360             }
361 590         2465 return $flag;
362             }
363              
364              
365              
366             sub DESTROY {
367 193     193   363 my $self = shift;
368             # close $self->[FILEHANDLE] if defined $self->[FILEHANDLE];
369 193 50       491 $self->_close if defined $self->[FILEHANDLE];
370 193 100       41306 unlink $self->[FILENAME] if defined $self->[FILENAME];
371             }
372              
373              
374              
375             1;
376             __END__