github.com/saferwall/pe@v1.5.2/cmd/size.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 ) 8 9 // See: http://en.wikipedia.org/wiki/Binary_prefix 10 const ( 11 // Decimal 12 13 KB = 1000 14 MB = 1000 * KB 15 GB = 1000 * MB 16 TB = 1000 * GB 17 PB = 1000 * TB 18 19 // Binary 20 21 KiB = 1024 22 MiB = 1024 * KiB 23 GiB = 1024 * MiB 24 TiB = 1024 * GiB 25 PiB = 1024 * TiB 26 ) 27 28 type unitMap map[byte]int64 29 30 var ( 31 decimalMap = unitMap{'k': KB, 'm': MB, 'g': GB, 't': TB, 'p': PB} 32 binaryMap = unitMap{'k': KiB, 'm': MiB, 'g': GiB, 't': TiB, 'p': PiB} 33 ) 34 35 var ( 36 decimapAbbrs = []string{"B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"} 37 binaryAbbrs = []string{"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB"} 38 ) 39 40 func getSizeAndUnit(size float64, base float64, _map []string) (float64, string) { 41 i := 0 42 unitsLimit := len(_map) - 1 43 for size >= base && i < unitsLimit { 44 size = size / base 45 i++ 46 } 47 return size, _map[i] 48 } 49 50 // CustomSize returns a human-readable approximation of a size 51 // using custom format. 52 func CustomSize(format string, size float64, base float64, _map []string) string { 53 size, unit := getSizeAndUnit(size, base, _map) 54 return fmt.Sprintf(format, size, unit) 55 } 56 57 // HumanSizeWithPrecision allows the size to be in any precision, 58 // instead of 4 digit precision used in units.HumanSize. 59 func HumanSizeWithPrecision(size float64, precision int) string { 60 size, unit := getSizeAndUnit(size, 1000.0, decimapAbbrs) 61 return fmt.Sprintf("%.*g%s", precision, size, unit) 62 } 63 64 // HumanSize returns a human-readable approximation of a size 65 // capped at 4 valid numbers (eg. "2.746 MB", "796 KB"). 66 func HumanSize(size float64) string { 67 return HumanSizeWithPrecision(size, 4) 68 } 69 70 // BytesSize returns a human-readable size in bytes, kibibytes, 71 // mebibytes, gibibytes, or tebibytes (eg. "44kiB", "17MiB"). 72 func BytesSize(size float64) string { 73 return CustomSize("%.4g%s", size, 1024.0, binaryAbbrs) 74 } 75 76 // FromHumanSize returns an integer from a human-readable specification of a 77 // size using SI standard (eg. "44kB", "17MB"). 78 func FromHumanSize(size string) (int64, error) { 79 return parseSize(size, decimalMap) 80 } 81 82 // RAMInBytes parses a human-readable string representing an amount of RAM 83 // in bytes, kibibytes, mebibytes, gibibytes, or tebibytes and 84 // returns the number of bytes, or -1 if the string is unparseable. 85 // Units are case-insensitive, and the 'b' suffix is optional. 86 func RAMInBytes(size string) (int64, error) { 87 return parseSize(size, binaryMap) 88 } 89 90 // Parses the human-readable size string into the amount it represents. 91 func parseSize(sizeStr string, uMap unitMap) (int64, error) { 92 // TODO: rewrite to use strings.Cut if there's a space 93 // once Go < 1.18 is deprecated. 94 sep := strings.LastIndexAny(sizeStr, "01234567890. ") 95 if sep == -1 { 96 // There should be at least a digit. 97 return -1, fmt.Errorf("invalid size: '%s'", sizeStr) 98 } 99 var num, sfx string 100 if sizeStr[sep] != ' ' { 101 num = sizeStr[:sep+1] 102 sfx = sizeStr[sep+1:] 103 } else { 104 // Omit the space separator. 105 num = sizeStr[:sep] 106 sfx = sizeStr[sep+1:] 107 } 108 109 size, err := strconv.ParseFloat(num, 64) 110 if err != nil { 111 return -1, err 112 } 113 // Backward compatibility: reject negative sizes. 114 if size < 0 { 115 return -1, fmt.Errorf("invalid size: '%s'", sizeStr) 116 } 117 118 if len(sfx) == 0 { 119 return int64(size), nil 120 } 121 122 // Process the suffix. 123 124 if len(sfx) > 3 { // Too long. 125 goto badSuffix 126 } 127 sfx = strings.ToLower(sfx) 128 // Trivial case: b suffix. 129 if sfx[0] == 'b' { 130 if len(sfx) > 1 { // no extra characters allowed after b. 131 goto badSuffix 132 } 133 return int64(size), nil 134 } 135 // A suffix from the map. 136 if mul, ok := uMap[sfx[0]]; ok { 137 size *= float64(mul) 138 } else { 139 goto badSuffix 140 } 141 142 // The suffix may have extra "b" or "ib" (e.g. KiB or MB). 143 switch { 144 case len(sfx) == 2 && sfx[1] != 'b': 145 goto badSuffix 146 case len(sfx) == 3 && sfx[1:] != "ib": 147 goto badSuffix 148 } 149 150 return int64(size), nil 151 152 badSuffix: 153 return -1, fmt.Errorf("invalid suffix: '%s'", sfx) 154 }