set { _noCache = value; }
}
- public ICollection<string> NoCacheHeaders
- {
- get
- {
- if (_noCacheHeaders == null)
- {
- _noCacheHeaders = new ObjectCollection<string>(s_checkIsValidToken);
- }
- return _noCacheHeaders;
- }
- }
+ public ICollection<string> NoCacheHeaders => _noCacheHeaders ??= new ObjectCollection<string>(s_checkIsValidToken);
public bool NoStore
{
set { _privateField = value; }
}
- public ICollection<string> PrivateHeaders
- {
- get
- {
- if (_privateHeaders == null)
- {
- _privateHeaders = new ObjectCollection<string>(s_checkIsValidToken);
- }
- return _privateHeaders;
- }
- }
+ public ICollection<string> PrivateHeaders => _privateHeaders ??= new ObjectCollection<string>(s_checkIsValidToken);
public bool MustRevalidate
{
set { _proxyRevalidate = value; }
}
- public ICollection<NameValueHeaderValue> Extensions
- {
- get
- {
- if (_extensions == null)
- {
- _extensions = new ObjectCollection<NameValueHeaderValue>();
- }
- return _extensions;
- }
- }
+ public ICollection<NameValueHeaderValue> Extensions => _extensions ??= new ObjectCollection<NameValueHeaderValue>();
public CacheControlHeaderValue()
{
return false;
}
- if (destination == null)
- {
- destination = new ObjectCollection<string>(s_checkIsValidToken);
- }
-
+ destination ??= new ObjectCollection<string>(s_checkIsValidToken);
destination.Add(valueString.Substring(current, tokenLength));
current = current + tokenLength;
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
+using System.Collections;
using System.Collections.Generic;
-using System.Collections.ObjectModel;
using System.Diagnostics;
namespace System.Net.Http.Headers
{
- // We need to prevent 'null' values in the collection. Since List<T> allows them, we will create
- // a custom collection class. It is less efficient than List<T> but only used for small collections.
- internal sealed class ObjectCollection<T> : Collection<T> where T : class
+ /// <summary>An <see cref="ICollection{T}"/> list that prohibits null elements and that is optimized for a small number of elements.</summary>
+ [DebuggerDisplay("Count = {Count}")]
+ [DebuggerTypeProxy(nameof(DebugView))]
+ internal sealed class ObjectCollection<T> : ICollection<T> where T : class
{
- private static readonly Action<T> s_defaultValidator = CheckNotNull;
+ private const int DefaultSize = 4;
- private readonly Action<T> _validator;
+ /// <summary>Optional delegate used to validate added items.</summary>
+ private readonly Action<T>? _validator;
+ /// <summary>null, a T, or a T[].</summary>
+ internal object? _items;
+ /// <summary>Number of elements stored in the collection.</summary>
+ internal int _size;
- public ObjectCollection()
- : this(s_defaultValidator)
+ public ObjectCollection() { }
+
+ public ObjectCollection(Action<T> validator) => _validator = validator;
+
+ public int Count => _size;
+
+ public bool IsReadOnly => false;
+
+ public void Add(T item)
{
+ // Validate the item, either just by checking it for null, or using a custom validator,
+ // which should also check for null.
+ if (_validator is null)
+ {
+ if (item is null)
+ {
+ throw new ArgumentNullException(nameof(item));
+ }
+ }
+ else
+ {
+ _validator.Invoke(item);
+ Debug.Assert(item != null);
+ }
+
+ if (_items is null)
+ {
+ // The collection is empty. Just store the new item directly.
+ _items = item;
+ _size = 1;
+ }
+ else if (_items is T existingItem)
+ {
+ // The collection has a single item stored directly. Upgrade to
+ // an array, and store both the existing and new items.
+ Debug.Assert(_size == 1);
+ T[] items = new T[DefaultSize];
+ items[0] = existingItem;
+ items[1] = item;
+ _items = items;
+ _size = 2;
+ }
+ else
+ {
+ T[] array = (T[])_items;
+ int size = _size;
+ if ((uint)size < (uint)array.Length)
+ {
+ // There's room in the existing array. Add the item.
+ array[size] = item;
+ }
+ else
+ {
+ // We need to grow the array. Do so, and store the new item.
+ Debug.Assert(_size > 0);
+ Debug.Assert(_size == array.Length);
+
+ var newItems = new T[array.Length * 2];
+ Array.Copy(array, newItems, size);
+ _items = newItems;
+ newItems[size] = item;
+ }
+ _size = size + 1;
+ }
}
- public ObjectCollection(Action<T> validator)
- : base(new List<T>())
+ public void Clear()
{
- Debug.Assert(validator != null, $"{nameof(validator)} must not be null.");
- _validator = validator;
+ _items = null;
+ _size = 0;
}
- // This is only used internally to enumerate the collection
- // without the enumerator allocation.
- public new List<T>.Enumerator GetEnumerator()
+ public bool Contains(T item) =>
+ ReferenceEquals(item, _items) ||
+ (_size != 0 && _items is T[] items && Array.IndexOf(items, item, 0, _size) != -1);
+
+ public void CopyTo(T[] array, int arrayIndex)
{
- return ((List<T>)Items).GetEnumerator();
+ if (_items is T[] items)
+ {
+ Array.Copy(items, 0, array, arrayIndex, _size);
+ }
+ else
+ {
+ Debug.Assert(_size == 0 || _size == 1);
+ if (array is null || _size > array.Length - arrayIndex)
+ {
+ // Use Array.CopyTo to throw the right exceptions.
+ new T[] { (T)_items! }.CopyTo(array!, arrayIndex);
+ }
+ else if (_size == 1)
+ {
+ array[arrayIndex] = (T)_items!;
+ }
+ }
}
- protected override void InsertItem(int index, T item)
+ public bool Remove(T item)
{
- _validator(item);
- base.InsertItem(index, item);
+ if (ReferenceEquals(_items, item))
+ {
+ _items = null;
+ _size = 0;
+ return true;
+ }
+
+ if (_items is T[] items)
+ {
+ int index = Array.IndexOf(items, item, 0, _size);
+ if (index != -1)
+ {
+ _size--;
+ if (index < _size)
+ {
+ Array.Copy(items, index + 1, items, index, _size - index);
+ }
+ items[_size] = null!;
+
+ return true;
+ }
+ }
+
+ return false;
}
- protected override void SetItem(int index, T item)
+ public Enumerator GetEnumerator() => new Enumerator(this);
+ IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
+ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
+
+ public struct Enumerator : IEnumerator<T>
{
- _validator(item);
- base.SetItem(index, item);
+ private readonly ObjectCollection<T> _list;
+ private int _index;
+ private T _current;
+
+ internal Enumerator(ObjectCollection<T> list)
+ {
+ _list = list;
+ _index = 0;
+ _current = default!;
+ }
+
+ public void Dispose() { }
+
+ public bool MoveNext()
+ {
+ ObjectCollection<T> list = _list;
+
+ if ((uint)_index < (uint)list._size)
+ {
+ _current = list._items is T[] items ? items[_index] : (T)list._items!;
+ _index++;
+ return true;
+ }
+
+ _index = _list._size + 1;
+ _current = default!;
+ return false;
+ }
+
+ public T Current => _current!;
+
+ object? IEnumerator.Current => _current;
+
+ void IEnumerator.Reset()
+ {
+ _index = 0;
+ _current = default!;
+ }
}
- private static void CheckNotNull(T item)
+ internal sealed class DebugView
{
- // Null values cannot be added to the collection.
- if (item == null)
+ private readonly ObjectCollection<T> _collection;
+
+ public DebugView(ObjectCollection<T> collection) => _collection = collection ?? throw new ArgumentNullException(nameof(collection));
+
+ [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
+ public T[] Items
{
- throw new ArgumentNullException(nameof(item));
+ get
+ {
+ T[] items = new T[_collection.Count];
+ _collection.CopyTo(items, 0);
+ return items;
+ }
}
}
}