=head1 NAME Cache::File::Heap - A file based heap for use by Cache::File =head1 SYNOPSIS use Cache::File::Heap; $heap = Cache::File::Heap->new('/path/to/some/heap/file'); $heap->add($key, $val); ($key, $val) = $heap->minimum; ($key, $val) = $heap->extract_minimum; $heap->delete($key, $val); =head1 DESCRIPTION This module is a wrapper around a Berkeley DB using a btree structure to implement a heap. It is specifically for use by Cache::File for storing expiry times (although with a bit of work it could be made more general). See LIMITATIONS below. =cut package Cache::File::Heap; require 5.005; use strict; use warnings; use DB_File; use Carp; use fields qw(db dbhash); our $VERSION = '2.00'; # common info object my $BTREEINFO = new DB_File::BTREEINFO; $BTREEINFO->{compare} = \&_Num_Compare; $BTREEINFO->{flags} = R_DUP; =head1 CONSTRUCTOR my $heap = Cache::File::Heap->new( [$dbfile] ); The heap constructor takes an optional argument which is the name of the database file to open. If specified, it will attempt to open the database during construction. A new Cache::File::Heap blessed reference will be returned, or undef if the open failed. =cut sub new { my Cache::File::Heap $self = shift; $self = fields::new($self) unless ref $self; if (@_) { $self->open(@_) or return undef; } return $self; } =head1 METHODS =over =item $h->open($dbfile) Opens the specified database file. =cut sub open { my Cache::File::Heap $self = shift; my ($dbfile) = @_; $self->close(); my %dbhash; my $db = tie %dbhash, 'DB_File', $dbfile, O_CREAT|O_RDWR, 0666, $BTREEINFO or return undef; $self->{db} = $db; $self->{dbhash} = \%dbhash; return 1; } =item $h->close() Closes a previously opened heap database. Note that the database will be automatically closed when the heap reference is destroyed. =cut sub close { my Cache::File::Heap $self = shift; $self->{db} = undef; untie %{$self->{dbhash}}; $self->{dbhash} = undef; } =item $h->add($key, $val) Adds a key and value pair to the heap. Currently the key should be a number, whilst the value may be any scalar. Invokes 'die' on failure (use eval to catch it). =cut sub add { my Cache::File::Heap $self = shift; my ($key, $val) = @_; defined $key or croak "key undefined"; defined $val or croak "value undefined"; # return code from DB_File is 0 on success..... $self->_db->put($key, $val) and die "Heap add failed: $@"; } =item $h->delete($key, $val) Removes a key and value pair from the heap. Returns 1 if the pair was found and removed, or 0 otherwise. =cut sub delete { my Cache::File::Heap $self = shift; my ($key, $val) = @_; defined $key or croak "key undefined"; defined $val or croak "value undefined"; # return code from DB_File is 0 on success..... $self->_db->del_dup($key, $val) and return 0; return 1; } =item ($key, $val) = $h->minimum() In list context, returns the smallest key and value pair from the heap. In scalar context only the key is returned. Note smallest is defined via a numerical comparison (hence keys should always be numbers). =cut sub minimum { my Cache::File::Heap $self = shift; my ($key, $val) = (0,0); $self->_db->seq($key, $val, R_FIRST) and return undef; return wantarray? ($key, $val) : $key; } =item ($key, $vals) = $h->minimum_dup() In list context, returns the smallest key and an array reference containing all the values for that key from the heap. In scalar context only the key is returned. =cut sub minimum_dup { my Cache::File::Heap $self = shift; my $db = $self->_db; my ($key, $val) = (0,0); $db->seq($key, $val, R_FIRST) and return undef; return wantarray? ($key, [ $db->get_dup($key) ]) : $key; } =item ($key, $val) = $h->extract_minimum() As for $h->minimum(), but the key and value pair is removed from the heap. =cut sub extract_minimum { my Cache::File::Heap $self = shift; my $db = $self->_db; my ($key, $val) = (0,0); $db->seq($key, $val, R_FIRST) and return undef; $db->del_dup($key, $val); return wantarray? ($key, $val) : $key; } =item ($key, $vals) = $h->extract_minimum_dup() As for $h->minimum_dup(), but all the values are removed from the heap. =cut sub extract_minimum_dup { my Cache::File::Heap $self = shift; my $db = $self->_db; my ($key, $val) = (0,0); $db->seq($key, $val, R_FIRST) and return undef; my @values = $db->get_dup($key) if wantarray; $db->del($key); # bugfix for broken db1 - not all values are removed the first time $db->del($key); return wantarray? ($key, \@values) : $key; } =back =cut sub _db { my Cache::File::Heap $self = shift; my $db = $self->{db}; croak "Heap not opened" unless $db; } sub _Num_Compare { my ($key1, $key2) = @_; return $key1 <=> $key2; } 1; __END__ =head1 SEE ALSO Cache::File =head1 AUTHOR Chris Leishman Based on work by DeWitt Clinton =head1 COPYRIGHT Copyright (C) 2003 Chris Leishman. All Rights Reserved. This module is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either expressed or implied. This program is free software; you can redistribute or modify it under the same terms as Perl itself. $Id: Heap.pm,v 1.2 2003/06/29 14:31:19 caleishm Exp $ =cut