Skip to content

Commit 7170c09

Browse files
authored
fix three bugs in discriminate and clean up (#567)
1 parent e48daf5 commit 7170c09

File tree

2 files changed

+39
-71
lines changed

2 files changed

+39
-71
lines changed

core/base/__tests__/layout.ts

+22-56
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,8 @@ import { describe, expect, it } from "@jest/globals";
22

33
import {
44
Layout,
5-
LayoutToType,
6-
RoArray,
75
addFixedValues,
86
bitsetItem,
9-
column,
107
deserializeLayout,
118
layoutDiscriminator,
129
serializeLayout,
@@ -383,63 +380,32 @@ describe("Layout tests", function () {
383380
expect(discriminator(Uint8Array.from([0, 0]))).toEqual([2]);
384381
});
385382

386-
});
387-
});
388-
389-
describe("Switch Layout Size Tests", () => {
390-
it("Can discriminate a set of layouts", () => {
391-
const layouta = [
392-
{
393-
name: "payload",
394-
binary: "bytes",
395-
lengthSize: 2,
396-
layout: [
397-
{
398-
name: "payload",
399-
binary: "switch",
400-
idSize: 1,
401-
layouts: [
402-
[[0, "Direct"], []],
403-
[[1, "Payload"], [{ name: "data", binary: "bytes", lengthSize: 4 }]],
404-
],
405-
},
383+
it("can discriminate a switch layout with the same byte value in all cases", () => {
384+
const switchLayout = {
385+
binary: "switch",
386+
idSize: 1,
387+
layouts: [
388+
[[0, "first"], [
389+
{name: "fixed1", binary: "uint", size: 1, custom: 32 },
390+
{name: "variable1", binary: "bytes", lengthSize: 1 },
391+
]],
392+
[[1, "second"], [
393+
{name: "fixed2", binary: "uint", size: 1, custom: 32 },
394+
{name: "variable2", binary: "bytes", lengthSize: 2 },
395+
]],
406396
],
407-
},
408-
] as const satisfies Layout;
397+
} as const satisfies Layout;
409398

410-
const layoutb = [
411-
{
412-
name: "payload",
399+
const secondByteLayout = {
413400
binary: "bytes",
414-
lengthSize: 3,
415-
layout: [
416-
{
417-
name: "payload",
418-
binary: "switch",
419-
idSize: 1,
420-
layouts: [
421-
[[0, "Nothing"], []],
422-
[[1, "Data"], [{ name: "data", binary: "bytes", lengthSize: 4 }]],
423-
],
424-
},
425-
],
426-
},
427-
] as const satisfies Layout;
401+
custom: new Uint8Array([1, 33, 0]),
402+
} as const satisfies Layout;
428403

429-
const messageLayouts = [
430-
["Layout", layouta],
431-
["LayoutB", layoutb],
432-
] as const satisfies RoArray<[string, Layout]>;
433-
const messageDiscriminator = layoutDiscriminator(column(messageLayouts, 1));
404+
const messageDiscriminator = layoutDiscriminator([switchLayout, secondByteLayout]);
434405

435-
const b: LayoutToType<typeof layoutb> = {
436-
payload: {
437-
payload: { id: "Data", data: new Uint8Array([0, 0, 0, 0]) },
438-
},
439-
};
440-
441-
const data = serializeLayout(layoutb, b);
442-
const idx = messageDiscriminator(data);
443-
expect(idx).toEqual(1);
406+
expect(messageDiscriminator(new Uint8Array([0, 32, 1, 128]))).toEqual(0);
407+
expect(messageDiscriminator(new Uint8Array([0, 33, 0]))).toEqual(1);
408+
expect(messageDiscriminator(new Uint8Array([0, 34, 0]))).toEqual(null);
409+
});
444410
});
445411
});

core/base/src/utils/layout/discriminate.ts

+17-15
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ function count(candidates: Candidates) {
3939
}
4040

4141
const lengthSizeMax = (lengthSize: number) =>
42-
lengthSize > 0 ? (1 << (8 * lengthSize)) - 1 : Infinity;
42+
lengthSize > 0 ? 2**(8 * lengthSize) - 1 : Infinity;
4343

4444
function layoutItemMeta(
4545
item: LayoutItem,
@@ -157,13 +157,13 @@ function layoutItemMeta(
157157
serializeNum(idVal, idSize, cursor, idEndianness);
158158
caseFixedBytes[caseIndex]!.push([0, cursor.bytes]);
159159
}
160-
const ret = createLayoutMeta(layout, offset ? idSize : null, caseFixedBytes[caseIndex]!);
160+
const ret = createLayoutMeta(layout, offset !== null ? idSize : null, caseFixedBytes[caseIndex]!);
161161
return [ret[0] + idSize, ret[1] + idSize] as Bounds;
162162
});
163163

164-
if (offset !== null)
165-
//find bytes that have the same value across all cases (turning this into a lambda to enable
166-
// early return from inner loops)
164+
if (offset !== null && caseFixedBytes.every(fbs => fbs.length > 0))
165+
//find bytes that have the same value across all cases
166+
// (it's a lambda to enable early return from inner loops)
167167
(() => {
168168
//constrain search to the minimum length of all cases
169169
const minLen = Math.min(
@@ -172,34 +172,36 @@ function layoutItemMeta(
172172
//keep track of the current index in each case's fixed bytes array
173173
const itIndexes = caseFixedBytes.map(_ => 0);
174174

175-
let caseIndex = 0;
176175
for (let bytePos = 0; bytePos < minLen;) {
177176
let byteVal = null;
177+
let caseIndex = 0;
178178
while (caseIndex < caseFixedBytes.length) {
179-
let curItIndex = itIndexes[caseIndex];
180-
const curFixedBytes = caseFixedBytes[caseIndex];
181-
const [curOffset, curSerialized] = curFixedBytes![curItIndex!]!;
179+
let curItIndex = itIndexes[caseIndex]!;
180+
const curFixedBytes = caseFixedBytes[caseIndex]!;
181+
const [curOffset, curSerialized] = curFixedBytes[curItIndex]!;
182182
if (curOffset + curSerialized.length <= bytePos) {
183183
//no fixed byte at this position in this case
184-
++curItIndex!;
184+
++curItIndex;
185185

186-
if (curItIndex === curFixedBytes!.length)
186+
if (curItIndex === curFixedBytes.length)
187187
return; //we have exhausted all fixed bytes in at least one case
188188

189-
itIndexes[caseIndex] = curItIndex!;
189+
itIndexes[caseIndex] = curItIndex;
190190
//jump to the next possible bytePos given the fixed bytes of the current case index
191-
bytePos = curFixedBytes![curItIndex!]![0];
191+
bytePos = curFixedBytes[curItIndex]![0];
192192
break;
193193
}
194194

195195
const curByteVal = curSerialized[bytePos - curOffset];
196196
if (byteVal === null)
197197
byteVal = curByteVal;
198198

199-
if (curByteVal !== byteVal)
199+
if (curByteVal !== byteVal) {
200+
++bytePos;
200201
break;
202+
}
201203

202-
caseIndex++;
204+
++caseIndex;
203205
}
204206

205207
//only if we made it through all cases without breaking do we have a fixed byte

0 commit comments

Comments
 (0)