diff --git a/src/userlib/js/src/idl.test.ts b/src/userlib/js/src/idl.test.ts index 7ecb3e4587..8bd658873a 100644 --- a/src/userlib/js/src/idl.test.ts +++ b/src/userlib/js/src/idl.test.ts @@ -60,6 +60,12 @@ test('IDL encoding (int)', () => { test_(IDL.Int, new BigNumber(0), '4449444c00017c00', 'Int'); test_(IDL.Int, new BigNumber(42), '4449444c00017c2a', 'Int'); test_(IDL.Int, new BigNumber(1234567890), '4449444c00017cd285d8cc04', 'Positive Int'); + test_( + IDL.Int, + new BigNumber('60000000000000000'), + '4449444c00017c808098f4e9b5caea00', + 'Positive BigInt', + ); test_(IDL.Int, new BigNumber(-1234567890), '4449444c00017caefaa7b37b', 'Negative Int'); test_(IDL.Opt(IDL.Int), new BigNumber(42), '4449444c016e7c0100012a', 'Nested Int'); testEncode(IDL.Opt(IDL.Int), 42, '4449444c016e7c0100012a', 'Nested Int (number)'); @@ -69,6 +75,12 @@ test('IDL encoding (nat)', () => { // Nat test_(IDL.Nat, new BigNumber(42), '4449444c00017d2a', 'Nat'); test_(IDL.Nat, new BigNumber(1234567890), '4449444c00017dd285d8cc04', 'Positive Nat'); + test_( + IDL.Nat, + new BigNumber('60000000000000000'), + '4449444c00017d808098f4e9b5ca6a', + 'Positive BigInt', + ); expect(() => IDL.encode([IDL.Nat], [-1])).toThrow(/Invalid Nat argument/); testEncode(IDL.Opt(IDL.Int), 42, '4449444c016e7c0100012a', 'Nested Int (number)'); }); diff --git a/src/userlib/js/src/utils/leb128.test.ts b/src/userlib/js/src/utils/leb128.test.ts index e3a245e0b8..88bf6ef46a 100644 --- a/src/userlib/js/src/utils/leb128.test.ts +++ b/src/userlib/js/src/utils/leb128.test.ts @@ -22,6 +22,12 @@ test('leb', () => { expect(lebEncode(new BigNumber('1234567890abcdef1234567890abcdef', 16)).toString('hex')).toBe( 'ef9baf8589cf959a92deb7de8a929eabb424', ); + expect( + lebEncode(new BigNumber('2000000')).toString('hex'), + ).toBe('80897a'); + expect( + lebEncode(new BigNumber('60000000000000000')).toString('hex'), + ).toBe('808098f4e9b5ca6a'); expect(lebDecode(new Pipe(Buffer.from([0]))).toNumber()).toBe(0); expect(lebDecode(new Pipe(Buffer.from([1]))).toNumber()).toBe(1); @@ -35,9 +41,18 @@ test('sleb', () => { expect(slebEncode(-1).toString('hex')).toBe('7f'); expect(slebEncode(-123456).toString('hex')).toBe('c0bb78'); expect(slebEncode(42).toString('hex')).toBe('2a'); + expect(slebEncode(new BigNumber('1234567890abcdef1234567890abcdef', 16)).toString('hex')).toBe( + 'ef9baf8589cf959a92deb7de8a929eabb424', + ); expect( slebEncode(new BigNumber('1234567890abcdef1234567890abcdef', 16).negated()).toString('hex'), ).toBe('91e4d0faf6b0eae5eda1c8a1f5ede1d4cb5b'); + expect( + slebEncode(new BigNumber('2000000')).toString('hex'), + ).toBe('8089fa00'); + expect( + slebEncode(new BigNumber('60000000000000000')).toString('hex'), + ).toBe('808098f4e9b5caea00'); expect(slebDecode(new Pipe(Buffer.from([0x7f]))).toNumber()).toBe(-1); expect(slebDecode(new Pipe(Buffer.from([0xc0, 0xbb, 0x78]))).toNumber()).toBe(-123456); @@ -45,6 +60,9 @@ test('sleb', () => { expect( slebDecode(new Pipe(Buffer.from('91e4d0faf6b0eae5eda1c8a1f5ede1d4cb5b', 'hex'))).toString(16), ).toBe('-1234567890abcdef1234567890abcdef'); + expect( + slebDecode(new Pipe(Buffer.from('808098f4e9b5caea00', 'hex'))).toString(), + ).toBe('60000000000000000'); }); test('IntLE', () => { diff --git a/src/userlib/js/src/utils/leb128.ts b/src/userlib/js/src/utils/leb128.ts index 2e5ffd04b0..e0767f87ab 100644 --- a/src/userlib/js/src/utils/leb128.ts +++ b/src/userlib/js/src/utils/leb128.ts @@ -11,20 +11,16 @@ export function lebEncode(value: number | BigNumber): Buffer { if (value.lt(0)) { throw new Error('Cannot leb encode negative values.'); } - if (value.eq(0)) { - // Clamp to 0. - return Buffer.from([0]); - } const pipe = new Pipe(); - while (value.gt(0)) { + while (true) { const i = value.mod(0x80).toNumber(); value = value.idiv(0x80); - - if (value.gt(0)) { - pipe.write([i | 0x80]); - } else { + if (value.eq(0)) { pipe.write([i]); + break; + } else { + pipe.write([i | 0x80]); } } @@ -49,35 +45,34 @@ export function slebEncode(value: BigNumber | number): Buffer { if (typeof value === 'number') { value = new BigNumber(value); } + value = value.integerValue(); - if (value.gte(0)) { - return lebEncode(value); - } - - value = value - .abs() - .integerValue() - .minus(1); - - // We need to special case 0, as it would return an empty buffer. Since - // we removed 1 above, this is really -1. - if (value.eq(0)) { - return Buffer.from([0x7f]); + const isNeg = value.lt(0); + if (isNeg) { + value = value.abs().minus(1); } - const pipe = new Pipe(); - while (value.gt(0)) { - // We swap the bits here again, and remove 1 to do two's complement. - const i = 0x80 - value.mod(0x80).toNumber() - 1; + while (true) { + const i = getLowerBytes(value); value = value.idiv(0x80); - - if (value.gt(0)) { - pipe.write([i | 0x80]); - } else { + if ((isNeg && value.eq(0) && (i & 0x40) !== 0) || + (!isNeg && value.eq(0) && (i & 0x40) === 0)) { pipe.write([i]); + break; + } else { + pipe.write([i | 0x80]); } } + function getLowerBytes(num: BigNumber): number { + const bytes = num.mod(0x80).toNumber(); + if (isNeg) { + // We swap the bits here again, and remove 1 to do two's complement. + return 0x80 - bytes - 1; + } else { + return bytes; + } + } return pipe.buffer; }