Index: ps/trunk/source/network/NMTCreator.h =================================================================== --- ps/trunk/source/network/NMTCreator.h (revision 23917) +++ ps/trunk/source/network/NMTCreator.h (revision 23918) @@ -1,326 +1,327 @@ -/* Copyright (C) 2015 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 GNU General Public License * along with 0 A.D. If not, see . */ #include "Serialization.h" #include // If included from within the NMT Creation process, perform a pass #ifdef CREATING_NMT #include NMT_CREATE_HEADER_NAME #undef START_NMTS #undef END_NMTS #undef START_NMT_CLASS #undef START_NMT_CLASS_DERIVED #undef NMT_FIELD_INT #undef NMT_FIELD #undef NMT_START_ARRAY #undef NMT_END_ARRAY #undef END_NMT_CLASS #else // If not within the creation process, and called with argument, perform the // creation process with the header specified #ifdef NMT_CREATE_HEADER_NAME #ifndef ARRAY_STRUCT_PREFIX #define ARRAY_STRUCT_PREFIX(_nm) S_##_nm #endif #define CREATING_NMT #ifndef NMT_CREATOR_IMPLEMENT /*************************************************************************/ // Pass 1, class definition #define NMT_CREATOR_PASS_CLASSDEF #define START_NMTS() #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) /** * Start the definition of a network message type. * * @param _base The name of the base class of the message * @param _nm The name of the class * @param _tp The NetMessageType associated with the class. It is *not* safe to * have several classes with the same value of _tp in the same executable */ #define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ CNetMessage *Deserialize##_nm(const u8 *, size_t); \ class _nm: public _base \ { \ protected: \ _nm(NetMessageType type): _base(type) {}\ \ /* This one is for subclasses that want to use the base class' string */ \ /* converters to get SubMessage { , ... } */ \ CStr ToStringRaw() const;\ public: \ _nm(): _base(_tp) {} \ virtual size_t GetSerializedLength() const; \ virtual u8 *Serialize(u8 *buffer) const; \ virtual const u8 *Deserialize(const u8 *pos, const u8 *end); \ virtual CStr ToString() const; \ inline operator CStr () const \ { return ToString(); } /** * Add an integer field to the message type. * * @param _nm The name of the field * @param _hosttp The local type of the field (the data type used in the field * definition) * @param _netsz The number of bytes that should be serialized. If the variable * has a value larger than the maximum value of the specified network size, * higher order bytes will be discarded. */ #define NMT_FIELD_INT(_nm, _hosttp, _netsz) \ _hosttp _nm; /** * Add a generic field to the message type. The data type must be a class * implementing the ISerializable interface * * @param _tp The local data type of the field * @param _nm The name of the field * @see ISerializable */ #define NMT_FIELD(_tp, _nm) \ _tp _nm; #define NMT_START_ARRAY(_nm) \ struct ARRAY_STRUCT_PREFIX(_nm); \ std::vector _nm; \ struct ARRAY_STRUCT_PREFIX(_nm) { #define NMT_END_ARRAY() \ }; #define END_NMT_CLASS() }; #include "NMTCreator.h" #undef NMT_CREATOR_PASS_CLASSDEF #else // NMT_CREATOR_IMPLEMENT #include "StringConverters.h" /*************************************************************************/ // Pass 2, GetSerializedLength #define NMT_CREATOR_PASS_GETLENGTH #define START_NMTS() #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) #define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ size_t _nm::GetSerializedLength() const \ { \ size_t ret=_base::GetSerializedLength(); \ const _nm *thiz=this;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_START_ARRAY(_nm) \ std::vector ::const_iterator it=_nm.begin(); \ while (it != _nm.end()) \ { \ const ARRAY_STRUCT_PREFIX(_nm) *thiz=&*it;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_END_ARRAY() \ ++it; \ } #define NMT_FIELD_INT(_nm, _hosttp, _netsz) \ ret += _netsz; #define NMT_FIELD(_tp, _nm) \ ret += thiz->_nm.GetSerializedLength(); #define END_NMT_CLASS() \ return ret; \ }; #include "NMTCreator.h" #undef NMT_CREATOR_PASS_GETLENGTH /*************************************************************************/ // Pass 3, Serialize #define NMT_CREATOR_PASS_SERIALIZE #define START_NMTS() #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) #define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ u8 *_nm::Serialize(u8 *buffer) const \ { \ /*printf("In " #_nm "::Serialize()\n");*/ \ u8 *pos=_base::Serialize(buffer); \ const _nm *thiz=this;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_START_ARRAY(_nm) \ std::vector ::const_iterator it=_nm.begin(); \ while (it != _nm.end()) \ { \ const ARRAY_STRUCT_PREFIX(_nm) *thiz=&*it;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_END_ARRAY() \ ++it; \ } #define NMT_FIELD_INT(_nm, _hosttp, _netsz) \ Serialize_int_##_netsz(pos, thiz->_nm); \ #define NMT_FIELD(_tp, _nm) \ pos=thiz->_nm.Serialize(pos); #define END_NMT_CLASS() \ return pos; \ } #include "NMTCreator.h" #undef NMT_CREATOR_PASS_SERIALIZE /*************************************************************************/ // Pass 4, Deserialize #define NMT_CREATOR_PASS_DESERIALIZE #define START_NMTS() #define END_NMTS() #define BAIL_DESERIALIZER return NULL #define START_NMT_CLASS(_nm, _tp) \ START_NMT_CLASS_DERIVED(CNetMessage, _nm, _tp) #define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ const u8 *_nm::Deserialize(const u8 *pos, const u8 *end) \ { \ pos=_base::Deserialize(pos, end); \ + if (pos == NULL) BAIL_DESERIALIZER;\ _nm *thiz=this; \ /*printf("In Deserialize" #_nm "\n"); */\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_START_ARRAY(_nm) \ while (pos < end) \ { \ ARRAY_STRUCT_PREFIX(_nm) *thiz=&*_nm.insert(_nm.end(), ARRAY_STRUCT_PREFIX(_nm)());\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_END_ARRAY() \ } #define NMT_FIELD_INT(_nm, _hosttp, _netsz) \ if (pos+_netsz > end) BAIL_DESERIALIZER; \ Deserialize_int_##_netsz(pos, thiz->_nm); \ /*printf("\t" #_nm " == 0x%x\n", thiz->_nm);*/ #define NMT_FIELD(_tp, _nm) \ if ((pos=thiz->_nm.Deserialize(pos, end)) == NULL) BAIL_DESERIALIZER; #define END_NMT_CLASS() \ return pos; \ } #include "NMTCreator.h" #undef BAIL_DESERIALIZER #undef NMT_CREATOR_PASS_DESERIALIZE /*************************************************************************/ // Pass 5, String Representation #define START_NMTS() #define END_NMTS() #define START_NMT_CLASS(_nm, _tp) \ CStr _nm::ToString() const \ { \ CStr ret=#_nm " { "; \ return ret + ToStringRaw() + " }"; \ } \ CStr _nm::ToStringRaw() const \ { \ CStr ret; \ const _nm *thiz=this;\ UNUSED2(thiz); // preempt any "unused" warning #define START_NMT_CLASS_DERIVED(_base, _nm, _tp) \ CStr _nm::ToString() const \ { \ CStr ret=#_nm " { "; \ return ret + ToStringRaw() + " }"; \ } \ CStr _nm::ToStringRaw() const \ { \ CStr ret=_base::ToStringRaw() + ", "; \ const _nm *thiz=this;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_START_ARRAY(_nm) \ ret+=#_nm ": { "; \ std::vector < ARRAY_STRUCT_PREFIX(_nm) >::const_iterator it=_nm.begin(); \ while (it != _nm.end()) \ { \ ret+=" { "; \ const ARRAY_STRUCT_PREFIX(_nm) *thiz=&*it;\ UNUSED2(thiz); // preempt any "unused" warning #define NMT_END_ARRAY() \ ++it; \ ret=ret.substr(0, ret.length()-2)+" }, "; \ } \ ret=ret.substr(0, ret.length()-2)+" }, "; #define NMT_FIELD_INT(_nm, _hosttp, _netsz) \ ret += #_nm ": "; \ ret += NetMessageStringConvert(thiz->_nm); \ ret += ", "; #define NMT_FIELD(_tp, _nm) \ ret += #_nm ": "; \ ret += NetMessageStringConvert(thiz->_nm); \ ret += ", "; #define END_NMT_CLASS() \ return ret.substr(0, ret.length()-2); \ } #include "NMTCreator.h" #endif // #ifdef NMT_CREATOR_IMPLEMENT /*************************************************************************/ // Cleanup #undef NMT_CREATE_HEADER_NAME #undef NMT_CREATOR_IMPLEMENT #undef CREATING_NMT #endif // #ifdef NMT_CREATE_HEADER_NAME #endif // #ifndef CREATING_NMT Index: ps/trunk/source/ps/CStr.cpp =================================================================== --- ps/trunk/source/ps/CStr.cpp (revision 23917) +++ ps/trunk/source/ps/CStr.cpp (revision 23918) @@ -1,534 +1,538 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2020 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version. * * 0 A.D. 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 GNU General Public License * along with 0 A.D. If not, see . */ /** * Description : Controls compilation of CStr class and * : includes some function implementations. **/ #include "precompiled.h" #ifndef CStr_CPP_FIRST #define CStr_CPP_FIRST #include "lib/fnv_hash.h" #include "lib/utf8.h" #include "lib/byte_order.h" #include "network/Serialization.h" #include #include #include #define UNIDOUBLER_HEADER "CStr.cpp" #include "UniDoubler.h" // Only include these function definitions in the first instance of CStr.cpp: /** * Convert CStr to UTF-8 * * @return CStr8 converted string **/ CStr8 CStrW::ToUTF8() const { Status err; return utf8_from_wstring(*this, &err); } /** * Convert UTF-8 to CStr * * @return CStrW converted string **/ CStrW CStr8::FromUTF8() const { Status err; return wstring_from_utf8(*this, &err); } #else // The following code is compiled twice, as CStrW then as CStr8: #include "CStr.h" #include #ifdef _UNICODE #define tstringstream wstringstream #define _istspace iswspace #define _totlower towlower #define _totupper towupper #else #define tstringstream stringstream #define _istspace isspace #define _totlower tolower #define _totupper toupper #endif CStr CStr::Repeat(const CStr& String, size_t Reps) { CStr ret; ret.reserve(String.length() * Reps); while (Reps--) ret += String; return ret; } // Construction from numbers: CStr CStr::FromInt(int n) { std::tstringstream ss; ss << n; return ss.str(); } CStr CStr::FromUInt(unsigned int n) { std::tstringstream ss; ss << n; return ss.str(); } CStr CStr::FromInt64(i64 n) { std::tstringstream ss; ss << n; return ss.str(); } CStr CStr::FromDouble(double n) { std::tstringstream ss; ss << n; return ss.str(); } // Conversion to numbers: int CStr::ToInt() const { int ret = 0; std::tstringstream str(*this); str >> ret; return ret; } unsigned int CStr::ToUInt() const { unsigned int ret = 0; std::tstringstream str(*this); str >> ret; return ret; } long CStr::ToLong() const { long ret = 0; std::tstringstream str(*this); str >> ret; return ret; } unsigned long CStr::ToULong() const { unsigned long ret = 0; std::tstringstream str(*this); str >> ret; return ret; } /** * libc++ and libstd++ differ on how they handle string-to-number parsing for floating-points numbers. * See https://trac.wildfiregames.com/ticket/2780#comment:4 for details. * To prevent this, only consider [0-9.-+], replace the others in-place with a neutral character. */ CStr ParseableAsNumber(CStr cleaned_copy) { for (tchar& c : cleaned_copy) if (!std::isdigit(c) && c != '.' && c != '-' && c != '+') c = ' '; return cleaned_copy; } float CStr::ToFloat() const { float ret = 0; std::tstringstream str(ParseableAsNumber(*this)); str >> ret; return ret; } double CStr::ToDouble() const { double ret = 0; std::tstringstream str(ParseableAsNumber(*this)); str >> ret; return ret; } // Search the string for another string long CStr::Find(const CStr& Str) const { size_t Pos = find(Str, 0); if (Pos != npos) return (long)Pos; return -1; } // Search the string for another string long CStr::Find(const tchar chr) const { size_t Pos = find(chr, 0); if (Pos != npos) return (long)Pos; return -1; } // Search the string for another string long CStr::Find(const int start, const tchar chr) const { size_t Pos = find(chr, start); if (Pos != npos) return (long)Pos; return -1; } long CStr::FindInsensitive(const int start, const tchar chr) const { return LowerCase().Find(start, _totlower(chr)); } long CStr::FindInsensitive(const tchar chr) const { return LowerCase().Find(_totlower(chr)); } long CStr::FindInsensitive(const CStr& Str) const { return LowerCase().Find(Str.LowerCase()); } long CStr::ReverseFind(const CStr& Str) const { size_t Pos = rfind(Str, length() ); if (Pos != npos) return (long)Pos; return -1; } // Lowercase and uppercase CStr CStr::LowerCase() const { std::tstring NewString = *this; for (size_t i = 0; i < length(); i++) NewString[i] = (tchar)_totlower((*this)[i]); return NewString; } CStr CStr::UpperCase() const { std::tstring NewString = *this; for (size_t i = 0; i < length(); i++) NewString[i] = (tchar)_totupper((*this)[i]); return NewString; } // Retrieve the substring of the first n characters CStr CStr::Left(size_t len) const { ENSURE(len <= length()); return substr(0, len); } // Retrieve the substring of the last n characters CStr CStr::Right(size_t len) const { ENSURE(len <= length()); return substr(length()-len, len); } // Retrieve the substring following the last occurrence of Str // (or the whole string if it doesn't contain Str) CStr CStr::AfterLast(const CStr& Str, size_t startPos) const { size_t pos = rfind(Str, startPos); if (pos == npos) return *this; else return substr(pos + Str.length()); } // Retrieve the substring preceding the last occurrence of Str // (or the whole string if it doesn't contain Str) CStr CStr::BeforeLast(const CStr& Str, size_t startPos) const { size_t pos = rfind(Str, startPos); if (pos == npos) return *this; else return substr(0, pos); } // Retrieve the substring following the first occurrence of Str // (or the whole string if it doesn't contain Str) CStr CStr::AfterFirst(const CStr& Str, size_t startPos) const { size_t pos = find(Str, startPos); if (pos == npos) return *this; else return substr(pos + Str.length()); } // Retrieve the substring preceding the first occurrence of Str // (or the whole string if it doesn't contain Str) CStr CStr::BeforeFirst(const CStr& Str, size_t startPos) const { size_t pos = find(Str, startPos); if (pos == npos) return *this; else return substr(0, pos); } // Remove all occurrences of some character or substring void CStr::Remove(const CStr& Str) { size_t FoundAt = 0; while (FoundAt != npos) { FoundAt = find(Str, 0); if (FoundAt != npos) erase(FoundAt, Str.length()); } } // Replace all occurrences of some substring by another void CStr::Replace(const CStr& ToReplace, const CStr& ReplaceWith) { size_t Pos = 0; while (Pos != npos) { Pos = find(ToReplace, Pos); if (Pos != npos) { erase(Pos, ToReplace.length()); insert(Pos, ReplaceWith); Pos += ReplaceWith.length(); } } } std::string CStr::EscapeToPrintableASCII() const { std::string NewString; for (size_t i = 0; i < length(); i++) { tchar ch = (*this)[i]; if (ch == '"') NewString += "\\\""; else if (ch == '\\') NewString += "\\\\"; else if (ch == '\b') NewString += "\\b"; else if (ch == '\f') NewString += "\\f"; else if (ch == '\n') NewString += "\\n"; else if (ch == '\r') NewString += "\\r"; else if (ch == '\t') NewString += "\\t"; else if (ch >= 32 && ch <= 126) NewString += ch; else { std::stringstream ss; ss << "\\u" << std::hex << std::setfill('0') << std::setw(4) << (int)(unsigned char)ch; NewString += ss.str(); } } return NewString; } // Returns a trimmed string, removes whitespace from the left/right/both CStr CStr::Trim(PS_TRIM_MODE Mode) const { size_t Left = 0, Right = 0; switch (Mode) { case PS_TRIM_LEFT: { for (Left = 0; Left < length(); Left++) if (_istspace((*this)[Left]) == false) break; // end found, trim 0 to Left-1 inclusive } break; case PS_TRIM_RIGHT: { Right = length(); while (Right--) if (_istspace((*this)[Right]) == false) break; // end found, trim len-1 to Right+1 inclusive } break; case PS_TRIM_BOTH: { for (Left = 0; Left < length(); Left++) if (_istspace((*this)[Left]) == false) break; // end found, trim 0 to Left-1 inclusive Right = length(); while (Right--) if (_istspace((*this)[Right]) == false) break; // end found, trim len-1 to Right+1 inclusive } break; default: debug_warn(L"CStr::Trim: invalid Mode"); } return substr(Left, Right-Left+1); } CStr CStr::Pad(PS_TRIM_MODE Mode, size_t Length) const { size_t Left = 0, Right = 0; if (Length <= length()) return *this; // From here: Length-length() >= 1 switch (Mode) { case PS_TRIM_LEFT: Left = Length - length(); break; case PS_TRIM_RIGHT: Right = Length - length(); break; case PS_TRIM_BOTH: Left = (Length - length() + 1)/2; Right = (Length - length() - 1)/2; // cannot be negative break; default: debug_warn(L"CStr::Trim: invalid Mode"); } return std::tstring(Left, _T(' ')) + *this + std::tstring(Right, _T(' ')); } size_t CStr::GetHashCode() const { return (size_t)fnv_hash(data(), length()*sizeof(value_type)); // janwas 2005-03-18: now use 32-bit version; 64 is slower and // the result was truncated down to 32 anyway. } #ifdef _UNICODE /* CStrW is always serialized to/from UTF-16 */ u8* CStrW::Serialize(u8* buffer) const { size_t len = length(); size_t i = 0; for (i = 0; i < len; i++) { const u16 bigEndian = to_be16((*this)[i]); *(u16 *)(buffer + i*2) = bigEndian; } *(u16 *)(buffer + i*2) = 0; return buffer + len*2 + 2; } const u8* CStrW::Deserialize(const u8* buffer, const u8* bufferend) { + ENSURE(buffer); + ENSURE(bufferend); const u16 *strend = (const u16 *)buffer; while ((const u8 *)strend < bufferend && *strend) strend++; if ((const u8 *)strend >= bufferend) return NULL; resize(strend - (const u16 *)buffer); const u16 *ptr = (const u16 *)buffer; std::wstring::iterator str = begin(); while (ptr < strend) { const u16 native = to_be16(*(ptr++)); // we want from_be16, but that's the same *(str++) = (tchar)native; } return (const u8 *)(strend+1); } size_t CStr::GetSerializedLength() const { return size_t(length()*2 + 2); } #else /* CStr8 is always serialized to/from ASCII (or whatever 8-bit codepage stored in the CStr) */ u8* CStr8::Serialize(u8* buffer) const { size_t len = length(); Serialize_int_4(buffer, (u32)len); size_t i = 0; for (i = 0; i < len; i++) buffer[i] = (*this)[i]; return buffer + len; } const u8* CStr8::Deserialize(const u8* buffer, const u8* bufferend) { + ENSURE(buffer); + ENSURE(bufferend); u32 len; Deserialize_int_4(buffer, len); if (buffer + len > bufferend) return NULL; *this = std::string(buffer, buffer + len); return buffer + len; } size_t CStr::GetSerializedLength() const { return length() + 4; } #endif // _UNICODE // Clean up, to keep the second pass through unidoubler happy #undef tstringstream #undef _tstod #undef _ttoi #undef _ttol #undef _istspace #undef _totlower #undef _totupper #endif // CStr_CPP_FIRST