diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 12:36:04 +0000 |
commit | b09c6d56832eb1718c07d74abf3bc6ae3fe4e030 (patch) | |
tree | d2caec2610d4ea887803ec9e9c3cd77136c448ba /dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan | |
parent | Initial commit. (diff) | |
download | icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.tar.xz icingadb-b09c6d56832eb1718c07d74abf3bc6ae3fe4e030.zip |
Adding upstream version 1.1.0.upstream/1.1.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
3 files changed, 472 insertions, 0 deletions
diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go new file mode 100644 index 0000000..852c8bd --- /dev/null +++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan.go @@ -0,0 +1,201 @@ +package hscan + +import ( + "errors" + "fmt" + "reflect" + "strconv" +) + +// decoderFunc represents decoding functions for default built-in types. +type decoderFunc func(reflect.Value, string) error + +var ( + // List of built-in decoders indexed by their numeric constant values (eg: reflect.Bool = 1). + decoders = []decoderFunc{ + reflect.Bool: decodeBool, + reflect.Int: decodeInt, + reflect.Int8: decodeInt8, + reflect.Int16: decodeInt16, + reflect.Int32: decodeInt32, + reflect.Int64: decodeInt64, + reflect.Uint: decodeUint, + reflect.Uint8: decodeUint8, + reflect.Uint16: decodeUint16, + reflect.Uint32: decodeUint32, + reflect.Uint64: decodeUint64, + reflect.Float32: decodeFloat32, + reflect.Float64: decodeFloat64, + reflect.Complex64: decodeUnsupported, + reflect.Complex128: decodeUnsupported, + reflect.Array: decodeUnsupported, + reflect.Chan: decodeUnsupported, + reflect.Func: decodeUnsupported, + reflect.Interface: decodeUnsupported, + reflect.Map: decodeUnsupported, + reflect.Ptr: decodeUnsupported, + reflect.Slice: decodeSlice, + reflect.String: decodeString, + reflect.Struct: decodeUnsupported, + reflect.UnsafePointer: decodeUnsupported, + } + + // Global map of struct field specs that is populated once for every new + // struct type that is scanned. This caches the field types and the corresponding + // decoder functions to avoid iterating through struct fields on subsequent scans. + globalStructMap = newStructMap() +) + +func Struct(dst interface{}) (StructValue, error) { + v := reflect.ValueOf(dst) + + // The destination to scan into should be a struct pointer. + if v.Kind() != reflect.Ptr || v.IsNil() { + return StructValue{}, fmt.Errorf("redis.Scan(non-pointer %T)", dst) + } + + v = v.Elem() + if v.Kind() != reflect.Struct { + return StructValue{}, fmt.Errorf("redis.Scan(non-struct %T)", dst) + } + + return StructValue{ + spec: globalStructMap.get(v.Type()), + value: v, + }, nil +} + +// Scan scans the results from a key-value Redis map result set to a destination struct. +// The Redis keys are matched to the struct's field with the `redis` tag. +func Scan(dst interface{}, keys []interface{}, vals []interface{}) error { + if len(keys) != len(vals) { + return errors.New("args should have the same number of keys and vals") + } + + strct, err := Struct(dst) + if err != nil { + return err + } + + // Iterate through the (key, value) sequence. + for i := 0; i < len(vals); i++ { + key, ok := keys[i].(string) + if !ok { + continue + } + + val, ok := vals[i].(string) + if !ok { + continue + } + + if err := strct.Scan(key, val); err != nil { + return err + } + } + + return nil +} + +func decodeBool(f reflect.Value, s string) error { + b, err := strconv.ParseBool(s) + if err != nil { + return err + } + f.SetBool(b) + return nil +} + +func decodeInt8(f reflect.Value, s string) error { + return decodeNumber(f, s, 8) +} + +func decodeInt16(f reflect.Value, s string) error { + return decodeNumber(f, s, 16) +} + +func decodeInt32(f reflect.Value, s string) error { + return decodeNumber(f, s, 32) +} + +func decodeInt64(f reflect.Value, s string) error { + return decodeNumber(f, s, 64) +} + +func decodeInt(f reflect.Value, s string) error { + return decodeNumber(f, s, 0) +} + +func decodeNumber(f reflect.Value, s string, bitSize int) error { + v, err := strconv.ParseInt(s, 10, bitSize) + if err != nil { + return err + } + f.SetInt(v) + return nil +} + +func decodeUint8(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 8) +} + +func decodeUint16(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 16) +} + +func decodeUint32(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 32) +} + +func decodeUint64(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 64) +} + +func decodeUint(f reflect.Value, s string) error { + return decodeUnsignedNumber(f, s, 0) +} + +func decodeUnsignedNumber(f reflect.Value, s string, bitSize int) error { + v, err := strconv.ParseUint(s, 10, bitSize) + if err != nil { + return err + } + f.SetUint(v) + return nil +} + +func decodeFloat32(f reflect.Value, s string) error { + v, err := strconv.ParseFloat(s, 32) + if err != nil { + return err + } + f.SetFloat(v) + return nil +} + +// although the default is float64, but we better define it. +func decodeFloat64(f reflect.Value, s string) error { + v, err := strconv.ParseFloat(s, 64) + if err != nil { + return err + } + f.SetFloat(v) + return nil +} + +func decodeString(f reflect.Value, s string) error { + f.SetString(s) + return nil +} + +func decodeSlice(f reflect.Value, s string) error { + // []byte slice ([]uint8). + if f.Type().Elem().Kind() == reflect.Uint8 { + f.SetBytes([]byte(s)) + } + return nil +} + +func decodeUnsupported(v reflect.Value, s string) error { + return fmt.Errorf("redis.Scan(unsupported %s)", v.Type()) +} diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go new file mode 100644 index 0000000..ab4c0e1 --- /dev/null +++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/hscan_test.go @@ -0,0 +1,178 @@ +package hscan + +import ( + "math" + "strconv" + "testing" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" +) + +type data struct { + Omit string `redis:"-"` + Empty string + + String string `redis:"string"` + Bytes []byte `redis:"byte"` + Int int `redis:"int"` + Int8 int8 `redis:"int8"` + Int16 int16 `redis:"int16"` + Int32 int32 `redis:"int32"` + Int64 int64 `redis:"int64"` + Uint uint `redis:"uint"` + Uint8 uint8 `redis:"uint8"` + Uint16 uint16 `redis:"uint16"` + Uint32 uint32 `redis:"uint32"` + Uint64 uint64 `redis:"uint64"` + Float float32 `redis:"float"` + Float64 float64 `redis:"float64"` + Bool bool `redis:"bool"` +} + +type i []interface{} + +func TestGinkgoSuite(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "hscan") +} + +var _ = Describe("Scan", func() { + It("catches bad args", func() { + var d data + + Expect(Scan(&d, i{}, i{})).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{})) + + Expect(Scan(&d, i{"key"}, i{})).To(HaveOccurred()) + Expect(Scan(&d, i{"key"}, i{"1", "2"})).To(HaveOccurred()) + Expect(Scan(nil, i{"key", "1"}, i{})).To(HaveOccurred()) + + var m map[string]interface{} + Expect(Scan(&m, i{"key"}, i{"1"})).To(HaveOccurred()) + Expect(Scan(data{}, i{"key"}, i{"1"})).To(HaveOccurred()) + Expect(Scan(data{}, i{"key", "string"}, i{nil, nil})).To(HaveOccurred()) + }) + + It("number out of range", func() { + f := func(v uint64) string { + return strconv.FormatUint(v, 10) + "1" + } + keys := i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"} + vals := i{ + f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64), + f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10) + "1", + "13.4028234663852886e+38", "11.79769313486231570e+308", + } + for k, v := range keys { + var d data + Expect(Scan(&d, i{v}, i{vals[k]})).To(HaveOccurred()) + } + + // success + f = func(v uint64) string { + return strconv.FormatUint(v, 10) + } + keys = i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"} + vals = i{ + f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64), + f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10), + "3.40282346638528859811704183484516925440e+38", "1.797693134862315708145274237317043567981e+308", + } + var d data + Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{ + Int8: math.MaxInt8, + Int16: math.MaxInt16, + Int32: math.MaxInt32, + Int64: math.MaxInt64, + Uint8: math.MaxUint8, + Uint16: math.MaxUint16, + Uint32: math.MaxUint32, + Uint64: math.MaxUint64, + Float: math.MaxFloat32, + Float64: math.MaxFloat64, + })) + }) + + It("scans good values", func() { + var d data + + // non-tagged fields. + Expect(Scan(&d, i{"key"}, i{"value"})).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{})) + + keys := i{"string", "byte", "int", "int64", "uint", "uint64", "float", "float64", "bool"} + vals := i{ + "str!", "bytes!", "123", "123456789123456789", "456", "987654321987654321", + "123.456", "123456789123456789.987654321987654321", "1", + } + Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{ + String: "str!", + Bytes: []byte("bytes!"), + Int: 123, + Int64: 123456789123456789, + Uint: 456, + Uint64: 987654321987654321, + Float: 123.456, + Float64: 1.2345678912345678e+17, + Bool: true, + })) + + // Scan a different type with the same values to test that + // the struct spec maps don't conflict. + type data2 struct { + String string `redis:"string"` + Bytes []byte `redis:"byte"` + Int int `redis:"int"` + Uint uint `redis:"uint"` + Float float32 `redis:"float"` + Bool bool `redis:"bool"` + } + var d2 data2 + Expect(Scan(&d2, keys, vals)).NotTo(HaveOccurred()) + Expect(d2).To(Equal(data2{ + String: "str!", + Bytes: []byte("bytes!"), + Int: 123, + Uint: 456, + Float: 123.456, + Bool: true, + })) + + Expect(Scan(&d, i{"string", "float", "bool"}, i{"", "1", "t"})).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{ + String: "", + Bytes: []byte("bytes!"), + Int: 123, + Int64: 123456789123456789, + Uint: 456, + Uint64: 987654321987654321, + Float: 1.0, + Float64: 1.2345678912345678e+17, + Bool: true, + })) + }) + + It("omits untagged fields", func() { + var d data + + Expect(Scan(&d, i{"empty", "omit", "string"}, i{"value", "value", "str!"})).NotTo(HaveOccurred()) + Expect(d).To(Equal(data{ + String: "str!", + })) + }) + + It("catches bad values", func() { + var d data + + Expect(Scan(&d, i{"int"}, i{"a"})).To(HaveOccurred()) + Expect(Scan(&d, i{"uint"}, i{"a"})).To(HaveOccurred()) + Expect(Scan(&d, i{"uint"}, i{""})).To(HaveOccurred()) + Expect(Scan(&d, i{"float"}, i{"b"})).To(HaveOccurred()) + Expect(Scan(&d, i{"bool"}, i{"-1"})).To(HaveOccurred()) + Expect(Scan(&d, i{"bool"}, i{""})).To(HaveOccurred()) + Expect(Scan(&d, i{"bool"}, i{"123"})).To(HaveOccurred()) + }) +}) diff --git a/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go new file mode 100644 index 0000000..6839412 --- /dev/null +++ b/dependencies/pkg/mod/github.com/go-redis/redis/v8@v8.11.5/internal/hscan/structmap.go @@ -0,0 +1,93 @@ +package hscan + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +// structMap contains the map of struct fields for target structs +// indexed by the struct type. +type structMap struct { + m sync.Map +} + +func newStructMap() *structMap { + return new(structMap) +} + +func (s *structMap) get(t reflect.Type) *structSpec { + if v, ok := s.m.Load(t); ok { + return v.(*structSpec) + } + + spec := newStructSpec(t, "redis") + s.m.Store(t, spec) + return spec +} + +//------------------------------------------------------------------------------ + +// structSpec contains the list of all fields in a target struct. +type structSpec struct { + m map[string]*structField +} + +func (s *structSpec) set(tag string, sf *structField) { + s.m[tag] = sf +} + +func newStructSpec(t reflect.Type, fieldTag string) *structSpec { + numField := t.NumField() + out := &structSpec{ + m: make(map[string]*structField, numField), + } + + for i := 0; i < numField; i++ { + f := t.Field(i) + + tag := f.Tag.Get(fieldTag) + if tag == "" || tag == "-" { + continue + } + + tag = strings.Split(tag, ",")[0] + if tag == "" { + continue + } + + // Use the built-in decoder. + out.set(tag, &structField{index: i, fn: decoders[f.Type.Kind()]}) + } + + return out +} + +//------------------------------------------------------------------------------ + +// structField represents a single field in a target struct. +type structField struct { + index int + fn decoderFunc +} + +//------------------------------------------------------------------------------ + +type StructValue struct { + spec *structSpec + value reflect.Value +} + +func (s StructValue) Scan(key string, value string) error { + field, ok := s.spec.m[key] + if !ok { + return nil + } + if err := field.fn(s.value.Field(field.index), value); err != nil { + t := s.value.Type() + return fmt.Errorf("cannot scan redis.result %s into struct field %s.%s of type %s, error-%s", + value, t.Name(), t.Field(field.index).Name, t.Field(field.index).Type, err.Error()) + } + return nil +} |