xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/lib/Tie/SubstrHash.pm (revision 0:68f95e015346)
1*0Sstevel@tonic-gatepackage Tie::SubstrHash;
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gateour $VERSION = '1.00';
4*0Sstevel@tonic-gate
5*0Sstevel@tonic-gate=head1 NAME
6*0Sstevel@tonic-gate
7*0Sstevel@tonic-gateTie::SubstrHash - Fixed-table-size, fixed-key-length hashing
8*0Sstevel@tonic-gate
9*0Sstevel@tonic-gate=head1 SYNOPSIS
10*0Sstevel@tonic-gate
11*0Sstevel@tonic-gate    require Tie::SubstrHash;
12*0Sstevel@tonic-gate
13*0Sstevel@tonic-gate    tie %myhash, 'Tie::SubstrHash', $key_len, $value_len, $table_size;
14*0Sstevel@tonic-gate
15*0Sstevel@tonic-gate=head1 DESCRIPTION
16*0Sstevel@tonic-gate
17*0Sstevel@tonic-gateThe B<Tie::SubstrHash> package provides a hash-table-like interface to
18*0Sstevel@tonic-gatean array of determinate size, with constant key size and record size.
19*0Sstevel@tonic-gate
20*0Sstevel@tonic-gateUpon tying a new hash to this package, the developer must specify the
21*0Sstevel@tonic-gatesize of the keys that will be used, the size of the value fields that the
22*0Sstevel@tonic-gatekeys will index, and the size of the overall table (in terms of key-value
23*0Sstevel@tonic-gatepairs, not size in hard memory). I<These values will not change for the
24*0Sstevel@tonic-gateduration of the tied hash>. The newly-allocated hash table may now have
25*0Sstevel@tonic-gatedata stored and retrieved. Efforts to store more than C<$table_size>
26*0Sstevel@tonic-gateelements will result in a fatal error, as will efforts to store a value
27*0Sstevel@tonic-gatenot exactly C<$value_len> characters in length, or reference through a
28*0Sstevel@tonic-gatekey not exactly C<$key_len> characters in length. While these constraints
29*0Sstevel@tonic-gatemay seem excessive, the result is a hash table using much less internal
30*0Sstevel@tonic-gatememory than an equivalent freely-allocated hash table.
31*0Sstevel@tonic-gate
32*0Sstevel@tonic-gate=head1 CAVEATS
33*0Sstevel@tonic-gate
34*0Sstevel@tonic-gateBecause the current implementation uses the table and key sizes for the
35*0Sstevel@tonic-gatehashing algorithm, there is no means by which to dynamically change the
36*0Sstevel@tonic-gatevalue of any of the initialization parameters.
37*0Sstevel@tonic-gate
38*0Sstevel@tonic-gateThe hash does not support exists().
39*0Sstevel@tonic-gate
40*0Sstevel@tonic-gate=cut
41*0Sstevel@tonic-gate
42*0Sstevel@tonic-gateuse Carp;
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gatesub TIEHASH {
45*0Sstevel@tonic-gate    my $pack = shift;
46*0Sstevel@tonic-gate    my ($klen, $vlen, $tsize) = @_;
47*0Sstevel@tonic-gate    my $rlen = 1 + $klen + $vlen;
48*0Sstevel@tonic-gate    $tsize = [$tsize,
49*0Sstevel@tonic-gate	      findgteprime($tsize * 1.1)]; # Allow 10% empty.
50*0Sstevel@tonic-gate    local $self = bless ["\0", $klen, $vlen, $tsize, $rlen, 0, -1];
51*0Sstevel@tonic-gate    $$self[0] x= $rlen * $tsize->[1];
52*0Sstevel@tonic-gate    $self;
53*0Sstevel@tonic-gate}
54*0Sstevel@tonic-gate
55*0Sstevel@tonic-gatesub CLEAR {
56*0Sstevel@tonic-gate    local($self) = @_;
57*0Sstevel@tonic-gate    $$self[0] = "\0" x ($$self[4] * $$self[3]->[1]);
58*0Sstevel@tonic-gate    $$self[5] =  0;
59*0Sstevel@tonic-gate    $$self[6] = -1;
60*0Sstevel@tonic-gate}
61*0Sstevel@tonic-gate
62*0Sstevel@tonic-gatesub FETCH {
63*0Sstevel@tonic-gate    local($self,$key) = @_;
64*0Sstevel@tonic-gate    local($klen, $vlen, $tsize, $rlen) = @$self[1..4];
65*0Sstevel@tonic-gate    &hashkey;
66*0Sstevel@tonic-gate    for (;;) {
67*0Sstevel@tonic-gate	$offset = $hash * $rlen;
68*0Sstevel@tonic-gate	$record = substr($$self[0], $offset, $rlen);
69*0Sstevel@tonic-gate	if (ord($record) == 0) {
70*0Sstevel@tonic-gate	    return undef;
71*0Sstevel@tonic-gate	}
72*0Sstevel@tonic-gate	elsif (ord($record) == 1) {
73*0Sstevel@tonic-gate	}
74*0Sstevel@tonic-gate	elsif (substr($record, 1, $klen) eq $key) {
75*0Sstevel@tonic-gate	    return substr($record, 1+$klen, $vlen);
76*0Sstevel@tonic-gate	}
77*0Sstevel@tonic-gate	&rehash;
78*0Sstevel@tonic-gate    }
79*0Sstevel@tonic-gate}
80*0Sstevel@tonic-gate
81*0Sstevel@tonic-gatesub STORE {
82*0Sstevel@tonic-gate    local($self,$key,$val) = @_;
83*0Sstevel@tonic-gate    local($klen, $vlen, $tsize, $rlen) = @$self[1..4];
84*0Sstevel@tonic-gate    croak("Table is full ($tsize->[0] elements)") if $$self[5] > $tsize->[0];
85*0Sstevel@tonic-gate    croak(qq/Value "$val" is not $vlen characters long/)
86*0Sstevel@tonic-gate	if length($val) != $vlen;
87*0Sstevel@tonic-gate    my $writeoffset;
88*0Sstevel@tonic-gate
89*0Sstevel@tonic-gate    &hashkey;
90*0Sstevel@tonic-gate    for (;;) {
91*0Sstevel@tonic-gate	$offset = $hash * $rlen;
92*0Sstevel@tonic-gate	$record = substr($$self[0], $offset, $rlen);
93*0Sstevel@tonic-gate	if (ord($record) == 0) {
94*0Sstevel@tonic-gate	    $record = "\2". $key . $val;
95*0Sstevel@tonic-gate	    die "panic" unless length($record) == $rlen;
96*0Sstevel@tonic-gate	    $writeoffset = $offset unless defined $writeoffset;
97*0Sstevel@tonic-gate	    substr($$self[0], $writeoffset, $rlen) = $record;
98*0Sstevel@tonic-gate	    ++$$self[5];
99*0Sstevel@tonic-gate	    return;
100*0Sstevel@tonic-gate	}
101*0Sstevel@tonic-gate	elsif (ord($record) == 1) {
102*0Sstevel@tonic-gate	    $writeoffset = $offset unless defined $writeoffset;
103*0Sstevel@tonic-gate	}
104*0Sstevel@tonic-gate	elsif (substr($record, 1, $klen) eq $key) {
105*0Sstevel@tonic-gate	    $record = "\2". $key . $val;
106*0Sstevel@tonic-gate	    die "panic" unless length($record) == $rlen;
107*0Sstevel@tonic-gate	    substr($$self[0], $offset, $rlen) = $record;
108*0Sstevel@tonic-gate	    return;
109*0Sstevel@tonic-gate	}
110*0Sstevel@tonic-gate	&rehash;
111*0Sstevel@tonic-gate    }
112*0Sstevel@tonic-gate}
113*0Sstevel@tonic-gate
114*0Sstevel@tonic-gatesub DELETE {
115*0Sstevel@tonic-gate    local($self,$key) = @_;
116*0Sstevel@tonic-gate    local($klen, $vlen, $tsize, $rlen) = @$self[1..4];
117*0Sstevel@tonic-gate    &hashkey;
118*0Sstevel@tonic-gate    for (;;) {
119*0Sstevel@tonic-gate	$offset = $hash * $rlen;
120*0Sstevel@tonic-gate	$record = substr($$self[0], $offset, $rlen);
121*0Sstevel@tonic-gate	if (ord($record) == 0) {
122*0Sstevel@tonic-gate	    return undef;
123*0Sstevel@tonic-gate	}
124*0Sstevel@tonic-gate	elsif (ord($record) == 1) {
125*0Sstevel@tonic-gate	}
126*0Sstevel@tonic-gate	elsif (substr($record, 1, $klen) eq $key) {
127*0Sstevel@tonic-gate	    substr($$self[0], $offset, 1) = "\1";
128*0Sstevel@tonic-gate	    return substr($record, 1+$klen, $vlen);
129*0Sstevel@tonic-gate	    --$$self[5];
130*0Sstevel@tonic-gate	}
131*0Sstevel@tonic-gate	&rehash;
132*0Sstevel@tonic-gate    }
133*0Sstevel@tonic-gate}
134*0Sstevel@tonic-gate
135*0Sstevel@tonic-gatesub FIRSTKEY {
136*0Sstevel@tonic-gate    local($self) = @_;
137*0Sstevel@tonic-gate    $$self[6] = -1;
138*0Sstevel@tonic-gate    &NEXTKEY;
139*0Sstevel@tonic-gate}
140*0Sstevel@tonic-gate
141*0Sstevel@tonic-gatesub NEXTKEY {
142*0Sstevel@tonic-gate    local($self) = @_;
143*0Sstevel@tonic-gate    local($klen, $vlen, $tsize, $rlen, $entries, $iterix) = @$self[1..6];
144*0Sstevel@tonic-gate    for (++$iterix; $iterix < $tsize->[1]; ++$iterix) {
145*0Sstevel@tonic-gate	next unless substr($$self[0], $iterix * $rlen, 1) eq "\2";
146*0Sstevel@tonic-gate	$$self[6] = $iterix;
147*0Sstevel@tonic-gate	return substr($$self[0], $iterix * $rlen + 1, $klen);
148*0Sstevel@tonic-gate    }
149*0Sstevel@tonic-gate    $$self[6] = -1;
150*0Sstevel@tonic-gate    undef;
151*0Sstevel@tonic-gate}
152*0Sstevel@tonic-gate
153*0Sstevel@tonic-gatesub EXISTS {
154*0Sstevel@tonic-gate    croak "Tie::SubstrHash does not support exists()";
155*0Sstevel@tonic-gate}
156*0Sstevel@tonic-gate
157*0Sstevel@tonic-gatesub hashkey {
158*0Sstevel@tonic-gate    croak(qq/Key "$key" is not $klen characters long/)
159*0Sstevel@tonic-gate	if length($key) != $klen;
160*0Sstevel@tonic-gate    $hash = 2;
161*0Sstevel@tonic-gate    for (unpack('C*', $key)) {
162*0Sstevel@tonic-gate	$hash = $hash * 33 + $_;
163*0Sstevel@tonic-gate	&_hashwrap if $hash >= 1e13;
164*0Sstevel@tonic-gate    }
165*0Sstevel@tonic-gate    &_hashwrap if $hash >= $tsize->[1];
166*0Sstevel@tonic-gate    $hash = 1 unless $hash;
167*0Sstevel@tonic-gate    $hashbase = $hash;
168*0Sstevel@tonic-gate}
169*0Sstevel@tonic-gate
170*0Sstevel@tonic-gatesub _hashwrap {
171*0Sstevel@tonic-gate    $hash -= int($hash / $tsize->[1]) * $tsize->[1];
172*0Sstevel@tonic-gate}
173*0Sstevel@tonic-gate
174*0Sstevel@tonic-gatesub rehash {
175*0Sstevel@tonic-gate    $hash += $hashbase;
176*0Sstevel@tonic-gate    $hash -= $tsize->[1] if $hash >= $tsize->[1];
177*0Sstevel@tonic-gate}
178*0Sstevel@tonic-gate
179*0Sstevel@tonic-gate# using POSIX::ceil() would be too heavy, and not all platforms have it.
180*0Sstevel@tonic-gatesub ceil {
181*0Sstevel@tonic-gate    my $num = shift;
182*0Sstevel@tonic-gate    $num = int($num + 1) unless $num == int $num;
183*0Sstevel@tonic-gate    return $num;
184*0Sstevel@tonic-gate}
185*0Sstevel@tonic-gate
186*0Sstevel@tonic-gate# See:
187*0Sstevel@tonic-gate#
188*0Sstevel@tonic-gate# http://www-groups.dcs.st-andrews.ac.uk/~history/HistTopics/Prime_numbers.html
189*0Sstevel@tonic-gate#
190*0Sstevel@tonic-gate
191*0Sstevel@tonic-gatesub findgteprime { # find the smallest prime integer greater than or equal to
192*0Sstevel@tonic-gate    use integer;
193*0Sstevel@tonic-gate
194*0Sstevel@tonic-gate    my $num = ceil(shift);
195*0Sstevel@tonic-gate    return 2 if $num <= 2;
196*0Sstevel@tonic-gate
197*0Sstevel@tonic-gate    $num++ unless $num % 2;
198*0Sstevel@tonic-gate    my $i;
199*0Sstevel@tonic-gate    my $sqrtnum = int sqrt $num;
200*0Sstevel@tonic-gate    my $sqrtnumsquared = $sqrtnum * $sqrtnum;
201*0Sstevel@tonic-gate
202*0Sstevel@tonic-gate  NUM:
203*0Sstevel@tonic-gate    for (;; $num += 2) {
204*0Sstevel@tonic-gate	if ($sqrtnumsquared < $num) {
205*0Sstevel@tonic-gate	    $sqrtnum++;
206*0Sstevel@tonic-gate	    $sqrtnumsquared = $sqrtnum * $sqrtnum;
207*0Sstevel@tonic-gate	}
208*0Sstevel@tonic-gate        for ($i = 3; $i <= $sqrtnum; $i += 2) {
209*0Sstevel@tonic-gate            next NUM unless $num % $i;
210*0Sstevel@tonic-gate        }
211*0Sstevel@tonic-gate        return $num;
212*0Sstevel@tonic-gate    }
213*0Sstevel@tonic-gate}
214*0Sstevel@tonic-gate
215*0Sstevel@tonic-gate1;
216