@@ -39,14 +39,20 @@ export type JSTypeOf = {
3939 [ BSONType . array ] : OnDemandDocument ;
4040} ;
4141
42+ /** @internal */
43+ type CachedBSONElement = { element : BSONElement ; value : any | undefined } ;
44+
4245/** @internal */
4346export class OnDemandDocument {
44- /** Caches the existence of a property */
45- private readonly existenceOf : Record < string , boolean > = Object . create ( null ) ;
46- /** Caches a look up of name to element */
47- private readonly elementOf : Record < string , BSONElement > = Object . create ( null ) ;
48- /** Caches the revived javascript value */
49- private readonly valueOf : Record < string , any > = Object . create ( null ) ;
47+ /**
48+ * Maps JS strings to elements and jsValues for speeding up subsequent lookups.
49+ * - If `false` then name does not exist in the BSON document
50+ * - If `CachedBSONElement` instance name exists
51+ * - If `cache[name].value == null` jsValue has not yet been parsed
52+ * - Null/Undefined values do not get cached because they are zero-length values.
53+ */
54+ private readonly cache : Record < string , CachedBSONElement | false | undefined > =
55+ Object . create ( null ) ;
5056 /** Caches the index of elements that have been named */
5157 private readonly indexFound : Record < number , boolean > = Object . create ( null ) ;
5258
@@ -90,29 +96,27 @@ export class OnDemandDocument {
9096 * @param name - a basic latin string name of a BSON element
9197 * @returns
9298 */
93- private getElement ( name : string ) : BSONElement | null {
94- if ( this . existenceOf [ name ] === false ) return null ;
99+ private getElement ( name : string ) : CachedBSONElement | null {
100+ const cachedElement = this . cache [ name ] ;
101+ if ( cachedElement === false ) return null ;
95102
96- if ( this . elementOf [ name ] != null ) {
97- return this . elementOf [ name ] ;
103+ if ( cachedElement != null ) {
104+ return cachedElement ;
98105 }
99106
100107 for ( let index = 0 ; index < this . elements . length ; index ++ ) {
101108 const element = this . elements [ index ] ;
102109
103- if (
104- // skip this element if it has already been associated with a name
105- ! this . indexFound [ index ] &&
106- this . isElementName ( name , element )
107- ) {
108- this . elementOf [ name ] = element ;
110+ // skip this element if it has already been associated with a name
111+ if ( ! this . indexFound [ index ] && this . isElementName ( name , element ) ) {
112+ const cachedElement = { element, value : undefined } ;
113+ this . cache [ name ] = cachedElement ;
109114 this . indexFound [ index ] = true ;
110- this . existenceOf [ name ] = true ;
111- return this . elementOf [ name ] ;
115+ return cachedElement ;
112116 }
113117 }
114118
115- this . existenceOf [ name ] = false ;
119+ this . cache [ name ] = false ;
116120 return null ;
117121 }
118122
@@ -202,7 +206,10 @@ export class OnDemandDocument {
202206 * @param name - element name
203207 */
204208 public has ( name : string ) : boolean {
205- return ( this . existenceOf [ name ] ??= this . getElement ( name ) != null ) ;
209+ const cachedElement = this . cache [ name ] ;
210+ if ( cachedElement === false ) return false ;
211+ if ( cachedElement != null ) return true ;
212+ return this . getElement ( name ) != null ;
206213 }
207214
208215 /**
@@ -236,8 +243,8 @@ export class OnDemandDocument {
236243 }
237244 }
238245
239- if ( ! ( name in this . valueOf ) ) {
240- const value = this . toJSValue ( element , as ) ;
246+ if ( element . value == null ) {
247+ const value = this . toJSValue ( element . element , as ) ;
241248 if ( value == null ) {
242249 if ( required === true ) {
243250 throw new BSONError ( `BSON element "${ name } " is missing` ) ;
@@ -246,10 +253,10 @@ export class OnDemandDocument {
246253 }
247254 }
248255 // It is important to never store null
249- this . valueOf [ name ] = value ;
256+ element . value = value ;
250257 }
251258
252- return this . valueOf [ name ] ;
259+ return element . value ;
253260 }
254261
255262 /**
@@ -306,9 +313,9 @@ export class OnDemandDocument {
306313 }
307314 let counter = 0 ;
308315 for ( const element of this . elements ) {
309- const item = this . toJSValue < T > ( element , as ) ;
310- this . valueOf [ counter ] = item ;
311- yield item ;
316+ const value = this . toJSValue < T > ( element , as ) ;
317+ this . cache [ counter ] = { element , value } ;
318+ yield value ;
312319 counter += 1 ;
313320 }
314321 }
0 commit comments