123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 |
- using System;
- using System.Globalization;
- namespace Optimizer {
- /// <summary>
- /// Represents a byte size value with support for decimal (KiloByte) and
- /// binary values (KibiByte).
- /// </summary>
- public partial struct ByteSize : IComparable<ByteSize>, IEquatable<ByteSize> {
- public static readonly ByteSize MinValue = ByteSize.FromBits(long.MinValue);
- public static readonly ByteSize MaxValue = ByteSize.FromBits(long.MaxValue);
- public const long BitsInByte = 8;
- public const string BitSymbol = "b";
- public const string ByteSymbol = "B";
- public long Bits { get; private set; }
- public double Bytes { get; private set; }
- public string LargestWholeNumberBinarySymbol {
- get {
- // Absolute value is used to deal with negative values
- if (Math.Abs(this.PebiBytes) >= 1)
- return PebiByteSymbol;
- if (Math.Abs(this.TebiBytes) >= 1)
- return TebiByteSymbol;
- if (Math.Abs(this.GibiBytes) >= 1)
- return GibiByteSymbol;
- if (Math.Abs(this.MebiBytes) >= 1)
- return MebiByteSymbol;
- if (Math.Abs(this.KibiBytes) >= 1)
- return KibiByteSymbol;
- if (Math.Abs(this.Bytes) >= 1)
- return ByteSymbol;
- return BitSymbol;
- }
- }
- public string LargestWholeNumberDecimalSymbol {
- get {
- // Absolute value is used to deal with negative values
- if (Math.Abs(this.PetaBytes) >= 1)
- return PetaByteSymbol;
- if (Math.Abs(this.TeraBytes) >= 1)
- return TeraByteSymbol;
- if (Math.Abs(this.GigaBytes) >= 1)
- return GigaByteSymbol;
- if (Math.Abs(this.MegaBytes) >= 1)
- return MegaByteSymbol;
- if (Math.Abs(this.KiloBytes) >= 1)
- return KiloByteSymbol;
- if (Math.Abs(this.Bytes) >= 1)
- return ByteSymbol;
- return BitSymbol;
- }
- }
- public double LargestWholeNumberBinaryValue {
- get {
- // Absolute value is used to deal with negative values
- if (Math.Abs(this.PebiBytes) >= 1)
- return this.PebiBytes;
- if (Math.Abs(this.TebiBytes) >= 1)
- return this.TebiBytes;
- if (Math.Abs(this.GibiBytes) >= 1)
- return this.GibiBytes;
- if (Math.Abs(this.MebiBytes) >= 1)
- return this.MebiBytes;
- if (Math.Abs(this.KibiBytes) >= 1)
- return this.KibiBytes;
- if (Math.Abs(this.Bytes) >= 1)
- return this.Bytes;
- return this.Bits;
- }
- }
- public double LargestWholeNumberDecimalValue {
- get {
- // Absolute value is used to deal with negative values
- if (Math.Abs(this.PetaBytes) >= 1)
- return this.PetaBytes;
- if (Math.Abs(this.TeraBytes) >= 1)
- return this.TeraBytes;
- if (Math.Abs(this.GigaBytes) >= 1)
- return this.GigaBytes;
- if (Math.Abs(this.MegaBytes) >= 1)
- return this.MegaBytes;
- if (Math.Abs(this.KiloBytes) >= 1)
- return this.KiloBytes;
- if (Math.Abs(this.Bytes) >= 1)
- return this.Bytes;
- return this.Bits;
- }
- }
- public ByteSize(long bits)
- : this() {
- Bits = bits;
- Bytes = bits / BitsInByte;
- }
- public ByteSize(double bytes)
- : this() {
- // Get ceiling because bits are whole units
- Bits = (long)Math.Ceiling(bytes * BitsInByte);
- Bytes = bytes;
- }
- public static ByteSize FromBits(long value) {
- return new ByteSize(value);
- }
- public static ByteSize FromBytes(double value) {
- return new ByteSize(value);
- }
- /// <summary>
- /// Converts the value of the current object to a string.
- /// The prefix symbol (bit, byte, kilo, mebi, gibi, tebi) used is the
- /// largest prefix such that the corresponding value is greater than or
- /// equal to one.
- /// </summary>
- public override string ToString() {
- return this.ToString("0.##", CultureInfo.CurrentCulture);
- }
- public string ToString(string format) {
- return this.ToString(format, CultureInfo.CurrentCulture);
- }
- public string ToString(string format, IFormatProvider provider, bool useBinaryByte = false) {
- if (!format.Contains("#") && !format.Contains("0"))
- format = "0.## " + format;
- if (provider == null) provider = CultureInfo.CurrentCulture;
- Func<string, bool> has = s => format.IndexOf(s, StringComparison.CurrentCultureIgnoreCase) != -1;
- Func<double, string> output = n => n.ToString(format, provider);
- // Binary
- if (has("PiB"))
- return output(this.PebiBytes);
- if (has("TiB"))
- return output(this.TebiBytes);
- if (has("GiB"))
- return output(this.GibiBytes);
- if (has("MiB"))
- return output(this.MebiBytes);
- if (has("KiB"))
- return output(this.KibiBytes);
- // Decimal
- if (has("PB"))
- return output(this.PetaBytes);
- if (has("TB"))
- return output(this.TeraBytes);
- if (has("GB"))
- return output(this.GigaBytes);
- if (has("MB"))
- return output(this.MegaBytes);
- if (has("KB"))
- return output(this.KiloBytes);
- // Byte and Bit symbol must be case-sensitive
- if (format.IndexOf(ByteSize.ByteSymbol) != -1)
- return output(this.Bytes);
- if (format.IndexOf(ByteSize.BitSymbol) != -1)
- return output(this.Bits);
- if (useBinaryByte) {
- return string.Format("{0} {1}", this.LargestWholeNumberBinaryValue.ToString(format, provider), this.LargestWholeNumberBinarySymbol);
- }
- else {
- return string.Format("{0} {1}", this.LargestWholeNumberDecimalValue.ToString(format, provider), this.LargestWholeNumberDecimalSymbol);
- }
- }
- public override bool Equals(object value) {
- if (value == null)
- return false;
- ByteSize other;
- if (value is ByteSize)
- other = (ByteSize)value;
- else
- return false;
- return Equals(other);
- }
- public bool Equals(ByteSize value) {
- return this.Bits == value.Bits;
- }
- public override int GetHashCode() {
- return this.Bits.GetHashCode();
- }
- public int CompareTo(ByteSize other) {
- return this.Bits.CompareTo(other.Bits);
- }
- public ByteSize Add(ByteSize bs) {
- return new ByteSize(this.Bytes + bs.Bytes);
- }
- public ByteSize AddBits(long value) {
- return this + FromBits(value);
- }
- public ByteSize AddBytes(double value) {
- return this + ByteSize.FromBytes(value);
- }
- public ByteSize Subtract(ByteSize bs) {
- return new ByteSize(this.Bytes - bs.Bytes);
- }
- public static ByteSize operator +(ByteSize b1, ByteSize b2) {
- return new ByteSize(b1.Bytes + b2.Bytes);
- }
- public static ByteSize operator ++(ByteSize b) {
- return new ByteSize(b.Bytes + 1);
- }
- public static ByteSize operator -(ByteSize b) {
- return new ByteSize(-b.Bytes);
- }
- public static ByteSize operator -(ByteSize b1, ByteSize b2) {
- return new ByteSize(b1.Bytes - b2.Bytes);
- }
- public static ByteSize operator --(ByteSize b) {
- return new ByteSize(b.Bytes - 1);
- }
- public static bool operator ==(ByteSize b1, ByteSize b2) {
- return b1.Bits == b2.Bits;
- }
- public static bool operator !=(ByteSize b1, ByteSize b2) {
- return b1.Bits != b2.Bits;
- }
- public static bool operator <(ByteSize b1, ByteSize b2) {
- return b1.Bits < b2.Bits;
- }
- public static bool operator <=(ByteSize b1, ByteSize b2) {
- return b1.Bits <= b2.Bits;
- }
- public static bool operator >(ByteSize b1, ByteSize b2) {
- return b1.Bits > b2.Bits;
- }
- public static bool operator >=(ByteSize b1, ByteSize b2) {
- return b1.Bits >= b2.Bits;
- }
- public static ByteSize Parse(string s) {
- return Parse(s, NumberFormatInfo.CurrentInfo);
- }
- public static ByteSize Parse(string s, IFormatProvider formatProvider) {
- return Parse(s, NumberStyles.Float | NumberStyles.AllowThousands, formatProvider);
- }
- public static ByteSize Parse(string s, NumberStyles numberStyles, IFormatProvider formatProvider) {
- // Arg checking
- if (string.IsNullOrWhiteSpace(s))
- throw new ArgumentNullException("s", "String is null or whitespace");
- // Get the index of the first non-digit character
- s = s.TrimStart(); // Protect against leading spaces
- var num = 0;
- var found = false;
- var numberFormatInfo = NumberFormatInfo.GetInstance(formatProvider);
- var decimalSeparator = Convert.ToChar(numberFormatInfo.NumberDecimalSeparator);
- var groupSeparator = Convert.ToChar(numberFormatInfo.NumberGroupSeparator);
- // Pick first non-digit number
- for (num = 0; num < s.Length; num++)
- if (!(char.IsDigit(s[num]) || s[num] == decimalSeparator || s[num] == groupSeparator)) {
- found = true;
- break;
- }
- if (found == false)
- throw new FormatException($"No byte indicator found in value '{s}'.");
- int lastNumber = num;
- // Cut the input string in half
- string numberPart = s.Substring(0, lastNumber).Trim();
- string sizePart = s.Substring(lastNumber, s.Length - lastNumber).Trim();
- // Get the numeric part
- double number;
- if (!double.TryParse(numberPart, numberStyles, formatProvider, out number))
- throw new FormatException($"No number found in value '{s}'.");
- // Get the magnitude part
- switch (sizePart) {
- case "b":
- if (number % 1 != 0) // Can't have partial bits
- throw new FormatException($"Can't have partial bits for value '{s}'.");
- return FromBits((long)number);
- case "B":
- return FromBytes(number);
- }
- switch (sizePart.ToLowerInvariant()) {
- // Binary
- case "kib":
- return FromKibiBytes(number);
- case "mib":
- return FromMebiBytes(number);
- case "gib":
- return FromGibiBytes(number);
- case "tib":
- return FromTebiBytes(number);
- case "pib":
- return FromPebiBytes(number);
- // Decimal
- case "kb":
- return FromKiloBytes(number);
- case "mb":
- return FromMegaBytes(number);
- case "gb":
- return FromGigaBytes(number);
- case "tb":
- return FromTeraBytes(number);
- case "pb":
- return FromPetaBytes(number);
- default:
- throw new FormatException($"Bytes of magnitude '{sizePart}' is not supported.");
- }
- }
- public static bool TryParse(string s, out ByteSize result) {
- try {
- result = Parse(s);
- return true;
- }
- catch {
- result = new ByteSize();
- return false;
- }
- }
- public static bool TryParse(string s, NumberStyles numberStyles, IFormatProvider formatProvider, out ByteSize result) {
- try {
- result = Parse(s, numberStyles, formatProvider);
- return true;
- }
- catch {
- result = new ByteSize();
- return false;
- }
- }
- }
- }
|