//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 m int var err error switch f.GetType() { case descriptor.FieldDescriptorProto_TYPE_DOUBLE: m, err = DecFloat64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FLOAT: m, err = DecFloat32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_INT64: m, err = DecInt64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_UINT64: m, err = DecUint64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_INT32: m, err = DecInt32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FIXED64: m, err = DecFixed64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_FIXED32: m, err = DecFixed32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_BOOL: m, err = DecBool(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_STRING: m, err = DecString(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_GROUP: panic("not implemented") case descriptor.FieldDescriptorProto_TYPE_BYTES: m, err = DecBytes(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_UINT32: m, err = DecUint32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_ENUM: m, err = DecInt32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SFIXED32: m, err = DecSfixed32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SFIXED64: m, err = DecSfixed64(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SINT32: m, err = DecSint32(buf, offset, w) case descriptor.FieldDescriptorProto_TYPE_SINT64: m, err = DecSint64(buf, offset, w) default: panic("unreachable") } offset += m 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 }