/// <summary> /// Optimized insert of presorted key/value pairs. /// If the input is not presorted, please use AddRange() instead. /// </summary> /// <param name="items">The ordered list of items to insert</param> /// <param name="allowUpdates">True to overwrite any existing records</param> /// <returns>The total number of records inserted or updated</returns> public int AddRangeSorted(IEnumerable <KeyValuePair <TKey, TValue> > items, bool allowUpdates) { int result = 0; using (AddRangeInfo bulk = new AddRangeInfo(allowUpdates, items)) { while (!bulk.IsComplete) { KeyRange range = new KeyRange(_keyComparer); using (RootLock root = LockRoot(LockType.Insert, "BulkInsert")) result += AddRange(root.Pin, ref range, bulk, null, int.MinValue); } } DebugComplete("AddRange({0} records)", result); return(result); }
private int AddRange(NodePin thisLock, ref KeyRange range, AddRangeInfo value, NodePin parent, int parentIx) { int counter = 0; Node me = thisLock.Ptr; if (me.Count == me.Size && parent != null) { using (NodeTransaction trans = _storage.BeginTransaction()) { TKey splitAt; if (parent.Ptr.IsRoot) //Is root node { Node rootNode = trans.BeginUpdate(parent); using (NodePin newRoot = trans.Create(parent, false)) { rootNode.ReplaceChild(0, thisLock.Handle, newRoot.Handle); newRoot.Ptr.Insert(0, new Element(default(TKey), thisLock.Handle)); using (NodePin next = Split(trans, ref thisLock, newRoot, 0, out splitAt, true)) using (thisLock) { trans.Commit(); GC.KeepAlive(thisLock); GC.KeepAlive(next); } return(AddRange(newRoot, ref range, value, parent, parentIx)); } } trans.BeginUpdate(parent); using (NodePin next = Split(trans, ref thisLock, parent, parentIx, out splitAt, true)) using (thisLock) { trans.Commit(); if (_keyComparer.Compare(value.Current.Key, splitAt) >= 0) { thisLock.Dispose(); range.SetMinKey(splitAt); return(AddRange(next, ref range, value, parent, parentIx + 1)); } next.Dispose(); range.SetMaxKey(splitAt); return(AddRange(thisLock, ref range, value, parent, parentIx)); } } } if (parent != null) { parent.Dispose(); } if (me.IsLeaf) { using (NodeTransaction trans = _storage.BeginTransaction()) { me = trans.BeginUpdate(thisLock); int inserted = 0; while (me.Count < me.Size && !value.IsComplete && range.IsKeyInRange(value.Current.Key)) { int ordinal; bool exists = me.BinarySearch(_itemComparer, new Element(value.Current.Key), out ordinal); DuplicateKeyException.Assert(!exists || value.AllowUpdate); if (exists) { me.SetValue(ordinal, value.Current.Key, value.Current.Value, _keyComparer); trans.UpdateValue(value.Current.Key, value.Current.Value); } else { me.Insert(ordinal, new Element(value.Current.Key, value.Current.Value)); trans.AddValue(value.Current.Key, value.Current.Value); inserted++; } counter++; value.MoveNext(); } trans.Commit(); if (_hasCount && inserted > 0) { int count = _count, test; while (count != (test = Interlocked.CompareExchange(ref _count, count + inserted, count))) { count = test; } } } } else { int ordinal; me.BinarySearch(_itemComparer, new Element(value.Current.Key), out ordinal); if (ordinal >= me.Count) { ordinal = me.Count - 1; } if (ordinal > 0) { range.SetMinKey(me[ordinal - 1].Key); } if (ordinal < (me.Count - 1)) { range.SetMaxKey(me[ordinal + 1].Key); } using (NodePin child = _storage.Lock(thisLock, me[ordinal].ChildNode)) counter += AddRange(child, ref range, value, thisLock, ordinal); } return(counter); }