Browse Source

str: Numeric comparison faux_str_numcmp()

Serj Kalichev 1 year ago
parent
commit
efb16644a3
5 changed files with 87 additions and 0 deletions
  1. 1 0
      faux/faux.map
  2. 1 0
      faux/str.h
  3. 52 0
      faux/str/str.c
  4. 32 0
      faux/str/testc_str.c
  5. 1 0
      faux/testc_module/testc_module.c

+ 1 - 0
faux/faux.map

@@ -298,6 +298,7 @@ FAUX_2.0 {
 		faux_str_cmp;
 		faux_str_casecmpn;
 		faux_str_casecmp;
+		faux_str_numcmp;
 		faux_str_casestr;
 		faux_str_charsn;
 		faux_str_chars;

+ 1 - 0
faux/str.h

@@ -35,6 +35,7 @@ int faux_str_cmpn(const char *str1, const char *str2, size_t n);
 int faux_str_cmp(const char *str1, const char *str2);
 int faux_str_casecmpn(const char *str1, const char *str2, size_t n);
 int faux_str_casecmp(const char *str1, const char *str2);
+int faux_str_numcmp(const char *str1, const char *str2);
 char *faux_str_casestr(const char *haystack, const char *needle);
 char *faux_str_charsn(const char *str, const char *chars_to_search, size_t n);
 char *faux_str_chars(const char *str, const char *chars_to_search);

+ 52 - 0
faux/str/str.c

@@ -13,6 +13,7 @@
 #include <stdarg.h>
 
 #include "faux/ctype.h"
+#include "faux/conv.h"
 #include "faux/str.h"
 
 /** @brief Free the memory allocated for the string.
@@ -438,6 +439,57 @@ int faux_str_casecmp(const char *str1, const char *str2)
 }
 
 
+/** @brief Compare two strings considering numbers.
+ *
+ * "a2" < "a10"
+ *
+ * @param [in] str1 First string to compare.
+ * @param [in] str2 Second string to compare.
+ * @return < 0, 0, > 0, see the strcasecmp().
+ */
+int faux_str_numcmp(const char *str1, const char *str2)
+{
+	const char *p1 = str1;
+	const char *p2 = str2;
+
+	if (!p1 && !p2) // Empty strings are equal
+		return 0;
+
+	if (!p1) // Consider NULL string to be less then empty string
+		return -1;
+
+	if (!p2) // Consider NULL string to be less then empty string
+		return 1;
+
+	while (*p1 != '\0' && *p2 != '\0') {
+		if (faux_ctype_isdigit(*p1) && faux_ctype_isdigit(*p2)) {
+			unsigned long long int v1 = 0;
+			unsigned long long int v2 = 0;
+			if (!faux_conv_atoull(p1, &v1, 10) ||
+				!faux_conv_atoull(p2, &v2, 10)) // Overflow?
+				return faux_str_cmp(str1, str2); // Standard comparison
+			if (v1 > v2)
+				return 1;
+			if (v1 < v2)
+				return -1;
+			// Skip all digits if equal
+			while (faux_ctype_isdigit(*p1))
+				p1++;
+			while (faux_ctype_isdigit(*p2))
+				p2++;
+		} else {
+			int res = faux_str_cmp_chars(*p1, *p2);
+			if (res != 0)
+				return res;
+			p1++;
+			p2++;
+		}
+	}
+
+	return faux_str_cmp_chars(*p1, *p2);
+}
+
+
 /** @brief Finds the first occurrence of the substring in the string
  *
  * Function is a faux version of strcasestr() function.

+ 32 - 0
faux/str/testc_str.c

@@ -93,3 +93,35 @@ int testc_faux_str_getline(void)
 
 	return 0;
 }
+
+
+int testc_faux_str_numcmp(void)
+{
+	if (faux_str_numcmp("abc2", "abc10") >= 0) {
+		printf("'abc2' >= 'abc10'\n");
+		return -1;
+	}
+
+	if (faux_str_numcmp("abc2ccc", "abc10ccc") >= 0) {
+		printf("'abc2ccc' >= 'abc10ccc'\n");
+		return -1;
+	}
+
+	if (faux_str_numcmp("abc2ccc", "abcaccc") >= 0) {
+		printf("'abc2ccc' >= 'abcaccc'\n");
+		return -1;
+	}
+
+	if (faux_str_numcmp("abc222222222222222ccc", "abc222222222222222cdc") >= 0) {
+		printf("'abc222222222222222ccc' >= 'abc222222222222222cdc'\n");
+		return -1;
+	}
+
+	// Overflow
+	if (faux_str_numcmp("abc222222222222222222222222222222ccc", "abc1022222222222222222222222222222ccc") <= 0) {
+		printf("'abc222222222222222222222222222222ccc' <= 'abc1022222222222222222222222222222ccc'\n");
+		return -1;
+	}
+
+	return 0;
+}

+ 1 - 0
faux/testc_module/testc_module.c

@@ -16,6 +16,7 @@ const char *testc_module[][2] = {
 	// str
 	{"testc_faux_str_nextword", "Find next word (quotation)"},
 	{"testc_faux_str_getline", "Get line from string"},
+	{"testc_faux_str_numcmp", "Numeric comparison"},
 
 	// ini
 	{"testc_faux_ini_parse_file", "Complex test of INI file parsing"},