Skip to content

Commit

Permalink
Fix LE constructor for HEX (#265)
Browse files Browse the repository at this point in the history
  • Loading branch information
fanatid committed Feb 24, 2021
1 parent 42a33cb commit 6196232
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 51 deletions.
111 changes: 60 additions & 51 deletions lib/bn.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -183,57 +179,68 @@
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);
for (var i = 0; i < this.length; i++) {
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();
};

Expand Down Expand Up @@ -304,6 +311,8 @@
this._iaddn(word);
}
}

this.strip();
};

BN.prototype.copy = function copy (dest) {
Expand Down
8 changes: 8 additions & 0 deletions test/constructor-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () {
Expand All @@ -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');
Expand All @@ -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),
Expand Down

0 comments on commit 6196232

Please sign in to comment.