Skip to content

Commit 1c5f42b

Browse files
committed
Add safe conversion source code
1 parent b7f050b commit 1c5f42b

File tree

5 files changed

+894
-33
lines changed

5 files changed

+894
-33
lines changed

examples/example.go

Lines changed: 0 additions & 15 deletions
This file was deleted.

go.mod

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
11
module github.com/bsv-blockchain/go-safe-conversion
22

33
go 1.24
4-
5-
require github.com/stretchr/testify v1.10.0
6-
7-
require (
8-
github.com/davecgh/go-spew v1.1.1 // indirect
9-
github.com/pmezard/go-difflib v1.0.0 // indirect
10-
gopkg.in/yaml.v3 v3.0.1 // indirect
11-
)

go.sum

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +0,0 @@
1-
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2-
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
4-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
5-
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
6-
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
7-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
8-
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
9-
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
10-
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

safe_conversion.go

Lines changed: 247 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,247 @@
1+
package safe
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"math/big"
7+
"time"
8+
)
9+
10+
const (
11+
MinInt16 = -1 << 15 // -32768
12+
MaxInt16 = 1<<15 - 1 // 32767
13+
)
14+
15+
// IntToUint32 converts an int to uint32 after ensuring it’s in range.
16+
// Returns an error if the input is negative or exceeds the maximum value of a uint32.
17+
func IntToUint32(v int) (uint32, error) {
18+
if v < 0 || v > math.MaxUint32 {
19+
return 0, fmt.Errorf("value %d out of range", v)
20+
}
21+
22+
return uint32(v), nil
23+
}
24+
25+
// Uint64ToUint32 converts a uint64 value to a uint32 value after ensuring it fits into 32 bits.
26+
// Returns an error if the input value is too large.
27+
func Uint64ToUint32(v uint64) (uint32, error) {
28+
// ^uint32(0) is the maximum value of a uint32 (all bits set).
29+
if v > uint64(math.MaxUint32) {
30+
return 0, fmt.Errorf("value %d overflows uint32 (max %d)", v, math.MaxUint32)
31+
}
32+
33+
return uint32(v), nil
34+
}
35+
36+
// Int64ToUint64 safely converts an int64 to uint64.
37+
// Returns an error if the input is negative.
38+
func Int64ToUint64(value int64) (uint64, error) {
39+
if value < 0 {
40+
return 0, fmt.Errorf("negative value cannot be converted to uint64: %d", value)
41+
}
42+
43+
return uint64(value), nil
44+
}
45+
46+
// IntToUint64 safely converts an int to uint64.
47+
// Returns an error if the input is negative.
48+
func IntToUint64(value int) (uint64, error) {
49+
if value < 0 {
50+
return 0, fmt.Errorf("negative value cannot be converted to uint64: %d", value)
51+
}
52+
53+
return uint64(value), nil
54+
}
55+
56+
// Uint64ToInt safely converts a uint64 to int.
57+
// Returns an error if the value exceeds the limits of an int.
58+
func Uint64ToInt(value uint64) (int, error) {
59+
if value > math.MaxInt {
60+
return 0, fmt.Errorf("value exceeds int limit: %d", value)
61+
}
62+
63+
return int(value), nil
64+
}
65+
66+
// Int64ToInt32 safely converts an int64 to int32.
67+
// Returns an error if the value is outside the range of int32.
68+
func Int64ToInt32(value int64) (int32, error) {
69+
if value < math.MinInt32 || value > math.MaxInt32 {
70+
return 0, fmt.Errorf("value out of int32 range: %d", value)
71+
}
72+
73+
return int32(value), nil
74+
}
75+
76+
// IntToInt32 safely converts an int to int32.
77+
// Checks if the value is within the valid int32 range.
78+
func IntToInt32(value int) (int32, error) {
79+
if value < math.MinInt32 || value > math.MaxInt32 {
80+
return 0, fmt.Errorf("value out of int32 range: %d", value)
81+
}
82+
83+
return int32(value), nil
84+
}
85+
86+
// Int32ToUint32 safely converts an int32 to uint32.
87+
// Checks only for negative values, as positive int32 values are always within uint32 range.
88+
func Int32ToUint32(value int32) (uint32, error) {
89+
if value < 0 {
90+
return 0, fmt.Errorf("negative value cannot be converted to uint32: %d", value)
91+
}
92+
93+
return uint32(value), nil
94+
}
95+
96+
// Int64ToUint32 safely converts an int64 to uint32.
97+
// Checks if the value is non-negative and within the uint32 range.
98+
func Int64ToUint32(value int64) (uint32, error) {
99+
if value < 0 {
100+
return 0, fmt.Errorf("negative value cannot be converted to uint32: %d", value)
101+
}
102+
103+
if value > math.MaxUint32 {
104+
return 0, fmt.Errorf("value exceeds uint32 range: %d", value)
105+
}
106+
107+
return uint32(value), nil
108+
}
109+
110+
// BigWordToUint32 safely converts a big.Word to uint32.
111+
// It ensures that the value is within the valid uint32 range before conversion.
112+
//
113+
// The function explicitly converts `big.Word` to `uint64` first to avoid the G115
114+
// lint warning (integer overflow conversion). This is necessary because `big.Word`
115+
// can be either `uint32` (on 32-bit systems) or `uint64` (on 64-bit systems).
116+
// Without this explicit conversion, the linter assumes a potential risk when
117+
// directly converting from `big.Word` to `uint32`.
118+
func BigWordToUint32(value big.Word) (uint32, error) {
119+
valueUint64 := uint64(value)
120+
121+
if valueUint64 > math.MaxUint32 {
122+
return 0, fmt.Errorf("big.Word exceeds uint32 range: %d", valueUint64)
123+
}
124+
125+
return uint32(valueUint64), nil
126+
}
127+
128+
// IntToUint16 safely converts an int to uint16.
129+
// Checks if the value is non-negative and within the uint16 range.
130+
func IntToUint16(value int) (uint16, error) {
131+
if value < 0 {
132+
return 0, fmt.Errorf("negative value cannot be converted to uint16: %d", value)
133+
}
134+
135+
if value > math.MaxUint16 {
136+
return 0, fmt.Errorf("value exceeds uint16 range: %d", value)
137+
}
138+
139+
return uint16(value), nil
140+
}
141+
142+
// IntToInt16 safely converts an int to int16.
143+
// Checks if the value is within the valid int16 range.
144+
func IntToInt16(value int) (int16, error) {
145+
if value < MinInt16 || value > MaxInt16 {
146+
return 0, fmt.Errorf("value out of int16 range: %d", value)
147+
}
148+
149+
return int16(value), nil
150+
}
151+
152+
// UintToUint32 safely converts a uint to uint32.
153+
// Checks if the value exceeds the uint32 range.
154+
func UintToUint32(value uint) (uint32, error) {
155+
if value > math.MaxUint32 {
156+
return 0, fmt.Errorf("value exceeds uint32 range: %d", value)
157+
}
158+
159+
return uint32(value), nil
160+
}
161+
162+
// TimeToUint32 safely converts a time.Time's Unix timestamp to uint32.
163+
// Checks if the timestamp is non-negative and within the uint32 range.
164+
func TimeToUint32(value time.Time) (uint32, error) {
165+
timestamp := value.Unix()
166+
if timestamp < 0 {
167+
return 0, fmt.Errorf("negative timestamp cannot be converted to uint32: %d", timestamp)
168+
}
169+
170+
if timestamp > math.MaxUint32 {
171+
return 0, fmt.Errorf("timestamp exceeds uint32 range: %d", timestamp)
172+
}
173+
174+
return uint32(timestamp), nil
175+
}
176+
177+
// Uint32ToUint8 safely converts a uint32 to uint8.
178+
// Checks if the value exceeds the uint8 range.
179+
func Uint32ToUint8(value uint32) (uint8, error) {
180+
if value > math.MaxUint8 {
181+
return 0, fmt.Errorf("value exceeds uint8 range: %d", value)
182+
}
183+
184+
return uint8(value), nil
185+
}
186+
187+
// UintptrToInt safely converts a uintptr to int.
188+
// Checks if the value exceeds the maximum int range.
189+
func UintptrToInt(value uintptr) (int, error) {
190+
if value > uintptr(math.MaxInt) {
191+
return 0, fmt.Errorf("value exceeds int range: %d", value)
192+
}
193+
194+
return int(value), nil
195+
}
196+
197+
// Uint64ToInt64 safely converts a uint64 to int64.
198+
// Checks if the value exceeds the maximum int64 range.
199+
func Uint64ToInt64(value uint64) (int64, error) {
200+
if value > math.MaxInt64 {
201+
return 0, fmt.Errorf("value exceeds int64 range: %d", value)
202+
}
203+
204+
return int64(value), nil
205+
}
206+
207+
// Uint32ToInt32 safely converts a uint32 to int32.
208+
// Checks if the value exceeds the maximum int32 range.
209+
func Uint32ToInt32(value uint32) (int32, error) {
210+
if value > math.MaxInt32 {
211+
return 0, fmt.Errorf("value exceeds int32 range: %d", value)
212+
}
213+
214+
return int32(value), nil
215+
}
216+
217+
// Uint64ToInt32 safely converts a uint64 to int32.
218+
// Checks if the value exceeds the int32 range or if it's negative.
219+
func Uint64ToInt32(value uint64) (int32, error) {
220+
if value > math.MaxInt32 {
221+
return 0, fmt.Errorf("value exceeds int32 range: %d", value)
222+
}
223+
224+
return int32(value), nil
225+
}
226+
227+
// Uint32ToInt64 safely converts a uint32 to int64.
228+
// Since all uint32 values are within the valid int64 range, the conversion is always safe.
229+
func Uint32ToInt64(value uint32) (int64, error) {
230+
return int64(value), nil
231+
}
232+
233+
// Uint32ToUint64 safely converts a uint32 to uint64.
234+
// Since all uint32 values fit within the uint64 range, the conversion is always safe.
235+
func Uint32ToUint64(value uint32) (uint64, error) {
236+
return uint64(value), nil
237+
}
238+
239+
// Uint64ToUint16 safely converts a uint64 to uint16.
240+
// Checks if the value exceeds the uint16 range.
241+
func Uint64ToUint16(value uint64) (uint16, error) {
242+
if value > math.MaxUint16 {
243+
return 0, fmt.Errorf("value exceeds uint16 range: %d", value)
244+
}
245+
246+
return uint16(value), nil
247+
}

0 commit comments

Comments
 (0)