From a2e065637e408ccbcf10d9bf9206da83d93d2628 Mon Sep 17 00:00:00 2001 From: Urban Wallasch Date: Sun, 27 Oct 2019 13:11:49 +0100 Subject: [PATCH] * str: initial commit * Updated README.html. --- README.html | 5 +- str/str.h | 141 ++++++++++++++++++++++++++++++++++++++++++++++++ str/str_test.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 288 insertions(+), 1 deletion(-) create mode 100644 str/str.h create mode 100644 str/str_test.c diff --git a/README.html b/README.html index 9b87fa7..6d0aebd 100644 --- a/README.html +++ b/README.html @@ -5,6 +5,9 @@ - C11 conforming expression tracing macro net -- Simple IPv4 and IPv6 based TCP and UDP client/server handling +- simple IPv4 and IPv6 based TCP and UDP client/server handling + + str +- string compare and search inline functions to complement string.h diff --git a/str/str.h b/str/str.h new file mode 100644 index 0000000..435587c --- /dev/null +++ b/str/str.h @@ -0,0 +1,141 @@ +/* + * str.h + * + * Copyright (c) 2019, Urban Wallasch + * BSD 3-Clause License, see LICENSE file for more details. + * + * Collection of inline functions to complement string.h + * + * str_icmp, + * str_nicmp - compare two strings ignoring case + * + * str_istr - locate a substring ignoring case + * + * mem_mem, + * mem_str, + * mem_istr - locate a pattern in memory + * + */ + +#ifndef STR_H_ +#define STR_H_ + +#ifdef cplusplus +extern "C" { +#endif + +#include +#include + + +/* + * str_icmp, str_nicmp - compare two strings ignoring case + * + * The strc_icmp() function performs a byte-by-byte comparison of the + * strings s1 and s2, ignoring the case of the characters. + * + * The str_nicmp() function is similar, except that it compares no more + * than n bytes of s1 and s2. If n is zero, the return value is zero. + * + * The strc_icmp() and str_nicmp() functions return an integer less than, + * equal to, or greater than zero if s1 is, after ignoring case, found to + * be less than, to match, or be greater than s2, respectively. +*/ +static inline int str_icmp(const char *s1, const char *s2) { + int r; + + do { + r = tolower((unsigned char)*s1) - tolower((unsigned char)*s2); + } + while (r == 0 && *s1++ && *s2++); + return r; +} + +static inline int str_nicmp(const char *s1, const char *s2, size_t n) { + int r = 0; + + if (n > 0) { + do { + r = tolower((unsigned char)*s1) - tolower((unsigned char)*s2); + } + while (r == 0 && --n && *s1++ && *s2++); + } + return r; +} + + +/* + * str_istr - locate a substring ignoring case + * + * The str_istr() function finds the first occurrence of the substring + * needle (excluding terminating null byte) in the string haystack, + * ignoring the case of both arguments. + * + * The function returns a pointer to the beginning of the located + * substring, or NULL if the substring is not found. If needle points + * to a string with zero length, the function returns haystack. + */ +static inline char *str_istr(const char *haystack, const char *needle) { + size_t n = needle ? strlen(needle) : 0; + + do { + if (str_nicmp(haystack, needle, n) == 0) + return (char *)haystack; + } while (*haystack++); + return NULL; +} + + +/* + * mem_mem, mem_str, mem_istr - locate a pattern in memory + * + * The mem_mem() function finds the first occurrence of the pattern + * needle of length n in an haystack of size h. + * + * The mem_str() function is similar, except that it expects a string + * for needle, ignoring the terminating null character. The mem_istr() + * function additionally ignores the case of characters in both needle + * and haystack. + * + * The mem_mem(), mem_str() and mem_istr() functions return a pointer + * to the beginning of the located pattern, or NULL if the pattern is + * not found. If the search pattern is of zero length, the functions + * return haystack. + */ +static inline void *mem_mem(const void *haystack, size_t h, const void *needle, size_t n) { + if (h >= n) { + const char *p = haystack; + h -= n; + do { + if (memcmp(p, needle, n) == 0) + return (void *)p; + ++p; + } while (h--); + } + return NULL; +} + +static inline void *mem_str(const void *haystack, size_t h, const char *needle) { + return mem_mem(haystack, h, needle, needle ? strlen(needle) : 0); +} + +static inline void *mem_istr(const void *haystack, size_t h, const char *needle) { + size_t n = needle ? strlen(needle) : 0; + + if (h >= n) { + const char *p = haystack; + h -= n; + do { + if (str_nicmp(p, needle, n) == 0) + return (void *)p; + ++p; + } while (h--); + } + return NULL; +} + +#ifdef cplusplus +} +#endif + +#endif /*ndef STR_H_*/ diff --git a/str/str_test.c b/str/str_test.c new file mode 100644 index 0000000..bc8a1cf --- /dev/null +++ b/str/str_test.c @@ -0,0 +1,143 @@ +/* + * str_test.c + * + * Copyright (c) 2019, Urban Wallasch + * BSD 3-Clause License, see LICENSE file for more details. + * + * Simple, non-exhaustive quick check for str.h. + * + * Build with: + * gcc -std=c99 -Wall -Wextra -Wpedantic -Werror -O2 -ostr_test str_test.c + */ + +#include +#include +#include + +#include "str.h" + + +#define H(a_) fprintf(stderr, "\n%s\n", a_) + +#define D(s_) do{ \ + fprintf(stderr, ". %s = '", #s_); \ + for (int i = 0; i < (int)sizeof(s_); ++i) { \ + if (s_[i] == 0) \ + fprintf(stderr, "\\0"); \ + else if (s_[i] < ' ' || s_[i] > 127) \ + fprintf(stderr, "\\x%02"PRIX8, (uint8_t)(s_[i])); \ + else \ + fputc(s_[i], stderr); \ + } \ + fprintf(stderr, "'\n"); \ + }while(0) \ + + +#define T(c_) do{ \ + assert(c_); \ + fprintf(stderr," %3d: %s \t pass\n",__LINE__,#c_); \ + }while(0) + + +int main(void) { + + H("str_icmp"); + T(str_icmp("asdf", "") > 0); + T(str_icmp("", "asdf") < 0); + T(str_icmp("asdf", "b") < 0); + T(str_icmp("b", "asdf") > 0); + T(str_icmp("asdfまほ", "asdfまほ") == 0); + T(str_icmp("Asdf", "asdf") == 0); + T(str_icmp("まほaSdf", "まほasdF") == 0); + T(str_icmp("asdf", "asdF") == 0); + + H("str_nicmp"); + T(str_nicmp(NULL, NULL, 0) == 0); + T(str_nicmp(NULL, "x", 0) == 0); + T(str_nicmp("x", NULL, 0) == 0); + T(str_nicmp("asdf", NULL, 0) == 0); + T(str_nicmp("asdf", NULL, 0) == 0); + T(str_nicmp("asdf", "", 0) == 0); + T(str_nicmp("asdf", "a", 1) == 0); + T(str_nicmp("a", "asdf", 1) == 0); + T(str_nicmp("asdf", "ab", 1) == 0); + T(str_nicmp("ab", "asdf", 1) == 0); + T(str_nicmp("asdf", "ab", 2) > 0); + T(str_nicmp("ab", "asdf", 2) < 0); + T(str_nicmp("asdfまほ", "asdまほ", 1) == 0); + T(str_nicmp("Asdf", "asdf", 1) == 0); + T(str_nicmp("まほaSdf", "まほasdF", 1) == 0); + T(str_nicmp("asdf", "asdF", 1) == 0); + + H("str_istr"); + char h1[] = "asdfま\0ほyxc"; + char h2[] = ""; + D(h1); + D(h2); + T(str_istr(NULL, NULL) == NULL); + T(str_istr(h2, NULL) == h2); + T(str_istr(NULL, "") == NULL); + T(str_istr(h2, "") == h2); + T(str_istr(h1, "") == h1); + T(str_istr(h2, "\0\0") == h2); + T(str_istr("", "a") == NULL); + T(str_istr(h1, "xoxoxoxo") == NULL); + T(str_istr(h1, "S") == h1 + 1); + T(str_istr(h1, "as") == h1); + T(str_istr(h1, "df") == h1 + 2); + T(str_istr(h1, "As") == h1); + T(str_istr(h1, "dF") == h1 + 2); + T(str_istr(h1, "X") == NULL); + T(str_istr(h1, "yxc") == NULL); + + H("mem_mem"); + char m1[] = { 1,'s','d','f', 0,'y','x','c', -1 }; + char m2[] = { 'a','b','c', 0 }; + D(m1); + D(m2); + T(mem_mem(NULL, 0, NULL, 0) == NULL); + T(mem_mem(m2, 0, NULL, 0) == m2); + T(mem_mem(m2, 0, "", 0) == m2); + T(mem_mem(NULL, 0, m2, 0) == NULL); + T(mem_mem(NULL, 0, "", 0) == NULL); + T(mem_mem(m2, 3, "abc", 3) == m2); + T(mem_mem(m2, 4, "abc", 4) == m2); + T(mem_mem(m1, sizeof m1, "", 0) == m1); + T(mem_mem(m1, sizeof m1, NULL, 0) == m1); + T(mem_mem(m1, sizeof m1, "\x01s", 2) == m1); + T(mem_mem(m1, sizeof m1, "f\0y", 3) == m1 + 3); + T(mem_mem(m1, sizeof m1, "f\0y" + 1, 2) == m1 + 4); + T(mem_mem(m1, sizeof m1, "f\0Y" + 1, 2) == NULL); + T(mem_mem(m1, sizeof m1, (char[]){-1}, 1) == m1 + 8); + T(mem_mem(m1, sizeof m1, "xc", 3) == NULL); + T(mem_mem(m1, sizeof m1, "xc", 2) == m1 + 6); + + H("mem_str"); + D(m1); + T(mem_str(NULL, 0, NULL) == NULL); + T(mem_str(NULL, 0, "") == NULL); + T(mem_str(m1, 0, NULL) == m1); + T(mem_str(m1, 0, "") == m1); + T(mem_str(m1, sizeof m1, "") == m1); + T(mem_str(m1, sizeof m1, NULL) == m1); + T(mem_str(m1, sizeof m1, "sdf") == m1 + 1); + T(mem_str(m1, sizeof m1, "yxc") == m1 + 5); + T(mem_str(m1, sizeof m1, "yxc\0qwe") == m1 + 5); + T(mem_str(m1, sizeof m1, "yXc\0qwe") == NULL); + + H("mem_istr"); + D(m1); + T(mem_istr(NULL, 0, NULL) == NULL); + T(mem_istr(NULL, 0, "") == NULL); + T(mem_istr(m1, 0, NULL) == m1); + T(mem_istr(m1, 0, "") == m1); + T(mem_istr(m1, sizeof m1, "") == m1); + T(mem_istr(m1, sizeof m1, NULL) == m1); + T(mem_istr(m1, sizeof m1, "") == m1); + T(mem_istr(m1, sizeof m1, "sDf") == m1 + 1); + T(mem_istr(m1, sizeof m1, "Yxc") == m1 + 5); + T(mem_istr(m1, sizeof m1, "yxC\0qwe") == m1 + 5); + + H("All tests passed."); + return 0; +} -- 2.30.2