Improve reusability of service_name and uuid components

To allow the service_name and uuid components to be reused
in constrained environments such as 'opteesp', the dependency
on sscanf (stdio.h) is removed and replaced with a portable
hex to byte and string to integer implementation.  This allows
the service locator to be used in SPs.

Signed-off-by: Julian Hall <julian.hall@arm.com>
Change-Id: I51bf7849d13568ef506c6244cab108e79b8bd09a
diff --git a/components/common/uuid/uuid.c b/components/common/uuid/uuid.c
index 7ce46c2..5d46441 100644
--- a/components/common/uuid/uuid.c
+++ b/components/common/uuid/uuid.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -7,80 +7,101 @@
 #include "uuid.h"
 #include <string.h>
 #include <ctype.h>
-#include <stdio.h>
+
+static uint8_t hex_to_nibble(char hex)
+{
+	uint8_t nibble = 0;
+
+	if (hex >= '0' && hex <= '9') {
+		nibble = hex - '0';
+	}
+	else {
+		nibble = ((hex | 0x20) - 'a') + 10;
+	}
+
+	return nibble;
+}
+
+static uint8_t hex_to_byte(const char *hex)
+{
+	/* Takes a validated input and returns the byte value */
+	uint8_t byte = hex_to_nibble(hex[0]) << 4;
+	byte |= (hex_to_nibble(hex[1]) & 0x0f);
+	return byte;
+}
 
 size_t uuid_is_valid(const char *canonical_form)
 {
-    size_t valid_chars = 0;
-    size_t input_len = strlen(canonical_form);
+	size_t valid_chars = 0;
+	size_t input_len = strlen(canonical_form);
 
-    if (input_len >= UUID_CANONICAL_FORM_LEN) {
+	if (input_len >= UUID_CANONICAL_FORM_LEN) {
 
-        size_t i;
-        valid_chars = UUID_CANONICAL_FORM_LEN;
+		size_t i;
+		valid_chars = UUID_CANONICAL_FORM_LEN;
 
-        for (i = 0; i < UUID_CANONICAL_FORM_LEN; ++i) {
+		for (i = 0; i < UUID_CANONICAL_FORM_LEN; ++i) {
 
-            if (i == 8 || i == 13 || i == 18 || i == 23) {
-                if (canonical_form[i] != '-') return 0;
-            }
-            else {
-                if (!isxdigit(canonical_form[i])) return 0;
-            }
-        }
-    }
+			if (i == 8 || i == 13 || i == 18 || i == 23) {
+				if (canonical_form[i] != '-') return 0;
+			}
+			else {
+				if (!isxdigit(canonical_form[i])) return 0;
+			}
+		}
+	}
 
-    return valid_chars;
+	return valid_chars;
 }
 
 size_t uuid_parse_to_octets(const char *canonical_form, uint8_t *buf, size_t buf_size)
 {
-    size_t octet_index = 0;
-    const char *pos;
-    size_t valid_chars = uuid_is_valid(canonical_form);
+	size_t octet_index = 0;
+	const char *pos;
+	size_t valid_chars = uuid_is_valid(canonical_form);
 
-    if ((buf_size < UUID_OCTETS_LEN) ||
-        (valid_chars != UUID_CANONICAL_FORM_LEN)) {
-        /* Invalid input */
-        return 0;
-    }
+	if ((buf_size < UUID_OCTETS_LEN) ||
+		(valid_chars != UUID_CANONICAL_FORM_LEN)) {
+		/* Invalid input */
+		return 0;
+	}
 
-    /*
-     * UUID string has been validates as having the following form:
-     * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
-     *     4      2    2   2       6
-     */
-    pos = &canonical_form[0];
-    while (octet_index < 4) {
-        sscanf(pos, "%02hhX", &buf[octet_index++]);
-        pos += 2;
-    }
+	/*
+	 * UUID string has been validates as having the following form:
+	 * xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
+	 *     4      2    2   2       6
+	 */
+	pos = &canonical_form[0];
+	while (octet_index < 4) {
+		buf[octet_index++] = hex_to_byte(pos);
+		pos += 2;
+	}
 
-    pos = &canonical_form[9];
-    while (octet_index < 6) {
-        sscanf(pos, "%02hhX", &buf[octet_index++]);
-        pos += 2;
-    }
+	pos = &canonical_form[9];
+	while (octet_index < 6) {
+		buf[octet_index++] = hex_to_byte(pos);
+		pos += 2;
+	}
 
-    pos = &canonical_form[14];
-    while (octet_index < 8) {
-        sscanf(pos, "%02hhX", &buf[octet_index++]);
-        pos += 2;
-    }
+	pos = &canonical_form[14];
+	while (octet_index < 8) {
+		buf[octet_index++] = hex_to_byte(pos);
+		pos += 2;
+	}
 
-    pos = &canonical_form[19];
-    while (octet_index < 10) {
-        sscanf(pos, "%02hhX", &buf[octet_index++]);
-        pos += 2;
-    }
+	pos = &canonical_form[19];
+	while (octet_index < 10) {
+		buf[octet_index++] = hex_to_byte(pos);
+		pos += 2;
+	}
 
-    pos = &canonical_form[24];
-    while (octet_index < 16) {
-        sscanf(pos, "%02hhX", &buf[octet_index++]);
-        pos += 2;
-    }
+	pos = &canonical_form[24];
+	while (octet_index < 16) {
+		buf[octet_index++] = hex_to_byte(pos);
+		pos += 2;
+	}
 
-    return valid_chars;
+	return valid_chars;
 }
 
 /*
@@ -91,34 +112,34 @@
  */
 size_t uuid_parse_to_octets_reversed(const char *canonical_form, uint8_t *buf, size_t buf_size)
 {
-    size_t valid_chars;
-    uint8_t standard_octets[UUID_OCTETS_LEN];
+	size_t valid_chars;
+	uint8_t standard_octets[UUID_OCTETS_LEN];
 
-    valid_chars = uuid_parse_to_octets(canonical_form, standard_octets, sizeof(standard_octets));
+	valid_chars = uuid_parse_to_octets(canonical_form, standard_octets, sizeof(standard_octets));
 
-    if ((valid_chars == UUID_CANONICAL_FORM_LEN) && (buf_size >= UUID_OCTETS_LEN)) {
-        /* Reverse bytes in each section */
-        buf[0] = standard_octets[3];
-        buf[1] = standard_octets[2];
-        buf[2] = standard_octets[1];
-        buf[3] = standard_octets[0];
+	if ((valid_chars == UUID_CANONICAL_FORM_LEN) && (buf_size >= UUID_OCTETS_LEN)) {
+		/* Reverse bytes in each section */
+		buf[0] = standard_octets[3];
+		buf[1] = standard_octets[2];
+		buf[2] = standard_octets[1];
+		buf[3] = standard_octets[0];
 
-        buf[4] = standard_octets[5];
-        buf[5] = standard_octets[4];
+		buf[4] = standard_octets[5];
+		buf[5] = standard_octets[4];
 
-        buf[6] = standard_octets[7];
-        buf[7] = standard_octets[6];
+		buf[6] = standard_octets[7];
+		buf[7] = standard_octets[6];
 
-        buf[8] = standard_octets[8];
-        buf[9] = standard_octets[9];
+		buf[8] = standard_octets[8];
+		buf[9] = standard_octets[9];
 
-        buf[10] = standard_octets[10];
-        buf[11] = standard_octets[11];
-        buf[12] = standard_octets[12];
-        buf[13] = standard_octets[13];
-        buf[14] = standard_octets[14];
-        buf[15] = standard_octets[15];
-    }
+		buf[10] = standard_octets[10];
+		buf[11] = standard_octets[11];
+		buf[12] = standard_octets[12];
+		buf[13] = standard_octets[13];
+		buf[14] = standard_octets[14];
+		buf[15] = standard_octets[15];
+	}
 
-    return valid_chars;
+	return valid_chars;
 }
diff --git a/components/service/locator/service_name.c b/components/service/locator/service_name.c
index 878b84b..0999787 100644
--- a/components/service/locator/service_name.c
+++ b/components/service/locator/service_name.c
@@ -1,11 +1,11 @@
 /*
- * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
 
+#include <ctype.h>
 #include <string.h>
-#include <stdio.h>
 #include "service_name.h"
 
 /*
@@ -15,14 +15,14 @@
  */
 struct sn_parsed_fields
 {
-    size_t authority_pos;
-    size_t authority_len;
-    size_t service_pos;
-    size_t service_len;
-    size_t version_pos;
-    size_t version_len;
-    size_t instance_pos;
-    size_t instance_len;
+	size_t authority_pos;
+	size_t authority_len;
+	size_t service_pos;
+	size_t service_len;
+	size_t version_pos;
+	size_t version_len;
+	size_t instance_pos;
+	size_t instance_len;
 };
 
 /*
@@ -31,18 +31,18 @@
  */
 static size_t field_len(const char *sn, size_t from, size_t len, char separator)
 {
-    size_t field_len = 0;
-    size_t pos = from;
+	size_t field_len = 0;
+	size_t pos = from;
 
-    while (pos < len) {
+	while (pos < len) {
 
-        if (sn[pos] == separator) break;
+		if (sn[pos] == separator) break;
 
-        ++pos;
-        ++field_len;
-    }
+		++pos;
+		++field_len;
+	}
 
-    return field_len;
+	return field_len;
 }
 
 /*
@@ -51,21 +51,21 @@
  */
 static bool find_next(const char *sn, size_t from, size_t len, char separator, size_t *next)
 {
-    bool found = false;
-    size_t pos = from;
+	bool found = false;
+	size_t pos = from;
 
-    while (pos < len) {
+	while (pos < len) {
 
-        if (sn[pos] == separator) {
-            *next = pos + 1;
-            found = (*next < len);
-            break;
-        }
+		if (sn[pos] == separator) {
+			*next = pos + 1;
+			found = (*next < len);
+			break;
+		}
 
-        ++pos;
-    }
+		++pos;
+	}
 
-    return found;
+	return found;
 }
 
 /*
@@ -74,123 +74,142 @@
  */
 static bool sn_parse(const char *sn, struct sn_parsed_fields *fields)
 {
-    size_t sn_len = strlen(sn);
-    size_t field_pos = 0;
+	size_t sn_len = strlen(sn);
+	size_t field_pos = 0;
 
-    fields->authority_pos = 0;
-    fields->authority_len = 0;
-    fields->service_pos = 0;
-    fields->service_len = 0;
-    fields->version_pos = 0;
-    fields->version_len = 0;
-    fields->instance_pos = 0;
-    fields->instance_len = 0;
+	fields->authority_pos = 0;
+	fields->authority_len = 0;
+	fields->service_pos = 0;
+	fields->service_len = 0;
+	fields->version_pos = 0;
+	fields->version_len = 0;
+	fields->instance_pos = 0;
+	fields->instance_len = 0;
 
-    /* Absorb urn: if present */
-    if (memcmp("urn", &sn[field_pos], strlen("urn")) == 0) {
-        if (!find_next(sn, field_pos, sn_len, ':', &field_pos)) return false;
-    }
+	/* Absorb urn: if present */
+	if (memcmp("urn", &sn[field_pos], strlen("urn")) == 0) {
+		if (!find_next(sn, field_pos, sn_len, ':', &field_pos)) return false;
+	}
 
-    /* Check it is a service name */
-    if (memcmp("sn", &sn[field_pos], strlen("sn")) != 0) {
-        /* Not a service name */
-         return false;
-    }
+	/* Check it is a service name */
+	if (memcmp("sn", &sn[field_pos], strlen("sn")) != 0) {
+		/* Not a service name */
+		 return false;
+	}
 
-    /* Expect the authority field */
-    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
-        fields->authority_pos = field_pos;
-        fields->authority_len = field_len(sn, field_pos, sn_len, ':');
-    }
-    else {
-        /* Missing mandatory authority field */
-        return false;
-    }
+	/* Expect the authority field */
+	if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+		fields->authority_pos = field_pos;
+		fields->authority_len = field_len(sn, field_pos, sn_len, ':');
+	}
+	else {
+		/* Missing mandatory authority field */
+		return false;
+	}
 
-    /* Expect the service field */
-    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
-        fields->service_pos = field_pos;
-        fields->service_len = field_len(sn, field_pos, sn_len, ':');
+	/* Expect the service field */
+	if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+		fields->service_pos = field_pos;
+		fields->service_len = field_len(sn, field_pos, sn_len, ':');
 
-        /* Check for the optional version */
-        if (find_next(sn, field_pos, field_pos + fields->service_len, '.', &field_pos)) {
-            fields->version_pos = field_pos;
-            fields->version_len = field_len(sn, field_pos, sn_len, ':');
-        }
-    }
-    else {
-        /* Missing mandatory service field */
-        return false;
-    }
+		/* Check for the optional version */
+		if (find_next(sn, field_pos, field_pos + fields->service_len, '.', &field_pos)) {
+			fields->version_pos = field_pos;
+			fields->version_len = field_len(sn, field_pos, sn_len, ':');
+		}
+	}
+	else {
+		/* Missing mandatory service field */
+		return false;
+	}
 
-    /* Check for optional instance */
-    if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
-        fields->instance_pos = field_pos;
-        fields->instance_len = sn_len - field_pos;
-    }
+	/* Check for optional instance */
+	if (find_next(sn, field_pos, sn_len, ':', &field_pos)) {
+		fields->instance_pos = field_pos;
+		fields->instance_len = sn_len - field_pos;
+	}
 
-    return true;
+	return true;
 }
 
 bool sn_is_valid(const char *sn)
 {
-    struct sn_parsed_fields fields;
-    return sn_parse(sn, &fields);
+	struct sn_parsed_fields fields;
+	return sn_parse(sn, &fields);
 }
 
 bool sn_check_authority(const char *sn, const char *auth)
 {
-    bool matched = false;
-    struct sn_parsed_fields fields;
+	bool matched = false;
+	struct sn_parsed_fields fields;
 
-    if (sn_parse(sn, &fields) && fields.authority_len) {
+	if (sn_parse(sn, &fields) && fields.authority_len) {
 
-        matched = (memcmp(auth, &sn[fields.authority_pos], strlen(auth)) == 0);
-    }
+		matched = (memcmp(auth, &sn[fields.authority_pos], strlen(auth)) == 0);
+	}
 
-    return matched;
+	return matched;
 }
 
 bool sn_check_service(const char *sn, const char *service)
 {
-    bool matched = false;
-    struct sn_parsed_fields fields;
+	bool matched = false;
+	struct sn_parsed_fields fields;
 
-    if (sn_parse(sn, &fields) && fields.service_len) {
+	if (sn_parse(sn, &fields) && fields.service_len) {
 
-        matched = (memcmp(service, &sn[fields.service_pos], strlen(service)) == 0);
-    }
+		matched = (memcmp(service, &sn[fields.service_pos], strlen(service)) == 0);
+	}
 
-    return matched;
+	return matched;
 }
 
 unsigned int sn_get_service_instance(const char *sn)
 {
-    unsigned int instance = 0;
+	unsigned int instance = 0;
 
-    struct sn_parsed_fields fields;
+	struct sn_parsed_fields fields;
 
-    if (sn_parse(sn, &fields) && fields.instance_len) {
+	if (sn_parse(sn, &fields) && fields.instance_len) {
 
-        sscanf(&sn[fields.instance_pos], "%d", &instance);
-    }
+		/* Instance must be expressed as a decimal */
+		unsigned int multiplier = 1;
 
-    return instance;
+		for (size_t i = 0; i < fields.instance_len; i++) {
+
+			size_t digit_index = fields.instance_pos + fields.instance_len - 1 - i;
+			char digit = sn[digit_index];
+
+			if (isdigit(digit)) {
+
+			   instance += ((digit - '0') * multiplier);
+			   multiplier *= 10;
+			}
+			else {
+
+				/* Invalid instance string */
+				instance = 0;
+				break;
+			}
+		}
+	}
+
+	return instance;
 }
 
 size_t sn_read_service(const char *sn, char *buf, size_t buf_len)
 {
-    size_t field_len = 0;
+	size_t field_len = 0;
 
-    memset(buf, 0, buf_len);
-    struct sn_parsed_fields fields;
+	memset(buf, 0, buf_len);
+	struct sn_parsed_fields fields;
 
-    if (sn_parse(sn, &fields) && fields.service_len && (fields.service_len < buf_len)) {
+	if (sn_parse(sn, &fields) && fields.service_len && (fields.service_len < buf_len)) {
 
-        field_len = fields.service_len;
-        memcpy(buf, &sn[fields.service_pos], field_len);
-        buf[field_len] = '\0';
-    }
+		field_len = fields.service_len;
+		memcpy(buf, &sn[fields.service_pos], field_len);
+		buf[field_len] = '\0';
+	}
 
-    return field_len;
+	return field_len;
 }
diff --git a/components/service/locator/test/sn_tests.cpp b/components/service/locator/test/sn_tests.cpp
index 4f8630a..50cce75 100644
--- a/components/service/locator/test/sn_tests.cpp
+++ b/components/service/locator/test/sn_tests.cpp
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2020, Arm Limited and Contributors. All rights reserved.
+ * Copyright (c) 2020-2021, Arm Limited and Contributors. All rights reserved.
  *
  * SPDX-License-Identifier: BSD-3-Clause
  */
@@ -54,10 +54,10 @@
     CHECK(sn_check_service(sn2, "secure-storage"));
     CHECK_EQUAL(0, sn_get_service_instance(sn2));
 
-    const char *sn3 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0:7";
+    const char *sn3 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0:77";
     CHECK(sn_check_authority(sn3, "ffa"));
     CHECK(sn_check_service(sn3, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0"));
-    CHECK_EQUAL(7, sn_get_service_instance(sn3));
+    CHECK_EQUAL(77, sn_get_service_instance(sn3));
 
     /* Check instance defaults to zero */
     const char *sn4 = "sn:ffa:d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0";
@@ -88,4 +88,4 @@
     CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, sn_read_service(sn4, buf, sizeof(buf)));
     CHECK(memcmp(buf, "d9df52d5-16a2-4bb2-9aa4-d26d3b84e8c0", UUID_CANONICAL_FORM_LEN + 1) == 0);
     CHECK_EQUAL(UUID_CANONICAL_FORM_LEN, strlen(buf));
-}
\ No newline at end of file
+}