// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.CompilerServices;
namespace Microsoft.AspNetCore.Http
{
///
/// A collection of arbitrary metadata associated with an endpoint.
///
///
/// instances contain a list of metadata items
/// of arbitrary types. The metadata items are stored as an ordered collection with
/// items arranged in ascending order of precedence.
///
public sealed class EndpointMetadataCollection : IReadOnlyList
{
///
/// An empty .
///
public static readonly EndpointMetadataCollection Empty = new EndpointMetadataCollection(Array.Empty());
private readonly object[] _items;
private readonly ConcurrentDictionary _cache;
///
/// Creates a new .
///
/// The metadata items.
public EndpointMetadataCollection(IEnumerable items)
{
if (items == null)
{
throw new ArgumentNullException(nameof(items));
}
_items = items.ToArray();
_cache = new ConcurrentDictionary();
}
///
/// Creates a new .
///
/// The metadata items.
public EndpointMetadataCollection(params object[] items)
: this((IEnumerable)items)
{
}
///
/// Gets the item at .
///
/// The index of the item to retrieve.
/// The item at .
public object this[int index] => _items[index];
///
/// Gets the count of metadata items.
///
public int Count => _items.Length;
///
/// Gets the most significant metadata item of type .
///
/// The type of metadata to retrieve.
///
/// The most significant metadata of type or null .
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public T GetMetadata() where T : class
{
if (_cache.TryGetValue(typeof(T), out var result))
{
var length = result.Length;
return length > 0 ? (T)result[length - 1] : default;
}
return GetMetadataSlow();
}
private T GetMetadataSlow() where T : class
{
var array = GetOrderedMetadataSlow();
var length = array.Length;
return length > 0 ? array[length - 1] : default;
}
///
/// Gets the metadata items of type in ascending
/// order of precedence.
///
/// The type of metadata.
/// A sequence of metadata items of .
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IEnumerable GetOrderedMetadata() where T : class
{
if (_cache.TryGetValue(typeof(T), out var result))
{
return (T[])result;
}
return GetOrderedMetadataSlow();
}
private T[] GetOrderedMetadataSlow() where T : class
{
var items = new List();
for (var i = 0; i < _items.Length; i++)
{
if (_items[i] is T item)
{
items.Add(item);
}
}
var array = items.ToArray();
_cache.TryAdd(typeof(T), array);
return array;
}
///
/// Gets an of all metadata items.
///
/// An of all metadata items.
public Enumerator GetEnumerator() => new Enumerator(this);
///
/// Gets an of all metadata items.
///
/// An of all metadata items.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
///
/// Gets an of all metadata items.
///
/// An of all metadata items.
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
///
/// Enumerates the elements of an .
///
public struct Enumerator : IEnumerator
{
// Intentionally not readonly to prevent defensive struct copies
private object[] _items;
private int _index;
internal Enumerator(EndpointMetadataCollection collection)
{
_items = collection._items;
_index = 0;
Current = null;
}
///
/// Gets the element at the current position of the enumerator
///
public object Current { get; private set; }
///
/// Releases all resources used by the .
///
public void Dispose()
{
}
///
/// Advances the enumerator to the next element of the .
///
///
/// true if the enumerator was successfully advanced to the next element;
/// false if the enumerator has passed the end of the collection.
///
public bool MoveNext()
{
if (_index < _items.Length)
{
Current = _items[_index++];
return true;
}
Current = null;
return false;
}
///
/// Sets the enumerator to its initial position, which is before the first element in the collection.
///
public void Reset()
{
_index = 0;
Current = null;
}
}
}
}