From 7e37f5bc04f0966ebc33081250ae981c4d2b9f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BC=A0=E5=BE=B7=E6=BB=A1?= Date: Tue, 23 Feb 2021 22:41:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BB=BB=E6=84=8F=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=B1=BB=E5=9E=8B=E4=B9=8B=E9=97=B4=E7=9A=84=E8=BD=AC?= =?UTF-8?q?=E6=8D=A2=20&&=20=E4=BD=BF=E7=94=A8=E9=94=81=E5=AE=9E=E7=8E=B0?= =?UTF-8?q?=E5=B9=B6=E5=8F=91=E5=AE=89=E5=85=A8=E7=9A=84=E6=99=AE=E9=80=9A?= =?UTF-8?q?map?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 20 +++ convert/convert.go | 393 ++++++++++++++++++++++++++++++++++++++++++ easymap/abstruct.go | 36 ++++ easymap/error.go | 28 +++ easymap/normal_map.go | 273 +++++++++++++++++++++++++++++ go.mod | 5 + go.sum | 2 + 7 files changed, 757 insertions(+) create mode 100644 .gitignore create mode 100644 convert/convert.go create mode 100644 easymap/abstruct.go create mode 100644 easymap/error.go create mode 100644 easymap/normal_map.go create mode 100644 go.mod create mode 100644 go.sum diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2f1f53 --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +# Created by .ignore support plugin (hsz.mobi) +### Go template +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ +.idea +.vscode + diff --git a/convert/convert.go b/convert/convert.go new file mode 100644 index 0000000..9deed6d --- /dev/null +++ b/convert/convert.go @@ -0,0 +1,393 @@ +// Package convert ... +// +// Description : 任意类型之间的相互转换 +// +// Author : go_developer@163.com<张德满> +// +// Date : 2021-02-23 10:23 下午 +package convert + +/* + Desc : 在处理一些参数的时候,可能需要将参数转换为各种类型,这里实现一个通用的转换函数,实现各种类型之间的相互转换。 + + 当然,如果源数据格式和目标数据类型不一致,是会返回错误的。例如将字符串“一二三”转换为数值类型则会报错,而将字符串“123”转换为数值类型则OK。 + + 这段代码实际抄自go自带的“database/sql”库,只是源代码作为内部函数无法在外面调用,可以单独把需要的功能拎出来使用: + + 代码中有一个Scaner接口,可以自行实现,然后通过"convertAssign()"函数,作为dst参数传入。 + + Author : zhangdeman001@ke.com<张德满> + +*/ +import ( + "errors" + "fmt" + "reflect" + "strconv" + "time" +) + +// RawBytes is a byte slice that holds a reference to memory owned by +// the database itself. After a Scan into a RawBytes, the slice is only +// valid until the next call to Next, Scan, or Close. +type RawBytes []byte + +var errNilPtr = errors.New("destination pointer is nil") // embedded in descriptive error + +// convertAssign copies to dest the value in src, converting it if possible. +// An error is returned if the copy would result in loss of information. +// dest should be a pointer type. +func ConvertAssign(dest, src interface{}) error { + // Common cases, without reflect. + switch s := src.(type) { + case string: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = s + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s) + return nil + case *RawBytes: + if d == nil { + return errNilPtr + } + *d = append((*d)[:0], s...) + return nil + } + case []byte: + switch d := dest.(type) { + case *string: + if d == nil { + return errNilPtr + } + *d = string(s) + return nil + case *interface{}: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = cloneBytes(s) + return nil + case *RawBytes: + if d == nil { + return errNilPtr + } + *d = s + return nil + } + case time.Time: + switch d := dest.(type) { + case *time.Time: + *d = s + return nil + case *string: + *d = s.Format(time.RFC3339Nano) + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = []byte(s.Format(time.RFC3339Nano)) + return nil + case *RawBytes: + if d == nil { + return errNilPtr + } + *d = s.AppendFormat((*d)[:0], time.RFC3339Nano) + return nil + } + case nil: + switch d := dest.(type) { + case *interface{}: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *[]byte: + if d == nil { + return errNilPtr + } + *d = nil + return nil + case *RawBytes: + if d == nil { + return errNilPtr + } + *d = nil + return nil + } + } + + var sv reflect.Value + + switch d := dest.(type) { + case *string: + sv = reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Bool, + reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, + reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, + reflect.Float32, reflect.Float64: + *d = asString(src) + return nil + } + case *[]byte: + sv = reflect.ValueOf(src) + if b, ok := asBytes(nil, sv); ok { + *d = b + return nil + } + case *RawBytes: + sv = reflect.ValueOf(src) + if b, ok := asBytes([]byte(*d)[:0], sv); ok { + *d = RawBytes(b) + return nil + } + case *bool: + bv, err := Bool.ConvertValue(src) + if err == nil { + *d = bv.(bool) + } + return err + case *interface{}: + *d = src + return nil + } + + if scanner, ok := dest.(Scanner); ok { + return scanner.Scan(src) + } + + dpv := reflect.ValueOf(dest) + if dpv.Kind() != reflect.Ptr { + return errors.New("destination not a pointer") + } + if dpv.IsNil() { + return errNilPtr + } + + if !sv.IsValid() { + sv = reflect.ValueOf(src) + } + + dv := reflect.Indirect(dpv) + if sv.IsValid() && sv.Type().AssignableTo(dv.Type()) { + switch b := src.(type) { + case []byte: + dv.Set(reflect.ValueOf(cloneBytes(b))) + default: + dv.Set(sv) + } + return nil + } + + if dv.Kind() == sv.Kind() && sv.Type().ConvertibleTo(dv.Type()) { + dv.Set(sv.Convert(dv.Type())) + return nil + } + + // The following conversions use a string value as an intermediate representation + // to convert between various numeric types. + // + // This also allows scanning into user defined types such as "type Int int64". + // For symmetry, also check for string destination types. + switch dv.Kind() { + case reflect.Ptr: + if src == nil { + dv.Set(reflect.Zero(dv.Type())) + return nil + } + dv.Set(reflect.New(dv.Type().Elem())) + return ConvertAssign(dv.Interface(), src) + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + s := asString(src) + i64, err := strconv.ParseInt(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetInt(i64) + return nil + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + s := asString(src) + u64, err := strconv.ParseUint(s, 10, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetUint(u64) + return nil + case reflect.Float32, reflect.Float64: + s := asString(src) + f64, err := strconv.ParseFloat(s, dv.Type().Bits()) + if err != nil { + err = strconvErr(err) + return fmt.Errorf("converting driver.Value type %T (%q) to a %s: %v", src, s, dv.Kind(), err) + } + dv.SetFloat(f64) + return nil + case reflect.String: + switch v := src.(type) { + case string: + dv.SetString(v) + return nil + case []byte: + dv.SetString(string(v)) + return nil + } + } + + return fmt.Errorf("unsupported Scan, storing driver.Value type %T into type %T", src, dest) +} + +func strconvErr(err error) error { + if ne, ok := err.(*strconv.NumError); ok { + return ne.Err + } + return err +} + +func cloneBytes(b []byte) []byte { + if b == nil { + return nil + } + c := make([]byte, len(b)) + copy(c, b) + return c +} + +func ToString(src interface{}) string { + return asString(src) +} + +func asString(src interface{}) string { + switch v := src.(type) { + case string: + return v + case []byte: + return string(v) + } + rv := reflect.ValueOf(src) + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.FormatInt(rv.Int(), 10) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.FormatUint(rv.Uint(), 10) + case reflect.Float64: + return strconv.FormatFloat(rv.Float(), 'g', -1, 64) + case reflect.Float32: + return strconv.FormatFloat(rv.Float(), 'g', -1, 32) + case reflect.Bool: + return strconv.FormatBool(rv.Bool()) + } + return fmt.Sprintf("%v", src) +} + +func asBytes(buf []byte, rv reflect.Value) (b []byte, ok bool) { + switch rv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return strconv.AppendInt(buf, rv.Int(), 10), true + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + return strconv.AppendUint(buf, rv.Uint(), 10), true + case reflect.Float32: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 32), true + case reflect.Float64: + return strconv.AppendFloat(buf, rv.Float(), 'g', -1, 64), true + case reflect.Bool: + return strconv.AppendBool(buf, rv.Bool()), true + case reflect.String: + s := rv.String() + return append(buf, s...), true + } + return +} + +// Value is a value that drivers must be able to handle. +// It is either nil, a type handled by a database driver's NamedValueChecker +// interface, or an instance of one of these types: +// +// int64 +// float64 +// bool +// []byte +// string +// time.Time +type Value interface{} + +type boolType struct{} + +var Bool boolType + +func (boolType) String() string { return "Bool" } +func (boolType) ConvertValue(src interface{}) (Value, error) { + switch s := src.(type) { + case bool: + return s, nil + case string: + b, err := strconv.ParseBool(s) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + case []byte: + b, err := strconv.ParseBool(string(s)) + if err != nil { + return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s) + } + return b, nil + } + + sv := reflect.ValueOf(src) + switch sv.Kind() { + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + iv := sv.Int() + if iv == 1 || iv == 0 { + return iv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv) + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: + uv := sv.Uint() + if uv == 1 || uv == 0 { + return uv == 1, nil + } + return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv) + } + + return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src) +} + +type Scanner interface { + // Scan assigns a value from a database driver. + // + // The src value will be of one of the following types: + // + // int64 + // float64 + // bool + // []byte + // string + // time.Time + // nil - for NULL values + // + // An error should be returned if the value cannot be stored + // without loss of information. + // + // Reference types such as []byte are only valid until the next call to Scan + // and should not be retained. Their underlying memory is owned by the driver. + // If retention is necessary, copy their values before the next call to Scan. + Scan(src interface{}) error +} diff --git a/easymap/abstruct.go b/easymap/abstruct.go new file mode 100644 index 0000000..e5fc888 --- /dev/null +++ b/easymap/abstruct.go @@ -0,0 +1,36 @@ +// Package easymap... +// +// Description : easymap... +// +// Author : go_developer@163.com<张德满> +// +// Date : 2021-02-23 9:55 下午 +package easymap + +// EasyMap 约束各种数据接口的实现 +// +// Author : go_developer@163.com<张德满> +// +// Date : 9:56 下午 2021/2/23 +type EasyMap interface { + Get(key interface{}) (interface{}, error) + GetWithReceiver(key interface{}, dest interface{}) error + GetUint(key interface{}) (uint, error) + GetUint8(key interface{}) (uint8, error) + GetUint16(key interface{}) (uint16, error) + GetUint32(key interface{}) (uint32, error) + GetUint64(key interface{}) (uint64, error) + GetInt(key interface{}) (int, error) + GetInt8(key interface{}) (int8, error) + GetInt16(key interface{}) (int16, error) + GetInt32(key interface{}) (int32, error) + GetInt64(key interface{}) (int64, error) + GetFloat32(key interface{}) (float32, error) + GetFloat64(key interface{}) (float64, error) + GetBool(key interface{}) (bool, error) + GetString(key interface{}) (string, error) + Set(key interface{}, value interface{}) + Del(key interface{}) + Exist(key interface{}) bool + GetAll() map[interface{}]interface{} +} diff --git a/easymap/error.go b/easymap/error.go new file mode 100644 index 0000000..493ec68 --- /dev/null +++ b/easymap/error.go @@ -0,0 +1,28 @@ +// Package easymap... +// +// Description : easymap... +// +// Author : go_developer@163.com<张德满> +// +// Date : 2021-02-23 10:15 下午 +package easymap + +import "github.com/pkg/errors" + +// keyNotFound key 不存在 +// +// Author : go_developer@163.com<张德满> +// +// Date : 10:17 下午 2021/2/23 +func keyNotFound(key interface{}) error { + return errors.Errorf("%v 未找到", key) +} + +// convertFail 数据类型妆换失败 +// +// Author : go_developer@163.com<张德满> +// +// Date : 10:28 下午 2021/2/23 +func convertFail(err error) error { + return errors.Wrapf(err, "数据类型转换失败") +} diff --git a/easymap/normal_map.go b/easymap/normal_map.go new file mode 100644 index 0000000..60f438d --- /dev/null +++ b/easymap/normal_map.go @@ -0,0 +1,273 @@ +// Package easymap ... +// +// Description : 普通的的map,增加锁支持 +// +// Author : go_developer@163.com<张德满> +// +// Date : 2021-02-23 10:03 下午 +package easymap + +import ( + "sync" + + "github.com/go-developer/gopkg/convert" +) + +// NewNormal 获取map实例 +// +// Author : go_developer@163.com<张德满> +// +// Date : 10:07 下午 2021/2/23 +func NewNormal(withLock bool) EasyMap { + em := &normal{ + data: make(map[interface{}]interface{}), + withLock: withLock, + } + if withLock { + em.lock = &sync.RWMutex{} + } + return em +} + +// normal 普通map,内部可以加锁 +// +// Author : go_developer@163.com<张德满> +// +// Date : 10:03 下午 2021/2/23 +type normal struct { + data map[interface{}]interface{} + withLock bool + lock *sync.RWMutex +} + +func (n *normal) Get(key interface{}) (interface{}, error) { + if !n.Exist(key) { + return nil, keyNotFound(key) + } + n.RLock() + defer n.RUnlock() + return n.data[key], nil +} + +func (n *normal) GetWithReceiver(key interface{}, dest interface{}) error { + var ( + val interface{} + err error + ) + if val, err = n.Get(key); nil != err { + return err + } + return convertFail(convert.ConvertAssign(dest, val)) +} + +func (n *normal) GetUint(key interface{}) (uint, error) { + var ( + result uint + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetUint8(key interface{}) (uint8, error) { + var ( + result uint8 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetUint16(key interface{}) (uint16, error) { + var ( + result uint16 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetUint32(key interface{}) (uint32, error) { + var ( + result uint32 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetUint64(key interface{}) (uint64, error) { + var ( + result uint64 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetInt(key interface{}) (int, error) { + var ( + result int + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetInt8(key interface{}) (int8, error) { + var ( + result int8 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetInt16(key interface{}) (int16, error) { + var ( + result int16 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetInt32(key interface{}) (int32, error) { + var ( + result int32 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetInt64(key interface{}) (int64, error) { + var ( + result int64 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetFloat32(key interface{}) (float32, error) { + var ( + result float32 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetFloat64(key interface{}) (float64, error) { + var ( + result float64 + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return 0, err + } + return result, nil +} + +func (n *normal) GetBool(key interface{}) (bool, error) { + var ( + result bool + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return false, err + } + return result, nil +} + +func (n *normal) GetString(key interface{}) (string, error) { + var ( + result string + err error + ) + if err = n.GetWithReceiver(key, &result); nil != err { + return "", err + } + return result, nil +} + +func (n *normal) Set(key interface{}, value interface{}) { + n.Lock() + defer n.Unlock() + n.data[key] = value +} + +func (n *normal) Del(key interface{}) { + n.Lock() + defer n.Unlock() + delete(n.data, key) +} + +func (n *normal) Exist(key interface{}) bool { + n.RLock() + defer n.RUnlock() + _, exist := n.data[key] + return exist +} + +// GetAll 读取全部数据使用的是原始数据深拷贝,避免获取到全部数据之后,直接读取导致并发读写 +// +// Author : go_developer@163.com<张德满> +// +// Date : 10:36 下午 2021/2/23 +func (n *normal) GetAll() map[interface{}]interface{} { + result := make(map[interface{}]interface{}) + n.lock.RLock() + defer n.lock.RUnlock() + for key, val := range n.data { + result[key] = val + } + return result +} + +func (n *normal) RLock() { + if n.withLock { + n.lock.RLock() + } +} + +func (n *normal) RUnlock() { + if n.withLock { + n.lock.RUnlock() + } +} + +func (n *normal) Lock() { + if n.withLock { + n.lock.Lock() + } +} + +func (n *normal) Unlock() { + if n.withLock { + n.lock.Unlock() + } +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..5f16773 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/go-developer/gopkg + +go 1.15 + +require github.com/pkg/errors v0.9.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7c401c3 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=