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:
		| @@ -324,277 +324,244 @@ 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]; | ||||||
| 	int status; | 	size_t len = 0; | ||||||
| 	 | 	char *fmtptr = fmt[level]; | ||||||
| 	// the output buffer | 	const char *fmtsrc = *fmtstr; | ||||||
| 	static char outbuf[4096]; | 	char ctrl_code; | ||||||
| 	char *outptr = outbuf; | 	int numParams = params[0] / sizeof(cell); | ||||||
| 	cell *src = get_amxaddr(amx, params[parm++]); | 	if (*param > numParams) | ||||||
|  |  | ||||||
| 	while (*src) |  | ||||||
| 	{ | 	{ | ||||||
| 		if (*src == '%') | 		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': | ||||||
| 		{ | 		{ | ||||||
| 			++src; | 			get_amxstring_r(amx, _addr, tmp_buf, 2047); | ||||||
| 			if (*src == 'L') | 			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 tmpLen; | ||||||
|  | 			if (addr[0] == LANG_PLAYER) | ||||||
| 			{ | 			{ | ||||||
| 				cell langName = params[parm];		// "en" case (langName contains the address to the string) | 				if ( (int)CVAR_GET_FLOAT("amx_client_languages") == 0 ) | ||||||
| 				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) | 					pLangName = g_vault.get("server_language"); | ||||||
| 					{ | 				} else { | ||||||
| 						cpLangName = g_vault.get("server_language"); | 					pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(g_langMngr.GetDefLang())->pEdict, "lang"); | ||||||
| 					} else { |  | ||||||
| 						cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(m_CurGlobId)->pEdict, "lang"); |  | ||||||
| 					} |  | ||||||
| 				} | 				} | ||||||
| 				else if (*pAmxLangName == LANG_SERVER) // LANG_SERVER | 			} else if (addr[0] == 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 { | ||||||
| 				else if (*pAmxLangName >= 1 && *pAmxLangName <= 32) // Direct Client Id | 					pLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(addr[0])->pEdict, "lang"); | ||||||
| 				{ |  | ||||||
| 					if ((int)CVAR_GET_FLOAT("amx_client_languages") == 0) |  | ||||||
| 					{ |  | ||||||
| 						cpLangName = g_vault.get("server_language"); |  | ||||||
| 					} else { |  | ||||||
| 						cpLangName = ENTITY_KEYVALUE(GET_PLAYER_POINTER_I(*pAmxLangName)->pEdict, "lang"); |  | ||||||
| 					} |  | ||||||
| 				} else {	// Language Name |  | ||||||
| 					int tmplen = 0; |  | ||||||
| 					cpLangName = get_amxstring(amx, langName, 2, tmplen); |  | ||||||
| 				} |  | ||||||
| 				 |  | ||||||
| 				if (!cpLangName || strlen(cpLangName) < 1) |  | ||||||
| 					cpLangName = "en"; |  | ||||||
| 				 |  | ||||||
| 				int tmplen = 0; |  | ||||||
| 				NEXT_PARAM(); |  | ||||||
| 				char *key = get_amxstring(amx, params[parm++], 1, tmplen); |  | ||||||
| 				const char *def = GetDef(cpLangName, key, status); |  | ||||||
|  |  | ||||||
| 				if (def == NULL) |  | ||||||
| 				{ |  | ||||||
| 					bool a = true; |  | ||||||
| 					if (status == LANG_STATUS_LNOTFOUND) |  | ||||||
| 					{ |  | ||||||
| 						AMXXLOG_Log("[AMXX] Language \"%s\" not found", cpLangName); |  | ||||||
| 					} |  | ||||||
| 					else if (status == LANG_STATUS_KLNOTFOUND) |  | ||||||
| 					{ |  | ||||||
| 						a = false; |  | ||||||
| 						AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, cpLangName); |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					if (*pAmxLangName != LANG_SERVER) |  | ||||||
| 					{ |  | ||||||
| 						def = GetDef(g_vault.get("server_language"), key, status); |  | ||||||
| 					} |  | ||||||
| 					 |  | ||||||
| 					if (!def && (strcmp(cpLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0)) |  | ||||||
| 					{ |  | ||||||
| 						def = GetDef("en", key, status); |  | ||||||
| 					} |  | ||||||
|  |  | ||||||
| 					if (!def) |  | ||||||
| 					{ |  | ||||||
| 						static char buf[512]; |  | ||||||
| 						CHECK_PTR((char*)(buf + 17 + strlen(key)), buf, sizeof(buf)); |  | ||||||
| 						sprintf(buf, "ML_LNOTFOUND: %s", key); |  | ||||||
| 						def = buf; |  | ||||||
| 						 |  | ||||||
| 						if (a) |  | ||||||
| 							AMXXLOG_Log("[AMXX] Language key \"%s\" not found, check \"%s\"", key, GetFileName(amx)); |  | ||||||
| 					}				 |  | ||||||
| 				} |  | ||||||
|  |  | ||||||
| 				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: |  | ||||||
| 								{ |  | ||||||
| 									CHECK_OUTPTR(strlen(format) + 1); |  | ||||||
| 									strcpy(outptr, format);	 |  | ||||||
| 									break; |  | ||||||
| 								} |  | ||||||
| 							} |  | ||||||
| 							 |  | ||||||
| 							outptr += strlen(outptr); |  | ||||||
| 						} |  | ||||||
| 					} |  | ||||||
| 					else if (*def == '^') |  | ||||||
| 					{ |  | ||||||
| 						++def; |  | ||||||
| 						 |  | ||||||
| 						switch (*def) |  | ||||||
| 						{ |  | ||||||
| 							case 'n': |  | ||||||
| 								CHECK_OUTPTR(1); |  | ||||||
| 								*outptr++ = '\n'; |  | ||||||
| 								break; |  | ||||||
| 							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 { | 			} else { | ||||||
| 				static char tmpString[4096]; | 				pLangName = get_amxstring(amx, _addr, 0, tmpLen); | ||||||
| 				char *tmpPtr = tmpString; | 			} | ||||||
| 				int tmpLen = 0; | 			if (!pLangName || !isalpha(pLangName[0])) | ||||||
| 				static char format[32] = {'%'}; | 				pLangName = "en"; | ||||||
| 				char *ptr = format + 1; | 			//next parameter! | ||||||
|  | 			_addr = params[*param]; | ||||||
|  | 			(*param)++; | ||||||
|  | 			key = get_amxstring(amx, _addr, 1, tmpLen); | ||||||
|  | 			def = g_langMngr.GetDef(pLangName, key, status); | ||||||
| 				 | 				 | ||||||
| 				if (*src != '%') | 			if (def == NULL) | ||||||
|  | 			{ | ||||||
|  | 				bool a = true; | ||||||
|  | 				if (status == LANG_STATUS_LNOTFOUND) | ||||||
| 				{ | 				{ | ||||||
| 					while (*src != 0 && ptr-format<sizeof(format) && !isalpha(*ptr++ = static_cast<char>(*src++))) | 					AMXXLOG_Log("[AMXX] Language \"%s\" not found", pLangName); | ||||||
| 						/*nothing*/; | 				} else if (status == LANG_STATUS_KLNOTFOUND) { | ||||||
| 					*ptr = 0; | 					a = false; | ||||||
| 					ZEROTERM(format); | 					AMXXLOG_Log("[AMXX] Language key \"%s\" not found for language \"%s\"", key, pLangName); | ||||||
| 					--src; | 				} | ||||||
| 					 |  | ||||||
| 					switch (*(ptr - 1)) | 				if (addr[0] != LANG_SERVER) | ||||||
|  | 					def = g_langMngr.GetDef(g_vault.get("server_language"), key, status); | ||||||
|  | 				 | ||||||
|  | 				if (!def && (strcmp(pLangName, "en") != 0 && strcmp(g_vault.get("server_language"), "en") != 0)) | ||||||
|  | 					def = g_langMngr.GetDef("en", key, status); | ||||||
|  |  | ||||||
|  | 				if (!def) | ||||||
|  | 				{ | ||||||
|  | 					return _snprintf(output, maxlen, "ML_NOTFOUND: %s", key); | ||||||
|  | 					if (a) | ||||||
|  | 						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); | ||||||
|  | 		} | ||||||
|  | 	default: | ||||||
|  | 		{ | ||||||
|  | 			return _snprintf(output, maxlen, "%s", fmtptr); | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #define DUMP_CP_BUFFER(expr) \ | ||||||
|  | 	if (sofar > 0) { \ | ||||||
|  | 		if (sofar <= (int)maxlen) { \ | ||||||
|  | 			memcpy(output, save, sofar); \ | ||||||
|  | 			output += sofar; \ | ||||||
|  | 			maxlen -= sofar; \ | ||||||
|  | 			sofar = 0; \ | ||||||
|  | 		} else { \ | ||||||
|  | 			expr; \ | ||||||
|  | 		} \ | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 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) | ||||||
|  | 	{ | ||||||
|  | 		switch (*lexptr) | ||||||
|  | 		{ | ||||||
|  | 		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 's': | 						case 'n': | ||||||
| 						{ | 						{ | ||||||
| 							NEXT_PARAM(); | 							*output++ = '\n'; | ||||||
| 							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; | 							break; | ||||||
| 						} | 						} | ||||||
| 						case 'g': | 						case 't': | ||||||
| 						case 'f': |  | ||||||
| 						{ | 						{ | ||||||
| 							NEXT_PARAM(); | 							*output++ = '\t'; | ||||||
| 							_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++; | ||||||
| 					outptr += strlen(outptr); | 					maxlen--; | ||||||
| 				} else { | 					save = lexptr; | ||||||
| 					CHECK_OUTPTR(1); | 					break; | ||||||
| 					*outptr++ = '%'; |  | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { | 		default: | ||||||
| 			CHECK_OUTPTR(1); | 			{ | ||||||
| 			*outptr++ = static_cast<char>(*src); | 				lexptr++; | ||||||
|  | 				sofar++; | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 		++src; |  | ||||||
| 	} | 	} | ||||||
| 	 | 	DUMP_CP_BUFFER(;); | ||||||
| 	len = outptr - outbuf; | 	*output = '\0'; | ||||||
| 	CHECK_OUTPTR(1); |  | ||||||
| 	*outptr++ = 0; | 	*lex = lexptr; | ||||||
| 	 |  | ||||||
|  | 	return (orig_maxlen-maxlen); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | 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; | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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(); | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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'; | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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); | ||||||
|   | |||||||
| @@ -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"); | ||||||
|   | |||||||
| @@ -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; | ||||||
| 		}; | 		}; | ||||||
|   | |||||||
| @@ -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]; | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user