@@ -86,6 +86,89 @@ type StackArrayState = {
8686 position : number ;
8787} ;
8888
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+
89172type StackState = StackArrayState | StackMapState ;
90173
91174const HEAD_BYTE_REQUIRED = - 1 ;
@@ -125,7 +208,7 @@ export class Decoder<ContextType = undefined> {
125208 private view = EMPTY_VIEW ;
126209 private bytes = EMPTY_BYTES ;
127210 private headByte = HEAD_BYTE_REQUIRED ;
128- private readonly stack : Array < StackState > = [ ] ;
211+ private readonly stack = new StackPool ( ) ;
129212
130213 public constructor ( options ?: DecoderOptions < ContextType > ) {
131214 this . extensionCodec = options ?. extensionCodec ?? ( ExtensionCodec . defaultCodec as ExtensionCodecType < ContextType > ) ;
@@ -143,7 +226,7 @@ export class Decoder<ContextType = undefined> {
143226 private reinitializeState ( ) {
144227 this . totalPos = 0 ;
145228 this . headByte = HEAD_BYTE_REQUIRED ;
146- this . stack . length = 0 ;
229+ this . stack . reset ( ) ;
147230
148231 // view, bytes, and pos will be re-initialized in setBuffer()
149232 }
@@ -465,13 +548,13 @@ export class Decoder<ContextType = undefined> {
465548 const stack = this . stack ;
466549 while ( stack . length > 0 ) {
467550 // arrays and maps
468- const state = stack [ stack . length - 1 ] ! ;
551+ const state = stack . top ( ) ! ;
469552 if ( state . type === STATE_ARRAY ) {
470553 state . array [ state . position ] = object ;
471554 state . position ++ ;
472555 if ( state . position === state . size ) {
473- stack . pop ( ) ;
474556 object = state . array ;
557+ stack . release ( state ) ;
475558 } else {
476559 continue DECODE;
477560 }
@@ -493,8 +576,8 @@ export class Decoder<ContextType = undefined> {
493576 state . readCount ++ ;
494577
495578 if ( state . readCount === state . size ) {
496- stack . pop ( ) ;
497579 object = state . map ;
580+ stack . release ( state ) ;
498581 } else {
499582 state . key = null ;
500583 state . type = STATE_MAP_KEY ;
@@ -543,26 +626,15 @@ export class Decoder<ContextType = undefined> {
543626 throw new DecodeError ( `Max length exceeded: map length (${ size } ) > maxMapLengthLength (${ this . maxMapLength } )` ) ;
544627 }
545628
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 ) ;
553630 }
554631
555632 private pushArrayState ( size : number ) {
556633 if ( size > this . maxArrayLength ) {
557634 throw new DecodeError ( `Max length exceeded: array length (${ size } ) > maxArrayLength (${ this . maxArrayLength } )` ) ;
558635 }
559636
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 ) ;
566638 }
567639
568640 private decodeUtf8String ( byteLength : number , headerOffset : number ) : string {
@@ -589,7 +661,7 @@ export class Decoder<ContextType = undefined> {
589661
590662 private stateIsMapKey ( ) : boolean {
591663 if ( this . stack . length > 0 ) {
592- const state = this . stack [ this . stack . length - 1 ] ! ;
664+ const state = this . stack . top ( ) ! ;
593665 return state . type === STATE_MAP_KEY ;
594666 }
595667 return false ;
0 commit comments