digest => undef,
hash_size => 16, # In bytes
max_buckets => 16,
+ num_txns => 16, # HEAD plus 15 running txns
storage => undef,
obj => undef,
sub read_value {
my $self = shift;
my ($trans_id, $base_offset, $key) = @_;
+
+ # This will be a Reference sector
+ my $sector = $self->_load_sector( $base_offset );
+ die "How did this fail (no sector for '$base_offset')?!\n" unless $sector;
+
+ my $key_md5 = $self->_apply_digest( $key );
+
+ # XXX What should happen if this fails?
+ my $blist = $sector->get_bucket_list({
+ key_md5 => $key_md5,
+ });
+ die "How did this fail (no blist)?!\n" unless $blist;
+
+ my $value_sector = $blist->get_data_for( $key_md5 );
+
+ return $value_sector->data;
}
sub key_exists {
sub write_value {
my $self = shift;
my ($trans_id, $base_offset, $key, $value) = @_;
+
+ # This will be a Reference sector
+ my $sector = $self->_load_sector( $base_offset );
+ die "How did this fail (no sector for '$base_offset')?!\n" unless $sector;
+
+ my $key_md5 = $self->_apply_digest( $key );
+
+ # XXX What should happen if this fails?
+ my $blist = $sector->get_bucket_list({
+ key_md5 => $key_md5,
+ create => 1,
+ });
+ die "How did this fail (no blist)?!\n" unless $blist;
+
+ my $value_sector = DBM::Deep::Engine::Sector::Scalar->new({
+ engine => $self,
+ data => $value,
+ });
+
+ $blist->write_md5( $key_md5, $value_sector->offset );
}
sub get_next_key {
# We're opening the file.
unless ( $obj->_base_offset ) {
- print "1\n";
my $bytes_read = $self->_read_file_header;
$self->_calculate_sizes;
$self->{hash_chars_used} = (2**8);
$self->{index_size} = $self->{hash_chars_used} * $self->byte_size;
- $self->{bucket_size} = $self->{hash_size} + $self->byte_size * 2;
- $self->{bucket_list_size} = $self->{max_buckets} * $self->{bucket_size};
-
- $self->{data_size} = 256; # In bytes
-
return;
}
# --- Above is $header_fixed. Below is $header_var
pack('N4', 0, 0, 0, 0), # currently running transaction IDs
pack('n', $self->byte_size),
- pack('n', $self->{max_buckets}),
+ pack('n', $self->max_buckets),
);
$self->storage->set_transaction_offset( 13 );
return SIG_SIZE + $self->byte_size + $size;
}
+sub _load_sector {
+ my $self = shift;
+ my ($offset) = @_;
+
+ my $type = $self->storage->read_at( $offset, 1 );
+ if ( $type eq $self->SIG_ARRAY || $type eq $self->SIG_HASH ) {
+ return DBM::Deep::Engine::Sector::Reference->new({
+ engine => $self,
+ type => $type,
+ offset => $offset,
+ });
+ }
+ elsif ( $type eq $self->SIG_BLIST ) {
+ return DBM::Deep::Engine::Sector::BucketList->new({
+ engine => $self,
+ type => $type,
+ offset => $offset,
+ });
+ }
+
+ die "Don't know what to do with type '$type' at offset '$offset'\n";
+}
+
+sub _apply_digest {
+ my $self = shift;
+ return $self->{digest}->(@_);
+}
+
################################################################################
-sub storage { $_[0]{storage} }
-sub byte_size { $_[0]{byte_size} }
+sub storage { $_[0]{storage} }
+sub byte_size { $_[0]{byte_size} }
+sub hash_size { $_[0]{hash_size} }
+sub num_txns { $_[0]{num_txns} }
+sub max_buckets { $_[0]{max_buckets} }
################################################################################
-package DBM::Deep::Engine::Sector::Reference;
+package DBM::Deep::Engine::Sector;
+
+sub new {
+ my $self = bless $_[1], $_[0];
+ Scalar::Util::weaken( $self->{engine} );
+ $self->_init;
+ return $self;
+}
+sub _init {}
+
+sub engine { $_[0]{engine} }
+sub offset { $_[0]{offset} }
+sub type { $_[0]{type} }
+
+
+package DBM::Deep::Engine::Sector::Data;
our @ISA = qw( DBM::Deep::Engine::Sector );
+# This is in bytes
+sub size { return 256 }
+
+package DBM::Deep::Engine::Sector::Scalar;
+
+our @ISA = qw( DBM::Deep::Engine::Sector::Data );
+
+sub type { $_[0]{engine}->SIG_DATA }
sub _init {
my $self = shift;
my $engine = $self->engine;
- my $leftover = $self->size - 3 - 2 * $engine->byte_size;
-
- my $offset = $engine->storage->request_space( $self->size );
- $engine->storage->print_at( $offset,
- $self->type,
- pack( $StP{1}, 0 ), # Recycled counter
- pack( $StP{$engine->byte_size}, 0 ), # Chain loc
- pack( $StP{$engine->byte_size}, 0 ), # Index/BList loc
- pack( $StP{1}, 0 ), # Blessedness
- pack( $StP{1}, 0 ), # Classname length
- chr(0) x $leftover, # Zero-fill the data
+ unless ( $self->offset ) {
+ my $leftover = $self->size - 3 - 1 * $engine->byte_size;
+
+ my $data = delete $self->{data};
+
+ # XXX Need to build in chaining
+ $leftover -= length( $data );
+
+ $self->{offset} = $engine->storage->request_space( $self->size );
+ $engine->storage->print_at( $self->offset,
+ $self->type, # Sector type
+ pack( $StP{1}, 0 ), # Recycled counter
+ pack( $StP{$engine->byte_size}, 0 ), # Chain loc
+ pack( $StP{1}, length($data) ), # Data length
+ $data, # Data to be stored
+ chr(0) x $leftover, # Zero-fill the rest
+ );
+
+ return;
+ }
+}
+
+sub data_length {
+ my $self = shift;
+
+ my $data_len = $self->engine->storage->read_at(
+ $self->offset + 2 + $self->engine->byte_size, 1
);
+ return unpack( $StP{1}, $data_len );
+}
+
+sub data {
+ my $self = shift;
- return $offset;
+ return $self->engine->storage->read_at(
+ $self->offset + 2 + $self->engine->byte_size + 1, $self->data_length,
+ );
}
-sub type { $_[0]{type} }
+package DBM::Deep::Engine::Sector::Reference;
-package DBM::Deep::Engine::Sector;
+our @ISA = qw( DBM::Deep::Engine::Sector::Data );
+
+sub _init {
+ my $self = shift;
+
+ my $engine = $self->engine;
+
+ unless ( $self->offset ) {
+ my $leftover = $self->size - 4 - 2 * $engine->byte_size;
+
+ $self->{offset} = $engine->storage->request_space( $self->size );
+ $engine->storage->print_at( $self->offset,
+ $self->type, # Sector type
+ pack( $StP{1}, 0 ), # Recycled counter
+ pack( $StP{$engine->byte_size}, 0 ), # Chain loc
+ pack( $StP{$engine->byte_size}, 0 ), # Index/BList loc
+ pack( $StP{1}, 0 ), # Blessedness
+ pack( $StP{1}, 0 ), # Classname length
+ chr(0) x $leftover, # Zero-fill the data
+ );
+
+ return;
+ }
+}
+
+sub get_blist_loc {
+ my $self = shift;
+
+ my $engine = $self->engine;
+ my $blist_loc = $engine->storage->read_at( $self->offset + 2 + $engine->byte_size, $engine->byte_size );
+ return unpack( $StP{$engine->byte_size}, $blist_loc );
+}
+
+sub get_bucket_list {
+ my $self = shift;
+ my ($args) = @_;
+
+ # XXX Add in check here for recycling?
+
+ my $engine = $self->engine;
+
+ my $blist_loc = $self->get_blist_loc;
+
+ # There's no index or blist yet
+ unless ( $blist_loc ) {
+ return unless $args->{create};
+
+ my $blist = DBM::Deep::Engine::Sector::BucketList->new({
+ engine => $engine,
+ });
+ $engine->storage->print_at( $self->offset + 2 + $engine->byte_size,
+ pack( $StP{$engine->byte_size}, $blist->offset ),
+ );
+ return $blist;
+ }
+
+ return DBM::Deep::Engine::Sector::BucketList->new({
+ engine => $engine,
+ offset => $blist_loc,
+ });
+}
+
+package DBM::Deep::Engine::Sector::BucketList;
+
+our @ISA = qw( DBM::Deep::Engine::Sector );
+
+sub _init {
+ my $self = shift;
+
+ my $engine = $self->engine;
+
+ unless ( $self->offset ) {
+ my $leftover = $self->size - 2;
+
+ $self->{offset} = $engine->storage->request_space( $self->size );
+ $engine->storage->print_at( $self->offset,
+ $engine->SIG_BLIST, # Sector type
+ pack( $StP{1}, 0 ), # Recycled counter
+ chr(0) x $leftover, # Zero-fill the data
+ );
+ }
-sub new {
- my $self = bless $_[1], $_[0];
- Scalar::Util::weaken( $self->{engine} );
- $self->{offset} = $self->_init;
return $self;
}
-sub _init {}
-sub engine { $_[0]{engine} }
-sub offset { $_[0]{offset} }
+sub size {
+ my $self = shift;
+ my $engine = $self->engine;
+ my $base_size = 2; # Sig + recycled counter
+ my $txn_size = $engine->byte_size; # Pointer to data with magic values to indicate status
+ my $bucket_size = $engine->hash_size + $engine->num_txns * $txn_size; # Hash + txn holders
+ return $base_size + $engine->max_buckets * $bucket_size;
+}
-# This is in bytes
-sub size { return 256 }
+sub has_md5 {
+ return 0;
+}
+
+sub write_md5 {
+ my $self = shift;
+ my ($md5, $value_loc) = @_;
+
+ my $engine = $self->engine;
+ $engine->storage->print_at( $self->offset + 2,
+ $md5, # The actual MD5
+ pack( $StP{$engine->byte_size}, $value_loc ), # The pointer to the data in the HEAD
+ );
+}
+
+sub get_data_for {
+ my $self = shift;
+ my ($md5) = @_;
+
+ my $location = $self->engine->storage->read_at(
+ $self->offset + 2 + $self->engine->hash_size, $self->engine->byte_size,
+ );
+ $location = unpack( $StP{$self->engine->byte_size}, $location );
+ return DBM::Deep::Engine::Sector::Scalar->new({
+ engine => $self->engine,
+ offset => $location,
+ });
+}
1;
__END__