File Coverage

blib/lib/Cache/LRU.pm
Criterion Covered Total %
statement 44 52 84.6
branch 8 12 66.6
condition n/a
subroutine 10 10 100.0
pod 5 5 100.0
total 67 79 84.8


line stmt bran cond sub pod time code
1             package Cache::LRU;
2              
3 2     2   49770 use strict;
  2         4  
  2         69  
4 2     2   9 use warnings;
  2         3  
  2         53  
5              
6 2     2   43 use 5.008_001;
  2         11  
  2         83  
7              
8 2     2   10 use Scalar::Util qw();
  2         2  
  2         1831  
9              
10             our $VERSION = '0.04';
11              
12             sub GC_FACTOR () { 10 }
13              
14             sub new {
15 2     2 1 30 my ($klass, %args) = @_;
16 2         19 return bless {
17             size => 1024,
18             %args,
19             _entries => {}, # $key => $weak_valueref
20             _fifo => [], # fifo queue of [ $key, $valueref ]
21             }, $klass;
22             }
23              
24             sub set {
25 9     9 1 875 my ($self, $key, $value) = @_;
26              
27 9         19 my $entries = $self->{_entries};
28              
29 9 100       29 if (my $old_value_ref = $entries->{$key}) {
30 2         5 $$old_value_ref = undef;
31             }
32              
33             # register
34 9         18 my $value_ref = \$value;
35 9         52 Scalar::Util::weaken($entries->{$key} = $value_ref);
36 9         21 $self->_update_fifo($key, $value_ref);
37              
38             # expire the oldest entry if full
39 9         28 while (scalar(keys %$entries) > $self->{size}) {
40 16         14 my $exp_key = shift(@{$self->{_fifo}})->[0];
  16         25  
41 16 100       59 delete $entries->{$exp_key}
42             unless $entries->{$exp_key};
43             }
44              
45 9         36 $value;
46             }
47              
48             sub remove {
49 2     2 1 345 my ($self, $key) = @_;
50 2         9 my $value_ref = delete $self->{_entries}->{$key};
51 2 50       9 return undef unless $value_ref;
52 2         4 my $value = $$value_ref;
53 2         14 $$value_ref = undef;
54 2         8 $value;
55             }
56              
57             sub get {
58 25     25 1 44 my ($self, $key) = @_;
59              
60 25         49 my $value_ref = $self->{_entries}->{$key};
61 25 100       72 return undef unless $value_ref;
62              
63 18         34 $self->_update_fifo($key, $value_ref);
64 18         72 $$value_ref;
65             }
66              
67             sub clear {
68 1     1 1 2 my $self = shift;
69              
70 1         2 $self->{_entries} = {};
71 1         5 $self->{_fifo} = [];
72             }
73              
74             sub _update_fifo {
75             # precondition: $self->{_entries} should contain given key
76 27     27   40 my ($self, $key, $value_ref) = @_;
77 27         33 my $fifo = $self->{_fifo};
78              
79 27         66 push @$fifo, [ $key, $value_ref ];
80 27 50       141 if (@$fifo >= $self->{size} * GC_FACTOR) {
81 0           my $entries = $self->{_entries};
82 0           my @new_fifo;
83 0           my %need = map { $_ => 1 } keys %$entries;
  0            
84 0           while (%need) {
85 0           my $fifo_entry = pop @$fifo;
86 0 0         unshift @new_fifo, $fifo_entry
87             if delete $need{$fifo_entry->[0]};
88             }
89 0           $self->{_fifo} = \@new_fifo;
90             }
91             }
92              
93             1;
94             __END__