using System; namespace PSMinIO.Utils { /// /// Utility class for formatting byte sizes into human-readable strings /// public static class SizeFormatter { /// /// Size units in order from smallest to largest /// private static readonly string[] SizeUnits = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; /// /// Formats bytes into a human-readable string with appropriate units /// /// Number of bytes /// Number of decimal places to show (default: 2) /// Formatted string with appropriate unit public static string FormatBytes(long bytes, int decimalPlaces = 2) { if (bytes == 0) return "0 B"; if (bytes < 0) return $"-{FormatBytes(-bytes, decimalPlaces)}"; int unitIndex = 0; double size = bytes; // Find the appropriate unit while (size >= 1024 && unitIndex < SizeUnits.Length - 1) { size /= 1024; unitIndex++; } // Format with specified decimal places var formatString = $"{{0:F{decimalPlaces}}} {{1}}"; return string.Format(formatString, size, SizeUnits[unitIndex]); } /// /// Formats bytes into a human-readable string with appropriate units (double overload) /// /// Number of bytes /// Number of decimal places to show (default: 2) /// Formatted string with appropriate unit public static string FormatBytes(double bytes, int decimalPlaces = 2) { return FormatBytes((long)Math.Round(bytes), decimalPlaces); } /// /// Formats bytes per second into a human-readable string /// /// Bytes per second /// Number of decimal places to show (default: 2) /// Formatted string with appropriate unit and "/s" suffix public static string FormatBytesPerSecond(double bytesPerSecond, int decimalPlaces = 2) { return $"{FormatBytes(bytesPerSecond, decimalPlaces)}/s"; } /// /// Formats a transfer rate with context /// /// Number of bytes transferred /// Time elapsed for the transfer /// Number of decimal places to show (default: 2) /// Formatted transfer rate string public static string FormatTransferRate(long bytesTransferred, TimeSpan elapsedTime, int decimalPlaces = 2) { if (elapsedTime.TotalSeconds <= 0) return "0 B/s"; var bytesPerSecond = bytesTransferred / elapsedTime.TotalSeconds; return FormatBytesPerSecond(bytesPerSecond, decimalPlaces); } /// /// Formats a progress string showing current/total with percentages /// /// Current bytes processed /// Total bytes to process /// Number of decimal places to show (default: 2) /// Formatted progress string public static string FormatProgress(long current, long total, int decimalPlaces = 2) { var currentFormatted = FormatBytes(current, decimalPlaces); var totalFormatted = FormatBytes(total, decimalPlaces); if (total > 0) { var percentage = (double)current / total * 100; return $"{currentFormatted} / {totalFormatted} ({percentage:F1}%)"; } return $"{currentFormatted} / {totalFormatted}"; } /// /// Gets the appropriate unit for a given byte size without formatting /// /// Number of bytes /// Appropriate unit string public static string GetAppropriateUnit(long bytes) { if (bytes == 0) return "B"; int unitIndex = 0; double size = Math.Abs(bytes); while (size >= 1024 && unitIndex < SizeUnits.Length - 1) { size /= 1024; unitIndex++; } return SizeUnits[unitIndex]; } /// /// Converts bytes to the specified unit /// /// Number of bytes /// Target unit (B, KB, MB, GB, TB, PB, EB) /// Value in the specified unit public static double ConvertToUnit(long bytes, string unit) { var unitIndex = Array.IndexOf(SizeUnits, unit.ToUpperInvariant()); if (unitIndex == -1) throw new ArgumentException($"Invalid unit: {unit}. Valid units are: {string.Join(", ", SizeUnits)}"); if (unitIndex == 0) // Bytes return bytes; return bytes / Math.Pow(1024, unitIndex); } /// /// Parses a size string back to bytes (e.g., "1.5 GB" -> bytes) /// /// Size string to parse /// Number of bytes public static long ParseSizeString(string sizeString) { if (string.IsNullOrWhiteSpace(sizeString)) throw new ArgumentException("Size string cannot be null or empty"); var parts = sizeString.Trim().Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (parts.Length != 2) throw new ArgumentException($"Invalid size string format: {sizeString}. Expected format: '1.5 GB'"); if (!double.TryParse(parts[0], out var value)) throw new ArgumentException($"Invalid numeric value: {parts[0]}"); var unit = parts[1].ToUpperInvariant(); var unitIndex = Array.IndexOf(SizeUnits, unit); if (unitIndex == -1) throw new ArgumentException($"Invalid unit: {unit}. Valid units are: {string.Join(", ", SizeUnits)}"); return (long)(value * Math.Pow(1024, unitIndex)); } /// /// Formats a size comparison between two values /// /// First value in bytes /// Second value in bytes /// Label for first value /// Label for second value /// Number of decimal places to show (default: 2) /// Formatted comparison string public static string FormatComparison(long value1, long value2, string label1, string label2, int decimalPlaces = 2) { var formatted1 = FormatBytes(value1, decimalPlaces); var formatted2 = FormatBytes(value2, decimalPlaces); var difference = value1 - value2; var diffFormatted = FormatBytes(Math.Abs(difference), decimalPlaces); var diffDirection = difference >= 0 ? "larger" : "smaller"; return $"{label1}: {formatted1}, {label2}: {formatted2} ({diffFormatted} {diffDirection})"; } } }