github.com/avahowell/sia@v0.5.1-beta.0.20160524050156-83dcc3d37c94/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  		{"", 1},  // no suffix is still a valid suffix
    42  	}
    43  
    44  	strSize = strings.ToLower(strSize)
    45  	for _, unit := range units {
    46  		if strings.HasSuffix(strSize, unit.suffix) {
    47  			r, ok := new(big.Rat).SetString(strings.TrimSuffix(strSize, unit.suffix))
    48  			if !ok {
    49  				return "", errUnableToParseSize
    50  			}
    51  			r.Mul(r, new(big.Rat).SetInt(big.NewInt(unit.multiplier)))
    52  			if !r.IsInt() {
    53  				f, _ := r.Float64()
    54  				return fmt.Sprintf("%d", int64(f)), nil
    55  			}
    56  			return r.RatString(), nil
    57  		}
    58  	}
    59  
    60  	return "", errUnableToParseSize
    61  }
    62  
    63  // parsePeriod converts a number of weeks to a number of blocks.
    64  func parsePeriod(period string) (string, error) {
    65  	var weeks float64
    66  	_, err := fmt.Sscan(period, &weeks)
    67  	if err != nil {
    68  		return "", errUnableToParseSize
    69  	}
    70  	blocks := int(weeks * 1008) // 1008 blocks per week
    71  	return fmt.Sprint(blocks), nil
    72  }
    73  
    74  // currencyUnits converts a types.Currency to a string with human-readable
    75  // units. The unit used will be the largest unit that results in a value
    76  // greater than 1. The value is rounded to 4 significant digits.
    77  func currencyUnits(c types.Currency) string {
    78  	pico := types.SiacoinPrecision.Div64(1e12)
    79  	if c.Cmp(pico) < 0 {
    80  		return c.String() + " H"
    81  	}
    82  
    83  	// iterate until we find a unit greater than c
    84  	mag := pico
    85  	unit := ""
    86  	for _, unit = range []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"} {
    87  		if c.Cmp(mag.Mul64(1e3)) < 0 {
    88  			break
    89  		} else if unit != "TS" {
    90  			// don't want to perform this multiply on the last iter; that
    91  			// would give us 1.235 TS instead of 1235 TS
    92  			mag = mag.Mul64(1e3)
    93  		}
    94  	}
    95  
    96  	num := new(big.Rat).SetInt(c.Big())
    97  	denom := new(big.Rat).SetInt(mag.Big())
    98  	res, _ := new(big.Rat).Mul(num, denom.Inv(denom)).Float64()
    99  
   100  	return fmt.Sprintf("%.4g %s", res, unit)
   101  }
   102  
   103  // parseCurrency converts a siacoin amount to base units.
   104  func parseCurrency(amount string) (string, error) {
   105  	units := []string{"pS", "nS", "uS", "mS", "SC", "KS", "MS", "GS", "TS"}
   106  	for i, unit := range units {
   107  		if strings.HasSuffix(amount, unit) {
   108  			// scan into big.Rat
   109  			r, ok := new(big.Rat).SetString(strings.TrimSuffix(amount, unit))
   110  			if !ok {
   111  				return "", errors.New("malformed amount")
   112  			}
   113  			// convert units
   114  			exp := 24 + 3*(int64(i)-4)
   115  			mag := new(big.Int).Exp(big.NewInt(10), big.NewInt(exp), nil)
   116  			r.Mul(r, new(big.Rat).SetInt(mag))
   117  			// r must be an integer at this point
   118  			if !r.IsInt() {
   119  				return "", errors.New("non-integer number of hastings")
   120  			}
   121  			return r.RatString(), nil
   122  		}
   123  	}
   124  	// check for hastings separately
   125  	if strings.HasSuffix(amount, "H") {
   126  		return strings.TrimSuffix(amount, "H"), nil
   127  	}
   128  
   129  	return "", errors.New("amount is missing units; run 'wallet --help' for a list of units")
   130  }