├── testdata ├── .gitkeep ├── 诗经.txt ├── diglett.png ├── gopher10th-small.jpg ├── rsa │ ├── public_key1024.pem │ ├── public_key2048.pem │ ├── public_key2048_PKCS8.pem │ ├── private_key1024.pem │ ├── private_key2048.pem │ └── private_key2048_PKCS8.pem └── jetbrains.svg ├── kgo_test.go ├── .gitignore ├── os_unix.go ├── file_windows_test.go ├── go.mod ├── docs ├── README.md ├── todo.md └── changelog.md ├── os_darwin_nocgo.go ├── os_darwin_cgo_test.go ├── os_darwin_nocgo_test.go ├── file_unix.go ├── file_windows.go ├── os_darwin_cgo.go ├── os_unix_test.go ├── .github └── workflows │ └── go.yml ├── file_unix_test.go ├── README.md ├── os_darwin_test.go ├── function_test.go ├── os_linux_test.go ├── debug.go ├── go.sum ├── os_windows_test.go ├── os_darwin.go ├── debug_test.go ├── os_linux.go ├── convert.go ├── os_windows.go ├── time.go ├── LICENSE ├── os_test.go ├── time_test.go ├── os.go └── number.go /testdata/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /testdata/诗经.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakuilan/kgo/HEAD/testdata/诗经.txt -------------------------------------------------------------------------------- /testdata/diglett.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakuilan/kgo/HEAD/testdata/diglett.png -------------------------------------------------------------------------------- /testdata/gopher10th-small.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kakuilan/kgo/HEAD/testdata/gopher10th-small.jpg -------------------------------------------------------------------------------- /kgo_test.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestVersion(t *testing.T) { 9 | v := Version 10 | assert.NotEmpty(t, v) 11 | } 12 | -------------------------------------------------------------------------------- /testdata/rsa/public_key1024.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDRQmvptUQ6zGtg16T489dlwIOt 3 | V+KI0Sar1J1dtnP69jqRqnkkRXmbI62XlSC/OsD2AswRY5nfkMUxN9dHqHzvhdaF 4 | 8VChfBfGrVM4jBMjUpJFoGubE4mxxeC9fEPm0QKpp/4QG37BCGwAG1CjUkHUk1I8 5 | NHfAXvdd8YnfmXpnKwIDAQAB 6 | -----END PUBLIC KEY----- 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #dir 2 | .DS_Store 3 | .buildpath 4 | .git 5 | .gitee 6 | .idea 7 | .project 8 | .settings 9 | .user.ini 10 | .vscode 11 | vendor/ 12 | 13 | #file 14 | *.bin 15 | *.cgo*.c 16 | *.cgo*.go 17 | *.dll 18 | *.dylib 19 | *.exe 20 | *.exe~ 21 | *.log 22 | *.orig 23 | *.out 24 | *.patch 25 | *.pid 26 | *.pyc 27 | *.rej 28 | *.so 29 | *.test 30 | *~ 31 | .*.swp 32 | ._* 33 | .nfs.* 34 | _cgo_* 35 | _obj 36 | _test 37 | core 38 | thumbs.db 39 | -------------------------------------------------------------------------------- /os_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | // +build linux darwin 3 | 4 | package kgo 5 | 6 | import ( 7 | "os" 8 | "syscall" 9 | ) 10 | 11 | // IsProcessExists 进程是否存在. 12 | func (ko *LkkOS) IsProcessExists(pid int) (res bool) { 13 | if pid > 0 { 14 | process, err := os.FindProcess(pid) 15 | if err == nil { 16 | if err = process.Signal(os.Signal(syscall.Signal(0))); err == nil { 17 | res = true 18 | } 19 | } 20 | } 21 | 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /testdata/rsa/public_key2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvEWbAtjL1gP9jsfZzpFn 3 | fqBmO5udoVN6XvxF+e+h0vaQ3Tw2VAyGf3IA7IMwBh00cTxiGkjdxKorWVr2QuLD 4 | Qzw+mC2dtusreJXMh9mzyr2KaM+5N6TbAnCroXNxEGLuJG68XTp960AalnkV0ICf 5 | FM6286seGDzK6nmWt8IxS9edfsBRvzkZT1AHgI5OcM7lND75mGXJjZiosoeLZTj/ 6 | tmN2OsbPe3diB94nIJ+Znw9YDpOjDtoaHHGDOBMuDaWFUfp1yF/3ZxteaxbJwtv9 7 | YVxQVQLxHg0KIl0TUGcnsZwrsw9GZQVjmkNj3kf5hQG4ognJa7HgANGclx1EU811 8 | iQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /testdata/rsa/public_key2048_PKCS8.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PUBLIC KEY----- 2 | MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyFrsDmGjjKcqMNsaQKO8 3 | Ie1hnE+DsB8ikJ5aMMFEHSqDh7x3DowAkSTfTjmftevxKaN4ZPVn12B+U5MP0JmB 4 | 45pJD3V6GBaLcsjw01ReB7xmejNRyGPyzwBe2QxFsORV2BQSGCMMefMVNEDJMa/e 5 | 8/9yULrhQ58PnuYW+HIP5ynk6giV4rPnA9xTkjuIaDvjutKSR0DaJr14uCqB+RRo 6 | FxLqU+RZEKWmWPlovVCXzB1JYjuWGHWT3zKk86dIJnLGB3XAF7b0kInRJt9Fg+Qv 7 | bweLNJI0amVNcFrdjRo0RH4xEe2HIPxZklZQ2S9KzjmzNdbOwuSi6TnZ/Yy0Ty2r 8 | sQIDAQAB 9 | -----END PUBLIC KEY----- 10 | -------------------------------------------------------------------------------- /file_windows_test.go: -------------------------------------------------------------------------------- 1 | // +build windows 2 | 3 | package kgo 4 | 5 | import ( 6 | "github.com/stretchr/testify/assert" 7 | "testing" 8 | ) 9 | 10 | func TestFileWins_IsReadable_Deny(t *testing.T) { 11 | var res bool 12 | res = KFile.IsReadable(admDir) 13 | assert.False(t, res) 14 | } 15 | 16 | func TestFileWins_IsWritable_Deny(t *testing.T) { 17 | var res bool 18 | res = KFile.IsWritable(admDir) 19 | assert.False(t, res) 20 | } 21 | 22 | func TestFileWins_IsExecutable_Deny(t *testing.T) { 23 | var res bool 24 | res = KFile.IsExecutable(admDir) 25 | assert.False(t, res) 26 | } 27 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kakuilan/kgo 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/yusufpapurcu/wmi v1.2.2 7 | github.com/brianvoe/gofakeit/v6 v6.16.0 8 | github.com/go-ole/go-ole v1.2.6 // indirect 9 | github.com/json-iterator/go v1.1.12 10 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 11 | github.com/stretchr/testify v1.7.1 12 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e 13 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b 14 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f 15 | golang.org/x/text v0.3.8 16 | gopkg.in/yaml.v3 v3.0.1 // indirect 17 | ) 18 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | ### 本地文档 2 | 3 | ```sh 4 | godoc -http=:6060 5 | 6 | #查看 7 | http://192.168.1.1:6060/pkg/github.com/kakuilan 8 | ``` 9 | 10 | ### 生成markdown 11 | 12 | ```sh 13 | go install github.com/robertkrimen/godocdown/godocdown@latest 14 | godocdown . > docs/v0.5.1.md 15 | ``` 16 | 17 | ### 安装依赖 18 | 19 | ```shell 20 | go get github.com/yusufpapurcu/wmi@v1.2.2 21 | go get github.com/brianvoe/gofakeit/v6 22 | go get github.com/json-iterator/go 23 | go get github.com/stretchr/testify 24 | go get golang.org/x/crypto 25 | go get golang.org/x/net 26 | go get golang.org/x/sys 27 | go get golang.org/x/text 28 | go get gopkg.in/yaml.v3 29 | ``` -------------------------------------------------------------------------------- /os_darwin_nocgo.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | // +build !cgo 3 | 4 | package kgo 5 | 6 | import ( 7 | "fmt" 8 | "os/exec" 9 | "strings" 10 | ) 11 | 12 | // CpuUsage 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数). 13 | // user为用户态(用户进程)的运行时间, 14 | // idle为空闲时间, 15 | // total为累计时间. 16 | func (ko *LkkOS) CpuUsage() (user, idle, total uint64) { 17 | //CPU counters for darwin is unavailable without cgo 18 | return 19 | } 20 | 21 | // getProcessPathByPid 根据PID获取进程的执行路径. 22 | func getProcessPathByPid(pid int) (res string) { 23 | lsof, err := exec.LookPath("lsof") 24 | if err != nil { 25 | return "" 26 | } 27 | command := fmt.Sprintf("%s -p %d -Fpfn", lsof, pid) 28 | _, out, _ := KOS.System(command) 29 | txtFound := 0 30 | lines := strings.Split(string(out), "\n") 31 | for i := 1; i < len(lines); i++ { 32 | if lines[i] == "ftxt" { 33 | txtFound++ 34 | if txtFound == 2 { 35 | return lines[i-1][1:] 36 | } 37 | } 38 | } 39 | 40 | return "" 41 | } 42 | -------------------------------------------------------------------------------- /testdata/rsa/private_key1024.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIICXgIBAAKBgQDRQmvptUQ6zGtg16T489dlwIOtV+KI0Sar1J1dtnP69jqRqnkk 3 | RXmbI62XlSC/OsD2AswRY5nfkMUxN9dHqHzvhdaF8VChfBfGrVM4jBMjUpJFoGub 4 | E4mxxeC9fEPm0QKpp/4QG37BCGwAG1CjUkHUk1I8NHfAXvdd8YnfmXpnKwIDAQAB 5 | AoGBAKTpc6H6+IpeD0GCsMCBan8F+L/jQuQP8Cn6pQn1f/GHmyCw+EddeTiKJYQQ 6 | Qn5yDx/RlIEfvWQy8zSWjVIrtkigh+wZjWFPi7VDhP5j/+W0ea7g+SETFgLq1AxS 7 | UFdUnEF7MEO43SjuI9DGTB8G2bffl+GEuaWFz8OXWVqhJdoBAkEA7Pu6Cq9kUx+n 8 | alJ+h1vJ37inrSoTq/59JiKJ53i/pXYfLus+nDfKlBk1U/hcCm2hQG0SCEjmpyKO 9 | hT5ehUq21QJBAOINLCvRKkzo3xtg1gxWjEMcYEbODJP/1cUPxaGoXoBPNQX94w7R 10 | GuERcwQb0iF7ERjD2cXm8sgQFacnujTbpf8CQAygjRUsq0fSk/USWDOWLHeDO0v6 11 | xfmQIdL6XCa9RFjopli1qCxivKSPg0vqG91iYgkwxpkyaTfWm4l1/UtU49ECQQCx 12 | 1pkkk7bKsP4tRN7A8CoC9kUMDzi4CrK8CO97gcFvAmF9qELEul+CJ78rwVNsgaxu 13 | pk4istHX8UeHrOqCgTczAkEAzFP75f6RObb/YfPGWRUZC0d+4POXwJRXUelxB0yA 14 | 5zA7BJ5kV9g8Cr7+RVtGEcyNd+KgGpHqfdcgpRVNRuGksg== 15 | -----END RSA PRIVATE KEY----- 16 | -------------------------------------------------------------------------------- /os_darwin_cgo_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | // +build cgo 3 | 4 | package kgo 5 | 6 | import ( 7 | "github.com/stretchr/testify/assert" 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestOS_Darwin_Cgo_CpuUsage(t *testing.T) { 13 | var user, idle, total uint64 14 | user, idle, total = KOS.CpuUsage() 15 | assert.Greater(t, int(user), 0) 16 | assert.Greater(t, int(idle), 0) 17 | assert.Greater(t, int(total), 0) 18 | } 19 | 20 | func BenchmarkOS_Darwin_Cgo_CpuUsage(b *testing.B) { 21 | b.ResetTimer() 22 | for i := 0; i < b.N; i++ { 23 | _, _, _ = KOS.CpuUsage() 24 | } 25 | } 26 | 27 | func TestOS_Darwin_Cgo_GetProcessExecPath(t *testing.T) { 28 | var res string 29 | 30 | pid := os.Getpid() 31 | res = KOS.GetProcessExecPath(pid) 32 | assert.NotEmpty(t, res) 33 | } 34 | 35 | func BenchmarkOS_Darwin_Cgo_GetProcessExecPath(b *testing.B) { 36 | b.ResetTimer() 37 | pid := os.Getpid() 38 | for i := 0; i < b.N; i++ { 39 | KOS.GetProcessExecPath(pid) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /os_darwin_nocgo_test.go: -------------------------------------------------------------------------------- 1 | // +build darwin 2 | // +build !cgo 3 | 4 | package kgo 5 | 6 | import ( 7 | "github.com/stretchr/testify/assert" 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestOS_Darwin_Nocgo_CpuUsage(t *testing.T) { 13 | var user, idle, total uint64 14 | user, idle, total = KOS.CpuUsage() 15 | assert.Equal(t, int(user), 0) 16 | assert.Equal(t, int(idle), 0) 17 | assert.Equal(t, int(total), 0) 18 | } 19 | 20 | func BenchmarkOS_Darwin_Nocgo_CpuUsage(b *testing.B) { 21 | b.ResetTimer() 22 | for i := 0; i < b.N; i++ { 23 | _, _, _ = KOS.CpuUsage() 24 | } 25 | } 26 | 27 | func TestOS_Darwin_Nocgo_GetProcessExecPath(t *testing.T) { 28 | var res string 29 | 30 | pid := os.Getpid() 31 | res = KOS.GetProcessExecPath(pid) 32 | assert.NotEmpty(t, res) 33 | } 34 | 35 | func BenchmarkOS_Darwin_Nocgo_GetProcessExecPath(b *testing.B) { 36 | b.ResetTimer() 37 | pid := os.Getpid() 38 | for i := 0; i < b.N; i++ { 39 | KOS.GetProcessExecPath(pid) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /file_unix.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | // +build linux darwin 3 | 4 | package kgo 5 | 6 | import ( 7 | "golang.org/x/sys/unix" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // IsReadable 路径是否可读. 13 | func (kf *LkkFile) IsReadable(fpath string) bool { 14 | err := unix.Access(fpath, unix.R_OK) 15 | return err == nil 16 | } 17 | 18 | // IsWritable 路径是否可写. 19 | func (kf *LkkFile) IsWritable(fpath string) bool { 20 | err := unix.Access(fpath, unix.W_OK) 21 | return err == nil 22 | } 23 | 24 | // IsExecutable 是否可执行文件. 25 | func (kf *LkkFile) IsExecutable(fpath string) bool { 26 | err := unix.Access(fpath, unix.X_OK) 27 | return err == nil 28 | } 29 | 30 | // FormatPath 格式化路径. 31 | func (kf *LkkFile) FormatPath(fpath string) string { 32 | if fpath == "" { 33 | return "" 34 | } 35 | 36 | fpath = formatPath(fpath) 37 | dir := filepath.Dir(fpath) 38 | 39 | if dir == `.` { 40 | return fpath 41 | } 42 | 43 | return strings.TrimRight(dir, "/") + "/" + filepath.Base(fpath) 44 | } 45 | -------------------------------------------------------------------------------- /file_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package kgo 5 | 6 | import ( 7 | "os" 8 | "path/filepath" 9 | "strings" 10 | ) 11 | 12 | // IsReadable 路径是否可读. 13 | func (kf *LkkFile) IsReadable(fpath string) bool { 14 | info, err := os.Stat(fpath) 15 | 16 | return err == nil && info.Mode().Perm()&(1<<(uint(8))) != 0 17 | } 18 | 19 | // IsWritable 路径是否可写. 20 | func (kf *LkkFile) IsWritable(fpath string) bool { 21 | info, err := os.Stat(fpath) 22 | 23 | return err == nil && info.Mode().Perm()&(1<<(uint(7))) != 0 24 | } 25 | 26 | // IsExecutable 是否可执行文件. 27 | func (kf *LkkFile) IsExecutable(fpath string) bool { 28 | info, err := os.Stat(fpath) 29 | 30 | return err == nil && info.Mode().IsRegular() && (info.Mode()&0111) != 0 31 | } 32 | 33 | // FormatPath 格式化路径. 34 | func (kf *LkkFile) FormatPath(fpath string) string { 35 | if fpath == "" { 36 | return "" 37 | } 38 | 39 | fpath = formatPath(fpath) 40 | dir := formatPath(filepath.Dir(fpath)) 41 | 42 | if dir == `.` { 43 | return fpath 44 | } 45 | 46 | return strings.TrimRight(dir, "/") + "/" + filepath.Base(fpath) 47 | } 48 | -------------------------------------------------------------------------------- /os_darwin_cgo.go: -------------------------------------------------------------------------------- 1 | //go:build darwin && cgo 2 | // +build darwin,cgo 3 | 4 | package kgo 5 | 6 | // #include 7 | // #include 8 | // #include 9 | // #include 10 | import "C" 11 | import ( 12 | "unsafe" 13 | ) 14 | 15 | // 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数). 16 | // user为用户态(用户进程)的运行时间, 17 | // idle为空闲时间, 18 | // total为累计时间. 19 | func (ko *LkkOS) CpuUsage() (user, idle, total uint64) { 20 | var cpuLoad C.host_cpu_load_info_data_t 21 | var count C.mach_msg_type_number_t = C.HOST_CPU_LOAD_INFO_COUNT 22 | ret := C.host_statistics(C.host_t(C.mach_host_self()), C.HOST_CPU_LOAD_INFO, C.host_info_t(unsafe.Pointer(&cpuLoad)), &count) 23 | if ret == C.KERN_SUCCESS { 24 | user = uint64(cpuLoad.cpu_ticks[C.CPU_STATE_USER]) 25 | idle = uint64(cpuLoad.cpu_ticks[C.CPU_STATE_IDLE]) 26 | total = user + idle + uint64(cpuLoad.cpu_ticks[C.CPU_STATE_SYSTEM]) + uint64(cpuLoad.cpu_ticks[C.CPU_STATE_NICE]) 27 | } 28 | 29 | return 30 | } 31 | 32 | // getProcessPathByPid 根据PID获取进程的执行路径. 33 | func getProcessPathByPid(pid int) (res string) { 34 | var c C.char // need a var for unsafe.Sizeof need a var 35 | const bufsize = C.PROC_PIDPATHINFO_MAXSIZE * unsafe.Sizeof(c) 36 | buffer := (*C.char)(C.malloc(C.size_t(bufsize))) 37 | defer C.free(unsafe.Pointer(buffer)) 38 | 39 | ret, err := C.proc_pidpath(C.int(pid), unsafe.Pointer(buffer), C.uint32_t(bufsize)) 40 | if err == nil && ret > 0 { 41 | res = C.GoString(buffer) 42 | } 43 | 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /testdata/rsa/private_key2048.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | MIIEogIBAAKCAQEAvEWbAtjL1gP9jsfZzpFnfqBmO5udoVN6XvxF+e+h0vaQ3Tw2 3 | VAyGf3IA7IMwBh00cTxiGkjdxKorWVr2QuLDQzw+mC2dtusreJXMh9mzyr2KaM+5 4 | N6TbAnCroXNxEGLuJG68XTp960AalnkV0ICfFM6286seGDzK6nmWt8IxS9edfsBR 5 | vzkZT1AHgI5OcM7lND75mGXJjZiosoeLZTj/tmN2OsbPe3diB94nIJ+Znw9YDpOj 6 | DtoaHHGDOBMuDaWFUfp1yF/3ZxteaxbJwtv9YVxQVQLxHg0KIl0TUGcnsZwrsw9G 7 | ZQVjmkNj3kf5hQG4ognJa7HgANGclx1EU811iQIDAQABAoIBAHY7L/FFvBwWPXEg 8 | yAMVBO1cRVdBjmf1SG/F9RvUBMe615ZXbbaydvQp37KnX803IevKg8EF6V7LjpV6 9 | rhOq2/ypJJXkb5qjbJU5XUwixumWxgi0ChiLio2pu96T5Hjp56sxb1EMnm7RvuCo 10 | Aa8CVuR2PCYhW7DU3IkK+j9D0DlThC1Yw/DW+kAUokC11Fu9Hg1qzQ3tvQVetwt0 11 | +N8YG2Q+1xnVKFlJPj34NlyVvByhbDsZf6YNtkxTg1wHbdf8T3silwFvzAQ7cvei 12 | cc1CothR0kI3jgqEdGRSM+zHihmSn0LrjxdzszKLu5P8+vD1NXFBGAKAv9yxHrmq 13 | +YamT1ECgYEA+ZpGdmH1xgvrXFYsSar4PSJZXvP0yGgwLCnQRidTR0bMdcHDj5zu 14 | mSzMpW2ggOpN+Tu/MbMneo+PDNj0GSh1dfhd+l06bxfh7y4NsoMkyvccVuBFWRiT 15 | 1guOPTuz+cCva7LnLQRAx6id1D+Qgk4xPfVUvz1yQ/OQ2YgDk3i0Hh8CgYEAwRjr 16 | TlRDR/khbQEt9giVuyNIikqBG+YQG1XorpZXzxmpfhFITi98q6aBK5TQts9LNJjr 17 | 0FTfNSvv7MhmdBGJfEHjB/iHDoRflP0OzfOTxAy34nDfbiiju1ZJuMCnynwtzvgm 18 | /epkWmsfQ+8UIQO1PpjDA9apr+w0eXGeSVDvp1cCgYAX7eVxdgTE/uGKkaxfR6fS 19 | 3Jcq1kKScAKmST/xadW/EJeLdo8UFpx9TOE1vuPTWCMvkL3Muvs+8cJqHeaQ/uL7 20 | 81q3JPb0LZr4fjbL9WyylKw/2/JqD0QlEiiJ0E551DI9JgKpuy2mtpCjB5EIHQz1 21 | fq6oQvDQeOpbIIIPQlQqSwKBgCotckBiwWt4LGft6Qcjquj8dPzX90mLwBrocfGo 22 | nzd6aH5D8iI51yZ7MR2+3LQBpXCwslmIIFUx6q8yusZBimFc1uVH0OBH48TcXZ54 23 | xN7+1C+e7f7W26Sw3VuAoWXmJ+9cXs/5ob/qRCMHlsL7V1x766Tr/5aQpomcWrGy 24 | /xXzAoGAIGADrejz5Ge/poTvebi3u4+z4eV/4k75eICqH1bXsds7WNJBvLaoYm3l 25 | JfLge2vpAUZduDnnEzBnHz4Vd7VkY0B8hzPxUhioBDV5FmzUsFpP5v3YSJWXDH9f 26 | JpRRxVih57S+ljO9q07Xt0C2nb1xncAgk9F95zI/qSuChkxjDg4= 27 | -----END RSA PRIVATE KEY----- 28 | -------------------------------------------------------------------------------- /testdata/rsa/private_key2048_PKCS8.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDIWuwOYaOMpyow 3 | 2xpAo7wh7WGcT4OwHyKQnlowwUQdKoOHvHcOjACRJN9OOZ+16/Epo3hk9WfXYH5T 4 | kw/QmYHjmkkPdXoYFotyyPDTVF4HvGZ6M1HIY/LPAF7ZDEWw5FXYFBIYIwx58xU0 5 | QMkxr97z/3JQuuFDnw+e5hb4cg/nKeTqCJXis+cD3FOSO4hoO+O60pJHQNomvXi4 6 | KoH5FGgXEupT5FkQpaZY+Wi9UJfMHUliO5YYdZPfMqTzp0gmcsYHdcAXtvSQidEm 7 | 30WD5C9vB4s0kjRqZU1wWt2NGjREfjER7Ycg/FmSVlDZL0rOObM11s7C5KLpOdn9 8 | jLRPLauxAgMBAAECggEAAXl8f+/uBoylvdhO+Cd8V0+U8VPl0eJYxxxd6OVIkre2 9 | YhsGpp/B0qT6fmvkJgWc2m6ZOzz8d6mpJFpSZSRMFW2+7eiHw9eeonNMz8aw2SO6 10 | KVoUAa03+JgLGqksIMzSvkAKO2KT++3IZoBiOrBjAVlhm6spxJf4wyBJuP216kCc 11 | UzTMrO7ekTAob8KqGD6PPWRm0h3yx+X+3fjrJpF5pJlOSre2uK2viTcDB9dUo3Uk 12 | SJlMojFzG3IlIZEZWFOoOUdgbZw/IqS7VFvH/EbVCqDmG7ZIG/znrZ4Cmh+Z6SPH 13 | r5qguIhcC0gMDi2n+L8NfT00phTXATl/9ltWxJfyFQKBgQD+T6yzJ8mQECTfrjF6 14 | akTxd8FXgib7ui28LJ3T8y1Y4DNdBJZPzPQ0mQuropbk/hrcJRFQwO1jTPAR9EA8 15 | SHvRVp4fn7V427Kr2G0+hBnW3oQZJdBOt0A/BmApxpAW/odt8Im2b0sPwFlWJiFY 16 | Zp5a4EnLpNhbhKTDR8GSQj9V/wKBgQDJr4Xgv8G8pjIUOLqyCxAqXttVYtt4mctO 17 | KlvJApAUqNXhKAauprpacwhSrY1kMFDDATwEJtXfxn5rhWJQ/imdrmaOE4MfPJtc 18 | oj7keDD3Xno1Z4pDUreLZQOb0huzFUx7oPU/AXCVjgDeoHRNew4KKJlHbqPaBgRy 19 | m0XZBPHeTwKBgBARnrrvP6gvrPTJP+0ZO+P6CihqW+Y5/FvQgeR7kBj2iahN+uVt 20 | 8NxF81vT+S7c83uHiBGPLzDAEsc/pLNqbRBsJa+f2sqZLy/2i+iQVkWiZ7xjDEd9 21 | Ar04vOjG2CHr0UjcEweI/rgvEjYEUMUSLt1VILSSZO1bc0G3jf6c2v2nAoGBAJz1 22 | T5BAS4sAIDsihxDmkTcYSUOjQsARmGoFhxbhOrYJSyIF32VuddrMaS7GyCGvhDqu 23 | rEFi49FaDaqpzIDRvBttzXxGbSYTKammzqFJdHoe+aa0JKMOLa969pr+ofX13mbc 24 | NeBJw49wSvb3Emv2F1AKlndEAJo9UprKgkpJkd37AoGAJ/Sizzb4VfOxoyxZ0Jg9 25 | zdHQD6uo5q2uIgm8a/V3xxWo/BRjEcXaJgdsmp+qIJJeqE5iUuZiFUaT2B0gKoZE 26 | DxJIYX0gfOXRZo/+jAy4hDIYXFbreBoIZzSKitHcmVyf+i/t1+PVxs8TJqC1MbXL 27 | QNThj1sOZrMA4GnvghFxZyI= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /os_unix_test.go: -------------------------------------------------------------------------------- 1 | // +build linux darwin 2 | 3 | package kgo 4 | 5 | import ( 6 | "github.com/stretchr/testify/assert" 7 | "os" 8 | "testing" 9 | ) 10 | 11 | func TestOS_Unix_NotWindows(t *testing.T) { 12 | res := KOS.IsWindows() 13 | assert.False(t, res) 14 | } 15 | 16 | func TestOS_Unix_HomeDir(t *testing.T) { 17 | res, err := KOS.HomeDir() 18 | assert.Nil(t, err) 19 | assert.NotEmpty(t, res) 20 | } 21 | 22 | func BenchmarkOS_Unix_HomeDir(b *testing.B) { 23 | b.ResetTimer() 24 | for i := 0; i < b.N; i++ { 25 | _, _ = KOS.HomeDir() 26 | } 27 | } 28 | 29 | func TestOS_Unix_Exec(t *testing.T) { 30 | var ret int 31 | var res []byte 32 | var err []byte 33 | 34 | ret, res, err = KOS.Exec(tesCommand01) 35 | assert.Equal(t, ret, 0) 36 | assert.NotEmpty(t, res) 37 | assert.Empty(t, err) 38 | 39 | //错误的命令 40 | ret, res, err = KOS.Exec(tesCommand02) 41 | assert.Equal(t, ret, 1) 42 | assert.Empty(t, res) 43 | assert.NotEmpty(t, err) 44 | } 45 | 46 | func BenchmarkOS_Unix_Exec(b *testing.B) { 47 | b.ResetTimer() 48 | for i := 0; i < b.N; i++ { 49 | _, _, _ = KOS.Exec(tesCommand01) 50 | } 51 | } 52 | 53 | func TestOS_Unix_System(t *testing.T) { 54 | var ret int 55 | var res []byte 56 | var err []byte 57 | 58 | ret, res, err = KOS.System(tesCommand01) 59 | assert.Equal(t, ret, 0) 60 | assert.Empty(t, err) 61 | assert.NotEmpty(t, res) 62 | 63 | //错误的命令 64 | ret, res, err = KOS.System(tesCommand02) 65 | assert.Equal(t, ret, 1) 66 | assert.NotEmpty(t, err) 67 | assert.Empty(t, res) 68 | } 69 | 70 | func BenchmarkOS_Unix_System(b *testing.B) { 71 | b.ResetTimer() 72 | for i := 0; i < b.N; i++ { 73 | _, _, _ = KOS.System(tesCommand01) 74 | } 75 | } 76 | 77 | func TestOS_Unix_IsProcessExists(t *testing.T) { 78 | var res bool 79 | 80 | pid := os.Getpid() 81 | res = KOS.IsProcessExists(pid) 82 | assert.True(t, res) 83 | 84 | res = KOS.IsProcessExists(-1) 85 | assert.False(t, res) 86 | } 87 | 88 | func BenchmarkOS_Unix_IsProcessExists(b *testing.B) { 89 | b.ResetTimer() 90 | pid := os.Getpid() 91 | for i := 0; i < b.N; i++ { 92 | KOS.IsProcessExists(pid) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: kgo-test 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | branches: 8 | - master 9 | - release 10 | pull_request: 11 | branches: [ master ] 12 | 13 | jobs: 14 | lint: 15 | name: Lint 16 | strategy: 17 | matrix: 18 | go-version: [ 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x ] 19 | os: [ ubuntu-latest ] 20 | runs-on: ${{ matrix.os }} 21 | steps: 22 | - uses: actions/checkout@v3 23 | 24 | - name: golangci-lint 25 | uses: golangci/golangci-lint-action@v3 26 | with: 27 | version: latest 28 | args: --issues-exit-code=0 --timeout=10m 29 | 30 | test-unix: 31 | name: TestUnix 32 | needs: Lint 33 | strategy: 34 | matrix: 35 | go-version: [ 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x, 1.21.x, 1.22.x, 1.23.x ] 36 | os: [ ubuntu-latest, macos-latest ] 37 | runs-on: ${{ matrix.os }} 38 | steps: 39 | - name: Install Go 40 | uses: actions/setup-go@v3 41 | with: 42 | go-version: ${{ matrix.go-version }} 43 | 44 | - name: Checkout code 45 | uses: actions/checkout@v3 46 | with: 47 | fetch-depth: 1 48 | 49 | - name: Test 50 | run: go mod vendor && go test -v -mod=mod -race -coverprofile=coverage.txt -covermode=atomic 51 | 52 | - name: Upload coverage to Codecov 53 | run: bash <(curl -s https://codecov.io/bash) 54 | 55 | test-win: 56 | name: TestWindows 57 | needs: Lint 58 | strategy: 59 | matrix: 60 | go-version: [ 1.16.x, 1.17.x, 1.18.x, 1.19.x, 1.20.x , 1.21.x, 1.22.x, 1.23.x ] 61 | os: [ windows-latest ] 62 | runs-on: ${{ matrix.os }} 63 | steps: 64 | - name: Install Go 65 | uses: actions/setup-go@v3 66 | with: 67 | go-version: ${{ matrix.go-version }} 68 | 69 | - name: Checkout code 70 | uses: actions/checkout@v3 71 | with: 72 | fetch-depth: 1 73 | 74 | - name: Test 75 | run: go mod vendor && go test -v -mod=mod -race -coverprofile=coverageout -covermode=atomic 76 | 77 | - name: Upload coverage to Codecov 78 | uses: codecov/codecov-action@v1 79 | with: 80 | files: coverageout 81 | flags: unittests 82 | name: codecov-umbrella 83 | fail_ci_if_error: false # optional (default = false) 84 | verbose: true # optional (default = false) 85 | -------------------------------------------------------------------------------- /file_unix_test.go: -------------------------------------------------------------------------------- 1 | //go:build linux || darwin 2 | // +build linux darwin 3 | 4 | package kgo 5 | 6 | import ( 7 | "github.com/stretchr/testify/assert" 8 | "os" 9 | "testing" 10 | ) 11 | 12 | func TestFileUnix_IsReadable_Deny(t *testing.T) { 13 | res := KFile.IsReadable(rootDir) 14 | assert.False(t, res) 15 | } 16 | 17 | func TestFileUnix_IsWritable_Deny(t *testing.T) { 18 | res := KFile.IsWritable(rootDir) 19 | assert.False(t, res) 20 | } 21 | 22 | func TestFileUnix_IsExecutable_Deny(t *testing.T) { 23 | var res bool 24 | res = KFile.IsExecutable(rootDir) 25 | assert.False(t, res) 26 | } 27 | 28 | func TestFileUnix_CopyFile_Deny(t *testing.T) { 29 | var err error 30 | //目标路径无权限 31 | _, err = KFile.CopyFile(imgPng, rootFile1, FILE_COVER_ALLOW) 32 | assert.NotNil(t, err) 33 | } 34 | 35 | func TestFileUnix_FastCopy_Deny(t *testing.T) { 36 | var err error 37 | //目录无权限 38 | _, err = KFile.FastCopy(imgJpg, rootFile1) 39 | assert.NotNil(t, err) 40 | } 41 | 42 | func TestFileUnix_CopyLink_Deny(t *testing.T) { 43 | var err error 44 | 45 | //创建链接文件 46 | if !KFile.IsExist(fileLink) { 47 | _ = os.Symlink(filePubPem, fileLink) 48 | } 49 | 50 | //目标路径无权限 51 | err = KFile.CopyLink(fileLink, rootFile1, FILE_COVER_ALLOW) 52 | assert.NotNil(t, err) 53 | } 54 | 55 | func TestFileUnix_CopyDir_Deny(t *testing.T) { 56 | var err error 57 | //目标路径无权限 58 | _, err = KFile.CopyDir(dirVendor, rootDir2, FILE_COVER_ALLOW) 59 | assert.NotNil(t, err) 60 | 61 | //源路径无权限 62 | _, err = KFile.CopyDir(rootDir, dirTdat, FILE_COVER_ALLOW) 63 | assert.NotNil(t, err) 64 | } 65 | 66 | func TestFileUnix_DelDir_Deny(t *testing.T) { 67 | var err error 68 | //目录无权限 69 | err = KFile.DelDir(rootDir, false) 70 | assert.NotNil(t, err) 71 | } 72 | 73 | func TestFileUnix_TarGzUnTarGz(t *testing.T) { 74 | var res bool 75 | var err error 76 | 77 | //打包-源目录无权限 78 | res, err = KFile.TarGz(rootDir, targzfile2) 79 | assert.False(t, res) 80 | assert.NotNil(t, err) 81 | 82 | //打包-目标目录无权限 83 | res, err = KFile.TarGz(dirVendor, rootFile3) 84 | assert.False(t, res) 85 | assert.NotNil(t, err) 86 | 87 | //解压到无权限的目录 88 | if !KFile.IsExist(targzfile1) { 89 | _, _ = KFile.TarGz(dirVendor, targzfile1) 90 | } 91 | res, err = KFile.UnTarGz(targzfile1, rootDir) 92 | assert.False(t, res) 93 | assert.NotNil(t, err) 94 | } 95 | 96 | func TestFileUnix_ChmodBatch(t *testing.T) { 97 | var res bool 98 | 99 | //无权限的目录 100 | res = KFile.ChmodBatch(rootDir, 0777, 0777) 101 | assert.False(t, res) 102 | } 103 | 104 | func TestFileUnix_ZipIszipUnzip(t *testing.T) { 105 | var res1, res2 bool 106 | var err1, err2 error 107 | 108 | //打包无权限的目录 109 | res1, err1 = KFile.Zip(zipfile2, rootDir) 110 | assert.False(t, res1) 111 | assert.NotNil(t, err1) 112 | 113 | //解压到无权限的目录 114 | res2, err2 = KFile.UnZip(zipfile1, rootDir) 115 | assert.False(t, res2) 116 | assert.NotNil(t, err2) 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kgo 2 | 3 | k`s golang helper/library/utils 4 | golang 常用函数库/工具集,仅测试支持有限的64位系统. 5 | 总共470多个通用的方法,涵盖字符串、数组、文件、时间、加密以及类型转换等操作. 6 | 7 | ### 文档 8 | 9 | [![GoDoc](https://godoc.org/github.com/kakuilan/kgo?status.svg)](https://pkg.go.dev/github.com/kakuilan/kgo) 10 | [![Go Report Card](https://goreportcard.com/badge/github.com/kakuilan/kgo)](https://goreportcard.com/report/github.com/kakuilan/kgo) 11 | [![Build Status](https://github.com/kakuilan/kgo/workflows/kgo-test/badge.svg)](https://github.com/kakuilan/kgo/actions) 12 | [![codecov](https://codecov.io/gh/kakuilan/kgo/branch/master/graph/badge.svg)](https://codecov.io/gh/kakuilan/kgo) 13 | [![Code Size](https://img.shields.io/github/languages/code-size/kakuilan/kgo.svg?style=flat-square)](https://github.com/kakuilan/kgo) 14 | [![Starts](https://img.shields.io/github/stars/kakuilan/kgo.svg)](https://github.com/kakuilan/kgo) 15 | [![Version](https://img.shields.io/github/v/tag/kakuilan/kgo)](https://img.shields.io/github/v/tag/kakuilan/kgo) 16 | 17 | ### 测试支持 18 | 19 | - GO版本 20 | - 1.16.x 21 | - 1.17.x 22 | - 1.18.x 23 | - 1.19.x 24 | - 1.20.x 25 | - 1.21.x 26 | - 1.22.x 27 | - 1.23.x 28 | - OS系统 29 | - ubuntu-latest 30 | - macos-latest 31 | - windows-latest 32 | 33 | ### 依赖第三方库 34 | 35 | - github.com/json-iterator/go 36 | - github.com/yusufpapurcu/wmi 37 | 38 | ### 安装使用 39 | 40 | 安装 41 | 42 | ```shell script 43 | go get -u github.com/kakuilan/kgo 44 | ``` 45 | 46 | 引入 47 | 48 | ```go 49 | import "github.com/kakuilan/kgo" 50 | ``` 51 | 52 | ### 函数接收器 53 | 54 | - *KFile* 为文件操作,如 55 | 56 | ```go 57 | chk := KFile.IsExist(filename) 58 | ``` 59 | 60 | - *KStr* 为字符串操作,如 61 | 62 | ```go 63 | res := KStr.Trim(" hello world ") 64 | ``` 65 | 66 | - *KNum* 为数值操作,如 67 | 68 | ```go 69 | res := KNum.NumberFormat(123.4567890, 3, ".", "") 70 | ``` 71 | 72 | - *KArr* 为数组(切片/字典)操作,如 73 | 74 | ```go 75 | mp := map[string]string{ 76 | "a": "aa", 77 | "b": "bb", 78 | } 79 | chk := KArr.InArray("bb", mp) 80 | ``` 81 | 82 | - *KTime* 为时间操作,如 83 | 84 | ```go 85 | res, err := KTime.Str2Timestamp("2019-07-11 10:11:23") 86 | ``` 87 | 88 | - *KConv* 为类型转换操作,如 89 | 90 | ```go 91 | res := KConv.ToStr(false) 92 | ``` 93 | 94 | - *KOS* 为系统和网络操作,如 95 | 96 | ```go 97 | res, err := KOS.LocalIP() 98 | ``` 99 | 100 | - *KEncr* 为加密操作,如 101 | 102 | ```go 103 | res, err := KEncr.PasswordHash([]byte("123456")) 104 | ``` 105 | 106 | - *KDbug* 为调试操作,如 107 | 108 | ```go 109 | KDbug.DumpPrint(1.2) 110 | ``` 111 | 112 | 具体函数请查看[godoc](https://pkg.go.dev/github.com/kakuilan/kgo),更多示例请参考*_test.go文件. 113 | 114 | ### 测试 115 | 116 | ```shell 117 | #使用go mod 118 | go mod tidy 119 | go mod vendor 120 | 121 | #单元测试 122 | go test -race 123 | 124 | #压测 125 | time go test -bench=. -run=none 126 | time go test -v -bench=. -cpu=4 -benchtime="10s" -timeout="15s" -benchmem 127 | 128 | #代码覆盖率 129 | go test -cover #概览 130 | 131 | go test -coverprofile=coverage.out #生成统计信息 132 | go test -v -covermode=count -coverprofile=coverage.out 133 | go tool cover -func=coverage.out #查看统计信息 134 | go tool cover -html=coverage.out #将统计信息转换为html 135 | 136 | #性能分析 137 | time go test -timeout 30m -bench=. -benchmem -memprofile memprofile.out -cpuprofile profile.out 138 | go tool pprof profile.out 139 | go tool pprof -http=192.168.1.2:8081 /usr/bin/dot profile.out 140 | ``` 141 | 142 | ### 更新日志 143 | 144 | 详见[[Changelog]](/docs/changelog.md) 145 | 146 | ### 鸣谢 147 | 148 | 感谢[JetBrains](https://www.jetbrains.com/?from=kakuilan/kgo)的赞助. 149 | [![JetBrains](testdata/jetbrains.svg)](https://www.jetbrains.com/?from=kakuilan/kgo) 150 | -------------------------------------------------------------------------------- /os_darwin_test.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | // +build darwin 3 | 4 | package kgo 5 | 6 | import ( 7 | "fmt" 8 | "github.com/stretchr/testify/assert" 9 | "io" 10 | "net" 11 | "testing" 12 | "time" 13 | ) 14 | 15 | func TestOS_Darwin_IsMac(t *testing.T) { 16 | res := KOS.IsMac() 17 | assert.True(t, res) 18 | } 19 | 20 | func BenchmarkOS_Darwin_IsMac(b *testing.B) { 21 | b.ResetTimer() 22 | for i := 0; i < b.N; i++ { 23 | KOS.IsMac() 24 | } 25 | } 26 | 27 | func TestOS_Darwin_MemoryUsage(t *testing.T) { 28 | var used, free, total uint64 29 | 30 | used, free, total = KOS.MemoryUsage(true) 31 | assert.Greater(t, int(used), 1) 32 | assert.Greater(t, int(free), 1) 33 | assert.Greater(t, int(total), 1) 34 | } 35 | 36 | func BenchmarkOS_Darwin_MemoryUsage(b *testing.B) { 37 | b.ResetTimer() 38 | for i := 0; i < b.N; i++ { 39 | KOS.MemoryUsage(true) 40 | } 41 | } 42 | 43 | func TestOS_Darwin_DiskUsage(t *testing.T) { 44 | var used, free, total uint64 45 | used, free, total = KOS.DiskUsage("/") 46 | assert.Greater(t, int(used), 1) 47 | assert.Greater(t, int(free), 1) 48 | assert.Greater(t, int(total), 1) 49 | } 50 | 51 | func BenchmarkOS_Darwin_DiskUsage(b *testing.B) { 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | _, _, _ = KOS.DiskUsage("/") 55 | } 56 | } 57 | 58 | func TestOS_Darwin_Uptime(t *testing.T) { 59 | res, err := KOS.Uptime() 60 | assert.Greater(t, int(res), 1) 61 | assert.Nil(t, err) 62 | } 63 | 64 | func BenchmarkOS_Darwin_Uptime(b *testing.B) { 65 | b.ResetTimer() 66 | for i := 0; i < b.N; i++ { 67 | _, _ = KOS.Uptime() 68 | } 69 | } 70 | 71 | func TestOS_Darwin_GetBiosInfo(t *testing.T) { 72 | res := KOS.GetBiosInfo() 73 | assert.Empty(t, res.Version) 74 | } 75 | 76 | func BenchmarkOS_Darwin_GetBiosInfo(b *testing.B) { 77 | b.ResetTimer() 78 | for i := 0; i < b.N; i++ { 79 | KOS.GetBiosInfo() 80 | } 81 | } 82 | 83 | func TestOS_Darwin_GetBoardInfo(t *testing.T) { 84 | res := KOS.GetBoardInfo() 85 | assert.NotNil(t, res) 86 | assert.NotEmpty(t, res.Name) 87 | assert.NotEmpty(t, res.Version) 88 | assert.NotEmpty(t, res.Serial) 89 | } 90 | 91 | func BenchmarkOS_Darwin_GetBoardInfo(b *testing.B) { 92 | b.ResetTimer() 93 | for i := 0; i < b.N; i++ { 94 | KOS.GetBoardInfo() 95 | } 96 | } 97 | 98 | func TestOS_Darwin_GetCpuInfo(t *testing.T) { 99 | res := KOS.GetCpuInfo() 100 | KDbug.DumpPrint(res) 101 | assert.NotNil(t, res) 102 | assert.NotEmpty(t, res.Model) 103 | assert.NotEmpty(t, res.Threads) 104 | } 105 | 106 | func BenchmarkOS_Darwin_GetCpuInfo(b *testing.B) { 107 | b.ResetTimer() 108 | for i := 0; i < b.N; i++ { 109 | KOS.GetCpuInfo() 110 | } 111 | } 112 | 113 | func TestOS_Darwin_GetPidByPort(t *testing.T) { 114 | time.AfterFunc(time.Millisecond*250, func() { 115 | res := KOS.GetPidByPort(8899) 116 | assert.Greater(t, res, 1) 117 | 118 | KOS.GetPidByPort(80) 119 | }) 120 | 121 | //发送消息 122 | time.AfterFunc(time.Millisecond*500, func() { 123 | conn, err := net.Dial("tcp", ":8899") 124 | assert.Nil(t, err) 125 | 126 | defer func() { 127 | _ = conn.Close() 128 | }() 129 | 130 | _, err = fmt.Fprintf(conn, helloEng) 131 | assert.Nil(t, err) 132 | }) 133 | 134 | //开启监听端口 135 | l, err := net.Listen("tcp", ":8899") 136 | assert.Nil(t, err) 137 | defer func() { 138 | _ = l.Close() 139 | }() 140 | 141 | for { 142 | conn, err := l.Accept() 143 | if err != nil { 144 | return 145 | } 146 | defer func() { 147 | _ = conn.Close() 148 | }() 149 | 150 | //接收 151 | buf, err := io.ReadAll(conn) 152 | assert.Nil(t, err) 153 | 154 | msg := string(buf[:]) 155 | assert.Equal(t, msg, helloEng) 156 | return 157 | } 158 | } 159 | 160 | func BenchmarkOS_Darwin_GetPidByPort(b *testing.B) { 161 | b.ResetTimer() 162 | for i := 0; i < b.N; i++ { 163 | KOS.GetPidByPort(8899) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /function_test.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestFun_lenArrayOrSlice(t *testing.T) { 9 | var res int 10 | 11 | res = lenArrayOrSlice(naturalArr, 3) 12 | assert.Greater(t, res, 0) 13 | 14 | res = lenArrayOrSlice(naturalArr, 1) 15 | assert.Greater(t, res, 0) 16 | 17 | res = lenArrayOrSlice(naturalArr, 2) 18 | assert.Equal(t, res, -1) 19 | 20 | res = lenArrayOrSlice(naturalArr, 9) 21 | assert.Greater(t, res, 0) 22 | } 23 | 24 | func TestFun_isNil(t *testing.T) { 25 | var s []int 26 | var i interface{} 27 | var res bool 28 | 29 | res = isNil(s) 30 | assert.True(t, res) 31 | 32 | res = isNil(&s) 33 | assert.False(t, res) 34 | 35 | res = isNil(i) 36 | assert.True(t, res) 37 | } 38 | 39 | func TestFun_numeric2Float(t *testing.T) { 40 | var tests = []struct { 41 | num interface{} 42 | expected error 43 | }{ 44 | {123, nil}, 45 | {int8(12), nil}, 46 | {int16(12), nil}, 47 | {int32(12), nil}, 48 | {int64(12), nil}, 49 | {uint(123), nil}, 50 | {uint8(13), nil}, 51 | {uint16(13), nil}, 52 | {uint32(13), nil}, 53 | {uint64(13), nil}, 54 | {float32(3.1415), nil}, 55 | {float64(3.1415), nil}, 56 | {"6145", nil}, 57 | } 58 | for _, test := range tests { 59 | _, actual := numeric2Float(test.num) 60 | assert.Equal(t, test.expected, actual) 61 | } 62 | } 63 | 64 | func TestFun_GetVariateType(t *testing.T) { 65 | var res string 66 | 67 | res = GetVariateType(1) 68 | assert.Equal(t, "int", res) 69 | 70 | res = GetVariateType(intAstronomicalUnit) 71 | assert.Equal(t, "int64", res) 72 | 73 | res = GetVariateType(flPi1) 74 | assert.Equal(t, "float32", res) 75 | 76 | res = GetVariateType(floAvogadro) 77 | assert.Equal(t, "float64", res) 78 | 79 | res = GetVariateType(strHello) 80 | assert.Equal(t, "string", res) 81 | 82 | res = GetVariateType(true) 83 | assert.Equal(t, "bool", res) 84 | 85 | res = GetVariateType(rune('你')) 86 | assert.Equal(t, "int32", res) 87 | 88 | res = GetVariateType('你') 89 | assert.Equal(t, "int32", res) 90 | 91 | res = GetVariateType([]byte("你好")) 92 | assert.Equal(t, "[]uint8", res) 93 | } 94 | 95 | func BenchmarkFun_GetVariateType(b *testing.B) { 96 | b.ResetTimer() 97 | for i := 0; i < b.N; i++ { 98 | GetVariateType(intAstronomicalUnit) 99 | } 100 | } 101 | 102 | func TestFun_GetVariatePointerAddr(t *testing.T) { 103 | var tests = []struct { 104 | input interface{} 105 | expected float64 106 | }{ 107 | {intSpeedLight, 0}, 108 | {strHello, 0}, 109 | {crowd, 0}, 110 | } 111 | for _, test := range tests { 112 | actual := GetVariatePointerAddr(test.input) 113 | assert.Greater(t, actual, int64(test.expected)) 114 | } 115 | 116 | res := GetVariatePointerAddr(&tests) 117 | assert.Greater(t, res, int64(0)) 118 | } 119 | 120 | func BenchmarkFun_GetVariatePointerAddr(b *testing.B) { 121 | b.ResetTimer() 122 | for i := 0; i < b.N; i++ { 123 | GetVariatePointerAddr(intSpeedLight) 124 | } 125 | } 126 | 127 | func TestFun_IsPointer(t *testing.T) { 128 | var chk bool 129 | 130 | //非指针 131 | chk = IsPointer(itfObj, false) 132 | assert.False(t, chk) 133 | 134 | //指针 135 | chk = IsPointer(orgS1, false) 136 | assert.True(t, chk) 137 | 138 | //非nil指针 139 | chk = IsPointer(orgS1, true) 140 | assert.True(t, chk) 141 | 142 | //空指针 143 | chk = IsPointer(itfObj, true) 144 | assert.False(t, chk) 145 | } 146 | 147 | func BenchmarkFun_IsPointer(b *testing.B) { 148 | b.ResetTimer() 149 | for i := 0; i < b.N; i++ { 150 | IsPointer(orgS1, true) 151 | } 152 | } 153 | 154 | func TestFun_VerifyFunc_CallFunc(t *testing.T) { 155 | var res []interface{} 156 | var err error 157 | 158 | //不存在的对象方法调用 159 | res, err = CallFunc(strHello, helloEngICase) 160 | assert.NotNil(t, err) 161 | assert.Empty(t, res) 162 | 163 | //方法存在,但参数数量错误 164 | fn := getMethod(&KConv, "BaseConvert") 165 | res, err = CallFunc(fn) 166 | assert.NotNil(t, err) 167 | assert.Empty(t, res) 168 | 169 | //方法存在,参数数量无误,但参数类型错误 170 | res, err = CallFunc(fn, strHello, false, 1) 171 | assert.NotNil(t, err) 172 | assert.Empty(t, res) 173 | } 174 | -------------------------------------------------------------------------------- /os_linux_test.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package kgo 5 | 6 | import ( 7 | "fmt" 8 | "github.com/stretchr/testify/assert" 9 | "io" 10 | "net" 11 | "os" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | func TestOS_Linux_IsLinux(t *testing.T) { 17 | res := KOS.IsLinux() 18 | assert.True(t, res) 19 | } 20 | 21 | func BenchmarkOS_Linux_IsLinux(b *testing.B) { 22 | b.ResetTimer() 23 | for i := 0; i < b.N; i++ { 24 | KOS.IsLinux() 25 | } 26 | } 27 | 28 | func TestOS_Linux_MemoryUsage(t *testing.T) { 29 | var used, free, total uint64 30 | 31 | // 虚拟内存 32 | used, free, total = KOS.MemoryUsage(true) 33 | assert.Greater(t, int(used), 1) 34 | assert.Greater(t, int(free), 1) 35 | assert.Greater(t, int(total), 1) 36 | 37 | // 真实物理内存 38 | used, free, total = KOS.MemoryUsage(false) 39 | assert.Greater(t, int(used), 1) 40 | assert.Greater(t, int(free), 1) 41 | assert.Greater(t, int(total), 1) 42 | } 43 | 44 | func BenchmarkOS_Linux_MemoryUsage_Virtual(b *testing.B) { 45 | b.ResetTimer() 46 | for i := 0; i < b.N; i++ { 47 | KOS.MemoryUsage(true) 48 | } 49 | } 50 | 51 | func BenchmarkOS_Linux_MemoryUsage_Physic(b *testing.B) { 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | KOS.MemoryUsage(false) 55 | } 56 | } 57 | 58 | func TestOS_Linux_CpuUsage(t *testing.T) { 59 | var user, idle, total uint64 60 | user, idle, total = KOS.CpuUsage() 61 | assert.Greater(t, int(user), 1) 62 | assert.Greater(t, int(idle), 1) 63 | assert.Greater(t, int(total), 1) 64 | } 65 | 66 | func BenchmarkOS_Linux_CpuUsage(b *testing.B) { 67 | b.ResetTimer() 68 | for i := 0; i < b.N; i++ { 69 | _, _, _ = KOS.CpuUsage() 70 | } 71 | } 72 | 73 | func TestOS_Linux_DiskUsage(t *testing.T) { 74 | var used, free, total uint64 75 | used, free, total = KOS.DiskUsage("/") 76 | assert.Greater(t, int(used), 1) 77 | assert.Greater(t, int(free), 1) 78 | assert.Greater(t, int(total), 1) 79 | } 80 | 81 | func BenchmarkOS_Linux_DiskUsage(b *testing.B) { 82 | b.ResetTimer() 83 | for i := 0; i < b.N; i++ { 84 | _, _, _ = KOS.DiskUsage("/") 85 | } 86 | } 87 | 88 | func TestOS_Linux_Uptime(t *testing.T) { 89 | res, err := KOS.Uptime() 90 | assert.Greater(t, int(res), 1) 91 | assert.Nil(t, err) 92 | } 93 | 94 | func BenchmarkOS_Linux_Uptime(b *testing.B) { 95 | b.ResetTimer() 96 | for i := 0; i < b.N; i++ { 97 | _, _ = KOS.Uptime() 98 | } 99 | } 100 | 101 | func TestOS_Linux_GetBiosInfo(t *testing.T) { 102 | res := KOS.GetBiosInfo() 103 | assert.NotNil(t, res) 104 | } 105 | 106 | func BenchmarkOS_Linux_GetBiosInfo(b *testing.B) { 107 | b.ResetTimer() 108 | for i := 0; i < b.N; i++ { 109 | KOS.GetBiosInfo() 110 | } 111 | } 112 | 113 | func TestOS_Linux_GetBoardInfo(t *testing.T) { 114 | res := KOS.GetBoardInfo() 115 | assert.NotNil(t, res) 116 | } 117 | 118 | func BenchmarkOS_Linux_GetBoardInfo(b *testing.B) { 119 | b.ResetTimer() 120 | for i := 0; i < b.N; i++ { 121 | KOS.GetBoardInfo() 122 | } 123 | } 124 | 125 | func TestOS_Linux_GetCpuInfo(t *testing.T) { 126 | res := KOS.GetCpuInfo() 127 | assert.NotNil(t, res) 128 | assert.NotEmpty(t, res.Vendor) 129 | assert.NotEmpty(t, res.Model) 130 | } 131 | 132 | func BenchmarkOS_Linux_GetCpuInfo(b *testing.B) { 133 | b.ResetTimer() 134 | for i := 0; i < b.N; i++ { 135 | KOS.GetCpuInfo() 136 | } 137 | } 138 | 139 | func TestOS_Linux_GetProcessExecPath(t *testing.T) { 140 | var res string 141 | 142 | pid := os.Getpid() 143 | res = KOS.GetProcessExecPath(pid) 144 | assert.NotEmpty(t, res) 145 | } 146 | 147 | func BenchmarkOS_Linux_GetProcessExecPath(b *testing.B) { 148 | b.ResetTimer() 149 | pid := os.Getpid() 150 | for i := 0; i < b.N; i++ { 151 | KOS.GetProcessExecPath(pid) 152 | } 153 | } 154 | 155 | func TestOS_Linux_GetPidByPort(t *testing.T) { 156 | time.AfterFunc(time.Millisecond*250, func() { 157 | res := KOS.GetPidByPort(8899) 158 | assert.Greater(t, res, 1) 159 | 160 | KOS.GetPidByPort(80) 161 | }) 162 | 163 | //发送消息 164 | time.AfterFunc(time.Millisecond*500, func() { 165 | conn, err := net.Dial("tcp", ":8899") 166 | assert.Nil(t, err) 167 | 168 | defer func() { 169 | _ = conn.Close() 170 | }() 171 | 172 | _, err = fmt.Fprintf(conn, helloEng) 173 | assert.Nil(t, err) 174 | }) 175 | 176 | //开启监听端口 177 | l, err := net.Listen("tcp", ":8899") 178 | assert.Nil(t, err) 179 | defer func() { 180 | _ = l.Close() 181 | }() 182 | 183 | for { 184 | conn, err := l.Accept() 185 | if err != nil { 186 | return 187 | } 188 | defer func() { 189 | _ = conn.Close() 190 | }() 191 | 192 | //接收 193 | buf, err := io.ReadAll(conn) 194 | assert.Nil(t, err) 195 | 196 | msg := string(buf[:]) 197 | assert.Equal(t, msg, helloEng) 198 | return 199 | } 200 | } 201 | 202 | func BenchmarkOS_Linux_GetPidByPort(b *testing.B) { 203 | b.ResetTimer() 204 | for i := 0; i < b.N; i++ { 205 | KOS.GetPidByPort(8899) 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /testdata/jetbrains.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 31 | 32 | 33 | 34 | 35 | 36 | 39 | 40 | 41 | 42 | 43 | 45 | 47 | 48 | 51 | 54 | 56 | 57 | 59 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /debug.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "bytes" 5 | "errors" 6 | "fmt" 7 | "go/parser" 8 | "go/token" 9 | "os" 10 | "path/filepath" 11 | "reflect" 12 | "runtime" 13 | "strings" 14 | ) 15 | 16 | // DumpPrint 打印调试变量. 17 | func (kd *LkkDebug) DumpPrint(vs ...interface{}) { 18 | dumpPrint(vs...) 19 | } 20 | 21 | // DumpStacks 打印堆栈信息. 22 | func (kd *LkkDebug) DumpStacks() { 23 | buf := make([]byte, 16384) 24 | buf = buf[:runtime.Stack(buf, true)] 25 | fmt.Printf("=== BEGIN stack dump ===\n%s\n=== END stack dump ===\n\n", buf) 26 | } 27 | 28 | // Stacks 获取堆栈信息;skip为要跳过的帧数. 29 | func (kd *LkkDebug) Stacks(skip int) []byte { 30 | buf := new(bytes.Buffer) 31 | var lastFile string 32 | 33 | //获取第N行的内容 34 | var sourceLine = func(lines [][]byte, n int) []byte { 35 | n-- // in stack trace, lines are 1-indexed but our array is 0-indexed 36 | var res []byte = bytDunno 37 | if n >= 0 && n < len(lines) { 38 | res = bytes.TrimSpace(lines[n]) 39 | } 40 | 41 | return res 42 | } 43 | 44 | for i := skip; ; i++ { 45 | pc, file, line, ok := runtime.Caller(i) 46 | if !ok { 47 | break 48 | } 49 | 50 | _, _ = fmt.Fprintf(buf, "%s:%d (0x%x)\n", file, line, pc) 51 | var lines [][]byte 52 | if file != lastFile { 53 | data, err := os.ReadFile(file) 54 | if err == nil { 55 | lines = bytes.Split(data, bytLinefeed) 56 | lastFile = file 57 | } 58 | } 59 | _, _ = fmt.Fprintf(buf, "\t%s: %s\n", kd.GetCallName(pc, true), sourceLine(lines, line)) 60 | } 61 | buf.Write(bytLinefeed) 62 | 63 | return buf.Bytes() 64 | } 65 | 66 | // GetCallName 获取调用的方法名称;f为目标方法;onlyFun为true时仅返回方法,不包括包名. 67 | func (kd *LkkDebug) GetCallName(f interface{}, onlyFun bool) string { 68 | var fn *runtime.Func 69 | r, _ := reflectFinalValue(reflect.ValueOf(f)) 70 | switch r.Kind() { 71 | case reflect.Invalid: 72 | // Skip this function, and fetch the PC and file for its parent 73 | pc, _, _, _ := runtime.Caller(1) 74 | // Retrieve a Function object this functions parent 75 | fn = runtime.FuncForPC(pc) 76 | case reflect.Func: 77 | fn = runtime.FuncForPC(r.Pointer()) 78 | case reflect.Uintptr: 79 | fn = runtime.FuncForPC(f.(uintptr)) 80 | default: 81 | return "" 82 | } 83 | 84 | name := fn.Name() 85 | if onlyFun { 86 | // extract just the function name (and not the module path) 87 | return strings.TrimPrefix(filepath.Ext(name), ".") 88 | } 89 | 90 | return name 91 | } 92 | 93 | // GetCallFile 获取调用方法的文件路径. 94 | func (kd *LkkDebug) GetCallFile() string { 95 | _, file, _, _ := runtime.Caller(1) 96 | return file 97 | } 98 | 99 | // GetCallDir 获取调用方法的文件目录. 100 | func (kd *LkkDebug) GetCallDir() string { 101 | return filepath.Dir(kd.GetCallFile()) 102 | } 103 | 104 | // GetCallLine 获取调用方法的行号. 105 | func (kd *LkkDebug) GetCallLine() int { 106 | // Skip this function, and fetch the PC and file for its parent 107 | _, _, line, _ := runtime.Caller(1) 108 | return line 109 | } 110 | 111 | // GetCallPackage 获取调用方法或调用文件的包名.callFile为调用文件路径. 112 | func (kd *LkkDebug) GetCallPackage(callFile ...string) string { 113 | var sourceFile string 114 | if len(callFile) == 0 { 115 | sourceFile = kd.GetCallFile() 116 | } else { 117 | sourceFile = callFile[0] 118 | } 119 | 120 | fset := token.NewFileSet() 121 | astFile, err := parser.ParseFile(fset, sourceFile, nil, parser.PackageClauseOnly) 122 | if err != nil || astFile.Name == nil { 123 | return "" 124 | } 125 | 126 | return astFile.Name.Name 127 | } 128 | 129 | // HasMethod 检查对象t是否具有method方法. 130 | func (kd *LkkDebug) HasMethod(t interface{}, method string) bool { 131 | _, err := methodExists(t, method) 132 | return err == nil 133 | } 134 | 135 | // GetMethod 获取对象t中的method方法. 136 | // 注意:返回的方法中的第一个参数是接收者. 137 | // 所以,调用返回的方法时,必须将接收者作为第一个参数传递. 138 | func (kd *LkkDebug) GetMethod(t interface{}, method string) interface{} { 139 | return getMethod(t, method) 140 | } 141 | 142 | // CallMethod 调用对象t的method方法. 143 | // 若执行成功,则结果是该方法的返回结果; 144 | // 否则返回(nil, error). 145 | func (kd *LkkDebug) CallMethod(t interface{}, method string, args ...interface{}) ([]interface{}, error) { 146 | m := kd.GetMethod(t, method) 147 | if m == nil { 148 | return nil, fmt.Errorf("[CallMethod] The %#v have no method: %s", t, method) 149 | } 150 | 151 | return CallFunc(m, args...) 152 | } 153 | 154 | // GetFuncNames 获取变量的所有(公开的)函数名. 155 | func (kd *LkkDebug) GetFuncNames(obj interface{}) (res []string) { 156 | return getFuncNames(obj) 157 | } 158 | 159 | // WrapError 错误包裹. 160 | func (kd *LkkDebug) WrapError(err error, args ...interface{}) (res error) { 161 | num := len(args) 162 | if err == nil && num == 0 { 163 | res = errors.New("[WrapError] parameter error") 164 | } else if err != nil && num == 0 { 165 | res = err 166 | } else { 167 | var msg []string 168 | for _, v := range args { 169 | msg = append(msg, toStr(v)) 170 | } 171 | 172 | if err != nil { 173 | msg = append(msg, fmt.Sprintf("last error: %s", err.Error())) 174 | } 175 | 176 | res = errors.New(strings.Join(msg, "\r\n")) 177 | } 178 | 179 | return 180 | } 181 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/brianvoe/gofakeit/v6 v6.16.0 h1:EelCqtfArd8ppJ0z+TpOxXH8sVWNPBadPNdCDSMMw7k= 2 | github.com/brianvoe/gofakeit/v6 v6.16.0/go.mod h1:Ow6qC71xtwm79anlwKRlWZW6zVq9D2XHE4QSSMP/rU8= 3 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 5 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 7 | github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= 8 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 9 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 10 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 11 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 12 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 13 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 14 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 15 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 16 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 17 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 18 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 19 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 20 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 21 | github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= 22 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 23 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 24 | github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg= 25 | github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 26 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 27 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 28 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e h1:T8NU3HyQ8ClP4SEE+KbFlg6n0NhuTsN4MyznaarGsZM= 29 | golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 30 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 31 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 32 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 33 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 34 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= 35 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 36 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 37 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 38 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 39 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 40 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 41 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 42 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 43 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 44 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s= 45 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 46 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 47 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 48 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 49 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 50 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 51 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 52 | golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= 53 | golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= 54 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 55 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 56 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 57 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 58 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 59 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 60 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 61 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 62 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 63 | -------------------------------------------------------------------------------- /os_windows_test.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package kgo 5 | 6 | import ( 7 | "fmt" 8 | "github.com/stretchr/testify/assert" 9 | "io" 10 | "net" 11 | "os" 12 | "testing" 13 | "time" 14 | ) 15 | 16 | func TestOS_Windows_IsWindows(t *testing.T) { 17 | res := KOS.IsWindows() 18 | assert.True(t, res) 19 | } 20 | 21 | func BenchmarkOS_Windows_IsWindows(b *testing.B) { 22 | b.ResetTimer() 23 | for i := 0; i < b.N; i++ { 24 | KOS.IsWindows() 25 | } 26 | } 27 | 28 | func TestOS_Windows_HomeDir(t *testing.T) { 29 | res, err := KOS.HomeDir() 30 | assert.Nil(t, err) 31 | assert.NotEmpty(t, res) 32 | } 33 | 34 | func BenchmarkOS_Windows_HomeDir(b *testing.B) { 35 | b.ResetTimer() 36 | for i := 0; i < b.N; i++ { 37 | _, _ = KOS.HomeDir() 38 | } 39 | } 40 | 41 | func TestOS_Windows_Exec(t *testing.T) { 42 | var ret int 43 | var res []byte 44 | var err []byte 45 | 46 | ret, res, err = KOS.Exec(tesCommand03) 47 | assert.Equal(t, ret, 0) 48 | assert.NotEmpty(t, res) 49 | assert.Empty(t, err) 50 | } 51 | 52 | func BenchmarkOS_Windows_Exec_Windows(b *testing.B) { 53 | b.ResetTimer() 54 | for i := 0; i < b.N; i++ { 55 | _, _, _ = KOS.Exec(tesCommand03) 56 | } 57 | } 58 | 59 | func TestOS_Windows_System(t *testing.T) { 60 | var ret int 61 | var res []byte 62 | var err []byte 63 | 64 | ret, res, err = KOS.System(tesCommand03) 65 | assert.Equal(t, ret, 0) 66 | assert.NotEmpty(t, res) 67 | assert.Empty(t, err) 68 | } 69 | 70 | func BenchmarkOS_Windows_System(b *testing.B) { 71 | b.ResetTimer() 72 | for i := 0; i < b.N; i++ { 73 | _, _, _ = KOS.System(tesCommand03) 74 | } 75 | } 76 | 77 | func TestOS_Windows_MemoryUsage(t *testing.T) { 78 | var used, free, total uint64 79 | 80 | used, free, total = KOS.MemoryUsage(true) 81 | assert.Greater(t, int(used), 1) 82 | assert.Greater(t, int(free), 1) 83 | assert.Greater(t, int(total), 1) 84 | } 85 | 86 | func BenchmarkOS_Windows_MemoryUsage(b *testing.B) { 87 | b.ResetTimer() 88 | for i := 0; i < b.N; i++ { 89 | KOS.MemoryUsage(true) 90 | } 91 | } 92 | 93 | func TestOS_Windows_CpuUsage(t *testing.T) { 94 | var user, idle, total uint64 95 | user, idle, total = KOS.CpuUsage() 96 | assert.Greater(t, int(user), 1) 97 | assert.Greater(t, int(idle), 1) 98 | assert.Greater(t, int(total), 1) 99 | } 100 | 101 | func BenchmarkOS_Windows_CpuUsage(b *testing.B) { 102 | b.ResetTimer() 103 | for i := 0; i < b.N; i++ { 104 | _, _, _ = KOS.CpuUsage() 105 | } 106 | } 107 | 108 | func TestOS_Windows_DiskUsage(t *testing.T) { 109 | var used, free, total uint64 110 | used, free, total = KOS.DiskUsage("C:") 111 | assert.Greater(t, int(used), 1) 112 | assert.Greater(t, int(free), 1) 113 | assert.Greater(t, int(total), 1) 114 | } 115 | 116 | func BenchmarkOS_Windows_DiskUsage(b *testing.B) { 117 | b.ResetTimer() 118 | for i := 0; i < b.N; i++ { 119 | _, _, _ = KOS.DiskUsage("C:") 120 | } 121 | } 122 | 123 | func TestOS_Windows_Uptime(t *testing.T) { 124 | res, err := KOS.Uptime() 125 | assert.Greater(t, int(res), 1) 126 | assert.Nil(t, err) 127 | } 128 | 129 | func BenchmarkOS_Windows_Uptime(b *testing.B) { 130 | b.ResetTimer() 131 | for i := 0; i < b.N; i++ { 132 | _, _ = KOS.Uptime() 133 | } 134 | } 135 | 136 | func TestOS_Windows_GetBiosInfo(t *testing.T) { 137 | res := KOS.GetBiosInfo() 138 | assert.NotNil(t, res) 139 | } 140 | 141 | func BenchmarkOS_Windows_GetBiosInfo(b *testing.B) { 142 | b.ResetTimer() 143 | for i := 0; i < b.N; i++ { 144 | KOS.GetBiosInfo() 145 | } 146 | } 147 | 148 | func TestOS_Windows_GetBoardInfo(t *testing.T) { 149 | res := KOS.GetBoardInfo() 150 | assert.NotNil(t, res) 151 | } 152 | 153 | func BenchmarkOS_Windows_GetBoardInfo(b *testing.B) { 154 | b.ResetTimer() 155 | for i := 0; i < b.N; i++ { 156 | KOS.GetBoardInfo() 157 | } 158 | } 159 | 160 | func TestOS_Windows_GetCpuInfo(t *testing.T) { 161 | res := KOS.GetCpuInfo() 162 | assert.NotNil(t, res) 163 | assert.NotEmpty(t, res.Vendor) 164 | assert.NotEmpty(t, res.Model) 165 | } 166 | 167 | func BenchmarkOS_Windows_GetCpuInfo(b *testing.B) { 168 | b.ResetTimer() 169 | for i := 0; i < b.N; i++ { 170 | KOS.GetCpuInfo() 171 | } 172 | } 173 | 174 | func TestOS_Windows_IsProcessExists(t *testing.T) { 175 | var res bool 176 | 177 | pid := os.Getpid() 178 | res = KOS.IsProcessExists(pid) 179 | assert.True(t, res) 180 | 181 | res = KOS.IsProcessExists(5) 182 | assert.False(t, res) 183 | 184 | res = KOS.IsProcessExists(-1) 185 | assert.False(t, res) 186 | } 187 | 188 | func BenchmarkOS_Windows_IsProcessExists(b *testing.B) { 189 | b.ResetTimer() 190 | pid := os.Getpid() 191 | for i := 0; i < b.N; i++ { 192 | KOS.IsProcessExists(pid) 193 | } 194 | } 195 | 196 | func TestOS_Windows_GetProcessExecPath(t *testing.T) { 197 | var res string 198 | 199 | pid := os.Getpid() 200 | res = KOS.GetProcessExecPath(pid) 201 | assert.NotEmpty(t, res) 202 | } 203 | 204 | func BenchmarkOS_Windows_GetProcessExecPath(b *testing.B) { 205 | b.ResetTimer() 206 | pid := os.Getpid() 207 | for i := 0; i < b.N; i++ { 208 | KOS.GetProcessExecPath(pid) 209 | } 210 | } 211 | 212 | func TestOS_Windows_GetPidByPort(t *testing.T) { 213 | time.AfterFunc(time.Millisecond*250, func() { 214 | res := KOS.GetPidByPort(8899) 215 | assert.Greater(t, res, 1) 216 | 217 | KOS.GetPidByPort(80) 218 | }) 219 | 220 | //发送消息 221 | time.AfterFunc(time.Millisecond*500, func() { 222 | conn, err := net.Dial("tcp", ":8899") 223 | assert.Nil(t, err) 224 | 225 | defer func() { 226 | _ = conn.Close() 227 | }() 228 | 229 | _, err = fmt.Fprintf(conn, helloEng) 230 | assert.Nil(t, err) 231 | }) 232 | 233 | //开启监听端口 234 | l, err := net.Listen("tcp", ":8899") 235 | assert.Nil(t, err) 236 | defer func() { 237 | _ = l.Close() 238 | }() 239 | 240 | for { 241 | conn, err := l.Accept() 242 | if err != nil { 243 | return 244 | } 245 | defer func() { 246 | _ = conn.Close() 247 | }() 248 | 249 | //接收 250 | buf, err := io.ReadAll(conn) 251 | assert.Nil(t, err) 252 | 253 | msg := string(buf[:]) 254 | assert.Equal(t, msg, helloEng) 255 | return 256 | } 257 | } 258 | 259 | func BenchmarkOS_Windows_GetPidByPort(b *testing.B) { 260 | b.ResetTimer() 261 | for i := 0; i < b.N; i++ { 262 | KOS.GetPidByPort(8899) 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /os_darwin.go: -------------------------------------------------------------------------------- 1 | //go:build darwin 2 | // +build darwin 3 | 4 | package kgo 5 | 6 | import ( 7 | "encoding/binary" 8 | "fmt" 9 | "golang.org/x/sys/unix" 10 | "os/exec" 11 | "strconv" 12 | "strings" 13 | "sync/atomic" 14 | "time" 15 | ) 16 | 17 | // cachedBootTime must be accessed via atomic.Load/StoreUint64 18 | var cachedBootTime uint64 19 | 20 | // 系统IO信息 21 | var cacheIOInfos []byte 22 | 23 | // bootTime 获取系统启动时间,秒. 24 | func bootTime() (uint64, error) { 25 | t := atomic.LoadUint64(&cachedBootTime) 26 | if t != 0 { 27 | return t, nil 28 | } 29 | tv, err := unix.SysctlTimeval("kern.boottime") 30 | if err != nil { 31 | return 0, err 32 | } 33 | 34 | atomic.StoreUint64(&cachedBootTime, uint64(tv.Sec)) 35 | 36 | return uint64(tv.Sec), nil 37 | } 38 | 39 | // getIOInfos 获取系统IO信息 40 | func (ko *LkkOS) getIOInfos() []byte { 41 | if len(cacheIOInfos) == 0 { 42 | _, cacheIOInfos, _ = ko.System("ioreg -l") 43 | } 44 | return cacheIOInfos 45 | } 46 | 47 | // MemoryUsage 获取内存使用率,单位字节. 48 | // 参数 virtual(仅支持linux),是否取虚拟内存. 49 | // used为已用, 50 | // free为空闲, 51 | // total为总数. 52 | func (ko *LkkOS) MemoryUsage(virtual bool) (used, free, total uint64) { 53 | totalStr, err := unix.Sysctl("hw.memsize") 54 | if err != nil { 55 | return 56 | } 57 | 58 | vm_stat, err := exec.LookPath("vm_stat") 59 | if err != nil { 60 | return 61 | } 62 | 63 | _, out, _ := ko.Exec(vm_stat) 64 | lines := strings.Split(string(out), "\n") 65 | pagesize := uint64(unix.Getpagesize()) 66 | var inactive uint64 67 | for _, line := range lines { 68 | fields := strings.Split(line, ":") 69 | if len(fields) < 2 { 70 | continue 71 | } 72 | key := strings.TrimSpace(fields[0]) 73 | value := strings.Trim(fields[1], " .") 74 | switch key { 75 | case "Pages free": 76 | f, e := strconv.ParseUint(value, 10, 64) 77 | if e != nil { 78 | err = e 79 | } 80 | free = f * pagesize 81 | case "Pages inactive": 82 | ina, e := strconv.ParseUint(value, 10, 64) 83 | if e != nil { 84 | err = e 85 | } 86 | inactive = ina * pagesize 87 | } 88 | } 89 | 90 | // unix.sysctl() helpfully assumes the result is a null-terminated string and 91 | // removes the last byte of the result if it's 0 :/ 92 | totalStr += "\x00" 93 | total = uint64(binary.LittleEndian.Uint64([]byte(totalStr))) 94 | used = total - (free + inactive) 95 | return 96 | } 97 | 98 | // DiskUsage 获取磁盘(目录)使用情况,单位字节.参数path为路径. 99 | // used为已用, 100 | // free为空闲, 101 | // total为总数. 102 | func (ko *LkkOS) DiskUsage(path string) (used, free, total uint64) { 103 | stat := unix.Statfs_t{} 104 | err := unix.Statfs(path, &stat) 105 | if err != nil { 106 | return 107 | } 108 | 109 | total = uint64(stat.Blocks) * uint64(stat.Bsize) 110 | free = uint64(stat.Bavail) * uint64(stat.Bsize) 111 | used = (uint64(stat.Blocks) - uint64(stat.Bfree)) * uint64(stat.Bsize) 112 | return 113 | } 114 | 115 | // Uptime 获取系统运行时间,秒. 116 | func (ko *LkkOS) Uptime() (uint64, error) { 117 | boot, err := bootTime() 118 | if err != nil { 119 | return 0, err 120 | } 121 | 122 | res := uint64(time.Now().Unix()) - boot 123 | return res, nil 124 | } 125 | 126 | // GetBiosInfo 获取BIOS信息. 127 | // 注意:Mac机器没有BIOS信息,它使用EFI,所以该函数将返回空信息. 128 | func (ko *LkkOS) GetBiosInfo() *BiosInfo { 129 | res := &BiosInfo{ 130 | Vendor: "", 131 | Version: "", 132 | Date: "", 133 | } 134 | 135 | return res 136 | } 137 | 138 | // GetBoardInfo 获取Board信息. 139 | func (ko *LkkOS) GetBoardInfo() *BoardInfo { 140 | res := &BoardInfo{ 141 | Name: "", 142 | Vendor: "", 143 | Version: "", 144 | Serial: "", 145 | AssetTag: "", 146 | } 147 | 148 | infos := ko.getIOInfos() 149 | if len(infos) > 0 { 150 | infoStr := string(infos) 151 | res.Name = trim(KStr.GetEquationValue(infoStr, "product-name"), "<", ">", `"`, `'`) 152 | res.Version = trim(KStr.GetEquationValue(infoStr, "board-id"), "<", ">", `"`, `'`) 153 | res.Serial = trim(KStr.GetEquationValue(infoStr, "IOPlatformUUID"), "<", ">", `"`, `'`) 154 | } 155 | 156 | return res 157 | } 158 | 159 | // GetCpuInfo 获取CPU信息. 160 | func (ko *LkkOS) GetCpuInfo() *CpuInfo { 161 | var res = &CpuInfo{ 162 | Vendor: "", 163 | Model: "", 164 | Speed: "", 165 | Cache: 0, 166 | Cpus: 0, 167 | Cores: 0, 168 | Threads: 0, 169 | } 170 | 171 | //调用系统命令获取,例如 172 | //$ sysctl machdep.cpu 173 | //machdep.cpu.cores_per_package: 10 174 | //machdep.cpu.core_count: 10 175 | //machdep.cpu.logical_per_package: 10 176 | //machdep.cpu.thread_count: 10 177 | //machdep.cpu.brand_string: Apple M1 Pro 178 | //machdep.cpu.features: FPU VME DE PSE TSC MSR PAE MCE CX8 APIC SEP MTRR PGE MCA CMOV PAT PSE36 CLFSH DS ACPI MMX FXSR SSE SSE2 SS HTT TM PBE SSE3 PCLMULQDQ DTSE64 MON DSCPL VMX EST TM2 SSSE3 CX16 TPR PDCM SSE4.1 SSE4.2 AES SEGLIM64 179 | //machdep.cpu.feature_bits: 151121000215084031 180 | //machdep.cpu.family: 6 181 | 182 | //打印CPU信息 183 | //cpuInfos, _ := unix.Sysctl("machdep.cpu") 184 | //println("cpuInfos: ", cpuInfos) 185 | 186 | res.Model, _ = unix.Sysctl("machdep.cpu.brand_string") 187 | res.Vendor, _ = unix.Sysctl("machdep.cpu.vendor") //有可能为空 188 | 189 | cacheSize, _ := unix.SysctlUint32("machdep.cpu.cache.size") //有可能为空 190 | cpus, _ := unix.SysctlUint32("hw.physicalcpu") 191 | cores, _ := unix.SysctlUint32("machdep.cpu.core_count") 192 | threads, _ := unix.SysctlUint32("machdep.cpu.thread_count") 193 | 194 | res.Cache = uint(cacheSize) 195 | res.Cpus = uint(cpus) 196 | res.Cores = uint(cores) 197 | res.Threads = uint(threads) 198 | 199 | // Use the rated frequency of the CPU. This is a static value and does not 200 | // account for low power or Turbo Boost modes. 201 | cpuFrequency, err := unix.SysctlUint64("hw.cpufrequency") 202 | if err == nil { 203 | speed := float64(cpuFrequency) / 1000000.0 204 | res.Speed = KNum.NumberFormat(speed, 2, ".", "") 205 | } 206 | 207 | return res 208 | } 209 | 210 | // GetPidByPort 根据端口号获取监听的进程PID. 211 | // linux可能要求root权限; 212 | // darwin依赖lsof; 213 | // windows依赖netstat. 214 | func (ko *LkkOS) GetPidByPort(port int) (pid int) { 215 | command := fmt.Sprintf("lsof -i tcp:%d", port) 216 | _, out, _ := ko.System(command) 217 | lines := strings.Split(string(out), "\n") 218 | for _, line := range lines { 219 | fields := strings.Fields(line) 220 | if len(fields) >= 9 && isNumeric(fields[1]) { 221 | p := toInt(fields[1]) 222 | if p > 0 { 223 | pid = p 224 | break 225 | } 226 | } 227 | } 228 | 229 | return 230 | } 231 | -------------------------------------------------------------------------------- /debug_test.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "errors" 5 | "github.com/stretchr/testify/assert" 6 | "testing" 7 | ) 8 | 9 | func TestDebug_DumpPrint(t *testing.T) { 10 | defer func() { 11 | r := recover() 12 | assert.Empty(t, r) 13 | }() 14 | 15 | KDbug.DumpPrint(Version) 16 | } 17 | 18 | func TestDebug_DumpStacks(t *testing.T) { 19 | defer func() { 20 | r := recover() 21 | assert.Empty(t, r) 22 | }() 23 | 24 | KDbug.DumpStacks() 25 | } 26 | 27 | func TestDebug_GetCallName(t *testing.T) { 28 | defer func() { 29 | r := recover() 30 | assert.NotEmpty(t, r) 31 | }() 32 | 33 | var res string 34 | 35 | res = KDbug.GetCallName(nil, false) 36 | assert.Contains(t, res, "TestDebug_GetCallName") 37 | 38 | res = KDbug.GetCallName(nil, true) 39 | assert.Equal(t, "TestDebug_GetCallName", res) 40 | 41 | res = KDbug.GetCallName("", false) 42 | assert.Empty(t, res) 43 | 44 | res = KDbug.GetCallName(KArr.ArrayRand, false) 45 | assert.Contains(t, res, "ArrayRand") 46 | 47 | res = KDbug.GetCallName(KArr.ArrayRand, true) 48 | assert.Equal(t, "ArrayRand-fm", res) 49 | 50 | //未实现的方法 51 | KDbug.GetCallName(itfObj.noRealize, false) 52 | } 53 | 54 | func BenchmarkDebug_GetCallName(b *testing.B) { 55 | b.ResetTimer() 56 | for i := 0; i < b.N; i++ { 57 | KDbug.GetCallName(KArr.ArrayRand, false) 58 | } 59 | } 60 | 61 | func TestDebug_GetCallFile(t *testing.T) { 62 | res := KDbug.GetCallFile() 63 | assert.NotEmpty(t, res) 64 | } 65 | 66 | func BenchmarkDebug_GetCallFile(b *testing.B) { 67 | b.ResetTimer() 68 | for i := 0; i < b.N; i++ { 69 | KDbug.GetCallFile() 70 | } 71 | } 72 | 73 | func TestDebug_GetCallDir(t *testing.T) { 74 | res := KDbug.GetCallDir() 75 | assert.NotEmpty(t, res) 76 | } 77 | 78 | func BenchmarkDebug_GetCallDir(b *testing.B) { 79 | b.ResetTimer() 80 | for i := 0; i < b.N; i++ { 81 | KDbug.GetCallDir() 82 | } 83 | } 84 | 85 | func TestDebug_GetCallLine(t *testing.T) { 86 | res := KDbug.GetCallLine() 87 | assert.Greater(t, res, 1) 88 | } 89 | 90 | func BenchmarkDebug_GetCallLine(b *testing.B) { 91 | b.ResetTimer() 92 | for i := 0; i < b.N; i++ { 93 | KDbug.GetCallLine() 94 | } 95 | } 96 | 97 | func TestDebug_GetCallPackage(t *testing.T) { 98 | var res string 99 | 100 | res = KDbug.GetCallPackage() 101 | assert.Equal(t, "kgo", res) 102 | 103 | res = KDbug.GetCallPackage(KDbug.GetCallFile()) 104 | assert.Equal(t, "kgo", res) 105 | 106 | res = KDbug.GetCallPackage(strHello) 107 | assert.Empty(t, res) 108 | } 109 | 110 | func BenchmarkDebug_GetCallPackage(b *testing.B) { 111 | b.ResetTimer() 112 | for i := 0; i < b.N; i++ { 113 | KDbug.GetCallPackage() 114 | } 115 | } 116 | 117 | func TestDebug_HasMethod(t *testing.T) { 118 | var tests = []struct { 119 | input interface{} 120 | method string 121 | expected bool 122 | }{ 123 | {intSpeedLight, "", false}, 124 | {strHello, "toString", false}, 125 | {KArr, "InIntSlice", true}, 126 | {&KArr, "InInt64Slice", true}, 127 | {&KArr, strHello, false}, 128 | } 129 | for _, test := range tests { 130 | actual := KDbug.HasMethod(test.input, test.method) 131 | assert.Equal(t, actual, test.expected) 132 | } 133 | } 134 | 135 | func BenchmarkDebug_HasMethod(b *testing.B) { 136 | b.ResetTimer() 137 | for i := 0; i < b.N; i++ { 138 | KDbug.HasMethod(KArr, "SliceFill") 139 | } 140 | } 141 | 142 | func TestDebug_GetMethod(t *testing.T) { 143 | var res interface{} 144 | 145 | res = KDbug.GetMethod(intSpeedLight, "") 146 | assert.Nil(t, res) 147 | 148 | res = KDbug.GetMethod(&KArr, "InIntSlice") 149 | assert.NotNil(t, res) 150 | 151 | res = KDbug.GetMethod(KArr, "InIntSlice") 152 | assert.NotNil(t, res) 153 | 154 | res = KDbug.GetMethod(KArr, strHello) 155 | assert.Nil(t, res) 156 | } 157 | 158 | func BenchmarkDebug_GetMethod(b *testing.B) { 159 | b.ResetTimer() 160 | for i := 0; i < b.N; i++ { 161 | KDbug.GetMethod(&KArr, "InIntSlice") 162 | } 163 | } 164 | 165 | func TestDebug_CallMethod(t *testing.T) { 166 | var res interface{} 167 | var err error 168 | 169 | //调用不存在的方法 170 | res, err = KDbug.CallMethod(KArr, strHello) 171 | assert.NotNil(t, err) 172 | 173 | //有参数调用 174 | res, err = KDbug.CallMethod(&KConv, "BaseConvert", "123456", 10, 16) 175 | assert.Nil(t, err) 176 | assert.NotEmpty(t, res) 177 | 178 | //无参数调用 179 | res, err = KDbug.CallMethod(&KOS, "Getcwd") 180 | assert.Nil(t, err) 181 | assert.NotEmpty(t, res) 182 | } 183 | 184 | func BenchmarkDebug_CallMethod(b *testing.B) { 185 | b.ResetTimer() 186 | for i := 0; i < b.N; i++ { 187 | _, _ = KDbug.CallMethod(&KOS, "Getcwd") 188 | } 189 | } 190 | 191 | func TestDebug_GetFuncNames(t *testing.T) { 192 | var res []string 193 | 194 | //空字符串 195 | res = KDbug.GetFuncNames("") 196 | assert.Empty(t, res) 197 | 198 | //空变量 199 | res = KDbug.GetFuncNames(nil) 200 | assert.Empty(t, res) 201 | 202 | //非指针变量 203 | res = KDbug.GetFuncNames(KStr) 204 | assert.NotEmpty(t, res) 205 | 206 | //指针变量 207 | res = KDbug.GetFuncNames(&KConv) 208 | assert.NotEmpty(t, res) 209 | 210 | n1 := KDbug.GetFuncNames(KFile) 211 | n2 := KDbug.GetFuncNames(KStr) 212 | n3 := KDbug.GetFuncNames(KNum) 213 | n4 := KDbug.GetFuncNames(KArr) 214 | n5 := KDbug.GetFuncNames(KTime) 215 | n6 := KDbug.GetFuncNames(KConv) 216 | n7 := KDbug.GetFuncNames(KOS) 217 | n8 := KDbug.GetFuncNames(KEncr) 218 | n9 := KDbug.GetFuncNames(KDbug) 219 | funTotal := len(n1) + len(n2) + len(n3) + len(n4) + len(n5) + len(n6) + len(n7) + len(n8) + len(n9) 220 | dumpPrint("the package function total:", funTotal) 221 | } 222 | 223 | func BenchmarkDebug_GetFuncNames(b *testing.B) { 224 | b.ResetTimer() 225 | for i := 0; i < b.N; i++ { 226 | KDbug.GetFuncNames(&KConv) 227 | } 228 | } 229 | 230 | func TestDebug_WrapError(t *testing.T) { 231 | var err = errors.New("a test error") 232 | 233 | err1 := KDbug.WrapError(nil) 234 | assert.NotNil(t, err1) 235 | assert.Contains(t, err1.Error(), "parameter error") 236 | 237 | err2 := KDbug.WrapError(err) 238 | assert.NotNil(t, err2) 239 | assert.Equal(t, err2, err) 240 | 241 | err3 := KDbug.WrapError(err, helloEng, utf8Hello) 242 | assert.NotNil(t, err3) 243 | assert.Contains(t, err3.Error(), helloEng) 244 | assert.Contains(t, err3.Error(), err.Error()) 245 | } 246 | 247 | func BenchmarkDebug_WrapError(b *testing.B) { 248 | var err = errors.New("a test error") 249 | b.ResetTimer() 250 | for i := 0; i < b.N; i++ { 251 | _ = KDbug.WrapError(err, helloEng, utf8Hello) 252 | } 253 | } 254 | 255 | func TestDebug_Stacks(t *testing.T) { 256 | var res1, res2, res3 []byte 257 | var len1, len2, len3 int 258 | 259 | res1 = KDbug.Stacks(-1) 260 | len1 = len(res1) 261 | assert.Greater(t, len1, 1) 262 | 263 | res2 = KDbug.Stacks(0) 264 | len2 = len(res2) 265 | assert.Greater(t, len1, len2) 266 | 267 | res3 = KDbug.Stacks(1) 268 | len3 = len(res3) 269 | assert.Greater(t, len2, len3) 270 | } 271 | 272 | func BenchmarkDebug_Stacks(b *testing.B) { 273 | b.ResetTimer() 274 | for i := 0; i < b.N; i++ { 275 | _ = KDbug.Stacks(0) 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /os_linux.go: -------------------------------------------------------------------------------- 1 | //go:build linux 2 | // +build linux 3 | 4 | package kgo 5 | 6 | import ( 7 | "bufio" 8 | "fmt" 9 | "golang.org/x/sys/unix" 10 | "os" 11 | "path/filepath" 12 | "regexp" 13 | "runtime" 14 | "strconv" 15 | "strings" 16 | "syscall" 17 | ) 18 | 19 | // getPidByInode 根据套接字的inode获取PID.须root权限. 20 | func getPidByInode(inode string, procDirs []string) (pid int) { 21 | re := regexp.MustCompile(inode) 22 | for _, item := range procDirs { 23 | path, _ := os.Readlink(item) 24 | out := re.FindString(path) 25 | if len(out) != 0 { 26 | pid, _ = strconv.Atoi(strings.Split(item, "/")[2]) 27 | break 28 | } 29 | } 30 | 31 | return pid 32 | } 33 | 34 | // getProcessPathByPid 根据PID获取进程的执行路径. 35 | func getProcessPathByPid(pid int) (res string) { 36 | exe := fmt.Sprintf("/proc/%d/exe", pid) 37 | res, _ = os.Readlink(exe) 38 | 39 | return 40 | } 41 | 42 | // MemoryUsage 获取内存使用率,单位字节. 43 | // 参数 virtual(仅支持linux),是否取虚拟内存. 44 | // used为已用, 45 | // free为空闲, 46 | // total为总数. 47 | func (ko *LkkOS) MemoryUsage(virtual bool) (used, free, total uint64) { 48 | if virtual { 49 | // 虚拟机的内存 50 | contents, err := os.ReadFile("/proc/meminfo") 51 | if err == nil { 52 | lines := strings.Split(string(contents), "\n") 53 | for _, line := range lines { 54 | fields := strings.Fields(line) 55 | if len(fields) == 3 { 56 | val, _ := strconv.ParseUint(fields[1], 10, 64) // kB 57 | 58 | if strings.HasPrefix(fields[0], "MemTotal") { 59 | total = val * 1024 60 | } else if strings.HasPrefix(fields[0], "MemFree") { 61 | free = val * 1024 62 | } 63 | } 64 | } 65 | 66 | //计算已用内存 67 | used = total - free 68 | } 69 | } else { 70 | // 真实物理机内存 71 | sysi := &syscall.Sysinfo_t{} 72 | err := syscall.Sysinfo(sysi) 73 | if err == nil { 74 | total = sysi.Totalram * uint64(sysi.Unit) 75 | free = sysi.Freeram * uint64(sysi.Unit) 76 | used = total - free 77 | } 78 | } 79 | 80 | return 81 | } 82 | 83 | // CpuUsage 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数). 84 | // user为用户态(用户进程)的运行时间, 85 | // idle为空闲时间, 86 | // total为累计时间. 87 | func (ko *LkkOS) CpuUsage() (user, idle, total uint64) { 88 | contents, _ := os.ReadFile("/proc/stat") 89 | if len(contents) > 0 { 90 | lines := strings.Split(string(contents), "\n") 91 | for _, line := range lines { 92 | fields := strings.Fields(line) 93 | if fields[0] == "cpu" { 94 | //CPU指标:user,nice, system, idle, iowait, irq, softirq 95 | // cpu 130216 19944 162525 1491240 3784 24749 17773 0 0 0 96 | 97 | numFields := len(fields) 98 | for i := 1; i < numFields; i++ { 99 | val, _ := strconv.ParseUint(fields[i], 10, 64) 100 | total += val // tally up all the numbers to get total ticks 101 | if i == 1 { 102 | user = val 103 | } else if i == 4 { // idle is the 5th field in the cpu line 104 | idle = val 105 | } 106 | } 107 | break 108 | } 109 | } 110 | } 111 | 112 | return 113 | } 114 | 115 | // DiskUsage 获取磁盘(目录)使用情况,单位字节.参数path为路径. 116 | // used为已用, 117 | // free为空闲, 118 | // total为总数. 119 | func (ko *LkkOS) DiskUsage(path string) (used, free, total uint64) { 120 | fs := &syscall.Statfs_t{} 121 | err := syscall.Statfs(path, fs) 122 | if err == nil { 123 | total = fs.Blocks * uint64(fs.Bsize) 124 | free = fs.Bfree * uint64(fs.Bsize) 125 | used = total - free 126 | } 127 | 128 | return 129 | } 130 | 131 | // Uptime 获取系统运行时间,秒. 132 | func (ko *LkkOS) Uptime() (res uint64, err error) { 133 | info := &unix.Sysinfo_t{} 134 | err = unix.Sysinfo(info) 135 | if err == nil { 136 | res = uint64(info.Uptime) 137 | } 138 | 139 | return 140 | } 141 | 142 | // GetBiosInfo 获取BIOS信息. 143 | // 注意:Mac机器没有BIOS信息,它使用EFI. 144 | func (ko *LkkOS) GetBiosInfo() *BiosInfo { 145 | return &BiosInfo{ 146 | Vendor: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_vendor"))), 147 | Version: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_version"))), 148 | Date: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/bios_date"))), 149 | } 150 | } 151 | 152 | // GetBoardInfo 获取Board信息. 153 | func (ko *LkkOS) GetBoardInfo() *BoardInfo { 154 | return &BoardInfo{ 155 | Name: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_name"))), 156 | Vendor: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_vendor"))), 157 | Version: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_version"))), 158 | Serial: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_serial"))), 159 | AssetTag: strings.TrimSpace(string(KFile.ReadFirstLine("/sys/class/dmi/id/board_asset_tag"))), 160 | } 161 | } 162 | 163 | // GetCpuInfo 获取CPU信息. 164 | func (ko *LkkOS) GetCpuInfo() *CpuInfo { 165 | var res = &CpuInfo{ 166 | Vendor: "", 167 | Model: "", 168 | Speed: "", 169 | Cache: 0, 170 | Cpus: 0, 171 | Cores: 0, 172 | Threads: 0, 173 | } 174 | 175 | res.Threads = uint(runtime.NumCPU()) 176 | f, err := os.Open("/proc/cpuinfo") 177 | if err == nil { 178 | cpu := make(map[string]bool) 179 | core := make(map[string]bool) 180 | var cpuID string 181 | 182 | s := bufio.NewScanner(f) 183 | for s.Scan() { 184 | if sl := cpuRegTwoColumns.Split(s.Text(), 2); sl != nil { 185 | switch sl[0] { 186 | case "physical id": 187 | cpuID = sl[1] 188 | cpu[cpuID] = true 189 | case "core id": 190 | coreID := fmt.Sprintf("%s/%s", cpuID, sl[1]) 191 | core[coreID] = true 192 | case "vendor_id": 193 | if res.Vendor == "" { 194 | res.Vendor = sl[1] 195 | } 196 | case "model name": 197 | if res.Model == "" { 198 | // CPU model, as reported by /proc/cpuinfo, can be a bit ugly. Clean up... 199 | model := cpuRegExtraSpace.ReplaceAllLiteralString(sl[1], " ") 200 | res.Model = strings.Replace(model, "- ", "-", 1) 201 | } 202 | case "cpu MHz": 203 | if res.Speed == "" { 204 | res.Speed = sl[1] 205 | } 206 | case "cache size": 207 | if res.Cache == 0 { 208 | if m := cpuRegCacheSize.FindStringSubmatch(sl[1]); m != nil { 209 | if cache, err := strconv.ParseUint(m[1], 10, 64); err == nil { 210 | res.Cache = uint(cache) 211 | } 212 | } 213 | } 214 | } 215 | } 216 | } 217 | 218 | res.Cpus = uint(len(cpu)) 219 | res.Cores = uint(len(core)) 220 | } 221 | defer func() { 222 | _ = f.Close() 223 | }() 224 | 225 | return res 226 | } 227 | 228 | // GetPidByPort 根据端口号获取监听的进程PID. 229 | // linux可能要求root权限; 230 | // darwin依赖lsof; 231 | // windows依赖netstat. 232 | func (ko *LkkOS) GetPidByPort(port int) (pid int) { 233 | files := []string{ 234 | "/proc/net/tcp", 235 | "/proc/net/udp", 236 | "/proc/net/tcp6", 237 | "/proc/net/udp6", 238 | } 239 | 240 | procDirs, _ := filepath.Glob("/proc/[0-9]*/fd/[0-9]*") 241 | for _, fpath := range files { 242 | lines, _ := KFile.ReadInArray(fpath) 243 | for _, line := range lines[1:] { 244 | fields := strings.Fields(line) 245 | if len(fields) < 10 { 246 | continue 247 | } 248 | 249 | //非 LISTEN 监听状态 250 | if fields[3] != "0A" { 251 | continue 252 | } 253 | 254 | //本地ip和端口 255 | ipport := strings.Split(fields[1], ":") 256 | locPort, _ := KConv.Hex2Dec(ipport[1]) 257 | 258 | // 非该端口 259 | if int(locPort) != port { 260 | continue 261 | } 262 | 263 | pid = getPidByInode(fields[9], procDirs) 264 | if pid > 0 { 265 | return 266 | } 267 | } 268 | } 269 | 270 | return 271 | } 272 | -------------------------------------------------------------------------------- /convert.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "fmt" 7 | "math" 8 | "net" 9 | "reflect" 10 | "strconv" 11 | ) 12 | 13 | // Struct2Map 结构体转为字典;tagName为要导出的标签名,可以为空,为空时将导出所有字段. 14 | func (kc *LkkConvert) Struct2Map(obj interface{}, tagName string) (map[string]interface{}, error) { 15 | return struct2Map(obj, tagName) 16 | } 17 | 18 | // Int2Str 将整数转换为字符串. 19 | func (kc *LkkConvert) Int2Str(val interface{}) string { 20 | switch val.(type) { 21 | case int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64: 22 | return fmt.Sprintf("%d", val) 23 | default: 24 | r := reflect.ValueOf(val) 25 | switch r.Kind() { 26 | case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int, reflect.Int64, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint, reflect.Uint64: 27 | return fmt.Sprintf("%d", r.Int()) 28 | default: 29 | return "" 30 | } 31 | } 32 | } 33 | 34 | // Float2Str 将浮点数转换为字符串,decimal为小数位数. 35 | func (kc *LkkConvert) Float2Str(val interface{}, decimal int) string { 36 | if decimal <= 0 { 37 | decimal = 2 38 | } 39 | 40 | switch val.(type) { 41 | case float32: 42 | return strconv.FormatFloat(float64(val.(float32)), 'f', decimal, 32) 43 | case float64: 44 | return strconv.FormatFloat(val.(float64), 'f', decimal, 64) 45 | default: 46 | r := reflect.ValueOf(val) 47 | switch r.Kind() { 48 | case reflect.Float32: 49 | return strconv.FormatFloat(r.Float(), 'f', decimal, 32) 50 | case reflect.Float64: 51 | return strconv.FormatFloat(r.Float(), 'f', decimal, 64) 52 | default: 53 | return "" 54 | } 55 | } 56 | } 57 | 58 | // Bool2Str 将布尔值转换为字符串. 59 | func (kc *LkkConvert) Bool2Str(val bool) string { 60 | if val { 61 | return "true" 62 | } 63 | return "false" 64 | } 65 | 66 | // Bool2Int 将布尔值转换为整型. 67 | func (kc *LkkConvert) Bool2Int(val bool) int { 68 | if val { 69 | return 1 70 | } 71 | return 0 72 | } 73 | 74 | // Str2Int 将字符串转换为int.其中"true", "TRUE", "True"为1;若为浮点字符串,则取整数部分. 75 | func (kc *LkkConvert) Str2Int(val string) int { 76 | return str2Int(val) 77 | } 78 | 79 | // Str2Int8 将字符串转换为int8. 80 | func (kc *LkkConvert) Str2Int8(val string) int8 { 81 | res, _ := strconv.ParseInt(val, 0, 8) 82 | return int8(res) 83 | } 84 | 85 | // Str2Int16 将字符串转换为int16. 86 | func (kc *LkkConvert) Str2Int16(val string) int16 { 87 | res, _ := strconv.ParseInt(val, 0, 16) 88 | return int16(res) 89 | } 90 | 91 | // Str2Int32 将字符串转换为int32. 92 | func (kc *LkkConvert) Str2Int32(val string) int32 { 93 | res, _ := strconv.ParseInt(val, 0, 32) 94 | return int32(res) 95 | } 96 | 97 | // Str2Int64 将字符串转换为int64. 98 | func (kc *LkkConvert) Str2Int64(val string) int64 { 99 | res, _ := strconv.ParseInt(val, 0, 64) 100 | return res 101 | } 102 | 103 | // Str2Uint 将字符串转换为uint.其中"true", "TRUE", "True"为1;若为浮点字符串,则取整数部分;若为负值则取0. 104 | func (kc *LkkConvert) Str2Uint(val string) uint { 105 | return str2Uint(val) 106 | } 107 | 108 | // Str2Uint8 将字符串转换为uint8. 109 | func (kc *LkkConvert) Str2Uint8(val string) uint8 { 110 | res, _ := strconv.ParseUint(val, 0, 8) 111 | return uint8(res) 112 | } 113 | 114 | // Str2Uint16 将字符串转换为uint16. 115 | func (kc *LkkConvert) Str2Uint16(val string) uint16 { 116 | res, _ := strconv.ParseUint(val, 0, 16) 117 | return uint16(res) 118 | } 119 | 120 | // Str2Uint32 将字符串转换为uint32. 121 | func (kc *LkkConvert) Str2Uint32(val string) uint32 { 122 | res, _ := strconv.ParseUint(val, 0, 32) 123 | return uint32(res) 124 | } 125 | 126 | // Str2Uint64 将字符串转换为uint64. 127 | func (kc *LkkConvert) Str2Uint64(val string) uint64 { 128 | res, _ := strconv.ParseUint(val, 0, 64) 129 | return res 130 | } 131 | 132 | // Str2Float32 将字符串转换为float32;其中"true", "TRUE", "True"为1.0 . 133 | func (kc *LkkConvert) Str2Float32(val string) float32 { 134 | return str2Float32(val) 135 | } 136 | 137 | // Str2Float64 将字符串转换为float64;其中"true", "TRUE", "True"为1.0 . 138 | func (kc *LkkConvert) Str2Float64(val string) float64 { 139 | return str2Float64(val) 140 | } 141 | 142 | // Str2Bool 将字符串转换为布尔值. 143 | // 1, t, T, TRUE, true, True 等字符串为真; 144 | // 0, f, F, FALSE, false, False 等字符串为假. 145 | func (kc *LkkConvert) Str2Bool(val string) bool { 146 | return str2Bool(val) 147 | } 148 | 149 | // Str2Bytes 将字符串转换为字节切片. 150 | func (kc *LkkConvert) Str2Bytes(val string) []byte { 151 | return str2Bytes(val) 152 | } 153 | 154 | // Bytes2Str 将字节切片转换为字符串. 155 | func (kc *LkkConvert) Bytes2Str(val []byte) string { 156 | return bytes2Str(val) 157 | } 158 | 159 | // Str2BytesUnsafe (非安全的)将字符串转换为字节切片. 160 | // 该方法零拷贝,但不安全.它直接转换底层指针,两者指向的相同的内存,改一个另外一个也会变. 161 | // 仅当临时需将长字符串转换且不长时间保存时可以使用. 162 | // 转换之后若没做其他操作直接改变里面的字符,则程序会崩溃. 163 | // 如 b:=Str2BytesUnsafe("xxx"); b[1]='d'; 程序将panic. 164 | func (kc *LkkConvert) Str2BytesUnsafe(val string) []byte { 165 | return str2BytesUnsafe(val) 166 | } 167 | 168 | // Bytes2StrUnsafe (非安全的)将字节切片转换为字符串. 169 | // 零拷贝,不安全.效率是string([]byte{})的百倍以上,且转换量越大效率优势越明显. 170 | func (kc *LkkConvert) Bytes2StrUnsafe(val []byte) string { 171 | return bytes2StrUnsafe(val) 172 | } 173 | 174 | // Dec2Bin 将十进制转换为二进制字符串. 175 | func (kc *LkkConvert) Dec2Bin(num int64) string { 176 | return dec2Bin(num) 177 | } 178 | 179 | // Bin2Dec 将二进制字符串转换为十进制. 180 | func (kc *LkkConvert) Bin2Dec(str string) (int64, error) { 181 | return bin2Dec(str) 182 | } 183 | 184 | // Hex2Bin 将十六进制字符串转换为二进制字符串. 185 | func (kc *LkkConvert) Hex2Bin(str string) (string, error) { 186 | return hex2Bin(str) 187 | } 188 | 189 | // Bin2Hex 将二进制字符串转换为十六进制字符串. 190 | func (kc *LkkConvert) Bin2Hex(str string) (string, error) { 191 | return bin2Hex(str) 192 | } 193 | 194 | // Dec2Hex 将十进制转换为十六进制. 195 | func (kc *LkkConvert) Dec2Hex(num int64) string { 196 | return dec2Hex(num) 197 | } 198 | 199 | // Hex2Dec 将十六进制转换为十进制. 200 | func (kc *LkkConvert) Hex2Dec(str string) (int64, error) { 201 | return hex2Dec(str) 202 | } 203 | 204 | // Dec2Oct 将十进制转换为八进制. 205 | func (kc *LkkConvert) Dec2Oct(num int64) string { 206 | return dec2Oct(num) 207 | } 208 | 209 | // Oct2Dec 将八进制转换为十进制. 210 | func (kc *LkkConvert) Oct2Dec(str string) (int64, error) { 211 | return oct2Dec(str) 212 | } 213 | 214 | // Runes2Bytes 将[]rune转为[]byte. 215 | func (kc *LkkConvert) Runes2Bytes(rs []rune) []byte { 216 | return runes2Bytes(rs) 217 | } 218 | 219 | // BaseConvert 进制转换,在任意进制之间转换数字. 220 | // num为输入数值,frombase为原进制,tobase为结果进制. 221 | func (kc *LkkConvert) BaseConvert(num string, frombase, tobase int) (string, error) { 222 | i, err := strconv.ParseInt(num, frombase, 0) 223 | if err != nil { 224 | return "", err 225 | } 226 | return strconv.FormatInt(i, tobase), nil 227 | } 228 | 229 | // Ip2Long 将 IPV4 的字符串互联网协议转换成长整型数字. 230 | func (kc *LkkConvert) Ip2Long(ipAddress string) uint32 { 231 | ip := net.ParseIP(ipAddress) 232 | if ip == nil { 233 | return 0 234 | } 235 | return binary.BigEndian.Uint32(ip.To4()) 236 | } 237 | 238 | // Long2Ip 将长整型转化为字符串形式带点的互联网标准格式地址(IPV4). 239 | func (kc *LkkConvert) Long2Ip(properAddress uint32) string { 240 | ipByte := make([]byte, 4) 241 | binary.BigEndian.PutUint32(ipByte, properAddress) 242 | ip := net.IP(ipByte) 243 | return ip.String() 244 | } 245 | 246 | // ToStr 强制将变量转换为字符串. 247 | func (kc *LkkConvert) ToStr(val interface{}) string { 248 | return toStr(val) 249 | } 250 | 251 | // ToBool 强制将变量转换为布尔值. 252 | // 数值类型将检查值是否>0; 253 | // 字符串将使用Str2Bool; 254 | // 数组、切片、字典、通道类型将检查它们的长度是否>0; 255 | // 指针、结构体类型为true,其他为false. 256 | func (kc *LkkConvert) ToBool(val interface{}) bool { 257 | return toBool(val) 258 | } 259 | 260 | // ToInt 强制将变量转换为整型. 261 | // 数值类型将转为整型; 262 | // 字符串类型将使用Str2Int; 263 | // 布尔型的true为1,false为0; 264 | // 数组、切片、字典、通道类型将取它们的长度; 265 | // 指针、结构体类型为1,其他为0. 266 | func (kc *LkkConvert) ToInt(val interface{}) int { 267 | return toInt(val) 268 | } 269 | 270 | // ToFloat 强制将变量转换为浮点型. 271 | // 数值类型将转为浮点型; 272 | // 字符串将使用Str2Float64; 273 | // 布尔型的true为1.0,false为0; 274 | // 数组、切片、字典、通道类型将取它们的长度; 275 | // 指针、结构体类型为1.0,其他为0. 276 | func (kc *LkkConvert) ToFloat(val interface{}) (res float64) { 277 | return toFloat(val) 278 | } 279 | 280 | // Float64ToByte 64位浮点数转字节切片. 281 | func (kc *LkkConvert) Float64ToByte(val float64) []byte { 282 | bits := math.Float64bits(val) 283 | res := make([]byte, 8) 284 | binary.LittleEndian.PutUint64(res, bits) 285 | 286 | return res 287 | } 288 | 289 | // Byte2Float64 字节切片转64位浮点数. 290 | func (kc *LkkConvert) Byte2Float64(bytes []byte) float64 { 291 | bits := binary.LittleEndian.Uint64(bytes) 292 | 293 | return math.Float64frombits(bits) 294 | } 295 | 296 | // Int64ToByte 64位整型转字节切片. 297 | func (kc *LkkConvert) Int64ToByte(val int64) []byte { 298 | res := make([]byte, 8) 299 | binary.BigEndian.PutUint64(res, uint64(val)) 300 | 301 | return res 302 | } 303 | 304 | // Byte2Int64 字节切片转64位整型. 305 | func (kc *LkkConvert) Byte2Int64(val []byte) int64 { 306 | return int64(binary.BigEndian.Uint64(val)) 307 | } 308 | 309 | // Byte2Hex 字节切片转16进制字符串. 310 | func (kc *LkkConvert) Byte2Hex(val []byte) string { 311 | return hex.EncodeToString(val) 312 | } 313 | 314 | // Byte2Hexs 字节切片转16进制切片. 315 | func (kc *LkkConvert) Byte2Hexs(val []byte) []byte { 316 | dst := make([]byte, hex.EncodedLen(len(val))) 317 | hex.Encode(dst, val) 318 | return dst 319 | } 320 | 321 | // Hex2Byte 16进制字符串转字节切片. 322 | func (kc *LkkConvert) Hex2Byte(str string) []byte { 323 | h, _ := hex2Byte(str) 324 | return h 325 | } 326 | 327 | // Hexs2Byte 16进制切片转byte切片. 328 | func (kc *LkkConvert) Hexs2Byte(val []byte) []byte { 329 | dst := make([]byte, hex.DecodedLen(len(val))) 330 | _, err := hex.Decode(dst, val) 331 | 332 | if err != nil { 333 | return nil 334 | } 335 | return dst 336 | } 337 | 338 | // IsString 变量是否字符串. 339 | func (kc *LkkConvert) IsString(val interface{}) bool { 340 | return isString(val) 341 | } 342 | 343 | // IsBinary 字符串是否二进制. 344 | func (kc *LkkConvert) IsBinary(s string) bool { 345 | return isBinary(s) 346 | } 347 | 348 | // IsNumeric 变量是否数值(不包含复数和科学计数法). 349 | func (kc *LkkConvert) IsNumeric(val interface{}) bool { 350 | return isNumeric(val) 351 | } 352 | 353 | // IsInt 变量是否整型数值. 354 | func (kc *LkkConvert) IsInt(val interface{}) bool { 355 | return isInt(val) 356 | } 357 | 358 | // IsFloat 变量是否浮点数值. 359 | func (kc *LkkConvert) IsFloat(val interface{}) bool { 360 | return isFloat(val) 361 | } 362 | 363 | // IsEmpty 变量是否为空. 364 | func (kc *LkkConvert) IsEmpty(val interface{}) bool { 365 | return isEmpty(val) 366 | } 367 | 368 | // IsNil 变量是否nil. 369 | func (kc *LkkConvert) IsNil(val interface{}) bool { 370 | return isNil(val) 371 | } 372 | 373 | // IsBool 是否布尔值. 374 | func (kc *LkkConvert) IsBool(val interface{}) bool { 375 | return isBool(val) 376 | } 377 | 378 | // IsHex 是否十六进制字符串. 379 | func (kc *LkkConvert) IsHex(str string) bool { 380 | return isHex(str) 381 | } 382 | 383 | // IsByte 变量是否字节切片. 384 | func (kc *LkkConvert) IsByte(val interface{}) bool { 385 | return isByte(val) 386 | } 387 | 388 | // IsStruct 变量是否结构体. 389 | func (kc *LkkConvert) IsStruct(val interface{}) bool { 390 | return isStruct(val) 391 | } 392 | 393 | // IsInterface 变量是否接口. 394 | func (kc *LkkConvert) IsInterface(val interface{}) bool { 395 | return isInterface(val) 396 | } 397 | 398 | // IsPort 变量值是否端口号(1~65535). 399 | func (kc *LkkConvert) IsPort(val interface{}) bool { 400 | return isPort(val) 401 | } 402 | 403 | // ToInterfaces 强制将变量转为接口切片;和KArr.ArrayValues相同. 404 | // 其中val类型必须是数组/切片/字典/结构体. 405 | func (kc *LkkConvert) ToInterfaces(val interface{}) []interface{} { 406 | return arrayValues(val, false) 407 | } 408 | -------------------------------------------------------------------------------- /os_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | // +build windows 3 | 4 | package kgo 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "github.com/yusufpapurcu/wmi" 10 | "golang.org/x/sys/windows" 11 | "strings" 12 | "syscall" 13 | "time" 14 | "unsafe" 15 | ) 16 | 17 | //内存状态扩展 18 | type memoryStatusEx struct { 19 | cbSize uint32 20 | dwMemoryLoad uint32 21 | ullTotalPhys uint64 // in bytes 22 | ullAvailPhys uint64 23 | ullTotalPageFile uint64 24 | ullAvailPageFile uint64 25 | ullTotalVirtual uint64 26 | ullAvailVirtual uint64 27 | ullAvailExtendedVirtual uint64 28 | } 29 | 30 | type fileTime struct { 31 | DwLowDateTime uint32 32 | DwHighDateTime uint32 33 | } 34 | 35 | type win32BIOS struct { 36 | InstallDate *string 37 | Manufacturer *string 38 | Version *string 39 | } 40 | 41 | type win32Baseboard struct { 42 | Manufacturer *string 43 | SerialNumber *string 44 | Tag *string 45 | Version *string 46 | Product *string 47 | } 48 | 49 | type win32Processor struct { 50 | Manufacturer *string 51 | Name *string 52 | NumberOfLogicalProcessors uint32 53 | NumberOfCores uint32 54 | MaxClockSpeed uint32 55 | L2CacheSize uint32 56 | L3CacheSize uint32 57 | } 58 | 59 | type Win32_Process struct { 60 | Name string 61 | ExecutablePath *string 62 | CommandLine *string 63 | Priority uint32 64 | CreationDate *time.Time 65 | ProcessId uint32 66 | ThreadCount uint32 67 | Status *string 68 | ReadOperationCount uint64 69 | ReadTransferCount uint64 70 | WriteOperationCount uint64 71 | WriteTransferCount uint64 72 | CSCreationClassName string 73 | CSName string 74 | Caption *string 75 | CreationClassName string 76 | Description *string 77 | ExecutionState *uint16 78 | HandleCount uint32 79 | KernelModeTime uint64 80 | MaximumWorkingSetSize *uint32 81 | MinimumWorkingSetSize *uint32 82 | OSCreationClassName string 83 | OSName string 84 | OtherOperationCount uint64 85 | OtherTransferCount uint64 86 | PageFaults uint32 87 | PageFileUsage uint32 88 | ParentProcessID uint32 89 | PeakPageFileUsage uint32 90 | PeakVirtualSize uint64 91 | PeakWorkingSetSize uint32 92 | PrivatePageCount uint64 93 | TerminationDate *time.Time 94 | UserModeTime uint64 95 | WorkingSetSize uint64 96 | } 97 | 98 | var ( 99 | kernel32 = windows.NewLazySystemDLL("kernel32.dll") 100 | 101 | procGlobalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx") 102 | procGetSystemTimes = kernel32.NewProc("GetSystemTimes") 103 | procGetDiskFreeSpaceExW = kernel32.NewProc("GetDiskFreeSpaceExW") 104 | procGetTickCount64 = kernel32.NewProc("GetTickCount64") 105 | procGetTickCount32 = kernel32.NewProc("GetTickCount") 106 | ) 107 | 108 | // getProcessByPid 根据pid获取进程列表. 109 | func getProcessByPid(pid int) (res []Win32_Process) { 110 | var dst []Win32_Process 111 | query := fmt.Sprintf("WHERE ProcessId = %d", pid) 112 | q := wmi.CreateQuery(&dst, query) 113 | err := wmi.Query(q, &dst) 114 | if err == nil && len(dst) > 0 { 115 | res = dst 116 | } 117 | 118 | return 119 | } 120 | 121 | // getProcessPathByPid 根据PID获取进程的执行路径. 122 | func getProcessPathByPid(pid int) (res string) { 123 | ps := getProcessByPid(pid) 124 | if len(ps) > 0 { 125 | res = *ps[0].ExecutablePath 126 | } 127 | 128 | return 129 | } 130 | 131 | // MemoryUsage 获取内存使用率,单位字节. 132 | // 参数 virtual(仅支持linux),是否取虚拟内存. 133 | // used为已用, 134 | // free为空闲, 135 | // total为总数. 136 | func (ko *LkkOS) MemoryUsage(virtual bool) (used, free, total uint64) { 137 | var memInfo memoryStatusEx 138 | memInfo.cbSize = uint32(unsafe.Sizeof(memInfo)) 139 | mem, _, _ := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memInfo))) 140 | if mem > 0 { 141 | total = memInfo.ullTotalPhys 142 | free = memInfo.ullAvailPhys 143 | used = memInfo.ullTotalPhys - memInfo.ullAvailPhys 144 | } 145 | 146 | return 147 | } 148 | 149 | // CpuUsage 获取CPU使用率(darwin系统必须使用cgo),单位jiffies(节拍数). 150 | // user为用户态(用户进程)的运行时间, 151 | // idle为空闲时间, 152 | // total为累计时间. 153 | func (ko *LkkOS) CpuUsage() (user, idle, total uint64) { 154 | var lpIdleTime fileTime 155 | var lpKernelTime fileTime 156 | var lpUserTime fileTime 157 | r, _, _ := procGetSystemTimes.Call( 158 | uintptr(unsafe.Pointer(&lpIdleTime)), 159 | uintptr(unsafe.Pointer(&lpKernelTime)), 160 | uintptr(unsafe.Pointer(&lpUserTime))) 161 | 162 | if r > 0 { 163 | LOT := float64(0.0000001) 164 | HIT := (LOT * 4294967296.0) 165 | tmpIdle := ((HIT * float64(lpIdleTime.DwHighDateTime)) + (LOT * float64(lpIdleTime.DwLowDateTime))) 166 | tmpUser := ((HIT * float64(lpUserTime.DwHighDateTime)) + (LOT * float64(lpUserTime.DwLowDateTime))) 167 | tmpKernel := ((HIT * float64(lpKernelTime.DwHighDateTime)) + (LOT * float64(lpKernelTime.DwLowDateTime))) 168 | //tmpSystem := (tmpKernel - tmpIdle) 169 | 170 | user = uint64(tmpUser) 171 | idle = uint64(tmpIdle) 172 | total = user + idle + uint64(tmpKernel) 173 | } 174 | 175 | return 176 | } 177 | 178 | // DiskUsage 获取磁盘(目录)使用情况,单位字节.参数path为路径. 179 | // used为已用, 180 | // free为空闲, 181 | // total为总数. 182 | func (ko *LkkOS) DiskUsage(path string) (used, free, total uint64) { 183 | lpFreeBytesAvailable := int64(0) 184 | lpTotalNumberOfBytes := int64(0) 185 | lpTotalNumberOfFreeBytes := int64(0) 186 | diskret, _, _ := procGetDiskFreeSpaceExW.Call( 187 | uintptr(unsafe.Pointer(windows.StringToUTF16Ptr(path))), 188 | uintptr(unsafe.Pointer(&lpFreeBytesAvailable)), 189 | uintptr(unsafe.Pointer(&lpTotalNumberOfBytes)), 190 | uintptr(unsafe.Pointer(&lpTotalNumberOfFreeBytes))) 191 | 192 | if diskret > 0 { 193 | total = uint64(lpTotalNumberOfBytes) 194 | free = uint64(lpTotalNumberOfFreeBytes) 195 | used = uint64(lpTotalNumberOfBytes - lpTotalNumberOfFreeBytes) 196 | } 197 | 198 | return 199 | } 200 | 201 | // Uptime 获取系统运行时间,秒. 202 | func (ko *LkkOS) Uptime() (uint64, error) { 203 | var res uint64 204 | var err error 205 | 206 | procGetTickCount := procGetTickCount64 207 | chkErr := procGetTickCount64.Find() 208 | if chkErr != nil { 209 | // handle WinXP, but keep in mind that "the time will wrap around to zero if the system is run continuously for 49.7 days." from MSDN 210 | procGetTickCount = procGetTickCount32 211 | } 212 | 213 | ret, _, errno := syscall.Syscall(procGetTickCount.Addr(), 0, 0, 0, 0) 214 | if errno == 0 { 215 | res = uint64((time.Duration(ret) * time.Millisecond).Seconds()) 216 | } else { 217 | err = errors.New(errno.Error()) 218 | } 219 | 220 | return res, err 221 | } 222 | 223 | // GetBiosInfo 获取BIOS信息. 224 | // 注意:Mac机器没有BIOS信息,它使用EFI. 225 | func (ko *LkkOS) GetBiosInfo() *BiosInfo { 226 | res := &BiosInfo{ 227 | Vendor: "", 228 | Version: "", 229 | Date: "", 230 | } 231 | 232 | // Getting data from WMI 233 | var win32BIOSDescriptions []win32BIOS 234 | if err := wmi.Query("SELECT InstallDate, Manufacturer, Version FROM CIM_BIOSElement", &win32BIOSDescriptions); err == nil { 235 | if len(win32BIOSDescriptions) > 0 { 236 | res.Vendor = *win32BIOSDescriptions[0].Manufacturer 237 | res.Version = *win32BIOSDescriptions[0].Version 238 | res.Date = *win32BIOSDescriptions[0].InstallDate 239 | } 240 | } 241 | 242 | return res 243 | } 244 | 245 | // GetBoardInfo 获取Board信息. 246 | func (ko *LkkOS) GetBoardInfo() *BoardInfo { 247 | res := &BoardInfo{ 248 | Name: "", 249 | Vendor: "", 250 | Version: "", 251 | Serial: "", 252 | AssetTag: "", 253 | } 254 | 255 | // Getting data from WMI 256 | var win32BaseboardDescriptions []win32Baseboard 257 | if err := wmi.Query("SELECT Manufacturer, SerialNumber, Tag, Version, Product FROM Win32_BaseBoard", &win32BaseboardDescriptions); err == nil { 258 | if len(win32BaseboardDescriptions) > 0 { 259 | res.Name = *win32BaseboardDescriptions[0].Product 260 | res.Vendor = *win32BaseboardDescriptions[0].Manufacturer 261 | res.Version = *win32BaseboardDescriptions[0].Version 262 | res.Serial = *win32BaseboardDescriptions[0].SerialNumber 263 | res.AssetTag = *win32BaseboardDescriptions[0].Tag 264 | } 265 | } 266 | 267 | return res 268 | } 269 | 270 | // GetCpuInfo 获取CPU信息. 271 | func (ko *LkkOS) GetCpuInfo() *CpuInfo { 272 | var res = &CpuInfo{ 273 | Vendor: "", 274 | Model: "", 275 | Speed: "", 276 | Cache: 0, 277 | Cpus: 0, 278 | Cores: 0, 279 | Threads: 0, 280 | } 281 | 282 | // Getting info from WMI 283 | var win32descriptions []win32Processor 284 | if err := wmi.Query("SELECT Manufacturer, Name, NumberOfLogicalProcessors, NumberOfCores, MaxClockSpeed, L2CacheSize, L3CacheSize FROM Win32_Processor", &win32descriptions); err == nil { 285 | var cores, threads uint 286 | for _, description := range win32descriptions { 287 | if res.Vendor == "" { 288 | res.Vendor = *description.Manufacturer 289 | } 290 | if res.Model == "" { 291 | res.Model = *description.Name 292 | } 293 | if res.Speed == "" { 294 | res.Speed = toStr(description.MaxClockSpeed) 295 | } 296 | if res.Cache == 0 { 297 | res.Cache = uint(description.L2CacheSize + description.L3CacheSize) 298 | } 299 | 300 | cores += uint(description.NumberOfCores) 301 | threads += uint(description.NumberOfLogicalProcessors) 302 | } 303 | 304 | res.Cpus = uint(len(win32descriptions)) 305 | res.Cores = cores 306 | res.Threads = threads 307 | } 308 | 309 | return res 310 | } 311 | 312 | // IsProcessExists 进程是否存在. 313 | func (ko *LkkOS) IsProcessExists(pid int) (res bool) { 314 | if pid <= 0 { 315 | return 316 | } else if pid%4 != 0 { 317 | // OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043 318 | // so we list every pid just to be sure and be future-proof 319 | ps := getProcessByPid(pid) 320 | if len(ps) > 0 && ps[0].ProcessId > 0 { 321 | return true 322 | } 323 | } else { 324 | var still_active uint32 = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess 325 | h, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, uint32(pid)) 326 | if err != nil { 327 | if err == windows.ERROR_ACCESS_DENIED { 328 | res = true 329 | } 330 | return 331 | } 332 | 333 | defer func() { 334 | _ = syscall.CloseHandle(syscall.Handle(h)) 335 | }() 336 | var exitCode uint32 337 | _ = windows.GetExitCodeProcess(h, &exitCode) 338 | res = exitCode == still_active 339 | } 340 | 341 | return 342 | } 343 | 344 | // GetPidByPort 根据端口号获取监听的进程PID. 345 | // linux可能要求root权限; 346 | // darwin依赖lsof; 347 | // windows依赖netstat. 348 | func (ko *LkkOS) GetPidByPort(port int) (pid int) { 349 | command := fmt.Sprintf("cmd /C netstat -ano | findstr %d", port) 350 | _, out, _ := ko.System(command) 351 | lines := strings.Split(string(out), "\r\n") 352 | for _, line := range lines { 353 | fields := strings.Fields(line) 354 | if len(fields) == 5 && isNumeric(fields[4]) { 355 | p := toInt(fields[4]) 356 | if p > 0 { 357 | pid = p 358 | break 359 | } 360 | } 361 | } 362 | 363 | return 364 | } 365 | -------------------------------------------------------------------------------- /time.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "time" 7 | ) 8 | 9 | // DateFormat pattern rules. 10 | var datePatterns = []string{ 11 | // year 12 | "Y", "2006", // A full numeric representation of a year, 4 digits Examples: 1999 or 2003 13 | "y", "06", // A two digit representation of a year Examples: 99 or 03 14 | 15 | // month 16 | "m", "01", // Numeric representation of a month, with leading zeros 01 through 12 17 | "n", "1", // Numeric representation of a month, without leading zeros 1 through 12 18 | "M", "Jan", // A short textual representation of a month, three letters Jan through Dec 19 | "F", "January", // A full textual representation of a month, such as January or March January through December 20 | 21 | // day 22 | "d", "02", // Day of the month, 2 digits with leading zeros 01 to 31 23 | "j", "2", // Day of the month without leading zeros 1 to 31 24 | 25 | // week 26 | "D", "Mon", // A textual representation of a day, three letters Mon through Sun 27 | "l", "Monday", // A full textual representation of the day of the week Sunday through Saturday 28 | 29 | // time 30 | "g", "3", // 12-hour format of an hour without leading zeros 1 through 12 31 | "G", "15", // 24-hour format of an hour without leading zeros 0 through 23 32 | "h", "03", // 12-hour format of an hour with leading zeros 01 through 12 33 | "H", "15", // 24-hour format of an hour with leading zeros 00 through 23 34 | 35 | "a", "pm", // Lowercase Ante meridiem and Post meridiem am or pm 36 | "A", "PM", // Uppercase Ante meridiem and Post meridiem AM or PM 37 | 38 | "i", "04", // Minutes with leading zeros 00 to 59 39 | "s", "05", // Seconds, with leading zeros 00 through 59 40 | 41 | // time zone 42 | "T", "MST", 43 | "P", "-07:00", 44 | "O", "-0700", 45 | 46 | // RFC 2822 47 | "r", time.RFC1123Z, 48 | } 49 | 50 | // UnixTime 获取当前Unix时间戳(秒,10位). 51 | func (kt *LkkTime) UnixTime() int64 { 52 | return time.Now().Unix() 53 | } 54 | 55 | // MilliTime 获取当前Unix时间戳(毫秒,13位). 56 | func (kt *LkkTime) MilliTime() int64 { 57 | return time.Now().UnixNano() / int64(time.Millisecond) 58 | } 59 | 60 | // MicroTime 获取当前Unix时间戳(微秒,16位). 61 | func (kt *LkkTime) MicroTime() int64 { 62 | return time.Now().UnixNano() / int64(time.Microsecond) 63 | } 64 | 65 | // Str2Timestruct 将字符串转换为时间结构,默认为本地时区. 66 | // str 为要转换的字符串; 67 | // option 为时间选项TimeOption. 68 | func (kt *LkkTime) Str2Timestruct(str string, option ...TimeOption) (res time.Time, err error) { 69 | var opt TimeOption 70 | if len(option) > 0 { 71 | opt = option[0] 72 | } else { 73 | opt = DefaultTimeOption 74 | } 75 | if opt.Layout == "" { 76 | opt.Layout = DefaultTimeOption.Layout 77 | } 78 | 79 | if opt.IsUTC { 80 | res, err = time.Parse(opt.Layout, str) 81 | } else if opt.Location != nil { 82 | res, err = time.ParseInLocation(opt.Layout, str, opt.Location) 83 | } else if opt.Zone != "" { 84 | var loc *time.Location 85 | loc, err = time.LoadLocation(opt.Zone) 86 | if err == nil { 87 | res, err = time.ParseInLocation(opt.Layout, str, loc) 88 | } 89 | } else { 90 | res, err = time.ParseInLocation(opt.Layout, str, kuptime.Location()) 91 | } 92 | 93 | return 94 | } 95 | 96 | // Str2Timestamp 将字符串转换为时间戳(默认为本地时区),秒. 97 | // str 为要转换的字符串; 98 | // option 为时间选项TimeOption. 99 | func (kt *LkkTime) Str2Timestamp(str string, option ...TimeOption) (int64, error) { 100 | tim, err := kt.Str2Timestruct(str, option...) 101 | if err != nil { 102 | return 0, err 103 | } 104 | 105 | return tim.Unix(), nil 106 | } 107 | 108 | // Date 格式化时间. 109 | // format 格式,如"Y-m-d H:i:s". 110 | // ts为int/int64类型时间戳或time.Time类型. 111 | func (kt *LkkTime) Date(format string, ts ...interface{}) string { 112 | replacer := strings.NewReplacer(datePatterns...) 113 | format = replacer.Replace(format) 114 | 115 | var t time.Time 116 | if len(ts) > 0 { 117 | val := ts[0] 118 | if v, ok := val.(time.Time); ok { 119 | t = v 120 | } else if v, ok := val.(int); ok { 121 | t = time.Unix(int64(v), 0) 122 | } else if v, ok := val.(int64); ok { 123 | t = time.Unix(v, 0) 124 | } else { 125 | return "" 126 | } 127 | } else { 128 | t = time.Now() 129 | } 130 | 131 | return t.Format(format) 132 | } 133 | 134 | // CheckDate 检查是否正常的日期. 135 | func (kt *LkkTime) CheckDate(year, month, day int) bool { 136 | if month < 1 || month > 12 || day < 1 || day > 31 || year < 1 || year > 32767 { 137 | return false 138 | } 139 | switch month { 140 | case 4, 6, 9, 11: 141 | if day > 30 { 142 | return false 143 | } 144 | case 2: 145 | // leap year 146 | if year%4 == 0 && (year%100 != 0 || year%400 == 0) { 147 | if day > 29 { 148 | return false 149 | } 150 | } else if day > 28 { 151 | return false 152 | } 153 | } 154 | 155 | return true 156 | } 157 | 158 | // Sleep 延缓执行,秒. 159 | func (kt *LkkTime) Sleep(t int64) { 160 | time.Sleep(time.Duration(t) * time.Second) 161 | } 162 | 163 | // Usleep 以指定的微秒数延迟执行. 164 | func (kt *LkkTime) Usleep(t int64) { 165 | time.Sleep(time.Duration(t) * time.Microsecond) 166 | } 167 | 168 | // ServiceStartime 获取当前服务启动时间戳,秒. 169 | func (kt *LkkTime) ServiceStartime() int64 { 170 | return kuptime.Unix() 171 | } 172 | 173 | // ServiceUptime 获取当前服务运行时间,纳秒int64. 174 | func (kt *LkkTime) ServiceUptime() time.Duration { 175 | return time.Since(kuptime) 176 | } 177 | 178 | // GetMonthDays 获取指定月份的天数.year年份,可选,默认当前年份. 179 | func (kt *LkkTime) GetMonthDays(month int, year ...int) (days int) { 180 | if month < 1 || month > 12 { 181 | return 182 | } 183 | 184 | var yr int 185 | if len(year) == 0 { 186 | yr = time.Now().Year() 187 | } else { 188 | yr = year[0] 189 | } 190 | 191 | if month != 2 { 192 | if month == 4 || month == 6 || month == 9 || month == 11 { 193 | days = 30 194 | } else { 195 | days = 31 196 | } 197 | } else { 198 | if ((yr%4) == 0 && (yr%100) != 0) || (yr%400) == 0 { 199 | days = 29 200 | } else { 201 | days = 28 202 | } 203 | } 204 | 205 | return 206 | } 207 | 208 | // Year 获取年份. 209 | func (kt *LkkTime) Year(t ...time.Time) int { 210 | var tm time.Time 211 | if len(t) > 0 { 212 | tm = t[0] 213 | } else { 214 | tm = time.Now() 215 | } 216 | return tm.Year() 217 | } 218 | 219 | // Month 获取月份. 220 | func (kt *LkkTime) Month(t ...time.Time) int { 221 | var tm time.Time 222 | if len(t) > 0 { 223 | tm = t[0] 224 | } else { 225 | tm = time.Now() 226 | } 227 | return int(tm.Month()) 228 | } 229 | 230 | // Day 获取日份. 231 | func (kt *LkkTime) Day(t ...time.Time) int { 232 | var tm time.Time 233 | if len(t) > 0 { 234 | tm = t[0] 235 | } else { 236 | tm = time.Now() 237 | } 238 | return tm.Day() 239 | } 240 | 241 | // Hour 获取小时. 242 | func (kt *LkkTime) Hour(t ...time.Time) int { 243 | var tm time.Time 244 | if len(t) > 0 { 245 | tm = t[0] 246 | } else { 247 | tm = time.Now() 248 | } 249 | return tm.Hour() 250 | } 251 | 252 | // Minute 获取分钟. 253 | func (kt *LkkTime) Minute(t ...time.Time) int { 254 | var tm time.Time 255 | if len(t) > 0 { 256 | tm = t[0] 257 | } else { 258 | tm = time.Now() 259 | } 260 | return tm.Minute() 261 | } 262 | 263 | // Second 获取秒数. 264 | func (kt *LkkTime) Second(t ...time.Time) int { 265 | var tm time.Time 266 | if len(t) > 0 { 267 | tm = t[0] 268 | } else { 269 | tm = time.Now() 270 | } 271 | return tm.Second() 272 | } 273 | 274 | // StartOfDay 获取日期中当天的开始时间. 275 | func (kt *LkkTime) StartOfDay(date time.Time) time.Time { 276 | return time.Date(date.Year(), date.Month(), date.Day(), 0, 0, 0, 0, date.Location()) 277 | } 278 | 279 | // EndOfDay 获取日期中当天的结束时间. 280 | func (kt *LkkTime) EndOfDay(date time.Time) time.Time { 281 | return time.Date(date.Year(), date.Month(), date.Day(), 23, 59, 59, int(time.Second-time.Nanosecond), date.Location()) 282 | } 283 | 284 | // StartOfMonth 获取日期中当月的开始时间. 285 | func (kt *LkkTime) StartOfMonth(date time.Time) time.Time { 286 | return time.Date(date.Year(), date.Month(), 1, 0, 0, 0, 0, date.Location()) 287 | } 288 | 289 | // EndOfMonth 获取日期中当月的结束时间. 290 | func (kt *LkkTime) EndOfMonth(date time.Time) time.Time { 291 | return kt.StartOfMonth(date).AddDate(0, 1, 0).Add(-time.Nanosecond) 292 | } 293 | 294 | // StartOfYear 获取日期中当年的开始时间. 295 | func (kt *LkkTime) StartOfYear(date time.Time) time.Time { 296 | return time.Date(date.Year(), 1, 1, 0, 0, 0, 0, date.Location()) 297 | } 298 | 299 | // EndOfYear 获取日期中当年的结束时间. 300 | func (kt *LkkTime) EndOfYear(date time.Time) time.Time { 301 | return kt.StartOfYear(date).AddDate(1, 0, 0).Add(-time.Nanosecond) 302 | } 303 | 304 | // StartOfWeek 获取日期中当周的开始时间; 305 | // weekStartDay 周几作为周的第一天,本库默认周一. 306 | func (kt *LkkTime) StartOfWeek(date time.Time, weekStartDay ...time.Weekday) time.Time { 307 | weekstart := time.Monday 308 | if len(weekStartDay) > 0 { 309 | weekstart = weekStartDay[0] 310 | } 311 | 312 | // 当前是周几 313 | weekday := int(date.Weekday()) 314 | if weekstart != time.Sunday { 315 | weekStartDayInt := int(weekstart) 316 | 317 | if weekday < weekStartDayInt { 318 | weekday = weekday + 7 - weekStartDayInt 319 | } else { 320 | weekday = weekday - weekStartDayInt 321 | } 322 | } 323 | 324 | return time.Date(date.Year(), date.Month(), date.Day()-weekday, 0, 0, 0, 0, date.Location()) 325 | } 326 | 327 | // EndOfWeek 获取日期中当周的结束时间; 328 | // weekStartDay 周几作为周的第一天,本库默认周一. 329 | func (kt *LkkTime) EndOfWeek(date time.Time, weekStartDay ...time.Weekday) time.Time { 330 | return kt.StartOfWeek(date, weekStartDay...).AddDate(0, 0, 7).Add(-time.Nanosecond) 331 | } 332 | 333 | // DaysBetween 获取两个日期的间隔天数. 334 | // fromDate 为开始时间,toDate 为终点时间. 335 | func (kt *LkkTime) DaysBetween(fromDate, toDate time.Time) int { 336 | return int(toDate.Sub(fromDate) / (24 * time.Hour)) 337 | } 338 | 339 | // IsDate2time 检查字符串是否日期格式,并转换为时间戳.注意,时间戳可能为负数(小于1970年时). 340 | // option 为时间选项TimeOption,默认为本地时区. 341 | // 匹配如: 342 | // 343 | // 0000 344 | // 0000-00 345 | // 0000/00 346 | // 0000-00-00 347 | // 0000/00/00 348 | // 0000-00-00 00 349 | // 0000/00/00 00 350 | // 0000-00-00 00:00 351 | // 0000/00/00 00:00 352 | // 0000-00-00 00:00:00 353 | // 0000/00/00 00:00:00 354 | // 355 | // 等日期格式. 356 | func (kt *LkkTime) IsDate2time(str string, option ...TimeOption) (bool, int64) { 357 | if str == "" { 358 | return false, 0 359 | } else if strings.ContainsRune(str, '/') { 360 | str = strings.Replace(str, "/", "-", -1) 361 | } 362 | 363 | chk := RegDatetime.MatchString(str) 364 | if !chk { 365 | return false, 0 366 | } 367 | 368 | leng := len(str) 369 | if leng < 19 { 370 | reference := "1970-01-01 00:00:00" 371 | str = str + reference[leng:19] 372 | } 373 | 374 | ts, err := KTime.Str2Timestamp(str, option...) 375 | if err != nil { 376 | return false, 0 377 | } 378 | 379 | return true, ts 380 | } 381 | 382 | // FormatDuration 格式化时长为字符串(如*h*m*s);其中t为时长,默认为秒;colon为是否使用冒号. 383 | func (kt *LkkTime) FormatDuration(t interface{}, colon bool) string { 384 | var dr time.Duration 385 | var res string 386 | 387 | if v, ok := t.(time.Duration); ok { 388 | dr = v 389 | } else { 390 | i := toInt(t) 391 | dr = time.Second * time.Duration(i) 392 | } 393 | 394 | if colon { 395 | h := dr / time.Hour 396 | dr -= h * time.Hour 397 | m := dr / time.Minute 398 | dr -= m * time.Minute 399 | s := dr / time.Second 400 | res = fmt.Sprintf("%d:%02d:%02d", h, m, s) 401 | } else { 402 | s := dr.Round(time.Millisecond).String() 403 | if strings.HasSuffix(s, "m0s") { 404 | s = s[:len(s)-2] 405 | } 406 | if strings.HasSuffix(s, "h0m") { 407 | s = s[:len(s)-2] 408 | } 409 | 410 | res = s 411 | } 412 | 413 | return res 414 | } 415 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /os_test.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "encoding/binary" 5 | "github.com/stretchr/testify/assert" 6 | "net" 7 | "net/http" 8 | "os/user" 9 | "strings" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | func TestOS_Pwd(t *testing.T) { 15 | res := KOS.Pwd() 16 | assert.NotEmpty(t, res) 17 | } 18 | 19 | func BenchmarkOS_Pwd(b *testing.B) { 20 | b.ResetTimer() 21 | for i := 0; i < b.N; i++ { 22 | KOS.Pwd() 23 | } 24 | } 25 | 26 | func TestOS_Getcwd_Chdir(t *testing.T) { 27 | var ori, res string 28 | var err error 29 | 30 | ori, err = KOS.Getcwd() 31 | assert.Nil(t, err) 32 | assert.NotEmpty(t, ori) 33 | 34 | //切换目录 35 | err = KOS.Chdir(dirTdat) 36 | assert.Nil(t, err) 37 | 38 | res, err = KOS.Getcwd() 39 | assert.Nil(t, err) 40 | 41 | //返回原来目录 42 | err = KOS.Chdir(ori) 43 | assert.Nil(t, err) 44 | assert.Equal(t, KFile.AbsPath(res), KFile.AbsPath(dirTdat)) 45 | } 46 | 47 | func BenchmarkOS_Getcwd(b *testing.B) { 48 | b.ResetTimer() 49 | for i := 0; i < b.N; i++ { 50 | _, _ = KOS.Getcwd() 51 | } 52 | } 53 | 54 | func BenchmarkOS_Chdir(b *testing.B) { 55 | b.ResetTimer() 56 | dir := KOS.Pwd() 57 | for i := 0; i < b.N; i++ { 58 | _ = KOS.Chdir(dir) 59 | } 60 | } 61 | 62 | func TestOS_LocalIP(t *testing.T) { 63 | res, err := KOS.LocalIP() 64 | assert.Nil(t, err) 65 | assert.NotEmpty(t, res) 66 | } 67 | 68 | func BenchmarkOS_LocalIP(b *testing.B) { 69 | b.ResetTimer() 70 | for i := 0; i < b.N; i++ { 71 | _, _ = KOS.LocalIP() 72 | } 73 | } 74 | 75 | func TestOS_OutboundIP(t *testing.T) { 76 | res, err := KOS.OutboundIP() 77 | assert.Nil(t, err) 78 | assert.NotEmpty(t, res) 79 | } 80 | 81 | func BenchmarkOS_OutboundIP(b *testing.B) { 82 | b.ResetTimer() 83 | for i := 0; i < b.N; i++ { 84 | _, _ = KOS.OutboundIP() 85 | } 86 | } 87 | 88 | func TestOS_IsPrivateIp(t *testing.T) { 89 | var res bool 90 | var err error 91 | 92 | res, err = KOS.IsPrivateIp(lanIp) 93 | assert.Nil(t, err) 94 | assert.True(t, res) 95 | 96 | res, err = KOS.IsPrivateIp(publicIp2) 97 | assert.Nil(t, err) 98 | assert.False(t, res) 99 | 100 | //非IP 101 | res, err = KOS.IsPrivateIp(strHello) 102 | assert.NotNil(t, err) 103 | } 104 | 105 | func BenchmarkOS_IsPrivateIp(b *testing.B) { 106 | b.ResetTimer() 107 | for i := 0; i < b.N; i++ { 108 | _, _ = KOS.IsPrivateIp(lanIp) 109 | } 110 | } 111 | 112 | func TestOS_IsPublicIP(t *testing.T) { 113 | var res bool 114 | var err error 115 | 116 | res, err = KOS.IsPublicIP(localIp) 117 | assert.Nil(t, err) 118 | assert.False(t, res) 119 | 120 | res, err = KOS.IsPublicIP(lanIp) 121 | assert.Nil(t, err) 122 | assert.False(t, res) 123 | 124 | res, err = KOS.IsPublicIP(googleIpv4) 125 | assert.Nil(t, err) 126 | assert.True(t, res) 127 | 128 | res, err = KOS.IsPublicIP(googleIpv6) 129 | assert.Nil(t, err) 130 | assert.True(t, res) 131 | 132 | //非IP 133 | res, err = KOS.IsPublicIP(strHello) 134 | assert.NotNil(t, err) 135 | } 136 | 137 | func BenchmarkOS_IsPublicIP(b *testing.B) { 138 | b.ResetTimer() 139 | for i := 0; i < b.N; i++ { 140 | _, _ = KOS.IsPublicIP(publicIp1) 141 | } 142 | } 143 | 144 | func TestOS_GetIPs(t *testing.T) { 145 | res := KOS.GetIPs() 146 | assert.NotEmpty(t, res) 147 | } 148 | 149 | func BenchmarkOS_GetIPs(b *testing.B) { 150 | b.ResetTimer() 151 | for i := 0; i < b.N; i++ { 152 | KOS.GetIPs() 153 | } 154 | } 155 | 156 | func TestOS_GetMacAddrs(t *testing.T) { 157 | res := KOS.GetMacAddrs() 158 | assert.NotEmpty(t, res) 159 | } 160 | 161 | func BenchmarkOS_GetMacAddrs(b *testing.B) { 162 | b.ResetTimer() 163 | for i := 0; i < b.N; i++ { 164 | KOS.GetMacAddrs() 165 | } 166 | } 167 | 168 | func TestOS_Hostname_GetIpByHostname(t *testing.T) { 169 | var res string 170 | var err error 171 | 172 | res, err = KOS.Hostname() 173 | assert.Nil(t, err) 174 | assert.NotEmpty(t, res) 175 | 176 | res, err = KOS.GetIpByHostname(tesDomain32) 177 | assert.Nil(t, err) 178 | assert.NotEmpty(t, res) 179 | 180 | res, err = KOS.GetIpByHostname(tesIp2) 181 | assert.Empty(t, res) 182 | 183 | res, err = KOS.GetIpByHostname(strHello) 184 | assert.NotNil(t, err) 185 | } 186 | 187 | func BenchmarkOS_Hostname(b *testing.B) { 188 | b.ResetTimer() 189 | for i := 0; i < b.N; i++ { 190 | _, _ = KOS.Hostname() 191 | } 192 | } 193 | 194 | func BenchmarkOS_GetIpByHostname(b *testing.B) { 195 | b.ResetTimer() 196 | host, _ := KOS.Hostname() 197 | for i := 0; i < b.N; i++ { 198 | _, _ = KOS.GetIpByHostname(host) 199 | } 200 | } 201 | 202 | func TestOS_GetIpsByDomain(t *testing.T) { 203 | var res []string 204 | var err error 205 | 206 | res, err = KOS.GetIpsByDomain(tesDomain30) 207 | assert.Nil(t, err) 208 | assert.NotEmpty(t, res) 209 | 210 | res, err = KOS.GetIpsByDomain(strHello) 211 | assert.NotNil(t, err) 212 | } 213 | 214 | func BenchmarkOS_GetIpsByDomain(b *testing.B) { 215 | b.ResetTimer() 216 | for i := 0; i < b.N; i++ { 217 | _, _ = KOS.GetIpsByDomain(tesDomain30) 218 | } 219 | } 220 | 221 | func TestOS_GetHostByIp(t *testing.T) { 222 | var res string 223 | var err error 224 | 225 | res, err = KOS.GetHostByIp(localIp) 226 | assert.Nil(t, err) 227 | assert.NotEmpty(t, res) 228 | 229 | res, err = KOS.GetHostByIp(strHello) 230 | assert.NotNil(t, err) 231 | assert.Empty(t, res) 232 | } 233 | 234 | func BenchmarkOS_GetHostByIp(b *testing.B) { 235 | b.ResetTimer() 236 | for i := 0; i < b.N; i++ { 237 | _, _ = KOS.GetHostByIp(localIp) 238 | } 239 | } 240 | 241 | func TestOS_Setenv_Getenv_Unsetenv(t *testing.T) { 242 | var res string 243 | var err error 244 | 245 | err = KOS.Setenv(helloEngICase, helloOther) 246 | assert.Nil(t, err) 247 | 248 | res = KOS.Getenv(helloEngICase) 249 | assert.Equal(t, res, helloOther) 250 | 251 | err = KOS.Unsetenv(helloEngICase) 252 | assert.Nil(t, err) 253 | 254 | res = KOS.Getenv(helloEngICase, helloOther2) 255 | assert.Equal(t, res, helloOther2) 256 | } 257 | 258 | func BenchmarkOS_Setenv(b *testing.B) { 259 | b.ResetTimer() 260 | for i := 0; i < b.N; i++ { 261 | _ = KOS.Setenv(helloEngICase, helloOther) 262 | } 263 | } 264 | 265 | func BenchmarkOS_Getenv(b *testing.B) { 266 | b.ResetTimer() 267 | for i := 0; i < b.N; i++ { 268 | KOS.Getenv(helloEngICase) 269 | } 270 | } 271 | 272 | func BenchmarkOS_Unsetenv(b *testing.B) { 273 | b.ResetTimer() 274 | for i := 0; i < b.N; i++ { 275 | _ = KOS.Unsetenv(helloEngICase) 276 | } 277 | } 278 | 279 | func TestOS_GetEndian_IsLittleEndian(t *testing.T) { 280 | res := KOS.GetEndian() 281 | chk := KOS.IsLittleEndian() 282 | if chk { 283 | assert.Equal(t, res, binary.LittleEndian) 284 | } else { 285 | assert.Equal(t, res, binary.BigEndian) 286 | } 287 | } 288 | 289 | func BenchmarkOS_GetEndian(b *testing.B) { 290 | b.ResetTimer() 291 | for i := 0; i < b.N; i++ { 292 | KOS.GetEndian() 293 | } 294 | } 295 | 296 | func BenchmarkOS_IsLittleEndian(b *testing.B) { 297 | b.ResetTimer() 298 | for i := 0; i < b.N; i++ { 299 | KOS.IsLittleEndian() 300 | } 301 | } 302 | 303 | func TestOS_Chmod_Chown(t *testing.T) { 304 | var res bool 305 | 306 | KFile.Touch(chownfile, 128) 307 | res = KOS.Chmod(chownfile, 0777) 308 | assert.True(t, res) 309 | 310 | usr, _ := user.Current() 311 | uid := toInt(usr.Uid) 312 | guid := toInt(usr.Gid) 313 | res = KOS.Chown(chownfile, uid, guid) 314 | if KOS.IsWindows() { 315 | assert.False(t, res) 316 | } else { 317 | assert.True(t, res) 318 | } 319 | } 320 | 321 | func BenchmarkOS_Chmod(b *testing.B) { 322 | b.ResetTimer() 323 | for i := 0; i < b.N; i++ { 324 | KOS.Chmod(dirDoc, 0777) 325 | } 326 | } 327 | 328 | func BenchmarkOS_Chown(b *testing.B) { 329 | b.ResetTimer() 330 | usr, _ := user.Current() 331 | uid := toInt(usr.Uid) 332 | guid := toInt(usr.Gid) 333 | for i := 0; i < b.N; i++ { 334 | KOS.Chown(dirDoc, uid, guid) 335 | } 336 | } 337 | 338 | func TestOS_GetTempDir(t *testing.T) { 339 | res := KOS.GetTempDir() 340 | assert.NotEmpty(t, res) 341 | } 342 | 343 | func BenchmarkOS_GetTempDir(b *testing.B) { 344 | b.ResetTimer() 345 | for i := 0; i < b.N; i++ { 346 | KOS.GetTempDir() 347 | } 348 | } 349 | 350 | func TestOS_ClientIp(t *testing.T) { 351 | // Create type and function for testing 352 | type testIP struct { 353 | name string 354 | request *http.Request 355 | expected string 356 | } 357 | 358 | newRequest := func(remoteAddr, xRealIP string, xForwardedFor ...string) *http.Request { 359 | h := http.Header{} 360 | h.Set("X-Real-IP", xRealIP) 361 | for _, address := range xForwardedFor { 362 | h.Set("X-Forwarded-For", address) 363 | } 364 | 365 | return &http.Request{ 366 | RemoteAddr: remoteAddr, 367 | Header: h, 368 | } 369 | } 370 | 371 | // Create test data 372 | testData := []testIP{ 373 | { 374 | name: "No header,no port", 375 | request: newRequest(publicIp1, ""), 376 | expected: publicIp1, 377 | }, { 378 | name: "No header,has port", 379 | request: newRequest(tesIp8, ""), 380 | expected: tesIp8, 381 | }, { 382 | name: "Has X-Forwarded-For", 383 | request: newRequest("", "", publicIp1), 384 | expected: publicIp1, 385 | }, { 386 | name: "Has multiple X-Forwarded-For", 387 | request: newRequest("", "", localIp, publicIp1, publicIp2), 388 | expected: publicIp2, 389 | }, { 390 | name: "Has X-Real-IP", 391 | request: newRequest("", publicIp1), 392 | expected: publicIp1, 393 | }, { 394 | name: "Local ip", 395 | request: newRequest("", tesIp2), 396 | expected: tesIp2, 397 | }, 398 | } 399 | 400 | // Run test 401 | var actual string 402 | for _, v := range testData { 403 | actual = KOS.ClientIp(v.request) 404 | if v.expected == "::1" { 405 | assert.Equal(t, actual, localIp) 406 | } else { 407 | if strings.Contains(v.expected, ":") { 408 | ip, _, _ := net.SplitHostPort(v.expected) 409 | assert.Equal(t, actual, ip) 410 | } else { 411 | assert.Equal(t, actual, v.expected) 412 | } 413 | } 414 | } 415 | } 416 | 417 | func BenchmarkOS_ClientIp(b *testing.B) { 418 | b.ResetTimer() 419 | req := &http.Request{ 420 | RemoteAddr: baiduIpv4, 421 | } 422 | for i := 0; i < b.N; i++ { 423 | KOS.ClientIp(req) 424 | } 425 | } 426 | 427 | func TestOS_IsPortOpen(t *testing.T) { 428 | var tests = []struct { 429 | host string 430 | port interface{} 431 | protocol string 432 | expected bool 433 | }{ 434 | {"", 23, "", false}, 435 | {localHost, 0, "", false}, 436 | {localIp, 23, "", false}, 437 | {tesDomain31, 80, "udp", true}, 438 | {tesDomain31, 80, "tcp", true}, 439 | {tesDomain32, "443", "tcp", true}, 440 | } 441 | for _, test := range tests { 442 | actual := KOS.IsPortOpen(test.host, test.port, test.protocol) 443 | assert.Equal(t, actual, test.expected) 444 | } 445 | 446 | //默认协议 447 | chk := KOS.IsPortOpen(lanIp, 80) 448 | assert.False(t, chk) 449 | } 450 | 451 | func BenchmarkOS_IsPortOpen(b *testing.B) { 452 | b.ResetTimer() 453 | for i := 0; i < b.N; i++ { 454 | KOS.IsPortOpen(localIp, 80, "tcp") 455 | } 456 | } 457 | 458 | func TestOS_ForceGC(t *testing.T) { 459 | KOS.ForceGC() 460 | } 461 | 462 | func BenchmarkOS_ForceGC(b *testing.B) { 463 | b.ResetTimer() 464 | for i := 0; i < b.N; i++ { 465 | KOS.ForceGC() 466 | } 467 | } 468 | 469 | func TestOS_TriggerGC(t *testing.T) { 470 | KOS.TriggerGC() 471 | } 472 | 473 | func BenchmarkOS_TriggerGC(b *testing.B) { 474 | b.ResetTimer() 475 | for i := 0; i < b.N; i++ { 476 | KOS.TriggerGC() 477 | } 478 | } 479 | 480 | func TestOS_GoMemory(t *testing.T) { 481 | res := KOS.GoMemory() 482 | assert.Greater(t, int(res), 1) 483 | } 484 | 485 | func BenchmarkOS_GoMemory(b *testing.B) { 486 | b.ResetTimer() 487 | for i := 0; i < b.N; i++ { 488 | KOS.GoMemory() 489 | } 490 | } 491 | 492 | func TestOS_GetSystemInfo(t *testing.T) { 493 | res := KOS.GetSystemInfo() 494 | assert.NotNil(t, res) 495 | } 496 | 497 | func BenchmarkOS_GetSystemInfo(b *testing.B) { 498 | b.ResetTimer() 499 | for i := 0; i < b.N; i++ { 500 | KOS.GetSystemInfo() 501 | } 502 | } 503 | 504 | func TestOS_DownloadFile(t *testing.T) { 505 | var written int64 506 | var err error 507 | 508 | //非url 509 | written, err = KOS.DownloadFile(strHello, "", false, nil) 510 | assert.NotNil(t, err) 511 | assert.Equal(t, written, int64(0)) 512 | 513 | //空路径 514 | written, err = KOS.DownloadFile(tesUrl39, "", false, nil) 515 | assert.NotNil(t, err) 516 | assert.Equal(t, written, int64(0)) 517 | 518 | //使用默认客户端 519 | written, err = KOS.DownloadFile(tesUrl39, downloadfile01, false, nil) 520 | assert.Nil(t, err) 521 | assert.Greater(t, written, int64(0)) 522 | 523 | //已存在文件 524 | written, err = KOS.DownloadFile(tesUrl39, downloadfile01, false, nil) 525 | assert.Nil(t, err) 526 | assert.Equal(t, written, int64(0)) 527 | 528 | //自定义客户端,覆盖已存在文件 529 | client := &http.Client{} 530 | client.Timeout = 6 * time.Second 531 | written, err = KOS.DownloadFile(tesUrl39, downloadfile01, true, client) 532 | assert.Nil(t, err) 533 | assert.Greater(t, written, int64(0)) 534 | } 535 | -------------------------------------------------------------------------------- /docs/todo.md: -------------------------------------------------------------------------------- 1 | ### TODO 2 | 3 | - 1.x版本使用泛型重构 4 | - 模糊测试 5 | 6 | ### 参考项目 7 | 8 | - https://www.php2golang.com/ 9 | - https://github.com/alessiosavi/GoGPUtils -x 10 | - https://github.com/b3log/gulu -x 11 | - https://github.com/syyongx/php2go -x 12 | - https://github.com/openset/php2go -x 13 | - https://github.com/yioio/fun -x 14 | - https://github.com/henrylee2cn/goutil -x 15 | - https://github.com/nutzam/zgo -x 16 | - https://github.com/bitnami/gonit -x 17 | - https://github.com/otiai10/copy -x 18 | - https://github.com/polaris1119/goutils -x 19 | - https://github.com/LyricTian/lib -x 20 | - https://github.com/antongulenko/golib -x 21 | - https://github.com/bocajim/helpers -x 22 | - https://github.com/elimisteve/fun -x 23 | - https://github.com/emirozer/go-helpers -x 24 | - https://github.com/evilsocket/islazy -x 25 | - https://github.com/fatih-yavuz/go-helpers -x 26 | - https://github.com/hhxsv5/go-helpers -x 27 | - https://github.com/idoall/TokenExchangeCommon/tree/master/commonutils -x 28 | - https://github.com/jiazhoulvke/goutil -x 29 | - https://github.com/jimmykuu/webhelpers -x 30 | - https://github.com/kooksee/cmn -x 31 | - https://github.com/kooksee/g -x 32 | - https://github.com/kooksee/common -x 33 | - https://github.com/leifengyao/go2php -x 34 | - https://github.com/leizongmin/go-utils -x 35 | - https://github.com/lets-go-go/helper -x 36 | - https://github.com/mylxsw/go-toolkit -x 37 | - https://github.com/nletech/go-func -x 38 | - https://github.com/orivil/helper -x 39 | - https://github.com/relunctance/goutils -x 40 | - https://github.com/seiflotfy/do -x 41 | - https://github.com/shuangdeyu/helper_go -x 42 | - https://github.com/sohaha/zlsgo -x 43 | - https://github.com/stephanbaker/go-simpletime -x 44 | - https://github.com/vence722/convert -x 45 | - https://github.com/jinzhu/now -x 46 | - https://github.com/thinkeridea/go-extend -x 47 | - https://github.com/lalamove/nui -x 48 | - https://github.com/go-eyas/toolkit -x yy 49 | - https://github.com/hwholiday/learning_tools -x 50 | - https://github.com/nothollyhigh/kiss -x 51 | - https://github.com/huandu/xstrings -x 52 | - https://github.com/bytedance/go-tagexpr -x 53 | - https://github.com/pibigstar/go-demo -x 54 | - https://github.com/techoner/gophp -x 55 | - https://github.com/m3ng9i/go-utils -x 56 | - https://github.com/torden/go-strutil -x 57 | - https://golang.hotexamples.com/examples/unicode/-/IsControl/golang-iscontrol-function-examples.html -x 58 | - https://github.com/idoall/TokenExchangeCommon/blob/master/commonutils/utils.go -x 59 | - https://github.com/afanti-com/utils-go -x 60 | - https://github.com/solo-io/go-utils -x 61 | - https://github.com/txthinking/x -x 62 | - https://github.com/TruthHun/gotil -x 63 | - https://github.com/xiaonanln/go-xnsyncutil -x 64 | - https://github.com/0xrawsec/golang-utils -x 65 | - https://github.com/Laisky/go-utils -x 66 | - https://github.com/xgfone/go-tools -x 67 | - https://github.com/printfcoder/goutils -x 68 | - https://github.com/space307/go-utils -x 69 | - https://github.com/liuyongshuai/goUtils -x 70 | - https://github.com/Jordanzuo/goutil -x 71 | - https://github.com/gxxgle/go-utils -x 72 | - https://github.com/q191201771/naza -x 73 | - https://github.com/kirinlabs/utils -x 74 | - https://github.com/xinliangnote/go-util -x 75 | - https://github.com/docker/go-units -x 76 | - https://github.com/alecthomas/units -x 77 | - https://github.com/prashantv/gostub -x 78 | - https://github.com/tgulacsi/go -x 79 | - https://github.com/UlricQin/goutils -x 80 | - https://github.com/chanxuehong/util -x 81 | - https://github.com/Akagi201/utilgo -x 82 | - https://github.com/fastly/go-utils -x 83 | - https://github.com/Teamwork/utils -x 84 | - https://github.com/OneOfOne/go-utils -x 85 | - https://github.com/bitrise-io/go-utils -x 86 | - https://github.com/ik5/gostrutils -x 87 | - https://github.com/hacdias/fileutils -x 88 | - https://github.com/gookit/goutil -x 89 | - https://github.com/henrylee2cn/utils -x 90 | - https://github.com/billmi/go-utils -x 91 | - https://github.com/zcalusic/sysinfo -x 92 | - https://github.com/mackerelio/go-osstat -x 93 | - https://github.com/unknwon/com -x 94 | - https://github.com/huandu/xstrings -x 95 | - https://github.com/astaxie/beego/tree/develop/utils -x 96 | - https://github.com/pubgo/g -x 97 | - https://github.com/layidao/utilx -x 98 | - https://github.com/siddontang/go -x 99 | - https://github.com/sohaha/zlsgo -x 100 | - https://github.com/ifree2017/EasyGoLib -x 101 | - https://github.com/go-board/x-go 102 | - https://github.com/qiniu/x 103 | - https://github.com/iiinsomnia/yiigo 104 | - https://github.com/xxjwxc/public 105 | - https://github.com/deepzz0/go-com 106 | - https://github.com/hprose/hprose-go 107 | - https://github.com/wumansgy/goEncrypt 108 | - https://github.com/hyperjiang/php 109 | - github.com/Unknwon/com 110 | - github.com/thoas/go-funk 111 | - https://github.com/mitchellh/go-homedir 112 | - https://github.com/Kretech/xgo 113 | - github.com/qit-team/snow-core 114 | - github.com/willf/pad 115 | - github.com/serkanalgur/phpfuncs 116 | - https://github.com/Nx-117/cyan 117 | - https://github.com/ThreeKing2018/goutil 118 | - https://github.com/tal-tech/xtools 119 | - https://github.com/php2go/php2go 120 | - https://github.com/yunionio/pkg 121 | - https://github.com/samber/lo 122 | - https://github.com/fishedee/tools 123 | - https://github.com/thoas/go-funk 124 | - https://gitee.com/tym_hmm/go-helper 125 | - https://github.com/hyacinthus/x 126 | - https://github.com/fufuok/utils 127 | - https://github.com/ScathonLin/scago 128 | - https://github.com/duke-git/lancet 129 | - https://github.com/samber/lo 130 | - https://github.com/elliotchance/pie 131 | - https://github.com/awesee/php2go 132 | - https://github.com/leeqvip/gophp 133 | - https://github.com/wulijun/go-php-serialize 134 | - https://github.com/elliotchance/phpserialize 135 | - github.com/jefferyjob/go-easy-utils 泛型 136 | 137 | ### 其他库 138 | 139 | - https://github.com/lalamove/konfig 140 | - https://github.com/jinzhu/configor 141 | - https://github.com/denisbrodbeck/machineid 142 | - github.com/karrick/godirwalk 143 | - https://github.com/gobwas/pool 144 | - https://github.com/shirou/gopsutil 145 | - https://github.com/sunmi-OS/gocore 146 | - https://github.com/akhenakh/statgo 147 | - https://github.com/elastic/go-sysinfo 148 | - https://github.com/matishsiao/goInfo 149 | - https://github.com/badoux/checkmail 150 | 151 | ### TODO 152 | 153 | - 时间轮定时器 154 | - 简单http请求 155 | - 增加sync.go 156 | 157 | pid 158 | https://github.com/bitnami/gonit/blob/master/utils/process.go 159 | https://github.com/henrylee2cn/goutil/blob/master/pid_file.go 160 | https://github.com/facebookarchive/pidfile/blob/master/pidfile.go 161 | https://github.com/struCoder/pidusage/blob/master/pidusage.go 162 | 163 | ping 164 | https://github.com/bocajim/helpers/blob/master/ping.go 165 | 166 | http/curl 167 | https://github.com/mreiferson/go-httpclient 168 | https://github.com/elimisteve/fun/blob/master/fetch.go 169 | https://github.com/nareix/curl 170 | https://github.com/andelf/go-curl 171 | https://github.com/parnurzeal/gorequest 172 | https://github.com/go-resty/resty 173 | https://github.com/gojek/heimdall 174 | https://github.com/dghubble/sling 175 | https://github.com/h2non/gentleman 176 | https://github.com/guonaihong/gout 177 | https://github.com/levigross/grequests 178 | https://github.com/alessiosavi/Requests 179 | 180 | queue 181 | https://github.com/evilsocket/islazy/blob/master/async/queue.go 182 | 183 | debug 184 | https://colobu.com/2018/11/03/get-function-name-in-go/ 185 | https://colobu.com/2016/12/21/how-to-dump-goroutine-stack-traces/ 186 | https://stackoverflow.com/questions/19094099/how-to-dump-goroutine-stacktraces 187 | https://github.com/rfyiamcool/stack_dump 188 | https://www.jianshu.com/p/abbe6663b672 189 | https://github.com/go-delve/delve 190 | 191 | array sort 192 | https://loesspie.com/2018/05/07/go-sort-with-multiple-parameters/ 193 | https://stackoverflow.com/questions/36122668/how-to-sort-struct-with-multiple-sort-parameters 194 | https://yourbasic.org/golang/how-to-sort-in-go/ 195 | https://itimetraveler.github.io/2016/09/07/%E3%80%90Go%E8%AF%AD%E8%A8%80%E3%80%91%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E6%8E%92%E5%BA%8F%E5%92%8C%20slice%20%E6%8E%92%E5%BA%8F/ 196 | https://blog.csdn.net/chenbaoke/article/details/42340301 197 | https://stackoverflow.com/questions/37695209/golang-sort-slice-ascending-or-descending 198 | https://www.cnblogs.com/kaixinyufeng/p/9609160.html 199 | https://www.cnblogs.com/chenpingzhao/p/4688526.html 200 | https://github.com/torden/go-strutil 201 | 202 | setInterval 203 | https://www.loxodrome.io/post/set-timeout-interval-go/ 204 | https://stackoverflow.com/questions/16466320/is-there-a-way-to-do-repetitive-tasks-at-intervals 205 | https://github.com/giuseppesalvo/tm 206 | https://github.com/coreybutler/go-timer 207 | https://github.com/cube-group/go-timer 208 | https://github.com/goinstant/delayd 209 | https://zhuanlan.zhihu.com/p/55295257 210 | https://github.com/segmentio/timers 211 | https://github.com/noaway/heartbeat 212 | https://github.com/liberalman/timer_server 213 | https://github.com/alex023/clock 214 | https://github.com/henrylee2cn/timer 215 | https://github.com/RussellLuo/timingwheel 216 | https://github.com/zhangwei1234/go-timer-wheel 217 | https://github.com/antlabs/timer 218 | 219 | serialize 220 | https://segmentfault.com/q/1010000010690732 221 | https://github.com/yvasiyarov/php_session_decoder/tree/master/php_serialize 222 | https://segmentfault.com/a/1190000016818544 223 | https://stackoverflow.com/questions/28020070/golang-serialize-and-deserialize-back 224 | https://github.com/techoner/gophp/tree/master/serialize 225 | 226 | net 简单http请求方法 227 | https://github.com/shuangdeyu/helper_go/blob/master/nethelper/http.go 228 | https://github.com/sohaha/zlsgo/blob/master/zhttp/client.go 229 | https://github.com/go-eyas/toolkit/tree/master/http 230 | 231 | base58 232 | https://godoc.org/github.com/btcsuite/btcutil/base58 233 | https://github.com/itchyny/base58-go 234 | https://blog.csdn.net/idwtwt/article/details/80740474 235 | https://studygolang.com/articles/16870 236 | https://github.com/mr-tron/base58 237 | https://github.com/shuangdeyu/helper_go/blob/master/pwdhelper/base58.go 238 | https://blog.csdn.net/jason_cuijiahui/article/details/79280362 239 | 240 | mutex 241 | https://github.com/nothollyhigh/kiss/blob/master/sync/mutex.go 242 | 243 | levenshtein 244 | https://syslog.ravelin.com/searching-for-levenshtein-eff8093b11d4 245 | https://www.socketloop.com/tutorials/golang-levenshtein-distance-example 246 | https://www.golangprograms.com/golang-program-for-implementation-of-levenshtein-distance.html 247 | https://github.com/philpearl/levenshtein/blob/master/levenshtein.go 248 | https://github.com/lithammer/fuzzysearch/blob/master/fuzzy/levenshtein.go 249 | https://github.com/schollz/closestmatch/blob/master/levenshtein/levenshtein.go 250 | https://github.com/agnivade/levenshtein/blob/master/levenshtein.go 251 | https://github.com/arbovm/levenshtein/blob/master/levenshtein.go 252 | https://github.com/texttheater/golang-levenshtein/blob/master/levenshtein/levenshtein.go 253 | https://github.com/ferhatelmas/levenshtein/blob/master/levenshtein.go 254 | https://github.com/jbowles/disfun/blob/master/levenshtein.go 255 | 256 | pack/unpack binary pack 257 | https://gist.github.com/cloveryume/9a59e8d77f5836f11720#file-golang_struct_packed-go 258 | https://github.com/lunixbochs/struc 259 | https://github.com/roman-kachanovsky/go-binary-pack 260 | https://github.com/syyongx/php2go/blob/master/php.go 261 | https://golangtc.com/t/55237dd8421aa9704b0000cb 262 | https://juejin.im/entry/5a9cec4ff265da239c7ad86d 263 | https://learnku.com/articles/31460 264 | https://stackoverflow.com/questions/32685687/convert-string-to-binary-in-go 265 | https://stackoverflow.com/questions/37349071/golang-how-to-convert-string-to-binary-representation/37350639 266 | https://stackoverflow.com/questions/40182289/golang-equivalent-of-pythons-struct-pack-struct-unpack 267 | https://stackoverflow.com/questions/8039552/byte-endian-convert-by-using-encoding-binary-in-go 268 | https://studygolang.com/articles/2791 269 | https://www.reddit.com/r/golang/comments/7pwlh6/binary_string_to_readable_string/ 270 | 271 | go get current package name 272 | https://stackoverflow.com/questions/25262754/how-to-get-name-of-current-package-in-go 273 | 274 | go get current file path 275 | https://coderwall.com/p/_fmbug/go-get-path-to-current-file 276 | https://github.com/unknwon/gcblog/blob/master/content/04-go-caller.md 277 | https://colobu.com/2018/11/03/get-function-name-in-go/ 278 | 279 | DeleteSlice 删除切片元素/多个 280 | https://blog.csdn.net/yue7603835/article/details/71196181 281 | 282 | golang 根据PID获取进程的执行路径 283 | https://www.systutorials.com/how-to-get-the-running-process-pid-in-go/ 284 | https://blog.csdn.net/weixin_42324368/article/details/107872138 285 | https://blog.csdn.net/qq_27870421/article/details/103290155 286 | https://blog.csdn.net/ycf8788/article/details/99074801 287 | https://stackoverflow.com/questions/15204162/check-if-a-process-exists-in-go-way 288 | https://github.com/golang/go/issues/33814 289 | https://www.btaz.com/mac-os-x/find-the-process-listening-to-port-on-mac-os-x/ 290 | https://blog.jayway.com/2012/09/08/finding-the-pid-listening-on-a-specific-port-on-mac-os-x/ 291 | https://stackoverflow.com/questions/3855127/find-and-kill-process-locking-port-3000-on-mac 292 | 293 | 结构体转MAP 294 | https://github.com/fatih/structs 295 | https://www.liwenzhou.com/posts/Go/struct2map/ 296 | https://www.cnblogs.com/liang1101/p/6741262.html 297 | https://www.cnblogs.com/Detector/p/9746284.html 298 | https://juejin.cn/post/6855129007193915400 299 | 300 | 字符编码 301 | https://github.com/hydra13142/chardet 302 | https://github.com/fesiong/goproject 303 | https://github.com/djimenez/iconv-go 304 | https://pkg.go.dev/golang.org/x/net/html/charset 305 | https://github.com/timakin/gonvert 306 | https://github.com/spiegel-im-spiegel/text 307 | https://github.com/saintfish/chardet 308 | https://siongui.github.io/2018/10/27/auto-detect-and-convert-html-encoding-to-utf8-in-go/ 309 | 310 | 311 | -------------------------------------------------------------------------------- /time_test.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestTime_UnixTime(t *testing.T) { 10 | var res int64 11 | 12 | res = KTime.UnixTime() 13 | assert.Equal(t, 10, len(toStr(res))) 14 | } 15 | 16 | func BenchmarkTime_UnixTime(b *testing.B) { 17 | b.ResetTimer() 18 | for i := 0; i < b.N; i++ { 19 | KTime.UnixTime() 20 | } 21 | } 22 | 23 | func TestTime_MilliTime(t *testing.T) { 24 | var res int64 25 | 26 | res = KTime.MilliTime() 27 | assert.Equal(t, 13, len(toStr(res))) 28 | } 29 | 30 | func BenchmarkTime_MilliTime(b *testing.B) { 31 | b.ResetTimer() 32 | for i := 0; i < b.N; i++ { 33 | KTime.MilliTime() 34 | } 35 | } 36 | 37 | func TestTime_MicroTime(t *testing.T) { 38 | var res int64 39 | 40 | res = KTime.MicroTime() 41 | assert.Equal(t, 16, len(toStr(res))) 42 | } 43 | func BenchmarkTime_MicroTime(b *testing.B) { 44 | b.ResetTimer() 45 | for i := 0; i < b.N; i++ { 46 | KTime.MicroTime() 47 | } 48 | } 49 | 50 | func TestTime_Str2Timestruct(t *testing.T) { 51 | var res time.Time 52 | var err error 53 | 54 | res, err = KTime.Str2Timestruct(strTime1) 55 | assert.Nil(t, err) 56 | assert.Equal(t, res.Year(), 2019) 57 | assert.Equal(t, int(res.Month()), 7) 58 | assert.Equal(t, res.Day(), 11) 59 | 60 | //UTC时间 61 | res, err = KTime.Str2Timestruct(strTime8, TimeOption{IsUTC: true}) 62 | assert.Nil(t, err) 63 | assert.Equal(t, res.Unix(), int64(1709724618)) 64 | //CST 65 | res, err = KTime.Str2Timestruct(strTime8, TimeOption{Zone: "Asia/Shanghai"}) 66 | assert.Nil(t, err) 67 | assert.Equal(t, res.Unix(), int64(1709695818)) 68 | //本地时区 69 | res, err = KTime.Str2Timestruct(strTime8, TimeOption{Location: kuptime.Location()}) 70 | assert.Nil(t, err) 71 | //错误的zone 72 | res, err = KTime.Str2Timestruct(strTime8, TimeOption{Zone: strHello}) 73 | assert.NotNil(t, err) 74 | } 75 | 76 | func BenchmarkTime_Str2Timestruct(b *testing.B) { 77 | b.ResetTimer() 78 | for i := 0; i < b.N; i++ { 79 | _, _ = KTime.Str2Timestruct(strTime1) 80 | } 81 | } 82 | 83 | func TestTime_Str2Timestamp(t *testing.T) { 84 | var res int64 85 | var err error 86 | 87 | res, err = KTime.Str2Timestamp(strTime1) 88 | assert.Nil(t, err) 89 | assert.Greater(t, res, int64(1)) 90 | 91 | res, err = KTime.Str2Timestamp(strTime3, TimeOption{Layout: "01/02/2006 15:04:05"}) 92 | assert.Nil(t, err) 93 | assert.Greater(t, res, int64(1)) 94 | 95 | //时间格式错误 96 | res, err = KTime.Str2Timestamp(strTime2, TimeOption{Layout: "2006-01-02"}) 97 | assert.NotNil(t, err) 98 | } 99 | 100 | func BenchmarkTime_Str2Timestamp(b *testing.B) { 101 | b.ResetTimer() 102 | for i := 0; i < b.N; i++ { 103 | _, _ = KTime.Str2Timestamp(strTime3, TimeOption{Layout: "01/02/2006 15:04:05"}) 104 | } 105 | } 106 | 107 | func TestTime_Date(t *testing.T) { 108 | var res string 109 | 110 | res = KTime.Date("Y-m-d H:i:s") 111 | assert.NotEmpty(t, res) 112 | 113 | res = KTime.Date("Y-m-d H:i:s", intTime1) 114 | assert.NotEmpty(t, res) 115 | 116 | res = KTime.Date("y-n-j H:i:s", int64(intTime1)) 117 | assert.NotEmpty(t, res) 118 | 119 | res = KTime.Date("m/d/y h-i-s", kuptime) 120 | assert.NotEmpty(t, res) 121 | 122 | res = KTime.Date("Y-m-d H:i:s") 123 | assert.NotEmpty(t, res) 124 | 125 | res = KTime.Date("Y-m-d H:i:s", strHello) 126 | assert.Empty(t, res) 127 | 128 | //时间戳为0的时间点 129 | res = KTime.Date("Y-m-d H:i:s", 0) 130 | assert.NotEmpty(t, res) 131 | //东八区 132 | //assert.Equal(t, res, "1970-01-01 08:00:00") 133 | } 134 | 135 | func BenchmarkTime_Date(b *testing.B) { 136 | b.ResetTimer() 137 | for i := 0; i < b.N; i++ { 138 | KTime.Date("Y-m-d H:i:s") 139 | } 140 | } 141 | 142 | func TestTime_CheckDate(t *testing.T) { 143 | var res bool 144 | 145 | res = KTime.CheckDate(2019, 7, 31) 146 | assert.True(t, res) 147 | 148 | res = KTime.CheckDate(2019, 2, 31) 149 | assert.False(t, res) 150 | 151 | res = KTime.CheckDate(2019, 0, 31) 152 | assert.False(t, res) 153 | 154 | res = KTime.CheckDate(2019, 4, 31) 155 | assert.False(t, res) 156 | 157 | res = KTime.CheckDate(2008, 2, 30) 158 | assert.False(t, res) 159 | } 160 | 161 | func BenchmarkTime_CheckDate(b *testing.B) { 162 | b.ResetTimer() 163 | for i := 0; i < b.N; i++ { 164 | KTime.CheckDate(2019, 7, 31) 165 | } 166 | } 167 | 168 | func TestTime_Sleep(t *testing.T) { 169 | var t0, t1, t2, res int64 170 | t0 = 1 171 | t1 = KTime.UnixTime() 172 | KTime.Sleep(t0) 173 | t2 = KTime.UnixTime() 174 | res = t2 - t1 175 | assert.GreaterOrEqual(t, res, t0) 176 | } 177 | 178 | func TestTime_Usleep(t *testing.T) { 179 | var t0, t1, t2, res int64 180 | t0 = 100 181 | t1 = KTime.MicroTime() 182 | KTime.Usleep(t0) 183 | t2 = KTime.MicroTime() 184 | res = t2 - t1 185 | assert.GreaterOrEqual(t, res, t0) 186 | } 187 | 188 | func TestTime_ServiceStartime(t *testing.T) { 189 | var res int64 190 | res = KTime.ServiceStartime() 191 | assert.Greater(t, res, int64(1)) 192 | } 193 | 194 | func BenchmarkTime_ServiceStartime(b *testing.B) { 195 | b.ResetTimer() 196 | for i := 0; i < b.N; i++ { 197 | KTime.ServiceStartime() 198 | } 199 | } 200 | 201 | func TestTime_ServiceUptime(t *testing.T) { 202 | var res time.Duration 203 | 204 | res = KTime.ServiceUptime() 205 | assert.Greater(t, int64(res), int64(1)) 206 | } 207 | 208 | func BenchmarkTime_ServiceUptime(b *testing.B) { 209 | b.ResetTimer() 210 | for i := 0; i < b.N; i++ { 211 | KTime.ServiceUptime() 212 | } 213 | } 214 | 215 | func TestTime_GetMonthDays(t *testing.T) { 216 | var res int 217 | var tests = []struct { 218 | month int 219 | year int 220 | expected int 221 | }{ 222 | {3, 1970, 31}, 223 | {1, 2009, 31}, 224 | {0, 2009, 0}, 225 | {2, 2009, 28}, 226 | {2, 2016, 29}, 227 | {2, 1900, 28}, 228 | {4, 2020, 30}, 229 | {2, 1600, 29}, 230 | } 231 | for _, test := range tests { 232 | actual := KTime.GetMonthDays(test.month, test.year) 233 | assert.Equal(t, actual, test.expected) 234 | } 235 | 236 | res = KTime.GetMonthDays(2) 237 | assert.Greater(t, res, 0) 238 | } 239 | 240 | func BenchmarkTime_GetMonthDays(b *testing.B) { 241 | b.ResetTimer() 242 | for i := 0; i < b.N; i++ { 243 | KTime.GetMonthDays(3, 1970) 244 | } 245 | } 246 | 247 | func TestTime_YearMonthDay(t *testing.T) { 248 | var y1, m1, d1, y2, m2, d2 int 249 | 250 | y1 = KTime.Year() 251 | m1 = KTime.Month() 252 | d1 = KTime.Day() 253 | 254 | y2 = KTime.Year(kuptime) 255 | m2 = KTime.Month(kuptime) 256 | d2 = KTime.Day(kuptime) 257 | 258 | assert.Equal(t, y1, y2) 259 | assert.Equal(t, m1, m2) 260 | assert.Equal(t, d1, d2) 261 | } 262 | 263 | func BenchmarkTime_Year(b *testing.B) { 264 | b.ResetTimer() 265 | for i := 0; i < b.N; i++ { 266 | KTime.Year(kuptime) 267 | } 268 | } 269 | 270 | func BenchmarkTime_Month(b *testing.B) { 271 | b.ResetTimer() 272 | for i := 0; i < b.N; i++ { 273 | KTime.Month(kuptime) 274 | } 275 | } 276 | 277 | func BenchmarkTime_Day(b *testing.B) { 278 | b.ResetTimer() 279 | for i := 0; i < b.N; i++ { 280 | KTime.Year(kuptime) 281 | } 282 | } 283 | 284 | func TestTime_HourMinuteSecond(t *testing.T) { 285 | var h1, m1, s1, h2, m2, s2 int 286 | 287 | h1 = KTime.Hour() 288 | m1 = KTime.Minute() 289 | s1 = KTime.Second() 290 | assert.GreaterOrEqual(t, h1, 0) 291 | assert.GreaterOrEqual(t, m1, 0) 292 | assert.GreaterOrEqual(t, s1, 0) 293 | 294 | h2 = KTime.Hour(myDate1) 295 | m2 = KTime.Minute(myDate1) 296 | s2 = KTime.Second(myDate1) 297 | assert.Equal(t, h2, 23) 298 | assert.Equal(t, m2, 4) 299 | assert.Equal(t, s2, 35) 300 | } 301 | 302 | func BenchmarkTime_Hour(b *testing.B) { 303 | b.ResetTimer() 304 | for i := 0; i < b.N; i++ { 305 | KTime.Hour(kuptime) 306 | } 307 | } 308 | 309 | func BenchmarkTime_Minute(b *testing.B) { 310 | b.ResetTimer() 311 | for i := 0; i < b.N; i++ { 312 | KTime.Minute(kuptime) 313 | } 314 | } 315 | 316 | func BenchmarkTime_Second(b *testing.B) { 317 | b.ResetTimer() 318 | for i := 0; i < b.N; i++ { 319 | KTime.Second(kuptime) 320 | } 321 | } 322 | 323 | func TestTime_StartOfDay(t *testing.T) { 324 | var res time.Time 325 | 326 | res = KTime.StartOfDay(myDate1) 327 | str := KTime.Date("Y-m-d H:i:s", res) 328 | assert.Equal(t, str, "2020-03-10 00:00:00") 329 | } 330 | 331 | func BenchmarkTime_StartOfDay(b *testing.B) { 332 | b.ResetTimer() 333 | for i := 0; i < b.N; i++ { 334 | KTime.StartOfDay(myDate1) 335 | } 336 | } 337 | 338 | func TestTime_EndOfDay(t *testing.T) { 339 | var res time.Time 340 | 341 | res = KTime.EndOfDay(myDate1) 342 | str := KTime.Date("Y-m-d H:i:s", res) 343 | assert.Equal(t, str, "2020-03-10 23:59:59") 344 | } 345 | 346 | func BenchmarkTime_EndOfDay(b *testing.B) { 347 | b.ResetTimer() 348 | for i := 0; i < b.N; i++ { 349 | KTime.EndOfDay(myDate1) 350 | } 351 | } 352 | 353 | func TestTime_StartOfMonth(t *testing.T) { 354 | var res time.Time 355 | 356 | res = KTime.StartOfMonth(myDate1) 357 | str := KTime.Date("Y-m-d H:i:s", res) 358 | assert.Equal(t, str, "2020-03-01 00:00:00") 359 | } 360 | 361 | func BenchmarkTime_StartOfMonth(b *testing.B) { 362 | b.ResetTimer() 363 | for i := 0; i < b.N; i++ { 364 | KTime.StartOfMonth(myDate1) 365 | } 366 | } 367 | 368 | func TestTime_EndOfMonth(t *testing.T) { 369 | var res time.Time 370 | 371 | res = KTime.EndOfMonth(myDate1) 372 | str := KTime.Date("Y-m-d H:i:s", res) 373 | assert.Equal(t, str, "2020-03-31 23:59:59") 374 | } 375 | 376 | func BenchmarkTime_EndOfMonth(b *testing.B) { 377 | b.ResetTimer() 378 | for i := 0; i < b.N; i++ { 379 | KTime.EndOfMonth(myDate1) 380 | } 381 | } 382 | 383 | func TestTime_StartOfYear(t *testing.T) { 384 | var res time.Time 385 | 386 | res = KTime.StartOfYear(myDate1) 387 | str := KTime.Date("Y-m-d H:i:s", res) 388 | assert.Equal(t, str, "2020-01-01 00:00:00") 389 | } 390 | 391 | func BenchmarkTime_StartOfYear(b *testing.B) { 392 | b.ResetTimer() 393 | for i := 0; i < b.N; i++ { 394 | KTime.StartOfYear(myDate1) 395 | } 396 | } 397 | 398 | func TestTime_EndOfYear(t *testing.T) { 399 | var res time.Time 400 | 401 | res = KTime.EndOfYear(myDate1) 402 | str := KTime.Date("Y-m-d H:i:s", res) 403 | assert.Equal(t, str, "2020-12-31 23:59:59") 404 | } 405 | 406 | func BenchmarkTime_EndOfYear(b *testing.B) { 407 | b.ResetTimer() 408 | for i := 0; i < b.N; i++ { 409 | KTime.EndOfYear(myDate1) 410 | } 411 | } 412 | 413 | func TestTime_StartOfWeek(t *testing.T) { 414 | var res time.Time 415 | 416 | res = KTime.StartOfWeek(myDate1) 417 | str := KTime.Date("Y-m-d H:i:s", res) 418 | assert.Equal(t, str, "2020-03-09 00:00:00") 419 | 420 | res = KTime.StartOfWeek(myDate2, time.Tuesday) 421 | str = KTime.Date("Y-m-d H:i:s", res) 422 | assert.Equal(t, str, "2020-03-03 00:00:00") 423 | } 424 | 425 | func BenchmarkTime_StartOfWeek(b *testing.B) { 426 | b.ResetTimer() 427 | for i := 0; i < b.N; i++ { 428 | KTime.StartOfWeek(myDate1) 429 | } 430 | } 431 | 432 | func TestTime_EndOfWeek(t *testing.T) { 433 | var res time.Time 434 | 435 | res = KTime.EndOfWeek(myDate1) 436 | str := KTime.Date("Y-m-d H:i:s", res) 437 | assert.Equal(t, str, "2020-03-15 23:59:59") 438 | 439 | res = KTime.EndOfWeek(myDate2, time.Tuesday) 440 | str = KTime.Date("Y-m-d H:i:s", res) 441 | assert.Equal(t, str, "2020-03-09 23:59:59") 442 | } 443 | 444 | func BenchmarkTime_EndOfWeek(b *testing.B) { 445 | b.ResetTimer() 446 | for i := 0; i < b.N; i++ { 447 | KTime.EndOfWeek(myDate1) 448 | } 449 | } 450 | 451 | func TestTime_DaysBetween(t *testing.T) { 452 | days := KTime.DaysBetween(myDate1, myDate3) 453 | assert.Equal(t, days, 107) 454 | 455 | days = KTime.DaysBetween(myDate3, myDate1) 456 | assert.Equal(t, days, -107) 457 | } 458 | 459 | func BenchmarkTime_DaysBetween(b *testing.B) { 460 | b.ResetTimer() 461 | for i := 0; i < b.N; i++ { 462 | KTime.DaysBetween(myDate1, myDate3) 463 | } 464 | } 465 | 466 | func TestTime_IsDate2time(t *testing.T) { 467 | var tests = []struct { 468 | param string 469 | expected bool 470 | }{ 471 | {"", false}, 472 | {"hello", false}, 473 | {"0000", true}, 474 | {"1970", true}, 475 | {"1970-01-01", true}, 476 | {"1970-01-01 00:00:01", true}, 477 | {"1971-01-01 00:00:01", true}, 478 | {"1990-01", true}, 479 | {"1990/01", true}, 480 | {"1990-01-02", true}, 481 | {"1990/01/02", true}, 482 | {"1990-01-02 03", true}, 483 | {"1990/01/02 03", true}, 484 | {"1990-01-02 03:14", true}, 485 | {"1990/01/02 03:14", true}, 486 | {"1990-01-02 03:14:59", true}, 487 | {"1990/01/02 03:14:59", true}, 488 | {"2990-00-00 03:14:59", false}, 489 | } 490 | for _, test := range tests { 491 | actual, tim := KTime.IsDate2time(test.param) 492 | assert.Equal(t, actual, test.expected) 493 | if actual { 494 | if toInt(test.param) > 1970 { 495 | assert.Greater(t, tim, int64(0)) 496 | } 497 | } 498 | } 499 | } 500 | 501 | func BenchmarkTime_IsDate2time(b *testing.B) { 502 | b.ResetTimer() 503 | for i := 0; i < b.N; i++ { 504 | _, _ = KTime.IsDate2time(strTime7) 505 | } 506 | } 507 | 508 | func TestTime_FormatDuration(t *testing.T) { 509 | var res1, res2 string 510 | 511 | ds := []time.Duration{ 512 | testDuration1 + testDuration2 + testDuration3, 513 | testDuration1 + testDuration2, 514 | testDuration1 + testDuration3, 515 | testDuration2 + testDuration3, 516 | testDuration1, 517 | testDuration2, 518 | testDuration3, 519 | } 520 | for _, d := range ds { 521 | res1 = KTime.FormatDuration(d, true) 522 | assert.Contains(t, res1, ":") 523 | res2 = KTime.FormatDuration(d, false) 524 | assert.NotEmpty(t, res2) 525 | } 526 | 527 | res1 = KTime.FormatDuration(testDuration0, true) 528 | res2 = KTime.FormatDuration(testDuration0, false) 529 | assert.Equal(t, res1, "0:00:00") 530 | assert.Equal(t, res2, "0s") 531 | 532 | res1 = KTime.FormatDuration(testDuration4, true) 533 | res2 = KTime.FormatDuration(testDuration4, false) 534 | assert.Equal(t, res1, "24:01:53") 535 | assert.Equal(t, res2, "24h1m53s") 536 | 537 | res1 = KTime.FormatDuration(testDuration5, true) 538 | res2 = KTime.FormatDuration(testDuration5, false) 539 | assert.Equal(t, res1, "1354:22:13") 540 | assert.Equal(t, res2, "1354h22m13.055s") 541 | } 542 | 543 | func BenchmarkTime_FormatDuration0(b *testing.B) { 544 | b.ResetTimer() 545 | for i := 0; i < b.N; i++ { 546 | _ = KTime.FormatDuration(testDuration5, false) 547 | } 548 | } 549 | 550 | func BenchmarkTime_FormatDuration1(b *testing.B) { 551 | b.ResetTimer() 552 | for i := 0; i < b.N; i++ { 553 | _ = KTime.FormatDuration(testDuration5, true) 554 | } 555 | } 556 | -------------------------------------------------------------------------------- /os.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "encoding/binary" 7 | "errors" 8 | "fmt" 9 | "io" 10 | "net" 11 | "net/http" 12 | "os" 13 | "os/exec" 14 | "path/filepath" 15 | "regexp" 16 | "runtime" 17 | "runtime/debug" 18 | "strings" 19 | "unicode" 20 | ) 21 | 22 | // SystemInfo 系统信息 23 | type SystemInfo struct { 24 | ServerName string `json:"server_name"` //服务器名称 25 | SystemArch string `json:"system_arch"` //系统架构 26 | SystemOs string `json:"system_os"` //操作系统名称 27 | Runtime uint64 `json:"run_time"` //服务运行时间,纳秒 28 | Uptime uint64 `json:"up_time"` //操作系统运行时间,秒 29 | GoroutineNum int `json:"goroutine_num"` //goroutine数量 30 | CpuNum int `json:"cpu_num"` //cpu核数 31 | CpuUser float64 `json:"cpu_user"` //cpu用户态比率 32 | CpuFree float64 `json:"cpu_free"` //cpu空闲比率 33 | DiskUsed uint64 `json:"disk_used"` //已用磁盘空间,字节数 34 | DiskFree uint64 `json:"disk_free"` //可用磁盘空间,字节数 35 | DiskTotal uint64 `json:"disk_total"` //总磁盘空间,字节数 36 | MemUsed uint64 `json:"mem_used"` //已用内存,字节数 37 | MemSys uint64 `json:"mem_sys"` //系统内存占用量,字节数 38 | MemFree uint64 `json:"mem_free"` //剩余内存,字节数 39 | MemTotal uint64 `json:"mem_total"` //总内存,字节数 40 | AllocGolang uint64 `json:"alloc_golang"` //golang内存使用量,字节数 41 | AllocTotal uint64 `json:"alloc_total"` //总分配的内存,字节数 42 | Lookups uint64 `json:"lookups"` //指针查找次数 43 | Mallocs uint64 `json:"mallocs"` //内存分配次数 44 | Frees uint64 `json:"frees"` //内存释放次数 45 | LastGCTime uint64 `json:"last_gc_time"` //上次GC时间,纳秒 46 | NextGC uint64 `json:"next_gc"` //下次GC内存回收量,字节数 47 | PauseTotalNs uint64 `json:"pause_total_ns"` //GC暂停时间总量,纳秒 48 | PauseNs uint64 `json:"pause_ns"` //上次GC暂停时间,纳秒 49 | } 50 | 51 | // BiosInfo BIOS信息 52 | type BiosInfo struct { 53 | Vendor string `json:"vendor"` 54 | Version string `json:"version"` 55 | Date string `json:"date"` 56 | } 57 | 58 | // BoardInfo Board信息 59 | type BoardInfo struct { 60 | Name string `json:"name"` 61 | Vendor string `json:"vendor"` 62 | Version string `json:"version"` 63 | Serial string `json:"serial"` 64 | AssetTag string `json:"assettag"` 65 | } 66 | 67 | // CpuInfo CPU信息 68 | type CpuInfo struct { 69 | Vendor string `json:"vendor"` 70 | Model string `json:"model"` 71 | Speed string `json:"speed"` // CPU clock rate in MHz 72 | Cache uint `json:"cache"` // CPU cache size in KB 73 | Cpus uint `json:"cpus"` // number of physical CPUs 74 | Cores uint `json:"cores"` // number of physical CPU cores 75 | Threads uint `json:"threads"` // number of logical (HT) CPU cores 76 | } 77 | 78 | var ( 79 | cpuRegTwoColumns = regexp.MustCompile("\t+: ") 80 | cpuRegExtraSpace = regexp.MustCompile(" +") 81 | cpuRegCacheSize = regexp.MustCompile(`^(\d+) KB$`) 82 | ) 83 | 84 | // IsWindows 当前操作系统是否Windows. 85 | func (ko *LkkOS) IsWindows() bool { 86 | return "windows" == runtime.GOOS 87 | } 88 | 89 | // IsLinux 当前操作系统是否Linux. 90 | func (ko *LkkOS) IsLinux() bool { 91 | return "linux" == runtime.GOOS 92 | } 93 | 94 | // IsMac 当前操作系统是否Mac OS/X. 95 | func (ko *LkkOS) IsMac() bool { 96 | return "darwin" == runtime.GOOS 97 | } 98 | 99 | // Pwd 获取当前程序运行所在的路径,注意和Getwd有所不同. 100 | // 若当前执行的是链接文件,则会指向真实二进制程序的所在目录. 101 | func (ko *LkkOS) Pwd() string { 102 | var dir, ex string 103 | var err error 104 | ex, err = os.Executable() 105 | if err == nil { 106 | exReal, _ := filepath.EvalSymlinks(ex) 107 | exReal, _ = filepath.Abs(exReal) 108 | dir = filepath.Dir(exReal) 109 | } 110 | 111 | return dir 112 | } 113 | 114 | // Getcwd 取得当前工作目录(程序可能在任务中进行多次目录切换). 115 | func (ko *LkkOS) Getcwd() (string, error) { 116 | dir, err := os.Getwd() 117 | return dir, err 118 | } 119 | 120 | // Chdir 改变/进入新的工作目录. 121 | func (ko *LkkOS) Chdir(dir string) error { 122 | return os.Chdir(dir) 123 | } 124 | 125 | // LocalIP 获取本机第一个NIC's IP. 126 | func (ko *LkkOS) LocalIP() (string, error) { 127 | res := "" 128 | addrs, err := net.InterfaceAddrs() 129 | if len(addrs) > 0 { 130 | for _, addr := range addrs { 131 | if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { 132 | if nil != ipnet.IP.To4() { 133 | res = ipnet.IP.String() 134 | break 135 | } 136 | } 137 | } 138 | } 139 | 140 | return res, err 141 | } 142 | 143 | // OutboundIP 获取本机的出口IP. 144 | func (ko *LkkOS) OutboundIP() (string, error) { 145 | res := "" 146 | conn, err := net.Dial("udp", "8.8.8.8:80") 147 | if conn != nil { 148 | addr := conn.LocalAddr().(*net.UDPAddr) 149 | res = addr.IP.String() 150 | _ = conn.Close() 151 | } 152 | 153 | return res, err 154 | } 155 | 156 | // PrivateCIDR 获取私有网段的CIDR(无类别域间路由). 157 | func (ko *LkkOS) PrivateCIDR() []*net.IPNet { 158 | maxCidrBlocks := []string{ 159 | "127.0.0.1/8", // localhost 160 | "10.0.0.0/8", // 24-bit block 161 | "172.16.0.0/12", // 20-bit block 162 | "192.168.0.0/16", // 16-bit block 163 | "169.254.0.0/16", // link local address 164 | "::1/128", // localhost IPv6 165 | "fc00::/7", // unique local address IPv6 166 | "fe80::/10", // link local address IPv6 167 | } 168 | 169 | res := make([]*net.IPNet, len(maxCidrBlocks)) 170 | for i, maxCidrBlock := range maxCidrBlocks { 171 | _, cidr, _ := net.ParseCIDR(maxCidrBlock) 172 | res[i] = cidr 173 | } 174 | 175 | return res 176 | } 177 | 178 | // IsPrivateIp 是否私有IP地址(ipv4/ipv6). 179 | func (ko *LkkOS) IsPrivateIp(str string) (bool, error) { 180 | ip := net.ParseIP(str) 181 | if ip == nil { 182 | return false, errors.New("[IsPrivateIp]`str is not valid ip") 183 | } 184 | 185 | if kPrivCidrs == nil { 186 | kPrivCidrs = ko.PrivateCIDR() 187 | } 188 | for i := range kPrivCidrs { 189 | if kPrivCidrs[i].Contains(ip) { 190 | return true, nil 191 | } 192 | } 193 | 194 | return false, nil 195 | } 196 | 197 | // IsPublicIP 是否公网IPv4. 198 | func (ko *LkkOS) IsPublicIP(str string) (bool, error) { 199 | ip := net.ParseIP(str) 200 | if ip == nil { 201 | return false, errors.New("[IsPublicIP]`str is not valid ip") 202 | } 203 | 204 | if ip.IsLoopback() || ip.IsLinkLocalMulticast() || ip.IsLinkLocalUnicast() { 205 | return false, nil 206 | } 207 | if ip4 := ip.To4(); ip4 != nil { 208 | switch true { 209 | case ip4[0] == 10: 210 | return false, nil 211 | case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31: 212 | return false, nil 213 | case ip4[0] == 192 && ip4[1] == 168: 214 | return false, nil 215 | } 216 | } 217 | 218 | return true, nil 219 | } 220 | 221 | // GetIPs 获取本机的IP列表. 222 | func (ko *LkkOS) GetIPs() (ips []string) { 223 | interfaceAddrs, _ := net.InterfaceAddrs() 224 | if len(interfaceAddrs) > 0 { 225 | for _, addr := range interfaceAddrs { 226 | ipNet, isValidIpNet := addr.(*net.IPNet) 227 | if isValidIpNet && !ipNet.IP.IsLoopback() { 228 | if ipNet.IP.To4() != nil { 229 | ips = append(ips, ipNet.IP.String()) 230 | } 231 | } 232 | } 233 | } 234 | 235 | return 236 | } 237 | 238 | // GetMacAddrs 获取本机的Mac网卡地址列表. 239 | func (ko *LkkOS) GetMacAddrs() (macAddrs []string) { 240 | netInterfaces, _ := net.Interfaces() 241 | if len(netInterfaces) > 0 { 242 | for _, netInterface := range netInterfaces { 243 | macAddr := netInterface.HardwareAddr.String() 244 | if len(macAddr) == 0 { 245 | continue 246 | } 247 | macAddrs = append(macAddrs, macAddr) 248 | } 249 | } 250 | 251 | return 252 | } 253 | 254 | // Hostname 获取主机名. 255 | func (ko *LkkOS) Hostname() (string, error) { 256 | return os.Hostname() 257 | } 258 | 259 | // GetIpByHostname 返回主机名对应的 IPv4地址. 260 | func (ko *LkkOS) GetIpByHostname(hostname string) (string, error) { 261 | ips, err := net.LookupIP(hostname) 262 | if ips != nil { 263 | for _, v := range ips { 264 | if v.To4() != nil { 265 | return v.String(), nil 266 | } 267 | } 268 | return "", nil 269 | } 270 | return "", err 271 | } 272 | 273 | // GetIpsByDomain 获取互联网域名/主机名对应的 IPv4 地址列表. 274 | func (ko *LkkOS) GetIpsByDomain(domain string) ([]string, error) { 275 | ips, err := net.LookupIP(domain) 276 | if ips != nil { 277 | var ipstrs []string 278 | for _, v := range ips { 279 | if v.To4() != nil { 280 | ipstrs = append(ipstrs, v.String()) 281 | } 282 | } 283 | return ipstrs, nil 284 | } 285 | return nil, err 286 | } 287 | 288 | // GetHostByIp 获取指定的IP地址对应的主机名. 289 | func (ko *LkkOS) GetHostByIp(ipAddress string) (string, error) { 290 | names, err := net.LookupAddr(ipAddress) 291 | if names != nil { 292 | return strings.TrimRight(names[0], "."), nil 293 | } 294 | return "", err 295 | } 296 | 297 | // Setenv 设置一个环境变量的值. 298 | func (ko *LkkOS) Setenv(varname, data string) error { 299 | return os.Setenv(varname, data) 300 | } 301 | 302 | // Getenv 获取一个环境变量的值.defvalue为默认值. 303 | func (ko *LkkOS) Getenv(varname string, defvalue ...string) string { 304 | val := os.Getenv(varname) 305 | if val == "" && len(defvalue) > 0 { 306 | val = defvalue[0] 307 | } 308 | 309 | return val 310 | } 311 | 312 | // Unsetenv 删除一个环境变量. 313 | func (ko *LkkOS) Unsetenv(varname string) error { 314 | return os.Unsetenv(varname) 315 | } 316 | 317 | // GetEndian 获取系统字节序类型,小端返回binary.LittleEndian,大端返回binary.BigEndian . 318 | func (ko *LkkOS) GetEndian() binary.ByteOrder { 319 | return getEndian() 320 | } 321 | 322 | // IsLittleEndian 系统字节序类型是否小端存储. 323 | func (ko *LkkOS) IsLittleEndian() bool { 324 | return isLittleEndian() 325 | } 326 | 327 | // Exec 执行一个外部命令. 328 | // retInt为1时失败,为0时成功;outStr为执行命令的输出;errStr为错误输出. 329 | // 命令如 330 | // "ls -a" 331 | // "/bin/bash -c \"ls -a\"" 332 | func (ko *LkkOS) Exec(command string) (retInt int, outStr, errStr []byte) { 333 | // split command 334 | q := rune(0) 335 | parts := strings.FieldsFunc(command, func(r rune) bool { 336 | switch { 337 | case r == q: 338 | q = rune(0) 339 | return false 340 | case q != rune(0): 341 | return false 342 | case unicode.In(r, unicode.Quotation_Mark): 343 | q = r 344 | return false 345 | default: 346 | return unicode.IsSpace(r) 347 | } 348 | }) 349 | 350 | // remove the " and ' on both sides 351 | for i, v := range parts { 352 | f, l := v[0], len(v) 353 | if l >= 2 && (f == '"' || f == '\'') { 354 | parts[i] = v[1 : l-1] 355 | } 356 | } 357 | 358 | var stdout, stderr bytes.Buffer 359 | cmd := exec.Command(parts[0], parts[1:]...) 360 | cmd.Stdout = &stdout 361 | cmd.Stderr = &stderr 362 | err := cmd.Run() 363 | if err != nil { 364 | retInt = 1 //失败 365 | stderr.WriteString(err.Error()) 366 | errStr = stderr.Bytes() 367 | } else { 368 | retInt = 0 //成功 369 | outStr, errStr = stdout.Bytes(), stderr.Bytes() 370 | } 371 | 372 | return 373 | } 374 | 375 | // System 与Exec相同,但会同时打印标准输出和标准错误. 376 | func (ko *LkkOS) System(command string) (retInt int, outStr, errStr []byte) { 377 | // split command 378 | q := rune(0) 379 | parts := strings.FieldsFunc(command, func(r rune) bool { 380 | switch { 381 | case r == q: 382 | q = rune(0) 383 | return false 384 | case q != rune(0): 385 | return false 386 | case unicode.In(r, unicode.Quotation_Mark): 387 | q = r 388 | return false 389 | default: 390 | return unicode.IsSpace(r) 391 | } 392 | }) 393 | 394 | // remove the " and ' on both sides 395 | for i, v := range parts { 396 | f, l := v[0], len(v) 397 | if l >= 2 && (f == '"' || f == '\'') { 398 | parts[i] = v[1 : l-1] 399 | } 400 | } 401 | 402 | var stdout, stderr bytes.Buffer 403 | var err error 404 | 405 | cmd := exec.Command(parts[0], parts[1:]...) 406 | stdoutIn, _ := cmd.StdoutPipe() 407 | stderrIn, _ := cmd.StderrPipe() 408 | outWr := io.MultiWriter(os.Stdout, &stdout) 409 | errWr := io.MultiWriter(os.Stderr, &stderr) 410 | 411 | err = cmd.Start() 412 | if err != nil { 413 | retInt = 1 //失败 414 | stderr.WriteString(err.Error()) 415 | fmt.Printf("%s\n", stderr.Bytes()) 416 | return 417 | } 418 | 419 | _, _ = io.Copy(outWr, stdoutIn) 420 | _, _ = io.Copy(errWr, stderrIn) 421 | err = cmd.Wait() 422 | if err != nil { 423 | stderr.WriteString(err.Error()) 424 | fmt.Println(stderr.Bytes()) 425 | retInt = 1 //失败 426 | } else { 427 | retInt = 0 //成功 428 | } 429 | outStr, errStr = stdout.Bytes(), stderr.Bytes() 430 | 431 | return 432 | } 433 | 434 | // Chmod 改变文件模式. 435 | func (ko *LkkOS) Chmod(filename string, mode os.FileMode) bool { 436 | return os.Chmod(filename, mode) == nil 437 | } 438 | 439 | // Chown 改变文件的所有者. 440 | func (ko *LkkOS) Chown(filename string, uid, gid int) bool { 441 | return os.Chown(filename, uid, gid) == nil 442 | } 443 | 444 | // GetTempDir 返回用于临时文件的目录. 445 | func (ko *LkkOS) GetTempDir() string { 446 | return os.TempDir() 447 | } 448 | 449 | // ClientIp 获取客户端真实IP,req为http请求. 450 | func (ko *LkkOS) ClientIp(req *http.Request) string { 451 | // 获取头部信息,有可能是代理 452 | xRealIP := req.Header.Get("X-Real-Ip") 453 | xForwardedFor := req.Header.Get("X-Forwarded-For") 454 | 455 | // If both empty, return IP from remote address 456 | if xRealIP == "" && xForwardedFor == "" { 457 | var remoteIP string 458 | 459 | // If there are colon in remote address, remove the port number 460 | // otherwise, return remote address as is 461 | if strings.ContainsRune(req.RemoteAddr, ':') { 462 | remoteIP, _, _ = net.SplitHostPort(req.RemoteAddr) 463 | } else { 464 | remoteIP = req.RemoteAddr 465 | } 466 | 467 | return remoteIP 468 | } 469 | 470 | // Check list of IP in X-Forwarded-For and return the first global address 471 | // X-Forwarded-For是逗号分隔的IP地址列表,如"10.0.0.1, 10.0.0.2, 10.0.0.3" 472 | for _, address := range strings.Split(xForwardedFor, ",") { 473 | address = strings.TrimSpace(address) 474 | isPrivate, err := ko.IsPrivateIp(address) 475 | if !isPrivate && err == nil { 476 | return address 477 | } 478 | } 479 | 480 | if xRealIP == "::1" { 481 | xRealIP = "127.0.0.1" 482 | } 483 | 484 | // If nothing succeed, return X-Real-IP 485 | return xRealIP 486 | } 487 | 488 | // IsPortOpen 检查主机端口是否开放. 489 | // host为主机名;port为(整型/字符串)端口号;protocols为协议名称,可选,默认tcp. 490 | func (ko *LkkOS) IsPortOpen(host string, port interface{}, protocols ...string) bool { 491 | if KStr.IsHost(host) && isPort(port) { 492 | // 默认tcp协议 493 | protocol := "tcp" 494 | if len(protocols) > 0 && len(protocols[0]) > 0 { 495 | protocol = strings.ToLower(protocols[0]) 496 | } 497 | 498 | conn, _ := net.DialTimeout(protocol, net.JoinHostPort(host, KConv.ToStr(port)), CHECK_CONNECT_TIMEOUT) 499 | if conn != nil { 500 | _ = conn.Close() 501 | return true 502 | } 503 | } 504 | 505 | return false 506 | } 507 | 508 | // ForceGC 强制手动GC垃圾回收(阻塞). 509 | func (ko *LkkOS) ForceGC() { 510 | runtime.GC() 511 | debug.FreeOSMemory() 512 | } 513 | 514 | // TriggerGC 触发GC(非阻塞). 515 | func (ko *LkkOS) TriggerGC() { 516 | go func() { 517 | ko.ForceGC() 518 | }() 519 | } 520 | 521 | // GoMemory 获取当前go程序的内存使用,返回字节数. 522 | func (ko *LkkOS) GoMemory() uint64 { 523 | stat := new(runtime.MemStats) 524 | runtime.ReadMemStats(stat) 525 | return stat.Alloc 526 | } 527 | 528 | // GetSystemInfo 获取系统运行信息. 529 | func (ko *LkkOS) GetSystemInfo() *SystemInfo { 530 | //运行时信息 531 | mstat := &runtime.MemStats{} 532 | runtime.ReadMemStats(mstat) 533 | 534 | //CPU信息 535 | cpuUser, cpuIdel, cpuTotal := ko.CpuUsage() 536 | cpuUserRate := float64(cpuUser) / float64(cpuTotal) 537 | cpuFreeRate := float64(cpuIdel) / float64(cpuTotal) 538 | 539 | //磁盘空间信息 540 | var diskUsed, diskFree, diskTotal uint64 541 | if runtime.GOOS == "windows" { 542 | //TODO 待修改 543 | diskUsed, diskFree, diskTotal = ko.DiskUsage("C:") 544 | } else { 545 | diskUsed, diskFree, diskTotal = ko.DiskUsage("/") 546 | } 547 | 548 | //内存使用信息 549 | memUsed, memFree, memTotal := ko.MemoryUsage(true) 550 | 551 | serverName, _ := os.Hostname() 552 | uptime, _ := ko.Uptime() 553 | 554 | return &SystemInfo{ 555 | ServerName: serverName, 556 | SystemArch: runtime.GOARCH, 557 | SystemOs: runtime.GOOS, 558 | Runtime: uint64(KTime.ServiceUptime()), 559 | Uptime: uptime, 560 | GoroutineNum: runtime.NumGoroutine(), 561 | CpuNum: runtime.NumCPU(), 562 | CpuUser: cpuUserRate, 563 | CpuFree: cpuFreeRate, 564 | DiskUsed: diskUsed, 565 | DiskFree: diskFree, 566 | DiskTotal: diskTotal, 567 | MemUsed: memUsed, 568 | MemSys: mstat.Sys, 569 | MemFree: memFree, 570 | MemTotal: memTotal, 571 | AllocGolang: mstat.Alloc, 572 | AllocTotal: mstat.TotalAlloc, 573 | Lookups: mstat.Lookups, 574 | Mallocs: mstat.Mallocs, 575 | Frees: mstat.Frees, 576 | LastGCTime: mstat.LastGC, 577 | NextGC: mstat.NextGC, 578 | PauseTotalNs: mstat.PauseTotalNs, 579 | PauseNs: mstat.PauseNs[(mstat.NumGC+255)%256], 580 | } 581 | } 582 | 583 | // GetProcessExecPath 根据PID获取进程的执行路径. 584 | func (ko *LkkOS) GetProcessExecPath(pid int) string { 585 | return getProcessPathByPid(pid) 586 | } 587 | 588 | // HomeDir 获取当前用户的主目录. 589 | func (ko *LkkOS) HomeDir() (string, error) { 590 | return os.UserHomeDir() 591 | } 592 | 593 | // DownloadFile 下载文件.其中 594 | // url 为文件网址; 595 | // savePath 为保存路径; 596 | // cover 是否覆盖已有文件; 597 | // client 为自定义的请求客户端,不提供时默认使用http.DefaultClient. 598 | func (ko *LkkOS) DownloadFile(url string, savePath string, cover bool, client *http.Client) (written int64, err error) { 599 | if !isUrl(url) { 600 | return 0, errors.New("url is not a valid URL") 601 | } 602 | 603 | savePath = trim(savePath) 604 | if savePath == "" { 605 | return 0, errors.New("savePath cannot be empty") 606 | } 607 | 608 | //已存在 609 | if !cover && KFile.IsExist(savePath) { 610 | return 0, nil 611 | } 612 | 613 | if client == nil { 614 | client = http.DefaultClient 615 | } 616 | 617 | //父级目录 618 | dir := KFile.Dirname(savePath) 619 | if err = os.MkdirAll(dir, 0766); err != nil { 620 | return 0, err 621 | } 622 | 623 | var resp *http.Response 624 | resp, err = client.Get(url) 625 | if err != nil { 626 | return 0, err 627 | } 628 | defer func() { 629 | _ = resp.Body.Close() 630 | }() 631 | 632 | //创建文件 633 | var out *os.File 634 | out, err = os.Create(savePath) 635 | if err != nil { 636 | return 0, err 637 | } 638 | defer func() { 639 | _ = out.Close() 640 | }() 641 | 642 | //添加缓冲 bufio 是通过缓冲来提高效率。 643 | wt := bufio.NewWriter(out) 644 | written, err = io.Copy(wt, resp.Body) 645 | if err == nil { 646 | //将缓存的数据写入到文件中 647 | err = wt.Flush() 648 | } 649 | 650 | return written, err 651 | } 652 | -------------------------------------------------------------------------------- /number.go: -------------------------------------------------------------------------------- 1 | package kgo 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "math/rand" 7 | "reflect" 8 | "strconv" 9 | "time" 10 | ) 11 | 12 | // AbsFloat 浮点型取绝对值. 13 | func (kn *LkkNumber) AbsFloat(number float64) float64 { 14 | return math.Abs(number) 15 | } 16 | 17 | // AbsInt 整型取绝对值. 18 | func (kn *LkkNumber) AbsInt(number int64) int64 { 19 | r := number >> 63 20 | return (number ^ r) - r 21 | } 22 | 23 | // Range 根据范围创建数组,包含指定的元素. 24 | // start为起始元素值,end为末尾元素值.若startend,返回降序的数组. 25 | func (kn *LkkNumber) Range(start, end int) []int { 26 | res := make([]int, kn.AbsInt(int64(end-start))+1) 27 | for i := range res { 28 | if end > start { 29 | res[i] = start + i 30 | } else { 31 | res[i] = start - i 32 | } 33 | } 34 | return res 35 | } 36 | 37 | // NumberFormat 以千位分隔符方式格式化一个数字. 38 | // decimal为要保留的小数位数,point为小数点显示的字符,thousand为千位分隔符显示的字符. 39 | // 有效数值是长度(包括小数点)为17位之内的数值,最后一位会四舍五入. 40 | func (kn *LkkNumber) NumberFormat(number float64, decimal uint8, point, thousand string) string { 41 | neg := false 42 | if number < 0 { 43 | number = -number 44 | neg = true 45 | } 46 | dec := int(decimal) 47 | // Will round off 48 | str := fmt.Sprintf("%."+strconv.Itoa(dec)+"F", number) 49 | prefix, suffix := "", "" 50 | if dec > 0 { 51 | prefix = str[:len(str)-(dec+1)] 52 | suffix = str[len(str)-dec:] 53 | } else { 54 | prefix = str 55 | } 56 | sep := []byte(thousand) 57 | n, l1, l2 := 0, len(prefix), len(sep) 58 | // thousands sep num 59 | c := (l1 - 1) / 3 60 | tmp := make([]byte, l2*c+l1) 61 | pos := len(tmp) - 1 62 | for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 { 63 | if l2 > 0 && n > 0 && n%3 == 0 { 64 | for j := range sep { 65 | tmp[pos] = sep[l2-j-1] 66 | pos-- 67 | } 68 | } 69 | tmp[pos] = prefix[i] 70 | } 71 | s := string(tmp) 72 | if dec > 0 { 73 | s += point + suffix 74 | } 75 | if neg { 76 | s = "-" + s 77 | } 78 | 79 | return s 80 | } 81 | 82 | // FloatEqual 比较两个浮点数是否相等.decimal为小数精确位数,默认为 FLOAT_DECIMAL . 83 | // 有效数值是长度(包括小数点)为17位之内的数值,最后一位会四舍五入. 84 | func (kn *LkkNumber) FloatEqual(f1 float64, f2 float64, decimal ...uint8) (res bool) { 85 | var threshold float64 86 | var dec uint8 87 | if len(decimal) == 0 { 88 | dec = FLOAT_DECIMAL 89 | } else { 90 | dec = decimal[0] 91 | } 92 | 93 | //比较精度 94 | threshold = math.Pow10(-int(dec)) 95 | var diff float64 96 | if f1 > f2 { 97 | diff = f1 - f2 98 | } else { 99 | diff = f2 - f1 100 | } 101 | 102 | //diff := math.Abs(f1 - f2) 103 | res = diff <= threshold 104 | 105 | return 106 | } 107 | 108 | // RandInt64 生成一个min~max范围内的随机int64整数. 109 | func (kn *LkkNumber) RandInt64(min, max int64) int64 { 110 | if min > max { 111 | min, max = max, min 112 | } else if min == max { 113 | return min 114 | } 115 | 116 | //范围是否在边界内 117 | mMax := int64(math.MaxInt32) 118 | mMin := int64(math.MinInt32) 119 | inrang := (mMin <= min && max <= mMax) || (INT64_MIN <= min && max <= 0) || (0 <= min && max <= INT64_MAX) 120 | if !inrang { 121 | min, max = mMin, mMax 122 | } 123 | 124 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 125 | return r.Int63n(max-min) + min 126 | } 127 | 128 | // RandInt 生成一个min~max范围内的随机int整数. 129 | func (kn *LkkNumber) RandInt(min, max int) int { 130 | if min > max { 131 | min, max = max, min 132 | } else if min == max { 133 | return min 134 | } 135 | 136 | //范围是否在边界内 137 | mMax := int(math.MaxInt32) 138 | mMin := math.MinInt32 139 | inrang := (mMin <= min && max <= mMax) || (INT_MIN <= min && max <= 0) || (0 <= min && max <= INT_MAX) 140 | if !inrang { 141 | min, max = mMin, mMax 142 | } 143 | 144 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 145 | return r.Intn(max-min) + min 146 | } 147 | 148 | // Rand RandInt的别名. 149 | func (kn *LkkNumber) Rand(min, max int) int { 150 | return kn.RandInt(min, max) 151 | } 152 | 153 | // RandFloat64 生成一个min~max范围内的随机float64浮点数. 154 | func (kn *LkkNumber) RandFloat64(min, max float64) float64 { 155 | if min > max { 156 | min, max = max, min 157 | } 158 | 159 | //范围是否在边界内 160 | mMax := math.MaxFloat32 161 | mMin := -mMax 162 | inrang := (mMin <= min && max <= mMax) || (-math.MaxFloat64 <= min && max <= 0) || (0 <= min && max <= math.MaxFloat64) 163 | if !inrang { 164 | min, max = mMin, mMax 165 | } 166 | 167 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 168 | num := r.Float64() 169 | 170 | res := min + num*(max-min) 171 | return res 172 | } 173 | 174 | // Round 对浮点数(的整数)进行四舍五入. 175 | func (kn *LkkNumber) Round(value float64) float64 { 176 | return math.Floor(value + 0.5) 177 | } 178 | 179 | // RoundPlus 对指定的小数位进行四舍五入. 180 | // precision为小数位数. 181 | func (kn *LkkNumber) RoundPlus(value float64, precision uint8) float64 { 182 | shift := math.Pow(10, float64(precision)) 183 | return kn.Round(value*shift) / shift 184 | } 185 | 186 | // Floor 向下取整. 187 | func (kn *LkkNumber) Floor(value float64) float64 { 188 | return math.Floor(value) 189 | } 190 | 191 | // Ceil 向上取整. 192 | func (kn *LkkNumber) Ceil(value float64) float64 { 193 | return math.Ceil(value) 194 | } 195 | 196 | // MaxInt int整数序列求最大值. 197 | func (kn *LkkNumber) MaxInt(nums ...int) (res int) { 198 | if len(nums) < 1 { 199 | panic("[MaxInt]` nums length is less than 1") 200 | } 201 | 202 | res = nums[0] 203 | for _, v := range nums { 204 | if v > res { 205 | res = v 206 | } 207 | } 208 | 209 | return 210 | } 211 | 212 | // MaxInt64 int64整数序列求最大值. 213 | func (kn *LkkNumber) MaxInt64(nums ...int64) (res int64) { 214 | if len(nums) < 1 { 215 | panic("[MaxInt64]` nums length is less than 1") 216 | } 217 | 218 | res = nums[0] 219 | for _, v := range nums { 220 | if v > res { 221 | res = v 222 | } 223 | } 224 | 225 | return 226 | } 227 | 228 | // MaxFloat32 32位浮点数序列求最大值. 229 | func (kn *LkkNumber) MaxFloat32(nums ...float32) (res float32) { 230 | if len(nums) < 1 { 231 | panic("[MaxFloat32]` nums length is less than 1") 232 | } 233 | 234 | res = nums[0] 235 | for _, v := range nums { 236 | if v > res { 237 | res = v 238 | } 239 | } 240 | 241 | return 242 | } 243 | 244 | // MaxFloat64 64位浮点数序列求最大值. 245 | func (kn *LkkNumber) MaxFloat64(nums ...float64) (res float64) { 246 | if len(nums) < 1 { 247 | panic("[MaxFloat64]` nums length is less than 1") 248 | } 249 | 250 | res = nums[0] 251 | for _, v := range nums { 252 | res = math.Max(res, v) 253 | } 254 | 255 | return 256 | } 257 | 258 | // Max 取出任意类型中数值类型的最大值,无数值类型则为0. 259 | func (kn *LkkNumber) Max(nums ...interface{}) (res float64) { 260 | if len(nums) < 1 { 261 | panic("[Max]` nums length is less than 1") 262 | } 263 | 264 | var err error 265 | var val float64 266 | res, _ = numeric2Float(nums[0]) 267 | for _, v := range nums { 268 | val, err = numeric2Float(v) 269 | if err == nil { 270 | res = math.Max(res, val) 271 | } 272 | } 273 | 274 | return 275 | } 276 | 277 | // MinInt int整数序列求最小值. 278 | func (kn *LkkNumber) MinInt(nums ...int) (res int) { 279 | if len(nums) < 1 { 280 | panic("[MinInt]` nums length is less than 1") 281 | } 282 | res = nums[0] 283 | for _, v := range nums { 284 | if v < res { 285 | res = v 286 | } 287 | } 288 | 289 | return 290 | } 291 | 292 | // MinInt64 int64整数序列求最小值. 293 | func (kn *LkkNumber) MinInt64(nums ...int64) (res int64) { 294 | if len(nums) < 1 { 295 | panic("[MinInt64]` nums length is less than 1") 296 | } 297 | res = nums[0] 298 | for _, v := range nums { 299 | if v < res { 300 | res = v 301 | } 302 | } 303 | 304 | return 305 | } 306 | 307 | // MinFloat32 32位浮点数序列求最小值. 308 | func (kn *LkkNumber) MinFloat32(nums ...float32) (res float32) { 309 | if len(nums) < 1 { 310 | panic("[MinFloat32]` nums length is less than 1") 311 | } 312 | res = nums[0] 313 | for _, v := range nums { 314 | if v < res { 315 | res = v 316 | } 317 | } 318 | 319 | return 320 | } 321 | 322 | // MinFloat64 64位浮点数序列求最小值. 323 | func (kn *LkkNumber) MinFloat64(nums ...float64) (res float64) { 324 | if len(nums) < 1 { 325 | panic("[MinFloat64]` nums length is less than 1") 326 | } 327 | res = nums[0] 328 | for _, v := range nums { 329 | res = math.Min(res, v) 330 | } 331 | 332 | return 333 | } 334 | 335 | // Min 取出任意类型中数值类型的最小值,无数值类型则为0. 336 | func (kn *LkkNumber) Min(nums ...interface{}) (res float64) { 337 | if len(nums) < 1 { 338 | panic("[Min]` nums length is less than 1") 339 | } 340 | 341 | var err error 342 | var val float64 343 | res, _ = numeric2Float(nums[0]) 344 | for _, v := range nums { 345 | val, err = numeric2Float(v) 346 | if err == nil { 347 | res = math.Min(res, val) 348 | } 349 | } 350 | 351 | return 352 | } 353 | 354 | // Exp 计算 e 的指数. 355 | func (kn *LkkNumber) Exp(x float64) float64 { 356 | return math.Exp(x) 357 | } 358 | 359 | // Expm1 返回 exp(x) - 1. 360 | func (kn *LkkNumber) Expm1(x float64) float64 { 361 | return math.Exp(x) - 1 362 | } 363 | 364 | // Pow 指数表达式,求x的y次方. 365 | func (kn *LkkNumber) Pow(x, y float64) float64 { 366 | return math.Pow(x, y) 367 | } 368 | 369 | // Log 对数表达式,求以y为底x的对数. 370 | func (kn *LkkNumber) Log(x, y float64) float64 { 371 | return math.Log(x) / math.Log(y) 372 | } 373 | 374 | // ByteFormat 格式化文件比特大小. 375 | // size为文件大小,decimal为要保留的小数位数,delimiter为数字和单位间的分隔符. 376 | func (kn *LkkNumber) ByteFormat(size float64, decimal uint8, delimiter string) string { 377 | var arr = []string{"B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", Unknown} 378 | var pos int = 0 379 | var j float64 = size 380 | for { 381 | if size >= 1024 { 382 | size = size / 1024 383 | j = j / 1024 384 | pos++ 385 | } else { 386 | break 387 | } 388 | } 389 | if pos >= len(arr) { // fixed out index bug 390 | pos = len(arr) - 1 391 | } 392 | 393 | return fmt.Sprintf("%."+strconv.Itoa(int(decimal))+"f%s%s", j, delimiter, arr[pos]) 394 | } 395 | 396 | // IsOdd 变量是否奇数. 397 | func (kn *LkkNumber) IsOdd(val int) bool { 398 | return val%2 != 0 399 | } 400 | 401 | // IsEven 变量是否偶数. 402 | func (kn *LkkNumber) IsEven(val int) bool { 403 | return val%2 == 0 404 | } 405 | 406 | // NumSign 返回数值的符号.值>0为1,<0为-1,其他为0. 407 | func (kn *LkkNumber) NumSign(value float64) (res int8) { 408 | if value > 0 { 409 | res = 1 410 | } else if value < 0 { 411 | res = -1 412 | } else { 413 | res = 0 414 | } 415 | 416 | return 417 | } 418 | 419 | // IsNegative 数值是否为负数. 420 | func (kn *LkkNumber) IsNegative(value float64) bool { 421 | return value < 0 422 | } 423 | 424 | // IsPositive 数值是否为正数. 425 | func (kn *LkkNumber) IsPositive(value float64) bool { 426 | return value > 0 427 | } 428 | 429 | // IsNonNegative 数值是否为非负数. 430 | func (kn *LkkNumber) IsNonNegative(value float64) bool { 431 | return value >= 0 432 | } 433 | 434 | // IsNonPositive 数值是否为非正数. 435 | func (kn *LkkNumber) IsNonPositive(value float64) bool { 436 | return value <= 0 437 | } 438 | 439 | // IsWhole 数值是否为整数. 440 | func (kn *LkkNumber) IsWhole(value float64) bool { 441 | return math.Remainder(value, 1) == 0 442 | } 443 | 444 | // IsNatural 数值是否为自然数(包括0). 445 | func (kn *LkkNumber) IsNatural(value float64) bool { 446 | return kn.IsNonNegative(value) && kn.IsWhole(value) 447 | } 448 | 449 | // InRangeInt 数值是否在2个整数范围内. 450 | func (kn *LkkNumber) InRangeInt(value, left, right int) bool { 451 | if left > right { 452 | left, right = right, left 453 | } 454 | return value >= left && value <= right 455 | } 456 | 457 | // InRangeFloat64 数值是否在2个64位浮点数范围内. 458 | func (kn *LkkNumber) InRangeFloat64(value, left, right float64) bool { 459 | if left > right { 460 | left, right = right, left 461 | } 462 | return value >= left && value <= right 463 | } 464 | 465 | // InRangeFloat32 数值是否在2个32位浮点数范围内. 466 | func (kn *LkkNumber) InRangeFloat32(value, left, right float32) bool { 467 | if left > right { 468 | left, right = right, left 469 | } 470 | return value >= left && value <= right 471 | } 472 | 473 | // InRange 数值是否在某个范围内,将自动转换类型再比较. 474 | func (kn *LkkNumber) InRange(value interface{}, left interface{}, right interface{}) bool { 475 | reflectValue := reflect.TypeOf(value).Kind() 476 | reflectLeft := reflect.TypeOf(left).Kind() 477 | reflectRight := reflect.TypeOf(right).Kind() 478 | 479 | if reflectValue == reflect.Int && reflectLeft == reflect.Int && reflectRight == reflect.Int { 480 | return kn.InRangeInt(value.(int), left.(int), right.(int)) 481 | } else if reflectValue == reflect.Float32 && reflectLeft == reflect.Float32 && reflectRight == reflect.Float32 { 482 | return kn.InRangeFloat32(value.(float32), left.(float32), right.(float32)) 483 | } else if reflectValue == reflect.Float64 && reflectLeft == reflect.Float64 && reflectRight == reflect.Float64 { 484 | return kn.InRangeFloat64(value.(float64), left.(float64), right.(float64)) 485 | } else if KConv.IsInt(value) && KConv.IsInt(left) && KConv.IsInt(right) { 486 | return kn.InRangeInt(KConv.ToInt(value), KConv.ToInt(left), KConv.ToInt(right)) 487 | } else if KConv.IsNumeric(value) && KConv.IsNumeric(left) && KConv.IsNumeric(right) { 488 | return kn.InRangeFloat64(KConv.ToFloat(value), KConv.ToFloat(left), KConv.ToFloat(right)) 489 | } 490 | 491 | return false 492 | } 493 | 494 | // SumInt int整数求和. 495 | func (kn *LkkNumber) SumInt(nums ...int) int { 496 | var sum int 497 | for _, v := range nums { 498 | sum += v 499 | } 500 | return sum 501 | } 502 | 503 | // SumInt64 int64整数求和. 504 | func (kn *LkkNumber) SumInt64(nums ...int64) int64 { 505 | var sum int64 506 | for _, v := range nums { 507 | sum += v 508 | } 509 | return sum 510 | } 511 | 512 | // SumFloat32 32位浮点数求和. 513 | func (kn *LkkNumber) SumFloat32(nums ...float32) float32 { 514 | var sum float32 515 | for _, v := range nums { 516 | sum += v 517 | } 518 | return sum 519 | } 520 | 521 | // SumFloat64 64位浮点数求和. 522 | func (kn *LkkNumber) SumFloat64(nums ...float64) float64 { 523 | var sum float64 524 | for _, v := range nums { 525 | sum += v 526 | } 527 | return sum 528 | } 529 | 530 | // Sum 对任意类型序列中的数值类型求和,忽略非数值的. 531 | func (kn *LkkNumber) Sum(nums ...interface{}) (res float64) { 532 | var err error 533 | var val float64 534 | for _, v := range nums { 535 | val, err = numeric2Float(v) 536 | if err == nil { 537 | res += val 538 | } 539 | } 540 | 541 | return 542 | } 543 | 544 | // AverageInt 对int整数序列求平均值. 545 | func (kn *LkkNumber) AverageInt(nums ...int) (res float64) { 546 | length := len(nums) 547 | if length == 0 { 548 | return 549 | } else if length == 1 { 550 | res = float64(nums[0]) 551 | } else { 552 | total := kn.SumInt(nums...) 553 | res = float64(total) / float64(length) 554 | } 555 | 556 | return 557 | } 558 | 559 | // AverageInt64 对int64整数序列求平均值. 560 | func (kn *LkkNumber) AverageInt64(nums ...int64) (res float64) { 561 | length := len(nums) 562 | if length == 0 { 563 | return 564 | } else if length == 1 { 565 | res = float64(nums[0]) 566 | } else { 567 | total := kn.SumInt64(nums...) 568 | res = float64(total) / float64(length) 569 | } 570 | 571 | return 572 | } 573 | 574 | // AverageFloat32 对32位浮点数序列求平均值. 575 | func (kn *LkkNumber) AverageFloat32(nums ...float32) (res float32) { 576 | length := len(nums) 577 | if length == 0 { 578 | return 579 | } else if length == 1 { 580 | res = nums[0] 581 | } else { 582 | total := kn.SumFloat32(nums...) 583 | res = total / float32(length) 584 | } 585 | 586 | return 587 | } 588 | 589 | // AverageFloat64 对64位浮点数序列求平均值. 590 | func (kn *LkkNumber) AverageFloat64(nums ...float64) (res float64) { 591 | length := len(nums) 592 | if length == 0 { 593 | return 594 | } else if length == 1 { 595 | res = nums[0] 596 | } else { 597 | total := kn.SumFloat64(nums...) 598 | res = total / float64(length) 599 | } 600 | 601 | return 602 | } 603 | 604 | // Average 对任意类型序列中的数值类型求平均值,忽略非数值的. 605 | func (kn *LkkNumber) Average(nums ...interface{}) (res float64) { 606 | length := len(nums) 607 | if length == 0 { 608 | return 609 | } else if length == 1 { 610 | res, _ = numeric2Float(nums[0]) 611 | } else { 612 | var count int 613 | var err error 614 | var val, total float64 615 | for _, v := range nums { 616 | val, err = numeric2Float(v) 617 | if err == nil { 618 | count++ 619 | total += val 620 | } 621 | } 622 | 623 | res = total / float64(count) 624 | } 625 | 626 | return 627 | } 628 | 629 | // Percent 返回百分比((val/total) *100). 630 | func (kn *LkkNumber) Percent(val, total interface{}) float64 { 631 | t := toFloat(total) 632 | if t == 0 { 633 | return float64(0) 634 | } 635 | 636 | v := toFloat(val) 637 | 638 | return (v / t) * 100 639 | } 640 | 641 | // IsNan 是否为“非数值”.注意,这里复数也算“非数值”. 642 | func (kn *LkkNumber) IsNan(val interface{}) bool { 643 | if isFloat(val) { 644 | return math.IsNaN(KConv.ToFloat(val)) 645 | } 646 | 647 | return !isNumeric(val) 648 | } 649 | 650 | // IsNaturalRange 是否连续的自然数数组/切片,如[0,1,2,3...],其中不能有间断. 651 | // strict为是否严格检查元素的顺序. 652 | func (kn *LkkNumber) IsNaturalRange(arr []int, strict bool) (res bool) { 653 | n := len(arr) 654 | if n == 0 { 655 | return 656 | } 657 | 658 | orig := kn.Range(0, n-1) 659 | ctyp := COMPARE_ONLY_VALUE 660 | 661 | if strict { 662 | ctyp = COMPARE_BOTH_KEYVALUE 663 | } 664 | 665 | diff := KArr.ArrayDiff(orig, arr, ctyp) 666 | 667 | res = len(diff) == 0 668 | return 669 | } 670 | 671 | // GeoDistance 获取地理距离/米. 672 | // 参数分别为两点的经度和纬度:lat:-90~90,lng:-180~180. 673 | func (kn *LkkNumber) GeoDistance(lng1, lat1, lng2, lat2 float64) float64 { 674 | //地球半径 675 | radius := 6371000.0 676 | rad := math.Pi / 180.0 677 | 678 | lng1 = lng1 * rad 679 | lat1 = lat1 * rad 680 | lng2 = lng2 * rad 681 | lat2 = lat2 * rad 682 | theta := lng2 - lng1 683 | 684 | dist := math.Acos(math.Sin(lat1)*math.Sin(lat2) + math.Cos(lat1)*math.Cos(lat2)*math.Cos(theta)) 685 | return dist * radius 686 | } 687 | 688 | // NearLogarithm 求以 base 为底 num 的对数临近值. 689 | // num为自然数,base为正整数,left是否向左取整. 690 | func (kn *LkkNumber) NearLogarithm(num, base int, left bool) int { 691 | if num < 0 { 692 | panic("[nearLogarithm]` num must be non-negative") 693 | } else if base <= 0 { 694 | panic("[nearLogarithm]` base must be a positive integer") 695 | } 696 | 697 | res := kn.Log(float64(num), float64(base)) 698 | if left { 699 | return int(kn.Floor(res)) 700 | } 701 | 702 | return int(kn.Ceil(res)) 703 | } 704 | 705 | // SplitNaturalNum 将自然数 num 按底数 base 进行拆解;所得结果的列表累计和为num. 706 | func (kn *LkkNumber) SplitNaturalNum(num, base int) []int { 707 | var res []int 708 | 709 | if !kn.IsNatural(toFloat(num)) { 710 | panic("[splitNaturalNum]` num must be a natural number") 711 | } else if base <= 0 { 712 | panic("[splitNaturalNum]` base must be a positive integer") 713 | } 714 | 715 | var n, child int 716 | for num > base { 717 | n = kn.NearLogarithm(num, base, true) 718 | child = int(math.Pow(float64(base), float64(n))) 719 | num -= child 720 | res = append(res, child) 721 | } 722 | 723 | if (num > 0) || (num == 0 && len(res) == 0) { 724 | res = append(res, num) 725 | } 726 | 727 | return res 728 | } 729 | -------------------------------------------------------------------------------- /docs/changelog.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | ## [v0.5.1]- 2025-05-13 6 | 7 | #### Added 8 | 9 | - none 10 | 11 | #### Fixed 12 | 13 | - none 14 | 15 | #### Changed 16 | 17 | - 修改`KEncr.RsaPrivateEncrypt`,兼容PKCS#8 18 | - 修改`KEncr.RsaPrivateDecryptLong`,兼容PKCS#8 19 | - 修改`KEncr.RsaPrivateEncryptLong`,兼容PKCS#8 20 | 21 | #### Removed 22 | 23 | - none 24 | 25 | ## [v0.5.0]- 2025-05-11 26 | 27 | #### Added 28 | 29 | - none 30 | 31 | #### Fixed 32 | 33 | - none 34 | 35 | #### Changed 36 | 37 | - 修改`KEncr.RsaPrivateDecrypt`,兼容PKCS#8 38 | 39 | #### Removed 40 | 41 | - none 42 | 43 | ## [v0.4.9]- 2025-01-09 44 | 45 | #### Added 46 | 47 | - none 48 | 49 | #### Fixed 50 | 51 | - none 52 | 53 | #### Changed 54 | 55 | - 修改`KEncr.AuthCode`,返回结果增加`error` 56 | 57 | #### Removed 58 | 59 | - none 60 | 61 | ## [v0.4.8]- 2024-12-15 62 | 63 | #### Added 64 | 65 | - none 66 | 67 | #### Fixed 68 | 69 | - 修改darwin的`KOS.GetBiosInfo`,返回空信息,因为苹果系统无BIOS. 70 | 71 | #### Changed 72 | 73 | - 修改一些单元测试和注释 74 | 75 | #### Removed 76 | 77 | - none 78 | 79 | ## [v0.4.7]- 2024-03-06 80 | 81 | #### Added 82 | 83 | - none 84 | 85 | #### Fixed 86 | 87 | - none 88 | 89 | #### Changed 90 | 91 | - 修改`KTime.Str2Timestruct`,将`format`参数改为`option`,以支持UTC和本地时区 92 | - 修改`KTime.Str2Timestamp`,将`format`参数改为`option`,以支持UTC和本地时区 93 | - 修改`KTime.IsDate2time`,增加参数`option`,以支持UTC和本地时区 94 | 95 | #### Removed 96 | 97 | - none 98 | 99 | ## [v0.4.6]- 2023-10-21 100 | 101 | #### Added 102 | 103 | - none 104 | 105 | #### Fixed 106 | 107 | - 修复`isUrl`,字符串是否URL 108 | - 修复`KStr.RemoveBefore`,移除before之前的字符串 109 | - 修复`KStr.RemoveAfter`,移除after之后的字符串 110 | 111 | #### Changed 112 | 113 | - 优化`KFile.Pathinfo`,获取文件路径的信息 114 | 115 | #### Removed 116 | 117 | - none 118 | 119 | ## [v0.4.5]- 2023-09-24 120 | 121 | #### Added 122 | 123 | - 新增`KTime.FormatDuration`,格式化时长为字符串 124 | 125 | #### Fixed 126 | 127 | - none 128 | 129 | #### Changed 130 | 131 | - none 132 | 133 | #### Removed 134 | 135 | - none 136 | 137 | ## [v0.4.4]- 2023-08-25 138 | 139 | #### Added 140 | 141 | - none 142 | 143 | #### Fixed 144 | 145 | - 修改`KFile.Pathinfo`,支持URL 146 | 147 | #### Changed 148 | 149 | - none 150 | 151 | #### Removed 152 | 153 | - none 154 | 155 | ## [v0.4.3]- 2023-07-19 156 | 157 | #### Added 158 | 159 | - none 160 | 161 | #### Fixed 162 | 163 | - none 164 | 165 | #### Changed 166 | 167 | - 修改`KArr.CopyStruct`,支持嵌套结构体 168 | 169 | #### Removed 170 | 171 | - none 172 | 173 | ## [v0.4.2]- 2023-07-17 174 | 175 | #### Added 176 | 177 | - none 178 | 179 | #### Fixed 180 | 181 | - none 182 | 183 | #### Changed 184 | 185 | - 修改`KArr.CopyStruct`,返回值类型为error 186 | 187 | #### Removed 188 | 189 | - none 190 | 191 | ## [v0.4.1]- 2023-03-27 192 | 193 | #### Added 194 | 195 | - none 196 | 197 | #### Fixed 198 | 199 | - none 200 | 201 | #### Changed 202 | 203 | - 修改`KStr.PasswordSafeLevel`,先检查是否弱密码 204 | 205 | #### Removed 206 | 207 | - none 208 | 209 | ## [v0.4.0]- 2023-02-24 210 | 211 | #### Added 212 | 213 | - none 214 | 215 | #### Fixed 216 | 217 | - none 218 | 219 | #### Changed 220 | 221 | - `github.com/StackExchange/wmi`不再维护,替换为`github.com/yusufpapurcu/wmi` 222 | - 更新包依赖 223 | 224 | #### Removed 225 | 226 | - none 227 | 228 | ## [v0.3.9]- 2023-01-30 229 | 230 | #### Added 231 | 232 | - 新增`LkkNumber.SumInt64`,int64整数求和 233 | - 新增`LkkNumber.SumFloat32`,32位浮点数求和 234 | - 新增`LkkNumber.AverageInt64`,对int64整数序列求平均值 235 | - 新增`LkkNumber.AverageFloat32`,对32位浮点数序列求平均值 236 | 237 | #### Fixed 238 | 239 | - none 240 | 241 | #### Changed 242 | 243 | - none 244 | 245 | #### Removed 246 | 247 | - none 248 | 249 | ## [v0.3.8]- 2023-01-02 250 | 251 | #### Added 252 | 253 | - 新增`LkkNumber.MaxInt64`,int64整数序列求最大值 254 | - 新增`LkkNumber.MaxFloat32`,32位浮点数序列求最大值 255 | - 新增`LkkNumber.MinInt64`,int64整数序列求最小值 256 | - 新增`LkkNumber.MinFloat32`,32位浮点数序列求最小值 257 | 258 | #### Fixed 259 | 260 | - none 261 | 262 | #### Changed 263 | 264 | - none 265 | 266 | #### Removed 267 | 268 | - none 269 | 270 | ## [v0.3.7]- 2022-12-31 271 | 272 | #### Added 273 | 274 | - none 275 | 276 | #### Fixed 277 | 278 | - none 279 | 280 | #### Changed 281 | 282 | - 修改`LkkString.IsBase64Image`,增加返回扩展名 283 | 284 | #### Removed 285 | 286 | - none 287 | 288 | ## [v0.3.6]- 2022-11-29 289 | 290 | #### Added 291 | 292 | - 新增`KOS.DownloadFile`,文件下载 293 | 294 | #### Fixed 295 | 296 | - none 297 | 298 | #### Changed 299 | 300 | - none 301 | 302 | #### Removed 303 | 304 | - none 305 | 306 | ## [v0.3.5]- 2022-10-10 307 | 308 | #### Added 309 | 310 | - 新增`KStr.StrOffset`,字符串整体偏移 311 | 312 | #### Fixed 313 | 314 | - fix ioutil.ReadAll Deprecated, replace to io.ReadAll 315 | - fix ioutil.ReadFile Deprecated, replace to os.ReadFile 316 | 317 | #### Changed 318 | 319 | - none 320 | 321 | #### Removed 322 | 323 | - none 324 | 325 | ## [v0.3.4]- 2022-09-05 326 | 327 | #### Added 328 | 329 | - 新增`LkkFile.Md5Reader` 330 | - 新增`LkkFile.ShaXReader` 331 | 332 | #### Fixed 333 | 334 | - none 335 | 336 | #### Changed 337 | 338 | - 重命名 `LkkFile.Md5` 为 `LkkFile.Md5File` 339 | - 重命名 `LkkFile.ShaX` 为 `LkkFile.ShaXFile` 340 | 341 | #### Removed 342 | 343 | - none 344 | 345 | ## [v0.3.3]- 2022-08-01 346 | 347 | #### Added 348 | 349 | - none 350 | 351 | #### Fixed 352 | 353 | - 优化`KFile.CopyFile` 354 | - 优化`KFile.FastCopy` 355 | - 优化`KFile.CopyDir` 356 | - 优化`KFile.DelDir` 357 | - 优化`KFile.WriteFile` 358 | - 优化`KFile.FileTree` 359 | - 优化`KFile.Md5` 360 | - 优化`KFile.IsZip` 361 | - 优化`KFile.TarGz` 362 | - 优化`KFile.UnTarGz` 363 | - 优化os_darwin_cgo下`getProcessPathByPid` 364 | - 优化os_darwin_cgo下`LkkOS.CpuUsage` 365 | - 优化os_linux下`getPidByInode` 366 | - 优化os_linux下`LkkOS.Uptime` 367 | - 优化os_windows下`LkkOS.MemoryUsage` 368 | - 优化os_windows下`LkkOS.CpuUsage` 369 | - 优化os_windows下`LkkOS.DiskUsage` 370 | - 优化os_windows下`LkkOS.Uptime` 371 | - 优化os_windows下`LkkOS.GetBiosInfo` 372 | - 优化os_windows下`LkkOS.GetBoardInfo` 373 | - 优化os_windows下`LkkOS.GetCpuInfo` 374 | - 优化os_windows下`LkkOS.IsProcessExists` 375 | - 优化`isEmpty` 376 | - 优化`shaXByte` 377 | - 优化`pkcs7UnPadding` 378 | 379 | #### Changed 380 | 381 | - 修改`KFile.CopyLink`,增加`cover`文件覆盖参数 382 | - 修改`LkkOS.HomeDir`,不再自行区分windows/unix,使用自带的`os.UserHomeDir` 383 | 384 | #### Removed 385 | 386 | - 删除os_darwin下`getPidByPort` 387 | - 删除os_windows下`getPidByPort` 388 | 389 | ## [v0.3.2]- 2022-06-26 390 | 391 | #### Added 392 | 393 | - none 394 | 395 | #### Fixed 396 | 397 | - 修复`KConv.IsFloat` 398 | 399 | #### Changed 400 | 401 | - none 402 | 403 | #### Removed 404 | 405 | - none 406 | 407 | ## [v0.3.1]- 2022-06-21 408 | 409 | #### Added 410 | 411 | - none 412 | 413 | #### Fixed 414 | 415 | - 修复`KFile.Touch`,创建文件前检查文件是否存在 416 | - 修复非cgo的darwin下`KOS.GetProcessExecPath`编译失败问题 417 | 418 | #### Changed 419 | 420 | - `SystemInfo`系统信息结构体增加`SystemArch`字段 421 | 422 | #### Removed 423 | 424 | - none 425 | 426 | ## [v0.3.0]- 2022-05-27 427 | 428 | #### Added 429 | 430 | - 新增`KStr.PasswordSafeLevel`,检查密码安全等级 431 | 432 | #### Fixed 433 | 434 | - none 435 | 436 | #### Changed 437 | 438 | - none 439 | 440 | #### Removed 441 | 442 | - none 443 | 444 | ## [v0.2.9]- 2022-04-15 445 | 446 | #### Added 447 | 448 | - none 449 | 450 | #### Fixed 451 | 452 | - 修复`KStr.MatchEquations` 453 | 454 | #### Changed 455 | 456 | - 优化`LkkEncrypt.aesDecrypt` 457 | 458 | #### Removed 459 | 460 | - none 461 | 462 | ## [v0.2.8]- 2022-04-15 463 | 464 | #### Added 465 | 466 | - none 467 | 468 | #### Fixed 469 | 470 | - none 471 | 472 | #### Changed 473 | 474 | - rename `KArr.CopyToStruct` to `KArr.CopyStruct` 475 | 476 | #### Removed 477 | 478 | - none 479 | 480 | ## [v0.2.7]- 2022-04-14 481 | 482 | #### Added 483 | 484 | - none 485 | 486 | #### Fixed 487 | 488 | - 修改`reflectTypesMap`,获取匿名字段 489 | 490 | #### Changed 491 | 492 | - none 493 | 494 | #### Removed 495 | 496 | - none 497 | 498 | ## [v0.2.6]- 2022-04-14 499 | 500 | #### Added 501 | 502 | - 新增`KArr.CopyToStruct`,拷贝结构体 503 | 504 | #### Fixed 505 | 506 | - none 507 | 508 | #### Changed 509 | 510 | - none 511 | 512 | #### Removed 513 | 514 | - none 515 | 516 | ## [v0.2.5]- 2022-03-31 517 | 518 | #### Added 519 | 520 | - none 521 | 522 | #### Fixed 523 | 524 | - 优化`KEncr.AuthCode` 525 | 526 | #### Changed 527 | 528 | - none 529 | 530 | #### Removed 531 | 532 | - none 533 | 534 | ## [v0.2.4]- 2022-03-05 535 | 536 | #### Added 537 | 538 | - 新增`KDbug.Stacks`,获取堆栈信息 539 | 540 | #### Fixed 541 | 542 | - 修改`KDbug.GetCallName`,参数`f`支持`uintptr`类型 543 | 544 | #### Changed 545 | 546 | - none 547 | 548 | #### Removed 549 | 550 | - none 551 | 552 | ## [v0.2.3]- 2022-01-21 553 | 554 | #### Added 555 | 556 | - 新增`KStr.UuidV5`,根据提供的字符,使用sha1生成36位哈希值 557 | 558 | #### Fixed 559 | 560 | - 修改`KStr.UuidV4`,添加version/variant信息 561 | 562 | #### Changed 563 | 564 | - none 565 | 566 | #### Removed 567 | 568 | - none 569 | 570 | ## [v0.2.2]- 2022-01-10 571 | 572 | #### Added 573 | 574 | - 新增`KEncr.RsaPrivateEncryptLong`使用RSA私钥加密长文本 575 | - 新增`KEncr.RsaPublicDecryptLong`使用RSA公钥解密长文本 576 | 577 | #### Fixed 578 | 579 | - none 580 | 581 | #### Changed 582 | 583 | - none 584 | 585 | #### Removed 586 | 587 | - none 588 | 589 | ## [v0.2.1]- 2022-01-05 590 | 591 | #### Added 592 | 593 | - 新增`KStr.ChunkBytes`将字节切片分割为多个小块 594 | - 新增`KEncr.RsaPublicEncryptLong`使用RSA公钥加密长文本 595 | - 新增`KEncr.RsaPrivateDecryptLong`使用RSA私钥解密长文本 596 | 597 | #### Fixed 598 | 599 | - none 600 | 601 | #### Changed 602 | 603 | - none 604 | 605 | #### Removed 606 | 607 | - none 608 | 609 | ## [v0.2.0]- 2021-12-09 610 | 611 | #### Added 612 | 613 | - 新增`KStr.ToRunes`将字符串转为字符切片 614 | - 新增`KConv.ToInterfaces`将变量转为接口切片 615 | - 新增`KDbug.WrapError`错误包裹 616 | 617 | #### Fixed 618 | 619 | - 修改`KStr.IsASCII`根据字符串长度使用不同方法 620 | 621 | #### Changed 622 | 623 | - 重命名`KStr.IsHexcolor`为`IsHexColor` 624 | - 重命名`KStr.IsRGBcolor`为`IsRgbColor` 625 | - 将部分公开变量转为私有 626 | 627 | #### Removed 628 | 629 | - none 630 | 631 | ## [v0.1.9]- 2021-11-27 632 | 633 | #### Added 634 | 635 | - 新增`IsPointer`检查变量是否指针类型 636 | 637 | #### Fixed 638 | 639 | - none 640 | 641 | #### Changed 642 | 643 | - none 644 | 645 | #### Removed 646 | 647 | - none 648 | 649 | ## [v0.1.8]- 2021-11-08 650 | 651 | #### Added 652 | 653 | - none 654 | 655 | #### Fixed 656 | 657 | - 修复`KDbug.DumpPrint`打印多变量问题 658 | - 修改`KStr.Ucwords`因go1.18废弃strings.Title,使用cases.Title代替 659 | 660 | #### Changed 661 | 662 | - none 663 | 664 | #### Removed 665 | 666 | - none 667 | 668 | ## [v0.1.7]- 2021-10-15 669 | 670 | #### Added 671 | 672 | - none 673 | 674 | #### Fixed 675 | 676 | - 修复`KEncr.AuthCode`中keyb变化问题 677 | 678 | #### Changed 679 | 680 | - none 681 | 682 | #### Removed 683 | 684 | - none 685 | 686 | ## [v0.1.6]- 2021-8-21 687 | 688 | #### Added 689 | 690 | - 新增`KNum.NearLogarithm`,求对数临近值 691 | - 新增`KNum.SplitNaturalNum`,将自然数按底数拆解 692 | 693 | #### Fixed 694 | 695 | - none 696 | 697 | #### Changed 698 | 699 | - none 700 | 701 | #### Removed 702 | 703 | - none 704 | 705 | ## [v0.1.5]- 2021-6-11 706 | 707 | - 重构版本,改动太多,懒得整理,就不再一一列出 708 | 709 | ## [v0.1.4]- 2020-12-1 710 | 711 | #### Added 712 | 713 | - 新增`KArr.ArrayIntersect`,求数组交集 714 | - 新增`KArr.DeleteSliceItems`,删除切片多个元素 715 | - 新增`KFile.FormatPath`,格式化路径 716 | - 新增`KNum.IsNaturalRange`,是否自然数数组 717 | - 新增`KNum.Log`,求任意底数的对数 718 | 719 | #### Fixed 720 | 721 | - 修改`KNum.IsNatural`,自然数包含0 722 | 723 | #### Changed 724 | 725 | - 修改`KArr.ArrayDiff`,增加比较方式参数`compareType`,返回字典 726 | - 修改`KFile.FormatDir`,过滤特殊字符 727 | - 修改`KNum.IsNan`,接收任意类型参数 728 | - 修改`KNum.NumSign`,结果类型为int8 729 | - 修改`KNum.Range`,支持生成降序的数组 730 | 731 | #### Removed 732 | 733 | - none 734 | 735 | ## [v0.1.3]- 2020-11-11 736 | 737 | #### Added 738 | 739 | - none 740 | 741 | #### Fixed 742 | 743 | - none 744 | 745 | #### Changed 746 | 747 | - 修改`KStr.Chr`,使用rune转换字符码 748 | - 修改`KDbug.CallMethod`错误提示 749 | 750 | #### Removed 751 | 752 | - none 753 | 754 | ## [v0.1.2]- 2020-06-25 755 | 756 | #### Added 757 | 758 | - `KStr.Md5Byte` 759 | - `KStr.ShaXByte` 760 | 761 | #### Fixed 762 | 763 | - none 764 | 765 | #### Changed 766 | 767 | - rename `md5Str` to `md5Byte` 768 | - rename `shaXStr` to `shaXByte` 769 | 770 | #### Removed 771 | 772 | - none 773 | 774 | ## [v0.1.1]- 2020-06-23 775 | 776 | #### Added 777 | 778 | - `KStr.Serialize` 779 | - `KStr.UnSerialize` 780 | - `KStr.TrimBOM` 781 | 782 | #### Fixed 783 | 784 | - none 785 | 786 | #### Changed 787 | 788 | - none 789 | 790 | #### Removed 791 | 792 | - none 793 | 794 | ## [v0.1.0]- 2020-06-22 795 | 796 | #### Added 797 | 798 | - none 799 | 800 | #### Fixed 801 | 802 | - none 803 | 804 | #### Changed 805 | 806 | - 修改`KTime.Str2Timestruct`使用本地时区而非UTC 807 | 808 | #### Removed 809 | 810 | - none 811 | 812 | ## [v0.0.9]- 2020-06-20 813 | 814 | #### Added 815 | 816 | - none 817 | 818 | #### Fixed 819 | 820 | - none 821 | 822 | #### Changed 823 | 824 | - 修改`KEncr.Base64Encode`结果类型为[]byte 825 | - 修改`KEncr.Base64Decode`参数类型为[]byte 826 | - 修改`KEncr.Base64UrlEncode`结果类型为[]byte 827 | - 修改`KEncr.Base64UrlDecode`结果类型为[]byte 828 | - 修改`KEncr.AuthCode`参数和结果类型为[]byte 829 | - 修改`KEncr.EasyEncrypt`参数和结果类型为[]byte 830 | - 修改`KEncr.EasyDecrypt`参数和结果类型为[]byte 831 | - 修改`KEncr.HmacShaX`结果类型为[]byte 832 | 833 | #### Removed 834 | 835 | - none 836 | 837 | ## [v0.0.8]- 2020-05-31 838 | 839 | #### Added 840 | 841 | - none 842 | 843 | #### Fixed 844 | 845 | - none 846 | 847 | #### Changed 848 | 849 | - `KOS.GetSystemInfo`增加`SystemOs`操作系统名称字段 850 | 851 | #### Removed 852 | 853 | - none 854 | 855 | ## [v0.0.7]- 2020-05-21 856 | 857 | #### Added 858 | 859 | - none 860 | 861 | #### Fixed 862 | 863 | - 修复`KStr.ToCamelCase`将首字母大写的驼峰串(如SayHello->Sayhello)转换错误问题 864 | 865 | #### Changed 866 | 867 | - none 868 | 869 | #### Removed 870 | 871 | - none 872 | 873 | ## [v0.0.6]- 2020-04-28 874 | 875 | #### Added 876 | 877 | - `KNum.AbsInt` 878 | 879 | #### Fixed 880 | 881 | - none 882 | 883 | #### Changed 884 | 885 | - rename `KNum.Abs` to `KNum.AbsFloat` 886 | 887 | #### Removed 888 | 889 | - none 890 | 891 | ## [v0.0.5]- 2020-03-20 892 | 893 | #### Added 894 | 895 | - `KArr.InInt64Slice` 896 | - `KArr.InIntSlice` 897 | - `KArr.InStringSlice` 898 | - `KArr.IsEqualArray` 899 | - `KConv.Byte2Hexs` 900 | - `KConv.Hexs2Byte` 901 | - `KConv.Runes2Bytes` 902 | - `KEncr.AesCBCDecrypt` 903 | - `KEncr.AesCBCEncrypt` 904 | - `KEncr.AesCFBDecrypt` 905 | - `KEncr.AesCFBEncrypt` 906 | - `KEncr.AesCTRDecrypt` 907 | - `KEncr.AesCTREncrypt` 908 | - `KEncr.AesOFBDecrypt` 909 | - `KEncr.AesOFBEncrypt` 910 | - `KEncr.GenerateRsaKeys` 911 | - `KEncr.RsaPrivateDecrypt` 912 | - `KEncr.RsaPrivateEncrypt` 913 | - `KEncr.RsaPublicDecrypt` 914 | - `KEncr.RsaPublicEncrypt` 915 | - `KFile.AppendFile` 916 | - `KFile.GetFileMode` 917 | - `KFile.ReadFirstLine` 918 | - `KFile.ReadLastLine` 919 | - `KNum.Percent` 920 | - `KNum.RoundPlus` 921 | - `KOS.GetBiosInfo` 922 | - `KOS.GetBoardInfo` 923 | - `KOS.GetCpuInfo` 924 | - `KOS.IsProcessExists` 925 | - `KStr.AtWho` 926 | - `KStr.ClearUrlPrefix` 927 | - `KStr.ClearUrlSuffix` 928 | - `KStr.Gravatar` 929 | - `KStr.IsWord` 930 | - `KStr.RemoveEmoji` 931 | - `KStr.UuidV4` 932 | - `KTime.DaysBetween` 933 | - `KTime.EndOfDay` 934 | - `KTime.EndOfMonth` 935 | - `KTime.EndOfWeek` 936 | - `KTime.EndOfYear` 937 | - `KTime.StartOfDay` 938 | - `KTime.StartOfMonth` 939 | - `KTime.StartOfWeek` 940 | - `KTime.StartOfYear` 941 | 942 | #### Fixed 943 | 944 | - none 945 | 946 | #### Changed 947 | 948 | - `KFile.IsFile` 增加文件类型参数LkkFileType 949 | - `KFile.WriteFile` 增加权限参数perm 950 | - `KOS.Getenv` 增加默认值参数 951 | - `KStr.Random` 移除time.Sleep 952 | - `KTime.GetMonthDays` 放弃map,直接比较 953 | - rename `KOS.GetProcessExeByPid` to `KOS.GetProcessExecPath` 954 | - rename `KTime.Time` to `KTime.UnixTime` 955 | 956 | #### Removed 957 | 958 | - none 959 | 960 | ## [v0.0.4]- 2020-03-06 961 | 962 | #### Added 963 | 964 | - `KStr.Index` 965 | - `KStr.LastIndex` 966 | 967 | #### Fixed 968 | 969 | - none 970 | 971 | #### Changed 972 | 973 | - `KStr.RemoveBefore` 增加参数ignoreCase 974 | - `KStr.RemoveAfter` 增加参数ignoreCase 975 | - `KStr.StartsWith` 增加参数ignoreCase,使用Index代替MbSubstr 976 | - `KStr.EndsWith` 增加参数ignoreCase,使用LastIndex代替MbSubstr 977 | 978 | #### Removed 979 | 980 | - none 981 | 982 | ## [v0.0.3]- 2020-03-03 983 | 984 | #### Added 985 | 986 | - 增加常量`DYNAMIC_KEY_LEN`动态密钥长度 987 | 988 | #### Fixed 989 | 990 | - `KEncr.AuthCode` 修复bounds out of range错误 991 | 992 | #### Changed 993 | 994 | - `KEncr.AuthCode` 动态密钥长度改为8 995 | - `KEncr.EasyEncrypt` 动态密钥长度改为8 996 | - `KEncr.EasyDecrypt` 动态密钥长度改为8 997 | - `KTime.CheckDate(month, day, year int)` to `CheckDate(year, month, day int)` 998 | - `KNum.ByteFormat` 增加'delimiter'参数,为数字和单位间的分隔符 999 | 1000 | #### Removed 1001 | 1002 | - none 1003 | 1004 | ## [v0.0.2]- 2020-02-09 1005 | 1006 | #### Added 1007 | 1008 | - `KArr.JoinInts` 1009 | - `KArr.JoinStrings` 1010 | - `KArr.Unique64Ints` 1011 | - `KArr.UniqueInts` 1012 | - `KArr.UniqueStrings` 1013 | - `KConv.ToBool` 1014 | - `KConv.IsNil` 1015 | - `KDbug.CallMethod` 1016 | - `KDbug.GetFuncDir` 1017 | - `KDbug.GetFuncFile` 1018 | - `KDbug.GetFuncPackage` 1019 | - `KDbug.GetMethod` 1020 | - `KDbug.HasMethod` 1021 | - `KFile.CountLines` 1022 | - `KFile.IsZip` 1023 | - `KFile.ReadInArray` 1024 | - `KFile.UnZip` 1025 | - `KFile.Zip` 1026 | - `KNum.Average` 1027 | - `KNum.AverageFloat64` 1028 | - `KNum.AverageInt` 1029 | - `KNum.FloatEqual` 1030 | - `KNum.GeoDistance` 1031 | - `KNum.MaxFloat64` 1032 | - `KNum.MaxInt` 1033 | - `KNum.RandFloat64` 1034 | - `KNum.RandInt64` 1035 | - `KNum:Sum` 1036 | - `KNum:SumFloat64` 1037 | - `KNum:SumInt` 1038 | - `KOS.ForceGC` 1039 | - `KOS.GetPidByPort` 1040 | - `KOS.GetProcessExeByPid` 1041 | - `KOS.TriggerGC` 1042 | - `KStr.CountWords` 1043 | - `KStr.EndsWith` 1044 | - `KStr.Img2Base64` 1045 | - `KStr.IsBlank` 1046 | - `KStr.IsEmpty` 1047 | - `KStr.IsLower` 1048 | - `KStr.IsMd5` 1049 | - `KStr.IsSha1` 1050 | - `KStr.IsSha256` 1051 | - `KStr.IsSha512` 1052 | - `KStr.IsUpper` 1053 | - `KStr.Jsonp2Json` 1054 | - `KStr.StartsWith` 1055 | - `KStr.ToKebabCase` 1056 | - `KStr.ToSnakeCase` 1057 | - `KTime.Day` 1058 | - `KTime.Hour` 1059 | - `KTime.Minute` 1060 | - `KTime.Month` 1061 | - `KTime.Second` 1062 | - `KTime.Str2Timestruct` 1063 | - `KTime.Year` 1064 | 1065 | #### Fixed 1066 | 1067 | - `KStr.Trim`, 当输入"0"时,结果为空的BUG. 1068 | 1069 | #### Changed 1070 | 1071 | - `KArr.Implode`, 增加对map的处理. 1072 | - `KEncr.EasyDecrypt`, 改进循环. 1073 | - `KEncr.EasyEncrypt`, 改进循环. 1074 | - `KNum.Max`, 接受任意类型的参数. 1075 | - `KNum.Min`, 接受任意类型的参数. 1076 | - `KNum.Sum`, 只对数值类型求和. 1077 | - `KOS.Pwd`, 弃用os.Args[0],改用os.Executable. 1078 | - `KStr.IsASCII`, 弃用正则判断. 1079 | - `KStr.IsEmail`, 去掉邮箱是否真实存在的检查. 1080 | - `KStr.MbSubstr`, 允许参数start/length为负数. 1081 | - `KStr.Substr`, 允许参数start/length为负数. 1082 | - rename `KArr.ArraySearchMutilItem` to `ArraySearchMutil` 1083 | - rename `KArr.MapMerge` to `MergeMap` 1084 | - rename `KArr.SliceMerge` to `MergeSlice` 1085 | - rename `KConv.Bin2dec` to `Bin2Dec` 1086 | - rename `KConv.Bin2hex` to `Bin2Hex` 1087 | - rename `KConv.ByteToFloat64` to `Byte2Float64` 1088 | - rename `KConv.ByteToInt64` to `Byte2Int64` 1089 | - rename `KConv.BytesSlice2Str` to `Bytes2Str` 1090 | - rename `KConv.Dec2bin` to `Dec2Bin` 1091 | - rename `KConv.Dec2hex` to `Dec2Hex` 1092 | - rename `KConv.Dec2oct` to `Dec2Oct` 1093 | - rename `KConv.Hex2bin` to `Hex2Bin` 1094 | - rename `KConv.Hex2dec` to `Hex2Dec` 1095 | - rename `KConv.Ip2long` to `Ip2Long` 1096 | - rename `KConv.Long2ip` to `Long2Ip` 1097 | - rename `KConv.Oct2dec` to `Oct2Dec` 1098 | - rename `KConv.Str2ByteSlice` to `Str2Bytes` 1099 | - rename `KConv.StrictStr2Float` to `Str2FloatStrict` 1100 | - rename `KConv.StrictStr2Int` to `Str2IntStrict` 1101 | - rename `KConv.StrictStr2Uint` to `Str2UintStrict` 1102 | - rename `KFile.Filemtime` to `GetModTime` 1103 | - rename `KFile.GetContents` to `ReadFile` 1104 | - rename `KFile.PutContents` to `WriteFile` 1105 | - rename `KStr.CamelName` to `ToCamelCase` 1106 | - rename `KStr.LowerCaseFirstWords` to `Lcwords` 1107 | - rename `KStr.StrShuffle` to `Shuffle` 1108 | - rename `KStr.Strrev` to `Reverse` 1109 | - rename `KStr.UpperCaseFirstWords` to `Ucwords` 1110 | - rename `KTime.Strtotime` to `Str2Timestamp` 1111 | 1112 | #### Removed 1113 | 1114 | - remove `KConv.Int2Bool` 1115 | 1116 | *--end of file--* --------------------------------------------------------------------------------