diff --git a/CHANGELOG.md b/CHANGELOG.md index 38d3f4e..a9193f3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Implement the FNV (Fowler–Noll–Vo) hashing algorithm in the project and drop dependency on the `fnv-hash` gem. [PR #14](https://github.com/riverqueue/riverqueue-ruby/pull/14). + ## [0.3.0] - 2024-04-27 ### Added diff --git a/Gemfile.lock b/Gemfile.lock index 6541b1f..b3b162d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,7 +2,6 @@ PATH remote: . specs: riverqueue (0.3.0) - fnv-hash GEM remote: https://rubygems.org/ @@ -32,7 +31,6 @@ GEM drb (2.2.1) ffi (1.16.3) fileutils (1.7.2) - fnv-hash (0.2.0) i18n (1.14.4) concurrent-ruby (~> 1.0) io-console (0.7.2) diff --git a/lib/client.rb b/lib/client.rb index b274f17..1e771ca 100644 --- a/lib/client.rb +++ b/lib/client.rb @@ -191,11 +191,11 @@ def insert_many(args) @driver.transaction do lock_key = if @advisory_lock_prefix.nil? - Fnv::Hash.fnv_1(lock_str, size: 64) + FNV.fnv1_hash(lock_str, size: 64) else # Steep should be able to tell that this is not nil, but it can't. prefix = @advisory_lock_prefix #: Integer # rubocop:disable Layout/LeadingCommentSpace - prefix << 32 | Fnv::Hash.fnv_1(lock_str, size: 32) + prefix << 32 | FNV.fnv1_hash(lock_str, size: 32) end # Packs a uint64 then unpacks to int64, which we need to do to keep the diff --git a/lib/fnv.rb b/lib/fnv.rb new file mode 100644 index 0000000..59761da --- /dev/null +++ b/lib/fnv.rb @@ -0,0 +1,41 @@ +module River + # FNV is the Fowler–Noll–Vo hash function, a simple hash that's very easy to + # implement, and hash the perfect characteristics for use with the 64 bits of + # available space in a PG advisory lock. + # + # I'm implemented it myself so that the River gem can stay dependency free + # (and because it's quite easy to do). + module FNV + def self.fnv1_hash(str, size:) + hash = OFFSET_BASIS.fetch(size) + mask = MASK.fetch(size) + prime = PRIME.fetch(size) + + str.each_byte do |byte| + hash *= prime + hash &= mask # take lower N bits of multiplication product + hash ^= byte + end + + hash + end + + MASK = { + 32 => 0xffffffff, # mask 32 bits long + 64 => 0xffffffffffffffff # mask 64 bits long + }.freeze + private_constant :MASK + + OFFSET_BASIS = { + 32 => 0x811c9dc5, + 64 => 0xcbf29ce484222325 + }.freeze + private_constant :OFFSET_BASIS + + PRIME = { + 32 => 0x01000193, + 64 => 0x00000100000001B3 + }.freeze + private_constant :PRIME + end +end diff --git a/lib/riverqueue.rb b/lib/riverqueue.rb index fd8309b..87f62b2 100644 --- a/lib/riverqueue.rb +++ b/lib/riverqueue.rb @@ -1,5 +1,6 @@ require "json" +require_relative "fnv" require_relative "insert_opts" require_relative "job" diff --git a/riverqueue.gemspec b/riverqueue.gemspec index 2adad30..a2b8758 100644 --- a/riverqueue.gemspec +++ b/riverqueue.gemspec @@ -14,6 +14,4 @@ Gem::Specification.new do |s| "rubygems_mfa_required" => "true", "source_code_uri" => "https://github.com/riverqueue/riverqueue-ruby" } - - s.add_dependency "fnv-hash" end diff --git a/sig/fnv.rbs b/sig/fnv.rbs new file mode 100644 index 0000000..3a016dc --- /dev/null +++ b/sig/fnv.rbs @@ -0,0 +1,9 @@ +module River + module FNV + def self.fnv1_hash: (String, size: 32 | 64) -> Integer + + MASK: Hash[Integer, Integer] + OFFSET_BASIS: Hash[Integer, Integer] + PRIME: Hash[Integer, Integer] + end +end \ No newline at end of file diff --git a/spec/client_spec.rb b/spec/client_spec.rb index 63c394f..23ba04a 100644 --- a/spec/client_spec.rb +++ b/spec/client_spec.rb @@ -222,7 +222,7 @@ def check_bigint_bounds(int) lock_str = "unique_keykind=#{job_args.kind}" \ "&queue=#{River::QUEUE_DEFAULT}" \ "&state=#{River::Client.const_get(:DEFAULT_UNIQUE_STATES).join(",")}" - expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, Fnv::Hash.fnv_1(lock_str, size: 64)))]) + expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, River::FNV.fnv1_hash(lock_str, size: 64)))]) end it "inserts a new unique job with all options" do @@ -245,7 +245,7 @@ def check_bigint_bounds(int) "&period=#{client.send(:truncate_time, now, 15 * 60).utc.strftime("%FT%TZ")}" \ "&queue=#{River::QUEUE_DEFAULT}" \ "&state=#{[River::JOB_STATE_AVAILABLE].join(",")}" - expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, Fnv::Hash.fnv_1(lock_str, size: 64)))]) + expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, River::FNV.fnv1_hash(lock_str, size: 64)))]) end it "inserts a new unique job with advisory lock prefix" do @@ -265,7 +265,7 @@ def check_bigint_bounds(int) lock_str = "unique_keykind=#{job_args.kind}" \ "&queue=#{River::QUEUE_DEFAULT}" \ "&state=#{River::Client.const_get(:DEFAULT_UNIQUE_STATES).join(",")}" - expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, 123456 << 32 | Fnv::Hash.fnv_1(lock_str, size: 32)))]) + expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, 123456 << 32 | River::FNV.fnv1_hash(lock_str, size: 32)))]) lock_key = mock_driver.advisory_lock_calls[0] expect(lock_key >> 32).to eq(123456) @@ -300,7 +300,7 @@ def job_args_to_row(job_args, insert_opts: River::InsertOpts.new) "&period=#{client.send(:truncate_time, now, 15 * 60).utc.strftime("%FT%TZ")}" \ "&queue=#{River::QUEUE_DEFAULT}" \ "&state=#{[River::JOB_STATE_AVAILABLE].join(",")}" - expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, Fnv::Hash.fnv_1(lock_str, size: 64)))]) + expect(mock_driver.advisory_lock_calls).to eq([check_bigint_bounds(client.send(:uint64_to_int64, River::FNV.fnv1_hash(lock_str, size: 64)))]) end it "skips unique check if unique opts empty" do diff --git a/spec/fnv_spec.rb b/spec/fnv_spec.rb new file mode 100644 index 0000000..142c974 --- /dev/null +++ b/spec/fnv_spec.rb @@ -0,0 +1,649 @@ +require "spec_helper" + +describe River::FNV do + describe "#fnv1_hash" do + it "hashes strings to 32 bits" do + TEST_STRS.each do |test_str| + expect(River::FNV.fnv1_hash(test_str, size: 32)).to eq(FNV1_32_HASHES[test_str]) + end + end + + it "hashes strings to 64 bits" do + TEST_STRS.each do |test_str| + expect(River::FNV.fnv1_hash(test_str, size: 64)).to eq(FNV1_64_HASHES[test_str]) + end + end + end + + # Just a sanity check to make sure the number of 0xffs is right. + describe "MASK" do + it "contains masks equal to integer limits" do + expect(River::FNV.const_get(:MASK)[32]).to eq(4_294_967_295) + expect(River::FNV.const_get(:MASK)[64]).to eq(18_446_744_073_709_551_615) + end + end +end + +# +# Test strings pulled from the Python FNV hash test suite: +# +# https://github.com/znerol/py-fnvhash/blob/master/fnvhash/test/vector.py +# + +TEST_STRS = [ + "", + "a", + "b", + "c", + "d", + "e", + "f", + "fo", + "foo", + "foob", + "fooba", + "foobar", + "" + "\x00", + "a" + "\x00", + "b" + "\x00", + "c" + "\x00", + "d" + "\x00", + "e" + "\x00", + "f" + "\x00", + "fo" + "\x00", + "foo" + "\x00", + "foob" + "\x00", + "fooba" + "\x00", + "foobar" + "\x00", + "ch", + "cho", + "chon", + "chong", + "chongo", + "chongo ", + "chongo w", + "chongo wa", + "chongo was", + "chongo was ", + "chongo was h", + "chongo was he", + "chongo was her", + "chongo was here", + "chongo was here!", + "chongo was here!\n", + "ch" + "\x00", + "cho" + "\x00", + "chon" + "\x00", + "chong" + "\x00", + "chongo" + "\x00", + "chongo " + "\x00", + "chongo w" + "\x00", + "chongo wa" + "\x00", + "chongo was" + "\x00", + "chongo was " + "\x00", + "chongo was h" + "\x00", + "chongo was he" + "\x00", + "chongo was her" + "\x00", + "chongo was here" + "\x00", + "chongo was here!" + "\x00", + "chongo was here!\n" + "\x00", + "cu", + "cur", + "curd", + "curds", + "curds ", + "curds a", + "curds an", + "curds and", + "curds and ", + "curds and w", + "curds and wh", + "curds and whe", + "curds and whey", + "curds and whey\n", + "cu" + "\x00", + "cur" + "\x00", + "curd" + "\x00", + "curds" + "\x00", + "curds " + "\x00", + "curds a" + "\x00", + "curds an" + "\x00", + "curds and" + "\x00", + "curds and " + "\x00", + "curds and w" + "\x00", + "curds and wh" + "\x00", + "curds and whe" + "\x00", + "curds and whey" + "\x00", + "curds and whey\n" + "\x00", + "hi", + "hi" + "\x00", + "hello", + "hello" + "\x00", + "\xff\x00\x00\x01", + "\x01\x00\x00\xff", + "\xff\x00\x00\x02", + "\x02\x00\x00\xff", + "\xff\x00\x00\x03", + "\x03\x00\x00\xff", + "\xff\x00\x00\x04", + "\x04\x00\x00\xff", + "\x40\x51\x4e\x44", + "\x44\x4e\x51\x40", + "\x40\x51\x4e\x4a", + "\x4a\x4e\x51\x40", + "\x40\x51\x4e\x54", + "\x54\x4e\x51\x40", + "127.0.0.1", + "127.0.0.1" + "\x00", + "127.0.0.2", + "127.0.0.2" + "\x00", + "127.0.0.3", + "127.0.0.3" + "\x00", + "64.81.78.68", + "64.81.78.68" + "\x00", + "64.81.78.74", + "64.81.78.74" + "\x00", + "64.81.78.84", + "64.81.78.84" + "\x00", + "feedface", + "feedface" + "\x00", + "feedfacedaffdeed", + "feedfacedaffdeed" + "\x00", + "feedfacedeadbeef", + "feedfacedeadbeef" + "\x00", + "line 1\nline 2\nline 3", + "chongo /\\../\\", + "chongo /\\../\\" + "\x00", + "chongo (Landon Curt Noll) /\\../\\", + "chongo (Landon Curt Noll) /\\../\\" + "\x00", + "http://antwrp.gsfc.nasa.gov/apod/astropix.html", + "http://en.wikipedia.org/wiki/Fowler_Noll_Vo_hash", + "http://epod.usra.edu/", + "http://exoplanet.eu/", + "http://hvo.wr.usgs.gov/cam3/", + "http://hvo.wr.usgs.gov/cams/HMcam/", + "http://hvo.wr.usgs.gov/kilauea/update/deformation.html", + "http://hvo.wr.usgs.gov/kilauea/update/images.html", + "http://hvo.wr.usgs.gov/kilauea/update/maps.html", + "http://hvo.wr.usgs.gov/volcanowatch/current_issue.html", + "http://neo.jpl.nasa.gov/risk/", + "http://norvig.com/21-days.html", + "http://primes.utm.edu/curios/home.php", + "http://slashdot.org/", + "http://tux.wr.usgs.gov/Maps/155.25-19.5.html", + "http://volcano.wr.usgs.gov/kilaueastatus.php", + "http://www.avo.alaska.edu/activity/Redoubt.php", + "http://www.dilbert.com/fast/", + "http://www.fourmilab.ch/gravitation/orbits/", + "http://www.fpoa.net/", + "http://www.ioccc.org/index.html", + "http://www.isthe.com/cgi-bin/number.cgi", + "http://www.isthe.com/chongo/bio.html", + "http://www.isthe.com/chongo/index.html", + "http://www.isthe.com/chongo/src/calc/lucas-calc", + "http://www.isthe.com/chongo/tech/astro/venus2004.html", + "http://www.isthe.com/chongo/tech/astro/vita.html", + "http://www.isthe.com/chongo/tech/comp/c/expert.html", + "http://www.isthe.com/chongo/tech/comp/calc/index.html", + "http://www.isthe.com/chongo/tech/comp/fnv/index.html", + "http://www.isthe.com/chongo/tech/math/number/howhigh.html", + "http://www.isthe.com/chongo/tech/math/number/number.html", + "http://www.isthe.com/chongo/tech/math/prime/mersenne.html", + "http://www.isthe.com/chongo/tech/math/prime/mersenne.html#largest", + "http://www.lavarnd.org/cgi-bin/corpspeak.cgi", + "http://www.lavarnd.org/cgi-bin/haiku.cgi", + "http://www.lavarnd.org/cgi-bin/rand-none.cgi", + "http://www.lavarnd.org/cgi-bin/randdist.cgi", + "http://www.lavarnd.org/index.html", + "http://www.lavarnd.org/what/nist-test.html", + "http://www.macosxhints.com/", + "http://www.mellis.com/", + "http://www.nature.nps.gov/air/webcams/parks/havoso2alert/havoalert.cfm", + "http://www.nature.nps.gov/air/webcams/parks/havoso2alert/timelines_24.cfm", + "http://www.paulnoll.com/", + "http://www.pepysdiary.com/", + "http://www.sciencenews.org/index/home/activity/view", + "http://www.skyandtelescope.com/", + "http://www.sput.nl/~rob/sirius.html", + "http://www.systemexperts.com/", + "http://www.tq-international.com/phpBB3/index.php", + "http://www.travelquesttours.com/index.htm", + "http://www.wunderground.com/global/stations/89606.html", + "21701" * 10, + "M21701" * 10, + "2^21701-1" * 10, + "\x54\xc5" * 10, + "\xc5\x54" * 10, + "23209" * 10, + "M23209" * 10, + "2^23209-1" * 10, + "\x5a\xa9" * 10, + "\xa9\x5a" * 10, + "391581216093" * 10, + "391581*2^216093-1" * 10, + "\x05\xf9\x9d\x03\x4c\x81" * 10, + "FEDCBA9876543210" * 10, + "\xfe\xdc\xba\x98\x76\x54\x32\x10" * 10, + "EFCDAB8967452301" * 10, + "\xef\xcd\xab\x89\x67\x45\x23\x01" * 10, + "0123456789ABCDEF" * 10, + "\x01\x23\x45\x67\x89\xab\xcd\xef" * 10, + "1032547698BADCFE" * 10, + "\x10\x32\x54\x76\x98\xba\xdc\xfe" * 10, + "\x00" * 500, + "\x07" * 500, + "~" * 500, + "\x7f" * 500 +] + +FNV1_32_HASHES = { + TEST_STRS[0] => 0x811c9dc5, + TEST_STRS[1] => 0x050c5d7e, + TEST_STRS[2] => 0x050c5d7d, + TEST_STRS[3] => 0x050c5d7c, + TEST_STRS[4] => 0x050c5d7b, + TEST_STRS[5] => 0x050c5d7a, + TEST_STRS[6] => 0x050c5d79, + TEST_STRS[7] => 0x6b772514, + TEST_STRS[8] => 0x408f5e13, + TEST_STRS[9] => 0xb4b1178b, + TEST_STRS[10] => 0xfdc80fb0, + TEST_STRS[11] => 0x31f0b262, + TEST_STRS[12] => 0x050c5d1f, + TEST_STRS[13] => 0x70772d5a, + TEST_STRS[14] => 0x6f772bc7, + TEST_STRS[15] => 0x6e772a34, + TEST_STRS[16] => 0x6d7728a1, + TEST_STRS[17] => 0x6c77270e, + TEST_STRS[18] => 0x6b77257b, + TEST_STRS[19] => 0x408f5e7c, + TEST_STRS[20] => 0xb4b117e9, + TEST_STRS[21] => 0xfdc80fd1, + TEST_STRS[22] => 0x31f0b210, + TEST_STRS[23] => 0xffe8d046, + TEST_STRS[24] => 0x6e772a5c, + TEST_STRS[25] => 0x4197aebb, + TEST_STRS[26] => 0xfcc8100f, + TEST_STRS[27] => 0xfdf147fa, + TEST_STRS[28] => 0xbcd44ee1, + TEST_STRS[29] => 0x23382c13, + TEST_STRS[30] => 0x846d619e, + TEST_STRS[31] => 0x1630abdb, + TEST_STRS[32] => 0xc99e89b2, + TEST_STRS[33] => 0x1692c316, + TEST_STRS[34] => 0x9f091bca, + TEST_STRS[35] => 0x2556be9b, + TEST_STRS[36] => 0x628e0e73, + TEST_STRS[37] => 0x98a0bf6c, + TEST_STRS[38] => 0xb10d5725, + TEST_STRS[39] => 0xdd002f35, + TEST_STRS[40] => 0x4197aed4, + TEST_STRS[41] => 0xfcc81061, + TEST_STRS[42] => 0xfdf1479d, + TEST_STRS[43] => 0xbcd44e8e, + TEST_STRS[44] => 0x23382c33, + TEST_STRS[45] => 0x846d61e9, + TEST_STRS[46] => 0x1630abba, + TEST_STRS[47] => 0xc99e89c1, + TEST_STRS[48] => 0x1692c336, + TEST_STRS[49] => 0x9f091ba2, + TEST_STRS[50] => 0x2556befe, + TEST_STRS[51] => 0x628e0e01, + TEST_STRS[52] => 0x98a0bf09, + TEST_STRS[53] => 0xb10d5704, + TEST_STRS[54] => 0xdd002f3f, + TEST_STRS[55] => 0x1c4a506f, + TEST_STRS[56] => 0x6e772a41, + TEST_STRS[57] => 0x26978421, + TEST_STRS[58] => 0xe184ff97, + TEST_STRS[59] => 0x9b5e5ac6, + TEST_STRS[60] => 0x5b88e592, + TEST_STRS[61] => 0xaa8164b7, + TEST_STRS[62] => 0x20b18c7b, + TEST_STRS[63] => 0xf28025c5, + TEST_STRS[64] => 0x84bb753f, + TEST_STRS[65] => 0x3219925a, + TEST_STRS[66] => 0x384163c6, + TEST_STRS[67] => 0x54f010d7, + TEST_STRS[68] => 0x8cea820c, + TEST_STRS[69] => 0xe12ab8ee, + TEST_STRS[70] => 0x26978453, + TEST_STRS[71] => 0xe184fff3, + TEST_STRS[72] => 0x9b5e5ab5, + TEST_STRS[73] => 0x5b88e5b2, + TEST_STRS[74] => 0xaa8164d6, + TEST_STRS[75] => 0x20b18c15, + TEST_STRS[76] => 0xf28025a1, + TEST_STRS[77] => 0x84bb751f, + TEST_STRS[78] => 0x3219922d, + TEST_STRS[79] => 0x384163ae, + TEST_STRS[80] => 0x54f010b2, + TEST_STRS[81] => 0x8cea8275, + TEST_STRS[82] => 0xe12ab8e4, + TEST_STRS[83] => 0x64411eaa, + TEST_STRS[84] => 0x6977223c, + TEST_STRS[85] => 0x428ae474, + TEST_STRS[86] => 0xb6fa7167, + TEST_STRS[87] => 0x73408525, + TEST_STRS[88] => 0xb78320a1, + TEST_STRS[89] => 0x0caf4135, + TEST_STRS[90] => 0xb78320a2, + TEST_STRS[91] => 0xcdc88e80, + TEST_STRS[92] => 0xb78320a3, + TEST_STRS[93] => 0x8ee1dbcb, + TEST_STRS[94] => 0xb78320a4, + TEST_STRS[95] => 0x4ffb2716, + TEST_STRS[96] => 0x860632aa, + TEST_STRS[97] => 0xcc2c5c64, + TEST_STRS[98] => 0x860632a4, + TEST_STRS[99] => 0x2a7ec4a6, + TEST_STRS[100] => 0x860632ba, + TEST_STRS[101] => 0xfefe8e14, + TEST_STRS[102] => 0x0a3cffd8, + TEST_STRS[103] => 0xf606c108, + TEST_STRS[104] => 0x0a3cffdb, + TEST_STRS[105] => 0xf906c5c1, + TEST_STRS[106] => 0x0a3cffda, + TEST_STRS[107] => 0xf806c42e, + TEST_STRS[108] => 0xc07167d7, + TEST_STRS[109] => 0xc9867775, + TEST_STRS[110] => 0xbf716668, + TEST_STRS[111] => 0xc78435b8, + TEST_STRS[112] => 0xc6717155, + TEST_STRS[113] => 0xb99568cf, + TEST_STRS[114] => 0x7662e0d6, + TEST_STRS[115] => 0x33a7f0e2, + TEST_STRS[116] => 0xc2732f95, + TEST_STRS[117] => 0xb053e78f, + TEST_STRS[118] => 0x3a19c02a, + TEST_STRS[119] => 0xa089821e, + TEST_STRS[120] => 0x31ae8f83, + TEST_STRS[121] => 0x995fa9c4, + TEST_STRS[122] => 0x35983f8c, + TEST_STRS[123] => 0x5036a251, + TEST_STRS[124] => 0x97018583, + TEST_STRS[125] => 0xb4448d60, + TEST_STRS[126] => 0x025dfe59, + TEST_STRS[127] => 0xc5eab3af, + TEST_STRS[128] => 0x7d21ba1e, + TEST_STRS[129] => 0x7704cddb, + TEST_STRS[130] => 0xd0071bfe, + TEST_STRS[131] => 0x0ff3774c, + TEST_STRS[132] => 0xb0fea0ea, + TEST_STRS[133] => 0x58177303, + TEST_STRS[134] => 0x4f599cda, + TEST_STRS[135] => 0x3e590a47, + TEST_STRS[136] => 0x965595f8, + TEST_STRS[137] => 0xc37f178d, + TEST_STRS[138] => 0x9711dd26, + TEST_STRS[139] => 0x23c99b7f, + TEST_STRS[140] => 0x6e568b17, + TEST_STRS[141] => 0x43f0245b, + TEST_STRS[142] => 0xbcb7a001, + TEST_STRS[143] => 0x12e6dffe, + TEST_STRS[144] => 0x0792f2d6, + TEST_STRS[145] => 0xb966936b, + TEST_STRS[146] => 0x46439ac5, + TEST_STRS[147] => 0x728d49af, + TEST_STRS[148] => 0xd33745c9, + TEST_STRS[149] => 0xbc382a57, + TEST_STRS[150] => 0x4bda1d31, + TEST_STRS[151] => 0xce35ccae, + TEST_STRS[152] => 0x3b6eed94, + TEST_STRS[153] => 0x445c9c58, + TEST_STRS[154] => 0x3db8bf9d, + TEST_STRS[155] => 0x2dee116d, + TEST_STRS[156] => 0xc18738da, + TEST_STRS[157] => 0x5b156176, + TEST_STRS[158] => 0x2aa7d593, + TEST_STRS[159] => 0xb2409658, + TEST_STRS[160] => 0xe1489528, + TEST_STRS[161] => 0xfe1ee07e, + TEST_STRS[162] => 0xe8842315, + TEST_STRS[163] => 0x3a6a63a2, + TEST_STRS[164] => 0x06d2c18c, + TEST_STRS[165] => 0xf8ef7225, + TEST_STRS[166] => 0x843d3300, + TEST_STRS[167] => 0xbb24f7ae, + TEST_STRS[168] => 0x878c0ec9, + TEST_STRS[169] => 0xb557810f, + TEST_STRS[170] => 0x57423246, + TEST_STRS[171] => 0x87f7505e, + TEST_STRS[172] => 0xbb809f20, + TEST_STRS[173] => 0x8932abb5, + TEST_STRS[174] => 0x0a9b3aa0, + TEST_STRS[175] => 0xb8682a24, + TEST_STRS[176] => 0xa7ac1c56, + TEST_STRS[177] => 0x11409252, + TEST_STRS[178] => 0xa987f517, + TEST_STRS[179] => 0xf309e7ed, + TEST_STRS[180] => 0xc9e8f417, + TEST_STRS[181] => 0x7f447bdd, + TEST_STRS[182] => 0xb929adc5, + TEST_STRS[183] => 0x57022879, + TEST_STRS[184] => 0xdcfd2c49, + TEST_STRS[185] => 0x6edafff5, + TEST_STRS[186] => 0xf04fb1f1, + TEST_STRS[187] => 0xfb7de8b9, + TEST_STRS[188] => 0xc5f1d7e9, + TEST_STRS[189] => 0x32c1f439, + TEST_STRS[190] => 0x7fd3eb7d, + TEST_STRS[191] => 0x81597da5, + TEST_STRS[192] => 0x05eb7a25, + TEST_STRS[193] => 0x9c0fa1b5, + TEST_STRS[194] => 0x53ccb1c5, + TEST_STRS[195] => 0xfabece15, + TEST_STRS[196] => 0x4ad745a5, + TEST_STRS[197] => 0xe5bdc495, + TEST_STRS[198] => 0x23b3c0a5, + TEST_STRS[199] => 0xfa823dd5, + TEST_STRS[200] => 0x0c6c58b9, + TEST_STRS[201] => 0xe2dbccd5, + TEST_STRS[202] => 0xdb7f50f9 +} + +FNV1_64_HASHES = { + TEST_STRS[0] => 0xcbf29ce484222325, + TEST_STRS[1] => 0xaf63bd4c8601b7be, + TEST_STRS[2] => 0xaf63bd4c8601b7bd, + TEST_STRS[3] => 0xaf63bd4c8601b7bc, + TEST_STRS[4] => 0xaf63bd4c8601b7bb, + TEST_STRS[5] => 0xaf63bd4c8601b7ba, + TEST_STRS[6] => 0xaf63bd4c8601b7b9, + TEST_STRS[7] => 0x08326207b4eb2f34, + TEST_STRS[8] => 0xd8cbc7186ba13533, + TEST_STRS[9] => 0x0378817ee2ed65cb, + TEST_STRS[10] => 0xd329d59b9963f790, + TEST_STRS[11] => 0x340d8765a4dda9c2, + TEST_STRS[12] => 0xaf63bd4c8601b7df, + TEST_STRS[13] => 0x08326707b4eb37da, + TEST_STRS[14] => 0x08326607b4eb3627, + TEST_STRS[15] => 0x08326507b4eb3474, + TEST_STRS[16] => 0x08326407b4eb32c1, + TEST_STRS[17] => 0x08326307b4eb310e, + TEST_STRS[18] => 0x08326207b4eb2f5b, + TEST_STRS[19] => 0xd8cbc7186ba1355c, + TEST_STRS[20] => 0x0378817ee2ed65a9, + TEST_STRS[21] => 0xd329d59b9963f7f1, + TEST_STRS[22] => 0x340d8765a4dda9b0, + TEST_STRS[23] => 0x50a6d3b724a774a6, + TEST_STRS[24] => 0x08326507b4eb341c, + TEST_STRS[25] => 0xd8d5c8186ba98bfb, + TEST_STRS[26] => 0x1ccefc7ef118dbef, + TEST_STRS[27] => 0x0c92fab3ad3db77a, + TEST_STRS[28] => 0x9b77794f5fdec421, + TEST_STRS[29] => 0x0ac742dfe7874433, + TEST_STRS[30] => 0xd7dad5766ad8e2de, + TEST_STRS[31] => 0xa1bb96378e897f5b, + TEST_STRS[32] => 0x5b3f9b6733a367d2, + TEST_STRS[33] => 0xb07ce25cbea969f6, + TEST_STRS[34] => 0x8d9e9997f9df0d6a, + TEST_STRS[35] => 0x838c673d9603cb7b, + TEST_STRS[36] => 0x8b5ee8a5e872c273, + TEST_STRS[37] => 0x4507c4e9fb00690c, + TEST_STRS[38] => 0x4c9ca59581b27f45, + TEST_STRS[39] => 0xe0aca20b624e4235, + TEST_STRS[40] => 0xd8d5c8186ba98b94, + TEST_STRS[41] => 0x1ccefc7ef118db81, + TEST_STRS[42] => 0x0c92fab3ad3db71d, + TEST_STRS[43] => 0x9b77794f5fdec44e, + TEST_STRS[44] => 0x0ac742dfe7874413, + TEST_STRS[45] => 0xd7dad5766ad8e2a9, + TEST_STRS[46] => 0xa1bb96378e897f3a, + TEST_STRS[47] => 0x5b3f9b6733a367a1, + TEST_STRS[48] => 0xb07ce25cbea969d6, + TEST_STRS[49] => 0x8d9e9997f9df0d02, + TEST_STRS[50] => 0x838c673d9603cb1e, + TEST_STRS[51] => 0x8b5ee8a5e872c201, + TEST_STRS[52] => 0x4507c4e9fb006969, + TEST_STRS[53] => 0x4c9ca59581b27f64, + TEST_STRS[54] => 0xe0aca20b624e423f, + TEST_STRS[55] => 0x13998e580afa800f, + TEST_STRS[56] => 0x08326507b4eb3401, + TEST_STRS[57] => 0xd8d5ad186ba95dc1, + TEST_STRS[58] => 0x1c72e17ef0ca4e97, + TEST_STRS[59] => 0x2183c1b327c38ae6, + TEST_STRS[60] => 0xb66d096c914504f2, + TEST_STRS[61] => 0x404bf57ad8476757, + TEST_STRS[62] => 0x887976bd815498bb, + TEST_STRS[63] => 0x3afd7f02c2bf85a5, + TEST_STRS[64] => 0xfc4476b0eb70177f, + TEST_STRS[65] => 0x186d2da00f77ecba, + TEST_STRS[66] => 0xf97140fa48c74066, + TEST_STRS[67] => 0xa2b1cf49aa926d37, + TEST_STRS[68] => 0x0690712cd6cf940c, + TEST_STRS[69] => 0xf7045b3102b8906e, + TEST_STRS[70] => 0xd8d5ad186ba95db3, + TEST_STRS[71] => 0x1c72e17ef0ca4ef3, + TEST_STRS[72] => 0x2183c1b327c38a95, + TEST_STRS[73] => 0xb66d096c914504d2, + TEST_STRS[74] => 0x404bf57ad8476736, + TEST_STRS[75] => 0x887976bd815498d5, + TEST_STRS[76] => 0x3afd7f02c2bf85c1, + TEST_STRS[77] => 0xfc4476b0eb70175f, + TEST_STRS[78] => 0x186d2da00f77eccd, + TEST_STRS[79] => 0xf97140fa48c7400e, + TEST_STRS[80] => 0xa2b1cf49aa926d52, + TEST_STRS[81] => 0x0690712cd6cf9475, + TEST_STRS[82] => 0xf7045b3102b89064, + TEST_STRS[83] => 0x74f762479f9d6aea, + TEST_STRS[84] => 0x08326007b4eb2b9c, + TEST_STRS[85] => 0xd8c4c9186b9b1a14, + TEST_STRS[86] => 0x7b495389bdbdd4c7, + TEST_STRS[87] => 0x3b6dba0d69908e25, + TEST_STRS[88] => 0xd6b2b17bf4b71261, + TEST_STRS[89] => 0x447bfb7f98e615b5, + TEST_STRS[90] => 0xd6b2b17bf4b71262, + TEST_STRS[91] => 0x3bd2807f93fe1660, + TEST_STRS[92] => 0xd6b2b17bf4b71263, + TEST_STRS[93] => 0x3329057f8f16170b, + TEST_STRS[94] => 0xd6b2b17bf4b71264, + TEST_STRS[95] => 0x2a7f8a7f8a2e19b6, + TEST_STRS[96] => 0x23d3767e64b2f98a, + TEST_STRS[97] => 0xff768d7e4f9d86a4, + TEST_STRS[98] => 0x23d3767e64b2f984, + TEST_STRS[99] => 0xccd1837e334e4aa6, + TEST_STRS[100] => 0x23d3767e64b2f99a, + TEST_STRS[101] => 0x7691fd7e028f6754, + TEST_STRS[102] => 0x34ad3b1041204318, + TEST_STRS[103] => 0xa29e749ea9d201c8, + TEST_STRS[104] => 0x34ad3b104120431b, + TEST_STRS[105] => 0xa29e779ea9d206e1, + TEST_STRS[106] => 0x34ad3b104120431a, + TEST_STRS[107] => 0xa29e769ea9d2052e, + TEST_STRS[108] => 0x02a17ebca4aa3497, + TEST_STRS[109] => 0x229ef18bcd375c95, + TEST_STRS[110] => 0x02a17dbca4aa32c8, + TEST_STRS[111] => 0x229b6f8bcd3449d8, + TEST_STRS[112] => 0x02a184bca4aa3ed5, + TEST_STRS[113] => 0x22b3618bcd48c3ef, + TEST_STRS[114] => 0x5c2c346706186f36, + TEST_STRS[115] => 0xb78c410f5b84f8c2, + TEST_STRS[116] => 0xed9478212b267395, + TEST_STRS[117] => 0xd9bbb55c5256662f, + TEST_STRS[118] => 0x8c54f0203249438a, + TEST_STRS[119] => 0xbd9790b5727dc37e, + TEST_STRS[120] => 0xa64e5f36c9e2b0e3, + TEST_STRS[121] => 0x8fd0680da3088a04, + TEST_STRS[122] => 0x67aad32c078284cc, + TEST_STRS[123] => 0xb37d55d81c57b331, + TEST_STRS[124] => 0x55ac0f3829057c43, + TEST_STRS[125] => 0xcb27f4b8e1b6cc20, + TEST_STRS[126] => 0x26caf88bcbef2d19, + TEST_STRS[127] => 0x8e6e063b97e61b8f, + TEST_STRS[128] => 0xb42750f7f3b7c37e, + TEST_STRS[129] => 0xf3c6ba64cf7ca99b, + TEST_STRS[130] => 0xebfb69b427ea80fe, + TEST_STRS[131] => 0x39b50c3ed970f46c, + TEST_STRS[132] => 0x5b9b177aa3eb3e8a, + TEST_STRS[133] => 0x6510063ecf4ec903, + TEST_STRS[134] => 0x2b3bbd2c00797c7a, + TEST_STRS[135] => 0xf1d6204ff5cb4aa7, + TEST_STRS[136] => 0x4836e27ccf099f38, + TEST_STRS[137] => 0x82efbb0dd073b44d, + TEST_STRS[138] => 0x4a80c282ffd7d4c6, + TEST_STRS[139] => 0x305d1a9c9ee43bdf, + TEST_STRS[140] => 0x15c366948ffc6997, + TEST_STRS[141] => 0x80153ae218916e7b, + TEST_STRS[142] => 0xfa23e2bdf9e2a9e1, + TEST_STRS[143] => 0xd47e8d8a2333c6de, + TEST_STRS[144] => 0x7e128095f688b056, + TEST_STRS[145] => 0x2f5356890efcedab, + TEST_STRS[146] => 0x95c2b383014f55c5, + TEST_STRS[147] => 0x4727a5339ce6070f, + TEST_STRS[148] => 0xb0555ecd575108e9, + TEST_STRS[149] => 0x48d785770bb4af37, + TEST_STRS[150] => 0x09d4701c12af02b1, + TEST_STRS[151] => 0x79f031e78f3cf62e, + TEST_STRS[152] => 0x52a1ee85db1b5a94, + TEST_STRS[153] => 0x6bd95b2eb37fa6b8, + TEST_STRS[154] => 0x74971b7077aef85d, + TEST_STRS[155] => 0xb4e4fae2ffcc1aad, + TEST_STRS[156] => 0x2bd48bd898b8f63a, + TEST_STRS[157] => 0xe9966ac1556257f6, + TEST_STRS[158] => 0x92a3d1cd078ba293, + TEST_STRS[159] => 0xf81175a482e20ab8, + TEST_STRS[160] => 0x5bbb3de722e73048, + TEST_STRS[161] => 0x6b4f363492b9f2be, + TEST_STRS[162] => 0xc2d559df73d59875, + TEST_STRS[163] => 0xf75f62284bc7a8c2, + TEST_STRS[164] => 0xda8dd8e116a9f1cc, + TEST_STRS[165] => 0xbdc1e6ab76057885, + TEST_STRS[166] => 0xfec6a4238a1224a0, + TEST_STRS[167] => 0xc03f40f3223e290e, + TEST_STRS[168] => 0x1ed21673466ffda9, + TEST_STRS[169] => 0xdf70f906bb0dd2af, + TEST_STRS[170] => 0xf3dcda369f2af666, + TEST_STRS[171] => 0x9ebb11573cdcebde, + TEST_STRS[172] => 0x81c72d9077fedca0, + TEST_STRS[173] => 0x0ec074a31be5fb15, + TEST_STRS[174] => 0x2a8b3280b6c48f20, + TEST_STRS[175] => 0xfd31777513309344, + TEST_STRS[176] => 0x194534a86ad006b6, + TEST_STRS[177] => 0x3be6fdf46e0cfe12, + TEST_STRS[178] => 0x017cc137a07eb057, + TEST_STRS[179] => 0x9428fc6e7d26b54d, + TEST_STRS[180] => 0x9aaa2e3603ef8ad7, + TEST_STRS[181] => 0x82c6d3f3a0ccdf7d, + TEST_STRS[182] => 0xc86eeea00cf09b65, + TEST_STRS[183] => 0x705f8189dbb58299, + TEST_STRS[184] => 0x415a7f554391ca69, + TEST_STRS[185] => 0xcfe3d49fa2bdc555, + TEST_STRS[186] => 0xf0f9c56039b25191, + TEST_STRS[187] => 0x7075cb6abd1d32d9, + TEST_STRS[188] => 0x43c94e2c8b277509, + TEST_STRS[189] => 0x3cbfd4e4ea670359, + TEST_STRS[190] => 0xc05887810f4d019d, + TEST_STRS[191] => 0x14468ff93ac22dc5, + TEST_STRS[192] => 0xebed699589d99c05, + TEST_STRS[193] => 0x6d99f6df321ca5d5, + TEST_STRS[194] => 0x0cd410d08c36d625, + TEST_STRS[195] => 0xef1b2a2c86831d35, + TEST_STRS[196] => 0x3b349c4d69ee5f05, + TEST_STRS[197] => 0x55248ce88f45f035, + TEST_STRS[198] => 0xaa69ca6a18a4c885, + TEST_STRS[199] => 0x1fe3fce62bd816b5, + TEST_STRS[200] => 0x0289a488a8df69d9, + TEST_STRS[201] => 0x15e96e1613df98b5, + TEST_STRS[202] => 0xe6be57375ad89b99 +}