Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}