-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathnumber.go
209 lines (181 loc) · 4.99 KB
/
number.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
package gofn
import (
"fmt"
"strconv"
"strings"
"unsafe"
)
const (
base10 = 10
byte2Bits = 8
fractionSep = '.'
noFractionSep = byte(0)
groupSep = ','
)
func ParseIntEx[T IntExt](s string, base int) (T, error) {
var zeroT T
v, err := strconv.ParseInt(s, base, int(unsafe.Sizeof(zeroT)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v), nil
}
return zeroT, err // nolint: wrapcheck
}
func ParseInt[T IntExt](s string) (T, error) {
return ParseIntEx[T](s, base10)
}
// ParseIntUngroup omit all grouping commas then parse the string value
func ParseIntUngroup[T IntExt](s string) (T, error) {
return ParseIntEx[T](NumberFmtUngroup(s, groupSep), base10)
}
func ParseIntDef[T IntExt](s string, defaultVal T) T {
v, err := strconv.ParseInt(s, base10, int(unsafe.Sizeof(defaultVal)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v)
}
return defaultVal
}
func ParseUintEx[T UIntExt](s string, base int) (T, error) {
var zeroT T
v, err := strconv.ParseUint(s, base, int(unsafe.Sizeof(zeroT)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v), nil
}
return zeroT, err // nolint: wrapcheck
}
func ParseUint[T UIntExt](s string) (T, error) {
return ParseUintEx[T](s, base10)
}
// ParseUintUngroup omit all grouping commas then parse the string value
func ParseUintUngroup[T UIntExt](s string) (T, error) {
return ParseUintEx[T](NumberFmtUngroup(s, groupSep), base10)
}
func ParseUintDef[T UIntExt](s string, defaultVal T) T {
v, err := strconv.ParseUint(s, base10, int(unsafe.Sizeof(defaultVal)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v)
}
return defaultVal
}
func ParseFloat[T FloatExt](s string) (T, error) {
var zeroT T
v, err := strconv.ParseFloat(s, int(unsafe.Sizeof(zeroT)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v), nil
}
return zeroT, err // nolint: wrapcheck
}
// ParseFloatUngroup omit all grouping commas then parse the string value
func ParseFloatUngroup[T FloatExt](s string) (T, error) {
return ParseFloat[T](NumberFmtUngroup(s, groupSep))
}
func ParseFloatDef[T FloatExt](s string, defaultVal T) T {
v, err := strconv.ParseFloat(s, int(unsafe.Sizeof(defaultVal)*byte2Bits)) //nolint:gosec
if err == nil {
return T(v)
}
return defaultVal
}
func FormatIntEx[T IntExt](v T, format string) string {
return fmt.Sprintf(format, v)
}
func FormatInt[T IntExt](v T) string {
return strconv.FormatInt(int64(v), base10)
}
// FormatIntGroup format the value then group the decimal using comma
func FormatIntGroup[T IntExt](v T) string {
s := strconv.FormatInt(int64(v), base10)
return NumberFmtGroup(s, noFractionSep, groupSep)
}
func FormatUintEx[T UIntExt](v T, format string) string {
return fmt.Sprintf(format, v)
}
func FormatUint[T UIntExt](v T) string {
return strconv.FormatUint(uint64(v), base10)
}
// FormatUintGroup format the value then group the decimal using comma
func FormatUintGroup[T UIntExt](v T) string {
return NumberFmtGroup(strconv.FormatUint(uint64(v), base10), noFractionSep, groupSep)
}
func FormatFloatEx[T FloatExt](v T, format string) string {
return fmt.Sprintf(format, v)
}
func FormatFloat[T FloatExt](v T) string {
return fmt.Sprintf("%f", v)
}
// FormatFloatGroup format the value then group the decimal using comma
func FormatFloatGroup[T FloatExt](v T) string {
return NumberFmtGroup(fmt.Sprintf("%f", v), fractionSep, groupSep)
}
// FormatFloatGroupEx format the value then group the decimal using comma
func FormatFloatGroupEx[T FloatExt](v T, format string) string {
return NumberFmtGroup(fmt.Sprintf(format, v), fractionSep, groupSep)
}
// NumberFmtGroup separate decimal groups in the value string
func NumberFmtGroup(num string, fractionSep, groupSep byte) string {
if len(num) < 4 { //nolint:mnd
return num
}
// Format as integer
if fractionSep == 0 {
return numberPartFmtGroup(num, groupSep)
}
// Format as real number
fractionIndex := strings.IndexByte(num, fractionSep)
if fractionIndex >= 0 {
return numberPartFmtGroup(num[:fractionIndex], groupSep) + num[fractionIndex:]
}
return numberPartFmtGroup(num, groupSep)
}
// NumberFmtUngroup ungroup the value string
func NumberFmtUngroup(num string, groupSep byte) string {
ret := make([]byte, 0, len(num))
for i := range num {
if num[i] == groupSep {
continue
}
ret = append(ret, num[i])
}
return string(ret)
}
func numberPartFmtGroup(s string, groupSep byte) string {
if groupSep == 0 || !stringIsInteger(s, true) {
return s
}
buf := make([]byte, 0, len(s)+5) //nolint:mnd
ch := s[0]
if ch == '-' {
buf = append(buf, ch)
s = s[1:]
}
start := len(s) % 3 //nolint:mnd
if start == 0 {
start = 3
}
for i := range s {
ch = s[i]
if i != 0 && i == start {
buf = append(buf, groupSep)
start += 3
}
buf = append(buf, ch)
}
return string(buf)
}
func stringIsInteger(s string, allowSign bool) bool {
length := len(s)
if length == 0 {
return false
}
if s[0] == '-' {
if !allowSign || length == 1 {
return false
}
s = s[1:]
}
for _, ch := range s {
if ch < '0' || ch > '9' {
return false
}
}
return true
}