using System.IO.Compression; using System.Text; using K4os.Compression.LZ4.Streams; namespace Nbt; internal static class Utils { #region conversion stuff public static TTo Convert(this TFrom value, Converter converter) => value is TTo sameValue ? sameValue : converter(value); #endregion #region enumerable stuff public static T[] AsArray(this IEnumerable enumerable) => Convert(enumerable, Enumerable.ToArray); public static IList AsIList(this IEnumerable enumerable) => Convert, IList>(enumerable, Enumerable.ToList); public static List AsList(this IEnumerable enumerable) => Convert(enumerable, Enumerable.ToList); public static IEnumerable Insert(this IEnumerable e, int index, T value) { ArgumentOutOfRangeException.ThrowIfNegative(index); if (e.TryGetNonEnumeratedCount(out var count)) { ArgumentOutOfRangeException.ThrowIfGreaterThan(index, count); } var i = 0; foreach (var o in e) { if (i == index) { yield return value; } yield return o; i++; } if (i == index) { yield return value; } } public static IEnumerable Replace(this IEnumerable e, int index, T value) { ArgumentOutOfRangeException.ThrowIfNegative(index); if (e.TryGetNonEnumeratedCount(out var count)) { ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(index, count); } var i = 0; foreach (var o in e) { yield return i == index ? value : o; i++; } } public static IEnumerable Yield(this T value) { yield return value; } #endregion #region streams, readers and writers public static GZipStream CreateGZipDeflater(Stream stream, bool leaveOpen) => new(stream, CompressionMode.Compress, leaveOpen); public static ZLibStream CreateZLibDeflater(Stream stream, bool leaveOpen) => new(stream, CompressionMode.Compress, leaveOpen); public static LZ4EncoderStream CreateLZ4Deflater(Stream stream, bool leaveOpen) => LZ4Stream.Encode(stream, leaveOpen: leaveOpen); public static GZipStream CreateGZipInflater(Stream stream, bool leaveOpen) => new(stream, CompressionMode.Decompress, leaveOpen); public static ZLibStream CreateZLibInflater(Stream stream, bool leaveOpen) => new(stream, CompressionMode.Decompress, leaveOpen); public static LZ4DecoderStream CreateLZ4Inflater(Stream stream, bool leaveOpen) => LZ4Stream.Decode(stream, leaveOpen: leaveOpen); public static char SkipWhiteSpaces(TextReader reader) { while (true) { var i = reader.Read(); if (i < 0) { throw new EndOfStreamException(); // return i; } var c = (char)i; if (char.IsWhiteSpace(c)) { continue; } return c; } } #endregion #region string and char quotation and whatnot public static string Repeat(this string s, int c) => c switch { < 0 => throw new ArgumentException("Count must be positive.", nameof(c)), 0 => string.Empty, 1 => s, _ => string.IsNullOrEmpty(s) ? s : string.Create(s.Length * c, (s, c), static (span, state) => { var (s, c) = state; var n = s.Length; for (var i = 0; i < c; i++) { var ss = span.Slice(n * i, n); s.CopyTo(ss); } }) }; public static string Unquote(string s, char quoteChar, char escapeChar) { var length = s.Length; if (length < 2 || s[0] != quoteChar || s[^1] != quoteChar) { throw new ArgumentException("Invalid quoted string", nameof(s)); } var sb = new StringBuilder(); var escape = false; for (var i = 1; i < length - 1; i++) { var c = s[i]; if (escape) { escape = false; switch (c) { case 't': c = '\t'; break; case 'r': c = '\r'; break; case 'n': c = '\n'; break; } } else if (c == quoteChar) { throw new ArgumentException("Invalid quoted string", nameof(s)); } else if (c == escapeChar) { escape = true; continue; } sb.Append(c); } return sb.ToString(); } public static string Quote(string s, char quoteChar, char escapeChar) { using var writer = new StringWriter(); Quote(writer, s, quoteChar, escapeChar); return writer.ToString(); } public static void Quote(TextWriter writer, string s, char quoteChar, char escapeChar) { if (s.Length == 0) { writer.Write(quoteChar); writer.Write(quoteChar); return; } using var quoter = new QuotedWriter(writer, quoteChar, escapeChar, true); quoter.Write(s); } #endregion #region equalities and comparisons public static bool EnumerableEquals(IEnumerable a, IEnumerable b, IEqualityComparer? elementComparer = null) => ReferenceEquals(a, b) || a is not null && b is not null && a.SequenceEqual(b, elementComparer); public static bool DictionaryEquals(IDictionary a, IDictionary b, IEqualityComparer? valueComparer = null) { if (ReferenceEquals(a, b)) { return true; } if (a is null || b is null) { return false; } var n = a.Count; if (n != b.Count) { return false; } if (n is 0) { return true; } valueComparer ??= EqualityComparer.Default; foreach (var entry in a) { if (!b.TryGetValue(entry.Key, out var value) || !valueComparer.Equals(entry.Value, value)) { return false; } } return true; } #endregion }