Skip to content

Commit 2d3832e

Browse files
author
rstam
committed
Change BsonTrieNode to eliminate the need for Freeze altogether. Instead, the trie representation is kept as compact as possible at all times. While this does slow down AddChild, we know that tries are created once and then used thousands of times so we are less concerned about overhead of creation.
1 parent b36ad30 commit 2d3832e

File tree

3 files changed

+64
-97
lines changed

3 files changed

+64
-97
lines changed

Bson/IO/BsonTrie.cs

Lines changed: 64 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,6 @@ public class BsonTrie<TValue>
2929

3030
// private fields
3131
private readonly BsonTrieNode<TValue> _root;
32-
private bool _isFrozen;
3332

3433
// constructors
3534
/// <summary>
@@ -60,7 +59,6 @@ public BsonTrieNode<TValue> Root
6059
/// <param name="value">The value to add. The value can be null for reference types.</param>
6160
public void Add(string elementName, TValue value)
6261
{
63-
if (_isFrozen) { throw new InvalidOperationException("BsonTrie is frozen."); }
6462
var keyBytes = __utf8Encoding.GetBytes(elementName);
6563

6664
var node = _root;
@@ -78,18 +76,6 @@ public void Add(string elementName, TValue value)
7876
node.SetValue(elementName, value);
7977
}
8078

81-
/// <summary>
82-
/// Freezes the BsonTrie and optimizes the nodes included so far for faster retrieval.
83-
/// </summary>
84-
public void Freeze()
85-
{
86-
if (!_isFrozen)
87-
{
88-
_root.Freeze();
89-
_isFrozen = true;
90-
}
91-
}
92-
9379
/// <summary>
9480
/// Gets the value associated with the specified element name.
9581
/// </summary>
@@ -136,11 +122,8 @@ public sealed class BsonTrieNode<TValue>
136122
private TValue _value;
137123
private BsonTrieNode<TValue> _onlyChild; // used when there is only one child
138124
private BsonTrieNode<TValue>[] _children; // used when there are two or more children
139-
140-
// private fields set when node is frozen
141-
private bool _isFrozen;
142-
private byte _minKeyByte;
143-
private byte[] _keyByteIndexes; // maps key bytes into indexes into the _children list
125+
private byte[] _childrenIndexes; // maps key bytes into indexes into the _children array
126+
private byte _minChildKeyByte; // key byte value of first element in _childrenIndexes
144127

145128
// constructors
146129
internal BsonTrieNode(byte keyByte)
@@ -209,12 +192,12 @@ public BsonTrieNode<TValue> GetChild(byte keyByte)
209192
}
210193
else if (_children != null)
211194
{
212-
var index = (uint)((int)keyByte - _minKeyByte);
195+
var index = (uint)((int)keyByte - _minChildKeyByte);
213196
// enable the .Net CLR to eliminate an array bounds check on _keyByteIndexes
214-
var keyByteIndexes = _keyByteIndexes;
215-
if (index < keyByteIndexes.Length)
197+
var childrenIndexes = _childrenIndexes;
198+
if (index < childrenIndexes.Length)
216199
{
217-
index = keyByteIndexes[index];
200+
index = childrenIndexes[index];
218201
// enable the .Net CLR to eliminate an array bounds check on _children
219202
var children = _children;
220203
if (index < children.Length)
@@ -229,100 +212,87 @@ public BsonTrieNode<TValue> GetChild(byte keyByte)
229212
// internal methods
230213
internal void AddChild(BsonTrieNode<TValue> child)
231214
{
232-
if (_isFrozen) { throw new InvalidOperationException("BsonTrieNode is frozen."); }
215+
if (GetChild(child._keyByte) != null)
216+
{
217+
throw new ArgumentException("BsonTrieNode already contains a child with the same keyByte.");
218+
}
219+
233220
if (_children != null)
234221
{
235-
if (_children[child._keyByte] != null)
222+
// add a new child to the existing _children
223+
var children = new BsonTrieNode<TValue>[_children.Length + 1];
224+
Array.Copy(_children, children, _children.Length);
225+
children[children.Length - 1] = child;
226+
227+
var childrenIndexes = _childrenIndexes;
228+
var minChildKeyByte = _minChildKeyByte;
229+
var maxChildKeyByte = _minChildKeyByte + _childrenIndexes.Length - 1;
230+
231+
// if new keyByte doesn't fall within existing min/max range expand the range
232+
if (child._keyByte < minChildKeyByte)
236233
{
237-
throw new ArgumentException("BsonTrieNode already contains a child with the same keyByte.");
234+
// grow the indexes on the min side
235+
minChildKeyByte = child._keyByte;
236+
childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
237+
var sizeDelta = childrenIndexes.Length - _childrenIndexes.Length;
238+
for (var i = 0; i < sizeDelta; i++)
239+
{
240+
childrenIndexes[i] = 255;
241+
}
242+
Array.Copy(_childrenIndexes, 0, childrenIndexes, sizeDelta, _childrenIndexes.Length);
238243
}
244+
else if (child._keyByte > maxChildKeyByte)
245+
{
246+
// grow the indexes on the max side
247+
maxChildKeyByte = child._keyByte;
248+
childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
249+
var sizeDelta = childrenIndexes.Length - _childrenIndexes.Length;
250+
Array.Copy(_childrenIndexes, 0, childrenIndexes, 0, _childrenIndexes.Length);
251+
for (var i = _childrenIndexes.Length; i < childrenIndexes.Length; i++)
252+
{
253+
childrenIndexes[i] = 255;
254+
}
255+
}
256+
childrenIndexes[child._keyByte - minChildKeyByte] = (byte)(children.Length - 1);
239257

240-
_children[child._keyByte] = child;
258+
_children = children;
259+
_childrenIndexes = childrenIndexes;
260+
_minChildKeyByte = minChildKeyByte;
241261
}
242262
else if (_onlyChild != null)
243263
{
244-
if (_onlyChild._keyByte == child._keyByte)
264+
// switch from having an _onlyChild to having two _children
265+
var children = new BsonTrieNode<TValue>[2];
266+
children[0] = _onlyChild;
267+
children[1] = child;
268+
269+
var minChildKeyByte = _onlyChild._keyByte;
270+
var maxChildKeyByte = child._keyByte;
271+
if (minChildKeyByte > maxChildKeyByte)
245272
{
246-
throw new ArgumentException("BsonTrieNode already contains a child with the same keyByte.");
273+
minChildKeyByte = child._keyByte;
274+
maxChildKeyByte = _onlyChild._keyByte;
247275
}
248276

249-
var children = new BsonTrieNode<TValue>[256];
250-
children[_onlyChild._keyByte] = _onlyChild;
251-
children[child._keyByte] = child;
252-
253-
var keyByteIndexes = new byte[256];
254-
for (var i = 0; i < keyByteIndexes.Length; i++)
277+
var childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
278+
for (var i = 0; i < childrenIndexes.Length; i++)
255279
{
256-
keyByteIndexes[i] = (byte)i;
280+
childrenIndexes[i] = 255;
257281
}
282+
childrenIndexes[_onlyChild._keyByte - minChildKeyByte] = 0;
283+
childrenIndexes[child._keyByte - minChildKeyByte] = 1;
258284

259-
_keyByteIndexes = keyByteIndexes;
260285
_onlyChild = null;
261286
_children = children;
287+
_childrenIndexes = childrenIndexes;
288+
_minChildKeyByte = minChildKeyByte;
262289
}
263290
else
264291
{
265292
_onlyChild = child;
266293
}
267294
}
268295

269-
internal void Freeze()
270-
{
271-
if (!_isFrozen)
272-
{
273-
if (_onlyChild != null)
274-
{
275-
_onlyChild.Freeze();
276-
}
277-
else if (_children != null)
278-
{
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++)
300-
{
301-
keyByteIndexes[i] = 255; // make sure unused entries can be identified
302-
}
303-
304-
var children = new BsonTrieNode<TValue>[(int)childIndex + 1];
305-
childIndex = 0;
306-
for (i = 0; i < _children.Length; i++)
307-
{
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-
}
316-
}
317-
318-
_minKeyByte = minKeyByte;
319-
_keyByteIndexes = keyByteIndexes;
320-
_children = children;
321-
}
322-
_isFrozen = true;
323-
}
324-
}
325-
326296
internal void SetValue(string elementName, TValue value)
327297
{
328298
if (elementName == null)
@@ -333,7 +303,6 @@ internal void SetValue(string elementName, TValue value)
333303
{
334304
throw new InvalidOperationException("BsonTrieNode already has a value.");
335305
}
336-
if (_isFrozen) { throw new InvalidOperationException("BsonTrieNode is frozen."); }
337306

338307
_elementName = elementName;
339308
_value = value;

Bson/Serialization/BsonClassMap.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -578,7 +578,6 @@ public BsonClassMap Freeze()
578578
_extraElementsMemberIndex = memberIndex;
579579
}
580580
}
581-
_elementTrie.Freeze();
582581

583582
// mark this classMap frozen before we start working on knownTypes
584583
// because we might get back to this same classMap while processing knownTypes

Bson/Serialization/Serializers/KeyValuePairSerializer.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,6 @@ private static BsonTrie<bool> BuildBsonTrie()
206206
var bsonTrie = new BsonTrie<bool>();
207207
bsonTrie.Add("k", true); // is key
208208
bsonTrie.Add("v", false);
209-
bsonTrie.Freeze();
210209
return bsonTrie;
211210
}
212211

0 commit comments

Comments
 (0)