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) }