list.go (324 lines of code) (raw):

/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 hessian import ( "io" "reflect" "strconv" "strings" "sync" "time" ) import ( perrors "github.com/pkg/errors" ) import ( "github.com/apache/dubbo-go-hessian2/java_exception" ) var ( listTypeNameMapper = &sync.Map{} listTypeMapper = map[string]reflect.Type{ "string": reflect.TypeOf(""), "java.lang.String": reflect.TypeOf(""), "char": reflect.TypeOf(""), "short": reflect.TypeOf(int32(0)), "int": reflect.TypeOf(int32(0)), "long": reflect.TypeOf(int64(0)), "float": reflect.TypeOf(float64(0)), "double": reflect.TypeOf(float64(0)), "boolean": reflect.TypeOf(true), "java.util.Date": reflect.TypeOf(time.Time{}), "date": reflect.TypeOf(time.Time{}), "object": reflect.TypeOf([]Object{}).Elem(), "java.lang.Object": reflect.TypeOf([]Object{}).Elem(), // exception field StackTraceElement "java.lang.StackTraceElement": reflect.TypeOf([]*java_exception.StackTraceElement{}).Elem(), } ) func init() { listTypeNameMapper.Store("string", "[string") listTypeNameMapper.Store("int8", "[short") listTypeNameMapper.Store("int16", "[short") listTypeNameMapper.Store("uint16", "[short") listTypeNameMapper.Store("int32", "[int") listTypeNameMapper.Store("uint32", "[int") listTypeNameMapper.Store("int", "[long") listTypeNameMapper.Store("uint", "[long") listTypeNameMapper.Store("int64", "[long") listTypeNameMapper.Store("uint64", "[long") listTypeNameMapper.Store("float32", "[float") listTypeNameMapper.Store("float64", "[double") listTypeNameMapper.Store("bool", "[boolean") listTypeNameMapper.Store("time.Time", "[date") listTypeNameMapper.Store("github.com/apache/dubbo-go-hessian2/java_exception/java_exception.Throwabler", "[java.lang.Throwable") listTypeNameMapper.Store("github.com/apache/dubbo-go-hessian2/hessian.Object", "[object") } func registerListNameMapping(gotype, javaType string) { listTypeNameMapper.Store(gotype, "["+javaType) } func getListTypeName(goType string) string { buf := strings.Builder{} count := strings.Count(goType, "[]") if count > 0 { for i := 1; i < count; i++ { buf.WriteString("[") } goType = strings.Replace(goType, "[]", "", -1) } v, ok := listTypeNameMapper.Load(goType) if !ok { v, ok = listTypeNameMapper.Load(strings.TrimPrefix(goType, "*")) } if ok { buf.WriteString(v.(string)) return buf.String() } return "" } func getListType(javaListName string) reflect.Type { javaName := javaListName if strings.Index(javaName, "[") == 0 { javaName = javaName[1:] } if strings.Index(javaName, "[") == 0 { lt := getListType(javaName) if lt == nil { return nil } return reflect.SliceOf(lt) } var sliceTy reflect.Type ltm := listTypeMapper[javaName] if ltm != nil { sliceTy = reflect.SliceOf(ltm) } if sliceTy == nil { tpStructInfo, _ := getStructInfo(javaName) if tpStructInfo == nil || tpStructInfo.typ == nil { return nil } tp := tpStructInfo.typ if tp.Kind() != reflect.Ptr { tp = reflect.New(tp).Type() } sliceTy = reflect.SliceOf(tp) } return sliceTy } // Object is equal to Object of java When encoding type Object interface{} ///////////////////////////////////////// // List ///////////////////////////////////////// // encList write list func (e *Encoder) encList(v interface{}) error { if !strings.Contains(reflect.TypeOf(v).String(), "interface {}") { return e.writeTypedList(v) } return e.writeUntypedList(v) } // writeTypedList write typed list // Include 3 formats: // ::= x55 type value* 'Z' # variable-length list // ::= 'V' type int value* # fixed-length list // ::= [x70-77] type value* # fixed-length typed list func (e *Encoder) writeTypedList(v interface{}) error { var err error value := reflect.ValueOf(v) // https://github.com/apache/dubbo-go-hessian2/issues/317 // if list is null, just return 'N' if value.IsNil() { e.buffer = encByte(e.buffer, BC_NULL) // 'N' return nil } // check ref if n, ok := e.checkRefMap(value); ok { e.buffer = encRef(e.buffer, n) return nil } // try to get list type name of the value type first, for hessian support pointer type. // if not found, try to get list type name of the unpacked type. typeName := getListTypeName(value.Type().String()) if typeName == "" { value = UnpackPtrValue(value) goType := UnpackPtrType(value.Type()) toType := combineGoTypeName(goType) typeName = getListTypeName(toType) if typeName == "" { return perrors.New("no this type name: " + toType) } } e.buffer = encByte(e.buffer, BC_LIST_FIXED) // 'V' e.buffer = encString(e.buffer, typeName) e.buffer = encInt32(e.buffer, int32(value.Len())) for i := 0; i < value.Len(); i++ { if err = e.Encode(value.Index(i).Interface()); err != nil { return err } } return nil } // writeUntypedList write untyped list // Include 3 formats: // ::= x57 value* 'Z' # variable-length untyped list // ::= x58 int value* # fixed-length untyped list // ::= [x78-7f] value* # fixed-length untyped list func (e *Encoder) writeUntypedList(v interface{}) error { var err error value := reflect.ValueOf(v) // check ref if n, ok := e.checkRefMap(value); ok { e.buffer = encRef(e.buffer, n) return nil } value = UnpackPtrValue(value) e.buffer = encByte(e.buffer, BC_LIST_FIXED_UNTYPED) // x58 e.buffer = encInt32(e.buffer, int32(value.Len())) for i := 0; i < value.Len(); i++ { if err = e.Encode(value.Index(i).Interface()); err != nil { return err } } return nil } ///////////////////////////////////////// // List ///////////////////////////////////////// // # list/vector // ::= x55 type value* 'Z' # variable-length list // ::= 'V' type int value* # fixed-length list // ::= x57 value* 'Z' # variable-length untyped list // ::= x58 int value* # fixed-length untyped list // ::= [x70-77] type value* # fixed-length typed list // ::= [x78-7f] value* # fixed-length untyped list func (d *Decoder) readBufByte() (byte, error) { var ( err error buf [1]byte ) _, err = io.ReadFull(d.reader, buf[:1]) if err != nil { return 0, perrors.WithStack(err) } return buf[0], nil } func listFixedTypedLenTag(tag byte) bool { return tag >= _listFixedTypedLenTagMin && tag <= _listFixedTypedLenTagMax } // list include 3 formats: // ::= x55 type value* 'Z' # variable-length list // ::= 'V' type int value* # fixed-length list // ::= [x70-77] type value* # fixed-length typed list func typedListTag(tag byte) bool { return tag == BC_LIST_FIXED || tag == BC_LIST_VARIABLE || listFixedTypedLenTag(tag) } func listFixedUntypedLenTag(tag byte) bool { return tag >= _listFixedUntypedLenTagMin && tag <= _listFixedUntypedLenTagMax } // include 3 formats: // ::= x57 value* 'Z' # variable-length untyped list // ::= x58 int value* # fixed-length untyped list // ::= [x78-7f] value* # fixed-length untyped list func untypedListTag(tag byte) bool { return tag == BC_LIST_FIXED_UNTYPED || tag == BC_LIST_VARIABLE_UNTYPED || listFixedUntypedLenTag(tag) } // decList read list func (d *Decoder) decList(flag int32) (interface{}, error) { var ( err error tag byte ) if flag != TAG_READ { tag = byte(flag) } else { tag, err = d.ReadByte() if err != nil { return nil, perrors.WithStack(err) } } switch { case tag == BC_NULL: return nil, nil case tag == BC_REF: return d.decRef(int32(tag)) case typedListTag(tag): return d.readTypedList(tag) case untypedListTag(tag): return d.readUntypedList(tag) case binaryTag(tag): return d.decBinary(int32(tag)) default: return nil, perrors.Errorf("error list tag: 0x%x", tag) } } // readTypedList read typed list // list include 3 formats: // ::= x55 type value* 'Z' # variable-length list // ::= 'V' type int value* # fixed-length list // ::= [x70-77] type value* # fixed-length typed list func (d *Decoder) readTypedList(tag byte) (interface{}, error) { listTyp, err := d.decString(TAG_READ) if err != nil { return nil, perrors.Errorf("error to read list type[%s]: %v", listTyp, err) } isVariableArr := tag == BC_LIST_VARIABLE var length int if listFixedTypedLenTag(tag) { length = int(tag - _listFixedTypedLenTagMin) } else if tag == BC_LIST_FIXED { ii, err := d.decInt32(TAG_READ) if err != nil { return nil, perrors.WithStack(err) } length = int(ii) } else if isVariableArr { length = 0 } else { return nil, perrors.Errorf("error typed list tag: 0x%x", tag) } if isCollectionSerialize(listTyp) { return d.decodeCollection(length, listTyp) } return d.readTypedListValue(length, listTyp, isVariableArr) } func (d *Decoder) readTypedListValue(length int, listTyp string, isVariableArr bool) (interface{}, error) { // return when no element if length < 0 { return nil, nil } var ( aryValue reflect.Value arrType reflect.Type ) t, err := strconv.Atoi(listTyp) if err == nil { // find the ref list type arrType = d.typeRefs.Get(t) if arrType == nil { return nil, perrors.Errorf("can't find ref list type at index %d", t) } aryValue = reflect.MakeSlice(arrType, length, length) } else { // try to find the registered list type arrType = getListType(listTyp) if arrType != nil { aryValue = reflect.MakeSlice(arrType, length, length) d.typeRefs.appendTypeRefs(listTyp, arrType) } else { // using default generic list type if not found registered aryValue = reflect.ValueOf(make([]interface{}, length, length)) d.typeRefs.appendTypeRefs(listTyp, aryValue.Type()) } } holder := d.appendRefs(aryValue) for j := 0; j < length || isVariableArr; j++ { it, err := d.DecodeValue() if err != nil { if perrors.Is(err, io.EOF) && isVariableArr { break } return nil, perrors.WithStack(err) } if isVariableArr { if it != nil { aryValue = reflect.Append(aryValue, EnsureRawValue(it)) } else { aryValue = reflect.Append(aryValue, reflect.Zero(aryValue.Type().Elem())) } holder.change(aryValue) } else { if it != nil { //aryValue.Index(j).Set(EnsureRawValue(it)) SetValue(aryValue.Index(j), EnsureRawValue(it)) } } } return holder, nil } // readUntypedList read untyped list // Include 3 formats: // ::= x57 value* 'Z' # variable-length untyped list // ::= x58 int value* # fixed-length untyped list // ::= [x78-7f] value* # fixed-length untyped list func (d *Decoder) readUntypedList(tag byte) (interface{}, error) { isVariableArr := tag == BC_LIST_VARIABLE_UNTYPED var length int if listFixedUntypedLenTag(tag) { length = int(tag - _listFixedUntypedLenTagMin) } else if tag == BC_LIST_FIXED_UNTYPED { ii, err := d.decInt32(TAG_READ) if err != nil { return nil, perrors.WithStack(err) } length = int(ii) } else if isVariableArr { length = 0 } else { return nil, perrors.Errorf("error untyped list tag: %x", tag) } ary := make([]interface{}, length) aryValue := reflect.ValueOf(ary) holder := d.appendRefs(aryValue) for j := 0; j < length || isVariableArr; j++ { it, err := d.DecodeValue() if err != nil { if perrors.Is(err, io.EOF) && isVariableArr { break } return nil, perrors.WithStack(err) } if isVariableArr { if it != nil { aryValue = reflect.Append(aryValue, EnsureRawValue(it)) } else { aryValue = reflect.Append(aryValue, reflect.Zero(aryValue.Type().Elem())) } holder.change(aryValue) } else { ary[j] = EnsureRawAny(it) } } return holder, nil }