mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-11-04 18:40:50 +08:00 
			
		
		
		
	Adding TransferSpeedColumn configuration to display bits/bytes + binary/decimal prefixes (#904)
* Adding configuration to TransferSpeedColumn to be able to display in both bytes/bits, as well as using binary/decimal prefix definitions. --------- Co-authored-by: Frank Ray <52075808+FrankRay78@users.noreply.github.com>
This commit is contained in:
		@@ -3,33 +3,71 @@ namespace Spectre.Console;
 | 
			
		||||
internal struct FileSize
 | 
			
		||||
{
 | 
			
		||||
    public double Bytes { get; }
 | 
			
		||||
    public FileSizeUnit Unit { get; }
 | 
			
		||||
    public double Bits => Bytes * 8;
 | 
			
		||||
 | 
			
		||||
    public FileSizePrefix Prefix { get; } = FileSizePrefix.None;
 | 
			
		||||
 | 
			
		||||
    private readonly FileSizeBase _prefixBase = FileSizeBase.Binary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// If enabled, will display the output in bits, rather than bytes.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private readonly bool _showBits = false;
 | 
			
		||||
 | 
			
		||||
    public string Suffix => GetSuffix();
 | 
			
		||||
 | 
			
		||||
    public FileSize(double bytes)
 | 
			
		||||
    {
 | 
			
		||||
        Bytes = bytes;
 | 
			
		||||
        Unit = Detect(bytes);
 | 
			
		||||
        Prefix = DetectPrefix(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FileSize(double bytes, FileSizeUnit unit)
 | 
			
		||||
    public FileSize(double bytes, FileSizeBase @base)
 | 
			
		||||
    {
 | 
			
		||||
        Bytes = bytes;
 | 
			
		||||
        Unit = unit;
 | 
			
		||||
        _prefixBase = @base;
 | 
			
		||||
        Prefix = DetectPrefix(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FileSize(double bytes, FileSizeBase @base, bool showBits)
 | 
			
		||||
    {
 | 
			
		||||
        Bytes = bytes;
 | 
			
		||||
        _showBits = showBits;
 | 
			
		||||
 | 
			
		||||
        _prefixBase = @base;
 | 
			
		||||
        Prefix = DetectPrefix(bytes);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FileSize(double bytes, FileSizePrefix prefix)
 | 
			
		||||
    {
 | 
			
		||||
        Bytes = bytes;
 | 
			
		||||
        Prefix = prefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public FileSize(double bytes, FileSizePrefix prefix, FileSizeBase @base, bool showBits)
 | 
			
		||||
    {
 | 
			
		||||
        Bytes = bytes;
 | 
			
		||||
        _showBits = showBits;
 | 
			
		||||
 | 
			
		||||
        _prefixBase = @base;
 | 
			
		||||
        Prefix = prefix;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public string Format(CultureInfo? culture = null)
 | 
			
		||||
    {
 | 
			
		||||
        var @base = GetBase(Unit);
 | 
			
		||||
        if (@base == 0)
 | 
			
		||||
        var unitBase = Math.Pow((int)_prefixBase, (int)Prefix);
 | 
			
		||||
 | 
			
		||||
        if (_showBits)
 | 
			
		||||
        {
 | 
			
		||||
            @base = 1;
 | 
			
		||||
            var bits = Bits / unitBase;
 | 
			
		||||
            return Prefix == FileSizePrefix.None ?
 | 
			
		||||
                ((int)bits).ToString(culture ?? CultureInfo.InvariantCulture)
 | 
			
		||||
                : bits.ToString("F1", culture ?? CultureInfo.InvariantCulture);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var bytes = Bytes / @base;
 | 
			
		||||
 | 
			
		||||
        return Unit == FileSizeUnit.Byte
 | 
			
		||||
            ? ((int)bytes).ToString(culture ?? CultureInfo.InvariantCulture)
 | 
			
		||||
        var bytes = Bytes / unitBase;
 | 
			
		||||
        return Prefix == FileSizePrefix.None ?
 | 
			
		||||
            ((int)bytes).ToString(culture ?? CultureInfo.InvariantCulture)
 | 
			
		||||
            : bytes.ToString("F1", culture ?? CultureInfo.InvariantCulture);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -50,36 +88,67 @@ internal struct FileSize
 | 
			
		||||
 | 
			
		||||
    private string GetSuffix()
 | 
			
		||||
    {
 | 
			
		||||
        return (Bytes, Unit) switch
 | 
			
		||||
        return (Bytes, Unit: Prefix, PrefixBase: _prefixBase, ShowBits: _showBits) switch
 | 
			
		||||
        {
 | 
			
		||||
            (_, FileSizeUnit.KiloByte) => "KB",
 | 
			
		||||
            (_, FileSizeUnit.MegaByte) => "MB",
 | 
			
		||||
            (_, FileSizeUnit.GigaByte) => "GB",
 | 
			
		||||
            (_, FileSizeUnit.TeraByte) => "TB",
 | 
			
		||||
            (_, FileSizeUnit.PetaByte) => "PB",
 | 
			
		||||
            (_, FileSizeUnit.ExaByte) => "EB",
 | 
			
		||||
            (_, FileSizeUnit.ZettaByte) => "ZB",
 | 
			
		||||
            (_, FileSizeUnit.YottaByte) => "YB",
 | 
			
		||||
            (1, _) => "byte",
 | 
			
		||||
            (_, _) => "bytes",
 | 
			
		||||
            (_, FileSizePrefix.Kilo, FileSizeBase.Binary, false) => "KiB",
 | 
			
		||||
            (_, FileSizePrefix.Mega, FileSizeBase.Binary, false) => "MiB",
 | 
			
		||||
            (_, FileSizePrefix.Giga, FileSizeBase.Binary, false) => "GiB",
 | 
			
		||||
            (_, FileSizePrefix.Tera, FileSizeBase.Binary, false) => "TiB",
 | 
			
		||||
            (_, FileSizePrefix.Peta, FileSizeBase.Binary, false) => "PiB",
 | 
			
		||||
            (_, FileSizePrefix.Exa, FileSizeBase.Binary, false) => "EiB",
 | 
			
		||||
            (_, FileSizePrefix.Zetta, FileSizeBase.Binary, false) => "ZiB",
 | 
			
		||||
            (_, FileSizePrefix.Yotta, FileSizeBase.Binary, false) => "YiB",
 | 
			
		||||
 | 
			
		||||
            (_, FileSizePrefix.Kilo, FileSizeBase.Binary, true) => "Kibit",
 | 
			
		||||
            (_, FileSizePrefix.Mega, FileSizeBase.Binary, true) => "Mibit",
 | 
			
		||||
            (_, FileSizePrefix.Giga, FileSizeBase.Binary, true) => "Gibit",
 | 
			
		||||
            (_, FileSizePrefix.Tera, FileSizeBase.Binary, true) => "Tibit",
 | 
			
		||||
            (_, FileSizePrefix.Peta, FileSizeBase.Binary, true) => "Pibit",
 | 
			
		||||
            (_, FileSizePrefix.Exa, FileSizeBase.Binary, true) => "Eibit",
 | 
			
		||||
            (_, FileSizePrefix.Zetta, FileSizeBase.Binary, true) => "Zibit",
 | 
			
		||||
            (_, FileSizePrefix.Yotta, FileSizeBase.Binary, true) => "Yibit",
 | 
			
		||||
 | 
			
		||||
            (_, FileSizePrefix.Kilo, FileSizeBase.Decimal, false) => "KB",
 | 
			
		||||
            (_, FileSizePrefix.Mega, FileSizeBase.Decimal, false) => "MB",
 | 
			
		||||
            (_, FileSizePrefix.Giga, FileSizeBase.Decimal, false) => "GB",
 | 
			
		||||
            (_, FileSizePrefix.Tera, FileSizeBase.Decimal, false) => "TB",
 | 
			
		||||
            (_, FileSizePrefix.Peta, FileSizeBase.Decimal, false) => "PB",
 | 
			
		||||
            (_, FileSizePrefix.Exa, FileSizeBase.Decimal, false) => "EB",
 | 
			
		||||
            (_, FileSizePrefix.Zetta, FileSizeBase.Decimal, false) => "ZB",
 | 
			
		||||
            (_, FileSizePrefix.Yotta, FileSizeBase.Decimal, false) => "YB",
 | 
			
		||||
 | 
			
		||||
            (_, FileSizePrefix.Kilo, FileSizeBase.Decimal, true) => "Kbit",
 | 
			
		||||
            (_, FileSizePrefix.Mega, FileSizeBase.Decimal, true) => "Mbit",
 | 
			
		||||
            (_, FileSizePrefix.Giga, FileSizeBase.Decimal, true) => "Gbit",
 | 
			
		||||
            (_, FileSizePrefix.Tera, FileSizeBase.Decimal, true) => "Tbit",
 | 
			
		||||
            (_, FileSizePrefix.Peta, FileSizeBase.Decimal, true) => "Pbit",
 | 
			
		||||
            (_, FileSizePrefix.Exa, FileSizeBase.Decimal, true) => "Ebit",
 | 
			
		||||
            (_, FileSizePrefix.Zetta, FileSizeBase.Decimal, true) => "Zbit",
 | 
			
		||||
            (_, FileSizePrefix.Yotta, FileSizeBase.Decimal, true) => "Ybit",
 | 
			
		||||
 | 
			
		||||
            (1, _, _, true) => "bit",
 | 
			
		||||
            (_, _, _, true) => "bits",
 | 
			
		||||
            (1, _, _, false) => "byte",
 | 
			
		||||
            (_, _, _, false) => "bytes",
 | 
			
		||||
        };
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static FileSizeUnit Detect(double bytes)
 | 
			
		||||
    private FileSizePrefix DetectPrefix(double bytes)
 | 
			
		||||
    {
 | 
			
		||||
        foreach (var unit in (FileSizeUnit[])Enum.GetValues(typeof(FileSizeUnit)))
 | 
			
		||||
        if (_showBits)
 | 
			
		||||
        {
 | 
			
		||||
            if (bytes < (GetBase(unit) * 1024))
 | 
			
		||||
            bytes *= 8;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        foreach (var prefix in (FileSizePrefix[])Enum.GetValues(typeof(FileSizePrefix)))
 | 
			
		||||
        {
 | 
			
		||||
            // Trying to find the largest unit, that the number of bytes can fit under. Ex. 40kb < 1mb
 | 
			
		||||
            if (bytes < Math.Pow((int)_prefixBase, (int)prefix + 1))
 | 
			
		||||
            {
 | 
			
		||||
                return unit;
 | 
			
		||||
                return prefix;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return FileSizeUnit.Byte;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static double GetBase(FileSizeUnit unit)
 | 
			
		||||
    {
 | 
			
		||||
        return Math.Pow(1024, (int)unit);
 | 
			
		||||
        return FileSizePrefix.None;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										17
									
								
								src/Spectre.Console/Internal/FileSizeBase.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/Spectre.Console/Internal/FileSizeBase.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
/// <summary>
 | 
			
		||||
/// Determines possible file size base prefixes.  (base 2/base 10).
 | 
			
		||||
/// </summary>
 | 
			
		||||
public enum FileSizeBase
 | 
			
		||||
{
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The SI prefix definition (base 10) of kilobyte, megabyte, etc.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Decimal = 1000,
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// The IEC binary prefix definition (base 2) of kibibyte, mebibyte, etc.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    Binary = 1024,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										14
									
								
								src/Spectre.Console/Internal/FileSizePrefix.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Spectre.Console/Internal/FileSizePrefix.cs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,14 @@
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal enum FileSizePrefix
 | 
			
		||||
{
 | 
			
		||||
    None = 0,
 | 
			
		||||
    Kilo = 1,
 | 
			
		||||
    Mega = 2,
 | 
			
		||||
    Giga = 3,
 | 
			
		||||
    Tera = 4,
 | 
			
		||||
    Peta = 5,
 | 
			
		||||
    Exa = 6,
 | 
			
		||||
    Zetta = 7,
 | 
			
		||||
    Yotta = 8,
 | 
			
		||||
}
 | 
			
		||||
@@ -1,14 +0,0 @@
 | 
			
		||||
namespace Spectre.Console;
 | 
			
		||||
 | 
			
		||||
internal enum FileSizeUnit
 | 
			
		||||
{
 | 
			
		||||
    Byte = 0,
 | 
			
		||||
    KiloByte = 1,
 | 
			
		||||
    MegaByte = 2,
 | 
			
		||||
    GigaByte = 3,
 | 
			
		||||
    TeraByte = 4,
 | 
			
		||||
    PetaByte = 5,
 | 
			
		||||
    ExaByte = 6,
 | 
			
		||||
    ZettaByte = 7,
 | 
			
		||||
    YottaByte = 8,
 | 
			
		||||
}
 | 
			
		||||
@@ -10,10 +10,20 @@ public sealed class DownloadedColumn : ProgressColumn
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public CultureInfo? Culture { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets the <see cref="FileSizeBase"/> to use.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public FileSizeBase Base { get; set; } = FileSizeBase.Binary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets a value indicating whether to display the transfer speed in bits.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool ShowBits { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
 | 
			
		||||
    {
 | 
			
		||||
        var total = new FileSize(task.MaxValue);
 | 
			
		||||
        var total = new FileSize(task.MaxValue, Base, ShowBits);
 | 
			
		||||
 | 
			
		||||
        if (task.IsFinished)
 | 
			
		||||
        {
 | 
			
		||||
@@ -24,7 +34,7 @@ public sealed class DownloadedColumn : ProgressColumn
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var downloaded = new FileSize(task.Value, total.Unit);
 | 
			
		||||
            var downloaded = new FileSize(task.Value, total.Prefix, Base, ShowBits);
 | 
			
		||||
 | 
			
		||||
            return new Markup(string.Format(
 | 
			
		||||
                "{0}[grey]/[/]{1} [grey]{2}[/]",
 | 
			
		||||
 
 | 
			
		||||
@@ -10,6 +10,16 @@ public sealed class TransferSpeedColumn : ProgressColumn
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public CultureInfo? Culture { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets the <see cref="FileSizeBase"/> to use.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public FileSizeBase Base { get; set; } = FileSizeBase.Binary;
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Gets or sets a value indicating whether to display the transfer speed in bits.
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    public bool ShowBits { get; set; }
 | 
			
		||||
 | 
			
		||||
    /// <inheritdoc/>
 | 
			
		||||
    public override IRenderable Render(RenderOptions options, ProgressTask task, TimeSpan deltaTime)
 | 
			
		||||
    {
 | 
			
		||||
@@ -18,7 +28,14 @@ public sealed class TransferSpeedColumn : ProgressColumn
 | 
			
		||||
            return new Text("?/s");
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        var size = new FileSize(task.Speed.Value);
 | 
			
		||||
        return new Markup(string.Format("{0}/s", size.ToString(suffix: true, Culture)));
 | 
			
		||||
        if (task.IsFinished)
 | 
			
		||||
        {
 | 
			
		||||
            return new Markup(string.Empty, Style.Plain);
 | 
			
		||||
        }
 | 
			
		||||
        else
 | 
			
		||||
        {
 | 
			
		||||
            var size = new FileSize(task.Speed.Value, Base, ShowBits);
 | 
			
		||||
            return new Markup(string.Format("{0}/s", size.ToString(suffix: true, Culture)));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user