File Coverage

blib/lib/Mail/SpamAssassin/Util/TieOneStringHash.pm
Criterion Covered Total %
statement 39 48 81.2
branch 13 22 59.0
condition n/a
subroutine 11 13 84.6
pod n/a
total 63 83 75.9


line stmt bran cond sub pod time code
1             # A memory-efficient, but slow, single-string structure with a hash interface.
2              
3             # <@LICENSE>
4             # Licensed to the Apache Software Foundation (ASF) under one or more
5             # contributor license agreements. See the NOTICE file distributed with
6             # this work for additional information regarding copyright ownership.
7             # The ASF licenses this file to you under the Apache License, Version 2.0
8             # (the "License"); you may not use this file except in compliance with
9             # the License. You may obtain a copy of the License at:
10             #
11             # http://www.apache.org/licenses/LICENSE-2.0
12             #
13             # Unless required by applicable law or agreed to in writing, software
14             # distributed under the License is distributed on an "AS IS" BASIS,
15             # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16             # See the License for the specific language governing permissions and
17             # limitations under the License.
18             # </@LICENSE>
19              
20             package Mail::SpamAssassin::Util::TieOneStringHash;
21              
22 40     40   287 use strict;
  40         98  
  40         1220  
23 40     40   221 use warnings;
  40         84  
  40         1146  
24 40     40   212 use re 'taint';
  40         87  
  40         1325  
25 40     40   229 use Carp qw(croak);
  40         80  
  40         21103  
26              
27             our @ISA = qw();
28              
29             # the structure is pretty simple: it's a single string, containing
30             # items like so:
31             #
32             # \n KEY 0x00 VALUE 0x00 \n
33             # \n KEY2 0x00 VALUE2 0x00 \n
34             # ...
35             #
36             # undef values are represented using $UNDEF_VALUE, a hacky magic string.
37             # Only simple scalars can be stored; refs of any kind produce a croak().
38             #
39             # writes are slowest, reads are slow, but memory usage is very low
40             # compared to a "real" hash table -- in other words, this is perfect
41             # for infrequently-read data that has to be kept around but should
42             # affect memory usage as little as possible.
43              
44             my $UNDEF_VALUE = "_UNDEF_\001";
45              
46             ###########################################################################
47              
48             sub TIEHASH {
49 81     81   418 my $class = shift;
50 81         379 my $str = '';
51 81         1038 return bless \$str, $class;
52             }
53              
54             sub STORE {
55 1744     1744   3841 my ($store, $k, $v) = @_;
56 1744 50       3118 $v = $UNDEF_VALUE unless defined($v);
57              
58 1744 50       3058 if (ref $v) {
59 0         0 croak "oops! only simple scalars can be stored in a TieOneStringHash";
60             }
61 1744 50       2983 if (!defined $k) {
62 0         0 croak "oops! TieOneStringHash requires defined keys";
63             }
64              
65 1744 100       26477 if ($$store !~ s{\n\Q$k\E\000.*?\000\n}
66             {\n$k\000$v\000\n}xgs)
67 1681         7726 {
68             $$store .= "\n$k\000$v\000\n";
69 1744         6254 }
70             1;
71             }
72              
73 1909     1909   6008 sub FETCH {
74 1909 100       28734 my ($store, $k) = @_;
75             if ($$store =~ m{\n\Q$k\E\000(.*?)\000\n}xs)
76 1796 50       10723 {
77             return $1 eq $UNDEF_VALUE ? undef : $1;
78 113         475 }
79             return;
80             }
81              
82 3378     3378   5733 sub EXISTS {
83 3378 100       31362 my ($store, $k) = @_;
84             if ($$store =~ m{\n\Q$k\E\000}xs)
85 1628         6159 {
86             return 1;
87 1750         5256 }
88             return;
89             }
90              
91 0     0   0 sub DELETE {
92 0 0       0 my ($store, $k) = @_;
93             if ($$store =~ s{\n\Q$k\E\000(.*?)\000\n}
94 0 0       0 {}xgs)
95             {
96 0         0 return $1 eq $UNDEF_VALUE ? undef : $1;
97             }
98             return;
99             }
100 34     34   122  
101 34 50       262 sub FIRSTKEY {
102             my ($store) = @_;
103 34         286 if ($$store =~ m{^\n(.*?)\000}s)
104             {
105 0         0 return $1;
106             }
107             return;
108             }
109 885     885   1570  
110 885 100       13271 sub NEXTKEY {
111             my ($store, $lastk) = @_;
112             if ($$store =~ m{\n\Q$lastk\E\000.*?\000\n
113 851         3980 \n(.*?)\000}xs)
114             {
115 34         174 return $1;
116             }
117             return;
118             }
119 2     2   5  
120 2         19 sub CLEAR {
121             my ($store) = @_;
122             $$store = '';
123             }
124 0     0      
125 0           sub SCALAR {
126             my ($store) = @_;
127             return $$store; # as a string!
128             }
129              
130             1;