gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

encoder.go (4616B)


      1 // Copyright The OpenTelemetry Authors
      2 //
      3 // Licensed under the Apache License, Version 2.0 (the "License");
      4 // you may not use this file except in compliance with the License.
      5 // You may obtain a copy of the License at
      6 //
      7 //     http://www.apache.org/licenses/LICENSE-2.0
      8 //
      9 // Unless required by applicable law or agreed to in writing, software
     10 // distributed under the License is distributed on an "AS IS" BASIS,
     11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 // See the License for the specific language governing permissions and
     13 // limitations under the License.
     14 
     15 package attribute // import "go.opentelemetry.io/otel/attribute"
     16 
     17 import (
     18 	"bytes"
     19 	"sync"
     20 	"sync/atomic"
     21 )
     22 
     23 type (
     24 	// Encoder is a mechanism for serializing an attribute set into a specific
     25 	// string representation that supports caching, to avoid repeated
     26 	// serialization. An example could be an exporter encoding the attribute
     27 	// set into a wire representation.
     28 	Encoder interface {
     29 		// Encode returns the serialized encoding of the attribute set using
     30 		// its Iterator. This result may be cached by a attribute.Set.
     31 		Encode(iterator Iterator) string
     32 
     33 		// ID returns a value that is unique for each class of attribute
     34 		// encoder. Attribute encoders allocate these using `NewEncoderID`.
     35 		ID() EncoderID
     36 	}
     37 
     38 	// EncoderID is used to identify distinct Encoder
     39 	// implementations, for caching encoded results.
     40 	EncoderID struct {
     41 		value uint64
     42 	}
     43 
     44 	// defaultAttrEncoder uses a sync.Pool of buffers to reduce the number of
     45 	// allocations used in encoding attributes. This implementation encodes a
     46 	// comma-separated list of key=value, with '/'-escaping of '=', ',', and
     47 	// '\'.
     48 	defaultAttrEncoder struct {
     49 		// pool is a pool of attribute set builders. The buffers in this pool
     50 		// grow to a size that most attribute encodings will not allocate new
     51 		// memory.
     52 		pool sync.Pool // *bytes.Buffer
     53 	}
     54 )
     55 
     56 // escapeChar is used to ensure uniqueness of the attribute encoding where
     57 // keys or values contain either '=' or ','.  Since there is no parser needed
     58 // for this encoding and its only requirement is to be unique, this choice is
     59 // arbitrary.  Users will see these in some exporters (e.g., stdout), so the
     60 // backslash ('\') is used as a conventional choice.
     61 const escapeChar = '\\'
     62 
     63 var (
     64 	_ Encoder = &defaultAttrEncoder{}
     65 
     66 	// encoderIDCounter is for generating IDs for other attribute encoders.
     67 	encoderIDCounter uint64
     68 
     69 	defaultEncoderOnce     sync.Once
     70 	defaultEncoderID       = NewEncoderID()
     71 	defaultEncoderInstance *defaultAttrEncoder
     72 )
     73 
     74 // NewEncoderID returns a unique attribute encoder ID. It should be called
     75 // once per each type of attribute encoder. Preferably in init() or in var
     76 // definition.
     77 func NewEncoderID() EncoderID {
     78 	return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
     79 }
     80 
     81 // DefaultEncoder returns an attribute encoder that encodes attributes in such
     82 // a way that each escaped attribute's key is followed by an equal sign and
     83 // then by an escaped attribute's value. All key-value pairs are separated by
     84 // a comma.
     85 //
     86 // Escaping is done by prepending a backslash before either a backslash, equal
     87 // sign or a comma.
     88 func DefaultEncoder() Encoder {
     89 	defaultEncoderOnce.Do(func() {
     90 		defaultEncoderInstance = &defaultAttrEncoder{
     91 			pool: sync.Pool{
     92 				New: func() interface{} {
     93 					return &bytes.Buffer{}
     94 				},
     95 			},
     96 		}
     97 	})
     98 	return defaultEncoderInstance
     99 }
    100 
    101 // Encode is a part of an implementation of the AttributeEncoder interface.
    102 func (d *defaultAttrEncoder) Encode(iter Iterator) string {
    103 	buf := d.pool.Get().(*bytes.Buffer)
    104 	defer d.pool.Put(buf)
    105 	buf.Reset()
    106 
    107 	for iter.Next() {
    108 		i, keyValue := iter.IndexedAttribute()
    109 		if i > 0 {
    110 			_, _ = buf.WriteRune(',')
    111 		}
    112 		copyAndEscape(buf, string(keyValue.Key))
    113 
    114 		_, _ = buf.WriteRune('=')
    115 
    116 		if keyValue.Value.Type() == STRING {
    117 			copyAndEscape(buf, keyValue.Value.AsString())
    118 		} else {
    119 			_, _ = buf.WriteString(keyValue.Value.Emit())
    120 		}
    121 	}
    122 	return buf.String()
    123 }
    124 
    125 // ID is a part of an implementation of the AttributeEncoder interface.
    126 func (*defaultAttrEncoder) ID() EncoderID {
    127 	return defaultEncoderID
    128 }
    129 
    130 // copyAndEscape escapes `=`, `,` and its own escape character (`\`),
    131 // making the default encoding unique.
    132 func copyAndEscape(buf *bytes.Buffer, val string) {
    133 	for _, ch := range val {
    134 		switch ch {
    135 		case '=', ',', escapeChar:
    136 			_, _ = buf.WriteRune(escapeChar)
    137 		}
    138 		_, _ = buf.WriteRune(ch)
    139 	}
    140 }
    141 
    142 // Valid returns true if this encoder ID was allocated by
    143 // `NewEncoderID`.  Invalid encoder IDs will not be cached.
    144 func (id EncoderID) Valid() bool {
    145 	return id.value != 0
    146 }