/* * This file is part of Deliantra, the Roguelike Realtime MMORPG. * * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team * * Deliantra is free software: you can redistribute it and/or modify it under * the terms of the Affero GNU General Public License as published by the * Free Software Foundation, either version 3 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. * * You should have received a copy of the Affero GNU General Public License * and the GNU General Public License along with this program. If not, see * . * * The authors can be reached via e-mail to */ #ifndef SHSTR_H__ #define SHSTR_H__ #include #include #include "traits.h" extern size_t shstr_alloc; extern int buf_overflow (const char *buf1, const char *buf2, int bufsize); template struct shstr_vec { uint32_t hash; uint32_t len; uint32_t refcnt; // pointer points here char string [size]; }; // this class is a non-refcounted shared string // it cannot be used to create or store shared strings, but // it can be used to pass shared strings around, i.e. as function arguments // or return values. their lifetime must not span a gc () call, i.e. // they are only valid as temporary values within the same server tick. struct shstr_tmp { static shstr_vec nullvec; static const char *null () { return nullvec.string; } // this is the null pointer value const char *s; static unsigned int &hash (const char *s) { return *((unsigned int *)s - 3); } int hash () const { return hash (s); } static unsigned int &length (const char *s) { return *((unsigned int *)s - 2); } int length () const { return length (s); } // returns whether this shared string begins with the given prefix, // used mainly for searches when users give only the start of a name. bool starts_with (const char *prefix) const { int plen = strlen (prefix); return length () >= plen && !strncasecmp (s, prefix, plen); } // returns true if the substring is contained in the shstr // if the shstr is 0, then this always returns false. // the shstr is (theoretically) treated as a comma/colon/space etc. separated list. bool contains (const char *substring) const; //TODO: case sensitive should be eradicated bool eq_nc (const char *otherstring) const { return !strcasecmp (s, otherstring); } shstr_tmp () : s (null ()) { } shstr_tmp (const shstr_tmp &sh) : s (sh.s) { } shstr_tmp operator =(const shstr_tmp &sh) { s = sh.s; return *this; } // this is used for informational messages and the like const char *operator &() const { return s; } operator const char *() const { return s == null () ? 0 : s; } protected: // dummy is there so it isn't used as type converter accidentally shstr_tmp (int dummy, const char *s) : s(s) { } }; inline bool operator ==(const shstr_tmp &a, const shstr_tmp &b) { return a.s == b.s; } inline bool operator !=(const shstr_tmp &a, const shstr_tmp &b) { return a.s != b.s; } inline int strlen (shstr_tmp sh) { return sh.length (); } static inline std::ostream &operator <<(std::ostream &o, shstr_tmp sh) { o.write (sh.s, sh.length ()); return o; } struct shstr : shstr_tmp { static unsigned int &refcnt (const char *s) { return *((unsigned int *)s - 1); } unsigned int &refcnt () const { return refcnt (s); } shstr () { } static const char *find (const char *s); static const char *intern (const char *s); static void gc (); // garbage collect a few strings shstr (const shstr &sh) : shstr_tmp (sh) { ++refcnt (); } shstr (const shstr_tmp &sh) : shstr_tmp (sh) { ++refcnt (); } explicit shstr (const char *str) : shstr_tmp (0, ecb_is_constant (str) && !str ? null () : intern (str)) { } ~shstr () { --refcnt (); } using shstr_tmp::operator &; using shstr_tmp::operator const char *; // (note: not the copy constructor) shstr &operator =(const shstr_tmp &sh) { --refcnt (); s = sh.s; ++refcnt (); return *this; } // here it comes shstr &operator =(const shstr &sh) { return (*this) = (shstr_tmp)sh; } // shstr_tmp doesn't have this one shstr &operator =(const char *str) { --refcnt (); s = ecb_is_constant (str) && !str ? null () : intern (str); return *this; } }; // only good for mass comparisons to shstr objects, or very // temporary passing, basically a non-refcounted shstr struct shstr_cmp { const char *s; // initialies to the non-matching string (as opposed to the null string) shstr_cmp () { s = 0; } shstr_cmp (const char *str) : s (shstr::find (str)) { } shstr_cmp (shstr_tmp sh) : s (sh.s) { } // this is used for informational messages and the like const char *operator &() const { return s; } operator const char *() const { return s; } }; inline bool operator ==(const shstr_cmp &a, const shstr_tmp &b) { return a.s == b.s; } inline bool operator ==(const shstr_tmp &a, const shstr_cmp &b) { return a.s == b.s; } #define def2(id,str) extern const shstr id; #define def(id) def2(shstr_ ## id, # id) # include "shstrinc.h" #undef def #undef def2 // undefined external reference to catch people using str* functions when they shouldn't //template void strcmp (const shstr_tmp &a, any b); template void strstr (const shstr_tmp &a, any b); #endif