BsonTrie.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359
  1. /* Copyright 2010-present 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.IO;
  17. using System.Text;
  18. namespace MongoDB.Bson.IO
  19. {
  20. /// <summary>
  21. /// Represents a mapping from a set of UTF8 encoded strings to a set of elementName/value pairs, implemented as a trie.
  22. /// </summary>
  23. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  24. public class BsonTrie<TValue>
  25. {
  26. // private fields
  27. private readonly BsonTrieNode<TValue> _root;
  28. // constructors
  29. /// <summary>
  30. /// Initializes a new instance of the BsonTrie class.
  31. /// </summary>
  32. public BsonTrie()
  33. {
  34. _root = new BsonTrieNode<TValue>(0);
  35. }
  36. // public properties
  37. /// <summary>
  38. /// Gets the root node.
  39. /// </summary>
  40. public BsonTrieNode<TValue> Root
  41. {
  42. get
  43. {
  44. return _root;
  45. }
  46. }
  47. // public methods
  48. /// <summary>
  49. /// Adds the specified elementName (after encoding as a UTF8 byte sequence) and value to the trie.
  50. /// </summary>
  51. /// <param name="elementName">The element name to add.</param>
  52. /// <param name="value">The value to add. The value can be null for reference types.</param>
  53. public void Add(string elementName, TValue value)
  54. {
  55. var utf8 = Utf8Encodings.Strict.GetBytes(elementName);
  56. var node = _root;
  57. foreach (var keyByte in utf8)
  58. {
  59. var child = node.GetChild(keyByte);
  60. if (child == null)
  61. {
  62. child = new BsonTrieNode<TValue>(keyByte);
  63. node.AddChild(child);
  64. }
  65. node = child;
  66. }
  67. node.SetValue(elementName, value);
  68. }
  69. /// <summary>
  70. /// Gets the node associated with the specified element name.
  71. /// </summary>
  72. /// <param name="utf8">The element name.</param>
  73. /// <param name="node">
  74. /// When this method returns, contains the node associated with the specified element name, if the key is found;
  75. /// otherwise, null. This parameter is passed unitialized.
  76. /// </param>
  77. /// <returns>True if the node was found; otherwise, false.</returns>
  78. public bool TryGetNode(ArraySegment<byte> utf8, out BsonTrieNode<TValue> node)
  79. {
  80. node = _root;
  81. for (var i = 0; node != null && i < utf8.Count; i++)
  82. {
  83. var keyByte = utf8.Array[utf8.Offset + i];
  84. node = node.GetChild(keyByte);
  85. }
  86. return node != null;
  87. }
  88. /// <summary>
  89. /// Tries to get the node associated with a name read from a stream.
  90. /// </summary>
  91. /// <param name="stream">The stream.</param>
  92. /// <param name="node">The node.</param>
  93. /// <returns>
  94. /// True if the node was found.
  95. /// If the node was found the stream is advanced over the name, otherwise
  96. /// the stream is repositioned to the beginning of the name.
  97. /// </returns>
  98. public bool TryGetNode(BsonStream stream, out BsonTrieNode<TValue> node)
  99. {
  100. var position = stream.Position;
  101. var utf8 = stream.ReadCStringBytes();
  102. if (TryGetNode(utf8, out node))
  103. {
  104. return true;
  105. }
  106. stream.Position = position;
  107. return false;
  108. }
  109. /// <summary>
  110. /// Gets the value associated with the specified element name.
  111. /// </summary>
  112. /// <param name="utf8">The element name.</param>
  113. /// <param name="value">
  114. /// When this method returns, contains the value associated with the specified element name, if the key is found;
  115. /// otherwise, the default value for the type of the value parameter. This parameter is passed unitialized.
  116. /// </param>
  117. /// <returns>True if the value was found; otherwise, false.</returns>
  118. public bool TryGetValue(ArraySegment<byte> utf8, out TValue value)
  119. {
  120. BsonTrieNode<TValue> node;
  121. if (TryGetNode(utf8, out node) && node.HasValue)
  122. {
  123. value = node.Value;
  124. return true;
  125. }
  126. value = default(TValue);
  127. return false;
  128. }
  129. /// <summary>
  130. /// Gets the value associated with the specified element name.
  131. /// </summary>
  132. /// <param name="elementName">The element name.</param>
  133. /// <param name="value">
  134. /// When this method returns, contains the value associated with the specified element name, if the key is found;
  135. /// otherwise, the default value for the type of the value parameter. This parameter is passed unitialized.
  136. /// </param>
  137. /// <returns>True if the value was found; otherwise, false.</returns>
  138. public bool TryGetValue(string elementName, out TValue value)
  139. {
  140. var bytes = Utf8Encodings.Strict.GetBytes(elementName);
  141. var utf8 = new ArraySegment<byte>(bytes, 0, bytes.Length);
  142. return TryGetValue(utf8, out value);
  143. }
  144. }
  145. /// <summary>
  146. /// Represents a node in a BsonTrie.
  147. /// </summary>
  148. /// <typeparam name="TValue">The type of the BsonTrie values.</typeparam>
  149. public sealed class BsonTrieNode<TValue>
  150. {
  151. // private fields
  152. private readonly byte _keyByte;
  153. private string _elementName;
  154. private TValue _value;
  155. private BsonTrieNode<TValue> _onlyChild; // used when there is only one child
  156. private BsonTrieNode<TValue>[] _children; // used when there are two or more children
  157. private byte[] _childrenIndexes; // maps key bytes into indexes into the _children array
  158. private byte _minChildKeyByte; // key byte value of first element in _childrenIndexes
  159. // constructors
  160. internal BsonTrieNode(byte keyByte)
  161. {
  162. _keyByte = keyByte;
  163. }
  164. /// <summary>
  165. /// Gets whether this node has a value.
  166. /// </summary>
  167. public bool HasValue
  168. {
  169. get
  170. {
  171. return _elementName != null;
  172. }
  173. }
  174. /// <summary>
  175. /// Gets the element name for this node.
  176. /// </summary>
  177. public string ElementName
  178. {
  179. get
  180. {
  181. if (_elementName == null)
  182. {
  183. throw new InvalidOperationException("BsonTrieNode doesn't have a value.");
  184. }
  185. return _elementName;
  186. }
  187. }
  188. /// <summary>
  189. /// Gets the value for this node.
  190. /// </summary>
  191. public TValue Value
  192. {
  193. get
  194. {
  195. if (_elementName == null)
  196. {
  197. throw new InvalidOperationException("BsonTrieNode doesn't have a value.");
  198. }
  199. return _value;
  200. }
  201. }
  202. // public methods
  203. /// <summary>
  204. /// Gets the child of this node for a given key byte.
  205. /// </summary>
  206. /// <param name="keyByte">The key byte.</param>
  207. /// <returns>The child node if it exists; otherwise, null.</returns>
  208. public BsonTrieNode<TValue> GetChild(byte keyByte)
  209. {
  210. if (_onlyChild != null)
  211. {
  212. // optimization for nodes that have only one child
  213. if (_onlyChild._keyByte == keyByte)
  214. {
  215. return _onlyChild;
  216. }
  217. }
  218. else if (_children != null)
  219. {
  220. // 这里做了修改,il2cpp uint跟int比较有bug
  221. int index = keyByte - _minChildKeyByte;
  222. if (index < 0)
  223. {
  224. return null;
  225. }
  226. if (index < _childrenIndexes.Length)
  227. {
  228. index = _childrenIndexes[index];
  229. if (index < _children.Length)
  230. {
  231. return _children[index];
  232. }
  233. }
  234. }
  235. return null;
  236. }
  237. // internal methods
  238. internal void AddChild(BsonTrieNode<TValue> child)
  239. {
  240. if (GetChild(child._keyByte) != null)
  241. {
  242. throw new ArgumentException("BsonTrieNode already contains a child with the same keyByte.");
  243. }
  244. if (_children != null)
  245. {
  246. // add a new child to the existing _children
  247. var children = new BsonTrieNode<TValue>[_children.Length + 1];
  248. Array.Copy(_children, children, _children.Length);
  249. children[children.Length - 1] = child;
  250. var childrenIndexes = _childrenIndexes;
  251. var minChildKeyByte = _minChildKeyByte;
  252. var maxChildKeyByte = _minChildKeyByte + _childrenIndexes.Length - 1;
  253. // if new keyByte doesn't fall within existing min/max range expand the range
  254. if (child._keyByte < minChildKeyByte)
  255. {
  256. // grow the indexes on the min side
  257. minChildKeyByte = child._keyByte;
  258. childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  259. var sizeDelta = childrenIndexes.Length - _childrenIndexes.Length;
  260. for (var i = 0; i < sizeDelta; i++)
  261. {
  262. childrenIndexes[i] = 255;
  263. }
  264. Array.Copy(_childrenIndexes, 0, childrenIndexes, sizeDelta, _childrenIndexes.Length);
  265. }
  266. else if (child._keyByte > maxChildKeyByte)
  267. {
  268. // grow the indexes on the max side
  269. maxChildKeyByte = child._keyByte;
  270. childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  271. Array.Copy(_childrenIndexes, 0, childrenIndexes, 0, _childrenIndexes.Length);
  272. for (var i = _childrenIndexes.Length; i < childrenIndexes.Length; i++)
  273. {
  274. childrenIndexes[i] = 255;
  275. }
  276. }
  277. childrenIndexes[child._keyByte - minChildKeyByte] = (byte)(children.Length - 1);
  278. _children = children;
  279. _childrenIndexes = childrenIndexes;
  280. _minChildKeyByte = minChildKeyByte;
  281. }
  282. else if (_onlyChild != null)
  283. {
  284. // switch from having an _onlyChild to having two _children
  285. var children = new BsonTrieNode<TValue>[2];
  286. children[0] = _onlyChild;
  287. children[1] = child;
  288. var minChildKeyByte = _onlyChild._keyByte;
  289. var maxChildKeyByte = child._keyByte;
  290. if (minChildKeyByte > maxChildKeyByte)
  291. {
  292. minChildKeyByte = child._keyByte;
  293. maxChildKeyByte = _onlyChild._keyByte;
  294. }
  295. var childrenIndexes = new byte[maxChildKeyByte - minChildKeyByte + 1];
  296. for (var i = 0; i < childrenIndexes.Length; i++)
  297. {
  298. childrenIndexes[i] = 255;
  299. }
  300. childrenIndexes[_onlyChild._keyByte - minChildKeyByte] = 0;
  301. childrenIndexes[child._keyByte - minChildKeyByte] = 1;
  302. _onlyChild = null;
  303. _children = children;
  304. _childrenIndexes = childrenIndexes;
  305. _minChildKeyByte = minChildKeyByte;
  306. }
  307. else
  308. {
  309. _onlyChild = child;
  310. }
  311. }
  312. internal void SetValue(string elementName, TValue value)
  313. {
  314. if (elementName == null)
  315. {
  316. throw new ArgumentNullException("elementName");
  317. }
  318. if (_elementName != null)
  319. {
  320. throw new InvalidOperationException("BsonTrieNode already has a value.");
  321. }
  322. _elementName = elementName;
  323. _value = value;
  324. }
  325. }
  326. }