Commit b0e6d54a authored by “李磊”'s avatar “李磊”
Browse files

feat: Add some toolkits

parent dd9ff9ae
0::/init.scope
\ No newline at end of file
package types
import "time"
type CPUStats struct {
Timestamp time.Time
// Total CPU time consumed.
// Units: nanoseconds.
TotalUsage uint64
// Total CPU time consumed per core.
// Units: nanoseconds.
PerCPUUsage []uint64
}
type MemoryStats struct {
Timestamp time.Time
Usage uint64
WorkingSet uint64
}
type IOStats struct {
Timestamp time.Time
Stats map[string]*IOStat
}
type IOStat struct {
Read uint64
Write uint64
}
type DevLimit struct {
DevName string
RdLimit bool
WrLimit bool
}
type DevInfo struct {
Name string
Major string
Minor string
}
type IOUsage struct {
Read float64
Write float64
}
package common
import (
"os"
"os/exec"
"path/filepath"
"runtime"
"strconv"
"strings"
)
func PathExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
func ExecCmd(cmd string) (string, error) {
var c *exec.Cmd
c = exec.Command("/bin/sh", "-c", cmd)
out, err := c.CombinedOutput()
return string(out), err
}
func RunCmd(cmd string, shell bool) ([]byte, error) {
if shell {
out, err := exec.Command("bash", "-c", cmd).CombinedOutput()
return out, err
} else {
out, err := exec.Command(cmd).CombinedOutput()
return out, err
}
}
func GetCurrentFileDir() string {
_, file, _, _ := runtime.Caller(1)
return filepath.Dir(file)
}
func ReadLink(path string) string {
info, err := os.Lstat(path)
if err != nil {
return path
}
if strings.HasPrefix(info.Mode().String(), "L") {
dstPath, err := os.Readlink(path)
if err != nil {
return path
}
if strings.HasPrefix(dstPath, "/") {
return dstPath
}
absDstPath := filepath.Join(filepath.Dir(path), dstPath)
return absDstPath
}
return path
}
// return whether s exists in sList
func InStringList(s string, sList []string) bool {
for _, str := range sList {
if len(s) == len(str) && s == str {
return true
}
}
return false
}
func InIntList(i int, iList []int) bool {
for _, num := range iList {
if i == num {
return true
}
}
return false
}
func InUintList(i uint, iList []uint) bool {
for _, num := range iList {
if i == num {
return true
}
}
return false
}
func CreateIfNotExistDir(dir string) error {
if PathExists(dir) {
return nil
}
return os.MkdirAll(dir, 0700)
}
// version_compare()
// The possible operators are: <, lt, <=, le, >, gt, >=, ge, ==, =, eq, !=, <>, ne respectively.
// special version strings these are handled in the following order,
// (any string not found) < dev < alpha = a < beta = b < RC = rc < # < pl = p
// Usage:
// VersionCompare("1.2.3-alpha", "1.2.3RC7", '>=')
// VersionCompare("1.2.3-beta", "1.2.3pl", 'lt')
// VersionCompare("1.1_dev", "1.2any", 'eq')
func VersionCompare(version1, version2, operator string) bool {
var vcompare func(string, string) int
var canonicalize func(string) string
var special func(string, string) int
// version compare
vcompare = func(origV1, origV2 string) int {
if origV1 == "" || origV2 == "" {
if origV1 == "" && origV2 == "" {
return 0
} else {
if origV1 == "" {
return -1
} else {
return 1
}
}
}
if (origV1 == "*" || origV2 == "*") && (operator == "eq" ||
operator == "=" || operator == "==") {
return 0
}
ver1, ver2, compare := "", "", 0
if origV1[0] == '#' {
ver1 = origV1
} else {
ver1 = canonicalize(origV1)
}
if origV2[0] == '#' {
ver2 = origV2
} else {
ver2 = canonicalize(origV2)
}
n1, n2 := 0, 0
for {
p1, p2 := "", ""
n1 = strings.IndexByte(ver1, '.')
if n1 == -1 {
p1, ver1 = ver1, ""
} else {
p1, ver1 = ver1[:n1], ver1[n1+1:]
}
n2 = strings.IndexByte(ver2, '.')
if n2 == -1 {
p2, ver2 = ver2, ""
} else {
p2, ver2 = ver2[:n2], ver2[n2+1:]
}
if (p1[0] >= '0' && p1[0] <= '9') && (p2[0] >= '0' && p2[0] <= '9') { // all isdigit
l1, _ := strconv.Atoi(p1)
l2, _ := strconv.Atoi(p2)
if l1 > l2 {
compare = 1
} else if l1 == l2 {
compare = 0
} else {
compare = -1
}
} else if !(p1[0] >= '0' && p1[0] <= '9') && !(p2[0] >= '0' && p2[0] <= '9') { // all isndigit
compare = special(p1, p2)
} else { // part isdigit
if p1[0] >= '0' && p1[0] <= '9' { // isdigit
compare = special("#N#", p2)
} else {
compare = special(p1, "#N#")
}
}
if compare != 0 || n1 == -1 || n2 == -1 {
break
}
}
if compare == 0 {
if ver1 != "" {
if ver1[0] >= '0' && ver1[0] <= '9' {
compare = 1
} else {
compare = vcompare(ver1, "#N#")
}
} else if ver2 != "" {
if ver2[0] >= '0' && ver2[0] <= '9' {
compare = -1
} else {
compare = vcompare("#N#", ver2)
}
}
}
return compare
}
// canonicalize
canonicalize = func(version string) string {
ver := []byte(version)
l := len(ver)
if l == 0 {
return ""
}
var buf = make([]byte, l*2)
j := 0
for i, v := range ver {
next := uint8(0)
if i+1 < l { // Have the next one
next = ver[i+1]
}
if v == '-' || v == '_' || v == '+' { // repalce "-","_","+" to "."
if j > 0 && buf[j-1] != '.' {
buf[j] = '.'
j++
}
} else if (next > 0) &&
(!(next >= '0' && next <= '9') && (v >= '0' && v <= '9')) ||
(!(v >= '0' && v <= '9') && (next >= '0' && next <= '9')) { // Insert '.' before and after a non-digit
buf[j] = v
j++
if v != '.' && next != '.' {
buf[j] = '.'
j++
}
continue
} else if !((v >= '0' && v <= '9') ||
(v >= 'a' && v <= 'z') || (v >= 'A' && v <= 'Z')) { // Non-letters and numbers
if j > 0 && buf[j-1] != '.' {
buf[j] = '.'
j++
}
} else {
buf[j] = v
j++
}
}
return string(buf[:j])
}
//compare special version forms
special = func(form1, form2 string) int {
found1, found2, len1, len2 := -1, -1, len(form1), len(form2)
// (Any string not found) < dev < alpha = a < beta = b < RC = rc < # < pl = p
forms := map[string]int{
"dev": 0,
"alpha": 1,
"a": 1,
"beta": 2,
"b": 2,
"RC": 3,
"rc": 3,
"#": 4,
"pl": 5,
"p": 5,
}
for name, order := range forms {
if len1 < len(name) {
continue
}
if strings.Compare(form1[:len(name)], name) == 0 {
found1 = order
break
}
}
for name, order := range forms {
if len2 < len(name) {
continue
}
if strings.Compare(form2[:len(name)], name) == 0 {
found2 = order
break
}
}
if found1 == found2 {
return 0
} else if found1 > found2 {
return 1
} else {
return -1
}
}
var compare int
// <> means between, version2 is a comma seperated version, eg: 1.2,2.0 means
// vesion1 > 1.2 and version1 < 2.0
if operator == "<>" {
splits := strings.Split(version2, ",")
if len(splits) != 2 {
return false
}
return VersionCompare(version1, splits[0], ">") && VersionCompare(version1,
splits[1], "<")
} else {
compare = vcompare(version1, version2)
}
switch operator {
case "<", "lt":
return compare == -1
case "<=", "le":
return compare != 1
case ">", "gt":
return compare == 1
case ">=", "ge":
return compare != -1
case "==", "=", "eq":
return compare == 0
case "!=", "ne":
return compare != 0
default:
//panic("operator: invalid")
return false
}
}
package common
import (
"fmt"
"time"
"linkfog.com/public/lib/l"
)
func TimeCost(describe string) func() {
start := time.Now()
return func() {
tc := time.Since(start)
l.Infof(describe+" cost %v", tc)
}
}
// Deprecated invalid usage
func TimeCostString(describe string) func() string {
start := time.Now()
return func() string {
tc := time.Since(start)
return fmt.Sprintf(describe+" cost %v", tc)
}
}
func TimeCostDebug(describe string) func() {
start := time.Now()
return func() {
tc := time.Since(start)
l.Debugf(describe+" cost %v", tc)
}
}
package common
import (
"bytes"
"encoding/gob"
)
// Warning: If src contains a pointer to a zero-valued object,
// the pointer will be converted to nil.
// such as *string -> nil; *[]string{} -> nil
func CopyByGob(dst, src interface{}) error {
var buf bytes.Buffer
if err := gob.NewEncoder(&buf).Encode(src); err != nil {
return err
}
return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst)
}
package common
import (
"reflect"
"time"
)
// Interface for delegating copy process to type
type Interface interface {
DeepCopy() interface{}
}
// warning: Multithreading is not safe
// Copy creates a deep copy of whatever is passed to it and returns the copy
// in an interface{}. The returned value will need to be asserted to the
// correct type.
func CopyByReflect(src interface{}) interface{} {
if src == nil {
return nil
}
// Make the interface a reflect.Value
original := reflect.ValueOf(src)
// Make a copy of the same type as the original.
cpy := reflect.New(original.Type()).Elem()
// Recursively copy the original.
copyRecursive(original, cpy)
// Return the copy as an interface.
return cpy.Interface()
}
// copyRecursive does the actual copying of the interface. It currently has
// limited support for what it can handle. Add as needed.
func copyRecursive(original, cpy reflect.Value) {
// check for implement common.Interface
if original.CanInterface() {
if copier, ok := original.Interface().(Interface); ok {
cpy.Set(reflect.ValueOf(copier.DeepCopy()))
return
}
}
// handle according to original's Kind
switch original.Kind() {
case reflect.Ptr:
// Get the actual value being pointed to.
originalValue := original.Elem()
// if it isn't valid, return.
if !originalValue.IsValid() {
return
}
cpy.Set(reflect.New(originalValue.Type()))
copyRecursive(originalValue, cpy.Elem())
case reflect.Interface:
// If this is a nil, don't do anything
if original.IsNil() {
return
}
// Get the value for the interface, not the pointer.
originalValue := original.Elem()
// Get the value by calling Elem().
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
cpy.Set(copyValue)
case reflect.Struct:
t, ok := original.Interface().(time.Time)
if ok {
cpy.Set(reflect.ValueOf(t))
return
}
// Go through each field of the struct and copy it.
for i := 0; i < original.NumField(); i++ {
// The Type's StructField for a given field is checked to see if StructField.PkgPath
// is set to determine if the field is exported or not because CanSet() returns false
// for settable fields. I'm not sure why. -mohae
if original.Type().Field(i).PkgPath != "" {
continue
}
copyRecursive(original.Field(i), cpy.Field(i))
}
case reflect.Slice:
if original.IsNil() {
return
}
// Make a new slice and copy each element.
cpy.Set(reflect.MakeSlice(original.Type(), original.Len(), original.Cap()))
for i := 0; i < original.Len(); i++ {
copyRecursive(original.Index(i), cpy.Index(i))
}
case reflect.Map:
if original.IsNil() {
return
}
cpy.Set(reflect.MakeMap(original.Type()))
for _, key := range original.MapKeys() {
originalValue := original.MapIndex(key)
copyValue := reflect.New(originalValue.Type()).Elem()
copyRecursive(originalValue, copyValue)
copyKey := CopyByReflect(key.Interface())
cpy.SetMapIndex(reflect.ValueOf(copyKey), copyValue)
}
default:
cpy.Set(original)
}
}
package common
import (
"container/list"
"sync"
)
// Note: This is a fixed length fifo
// Push(): if fifo is full, remove (old data) from front
// Pop() : if fifo is empty, return nil
type Fifo struct {
l *list.List
len int
sync.RWMutex
}
func NewFifo(len int) *Fifo {
return &Fifo{
l: list.New(),
len: len,
}
}
func (f *Fifo) Front() *list.Element {
return f.l.Front()
}
func (f *Fifo) Back() *list.Element {
return f.l.Back()
}
// Push enqueue to back (new data), if fifo is full, remove from front (old data)
func (f *Fifo) Push(item interface{}) {
f.Lock()
defer f.Unlock()
for f.l.Len() >= f.len {
f.l.Remove(f.l.Front())
}
f.l.PushBack(item)
}
// Pop dequeue from front (old data), if fifo is empty, return nil
func (f *Fifo) Pop() interface{} {
f.Lock()
defer f.Unlock()
if f.l.Len() == 0 {
return nil
}
return f.l.Remove(f.l.Front())
}
func (f *Fifo) Has(item interface{}) bool {
f.RLock()
defer f.RUnlock()
for i := f.l.Front(); i != nil; i = i.Next() {
if i.Value == item {
return true
}
}
return false
}
func (f *Fifo) List() []interface{} {
f.RLock()
defer f.RUnlock()
var l []interface{}
for i := f.l.Front(); i != nil; i = i.Next() {
l = append(l, i.Value)
}
return l
}
func (f *Fifo) Len() int {
return f.l.Len()
}
func (f *Fifo) MaxLen() int {
return f.len
}
package common
import (
"testing"
)
func TestFIFO(t *testing.T) {
fifo := NewFifo(5)
if fifo.MaxLen() != 5 {
t.Fatal("unexpected fifo max_len:", fifo.MaxLen())
}
if fifo.Len() != 0 {
t.Fatal("unexpected fifo len:", fifo.Len())
}
fifo.Push(1)
fifo.Push(2)
fifo.Push(3)
fifo.Push(4)
fifo.Push(5)
fifo.Push(6)
t.Log("fifo:", fifo.List())
if fifo.Len() != 5 {
t.Fatal("unexpected fifo len:", fifo.Len())
}
if fifo.Front().Value.(int) != 2 {
t.Fatalf("unexpected front:%v", fifo.Front().Value.(int))
}
if fifo.Back().Value.(int) != 6 {
t.Fatalf("unexpected back:%v", fifo.Back().Value.(int))
}
pop := fifo.Pop().(int)
if pop != 2 {
t.Fatalf("unexpected pop:%v", pop)
}
fifo.Pop()
fifo.Pop()
fifo.Pop()
fifo.Pop()
if fifo.Len() != 0 {
t.Fatal("unexpected fifo len:", fifo.Len())
}
if fifo.Pop() != nil {
t.Fatal("fifo pop empty should return nil")
}
}
package common
import (
"reflect"
"runtime"
"strings"
)
func GetFunctionName(i interface{}) string {
return GetFunctionNameBySep(i, '/', '.')
}
func GetFunctionNameBySep(i interface{}, seps ...rune) string {
// 获取函数名称
fn := runtime.FuncForPC(reflect.ValueOf(i).Pointer()).Name()
// 用 seps 进行分割
fields := strings.FieldsFunc(fn, func(sep rune) bool {
for _, s := range seps {
if sep == s {
return true
}
}
return false
})
// fmt.Println(fields)
if size := len(fields); size > 0 {
return fields[size-1]
}
return ""
}
package common
import (
"io/ioutil"
"os"
"path/filepath"
"strings"
)
func GetConfigValue(dir, name string) string {
b, err := ioutil.ReadFile(filepath.Join(dir, name))
v := string(b)
if err != nil {
v = os.Getenv(name)
}
trimRightStr := strings.TrimRight(strings.TrimRight(v, "\n"), " ")
str := strings.TrimLeft(strings.TrimLeft(trimRightStr, "\n"), " ")
return str
}
package common
import (
"errors"
"reflect"
"unsafe"
)
const (
mergeAllFields = true
mergeExportedFields = false
)
// used to avoid zero value risk
type EnableType int
const (
Unset EnableType = 0
Enabled EnableType = 1
Disabled EnableType = 2
)
func (e EnableType) IsSet() bool {
return e != Unset
}
func (e EnableType) Enabled() bool {
return e == Enabled
}
func (e EnableType) Disabled() bool {
return e == Disabled
}
// dst and src need addressable
func MergeStructAllFields(dst, src interface{}) error {
return mergeStruct(dst, src, mergeAllFields)
}
// dst need addressable
func MergeStructExportedFields(dst, src interface{}) error {
return mergeStruct(dst, src, mergeExportedFields)
}
func mergeStruct(dst, src interface{}, isAll bool) (err error) {
var vDst, vSrc reflect.Value
if vDst, vSrc, err = reflectValue(dst, src); err != nil {
return err
}
if vDst.Type() != vSrc.Type() {
return errors.New("src and dst must be of same type")
}
return deepMergeStruct(vDst, vSrc, isAll)
}
func deepMergeStruct(dst, src reflect.Value, isAll bool) (err error) {
if !src.IsValid() {
return errors.New("src is invalid")
}
if dst.Kind() == reflect.Struct {
for i, n := 0, dst.NumField(); i < n; i++ {
setNotZeroValue(dst.Field(i), src.Field(i), isAll)
}
}
return
}
func reflectValue(dst, src interface{}) (vDst, vSrc reflect.Value, err error) {
if dst == nil || src == nil {
err = errors.New("src and dst must not be nil")
return
}
vDst = reflect.ValueOf(dst).Elem()
if vDst.Kind() != reflect.Struct {
err = errors.New("only structs are supported")
return
}
vSrc = reflect.ValueOf(src)
if vSrc.Kind() == reflect.Ptr {
vSrc = vSrc.Elem()
}
return
}
func setNotZeroValue(dst, src reflect.Value, isAll bool) {
if src.IsZero() {
return
}
if dst.CanSet() {
dst.Set(src)
} else if isAll {
dst = reflect.NewAt(dst.Type(), unsafe.Pointer(dst.UnsafeAddr())).Elem()
src = reflect.NewAt(src.Type(), unsafe.Pointer(src.UnsafeAddr())).Elem()
dst.Set(src)
}
}
package common
import (
"fmt"
"reflect"
"strconv"
)
func IsIntArrayEqual(x, y []int) bool {
if len(x) != len(y) {
return false
}
for i := 0; i < len(x); i++ {
if x[i] != y[i] {
return false
}
}
return true
}
func IsIntSliceContains(x int, xs []int) bool {
for _, i := range xs {
if i == x {
return true
}
}
return false
}
func In(obj interface{}, target interface{}) (bool, error) {
targetValue := reflect.ValueOf(target)
switch reflect.TypeOf(target).Kind() {
case reflect.Slice, reflect.Array:
for i := 0; i < targetValue.Len(); i++ {
if targetValue.Index(i).Interface() == obj {
return true, nil
}
}
case reflect.Map:
if targetValue.MapIndex(reflect.ValueOf(obj)).IsValid() {
return true, nil
}
}
return false, fmt.Errorf("not in array")
}
func PSQLIntArray(arr []int) string {
str := "{"
for i := 0; i < len(arr)-1; i++ {
str = str + strconv.Itoa(arr[i]) + ","
}
str = str + strconv.Itoa(arr[len(arr)-1]) + "}"
return str
}
// IntArrayMinus 返回的zs是在xs中但不在ys中数的集合
func IntArrayMinus(xs, ys []int) (zs []int) {
zs = []int{}
for _, x := range xs {
if !InIntList(x, ys) {
zs = append(zs, x)
}
}
return zs
}
// InStringSliceAnyFunc
func InStringSliceAnyFunc(s string, slice []string, matchFunc func(string, string) bool) bool {
for _, l := range slice {
if matchFunc(s, l) {
return true
}
}
return false
}
package common
import (
"reflect"
"testing"
)
func TestIntArrayMinus(t *testing.T) {
type args struct {
xs []int
ys []int
}
tests := []struct {
name string
args args
wantZs []int
}{
{
name: "test1",
args: args{
xs: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
ys: []int{2, 4, 6, 8},
},
wantZs: []int{1, 3, 5, 7, 9},
}, {
name: "test2",
args: args{
xs: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
ys: []int{1, 2, 3, 11},
},
wantZs: []int{4, 5, 6, 7, 8, 9},
}, {
name: "test3",
args: args{
xs: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
ys: []int{},
},
wantZs: []int{1, 2, 3, 4, 5, 6, 7, 8, 9},
}, {
name: "test4",
args: args{
xs: []int{},
ys: []int{1},
},
wantZs: []int{},
}, {
name: "test5",
args: args{
xs: []int{},
ys: []int{},
},
wantZs: []int{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotZs := IntArrayMinus(tt.args.xs, tt.args.ys); !reflect.DeepEqual(gotZs, tt.wantZs) {
t.Errorf("IntArrayMinus() got %v, want %v", gotZs, tt.wantZs)
}
})
}
}
/*
* MinIO Cloud Storage, (C) 2015, 2016 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package common
// MatchSimple - finds whether the text matches/satisfies the pattern string.
// supports only '*' wildcard in the pattern.
// considers a file system path as a flat name space.
func MatchSimple(pattern, name string) bool {
if pattern == "" {
return name == pattern
}
if pattern == "*" {
return true
}
// Does only wildcard '*' match.
return deepMatchRune([]rune(name), []rune(pattern), true)
}
// Match - finds whether the text matches/satisfies the pattern string.
// supports '*' and '?' wildcards in the pattern string.
// unlike path.Match(), considers a path as a flat name space while matching the pattern.
// The difference is illustrated in the example here https://play.golang.org/p/Ega9qgD4Qz .
func Match(pattern, name string) (matched bool) {
if pattern == "" {
return name == pattern
}
if pattern == "*" {
return true
}
// Does extended wildcard '*' and '?' match.
return deepMatchRune([]rune(name), []rune(pattern), false)
}
func deepMatchRune(str, pattern []rune, simple bool) bool {
for len(pattern) > 0 {
switch pattern[0] {
default:
if len(str) == 0 || str[0] != pattern[0] {
return false
}
case '?':
if len(str) == 0 && !simple {
return false
}
case '*':
return deepMatchRune(str, pattern[1:], simple) ||
(len(str) > 0 && deepMatchRune(str[1:], pattern, simple))
}
str = str[1:]
pattern = pattern[1:]
}
return len(str) == 0 && len(pattern) == 0
}
package common
import "testing"
func TestMatch(t *testing.T) {
type args struct {
pattern string
name string
}
tests := []struct {
name string
args args
wantMatched bool
}{
{name: "t1", args: args{name: "wx/nginx", pattern: "*"}, wantMatched: true},
{name: "t2", args: args{name: "wx/nginx", pattern: "wx/*"}, wantMatched: true},
{name: "t3", args: args{name: "wx/nginx", pattern: "*/nginx"}, wantMatched: true},
{name: "t4", args: args{name: "wx/nginx", pattern: "wx*nginx"}, wantMatched: true},
{name: "t5", args: args{name: "wx/nginx", pattern: "wx?nginx"}, wantMatched: true},
{name: "t6", args: args{name: "harbor.linkfog.com:8443/wx/ubuntu", pattern: "*wx?ubuntu*"}, wantMatched: true},
{name: "t7", args: args{name: "harbor.linkfog.com:8443/wx/ubuntu", pattern: "*wx*"}, wantMatched: true},
{name: "t8", args: args{name: "harbor.linkfog.com:8443/wx/ubuntu", pattern: "harbor*"}, wantMatched: true},
{name: "t9", args: args{name: "harbor.linkfog.com:8443/wx/ubuntu", pattern: "*ubuntu"}, wantMatched: true},
{name: "t10", args: args{name: "**", pattern: "*"}, wantMatched: true},
{name: "t11", args: args{name: "**", pattern: "??"}, wantMatched: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if gotMatched := Match(tt.args.pattern, tt.args.name); gotMatched != tt.wantMatched {
t.Errorf("Match() = %v, want %v", gotMatched, tt.wantMatched)
}
})
}
}
package config
import "linkfog.com/public/pkg/ratelimit"
var c Config
type Config struct {
RateLimitBucket *ratelimit.Bucket
EnableRateLimitBucket bool
}
func Set(config Config) {
c = config
}
func RateLimitBucket() *ratelimit.Bucket {
return c.RateLimitBucket
}
func EnableRateLimitBucket() bool {
return c.EnableRateLimitBucket
}
\ No newline at end of file
package file
import (
"io"
"linkfog.com/public/lib/file/config"
"linkfog.com/public/pkg/ratelimit"
)
func CopyRateLimit(dst io.Writer, src io.Reader) (written int64, err error) {
if !config.EnableRateLimitBucket() || config.RateLimitBucket() == nil {
written, err = io.Copy(dst, src)
if err != nil {
return written, err
}
return written, err
}
return copyBufferRateLimit(dst, src, nil)
}
// copyBufferRateLimit is the actual implementation of Copy and CopyBuffer.
// if buf is nil, one is allocated.
func copyBufferRateLimit(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
// If the reader has a WriteTo method, use it to do the copy.
// Avoids an allocation and a copy.
if wt, ok := src.(io.WriterTo); ok {
return wt.WriteTo(dst)
}
// Similarly, if the writer has a ReadFrom method, use it to do the copy.
if rt, ok := dst.(io.ReaderFrom); ok {
return rt.ReadFrom(src)
}
if buf == nil {
size := config.RateLimitBucket().Capacity()
if l, ok := src.(*io.LimitedReader); ok && size > l.N {
if l.N < 1 {
size = 1
} else {
size = l.N
}
}
buf = make([]byte, size)
}
rateWriter := ratelimit.Writer(dst, config.RateLimitBucket())
for {
nr, er := src.Read(buf)
if nr > 0 {
nw, ew := rateWriter.Write(buf[0:nr])
if nw < 0 || nr < nw {
nw = 0
if ew == nil {
ew = errInvalidWrite
}
}
written += int64(nw)
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
if er != io.EOF {
err = er
}
break
}
}
return written, err
}
func CopyBufferRateLimit(dst io.Writer, src io.Reader, buf []byte) (written int64, err error) {
if !config.EnableRateLimitBucket() || config.RateLimitBucket() == nil {
written, err = io.CopyBuffer(dst, src, buf)
if err != nil {
return written, err
}
return written, err
}
return copyBufferRateLimit(dst, src, buf)
}
// the copy is implemented using it.
func CopyNRateLimit(dst io.Writer, src io.Reader, n int64) (written int64, err error) {
written, err = CopyRateLimit(dst, io.LimitReader(src, n))
if written == n {
return n, nil
}
if written < n && err == nil {
// src stopped early; must have been EOF.
err = io.EOF
}
return
}
\ No newline at end of file
package file
import (
"fmt"
"io"
"os"
"linkfog.com/public/lib/common"
)
func CopyFile(dstFile, srcFile string, perm os.FileMode) error {
srcStat, err := os.Stat(srcFile)
if err != nil {
return err
}
if !srcStat.Mode().IsRegular() {
return fmt.Errorf("src file is not a regular file")
}
srcf, err := os.Open(srcFile)
if err != nil {
return err
}
defer srcf.Close()
defer FadviseSwitch(srcf)
dstf, err := os.OpenFile(dstFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, perm)
if err != nil {
return err
}
defer dstf.Close()
defer FadviseSwitch(dstf)
_, err = io.Copy(dstf, srcf)
return err
}
func CopyFileWithCmd(dstFile, srcFile string) error {
srcStat, err := os.Stat(srcFile)
if err != nil {
return err
}
if !srcStat.Mode().IsRegular() {
return fmt.Errorf("src file is not a regular file")
}
out, err := common.ExecCmd(fmt.Sprintf("cp -f %s %s", srcFile, dstFile))
if err != nil {
return fmt.Errorf("exec cp err: %s %s", err, out)
}
return nil
}
// CopyFileRateLimit 限速拷贝文件
func CopyFileRateLimit(dstFile, srcFile string) error {
src, err := os.Open(srcFile)
if err != nil {
return err
}
defer src.Close()
defer FadviseSwitch(src)
dst, err := os.Create(dstFile)
if err != nil {
return err
}
defer dst.Close()
defer FadviseSwitch(dst)
_, err = CopyRateLimit(dst, src)
if err != nil {
return err
}
return nil
}
package file
import (
"encoding/json"
"os"
"testing"
"time"
"linkfog.com/public/lib/file/config"
"linkfog.com/public/pkg/ratelimit"
)
func TestCopyFileRateLimit(t *testing.T) {
config.Set(config.Config{
RateLimitBucket: ratelimit.NewBucket(100*time.Nanosecond, int64(10*1024*1024)),
EnableRateLimitBucket: true,
})
f, err := os.CreateTemp("", "copyTest")
if err != nil {
t.Error("file create err", err)
return
}
defer os.RemoveAll(f.Name())
data := []byte{}
aData, _ := json.Marshal("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\\n")
for i := 0; i < 10000000; i++ {
data = append(data, aData...)
}
_, err = f.Write(data)
if err != nil {
t.Error("file write err", err)
return
}
err = CopyFileRateLimit("/tmp/copyTest_bank", f.Name())
if os.IsNotExist(err) {
t.Error("no file err", err)
return
} else if err != nil {
t.Error("file err", err)
return
}
t.Log("copy success")
}
package file
import (
"os"
)
func IsELF(fPath string) (bool, error) {
f, err := os.Open(fPath)
if err != nil {
return false, err
}
defer f.Close()
defer FadviseSwitch(f)
//sr := io.NewSectionReader(file, 0, 1<<62)
// 读取elf文件的ident
var ident [16]uint8
if _, err := f.ReadAt(ident[0:], 0); err != nil {
return false, err
}
// 判断是否是elf文件
// 校验前四位魔数
if ident[0] != '\x7f' || ident[1] != 'E' || ident[2] != 'L' || ident[3] != 'F' {
return false, nil
}
return true, nil
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment