Example #1
0
        /// <summary>Called by DoSingleOperation to split a full node, then retry the add operation.</summary>
        /// <remarks>Same arguments and return value as DoSingleOperation.</remarks>
        internal virtual int SplitAndAdd(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight)
        {
            // Tell DoSingleOperation not to send notifications to the observer
            op.AggregateChanged |= 2;

            int divAt = _list.Count >> 1;
            var mid   = _list[divAt];
            var left  = new BListLeaf <K, T>(_maxNodeSize, _list.CopySection(0, divAt));
            var right = new BListLeaf <K, T>(_maxNodeSize, _list.CopySection(divAt, _list.Count - divAt));
            int sizeChange;

            if (op.CompareToKey(mid, op.Key) >= 0)
            {
                sizeChange = left.DoSingleOperation(ref op, out splitLeft, out splitRight);
            }
            else
            {
                op.BaseIndex += left.TotalCount;
                sizeChange    = right.DoSingleOperation(ref op, out splitLeft, out splitRight);
            }

            op.AggregateChanged &= unchecked ((byte)~2);

            // (splitLeft may be non-null, meaning that the highest key changed, which doesn't matter here.)
            Debug.Assert(splitRight == null);
            Debug.Assert(sizeChange == 1);
            splitLeft  = left;
            splitRight = right;
            return(sizeChange);
        }
Example #2
0
        internal override int DoSingleOperation(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight)
        {
            Debug.Assert(!IsFrozen || op.Mode == AListOperation.Retrieve);

            AssertValid();
            var tob = GetObserver(op.List);
            int i   = BinarySearchK(op.Key, op.CompareKeys);

            if (op.Mode != AListOperation.Retrieve)
            {
                AutoClone(ref _children[i].Node, this, tob);
                if (op.Mode >= AListOperation.Add && _children[i].Node.IsFullLeaf)
                {
                    TryToShiftAnItemToSiblingOfLeaf(i, tob);
                    // Binary search result might now be off-by-1
                    i = BinarySearchK(op.Key, op.CompareKeys);
                }
            }
            AssertValid();
            op.BaseIndex += _children[i].Index;
            int sizeChange = _children[i].Node.DoSingleOperation(ref op, out splitLeft, out splitRight);

            if (sizeChange != 0)
            {
                AdjustIndexesAfter(i, sizeChange);
            }

            // Handle child split / undersized / highest key changed
            if (splitLeft != null)
            {
                if (splitRight != null)
                {
                    splitLeft = HandleChildSplit(i, splitLeft, ref splitRight, tob);
                }
                else
                {
                    // Node is undersized and/or highest key changed
                    bool flagParent = false;

                    if (op.AggregateChanged != 0)
                    {
                        if (i < _childCount - 1)
                        {
                            _highestKey[i]      = op.AggregateKey;
                            op.AggregateChanged = 0;
                        }
                        else
                        {
                            // Update highest key in parent node instead
                            flagParent = true;
                        }
                    }

                    if (splitLeft.IsUndersized)
                    {
                        flagParent |= HandleUndersized(i, tob);
                    }

                    if (flagParent)
                    {
                        splitLeft = this;
                    }
                    else
                    {
                        splitLeft = null;
                    }
                }
            }
            AssertValid();
            return(sizeChange);
        }
Example #3
0
 /// <summary>Performs a retrieve, add, remove or replace operation on a
 /// single item in an organized A-list (such as a BList or BDictionary).</summary>
 /// <param name="op">An object that describes the operation to be performed
 /// and the parameters of the tree (comparers and observers).</param>
 /// <param name="splitLeft">null if the operation completed normally. If an
 /// item was added and the node split, splitLeft and splitRight are new
 /// nodes that each contain roughly half of the items from this node.
 /// <para/>
 /// If an item was removed and the node became undersized, splitLeft is set
 /// to this (the node itself) and splitRight is set to null. Likewise, if
 /// the aggregate value of the node changed (in a B+tree, this means that
 /// the highest key changed) then splitLeft is set to the node itself and
 /// splitRight is set to null.
 /// </param>
 /// <returns>Returns 1 if a new item was added, -1 if an item was removed,
 /// or 0 if the number of items in the tree did not change.</returns>
 /// <exception cref="NotSupportedException">This node does not belong to an
 /// organized tree (e.g. normal AList).</exception>
 /// <exception cref="KeyAlreadyExistsException">The key op.NewKey already
 /// existed in the tree and op.Mode was
 /// <see cref="AListOperation"/>.AddOrThrow.</exception>
 /// <remarks>
 /// If op.Mode is <see cref="AListOperation"/>.ReplaceIfPresent, this method
 /// informs the caller when replacement occurs in this mode by changing
 /// op.Mode to AddDuplicateMode.ReplaceExisting.
 /// </remarks>
 internal virtual int DoSingleOperation(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight)
 {
     throw new NotSupportedException();
 }
Example #4
0
        internal override int DoSingleOperation(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight)
        {
            Debug.Assert(!IsFrozen || op.Mode == AListOperation.Retrieve);

            AssertValid();
            var tob = GetObserver(op.List);
            int i   = BinarySearchK(op.Key, op.CompareKeys);

            if (op.Mode != AListOperation.Retrieve)
            {
                AutoClone(ref _children[i].Node, this, tob);
                if (op.Mode >= AListOperation.__AddThreshold && _children[i].Node.IsFullLeaf)
                {
                    if (!PrepareToInsert(i, tob))
                    {                           // Items have been shifted out of _children[i] (to the left or right)
                        if (i < _highestKey.Length)
                        {
                            _highestKey[i] = GetHighestKey(_children[i].Node);
                        }
                        if (i > 0)
                        {
                            _highestKey[i - 1] = GetHighestKey(_children[i - 1].Node);
                        }
                        i = BinarySearchK(op.Key, op.CompareKeys);
                    }
                }
            }
            AssertValid();
            op.BaseIndex += _children[i].Index;
            int sizeChange = _children[i].Node.DoSingleOperation(ref op, out splitLeft, out splitRight);

            if (sizeChange != 0)
            {
                AdjustIndexesAfter(i, sizeChange);
            }

            // Handle child split / undersized / highest key changed
            if (splitLeft != null)
            {
                if (splitRight != null)
                {
                    splitLeft = HandleChildSplit(i, splitLeft, ref splitRight, tob);
                }
                else
                {
                    // Node is undersized and/or highest key changed
                    bool flagParent = false;

                    if (op.AggregateChanged != 0)
                    {
                        if (i < _childCount - 1)
                        {
                            _highestKey[i]      = op.AggregateKey;
                            op.AggregateChanged = 0;
                        }
                        else
                        {
                            // Update highest key in parent node instead
                            flagParent = true;
                        }
                    }
                    else if (i + 1 == LocalCount && i > 0 && _children[i].Node.LocalCount == 0)
                    {                       // (2020/03 fix) The last item in the last node was removed, so the highest
                        // key of this node may have decreased. AssertValid() failed in the parent
                        // on rare occasions when the max node size was 3, because the parent node
                        // could have a _highestKey value that was too high. No malfunctions were
                        // observed as a result, and I'm hard pressed to think of anything that
                        // could go wrong when the highest-key value is too high but lower than the
                        // next sibling. Note: don't update _highestKey[i] here because the highest
                        // key of the final node is never tracked.
                        Debug.Assert(sizeChange == -1);
                        flagParent           = true;
                        op.AggregateChanged |= 1;
                        op.AggregateKey      = GetHighestKey(_children[i - 1].Node);
                    }

                    if (splitLeft.IsUndersized)
                    {
                        flagParent |= HandleUndersized(i, tob);
                    }

                    if (flagParent)
                    {
                        splitLeft = this;                         // inform parent about highest-key change or undersized node
                    }
                    else
                    {
                        splitLeft = null;
                    }
                }
            }
            AssertValid();
            return(sizeChange);
        }
Example #5
0
        internal override int DoSingleOperation(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight)
        {
            T   searchItem = op.Item;
            int index      = _list.BinarySearch(op.Key, op.CompareToKey, op.LowerBound);

            if (op.Found = (index >= 0))
            {
                op.Item = _list[index];                 // save old value
                if (op.RequireExactMatch && (searchItem == null ? op.Item == null : !searchItem.Equals(op.Item)))
                {
                    op.Found = false;
                }
            }
            else
            {
                index = ~index;
            }

            splitLeft     = splitRight = null;
            op.BaseIndex += (uint)index;

            if (op.Mode == AListOperation.Retrieve)
            {
                return(0);
            }

            if (op.Mode >= AListOperation.Add)
            {
                // Possible operations: Add, AddOrReplace, AddIfNotPresent, AddOrThrow
                if (_list.Count >= _maxNodeSize && (op.Mode == AListOperation.Add || !op.Found))
                {
                    op.BaseIndex -= (uint)index;
                    op.Item       = searchItem;
                    return(SplitAndAdd(ref op, out splitLeft, out splitRight));
                }

                if (op.Found && op.Mode != AListOperation.Add)
                {
                    if (op.Mode == AListOperation.AddOrThrow)
                    {
                        throw new KeyAlreadyExistsException();
                    }
                    else if (op.Mode == AListOperation.AddIfNotPresent)
                    {
                        return(0);
                    }
                }
                else                 // add new item
                {
                    if (HasListChanging(op.List))
                    {
                        CallListChanging(op.List, new ListChangeInfo <T>(NotifyCollectionChangedAction.Add, (int)op.BaseIndex, 1, Range.Single(searchItem)));
                    }

                    if (index == _list.Count)
                    {                           // Highest key may change
                        splitLeft            = this;
                        op.AggregateChanged |= 1;
                        op.AggregateKey      = op.List.GetKey(searchItem);
                    }

                    _list.AutoRaiseCapacity(1, _maxNodeSize);
                    _list.Insert(index, searchItem);

                    if (GetObserver(op.List) != null)
                    {
                        if ((op.AggregateChanged & 2) == 0)
                        {
                            GetObserver(op.List).ItemAdded(searchItem, this);
                        }
                    }
                    return(1);
                }
                Debug.Assert(op.Mode == AListOperation.AddOrReplace);
            }
            else if (op.Found)
            {
                // Possible operations: ReplaceIfPresent, Remove
                if (op.Mode == AListOperation.Remove)
                {
                    if (HasListChanging(op.List))
                    {
                        CallListChanging(op.List, new ListChangeInfo <T>(NotifyCollectionChangedAction.Remove, (int)op.BaseIndex, -1, null));
                    }

                    _list.RemoveAt(index);

                    if (index == _list.Count)
                    {                           // Highest key may change
                        splitLeft = this;
                        if (_list.Count != 0)
                        {
                            op.AggregateChanged |= 1;
                            op.AggregateKey      = op.List.GetKey(_list.Last);
                        }
                    }
                    else if (IsUndersized)
                    {
                        splitLeft = this;
                    }

                    if (GetObserver(op.List) != null)
                    {
                        Debug.Assert((op.AggregateChanged & 2) == 0);
                        GetObserver(op.List).ItemRemoved(op.Item, this);
                    }

                    return(-1);
                }
                Debug.Assert(op.Mode == AListOperation.ReplaceIfPresent);
            }
            else
            {
                Debug.Assert(op.Mode == AListOperation.Remove || op.Mode == AListOperation.ReplaceIfPresent);
                return(0);                // can't remove/replace because item was not found.
            }

            // Fallthrough action: replace existing item
            Debug.Assert(op.Found);
            if (HasListChanging(op.List))
            {
                CallListChanging(op.List, new ListChangeInfo <T>(NotifyCollectionChangedAction.Replace, (int)op.BaseIndex, 0, Range.Single(searchItem)));
            }

            _list[index] = searchItem;

            if (index + 1 == _list.Count)
            {                   // Highest key may change
                splitLeft            = this;
                op.AggregateChanged |= 1;
                op.AggregateKey      = op.List.GetKey(searchItem);
            }

            if (GetObserver(op.List) != null)
            {
                GetObserver(op.List).ItemRemoved(op.Item, this);
                GetObserver(op.List).ItemAdded(searchItem, this);
            }
            return(0);
        }