github.com/sagernet/sing-box@v1.9.0-rc.20/common/humanize/bytes.go (about)

     1  package humanize
     2  
     3  import (
     4  	"fmt"
     5  	"math"
     6  	"strconv"
     7  	"strings"
     8  	"unicode"
     9  )
    10  
    11  // IEC Sizes.
    12  // kibis of bits
    13  const (
    14  	Byte = 1 << (iota * 10)
    15  	KiByte
    16  	MiByte
    17  	GiByte
    18  	TiByte
    19  	PiByte
    20  	EiByte
    21  )
    22  
    23  // SI Sizes.
    24  const (
    25  	IByte = 1
    26  	KByte = IByte * 1000
    27  	MByte = KByte * 1000
    28  	GByte = MByte * 1000
    29  	TByte = GByte * 1000
    30  	PByte = TByte * 1000
    31  	EByte = PByte * 1000
    32  )
    33  
    34  var defaultSizeTable = map[string]uint64{
    35  	"b":   Byte,
    36  	"kib": KiByte,
    37  	"kb":  KByte,
    38  	"mib": MiByte,
    39  	"mb":  MByte,
    40  	"gib": GiByte,
    41  	"gb":  GByte,
    42  	"tib": TiByte,
    43  	"tb":  TByte,
    44  	"pib": PiByte,
    45  	"pb":  PByte,
    46  	"eib": EiByte,
    47  	"eb":  EByte,
    48  	// Without suffix
    49  	"":   Byte,
    50  	"ki": KiByte,
    51  	"k":  KByte,
    52  	"mi": MiByte,
    53  	"m":  MByte,
    54  	"gi": GiByte,
    55  	"g":  GByte,
    56  	"ti": TiByte,
    57  	"t":  TByte,
    58  	"pi": PiByte,
    59  	"p":  PByte,
    60  	"ei": EiByte,
    61  	"e":  EByte,
    62  }
    63  
    64  var memorysSizeTable = map[string]uint64{
    65  	"b":  Byte,
    66  	"kb": KiByte,
    67  	"mb": MiByte,
    68  	"gb": GiByte,
    69  	"tb": TiByte,
    70  	"pb": PiByte,
    71  	"eb": EiByte,
    72  	"":   Byte,
    73  	"k":  KiByte,
    74  	"m":  MiByte,
    75  	"g":  GiByte,
    76  	"t":  TiByte,
    77  	"p":  PiByte,
    78  	"e":  EiByte,
    79  }
    80  
    81  var (
    82  	defaultSizes = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB"}
    83  	iSizes       = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"}
    84  )
    85  
    86  func Bytes(s uint64) string {
    87  	return humanateBytes(s, 1000, defaultSizes)
    88  }
    89  
    90  func MemoryBytes(s uint64) string {
    91  	return humanateBytes(s, 1024, defaultSizes)
    92  }
    93  
    94  func IBytes(s uint64) string {
    95  	return humanateBytes(s, 1024, iSizes)
    96  }
    97  
    98  func logn(n, b float64) float64 {
    99  	return math.Log(n) / math.Log(b)
   100  }
   101  
   102  func humanateBytes(s uint64, base float64, sizes []string) string {
   103  	if s < 10 {
   104  		return fmt.Sprintf("%d B", s)
   105  	}
   106  	e := math.Floor(logn(float64(s), base))
   107  	suffix := sizes[int(e)]
   108  	val := math.Floor(float64(s)/math.Pow(base, e)*10+0.5) / 10
   109  	f := "%.0f %s"
   110  	if val < 10 {
   111  		f = "%.1f %s"
   112  	}
   113  
   114  	return fmt.Sprintf(f, val, suffix)
   115  }
   116  
   117  func ParseBytes(s string) (uint64, error) {
   118  	return parseBytes0(s, defaultSizeTable)
   119  }
   120  
   121  func ParseMemoryBytes(s string) (uint64, error) {
   122  	return parseBytes0(s, memorysSizeTable)
   123  }
   124  
   125  func parseBytes0(s string, sizeTable map[string]uint64) (uint64, error) {
   126  	lastDigit := 0
   127  	hasComma := false
   128  	for _, r := range s {
   129  		if !(unicode.IsDigit(r) || r == '.' || r == ',') {
   130  			break
   131  		}
   132  		if r == ',' {
   133  			hasComma = true
   134  		}
   135  		lastDigit++
   136  	}
   137  
   138  	num := s[:lastDigit]
   139  	if hasComma {
   140  		num = strings.Replace(num, ",", "", -1)
   141  	}
   142  
   143  	f, err := strconv.ParseFloat(num, 64)
   144  	if err != nil {
   145  		return 0, err
   146  	}
   147  
   148  	extra := strings.ToLower(strings.TrimSpace(s[lastDigit:]))
   149  	if m, ok := sizeTable[extra]; ok {
   150  		f *= float64(m)
   151  		if f >= math.MaxUint64 {
   152  			return 0, fmt.Errorf("too large: %v", s)
   153  		}
   154  		return uint64(f), nil
   155  	}
   156  
   157  	return 0, fmt.Errorf("unhandled size name: %v", extra)
   158  }