p2p/stingray_sdk/plugin_foundation/string.h
Lucas Schwiderski 2c9ce46dd2
chore: Rework project structure
There likely won't be much need for multiple separate crates.
2023-05-26 23:42:01 +02:00

322 lines
13 KiB
C++

#pragma once
#include "allocator.h"
#include "assert.h"
#include "array.h"
#include <string.h>
#include <stdlib.h>
#include <limits.h>
namespace stingray_plugin_foundation {
template <class T> class Vector;
// ----------------------------------------------------------------------
// Functions that extend the normal ANSI C string interface.
// ----------------------------------------------------------------------
// Returns the length of the string a 32-bit number (unsigned rather
// than size_t).
inline unsigned strlen32(const char *s);
// Returns the length of the string a 32-bit number (unsigned rather
// than size_t).
inline unsigned strlenw32(const wchar_t *s);
// Returns true if strings s1 and s2 are identical.
inline bool strequal(const char *s1, const char *s2);
// Returns true if strings s1 and s2 are identical when ignoring case.
// Only ascii characters (A-Z) are considered when comparing case.
bool strequali(const char *s1, const char *s2);
// Returns true if string s is empty.
inline bool strempty(const char *s);
// Converts s to an integer and returns the result.
inline int to_int(const char *s);
// Converts s to an unsigned and returns the result.
inline unsigned to_unsigned(const char *s);
// Converts s to a float and returns the result.
inline float to_float(const char *s);
// Converts s to an integer and returns the result.
inline int to_int(const char *s, bool &error);
// Converts s to an unsigned and returns the result.
inline unsigned to_unsigned(const char *s, bool &error);
// Converts s to a float and returns the result.
inline float to_float(const char *s, bool &error);
// ----------------------------------------------------------------------
// A thin wrapper around a const char * that just adds a == operator for string
// comparison. Note especially that it doesn't make a copy of the string. The
// original string pointer must be valid as long as this class is used.
//
// Typically, this class is used to make string comparisons easier to read
// and prevent mistakes. An example:
//
// ConstString s(p)
// if (s == "open")
// ...
class ConstString
{
public:
// ConstString that wraps the empty string.
ConstString() : s("") {}
// ConstString that wraps the specified string.
ConstString(const char *s_) : s(s_) {}
// Returns the raw string pointer.
const char *c_str() const {return s;}
// Returns true if the string is empty.
bool empty() const {return *s == 0;}
// Returns the length of the string
unsigned size() const {return strlen32(s);}
const char *s;
};
// Comparison operators for ConstString.
inline bool operator==(const ConstString &s1, const char *s2) {return strcmp(s1.s, s2) == 0;}
inline bool operator==(const char *s1, const ConstString &s2) {return strcmp(s1, s2.s) == 0;}
inline bool operator==(const ConstString &s1, const ConstString &s2) {return strcmp(s1.s, s2.s) == 0;}
inline bool operator!=(const ConstString &s1, const char *s2) {return strcmp(s1.s, s2) != 0;}
inline bool operator!=(const char *s1, const ConstString &s2) {return strcmp(s1, s2.s) != 0;}
inline bool operator!=(const ConstString &s1, const ConstString &s2) {return strcmp(s1.s, s2.s) != 0;}
inline bool operator<(const ConstString &s1, const char *s2) {return strcmp(s1.s, s2) < 0;}
inline bool operator<(const char *s1, const ConstString &s2) {return strcmp(s1, s2.s) < 0;}
inline bool operator<(const ConstString &s1, const ConstString &s2) {return strcmp(s1.s, s2.s) < 0;}
// ----------------------------------------------------------------------
// Class that represents a dynamic string -- a string whose content can be modified.
// This class is basically just a thin wrapper around an Array<char> buffer that stores
// the content of the string.
class DynamicString
{
public:
ALLOCATOR_AWARE;
// Creates a new dynamic string using the specified allocator.
DynamicString(Allocator &a) : _buffer(a) {}
// Creates a new dynamic string initialized from s.
DynamicString(Allocator &a, const char *s) : _buffer(a) { unsigned length = s ? strlen32(s) : 0; if (length > 0) { _buffer.resize(length + 1); memmove(_buffer.begin(), s, _buffer.size()); } }
// Creates a new dynamic string initialized from the first n characters of s.
DynamicString(Allocator &a, const char *s, unsigned n) : _buffer(a) { if (n > 0) { _buffer.resize(n + 1); memmove(_buffer.begin(), s, n); _buffer[n] = 0; } }
// Copy constructor
DynamicString(const DynamicString &o) : _buffer(o._buffer) {}
// Assignment.
void operator=(const char *s) { unsigned length = strlen32(s); if (length > 0) { _buffer.resize(length + 1); memmove(_buffer.begin(), s, _buffer.size()); } else _buffer.clear(); }
// Assigns the characters from ds to this string. Does not change the allocator.
void operator=(const DynamicString &ds) { _buffer = ds._buffer; }
// Returns the size/length of the string. The size does not include the terminating zero.
unsigned size() const {return _buffer.empty() ? 0 : _buffer.size() - 1;}
// Returns true if the string is empty.
bool empty() const {return size() == 0;}
// Returns the C-string held by this dynamic string.
char *c_str() { return _buffer.empty() ? empty_string() : _buffer.begin(); }
const char *c_str() const {return _buffer.empty() ? empty_string() : _buffer.begin();}
// Returns a pointer to the terminating zero at the end of C-string.
char *end() { return _buffer.empty() ? c_str() : _buffer.end() - 1; }
const char *end() const { return _buffer.empty() ? c_str() : _buffer.end() - 1; }
// Accesses characters in the string.
char &operator[](unsigned i) { return c_str()[i];}
const char &operator[](unsigned i) const { return c_str()[i]; }
// Resizes the string to the specified size (size does not include terminating zero).
void resize(unsigned size) { bool empty = _buffer.empty(); _buffer.resize(size + 1); if (empty) _buffer[0] = '\0'; _buffer[size] = '\0'; }
// Extends the string with the specified number of bytes.
void extend(unsigned bytes) {resize(size() + bytes);}
// Clears the memory and sets this string to the empty string.
void clear() { _buffer.clear(); }
// Returns the allocator of the string.
Allocator &allocator() const {return _buffer.allocator();}
// Swaps the contents efficiently.
void swap(DynamicString &other);
// Serializes the string to the stream
template <class STREAM> void serialize(STREAM &s) {
unsigned sz = size();
s & sz;
if (sz != size())
resize(sz);
if (sz != 0) {
if (s.is_output())
s.write(_buffer.begin(), sz);
else
s.read(_buffer.begin(), sz);
}
}
Array<char> &buffer() { if (_buffer.empty()) _buffer.push_back(0); return _buffer; }
private:
// Raw access to the string buffer.
Array<char> _buffer;
static char *empty_string() { static char c = '\0'; return &c; }
};
// Comparison operators
inline bool operator==(const DynamicString &s1, const char *s2) {return strcmp(s1.c_str(), s2) == 0;}
inline bool operator==(const char *s1, const DynamicString &s2) {return strcmp(s1, s2.c_str()) == 0;}
inline bool operator==(const DynamicString &s1, const DynamicString &s2) {return strcmp(s1.c_str(), s2.c_str()) == 0;}
inline bool operator!=(const DynamicString &s1, const char *s2) {return strcmp(s1.c_str(), s2) != 0;}
inline bool operator!=(const char *s1, const DynamicString &s2) {return strcmp(s1, s2.c_str()) != 0;}
inline bool operator!=(const DynamicString &s1, const DynamicString &s2) {return strcmp(s1.c_str(), s2.c_str()) != 0;}
inline bool operator<(const DynamicString &s1, const char *s2) {return strcmp(s1.c_str(), s2) < 0;}
inline bool operator<(const char *s1, const DynamicString &s2) {return strcmp(s1, s2.c_str()) < 0;}
inline bool operator<(const DynamicString &s1, const DynamicString &s2) {return strcmp(s1.c_str(), s2.c_str()) < 0;}
// Appends `s` to the end of `str`.
inline void append(DynamicString &str, const char *s);
// Appends `c` to the end of `str`.
inline void append(DynamicString &str, char c);
// Appends `len` characters beginning from `s` to the end of `str`.
inline void append(DynamicString &str, const char *s, unsigned len);
// Inserts `s` in front of any characters in `str`.
inline void prefix(DynamicString &str, const char *s);
// ----------------------------------------------------------------------
// Namespace for string operations.
namespace string {
const unsigned npos = UINT_MAX;
// Returns true if c is a whitespace character.
bool is_whitespace(char c);
// Returns the index of the first occurrence of `c` in
// `s` or `npos` if not found.
unsigned find(const char *s, char c);
// Returns the index of the last occurrence of `c` in
// `s` or `npos` if not found.
unsigned find_last(const char *s, char c);
// Returns the index of the first occurrence of `substring` in
// `s` or `npos` if not found.
unsigned find(const char *s, const char *substring);
// Returns the index of the last occurrence of `substring` in
// `s` or `npos` if not found.
unsigned find_last(const char *s, const char *substring);
// Returns true if `s` contains `substring`.
bool contains(const char *s, const char *substring);
// Splits the string `s` on the first instance of `split_on` and stores the
// results in `first` and `second`. If `split_on` is not found, `first`
// will get the entire string.
void split(const char *s, const char *split_on, DynamicString &first, DynamicString &second);
// Splits the string `s` on all instances of `split_on` and stores the
// results in `result`
void split(const char *s, const char *split_by, Vector<DynamicString> &result);
enum SkipEmpty { SKIP_EMPTY, DONT_SKIP_EMPTY };
// Joins each string in `strings` into a single string separated by `separator` and stores the result in `result`.
void join(const DynamicString* strings, unsigned num_strings, const char* separator, DynamicString& result, SkipEmpty skip_empty = DONT_SKIP_EMPTY);
// Returns a pointer to a string with n spaces for indentation purposes. If n > 200 a string with 200 spaces
// will be returned.
const char *spaces(int n);
// Returns true if s is a lowercase string.
bool is_lowercase(const char *s);
// Replaces the character range (start, start+size) in the string s with the
// replacement rep.
void replace(DynamicString &s, unsigned start, unsigned size, const char *rep);
// Replaces the occurances of the string `find` with the replacement `rep` and returns the number of successful replacements made.
unsigned replace_all(DynamicString &s, const char *find, const char *rep);
// In-place converts the string to lower case
void to_lower_case(char *s);
// In-place converts the string to upper case
void to_upper_case(char *s);
// Converts from camel case to underscore representation.
DynamicString from_camel_case(const char *s, Allocator &a);
// Returns the string with the character stripped out.
DynamicString strip(const char *s, char c, Allocator &a);
// Trims whitespace from the start and end of the string and returns the result.
DynamicString trim(const char *s, Allocator &a);
// Returns a substring of s starting at start and of length.
DynamicString substring(const char *s, unsigned start, unsigned length, Allocator &a);
// Returns true if `str` contains `c`.
bool in_string(const char *str, char c);
// Returns true if `str` only contains characters in `allowed`.
bool consists_of(const char *str, const char *allowed);
// Returns true if `str` begins with `begins`.
bool begins_with(const char *str, const char *begins);
// Returns true if `str` ends with `ends`.
bool ends_with(const char *str, const char *ends);
bool ends_with(const wchar_t *str, const wchar_t *ends);
// Returns a copy of the string `str` allocated with the allocator `a`.
char* copy(const char *str, Allocator& a);
// Copies src to dest. Truncates the copy if necessary so that more than
// `dest_size` bytes are never written to `dest`. Always terminates
// dest with a \0 character. Utf-8 characters are guaranteed to not get
// truncated in the middle of a multibyte character.
void copy(char *dest, const char *src, unsigned dest_size);
void copy(wchar_t *dest, const wchar_t *src, unsigned dest_size);
// Appends `src` to the end of `dest`. Truncates `dest` if necessary so that
// more than `dest_size` bytes are never written to `dest`. Always terminates
// dest with a \0 character. Utf-8 characters are guaranteed to not get
// truncated in the middle of a multibyte character.
void append(char *dest, const char *src, unsigned dest_size);
void append(wchar_t *dest, const wchar_t *src, unsigned dest_size);
#ifdef DEVELOPMENT
// Returns a string allocated by `a` representing a typcial hex_view of the `data`.
// (Such as shown by a memory debugger, for instance.)
// `columns` is the number of columns of hex values in the view.
// If `ascii_column` is true a column that shows ascii representation is included.
DynamicString hex_view(Allocator &a, const char *data, unsigned size, unsigned columns = 16, bool ascii_column = true);
#endif
} // namespace string
}
#include "string.inl"