github.com/ncw/rclone@v1.48.1-0.20190724201158-a35aa1360e3e/fs/sizesuffix.go (about) 1 package fs 2 3 // SizeSuffix is parsed by flag with k/M/G suffixes 4 import ( 5 "fmt" 6 "math" 7 "sort" 8 "strconv" 9 "strings" 10 11 "github.com/pkg/errors" 12 ) 13 14 // SizeSuffix is an int64 with a friendly way of printing setting 15 type SizeSuffix int64 16 17 // Common multipliers for SizeSuffix 18 const ( 19 Byte SizeSuffix = 1 << (iota * 10) 20 KibiByte 21 MebiByte 22 GibiByte 23 TebiByte 24 PebiByte 25 ExbiByte 26 ) 27 28 // Turn SizeSuffix into a string and a suffix 29 func (x SizeSuffix) string() (string, string) { 30 scaled := float64(0) 31 suffix := "" 32 switch { 33 case x < 0: 34 return "off", "" 35 case x == 0: 36 return "0", "" 37 case x < 1<<10: 38 scaled = float64(x) 39 suffix = "" 40 case x < 1<<20: 41 scaled = float64(x) / (1 << 10) 42 suffix = "k" 43 case x < 1<<30: 44 scaled = float64(x) / (1 << 20) 45 suffix = "M" 46 case x < 1<<40: 47 scaled = float64(x) / (1 << 30) 48 suffix = "G" 49 case x < 1<<50: 50 scaled = float64(x) / (1 << 40) 51 suffix = "T" 52 default: 53 scaled = float64(x) / (1 << 50) 54 suffix = "P" 55 } 56 if math.Floor(scaled) == scaled { 57 return fmt.Sprintf("%.0f", scaled), suffix 58 } 59 return fmt.Sprintf("%.3f", scaled), suffix 60 } 61 62 // String turns SizeSuffix into a string 63 func (x SizeSuffix) String() string { 64 val, suffix := x.string() 65 return val + suffix 66 } 67 68 // Unit turns SizeSuffix into a string with a unit 69 func (x SizeSuffix) Unit(unit string) string { 70 val, suffix := x.string() 71 if val == "off" { 72 return val 73 } 74 return val + " " + suffix + unit 75 } 76 77 // Set a SizeSuffix 78 func (x *SizeSuffix) Set(s string) error { 79 if len(s) == 0 { 80 return errors.New("empty string") 81 } 82 if strings.ToLower(s) == "off" { 83 *x = -1 84 return nil 85 } 86 suffix := s[len(s)-1] 87 suffixLen := 1 88 var multiplier float64 89 switch suffix { 90 case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.': 91 suffixLen = 0 92 multiplier = 1 << 10 93 case 'b', 'B': 94 multiplier = 1 95 case 'k', 'K': 96 multiplier = 1 << 10 97 case 'm', 'M': 98 multiplier = 1 << 20 99 case 'g', 'G': 100 multiplier = 1 << 30 101 case 't', 'T': 102 multiplier = 1 << 40 103 case 'p', 'P': 104 multiplier = 1 << 50 105 default: 106 return errors.Errorf("bad suffix %q", suffix) 107 } 108 s = s[:len(s)-suffixLen] 109 value, err := strconv.ParseFloat(s, 64) 110 if err != nil { 111 return err 112 } 113 if value < 0 { 114 return errors.Errorf("size can't be negative %q", s) 115 } 116 value *= multiplier 117 *x = SizeSuffix(value) 118 return nil 119 } 120 121 // Type of the value 122 func (x *SizeSuffix) Type() string { 123 return "SizeSuffix" 124 } 125 126 // Scan implements the fmt.Scanner interface 127 func (x *SizeSuffix) Scan(s fmt.ScanState, ch rune) error { 128 token, err := s.Token(true, nil) 129 if err != nil { 130 return err 131 } 132 return x.Set(string(token)) 133 } 134 135 // SizeSuffixList is a slice SizeSuffix values 136 type SizeSuffixList []SizeSuffix 137 138 func (l SizeSuffixList) Len() int { return len(l) } 139 func (l SizeSuffixList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } 140 func (l SizeSuffixList) Less(i, j int) bool { return l[i] < l[j] } 141 142 // Sort sorts the list 143 func (l SizeSuffixList) Sort() { 144 sort.Sort(l) 145 }