mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-upf.git
				synced 2025-11-04 05:53:29 +00:00 
			
		
		
		
	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
This commit is contained in:
		@@ -205,6 +205,7 @@ AC_OUTPUT(
 | 
			
		||||
    tests/Makefile
 | 
			
		||||
    tests/atlocal
 | 
			
		||||
    tests/libosmo-gtlv/Makefile
 | 
			
		||||
    tests/libosmo-gtlv/test_gtlv_gen/Makefile
 | 
			
		||||
    doc/Makefile
 | 
			
		||||
    doc/examples/Makefile
 | 
			
		||||
    doc/manuals/Makefile
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
tlv_HEADERS = \
 | 
			
		||||
	gtlv.h \
 | 
			
		||||
	gtlv_dec_enc.h \
 | 
			
		||||
	gtlv_gen.h \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
tlvdir = $(includedir)/osmocom/gtlv
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										166
									
								
								include/osmocom/gtlv/gtlv_gen.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										166
									
								
								include/osmocom/gtlv/gtlv_gen.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,166 @@
 | 
			
		||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
 | 
			
		||||
 * For a usage example see tests/libosmo-gtlv/test_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/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <stdbool.h>
 | 
			
		||||
 | 
			
		||||
struct osmo_gtlv_gen_ie;
 | 
			
		||||
 | 
			
		||||
/* O means optional, M means mandatory.
 | 
			
		||||
 * If all of the IE struct, tag name and functions can be derived from the name, just pass osmo_gtlv_gen_ie_auto as
 | 
			
		||||
 * TLV_GEN_IE. */
 | 
			
		||||
#define OSMO_GTLV_GEN_O(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .optional = true, .ie = &(TLV_GEN_IE) }
 | 
			
		||||
#define OSMO_GTLV_GEN_M(TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .ie = &(TLV_GEN_IE) }
 | 
			
		||||
#define OSMO_GTLV_GEN_O_MULTI(MAX, TLV_GEN_IE, MEMB_NAME) { MEMB_NAME, .multi = MAX, .ie = &(TLV_GEN_IE) }
 | 
			
		||||
#define OSMO_GTLV_GEN_M_MULTI(MAX, MAND_COUNT, TLV_GEN_IE, MEMB_NAME) \
 | 
			
		||||
	{ MEMB_NAME, .multi = MAX, .multi_mandatory = MAND_COUNT, .ie = &(TLV_GEN_IE) }
 | 
			
		||||
 | 
			
		||||
/*! osmo_gtlv_gen_ie with all members == NULL, so that all are derived from the member name. */
 | 
			
		||||
extern const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto;
 | 
			
		||||
 | 
			
		||||
/*! Modifier for Mandatory/Optional/Multiple around an osmo_gtlv_gen_ie. */
 | 
			
		||||
struct osmo_gtlv_gen_ie_o {
 | 
			
		||||
	/*! The C name of the member in a decoded struct, to be of the type defined by .ie.
 | 
			
		||||
	 * All parts of .ie, if NULL, are derived from this name.
 | 
			
		||||
	 *
 | 
			
		||||
	 * For example, simply this
 | 
			
		||||
	 *
 | 
			
		||||
	 *   struct osmo_gtlv_gen_ie_o foo[] = {
 | 
			
		||||
	 *       OSMO_GTLV_GEN_O("bar", NULL),
 | 
			
		||||
	 *   };
 | 
			
		||||
	 *
 | 
			
		||||
	 * Generates
 | 
			
		||||
	 *
 | 
			
		||||
	 *   struct myproto_msg_foo {
 | 
			
		||||
	 *       struct myproto_ie_bar bar;
 | 
			
		||||
	 *   }
 | 
			
		||||
	 *
 | 
			
		||||
	 * and an osmo_gtlv_coding entry of
 | 
			
		||||
	 *
 | 
			
		||||
	 * { MYPROTO_IEI_BAR,
 | 
			
		||||
	 *   .memb_ofs = offsetof(struct myproto_msg_foo, bar),
 | 
			
		||||
	 *   .dec_func = myproto_dec_bar,
 | 
			
		||||
	 *   .enc_func = myproto_enc_bar,
 | 
			
		||||
	 *   .enc_to_str_func = myproto_enc_to_str_bar,
 | 
			
		||||
	 * }
 | 
			
		||||
	 *
 | 
			
		||||
	 * See also osmo_gtlv_gen_cfg.add_enc_to_str.
 | 
			
		||||
	 */
 | 
			
		||||
	const char *name;
 | 
			
		||||
 | 
			
		||||
	/*! Whether to add a bool foo_present, and to skip encoding/decoding if false.
 | 
			
		||||
	 * Only useful for non-multi IEs (compare OSMO_GTLV_GEN_O_MULTI() vs OSMO_GTLV_GEN_M_MULTI()). */
 | 
			
		||||
	bool optional;
 | 
			
		||||
 | 
			
		||||
	/*! If non-NULL, the member is an array: foo[123] with an unsigned int foo_count.
 | 
			
		||||
	 * Set to the maximum number of array elements; for foo[123] set .multi = 123. */
 | 
			
		||||
	unsigned int multi;
 | 
			
		||||
	/*! Number of mandatory occurences of the IE, only has an effect if .multi > 0. */
 | 
			
		||||
	unsigned int multi_mandatory;
 | 
			
		||||
 | 
			
		||||
	/*! IE decoding / encoding instructions. If NULL, the entire IE definition is derived from .name.
 | 
			
		||||
	 * 'MYPROTO_IEI_NAME', 'myproto_dec_name()', 'myproto_enc_name()', 'myproto_enc_to_str_name()'.
 | 
			
		||||
	 * Your myproto_ies_custom.h needs to define an enum value MYPROTO_IEI_NAME and*/
 | 
			
		||||
	const struct osmo_gtlv_gen_ie *ie;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! Define decoding and encoding of a single IE, i.e. one full TLV. */
 | 
			
		||||
struct osmo_gtlv_gen_ie {
 | 
			
		||||
	/*! like "uint32_t" or "struct foo".
 | 
			
		||||
	 * If NULL, use "struct myproto_ie_<name>" instead, where <name> comes from the osmo_gtlv_gen_ie_o.
 | 
			
		||||
	 * When there are nested IEs, the struct definition is auto-generated, deriving the struct members from the
 | 
			
		||||
	 * nested_ies list.
 | 
			
		||||
	 * When there are no nested IEs, the type needs to be defined manually by a myproto_ies_custom.h. */
 | 
			
		||||
	const char *decoded_type;
 | 
			
		||||
 | 
			
		||||
	/*! C name of this tag value, e.g. "MYPROTO_IEI_FOO". If NULL, take "MYPROTO_IEI_"+upper(name) instead. */
 | 
			
		||||
	const char *tag_name;
 | 
			
		||||
 | 
			
		||||
	/*! Name suffix of the dec/enc functions. "foo" -> myproto_dec_foo(), myproto_enc_foo(),
 | 
			
		||||
	 * myproto_enc_to_str_foo().
 | 
			
		||||
	 * These functions need to be implemented manually in a myproto_ies_custom.c.
 | 
			
		||||
	 * When osmo_gtlv_gen_cfg.add_enc_to_str is false, the myproto_enc_to_str_foo() is not required. */
 | 
			
		||||
	const char *dec_enc;
 | 
			
		||||
 | 
			
		||||
	/*! List of inner IEs terminated by {}. If non-NULL, this is a "Grouped IE" with an inner TLV structure inside
 | 
			
		||||
	 * this IE's V part. */
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *nested_ies;
 | 
			
		||||
 | 
			
		||||
	/*! To place a spec comment in the generated code. */
 | 
			
		||||
	const char *spec_ref;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! General TLV decoding and encoding definitions applying to all IEs (and nested IEs). */
 | 
			
		||||
struct osmo_gtlv_gen_cfg {
 | 
			
		||||
	/*! Name of the protocol for use in C type or function names, like "myproto". */
 | 
			
		||||
	const char *proto_name;
 | 
			
		||||
 | 
			
		||||
	/*! When placing comments to spec references, prefix with this. For example, "3GPP TS 12.345 ". */
 | 
			
		||||
	const char *spec_ref_prefix;
 | 
			
		||||
 | 
			
		||||
	/*! The type to pass a message discriminator as, like 'enum myproto_message_types' */
 | 
			
		||||
	const char *message_type_enum;
 | 
			
		||||
	/*! To reference a message type discriminator like MYPROTO_MSGT_FOO, this would be "MYPROTO_MSGT_". */
 | 
			
		||||
	const char *message_type_prefix;
 | 
			
		||||
 | 
			
		||||
	/*! Type to use to represent tag IEI in decoded form.
 | 
			
		||||
	 * For example "enum foo_msg_iei". */
 | 
			
		||||
	const char *tag_enum;
 | 
			
		||||
	/*! The tag IEI enum value is uppercase(tag_prefix + (iedef->tag_name or iedef->name)).
 | 
			
		||||
	 * For example, with tag_prefix = "OSMO_FOO_IEI_", we would generate code like
 | 
			
		||||
	 * enum osmo_foo_iei tag = OSMO_FOO_IEI_BAR; */
 | 
			
		||||
	const char *tag_prefix;
 | 
			
		||||
 | 
			
		||||
	/*! When an osmo_gtlv_gen_ie provides no decoded_type string, it is derived from .name and this prefix is
 | 
			
		||||
	 * added. For example, with decoded_type_prefix = "struct foo_ie_", the decoded_type defaults to
 | 
			
		||||
	 * struct foo_ie_bar for an IE definition with name = "bar". */
 | 
			
		||||
	const char *decoded_type_prefix;
 | 
			
		||||
 | 
			
		||||
	/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_tlv_devs.h". This is put at
 | 
			
		||||
	 * the head of the generated .h file. */
 | 
			
		||||
	const char *h_header;
 | 
			
		||||
 | 
			
		||||
	/*! To include user defined headers, set to something like "#include <osmocom/foo/foo_msg.h". This is put at
 | 
			
		||||
	 * the head of the generated .c file. */
 | 
			
		||||
	const char *c_header;
 | 
			
		||||
 | 
			
		||||
	/*! Array of message IE definitions, indexed by message type. */
 | 
			
		||||
	const struct osmo_gtlv_gen_msg *msg_defs;
 | 
			
		||||
 | 
			
		||||
	/*! Whether to add to_str functions. When true, every automatically derived IE (that has no nested IEs) needs to
 | 
			
		||||
	 * have a myproto_enc_to_str_foo() defined by a myproto_ies_custom.c. When false, osmo_gtlvs_encode_to_str_buf()
 | 
			
		||||
	 * will print '?' instead of the IE contents. */
 | 
			
		||||
	bool add_enc_to_str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/*! For generating the outer union that composes a protocol's PDU variants, an entry of the list of message names and
 | 
			
		||||
 * IEs in each message. */
 | 
			
		||||
struct osmo_gtlv_gen_msg {
 | 
			
		||||
	const char *name;
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ies;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv);
 | 
			
		||||
@@ -23,4 +23,5 @@ noinst_LIBRARIES = \
 | 
			
		||||
libosmo_gtlv_a_SOURCES = \
 | 
			
		||||
	gtlv.c \
 | 
			
		||||
	gtlv_dec_enc.c \
 | 
			
		||||
	gtlv_gen.c \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										417
									
								
								src/libosmo-gtlv/gtlv_gen.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										417
									
								
								src/libosmo-gtlv/gtlv_gen.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,417 @@
 | 
			
		||||
/* Write h and c source files for TLV protocol definitions, based on very sparse TLV definitions.
 | 
			
		||||
 * For a usage example see tests/libosmo-gtlv/test_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 <string.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/linuxlist.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/gtlv/gtlv_gen.h>
 | 
			
		||||
 | 
			
		||||
static const struct osmo_gtlv_gen_cfg *g_cfg = NULL;
 | 
			
		||||
 | 
			
		||||
const struct osmo_gtlv_gen_ie osmo_gtlv_gen_ie_auto = {};
 | 
			
		||||
 | 
			
		||||
/* Helps avoid redundant definitions of the same type. */
 | 
			
		||||
struct seen_entry {
 | 
			
		||||
	struct llist_head entry;
 | 
			
		||||
	char str[256];
 | 
			
		||||
	const void *from_def;
 | 
			
		||||
};
 | 
			
		||||
static LLIST_HEAD(seen_list);
 | 
			
		||||
 | 
			
		||||
static bool seen(const char *str, const void *from_def)
 | 
			
		||||
{
 | 
			
		||||
	struct seen_entry *s;
 | 
			
		||||
	llist_for_each_entry(s, &seen_list, entry) {
 | 
			
		||||
		if (!strcmp(s->str, str)) {
 | 
			
		||||
			if (from_def != s->from_def) {
 | 
			
		||||
				fprintf(stderr, "ERROR: %s: multiple definitions use the same name: '%s'\n",
 | 
			
		||||
					g_cfg->proto_name, str);
 | 
			
		||||
				exit(1);
 | 
			
		||||
			}
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	s = talloc_zero(NULL, struct seen_entry);
 | 
			
		||||
	OSMO_STRLCPY_ARRAY(s->str, str);
 | 
			
		||||
	s->from_def = from_def;
 | 
			
		||||
	llist_add(&s->entry, &seen_list);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
static void clear_seen()
 | 
			
		||||
{
 | 
			
		||||
	struct seen_entry *s;
 | 
			
		||||
	while ((s = llist_first_entry_or_null(&seen_list, struct seen_entry, entry))) {
 | 
			
		||||
		llist_del(&s->entry);
 | 
			
		||||
		talloc_free(s);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Return "struct foo_ie_bar" from g_cfg->decoded_type_prefix and ie. */
 | 
			
		||||
static inline const char *decoded_type(const struct osmo_gtlv_gen_ie_o *ie_o)
 | 
			
		||||
{
 | 
			
		||||
	static char b[255];
 | 
			
		||||
	const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
 | 
			
		||||
	const char *tag_name;
 | 
			
		||||
	if (ie && ie->decoded_type)
 | 
			
		||||
		return ie->decoded_type;
 | 
			
		||||
	/* "struct foo_ie_" + "bar" = struct foo_ie_bar*/
 | 
			
		||||
	tag_name = ie ? ie->tag_name : NULL;
 | 
			
		||||
	snprintf(b, sizeof(b), "%s%s", g_cfg->decoded_type_prefix, tag_name ? : ie_o->name);
 | 
			
		||||
	return b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* --- .h file --- */
 | 
			
		||||
 | 
			
		||||
/* Write a listing of struct members like
 | 
			
		||||
 *     bool foo_present;
 | 
			
		||||
 *     int foo;
 | 
			
		||||
 *     struct myproto_ie_bar bar;
 | 
			
		||||
 *     struct abc abc[10];
 | 
			
		||||
 *     int abc_count;
 | 
			
		||||
 */
 | 
			
		||||
static void write_ie_members(const struct osmo_gtlv_gen_ie_o ies[])
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ie_o;
 | 
			
		||||
	for (ie_o = ies; ie_o->ie; ie_o++) {
 | 
			
		||||
		if (ie_o->optional)
 | 
			
		||||
			printf("\tbool %s_present;\n", ie_o->name);
 | 
			
		||||
		printf("\t%s %s", decoded_type(ie_o), ie_o->name);
 | 
			
		||||
		if (ie_o->multi) {
 | 
			
		||||
			printf("[%u];\n", ie_o->multi);
 | 
			
		||||
			printf("\tunsigned int %s_count", ie_o->name);
 | 
			
		||||
		}
 | 
			
		||||
		printf(";\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Traverse nesting levels in the message definitions and generate the structs for all as needed. */
 | 
			
		||||
static void write_ie_auto_structs(const struct osmo_gtlv_gen_ie_o ies[])
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ie_o;
 | 
			
		||||
	if (!ies)
 | 
			
		||||
		return;
 | 
			
		||||
	for (ie_o = ies; ie_o->ie; ie_o++) {
 | 
			
		||||
		const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
 | 
			
		||||
		if (!ie || !ie->nested_ies)
 | 
			
		||||
			continue;
 | 
			
		||||
		/* Recurse to write inner layers first, so that they can be referenced in outer layers. */
 | 
			
		||||
		write_ie_auto_structs(ie->nested_ies);
 | 
			
		||||
 | 
			
		||||
		/* Various IE definitions can use the same underlying type. Only generate each type once. */
 | 
			
		||||
		if (seen(decoded_type(ie_o), NULL))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		/* Print:
 | 
			
		||||
		 *
 | 
			
		||||
		 * \* spec ref *\
 | 
			
		||||
		 * struct myproto_ie_goo {
 | 
			
		||||
		 *     bool foo_present;
 | 
			
		||||
		 *     int foo;
 | 
			
		||||
		 *     struct myproto_ie_bar bar;
 | 
			
		||||
		 *     struct abc abc[10];
 | 
			
		||||
		 *     int abc_count;
 | 
			
		||||
		 * };
 | 
			
		||||
		 */
 | 
			
		||||
		printf("\n");
 | 
			
		||||
		if (ie->spec_ref)
 | 
			
		||||
			printf("/* %s%s */\n", g_cfg->spec_ref_prefix, ie->spec_ref);
 | 
			
		||||
		printf("%s {\n", decoded_type(ie_o));
 | 
			
		||||
		write_ie_members(ie->nested_ies);
 | 
			
		||||
		printf("};\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write all auto-generated structs, starting with the outer message definitions and nesting into all contained IE
 | 
			
		||||
 * definitions. */
 | 
			
		||||
static void write_auto_structs()
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_msg *gen_msg;
 | 
			
		||||
	clear_seen();
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		write_ie_auto_structs(gen_msg->ies);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write the struct definitions for each message, i.e. for each entry in the outer PDU's message union, as well as the
 | 
			
		||||
 * union itself.
 | 
			
		||||
 *
 | 
			
		||||
 * struct myproto_msg_foo {
 | 
			
		||||
 *    ...
 | 
			
		||||
 * }:
 | 
			
		||||
 * struct myproto_msg_goo {
 | 
			
		||||
 *    ...
 | 
			
		||||
 * };
 | 
			
		||||
 * union myproto_ies {
 | 
			
		||||
 *        myproto_msg_foo foo;
 | 
			
		||||
 *        myproto_msg_goo goo;
 | 
			
		||||
 * };
 | 
			
		||||
 */
 | 
			
		||||
static void write_msg_union()
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_msg *gen_msg;
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		/* "struct foo_msg" + "_%s" { *
 | 
			
		||||
		 * struct foo_msg_goo_request { ... }; */
 | 
			
		||||
		printf("\nstruct %s_msg_%s {\n",
 | 
			
		||||
		       g_cfg->proto_name,
 | 
			
		||||
		       gen_msg->name);
 | 
			
		||||
		write_ie_members(gen_msg->ies);
 | 
			
		||||
		printf("};\n");
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	printf("\nunion %s_ies {\n", g_cfg->proto_name);
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		printf("\tstruct %s_msg_%s %s;\n", g_cfg->proto_name,
 | 
			
		||||
		       gen_msg->name, gen_msg->name);
 | 
			
		||||
	}
 | 
			
		||||
	printf("};\n");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write the C header, myproto_ies_auto.h */
 | 
			
		||||
static void write_h()
 | 
			
		||||
{
 | 
			
		||||
	printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
 | 
			
		||||
	printf("#include <stdint.h>\n");
 | 
			
		||||
	printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
 | 
			
		||||
	if (g_cfg->h_header)
 | 
			
		||||
		printf("\n%s\n", g_cfg->h_header);
 | 
			
		||||
	write_auto_structs();
 | 
			
		||||
	write_msg_union();
 | 
			
		||||
	printf("\nconst struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type);\n",
 | 
			
		||||
	       g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
 | 
			
		||||
		"	%s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
 | 
			
		||||
		"	%s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs);\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
 | 
			
		||||
		"	%s message_type, const struct value_string *iei_strs);\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* --- .c file --- */
 | 
			
		||||
 | 
			
		||||
/* Write a listing of:
 | 
			
		||||
 * extern int myproto_dec_foo(...);
 | 
			
		||||
 * extern int myproto_enc_foo(...);
 | 
			
		||||
 */
 | 
			
		||||
static void write_extern_dec_enc(const struct osmo_gtlv_gen_ie_o *ies)
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ie_o;
 | 
			
		||||
	for (ie_o = ies; ie_o->ie; ie_o++) {
 | 
			
		||||
		const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
 | 
			
		||||
		const char *dec_enc = ie_o->name;
 | 
			
		||||
		if (ie)
 | 
			
		||||
			dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
 | 
			
		||||
		if (ie && ie->nested_ies) {
 | 
			
		||||
			write_extern_dec_enc(ie->nested_ies);
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
		if (seen(dec_enc, NULL))
 | 
			
		||||
			continue;
 | 
			
		||||
		printf("extern int %s_dec_%s(void *decoded_struct, void *decode_to, const struct osmo_gtlv_load *gtlv);\n",
 | 
			
		||||
		       g_cfg->proto_name, dec_enc);
 | 
			
		||||
		printf("extern int %s_enc_%s(struct osmo_gtlv_put *gtlv, const void *decoded_struct, const void *encode_from);\n",
 | 
			
		||||
		       g_cfg->proto_name, dec_enc);
 | 
			
		||||
		if (g_cfg->add_enc_to_str)
 | 
			
		||||
			printf("extern int %s_enc_to_str_%s(char *buf, size_t buflen, const void *encode_from);\n",
 | 
			
		||||
			       g_cfg->proto_name, dec_enc);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
 | 
			
		||||
 * { MYPROTO_IEI_BAR,
 | 
			
		||||
 *   .memb_ofs = offsetof(struct myproto_foo, bar),
 | 
			
		||||
 *   .dec_func = myproto_dec_bar,
 | 
			
		||||
 *   .enc_func = myproto_enc_bar,
 | 
			
		||||
 * },
 | 
			
		||||
 */
 | 
			
		||||
static void write_ies_array(const char *indent, const struct osmo_gtlv_gen_ie_o *ies, const char *obj_type, const char *substruct)
 | 
			
		||||
{
 | 
			
		||||
#define printi(FMT, ARGS...) printf("%s" FMT, indent, ##ARGS)
 | 
			
		||||
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ie_o;
 | 
			
		||||
	for (ie_o = ies; ie_o->ie; ie_o++) {
 | 
			
		||||
		const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
 | 
			
		||||
		const char *tag_name = (ie && ie->tag_name) ? ie->tag_name : ie_o->name;
 | 
			
		||||
		printi("{ %s%s,\n", g_cfg->tag_prefix, osmo_str_toupper(tag_name));
 | 
			
		||||
		printi("  .memb_ofs = offsetof(%s, %s%s),\n", obj_type, substruct, ie_o->name);
 | 
			
		||||
		if (ie && ie->nested_ies) {
 | 
			
		||||
			printi("  .nested_ies = ies_in_%s,\n", ie->tag_name ? : ie_o->name);
 | 
			
		||||
		} else {
 | 
			
		||||
			const char *dec_enc = ie->dec_enc ? : (ie->tag_name ? : ie_o->name);
 | 
			
		||||
			printi("  .dec_func = %s_dec_%s,\n", g_cfg->proto_name, dec_enc);
 | 
			
		||||
			printi("  .enc_func = %s_enc_%s,\n", g_cfg->proto_name, dec_enc);
 | 
			
		||||
			if (g_cfg->add_enc_to_str)
 | 
			
		||||
				printi("  .enc_to_str_func = %s_enc_to_str_%s,\n", g_cfg->proto_name, dec_enc);
 | 
			
		||||
		}
 | 
			
		||||
		if (ie_o->multi) {
 | 
			
		||||
			printi("  .memb_array_pitch = OSMO_MEMB_ARRAY_PITCH(%s, %s%s),\n",
 | 
			
		||||
			       obj_type, substruct, ie_o->name);
 | 
			
		||||
			printi("  .has_count = true, .count_max = %u,\n", ie_o->multi);
 | 
			
		||||
			printi("  .count_mandatory = %u,\n", ie_o->multi_mandatory);
 | 
			
		||||
			printi("  .count_ofs = offsetof(%s, %s%s_count),\n", obj_type, substruct, ie_o->name);
 | 
			
		||||
		}
 | 
			
		||||
		if (ie_o->optional) {
 | 
			
		||||
			printi("  .has_presence_flag = true,\n");
 | 
			
		||||
			printi("  .presence_flag_ofs = offsetof(%s, %s%s_present),\n", obj_type, substruct, ie_o->name);
 | 
			
		||||
		}
 | 
			
		||||
		printi("},\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* For a nested IE, write the struct osmo_gtlv_coding array of the inner IEs.
 | 
			
		||||
 * static const struct osmo_gtlv_coding ies_in_foo[] = {
 | 
			
		||||
 *         { MYPROTO_IEI_BAR,
 | 
			
		||||
 *           .memb_ofs = offsetof(struct myproto_foo, bar),
 | 
			
		||||
 *           .dec_func = myproto_dec_bar,
 | 
			
		||||
 *           .enc_func = myproto_enc_bar,
 | 
			
		||||
 *         },
 | 
			
		||||
 *         ...
 | 
			
		||||
 * };
 | 
			
		||||
 */
 | 
			
		||||
static void write_nested_ies_array(const struct osmo_gtlv_gen_ie_o *ies)
 | 
			
		||||
{
 | 
			
		||||
	const char *indent = "\t";
 | 
			
		||||
	const struct osmo_gtlv_gen_ie_o *ie_o;
 | 
			
		||||
	for (ie_o = ies; ie_o->ie; ie_o++) {
 | 
			
		||||
		const struct osmo_gtlv_gen_ie *ie = ie_o->ie;
 | 
			
		||||
		if (!ie || !ie->nested_ies)
 | 
			
		||||
			continue;
 | 
			
		||||
		write_nested_ies_array(ie->nested_ies);
 | 
			
		||||
 | 
			
		||||
		const char *ies_in_name = ie->tag_name ? : ie_o->name;
 | 
			
		||||
		if (seen(ies_in_name, ie))
 | 
			
		||||
			continue;
 | 
			
		||||
 | 
			
		||||
		printf("\nstatic const struct osmo_gtlv_coding ies_in_%s[] = {\n", ies_in_name);
 | 
			
		||||
		write_ies_array(indent, ie->nested_ies, decoded_type(ie_o), "");
 | 
			
		||||
		printi("{}\n");
 | 
			
		||||
		printf("};\n");
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Write the bulk of the C code: on the basis of the list of messages (g_cfg->msg_defs), write all dec/enc function
 | 
			
		||||
 * declarations, all IEs arrays as well as the list of message types, first triggering to write the C code for any inner
 | 
			
		||||
 * layers. */
 | 
			
		||||
static void write_c()
 | 
			
		||||
{
 | 
			
		||||
	const struct osmo_gtlv_gen_msg *gen_msg;
 | 
			
		||||
 | 
			
		||||
	printf("/* THIS FILE IS GENERATED FROM %s */\n", __FILE__);
 | 
			
		||||
	printf("#include <stddef.h>\n");
 | 
			
		||||
	printf("#include <errno.h>\n");
 | 
			
		||||
	printf("#include <osmocom/core/utils.h>\n");
 | 
			
		||||
	printf("#include <osmocom/gtlv/gtlv.h>\n");
 | 
			
		||||
	printf("#include <osmocom/gtlv/gtlv_dec_enc.h>\n");
 | 
			
		||||
	printf("#include <osmocom/gtlv/gtlv_gen.h>\n");
 | 
			
		||||
	if (g_cfg->c_header)
 | 
			
		||||
		printf("\n%s\n", g_cfg->c_header);
 | 
			
		||||
 | 
			
		||||
	printf("\n");
 | 
			
		||||
	clear_seen();
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		write_extern_dec_enc(gen_msg->ies);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	clear_seen();
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		write_nested_ies_array(gen_msg->ies);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		char *obj_type = talloc_asprintf(NULL, "union %s_ies", g_cfg->proto_name);
 | 
			
		||||
		char *substruct = talloc_asprintf(NULL, "%s.", gen_msg->name);
 | 
			
		||||
		printf("\nstatic const struct osmo_gtlv_coding ies_in_msg_%s[] = {\n", gen_msg->name);
 | 
			
		||||
		write_ies_array("\t", gen_msg->ies, obj_type, substruct);
 | 
			
		||||
		printf("\t{}\n};\n");
 | 
			
		||||
		talloc_free(substruct);
 | 
			
		||||
		talloc_free(obj_type);
 | 
			
		||||
	}
 | 
			
		||||
	printf("\nstatic const struct osmo_gtlv_coding *msg_defs[] = {\n");
 | 
			
		||||
	for (gen_msg = g_cfg->msg_defs; gen_msg->name; gen_msg++) {
 | 
			
		||||
		printf("\t[%s%s] = ies_in_msg_%s,\n", g_cfg->message_type_prefix, osmo_str_toupper(gen_msg->name), gen_msg->name);
 | 
			
		||||
	}
 | 
			
		||||
	printf("};\n");
 | 
			
		||||
 | 
			
		||||
	/* print this code snippet into the .c file, because only there can we do ARRAY_SIZE(foo_msg_coding). */
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"const struct osmo_gtlv_coding *%s_get_msg_coding(%s message_type)\n"
 | 
			
		||||
		"{\n"
 | 
			
		||||
		"	if (message_type >= ARRAY_SIZE(msg_defs))\n"
 | 
			
		||||
		"		return NULL;\n"
 | 
			
		||||
		"	return msg_defs[message_type];\n"
 | 
			
		||||
		"}\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->message_type_enum ? : "int");
 | 
			
		||||
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_decode(union %s_ies *dst, struct osmo_gtlv_load *gtlv, bool tlv_ordered,\n"
 | 
			
		||||
		"	%s message_type,\n"
 | 
			
		||||
		"	osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
 | 
			
		||||
		"{\n"
 | 
			
		||||
		"	return osmo_gtlvs_decode(dst, 0, gtlv, tlv_ordered, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
 | 
			
		||||
		"}\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_encode(struct osmo_gtlv_put *gtlv, const union %s_ies *src,\n"
 | 
			
		||||
		"	%s message_type, osmo_gtlv_err_cb err_cb, void *err_cb_data, const struct value_string *iei_strs)\n"
 | 
			
		||||
		"{\n"
 | 
			
		||||
		"	return osmo_gtlvs_encode(gtlv, src, 0, %s_get_msg_coding(message_type), err_cb, err_cb_data, iei_strs);\n"
 | 
			
		||||
		"}\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
 | 
			
		||||
	printf("\n"
 | 
			
		||||
		"int %s_ies_encode_to_str(char *buf, size_t buflen, const union %s_ies *src,\n"
 | 
			
		||||
		"	%s message_type, const struct value_string *iei_strs)\n"
 | 
			
		||||
		"{\n"
 | 
			
		||||
		"	return osmo_gtlvs_encode_to_str_buf(buf, buflen, src, 0, %s_get_msg_coding(message_type), iei_strs);\n"
 | 
			
		||||
		"}\n",
 | 
			
		||||
		g_cfg->proto_name, g_cfg->proto_name, g_cfg->message_type_enum ? : "int", g_cfg->proto_name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Call this from your main(). */
 | 
			
		||||
int osmo_gtlv_gen_main(const struct osmo_gtlv_gen_cfg *cfg, int argc, const char **argv)
 | 
			
		||||
{
 | 
			
		||||
	if (argc < 2)
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	g_cfg = cfg;
 | 
			
		||||
 | 
			
		||||
	if (strcmp(argv[1], "h") == 0)
 | 
			
		||||
		write_h();
 | 
			
		||||
	else if (strcmp(argv[1], "c") == 0)
 | 
			
		||||
		write_c();
 | 
			
		||||
	else
 | 
			
		||||
		return 1;
 | 
			
		||||
 | 
			
		||||
	clear_seen();
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										60
									
								
								tests/libosmo-gtlv/test_gtlv_gen/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								tests/libosmo-gtlv/test_gtlv_gen/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
							
								
								
									
										114
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gen__myproto_ies_auto.c
									
									
									
									
									
										Normal file
									
								
							@@ -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);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										261
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.c
									
									
									
									
									
										Normal file
									
								
							@@ -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(>lv, (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, >lv, 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;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										160
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok
									
									
									
									
									
										Normal file
									
								
							@@ -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]
 | 
			
		||||
							
								
								
									
										180
									
								
								tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.c
									
									
									
									
									
										Normal file
									
								
							@@ -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),
 | 
			
		||||
	{}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										70
									
								
								tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										70
									
								
								tests/libosmo-gtlv/test_gtlv_gen/myproto_ies_custom.h
									
									
									
									
									
										Normal file
									
								
							@@ -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;
 | 
			
		||||
};
 | 
			
		||||
@@ -12,3 +12,9 @@ AT_KEYWORDS([gtlv_dec_enc])
 | 
			
		||||
cat $abs_srcdir/libosmo-gtlv/gtlv_dec_enc_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/gtlv_dec_enc_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 | 
			
		||||
AT_SETUP([gtlv_gen])
 | 
			
		||||
AT_KEYWORDS([gtlv_gen])
 | 
			
		||||
cat $abs_srcdir/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test.ok > expout
 | 
			
		||||
AT_CHECK([$abs_top_builddir/tests/libosmo-gtlv/test_gtlv_gen/gtlv_gen_test], [], [expout], [ignore])
 | 
			
		||||
AT_CLEANUP
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user