// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; namespace EpicGames.Core { /// /// View of a character string. Allows comparing/manipulating substrings without unnecessary memory allocations. /// public readonly struct StringView : IEquatable { /// /// Memory containing the characters /// public ReadOnlyMemory Memory { get; } /// /// Span for the sequence of characters /// public ReadOnlySpan Span => Memory.Span; /// /// Length of the sequence of characters /// public int Length => Memory.Length; /// /// Constructor /// /// String view to construct from public StringView(StringView text) { Memory = text.Memory; } /// /// Constructor /// /// String view to construct from /// Offset within the string for this view public StringView(StringView text, int index) { Memory = text.Memory.Slice(index); } /// /// Constructor /// /// String view to construct from /// Offset within the string for this view /// Number of characters to include public StringView(StringView text, int index, int count) { Memory = text.Memory.Slice(index, count); } /// /// Constructor /// /// The memory containing the characters public StringView(ReadOnlyMemory memory) { Memory = memory; } /// /// Constructor /// /// The memory containing the characters /// Offset within the string for this view public StringView(ReadOnlyMemory memory, int index) { Memory = memory.Slice(index); } /// /// Constructor /// /// The memory containing the characters /// Offset within the string for this view /// Number of characters to include public StringView(ReadOnlyMemory memory, int index, int count) { Memory = memory.Slice(index, count); } /// /// Constructor /// /// String to construct from public StringView(string text) { Memory = text.AsMemory(); } /// /// Constructs a view onto a substring of the given string /// /// String to construct from /// Offset within the string for this view public StringView(string text, int index) { Memory = text.AsMemory(index); } /// /// Constructs a view onto a substring of the given string /// /// String to construct from /// Offset within the string for this view /// Number of characters to include public StringView(string text, int index, int count) { Memory = text.AsMemory(index, count); } /// /// Equality comparer /// /// First string to compare /// Second string to compare /// True if the strings are equal public static bool operator ==(StringView x, StringView y) { return x.Equals(y); } /// /// Inequality comparer /// /// First string to compare /// Second string to compare /// True if the strings are equal public static bool operator !=(StringView x, StringView y) { return !x.Equals(y); } /// public override bool Equals(object? obj) { return obj is StringView view && Equals(view); } /// public override int GetHashCode() { return String.GetHashCode(Memory.Span, StringComparison.Ordinal); } /// public int GetHashCode(StringComparison comparisonType) { return String.GetHashCode(Memory.Span, comparisonType); } /// public override string ToString() { return new string(Memory.Span); } /// public bool Equals(StringView other) { return Equals(other, StringComparison.CurrentCulture); } /// public bool Equals(StringView? other, StringComparison comparisonType) { return other.HasValue && Memory.Span.Equals(other.Value.Memory.Span, comparisonType); } /// /// Implicit conversion operator from a regular string /// /// The string to construct from public static implicit operator StringView(string text) => new StringView(text); } /// /// Comparer for StringView objects /// public class StringViewComparer : IComparer, IEqualityComparer { /// /// Static instance of an ordinal StringView comparer /// public static StringViewComparer Ordinal { get; } = new StringViewComparer(StringComparison.Ordinal); /// /// Static instance of an ordinal StringView comparer which ignores case /// public static StringViewComparer OrdinalIgnoreCase { get; } = new StringViewComparer(StringComparison.OrdinalIgnoreCase); /// /// The comparison type /// public StringComparison ComparisonType { get; } /// /// Constructor /// /// Type of comparison to perform public StringViewComparer(StringComparison comparisonType) { ComparisonType = comparisonType; } /// public bool Equals(StringView x, StringView y) { return x.Span.Equals(y.Span, ComparisonType); } /// public int GetHashCode(StringView obj) { return String.GetHashCode(obj.Span, ComparisonType); } /// public int Compare(StringView x, StringView y) { return x.Span.CompareTo(y.Span, ComparisonType); } } /// /// Comparer for StringView objects. However, it implements UE style ignore case compare /// public class StringViewComparerUe : IComparer, IEqualityComparer { /// /// Static instance of an ordinal StringView comparer /// public static StringViewComparerUe Ordinal { get; } = new StringViewComparerUe(StringComparison.Ordinal); /// /// Static instance of an ordinal StringView comparer which ignores case /// public static StringViewComparerUe OrdinalIgnoreCase { get; } = new StringViewComparerUe(StringComparison.OrdinalIgnoreCase); /// /// The comparison type /// public StringComparison ComparisonType { get; } /// /// Constructor /// /// Type of comparison to perform public StringViewComparerUe(StringComparison comparisonType) { ComparisonType = comparisonType; } /// public bool Equals(StringView x, StringView y) { return x.Span.Equals(y.Span, ComparisonType); } /// public int GetHashCode(StringView obj) { return String.GetHashCode(obj.Span, ComparisonType); } /// public int Compare(StringView x, StringView y) { if (ComparisonType == StringComparison.OrdinalIgnoreCase) { return StringUtils.CompareIgnoreCaseUe(x.Span, y.Span); } else { return x.Span.CompareTo(y.Span, ComparisonType); } } } }