gitee.com/sy_183/go-common@v1.0.5-0.20231205030221-958cfe129b47/unit/size.go (about)

     1  package unit
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	sliceUnsafe "gitee.com/sy_183/go-common/slice/unsafe"
     7  	stringUnsafe "gitee.com/sy_183/go-common/strings/unsafe"
     8  	"gopkg.in/yaml.v3"
     9  	"reflect"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  type UnknownSizeUnitError struct {
    15  	Unit string
    16  }
    17  
    18  func (e *UnknownSizeUnitError) Error() string {
    19  	if e == nil {
    20  		return "<nil>"
    21  	}
    22  	return fmt.Sprintf("unknown size unit '%s'", e.Unit)
    23  }
    24  
    25  const (
    26  	Bit      = 0.125
    27  	BitDiv   = 8
    28  	Byte     = 1
    29  	KiloByte = 1000
    30  	KiBiByte = 1 << 10
    31  	KiloBit  = KiloByte / 8
    32  	KiBiBit  = KiBiByte / 8
    33  	MegaByte = 1000 * 1000
    34  	MeBiByte = 1 << 20
    35  	MegaBit  = MegaByte / 8
    36  	MeBiBit  = MeBiByte / 8
    37  	GigaByte = 1000 * 1000 * 1000
    38  	GiBiByte = 1 << 30
    39  	GigaBit  = GigaByte / 8
    40  	GiBiBit  = GiBiByte / 8
    41  	TeraByte = 1000 * 1000 * 1000 * 1000
    42  	TeBiByte = 1 << 40
    43  	TeraBit  = TeraByte / 8
    44  	TeBiBit  = TeBiByte / 8
    45  	PetaByte = 1000 * 1000 * 1000 * 1000 * 1000
    46  	PeBiByte = 1 << 50
    47  	PetaBit  = PetaByte / 8
    48  	PeBiBit  = PeBiByte / 8
    49  	ExaByte  = 1000 * 1000 * 1000 * 1000 * 1000 * 1000
    50  	EbiByte  = 1 << 60
    51  	ExaBit   = ExaByte / 8
    52  	EbiBit   = EbiByte / 8
    53  )
    54  
    55  type Size uint64
    56  
    57  func (s Size) Uint64() uint64 {
    58  	return uint64(s)
    59  }
    60  
    61  func (s Size) Uint() uint {
    62  	return uint(s)
    63  }
    64  
    65  func (s Size) Int64() int64 {
    66  	return int64(s)
    67  }
    68  
    69  func (s Size) Int() int {
    70  	return int(s)
    71  }
    72  
    73  func (s Size) Float64() float64 {
    74  	return float64(s)
    75  }
    76  
    77  func (s *Size) UnmarshalText(text []byte) error {
    78  	text = bytes.TrimSpace(text)
    79  	if len(text) == 0 {
    80  		return nil
    81  	}
    82  	ns := sliceUnsafe.String(text)
    83  	nsu := strings.ToUpper(ns)
    84  	us, usu := "", ""
    85  	i := strings.IndexAny(nsu, "BKMGTPE")
    86  	if i >= 0 {
    87  		us = strings.TrimSpace(ns[i:])
    88  		usu = strings.TrimSpace(nsu[i:])
    89  		ns = strings.TrimSpace(nsu[:i])
    90  	}
    91  	mul := uint64(1)
    92  	div := uint64(1)
    93  	if usu != "" {
    94  		switch usu[0] {
    95  		case 'B':
    96  			switch usu[1:] {
    97  			case "", "YTE":
    98  			case "IT":
    99  				div = 8
   100  			default:
   101  				return &UnknownSizeUnitError{Unit: us}
   102  			}
   103  		case 'K':
   104  			switch usu[1:] {
   105  			case "", "B", "BYTE", "ILOBYTE":
   106  				mul = 1000
   107  			case "IB", "IBYTE", "IBIBYTE":
   108  				mul = 1 << 10
   109  			case "BIT", "ILOBIT":
   110  				mul, div = 1000, 8
   111  			case "IBIT", "IBIBIT":
   112  				mul, div = 1<<10, 8
   113  			default:
   114  				return &UnknownSizeUnitError{Unit: us}
   115  			}
   116  		case 'M':
   117  			switch usu[1:] {
   118  			case "", "B", "BYTE", "EGABYTE":
   119  				mul = 1000 * 1000
   120  			case "IB", "IBYTE", "EBIBYTE":
   121  				mul = 1 << 20
   122  			case "BIT", "EGABIT":
   123  				mul, div = 1000*1000, 8
   124  			case "IBIT", "EBIBIT":
   125  				mul, div = 1<<20, 8
   126  			default:
   127  				return &UnknownSizeUnitError{Unit: us}
   128  			}
   129  		case 'G':
   130  			switch usu[1:] {
   131  			case "", "B", "BYTE", "IGABYTE":
   132  				mul = 1000 * 1000 * 1000
   133  			case "IB", "IBYTE", "IBIBYTE":
   134  				mul = 1 << 30
   135  			case "BIT", "IGABIT":
   136  				mul, div = 1000*1000*1000, 8
   137  			case "IBIT", "IBIBIT":
   138  				mul, div = 1<<20, 8
   139  			default:
   140  				return &UnknownSizeUnitError{Unit: us}
   141  			}
   142  		case 'T':
   143  			switch usu[1:] {
   144  			case "", "B", "BYTE", "ERABYTE":
   145  				mul = 1000 * 1000 * 1000 * 1000
   146  			case "IB", "IBYTE", "EBIBYTE":
   147  				mul = 1 << 40
   148  			case "BIT", "ERABIT":
   149  				mul, div = 1000*1000*1000*1000, 8
   150  			case "IBIT", "EBIBIT":
   151  				mul, div = 1<<40, 8
   152  			default:
   153  				return &UnknownSizeUnitError{Unit: us}
   154  			}
   155  		case 'P':
   156  			switch usu[1:] {
   157  			case "", "B", "BYTE", "ETABYTE":
   158  				mul = 1000 * 1000 * 1000 * 1000 * 1000
   159  			case "IB", "IBYTE", "EBIBYTE":
   160  				mul = 1 << 50
   161  			case "BIT", "ETABIT":
   162  				mul, div = 1000*1000*1000*1000*1000, 8
   163  			case "IBIT", "EBIBIT":
   164  				mul, div = 1<<50, 8
   165  			default:
   166  				return &UnknownSizeUnitError{Unit: us}
   167  			}
   168  		case 'E':
   169  			switch usu[1:] {
   170  			case "", "B", "BYTE", "XABYTE":
   171  				mul = 1000 * 1000 * 1000 * 1000 * 1000 * 1000
   172  			case "IB", "IBYTE", "BIBYTE":
   173  				mul = 1 << 60
   174  			case "BIT", "XABIT":
   175  				mul, div = 1000*1000*1000*1000*1000*1000, 8
   176  			case "IBIT", "BIBIT":
   177  				mul, div = 1<<60, 8
   178  			default:
   179  				return &UnknownSizeUnitError{Unit: us}
   180  			}
   181  		default:
   182  			panic("impossible")
   183  		}
   184  	}
   185  	n, err := strconv.ParseUint(ns, 10, 64)
   186  	if err != nil {
   187  		f, err := strconv.ParseFloat(ns, 64)
   188  		if err != nil {
   189  			return err
   190  		}
   191  		*s = (Size)(f * float64(mul) / float64(div))
   192  		return nil
   193  	}
   194  	*s = (Size)(n * mul / div)
   195  	return nil
   196  }
   197  
   198  func (s *Size) yamlTypeError(value *yaml.Node, err error) error {
   199  	v := value.Value
   200  	if value.Tag != "!!seq" && value.Tag != "!!map" {
   201  		if len(v) > 10 {
   202  			v = " `" + v[:7] + "...`"
   203  		} else {
   204  			v = " `" + v + "`"
   205  		}
   206  	}
   207  	return &yaml.TypeError{Errors: []string{
   208  		fmt.Sprintf("line %d: cannot unmarshal %s%s into %s, cause: %s", value.Line, value.Tag, v, reflect.TypeOf(s).Elem(), err.Error()),
   209  	}}
   210  }
   211  
   212  func (s *Size) UnmarshalYAML(value *yaml.Node) error {
   213  	if value.Kind == yaml.ScalarNode {
   214  		if err := s.UnmarshalText(stringUnsafe.Bytes(value.Value)); err != nil {
   215  			return s.yamlTypeError(value, err)
   216  		}
   217  		return nil
   218  	}
   219  	return s.yamlTypeError(value, nil)
   220  }
   221  
   222  func (s *Size) UnmarshalJSON(bytes []byte) error {
   223  	return s.UnmarshalText(bytes)
   224  }
   225  
   226  func (s Size) String() string {
   227  	switch {
   228  	case s < KiloByte:
   229  		return fmt.Sprintf("%dB", s)
   230  	case s < MegaByte:
   231  		return fmt.Sprintf("%fKB", float64(s)/KiloByte)
   232  	case s < GigaByte:
   233  		return fmt.Sprintf("%fMB", float64(s)/MegaByte)
   234  	case s < TeraByte:
   235  		return fmt.Sprintf("%fGB", float64(s)/GigaByte)
   236  	case s < PetaByte:
   237  		return fmt.Sprintf("%fTB", float64(s)/TeraByte)
   238  	case s < ExaByte:
   239  		return fmt.Sprintf("%fPB", float64(s)/PetaByte)
   240  	default:
   241  		return fmt.Sprintf("%fEB", float64(s)/ExaByte)
   242  	}
   243  }