示例#1
0
        /// <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));
        }
示例#2
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));
        }
示例#3
0
            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));
            }
示例#4
0
            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));
            }
示例#5
0
            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));
                }
            }
示例#6
0
            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));
                }
            }
示例#7
0
 internal abstract NodeBase Update(object owner, int shift, int hash, TKey key, TValue value, Comparers comparers, KeyCollisionBehavior behavior, out OperationResult result);
示例#8
0
            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));
                }
            }
示例#9
0
            /// <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));
 }