| package internal |
| |
| import ( |
| "reflect" |
| |
| "github.com/golang/protobuf/proto" |
| ) |
| |
| var typeOfBytes = reflect.TypeOf([]byte(nil)) |
| |
| // GetUnrecognized fetches the bytes of unrecognized fields for the given message. |
| func GetUnrecognized(msg proto.Message) []byte { |
| val := reflect.Indirect(reflect.ValueOf(msg)) |
| u := val.FieldByName("XXX_unrecognized") |
| if u.IsValid() && u.Type() == typeOfBytes { |
| return u.Interface().([]byte) |
| } |
| |
| // Fallback to reflection for API v2 messages |
| get, _, _, ok := unrecognizedGetSetMethods(val) |
| if !ok { |
| return nil |
| } |
| |
| return get.Call([]reflect.Value(nil))[0].Convert(typeOfBytes).Interface().([]byte) |
| } |
| |
| // SetUnrecognized adds the given bytes to the unrecognized fields for the given message. |
| func SetUnrecognized(msg proto.Message, data []byte) { |
| val := reflect.Indirect(reflect.ValueOf(msg)) |
| u := val.FieldByName("XXX_unrecognized") |
| if u.IsValid() && u.Type() == typeOfBytes { |
| // Just store the bytes in the unrecognized field |
| ub := u.Interface().([]byte) |
| ub = append(ub, data...) |
| u.Set(reflect.ValueOf(ub)) |
| return |
| } |
| |
| // Fallback to reflection for API v2 messages |
| get, set, argType, ok := unrecognizedGetSetMethods(val) |
| if !ok { |
| return |
| } |
| |
| existing := get.Call([]reflect.Value(nil))[0].Convert(typeOfBytes).Interface().([]byte) |
| if len(existing) > 0 { |
| data = append(existing, data...) |
| } |
| set.Call([]reflect.Value{reflect.ValueOf(data).Convert(argType)}) |
| } |
| |
| func unrecognizedGetSetMethods(val reflect.Value) (get reflect.Value, set reflect.Value, argType reflect.Type, ok bool) { |
| // val could be an APIv2 message. We use reflection to interact with |
| // this message so that we don't have a hard dependency on the new |
| // version of the protobuf package. |
| refMethod := val.MethodByName("ProtoReflect") |
| if !refMethod.IsValid() { |
| if val.CanAddr() { |
| refMethod = val.Addr().MethodByName("ProtoReflect") |
| } |
| if !refMethod.IsValid() { |
| return |
| } |
| } |
| refType := refMethod.Type() |
| if refType.NumIn() != 0 || refType.NumOut() != 1 { |
| return |
| } |
| ref := refMethod.Call([]reflect.Value(nil)) |
| getMethod, setMethod := ref[0].MethodByName("GetUnknown"), ref[0].MethodByName("SetUnknown") |
| if !getMethod.IsValid() || !setMethod.IsValid() { |
| return |
| } |
| getType := getMethod.Type() |
| setType := setMethod.Type() |
| if getType.NumIn() != 0 || getType.NumOut() != 1 || setType.NumIn() != 1 || setType.NumOut() != 0 { |
| return |
| } |
| arg := setType.In(0) |
| if !arg.ConvertibleTo(typeOfBytes) || getType.Out(0) != arg { |
| return |
| } |
| |
| return getMethod, setMethod, arg, true |
| } |