--- deliantra/server/include/shstr.h 2006/02/03 07:12:50 1.1.1.1 +++ deliantra/server/include/shstr.h 2009/11/03 23:44:21 1.34 @@ -1,70 +1,261 @@ -/* The size of the shared strings hashtable. This must be smaller than - * 32767, but 947 ought to be plenty enough. +/* + * This file is part of Deliantra, the Roguelike Realtime MMORPG. + * + * Copyright (©) 2005,2006,2007,2008,2009 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 */ -#define TABLESIZE 4133 -/* This specifies how many characters the hashing routine should look at. - * You may actually save CPU by increasing this number if the typical string - * is large. - */ -#ifndef MAXSTRING -#define MAXSTRING 20 -#endif +#ifndef SHSTR_H__ +#define SHSTR_H__ -/* In the unlikely occurence that 16383 references to a string are too - * few, you can modify the below type to something bigger. - * (The top bit of "refcount" is used to signify that "u.array" points - * at the array entry.) - */ -#define REFCOUNT_TYPE int +#include +#include -/* The offsetof macro is part of ANSI C, but many compilers lack it, for - * example "gcc -ansi" - */ -#if !defined (offsetof) -#define offsetof(type, member) (int)&(((type *)0)->member) -#endif +#include "traits.h" -/* SS(string) will return the address of the shared_string struct which - * contains "string". - */ -#define SS(x) ((shared_string *) ((x) - offsetof(shared_string, string))) +extern size_t shstr_alloc; + +extern int buf_overflow (const char *buf1, const char *buf2, int bufsize); + +template +struct shstr_vec +{ + uint32_t len; + uint32_t refcnt; + 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 apss 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 &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 + { + return s != null () && strstr (s, substring); + } + + //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 std::ostream &operator <<(std::ostream &o, shstr_tmp sh) +{ + o.write (sh.s, sh.length ()); -#define SS_STATISTICS + return o; +} -#define SS_DUMP_TABLE 1 -#define SS_DUMP_TOTALS 2 +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, 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 = 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 -#ifdef SS_STATISTICS -static struct statistics { - int calls; - int hashed; - int strcmps; - int search; - int linked; -} add_stats, add_ref_stats, free_stats, find_stats, hash_stats; -#define GATHER(n) (++n) -#else /* !SS_STATISTICS */ -#define GATHER(n) -#endif /* SS_STATISTICS */ - -#define TOPBIT ((unsigned REFCOUNT_TYPE) 1 << (sizeof(REFCOUNT_TYPE) * CHAR_BIT - 1)) - -#define PADDING ((2 * sizeof(long) - sizeof(REFCOUNT_TYPE)) % sizeof(long)) + 1 - -typedef struct _shared_string { - union { - struct _shared_string **array; - struct _shared_string *previous; - } u; - struct _shared_string *next; - /* The top bit of "refcount" is used to signify that "u.array" points - * at the array entry. - */ - unsigned REFCOUNT_TYPE refcount; - /* Padding will be unused memory, since we can't know how large - * the padding when allocating memory. We assume here that - * sizeof(long) is a good boundary. - */ - char string[PADDING]; -} shared_string;