Review at  https://gerrit.osmocom.org/7843

add gsm0808_cell_{id,id_list}_name() and friends

Provide comprehensive API to obtain string representations of Cell Identifiers
and -Lists.

Change gsm0808_test.c to use the new functions (which simplifies the output a
bit), so that we don't duplicate printing code in gsm0808_test.c, and so that
the not-so-trivial printing code is also tested.

In gsm0808_test, also test gsm0808_cell_id_list_name_buf()'s return value and
truncation behavior.

The rationale for gsm0808_cell_id_list_name(), i.e. printing an entire list of
cell identifiers, is that even though the maximum is 127 elements, a list of
more than a few elements is hardly ever expected in practice (even more than
one element isn't actually expected: either "entire BSS" or a single LAC). It
is thus useful to log the entire list when it shows up in Paging and Handover.

Change-Id: I9b2106805422f96c5cc96ebb9178451355582df3
---
M include/osmocom/gsm/gsm0808_utils.h
M include/osmocom/gsm/protocol/gsm_08_08.h
M src/gsm/gsm0808_utils.c
M src/gsm/libosmogsm.map
M tests/gsm0808/gsm0808_test.c
M tests/gsm0808/gsm0808_test.ok
6 files changed, 199 insertions(+), 98 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/libosmocore refs/changes/43/7843/1

diff --git a/include/osmocom/gsm/gsm0808_utils.h 
b/include/osmocom/gsm/gsm0808_utils.h
index 8e71b43..c007c28 100644
--- a/include/osmocom/gsm/gsm0808_utils.h
+++ b/include/osmocom/gsm/gsm0808_utils.h
@@ -55,6 +55,16 @@
        unsigned int id_list_len;
 };
 
+extern const struct value_string gsm0808_cell_id_discr_names[];
+static inline const char *gsm0808_cell_id_discr_name(enum CELL_IDENT id_discr)
+{ return get_value_string(gsm0808_cell_id_discr_names, id_discr); }
+
+const char *gsm0808_cell_id_name(const struct gsm0808_cell_id *cid);
+const char *gsm0808_cell_id_list_name(const struct gsm0808_cell_id_list2 *cil);
+int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct 
gsm0808_cell_id_list2 *cil);
+int gsm0808_cell_id_u_name(char *buf, size_t buflen,
+                          enum CELL_IDENT id_discr, const union 
gsm0808_cell_id_u *u);
+
 uint8_t gsm0808_enc_aoip_trasp_addr(struct msgb *msg,
                                    const struct sockaddr_storage *ss);
 int gsm0808_dec_aoip_trasp_addr(struct sockaddr_storage *ss,
diff --git a/include/osmocom/gsm/protocol/gsm_08_08.h 
b/include/osmocom/gsm/protocol/gsm_08_08.h
index 4539b96..5d2864f 100644
--- a/include/osmocom/gsm/protocol/gsm_08_08.h
+++ b/include/osmocom/gsm/protocol/gsm_08_08.h
@@ -10,7 +10,8 @@
 
 /*
  * this is from GSM 03.03 CGI but is copied in GSM 08.08
- * in § 3.2.2.27 for Cell Identifier List
+ * in § 3.2.2.27 for Cell Identifier List.
+ * See gsm0808_cell_id_discr_name() for a human readable representation.
  */
 enum CELL_IDENT {
        CELL_IDENT_WHOLE_GLOBAL         = 0,
diff --git a/src/gsm/gsm0808_utils.c b/src/gsm/gsm0808_utils.c
index 73d9362..7ed4873 100644
--- a/src/gsm/gsm0808_utils.c
+++ b/src/gsm/gsm0808_utils.c
@@ -1130,4 +1130,132 @@
        return 0;
 }
 
+/*! Print a human readable name of the cell identifier to the char buffer.
+ * This is useful both for struct gsm0808_cell_id and struct 
gsm0808_cell_id_list2.
+ * See also gsm0808_cell_id_name() and gsm0808_cell_id_list_name().
+ * \param[out] buf  Destination buffer to write string representation to.
+ * \param[in] buflen  Amount of memory available in \a buf.
+ * \param[in] id_discr  Cell Identifier type.
+ * \param[in] u  Cell Identifer value.
+ * \returns Like snprintf(): the amount of characters (excluding terminating 
nul) written,
+ *          or that would have been written if the buffer were large enough.
+ */
+int gsm0808_cell_id_u_name(char *buf, size_t buflen,
+                          enum CELL_IDENT id_discr, const union 
gsm0808_cell_id_u *u)
+{
+       switch (id_discr) {
+       case CELL_IDENT_LAC:
+               return snprintf(buf, buflen, "%u", u->lac);
+       case CELL_IDENT_CI:
+               return snprintf(buf, buflen, "%u", u->ci);
+       case CELL_IDENT_LAC_AND_CI:
+               return snprintf(buf, buflen, "%u-%u", u->lac_and_ci.lac, 
u->lac_and_ci.ci);
+       case CELL_IDENT_LAI_AND_LAC:
+               return snprintf(buf, buflen, "%s", 
osmo_lai_name(&u->lai_and_lac));
+       case CELL_IDENT_WHOLE_GLOBAL:
+               return snprintf(buf, buflen, "%s", osmo_cgi_name(&u->global));
+       default:
+               /* For CELL_IDENT_BSS and CELL_IDENT_NO_CELL, just print the 
discriminator.
+                * Same for kinds we have no string representation of yet. */
+               return snprintf(buf, buflen, "%s", 
gsm0808_cell_id_discr_name(id_discr));
+       }
+}
+
+/*! value_string[] for enum CELL_IDENT. */
+const struct value_string gsm0808_cell_id_discr_names[] = {
+       { CELL_IDENT_WHOLE_GLOBAL, "CGI" },
+       { CELL_IDENT_LAC_AND_CI, "LAC-CI" },
+       { CELL_IDENT_CI, "CI" },
+       { CELL_IDENT_NO_CELL, "NO-CELL" },
+       { CELL_IDENT_LAI_AND_LAC, "LAI" },
+       { CELL_IDENT_LAC, "LAC" },
+       { CELL_IDENT_BSS, "BSS" },
+       { CELL_IDENT_UTRAN_PLMN_LAC_RNC, "UTRAN-PLMN-LAC-RNC" },
+       { CELL_IDENT_UTRAN_RNC, "UTRAN-RNC" },
+       { CELL_IDENT_UTRAN_LAC_RNC, "UTRAN-LAC-RNC" },
+       { 0, NULL }
+};
+
+#define APPEND_THING(func, args...) do { \
+               int remain = buflen - (pos - buf); \
+               int l = func(pos, remain, ##args); \
+               if (l < 0 || l > remain) \
+                       pos = buf + buflen; \
+               else \
+                       pos += l; \
+               if (l > 0) \
+                       total_len += l; \
+       } while(0)
+#define APPEND_STR(fmt, args...) APPEND_THING(snprintf, fmt, ##args)
+#define APPEND_CELL_ID_U(DISCR, U) APPEND_THING(gsm0808_cell_id_u_name, DISCR, 
U)
+
+/*! Return a human readable representation of a Cell Identifier, like "LAC:123"
+ * or "CGI:001-01-42-23".
+ * \param[in] cid  Cell Identifer.
+ * \returns String in a static buffer.
+ */
+const char *gsm0808_cell_id_name(const struct gsm0808_cell_id *cid)
+{
+       static char buf[64];
+       const size_t buflen = sizeof(buf);
+       char *pos = buf;
+       int total_len = 0;
+       APPEND_STR("%s:", gsm0808_cell_id_discr_name(cid->id_discr));
+       APPEND_CELL_ID_U(cid->id_discr, &cid->id);
+       return buf;
+}
+
+/*! Return a human readable representation of the Cell Identifier List, like
+ * "LAC[2]:{123, 456}".
+ * The return value semantics are like snprintf() and thus allow ensuring a 
complete
+ * untruncated string by determining the required string length from the 
return value.
+ * If buflen > 0, always nul-terminate the string in buf, also when it is 
truncated.
+ * If buflen == 0, do not modify buf, just return the would-be length.
+ * \param[out] buf  Destination buffer to write string representation to.
+ * \param[in] buflen  Amount of memory available in \a buf.
+ * \param[in] cil  Cell Identifer List.
+ * \returns Like snprintf(): the amount of characters (excluding terminating 
nul) written,
+ *          or that would have been written if the buffer were large enough.
+ */
+int gsm0808_cell_id_list_name_buf(char *buf, size_t buflen, const struct 
gsm0808_cell_id_list2 *cil)
+{
+       char *pos = buf;
+       int total_len = 0;
+       int i;
+
+       APPEND_STR("%s[%u]", gsm0808_cell_id_discr_name(cil->id_discr), 
cil->id_list_len);
+
+       switch (cil->id_discr) {
+       case CELL_IDENT_BSS:
+       case CELL_IDENT_NO_CELL:
+               return total_len;
+       default:
+               break;
+       }
+
+       APPEND_STR(":{");
+
+       for (i = 0; i < cil->id_list_len; i++) {
+               if (i)
+                       APPEND_STR(", ");
+               APPEND_CELL_ID_U(cil->id_discr, &cil->id_list[i]);
+       }
+
+       APPEND_STR("}");
+       return total_len;
+}
+
+/*! Return a human-readable representation of \a cil in a static buffer.
+ * If the list is too long, the output may be truncated.
+ * See also gsm0808_cell_id_list_name_buf(). */
+const char *gsm0808_cell_id_list_name(const struct gsm0808_cell_id_list2 *cil)
+{
+       static char buf[1024];
+       gsm0808_cell_id_list_name_buf(buf, sizeof(buf), cil);
+       return buf;
+}
+
+#undef APPEND_STR
+#undef APPEND_CELL_ID_U
+
 /*! @} */
diff --git a/src/gsm/libosmogsm.map b/src/gsm/libosmogsm.map
index 388fcc0..f5ed1bb 100644
--- a/src/gsm/libosmogsm.map
+++ b/src/gsm/libosmogsm.map
@@ -179,6 +179,11 @@
 gsm0808_cell_id_list_add;
 gsm0808_enc_cell_id;
 gsm0808_dec_cell_id;
+gsm0808_cell_id_name;
+gsm0808_cell_id_list_name;
+gsm0808_cell_id_list_name_buf;
+gsm0808_cell_id_discr_names;
+gsm0808_cell_id_u_name;
 gsm0808_chan_type_to_speech_codec;
 gsm0808_speech_codec_from_chan_type;
 gsm0808_speech_codec_type_names;
diff --git a/tests/gsm0808/gsm0808_test.c b/tests/gsm0808/gsm0808_test.c
index 49673fe..78238ff 100644
--- a/tests/gsm0808/gsm0808_test.c
+++ b/tests/gsm0808/gsm0808_test.c
@@ -1073,36 +1073,12 @@
 
 static void print_cil(const struct gsm0808_cell_id_list2 *cil)
 {
-       unsigned int i;
-       if (!cil) {
-               printf("     cell_id_list == NULL\n");
-               return;
-       }
-       switch (cil->id_discr) {
-       case CELL_IDENT_WHOLE_GLOBAL:
-               printf("     cell_id_list cgi[%u] = {\n", cil->id_list_len);
-               for (i = 0; i < cil->id_list_len; i++)
-                       printf("       %2d: %s\n", i, 
osmo_cgi_name(&cil->id_list[i].global));
-               printf("     }\n");
-               break;
-       case CELL_IDENT_LAC:
-               printf("     cell_id_list lac[%u] = {\n", cil->id_list_len);
-               for (i = 0; i < cil->id_list_len; i++)
-                       printf("      %2d: %u\n", i, cil->id_list[i].lac);
-               printf("     }\n");
-               break;
-       case CELL_IDENT_BSS:
-               printf("     cell_id_list bss[%u]\n", cil->id_list_len);
-               break;
-       case CELL_IDENT_NO_CELL:
-               printf("     cell_id_list no_cell[%u]\n", cil->id_list_len);
-               break;
-       default:
-               printf("     Unimplemented id_disc\n");
-       }
+       printf("     cell_id_list == %s\n", gsm0808_cell_id_list_name(cil));
 }
 
 void test_cell_id_list_add() {
+       size_t zu;
+
        const struct gsm0808_cell_id_list2 cgi1 = {
                .id_discr = CELL_IDENT_WHOLE_GLOBAL,
                .id_list_len = 1,
@@ -1220,7 +1196,7 @@
 
 #define ADD_QUIET(other_cil, expect_rc) do { \
                int rc = gsm0808_cell_id_list_add(&cil, &other_cil); \
-               printf("\ngsm0808_cell_id_list_add(&cil, &" #other_cil ") --> 
rc = %d\n", rc); \
+               printf("gsm0808_cell_id_list_add(&cil, &" #other_cil ") --> rc 
= %d\n", rc); \
                OSMO_ASSERT(rc == expect_rc); \
        } while(0)
 
@@ -1233,13 +1209,13 @@
        ADD(cil, 0);
        ADD(cgi1, -EINVAL);
 
-       printf("\ncan't add to BSS list\n");
+       printf("* can't add to BSS list\n");
        cil.id_list_len = 0;
        cil.id_discr = CELL_IDENT_BSS;
        print_cil(&cil);
        ADD(lac1, -EINVAL);
 
-       printf("\nother types (including NO_CELL) take on new type iff 
empty\n");
+       printf("* other types (including NO_CELL) take on new type iff 
empty\n");
        cil.id_list_len = 0;
        cil.id_discr = CELL_IDENT_NO_CELL;
        print_cil(&cil);
@@ -1248,15 +1224,34 @@
        ADD(cgi2, 2);
        ADD(cgi2, 0);
 
+       printf("* test gsm0808_cell_id_list_name_buf()'s return val\n");
+       zu = strlen(gsm0808_cell_id_list_name(&cil));
+       printf("  strlen(gsm0808_cell_id_list_name(cil)) == %zu\n", zu);
+       zu ++;
+       while (1) {
+               char buf[128] = "?";
+               int rc;
+               OSMO_ASSERT(zu < sizeof(buf));
+               buf[zu] = '#';
+               rc = gsm0808_cell_id_list_name_buf(buf, zu, &cil);
+               printf("  gsm0808_cell_id_list_name_buf(buf, %zu, cil)) == %d 
\"%s\"\n",
+                      zu, rc, buf);
+               OSMO_ASSERT(buf[zu] == '#');
+               if (!zu)
+                       break;
+               zu /= 2;
+       }
+
+       printf("* list-full behavior\n");
        cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1;
-       printf("\ncil.id_list_len = %u", cil.id_list_len);
+       printf("cil.id_list_len = %u\n", cil.id_list_len);
        ADD_QUIET(cgi2a, 1);
        printf("cil.id_list_len = %u\n", cil.id_list_len);
 
        cil.id_list_len = GSM0808_CELL_ID_LIST2_MAXLEN - 1;
-       printf("\ncil.id_list_len = %u", cil.id_list_len);
+       printf("cil.id_list_len = %u\n", cil.id_list_len);
        ADD_QUIET(cgi3, -ENOSPC);
-       printf("cil.id_list_len = %u", cil.id_list_len);
+       printf("cil.id_list_len = %u\n", cil.id_list_len);
        ADD_QUIET(cgi2a, -ENOSPC);
        printf("cil.id_list_len = %u\n", cil.id_list_len);
 
diff --git a/tests/gsm0808/gsm0808_test.ok b/tests/gsm0808/gsm0808_test.ok
index 34d7ebf..6cd7982 100644
--- a/tests/gsm0808/gsm0808_test.ok
+++ b/tests/gsm0808/gsm0808_test.ok
@@ -21,84 +21,46 @@
 Testing prepend DTAP
 test_gsm0808_enc_dec_cell_id_list_lac: encoded: 1a 07 05 01 24 ab cd 56 78 (rc 
= 9)
 ------- test_cell_id_list_add
-     cell_id_list cgi[0] = {
-     }
-
+     cell_id_list == CGI[0]:{}
 gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 1
-     cell_id_list lac[1] = {
-       0: 123
-     }
-
+     cell_id_list == LAC[1]:{123}
 gsm0808_cell_id_list_add(&cil, &lac1) --> rc = 0
-     cell_id_list lac[1] = {
-       0: 123
-     }
-
+     cell_id_list == LAC[1]:{123}
 gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 2
-     cell_id_list lac[3] = {
-       0: 123
-       1: 456
-       2: 789
-     }
-
+     cell_id_list == LAC[3]:{123, 456, 789}
 gsm0808_cell_id_list_add(&cil, &lac2) --> rc = 0
-     cell_id_list lac[3] = {
-       0: 123
-       1: 456
-       2: 789
-     }
-
+     cell_id_list == LAC[3]:{123, 456, 789}
 gsm0808_cell_id_list_add(&cil, &cil) --> rc = 0
-     cell_id_list lac[3] = {
-       0: 123
-       1: 456
-       2: 789
-     }
-
+     cell_id_list == LAC[3]:{123, 456, 789}
 gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = -22
-     cell_id_list lac[3] = {
-       0: 123
-       1: 456
-       2: 789
-     }
-
-can't add to BSS list
-     cell_id_list bss[0]
-
+     cell_id_list == LAC[3]:{123, 456, 789}
+* can't add to BSS list
+     cell_id_list == BSS[0]
 gsm0808_cell_id_list_add(&cil, &lac1) --> rc = -22
-     cell_id_list bss[0]
-
-other types (including NO_CELL) take on new type iff empty
-     cell_id_list no_cell[0]
-
+     cell_id_list == BSS[0]
+* other types (including NO_CELL) take on new type iff empty
+     cell_id_list == NO-CELL[0]
 gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 1
-     cell_id_list cgi[1] = {
-        0: 001-02-3-4
-     }
-
+     cell_id_list == CGI[1]:{001-02-3-4}
 gsm0808_cell_id_list_add(&cil, &cgi1) --> rc = 0
-     cell_id_list cgi[1] = {
-        0: 001-02-3-4
-     }
-
+     cell_id_list == CGI[1]:{001-02-3-4}
 gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 2
-     cell_id_list cgi[3] = {
-        0: 001-02-3-4
-        1: 001-002-3-4
-        2: 005-006-7-8
-     }
-
+     cell_id_list == CGI[3]:{001-02-3-4, 001-002-3-4, 005-006-7-8}
 gsm0808_cell_id_list_add(&cil, &cgi2) --> rc = 0
-     cell_id_list cgi[3] = {
-        0: 001-02-3-4
-        1: 001-002-3-4
-        2: 005-006-7-8
-     }
-
+     cell_id_list == CGI[3]:{001-02-3-4, 001-002-3-4, 005-006-7-8}
+* test gsm0808_cell_id_list_name_buf()'s return val
+  strlen(gsm0808_cell_id_list_name(cil)) == 45
+  gsm0808_cell_id_list_name_buf(buf, 46, cil)) == 45 "CGI[3]:{001-02-3-4, 
001-002-3-4, 005-006-7-8}"
+  gsm0808_cell_id_list_name_buf(buf, 23, cil)) == 45 "CGI[3]:{001-02-3-4, 00"
+  gsm0808_cell_id_list_name_buf(buf, 11, cil)) == 45 "CGI[3]:{00"
+  gsm0808_cell_id_list_name_buf(buf, 5, cil)) == 45 "CGI["
+  gsm0808_cell_id_list_name_buf(buf, 2, cil)) == 45 "C"
+  gsm0808_cell_id_list_name_buf(buf, 1, cil)) == 45 ""
+  gsm0808_cell_id_list_name_buf(buf, 0, cil)) == 45 "#"
+* list-full behavior
 cil.id_list_len = 126
 gsm0808_cell_id_list_add(&cil, &cgi2a) --> rc = 1
 cil.id_list_len = 127
-
 cil.id_list_len = 126
 gsm0808_cell_id_list_add(&cil, &cgi3) --> rc = -28
 cil.id_list_len = 127

-- 
To view, visit https://gerrit.osmocom.org/7843
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I9b2106805422f96c5cc96ebb9178451355582df3
Gerrit-PatchSet: 1
Gerrit-Project: libosmocore
Gerrit-Branch: master
Gerrit-Owner: Neels Hofmeyr <nhofm...@sysmocom.de>

Reply via email to