Data-CompactReadonly
view release on metacpan or search on metacpan
lib/Data/CompactReadonly/V0/Dictionary.pm view on Meta::CPAN
}, $class);
if($root->_tied()) {
tie my %dict, 'Data::CompactReadonly::V0::TiedDictionary', $object;
return \%dict;
} else {
return $object;
}
}
# write a Dictionary to the file at the current offset
sub _create {
my($class, %args) = @_;
my $fh = $args{fh};
$class->_stash_already_seen(%args);
(my $scalar_type = $class) =~ s/Dictionary/Scalar/;
# node header
print $fh $class->_type_byte_from_class().
$scalar_type->_get_bytes_from_word(scalar(keys %{$args{data}}));
# empty pointer table
my $table_start_ptr = tell($fh);
print $fh "\x00" x $args{ptr_size} x 2 x scalar(keys %{$args{data}});
$class->_set_next_free_ptr(%args);
my @sorted_keys = sort keys %{$args{data}};
foreach my $index (0 .. $#sorted_keys) {
my $this_key = $sorted_keys[$index];
my $this_value = $args{data}->{$this_key};
# write the pointer to the key, and the key if needed. Then write the
# pointer to the value, and the value if needed. The value can be any
# type. Keys are coerced Text to avoid floating point problems.
foreach my $item (
{ data => $this_key, ptr_offset => 0, coerce_to_text => 1 },
{ data => $this_value, ptr_offset => $args{ptr_size} }
) {
$class->_seek(%args, pointer => $item->{ptr_offset} + $table_start_ptr + 2 * $index * $args{ptr_size});
if(my $ptr = $class->_get_already_seen(%args, data => $item->{data})) {
print $fh $class->_encode_ptr(%args, pointer => $ptr);
} else {
print $fh $class->_encode_ptr(%args, pointer => $class->_get_next_free_ptr(%args));
$class->_seek(%args, pointer => $class->_get_next_free_ptr(%args));
my $node_class = 'Data::CompactReadonly::V0::Node';
if($item->{coerce_to_text}) {
$node_class = 'Data::CompactReadonly::V0::'.$class->_text_type_for_data($item->{data});
unless($node_class->VERSION()) {
eval "use $node_class";
die($@) if($@);
}
}
$node_class->_create(%args, data => $item->{data});
}
}
}
}
# Efficient binary search. Relies on elements' being ASCIIbetically sorted by key.
# 1 <= iterations to find key (or find that there is no key) <= ceil(log2(N))
# so no more than 4 iterations for a ten element list, no more than 20 for
# a million element list. Each iteration takes two seeks and two reads there
# are then two more seeks and reads to get the value
sub element {
my($self, $element) = @_;
die(
"$self: Invalid element: ".
(!defined($element) ? '[undef]' : $element).
" isn't Text or numeric\n"
) unless(defined($element) && !ref($element));
# first we need to find that key
my $max_candidate = $self->count() - 1;
my $min_candidate = 0;
my $cur_candidate = int($max_candidate / 2);
my $prev_candidate = -1;
while(1) {
my $key = $self->_nth_key($cur_candidate);
$prev_candidate = $cur_candidate;
if($key eq $element) {
return $self->_nth_value($cur_candidate);
} elsif($key lt $element) { # our target is futher down the list
($min_candidate, $cur_candidate, $max_candidate) = (
$cur_candidate + 1,
int(($cur_candidate + $max_candidate + 1) / 2),
$max_candidate
);
} else { # our target is further up the list
($min_candidate, $cur_candidate, $max_candidate) = (
$min_candidate,
int(($min_candidate + $cur_candidate) / 2),
$cur_candidate - 1
);
}
last if($prev_candidate == $cur_candidate);
}
die("$self: Invalid element: $element: doesn't exist\n");
}
sub exists {
my($self, $element) = @_;
return 0 if($self->count() == 0);
eval { $self->element($element) };
if($@ =~ /doesn't exist/) {
return 0;
} elsif($@) {
die($@);
} else {
return 1;
}
}
sub _nth_key {
my($self, $n) = @_;
if($self->{cache} && exists($self->{cache}->{keys}->{$n})) {
return $self->{cache}->{keys}->{$n}
}
$self->_seek($self->_nth_key_ptr_location($n));
( run in 1.298 second using v1.01-cache-2.11-cpan-71847e10f99 )