khenaidoo | ab1f7bd | 2019-11-14 14:00:27 -0500 | [diff] [blame] | 1 | package humanize |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "math/big" |
| 6 | "strings" |
| 7 | "unicode" |
| 8 | ) |
| 9 | |
| 10 | var ( |
| 11 | bigIECExp = big.NewInt(1024) |
| 12 | |
| 13 | // BigByte is one byte in bit.Ints |
| 14 | BigByte = big.NewInt(1) |
| 15 | // BigKiByte is 1,024 bytes in bit.Ints |
| 16 | BigKiByte = (&big.Int{}).Mul(BigByte, bigIECExp) |
| 17 | // BigMiByte is 1,024 k bytes in bit.Ints |
| 18 | BigMiByte = (&big.Int{}).Mul(BigKiByte, bigIECExp) |
| 19 | // BigGiByte is 1,024 m bytes in bit.Ints |
| 20 | BigGiByte = (&big.Int{}).Mul(BigMiByte, bigIECExp) |
| 21 | // BigTiByte is 1,024 g bytes in bit.Ints |
| 22 | BigTiByte = (&big.Int{}).Mul(BigGiByte, bigIECExp) |
| 23 | // BigPiByte is 1,024 t bytes in bit.Ints |
| 24 | BigPiByte = (&big.Int{}).Mul(BigTiByte, bigIECExp) |
| 25 | // BigEiByte is 1,024 p bytes in bit.Ints |
| 26 | BigEiByte = (&big.Int{}).Mul(BigPiByte, bigIECExp) |
| 27 | // BigZiByte is 1,024 e bytes in bit.Ints |
| 28 | BigZiByte = (&big.Int{}).Mul(BigEiByte, bigIECExp) |
| 29 | // BigYiByte is 1,024 z bytes in bit.Ints |
| 30 | BigYiByte = (&big.Int{}).Mul(BigZiByte, bigIECExp) |
| 31 | ) |
| 32 | |
| 33 | var ( |
| 34 | bigSIExp = big.NewInt(1000) |
| 35 | |
| 36 | // BigSIByte is one SI byte in big.Ints |
| 37 | BigSIByte = big.NewInt(1) |
| 38 | // BigKByte is 1,000 SI bytes in big.Ints |
| 39 | BigKByte = (&big.Int{}).Mul(BigSIByte, bigSIExp) |
| 40 | // BigMByte is 1,000 SI k bytes in big.Ints |
| 41 | BigMByte = (&big.Int{}).Mul(BigKByte, bigSIExp) |
| 42 | // BigGByte is 1,000 SI m bytes in big.Ints |
| 43 | BigGByte = (&big.Int{}).Mul(BigMByte, bigSIExp) |
| 44 | // BigTByte is 1,000 SI g bytes in big.Ints |
| 45 | BigTByte = (&big.Int{}).Mul(BigGByte, bigSIExp) |
| 46 | // BigPByte is 1,000 SI t bytes in big.Ints |
| 47 | BigPByte = (&big.Int{}).Mul(BigTByte, bigSIExp) |
| 48 | // BigEByte is 1,000 SI p bytes in big.Ints |
| 49 | BigEByte = (&big.Int{}).Mul(BigPByte, bigSIExp) |
| 50 | // BigZByte is 1,000 SI e bytes in big.Ints |
| 51 | BigZByte = (&big.Int{}).Mul(BigEByte, bigSIExp) |
| 52 | // BigYByte is 1,000 SI z bytes in big.Ints |
| 53 | BigYByte = (&big.Int{}).Mul(BigZByte, bigSIExp) |
| 54 | ) |
| 55 | |
| 56 | var bigBytesSizeTable = map[string]*big.Int{ |
| 57 | "b": BigByte, |
| 58 | "kib": BigKiByte, |
| 59 | "kb": BigKByte, |
| 60 | "mib": BigMiByte, |
| 61 | "mb": BigMByte, |
| 62 | "gib": BigGiByte, |
| 63 | "gb": BigGByte, |
| 64 | "tib": BigTiByte, |
| 65 | "tb": BigTByte, |
| 66 | "pib": BigPiByte, |
| 67 | "pb": BigPByte, |
| 68 | "eib": BigEiByte, |
| 69 | "eb": BigEByte, |
| 70 | "zib": BigZiByte, |
| 71 | "zb": BigZByte, |
| 72 | "yib": BigYiByte, |
| 73 | "yb": BigYByte, |
| 74 | // Without suffix |
| 75 | "": BigByte, |
| 76 | "ki": BigKiByte, |
| 77 | "k": BigKByte, |
| 78 | "mi": BigMiByte, |
| 79 | "m": BigMByte, |
| 80 | "gi": BigGiByte, |
| 81 | "g": BigGByte, |
| 82 | "ti": BigTiByte, |
| 83 | "t": BigTByte, |
| 84 | "pi": BigPiByte, |
| 85 | "p": BigPByte, |
| 86 | "ei": BigEiByte, |
| 87 | "e": BigEByte, |
| 88 | "z": BigZByte, |
| 89 | "zi": BigZiByte, |
| 90 | "y": BigYByte, |
| 91 | "yi": BigYiByte, |
| 92 | } |
| 93 | |
| 94 | var ten = big.NewInt(10) |
| 95 | |
| 96 | func humanateBigBytes(s, base *big.Int, sizes []string) string { |
| 97 | if s.Cmp(ten) < 0 { |
| 98 | return fmt.Sprintf("%d B", s) |
| 99 | } |
| 100 | c := (&big.Int{}).Set(s) |
| 101 | val, mag := oomm(c, base, len(sizes)-1) |
| 102 | suffix := sizes[mag] |
| 103 | f := "%.0f %s" |
| 104 | if val < 10 { |
| 105 | f = "%.1f %s" |
| 106 | } |
| 107 | |
| 108 | return fmt.Sprintf(f, val, suffix) |
| 109 | |
| 110 | } |
| 111 | |
| 112 | // BigBytes produces a human readable representation of an SI size. |
| 113 | // |
| 114 | // See also: ParseBigBytes. |
| 115 | // |
| 116 | // BigBytes(82854982) -> 83 MB |
| 117 | func BigBytes(s *big.Int) string { |
| 118 | sizes := []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} |
| 119 | return humanateBigBytes(s, bigSIExp, sizes) |
| 120 | } |
| 121 | |
| 122 | // BigIBytes produces a human readable representation of an IEC size. |
| 123 | // |
| 124 | // See also: ParseBigBytes. |
| 125 | // |
| 126 | // BigIBytes(82854982) -> 79 MiB |
| 127 | func BigIBytes(s *big.Int) string { |
| 128 | sizes := []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} |
| 129 | return humanateBigBytes(s, bigIECExp, sizes) |
| 130 | } |
| 131 | |
| 132 | // ParseBigBytes parses a string representation of bytes into the number |
| 133 | // of bytes it represents. |
| 134 | // |
| 135 | // See also: BigBytes, BigIBytes. |
| 136 | // |
| 137 | // ParseBigBytes("42 MB") -> 42000000, nil |
| 138 | // ParseBigBytes("42 mib") -> 44040192, nil |
| 139 | func ParseBigBytes(s string) (*big.Int, error) { |
| 140 | lastDigit := 0 |
| 141 | hasComma := false |
| 142 | for _, r := range s { |
| 143 | if !(unicode.IsDigit(r) || r == '.' || r == ',') { |
| 144 | break |
| 145 | } |
| 146 | if r == ',' { |
| 147 | hasComma = true |
| 148 | } |
| 149 | lastDigit++ |
| 150 | } |
| 151 | |
| 152 | num := s[:lastDigit] |
| 153 | if hasComma { |
| 154 | num = strings.Replace(num, ",", "", -1) |
| 155 | } |
| 156 | |
| 157 | val := &big.Rat{} |
| 158 | _, err := fmt.Sscanf(num, "%f", val) |
| 159 | if err != nil { |
| 160 | return nil, err |
| 161 | } |
| 162 | |
| 163 | extra := strings.ToLower(strings.TrimSpace(s[lastDigit:])) |
| 164 | if m, ok := bigBytesSizeTable[extra]; ok { |
| 165 | mv := (&big.Rat{}).SetInt(m) |
| 166 | val.Mul(val, mv) |
| 167 | rv := &big.Int{} |
| 168 | rv.Div(val.Num(), val.Denom()) |
| 169 | return rv, nil |
| 170 | } |
| 171 | |
| 172 | return nil, fmt.Errorf("unhandled size name: %v", extra) |
| 173 | } |