Fixed how header_var was set during _read_file_header so that a validation is more...
rkinyon [Mon, 4 Dec 2006 02:40:00 +0000 (02:40 +0000)]
lib/DBM/Deep/Engine3.pm
t/37_delete_edge_cases.t
t/lib/Test1.pm [deleted file]
t/lib/Test2.pm [deleted file]
t/lib/TestBase.pm [deleted file]
t/lib/TestSimpleArray.pm [deleted file]
t/lib/TestSimpleHash.pm [deleted file]
t/run.t [deleted file]

index 002a005..19f0849 100644 (file)
@@ -95,6 +95,9 @@ sub new {
         $self->{digest} = \&Digest::MD5::md5;
     }
 
+    #XXX HACK
+    $self->{chains_loc} = 15;
+
     return $self;
 }
 
@@ -309,7 +312,7 @@ sub _write_file_header {
     my $self = shift;
 
     my $header_fixed = length( SIG_FILE ) + 1 + 4 + 4;
-    my $header_var = 1 + 1;
+    my $header_var = 1 + 1 + 2 * $self->byte_size;
 
     my $loc = $self->storage->request_space( $header_fixed + $header_var );
 
@@ -321,9 +324,13 @@ sub _write_file_header {
         # --- Above is $header_fixed. Below is $header_var
         pack('C', $self->byte_size),
         pack('C', $self->max_buckets),
+        pack($StP{$self->byte_size}, 0), # Start of free chain (blist size)
+        pack($StP{$self->byte_size}, 0), # Start of free chain (data size)
     );
 
-    $self->storage->set_transaction_offset( 13 );
+    $self->set_chains_loc( $header_fixed + 2 );
+
+#    $self->storage->set_transaction_offset( $header_fixed );
 
     return;
 }
@@ -332,7 +339,6 @@ sub _read_file_header {
     my $self = shift;
 
     my $header_fixed = length( SIG_FILE ) + 1 + 4 + 4;
-    my $header_var = 1 + 1;
 
     my $buffer = $self->storage->read_at( 0, $header_fixed );
     return unless length($buffer);
@@ -351,16 +357,13 @@ sub _read_file_header {
         DBM::Deep->_throw_error( "Old file version found." );
     }
 
-    unless ( $size eq $header_var ) {
-        $self->storage->close;
-        DBM::Deep->_throw_error( "Unexpected size found." );
-    }
-
     my $buffer2 = $self->storage->read_at( undef, $size );
     my @values = unpack( 'C C', $buffer2 );
 
+    $self->set_chains_loc( $header_fixed + 2 );
+
     # The transaction offset is the first thing after the fixed header section
-    $self->storage->set_transaction_offset( $header_fixed );
+    #$self->storage->set_transaction_offset( $header_fixed );
 
     if ( @values < 2 || grep { !defined } @values ) {
         $self->storage->close;
@@ -370,6 +373,12 @@ sub _read_file_header {
     #XXX Add warnings if values weren't set right
     @{$self}{qw(byte_size max_buckets)} = @values;
 
+    my $header_var = 1 + 1 + 2 * $self->byte_size;
+    unless ( $size eq $header_var ) {
+        $self->storage->close;
+        DBM::Deep->_throw_error( "Unexpected size found ($size <-> $header_var)." );
+    }
+
     return length($buffer) + length($buffer2);
 }
 
@@ -378,6 +387,8 @@ sub _load_sector {
     my ($offset) = @_;
 
     my $type = $self->storage->read_at( $offset, 1 );
+    return if $type eq chr(0);
+
     if ( $type eq $self->SIG_ARRAY || $type eq $self->SIG_HASH ) {
         return DBM::Deep::Engine::Sector::Reference->new({
             engine => $self,
@@ -406,6 +417,10 @@ sub _load_sector {
             offset => $offset,
         });
     }
+    # This was deleted from under us, so just return and let the caller figure it out.
+    elsif ( $type eq $self->SIG_FREE ) {
+        return;
+    }
 
     die "'$offset': Don't know what to do with type '$type'\n";
 }
@@ -418,6 +433,53 @@ sub _apply_digest {
 sub _add_free_sector {
     my $self = shift;
     my ($offset, $size) = @_;
+
+    my $chains_offset;
+    # Data sector
+    if ( $size == 256 ) {
+        $chains_offset = $self->byte_size;
+    }
+    # Blist sector
+    else {
+        $chains_offset = 0;
+    }
+
+    my $old_head = $self->storage->read_at( $self->chains_loc + $chains_offset, $self->byte_size );
+
+    $self->storage->print_at( $self->chains_loc + $offset,
+        pack( $StP{$self->byte_size}, $offset ),
+    );
+
+    # Record the old head in the new sector after the signature
+    $self->storage->print_at( $offset + 1, $old_head );
+}
+
+sub _request_sector {
+    my $self = shift;
+    my ($size) = @_;
+
+    my $chains_offset;
+    # Data sector
+    if ( $size == 256 ) {
+        $chains_offset = $self->byte_size;
+    }
+    # Blist sector
+    else {
+        $chains_offset = 0;
+    }
+
+    my $old_head = $self->storage->read_at( $self->chains_loc + $chains_offset, $self->byte_size );
+    my $loc = unpack( $StP{$self->byte_size}, $old_head );
+
+    # We don't have any free sectors of the right size, so allocate a new one.
+    unless ( $loc ) {
+        return $self->storage->request_space( $size );
+    }
+
+    my $new_head = $self->storage->read_at( $loc + 1, $self->byte_size );
+    $self->storage->print_at( $self->chains_loc + $chains_offset, $new_head );
+
+    return $loc;
 }
 
 ################################################################################
@@ -430,6 +492,9 @@ sub max_buckets { $_[0]{max_buckets} }
 sub iterator    { $_[0]{iterator} }
 sub blank_md5   { chr(0) x $_[0]->hash_size }
 
+sub chains_loc     { $_[0]{chains_loc} }
+sub set_chains_loc { $_[0]{chains_loc} = $_[1] }
+
 ################################################################################
 
 package DBM::Deep::Engine::Iterator;
@@ -463,7 +528,9 @@ sub get_next_key {
     unless ( @$crumbs ) {
         # This will be a Reference sector
         my $sector = $self->{engine}->_load_sector( $self->{base_offset} )
-            or die "Iterator: How did this fail (no sector for '$self->{base_offset}')?!\n";
+            # or die "Iterator: How did this fail (no ref sector for '$self->{base_offset}')?!\n";
+            # If no sector is found, thist must have been deleted from under us.
+            or return;
         push @$crumbs, [ $sector->get_blist_loc, 0 ];
     }
 
@@ -476,7 +543,7 @@ sub get_next_key {
         }
 
         my $sector = $self->{engine}->_load_sector( $offset )
-            or die "Iterator: How did this fail (no sector for '$offset')?!\n";
+            or die "Iterator: How did this fail (no blist sector for '$offset')?!\n";
 
         my $key_sector = $sector->get_key_for( $idx );
         unless ( $key_sector ) {
@@ -509,14 +576,16 @@ sub type   { $_[0]{type} }
 sub free {
     my $self = shift;
 
-    return;
+    $self->engine->storage->print_at( $self->offset,
+        $self->engine->SIG_FREE,
+        chr(0) x ($self->size - 1),
+    );
+
     $self->engine->_add_free_sector(
         $self->offset, $self->size,
     );
 
-    $self->engine->storage->print_at( $self->offset,
-        chr(0) x $self->size,
-    );
+    return;
 }
 
 package DBM::Deep::Engine::Sector::Data;
@@ -545,7 +614,7 @@ sub _init {
         #XXX This assumes that length($data) > $leftover
         $leftover -= length( $data );
 
-        $self->{offset} = $engine->storage->request_space( $self->size );
+        $self->{offset} = $engine->_request_sector( $self->size );
         $engine->storage->print_at( $self->offset,
             $self->type,                          # Sector type
             pack( $StP{1}, 0 ),                   # Recycled counter
@@ -592,7 +661,7 @@ sub _init {
     unless ( $self->offset ) {
         my $leftover = $self->size - 3 - 1 * $engine->byte_size;
 
-        $self->{offset} = $engine->storage->request_space( $self->size );
+        $self->{offset} = $engine->_request_sector( $self->size );
         $engine->storage->print_at( $self->offset,
             $self->type,                          # Sector type
             pack( $StP{1}, 0 ),                   # Recycled counter
@@ -617,7 +686,7 @@ sub _init {
     unless ( $self->offset ) {
         my $leftover = $self->size - 4 - 2 * $engine->byte_size;
 
-        $self->{offset} = $engine->storage->request_space( $self->size );
+        $self->{offset} = $engine->_request_sector( $self->size );
         $engine->storage->print_at( $self->offset,
             $self->type,                         # Sector type
             pack( $StP{1}, 0 ),                  # Recycled counter
@@ -700,7 +769,7 @@ sub _init {
     unless ( $self->offset ) {
         my $leftover = $self->size - $self->base_size;
 
-        $self->{offset} = $engine->storage->request_space( $self->size );
+        $self->{offset} = $engine->_request_sector( $self->size );
         $engine->storage->print_at( $self->offset,
             $engine->SIG_BLIST, # Sector type
             pack( $StP{1}, 0 ), # Recycled counter
index 6638372..82d95ea 100644 (file)
@@ -27,6 +27,6 @@ delete $db->{foo};
 
 TODO: {
     local $TODO = "Delete isn't working right";
-ok( !tied(%$x), "\$x is NOT tied" );
-cmp_deeply( $x, $x_save, "When it's deleted, it's untied" );
+    ok( !tied(%$x), "\$x is NOT tied" );
+    cmp_deeply( $x, $x_save, "When it's deleted, it's untied" );
 }
diff --git a/t/lib/Test1.pm b/t/lib/Test1.pm
deleted file mode 100644 (file)
index adfe9ba..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package Test1;
-
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use base 'TestBase';
-use base 'TestSimpleHash';
-
-#sub setup : Test(startup) {
-#    my $self = shift;
-#
-#    $self->{db} = DBM::Deep->new( $self->new_file );
-#
-#    return;
-#}
-
-1;
-__END__
diff --git a/t/lib/Test2.pm b/t/lib/Test2.pm
deleted file mode 100644 (file)
index b4cde50..0000000
+++ /dev/null
@@ -1,20 +0,0 @@
-package Test2;
-
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use base 'TestBase';
-use base 'TestSimpleArray';
-
-#sub setup : Test(startup) {
-#    my $self = shift;
-#
-#    $self->{db} = DBM::Deep->new( $self->new_file );
-#
-#    return;
-#}
-
-1;
-__END__
diff --git a/t/lib/TestBase.pm b/t/lib/TestBase.pm
deleted file mode 100644 (file)
index 95ee9fb..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package TestBase;
-
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use Fcntl qw( :flock );
-use File::Path ();
-use File::Temp ();
-use Scalar::Util ();
-
-use base 'Test::Class';
-
-use DBM::Deep;
-
-sub setup_db : Test(startup) {
-    my $self = shift;
-
-    my $data = ($self->{data} ||= {});
-
-    my $r = Scalar::Util::reftype( $data );
-    my $type = $r eq 'HASH' ? DBM::Deep->TYPE_HASH : DBM::Deep->TYPE_ARRAY;
-
-    $self->{db} = DBM::Deep->new({
-        file => $self->new_file,
-        type => $type,
-    });
-
-    return;
-}
-
-sub setup_dir : Test(startup) {
-    my $self = shift;
-
-    $self->{workdir} ||= File::Temp::tempdir();
-
-    return;
-}
-
-sub new_file {
-    my $self = shift;
-
-    $self->setup_dir;
-
-    my ($fh, $filename) = File::Temp::tempfile(
-        'tmpXXXX', DIR => $self->{workdir}, CLEANUP => 1,
-    );
-    flock( $fh, LOCK_UN );
-
-    return $filename;
-}
-
-sub remove_dir : Test(shutdown) {
-    my $self = shift;
-
-    File::Path::rmtree( $self->{workdir} );
-
-    return;
-}
-
-1;
-__END__
diff --git a/t/lib/TestSimpleArray.pm b/t/lib/TestSimpleArray.pm
deleted file mode 100644 (file)
index 1c0d55b..0000000
+++ /dev/null
@@ -1,51 +0,0 @@
-package TestSimpleArray;
-
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use Test::More;
-use Test::Exception;
-
-use base 'TestBase';
-
-sub A_assignment : Test( 37 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my @keys = 0 .. $#{$self->{data}};
-
-    push @keys, $keys[0] while @keys < 5;
-
-    cmp_ok( @$db, '==', 0 );
-
-    foreach my $k ( @keys[0..4] ) {
-        ok( !exists $db->[$k] );
-        ok( !$db->exists( $k ) );
-    }
-
-    $db->[$keys[0]] = $self->{data}[$keys[1]];
-    $db->push( $self->{data}[$keys[2]] );
-    $db->put( $keys[2] => $self->{data}[$keys[3]] );
-    $db->store( $keys[3] => $self->{data}[$keys[4]] );
-    $db->unshift( $self->{data}[$keys[0]] );
-
-    foreach my $k ( @keys[0..4] ) {
-        ok( $db->exists( $k ) );
-        ok( exists $db->[$k] );
-
-        is( $db->[$k], $self->{data}[$k] );
-        is( $db->get($k), $self->{data}[$k] );
-        is( $db->fetch($k), $self->{data}[$k] );
-    }
-
-    if ( @keys > 5 ) {
-        $db->[$_] = $self->{data}[$_] for @keys[5..$#keys];
-    }
-
-    cmp_ok( @$db, '==', @keys );
-}
-
-1;
-__END__
diff --git a/t/lib/TestSimpleHash.pm b/t/lib/TestSimpleHash.pm
deleted file mode 100644 (file)
index fdfbeb0..0000000
+++ /dev/null
@@ -1,150 +0,0 @@
-package TestSimpleHash;
-
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use Test::More;
-use Test::Exception;
-
-use base 'TestBase';
-
-sub A_assignment : Test( 23 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my @keys = keys %{$self->{data}};
-
-    push @keys, $keys[0] while @keys < 3;
-
-    cmp_ok( keys %$db, '==', 0 );
-
-    foreach my $k ( @keys[0..2] ) {
-        ok( !exists $db->{$k} );
-        ok( !$db->exists( $k ) );
-    }
-
-    $db->{$keys[0]} = $self->{data}{$keys[0]};
-    $db->put( $keys[1] => $self->{data}{$keys[1]} );
-    $db->store( $keys[2] => $self->{data}{$keys[2]} );
-
-    foreach my $k ( @keys[0..2] ) {
-        ok( $db->exists( $k ) );
-        ok( exists $db->{$k} );
-
-        is( $db->{$k}, $self->{data}{$k} );
-        is( $db->get($k), $self->{data}{$k} );
-        is( $db->fetch($k), $self->{data}{$k} );
-    }
-
-    if ( @keys > 3 ) {
-        $db->{$_} = $self->{data}{$_} for @keys[3..$#keys];
-    }
-
-    cmp_ok( keys %$db, '==', @keys );
-}
-
-sub B_check_keys : Test( 1 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my @control = sort keys %{$self->{data}};
-    my @test1 = sort keys %$db;
-    is_deeply( \@test1, \@control );
-}
-
-sub C_each : Test( 1 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my $temp = {};
-    while ( my ($k,$v) = each %$db ) {
-        $temp->{$k} = $v;
-    }
-
-    is_deeply( $temp, $self->{data} );
-}
-
-sub D_firstkey : Test( 1 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my $temp = {};
-
-    my $key = $db->first_key;
-    while ( $key ) {
-        $temp->{$key} = $db->get( $key );
-        $key = $db->next_key( $key );
-    }
-
-    is_deeply( $temp, $self->{data} );
-}
-
-sub E_delete : Test( 12 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my @keys = keys %{$self->{data}};
-    cmp_ok( keys %$db, '==', @keys );
-
-    my $key1 = $keys[0];
-    ok( exists $db->{$key1} );
-    is( $db->{$key1}, $self->{data}{$key1} );
-    is( delete $db->{$key1}, $self->{data}{$key1} );
-    ok( !exists $db->{$key1} );
-    cmp_ok( keys %$db, '==', @keys - 1 );
-
-    my $key2 = $keys[1];
-    ok( exists $db->{$key2} );
-    is( $db->{$key2}, $self->{data}{$key2} );
-    is( $db->delete( $key2 ), $self->{data}{$key2} );
-    ok( !exists $db->{$key2} );
-    cmp_ok( keys %$db, '==', @keys - 2 );
-
-    @{$db}{ @keys[0,1] } = @{$self->{data}}{@keys[0,1]};
-
-    cmp_ok( keys %$db, '==', @keys );
-}
-
-sub F_clear : Test( 3 ) {
-    my $self = shift;
-    my $db = $self->{db};
-
-    my @keys = keys %{$self->{data}};
-    cmp_ok( keys %$db, '==', @keys );
-
-    %$db = ();
-
-    cmp_ok( keys %$db, '==', 0 );
-
-    %$db = %{$self->{data}};
-    cmp_ok( keys %$db, '==', @keys );
-}
-
-sub G_reassign_and_close : Test( 4 ) {
-    my $self = shift;
-
-    my @keys = keys %{$self->{data}};
-
-    my $key1 = $keys[0];
-
-    my $long_value = 'long value' x 100;
-    $self->{db}{$key1} = $long_value;
-    is( $self->{db}{$key1}, $long_value );
-
-    my $filename = $self->{db}->_root->{file};
-    undef $self->{db};
-
-    $self->{db} = DBM::Deep->new( $filename );
-
-    is( $self->{db}{$key1}, $long_value );
-
-    $self->{db}{$key1} = $self->{data}{$key1};
-    is( $self->{db}{$key1}, $self->{data}{$key1} );
-
-    cmp_ok( keys %{$self->{db}}, '==', @keys );
-}
-
-1;
-__END__
diff --git a/t/run.t b/t/run.t
deleted file mode 100644 (file)
index cdd89f3..0000000
--- a/t/run.t
+++ /dev/null
@@ -1,37 +0,0 @@
-use 5.6.0;
-
-use strict;
-use warnings;
-
-use lib 't/lib';
-
-use DBM::Deep;
-
-use Test1;
-use Test2;
-
-my $test1 = Test1->new(
-    data => {
-        key1 => 'value1',
-        key2 => undef,
-        key3 => 1.23,
-    },
-);
-
-my %test2;
-$test2{"key $_"} = "value $_" for 1 .. 4000;
-
-my $test2 = Test1->new(
-    data => \%test2,
-);
-
-my $test3 = Test2->new(
-    data => [
-        1 .. 5,
-    ],
-);
-
-Test::Class->runtests(
-    $test1,
-    $test3,
-);