File Coverage

blib/lib/Cache/Memcached/GetParser.pm
Criterion Covered Total %
statement 36 84 42.8
branch 0 22 0.0
condition 0 9 0.0
subroutine 12 16 75.0
pod 0 4 0.0
total 48 135 35.5


line stmt bran cond sub pod time code
1             package Cache::Memcached::GetParser;
2 9     9   55 use strict;
  9         15  
  9         315  
3 9     9   49 use warnings;
  9         16  
  9         329  
4 9     9   9128 use integer;
  9         97  
  9         55  
5              
6 9     9   353 use Errno qw( EINPROGRESS EWOULDBLOCK EISCONN );
  9         21  
  9         511  
7              
8 9     9   53 use constant DEST => 0; # destination hashref we're writing into
  9         14  
  9         836  
9 9     9   48 use constant NSLEN => 1; # length of namespace to ignore on keys
  9         15  
  9         403  
10 9     9   46 use constant ON_ITEM => 2;
  9         19  
  9         393  
11 9     9   40 use constant BUF => 3; # read buffer
  9         17  
  9         430  
12 9     9   45 use constant STATE => 4; # 0 = waiting for a line, N = reading N bytes
  9         16  
  9         341  
13 9     9   49 use constant OFFSET => 5; # offsets to read into buffers
  9         14  
  9         416  
14 9     9   42 use constant FLAGS => 6;
  9         16  
  9         399  
15 9     9   45 use constant KEY => 7; # current key we're parsing (without the namespace prefix)
  9         22  
  9         9168  
16              
17             sub new {
18 0     0 0   my ($class, $dest, $nslen, $on_item) = @_;
19 0           return bless [$dest, $nslen, $on_item, '', 0, 0], $class;
20             }
21              
22             sub current_key {
23 0     0 0   return $_[0][KEY];
24             }
25              
26             # returns 1 on success, -1 on failure, and 0 if still working.
27             sub parse_from_sock {
28 0     0 0   my ($self, $sock) = @_;
29 0           my $res;
30              
31             # where are we reading into?
32 0 0         if ($self->[STATE]) { # reading value into $ret
33 0           my $ret = $self->[DEST];
34 0           $res = sysread($sock, $ret->{$self->[KEY]},
35             $self->[STATE] - $self->[OFFSET],
36             $self->[OFFSET]);
37              
38 0 0 0       return 0
39             if !defined($res) and $!==EWOULDBLOCK;
40              
41 0 0         if ($res == 0) { # catches 0=conn closed or undef=error
42 0           $self->[ON_ITEM] = undef;
43 0           return -1;
44             }
45              
46 0           $self->[OFFSET] += $res;
47 0 0         if ($self->[OFFSET] == $self->[STATE]) { # finished reading
48 0           $self->[ON_ITEM]->($self->[KEY], $self->[FLAGS]);
49 0           $self->[OFFSET] = 0;
50 0           $self->[STATE] = 0;
51             # wait for another VALUE line or END...
52             }
53 0           return 0; # still working, haven't got to end yet
54             }
55              
56             # we're reading a single line.
57             # first, read whatever's there, but be satisfied with 2048 bytes
58 0           $res = sysread($sock, $self->[BUF],
59             128*1024, $self->[OFFSET]);
60 0 0 0       return 0
61             if !defined($res) and $!==EWOULDBLOCK;
62 0 0 0       if (!defined($res) || $res == 0) {
63 0           $self->[ON_ITEM] = undef;
64 0           return -1;
65             }
66              
67 0           $self->[OFFSET] += $res;
68              
69 0           return $self->parse_buffer;
70             }
71              
72             # returns 1 on success, -1 on failure, and 0 if still working.
73             sub parse_buffer {
74 0     0 0   my ($self) = @_;
75 0           my $ret = $self->[DEST];
76              
77             SEARCH:
78 0           while (1) { # may have to search many times
79              
80             # do we have a complete END line?
81 0 0         if ($self->[BUF] =~ /^END\r\n/) {
82 0           $self->[ON_ITEM] = undef;
83 0           return 1; # we're done successfully, return 1 to finish
84             }
85              
86             # do we have a complete VALUE line?
87 0 0         if ($self->[BUF] =~ /^VALUE (\S+) (\d+) (\d+)\r\n/) {
88 0           ($self->[KEY], $self->[FLAGS], $self->[STATE]) =
89             (substr($1, $self->[NSLEN]), int($2), $3+2);
90             # Note: we use $+[0] and not pos($self->[BUF]) because pos()
91             # seems to have problems under perl's taint mode. nobody
92             # on the list discovered why, but this seems a reasonable
93             # work-around:
94 0           my $p = $+[0];
95 0           my $len = length($self->[BUF]);
96 0 0         my $copy = $len-$p > $self->[STATE] ? $self->[STATE] : $len-$p;
97 0 0         $ret->{$self->[KEY]} = substr($self->[BUF], $p, $copy)
98             if $copy;
99 0           $self->[OFFSET] = $copy;
100 0           substr($self->[BUF], 0, $p+$copy, ''); # delete the stuff we used
101              
102 0 0         if ($self->[OFFSET] == $self->[STATE]) { # have it all?
103 0           $self->[ON_ITEM]->($self->[KEY], $self->[FLAGS]);
104 0           $self->[OFFSET] = 0;
105 0           $self->[STATE] = 0;
106 0           next SEARCH; # look again
107             }
108              
109 0           last SEARCH; # buffer is empty now
110             }
111              
112             # if we're here probably means we only have a partial VALUE
113             # or END line in the buffer. Could happen with multi-get,
114             # though probably very rarely. Exit the loop and let it read
115             # more.
116              
117             # but first, make sure subsequent reads don't destroy our
118             # partial VALUE/END line.
119 0           $self->[OFFSET] = length($self->[BUF]);
120 0           last SEARCH;
121             }
122 0           return 0;
123             }
124              
125             1;