og-odamex/common/m_ostring.cpp
2026-01-05 01:52:47 +00:00

840 lines
19 KiB
C++

// Emacs style mode select -*- C++ -*-
//-----------------------------------------------------------------------------
//
// $Id$
//
// Copyright (C) 2006-2026 by The Odamex Team.
//
// This program 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.
//
// This program 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.
//
// DESCRIPTION:
//
// Drop-in replacement for std::string featuring string interning.
// When an OString is compared for equality with another OString, an integer
// hash of the string is used for extremely quick comparison rather than
// the normal strcmp style of comparison.
//
//-----------------------------------------------------------------------------
#include "odamex.h"
#include "m_ostring.h"
// initialize static member variables
bool OString::mInitialized = false;
OString::StringTable* OString::mStrings = NULL;
OString::StringLookupTable* OString::mStringLookup = NULL;
std::string* OString::mEmptyString = NULL;
// ------------------------------------------------------------------------
// startup / shutdown
// ------------------------------------------------------------------------
void OString::startup()
{
if (!mInitialized)
{
mStrings = new StringTable(OString::MAX_STRINGS);
mStringLookup = new StringLookupTable(OString::MAX_STRINGS);
mEmptyString = new std::string("");
mInitialized = true;
}
}
void OString::shutdown()
{
delete mStrings;
mStrings = NULL;
delete mStringLookup;
mStringLookup = NULL;
delete mEmptyString;
mEmptyString = NULL;
mInitialized = false;
}
// ------------------------------------------------------------------------
// debugging functions
// ------------------------------------------------------------------------
void OString::printStringTable()
{
fmt::print("OString Table\n");
fmt::print("=============\n");
for (const auto& string : *mStrings)
fmt::print("id 0x{:08} hash 0x{:08} ({}): {}\n", mStrings->getId(string), hash(string.mString.c_str()),
string.mRefCount, string.mString);
fmt::print("\n");
}
// ------------------------------------------------------------------------
// OString Constructors
// ------------------------------------------------------------------------
OString::OString() :
mId(mEmptyStringId)
{
startup();
assign("");
}
OString::OString(const OString& other) :
mId(mEmptyStringId)
{
startup();
assign(other);
}
OString::OString(const std::string& str) :
mId(mEmptyStringId)
{
startup();
assign(str);
}
OString::OString(std::string_view str) :
mId(mEmptyStringId)
{
startup();
assign(str.data(), str.length());
}
OString::OString(const OString& other, size_t pos, size_t len) :
mId(mEmptyStringId)
{
startup();
assign(std::string(other.getString(), pos, len));
}
OString::OString(const std::string& str, size_t pos, size_t len) :
mId(mEmptyStringId)
{
startup();
assign(std::string(str, pos, len));
}
OString::OString(std::string_view str, size_t pos, size_t len) :
mId(mEmptyStringId)
{
startup();
str = str.substr(pos, len);
assign(str.data(), str.length());
}
OString::OString(const char* s, size_t n) :
mId(mEmptyStringId)
{
startup();
assign(s, n);
}
OString::OString(size_t n, char c) :
mId(mEmptyStringId)
{
startup();
assign(std::string(n, c));
}
template <class InputIterator>
OString::OString(InputIterator first, InputIterator last) :
mId(mEmptyStringId)
{
startup();
assign(std::string(first, last));
}
// ------------------------------------------------------------------------
// OString Destructor
// ------------------------------------------------------------------------
OString::~OString()
{
clear();
// Last string was removed so shutdown
if (mStrings && mStrings->empty())
shutdown();
}
// ------------------------------------------------------------------------
// OString::operator=
// ------------------------------------------------------------------------
OString& OString::operator= (const OString& other)
{
return assign(other);
}
OString& OString::operator= (const std::string& str)
{
return assign(str);
}
OString& OString::operator= (const char* s)
{
return assign(s);
}
OString& OString::operator= (char c)
{
return assign(1, c);
}
// ------------------------------------------------------------------------
// OString::begin
// ------------------------------------------------------------------------
OString::const_iterator OString::begin() const
{
return getString().begin();
}
// ------------------------------------------------------------------------
// OString::end
// ------------------------------------------------------------------------
OString::const_iterator OString::end() const
{
return getString().end();
}
// ------------------------------------------------------------------------
// OString::rbegin
// ------------------------------------------------------------------------
OString::const_reverse_iterator OString::rbegin() const
{
return getString().rbegin();
}
// ------------------------------------------------------------------------
// OString::rend
// ------------------------------------------------------------------------
OString::const_reverse_iterator OString::rend() const
{
return getString().rend();
}
// ------------------------------------------------------------------------
// OString::size
// ------------------------------------------------------------------------
size_t OString::size() const
{
return getString().size();
}
// ------------------------------------------------------------------------
// OString::length
// ------------------------------------------------------------------------
size_t OString::length() const
{
return getString().length();
}
// ------------------------------------------------------------------------
// OString::max_size
// ------------------------------------------------------------------------
size_t OString::max_size() const
{
return getString().max_size();
}
// ------------------------------------------------------------------------
// OString::capacity
// ------------------------------------------------------------------------
size_t OString::capacity() const
{
return getString().capacity();
}
// ------------------------------------------------------------------------
// OString::empty
// ------------------------------------------------------------------------
bool OString::empty() const
{
return getString().empty();
}
// ------------------------------------------------------------------------
// OString::clear
// ------------------------------------------------------------------------
void OString::clear()
{
assign("");
}
// ------------------------------------------------------------------------
// OString::operator[]
// ------------------------------------------------------------------------
const char& OString::operator[] (size_t pos) const
{
return getString().operator[] (pos);
}
// ------------------------------------------------------------------------
// OString::at
// ------------------------------------------------------------------------
const char& OString::at(size_t pos) const
{
return getString().at(pos);
}
// ------------------------------------------------------------------------
// OString::swap
// ------------------------------------------------------------------------
void OString::swap(OString& other)
{
std::swap(mId, other.mId);
}
// ------------------------------------------------------------------------
// OString::get_allocator
// ------------------------------------------------------------------------
OString::allocator_type OString::get_allocator() const
{
return getString().get_allocator();
}
// ------------------------------------------------------------------------
// OString::copy
// ------------------------------------------------------------------------
size_t OString::copy(char* s, size_t len, size_t pos) const
{
return getString().copy(s, len, pos);
}
// ------------------------------------------------------------------------
// OString::find
// ------------------------------------------------------------------------
size_t OString::find(const OString& other, size_t pos) const
{
return getString().find(other.getString(), pos);
}
size_t OString::find(const std::string& str, size_t pos) const
{
return getString().find(str, pos);
}
size_t OString::find(const char* s, size_t pos) const
{
return getString().find(s, pos);
}
size_t OString::find(const char* s, size_t pos, size_t n) const
{
return getString().find(s, pos, n);
}
size_t OString::find(char c, size_t pos) const
{
return getString().find(c, pos);
}
// ------------------------------------------------------------------------
// OString::rfind
// ------------------------------------------------------------------------
size_t OString::rfind(const OString& other, size_t pos) const
{
return getString().rfind(other.getString(), pos);
}
size_t OString::rfind(const std::string& str, size_t pos) const
{
return getString().rfind(str, pos);
}
size_t OString::rfind(const char* s, size_t pos) const
{
return getString().rfind(s, pos);
}
size_t OString::rfind(const char* s, size_t pos, size_t n) const
{
return getString().rfind(s, pos, n);
}
size_t OString::rfind(char c, size_t pos) const
{
return getString().rfind(c, pos);
}
// ------------------------------------------------------------------------
// OString::find_first_of
// ------------------------------------------------------------------------
size_t OString::find_first_of(const OString& other, size_t pos) const
{
return getString().find_first_of(other.getString(), pos);
}
size_t OString::find_first_of(const std::string& str, size_t pos) const
{
return getString().find_first_of(str, pos);
}
size_t OString::find_first_of(const char* s, size_t pos) const
{
return getString().find_first_of(s, pos);
}
size_t OString::find_first_of(const char* s, size_t pos, size_t n) const
{
return getString().find_first_of(s, pos, n);
}
size_t OString::find_first_of(char c, size_t pos) const
{
return getString().find_first_of(c, pos);
}
// ------------------------------------------------------------------------
// OString::find_last_of
// ------------------------------------------------------------------------
size_t OString::find_last_of(const OString& other, size_t pos) const
{
return getString().find_last_of(other.getString(), pos);
}
size_t OString::find_last_of(const std::string& str, size_t pos) const
{
return getString().find_last_of(str, pos);
}
size_t OString::find_last_of(const char* s, size_t pos) const
{
return getString().find_last_of(s, pos);
}
size_t OString::find_last_of(const char* s, size_t pos, size_t n) const
{
return getString().find_last_of(s, pos, n);
}
size_t OString::find_last_of(char c, size_t pos) const
{
return getString().find_last_of(c, pos);
}
// ------------------------------------------------------------------------
// OString::find_first_not_of
// ------------------------------------------------------------------------
size_t OString::find_first_not_of(const OString& other, size_t pos) const
{
return getString().find_first_not_of(other.getString(), pos);
}
size_t OString::find_first_not_of(const std::string& str, size_t pos) const
{
return getString().find_first_not_of(str, pos);
}
size_t OString::find_first_not_of(const char* s, size_t pos) const
{
return getString().find_first_not_of(s, pos);
}
size_t OString::find_first_not_of(const char* s, size_t pos, size_t n) const
{
return getString().find_first_not_of(s, pos, n);
}
size_t OString::find_first_not_of(char c, size_t pos) const
{
return getString().find_first_not_of(c, pos);
}
// ------------------------------------------------------------------------
// OString::find_last_not_of
// ------------------------------------------------------------------------
size_t OString::find_last_not_of(const OString& other, size_t pos) const
{
return getString().find_last_not_of(other.getString(), pos);
}
size_t OString::find_last_not_of(const std::string& str, size_t pos) const
{
return getString().find_last_not_of(str, pos);
}
size_t OString::find_last_not_of(const char* s, size_t pos) const
{
return getString().find_last_not_of(s, pos);
}
size_t OString::find_last_not_of(const char* s, size_t pos, size_t n) const
{
return getString().find_last_not_of(s, pos, n);
}
size_t OString::find_last_not_of(char c, size_t pos) const
{
return getString().find_last_not_of(c, pos);
}
// ------------------------------------------------------------------------
// OString::substr
// ------------------------------------------------------------------------
OString OString::substr(size_t pos, size_t len) const
{
return OString(getString().substr(pos, len));
}
// ------------------------------------------------------------------------
// OString::compare
// ------------------------------------------------------------------------
int OString::compare(size_t pos, size_t len, const OString& other) const
{
return getString().compare(pos, len, other.getString());
}
int OString::compare(size_t pos, size_t len, const OString& other, size_t subpos, size_t sublen) const
{
return getString().compare(pos, len, other.getString(), subpos, sublen);
}
int OString::compare(const std::string& str) const
{
return getString().compare(str);
}
int OString::compare(size_t pos, size_t len, const std::string& str) const
{
return getString().compare(pos, len, str);
}
int OString::compare(size_t pos, size_t len, const std::string& str, size_t subpos, size_t sublen) const
{
return getString().compare(pos, len, str, subpos, sublen);
}
int OString::compare(const char* s) const
{
return getString().compare(s);
}
int OString::compare(size_t pos, size_t len, const char* s) const
{
return getString().compare(pos, len, s);
}
int OString::compare(size_t pos, size_t len, const char* s, size_t n) const
{
return getString().compare(pos, len, s, n);
}
// ------------------------------------------------------------------------
// operator==
// ------------------------------------------------------------------------
bool operator== (const OString& lhs, const OString& rhs)
{
return lhs.equals(rhs);
}
bool operator== (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) == 0;
}
bool operator== (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) == 0;
}
bool operator== (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) == 0;
}
bool operator== (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) == 0;
}
// ------------------------------------------------------------------------
// operator!=
// ------------------------------------------------------------------------
bool operator!= (const OString& lhs, const OString& rhs)
{
return !(lhs.equals(rhs));
}
bool operator!= (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) != 0;
}
bool operator!= (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) != 0;
}
bool operator!= (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) != 0;
}
bool operator!= (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) != 0;
}
// ------------------------------------------------------------------------
// operator<
// ------------------------------------------------------------------------
bool operator< (const OString& lhs, const OString& rhs)
{
return lhs.compare(rhs) < 0;
}
bool operator< (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) < 0;
}
bool operator< (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) > 0;
}
bool operator< (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) < 0;
}
bool operator< (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) > 0;
}
// ------------------------------------------------------------------------
// operator<=
// ------------------------------------------------------------------------
bool operator<= (const OString& lhs, const OString& rhs)
{
return lhs.equals(rhs) || lhs.compare(rhs) < 0;
}
bool operator<= (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) < 0;
}
bool operator<= (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) > 0;
}
bool operator<= (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) < 0;
}
bool operator<= (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) > 0;
}
// ------------------------------------------------------------------------
// operator>
// ------------------------------------------------------------------------
bool operator> (const OString& lhs, const OString& rhs)
{
return lhs.compare(rhs) > 0;
}
bool operator> (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) > 0;
}
bool operator> (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) < 0;
}
bool operator> (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) > 0;
}
bool operator> (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) < 0;
}
// ------------------------------------------------------------------------
// operator>=
// ------------------------------------------------------------------------
bool operator>= (const OString& lhs, const OString& rhs)
{
return lhs.equals(rhs) || lhs.compare(rhs) > 0;
}
bool operator>= (const OString& lhs, const std::string& rhs)
{
return lhs.compare(rhs) > 0;
}
bool operator>= (const std::string& lhs, const OString& rhs)
{
return rhs.compare(lhs) < 0;
}
bool operator>= (const OString& lhs, const char* rhs)
{
return lhs.compare(rhs) > 0;
}
bool operator>= (const char* lhs, const OString& rhs)
{
return rhs.compare(lhs) < 0;
}
// ------------------------------------------------------------------------
// swap
// ------------------------------------------------------------------------
namespace std {
void swap(::OString& x, ::OString& y)
{
x.swap(y);
}
}
// ----------------------------------------------------------------------------
// utility functions
// ----------------------------------------------------------------------------
struct UpperFunctor
{
inline char operator()(const char c) const
{ return toupper(c); }
};
struct LowerFunctor
{
inline char operator()(const char c) const
{ return tolower(c); }
};
template <typename FUNC>
static OString OStringConverter(const char* s, size_t length)
{
char fixed_buf[1024];
char* dyn_buf = NULL;
char* out;
const char* in = s;
FUNC func; // instance of the conversion functor
if (length < 1024)
out = fixed_buf;
else
out = dyn_buf = new char[length + 1];
for (size_t i = 0; i < length && *in != '\0'; i++)
*out++ = func(*in++);
*out = '\0';
if (length < 1024)
return OString(fixed_buf);
else
{
OString result(dyn_buf);
delete [] dyn_buf;
return result;
}
}
OString OStringToUpper(const char* s, size_t length)
{
return OStringConverter<UpperFunctor>(s, length);
}
OString OStringToUpper(const OString& str)
{
return OStringConverter<UpperFunctor>(str.c_str(), str.length());
}
OString OStringToLower(const char* s, size_t length)
{
return OStringConverter<LowerFunctor>(s, length);
}
OString OStringToLower(const OString& str)
{
return OStringConverter<LowerFunctor>(str.c_str(), str.length());
}
VERSION_CONTROL (m_ostring_cpp, "$Id$")