15
15
16
16
using System ;
17
17
using System . Collections . Generic ;
18
- using System . Linq ;
19
18
using System . Text ;
20
19
21
20
namespace MongoDB . Bson . IO
@@ -92,7 +91,7 @@ public void Freeze()
92
91
}
93
92
94
93
/// <summary>
95
- /// Gets the value associated with the specifie element name.
94
+ /// Gets the value associated with the specified element name.
96
95
/// </summary>
97
96
/// <param name="elementName">The element name.</param>
98
97
/// <param name="value">
@@ -105,25 +104,24 @@ public bool TryGetValue(string elementName, out TValue value)
105
104
var keyBytes = __utf8Encoding . GetBytes ( elementName ) ;
106
105
107
106
var node = _root ;
108
- foreach ( var keyByte in keyBytes )
107
+ for ( var i = 0 ; i < keyBytes . Length ; i ++ )
109
108
{
110
- node = node . GetChild ( keyByte ) ;
109
+ node = node . GetChild ( keyBytes [ i ] ) ;
111
110
if ( node == null )
112
111
{
113
- break ;
112
+ value = default ( TValue ) ;
113
+ return false ;
114
114
}
115
115
}
116
116
117
- if ( node != null && node . HasValue )
118
- {
119
- value = node . Value ;
120
- return true ;
121
- }
122
- else
117
+ if ( ! node . HasValue )
123
118
{
124
119
value = default ( TValue ) ;
125
120
return false ;
126
121
}
122
+
123
+ value = node . Value ;
124
+ return true ;
127
125
}
128
126
}
129
127
@@ -137,12 +135,11 @@ public sealed class BsonTrieNode<TValue>
137
135
private string _elementName ;
138
136
private TValue _value ;
139
137
private BsonTrieNode < TValue > _onlyChild ; // used when there is only one child
140
- private List < BsonTrieNode < TValue > > _children ; // used when there are two or more children
138
+ private BsonTrieNode < TValue > [ ] _children ; // used when there are two or more children
141
139
142
140
// private fields set when node is frozen
143
141
private bool _isFrozen ;
144
142
private byte _minKeyByte ;
145
- private byte _maxKeyByte ;
146
143
private byte [ ] _keyByteIndexes ; // maps key bytes into indexes into the _children list
147
144
148
145
// constructors
@@ -212,23 +209,19 @@ public BsonTrieNode<TValue> GetChild(byte keyByte)
212
209
}
213
210
else if ( _children != null )
214
211
{
215
- if ( _isFrozen )
212
+ var index = ( uint ) ( ( int ) keyByte - _minKeyByte ) ;
213
+ // enable the .Net CLR to eliminate an array bounds check on _keyByteIndexes
214
+ var keyByteIndexes = _keyByteIndexes ;
215
+ if ( index < keyByteIndexes . Length )
216
216
{
217
- // once the node is frozen we can use a faster lookup based on fields initialized when Freeze was called
218
- if ( keyByte >= _minKeyByte && keyByte <= _maxKeyByte )
217
+ index = keyByteIndexes [ index ] ;
218
+ // enable the .Net CLR to eliminate an array bounds check on _children
219
+ var children = _children ;
220
+ if ( index < children . Length )
219
221
{
220
- var index = _keyByteIndexes [ keyByte - _minKeyByte ] ;
221
- if ( index < _children . Count )
222
- {
223
- return _children [ index ] ;
224
- }
222
+ return children [ index ] ;
225
223
}
226
224
}
227
- else
228
- {
229
- // until the node is frozen do a simple linear scan of the _children list
230
- return _children . Where ( child => child . _keyByte == keyByte ) . SingleOrDefault ( ) ;
231
- }
232
225
}
233
226
return null ;
234
227
}
@@ -237,35 +230,39 @@ public BsonTrieNode<TValue> GetChild(byte keyByte)
237
230
internal void AddChild ( BsonTrieNode < TValue > child )
238
231
{
239
232
if ( _isFrozen ) { throw new InvalidOperationException ( "BsonTrieNode is frozen." ) ; }
240
- if ( _children = = null )
233
+ if ( _children ! = null )
241
234
{
242
- if ( _onlyChild = = null )
235
+ if ( _children [ child . _keyByte ] ! = null )
243
236
{
244
- _onlyChild = child ;
237
+ throw new ArgumentException ( "BsonTrieNode already contains a child with the same keyByte." ) ;
245
238
}
246
- else
239
+
240
+ _children [ child . _keyByte ] = child ;
241
+ }
242
+ else if ( _onlyChild != null )
243
+ {
244
+ if ( _onlyChild . _keyByte == child . _keyByte )
247
245
{
248
- if ( _onlyChild . _keyByte == child . _keyByte )
249
- {
250
- throw new ArgumentException ( "BsonTrieNode already contains a child with the same keyByte" ) ;
251
- }
246
+ throw new ArgumentException ( "BsonTrieNode already contains a child with the same keyByte." ) ;
247
+ }
252
248
253
- var children = new List < BsonTrieNode < TValue > > ( ) ;
254
- children . Add ( _onlyChild ) ;
255
- children . Add ( child ) ;
249
+ var children = new BsonTrieNode < TValue > [ 256 ] ;
250
+ children [ _onlyChild . _keyByte ] = _onlyChild ;
251
+ children [ child . _keyByte ] = child ;
256
252
257
- _onlyChild = null ;
258
- _children = children ;
253
+ var keyByteIndexes = new byte [ 256 ] ;
254
+ for ( var i = 0 ; i < keyByteIndexes . Length ; i ++ )
255
+ {
256
+ keyByteIndexes [ i ] = ( byte ) i ;
259
257
}
258
+
259
+ _keyByteIndexes = keyByteIndexes ;
260
+ _onlyChild = null ;
261
+ _children = children ;
260
262
}
261
263
else
262
264
{
263
- if ( _children . Any ( n => n . _keyByte == child . _keyByte ) )
264
- {
265
- throw new ArgumentException ( "BsonTrieNode already contains a child with the same keyByte" ) ;
266
- }
267
-
268
- _children . Add ( child ) ;
265
+ _onlyChild = child ;
269
266
}
270
267
}
271
268
@@ -279,18 +276,48 @@ internal void Freeze()
279
276
}
280
277
else if ( _children != null )
281
278
{
282
- _children . ForEach ( child => child . Freeze ( ) ) ;
283
- _minKeyByte = _children . Min ( n => n . _keyByte ) ;
284
- _maxKeyByte = _children . Max ( n => n . _keyByte ) ;
285
- _keyByteIndexes = new byte [ _maxKeyByte - _minKeyByte + 1 ] ;
286
- for ( var index = 0 ; index < _keyByteIndexes . Length ; index ++ )
279
+ var i = 0 ;
280
+
281
+ // _children is guaranteed to have at least one element that isn't null
282
+ for ( ; _children [ i ] == null ; i ++ ) ;
283
+
284
+ var minKeyByte = ( byte ) i ;
285
+ var maxKeyByte = minKeyByte ;
286
+
287
+ byte childIndex = 0 ;
288
+
289
+ for ( i ++ ; i < _children . Length ; i ++ )
290
+ {
291
+ if ( _children [ i ] != null )
292
+ {
293
+ maxKeyByte = ( byte ) i ;
294
+ childIndex ++ ;
295
+ }
296
+ }
297
+
298
+ var keyByteIndexes = new byte [ ( int ) maxKeyByte - minKeyByte + 1 ] ;
299
+ for ( i = 0 ; i < keyByteIndexes . Length ; i ++ )
287
300
{
288
- _keyByteIndexes [ index ] = 255 ; // make sure unused entries can be identified
301
+ keyByteIndexes [ i ] = 255 ; // make sure unused entries can be identified
289
302
}
290
- for ( var index = 0 ; index < _children . Count ; index ++ )
303
+
304
+ var children = new BsonTrieNode < TValue > [ ( int ) childIndex + 1 ] ;
305
+ childIndex = 0 ;
306
+ for ( i = 0 ; i < _children . Length ; i ++ )
291
307
{
292
- _keyByteIndexes [ _children [ index ] . _keyByte - _minKeyByte ] = ( byte ) index ;
308
+ var child = _children [ i ] ;
309
+ if ( child != null )
310
+ {
311
+ keyByteIndexes [ child . _keyByte - minKeyByte ] = childIndex ;
312
+ children [ childIndex ] = child ;
313
+ child . Freeze ( ) ;
314
+ childIndex ++ ;
315
+ }
293
316
}
317
+
318
+ _minKeyByte = minKeyByte ;
319
+ _keyByteIndexes = keyByteIndexes ;
320
+ _children = children ;
294
321
}
295
322
_isFrozen = true ;
296
323
}
@@ -306,6 +333,7 @@ internal void SetValue(string elementName, TValue value)
306
333
{
307
334
throw new InvalidOperationException ( "BsonTrieNode already has a value." ) ;
308
335
}
336
+ if ( _isFrozen ) { throw new InvalidOperationException ( "BsonTrieNode is frozen." ) ; }
309
337
310
338
_elementName = elementName ;
311
339
_value = value ;
0 commit comments