public IAsyncLocalValueMap Set(IAsyncLocal key, object? value, bool treatNullValueAsNonexistent) { if (value != null || !treatNullValueAsNonexistent) { // If the key matches one already contained in this map, then create a new three-element map with the // updated value. if (ReferenceEquals(key, _key1)) return new ThreeElementAsyncLocalValueMap(key, value, _key2, _value2, _key3, _value3); if (ReferenceEquals(key, _key2)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, key, value, _key3, _value3); if (ReferenceEquals(key, _key3)) return new ThreeElementAsyncLocalValueMap(_key1, _value1, _key2, _value2, key, value); // The key doesn't exist in this map, so upgrade to a multi map that contains // the additional key/value pair. var multi = new MultiElementAsyncLocalValueMap(4); multi.UnsafeStore(0, _key1, _value1); multi.UnsafeStore(1, _key2, _value2); multi.UnsafeStore(2, _key3, _value3); multi.UnsafeStore(3, key, value); return multi; } else { // If the key exists in this map, remove it by downgrading to a two-element map without the key. Otherwise, // there's nothing to add or remove, so just return this map. return ReferenceEquals(key, _key1) ? new TwoElementAsyncLocalValueMap(_key2, _value2, _key3, _value3) : ReferenceEquals(key, _key2) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key3, _value3) : ReferenceEquals(key, _key3) ? new TwoElementAsyncLocalValueMap(_key1, _value1, _key2, _value2) : (IAsyncLocalValueMap)this; } }
public IAsyncLocalValueMap Set(IAsyncLocal key, object?value, bool treatNullValueAsNonexistent) { int count = Count; bool containsKey = ContainsKey(key); // If the value being set exists, create a new many map, copy all of the elements from this one, // and then store the new key/value pair into it. This is the most common case. if (value != null || !treatNullValueAsNonexistent) { var map = new ManyElementAsyncLocalValueMap(count + (containsKey ? 0 : 1)); foreach (KeyValuePair <IAsyncLocal, object?> pair in this) { map[pair.Key] = pair.Value; } map[key] = value; return(map); } // Otherwise, the value is null and a null value may be treated as nonexistent. We can downgrade to a smaller // map rather than storing null. // If the key is contained in this map, we're going to create a new map that's one pair smaller. if (containsKey) { // If the new count would be within range of a multi map instead of a many map, // downgrade to the multi map, which uses less memory and is faster to access. // Otherwise, just create a new many map that's missing this key. if (count == MultiElementAsyncLocalValueMap.MaxMultiElements + 1) { var multi = new MultiElementAsyncLocalValueMap(MultiElementAsyncLocalValueMap.MaxMultiElements); int index = 0; foreach (KeyValuePair <IAsyncLocal, object> pair in this) { if (!ReferenceEquals(key, pair.Key)) { multi.UnsafeStore(index++, pair.Key, pair.Value); } } Debug.Assert(index == MultiElementAsyncLocalValueMap.MaxMultiElements); return(multi); } else { var map = new ManyElementAsyncLocalValueMap(count - 1); foreach (KeyValuePair <IAsyncLocal, object?> pair in this) { if (!ReferenceEquals(key, pair.Key)) { map[pair.Key] = pair.Value; } } Debug.Assert(map.Count == count - 1); return(map); } } // We were storing null and a null value may be treated as nonexistent, but the key wasn't in the map, so // there's nothing to change. Just return this instance. return(this); }