diff --git a/packages-user/data-common/src/replay/array.test.ts b/packages-user/data-common/src/replay/array.test.ts index b0baf78..5f62e93 100644 --- a/packages-user/data-common/src/replay/array.test.ts +++ b/packages-user/data-common/src/replay/array.test.ts @@ -274,8 +274,8 @@ describe('ReplayArray param codec', () => { expect(firstParamToken(array)).toBe(7); }); - // 疑似 bug:bigint 编码循环缺少按字节右移,多字节 bigint 只能还原最低字节,详见 06-TEST-FINDINGS.md #06-04-2,修复后取消 skip - it.skip('round-trips a multi-byte bigint', () => { + // 验证多字节 bigint 各幅值字节经 type 7 完整读回 + it('round-trips a multi-byte bigint', () => { const array = createArray(); const value = 0x0102030405060708n; array.add(0, [value]); @@ -283,6 +283,36 @@ describe('ReplayArray param codec', () => { expect(array.get(0).params).toEqual([value]); }); + // 负 bigint 使用独立类型码 8,载荷为幅值 |n|,解码后取负(A9) + it('round-trips negative bigint values through the dedicated type', () => { + const negativeCases: readonly bigint[] = [ + -1n, + -128n, + -129n, + -256n, + -65537n + ]; + + for (const value of negativeCases) { + const array = createArray(); + array.add(0, [value]); + expectParamTyped(array.get(0).params[0], value); + expect(firstParamToken(array)).toBe(8); + } + }); + + // 非负 bigint 仍为 type 7,单字节与多字节幅值逐字节不变 + it('round-trips non-negative bigint values through type 7', () => { + const nonNegativeCases: readonly bigint[] = [100n, 0x0102030405060708n]; + + for (const value of nonNegativeCases) { + const array = createArray(); + array.add(0, [value]); + expectParamTyped(array.get(0).params[0], value); + expect(firstParamToken(array)).toBe(7); + } + }); + // 验证超过 int32 范围的非负 int64 参数经 type 4 精确读回 it('round-trips int64 values above the int32 range', () => { const array = createArray(); @@ -320,8 +350,8 @@ describe('ReplayArray param codec', () => { } }); - // 疑似 bug:多字节 bigint 与超 int32 的 int64 混在同一步时同样失真,详见 06-TEST-FINDINGS.md #06-04-1/#06-04-2,修复两个编码与解码缺陷后取消 skip - it.skip('round-trips a heterogeneous step mixing a multi-byte bigint and an int64 value', () => { + // 验证多字节 bigint 与超 int32 的 int64 混在同一步时逐参数精确读回 + it('round-trips a heterogeneous step mixing a multi-byte bigint and an int64 value', () => { const array = createArray(); array.add(0, [true, 0x0102030405060708n, 2147483648, 'x']); diff --git a/packages-user/data-common/src/replay/array.ts b/packages-user/data-common/src/replay/array.ts index 9a4dab5..ce4947d 100644 --- a/packages-user/data-common/src/replay/array.ts +++ b/packages-user/data-common/src/replay/array.ts @@ -259,23 +259,20 @@ export class ReplayArray implements IReplayArray { byteLength: byte }; } else if (typeof param === 'bigint') { - // 7 - bigint + // 7 - 非负 bigint / 8 - 负 bigint,均以幅值按字节写入 const wall = 2n ** 2047n; if (param > wall - 1n || param < -wall) { logger.warn(152); } - const bit = param.toString(2); + const magnitude = param < 0n ? -param : param; + const bit = magnitude.toString(2); const length = Math.ceil(bit.length / 8); const arr = new Uint8Array(length); - let total = 0n; for (let i = 0; i < length; i++) { - const base = param - total; - const remain = base % 256n; - total += remain << (BigInt(i) * 8n); - arr[i] = Number(remain); + arr[i] = Number((magnitude >> (8n * BigInt(i))) & 0xffn); } return { - paramType: 7, + paramType: param < 0n ? 8 : 7, paramValue: arr, byteLength: arr.length + 2 }; @@ -380,8 +377,8 @@ export class ReplayArray implements IReplayArray { } else if (param.paramType === 6) { // 6 - float this.paramView.setFloat64(index + 1, num); - } else if (param.paramType === 7) { - // 7 - bigint + } else if (param.paramType === 7 || param.paramType === 8) { + // 7 - 非负 bigint / 8 - 负 bigint,写入幅值字节 this.paramArray[index + 1] = arr.length; this.paramArray.set(arr, index + 2); } else if (param.paramType === 9) { @@ -636,16 +633,16 @@ export class ReplayArray implements IReplayArray { // 6 - float byte = 9; value = this.paramView.getFloat64(startIndex + 1); - } else if (type === 7) { - // 7 - bigint - const length = this.paramView.getInt8(startIndex + 1); + } else if (type === 7 || type === 8) { + // 7 - 非负 bigint / 8 - 负 bigint,读回幅值后按类型码还原符号 + const length = this.paramView.getUint8(startIndex + 1); let base = 0n; for (let i = 0; i < length; i++) { - const num = this.paramView.getInt8(startIndex + 2 + i); + const num = this.paramView.getUint8(startIndex + 2 + i); base += BigInt(num) << (8n * BigInt(i)); } byte = length + 2; - value = base; + value = type === 8 ? -base : base; } else if (type === 9) { // 9 - string const length = this.paramView.getInt32(startIndex + 1);