/AvalonEdit/ICSharpCode.AvalonEdit/Utils/RopeNode.cs

http://github.com/icsharpcode/ILSpy · C# · 620 lines · 455 code · 53 blank · 112 comment · 141 complexity · aa07f4173287c9f43bda01bccee577bc MD5 · raw file

  1. // Copyright (c) 2014 AlphaSierraPapa for the SharpDevelop Team
  2. //
  3. // Permission is hereby granted, free of charge, to any person obtaining a copy of this
  4. // software and associated documentation files (the "Software"), to deal in the Software
  5. // without restriction, including without limitation the rights to use, copy, modify, merge,
  6. // publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons
  7. // to whom the Software is furnished to do so, subject to the following conditions:
  8. //
  9. // The above copyright notice and this permission notice shall be included in all copies or
  10. // substantial portions of the Software.
  11. //
  12. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
  13. // INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  14. // PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
  15. // FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  16. // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  17. // DEALINGS IN THE SOFTWARE.
  18. using System;
  19. using System.Diagnostics;
  20. using System.Runtime.Serialization;
  21. using System.Text;
  22. namespace ICSharpCode.AvalonEdit.Utils
  23. {
  24. // Class used to represent a node in the tree.
  25. // There are three types of nodes:
  26. // Concat nodes: height>0, left!=null, right!=null, contents==null
  27. // Leaf nodes: height==0, left==null, right==null, contents!=null
  28. // Function nodes: height==0, left==null, right==null, contents==null, are of type FunctionNode<T>
  29. [Serializable]
  30. class RopeNode<T>
  31. {
  32. internal const int NodeSize = 256;
  33. internal static readonly RopeNode<T> emptyRopeNode = new RopeNode<T> { isShared = true, contents = new T[RopeNode<T>.NodeSize] };
  34. // Fields for pointers to sub-nodes. Only non-null for concat nodes (height>=1)
  35. internal RopeNode<T> left, right;
  36. internal volatile bool isShared; // specifies whether this node is shared between multiple ropes
  37. // the total length of all text in this subtree
  38. internal int length;
  39. // the height of this subtree: 0 for leaf nodes; 1+max(left.height,right.height) for concat nodes
  40. internal byte height;
  41. // The character data. Only non-null for leaf nodes (height=0) that aren't function nodes.
  42. internal T[] contents;
  43. internal int Balance {
  44. get { return right.height - left.height; }
  45. }
  46. [Conditional("DATACONSISTENCYTEST")]
  47. internal void CheckInvariants()
  48. {
  49. if (height == 0) {
  50. Debug.Assert(left == null && right == null);
  51. if (contents == null) {
  52. Debug.Assert(this is FunctionNode<T>);
  53. Debug.Assert(length > 0);
  54. Debug.Assert(isShared);
  55. } else {
  56. Debug.Assert(contents != null && contents.Length == NodeSize);
  57. Debug.Assert(length >= 0 && length <= NodeSize);
  58. }
  59. } else {
  60. Debug.Assert(left != null && right != null);
  61. Debug.Assert(contents == null);
  62. Debug.Assert(length == left.length + right.length);
  63. Debug.Assert(height == 1 + Math.Max(left.height, right.height));
  64. Debug.Assert(Math.Abs(this.Balance) <= 1);
  65. // this is an additional invariant that forces the tree to combine small leafs to prevent excessive memory usage:
  66. Debug.Assert(length > NodeSize);
  67. // note that this invariant ensures that all nodes except for the empty rope's single node have at least length 1
  68. if (isShared)
  69. Debug.Assert(left.isShared && right.isShared);
  70. left.CheckInvariants();
  71. right.CheckInvariants();
  72. }
  73. }
  74. internal RopeNode<T> Clone()
  75. {
  76. if (height == 0) {
  77. // If a function node needs cloning, we'll evaluate it.
  78. if (contents == null)
  79. return GetContentNode().Clone();
  80. T[] newContents = new T[NodeSize];
  81. contents.CopyTo(newContents, 0);
  82. return new RopeNode<T> {
  83. length = this.length,
  84. contents = newContents
  85. };
  86. } else {
  87. return new RopeNode<T> {
  88. left = this.left,
  89. right = this.right,
  90. length = this.length,
  91. height = this.height
  92. };
  93. }
  94. }
  95. internal RopeNode<T> CloneIfShared()
  96. {
  97. if (isShared)
  98. return Clone();
  99. else
  100. return this;
  101. }
  102. internal void Publish()
  103. {
  104. if (!isShared) {
  105. if (left != null)
  106. left.Publish();
  107. if (right != null)
  108. right.Publish();
  109. // it's important that isShared=true is set at the end:
  110. // Publish() must not return until the whole subtree is marked as shared, even when
  111. // Publish() is called concurrently.
  112. isShared = true;
  113. }
  114. }
  115. internal static RopeNode<T> CreateFromArray(T[] arr, int index, int length)
  116. {
  117. if (length == 0) {
  118. return emptyRopeNode;
  119. }
  120. RopeNode<T> node = CreateNodes(length);
  121. return node.StoreElements(0, arr, index, length);
  122. }
  123. internal static RopeNode<T> CreateNodes(int totalLength)
  124. {
  125. int leafCount = (totalLength + NodeSize - 1) / NodeSize;
  126. return CreateNodes(leafCount, totalLength);
  127. }
  128. static RopeNode<T> CreateNodes(int leafCount, int totalLength)
  129. {
  130. Debug.Assert(leafCount > 0);
  131. Debug.Assert(totalLength > 0);
  132. RopeNode<T> result = new RopeNode<T>();
  133. result.length = totalLength;
  134. if (leafCount == 1) {
  135. result.contents = new T[NodeSize];
  136. } else {
  137. int rightSide = leafCount / 2;
  138. int leftSide = leafCount - rightSide;
  139. int leftLength = leftSide * NodeSize;
  140. result.left = CreateNodes(leftSide, leftLength);
  141. result.right = CreateNodes(rightSide, totalLength - leftLength);
  142. result.height = (byte)(1 + Math.Max(result.left.height, result.right.height));
  143. }
  144. return result;
  145. }
  146. /// <summary>
  147. /// Balances this node and recomputes the 'height' field.
  148. /// This method assumes that the children of this node are already balanced and have an up-to-date 'height' value.
  149. /// </summary>
  150. internal void Rebalance()
  151. {
  152. // Rebalance() shouldn't be called on shared nodes - it's only called after modifications!
  153. Debug.Assert(!isShared);
  154. // leaf nodes are always balanced (we don't use 'height' to detect leaf nodes here
  155. // because Balance is supposed to recompute the height).
  156. if (left == null)
  157. return;
  158. // ensure we didn't miss a MergeIfPossible step
  159. Debug.Assert(this.length > NodeSize);
  160. // We need to loop until it's balanced. Rotations might cause two small leaves to combine to a larger one,
  161. // which changes the height and might mean we need additional balancing steps.
  162. while (Math.Abs(this.Balance) > 1) {
  163. // AVL balancing
  164. // note: because we don't care about the identity of concat nodes, this works a little different than usual
  165. // tree rotations: in our implementation, the "this" node will stay at the top, only its children are rearranged
  166. if (this.Balance > 1) {
  167. if (right.Balance < 0) {
  168. right = right.CloneIfShared();
  169. right.RotateRight();
  170. }
  171. this.RotateLeft();
  172. // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the left node; so rebalance that.
  173. this.left.Rebalance();
  174. } else if (this.Balance < -1) {
  175. if (left.Balance > 0) {
  176. left = left.CloneIfShared();
  177. left.RotateLeft();
  178. }
  179. this.RotateRight();
  180. // If 'this' was unbalanced by more than 2, we've shifted some of the inbalance to the right node; so rebalance that.
  181. this.right.Rebalance();
  182. }
  183. }
  184. Debug.Assert(Math.Abs(this.Balance) <= 1);
  185. this.height = (byte)(1 + Math.Max(left.height, right.height));
  186. }
  187. void RotateLeft()
  188. {
  189. Debug.Assert(!isShared);
  190. /* Rotate tree to the left
  191. *
  192. * this this
  193. * / \ / \
  194. * A right ===> left C
  195. * / \ / \
  196. * B C A B
  197. */
  198. RopeNode<T> a = left;
  199. RopeNode<T> b = right.left;
  200. RopeNode<T> c = right.right;
  201. // reuse right concat node, if possible
  202. this.left = right.isShared ? new RopeNode<T>() : right;
  203. this.left.left = a;
  204. this.left.right = b;
  205. this.left.length = a.length + b.length;
  206. this.left.height = (byte)(1 + Math.Max(a.height, b.height));
  207. this.right = c;
  208. this.left.MergeIfPossible();
  209. }
  210. void RotateRight()
  211. {
  212. Debug.Assert(!isShared);
  213. /* Rotate tree to the right
  214. *
  215. * this this
  216. * / \ / \
  217. * left C ===> A right
  218. * / \ / \
  219. * A B B C
  220. */
  221. RopeNode<T> a = left.left;
  222. RopeNode<T> b = left.right;
  223. RopeNode<T> c = right;
  224. // reuse left concat node, if possible
  225. this.right = left.isShared ? new RopeNode<T>() : left;
  226. this.right.left = b;
  227. this.right.right = c;
  228. this.right.length = b.length + c.length;
  229. this.right.height = (byte)(1 + Math.Max(b.height, c.height));
  230. this.left = a;
  231. this.right.MergeIfPossible();
  232. }
  233. void MergeIfPossible()
  234. {
  235. Debug.Assert(!isShared);
  236. if (this.length <= NodeSize) {
  237. // Convert this concat node to leaf node.
  238. // We know left and right cannot be concat nodes (they would have merged already),
  239. // but they could be function nodes.
  240. this.height = 0;
  241. int lengthOnLeftSide = this.left.length;
  242. if (this.left.isShared) {
  243. this.contents = new T[NodeSize];
  244. left.CopyTo(0, this.contents, 0, lengthOnLeftSide);
  245. } else {
  246. // must be a leaf node: function nodes are always marked shared
  247. Debug.Assert(this.left.contents != null);
  248. // steal buffer from left side
  249. this.contents = this.left.contents;
  250. #if DEBUG
  251. // In debug builds, explicitly mark left node as 'damaged' - but no one else should be using it
  252. // because it's not shared.
  253. this.left.contents = Empty<T>.Array;
  254. #endif
  255. }
  256. this.left = null;
  257. right.CopyTo(0, this.contents, lengthOnLeftSide, this.right.length);
  258. this.right = null;
  259. }
  260. }
  261. /// <summary>
  262. /// Copies from the array to this node.
  263. /// </summary>
  264. internal RopeNode<T> StoreElements(int index, T[] array, int arrayIndex, int count)
  265. {
  266. RopeNode<T> result = this.CloneIfShared();
  267. // result cannot be function node after a call to Clone()
  268. if (result.height == 0) {
  269. // leaf node:
  270. Array.Copy(array, arrayIndex, result.contents, index, count);
  271. } else {
  272. // concat node:
  273. if (index + count <= result.left.length) {
  274. result.left = result.left.StoreElements(index, array, arrayIndex, count);
  275. } else if (index >= this.left.length) {
  276. result.right = result.right.StoreElements(index - result.left.length, array, arrayIndex, count);
  277. } else {
  278. int amountInLeft = result.left.length - index;
  279. result.left = result.left.StoreElements(index, array, arrayIndex, amountInLeft);
  280. result.right = result.right.StoreElements(0, array, arrayIndex + amountInLeft, count - amountInLeft);
  281. }
  282. result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
  283. }
  284. return result;
  285. }
  286. /// <summary>
  287. /// Copies from this node to the array.
  288. /// </summary>
  289. internal void CopyTo(int index, T[] array, int arrayIndex, int count)
  290. {
  291. if (height == 0) {
  292. if (this.contents == null) {
  293. // function node
  294. this.GetContentNode().CopyTo(index, array, arrayIndex, count);
  295. } else {
  296. // leaf node
  297. Array.Copy(this.contents, index, array, arrayIndex, count);
  298. }
  299. } else {
  300. // concat node
  301. if (index + count <= this.left.length) {
  302. this.left.CopyTo(index, array, arrayIndex, count);
  303. } else if (index >= this.left.length) {
  304. this.right.CopyTo(index - this.left.length, array, arrayIndex, count);
  305. } else {
  306. int amountInLeft = this.left.length - index;
  307. this.left.CopyTo(index, array, arrayIndex, amountInLeft);
  308. this.right.CopyTo(0, array, arrayIndex + amountInLeft, count - amountInLeft);
  309. }
  310. }
  311. }
  312. internal RopeNode<T> SetElement(int offset, T value)
  313. {
  314. RopeNode<T> result = CloneIfShared();
  315. // result of CloneIfShared() is leaf or concat node
  316. if (result.height == 0) {
  317. result.contents[offset] = value;
  318. } else {
  319. if (offset < result.left.length) {
  320. result.left = result.left.SetElement(offset, value);
  321. } else {
  322. result.right = result.right.SetElement(offset - result.left.length, value);
  323. }
  324. result.Rebalance(); // tree layout might have changed if function nodes were replaced with their content
  325. }
  326. return result;
  327. }
  328. internal static RopeNode<T> Concat(RopeNode<T> left, RopeNode<T> right)
  329. {
  330. if (left.length == 0)
  331. return right;
  332. if (right.length == 0)
  333. return left;
  334. if (left.length + right.length <= NodeSize) {
  335. left = left.CloneIfShared();
  336. // left is guaranteed to be leaf node after cloning:
  337. // - it cannot be function node (due to clone)
  338. // - it cannot be concat node (too short)
  339. right.CopyTo(0, left.contents, left.length, right.length);
  340. left.length += right.length;
  341. return left;
  342. } else {
  343. RopeNode<T> concatNode = new RopeNode<T>();
  344. concatNode.left = left;
  345. concatNode.right = right;
  346. concatNode.length = left.length + right.length;
  347. concatNode.Rebalance();
  348. return concatNode;
  349. }
  350. }
  351. /// <summary>
  352. /// Splits this leaf node at offset and returns a new node with the part of the text after offset.
  353. /// </summary>
  354. RopeNode<T> SplitAfter(int offset)
  355. {
  356. Debug.Assert(!isShared && height == 0 && contents != null);
  357. RopeNode<T> newPart = new RopeNode<T>();
  358. newPart.contents = new T[NodeSize];
  359. newPart.length = this.length - offset;
  360. Array.Copy(this.contents, offset, newPart.contents, 0, newPart.length);
  361. this.length = offset;
  362. return newPart;
  363. }
  364. internal RopeNode<T> Insert(int offset, RopeNode<T> newElements)
  365. {
  366. if (offset == 0) {
  367. return Concat(newElements, this);
  368. } else if (offset == this.length) {
  369. return Concat(this, newElements);
  370. }
  371. // first clone this node (converts function nodes to leaf or concat nodes)
  372. RopeNode<T> result = CloneIfShared();
  373. if (result.height == 0) {
  374. // leaf node: we'll need to split this node
  375. RopeNode<T> left = result;
  376. RopeNode<T> right = left.SplitAfter(offset);
  377. return Concat(Concat(left, newElements), right);
  378. } else {
  379. // concat node
  380. if (offset < result.left.length) {
  381. result.left = result.left.Insert(offset, newElements);
  382. } else {
  383. result.right = result.right.Insert(offset - result.left.length, newElements);
  384. }
  385. result.length += newElements.length;
  386. result.Rebalance();
  387. return result;
  388. }
  389. }
  390. internal RopeNode<T> Insert(int offset, T[] array, int arrayIndex, int count)
  391. {
  392. Debug.Assert(count > 0);
  393. if (this.length + count < RopeNode<char>.NodeSize) {
  394. RopeNode<T> result = CloneIfShared();
  395. // result must be leaf node (Clone never returns function nodes, too short for concat node)
  396. int lengthAfterOffset = result.length - offset;
  397. T[] resultContents = result.contents;
  398. for (int i = lengthAfterOffset; i >= 0; i--) {
  399. resultContents[i + offset + count] = resultContents[i + offset];
  400. }
  401. Array.Copy(array, arrayIndex, resultContents, offset, count);
  402. result.length += count;
  403. return result;
  404. } else if (height == 0) {
  405. // TODO: implement this more efficiently?
  406. return Insert(offset, CreateFromArray(array, arrayIndex, count));
  407. } else {
  408. // this is a concat node (both leafs and function nodes are handled by the case above)
  409. RopeNode<T> result = CloneIfShared();
  410. if (offset < result.left.length) {
  411. result.left = result.left.Insert(offset, array, arrayIndex, count);
  412. } else {
  413. result.right = result.right.Insert(offset - result.left.length, array, arrayIndex, count);
  414. }
  415. result.length += count;
  416. result.Rebalance();
  417. return result;
  418. }
  419. }
  420. internal RopeNode<T> RemoveRange(int index, int count)
  421. {
  422. Debug.Assert(count > 0);
  423. // produce empty node when one node is deleted completely
  424. if (index == 0 && count == this.length)
  425. return emptyRopeNode;
  426. int endIndex = index + count;
  427. RopeNode<T> result = CloneIfShared(); // convert function node to concat/leaf
  428. if (result.height == 0) {
  429. int remainingAfterEnd = result.length - endIndex;
  430. for (int i = 0; i < remainingAfterEnd; i++) {
  431. result.contents[index + i] = result.contents[endIndex + i];
  432. }
  433. result.length -= count;
  434. } else {
  435. if (endIndex <= result.left.length) {
  436. // deletion is only within the left part
  437. result.left = result.left.RemoveRange(index, count);
  438. } else if (index >= result.left.length) {
  439. // deletion is only within the right part
  440. result.right = result.right.RemoveRange(index - result.left.length, count);
  441. } else {
  442. // deletion overlaps both parts
  443. int deletionAmountOnLeftSide = result.left.length - index;
  444. result.left = result.left.RemoveRange(index, deletionAmountOnLeftSide);
  445. result.right = result.right.RemoveRange(0, count - deletionAmountOnLeftSide);
  446. }
  447. // The deletion might have introduced empty nodes. Those must be removed.
  448. if (result.left.length == 0)
  449. return result.right;
  450. if (result.right.length == 0)
  451. return result.left;
  452. result.length -= count;
  453. result.MergeIfPossible();
  454. result.Rebalance();
  455. }
  456. return result;
  457. }
  458. #region Debug Output
  459. #if DEBUG
  460. internal virtual void AppendTreeToString(StringBuilder b, int indent)
  461. {
  462. b.AppendLine(ToString());
  463. indent += 2;
  464. if (left != null) {
  465. b.Append(' ', indent);
  466. b.Append("L: ");
  467. left.AppendTreeToString(b, indent);
  468. }
  469. if (right != null) {
  470. b.Append(' ', indent);
  471. b.Append("R: ");
  472. right.AppendTreeToString(b, indent);
  473. }
  474. }
  475. public override string ToString()
  476. {
  477. if (contents != null) {
  478. char[] charContents = contents as char[];
  479. if (charContents != null)
  480. return "[Leaf length=" + length + ", isShared=" + isShared + ", text=\"" + new string(charContents, 0, length) + "\"]";
  481. else
  482. return "[Leaf length=" + length + ", isShared=" + isShared + "\"]";
  483. } else {
  484. return "[Concat length=" + length + ", isShared=" + isShared + ", height=" + height + ", Balance=" + this.Balance + "]";
  485. }
  486. }
  487. internal string GetTreeAsString()
  488. {
  489. StringBuilder b = new StringBuilder();
  490. AppendTreeToString(b, 0);
  491. return b.ToString();
  492. }
  493. #endif
  494. #endregion
  495. /// <summary>
  496. /// Gets the root node of the subtree from a lazily evaluated function node.
  497. /// Such nodes are always marked as shared.
  498. /// GetContentNode() will return either a Concat or Leaf node, never another FunctionNode.
  499. /// </summary>
  500. internal virtual RopeNode<T> GetContentNode()
  501. {
  502. throw new InvalidOperationException("Called GetContentNode() on non-FunctionNode.");
  503. }
  504. }
  505. sealed class FunctionNode<T> : RopeNode<T>
  506. {
  507. Func<Rope<T>> initializer;
  508. RopeNode<T> cachedResults;
  509. public FunctionNode(int length, Func<Rope<T>> initializer)
  510. {
  511. Debug.Assert(length > 0);
  512. Debug.Assert(initializer != null);
  513. this.length = length;
  514. this.initializer = initializer;
  515. // Function nodes are immediately shared, but cannot be cloned.
  516. // This ensures we evaluate every initializer only once.
  517. this.isShared = true;
  518. }
  519. internal override RopeNode<T> GetContentNode()
  520. {
  521. lock (this) {
  522. if (this.cachedResults == null) {
  523. if (this.initializer == null)
  524. throw new InvalidOperationException("Trying to load this node recursively; or: a previous call to a rope initializer failed.");
  525. Func<Rope<T>> initializerCopy = this.initializer;
  526. this.initializer = null;
  527. Rope<T> resultRope = initializerCopy();
  528. if (resultRope == null)
  529. throw new InvalidOperationException("Rope initializer returned null.");
  530. RopeNode<T> resultNode = resultRope.root;
  531. resultNode.Publish(); // result is shared between returned rope and the rope containing this function node
  532. if (resultNode.length != this.length)
  533. throw new InvalidOperationException("Rope initializer returned rope with incorrect length.");
  534. if (resultNode.height == 0 && resultNode.contents == null) {
  535. // ResultNode is another function node.
  536. // We want to guarantee that GetContentNode() never returns function nodes, so we have to
  537. // go down further in the tree.
  538. this.cachedResults = resultNode.GetContentNode();
  539. } else {
  540. this.cachedResults = resultNode;
  541. }
  542. }
  543. return this.cachedResults;
  544. }
  545. }
  546. #if DEBUG
  547. internal override void AppendTreeToString(StringBuilder b, int indent)
  548. {
  549. RopeNode<T> resultNode;
  550. lock (this) {
  551. b.AppendLine(ToString());
  552. resultNode = cachedResults;
  553. }
  554. indent += 2;
  555. if (resultNode != null) {
  556. b.Append(' ', indent);
  557. b.Append("C: ");
  558. resultNode.AppendTreeToString(b, indent);
  559. }
  560. }
  561. public override string ToString()
  562. {
  563. return "[FunctionNode length=" + length + " initializerRan=" + (initializer == null) + "]";
  564. }
  565. #endif
  566. }
  567. }