flipperzero-firmware/furi/core/string.h
Sergey Gavrilov 4bf29827f8
M*LIB: non-inlined strings, FuriString primitive (#1795)
* Quicksave 1
* Header stage complete
* Source stage complete
* Lint & merge fixes
* Includes
* Documentation step 1
* FBT: output free size considering BT STACK
* Documentation step 2
* py lint
* Fix music player plugin
* unit test stage 1: string allocator, mem, getters, setters, appends, compare, search.
* unit test: string equality
* unit test: string replace
* unit test: string start_with, end_with
* unit test: string trim
* unit test: utf-8
* Rename
* Revert fw_size changes
* Simplify CLI backspace handling
* Simplify CLI character insert
* Merge fixes
* Furi: correct filenaming and spelling
* Bt: remove furi string include

Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
2022-10-06 00:15:23 +09:00

738 lines
21 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* @file string.h
* Furi string primitive
*/
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <stdarg.h>
#include <m-core.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Furi string failure constant.
*/
#define FURI_STRING_FAILURE ((size_t)-1)
/**
* @brief Furi string primitive.
*/
typedef struct FuriString FuriString;
//---------------------------------------------------------------------------
// Constructors
//---------------------------------------------------------------------------
/**
* @brief Allocate new FuriString.
* @return FuriString*
*/
FuriString* furi_string_alloc();
/**
* @brief Allocate new FuriString and set it to string.
* Allocate & Set the string a to the string.
* @param source
* @return FuriString*
*/
FuriString* furi_string_alloc_set(const FuriString* source);
/**
* @brief Allocate new FuriString and set it to C string.
* Allocate & Set the string a to the C string.
* @param cstr_source
* @return FuriString*
*/
FuriString* furi_string_alloc_set_str(const char cstr_source[]);
/**
* @brief Allocate new FuriString and printf to it.
* Initialize and set a string to the given formatted value.
* @param format
* @param ...
* @return FuriString*
*/
FuriString* furi_string_alloc_printf(const char format[], ...);
/**
* @brief Allocate new FuriString and printf to it.
* Initialize and set a string to the given formatted value.
* @param format
* @param args
* @return FuriString*
*/
FuriString* furi_string_alloc_vprintf(const char format[], va_list args);
/**
* @brief Allocate new FuriString and move source string content to it.
* Allocate the string, set it to the other one, and destroy the other one.
* @param source
* @return FuriString*
*/
FuriString* furi_string_alloc_move(FuriString* source);
//---------------------------------------------------------------------------
// Destructors
//---------------------------------------------------------------------------
/**
* @brief Free FuriString.
* @param string
*/
void furi_string_free(FuriString* string);
//---------------------------------------------------------------------------
// String memory management
//---------------------------------------------------------------------------
/**
* @brief Reserve memory for string.
* Modify the string capacity to be able to handle at least 'alloc' characters (including final null char).
* @param string
* @param size
*/
void furi_string_reserve(FuriString* string, size_t size);
/**
* @brief Reset string.
* Make the string empty.
* @param s
*/
void furi_string_reset(FuriString* string);
/**
* @brief Swap two strings.
* Swap the two strings string_1 and string_2.
* @param string_1
* @param string_2
*/
void furi_string_swap(FuriString* string_1, FuriString* string_2);
/**
* @brief Move string_2 content to string_1.
* Set the string to the other one, and destroy the other one.
* @param string_1
* @param string_2
*/
void furi_string_move(FuriString* string_1, FuriString* string_2);
/**
* @brief Compute a hash for the string.
* @param string
* @return size_t
*/
size_t furi_string_hash(const FuriString* string);
/**
* @brief Get string size (usually length, but not for UTF-8)
* @param string
* @return size_t
*/
size_t furi_string_size(const FuriString* string);
/**
* @brief Check that string is empty or not
* @param string
* @return bool
*/
bool furi_string_empty(const FuriString* string);
//---------------------------------------------------------------------------
// Getters
//---------------------------------------------------------------------------
/**
* @brief Get the character at the given index.
* Return the selected character of the string.
* @param string
* @param index
* @return char
*/
char furi_string_get_char(const FuriString* string, size_t index);
/**
* @brief Return the string view a classic C string.
* @param string
* @return const char*
*/
const char* furi_string_get_cstr(const FuriString* string);
//---------------------------------------------------------------------------
// Setters
//---------------------------------------------------------------------------
/**
* @brief Set the string to the other string.
* Set the string to the source string.
* @param string
* @param source
*/
void furi_string_set(FuriString* string, FuriString* source);
/**
* @brief Set the string to the other C string.
* Set the string to the source C string.
* @param string
* @param source
*/
void furi_string_set_str(FuriString* string, const char source[]);
/**
* @brief Set the string to the n first characters of the C string.
* @param string
* @param source
* @param length
*/
void furi_string_set_strn(FuriString* string, const char source[], size_t length);
/**
* @brief Set the character at the given index.
* @param string
* @param index
* @param c
*/
void furi_string_set_char(FuriString* string, size_t index, const char c);
/**
* @brief Set the string to the n first characters of other one.
* @param string
* @param source
* @param offset
* @param length
*/
void furi_string_set_n(FuriString* string, const FuriString* source, size_t offset, size_t length);
/**
* @brief Format in the string the given printf format
* @param string
* @param format
* @param ...
* @return int
*/
int furi_string_printf(FuriString* string, const char format[], ...);
/**
* @brief Format in the string the given printf format
* @param string
* @param format
* @param args
* @return int
*/
int furi_string_vprintf(FuriString* string, const char format[], va_list args);
//---------------------------------------------------------------------------
// Appending
//---------------------------------------------------------------------------
/**
* @brief Append a character to the string.
* @param string
* @param c
*/
void furi_string_push_back(FuriString* string, char c);
/**
* @brief Append a string to the string.
* Concatenate the string with the other string.
* @param string_1
* @param string_2
*/
void furi_string_cat(FuriString* string_1, const FuriString* string_2);
/**
* @brief Append a C string to the string.
* Concatenate the string with the C string.
* @param string_1
* @param cstring_2
*/
void furi_string_cat_str(FuriString* string_1, const char cstring_2[]);
/**
* @brief Append to the string the formatted string of the given printf format.
* @param string
* @param format
* @param ...
* @return int
*/
int furi_string_cat_printf(FuriString* string, const char format[], ...);
/**
* @brief Append to the string the formatted string of the given printf format.
* @param string
* @param format
* @param args
* @return int
*/
int furi_string_cat_vprintf(FuriString* string, const char format[], va_list args);
//---------------------------------------------------------------------------
// Comparators
//---------------------------------------------------------------------------
/**
* @brief Compare two strings and return the sort order.
* @param string_1
* @param string_2
* @return int
*/
int furi_string_cmp(const FuriString* string_1, const FuriString* string_2);
/**
* @brief Compare string with C string and return the sort order.
* @param string_1
* @param cstring_2
* @return int
*/
int furi_string_cmp_str(const FuriString* string_1, const char cstring_2[]);
/**
* @brief Compare two strings (case insensitive according to the current locale) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* @param string_1
* @param string_2
* @return int
*/
int furi_string_cmpi(const FuriString* string_1, const FuriString* string_2);
/**
* @brief Compare string with C string (case insensitive according to the current locale) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* @param string_1
* @param cstring_2
* @return int
*/
int furi_string_cmpi_str(const FuriString* string_1, const char cstring_2[]);
//---------------------------------------------------------------------------
// Search
//---------------------------------------------------------------------------
/**
* @brief Search the first occurrence of the needle in the string from the position start.
* Return STRING_FAILURE if not found.
* By default, start is zero.
* @param string
* @param needle
* @param start
* @return size_t
*/
size_t furi_string_search(const FuriString* string, const FuriString* needle, size_t start);
/**
* @brief Search the first occurrence of the needle in the string from the position start.
* Return STRING_FAILURE if not found.
* @param string
* @param needle
* @param start
* @return size_t
*/
size_t furi_string_search_str(const FuriString* string, const char needle[], size_t start);
/**
* @brief Search for the position of the character c from the position start (include) in the string.
* Return STRING_FAILURE if not found.
* By default, start is zero.
* @param string
* @param c
* @param start
* @return size_t
*/
size_t furi_string_search_char(const FuriString* string, char c, size_t start);
/**
* @brief Reverse search for the position of the character c from the position start (include) in the string.
* Return STRING_FAILURE if not found.
* By default, start is zero.
* @param string
* @param c
* @param start
* @return size_t
*/
size_t furi_string_search_rchar(const FuriString* string, char c, size_t start);
//---------------------------------------------------------------------------
// Equality
//---------------------------------------------------------------------------
/**
* @brief Test if two strings are equal.
* @param string_1
* @param string_2
* @return bool
*/
bool furi_string_equal(const FuriString* string_1, const FuriString* string_2);
/**
* @brief Test if the string is equal to the C string.
* @param string_1
* @param cstring_2
* @return bool
*/
bool furi_string_equal_str(const FuriString* string_1, const char cstring_2[]);
//---------------------------------------------------------------------------
// Replace
//---------------------------------------------------------------------------
/**
* @brief Replace in the string the sub-string at position 'pos' for 'len' bytes into the C string 'replace'.
* @param string
* @param pos
* @param len
* @param replace
*/
void furi_string_replace_at(FuriString* string, size_t pos, size_t len, const char replace[]);
/**
* @brief Replace a string 'needle' to string 'replace' in a string from 'start' position.
* By default, start is zero.
* Return STRING_FAILURE if 'needle' not found or replace position.
* @param string
* @param needle
* @param replace
* @param start
* @return size_t
*/
size_t
furi_string_replace(FuriString* string, FuriString* needle, FuriString* replace, size_t start);
/**
* @brief Replace a C string 'needle' to C string 'replace' in a string from 'start' position.
* By default, start is zero.
* Return STRING_FAILURE if 'needle' not found or replace position.
* @param string
* @param needle
* @param replace
* @param start
* @return size_t
*/
size_t furi_string_replace_str(
FuriString* string,
const char needle[],
const char replace[],
size_t start);
/**
* @brief Replace all occurrences of 'needle' string into 'replace' string.
* @param string
* @param needle
* @param replace
*/
void furi_string_replace_all(
FuriString* string,
const FuriString* needle,
const FuriString* replace);
/**
* @brief Replace all occurrences of 'needle' C string into 'replace' C string.
* @param string
* @param needle
* @param replace
*/
void furi_string_replace_all_str(FuriString* string, const char needle[], const char replace[]);
//---------------------------------------------------------------------------
// Start / End tests
//---------------------------------------------------------------------------
/**
* @brief Test if the string starts with the given string.
* @param string
* @param start
* @return bool
*/
bool furi_string_start_with(const FuriString* string, const FuriString* start);
/**
* @brief Test if the string starts with the given C string.
* @param string
* @param start
* @return bool
*/
bool furi_string_start_with_str(const FuriString* string, const char start[]);
/**
* @brief Test if the string ends with the given string.
* @param string
* @param end
* @return bool
*/
bool furi_string_end_with(const FuriString* string, const FuriString* end);
/**
* @brief Test if the string ends with the given C string.
* @param string
* @param end
* @return bool
*/
bool furi_string_end_with_str(const FuriString* string, const char end[]);
//---------------------------------------------------------------------------
// Trim
//---------------------------------------------------------------------------
/**
* @brief Trim the string left to the first 'index' bytes.
* @param string
* @param index
*/
void furi_string_left(FuriString* string, size_t index);
/**
* @brief Trim the string right from the 'index' position to the last position.
* @param string
* @param index
*/
void furi_string_right(FuriString* string, size_t index);
/**
* @brief Trim the string from position index to size bytes.
* See also furi_string_set_n.
* @param string
* @param index
* @param size
*/
void furi_string_mid(FuriString* string, size_t index, size_t size);
/**
* @brief Trim a string from the given set of characters (default is " \n\r\t").
* @param string
* @param chars
*/
void furi_string_trim(FuriString* string, const char chars[]);
//---------------------------------------------------------------------------
// UTF8
//---------------------------------------------------------------------------
/**
* @brief An unicode value.
*/
typedef unsigned int FuriStringUnicodeValue;
/**
* @brief Compute the length in UTF8 characters in the string.
* @param string
* @return size_t
*/
size_t furi_string_utf8_length(FuriString* string);
/**
* @brief Push unicode into string, encoding it in UTF8.
* @param string
* @param unicode
*/
void furi_string_utf8_push(FuriString* string, FuriStringUnicodeValue unicode);
/**
* @brief State of the UTF8 decoding machine state.
*/
typedef enum {
FuriStringUTF8StateStarting,
FuriStringUTF8StateDecoding1,
FuriStringUTF8StateDecoding2,
FuriStringUTF8StateDecoding3,
FuriStringUTF8StateError
} FuriStringUTF8State;
/**
* @brief Main generic UTF8 decoder.
* It takes a character, and the previous state and the previous value of the unicode value.
* It updates the state and the decoded unicode value.
* A decoded unicode encoded value is valid only when the state is FuriStringUTF8StateStarting.
* @param c
* @param state
* @param unicode
*/
void furi_string_utf8_decode(char c, FuriStringUTF8State* state, FuriStringUnicodeValue* unicode);
//---------------------------------------------------------------------------
// Lasciate ogne speranza, voi chentrate
//---------------------------------------------------------------------------
/**
*
* Select either the string function or the str function depending on
* the b operand to the function.
* func1 is the string function / func2 is the str function.
*/
/**
* @brief Select for 1 argument
*/
#define FURI_STRING_SELECT1(func1, func2, a) \
_Generic((a), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a)
/**
* @brief Select for 2 arguments
*/
#define FURI_STRING_SELECT2(func1, func2, a, b) \
_Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b)
/**
* @brief Select for 3 arguments
*/
#define FURI_STRING_SELECT3(func1, func2, a, b, c) \
_Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c)
/**
* @brief Select for 4 arguments
*/
#define FURI_STRING_SELECT4(func1, func2, a, b, c, d) \
_Generic((b), char* : func2, const char* : func2, FuriString* : func1, const FuriString* : func1)(a, b, c, d)
/**
* @brief Allocate new FuriString and set it content to string (or C string).
* ([c]string)
*/
#define furi_string_alloc_set(a) \
FURI_STRING_SELECT1(furi_string_alloc_set, furi_string_alloc_set_str, a)
/**
* @brief Set the string content to string (or C string).
* (string, [c]string)
*/
#define furi_string_set(a, b) FURI_STRING_SELECT2(furi_string_set, furi_string_set_str, a, b)
/**
* @brief Compare string with string (or C string) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* (string, [c]string)
*/
#define furi_string_cmp(a, b) FURI_STRING_SELECT2(furi_string_cmp, furi_string_cmp_str, a, b)
/**
* @brief Compare string with string (or C string) (case insensitive according to the current locale) and return the sort order.
* Note: doesn't work with UTF-8 strings.
* (string, [c]string)
*/
#define furi_string_cmpi(a, b) FURI_STRING_SELECT2(furi_string_cmpi, furi_string_cmpi_str, a, b)
/**
* @brief Test if the string is equal to the string (or C string).
* (string, [c]string)
*/
#define furi_string_equal(a, b) FURI_STRING_SELECT2(furi_string_equal, furi_string_equal_str, a, b)
/**
* @brief Replace all occurrences of string into string (or C string to another C string) in a string.
* (string, [c]string, [c]string)
*/
#define furi_string_replace_all(a, b, c) \
FURI_STRING_SELECT3(furi_string_replace_all, furi_string_replace_all_str, a, b, c)
/**
* @brief Search for a string (or C string) in a string
* (string, [c]string[, start=0])
*/
#define furi_string_search(v, ...) \
M_APPLY( \
FURI_STRING_SELECT3, \
furi_string_search, \
furi_string_search_str, \
v, \
M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief Search for a C string in a string
* (string, cstring[, start=0])
*/
#define furi_string_search_str(v, ...) \
M_APPLY(furi_string_search_str, v, M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief Test if the string starts with the given string (or C string).
* (string, [c]string)
*/
#define furi_string_start_with(a, b) \
FURI_STRING_SELECT2(furi_string_start_with, furi_string_start_with_str, a, b)
/**
* @brief Test if the string ends with the given string (or C string).
* (string, [c]string)
*/
#define furi_string_end_with(a, b) \
FURI_STRING_SELECT2(furi_string_end_with, furi_string_end_with_str, a, b)
/**
* @brief Append a string (or C string) to the string.
* (string, [c]string)
*/
#define furi_string_cat(a, b) FURI_STRING_SELECT2(furi_string_cat, furi_string_cat_str, a, b)
/**
* @brief Trim a string from the given set of characters (default is " \n\r\t").
* (string[, set=" \n\r\t"])
*/
#define furi_string_trim(...) M_APPLY(furi_string_trim, M_IF_DEFAULT1(" \n\r\t", __VA_ARGS__))
/**
* @brief Search for a character in a string.
* (string, character[, start=0])
*/
#define furi_string_search_char(v, ...) \
M_APPLY(furi_string_search_char, v, M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief Reverse Search for a character in a string.
* (string, character[, start=0])
*/
#define furi_string_search_rchar(v, ...) \
M_APPLY(furi_string_search_rchar, v, M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief Replace a string to another string (or C string to another C string) in a string.
* (string, [c]string, [c]string[, start=0])
*/
#define furi_string_replace(a, b, ...) \
M_APPLY( \
FURI_STRING_SELECT4, \
furi_string_replace, \
furi_string_replace_str, \
a, \
b, \
M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief Replace a C string to another C string in a string.
* (string, cstring, cstring[, start=0])
*/
#define furi_string_replace_str(a, b, ...) \
M_APPLY(furi_string_replace_str, a, b, M_IF_DEFAULT1(0, __VA_ARGS__))
/**
* @brief INIT OPLIST for FuriString.
*/
#define F_STR_INIT(a) ((a) = furi_string_alloc())
/**
* @brief INIT SET OPLIST for FuriString.
*/
#define F_STR_INIT_SET(a, b) ((a) = furi_string_alloc_set(b))
/**
* @brief OPLIST for FuriString.
*/
#define FURI_STRING_OPLIST \
(INIT(F_STR_INIT), \
INIT_SET(F_STR_INIT_SET), \
SET(furi_string_set), \
INIT_MOVE(furi_string_alloc_move), \
MOVE(furi_string_move), \
SWAP(furi_string_swap), \
RESET(furi_string_reset), \
EMPTY_P(furi_string_empty), \
CLEAR(furi_string_free), \
HASH(furi_string_hash), \
EQUAL(furi_string_equal), \
CMP(furi_string_cmp), \
TYPE(FuriString*))
#ifdef __cplusplus
}
#endif