Skip to content

Commit 4f6486f

Browse files
author
AndrewMorgan1
committed
Added Support for both IEC (binary: KiB, MiB) and SI (decimal: KB, MB) unit standards and updated tests
1 parent ef37c89 commit 4f6486f

File tree

3 files changed

+165
-127
lines changed

3 files changed

+165
-127
lines changed

src/ByteFlow/ByteFlow/HumanBytesExtensions.cs

Lines changed: 59 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,70 +12,81 @@ public static class HumanBytesExtensions
1212
private static readonly string[] SizeSuffixes = { "B", "KB", "MB", "GB", "TB", "PB" };
1313

1414
/// <summary>
15-
/// Converts a number of bytes into a human-readable string (e.g. "1.18 MB").
15+
/// Converts a number of bytes into a human-readable string
16+
/// using either SI (decimal: KB, MB, GB) or IEC (binary: KiB, MiB, GiB) units.
1617
/// </summary>
1718
/// <param name="bytes">The size in bytes.</param>
1819
/// <param name="decimalPlaces">Number of decimal places to display.</param>
19-
public static string ToHumanBytes(this long bytes, int decimalPlaces = 2)
20+
/// <param name="standard">Whether to use SI (base 1000) or IEC (base 1024) units.</param>
21+
/// <returns>A formatted string such as "1.23 MB" (SI) or "1.18 MiB" (IEC).</returns>
22+
public static string ToHumanBytes(this long bytes, int decimalPlaces = 2, UnitStandard standard = UnitStandard.IEC)
2023
{
2124
if (bytes < 0)
2225
throw new ArgumentOutOfRangeException(nameof(bytes), "Value must be non-negative.");
2326

27+
var suffixes = standard == UnitStandard.SI ? SiSuffixes : IecSuffixes;
28+
2429
if (bytes == 0)
25-
return $"0 {SizeSuffixes[0]}";
30+
return $"0 {suffixes[0].Symbol}";
2631

27-
int mag = (int)Math.Log(bytes, 1024);
28-
if (mag >= SizeSuffixes.Length)
32+
var mag = suffixes.Length - 1;
33+
for (int i = 1; i < suffixes.Length; i++)
2934
{
30-
mag = SizeSuffixes.Length - 1; // clamp to PB
35+
if (bytes < suffixes[i].Factor)
36+
{
37+
mag = i - 1;
38+
break;
39+
}
3140
}
3241

33-
double adjustedSize = bytes / Math.Pow(1024, mag);
34-
42+
double adjusted = bytes / suffixes[mag].Factor;
3543
return string.Format(CultureInfo.InvariantCulture,
36-
$"{{0:F{decimalPlaces}}} {{1}}", adjustedSize, SizeSuffixes[mag]);
44+
$"{{0:F{decimalPlaces}}} {{1}}", adjusted, suffixes[mag].Symbol);
3745
}
3846

3947
/// <summary>
40-
/// Parses a human-readable size string (e.g. "2.5 GB") back into bytes.
48+
/// Parses a human-readable size string (e.g. "2.5 GB" or "2.5 GiB")
49+
/// back into bytes, using either SI (decimal) or IEC (binary) interpretation.
4150
/// </summary>
42-
/// <param name="input">The input string.</param>
43-
public static long ToBytes(this string input)
51+
/// <param name="input">The input string, e.g. "1 KB", "1 KiB", "1 MB".</param>
52+
/// <param name="standard">Whether to interpret units as SI (base 1000) or IEC (base 1024).</param>
53+
/// <returns>The size in bytes.</returns>
54+
public static long ToBytes(this string input, UnitStandard standard = UnitStandard.IEC)
4455
{
4556
if (string.IsNullOrWhiteSpace(input))
4657
throw new ArgumentNullException(nameof(input));
4758

4859
input = input.Trim();
60+
var suffixes = standard == UnitStandard.SI ? SiSuffixes : IecSuffixes;
4961

50-
// Order suffixes by length so "KB" matches before "B"
51-
foreach (var suffix in SizeSuffixes.OrderByDescending(s => s.Length))
62+
foreach (var (symbol, factor) in suffixes.OrderByDescending(s => s.Symbol.Length))
5263
{
53-
if (input.EndsWith(suffix, StringComparison.OrdinalIgnoreCase))
64+
if (input.EndsWith(symbol, StringComparison.OrdinalIgnoreCase))
5465
{
55-
string numberPart = input.Substring(0, input.Length - suffix.Length).TrimEnd();
56-
57-
if (double.TryParse(numberPart, NumberStyles.Float, CultureInfo.InvariantCulture, out double value))
58-
{
59-
int index = Array.IndexOf(SizeSuffixes, suffix);
60-
return (long)(value * Math.Pow(1024, index));
61-
}
62-
63-
throw new FormatException($"Invalid number format: {numberPart}");
66+
var numberPart = input.Substring(0, input.Length - symbol.Length).Trim();
67+
if (!double.TryParse(numberPart, NumberStyles.Float, CultureInfo.InvariantCulture, out var value))
68+
throw new FormatException($"Invalid number format: {numberPart}");
69+
return (long)(value * factor);
6470
}
6571
}
6672

6773
throw new FormatException($"Invalid size suffix in: {input}");
6874
}
6975

7076
/// <summary>
71-
/// Safely parses a human-readable string into bytes.
72-
/// Returns false if parsing fails.
77+
/// Safely parses a human-readable string into bytes, using either SI (decimal) or IEC (binary) units.
7378
/// </summary>
74-
public static bool TryParseHumanBytes(this string input, out long result)
79+
/// <param name="input">The input string (e.g. "1 KB", "1 KiB", "2.5 MB").</param>
80+
/// <param name="result">The parsed byte value if successful, or 0 if parsing fails.</param>
81+
/// <param name="standard">Whether to interpret units as SI (base 1000) or IEC (base 1024).</param>
82+
/// <returns>
83+
/// <c>true</c> if parsing was successful; otherwise <c>false</c>.
84+
/// </returns>
85+
public static bool TryParseHumanBytes(this string input, out long result, UnitStandard standard = UnitStandard.IEC)
7586
{
7687
try
7788
{
78-
result = input.ToBytes();
89+
result = input.ToBytes(standard);
7990
return true;
8091
}
8192
catch
@@ -84,5 +95,25 @@ public static bool TryParseHumanBytes(this string input, out long result)
8495
return false;
8596
}
8697
}
98+
99+
private static readonly (string Symbol, double Factor)[] SiSuffixes =
100+
{
101+
("B", 1d),
102+
("KB", 1e3),
103+
("MB", 1e6),
104+
("GB", 1e9),
105+
("TB", 1e12),
106+
("PB", 1e15)
107+
};
108+
109+
private static readonly (string Symbol, double Factor)[] IecSuffixes =
110+
{
111+
("B", 1d),
112+
("KiB", 1024d),
113+
("MiB", Math.Pow(1024, 2)),
114+
("GiB", Math.Pow(1024, 3)),
115+
("TiB", Math.Pow(1024, 4)),
116+
("PiB", Math.Pow(1024, 5))
117+
};
87118
}
88119
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
namespace ByteFlow
2+
{
3+
/// <summary>
4+
/// Defines whether to use decimal (SI: KB, MB, ...) or binary (IEC: KiB, MiB, ...) units.
5+
/// </summary>
6+
public enum UnitStandard
7+
{
8+
/// <summary>
9+
/// Binary (base 1024): KiB, MiB, GiB...
10+
/// </summary>
11+
IEC, // 1024-based: KiB, MiB, …
12+
/// <summary>
13+
/// Decimal (base 1000): KB, MB, GB...
14+
/// </summary>
15+
SI // 1000-based: KB, MB, …
16+
}
17+
18+
}

0 commit comments

Comments
 (0)