Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/structured/TemplateJson.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {clone} from '../util';
import * as templates from './templates';
import type {
ArrayTemplate,
BinTemplate,
BooleanTemplate,
FloatTemplate,
IntegerTemplate,
Expand Down Expand Up @@ -70,6 +71,8 @@ export class TemplateJson {
return this.generateFloat(template as FloatTemplate);
case 'bool':
return this.generateBoolean(template as BooleanTemplate);
case 'bin':
return this.generateBin(template as BinTemplate);
case 'nil':
return null;
case 'lit':
Expand Down Expand Up @@ -153,6 +156,16 @@ export class TemplateJson {
return value !== undefined ? value : Math.random() < 0.5;
}

protected generateBin(template: BinTemplate): Uint8Array {
const [, min = 0, max = 5, omin = 0, omax = 255] = template;
const length = this.minmax(min, max);
const result = new Uint8Array(length);
for (let i = 0; i < length; i++) {
result[i] = int(omin, omax);
}
return result;
}

protected generateLiteral(template: LiteralTemplate): unknown {
return clone(template[1]);
}
Expand Down
103 changes: 103 additions & 0 deletions src/structured/__tests__/TemplateJson.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,66 @@ describe('TemplateJson', () => {
});
});

describe('bin', () => {
test('uses default binary schema, if not provided', () => {
resetMathRandom();
const bin = TemplateJson.gen('bin');
expect(bin instanceof Uint8Array).toBe(true);
expect((bin as Uint8Array).length).toBeGreaterThanOrEqual(0);
expect((bin as Uint8Array).length).toBeLessThanOrEqual(5);
});

test('can specify length range', () => {
resetMathRandom();
const bin = TemplateJson.gen(['bin', 2, 4]) as Uint8Array;
expect(bin instanceof Uint8Array).toBe(true);
expect(bin.length).toBeGreaterThanOrEqual(2);
expect(bin.length).toBeLessThanOrEqual(4);
});

test('can specify octet value range', () => {
resetMathRandom();
const bin = TemplateJson.gen(['bin', 5, 5, 100, 150]) as Uint8Array;
expect(bin instanceof Uint8Array).toBe(true);
expect(bin.length).toBe(5);
for (let i = 0; i < bin.length; i++) {
expect(bin[i]).toBeGreaterThanOrEqual(100);
expect(bin[i]).toBeLessThanOrEqual(150);
}
});

test('handles edge cases', () => {
// Empty array
const empty = TemplateJson.gen(['bin', 0, 0]) as Uint8Array;
expect(empty instanceof Uint8Array).toBe(true);
expect(empty.length).toBe(0);

// Single byte with fixed value range
resetMathRandom();
const single = TemplateJson.gen(['bin', 1, 1, 42, 42]) as Uint8Array;
expect(single instanceof Uint8Array).toBe(true);
expect(single.length).toBe(1);
expect(single[0]).toBe(42);
});

test('uses default octet range when not specified', () => {
resetMathRandom();
const bin = TemplateJson.gen(['bin', 3, 3]) as Uint8Array;
expect(bin instanceof Uint8Array).toBe(true);
expect(bin.length).toBe(3);
for (let i = 0; i < bin.length; i++) {
expect(bin[i]).toBeGreaterThanOrEqual(0);
expect(bin[i]).toBeLessThanOrEqual(255);
}
});

test('respects maxNodes limit', () => {
const bin = TemplateJson.gen(['bin', 10, 20], {maxNodes: 5}) as Uint8Array;
expect(bin instanceof Uint8Array).toBe(true);
expect(bin.length).toBeLessThanOrEqual(10);
});
});

describe('nil', () => {
test('always returns null', () => {
expect(TemplateJson.gen('nil')).toBe(null);
Expand Down Expand Up @@ -375,6 +435,16 @@ describe('TemplateJson', () => {
const result = TemplateJson.gen(['or', ['lit', 'only']]);
expect(result).toBe('only');
});

test('works with bin templates', () => {
resetMathRandom();
const result = TemplateJson.gen(['or', 'str', 'int', ['bin', 2, 2]]);
// Result should be one of the template types
const isString = typeof result === 'string';
const isNumber = typeof result === 'number';
const isBin = result instanceof Uint8Array;
expect(isString || isNumber || isBin).toBe(true);
});
});

describe('maxNodeCount', () => {
Expand Down Expand Up @@ -449,6 +519,39 @@ describe('TemplateJson', () => {
expect(typeof result).toBe('number');
expect(Number.isInteger(result)).toBe(true);
});

test('handles bin templates in complex structures', () => {
resetMathRandom();
const template: any = [
'obj',
[
['name', 'str'],
['data', ['bin', 3, 3]],
['metadata', [
'obj',
[
['hash', ['bin', 32, 32]],
['signature', ['bin', 64, 64, 0, 127]],
],
]],
],
];
const result = TemplateJson.gen(template) as any;
expect(typeof result).toBe('object');
expect(typeof result.name).toBe('string');
expect(result.data instanceof Uint8Array).toBe(true);
expect(result.data.length).toBe(3);
expect(typeof result.metadata).toBe('object');
expect(result.metadata.hash instanceof Uint8Array).toBe(true);
expect(result.metadata.hash.length).toBe(32);
expect(result.metadata.signature instanceof Uint8Array).toBe(true);
expect(result.metadata.signature.length).toBe(64);
// Check signature values are in the specified range
for (let i = 0; i < result.metadata.signature.length; i++) {
expect(result.metadata.signature[i]).toBeGreaterThanOrEqual(0);
expect(result.metadata.signature[i]).toBeLessThanOrEqual(127);
}
});
});
});

Expand Down
27 changes: 26 additions & 1 deletion src/structured/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ export type TemplateNode =
| FloatTemplate
| StringTemplate
| BooleanTemplate
| BinTemplate
| NullTemplate
| ArrayTemplate
| ObjectTemplate
| MapTemplate
| OrTemplate;

export type TemplateShorthand = 'num' | 'int' | 'float' | 'str' | 'bool' | 'nil' | 'arr' | 'obj' | 'map';
export type TemplateShorthand = 'num' | 'int' | 'float' | 'str' | 'bool' | 'bin' | 'nil' | 'arr' | 'obj' | 'map';

/**
* Recursive reference allows for recursive template construction, for example:
Expand Down Expand Up @@ -80,6 +81,30 @@ export type StringTemplate = [type: 'str', token?: Token];
*/
export type BooleanTemplate = [type: 'bool', value?: boolean];

/**
* Binary template. Generates a random Uint8Array. The template allows
* specifying the length of binary data and the range of values in each octet.
*/
export type BinTemplate = [
type: 'bin',
/**
* The minimum length of binary data. Defaults to 0.
*/
min?: number,
/**
* The maximum length of binary data. Defaults to 5.
*/
max?: number,
/**
* The minimum octet value. Defaults to 0.
*/
omin?: number,
/**
* The maximum octet value. Defaults to 255.
*/
omax?: number,
];

/**
* Null template. Generates a `null` value. If a specific value is provided, it
* will always return that value; otherwise, it returns `null`.
Expand Down
Loading