Last active
August 29, 2015 13:56
-
-
Save atifaziz/9109568 to your computer and use it in GitHub Desktop.
Custom formatter in F# & C# for formatting byte (file/disk/stream) sizes/lengths (e.g. 1.1MB, 2.3GB, 4 bytes, etc.)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
// Adapted from: http://stackoverflow.com/questions/128618/c-file-size-format-provider | |
// Credit: http://flimflan.com/blog/FileSizeFormatProvider.aspx | |
public sealed class ByteSizeFormatProvider : IFormatProvider, ICustomFormatter | |
{ | |
public object GetFormat(Type formatType) | |
{ | |
return formatType == typeof(ICustomFormatter) ? this : null; | |
} | |
private const string FormatSpecifier = "SZ"; | |
private const decimal OneKiloByte = 1024m; | |
private const decimal OneMegaByte = OneKiloByte * 1024m; | |
private const decimal OneGigaByte = OneMegaByte * 1024m; | |
public string Format(string format, object arg, IFormatProvider formatProvider) | |
{ | |
if (format == null || !format.StartsWith(FormatSpecifier, StringComparison.Ordinal)) | |
return DefaultFormat(format, arg, formatProvider); | |
if (arg is string) | |
return DefaultFormat(format, arg, formatProvider); | |
decimal size; | |
try | |
{ | |
size = Convert.ToDecimal(arg, formatProvider); | |
} | |
catch (InvalidCastException) | |
{ | |
return DefaultFormat(format, arg, formatProvider); | |
} | |
// TODO: Localization of byte(s), KB, MB, etc. | |
string suffix; | |
var ignorePrecision = false; | |
if (size > OneGigaByte) | |
{ | |
size /= OneGigaByte; | |
suffix = "GB"; | |
} | |
else if (size > OneMegaByte) | |
{ | |
size /= OneMegaByte; | |
suffix = "MB"; | |
} | |
else if (size > OneKiloByte) | |
{ | |
size /= OneKiloByte; | |
suffix = "KB"; | |
} | |
else if (size == 1) | |
{ | |
suffix = " byte"; | |
ignorePrecision = true; | |
} | |
else | |
{ | |
suffix = " bytes"; | |
ignorePrecision = true; | |
} | |
var precision = ignorePrecision ? "0" : format.Substring(FormatSpecifier.Length); | |
return size.ToString(GetPrecisionFormat(precision), formatProvider) + suffix; | |
} | |
static string GetPrecisionFormat(string precision) | |
{ | |
if (string.IsNullOrEmpty(precision)) | |
return GetPrecisionFormat("2"); | |
if (precision.Length == 1) | |
{ | |
switch (precision[0]) | |
{ | |
case '0': return "N0"; | |
case '1': return "N1"; | |
case '2': return "N2"; | |
case '3': return "N3"; | |
case '4': return "N4"; | |
} | |
} | |
return "N" + precision; | |
} | |
static string DefaultFormat(string format, object arg, IFormatProvider formatProvider) | |
{ | |
var formattable = arg as IFormattable; | |
return formattable != null | |
? formattable.ToString(format, formatProvider) | |
: arg.ToString(); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
open System | |
type ByteSizeFormatProvider() = | |
let formatSpecifier = "SZ" | |
let kiloByte = 1024m | |
let megaByte = kiloByte * 1024m | |
let gigaByte = megaByte * 1024m | |
let rec getPrecisionFormat precision = | |
if String.IsNullOrEmpty(precision) then getPrecisionFormat "2" | |
else match precision with | |
| "0" -> "N0" | |
| "1" -> "N1" | |
| "2" -> "N2" | |
| "3" -> "N3" | |
| "4" -> "N4" | |
| _ -> "N" + precision | |
let defaultFormat format formatProvider (arg : obj) = | |
match arg with | |
| :? IFormattable as formattable -> formattable.ToString(format, formatProvider) | |
| _ -> arg.ToString() | |
interface IFormatProvider with | |
member this.GetFormat(formatType) = | |
if formatType = typeof<ICustomFormatter> then this :> obj else null | |
interface ICustomFormatter with | |
member this.Format(format, arg, formatProvider) = | |
if (format = null | |
|| not (format.StartsWith(formatSpecifier, StringComparison.Ordinal)) | |
|| arg :? string) then | |
arg |> defaultFormat format formatProvider | |
else | |
let size = try Some(Convert.ToDecimal(arg, formatProvider)) | |
with | :? InvalidCastException -> None | |
match size with | |
| None -> arg |> defaultFormat format formatProvider | |
| Some(size) -> | |
let (size, suffix, ignorePrecision) = | |
if (size > gigaByte) then (size / gigaByte, "GB", false) | |
else if (size > megaByte) then (size / megaByte, "MB", false) | |
else if (size > kiloByte) then (size / kiloByte, "KB", false) | |
else if (size = 1m ) then (size, " byte", true) | |
else (size, " bytes", true) | |
let precision = if ignorePrecision then "0" else format.Substring(formatSpecifier.Length) | |
size.ToString(getPrecisionFormat precision, formatProvider) + suffix | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment