// dnlib: See LICENSE.txt for more info
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Text;
namespace dnlib.DotNet {
///
/// Compares s
///
public sealed class UTF8StringEqualityComparer : IEqualityComparer {
///
/// The default instance
///
public static readonly UTF8StringEqualityComparer Instance = new UTF8StringEqualityComparer();
///
public bool Equals(UTF8String x, UTF8String y) => UTF8String.Equals(x, y);
///
public int GetHashCode(UTF8String obj) => UTF8String.GetHashCode(obj);
}
///
/// A UTF-8 encoded string where the original data is kept in memory to avoid conversions
/// when the data is not really valid UTF-8 encoded data
///
/// When comparing strings, a byte compare is performed. The reason is that this
/// is what the CLR does when comparing strings in the #Strings stream.
[DebuggerDisplay("{String}")]
public sealed class UTF8String : IEquatable, IComparable {
///
/// An empty
///
public static readonly UTF8String Empty = new UTF8String(string.Empty);
readonly byte[] data;
string asString;
///
/// Gets the value as a UTF8 decoded string. Only use it for display purposes,
/// not for serialization.
///
public string String {
get {
if (asString is null)
asString = ConvertFromUTF8(data);
return asString;
}
}
///
/// Gets the original encoded data. Don't modify this data.
///
public byte[] Data => data;
///
/// Gets the length of the this as a . I.e., it's the same as
/// String.Length.
///
///
public int Length => String.Length;
///
/// Gets the length of the raw data. It's the same as Data.Length
///
///
public int DataLength => data is null ? 0 : data.Length;
///
/// Checks whether is null or if its data is null.
///
/// The instance to check
/// true if null or empty, false otherwise
public static bool IsNull(UTF8String utf8) => utf8 is null || utf8.data is null;
///
/// Checks whether is null or if its data is null or the
/// data is zero length.
///
/// The instance to check
/// true if null or empty, false otherwise
public static bool IsNullOrEmpty(UTF8String utf8) => utf8 is null || utf8.data is null || utf8.data.Length == 0;
/// Implicit conversion from to
public static implicit operator string(UTF8String s) => UTF8String.ToSystemString(s);
/// Implicit conversion from to
public static implicit operator UTF8String(string s) => s is null ? null : new UTF8String(s);
///
/// Converts it to a
///
/// The UTF-8 string instace or null
/// A or null if is null
public static string ToSystemString(UTF8String utf8) {
if (utf8 is null || utf8.data is null)
return null;
if (utf8.data.Length == 0)
return string.Empty;
return utf8.String;
}
///
/// Converts it to a or an empty string if is null
///
/// The UTF-8 string instace or null
/// A (never null)
public static string ToSystemStringOrEmpty(UTF8String utf8) => ToSystemString(utf8) ?? string.Empty;
///
/// Gets the hash code of a
///
/// Input
public static int GetHashCode(UTF8String utf8) {
if (IsNullOrEmpty(utf8))
return 0;
return Utils.GetHashCode(utf8.data);
}
///
public int CompareTo(UTF8String other) => CompareTo(this, other);
///
/// Compares two instances (case sensitive)
///
/// Instance #1 or null
/// Instance #2 or null
/// < 0 if a < b, 0 if a == b, > 0 if a > b
public static int CompareTo(UTF8String a, UTF8String b) => Utils.CompareTo(a?.data, b?.data);
///
/// Compares two instances (case insensitive)
///
/// Instance #1 or null
/// Instance #2 or null
/// < 0 if a < b, 0 if a == b, > 0 if a > b
public static int CaseInsensitiveCompareTo(UTF8String a, UTF8String b) {
if ((object)a == (object)b)
return 0;
var sa = ToSystemString(a);
var sb = ToSystemString(b);
if ((object)sa == (object)sb)
return 0;
if (sa is null)
return -1;
if (sb is null)
return 1;
return StringComparer.OrdinalIgnoreCase.Compare(sa, sb);
}
///
/// Compares two instances (case insensitive)
///
/// Instance #1 or null
/// Instance #2 or null
/// true if equals, false otherwise
public static bool CaseInsensitiveEquals(UTF8String a, UTF8String b) => CaseInsensitiveCompareTo(a, b) == 0;
/// Overloaded operator
public static bool operator ==(UTF8String left, UTF8String right) => CompareTo(left, right) == 0;
/// Overloaded operator
public static bool operator ==(UTF8String left, string right) => ToSystemString(left) == right;
/// Overloaded operator
public static bool operator ==(string left, UTF8String right) => left == ToSystemString(right);
/// Overloaded operator
public static bool operator !=(UTF8String left, UTF8String right) => CompareTo(left, right) != 0;
/// Overloaded operator
public static bool operator !=(UTF8String left, string right) => ToSystemString(left) != right;
/// Overloaded operator
public static bool operator !=(string left, UTF8String right) => left != ToSystemString(right);
/// Overloaded operator
public static bool operator >(UTF8String left, UTF8String right) => CompareTo(left, right) > 0;
/// Overloaded operator
public static bool operator <(UTF8String left, UTF8String right) => CompareTo(left, right) < 0;
/// Overloaded operator
public static bool operator >=(UTF8String left, UTF8String right) => CompareTo(left, right) >= 0;
/// Overloaded operator
public static bool operator <=(UTF8String left, UTF8String right) => CompareTo(left, right) <= 0;
///
/// Constructor
///
/// UTF-8 data that this instance now owns
public UTF8String(byte[] data) => this.data = data;
///
/// Constructor
///
/// The string
public UTF8String(string s)
: this(s is null ? null : Encoding.UTF8.GetBytes(s)) {
}
static string ConvertFromUTF8(byte[] data) {
if (data is null)
return null;
try {
return Encoding.UTF8.GetString(data);
}
catch {
}
return null;
}
///
/// Compares two instances
///
/// First
/// Second
/// true if equals, false otherwise
public static bool Equals(UTF8String a, UTF8String b) => CompareTo(a, b) == 0;
///
public bool Equals(UTF8String other) => CompareTo(this, other) == 0;
///
public override bool Equals(object obj) {
var other = obj as UTF8String;
if (other is null)
return false;
return CompareTo(this, other) == 0;
}
///
/// Checks whether exists in this string
///
/// Value to find
/// true if exists in string or is the
/// empty string, else false
public bool Contains(string value) => String.Contains(value);
///
/// Checks whether matches the end of this string
///
/// Value
///
public bool EndsWith(string value) => String.EndsWith(value);
///
/// Checks whether matches the end of this string
///
/// Value
/// true to ignore case
/// Culture info
///
public bool EndsWith(string value, bool ignoreCase, CultureInfo culture) => String.EndsWith(value, ignoreCase, culture);
///
/// Checks whether matches the end of this string
///
/// Value
/// Comparison type
///
public bool EndsWith(string value, StringComparison comparisonType) => String.EndsWith(value, comparisonType);
///
/// Checks whether matches the beginning of this string
///
/// Value
///
public bool StartsWith(string value) => String.StartsWith(value);
///
/// Checks whether matches the beginning of this string
///
/// Value
/// true to ignore case
/// Culture info
///
public bool StartsWith(string value, bool ignoreCase, CultureInfo culture) => String.StartsWith(value, ignoreCase, culture);
///
/// Checks whether matches the beginning of this string
///
/// Value
/// Comparison type
///
public bool StartsWith(string value, StringComparison comparisonType) => String.StartsWith(value, comparisonType);
///
/// Compares this instance with
///
/// Other string
/// < 0 if a < b, 0 if a == b, > 0 if a > b
public int CompareTo(string strB) => String.CompareTo(strB);
///
/// Returns the index of the first character in this string
///
/// Character
/// The index of or -1 if not found
public int IndexOf(char value) => String.IndexOf(value);
///
/// Returns the index of the first character in this string
/// starting from index
///
/// Character
/// Start index
/// The index of or -1 if not found
public int IndexOf(char value, int startIndex) => String.IndexOf(value, startIndex);
///
/// Returns the index of the first character in this string
/// starting from index for max
/// characters.
///
/// Character
/// Start index
/// Max number of chars to scan
/// The index of or -1 if not found
public int IndexOf(char value, int startIndex, int count) => String.IndexOf(value, startIndex, count);
///
/// Returns the index of the first sub string in this string
///
/// String
/// The index of or -1 if not found
public int IndexOf(string value) => String.IndexOf(value);
///
/// Returns the index of the first sub string in this string
/// starting from index
///
/// String
/// Start index
/// The index of or -1 if not found
public int IndexOf(string value, int startIndex) => String.IndexOf(value, startIndex);
///
/// Returns the index of the first sub string in this string
/// starting from index for max
/// characters.
///
/// String
/// Start index
/// Max number of chars to scan
/// The index of or -1 if not found
public int IndexOf(string value, int startIndex, int count) => String.IndexOf(value, startIndex, count);
///
/// Returns the index of the first sub string in this string
/// starting from index for max
/// characters.
///
/// String
/// Start index
/// Max number of chars to scan
/// Comparison type
/// The index of or -1 if not found
public int IndexOf(string value, int startIndex, int count, StringComparison comparisonType) => String.IndexOf(value, startIndex, count, comparisonType);
///
/// Returns the index of the first sub string in this string
/// starting from index
///
/// String
/// Start index
/// Comparison type
/// The index of or -1 if not found
public int IndexOf(string value, int startIndex, StringComparison comparisonType) => String.IndexOf(value, startIndex, comparisonType);
///
/// Returns the index of the first sub string in this string
///
/// String
/// Comparison type
/// The index of or -1 if not found
public int IndexOf(string value, StringComparison comparisonType) => String.IndexOf(value, comparisonType);
///
/// Returns the index of the last character in this string
///
/// Character
/// The index of or -1 if not found
public int LastIndexOf(char value) => String.LastIndexOf(value);
///
/// Returns the index of the last character in this string
/// starting from index
///
/// Character
/// Start index
/// The index of or -1 if not found
public int LastIndexOf(char value, int startIndex) => String.LastIndexOf(value, startIndex);
///
/// Returns the index of the last character in this string
/// starting from index for max
/// characters.
///
/// Character
/// Start index
/// Max number of chars to scan
/// The index of or -1 if not found
public int LastIndexOf(char value, int startIndex, int count) => String.LastIndexOf(value, startIndex, count);
///
/// Returns the index of the last sub string in this string
///
/// String
/// The index of or -1 if not found
public int LastIndexOf(string value) => String.LastIndexOf(value);
///
/// Returns the index of the last sub string in this string
/// starting from index
///
/// String
/// Start index
/// The index of or -1 if not found
public int LastIndexOf(string value, int startIndex) => String.LastIndexOf(value, startIndex);
///
/// Returns the index of the last sub string in this string
/// starting from index for max
/// characters.
///
/// String
/// Start index
/// Max number of chars to scan
/// The index of or -1 if not found
public int LastIndexOf(string value, int startIndex, int count) => String.LastIndexOf(value, startIndex, count);
///
/// Returns the index of the last sub string in this string
/// starting from index for max
/// characters.
///
/// String
/// Start index
/// Max number of chars to scan
/// Comparison type
/// The index of or -1 if not found
public int LastIndexOf(string value, int startIndex, int count, StringComparison comparisonType) => String.LastIndexOf(value, startIndex, count, comparisonType);
///
/// Returns the index of the last sub string in this string
/// starting from index
///
/// String
/// Start index
/// Comparison type
/// The index of or -1 if not found
public int LastIndexOf(string value, int startIndex, StringComparison comparisonType) => String.LastIndexOf(value, startIndex, comparisonType);
///
/// Returns the index of the last sub string in this string
///
/// String
/// Comparison type
/// The index of or -1 if not found
public int LastIndexOf(string value, StringComparison comparisonType) => String.LastIndexOf(value, comparisonType);
///
/// Inserts string at a index
///
/// Start index
/// Value to insert
/// A new instance with the inserted at position
///
public UTF8String Insert(int startIndex, string value) => new UTF8String(String.Insert(startIndex, value));
///
/// Removes all characters starting from position
///
/// Start index
/// A new instance
public UTF8String Remove(int startIndex) => new UTF8String(String.Remove(startIndex));
///
/// Removes characters starting from position
///
///
/// Start index
/// Number of characters to remove
/// A new instance
public UTF8String Remove(int startIndex, int count) => new UTF8String(String.Remove(startIndex, count));
///
/// Replaces all characters with
///
/// Character to find
/// Character to replace all
/// A new instance
public UTF8String Replace(char oldChar, char newChar) => new UTF8String(String.Replace(oldChar, newChar));
///
/// Replaces all sub strings with
///
/// Sub string to find
/// Sub string to replace all
/// A new instance
public UTF8String Replace(string oldValue, string newValue) => new UTF8String(String.Replace(oldValue, newValue));
///
/// Returns a sub string of this string starting at offset
///
/// Start index
/// A new instance
public UTF8String Substring(int startIndex) => new UTF8String(String.Substring(startIndex));
///
/// Returns a sub string of this string starting at offset .
/// Length of sub string is .
///
/// Start index
/// Length of sub string
/// A new instance
public UTF8String Substring(int startIndex, int length) => new UTF8String(String.Substring(startIndex, length));
///
/// Returns the lower case version of this string
///
/// A new instance
public UTF8String ToLower() => new UTF8String(String.ToLower());
///
/// Returns the lower case version of this string
///
/// Culture info
/// A new instance
public UTF8String ToLower(CultureInfo culture) => new UTF8String(String.ToLower(culture));
///
/// Returns the lower case version of this string using the invariant culture
///
/// A new instance
public UTF8String ToLowerInvariant() => new UTF8String(String.ToLowerInvariant());
///
/// Returns the upper case version of this string
///
/// A new instance
public UTF8String ToUpper() => new UTF8String(String.ToUpper());
///
/// Returns the upper case version of this string
///
/// Culture info
/// A new instance
public UTF8String ToUpper(CultureInfo culture) => new UTF8String(String.ToUpper(culture));
///
/// Returns the upper case version of this string using the invariant culture
///
/// A new instance
public UTF8String ToUpperInvariant() => new UTF8String(String.ToUpperInvariant());
///
/// Removes all leading and trailing whitespace characters
///
/// A new instance
public UTF8String Trim() => new UTF8String(String.Trim());
///
public override int GetHashCode() => UTF8String.GetHashCode(this);
///
public override string ToString() => String;
}
}