<p>neels has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.osmocom.org/c/libosmocore/+/26459">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">move tlv.h, tlv_parser.c from libosmogsm to libosmocore<br><br>TLV decoding and encoding has turned out useful outside of GSM, so move<br>it to libosmocore.<br><br>The current specific reason to move it is the implementation of<br>osmo-upf, which depends on libosmocore, but does not need libosmogsm,<br>besides the TLV parser/encoder for PFCP message coding.<br><br>Related: SYS#5599<br>Change-Id: I3cda0143af9eaf90dc73f9deccd651117fdf8845<br>---<br>M TODO-RELEASE<br>M include/Makefile.am<br>A include/osmocom/core/tlv.h<br>M include/osmocom/gsm/tlv.h<br>M src/Makefile.am<br>M src/gsm/Makefile.am<br>R src/tlv_parser.c<br>M tests/Makefile.am<br>M tests/tlv/tlv_test.c<br>9 files changed, 728 insertions(+), 709 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/59/26459/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/TODO-RELEASE b/TODO-RELEASE</span><br><span>index 8ccfa49..f8ba440 100644</span><br><span>--- a/TODO-RELEASE</span><br><span>+++ b/TODO-RELEASE</span><br><span>@@ -7,3 +7,4 @@</span><br><span> # If any interfaces have been added since the last public release: c:r:a + 1.</span><br><span> # If any interfaces have been removed or changed since the last public release: c:r:0.</span><br><span> #library       what                    description / commit summary line</span><br><span style="color: hsl(120, 100%, 40%);">+libosmogsm   osmocom/gsm/tlv.h       gsm/tlv.h and gsm/tlv_parser.c moved to libosmocore</span><br><span>diff --git a/include/Makefile.am b/include/Makefile.am</span><br><span>index e3246cf..948cc70 100644</span><br><span>--- a/include/Makefile.am</span><br><span>+++ b/include/Makefile.am</span><br><span>@@ -62,6 +62,7 @@</span><br><span>                        osmocom/core/sockaddr_str.h \</span><br><span>                        osmocom/core/time_cc.h \</span><br><span>                        osmocom/core/use_count.h \</span><br><span style="color: hsl(120, 100%, 40%);">+                       osmocom/core/tlv.h \</span><br><span>                        osmocom/crypt/auth.h \</span><br><span>                        osmocom/crypt/gprs_cipher.h \</span><br><span>                        osmocom/crypt/kdf.h \</span><br><span>diff --git a/include/osmocom/core/tlv.h b/include/osmocom/core/tlv.h</span><br><span>new file mode 100644</span><br><span>index 0000000..7e6dfb5</span><br><span>--- /dev/null</span><br><span>+++ b/include/osmocom/core/tlv.h</span><br><span>@@ -0,0 +1,708 @@</span><br><span style="color: hsl(120, 100%, 40%);">+#pragma once</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <stdint.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <string.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/msgb.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/byteswap.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/bit16gen.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/bit32gen.h></span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \defgroup tlv TLV parser</span><br><span style="color: hsl(120, 100%, 40%);">+ *  @{</span><br><span style="color: hsl(120, 100%, 40%);">+ * \file tlv.h */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Terminology / wording</span><br><span style="color: hsl(120, 100%, 40%);">+               tag     length          value   (in bits)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       V   -       -               8</span><br><span style="color: hsl(120, 100%, 40%);">+        LV   -       8               N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+   TLV   8       8               N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ TL16V   8       16              N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ TLV16   8       8               N * 16</span><br><span style="color: hsl(120, 100%, 40%);">+         TvLV   8       8/16            N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ vTvLV   8/16    8/16            N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+ T16LV   16      8               N * 8</span><br><span style="color: hsl(120, 100%, 40%);">+*/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a LV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define LV_GROSS_LEN(x)            (x+1)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TLV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLV_GROSS_LEN(x)        (x+2)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TLV16 type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLV16_GROSS_LEN(x)    ((2*x)+2)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TL16V type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TL16V_GROSS_LEN(x)        (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a L16TV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define L16TV_GROSS_LEN(x)    (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a T16LV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+#define T16LV_GROSS_LEN(x)    (x+3)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! maximum length of TLV of one byte length */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TVLV_MAX_ONEBYTE      0x7f</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! error return codes of various TLV parser functions */</span><br><span style="color: hsl(120, 100%, 40%);">+enum osmo_tlv_parser_error {</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_TLVP_ERR_OFS_BEYOND_BUFFER         = -1,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER     = -2,</span><br><span style="color: hsl(120, 100%, 40%);">+ OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE          = -3,</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       OSMO_TLVP_ERR_MAND_IE_MISSING           = -50,</span><br><span style="color: hsl(120, 100%, 40%);">+        OSMO_TLVP_ERR_IE_TOO_SHORT              = -51,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of a TVLV type field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t TVLV_GROSS_LEN(uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+               return TLV_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+    else</span><br><span style="color: hsl(120, 100%, 40%);">+          return TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of vTvL header (tag+len) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     uint16_t ret = 2;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+                ret++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      if (len > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+                ret++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! gross length of vTvLV (tag+len+val) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+               ret = TLV_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+     else</span><br><span style="color: hsl(120, 100%, 40%);">+          ret = TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(120, 100%, 40%);">+                ret += 1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TLV generation */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a LV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                            const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Append a TLV field, a Tag-Length-Value field.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[out] buf  Location in a buffer to append TLV at.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag  Tag id to write.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] len  Length field to write and amount of bytes to append.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] val  Pointer to data to append, or NULL to append zero data.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Always append tag and length. Append \a len bytes read from \a val. If val is NULL, append \a len zero</span><br><span style="color: hsl(120, 100%, 40%);">+ * bytes instead. If \a len is zero, do not append any data apart from tag and length. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                         const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ if (len) {</span><br><span style="color: hsl(120, 100%, 40%);">+            if (val)</span><br><span style="color: hsl(120, 100%, 40%);">+                      memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+                else</span><br><span style="color: hsl(120, 100%, 40%);">+                  memset(buf, 0, len);</span><br><span style="color: hsl(120, 100%, 40%);">+  }</span><br><span style="color: hsl(120, 100%, 40%);">+     return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL field (a TLV field but omitting the value part). */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl_put(uint8_t *buf, uint8_t tag, uint8_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TLV16 field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                         const uint16_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len*2);</span><br><span style="color: hsl(120, 100%, 40%);">+      return buf + len*2;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL16V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+      memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL16 field. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tl16_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+      return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL16V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *t16lv_put(uint8_t *buf, uint16_t tag, uint8_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        *buf++ = tag >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = tag & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = len;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvLV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                             const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ret = tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+            buf[1] |= 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else</span><br><span style="color: hsl(120, 100%, 40%);">+                ret = tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvL field (a TvLV with variable-size length, where the value part's length is already known, but will</span><br><span style="color: hsl(120, 100%, 40%);">+ * be put() later).</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to the value's start position.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tvl_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+             ret = tl_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+          buf[1] |= 0x80;</span><br><span style="color: hsl(120, 100%, 40%);">+       } else</span><br><span style="color: hsl(120, 100%, 40%);">+                ret = tl16_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a variable-length tag or variable-length length * */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     if (tag > TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(120, 100%, 40%);">+              /* two-byte TAG */</span><br><span style="color: hsl(120, 100%, 40%);">+            *buf++ = 0x80 | (tag >> 8);</span><br><span style="color: hsl(120, 100%, 40%);">+             *buf++ = (tag & 0xff);</span><br><span style="color: hsl(120, 100%, 40%);">+    } else</span><br><span style="color: hsl(120, 100%, 40%);">+                *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* put (append) vTvL (GAN) field (tag + length)*/</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ret = vt_gan_put(buf, tag);</span><br><span style="color: hsl(120, 100%, 40%);">+   return vt_gan_put(ret, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* put (append) vTvLV (GAN) field (tag + length + val) */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                                const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *ret;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+       ret = vtvl_gan_put(buf, tag, len );</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(ret, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        ret = buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+    return ret;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TLV16 field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+   return tlv16_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL16V field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+   return tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_t16lv_put(struct msgb *msg, uint16_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ uint8_t *buf = msgb_put(msg, T16LV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+   return t16lv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvL field to \ref msgb, i.e. a TvLV with variable-size length, where the value's length is already</span><br><span style="color: hsl(120, 100%, 40%);">+ * known, but will be put() later. The value section is not yet reserved, only tag and variable-length are put in the</span><br><span style="color: hsl(120, 100%, 40%);">+ * msgb.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns pointer to the value's start position and end of the msgb.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvl_put(struct msgb *msg, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+    return tvl_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvLV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                               const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+    return tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put_16be(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   uint16_t val_be = osmo_htons(val);</span><br><span style="color: hsl(120, 100%, 40%);">+    return msgb_tvlv_put(msg, tag, 2, (const uint8_t *)&val_be);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_put_32be(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint32_t val_be = osmo_htonl(val);</span><br><span style="color: hsl(120, 100%, 40%);">+    return msgb_tvlv_put(msg, tag, 4, (const uint8_t *)&val_be);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a vTvLV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                                        uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+  return vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a L16TV field to \ref msgb */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = len >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = len & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a V field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *v_put(uint8_t *buf, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     *buf++ = val;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TV field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(120, 100%, 40%);">+                           uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val;</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TVfixed field */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                             unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(buf, val, len);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf + len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TV16 field</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in,out] buf data buffer</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tag Tag value</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] val Value (in host byte order!)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(120, 100%, 40%);">+                              uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf++ = val >> 8;</span><br><span style="color: hsl(120, 100%, 40%);">+      *buf++ = val & 0xff;</span><br><span style="color: hsl(120, 100%, 40%);">+      return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a LV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+      return lv_put(buf, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+     return tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t *buf = msgb_put(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+      return tv_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TVfixed field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                                       unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t *buf = msgb_put(msg, 1+len);</span><br><span style="color: hsl(120, 100%, 40%);">+  return tv_fixed_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a V field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      uint8_t *buf = msgb_put(msg, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+      return v_put(buf, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TL fields to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to the length field so it can be updated after adding new information under specified tag */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tl_put(struct msgb *msg, uint8_t tag)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t *len = msgb_v_put(msg, tag);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        /* reserve space for length, len points to this reserved space already */</span><br><span style="color: hsl(120, 100%, 40%);">+     msgb_v_put(msg, 0);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return len;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TV16 field (network order) to the given msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t *buf = msgb_put(msg, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+      return tv16_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! put (append) a TV32 field (network order) to the given msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv32_put(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t *buf = msgb_put(msg, 1 + 4);</span><br><span style="color: hsl(120, 100%, 40%);">+  *buf++ = tag;</span><br><span style="color: hsl(120, 100%, 40%);">+ osmo_store32be(val, buf);</span><br><span style="color: hsl(120, 100%, 40%);">+     return msg->tail;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! push (prepend) a TLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+    tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+  return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! push 1-byte tagged value */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tlv1_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       return msgb_tlv_push(msg, tag, 1, &val);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! push (prepend) a TV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t *buf = msgb_push(msg, 2);</span><br><span style="color: hsl(120, 100%, 40%);">+     tv_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+        return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! push (prepend) a TV16 field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint8_t *buf = msgb_push(msg, 3);</span><br><span style="color: hsl(120, 100%, 40%);">+     tv16_put(buf, tag, val);</span><br><span style="color: hsl(120, 100%, 40%);">+      return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! push (prepend) a TvLV field to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(120, 100%, 40%);">+                                     const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(120, 100%, 40%);">+   tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+ return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* push (prepend) a vTvL header to a \ref msgb</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                                         uint16_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+  vtvl_gan_put(buf, tag, len);</span><br><span style="color: hsl(120, 100%, 40%);">+  return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+                                           uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(120, 100%, 40%);">+ vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(120, 100%, 40%);">+    return buf;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* TLV parsing */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Entry in a TLV parser array */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_p_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t len;           /*!< length */</span><br><span style="color: hsl(120, 100%, 40%);">+     const uint8_t *val;     /*!< pointer to value */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! TLV type */</span><br><span style="color: hsl(120, 100%, 40%);">+enum tlv_type {</span><br><span style="color: hsl(120, 100%, 40%);">+   TLV_TYPE_NONE,          /*!< no type */</span><br><span style="color: hsl(120, 100%, 40%);">+    TLV_TYPE_FIXED,         /*!< fixed-length value-only */</span><br><span style="color: hsl(120, 100%, 40%);">+    TLV_TYPE_T,             /*!< tag-only */</span><br><span style="color: hsl(120, 100%, 40%);">+   TLV_TYPE_TV,            /*!< tag-value (8bit) */</span><br><span style="color: hsl(120, 100%, 40%);">+   TLV_TYPE_TLV,           /*!< tag-length-value */</span><br><span style="color: hsl(120, 100%, 40%);">+   TLV_TYPE_TL16V,         /*!< tag, 16 bit length, value */</span><br><span style="color: hsl(120, 100%, 40%);">+  TLV_TYPE_TvLV,          /*!< tag, variable length, value */</span><br><span style="color: hsl(120, 100%, 40%);">+        TLV_TYPE_SINGLE_TV,     /*!< tag and value (both 4 bit) in 1 byte */</span><br><span style="color: hsl(120, 100%, 40%);">+       TLV_TYPE_vTvLV_GAN,     /*!< variable-length tag, variable-length length */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Definition of a single IE (Information Element) */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_def {</span><br><span style="color: hsl(120, 100%, 40%);">+        enum tlv_type type;     /*!< TLV type */</span><br><span style="color: hsl(120, 100%, 40%);">+   uint8_t fixed_len;      /*!< length in case of \ref TLV_TYPE_FIXED */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Definition of All 256 IE / TLV */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_definition {</span><br><span style="color: hsl(120, 100%, 40%);">+        struct tlv_def def[256];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! result of the TLV parser */</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_parsed {</span><br><span style="color: hsl(120, 100%, 40%);">+  struct tlv_p_entry lv[256];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct tlv_definition tvlv_att_def;</span><br><span style="color: hsl(120, 100%, 40%);">+extern struct tlv_definition vtvlv_gan_att_def;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const struct tlv_definition *def,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const uint8_t *buf, int buf_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,</span><br><span style="color: hsl(120, 100%, 40%);">+              const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,</span><br><span style="color: hsl(120, 100%, 40%);">+          const struct tlv_definition *def, const uint8_t *buf, int buf_len,</span><br><span style="color: hsl(120, 100%, 40%);">+            uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(120, 100%, 40%);">+/* take a master (src) tlv def and fill up all empty slots in 'dst' */</span><br><span style="color: hsl(120, 100%, 40%);">+void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,</span><br><span style="color: hsl(120, 100%, 40%);">+             unsigned int len, const uint8_t *val);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp);</span><br><span style="color: hsl(120, 100%, 40%);">+int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,</span><br><span style="color: hsl(120, 100%, 40%);">+                  const uint8_t *tag_order, unsigned int tag_order_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_PRESENT(x, y)    ((x)->lv[y].val)</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_LEN(x, y)             (x)->lv[y].len</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_VAL(x, y)               (x)->lv[y].val</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_PRES_LEN(tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+        (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Return pointer to a TLV element if it is present.</span><br><span style="color: hsl(120, 100%, 40%);">+ * Usage:</span><br><span style="color: hsl(120, 100%, 40%);">+ *   struct tlv_p_entry *e = TVLP_GET(&tp, TAG);</span><br><span style="color: hsl(120, 100%, 40%);">+ *   if (!e)</span><br><span style="color: hsl(120, 100%, 40%);">+ *         return -ENOENT;</span><br><span style="color: hsl(120, 100%, 40%);">+ *   hexdump(e->val, e->len);</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns struct tlv_p_entry pointer, or NULL if not present.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_GET(_tp, tag)     (TLVP_PRESENT(_tp, tag)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Like TLVP_GET(), but enforcing a minimum val length.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] min_len  Minimum value length in bytes.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns struct tlv_p_entry pointer, or NULL if not present or too short.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_GET_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+     (TLVP_PRES_LEN(_tp, tag, min_len)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Like TLVP_VAL(), but enforcing a minimum val length.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param[in] min_len  Minimum value length in bytes.</span><br><span style="color: hsl(120, 100%, 40%);">+ * \returns const uint8_t pointer to value, or NULL if not present or too short.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+#define TLVP_VAL_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(120, 100%, 40%);">+       (TLVP_PRES_LEN(_tp, tag, min_len)? (_tp)->lv[tag].val : NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Obtain 1-byte TLV element.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tag the Tag to look for</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] default_val default value to use if tag not available</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns the 1st byte of value with a given tag or default_val if tag was not found</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint8_t tlvp_val8(const struct tlv_parsed *tp, uint8_t tag, uint8_t default_val)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+     const uint8_t *res = TLVP_VAL_MINLEN(tp, tag, 1);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   if (res)</span><br><span style="color: hsl(120, 100%, 40%);">+              return res[0];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+      return default_val;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Align given TLV element with 16 bit value to an even address</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns aligned 16 bit value</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+       uint16_t res;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Align given TLV element with 32 bit value to an address that is a multiple of 4</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns aligned 32 bit value</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+    uint32_t res;</span><br><span style="color: hsl(120, 100%, 40%);">+ memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(120, 100%, 40%);">+     return res;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns aligned 16 bit value in host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint16_t tlvp_val16be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return osmo_load16be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] pos element to return</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns aligned 32 bit value in host byte order</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t tlvp_val32be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      return osmo_load32be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                       size_t len, uint8_t **value);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                       uint8_t tag, size_t len, uint8_t **value);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                   uint8_t *tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+                uint8_t tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_shift_lv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(120, 100%, 40%);">+               uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#define MSG_DEF(name, mand_ies, flags) { name, mand_ies, ARRAY_SIZE(mand_ies), flags }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_msg_def {</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! human-readable name of message type (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! array of mandatory IEs */</span><br><span style="color: hsl(120, 100%, 40%);">+ const uint8_t *mand_ies;</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! number of entries in 'mand_ies' above */</span><br><span style="color: hsl(120, 100%, 40%);">+  uint8_t mand_count;</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! user-defined flags (like uplink/downlink/...) */</span><br><span style="color: hsl(120, 100%, 40%);">+  uint32_t flags;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_ie_def {</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! minimum length of IE value part, in octets */</span><br><span style="color: hsl(120, 100%, 40%);">+     uint16_t min_len;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! huamn-readable name (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! Osmocom TLV protocol definition */</span><br><span style="color: hsl(120, 100%, 40%);">+struct osmo_tlv_prot_def {</span><br><span style="color: hsl(120, 100%, 40%);">+   /*! human-readable name of protocol */</span><br><span style="color: hsl(120, 100%, 40%);">+        const char *name;</span><br><span style="color: hsl(120, 100%, 40%);">+     /*! TLV parser definition (optional) */</span><br><span style="color: hsl(120, 100%, 40%);">+       const struct tlv_definition *tlv_def;</span><br><span style="color: hsl(120, 100%, 40%);">+ /*! definition of each message (8-bit message type) */</span><br><span style="color: hsl(120, 100%, 40%);">+        struct osmo_tlv_prot_msg_def msg_def[256];</span><br><span style="color: hsl(120, 100%, 40%);">+    /*! definition of IE for each 8-bit tag */</span><br><span style="color: hsl(120, 100%, 40%);">+    struct osmo_tlv_prot_ie_def ie_def[256];</span><br><span style="color: hsl(120, 100%, 40%);">+      /*! value_string array of message type names (legacy, if not populated in msg_def) */</span><br><span style="color: hsl(120, 100%, 40%);">+ const struct value_string *msgt_names;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type);</span><br><span style="color: hsl(120, 100%, 40%);">+const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                           const struct tlv_parsed *tp, int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,</span><br><span style="color: hsl(120, 100%, 40%);">+                     struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,</span><br><span style="color: hsl(120, 100%, 40%);">+                 const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,</span><br><span style="color: hsl(120, 100%, 40%);">+                    int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static inline uint32_t osmo_tlv_prot_msgt_flags(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   return pdef->msg_def[msg_type].flags;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! @} */</span><br><span>diff --git a/include/osmocom/gsm/tlv.h b/include/osmocom/gsm/tlv.h</span><br><span>index 7e6dfb5..05a7261 100644</span><br><span>--- a/include/osmocom/gsm/tlv.h</span><br><span>+++ b/include/osmocom/gsm/tlv.h</span><br><span>@@ -1,708 +1,8 @@</span><br><span> #pragma once</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-#include <stdint.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <string.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/msgb.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/byteswap.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/bit16gen.h></span><br><span style="color: hsl(0, 100%, 40%);">-#include <osmocom/core/bit32gen.h></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! \defgroup tlv TLV parser</span><br><span style="color: hsl(0, 100%, 40%);">- *  @{</span><br><span style="color: hsl(0, 100%, 40%);">- * \file tlv.h */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* Terminology / wording</span><br><span style="color: hsl(0, 100%, 40%);">-            tag     length          value   (in bits)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-           V   -       -               8</span><br><span style="color: hsl(0, 100%, 40%);">-          LV   -       8               N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-     TLV   8       8               N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-   TL16V   8       16              N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-   TLV16   8       8               N * 16</span><br><span style="color: hsl(0, 100%, 40%);">-   TvLV   8       8/16            N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-   vTvLV   8/16    8/16            N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-   T16LV   16      8               N * 8</span><br><span style="color: hsl(0, 100%, 40%);">-*/</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a LV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define LV_GROSS_LEN(x)            (x+1)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TLV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLV_GROSS_LEN(x)    (x+2)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TLV16 type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLV16_GROSS_LEN(x)        ((2*x)+2)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TL16V type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TL16V_GROSS_LEN(x)    (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a L16TV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define L16TV_GROSS_LEN(x)        (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a T16LV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-#define T16LV_GROSS_LEN(x)        (x+3)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! maximum length of TLV of one byte length */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TVLV_MAX_ONEBYTE    0x7f</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! error return codes of various TLV parser functions */</span><br><span style="color: hsl(0, 100%, 40%);">-enum osmo_tlv_parser_error {</span><br><span style="color: hsl(0, 100%, 40%);">-       OSMO_TLVP_ERR_OFS_BEYOND_BUFFER         = -1,</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_TLVP_ERR_OFS_LEN_BEYOND_BUFFER     = -2,</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_TLVP_ERR_UNKNOWN_TLV_TYPE          = -3,</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   OSMO_TLVP_ERR_MAND_IE_MISSING           = -50,</span><br><span style="color: hsl(0, 100%, 40%);">-  OSMO_TLVP_ERR_IE_TOO_SHORT              = -51,</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of a TVLV type field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t TVLV_GROSS_LEN(uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">-         return TLV_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-      else</span><br><span style="color: hsl(0, 100%, 40%);">-            return TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of vTvL header (tag+len) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t VTVL_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- uint16_t ret = 2;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">-          ret++;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  if (len > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">-          ret++;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! gross length of vTvLV (tag+len+val) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t VTVLV_GAN_GROSS_LEN(uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint16_t ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (len <= TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">-         ret = TLV_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-       else</span><br><span style="color: hsl(0, 100%, 40%);">-            ret = TL16V_GROSS_LEN(len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     if (tag > TVLV_MAX_ONEBYTE)</span><br><span style="color: hsl(0, 100%, 40%);">-          ret += 1;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* TLV generation */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a LV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *lv_put(uint8_t *buf, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                          const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Append a TLV field, a Tag-Length-Value field.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[out] buf  Location in a buffer to append TLV at.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag  Tag id to write.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] len  Length field to write and amount of bytes to append.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] val  Pointer to data to append, or NULL to append zero data.</span><br><span style="color: hsl(0, 100%, 40%);">- * Always append tag and length. Append \a len bytes read from \a val. If val is NULL, append \a len zero</span><br><span style="color: hsl(0, 100%, 40%);">- * bytes instead. If \a len is zero, do not append any data apart from tag and length. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tlv_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                               const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">-   if (len) {</span><br><span style="color: hsl(0, 100%, 40%);">-              if (val)</span><br><span style="color: hsl(0, 100%, 40%);">-                        memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-          else</span><br><span style="color: hsl(0, 100%, 40%);">-                    memset(buf, 0, len);</span><br><span style="color: hsl(0, 100%, 40%);">-    }</span><br><span style="color: hsl(0, 100%, 40%);">-       return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL field (a TLV field but omitting the value part). */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl_put(uint8_t *buf, uint8_t tag, uint8_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">-   return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV16 field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tlv16_put(uint8_t *buf, uint8_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                           const uint16_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(buf, val, len*2);</span><br><span style="color: hsl(0, 100%, 40%);">-        return buf + len*2;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl16v_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                          const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">-        memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16 field. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tl16_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">-        return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *t16lv_put(uint8_t *buf, uint16_t tag, uint8_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                          const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = tag >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = tag & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = len;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tvlv_put(uint8_t *buf, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                               const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">-               ret = tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-              buf[1] |= 0x80;</span><br><span style="color: hsl(0, 100%, 40%);">- } else</span><br><span style="color: hsl(0, 100%, 40%);">-          ret = tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvL field (a TvLV with variable-size length, where the value part's length is already known, but will</span><br><span style="color: hsl(0, 100%, 40%);">- * be put() later).</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to the value's start position.</span><br><span style="color: hsl(120, 100%, 40%);">+/* Up to libosmocore v1.6.0, tlv.h lived in osmocom/gsm/tlv.h.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The file was moved to osmocom/core/tlv.h, because TLV turned out to be useful outside of GSM.</span><br><span style="color: hsl(120, 100%, 40%);">+ * For easy compatibility, this file remains as redirection to core/tlv.h.</span><br><span>  */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tvl_put(uint8_t *buf, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *ret;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-       if (len <= TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">-               ret = tl_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-            buf[1] |= 0x80;</span><br><span style="color: hsl(0, 100%, 40%);">- } else</span><br><span style="color: hsl(0, 100%, 40%);">-          ret = tl16_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a variable-length tag or variable-length length * */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vt_gan_put(uint8_t *buf, uint16_t tag)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- if (tag > TVLV_MAX_ONEBYTE) {</span><br><span style="color: hsl(0, 100%, 40%);">-                /* two-byte TAG */</span><br><span style="color: hsl(0, 100%, 40%);">-              *buf++ = 0x80 | (tag >> 8);</span><br><span style="color: hsl(0, 100%, 40%);">-               *buf++ = (tag & 0xff);</span><br><span style="color: hsl(0, 100%, 40%);">-      } else</span><br><span style="color: hsl(0, 100%, 40%);">-          *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* put (append) vTvL (GAN) field (tag + length)*/</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vtvl_gan_put(uint8_t *buf, uint16_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-     uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   ret = vt_gan_put(buf, tag);</span><br><span style="color: hsl(0, 100%, 40%);">-     return vt_gan_put(ret, len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* put (append) vTvLV (GAN) field (tag + length + val) */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *vtvlv_gan_put(uint8_t *buf, uint16_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                                  const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *ret;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-   ret = vtvl_gan_put(buf, tag, len );</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     memcpy(ret, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  ret = buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-        return ret;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV16 field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv16_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint16_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       uint8_t *buf = msgb_put(msg, TLV16_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-     return tlv16_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL16V field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tl16v_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                                  const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t *buf = msgb_put(msg, TL16V_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-     return tl16v_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_t16lv_put(struct msgb *msg, uint16_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint8_t *buf = msgb_put(msg, T16LV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-     return t16lv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvL field to \ref msgb, i.e. a TvLV with variable-size length, where the value's length is already</span><br><span style="color: hsl(0, 100%, 40%);">- * known, but will be put() later. The value section is not yet reserved, only tag and variable-length are put in the</span><br><span style="color: hsl(0, 100%, 40%);">- * msgb.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns pointer to the value's start position and end of the msgb.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvl_put(struct msgb *msg, uint8_t tag, uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-      return tvl_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                                 const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *buf = msgb_put(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-      return tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put_16be(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       uint16_t val_be = osmo_htons(val);</span><br><span style="color: hsl(0, 100%, 40%);">-      return msgb_tvlv_put(msg, tag, 2, (const uint8_t *)&val_be);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TvLV field containing a big-endian 16bit value to msgb. */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_put_32be(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint32_t val_be = osmo_htonl(val);</span><br><span style="color: hsl(0, 100%, 40%);">-      return msgb_tvlv_put(msg, tag, 4, (const uint8_t *)&val_be);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a vTvLV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvlv_gan_put(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                                          uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t *buf = msgb_put(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">-    return vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a L16TV field to \ref msgb */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_l16tv_put(struct msgb *msg, uint16_t len, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                                       const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint8_t *buf = msgb_put(msg, L16TV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     *buf++ = len >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = len & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a V field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *v_put(uint8_t *buf, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = val;</span><br><span style="color: hsl(0, 100%, 40%);">-   return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(0, 100%, 40%);">-                             uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = val;</span><br><span style="color: hsl(0, 100%, 40%);">-   return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TVfixed field */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv_fixed_put(uint8_t *buf, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                               unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(buf, val, len);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf + len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV16 field</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in,out] buf data buffer</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tag Tag value</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] val Value (in host byte order!)</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *tv16_put(uint8_t *buf, uint8_t tag, </span><br><span style="color: hsl(0, 100%, 40%);">-                                uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">- *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   *buf++ = val >> 8;</span><br><span style="color: hsl(0, 100%, 40%);">-        *buf++ = val & 0xff;</span><br><span style="color: hsl(0, 100%, 40%);">-        return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a LV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_lv_put(struct msgb *msg, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint8_t *buf = msgb_put(msg, LV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-        return lv_put(buf, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv_put(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *buf = msgb_put(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-       return tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_put(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *buf = msgb_put(msg, 2);</span><br><span style="color: hsl(0, 100%, 40%);">-        return tv_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TVfixed field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_fixed_put(struct msgb *msg, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                                   unsigned int len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *buf = msgb_put(msg, 1+len);</span><br><span style="color: hsl(0, 100%, 40%);">-    return tv_fixed_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a V field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_v_put(struct msgb *msg, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t *buf = msgb_put(msg, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-        return v_put(buf, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TL fields to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to the length field so it can be updated after adding new information under specified tag */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tl_put(struct msgb *msg, uint8_t tag)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *len = msgb_v_put(msg, tag);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-    /* reserve space for length, len points to this reserved space already */</span><br><span style="color: hsl(0, 100%, 40%);">-       msgb_v_put(msg, 0);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-     return len;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV16 field (network order) to the given msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv16_put(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *buf = msgb_put(msg, 3);</span><br><span style="color: hsl(0, 100%, 40%);">-        return tv16_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! put (append) a TV32 field (network order) to the given msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte after newly-put information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv32_put(struct msgb *msg, uint8_t tag, uint32_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *buf = msgb_put(msg, 1 + 4);</span><br><span style="color: hsl(0, 100%, 40%);">-    *buf++ = tag;</span><br><span style="color: hsl(0, 100%, 40%);">-   osmo_store32be(val, buf);</span><br><span style="color: hsl(0, 100%, 40%);">-       return msg->tail;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv_push(struct msgb *msg, uint8_t tag, uint8_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        uint8_t *buf = msgb_push(msg, TLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-      tlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-    return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push 1-byte tagged value */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tlv1_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   return msgb_tlv_push(msg, tag, 1, &val);</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv_push(struct msgb *msg, uint8_t tag, uint8_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *buf = msgb_push(msg, 2);</span><br><span style="color: hsl(0, 100%, 40%);">-       tv_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-  return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TV16 field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tv16_push(struct msgb *msg, uint8_t tag, uint16_t val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  uint8_t *buf = msgb_push(msg, 3);</span><br><span style="color: hsl(0, 100%, 40%);">-       tv16_put(buf, tag, val);</span><br><span style="color: hsl(0, 100%, 40%);">-        return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! push (prepend) a TvLV field to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns pointer to first byte of newly-pushed information */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_tvlv_push(struct msgb *msg, uint8_t tag, uint16_t len,</span><br><span style="color: hsl(0, 100%, 40%);">-                                 const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-      uint8_t *buf = msgb_push(msg, TVLV_GROSS_LEN(len));</span><br><span style="color: hsl(0, 100%, 40%);">-     tvlv_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-   return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* push (prepend) a vTvL header to a \ref msgb</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvl_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     uint16_t len)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       uint8_t *buf = msgb_push(msg, VTVL_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">-    vtvl_gan_put(buf, tag, len);</span><br><span style="color: hsl(0, 100%, 40%);">-    return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t *msgb_vtvlv_gan_push(struct msgb *msg, uint16_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-                                     uint16_t len, const uint8_t *val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint8_t *buf = msgb_push(msg, VTVLV_GAN_GROSS_LEN(tag, len));</span><br><span style="color: hsl(0, 100%, 40%);">-   vtvlv_gan_put(buf, tag, len, val);</span><br><span style="color: hsl(0, 100%, 40%);">-      return buf;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/* TLV parsing */</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Entry in a TLV parser array */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_p_entry {</span><br><span style="color: hsl(0, 100%, 40%);">-     uint16_t len;           /*!< length */</span><br><span style="color: hsl(0, 100%, 40%);">-       const uint8_t *val;     /*!< pointer to value */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! TLV type */</span><br><span style="color: hsl(0, 100%, 40%);">-enum tlv_type {</span><br><span style="color: hsl(0, 100%, 40%);">-     TLV_TYPE_NONE,          /*!< no type */</span><br><span style="color: hsl(0, 100%, 40%);">-      TLV_TYPE_FIXED,         /*!< fixed-length value-only */</span><br><span style="color: hsl(0, 100%, 40%);">-      TLV_TYPE_T,             /*!< tag-only */</span><br><span style="color: hsl(0, 100%, 40%);">-     TLV_TYPE_TV,            /*!< tag-value (8bit) */</span><br><span style="color: hsl(0, 100%, 40%);">-     TLV_TYPE_TLV,           /*!< tag-length-value */</span><br><span style="color: hsl(0, 100%, 40%);">-     TLV_TYPE_TL16V,         /*!< tag, 16 bit length, value */</span><br><span style="color: hsl(0, 100%, 40%);">-    TLV_TYPE_TvLV,          /*!< tag, variable length, value */</span><br><span style="color: hsl(0, 100%, 40%);">-  TLV_TYPE_SINGLE_TV,     /*!< tag and value (both 4 bit) in 1 byte */</span><br><span style="color: hsl(0, 100%, 40%);">- TLV_TYPE_vTvLV_GAN,     /*!< variable-length tag, variable-length length */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Definition of a single IE (Information Element) */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_def {</span><br><span style="color: hsl(0, 100%, 40%);">-  enum tlv_type type;     /*!< TLV type */</span><br><span style="color: hsl(0, 100%, 40%);">-     uint8_t fixed_len;      /*!< length in case of \ref TLV_TYPE_FIXED */</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Definition of All 256 IE / TLV */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_definition {</span><br><span style="color: hsl(0, 100%, 40%);">-  struct tlv_def def[256];</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! result of the TLV parser */</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_parsed {</span><br><span style="color: hsl(0, 100%, 40%);">-    struct tlv_p_entry lv[256];</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-extern struct tlv_definition tvlv_att_def;</span><br><span style="color: hsl(0, 100%, 40%);">-extern struct tlv_definition vtvlv_gan_att_def;</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse_one(uint8_t *o_tag, uint16_t *o_len, const uint8_t **o_val,</span><br><span style="color: hsl(0, 100%, 40%);">-                  const struct tlv_definition *def,</span><br><span style="color: hsl(0, 100%, 40%);">-                  const uint8_t *buf, int buf_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def,</span><br><span style="color: hsl(0, 100%, 40%);">-          const uint8_t *buf, int buf_len, uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_parse2(struct tlv_parsed *dec, int dec_multiples,</span><br><span style="color: hsl(0, 100%, 40%);">-              const struct tlv_definition *def, const uint8_t *buf, int buf_len,</span><br><span style="color: hsl(0, 100%, 40%);">-              uint8_t lv_tag, uint8_t lv_tag2);</span><br><span style="color: hsl(0, 100%, 40%);">-/* take a master (src) tlv def and fill up all empty slots in 'dst' */</span><br><span style="color: hsl(0, 100%, 40%);">-void tlv_def_patch(struct tlv_definition *dst, const struct tlv_definition *src);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_encode_one(struct msgb *msg, enum tlv_type type, uint8_t tag,</span><br><span style="color: hsl(0, 100%, 40%);">-               unsigned int len, const uint8_t *val);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_encode(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp);</span><br><span style="color: hsl(0, 100%, 40%);">-int tlv_encode_ordered(struct msgb *msg, const struct tlv_definition *def, const struct tlv_parsed *tp,</span><br><span style="color: hsl(0, 100%, 40%);">-                        const uint8_t *tag_order, unsigned int tag_order_len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_PRESENT(x, y)        ((x)->lv[y].val)</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_LEN(x, y)               (x)->lv[y].len</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_VAL(x, y)         (x)->lv[y].val</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_PRES_LEN(tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">-      (TLVP_PRESENT(tp, tag) && TLVP_LEN(tp, tag) >= min_len)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Return pointer to a TLV element if it is present.</span><br><span style="color: hsl(0, 100%, 40%);">- * Usage:</span><br><span style="color: hsl(0, 100%, 40%);">- *   struct tlv_p_entry *e = TVLP_GET(&tp, TAG);</span><br><span style="color: hsl(0, 100%, 40%);">- *   if (!e)</span><br><span style="color: hsl(0, 100%, 40%);">- *         return -ENOENT;</span><br><span style="color: hsl(0, 100%, 40%);">- *   hexdump(e->val, e->len);</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns struct tlv_p_entry pointer, or NULL if not present.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_GET(_tp, tag)     (TLVP_PRESENT(_tp, tag)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Like TLVP_GET(), but enforcing a minimum val length.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] min_len  Minimum value length in bytes.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns struct tlv_p_entry pointer, or NULL if not present or too short.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_GET_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">-       (TLVP_PRES_LEN(_tp, tag, min_len)? &(_tp)->lv[tag] : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Like TLVP_VAL(), but enforcing a minimum val length.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] _tp  pointer to \ref tlv_parsed.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] tag  IE tag to return.</span><br><span style="color: hsl(0, 100%, 40%);">- * \param[in] min_len  Minimum value length in bytes.</span><br><span style="color: hsl(0, 100%, 40%);">- * \returns const uint8_t pointer to value, or NULL if not present or too short.</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-#define TLVP_VAL_MINLEN(_tp, tag, min_len) \</span><br><span style="color: hsl(0, 100%, 40%);">- (TLVP_PRES_LEN(_tp, tag, min_len)? (_tp)->lv[tag].val : NULL)</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Obtain 1-byte TLV element.</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tag the Tag to look for</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] default_val default value to use if tag not available</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns the 1st byte of value with a given tag or default_val if tag was not found</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint8_t tlvp_val8(const struct tlv_parsed *tp, uint8_t tag, uint8_t default_val)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   const uint8_t *res = TLVP_VAL_MINLEN(tp, tag, 1);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-       if (res)</span><br><span style="color: hsl(0, 100%, 40%);">-                return res[0];</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-  return default_val;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Align given TLV element with 16 bit value to an even address</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns aligned 16 bit value</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t tlvp_val16_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   uint16_t res;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(0, 100%, 40%);">-       return res;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Align given TLV element with 32 bit value to an address that is a multiple of 4</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns aligned 32 bit value</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t tlvp_val32_unal(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-        uint32_t res;</span><br><span style="color: hsl(0, 100%, 40%);">-   memcpy(&res, TLVP_VAL(tp, pos), sizeof(res));</span><br><span style="color: hsl(0, 100%, 40%);">-       return res;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns aligned 16 bit value in host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint16_t tlvp_val16be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-       return osmo_load16be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Retrieve (possibly unaligned) TLV element and convert to host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] tp pointer to \ref tlv_parsed</span><br><span style="color: hsl(0, 100%, 40%);">- *  \param[in] pos element to return</span><br><span style="color: hsl(0, 100%, 40%);">- *  \returns aligned 32 bit value in host byte order</span><br><span style="color: hsl(0, 100%, 40%);">- */</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t tlvp_val32be(const struct tlv_parsed *tp, int pos)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-  return osmo_load32be(TLVP_VAL(tp, pos));</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct tlv_parsed *osmo_tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlvp_merge(struct tlv_parsed *dst, const struct tlv_parsed *src);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_v_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">-                     size_t len, uint8_t **value);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_match_shift_tv_fixed(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">-                           uint8_t tag, size_t len, uint8_t **value);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">-               uint8_t *tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_match_shift_tlv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">-            uint8_t tag, uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_shift_lv(uint8_t **data, size_t *data_len,</span><br><span style="color: hsl(0, 100%, 40%);">-           uint8_t **value, size_t *value_len);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-#define MSG_DEF(name, mand_ies, flags) { name, mand_ies, ARRAY_SIZE(mand_ies), flags }</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_msg_def {</span><br><span style="color: hsl(0, 100%, 40%);">-      /*! human-readable name of message type (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">-   const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! array of mandatory IEs */</span><br><span style="color: hsl(0, 100%, 40%);">-   const uint8_t *mand_ies;</span><br><span style="color: hsl(0, 100%, 40%);">-        /*! number of entries in 'mand_ies' above */</span><br><span style="color: hsl(0, 100%, 40%);">-    uint8_t mand_count;</span><br><span style="color: hsl(0, 100%, 40%);">-     /*! user-defined flags (like uplink/downlink/...) */</span><br><span style="color: hsl(0, 100%, 40%);">-    uint32_t flags;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_ie_def {</span><br><span style="color: hsl(0, 100%, 40%);">-  /*! minimum length of IE value part, in octets */</span><br><span style="color: hsl(0, 100%, 40%);">-       uint16_t min_len;</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! huamn-readable name (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">-   const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! Osmocom TLV protocol definition */</span><br><span style="color: hsl(0, 100%, 40%);">-struct osmo_tlv_prot_def {</span><br><span style="color: hsl(0, 100%, 40%);">-     /*! human-readable name of protocol */</span><br><span style="color: hsl(0, 100%, 40%);">-  const char *name;</span><br><span style="color: hsl(0, 100%, 40%);">-       /*! TLV parser definition (optional) */</span><br><span style="color: hsl(0, 100%, 40%);">- const struct tlv_definition *tlv_def;</span><br><span style="color: hsl(0, 100%, 40%);">-   /*! definition of each message (8-bit message type) */</span><br><span style="color: hsl(0, 100%, 40%);">-  struct osmo_tlv_prot_msg_def msg_def[256];</span><br><span style="color: hsl(0, 100%, 40%);">-      /*! definition of IE for each 8-bit tag */</span><br><span style="color: hsl(0, 100%, 40%);">-      struct osmo_tlv_prot_ie_def ie_def[256];</span><br><span style="color: hsl(0, 100%, 40%);">-        /*! value_string array of message type names (legacy, if not populated in msg_def) */</span><br><span style="color: hsl(0, 100%, 40%);">-   const struct value_string *msgt_names;</span><br><span style="color: hsl(0, 100%, 40%);">-};</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-const char *osmo_tlv_prot_msg_name(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type);</span><br><span style="color: hsl(0, 100%, 40%);">-const char *osmo_tlv_prot_ie_name(const struct osmo_tlv_prot_def *pdef, uint8_t iei);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlv_prot_validate_tp(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type,</span><br><span style="color: hsl(0, 100%, 40%);">-                         const struct tlv_parsed *tp, int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-int osmo_tlv_prot_parse(const struct osmo_tlv_prot_def *pdef,</span><br><span style="color: hsl(0, 100%, 40%);">-                   struct tlv_parsed *dec, unsigned int dec_multiples, uint8_t msg_type,</span><br><span style="color: hsl(0, 100%, 40%);">-                   const uint8_t *buf, unsigned int buf_len, uint8_t lv_tag, uint8_t lv_tag2,</span><br><span style="color: hsl(0, 100%, 40%);">-                      int log_subsys, const char *log_pfx);</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-static inline uint32_t osmo_tlv_prot_msgt_flags(const struct osmo_tlv_prot_def *pdef, uint8_t msg_type)</span><br><span style="color: hsl(0, 100%, 40%);">-{</span><br><span style="color: hsl(0, 100%, 40%);">-   return pdef->msg_def[msg_type].flags;</span><br><span style="color: hsl(0, 100%, 40%);">-}</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(0, 100%, 40%);">-/*! @} */</span><br><span style="color: hsl(120, 100%, 40%);">+#include <osmocom/core/tlv.h></span><br><span>diff --git a/src/Makefile.am b/src/Makefile.am</span><br><span>index 4bda456..953b53b 100644</span><br><span>--- a/src/Makefile.am</span><br><span>+++ b/src/Makefile.am</span><br><span>@@ -33,6 +33,7 @@</span><br><span>                          it_q.c \</span><br><span>                     probes.d \</span><br><span>                   base64.c \</span><br><span style="color: hsl(120, 100%, 40%);">+                    tlv_parser.c \</span><br><span>                       $(NULL)</span><br><span> </span><br><span> if HAVE_SSSE3</span><br><span>diff --git a/src/gsm/Makefile.am b/src/gsm/Makefile.am</span><br><span>index 580e397..8b19a5e 100644</span><br><span>--- a/src/gsm/Makefile.am</span><br><span>+++ b/src/gsm/Makefile.am</span><br><span>@@ -20,7 +20,7 @@</span><br><span> </span><br><span> BUILT_SOURCES = gsm0503_conv.c</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-libgsmint_la_SOURCES =  a5.c rxlev_stat.c tlv_parser.c comp128.c comp128v23.c \</span><br><span style="color: hsl(120, 100%, 40%);">+libgsmint_la_SOURCES =  a5.c rxlev_stat.c comp128.c comp128v23.c \</span><br><span>                      gsm_utils.c rsl.c gsm48.c gsm48_arfcn_range_encode.c \</span><br><span>                       gsm48_ie.c gsm0808.c sysinfo.c \</span><br><span>                     gprs_cipher_core.c gprs_rlc.c gsm0480.c abis_nm.c gsm0502.c \</span><br><span>diff --git a/src/gsm/tlv_parser.c b/src/tlv_parser.c</span><br><span>similarity index 100%</span><br><span>rename from src/gsm/tlv_parser.c</span><br><span>rename to src/tlv_parser.c</span><br><span>diff --git a/tests/Makefile.am b/tests/Makefile.am</span><br><span>index a6f6017..176532e 100644</span><br><span>--- a/tests/Makefile.am</span><br><span>+++ b/tests/Makefile.am</span><br><span>@@ -240,7 +240,7 @@</span><br><span>               $(top_builddir)/src/gsm/libosmogsm.la</span><br><span> </span><br><span> tlv_tlv_test_SOURCES = tlv/tlv_test.c</span><br><span style="color: hsl(0, 100%, 40%);">-tlv_tlv_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span style="color: hsl(120, 100%, 40%);">+tlv_tlv_test_LDADD = $(LDADD)</span><br><span> </span><br><span> gsup_gsup_test_SOURCES = gsup/gsup_test.c</span><br><span> gsup_gsup_test_LDADD = $(LDADD) $(top_builddir)/src/gsm/libosmogsm.la</span><br><span>diff --git a/tests/tlv/tlv_test.c b/tests/tlv/tlv_test.c</span><br><span>index fdd15ab..f30f635 100644</span><br><span>--- a/tests/tlv/tlv_test.c</span><br><span>+++ b/tests/tlv/tlv_test.c</span><br><span>@@ -288,6 +288,14 @@</span><br><span>      OSMO_ASSERT(dec3[2].lv[tag].val == &test_data[2 + 3 + 3]);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static const struct tlv_definition test_tlvdef = {</span><br><span style="color: hsl(120, 100%, 40%);">+   .def = {</span><br><span style="color: hsl(120, 100%, 40%);">+              [0x17] = { TLV_TYPE_TLV },</span><br><span style="color: hsl(120, 100%, 40%);">+            [0x2c] = { TLV_TYPE_TV },</span><br><span style="color: hsl(120, 100%, 40%);">+             [0x40] = { TLV_TYPE_TV },</span><br><span style="color: hsl(120, 100%, 40%);">+     },</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void test_tlv_encoder()</span><br><span> {</span><br><span>      const uint8_t enc_ies[] = {</span><br><span>@@ -312,11 +320,11 @@</span><br><span>  OSMO_ASSERT(msg);</span><br><span> </span><br><span>        /* decode BSSAP IEs specified above */</span><br><span style="color: hsl(0, 100%, 40%);">-  rc = osmo_bssap_tlv_parse(&tp, enc_ies, ARRAY_SIZE(enc_ies));</span><br><span style="color: hsl(120, 100%, 40%);">+     rc = tlv_parse(&tp, &test_tlvdef, enc_ies, ARRAY_SIZE(enc_ies), 0, 0);</span><br><span>       OSMO_ASSERT(rc == 3);</span><br><span> </span><br><span>    /* re-encode it */</span><br><span style="color: hsl(0, 100%, 40%);">-      rc = tlv_encode(msg, gsm0808_att_tlvdef(), &tp);</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = tlv_encode(msg, &test_tlvdef, &tp);</span><br><span>     OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span>      OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies, ARRAY_SIZE(enc_ies)));</span><br><span> </span><br><span>@@ -325,7 +333,7 @@</span><br><span>        printf("Testing TLV encoder with IE ordering\n");</span><br><span> </span><br><span>      /* re-encodei in different order */</span><br><span style="color: hsl(0, 100%, 40%);">-     rc = tlv_encode_ordered(msg, gsm0808_att_tlvdef(), &tp, ie_order, ARRAY_SIZE(ie_order));</span><br><span style="color: hsl(120, 100%, 40%);">+  rc = tlv_encode_ordered(msg, &test_tlvdef, &tp, ie_order, ARRAY_SIZE(ie_order));</span><br><span>     OSMO_ASSERT(rc == ARRAY_SIZE(enc_ies));</span><br><span>      OSMO_ASSERT(!memcmp(msgb_data(msg), enc_ies_reordered, ARRAY_SIZE(enc_ies_reordered)));</span><br><span> </span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/c/libosmocore/+/26459">change 26459</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.osmocom.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.osmocom.org/c/libosmocore/+/26459"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: libosmocore </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-Change-Id: I3cda0143af9eaf90dc73f9deccd651117fdf8845 </div>
<div style="display:none"> Gerrit-Change-Number: 26459 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: neels <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>