diff options
| -rw-r--r-- | include/osmocom/core/utils.h | 2 | ||||
| -rw-r--r-- | src/utils.c | 41 | ||||
| -rw-r--r-- | tests/utils/utils_test.c | 114 | ||||
| -rw-r--r-- | tests/utils/utils_test.ok | 29 | 
4 files changed, 186 insertions, 0 deletions
| diff --git a/include/osmocom/core/utils.h b/include/osmocom/core/utils.h index 0b54c880..04f2fb4b 100644 --- a/include/osmocom/core/utils.h +++ b/include/osmocom/core/utils.h @@ -49,6 +49,8 @@ char osmo_bcd2char(uint8_t bcd);  /* only works for numbers in ascci */  uint8_t osmo_char2bcd(char c); +int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex); +  int osmo_hexparse(const char *str, uint8_t *b, int max_len);  char *osmo_ubit_dump(const uint8_t *bits, unsigned int len); diff --git a/src/utils.c b/src/utils.c index e6adcf86..4b4e6d25 100644 --- a/src/utils.c +++ b/src/utils.c @@ -129,6 +129,47 @@ uint8_t osmo_char2bcd(char c)  		return 0;  } +/*! Convert BCD to string. + * The given nibble offsets are interpreted in BCD order, i.e. nibble 0 is bcd[0] & 0xf, nibble 1 is bcd[0] >> 4, nibble + * 3 is bcd[1] & 0xf, etc.. + *  \param[out] dst  Output string buffer, is always nul terminated when dst_size > 0. + *  \param[in] dst_size  sizeof() the output string buffer. + *  \param[in] bcd  Binary coded data buffer. + *  \param[in] start_nibble  Offset to start from, in nibbles, typically 1 to skip the first nibble. + *  \param[in] end_nibble  Offset to stop before, in nibbles, e.g. sizeof(bcd) - (bcd[0] & GSM_MI_ODD? 0:1). + *  \param[in] allow_hex  If false, return error if there are digits other than 0-9. If true, return those as [A-F]. + *  \returns The strlen that would be written if the output buffer is large enough, excluding nul byte (like + *           snprintf()), or -EINVAL if allow_hex is false and a digit > 9 is encountered. On -EINVAL, the conversion is + *           still completed as if allow_hex were passed as true. Return -ENOMEM if dst is NULL or dst_size is zero. + *           If end_nibble <= start_nibble, write an empty string to dst and return 0. + */ +int osmo_bcd2str(char *dst, size_t dst_size, const uint8_t *bcd, int start_nibble, int end_nibble, bool allow_hex) +{ +	char *dst_end = dst + dst_size - 1; +	int nibble_i; +	int rc = 0; + +	if (!dst || dst_size < 1) +		return -ENOMEM; + +	for (nibble_i = start_nibble; nibble_i < end_nibble && dst < dst_end; nibble_i++, dst++) { +		uint8_t nibble = bcd[nibble_i >> 1]; +		if ((nibble_i & 1)) +			nibble >>= 4; +		nibble &= 0xf; + +		if (!allow_hex && nibble > 9) +			rc = -EINVAL; + +		*dst = osmo_bcd2char(nibble); +	} +	*dst = '\0'; + +	if (rc < 0) +		return rc; +	return OSMO_MAX(0, end_nibble - start_nibble); +} +  /*! Parse a string containing hexadecimal digits   *  \param[in] str string containing ASCII encoded hexadecimal digits   *  \param[out] b output buffer diff --git a/tests/utils/utils_test.c b/tests/utils/utils_test.c index 2bb1f9ca..a773b3f5 100644 --- a/tests/utils/utils_test.c +++ b/tests/utils/utils_test.c @@ -32,6 +32,7 @@  #include <time.h>  #include <netinet/in.h>  #include <arpa/inet.h> +#include <errno.h>  static void hexdump_test(void)  { @@ -383,6 +384,118 @@ static void bcd_test(void)  	}  } +struct bcd2str_test { +	const char *bcd_hex; +	int start_nibble; +	int end_nibble; +	bool allow_hex; +	size_t str_size; +	const char *expect_str; +	int expect_rc; +}; + +static const struct bcd2str_test bcd2str_tests[] = { +	{ +		.bcd_hex = "1a 32 54 76 98 f0", +		.start_nibble = 1, +		.end_nibble = 11, +		.expect_str = "1234567890", +		.expect_rc = 10, +	}, +	{ +		.bcd_hex = "1a 32 a4 cb 9d f0", +		.start_nibble = 1, +		.end_nibble = 11, +		.expect_str = "1234ABCD90", +		.expect_rc = -EINVAL, +	}, +	{ +		.bcd_hex = "1a 32 a4 cb 9d f0", +		.start_nibble = 1, +		.end_nibble = 11, +		.allow_hex = true, +		.expect_str = "1234ABCD90", +		.expect_rc = 10, +	}, +	{ +		.bcd_hex = "1a 32 54 76 98 f0", +		.start_nibble = 1, +		.end_nibble = 12, +		.expect_str = "1234567890F", +		.expect_rc = -EINVAL, +	}, +	{ +		.bcd_hex = "1a 32 54 76 98 f0", +		.start_nibble = 1, +		.end_nibble = 12, +		.allow_hex = true, +		.expect_str = "1234567890F", +		.expect_rc = 11, +	}, +	{ +		.bcd_hex = "1a 32 54 76 98 f0", +		.start_nibble = 0, +		.end_nibble = 12, +		.allow_hex = true, +		.expect_str = "A1234567890F", +		.expect_rc = 12, +	}, +	{ +		.bcd_hex = "1a 32 54 76 98 f0", +		.start_nibble = 1, +		.end_nibble = 12, +		.str_size = 5, +		.expect_str = "1234", +		.expect_rc = 11, +	}, +	{ +		.bcd_hex = "", +		.start_nibble = 1, +		.end_nibble = 1, +		.expect_str = "", +		.expect_rc = 0, +	}, +}; + +static void bcd2str_test(void) +{ +	int i; +	uint8_t bcd[64]; +	int rc; + +	printf("\nTesting bcd to string conversion\n"); + +	for (i = 0; i < ARRAY_SIZE(bcd2str_tests); i++) { +		const struct bcd2str_test *t = &bcd2str_tests[i]; +		char str[64] = {}; +		size_t str_size = t->str_size ? : sizeof(str); + +		osmo_hexparse(t->bcd_hex, bcd, sizeof(bcd)); + +		printf("- BCD-input='%s' nibbles=[%d..%d[ str_size=%zu\n", t->bcd_hex, +		       t->start_nibble, t->end_nibble, str_size); +		rc = osmo_bcd2str(str, str_size, bcd, t->start_nibble, t->end_nibble, t->allow_hex); + +		printf("  rc=%d\n", rc); + +		OSMO_ASSERT(str[str_size-1] == '\0'); +		printf("  -> %s\n", osmo_quote_str(str, -1)); + +		if (rc != t->expect_rc) +			printf("    ERROR: expected rc=%d\n", t->expect_rc); +		if (strcmp(str, t->expect_str)) +			printf("    ERROR: expected result %s\n", osmo_quote_str(t->expect_str, -1)); +	} + +	printf("- zero output buffer\n"); +	rc = osmo_bcd2str(NULL, 100, bcd, 1, 2, false); +	printf("  bcd2str(NULL, ...) -> %d\n", rc); +	OSMO_ASSERT(rc < 0); +	rc = osmo_bcd2str((char*)23, 0, bcd, 1, 2, false); +	printf("  bcd2str(dst, 0, ...) -> %d\n", rc); +	OSMO_ASSERT(rc < 0); +} +  static void str_escape_test(void)  {  	int i; @@ -810,6 +923,7 @@ int main(int argc, char **argv)  	test_ipa_ccm_id_resp_parsing();  	test_is_hexstr();  	bcd_test(); +	bcd2str_test();  	str_escape_test();  	str_quote_test();  	isqrt_test(); diff --git a/tests/utils/utils_test.ok b/tests/utils/utils_test.ok index 3ea8ec6a..65e32ed2 100644 --- a/tests/utils/utils_test.ok +++ b/tests/utils/utils_test.ok @@ -80,6 +80,35 @@ Testing BCD conversion  	val=0xe, expected=E, found=E  	val=0xf, expected=F, found=F +Testing bcd to string conversion +- BCD-input='1a 32 54 76 98 f0' nibbles=[1..11[ str_size=64 +  rc=10 +  -> "1234567890" +- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64 +  rc=-22 +  -> "1234ABCD90" +- BCD-input='1a 32 a4 cb 9d f0' nibbles=[1..11[ str_size=64 +  rc=10 +  -> "1234ABCD90" +- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64 +  rc=-22 +  -> "1234567890F" +- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=64 +  rc=11 +  -> "1234567890F" +- BCD-input='1a 32 54 76 98 f0' nibbles=[0..12[ str_size=64 +  rc=12 +  -> "A1234567890F" +- BCD-input='1a 32 54 76 98 f0' nibbles=[1..12[ str_size=5 +  rc=11 +  -> "1234" +- BCD-input='' nibbles=[1..1[ str_size=64 +  rc=0 +  -> "" +- zero output buffer +  bcd2str(NULL, ...) -> -12 +  bcd2str(dst, 0, ...) -> -12 +  Testing string escaping  - all chars from 0 to 255 in batches of 16:  "\0\1\2\3\4\5\6\a\b\t\n\v\f\r\14\15" | 
