File Coverage

blib/lib/Data/SortedSet/Shared/Strings.pm
Criterion Covered Total %
statement 96 96 100.0
branch 29 34 85.2
condition 10 14 71.4
subroutine 33 33 100.0
pod 0 24 0.0
total 168 201 83.5


line stmt bran cond sub pod time code
1             package Data::SortedSet::Shared::Strings;
2 3     3   245753 use strict;
  3         5  
  3         93  
3 3     3   11 use warnings;
  3         4  
  3         109  
4 3     3   12 use Carp ();
  3         5  
  3         48  
5 3     3   10 use Data::Intern::Shared ();
  3         4  
  3         42  
6 3     3   1141 use Data::SortedSet::Shared ();
  3         14  
  3         2949  
7              
8             our $VERSION = '0.02';
9              
10             # ---- construction ----
11              
12             sub new {
13 10     10 0 398731 my ($class, %opt) = @_;
14 10         22 my $max = $opt{max};
15 10 100       189 Carp::croak("new: 'max' is required") unless defined $max;
16 9         1950 my $set = Data::SortedSet::Shared->new($opt{set}, $max);
17 9   66     760 my $keys = Data::Intern::Shared->new($opt{keys}, $opt{max_keys} // $max, $opt{arena} // 0);
      100        
18 9         70 return bless { set => $set, keys => $keys }, $class;
19             }
20              
21             # wrap two already-constructed shared objects (e.g. memfd-backed)
22             sub wrap {
23 3     3 0 1856 my ($class, $set, $keys) = @_;
24 3 100 66     113 Carp::croak("wrap: expected a Data::SortedSet::Shared")
25             unless ref $set && $set->isa('Data::SortedSet::Shared');
26 2 100 66     168 Carp::croak("wrap: expected a Data::Intern::Shared")
27             unless ref $keys && $keys->isa('Data::Intern::Shared');
28 1         6 return bless { set => $set, keys => $keys }, $class;
29             }
30              
31 1     1 0 560 sub set { $_[0]{set} } # the underlying Data::SortedSet::Shared
32 3     3 0 22 sub key_table { $_[0]{keys} } # the underlying Data::Intern::Shared
33              
34             # ---- mutators (intern the key -> id) ----
35              
36             sub add {
37 20232     20232 0 120942 my ($self, $str, $score) = @_;
38 20232         32067 my $id = $self->{keys}->intern($str);
39 20232 50       27649 return undef unless defined $id; # key table full
40 20232         52753 return $self->{set}->add($id, $score);
41             }
42              
43             sub incr {
44 5974     5974 0 38603 my ($self, $str, $delta) = @_;
45 5974         9507 my $id = $self->{keys}->intern($str);
46 5974 100       8666 Carp::croak("incr: key table full") unless defined $id;
47 5973         15701 return $self->{set}->incr($id, $delta);
48             }
49              
50             sub remove {
51 7762     7762 0 42643 my ($self, $str) = @_;
52 7762         12972 my $id = $self->{keys}->id_of($str);
53 7762 100       18393 return defined $id ? $self->{set}->remove($id) : 0;
54             }
55              
56             sub add_many {
57 2     2 0 31 my ($self, $rows) = @_;
58 2 50       7 Carp::croak("add_many: expected an arrayref") unless ref $rows eq 'ARRAY';
59 2         4 my @id_rows;
60 2         5 for my $r (@$rows) {
61 7 100 66     21 next unless ref $r eq 'ARRAY' && @$r >= 2;
62 6 100       12 next if $r->[1] != $r->[1]; # skip a NaN score before interning (no ghost key slot)
63 5         13 my $id = $self->{keys}->intern($r->[0]);
64 5 50       9 last unless defined $id; # key table full -> stop
65 5         10 push @id_rows, [ $id, $r->[1] ];
66             }
67 2         19 return $self->{set}->add_many(\@id_rows);
68             }
69              
70 1     1 0 26 sub clear { $_[0]{set}->clear; $_[0]{keys}->clear; return }
  1         6  
  1         1  
71              
72             # ---- lookup (id_of the key; undef short-circuits) ----
73              
74 1766 100   1766 0 3891 sub score { my $id = $_[0]{keys}->id_of($_[1]); defined $id ? $_[0]{set}->score($id) : undef }
  1766         3307  
75 883 100   883 0 2360 sub rank { my $id = $_[0]{keys}->id_of($_[1]); defined $id ? $_[0]{set}->rank($id) : undef }
  883         1874  
76 1 50   1 0 6 sub rev_rank { my $id = $_[0]{keys}->id_of($_[1]); defined $id ? $_[0]{set}->rev_rank($id) : undef }
  1         11  
77 5 100   5 0 624 sub exists { my $id = $_[0]{keys}->id_of($_[1]); defined $id ? $_[0]{set}->exists($id) : 0 }
  5         28  
78 12     12 0 421 sub count { $_[0]{set}->count }
79 13     13 0 277 sub count_in_score { shift->{set}->count_in_score(@_) }
80              
81             # ---- rank / range (decode ids back to strings) ----
82              
83             sub _decode {
84 21     21   91 my ($self, $ws, @list) = @_;
85 21         35 my $k = $self->{keys};
86 21 100       55 return map { $k->string($_) } @list unless $ws;
  1201         2294  
87 2         4 my @out;
88 2         8 for (my $i = 0; $i < @list; $i += 2) { push @out, $k->string($list[$i]), $list[$i + 1] }
  5         20  
89 2         16 return @out;
90             }
91              
92             sub at_rank {
93 3     3 0 16 my $id = $_[0]{set}->at_rank($_[1]);
94 3 100       19 return defined $id ? $_[0]{keys}->string($id) : undef;
95             }
96              
97             sub range_by_rank {
98 4     4 0 18 my ($self, $start, $stop, %opt) = @_;
99 4         45 $self->_decode($opt{withscores}, $self->{set}->range_by_rank($start, $stop, %opt));
100             }
101             sub rev_range_by_rank {
102 2     2 0 5 my ($self, $start, $stop, %opt) = @_;
103 2         18 $self->_decode($opt{withscores}, $self->{set}->rev_range_by_rank($start, $stop, %opt));
104             }
105             sub range_by_score {
106 14     14 0 10674 my ($self, $min, $max, %opt) = @_;
107 14         190 $self->_decode($opt{withscores}, $self->{set}->range_by_score($min, $max, %opt));
108             }
109             sub rev_range_by_score {
110 1     1 0 2 my ($self, $max, $min, %opt) = @_;
111 1         25 $self->_decode($opt{withscores}, $self->{set}->rev_range_by_score($max, $min, %opt));
112             }
113              
114             # ---- pop / peek (id -> string) ----
115              
116             for my $m (qw(pop_min pop_max peek_min peek_max)) {
117 3     3   19 no strict 'refs';
  3         3  
  3         716  
118             *$m = sub {
119 6059     6059   42527 my ($id, $score) = $_[0]{set}->$m;
120 6059 50       14721 return defined $id ? ($_[0]{keys}->string($id), $score) : ();
121             };
122             }
123              
124             # ---- iteration ----
125              
126             sub each {
127 2     2 0 414 my ($self, $cb) = @_;
128 2         5 my $k = $self->{keys};
129 2     882   67 $self->{set}->each(sub { $cb->($k->string($_[0]), $_[1]) });
  882         2889  
130 2         28 return;
131             }
132              
133             # ---- lifecycle ----
134              
135 1     1 0 5925 sub sync { $_[0]{set}->sync; $_[0]{keys}->sync; return }
  1         3648  
  1         75  
136 1     1 0 84 sub unlink { $_[0]{set}->unlink; $_[0]{keys}->unlink; return }
  1         31  
  1         180  
137 1     1 0 29 sub stats { { set => $_[0]{set}->stats, keys => $_[0]{keys}->stats } }
138              
139             1;
140             __END__