using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Reflection.Emit; using System.Runtime.InteropServices; #if NETCORE using SharpCompress.Buffers; #endif using SharpCompress.Readers; namespace SharpCompress { internal static class Utility { public static ReadOnlyCollection ToReadOnly(this IEnumerable items) { return new ReadOnlyCollection(items.ToList()); } /// /// Performs an unsigned bitwise right shift with the specified number /// /// Number to operate on /// Ammount of bits to shift /// The resulting number from the shift operation public static int URShift(int number, int bits) { if (number >= 0) { return number >> bits; } return (number >> bits) + (2 << ~bits); } /// /// Performs an unsigned bitwise right shift with the specified number /// /// Number to operate on /// Ammount of bits to shift /// The resulting number from the shift operation public static long URShift(long number, int bits) { if (number >= 0) { return number >> bits; } return (number >> bits) + (2L << ~bits); } /// /// Fills the array with an specific value from an specific index to an specific index. /// /// The array to be filled. /// The first index to be filled. /// The last index to be filled. /// The value to fill the array with. public static void Fill(T[] array, int fromindex, int toindex, T val) where T : struct { if (array.Length == 0) { throw new NullReferenceException(); } if (fromindex > toindex) { throw new ArgumentException(); } if ((fromindex < 0) || array.Length < toindex) { throw new IndexOutOfRangeException(); } for (int index = (fromindex > 0) ? fromindex-- : fromindex; index < toindex; index++) { array[index] = val; } } #if NET45 // super fast memset, up to 40x faster than for loop on large arrays // see https://stackoverflow.com/questions/1897555/what-is-the-equivalent-of-memset-in-c private static readonly Action MemsetDelegate = CreateMemsetDelegate(); private static Action CreateMemsetDelegate() { var dynamicMethod = new DynamicMethod( "Memset", MethodAttributes.Public | MethodAttributes.Static, CallingConventions.Standard, null, new[] { typeof(IntPtr), typeof(byte), typeof(uint) }, typeof(Utility), true); var generator = dynamicMethod.GetILGenerator(); generator.Emit(OpCodes.Ldarg_0); generator.Emit(OpCodes.Ldarg_1); generator.Emit(OpCodes.Ldarg_2); generator.Emit(OpCodes.Initblk); generator.Emit(OpCodes.Ret); return (Action)dynamicMethod.CreateDelegate(typeof(Action)); } public static void Memset(byte[] array, byte what, int length) { var gcHandle = GCHandle.Alloc(array, GCHandleType.Pinned); MemsetDelegate(gcHandle.AddrOfPinnedObject(), what, (uint)length); gcHandle.Free(); } #else public static void Memset(byte[] array, byte what, int length) { for(var i = 0; i < length; i++) { array[i] = what; } } #endif public static void Memset(T[] array, T what, int length) { for(var i = 0; i < length; i++) { array[i] = what; } } public static void FillFast(T[] array, T val) where T : struct { for (int i = 0; i < array.Length; i++) { array[i] = val; } } public static void FillFast(T[] array, int start, int length, T val) where T : struct { int toIndex = start + length; for (int i = start; i < toIndex; i++) { array[i] = val; } } /// /// Fills the array with an specific value. /// /// The array to be filled. /// The value to fill the array with. public static void Fill(T[] array, T val) where T : struct { Fill(array, 0, array.Length, val); } public static void SetSize(this List list, int count) { if (count > list.Count) { for (int i = list.Count; i < count; i++) { list.Add(0x0); } } else { byte[] temp = new byte[count]; list.CopyTo(temp, 0); list.Clear(); list.AddRange(temp); } } public static void AddRange(this ICollection destination, IEnumerable source) { foreach (T item in source) { destination.Add(item); } } public static void ForEach(this IEnumerable items, Action action) { foreach (T item in items) { action(item); } } public static void Copy(Array sourceArray, long sourceIndex, Array destinationArray, long destinationIndex, long length) { if (sourceIndex > Int32.MaxValue || sourceIndex < Int32.MinValue) throw new ArgumentOutOfRangeException(); if (destinationIndex > Int32.MaxValue || destinationIndex < Int32.MinValue) throw new ArgumentOutOfRangeException(); if (length > Int32.MaxValue || length < Int32.MinValue) throw new ArgumentOutOfRangeException(); Array.Copy(sourceArray, (int)sourceIndex, destinationArray, (int)destinationIndex, (int)length); } public static IEnumerable AsEnumerable(this T item) { yield return item; } public static void CheckNotNull(this object obj, string name) { if (obj == null) { throw new ArgumentNullException(name); } } public static void CheckNotNullOrEmpty(this string obj, string name) { obj.CheckNotNull(name); if (obj.Length == 0) { throw new ArgumentException("String is empty."); } } public static void Skip(this Stream source, long advanceAmount) { if (source.CanSeek) { source.Position += advanceAmount; return; } byte[] buffer = GetTransferByteArray(); try { int read = 0; int readCount = 0; do { readCount = buffer.Length; if (readCount > advanceAmount) { readCount = (int)advanceAmount; } read = source.Read(buffer, 0, readCount); if (read <= 0) { break; } advanceAmount -= read; if (advanceAmount == 0) { break; } } while (true); } finally { #if NETCORE ArrayPool.Shared.Return(buffer); #endif } } public static void Skip(this Stream source) { byte[] buffer = GetTransferByteArray(); try { do { } while (source.Read(buffer, 0, buffer.Length) == buffer.Length); } finally { #if NETCORE ArrayPool.Shared.Return(buffer); #endif } } public static DateTime DosDateToDateTime(UInt16 iDate, UInt16 iTime) { int year = iDate / 512 + 1980; int month = iDate % 512 / 32; int day = iDate % 512 % 32; int hour = iTime / 2048; int minute = iTime % 2048 / 32; int second = iTime % 2048 % 32 * 2; if (iDate == UInt16.MaxValue || month == 0 || day == 0) { year = 1980; month = 1; day = 1; } if (iTime == UInt16.MaxValue) { hour = minute = second = 0; } DateTime dt; try { dt = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Local); } catch { dt = new DateTime(); } return dt; } public static uint DateTimeToDosTime(this DateTime? dateTime) { if (dateTime == null) { return 0; } var localDateTime = dateTime.Value.ToLocalTime(); return (uint)( (localDateTime.Second / 2) | (localDateTime.Minute << 5) | (localDateTime.Hour << 11) | (localDateTime.Day << 16) | (localDateTime.Month << 21) | ((localDateTime.Year - 1980) << 25)); } public static DateTime DosDateToDateTime(UInt32 iTime) { return DosDateToDateTime((UInt16)(iTime / 65536), (UInt16)(iTime % 65536)); } /// /// Convert Unix time value to a DateTime object. /// /// The Unix time stamp you want to convert to DateTime. /// Returns a DateTime object that represents value of the Unix time. public static DateTime UnixTimeToDateTime(long unixtime) { DateTime sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); return sTime.AddSeconds(unixtime); } public static long TransferTo(this Stream source, Stream destination) { byte[] array = GetTransferByteArray(); try { int count; long total = 0; while (ReadTransferBlock(source, array, out count)) { total += count; destination.Write(array, 0, count); } return total; } finally { #if NETCORE ArrayPool.Shared.Return(array); #endif } } public static long TransferTo(this Stream source, Stream destination, Common.Entry entry, IReaderExtractionListener readerExtractionListener) { byte[] array = GetTransferByteArray(); try { int count; var iterations = 0; long total = 0; while (ReadTransferBlock(source, array, out count)) { total += count; destination.Write(array, 0, count); iterations++; readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations); } return total; } finally { #if NETCORE ArrayPool.Shared.Return(array); #endif } } private static bool ReadTransferBlock(Stream source, byte[] array, out int count) { return (count = source.Read(array, 0, array.Length)) != 0; } private static byte[] GetTransferByteArray() { #if NETCORE return ArrayPool.Shared.Rent(81920); #else return new byte[81920]; #endif } public static bool ReadFully(this Stream stream, byte[] buffer) { int total = 0; int read; while ((read = stream.Read(buffer, total, buffer.Length - total)) > 0) { total += read; if (total >= buffer.Length) { return true; } } return (total >= buffer.Length); } public static string TrimNulls(this string source) { return source.Replace('\0', ' ').Trim(); } public static bool BinaryEquals(this byte[] source, byte[] target) { if (source.Length != target.Length) { return false; } for (int i = 0; i < source.Length; ++i) { if (source[i] != target[i]) { return false; } } return true; } } }