Rewrote format() parser to be re-entrant and easier to read

Added various optimizations
Fixed memory leak in sh_tinyhash
This commit is contained in:
David Anderson 2005-11-21 10:04:43 +00:00
parent f13599177f
commit c6fc34a64d
7 changed files with 269 additions and 276 deletions

View File

@ -324,276 +324,243 @@ int CLangMngr::GetKeyEntry(String &key)
return val.index; return val.index;
} }
#define CHECK_PTR(ptr, start, bufsize) if ((ptr) - (start) >= (bufsize)) { \ /**
LogError(amx, AMX_ERR_STACKERR, "Buffer overflow in string formatting"); \ * FORMATTING ROUTINES
outbuf[0] = 0; \ */
len = 0; \
return outbuf; }
#define CHECK_OUTPTR(offset) CHECK_PTR(outptr+offset, outbuf, sizeof(outbuf))
#define ZEROTERM(buf) buf[(sizeof(buf)/sizeof(buf[0]))-1]=0;
#define NEXT_PARAM() \
if (parm > paramCount) \
{ \
strcpy(outbuf, ""); \
len = 0; \
LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", parm, paramCount); \
return outbuf; \
}
char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len)
#define MAX_LEVELS 4
size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level);
size_t do_amx_format_parameter(AMX *amx, cell *params, const char **fmtstr, int *param, char *output, size_t maxlen, int level)
{ {
// number of parameters ( for NEXT_PARAM macro ) static char buffer[MAX_LEVELS][2048];
int paramCount = *params / sizeof(cell); static char fmt[MAX_LEVELS][32];
size_t len = 0;
char *fmtptr = fmt[level];
const char *fmtsrc = *fmtstr;
char ctrl_code;
int numParams = params[0] / sizeof(cell);
if (*param > numParams)
{
LogError(amx, AMX_ERR_PARAMS, "String formatted incorrectly - parameter %d (total %d)", *param, numParams);
return 0;
}
cell _addr = params[*param];
cell *addr = get_amxaddr(amx, _addr);
(*param)++;
if (level >= MAX_LEVELS)
{
output[0] = '\0';
return 0;
}
*fmtptr++ = '%';
while (fmtsrc[len] && !isalpha((char)fmtsrc[len]))
{
if (len >= sizeof(fmt)-2)
break;
*fmtptr++ = static_cast<char>(fmtsrc[len++]);
}
//get the final character
ctrl_code = fmtsrc[len++];
//inc the source pointer
*fmtstr = &(fmtsrc[len]);
//finalize the string
*fmtptr++ = ctrl_code;
*fmtptr = '\0';
len = 0;
//reset the format pointer
fmtptr = fmt[level];
char *tmp_buf = buffer[level];
//we now have the format
switch (ctrl_code)
{
case 's':
{
get_amxstring_r(amx, _addr, tmp_buf, 2047);
return _snprintf(output, maxlen, fmtptr, tmp_buf);
break;
}
case 'g':
case 'f':
{
return _snprintf(output, maxlen, fmtptr, *(REAL *)addr);
break;
}
case 'i':
case 'd':
case 'c':
{
return _snprintf(output, maxlen, fmtptr, (int)addr[0]);
break;
}
case 'L':
{
const char *pLangName = NULL;
const char *def = NULL, *key = NULL;
int status; int status;
int tmpLen;
// the output buffer if (addr[0] == LANG_PLAYER)
static char outbuf[4096];
char *outptr = outbuf;
cell *src = get_amxaddr(amx, params[parm++]);
while (*src)
{ {
if (*src == '%') if ( (int)CVAR_GET_FLOAT("amx_client_languages") == 0 )
{ {
++src; pLangName = g_vault.get("server_language");
if (*src == 'L')
{
cell langName = params[parm]; // "en" case (langName contains the address to the string)
NEXT_PARAM();
cell *pAmxLangName = get_amxaddr(amx, params[parm++]); // other cases
const char *cpLangName=NULL;
// Handle player ids (1-32) and server language
if (*pAmxLangName == LANG_PLAYER) // LANG_PLAYER
{
if ((int)CVAR_GET_FLOAT("amx_client_languages") == 0)
{
cpLangName = g_vault.get("server_language");
} else { } else {
cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(m_CurGlobId)->pEdict, "lang"); pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(g_langMngr.GetDefLang())->pEdict, "lang");
} }
} } else if (addr[0] == LANG_SERVER) {
else if (*pAmxLangName == LANG_SERVER) // LANG_SERVER pLangName = g_vault.get("server_language");
} else if (addr[0] >= 1 && addr[0] <= gpGlobals->maxClients) {
if ( (int)CVAR_GET_FLOAT("amx_client_languages") == 0 )
{ {
cpLangName = g_vault.get("server_language"); pLangName = g_vault.get("server_language");
}
else if (*pAmxLangName >= 1 && *pAmxLangName <= 32) // Direct Client Id
{
if ((int)CVAR_GET_FLOAT("amx_client_languages") == 0)
{
cpLangName = g_vault.get("server_language");
} else { } else {
cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(*pAmxLangName)->pEdict, "lang"); pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(addr[0])->pEdict, "lang");
} }
} else { // Language Name } else {
int tmplen = 0; pLangName = get_amxstring(amx, _addr, 0, tmpLen);
cpLangName = get_amxstring(amx, langName, 2, tmplen);
} }
if (!pLangName || !isalpha(pLangName[0]))
if (!cpLangName || strlen(cpLangName) < 1) pLangName = "en";
cpLangName = "en"; //next parameter!
_addr = params[*param];
int tmplen = 0; (*param)++;
NEXT_PARAM(); key = get_amxstring(amx, _addr, 1, tmpLen);
char *key = get_amxstring(amx, params[parm++], 1, tmplen); def = g_langMngr.GetDef(pLangName, key, status);
const char *def = GetDef(cpLangName, key, status);
if (def == NULL) if (def == NULL)
{ {
bool a = true; bool a = true;
if (status == LANG_STATUS_LNOTFOUND) if (status == LANG_STATUS_LNOTFOUND)
{ {
AMXXLOG_Log("[AMXX] Language \"%s\" not found", cpLangName); AMXXLOG_Log("[AMXX] Language \"%s\" not found", pLangName);
} } else if (status == LANG_STATUS_KLNOTFOUND) {
else if (status == LANG_STATUS_KLNOTFOUND)
{
a = false; a = false;
AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, cpLangName); AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, pLangName);
} }
if (*pAmxLangName != LANG_SERVER) if (addr[0] != LANG_SERVER)
{ def = g_langMngr.GetDef(g_vault.get("server_language"), key, status);
def = GetDef(g_vault.get("server_language"), key, status);
}
if (!def && (strcmp(cpLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0)) if (!def && (strcmp(pLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0))
{ def = g_langMngr.GetDef("en", key, status);
def = GetDef("en", key, status);
}
if (!def) if (!def)
{ {
static char buf[512]; return _snprintf(output, maxlen, "ML_NOTFOUND: %s", key);
CHECK_PTR((char*)(buf + 17 + strlen(key)), buf, sizeof(buf));
sprintf(buf, "ML_LNOTFOUND: %s", key);
def = buf;
if (a) if (a)
AMXXLOG_Log("[AMXX] Language key \"%s\" not found, check \"%s\"", key, GetFileName(amx)); AMXXLOG_Log("[AMXX] Language key \"%s\" not found, check \"%s\"", key, GetFileName(amx));
} }
} }
return do_amx_format(amx, params, param, &def, output, maxlen, level + 1);
while (*def)
{
if (*def == '%')
{
++def;
if (*def == '%' || *def == 0)
{
*outptr++ = '%';
++def;
} else {
static char format[32];
format[0] = '%';
char *ptr = format + 1;
while (ptr-format<sizeof(format) && !isalpha(*ptr++ = *def++))
/*nothing*/;
ZEROTERM(format);
*ptr = 0;
switch (*(ptr - 1))
{
case 's':
{
static char tmpString[4096];
char *tmpPtr = tmpString;
NEXT_PARAM();
cell *tmpCell = get_amxaddr(amx, params[parm++]);
while (tmpPtr-tmpString < sizeof(tmpString) && *tmpCell)
*tmpPtr++ = static_cast<char>(*tmpCell++);
*tmpPtr = 0;
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, tmpString);
ZEROTERM(outbuf);
break;
}
case 'g':
case 'f':
{
NEXT_PARAM();
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, *(REAL*)get_amxaddr(amx, params[parm++]));
ZEROTERM(outbuf);
break;
}
case 'i':
case 'd':
case 'c':
{
NEXT_PARAM();
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, (int)*get_amxaddr(amx, params[parm++]));
ZEROTERM(outbuf);
break;
} }
default: default:
{ {
CHECK_OUTPTR(strlen(format) + 1); return _snprintf(output, maxlen, "%s", fmtptr);
strcpy(outptr, format);
break; break;
} }
} }
}
outptr += strlen(outptr); #define DUMP_CP_BUFFER(expr) \
if (sofar > 0) { \
if (sofar <= (int)maxlen) { \
memcpy(output, save, sofar); \
output += sofar; \
maxlen -= sofar; \
sofar = 0; \
} else { \
expr; \
} \
} }
}
else if (*def == '^') size_t do_amx_format(AMX *amx, cell *params, int *param, const char **lex, char *output, size_t maxlen, int level)
{
int sofar = 0;
size_t written;
size_t orig_maxlen = maxlen;
const char *save = *lex;
register const char *lexptr = save;
while (*lexptr)
{ {
++def; switch (*lexptr)
{
switch (*def) case '%':
{
lexptr++;
if (*lexptr == '%' || *lexptr == '\0')
{
sofar+=2;
} else {
DUMP_CP_BUFFER(break);
written = do_amx_format_parameter(amx, params, &lexptr, param, output, maxlen, level + 1);
output += written;
maxlen -= written;
save = lexptr;
}
break;
}
case '^':
{
if (level)
{
DUMP_CP_BUFFER(break);
lexptr++;
switch (*lexptr)
{ {
case 'n': case 'n':
CHECK_OUTPTR(1); {
*outptr++ = '\n'; *output++ = '\n';
break; break;
}
case 't': case 't':
CHECK_OUTPTR(1);
*outptr++ = '\t';
break;
case '^':
CHECK_OUTPTR(1);
*outptr++ = '^';
break;
default:
CHECK_OUTPTR(2);
*outptr++ = '^';
*outptr++ = *def;
break;
}
++def;
} else {
CHECK_OUTPTR(1);
*outptr++ = *def++;
}
}
} else {
static char tmpString[4096];
char *tmpPtr = tmpString;
int tmpLen = 0;
static char format[32] = {'%'};
char *ptr = format + 1;
if (*src != '%')
{ {
while (*src != 0 && ptr-format<sizeof(format) && !isalpha(*ptr++ = static_cast<char>(*src++))) *output++ = '\t';
/*nothing*/;
*ptr = 0;
ZEROTERM(format);
--src;
switch (*(ptr - 1))
{
case 's':
{
NEXT_PARAM();
cell *tmpCell = get_amxaddr(amx, params[parm++]);
while (tmpPtr-tmpString<sizeof(tmpString) && *tmpCell)
*tmpPtr++ = static_cast<char>(*tmpCell++);
*tmpPtr = 0;
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, tmpString);
ZEROTERM(outbuf);
break;
}
case 'g':
case 'f':
{
NEXT_PARAM();
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, *(REAL*)get_amxaddr(amx, params[parm++]));
break;
}
case 'i':
case 'd':
case 'c':
{
NEXT_PARAM();
_snprintf(outptr, sizeof(outbuf)-(outptr-outbuf)-1, format, (int)*get_amxaddr(amx, params[parm++]));
break; break;
} }
default: default:
{ {
CHECK_OUTPTR(strlen(format) + 1); *output++ = *lexptr;
strcpy(outptr, format);
break; break;
} }
} }
lexptr++;
maxlen--;
save = lexptr;
break;
}
}
default:
{
lexptr++;
sofar++;
}
}
}
DUMP_CP_BUFFER(;);
*output = '\0';
outptr += strlen(outptr); *lex = lexptr;
} else {
CHECK_OUTPTR(1);
*outptr++ = '%';
}
}
} else {
CHECK_OUTPTR(1);
*outptr++ = static_cast<char>(*src);
}
++src;
}
len = outptr - outbuf; return (orig_maxlen-maxlen);
CHECK_OUTPTR(1); }
*outptr++ = 0;
char * CLangMngr::FormatAmxString(AMX *amx, cell *params, int parm, int &len)
{
//do an initial run through all this
static char mystr[4096];
static char outbuf[4096];
const char *ptr = mystr;
get_amxstring_r(amx, params[parm++], mystr, sizeof(mystr)-1);
len = do_amx_format(amx, params, &parm, &ptr, outbuf, sizeof(outbuf)-1, 0);
return outbuf; return outbuf;
} }

View File

@ -59,13 +59,12 @@ public:
defentry() : definition(NULL) defentry() : definition(NULL)
{ {
}; };
defentry(const defentry &src)
{
definition = src.definition;
}
~defentry() ~defentry()
{ {
if (definition)
{
delete definition;
definition = NULL;
}
} }
String *definition; String *definition;
}; };
@ -184,6 +183,8 @@ public:
// When a language id in a format string in FormatAmxString is LANG_PLAYER, the glob id decides which language to take. // When a language id in a format string in FormatAmxString is LANG_PLAYER, the glob id decides which language to take.
void SetDefLang(int id); void SetDefLang(int id);
inline int GetDefLang() const { return m_CurGlobId; }
// Reset // Reset
void Clear(); void Clear();

View File

@ -107,8 +107,10 @@ public:
{ {
clear(); clear();
} else { } else {
Grow(strlen(d) + 1, false); size_t len = strlen(d);
strcpy(v, d); Grow(len + 1, false);
memcpy(v, d, len);
v[len] = '\0';
} }
} }

View File

@ -257,6 +257,7 @@ char* format_amxstring(AMX *amx, cell *params, int parm, int& len);
AMX* get_amxscript(int, void**, const char**); AMX* get_amxscript(int, void**, const char**);
const char* get_amxscriptname(AMX* amx); const char* get_amxscriptname(AMX* amx);
char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len); char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len);
size_t get_amxstring_r(AMX *amx, cell amx_addr, char *destination, int maxlen);
int amxstring_len(cell* cstr); int amxstring_len(cell* cstr);
int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug); int load_amxscript(AMX* amx, void** program, const char* path, char error[64], int debug);

View File

@ -249,10 +249,14 @@ int C_Spawn(edict_t *pent)
// ###### Load lang // ###### Load lang
char file[256]; char file[256];
g_langMngr.LoadCache(build_pathname_r(file, sizeof(file) - 1, "%s/dictionary.cache", get_localinfo("amxx_datadir", "addons/amxmodx/data"))); g_langMngr.LoadCache(build_pathname_r(file, sizeof(file) - 1, "%s/dictionary.cache", get_localinfo("amxx_datadir", "addons/amxmodx/data")));
DWORD stop,start=GetTickCount();
if (!g_langMngr.Load(build_pathname_r(file, sizeof(file) - 1, "%s/languages.dat", get_localinfo("amxmodx_datadir", "addons/amxmodx/data")))) if (!g_langMngr.Load(build_pathname_r(file, sizeof(file) - 1, "%s/languages.dat", get_localinfo("amxmodx_datadir", "addons/amxmodx/data"))))
{ {
LOG_MESSAGE(PLID, "Cache invalidated!");
g_langMngr.InvalidateCache(); g_langMngr.InvalidateCache();
} }
stop=GetTickCount();
LOG_MESSAGE(PLID, "CacheDB load time: %d milliseconds", stop-start);
// ###### Initialize commands prefixes // ###### Initialize commands prefixes
g_commands.registerPrefix("amx"); g_commands.registerPrefix("amx");

View File

@ -13,7 +13,7 @@
#include "sh_list.h" #include "sh_list.h"
#define _T_INIT_HASH_SIZE 32 #define _T_INIT_HASH_SIZE 128
//namespace SourceHook //namespace SourceHook
//{ //{
@ -43,7 +43,7 @@
K key; K key;
V val; V val;
}; };
typedef List<THashNode *> * NodePtr; typedef List<THashNode> * NodePtr;
public: public:
THash() : m_Buckets(NULL), m_numBuckets(0), m_percentUsed(0.0f), m_NumItems(0) THash() : m_Buckets(NULL), m_numBuckets(0), m_percentUsed(0.0f), m_NumItems(0)
{ {
@ -93,15 +93,10 @@
private: private:
void _Clear() void _Clear()
{ {
List<THashNode *>::iterator iter, begin, end;
for (size_t i=0; i<m_numBuckets; i++) for (size_t i=0; i<m_numBuckets; i++)
{ {
if (m_Buckets[i]) if (m_Buckets[i])
{ {
begin = m_Buckets[i]->begin();
end = m_Buckets[i]->end();
for (iter=begin; iter!=end; iter++)
delete (*iter);
delete m_Buckets[i]; delete m_Buckets[i];
m_Buckets[i] = NULL; m_Buckets[i] = NULL;
} }
@ -118,25 +113,33 @@
THashNode *pNode = NULL; THashNode *pNode = NULL;
if (!m_Buckets[place]) if (!m_Buckets[place])
{ {
m_Buckets[place] = new List<THashNode *>; m_Buckets[place] = new List<THashNode>;
pNode = new THashNode(key, V()); m_Buckets[place]->push_back(THashNode(key, V()));
m_Buckets[place]->push_back(pNode);
m_percentUsed += (1.0f / (float)m_numBuckets); m_percentUsed += (1.0f / (float)m_numBuckets);
m_NumItems++; m_NumItems++;
typename List<THashNode>::iterator iter;
iter = m_Buckets[place]->end();
iter--;
pNode = &(*iter);
} else { } else {
typename List<THashNode *>::iterator iter; typename List<THashNode>::iterator iter, end=m_Buckets[place]->end();
for (iter=m_Buckets[place]->begin(); iter!=m_Buckets[place]->end(); iter++) for (iter=m_Buckets[place]->begin(); iter!=end; iter++)
{ {
if (Compare((*iter)->key, key) == 0) if (Compare((*iter).key, key) == 0)
return (*iter); return &(*iter);
} }
//node does not exist //node does not exist
pNode = new THashNode(key, V()); m_Buckets[place]->push_back(THashNode(key, V()));
m_Buckets[place]->push_back(pNode);
m_NumItems++; m_NumItems++;
iter = m_Buckets[place]->end();
iter--;
pNode = &(*iter);
} }
if (PercentUsed() > 0.75f) if (PercentUsed() > 0.75f)
{
_Refactor(); _Refactor();
return _FindOrInsert(key);
}
return pNode; return pNode;
} }
void _Refactor() void _Refactor()
@ -151,7 +154,7 @@
} else { } else {
size_t oldSize = m_numBuckets; size_t oldSize = m_numBuckets;
m_numBuckets *= 2; m_numBuckets *= 2;
typename List<THashNode *>::iterator iter; typename List<THashNode>::iterator iter, end;
size_t place; size_t place;
THashNode *pHashNode; THashNode *pHashNode;
NodePtr *temp = new NodePtr[m_numBuckets]; NodePtr *temp = new NodePtr[m_numBuckets];
@ -164,18 +167,19 @@
if (m_Buckets[i]) if (m_Buckets[i])
{ {
//go through the list of items //go through the list of items
for (iter = m_Buckets[i]->begin(); iter != m_Buckets[i]->end(); iter++) end = m_Buckets[i]->end();
for (iter = m_Buckets[i]->begin(); iter!=end; iter++)
{ {
pHashNode = (*iter); pHashNode = &(*iter);
//rehash it with the new bucket filter //rehash it with the new bucket filter
place = HashFunction(pHashNode->key) % m_numBuckets; place = HashFunction(pHashNode->key) % m_numBuckets;
//add it to the new hash table //add it to the new hash table
if (!temp[place]) if (!temp[place])
{ {
temp[place] = new List<THashNode *>; temp[place] = new List<THashNode>;
m_percentUsed += (1.0f / (float)m_numBuckets); m_percentUsed += (1.0f / (float)m_numBuckets);
} }
temp[place]->push_back(pHashNode); temp[place]->push_back((*iter));
} }
//delete that bucket! //delete that bucket!
delete m_Buckets[i]; delete m_Buckets[i];
@ -219,19 +223,19 @@
} }
const THashNode & operator * () const const THashNode & operator * () const
{ {
return *(*iter); return (*iter);
} }
THashNode & operator * () THashNode & operator * ()
{ {
return *(*iter); return (*iter);
} }
const THashNode * operator ->() const const THashNode * operator ->() const
{ {
return (*iter); return &(*iter);
} }
THashNode * operator ->() THashNode * operator ->()
{ {
return (*iter); return &(*iter);
} }
bool operator ==(const iterator &where) const bool operator ==(const iterator &where) const
{ {
@ -308,7 +312,7 @@
} }
private: private:
int curbucket; int curbucket;
typename List<THashNode *>::iterator iter; typename List<THashNode>::iterator iter;
THash *hash; THash *hash;
bool end; bool end;
}; };

View File

@ -87,6 +87,20 @@ int set_amxstring(AMX *amx, cell amx_addr, const char *source, int max)
return dest - start; return dest - start;
} }
size_t get_amxstring_r(AMX *amx, cell amx_addr, char *destination, int maxlen)
{
register cell *source = (cell *)(amx->base + (int)(((AMX_HEADER *)amx->base)->dat + amx_addr));
register char *dest = destination;
char *start = dest;
while (*source && maxlen-- > 0)
*dest++=(char)(*source++);
if (dest)
*dest = '\0';
return --dest - start;
}
char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len) char* get_amxstring(AMX *amx, cell amx_addr, int id, int& len)
{ {
static char buffor[4][3072]; static char buffor[4][3072];