Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .chloggen/avoid-pointers-optional.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. receiver/otlp)
component: pkg/pdata

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Avoid using interfaces/oneof like style for optional fields

# One or more tracking issues or pull requests related to the change
issues: [14333]

# (Optional) One or more lines of additional information to render under the primary note.
# These lines will be padded with 2 spaces and then inserted directly into the document.
# Use pipe (|) for multiline entries.
subtext:

# Optional: The change log or logs in which this entry should be included.
# e.g. '[user]' or '[user, api]'
# Include 'user' if the change is relevant to end users.
# Include 'api' if there is a change to a library API.
# Default: '[user]'
change_logs: [user]
4 changes: 4 additions & 0 deletions internal/cmd/pdatagen/internal/pdata/one_of_field.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,10 @@ func (of *oneOfProtoField) DefaultValue() string {
panic("implement me")
}

func (of *oneOfProtoField) GenTest() string {
return "orig." + of.GetName() + " = " + of.TestValue()
}

func (of *oneOfProtoField) TestValue() string {
return "&" + of.protoName + "_" + of.fields[0].GetName() + "{" + of.fields[0].GetName() + ": " + of.fields[0].TestValue() + "}"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,25 @@ import (

const optionalPrimitiveAccessorsTemplate = `// {{ .fieldName }} returns the {{ .lowerFieldName }} associated with this {{ .structName }}.
func (ms {{ .structName }}) {{ .fieldName }}() {{ .returnType }} {
return ms.orig.Get{{ .fieldName }}()
return ms.orig.{{ .fieldName }}
}

// Has{{ .fieldName }} returns true if the {{ .structName }} contains a
// {{ .fieldName }} value otherwise.
func (ms {{ .structName }}) Has{{ .fieldName }}() bool {
return ms.orig.{{ .fieldName }}_ != nil
return ms.orig.Has{{ .fieldName }}()
}

// Set{{ .fieldName }} replaces the {{ .lowerFieldName }} associated with this {{ .structName }}.
func (ms {{ .structName }}) Set{{ .fieldName }}(v {{ .returnType }}) {
ms.state.AssertMutable()
ms.orig.{{ .fieldName }}_ = &internal.{{ .originStructType }}{{ "{" }}{{ .fieldName }}: v}
ms.orig.Set{{ .fieldName }}(v)
}

// Remove{{ .fieldName }} removes the {{ .lowerFieldName }} associated with this {{ .structName }}.
func (ms {{ .structName }}) Remove{{ .fieldName }}() {
ms.state.AssertMutable()
ms.orig.{{ .fieldName }}_ = nil
ms.orig.Remove{{ .fieldName }}()
}`

const optionalPrimitiveAccessorsTestTemplate = `func Test{{ .structName }}_{{ .fieldName }}(t *testing.T) {
Expand Down Expand Up @@ -57,16 +57,6 @@ const optionalPrimitiveAccessorsTestTemplate = `func Test{{ .structName }}_{{ .f
const optionalPrimitiveSetTestTemplate = `orig.{{ .fieldName }}_ = &internal.{{ .originStructType }}{
{{- .fieldName }}: {{ .testValue }}}`

const optionalOneOfMessageOrigTemplate = `
func (m *{{ .ParentMessageName }}) Get{{ .OneOfGroup }}() any {
if m != nil {
return m.{{ .Name }}_
}
return nil
}

`

type OptionalPrimitiveField struct {
fieldName string
protoID uint32
Expand All @@ -85,66 +75,24 @@ func (opv *OptionalPrimitiveField) GenerateTestValue(ms *messageStruct) string {
return template.Execute(template.Parse("optionalPrimitiveSetTestTemplate", []byte(optionalPrimitiveSetTestTemplate)), opv.templateFields(ms))
}

type optionalPrimitiveProtoField struct {
*proto.Field
}

func (opv optionalPrimitiveProtoField) GetName() string {
return opv.Name + "_"
}

func (opv optionalPrimitiveProtoField) TestValue() string {
return "&" + opv.OneOfMessageName + "{" + opv.Name + ": " + opv.Field.TestValue() + "}"
}

func (opv optionalPrimitiveProtoField) GenMessageField() string {
return opv.Name + "_ any"
}

func (opv optionalPrimitiveProtoField) GenOneOfMessages() string {
return template.Execute(template.Parse("optionalOneOfMessageOrigTemplate", []byte(optionalOneOfMessageOrigTemplate)), opv.Field) + opv.Field.GenOneOfMessages()
}

func (opv optionalPrimitiveProtoField) GenCopy() string {
return "switch t := src." + opv.Name + "_.(type) {\n\tcase *" + opv.OneOfMessageName + ":\n\t" + opv.Field.GenCopy() + "\ndefault: dest." + opv.Name + "_ = nil\n}\n"
}

func (opv optionalPrimitiveProtoField) GenDelete() string {
return "switch ov := orig." + opv.Name + "_.(type) {\n\tcase *" + opv.OneOfMessageName + ":\n\t" + opv.Field.GenDelete() + "\n}\n"
}

func (opv optionalPrimitiveProtoField) GenMarshalJSON() string {
return "if orig, ok := orig." + opv.Name + "_.(*" + opv.OneOfMessageName + "); ok {\n\t" + opv.Field.GenMarshalJSON() + "}"
}

func (opv optionalPrimitiveProtoField) GenSizeProto() string {
return "if orig, ok := orig." + opv.Name + "_.(*" + opv.OneOfMessageName + "); ok {\n\t_ = orig\n\t" + opv.Field.GenSizeProto() + "}"
}

func (opv optionalPrimitiveProtoField) GenMarshalProto() string {
return "if orig, ok := orig." + opv.Name + "_.(*" + opv.OneOfMessageName + "); ok {\n\t" + opv.Field.GenMarshalProto() + "}"
}

func (opv *OptionalPrimitiveField) toProtoField(ms *messageStruct) proto.FieldInterface {
return optionalPrimitiveProtoField{&proto.Field{
return &proto.Field{
Type: opv.protoType,
ID: opv.protoID,
OneOfGroup: opv.fieldName + "_",
Name: opv.fieldName,
OneOfMessageName: ms.protoName + "_" + opv.fieldName,
ParentMessageName: ms.protoName,
Nullable: true,
}}
}
}

func (opv *OptionalPrimitiveField) templateFields(ms *messageStruct) map[string]any {
pf := opv.toProtoField(ms).(optionalPrimitiveProtoField)
pf := opv.toProtoField(ms).(*proto.Field)
return map[string]any{
"structName": ms.getName(),
"defaultVal": pf.DefaultValue(),
"fieldName": opv.fieldName,
"lowerFieldName": strings.ToLower(opv.fieldName),
"testValue": pf.Field.TestValue(),
"testValue": pf.TestValue(),
"returnType": pf.GoType(),
"originName": ms.getOriginName(),
"originStructName": ms.getOriginFullName(),
Expand Down
12 changes: 9 additions & 3 deletions internal/cmd/pdatagen/internal/proto/copy.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import (

const copyOther = `{{ if .repeated -}}
dest.{{ .fieldName }} = append(dest.{{ .fieldName }}[:0], src.{{ .fieldName }}...)
{{- else if not .nullable -}}
dest.{{ .fieldName }} = src.{{ .fieldName }}
{{ else -}}
{{ else if ne .oneOfGroup "" -}}
var ov *{{ .oneOfMessageName }}
if !UseProtoPooling.IsEnabled() {
ov = &{{ .oneOfMessageName }}{}
Expand All @@ -20,6 +18,14 @@ const copyOther = `{{ if .repeated -}}
}
ov.{{ .fieldName }} = t.{{ .fieldName }}
dest.{{ .oneOfGroup }} = ov
{{ else if .nullable -}}
if src.Has{{ .fieldName }}() {
dest.Set{{ .fieldName }}(src.{{ .fieldName }})
} else {
dest.Remove{{ .fieldName }}()
}
{{ else -}}
dest.{{ .fieldName }} = src.{{ .fieldName }}
{{- end }}`

const copyMessage = `{{ if .repeated -}}
Expand Down
10 changes: 0 additions & 10 deletions internal/cmd/pdatagen/internal/proto/enum.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 17 additions & 1 deletion internal/cmd/pdatagen/internal/proto/field.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ type FieldInterface interface {

GenOneOfMessages() string

GenTest() string

GoType() string

DefaultValue() string
Expand Down Expand Up @@ -105,6 +107,17 @@ func (pf *Field) DefaultValue() string {
}
}

func (pf *Field) GenTest() string {
if pf.Repeated {
return "orig." + pf.GetName() + " = " + pf.TestValue()
}

if pf.Type != TypeMessage && pf.Nullable {
return "orig.Set" + pf.GetName() + "(" + pf.TestValue() + ")"
}
return "orig." + pf.GetName() + " = " + pf.TestValue()
}

func (pf *Field) TestValue() string {
if pf.Repeated {
return pf.MemberGoType() + "{" + pf.DefaultValue() + ", " + pf.rawTestValue() + "}"
Expand Down Expand Up @@ -171,7 +184,10 @@ func (pf *Field) MemberGoType() string {
if pf.Repeated {
return "[]" + ptrGoType()
}
return ptrGoType()
if pf.Type == TypeMessage {
return ptrGoType()
}
return pf.GoType()
}

func (pf *Field) GenMessageField() string {
Expand Down
8 changes: 6 additions & 2 deletions internal/cmd/pdatagen/internal/proto/json_marshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ const marshalJSONPrimitive = `{{ if .repeated -}}
}
dest.WriteArrayEnd()
}
{{ else if ne .oneOfGroup "" -}}
dest.WriteObjectField("{{ .jsonTag }}")
dest.Write{{ upperFirst .goType }}(orig.{{ .fieldName }})
{{- else }}
{{- if not .nullable -}}
if orig.{{ .fieldName }} != {{ .defaultValue }} {
{{- else -}}
if orig.Has{{ .fieldName }} () {
{{ end -}}
dest.WriteObjectField("{{ .jsonTag }}")
dest.Write{{ upperFirst .goType }}(orig.{{ .fieldName }})
{{- if not .nullable -}}
}
{{- end }}{{- end }}`
{{- end }}`

const marshalJSONEnum = `{{ if .repeated -}}
if len(orig.{{ .fieldName }}) > 0 {
Expand Down
4 changes: 3 additions & 1 deletion internal/cmd/pdatagen/internal/proto/json_unmarshal.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const unmarshalJSONPrimitive = ` case {{ .allJSONTags }}:
for iter.ReadArray() {
orig.{{ .fieldName }} = append(orig.{{ .fieldName }}, iter.Read{{ upperFirst .goType }}())
}
{{ else if .nullable -}}
{{ else if ne .oneOfGroup "" -}}
{
var ov *{{ .oneOfMessageName }}
if !UseProtoPooling.IsEnabled() {
Expand All @@ -28,6 +28,8 @@ const unmarshalJSONPrimitive = ` case {{ .allJSONTags }}:
ov.{{ .fieldName }} = iter.Read{{ upperFirst .goType }}()
orig.{{ .oneOfGroup }} = ov
}
{{ else if .nullable -}}
orig.Set{{ .fieldName }}(iter.Read{{ upperFirst .goType }}())
{{ else -}}
orig.{{ .fieldName }} = iter.Read{{ upperFirst .goType }}()
{{- end }}`
Expand Down
21 changes: 21 additions & 0 deletions internal/cmd/pdatagen/internal/proto/message.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,22 @@ type Message struct {
OriginFullName string
UpstreamMessage string
Fields []FieldInterface
metadata *Metadata
}

func (ms *Message) GenerateMessage(imports, testImports []string) []byte {
ms.metadata = newMetadata(ms)
return []byte(template.Execute(messageTemplate, ms.templateFields(imports, testImports)))
}

func (ms *Message) GenerateMessageTests(imports, testImports []string) []byte {
return []byte(template.Execute(messageTestTemplate, ms.templateFields(imports, testImports)))
}

func (ms *Message) GenerateMetadata() string {
return string(ms.metadata.Generate())
}

func (ms *Message) templateFields(imports, testImports []string) map[string]any {
return map[string]any{
"fields": ms.Fields,
Expand All @@ -43,5 +49,20 @@ func (ms *Message) templateFields(imports, testImports []string) map[string]any
"description": ms.Description,
"imports": imports,
"testImports": testImports,
// 0 size means no metadata is needed
"metadataSize": ms.metadataSize(),
"GenerateMetadata": ms.GenerateMetadata,
}
}

func (ms *Message) metadataSize() uint {
if ms.metadata == nil {
return 0
}

if len(ms.metadata.OptionalFields) == 0 {
return 0
}

return uint(ms.metadata.OptionalFields[len(ms.metadata.OptionalFields)-1].Value/64 + 1)
}
77 changes: 77 additions & 0 deletions internal/cmd/pdatagen/internal/proto/metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package proto // import "go.opentelemetry.io/collector/internal/cmd/pdatagen/internal/proto"

import (
"go.opentelemetry.io/collector/internal/cmd/pdatagen/internal/template"
)

const metadataMessageTemplate = `
{{- range .OptionalFields }}
const fieldBlock{{ $.Name }}{{ .Name }} = uint64({{ .Value }} >> 6)
const fieldBit{{ $.Name }}{{ .Name }} = uint64(1 << {{ .Value }} & 0x3F)

func (m *{{ $.Name }}) Set{{ .Name }}(value {{ .GoType }}) {
m.{{ .Name }} = value
m.metadata[fieldBlock{{ $.Name }}{{ .Name }}] |= fieldBit{{ $.Name }}{{ .Name }}
}

func (m *{{ $.Name }}) Remove{{ .Name }}() {
m.{{ .Name }} = {{ .DefaultValue }}
m.metadata[fieldBlock{{ $.Name }}{{ .Name }}] &^= fieldBit{{ $.Name }}{{ .Name }}
}

func (m *{{ $.Name }}) Has{{ .Name }}() bool {
return m.metadata[fieldBlock{{ $.Name }}{{ .Name }}] & fieldBit{{ $.Name }}{{ .Name }} != 0
}
{{- end }}

`

type Metadata struct {
Name string
OptionalFields []*MetadataOptionalField
}

type MetadataOptionalField struct {
*Field
Value int
}

func newMetadata(ms *Message) *Metadata {
meta := &Metadata{
Name: ms.Name,
}
value := 0
for _, fieldI := range ms.Fields {
field, ok := fieldI.(*Field)
if !ok {
continue
}
if field.Repeated {
continue
}
switch field.Type {
case TypeDouble, TypeFloat, TypeInt32, TypeInt64, TypeUint32, TypeUint64, TypeSInt32, TypeSInt64, TypeFixed32, TypeFixed64, TypeSFixed32, TypeSFixed64, TypeBool:
if !field.Nullable {
continue
}
default:
continue
}
meta.OptionalFields = append(meta.OptionalFields, &MetadataOptionalField{
Field: field,
Value: value,
})
value++
}
if len(meta.OptionalFields) == 0 {
return nil
}
return meta
}

func (meta *Metadata) Generate() []byte {
return []byte(template.Execute(template.Parse("metadataMessageTemplate", []byte(metadataMessageTemplate)), meta))
}
Loading
Loading