This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/fory.git
The following commit(s) were added to refs/heads/main by this push:
new e55c50826 feat(javascript): add configurable size guardrails (#3539)
e55c50826 is described below
commit e55c508260ba0b7c1cb75079c300b2ceb225bdc2
Author: Ayush Kumar <[email protected]>
AuthorDate: Thu Apr 9 17:09:26 2026 +0530
feat(javascript): add configurable size guardrails (#3539)
## Why?
Untrusted binary/map/list lengths can trigger extremely large,
uncontrolled memory allocations during deserialization. Relying on
process-level memory limits is late-failing and risks heap exhaustion or
OOM crashes from malicious payloads.
## What does this PR do?
Adds configurable size guardrails to the Fory JavaScript library,
checked immediately after the wire length is read to reject malformed
payloads before any heavy allocation happens.
### 1. type.ts
Added optional `maxBinarySize` and `maxCollectionSize` fields to the
Config interface
### 2. fory.ts
Added default limits (1,000,000 for collection, 64 MiB for binary) and
added `checkCollectionSize` and `checkBinarySize` validation methods.
### 3. typedArray.ts
Injected guard checks into the codegen and runtime read paths before
arrays, maps, and buffers are allocated.
### 4. sizeLimit.test.ts
Added a new test file validating limit boundaries, default thresholds,
fallback typing, and polymorphism across all collection and binary read
paths.
## Related issues
Closes #3414
## AI Contribution Checklist
- [No] Substantial AI assistance was used in this PR: `yes` / `no`
- [No] If `yes`, I included a completed [AI Contribution
Checklist](https://github.com/apache/fory/blob/main/AI_POLICY.md#9-contributor-checklist-for-ai-assisted-prs)
in this PR description and the required `AI Usage Disclosure`.
## Does this PR introduce any user-facing change?
- [x] Does this PR introduce any public API change? (Yes, adds optional
`maxBinarySize` and `maxCollectionSize` fields to the configuration.)
- [ ] Does this PR introduce any binary protocol compatibility change?
## Benchmark
N/A
---
javascript/packages/core/lib/fory.ts | 34 +++
javascript/packages/core/lib/gen/collection.ts | 2 +
javascript/packages/core/lib/gen/map.ts | 2 +
javascript/packages/core/lib/gen/typedArray.ts | 12 +-
javascript/packages/core/lib/type.ts | 2 +
javascript/test/sizeLimit.test.ts | 351 +++++++++++++++++++++++++
6 files changed, 401 insertions(+), 2 deletions(-)
diff --git a/javascript/packages/core/lib/fory.ts
b/javascript/packages/core/lib/fory.ts
index 2d039b169..5087ffdad 100644
--- a/javascript/packages/core/lib/fory.ts
+++ b/javascript/packages/core/lib/fory.ts
@@ -32,6 +32,8 @@ import { MetaStringResolver } from "./metaStringResolver";
const DEFAULT_DEPTH_LIMIT = 50 as const;
const MIN_DEPTH_LIMIT = 2 as const;
+const DEFAULT_MAX_COLLECTION_SIZE = 1_000_000 as const;
+const DEFAULT_MAX_BINARY_SIZE = 64 * 1024 * 1024; // 64 MiB
export default class {
binaryReader: BinaryReader;
@@ -45,6 +47,8 @@ export default class {
config: Config;
depth = 0;
maxDepth: number;
+ maxBinarySize: number;
+ maxCollectionSize: number;
constructor(config?: Partial<Config>) {
this.config = this.initConfig(config);
@@ -53,6 +57,16 @@ export default class {
throw new Error(`maxDepth must be an integer >= ${MIN_DEPTH_LIMIT} but
got ${maxDepth}`);
}
this.maxDepth = maxDepth;
+ const maxBinarySize = config?.maxBinarySize ?? DEFAULT_MAX_BINARY_SIZE;
+ if (!Number.isInteger(maxBinarySize) || maxBinarySize < 0) {
+ throw new Error(`maxBinarySize must be a non-negative integer but got
${maxBinarySize}`);
+ }
+ this.maxBinarySize = maxBinarySize;
+ const maxCollectionSize = config?.maxCollectionSize ??
DEFAULT_MAX_COLLECTION_SIZE;
+ if (!Number.isInteger(maxCollectionSize) || maxCollectionSize < 0) {
+ throw new Error(`maxCollectionSize must be a non-negative integer but
got ${maxCollectionSize}`);
+ }
+ this.maxCollectionSize = maxCollectionSize;
this.binaryReader = new BinaryReader(this.config);
this.binaryWriter = new BinaryWriter(this.config);
this.referenceResolver = new ReferenceResolver(this.binaryReader);
@@ -68,6 +82,8 @@ export default class {
refTracking: config?.refTracking !== null ? Boolean(config?.refTracking)
: null,
useSliceString: Boolean(config?.useSliceString),
maxDepth: config?.maxDepth,
+ maxBinarySize: config?.maxBinarySize,
+ maxCollectionSize: config?.maxCollectionSize,
hooks: config?.hooks || {},
compatible: Boolean(config?.compatible),
};
@@ -91,6 +107,24 @@ export default class {
this.depth--;
}
+ checkCollectionSize(size: number): void {
+ if (size > this.maxCollectionSize) {
+ throw new Error(
+ `Collection size ${size} exceeds maxCollectionSize
${this.maxCollectionSize}. `
+ + "The data may be malicious, or increase maxCollectionSize if needed."
+ );
+ }
+ }
+
+ checkBinarySize(size: number): void {
+ if (size > this.maxBinarySize) {
+ throw new Error(
+ `Binary size ${size} exceeds maxBinarySize ${this.maxBinarySize}. `
+ + "The data may be malicious, or increase maxBinarySize if needed."
+ );
+ }
+ }
+
private resetRead(): void {
this.referenceResolver.resetRead();
this.typeMetaResolver.resetRead();
diff --git a/javascript/packages/core/lib/gen/collection.ts
b/javascript/packages/core/lib/gen/collection.ts
index 396885abb..a08577759 100644
--- a/javascript/packages/core/lib/gen/collection.ts
+++ b/javascript/packages/core/lib/gen/collection.ts
@@ -152,6 +152,7 @@ class CollectionAnySerializer {
read(accessor: (result: any, index: number, v: any) => void,
createCollection: (len: number) => any, fromRef: boolean): any {
void fromRef;
const len = this.fory.binaryReader.readVarUint32Small7();
+ this.fory.checkCollectionSize(len);
const flags = this.fory.binaryReader.readUint8();
const isSame = flags & CollectionFlags.SAME_TYPE;
const includeNone = flags & CollectionFlags.HAS_NULL;
@@ -304,6 +305,7 @@ export abstract class CollectionSerializerGenerator extends
BaseSerializerGenera
const refFlag = this.scope.uniqueName("refFlag");
return `
const ${len} = ${this.builder.reader.readVarUint32Small7()};
+ fory.checkCollectionSize(${len});
const ${flags} = ${this.builder.reader.readUint8()};
const ${result} = ${this.newCollection(len)};
${this.maybeReference(result, refState)}
diff --git a/javascript/packages/core/lib/gen/map.ts
b/javascript/packages/core/lib/gen/map.ts
index 7f80bbc20..326cf4605 100644
--- a/javascript/packages/core/lib/gen/map.ts
+++ b/javascript/packages/core/lib/gen/map.ts
@@ -242,6 +242,7 @@ class MapAnySerializer {
read(fromRef: boolean): any {
let count = this.fory.binaryReader.readVarUint32Small7();
+ this.fory.checkCollectionSize(count);
const result = new Map();
if (fromRef) {
this.fory.referenceResolver.reference(result);
@@ -409,6 +410,7 @@ export class MapSerializerGenerator extends
BaseSerializerGenerator {
return `
let ${count} = ${this.builder.reader.readVarUint32Small7()};
+ fory.checkCollectionSize(${count});
const ${result} = new Map();
if (${refState}) {
${this.builder.referenceResolver.reference(result)}
diff --git a/javascript/packages/core/lib/gen/typedArray.ts
b/javascript/packages/core/lib/gen/typedArray.ts
index e3f776499..a1c1f0f78 100644
--- a/javascript/packages/core/lib/gen/typedArray.ts
+++ b/javascript/packages/core/lib/gen/typedArray.ts
@@ -47,6 +47,7 @@ function build(inner: TypeInfo, creator: string, size:
number) {
return `
const ${len} = ${this.builder.reader.readVarUInt32()};
+ fory.checkBinarySize(${len});
const ${copied} = ${this.builder.reader.buffer(len)}
const ${result} = new ${creator}(${copied}.buffer,
${copied}.byteOffset, ${copied}.byteLength / ${size});
${this.maybeReference(result, refState)}
@@ -85,6 +86,7 @@ class BoolArraySerializerGenerator extends
BaseSerializerGenerator {
const idx = this.scope.uniqueName("idx");
return `
const ${len} = ${this.builder.reader.readVarUInt32()};
+ fory.checkCollectionSize(${len});
const ${result} = new Array(${len});
${this.maybeReference(result, refState)}
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
@@ -120,10 +122,13 @@ class Float16ArraySerializerGenerator extends
BaseSerializerGenerator {
read(accessor: (expr: string) => string, refState: string): string {
const result = this.scope.uniqueName("result");
+ const rawLen = this.scope.uniqueName("rawLen");
const len = this.scope.uniqueName("len");
const idx = this.scope.uniqueName("idx");
return `
- const ${len} = ${this.builder.reader.readVarUInt32()} / 2;
+ const ${rawLen} = ${this.builder.reader.readVarUInt32()};
+ fory.checkBinarySize(${rawLen});
+ const ${len} = ${rawLen} / 2;
const ${result} = new Array(${len});
${this.maybeReference(result, refState)}
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
@@ -159,10 +164,13 @@ class BFloat16ArraySerializerGenerator extends
BaseSerializerGenerator {
read(accessor: (expr: string) => string, refState: string): string {
const result = this.scope.uniqueName("result");
+ const rawLen = this.scope.uniqueName("rawLen");
const len = this.scope.uniqueName("len");
const idx = this.scope.uniqueName("idx");
return `
- const ${len} = ${this.builder.reader.readVarUInt32()} / 2;
+ const ${rawLen} = ${this.builder.reader.readVarUInt32()};
+ fory.checkBinarySize(${rawLen});
+ const ${len} = ${rawLen} / 2;
const ${result} = new Array(${len});
${this.maybeReference(result, refState)}
for (let ${idx} = 0; ${idx} < ${len}; ${idx}++) {
diff --git a/javascript/packages/core/lib/type.ts
b/javascript/packages/core/lib/type.ts
index 52f320869..cdab57f59 100644
--- a/javascript/packages/core/lib/type.ts
+++ b/javascript/packages/core/lib/type.ts
@@ -265,6 +265,8 @@ export interface Config {
refTracking: boolean | null;
useSliceString: boolean;
maxDepth?: number;
+ maxBinarySize?: number;
+ maxCollectionSize?: number;
hooks: {
afterCodeGenerated?: (code: string) => string;
};
diff --git a/javascript/test/sizeLimit.test.ts
b/javascript/test/sizeLimit.test.ts
new file mode 100644
index 000000000..7a53ba524
--- /dev/null
+++ b/javascript/test/sizeLimit.test.ts
@@ -0,0 +1,351 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import Fory, { Type } from '../packages/core/index';
+import { describe, expect, test } from '@jest/globals';
+
+describe('size-limit guardrails', () => {
+ describe('configuration', () => {
+ test('should have default limits matching Go', () => {
+ const fory = new Fory();
+ expect(fory.maxBinarySize).toBe(64 * 1024 * 1024);
+ expect(fory.maxCollectionSize).toBe(1_000_000);
+ });
+
+ test('should accept custom maxBinarySize', () => {
+ const fory = new Fory({ maxBinarySize: 1024 });
+ expect(fory.maxBinarySize).toBe(1024);
+ });
+
+ test('should accept custom maxCollectionSize', () => {
+ const fory = new Fory({ maxCollectionSize: 500 });
+ expect(fory.maxCollectionSize).toBe(500);
+ });
+
+ test('should accept zero as a valid limit', () => {
+ const fory = new Fory({ maxBinarySize: 0, maxCollectionSize: 0 });
+ expect(fory.maxBinarySize).toBe(0);
+ expect(fory.maxCollectionSize).toBe(0);
+ });
+
+ test('should reject negative maxBinarySize', () => {
+ expect(() => new Fory({ maxBinarySize: -1 })).toThrow(
+ 'maxBinarySize must be a non-negative integer'
+ );
+ });
+
+ test('should reject non-integer maxBinarySize', () => {
+ expect(() => new Fory({ maxBinarySize: 1.5 })).toThrow(
+ 'maxBinarySize must be a non-negative integer'
+ );
+ });
+
+ test('should reject NaN maxBinarySize', () => {
+ expect(() => new Fory({ maxBinarySize: NaN })).toThrow(
+ 'maxBinarySize must be a non-negative integer'
+ );
+ });
+
+ test('should reject negative maxCollectionSize', () => {
+ expect(() => new Fory({ maxCollectionSize: -10 })).toThrow(
+ 'maxCollectionSize must be a non-negative integer'
+ );
+ });
+
+ test('should reject non-integer maxCollectionSize', () => {
+ expect(() => new Fory({ maxCollectionSize: 2.7 })).toThrow(
+ 'maxCollectionSize must be a non-negative integer'
+ );
+ });
+
+ test('should work with other options combined', () => {
+ const fory = new Fory({
+ maxDepth: 100,
+ maxBinarySize: 1024,
+ maxCollectionSize: 500,
+ refTracking: true,
+ compatible: true,
+ });
+ expect(fory.maxDepth).toBe(100);
+ expect(fory.maxBinarySize).toBe(1024);
+ expect(fory.maxCollectionSize).toBe(500);
+ });
+ });
+
+ describe('checkCollectionSize', () => {
+ test('should not throw when size is within default limit', () => {
+ const fory = new Fory();
+ expect(() => fory.checkCollectionSize(999999)).not.toThrow();
+ });
+
+ test('should not throw when size is within limit', () => {
+ const fory = new Fory({ maxCollectionSize: 100 });
+ expect(() => fory.checkCollectionSize(100)).not.toThrow();
+ expect(() => fory.checkCollectionSize(0)).not.toThrow();
+ });
+
+ test('should throw when size exceeds limit', () => {
+ const fory = new Fory({ maxCollectionSize: 100 });
+ expect(() => fory.checkCollectionSize(101)).toThrow(
+ 'Collection size 101 exceeds maxCollectionSize 100'
+ );
+ });
+
+ test('error message should include helpful suggestion', () => {
+ const fory = new Fory({ maxCollectionSize: 10 });
+ expect(() => fory.checkCollectionSize(20)).toThrow(
+ 'increase maxCollectionSize if needed'
+ );
+ });
+ });
+
+ describe('checkBinarySize', () => {
+ test('should not throw when size is within default limit', () => {
+ const fory = new Fory();
+ expect(() => fory.checkBinarySize(999999)).not.toThrow();
+ });
+
+ test('should not throw when size is within limit', () => {
+ const fory = new Fory({ maxBinarySize: 1024 });
+ expect(() => fory.checkBinarySize(1024)).not.toThrow();
+ expect(() => fory.checkBinarySize(0)).not.toThrow();
+ });
+
+ test('should throw when size exceeds limit', () => {
+ const fory = new Fory({ maxBinarySize: 1024 });
+ expect(() => fory.checkBinarySize(1025)).toThrow(
+ 'Binary size 1025 exceeds maxBinarySize 1024'
+ );
+ });
+ });
+
+ describe('list deserialization with maxCollectionSize', () => {
+ test('should deserialize list within limit', () => {
+ const fory = new Fory({ maxCollectionSize: 10 });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.array(Type.int32()));
+ const data = [1, 2, 3];
+ const result = deserialize(serialize(data));
+ expect(result).toEqual(data);
+ });
+
+ test('should throw when list exceeds maxCollectionSize', () => {
+ const serializeFory = new Fory();
+ const { serialize } =
serializeFory.registerSerializer(Type.array(Type.int32()));
+ const bytes = serialize([1, 2, 3, 4, 5]);
+
+ const deserializeFory = new Fory({ maxCollectionSize: 3 });
+ const { deserialize } =
deserializeFory.registerSerializer(Type.array(Type.int32()));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxCollectionSize');
+ });
+
+ test('should deserialize list at exact limit', () => {
+ const fory = new Fory({ maxCollectionSize: 3 });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.array(Type.int32()));
+ const data = [1, 2, 3];
+ const result = deserialize(serialize(data));
+ expect(result).toEqual(data);
+ });
+ });
+
+ describe('set deserialization with maxCollectionSize', () => {
+ test('should deserialize set within limit', () => {
+ const fory = new Fory({ maxCollectionSize: 10, refTracking: true });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.set(Type.int32()));
+ const data = new Set([1, 2, 3]);
+ const result = deserialize(serialize(data));
+ expect(result).toEqual(data);
+ });
+
+ test('should throw when set exceeds maxCollectionSize', () => {
+ const serializeFory = new Fory({ refTracking: true });
+ const { serialize } =
serializeFory.registerSerializer(Type.set(Type.int32()));
+ const bytes = serialize(new Set([1, 2, 3, 4, 5]));
+
+ const deserializeFory = new Fory({ maxCollectionSize: 3, refTracking:
true });
+ const { deserialize } =
deserializeFory.registerSerializer(Type.set(Type.int32()));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxCollectionSize');
+ });
+ });
+
+ describe('map deserialization with maxCollectionSize', () => {
+ test('should deserialize map within limit', () => {
+ const fory = new Fory({ maxCollectionSize: 10, refTracking: true });
+ const { serialize, deserialize } = fory.registerSerializer(
+ Type.map(Type.string(), Type.int32())
+ );
+ const data = new Map([['a', 1], ['b', 2]]);
+ const result = deserialize(serialize(data));
+ expect(result).toEqual(data);
+ });
+
+ test('should throw when map exceeds maxCollectionSize', () => {
+ const serializeFory = new Fory({ refTracking: true });
+ const { serialize } = serializeFory.registerSerializer(
+ Type.map(Type.string(), Type.int32())
+ );
+ const bytes = serialize(new Map([['a', 1], ['b', 2], ['c', 3], ['d',
4]]));
+
+ const deserializeFory = new Fory({ maxCollectionSize: 2, refTracking:
true });
+ const { deserialize } = deserializeFory.registerSerializer(
+ Type.map(Type.string(), Type.int32())
+ );
+ expect(() => deserialize(bytes)).toThrow('exceeds maxCollectionSize');
+ });
+ });
+
+ describe('binary deserialization with maxBinarySize', () => {
+ test('should deserialize binary within limit', () => {
+ const fory = new Fory({ maxBinarySize: 1024, refTracking: true });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.struct("test.binary", {
+ data: Type.binary(),
+ }));
+ const data = { data: new Uint8Array([1, 2, 3]) };
+ const result = deserialize(serialize(data));
+ expect(result!.data![0]).toBe(1);
+ expect(result!.data![1]).toBe(2);
+ expect(result!.data![2]).toBe(3);
+ });
+
+ test('should throw when binary exceeds maxBinarySize', () => {
+ const serializeFory = new Fory({ refTracking: true });
+ const { serialize } =
serializeFory.registerSerializer(Type.struct("test.binary2", {
+ data: Type.binary(),
+ }));
+ const bytes = serialize({ data: new Uint8Array(100) });
+
+ const deserializeFory = new Fory({ maxBinarySize: 50, refTracking: true
});
+ const { deserialize } =
deserializeFory.registerSerializer(Type.struct("test.binary2", {
+ data: Type.binary(),
+ }));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxBinarySize');
+ });
+ });
+
+ describe('default limits allow normal payloads', () => {
+ test('should allow large collections within default limit', () => {
+ const fory = new Fory({ refTracking: true });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.array(Type.int32()));
+ const bigArray = Array.from({ length: 1000 }, (_, i) => i);
+ const result = deserialize(serialize(bigArray));
+ expect(result).toEqual(bigArray);
+ });
+ });
+
+ describe('polymorphic (any-typed) collection paths', () => {
+ test('should enforce maxCollectionSize on untyped list', () => {
+ const serializeFory = new Fory({ refTracking: true });
+ const bytes = serializeFory.serialize([1, "two", 3.0]);
+
+ const deserializeFory = new Fory({ maxCollectionSize: 2, refTracking:
true });
+ expect(() => deserializeFory.deserialize(bytes)).toThrow('exceeds
maxCollectionSize');
+ });
+
+ test('should enforce maxCollectionSize on untyped map', () => {
+ const serializeFory = new Fory({ refTracking: true });
+ const bytes = serializeFory.serialize(new Map([["a", 1], ["b", 2], ["c",
3]]));
+
+ const deserializeFory = new Fory({ maxCollectionSize: 2, refTracking:
true });
+ expect(() => deserializeFory.deserialize(bytes)).toThrow('exceeds
maxCollectionSize');
+ });
+ });
+
+ describe('bool array deserialization with maxCollectionSize', () => {
+ // BoolArraySerializerGenerator reads an element count — guarded by
checkCollectionSize
+ test('should deserialize bool array within limit', () => {
+ const fory = new Fory({ maxCollectionSize: 10 });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.struct("test.boolArr", {
+ flags: Type.boolArray(),
+ }));
+ const data = { flags: [true, false, true] };
+ const result = deserialize(serialize(data));
+ expect(result!.flags).toEqual([true, false, true]);
+ });
+
+ test('should throw when bool array exceeds maxCollectionSize', () => {
+ const serializeFory = new Fory();
+ const { serialize } =
serializeFory.registerSerializer(Type.struct("test.boolArr2", {
+ flags: Type.boolArray(),
+ }));
+ const bytes = serialize({ flags: [true, false, true, true, false] });
+
+ const deserializeFory = new Fory({ maxCollectionSize: 3 });
+ const { deserialize } =
deserializeFory.registerSerializer(Type.struct("test.boolArr2", {
+ flags: Type.boolArray(),
+ }));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxCollectionSize');
+ });
+ });
+
+ describe('float16 array deserialization with maxBinarySize', () => {
+ // Float16ArraySerializerGenerator writes byte count (elements * 2) —
guarded by checkBinarySize
+ test('should deserialize float16 array within limit', () => {
+ const fory = new Fory({ maxBinarySize: 1024 });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.struct("test.f16Arr", {
+ vals: Type.float16Array(),
+ }));
+ const data = { vals: [1.0, 2.0, 3.0] };
+ const result = deserialize(serialize(data));
+ expect(result!.vals!.length).toBe(3);
+ });
+
+ test('should throw when float16 array byte length exceeds maxBinarySize',
() => {
+ const serializeFory = new Fory();
+ const { serialize } =
serializeFory.registerSerializer(Type.struct("test.f16Arr2", {
+ vals: Type.float16Array(),
+ }));
+ // 10 elements × 2 bytes each = 20 raw bytes on the wire
+ const bytes = serialize({ vals: Array.from({ length: 10 }, (_, i) => i *
0.5) });
+
+ const deserializeFory = new Fory({ maxBinarySize: 10 }); // 10 < 20
+ const { deserialize } =
deserializeFory.registerSerializer(Type.struct("test.f16Arr2", {
+ vals: Type.float16Array(),
+ }));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxBinarySize');
+ });
+ });
+
+ describe('bfloat16 array deserialization with maxBinarySize', () => {
+ // BFloat16ArraySerializerGenerator writes byte count (elements * 2) —
same pattern as float16
+ test('should deserialize bfloat16 array within limit', () => {
+ const fory = new Fory({ maxBinarySize: 1024 });
+ const { serialize, deserialize } =
fory.registerSerializer(Type.struct("test.bf16Arr", {
+ vals: Type.bfloat16Array(),
+ }));
+ const data = { vals: [1.0, 2.0, 3.0] };
+ const result = deserialize(serialize(data));
+ expect(result!.vals!.length).toBe(3);
+ });
+
+ test('should throw when bfloat16 array byte length exceeds maxBinarySize',
() => {
+ const serializeFory = new Fory();
+ const { serialize } =
serializeFory.registerSerializer(Type.struct("test.bf16Arr2", {
+ vals: Type.bfloat16Array(),
+ }));
+ // 10 elements × 2 bytes each = 20 raw bytes on the wire
+ const bytes = serialize({ vals: Array.from({ length: 10 }, (_, i) => i *
0.5) });
+
+ const deserializeFory = new Fory({ maxBinarySize: 10 }); // 10 < 20
+ const { deserialize } =
deserializeFory.registerSerializer(Type.struct("test.bf16Arr2", {
+ vals: Type.bfloat16Array(),
+ }));
+ expect(() => deserialize(bytes)).toThrow('exceeds maxBinarySize');
+ });
+ });
+});
+
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]