• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokath / trice / 25233039650

01 May 2026 09:02PM UTC coverage: 90.93% (-0.09%) from 91.023%
25233039650

Pull #650

github

web-flow
Merge 924339d77 into ab3ce9d7b
Pull Request #650: Issue #649 fixed

194 of 231 new or added lines in 4 files covered. (83.98%)

1 existing line in 1 file now uncovered.

5504 of 6053 relevant lines covered (90.93%)

893.08 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

90.59
/internal/trexDecoder/trexDecoder.go
1
// SPDX-License-Identifier: MIT
2

3
// Package trexDecoder decodes framed TREX trice byte streams.
4
package trexDecoder
5

6
import (
7
        "bytes"
8
        "encoding/binary"
9
        "encoding/hex"
10
        "fmt"
11
        "io"
12
        "log"
13
        "math"
14
        "strings"
15
        "sync"
16

17
        cobs "github.com/rokath/cobs/go"
18
        "github.com/rokath/tcobs/v1"
19
        "github.com/rokath/trice/internal/decoder"
20
        "github.com/rokath/trice/internal/emitter"
21
        "github.com/rokath/trice/internal/fmtspec"
22
        "github.com/rokath/trice/internal/id"
23
        "github.com/rokath/trice/pkg/cipher"
24
)
25

26
const (
27
        tyIdSize = 2 // tySize is what each trice message starts with: 2 bytes
28
        ncSize   = 2 // countSize is what each regular trice message contains after an optional target timestamp
29
        typeS0   = 1 // regular trice format without stamp     : 011iiiiiI NC ...
30
        typeS2   = 2 // regular trice format with 16-bit stamp : 101iiiiiI TT NC ...
31
        typeS4   = 3 // regular trice format with 32-bit stamp : 111iiiiiI TT TT NC ...
32
        typeX0   = 0 // regular trice format with 32-bit stamp : 001iiiiiI TT TT TT TT NC ...
33

34
        packageFramingNone = iota
35
        packageFramingCOBS
36
        packageFramingTCOBS   //v1
37
        packageFramingTCOBSv2 //v2
38
)
39

40
var (
41
        // Doubled16BitID enables acceptance of 16-bit IDs repeated twice in the header.
42
        Doubled16BitID bool
43

44
        // AddNewlineToEachTriceMessage appends a newline to each decoded Trice message when needed.
45
        AddNewlineToEachTriceMessage bool
46

47
        // SingleFraming demands that each received package contains at most one Trice message.
48
        SingleFraming bool
49

50
        // DisableCycleErrors suppresses cycle counter mismatch diagnostics.
51
        DisableCycleErrors bool
52
)
53

54
var specialCaseTriceTypes = map[string]struct{}{
55
        "TRICES": {}, "TRICEN": {}, "TRICEB": {}, "TRICEF": {},
56
        "TRICE8B": {}, "TRICE16B": {}, "TRICE32B": {}, "TRICE64B": {},
57
        "TRICE8F": {}, "TRICE16F": {}, "TRICE32F": {}, "TRICE64F": {},
58
        "TRICE_S": {}, "TRICE_N": {}, "TRICE_B": {}, "TRICE_F": {},
59
        "TRICE8_B": {}, "TRICE16_B": {}, "TRICE32_B": {}, "TRICE64_B": {},
60
        "TRICE8_F": {}, "TRICE16_F": {}, "TRICE32_F": {}, "TRICE64_F": {},
61
}
62

63
func init() {
12✔
64
        decoder.Register("TREX", New)
12✔
65
}
12✔
66

67
// trexDec is the decoder instance for TREX-encoded Trices.
68
type trexDec struct {
69
        decoder.DecoderData
70
        cycle          uint8  // cycle date: c0...bf
71
        pFmt           string // modified trice format string: %u -> %d
72
        u              []int  // 1: modified format string positions:  %u -> %d, 2: float (%f)
73
        packageFraming int
74
}
75

76
// New provides a TREX decoder instance.
77
//
78
// in is the input byte stream source.
79
func New(w io.Writer, lut id.TriceIDLookUp, m *sync.RWMutex, li id.TriceIDLookUpLI, in io.Reader, endian bool) decoder.Decoder {
21✔
80
        // Todo: rewrite using the TCOBS Reader. The provided in io.Reader provides a raw data stream.
21✔
81
        // https://github.com/rokath/tcobs/blob/master/TCOBSv1/read.go -> use NewDecoder ...
21✔
82

21✔
83
        p := &trexDec{
21✔
84
                DecoderData: decoder.NewDecoderData(decoder.Config{
21✔
85
                        Out:         w,
21✔
86
                        LUT:         lut,
21✔
87
                        LUTMutex:    m,
21✔
88
                        LI:          li,
21✔
89
                        In:          in,
21✔
90
                        Endian:      endian,
21✔
91
                        NeedBuffers: true,
21✔
92
                }),
21✔
93
                cycle: 0xc0, // start value
21✔
94
        }
21✔
95

21✔
96
        switch strings.ToLower(decoder.PackageFraming) {
21✔
97
        case "cobs":
8✔
98
                p.packageFraming = packageFramingCOBS
8✔
99
        case "tcobs", "tcobsv1":
3✔
100
                p.packageFraming = packageFramingTCOBS
3✔
101
        case "tcobsv2":
1✔
102
                p.packageFraming = packageFramingTCOBSv2
1✔
103
        case "none":
9✔
104
                p.packageFraming = packageFramingNone
9✔
105
        default:
×
106
                log.Fatal("Invalid framing switch:\a", decoder.PackageFraming)
×
107
        }
108
        return p
21✔
109
}
110

111
// nextData reads with an inner reader a raw byte stream.
112
//
113
// When fewer than 4 bytes are available, nextData returns without processing.
114
// That means the incoming data stream is exhausted and a next try should be started a bit later.
115
// Some arrived bytes are kept internally and concatenated with the following bytes in a next Read.
116
// Afterwards 0 or at least 4 bytes are inside p.B
117
func (p *trexDec) nextData() {
9✔
118
        m, err := p.In.Read(p.InnerBuffer)      // use p.InnerBuffer as destination read buffer
9✔
119
        p.B = append(p.B, p.InnerBuffer[:m]...) // merge with leftovers
9✔
120
        if err != nil && err != io.EOF {        // some serious error
9✔
121
                log.Fatal("ERROR:internal reader error\a", err) // exit
×
122
        }
×
123
}
124

125
// nextPackage reads with an inner reader a TCOBSv1 encoded byte stream.
126
//
127
// When no terminating 0 is found in the incoming bytes nextPackage returns without action.
128
// That means the incoming data stream is exhausted and a next try should be started a bit later.
129
// Some arrived bytes are kept internally and concatenated with the following bytes in a next Read.
130
// When a terminating 0 is found in the incoming bytes ReadFromCOBS decodes the COBS package
131
// and returns it in b and its len in n. If more data arrived after the first terminating 0,
132
// these are kept internally and concatenated with the following bytes in a next Read.
133
func (p *trexDec) nextPackage() {
632✔
134
        // Here p.IBuf contains none or available bytes, what can be several trice messages.
632✔
135
        // So first try to process p.IBuf.
632✔
136
        index := bytes.IndexByte(p.IBuf, 0) // find terminating 0
632✔
137
        if index == -1 {                    // p.IBuf has no complete COBS data, so try to read more input
1,258✔
138
                m, err := p.In.Read(p.InnerBuffer)            // use p.InnerBuffer as bytes read buffer
626✔
139
                p.IBuf = append(p.IBuf, p.InnerBuffer[:m]...) // merge with leftovers
626✔
140
                if err != nil && err != io.EOF {              // some serious error
626✔
141
                        log.Fatal("ERROR:internal reader error\a", err) // exit
×
142
                }
×
143
                index = bytes.IndexByte(p.IBuf, 0) // find terminating 0
626✔
144
                if index == -1 {                   // p.IBuf has no complete COBS data, so leave
1,241✔
145
                        // Even err could be io.EOF, some valid data possibly in p.iBUf.
615✔
146
                        // In case of file input (J-LINK usage) a plug off is not detectable here.
615✔
147
                        return // no terminating 0, nothing to do
615✔
148
                }
615✔
149
        }
150
        if decoder.TestTableMode {
17✔
151
                p.printTestTableLine(index + 1)
×
152
        }
×
153
        // here a complete COBS or TCOBS package exists
154
        if decoder.DebugOut { // Debug output
19✔
155
                fmt.Fprintf(p.W, "%s: ", decoder.PackageFraming)
2✔
156
                decoder.Dump(p.W, p.IBuf[:index+1])
2✔
157
        }
2✔
158

159
        frame := p.IBuf[:index]
17✔
160

17✔
161
        switch p.packageFraming {
17✔
162

163
        case packageFramingCOBS:
11✔
164
                p.B = p.B0                      // make([]byte, decoder.DefaultSize) // todo: avoid allocation
11✔
165
                n, e := cobs.Decode(p.B, frame) // if index is 0, an empty buffer is decoded
11✔
166
                p.IBuf = p.IBuf[index+1:]       // step forward (next package data in p.IBuf now, if any)
11✔
167
                if e != nil {
11✔
168
                        if decoder.Verbose {
×
169
                                fmt.Println("\ainconsistent COBS buffer!") // show also terminating 0
×
170
                        }
×
171
                }
172
                p.B = p.B[:n]
11✔
173

174
        case packageFramingTCOBS:
6✔
175
        repeat:
6✔
176
                p.B = p.B0                       // make([]byte, decoder.DefaultSize) // todo: avoid allocation
7✔
177
                n, e := tcobs.Decode(p.B, frame) // if index is 0, an empty buffer is decoded
7✔
178
                // from merging: p.IBuf = p.IBuf[index+1:]        // step forward (next package data in p.IBuf now, if any)
7✔
179
                if e != nil {
9✔
180
                        // remove 3 lines if they exist - see issue #403 for the reason.
2✔
181
                        s := strings.SplitN(strings.ReplaceAll(string(frame), "\r\n", "\n"), "\n", 4)
2✔
182
                        var bytesCount int
2✔
183
                        if len(s) >= 3 {
3✔
184
                                var newLines int
1✔
185
                                fmt.Println(s[0])
1✔
186
                                fmt.Println(s[1])
1✔
187
                                fmt.Println(s[2])
1✔
188
                                for _, b := range frame {
148✔
189
                                        frame = frame[1:]
147✔
190
                                        bytesCount++
147✔
191
                                        if b == 10 {
150✔
192
                                                newLines++
3✔
193
                                                if newLines == 3 {
4✔
194
                                                        break
1✔
195
                                                }
196
                                        }
197
                                        continue
146✔
198
                                }
199
                                index -= bytesCount
1✔
200
                                goto repeat
1✔
201
                        }
202
                        if decoder.Verbose {
1✔
203
                                fmt.Println(e, "\ainconsistent TCOBSv1 buffer:")
×
204
                                fmt.Println(e, hex.Dump(frame)) // show also terminating 0
×
205
                        }
×
206
                        e = nil
1✔
207
                        p.B = p.B[:0]
1✔
208
                        p.IBuf = p.IBuf[index+1:] // step forward (next package data in p.IBuf now, if any) // from merging:
1✔
209
                } else {
5✔
210
                        p.B = p.B[len(p.B)-n:]    // buffer is filled from the end
5✔
211
                        p.IBuf = p.IBuf[index+1:] // step forward (next package data in p.IBuf now, if any) // from merging:
5✔
212
                }
5✔
213
        default:
×
214
                log.Fatalln("unexpected execution path", p.packageFraming)
×
215
        }
216

217
        if decoder.DebugOut { // Debug output
19✔
218
                fmt.Fprint(p.W, "->TRICE: ")
2✔
219
                decoder.Dump(p.W, p.B)
2✔
220
        }
2✔
221

222
        if cipher.Password != "" { // encrypted
24✔
223
                cipher.Decrypt(p.B, p.B)
7✔
224
                if decoder.DebugOut { // Debug output
8✔
225
                        fmt.Fprint(p.W, "-> DEC:  ")
1✔
226
                        decoder.Dump(p.W, p.B)
1✔
227
                }
1✔
228
        }
229
}
230

231
// isZero reports whether all bytes in the slice are zero.
232
func isZero(bytes []byte) bool {
620✔
233
        b := byte(0)
620✔
234
        for _, s := range bytes {
630✔
235
                b |= s
10✔
236
        }
10✔
237
        return b == 0
620✔
238
}
239

240
// removeZeroHiByte discards one high-order padding zero byte from a candidate frame.
241
//
242
// The removed byte depends on configured endianness.
243
func (p *trexDec) removeZeroHiByte(s []byte) (r []byte) {
5✔
244
        // The package interpreter does not know the number of padding zeroes, so it needs to discard them one by one.
5✔
245
        // If they are not zero, this is an error.
5✔
246
        switch p.Endian {
5✔
247
        case decoder.BigEndian:
1✔
248
                // Big endian case: 00 00 AA AA C0 00 -> 00 AA AA C0 00 -> still typeX0 -> AA AA C0 00 -> ok next package
1✔
249
                if s[0] != 0 {
1✔
250
                        fmt.Println("unexpected case in line 273", string(s))
×
251
                }
×
252
                r = s[1:]
1✔
253
        case decoder.LittleEndian:
4✔
254
                // Little endian case: 00 00 AA AA C0 00 -> 00 AA AA C0 00 -> AA00 signals a valid Trice, but it is not! -> We need to remove the HI byte!
4✔
255
                if s[1] != 0 {
4✔
256
                        //log.Fatal("unexpected case", s)
×
257
                        // todo: This needs to be disabled for successfully running all test cases.
×
258
                        // BUT: deferred package framing NONE does not work
×
259
                }
×
260
                r = append(s[:1], s[2:]...)
4✔
261
        default:
×
262
                fmt.Println("unexpected case 927346193377", string(s))
×
263
        }
264
        return
5✔
265
}
266

267
// Read returns a single Trice conversion result or a single error message in b[:n].
268
// Read is the provided read method for TREX decoding and provides next string as byte slice.
269
//
270
// It uses inner reader p.In and internal id look-up table to fill b with a string.
271
// b is a slice of bytes with a len for the max expected string size.
272
// n is the count of read bytes inside b.
273
// Read returns usually one complete trice string or nothing but can return concatenated
274
// trice strings, each ending with a newline despite the last one, when messages added.
275
// Read does not process all internally read complete trice packages to be able later to
276
// separate Trices within one line to keep them separated for color processing.
277
// Therefore, Read needs to be called cyclically even after returning io.EOF to process internal data.
278
// When Read returns n=0, all processable complete trice packages are done,
279
// but the start of a following trice package can be already inside the internal buffer.
280
// In case of a not matching cycle, a warning message in trice format is prefixed.
281
// In case of invalid package data, error messages in trice format are returned and the package is dropped.
282
func (p *trexDec) Read(b []byte) (n int, err error) {
639✔
283
        if p.packageFraming == packageFramingNone {
647✔
284
                p.nextData() // returns all unprocessed data inside p.B
8✔
285
                p.B0 = p.B   // keep data for re-sync
8✔
286
        } else {
639✔
287
                if cipher.Password != "" && len(p.B) < 8 && isZero(p.B) {
1,249✔
288
                        p.B = p.B[:0] // Discard trailing zeroes. ATTENTION: incomplete trice messages containing many zeroes could be problematic here!
618✔
289
                }
618✔
290
                if len(p.B) == 1 { // last decoded package exhausted
632✔
291
                        if decoder.Verbose {
2✔
292
                                fmt.Println("Inconsistent data, discarding last single byte", p.B[0], "from:")
1✔
293
                                fmt.Println(hex.Dump(p.B))
1✔
294
                        }
1✔
295
                        p.B = p.B[:0]
1✔
296
                }
297
                if len(p.B) == 0 { // last decoded package exhausted
1,255✔
298
                        p.nextPackage() // returns one decoded package inside p.B
624✔
299
                }
624✔
300
        }
301
        packageSize := len(p.B)
639✔
302
        if packageSize < tyIdSize { // not enough data for a next package
1,255✔
303
                return
616✔
304
        }
616✔
305
        packed := p.B
23✔
306
        tyId := p.ReadU16(p.B)
23✔
307
        p.B = p.B[tyIdSize:]
23✔
308

23✔
309
        triceType := int(tyId >> decoder.IDBits) // most significant bit are the triceType
23✔
310
        triceID := id.TriceID(0x3FFF & tyId)     // 14 least significant bits are the ID
23✔
311
        decoder.LastTriceID = triceID            // used for showID
23✔
312
        decoder.RecordForStatistics(triceID)     // This is for the "trice log -stat" flag
23✔
313

23✔
314
        //typeX0Handler := "countedString"
23✔
315

23✔
316
        switch triceType {
23✔
317
        case typeS0: // no timestamp
10✔
318
                decoder.TargetTimestampSize = 0
10✔
319
        case typeS2: // 16-bit stamp
4✔
320
                decoder.TargetTimestampSize = 2
4✔
321
                if Doubled16BitID { // p.packageFraming == packageFramingNone || cipher.Password != "" {
6✔
322
                        if len(p.B) < 2 {
3✔
323
                                return // wait for more data
1✔
324
                        }
1✔
325

326
                        // Without encoding it needs to be done here.
327
                        // Also encrypted trice messages carry a double 16-bit ID.
328
                        p.B = p.B[tyIdSize:] // When target encoding is done, it removes the double 16-bit ID at the 16-bit timestamp trices.
1✔
329
                }
330
        case typeS4: // 32-bit stamp
6✔
331
                decoder.TargetTimestampSize = 4
6✔
332
        case typeX0: // extended trice type X0
3✔
333
                if p.packageFraming == packageFramingNone {
5✔
334
                        // typeX0 is not supported (yet)
2✔
335
                        if decoder.Verbose {
3✔
336
                                n += copy(b[n:], fmt.Sprintln("wrn:\aTo try to resync removing zero HI byte from:"))
1✔
337
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
1✔
338
                        }
1✔
339
                        p.B = p.removeZeroHiByte(p.B0)
2✔
340
                        return
2✔
341
                }
342

343
                //  decoder.TargetTimestampSize = 0
344
                //  switch typeX0Handler {
345
                //  case "countedString":
346
                //          len := triceID
347
                //          //n += copy(b[n:], "vintage:")
348
                //          n += copy(b[n:], fmt.Sprintln(string(p.B[:len])))
349
                //          p.B = p.B[len:]
350
                //  default:
351
                //          n += copy(b[n:], fmt.Sprintln("ERROR:\aNo handler for triceType typeX0"))
352
                //  }
353
                //  return n, nil
354

355
                // We can reach here in target TRICE_MULTI_PACK_MODE, when a trice message is followed by several zeroes (up to 7 possible with encryption).
356
                p.B = p.removeZeroHiByte(packed)
1✔
357
        }
358

359
        if packageSize < tyIdSize+decoder.TargetTimestampSize+ncSize { // for non typeEX trices
20✔
360
                return // not enough data
×
361
        }
×
362

363
        // try to interpret
364
        switch triceType {
20✔
365
        case typeS0:
10✔
366
                decoder.TargetTimestamp = 0
10✔
367
        case typeS2: // 16-bit stamp
3✔
368
                decoder.TargetTimestamp = uint64(p.ReadU16(p.B))
3✔
369
        case typeS4: // 32-bit stamp
6✔
370
                decoder.TargetTimestamp = uint64(p.ReadU32(p.B))
6✔
371
        default: // typeX0
1✔
372
                //  switch typeX0Handler {
1✔
373
                //  case "countedString":
1✔
374
                //          len := triceID
1✔
375
                //          n += copy(b[n:], "USER:")
1✔
376
                //          n += copy(b[n:], fmt.Sprintln(string(p.B[:len])))
1✔
377
                //          p.B = p.B[len:]
1✔
378
                //  default:
1✔
379
                n += copy(b[n:], fmt.Sprintln("ERROR:\atriceType typeX0 not implemented (hint: IDBits value?)"))
1✔
380
                //  }
1✔
381
                return n, nil
1✔
382
        }
383
        //                decoder.TargetTimestampSize = 0
384
        //                switch typeX0Handler {
385
        //                case "countedString":
386
        //                        len := triceID
387
        //                        //n += copy(b[n:], "vintage:")
388
        //                        n += copy(b[n:], fmt.Sprintln(string(p.B[:len])))
389
        //                        p.B = p.B[len:]
390
        //                default:
391
        //                        n += copy(b[n:], fmt.Sprintln("ERROR:\aNo handler for triceType typeX0"))
392
        //                }
393
        //                return n, nil
394
        //
395
        //                //  if p.packageFraming == packageFramingNone {
396
        //                //          // typeX0 is not supported (yet)
397
        //                //          if decoder.Verbose {
398
        //                //                  n += copy(b[n:], fmt.Sprintln("wrn:\aTo try to resync removing zero HI byte from:"))
399
        //                //                  n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
400
        //                //          }
401
        //                //          p.B = p.removeZeroHiByte(p.B0)
402
        //                //          return
403
        //                //  }
404
        //                // We can reach here in target TRICE_MULTI_PACK_MODE, when a trice message is followed by several zeroes (up to 7 possible with encryption).
405
        //                // p.B = p.removeZeroHiByte(packed)
406
        //        }
407
        //
408
        //        if packageSize < tyIdSize+decoder.TargetTimestampSize+ncSize { // for non typeEX trices
409
        //                return // not enough data
410
        //        }
411
        //
412
        //        // try to interpret
413
        //        switch triceType {
414
        //        case typeS0:
415
        //                decoder.TargetTimestamp = 0
416
        //        case typeS2: // 16-bit stamp
417
        //                decoder.TargetTimestamp = uint64(p.ReadU16(p.B))
418
        //        case typeS4: // 32-bit stamp
419
        //                decoder.TargetTimestamp = uint64(p.ReadU32(p.B))
420
        //        default: // typeX0
421
        //                switch typeX0Handler {
422
        //                case "countedString":
423
        //                        len := triceID
424
        //                        n += copy(b[n:], "USER:")
425
        //                        n += copy(b[n:], fmt.Sprintln(string(p.B[:len])))
426
        //                        p.B = p.B[len:]
427
        //                default:
428
        //                        n += copy(b[n:], fmt.Sprintln("ERROR:\atriceType typeX0 not implemented (hint: IDBits value?)"))
429
        //                }
430
        //                return n, nil
431
        //        }
432

433
        p.B = p.B[decoder.TargetTimestampSize:]
19✔
434

19✔
435
        if len(p.B) < 2 {
20✔
436
                return // wait for more data
1✔
437
        }
1✔
438
        nc := p.ReadU16(p.B) // n = number of data bytes (without timestamp), most significant bit is the count encoding, c = cycle
18✔
439
        p.B = p.B[ncSize:]
18✔
440

18✔
441
        var cycle uint8
18✔
442
        if nc>>15 == 1 { // special case: more than data 127 bytes
19✔
443
                // C code: #define TRICE_LCNT(count) TRICE_PUT16( (0x8000 | (count)) );
1✔
444
                cycle = p.cycle                 // cycle is not transmitted, so set expected value
1✔
445
                p.ParamSpace = int(0x7FFF & nc) // 15 bit for data byte count excluding timestamp
1✔
446
        } else {
18✔
447
                // C code: #define TRICE_CNTC(count) TRICE_PUT16( ((count)<<8) | TRICE_CYCLE )
17✔
448
                cycle = uint8(nc)           // low byte is cycle
17✔
449
                p.ParamSpace = int(nc >> 8) // high byte is 7 bit number of bytes for data count excluding timestamp
17✔
450
        }
17✔
451

452
        p.TriceSize = tyIdSize + decoder.TargetTimestampSize + ncSize + p.ParamSpace
18✔
453
        if p.TriceSize > packageSize { //  '>' for multiple trices in one package (case TriceOutMultiPackMode), todo: discuss all possible variants
20✔
454
                if p.packageFraming == packageFramingNone {
3✔
455
                        if decoder.Verbose {
1✔
456
                                n += copy(b[n:], fmt.Sprintln("wrn:\adiscarding first byte", p.B0[0], "from:"))
×
457
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
×
458
                        }
×
459
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
460
                        p.B = p.B0
1✔
461
                        return
1✔
462
                }
463
                if decoder.Verbose {
2✔
464
                        n += copy(b[n:], fmt.Sprintln("ERROR:\apackage size", packageSize, "is <", p.TriceSize, " - ignoring package:"))
1✔
465
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
466
                        n += copy(b[n:], fmt.Sprintln("tyIdSize=", tyIdSize, "tsSize=", decoder.TargetTimestampSize, "ncSize=", ncSize, "ParamSpae=", p.ParamSpace))
1✔
467
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
468
                }
1✔
469
                p.B = p.B[len(p.B):] // discard buffer
1✔
470
        }
471
        if SingleFraming && p.TriceSize != packageSize {
18✔
472
                if decoder.Verbose {
2✔
473
                        n += copy(b[n:], fmt.Sprintln("ERROR:\asingle framed package size", packageSize, "is !=", p.TriceSize, " - ignoring package:"))
1✔
474
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
475
                        n += copy(b[n:], fmt.Sprintln("tyIdSize=", tyIdSize, "tsSize=", decoder.TargetTimestampSize, "ncSize=", ncSize, "ParamSpae=", p.ParamSpace))
1✔
476
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
477
                }
1✔
478
                p.B = p.B[len(p.B):] // discard buffer
1✔
479
        }
480

481
        // cycle counter automatic & check
482
        if cycle == 0xc0 && p.cycle != 0xc0 && decoder.InitialCycle { // with cycle counter and seems to be a target reset
18✔
483
                n += copy(b[n:], fmt.Sprintln("warning:\a   Target Reset?   "))
1✔
484
                p.cycle = cycle + 1 // adjust cycle
1✔
485
                decoder.InitialCycle = false
1✔
486
        }
1✔
487
        if cycle == 0xc0 && p.cycle != 0xc0 && !decoder.InitialCycle { // with cycle counter and seems to be a target reset
19✔
488
                //n += copy(b[n:], fmt.Sprintln("info:   Target Reset?   ")) // todo: This line is ok with cycle counter but not without cycle counter
2✔
489
                p.cycle = cycle + 1 // adjust cycle
2✔
490
        }
2✔
491
        if cycle == 0xc0 && p.cycle == 0xc0 && decoder.InitialCycle { // with or without cycle counter and seems to be a target reset
20✔
492
                //n += copy(b[n:], fmt.Sprintln("warning:   Restart?   "))
3✔
493
                p.cycle = cycle + 1 // adjust cycle
3✔
494
                decoder.InitialCycle = false
3✔
495
        }
3✔
496
        if cycle == 0xc0 && p.cycle == 0xc0 && !decoder.InitialCycle { // with or without cycle counter and seems to be a normal case
24✔
497
                p.cycle = cycle + 1 // adjust cycle
7✔
498
        }
7✔
499
        if cycle != 0xc0 && !DisableCycleErrors { // with cycle counter and s.th. lost
22✔
500
                if cycle != p.cycle { // no cycle check for 0xc0 to avoid messages on every target reset and when no cycle counter is active
8✔
501
                        n += copy(b[n:], fmt.Sprintln("CYCLE_ERROR:\a", cycle, "!=", p.cycle, " (count=", emitter.TagEvents("CYCLE_ERROR")+1, ")"))
3✔
502
                        n += copy(b[n:], "                                         ") // len of location information plus stamp: 41 spaces - see NewlineIndent below - todo: make it generic
3✔
503
                        p.cycle = cycle                                               // adjust cycle
3✔
504
                }
3✔
505
                decoder.InitialCycle = false
5✔
506
                p.cycle++
5✔
507
        }
508

509
        var ok bool
17✔
510
        p.LutMutex.RLock()
17✔
511
        p.Trice, ok = p.Lut[triceID]
17✔
512
        if AddNewlineToEachTriceMessage {
17✔
513
                p.Trice.Strg += `\n` // this adds a newline to each single Trice message
×
514
        }
×
515
        p.LutMutex.RUnlock()
17✔
516
        if !ok {
19✔
517
                if p.packageFraming == packageFramingNone {
3✔
518
                        if decoder.Verbose {
1✔
519
                                n += copy(b[n:], fmt.Sprintln("wrn:\adiscarding first byte", p.B0[0], "from:"))
×
520
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
×
521
                        }
×
522
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
523
                        p.B = p.B0
1✔
524
                } else {
1✔
525
                        n += copy(b[n:], fmt.Sprintln("WARNING:\aunknown ID ", triceID, "- ignoring trice ending with:"))
1✔
526
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
527
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
528
                        p.B = p.B[:0] // discard all
1✔
529
                }
1✔
530
                return
2✔
531
        }
532

533
        n += p.sprintTrice(b[n:]) // use param info
15✔
534
        if len(p.B) < p.ParamSpace {
17✔
535
                if p.packageFraming == packageFramingNone {
3✔
536
                        if decoder.Verbose {
2✔
537
                                n += copy(b[n:], fmt.Sprintln("wrn:discarding first byte", p.B0[0], "from:"))
1✔
538
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
1✔
539
                        }
1✔
540
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
541
                        p.B = p.B0
1✔
542
                } else {
1✔
543
                        n += copy(b[n:], fmt.Sprintln("ERROR:ignoring data garbage:"))
1✔
544
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
545
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
546
                        p.B = p.B[:0] // discard all
1✔
547
                }
1✔
548
        } else {
13✔
549
                if p.packageFraming != packageFramingNone { // COBS | TCOBS are exact
24✔
550
                        p.B = p.B[p.ParamSpace:] // drop param info
11✔
551
                } else { // no package framing
13✔
552
                        padding := (p.ParamSpace + 3) & ^3
2✔
553
                        if padding <= len(p.B) {
3✔
554
                                p.B = p.B[padding:]
1✔
555
                        } else {
2✔
556
                                // n += copy(b[n:], fmt.Sprintln("wrn: cannot discard padding bytes", ))
1✔
557
                        }
1✔
558
                }
559
        }
560
        return
15✔
561
}
562

563
// sprintTrice writes a trice string or appropriate message into b and returns that len.
564
//
565
// p.Trice.Type is the received trice, in fact the name from til.json.
566
func (p *trexDec) sprintTrice(b []byte) (n int) {
25✔
567

25✔
568
        isSAlias := strings.HasPrefix(p.Trice.Strg, id.SAliasStrgPrefix) && strings.HasSuffix(p.Trice.Strg, id.SAliasStrgSuffix)
25✔
569
        if isSAlias { // A SAlias Strg is covered with id.SAliasStrgPrefix and id.SAliasStrgSuffix in til.json and it needs to be replaced with "%s" here.
26✔
570
                p.Trice.Strg = "%s" // See appropriate comment inside insertTriceIDs().
1✔
571
        }
1✔
572

573
        p.pFmt, p.u = decoder.UReplaceN(p.Trice.Strg)
25✔
574

25✔
575
        // remove Assert* from triceAssert* name if found
25✔
576
        before, _, found := strings.Cut(p.Trice.Type, "Assert")
25✔
577
        if found {
26✔
578
                p.Trice.Type = strings.TrimSpace(before)
1✔
579
        }
1✔
580

581
        triceType, err := id.ConstructFullTriceInfo(p.Trice.Type, len(p.u))
25✔
582

25✔
583
        if err != nil {
26✔
584
                n += copy(b[n:], fmt.Sprintln("err:ConstructFullTriceInfo failed with:", p.Trice.Type, len(p.B), "- ignoring package:"))
1✔
585
                return
1✔
586
        }
1✔
587
        ucTriceTypeReceived := strings.ToUpper(p.Trice.Type)   // examples: TRICE_S,   TRICE,   TRICE32,   TRICE16_2
24✔
588
        ucTriceTypeReconstructed := strings.ToUpper(triceType) // examples: TRICE32_S, TRICE0,  TRICE32_4, TRICE16_2
24✔
589
        for _, s := range cobsFunctionPtrList {                // walk through the list and try to find a match for execution
404✔
590
                if s.triceType == ucTriceTypeReconstructed || s.triceType == ucTriceTypeReceived { // match list entry "TRICE..."
403✔
591
                        if len(p.B) < p.ParamSpace {
26✔
592
                                n += copy(b[n:], fmt.Sprintln("err:len(p.B) =", len(p.B), "< p.ParamSpace = ", p.ParamSpace, "- ignoring package:"))
3✔
593
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:len(p.B)])))
3✔
594
                                n += copy(b[n:], fmt.Sprintln(decoder.Hints))
3✔
595
                                return
3✔
596
                        }
3✔
597
                        if p.ParamSpace != (s.bitWidth>>3)*s.paramCount {
23✔
598
                                if !isSpecialCaseTriceType(s.triceType) {
4✔
599
                                        n += copy(b[n:], fmt.Sprintln("err:s.triceType =", s.triceType, "ParamSpace =", p.ParamSpace, "not matching with bitWidth ", s.bitWidth, "and paramCount", s.paramCount, "- ignoring package:"))
1✔
600
                                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:len(p.B)])))
1✔
601
                                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
602
                                        return
1✔
603
                                }
1✔
604
                        }
605
                        p.pFmt = applyMultilineIndent(p.pFmt)
19✔
606

19✔
607
                        n += s.triceFn(p, b, s.bitWidth, s.paramCount) // match found, call handler
19✔
608
                        return
19✔
609
                }
610
        }
611
        n += copy(b[n:], fmt.Sprintln("err:Unknown trice.Type:", p.Trice.Type, "and", triceType, "not matching - ignoring trice data:"))
1✔
612
        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:p.ParamSpace])))
1✔
613
        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
614
        return
1✔
615
}
616

617
func isSpecialCaseTriceType(triceType string) bool {
5✔
618
        _, ok := specialCaseTriceTypes[strings.ToUpper(triceType)]
5✔
619
        return ok
5✔
620
}
5✔
621

622
func applyMultilineIndent(format string) string {
22✔
623
        segments := strings.Split(format, `\n`)
22✔
624
        if len(segments) < 3 {
42✔
625
                return format
20✔
626
        }
20✔
627
        if decoder.NewlineIndent == -1 {
4✔
628
                decoder.NewlineIndent = 12 + 1
2✔
629
                if !(id.LIFnJSON == "off" || id.LIFnJSON == "none") {
3✔
630
                        decoder.NewlineIndent += 28
1✔
631
                }
1✔
632
                if decoder.ShowID != "" {
3✔
633
                        decoder.NewlineIndent += 5
1✔
634
                }
1✔
635
        }
636
        skip := `\n` + strings.Repeat(" ", decoder.NewlineIndent)
2✔
637
        return strings.TrimRight(strings.Join(segments, skip), " ")
2✔
638
}
639

640
func splitChannelFormat(format string) (prefix string, itemFormat string, hadTrailingNewline bool) {
15✔
641
        before, after, found := strings.Cut(format, ":")
15✔
642
        if found {
24✔
643
                prefix = before + ":"
9✔
644
        } else {
15✔
645
                after = format
6✔
646
        }
6✔
647
        itemFormat = strings.TrimSuffix(after, `\n`)
15✔
648
        hadTrailingNewline = len(itemFormat) < len(after)
15✔
649
        return
15✔
650
}
651

652
func normalizeBufferItemFormat(format string) (normalized string, kind fmtspec.Kind) {
13✔
653
        // Buffer-format decoding uses a single repeated item verb. Issue #649 added
13✔
654
        // proper support for C length modifiers in the main decoder path, and the
13✔
655
        // buffer path must apply the same normalization to avoid raw `%ld`, `%zx`,
13✔
656
        // `%lx` or `%llx` reaching Go fmt unchanged.
13✔
657
        normalized, specs := fmtspec.Normalize(format)
13✔
658
        if len(specs) == 0 {
13✔
NEW
659
                return format, fmtspec.KindSigned
×
NEW
660
        }
×
661
        return normalized, specs[0].Kind
13✔
662
}
663

664
func bufferValue8(v byte, kind fmtspec.Kind) interface{} {
28✔
665
        switch kind {
28✔
NEW
666
        case fmtspec.KindUnsigned:
×
NEW
667
                return v
×
668
        case fmtspec.KindBasedInteger:
12✔
669
                if decoder.Unsigned {
24✔
670
                        return v
12✔
671
                }
12✔
NEW
672
                return int8(v)
×
673
        default:
16✔
674
                return int8(v)
16✔
675
        }
676
}
677

678
func bufferValue16(v uint16, kind fmtspec.Kind) interface{} {
2✔
679
        switch kind {
2✔
NEW
680
        case fmtspec.KindUnsigned:
×
NEW
681
                return v
×
NEW
682
        case fmtspec.KindBasedInteger:
×
NEW
683
                if decoder.Unsigned {
×
NEW
684
                        return v
×
NEW
685
                }
×
NEW
686
                return int16(v)
×
687
        default:
2✔
688
                return int16(v)
2✔
689
        }
690
}
691

692
func bufferValue32(v uint32, kind fmtspec.Kind) interface{} {
2✔
693
        switch kind {
2✔
NEW
694
        case fmtspec.KindUnsigned:
×
NEW
695
                return v
×
NEW
696
        case fmtspec.KindBasedInteger:
×
NEW
697
                if decoder.Unsigned {
×
NEW
698
                        return v
×
NEW
699
                }
×
NEW
700
                return int32(v)
×
701
        default:
2✔
702
                return int32(v)
2✔
703
        }
704
}
705

706
func bufferValue64(v uint64, kind fmtspec.Kind) interface{} {
2✔
707
        switch kind {
2✔
NEW
708
        case fmtspec.KindUnsigned:
×
NEW
709
                return v
×
NEW
710
        case fmtspec.KindBasedInteger:
×
NEW
711
                if decoder.Unsigned {
×
NEW
712
                        return v
×
NEW
713
                }
×
NEW
714
                return int64(v)
×
715
        default:
2✔
716
                return int64(v)
2✔
717
        }
718
}
719

720
// triceTypeFn is the type for cobsFunctionPtrList elements.
721
type triceTypeFn struct {
722
        triceType  string                                              // triceType describes if parameters, the parameter bit width or if the parameter is a string.
723
        triceFn    func(p *trexDec, b []byte, bitwidth, count int) int // triceFn performs the conversion to the output string.
724
        ParamSpace int                                                 // ParamSpace is the count of bytes allocated for the parameters.
725
        bitWidth   int                                                 // bitWidth is the individual parameter width.
726
        paramCount int                                                 // paramCount is the amount pf parameters for the format string, which must match the count of format specifiers.
727
}
728

729
// cobsFunctionPtrList is a function pointer list.
730
var cobsFunctionPtrList = [...]triceTypeFn{
731
        {"TRICE_0", (*trexDec).trice0, 0, 0, 0},
732
        {"TRICE8_1", (*trexDec).unSignedOrSignedOut, 1, 8, 1},
733
        {"TRICE8_2", (*trexDec).unSignedOrSignedOut, 2, 8, 2},
734
        {"TRICE8_3", (*trexDec).unSignedOrSignedOut, 3, 8, 3},
735
        {"TRICE8_4", (*trexDec).unSignedOrSignedOut, 4, 8, 4},
736
        {"TRICE8_5", (*trexDec).unSignedOrSignedOut, 5, 8, 5},
737
        {"TRICE8_6", (*trexDec).unSignedOrSignedOut, 6, 8, 6},
738
        {"TRICE8_7", (*trexDec).unSignedOrSignedOut, 7, 8, 7},
739
        {"TRICE8_8", (*trexDec).unSignedOrSignedOut, 8, 8, 8},
740
        {"TRICE8_9", (*trexDec).unSignedOrSignedOut, 9, 8, 9},
741
        {"TRICE8_10", (*trexDec).unSignedOrSignedOut, 10, 8, 10},
742
        {"TRICE8_11", (*trexDec).unSignedOrSignedOut, 11, 8, 11},
743
        {"TRICE8_12", (*trexDec).unSignedOrSignedOut, 12, 8, 12},
744
        {"TRICE16_1", (*trexDec).unSignedOrSignedOut, 2, 16, 1},
745
        {"TRICE16_2", (*trexDec).unSignedOrSignedOut, 4, 16, 2},
746
        {"TRICE16_3", (*trexDec).unSignedOrSignedOut, 6, 16, 3},
747
        {"TRICE16_4", (*trexDec).unSignedOrSignedOut, 8, 16, 4},
748
        {"TRICE16_5", (*trexDec).unSignedOrSignedOut, 10, 16, 5},
749
        {"TRICE16_6", (*trexDec).unSignedOrSignedOut, 12, 16, 6},
750
        {"TRICE16_7", (*trexDec).unSignedOrSignedOut, 14, 16, 7},
751
        {"TRICE16_8", (*trexDec).unSignedOrSignedOut, 16, 16, 8},
752
        {"TRICE16_9", (*trexDec).unSignedOrSignedOut, 18, 16, 9},
753
        {"TRICE16_10", (*trexDec).unSignedOrSignedOut, 20, 16, 10},
754
        {"TRICE16_11", (*trexDec).unSignedOrSignedOut, 22, 16, 11},
755
        {"TRICE16_12", (*trexDec).unSignedOrSignedOut, 24, 16, 12},
756
        {"TRICE32_1", (*trexDec).unSignedOrSignedOut, 4, 32, 1},
757
        {"TRICE32_2", (*trexDec).unSignedOrSignedOut, 8, 32, 2},
758
        {"TRICE32_3", (*trexDec).unSignedOrSignedOut, 12, 32, 3},
759
        {"TRICE32_4", (*trexDec).unSignedOrSignedOut, 16, 32, 4},
760
        {"TRICE32_5", (*trexDec).unSignedOrSignedOut, 20, 32, 5},
761
        {"TRICE32_6", (*trexDec).unSignedOrSignedOut, 24, 32, 6},
762
        {"TRICE32_7", (*trexDec).unSignedOrSignedOut, 28, 32, 7},
763
        {"TRICE32_8", (*trexDec).unSignedOrSignedOut, 32, 32, 8},
764
        {"TRICE32_9", (*trexDec).unSignedOrSignedOut, 36, 32, 9},
765
        {"TRICE32_10", (*trexDec).unSignedOrSignedOut, 40, 32, 10},
766
        {"TRICE32_11", (*trexDec).unSignedOrSignedOut, 44, 32, 11},
767
        {"TRICE32_12", (*trexDec).unSignedOrSignedOut, 48, 32, 12},
768
        {"TRICE64_1", (*trexDec).unSignedOrSignedOut, 8, 64, 1},
769
        {"TRICE64_2", (*trexDec).unSignedOrSignedOut, 16, 64, 2},
770
        {"TRICE64_3", (*trexDec).unSignedOrSignedOut, 24, 64, 3},
771
        {"TRICE64_4", (*trexDec).unSignedOrSignedOut, 32, 64, 4},
772
        {"TRICE64_5", (*trexDec).unSignedOrSignedOut, 40, 64, 5},
773
        {"TRICE64_6", (*trexDec).unSignedOrSignedOut, 48, 64, 6},
774
        {"TRICE64_7", (*trexDec).unSignedOrSignedOut, 56, 64, 7},
775
        {"TRICE64_8", (*trexDec).unSignedOrSignedOut, 64, 64, 8},
776
        {"TRICE64_9", (*trexDec).unSignedOrSignedOut, 72, 64, 9},
777
        {"TRICE64_10", (*trexDec).unSignedOrSignedOut, 80, 64, 10},
778
        {"TRICE64_11", (*trexDec).unSignedOrSignedOut, 88, 64, 11},
779
        {"TRICE64_12", (*trexDec).unSignedOrSignedOut, 96, 64, 12},
780

781
        {"TRICES", (*trexDec).triceS, -1, 0, 0},
782
        {"TRICEN", (*trexDec).triceN, -1, 0, 0},
783

784
        {"TRICE8F", (*trexDec).trice8F, -1, 0, 0},
785
        {"TRICE16F", (*trexDec).trice16F, -1, 0, 0},
786
        {"TRICE32F", (*trexDec).trice32F, -1, 0, 0},
787
        {"TRICE64F", (*trexDec).trice64F, -1, 0, 0},
788

789
        {"TRICE8B", (*trexDec).trice8B, -1, 0, 0},
790
        {"TRICE16B", (*trexDec).trice16B, -1, 0, 0},
791
        {"TRICE32B", (*trexDec).trice32B, -1, 0, 0},
792
        {"TRICE64B", (*trexDec).trice64B, -1, 0, 0},
793
}
794

795
// triceN converts dynamic strings.
796
func (p *trexDec) triceN(b []byte, _ int, _ int) int {
2✔
797
        s := string(p.B[:p.ParamSpace])
2✔
798
        // todo: evaluate p.Trice.Strg, use p.SLen and do whatever should be done
2✔
799
        return copy(b, fmt.Sprintf(p.Trice.Strg, s))
2✔
800
}
2✔
801

802
// triceS converts dynamic strings.
803
func (p *trexDec) triceS(b []byte, _ int, _ int) int {
2✔
804
        s := string(p.B[:p.ParamSpace])
2✔
805
        return copy(b, fmt.Sprintf(p.Trice.Strg, s))
2✔
806
}
2✔
807

808
// triceB converts dynamic buffers.
809
func (p *trexDec) trice8B(b []byte, _ int, _ int) (n int) {
7✔
810
        if decoder.DebugOut {
8✔
811
                fmt.Fprintln(p.W, string(p.B))
1✔
812
        }
1✔
813
        s := p.B[:p.ParamSpace]
7✔
814
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
7✔
815
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
7✔
816
        if prefix != "" {
9✔
817
                n += copy(b[n:], prefix)
2✔
818
        }
2✔
819

820
        for i := 0; i < len(s); i++ {
35✔
821
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue8(s[i], itemKind)))
28✔
822
        }
28✔
823
        if addLineBreak {
8✔
824
                n += copy(b[n:], fmt.Sprintln())
1✔
825
        }
1✔
826
        return
7✔
827
}
828

829
// trice16B converts dynamic buffers.
830
func (p *trexDec) trice16B(b []byte, _ int, _ int) (n int) {
2✔
831
        if decoder.DebugOut {
3✔
832
                fmt.Fprintln(p.W, string(p.B))
1✔
833
        }
1✔
834
        s := p.B[:p.ParamSpace]
2✔
835
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
836
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
837
        if prefix != "" {
4✔
838
                n += copy(b[n:], prefix)
2✔
839
        }
2✔
840

841
        for i := 0; i < len(s); i += 2 {
4✔
842
                nn := binary.LittleEndian.Uint16(s[i:])
2✔
843
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue16(nn, itemKind)))
2✔
844
        }
2✔
845
        if addLineBreak {
3✔
846
                n += copy(b[n:], fmt.Sprintln())
1✔
847
        }
1✔
848

849
        return
2✔
850
}
851

852
// trice32B converts dynamic buffers.
853
func (p *trexDec) trice32B(b []byte, _ int, _ int) (n int) {
2✔
854
        if decoder.DebugOut {
3✔
855
                fmt.Fprintln(p.W, string(p.B))
1✔
856
        }
1✔
857
        s := p.B[:p.ParamSpace]
2✔
858
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
859
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
860
        if prefix != "" {
4✔
861
                n += copy(b[n:], prefix)
2✔
862
        }
2✔
863

864
        for i := 0; i < len(s); i += 4 {
4✔
865
                nn := binary.LittleEndian.Uint32(s[i:])
2✔
866
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue32(nn, itemKind)))
2✔
867
        }
2✔
868
        if addLineBreak {
3✔
869
                n += copy(b[n:], fmt.Sprintln())
1✔
870
        }
1✔
871
        return
2✔
872
}
873

874
// trice64B converts dynamic buffers.
875
func (p *trexDec) trice64B(b []byte, _ int, _ int) (n int) {
2✔
876
        if decoder.DebugOut {
3✔
877
                fmt.Fprintln(p.W, string(p.B))
1✔
878
        }
1✔
879
        s := p.B[:p.ParamSpace]
2✔
880
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
881
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
882
        if prefix != "" {
4✔
883
                n += copy(b[n:], prefix)
2✔
884
        }
2✔
885

886
        for i := 0; i < len(s); i += 8 {
4✔
887
                nn := binary.LittleEndian.Uint64(s[i:])
2✔
888
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue64(nn, itemKind)))
2✔
889
        }
2✔
890
        if addLineBreak {
3✔
891
                n += copy(b[n:], fmt.Sprintln())
1✔
892
        }
1✔
893
        return
2✔
894
}
895

896
// trice8F display function call with 8-bit parameters.
897
func (p *trexDec) trice8F(b []byte, _ int, _ int) (n int) {
2✔
898
        if decoder.DebugOut {
3✔
899
                fmt.Fprintln(p.W, string(p.B))
1✔
900
        }
1✔
901
        s := p.B[:p.ParamSpace]
2✔
902
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
903
        for i := 0; i < len(s); i++ {
5✔
904
                n += copy(b[n:], fmt.Sprintf("(%02x)", s[i]))
3✔
905
        }
3✔
906
        n += copy(b[n:], fmt.Sprintln())
2✔
907
        return
2✔
908
}
909

910
// trice16F display function call with 16-bit parameters.
911
func (p *trexDec) trice16F(b []byte, _ int, _ int) (n int) {
2✔
912
        if decoder.DebugOut {
3✔
913
                fmt.Fprintln(p.W, string(p.B))
1✔
914
        }
1✔
915
        s := p.B[:p.ParamSpace]
2✔
916
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
917
        for i := 0; i < len(s); i += 2 {
4✔
918
                n += copy(b[n:], fmt.Sprintf("(%04x)", binary.LittleEndian.Uint16(s[i:])))
2✔
919
        }
2✔
920
        n += copy(b[n:], fmt.Sprintln())
2✔
921
        return
2✔
922
}
923

924
// trice32F display function call with 32-bit parameters.
925
func (p *trexDec) trice32F(b []byte, _ int, _ int) (n int) {
2✔
926
        if decoder.DebugOut {
3✔
927
                fmt.Fprintln(p.W, string(p.B))
1✔
928
        }
1✔
929
        s := p.B[:p.ParamSpace]
2✔
930
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
931
        for i := 0; i < len(s); i += 4 {
4✔
932
                n += copy(b[n:], fmt.Sprintf("(%08x)", binary.LittleEndian.Uint32(s[i:])))
2✔
933
        }
2✔
934
        n += copy(b[n:], fmt.Sprintln())
2✔
935
        return
2✔
936
}
937

938
// trice64F display function call with 64-bit parameters.
939
func (p *trexDec) trice64F(b []byte, _ int, _ int) (n int) {
2✔
940
        if decoder.DebugOut {
3✔
941
                fmt.Fprintln(p.W, string(p.B))
1✔
942
        }
1✔
943
        s := p.B[:p.ParamSpace]
2✔
944
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
945
        for i := 0; i < len(s); i += 8 {
4✔
946
                n += copy(b[n:], fmt.Sprintf("(%016x)", binary.LittleEndian.Uint64(s[i:])))
2✔
947
        }
2✔
948
        n += copy(b[n:], fmt.Sprintln())
2✔
949
        return
2✔
950
}
951

952
// trice0 prints the trice format string.
953
func (p *trexDec) trice0(b []byte, _ int, _ int) int {
7✔
954
        return copy(b, fmt.Sprint(p.pFmt))
7✔
955
}
7✔
956

957
// unSignedOrSignedOut prints p.B according to the format string.
958
func (p *trexDec) unSignedOrSignedOut(b []byte, bitwidth, count int) int {
25✔
959
        if len(p.u) != count {
26✔
960
                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier count inside", p.Trice.Type, p.Trice.Strg))
1✔
961
        }
1✔
962
        v := make([]interface{}, 32768) // theoretical 2^15 bytes could arrive
24✔
963
        switch bitwidth {
24✔
964
        case 8:
7✔
965
                for i, f := range p.u {
18✔
966
                        switch f {
11✔
967
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
3✔
968
                                v[i] = p.B[i]
3✔
969
                        case decoder.SignedFormatSpecifier:
6✔
970
                                v[i] = int8(p.B[i])
6✔
971
                        case decoder.BooleanFormatSpecifier:
1✔
972
                                v[i] = p.B[i] != 0
1✔
973
                        default:
1✔
974
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier (float?) inside", p.Trice.Type, p.Trice.Strg))
1✔
975
                        }
976
                }
977
        case 16:
4✔
978
                for i, f := range p.u {
10✔
979
                        n := p.ReadU16(p.B[2*i:])
6✔
980
                        switch f {
6✔
981
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
1✔
982
                                v[i] = n
1✔
983
                        case decoder.SignedFormatSpecifier:
3✔
984
                                v[i] = int16(n)
3✔
985
                        case decoder.BooleanFormatSpecifier:
1✔
986
                                v[i] = n != 0
1✔
987
                        default:
1✔
988
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier (float?) inside", p.Trice.Type, p.Trice.Strg))
1✔
989
                        }
990
                }
991
        case 32:
9✔
992
                for i, f := range p.u {
23✔
993
                        n := p.ReadU32(p.B[4*i:])
14✔
994
                        switch f {
14✔
995
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
4✔
996
                                v[i] = n
4✔
997
                        case decoder.SignedFormatSpecifier:
7✔
998
                                v[i] = int32(n)
7✔
999
                        case decoder.FloatFormatSpecifier:
1✔
1000
                                v[i] = math.Float32frombits(n)
1✔
1001
                        case decoder.BooleanFormatSpecifier:
1✔
1002
                                v[i] = n != 0
1✔
1003
                        default:
1✔
1004
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier inside", p.Trice.Type, p.Trice.Strg))
1✔
1005
                        }
1006
                }
1007
        case 64:
4✔
1008
                for i, f := range p.u {
10✔
1009
                        n := p.ReadU64(p.B[8*i:])
6✔
1010
                        switch f {
6✔
1011
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
2✔
1012
                                v[i] = n
2✔
1013
                        case decoder.SignedFormatSpecifier:
2✔
1014
                                v[i] = int64(n)
2✔
1015
                        case decoder.FloatFormatSpecifier:
1✔
1016
                                v[i] = math.Float64frombits(n)
1✔
1017
                        case decoder.BooleanFormatSpecifier:
1✔
1018
                                v[i] = n != 0
1✔
1019
                        default:
×
1020
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier inside", p.Trice.Type, p.Trice.Strg))
×
1021
                        }
1022
                }
1023
        }
1024
        return copy(b, fmt.Sprintf(p.pFmt, v[:len(p.u)]...))
21✔
1025
}
1026

1027
var testTableVirgin = true
1028

1029
// printTestTableLine is used to generate testdata
1030
func (p *trexDec) printTestTableLine(n int) {
1✔
1031
        if emitter.NextLine || testTableVirgin {
2✔
1032
                emitter.NextLine = false
1✔
1033
                testTableVirgin = false
1✔
1034
                fmt.Printf("{ []byte{ ")
1✔
1035
        }
1✔
1036
        for _, b := range p.IBuf[0:n] { // just to see trice bytes per trice
3✔
1037
                fmt.Printf("%3d,", b)
2✔
1038
        }
2✔
1039
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc