2024-04-22 00:09:51 +08:00

841 lines
22 KiB
C

/*********************************************************************************************************
* ------------------------------------------------------------------------------------------------------
* file description
* ------------------------------------------------------------------------------------------------------
* \file str.c
* \unit str
* \brief This is a general C language string container module
* \author Lamdonn
* \version v1.0.0
* \license GPL-2.0
* \copyright Copyright (C) 2023 Lamdonn.
********************************************************************************************************/
#include "str.h"
#include <string.h>
#include <stdio.h>
#include <math.h>
/* str define */
typedef struct STR
{
char ident; /**< ident of str */
char* base; /**< base address of string */
int length; /**< length of str */
int capacity; /**< capacity of str */
} STR;
/* str identification mark */
#define ident() (-1)
/* tool */
#define up_multiple(x, mul) ((x)+((mul)-((x)-1)%(mul))-1)
#define is_digit(c) ((c)>='0'&&(c)<='9')
#define MAX(x, y) ((x)>(y)?(x):(y))
/* error space */
static char error = 0;
/* str info define */
typedef struct
{
char *base; /**< base address of string */
int length; /**< length of str */
} str_info_t;
/**
* \brief calculate capacity according to gradient
* \param[in] size: size
* \return capacity
*/
static int gradient_capacity(int size)
{
int capacity = 1;
if (size <= 1) return 1;
while (capacity < size) capacity <<= 1;
capacity >>= 1;
if (capacity < 4) capacity = capacity << 1;
else if (capacity < 16) capacity = up_multiple(size, capacity >> 1);
else if (capacity < 256) capacity = up_multiple(size, capacity >> 2);
else capacity = up_multiple(size, 64);
return capacity;
}
/**
* \brief get string information
* \param[in] string: string, type can be array string or str
* \return information of string
*/
static str_info_t string_info(void *string)
{
str_info_t info;
if (((char *)string)[0] == 0) /* empty array string */
{
info.base = (char *)string;
info.length = 0;
}
else if (((str_t)string)->ident == ident()) /* str type */
{
info.base = ((str_t)string)->base;
info.length = ((str_t)string)->length;
}
else /* array string type */
{
info.base = (char *)string;
info.length = strlen((char *)string);
}
return info;
}
/**
* \brief alter the capacity of the str
* \param[in] str: str handler
* \param[in] length: length of str
* \return new capacity of str
*/
static int str_alter_capacity(str_t str, int length)
{
int capacity = 0;
char *base = NULL;
/* check whether the capacity changes and need to adjust the capacity space */
capacity = gradient_capacity(length);
if (str->capacity != 0 && str->capacity == capacity) return 1;
if (!str->base) /* allocate new space */
{
str->base = (char *)malloc(capacity + 1);
if (!str->base) return 0;
}
else /* reallocate space */
{
base = (char *)realloc(str->base, capacity + 1);
if (!base) return 0;
str->base = base;
}
str->capacity = capacity;
return 1;
}
str_t str_create(void *string)
{
str_t str;
/* Input value validity check */
if (!string) return NULL;
/* Allocate memory for the STR structure */
str = (str_t)malloc(sizeof(STR));
if (!str) return NULL;
/* Initialize structural parameters */
str->ident = ident();
str->base = NULL;
str->length = 0;
str->capacity = 0;
/* Assign initial value */
if (!str_assign(str, string))
{
free(str);
return NULL;
}
return str;
}
void str_delete(str_t str)
{
/* Input value validity check */
if (!str) return;
/* If it is not a constant array, release the allocated space */
if (str->base) free(str->base);
/* Free str structure */
free(str);
}
str_t str_assign(str_t str, void *string)
{
str_info_t info;
/* Input value validity check */
if (!str) return NULL;
if (!string) return NULL;
/* Get basic information about the incoming string */
info = string_info(string);
/* Allocate space to the string */
if (!str_alter_capacity(str, info.length)) return NULL;
/* Assignment string */
strcpy(str->base, info.base);
/* Update str status */
str->length = info.length;
return str;
}
char* str_data(str_t str, int pos)
{
/* Input value validity check */
if (!str || pos < 0 || pos >= str->length)
{
error = 0; /* reset error area */
return &error;
}
/* Return the base address of the string storage */
return &str->base[pos];
}
const char* str_c_str(str_t str)
{
return (const char *)str_data(str, 0);
}
int str_length(str_t str)
{
/* Input value validity check */
if (!str) return 0;
/* Return the length of the string */
return str->length;
}
int str_capacity(str_t str)
{
/* Input value validity check */
if (!str) return 0;
/* Return the capacity of the string */
return str->capacity;
}
int str_empty(str_t str)
{
/* Input value validity check */
if (!str) return 1;
/* Judging whether it is empty based on its length */
return str->length == 0 ? 1 : 0;
}
void str_clear(str_t str)
{
/* Directly assign a value to an empty string */
str_assign(str, "");
}
str_t str_insert(str_t str, int pos, void *string)
{
return str_replace(str, pos, 0, string);
}
str_t str_erase(str_t str, int pos, int len)
{
return str_replace(str, pos, len, "");
}
str_t str_append_series(str_t str, ...)
{
va_list args;
void* s = NULL;
/* Input value validity check */
if (!str) return NULL;
va_start(args, str);
/* Loop to obtain indefinite parameters */
s = va_arg(args, void*);
while (s)
{
/* insert at the end */
if (!str_insert(str, str->length, s))
{
va_end(args);
return NULL;
}
s = va_arg(args, void*);
}
va_end(args);
return str;
}
int str_push_back(str_t str, char c)
{
char temp[2] = {0};
/* Input value validity check */
if (!str) return 0;
/* Combine characters into a short string */
temp[0] = c;
/* Append characters to the end */
if (!str_append_series(str, temp, NULL)) return 0;
return 1;
}
char str_pop_back(str_t str)
{
char c = 0;
/* Input value validity check */
if (!str) return 0;
/* Check if the string is not empty */
if (str->length <= 0) return 0;
/* Record Tail */
c = str->base[str->length - 1];
/* Remove the tail */
if (!str_erase(str, str->length - 1, 1)) return 0;
return c;
}
int str_compare(str_t str, void *string)
{
str_info_t info;
int ret = 0;
/* Input value validity check */
if (!str) return -1;
if (!string) return 1;
/* Get basic information about the incoming string */
info = string_info(string);
/* Compare two strings */
ret = strcmp(str->base, info.base);
/* Update return value */
if (ret > 0) ret = 1;
else if (ret < 0) ret = -1;
return ret;
}
str_t str_substr(str_t str, int pos, int len)
{
str_t copy = NULL;
/* Input value validity check */
if (!str) return NULL;
if (pos < 0 || pos >= str->length) return NULL;
if (len <= 0) return NULL;
/* Create an empty string */
copy = str_create("");
if (!copy) return NULL;
/* Update the copied length */
if (len > str->length - pos) len = str->length - pos;
/* Allocate space to the string */
if (!str_alter_capacity(copy, len))
{
str_delete(copy);
return NULL;
}
/* Assignment string */
memcpy(copy->base, &str->base[pos], len);
copy->base[len] = 0;
copy->length = len;
return copy;
}
int str_find(str_t str, void *string, int pos)
{
str_info_t info;
char *find = NULL;
int ret = 0;
/* Input value validity check */
if (!str) return str_npos;
if (!string) return str_npos;
if (pos < 0 || pos >= str->length) return str_npos;
/* Get basic information about the incoming string */
info = string_info(string);
/* The length of the substring exceeds the limit, return directly */
if (info.length > str->length) return str_npos;
/* Find substrings */
find = strstr(&str->base[pos], info.base);
if (!find) return str_npos;
/* Calculate find location */
ret = find - str->base;
return ret;
}
/**
* \brief match substrings from the tail
* \param[in] *haystack: parent string
* \param[in] tail_index: specifies the end index of the parent string character array
* \param[in] *needle: child string
* \return matching segment in the parent string or NULL: fail
*/
static char* strrstr(char *haystack, int end_index, char *needle)
{
char *find = NULL;
int index = 0;
int i = 0;
int hay_len = end_index;
int need_len = strlen(needle);
for (index = hay_len; index >= 0; index--)
{
if (haystack[index] == needle[0])
{
find = &haystack[index];
for (i = 0; i < need_len; i++)
{
if (find[i] != needle[i]) break;
}
if (i == need_len) return find;
}
}
return NULL;
}
int str_rfind(str_t str, void *string, int pos)
{
str_info_t info;
char *find = NULL;
int ret = 0;
/* Input value validity check */
if (!str) return str_npos;
if (!string) return str_npos;
if (pos < 0 || pos >= str->length) return str_npos;
/* Get basic information about the incoming string */
info = string_info(string);
/* The length of the substring exceeds the limit, return directly */
if (info.length > str->length) return str_npos;
/* Find substrings */
find = strrstr(str->base, pos, info.base);
if (!find) return str_npos;
/* Calculate find location */
ret = find - str->base;
return ret;
}
/** str_find_of
* \brief find function of "of" type
* \param[in] str: str handler
* \param[in] *string: string, type can be array string or str
* \param[in] pos: the position to start find
* \param[in] from: 0: first, 1: last
* \param[in] of: 0: not of, 1: of
* \return position of the specified character or str_npos: fail
*/
static int str_find_of(str_t str, void *string, int pos, int from, int of)
{
str_info_t info;
int i = 0;
if (!str) return str_npos;
if (!string) return str_npos;
if (pos < 0 || pos >= str->length) return str_npos;
info = string_info(string);
if (info.length <= 0) return str_npos;
/* matching */
if (from == 0) /* first */
{
for (i = pos; i < str->length; i++)
{
if ((of == 1 && strchr(info.base, str->base[i])) ||
(of == 0 && !strchr(info.base, str->base[i])))
{
return i;
}
}
}
else /* last */
{
for (i = pos; i >= 0; i--)
{
if ((of == 1 && strchr(info.base, str->base[i])) ||
(of == 0 && !strchr(info.base, str->base[i])))
{
return i;
}
}
}
return str_npos;
}
int str_find_first_of(str_t str, void *string, int pos)
{
return str_find_of(str, string, pos, 0, 1);
}
int str_find_first_not_of(str_t str, void *string, int pos)
{
return str_find_of(str, string, pos, 0, 0);
}
int str_find_last_of(str_t str, void *string, int pos)
{
return str_find_of(str, string, pos, 1, 1);
}
int str_find_last_not_of(str_t str, void *string, int pos)
{
return str_find_of(str, string, pos, 1, 0);
}
str_t str_reverse(str_t str, int begin, int end)
{
int i = 0;
char c = 0;
/* Input value validity check */
if (!str) return NULL;
if (begin < 0) begin = 0;
if (end >= str->length) end = str->length - 1;
if (begin >= end) return NULL;
/* Reverse characters with symmetrical positions in a loop */
for (i = 0; i <= (end - begin) / 2; i++)
{
c = str->base[begin + i];
str->base[begin + i] = str->base[end - i];
str->base[end - i] = c;
}
return str;
}
str_t str_replace(str_t str, int pos, int len, void *string)
{
str_info_t info;
char *overlap = NULL;
/* Input value validity check */
if (!str) return NULL;
if (!string) return NULL;
if (pos < 0 || pos > str->length) return NULL;
/* Update the copied length */
if (len > str->length - pos) len = str->length - pos;
/* Get basic information about the incoming string */
info = string_info(string);
/* Check if addresses overlap */
if (str->base <= info.base && info.base <= str->base + str->length && pos < str->length)
{
/* Allocate temporary space */
overlap = (char *)malloc(info.length + 1);
if (!overlap) return NULL;
strcpy(overlap, info.base);
info.base = overlap;
}
if (info.length > len) /* lengthen */
{
if (str_alter_capacity(str, str->length + (info.length - len)) == 0)
{
if (overlap) free(overlap);
return NULL;
}
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
memcpy(&str->base[pos], info.base, info.length);
}
else if (info.length < len) /* shorten */
{
memmove(&str->base[pos + info.length], &str->base[pos + len], str->length - (pos + len));
memcpy(&str->base[pos], info.base, info.length);
str_alter_capacity(str, str->length + (info.length - len));
}
else
{
memcpy(&str->base[pos], info.base, info.length);
}
str->length += (info.length - len);
str->base[str->length] = 0;
/* Free temporary space */
if (overlap) free(overlap);
return str;
}
void str_swap(str_t str, str_t swap)
{
STR temp;
/* Input value validity check */
if (!str) return;
if (!swap) return;
/* Swap string structures */
temp = *str;
*str = *swap;
*swap = temp;
}
int str_copy(str_t str, int pos, int len, char *buf)
{
/* Input value validity check */
if (!str) return 0;
if (pos < 0 || pos > str->length) return 0;
if (len > str->length - pos) len = str->length - pos;
/* Copy out a string */
memcpy(buf, &str->base[pos], len);
return len;
}
/**
* \brief get digit from string
* \param[in] s: char string
* \param[out] out_digit: out digit
* \return length of digit
*/
static int get_digit(char *s, int *out_digit)
{
int digit = 0;
int len = 0;
while (is_digit(*s))
{
digit = digit * 10 + *(s++) - '0';
len++;
}
*out_digit = digit;
return len;
}
str_t str_format(str_t str, char *format, ...)
{
va_list args;
int len = 0; /* length of sub string */
char *s = NULL; /* temp string */
str_info_t info; /* str info */
double dbl = 0.0; /* double precision floating point */
char *begin = NULL; /* begin of format */
char qualifier = 0; /* conversion qualifier for integer as 'h','l','L' */
int width = 0; /* transition field width */
int precision = 0; /* minimum digits of integers and maximum digits of strings */
char tfmt[16] = "%"; /* temporary format */
/* Input value validity check */
if (!str) return NULL;
/* Reset str */
str_assign(str, "");
va_start(args, format);
/* Traverse format string */
for (; *format; format++)
{
if (*format != '%')
{
if (!begin) begin = format;
continue;
}
if (begin)
{
len = format - begin;
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
while (len--) str->base[str->length++] = *begin++;
begin = NULL;
}
begin = format;
while (1)
{
/* skips the first '%' also */
format++;
if ((*format != '0') &&
(*format != ' ') &&
(*format != '+') &&
(*format != '-') &&
(*format != '#')) break;
}
/* get field width */
width = -1;
if (is_digit(*format))
{
format += get_digit(format, &width);
}
else if (*format == '*')
{
format++;
width = va_arg(args, int);
if (width < 0) width = -width;
}
/* get the precision */
precision = -1;
if (*format == '.')
{
format++;
if (is_digit(*format)) format += get_digit(format, &precision);
else if (*format == '*')
{
format++;
precision = va_arg(args, int);
}
if (precision < 0) precision = 0;
}
/* get the conversion qualifier */
qualifier = 0;
if (*format == 'h' || *format == 'l' || *format == 'L')
{
qualifier = *format;
format++;
if (qualifier == 'l' && *format == 'l')
{
qualifier = 'L';
format++;
}
}
/* format distribution */
switch (*format)
{
case 'c':
len = MAX(width, 1);
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
memcpy(&tfmt[1], begin + 1, format - begin);
tfmt[format - begin + 1] = 0;
begin = NULL;
len = sprintf(&str->base[str->length], tfmt, va_arg(args, int));
if (len > 0) str->length += len;
break;
case 's':
info = string_info(va_arg(args, void*));
s = info.base;
len = info.length;
if (precision > 0 && len > precision) len = precision;
len = MAX(width, len);
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
memcpy(&tfmt[1], begin + 1, format - begin);
tfmt[format - begin + 1] = 0;
begin = NULL;
len = sprintf(&str->base[str->length], tfmt, s);
if (len > 0) str->length += len;
break;
case 'p':
if (width == -1) width = sizeof(void*) * 2; /* if no width is specified, the default width will be used */
len = width;
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
memcpy(&tfmt[1], begin + 1, format - begin);
tfmt[format - begin + 1] = 0;
begin = NULL;
len = sprintf(&str->base[str->length], tfmt, (long)va_arg(args, void*));
if (len > 0) str->length += len;
break;
/* float number formats */
case 'a':
case 'A':
case 'e':
case 'E':
case 'g':
case 'G':
case 'f':
dbl = va_arg(args, double);
if (*format == 'f')
{
/* get the gradient length of the integer part of the floating point number */
if (fabs(dbl) < powl(10, 8)) len = 8;
else if (fabs(dbl) < powl(10, 16)) len = 16;
else if (fabs(dbl) < powl(10, 32)) len = 32;
else if (fabs(dbl) < powl(10, 64)) len = 64;
else if (fabs(dbl) < powl(10, 128)) len = 128;
else if (fabs(dbl) < powl(10, 256)) len = 256;
else len = 309;
}
else len = 24;
len += ((precision==-1?6:precision) + 1);
len = MAX(width, len);
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
memcpy(&tfmt[1], begin + 1, format - begin);
tfmt[format - begin + 1] = 0;
begin = NULL;
len = sprintf(&str->base[str->length], tfmt, dbl);
if (len > 0) str->length += len;
break;
/* integer number formats */
case 'o':
case 'X':
case 'x':
case 'd':
case 'i':
case 'u':
len = qualifier=='L'?32:16;
len = MAX(width, len);
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
memcpy(&tfmt[1], begin + 1, format - begin);
tfmt[format - begin + 1] = 0;
begin = NULL;
/* format conversion */
if (qualifier == 'L') len = sprintf(&str->base[str->length], tfmt, va_arg(args, long long));
else if (qualifier == 'l') len = sprintf(&str->base[str->length], tfmt, va_arg(args, int));
else if (qualifier == 'h') len = sprintf(&str->base[str->length], tfmt, (short)va_arg(args, int));
else len = sprintf(&str->base[str->length], tfmt, (int)va_arg(args, int));
if (len > 0) str->length += len;
break;
case '%':
str_push_back(str, '%'); /* push % */
begin = NULL;
break;
default:
str_push_back(str, '%'); /* push % */
begin = NULL;
if (*format) str_push_back(str, *format);
else format--;
break;
}
}
/* copy tail string to str */
if (begin)
{
len = format - begin;
if (!str_alter_capacity(str, str->length + len)) goto FAIL_CAPACITY;
while (len--) str->base[str->length++] = *begin++;
}
if (!str_alter_capacity(str, str->length)) goto FAIL_CAPACITY;
str->base[str->length] = '\0';
va_end(args);
return str;
FAIL_CAPACITY:
str->base[str->length] = '\0';
va_end(args);
return NULL;
}