Example #1
0
        /// <summary>
        /// Gets/Sets a single character.
        /// Runs in O(lg N) for random access. Sequential read-only access benefits from a special optimization and runs in amortized O(1).
        /// </summary>
        /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to Length-1).</exception>
        /// <remarks>
        /// The getter counts as a read access and may be called concurrently to other read accesses.
        /// </remarks>
        public T this[int index]
        {
            get
            {
                // use unsigned integers - this way negative values for index overflow and can be tested for with the same check
                if (unchecked ((uint)index >= (uint)this.Length))
                {
                    throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
                }
                RopeCacheEntry entry = FindNodeUsingCache(index).PeekOrDefault();
                return(entry.node.contents[index - entry.nodeStartIndex]);
            }
            set
            {
                if (index < 0 || index >= this.Length)
                {
                    throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + this.Length.ToString(CultureInfo.InvariantCulture));
                }
                root = root.SetElement(index, value);
                OnChanged();

                /* Here's a try at implementing the setter using the cached node stack (UNTESTED code!).
                 * However I don't use the code because it's complicated and doesn't integrate correctly with change notifications.
                 * Instead, I'll use the much easier to understand recursive solution.
                 * Oh, and it also doesn't work correctly with function nodes.
                 *              ImmutableStack<RopeCacheEntry> nodeStack = FindNodeUsingCache(offset);
                 *              RopeCacheEntry entry = nodeStack.Peek();
                 *              if (!entry.node.isShared) {
                 *                      entry.node.contents[offset - entry.nodeStartOffset] = value;
                 *                      // missing: clear the caches except for the node stack cache (e.g. ToString() cache?)
                 *              } else {
                 *                      RopeNode oldNode = entry.node;
                 *                      RopeNode newNode = oldNode.Clone();
                 *                      newNode.contents[offset - entry.nodeStartOffset] = value;
                 *                      for (nodeStack = nodeStack.Pop(); !nodeStack.IsEmpty; nodeStack = nodeStack.Pop()) {
                 *                              RopeNode parentNode = nodeStack.Peek().node;
                 *                              RopeNode newParentNode = parentNode.CloneIfShared();
                 *                              if (newParentNode.left == oldNode) {
                 *                                      newParentNode.left = newNode;
                 *                              } else {
                 *                                      Debug.Assert(newParentNode.right == oldNode);
                 *                                      newParentNode.right = newNode;
                 *                              }
                 *                              if (parentNode == newParentNode) {
                 *                                      // we were able to change the existing node (it was not shared);
                 *                                      // there's no reason to go further upwards
                 *                                      ClearCacheOnModification();
                 *                                      return;
                 *                              } else {
                 *                                      oldNode = parentNode;
                 *                                      newNode = newParentNode;
                 *                              }
                 *                      }
                 *                      // we reached the root of the rope.
                 *                      Debug.Assert(root == oldNode);
                 *                      root = newNode;
                 *                      ClearCacheOnModification();
                 *              }*/
            }
        }
Example #2
0
        internal ImmutableStack <RopeCacheEntry> FindNodeUsingCache(int index)
        {
            Debug.Assert(index >= 0 && index < Length);

            // thread safety: fetch stack into local variable
            var stack    = lastUsedNodeStack;
            var oldStack = stack;

            if (stack == null)
            {
                stack = ImmutableStack <RopeCacheEntry> .Empty.Push(new RopeCacheEntry(root, 0));
            }
            while (!stack.PeekOrDefault().IsInside(index))
            {
                stack = stack.Pop();
            }
            while (true)
            {
                var 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);
        }
Example #3
0
File: Rope.cs Project: zz110/dnSpy
 /// <summary>
 /// Gets/Sets a single character.
 /// Runs in O(lg N) for random access. Sequential read-only access benefits from a special optimization and runs in amortized O(1).
 /// </summary>
 /// <exception cref="ArgumentOutOfRangeException">Offset is outside the valid range (0 to Length-1).</exception>
 /// <remarks>
 /// The getter counts as a read access and may be called concurrently to other read accesses.
 /// </remarks>
 public T this[int index] {
     get {
         // use unsigned integers - this way negative values for index overflow and can be tested for with the same check
         if (unchecked ((uint)index >= (uint)Length))
         {
             throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + Length.ToString(CultureInfo.InvariantCulture));
         }
         RopeCacheEntry entry = FindNodeUsingCache(index).PeekOrDefault();
         return(entry.node.contents[index - entry.nodeStartIndex]);
     }
     set {
         if (index < 0 || index >= Length)
         {
             throw new ArgumentOutOfRangeException("index", index, "0 <= index < " + Length.ToString(CultureInfo.InvariantCulture));
         }
         root = root.SetElement(index, value);
         OnChanged();
     }
 }