Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-daemon/pkg/quota/size.go
2499 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package quota
6
7
import (
8
"encoding/json"
9
"errors"
10
"fmt"
11
"regexp"
12
"strconv"
13
14
"golang.org/x/xerrors"
15
)
16
17
// Size denotes a filesize / amount of bytes
18
type Size int64
19
20
const (
21
// Byte is a single byte
22
Byte Size = 1
23
// Kilobyte is 1024 bytes
24
Kilobyte Size = 1024 * Byte
25
// Megabyte is 1024 kilobytes
26
Megabyte Size = 1024 * Kilobyte
27
// Gigabyte is 1024 megabytes
28
Gigabyte Size = 1024 * Megabyte
29
// Terabyte is 1024 gigabyte
30
Terabyte Size = 1024 * Gigabyte
31
)
32
33
var (
34
sizeRegexp = regexp.MustCompile(`^(\d+)(k|m|g|t)?$`)
35
36
// ErrInvalidSize is returned by ParseSize if input was not a valid size
37
ErrInvalidSize = errors.New("invalid size")
38
)
39
40
// ParseSize parses a number of bytes (e.g. 10) into a size.
41
// ParseSize supports units: k(ilobyte), m(egabyte), (g)igatebyte, (t)erabyte. E.g. 50m or 200g
42
func ParseSize(q string) (Size, error) {
43
matches := sizeRegexp.FindSubmatch([]byte(q))
44
if len(matches) == 0 {
45
return 0, ErrInvalidSize
46
}
47
48
val, err := strconv.ParseUint(string(matches[1]), 10, 64)
49
if err != nil {
50
return 0, ErrInvalidSize
51
}
52
size := Size(val)
53
54
unit := string(matches[2])
55
switch unit {
56
case "":
57
size = size * Byte
58
case "k":
59
size = size * Kilobyte
60
case "m":
61
size = size * Megabyte
62
case "g":
63
size = size * Gigabyte
64
case "t":
65
size = size * Terabyte
66
}
67
68
return size, nil
69
}
70
71
// String converts the size to a string
72
func (s Size) String() string {
73
if s == 0 {
74
return "0"
75
}
76
77
steps := []struct {
78
Prefix string
79
Q Size
80
}{
81
{"t", Terabyte},
82
{"g", Gigabyte},
83
{"m", Megabyte},
84
{"k", Kilobyte},
85
}
86
for _, stp := range steps {
87
if s%stp.Q == 0 {
88
return fmt.Sprintf("%d%s", s/stp.Q, stp.Prefix)
89
}
90
}
91
return fmt.Sprintf("%d", s)
92
}
93
94
// MarshalJSON marshals a duration to JSON
95
func (s Size) MarshalJSON() ([]byte, error) {
96
return json.Marshal(s.String())
97
}
98
99
// UnmarshalJSON unmarshals a duration from JSON
100
func (s *Size) UnmarshalJSON(b []byte) error {
101
var v interface{}
102
if err := json.Unmarshal(b, &v); err != nil {
103
return err
104
}
105
switch value := v.(type) {
106
case string:
107
if value == "" {
108
*s = 0
109
return nil
110
}
111
112
tmp, err := ParseSize(value)
113
if err != nil {
114
return err
115
}
116
*s = tmp
117
return nil
118
default:
119
return xerrors.Errorf("invalid size: %s", string(b))
120
}
121
}
122
123