<p>Harald Welte <strong>merged</strong> this change.</p><p><a href="https://gerrit.osmocom.org/12153">View Change</a></p><div style="white-space:pre-wrap">Approvals:
  Harald Welte: Looks good to me, approved
  Pau Espin Pedrol: Looks good to me, but someone else must approve
  Jenkins Builder: Verified

</div><pre style="font-family: monospace,monospace; white-space: pre-wrap;">add osmo_bcd2str()<br><br>Add a standalone bcd-to-string conversion function with generic parameters.<br>Add a regression test in utils_test.c.<br><br>So far there is no single universal implementation that converts a BCD to a<br>string. I could only find gsm48_mi_to_string(), which also interprets<br>surrounding bytes, MI type and TMSI as non-BCD value.<br><br>The idea is to use this function from gsm48_mi_to_string() and similar<br>implementations in subsequent commits.<br><br>Root cause: in osmo-msc, I want to have an alternative MI-to-string function<br>for composing an FSM name, which needs the BCD part of gsm48_mi_to_string() but<br>not the TMSI part.<br><br>Change-Id: I86b09d37ceef33331c1a56046a5443127d6c6be0<br>---<br>M include/osmocom/core/utils.h<br>M src/utils.c<br>M tests/utils/utils_test.c<br>M tests/utils/utils_test.ok<br>4 files changed, 186 insertions(+), 0 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h</span><br><span>index 0b54c88..04f2fb4 100644</span><br><span>--- a/include/osmocom/core/utils.h</span><br><span>+++ b/include/osmocom/core/utils.h</span><br><span>@@ -49,6 +49,8 @@</span><br><span> /* only works for numbers in ascci */</span><br><span> uint8_t osmo_char2bcd(char c);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int osmo_hexparse(const char *str, uint8_t *b, int max_len);</span><br><span> </span><br><span> char *osmo_ubit_dump(const uint8_t *bits, unsigned int len);</span><br><span>diff --git a/src/utils.c b/src/utils.c</span><br><span>index e6adcf8..4b4e6d2 100644</span><br><span>--- a/src/utils.c</span><br><span>+++ b/src/utils.c</span><br><span>@@ -129,6 +129,47 @@</span><br><span>           return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! Convert BCD to string.</span><br><span style="color: hsl(120, 100%, 40%);">+ * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0xf, nibble 1 is bcd[0] >> 4, nibble</span><br><span style="color: hsl(120, 100%, 40%);">+ * 3 is bcd[1] & 0xf, etc..</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[out] dst  Output string buffer, is always nul terminated when dst_size > 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] dst_size  sizeof() the output string buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] bcd  Binary coded data buffer.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] start_nibble  Offset to start from, in nibbles, typically 1 to skip the first nibble.</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] end_nibble  Offset to stop before, in nibbles, e.g. sizeof(bcd) - (bcd[0] & GSM_MI_ODD? 0:1).</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \param[in] allow_hex  If false, return error if there are digits other than 0-9. If true, return those as [A-F].</span><br><span style="color: hsl(120, 100%, 40%);">+ *  \returns The strlen that would be written if the output buffer is large enough, excluding nul byte (like</span><br><span style="color: hsl(120, 100%, 40%);">+ *           snprintf()), or -EINVAL if allow_hex is false and a digit > 9 is encountered. On -EINVAL, the conversion is</span><br><span style="color: hsl(120, 100%, 40%);">+ *           still completed as if allow_hex were passed as true. Return -ENOMEM if dst is NULL or dst_size is zero.</span><br><span style="color: hsl(120, 100%, 40%);">+ *           If end_nibble <= start_nibble, write an empty string to dst and return 0.</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+      char *dst_end = dst + dst_size - 1;</span><br><span style="color: hsl(120, 100%, 40%);">+   int nibble_i;</span><br><span style="color: hsl(120, 100%, 40%);">+ int rc = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!dst || dst_size < 1)</span><br><span style="color: hsl(120, 100%, 40%);">+          return -ENOMEM;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) {</span><br><span style="color: hsl(120, 100%, 40%);">+              uint8_t nibble = bcd[nibble_i >> 1];</span><br><span style="color: hsl(120, 100%, 40%);">+            if ((nibble_i & 1))</span><br><span style="color: hsl(120, 100%, 40%);">+                       nibble >>= 4;</span><br><span style="color: hsl(120, 100%, 40%);">+           nibble &= 0xf;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          if (!allow_hex && nibble > 9)</span><br><span style="color: hsl(120, 100%, 40%);">+                      rc = -EINVAL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               *dst = osmo_bcd2char(nibble);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+     *dst = '\0';</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+        if (rc < 0)</span><br><span style="color: hsl(120, 100%, 40%);">+                return rc;</span><br><span style="color: hsl(120, 100%, 40%);">+    return OSMO_MAX(0, end_nibble - start_nibble);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! Parse a string containing hexadecimal digits</span><br><span>  *  \param[in] str string containing ASCII encoded hexadecimal digits</span><br><span>  *  \param[out] b output buffer</span><br><span>diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c</span><br><span>index 2bb1f9c..a773b3f 100644</span><br><span>--- a/tests/utils/utils_test.c</span><br><span>+++ b/tests/utils/utils_test.c</span><br><span>@@ -32,6 +32,7 @@</span><br><span> #include <time.h></span><br><span> #include <netinet/in.h></span><br><span> #include <arpa/inet.h></span><br><span style="color: hsl(120, 100%, 40%);">+#include <errno.h></span><br><span> </span><br><span> static void hexdump_test(void)</span><br><span> {</span><br><span>@@ -383,6 +384,118 @@</span><br><span>        }</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct bcd2str_test {</span><br><span style="color: hsl(120, 100%, 40%);">+     const char *bcd_hex;</span><br><span style="color: hsl(120, 100%, 40%);">+  int start_nibble;</span><br><span style="color: hsl(120, 100%, 40%);">+     int end_nibble;</span><br><span style="color: hsl(120, 100%, 40%);">+       bool allow_hex;</span><br><span style="color: hsl(120, 100%, 40%);">+       size_t str_size;</span><br><span style="color: hsl(120, 100%, 40%);">+      const char *expect_str;</span><br><span style="color: hsl(120, 100%, 40%);">+       int expect_rc;</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 const struct bcd2str_test bcd2str_tests[] = {</span><br><span style="color: hsl(120, 100%, 40%);">+    {</span><br><span style="color: hsl(120, 100%, 40%);">+             .bcd_hex = "1a 32 54 76 98 f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 11,</span><br><span style="color: hsl(120, 100%, 40%);">+             .expect_str = "1234567890",</span><br><span style="color: hsl(120, 100%, 40%);">+         .expect_rc = 10,</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%);">+             .bcd_hex = "1a 32 a4 cb 9d f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 11,</span><br><span style="color: hsl(120, 100%, 40%);">+             .expect_str = "1234ABCD90",</span><br><span style="color: hsl(120, 100%, 40%);">+         .expect_rc = -EINVAL,</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%);">+             .bcd_hex = "1a 32 a4 cb 9d f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 11,</span><br><span style="color: hsl(120, 100%, 40%);">+             .allow_hex = true,</span><br><span style="color: hsl(120, 100%, 40%);">+            .expect_str = "1234ABCD90",</span><br><span style="color: hsl(120, 100%, 40%);">+         .expect_rc = 10,</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%);">+             .bcd_hex = "1a 32 54 76 98 f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 12,</span><br><span style="color: hsl(120, 100%, 40%);">+             .expect_str = "1234567890F",</span><br><span style="color: hsl(120, 100%, 40%);">+                .expect_rc = -EINVAL,</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%);">+             .bcd_hex = "1a 32 54 76 98 f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 12,</span><br><span style="color: hsl(120, 100%, 40%);">+             .allow_hex = true,</span><br><span style="color: hsl(120, 100%, 40%);">+            .expect_str = "1234567890F",</span><br><span style="color: hsl(120, 100%, 40%);">+                .expect_rc = 11,</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%);">+             .bcd_hex = "1a 32 54 76 98 f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 0,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 12,</span><br><span style="color: hsl(120, 100%, 40%);">+             .allow_hex = true,</span><br><span style="color: hsl(120, 100%, 40%);">+            .expect_str = "A1234567890F",</span><br><span style="color: hsl(120, 100%, 40%);">+               .expect_rc = 12,</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%);">+             .bcd_hex = "1a 32 54 76 98 f0",</span><br><span style="color: hsl(120, 100%, 40%);">+             .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 12,</span><br><span style="color: hsl(120, 100%, 40%);">+             .str_size = 5,</span><br><span style="color: hsl(120, 100%, 40%);">+                .expect_str = "1234",</span><br><span style="color: hsl(120, 100%, 40%);">+               .expect_rc = 11,</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%);">+             .bcd_hex = "",</span><br><span style="color: hsl(120, 100%, 40%);">+              .start_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+            .end_nibble = 1,</span><br><span style="color: hsl(120, 100%, 40%);">+              .expect_str = "",</span><br><span style="color: hsl(120, 100%, 40%);">+           .expect_rc = 0,</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 void bcd2str_test(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+   int i;</span><br><span style="color: hsl(120, 100%, 40%);">+        uint8_t bcd[64];</span><br><span style="color: hsl(120, 100%, 40%);">+      int rc;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+     printf("\nTesting bcd to string conversion\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+   for (i = 0; i < ARRAY_SIZE(bcd2str_tests); i++) {</span><br><span style="color: hsl(120, 100%, 40%);">+          const struct bcd2str_test *t = &bcd2str_tests[i];</span><br><span style="color: hsl(120, 100%, 40%);">+         char str[64] = {};</span><br><span style="color: hsl(120, 100%, 40%);">+            size_t str_size = t->str_size ? : sizeof(str);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+           osmo_hexparse(t->bcd_hex, bcd, sizeof(bcd));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+             printf("- BCD-input='%s' nibbles=[%d..%d[ str_size=%zu\n", t->bcd_hex,</span><br><span style="color: hsl(120, 100%, 40%);">+                  t->start_nibble, t->end_nibble, str_size);</span><br><span style="color: hsl(120, 100%, 40%);">+               rc = osmo_bcd2str(str, str_size, bcd, t->start_nibble, t->end_nibble, t->allow_hex);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+               printf("  rc=%d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          OSMO_ASSERT(str[str_size-1] == '\0');</span><br><span style="color: hsl(120, 100%, 40%);">+         printf("  -> %s\n", osmo_quote_str(str, -1));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+          if (rc != t->expect_rc)</span><br><span style="color: hsl(120, 100%, 40%);">+                    printf("    ERROR: expected rc=%d\n", t->expect_rc);</span><br><span style="color: hsl(120, 100%, 40%);">+             if (strcmp(str, t->expect_str))</span><br><span style="color: hsl(120, 100%, 40%);">+                    printf("    ERROR: expected result %s\n", osmo_quote_str(t->expect_str, -1));</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%);">+   printf("- zero output buffer\n");</span><br><span style="color: hsl(120, 100%, 40%);">+   rc = osmo_bcd2str(NULL, 100, bcd, 1, 2, false);</span><br><span style="color: hsl(120, 100%, 40%);">+       printf("  bcd2str(NULL, ...) -> %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+      OSMO_ASSERT(rc < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+       rc = osmo_bcd2str((char*)23, 0, bcd, 1, 2, false);</span><br><span style="color: hsl(120, 100%, 40%);">+    printf("  bcd2str(dst, 0, ...) -> %d\n", rc);</span><br><span style="color: hsl(120, 100%, 40%);">+    OSMO_ASSERT(rc < 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static void str_escape_test(void)</span><br><span> {</span><br><span>       int i;</span><br><span>@@ -810,6 +923,7 @@</span><br><span>         test_ipa_ccm_id_resp_parsing();</span><br><span>      test_is_hexstr();</span><br><span>    bcd_test();</span><br><span style="color: hsl(120, 100%, 40%);">+   bcd2str_test();</span><br><span>      str_escape_test();</span><br><span>   str_quote_test();</span><br><span>    isqrt_test();</span><br><span>diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok</span><br><span>index 3ea8ec6..65e32ed 100644</span><br><span>--- a/tests/utils/utils_test.ok</span><br><span>+++ b/tests/utils/utils_test.ok</span><br><span>@@ -80,6 +80,35 @@</span><br><span>  val=0xe, expected=E, found=E</span><br><span>         val=0xf, expected=F, found=F</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+Testing bcd to string conversion</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 54 76 98 f0' nibbles=[1..11[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=10</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234567890"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=-22</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234ABCD90"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=10</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234ABCD90"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=-22</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234567890F"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=11</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234567890F"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 54 76 98 f0' nibbles=[0..12[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=12</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "A1234567890F"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=5</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=11</span><br><span style="color: hsl(120, 100%, 40%);">+  -> "1234"</span><br><span style="color: hsl(120, 100%, 40%);">+- BCD-input='' nibbles=[1..1[ str_size=64</span><br><span style="color: hsl(120, 100%, 40%);">+  rc=0</span><br><span style="color: hsl(120, 100%, 40%);">+  -> ""</span><br><span style="color: hsl(120, 100%, 40%);">+- zero output buffer</span><br><span style="color: hsl(120, 100%, 40%);">+  bcd2str(NULL, ...) -> -12</span><br><span style="color: hsl(120, 100%, 40%);">+  bcd2str(dst, 0, ...) -> -12</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> Testing string escaping</span><br><span> - all chars from 0 to 255 in batches of 16:</span><br><span> "\0\1\2\3\4\5\6\a\b\t\n\v\f\r\14\15"</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.osmocom.org/12153">change 12153</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/12153"/><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-MessageType: merged </div>
<div style="display:none"> Gerrit-Change-Id: I86b09d37ceef33331c1a56046a5443127d6c6be0 </div>
<div style="display:none"> Gerrit-Change-Number: 12153 </div>
<div style="display:none"> Gerrit-PatchSet: 7 </div>
<div style="display:none"> Gerrit-Owner: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Harald Welte <laforge@gnumonks.org> </div>
<div style="display:none"> Gerrit-Reviewer: Jenkins Builder (1000002) </div>
<div style="display:none"> Gerrit-Reviewer: Neels Hofmeyr <nhofmeyr@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Pau Espin Pedrol <pespin@sysmocom.de> </div>
<div style="display:none"> Gerrit-Reviewer: Stefan Sperling <stsp@stsp.name> </div>