Skip to content

Commit 0fed7c1

Browse files
committed
feat: 🎸 harden tuple capacity estimator codegen
1 parent f0e1d49 commit 0fed7c1

File tree

2 files changed

+89
-1
lines changed

2 files changed

+89
-1
lines changed

src/codegen/capacity/CapacityEstimatorCodegen.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class CapacityEstimatorCodegen extends AbstractCodegen<CompiledCapacityEs
116116
if (tailLength > 0) {
117117
const rr = codegen.var(r.use());
118118
for (let i = 0; i < tailLength; i++)
119-
this.onNode([...path, {r: `${rLen} - ${tailLength - i}`}], new JsExpression(() => /* js */ `${rr}[${rLen} - ${i + 1}]`), _tail![i]);
119+
this.onNode([...path, {r: `${rLen} - ${(tailLength - i)}`}], new JsExpression(() => /* js */ `${rr}[${rLen} - ${(tailLength - i)}]`), _tail![i]);
120120
}
121121
}
122122

src/codegen/capacity/__tests__/CapacityEstimatorCodegenContext.spec.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,94 @@ describe('"arr" type', () => {
150150
const estimator = CapacityEstimatorCodegen.get(type);
151151
expect(estimator([1, 'abc', 'xxxxxxxxx'])).toBe(maxEncodingCapacity([1, 'abc', 'xxxxxxxxx']));
152152
});
153+
154+
test('named head 2-tuple', () => {
155+
const system = new ModuleType();
156+
const type = system.t.Tuple([t.Key('first', t.Const('abc')), t.Key('second', t.Const('xxxxxxxxx'))], t.num);
157+
const estimator = CapacityEstimatorCodegen.get(type);
158+
expect(estimator(['abc', 'xxxxxxxxx', 1])).toBe(maxEncodingCapacity(['abc', 'xxxxxxxxx', 1]));
159+
});
160+
161+
test('mixed head and tail tuple', () => {
162+
const system = new ModuleType();
163+
const type = system.t.Tuple([t.Const('start')], t.str).tail(t.Const('end'));
164+
const estimator = CapacityEstimatorCodegen.get(type);
165+
expect(estimator(['start', 'middle1', 'middle2', 'end'])).toBe(maxEncodingCapacity(['start', 'middle1', 'middle2', 'end']));
166+
});
167+
168+
test('complex named tail tuple', () => {
169+
const system = new ModuleType();
170+
const type = system.t.Array(t.num).tail(
171+
t.Key('status', t.str),
172+
t.Key('timestamp', t.num),
173+
t.Key('metadata', t.bool)
174+
);
175+
const estimator = CapacityEstimatorCodegen.get(type);
176+
expect(estimator([1, 2, 3, 'success', 1234567890, true])).toBe(maxEncodingCapacity([1, 2, 3, 'success', 1234567890, true]));
177+
});
178+
179+
test('empty array with head/tail definition', () => {
180+
const system = new ModuleType();
181+
const type = system.t.Tuple([t.Const('required')], t.str).tail(t.Const('end'));
182+
const estimator = CapacityEstimatorCodegen.get(type);
183+
expect(estimator(['required', 'end'])).toBe(maxEncodingCapacity(['required', 'end']));
184+
});
185+
186+
test('head tuple with different types', () => {
187+
const system = new ModuleType();
188+
const type = system.t.Tuple([
189+
t.Key('id', t.num),
190+
t.Key('name', t.str),
191+
t.Key('active', t.bool)
192+
], t.str);
193+
const estimator = CapacityEstimatorCodegen.get(type);
194+
expect(estimator([42, 'test', true, 'extra1', 'extra2'])).toBe(maxEncodingCapacity([42, 'test', true, 'extra1', 'extra2']));
195+
});
196+
197+
test('tail tuple with different types', () => {
198+
const system = new ModuleType();
199+
const type = system.t.Array(t.str).tail(
200+
t.Key('count', t.num),
201+
t.Key('valid', t.bool)
202+
);
203+
const estimator = CapacityEstimatorCodegen.get(type);
204+
expect(estimator(['item1', 'item2', 'item3', 5, true])).toBe(maxEncodingCapacity(['item1', 'item2', 'item3', 5, true]));
205+
});
206+
207+
test('nested objects in named tuples', () => {
208+
const system = new ModuleType();
209+
const type = system.t.Array(t.Object(t.Key('value', t.num))).tail(
210+
t.Key('summary', t.Object(t.Key('total', t.num), t.Key('average', t.num)))
211+
);
212+
const estimator = CapacityEstimatorCodegen.get(type);
213+
const data = [
214+
{value: 10},
215+
{value: 20},
216+
{total: 30, average: 15} // summary
217+
];
218+
expect(estimator(data)).toBe(maxEncodingCapacity(data));
219+
});
220+
221+
test('single element named tail', () => {
222+
const system = new ModuleType();
223+
const type = system.t.Array(t.num).tail(t.Key('final', t.str));
224+
const estimator = CapacityEstimatorCodegen.get(type);
225+
expect(estimator([1, 2, 3, 'done'])).toBe(maxEncodingCapacity([1, 2, 3, 'done']));
226+
});
227+
228+
test('single element named head', () => {
229+
const system = new ModuleType();
230+
const type = system.t.Tuple([t.Key('header', t.str)], t.num);
231+
const estimator = CapacityEstimatorCodegen.get(type);
232+
expect(estimator(['header', 1, 2, 3])).toBe(maxEncodingCapacity(['header', 1, 2, 3]));
233+
});
234+
235+
test('both head and tail with same type', () => {
236+
const system = new ModuleType();
237+
const type = system.t.Tuple([t.Key('start', t.str)], t.num).tail(t.Key('end', t.str));
238+
const estimator = CapacityEstimatorCodegen.get(type);
239+
expect(estimator(['begin', 1, 2, 3, 'finish'])).toBe(maxEncodingCapacity(['begin', 1, 2, 3, 'finish']));
240+
});
153241
});
154242

155243
describe('"obj" type', () => {

0 commit comments

Comments
 (0)