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