//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 }
//Converts a marshalled protocol buffer to string. //It does this pretty effienctly by not combining slices. //It rather prints every element of the slice seperately. //This is typically used for debugging. func ToString(rootPkg string, rootMsg string, desc *descriptor.FileDescriptorSet, fpath string, buf []byte, tabs int, writer io.Writer) error { w := &stringWriter{tabs, writer} messagePkg := rootPkg messageName := rootMsg if len(fpath) > 0 { fd, err := new(rootPkg, rootMsg, desc, fpath) if err != nil { panic(err) } messagePkg = fd.rootPkg messageName = fd.rootMsg } msg := desc.GetMessage(messagePkg, messageName) if msg == nil { panic("message not found: " + messagePkg + "." + messageName) } offset := 0 for offset < len(buf) { key, n, err := decodeVarint(buf, offset) if err != nil { panic(err) } offset += n fieldNum := int32(key >> 3) found := false for _, f := range msg.GetField() { if f.IsPacked() { panic("not implemented") } if f.GetNumber() != fieldNum { continue } found = true if f.GetType() == descriptor.FieldDescriptorProto_TYPE_MESSAGE { length, nn, err := decodeVarint(buf, offset) if err != nil { panic(err) } offset += nn w.tab() w.String(f.GetName() + `:{` + "\n") err = ToString(messagePkg, messageName, desc, f.GetName(), buf[offset:offset+int(length)], tabs+1, writer) if err != nil { return err } offset += int(length) w.tab() w.String(`}` + "\n") break } else { w.tab() w.String(f.GetName() + `:`) var n int var err error switch f.GetType() { case descriptor.FieldDescriptorProto_TYPE_DOUBLE: n, err = DecFloat64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FLOAT: n, err = DecFloat32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_INT64: n, err = DecInt64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_UINT64: n, err = DecUint64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_INT32: n, err = DecInt32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FIXED64: n, err = DecFixed64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FIXED32: n, err = DecFixed32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_BOOL: n, err = DecBool(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_STRING: n, err = DecString(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_GROUP: panic("not implemented") case descriptor.FieldDescriptorProto_TYPE_BYTES: n, err = DecBytes(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_UINT32: n, err = DecUint32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_ENUM: n, err = DecInt32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SFIXED32: n, err = DecSfixed32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SFIXED64: n, err = DecSfixed64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SINT32: n, err = DecSint32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SINT64: n, err = DecSint64(buf, offset, w) default: panic("unreachable") } offset += n if err != nil { panic(err) } w.String(",\n") break } } if !found { for _, extRange := range msg.GetExtensionRange() { if extRange.GetStart() <= fieldNum && fieldNum <= extRange.GetEnd() { found = true break } } w.tab() offset -= n nn, err := protoSkip(buf[offset:]) if err != nil { panic(err) } if !found { w.String("XXX_unrecognized:") } else { w.String("XXX_extensions:") extPkg, ext := desc.FindExtensionByFieldNumber(messagePkg, messageName, fieldNum) if ext != nil && ext.IsMessage() { names := strings.Split(ext.GetTypeName(), ".") if len(names) == 3 { _, nnn, err := decodeVarint(buf, offset+n) if err != nil { panic(err) } w.tab() w.String(extPkg + "." + names[2] + `:{` + "\n") err = ToString(extPkg, names[2], desc, "", buf[offset+n+nnn:offset+nn], tabs+1, writer) if err != nil { return err } offset += nn w.tab() w.String(`}` + "\n") break } else { panic(ext.GetTypeName()) } } } w.Bytes(buf[offset : offset+nn]) w.String("\n") offset += nn } } return nil }