/// <summary> /// Performs the operation on a given data structure. /// </summary> private static MutationResult Add(TKey key, TValue value, KeyCollisionBehavior behavior, MutationInput origin) { Requires.NotNullAllowStructs(key, nameof(key)); OperationResult result; int hashCode = origin.KeyComparer.GetHashCode(key); HashBucket bucket = origin.Root.GetValueOrDefault(hashCode); var newBucket = bucket.Add(key, value, origin.KeyOnlyComparer, origin.ValueComparer, behavior, out result); if (result == OperationResult.NoChangeRequired) { return(new MutationResult(origin)); } var newRoot = UpdateRoot(origin.Root, hashCode, newBucket, origin.HashBucketComparer); return(new MutationResult(newRoot, result == OperationResult.SizeChanged ? +1 : 0)); }
/// <summary> /// Performs the operation on a given data structure. /// </summary> private static MutationResult AddRange(IEnumerable <KeyValuePair <TKey, TValue> > items, MutationInput origin, KeyCollisionBehavior collisionBehavior = KeyCollisionBehavior.ThrowIfValueDifferent) { Requires.NotNull(items, nameof(items)); int countAdjustment = 0; var newRoot = origin.Root; foreach (var pair in items) { int hashCode = origin.KeyComparer.GetHashCode(pair.Key); HashBucket bucket = newRoot.GetValueOrDefault(hashCode); OperationResult result; var newBucket = bucket.Add(pair.Key, pair.Value, origin.KeyOnlyComparer, origin.ValueComparer, collisionBehavior, out result); newRoot = UpdateRoot(newRoot, hashCode, newBucket, origin.HashBucketComparer); if (result == OperationResult.SizeChanged) { countAdjustment++; } } return(new MutationResult(newRoot, countAdjustment)); }
internal override NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result) { if (comparers.KeyComparer.Equals(this.Key, key)) { switch (behavior) { case KeyCollisionBehavior.SetValue: result = OperationResult.AppliedWithoutSizeChange; return(SetValue(owner, key, value)); case KeyCollisionBehavior.SetIfValueDifferent: if (comparers.ValueComparer.Equals(this.Value, value)) { result = OperationResult.NoChangeRequired; return(this); } else { result = OperationResult.AppliedWithoutSizeChange; return(SetValue(owner, key, value)); } case KeyCollisionBehavior.Skip: result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowIfValueDifferent: if (!comparers.ValueComparer.Equals(this.Value, value)) { throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Strings.DuplicateKey, key)); } result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowAlways: throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, Strings.DuplicateKey, key)); default: throw new InvalidOperationException(); // unreachable } } result = OperationResult.SizeChanged; return(MergeIntoNode(owner, shift, this, comparers.KeyComparer.GetHashCode(this.Key), hash, key, value)); }
private HashArrayMapNode ExpandToArrayMap(object owner, int shift, int count, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result) { NodeBase[] nodes = new NodeBase[WIDTH]; int index = Mask(hash, shift); nodes[index] = Empty.Update(owner, shift + BITS, hash, key, value, comparers, behavior, out result); int j = 0; for (int i = 0; i < nodes.Length; i++) { if (((_bitmap >> i) & 1) != 0) { nodes[i] = _nodes[j++]; } } return(new HashArrayMapNode(owner, count + 1, nodes)); }
internal override NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result) { if (hash == _hash) { int idx = IndexOf(key, comparers); if (idx != -1) { // Found the same key, just update var editable = EnsureEditable(owner); editable._values[idx] = (ValueNode)editable._values[idx] .Update(owner, shift, hash, key, value, comparers, behavior, out result); return(editable); } if (_count < _values.Length && IsEditable(owner)) // There are still some spots left { _values[_count] = new ValueNode(owner, key, value); _count++; result = OperationResult.SizeChanged; return(this); } // Key cannot be found in the existing buckets, nor the list has extra room // let's copy the array again ValueNode[] newValues = new ValueNode[_count + 1]; Array.Copy(_values, newValues, _count); newValues[_count] = new ValueNode(owner, key, value); result = OperationResult.SizeChanged; return(SetNode(owner, _count + 1, newValues)); } else { // different hash, nest in a bit mapnode result = OperationResult.SizeChanged; return(NestToBitmapNode(owner, shift, this, _hash, hash, key, value)); } }
internal override NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result) { int bit = 1 << Mask(hash, shift); int idx = GetIndex(bit); if ((_bitmap & bit) != 0) { // exist NodeBase node = _nodes[idx]; var newSubNode = node.Update(owner, shift + BITS, hash, key, value, comparers, behavior, out result); if (newSubNode == node) { return(this); } var newNode = EnsureEditable(owner); newNode._nodes[idx] = newSubNode; return(newNode); } else { // not exist int count = PopCount(_bitmap); if (count == 0) { result = OperationResult.SizeChanged; return(new ValueNode(owner, key, value)); } if (count < _nodes.Length && IsEditable(owner)) { // still has room in the array and editable result = OperationResult.SizeChanged; Array.Copy(_nodes, idx, _nodes, idx + 1, count - idx); _nodes[idx] = new ValueNode(owner, key, value); _bitmap |= bit; return(this); } return((count >= MAX_BITMAP_INDEXED_SIZE) ? (NodeBase)ExpandToArrayMap(owner, shift, count, hash, key, value, comparers, behavior, out result) : AddToNode(owner, idx, bit, count, key, value, out result)); } }
internal abstract NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result);
internal override NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result) { int idx = Mask(hash, shift); NodeBase node = _array[idx]; if (node == null) { NodeBase newSubNode = BitmapIndexedNode.Empty.Update(owner, shift + BITS, hash, key, value, comparers, behavior, out result); return(SetNode(owner, _count + 1, idx, newSubNode)); } else { NodeBase newSubNode = node.Update(owner, shift + BITS, hash, key, value, comparers, behavior, out result); if (newSubNode == node) { return(this); } // No Change return(SetNode(owner, _count, idx, newSubNode)); } }
/// <summary> /// Adds the specified key. /// </summary> /// <param name="key">The key to add.</param> /// <param name="value">The value to add.</param> /// <param name="keyOnlyComparer">The key comparer.</param> /// <param name="valueComparer">The value comparer.</param> /// <param name="behavior">The intended behavior for certain cases that may come up during the operation.</param> /// <param name="result">A description of the effect was on adding an element to this HashBucket.</param> /// <returns>A new HashBucket that contains the added value and any values already held by this hashbucket.</returns> internal HashBucket Add(TKey key, TValue value, IEqualityComparer <KeyValuePair <TKey, TValue> > keyOnlyComparer, IEqualityComparer <TValue> valueComparer, KeyCollisionBehavior behavior, out OperationResult result) { var kv = new KeyValuePair <TKey, TValue>(key, value); if (this.IsEmpty) { result = OperationResult.SizeChanged; return(new HashBucket(kv)); } if (keyOnlyComparer.Equals(kv, this.firstValue)) { switch (behavior) { case KeyCollisionBehavior.SetValue: result = OperationResult.AppliedWithoutSizeChange; return(new HashBucket(kv, this.additionalElements)); case KeyCollisionBehavior.Skip: result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowIfValueDifferent: if (!valueComparer.Equals(this.firstValue.Value, value)) { throw new ArgumentException(Strings.DuplicateKey); } result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowAlways: throw new ArgumentException(Strings.DuplicateKey); default: throw new InvalidOperationException(); // unreachable } } int keyCollisionIndex = this.additionalElements.IndexOf(kv, keyOnlyComparer); if (keyCollisionIndex < 0) { result = OperationResult.SizeChanged; return(new HashBucket(this.firstValue, this.additionalElements.Add(kv))); } else { switch (behavior) { case KeyCollisionBehavior.SetValue: result = OperationResult.AppliedWithoutSizeChange; return(new HashBucket(this.firstValue, this.additionalElements.ReplaceAt(keyCollisionIndex, kv))); case KeyCollisionBehavior.Skip: result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowIfValueDifferent: var existingEntry = this.additionalElements[keyCollisionIndex]; if (!valueComparer.Equals(existingEntry.Value, value)) { throw new ArgumentException(Strings.DuplicateKey); } result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowAlways: throw new ArgumentException(Strings.DuplicateKey); default: throw new InvalidOperationException(); // unreachable } } }
private ImmutableTrieDictionary <TKey, TValue> UpdateItem(TKey key, TValue value, KeyCollisionBehavior behavior) { NodeBase newRoot = (_root ?? BitmapIndexedNode.Empty) .Update(null, 0, _comparers.KeyComparer.GetHashCode(key), key, value, _comparers, behavior, out OperationResult result); if (newRoot == _root) { return(this); } return(new ImmutableTrieDictionary <TKey, TValue>(result == OperationResult.SizeChanged ? _count + 1 : _count, newRoot, _comparers)); }
/// <summary> /// Adds the specified key. /// </summary> /// <param name="key">The key to add.</param> /// <param name="value">The value to add.</param> /// <param name="keyOnlyComparer">The key comparer.</param> /// <param name="valueComparer">The value comparer.</param> /// <param name="behavior">The intended behavior for certain cases that may come up during the operation.</param> /// <param name="result">A description of the effect was on adding an element to this <see cref="HashBucket"/>.</param> /// <returns>A new <see cref="HashBucket"/> that contains the added value and any values already held by this <see cref="HashBucket"/>.</returns> internal HashBucket Add(TKey key, TValue value, IEqualityComparer <KeyValuePair <TKey, TValue> > keyOnlyComparer, IEqualityComparer <TValue> valueComparer, KeyCollisionBehavior behavior, out OperationResult result) { var kv = new KeyValuePair <TKey, TValue>(key, value); if (this.IsEmpty) { result = OperationResult.SizeChanged; return(new HashBucket(kv)); } if (keyOnlyComparer.Equals(kv, _firstValue)) { switch (behavior) { case KeyCollisionBehavior.SetValue: result = OperationResult.AppliedWithoutSizeChange; return(new HashBucket(kv, _additionalElements)); case KeyCollisionBehavior.Skip: result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowIfValueDifferent: if (!valueComparer.Equals(_firstValue.Value, value)) { throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.DuplicateKey, key)); } result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowAlways: throw new ArgumentException(string.Format(CultureInfo.CurrentCulture, SR.DuplicateKey, key)); default: throw new InvalidOperationException(); // unreachable } } int keyCollisionIndex = _additionalElements.IndexOf(kv, keyOnlyComparer); if (keyCollisionIndex < 0) { result = OperationResult.SizeChanged; return(new HashBucket(_firstValue, _additionalElements.Add(kv))); } else { switch (behavior) { case KeyCollisionBehavior.SetValue: result = OperationResult.AppliedWithoutSizeChange; return(new HashBucket(_firstValue, _additionalElements.ReplaceAt(keyCollisionIndex, kv))); case KeyCollisionBehavior.Skip: result = OperationResult.NoChangeRequired; return(this); case KeyCollisionBehavior.ThrowIfValueDifferent: #if FEATURE_ITEMREFAPI ref readonly var existingEntry = ref _additionalElements.ItemRef(keyCollisionIndex);
public static ImmutableDictionary <TKey, TValue> Add(ImmutableDictionary <TKey, TValue> source, TKey key, TValue value, KeyCollisionBehavior behavior) { if (source == null) { throw new ArgumentNullException(nameof(source)); } return(AddAndFinalize(key, value, behavior, source)); }