File Coverage

blib/lib/Paranoid/IO/FileMultiplexer/Block.pm
Criterion Covered Total %
statement 145 162 89.5
branch 38 54 70.3
condition 15 21 71.4
subroutine 22 25 88.0
pod 12 12 100.0
total 232 274 84.6


line stmt bran cond sub pod time code
1             #
2             #
3             # # Will need to zero out all data between old eos and new eosParanoid::IO::FileMultiplexer::Block -- PIOFM Base Block Class
4             #
5             # $Id: lib/Paranoid/IO/FileMultiplexer/Block.pm, 2.09 2021/12/28 15:46:49 acorliss Exp $
6             #
7             # This software is free software. Similar to Perl, you can redistribute it
8             # and/or modify it under the terms of either:
9             #
10             # a) the GNU General Public License
11             # as published by the
12             # Free Software Foundation ; either version 1
13             # , or any later version
14             # , or
15             # b) the Artistic License 2.0
16             # ,
17             #
18             # subject to the following additional term: No trademark rights to
19             # "Paranoid" have been or are conveyed under any of the above licenses.
20             # However, "Paranoid" may be used fairly to describe this unmodified
21             # software, in good faith, but not as a trademark.
22             #
23             # (c) 2005 - 2021, Arthur Corliss (corliss@digitalmages.com)
24             # (tm) 2008 - 2021, Paranoid Inc. (www.paranoid.com)
25             #
26             #####################################################################
27              
28             #####################################################################
29             #
30             # Environment definitions
31             #
32             #####################################################################
33              
34             package Paranoid::IO::FileMultiplexer::Block;
35              
36 11     11   187 use 5.008;
  11         33  
37              
38 11     11   77 use strict;
  11         22  
  11         286  
39 11     11   44 use warnings;
  11         22  
  11         275  
40 11     11   55 use vars qw($VERSION);
  11         11  
  11         429  
41 11     11   55 use base qw(Exporter);
  11         11  
  11         891  
42 11     11   77 use Paranoid;
  11         22  
  11         572  
43 11     11   77 use Paranoid::IO qw(:all);
  11         22  
  11         1793  
44 11     11   88 use Paranoid::Debug qw(:all);
  11         121  
  11         2123  
45 11     11   88 use Fcntl qw(:DEFAULT :flock :mode :seek);
  11         22  
  11         5335  
46              
47             ($VERSION) = ( q$Revision: 2.09 $ =~ /(\d+(?:\.\d+)+)/sm );
48              
49 11     11   88 use constant MINBSIZE => 4_096;
  11         22  
  11         792  
50 11     11   66 use constant MAXBSIZE => 1_048_576;
  11         22  
  11         660  
51 11     11   77 use constant TEST32INT => 1 << 32;
  11         11  
  11         517  
52 11     11   55 use constant MAX32VAL => 0b11111111_11111111_11111111_11111111;
  11         143  
  11         16324  
53              
54             #####################################################################
55             #
56             # Module code follows
57             #
58             #####################################################################
59              
60             sub new {
61              
62             # Purpose: Creates a block object
63             # Returns: Object reference or undef
64             # Usage: $obj = Paranoid::IO::FileMultiplexer::Block->new(
65             # $filename, $bnum, $bsize);
66              
67 3691     3691 1 7996 my $class = shift;
68 3691         5686 my $file = shift;
69 3691         6432 my $bnum = shift;
70 3691         6177 my $bsize = shift;
71 3691         15448 my $self = {
72             file => $file,
73             blockNum => 0,
74             blockSize => MINBSIZE,
75             minPos => 0,
76             maxPos => MINBSIZE - 1,
77             };
78              
79 3691         10519 pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL3, $file, $bnum, $bsize );
80 3691         9649 pIn();
81              
82 3691         7513 bless $self, $class;
83              
84             # Check mandatory values
85 3691 50 100     30572 $self = undef
      66        
      66        
86             unless defined $bnum
87             and defined $bsize
88             and defined $file
89             and length $file;
90 3691 100       8360 pdebug( 'invalid or missing arguments', PDLEVEL1 )
91             unless defined $self;
92              
93 3691 100       7991 if ( defined $self ) {
94              
95             # Make sure we only have positive values for the block number and size
96 3669         9621 $$self{blockNum} = int $bnum;
97 3669 50       12088 $$self{blockSize} = int $bsize if defined $bsize;
98 3669         10967 $$self{minPos} = $$self{blockNum} * $$self{blockSize};
99 3669         12183 $$self{maxPos} = $$self{minPos} + $$self{blockSize} - 1;
100              
101             # Make sure block size is in range and a multiple of MINBSIZE
102             $self = undef
103             unless $$self{blockSize} >= MINBSIZE
104             and $$self{blockSize} <= MAXBSIZE
105 3669 100 100     28031 and $$self{blockSize} % MINBSIZE == 0;
      100        
106 3669 100       10675 pdebug( 'invalid block size', PDLEVEL1 ) unless defined $self;
107             }
108              
109 3691         10009 pOut();
110 3691         9016 pdebug( 'leaving w/rv: %s', PDLEVEL3, $self );
111              
112 3691         12095 return $self;
113             }
114              
115             sub recalibrate {
116              
117             # Purpose: Recalibrates min/max positions in the block based
118             # on block size.
119             # Returns: Boolean
120             # Usage: $rv = $obj->recalibrate;
121              
122 85     85 1 145 my $self = shift;
123              
124 85         398 $$self{minPos} = $$self{blockNum} * $$self{blockSize};
125 85         353 $$self{maxPos} = $$self{minPos} + $$self{blockSize} - 1;
126              
127 85         325 return 1;
128             }
129              
130             sub blockNum {
131              
132             # Purpose: Returns the block number
133             # Returns: Integer
134             # Usage: $bn = $obj->blockNum;
135              
136 0     0 1 0 my $self = shift;
137 0         0 return $$self{blockNum};
138             }
139              
140             sub blockSize {
141              
142             # Purpose: Returns the block size
143             # Returns: Integer
144             # Usage: $bs = $obj->blockSize;
145              
146 166     166 1 361 my $self = shift;
147 166         1107 return $$self{blockSize};
148             }
149              
150             sub minPos {
151              
152             # Purpose: Returns the min writable file position for the block
153             # Returns: Integer
154             # Usage: $minp = $obj->minPos;
155              
156 0     0 1 0 my $self = shift;
157 0         0 return $$self{minPos};
158             }
159              
160             sub maxPos {
161              
162             # Purpose: Returns the max writable file position for the block
163             # Returns: Integer
164             # Usage: $maxp = $obj->maxPos;
165              
166 0     0 1 0 my $self = shift;
167 0         0 return $$self{maxPos};
168             }
169              
170             sub allocate {
171              
172             # Purpose: Writes a new block to disk
173             # Returns: Boolean
174             # Usage: $rv = $obj->allocate;
175              
176 345     345 1 10414 my $self = shift;
177 345         745 my $file = $$self{file};
178 345         937 my $minPos = $$self{minPos};
179 345         780 my $maxPos = $$self{maxPos};
180 345         581 my $rv = 0;
181              
182 345         1190 pdebug( 'entering', PDLEVEL3 );
183 345         1091 pIn();
184              
185 345 50       1093 if ( pflock( $$self{file}, LOCK_EX ) ) {
186              
187             # Seek and write a null byte at the end of the block
188 345         1445 pdebug( 'end of file should be at %s', PDLEVEL4, $minPos );
189 345         1279 pseek( $file, 0, SEEK_END );
190 345 100       1107 if ( ptell($file) == $minPos ) {
191 323         1288 pseek( $file, $maxPos, SEEK_SET );
192 323         1225 $rv = pwrite( $file, pack 'x' );
193             } else {
194 22         77 pdebug('block already allocated');
195             }
196              
197 345         1181 pflock( $$self{file}, LOCK_UN );
198             }
199              
200 345         1100 pOut();
201 345         989 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
202              
203 345         2788 return $rv;
204             }
205              
206             sub bread {
207              
208             # Purpose: Reads the contents of the entire block. or a specified range
209             # Returns: Integer (bytes read) or undef on error
210             # Usage: $bytesRead = $obj->bread(\$content);
211             # Usage: $bytesRead = $obj->bread(\$content, $start);
212             # Usage: $bytesRead = $obj->bread(\$content, undef, $bytes);
213             # Usage: $bytesRead = $obj->bread(\$content, $start, $bytes);
214              
215 8955     8955 1 16195 my $self = shift;
216 8955         12566 my $cref = shift;
217 8955         16215 my $start = shift;
218 8955         14544 my $bytes = shift;
219 8955         17357 my $file = $$self{file};
220 8955         19448 my $bsize = $$self{blockSize};
221 8955         17947 my $minp = $$self{minPos};
222 8955         32026 my $maxp = $$self{maxPos};
223 8955         14843 my $rv = '0 but true';
224              
225 8955         23515 pdebug( 'entering w/(%s)(%s)(%s)', PDLEVEL3, $cref, $start, $bytes );
226 8955         22304 pIn();
227              
228             # NOTE: This method intentionally allows reads of a length greater than
229             # the block size, but it will only return content from within the block
230             # boundaries.
231              
232             # Error out if we were not given a valid scalar ref
233 8955 50 33     37379 unless ( defined $cref and ref($cref) eq 'SCALAR' ) {
234 0         0 $rv = undef;
235 0         0 pdebug( 'invalid argument for content ref', PDLEVEL1 );
236             }
237              
238             # Set start to beginning of block if not specified
239 8955 100       21982 $start = 0 unless defined $start;
240              
241             # Set default bytes if not specified
242 8955 100       22319 $bytes = $bsize - $start unless defined $bytes;
243              
244             # Make sure start is in range
245 8955 50       26056 if ( $minp + $start > $maxp ) {
246 0         0 pdebug( 'starting position is out of range', PDLEVEL1 );
247 0         0 $rv = undef;
248             }
249              
250 8955 50       19669 if ($rv) {
251              
252             # Make sure we limit read to our block
253 8955 100       25383 $bytes = ( $maxp + 1 ) - ( $minp + $start )
254             if ( $minp + $start + $bytes ) > ( $maxp + 1 );
255              
256             # Perform the read
257 8955 50       29931 if ( pseek( $file, $minp + $start, SEEK_SET ) ) {
258 8955         25913 $rv = pread( $file, $$cref, $bytes );
259             } else {
260 0         0 $rv = undef;
261             }
262             }
263              
264 8955         28724 pOut();
265 8955         21266 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
266              
267 8955         44678 return $rv;
268             }
269              
270             sub bwrite {
271              
272             # Purpose: Writes the contents of the entire block. or a specified range
273             # Returns: Integer (bytes written) or undef on error
274             # Usage: $bytesWritten = $obj->bwrite($content);
275             # Usage: $bytesWritten = $obj->bwrite($content, $start );
276             # Usage: $bytesWritten = $obj->bwrite($content, $start, $length );
277             # Usage: $bytesWritten = $obj->bwrite($content, $start, $length, $offset );
278              
279 1925     1925 1 3412 my $self = shift;
280 1925         3047 my $content = shift;
281 1925         3302 my $start = shift;
282 1925         3410 my $length = shift;
283 1925         3446 my $offset = shift;
284 1925         3784 my $file = $$self{file};
285 1925         4098 my $bsize = $$self{blockSize};
286 1925         3992 my $minp = $$self{minPos};
287 1925         3920 my $maxp = $$self{maxPos};
288 1925         3298 my $rv = '0 but true';
289 1925 50       6783 my $cdata = defined $content ? ( length $content ) . ' bytes' : undef;
290 1925         3555 my $blkLeft;
291              
292 1925         6087 pdebug( 'entering w/(%s)(%s)(%s)(%s)',
293             PDLEVEL3, $cdata, $start, $length, $offset );
294 1925         5006 pIn();
295              
296             # NOTE: This method intentionally allows writes of a length greater than
297             # the block size, but it will only write content from within the block
298             # boundaries.
299              
300             # Error out if we were not given a valid scalar ref
301 1925 50 33     9718 unless ( defined $content and length $content ) {
302 0         0 $rv = undef;
303 0         0 pdebug( 'invalid argument for content', PDLEVEL1 );
304             }
305              
306             # Set start to beginning of block if not specified
307 1925 100       5074 $start = 0 unless defined $start;
308              
309             # Set offset to zero if not specified
310 1925 100       4950 $offset = 0 unless defined $offset;
311              
312             # Set length to max content length available if not defined
313 1925 100       5519 $length = length($content) - $offset unless defined $length;
314 1925         4212 $blkLeft = $bsize - $start;
315 1925 100       5540 $length = $blkLeft if $blkLeft < $length;
316              
317             # Make sure start is in range
318 1925 50       6751 if ( $minp + $start > $maxp ) {
319 0         0 pdebug( 'starting position is out of range', PDLEVEL1 );
320 0         0 $rv = undef;
321             }
322              
323 1925 50       4618 if ($rv) {
324              
325             # Perform the write
326 1925 50       7820 if ( pseek( $file, $minp + $start, SEEK_SET ) ) {
327 1925         5555 $rv = pwrite( $file, $content, $length, $offset );
328             } else {
329 0         0 $rv = undef;
330             }
331             }
332              
333 1925         6775 pOut();
334 1925         4761 pdebug( 'leaving w/rv: %s', PDLEVEL3, $rv );
335              
336 1925         11022 return $rv;
337             }
338              
339             sub has64bInt {
340              
341             # Purpose: Returns whether the current platform supports 64b integers
342             # Returns: Boolean
343             # Usage: $rv = $obj->has64bInt;
344              
345 12955     12955 1 31235 return TEST32INT == 1 ? 0 : 1;
346             }
347              
348             sub splitInt {
349              
350             # Purpose: Splits the passed integer into two 32b integers
351             # Returns: Two integers (lower, upper)
352             # Usage: @split = $obj->splitInt($num);
353              
354 1311     1311 1 2987 my $self = shift;
355 1311         2704 my $num = shift;
356 1311         2340 my ( $upper, $lower );
357              
358             # Extract lower 32 bits
359 1311         3680 $lower = $num & MAX32VAL;
360              
361             # Extract upper 32 bits
362 1311 50       3891 $upper = $self->has64bInt ? ( $num & ~MAX32VAL ) >> 32 : 0;
363              
364 1311         8381 return ( $lower, $upper );
365             }
366              
367             sub joinInt {
368              
369             # Purpose: Joins to 32b integers into a single 64b integer
370             # Returns: Integer/undef
371             # Usage: $i = $obj->joinInt($lower, $upper);
372              
373 11644     11644 1 22080 my $self = shift;
374 11644         22675 my $lower = shift;
375 11644         20254 my $upper = shift;
376 11644         16860 my $rv;
377              
378 11644 50       23634 if ( $self->has64bInt ) {
379 11644         32286 $rv = $lower | ( $upper << 32 );
380             } else {
381 0 0       0 $rv = $lower if $upper == 0;
382             }
383              
384 11644         39566 return $rv;
385             }
386              
387             1;
388              
389             __END__