utils.gno
4.50 Kb ยท 190 lines
1package v1
2
3import (
4 "encoding/base64"
5 "math"
6 "strconv"
7 "strings"
8
9 "gno.land/p/nt/ufmt"
10)
11
12// b64Encode encodes a string to base64.
13func b64Encode(data string) string {
14 return base64.StdEncoding.EncodeToString([]byte(data))
15}
16
17// formatInt formats an integer to a string.
18func formatInt(v any) string {
19 switch v := v.(type) {
20 case int8:
21 return strconv.FormatInt(int64(v), 10)
22 case int32:
23 return strconv.FormatInt(int64(v), 10)
24 case int64:
25 return strconv.FormatInt(v, 10)
26 default:
27 panic(ufmt.Sprintf("invalid type: %T", v))
28 }
29}
30
31// formatBool formats a boolean to a string.
32func formatBool(v bool) string {
33 return strconv.FormatBool(v)
34}
35
36// numberKind represents the type of number to parse.
37type numberKind int
38
39const (
40 kindInt numberKind = iota
41 kindInt64
42 kindUint64
43)
44
45// parseNumber parses a string to a number (int, int64, or uint64) with proper validation.
46func parseNumber(s string, kind numberKind) any {
47 if len(strings.TrimSpace(s)) == 0 {
48 panic(ufmt.Sprint("invalid number value: empty or whitespace string"))
49 }
50
51 switch kind {
52 case kindInt:
53 num, err := strconv.ParseInt(s, 10, 64)
54 if err != nil {
55 panic(ufmt.Sprintf("invalid int value: %s", s))
56 }
57 return int(num)
58 case kindInt64:
59 num, err := strconv.ParseInt(s, 10, 64)
60 if err != nil {
61 panic(ufmt.Sprintf("invalid int64 value: %s", s))
62 }
63 return num
64 case kindUint64:
65 num, err := strconv.ParseUint(s, 10, 64)
66 if err != nil {
67 panic(ufmt.Sprintf("invalid uint64 value: %s", s))
68 }
69 return num
70 default:
71 panic(ufmt.Sprintf("unsupported number kind: %v", kind))
72 }
73}
74
75// parseBool parses a string to a boolean.
76func parseBool(s string) bool {
77 if len(strings.TrimSpace(s)) == 0 {
78 panic(ufmt.Sprint("invalid bool value: empty or whitespace string"))
79 }
80
81 switch s {
82 case "true":
83 return true
84 case "false":
85 return false
86 default:
87 panic(ufmt.Sprintf("invalid bool value: %s", s))
88 }
89}
90
91// parseInt parses a string to int with proper validation and overflow checking.
92func parseInt(s string) int {
93 if len(strings.TrimSpace(s)) == 0 {
94 panic(ufmt.Sprint("invalid int value: empty or whitespace string"))
95 }
96
97 num, err := strconv.ParseInt(s, 10, 64)
98 if err != nil {
99 panic(ufmt.Sprintf("invalid int value: %s", s))
100 }
101
102 const maxInt = int(^uint(0) >> 1)
103 const minInt = -maxInt - 1
104
105 if num > int64(maxInt) || num < int64(minInt) {
106 panic(ufmt.Sprintf("int overflow: value %d exceeds int range [%d, %d]", num, minInt, maxInt))
107 }
108
109 return int(num)
110}
111
112// parseInt64 parses a string to int64 with proper validation.
113func parseInt64(s string) int64 {
114 if len(strings.TrimSpace(s)) == 0 {
115 panic(ufmt.Sprint("invalid int64 value: empty or whitespace string"))
116 }
117
118 num, err := strconv.ParseInt(s, 10, 64)
119 if err != nil {
120 panic(ufmt.Sprintf("invalid int64 value: %s", s))
121 }
122
123 return num
124}
125
126// parseUint64 parses a string to uint64 with proper validation.
127func parseUint64(s string) uint64 {
128 if len(strings.TrimSpace(s)) == 0 {
129 panic(ufmt.Sprint("invalid uint64 value: empty or whitespace string"))
130 }
131
132 num, err := strconv.ParseUint(s, 10, 64)
133 if err != nil {
134 panic(ufmt.Sprintf("invalid uint64 value: %s", s))
135 }
136
137 return num
138}
139
140// safeAddInt64 performs safe addition of int64 values, panicking on overflow or underflow
141func safeAddInt64(a, b int64) int64 {
142 if a > 0 && b > math.MaxInt64-a {
143 panic("int64 addition overflow")
144 }
145 if a < 0 && b < math.MinInt64-a {
146 panic("int64 addition underflow")
147 }
148 return a + b
149}
150
151// safeSubInt64 performs safe subtraction of int64 values, panicking on overflow or underflow
152func safeSubInt64(a, b int64) int64 {
153 if b > 0 && a < math.MinInt64+b {
154 panic("int64 subtraction underflow")
155 }
156 if b < 0 && a > math.MaxInt64+b {
157 panic("int64 subtraction overflow")
158 }
159 return a - b
160}
161
162// safeMulDiv performs safe multiplication and division: (a * b) / c
163// Prevents overflow in multiplication step by checking bounds
164func safeMulDiv(a, b, c int64) int64 {
165 if c == 0 {
166 panic("division by zero in safeMulDiv")
167 }
168
169 // Check for multiplication overflow
170 // If a * b would overflow, we need to be careful
171 if a != 0 && b != 0 {
172 // Check if multiplication would overflow
173 if a > 0 && b > 0 && a > math.MaxInt64/b {
174 panic("int64 multiplication overflow in safeMulDiv")
175 }
176 if a > 0 && b < 0 && b < math.MinInt64/a {
177 panic("int64 multiplication underflow in safeMulDiv")
178 }
179 if a < 0 && b > 0 && a < math.MinInt64/b {
180 panic("int64 multiplication underflow in safeMulDiv")
181 }
182 if a < 0 && b < 0 && a < math.MaxInt64/b {
183 panic("int64 multiplication overflow in safeMulDiv")
184 }
185 }
186
187 result := (a * b) / c
188
189 return result
190}