1
0
mirror of https://github.com/duke-git/lancet.git synced 2026-03-01 00:35:28 +08:00

Compare commits

...

5 Commits

Author SHA1 Message Date
dudaodong 921f218ef7 test: run test cases on rc branch 2024-10-18 16:40:20 +08:00
dudaodong 0a2cc9c928 doc: update doc for RandNumberOfLength and GetExeOrDllVersion 2024-10-18 16:38:39 +08:00
今晚打打 ed93aae970 feat:add GetExeDllVersion for fileutil,random,package (#257)
* 增加GetExeDllVersion函数获取exe,dll版本号

* 多打文字

* 定位忘记...

* 单独剥离出来一个为windows的,增加RandNumLen为取指定长度随机数.

* ....

* 增加test测试
2024-10-18 16:17:14 +08:00
BoWen Qiu 1671f7856a feat(netutil): add async http request for http client (#256)
* fix(algorithm): fix bug of lrucache in issue #251

* feat(netutil): add async send request for HttpClient
2024-10-18 15:14:52 +08:00
燕归来 1008dd4956 fix: fix compile error (#255) (#258) 2024-10-18 15:09:28 +08:00
16 changed files with 357 additions and 71 deletions
+49 -49
View File
@@ -1,51 +1,51 @@
-----BEGIN rsa private key----- -----BEGIN rsa private key-----
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
-----END rsa private key----- -----END rsa private key-----
+12 -12
View File
@@ -1,14 +1,14 @@
-----BEGIN rsa public key----- -----BEGIN rsa public key-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAs+rfCwwvBb9JplwDAaNt MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAsZoYOi7lxZ3zk4zTa/nv
pEPCm1DoS8PkqzwSSxfA7JHSMYdYUMq92vbSqEKomNgyksaGIiqDHop+9pHjYI1A lQzPeXNBthaUpAJmC1b2/ldmglZ8ARGoVv45VCjG1yKO47SiN8ijcA///o/EawGZ
ftd6mbZJwRtQxo+5eYGh88FzPqemmNubZk7SxGq0MEz1XmZ9viQyp3GxAJjeSdAG 8mJHv8WsPUBxzUF2OQHSZ/kHlbi1vZsbGXLcM9siBU79uqudbyqUm/9OkgnTPCUU
9GRdstNcczSy9Ij9YGJ8ehLhL/png4ZsLUtwDbYsilJDx28qDX/jJ/nbopaHWk2X zOlaFtKGeyoDM4TBAX7ec8G8LWBdBAgNAaK/vP+iXuPHxLdKNMDDel67nLNYJ30K
TSu8KK3zXBjppTU4uKKicOswaJ6kfk9J6ItkQ15uS+z2eHsxpa8K++gIt2swJkiE C/fUbwHixn5yQLx4zVLF+rv3pUbQuTeEvS+N19Cg1/+5/xzGK+YYdO1ZH4qgubNB
RlqUzwiC75w92JH1nQPXqEqaaEx0izdHUmFF33cgGb6gw5tJePzOmYs8KLJcOhfJ u9C90wH0FhIuDDfswh8liZbjHcXsJ82QPdaxjbz1bSb8OToackMMocvJVI9ToxSB
QcqjW4MjCfnaw4tB/cpi3zYKzDoC7mzwitrkBO1MG11luzR1m9uMfWczL2hK2zMH dGvGoBk1mnLJRSab6nOD56+iRoGJmy6GTpD2I1ie1PHV1Y2TIjUESnxjgPigaYA5
I51vayC1d1T0gc56vS/SAdicl5OqcD6oc6Dmym3rguF/pauTIOrvGtipeYk/Mbb4 i/MkXlD/1CtrDW6hsc0/WgCSD7ZGjNTduUwdwmd2es1XIqnamht+QJE1JTbBFLd9
qj2CI33QFTZc3G55yHVB6vDWIpMSWF60HHHqv7Ens4MIPIev0WS893vsNscg4Ph3 r5zXQY/5W5nRQHBmWP5fVHy7a/wmF9N2ZpI/DYMarCRIVBsiMdsE62mq63haB6yw
QxXDNjOc3kEK17TnDGXmL7C+Ykfq9brm32IrOMZ4KR3NJ+aO83yLewPoQAy8QCC3 jkVk+78TXtmXTTE8ts6XImt+m+0RY4lAd+UfHvyOe3dH2y5nGSHG4C8CXB264YdA
2b1O3c1nAgSY9sIwJV8ikAdNDBhLnkXFrYE2ZFl/Kl5HXd0LCz/BQu3Ey0RsZfXI pZZjjvVZy5K3lsmmBB+4Kje1Rux36F76qEOvjZ2QvpJDTXSXOk1POu6aZc8N+XRu
pD7KUcdNq3QRloTATntE9U0CAwEAAQ== vG7hmEPdF3xD68VPSZ5/gfcCAwEAAQ==
-----END rsa public key----- -----END rsa public key-----
+31
View File
@@ -52,6 +52,7 @@ import (
- [ReadFile](#ReadFile) - [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead) - [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead) - [ParallelChunkRead](#ParallelChunkRead)
- [GetExeOrDllVersion](#GetExeOrDllVersion)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1076,4 +1077,34 @@ func main() {
// Jim,21,male // Jim,21,male
// 2 // 2
} }
```
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
<p>返回exe,dll文件版本号(仅Window平台).</p>
<b>函数签名:</b>
```go
func GetExeOrDllVersion(filePath string) (string, error)
```
<b>示例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
if err != nil {
panic(err)
}
fmt.Println(v)
// Output:
// 3.9.10.19
}
``` ```
+26
View File
@@ -40,6 +40,7 @@ import (
- [RandStringSlice](#RandStringSlice) - [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool) - [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice) - [RandBoolSlice](#RandBoolSlice)
- [RandNumberOfLength](#RandNumberOfLength)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -522,4 +523,29 @@ func main() {
result := random.RandBoolSlice(2) result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random) fmt.Println(result) // [true false] (random)
} }
```
### <span id="RandNumberOfLength">RandNumberOfLength</span>
<p>生成指定长度的随机数</p>
<b>函数签名:</b>
```go
func RandNumberOfLength(len int) int
```
<b>实例:<span style="float:right;display:inline-block;">[运行](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
i := random.RandNumberOfLength(2)
fmt.Println(i) // 21 (random number of length 2)
}
``` ```
+34
View File
@@ -52,6 +52,7 @@ import (
- [ReadFile](#ReadFile) - [ReadFile](#ReadFile)
- [ChunkRead](#ChunkRead) - [ChunkRead](#ChunkRead)
- [ParallelChunkRead](#ParallelChunkRead) - [ParallelChunkRead](#ParallelChunkRead)
- [GetExeOrDllVersion](#GetExeOrDllVersion)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -1074,4 +1075,37 @@ func main() {
// Jim,21,male // Jim,21,male
// 2 // 2
} }
```
### <span id="GetExeOrDllVersion">GetExeOrDllVersion</span>
<p>Get the version of exe or dll file on windows.</p>
<b>Signature:</b>
```go
func GetExeOrDllVersion(filePath string) (string, error)
```
<b>Example:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/fileutil"
)
func main() {
v, err := fileutil.GetExeOrDllVersion(`C:\Program Files\Tencent\WeChat\WeChat.exe`)
if err != nil {
panic(err)
}
fmt.Println(v)
// Output:
// 3.9.10.19
}
``` ```
+27
View File
@@ -40,6 +40,7 @@ import (
- [RandStringSlice](#RandStringSlice) - [RandStringSlice](#RandStringSlice)
- [RandBool](#RandBool) - [RandBool](#RandBool)
- [RandBoolSlice](#RandBoolSlice) - [RandBoolSlice](#RandBoolSlice)
- [RandNumberOfLength](#RandNumberOfLength)
<div STYLE="page-break-after: always;"></div> <div STYLE="page-break-after: always;"></div>
@@ -523,4 +524,30 @@ func main() {
result := random.RandBoolSlice(2) result := random.RandBoolSlice(2)
fmt.Println(result) // [true false] (random) fmt.Println(result) // [true false] (random)
} }
```
### <span id="RandNumberOfLength">RandNumberOfLength</span>
<p>Generates a random int number of specified length.</p>
<b>Signature:</b>
```go
func RandNumberOfLength(len int) int
```
<b>Signature:<span style="float:right;display:inline-block;">[Run](todo)</span></b>
```go
package main
import (
"fmt"
"github.com/duke-git/lancet/v2/random"
)
func main() {
i := random.RandNumberOfLength(2)
fmt.Println(i) // 21 (random number of length 2)
}
``` ```
+1 -2
View File
@@ -14,6 +14,7 @@ import (
"encoding/csv" "encoding/csv"
"errors" "errors"
"fmt" "fmt"
"github.com/duke-git/lancet/v2/validator"
"io" "io"
"io/fs" "io/fs"
"net/http" "net/http"
@@ -23,8 +24,6 @@ import (
"sort" "sort"
"strings" "strings"
"sync" "sync"
"github.com/duke-git/lancet/v2/validator"
) )
// FileReader is a reader supporting offset seeking and reading one // FileReader is a reader supporting offset seeking and reading one
+83
View File
@@ -0,0 +1,83 @@
//go:build windows
package fileutil
import (
"fmt"
"syscall"
"unsafe"
)
// tagVS_FIXEDFILEINFO 参考结构体https://learn.microsoft.com/zh-cn/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
type tagVS_FIXEDFILEINFO struct {
Signature uint32
StructVersion uint32
FileVersionMS uint32
FileVersionLS uint32
ProductVersionMS uint32
ProductVersionLS uint32
FileFlagsMask uint32
FileFlags uint32
FileOS uint32
FileType uint32
FileSubtype uint32
FileDateMS uint32
FileDateLS uint32
}
// GetExeOrDllVersion get the version of exe or dll file on windows.
// Play: todo
func GetExeOrDllVersion(filePath string) (string, error) {
// 加载系统dll
versionDLL := syscall.NewLazyDLL("version.dll")
getFileVersionInfoSize := versionDLL.NewProc("GetFileVersionInfoSizeW")
getFileVersionInfo := versionDLL.NewProc("GetFileVersionInfoW")
verQueryValue := versionDLL.NewProc("VerQueryValueW")
// 转换路径为UTF-16
filePathPtr, err := syscall.UTF16PtrFromString(filePath)
if err != nil {
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
}
// 获取version信息大小
size, _, err := getFileVersionInfoSize.Call(
uintptr(unsafe.Pointer(filePathPtr)),
0,
)
if size == 0 {
return "", fmt.Errorf("unable to obtain version information size: %v", err)
}
// 加载version信息
data := make([]byte, size)
ret, _, err := getFileVersionInfo.Call(uintptr(unsafe.Pointer(filePathPtr)), 0, size, uintptr(unsafe.Pointer(&data[0])))
if ret == 0 {
return "", fmt.Errorf("unable to obtain version information: %v", err)
}
// 查询version信息
var fixedInfo *tagVS_FIXEDFILEINFO
var fixedInfoLen uint32
u16, err := syscall.UTF16PtrFromString(`\`)
if err != nil {
return "", fmt.Errorf("unable to convert file path to UTF-16: %w", err)
}
ret, _, err = verQueryValue.Call(
uintptr(unsafe.Pointer(&data[0])),
uintptr(unsafe.Pointer(u16)),
uintptr(unsafe.Pointer(&fixedInfo)),
uintptr(unsafe.Pointer(&fixedInfoLen)),
)
if ret == 0 {
return "", fmt.Errorf("unable to query version information: %v", err)
}
// 转换结构体
major := fixedInfo.FileVersionMS >> 16
minor := fixedInfo.FileVersionMS & 0xFFFF
build := fixedInfo.FileVersionLS >> 16
revision := fixedInfo.FileVersionLS & 0xFFFF
return fmt.Sprintf("%d.%d.%d.%d", major, minor, build, revision), nil
}
+13
View File
@@ -0,0 +1,13 @@
//go:build windows
package fileutil
import "testing"
func TestGetExeOrDllVersion(t *testing.T) {
v, err := GetExeOrDllVersion(`C:\Windows\System32\cmd.exe`)
if err != nil {
t.Error(err)
}
t.Log(v)
}
-2
View File
@@ -1,6 +1,4 @@
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a h1:4iLhBPcpqFmylhnkbY3W0ONLUYYkDAW9xMFLfxgsvCw=
golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20221208152030-732eee02a75a/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
golang.org/x/text v0.5.0 h1:OLmvp0KP+FVG99Ct/qFiL/Fhk4zp4QQnZ7b2U+5piUM=
golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+16
View File
@@ -221,6 +221,22 @@ func (client *HttpClient) SendRequest(request *HttpRequest) (*http.Response, err
return resp, nil return resp, nil
} }
// AsyncSendRequest send http request with goroutine, pop response and error to channels
func (client *HttpClient) AsyncSendRequest(request *HttpRequest, respChan chan *http.Response, errChan chan error) {
go func() {
defer func() {
if err := recover(); err != nil {
errChan <- fmt.Errorf("%v", err)
}
}()
resp, err := client.SendRequest(request)
if err != nil {
errChan <- err
}
respChan <- resp
}()
}
// DecodeResponse decode response into target object. // DecodeResponse decode response into target object.
// Play: https://go.dev/play/p/jUSgynekH7G // Play: https://go.dev/play/p/jUSgynekH7G
func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error { func (client *HttpClient) DecodeResponse(resp *http.Response, target any) error {
+39
View File
@@ -373,6 +373,45 @@ func TestSendRequestWithFilePath(t *testing.T) {
} }
} }
func TestHttpClient_AsyncSendRequest(t *testing.T) {
t.Parallel()
assert := internal.NewAssert(t, "TestHttpClient_Get")
request := &HttpRequest{
RawURL: "https://jsonplaceholder.typicode.com/todos/1",
Method: "GET",
}
httpClient := NewHttpClient()
respCh := make(chan *http.Response, 20)
errCh := make(chan error, 20)
for i := 0; i < 50; i++ {
httpClient.AsyncSendRequest(request, respCh, errCh)
}
for i := 0; i < 50; i++ {
select {
case resp := <-respCh:
type Todo struct {
UserId int `json:"userId"`
Id int `json:"id"`
Title string `json:"title"`
Completed bool `json:"completed"`
}
var todo Todo
err := httpClient.DecodeResponse(resp, &todo)
if err != nil {
t.FailNow()
}
assert.Equal(1, todo.Id)
case err := <-errCh:
if err != nil {
t.Log("net error: ", err.Error())
}
}
}
}
func TestProxy(t *testing.T) { func TestProxy(t *testing.T) {
config := &HttpClientConfig{ config := &HttpClientConfig{
HandshakeTimeout: 20 * time.Second, HandshakeTimeout: 20 * time.Second,
+10
View File
@@ -327,3 +327,13 @@ func UUIdV4() (string, error) {
return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil return fmt.Sprintf("%x-%x-%x-%x-%x", uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:]), nil
} }
// RandNumberOfLength 生成一个长度为len的随机数
// Play: todo
func RandNumberOfLength(len int) int {
m := int(math.Pow10(len) - 1)
i := int(math.Pow10(len - 1))
ret := rand.Intn(m-i+1) + i
return ret
}
+7
View File
@@ -3,6 +3,7 @@ package random
import ( import (
"reflect" "reflect"
"regexp" "regexp"
"strconv"
"testing" "testing"
"github.com/duke-git/lancet/v2/internal" "github.com/duke-git/lancet/v2/internal"
@@ -361,3 +362,9 @@ func TestRandBoolSlice(t *testing.T) {
} }
}) })
} }
func TestRandNumberOfLength(t *testing.T) {
t.Parallel()
randi := RandNumberOfLength(6)
assert := internal.NewAssert(t, "TestRandNumberOfLength")
assert.Equal(6, len(strconv.Itoa(randi)))
}
+2 -6
View File
@@ -195,18 +195,14 @@ func UniqueByConcurrent[T comparable](slice []T, comparator func(item T, other T
chunks = append(chunks, slice[i:end]) chunks = append(chunks, slice[i:end])
} }
type resultChunk struct { resultCh := make(chan resultChunk[T], numThreads)
index int
data []T
}
resultCh := make(chan resultChunk, numThreads)
var wg sync.WaitGroup var wg sync.WaitGroup
for i, chunk := range chunks { for i, chunk := range chunks {
wg.Add(1) wg.Add(1)
go func(index int, chunk []T) { go func(index int, chunk []T) {
defer wg.Done() defer wg.Done()
resultCh <- resultChunk{index, removeDuplicate(chunk, comparator)} resultCh <- resultChunk[T]{index, removeDuplicate(chunk, comparator)}
}(i, chunk) }(i, chunk)
} }
+7
View File
@@ -7,6 +7,13 @@ import (
"golang.org/x/exp/constraints" "golang.org/x/exp/constraints"
) )
// resultChunk is used to store the intermediate results of UniqueByConcurrent.
// It is defined separately to be compatible with versions of go up to 1.20.
type resultChunk[T comparable] struct {
index int
data []T
}
// sliceValue return the reflect value of a slice // sliceValue return the reflect value of a slice
func sliceValue(slice any) reflect.Value { func sliceValue(slice any) reflect.Value {
v := reflect.ValueOf(slice) v := reflect.ValueOf(slice)