github.com/AntonOrnatskyi/goproxy@v0.0.0-20190205095733-4526a9fa18b4/utils/datasize/datasize.go (about) 1 package datasize 2 3 import ( 4 "errors" 5 "fmt" 6 "strconv" 7 "strings" 8 ) 9 10 type ByteSize uint64 11 12 const ( 13 B ByteSize = 1 14 KB = B << 10 15 MB = KB << 10 16 GB = MB << 10 17 TB = GB << 10 18 PB = TB << 10 19 EB = PB << 10 20 21 fnUnmarshalText string = "UnmarshalText" 22 maxUint64 uint64 = (1 << 64) - 1 23 cutoff uint64 = maxUint64 / 10 24 ) 25 26 var ErrBits = errors.New("unit with capital unit prefix and lower case unit (b) - bits, not bytes ") 27 28 var defaultDatasize ByteSize 29 30 func Parse(s string) (bytes uint64, err error) { 31 err = defaultDatasize.UnmarshalText([]byte(s)) 32 if err != nil { 33 return 34 } 35 bytes = defaultDatasize.Bytes() 36 return 37 } 38 func HumanSize(bytes uint64) (s string, err error) { 39 err = defaultDatasize.UnmarshalText([]byte(fmt.Sprintf("%d", bytes))) 40 if err != nil { 41 return 42 } 43 s = defaultDatasize.HumanReadable() 44 return 45 } 46 func (b ByteSize) Bytes() uint64 { 47 return uint64(b) 48 } 49 50 func (b ByteSize) KBytes() float64 { 51 v := b / KB 52 r := b % KB 53 return float64(v) + float64(r)/float64(KB) 54 } 55 56 func (b ByteSize) MBytes() float64 { 57 v := b / MB 58 r := b % MB 59 return float64(v) + float64(r)/float64(MB) 60 } 61 62 func (b ByteSize) GBytes() float64 { 63 v := b / GB 64 r := b % GB 65 return float64(v) + float64(r)/float64(GB) 66 } 67 68 func (b ByteSize) TBytes() float64 { 69 v := b / TB 70 r := b % TB 71 return float64(v) + float64(r)/float64(TB) 72 } 73 74 func (b ByteSize) PBytes() float64 { 75 v := b / PB 76 r := b % PB 77 return float64(v) + float64(r)/float64(PB) 78 } 79 80 func (b ByteSize) EBytes() float64 { 81 v := b / EB 82 r := b % EB 83 return float64(v) + float64(r)/float64(EB) 84 } 85 86 func (b ByteSize) String() string { 87 switch { 88 case b == 0: 89 return fmt.Sprint("0B") 90 case b%EB == 0: 91 return fmt.Sprintf("%dEB", b/EB) 92 case b%PB == 0: 93 return fmt.Sprintf("%dPB", b/PB) 94 case b%TB == 0: 95 return fmt.Sprintf("%dTB", b/TB) 96 case b%GB == 0: 97 return fmt.Sprintf("%dGB", b/GB) 98 case b%MB == 0: 99 return fmt.Sprintf("%dMB", b/MB) 100 case b%KB == 0: 101 return fmt.Sprintf("%dKB", b/KB) 102 default: 103 return fmt.Sprintf("%dB", b) 104 } 105 } 106 107 func (b ByteSize) HR() string { 108 return b.HumanReadable() 109 } 110 111 func (b ByteSize) HumanReadable() string { 112 switch { 113 case b > EB: 114 return fmt.Sprintf("%.1f EB", b.EBytes()) 115 case b > PB: 116 return fmt.Sprintf("%.1f PB", b.PBytes()) 117 case b > TB: 118 return fmt.Sprintf("%.1f TB", b.TBytes()) 119 case b > GB: 120 return fmt.Sprintf("%.1f GB", b.GBytes()) 121 case b > MB: 122 return fmt.Sprintf("%.1f MB", b.MBytes()) 123 case b > KB: 124 return fmt.Sprintf("%.1f KB", b.KBytes()) 125 default: 126 return fmt.Sprintf("%d B", b) 127 } 128 } 129 130 func (b ByteSize) MarshalText() ([]byte, error) { 131 return []byte(b.String()), nil 132 } 133 134 func (b *ByteSize) UnmarshalText(t []byte) error { 135 var val uint64 136 var unit string 137 138 // copy for error message 139 t0 := t 140 141 var c byte 142 var i int 143 144 ParseLoop: 145 for i < len(t) { 146 c = t[i] 147 switch { 148 case '0' <= c && c <= '9': 149 if val > cutoff { 150 goto Overflow 151 } 152 153 c = c - '0' 154 val *= 10 155 156 if val > val+uint64(c) { 157 // val+v overflows 158 goto Overflow 159 } 160 val += uint64(c) 161 i++ 162 163 default: 164 if i == 0 { 165 goto SyntaxError 166 } 167 break ParseLoop 168 } 169 } 170 171 unit = strings.TrimSpace(string(t[i:])) 172 switch unit { 173 case "Kb", "Mb", "Gb", "Tb", "Pb", "Eb": 174 goto BitsError 175 } 176 unit = strings.ToLower(unit) 177 switch unit { 178 case "", "b", "byte": 179 // do nothing - already in bytes 180 181 case "k", "kb", "kilo", "kilobyte", "kilobytes": 182 if val > maxUint64/uint64(KB) { 183 goto Overflow 184 } 185 val *= uint64(KB) 186 187 case "m", "mb", "mega", "megabyte", "megabytes": 188 if val > maxUint64/uint64(MB) { 189 goto Overflow 190 } 191 val *= uint64(MB) 192 193 case "g", "gb", "giga", "gigabyte", "gigabytes": 194 if val > maxUint64/uint64(GB) { 195 goto Overflow 196 } 197 val *= uint64(GB) 198 199 case "t", "tb", "tera", "terabyte", "terabytes": 200 if val > maxUint64/uint64(TB) { 201 goto Overflow 202 } 203 val *= uint64(TB) 204 205 case "p", "pb", "peta", "petabyte", "petabytes": 206 if val > maxUint64/uint64(PB) { 207 goto Overflow 208 } 209 val *= uint64(PB) 210 211 case "E", "EB", "e", "eb", "eB": 212 if val > maxUint64/uint64(EB) { 213 goto Overflow 214 } 215 val *= uint64(EB) 216 217 default: 218 goto SyntaxError 219 } 220 221 *b = ByteSize(val) 222 return nil 223 224 Overflow: 225 *b = ByteSize(maxUint64) 226 return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrRange} 227 228 SyntaxError: 229 *b = 0 230 return &strconv.NumError{fnUnmarshalText, string(t0), strconv.ErrSyntax} 231 232 BitsError: 233 *b = 0 234 return &strconv.NumError{fnUnmarshalText, string(t0), ErrBits} 235 }