# XXX Need to add logic about resetting the iterator if any key in the reference has changed
unless ( $prev_key ) {
- $obj->{iterator} = DBM::Deep::Engine::Iterator->new({
+ $obj->{iterator} = DBM::Deep::Iterator->new({
base_offset => $obj->_base_offset,
engine => $self,
});
my $self = shift;
my ($offset) = @_;
+ # Add a catch for offset of 0 or 1
+ return if $offset <= 1;
+
my $type = $self->storage->read_at( $offset, 1 );
return if $type eq chr(0);
################################################################################
-package DBM::Deep::Engine::Iterator;
+package DBM::Deep::Iterator;
sub new {
my $class = shift;
return $self;
}
-sub reset {
+sub reset { $_[0]{breadcrumbs} = [] }
+
+sub get_sector_iterator {
my $self = shift;
- $self->{breadcrumbs} = [];
+ my ($loc) = @_;
+
+ my $sector = $self->{engine}->_load_sector( $loc )
+ or return;
+
+ if ( $sector->isa( 'DBM::Deep::Engine::Sector::Index' ) ) {
+ return DBM::Deep::Iterator::Index->new({
+ iterator => $self,
+ sector => $sector,
+ });
+ }
+ elsif ( $sector->isa( 'DBM::Deep::Engine::Sector::BucketList' ) ) {
+ return DBM::Deep::Iterator::BucketList->new({
+ iterator => $self,
+ sector => $sector,
+ });
+ }
+ else {
+ die "Why did $loc make a $sector?";
+ }
}
sub get_next_key {
my ($obj) = @_;
my $crumbs = $self->{breadcrumbs};
+ my $e = $self->{engine};
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 ref sector for '$self->{base_offset}')?!\n";
+ my $sector = $e->_load_sector( $self->{base_offset} )
# If no sector is found, thist must have been deleted from under us.
or return;
return;
}
- push @$crumbs, [ $sector->get_blist_loc, 0 ];
+ my $loc = $sector->get_blist_loc
+ or return;
+
+ push @$crumbs, $self->get_sector_iterator( $loc );
}
- my $key;
- while ( 1 ) {
- my ($offset, $idx) = @{ $crumbs->[-1] };
- unless ( $offset ) {
+ FIND_NEXT_KEY: {
+ # We're at the end.
+ unless ( @$crumbs ) {
$self->reset;
- last;
+ return;
}
- if ( $idx >= $self->{engine}->max_buckets ) {
- $self->reset;
- last;
+ my $iterator = $crumbs->[-1];
+
+ # This level is done.
+ if ( $iterator->at_end ) {
+ pop @$crumbs;
+ redo FIND_NEXT_KEY;
}
- my $sector = $self->{engine}->_load_sector( $offset )
- or die "Iterator: How did this fail (no blist sector for '$offset')?!\n";
+ if ( $iterator->isa( 'DBM::Deep::Iterator::Index' ) ) {
+ # If we don't have any more, it will be caught at the
+ # prior check.
+ if ( my $next = $iterator->get_next_iterator ) {
+ push @$crumbs, $next;
+ }
+ redo FIND_NEXT_KEY;
+ }
- #XXX Think this through!
- my $loc = $sector->get_data_location_for({
- idx => $idx,
- allow_head => 1,
- });
- unless ( $loc ) {
- $crumbs->[-1][1]++;
- next;
+ unless ( $iterator->isa( 'DBM::Deep::Iterator::BucketList' ) ) {
+ DBM::Deep->_throw_error(
+ "Should have a bucketlist iterator here - instead have $iterator"
+ );
}
- my $key_sector = $sector->get_key_for( $idx );
- unless ( $key_sector ) {
- $self->reset;
- last;
+ # At this point, we have a BucketList iterator
+ my $key = $iterator->get_next_key;
+ if ( defined $key ) {
+ return $key;
}
- $crumbs->[-1][1]++;
- $key = $key_sector->data;
- last;
+ # We hit the end of the bucketlist iterator, so redo
+ redo FIND_NEXT_KEY;
+ }
+
+ DBM::Deep->_throw_error( "get_next_key(): How did we get here?" );
+}
+
+package DBM::Deep::Iterator::Index;
+
+sub new {
+ my $self = bless $_[1] => $_[0];
+ $self->{curr_index} = 0;
+ return $self;
+}
+
+sub at_end {
+ my $self = shift;
+ return $self->{curr_index} >= $self->{iterator}{engine}->hash_chars;
+}
+
+sub get_next_iterator {
+ my $self = shift;
+
+ my $loc;
+ while ( !$loc ) {
+ return if $self->at_end;
+ $loc = $self->{sector}->get_entry( $self->{curr_index}++ );
}
- return $key;
+ return $self->{iterator}->get_sector_iterator( $loc );
+}
+
+package DBM::Deep::Iterator::BucketList;
+
+sub new {
+ my $self = bless $_[1] => $_[0];
+ $self->{curr_index} = 0;
+ return $self;
+}
+
+sub at_end {
+ my $self = shift;
+ return $self->{curr_index} >= $self->{iterator}{engine}->max_buckets;
+}
+
+sub get_next_key {
+ my $self = shift;
+
+ return if $self->at_end;
+
+ my $key_sector = $self->{sector}->get_key_for( $self->{curr_index}++ );
+ return unless $key_sector;
+
+ return $key_sector->data;
}
package DBM::Deep::Engine::Sector;
my $blist = $self->get_bucket_list({
key_md5 => $args->{key_md5},
+ key => $args->{key},
create => $args->{create},
});
return unless $blist && $blist->{found};
my $blist = $self->get_bucket_list({
key_md5 => $args->{key_md5},
+ key => $args->{key},
create => 1,
}) or die "How did write_data fail (no blist)?!\n";
});
my %blist_cache;
-
- foreach my $md5 ( $args->{key_md5}, $sector->chopped_up ) {
+ foreach my $md5 ( $sector->chopped_up ) {
my $idx = ord( substr( $md5, $i, 1 ) );
+ # XXX This is inefficient
my $blist = $blist_cache{$idx}
||= DBM::Deep::Engine::Sector::BucketList->new({
engine => $engine,
$blist->write_at_next_open( $md5 );
}
+ # Handle the new item separately.
+ {
+ my $idx = ord( substr( $args->{key_md5}, $i, 1 ) );
+ my $blist = $blist_cache{$idx}
+ ||= DBM::Deep::Engine::Sector::BucketList->new({
+ engine => $engine,
+ });
+
+ $new_index->set_entry( $idx => $blist->offset );
+
+ #XXX THIS IS HACKY!
+ $blist->find_md5( $args->{key_md5} );
+ $blist->write_md5({
+ key => $args->{key},
+ key_md5 => $args->{key_md5},
+ value => DBM::Deep::Engine::Sector::Null->new({
+ engine => $engine,
+ data => undef,
+ }),
+ });
+ }
+
if ( $last_sector ) {
$last_sector->set_entry(
ord( substr( $args->{key_md5}, $i - 1, 1 ) ),
my ($idx) = @_;
$idx = $self->{idx} unless defined $idx;
+ if ( $idx >= $self->engine->max_buckets ) {
+ DBM::Deep->_throw_error( "get_key_for(): Attempting to retrieve $idx" );
+ }
+
my $location = $self->engine->storage->read_at(
$self->offset + $self->base_size + $idx * $self->bucket_size + $self->engine->hash_size,
$self->engine->byte_size,
my $e = $self->engine;
+ die "get_entry: Out of range ($idx)"
+ if $idx < 0 || $idx >= $e->hash_chars;
+
return unpack(
$StP{$e->byte_size},
$e->storage->read_at( $self->_loc_for( $idx ), $e->byte_size ),
my $self = shift;
my ($idx, $loc) = @_;
+ my $e = $self->engine;
+
+ die "set_entry: Out of range ($idx)"
+ if $idx < 0 || $idx >= $e->hash_chars;
+
$self->engine->storage->print_at(
$self->_loc_for( $idx ),
- pack( $StP{$self->engine->byte_size}, $loc ),
+ pack( $StP{$e->byte_size}, $loc ),
);
}