diff --git a/guid.js b/guid.js index 4140e99..10b7f78 100644 --- a/guid.js +++ b/guid.js @@ -1,4 +1,5 @@ var validator = new RegExp("^[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}$", "i"); +var hex_validator = new RegExp("^[a-z0-9]{32}$", "i"); function gen(count) { var out = ""; @@ -8,21 +9,107 @@ function gen(count) { return out; } +var _bin2hex = [ + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' +]; + +var _hex2bin = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // 0-9 + 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // A-F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0,10,11,12,13,14,15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // a-f + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]; + +function bin2hex(str) { + var len = str.length; + var rv = ''; + var i = 0; + var c; + + while (len-- > 0) { + if(str instanceof Buffer) + c = str[i++]; + else + c = str.charCodeAt(i++); + + rv += _bin2hex[(c & 0xf0) >> 4]; + rv += _bin2hex[(c & 0x0f)]; + } + return rv; +} + +function hex2bin(str) { + var len = str.length; + var rv = []; + var i = 0; + + var c1; + var c2; + + while (len > 1) { + h1 = str.charAt(i++); + c1 = h1.charCodeAt(0); + h2 = str.charAt(i++); + c2 = h2.charCodeAt(0); + + rv.push((_hex2bin[c1] << 4) + _hex2bin[c2]); + len -= 2; + } + + return rv; +} + +function hex2uuid(hex) { + return hex.substr(0, 8) + "-" + + hex.substr(8, 4) + "-" + + hex.substr(12, 4) + "-" + + hex.substr(16, 4) + "-" + + hex.substr(20); +} + + function Guid(guid) { if (!guid) throw new TypeError("Invalid argument; `value` has no value."); var value = Guid.EMPTY; + var bin = null; if (guid && guid instanceof Guid) { + // Guid instances value = Guid.toString(); - - } else if (guid && Object.prototype.toString.call(guid) === "[object String]" && Guid.isGuid(guid)) { - value = guid; + } else if (guid && guid instanceof Buffer) { + // binary buffer + value = hex2uuid(bin2hex(guid)); + } else if (guid && Object.prototype.toString.call(guid) === "[object String]") { + if(Guid.isGuid(guid, true)) { + if(guid.length == 32) // value is a pure hex string + value = hex2uuid(guid) + else + value = guid; + } else if (guid.length == 16) { + value = hex2uuid(bin2hex(guid)); + } } - + this.equals = function(other) { // Comparing string `value` against provided `guid` will auto-call // toString on `guid` for comparison + if (typeof(other) == 'string') { + return (other === this.toString()); + } return Guid.isGuid(other) && value == other; }; @@ -37,22 +124,47 @@ function Guid(guid) { this.toJSON = function() { return value; }; + + this.toHex = function() { + return value.split("-").join("") + } + + this.toArray = function() { + return hex2bin(this.toHex()) + } + + this.toBytes = function() { + if(bin) return bin + return bin = new Buffer(this.toArray()); + } + this.inspect = function() { + return "" + } + Object.defineProperty(this, "value", { get: function() { return value; }, enumerable: true }); + + Object.defineProperty(this, "bytes", { + get: function() { return this.toBytes(); }, + enumerable: true + }); }; Object.defineProperty(Guid, "EMPTY", { value: "00000000-0000-0000-0000-000000000000" }); -Guid.isGuid = function(value) { - return value && (value instanceof Guid || validator.test(value.toString())); +Guid.isGuid = function(value, loose) { + return value && (value instanceof Guid || validator.test(value.toString()) || + loose && hex_validator.test(value.toString())); }; -Guid.create = function() { +Guid.create = function(value) { + if(value) + return new Guid(value); return new Guid([gen(2), gen(1), gen(1), gen(1), gen(3)].join("-")); }; diff --git a/tests/test.js b/tests/test.js new file mode 100644 index 0000000..a6a37ec --- /dev/null +++ b/tests/test.js @@ -0,0 +1,47 @@ +var Guid = require('../guid'); +var vows = require('vows'), + assert = require('assert'); + + +module.exports = tests = {} + + +tests.suite1 = vows.describe('guid tests').addBatch({ +"test1": { + "raw interface": function() { + var g = Guid.raw(); + assert.ok(g); + assert.equal(typeof(g), 'string') + assert.equal(g.length, 36) + return + }, + + "object interface": function() { + var tar = [109, 210, 117, 221, 229, 152, 237, 212, 77, 119, 59, 248, 68, 107, 97, 183]; + + var g = Guid.create("6dd275dd-e598-edd4-4d77-3bf8446b61b7"); + 'm\xd2u\xdd\xe5\x98\xed\xd4Mw;\xf8Dka\xb7' + assert.ok(g.equals("6dd275dd-e598-edd4-4d77-3bf8446b61b7"), "not equal to string"); + + assert.equal(g.toHex(), '6dd275dde598edd44d773bf8446b61b7'); + assert.equal(g.value, new Guid.create('6dd275dde598edd44d773bf8446b61b7').value); + assert.deepEqual(g.toArray(), + tar, + "array is not equal"); + + buf = g.toBytes(); + assert.ok(buf instanceof Buffer, "buffer"); + for(var i = 0; i < tar.length; i++) + assert.equal(buf[i], tar[i], "buffer differs"); + + var g2 = Guid.create(buf); + assert.equal(g.value, g2.value, "copy over buffer failed"); + + var g3 = Guid.create('\x00\x00\x00\x00\x00\x00\x00\x00\xab\xcd\x12\x34\x56\x78\x90\x12'); + assert.equal(g3.value, "00000000-0000-0000-abcd-123456789012") + + } +}, +}); + +console.log(vows)