func new(rootPackage string, rootMessage string, descSet *descriptor.FileDescriptorSet, path string) (*fdesc, error) { fieldpaths := strings.Split(path, ".") keys := make([]uint64, len(fieldpaths)) fields := make([]*descriptor.FieldDescriptorProto, len(fieldpaths)) curPackage := rootPackage curMessage := rootMessage last := len(fieldpaths) - 1 var fieldDesc *descriptor.FieldDescriptorProto for i, f := range fieldpaths { fieldName := f fieldDesc = descSet.GetField(curPackage, curMessage, fieldName) if fieldDesc == nil { curPackage, fieldDesc = descSet.FindExtension(curPackage, curMessage, fieldName) if fieldDesc == nil { return nil, &errChild{fieldName: fieldName, pkg: curPackage, msg: curMessage} } typeNames := strings.Split(fieldDesc.GetTypeName(), ".") curMessage = fieldDesc.GetTypeName() if len(typeNames) > 1 { curPackage = typeNames[1] curMessage = typeNames[2] } fieldKey := fieldDesc.GetKeyUint64() keys[i] = fieldKey fields[i] = fieldDesc } else { fieldKey := fieldDesc.GetKeyUint64() if fieldDesc.IsMessage() { curPackage, curMessage = descSet.FindMessage(curPackage, curMessage, fieldName) } else if i != last { return nil, &errMessage{fieldName} } keys[i] = fieldKey fields[i] = fieldDesc } } fd := &fdesc{curPackage, curMessage, fields, fieldDesc, keys, 0} if fieldDesc.GetType() == descriptor.FieldDescriptorProto_TYPE_ENUM { typeNames := strings.Split(fieldDesc.GetTypeName(), ".") enumMessage := fieldDesc.GetTypeName() enumPackage := curPackage if len(typeNames) > 1 { enumPackage = typeNames[1] enumMessage = typeNames[2] } enum := descSet.GetEnum(enumPackage, enumMessage) if enum == nil { return nil, &errChild{fieldName: fieldDesc.GetName(), pkg: enumPackage, msg: enumMessage} } for _, v := range enum.GetValue() { if v.GetNumber() < fd.firstEnumValue { fd.firstEnumValue = v.GetNumber() } } } return fd, nil }
//Collapses a proto fieldpath into a go fieldpath. They are different if some of the fields in the fieldpath have been embedded. func Collapse(rootPkg string, rootMsg string, path string, descriptorSet *descriptor.FileDescriptorSet) (string, error) { msg := descriptorSet.GetMessage(rootPkg, rootMsg) if msg == nil { return "", &errUndefined{rootPkg, rootMsg, path} } paths := strings.Split(path, ".") if len(paths) == 0 { return "", &errUndefined{rootPkg, rootMsg, path} } if len(paths) == 1 { return path, nil } for _, f := range msg.GetField() { if f.GetName() != paths[0] { continue } if f.IsMessage() { newRootPkg, newRootMsg := descriptorSet.FindMessage(rootPkg, rootMsg, f.GetName()) if len(newRootPkg) == 0 || len(newRootMsg) == 0 { return "", &errUndefined{rootPkg, rootMsg, path} } newPath, err := Collapse(newRootPkg, newRootMsg, strings.Join(paths[1:], "."), descriptorSet) if err != nil { return "", err } if gogoproto.IsEmbed(f) { return newPath, nil } else { return paths[0] + "." + newPath, nil } } } if msg.IsExtendable() { newRootPkg, f := descriptorSet.FindExtension(rootPkg, rootMsg, paths[0]) if f == nil { return "", &errUndefined{rootPkg, rootMsg, path} } typeName := f.GetTypeName() typeNames := strings.Split(typeName, ".") newRootMsg := typeName if len(typeNames) > 1 { newRootMsg = typeNames[len(typeNames)-1] } newPath, err := Collapse(newRootPkg, newRootMsg, strings.Join(paths[1:], "."), descriptorSet) if err != nil { return "", err } if gogoproto.IsEmbed(f) { return newPath, nil } else { return paths[0] + "." + newPath, nil } } return "", nil }
// The NoMerge function checks that the marshaled protocol buffer does not require any merging when unmarshaling. // When this property holds, streaming processing is possible. // // See below quotes from the protocol buffer documentation that describes how merging should work. // // https://developers.google.com/protocol-buffers/docs/encoding#optional // // Normally, an encoded message would never have more than one instance of an optional or required field. // However, parsers are expected to handle the case in which they do. // For numeric types and strings, if the same value appears multiple times, the parser accepts the last value it sees. // For embedded message fields, the parser merges multiple instances of the same field, // as if with the Message::MergeFrom method – that is, // all singular scalar fields in the latter instance replace those in the former, // singular embedded messages are merged, and repeated fields are concatenated. // The effect of these rules is that parsing the concatenation of two encoded messages produces // exactly the same result as if you had parsed the two messages separately and merged the resulting objects. // That is, this: // // MyMessage message; // message.ParseFromString(str1 + str2); // // is equivalent to this: // // MyMessage message, message2; // message.ParseFromString(str1); // message2.ParseFromString(str2); // message.MergeFrom(message2); // // This property is occasionally useful, as it allows you to merge two messages even if you do not know their types. // // https://developers.google.com/protocol-buffers/docs/encoding#order // // However, protocol buffer parsers must be able to parse fields in any order, // as not all messages are created by simply serializing an object – for instance, // it's sometimes useful to merge two messages by simply concatenating them. func NoMerge(buf []byte, descriptorSet *descriptor.FileDescriptorSet, rootPkg string, rootMsg string) error { msg := descriptorSet.GetMessage(rootPkg, rootMsg) if msg == nil { return &errUndefined{rootPkg, rootMsg, ""} } i := 0 seen := make(map[int32]bool) for i < len(buf) { key, n, err := decodeVarint(buf, i) if err != nil { return err } i = i + n fieldNum := int32(key >> 3) wireType := int(key & 0x7) field := getFieldNumber(descriptorSet, rootPkg, rootMsg, msg, fieldNum) if field == nil || field.GetLabel() == descriptor.FieldDescriptorProto_LABEL_REPEATED { i, err = skip(buf, i, wireType) if err != nil { return err } continue } if seen[fieldNum] { return &errMerge{msg.GetName() + "." + field.GetName()} } seen[fieldNum] = true if !field.IsMessage() { i, err = skip(buf, i, wireType) if err != nil { return err } continue } length, n, err := decodeVarint(buf, i) if err != nil { return err } i += n fieldPkg, fieldMsg := descriptorSet.FindMessage(rootPkg, rootMsg, field.GetName()) if len(fieldMsg) == 0 { return &errUndefined{rootPkg, rootMsg, field.GetName()} } err = NoMerge(buf[i:i+int(length)], descriptorSet, fieldPkg, fieldMsg) if err != nil { return err } i += int(length) } return nil }
func NoLatentAppendingOrMerging(buf []byte, descriptorSet *descriptor.FileDescriptorSet, rootPkg string, rootMsg string) error { msg := descriptorSet.GetMessage(rootPkg, rootMsg) if msg == nil { return &errUndefined{rootPkg, rootMsg, ""} } i := 0 var lookingAt int32 = -1 seen := make(map[int32]bool) for i < len(buf) { key, n, err := decodeVarint(buf, i) if err != nil { return err } i = i + n fieldNum := int32(key >> 3) wireType := int(key & 0x7) field := getFieldNumber(descriptorSet, rootPkg, rootMsg, msg, fieldNum) if seen[fieldNum] { return &errLatent{msg.GetName() + "." + field.GetName()} } if lookingAt != fieldNum { seen[lookingAt] = true lookingAt = fieldNum } if !field.IsMessage() { i, err = skip(buf, i, wireType) if err != nil { return err } continue } length, n, err := decodeVarint(buf, i) if err != nil { return err } i += n fieldPkg, fieldMsg := descriptorSet.FindMessage(rootPkg, rootMsg, field.GetName()) if len(fieldMsg) == 0 { return &errUndefined{rootPkg, rootMsg, field.GetName()} } err = NoLatentAppendingOrMerging(buf[i:i+int(length)], descriptorSet, fieldPkg, fieldMsg) if err != nil { return err } i += int(length) } return nil }
func toMessages(elems []string, fileDescriptorSet *descriptor.FileDescriptorSet, messages []string) ([]string, error) { if len(elems) < 2 { return nil, &errUndefinedMsg{elems[0], "???"} } msg := fileDescriptorSet.GetMessage(elems[0], elems[1]) if msg == nil { return nil, &errUndefinedMsg{elems[0], elems[1]} } if len(elems) == 2 { messages = append(messages, elems[0]+"."+elems[1]) return messages, nil } newPkg, newMsg := fileDescriptorSet.FindMessage(elems[0], elems[1], elems[2]) if len(newMsg) == 0 { return nil, &errUndefined{elems[0], elems[1], elems[2]} } messages = append(messages, elems[0]+"."+elems[1]) elems[1] = newPkg elems[2] = newMsg return toMessages(elems[1:], fileDescriptorSet, messages) }