diff --git a/lib/bn.js b/lib/bn.js index 855dfa62..24d8d33a 100644 --- a/lib/bn.js +++ b/lib/bn.js @@ -91,23 +91,19 @@ var start = 0; if (number[0] === '-') { start++; - } - - if (base === 16) { - this._parseHex(number, start); - } else { - this._parseBase(number, base, start); - } - - if (number[0] === '-') { this.negative = 1; } - this.strip(); - - if (endian !== 'le') return; - - this._initArray(this.toArray(), base, endian); + if (start < number.length) { + if (base === 16) { + this._parseHex(number, start, endian); + } else { + this._parseBase(number, base, start); + if (endian === 'le') { + this._initArray(this.toArray(), base, endian); + } + } + } }; BN.prototype._initNumber = function _initNumber (number, base, endian) { @@ -183,31 +179,29 @@ return this.strip(); }; - function parseHex (str, start, end) { - var r = 0; - var len = Math.min(str.length, end); - for (var i = start; i < len; i++) { - var c = str.charCodeAt(i) - 48; - - r <<= 4; - - // 'a' - 'f' - if (c >= 49 && c <= 54) { - r |= c - 49 + 0xa; - - // 'A' - 'F' - } else if (c >= 17 && c <= 22) { - r |= c - 17 + 0xa; + function parseHex4Bits (string, index) { + var c = string.charCodeAt(index); + // 'A' - 'F' + if (c >= 65 && c <= 70) { + return c - 55; + // 'a' - 'f' + } else if (c >= 97 && c <= 102) { + return c - 87; + // '0' - '9' + } else { + return (c - 48) & 0xf; + } + } - // '0' - '9' - } else { - r |= c & 0xf; - } + function parseHexByte (string, lowerBound, index) { + var r = parseHex4Bits(string, index); + if (index - 1 >= lowerBound) { + r |= parseHex4Bits(string, index - 1) << 4; } return r; } - BN.prototype._parseHex = function _parseHex (number, start) { + BN.prototype._parseHex = function _parseHex (number, start, endian) { // Create possibly bigger array to ensure that it fits the number this.length = Math.ceil((number.length - start) / 6); this.words = new Array(this.length); @@ -215,25 +209,38 @@ this.words[i] = 0; } - var j, w; - // Scan 24-bit chunks and add them to the number + // 24-bits chunks var off = 0; - for (i = number.length - 6, j = 0; i >= start; i -= 6) { - w = parseHex(number, i, i + 6); - this.words[j] |= (w << off) & 0x3ffffff; - // NOTE: `0x3fffff` is intentional here, 26bits max shift + 24bit hex limb - this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; - off += 24; - if (off >= 26) { - off -= 26; - j++; - } - } - if (i + 6 !== start) { - w = parseHex(number, start, i + 6); - this.words[j] |= (w << off) & 0x3ffffff; - this.words[j + 1] |= w >>> (26 - off) & 0x3fffff; + var j = 0; + + var w; + if (endian === 'be') { + for (i = number.length - 1; i >= start; i -= 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } + } else { + var parseLength = number.length - start; + for (i = parseLength % 2 === 0 ? start + 1 : start; i < number.length; i += 2) { + w = parseHexByte(number, start, i) << off; + this.words[j] |= w & 0x3ffffff; + if (off >= 18) { + off -= 18; + j += 1; + this.words[j] |= w >>> 26; + } else { + off += 8; + } + } } + this.strip(); }; @@ -304,6 +311,8 @@ this._iaddn(word); } } + + this.strip(); }; BN.prototype.copy = function copy (dest) { diff --git a/test/constructor-test.js b/test/constructor-test.js index 11c7df08..b20cb3a2 100644 --- a/test/constructor-test.js +++ b/test/constructor-test.js @@ -90,6 +90,12 @@ describe('BN.js/Constructor', function () { assert.equal(new BN('1A6B765D8CDF', 16, 'le').toString(16), 'df8c5d766b1a'); }); + + it('should accept base-16 LE integer with leading zeros', function () { + assert.equal(new BN('0010', 16, 'le').toNumber(), 4096); + assert.equal(new BN('-010', 16, 'le').toNumber(), -4096); + assert.equal(new BN('010', 16, 'le').toNumber(), 4096); + }); }); describe('with Array input', function () { @@ -98,6 +104,7 @@ describe('BN.js/Constructor', function () { }); it('should import/export big endian', function () { + assert.equal(new BN([ 0, 1 ], 16).toString(16), '1'); assert.equal(new BN([ 1, 2, 3 ]).toString(16), '10203'); assert.equal(new BN([ 1, 2, 3, 4 ]).toString(16), '1020304'); assert.equal(new BN([ 1, 2, 3, 4, 5 ]).toString(16), '102030405'); @@ -109,6 +116,7 @@ describe('BN.js/Constructor', function () { }); it('should import little endian', function () { + assert.equal(new BN([ 0, 1 ], 16, 'le').toString(16), '100'); assert.equal(new BN([ 1, 2, 3 ], 10, 'le').toString(16), '30201'); assert.equal(new BN([ 1, 2, 3, 4 ], 10, 'le').toString(16), '4030201'); assert.equal(new BN([ 1, 2, 3, 4, 5 ], 10, 'le').toString(16),