/// <summary>Called by DoSingleOperation to split a full node, then retry the add operation.</summary> /// <remarks>Same arguments and return value as DoSingleOperation.</remarks> protected 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); }
/// <summary>Performs a retrieve, add, remove or replace operation on a /// single item in an organized A-list (such as a B+ tree).</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.</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> public virtual int DoSingleOperation(ref AListSingleOperation <K, T> op, out AListNode <K, T> splitLeft, out AListNode <K, T> splitRight) { throw new NotSupportedException(); }
public 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); }
public 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, Iterable.Single(searchItem))); } if (index == _list.Count) { // Highest key may change splitLeft = this; op.AggregateChanged |= 1; op.AggregateKey = GetKey(op.List, 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 = GetKey(op.List, _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, Iterable.Single(searchItem))); } _list[index] = searchItem; if (index + 1 == _list.Count) { // Highest key may change splitLeft = this; op.AggregateChanged |= 1; op.AggregateKey = GetKey(op.List, searchItem); } if (GetObserver(op.List) != null) { GetObserver(op.List).ItemRemoved(op.Item, this); GetObserver(op.List).ItemAdded(searchItem, this); } return(0); }