コード例 #1
0
            internal override Node?InsertRange(int branchingFactor, bool isAppend, int index, IEnumerable <T> collection)
            {
                int  pageIndex         = FindLowerBound(_offsets, _nodeCount, index);
                int  previousCount     = _nodes[pageIndex].Count;
                Node?lastImpactedChild = _nodes[pageIndex].InsertRange(branchingFactor, isAppend, index - _offsets[pageIndex], collection);

                if (lastImpactedChild == null)
                {
                    int insertionCount = _nodes[pageIndex].Count - previousCount;
                    for (int i = pageIndex + 1; i < _nodeCount; i++)
                    {
                        _offsets[i] += insertionCount;
                    }

                    _count += insertionCount;
                    return(null);
                }

                for (int i = pageIndex + 1; i < _nodeCount; i++)
                {
                    _offsets[i] = _offsets[i - 1] + _nodes[i - 1].Count;
                }

                _count = _offsets[_nodeCount - 1] + _nodes[_nodeCount - 1].Count;
                pageIndex++;

                IndexNode insertionNode = this;
                Node?     lastIndexNode = null;

                for (Node?item = _nodes[pageIndex - 1].NextNode; true; item = item.NextNode)
                {
                    Debug.Assert(item != null, "Assertion failed: item != null");
                    Debug.Assert(pageIndex >= 0 && pageIndex <= insertionNode._nodes.Length, "Assertion failed: pageIndex >= 0 && pageIndex <= insertionNode._nodes.Length");

                    IndexNode?newLastIndex = insertionNode.InsertIndex(branchingFactor, isAppend, pageIndex, item);
                    if (newLastIndex != null)
                    {
                        // this insertion resulted in a split, so at minimum 'pageIndex' must be updated
                        if (lastIndexNode != null && insertionNode != lastIndexNode)
                        {
                            // We were not inserting into the last node (an earlier split in the InsertRange operation
                            // resulted in insertions prior to the last node)
                            //
                            // When we reach this point, a previous index insertion caused a split which did not change
                            // the insertion node. Afterwards, we continued inserting and have now reached a point where
                            // the page is splitting a second time. It is impossible for this split to be caused by an
                            // insertion in the first half of the list. The primary difference between this situation
                            // and similar code in LeafNode is the first insertion index - for LeafNode it is possible
                            // to start inserting at index 0, but for IndexNode the first possible insertion is index 1.
                            Debug.Assert(pageIndex >= insertionNode._nodeCount, $"Assertion failed: {nameof(pageIndex)} >= {nameof(insertionNode)}._nodeCount");
                            pageIndex     = pageIndex + 1 - insertionNode._nodeCount;
                            insertionNode = newLastIndex;
                        }
                        else if (pageIndex < insertionNode._nodeCount)
                        {
                            // The split resulted in a new last node, but no change in the insertion node.
                            pageIndex++;
                            lastIndexNode = newLastIndex;
                        }
                        else
                        {
                            // The split resulted in a new last node which becomes the new insertion node.
                            pageIndex     = pageIndex + 1 - insertionNode._nodeCount;
                            lastIndexNode = newLastIndex;
                            insertionNode = newLastIndex;
                        }
                    }
                    else
                    {
                        pageIndex++;
                    }

                    if (item == lastImpactedChild)
                    {
                        break;
                    }
                }

                return(lastIndexNode);
            }
コード例 #2
0
            private IndexNode InsertIndex(int branchingFactor, bool isAppend, int index, Node node)
            {
                if (_nodeCount < _nodes.Length)
                {
                    if (index < _nodeCount)
                    {
                        Array.Copy(_nodes, index, _nodes, index + 1, _nodeCount - index);
                        Array.Copy(_offsets, index, _offsets, index + 1, _nodeCount - index);
                    }
                    else
                    {
                        _offsets[_nodeCount] = Count;
                    }

                    _nodes[index] = node;
                    _nodeCount++;

                    int delta = node.Count;
                    for (int i = index + 1; i < _nodeCount; i++)
                    {
                        _offsets[i] += delta;
                    }

                    _count += delta;
                    return(null);
                }

                if (isAppend)
                {
                    // optimize the case of adding at the end of the overall list
                    IndexNode result = new IndexNode(branchingFactor);
                    result._nodes[0]  = node;
                    result._nodeCount = 1;
                    result._count     = node.Count;
                    _next             = result;
                    return(result);
                }
                else
                {
                    // split the node
                    IndexNode splitNode  = new IndexNode(branchingFactor);
                    int       splitPoint = _nodeCount / 2;

                    bool forceNext = false;
                    if ((_nodeCount + 1) / 2 > splitPoint && index > splitPoint)
                    {
                        // When splitting a node with an odd branching factor, prior to insertion one split node will
                        // have (b-1)/2 nodes and the other will have (b+1)/2 nodes. Since the minimum number of nodes
                        // after insertion is (b+1)/2, the split point uniquely determines the insertion point. This
                        // block handles the case where the insertion point is index (b+1)/2 by forcing it to the first
                        // node of the next page instead of adding it (where it fits) at the end of the first page.
                        splitPoint++;
                        forceNext = true;
                    }

                    Array.Copy(_nodes, splitPoint, splitNode._nodes, 0, _nodeCount - splitPoint);
                    Array.Copy(_offsets, splitPoint, splitNode._offsets, 0, _nodeCount - splitPoint);
                    Array.Clear(_nodes, splitPoint, _nodeCount - splitPoint);
                    Array.Clear(_offsets, splitPoint, _nodeCount - splitPoint);

                    splitNode._nodeCount = _nodeCount - splitPoint;
                    int adjustment = splitNode._offsets[0];
                    for (int i = 0; i < splitNode._nodeCount; i++)
                    {
                        splitNode._offsets[i] -= adjustment;
                    }

                    splitNode._count = _count - adjustment;

                    _nodeCount = splitPoint;
                    _count     = adjustment;

                    // insert the new element into the correct half
                    if (!forceNext && index <= splitPoint)
                    {
                        InsertIndex(branchingFactor, false, index, node);
                    }
                    else
                    {
                        splitNode.InsertIndex(branchingFactor, false, index - splitPoint, node);
                    }

                    splitNode._next = _next;
                    _next           = splitNode;
                    return(splitNode);
                }
            }