| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454 | 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;            }        }    }}
 |