r14213@rob-kinyons-computer (orig r8080): rkinyon | 2006-11-17 20:47:50 -0500
[dbsrgits/DBM-Deep.git] / lib / DBM / Deep / File.pm
CommitLineData
460b1067 1package DBM::Deep::File;
2
2120a181 3use 5.006_000;
460b1067 4
5use strict;
6use warnings;
7
2120a181 8our $VERSION = q(0.99_04);
460b1067 9
86867f3a 10use Fcntl qw( :DEFAULT :flock :seek );
460b1067 11
12sub new {
13 my $class = shift;
14 my ($args) = @_;
15
16 my $self = bless {
359a01ac 17 autobless => 1,
2120a181 18 autoflush => 1,
460b1067 19 end => 0,
20 fh => undef,
21 file => undef,
22 file_offset => 0,
2120a181 23 locking => 1,
460b1067 24 locked => 0,
2120a181 25#XXX Migrate this to the engine, where it really belongs.
460b1067 26 filter_store_key => undef,
27 filter_store_value => undef,
28 filter_fetch_key => undef,
29 filter_fetch_value => undef,
30 }, $class;
31
32 # Grab the parameters we want to use
33 foreach my $param ( keys %$self ) {
34 next unless exists $args->{$param};
35 $self->{$param} = $args->{$param};
36 }
37
38 if ( $self->{fh} && !$self->{file_offset} ) {
39 $self->{file_offset} = tell( $self->{fh} );
40 }
41
42 $self->open unless $self->{fh};
43
44 return $self;
45}
46
47sub open {
48 my $self = shift;
49
633df1fd 50 # Adding O_BINARY should remove the need for the binmode below. However,
460b1067 51 # I'm not going to remove it because I don't have the Win32 chops to be
52 # absolutely certain everything will be ok.
53 my $flags = O_RDWR | O_CREAT | O_BINARY;
54
55 my $fh;
56 sysopen( $fh, $self->{file}, $flags )
57 or die "DBM::Deep: Cannot sysopen file '$self->{file}': $!\n";
58 $self->{fh} = $fh;
59
60 # Even though we use O_BINARY, better be safe than sorry.
61 binmode $fh;
62
63 if ($self->{autoflush}) {
64 my $old = select $fh;
65 $|=1;
66 select $old;
67 }
68
69 return 1;
70}
71
72sub close {
73 my $self = shift;
74
75 if ( $self->{fh} ) {
76 close $self->{fh};
77 $self->{fh} = undef;
78 }
79
80 return 1;
81}
82
7dcefff3 83sub set_inode {
84 my $self = shift;
85
2120a181 86 unless ( defined $self->{inode} ) {
7dcefff3 87 my @stats = stat($self->{fh});
88 $self->{inode} = $stats[1];
89 $self->{end} = $stats[7];
90 }
91
92 return 1;
93}
94
019404df 95sub print_at {
96 my $self = shift;
97 my $loc = shift;
98
99 local ($/,$\);
100
101 my $fh = $self->{fh};
7dcefff3 102 if ( defined $loc ) {
103 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
104 }
105
019404df 106 print( $fh @_ );
107
108 return 1;
109}
110
7dcefff3 111sub read_at {
112 my $self = shift;
113 my ($loc, $size) = @_;
114
115 local ($/,$\);
116
117 my $fh = $self->{fh};
118 if ( defined $loc ) {
119 seek( $fh, $loc + $self->{file_offset}, SEEK_SET );
120 }
121
122 my $buffer;
123 read( $fh, $buffer, $size);
124
125 return $buffer;
126}
127
460b1067 128sub DESTROY {
129 my $self = shift;
130 return unless $self;
131
132 $self->close;
133
134 return;
135}
136
019404df 137sub request_space {
138 my $self = shift;
139 my ($size) = @_;
140
7dcefff3 141 #XXX Do I need to reset $self->{end} here? I need a testcase
019404df 142 my $loc = $self->{end};
143 $self->{end} += $size;
144
145 return $loc;
146}
147
15ba72cc 148##
149# If db locking is set, flock() the db file. If called multiple
150# times before unlock(), then the same number of unlocks() must
151# be called before the lock is released.
152##
153sub lock {
154 my $self = shift;
155 my ($obj, $type) = @_;
42717e46 156
15ba72cc 157 $type = LOCK_EX unless defined $type;
158
159 if (!defined($self->{fh})) { return; }
160
161 if ($self->{locking}) {
162 if (!$self->{locked}) {
163 flock($self->{fh}, $type);
164
165 # refresh end counter in case file has changed size
166 my @stats = stat($self->{fh});
167 $self->{end} = $stats[7];
168
169 # double-check file inode, in case another process
170 # has optimize()d our file while we were waiting.
2120a181 171 if (defined($self->{inode}) && $stats[1] != $self->{inode}) {
15ba72cc 172 $self->close;
173 $self->open;
174
175 #XXX This needs work
176 $obj->{engine}->setup_fh( $obj );
177
178 flock($self->{fh}, $type); # re-lock
179
180 # This may not be necessary after re-opening
181 $self->{end} = (stat($self->{fh}))[7]; # re-end
182 }
183 }
184 $self->{locked}++;
185
186 return 1;
187 }
188
189 return;
190}
191
192##
193# If db locking is set, unlock the db file. See note in lock()
194# regarding calling lock() multiple times.
195##
196sub unlock {
197 my $self = shift;
198
199 if (!defined($self->{fh})) { return; }
200
201 if ($self->{locking} && $self->{locked} > 0) {
202 $self->{locked}--;
203 if (!$self->{locked}) { flock($self->{fh}, LOCK_UN); }
204
205 return 1;
206 }
207
208 return;
209}
210
2120a181 211sub flush {
25c7c8d6 212 my $self = shift;
213
2120a181 214 # Flush the filehandle
215 my $old_fh = select $self->{fh};
216 my $old_af = $|; $| = 1; $| = $old_af;
217 select $old_fh;
25c7c8d6 218
219 return 1;
220}
28394a1a 221
460b1067 2221;
223__END__