├── .travis.yml ├── .gitignore ├── AUTHORS ├── appveyor.yml ├── lib ├── murmurhash.js └── murmur.js ├── LICENSE.txt ├── package.json ├── History.md ├── test ├── benchmark.js └── murmurhash.test.js └── README.md /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.10' 5 | - '0.12' 6 | - '4' 7 | - '6' 8 | - '7' 9 | script: 10 | - npm run ci 11 | after_script: 12 | - npm i codecov && codecov 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage.html 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | build/ 14 | 15 | node_modules 16 | npm-debug.log 17 | coverage/ 18 | lib/binding 19 | .idea 20 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | # Ordered by date of first contribution. 2 | 3 | fengmk2 (https://github.com/fengmk2) 4 | Jackson Tian (https://github.com/JacksonTian) 5 | zhangzifa (https://github.com/zhangzifa) 6 | alsotang (https://github.com/alsotang) 7 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | matrix: 3 | - nodejs_version: '0.10' 4 | - nodejs_version: '0.12' 5 | - nodejs_version: '4' 6 | - nodejs_version: '6' 7 | - nodejs_version: '7' 8 | 9 | install: 10 | - ps: Install-Product node $env:nodejs_version 11 | - npm i 12 | 13 | test_script: 14 | - npm run ci 15 | 16 | build: off 17 | -------------------------------------------------------------------------------- /lib/murmurhash.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var murmur = require('./murmur'); 4 | 5 | /** 6 | * Murmur hash v2 7 | * 8 | * @param {Buffer|String} key 9 | * @param {Number} seed default is 97 10 | * @return {Number} hash value 11 | */ 12 | function murmurhash(key, seed) { 13 | if (typeof key === 'string') { 14 | key = new Buffer(key); 15 | } else if (!Buffer.isBuffer(key)) { 16 | key = new Buffer(String(key)); 17 | } 18 | if (typeof seed !== 'number') { 19 | seed = 97; 20 | } 21 | return murmur(key, seed); 22 | } 23 | 24 | module.exports = murmurhash; 25 | module.exports.murmur = murmur; 26 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | This software is licensed under the MIT License. 2 | 3 | Copyright(c) node-modules and other contributors. 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 13 | all 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 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-murmurhash", 3 | "version": "2.1.0", 4 | "description": "murmurhash V2, support utf8 Buffer", 5 | "main": "lib/murmurhash.js", 6 | "files": [ 7 | "lib" 8 | ], 9 | "scripts": { 10 | "test": "mocha --check-leaks -R spec test/*.test.js", 11 | "ci": "node_modules/.bin/istanbul cover node_modules/.bin/_mocha -- --check-leaks test/*.test.js" 12 | }, 13 | "dependencies": { 14 | }, 15 | "devDependencies": { 16 | "qn": "*", 17 | "beautify-benchmark": "0", 18 | "benchmark": "1", 19 | "istanbul": "*", 20 | "mocha": "*", 21 | "should": "4" 22 | }, 23 | "homepage": "https://github.com/node-modules/node-murmurhash", 24 | "repository": { 25 | "type": "git", 26 | "url": "git://github.com/node-modules/node-murmurhash.git", 27 | "web": "https://github.com/node-modules/node-murmurhash" 28 | }, 29 | "bugs": { 30 | "url": "https://github.com/node-modules/node-murmurhash/issues", 31 | "email": "fengmk2@gmail.com" 32 | }, 33 | "keywords": [ 34 | "node-murmurhash", 35 | "murmurhash", 36 | "murmurhashV2", 37 | "murmurhash2" 38 | ], 39 | "engines": { 40 | "node": ">= 0.10.0" 41 | }, 42 | "author": "fengmk2 (https://fengmk2.com)", 43 | "license": "MIT" 44 | } 45 | -------------------------------------------------------------------------------- /History.md: -------------------------------------------------------------------------------- 1 | 2 | 2.1.0 / 2015-11-26 3 | ================== 4 | 5 | * feat: use Math.imul (@alsotang) 6 | 7 | 2.0.1 / 2015-07-21 8 | ================== 9 | 10 | * remove `& 0xff` when retrive byte (@alsotang) 11 | * simple benchmark. clean code. 12 | 13 | 2.0.0 / 2015-04-20 14 | ================== 15 | 16 | * test: add more boundary test cases 17 | * feat: pure javascript impl murmurhash v2. Thanks @zhangzifa 18 | 19 | 1.1.0 / 2015-02-04 20 | ================== 21 | 22 | * use node-pre-gyp-qn to prebuild binary package 23 | * upgrade nan to 1.5.2 support iojs and more node versions 24 | 25 | 1.0.1 / 2015-01-13 26 | ================== 27 | 28 | * Upgrade nan to 1.4.1 for support alinode/iojs 29 | 30 | 1.0.0 / 2014-09-25 31 | ================== 32 | 33 | * bump nan to 1.3.0 support node >= 0.11.13 now 34 | * add totoro ci tests 35 | * add missing line to murmur.cc 36 | * is hard to impl uint32 op in js 37 | 38 | 0.0.4 / 2014-03-22 39 | ================== 40 | 41 | * update deps and add jshint 42 | 43 | 0.0.3 / 2013-12-10 44 | ================== 45 | 46 | * remove node.h, because nan.h had include it 47 | 48 | 0.0.2 / 2013-12-10 49 | ================== 50 | 51 | * .node-gyp/0.11.8/src/node.h:176: error: ‘ssize_t’ does not name a type bug fix 52 | 53 | 0.0.1 / 2013-12-04 54 | ================== 55 | 56 | * add benchmark 57 | * add logo 58 | * init 59 | -------------------------------------------------------------------------------- /lib/murmur.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = murmurhash2js; 4 | 5 | var MURMURHASH_M = 0x5bd1e995; 6 | 7 | function negative(num) { 8 | if (num < 127) { 9 | return num; 10 | } 11 | return num | 0xffffff00 12 | } 13 | 14 | var imul = (function () { 15 | if (Math.imul) { 16 | return Math.imul; 17 | } else { 18 | return function (a, b) { 19 | return ((((a >> 16) * b) & 0xffff) << 16) + ((a & 0xffff) * b) 20 | } 21 | } 22 | })() 23 | 24 | function murmurhash2js(key, seed) { 25 | var l = key.length; 26 | var h = seed ^ l; 27 | var i = 0; 28 | var k = 0; 29 | 30 | while (l >= 4) { 31 | k = (key[i] | 32 | (key[i + 1] << 8) | 33 | (key[i + 2] << 16) | 34 | (key[i + 3] << 24)); 35 | 36 | // js 中,Number 能表示的最高精度整数是 (2 ** 53 - 1), 37 | // 此处两个 32 位整数相乘时,有可能产生 64 位的结果,导致精度丢失。 38 | k = imul(k, MURMURHASH_M) 39 | 40 | k ^= k >>> 24; 41 | k = imul(k, MURMURHASH_M) 42 | 43 | h = imul(h, MURMURHASH_M) ^ k 44 | 45 | l -= 4; 46 | i += 4; 47 | } 48 | 49 | switch (l) { 50 | case 3: 51 | h ^= negative(key[i + 2]) << 16; 52 | case 2: 53 | h ^= negative(key[i + 1]) << 8; 54 | case 1: 55 | h ^= negative(key[i]); 56 | h = imul(h, MURMURHASH_M) 57 | } 58 | 59 | h ^= h >>> 13; 60 | h = imul(h, MURMURHASH_M) 61 | h ^= h >>> 15; 62 | 63 | return h >>> 0; 64 | } 65 | -------------------------------------------------------------------------------- /test/benchmark.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var Benchmark = require('benchmark'); 4 | var benchmarks = require('beautify-benchmark'); 5 | var murmurhash = require('../'); 6 | 7 | var asciiBuffer = new Buffer('haha, this is key'); 8 | var utf8Buffer = new Buffer('快使用双节棍,嘿嘿嘿嘿。'); 9 | var asciiString = 'haha, this is key'; 10 | var utf8String = '快使用双节棍,嘿嘿嘿嘿。'; 11 | 12 | console.log('murmurhash should be 335538535: %s', murmurhash(asciiBuffer)); 13 | console.log('murmurhash utf8 should be 705333708: %s', murmurhash(utf8Buffer)); 14 | console.log('murmurhash with custom key should be 2639541842: %s', murmurhash(utf8Buffer, 12333)); 15 | console.log("murmurhash('hello 中国') should be 1248731102: %s", murmurhash('hello 中国')); 16 | 17 | var suite = new Benchmark.Suite(); 18 | 19 | suite 20 | .add("murmurhash(new Buffer('haha, this is key'))", function () { 21 | murmurhash(asciiBuffer); 22 | }) 23 | .add("murmurhash(new Buffer('快使用双节棍,嘿嘿嘿嘿。'))", function () { 24 | murmurhash(utf8Buffer); 25 | }) 26 | .add("murmurhash('haha, this is key')", function () { 27 | murmurhash(asciiString); 28 | }) 29 | .add("murmurhash('快使用双节棍,嘿嘿嘿嘿。')", function () { 30 | murmurhash(utf8String); 31 | }) 32 | .on('cycle', function(event) { 33 | benchmarks.add(event.target); 34 | }) 35 | .on('start', function(event) { 36 | console.log('\n node version: %s, date: %s\n Starting...', process.version, Date()); 37 | }) 38 | .on('complete', function done() { 39 | benchmarks.log(); 40 | }) 41 | .run({ 'async': false }); 42 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-murmurhash 2 | ======= 3 | 4 | [![NPM version][npm-image]][npm-url] 5 | [![build status][travis-image]][travis-url] 6 | [![Test coverage][codecov-image]][codecov-url] 7 | [![David deps][david-image]][david-url] 8 | [![npm download][download-image]][download-url] 9 | 10 | [npm-image]: https://img.shields.io/npm/v/node-murmurhash.svg?style=flat-square 11 | [npm-url]: https://npmjs.org/package/node-murmurhash 12 | [travis-image]: https://img.shields.io/travis/node-modules/node-murmurhash.svg?style=flat-square 13 | [travis-url]: https://travis-ci.org/node-modules/node-murmurhash 14 | [codecov-image]: https://codecov.io/github/node-modules/node-murmurhash/coverage.svg?branch=master 15 | [codecov-url]: https://codecov.io/github/node-modules/node-murmurhash?branch=master 16 | [david-image]: https://img.shields.io/david/node-modules/node-murmurhash.svg?style=flat-square 17 | [david-url]: https://david-dm.org/node-modules/node-murmurhash 18 | [download-image]: https://img.shields.io/npm/dm/node-murmurhash.svg?style=flat-square 19 | [download-url]: https://npmjs.org/package/node-murmurhash 20 | 21 | murmurhash V2, support utf8 Buffer. 22 | 23 | ## Install 24 | 25 | ```bash 26 | $ npm install node-murmurhash --save 27 | ``` 28 | 29 | ## Usage 30 | 31 | ```js 32 | var murmurhash = require('node-murmurhash'); 33 | 34 | murmurhash('hello 中国', 97); // 1248731102 35 | ``` 36 | 37 | ## Benchmark 38 | 39 | ``` 40 | node version: v4.2.1, date: Tue Dec 01 2015 14:06:01 GMT+0800 (CST) 41 | 42 | Starting... 43 | 4 tests completed. 44 | 45 | murmurhash(new Buffer('haha, this is key')) x 18,416,878 ops/sec ±0.70% (96 runs sampled) 46 | murmurhash(new Buffer('快使用双节棍,嘿嘿嘿嘿。')) x 12,709,085 ops/sec ±0.80% (93 runs sampled) 47 | murmurhash('haha, this is key') x 804,541 ops/sec ±24.21% (90 runs sampled) 48 | murmurhash('快使用双节棍,嘿嘿嘿嘿。') x 797,345 ops/sec ±0.93% (92 runs sampled) 49 | ``` 50 | 51 | see also: [benchmark.js](test/benchmark.js) 52 | 53 | 54 | ## License 55 | 56 | [MIT](LICENSE.txt) 57 | -------------------------------------------------------------------------------- /test/murmurhash.test.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var should = require('should'); 4 | var murmurhash = require('../'); 5 | 6 | describe('murmurhash.test.js', function () { 7 | it('should return hash number value', function () { 8 | var v = murmurhash('foo'); 9 | v.should.equal(2197288083); 10 | 11 | murmurhash('a').should.equal(3914231485); 12 | murmurhash('z').should.equal(2176583841); 13 | murmurhash('hello').should.equal(867496620); 14 | murmurhash('hello 中国').should.equal(1248731102); 15 | murmurhash('hello 中国', 0).should.equal(3760673533); 16 | murmurhash('hello 中国', 1).should.equal(540531333); 17 | 18 | var keybytes = new Buffer([ 19 | 0, 4, 111, 108, 95, 99, 51, 95, 117, 95, 105, 112, 95, -27, -116, -105, -26, 20 | -98, -127, -25, -69, -110, -25, -122, -108, -24, -98, -115, -28, -72, -109, 21 | -27, -115, -106, -27, -70, -105, 95, 112 22 | ]); 23 | // console.log(murmurhash(keybytes), parseInt(murmurhash(keybytes) % 1023, 10)); 24 | murmurhash(keybytes).should.equal(3300337389); 25 | parseInt(murmurhash(keybytes) % 1023, 10).should.equal(261); 26 | 27 | var wantBytes = new Buffer([0, 24, 0, 4, 52, 51, 53, 54, 55, 52, 52, 53, 54]); 28 | murmurhash(wantBytes).should.equal(2351022266); 29 | parseInt(murmurhash(wantBytes) % 1023, 10).should.equal(494); 30 | }); 31 | 32 | it('should support not string type', function () { 33 | murmurhash('1').should.equal(murmurhash(1)); 34 | }); 35 | 36 | it('should work with large buffer', function () { 37 | var buf = new Buffer([ 38 | 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 39 | 0xc, 0xd, 0xe, 0xf, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 40 | 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 41 | 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 42 | 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 43 | 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 44 | 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53, 45 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 46 | 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 47 | 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 48 | 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 49 | 0xffffff80, 0xffffff81, 0xffffff82, 0xffffff83, 0xffffff84, 0xffffff85, 50 | 0xffffff86, 0xffffff87, 0xffffff88, 0xffffff89, 0xffffff8a, 0xffffff8b, 51 | 0xffffff8c, 0xffffff8d, 0xffffff8e, 0xffffff8f, 0xffffff90, 0xffffff91, 52 | 0xffffff92, 0xffffff93, 0xffffff94, 0xffffff95, 0xffffff96, 0xffffff97, 53 | 0xffffff98, 0xffffff99, 0xffffff9a, 0xffffff9b, 0xffffff9c, 0xffffff9d, 54 | 0xffffff9e, 0xffffff9f, 0xffffffa0, 0xffffffa1, 0xffffffa2, 0xffffffa3, 55 | 0xffffffa4, 0xffffffa5, 0xffffffa6, 0xffffffa7, 0xffffffa8, 0xffffffa9, 56 | 0xffffffaa, 0xffffffab, 0xffffffac, 0xffffffad, 0xffffffae, 0xffffffaf, 57 | 0xffffffb0, 0xffffffb1, 0xffffffb2, 0xffffffb3, 0xffffffb4, 0xffffffb5, 58 | 0xffffffb6, 0xffffffb7, 0xffffffb8, 0xffffffb9, 0xffffffba, 0xffffffbb, 59 | 0xffffffbc, 0xffffffbd, 0xffffffbe, 0xffffffbf, 0xffffffc0, 0xffffffc1, 60 | 0xffffffc2, 0xffffffc3, 0xffffffc4, 0xffffffc5, 0xffffffc6, 0xffffffc7, 61 | 0xffffffc8, 0xffffffc9, 0xffffffca, 0xffffffcb, 0xffffffcc, 0xffffffcd, 62 | 0xffffffce, 0xffffffcf, 0xffffffd0, 0xffffffd1, 0xffffffd2, 0xffffffd3, 63 | 0xffffffd4, 0xffffffd5, 0xffffffd6, 0xffffffd7, 0xffffffd8, 0xffffffd9, 64 | 0xffffffda, 0xffffffdb, 0xffffffdc, 0xffffffdd, 0xffffffde, 0xffffffdf, 65 | 0xffffffe0, 0xffffffe1, 0xffffffe2, 0xffffffe3, 0xffffffe4, 0xffffffe5, 66 | 0xffffffe6, 0xffffffe7, 0xffffffe8, 0xffffffe9, 0xffffffea, 0xffffffeb, 67 | 0xffffffec, 0xffffffed, 0xffffffee, 0xffffffef, 0xfffffff0, 0xfffffff1, 68 | 0xfffffff2, 0xfffffff3, 0xfffffff4, 0xfffffff5, 0xfffffff6, 0xfffffff7, 69 | 0xfffffff8, 0xfffffff9, 0xfffffffa, 0xfffffffb, 0xfffffffc, 0xfffffffd, 70 | 0xfffffffe, 0xffffffff 71 | ]); 72 | // c++ => 2673451384 73 | murmurhash(buf).should.equal(2673451384); 74 | }); 75 | 76 | it('should emtpy string got 3397915750', function () { 77 | murmurhash('').should.equal(3397915750); 78 | murmurhash(new Buffer('')).should.equal(3397915750); 79 | murmurhash(new Buffer([])).should.equal(3397915750); 80 | }); 81 | 82 | it('should work with boundary cases: buffer mod length equal to 0,1,2,3', function () { 83 | // @zhangzifa: 84 | // 要覆盖到buffer的长度对4取模后余0/1/2/3个字节的情况 85 | // 并且这些尾数要分正负数情况 86 | 87 | var b = new Buffer([1, 2, 3, 4, 5, 6, 7, 8, 9]); 88 | murmurhash(b).should.equal(1543150919); 89 | var b1 = new Buffer([1, 2, 3, 4, 5, 6, 7, 8]); 90 | murmurhash(b1).should.equal(3881185275); 91 | var b2 = new Buffer([1, 2, 3, 4, 5, 6, 7]); 92 | murmurhash(b2).should.equal(782934980); 93 | var b3 = new Buffer([1, 2, 3, 4, 5, 6]); 94 | murmurhash(b3).should.equal(764621759); 95 | 96 | var nb = new Buffer([-1, -2, -3, -4, -5, -6, -7, -8, -9]); 97 | murmurhash(nb).should.equal(1119296559); 98 | var nb1 = new Buffer([-1, -2, -3, -4, -5, -6, -7, -8]); 99 | murmurhash(nb1).should.equal(3332328025); 100 | var nb2 = new Buffer([-1, -2, -3, -4, -5, -6, -7]); 101 | murmurhash(nb2).should.equal(1302582711); 102 | var nb3 = new Buffer([-1, -2, -3, -4, -5, -6]); 103 | murmurhash(nb3).should.equal(85049495); 104 | 105 | var bnb = new Buffer([22, 145, 123, 149, 111, 156, 138, 238, 219]); 106 | murmurhash(bnb).should.equal(2935442895); 107 | var bnb1 = new Buffer([45, 222, 147, 158, 233, 156, 138, 238]); 108 | murmurhash(bnb1).should.equal(2003139132); 109 | var bnb2 = new Buffer([34, 255, 234, 168, 222, 145, 138]); 110 | murmurhash(bnb2).should.equal(2619540178); 111 | var bnb3 = new Buffer([88, 254, 222, 168, 134, 145]); 112 | murmurhash(bnb3).should.equal(3356056646); 113 | }); 114 | }); 115 | --------------------------------------------------------------------------------