Skip to content

Commit dac9da0

Browse files
authored
Merge pull request #62193 from sharwell/value-builder
Implement ValueBuilder types for immutable collections
2 parents 30acc76 + b3d5e63 commit dac9da0

11 files changed

+760
-360
lines changed

src/Dependencies/Collections/ImmutableSegmentedDictionary`2+Builder+PrivateMarshal.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal static class PrivateMarshal
1515
{
1616
/// <inheritdoc cref="SegmentedCollectionsMarshal.GetValueRefOrNullRef{TKey, TValue}(ImmutableSegmentedDictionary{TKey, TValue}.Builder, TKey)"/>
1717
public static ref TValue FindValue(Builder dictionary, TKey key)
18-
=> ref SegmentedCollectionsMarshal.GetValueRefOrNullRef(dictionary.GetOrCreateMutableDictionary(), key);
18+
=> ref SegmentedCollectionsMarshal.GetValueRefOrNullRef(dictionary._builder.GetOrCreateMutableDictionary(), key);
1919
}
2020
}
2121
}

src/Dependencies/Collections/ImmutableSegmentedDictionary`2+Builder.cs

+41-140
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections;
77
using System.Collections.Generic;
88
using System.Diagnostics.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.Collections.Internal;
910

1011
namespace Microsoft.CodeAnalysis.Collections
1112
{
@@ -14,50 +15,26 @@ internal readonly partial struct ImmutableSegmentedDictionary<TKey, TValue>
1415
public sealed partial class Builder : IDictionary<TKey, TValue>, IReadOnlyDictionary<TKey, TValue>, IDictionary
1516
{
1617
/// <summary>
17-
/// The immutable collection this builder is based on.
18+
/// The private builder implementation.
1819
/// </summary>
19-
private ImmutableSegmentedDictionary<TKey, TValue> _dictionary;
20-
21-
/// <summary>
22-
/// The current mutable collection this builder is operating on. This field is initialized to a copy of
23-
/// <see cref="_dictionary"/> the first time a change is made.
24-
/// </summary>
25-
private SegmentedDictionary<TKey, TValue>? _mutableDictionary;
20+
private ValueBuilder _builder;
2621

2722
internal Builder(ImmutableSegmentedDictionary<TKey, TValue> dictionary)
28-
{
29-
_dictionary = dictionary;
30-
}
23+
=> _builder = new ValueBuilder(dictionary);
3124

3225
public IEqualityComparer<TKey> KeyComparer
3326
{
34-
get
35-
{
36-
return ReadOnlyDictionary.Comparer;
37-
}
38-
39-
set
40-
{
41-
if (value is null)
42-
throw new ArgumentNullException(nameof(value));
43-
44-
if (value != KeyComparer)
45-
{
46-
// Rewrite the mutable dictionary using a new comparer
47-
var valuesToAdd = ReadOnlyDictionary;
48-
_mutableDictionary = new SegmentedDictionary<TKey, TValue>(value);
49-
AddRange(valuesToAdd);
50-
}
51-
}
27+
get => _builder.KeyComparer;
28+
set => _builder.KeyComparer = value;
5229
}
5330

54-
public int Count => ReadOnlyDictionary.Count;
31+
public int Count => _builder.Count;
5532

5633
public KeyCollection Keys => new(this);
5734

5835
public ValueCollection Values => new(this);
5936

60-
private SegmentedDictionary<TKey, TValue> ReadOnlyDictionary => _mutableDictionary ?? _dictionary._dictionary;
37+
private SegmentedDictionary<TKey, TValue> ReadOnlyDictionary => _builder.ReadOnlyDictionary;
6138

6239
IEnumerable<TKey> IReadOnlyDictionary<TKey, TValue>.Keys => Keys;
6340

@@ -67,189 +44,113 @@ public IEqualityComparer<TKey> KeyComparer
6744

6845
ICollection<TValue> IDictionary<TKey, TValue>.Values => Values;
6946

70-
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => false;
47+
bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly => ICollectionCalls<KeyValuePair<TKey, TValue>>.IsReadOnly(ref _builder);
7148

7249
ICollection IDictionary.Keys => Keys;
7350

7451
ICollection IDictionary.Values => Values;
7552

76-
bool IDictionary.IsReadOnly => false;
53+
bool IDictionary.IsReadOnly => IDictionaryCalls.IsReadOnly(ref _builder);
7754

78-
bool IDictionary.IsFixedSize => false;
55+
bool IDictionary.IsFixedSize => IDictionaryCalls.IsFixedSize(ref _builder);
7956

8057
object ICollection.SyncRoot => this;
8158

82-
bool ICollection.IsSynchronized => false;
59+
bool ICollection.IsSynchronized => ICollectionCalls.IsSynchronized(ref _builder);
8360

8461
public TValue this[TKey key]
8562
{
86-
get => ReadOnlyDictionary[key];
87-
set => GetOrCreateMutableDictionary()[key] = value;
63+
get => _builder[key];
64+
set => _builder[key] = value;
8865
}
8966

9067
object? IDictionary.this[object key]
9168
{
92-
get => ((IDictionary)ReadOnlyDictionary)[key];
93-
set => ((IDictionary)GetOrCreateMutableDictionary())[key] = value;
94-
}
95-
96-
private SegmentedDictionary<TKey, TValue> GetOrCreateMutableDictionary()
97-
{
98-
return _mutableDictionary ??= new SegmentedDictionary<TKey, TValue>(_dictionary._dictionary, _dictionary.KeyComparer);
69+
get => IDictionaryCalls.GetItem(ref _builder, key);
70+
set => IDictionaryCalls.SetItem(ref _builder, key, value);
9971
}
10072

10173
public void Add(TKey key, TValue value)
102-
{
103-
if (Contains(new KeyValuePair<TKey, TValue>(key, value)))
104-
return;
105-
106-
GetOrCreateMutableDictionary().Add(key, value);
107-
}
74+
=> _builder.Add(key, value);
10875

10976
public void Add(KeyValuePair<TKey, TValue> item)
110-
=> Add(item.Key, item.Value);
77+
=> _builder.Add(item);
11178

11279
public void AddRange(IEnumerable<KeyValuePair<TKey, TValue>> items)
113-
{
114-
if (items == null)
115-
throw new ArgumentNullException(nameof(items));
116-
117-
foreach (var pair in items)
118-
Add(pair.Key, pair.Value);
119-
}
80+
=> _builder.AddRange(items);
12081

12182
public void Clear()
122-
{
123-
if (ReadOnlyDictionary.Count != 0)
124-
{
125-
if (_mutableDictionary is null)
126-
_mutableDictionary = new SegmentedDictionary<TKey, TValue>(KeyComparer);
127-
else
128-
_mutableDictionary.Clear();
129-
}
130-
}
83+
=> _builder.Clear();
13184

13285
public bool Contains(KeyValuePair<TKey, TValue> item)
133-
{
134-
return TryGetValue(item.Key, out var value)
135-
&& EqualityComparer<TValue>.Default.Equals(value, item.Value);
136-
}
86+
=> _builder.Contains(item);
13787

13888
public bool ContainsKey(TKey key)
139-
=> ReadOnlyDictionary.ContainsKey(key);
89+
=> _builder.ContainsKey(key);
14090

14191
public bool ContainsValue(TValue value)
142-
{
143-
return _dictionary.ContainsValue(value);
144-
}
92+
=> _builder.ContainsValue(value);
14593

14694
public Enumerator GetEnumerator()
147-
=> new(GetOrCreateMutableDictionary(), Enumerator.ReturnType.KeyValuePair);
95+
=> _builder.GetEnumerator();
14896

14997
public TValue? GetValueOrDefault(TKey key)
150-
{
151-
if (TryGetValue(key, out var value))
152-
return value;
153-
154-
return default;
155-
}
98+
=> _builder.GetValueOrDefault(key);
15699

157100
public TValue GetValueOrDefault(TKey key, TValue defaultValue)
158-
{
159-
if (TryGetValue(key, out var value))
160-
return value;
161-
162-
return defaultValue;
163-
}
101+
=> _builder.GetValueOrDefault(key, defaultValue);
164102

165103
public bool Remove(TKey key)
166-
{
167-
if (_mutableDictionary is null && !ContainsKey(key))
168-
return false;
169-
170-
return GetOrCreateMutableDictionary().Remove(key);
171-
}
104+
=> _builder.Remove(key);
172105

173106
public bool Remove(KeyValuePair<TKey, TValue> item)
174-
{
175-
if (!Contains(item))
176-
{
177-
return false;
178-
}
179-
180-
GetOrCreateMutableDictionary().Remove(item.Key);
181-
return true;
182-
}
107+
=> _builder.Remove(item);
183108

184109
public void RemoveRange(IEnumerable<TKey> keys)
185-
{
186-
if (keys is null)
187-
throw new ArgumentNullException(nameof(keys));
188-
189-
foreach (var key in keys)
190-
{
191-
Remove(key);
192-
}
193-
}
110+
=> _builder.RemoveRange(keys);
194111

195112
public bool TryGetKey(TKey equalKey, out TKey actualKey)
196-
{
197-
foreach (var key in Keys)
198-
{
199-
if (KeyComparer.Equals(key, equalKey))
200-
{
201-
actualKey = key;
202-
return true;
203-
}
204-
}
205-
206-
actualKey = equalKey;
207-
return false;
208-
}
113+
=> _builder.TryGetKey(equalKey, out actualKey);
209114

210115
#pragma warning disable CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
211116
public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value)
212117
#pragma warning restore CS8767 // Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes).
213-
=> ReadOnlyDictionary.TryGetValue(key, out value);
118+
=> _builder.TryGetValue(key, out value);
214119

215120
public ImmutableSegmentedDictionary<TKey, TValue> ToImmutable()
216-
{
217-
_dictionary = new ImmutableSegmentedDictionary<TKey, TValue>(ReadOnlyDictionary);
218-
_mutableDictionary = null;
219-
return _dictionary;
220-
}
121+
=> _builder.ToImmutable();
221122

222123
void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
223-
=> ((ICollection<KeyValuePair<TKey, TValue>>)ReadOnlyDictionary).CopyTo(array, arrayIndex);
124+
=> ICollectionCalls<KeyValuePair<TKey, TValue>>.CopyTo(ref _builder, array, arrayIndex);
224125

225126
IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator()
226-
=> new Enumerator(GetOrCreateMutableDictionary(), Enumerator.ReturnType.KeyValuePair);
127+
=> IEnumerableCalls<KeyValuePair<TKey, TValue>>.GetEnumerator(ref _builder);
227128

228129
IEnumerator IEnumerable.GetEnumerator()
229-
=> new Enumerator(GetOrCreateMutableDictionary(), Enumerator.ReturnType.KeyValuePair);
130+
=> IEnumerableCalls.GetEnumerator(ref _builder);
230131

231132
bool IDictionary.Contains(object key)
232-
=> ((IDictionary)ReadOnlyDictionary).Contains(key);
133+
=> IDictionaryCalls.Contains(ref _builder, key);
233134

234135
void IDictionary.Add(object key, object? value)
235-
=> ((IDictionary)GetOrCreateMutableDictionary()).Add(key, value);
136+
=> IDictionaryCalls.Add(ref _builder, key, value);
236137

237138
IDictionaryEnumerator IDictionary.GetEnumerator()
238-
=> new Enumerator(GetOrCreateMutableDictionary(), Enumerator.ReturnType.DictionaryEntry);
139+
=> IDictionaryCalls.GetEnumerator(ref _builder);
239140

240141
void IDictionary.Remove(object key)
241-
=> ((IDictionary)GetOrCreateMutableDictionary()).Remove(key);
142+
=> IDictionaryCalls.Remove(ref _builder, key);
242143

243144
void ICollection.CopyTo(Array array, int index)
244-
=> ((ICollection)ReadOnlyDictionary).CopyTo(array, index);
145+
=> ICollectionCalls.CopyTo(ref _builder, array, index);
245146

246147
internal TestAccessor GetTestAccessor()
247148
=> new TestAccessor(this);
248149

249150
internal readonly struct TestAccessor(Builder instance)
250151
{
251152
internal SegmentedDictionary<TKey, TValue> GetOrCreateMutableDictionary()
252-
=> instance.GetOrCreateMutableDictionary();
153+
=> instance._builder.GetOrCreateMutableDictionary();
253154
}
254155
}
255156
}

0 commit comments

Comments
 (0)