Files
sharpcompress/src/SharpCompress/Utility.cs

357 lines
10 KiB
C#
Raw Normal View History

2015-12-30 11:19:42 +00:00
using System;
2019-09-11 20:06:50 +02:00
using System.Buffers;
using System.Collections.Generic;
using System.Collections.ObjectModel;
2019-09-11 20:06:50 +02:00
using System.IO;
using System.Text;
using SharpCompress.Readers;
2015-12-30 11:19:42 +00:00
2025-10-22 09:17:13 +01:00
namespace SharpCompress;
2022-12-20 15:06:44 +00:00
2025-01-15 04:46:22 +03:00
internal static class Utility
2015-12-30 11:19:42 +00:00
{
2025-10-22 10:16:45 +01:00
//80kb is a good industry standard temporary buffer size
private const int TEMP_BUFFER_SIZE = 81920;
2025-10-22 09:28:07 +01:00
private static readonly HashSet<char> invalidChars = new(Path.GetInvalidFileNameChars());
2025-10-22 09:10:16 +01:00
public static ReadOnlyCollection<T> ToReadOnly<T>(this IList<T> items) => new(items);
2022-12-20 15:06:44 +00:00
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Amount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static int URShift(int number, int bits)
{
if (number >= 0)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
return number >> bits;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
return (number >> bits) + (2 << ~bits);
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
/// <summary>
/// Performs an unsigned bitwise right shift with the specified number
/// </summary>
/// <param name="number">Number to operate on</param>
/// <param name="bits">Amount of bits to shift</param>
/// <returns>The resulting number from the shift operation</returns>
public static long URShift(long number, int bits)
{
if (number >= 0)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
return number >> bits;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
return (number >> bits) + (2L << ~bits);
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static void SetSize(this List<byte> list, int count)
{
if (count > list.Count)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
// Ensure the list only needs to grow once
list.Capacity = count;
for (var i = list.Count; i < count; i++)
{
2022-12-20 15:06:44 +00:00
list.Add(0x0);
}
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
else
{
list.RemoveRange(count, list.Count - count);
}
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static void ForEach<T>(this IEnumerable<T> items, Action<T> action)
{
foreach (var item in items)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
action(item);
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static IEnumerable<T> AsEnumerable<T>(this T item)
{
yield return item;
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static void Skip(this Stream source, long advanceAmount)
{
if (source.CanSeek)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
source.Position += advanceAmount;
return;
2015-12-30 11:19:42 +00:00
}
using var buffer = MemoryPool<byte>.Shared.Rent(TEMP_BUFFER_SIZE);
2025-10-22 09:10:16 +01:00
while (advanceAmount > 0)
2015-12-30 11:19:42 +00:00
{
2025-10-22 09:10:16 +01:00
var toRead = (int)Math.Min(buffer.Memory.Length, advanceAmount);
var read = source.Read(buffer.Memory.Slice(0, toRead).Span);
if (read <= 0)
2018-01-03 00:23:34 +09:00
{
break;
}
advanceAmount -= read;
2025-10-22 09:10:16 +01:00
}
2022-12-20 15:06:44 +00:00
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static void Skip(this Stream source)
{
using var buffer = MemoryPool<byte>.Shared.Rent(TEMP_BUFFER_SIZE);
2025-10-22 09:19:30 +01:00
while (source.Read(buffer.Memory.Span) > 0) { }
2022-12-20 15:06:44 +00:00
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static DateTime DosDateToDateTime(ushort iDate, ushort iTime)
{
var year = (iDate / 512) + 1980;
var month = iDate % 512 / 32;
var day = iDate % 512 % 32;
var hour = iTime / 2048;
var minute = iTime % 2048 / 32;
var second = iTime % 2048 % 32 * 2;
if (iDate == ushort.MaxValue || month == 0 || day == 0)
{
year = 1980;
month = 1;
day = 1;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
if (iTime == ushort.MaxValue)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
hour = minute = second = 0;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
DateTime dt;
try
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
dt = new DateTime(year, month, day, hour, minute, second, DateTimeKind.Local);
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
catch
{
dt = new DateTime();
}
return dt;
}
2015-12-30 11:19:42 +00:00
2022-12-20 15:06:44 +00:00
public static uint DateTimeToDosTime(this DateTime? dateTime)
{
if (dateTime is null)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
return 0;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
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)
);
}
2022-12-20 15:20:49 +00:00
public static DateTime DosDateToDateTime(uint iTime) =>
DosDateToDateTime((ushort)(iTime / 65536), (ushort)(iTime % 65536));
2022-12-20 15:06:44 +00:00
/// <summary>
/// Convert Unix time value to a DateTime object.
/// </summary>
/// <param name="unixtime">The Unix time stamp you want to convert to DateTime.</param>
/// <returns>Returns a DateTime object that represents value of the Unix time.</returns>
public static DateTime UnixTimeToDateTime(long unixtime)
{
var sTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return sTime.AddSeconds(unixtime);
}
public static long TransferTo(this Stream source, Stream destination, long maxLength)
2024-04-09 15:35:15 +01:00
{
var array = ArrayPool<byte>.Shared.Rent(TEMP_BUFFER_SIZE);
2024-04-09 15:35:15 +01:00
try
{
var maxReadSize = array.Length;
2024-04-09 15:35:15 +01:00
long total = 0;
var remaining = maxLength;
2024-04-11 09:05:45 +01:00
if (remaining < maxReadSize)
{
maxReadSize = (int)remaining;
}
while (ReadTransferBlock(source, array, maxReadSize, out var count))
2024-04-09 15:35:15 +01:00
{
destination.Write(array, 0, count);
total += count;
if (remaining - count < 0)
2024-04-09 08:19:23 +01:00
{
2024-04-09 15:35:15 +01:00
break;
2024-04-09 08:19:23 +01:00
}
2024-04-10 08:53:20 +01:00
remaining -= count;
2024-04-11 09:05:45 +01:00
if (remaining < maxReadSize)
{
maxReadSize = (int)remaining;
}
}
2022-12-20 15:06:44 +00:00
return total;
}
finally
{
ArrayPool<byte>.Shared.Return(array);
}
2022-12-20 15:06:44 +00:00
}
2022-12-20 15:06:44 +00:00
public static long TransferTo(
this Stream source,
Stream destination,
Common.Entry entry,
IReaderExtractionListener readerExtractionListener
)
{
var array = ArrayPool<byte>.Shared.Rent(TEMP_BUFFER_SIZE);
2022-12-20 15:06:44 +00:00
try
{
2022-12-20 15:06:44 +00:00
var iterations = 0;
long total = 0;
2025-10-22 10:16:45 +01:00
int count;
while ((count = source.Read(array, 0, array.Length)) != 0)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
total += count;
destination.Write(array, 0, count);
iterations++;
readerExtractionListener.FireEntryExtractionProgress(entry, total, iterations);
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
return total;
2015-12-30 11:19:42 +00:00
}
2022-12-20 15:06:44 +00:00
finally
{
2022-12-20 15:06:44 +00:00
ArrayPool<byte>.Shared.Return(array);
}
2022-12-20 15:06:44 +00:00
}
2025-10-22 10:16:45 +01:00
private static bool ReadTransferBlock(Stream source, byte[] array, int maxSize, out int count)
2024-04-09 15:35:15 +01:00
{
2025-10-22 10:16:45 +01:00
var size = maxSize;
if (maxSize > array.Length)
2024-04-09 15:35:15 +01:00
{
size = array.Length;
}
count = source.Read(array, 0, size);
return count != 0;
}
2025-10-22 09:10:16 +01:00
#if NET60_OR_GREATER
public static bool ReadFully(this Stream stream, byte[] buffer)
{
try
{
stream.ReadExactly(buffer);
return true;
}
catch (EndOfStreamException)
{
return false;
}
}
public static bool ReadFully(this Stream stream, Span<byte> buffer)
{
try
{
stream.ReadExactly(buffer);
return true;
}
catch (EndOfStreamException)
{
return false;
}
}
#else
2022-12-20 15:06:44 +00:00
public static bool ReadFully(this Stream stream, byte[] buffer)
{
var total = 0;
int read;
while ((read = stream.Read(buffer, total, buffer.Length - total)) > 0)
{
2022-12-20 15:06:44 +00:00
total += read;
if (total >= buffer.Length)
{
2022-12-20 15:06:44 +00:00
return true;
}
}
2022-12-20 15:06:44 +00:00
return (total >= buffer.Length);
}
2021-10-02 15:29:03 +01:00
2022-12-20 15:06:44 +00:00
public static bool ReadFully(this Stream stream, Span<byte> buffer)
{
var total = 0;
int read;
while ((read = stream.Read(buffer.Slice(total, buffer.Length - total))) > 0)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
total += read;
if (total >= buffer.Length)
2015-12-30 11:19:42 +00:00
{
2022-12-20 15:06:44 +00:00
return true;
2015-12-30 11:19:42 +00:00
}
}
2022-12-20 15:06:44 +00:00
return (total >= buffer.Length);
}
2025-10-22 09:10:16 +01:00
#endif
2015-12-30 11:19:42 +00:00
2022-12-20 15:20:49 +00:00
public static string TrimNulls(this string source) => source.Replace('\0', ' ').Trim();
2022-12-20 15:06:44 +00:00
/// <summary>
/// Swap the endianness of a UINT32
/// </summary>
/// <param name="number">The UINT32 you want to swap his endianness</param>
/// <returns>Return the new UINT32 in the other endianness format</returns>
2022-12-20 15:20:49 +00:00
public static uint SwapUINT32(uint number) =>
(number >> 24)
| ((number << 8) & 0x00FF0000)
| ((number >> 8) & 0x0000FF00)
| (number << 24);
2022-12-20 15:06:44 +00:00
/// <summary>
/// Insert a little endian UINT32 into a byte array
/// </summary>
/// <param name="buffer">The buffer to insert into</param>
/// <param name="number">The UINT32 to insert</param>
/// <param name="offset">Offset of the buffer to insert into</param>
public static void SetLittleUInt32(ref byte[] buffer, uint number, long offset)
{
buffer[offset] = (byte)(number);
buffer[offset + 1] = (byte)(number >> 8);
buffer[offset + 2] = (byte)(number >> 16);
buffer[offset + 3] = (byte)(number >> 24);
}
2022-12-20 15:06:44 +00:00
/// <summary>
/// Insert a big endian UINT32 into a byte array
/// </summary>
/// <param name="buffer">The buffer to insert into</param>
/// <param name="number">The UINT32 to insert</param>
/// <param name="offset">Offset of the buffer to insert into</param>
public static void SetBigUInt32(ref byte[] buffer, uint number, long offset)
{
buffer[offset] = (byte)(number >> 24);
buffer[offset + 1] = (byte)(number >> 16);
buffer[offset + 2] = (byte)(number >> 8);
buffer[offset + 3] = (byte)number;
2015-12-30 11:19:42 +00:00
}
public static string ReplaceInvalidFileNameChars(string fileName)
{
var sb = new StringBuilder(fileName.Length);
foreach (var c in fileName)
{
var newChar = invalidChars.Contains(c) ? '_' : c;
sb.Append(newChar);
}
return sb.ToString();
}
2019-09-11 20:06:50 +02:00
}