openmohaa/code/qcommon/str.h

669 lines
13 KiB
C
Raw Normal View History

2016-03-27 11:49:47 +02:00
/*
===========================================================================
Copyright (C) 2015 the OpenMoHAA team
This file is part of OpenMoHAA source code.
OpenMoHAA source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
OpenMoHAA source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with OpenMoHAA source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
// str.cpp: Simple, DLL portable string class
//
#pragma once
2016-03-27 11:49:47 +02:00
2023-02-01 00:28:40 +01:00
#include <cassert>
#include <cstring>
#include <cstdio>
#include <cstdint>
#include <cinttypes>
2016-03-27 11:49:47 +02:00
#ifdef _WIN32
2023-07-05 21:23:39 +02:00
# pragma warning(disable : 4710) // function 'blah' not inlined
2016-03-27 11:49:47 +02:00
#endif
2023-07-05 21:23:39 +02:00
void TestStringClass();
2016-03-27 11:49:47 +02:00
class strdata
2023-07-05 21:23:39 +02:00
{
public:
strdata()
: len(0)
, refcount(0)
, data(NULL)
, alloced(0)
{}
~strdata()
{
if (data) {
delete[] data;
}
}
void AddRef() { refcount++; }
bool DelRef() // True if killed
{
refcount--;
if (refcount < 0) {
2016-03-27 11:49:47 +02:00
delete this;
return true;
2023-07-05 21:23:39 +02:00
}
return false;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
char *data;
int refcount;
size_t alloced;
size_t len;
};
2016-03-27 11:49:47 +02:00
class str
2023-07-05 21:23:39 +02:00
{
protected:
friend class Archiver;
strdata *m_data;
void EnsureAlloced(size_t, bool keepold = true);
void EnsureDataWritable();
public:
~str();
str();
str(const char *text);
str(const str& string);
str(const str& string, size_t start, size_t end);
str(const char ch);
str(const int num);
str(const float num);
str(const unsigned int num);
str(const long num);
str(const unsigned long num);
str(const long long num);
str(const unsigned long long num);
2023-07-05 21:23:39 +02:00
size_t length(void) const;
const char *c_str(void) const;
void append(const char *text);
void append(const str& text);
char operator[](intptr_t index) const;
char& operator[](intptr_t index);
void operator=(const str& text);
void operator=(const char *text);
friend str operator+(const str& a, const str& b);
friend str operator+(const str& a, const char *b);
friend str operator+(const char *a, const str& b);
friend str operator+(const str& a, const float b);
friend str operator+(const str& a, const int b);
friend str operator+(const str& a, const unsigned b);
friend str operator+(const str& a, const bool b);
friend str operator+(const str& a, const char b);
str& operator+=(const str& a);
str& operator+=(const char *a);
str& operator+=(const float a);
str& operator+=(const char a);
str& operator+=(const int a);
str& operator+=(const unsigned a);
str& operator+=(const bool a);
str& operator-=(int c);
str& operator--(int);
friend bool operator==(const str& a, const str& b);
friend bool operator==(const str& a, const char *b);
friend bool operator==(const char *a, const str& b);
friend bool operator!=(const str& a, const str& b);
friend bool operator!=(const str& a, const char *b);
friend bool operator!=(const char *a, const str& b);
operator const char *() const;
int icmpn(const char *text, size_t n) const;
int icmpn(const str& text, size_t n) const;
int icmp(const char *text) const;
int icmp(const str& text) const;
int cmpn(const char *text, size_t n) const;
int cmpn(const str& text, size_t n) const;
void tolower(void);
void toupper(void);
static char *tolower(char *s1);
static char *toupper(char *s1);
static int icmpn(const char *s1, const char *s2, size_t n);
static int icmp(const char *s1, const char *s2);
static int cmpn(const char *s1, const char *s2, size_t n);
static int cmp(const char *s1, const char *s2);
static void snprintf(char *dst, int size, const char *fmt, ...);
void strip(void);
static bool isNumeric(const char *str);
bool isNumeric(void) const;
void CapLength(size_t newlen);
void BackSlashesToSlashes();
void SlashesToBackSlashes();
void DefaultExtension(const char *extension);
const char *GetExtension() const;
void StripExtension();
void SkipFile();
void SkipPath();
};
char *strstrip(char *string);
char *strlwc(char *string);
inline char str::operator[](intptr_t index) const
{
assert(m_data);
if (!m_data) {
return 0;
}
// don't include the '/0' in the test, because technically, it's out of bounds
assert((index >= 0) && (index < (int)m_data->len));
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
// In release mode, give them a null character
// don't include the '/0' in the test, because technically, it's out of bounds
if ((index < 0) || (index >= (int)m_data->len)) {
return 0;
}
return m_data->data[index];
}
inline size_t str::length(void) const
{
return (m_data != NULL) ? m_data->len : 0;
}
2016-03-27 11:49:47 +02:00
inline str::~str()
{
2023-07-05 21:23:39 +02:00
if (m_data) {
m_data->DelRef();
m_data = NULL;
}
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline str::str()
: m_data(NULL)
{}
inline str::str(const char *text)
: m_data(NULL)
{
size_t len;
assert(text);
if (*text) {
len = strlen(text);
if (len) {
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
}
}
}
inline str::str(const str& text)
: m_data(NULL)
{
if (text.m_data) {
text.m_data->AddRef();
}
if (m_data) {
m_data->DelRef();
}
m_data = text.m_data;
}
inline str::str(const str& text, size_t start, size_t end)
: m_data(NULL)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
size_t i;
size_t len;
if (end > text.length()) {
end = text.length();
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
if (start > text.length()) {
start = text.length();
}
if (end >= start) {
len = end - start;
} else {
len = 0;
}
if (len > 0) {
EnsureAlloced(len + 1);
for (i = 0; i < len; i++) {
m_data->data[i] = text[start + i];
}
m_data->data[len] = 0;
m_data->len = len;
}
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline str::str(const char ch)
: m_data(NULL)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
EnsureAlloced(2);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
m_data->data[0] = ch;
m_data->data[1] = 0;
m_data->len = 1;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str::str(const float num)
: m_data(NULL)
{
char text[32];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%.3f", num);
2023-07-05 21:23:39 +02:00
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline str::str(const int num)
: m_data(NULL)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
char text[32];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%d", num);
2023-07-05 21:23:39 +02:00
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
}
2016-03-27 11:49:47 +02:00
inline str::str(const unsigned int num)
2023-07-05 21:23:39 +02:00
: m_data(NULL)
{
char text[32];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%u", num);
2023-07-05 21:23:39 +02:00
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
2023-10-27 19:58:29 +02:00
m_data->len = len;
}
inline str::str(const long num)
2023-10-27 19:58:29 +02:00
: m_data(NULL)
{
char text[64];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%ld", num);
2023-10-27 19:58:29 +02:00
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
}
inline str::str(const unsigned long num)
2023-10-27 19:58:29 +02:00
: m_data(NULL)
{
char text[64];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%lu", num);
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
}
inline str::str(const long long num)
: m_data(NULL)
{
char text[64];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%lld", num);
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
m_data->len = len;
}
inline str::str(const unsigned long long num)
: m_data(NULL)
{
char text[64];
size_t len;
2024-09-20 21:53:48 +02:00
snprintf(text, sizeof(text), "%llu", num);
2023-10-27 19:58:29 +02:00
len = strlen(text);
EnsureAlloced(len + 1);
strcpy(m_data->data, text);
2023-07-05 21:23:39 +02:00
m_data->len = len;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline const char *str::c_str(void) const
{
if (m_data) {
return m_data->data;
} else {
return "";
}
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline void str::append(const char *text)
{
2023-07-05 21:23:39 +02:00
size_t len;
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
assert(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
if (*text) {
len = length();
len += strlen(text);
EnsureAlloced(len + 1);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
strcat(m_data->data, text);
m_data->len = len;
}
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline void str::append(const str& text)
{
append(text.c_str());
}
2023-07-05 21:23:39 +02:00
inline char& str::operator[](intptr_t index)
{
// Used for result for invalid indices
static char dummy = 0;
assert(m_data);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
// We don't know if they'll write to it or not
// if it's not a const object
EnsureDataWritable();
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
if (!m_data) {
return dummy;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
// don't include the '/0' in the test, because technically, it's out of bounds
assert((index >= 0) && (index < (int)m_data->len));
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
// In release mode, let them change a safe variable
// don't include the '/0' in the test, because technically, it's out of bounds
if ((index < 0) || (index >= (int)m_data->len)) {
return dummy;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return m_data->data[index];
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline void str::operator=(const str& text)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
// adding the reference before deleting our current reference prevents
// us from deleting our string if we are copying from ourself
if (text.m_data) {
text.m_data->AddRef();
}
if (m_data) {
m_data->DelRef();
}
m_data = text.m_data;
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline void str::operator=(const char *text)
{
2023-07-05 21:23:39 +02:00
size_t len;
assert(text);
if (m_data) {
if (text == m_data->data) {
return; // Copying same thing. Punt.
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
m_data->DelRef();
m_data = NULL;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
if (*text) {
len = strlen(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
m_data = new strdata;
m_data->len = len;
m_data->alloced = len + 1;
m_data->data = new char[len + 1];
strcpy(m_data->data, text);
}
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str operator+(const str& a, const str& b)
{
2023-07-05 21:23:39 +02:00
str result(a);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
result.append(b);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return result;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str operator+(const str& a, const char *b)
{
str result(a);
result.append(b);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return result;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str operator+(const char *a, const str& b)
{
str result(a);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
result.append(b);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return result;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str operator+(const str& a, const bool b)
{
str result(a);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
result.append(b ? "true" : "false");
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return result;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str operator+(const str& a, const char b)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
char text[2];
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
text[0] = b;
text[1] = 0;
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return a + text;
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline str& str::operator+=(const str& a)
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
append(a);
return *this;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str& str::operator+=(const char *a)
{
append(a);
return *this;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str& str::operator+=(const char a)
{
char text[2];
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
text[0] = a;
text[1] = 0;
append(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return *this;
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str& str::operator+=(const bool a)
{
append(a ? "true" : "false");
return *this;
2016-03-27 11:49:47 +02:00
}
2023-07-05 21:23:39 +02:00
inline bool operator==(const str& a, const str& b)
{
return (!strcmp(a.c_str(), b.c_str()));
}
inline bool operator==(const str& a, const char *b)
{
assert(b);
if (!b) {
return false;
}
return (!strcmp(a.c_str(), b));
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline bool operator==(const char *a, const str& b)
{
assert(a);
if (!a) {
return false;
}
return (!strcmp(a, b.c_str()));
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline bool operator!=(const str& a, const str& b)
{
return !(a == b);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline bool operator!=(const str& a, const char *b)
{
return !(a == b);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline bool operator!=(const char *a, const str& b)
{
return !(a == b);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::icmpn(const char *text, size_t n) const
{
assert(m_data);
assert(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::icmpn(m_data->data, text, n);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::icmpn(const str& text, size_t n) const
{
assert(m_data);
assert(text.m_data);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::icmpn(m_data->data, text.m_data->data, n);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::icmp(const char *text) const
{
assert(m_data);
assert(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::icmp(m_data->data, text);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::icmp(const str& text) const
{
assert(c_str());
assert(text.c_str());
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::icmp(c_str(), text.c_str());
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::cmpn(const char *text, size_t n) const
{
assert(c_str());
assert(text);
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::cmpn(c_str(), text, n);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline int str::cmpn(const str& text, size_t n) const
{
assert(c_str());
assert(text.c_str());
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
return str::cmpn(c_str(), text.c_str(), n);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline void str::tolower(void)
{
if (m_data)
{
EnsureDataWritable();
2016-03-27 11:49:47 +02:00
str::tolower(m_data->data);
}
2023-07-05 21:23:39 +02:00
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline void str::toupper(void)
{
if (m_data)
{
EnsureDataWritable();
2016-03-27 11:49:47 +02:00
str::toupper(m_data->data);
}
2023-07-05 21:23:39 +02:00
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline bool str::isNumeric(void) const
{
assert(m_data);
return str::isNumeric(m_data->data);
}
2016-03-27 11:49:47 +02:00
2023-07-05 21:23:39 +02:00
inline str::operator const char *(void) const
2016-03-27 11:49:47 +02:00
{
2023-07-05 21:23:39 +02:00
return c_str();
2016-03-27 11:49:47 +02:00
}