@@ -39,14 +39,20 @@ export type JSTypeOf = {
39
39
[ BSONType . array ] : OnDemandDocument ;
40
40
} ;
41
41
42
+ /** @internal */
43
+ type CachedBSONElement = { element : BSONElement ; value : any | undefined } ;
44
+
42
45
/** @internal */
43
46
export 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 ) ;
50
56
/** Caches the index of elements that have been named */
51
57
private readonly indexFound : Record < number , boolean > = Object . create ( null ) ;
52
58
@@ -90,29 +96,27 @@ export class OnDemandDocument {
90
96
* @param name - a basic latin string name of a BSON element
91
97
* @returns
92
98
*/
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 ;
95
102
96
- if ( this . elementOf [ name ] != null ) {
97
- return this . elementOf [ name ] ;
103
+ if ( cachedElement != null ) {
104
+ return cachedElement ;
98
105
}
99
106
100
107
for ( let index = 0 ; index < this . elements . length ; index ++ ) {
101
108
const element = this . elements [ index ] ;
102
109
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 ;
109
114
this . indexFound [ index ] = true ;
110
- this . existenceOf [ name ] = true ;
111
- return this . elementOf [ name ] ;
115
+ return cachedElement ;
112
116
}
113
117
}
114
118
115
- this . existenceOf [ name ] = false ;
119
+ this . cache [ name ] = false ;
116
120
return null ;
117
121
}
118
122
@@ -202,7 +206,10 @@ export class OnDemandDocument {
202
206
* @param name - element name
203
207
*/
204
208
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 ;
206
213
}
207
214
208
215
/**
@@ -236,8 +243,8 @@ export class OnDemandDocument {
236
243
}
237
244
}
238
245
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 ) ;
241
248
if ( value == null ) {
242
249
if ( required === true ) {
243
250
throw new BSONError ( `BSON element "${ name } " is missing` ) ;
@@ -246,10 +253,10 @@ export class OnDemandDocument {
246
253
}
247
254
}
248
255
// It is important to never store null
249
- this . valueOf [ name ] = value ;
256
+ element . value = value ;
250
257
}
251
258
252
- return this . valueOf [ name ] ;
259
+ return element . value ;
253
260
}
254
261
255
262
/**
@@ -306,9 +313,9 @@ export class OnDemandDocument {
306
313
}
307
314
let counter = 0 ;
308
315
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 ;
312
319
counter += 1 ;
313
320
}
314
321
}
0 commit comments