Skip to content
Merged
25 changes: 25 additions & 0 deletions .chloggen/xpdata-mapbuilder.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. otlpreceiver)
component: xpdata

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: Add experimental MapBuilder struct to optimize pcommon.Map construction

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

# (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: [api]
51 changes: 51 additions & 0 deletions pdata/xpdata/map_builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package xpdata // import "go.opentelemetry.io/collector/pdata/xpdata"

import (
"go.opentelemetry.io/collector/pdata/internal"
otlpcommon "go.opentelemetry.io/collector/pdata/internal/data/protogen/common/v1"
"go.opentelemetry.io/collector/pdata/pcommon"
)

// MapBuilder is an experimental struct which can be used to create a pcommon.Map more efficiently
// than by repeated use of the Put family of methods, which check for duplicate keys on every call
// (a linear time operation).
// A zero-initialized MapBuilder is ready for use.
type MapBuilder struct {
state internal.State
pairs []otlpcommon.KeyValue
}

// EnsureCapacity increases the capacity of this MapBuilder instance, if necessary,
// to ensure that it can hold at least the number of elements specified by the capacity argument.
func (mb *MapBuilder) EnsureCapacity(capacity int) {
oldValues := mb.pairs
if capacity <= cap(oldValues) {
return
}
mb.pairs = make([]otlpcommon.KeyValue, len(oldValues), capacity)
copy(mb.pairs, oldValues)
}

func (mb *MapBuilder) getValue(i int) pcommon.Value {
return pcommon.Value(internal.NewValue(&mb.pairs[i].Value, &mb.state))
}

// AppendEmpty appends a key/value pair to the MapBuilder and return the inserted value.
// This method does not check for duplicate keys and has an amortized constant time complexity.
func (mb *MapBuilder) AppendEmpty(k string) pcommon.Value {
mb.pairs = append(mb.pairs, otlpcommon.KeyValue{Key: k})
return mb.getValue(len(mb.pairs) - 1)
}

// UnsafeIntoMap transfers the contents of a MapBuilder into a Map, without checking for duplicate keys.
// If the MapBuilder contains duplicate keys, the behavior of the resulting Map is unspecified.
// This operation has constant time complexity and makes no allocations.
// After this operation, the MapBuilder is reset to an empty state.
func (mb *MapBuilder) UnsafeIntoMap(m pcommon.Map) {
internal.GetMapState(internal.Map(m)).AssertMutable()
*internal.GetOrigMap(internal.Map(m)) = mb.pairs
mb.pairs = nil
}
29 changes: 29 additions & 0 deletions pdata/xpdata/map_builder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0

package xpdata_test

import (
"testing"

"github.com/stretchr/testify/assert"

"go.opentelemetry.io/collector/pdata/pcommon"
"go.opentelemetry.io/collector/pdata/xpdata"
)

func TestMapBuilder(t *testing.T) {
var mb xpdata.MapBuilder
mb.EnsureCapacity(3)
mb.AppendEmpty("key1").SetStr("val")
mb.AppendEmpty("key2").SetInt(42)

m := pcommon.NewMap()
mb.UnsafeIntoMap(m)

assert.Equal(t, 2, m.Len())
val, ok := m.Get("key1")
assert.True(t, ok && val.Type() == pcommon.ValueTypeStr && val.Str() == "val")
val, ok = m.Get("key2")
assert.True(t, ok && val.Type() == pcommon.ValueTypeInt && val.Int() == 42)
}