Host C Specs


Directory: src/c/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 95.8% 23 / 0 / 24
Functions: 100.0% 1 / 0 / 1
Branches: 78.6% 11 / 0 / 14

core/wire/stock_wire.c
Line Branch Exec Source
1 /**
2 * @file stock_wire.c
3 * @brief The watchlist as the phone packs it, and the reader that unpacks it.
4 *
5 * @ingroup lib_core
6 */
7 #include "wire/stock_wire.h"
8
9 #include "io/bytes_le.h"
10 #include "text/number_format.h"
11
12 9 bool stock_wire_decode(const uint8_t *buf, uint16_t len, StockStrip *out)
13 {
14
3/6
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 9 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 9 times.
9 if (!buf || !out || len < 1)
15 {
16 ✗ return false;
17 }
18
19 9 uint8_t count = buf[0];
20
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 8 times.
9 if (count > STOCK_SLOTS_MAX)
21 {
22 // the phone's word for how many, pinned to how many there is room for. without this the
23 // fill below walks straight off the end of the slot array
24 1 count = STOCK_SLOTS_MAX;
25 }
26
27 // zero-init so the unused slot[count..MAX-1] bytes are deterministic, not stale
28 // stack, once the whole strip gets persisted to flash
29 9 StockStrip built = {0};
30
31 9 uint16_t offset = 1;
32
2/2
✓ Branch 0 taken 15 times.
✓ Branch 1 taken 6 times.
21 for (uint8_t i = 0; i < count; i++)
33 {
34 // the fixed part of a slot is ok(1) + price(4) + pct(2) + symLen(1) = 8 bytes
35
2/2
✓ Branch 0 taken 2 times.
✓ Branch 1 taken 13 times.
15 if (offset + 8 > len)
36 {
37 2 return false; // malformed, the caller keeps the old strip
38 }
39
40 13 StockSlot *slot = &built.slot[i];
41 13 slot->ok = buf[offset] != 0;
42 13 slot->price_cents = read_i32_le(buf + offset + 1);
43 13 slot->change_pct = read_i16_le(buf + offset + 5);
44 13 uint8_t sym_len = buf[offset + 7];
45 13 offset += 8;
46
47
2/2
✓ Branch 0 taken 1 time.
✓ Branch 1 taken 12 times.
13 if (offset + sym_len > len)
48 {
49 1 return false; // symbol runs past the buffer
50 }
51
52 // take what fits, then step past the whole wire symbol however long it was, or a long one
53 // would leave the walk pointing into the middle of itself and the next slot reads as noise
54 12 copy_bounded(slot->symbol, STOCK_SYMBOL_LEN, buf + offset, sym_len);
55 12 offset += sym_len;
56
57 12 built.count++;
58 }
59
60 // only now, once the whole run has read clean
61 6 *out = built;
62 6 return true;
63 }
64