| File: System\Diagnostics\DiagLinkedList.cs | Web Access |
| Project: src\runtime\src\libraries\System.Diagnostics.DiagnosticSource\src\System.Diagnostics.DiagnosticSource.csproj (System.Diagnostics.DiagnosticSource) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; using System.Collections.Generic; using System.Text; namespace System.Diagnostics { internal sealed partial class DiagNode<T> { public DiagNode(T value) => Value = value; public T Value; public DiagNode<T>? Next; } // We are not using the public LinkedList<T> because we need to ensure thread safety operation on the list. internal sealed class DiagLinkedList<T> : IEnumerable<T> { private DiagNode<T>? _first; private DiagNode<T>? _last; public DiagLinkedList() { } public DiagLinkedList(T firstValue) => _last = _first = new DiagNode<T>(firstValue); public DiagLinkedList(IEnumerator<T> e) { Debug.Assert(e is not null); _last = _first = new DiagNode<T>(e.Current); while (e.MoveNext()) { _last.Next = new DiagNode<T>(e.Current); _last = _last.Next; } } public DiagNode<T>? First => _first; public void Clear() { lock (this) { _first = _last = null; } } private void UnsafeAdd(DiagNode<T> newNode) { if (_first is null) { _first = _last = newNode; return; } Debug.Assert(_first is not null); Debug.Assert(_last is not null); _last!.Next = newNode; _last = newNode; } public void Add(T value) { DiagNode<T> newNode = new DiagNode<T>(value); lock (this) { UnsafeAdd(newNode); } } public bool AddIfNotExist(T value, Func<T, T, bool> compare) { lock (this) { DiagNode<T>? current = _first; while (current is not null) { if (compare(value, current.Value)) { return false; } current = current.Next; } DiagNode<T> newNode = new DiagNode<T>(value); UnsafeAdd(newNode); return true; } } public T? Remove(T value, Func<T, T, bool> compare) { lock (this) { DiagNode<T>? previous = _first; if (previous is null) { return default; } if (compare(previous.Value, value)) { _first = previous.Next; if (_first is null) { _last = null; } return previous.Value; } DiagNode<T>? current = previous.Next; while (current is not null) { if (compare(current.Value, value)) { previous.Next = current.Next; if (object.ReferenceEquals(_last, current)) { _last = previous; } return current.Value; } previous = current; current = current.Next; } return default; } } public void AddFront(T value) { DiagNode<T> newNode = new DiagNode<T>(value); lock (this) { newNode.Next = _first; _first = newNode; } } public DiagEnumerator<T> GetEnumerator() => new DiagEnumerator<T>(_first); IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); private static void ActivityLinkToString(ref ActivityLink al, ref ValueStringBuilder vsb) { ActivityContext ac = al.Context; vsb.Append("("); vsb.Append(ac.TraceId.ToHexString()); vsb.Append(",\u200B"); vsb.Append(ac.SpanId.ToHexString()); vsb.Append(",\u200B"); vsb.Append(ac.TraceFlags.ToString()); vsb.Append(",\u200B"); vsb.Append(ac.TraceState ?? "null"); vsb.Append(",\u200B"); vsb.Append(ac.IsRemote ? "true" : "false"); if (al.Tags is not null) { vsb.Append(",\u200B["); string sep = ""; foreach (KeyValuePair<string, object?> kvp in al.EnumerateTagObjects()) { vsb.Append(sep); vsb.Append(kvp.Key); vsb.Append(":\u200B"); vsb.Append(kvp.Value?.ToString() ?? "null"); sep = ",\u200B"; } vsb.Append("]"); } vsb.Append(")"); } private static void ActivityEventToString(ref ActivityEvent ae, ref ValueStringBuilder vsb) { vsb.Append("("); vsb.Append(ae.Name); vsb.Append(",\u200B"); vsb.Append(ae.Timestamp.ToString("o")); if (ae.Tags is not null) { vsb.Append(",\u200B["); string sep = ""; foreach (KeyValuePair<string, object?> kvp in ae.EnumerateTagObjects()) { vsb.Append(sep); vsb.Append(kvp.Key); vsb.Append(":\u200B"); vsb.Append(kvp.Value?.ToString() ?? "null"); sep = ",\u200B"; } vsb.Append("]"); } vsb.Append(")"); } public override string ToString() { lock (this) { DiagNode<T>? current = _first; if (current is null) { return "[]"; } var vsb = new ValueStringBuilder(stackalloc char[256]); vsb.Append("["); if (typeof(T) == typeof(ActivityLink)) { while (current is not null) { ActivityLink al = (ActivityLink)(object)current.Value!; ActivityLinkToString(ref al, ref vsb); current = current.Next; if (current is not null) { vsb.Append(",\u200B"); } } } else if (typeof(T) == typeof(ActivityEvent)) { while (current is not null) { ActivityEvent ae = (ActivityEvent)(object)current.Value!; ActivityEventToString(ref ae, ref vsb); current = current.Next; if (current is not null) { vsb.Append(",\u200B"); } } } else { while (current is not null) { vsb.Append(current.Value?.ToString() ?? "null"); current = current.Next; if (current is not null) { vsb.Append(",\u200B"); } } } vsb.Append("]"); return vsb.ToString(); } } } internal struct DiagEnumerator<T> : IEnumerator<T> { private static readonly DiagNode<T> s_Empty = new DiagNode<T>(default!); private DiagNode<T>? _nextNode; private DiagNode<T> _currentNode; public DiagEnumerator(DiagNode<T>? head) { _nextNode = head; _currentNode = s_Empty; } public T Current => _currentNode.Value; object? IEnumerator.Current => Current; public bool MoveNext() { if (_nextNode == null) { _currentNode = s_Empty; return false; } _currentNode = _nextNode; _nextNode = _nextNode.Next; return true; } public void Reset() => throw new NotSupportedException(); public void Dispose() { } } }