// Copyright 2018-2019 opcua authors. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. package ua import ( "fmt" "math" "reflect" "time" "github.com/gopcua/opcua/errors" ) var ( binaryDecoder = reflect.TypeOf((*BinaryDecoder)(nil)).Elem() timeType = reflect.TypeOf(time.Time{}) ) func isBinaryDecoder(val reflect.Value) bool { return val.Type().Implements(binaryDecoder) } func isTime(val reflect.Value) bool { return val.Type() == timeType } type BinaryDecoder interface { Decode([]byte) (int, error) } func Decode(b []byte, v interface{}) (int, error) { val := reflect.ValueOf(v) n, err := decode(b, val, val.Type().String()) if err != nil { return n, err } return n, nil } func decode(b []byte, val reflect.Value, name string) (n int, err error) { if debugCodec { fmt.Printf("decode: %s has type %v and is a %s, %d bytes\n", name, val.Type(), val.Type().Kind(), len(b)) defer func() { fmt.Printf("decode: decoded %d bytes into %s\n", n, name) }() } buf := NewBuffer(b) switch { case isBinaryDecoder(val): v := val.Interface().(BinaryDecoder) return v.Decode(b) case isTime(val): val.Set(reflect.ValueOf(buf.ReadTime())) default: // fmt.Printf("decode: %s is a %s\n", name, val.Kind()) switch val.Kind() { case reflect.Bool: val.SetBool(buf.ReadBool()) case reflect.Int8: val.SetInt(int64(buf.ReadInt8())) case reflect.Uint8: val.SetUint(uint64(buf.ReadByte())) case reflect.Int16: val.SetInt(int64(buf.ReadInt16())) case reflect.Uint16: val.SetUint(uint64(buf.ReadUint16())) case reflect.Int32: val.SetInt(int64(buf.ReadInt32())) case reflect.Uint32: val.SetUint(uint64(buf.ReadUint32())) case reflect.Int64: val.SetInt(buf.ReadInt64()) case reflect.Uint64: val.SetUint(buf.ReadUint64()) case reflect.Float32: val.SetFloat(float64(buf.ReadFloat32())) case reflect.Float64: val.SetFloat(buf.ReadFloat64()) case reflect.String: val.SetString(buf.ReadString()) case reflect.Slice: return decodeSlice(b, val, name) case reflect.Ptr: return decode(b, val.Elem(), name) case reflect.Struct: return decodeStruct(b, val, name) default: return 0, errors.Errorf("unsupported type %s", val.Type()) } } return buf.Pos(), buf.Error() } func decodeStruct(b []byte, val reflect.Value, name string) (int, error) { pos := 0 valt := val.Type() for i := 0; i < val.NumField(); i++ { ft := valt.Field(i) fname := name + "." + ft.Name // if the field is a pointer we need to create // the value before we can marshal data into it. f := val.Field(i) if f.Type().Kind() == reflect.Ptr { f.Set(reflect.New(f.Type().Elem())) // fmt.Printf("decode: %s has type %v and has new value %#v\n", fname, f.Type(), f.Interface()) } n, err := decode(b[pos:], f, fname) if err != nil { return pos, err } pos += n } return pos, nil } func decodeSlice(b []byte, val reflect.Value, name string) (int, error) { buf := NewBuffer(b) n := buf.ReadUint32() if buf.Error() != nil { return buf.Pos(), buf.Error() } if n == null { return buf.Pos(), nil } if n > math.MaxInt32 { return buf.Pos(), errors.Errorf("array too large: %d", n) } // elemType is the type of the slice elements // e.g. *Foo for []*Foo elemType := val.Type().Elem() // fmt.Println("elemType: ", elemType.String()) // fast path for []byte if elemType.Kind() == reflect.Uint8 { // fmt.Println("decode: []byte fast path") val.SetBytes(buf.ReadN(int(n))) return buf.Pos(), buf.Error() } pos := buf.Pos() // a is a slice of []*Foo a := reflect.MakeSlice(val.Type(), int(n), int(n)) for i := 0; i < int(n); i++ { // if the slice elements are pointers we need to create // them before we can marshal data into them. if elemType.Kind() == reflect.Ptr { a.Index(i).Set(reflect.New(elemType.Elem())) } ename := fmt.Sprintf("%s[%d]", name, i) m, err := decode(b[pos:], a.Index(i), ename) if err != nil { return pos, err } pos += m } val.Set(a) return pos, nil }