aboutsummaryrefslogtreecommitdiffstats
path: root/tests/libosmo-gtlv
diff options
context:
space:
mode:
authorNeels Hofmeyr <nhofmeyr@sysmocom.de>2022-01-12 02:58:02 +0100
committerNeels Hofmeyr <neels@hofmeyr.de>2022-06-16 13:04:33 +0200
commite011b04c6b2e31bdc46320e5d8a4f48715681f15 (patch)
tree4e4f9a4ad06ca68996cbb3dd899081d75979b713 /tests/libosmo-gtlv
parentf842c8c8d0d809b298668c9f547e6df837532fce (diff)
libosmo-gtlv: add C code generator for IE structs and arrays
Defining a protocol of message types with lists of IEs bears a lot of repetitive, copy-paste-error-prone writing out of data structures. Add a third layer to libosmo-gtlv, which allows helpful code generation. By non-repetitive data structures that briefly describe the protocol's messages and IEs, generate possibly repetitive IE list arrays and decoded-struct definitions automatically, avoiding grunt work errors. I tried C macros for this at first, but it became too convoluted. Generating C code that can be read and grepped makes things easier. A usage example is found in tests/libosmo-gtlv/test_gtlv_gen/. Related: SYS#5599 Change-Id: Ifb3ea54d2797ce060b95834aa117725ec2d6c4cf
Diffstat (limited to 'tests/libosmo-gtlv')
-rw-r--r--tests/libosmo-gtlv/Makefile.am5
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/Makefile.am60
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c114
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c261
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok160
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c180
-rw-r--r--tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h70
7 files changed, 850 insertions, 0 deletions
diff --git a/tests/libosmo-gtlv/Makefile.am b/tests/libosmo-gtlv/Makefile.am
index 6d85334..392a73e 100644
--- a/tests/libosmo-gtlv/Makefile.am
+++ b/tests/libosmo-gtlv/Makefile.am
@@ -1,3 +1,7 @@
+SUBDIRS = \
+ test_gtlv_gen \
+ $(NULL)
+
AM_CPPFLAGS = \
$(all_includes) \
-I$(top_srcdir)/include \
@@ -40,3 +44,4 @@ gtlv_dec_enc_test_LDADD = \
update_exp:
$(builddir)/gtlv_test >$(srcdir)/gtlv_test.ok
$(builddir)/gtlv_dec_enc_test >$(srcdir)/gtlv_dec_enc_test.ok
+ $(MAKE) -C test_gtlv_gen update_exp
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/Makefile.am b/tests/libosmo-gtlv/test_gtlv_gen/Makefile.am
new file mode 100644
index 0000000..0d27292
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/Makefile.am
@@ -0,0 +1,60 @@
+AM_CPPFLAGS = \
+ $(all_includes) \
+ -I$(top_srcdir)/include \
+ -I$(bulddir) \
+ $(NULL)
+
+AM_CFLAGS = \
+ -Wall \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(NULL)
+
+noinst_PROGRAMS = \
+ gen__myproto_ies_auto \
+ gtlv_gen_test \
+ $(NULL)
+
+EXTRA_DIST = \
+ myproto_ies_custom.h \
+ gtlv_gen_test.ok \
+ $(NULL)
+
+BUILT_SOURCES = \
+ myproto_ies_auto.h \
+ myproto_ies_auto.c \
+ $(NULL)
+
+CLEANFILES = \
+ myproto_ies_auto.h \
+ myproto_ies_auto.c \
+ $(NULL)
+
+gen__myproto_ies_auto_SOURCES = \
+ gen__myproto_ies_auto.c \
+ myproto_ies_custom.c \
+ $(NULL)
+
+gen__myproto_ies_auto_LDADD = \
+ $(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(NULL)
+
+myproto_ies_auto.h: $(builddir)/gen__myproto_ies_auto
+ $(builddir)/gen__myproto_ies_auto h > $(builddir)/myproto_ies_auto.h
+myproto_ies_auto.c: $(builddir)/gen__myproto_ies_auto
+ $(builddir)/gen__myproto_ies_auto c > $(builddir)/myproto_ies_auto.c
+
+gtlv_gen_test_SOURCES = \
+ gtlv_gen_test.c \
+ myproto_ies_custom.c \
+ myproto_ies_auto.c \
+ $(NULL)
+
+gtlv_gen_test_LDADD = \
+ $(top_builddir)/src/libosmo-gtlv/libosmo-gtlv.a \
+ $(LIBOSMOCORE_LIBS) \
+ $(NULL)
+
+.PHONY: update_exp
+update_exp:
+ $(builddir)/gtlv_gen_test >$(srcdir)/gtlv_gen_test.ok
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c b/tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c
new file mode 100644
index 0000000..75d657d
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c
@@ -0,0 +1,114 @@
+/*
+ * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/linuxlist.h>
+#include <osmocom/core/talloc.h>
+#include <osmocom/gtlv/gtlv_gen.h>
+
+#define O OSMO_GTLV_GEN_O
+#define M OSMO_GTLV_GEN_M
+#define O_MULTI OSMO_GTLV_GEN_O_MULTI
+#define M_MULTI OSMO_GTLV_GEN_M_MULTI
+
+#define ALL_FROM_NAME osmo_gtlv_gen_ie_auto
+
+/* An IE where the type is not a 'struct myproto_ie_${name}'. */
+static const struct osmo_gtlv_gen_ie number = {
+ .decoded_type = "int", /* add 'int foo;' to the struct */
+ .dec_enc = "u16", /* use myproto_dec_u16() and myproto_enc_u16() for the TLV value part */
+ .spec_ref = "an int coded as uint16_t",
+};
+
+static const struct osmo_gtlv_gen_ie_o ies_in_moo_nest[] = {
+ /* Mandatory member xxx.foo of the type defined in 'number' above. */
+ M(number, "foo"),
+ /* Mandatory member xxx.bar of type 'struct myproto_ie_bar', using myproto_ie_dec_bar(), myproto_ie_enc_bar(),
+ * myproto_ie_enc_to_str_bar(), all defined in myproto_ies_custom.h/c. */
+ M(ALL_FROM_NAME, "bar"),
+ M(ALL_FROM_NAME, "baz"),
+ {}
+};
+
+static const struct osmo_gtlv_gen_ie huge_number = {
+ .decoded_type = "uint64_t",
+ .dec_enc = "u64",
+};
+
+static const struct osmo_gtlv_gen_ie moo_nest = {
+ .tag_name = "moo_nest",
+ .nested_ies = ies_in_moo_nest,
+};
+
+static const struct osmo_gtlv_gen_ie_o ies_in_goo_nest[] = {
+ O(huge_number, "val"),
+ M(moo_nest, "nest"),
+ {}
+};
+
+static const struct osmo_gtlv_gen_ie goo_nest = {
+ .tag_name = "goo_nest",
+ .nested_ies = ies_in_goo_nest,
+};
+
+static const struct osmo_gtlv_gen_ie_o ies_in_moo_msg[] = {
+ M(number, "foo"),
+ M(ALL_FROM_NAME, "bar"),
+ O(ALL_FROM_NAME, "baz"),
+ O_MULTI(32, number, "repeat_int"),
+ O_MULTI(32, ALL_FROM_NAME, "repeat_struct"),
+ O(moo_nest, "nest"),
+ {}
+};
+
+static const struct osmo_gtlv_gen_ie_o ies_in_goo_msg[] = {
+ M(number, "foo"),
+ O(ALL_FROM_NAME, "bar"),
+ O_MULTI(8, goo_nest, "nest"),
+ {}
+};
+
+static const struct osmo_gtlv_gen_msg msg_defs[] = {
+ { "moo", ies_in_moo_msg },
+ { "goo", ies_in_goo_msg },
+ {}
+};
+
+int main(int argc, const char **argv)
+{
+ struct osmo_gtlv_gen_cfg cfg = {
+ .proto_name = "myproto",
+ .message_type_enum = "enum myproto_msg_type",
+ .message_type_prefix = "MYPROTO_MSGT_",
+ .tag_enum = "enum myproto_iei",
+ .tag_prefix = "MYPROTO_IEI_",
+ .decoded_type_prefix = "struct myproto_ie_",
+ .h_header = "#include \"myproto_ies_custom.h\"",
+ .c_header = "#include <myproto_ies_auto.h>",
+ .msg_defs = msg_defs,
+ .add_enc_to_str = true,
+ };
+ return osmo_gtlv_gen_main(&cfg, argc, argv);
+}
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c b/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c
new file mode 100644
index 0000000..ef5372c
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c
@@ -0,0 +1,261 @@
+/*
+ * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/msgb.h>
+
+#include <osmocom/gtlv/gtlv.h>
+
+#include <myproto_ies_auto.h>
+
+struct myproto_msg {
+ enum myproto_msg_type type;
+ union myproto_ies ies;
+};
+
+static void err_cb(void *data, void *decoded_struct, const char *file, int line, const char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ //printf("ERR: %s:%d ", file, line);
+ printf("ERR: ");
+ vprintf(fmt, args);
+ va_end(args);
+}
+
+static int myproto_msg_enc(struct msgb *dst, const struct myproto_msg *msg, const struct osmo_gtlv_cfg *cfg)
+{
+ struct osmo_gtlv_put gtlv = {
+ .cfg = cfg,
+ .dst = dst,
+ };
+
+ msgb_put_u8(gtlv.dst, msg->type);
+ return myproto_ies_encode(&gtlv, (void *)&msg->ies, msg->type, err_cb, NULL, myproto_iei_names);
+}
+
+static int myproto_msg_dec(struct myproto_msg *msg, const uint8_t *data, size_t data_len,
+ const struct osmo_gtlv_cfg *cfg, bool ordered)
+{
+ struct osmo_gtlv_load gtlv;
+ if (data_len < 1)
+ return -EINVAL;
+ msg->type = data[0];
+ gtlv = (struct osmo_gtlv_load){
+ .cfg = cfg,
+ .src = { data + 1, data_len - 1 },
+ };
+ return myproto_ies_decode(&msg->ies, &gtlv, ordered, msg->type, err_cb, NULL, myproto_iei_names);
+}
+
+void *ctx;
+
+struct myproto_msg tests[] = {
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .foo = 23,
+ .bar = { "twentythree" },
+ },
+ },
+ },
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .foo = 23,
+ .bar = { "twentythree" },
+
+ .baz_present = true,
+ .baz = {
+ .v_int = 2323,
+ .v_bool = true,
+ },
+ },
+ },
+ },
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .foo = 23,
+ .bar = { "twentythree" },
+
+ .baz_present = true,
+ .baz = {
+ .v_int = 2323,
+ .v_bool = true,
+ },
+
+ .repeat_int_count = 3,
+ .repeat_int = { 1, 2, 0x7fff },
+ },
+ },
+ },
+ {
+ MYPROTO_MSGT_MOO,
+ {
+ .moo = {
+ .foo = 23,
+ .bar = { "twentythree" },
+
+ .baz_present = true,
+ .baz = {
+ .v_int = 2323,
+ .v_bool = true,
+ },
+
+ .repeat_int_count = 3,
+ .repeat_int = { 1, 2, 0x7fff },
+
+ .repeat_struct_count = 2,
+ .repeat_struct = {
+ {
+ .v_int = 1001,
+ .v_bool = true,
+ .v_enum = R_A,
+ },
+ {
+ .v_int = 1002,
+ .v_bool = false,
+ .v_enum = R_B,
+ },
+ },
+
+ .nest_present = true,
+ .nest = {
+ .foo = 42,
+ .bar = { "fortytwo" },
+ .baz = {
+ .v_int = 4242,
+ .v_bool = false,
+ },
+ },
+ },
+ },
+ },
+ {
+ MYPROTO_MSGT_GOO,
+ {
+ .goo = {
+ .foo = 17,
+
+ .bar_present = true,
+ .bar = { "gooei" },
+
+ .nest_count = 2,
+ .nest = {
+ {
+ .val_present = true,
+ .val = 0x0123456789abcdef,
+ .nest = {
+ .foo = 11,
+ .bar = { "eleven" },
+ .baz = {
+ .v_int = 1111,
+ .v_bool = true,
+ },
+ },
+ },
+ {
+ .val_present = false,
+ .nest = {
+ .foo = 12,
+ .bar = { "twelve" },
+ .baz = {
+ .v_int = 1212,
+ .v_bool = false,
+ },
+ },
+ },
+ },
+ },
+ },
+ },
+};
+
+int myproto_msg_to_str_buf(char *buf, size_t buflen, const struct myproto_msg *m)
+{
+ struct osmo_strbuf sb = { .buf = buf, .len = buflen };
+ OSMO_STRBUF_PRINTF(sb, "%s={", get_value_string(myproto_msg_type_names, m->type));
+ OSMO_STRBUF_APPEND(sb, osmo_gtlvs_encode_to_str_buf, &m->ies, 0, myproto_get_msg_coding(m->type),
+ myproto_iei_names);
+ OSMO_STRBUF_PRINTF(sb, " }");
+ return sb.chars_needed;
+
+}
+
+char *myproto_msg_to_str(const struct myproto_msg *m)
+{
+ OSMO_NAME_C_IMPL(ctx, 256, "ERROR", myproto_msg_to_str_buf, m)
+}
+
+void test_enc_dec(const char *label, const struct osmo_gtlv_cfg *cfg, bool ordered)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(tests); i++) {
+ int rc;
+ const struct myproto_msg *orig = &tests[i];
+ struct myproto_msg parsed = {};
+ struct msgb *msg;
+
+ printf("\n=== start %s %s[%d]\n", label, __func__, i);
+ printf("encoded: %s\n", myproto_msg_to_str(orig));
+
+ msg = msgb_alloc(1024, __func__);
+ rc = myproto_msg_enc(msg, orig, cfg);
+ printf("myproto_msg_enc() rc = %d\n", rc);
+ printf("%s.\n", osmo_hexdump(msg->data, msg->len));
+
+ rc = myproto_msg_dec(&parsed, msg->data, msg->len, cfg, ordered);
+ printf("myproto_msg_dec() rc = %d\n", rc);
+ printf("decoded: %s\n", myproto_msg_to_str(&parsed));
+ if (strcmp(myproto_msg_to_str(orig), myproto_msg_to_str(&parsed))) {
+ printf(" ERROR: parsed != orig\n");
+ exit(1);
+ }
+
+ msgb_free(msg);
+ printf("=== end %s %s[%d]\n", label, __func__, i);
+ }
+}
+
+int main()
+{
+ ctx = talloc_named_const(NULL, 0, "test_gen_tlv");
+ msgb_talloc_ctx_init(ctx, 0);
+
+ test_enc_dec("t8l8v ordered", &osmo_t8l8v_cfg, true);
+ test_enc_dec("t8l8v unordered", &osmo_t8l8v_cfg, false);
+
+ test_enc_dec("t16l16v ordered", &osmo_t16l16v_cfg, true);
+ test_enc_dec("t16l16v unordered", &osmo_t16l16v_cfg, false);
+
+ talloc_free(ctx);
+ return 0;
+}
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok b/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok
new file mode 100644
index 0000000..e178831
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok
@@ -0,0 +1,160 @@
+
+=== start t8l8v ordered test_enc_dec[0]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+=== end t8l8v ordered test_enc_dec[0]
+
+=== start t8l8v ordered test_enc_dec[1]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+=== end t8l8v ordered test_enc_dec[1]
+
+=== start t8l8v ordered test_enc_dec[2]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+=== end t8l8v ordered test_enc_dec[2]
+
+=== start t8l8v ordered test_enc_dec[3]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+=== end t8l8v ordered test_enc_dec[3]
+
+=== start t8l8v ordered test_enc_dec[4]
+encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+myproto_msg_enc() rc = 0
+07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc .
+myproto_msg_dec() rc = 0
+decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+=== end t8l8v ordered test_enc_dec[4]
+
+=== start t8l8v unordered test_enc_dec[0]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+=== end t8l8v unordered test_enc_dec[0]
+
+=== start t8l8v unordered test_enc_dec[1]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+=== end t8l8v unordered test_enc_dec[1]
+
+=== start t8l8v unordered test_enc_dec[2]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+=== end t8l8v unordered test_enc_dec[2]
+
+=== start t8l8v unordered test_enc_dec[3]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+myproto_msg_enc() rc = 0
+01 01 02 00 17 02 0b 74 77 65 6e 74 79 74 68 72 65 65 03 02 89 13 04 02 00 01 04 02 00 02 04 02 7f ff 05 03 03 e9 80 05 03 03 ea 01 06 12 01 02 00 2a 02 08 66 6f 72 74 79 74 77 6f 03 02 10 92 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+=== end t8l8v unordered test_enc_dec[3]
+
+=== start t8l8v unordered test_enc_dec[4]
+encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+myproto_msg_enc() rc = 0
+07 01 02 00 11 02 05 67 6f 6f 65 69 08 1c 07 08 01 23 45 67 89 ab cd ef 06 10 01 02 00 0b 02 06 65 6c 65 76 65 6e 03 02 84 57 08 12 06 10 01 02 00 0c 02 06 74 77 65 6c 76 65 03 02 04 bc .
+myproto_msg_dec() rc = 0
+decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+=== end t8l8v unordered test_enc_dec[4]
+
+=== start t16l16v ordered test_enc_dec[0]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+=== end t16l16v ordered test_enc_dec[0]
+
+=== start t16l16v ordered test_enc_dec[1]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+=== end t16l16v ordered test_enc_dec[1]
+
+=== start t16l16v ordered test_enc_dec[2]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+=== end t16l16v ordered test_enc_dec[2]
+
+=== start t16l16v ordered test_enc_dec[3]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+=== end t16l16v ordered test_enc_dec[3]
+
+=== start t16l16v ordered test_enc_dec[4]
+encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+myproto_msg_enc() rc = 0
+07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc .
+myproto_msg_dec() rc = 0
+decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+=== end t16l16v ordered test_enc_dec[4]
+
+=== start t16l16v unordered test_enc_dec[0]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" }
+=== end t16l16v unordered test_enc_dec[0]
+
+=== start t16l16v unordered test_enc_dec[1]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} }
+=== end t16l16v unordered test_enc_dec[1]
+
+=== start t16l16v unordered test_enc_dec[2]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } }
+=== end t16l16v unordered test_enc_dec[2]
+
+=== start t16l16v unordered test_enc_dec[3]
+encoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+myproto_msg_enc() rc = 0
+01 00 01 00 02 00 17 00 02 00 0b 74 77 65 6e 74 79 74 68 72 65 65 00 03 00 02 89 13 00 04 00 02 00 01 00 04 00 02 00 02 00 04 00 02 7f ff 00 05 00 03 03 e9 80 00 05 00 03 03 ea 01 00 06 00 18 00 01 00 02 00 2a 00 02 00 08 66 6f 72 74 79 74 77 6f 00 03 00 02 10 92 .
+myproto_msg_dec() rc = 0
+decoded: MOO={ 'FOO'=23 'BAR'="twentythree" 'BAZ'={2323,true} 'REPEAT_INT'={ 1, 2, 32767 } 'REPEAT_STRUCT'={ {1001,true,R_A}, {1002,false,R_B} } 'MOO_NEST'={ 'FOO'=42 'BAR'="fortytwo" 'BAZ'={4242,false} } }
+=== end t16l16v unordered test_enc_dec[3]
+
+=== start t16l16v unordered test_enc_dec[4]
+encoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+myproto_msg_enc() rc = 0
+07 00 01 00 02 00 11 00 02 00 05 67 6f 6f 65 69 00 08 00 26 00 07 00 08 01 23 45 67 89 ab cd ef 00 06 00 16 00 01 00 02 00 0b 00 02 00 06 65 6c 65 76 65 6e 00 03 00 02 84 57 00 08 00 1a 00 06 00 16 00 01 00 02 00 0c 00 02 00 06 74 77 65 6c 76 65 00 03 00 02 04 bc .
+myproto_msg_dec() rc = 0
+decoded: GOO={ 'FOO'=17 'BAR'="gooei" 'GOO_NEST'={ { 'VAL'=0x123456789abcdef 'MOO_NEST'={ 'FOO'=11 'BAR'="eleven" 'BAZ'={1111,true} } }, { 'MOO_NEST'={ 'FOO'=12 'BAR'="twelve" 'BAZ'={1212,false} } } } }
+=== end t16l16v unordered test_enc_dec[4]
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c b/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c
new file mode 100644
index 0000000..8548165
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c
@@ -0,0 +1,180 @@
+/* Example for defining custom IES for gtlv_gen. */
+/*
+ * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <errno.h>
+#include <inttypes.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/msgb.h>
+#include <osmocom/gtlv/gtlv.h>
+
+#include <myproto_ies_custom.h>
+
+int myproto_dec_u16(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ int *foo = decode_to;
+ if (gtlv->len != 2)
+ return -EINVAL;
+ *foo = osmo_load16be(gtlv->val);
+ return 0;
+}
+
+int myproto_enc_u16(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ int *foo = encode_from;
+ if (*foo > INT16_MAX)
+ return -EINVAL;
+ msgb_put_u16(gtlv->dst, *foo);
+ return 0;
+}
+
+int myproto_enc_to_str_u16(char *buf, size_t buflen, void *encode_from)
+{
+ int *foo = encode_from;
+ return snprintf(buf, buflen, "%d", *foo);
+}
+
+int myproto_dec_u64(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ uint64_t *val = decode_to;
+ if (gtlv->len != sizeof(uint64_t))
+ return -EINVAL;
+ *val = osmo_load64be(gtlv->val);
+ return 0;
+}
+
+int myproto_enc_u64(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ uint64_t *val = encode_from;
+ osmo_store64be(*val, msgb_put(gtlv->dst, sizeof(*val)));
+ return 0;
+}
+
+int myproto_enc_to_str_u64(char *buf, size_t buflen, void *encode_from)
+{
+ uint64_t *val = encode_from;
+ return snprintf(buf, buflen, "0x%"PRIx64, *val);
+}
+
+int myproto_dec_bar(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ struct myproto_ie_bar *bar = decode_to;
+ if (gtlv->len > sizeof(bar->str) - 1)
+ return -EINVAL;
+ osmo_strlcpy(bar->str, (const char *)gtlv->val, OSMO_MIN(gtlv->len + 1, sizeof(bar->str)));
+ return 0;
+}
+
+int myproto_enc_bar(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ struct myproto_ie_bar *bar = encode_from;
+ int len = strnlen(bar->str, sizeof(bar->str));
+ memcpy(msgb_put(gtlv->dst, len), bar, len);
+ return 0;
+}
+
+int myproto_enc_to_str_bar(char *buf, size_t buflen, void *encode_from)
+{
+ struct myproto_ie_bar *bar = encode_from;
+ return osmo_quote_str_buf3(buf, buflen, bar->str, -1);
+}
+
+int myproto_dec_baz(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ struct myproto_ie_baz *baz = decode_to;
+ uint16_t l;
+ if (gtlv->len != 2)
+ return -EINVAL;
+ l = osmo_load16be(gtlv->val);
+ baz->v_int = l & 0x7fff;
+ baz->v_bool = (l & 0x8000) ? true : false;
+ return 0;
+}
+
+int myproto_enc_baz(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ struct myproto_ie_baz *baz = encode_from;
+ if (baz->v_int > 0x7fff)
+ return -EINVAL;
+ msgb_put_u16(gtlv->dst, (baz->v_bool ? 0x8000 : 0) + (baz->v_int & 0x7fff));
+ return 0;
+}
+
+int myproto_enc_to_str_baz(char *buf, size_t buflen, void *encode_from)
+{
+ struct myproto_ie_baz *baz = encode_from;
+ return snprintf(buf, buflen, "{%d,%s}", baz->v_int, baz->v_bool ? "true" : "false");
+}
+
+int myproto_dec_repeat_struct(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv)
+{
+ struct myproto_ie_repeat_struct *repeat_struct = decode_to;
+ if (gtlv->len != 3)
+ return -EINVAL;
+ repeat_struct->v_int = osmo_load16be(gtlv->val);
+ repeat_struct->v_bool = gtlv->val[2] & 0x80;
+ repeat_struct->v_enum = gtlv->val[2] & 0x7f;
+ return 0;
+}
+
+int myproto_enc_repeat_struct(struct osmo_gtlv_put *gtlv, void *decoded_struct, void *encode_from)
+{
+ struct myproto_ie_repeat_struct *repeat_struct = encode_from;
+ msgb_put_u16(gtlv->dst, repeat_struct->v_int);
+ msgb_put_u8(gtlv->dst, (repeat_struct->v_bool ? 0x80 : 0) + (repeat_struct->v_enum & 0x7f));
+ return 0;
+}
+
+int myproto_enc_to_str_repeat_struct(char *buf, size_t buflen, void *encode_from)
+{
+ struct myproto_ie_repeat_struct *repeat_struct = encode_from;
+ return snprintf(buf, buflen, "{%d,%s,%s}",
+ repeat_struct->v_int, repeat_struct->v_bool ? "true" : "false",
+ get_value_string(myproto_repeat_enum_names, repeat_struct->v_enum));
+}
+
+const struct value_string myproto_msg_type_names[] = {
+ { MYPROTO_MSGT_MOO, "MOO" },
+ { MYPROTO_MSGT_GOO, "GOO" },
+ {}
+};
+
+const struct value_string myproto_iei_names[] = {
+ { MYPROTO_IEI_FOO, "FOO" },
+ { MYPROTO_IEI_BAR, "BAR" },
+ { MYPROTO_IEI_BAZ, "BAZ" },
+ { MYPROTO_IEI_REPEAT_INT, "REPEAT_INT" },
+ { MYPROTO_IEI_REPEAT_STRUCT, "REPEAT_STRUCT" },
+ { MYPROTO_IEI_MOO_NEST, "MOO_NEST" },
+ { MYPROTO_IEI_VAL, "VAL" },
+ { MYPROTO_IEI_GOO_NEST, "GOO_NEST" },
+ {}
+};
+
+const struct value_string myproto_repeat_enum_names[] = {
+ OSMO_VALUE_STRING(R_A),
+ OSMO_VALUE_STRING(R_B),
+ OSMO_VALUE_STRING(R_C),
+ {}
+};
diff --git a/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h b/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h
new file mode 100644
index 0000000..56039af
--- /dev/null
+++ b/tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h
@@ -0,0 +1,70 @@
+/* Definitions for decoded message IEs, to be used by the auto-generated myproto_ies_auto.c. */
+/*
+ * (C) 2021-2022 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved.
+ *
+ * Author: Neels Janosch Hofmeyr <nhofmeyr@sysmocom.de>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#pragma once
+
+#include <osmocom/core/utils.h>
+
+enum myproto_msg_type {
+ MYPROTO_MSGT_MOO = 1,
+ MYPROTO_MSGT_GOO = 7,
+};
+
+extern const struct value_string myproto_msg_type_names[];
+
+enum myproto_iei {
+ MYPROTO_IEI_FOO = 1,
+ MYPROTO_IEI_BAR,
+ MYPROTO_IEI_BAZ,
+ MYPROTO_IEI_REPEAT_INT,
+ MYPROTO_IEI_REPEAT_STRUCT,
+ MYPROTO_IEI_MOO_NEST,
+ MYPROTO_IEI_VAL,
+ MYPROTO_IEI_GOO_NEST,
+};
+
+extern const struct value_string myproto_iei_names[];
+
+struct myproto_ie_bar {
+ char str[23];
+};
+
+struct myproto_ie_baz {
+ int v_int;
+ bool v_bool;
+};
+
+enum myproto_repeat_enum {
+ R_A,
+ R_B,
+ R_C,
+};
+
+extern const struct value_string myproto_repeat_enum_names[];
+
+struct myproto_ie_repeat_struct {
+ int v_int;
+ bool v_bool;
+ enum myproto_repeat_enum v_enum;
+};