1
1
import { utf8Encode } from "./utils/uf8Encode" ;
2
2
import { ExtensionCodec , ExtensionCodecType } from "./ExtensionCodec" ;
3
3
import { encodeUint32 , encodeInt64 , encodeInt32 , encodeUint64 } from "./utils/int" ;
4
- import { isObject , isNodeJsBuffer } from "./utils/is" ;
4
+ import { isNodeJsBuffer } from "./utils/is" ;
5
5
import { Writable } from "./utils/Writable" ;
6
6
7
7
export type EncodeOptions = Readonly < {
@@ -23,45 +23,44 @@ export function encode(value: unknown, options: Partial<EncodeOptions> = {}): Ar
23
23
return output ;
24
24
}
25
25
26
- export class Encoder {
26
+ export class Encoder < OutputType extends Writable < number > > {
27
+ readonly typeofMap = {
28
+ "undefined" : this . encodeNil ,
29
+ "boolean" : this . encodeBoolean ,
30
+ "number" : this . encodeNumber ,
31
+ "bigint" : this . encodeBigInt ,
32
+ "string" : this . encodeString ,
33
+ "object" : this . encodeObject ,
34
+ } as Record < string , ( this : Encoder < OutputType > , rv : OutputType , object : unknown , depth : number ) => void > ;
35
+
27
36
constructor ( readonly maxDepth : number , readonly extensionCodec : ExtensionCodecType ) { }
28
37
29
- encode < OutputType extends Writable < number > > ( rv : OutputType , object : unknown , depth : number ) : void {
38
+ encode ( rv : OutputType , object : unknown , depth : number ) : void {
30
39
if ( depth > this . maxDepth ) {
31
40
throw new Error ( `Too deep objects in depth ${ depth } ` ) ;
32
41
}
33
42
34
- if ( object == null ) {
35
- rv . push ( 0xc0 ) ;
36
- } else if ( object === false ) {
43
+ const encodeFunc = this . typeofMap [ typeof object ] ;
44
+ if ( ! encodeFunc ) {
45
+ throw new Error ( `Unrecognized object: ${ Object . prototype . toString . apply ( object ) } ` ) ;
46
+ }
47
+
48
+ encodeFunc . call ( this , rv , object , depth ) ;
49
+ }
50
+
51
+ encodeNil ( rv : OutputType ) {
52
+ rv . push ( 0xc0 ) ;
53
+ }
54
+
55
+ encodeBoolean ( rv : OutputType , object : boolean ) {
56
+ if ( object === false ) {
37
57
rv . push ( 0xc2 ) ;
38
- } else if ( object === true ) {
39
- rv . push ( 0xc3 ) ;
40
- } else if ( typeof object === "number" ) {
41
- this . encodeNumber ( rv , object ) ;
42
- } else if ( typeof object === "string" ) {
43
- this . encodeString ( rv , object ) ;
44
58
} else {
45
- // try to encode objects with custom codec first of non-primitives
46
- const ext = this . extensionCodec . tryToEncode ( object ) ;
47
- if ( ext != null ) {
48
- this . encodeExtension ( rv , ext ) ;
49
- } else if ( ArrayBuffer . isView ( object ) ) {
50
- this . encodeBinary ( rv , object ) ;
51
- } else if ( Array . isArray ( object ) ) {
52
- this . encodeArray ( rv , object , depth ) ;
53
- } else if ( isObject ( object ) ) {
54
- this . encodeMap ( rv , object , depth ) ;
55
- } else {
56
- // not encodable unless ExtensionCodec handles it,
57
- // for example Symbol, Function, and so on.
58
- // Note that some objects, for example Symbol, throws errors by its own toString() method
59
- throw new Error ( `Unrecognized object: ${ Object . prototype . toString . apply ( object ) } ` ) ;
60
- }
59
+ rv . push ( 0xc3 ) ;
61
60
}
62
61
}
63
62
64
- encodeNumber < OutputType extends Writable < number > > ( rv : OutputType , object : number ) {
63
+ encodeNumber ( rv : OutputType , object : number ) {
65
64
if ( Number . isSafeInteger ( object ) ) {
66
65
if ( object >= 0 ) {
67
66
if ( object < 0x80 ) {
@@ -133,7 +132,12 @@ export class Encoder {
133
132
}
134
133
}
135
134
136
- encodeString < OutputType extends Writable < number > > ( rv : OutputType , object : string ) {
135
+ encodeBigInt ( _rv : OutputType , _object : bigint ) {
136
+ // BigInt literals is not available here!
137
+ throw new Error ( "BigInt is not yet implemented!" ) ;
138
+ }
139
+
140
+ encodeString ( rv : OutputType , object : string ) {
137
141
const bytes = utf8Encode ( object ) ;
138
142
const size = bytes . length ;
139
143
if ( size < 32 ) {
@@ -152,10 +156,30 @@ export class Encoder {
152
156
} else {
153
157
throw new Error ( `Too long string: ${ size } bytes in UTF-8` ) ;
154
158
}
159
+
155
160
rv . push ( ...bytes ) ;
156
161
}
157
162
158
- encodeBinary < OutputType extends Writable < number > > ( rv : OutputType , object : ArrayBufferView ) {
163
+ encodeObject ( rv : OutputType , object : object | null , depth : number ) {
164
+ if ( object === null ) {
165
+ this . encodeNil ( rv ) ;
166
+ return ;
167
+ }
168
+
169
+ // try to encode objects with custom codec first of non-primitives
170
+ const ext = this . extensionCodec . tryToEncode ( object ) ;
171
+ if ( ext != null ) {
172
+ this . encodeExtension ( rv , ext ) ;
173
+ } else if ( ArrayBuffer . isView ( object ) ) {
174
+ this . encodeBinary ( rv , object ) ;
175
+ } else if ( Array . isArray ( object ) ) {
176
+ this . encodeArray ( rv , object , depth ) ;
177
+ } else {
178
+ this . encodeMap ( rv , object as Record < string , unknown > , depth ) ;
179
+ }
180
+ }
181
+
182
+ encodeBinary ( rv : OutputType , object : ArrayBufferView ) {
159
183
const size = object . byteLength ;
160
184
if ( size < 0x100 ) {
161
185
// bin 8
@@ -176,7 +200,7 @@ export class Encoder {
176
200
}
177
201
}
178
202
179
- encodeArray < OutputType extends Writable < number > > ( rv : OutputType , object : Array < unknown > , depth : number ) {
203
+ encodeArray ( rv : OutputType , object : Array < unknown > , depth : number ) {
180
204
const size = object . length ;
181
205
if ( size < 16 ) {
182
206
// fixarray
@@ -196,7 +220,7 @@ export class Encoder {
196
220
}
197
221
}
198
222
199
- encodeMap < OutputType extends Writable < number > > ( rv : OutputType , object : Record < string , unknown > , depth : number ) {
223
+ encodeMap ( rv : OutputType , object : Record < string , unknown > , depth : number ) {
200
224
const keys = Object . keys ( object ) ;
201
225
const size = keys . length ;
202
226
// map
@@ -217,10 +241,7 @@ export class Encoder {
217
241
}
218
242
}
219
243
220
- encodeExtension < OutputType extends Writable < number > > (
221
- rv : OutputType ,
222
- ext : { type : number ; data : ReadonlyArray < number > } ,
223
- ) {
244
+ encodeExtension ( rv : OutputType , ext : { type : number ; data : ReadonlyArray < number > } ) {
224
245
const size = ext . data . length ;
225
246
const typeByte = ext . type < 0 ? ext . type + 0x100 : ext . type ;
226
247
if ( size === 1 ) {
0 commit comments