mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-30 22:42:24 -05:00 
			
		
		
		
	
		
			
	
	
		
			454 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			454 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // Copyright The OpenTelemetry Authors | ||
|  | // SPDX-License-Identifier: Apache-2.0 | ||
|  | 
 | ||
|  | package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"bytes" | ||
|  | 	"cmp" | ||
|  | 	"encoding/base64" | ||
|  | 	"encoding/json" | ||
|  | 	"errors" | ||
|  | 	"fmt" | ||
|  | 	"io" | ||
|  | 	"math" | ||
|  | 	"slices" | ||
|  | 	"strconv" | ||
|  | 	"unsafe" | ||
|  | ) | ||
|  | 
 | ||
|  | // A Value represents a structured value. | ||
|  | // A zero value is valid and represents an empty value. | ||
|  | type Value struct { | ||
|  | 	// Ensure forward compatibility by explicitly making this not comparable. | ||
|  | 	noCmp [0]func() //nolint: unused  // This is indeed used. | ||
|  | 
 | ||
|  | 	// num holds the value for Int64, Float64, and Bool. It holds the length | ||
|  | 	// for String, Bytes, Slice, Map. | ||
|  | 	num uint64 | ||
|  | 	// any holds either the KindBool, KindInt64, KindFloat64, stringptr, | ||
|  | 	// bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64 | ||
|  | 	// then the value of Value is in num as described above. Otherwise, it | ||
|  | 	// contains the value wrapped in the appropriate type. | ||
|  | 	any any | ||
|  | } | ||
|  | 
 | ||
|  | type ( | ||
|  | 	// sliceptr represents a value in Value.any for KindString Values. | ||
|  | 	stringptr *byte | ||
|  | 	// bytesptr represents a value in Value.any for KindBytes Values. | ||
|  | 	bytesptr *byte | ||
|  | 	// sliceptr represents a value in Value.any for KindSlice Values. | ||
|  | 	sliceptr *Value | ||
|  | 	// mapptr represents a value in Value.any for KindMap Values. | ||
|  | 	mapptr *Attr | ||
|  | ) | ||
|  | 
 | ||
|  | // ValueKind is the kind of a [Value]. | ||
|  | type ValueKind int | ||
|  | 
 | ||
|  | // ValueKind values. | ||
|  | const ( | ||
|  | 	ValueKindEmpty ValueKind = iota | ||
|  | 	ValueKindBool | ||
|  | 	ValueKindFloat64 | ||
|  | 	ValueKindInt64 | ||
|  | 	ValueKindString | ||
|  | 	ValueKindBytes | ||
|  | 	ValueKindSlice | ||
|  | 	ValueKindMap | ||
|  | ) | ||
|  | 
 | ||
|  | var valueKindStrings = []string{ | ||
|  | 	"Empty", | ||
|  | 	"Bool", | ||
|  | 	"Float64", | ||
|  | 	"Int64", | ||
|  | 	"String", | ||
|  | 	"Bytes", | ||
|  | 	"Slice", | ||
|  | 	"Map", | ||
|  | } | ||
|  | 
 | ||
|  | func (k ValueKind) String() string { | ||
|  | 	if k >= 0 && int(k) < len(valueKindStrings) { | ||
|  | 		return valueKindStrings[k] | ||
|  | 	} | ||
|  | 	return "<unknown telemetry.ValueKind>" | ||
|  | } | ||
|  | 
 | ||
|  | // StringValue returns a new [Value] for a string. | ||
|  | func StringValue(v string) Value { | ||
|  | 	return Value{ | ||
|  | 		num: uint64(len(v)), | ||
|  | 		any: stringptr(unsafe.StringData(v)), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // IntValue returns a [Value] for an int. | ||
|  | func IntValue(v int) Value { return Int64Value(int64(v)) } | ||
|  | 
 | ||
|  | // Int64Value returns a [Value] for an int64. | ||
|  | func Int64Value(v int64) Value { | ||
|  | 	return Value{ | ||
|  | 		num: uint64(v), // nolint: gosec  // Store raw bytes. | ||
|  | 		any: ValueKindInt64, | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Float64Value returns a [Value] for a float64. | ||
|  | func Float64Value(v float64) Value { | ||
|  | 	return Value{num: math.Float64bits(v), any: ValueKindFloat64} | ||
|  | } | ||
|  | 
 | ||
|  | // BoolValue returns a [Value] for a bool. | ||
|  | func BoolValue(v bool) Value { //nolint:revive // Not a control flag. | ||
|  | 	var n uint64 | ||
|  | 	if v { | ||
|  | 		n = 1 | ||
|  | 	} | ||
|  | 	return Value{num: n, any: ValueKindBool} | ||
|  | } | ||
|  | 
 | ||
|  | // BytesValue returns a [Value] for a byte slice. The passed slice must not be | ||
|  | // changed after it is passed. | ||
|  | func BytesValue(v []byte) Value { | ||
|  | 	return Value{ | ||
|  | 		num: uint64(len(v)), | ||
|  | 		any: bytesptr(unsafe.SliceData(v)), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // SliceValue returns a [Value] for a slice of [Value]. The passed slice must | ||
|  | // not be changed after it is passed. | ||
|  | func SliceValue(vs ...Value) Value { | ||
|  | 	return Value{ | ||
|  | 		num: uint64(len(vs)), | ||
|  | 		any: sliceptr(unsafe.SliceData(vs)), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // MapValue returns a new [Value] for a slice of key-value pairs. The passed | ||
|  | // slice must not be changed after it is passed. | ||
|  | func MapValue(kvs ...Attr) Value { | ||
|  | 	return Value{ | ||
|  | 		num: uint64(len(kvs)), | ||
|  | 		any: mapptr(unsafe.SliceData(kvs)), | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // AsString returns the value held by v as a string. | ||
|  | func (v Value) AsString() string { | ||
|  | 	if sp, ok := v.any.(stringptr); ok { | ||
|  | 		return unsafe.String(sp, v.num) | ||
|  | 	} | ||
|  | 	// TODO: error handle | ||
|  | 	return "" | ||
|  | } | ||
|  | 
 | ||
|  | // asString returns the value held by v as a string. It will panic if the Value | ||
|  | // is not KindString. | ||
|  | func (v Value) asString() string { | ||
|  | 	return unsafe.String(v.any.(stringptr), v.num) | ||
|  | } | ||
|  | 
 | ||
|  | // AsInt64 returns the value held by v as an int64. | ||
|  | func (v Value) AsInt64() int64 { | ||
|  | 	if v.Kind() != ValueKindInt64 { | ||
|  | 		// TODO: error handle | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 	return v.asInt64() | ||
|  | } | ||
|  | 
 | ||
|  | // asInt64 returns the value held by v as an int64. If v is not of KindInt64, | ||
|  | // this will return garbage. | ||
|  | func (v Value) asInt64() int64 { | ||
|  | 	// Assumes v.num was a valid int64 (overflow not checked). | ||
|  | 	return int64(v.num) // nolint: gosec | ||
|  | } | ||
|  | 
 | ||
|  | // AsBool returns the value held by v as a bool. | ||
|  | func (v Value) AsBool() bool { | ||
|  | 	if v.Kind() != ValueKindBool { | ||
|  | 		// TODO: error handle | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	return v.asBool() | ||
|  | } | ||
|  | 
 | ||
|  | // asBool returns the value held by v as a bool. If v is not of KindBool, this | ||
|  | // will return garbage. | ||
|  | func (v Value) asBool() bool { return v.num == 1 } | ||
|  | 
 | ||
|  | // AsFloat64 returns the value held by v as a float64. | ||
|  | func (v Value) AsFloat64() float64 { | ||
|  | 	if v.Kind() != ValueKindFloat64 { | ||
|  | 		// TODO: error handle | ||
|  | 		return 0 | ||
|  | 	} | ||
|  | 	return v.asFloat64() | ||
|  | } | ||
|  | 
 | ||
|  | // asFloat64 returns the value held by v as a float64. If v is not of | ||
|  | // KindFloat64, this will return garbage. | ||
|  | func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) } | ||
|  | 
 | ||
|  | // AsBytes returns the value held by v as a []byte. | ||
|  | func (v Value) AsBytes() []byte { | ||
|  | 	if sp, ok := v.any.(bytesptr); ok { | ||
|  | 		return unsafe.Slice((*byte)(sp), v.num) | ||
|  | 	} | ||
|  | 	// TODO: error handle | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // asBytes returns the value held by v as a []byte. It will panic if the Value | ||
|  | // is not KindBytes. | ||
|  | func (v Value) asBytes() []byte { | ||
|  | 	return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num) | ||
|  | } | ||
|  | 
 | ||
|  | // AsSlice returns the value held by v as a []Value. | ||
|  | func (v Value) AsSlice() []Value { | ||
|  | 	if sp, ok := v.any.(sliceptr); ok { | ||
|  | 		return unsafe.Slice((*Value)(sp), v.num) | ||
|  | 	} | ||
|  | 	// TODO: error handle | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // asSlice returns the value held by v as a []Value. It will panic if the Value | ||
|  | // is not KindSlice. | ||
|  | func (v Value) asSlice() []Value { | ||
|  | 	return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num) | ||
|  | } | ||
|  | 
 | ||
|  | // AsMap returns the value held by v as a []Attr. | ||
|  | func (v Value) AsMap() []Attr { | ||
|  | 	if sp, ok := v.any.(mapptr); ok { | ||
|  | 		return unsafe.Slice((*Attr)(sp), v.num) | ||
|  | 	} | ||
|  | 	// TODO: error handle | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // asMap returns the value held by v as a []Attr. It will panic if the | ||
|  | // Value is not KindMap. | ||
|  | func (v Value) asMap() []Attr { | ||
|  | 	return unsafe.Slice((*Attr)(v.any.(mapptr)), v.num) | ||
|  | } | ||
|  | 
 | ||
|  | // Kind returns the Kind of v. | ||
|  | func (v Value) Kind() ValueKind { | ||
|  | 	switch x := v.any.(type) { | ||
|  | 	case ValueKind: | ||
|  | 		return x | ||
|  | 	case stringptr: | ||
|  | 		return ValueKindString | ||
|  | 	case bytesptr: | ||
|  | 		return ValueKindBytes | ||
|  | 	case sliceptr: | ||
|  | 		return ValueKindSlice | ||
|  | 	case mapptr: | ||
|  | 		return ValueKindMap | ||
|  | 	default: | ||
|  | 		return ValueKindEmpty | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // Empty returns if v does not hold any value. | ||
|  | func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty } | ||
|  | 
 | ||
|  | // Equal returns if v is equal to w. | ||
|  | func (v Value) Equal(w Value) bool { | ||
|  | 	k1 := v.Kind() | ||
|  | 	k2 := w.Kind() | ||
|  | 	if k1 != k2 { | ||
|  | 		return false | ||
|  | 	} | ||
|  | 	switch k1 { | ||
|  | 	case ValueKindInt64, ValueKindBool: | ||
|  | 		return v.num == w.num | ||
|  | 	case ValueKindString: | ||
|  | 		return v.asString() == w.asString() | ||
|  | 	case ValueKindFloat64: | ||
|  | 		return v.asFloat64() == w.asFloat64() | ||
|  | 	case ValueKindSlice: | ||
|  | 		return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal) | ||
|  | 	case ValueKindMap: | ||
|  | 		sv := sortMap(v.asMap()) | ||
|  | 		sw := sortMap(w.asMap()) | ||
|  | 		return slices.EqualFunc(sv, sw, Attr.Equal) | ||
|  | 	case ValueKindBytes: | ||
|  | 		return bytes.Equal(v.asBytes(), w.asBytes()) | ||
|  | 	case ValueKindEmpty: | ||
|  | 		return true | ||
|  | 	default: | ||
|  | 		// TODO: error handle | ||
|  | 		return false | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func sortMap(m []Attr) []Attr { | ||
|  | 	sm := make([]Attr, len(m)) | ||
|  | 	copy(sm, m) | ||
|  | 	slices.SortFunc(sm, func(a, b Attr) int { | ||
|  | 		return cmp.Compare(a.Key, b.Key) | ||
|  | 	}) | ||
|  | 
 | ||
|  | 	return sm | ||
|  | } | ||
|  | 
 | ||
|  | // String returns Value's value as a string, formatted like [fmt.Sprint]. | ||
|  | // | ||
|  | // The returned string is meant for debugging; | ||
|  | // the string representation is not stable. | ||
|  | func (v Value) String() string { | ||
|  | 	switch v.Kind() { | ||
|  | 	case ValueKindString: | ||
|  | 		return v.asString() | ||
|  | 	case ValueKindInt64: | ||
|  | 		// Assumes v.num was a valid int64 (overflow not checked). | ||
|  | 		return strconv.FormatInt(int64(v.num), 10) // nolint: gosec | ||
|  | 	case ValueKindFloat64: | ||
|  | 		return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64) | ||
|  | 	case ValueKindBool: | ||
|  | 		return strconv.FormatBool(v.asBool()) | ||
|  | 	case ValueKindBytes: | ||
|  | 		return fmt.Sprint(v.asBytes()) | ||
|  | 	case ValueKindMap: | ||
|  | 		return fmt.Sprint(v.asMap()) | ||
|  | 	case ValueKindSlice: | ||
|  | 		return fmt.Sprint(v.asSlice()) | ||
|  | 	case ValueKindEmpty: | ||
|  | 		return "<nil>" | ||
|  | 	default: | ||
|  | 		// Try to handle this as gracefully as possible. | ||
|  | 		// | ||
|  | 		// Don't panic here. The goal here is to have developers find this | ||
|  | 		// first if a slog.Kind is is not handled. It is | ||
|  | 		// preferable to have user's open issue asking why their attributes | ||
|  | 		// have a "unhandled: " prefix than say that their code is panicking. | ||
|  | 		return fmt.Sprintf("<unhandled telemetry.ValueKind: %s>", v.Kind()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // MarshalJSON encodes v into OTLP formatted JSON. | ||
|  | func (v *Value) MarshalJSON() ([]byte, error) { | ||
|  | 	switch v.Kind() { | ||
|  | 	case ValueKindString: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value string `json:"stringValue"` | ||
|  | 		}{v.asString()}) | ||
|  | 	case ValueKindInt64: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value string `json:"intValue"` | ||
|  | 		}{strconv.FormatInt(int64(v.num), 10)}) // nolint: gosec  // From raw bytes. | ||
|  | 	case ValueKindFloat64: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value float64 `json:"doubleValue"` | ||
|  | 		}{v.asFloat64()}) | ||
|  | 	case ValueKindBool: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value bool `json:"boolValue"` | ||
|  | 		}{v.asBool()}) | ||
|  | 	case ValueKindBytes: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value []byte `json:"bytesValue"` | ||
|  | 		}{v.asBytes()}) | ||
|  | 	case ValueKindMap: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value struct { | ||
|  | 				Values []Attr `json:"values"` | ||
|  | 			} `json:"kvlistValue"` | ||
|  | 		}{struct { | ||
|  | 			Values []Attr `json:"values"` | ||
|  | 		}{v.asMap()}}) | ||
|  | 	case ValueKindSlice: | ||
|  | 		return json.Marshal(struct { | ||
|  | 			Value struct { | ||
|  | 				Values []Value `json:"values"` | ||
|  | 			} `json:"arrayValue"` | ||
|  | 		}{struct { | ||
|  | 			Values []Value `json:"values"` | ||
|  | 		}{v.asSlice()}}) | ||
|  | 	case ValueKindEmpty: | ||
|  | 		return nil, nil | ||
|  | 	default: | ||
|  | 		return nil, fmt.Errorf("unknown Value kind: %s", v.Kind().String()) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | // UnmarshalJSON decodes the OTLP formatted JSON contained in data into v. | ||
|  | func (v *Value) UnmarshalJSON(data []byte) error { | ||
|  | 	decoder := json.NewDecoder(bytes.NewReader(data)) | ||
|  | 
 | ||
|  | 	t, err := decoder.Token() | ||
|  | 	if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 	if t != json.Delim('{') { | ||
|  | 		return errors.New("invalid Value type") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	for decoder.More() { | ||
|  | 		keyIface, err := decoder.Token() | ||
|  | 		if err != nil { | ||
|  | 			if errors.Is(err, io.EOF) { | ||
|  | 				// Empty. | ||
|  | 				return nil | ||
|  | 			} | ||
|  | 			return err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		key, ok := keyIface.(string) | ||
|  | 		if !ok { | ||
|  | 			return fmt.Errorf("invalid Value key: %#v", keyIface) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		switch key { | ||
|  | 		case "stringValue", "string_value": | ||
|  | 			var val string | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = StringValue(val) | ||
|  | 		case "boolValue", "bool_value": | ||
|  | 			var val bool | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = BoolValue(val) | ||
|  | 		case "intValue", "int_value": | ||
|  | 			var val protoInt64 | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = Int64Value(val.Int64()) | ||
|  | 		case "doubleValue", "double_value": | ||
|  | 			var val float64 | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = Float64Value(val) | ||
|  | 		case "bytesValue", "bytes_value": | ||
|  | 			var val64 string | ||
|  | 			if err := decoder.Decode(&val64); err != nil { | ||
|  | 				return err | ||
|  | 			} | ||
|  | 			var val []byte | ||
|  | 			val, err = base64.StdEncoding.DecodeString(val64) | ||
|  | 			*v = BytesValue(val) | ||
|  | 		case "arrayValue", "array_value": | ||
|  | 			var val struct{ Values []Value } | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = SliceValue(val.Values...) | ||
|  | 		case "kvlistValue", "kvlist_value": | ||
|  | 			var val struct{ Values []Attr } | ||
|  | 			err = decoder.Decode(&val) | ||
|  | 			*v = MapValue(val.Values...) | ||
|  | 		default: | ||
|  | 			// Skip unknown. | ||
|  | 			continue | ||
|  | 		} | ||
|  | 		// Use first valid. Ignore the rest. | ||
|  | 		return err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Only unknown fields. Return nil without unmarshaling any value. | ||
|  | 	return nil | ||
|  | } |