Search Apps Documentation Source Content File Folder Download Copy Actions Download

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}