-
Notifications
You must be signed in to change notification settings - Fork 37
/
binary.ts
134 lines (118 loc) · 5.75 KB
/
binary.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import * as errors from "../../errors";
import {
Type,
EnumType,
List,
onTypedValueSelect,
onTypeSelect,
OptionValue,
PrimitiveType,
PrimitiveValue,
Struct,
StructType,
TypedValue,
EnumValue,
TupleType,
Tuple,
ArrayVecType,
ArrayVec,
} from "../typesystem";
import { guardTrue } from "../../utils";
import { OptionValueBinaryCodec } from "./option";
import { PrimitiveBinaryCodec } from "./primitive";
import { ListBinaryCodec } from "./list";
import { StructBinaryCodec } from "./struct";
import { EnumBinaryCodec } from "./enum";
import { TupleBinaryCodec } from "./tuple";
import { ArrayVecBinaryCodec } from "./arrayVec";
export class BinaryCodec {
readonly constraints: BinaryCodecConstraints;
private readonly optionCodec: OptionValueBinaryCodec;
private readonly listCodec: ListBinaryCodec;
private readonly arrayCodec: ArrayVecBinaryCodec;
private readonly primitiveCodec: PrimitiveBinaryCodec;
private readonly structCodec: StructBinaryCodec;
private readonly tupleCodec: TupleBinaryCodec;
private readonly enumCodec: EnumBinaryCodec;
constructor(constraints: BinaryCodecConstraints | null = null) {
this.constraints = constraints || new BinaryCodecConstraints();
this.optionCodec = new OptionValueBinaryCodec(this);
this.listCodec = new ListBinaryCodec(this);
this.arrayCodec = new ArrayVecBinaryCodec(this);
this.primitiveCodec = new PrimitiveBinaryCodec(this);
this.structCodec = new StructBinaryCodec(this);
this.tupleCodec = new TupleBinaryCodec(this);
this.enumCodec = new EnumBinaryCodec(this);
}
decodeTopLevel<TResult extends TypedValue = TypedValue>(buffer: Buffer, type: Type): TResult {
this.constraints.checkBufferLength(buffer);
let typedValue = onTypeSelect<TypedValue>(type, {
onOption: () => this.optionCodec.decodeTopLevel(buffer, type.getFirstTypeParameter()),
onList: () => this.listCodec.decodeTopLevel(buffer, type),
onArray: () => this.arrayCodec.decodeTopLevel(buffer, <ArrayVecType>type),
onPrimitive: () => this.primitiveCodec.decodeTopLevel(buffer, <PrimitiveType>type),
onStruct: () => this.structCodec.decodeTopLevel(buffer, <StructType>type),
onTuple: () => this.tupleCodec.decodeTopLevel(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeTopLevel(buffer, <EnumType>type),
});
return <TResult>typedValue;
}
decodeNested<TResult extends TypedValue = TypedValue>(buffer: Buffer, type: Type): [TResult, number] {
this.constraints.checkBufferLength(buffer);
let [typedResult, decodedLength] = onTypeSelect<[TypedValue, number]>(type, {
onOption: () => this.optionCodec.decodeNested(buffer, type.getFirstTypeParameter()),
onList: () => this.listCodec.decodeNested(buffer, type),
onArray: () => this.arrayCodec.decodeNested(buffer, <ArrayVecType>type),
onPrimitive: () => this.primitiveCodec.decodeNested(buffer, <PrimitiveType>type),
onStruct: () => this.structCodec.decodeNested(buffer, <StructType>type),
onTuple: () => this.tupleCodec.decodeNested(buffer, <TupleType>type),
onEnum: () => this.enumCodec.decodeNested(buffer, <EnumType>type),
});
return [<TResult>typedResult, decodedLength];
}
encodeNested(typedValue: TypedValue): Buffer {
guardTrue(typedValue.getType().getCardinality().isSingular(), "singular cardinality, thus encodable type");
return onTypedValueSelect(typedValue, {
onPrimitive: () => this.primitiveCodec.encodeNested(<PrimitiveValue>typedValue),
onOption: () => this.optionCodec.encodeNested(<OptionValue>typedValue),
onList: () => this.listCodec.encodeNested(<List>typedValue),
onArray: () => this.arrayCodec.encodeNested(<ArrayVec>typedValue),
onStruct: () => this.structCodec.encodeNested(<Struct>typedValue),
onTuple: () => this.tupleCodec.encodeNested(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeNested(<EnumValue>typedValue),
});
}
encodeTopLevel(typedValue: TypedValue): Buffer {
guardTrue(typedValue.getType().getCardinality().isSingular(), "singular cardinality, thus encodable type");
return onTypedValueSelect(typedValue, {
onPrimitive: () => this.primitiveCodec.encodeTopLevel(<PrimitiveValue>typedValue),
onOption: () => this.optionCodec.encodeTopLevel(<OptionValue>typedValue),
onList: () => this.listCodec.encodeTopLevel(<List>typedValue),
onArray: () => this.arrayCodec.encodeTopLevel(<ArrayVec>typedValue),
onStruct: () => this.structCodec.encodeTopLevel(<Struct>typedValue),
onTuple: () => this.tupleCodec.encodeTopLevel(<Tuple>typedValue),
onEnum: () => this.enumCodec.encodeTopLevel(<EnumValue>typedValue),
});
}
}
export class BinaryCodecConstraints {
maxBufferLength: number;
maxListLength: number;
constructor(init?: Partial<BinaryCodecConstraints>) {
this.maxBufferLength = init?.maxBufferLength || 256000;
this.maxListLength = init?.maxListLength || 128000;
}
checkBufferLength(buffer: Buffer) {
if (buffer.length > this.maxBufferLength) {
throw new errors.ErrCodec(`Buffer too large: ${buffer.length} > ${this.maxBufferLength}`);
}
}
/**
* This constraint avoids computer-freezing decode bugs (e.g. due to invalid ABI or struct definitions).
*/
checkListLength(length: number) {
if (length > this.maxListLength) {
throw new errors.ErrCodec(`List too large: ${length} > ${this.maxListLength}`);
}
}
}