240 lines
5.6 KiB
Go
240 lines
5.6 KiB
Go
|
// Copyright 2017, OpenCensus Authors
|
||
|
//
|
||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
// you may not use this file except in compliance with the License.
|
||
|
// You may obtain a copy of the License at
|
||
|
//
|
||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||
|
//
|
||
|
// Unless required by applicable law or agreed to in writing, software
|
||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
// See the License for the specific language governing permissions and
|
||
|
// limitations under the License.
|
||
|
//
|
||
|
|
||
|
package tag
|
||
|
|
||
|
import (
|
||
|
"encoding/binary"
|
||
|
"fmt"
|
||
|
)
|
||
|
|
||
|
// KeyType defines the types of keys allowed. Currently only keyTypeString is
|
||
|
// supported.
|
||
|
type keyType byte
|
||
|
|
||
|
const (
|
||
|
keyTypeString keyType = iota
|
||
|
keyTypeInt64
|
||
|
keyTypeTrue
|
||
|
keyTypeFalse
|
||
|
|
||
|
tagsVersionID = byte(0)
|
||
|
)
|
||
|
|
||
|
type encoderGRPC struct {
|
||
|
buf []byte
|
||
|
writeIdx, readIdx int
|
||
|
}
|
||
|
|
||
|
// writeKeyString writes the fieldID '0' followed by the key string and value
|
||
|
// string.
|
||
|
func (eg *encoderGRPC) writeTagString(k, v string) {
|
||
|
eg.writeByte(byte(keyTypeString))
|
||
|
eg.writeStringWithVarintLen(k)
|
||
|
eg.writeStringWithVarintLen(v)
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeTagUint64(k string, i uint64) {
|
||
|
eg.writeByte(byte(keyTypeInt64))
|
||
|
eg.writeStringWithVarintLen(k)
|
||
|
eg.writeUint64(i)
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeTagTrue(k string) {
|
||
|
eg.writeByte(byte(keyTypeTrue))
|
||
|
eg.writeStringWithVarintLen(k)
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeTagFalse(k string) {
|
||
|
eg.writeByte(byte(keyTypeFalse))
|
||
|
eg.writeStringWithVarintLen(k)
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeBytesWithVarintLen(bytes []byte) {
|
||
|
length := len(bytes)
|
||
|
|
||
|
eg.growIfRequired(binary.MaxVarintLen64 + length)
|
||
|
eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
|
||
|
copy(eg.buf[eg.writeIdx:], bytes)
|
||
|
eg.writeIdx += length
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeStringWithVarintLen(s string) {
|
||
|
length := len(s)
|
||
|
|
||
|
eg.growIfRequired(binary.MaxVarintLen64 + length)
|
||
|
eg.writeIdx += binary.PutUvarint(eg.buf[eg.writeIdx:], uint64(length))
|
||
|
copy(eg.buf[eg.writeIdx:], s)
|
||
|
eg.writeIdx += length
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeByte(v byte) {
|
||
|
eg.growIfRequired(1)
|
||
|
eg.buf[eg.writeIdx] = v
|
||
|
eg.writeIdx++
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeUint32(i uint32) {
|
||
|
eg.growIfRequired(4)
|
||
|
binary.LittleEndian.PutUint32(eg.buf[eg.writeIdx:], i)
|
||
|
eg.writeIdx += 4
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) writeUint64(i uint64) {
|
||
|
eg.growIfRequired(8)
|
||
|
binary.LittleEndian.PutUint64(eg.buf[eg.writeIdx:], i)
|
||
|
eg.writeIdx += 8
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readByte() byte {
|
||
|
b := eg.buf[eg.readIdx]
|
||
|
eg.readIdx++
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readUint32() uint32 {
|
||
|
i := binary.LittleEndian.Uint32(eg.buf[eg.readIdx:])
|
||
|
eg.readIdx += 4
|
||
|
return i
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readUint64() uint64 {
|
||
|
i := binary.LittleEndian.Uint64(eg.buf[eg.readIdx:])
|
||
|
eg.readIdx += 8
|
||
|
return i
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readBytesWithVarintLen() ([]byte, error) {
|
||
|
if eg.readEnded() {
|
||
|
return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
|
||
|
}
|
||
|
length, valueStart := binary.Uvarint(eg.buf[eg.readIdx:])
|
||
|
if valueStart <= 0 {
|
||
|
return nil, fmt.Errorf("unexpected end while readBytesWithVarintLen '%x' starting at idx '%v'", eg.buf, eg.readIdx)
|
||
|
}
|
||
|
|
||
|
valueStart += eg.readIdx
|
||
|
valueEnd := valueStart + int(length)
|
||
|
if valueEnd > len(eg.buf) {
|
||
|
return nil, fmt.Errorf("malformed encoding: length:%v, upper:%v, maxLength:%v", length, valueEnd, len(eg.buf))
|
||
|
}
|
||
|
|
||
|
eg.readIdx = valueEnd
|
||
|
return eg.buf[valueStart:valueEnd], nil
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readStringWithVarintLen() (string, error) {
|
||
|
bytes, err := eg.readBytesWithVarintLen()
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
return string(bytes), nil
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) growIfRequired(expected int) {
|
||
|
if len(eg.buf)-eg.writeIdx < expected {
|
||
|
tmp := make([]byte, 2*(len(eg.buf)+1)+expected)
|
||
|
copy(tmp, eg.buf)
|
||
|
eg.buf = tmp
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) readEnded() bool {
|
||
|
return eg.readIdx >= len(eg.buf)
|
||
|
}
|
||
|
|
||
|
func (eg *encoderGRPC) bytes() []byte {
|
||
|
return eg.buf[:eg.writeIdx]
|
||
|
}
|
||
|
|
||
|
// Encode encodes the tag map into a []byte. It is useful to propagate
|
||
|
// the tag maps on wire in binary format.
|
||
|
func Encode(m *Map) []byte {
|
||
|
if m == nil {
|
||
|
return nil
|
||
|
}
|
||
|
eg := &encoderGRPC{
|
||
|
buf: make([]byte, len(m.m)),
|
||
|
}
|
||
|
eg.writeByte(byte(tagsVersionID))
|
||
|
for k, v := range m.m {
|
||
|
if v.m.ttl.ttl == valueTTLUnlimitedPropagation {
|
||
|
eg.writeByte(byte(keyTypeString))
|
||
|
eg.writeStringWithVarintLen(k.name)
|
||
|
eg.writeBytesWithVarintLen([]byte(v.value))
|
||
|
}
|
||
|
}
|
||
|
return eg.bytes()
|
||
|
}
|
||
|
|
||
|
// Decode decodes the given []byte into a tag map.
|
||
|
func Decode(bytes []byte) (*Map, error) {
|
||
|
ts := newMap()
|
||
|
err := DecodeEach(bytes, ts.upsert)
|
||
|
if err != nil {
|
||
|
// no partial failures
|
||
|
return nil, err
|
||
|
}
|
||
|
return ts, nil
|
||
|
}
|
||
|
|
||
|
// DecodeEach decodes the given serialized tag map, calling handler for each
|
||
|
// tag key and value decoded.
|
||
|
func DecodeEach(bytes []byte, fn func(key Key, val string, md metadatas)) error {
|
||
|
eg := &encoderGRPC{
|
||
|
buf: bytes,
|
||
|
}
|
||
|
if len(eg.buf) == 0 {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
version := eg.readByte()
|
||
|
if version > tagsVersionID {
|
||
|
return fmt.Errorf("cannot decode: unsupported version: %q; supports only up to: %q", version, tagsVersionID)
|
||
|
}
|
||
|
|
||
|
for !eg.readEnded() {
|
||
|
typ := keyType(eg.readByte())
|
||
|
|
||
|
if typ != keyTypeString {
|
||
|
return fmt.Errorf("cannot decode: invalid key type: %q", typ)
|
||
|
}
|
||
|
|
||
|
k, err := eg.readBytesWithVarintLen()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
v, err := eg.readBytesWithVarintLen()
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
key, err := NewKey(string(k))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
val := string(v)
|
||
|
if !checkValue(val) {
|
||
|
return errInvalidValue
|
||
|
}
|
||
|
fn(key, val, createMetadatas(WithTTL(TTLUnlimitedPropagation)))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
}
|
||
|
return nil
|
||
|
}
|