1 package DBM::Deep::Engine::File;
6 use warnings FATAL => 'all';
8 use base qw( DBM::Deep::Engine );
12 use DBM::Deep::Null ();
13 use DBM::Deep::Sector::File ();
14 use DBM::Deep::Storage::File ();
16 sub sector_type { 'DBM::Deep::Sector::File' }
20 # Please refer to the pack() documentation for further information
22 1 => 'C', # Unsigned char value (no order needed as it's just one byte)
23 2 => 'n', # Unsigned short in "network" (big-endian) order
24 4 => 'N', # Unsigned long in "network" (big-endian) order
25 8 => 'Q', # Usigned quad (no order specified, presumably machine-dependent)
30 DBM::Deep::Engine::File
34 This is the engine for use with L<DBM::Deep::Storage::File/>.
36 =head1 EXTERNAL METHODS
40 This takes a set of args. These args are described in the documentation for
49 $args->{storage} = DBM::Deep::Storage::File->new( $args )
50 unless exists $args->{storage};
56 hash_size => 16, # In bytes
57 hash_chars => 256, # Number of chars the algorithm uses per byte
59 num_txns => 1, # The HEAD
60 trans_id => 0, # Default to the HEAD
62 data_sector_size => 64, # Size in bytes of each data sector
64 entries => {}, # This is the list of entries for transactions
68 # Never allow byte_size to be set directly.
69 delete $args->{byte_size};
70 if ( defined $args->{pack_size} ) {
71 if ( lc $args->{pack_size} eq 'small' ) {
72 $args->{byte_size} = 2;
74 elsif ( lc $args->{pack_size} eq 'medium' ) {
75 $args->{byte_size} = 4;
77 elsif ( lc $args->{pack_size} eq 'large' ) {
78 $args->{byte_size} = 8;
81 DBM::Deep->_throw_error( "Unknown pack_size value: '$args->{pack_size}'" );
85 # Grab the parameters we want to use
86 foreach my $param ( keys %$self ) {
87 next unless exists $args->{$param};
88 $self->{$param} = $args->{$param};
92 max_buckets => { floor => 16, ceil => 256 },
93 num_txns => { floor => 1, ceil => 255 },
94 data_sector_size => { floor => 32, ceil => 256 },
97 while ( my ($attr, $c) = each %validations ) {
98 if ( !defined $self->{$attr}
99 || !length $self->{$attr}
100 || $self->{$attr} =~ /\D/
101 || $self->{$attr} < $c->{floor}
103 $self->{$attr} = '(undef)' if !defined $self->{$attr};
104 warn "Floor of $attr is $c->{floor}. Setting it to $c->{floor} from '$self->{$attr}'\n";
105 $self->{$attr} = $c->{floor};
107 elsif ( $self->{$attr} > $c->{ceil} ) {
108 warn "Ceiling of $attr is $c->{ceil}. Setting it to $c->{ceil} from '$self->{$attr}'\n";
109 $self->{$attr} = $c->{ceil};
113 if ( !$self->{digest} ) {
115 $self->{digest} = \&Digest::MD5::md5;
123 my ($obj, $key) = @_;
125 # This will be a Reference sector
126 my $sector = $self->load_sector( $obj->_base_offset )
129 if ( $sector->staleness != $obj->_staleness ) {
133 my $key_md5 = $self->_apply_digest( $key );
135 my $value_sector = $sector->get_data_for({
140 unless ( $value_sector ) {
141 $value_sector = DBM::Deep::Sector::File::Null->new({
146 $sector->write_data({
149 value => $value_sector,
153 return $value_sector->data;
160 # This will be a Reference sector
161 my $sector = $self->load_sector( $obj->_base_offset )
162 or DBM::Deep->_throw_error( "How did get_classname fail (no sector for '$obj')?!" );
164 if ( $sector->staleness != $obj->_staleness ) {
168 return $sector->get_classname;
173 my ($obj, $old_key, $new_key) = @_;
175 # This will be a Reference sector
176 my $sector = $self->load_sector( $obj->_base_offset )
177 or DBM::Deep->_throw_error( "How did make_reference fail (no sector for '$obj')?!" );
179 if ( $sector->staleness != $obj->_staleness ) {
183 my $old_md5 = $self->_apply_digest( $old_key );
185 my $value_sector = $sector->get_data_for({
190 unless ( $value_sector ) {
191 $value_sector = DBM::Deep::Sector::File::Null->new({
196 $sector->write_data({
199 value => $value_sector,
203 if ( $value_sector->isa( 'DBM::Deep::Sector::File::Reference' ) ) {
204 $sector->write_data({
206 key_md5 => $self->_apply_digest( $new_key ),
207 value => $value_sector,
209 $value_sector->increment_refcount;
212 $sector->write_data({
214 key_md5 => $self->_apply_digest( $new_key ),
215 value => $value_sector->clone,
224 my ($obj, $key) = @_;
226 # This will be a Reference sector
227 my $sector = $self->load_sector( $obj->_base_offset )
230 if ( $sector->staleness != $obj->_staleness ) {
234 my $data = $sector->get_data_for({
235 key_md5 => $self->_apply_digest( $key ),
239 # exists() returns 1 or '' for true/false.
240 return $data ? 1 : '';
245 my ($obj, $key) = @_;
247 my $sector = $self->load_sector( $obj->_base_offset )
250 if ( $sector->staleness != $obj->_staleness ) {
254 return $sector->delete_key({
255 key_md5 => $self->_apply_digest( $key ),
262 my ($obj, $key, $value) = @_;
264 my $r = Scalar::Util::reftype( $value ) || '';
267 last if $r eq 'HASH';
268 last if $r eq 'ARRAY';
270 DBM::Deep->_throw_error(
271 "Storage of references of type '$r' is not supported."
275 # This will be a Reference sector
276 my $sector = $self->load_sector( $obj->_base_offset )
277 or DBM::Deep->_throw_error( "Cannot write to a deleted spot in DBM::Deep." );
279 if ( $sector->staleness != $obj->_staleness ) {
280 DBM::Deep->_throw_error( "Cannot write to a deleted spot in DBM::Deep." );
284 if ( !defined $value ) {
285 $class = 'DBM::Deep::Sector::File::Null';
287 elsif ( $r eq 'ARRAY' || $r eq 'HASH' ) {
289 if ( $r eq 'ARRAY' ) {
290 $tmpvar = tied @$value;
291 } elsif ( $r eq 'HASH' ) {
292 $tmpvar = tied %$value;
296 my $is_dbm_deep = eval { local $SIG{'__DIE__'}; $tmpvar->isa( 'DBM::Deep' ); };
298 unless ( $is_dbm_deep ) {
299 DBM::Deep->_throw_error( "Cannot store something that is tied." );
302 unless ( $tmpvar->_engine->storage == $self->storage ) {
303 DBM::Deep->_throw_error( "Cannot store values across DBM::Deep files. Please use export() instead." );
306 # First, verify if we're storing the same thing to this spot. If we
307 # are, then this should be a no-op. -EJS, 2008-05-19
308 my $loc = $sector->get_data_location_for({
309 key_md5 => $self->_apply_digest( $key ),
313 if ( defined($loc) && $loc == $tmpvar->_base_offset ) {
317 #XXX Can this use $loc?
318 my $value_sector = $self->load_sector( $tmpvar->_base_offset );
319 $sector->write_data({
321 key_md5 => $self->_apply_digest( $key ),
322 value => $value_sector,
324 $value_sector->increment_refcount;
329 $class = 'DBM::Deep::Sector::File::Reference';
330 $type = substr( $r, 0, 1 );
333 if ( tied($value) ) {
334 DBM::Deep->_throw_error( "Cannot store something that is tied." );
336 $class = 'DBM::Deep::Sector::File::Scalar';
339 # Create this after loading the reference sector in case something bad
340 # happens. This way, we won't allocate value sector(s) needlessly.
341 my $value_sector = $class->new({
347 $sector->write_data({
349 key_md5 => $self->_apply_digest( $key ),
350 value => $value_sector,
353 # This code is to make sure we write all the values in the $value to the
354 # disk and to make sure all changes to $value after the assignment are
355 # reflected on disk. This may be counter-intuitive at first, but it is
357 # NOTE - simply tying $value won't perform a STORE on each value. Hence,
358 # the copy to a temp value.
359 if ( $r eq 'ARRAY' ) {
361 tie @$value, 'DBM::Deep', {
362 base_offset => $value_sector->offset,
363 staleness => $value_sector->staleness,
364 storage => $self->storage,
368 bless $value, 'DBM::Deep::Array' unless Scalar::Util::blessed( $value );
370 elsif ( $r eq 'HASH' ) {
372 tie %$value, 'DBM::Deep', {
373 base_offset => $value_sector->offset,
374 staleness => $value_sector->staleness,
375 storage => $self->storage,
380 bless $value, 'DBM::Deep::Hash' unless Scalar::Util::blessed( $value );
390 # We're opening the file.
391 unless ( $obj->_base_offset ) {
392 my $bytes_read = $self->_read_file_header;
394 # Creating a new file
395 unless ( $bytes_read ) {
396 $self->_write_file_header;
398 # 1) Create Array/Hash entry
399 my $initial_reference = DBM::Deep::Sector::File::Reference->new({
403 $obj->{base_offset} = $initial_reference->offset;
404 $obj->{staleness} = $initial_reference->staleness;
406 $self->storage->flush;
408 # Reading from an existing file
410 $obj->{base_offset} = $bytes_read;
411 my $initial_reference = DBM::Deep::Sector::File::Reference->new({
413 offset => $obj->_base_offset,
415 unless ( $initial_reference ) {
416 DBM::Deep->_throw_error("Corrupted file, no master index record");
419 unless ($obj->_type eq $initial_reference->type) {
420 DBM::Deep->_throw_error("File type mismatch");
423 $obj->{staleness} = $initial_reference->staleness;
427 $self->storage->set_inode;
436 if ( $self->trans_id ) {
437 DBM::Deep->_throw_error( "Cannot begin_work within an active transaction" );
440 my @slots = $self->read_txn_slots;
442 for my $i ( 0 .. $#slots ) {
446 $self->set_trans_id( $i + 1 );
451 DBM::Deep->_throw_error( "Cannot allocate transaction ID" );
453 $self->write_txn_slots( @slots );
455 if ( !$self->trans_id ) {
456 DBM::Deep->_throw_error( "Cannot begin_work - no available transactions" );
466 if ( !$self->trans_id ) {
467 DBM::Deep->_throw_error( "Cannot rollback without an active transaction" );
470 # Each entry is the file location for a bucket that has a modification for
471 # this transaction. The entries need to be expunged.
472 foreach my $entry (@{ $self->get_entries } ) {
473 # Remove the entry here
474 my $read_loc = $entry
478 + ($self->trans_id - 1) * ( $self->byte_size + $STALE_SIZE );
480 my $data_loc = $self->storage->read_at( $read_loc, $self->byte_size );
481 $data_loc = unpack( $StP{$self->byte_size}, $data_loc );
482 $self->storage->print_at( $read_loc, pack( $StP{$self->byte_size}, 0 ) );
484 if ( $data_loc > 1 ) {
485 $self->load_sector( $data_loc )->free;
489 $self->clear_entries;
491 my @slots = $self->read_txn_slots;
492 $slots[$self->trans_id-1] = 0;
493 $self->write_txn_slots( @slots );
494 $self->inc_txn_staleness_counter( $self->trans_id );
495 $self->set_trans_id( 0 );
504 if ( !$self->trans_id ) {
505 DBM::Deep->_throw_error( "Cannot commit without an active transaction" );
508 foreach my $entry (@{ $self->get_entries } ) {
509 # Overwrite the entry in head with the entry in trans_id
514 my $head_loc = $self->storage->read_at( $base, $self->byte_size );
515 $head_loc = unpack( $StP{$self->byte_size}, $head_loc );
517 my $spot = $base + $self->byte_size + ($self->trans_id - 1) * ( $self->byte_size + $STALE_SIZE );
518 my $trans_loc = $self->storage->read_at(
519 $spot, $self->byte_size,
522 $self->storage->print_at( $base, $trans_loc );
523 $self->storage->print_at(
525 pack( $StP{$self->byte_size} . ' ' . $StP{$STALE_SIZE}, (0) x 2 ),
528 if ( $head_loc > 1 ) {
529 $self->load_sector( $head_loc )->free;
533 $self->clear_entries;
535 my @slots = $self->read_txn_slots;
536 $slots[$self->trans_id-1] = 0;
537 $self->write_txn_slots( @slots );
538 $self->inc_txn_staleness_counter( $self->trans_id );
539 $self->set_trans_id( 0 );
544 =head1 INTERNAL METHODS
546 The following methods are internal-use-only to DBM::Deep::Engine::File.
550 =head2 read_txn_slots()
552 This takes no arguments.
554 This will return an array with a 1 or 0 in each slot. Each spot represents one
555 available transaction. If the slot is 1, that transaction is taken. If it is 0,
556 the transaction is available.
562 my $bl = $self->txn_bitfield_len;
563 my $num_bits = $bl * 8;
564 return split '', unpack( 'b'.$num_bits,
565 $self->storage->read_at(
566 $self->trans_loc, $bl,
571 =head2 write_txn_slots( @slots )
573 This takes an array of 1's and 0's. This array represents the transaction slots
574 returned by L</read_txn_slots()>. In other words, the following is true:
576 @x = read_txn_slots( write_txn_slots( @x ) );
578 (With the obviously missing object referents added back in.)
582 sub write_txn_slots {
584 my $num_bits = $self->txn_bitfield_len * 8;
585 $self->storage->print_at( $self->trans_loc,
586 pack( 'b'.$num_bits, join('', @_) ),
590 =head2 get_running_txn_ids()
592 This takes no arguments.
594 This will return an array of taken transaction IDs. This wraps L</read_txn_slots()>.
598 sub get_running_txn_ids {
600 my @transactions = $self->read_txn_slots;
601 my @trans_ids = map { $_+1 } grep { $transactions[$_] } 0 .. $#transactions;
604 =head2 get_txn_staleness_counter( $trans_id )
606 This will return the staleness counter for the given transaction ID. Please see
607 L</TRANSACTION STALENESS> for more information.
611 sub get_txn_staleness_counter {
615 # Hardcode staleness of 0 for the HEAD
616 return 0 unless $trans_id;
618 return unpack( $StP{$STALE_SIZE},
619 $self->storage->read_at(
620 $self->trans_loc + $self->txn_bitfield_len + $STALE_SIZE * ($trans_id - 1),
626 =head2 inc_txn_staleness_counter( $trans_id )
628 This will increment the staleness counter for the given transaction ID. Please see
629 L</TRANSACTION STALENESS> for more information.
633 sub inc_txn_staleness_counter {
637 # Hardcode staleness of 0 for the HEAD
638 return 0 unless $trans_id;
640 $self->storage->print_at(
641 $self->trans_loc + $self->txn_bitfield_len + $STALE_SIZE * ($trans_id - 1),
642 pack( $StP{$STALE_SIZE}, $self->get_txn_staleness_counter( $trans_id ) + 1 ),
648 This takes no arguments.
650 This returns a list of all the sectors that have been modified by this transaction.
656 return [ keys %{ $self->{entries}{$self->trans_id} ||= {} } ];
659 =head2 add_entry( $trans_id, $location )
661 This takes a transaction ID and a file location and marks the sector at that
662 location as having been modified by the transaction identified by $trans_id.
664 This returns nothing.
666 B<NOTE>: Unlike all the other _entries() methods, there are several cases where
667 C<< $trans_id != $self->trans_id >> for this method.
673 my ($trans_id, $loc) = @_;
675 $self->{entries}{$trans_id} ||= {};
676 $self->{entries}{$trans_id}{$loc} = undef;
679 =head2 reindex_entry( $old_loc, $new_loc )
681 This takes two locations (old and new, respectively). If a location that has
682 been modified by this transaction is subsequently reindexed due to a bucketlist
683 overflowing, then the entries hash needs to be made aware of this change.
685 This returns nothing.
691 my ($old_loc, $new_loc) = @_;
694 while ( my ($trans_id, $locs) = each %{ $self->{entries} } ) {
695 if ( exists $locs->{$old_loc} ) {
696 delete $locs->{$old_loc};
697 $locs->{$new_loc} = undef;
703 =head2 clear_entries()
705 This takes no arguments. It will clear the entries list for the running
708 This returns nothing.
714 delete $self->{entries}{$self->trans_id};
717 =head2 _write_file_header()
719 This writes the file header for a new file. This will write the various settings
720 that set how the file is interpreted.
722 =head2 _read_file_header()
724 This reads the file header from an existing file. This will read the various
725 settings that set how the file is interpreted.
730 my $header_fixed = length( __PACKAGE__->SIG_FILE ) + 1 + 4 + 4;
731 my $this_file_version = 3;
733 sub _write_file_header {
736 my $nt = $self->num_txns;
737 my $bl = $self->txn_bitfield_len;
739 my $header_var = 1 + 1 + 1 + 1 + $bl + $STALE_SIZE * ($nt - 1) + 3 * $self->byte_size;
741 my $loc = $self->storage->request_space( $header_fixed + $header_var );
743 $self->storage->print_at( $loc,
746 pack('N', $this_file_version), # At this point, we're at 9 bytes
747 pack('N', $header_var), # header size
748 # --- Above is $header_fixed. Below is $header_var
749 pack('C', $self->byte_size),
751 # These shenanigans are to allow a 256 within a C
752 pack('C', $self->max_buckets - 1),
753 pack('C', $self->data_sector_size - 1),
756 pack('C' . $bl, 0 ), # Transaction activeness bitfield
757 pack($StP{$STALE_SIZE}.($nt-1), 0 x ($nt-1) ), # Transaction staleness counters
758 pack($StP{$self->byte_size}, 0), # Start of free chain (blist size)
759 pack($StP{$self->byte_size}, 0), # Start of free chain (data size)
760 pack($StP{$self->byte_size}, 0), # Start of free chain (index size)
763 #XXX Set these less fragilely
764 $self->set_trans_loc( $header_fixed + 4 );
765 $self->set_chains_loc( $header_fixed + 4 + $bl + $STALE_SIZE * ($nt-1) );
770 sub _read_file_header {
773 my $buffer = $self->storage->read_at( 0, $header_fixed );
774 return unless length($buffer);
776 my ($file_signature, $sig_header, $file_version, $size) = unpack(
780 unless ( $file_signature eq $self->SIG_FILE ) {
781 $self->storage->close;
782 DBM::Deep->_throw_error( "Signature not found -- file is not a Deep DB" );
785 unless ( $sig_header eq $self->SIG_HEADER ) {
786 $self->storage->close;
787 DBM::Deep->_throw_error( "Pre-1.00 file version found" );
790 unless ( $file_version == $this_file_version ) {
791 $self->storage->close;
792 DBM::Deep->_throw_error(
793 "Wrong file version found - " . $file_version .
794 " - expected " . $this_file_version
798 my $buffer2 = $self->storage->read_at( undef, $size );
799 my @values = unpack( 'C C C C', $buffer2 );
801 if ( @values != 4 || grep { !defined } @values ) {
802 $self->storage->close;
803 DBM::Deep->_throw_error("Corrupted file - bad header");
806 #XXX Add warnings if values weren't set right
807 @{$self}{qw(byte_size max_buckets data_sector_size num_txns)} = @values;
809 # These shenangians are to allow a 256 within a C
810 $self->{max_buckets} += 1;
811 $self->{data_sector_size} += 1;
813 my $bl = $self->txn_bitfield_len;
815 my $header_var = scalar(@values) + $bl + $STALE_SIZE * ($self->num_txns - 1) + 3 * $self->byte_size;
816 unless ( $size == $header_var ) {
817 $self->storage->close;
818 DBM::Deep->_throw_error( "Unexpected size found ($size <-> $header_var)." );
821 $self->set_trans_loc( $header_fixed + scalar(@values) );
822 $self->set_chains_loc( $header_fixed + scalar(@values) + $bl + $STALE_SIZE * ($self->num_txns - 1) );
824 return length($buffer) + length($buffer2);
828 =head2 _apply_digest( @stuff )
830 This will apply the digest methd (default to Digest::MD5::md5) to the arguments
831 passed in and return the result.
837 return $self->{digest}->(@_);
840 =head2 _add_free_blist_sector( $offset, $size )
842 =head2 _add_free_data_sector( $offset, $size )
844 =head2 _add_free_index_sector( $offset, $size )
846 These methods are all wrappers around _add_free_sector(), providing the proper
847 chain offset ($multiple) for the sector type.
851 sub _add_free_blist_sector { shift->_add_free_sector( 0, @_ ) }
852 sub _add_free_data_sector { shift->_add_free_sector( 1, @_ ) }
853 sub _add_free_index_sector { shift->_add_free_sector( 2, @_ ) }
855 =head2 _add_free_sector( $multiple, $offset, $size )
857 _add_free_sector() takes the offset into the chains location, the offset of the
858 sector, and the size of that sector. It will mark the sector as a free sector
859 and put it into the list of sectors that are free of this type for use later.
861 This returns nothing.
863 B<NOTE>: $size is unused?
867 sub _add_free_sector {
869 my ($multiple, $offset, $size) = @_;
871 my $chains_offset = $multiple * $self->byte_size;
873 my $storage = $self->storage;
875 # Increment staleness.
876 # XXX Can this increment+modulo be done by "&= 0x1" ?
877 my $staleness = unpack( $StP{$STALE_SIZE}, $storage->read_at( $offset + $self->SIG_SIZE, $STALE_SIZE ) );
878 $staleness = ($staleness + 1 ) % ( 2 ** ( 8 * $STALE_SIZE ) );
879 $storage->print_at( $offset + $self->SIG_SIZE, pack( $StP{$STALE_SIZE}, $staleness ) );
881 my $old_head = $storage->read_at( $self->chains_loc + $chains_offset, $self->byte_size );
883 $storage->print_at( $self->chains_loc + $chains_offset,
884 pack( $StP{$self->byte_size}, $offset ),
887 # Record the old head in the new sector after the signature and staleness counter
888 $storage->print_at( $offset + $self->SIG_SIZE + $STALE_SIZE, $old_head );
891 =head2 _request_blist_sector( $size )
893 =head2 _request_data_sector( $size )
895 =head2 _request_index_sector( $size )
897 These methods are all wrappers around _request_sector(), providing the proper
898 chain offset ($multiple) for the sector type.
902 sub _request_blist_sector { shift->_request_sector( 0, @_ ) }
903 sub _request_data_sector { shift->_request_sector( 1, @_ ) }
904 sub _request_index_sector { shift->_request_sector( 2, @_ ) }
906 =head2 _request_sector( $multiple $size )
908 This takes the offset into the chains location and the size of that sector.
910 This returns the object with the sector. If there is an available free sector of
911 that type, then it will be reused. If there isn't one, then a new one will be
916 sub _request_sector {
918 my ($multiple, $size) = @_;
920 my $chains_offset = $multiple * $self->byte_size;
922 my $old_head = $self->storage->read_at( $self->chains_loc + $chains_offset, $self->byte_size );
923 my $loc = unpack( $StP{$self->byte_size}, $old_head );
925 # We don't have any free sectors of the right size, so allocate a new one.
927 my $offset = $self->storage->request_space( $size );
929 # Zero out the new sector. This also guarantees correct increases
931 $self->storage->print_at( $offset, chr(0) x $size );
936 # Read the new head after the signature and the staleness counter
937 my $new_head = $self->storage->read_at( $loc + $self->SIG_SIZE + $STALE_SIZE, $self->byte_size );
938 $self->storage->print_at( $self->chains_loc + $chains_offset, $new_head );
939 $self->storage->print_at(
940 $loc + $self->SIG_SIZE + $STALE_SIZE,
941 pack( $StP{$self->byte_size}, 0 ),
949 The following are readonly attributes.
965 =item * data_sector_size
967 =item * txn_bitfield_len
973 sub byte_size { $_[0]{byte_size} }
974 sub hash_size { $_[0]{hash_size} }
975 sub hash_chars { $_[0]{hash_chars} }
976 sub num_txns { $_[0]{num_txns} }
977 sub max_buckets { $_[0]{max_buckets} }
978 sub blank_md5 { chr(0) x $_[0]->hash_size }
979 sub data_sector_size { $_[0]{data_sector_size} }
981 # This is a calculated value
982 sub txn_bitfield_len {
984 unless ( exists $self->{txn_bitfield_len} ) {
985 my $temp = ($self->num_txns) / 8;
986 if ( $temp > int( $temp ) ) {
987 $temp = int( $temp ) + 1;
989 $self->{txn_bitfield_len} = $temp;
991 return $self->{txn_bitfield_len};
996 The following are read/write attributes.
1000 =item * trans_id / set_trans_id( $new_id )
1002 =item * trans_loc / set_trans_loc( $new_loc )
1004 =item * chains_loc / set_chains_loc( $new_loc )
1010 sub trans_id { $_[0]{trans_id} }
1011 sub set_trans_id { $_[0]{trans_id} = $_[1] }
1013 sub trans_loc { $_[0]{trans_loc} }
1014 sub set_trans_loc { $_[0]{trans_loc} = $_[1] }
1016 sub chains_loc { $_[0]{chains_loc} }
1017 sub set_chains_loc { $_[0]{chains_loc} = $_[1] }
1019 sub cache { $_[0]{cache} ||= {} }
1020 sub clear_cache { %{$_[0]->cache} = () }
1024 This method takes no arguments. It's used to print out a textual representation
1025 of the DBM::Deep DB file. It assumes the file is not-corrupted.
1033 my $spot = $self->_read_file_header();
1042 'D' => $self->data_sector_size,
1043 'B' => DBM::Deep::Sector::File::BucketList->new({engine=>$self,offset=>1})->size,
1044 'I' => DBM::Deep::Sector::File::Index->new({engine=>$self,offset=>1})->size,
1050 $return .= "NumTxns: " . $self->num_txns . $/;
1052 # Read the free sector chains
1054 foreach my $multiple ( 0 .. 2 ) {
1055 $return .= "Chains($types{$multiple}):";
1056 my $old_loc = $self->chains_loc + $multiple * $self->byte_size;
1059 $StP{$self->byte_size},
1060 $self->storage->read_at( $old_loc, $self->byte_size ),
1063 # We're now out of free sectors of this kind.
1068 $sectors{ $types{$multiple} }{ $loc } = undef;
1069 $old_loc = $loc + $self->SIG_SIZE + $STALE_SIZE;
1076 while ( $spot < $self->storage->{end} ) {
1077 # Read each sector in order.
1078 my $sector = $self->load_sector( $spot );
1080 # Find it in the free-sectors that were found already
1081 foreach my $type ( keys %sectors ) {
1082 if ( exists $sectors{$type}{$spot} ) {
1083 my $size = $sizes{$type};
1084 $return .= sprintf "%08d: %s %04d\n", $spot, 'F' . $type, $size;
1090 die "********\n$return\nDidn't find free sector for $spot in chains\n********\n";
1093 $return .= sprintf "%08d: %s %04d", $spot, $sector->type, $sector->size;
1094 if ( $sector->type eq 'D' ) {
1095 $return .= ' ' . $sector->data;
1097 elsif ( $sector->type eq 'A' || $sector->type eq 'H' ) {
1098 $return .= ' REF: ' . $sector->get_refcount;
1100 elsif ( $sector->type eq 'B' ) {
1101 foreach my $bucket ( $sector->chopped_up ) {
1103 $return .= sprintf "%08d", unpack($StP{$self->byte_size},
1104 substr( $bucket->[-1], $self->hash_size, $self->byte_size),
1106 my $l = unpack( $StP{$self->byte_size},
1107 substr( $bucket->[-1],
1108 $self->hash_size + $self->byte_size,
1112 $return .= sprintf " %08d", $l;
1113 foreach my $txn ( 0 .. $self->num_txns - 2 ) {
1114 my $l = unpack( $StP{$self->byte_size},
1115 substr( $bucket->[-1],
1116 $self->hash_size + 2 * $self->byte_size + $txn * ($self->byte_size + $STALE_SIZE),
1120 $return .= sprintf " %08d", $l;
1126 $spot += $sector->size;