mirror of
https://github.com/motajs/template.git
synced 2026-09-21 06:50:18 +08:00
fix(07-03): #06-04-1 round-trip negative int64 and renumber param type codes
- Correct the int64 decode multiplier to 2147483648 - Encode negative int64 as dedicated type 5 with magnitude payload - Shift float/bigint/long-string/short-string type codes to 6/7/9/length+9 - Un-skip the int64 round-trip case and add negative int64 focused cases
This commit is contained in:
parent
c7e5a643c2
commit
528b8c91a5
@ -79,7 +79,7 @@ function expectStepTyped(
|
||||
expect(step.index).toBe(index);
|
||||
}
|
||||
|
||||
// 整数与浮点参数用例:值 → 期望参数类型 token(int64 见 #06-04-1 跳过)
|
||||
// 整数与浮点参数用例:值 → 期望参数类型 token(int64 见下方专门用例)
|
||||
const paramCases: ReadonlyArray<readonly [ReplayParamValue, number]> = [
|
||||
[-128, 1],
|
||||
[127, 1],
|
||||
@ -91,8 +91,8 @@ const paramCases: ReadonlyArray<readonly [ReplayParamValue, number]> = [
|
||||
[-32769, 3],
|
||||
[-2147483648, 3],
|
||||
[2147483647, 3],
|
||||
[1.5, 5],
|
||||
[-1.5, 5]
|
||||
[1.5, 6],
|
||||
[-1.5, 6]
|
||||
];
|
||||
|
||||
// 异质命令序列:参数个数与类型各不相同(含 boolean、多位宽整数、float、string、bigint)
|
||||
@ -271,7 +271,7 @@ describe('ReplayArray param codec', () => {
|
||||
array.add(0, [100n]);
|
||||
|
||||
expect(array.get(0).params).toEqual([100n]);
|
||||
expect(firstParamToken(array)).toBe(6);
|
||||
expect(firstParamToken(array)).toBe(7);
|
||||
});
|
||||
|
||||
// 疑似 bug:bigint 编码循环缺少按字节右移,多字节 bigint 只能还原最低字节,详见 06-TEST-FINDINGS.md #06-04-2,修复后取消 skip
|
||||
@ -283,13 +283,43 @@ describe('ReplayArray param codec', () => {
|
||||
expect(array.get(0).params).toEqual([value]);
|
||||
});
|
||||
|
||||
// 疑似 bug:int64 解码乘数误用 2147483647,导致 int64 参数无法按写入值读回,详见 06-TEST-FINDINGS.md #06-04-1,修复后取消 skip
|
||||
it.skip('round-trips int64 values above the int32 range', () => {
|
||||
// 验证超过 int32 范围的非负 int64 参数经 type 4 精确读回
|
||||
it('round-trips int64 values above the int32 range', () => {
|
||||
const array = createArray();
|
||||
array.add(0, [2147483648]);
|
||||
expect(array.get(0).params).toEqual([2147483648]);
|
||||
});
|
||||
|
||||
// 负 int64 使用独立类型码 5,载荷为幅值 |n|,解码后取负(A8);小负值仍走更窄的位宽类型
|
||||
it('round-trips negative int64 values through the dedicated type', () => {
|
||||
const negativeCases: ReadonlyArray<readonly [number, number]> = [
|
||||
[-1, 1],
|
||||
[-2147483649, 5],
|
||||
[-4294967297, 5]
|
||||
];
|
||||
|
||||
for (const [value, token] of negativeCases) {
|
||||
const array = createArray();
|
||||
array.add(0, [value]);
|
||||
expectParamTyped(array.get(0).params[0], value);
|
||||
expect(firstParamToken(array)).toBe(token);
|
||||
}
|
||||
});
|
||||
|
||||
// 非负 int64 仍为 type 4,上界 2^53 - 1 精确读回,幅值编码逐位不变
|
||||
it('round-trips non-negative int64 values through type 4', () => {
|
||||
const nonNegativeCases: readonly number[] = [
|
||||
2147483648, 9007199254740991
|
||||
];
|
||||
|
||||
for (const value of nonNegativeCases) {
|
||||
const array = createArray();
|
||||
array.add(0, [value]);
|
||||
expectParamTyped(array.get(0).params[0], value);
|
||||
expect(firstParamToken(array)).toBe(4);
|
||||
}
|
||||
});
|
||||
|
||||
// 疑似 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', () => {
|
||||
const array = createArray();
|
||||
@ -303,32 +333,32 @@ describe('ReplayArray param codec', () => {
|
||||
]);
|
||||
});
|
||||
|
||||
// 验证短字符串参数使用内联类型 token 并读回一致
|
||||
// 验证短字符串参数使用内联类型 token(长度 + 9)并读回一致
|
||||
it('round-trips a short string with an inline type token', () => {
|
||||
const array = createArray();
|
||||
array.add(0, ['hi']);
|
||||
|
||||
expect(array.get(0).params).toEqual(['hi']);
|
||||
expect(firstParamToken(array)).toBe(9);
|
||||
expect(firstParamToken(array)).toBe(11);
|
||||
});
|
||||
|
||||
// 验证超过内联长度的字符串参数使用 type 7 并读回一致
|
||||
// 验证超过内联长度的字符串参数使用 type 9 并读回一致
|
||||
it('round-trips a long string through the length-prefixed type', () => {
|
||||
const array = createArray();
|
||||
const value = 'a'.repeat(300);
|
||||
array.add(0, [value]);
|
||||
|
||||
expect(array.get(0).params).toEqual([value]);
|
||||
expect(firstParamToken(array)).toBe(7);
|
||||
expect(firstParamToken(array)).toBe(9);
|
||||
});
|
||||
|
||||
// 验证空字符串参数回退到 type 7 并读回为空串
|
||||
// 验证空字符串参数回退到 type 9 并读回为空串
|
||||
it('round-trips an empty string', () => {
|
||||
const array = createArray();
|
||||
array.add(0, ['']);
|
||||
|
||||
expect(array.get(0).params).toEqual(['']);
|
||||
expect(firstParamToken(array)).toBe(7);
|
||||
expect(firstParamToken(array)).toBe(9);
|
||||
});
|
||||
|
||||
// 验证单条录像步的多个不同类型参数按顺序完整读回
|
||||
|
||||
@ -16,11 +16,13 @@ interface INormalizedParam {
|
||||
* - 1: int8
|
||||
* - 2: int16
|
||||
* - 3: int32
|
||||
* - 4: int64
|
||||
* - 5: float
|
||||
* - 6: bigint
|
||||
* - 7: string
|
||||
* - 8 ~ 255: n - 7 长度的字符串
|
||||
* - 4: 非负 int64,载荷为幅值
|
||||
* - 5: 负 int64,载荷为幅值
|
||||
* - 6: float
|
||||
* - 7: 非负 bigint,载荷为幅值
|
||||
* - 8: 负 bigint,载荷为幅值
|
||||
* - 9: string,带 int32 长度前缀
|
||||
* - 10 ~ 255: n - 9 长度的字符串
|
||||
*/
|
||||
readonly paramType: number;
|
||||
|
||||
@ -237,14 +239,18 @@ export class ReplayArray implements IReplayArray {
|
||||
// 3 - int32
|
||||
type = 3;
|
||||
byte = 5;
|
||||
} else {
|
||||
// 4 - int64
|
||||
} else if (param >= 2147483648) {
|
||||
// 4 - 非负 int64
|
||||
type = 4;
|
||||
byte = 9;
|
||||
} else {
|
||||
// 5 - 负 int64,载荷为幅值
|
||||
type = 5;
|
||||
byte = 9;
|
||||
}
|
||||
} else {
|
||||
// 5 - float
|
||||
type = 5;
|
||||
// 6 - float
|
||||
type = 6;
|
||||
byte = 9;
|
||||
}
|
||||
return {
|
||||
@ -253,7 +259,7 @@ export class ReplayArray implements IReplayArray {
|
||||
byteLength: byte
|
||||
};
|
||||
} else if (typeof param === 'bigint') {
|
||||
// 6 - bigint
|
||||
// 7 - bigint
|
||||
const wall = 2n ** 2047n;
|
||||
if (param > wall - 1n || param < -wall) {
|
||||
logger.warn(152);
|
||||
@ -269,23 +275,23 @@ export class ReplayArray implements IReplayArray {
|
||||
arr[i] = Number(remain);
|
||||
}
|
||||
return {
|
||||
paramType: 6,
|
||||
paramType: 7,
|
||||
paramValue: arr,
|
||||
byteLength: arr.length + 2
|
||||
};
|
||||
} else if (typeof param === 'string') {
|
||||
const arr = this.textEncoder.encode(param);
|
||||
if (arr.length > 0 && arr.length <= 248) {
|
||||
// 8 ~ 255 - string
|
||||
if (arr.length > 0 && arr.length <= 246) {
|
||||
// 10 ~ 255 - string
|
||||
return {
|
||||
paramType: arr.length + 7,
|
||||
paramType: arr.length + 9,
|
||||
paramValue: arr,
|
||||
byteLength: arr.length + 1
|
||||
};
|
||||
} else {
|
||||
// 7 - string
|
||||
// 9 - string
|
||||
return {
|
||||
paramType: 7,
|
||||
paramType: 9,
|
||||
paramValue: arr,
|
||||
byteLength: arr.length + 5
|
||||
};
|
||||
@ -364,25 +370,26 @@ export class ReplayArray implements IReplayArray {
|
||||
} else if (param.paramType === 3) {
|
||||
// 3 - int32
|
||||
this.paramView.setInt32(index + 1, num);
|
||||
} else if (param.paramType === 4) {
|
||||
// 4 - int64
|
||||
const high = Math.floor(num / 2147483648);
|
||||
const low = num % 2147483648;
|
||||
} else if (param.paramType === 4 || param.paramType === 5) {
|
||||
// 4 - 非负 int64 / 5 - 负 int64,均以幅值拆分高低 32 位
|
||||
const magnitude = num < 0 ? -num : num;
|
||||
const high = Math.floor(magnitude / 2147483648);
|
||||
const low = magnitude - high * 2147483648;
|
||||
this.paramView.setInt32(index + 1, low);
|
||||
this.paramView.setInt32(index + 5, high);
|
||||
} else if (param.paramType === 5) {
|
||||
// 5 - float
|
||||
this.paramView.setFloat64(index + 1, num);
|
||||
} else if (param.paramType === 6) {
|
||||
// 6 - bigint
|
||||
// 6 - float
|
||||
this.paramView.setFloat64(index + 1, num);
|
||||
} else if (param.paramType === 7) {
|
||||
// 7 - bigint
|
||||
this.paramArray[index + 1] = arr.length;
|
||||
this.paramArray.set(arr, index + 2);
|
||||
} else if (param.paramType === 7) {
|
||||
// 7 - string
|
||||
} else if (param.paramType === 9) {
|
||||
// 9 - string
|
||||
this.paramView.setInt32(index + 1, arr.length);
|
||||
this.paramArray.set(arr, index + 5);
|
||||
} else {
|
||||
// 8 ~ 256 - string
|
||||
// 10 ~ 255 - string
|
||||
this.paramArray.set(arr, index + 1);
|
||||
}
|
||||
index += param.byteLength;
|
||||
@ -614,17 +621,23 @@ export class ReplayArray implements IReplayArray {
|
||||
byte = 5;
|
||||
value = this.paramView.getInt32(startIndex + 1);
|
||||
} else if (type === 4) {
|
||||
// 4 - int64
|
||||
// 4 - 非负 int64
|
||||
const low = this.paramView.getInt32(startIndex + 1);
|
||||
const high = this.paramView.getInt32(startIndex + 5);
|
||||
byte = 9;
|
||||
value = low + high * 2147483647;
|
||||
value = low + high * 2147483648;
|
||||
} else if (type === 5) {
|
||||
// 5 - float
|
||||
// 5 - 负 int64,读回幅值后取负
|
||||
const low = this.paramView.getInt32(startIndex + 1);
|
||||
const high = this.paramView.getInt32(startIndex + 5);
|
||||
byte = 9;
|
||||
value = -(low + high * 2147483648);
|
||||
} else if (type === 6) {
|
||||
// 6 - float
|
||||
byte = 9;
|
||||
value = this.paramView.getFloat64(startIndex + 1);
|
||||
} else if (type === 6) {
|
||||
// 6 - bigint
|
||||
} else if (type === 7) {
|
||||
// 7 - bigint
|
||||
const length = this.paramView.getInt8(startIndex + 1);
|
||||
let base = 0n;
|
||||
for (let i = 0; i < length; i++) {
|
||||
@ -633,16 +646,16 @@ export class ReplayArray implements IReplayArray {
|
||||
}
|
||||
byte = length + 2;
|
||||
value = base;
|
||||
} else if (type === 7) {
|
||||
// 7 - string
|
||||
} else if (type === 9) {
|
||||
// 9 - string
|
||||
const length = this.paramView.getInt32(startIndex + 1);
|
||||
const endIndex = startIndex + 5 + length;
|
||||
const arr = this.paramArray.slice(startIndex + 5, endIndex);
|
||||
byte = length + 5;
|
||||
value = this.textDecoder.decode(arr);
|
||||
} else {
|
||||
// 8 ~ 255 - string
|
||||
const length = type - 7;
|
||||
// 10 ~ 255 - string
|
||||
const length = type - 9;
|
||||
const endIndex = startIndex + 1 + length;
|
||||
const arr = this.paramArray.slice(startIndex + 1, endIndex);
|
||||
byte = length + 1;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user