github.com/dmmcquay/sia@v1.3.1-0.20180712220038-9f8d535311b9/cmd/siac/parse.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  	"math/big"
     8  	"strings"
     9  
    10  	"github.com/NebulousLabs/Sia/types"
    11  )
    12  
    13  var errUnableToParseSize = errors.New("unable to parse size")
    14  
    15  // filesize returns a string that displays a filesize in human-readable units.
    16  func filesizeUnits(size int64) string {
    17  	if size == 0 {
    18  		return "0 B"
    19  	}
    20  	sizes := []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"}
    21  	i := int(math.Log10(float64(size)) / 3)
    22  	return fmt.Sprintf("%.*f %s", i, float64(size)/math.Pow10(3*i), sizes[i])
    23  }
    24  
    25  // parseFilesize converts strings of form 10GB to a size in bytes. Fractional
    26  // sizes are truncated at the byte size.
    27  func parseFilesize(strSize string) (string, error) {
    28  	units := []struct {
    29  		suffix     string
    30  		multiplier int64
    31  	}{
    32  		{"kb", 1e3},
    33  		{"mb", 1e6},
    34  		{"gb", 1e9},
    35  		{"tb", 1e12},
    36  		{"kib", 1 << 10},
    37  		{"mib", 1 << 20},
    38  		{"gib", 1 << 30},
    39  		{"tib", 1 << 40},
    40  		{"b", 1}, // must be after others else it'll match on them all
    41  	}
    42  
    43  	strSize = strings.ToLower(strSize)
    44  	for _, unit := range units {
    45  		if strings.HasSuffix(strSize, unit.suffix) {
    46  			r, ok := new(big.Rat).SetString(strings.TrimSuffix(strSize, unit.suffix))
    47  			if !ok {
    48  				return "", errUnableToParseSize
    49  			}
    50  			r.Mul(r, new(big.Rat).SetInt(big.NewInt(unit.multiplier)))
    51  			if !r.IsInt() {
    52  				f, _ := r.Float64()
    53  				return fmt.Sprintf("%d", int64(f)), nil
    54  			}
    55  			return r.RatString(), nil
    56  		}
    57  	}
    58  
    59  	return "", errUnableToParseSize
    60  }
    61  
    62  // periodUnits turns a period in terms of blocks to a number of weeks.
    63  func periodUnits(blocks types.BlockHeight) string {
    64  	return fmt.Sprint(blocks / 1008) // 1008 blocks per week
    65  }
    66  
    67  // parsePeriod converts a duration specified in blocks, hours, or weeks to a
    68  // number of blocks.
    69  func parsePeriod(period string) (string, error) {
    70  	units := []struct {
    71  		suffix     string
    72  		multiplier float64
    73  	}{
    74  		{"b", 1},        // blocks
    75  		{"block", 1},    // blocks
    76  		{"blocks", 1},   // blocks
    77  		{"h", 6},        // hours
    78  		{"hour", 6},     // hours
    79  		{"hours", 6},    // hours
    80  		{"d", 144},      // days
    81  		{"day", 144},    // days
    82  		{"days", 144},   // days
    83  		{"w", 1008},     // weeks
    84  		{"week", 1008},  // weeks
    85  		{"weeks", 1008}, // weeks
    86  	}
    87  
    88  	period = strings.ToLower(period)
    89  	for _, unit := range units {
    90  		if strings.HasSuffix(period, unit.suffix) {
    91  			var base float64
    92  			_, err := fmt.Sscan(strings.TrimSuffix(period, unit.suffix), &base)
    93  			if err != nil {
    94  				return "", errUnableToParseSize
    95  			}
    96  			blocks := int(base * unit.multiplier)
    97  			return fmt.Sprint(blocks), nil
    98  		}
    99  	}
   100  
   101  	return "", errUnableToParseSize
   102  }
   103  
   104  // currencyUnits converts a types.Currency to a string with human-readable
   105  // units. The unit used will be the largest unit that results in a value
   106  // greater than 1. The value is rounded to 4 significant digits.
   107  func currencyUnits(c types.Currency) string {
   108  	pico := types.SiacoinPrecision.Div64(1e12)
   109  	if c.Cmp(pico) < 0 {
   110  		return c.String() + " H"
   111  	}
   112  
   113  	// iterate until we find a unit greater than c
   114  	mag := pico
   115  	unit := ""
   116  	for _, unit = range []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"} {
   117  		if c.Cmp(mag.Mul64(1e3)) < 0 {
   118  			break
   119  		} else if unit != "TS" {
   120  			// don't want to perform this multiply on the last iter; that
   121  			// would give us 1.235 TS instead of 1235 TS
   122  			mag = mag.Mul64(1e3)
   123  		}
   124  	}
   125  
   126  	num := new(big.Rat).SetInt(c.Big())
   127  	denom := new(big.Rat).SetInt(mag.Big())
   128  	res, _ := new(big.Rat).Mul(num, denom.Inv(denom)).Float64()
   129  
   130  	return fmt.Sprintf("%.4g %s", res, unit)
   131  }
   132  
   133  // parseCurrency converts a siacoin amount to base units.
   134  func parseCurrency(amount string) (string, error) {
   135  	units := []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"}
   136  	for i, unit := range units {
   137  		if strings.HasSuffix(amount, unit) {
   138  			// scan into big.Rat
   139  			r, ok := new(big.Rat).SetString(strings.TrimSuffix(amount, unit))
   140  			if !ok {
   141  				return "", errors.New("malformed amount")
   142  			}
   143  			// convert units
   144  			exp := 24 + 3*(int64(i)-4)
   145  			mag := new(big.Int).Exp(big.NewInt(10), big.NewInt(exp), nil)
   146  			r.Mul(r, new(big.Rat).SetInt(mag))
   147  			// r must be an integer at this point
   148  			if !r.IsInt() {
   149  				return "", errors.New("non-integer number of hastings")
   150  			}
   151  			return r.RatString(), nil
   152  		}
   153  	}
   154  	// check for hastings separately
   155  	if strings.HasSuffix(amount, "H") {
   156  		return strings.TrimSuffix(amount, "H"), nil
   157  	}
   158  
   159  	return "", errors.New("amount is missing units; run 'wallet --help' for a list of units")
   160  }
   161  
   162  // yesNo returns "Yes" if b is true, and "No" if b is false.
   163  func yesNo(b bool) string {
   164  	if b {
   165  		return "Yes"
   166  	}
   167  	return "No"
   168  }