github.com/sandwich-go/boost@v1.3.29/misc/hrff/hrff.go (about) 1 // Copyright © 2012-2019 Lawrence E. Bakst. All rights reserved. 2 3 // Package hrff (Human Readbale Flags and Formatting) 4 // Allows command line arguments like % dd bs=1Mi 5 // Provides SI unit formatting via %h and %H format characters 6 // Defines two news types, Int64 and Float64 which provide methods for flags to accept these kind of args 7 package hrff // import "leb.io/hrff" 8 9 import ( 10 "fmt" 11 "strconv" 12 ) 13 14 // SIsufixes is public so you can add a prefix if you want to 15 var SIsufixes = map[string]float64{ 16 17 "geop": 10000000000000000000000000000000, // geop 10^30 18 "bronto": 10000000000000000000000000000, // bronto 10^27 19 "Y": 1000000000000000000000000, // yota 20 "Z": 1000000000000000000000, // zetta 21 "E": 1000000000000000000, // exa 22 "P": 1000000000000000, // peta 23 "T": 1000000000000, // tera 24 "G": 1000000000, // giga 25 "M": 1000000, // mega 26 //"my": 10000, // prefix myria- (my-) was formerly used for 10^4 but now depricated 27 "k": 1000, // kilo, chilo in Italian 28 "h": 100, // hecto, etto in Italian 29 "da": 10, // SI: deca, NIST: deka 30 "": 1, // not real dummy stopper 31 "d": .1, // deci 32 "c": .01, // centi 33 "m": .001, // milli 34 "µ": .000001, // micro (unicode char see below) 35 "n": .000000001, // nano 36 "p": .00000000001, // pico 37 "f": .000000000000001, // femto 38 "a": .000000000000000001, // atto 39 "z": .000000000000000000001, // zepto 40 "y": .000000000000000000000001, // yocto 41 42 "u": .000001, // micro (with u) 43 44 "Ki": 1024, // kibi 45 "Mi": 1024 * 1024, // mebi 46 "Gi": 1024 * 1024 * 1024, // gibi 47 "Ti": 1024 * 1024 * 1024 * 1024, // tebi 48 "Pi": 1024 * 1024 * 1024 * 1024 * 1024, // pebi 49 "Ei": 1024 * 1024 * 1024 * 1024 * 1024 * 1024, // exbi 50 "Zi": 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, // zebi 51 "Yi": 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, // yobi 52 } 53 54 var order = []string{"geop", "bronto", "Y", "Z", "E", "P", "T", "G", "M", "k", "h", "da", "", "d", "c", "m", "µ", "n", "p", "f", "a", "z", "y"} 55 var order2 = []string{"Yi", "Zi", "Ei", "Pi", "Ti", "Gi", "Mi", "Ki", "", "d", "c", "m", "µ", "n", "p", "f", "a", "z", "y"} 56 var skips = map[string]bool{"h": true, "da": true, "d": true, "c": true} // The sufixes h, da, d, c aren't much used for scienttific work, skip them 57 58 // Classic is what life was like before SI 59 func Classic() { 60 SIsufixes["K"] = SIsufixes["Ki"] 61 SIsufixes["M"] = SIsufixes["Mi"] 62 SIsufixes["G"] = SIsufixes["Gi"] 63 SIsufixes["T"] = SIsufixes["Ti"] 64 SIsufixes["P"] = SIsufixes["Pi"] 65 SIsufixes["E"] = SIsufixes["Ei"] 66 SIsufixes["Z"] = SIsufixes["Zi"] 67 SIsufixes["Y"] = SIsufixes["Yi"] 68 } 69 70 // RemoveNominal removes unoffice suffixes 71 func RemoveNominal() { 72 delete(SIsufixes, "h") 73 delete(SIsufixes, "da") 74 delete(SIsufixes, "d") 75 delete(SIsufixes, "c") 76 } 77 78 // UseHella allows use of H over bronto 79 func UseHella() { 80 delete(SIsufixes, "bronto") 81 SIsufixes["H"] = 10000000000000000000000000000 // hella (one for the team) 82 } 83 84 // Int is a version of int with a unit 85 type Int struct { 86 V int 87 U string 88 } 89 90 // Float64 is a version of float64 with a unit 91 type Float64 struct { 92 V float64 93 U string 94 } 95 96 // Int64 is a version of int64 with a unit 97 type Int64 struct { 98 V int64 99 U string 100 } 101 102 // AddSkip adds a skip 103 func AddSkip(sip string, b bool) { 104 skips[sip] = b 105 } 106 107 // NoSkips gets rid of all the skips 108 func NoSkips() { 109 for k := range skips { 110 skips[k] = false 111 } 112 } 113 114 // thanks to my mentor 115 func knot(c rune, chars string) bool { 116 for _, v := range chars { 117 if c == v { 118 return false 119 } 120 } 121 return true 122 } 123 124 func getPrefix(s string) (float64, int, bool) { 125 var m float64 = 1 126 var o int = 0 127 128 // fmt.Printf("getPrefix: s=%q\n", s) 129 _, ok := SIsufixes["xxx"] // better way? 130 l := len(s) 131 if l > 1 { 132 if knot(rune(s[l-1]), "0123456789.") { 133 if l > 2 { 134 if knot(rune(s[l-2]), "0123456789.+-eE") { 135 o = 2 136 } else { 137 o = 1 138 } 139 } else { 140 o = 1 141 } 142 } 143 m, ok = SIsufixes[s[l-o:]] 144 // fmt.Printf("getPrefix: %q, m=%f, l=%d, o=%d, ok=%v\n", s[l-o:], m, l, o, ok) 145 } 146 return m, l - o, ok 147 } 148 149 // print integer format 150 func pif(val int64, units string, w, p int, okw, okp bool, order []string) string { 151 var sip string 152 153 //fmt.Printf("pif: %d\n", val) 154 sgn := "" 155 if val < 0 { 156 sgn = "-" 157 val = -val 158 } 159 if val == 0 { 160 p = 1 161 okp = true 162 } 163 164 //fs := fmt.Sprintf("%%s%%%d.%dd %%s%%s", w, p) 165 fs := "" 166 switch { 167 case okw == false && okp == false: 168 fs = fmt.Sprintf("%%s%%%d.%dd %%s%%s", 0, 1) 169 case okw == false && okp == true: 170 fs = fmt.Sprintf("%%s%%.%dd %%s%%s", p) 171 case okw == true && okp == false: 172 fs = fmt.Sprintf("%%s%%%d.d %%s%%s", w) 173 case okw == true && okp == true: 174 fs = fmt.Sprintf("%%s%%%d.%dd %%s%%s", w, p) 175 } 176 177 //fmt.Printf("sgn=%q, fs=%q\n", sgn, fs) 178 179 for _, sip = range order { 180 // fmt.Printf("Format: try %q, ", sip) 181 if skips[sip] { 182 continue 183 } 184 if (SIsufixes[sip] <= float64(val)) || (sip == "" && val == 0) { 185 break 186 } 187 } 188 //fmt.Printf("pif: sip=%q\n", sip) 189 val = val / int64(SIsufixes[sip]) 190 //fmt.Printf("pif: val=%d\n", val) 191 str := fmt.Sprintf(fs, sgn, val, sip, units) 192 if str[len(str)-1] == ' ' { 193 str = str[:len(str)-1] 194 } 195 return str 196 } 197 198 // print floating format 199 func pff(val float64, units string, w, p int, okw, okp bool, order []string) string { 200 var sip string 201 202 // fmt.Printf("pff: %f\n", val) 203 sgn := "" 204 if val < 0 { 205 sgn = "-" 206 val = -val 207 } 208 if val == 0 { 209 w = 1 210 } 211 212 fs := "" 213 switch { 214 case okw == false && okp == false: 215 fs = fmt.Sprintf("%%s%%%d.%df %%s%%s", 0, 0) 216 case okw == false && okp == true: 217 fs = fmt.Sprintf("%%s%%.%df %%s%%s", p) 218 case okw == true && okp == false: 219 fs = fmt.Sprintf("%%s%%%d.f %%s%%s", w) 220 case okw == true && okp == true: 221 fs = fmt.Sprintf("%%s%%%d.%df %%s%%s", w, p) 222 } 223 //fmt.Printf("sgn=%q, fs=%q\n", sgn, fs) 224 225 for _, sip = range order { 226 if skips[sip] { 227 continue 228 } 229 //fmt.Printf("pff: %q, %f <= %f\n", sip, SIsufixes[sip], val) 230 if SIsufixes[sip] == 1 { 231 if val == 0.0 || val == 1.0 { 232 break 233 } 234 //continue 235 } 236 if SIsufixes[sip] <= val { 237 break 238 } 239 } 240 //fmt.Printf("pff: val=%f, sip=%q\n", val, sip) 241 val = val / SIsufixes[sip] 242 str := fmt.Sprintf(fs, sgn, val, sip, units) 243 if str[len(str)-1] == ' ' { 244 str = str[:len(str)-1] 245 } 246 return str 247 } 248 249 // called to parse format descriptor 250 func i(v *Int64, s fmt.State, c rune) { 251 var val = int64(v.V) 252 var str string 253 var w, p int 254 var okw, okp bool 255 256 // not checking is OK because 0 is the default behavior 257 w, okw = s.Width() 258 p, okp = s.Precision() 259 //fmt.Printf("i: w=%d, okw=%v, p=%d, okp=%v\n", w, okw, p, okp) 260 //mi, pl, sh, sp, ze := s.Flag('-'), s.Flag('+'), s.Flag('#'), s.Flag(' '), s.Flag('0') 261 262 switch c { 263 case 'h': 264 str = pif(val, v.U, w, p, okw, okp, order) 265 case 'H': 266 str = pif(val, v.U, w, p, okw, okp, order2) 267 case 'd': 268 str = fmt.Sprintf("%d", val) 269 case 'D': 270 fs := fmt.Sprintf("%%%d.%dd", w, p) 271 tmp := fmt.Sprintf(fs, val) 272 str = "" 273 for k := range tmp { 274 c := string(tmp[len(tmp)-k-1]) 275 if c < `0` || c > `9` { 276 str = tmp[0:len(tmp)-k] + str 277 break 278 } 279 if k > 0 && k%3 == 0 { 280 str = "," + str 281 } 282 str = c + str 283 } 284 case 'v': 285 str = fmt.Sprintf("%v", val) 286 default: 287 // fmt.Printf("default\n") 288 str = fmt.Sprintf("%d", val) 289 } 290 b := []byte(str) 291 s.Write(b) 292 } 293 294 // called to parse format descriptor 295 func f(v *Float64, s fmt.State, c rune) { 296 var val = float64(v.V) 297 var str string 298 var w, p int 299 var okw, okp bool 300 301 w, okw = s.Width() 302 p, okp = s.Precision() 303 304 switch c { 305 case 'h': 306 str = pff(val, v.U, w, p, okw, okp, order) 307 case 'H': 308 str = pff(val, v.U, w, p, okw, okp, order2) 309 case 'd': 310 str = fmt.Sprintf("%d", int(val)) 311 case 'v': 312 str = fmt.Sprintf("%v", val) 313 default: 314 // fmt.Printf("default\n") 315 str = fmt.Sprintf("%d", int(val)) 316 } 317 b := []byte(str) 318 s.Write(b) 319 } 320 321 // FIX FIX FIX check ok or err? if no prefix we must convert anyway not err 322 func (r *Int64) Set(s string) error { 323 324 m, l, _ := getPrefix(s) 325 v, err := strconv.ParseInt(s[:l], 10, 64) 326 if err != nil { 327 return err 328 } 329 // fmt.Printf("Set: v=%d, m=%f, v*m=%v\n", v, m, v*int64(m)) 330 r.V = int64(v * int64(m)) 331 return err 332 } 333 334 func (r *Int) Set(s string) error { 335 m, l, _ := getPrefix(s) 336 v, err := strconv.ParseInt(s[:l], 10, 64) 337 if err != nil { 338 return err 339 } 340 // fmt.Printf("Set: v=%d, m=%f, v*m=%v\n", v, m, v*int64(m)) 341 r.V = int(v * int64(m)) 342 return err 343 } 344 345 func (r *Float64) Set(s string) error { 346 347 m, l, ok := getPrefix(s) 348 v, err := strconv.ParseFloat(s[:l], 64) 349 if !ok { 350 return err 351 } 352 // fmt.Printf("Set: v=%f, m=%f, v*m=%v\n", v, m, v*m) 353 r.V = float64(v * m) 354 return err 355 } 356 357 func (v Int64) String() string { 358 // fmt.Printf("String: I\n") 359 return fmt.Sprintf("%s", pif(v.V, v.U, 0, 0, true, true, order)) 360 } 361 362 func (v Int) String() string { 363 // fmt.Printf("String: I\n") 364 return fmt.Sprintf("%s", pif(int64(v.V), v.U, 0, 0, true, true, order)) 365 } 366 367 func (v Float64) String() string { 368 // fmt.Printf("String: F\n") 369 return fmt.Sprintf("%s", pff(v.V, v.U, 0, 0, true, true, order)) 370 } 371 372 func (v Int64) Format(s fmt.State, c rune) { 373 i(&v, s, c) 374 } 375 376 func (v Int) Format(s fmt.State, c rune) { 377 v2 := Int64{int64(v.V), v.U} 378 i(&v2, s, c) 379 } 380 381 func (v Float64) Format(s fmt.State, c rune) { 382 f(&v, s, c) 383 }