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

rokath / trice / 30218872366

26 Jul 2026 08:23PM UTC coverage: 86.601%. First build
30218872366

Pull #706

github

web-flow
Merge 2c88a83fe into 05c40a991
Pull Request #706: Vis

1010 of 1215 new or added lines in 8 files covered. (83.13%)

7090 of 8187 relevant lines covered (86.6%)

748.01 hits per line

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

88.25
/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 // selector-0 extension record without Trice ID, timestamp or cycle counter
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
        "TRICE8C": {}, "TRICE16C": {}, "TRICE32C": {}, "TRICE64C": {},
59
        "TRICE_S": {}, "TRICE_N": {}, "TRICE_B": {}, "TRICE_F": {},
60
        "TRICE8_B": {}, "TRICE16_B": {}, "TRICE32_B": {}, "TRICE64_B": {},
61
        "TRICE8_F": {}, "TRICE16_F": {}, "TRICE32_F": {}, "TRICE64_F": {},
62
        "TRICE8_C": {}, "TRICE16_C": {}, "TRICE32_C": {}, "TRICE64_C": {},
63
}
64

65
func init() {
13✔
66
        decoder.Register("TREX", New)
13✔
67
}
13✔
68

69
// trexDec is the decoder instance for TREX-encoded Trices.
70
type trexDec struct {
71
        decoder.DecoderData
72
        cycle          uint8  // cycle date: c0...bf
73
        pFmt           string // modified trice format string: %u -> %d
74
        u              []int  // 1: modified format string positions:  %u -> %d, 2: float (%f)
75
        packageFraming int
76
        visEnabled     bool              // avoids record-capture work unless the translator has an active -vis router
77
        visRecord      decoder.VisRecord // typed data for the most recently decoded supported numeric Trice
78
        visValid       bool              // true only when visRecord belongs to the most recent successful Read
79
}
80

81
// SetVisRecordEnabled controls optional typed-record capture without changing normal decoding.
82
func (p *trexDec) SetVisRecordEnabled(enabled bool) {
9✔
83
        p.visEnabled = enabled
9✔
84
        if !enabled {
16✔
85
                p.visRecord = decoder.VisRecord{}
7✔
86
                p.visValid = false
7✔
87
        }
7✔
88
}
89

90
// VisRecord returns the typed fixed-width numeric record produced by the most recent Read.
91
//
92
// Returning a value copy prevents downstream visualization processing from
93
// retaining or mutating decoder-owned state across subsequent reads.
94
func (p *trexDec) VisRecord() (decoder.VisRecord, bool) {
11✔
95
        return p.visRecord, p.visValid
11✔
96
}
11✔
97

98
// New provides a TREX decoder instance.
99
//
100
// in is the input byte stream source.
101
func New(w io.Writer, lut id.TriceIDLookUp, m *sync.RWMutex, li id.TriceIDLookUpLI, in io.Reader, endian bool) decoder.Decoder {
24✔
102
        // Todo: rewrite using the TCOBS Reader. The provided in io.Reader provides a raw data stream.
24✔
103
        // https://github.com/rokath/tcobs/blob/master/TCOBSv1/read.go -> use NewDecoder ...
24✔
104

24✔
105
        p := &trexDec{
24✔
106
                DecoderData: decoder.NewDecoderData(decoder.Config{
24✔
107
                        Out:         w,
24✔
108
                        LUT:         lut,
24✔
109
                        LUTMutex:    m,
24✔
110
                        LI:          li,
24✔
111
                        In:          in,
24✔
112
                        Endian:      endian,
24✔
113
                        NeedBuffers: true,
24✔
114
                }),
24✔
115
                cycle: 0xc0, // start value
24✔
116
        }
24✔
117

24✔
118
        switch strings.ToLower(decoder.PackageFraming) {
24✔
119
        case "cobs":
8✔
120
                p.packageFraming = packageFramingCOBS
8✔
121
        case "tcobs", "tcobsv1":
4✔
122
                p.packageFraming = packageFramingTCOBS
4✔
123
        case "tcobsv2":
1✔
124
                p.packageFraming = packageFramingTCOBSv2
1✔
125
        case "none":
11✔
126
                p.packageFraming = packageFramingNone
11✔
127
        default:
×
128
                log.Fatal("Invalid framing switch:\a", decoder.PackageFraming)
×
129
        }
130
        return p
24✔
131
}
132

133
// nextData reads with an inner reader a raw byte stream.
134
//
135
// When fewer than 4 bytes are available, nextData returns without processing.
136
// That means the incoming data stream is exhausted and a next try should be started a bit later.
137
// Some arrived bytes are kept internally and concatenated with the following bytes in a next Read.
138
// Afterwards 0 or at least 4 bytes are inside p.B
139
func (p *trexDec) nextData() {
117✔
140
        m, err := p.In.Read(p.InnerBuffer)      // use p.InnerBuffer as destination read buffer
117✔
141
        p.B = append(p.B, p.InnerBuffer[:m]...) // merge with leftovers
117✔
142
        if err != nil && err != io.EOF {        // some serious error
117✔
143
                log.Fatal("ERROR:internal reader error\a", err) // exit
×
144
        }
×
145
}
146

147
// nextPackage reads with an inner reader a TCOBSv1 encoded byte stream.
148
//
149
// When no terminating 0 is found in the incoming bytes nextPackage returns without action.
150
// That means the incoming data stream is exhausted and a next try should be started a bit later.
151
// Some arrived bytes are kept internally and concatenated with the following bytes in a next Read.
152
// When a terminating 0 is found in the incoming bytes ReadFromCOBS decodes the COBS package
153
// and returns it in b and its len in n. If more data arrived after the first terminating 0,
154
// these are kept internally and concatenated with the following bytes in a next Read.
155
func (p *trexDec) nextPackage() {
634✔
156
        // Here p.IBuf contains none or available bytes, what can be several trice messages.
634✔
157
        // So first try to process p.IBuf.
634✔
158
        var index int
634✔
159
        for {
1,270✔
160
                index = bytes.IndexByte(p.IBuf, 0) // find terminating 0
636✔
161
                if index == -1 {                   // p.IBuf has no complete COBS data, so try to read more input
1,263✔
162
                        m, err := p.In.Read(p.InnerBuffer)            // use p.InnerBuffer as bytes read buffer
627✔
163
                        p.IBuf = append(p.IBuf, p.InnerBuffer[:m]...) // merge with leftovers
627✔
164
                        if err != nil && err != io.EOF {              // some serious error
627✔
165
                                log.Fatal("ERROR:internal reader error\a", err) // exit
×
166
                        }
×
167
                        index = bytes.IndexByte(p.IBuf, 0) // find terminating 0
627✔
168
                        if index == -1 {                   // p.IBuf has no complete COBS data, so leave
1,242✔
169
                                // Even err could be io.EOF, some valid data possibly in p.iBUf.
615✔
170
                                // In case of file input (J-LINK usage) a plug off is not detectable here.
615✔
171
                                return // no terminating 0, nothing to do
615✔
172
                        }
615✔
173
                }
174
                if index != 0 {
40✔
175
                        break
19✔
176
                }
177
                p.IBuf = p.IBuf[1:] // skip empty frames from 32-bit direct-output delimiter padding
2✔
178
        }
179
        if decoder.TestTableMode {
19✔
180
                p.printTestTableLine(index + 1)
×
181
        }
×
182
        // here a complete COBS or TCOBS package exists
183
        if decoder.DebugOut { // Debug output
21✔
184
                fmt.Fprintf(p.W, "%s: ", decoder.PackageFraming)
2✔
185
                decoder.Dump(p.W, p.IBuf[:index+1])
2✔
186
        }
2✔
187

188
        frame := p.IBuf[:index]
19✔
189

19✔
190
        switch p.packageFraming {
19✔
191

192
        case packageFramingCOBS:
12✔
193
                p.B = p.B0                      // make([]byte, decoder.DefaultSize) // todo: avoid allocation
12✔
194
                n, e := cobs.Decode(p.B, frame) // if index is 0, an empty buffer is decoded
12✔
195
                p.IBuf = p.IBuf[index+1:]       // step forward (next package data in p.IBuf now, if any)
12✔
196
                if e != nil {
12✔
197
                        if decoder.Verbose {
×
198
                                fmt.Println("\ainconsistent COBS buffer!") // show also terminating 0
×
199
                        }
×
200
                }
201
                p.B = p.B[:n]
12✔
202

203
        case packageFramingTCOBS:
7✔
204
        repeat:
7✔
205
                p.B = p.B0                       // make([]byte, decoder.DefaultSize) // todo: avoid allocation
8✔
206
                n, e := tcobs.Decode(p.B, frame) // if index is 0, an empty buffer is decoded
8✔
207
                // from merging: p.IBuf = p.IBuf[index+1:]        // step forward (next package data in p.IBuf now, if any)
8✔
208
                if e != nil {
10✔
209
                        // remove 3 lines if they exist - see issue #403 for the reason.
2✔
210
                        s := strings.SplitN(strings.ReplaceAll(string(frame), "\r\n", "\n"), "\n", 4)
2✔
211
                        var bytesCount int
2✔
212
                        if len(s) >= 3 {
3✔
213
                                var newLines int
1✔
214
                                fmt.Println(s[0])
1✔
215
                                fmt.Println(s[1])
1✔
216
                                fmt.Println(s[2])
1✔
217
                                for _, b := range frame {
148✔
218
                                        frame = frame[1:]
147✔
219
                                        bytesCount++
147✔
220
                                        if b == 10 {
150✔
221
                                                newLines++
3✔
222
                                                if newLines == 3 {
4✔
223
                                                        break
1✔
224
                                                }
225
                                        }
226
                                        continue
146✔
227
                                }
228
                                index -= bytesCount
1✔
229
                                goto repeat
1✔
230
                        }
231
                        if decoder.Verbose {
1✔
232
                                fmt.Println(e, "\ainconsistent TCOBSv1 buffer:")
×
233
                                fmt.Println(e, hex.Dump(frame)) // show also terminating 0
×
234
                        }
×
235
                        e = nil
1✔
236
                        p.B = p.B[:0]
1✔
237
                        p.IBuf = p.IBuf[index+1:] // step forward (next package data in p.IBuf now, if any) // from merging:
1✔
238
                } else {
6✔
239
                        p.B = p.B[len(p.B)-n:]    // buffer is filled from the end
6✔
240
                        p.IBuf = p.IBuf[index+1:] // step forward (next package data in p.IBuf now, if any) // from merging:
6✔
241
                }
6✔
242
        default:
×
243
                log.Fatalln("unexpected execution path", p.packageFraming)
×
244
        }
245

246
        if decoder.DebugOut { // Debug output
21✔
247
                fmt.Fprint(p.W, "->TRICE: ")
2✔
248
                decoder.Dump(p.W, p.B)
2✔
249
        }
2✔
250

251
        if cipher.Password != "" { // encrypted
26✔
252
                cipher.Decrypt(p.B, p.B)
7✔
253
                if decoder.DebugOut { // Debug output
8✔
254
                        fmt.Fprint(p.W, "-> DEC:  ")
1✔
255
                        decoder.Dump(p.W, p.B)
1✔
256
                }
1✔
257
        }
258
}
259

260
// isZero reports whether all bytes in the slice are zero.
261
func isZero(bytes []byte) bool {
638✔
262
        b := byte(0)
638✔
263
        for _, s := range bytes {
654✔
264
                b |= s
16✔
265
        }
16✔
266
        return b == 0
638✔
267
}
268

269
// removeZeroHiByte discards one high-order padding zero byte from a candidate frame.
270
//
271
// The removed byte depends on configured endianness.
272
func (p *trexDec) removeZeroHiByte(s []byte) (r []byte) {
2✔
273
        // The package interpreter does not know the number of padding zeroes, so it needs to discard them one by one.
2✔
274
        // If they are not zero, this is an error.
2✔
275
        switch p.Endian {
2✔
276
        case decoder.BigEndian:
1✔
277
                // Big endian case: 00 00 AA AA C0 00 -> 00 AA AA C0 00 -> still typeX0 -> AA AA C0 00 -> ok next package
1✔
278
                if s[0] != 0 {
1✔
279
                        fmt.Println("unexpected case in line 273", string(s))
×
280
                }
×
281
                r = s[1:]
1✔
282
        case decoder.LittleEndian:
1✔
283
                // 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!
1✔
284
                if s[1] != 0 {
1✔
285
                        //log.Fatal("unexpected case", s)
×
286
                        // todo: This needs to be disabled for successfully running all test cases.
×
287
                        // BUT: deferred package framing NONE does not work
×
288
                }
×
289
                r = append(s[:1], s[2:]...)
1✔
290
        default:
×
291
                fmt.Println("unexpected case 927346193377", string(s))
×
292
        }
293
        return
2✔
294
}
295

296
// Read returns a single Trice conversion result or a single error message in b[:n].
297
// Read is the provided read method for TREX decoding and provides next string as byte slice.
298
//
299
// It uses inner reader p.In and internal id look-up table to fill b with a string.
300
// b is a slice of bytes with a len for the max expected string size.
301
// n is the count of read bytes inside b.
302
// Read returns usually one complete trice string or nothing but can return concatenated
303
// trice strings, each ending with a newline despite the last one, when messages added.
304
// Read does not process all internally read complete trice packages to be able later to
305
// separate Trices within one line to keep them separated for color processing.
306
// Therefore, Read needs to be called cyclically even after returning io.EOF to process internal data.
307
// When Read returns n=0, all processable complete trice packages are done,
308
// but the start of a following trice package can be already inside the internal buffer.
309
// In case of a not matching cycle, a warning message in trice format is prefixed.
310
// In case of invalid package data, error messages in trice format are returned and the package is dropped.
311
func (p *trexDec) Read(b []byte) (n int, err error) {
754✔
312
        decoder.BlankMetadata = false
754✔
313
        if p.visEnabled {
861✔
314
                // A failed, incomplete, or unsupported read must never expose the previous record again.
107✔
315
                p.visRecord = decoder.VisRecord{}
107✔
316
                p.visValid = false
107✔
317
        }
107✔
318
        if p.packageFraming == packageFramingNone {
870✔
319
                p.nextData() // returns all unprocessed data inside p.B
116✔
320
                p.B0 = p.B   // keep data for re-sync
116✔
321
        } else {
754✔
322
                if cipher.Password != "" && len(p.B) < 8 && isZero(p.B) {
1,256✔
323
                        p.B = p.B[:0] // Discard trailing zeroes. ATTENTION: incomplete trice messages containing many zeroes could be problematic here!
618✔
324
                }
618✔
325
                if len(p.B) == 1 { // one leftover byte cannot form a supported framed record
640✔
326
                        n += copy(b[n:], fmt.Sprintln("ERROR:\aunsupported short packet size 1 - ignoring package:"))
2✔
327
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
2✔
328
                        p.B = p.B[:0]
2✔
329
                        return n, nil
2✔
330
                }
2✔
331
                if len(p.B) == 0 { // last decoded package exhausted
1,261✔
332
                        p.nextPackage() // returns one decoded package inside p.B
625✔
333
                }
625✔
334
        }
335
        packageSize := len(p.B)
752✔
336
        if packageSize == 1 && p.packageFraming != packageFramingNone {
752✔
337
                n += copy(b[n:], fmt.Sprintln("ERROR:\aunsupported short packet size 1 - ignoring package:"))
×
338
                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
×
339
                p.B = p.B[:0]
×
340
                return n, nil
×
341
        }
×
342
        if packageSize < tyIdSize { // not enough data for a next package
1,471✔
343
                return
719✔
344
        }
719✔
345
        packed := p.B
33✔
346
        tyId := p.ReadU16(p.B)
33✔
347
        p.B = p.B[tyIdSize:]
33✔
348

33✔
349
        triceType := int(tyId >> decoder.IDBits) // most significant bit are the triceType
33✔
350
        if triceType == typeX0 {
37✔
351
                x0 := decoder.HandleTypeX0(packed, p.Endian, p.packageFraming == packageFramingNone)
4✔
352
                if x0.Consumed > len(packed) {
4✔
353
                        x0.Consumed = len(packed)
×
354
                }
×
355
                p.B = packed[x0.Consumed:]
4✔
356
                decoder.LastTriceID = 0
4✔
357
                decoder.TargetTimestamp = 0
4✔
358
                decoder.TargetTimestampSize = 0
4✔
359
                decoder.BlankMetadata = x0.BlankMetadata
4✔
360
                if x0.Text == "" {
4✔
361
                        return
×
362
                }
×
363
                n += copy(b[n:], x0.Text)
4✔
364
                return n, nil
4✔
365
        }
366

367
        triceID := id.TriceID(0x3FFF & tyId) // 14 least significant bits are the ID
29✔
368
        decoder.LastTriceID = triceID        // used for showID
29✔
369
        decoder.RecordForStatistics(triceID) // This is for the "trice log -stat" flag
29✔
370

29✔
371
        switch triceType {
29✔
372
        case typeS0: // no timestamp
16✔
373
                decoder.TargetTimestampSize = 0
16✔
374
        case typeS2: // 16-bit stamp
6✔
375
                decoder.TargetTimestampSize = 2
6✔
376
                if Doubled16BitID { // p.packageFraming == packageFramingNone || cipher.Password != "" {
8✔
377
                        if len(p.B) < 2 {
3✔
378
                                return // wait for more data
1✔
379
                        }
1✔
380

381
                        // Without encoding it needs to be done here.
382
                        // Also encrypted trice messages carry a double 16-bit ID.
383
                        p.B = p.B[tyIdSize:] // When target encoding is done, it removes the double 16-bit ID at the 16-bit timestamp trices.
1✔
384
                }
385
        case typeS4: // 32-bit stamp
7✔
386
                decoder.TargetTimestampSize = 4
7✔
387
        default:
×
388
                n += copy(b[n:], fmt.Sprintln("ERROR:\aunknown trice type", triceType, "(hint: IDBits value?)"))
×
389
                p.B = p.B[:0]
×
390
                return n, nil
×
391
        }
392

393
        if packageSize < tyIdSize+decoder.TargetTimestampSize+ncSize { // for non typeEX trices
29✔
394
                if p.packageFraming != packageFramingNone {
2✔
395
                        n += copy(b[n:], fmt.Sprintln("ERROR:\aunsupported short non-X0 packet size", packageSize, "- ignoring package:"))
1✔
396
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(packed)))
1✔
397
                        p.B = p.B[:0]
1✔
398
                        return n, nil
1✔
399
                }
1✔
400
                return // not enough data
×
401
        }
402

403
        // try to interpret
404
        switch triceType {
27✔
405
        case typeS0:
15✔
406
                decoder.TargetTimestamp = 0
15✔
407
        case typeS2: // 16-bit stamp
5✔
408
                decoder.TargetTimestamp = uint64(p.ReadU16(p.B))
5✔
409
        case typeS4: // 32-bit stamp
7✔
410
                decoder.TargetTimestamp = uint64(p.ReadU32(p.B))
7✔
411
        }
412

413
        p.B = p.B[decoder.TargetTimestampSize:]
27✔
414

27✔
415
        if len(p.B) < 2 {
28✔
416
                return // wait for more data
1✔
417
        }
1✔
418
        nc := p.ReadU16(p.B) // n = number of data bytes (without timestamp), most significant bit is the count encoding, c = cycle
26✔
419
        p.B = p.B[ncSize:]
26✔
420

26✔
421
        var cycle uint8
26✔
422
        if nc>>15 == 1 { // special case: more than data 127 bytes
27✔
423
                // C code: #define TRICE_LCNT(count) TRICE_PUT16( (0x8000 | (count)) );
1✔
424
                cycle = p.cycle                 // cycle is not transmitted, so set expected value
1✔
425
                p.ParamSpace = int(0x7FFF & nc) // 15 bit for data byte count excluding timestamp
1✔
426
        } else {
26✔
427
                // C code: #define TRICE_CNTC(count) TRICE_PUT16( ((count)<<8) | TRICE_CYCLE )
25✔
428
                cycle = uint8(nc)           // low byte is cycle
25✔
429
                p.ParamSpace = int(nc >> 8) // high byte is 7 bit number of bytes for data count excluding timestamp
25✔
430
        }
25✔
431

432
        p.TriceSize = tyIdSize + decoder.TargetTimestampSize + ncSize + p.ParamSpace
26✔
433
        if p.TriceSize > packageSize { //  '>' for multiple trices in one package (case TriceOutMultiPackMode), todo: discuss all possible variants
28✔
434
                if p.packageFraming == packageFramingNone {
3✔
435
                        if decoder.Verbose {
1✔
436
                                n += copy(b[n:], fmt.Sprintln("wrn:\adiscarding first byte", p.B0[0], "from:"))
×
437
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
×
438
                        }
×
439
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
440
                        p.B = p.B0
1✔
441
                        return
1✔
442
                }
443
                if decoder.Verbose {
2✔
444
                        n += copy(b[n:], fmt.Sprintln("ERROR:\apackage size", packageSize, "is <", p.TriceSize, " - ignoring package:"))
1✔
445
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
446
                        n += copy(b[n:], fmt.Sprintln("tyIdSize=", tyIdSize, "tsSize=", decoder.TargetTimestampSize, "ncSize=", ncSize, "ParamSpae=", p.ParamSpace))
1✔
447
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
448
                }
1✔
449
                p.B = p.B[len(p.B):] // discard buffer
1✔
450
        }
451
        if SingleFraming && p.TriceSize != packageSize {
26✔
452
                if decoder.Verbose {
2✔
453
                        n += copy(b[n:], fmt.Sprintln("ERROR:\asingle framed package size", packageSize, "is !=", p.TriceSize, " - ignoring package:"))
1✔
454
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
455
                        n += copy(b[n:], fmt.Sprintln("tyIdSize=", tyIdSize, "tsSize=", decoder.TargetTimestampSize, "ncSize=", ncSize, "ParamSpae=", p.ParamSpace))
1✔
456
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
457
                }
1✔
458
                p.B = p.B[len(p.B):] // discard buffer
1✔
459
        }
460

461
        // cycle counter automatic & check
462
        if cycle == 0xc0 && p.cycle != 0xc0 && decoder.InitialCycle { // with cycle counter and seems to be a target reset
26✔
463
                n += copy(b[n:], fmt.Sprintln("warning:\a   Target Reset?   "))
1✔
464
                p.cycle = cycle + 1 // adjust cycle
1✔
465
                decoder.InitialCycle = false
1✔
466
        }
1✔
467
        if cycle == 0xc0 && p.cycle != 0xc0 && !decoder.InitialCycle { // with cycle counter and seems to be a target reset
27✔
468
                //n += copy(b[n:], fmt.Sprintln("info:   Target Reset?   ")) // todo: This line is ok with cycle counter but not without cycle counter
2✔
469
                p.cycle = cycle + 1 // adjust cycle
2✔
470
        }
2✔
471
        if cycle == 0xc0 && p.cycle == 0xc0 && decoder.InitialCycle { // with or without cycle counter and seems to be a target reset
34✔
472
                //n += copy(b[n:], fmt.Sprintln("warning:   Restart?   "))
9✔
473
                p.cycle = cycle + 1 // adjust cycle
9✔
474
                decoder.InitialCycle = false
9✔
475
        }
9✔
476
        if cycle == 0xc0 && p.cycle == 0xc0 && !decoder.InitialCycle { // with or without cycle counter and seems to be a normal case
32✔
477
                p.cycle = cycle + 1 // adjust cycle
7✔
478
        }
7✔
479
        if cycle != 0xc0 && !DisableCycleErrors { // with cycle counter and s.th. lost
32✔
480
                if cycle != p.cycle { // no cycle check for 0xc0 to avoid messages on every target reset and when no cycle counter is active
10✔
481
                        n += copy(b[n:], fmt.Sprintln("CYCLE_ERROR:\a", cycle, "!=", p.cycle, " (count=", emitter.TagEvents("CYCLE_ERROR")+1, ")"))
3✔
482
                        n += copy(b[n:], "                                         ") // len of location information plus stamp: 41 spaces - see NewlineIndent below - todo: make it generic
3✔
483
                        p.cycle = cycle                                               // adjust cycle
3✔
484
                }
3✔
485
                decoder.InitialCycle = false
7✔
486
                p.cycle++
7✔
487
        }
488

489
        var ok bool
25✔
490
        p.LutMutex.RLock()
25✔
491
        p.Trice, ok = p.Lut[triceID]
25✔
492
        // Keep the LUT representation separate from the optional display-only newline mutation.
25✔
493
        originalTrice := p.Trice
25✔
494
        if AddNewlineToEachTriceMessage {
25✔
495
                p.Trice.Strg += `\n` // this adds a newline to each single Trice message
×
496
        }
×
497
        p.LutMutex.RUnlock()
25✔
498
        if !ok {
27✔
499
                if p.packageFraming == packageFramingNone {
3✔
500
                        if decoder.Verbose {
1✔
501
                                n += copy(b[n:], fmt.Sprintln("wrn:\adiscarding first byte", p.B0[0], "from:"))
×
502
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
×
503
                        }
×
504
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
505
                        p.B = p.B0
1✔
506
                } else {
1✔
507
                        n += copy(b[n:], fmt.Sprintln("WARNING:\aunknown ID ", triceID, "- ignoring trice ending with:"))
1✔
508
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
509
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
510
                        p.B = p.B[:0] // discard all
1✔
511
                }
1✔
512
                return
2✔
513
        }
514

515
        if p.visEnabled {
27✔
516
                p.visRecord = decoder.VisRecord{
4✔
517
                        ID:         triceID,
4✔
518
                        Type:       originalTrice.Type,
4✔
519
                        Format:     originalTrice.Strg,
4✔
520
                        Stamp:      decoder.TargetTimestamp,
4✔
521
                        StampBits:  decoder.TargetTimestampSize * 8,
4✔
522
                        SingleLine: isSingleVisLine(p.Trice.Strg),
4✔
523
                }
4✔
524
        }
4✔
525
        // Decoder diagnostics prefixed to a valid message make this Read unsuitable
526
        // for record-oriented visualization, even if the numeric payload decodes.
527
        hadPrefixedDiagnostic := n != 0
23✔
528
        n += p.sprintTrice(b[n:]) // use param info
23✔
529
        if p.visEnabled && hadPrefixedDiagnostic {
23✔
NEW
530
                p.visValid = false
×
NEW
531
        }
×
532
        if len(p.B) < p.ParamSpace {
25✔
533
                if p.packageFraming == packageFramingNone {
3✔
534
                        if decoder.Verbose {
2✔
535
                                n += copy(b[n:], fmt.Sprintln("wrn:discarding first byte", p.B0[0], "from:"))
1✔
536
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B0)))
1✔
537
                        }
1✔
538
                        p.B0 = p.B0[1:] // discard first byte and try again
1✔
539
                        p.B = p.B0
1✔
540
                } else {
1✔
541
                        n += copy(b[n:], fmt.Sprintln("ERROR:ignoring data garbage:"))
1✔
542
                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B)))
1✔
543
                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
544
                        p.B = p.B[:0] // discard all
1✔
545
                }
1✔
546
        } else {
21✔
547
                if p.packageFraming != packageFramingNone { // COBS | TCOBS are exact
35✔
548
                        p.B = p.B[p.ParamSpace:] // drop param info
14✔
549
                        if len(p.B) < 4 && isZero(p.B) {
28✔
550
                                p.B = p.B[:0] // drop framed direct-output word padding after the decoded Trice
14✔
551
                        }
14✔
552
                } else { // no package framing
7✔
553
                        alignedParamSpace := (p.ParamSpace + 3) & ^3
7✔
554
                        if alignedParamSpace <= len(p.B) && isZero(p.B[p.ParamSpace:alignedParamSpace]) {
11✔
555
                                p.B = p.B[alignedParamSpace:]
4✔
556
                        } else if p.ParamSpace <= len(p.B) {
10✔
557
                                p.B = p.B[p.ParamSpace:]
3✔
558
                        } else {
3✔
559
                                // n += copy(b[n:], fmt.Sprintln("wrn: cannot discard padding bytes", ))
×
560
                        }
×
561
                }
562
        }
563
        return
23✔
564
}
565

566
// isSingleVisLine mirrors the line composer's escaped-newline interpretation.
567
//
568
// A visualization record is self-contained only when its format contributes
569
// exactly one logical newline and that newline terminates the message.
570
func isSingleVisLine(format string) bool {
12✔
571
        var normalized strings.Builder
12✔
572
        normalized.Grow(len(format))
12✔
573
        for i := 0; i < len(format); i++ {
156✔
574
                switch {
144✔
575
                case format[i] == '\r' && i+1 < len(format) && format[i+1] == '\n':
1✔
576
                        normalized.WriteByte('\n')
1✔
577
                        i++
1✔
578
                case format[i] == '\\' && i+1 < len(format) && format[i+1] == '\\':
1✔
579
                        // The line composer protects an escaped backslash before interpreting \n.
1✔
580
                        normalized.WriteByte('\\')
1✔
581
                        i++
1✔
582
                case format[i] == '\\' && i+3 < len(format) &&
583
                        format[i+1] == 'r' && format[i+2] == '\\' && format[i+3] == 'n':
1✔
584
                        normalized.WriteByte('\n')
1✔
585
                        i += 3
1✔
586
                case format[i] == '\\' && i+1 < len(format) && format[i+1] == 'n':
5✔
587
                        normalized.WriteByte('\n')
5✔
588
                        i++
5✔
589
                default:
136✔
590
                        normalized.WriteByte(format[i])
136✔
591
                }
592
        }
593
        logical := normalized.String()
12✔
594
        return strings.Count(logical, "\n") == 1 && strings.HasSuffix(logical, "\n")
12✔
595
}
596

597
// sprintTrice writes a trice string or appropriate message into b and returns that len.
598
//
599
// p.Trice.Type is the received trice, in fact the name from til.json.
600
func (p *trexDec) sprintTrice(b []byte) (n int) {
33✔
601

33✔
602
        isSAlias := strings.HasPrefix(p.Trice.Strg, id.SAliasStrgPrefix) && strings.HasSuffix(p.Trice.Strg, id.SAliasStrgSuffix)
33✔
603
        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.
34✔
604
                p.Trice.Strg = "%s" // See appropriate comment inside insertTriceIDs().
1✔
605
        }
1✔
606

607
        p.pFmt, p.u = decoder.UReplaceN(p.Trice.Strg)
33✔
608

33✔
609
        // remove Assert* from triceAssert* name if found
33✔
610
        before, _, found := strings.Cut(p.Trice.Type, "Assert")
33✔
611
        if found {
34✔
612
                p.Trice.Type = strings.TrimSpace(before)
1✔
613
        }
1✔
614

615
        triceType, err := id.ConstructFullTriceInfo(p.Trice.Type, len(p.u))
33✔
616

33✔
617
        if err != nil {
34✔
618
                n += copy(b[n:], fmt.Sprintln("err:ConstructFullTriceInfo failed with:", p.Trice.Type, len(p.B), "- ignoring package:"))
1✔
619
                return
1✔
620
        }
1✔
621
        ucTriceTypeReceived := strings.ToUpper(p.Trice.Type)   // examples: TRICE_S,   TRICE,   TRICE32,   TRICE16_2
32✔
622
        ucTriceTypeReconstructed := strings.ToUpper(triceType) // examples: TRICE32_S, TRICE0,  TRICE32_4, TRICE16_2
32✔
623
        for _, s := range cobsFunctionPtrList {                // walk through the list and try to find a match for execution
543✔
624
                if s.triceType == ucTriceTypeReconstructed || s.triceType == ucTriceTypeReceived { // match list entry "TRICE..."
542✔
625
                        if len(p.B) < p.ParamSpace {
34✔
626
                                n += copy(b[n:], fmt.Sprintln("err:len(p.B) =", len(p.B), "< p.ParamSpace = ", p.ParamSpace, "- ignoring package:"))
3✔
627
                                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:len(p.B)])))
3✔
628
                                n += copy(b[n:], fmt.Sprintln(decoder.Hints))
3✔
629
                                return
3✔
630
                        }
3✔
631
                        if p.ParamSpace != (s.bitWidth>>3)*s.paramCount {
31✔
632
                                if !isSpecialCaseTriceType(s.triceType) {
4✔
633
                                        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✔
634
                                        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:len(p.B)])))
1✔
635
                                        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
636
                                        return
1✔
637
                                }
1✔
638
                        }
639
                        p.pFmt = applyMultilineIndent(p.pFmt)
27✔
640

27✔
641
                        n += s.triceFn(p, b, s.bitWidth, s.paramCount) // match found, call handler
27✔
642
                        return
27✔
643
                }
644
        }
645
        n += copy(b[n:], fmt.Sprintln("err:Unknown trice.Type:", p.Trice.Type, "and", triceType, "not matching - ignoring trice data:"))
1✔
646
        n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:p.ParamSpace])))
1✔
647
        n += copy(b[n:], fmt.Sprintln(decoder.Hints))
1✔
648
        return
1✔
649
}
650

651
func isSpecialCaseTriceType(triceType string) bool {
7✔
652
        _, ok := specialCaseTriceTypes[strings.ToUpper(triceType)]
7✔
653
        return ok
7✔
654
}
7✔
655

656
func applyMultilineIndent(format string) string {
30✔
657
        segments := strings.Split(format, `\n`)
30✔
658
        if len(segments) < 3 {
58✔
659
                return format
28✔
660
        }
28✔
661
        if decoder.NewlineIndent == -1 {
4✔
662
                decoder.NewlineIndent = 12 + 1
2✔
663
                if !(id.LIFnJSON == "off" || id.LIFnJSON == "none") {
3✔
664
                        decoder.NewlineIndent += 28
1✔
665
                }
1✔
666
                if decoder.ShowID != "" {
3✔
667
                        decoder.NewlineIndent += 5
1✔
668
                }
1✔
669
        }
670
        skip := `\n` + strings.Repeat(" ", decoder.NewlineIndent)
2✔
671
        return strings.TrimRight(strings.Join(segments, skip), " ")
2✔
672
}
673

674
func splitChannelFormat(format string) (prefix string, itemFormat string, hadTrailingNewline bool) {
15✔
675
        before, after, found := strings.Cut(format, ":")
15✔
676
        if found {
24✔
677
                prefix = before + ":"
9✔
678
        } else {
15✔
679
                after = format
6✔
680
        }
6✔
681
        itemFormat = strings.TrimSuffix(after, `\n`)
15✔
682
        hadTrailingNewline = len(itemFormat) < len(after)
15✔
683
        return
15✔
684
}
685

686
func normalizeBufferItemFormat(format string) (normalized string, kind fmtspec.Kind) {
13✔
687
        // Buffer-format decoding uses a single repeated item verb. Issue #649 added
13✔
688
        // proper support for C length modifiers in the main decoder path, and the
13✔
689
        // buffer path must apply the same normalization to avoid raw `%ld`, `%zx`,
13✔
690
        // `%lx` or `%llx` reaching Go fmt unchanged.
13✔
691
        normalized, specs := fmtspec.Normalize(format)
13✔
692
        if len(specs) == 0 {
13✔
693
                return format, fmtspec.KindSigned
×
694
        }
×
695
        return normalized, specs[0].Kind
13✔
696
}
697

698
func bufferValue8(v byte, kind fmtspec.Kind) interface{} {
28✔
699
        switch kind {
28✔
700
        case fmtspec.KindUnsigned:
×
701
                return v
×
702
        case fmtspec.KindBasedInteger:
12✔
703
                if decoder.Unsigned {
24✔
704
                        return v
12✔
705
                }
12✔
706
                return int8(v)
×
707
        default:
16✔
708
                return int8(v)
16✔
709
        }
710
}
711

712
func bufferValue16(v uint16, kind fmtspec.Kind) interface{} {
2✔
713
        switch kind {
2✔
714
        case fmtspec.KindUnsigned:
×
715
                return v
×
716
        case fmtspec.KindBasedInteger:
×
717
                if decoder.Unsigned {
×
718
                        return v
×
719
                }
×
720
                return int16(v)
×
721
        default:
2✔
722
                return int16(v)
2✔
723
        }
724
}
725

726
func bufferValue32(v uint32, kind fmtspec.Kind) interface{} {
2✔
727
        switch kind {
2✔
728
        case fmtspec.KindUnsigned:
×
729
                return v
×
730
        case fmtspec.KindBasedInteger:
×
731
                if decoder.Unsigned {
×
732
                        return v
×
733
                }
×
734
                return int32(v)
×
735
        default:
2✔
736
                return int32(v)
2✔
737
        }
738
}
739

740
func bufferValue64(v uint64, kind fmtspec.Kind) interface{} {
2✔
741
        switch kind {
2✔
742
        case fmtspec.KindUnsigned:
×
743
                return v
×
744
        case fmtspec.KindBasedInteger:
×
745
                if decoder.Unsigned {
×
746
                        return v
×
747
                }
×
748
                return int64(v)
×
749
        default:
2✔
750
                return int64(v)
2✔
751
        }
752
}
753

754
// triceTypeFn is the type for cobsFunctionPtrList elements.
755
type triceTypeFn struct {
756
        triceType  string                                              // triceType describes if parameters, the parameter bit width or if the parameter is a string.
757
        triceFn    func(p *trexDec, b []byte, bitwidth, count int) int // triceFn performs the conversion to the output string.
758
        ParamSpace int                                                 // ParamSpace is the count of bytes allocated for the parameters.
759
        bitWidth   int                                                 // bitWidth is the individual parameter width.
760
        paramCount int                                                 // paramCount is the amount pf parameters for the format string, which must match the count of format specifiers.
761
}
762

763
// cobsFunctionPtrList is a function pointer list.
764
var cobsFunctionPtrList = [...]triceTypeFn{
765
        {"TRICE_0", (*trexDec).trice0, 0, 0, 0},
766
        {"TRICEC", (*trexDec).triceC, 0, 0, 0},
767
        {"TRICE8_1", (*trexDec).unSignedOrSignedOut, 1, 8, 1},
768
        {"TRICE8_2", (*trexDec).unSignedOrSignedOut, 2, 8, 2},
769
        {"TRICE8_3", (*trexDec).unSignedOrSignedOut, 3, 8, 3},
770
        {"TRICE8_4", (*trexDec).unSignedOrSignedOut, 4, 8, 4},
771
        {"TRICE8_5", (*trexDec).unSignedOrSignedOut, 5, 8, 5},
772
        {"TRICE8_6", (*trexDec).unSignedOrSignedOut, 6, 8, 6},
773
        {"TRICE8_7", (*trexDec).unSignedOrSignedOut, 7, 8, 7},
774
        {"TRICE8_8", (*trexDec).unSignedOrSignedOut, 8, 8, 8},
775
        {"TRICE8_9", (*trexDec).unSignedOrSignedOut, 9, 8, 9},
776
        {"TRICE8_10", (*trexDec).unSignedOrSignedOut, 10, 8, 10},
777
        {"TRICE8_11", (*trexDec).unSignedOrSignedOut, 11, 8, 11},
778
        {"TRICE8_12", (*trexDec).unSignedOrSignedOut, 12, 8, 12},
779
        {"TRICE16_1", (*trexDec).unSignedOrSignedOut, 2, 16, 1},
780
        {"TRICE16_2", (*trexDec).unSignedOrSignedOut, 4, 16, 2},
781
        {"TRICE16_3", (*trexDec).unSignedOrSignedOut, 6, 16, 3},
782
        {"TRICE16_4", (*trexDec).unSignedOrSignedOut, 8, 16, 4},
783
        {"TRICE16_5", (*trexDec).unSignedOrSignedOut, 10, 16, 5},
784
        {"TRICE16_6", (*trexDec).unSignedOrSignedOut, 12, 16, 6},
785
        {"TRICE16_7", (*trexDec).unSignedOrSignedOut, 14, 16, 7},
786
        {"TRICE16_8", (*trexDec).unSignedOrSignedOut, 16, 16, 8},
787
        {"TRICE16_9", (*trexDec).unSignedOrSignedOut, 18, 16, 9},
788
        {"TRICE16_10", (*trexDec).unSignedOrSignedOut, 20, 16, 10},
789
        {"TRICE16_11", (*trexDec).unSignedOrSignedOut, 22, 16, 11},
790
        {"TRICE16_12", (*trexDec).unSignedOrSignedOut, 24, 16, 12},
791
        {"TRICE32_1", (*trexDec).unSignedOrSignedOut, 4, 32, 1},
792
        {"TRICE32_2", (*trexDec).unSignedOrSignedOut, 8, 32, 2},
793
        {"TRICE32_3", (*trexDec).unSignedOrSignedOut, 12, 32, 3},
794
        {"TRICE32_4", (*trexDec).unSignedOrSignedOut, 16, 32, 4},
795
        {"TRICE32_5", (*trexDec).unSignedOrSignedOut, 20, 32, 5},
796
        {"TRICE32_6", (*trexDec).unSignedOrSignedOut, 24, 32, 6},
797
        {"TRICE32_7", (*trexDec).unSignedOrSignedOut, 28, 32, 7},
798
        {"TRICE32_8", (*trexDec).unSignedOrSignedOut, 32, 32, 8},
799
        {"TRICE32_9", (*trexDec).unSignedOrSignedOut, 36, 32, 9},
800
        {"TRICE32_10", (*trexDec).unSignedOrSignedOut, 40, 32, 10},
801
        {"TRICE32_11", (*trexDec).unSignedOrSignedOut, 44, 32, 11},
802
        {"TRICE32_12", (*trexDec).unSignedOrSignedOut, 48, 32, 12},
803
        {"TRICE64_1", (*trexDec).unSignedOrSignedOut, 8, 64, 1},
804
        {"TRICE64_2", (*trexDec).unSignedOrSignedOut, 16, 64, 2},
805
        {"TRICE64_3", (*trexDec).unSignedOrSignedOut, 24, 64, 3},
806
        {"TRICE64_4", (*trexDec).unSignedOrSignedOut, 32, 64, 4},
807
        {"TRICE64_5", (*trexDec).unSignedOrSignedOut, 40, 64, 5},
808
        {"TRICE64_6", (*trexDec).unSignedOrSignedOut, 48, 64, 6},
809
        {"TRICE64_7", (*trexDec).unSignedOrSignedOut, 56, 64, 7},
810
        {"TRICE64_8", (*trexDec).unSignedOrSignedOut, 64, 64, 8},
811
        {"TRICE64_9", (*trexDec).unSignedOrSignedOut, 72, 64, 9},
812
        {"TRICE64_10", (*trexDec).unSignedOrSignedOut, 80, 64, 10},
813
        {"TRICE64_11", (*trexDec).unSignedOrSignedOut, 88, 64, 11},
814
        {"TRICE64_12", (*trexDec).unSignedOrSignedOut, 96, 64, 12},
815

816
        {"TRICES", (*trexDec).triceS, -1, 0, 0},
817
        {"TRICEN", (*trexDec).triceN, -1, 0, 0},
818

819
        {"TRICE8F", (*trexDec).trice8F, -1, 0, 0},
820
        {"TRICE16F", (*trexDec).trice16F, -1, 0, 0},
821
        {"TRICE32F", (*trexDec).trice32F, -1, 0, 0},
822
        {"TRICE64F", (*trexDec).trice64F, -1, 0, 0},
823

824
        {"TRICE8B", (*trexDec).trice8B, -1, 0, 0},
825
        {"TRICE16B", (*trexDec).trice16B, -1, 0, 0},
826
        {"TRICE32B", (*trexDec).trice32B, -1, 0, 0},
827
        {"TRICE64B", (*trexDec).trice64B, -1, 0, 0},
828

829
        {"TRICE8C", (*trexDec).trice8F, -1, 0, 0},
830
        {"TRICE16C", (*trexDec).trice16F, -1, 0, 0},
831
        {"TRICE32C", (*trexDec).trice32F, -1, 0, 0},
832
        {"TRICE64C", (*trexDec).trice64F, -1, 0, 0},
833
}
834

835
func (p *trexDec) alignedParamBytes(b []byte, width int) (s []byte, n int, ok bool) {
21✔
836
        if p.ParamSpace > len(p.B) {
21✔
837
                n += copy(b[n:], fmt.Sprintln("err:len(p.B) =", len(p.B), "< p.ParamSpace = ", p.ParamSpace, "- ignoring package:"))
×
838
                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:len(p.B)])))
×
839
                n += copy(b[n:], fmt.Sprintln(decoder.Hints))
×
840
                return nil, n, false
×
841
        }
×
842
        if p.ParamSpace%width != 0 {
30✔
843
                dumpLen := p.ParamSpace
9✔
844
                if dumpLen > len(p.B) {
9✔
845
                        dumpLen = len(p.B)
×
846
                }
×
847
                n += copy(b[n:], fmt.Sprintln("err:", p.Trice.Type, "ParamSpace =", p.ParamSpace, "is not aligned to", width, "byte values - ignoring package:"))
9✔
848
                n += copy(b[n:], fmt.Sprintln(hex.Dump(p.B[:dumpLen])))
9✔
849
                n += copy(b[n:], fmt.Sprintln(decoder.Hints))
9✔
850
                return nil, n, false
9✔
851
        }
852
        return p.B[:p.ParamSpace], 0, true
12✔
853
}
854

855
// triceN converts dynamic strings.
856
func (p *trexDec) triceN(b []byte, _ int, _ int) int {
2✔
857
        s := string(p.B[:p.ParamSpace])
2✔
858
        // todo: evaluate p.Trice.Strg, use p.SLen and do whatever should be done
2✔
859
        return copy(b, fmt.Sprintf(p.Trice.Strg, s))
2✔
860
}
2✔
861

862
// triceS converts dynamic strings.
863
func (p *trexDec) triceS(b []byte, _ int, _ int) int {
2✔
864
        s := string(p.B[:p.ParamSpace])
2✔
865
        return copy(b, fmt.Sprintf(p.Trice.Strg, s))
2✔
866
}
2✔
867

868
// triceB converts dynamic buffers.
869
func (p *trexDec) trice8B(b []byte, _ int, _ int) (n int) {
7✔
870
        if decoder.DebugOut {
8✔
871
                fmt.Fprintln(p.W, string(p.B))
1✔
872
        }
1✔
873
        s := p.B[:p.ParamSpace]
7✔
874
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
7✔
875
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
7✔
876
        if prefix != "" {
9✔
877
                n += copy(b[n:], prefix)
2✔
878
        }
2✔
879

880
        for i := 0; i < len(s); i++ {
35✔
881
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue8(s[i], itemKind)))
28✔
882
        }
28✔
883
        if addLineBreak {
8✔
884
                n += copy(b[n:], fmt.Sprintln())
1✔
885
        }
1✔
886
        return
7✔
887
}
888

889
// trice16B converts dynamic buffers.
890
func (p *trexDec) trice16B(b []byte, _ int, _ int) (n int) {
3✔
891
        if decoder.DebugOut {
4✔
892
                fmt.Fprintln(p.W, string(p.B))
1✔
893
        }
1✔
894
        s, n, ok := p.alignedParamBytes(b, 2)
3✔
895
        if !ok {
4✔
896
                return n
1✔
897
        }
1✔
898
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
899
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
900
        if prefix != "" {
4✔
901
                n += copy(b[n:], prefix)
2✔
902
        }
2✔
903

904
        for i := 0; i < len(s); i += 2 {
4✔
905
                nn := binary.LittleEndian.Uint16(s[i:])
2✔
906
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue16(nn, itemKind)))
2✔
907
        }
2✔
908
        if addLineBreak {
3✔
909
                n += copy(b[n:], fmt.Sprintln())
1✔
910
        }
1✔
911

912
        return
2✔
913
}
914

915
// trice32B converts dynamic buffers.
916
func (p *trexDec) trice32B(b []byte, _ int, _ int) (n int) {
3✔
917
        if decoder.DebugOut {
4✔
918
                fmt.Fprintln(p.W, string(p.B))
1✔
919
        }
1✔
920
        s, n, ok := p.alignedParamBytes(b, 4)
3✔
921
        if !ok {
4✔
922
                return n
1✔
923
        }
1✔
924
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
925
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
926
        if prefix != "" {
4✔
927
                n += copy(b[n:], prefix)
2✔
928
        }
2✔
929

930
        for i := 0; i < len(s); i += 4 {
4✔
931
                nn := binary.LittleEndian.Uint32(s[i:])
2✔
932
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue32(nn, itemKind)))
2✔
933
        }
2✔
934
        if addLineBreak {
3✔
935
                n += copy(b[n:], fmt.Sprintln())
1✔
936
        }
1✔
937
        return
2✔
938
}
939

940
// trice64B converts dynamic buffers.
941
func (p *trexDec) trice64B(b []byte, _ int, _ int) (n int) {
3✔
942
        if decoder.DebugOut {
4✔
943
                fmt.Fprintln(p.W, string(p.B))
1✔
944
        }
1✔
945
        s, n, ok := p.alignedParamBytes(b, 8)
3✔
946
        if !ok {
4✔
947
                return n
1✔
948
        }
1✔
949
        prefix, itemFormat, addLineBreak := splitChannelFormat(p.Trice.Strg)
2✔
950
        itemFormat, itemKind := normalizeBufferItemFormat(itemFormat)
2✔
951
        if prefix != "" {
4✔
952
                n += copy(b[n:], prefix)
2✔
953
        }
2✔
954

955
        for i := 0; i < len(s); i += 8 {
4✔
956
                nn := binary.LittleEndian.Uint64(s[i:])
2✔
957
                n += copy(b[n:], fmt.Sprintf(itemFormat, bufferValue64(nn, itemKind)))
2✔
958
        }
2✔
959
        if addLineBreak {
3✔
960
                n += copy(b[n:], fmt.Sprintln())
1✔
961
        }
1✔
962
        return
2✔
963
}
964

965
// trice8F display function call with 8-bit parameters.
966
func (p *trexDec) trice8F(b []byte, _ int, _ int) (n int) {
2✔
967
        if decoder.DebugOut {
3✔
968
                fmt.Fprintln(p.W, string(p.B))
1✔
969
        }
1✔
970
        s := p.B[:p.ParamSpace]
2✔
971
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
972
        for i := 0; i < len(s); i++ {
5✔
973
                n += copy(b[n:], fmt.Sprintf("(%02x)", s[i]))
3✔
974
        }
3✔
975
        n += copy(b[n:], fmt.Sprintln())
2✔
976
        return
2✔
977
}
978

979
// trice16F display function call with 16-bit parameters.
980
func (p *trexDec) trice16F(b []byte, _ int, _ int) (n int) {
4✔
981
        if decoder.DebugOut {
5✔
982
                fmt.Fprintln(p.W, string(p.B))
1✔
983
        }
1✔
984
        s, n, ok := p.alignedParamBytes(b, 2)
4✔
985
        if !ok {
6✔
986
                return n
2✔
987
        }
2✔
988
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
989
        for i := 0; i < len(s); i += 2 {
4✔
990
                n += copy(b[n:], fmt.Sprintf("(%04x)", binary.LittleEndian.Uint16(s[i:])))
2✔
991
        }
2✔
992
        n += copy(b[n:], fmt.Sprintln())
2✔
993
        return
2✔
994
}
995

996
// trice32F display function call with 32-bit parameters.
997
func (p *trexDec) trice32F(b []byte, _ int, _ int) (n int) {
4✔
998
        if decoder.DebugOut {
5✔
999
                fmt.Fprintln(p.W, string(p.B))
1✔
1000
        }
1✔
1001
        s, n, ok := p.alignedParamBytes(b, 4)
4✔
1002
        if !ok {
6✔
1003
                return n
2✔
1004
        }
2✔
1005
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
1006
        for i := 0; i < len(s); i += 4 {
4✔
1007
                n += copy(b[n:], fmt.Sprintf("(%08x)", binary.LittleEndian.Uint32(s[i:])))
2✔
1008
        }
2✔
1009
        n += copy(b[n:], fmt.Sprintln())
2✔
1010
        return
2✔
1011
}
1012

1013
// trice64F display function call with 64-bit parameters.
1014
func (p *trexDec) trice64F(b []byte, _ int, _ int) (n int) {
4✔
1015
        if decoder.DebugOut {
5✔
1016
                fmt.Fprintln(p.W, string(p.B))
1✔
1017
        }
1✔
1018
        s, n, ok := p.alignedParamBytes(b, 8)
4✔
1019
        if !ok {
6✔
1020
                return n
2✔
1021
        }
2✔
1022
        n += copy(b[n:], fmt.Sprint(p.Trice.Strg))
2✔
1023
        for i := 0; i < len(s); i += 8 {
4✔
1024
                n += copy(b[n:], fmt.Sprintf("(%016x)", binary.LittleEndian.Uint64(s[i:])))
2✔
1025
        }
2✔
1026
        n += copy(b[n:], fmt.Sprintln())
2✔
1027
        return
2✔
1028
}
1029

1030
// trice0 prints the trice format string.
1031
func (p *trexDec) trice0(b []byte, _ int, _ int) int {
7✔
1032
        if p.visEnabled {
7✔
NEW
1033
                p.visRecord.ValueCount = 0
×
NEW
1034
                p.visValid = true
×
NEW
1035
        }
×
1036
        return copy(b, fmt.Sprint(p.pFmt))
7✔
1037
}
1038

1039
// triceC prints a no-payload ABC command as one complete output line.
1040
func (p *trexDec) triceC(b []byte, _ int, _ int) int {
1✔
1041
        return copy(b, fmt.Sprintln(p.pFmt))
1✔
1042
}
1✔
1043

1044
// unSignedOrSignedOut prints p.B according to the format string.
1045
func (p *trexDec) unSignedOrSignedOut(b []byte, bitwidth, count int) int {
37✔
1046
        if len(p.u) != count {
38✔
1047
                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier count inside", p.Trice.Type, p.Trice.Strg))
1✔
1048
        }
1✔
1049
        // Keep normal decoding compatible with larger fixed-width Trices. The MVP
1050
        // visualization record deliberately captures only v0 through v11.
1051
        captureVis := p.visEnabled
36✔
1052
        var fixedValues [decoder.VisValueCapacity]interface{}
36✔
1053
        var values []interface{}
36✔
1054
        if count > decoder.VisValueCapacity {
37✔
1055
                values = make([]interface{}, count)
1✔
1056
        } else {
36✔
1057
                values = fixedValues[:count]
35✔
1058
        }
35✔
1059
        switch bitwidth {
36✔
1060
        case 8:
13✔
1061
                for i, f := range p.u {
54✔
1062
                        switch f {
41✔
1063
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
3✔
1064
                                values[i] = p.B[i]
3✔
1065
                                if captureVis && i < decoder.VisValueCapacity {
3✔
NEW
1066
                                        p.setVisUnsigned(i, bitwidth, uint64(p.B[i]))
×
NEW
1067
                                }
×
1068
                        case decoder.SignedFormatSpecifier:
36✔
1069
                                values[i] = int8(p.B[i])
36✔
1070
                                if captureVis && i < decoder.VisValueCapacity {
60✔
1071
                                        p.setVisSigned(i, bitwidth, int64(int8(p.B[i])))
24✔
1072
                                }
24✔
1073
                        case decoder.BooleanFormatSpecifier:
1✔
1074
                                values[i] = p.B[i] != 0
1✔
1075
                                if captureVis && i < decoder.VisValueCapacity {
1✔
NEW
1076
                                        p.setVisBool(i, bitwidth, p.B[i] != 0)
×
NEW
1077
                                }
×
1078
                        default:
1✔
1079
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier (float?) inside", p.Trice.Type, p.Trice.Strg))
1✔
1080
                        }
1081
                }
1082
        case 16:
5✔
1083
                for i, f := range p.u {
12✔
1084
                        n := p.ReadU16(p.B[2*i:])
7✔
1085
                        switch f {
7✔
1086
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
1✔
1087
                                values[i] = n
1✔
1088
                                if captureVis && i < decoder.VisValueCapacity {
1✔
NEW
1089
                                        p.setVisUnsigned(i, bitwidth, uint64(n))
×
NEW
1090
                                }
×
1091
                        case decoder.SignedFormatSpecifier:
4✔
1092
                                values[i] = int16(n)
4✔
1093
                                if captureVis && i < decoder.VisValueCapacity {
5✔
1094
                                        p.setVisSigned(i, bitwidth, int64(int16(n)))
1✔
1095
                                }
1✔
1096
                        case decoder.BooleanFormatSpecifier:
1✔
1097
                                values[i] = n != 0
1✔
1098
                                if captureVis && i < decoder.VisValueCapacity {
1✔
NEW
1099
                                        p.setVisBool(i, bitwidth, n != 0)
×
NEW
1100
                                }
×
1101
                        default:
1✔
1102
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier (float?) inside", p.Trice.Type, p.Trice.Strg))
1✔
1103
                        }
1104
                }
1105
        case 32:
13✔
1106
                for i, f := range p.u {
34✔
1107
                        n := p.ReadU32(p.B[4*i:])
21✔
1108
                        switch f {
21✔
1109
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
5✔
1110
                                values[i] = n
5✔
1111
                                if captureVis && i < decoder.VisValueCapacity {
6✔
1112
                                        p.setVisUnsigned(i, bitwidth, uint64(n))
1✔
1113
                                }
1✔
1114
                        case decoder.SignedFormatSpecifier:
11✔
1115
                                values[i] = int32(n)
11✔
1116
                                if captureVis && i < decoder.VisValueCapacity {
15✔
1117
                                        p.setVisSigned(i, bitwidth, int64(int32(n)))
4✔
1118
                                }
4✔
1119
                        case decoder.FloatFormatSpecifier:
2✔
1120
                                values[i] = math.Float32frombits(n)
2✔
1121
                                if captureVis && i < decoder.VisValueCapacity {
3✔
1122
                                        p.setVisFloat(i, bitwidth, float64(math.Float32frombits(n)))
1✔
1123
                                }
1✔
1124
                        case decoder.BooleanFormatSpecifier:
2✔
1125
                                values[i] = n != 0
2✔
1126
                                if captureVis && i < decoder.VisValueCapacity {
3✔
1127
                                        p.setVisBool(i, bitwidth, n != 0)
1✔
1128
                                }
1✔
1129
                        default:
1✔
1130
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier inside", p.Trice.Type, p.Trice.Strg))
1✔
1131
                        }
1132
                }
1133
        case 64:
5✔
1134
                for i, f := range p.u {
13✔
1135
                        n := p.ReadU64(p.B[8*i:])
8✔
1136
                        switch f {
8✔
1137
                        case decoder.UnsignedFormatSpecifier, decoder.PointerFormatSpecifier: // see comment inside decoder.UReplaceN
3✔
1138
                                values[i] = n
3✔
1139
                                if captureVis && i < decoder.VisValueCapacity {
4✔
1140
                                        p.setVisUnsigned(i, bitwidth, n)
1✔
1141
                                }
1✔
1142
                        case decoder.SignedFormatSpecifier:
2✔
1143
                                values[i] = int64(n)
2✔
1144
                                if captureVis && i < decoder.VisValueCapacity {
2✔
NEW
1145
                                        p.setVisSigned(i, bitwidth, int64(n))
×
NEW
1146
                                }
×
1147
                        case decoder.FloatFormatSpecifier:
2✔
1148
                                values[i] = math.Float64frombits(n)
2✔
1149
                                if captureVis && i < decoder.VisValueCapacity {
3✔
1150
                                        p.setVisFloat(i, bitwidth, math.Float64frombits(n))
1✔
1151
                                }
1✔
1152
                        case decoder.BooleanFormatSpecifier:
1✔
1153
                                values[i] = n != 0
1✔
1154
                                if captureVis && i < decoder.VisValueCapacity {
1✔
NEW
1155
                                        p.setVisBool(i, bitwidth, n != 0)
×
NEW
1156
                                }
×
1157
                        default:
×
1158
                                return copy(b, fmt.Sprintln("ERROR: Invalid format specifier inside", p.Trice.Type, p.Trice.Strg))
×
1159
                        }
1160
                }
1161
        }
1162
        if captureVis {
41✔
1163
                p.visRecord.ValueCount = min(count, decoder.VisValueCapacity)
8✔
1164
                p.visValid = true
8✔
1165
        }
8✔
1166
        return copy(b, fmt.Sprintf(p.pFmt, values...))
33✔
1167
}
1168

1169
// setVisSigned stores an exact signed TREX parameter in the pending typed record.
1170
func (p *trexDec) setVisSigned(index, bits int, value int64) {
29✔
1171
        p.visRecord.Values[index] = decoder.VisValue{
29✔
1172
                Kind:   decoder.VisValueSigned,
29✔
1173
                Bits:   bits,
29✔
1174
                Signed: value,
29✔
1175
        }
29✔
1176
}
29✔
1177

1178
// setVisUnsigned stores an exact unsigned TREX parameter in the pending typed record.
1179
func (p *trexDec) setVisUnsigned(index, bits int, value uint64) {
2✔
1180
        p.visRecord.Values[index] = decoder.VisValue{
2✔
1181
                Kind:     decoder.VisValueUnsigned,
2✔
1182
                Bits:     bits,
2✔
1183
                Unsigned: value,
2✔
1184
        }
2✔
1185
}
2✔
1186

1187
// setVisFloat stores a decoded TREX floating-point parameter in the pending typed record.
1188
func (p *trexDec) setVisFloat(index, bits int, value float64) {
2✔
1189
        p.visRecord.Values[index] = decoder.VisValue{
2✔
1190
                Kind:  decoder.VisValueFloat,
2✔
1191
                Bits:  bits,
2✔
1192
                Float: value,
2✔
1193
        }
2✔
1194
}
2✔
1195

1196
// setVisBool stores a decoded TREX Boolean parameter in the pending typed record.
1197
func (p *trexDec) setVisBool(index, bits int, value bool) {
1✔
1198
        p.visRecord.Values[index] = decoder.VisValue{
1✔
1199
                Kind: decoder.VisValueBool,
1✔
1200
                Bits: bits,
1✔
1201
                Bool: value,
1✔
1202
        }
1✔
1203
}
1✔
1204

1205
var testTableVirgin = true
1206

1207
// printTestTableLine is used to generate testdata
1208
func (p *trexDec) printTestTableLine(n int) {
1✔
1209
        if emitter.NextLine || testTableVirgin {
2✔
1210
                emitter.NextLine = false
1✔
1211
                testTableVirgin = false
1✔
1212
                fmt.Printf("{ []byte{ ")
1✔
1213
        }
1✔
1214
        for _, b := range p.IBuf[0:n] { // just to see trice bytes per trice
3✔
1215
                fmt.Printf("%3d,", b)
2✔
1216
        }
2✔
1217
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc