using System; using System.Collections.Generic; using System.Globalization; namespace PckStudio.Classes.Utils; ///! Credits ///! /// /// Provides the RLE codec for any integer data type. /// /// The data's type. Must be an integer type or an ArgumentException will be thrown static class RLE where T : struct, IConvertible { /// /// This is the marker that identifies a compressed run /// private static T rleMarker; /// /// A run can be at most as long as the marker - 1 /// private static ulong maxLength; static RLE() { GetMaxValues(); } /// /// RLE-Encodes a data set. /// /// The data to encode /// Encoded data public static IEnumerable Encode(IEnumerable data) { var enumerator = data.GetEnumerator(); if (!enumerator.MoveNext()) yield break; var firstRunValue = enumerator.Current; ulong runLength = 1; while (enumerator.MoveNext()) { var currentValue = enumerator.Current; // if the current value is the value of the current run, don't yield anything, // just extend the run if (currentValue.Equals(firstRunValue)) runLength++; else { // the current value is different from the current run // yield what we have so far foreach (var item in MakeRun(firstRunValue, runLength)) yield return item; // and reset the run firstRunValue = currentValue; runLength = 1; } // if there are very many identical values, don't exceed the max length if (runLength > maxLength) { foreach (var item in MakeRun(firstRunValue, maxLength)) yield return item; runLength -= maxLength; } } //yield everything that has been buffered foreach (var item in MakeRun(firstRunValue, runLength)) yield return item; } /// /// Decodes RLE-encoded data /// /// RLE-encoded data /// The original data public static IEnumerable Decode(IEnumerable data) { var enumerator = data.GetEnumerator(); if (!enumerator.MoveNext()) yield break; do { var value = enumerator.Current; if (!value.Equals(rleMarker)) { //an ordinary value yield return value; } else { //might be flag or escape //examine the next value if (!enumerator.MoveNext()) throw new ArgumentException("The provided data is not properly encoded."); if (enumerator.Current.Equals(rleMarker)) { //escaped value yield return value; } else { //rle marker var length = enumerator.Current.ToInt64(CultureInfo.InvariantCulture); if (!enumerator.MoveNext()) throw new ArgumentException("The provided data is not properly encoded."); var val = enumerator.Current; for (var j = 0; j < length+1; ++j) yield return val; } } } while (enumerator.MoveNext()); } private static IEnumerable MakeRun(T value, ulong length) { if ((length <= 3 && !value.Equals(rleMarker)) || length <= 1) { //don't compress this run, it is just too small for (ulong i = 0; i < length; i++) { yield return value.Equals(rleMarker) ? rleMarker : value; } } else { //compressed run yield return rleMarker; yield return (T)(dynamic)(length-1); yield return value; } } private static void GetMaxValues() { TypeCode typeCode = Type.GetTypeCode(typeof(T)); switch (typeCode) { case TypeCode.Byte: { var limit = byte.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.Char: { var limit = char.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.Int16: { var limit = short.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.Int32: { var limit = int.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.Int64: { var limit = long.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.SByte: { var limit = sbyte.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.UInt16: { var limit = ushort.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.UInt32: { var limit = uint.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } case TypeCode.UInt64: { var limit = ulong.MaxValue; rleMarker = __refvalue(__makeref(limit), T); maxLength = (ulong)(limit - 1); break; } default: throw new ArgumentException("The provided type parameter is not an integer type"); } } }