BsonTrie.cs 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. /* Copyright 2010-2014 MongoDB Inc.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. */
  15. using System;
  16. using System.Text;
  17. namespace MongoDB.Bson.IO
  18. {
  19. /// <summary>
  20. /// Represents a mapping from a set of UTF8 encoded strings to a set of elementName/value pairs, implemented as a trie.
  21. /// </summary>
  22. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  23. public class BsonTrie<TValue>
  24. {
  25. // private static fields
  26. private static readonly UTF8Encoding __utf8Encoding = new UTF8Encoding(false, true); // throw on invalid bytes
  27. // private fields
  28. private readonly BsonTrieNode<TValue> _root;
  29. // constructors
  30. /// <summary>
  31. /// Initializes a new instance of the BsonTrie class.
  32. /// </summary>
  33. public BsonTrie()
  34. {
  35. _root = new BsonTrieNode<TValue>(0);
  36. }
  37. // public properties
  38. /// <summary>
  39. /// Gets the root node.
  40. /// </summary>
  41. public BsonTrieNode<TValue> Root
  42. {
  43. get
  44. {
  45. return _root;
  46. }
  47. }
  48. // public methods
  49. /// <summary>
  50. /// Adds the specified elementName (after encoding as a UTF8 byte sequence) and value to the trie.
  51. /// </summary>
  52. /// <param name="elementName">The element name to add.</param>
  53. /// <param name="value">The value to add. The value can be null for reference types.</param>
  54. public void Add(string elementName, TValue value)
  55. {
  56. var keyBytes = __utf8Encoding.GetBytes(elementName);
  57. var node = _root;
  58. foreach (var keyByte in keyBytes)
  59. {
  60. var child = node.GetChild(keyByte);
  61. if (child == null)
  62. {
  63. child = new BsonTrieNode<TValue>(keyByte);
  64. node.AddChild(child);
  65. }
  66. node = child;
  67. }
  68. node.SetValue(elementName, value);
  69. }
  70. /// <summary>
  71. /// Gets the value associated with the specified element name.
  72. /// </summary>
  73. /// <param name="elementName">The element name.</param>
  74. /// <param name="value">
  75. /// When this method returns, contains the value associated with the specified element name, if the key is found;
  76. /// otherwise, the default value for the type of the value parameter. This parameter is passed unitialized.
  77. /// </param>
  78. /// <returns>True if the value was found; otherwise, false.</returns>
  79. public bool TryGetValue(string elementName, out TValue value)
  80. {
  81. var keyBytes = __utf8Encoding.GetBytes(elementName);
  82. var node = _root;
  83. for (var i = 0; i < keyBytes.Length; i++)
  84. {
  85. node = node.GetChild(keyBytes[i]);
  86. if (node == null)
  87. {
  88. value = default(TValue);
  89. return false;
  90. }
  91. }
  92. if (!node.HasValue)
  93. {
  94. value = default(TValue);
  95. return false;
  96. }
  97. value = node.Value;
  98. return true;
  99. }
  100. }
  101. /// <summary>
  102. /// Represents a node in a BsonTrie.
  103. /// </summary>
  104. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  105. public sealed class BsonTrieNode<TValue>
  106. {
  107. // private fields
  108. private readonly byte _keyByte;
  109. private string _elementName;
  110. private TValue _value;
  111. private BsonTrieNode<TValue> _onlyChild; // used when there is only one child
  112. private BsonTrieNode<TValue>[] _children; // used when there are two or more children
  113. private byte[] _childrenIndexes; // maps key bytes into indexes into the _children array
  114. private byte _minChildKeyByte; // key byte value of first element in _childrenIndexes
  115. // constructors
  116. internal BsonTrieNode(byte keyByte)
  117. {
  118. _keyByte = keyByte;
  119. }
  120. /// <summary>
  121. /// Gets whether this node has a value.
  122. /// </summary>
  123. public bool HasValue
  124. {
  125. get
  126. {
  127. return _elementName != null;
  128. }
  129. }
  130. /// <summary>
  131. /// Gets the element name for this node.
  132. /// </summary>
  133. public string ElementName
  134. {
  135. get
  136. {
  137. if (_elementName == null)
  138. {
  139. throw new InvalidOperationException("BsonTrieNode doesn't have a value.");
  140. }
  141. return _elementName;
  142. }
  143. }
  144. /// <summary>
  145. /// Gets the value for this node.
  146. /// </summary>
  147. public TValue Value
  148. {
  149. get
  150. {
  151. if (_elementName == null)
  152. {
  153. throw new InvalidOperationException("BsonTrieNode doesn't have a value.");
  154. }
  155. return _value;
  156. }
  157. }
  158. // public methods
  159. /// <summary>
  160. /// Gets the child of this node for a given key byte.
  161. /// </summary>
  162. /// <param name="keyByte">The key byte.</param>
  163. /// <returns>The child node if it exists; otherwise, null.</returns>
  164. public BsonTrieNode<TValue> GetChild(byte keyByte)
  165. {
  166. if (_onlyChild != null)
  167. {
  168. // optimization for nodes that have only one child
  169. if (_onlyChild._keyByte == keyByte)
  170. {
  171. return _onlyChild;
  172. }
  173. }
  174. else if (_children != null)
  175. {
  176. var index = (uint)((int)keyByte - _minChildKeyByte);
  177. if (index < _childrenIndexes.Length)
  178. {
  179. index = _childrenIndexes[index];
  180. if (index < _children.Length)
  181. {
  182. return _children[index];
  183. }
  184. }
  185. }
  186. return null;
  187. }
  188. // internal methods
  189. internal void AddChild(BsonTrieNode<TValue> child)
  190. {
  191. if (GetChild(child._keyByte) != null)
  192. {
  193. throw new ArgumentException("BsonTrieNode already contains a child with the same keyByte.");
  194. }
  195. if (_children != null)
  196. {
  197. // add a new child to the existing _children
  198. var children = new BsonTrieNode<TValue>[_children.Length + 1];
  199. Array.Copy(_children, children, _children.Length);
  200. children[children.Length - 1] = child;
  201. var childrenIndexes = _childrenIndexes;
  202. var minChildKeyByte = _minChildKeyByte;
  203. var maxChildKeyByte = _minChildKeyByte + _childrenIndexes.Length - 1;
  204. // if new keyByte doesn't fall within existing min/max range expand the range
  205. if (child._keyByte < minChildKeyByte)
  206. {
  207. // grow the indexes on the min side
  208. minChildKeyByte = child._keyByte;
  209. childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  210. var sizeDelta = childrenIndexes.Length - _childrenIndexes.Length;
  211. for (var i = 0; i < sizeDelta; i++)
  212. {
  213. childrenIndexes[i] = 255;
  214. }
  215. Array.Copy(_childrenIndexes, 0, childrenIndexes, sizeDelta, _childrenIndexes.Length);
  216. }
  217. else if (child._keyByte > maxChildKeyByte)
  218. {
  219. // grow the indexes on the max side
  220. maxChildKeyByte = child._keyByte;
  221. childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  222. Array.Copy(_childrenIndexes, 0, childrenIndexes, 0, _childrenIndexes.Length);
  223. for (var i = _childrenIndexes.Length; i < childrenIndexes.Length; i++)
  224. {
  225. childrenIndexes[i] = 255;
  226. }
  227. }
  228. childrenIndexes[child._keyByte - minChildKeyByte] = (byte)(children.Length - 1);
  229. _children = children;
  230. _childrenIndexes = childrenIndexes;
  231. _minChildKeyByte = minChildKeyByte;
  232. }
  233. else if (_onlyChild != null)
  234. {
  235. // switch from having an _onlyChild to having two _children
  236. var children = new BsonTrieNode<TValue>[2];
  237. children[0] = _onlyChild;
  238. children[1] = child;
  239. var minChildKeyByte = _onlyChild._keyByte;
  240. var maxChildKeyByte = child._keyByte;
  241. if (minChildKeyByte > maxChildKeyByte)
  242. {
  243. minChildKeyByte = child._keyByte;
  244. maxChildKeyByte = _onlyChild._keyByte;
  245. }
  246. var childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  247. for (var i = 0; i < childrenIndexes.Length; i++)
  248. {
  249. childrenIndexes[i] = 255;
  250. }
  251. childrenIndexes[_onlyChild._keyByte - minChildKeyByte] = 0;
  252. childrenIndexes[child._keyByte - minChildKeyByte] = 1;
  253. _onlyChild = null;
  254. _children = children;
  255. _childrenIndexes = childrenIndexes;
  256. _minChildKeyByte = minChildKeyByte;
  257. }
  258. else
  259. {
  260. _onlyChild = child;
  261. }
  262. }
  263. internal void SetValue(string elementName, TValue value)
  264. {
  265. if (elementName == null)
  266. {
  267. throw new ArgumentNullException("elementName");
  268. }
  269. if (_elementName != null)
  270. {
  271. throw new InvalidOperationException("BsonTrieNode already has a value.");
  272. }
  273. _elementName = elementName;
  274. _value = value;
  275. }
  276. }
  277. }