ref.go (121 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 ( "reflect" "unsafe" ) import ( perrors "github.com/pkg/errors" ) // Empty slice is not nil, but the addresses of all empty slice are the same. var _emptySliceAddr = unsafe.Pointer(reflect.ValueOf([]interface{}{}).Pointer()) // The addresses of all nil map are the same. var _nilMapAddr = unsafe.Pointer(reflect.ValueOf(map[interface{}]interface{}(nil)).Pointer()) // the ref holder pointer type. var _refHolderPtrType = reflect.TypeOf(&_refHolder{}) // used to ref object,list,map type _refElem struct { // record the kind of target, objects are the same only if the address and kind are the same kind reflect.Kind // Different struct may share the same address and kind, // so using type information to distinguish them. tp reflect.Type // ref index index int } // _refHolder is used to record decode list, the address of which may change when appending more element. type _refHolder struct { // destinations destinations []reflect.Value value reflect.Value } // change ref value func (h *_refHolder) change(v reflect.Value) { if h.value.CanAddr() && v.CanAddr() && h.value.Pointer() == v.Pointer() { return } h.value = v } // notice all destinations ref to the value func (h *_refHolder) notify() { for _, dest := range h.destinations { SetValue(dest, h.value) } } // add destination func (h *_refHolder) add(dest reflect.Value) { h.destinations = append(h.destinations, dest) } // Add reference func (d *Decoder) appendRefs(v interface{}) *_refHolder { var holder *_refHolder vv := EnsurePackValue(v) // only slice and array need ref holder , for its address changes when decoding if vv.Kind() == reflect.Slice || vv.Kind() == reflect.Array { holder = &_refHolder{ value: vv, } // pack holder value v = reflect.ValueOf(holder) d.refHolders = append(d.refHolders, holder) } d.refs = append(d.refs, v) return holder } // encRef encode ref index func encRef(b []byte, index int) []byte { return encInt32(append(b, BC_REF), int32(index)) } // return the order number of ref object if found , // otherwise, add the object into the encode ref map func (e *Encoder) checkRefMap(v reflect.Value) (int, bool) { var ( kind reflect.Kind tp reflect.Type addr unsafe.Pointer ) if v.Kind() == reflect.Ptr { for v.Elem().Kind() == reflect.Ptr { v = v.Elem() } kind = v.Elem().Kind() if kind != reflect.Invalid { tp = v.Elem().Type() } if kind == reflect.Slice || kind == reflect.Map { addr = unsafe.Pointer(v.Elem().Pointer()) } else { addr = unsafe.Pointer(v.Pointer()) } } else { kind = v.Kind() tp = v.Type() switch kind { case reflect.Slice, reflect.Map: addr = unsafe.Pointer(v.Pointer()) default: addr = unsafe.Pointer(PackPtr(v).Pointer()) } } if addr == _emptySliceAddr || addr == _nilMapAddr { return 0, false } if elem, ok := e.refMap[addr]; ok { if elem.kind == kind { // If kind is not struct, just return the index. Otherwise, // check whether the types are same, because the different // empty struct may share the same address and kind. if elem.kind != reflect.Struct { return elem.index, ok } else if elem.tp == tp { return elem.index, ok } } return 0, false } n := len(e.refMap) e.refMap[addr] = _refElem{kind, tp, n} return 0, false } ///////////////////////////////////////// // Ref ///////////////////////////////////////// // # value reference (e.g. circular trees and graphs) // ref ::= x51 int # reference to nth map/list/object func (d *Decoder) decRef(flag int32) (interface{}, error) { var ( err error tag byte i int32 ) if flag != TAG_READ { tag = byte(flag) } else { tag, _ = d.ReadByte() } switch { case tag == BC_REF: i, err = d.decInt32(TAG_READ) if err != nil { return nil, err } if len(d.refs) <= int(i) { return nil, nil // return nil, ErrIllegalRefIndex } // return the exact ref object, which maybe a _refHolder return d.refs[i], nil default: return nil, perrors.Errorf("decRef illegal ref type tag:%+v", tag) } }