kythe.io@v0.0.68-0.20240422202219-7225dbc01741/kythe/go/util/datasize/datasize.go (about) 1 /* 2 * Copyright 2015 The Kythe Authors. All rights reserved. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 // Package datasize implements a type representing data sizes in bytes. 18 package datasize // import "kythe.io/kythe/go/util/datasize" 19 20 import ( 21 "errors" 22 "flag" 23 "fmt" 24 "math" 25 "regexp" 26 "strconv" 27 "strings" 28 ) 29 30 type sizeFlag struct{ *Size } 31 32 // Flag defines a Size flag with specified name, default value, and usage string. 33 func Flag(name, value, description string) *Size { 34 sz, err := Parse(value) 35 if err != nil { 36 panic(fmt.Sprintf("Invalid default Size value for flag --%q: %q", name, value)) 37 } 38 return FlagVar(flag.CommandLine, &sz, name, sz, description) 39 } 40 41 // FlagVar defines a Size flag with specified name, default value, and usage string 42 // into the provided FlagSet. 43 func FlagVar(fs *flag.FlagSet, s *Size, name string, value Size, description string) *Size { 44 *s = value 45 f := &sizeFlag{s} 46 fs.Var(f, name, description) 47 return f.Size 48 } 49 50 // Get implements part of the flag.Getter interface. 51 func (f *sizeFlag) Get() any { 52 return *f.Size 53 } 54 55 // Set implements part of the flag.Value interface. 56 func (f *sizeFlag) Set(s string) error { 57 sz, err := Parse(s) 58 if err != nil { 59 return err 60 } 61 *f.Size = sz 62 return nil 63 } 64 65 // Size represents the size of data in bytes. 66 type Size uint64 67 68 var sizeRE = regexp.MustCompile(`([0-9]*)(\.[0-9]*)?([a-z]+)`) 69 70 // Parse parses a Size from a string. A Size is an unsigned decimal number with 71 // an optional fraction and a unit suffix. Examples: "0", "10B", "1kB", "4GB", 72 // "5GiB". Valid units are "B", (decimal: "kB", "MB", "GB, "TB, "PB"), (binary: 73 // "KiB", "MiB", "GiB", "TiB", "PiB") 74 func Parse(s string) (Size, error) { 75 // ([0-9]*(\.[0-9]*)?[a-z]+)+ 76 if s == "" { 77 return 0, errors.New("datasize: invalid Size: empty") 78 } 79 80 num, err := strconv.ParseFloat(s, 64) 81 if err == nil { 82 return Size(num), nil 83 } 84 85 ss := sizeRE.FindStringSubmatch(strings.ToLower(s)) 86 if len(ss) == 0 { 87 return 0, fmt.Errorf("datasize: invalid Size format %q", s) 88 } 89 90 num, err = strconv.ParseFloat(ss[1]+ss[2], 64) 91 if err != nil { 92 return 0, err 93 } 94 95 sz, err := suffixSize(ss[3]) 96 if err != nil { 97 return 0, err 98 } 99 100 return Size(num * float64(sz)), nil 101 } 102 103 func suffixSize(suffix string) (Size, error) { 104 switch suffix { 105 case "b": 106 return Byte, nil 107 case "kb": 108 return Kilobyte, nil 109 case "mb": 110 return Megabyte, nil 111 case "gb": 112 return Gigabyte, nil 113 case "tb": 114 return Terabyte, nil 115 case "pb": 116 return Petabyte, nil 117 case "kib": 118 return Kibibyte, nil 119 case "mib": 120 return Mebibyte, nil 121 case "gib": 122 return Gibibyte, nil 123 case "tib": 124 return Tebibyte, nil 125 case "pib": 126 return Pebibyte, nil 127 default: 128 return 0, fmt.Errorf("unknown datasize unit suffix: %q", suffix) 129 } 130 } 131 132 // From highest to lowest excluding Byte. 133 var allUnits = []Size{ 134 Pebibyte, 135 Petabyte, 136 Tebibyte, 137 Terabyte, 138 Gibibyte, 139 Gigabyte, 140 Mebibyte, 141 Megabyte, 142 Kibibyte, 143 Kilobyte, 144 } 145 146 // Common decimal data sizes 147 const ( 148 Kilobyte Size = 1000 * Byte 149 Megabyte = 1000 * Kilobyte 150 Gigabyte = 1000 * Megabyte 151 Terabyte = 1000 * Gigabyte 152 Petabyte = 1000 * Terabyte 153 ) 154 155 // Common binary data sizes 156 const ( 157 Byte Size = 1 158 Kibibyte = 1024 * Byte 159 Mebibyte = 1024 * Kibibyte 160 Gibibyte = 1024 * Mebibyte 161 Tebibyte = 1024 * Gibibyte 162 Pebibyte = 1024 * Tebibyte 163 ) 164 165 func unitSuffix(unit Size) string { 166 switch unit { 167 default: 168 return "B" 169 case Petabyte: 170 return "PB" 171 case Pebibyte: 172 return "PiB" 173 case Terabyte: 174 return "TB" 175 case Tebibyte: 176 return "TiB" 177 case Gigabyte: 178 return "GB" 179 case Gibibyte: 180 return "GiB" 181 case Megabyte: 182 return "MB" 183 case Mebibyte: 184 return "MiB" 185 case Kilobyte: 186 return "kB" 187 case Kibibyte: 188 return "KiB" 189 } 190 } 191 192 // Floor returns a Size nearest to a whole unit less than or equal to itself. 193 func (s Size) Floor() Size { 194 for _, unit := range allUnits { 195 if s >= unit { 196 return (s / unit) * unit 197 } 198 } 199 return s 200 } 201 202 // Round returns a Size nearest to a whole unit. 203 func (s Size) Round() Size { 204 for _, unit := range allUnits { 205 if s >= unit { 206 return Size(math.Round(float64(s)/float64(unit))) * unit 207 } 208 } 209 return s 210 } 211 212 // String implements the Stringer interface. 213 func (s Size) String() string { 214 switch { 215 case s == 0: 216 return "0B" 217 case s%Petabyte == 0: 218 return format(s.Petabytes(), "PB") 219 case s >= Pebibyte: 220 return format(s.Pebibytes(), "PiB") 221 case s%Terabyte == 0: 222 return format(s.Terabytes(), "TB") 223 case s >= Tebibyte: 224 return format(s.Tebibytes(), "TiB") 225 case s%Gigabyte == 0: 226 return format(s.Gigabytes(), "GB") 227 case s >= Gibibyte: 228 return format(s.Gibibytes(), "GiB") 229 case s%Megabyte == 0: 230 return format(s.Megabytes(), "MB") 231 case s >= Mebibyte: 232 return format(s.Mebibytes(), "MiB") 233 case s%Kilobyte == 0: 234 return format(s.Kilobytes(), "kB") 235 case s >= Kibibyte: 236 return format(s.Kibibytes(), "KiB") 237 } 238 return fmt.Sprintf("%dB", s) 239 } 240 241 func format(sz float64, suffix string) string { 242 if math.Floor(sz) == sz { 243 return fmt.Sprintf("%d%s", int64(sz), suffix) 244 } 245 return fmt.Sprintf("%.2f%s", sz, suffix) 246 } 247 248 // Bytes returns s in the equivalent number of bytes. 249 func (s Size) Bytes() uint64 { return uint64(s) } 250 251 // Kilobytes returns s in the equivalent number of kilobytes. 252 func (s Size) Kilobytes() float64 { return float64(s) / float64(Kilobyte) } 253 254 // Megabytes returns s in the equivalent number of megabytes. 255 func (s Size) Megabytes() float64 { return float64(s) / float64(Megabyte) } 256 257 // Gigabytes returns s in the equivalent number of gigabytes. 258 func (s Size) Gigabytes() float64 { return float64(s) / float64(Gigabyte) } 259 260 // Terabytes returns s in the equivalent number of terabytes. 261 func (s Size) Terabytes() float64 { return float64(s) / float64(Terabyte) } 262 263 // Petabytes returns s in the equivalent number of petabytes. 264 func (s Size) Petabytes() float64 { return float64(s) / float64(Petabyte) } 265 266 // Kibibytes returns s in the equivalent number of kibibytes. 267 func (s Size) Kibibytes() float64 { return float64(s) / float64(Kibibyte) } 268 269 // Mebibytes returns s in the equivalent number of mebibytes. 270 func (s Size) Mebibytes() float64 { return float64(s) / float64(Mebibyte) } 271 272 // Gibibytes returns s in the equivalent number of gibibytes. 273 func (s Size) Gibibytes() float64 { return float64(s) / float64(Gibibyte) } 274 275 // Tebibytes returns s in the equivalent number of tebibytes. 276 func (s Size) Tebibytes() float64 { return float64(s) / float64(Tebibyte) } 277 278 // Pebibytes returns s in the equivalent number of pebibytes. 279 func (s Size) Pebibytes() float64 { return float64(s) / float64(Pebibyte) }