File Coverage

blib/lib/File/Marker.pm
Criterion Covered Total %
statement 71 79 89.8
branch 16 20 80.0
condition n/a
subroutine 15 16 93.7
pod 7 7 100.0
total 109 122 89.3


line stmt bran cond sub pod time code
1 4     4   4733 use 5.006;
  4         15  
  4         168  
2 4     4   23 use strict;
  4         10  
  4         148  
3 4     4   23 use warnings;
  4         6  
  4         294  
4              
5             package File::Marker;
6             # ABSTRACT: Set and jump between named position markers on a filehandle
7             our $VERSION = '0.14'; # VERSION
8              
9             our @ISA = qw( IO::File );
10              
11 4     4   23 use Carp;
  4         7  
  4         396  
12 4     4   3763 use IO::File;
  4         39221  
  4         741  
13 4     4   35 use Scalar::Util 1.09 qw( refaddr weaken );
  4         113  
  4         4037  
14              
15             #--------------------------------------------------------------------------#
16             # Inside-out data storage
17             #--------------------------------------------------------------------------#
18              
19             my %MARKS = ();
20              
21             # Track objects for thread-safety
22              
23             my %REGISTRY = ();
24              
25             #--------------------------------------------------------------------------#
26             # new()
27             #--------------------------------------------------------------------------#
28              
29             sub new {
30 5     5 1 2978 my $class = shift;
31 5         30 my $self = IO::File->new();
32 5         197 bless $self, $class;
33 5         33 weaken( $REGISTRY{ refaddr $self } = $self );
34 5 100       27 $self->open(@_) if @_;
35 5         20 return $self;
36             }
37              
38             #--------------------------------------------------------------------------#
39             # open()
40             #--------------------------------------------------------------------------#
41              
42             sub open {
43 7     7 1 1207 my $self = shift;
44 7         26 $MARKS{ refaddr $self } = {};
45 7         43 $self->SUPER::open(@_);
46 7         517 $MARKS{ refaddr $self }{'LAST'} = $self->getpos;
47 7         26 return 1;
48             }
49              
50             #--------------------------------------------------------------------------#
51             # set_marker()
52             #--------------------------------------------------------------------------#
53              
54             sub set_marker {
55 11     11 1 2810 my ( $self, $mark ) = @_;
56              
57 11 100       62 croak "Can't set marker on closed filehandle"
58             if !$self->opened;
59              
60 10 100       365 croak "Can't set special marker 'LAST'"
61             if $mark eq 'LAST';
62              
63 9         43 my $position = $self->getpos;
64              
65 9 50       23 croak "Couldn't set marker '$mark': couldn't locate position in file"
66             if !defined $position;
67              
68 9         47 $MARKS{ refaddr $self }{$mark} = $self->getpos;
69              
70 9         31 return 1;
71             }
72              
73             #--------------------------------------------------------------------------#
74             # goto_marker()
75             #--------------------------------------------------------------------------#
76              
77             sub goto_marker {
78 14     14 1 4060 my ( $self, $mark ) = @_;
79              
80 14 100       108 croak "Can't goto marker on closed filehandle"
81             if !$self->opened;
82              
83 13 100       526 croak "Unknown file marker '$mark'"
84             if !exists $MARKS{ refaddr $self}{$mark};
85              
86 12         76 my $old_position = $self->getpos; # save for LAST
87              
88 12         306 my $rc = $self->setpos( $MARKS{ refaddr $self }{$mark} );
89              
90 12 50       32 croak "Couldn't goto marker '$mark': could not seek to location in file"
91             if !defined $rc;
92              
93 12         55 $MARKS{ refaddr $self }{'LAST'} = $old_position;
94              
95 12         162 return 1;
96             }
97              
98             #--------------------------------------------------------------------------#
99             # markers()
100             #--------------------------------------------------------------------------#
101              
102             sub markers {
103 2     2 1 657 my $self = shift;
104 2         4 return keys %{ $MARKS{ refaddr $self } };
  2         27  
105             }
106              
107             #--------------------------------------------------------------------------#
108             # save_markers()
109             #--------------------------------------------------------------------------#
110              
111             sub save_markers {
112 1     1 1 3 my ( $self, $filename ) = @_;
113 1 50       6 my $outfile = IO::File->new( $filename, "w" )
114             or croak "Couldn't open $filename for writing";
115 1         149 my $markers = $MARKS{ refaddr $self };
116 1         5 for my $mark ( keys %$markers ) {
117 6 100       60 next if $mark eq 'LAST';
118 5         23 print $outfile "$mark\n";
119 5         22 print $outfile unpack( "H*", $markers->{$mark} ), "\n";
120             }
121 1         65 close $outfile;
122             }
123              
124             #--------------------------------------------------------------------------#
125             # load_markers()
126             #--------------------------------------------------------------------------#
127              
128             sub load_markers {
129 1     1 1 21 my ( $self, $filename ) = @_;
130 1 50       8 my $infile = IO::File->new( $filename, "r" )
131             or croak "Couldn't open $filename for reading";
132 1         94 my $markers = $MARKS{ refaddr $self };
133 1         2 my $mark;
134 1         12 while ( defined( $mark = <$infile> ) ) {
135 5         7 chomp $mark;
136 5         8 my $position = <$infile>;
137 5         6 chomp $position;
138 5         13 $position = pack( "H*", $position );
139 5         22 $markers->{$mark} = $position;
140             }
141 1         18 close $infile;
142             }
143              
144             #--------------------------------------------------------------------------#
145             # DESTROY()
146             #--------------------------------------------------------------------------#
147              
148             sub DESTROY {
149 5     5   2917 my $self = shift;
150 5         74 delete $MARKS{ refaddr $self };
151 5         41 delete $REGISTRY{ refaddr $self };
152              
153 5         228 $self->SUPER::DESTROY;
154             }
155              
156             #--------------------------------------------------------------------------#
157             # CLONE()
158             #--------------------------------------------------------------------------#
159              
160             sub CLONE {
161 0     0   0 for my $old_id ( keys %REGISTRY ) {
162              
163             # look under old_id to find the new, cloned reference
164 0         0 my $object = $REGISTRY{$old_id};
165 0         0 my $new_id = refaddr $object;
166              
167             # relocate data
168 0         0 $MARKS{$new_id} = $MARKS{$old_id};
169 0         0 delete $MARKS{$old_id};
170              
171             # update the weak reference to the new, cloned object
172 0         0 weaken( $REGISTRY{$new_id} = $object );
173 0         0 delete $REGISTRY{$old_id};
174             }
175              
176 0         0 return;
177             }
178              
179             #--------------------------------------------------------------------------#
180             # _object_count() -- used in test scripts to see if memory is leaking
181             #--------------------------------------------------------------------------#
182              
183             sub _object_count {
184 1     1   1926 return scalar keys %REGISTRY;
185             }
186              
187             1;
188              
189             __END__