internal ImmutableStack <RopeCacheEntry> FindNodeUsingCache(int index) { Debug.Assert(index >= 0 && index < this.Length); // thread safety: fetch stack into local variable ImmutableStack <RopeCacheEntry> stack = lastUsedNodeStack; ImmutableStack <RopeCacheEntry> oldStack = stack; if (stack == null) { stack = ImmutableStack <RopeCacheEntry> .Empty.Push(new RopeCacheEntry(root, 0)); } while (!stack.PeekOrDefault().IsInside(index)) { stack = stack.Pop(); } while (true) { RopeCacheEntry entry = stack.PeekOrDefault(); // check if we've reached a leaf or function node if (entry.node.height == 0) { if (entry.node.contents == null) { // this is a function node - go down into its subtree entry = new RopeCacheEntry(entry.node.GetContentNode(), entry.nodeStartIndex); // entry is now guaranteed NOT to be another function node } if (entry.node.contents != null) { // this is a node containing actual content, so we're done break; } } // go down towards leaves if (index - entry.nodeStartIndex >= entry.node.left.length) { stack = stack.Push(new RopeCacheEntry(entry.node.right, entry.nodeStartIndex + entry.node.left.length)); } else { stack = stack.Push(new RopeCacheEntry(entry.node.left, entry.nodeStartIndex)); } } // write back stack to volatile cache variable // (in multithreaded access, it doesn't matter which of the threads wins - it's just a cache) if (oldStack != stack) { // no need to write when we the cache variable didn't change lastUsedNodeStack = stack; } // this method guarantees that it finds a leaf node Debug.Assert(stack.Peek().node.contents != null); return(stack); }