github.com/anonymouse64/snapd@v0.0.0-20210824153203-04c4c42d842d/gadget/quantity/size.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package quantity 21 22 import ( 23 "errors" 24 "fmt" 25 "math" 26 27 "github.com/snapcore/snapd/strutil" 28 ) 29 30 // Size describes the size in bytes. 31 type Size uint64 32 33 const ( 34 // SizeKiB is the byte size of one kibibyte (2^10 = 1024 bytes) 35 SizeKiB = Size(1 << 10) 36 // SizeMiB is the size of one mebibyte (2^20) 37 SizeMiB = Size(1 << 20) 38 // SizeGiB is the size of one gibibyte (2^30) 39 SizeGiB = Size(1 << 30) 40 ) 41 42 func (s *Size) String() string { 43 if s == nil { 44 return "unspecified" 45 } 46 return fmt.Sprintf("%d", *s) 47 } 48 49 // iecSizeString formats the size using multiples from IEC units (i.e. 50 // kibibytes, mebibytes), that is as multiples of 1024. Printed values are 51 // truncated to 2 decimal points. 52 func iecSizeString(sz int64) string { 53 maxFloat := float64(1023.5) 54 r := float64(sz) 55 unit := "B" 56 for _, rangeUnit := range []string{"KiB", "MiB", "GiB", "TiB", "PiB"} { 57 if r < maxFloat { 58 break 59 } 60 r /= 1024 61 unit = rangeUnit 62 } 63 precision := 0 64 if math.Floor(r) != r { 65 precision = 2 66 } 67 return fmt.Sprintf("%.*f %s", precision, r, unit) 68 } 69 70 // IECString formats the size using multiples from IEC units (i.e. kibibytes, 71 // mebibytes), that is as multiples of 1024. Printed values are truncated to 2 72 // decimal points. 73 func (s *Size) IECString() string { 74 return iecSizeString(int64(*s)) 75 } 76 77 func (s *Size) UnmarshalYAML(unmarshal func(interface{}) error) error { 78 var gs string 79 if err := unmarshal(&gs); err != nil { 80 return errors.New(`cannot unmarshal gadget size`) 81 } 82 83 var err error 84 *s, err = ParseSize(gs) 85 if err != nil { 86 return fmt.Errorf("cannot parse size %q: %v", gs, err) 87 } 88 return err 89 } 90 91 // parseSizeOrOffset parses a string expressing size or offset in a gadget 92 // specific format. 93 func parseSizeOrOffset(szOrOffs string) (int64, error) { 94 number, unit, err := strutil.SplitUnit(szOrOffs) 95 if err != nil { 96 return 0, err 97 } 98 switch unit { 99 case "M": 100 // MiB 101 number = number * int64(SizeMiB) 102 case "G": 103 // GiB 104 number = number * int64(SizeGiB) 105 case "": 106 // straight bytes 107 108 default: 109 return 0, fmt.Errorf("invalid suffix %q", unit) 110 } 111 return number, nil 112 } 113 114 // ParseSize parses a string expressing size in a gadget specific format. The 115 // accepted format is one of: <bytes> | <bytes/2^20>M | <bytes/2^30>G. 116 func ParseSize(gs string) (Size, error) { 117 sz, err := parseSizeOrOffset(gs) 118 if sz < 0 { 119 return 0, errors.New("size cannot be negative") 120 } 121 return Size(sz), err 122 }