blob: a43a44aa139a3b6aad0f69fcc6f6e8f9c980deaf [file] [log] [blame] [edit]
// Protocol Buffers - Google's data interchange format
// Copyright 2008 Google Inc. All rights reserved.
// https://protobuf.dev/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/**
* @fileoverview Test cases for Int64-manipulation functions.
*
* Test suite is written using Jasmine -- see http://jasmine.github.io/
*
* @author [email protected] (Chris Fallin)
*/
goog.require('jspb.arith.Int64');
goog.require('jspb.arith.UInt64');
const Int64 = goog.module.get('jspb.arith').Int64;
const UInt64 = goog.module.get('jspb.arith').UInt64;
describe('binaryArithTest', () => {
describe('UInt64', () => {
const /** ReadonlyArray<[number, number, string]> */ testData = Object.freeze([
[0x0, 0x0, '0'],
[0xffffffff, 0xffffffff, '18446744073709551615'],
[0xffffffff, 0x00000000, '4294967295'],
[0x00000000, 0xffffffff, '18446744069414584320'],
[0x000f4240, 0x00000000, '1000000'],
[0x000f423f, 0x00000000, '999999'],
[0xd4a51000, 0x0000000e8, '1000000000000'],
[0xd4a50fff, 0x0000000e8, '999999999999'],
[0x5e84c935, 0xcae33d0e, '14619595947299359029'],
[0x62b3b8b8, 0x93480544, '10612738313170434232'],
[0x319bfb13, 0xc01c4172, '13843011313344445203'],
[0x5b8a65fb, 0xa5885b31, '11927883880638080507'],
[0x6bdb80f1, 0xb0d1b16b, '12741159895737008369'],
[0x4b82b442, 0x2e0d8c97, '3318463081876730946'],
[0x780d5208, 0x7d76752c, '9040542135845999112'],
[0x2e46800f, 0x0993778d, '690026616168284175'],
[0xf00a7e32, 0xcd8e3931, '14811839111111540274'],
[0x1baeccd6, 0x923048c4, '10533999535534820566'],
[0x03669d29, 0xbff3ab72, '13831587386756603177'],
[0x2526073e, 0x01affc81, '121593346566522686'],
[0xc24244e0, 0xd7f40d0e, '15561076969511732448'],
[0xc56a341e, 0xa68b66a7, '12000798502816461854'],
[0x8738d64d, 0xbfe78604, '13828168534871037517'],
[0x5baff03b, 0xd7572aea, '15516918227177304123'],
[0x4a843d8a, 0x864e132b, '9677693725920476554'],
[0x25b4e94d, 0x22b54dc6, '2500990681505655117'],
[0x6bbe664b, 0x55a5cc0e, '6171563226690381387'],
[0xee916c81, 0xb00aabb3, '12685140089732426881'],
]);
// If BigInt is available, verify our test data with that.
if (typeof BigInt !== 'undefined') {
it('is tested with valid test data as verified with BigInt', () => {
for (const testCase of testData) {
const big = (BigInt(testCase[1]) << BigInt(32)) + BigInt(testCase[0]);
expect(big.toString()).toEqual(testCase[2]);
}
});
describe('fromBigInt', () => {
it(`parses testData`, () => {
for (const testCase of testData) {
const roundtrip = UInt64.fromBigInt(BigInt(testCase[2]));
expect(roundtrip.lo).withContext(testCase[2]).toEqual(testCase[0]);
expect(roundtrip.hi).withContext(testCase[2]).toEqual(testCase[1]);
}
});
});
}
describe('toDecimalString', () => {
it(`serializes testData`, () => {
for (const testCase of testData) {
const a = new UInt64(testCase[0], testCase[1]);
const actualString = a.toDecimalString();
expect(actualString).withContext(testCase[2]).toEqual(testCase[2]);
}
});
});
describe('fromString', () => {
it(`parses testData`, () => {
for (const testCase of testData) {
const roundtrip = UInt64.fromString(testCase[2]);
expect(roundtrip.lo).withContext(testCase[2]).toEqual(testCase[0]);
expect(roundtrip.hi).withContext(testCase[2]).toEqual(testCase[1]);
}
});
it('parses empty string as zero', () => {
expect(UInt64.fromString('')).toEqual(UInt64.fromString('0'));
});
it('does not parse non-decimal strings', () => {
expect(UInt64.fromString('0x123')).toBeNull();
expect(UInt64.fromString(' 123')).toBeNull();
});
it('tolerates extra leading zeros', () => {
expect(UInt64.fromString('0018446744073709551615')).toEqual(
UInt64.fromString('18446744073709551615'),
);
});
it('truncates values that are too big', () => {
// 0x1_FFFF_FFFF_FFFF_FFFF = 36893488147419103231
// 0xFFFF_FFFF_FFFF_FFFF = 18446744073709551615
expect(UInt64.fromString('36893488147419103231')).toEqual(
UInt64.fromString('18446744073709551615'),
);
});
});
describe('negateInTwosComplement', () => {
if (typeof BigInt !== 'undefined') {
it('produces correct values', () => {
for (const testCase of testData) {
if (testCase[0] === 0 && testCase[1] === 0) continue;
const result = new UInt64(
testCase[0],
testCase[1],
).negateInTwosComplement();
const resultBigInt =
(BigInt(result.hi) << BigInt(32)) + BigInt(result.lo);
const expected =
(BigInt(1) << BigInt(64)) -
((BigInt(testCase[1]) << BigInt(32)) + BigInt(testCase[0]));
expect(resultBigInt).toEqual(expected);
}
});
}
it('round-trips', () => {
for (const testCase of testData) {
expect(
new UInt64(testCase[0], testCase[1])
.negateInTwosComplement()
.negateInTwosComplement()
.toDecimalString(),
).toBe(testCase[2]);
}
});
});
});
describe('Int64', () => {
const /** ReadonlyArray<[number, number, string]> */ testData = Object.freeze([
[0x0, 0x0, '0'],
[0xffffffff, 0xffffffff, '-1'],
[0x00000000, 0x80000000, '-9223372036854775808'],
[0xffffffff, 0x3fffffff, '4611686018427387903'],
[0xffffffff, 0x00000000, '4294967295'],
[0x00000000, 0xffffffff, '-4294967296'],
[0x000f4240, 0x00000000, '1000000'],
[0xd4a51000, 0x0000000e8, '1000000000000'],
[0xc5bb087e, 0x931814a6, '-7847499644178593666'],
[0xb58a5643, 0x3458a55b, '3771946501229139523'],
[0x7113da74, 0x27de6fcf, '2872856549054995060'],
[0x9d22f8c8, 0xafc92384, '-5780049594274350904'],
[0x48c45eb1, 0x2ef59f66, '3383785956695105201'],
[0x4f0a03e2, 0x294269f0, '2973055184857072610'],
[0xd4e95c3a, 0xca29805e, '-3879428459215627206'],
[0x934049d7, 0x3fb24a16, '4589812431064156631'],
[0xd272e654, 0x75bd7dc4, '8484075557333689940'],
[0x21f03f77, 0xeec5322, '1075325817098092407'],
[0x12158d26, 0xc3ad6da7, '-4346697501012292314'],
[0x7247925d, 0x22895b1f, '2488620459718316637'],
[0xb8d3e7a0, 0x54d48381, '6112655187423520672'],
[0xbf4836f8, 0xcd45d7c6, '-3655278273928612104'],
[0xf853f23c, 0x2fba545b, '3439154019435803196'],
[0xc004fc2d, 0xdef52fa, '1004112478843763757'],
[0x394e4b63, 0xa4937731, '-6587790776614368413'],
[0x596ed01a, 0x09382394, '664320065099714586'],
[0xe3244770, 0x42106251, '4760412909973292912'],
[0x92c2b290, 0x9233453d, '-7911903989602274672'],
]);
// If BigInt is available, verify our test data with that.
if (typeof BigInt !== 'undefined') {
it('is tested with valid test data as verified with BigInt', () => {
for (const testCase of testData) {
let big = (BigInt(testCase[1]) << BigInt(32)) + BigInt(testCase[0]);
if (testCase[2][0] === '-') {
big -= BigInt(2 ** 64);
}
expect(big.toString()).toEqual(testCase[2]);
}
});
describe('fromBigInt', () => {
it(`parses testData`, () => {
for (const testCase of testData) {
const roundtrip = Int64.fromBigInt(BigInt(testCase[2]));
expect(roundtrip.lo).withContext(testCase[2]).toEqual(testCase[0]);
expect(roundtrip.hi).withContext(testCase[2]).toEqual(testCase[1]);
}
});
});
}
describe('toDecimalString', () => {
it(`serializes testData`, () => {
for (const testCase of testData) {
const a = new Int64(testCase[0], testCase[1]);
const actualString = a.toDecimalString();
expect(actualString).withContext(testCase[2]).toEqual(testCase[2]);
}
});
});
describe('fromString', () => {
it(`parses testData`, () => {
for (const testCase of testData) {
const roundtrip = Int64.fromString(testCase[2]);
expect(roundtrip.lo).withContext(testCase[2]).toEqual(testCase[0]);
expect(roundtrip.hi).withContext(testCase[2]).toEqual(testCase[1]);
}
});
it('parses empty string as zero', () => {
expect(Int64.fromString('')).toEqual(Int64.fromString('0'));
});
it('parses -0 string as zero', () => {
expect(Int64.fromString('-0')).toEqual(Int64.fromString('0'));
});
it('parses positive values that overflow into the negative space', () => {
expect(
Int64.fromString('9223372036854775808')?.toDecimalString(),
).toEqual('-9223372036854775808');
});
it('truncates values that are too big', () => {
// 0x1_FFFF_FFFF_FFFF_FFFF = 36893488147419103231
// 0xFFFF_FFFF_FFFF_FFFF = 18446744073709551615
expect(Int64.fromString('36893488147419103231')).toEqual(
Int64.fromString('18446744073709551615'),
);
});
it('does not parse non-decimal strings', () => {
expect(Int64.fromString('0x123')).toBeNull();
expect(Int64.fromString(' 123')).toBeNull();
});
});
});
});