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  }