1*b39c5158Smillertuse strict;
2*b39c5158Smillertpackage Tie::Memoize;
3*b39c5158Smillertuse Tie::Hash;
4*b39c5158Smillertour @ISA = 'Tie::ExtraHash';
5*b39c5158Smillertour $VERSION = '1.1';
6*b39c5158Smillert
7*b39c5158Smillertour $exists_token = \undef;
8*b39c5158Smillert
9*b39c5158Smillertsub croak {require Carp; goto &Carp::croak}
10*b39c5158Smillert
11*b39c5158Smillert# Format: [0: STORAGE, 1: EXISTS-CACHE, 2: FETCH_function;
12*b39c5158Smillert#	   3: EXISTS_function, 4: DATA, 5: EXISTS_different ]
13*b39c5158Smillert
14*b39c5158Smillertsub FETCH {
15*b39c5158Smillert  my ($h,$key) = ($_[0][0], $_[1]);
16*b39c5158Smillert  my $res = $h->{$key};
17*b39c5158Smillert  return $res if defined $res;	# Shortcut if accessible
18*b39c5158Smillert  return $res if exists $h->{$key}; # Accessible, but undef
19*b39c5158Smillert  my $cache = $_[0][1]{$key};
20*b39c5158Smillert  return if defined $cache and not $cache; # Known to not exist
21*b39c5158Smillert  my @res = $_[0][2]->($key, $_[0][4]);	# Autoload
22*b39c5158Smillert  $_[0][1]{$key} = 0, return unless @res; # Cache non-existence
23*b39c5158Smillert  delete $_[0][1]{$key};	# Clear existence cache, not needed any more
24*b39c5158Smillert  $_[0][0]{$key} = $res[0];	# Store data and return
25*b39c5158Smillert}
26*b39c5158Smillert
27*b39c5158Smillertsub EXISTS   {
28*b39c5158Smillert  my ($a,$key) = (shift, shift);
29*b39c5158Smillert  return 1 if exists $a->[0]{$key}; # Have data
30*b39c5158Smillert  my $cache = $a->[1]{$key};
31*b39c5158Smillert  return $cache if defined $cache; # Existence cache
32*b39c5158Smillert  my @res = $a->[3]($key,$a->[4]);
33*b39c5158Smillert  $a->[1]{$key} = 0, return unless @res; # Cache non-existence
34*b39c5158Smillert  # Now we know it exists
35*b39c5158Smillert  return ($a->[1]{$key} = 1) if $a->[5]; # Only existence reported
36*b39c5158Smillert  # Now know the value
37*b39c5158Smillert  $a->[0]{$key} = $res[0];    # Store data
38*b39c5158Smillert  return 1
39*b39c5158Smillert}
40*b39c5158Smillert
41*b39c5158Smillertsub TIEHASH  {
42*b39c5158Smillert  croak 'syntax: tie %hash, \'Tie::AutoLoad\', \&fetch_subr' if @_ < 2;
43*b39c5158Smillert  croak 'syntax: tie %hash, \'Tie::AutoLoad\', \&fetch_subr, $data, \&exists_subr, \%data_cache, \%existence_cache' if @_ > 6;
44*b39c5158Smillert  push @_, undef if @_ < 3;	# Data
45*b39c5158Smillert  push @_, $_[1] if @_ < 4;	# exists
46*b39c5158Smillert  push @_, {} while @_ < 6;	# initial value and caches
47*b39c5158Smillert  bless [ @_[4,5,1,3,2], $_[1] ne $_[3]], $_[0]
48*b39c5158Smillert}
49*b39c5158Smillert
50*b39c5158Smillert1;
51*b39c5158Smillert
52*b39c5158Smillert=head1 NAME
53*b39c5158Smillert
54*b39c5158SmillertTie::Memoize - add data to hash when needed
55*b39c5158Smillert
56*b39c5158Smillert=head1 SYNOPSIS
57*b39c5158Smillert
58*b39c5158Smillert  require Tie::Memoize;
59*b39c5158Smillert  tie %hash, 'Tie::Memoize',
60*b39c5158Smillert      \&fetch,			# The rest is optional
61*b39c5158Smillert      $DATA, \&exists,
62*b39c5158Smillert      {%ini_value}, {%ini_existence};
63*b39c5158Smillert
64*b39c5158Smillert=head1 DESCRIPTION
65*b39c5158Smillert
66*b39c5158SmillertThis package allows a tied hash to autoload its values on the first access,
67*b39c5158Smillertand to use the cached value on the following accesses.
68*b39c5158Smillert
69*b39c5158SmillertOnly read-accesses (via fetching the value or C<exists>) result in calls to
70*b39c5158Smillertthe functions; the modify-accesses are performed as on a normal hash.
71*b39c5158Smillert
72*b39c5158SmillertThe required arguments during C<tie> are the hash, the package, and
73*b39c5158Smillertthe reference to the C<FETCH>ing function.  The optional arguments are
74*b39c5158Smillertan arbitrary scalar $data, the reference to the C<EXISTS> function,
75*b39c5158Smillertand initial values of the hash and of the existence cache.
76*b39c5158Smillert
77*b39c5158SmillertBoth the C<FETCH>ing function and the C<EXISTS> functions have the
78*b39c5158Smillertsame signature: the arguments are C<$key, $data>; $data is the same
79*b39c5158Smillertvalue as given as argument during tie()ing.  Both functions should
80*b39c5158Smillertreturn an empty list if the value does not exist.  If C<EXISTS>
81*b39c5158Smillertfunction is different from the C<FETCH>ing function, it should return
82*b39c5158Smillerta TRUE value on success.  The C<FETCH>ing function should return the
83*b39c5158Smillertintended value if the key is valid.
84*b39c5158Smillert
85*b39c5158Smillert=head1 Inheriting from B<Tie::Memoize>
86*b39c5158Smillert
87*b39c5158SmillertThe structure of the tied() data is an array reference with elements
88*b39c5158Smillert
89*b39c5158Smillert  0:  cache of known values
90*b39c5158Smillert  1:  cache of known existence of keys
91*b39c5158Smillert  2:  FETCH  function
92*b39c5158Smillert  3:  EXISTS function
93*b39c5158Smillert  4:  $data
94*b39c5158Smillert
95*b39c5158SmillertThe rest is for internal usage of this package.  In particular, if
96*b39c5158SmillertTIEHASH is overwritten, it should call SUPER::TIEHASH.
97*b39c5158Smillert
98*b39c5158Smillert=head1 EXAMPLE
99*b39c5158Smillert
100*b39c5158Smillert  sub slurp {
101*b39c5158Smillert    my ($key, $dir) = shift;
102*b39c5158Smillert    open my $h, '<', "$dir/$key" or return;
103*b39c5158Smillert    local $/; <$h>			# slurp it all
104*b39c5158Smillert  }
105*b39c5158Smillert  sub exists { my ($key, $dir) = shift; return -f "$dir/$key" }
106*b39c5158Smillert
107*b39c5158Smillert  tie %hash, 'Tie::Memoize', \&slurp, $directory, \&exists,
108*b39c5158Smillert      { fake_file1 => $content1, fake_file2 => $content2 },
109*b39c5158Smillert      { pretend_does_not_exists => 0, known_to_exist => 1 };
110*b39c5158Smillert
111*b39c5158SmillertThis example treats the slightly modified contents of $directory as a
112*b39c5158Smillerthash.  The modifications are that the keys F<fake_file1> and
113*b39c5158SmillertF<fake_file2> fetch values $content1 and $content2, and
114*b39c5158SmillertF<pretend_does_not_exists> will never be accessed.  Additionally, the
115*b39c5158Smillertexistence of F<known_to_exist> is never checked (so if it does not
116*b39c5158Smillertexists when its content is needed, the user of %hash may be confused).
117*b39c5158Smillert
118*b39c5158Smillert=head1 BUGS
119*b39c5158Smillert
120*b39c5158SmillertFIRSTKEY and NEXTKEY methods go through the keys which were already read,
121*b39c5158Smillertnot all the possible keys of the hash.
122*b39c5158Smillert
123*b39c5158Smillert=head1 AUTHOR
124*b39c5158Smillert
125*b39c5158SmillertIlya Zakharevich L<mailto:perl-module-hash-memoize@ilyaz.org>.
126*b39c5158Smillert
127*b39c5158Smillert=cut
128*b39c5158Smillert
129