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