├── .gitignore ├── index.js ├── .travis.yml ├── appveyor.yml ├── package.json ├── index.d.ts ├── LICENSE.md ├── binding.gyp ├── src ├── metrohash.h ├── metrohash128crc.h ├── platform.h ├── testvector.h ├── metrohash128.h ├── metrohash64.h ├── metrohash_wrapper.cpp ├── metrohash128crc.cpp ├── metrohash64.cpp └── metrohash128.cpp ├── benchmark.js ├── README.md └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | MetroHash 2 | node_modules 3 | build 4 | .vscode -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('bindings')('metrohash.node'); 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "node" 5 | - "16" 6 | - "14" 7 | - "12" 8 | - "10" 9 | 10 | branches: 11 | only: 12 | - master 13 | 14 | before_install: 15 | - sudo add-apt-repository -y ppa:ubuntu-toolchain-r/test 16 | - sudo apt-get update -qq 17 | - sudo apt-get install -qq g++-9 18 | - sudo update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-9 50 19 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | clone_depth: 10 2 | 3 | environment: 4 | matrix: 5 | - nodejs_version: 10 6 | - nodejs_version: 12 7 | - nodejs_version: 14 8 | - nodejs_version: 16 9 | 10 | matrix: 11 | fast_finish: true 12 | 13 | platform: 14 | - x64 15 | 16 | branches: 17 | only: 18 | - master 19 | 20 | version: "{build}" 21 | build: off 22 | deploy: off 23 | 24 | install: 25 | - ps: Install-Product node $env:nodejs_version $env:platform 26 | - npm install 27 | 28 | test_script: 29 | - npm test 30 | 31 | cache: 32 | # global npm cache 33 | - '%APPDATA%\npm-cache' 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "metrohash", 3 | "version": "3.0.0", 4 | "description": "Node.js bindings for MetroHash hashing library", 5 | "scripts": { 6 | "build": "node-gyp rebuild", 7 | "rebuild": "node-gyp rebuild", 8 | "test": "mocha test.js" 9 | }, 10 | "main": "index.js", 11 | "types": "index.d.ts", 12 | "gypfile": true, 13 | "author": "Robert Klep ", 14 | "repository": "robertklep/node-metrohash", 15 | "license": "MIT", 16 | "dependencies": { 17 | "bindings": "^1.5.0", 18 | "nan": "^2.22.0", 19 | "node-gyp": "^10.3.1" 20 | }, 21 | "devDependencies": { 22 | "chai": "^3.5.0", 23 | "mocha": "^11.0.1" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | interface MetroHasher { 4 | // Update. 5 | update(input: string | Buffer): this; 6 | 7 | // Finalize and get hash digest. 8 | digest(): string; 9 | } 10 | 11 | export class MetroHash64 implements MetroHasher { 12 | // Constructor. 13 | constructor(seed?: number); 14 | 15 | // Update. 16 | update(input: string | Buffer): this; 17 | 18 | // Finalize and get hash digest. 19 | digest(): string; 20 | } 21 | 22 | export class MetroHash128 implements MetroHasher { 23 | // Constructor. 24 | constructor(seed?: number); 25 | 26 | // Update. 27 | update(input: string | Buffer): this; 28 | 29 | // Finalize and get hash digest. 30 | digest(): string; 31 | } 32 | 33 | export function metrohash64(input: string | Buffer, seed?: number): string; 34 | 35 | export function metrohash128(input: string | Buffer, seed?: number): string; 36 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Robert Klep 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [{ 3 | "target_name" : "metrohash", 4 | "sources" : [ "./src/metrohash_wrapper.cpp", "./src/metrohash128.cpp", "./src/metrohash128crc.cpp", "./src/metrohash64.cpp" ], 5 | "cflags" : [ "-std=c++11 -Wno-deprecated-declarations -Wno-unused-value -Wno-unused-function -Wno-unknown-pragmas -Wno-format -msse4.2" ], 6 | "include_dirs" : [ " { 20 | 21 | for (const test of TESTS) { 22 | 23 | suite(test.name, () => { 24 | 25 | benchmark('MetroHash64', () => { 26 | let hasher = new MetroHash64(); 27 | hasher.update(test.input); 28 | return hasher.digest(); 29 | }); 30 | 31 | benchmark('MetroHash128', () => { 32 | let hasher = new MetroHash128(); 33 | hasher.update(test.input); 34 | return hasher.digest(); 35 | }); 36 | 37 | benchmark('metrohash64', () => { 38 | return metrohash64(test.input); 39 | }); 40 | 41 | benchmark('metrohash128', () => { 42 | return metrohash128(test.input); 43 | }); 44 | 45 | }); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/metrohash128crc.h: -------------------------------------------------------------------------------- 1 | // metrohash128crc.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #ifndef METROHASH_METROHASH_128_CRC_H 27 | #define METROHASH_METROHASH_128_CRC_H 28 | 29 | #include 30 | 31 | // Legacy 128-bit hash functions 32 | void metrohash128crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 33 | void metrohash128crc_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 34 | 35 | 36 | #endif // #ifndef METROHASH_METROHASH_128_CRC_H 37 | -------------------------------------------------------------------------------- /src/platform.h: -------------------------------------------------------------------------------- 1 | // platform.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #ifndef METROHASH_PLATFORM_H 27 | #define METROHASH_PLATFORM_H 28 | 29 | #include 30 | 31 | // rotate right idiom recognized by most compilers 32 | inline static uint64_t rotate_right(uint64_t v, unsigned k) 33 | { 34 | return (v >> k) | (v << (64 - k)); 35 | } 36 | 37 | // unaligned reads, fast and safe on Nehalem and later microarchitectures 38 | inline static uint64_t read_u64(const void * const ptr) 39 | { 40 | return static_cast(*reinterpret_cast(ptr)); 41 | } 42 | 43 | inline static uint64_t read_u32(const void * const ptr) 44 | { 45 | return static_cast(*reinterpret_cast(ptr)); 46 | } 47 | 48 | inline static uint64_t read_u16(const void * const ptr) 49 | { 50 | return static_cast(*reinterpret_cast(ptr)); 51 | } 52 | 53 | inline static uint64_t read_u8 (const void * const ptr) 54 | { 55 | return static_cast(*reinterpret_cast(ptr)); 56 | } 57 | 58 | 59 | #endif // #ifndef METROHASH_PLATFORM_H 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # node-metrohash 2 | 3 | Wrapper around [MetroHash](https://github.com/jandrewrogers/MetroHash). 4 | 5 | [![Build Status](https://travis-ci.org/robertklep/node-metrohash.svg)](https://travis-ci.org/robertklep/node-metrohash) 6 | [![Build status](https://ci.appveyor.com/api/projects/status/49944yng9l70q41k/branch/master?svg=true)](https://ci.appveyor.com/project/robertklep/node-metrohash/branch/master) 7 | 8 | ### Installation 9 | 10 | ``` 11 | $ npm install metrohash 12 | ``` 13 | 14 | ### API change! 15 | 16 | Between v1 and v2, the API for this library has changed to allow for maximum hashing speed. 17 | 18 | The biggest change is that a calculated hash is now returned as a (hex-encoded) string instead of a `Buffer`. 19 | 20 | If a `Buffer` is still required, it's easy to convert the string: 21 | 22 | ``` 23 | let buffer = Buffer.from(metrohash64('input'), 'hex'); 24 | ``` 25 | 26 | Also, the `.hash()` methods for the hasher classes have been removed in favor of standalone functions (see below). 27 | 28 | ### Usage 29 | 30 | The module exports 2 classes, `MetroHash64` and `MetroHash128`, and two functions, `metrohash64` and `metrohash128`. 31 | 32 | The classes are meant for incremental hashing, the functions for standalone hash calculations. 33 | 34 | The class constructors and functions accept an optional `seed` numerical argument, which defaults to `0`. 35 | 36 | #### Class interface 37 | 38 | ``` javascript 39 | const MetroHash64 = require('metrohash').MetroHash64; 40 | 41 | // Constructor. 42 | MetroHash64(seed? : number) : this 43 | 44 | // Update. 45 | MetroHash64#update(input : String | Buffer) : this 46 | 47 | // Finalize and get hash digest. 48 | MetroHash64#digest() : String 49 | ``` 50 | 51 | (likewise for `MetroHash128`). 52 | 53 | #### Function interface 54 | 55 | ``` 56 | const metrohash64 = require('metrohash').metrohash64; 57 | 58 | metrohash64(input : String | Buffer, seed? : number) : String 59 | ``` 60 | 61 | (likewise for `metrohash128`). 62 | 63 | ### Examples 64 | 65 | ``` javascript 66 | //// Classes 67 | 68 | const MetroHash64 = require('metrohash').MetroHash64; 69 | 70 | // Instantiate using seed 123 (`new` is optional). 71 | let hash = new MetroHash64(123); 72 | 73 | // Update using a string as input. 74 | hash.update('Hello, World!'); 75 | 76 | // The same as above: 77 | // hash.update('Hello, ').update('World!'); 78 | 79 | // Finalize to get the digest as a hex string. 80 | let digest = hash.digest(); 81 | 82 | //// Functions 83 | const metrohash64 = require('metrohash').metrohash64; 84 | 85 | let digest = metrohash64('Hello, World!', 123); 86 | ``` 87 | 88 | ### Speed 89 | 90 | From v2.0 onwards, MetroHash is [pretty fast](https://medium.com/@drainingsun/in-search-of-a-good-node-js-hashing-algorithm-8052b6923a3b). 91 | -------------------------------------------------------------------------------- /src/testvector.h: -------------------------------------------------------------------------------- 1 | // testvector.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #ifndef METROHASH_TESTVECTOR_H 27 | #define METROHASH_TESTVECTOR_H 28 | 29 | #include "metrohash.h" 30 | 31 | 32 | typedef void (*HashFunction) (const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * hash); 33 | 34 | struct TestVectorData 35 | { 36 | HashFunction function; 37 | uint32_t bits; 38 | const char * key; 39 | uint32_t seed; 40 | uint8_t hash[64]; 41 | }; 42 | 43 | // The test vector string is selected such that it will properly exercise every 44 | // internal branch of the hash function. Currently that requires a string with 45 | // a length of (at least) 63 bytes. 46 | 47 | static const char * test_key_63 = "012345678901234567890123456789012345678901234567890123456789012"; 48 | 49 | // The hash assumes a little-endian architecture. Treating the hash results 50 | // as an array of uint64_t should enable conversion for big-endian implementations. 51 | const TestVectorData TestVector [] = 52 | { 53 | // seed = 0 54 | { metrohash64_1, 64, test_key_63, 0, "658F044F5C730E40" }, 55 | { metrohash64_2, 64, test_key_63, 0, "073CAAB960623211" }, 56 | { metrohash128_1, 128, test_key_63, 0, "ED9997ED9D0A8B0FF3F266399477788F" }, 57 | { metrohash128_2, 128, test_key_63, 0, "7BBA6FE119CF35D45507EDF3505359AB" }, 58 | { metrohash128crc_1, 128, test_key_63, 0, "B329ED67831604D3DFAC4E4876D8262F" }, 59 | { metrohash128crc_2, 128, test_key_63, 0, "0502A67E257BBD77206BBCA6BBEF2653" }, 60 | 61 | // seed = 1 62 | { metrohash64_1, 64, test_key_63, 1, "AE49EBB0A856537B" }, 63 | { metrohash64_2, 64, test_key_63, 1, "CF518E9CF58402C0" }, 64 | { metrohash128_1, 128, test_key_63, 1, "DDA6BA67F7DE755EFDF6BEABECCFD1F4" }, 65 | { metrohash128_2, 128, test_key_63, 1, "2DA6AF149A5CDBC12B09DB0846D69EF0" }, 66 | { metrohash128crc_1, 128, test_key_63, 1, "E8FAB51AF19F18A7B10D0A57D4276DF2" }, 67 | { metrohash128crc_2, 128, test_key_63, 1, "2D54F87181A0CF64B02C50D95692BC19" }, 68 | }; 69 | 70 | 71 | 72 | #endif // #ifndef METROHASH_TESTVECTOR_H 73 | -------------------------------------------------------------------------------- /src/metrohash128.h: -------------------------------------------------------------------------------- 1 | // metrohash128.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #ifndef METROHASH_METROHASH_128_H 27 | #define METROHASH_METROHASH_128_H 28 | 29 | #include 30 | 31 | class MetroHash128 32 | { 33 | public: 34 | static const uint32_t bits = 128; 35 | 36 | // Constructor initializes the same as Initialize() 37 | MetroHash128(const uint64_t seed=0); 38 | 39 | // Initializes internal state for new hash with optional seed 40 | void Initialize(const uint64_t seed=0); 41 | 42 | // Update the hash state with a string of bytes. If the length 43 | // is sufficiently long, the implementation switches to a bulk 44 | // hashing algorithm directly on the argument buffer for speed. 45 | void Update(const uint8_t * buffer, const uint64_t length); 46 | 47 | // Constructs the final hash and writes it to the argument buffer. 48 | // After a hash is finalized, this instance must be Initialized()-ed 49 | // again or the behavior of Update() and Finalize() is undefined. 50 | void Finalize(uint8_t * const hash); 51 | 52 | // A non-incremental function implementation. This can be significantly 53 | // faster than the incremental implementation for some usage patterns. 54 | static void Hash(const uint8_t * buffer, const uint64_t length, uint8_t * const hash, const uint64_t seed=0); 55 | 56 | // Does implementation correctly execute test vectors? 57 | static bool ImplementationVerified(); 58 | 59 | // test vectors -- Hash(test_string, seed=0) => test_seed_0 60 | static const char * test_string; 61 | static const uint8_t test_seed_0[16]; 62 | static const uint8_t test_seed_1[16]; 63 | 64 | private: 65 | static const uint64_t k0 = 0xC83A91E1; 66 | static const uint64_t k1 = 0x8648DBDB; 67 | static const uint64_t k2 = 0x7BDEC03B; 68 | static const uint64_t k3 = 0x2F5870A5; 69 | 70 | struct { uint64_t v[4]; } state; 71 | struct { uint8_t b[32]; } input; 72 | uint64_t bytes; 73 | }; 74 | 75 | 76 | // Legacy 128-bit hash functions -- do not use 77 | void metrohash128_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 78 | void metrohash128_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 79 | 80 | 81 | #endif // #ifndef METROHASH_METROHASH_128_H 82 | -------------------------------------------------------------------------------- /src/metrohash64.h: -------------------------------------------------------------------------------- 1 | // metrohash64.h 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #ifndef METROHASH_METROHASH_64_H 27 | #define METROHASH_METROHASH_64_H 28 | 29 | #include 30 | 31 | class MetroHash64 32 | { 33 | public: 34 | static const uint32_t bits = 64; 35 | 36 | // Constructor initializes the same as Initialize() 37 | MetroHash64(const uint64_t seed=0); 38 | 39 | // Initializes internal state for new hash with optional seed 40 | void Initialize(const uint64_t seed=0); 41 | 42 | // Update the hash state with a string of bytes. If the length 43 | // is sufficiently long, the implementation switches to a bulk 44 | // hashing algorithm directly on the argument buffer for speed. 45 | void Update(const uint8_t * buffer, const uint64_t length); 46 | 47 | // Constructs the final hash and writes it to the argument buffer. 48 | // After a hash is finalized, this instance must be Initialized()-ed 49 | // again or the behavior of Update() and Finalize() is undefined. 50 | void Finalize(uint8_t * const hash); 51 | 52 | // A non-incremental function implementation. This can be significantly 53 | // faster than the incremental implementation for some usage patterns. 54 | static void Hash(const uint8_t * buffer, const uint64_t length, uint8_t * const hash, const uint64_t seed=0); 55 | 56 | // Does implementation correctly execute test vectors? 57 | static bool ImplementationVerified(); 58 | 59 | // test vectors -- Hash(test_string, seed=0) => test_seed_0 60 | static const char * test_string; 61 | static const uint8_t test_seed_0[8]; 62 | static const uint8_t test_seed_1[8]; 63 | 64 | private: 65 | static const uint64_t k0 = 0xD6D018F5; 66 | static const uint64_t k1 = 0xA2AA033B; 67 | static const uint64_t k2 = 0x62992FC1; 68 | static const uint64_t k3 = 0x30BC5B29; 69 | 70 | struct { uint64_t v[4]; } state; 71 | struct { uint8_t b[32]; } input; 72 | uint64_t bytes; 73 | uint64_t vseed; 74 | }; 75 | 76 | 77 | // Legacy 64-bit hash functions -- do not use 78 | void metrohash64_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 79 | void metrohash64_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out); 80 | 81 | 82 | #endif // #ifndef METROHASH_METROHASH_64_H 83 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | var MetroHash64 = require('./index').MetroHash64; 3 | var MetroHash128 = require('./index').MetroHash128; 4 | var metrohash64 = require('./index').metrohash64; 5 | var metrohash128 = require('./index').metrohash128; 6 | var expect = require('chai').expect; 7 | 8 | const TESTVECTOR_S = '012345678901234567890123456789012345678901234567890123456789012'; 9 | const TESTVECTOR_B = Buffer.from(TESTVECTOR_S); 10 | 11 | const CLS_TESTS = [ 12 | { cls : MetroHash64, expected : '6b753dae06704bad' }, 13 | { cls : MetroHash64, seed : 0, expected : '6b753dae06704bad' }, 14 | { cls : MetroHash64, seed : 1, expected : '3b0d481cf4b9b8df' }, 15 | { cls : MetroHash128, expected : 'c77ce2bfa4ed9f9b0548b2ac5074a297' }, 16 | { cls : MetroHash128, seed : 0, expected : 'c77ce2bfa4ed9f9b0548b2ac5074a297' }, 17 | { cls : MetroHash128, seed : 1, expected : '45a3cdb838199d7fbdd68d867a14ecef' }, 18 | ]; 19 | 20 | const FUN_TESTS = [ 21 | { fn : metrohash64, expected : '6b753dae06704bad' }, 22 | { fn : metrohash64, seed : 0, expected : '6b753dae06704bad' }, 23 | { fn : metrohash64, seed : 1, expected : '3b0d481cf4b9b8df' }, 24 | { fn : metrohash128, expected : 'c77ce2bfa4ed9f9b0548b2ac5074a297' }, 25 | { fn : metrohash128, seed : 0, expected : 'c77ce2bfa4ed9f9b0548b2ac5074a297' }, 26 | { fn : metrohash128, seed : 1, expected : '45a3cdb838199d7fbdd68d867a14ecef' }, 27 | ]; 28 | 29 | describe('Instantiation and method calling', function() { 30 | 31 | it('new MetroHash64() should work', function() { 32 | expect(function() { new MetroHash64(); }).to.not.throw(); 33 | }); 34 | 35 | it('MetroHash64() should work', function() { 36 | expect(function() { MetroHash64(); }).to.not.throw(); 37 | }); 38 | 39 | it('new MetroHash128() should work', function() { 40 | expect(function() { new MetroHash128(); }).to.not.throw(); 41 | }); 42 | 43 | it('MetroHash128() should work', function() { 44 | expect(function() { MetroHash128(); }).to.not.throw(); 45 | }); 46 | 47 | it('MetroHash64() should allow chaining', function() { 48 | expect(function() { 49 | new MetroHash64().update(TESTVECTOR_B).digest(); 50 | }).to.not.throw(); 51 | }); 52 | 53 | it('MetroHash128() should allow chaining', function() { 54 | expect(function() { 55 | new MetroHash128().update(TESTVECTOR_B).digest(); 56 | }).to.not.throw(); 57 | }); 58 | 59 | }); 60 | 61 | describe('Test vectors', function() { 62 | 63 | CLS_TESTS.forEach(function(test) { 64 | var suffix = test.seed !== undefined ? ', seed = ' + test.seed + ')' : ')'; 65 | 66 | it('new ' + test.cls.name + '(String' + suffix, function() { 67 | var instance = new test.cls(test.seed); 68 | instance.update(TESTVECTOR_S); 69 | expect(instance.digest()).to.equal(test.expected); 70 | }); 71 | 72 | it(test.cls.name + '(String' + suffix, function() { 73 | var instance = test.cls(test.seed); 74 | instance.update(TESTVECTOR_S); 75 | expect(instance.digest()).to.equal(test.expected); 76 | }); 77 | 78 | it('new ' + test.cls.name + '(Buffer' + suffix, function() { 79 | var instance = new test.cls(test.seed); 80 | instance.update(TESTVECTOR_B); 81 | expect(instance.digest()).to.equal(test.expected); 82 | }); 83 | 84 | it(test.cls.name + '(Buffer' + suffix, function() { 85 | var instance = test.cls(test.seed); 86 | instance.update(TESTVECTOR_B); 87 | expect(instance.digest()).to.equal(test.expected); 88 | }); 89 | 90 | it(test.cls.name + '(String' + suffix + ' incremental', function() { 91 | var instance = test.cls(test.seed); 92 | TESTVECTOR_S.split('').forEach(function(c) { 93 | instance.update(c); 94 | }); 95 | expect(instance.digest()).to.equal(test.expected); 96 | }); 97 | 98 | it(test.cls.name + '(Buffer' + suffix + ' incremental', function() { 99 | var instance = test.cls(test.seed); 100 | for (var i = 0; i < TESTVECTOR_B.length; i++) { 101 | instance.update(TESTVECTOR_B.slice(i, i + 1)); 102 | } 103 | expect(instance.digest()).to.equal(test.expected); 104 | }); 105 | }); 106 | 107 | FUN_TESTS.forEach(function(test) { 108 | let hasSeed = test.seed !== undefined; 109 | let suffix = hasSeed ? ', seed = ' + test.seed + ')' : ')'; 110 | 111 | it('Standalone: ' + test.fn.name + '(String' + suffix, function() { 112 | let result = hasSeed ? test.fn(TESTVECTOR_S, test.seed) : test.fn(TESTVECTOR_S); 113 | expect(result).to.equal(test.expected); 114 | }); 115 | 116 | it('Standalone: ' + test.fn.name + '(Buffer' + suffix, function() { 117 | let result = hasSeed ? test.fn(TESTVECTOR_B, test.seed) : test.fn(TESTVECTOR_B); 118 | expect(result).to.equal(test.expected); 119 | }); 120 | 121 | it('Standalone: ' + test.fn.name + ', handle invalid data', function() { 122 | expect(function() { test.fn(123) }).to.fail; 123 | }); 124 | 125 | it('Standalone: ' + test.fn.name + ', handle invalid seed', function() { 126 | expect(function() { test.fn(TESTVECTOR_S, 'foo') }).to.fail; 127 | }); 128 | 129 | it('Standalone: ' + test.fn.name + ', handle invalid data and seed', function() { 130 | expect(function() { test.fn(123, 'foo') }).to.fail; 131 | }); 132 | 133 | }); 134 | 135 | }); 136 | -------------------------------------------------------------------------------- /src/metrohash_wrapper.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "metrohash.h" 4 | 5 | using namespace v8; 6 | using namespace node; 7 | 8 | template 9 | class NodeMetroHash : public Nan::ObjectWrap { 10 | static Nan::Persistent constructor; 11 | HashClass metro; 12 | uint64_t seed; 13 | uint8_t digest[DIGEST_SIZE]; 14 | bool wasFinalized = false; 15 | 16 | explicit NodeMetroHash(uint64_t seed = 0) : seed(seed) { 17 | metro.Initialize(seed); 18 | } 19 | 20 | static NAN_METHOD(New) { 21 | Nan::HandleScope scope; 22 | if (info.IsConstructCall()) { 23 | // TODO: check if argument type makes sense 24 | NodeMetroHash* hash = new NodeMetroHash(info[0]->IsUndefined() ? 0 : Nan::To(info[0]).FromJust()); 25 | hash->Wrap(info.This()); 26 | info.GetReturnValue().Set(info.This()); 27 | } else { 28 | const int argc = 1; 29 | Local argv[argc] = { info[0] }; 30 | Local cons = Nan::New(constructor); 31 | info.GetReturnValue().Set(Nan::NewInstance(cons, argc, argv).ToLocalChecked()); 32 | } 33 | } 34 | 35 | static NAN_METHOD(Update) { 36 | Nan::HandleScope scope; 37 | NodeMetroHash* self = Nan::ObjectWrap::Unwrap(info.This()); 38 | 39 | // Argument validation. 40 | if (info.Length() != 1) { 41 | Nan::ThrowTypeError("Missing argument"); 42 | return; 43 | } 44 | 45 | // Determine data and size of content to be hashed. 46 | char* data; 47 | size_t size; 48 | std::string str; 49 | if (Buffer::HasInstance(info[0])) { 50 | data = Buffer::Data(info[0]); 51 | size = Buffer::Length(info[0]); 52 | } else if (info[0]->IsString()) { 53 | str = *Nan::Utf8String(info[0]); 54 | data = (char *) str.c_str(); 55 | size = str.length(); 56 | } else { 57 | Nan::ThrowTypeError("`data` argument must be String or Buffer"); 58 | return; 59 | } 60 | 61 | // Update hash. 62 | self->metro.Update((const uint8_t *) data, size); 63 | 64 | // Allow for chaining. 65 | info.GetReturnValue().Set(info.This()); 66 | } 67 | 68 | static NAN_METHOD(Digest) { 69 | Nan::HandleScope scope; 70 | NodeMetroHash* self = Nan::ObjectWrap::Unwrap(info.This()); 71 | 72 | // Finalize and get the hash value. Make sure we only finalize once. 73 | if (! self->wasFinalized) { 74 | self->metro.Finalize(self->digest); 75 | self->wasFinalized = true; 76 | } 77 | 78 | // Return as hex string. 79 | info.GetReturnValue().Set(Nan::Encode((char *) self->digest, DIGEST_SIZE, Nan::HEX)); 80 | } 81 | 82 | public: 83 | static void Init(const char *js_class_name, Local target) { 84 | Nan::HandleScope scope; 85 | Local tpl = Nan::New(New); 86 | 87 | // Prepare constructor template 88 | tpl->SetClassName(Nan::New(js_class_name).ToLocalChecked()); 89 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 90 | 91 | // Prototype 92 | Nan::SetPrototypeMethod(tpl, "update", Update); 93 | Nan::SetPrototypeMethod(tpl, "digest", Digest); 94 | 95 | constructor.Reset(Nan::GetFunction(tpl).ToLocalChecked()); 96 | Nan::Set(target, Nan::New(js_class_name).ToLocalChecked(), Nan::GetFunction(tpl).ToLocalChecked()); 97 | } 98 | }; 99 | 100 | template 101 | Nan::Persistent NodeMetroHash::constructor; 102 | 103 | typedef NodeMetroHash NodeMetroHash64; 104 | typedef NodeMetroHash NodeMetroHash128; 105 | 106 | // Standalone hashers 107 | template 108 | NAN_METHOD(NodeMetroHashFn) { 109 | if (info.Length() < 1) { 110 | Nan::ThrowTypeError("Missing argument(s)"); 111 | return; 112 | } 113 | char *data; 114 | size_t size; 115 | std::string str; 116 | if (Buffer::HasInstance(info[0])) { 117 | data = Buffer::Data(info[0]); 118 | size = Buffer::Length(info[0]); 119 | } else if (info[0]->IsString()) { 120 | str = *Nan::Utf8String(info[0]); 121 | data = (char *) str.c_str(); 122 | size = str.length(); 123 | } else { 124 | Nan::ThrowTypeError("data must be String or Buffer"); 125 | return; 126 | } 127 | uint64_t seed = 0; 128 | if (info.Length() > 1) { 129 | if (! info[1]->IsNumber()) { 130 | Nan::ThrowTypeError("seed must be numerical"); 131 | return; 132 | } 133 | seed = Nan::To(info[1]).FromJust(); 134 | } 135 | uint8_t digest[ BITS >> 3 ]; 136 | HashClass::Hash((const unsigned char *) data, size, digest, seed); 137 | info.GetReturnValue().Set(Nan::Encode((char *) digest, BITS >> 3, Nan::HEX)); 138 | }; 139 | 140 | const auto NodeMetroHashFn64 = &NodeMetroHashFn; 141 | const auto NodeMetroHashFn128 = &NodeMetroHashFn; 142 | 143 | // Addon initialization. 144 | NAN_MODULE_INIT(InitAll) { 145 | // Export classes. 146 | NodeMetroHash64::Init("MetroHash64", target); 147 | NodeMetroHash128::Init("MetroHash128", target); 148 | 149 | // Export standalone hashing functions 150 | Local fn64 = Nan::GetFunction(Nan::New(NodeMetroHashFn64)).ToLocalChecked(); 151 | Local name64 = Nan::New("metrohash64").ToLocalChecked(); 152 | fn64->SetName(name64); 153 | Nan::Set(target, name64, fn64); 154 | 155 | Local fn128 = Nan::GetFunction(Nan::New(NodeMetroHashFn128)).ToLocalChecked(); 156 | Local name128 = Nan::New("metrohash128").ToLocalChecked(); 157 | fn128->SetName(name128); 158 | Nan::Set(target, name128, fn128); 159 | } 160 | 161 | NODE_MODULE(metrohash, InitAll) 162 | -------------------------------------------------------------------------------- /src/metrohash128crc.cpp: -------------------------------------------------------------------------------- 1 | // metrohash128crc.cpp 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | 27 | #if defined(__x86_64__) 28 | #include // For SSE4.2 29 | #define METROHASH_CRC32_U64(val, data) _mm_crc32_u64(val, data) 30 | #elif defined(__aarch64__) 31 | #include // For ARM CRC32 32 | #define METROHASH_CRC32_U64(val, data) __crc32d(val, data) 33 | #else 34 | #error "Architecture not supported" 35 | #endif 36 | #include 37 | #include "metrohash.h" 38 | #include "platform.h" 39 | 40 | 41 | void metrohash128crc_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 42 | { 43 | static const uint64_t k0 = 0xC83A91E1; 44 | static const uint64_t k1 = 0x8648DBDB; 45 | static const uint64_t k2 = 0x7BDEC03B; 46 | static const uint64_t k3 = 0x2F5870A5; 47 | 48 | const uint8_t * ptr = reinterpret_cast(key); 49 | const uint8_t * const end = ptr + len; 50 | 51 | uint64_t v[4]; 52 | 53 | v[0] = ((static_cast(seed) - k0) * k3) + len; 54 | v[1] = ((static_cast(seed) + k1) * k2) + len; 55 | 56 | if (len >= 32) 57 | { 58 | v[2] = ((static_cast(seed) + k0) * k2) + len; 59 | v[3] = ((static_cast(seed) - k1) * k3) + len; 60 | 61 | do 62 | { 63 | v[0] ^= METROHASH_CRC32_U64(v[0], read_u64(ptr)); ptr += 8; 64 | v[1] ^= METROHASH_CRC32_U64(v[1], read_u64(ptr)); ptr += 8; 65 | v[2] ^= METROHASH_CRC32_U64(v[2], read_u64(ptr)); ptr += 8; 66 | v[3] ^= METROHASH_CRC32_U64(v[3], read_u64(ptr)); ptr += 8; 67 | } 68 | while (ptr <= (end - 32)); 69 | 70 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 34) * k1; 71 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 37) * k0; 72 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 34) * k1; 73 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 37) * k0; 74 | } 75 | 76 | if ((end - ptr) >= 16) 77 | { 78 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3; 79 | v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],34) * k3; 80 | v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1; 81 | v[1] ^= rotate_right((v[1] * k3) + v[0], 30) * k0; 82 | } 83 | 84 | if ((end - ptr) >= 8) 85 | { 86 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],36) * k3; 87 | v[0] ^= rotate_right((v[0] * k2) + v[1], 23) * k1; 88 | } 89 | 90 | if ((end - ptr) >= 4) 91 | { 92 | v[1] ^= METROHASH_CRC32_U64(v[0], read_u32(ptr)); ptr += 4; 93 | v[1] ^= rotate_right((v[1] * k3) + v[0], 19) * k0; 94 | } 95 | 96 | if ((end - ptr) >= 2) 97 | { 98 | v[0] ^= METROHASH_CRC32_U64(v[1], read_u16(ptr)); ptr += 2; 99 | v[0] ^= rotate_right((v[0] * k2) + v[1], 13) * k1; 100 | } 101 | 102 | if ((end - ptr) >= 1) 103 | { 104 | v[1] ^= METROHASH_CRC32_U64(v[0], read_u8 (ptr)); 105 | v[1] ^= rotate_right((v[1] * k3) + v[0], 17) * k0; 106 | } 107 | 108 | v[0] += rotate_right((v[0] * k0) + v[1], 11); 109 | v[1] += rotate_right((v[1] * k1) + v[0], 26); 110 | v[0] += rotate_right((v[0] * k0) + v[1], 11); 111 | v[1] += rotate_right((v[1] * k1) + v[0], 26); 112 | 113 | memcpy(out, v, 16); 114 | } 115 | 116 | 117 | void metrohash128crc_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 118 | { 119 | static const uint64_t k0 = 0xEE783E2F; 120 | static const uint64_t k1 = 0xAD07C493; 121 | static const uint64_t k2 = 0x797A90BB; 122 | static const uint64_t k3 = 0x2E4B2E1B; 123 | 124 | const uint8_t * ptr = reinterpret_cast(key); 125 | const uint8_t * const end = ptr + len; 126 | 127 | uint64_t v[4]; 128 | 129 | v[0] = ((static_cast(seed) - k0) * k3) + len; 130 | v[1] = ((static_cast(seed) + k1) * k2) + len; 131 | 132 | if (len >= 32) 133 | { 134 | v[2] = ((static_cast(seed) + k0) * k2) + len; 135 | v[3] = ((static_cast(seed) - k1) * k3) + len; 136 | 137 | do 138 | { 139 | v[0] ^= METROHASH_CRC32_U64(v[0], read_u64(ptr)); ptr += 8; 140 | v[1] ^= METROHASH_CRC32_U64(v[1], read_u64(ptr)); ptr += 8; 141 | v[2] ^= METROHASH_CRC32_U64(v[2], read_u64(ptr)); ptr += 8; 142 | v[3] ^= METROHASH_CRC32_U64(v[3], read_u64(ptr)); ptr += 8; 143 | } 144 | while (ptr <= (end - 32)); 145 | 146 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 12) * k1; 147 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 19) * k0; 148 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 12) * k1; 149 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 19) * k0; 150 | } 151 | 152 | if ((end - ptr) >= 16) 153 | { 154 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],41) * k3; 155 | v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],41) * k3; 156 | v[0] ^= rotate_right((v[0] * k2) + v[1], 10) * k1; 157 | v[1] ^= rotate_right((v[1] * k3) + v[0], 10) * k0; 158 | } 159 | 160 | if ((end - ptr) >= 8) 161 | { 162 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],34) * k3; 163 | v[0] ^= rotate_right((v[0] * k2) + v[1], 22) * k1; 164 | } 165 | 166 | if ((end - ptr) >= 4) 167 | { 168 | v[1] ^= METROHASH_CRC32_U64(v[0], read_u32(ptr)); ptr += 4; 169 | v[1] ^= rotate_right((v[1] * k3) + v[0], 14) * k0; 170 | } 171 | 172 | if ((end - ptr) >= 2) 173 | { 174 | v[0] ^= METROHASH_CRC32_U64(v[1], read_u16(ptr)); ptr += 2; 175 | v[0] ^= rotate_right((v[0] * k2) + v[1], 15) * k1; 176 | } 177 | 178 | if ((end - ptr) >= 1) 179 | { 180 | v[1] ^= METROHASH_CRC32_U64(v[0], read_u8 (ptr)); 181 | v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0; 182 | } 183 | 184 | v[0] += rotate_right((v[0] * k0) + v[1], 15); 185 | v[1] += rotate_right((v[1] * k1) + v[0], 27); 186 | v[0] += rotate_right((v[0] * k0) + v[1], 15); 187 | v[1] += rotate_right((v[1] * k1) + v[0], 27); 188 | 189 | memcpy(out, v, 16); 190 | } 191 | -------------------------------------------------------------------------------- /src/metrohash64.cpp: -------------------------------------------------------------------------------- 1 | // metrohash64.cpp 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #include 27 | #include "platform.h" 28 | #include "metrohash64.h" 29 | 30 | const char * MetroHash64::test_string = "012345678901234567890123456789012345678901234567890123456789012"; 31 | 32 | const uint8_t MetroHash64::test_seed_0[8] = { 0x6B, 0x75, 0x3D, 0xAE, 0x06, 0x70, 0x4B, 0xAD }; 33 | const uint8_t MetroHash64::test_seed_1[8] = { 0x3B, 0x0D, 0x48, 0x1C, 0xF4, 0xB9, 0xB8, 0xDF }; 34 | 35 | 36 | 37 | MetroHash64::MetroHash64(const uint64_t seed) 38 | { 39 | Initialize(seed); 40 | } 41 | 42 | 43 | void MetroHash64::Initialize(const uint64_t seed) 44 | { 45 | vseed = (static_cast(seed) + k2) * k0; 46 | 47 | // initialize internal hash registers 48 | state.v[0] = vseed; 49 | state.v[1] = vseed; 50 | state.v[2] = vseed; 51 | state.v[3] = vseed; 52 | 53 | // initialize total length of input 54 | bytes = 0; 55 | } 56 | 57 | 58 | void MetroHash64::Update(const uint8_t * const buffer, const uint64_t length) 59 | { 60 | const uint8_t * ptr = reinterpret_cast(buffer); 61 | const uint8_t * const end = ptr + length; 62 | 63 | // input buffer may be partially filled 64 | if (bytes % 32) 65 | { 66 | uint64_t fill = 32 - (bytes % 32); 67 | if (fill > length) 68 | fill = length; 69 | 70 | memcpy(input.b + (bytes % 32), ptr, fill); 71 | ptr += fill; 72 | bytes += fill; 73 | 74 | // input buffer is still partially filled 75 | if ((bytes % 32) != 0) return; 76 | 77 | // process full input buffer 78 | state.v[0] += read_u64(&input.b[ 0]) * k0; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; 79 | state.v[1] += read_u64(&input.b[ 8]) * k1; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; 80 | state.v[2] += read_u64(&input.b[16]) * k2; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; 81 | state.v[3] += read_u64(&input.b[24]) * k3; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; 82 | } 83 | 84 | // bulk update 85 | bytes += (end - ptr); 86 | while (ptr <= (end - 32)) 87 | { 88 | // process directly from the source, bypassing the input buffer 89 | state.v[0] += read_u64(ptr) * k0; ptr += 8; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; 90 | state.v[1] += read_u64(ptr) * k1; ptr += 8; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; 91 | state.v[2] += read_u64(ptr) * k2; ptr += 8; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; 92 | state.v[3] += read_u64(ptr) * k3; ptr += 8; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; 93 | } 94 | 95 | // store remaining bytes in input buffer 96 | if (ptr < end) 97 | memcpy(input.b, ptr, end - ptr); 98 | } 99 | 100 | 101 | void MetroHash64::Finalize(uint8_t * const hash) 102 | { 103 | // finalize bulk loop, if used 104 | if (bytes >= 32) 105 | { 106 | state.v[2] ^= rotate_right(((state.v[0] + state.v[3]) * k0) + state.v[1], 37) * k1; 107 | state.v[3] ^= rotate_right(((state.v[1] + state.v[2]) * k1) + state.v[0], 37) * k0; 108 | state.v[0] ^= rotate_right(((state.v[0] + state.v[2]) * k0) + state.v[3], 37) * k1; 109 | state.v[1] ^= rotate_right(((state.v[1] + state.v[3]) * k1) + state.v[2], 37) * k0; 110 | 111 | state.v[0] = vseed + (state.v[0] ^ state.v[1]); 112 | } 113 | 114 | // process any bytes remaining in the input buffer 115 | const uint8_t * ptr = reinterpret_cast(input.b); 116 | const uint8_t * const end = ptr + (bytes % 32); 117 | 118 | if ((end - ptr) >= 16) 119 | { 120 | state.v[1] = state.v[0] + (read_u64(ptr) * k2); ptr += 8; state.v[1] = rotate_right(state.v[1],29) * k3; 121 | state.v[2] = state.v[0] + (read_u64(ptr) * k2); ptr += 8; state.v[2] = rotate_right(state.v[2],29) * k3; 122 | state.v[1] ^= rotate_right(state.v[1] * k0, 21) + state.v[2]; 123 | state.v[2] ^= rotate_right(state.v[2] * k3, 21) + state.v[1]; 124 | state.v[0] += state.v[2]; 125 | } 126 | 127 | if ((end - ptr) >= 8) 128 | { 129 | state.v[0] += read_u64(ptr) * k3; ptr += 8; 130 | state.v[0] ^= rotate_right(state.v[0], 55) * k1; 131 | } 132 | 133 | if ((end - ptr) >= 4) 134 | { 135 | state.v[0] += read_u32(ptr) * k3; ptr += 4; 136 | state.v[0] ^= rotate_right(state.v[0], 26) * k1; 137 | } 138 | 139 | if ((end - ptr) >= 2) 140 | { 141 | state.v[0] += read_u16(ptr) * k3; ptr += 2; 142 | state.v[0] ^= rotate_right(state.v[0], 48) * k1; 143 | } 144 | 145 | if ((end - ptr) >= 1) 146 | { 147 | state.v[0] += read_u8 (ptr) * k3; 148 | state.v[0] ^= rotate_right(state.v[0], 37) * k1; 149 | } 150 | 151 | state.v[0] ^= rotate_right(state.v[0], 28); 152 | state.v[0] *= k0; 153 | state.v[0] ^= rotate_right(state.v[0], 29); 154 | 155 | bytes = 0; 156 | 157 | // do any endian conversion here 158 | 159 | memcpy(hash, state.v, 8); 160 | } 161 | 162 | 163 | void MetroHash64::Hash(const uint8_t * buffer, const uint64_t length, uint8_t * const hash, const uint64_t seed) 164 | { 165 | const uint8_t * ptr = reinterpret_cast(buffer); 166 | const uint8_t * const end = ptr + length; 167 | 168 | uint64_t h = (static_cast(seed) + k2) * k0; 169 | 170 | if (length >= 32) 171 | { 172 | uint64_t v[4]; 173 | v[0] = h; 174 | v[1] = h; 175 | v[2] = h; 176 | v[3] = h; 177 | 178 | do 179 | { 180 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 181 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 182 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 183 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 184 | } 185 | while (ptr <= (end - 32)); 186 | 187 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 37) * k1; 188 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 37) * k0; 189 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 37) * k1; 190 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 37) * k0; 191 | h += v[0] ^ v[1]; 192 | } 193 | 194 | if ((end - ptr) >= 16) 195 | { 196 | uint64_t v0 = h + (read_u64(ptr) * k2); ptr += 8; v0 = rotate_right(v0,29) * k3; 197 | uint64_t v1 = h + (read_u64(ptr) * k2); ptr += 8; v1 = rotate_right(v1,29) * k3; 198 | v0 ^= rotate_right(v0 * k0, 21) + v1; 199 | v1 ^= rotate_right(v1 * k3, 21) + v0; 200 | h += v1; 201 | } 202 | 203 | if ((end - ptr) >= 8) 204 | { 205 | h += read_u64(ptr) * k3; ptr += 8; 206 | h ^= rotate_right(h, 55) * k1; 207 | } 208 | 209 | if ((end - ptr) >= 4) 210 | { 211 | h += read_u32(ptr) * k3; ptr += 4; 212 | h ^= rotate_right(h, 26) * k1; 213 | } 214 | 215 | if ((end - ptr) >= 2) 216 | { 217 | h += read_u16(ptr) * k3; ptr += 2; 218 | h ^= rotate_right(h, 48) * k1; 219 | } 220 | 221 | if ((end - ptr) >= 1) 222 | { 223 | h += read_u8 (ptr) * k3; 224 | h ^= rotate_right(h, 37) * k1; 225 | } 226 | 227 | h ^= rotate_right(h, 28); 228 | h *= k0; 229 | h ^= rotate_right(h, 29); 230 | 231 | memcpy(hash, &h, 8); 232 | } 233 | 234 | 235 | bool MetroHash64::ImplementationVerified() 236 | { 237 | uint8_t hash[8]; 238 | const uint8_t * key = reinterpret_cast(MetroHash64::test_string); 239 | 240 | // verify one-shot implementation 241 | MetroHash64::Hash(key, strlen(MetroHash64::test_string), hash, 0); 242 | if (memcmp(hash, MetroHash64::test_seed_0, 8) != 0) return false; 243 | 244 | MetroHash64::Hash(key, strlen(MetroHash64::test_string), hash, 1); 245 | if (memcmp(hash, MetroHash64::test_seed_1, 8) != 0) return false; 246 | 247 | // verify incremental implementation 248 | MetroHash64 metro; 249 | 250 | metro.Initialize(0); 251 | metro.Update(reinterpret_cast(MetroHash64::test_string), strlen(MetroHash64::test_string)); 252 | metro.Finalize(hash); 253 | if (memcmp(hash, MetroHash64::test_seed_0, 8) != 0) return false; 254 | 255 | metro.Initialize(1); 256 | metro.Update(reinterpret_cast(MetroHash64::test_string), strlen(MetroHash64::test_string)); 257 | metro.Finalize(hash); 258 | if (memcmp(hash, MetroHash64::test_seed_1, 8) != 0) return false; 259 | 260 | return true; 261 | } 262 | 263 | 264 | void metrohash64_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 265 | { 266 | static const uint64_t k0 = 0xC83A91E1; 267 | static const uint64_t k1 = 0x8648DBDB; 268 | static const uint64_t k2 = 0x7BDEC03B; 269 | static const uint64_t k3 = 0x2F5870A5; 270 | 271 | const uint8_t * ptr = reinterpret_cast(key); 272 | const uint8_t * const end = ptr + len; 273 | 274 | uint64_t hash = ((static_cast(seed) + k2) * k0) + len; 275 | 276 | if (len >= 32) 277 | { 278 | uint64_t v[4]; 279 | v[0] = hash; 280 | v[1] = hash; 281 | v[2] = hash; 282 | v[3] = hash; 283 | 284 | do 285 | { 286 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 287 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 288 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 289 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 290 | } 291 | while (ptr <= (end - 32)); 292 | 293 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1; 294 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0; 295 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1; 296 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0; 297 | hash += v[0] ^ v[1]; 298 | } 299 | 300 | if ((end - ptr) >= 16) 301 | { 302 | uint64_t v0 = hash + (read_u64(ptr) * k0); ptr += 8; v0 = rotate_right(v0,33) * k1; 303 | uint64_t v1 = hash + (read_u64(ptr) * k1); ptr += 8; v1 = rotate_right(v1,33) * k2; 304 | v0 ^= rotate_right(v0 * k0, 35) + v1; 305 | v1 ^= rotate_right(v1 * k3, 35) + v0; 306 | hash += v1; 307 | } 308 | 309 | if ((end - ptr) >= 8) 310 | { 311 | hash += read_u64(ptr) * k3; ptr += 8; 312 | hash ^= rotate_right(hash, 33) * k1; 313 | 314 | } 315 | 316 | if ((end - ptr) >= 4) 317 | { 318 | hash += read_u32(ptr) * k3; ptr += 4; 319 | hash ^= rotate_right(hash, 15) * k1; 320 | } 321 | 322 | if ((end - ptr) >= 2) 323 | { 324 | hash += read_u16(ptr) * k3; ptr += 2; 325 | hash ^= rotate_right(hash, 13) * k1; 326 | } 327 | 328 | if ((end - ptr) >= 1) 329 | { 330 | hash += read_u8 (ptr) * k3; 331 | hash ^= rotate_right(hash, 25) * k1; 332 | } 333 | 334 | hash ^= rotate_right(hash, 33); 335 | hash *= k0; 336 | hash ^= rotate_right(hash, 33); 337 | 338 | memcpy(out, &hash, 8); 339 | } 340 | 341 | 342 | void metrohash64_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 343 | { 344 | static const uint64_t k0 = 0xD6D018F5; 345 | static const uint64_t k1 = 0xA2AA033B; 346 | static const uint64_t k2 = 0x62992FC1; 347 | static const uint64_t k3 = 0x30BC5B29; 348 | 349 | const uint8_t * ptr = reinterpret_cast(key); 350 | const uint8_t * const end = ptr + len; 351 | 352 | uint64_t hash = ((static_cast(seed) + k2) * k0) + len; 353 | 354 | if (len >= 32) 355 | { 356 | uint64_t v[4]; 357 | v[0] = hash; 358 | v[1] = hash; 359 | v[2] = hash; 360 | v[3] = hash; 361 | 362 | do 363 | { 364 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 365 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 366 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 367 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 368 | } 369 | while (ptr <= (end - 32)); 370 | 371 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 30) * k1; 372 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 30) * k0; 373 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 30) * k1; 374 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 30) * k0; 375 | hash += v[0] ^ v[1]; 376 | } 377 | 378 | if ((end - ptr) >= 16) 379 | { 380 | uint64_t v0 = hash + (read_u64(ptr) * k2); ptr += 8; v0 = rotate_right(v0,29) * k3; 381 | uint64_t v1 = hash + (read_u64(ptr) * k2); ptr += 8; v1 = rotate_right(v1,29) * k3; 382 | v0 ^= rotate_right(v0 * k0, 34) + v1; 383 | v1 ^= rotate_right(v1 * k3, 34) + v0; 384 | hash += v1; 385 | } 386 | 387 | if ((end - ptr) >= 8) 388 | { 389 | hash += read_u64(ptr) * k3; ptr += 8; 390 | hash ^= rotate_right(hash, 36) * k1; 391 | } 392 | 393 | if ((end - ptr) >= 4) 394 | { 395 | hash += read_u32(ptr) * k3; ptr += 4; 396 | hash ^= rotate_right(hash, 15) * k1; 397 | } 398 | 399 | if ((end - ptr) >= 2) 400 | { 401 | hash += read_u16(ptr) * k3; ptr += 2; 402 | hash ^= rotate_right(hash, 15) * k1; 403 | } 404 | 405 | if ((end - ptr) >= 1) 406 | { 407 | hash += read_u8 (ptr) * k3; 408 | hash ^= rotate_right(hash, 23) * k1; 409 | } 410 | 411 | hash ^= rotate_right(hash, 28); 412 | hash *= k0; 413 | hash ^= rotate_right(hash, 29); 414 | 415 | memcpy(out, &hash, 8); 416 | } 417 | 418 | 419 | -------------------------------------------------------------------------------- /src/metrohash128.cpp: -------------------------------------------------------------------------------- 1 | // metrohash128.cpp 2 | // 3 | // The MIT License (MIT) 4 | // 5 | // Copyright (c) 2015 J. Andrew Rogers 6 | // 7 | // Permission is hereby granted, free of charge, to any person obtaining a copy 8 | // of this software and associated documentation files (the "Software"), to deal 9 | // in the Software without restriction, including without limitation the rights 10 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | // copies of the Software, and to permit persons to whom the Software is 12 | // furnished to do so, subject to the following conditions: 13 | // 14 | // The above copyright notice and this permission notice shall be included in all 15 | // copies or substantial portions of the Software. 16 | // 17 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | // SOFTWARE. 24 | // 25 | 26 | #include 27 | #include "platform.h" 28 | #include "metrohash128.h" 29 | 30 | const char * MetroHash128::test_string = "012345678901234567890123456789012345678901234567890123456789012"; 31 | 32 | const uint8_t MetroHash128::test_seed_0[16] = { 33 | 0xC7, 0x7C, 0xE2, 0xBF, 0xA4, 0xED, 0x9F, 0x9B, 34 | 0x05, 0x48, 0xB2, 0xAC, 0x50, 0x74, 0xA2, 0x97 35 | }; 36 | 37 | const uint8_t MetroHash128::test_seed_1[16] = { 38 | 0x45, 0xA3, 0xCD, 0xB8, 0x38, 0x19, 0x9D, 0x7F, 39 | 0xBD, 0xD6, 0x8D, 0x86, 0x7A, 0x14, 0xEC, 0xEF 40 | }; 41 | 42 | 43 | 44 | MetroHash128::MetroHash128(const uint64_t seed) 45 | { 46 | Initialize(seed); 47 | } 48 | 49 | 50 | void MetroHash128::Initialize(const uint64_t seed) 51 | { 52 | // initialize internal hash registers 53 | state.v[0] = (static_cast(seed) - k0) * k3; 54 | state.v[1] = (static_cast(seed) + k1) * k2; 55 | state.v[2] = (static_cast(seed) + k0) * k2; 56 | state.v[3] = (static_cast(seed) - k1) * k3; 57 | 58 | // initialize total length of input 59 | bytes = 0; 60 | } 61 | 62 | 63 | void MetroHash128::Update(const uint8_t * const buffer, const uint64_t length) 64 | { 65 | const uint8_t * ptr = reinterpret_cast(buffer); 66 | const uint8_t * const end = ptr + length; 67 | 68 | // input buffer may be partially filled 69 | if (bytes % 32) 70 | { 71 | uint64_t fill = 32 - (bytes % 32); 72 | if (fill > length) 73 | fill = length; 74 | 75 | memcpy(input.b + (bytes % 32), ptr, fill); 76 | ptr += fill; 77 | bytes += fill; 78 | 79 | // input buffer is still partially filled 80 | if ((bytes % 32) != 0) return; 81 | 82 | // process full input buffer 83 | state.v[0] += read_u64(&input.b[ 0]) * k0; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; 84 | state.v[1] += read_u64(&input.b[ 8]) * k1; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; 85 | state.v[2] += read_u64(&input.b[16]) * k2; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; 86 | state.v[3] += read_u64(&input.b[24]) * k3; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; 87 | } 88 | 89 | // bulk update 90 | bytes += (end - ptr); 91 | while (ptr <= (end - 32)) 92 | { 93 | // process directly from the source, bypassing the input buffer 94 | state.v[0] += read_u64(ptr) * k0; ptr += 8; state.v[0] = rotate_right(state.v[0],29) + state.v[2]; 95 | state.v[1] += read_u64(ptr) * k1; ptr += 8; state.v[1] = rotate_right(state.v[1],29) + state.v[3]; 96 | state.v[2] += read_u64(ptr) * k2; ptr += 8; state.v[2] = rotate_right(state.v[2],29) + state.v[0]; 97 | state.v[3] += read_u64(ptr) * k3; ptr += 8; state.v[3] = rotate_right(state.v[3],29) + state.v[1]; 98 | } 99 | 100 | // store remaining bytes in input buffer 101 | if (ptr < end) 102 | memcpy(input.b, ptr, end - ptr); 103 | } 104 | 105 | 106 | void MetroHash128::Finalize(uint8_t * const hash) 107 | { 108 | // finalize bulk loop, if used 109 | if (bytes >= 32) 110 | { 111 | state.v[2] ^= rotate_right(((state.v[0] + state.v[3]) * k0) + state.v[1], 21) * k1; 112 | state.v[3] ^= rotate_right(((state.v[1] + state.v[2]) * k1) + state.v[0], 21) * k0; 113 | state.v[0] ^= rotate_right(((state.v[0] + state.v[2]) * k0) + state.v[3], 21) * k1; 114 | state.v[1] ^= rotate_right(((state.v[1] + state.v[3]) * k1) + state.v[2], 21) * k0; 115 | } 116 | 117 | // process any bytes remaining in the input buffer 118 | const uint8_t * ptr = reinterpret_cast(input.b); 119 | const uint8_t * const end = ptr + (bytes % 32); 120 | 121 | if ((end - ptr) >= 16) 122 | { 123 | state.v[0] += read_u64(ptr) * k2; ptr += 8; state.v[0] = rotate_right(state.v[0],33) * k3; 124 | state.v[1] += read_u64(ptr) * k2; ptr += 8; state.v[1] = rotate_right(state.v[1],33) * k3; 125 | state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 45) * k1; 126 | state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 45) * k0; 127 | } 128 | 129 | if ((end - ptr) >= 8) 130 | { 131 | state.v[0] += read_u64(ptr) * k2; ptr += 8; state.v[0] = rotate_right(state.v[0],33) * k3; 132 | state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 27) * k1; 133 | } 134 | 135 | if ((end - ptr) >= 4) 136 | { 137 | state.v[1] += read_u32(ptr) * k2; ptr += 4; state.v[1] = rotate_right(state.v[1],33) * k3; 138 | state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 46) * k0; 139 | } 140 | 141 | if ((end - ptr) >= 2) 142 | { 143 | state.v[0] += read_u16(ptr) * k2; ptr += 2; state.v[0] = rotate_right(state.v[0],33) * k3; 144 | state.v[0] ^= rotate_right((state.v[0] * k2) + state.v[1], 22) * k1; 145 | } 146 | 147 | if ((end - ptr) >= 1) 148 | { 149 | state.v[1] += read_u8 (ptr) * k2; state.v[1] = rotate_right(state.v[1],33) * k3; 150 | state.v[1] ^= rotate_right((state.v[1] * k3) + state.v[0], 58) * k0; 151 | } 152 | 153 | state.v[0] += rotate_right((state.v[0] * k0) + state.v[1], 13); 154 | state.v[1] += rotate_right((state.v[1] * k1) + state.v[0], 37); 155 | state.v[0] += rotate_right((state.v[0] * k2) + state.v[1], 13); 156 | state.v[1] += rotate_right((state.v[1] * k3) + state.v[0], 37); 157 | 158 | bytes = 0; 159 | 160 | // do any endian conversion here 161 | 162 | memcpy(hash, state.v, 16); 163 | } 164 | 165 | 166 | void MetroHash128::Hash(const uint8_t * buffer, const uint64_t length, uint8_t * const hash, const uint64_t seed) 167 | { 168 | const uint8_t * ptr = reinterpret_cast(buffer); 169 | const uint8_t * const end = ptr + length; 170 | 171 | uint64_t v[4]; 172 | 173 | v[0] = (static_cast(seed) - k0) * k3; 174 | v[1] = (static_cast(seed) + k1) * k2; 175 | 176 | if (length >= 32) 177 | { 178 | v[2] = (static_cast(seed) + k0) * k2; 179 | v[3] = (static_cast(seed) - k1) * k3; 180 | 181 | do 182 | { 183 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 184 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 185 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 186 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 187 | } 188 | while (ptr <= (end - 32)); 189 | 190 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 21) * k1; 191 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 21) * k0; 192 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 21) * k1; 193 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 21) * k0; 194 | } 195 | 196 | if ((end - ptr) >= 16) 197 | { 198 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; 199 | v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],33) * k3; 200 | v[0] ^= rotate_right((v[0] * k2) + v[1], 45) * k1; 201 | v[1] ^= rotate_right((v[1] * k3) + v[0], 45) * k0; 202 | } 203 | 204 | if ((end - ptr) >= 8) 205 | { 206 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; 207 | v[0] ^= rotate_right((v[0] * k2) + v[1], 27) * k1; 208 | } 209 | 210 | if ((end - ptr) >= 4) 211 | { 212 | v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],33) * k3; 213 | v[1] ^= rotate_right((v[1] * k3) + v[0], 46) * k0; 214 | } 215 | 216 | if ((end - ptr) >= 2) 217 | { 218 | v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],33) * k3; 219 | v[0] ^= rotate_right((v[0] * k2) + v[1], 22) * k1; 220 | } 221 | 222 | if ((end - ptr) >= 1) 223 | { 224 | v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],33) * k3; 225 | v[1] ^= rotate_right((v[1] * k3) + v[0], 58) * k0; 226 | } 227 | 228 | v[0] += rotate_right((v[0] * k0) + v[1], 13); 229 | v[1] += rotate_right((v[1] * k1) + v[0], 37); 230 | v[0] += rotate_right((v[0] * k2) + v[1], 13); 231 | v[1] += rotate_right((v[1] * k3) + v[0], 37); 232 | 233 | // do any endian conversion here 234 | 235 | memcpy(hash, v, 16); 236 | } 237 | 238 | 239 | bool MetroHash128::ImplementationVerified() 240 | { 241 | uint8_t hash[16]; 242 | const uint8_t * key = reinterpret_cast(MetroHash128::test_string); 243 | 244 | // verify one-shot implementation 245 | MetroHash128::Hash(key, strlen(MetroHash128::test_string), hash, 0); 246 | if (memcmp(hash, MetroHash128::test_seed_0, 16) != 0) return false; 247 | 248 | MetroHash128::Hash(key, strlen(MetroHash128::test_string), hash, 1); 249 | if (memcmp(hash, MetroHash128::test_seed_1, 16) != 0) return false; 250 | 251 | // verify incremental implementation 252 | MetroHash128 metro; 253 | 254 | metro.Initialize(0); 255 | metro.Update(reinterpret_cast(MetroHash128::test_string), strlen(MetroHash128::test_string)); 256 | metro.Finalize(hash); 257 | if (memcmp(hash, MetroHash128::test_seed_0, 16) != 0) return false; 258 | 259 | metro.Initialize(1); 260 | metro.Update(reinterpret_cast(MetroHash128::test_string), strlen(MetroHash128::test_string)); 261 | metro.Finalize(hash); 262 | if (memcmp(hash, MetroHash128::test_seed_1, 16) != 0) return false; 263 | 264 | return true; 265 | } 266 | 267 | 268 | void metrohash128_1(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 269 | { 270 | static const uint64_t k0 = 0xC83A91E1; 271 | static const uint64_t k1 = 0x8648DBDB; 272 | static const uint64_t k2 = 0x7BDEC03B; 273 | static const uint64_t k3 = 0x2F5870A5; 274 | 275 | const uint8_t * ptr = reinterpret_cast(key); 276 | const uint8_t * const end = ptr + len; 277 | 278 | uint64_t v[4]; 279 | 280 | v[0] = ((static_cast(seed) - k0) * k3) + len; 281 | v[1] = ((static_cast(seed) + k1) * k2) + len; 282 | 283 | if (len >= 32) 284 | { 285 | v[2] = ((static_cast(seed) + k0) * k2) + len; 286 | v[3] = ((static_cast(seed) - k1) * k3) + len; 287 | 288 | do 289 | { 290 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 291 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 292 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 293 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 294 | } 295 | while (ptr <= (end - 32)); 296 | 297 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 26) * k1; 298 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 26) * k0; 299 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 26) * k1; 300 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 30) * k0; 301 | } 302 | 303 | if ((end - ptr) >= 16) 304 | { 305 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; 306 | v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],33) * k3; 307 | v[0] ^= rotate_right((v[0] * k2) + v[1], 17) * k1; 308 | v[1] ^= rotate_right((v[1] * k3) + v[0], 17) * k0; 309 | } 310 | 311 | if ((end - ptr) >= 8) 312 | { 313 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],33) * k3; 314 | v[0] ^= rotate_right((v[0] * k2) + v[1], 20) * k1; 315 | } 316 | 317 | if ((end - ptr) >= 4) 318 | { 319 | v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],33) * k3; 320 | v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0; 321 | } 322 | 323 | if ((end - ptr) >= 2) 324 | { 325 | v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],33) * k3; 326 | v[0] ^= rotate_right((v[0] * k2) + v[1], 24) * k1; 327 | } 328 | 329 | if ((end - ptr) >= 1) 330 | { 331 | v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],33) * k3; 332 | v[1] ^= rotate_right((v[1] * k3) + v[0], 24) * k0; 333 | } 334 | 335 | v[0] += rotate_right((v[0] * k0) + v[1], 13); 336 | v[1] += rotate_right((v[1] * k1) + v[0], 37); 337 | v[0] += rotate_right((v[0] * k2) + v[1], 13); 338 | v[1] += rotate_right((v[1] * k3) + v[0], 37); 339 | 340 | // do any endian conversion here 341 | 342 | memcpy(out, v, 16); 343 | } 344 | 345 | 346 | void metrohash128_2(const uint8_t * key, uint64_t len, uint32_t seed, uint8_t * out) 347 | { 348 | static const uint64_t k0 = 0xD6D018F5; 349 | static const uint64_t k1 = 0xA2AA033B; 350 | static const uint64_t k2 = 0x62992FC1; 351 | static const uint64_t k3 = 0x30BC5B29; 352 | 353 | const uint8_t * ptr = reinterpret_cast(key); 354 | const uint8_t * const end = ptr + len; 355 | 356 | uint64_t v[4]; 357 | 358 | v[0] = ((static_cast(seed) - k0) * k3) + len; 359 | v[1] = ((static_cast(seed) + k1) * k2) + len; 360 | 361 | if (len >= 32) 362 | { 363 | v[2] = ((static_cast(seed) + k0) * k2) + len; 364 | v[3] = ((static_cast(seed) - k1) * k3) + len; 365 | 366 | do 367 | { 368 | v[0] += read_u64(ptr) * k0; ptr += 8; v[0] = rotate_right(v[0],29) + v[2]; 369 | v[1] += read_u64(ptr) * k1; ptr += 8; v[1] = rotate_right(v[1],29) + v[3]; 370 | v[2] += read_u64(ptr) * k2; ptr += 8; v[2] = rotate_right(v[2],29) + v[0]; 371 | v[3] += read_u64(ptr) * k3; ptr += 8; v[3] = rotate_right(v[3],29) + v[1]; 372 | } 373 | while (ptr <= (end - 32)); 374 | 375 | v[2] ^= rotate_right(((v[0] + v[3]) * k0) + v[1], 33) * k1; 376 | v[3] ^= rotate_right(((v[1] + v[2]) * k1) + v[0], 33) * k0; 377 | v[0] ^= rotate_right(((v[0] + v[2]) * k0) + v[3], 33) * k1; 378 | v[1] ^= rotate_right(((v[1] + v[3]) * k1) + v[2], 33) * k0; 379 | } 380 | 381 | if ((end - ptr) >= 16) 382 | { 383 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3; 384 | v[1] += read_u64(ptr) * k2; ptr += 8; v[1] = rotate_right(v[1],29) * k3; 385 | v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1; 386 | v[1] ^= rotate_right((v[1] * k3) + v[0], 29) * k0; 387 | } 388 | 389 | if ((end - ptr) >= 8) 390 | { 391 | v[0] += read_u64(ptr) * k2; ptr += 8; v[0] = rotate_right(v[0],29) * k3; 392 | v[0] ^= rotate_right((v[0] * k2) + v[1], 29) * k1; 393 | } 394 | 395 | if ((end - ptr) >= 4) 396 | { 397 | v[1] += read_u32(ptr) * k2; ptr += 4; v[1] = rotate_right(v[1],29) * k3; 398 | v[1] ^= rotate_right((v[1] * k3) + v[0], 25) * k0; 399 | } 400 | 401 | if ((end - ptr) >= 2) 402 | { 403 | v[0] += read_u16(ptr) * k2; ptr += 2; v[0] = rotate_right(v[0],29) * k3; 404 | v[0] ^= rotate_right((v[0] * k2) + v[1], 30) * k1; 405 | } 406 | 407 | if ((end - ptr) >= 1) 408 | { 409 | v[1] += read_u8 (ptr) * k2; v[1] = rotate_right(v[1],29) * k3; 410 | v[1] ^= rotate_right((v[1] * k3) + v[0], 18) * k0; 411 | } 412 | 413 | v[0] += rotate_right((v[0] * k0) + v[1], 33); 414 | v[1] += rotate_right((v[1] * k1) + v[0], 33); 415 | v[0] += rotate_right((v[0] * k2) + v[1], 33); 416 | v[1] += rotate_right((v[1] * k3) + v[0], 33); 417 | 418 | // do any endian conversion here 419 | 420 | memcpy(out, v, 16); 421 | } 422 | 423 | --------------------------------------------------------------------------------