/
util.go
145 lines (131 loc) · 3.57 KB
/
util.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
package base
import (
"errors"
"math/big"
"strconv"
"sync"
)
// This method will traverse the array concurrently and map each object in the array.
// @param list: [TYPE1], a list that all item is TYPE1
// @param limit: maximum number of tasks to execute, 0 means no limit
// @param maper: func(TYPE1) (TYPE2, error), a function that input TYPE1, return TYPE2
// you can throw an error to finish task.
// @return : [TYPE2], a list that all item is TYPE2
// @example : ```
// nums := []interface{}{1, 2, 3, 4, 5, 6}
// res, _ := MapListConcurrent(nums, func(i interface{}) (interface{}, error) {
// return strconv.Itoa(i.(int) * 100), nil
// })
// println(res) // ["100" "200" "300" "400" "500" "600"]
// ```
func MapListConcurrent(list []interface{}, limit int, maper func(interface{}) (interface{}, error)) ([]interface{}, error) {
thread := 0
max := limit
wg := sync.WaitGroup{}
mapContainer := newSafeMap()
var firstError error
for _, item := range list {
if firstError != nil {
continue
}
if max == 0 {
wg.Add(1)
// no limit
} else {
if thread == max {
wg.Wait()
thread = 0
}
if thread < max {
wg.Add(1)
}
}
go func(w *sync.WaitGroup, item interface{}, mapContainer *safeMap, firstError *error) {
maped, err := maper(item)
if *firstError == nil && err != nil {
*firstError = err
} else {
mapContainer.writeMap(item, maped)
}
wg.Done()
}(&wg, item, mapContainer, &firstError)
thread++
}
wg.Wait()
if firstError != nil {
return nil, firstError
}
result := []interface{}{}
for _, item := range list {
result = append(result, mapContainer.Map[item])
}
return result, nil
}
// The encapsulation of MapListConcurrent.
func MapListConcurrentStringToString(strList []string, maper func(string) (string, error)) ([]string, error) {
list := make([]interface{}, len(strList))
for i, s := range strList {
list[i] = s
}
temp, err := MapListConcurrent(list, 10, func(i interface{}) (interface{}, error) {
return maper(i.(string))
})
if err != nil {
return nil, err
}
result := make([]string, len(temp))
for i, v := range temp {
result[i] = v.(string)
}
return result, nil
}
// Return the more biger of the two numbers
func MaxBigInt(x, y *big.Int) *big.Int {
if x.Cmp(y) > 0 {
return x
} else {
return y
}
}
/* [zh] 该方法会捕捉 panic 抛出的值,并转成一个 error 对象通过参数指针返回
* 注意: 如果想要返回它抓住的 error, 必须使用命名返回值!!
* [en] This method will catch the value thrown by panic, and turn it into an error object and return it through the parameter pointer
* Note: If you want to return the error it caught, you must use a named return value! !
* ```
* func actionWillThrowError(parameters...) (namedErr error, other...) {
* defer CatchPanicAndMapToBasicError(&namedErr)
* // action code ...
* return namedErr, other...
* }
* ```
*/
func CatchPanicAndMapToBasicError(errOfResult *error) {
// first we have to recover()
errOfPanic := recover()
if errOfResult == nil {
return
}
if errOfPanic != nil {
*errOfResult = MapAnyToBasicError(errOfPanic)
} else {
*errOfResult = MapAnyToBasicError(*errOfResult)
}
}
func MapAnyToBasicError(e any) error {
if e == nil {
return nil
}
err, ok := e.(error)
if ok {
return errors.New(err.Error())
}
msg, ok := e.(string)
if ok {
return errors.New("panic error: " + msg)
}
code, ok := e.(int)
if ok {
return errors.New("panic error: code = " + strconv.Itoa(code))
}
return errors.New("panic error: unexpected error.")
}