@@ -86,6 +86,89 @@ type StackArrayState = {
86
86
position : number ;
87
87
} ;
88
88
89
+ class StackPool {
90
+ private readonly stack : Array < StackState > = [ ] ;
91
+ private stackHeadPosition = - 1 ;
92
+
93
+ public get length ( ) : number {
94
+ return this . stackHeadPosition + 1 ;
95
+ }
96
+
97
+ public top ( ) : StackState | undefined {
98
+ return this . stack [ this . stackHeadPosition ] ;
99
+ }
100
+
101
+ public pushArrayState ( size : number ) {
102
+ const state = this . getUninitializedStateFromPool ( ) as StackArrayState ;
103
+
104
+ state . type = STATE_ARRAY ;
105
+ state . position = 0 ;
106
+ state . size = size ;
107
+ state . array = new Array ( size ) ;
108
+ }
109
+
110
+ public pushMapState ( size : number ) {
111
+ const state = this . getUninitializedStateFromPool ( ) as StackMapState ;
112
+
113
+ state . type = STATE_MAP_KEY ;
114
+ state . readCount = 0 ;
115
+ state . size = size ;
116
+ state . map = { } ;
117
+ }
118
+
119
+ private getUninitializedStateFromPool ( ) {
120
+ this . stackHeadPosition ++ ;
121
+
122
+ if ( this . stackHeadPosition === this . stack . length ) {
123
+
124
+ const partialState : Partial < StackState > = {
125
+ type : undefined ,
126
+ size : 0 ,
127
+ array : undefined ,
128
+ position : 0 ,
129
+ readCount : 0 ,
130
+ map : undefined ,
131
+ key : null ,
132
+ } ;
133
+
134
+ this . stack . push ( partialState as StackState )
135
+ }
136
+
137
+ return this . stack [ this . stackHeadPosition ] ;
138
+ }
139
+
140
+ public release ( state : StackState ) : void {
141
+ const topStackState = this . stack [ this . stackHeadPosition ] ;
142
+
143
+ if ( topStackState !== state ) {
144
+ throw new Error ( "Invalid stack state. Released state is not on top of the stack." ) ;
145
+ }
146
+
147
+ if ( state . type === STATE_ARRAY ) {
148
+ const partialState = state as Partial < StackArrayState > ;
149
+ partialState . size = 0 ;
150
+ partialState . array = undefined ;
151
+ partialState . position = 0 ;
152
+ partialState . type = undefined ;
153
+ }
154
+
155
+ if ( state . type === STATE_MAP_KEY || state . type === STATE_MAP_VALUE ) {
156
+ const partialState = state as Partial < StackMapState > ;
157
+ partialState . size = 0 ;
158
+ partialState . map = undefined ;
159
+ partialState . readCount = 0 ;
160
+ partialState . type = undefined ;
161
+ }
162
+
163
+ this . stackHeadPosition -- ;
164
+ }
165
+
166
+ public reset ( ) : void {
167
+ this . stack . length = 0 ;
168
+ this . stackHeadPosition = - 1 ;
169
+ }
170
+ }
171
+
89
172
type StackState = StackArrayState | StackMapState ;
90
173
91
174
const HEAD_BYTE_REQUIRED = - 1 ;
@@ -125,7 +208,7 @@ export class Decoder<ContextType = undefined> {
125
208
private view = EMPTY_VIEW ;
126
209
private bytes = EMPTY_BYTES ;
127
210
private headByte = HEAD_BYTE_REQUIRED ;
128
- private readonly stack : Array < StackState > = [ ] ;
211
+ private readonly stack = new StackPool ( ) ;
129
212
130
213
public constructor ( options ?: DecoderOptions < ContextType > ) {
131
214
this . extensionCodec = options ?. extensionCodec ?? ( ExtensionCodec . defaultCodec as ExtensionCodecType < ContextType > ) ;
@@ -143,7 +226,7 @@ export class Decoder<ContextType = undefined> {
143
226
private reinitializeState ( ) {
144
227
this . totalPos = 0 ;
145
228
this . headByte = HEAD_BYTE_REQUIRED ;
146
- this . stack . length = 0 ;
229
+ this . stack . reset ( ) ;
147
230
148
231
// view, bytes, and pos will be re-initialized in setBuffer()
149
232
}
@@ -465,13 +548,13 @@ export class Decoder<ContextType = undefined> {
465
548
const stack = this . stack ;
466
549
while ( stack . length > 0 ) {
467
550
// arrays and maps
468
- const state = stack [ stack . length - 1 ] ! ;
551
+ const state = stack . top ( ) ! ;
469
552
if ( state . type === STATE_ARRAY ) {
470
553
state . array [ state . position ] = object ;
471
554
state . position ++ ;
472
555
if ( state . position === state . size ) {
473
- stack . pop ( ) ;
474
556
object = state . array ;
557
+ stack . release ( state ) ;
475
558
} else {
476
559
continue DECODE;
477
560
}
@@ -493,8 +576,8 @@ export class Decoder<ContextType = undefined> {
493
576
state . readCount ++ ;
494
577
495
578
if ( state . readCount === state . size ) {
496
- stack . pop ( ) ;
497
579
object = state . map ;
580
+ stack . release ( state ) ;
498
581
} else {
499
582
state . key = null ;
500
583
state . type = STATE_MAP_KEY ;
@@ -543,26 +626,15 @@ export class Decoder<ContextType = undefined> {
543
626
throw new DecodeError ( `Max length exceeded: map length (${ size } ) > maxMapLengthLength (${ this . maxMapLength } )` ) ;
544
627
}
545
628
546
- this . stack . push ( {
547
- type : STATE_MAP_KEY ,
548
- size,
549
- key : null ,
550
- readCount : 0 ,
551
- map : { } ,
552
- } ) ;
629
+ this . stack . pushMapState ( size ) ;
553
630
}
554
631
555
632
private pushArrayState ( size : number ) {
556
633
if ( size > this . maxArrayLength ) {
557
634
throw new DecodeError ( `Max length exceeded: array length (${ size } ) > maxArrayLength (${ this . maxArrayLength } )` ) ;
558
635
}
559
636
560
- this . stack . push ( {
561
- type : STATE_ARRAY ,
562
- size,
563
- array : new Array < unknown > ( size ) ,
564
- position : 0 ,
565
- } ) ;
637
+ this . stack . pushArrayState ( size ) ;
566
638
}
567
639
568
640
private decodeUtf8String ( byteLength : number , headerOffset : number ) : string {
@@ -589,7 +661,7 @@ export class Decoder<ContextType = undefined> {
589
661
590
662
private stateIsMapKey ( ) : boolean {
591
663
if ( this . stack . length > 0 ) {
592
- const state = this . stack [ this . stack . length - 1 ] ! ;
664
+ const state = this . stack . top ( ) ! ;
593
665
return state . type === STATE_MAP_KEY ;
594
666
}
595
667
return false ;
0 commit comments