Skip to content

Commit

Permalink
Use strings.Cut() instead of string.SplitN()
Browse files Browse the repository at this point in the history
strings.Cut() generates less garbage as it does not allocate the slice to hold parts.
  • Loading branch information
ash2k committed May 3, 2023
1 parent 17903bc commit 3e80b34
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 68 deletions.
56 changes: 24 additions & 32 deletions baggage/baggage.go
Expand Up @@ -289,45 +289,37 @@ func parseMember(member string) (Member, error) {
props properties
)

parts := strings.SplitN(member, propertyDelimiter, 2)
switch len(parts) {
case 2:
part0, part1, found := strings.Cut(member, propertyDelimiter)
if found {
// Parse the member properties.
for _, pStr := range strings.Split(parts[1], propertyDelimiter) {
for _, pStr := range strings.Split(part1, propertyDelimiter) {
p, err := parseProperty(pStr)
if err != nil {
return newInvalidMember(), err
}
props = append(props, p)
}
fallthrough
case 1:
// Parse the member key/value pair.

// Take into account a value can contain equal signs (=).
kv := strings.SplitN(parts[0], keyValueDelimiter, 2)
if len(kv) != 2 {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
}
// "Leading and trailing whitespaces are allowed but MUST be trimmed
// when converting the header into a data structure."
key = strings.TrimSpace(kv[0])
var err error
value, err = url.QueryUnescape(strings.TrimSpace(kv[1]))
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
}
if !keyRe.MatchString(key) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
if !valueRe.MatchString(value) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}
default:
// This should never happen unless a developer has changed the string
// splitting somehow. Panic instead of failing silently and allowing
// the bug to slip past the CI checks.
panic("failed to parse baggage member")
}
// Parse the member key/value pair.

// Take into account a value can contain equal signs (=).
k, v, found := strings.Cut(part0, keyValueDelimiter)
if !found {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidMember, member)
}
// "Leading and trailing whitespaces are allowed but MUST be trimmed
// when converting the header into a data structure."
key = strings.TrimSpace(k)
var err error
value, err = url.QueryUnescape(strings.TrimSpace(v))
if err != nil {
return newInvalidMember(), fmt.Errorf("%w: %q", err, value)
}

Check warning on line 317 in baggage/baggage.go

View check run for this annotation

Codecov / codecov/patch

baggage/baggage.go#L316-L317

Added lines #L316 - L317 were not covered by tests
if !keyRe.MatchString(key) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidKey, key)
}
if !valueRe.MatchString(value) {
return newInvalidMember(), fmt.Errorf("%w: %q", errInvalidValue, value)
}

return Member{key: key, value: value, properties: props, hasData: true}, nil
Expand Down
Expand Up @@ -160,15 +160,15 @@ func (t *TMultiplexedProcessor) ProcessorMap() map[string]TProcessorFunction {
// the given ProcessorName or if all that is given is the FunctionName and there
// is no DefaultProcessor set.
func (t *TMultiplexedProcessor) AddToProcessorMap(name string, processorFunc TProcessorFunction) {
components := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
if len(components) != 2 {
if t.DefaultProcessor != nil && len(components) == 1 {
t.DefaultProcessor.AddToProcessorMap(components[0], processorFunc)
component0, component1, found := strings.Cut(name, MULTIPLEXED_SEPARATOR)
if !found {
if t.DefaultProcessor != nil {
t.DefaultProcessor.AddToProcessorMap(component0, processorFunc)
}
return
}
processorName := components[0]
funcName := components[1]
processorName := component0
funcName := component1
if processor, ok := t.serviceProcessorMap[processorName]; ok {
processor.AddToProcessorMap(funcName, processorFunc)
}
Expand Down Expand Up @@ -197,9 +197,9 @@ func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol)
if typeId != CALL && typeId != ONEWAY {
return false, NewTProtocolException(fmt.Errorf("Unexpected message type %v", typeId))
}
//extract the service name
v := strings.SplitN(name, MULTIPLEXED_SEPARATOR, 2)
if len(v) != 2 {
// extract the service name
v0, v1, found := strings.Cut(name, MULTIPLEXED_SEPARATOR)
if !found {
if t.DefaultProcessor != nil {
smb := NewStoredMessageProtocol(in, name, typeId, seqid)
return t.DefaultProcessor.Process(ctx, smb, out)
Expand All @@ -209,18 +209,18 @@ func (t *TMultiplexedProcessor) Process(ctx context.Context, in, out TProtocol)
name,
))
}
actualProcessor, ok := t.serviceProcessorMap[v[0]]
actualProcessor, ok := t.serviceProcessorMap[v0]
if !ok {
return false, NewTProtocolException(fmt.Errorf(
"Service name not found: %s. Did you forget to call registerProcessor()?",
v[0],
v0,
))
}
smb := NewStoredMessageProtocol(in, v[1], typeId, seqid)
smb := NewStoredMessageProtocol(in, v1, typeId, seqid)
return actualProcessor.Process(ctx, smb, out)
}

//Protocol that use stored message for ReadMessageBegin
// Protocol that use stored message for ReadMessageBegin
type storedMessageProtocol struct {
TProtocol
name string
Expand Down
14 changes: 7 additions & 7 deletions exporters/otlp/internal/envconfig/envconfig.go
Expand Up @@ -166,20 +166,20 @@ func stringToHeader(value string) map[string]string {
headers := make(map[string]string)

for _, header := range headersPairs {
nameValue := strings.SplitN(header, "=", 2)
if len(nameValue) < 2 {
global.Error(errors.New("missing '="), "parse headers", "input", nameValue)
n, v, found := strings.Cut(header, "=")
if !found {
global.Error(errors.New("missing '="), "parse headers", "input", header)
continue
}
name, err := url.QueryUnescape(nameValue[0])
name, err := url.QueryUnescape(n)
if err != nil {
global.Error(err, "escape header key", "key", nameValue[0])
global.Error(err, "escape header key", "key", n)
continue
}
trimmedName := strings.TrimSpace(name)
value, err := url.QueryUnescape(nameValue[1])
value, err := url.QueryUnescape(v)
if err != nil {
global.Error(err, "escape header value", "value", nameValue[1])
global.Error(err, "escape header value", "value", v)
continue
}
trimmedValue := strings.TrimSpace(value)
Expand Down
6 changes: 3 additions & 3 deletions internal/internaltest/text_map_propagator.go
Expand Up @@ -35,9 +35,9 @@ func newState(encoded string) state {
if encoded == "" {
return state{}
}
split := strings.SplitN(encoded, ",", 2)
injects, _ := strconv.ParseUint(split[0], 10, 64)
extracts, _ := strconv.ParseUint(split[1], 10, 64)
s0, s1, _ := strings.Cut(encoded, ",")
injects, _ := strconv.ParseUint(s0, 10, 64)
extracts, _ := strconv.ParseUint(s1, 10, 64)
return state{
Injections: injects,
Extractions: extracts,
Expand Down
12 changes: 6 additions & 6 deletions sdk/resource/env.go
Expand Up @@ -82,20 +82,20 @@ func constructOTResources(s string) (*Resource, error) {
return Empty(), nil
}
pairs := strings.Split(s, ",")
attrs := []attribute.KeyValue{}
var attrs []attribute.KeyValue
var invalid []string
for _, p := range pairs {
field := strings.SplitN(p, "=", 2)
if len(field) != 2 {
f0, f1, found := strings.Cut(p, "=")
if !found {
invalid = append(invalid, p)
continue
}
k := strings.TrimSpace(field[0])
v, err := url.QueryUnescape(strings.TrimSpace(field[1]))
k := strings.TrimSpace(f0)
v, err := url.QueryUnescape(strings.TrimSpace(f1))
if err != nil {
// Retain original value if decoding fails, otherwise it will be
// an empty string.
v = field[1]
v = f1
otel.Handle(err)
}
attrs = append(attrs, attribute.String(k, v))
Expand Down
8 changes: 4 additions & 4 deletions sdk/resource/os_release_unix.go
Expand Up @@ -85,14 +85,14 @@ func skip(line string) bool {
// parse attempts to split the provided line on the first '=' character, and then
// sanitize each side of the split before returning them as a key-value pair.
func parse(line string) (string, string, bool) {
parts := strings.SplitN(line, "=", 2)
p0, p1, found := strings.Cut(line, "=")

if len(parts) != 2 || len(parts[0]) == 0 {
if !found || len(p0) == 0 {
return "", "", false
}

key := strings.TrimSpace(parts[0])
value := unescape(unquote(strings.TrimSpace(parts[1])))
key := strings.TrimSpace(p0)
value := unescape(unquote(strings.TrimSpace(p1)))

return key, value, true
}
Expand Down
8 changes: 5 additions & 3 deletions semconv/internal/http.go
Expand Up @@ -232,10 +232,12 @@ func (sc *SemanticConventions) HTTPServerAttributesFromHTTPRequest(serverName, r
if route != "" {
attrs = append(attrs, sc.HTTPRouteKey.String(route))
}
if values, ok := request.Header["X-Forwarded-For"]; ok && len(values) > 0 {
if addresses := strings.SplitN(values[0], ",", 2); len(addresses) > 0 {
attrs = append(attrs, sc.HTTPClientIPKey.String(addresses[0]))
if values := request.Header["X-Forwarded-For"]; len(values) > 0 {
addr := values[0]
if i := strings.Index(addr, ","); i > 0 {
addr = addr[:i]
}
attrs = append(attrs, sc.HTTPClientIPKey.String(addr))
}

return append(attrs, sc.httpCommonAttributesFromHTTPRequest(request)...)
Expand Down

0 comments on commit 3e80b34

Please sign in to comment.