1package numfmt23import (4 "math"5 "strings"67 "src.rest/cat/012/internal/locale"8)910// sizeUnits are the units of the Size format, a thousand times apart,11// as nushell shows file sizes.12var sizeUnits = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}1314// Size shows v bytes as nushell shows a file size: whole bytes below15// 1000 ("512 B"), and above that dec decimals of the largest unit, a16// thousand times apart, that keeps the number under 1000. Like nushell,17// it cuts the digits past dec rather than rounding them, so 2998 bytes18// is "2.9 kB", never a unit it hasn't reached. Negative sizes show a19// minus sign.20func Size(v float64, dec int) string { return SizeIn(v, dec, locale.Canonical) }2122// SizeIn is Size as shown in loc: 1,6 kB in de-DE.23func SizeIn(v float64, dec int, loc *locale.Locale) string {24 sign := ""25 if v < 0 {26 sign, v = "-", -v27 }28 if math.IsInf(v, 0) || math.IsNaN(v) {29 return sign + "∞ B"30 }31 unit := 032 for unit < len(sizeUnits)-1 && v >= 1000 {33 v /= 100034 unit++35 }36 pat := "0"37 if unit > 0 && dec > 0 {38 pat += "." + strings.Repeat("0", dec)39 } else {40 dec = 041 }42 return sign + FormatIn(truncTo(v, dec), pat, loc) + " " + sizeUnits[unit]43}4445// truncTo cuts v to dec decimals. The nudge keeps a value that is46// exactly on a digit, such as 1.6 held as 1.5999999, from losing it.47func truncTo(v float64, dec int) float64 {48 p := math.Pow10(dec)49 return math.Trunc(v*p*(1+1e-12)) / p50}