11import { utf8Encode } from "./utils/uf8Encode" ;
22import { ExtensionCodec , ExtensionCodecType } from "./ExtensionCodec" ;
33import { encodeUint32 , encodeInt64 , encodeInt32 , encodeUint64 } from "./utils/int" ;
4- import { isObject , isNodeJsBuffer } from "./utils/is" ;
4+ import { isNodeJsBuffer } from "./utils/is" ;
55import { Writable } from "./utils/Writable" ;
66
77export type EncodeOptions = Readonly < {
@@ -23,45 +23,44 @@ export function encode(value: unknown, options: Partial<EncodeOptions> = {}): Ar
2323 return output ;
2424}
2525
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+
2736 constructor ( readonly maxDepth : number , readonly extensionCodec : ExtensionCodecType ) { }
2837
29- encode < OutputType extends Writable < number > > ( rv : OutputType , object : unknown , depth : number ) : void {
38+ encode ( rv : OutputType , object : unknown , depth : number ) : void {
3039 if ( depth > this . maxDepth ) {
3140 throw new Error ( `Too deep objects in depth ${ depth } ` ) ;
3241 }
3342
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 ) {
3757 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 ) ;
4458 } 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 ) ;
6160 }
6261 }
6362
64- encodeNumber < OutputType extends Writable < number > > ( rv : OutputType , object : number ) {
63+ encodeNumber ( rv : OutputType , object : number ) {
6564 if ( Number . isSafeInteger ( object ) ) {
6665 if ( object >= 0 ) {
6766 if ( object < 0x80 ) {
@@ -133,7 +132,12 @@ export class Encoder {
133132 }
134133 }
135134
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 ) {
137141 const bytes = utf8Encode ( object ) ;
138142 const size = bytes . length ;
139143 if ( size < 32 ) {
@@ -152,10 +156,30 @@ export class Encoder {
152156 } else {
153157 throw new Error ( `Too long string: ${ size } bytes in UTF-8` ) ;
154158 }
159+
155160 rv . push ( ...bytes ) ;
156161 }
157162
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 ) {
159183 const size = object . byteLength ;
160184 if ( size < 0x100 ) {
161185 // bin 8
@@ -176,7 +200,7 @@ export class Encoder {
176200 }
177201 }
178202
179- encodeArray < OutputType extends Writable < number > > ( rv : OutputType , object : Array < unknown > , depth : number ) {
203+ encodeArray ( rv : OutputType , object : Array < unknown > , depth : number ) {
180204 const size = object . length ;
181205 if ( size < 16 ) {
182206 // fixarray
@@ -196,7 +220,7 @@ export class Encoder {
196220 }
197221 }
198222
199- encodeMap < OutputType extends Writable < number > > ( rv : OutputType , object : Record < string , unknown > , depth : number ) {
223+ encodeMap ( rv : OutputType , object : Record < string , unknown > , depth : number ) {
200224 const keys = Object . keys ( object ) ;
201225 const size = keys . length ;
202226 // map
@@ -217,10 +241,7 @@ export class Encoder {
217241 }
218242 }
219243
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 > } ) {
224245 const size = ext . data . length ;
225246 const typeByte = ext . type < 0 ? ext . type + 0x100 : ext . type ;
226247 if ( size === 1 ) {
0 commit comments