blob: e3cf3b7db034d61ff651f4bfbcb8a99fb715aba9 [file] [log] [blame]
khenaidooac637102019-01-14 15:44:34 -05001package cbor
2
3// This file contains code to decode a stream of CBOR Data into JSON.
4
5import (
6 "bufio"
7 "bytes"
8 "fmt"
9 "io"
10 "math"
11 "net"
12 "runtime"
13 "strconv"
14 "strings"
15 "time"
16 "unicode/utf8"
17)
18
19var decodeTimeZone *time.Location
20
21const hexTable = "0123456789abcdef"
22
23const isFloat32 = 4
24const isFloat64 = 8
25
26func readNBytes(src *bufio.Reader, n int) []byte {
27 ret := make([]byte, n)
28 for i := 0; i < n; i++ {
29 ch, e := src.ReadByte()
30 if e != nil {
31 panic(fmt.Errorf("Tried to Read %d Bytes.. But hit end of file", n))
32 }
33 ret[i] = ch
34 }
35 return ret
36}
37
38func readByte(src *bufio.Reader) byte {
39 b, e := src.ReadByte()
40 if e != nil {
41 panic(fmt.Errorf("Tried to Read 1 Byte.. But hit end of file"))
42 }
43 return b
44}
45
46func decodeIntAdditonalType(src *bufio.Reader, minor byte) int64 {
47 val := int64(0)
48 if minor <= 23 {
49 val = int64(minor)
50 } else {
51 bytesToRead := 0
52 switch minor {
53 case additionalTypeIntUint8:
54 bytesToRead = 1
55 case additionalTypeIntUint16:
56 bytesToRead = 2
57 case additionalTypeIntUint32:
58 bytesToRead = 4
59 case additionalTypeIntUint64:
60 bytesToRead = 8
61 default:
62 panic(fmt.Errorf("Invalid Additional Type: %d in decodeInteger (expected <28)", minor))
63 }
64 pb := readNBytes(src, bytesToRead)
65 for i := 0; i < bytesToRead; i++ {
66 val = val * 256
67 val += int64(pb[i])
68 }
69 }
70 return val
71}
72
73func decodeInteger(src *bufio.Reader) int64 {
74 pb := readByte(src)
75 major := pb & maskOutAdditionalType
76 minor := pb & maskOutMajorType
77 if major != majorTypeUnsignedInt && major != majorTypeNegativeInt {
78 panic(fmt.Errorf("Major type is: %d in decodeInteger!! (expected 0 or 1)", major))
79 }
80 val := decodeIntAdditonalType(src, minor)
81 if major == 0 {
82 return val
83 }
84 return (-1 - val)
85}
86
87func decodeFloat(src *bufio.Reader) (float64, int) {
88 pb := readByte(src)
89 major := pb & maskOutAdditionalType
90 minor := pb & maskOutMajorType
91 if major != majorTypeSimpleAndFloat {
92 panic(fmt.Errorf("Incorrect Major type is: %d in decodeFloat", major))
93 }
94
95 switch minor {
96 case additionalTypeFloat16:
97 panic(fmt.Errorf("float16 is not suppported in decodeFloat"))
98
99 case additionalTypeFloat32:
100 pb := readNBytes(src, 4)
101 switch string(pb) {
102 case float32Nan:
103 return math.NaN(), isFloat32
104 case float32PosInfinity:
105 return math.Inf(0), isFloat32
106 case float32NegInfinity:
107 return math.Inf(-1), isFloat32
108 }
109 n := uint32(0)
110 for i := 0; i < 4; i++ {
111 n = n * 256
112 n += uint32(pb[i])
113 }
114 val := math.Float32frombits(n)
115 return float64(val), isFloat32
116 case additionalTypeFloat64:
117 pb := readNBytes(src, 8)
118 switch string(pb) {
119 case float64Nan:
120 return math.NaN(), isFloat64
121 case float64PosInfinity:
122 return math.Inf(0), isFloat64
123 case float64NegInfinity:
124 return math.Inf(-1), isFloat64
125 }
126 n := uint64(0)
127 for i := 0; i < 8; i++ {
128 n = n * 256
129 n += uint64(pb[i])
130 }
131 val := math.Float64frombits(n)
132 return val, isFloat64
133 }
134 panic(fmt.Errorf("Invalid Additional Type: %d in decodeFloat", minor))
135}
136
137func decodeStringComplex(dst []byte, s string, pos uint) []byte {
138 i := int(pos)
139 start := 0
140
141 for i < len(s) {
142 b := s[i]
143 if b >= utf8.RuneSelf {
144 r, size := utf8.DecodeRuneInString(s[i:])
145 if r == utf8.RuneError && size == 1 {
146 // In case of error, first append previous simple characters to
147 // the byte slice if any and append a replacement character code
148 // in place of the invalid sequence.
149 if start < i {
150 dst = append(dst, s[start:i]...)
151 }
152 dst = append(dst, `\ufffd`...)
153 i += size
154 start = i
155 continue
156 }
157 i += size
158 continue
159 }
160 if b >= 0x20 && b <= 0x7e && b != '\\' && b != '"' {
161 i++
162 continue
163 }
164 // We encountered a character that needs to be encoded.
165 // Let's append the previous simple characters to the byte slice
166 // and switch our operation to read and encode the remainder
167 // characters byte-by-byte.
168 if start < i {
169 dst = append(dst, s[start:i]...)
170 }
171 switch b {
172 case '"', '\\':
173 dst = append(dst, '\\', b)
174 case '\b':
175 dst = append(dst, '\\', 'b')
176 case '\f':
177 dst = append(dst, '\\', 'f')
178 case '\n':
179 dst = append(dst, '\\', 'n')
180 case '\r':
181 dst = append(dst, '\\', 'r')
182 case '\t':
183 dst = append(dst, '\\', 't')
184 default:
185 dst = append(dst, '\\', 'u', '0', '0', hexTable[b>>4], hexTable[b&0xF])
186 }
187 i++
188 start = i
189 }
190 if start < len(s) {
191 dst = append(dst, s[start:]...)
192 }
193 return dst
194}
195
196func decodeString(src *bufio.Reader, noQuotes bool) []byte {
197 pb := readByte(src)
198 major := pb & maskOutAdditionalType
199 minor := pb & maskOutMajorType
200 if major != majorTypeByteString {
201 panic(fmt.Errorf("Major type is: %d in decodeString", major))
202 }
203 result := []byte{}
204 if !noQuotes {
205 result = append(result, '"')
206 }
207 length := decodeIntAdditonalType(src, minor)
208 len := int(length)
209 pbs := readNBytes(src, len)
210 result = append(result, pbs...)
211 if noQuotes {
212 return result
213 }
214 return append(result, '"')
215}
216
217func decodeUTF8String(src *bufio.Reader) []byte {
218 pb := readByte(src)
219 major := pb & maskOutAdditionalType
220 minor := pb & maskOutMajorType
221 if major != majorTypeUtf8String {
222 panic(fmt.Errorf("Major type is: %d in decodeUTF8String", major))
223 }
224 result := []byte{'"'}
225 length := decodeIntAdditonalType(src, minor)
226 len := int(length)
227 pbs := readNBytes(src, len)
228
229 for i := 0; i < len; i++ {
230 // Check if the character needs encoding. Control characters, slashes,
231 // and the double quote need json encoding. Bytes above the ascii
232 // boundary needs utf8 encoding.
233 if pbs[i] < 0x20 || pbs[i] > 0x7e || pbs[i] == '\\' || pbs[i] == '"' {
234 // We encountered a character that needs to be encoded. Switch
235 // to complex version of the algorithm.
236 dst := []byte{'"'}
237 dst = decodeStringComplex(dst, string(pbs), uint(i))
238 return append(dst, '"')
239 }
240 }
241 // The string has no need for encoding an therefore is directly
242 // appended to the byte slice.
243 result = append(result, pbs...)
244 return append(result, '"')
245}
246
247func array2Json(src *bufio.Reader, dst io.Writer) {
248 dst.Write([]byte{'['})
249 pb := readByte(src)
250 major := pb & maskOutAdditionalType
251 minor := pb & maskOutMajorType
252 if major != majorTypeArray {
253 panic(fmt.Errorf("Major type is: %d in array2Json", major))
254 }
255 len := 0
256 unSpecifiedCount := false
257 if minor == additionalTypeInfiniteCount {
258 unSpecifiedCount = true
259 } else {
260 length := decodeIntAdditonalType(src, minor)
261 len = int(length)
262 }
263 for i := 0; unSpecifiedCount || i < len; i++ {
264 if unSpecifiedCount {
265 pb, e := src.Peek(1)
266 if e != nil {
267 panic(e)
268 }
269 if pb[0] == byte(majorTypeSimpleAndFloat|additionalTypeBreak) {
270 readByte(src)
271 break
272 }
273 }
274 cbor2JsonOneObject(src, dst)
275 if unSpecifiedCount {
276 pb, e := src.Peek(1)
277 if e != nil {
278 panic(e)
279 }
280 if pb[0] == byte(majorTypeSimpleAndFloat|additionalTypeBreak) {
281 readByte(src)
282 break
283 }
284 dst.Write([]byte{','})
285 } else if i+1 < len {
286 dst.Write([]byte{','})
287 }
288 }
289 dst.Write([]byte{']'})
290}
291
292func map2Json(src *bufio.Reader, dst io.Writer) {
293 pb := readByte(src)
294 major := pb & maskOutAdditionalType
295 minor := pb & maskOutMajorType
296 if major != majorTypeMap {
297 panic(fmt.Errorf("Major type is: %d in map2Json", major))
298 }
299 len := 0
300 unSpecifiedCount := false
301 if minor == additionalTypeInfiniteCount {
302 unSpecifiedCount = true
303 } else {
304 length := decodeIntAdditonalType(src, minor)
305 len = int(length)
306 }
307 dst.Write([]byte{'{'})
308 for i := 0; unSpecifiedCount || i < len; i++ {
309 if unSpecifiedCount {
310 pb, e := src.Peek(1)
311 if e != nil {
312 panic(e)
313 }
314 if pb[0] == byte(majorTypeSimpleAndFloat|additionalTypeBreak) {
315 readByte(src)
316 break
317 }
318 }
319 cbor2JsonOneObject(src, dst)
320 if i%2 == 0 {
321 // Even position values are keys.
322 dst.Write([]byte{':'})
323 } else {
324 if unSpecifiedCount {
325 pb, e := src.Peek(1)
326 if e != nil {
327 panic(e)
328 }
329 if pb[0] == byte(majorTypeSimpleAndFloat|additionalTypeBreak) {
330 readByte(src)
331 break
332 }
333 dst.Write([]byte{','})
334 } else if i+1 < len {
335 dst.Write([]byte{','})
336 }
337 }
338 }
339 dst.Write([]byte{'}'})
340}
341
342func decodeTagData(src *bufio.Reader) []byte {
343 pb := readByte(src)
344 major := pb & maskOutAdditionalType
345 minor := pb & maskOutMajorType
346 if major != majorTypeTags {
347 panic(fmt.Errorf("Major type is: %d in decodeTagData", major))
348 }
349 switch minor {
350 case additionalTypeTimestamp:
351 return decodeTimeStamp(src)
352
353 // Tag value is larger than 256 (so uint16).
354 case additionalTypeIntUint16:
355 val := decodeIntAdditonalType(src, minor)
356
357 switch uint16(val) {
358 case additionalTypeEmbeddedJSON:
359 pb := readByte(src)
360 dataMajor := pb & maskOutAdditionalType
361 if dataMajor != majorTypeByteString {
362 panic(fmt.Errorf("Unsupported embedded Type: %d in decodeEmbeddedJSON", dataMajor))
363 }
364 src.UnreadByte()
365 return decodeString(src, true)
366
367 case additionalTypeTagNetworkAddr:
368 octets := decodeString(src, true)
369 ss := []byte{'"'}
370 switch len(octets) {
371 case 6: // MAC address.
372 ha := net.HardwareAddr(octets)
373 ss = append(append(ss, ha.String()...), '"')
374 case 4: // IPv4 address.
375 fallthrough
376 case 16: // IPv6 address.
377 ip := net.IP(octets)
378 ss = append(append(ss, ip.String()...), '"')
379 default:
380 panic(fmt.Errorf("Unexpected Network Address length: %d (expected 4,6,16)", len(octets)))
381 }
382 return ss
383
384 case additionalTypeTagNetworkPrefix:
385 pb := readByte(src)
386 if pb != byte(majorTypeMap|0x1) {
387 panic(fmt.Errorf("IP Prefix is NOT of MAP of 1 elements as expected"))
388 }
389 octets := decodeString(src, true)
390 val := decodeInteger(src)
391 ip := net.IP(octets)
392 var mask net.IPMask
393 pfxLen := int(val)
394 if len(octets) == 4 {
395 mask = net.CIDRMask(pfxLen, 32)
396 } else {
397 mask = net.CIDRMask(pfxLen, 128)
398 }
399 ipPfx := net.IPNet{IP: ip, Mask: mask}
400 ss := []byte{'"'}
401 ss = append(append(ss, ipPfx.String()...), '"')
402 return ss
403
404 case additionalTypeTagHexString:
405 octets := decodeString(src, true)
406 ss := []byte{'"'}
407 for _, v := range octets {
408 ss = append(ss, hexTable[v>>4], hexTable[v&0x0f])
409 }
410 return append(ss, '"')
411
412 default:
413 panic(fmt.Errorf("Unsupported Additional Tag Type: %d in decodeTagData", val))
414 }
415 }
416 panic(fmt.Errorf("Unsupported Additional Type: %d in decodeTagData", minor))
417}
418
419func decodeTimeStamp(src *bufio.Reader) []byte {
420 pb := readByte(src)
421 src.UnreadByte()
422 tsMajor := pb & maskOutAdditionalType
423 if tsMajor == majorTypeUnsignedInt || tsMajor == majorTypeNegativeInt {
424 n := decodeInteger(src)
425 t := time.Unix(n, 0)
426 if decodeTimeZone != nil {
427 t = t.In(decodeTimeZone)
428 } else {
429 t = t.In(time.UTC)
430 }
431 tsb := []byte{}
432 tsb = append(tsb, '"')
433 tsb = t.AppendFormat(tsb, IntegerTimeFieldFormat)
434 tsb = append(tsb, '"')
435 return tsb
436 } else if tsMajor == majorTypeSimpleAndFloat {
437 n, _ := decodeFloat(src)
438 secs := int64(n)
439 n -= float64(secs)
440 n *= float64(1e9)
441 t := time.Unix(secs, int64(n))
442 if decodeTimeZone != nil {
443 t = t.In(decodeTimeZone)
444 } else {
445 t = t.In(time.UTC)
446 }
447 tsb := []byte{}
448 tsb = append(tsb, '"')
449 tsb = t.AppendFormat(tsb, NanoTimeFieldFormat)
450 tsb = append(tsb, '"')
451 return tsb
452 }
453 panic(fmt.Errorf("TS format is neigther int nor float: %d", tsMajor))
454}
455
456func decodeSimpleFloat(src *bufio.Reader) []byte {
457 pb := readByte(src)
458 major := pb & maskOutAdditionalType
459 minor := pb & maskOutMajorType
460 if major != majorTypeSimpleAndFloat {
461 panic(fmt.Errorf("Major type is: %d in decodeSimpleFloat", major))
462 }
463 switch minor {
464 case additionalTypeBoolTrue:
465 return []byte("true")
466 case additionalTypeBoolFalse:
467 return []byte("false")
468 case additionalTypeNull:
469 return []byte("null")
470 case additionalTypeFloat16:
471 fallthrough
472 case additionalTypeFloat32:
473 fallthrough
474 case additionalTypeFloat64:
475 src.UnreadByte()
476 v, bc := decodeFloat(src)
477 ba := []byte{}
478 switch {
479 case math.IsNaN(v):
480 return []byte("\"NaN\"")
481 case math.IsInf(v, 1):
482 return []byte("\"+Inf\"")
483 case math.IsInf(v, -1):
484 return []byte("\"-Inf\"")
485 }
486 if bc == isFloat32 {
487 ba = strconv.AppendFloat(ba, v, 'f', -1, 32)
488 } else if bc == isFloat64 {
489 ba = strconv.AppendFloat(ba, v, 'f', -1, 64)
490 } else {
491 panic(fmt.Errorf("Invalid Float precision from decodeFloat: %d", bc))
492 }
493 return ba
494 default:
495 panic(fmt.Errorf("Invalid Additional Type: %d in decodeSimpleFloat", minor))
496 }
497}
498
499func cbor2JsonOneObject(src *bufio.Reader, dst io.Writer) {
500 pb, e := src.Peek(1)
501 if e != nil {
502 panic(e)
503 }
504 major := (pb[0] & maskOutAdditionalType)
505
506 switch major {
507 case majorTypeUnsignedInt:
508 fallthrough
509 case majorTypeNegativeInt:
510 n := decodeInteger(src)
511 dst.Write([]byte(strconv.Itoa(int(n))))
512
513 case majorTypeByteString:
514 s := decodeString(src, false)
515 dst.Write(s)
516
517 case majorTypeUtf8String:
518 s := decodeUTF8String(src)
519 dst.Write(s)
520
521 case majorTypeArray:
522 array2Json(src, dst)
523
524 case majorTypeMap:
525 map2Json(src, dst)
526
527 case majorTypeTags:
528 s := decodeTagData(src)
529 dst.Write(s)
530
531 case majorTypeSimpleAndFloat:
532 s := decodeSimpleFloat(src)
533 dst.Write(s)
534 }
535}
536
537func moreBytesToRead(src *bufio.Reader) bool {
538 _, e := src.ReadByte()
539 if e == nil {
540 src.UnreadByte()
541 return true
542 }
543 return false
544}
545
546// Cbor2JsonManyObjects decodes all the CBOR Objects read from src
547// reader. It keeps on decoding until reader returns EOF (error when reading).
548// Decoded string is written to the dst. At the end of every CBOR Object
549// newline is written to the output stream.
550//
551// Returns error (if any) that was encountered during decode.
552// The child functions will generate a panic when error is encountered and
553// this function will recover non-runtime Errors and return the reason as error.
554func Cbor2JsonManyObjects(src io.Reader, dst io.Writer) (err error) {
555 defer func() {
556 if r := recover(); r != nil {
557 if _, ok := r.(runtime.Error); ok {
558 panic(r)
559 }
560 err = r.(error)
561 }
562 }()
563 bufRdr := bufio.NewReader(src)
564 for moreBytesToRead(bufRdr) {
565 cbor2JsonOneObject(bufRdr, dst)
566 dst.Write([]byte("\n"))
567 }
568 return nil
569}
570
571// Detect if the bytes to be printed is Binary or not.
572func binaryFmt(p []byte) bool {
573 if len(p) > 0 && p[0] > 0x7F {
574 return true
575 }
576 return false
577}
578
579func getReader(str string) *bufio.Reader {
580 return bufio.NewReader(strings.NewReader(str))
581}
582
583// DecodeIfBinaryToString converts a binary formatted log msg to a
584// JSON formatted String Log message - suitable for printing to Console/Syslog.
585func DecodeIfBinaryToString(in []byte) string {
586 if binaryFmt(in) {
587 var b bytes.Buffer
588 Cbor2JsonManyObjects(strings.NewReader(string(in)), &b)
589 return b.String()
590 }
591 return string(in)
592}
593
594// DecodeObjectToStr checks if the input is a binary format, if so,
595// it will decode a single Object and return the decoded string.
596func DecodeObjectToStr(in []byte) string {
597 if binaryFmt(in) {
598 var b bytes.Buffer
599 cbor2JsonOneObject(getReader(string(in)), &b)
600 return b.String()
601 }
602 return string(in)
603}
604
605// DecodeIfBinaryToBytes checks if the input is a binary format, if so,
606// it will decode all Objects and return the decoded string as byte array.
607func DecodeIfBinaryToBytes(in []byte) []byte {
608 if binaryFmt(in) {
609 var b bytes.Buffer
610 Cbor2JsonManyObjects(bytes.NewReader(in), &b)
611 return b.Bytes()
612 }
613 return in
614}