diff --git a/binding.cc b/binding.cc index 636a291..c29ef56 100644 --- a/binding.cc +++ b/binding.cc @@ -29,26 +29,19 @@ using namespace v8; -Local -stringify_hash(uint64 hash) { - // hash to char - char buf[MAX_64_HASH_LEN]; - memset(buf, 0, MAX_64_HASH_LEN); - snprintf(buf, MAX_64_HASH_LEN, "%llu", hash); - - return String::New(buf); +inline uint32 Uint64High32(const uint64 &x) { + return (uint32)((x >> 32) & 0xFFFFFFFF); } -Local -stringify_hash(uint128* hash) { - // hash to char - char buf[MAX_128_HASH_LEN]; - memset(buf, 0, MAX_128_HASH_LEN); - snprintf(buf, MAX_128_HASH_LEN, "%llu,%llu", Uint128Low64(*hash), Uint128High64(*hash)); +inline uint32 Uint64Low32(const uint64 &x) { + return (uint32)x; +} - return String::New(buf); +inline uint64 HighLow32ToUint64(const uint32 &low, const uint32 &high) { + return ((uint64_t)low) + ((((uint64_t)high) << 32) & 0xFFFFFFFF00000000); } + uint64 to_uint64(const char* data, size_t len) { std::stringstream str; @@ -59,6 +52,13 @@ to_uint64(const char* data, size_t len) { return v; } +inline uint64 +to_uint64(const Local &obj) { + Local low = obj->Get(String::New("low"))->ToUint32(); + Local high = obj->Get(String::New("high"))->ToUint32(); + return HighLow32ToUint64(low->Value(), high->Value()); +} + void to_uint128(uint128* v, const char* data, size_t len) { std::stringstream str; @@ -84,6 +84,110 @@ to_uint128(uint128* v, const char* data, size_t len) { } } +inline void +to_uint128(uint128 *v, const Local &obj) { + v->first = to_uint64(obj->Get(String::New("low"))->ToObject()); + v->second = to_uint64(obj->Get(String::New("high"))->ToObject()); +} + +Local +stringify_hash(const uint64 &hash) { + // hash to char + char buf[MAX_64_HASH_LEN]; + memset(buf, 0, MAX_64_HASH_LEN); + snprintf(buf, MAX_64_HASH_LEN, "%llu", hash); + + return String::New(buf); +} + +Local +stringify_hash(const uint128 &hash) { + // hash to char + char buf[MAX_128_HASH_LEN]; + memset(buf, 0, MAX_128_HASH_LEN); + snprintf(buf, MAX_128_HASH_LEN, "%llu,%llu", Uint128Low64(hash), Uint128High64(hash)); + + return String::New(buf); +} + +Local +stringify_hash(Local obj) { + Local lowObject = obj->Get(String::New("low")); + if(lowObject->IsObject()) { + uint128 hash128; + to_uint128(&hash128, obj); + return stringify_hash(hash128); + } else { + return stringify_hash(to_uint64(obj)); + } +} + +Local +objectify_hash(const uint64 &hash) { + uint32 low = Uint64Low32(hash); + uint32 high = Uint64High32(hash); + + Local ret = Object::New(); + ret->Set(String::New("low"), Integer::NewFromUnsigned(low)); + ret->Set(String::New("high"), Integer::NewFromUnsigned(high)); + ret->Set(String::New("value"), stringify_hash(hash)); + ret->Set(String::New("uint64"), Boolean::New(true)); + + return ret; +} + +Local +objectify_hash(const uint128 &hash) { + Local ret = Object::New(); + ret->Set(String::New("low"), objectify_hash(Uint128Low64(hash))); + ret->Set(String::New("high"), objectify_hash(Uint128High64(hash))); + ret->Set(String::New("value"), stringify_hash(hash)); + ret->Set(String::New("uint128"), Boolean::New(true)); + + return ret; +} + +Local +objectify_hash(const String::AsciiValue &hash) { + const char *data = *hash; + size_t len = hash.length(); + + char *ch = strchr(data, ','); + if(ch == NULL) { + return objectify_hash(to_uint64(data, len)); + } else { + uint128 hash128; + to_uint128(&hash128, data, len); + return objectify_hash(hash128); + } +} + +Handle +node_Stringify(const Arguments& args) { + HandleScope scope; + + int args_len = args.Length(); + if(args_len != 1 || !args[0]->IsObject()) { + return ThrowException(String::New("Invalid arguments.")); + } + + Local obj = args[0]->ToObject(); + return scope.Close(stringify_hash(obj)); +} + +Handle +node_Objectify(const Arguments& args) { + HandleScope scope; + + int args_len = args.Length(); + if(args_len != 1) { + return ThrowException(String::New("Invalid arguments.")); + } + + String::AsciiValue obj(args[0]->ToString()); + return scope.Close(objectify_hash(obj)); +} + Handle node_CityHash64(const Arguments& args) { HandleScope scope; @@ -115,7 +219,7 @@ node_CityHash64(const Arguments& args) { hash = CityHash64WithSeeds(str, len, seed0, seed1); } - return scope.Close(stringify_hash(hash)); + return scope.Close(objectify_hash(hash)); } Handle @@ -143,7 +247,7 @@ node_CityHash128(const Arguments& args) { hash = CityHash128(str, len); } - return scope.Close(stringify_hash(&hash)); + return scope.Close(objectify_hash(hash)); } Handle @@ -171,12 +275,14 @@ node_CityHashCrc128(const Arguments& args) { hash = CityHashCrc128(str, len); } - return scope.Close(stringify_hash(&hash)); + return scope.Close(objectify_hash(hash)); } extern "C" void init (Handle target) { HandleScope scope; + target->Set(String::New("stringify"), FunctionTemplate::New(node_Stringify)->GetFunction()); + target->Set(String::New("objectify"), FunctionTemplate::New(node_Objectify)->GetFunction()); target->Set(String::New("hash64"), FunctionTemplate::New(node_CityHash64)->GetFunction()); target->Set(String::New("hash128"), FunctionTemplate::New(node_CityHash128)->GetFunction()); target->Set(String::New("crc128"), FunctionTemplate::New(node_CityHashCrc128)->GetFunction()); diff --git a/test.js b/test.js index 381a12a..7e10e90 100644 --- a/test.js +++ b/test.js @@ -2,15 +2,37 @@ var cityhash = require('./build/default/cityhash.node'); var passed = true; +function equal(expected, actual) { + var type_expected = typeof expected; + var type_actual = typeof actual; + + if(type_expected != type_actual) { + return false; + } else if(type_expected === 'object') { + for(var key in expected) { + var expected_value = expected[key]; + var actual_value = actual[key]; + if(!equal(expected_value, actual_value)) { + return false; + } + } + } else if(expected != actual) { + return false; + } + return true; +} + function assertEqual(expected, actual, message) { - if(expected != actual) { + var failed = !equal(expected, actual); + + if(failed) { if(message) { message += '. '; } else { message = ''; } passed = false; - console.log('[Error]' + message + 'Expected ' + expected + ' , but actual is ' + actual); + console.log('[Error]' + message + 'Expected ' + JSON.stringify(expected) + ' , but actual is ' + JSON.stringify(actual)); } } @@ -20,16 +42,140 @@ function end() { } } -assertEqual('6596376470467341850', cityhash.hash64('Hello'), 'Hash64 for "Hello"'); -assertEqual('2578220239953316063', cityhash.hash64('hello'), 'Hash64 for "hello"'); -assertEqual('15738392108067291633', cityhash.hash64('Hello', 87392039), 'Hash64 for "hello" with seed 87392039'); -assertEqual('16267654833214665223', cityhash.hash64('Hello', 87392039, 1230234), 'Hash64 for "hello" with seed 87392039 and 1230234'); +assertEqual('6596376470467341850', cityhash.stringify({low:1727229466, high:1535838579}), 'Stringify for uint64 object'); +assertEqual({low:1727229466, high:1535838579}, cityhash.objectify('6596376470467341850'), 'Objectify for uint64 hash string'); + +assertEqual('9138004313465017137,12242971252332641544', cityhash.stringify({ + "low": { + "low": 68277041, + "high": 2127607426 + }, + "high": { + "low": 3936042248, + "high": 2850538876 + }}), 'Stringify for uint128 object'); + +assertEqual({ + "low": { + "low": 68277041, + "high": 2127607426 + }, + "high": { + "low": 3936042248, + "high": 2850538876 + }}, cityhash.objectify('9138004313465017137,12242971252332641544'), 'Stringify for uint128 object'); + +assertEqual({ + low: 1727229466, + high: 1535838579, + uint64: true, + value:'6596376470467341850' + }, cityhash.hash64('Hello'), 'Hash64 for "Hello"'); + +assertEqual({ + low: 4079208671, + high: 600288677, + uint64: true, + value: '2578220239953316063' + }, cityhash.hash64('hello'), 'Hash64 for "hello"'); + +assertEqual({ + low: 2569634289, + high: 3664379964, + uint64: true, + value: '15738392108067291633' + }, cityhash.hash64('Hello', 87392039), 'Hash64 for "hello" with seed 87392039'); + +assertEqual({ + low: 2389520903, + high: 3787608545, + value: '16267654833214665223', + uint64: true, + }, cityhash.hash64('Hello', 87392039, 1230234), 'Hash64 for "hello" with seed 87392039 and 1230234'); + +assertEqual({ + "low": { + "low": 68277041, + "high": 2127607426, + "value": "9138004313465017137", + "uint64": true + }, + "high": { + "low": 3936042248, + "high": 2850538876, + "value": "12242971252332641544", + "uint64": true + }, + "value": "9138004313465017137,12242971252332641544", + "uint128": true + }, cityhash.hash128('Hello'), 'Hash128 for "Hello"'); + +assertEqual({ + "low": { + "low": 3440602095, + "high": 3148776037, + "value": "13523890104784088047", + "uint64": true + }, + "high": { + "low": 2750723564, + "high": 4052229467, + "value": "17404193039403234796", + "uint64": true + }, + "value": "13523890104784088047,17404193039403234796", + "uint128": true + }, cityhash.hash128('hello'), 'Hash128 for "hello"'); + +assertEqual({ + "low": { + "low": 3184066266, + "high": 3674042232, + "value": "15779891233746910938", + "uint64": true + }, + "high": { + "low": 4196783977, + "high": 3519958761, + "value": "15118107765960464233", + "uint64": true + }, + "value": "15779891233746910938,15118107765960464233", + "uint128": true + }, cityhash.hash128('Hello', '12343,30293'), 'Hash128 for "Hello" with seed 12343,30293'); -assertEqual('9138004313465017137,12242971252332641544', cityhash.hash128('Hello'), 'Hash128 for "Hello"'); -assertEqual('13523890104784088047,17404193039403234796', cityhash.hash128('hello'), 'Hash128 for "hello"'); -assertEqual('15779891233746910938,15118107765960464233', cityhash.hash128('Hello', '12343,30293'), 'Hash128 for "Hello" with seed 12343,30293'); +assertEqual({ + "low": { + "low": 68277041, + "high": 2127607426, + "value": "9138004313465017137", + "uint64": true + }, + "high": { + "low": 3936042248, + "high": 2850538876, + "value": "12242971252332641544", + "uint64": true + }, + "value": "9138004313465017137,12242971252332641544", + "uint128": true + }, cityhash.crc128('Hello'), 'Crc128 for "Hello"'); -assertEqual('9138004313465017137,12242971252332641544', cityhash.crc128('Hello'), 'Crc128 for "Hello"'); -assertEqual('15779891233746910938,15118107765960464233', cityhash.crc128('Hello', '12343,30293'), 'Crc128 for "Hello" with seed 12343,30293'); +assertEqual({ + "low": { + "low": 3184066266, + "high": 3674042232, + "value": "15779891233746910938", + "uint64": true + }, + "high": { + "low": 4196783977, + "high": 3519958761, + "value": "15118107765960464233", + "uint64": true + }, + "value": "15779891233746910938,15118107765960464233", + "uint128": true + }, cityhash.crc128('Hello', '12343,30293'), 'Crc128 for "Hello" with seed 12343,30293'); end(); \ No newline at end of file