ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
lwm2m_tlv.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file pc_lwm2m_tlv.cpp
6 * @brief OMA LwM2M TLV writer + reader (pure, host-tested).
7 */
8
10
11#if PC_ENABLE_LWM2M
12
13#include <string.h>
14
15void pc_lwm2m_tlv_init(Lwm2mTlvWriter *w, uint8_t *buf, size_t cap)
16{
17 w->buf = buf;
18 w->cap = cap;
19 w->pos = 0;
20 w->error = false;
21}
22
23bool pc_lwm2m_tlv_write(Lwm2mTlvWriter *w, uint8_t id_type, uint16_t id, const uint8_t *value, size_t value_len)
24{
25 if (!w || (value_len && !value))
26 {
27 return false;
28 }
29 uint8_t type = (uint8_t)(id_type & LWM2M_TLV_IDKIND_MASK);
30 bool id16 = id > 0xFF; // a 16-bit identifier is needed past 255
31 if (id16)
32 {
33 type |= LWM2M_TLV_ID16_FLAG;
34 }
35
36 size_t lenbytes;
37 if (value_len <= LWM2M_TLV_INLINE_LEN_MASK) // 0..7 fits inline (length-type 0)
38 {
39 lenbytes = 0;
40 type |= (uint8_t)value_len;
41 }
42 else if (value_len <= 0xFF) // 8-bit length field (length-type 1)
43 {
44 lenbytes = 1;
45 type |= (uint8_t)(1 << LWM2M_TLV_LENTYPE_SHIFT);
46 }
47 else if (value_len <= 0xFFFF) // 16-bit length field (length-type 2)
48 {
49 lenbytes = 2;
50 type |= (uint8_t)(2 << LWM2M_TLV_LENTYPE_SHIFT);
51 }
52 else if (value_len <= 0xFFFFFF) // 24-bit length field (length-type 3)
53 {
54 lenbytes = 3;
55 type |= (uint8_t)(3 << LWM2M_TLV_LENTYPE_SHIFT);
56 }
57 else
58 {
59 return false;
60 }
61
62 size_t need = 1 + (id16 ? 2 : 1) + lenbytes + value_len;
63 if (w->error || w->pos + need > w->cap)
64 {
65 w->error = true;
66 return false;
67 }
68
69 w->buf[w->pos++] = type;
70 if (id16)
71 {
72 w->buf[w->pos++] = (uint8_t)(id >> 8);
73 }
74 w->buf[w->pos++] = (uint8_t)id;
75 for (size_t i = 0; i < lenbytes; i++)
76 {
77 w->buf[w->pos++] = (uint8_t)(value_len >> (8 * (lenbytes - 1 - i)));
78 }
79 if (value_len)
80 {
81 memcpy(w->buf + w->pos, value, value_len);
82 w->pos += value_len;
83 }
84 return true;
85}
86
87bool pc_lwm2m_tlv_write_int(Lwm2mTlvWriter *w, uint16_t id, int64_t v)
88{
89 size_t n;
90 if (v >= -128 && v <= 127)
91 {
92 n = 1;
93 }
94 else if (v >= -32768 && v <= 32767)
95 {
96 n = 2;
97 }
98 else if (v >= -2147483648LL && v <= 2147483647LL)
99 {
100 n = 4;
101 }
102 else
103 {
104 n = 8;
105 }
106 uint8_t b[8];
107 for (size_t i = 0; i < n; i++)
108 {
109 b[i] = (uint8_t)((uint64_t)v >> (8 * (n - 1 - i)));
110 }
111 return pc_lwm2m_tlv_write(w, LWM2M_TLV_RESOURCE, id, b, n);
112}
113
114bool pc_lwm2m_tlv_write_bool(Lwm2mTlvWriter *w, uint16_t id, bool v)
115{
116 uint8_t b = v ? 1 : 0;
117 return pc_lwm2m_tlv_write(w, LWM2M_TLV_RESOURCE, id, &b, 1);
118}
119
120bool pc_lwm2m_tlv_write_string(Lwm2mTlvWriter *w, uint16_t id, const char *s)
121{
122 if (!s)
123 {
124 return false;
125 }
126 return pc_lwm2m_tlv_write(w, LWM2M_TLV_RESOURCE, id, (const uint8_t *)s, strnlen(s, w->cap + 1));
127}
128
129bool pc_lwm2m_tlv_write_float(Lwm2mTlvWriter *w, uint16_t id, double v)
130{
131 uint64_t bits;
132 memcpy(&bits, &v, 8);
133 uint8_t b[8];
134 for (size_t i = 0; i < 8; i++)
135 {
136 b[i] = (uint8_t)(bits >> (8 * (7 - i))); // big-endian
137 }
138 return pc_lwm2m_tlv_write(w, LWM2M_TLV_RESOURCE, id, b, 8);
139}
140
141size_t pc_lwm2m_tlv_finish(Lwm2mTlvWriter *w)
142{
143 return w->error ? 0 : w->pos;
144}
145
146bool pc_lwm2m_tlv_read(const uint8_t *buf, size_t len, size_t *pos, Lwm2mTlv *out)
147{
148 if (!buf || !pos || !out || *pos >= len)
149 {
150 return false;
151 }
152 size_t p = *pos;
153 uint8_t type = buf[p++];
154 bool id16 = (type & LWM2M_TLV_ID16_FLAG) != 0;
155 if (p + (id16 ? 2u : 1u) > len)
156 {
157 return false;
158 }
159 uint16_t id = buf[p++];
160 if (id16)
161 {
162 id = (uint16_t)((id << 8) | buf[p++]);
163 }
164
165 uint8_t lentype = (uint8_t)((type >> LWM2M_TLV_LENTYPE_SHIFT) & LWM2M_TLV_LENTYPE_MASK);
166 size_t vlen;
167 if (lentype == 0)
168 {
169 vlen = type & LWM2M_TLV_INLINE_LEN_MASK;
170 }
171 else
172 {
173 if (p + lentype > len)
174 {
175 return false;
176 }
177 vlen = 0;
178 for (uint8_t i = 0; i < lentype; i++)
179 {
180 vlen = (vlen << 8) | buf[p++];
181 }
182 }
183 if (p + vlen > len)
184 {
185 return false;
186 }
187
188 out->id_type = (uint8_t)(type & LWM2M_TLV_IDKIND_MASK);
189 out->id = id;
190 out->value = buf + p;
191 out->value_len = vlen;
192 *pos = p + vlen;
193 return true;
194}
195
196bool pc_lwm2m_tlv_value_int(const uint8_t *value, size_t len, int64_t *out)
197{
198 if (!value || (len != 1 && len != 2 && len != 4 && len != 8))
199 {
200 return false;
201 }
202 int64_t r = (value[0] & 0x80) ? -1 : 0; // sign-extend from the MSB
203 for (size_t i = 0; i < len; i++)
204 {
205 r = (int64_t)(((uint64_t)r << 8) | value[i]);
206 }
207 if (out)
208 {
209 *out = r;
210 }
211 return true;
212}
213
214#endif // PC_ENABLE_LWM2M