Starbound/attic/old_source/StarStringContainers.hpp
2025-03-21 22:23:30 +11:00

698 lines
21 KiB
C++

#ifndef STAR_STRING_CONTAINERS_HPP
#define STAR_STRING_CONTAINERS_HPP
#include "StarString.hpp"
#include "StarList.hpp"
#include "StarMap.hpp"
#include "StarSet.hpp"
#include "StarMaybe.hpp"
namespace Star {
class StringList : public List<String> {
public:
typedef List<String> Base;
typedef Base::iterator iterator;
typedef Base::const_iterator const_iterator;
typedef Base::value_type value_type;
template<typename Container>
static StringList from(Container const& m) {
return StringList(m.begin(), m.end());
}
StringList();
StringList(Base const& l);
StringList(Base&& l);
StringList(size_t len, String::Char const* const* list);
StringList(size_t len, String const& s1);
StringList(std::initializer_list<String> list);
template<typename InputIterator>
StringList(InputIterator beg, InputIterator end) : Base(beg, end) {}
bool contains(String const& s, String::CaseSensitivity cs = String::CaseSensitive) const;
StringList trimAll(String const& pattern = "") const;
String join(String const& separator = "") const;
StringList& append(String const& e);
StringList& append(String&& e);
StringList& prepend(String const& e);
StringList& prepend(String&& e);
template<typename Container>
StringList& appendAll(Container&& list);
template<typename Container>
StringList& prependAll(Container&& list);
StringList& remove(String const& e);
StringList& erase(size_t index);
StringList& erase(size_t begin, size_t end);
using Base::erase;
StringList& insert(size_t pos, String const& e);
template<typename Container>
StringList& insertAll(size_t pos, Container const& l);
using Base::insert;
StringList& set(size_t pos, String const& e);
StringList slice(SliceIndex a = SliceIndex(), SliceIndex b = SliceIndex(), int i = 1) const;
};
std::ostream& operator<<(std::ostream& os, StringList const& list);
// A type that holds a reference to String/std::string/char*, or an actual held
// String. The lifetime of the class must not be longer than that of a normal
// const&. Is efficient for both lvalue and rvalue references. Internally to
// StringMap, hold() will be called to ensure that the ref holds an actual
// copy of the string.
class StringRef {
public:
// All lvalue constructors make the StringRef hold only the pointer, all
// rvalue constructors cause StringRef to hold the actual String.
StringRef(String const& s);
StringRef(String&& s);
StringRef(std::string const& s);
StringRef(std::string&& s);
StringRef(char const* p);
// No copy constructor is provided to make tracking ownership sane.
StringRef(StringRef&& sr);
StringRef& operator=(StringRef&& sr);
char const* ptr() const;
// Cause this StringRef to always hold a String, not just a ptr. Will be
// cheap if StringRef was constructed with an rvalue.
void hold();
// Gets a const& to the held String if it is held, UB otherwise.
String const& held() const;
bool operator==(StringRef const& ref) const;
private:
void set(String s);
Maybe<String> m_held;
char const* m_ptr;
};
std::ostream& operator<<(std::ostream& os, StringRef const& ref);
template<>
struct hash<StringRef> {
size_t operator()(StringRef const& s) const;
CharHasher<char> impl;
};
struct CaseInsensitiveStringHash {
size_t operator()(StringRef const& s) const;
CharHasher<char> impl;
};
struct CaseInsensitiveStringCompare {
bool operator()(StringRef const& lhs, StringRef const& rhs) const;
};
// A sort of version of HashMap<String, T> that is much faster than the normal
// one for a great many use cases. For example, normal HashMap<String, T>
// forces the user to copy from a char* into a String to do basic things like
// search. This class avoids that by nicely supporting char const* as a key
// type without inducing copying.
//
// It is *almost*, but not *completely* compatible with a normal Map type. In
// particular, iteration is difficult because the key type held internally
// *cannot* be String when using std::unordered_map as the base map. As a
// result, iteration is somewhat weird in that an iterator always returns a
// pair of refs, so something like:
//
// for (auto p : map)
// p.second.mutate();
//
// Will affect the copy in the map, and the second value in the pair is always
// mutable whenever non-const iterators are used.
template<typename MappedT, typename HashT = CharHasher<char>, typename ComparatorT = std::equal_to<StringRef>>
class StringMap {
private:
public:
typedef String key_type;
typedef MappedT mapped_type;
typedef pair<key_type, mapped_type> value_type;
typedef HashT Hash;
typedef ComparatorT Comparator;
struct InternalHash {
bool operator()(StringRef const& s) const;
Hash hash;
};
struct InternalComparator {
bool operator()(StringRef const& a, StringRef const& b) const;
Comparator comparator;
};
typedef std::unordered_map<StringRef, mapped_type, InternalHash, InternalComparator> InternalMap;
typedef typename InternalMap::iterator IteratorBase;
typedef typename InternalMap::const_iterator ConstIteratorBase;
template<typename T>
struct ArrowProxy {
T* operator->();
T t;
};
struct iterator : public IteratorBase {
iterator();
iterator(IteratorBase i);
IteratorBase const& base() const;
ArrowProxy<pair<key_type const&, mapped_type&> const> operator->() const;
pair<key_type const&, mapped_type&> const operator*() const;
};
struct const_iterator : public ConstIteratorBase {
const_iterator();
const_iterator(ConstIteratorBase i);
ConstIteratorBase const& base() const;
ArrowProxy<pair<key_type const&, mapped_type const&> const> operator->() const;
pair<key_type const&, mapped_type const&> const operator*() const;
};
template<typename Container>
static StringMap from(Container const& m) {
return StringMap(m.begin(), m.end());
}
StringMap();
StringMap(StringMap const& map);
StringMap(StringMap&& map);
template<typename InputIterator>
StringMap(InputIterator beg, InputIterator end);
StringMap(std::initializer_list<value_type> list);
StringList keys() const;
List<mapped_type> values() const;
List<value_type> pairs() const;
bool contains(StringRef const& k) const;
mapped_type& get(StringRef const& k);
mapped_type const& get(StringRef const& k) const;
mapped_type value(StringRef const& k, mapped_type d = mapped_type()) const;
Maybe<mapped_type const&> maybe(StringRef const& k) const;
Maybe<mapped_type&> maybe(StringRef const& k);
mapped_type& operator[](StringRef k);
StringMap& operator=(StringMap const& map);
StringMap& operator=(StringMap&& map);
pair<iterator, bool> insert(value_type v);
bool insert(StringRef k, mapped_type v);
iterator insert(iterator pos, value_type v);
bool remove(StringRef const& k);
mapped_type take(StringRef const& k);
const_iterator cbegin() const;
const_iterator cend() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
size_t size() const;
iterator erase(iterator i);
size_t erase(key_type const& k);
iterator find(StringRef const& k);
const_iterator find(StringRef const& k) const;
void clear();
bool empty() const;
// Appends all values of given map into this map. If overwite is false, then
// skips values that already exist in this map. Returns false if any keys
// previously existed.
template<typename MapType>
bool merge(MapType const& m, bool overwrite = false);
mapped_type& add(StringRef k, mapped_type v);
bool operator==(StringMap const& m) const;
private:
InternalMap m_map;
};
typedef HashSet<String> StringSet;
template<typename MappedT, typename HashT, typename ComparatorT>
std::ostream& operator<<(std::ostream& os, StringMap<MappedT, HashT, ComparatorT> const& ref);
inline size_t hash<StringRef>::operator()(StringRef const& s) const {
return impl(s.ptr());
};
inline size_t CaseInsensitiveStringHash::operator()(StringRef const& s) const {
auto p = s.ptr();
size_t h = 0;
while (*p) {
String::Char c;
p += utf8DecodeChar(p, &c);
h = h * 101 + String::toLower(c);
}
return h;
}
inline bool CaseInsensitiveStringCompare::operator()(StringRef const& a, StringRef const& b) const {
auto lhs = a.ptr();
auto rhs = b.ptr();
while (*lhs && *rhs) {
String::Char lhsc;
lhs += utf8DecodeChar(lhs, &lhsc);
String::Char rhsc;
rhs += utf8DecodeChar(rhs, &rhsc);
if (String::toLower(lhsc) != String::toLower(rhsc))
return false;
}
return !*lhs && !*rhs;
}
template<typename Container>
StringList& StringList::appendAll(Container&& list) {
Base::appendAll(std::forward<Container&&>(list));
return *this;
}
template<typename Container>
StringList& StringList::prependAll(Container&& list) {
Base::prependAll(std::forward<Container&&>(list));
return *this;
}
template<typename Container>
StringList& StringList::insertAll(size_t pos, Container const& l) {
Base::insertAll(pos, std::forward<Container&&>(l));
return *this;
}
inline StringRef::StringRef(StringRef&& sr) {
operator=(move(sr));
}
inline StringRef::StringRef(String const& s)
: m_ptr(s.utf8Ptr()) {}
inline StringRef::StringRef(String&& s) {
set(move(s));
}
inline StringRef::StringRef(std::string const& s)
: m_ptr(s.c_str()) {}
inline StringRef::StringRef(std::string&& s) {
set(move(s));
}
inline StringRef::StringRef(char const* p)
: m_ptr(p) {}
inline StringRef& StringRef::operator=(StringRef&& sr) {
if (sr.m_held) {
set(sr.m_held.take());
} else {
m_held.reset();
m_ptr = sr.m_ptr;
}
return *this;
}
inline char const* StringRef::ptr() const {
return m_ptr;
}
inline void StringRef::hold() {
if (!m_held)
set(String(m_ptr));
}
inline String const& StringRef::held() const {
return *m_held;
}
inline bool StringRef::operator==(StringRef const& ref) const {
if (m_held) {
if (ref.m_held)
return *m_held == *ref.m_held;
}
return strcmp(ptr(), ref.ptr()) == 0;
}
inline void StringRef::set(String s) {
m_held = move(s);
m_ptr = m_held->utf8Ptr();
}
inline std::ostream& operator<<(std::ostream& os, StringRef const& ref) {
os << ref.ptr();
return os;
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::InternalHash::operator()(StringRef const& s) const {
return hash(s.ptr());
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::InternalComparator::operator()(StringRef const& a, StringRef const& b) const {
return comparator(a.ptr(), b.ptr());
}
template<typename MappedT, typename HashT, typename ComparatorT>
template<typename T>
T* StringMap<MappedT, HashT, ComparatorT>::ArrowProxy<T>::operator->() {
return &t;
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::iterator::iterator() {}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::iterator::iterator(IteratorBase i)
: IteratorBase(move(i)) {}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::iterator::base() const -> IteratorBase const& {
return *this;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::iterator::operator->() const -> ArrowProxy<pair<key_type const&, mapped_type&> const> {
return {pair<key_type const&, mapped_type&>(base()->first.held(), base()->second)};
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::iterator::operator*() const -> pair<key_type const&, mapped_type&> const {
return {base()->first.held(), base()->second};
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::const_iterator::const_iterator() {}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::const_iterator::const_iterator(ConstIteratorBase i)
: ConstIteratorBase(move(i)) {}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::base() const -> ConstIteratorBase const& {
return *this;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::operator->() const -> ArrowProxy<pair<key_type const&, mapped_type const&> const> {
return {pair<key_type const&, mapped_type const&>(base()->first.held(), base()->second)};
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::const_iterator::operator*() const -> pair<key_type const&, mapped_type const&> const {
return {base()->first.held(), base()->second};
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::StringMap() {}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::StringMap(StringMap const& map) {
for (auto const& p : map)
insert(p);
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::StringMap(StringMap&& map) {
m_map = move(map.m_map);
}
template<typename MappedT, typename HashT, typename ComparatorT>
template<typename InputIterator>
StringMap<MappedT, HashT, ComparatorT>::StringMap(InputIterator beg, InputIterator end) {
for (auto i = beg; i != end; ++i)
insert(*i);
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>::StringMap(std::initializer_list<value_type> list) {
for (auto const& p : list)
insert(p);
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringList StringMap<MappedT, HashT, ComparatorT>::keys() const {
StringList keys;
for (auto const& p : *this)
keys.append(p.first);
return keys;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::values() const -> List<mapped_type> {
List<mapped_type> values;
for (auto const& p : *this)
values.append(p.second);
return values;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::pairs() const -> List<value_type> {
List<value_type> pairs;
for (auto const& p : *this)
pairs.append(p);
return pairs;
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::contains(StringRef const& k) const {
return m_map.find(k) != m_map.end();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::get(StringRef const& k) -> mapped_type& {
auto i = m_map.find(k);
if (i == m_map.end())
throw MapException(strf("Key '%s' not found in StringMap::get()", k.ptr()));
return i->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::get(StringRef const& k) const -> mapped_type const& {
return const_cast<StringMap*>(this)->get(k);
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::value(StringRef const& k, mapped_type d) const -> mapped_type {
auto i = m_map.find(k);
if (i == m_map.end())
return move(d);
else
return i->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::maybe(StringRef const& k) const -> Maybe<mapped_type const&> {
auto i = m_map.find(k);
if (i == m_map.end())
return {};
else
return i->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::maybe(StringRef const& k) -> Maybe<mapped_type&> {
auto i = m_map.find(k);
if (i == m_map.end())
return {};
else
return i->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::operator[](StringRef k) -> mapped_type& {
auto i = m_map.find(k);
if (i == m_map.end()) {
k.hold();
i = m_map.insert(pair<StringRef, mapped_type>(move(k), mapped_type())).first;
}
return i->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>& StringMap<MappedT, HashT, ComparatorT>::operator=(StringMap const& map) {
clear();
for (auto const& p : map)
insert(p);
return *this;
}
template<typename MappedT, typename HashT, typename ComparatorT>
StringMap<MappedT, HashT, ComparatorT>& StringMap<MappedT, HashT, ComparatorT>::operator=(StringMap&& map) {
m_map = move(map.m_map);
return *this;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::insert(value_type v) -> std::pair<iterator, bool> {
return m_map.insert(pair<StringRef, mapped_type>(move(v.first), move(v.second)));
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::insert(StringRef k, mapped_type v) {
k.hold();
return m_map.insert(pair<StringRef, mapped_type>(move(k), move(v))).second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::insert(iterator pos, value_type v) -> iterator {
return m_map.insert(pos, pair<StringRef, mapped_type>(move(v.first), move(v.second)));
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::remove(StringRef const& k) {
auto i = m_map.find(k);
if (i != m_map.end()) {
m_map.erase(i);
return true;
} else {
return false;
}
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::take(StringRef const& k) -> mapped_type {
auto i = m_map.find(k);
if (i != m_map.end()) {
MappedT v = std::move(i->second);
m_map.erase(i);
return std::move(v);
} else {
throw MapException(strf("Key '%s' not found in StringMap::take()", k));
}
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::cbegin() const -> const_iterator {
return m_map.cbegin();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::cend() const -> const_iterator {
return m_map.cend();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::begin() const -> const_iterator {
return m_map.begin();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::end() const -> const_iterator {
return m_map.end();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::begin() -> iterator {
return m_map.begin();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::end() -> iterator {
return m_map.end();
}
template<typename MappedT, typename HashT, typename ComparatorT>
size_t StringMap<MappedT, HashT, ComparatorT>::size() const {
return m_map.size();
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::erase(iterator i) -> iterator {
return m_map.erase(i);
}
template<typename MappedT, typename HashT, typename ComparatorT>
size_t StringMap<MappedT, HashT, ComparatorT>::erase(key_type const& k) {
if (remove(k))
return 1;
return 0;
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::find(StringRef const& k) -> iterator {
return m_map.find(k);
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::find(StringRef const& k) const -> const_iterator {
return m_map.find(k);
}
template<typename MappedT, typename HashT, typename ComparatorT>
void StringMap<MappedT, HashT, ComparatorT>::clear() {
m_map.clear();
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::empty() const {
return m_map.empty();
}
template<typename MappedT, typename HashT, typename ComparatorT>
template<typename MapType>
bool StringMap<MappedT, HashT, ComparatorT>::merge(MapType const& m, bool overwrite) {
return mapMerge(*this, m, overwrite);
}
template<typename MappedT, typename HashT, typename ComparatorT>
auto StringMap<MappedT, HashT, ComparatorT>::add(StringRef k, mapped_type v) -> mapped_type& {
k.hold();
auto p = m_map.insert(pair<StringRef, mapped_type>(move(k), move(v)));
if (!p.second)
throw MapException(strf("Entry with key '%s' already present.", outputAny(k)));
else
return p.first->second;
}
template<typename MappedT, typename HashT, typename ComparatorT>
bool StringMap<MappedT, HashT, ComparatorT>::operator==(StringMap const& m) const {
return mapsEqual(*this, m);
}
template<typename MappedT, typename HashT, typename ComparatorT>
std::ostream& operator<<(std::ostream& os, StringMap<MappedT, HashT, ComparatorT> const& ref) {
printMap(os, ref);
return os;
}
}
#endif