summary refs log tree commit diff
path: root/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go')
-rw-r--r--vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go654
1 files changed, 654 insertions, 0 deletions
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
new file mode 100644
index 0000000..5633e66
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
@@ -0,0 +1,654 @@
+package cbor
+
+// This file contains code to decode a stream of CBOR Data into JSON.
+
+import (
+	"bufio"
+	"bytes"
+	"encoding/base64"
+	"fmt"
+	"io"
+	"math"
+	"net"
+	"runtime"
+	"strconv"
+	"strings"
+	"time"
+	"unicode/utf8"
+)
+
+var decodeTimeZone *time.Location
+
+const hexTable = "0123456789abcdef"
+
+const isFloat32 = 4
+const isFloat64 = 8
+
+func readNBytes(src *bufio.Reader, n int) []byte {
+	ret := make([]byte, n)
+	for i := 0; i < n; i++ {
+		ch, e := src.ReadByte()
+		if e != nil {
+			panic(fmt.Errorf("Tried to Read %d Bytes.. But hit end of file", n))
+		}
+		ret[i] = ch
+	}
+	return ret
+}
+
+func readByte(src *bufio.Reader) byte {
+	b, e := src.ReadByte()
+	if e != nil {
+		panic(fmt.Errorf("Tried to Read 1 Byte.. But hit end of file"))
+	}
+	return b
+}
+
+func decodeIntAdditionalType(src *bufio.Reader, minor byte) int64 {
+	val := int64(0)
+	if minor <= 23 {
+		val = int64(minor)
+	} else {
+		bytesToRead := 0
+		switch minor {
+		case additionalTypeIntUint8:
+			bytesToRead = 1
+		case additionalTypeIntUint16:
+			bytesToRead = 2
+		case additionalTypeIntUint32:
+			bytesToRead = 4
+		case additionalTypeIntUint64:
+			bytesToRead = 8
+		default:
+			panic(fmt.Errorf("Invalid Additional Type: %d in decodeInteger (expected <28)", minor))
+		}
+		pb := readNBytes(src, bytesToRead)
+		for i := 0; i < bytesToRead; i++ {
+			val = val * 256
+			val += int64(pb[i])
+		}
+	}
+	return val
+}
+
+func decodeInteger(src *bufio.Reader) int64 {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeUnsignedInt && major != majorTypeNegativeInt {
+		panic(fmt.Errorf("Major type is: %d in decodeInteger!! (expected 0 or 1)", major))
+	}
+	val := decodeIntAdditionalType(src, minor)
+	if major == 0 {
+		return val
+	}
+	return (-1 - val)
+}
+
+func decodeFloat(src *bufio.Reader) (float64, int) {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeSimpleAndFloat {
+		panic(fmt.Errorf("Incorrect Major type is: %d in decodeFloat", major))
+	}
+
+	switch minor {
+	case additionalTypeFloat16:
+		panic(fmt.Errorf("float16 is not supported in decodeFloat"))
+
+	case additionalTypeFloat32:
+		pb := readNBytes(src, 4)
+		switch string(pb) {
+		case float32Nan:
+			return math.NaN(), isFloat32
+		case float32PosInfinity:
+			return math.Inf(0), isFloat32
+		case float32NegInfinity:
+			return math.Inf(-1), isFloat32
+		}
+		n := uint32(0)
+		for i := 0; i < 4; i++ {
+			n = n * 256
+			n += uint32(pb[i])
+		}
+		val := math.Float32frombits(n)
+		return float64(val), isFloat32
+	case additionalTypeFloat64:
+		pb := readNBytes(src, 8)
+		switch string(pb) {
+		case float64Nan:
+			return math.NaN(), isFloat64
+		case float64PosInfinity:
+			return math.Inf(0), isFloat64
+		case float64NegInfinity:
+			return math.Inf(-1), isFloat64
+		}
+		n := uint64(0)
+		for i := 0; i < 8; i++ {
+			n = n * 256
+			n += uint64(pb[i])
+		}
+		val := math.Float64frombits(n)
+		return val, isFloat64
+	}
+	panic(fmt.Errorf("Invalid Additional Type: %d in decodeFloat", minor))
+}
+
+func decodeStringComplex(dst []byte, s string, pos uint) []byte {
+	i := int(pos)
+	start := 0
+
+	for i < len(s) {
+		b := s[i]
+		if b >= utf8.RuneSelf {
+			r, size := utf8.DecodeRuneInString(s[i:])
+			if r == utf8.RuneError && size == 1 {
+				// In case of error, first append previous simple characters to
+				// the byte slice if any and append a replacement character code
+				// in place of the invalid sequence.
+				if start < i {
+					dst = append(dst, s[start:i]...)
+				}
+				dst = append(dst, `\ufffd`...)
+				i += size
+				start = i
+				continue
+			}
+			i += size
+			continue
+		}
+		if b >= 0x20 && b <= 0x7e && b != '\\' && b != '"' {
+			i++
+			continue
+		}
+		// We encountered a character that needs to be encoded.
+		// Let's append the previous simple characters to the byte slice
+		// and switch our operation to read and encode the remainder
+		// characters byte-by-byte.
+		if start < i {
+			dst = append(dst, s[start:i]...)
+		}
+		switch b {
+		case '"', '\\':
+			dst = append(dst, '\\', b)
+		case '\b':
+			dst = append(dst, '\\', 'b')
+		case '\f':
+			dst = append(dst, '\\', 'f')
+		case '\n':
+			dst = append(dst, '\\', 'n')
+		case '\r':
+			dst = append(dst, '\\', 'r')
+		case '\t':
+			dst = append(dst, '\\', 't')
+		default:
+			dst = append(dst, '\\', 'u', '0', '0', hexTable[b>>4], hexTable[b&0xF])
+		}
+		i++
+		start = i
+	}
+	if start < len(s) {
+		dst = append(dst, s[start:]...)
+	}
+	return dst
+}
+
+func decodeString(src *bufio.Reader, noQuotes bool) []byte {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeByteString {
+		panic(fmt.Errorf("Major type is: %d in decodeString", major))
+	}
+	result := []byte{}
+	if !noQuotes {
+		result = append(result, '"')
+	}
+	length := decodeIntAdditionalType(src, minor)
+	len := int(length)
+	pbs := readNBytes(src, len)
+	result = append(result, pbs...)
+	if noQuotes {
+		return result
+	}
+	return append(result, '"')
+}
+func decodeStringToDataUrl(src *bufio.Reader, mimeType string) []byte {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeByteString {
+		panic(fmt.Errorf("Major type is: %d in decodeString", major))
+	}
+	length := decodeIntAdditionalType(src, minor)
+	l := int(length)
+	enc := base64.StdEncoding
+	lEnc := enc.EncodedLen(l)
+	result := make([]byte, len("\"data:;base64,\"")+len(mimeType)+lEnc)
+	dest := result
+	u := copy(dest, "\"data:")
+	dest = dest[u:]
+	u = copy(dest, mimeType)
+	dest = dest[u:]
+	u = copy(dest, ";base64,")
+	dest = dest[u:]
+	pbs := readNBytes(src, l)
+	enc.Encode(dest, pbs)
+	dest = dest[lEnc:]
+	dest[0] = '"'
+	return result
+}
+
+func decodeUTF8String(src *bufio.Reader) []byte {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeUtf8String {
+		panic(fmt.Errorf("Major type is: %d in decodeUTF8String", major))
+	}
+	result := []byte{'"'}
+	length := decodeIntAdditionalType(src, minor)
+	len := int(length)
+	pbs := readNBytes(src, len)
+
+	for i := 0; i < len; i++ {
+		// Check if the character needs encoding. Control characters, slashes,
+		// and the double quote need json encoding. Bytes above the ascii
+		// boundary needs utf8 encoding.
+		if pbs[i] < 0x20 || pbs[i] > 0x7e || pbs[i] == '\\' || pbs[i] == '"' {
+			// We encountered a character that needs to be encoded. Switch
+			// to complex version of the algorithm.
+			dst := []byte{'"'}
+			dst = decodeStringComplex(dst, string(pbs), uint(i))
+			return append(dst, '"')
+		}
+	}
+	// The string has no need for encoding and therefore is directly
+	// appended to the byte slice.
+	result = append(result, pbs...)
+	return append(result, '"')
+}
+
+func array2Json(src *bufio.Reader, dst io.Writer) {
+	dst.Write([]byte{'['})
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeArray {
+		panic(fmt.Errorf("Major type is: %d in array2Json", major))
+	}
+	len := 0
+	unSpecifiedCount := false
+	if minor == additionalTypeInfiniteCount {
+		unSpecifiedCount = true
+	} else {
+		length := decodeIntAdditionalType(src, minor)
+		len = int(length)
+	}
+	for i := 0; unSpecifiedCount || i < len; i++ {
+		if unSpecifiedCount {
+			pb, e := src.Peek(1)
+			if e != nil {
+				panic(e)
+			}
+			if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+				readByte(src)
+				break
+			}
+		}
+		cbor2JsonOneObject(src, dst)
+		if unSpecifiedCount {
+			pb, e := src.Peek(1)
+			if e != nil {
+				panic(e)
+			}
+			if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+				readByte(src)
+				break
+			}
+			dst.Write([]byte{','})
+		} else if i+1 < len {
+			dst.Write([]byte{','})
+		}
+	}
+	dst.Write([]byte{']'})
+}
+
+func map2Json(src *bufio.Reader, dst io.Writer) {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeMap {
+		panic(fmt.Errorf("Major type is: %d in map2Json", major))
+	}
+	len := 0
+	unSpecifiedCount := false
+	if minor == additionalTypeInfiniteCount {
+		unSpecifiedCount = true
+	} else {
+		length := decodeIntAdditionalType(src, minor)
+		len = int(length)
+	}
+	dst.Write([]byte{'{'})
+	for i := 0; unSpecifiedCount || i < len; i++ {
+		if unSpecifiedCount {
+			pb, e := src.Peek(1)
+			if e != nil {
+				panic(e)
+			}
+			if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+				readByte(src)
+				break
+			}
+		}
+		cbor2JsonOneObject(src, dst)
+		if i%2 == 0 {
+			// Even position values are keys.
+			dst.Write([]byte{':'})
+		} else {
+			if unSpecifiedCount {
+				pb, e := src.Peek(1)
+				if e != nil {
+					panic(e)
+				}
+				if pb[0] == majorTypeSimpleAndFloat|additionalTypeBreak {
+					readByte(src)
+					break
+				}
+				dst.Write([]byte{','})
+			} else if i+1 < len {
+				dst.Write([]byte{','})
+			}
+		}
+	}
+	dst.Write([]byte{'}'})
+}
+
+func decodeTagData(src *bufio.Reader) []byte {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeTags {
+		panic(fmt.Errorf("Major type is: %d in decodeTagData", major))
+	}
+	switch minor {
+	case additionalTypeTimestamp:
+		return decodeTimeStamp(src)
+	case additionalTypeIntUint8:
+		val := decodeIntAdditionalType(src, minor)
+		switch byte(val) {
+		case additionalTypeEmbeddedCBOR:
+			pb := readByte(src)
+			dataMajor := pb & maskOutAdditionalType
+			if dataMajor != majorTypeByteString {
+				panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedCBOR", dataMajor))
+			}
+			src.UnreadByte()
+			return decodeStringToDataUrl(src, "application/cbor")
+		default:
+			panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
+		}
+
+	// Tag value is larger than 256 (so uint16).
+	case additionalTypeIntUint16:
+		val := decodeIntAdditionalType(src, minor)
+
+		switch uint16(val) {
+		case additionalTypeEmbeddedJSON:
+			pb := readByte(src)
+			dataMajor := pb & maskOutAdditionalType
+			if dataMajor != majorTypeByteString {
+				panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedJSON", dataMajor))
+			}
+			src.UnreadByte()
+			return decodeString(src, true)
+
+		case additionalTypeTagNetworkAddr:
+			octets := decodeString(src, true)
+			ss := []byte{'"'}
+			switch len(octets) {
+			case 6: // MAC address.
+				ha := net.HardwareAddr(octets)
+				ss = append(append(ss, ha.String()...), '"')
+			case 4: // IPv4 address.
+				fallthrough
+			case 16: // IPv6 address.
+				ip := net.IP(octets)
+				ss = append(append(ss, ip.String()...), '"')
+			default:
+				panic(fmt.Errorf("Unexpected Network Address length: %d (expected 4,6,16)", len(octets)))
+			}
+			return ss
+
+		case additionalTypeTagNetworkPrefix:
+			pb := readByte(src)
+			if pb != majorTypeMap|0x1 {
+				panic(fmt.Errorf("IP Prefix is NOT of MAP of 1 elements as expected"))
+			}
+			octets := decodeString(src, true)
+			val := decodeInteger(src)
+			ip := net.IP(octets)
+			var mask net.IPMask
+			pfxLen := int(val)
+			if len(octets) == 4 {
+				mask = net.CIDRMask(pfxLen, 32)
+			} else {
+				mask = net.CIDRMask(pfxLen, 128)
+			}
+			ipPfx := net.IPNet{IP: ip, Mask: mask}
+			ss := []byte{'"'}
+			ss = append(append(ss, ipPfx.String()...), '"')
+			return ss
+
+		case additionalTypeTagHexString:
+			octets := decodeString(src, true)
+			ss := []byte{'"'}
+			for _, v := range octets {
+				ss = append(ss, hexTable[v>>4], hexTable[v&0x0f])
+			}
+			return append(ss, '"')
+
+		default:
+			panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
+		}
+	}
+	panic(fmt.Errorf("Unsupported Additional Type: %d in decodeTagData", minor))
+}
+
+func decodeTimeStamp(src *bufio.Reader) []byte {
+	pb := readByte(src)
+	src.UnreadByte()
+	tsMajor := pb & maskOutAdditionalType
+	if tsMajor == majorTypeUnsignedInt || tsMajor == majorTypeNegativeInt {
+		n := decodeInteger(src)
+		t := time.Unix(n, 0)
+		if decodeTimeZone != nil {
+			t = t.In(decodeTimeZone)
+		} else {
+			t = t.In(time.UTC)
+		}
+		tsb := []byte{}
+		tsb = append(tsb, '"')
+		tsb = t.AppendFormat(tsb, IntegerTimeFieldFormat)
+		tsb = append(tsb, '"')
+		return tsb
+	} else if tsMajor == majorTypeSimpleAndFloat {
+		n, _ := decodeFloat(src)
+		secs := int64(n)
+		n -= float64(secs)
+		n *= float64(1e9)
+		t := time.Unix(secs, int64(n))
+		if decodeTimeZone != nil {
+			t = t.In(decodeTimeZone)
+		} else {
+			t = t.In(time.UTC)
+		}
+		tsb := []byte{}
+		tsb = append(tsb, '"')
+		tsb = t.AppendFormat(tsb, NanoTimeFieldFormat)
+		tsb = append(tsb, '"')
+		return tsb
+	}
+	panic(fmt.Errorf("TS format is neigther int nor float: %d", tsMajor))
+}
+
+func decodeSimpleFloat(src *bufio.Reader) []byte {
+	pb := readByte(src)
+	major := pb & maskOutAdditionalType
+	minor := pb & maskOutMajorType
+	if major != majorTypeSimpleAndFloat {
+		panic(fmt.Errorf("Major type is: %d in decodeSimpleFloat", major))
+	}
+	switch minor {
+	case additionalTypeBoolTrue:
+		return []byte("true")
+	case additionalTypeBoolFalse:
+		return []byte("false")
+	case additionalTypeNull:
+		return []byte("null")
+	case additionalTypeFloat16:
+		fallthrough
+	case additionalTypeFloat32:
+		fallthrough
+	case additionalTypeFloat64:
+		src.UnreadByte()
+		v, bc := decodeFloat(src)
+		ba := []byte{}
+		switch {
+		case math.IsNaN(v):
+			return []byte("\"NaN\"")
+		case math.IsInf(v, 1):
+			return []byte("\"+Inf\"")
+		case math.IsInf(v, -1):
+			return []byte("\"-Inf\"")
+		}
+		if bc == isFloat32 {
+			ba = strconv.AppendFloat(ba, v, 'f', -1, 32)
+		} else if bc == isFloat64 {
+			ba = strconv.AppendFloat(ba, v, 'f', -1, 64)
+		} else {
+			panic(fmt.Errorf("Invalid Float precision from decodeFloat: %d", bc))
+		}
+		return ba
+	default:
+		panic(fmt.Errorf("Invalid Additional Type: %d in decodeSimpleFloat", minor))
+	}
+}
+
+func cbor2JsonOneObject(src *bufio.Reader, dst io.Writer) {
+	pb, e := src.Peek(1)
+	if e != nil {
+		panic(e)
+	}
+	major := (pb[0] & maskOutAdditionalType)
+
+	switch major {
+	case majorTypeUnsignedInt:
+		fallthrough
+	case majorTypeNegativeInt:
+		n := decodeInteger(src)
+		dst.Write([]byte(strconv.Itoa(int(n))))
+
+	case majorTypeByteString:
+		s := decodeString(src, false)
+		dst.Write(s)
+
+	case majorTypeUtf8String:
+		s := decodeUTF8String(src)
+		dst.Write(s)
+
+	case majorTypeArray:
+		array2Json(src, dst)
+
+	case majorTypeMap:
+		map2Json(src, dst)
+
+	case majorTypeTags:
+		s := decodeTagData(src)
+		dst.Write(s)
+
+	case majorTypeSimpleAndFloat:
+		s := decodeSimpleFloat(src)
+		dst.Write(s)
+	}
+}
+
+func moreBytesToRead(src *bufio.Reader) bool {
+	_, e := src.ReadByte()
+	if e == nil {
+		src.UnreadByte()
+		return true
+	}
+	return false
+}
+
+// Cbor2JsonManyObjects decodes all the CBOR Objects read from src
+// reader. It keeps on decoding until reader returns EOF (error when reading).
+// Decoded string is written to the dst. At the end of every CBOR Object
+// newline is written to the output stream.
+//
+// Returns error (if any) that was encountered during decode.
+// The child functions will generate a panic when error is encountered and
+// this function will recover non-runtime Errors and return the reason as error.
+func Cbor2JsonManyObjects(src io.Reader, dst io.Writer) (err error) {
+	defer func() {
+		if r := recover(); r != nil {
+			if _, ok := r.(runtime.Error); ok {
+				panic(r)
+			}
+			err = r.(error)
+		}
+	}()
+	bufRdr := bufio.NewReader(src)
+	for moreBytesToRead(bufRdr) {
+		cbor2JsonOneObject(bufRdr, dst)
+		dst.Write([]byte("\n"))
+	}
+	return nil
+}
+
+// Detect if the bytes to be printed is Binary or not.
+func binaryFmt(p []byte) bool {
+	if len(p) > 0 && p[0] > 0x7F {
+		return true
+	}
+	return false
+}
+
+func getReader(str string) *bufio.Reader {
+	return bufio.NewReader(strings.NewReader(str))
+}
+
+// DecodeIfBinaryToString converts a binary formatted log msg to a
+// JSON formatted String Log message - suitable for printing to Console/Syslog.
+func DecodeIfBinaryToString(in []byte) string {
+	if binaryFmt(in) {
+		var b bytes.Buffer
+		Cbor2JsonManyObjects(strings.NewReader(string(in)), &b)
+		return b.String()
+	}
+	return string(in)
+}
+
+// DecodeObjectToStr checks if the input is a binary format, if so,
+// it will decode a single Object and return the decoded string.
+func DecodeObjectToStr(in []byte) string {
+	if binaryFmt(in) {
+		var b bytes.Buffer
+		cbor2JsonOneObject(getReader(string(in)), &b)
+		return b.String()
+	}
+	return string(in)
+}
+
+// DecodeIfBinaryToBytes checks if the input is a binary format, if so,
+// it will decode all Objects and return the decoded string as byte array.
+func DecodeIfBinaryToBytes(in []byte) []byte {
+	if binaryFmt(in) {
+		var b bytes.Buffer
+		Cbor2JsonManyObjects(bytes.NewReader(in), &b)
+		return b.Bytes()
+	}
+	return in
+}