| File: src\runtime\src\libraries\System.Text.Json\Common\JsonHelpers.cs | Web Access |
| Project: src\runtime\src\libraries\System.Text.Json\src\System.Text.Json.csproj (System.Text.Json) |
// 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.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using System.Text.Json.Serialization; namespace System.Text.Json { internal static partial class JsonHelpers { #if !NET /// <summary> /// netstandard/netfx polyfill for Dictionary.TryAdd /// </summary> public static bool TryAdd<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, TKey key, TValue value) where TKey : notnull { if (!dictionary.ContainsKey(key)) { dictionary[key] = value; return true; } return false; } /// <summary> /// netstandard/netfx polyfill for IDictionary.TryAdd /// </summary> public static bool TryAdd<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value) where TKey : notnull { if (!dictionary.ContainsKey(key)) { dictionary[key] = value; return true; } return false; } /// <summary> /// netstandard/netfx polyfill for Queue.TryDequeue /// </summary> public static bool TryDequeue<T>(this Queue<T> queue, [NotNullWhen(true)] out T? result) { if (queue.Count > 0) { result = queue.Dequeue(); return true; } result = default; return false; } #endif internal static bool RequiresSpecialNumberHandlingOnWrite(JsonNumberHandling? handling) { return handling != null ? (handling.Value & (JsonNumberHandling.WriteAsString | JsonNumberHandling.AllowNamedFloatingPointLiterals)) != 0 : false; } /// <summary> /// Provides an in-place, stable sorting implementation for List. /// </summary> internal static void StableSortByKey<T, TKey>(this List<T> items, Func<T, TKey> keySelector) where TKey : unmanaged, IComparable<TKey> { #if NET Span<T> span = CollectionsMarshal.AsSpan(items); // Tuples implement lexical ordering OOTB which can be used to encode stable sorting // using the actual key as the first element and index as the second element. const int StackallocThreshold = 32; Span<(TKey, int)> keys = span.Length <= StackallocThreshold ? (stackalloc (TKey, int)[StackallocThreshold]).Slice(0, span.Length) : new (TKey, int)[span.Length]; for (int i = 0; i < keys.Length; i++) { keys[i] = (keySelector(span[i]), i); } MemoryExtensions.Sort(keys, span); #else T[] arrayCopy = items.ToArray(); (TKey, int)[] keys = new (TKey, int)[arrayCopy.Length]; for (int i = 0; i < keys.Length; i++) { keys[i] = (keySelector(arrayCopy[i]), i); } Array.Sort(keys, arrayCopy); items.Clear(); items.AddRange(arrayCopy); #endif } /// <summary> /// Traverses a DAG and returns its nodes applying topological sorting to the result. /// </summary> public static T[] TraverseGraphWithTopologicalSort<T>(T entryNode, Func<T, ICollection<T>> getChildren, IEqualityComparer<T>? comparer = null) where T : notnull { comparer ??= EqualityComparer<T>.Default; // Implements Kahn's algorithm. // Step 1. Traverse and build the graph, labeling each node with an integer. var nodes = new List<T> { entryNode }; // the integer-to-node mapping var nodeIndex = new Dictionary<T, int>(comparer) { [entryNode] = 0 }; // the node-to-integer mapping var adjacency = new List<bool[]?>(); // the growable adjacency matrix var childlessQueue = new Queue<int>(); // the queue of nodes without children or whose children have been visited for (int i = 0; i < nodes.Count; i++) { T next = nodes[i]; ICollection<T> children = getChildren(next); int count = children.Count; if (count == 0) { adjacency.Add(null); // can use null in this row of the adjacency matrix. childlessQueue.Enqueue(i); continue; } var adjacencyRow = new bool[Math.Max(nodes.Count, count)]; foreach (T childNode in children) { if (!nodeIndex.TryGetValue(childNode, out int index)) { // this is the first time we're encountering this node. // Assign it an index and append it to the maps. index = nodes.Count; nodeIndex.Add(childNode, index); nodes.Add(childNode); } // Grow the adjacency row as appropriate. if (index >= adjacencyRow.Length) { Array.Resize(ref adjacencyRow, index + 1); } // Set the relevant bit in the adjacency row. adjacencyRow[index] = true; } // Append the row to the adjacency matrix. adjacency.Add(adjacencyRow); } Debug.Assert(childlessQueue.Count > 0, "The graph contains cycles."); // Step 2. Build the sorted array, walking from the nodes without children upward. var sortedNodes = new T[nodes.Count]; int idx = sortedNodes.Length; do { int nextIndex = childlessQueue.Dequeue(); sortedNodes[--idx] = nodes[nextIndex]; // Iterate over the adjacency matrix, removing any occurrence of nextIndex. for (int i = 0; i < adjacency.Count; i++) { if (adjacency[i] is { } childMap && nextIndex < childMap.Length && childMap[nextIndex]) { childMap[nextIndex] = false; if (childMap.AsSpan().IndexOf(true) == -1) { // nextIndex was the last child removed from i, add to queue. childlessQueue.Enqueue(i); } } } } while (childlessQueue.Count > 0); Debug.Assert(idx == 0, "should have populated the entire sortedNodes array."); return sortedNodes; } } }