File Coverage

blib/lib/Digest/FNV/PurePerl.pm
Criterion Covered Total %
statement 131 131 100.0
branch n/a
condition n/a
subroutine 12 12 100.0
pod 5 5 100.0
total 148 148 100.0


line stmt bran cond sub pod time code
1             package Digest::FNV::PurePerl;
2              
3 3     3   72787 use warnings;
  3         7  
  3         96  
4 3     3   19 use strict;
  3         6  
  3         97  
5 3     3   16 use Exporter;
  3         10  
  3         611  
6              
7             our @ISA = qw(Exporter);
8             our @EXPORT = qw( fnv fnv32 fnv32a fnv64 fnv64a );
9              
10             =head1 NAME
11              
12             Digest::FNV::PurePerl - PurePerl implementation of Digest::FNV hashing algorithm.
13              
14             =head1 VERSION
15              
16             Version 0.01
17              
18             =cut
19              
20             our $VERSION = '0.03';
21              
22              
23             =head1 SYNOPSIS
24              
25             use Digest::FNV::PurePerl qw( fnv fnv32 fnv32a fnv64 fnv64a );
26              
27             my $fnv32hash = fnv("abc123");
28            
29             my $fnv32hash = fnv32("abc123"); # This does the same as the previous example
30            
31             my $hashref = fnv64("abc123");
32             $hashref->{bits}; # 32 for 32 bit systems, 64 for 64 bit systems
33             $hashref->{upper}; # Upper 32 bits
34             $hashref->{lower}; # Lower 32 bits
35             $hashref->{bigint} # use bigint; version of this possibly large number
36             $hashref->{longlong}; # 64 bit representation (i.e. (upper << 32) | lower)
37             # This value is useless on 32 bit systems
38              
39             =head1 DESCRIPTION
40              
41             FNV is a hashing algorithm for short to medium length strings. It is best
42             suited for strings that are typically around 1024 bytes or less (URLs, IP
43             addresses, hostnames, etc). This implementation is based on the code provided
44             by Landon Curt Noll.
45              
46             There are two slightly different algorithms. One is called FNV-1, and the other
47             is FNV-1a. Both algorithms are provided for each of 32 and 64 bit hash values.
48              
49             For full information on this algorithm please visit
50             http://isthe.com/chongo/tech/comp/fnv/
51              
52             The original Digest::FNV was written by Tan D Nguyen . This
53             version is a drop-in replacement (all existing code should continue to work).
54             However, it is a complete rewrite.
55              
56             This new version works on both 32 and 64 bit platforms.
57              
58             =head1 CAVEATS
59              
60             Part of the challenge of supporting this module are the differences between
61             32-bit and 64-bit architectures.
62              
63             In practice the values returned by these algorithms are often further processed
64             further algorithms. It is for that reason that the nature of what the
65             fnv64/fnv64a functions return is exposed. When trying to support both 64 and
66             32 bit architectures it is necessary.
67              
68             You cannot rely on only $hashref->{bigint} if you plan to perform and further
69             math on that value on 32 bit systems. You also cannot rely on
70             $hashref->{longlong} unless you know the architecture.
71              
72             This module attempts to provide all of the necessary information to arrive at a
73             true 64-bit value. Often times you're passing values to other software (a
74             database, for example), and that database probably provides 64-bit left shift
75             operations.
76              
77             =head1 EXPORT
78              
79             fnv fnv32 fnv32a fnv64 fnv64a
80              
81             =head1 FUNCTIONS
82              
83             =head2 fnv fnv32 fnv32a
84              
85             use Digest::FNV::PurePerl;
86              
87             my $url = "http://www.google.com/";
88             print fnv($url),"\n";
89             #-> 1088293357
90              
91             print fnv32($url),"\n";
92             #-> 1088293357
93              
94             print fnv32a($url),"\n";
95             #-> 912201313
96              
97             =cut
98              
99             sub fnv {
100 8     8 1 1730 my ($string) = @_;
101 8         14 my $fnv_prime = 0x01000193;
102 8         7 my $hval = 0x811c9dc5;
103              
104 8         8 if ((1<<32) == 4294967296) {
105 8         23 foreach my $c (unpack('C*', $string)) {
106 140         172 $hval += (
107             (($hval << 1) ) +
108             (($hval << 4) ) +
109             (($hval << 7) ) +
110             (($hval << 8) ) +
111             (($hval << 24) ) );
112 140         127 $hval = $hval & 0xffffffff;
113 140         154 $hval ^= $c;
114             }
115             }
116             else {
117 3     3   3478 use bigint;
  3         21706  
  3         13  
118             foreach my $c (unpack('C*', $string)) {
119             $hval += (
120             (($hval << 1) ) +
121             (($hval << 4) ) +
122             (($hval << 7) ) +
123             (($hval << 8) ) +
124             (($hval << 24) ) );
125             $hval = $hval & 0xffffffff;
126             $hval ^= $c;
127             }
128             }
129 8         24 return $hval;
130             }
131              
132             sub fnv32 {
133 4     4 1 1892 my ($string) = @_;
134 4         9 return fnv($string);
135             }
136              
137             sub fnv32a {
138 4     4 1 1842 my ($string) = @_;
139 4         6 my $fnv_prime = 0x01000193;
140 4         5 my $hval = 0x811c9dc5;
141              
142 4         5 if ((1<<32) == 4294967296) {
143 4         14 foreach my $c (unpack('C*', $string)) {
144 70         61 $hval ^= $c;
145 70         87 $hval += (
146             (($hval << 1) ) +
147             (($hval << 4) ) +
148             (($hval << 7) ) +
149             (($hval << 8) ) +
150             (($hval << 24) ) );
151 70         85 $hval = $hval & 0xffffffff;
152             }
153             }
154             else {
155 3     3   238168 use bigint;
  3         8  
  3         14  
156             foreach my $c (unpack('C*', $string)) {
157             $hval ^= $c;
158             $hval += (
159             (($hval << 1) ) +
160             (($hval << 4) ) +
161             (($hval << 7) ) +
162             (($hval << 8) ) +
163             (($hval << 24) ) );
164             $hval = $hval & 0xffffffff;
165             }
166             }
167 4         12 return $hval;
168             }
169              
170             =head2 fnv64 fnv64a
171              
172             use Digest::FNV::PurePerl;
173             use Data::Dumper;
174              
175             my $url = "http://www.google.com/";
176             my $fnv64hash = fnv64($url);
177             print Dumper($fnv64hash);
178             #-> $VAR1 = {
179             #-> 'bigint' => bless( {
180             #-> 'value' => [
181             #-> 290527405,
182             #-> 988083964,
183             #-> 9
184             #-> ],
185             #-> 'sign' => '+'
186             #-> }, 'Math::BigInt' ),
187             #-> 'upper' => 2325532018,
188             #-> 'lower' => 1179644077,
189             #-> 'longlong' => '9988083964290527405',
190             #-> 'bits' => 64
191             #-> };
192              
193             fnv65a($url);
194              
195             =cut
196              
197             sub fnv64 {
198 4     4 1 1956 my ($string) = @_;
199 4         9 my $fnv_prime = 0;
200 4         32 my %hval = (
201             'bits' => 0,
202             'upper' => 0,
203             'lower' => 0,
204             'longlong' => 0,
205             'bigint' => 0
206             );
207              
208 4         6 if ((1<<32) == 4294967296) {
209 4         10 $hval{'bits'} = 64;
210             }
211             elsif ((1<<32) == 0) {
212             $hval{'bits'} = 32;
213             }
214             else {
215             $hval{'bits'} = undef;
216             }
217              
218 4         5 my $FNV_64_PRIME_LOW = 0x1b3; # lower bits of FNV prime
219 4         3 my $FNV_64_PRIME_SHIFT = 8; # top FNV prime shift above 2^32
220 4         15 my @val = (0, 0, 0, 0);
221 4         10 my @tmp = (0, 0, 0, 0);
222 4         4 my $FNV1_64_LOWER = 0x84222325;
223 4         7 my $FNV1_64_UPPER = 0xcbf29ce4;
224 4         5 my $upper;
225             my $lower;
226              
227 4         6 $val[0] = $FNV1_64_LOWER;
228 4         8 $val[1] = ($val[0] >> 16);
229 4         6 $val[0] &= 0xffff;
230 4         6 $val[2] = $FNV1_64_UPPER;
231 4         6 $val[3] = ($val[2] >> 16);
232 4         7 $val[2] &= 0xffff;
233              
234 4         18 foreach my $c (unpack('C*', $string)) {
235 70         91 $tmp[0] = $val[0] * $FNV_64_PRIME_LOW;
236 70         86 $tmp[1] = $val[1] * $FNV_64_PRIME_LOW;
237 70         71 $tmp[2] = $val[2] * $FNV_64_PRIME_LOW;
238 70         70 $tmp[3] = $val[3] * $FNV_64_PRIME_LOW;
239             # multiply by the other non-zero digit
240 70         75 $tmp[2] += $val[0] << $FNV_64_PRIME_SHIFT; # tmp[2] += val[0] * 0x100
241 70         77 $tmp[3] += $val[1] << $FNV_64_PRIME_SHIFT; # tmp[3] += val[1] * 0x100
242             # propagate carries
243 70         70 $tmp[1] += ($tmp[0] >> 16);
244 70         71 $val[0] = $tmp[0] & 0xffff;
245 70         74 $tmp[2] += ($tmp[1] >> 16);
246 70         73 $val[1] = $tmp[1] & 0xffff;
247 70         81 $val[3] = $tmp[3] + ($tmp[2] >> 16);
248 70         73 $val[2] = $tmp[2] & 0xffff;
249              
250             # Doing a val[3] &= 0xffff; is not really needed since it simply
251             # removes multiples of 2^64. We can discard these excess bits
252             # outside of the loop when we convert to Fnv64_t.
253            
254 70         78 $val[0] &= 0xffff;
255 70         73 $val[1] &= 0xffff;
256 70         87 $val[2] &= 0xffff;
257 70         61 $val[3] &= 0xffff;
258              
259 70         68 $tmp[0] &= 0xffff;
260 70         62 $tmp[1] &= 0xffff;
261 70         67 $tmp[2] &= 0xffff;
262 70         61 $tmp[3] &= 0xffff;
263              
264             # xor the bottom with the current octet
265 70         122 $val[0] ^= $c;
266             }
267 4         12 $upper = $hval{'upper'} = (($val[3]<<16) | $val[2]) & 0xffffffff;
268 4         10 $lower = $hval{'lower'} = (($val[1]<<16) | $val[0]) & 0xffffffff;
269 4         8 $hval{'longlong'} = ($upper << 32) | $lower;
270 3     3   5239 use bigint;
  3         7  
  3         13  
271 4         87 $hval{'bigint'} = (($upper << 32) | $lower);
272 4         3764 return \%hval;
273             }
274              
275             sub fnv64a {
276 4     4 1 2276 my ($string) = @_;
277 4         10 my $fnv_prime = 0;
278 4         20 my %hval = (
279             'bits' => 0,
280             'upper' => 0,
281             'lower' => 0,
282             'longlong' => 0,
283             'bigint' => 0
284             );
285              
286 4         7 if ((1<<32) == 4294967296) {
287 4         7 $hval{'bits'} = 64;
288             }
289             elsif ((1<<32) == 0) {
290             $hval{'bits'} = 32;
291             }
292             else {
293             $hval{'bits'} = undef;
294             }
295              
296 4         7 my $FNV_64_PRIME_LOW = 0x1b3; # lower bits of FNV prime
297 4         6 my $FNV_64_PRIME_SHIFT = 8; # top FNV prime shift above 2^32
298 4         9 my @val = (0, 0, 0, 0);
299 4         9 my @tmp = (0, 0, 0, 0);
300 4         5 my $FNV1_64_LOWER = 0x84222325;
301 4         7 my $FNV1_64_UPPER = 0xcbf29ce4;
302 4         5 my $upper;
303             my $lower;
304              
305 4         7 $val[0] = $FNV1_64_LOWER;
306 4         21 $val[1] = ($val[0] >> 16);
307 4         6 $val[0] &= 0xffff;
308 4         7 $val[2] = $FNV1_64_UPPER;
309 4         6 $val[3] = ($val[2] >> 16);
310 4         6 $val[2] &= 0xffff;
311              
312 4         23 foreach my $c (unpack('C*', $string)) {
313             # xor the bottom with the current octet
314 70         90 $val[0] ^= $c;
315              
316 70         95 $tmp[0] = $val[0] * $FNV_64_PRIME_LOW;
317 70         92 $tmp[1] = $val[1] * $FNV_64_PRIME_LOW;
318 70         98 $tmp[2] = $val[2] * $FNV_64_PRIME_LOW;
319 70         92 $tmp[3] = $val[3] * $FNV_64_PRIME_LOW;
320             # multiply by the other non-zero digit
321 70         88 $tmp[2] += $val[0] << $FNV_64_PRIME_SHIFT; # tmp[2] += val[0] * 0x100
322 70         95 $tmp[3] += $val[1] << $FNV_64_PRIME_SHIFT; # tmp[3] += val[1] * 0x100
323             # propagate carries
324 70         91 $tmp[1] += ($tmp[0] >> 16);
325 70         91 $val[0] = $tmp[0] & 0xffff;
326 70         144 $tmp[2] += ($tmp[1] >> 16);
327 70         95 $val[1] = $tmp[1] & 0xffff;
328 70         100 $val[3] = $tmp[3] + ($tmp[2] >> 16);
329 70         84 $val[2] = $tmp[2] & 0xffff;
330              
331             # Doing a val[3] &= 0xffff; is not really needed since it simply
332             # removes multiples of 2^64. We can discard these excess bits
333             # outside of the loop when we convert to Fnv64_t.
334              
335 70         90 $val[0] &= 0xffff;
336 70         89 $val[1] &= 0xffff;
337 70         77 $val[2] &= 0xffff;
338 70         81 $val[3] &= 0xffff;
339              
340 70         76 $tmp[0] &= 0xffff;
341 70         79 $tmp[1] &= 0xffff;
342 70         82 $tmp[2] &= 0xffff;
343 70         145 $tmp[3] &= 0xffff;
344             }
345 4         15 $upper = $hval{'upper'} = (($val[3]<<16) | $val[2]) & 0xffffffff;
346 4         9 $lower = $hval{'lower'} = (($val[1]<<16) | $val[0]) & 0xffffffff;
347 4         6 $hval{'longlong'} = ($upper << 32) | $lower;
348 3     3   3964 use bigint;
  3         7  
  3         12  
349 4         17 $hval{'bigint'} = (($upper << 32) | $lower);
350             #print "Bigint: ".$hval{'bigint'}."\n";
351             #print "Longlong: ".$hval{'longlong'}."\n";
352             #print "Upper: ".$upper."\n";
353             #print "Lower: ".$lower."\n";
354 4         2442 return \%hval;
355             }
356              
357             =head1 AUTHOR
358              
359             Jeffrey Webster, C<< >>
360              
361             =head1 BUGS
362              
363             Please report any bugs or feature requests to C, or through
364             the web interface at L. I will be notified, and then you'll
365             automatically be notified of progress on your bug as I make changes.
366              
367              
368             =head1 SUPPORT
369              
370             You can find documentation for this module with the perldoc command.
371              
372             perldoc Digest::FNV::PurePerl
373              
374              
375             You can also look for information at:
376              
377             =over 4
378              
379             =item * RT: CPAN's request tracker
380              
381             L
382              
383             =item * AnnoCPAN: Annotated CPAN documentation
384              
385             L
386              
387             =item * CPAN Ratings
388              
389             L
390              
391             =item * Search CPAN
392              
393             L
394              
395             =back
396              
397              
398             =head1 ACKNOWLEDGEMENTS
399              
400             Inspired by Fowler, Noll, and Vo for their nifty little hashing algorithm.
401              
402             Thanks to Tan Nguyen for handing over control of Digest::FNV
403              
404             =head1 COPYRIGHT & LICENSE
405              
406             Copyright 2010 Jeffrey Webster.
407              
408             This program is free software; you can redistribute it and/or modify it
409             under the terms of either: the GNU General Public License as published
410             by the Free Software Foundation; or the Artistic License.
411              
412             See http://dev.perl.org/licenses/ for more information.
413              
414              
415             =cut
416              
417             1; # End of Digest::FNV::PurePerl