mirror of
https://github.com/RetroShare/RetroShare.git
synced 2025-06-27 23:57:38 -04:00
Fix memory management and deprecated removal from serialization
Fix missing RsDiscPgpKeyItem initialization Fix inconsistent new[]/delete[] usage in RsDiscPgpKeyItem and PGPHandler::exportPublicKey which now consistently uses malloc/free Remove deprecated RsGenericSerializer::FORMAT_* Move from deprecated RsServiceSerializer::SERIALIZATION_FLAG_* to RsSerializationFlags Solve a bunch of compiler warnings Stricter checks in SerializeContext costructor
This commit is contained in:
parent
39bde58c29
commit
5610cc8600
24 changed files with 230 additions and 244 deletions
|
@ -4,6 +4,8 @@
|
|||
* libretroshare: retroshare core library *
|
||||
* *
|
||||
* Copyright (C) 2016 Cyril Soler <csoler@users.sourceforge.net> *
|
||||
* Copyright (C) 2020 Gioacchino Mazzurco <gio@eigenlab.org> *
|
||||
* Copyright (C) 2020 Asociación Civil Altermundi <info@altermundi.net> *
|
||||
* *
|
||||
* This program is free software: you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU Lesser General Public License as *
|
||||
|
@ -18,23 +20,17 @@
|
|||
* You should have received a copy of the GNU Lesser General Public License *
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>. *
|
||||
* *
|
||||
*******************************************************************************/
|
||||
******************************************************************************/
|
||||
|
||||
#include <typeinfo>
|
||||
|
||||
#include "rsitems/rsitem.h"
|
||||
|
||||
#include "util/rsprint.h"
|
||||
#include "serialiser/rsserializer.h"
|
||||
#include "serialiser/rstypeserializer.h"
|
||||
#include "util/stacktrace.h"
|
||||
#include "util/rsdebug.h"
|
||||
|
||||
const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_NONE ( 0x0000 );
|
||||
const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_CONFIG ( 0x0001 );
|
||||
const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_SIGNATURE ( 0x0002 );
|
||||
const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_SKIP_HEADER ( 0x0004 );
|
||||
const SerializationFlags RsGenericSerializer::SERIALIZATION_FLAG_YIELDING ( 0x0008 );
|
||||
|
||||
RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size)
|
||||
{
|
||||
if(!data || !size || !*size)
|
||||
|
@ -47,11 +43,13 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER)
|
||||
{
|
||||
std::cerr << "(EE) Cannot deserialise item with flags SERIALIZATION_FLAG_SKIP_HEADER. Check your code!" << std::endl;
|
||||
return NULL ;
|
||||
}
|
||||
if(!!(mFlags & RsSerializationFlags::SKIP_HEADER))
|
||||
{
|
||||
RsErr() << __PRETTY_FUNCTION__ << " Cannot deserialise item with flag "
|
||||
<< "SKIP_HEADER. Check your code!" << std::endl;
|
||||
print_stacktrace();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t rstype = getRsItemId(const_cast<void*>((const void*)data)) ;
|
||||
|
||||
|
@ -64,7 +62,9 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size)
|
|||
return NULL ;
|
||||
}
|
||||
|
||||
SerializeContext ctx(const_cast<uint8_t*>(static_cast<uint8_t*>(data)),*size,mFormat,mFlags);
|
||||
SerializeContext ctx(
|
||||
const_cast<uint8_t*>(static_cast<uint8_t*>(data)), *size,
|
||||
mFlags );
|
||||
ctx.mOffset = 8 ;
|
||||
|
||||
item->serial_process(RsGenericSerializer::DESERIALIZE, ctx) ;
|
||||
|
@ -85,11 +85,13 @@ RsItem *RsServiceSerializer::deserialise(void *data, uint32_t *size)
|
|||
}
|
||||
RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size)
|
||||
{
|
||||
if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER)
|
||||
{
|
||||
std::cerr << "(EE) Cannot deserialise item with flags SERIALIZATION_FLAG_SKIP_HEADER. Check your code!" << std::endl;
|
||||
return NULL ;
|
||||
}
|
||||
if(!!(mFlags & RsSerializationFlags::SKIP_HEADER))
|
||||
{
|
||||
RsErr() << __PRETTY_FUNCTION__ << " Cannot deserialise item with flag "
|
||||
<< "SKIP_HEADER. Check your code!" << std::endl;
|
||||
print_stacktrace();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint32_t rstype = getRsItemId(const_cast<void*>((const void*)data)) ;
|
||||
|
||||
|
@ -102,7 +104,9 @@ RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size)
|
|||
return NULL ;
|
||||
}
|
||||
|
||||
SerializeContext ctx(const_cast<uint8_t*>(static_cast<uint8_t*>(data)),*size,mFormat,mFlags);
|
||||
SerializeContext ctx(
|
||||
const_cast<uint8_t*>(static_cast<uint8_t*>(data)), *size,
|
||||
mFlags );
|
||||
ctx.mOffset = 8 ;
|
||||
|
||||
item->serial_process(DESERIALIZE, ctx) ;
|
||||
|
@ -121,50 +125,44 @@ RsItem *RsConfigSerializer::deserialise(void *data, uint32_t *size)
|
|||
delete item ;
|
||||
return NULL ;
|
||||
}
|
||||
bool RsGenericSerializer::serialise(RsItem *item,void *data,uint32_t *size)
|
||||
|
||||
bool RsGenericSerializer::serialise(RsItem* item, void* data, uint32_t* size)
|
||||
{
|
||||
SerializeContext ctx(static_cast<uint8_t*>(data),0,mFormat,mFlags);
|
||||
uint32_t tlvsize = this->size(item);
|
||||
|
||||
uint32_t tlvsize = this->size(item) ;
|
||||
constexpr auto fName = __PRETTY_FUNCTION__;
|
||||
const auto failure = [=](std::error_condition ec)
|
||||
{
|
||||
RsErr() << fName << " " << ec << std::endl;
|
||||
print_stacktrace();
|
||||
return false;
|
||||
};
|
||||
|
||||
if(tlvsize > *size)
|
||||
throw std::runtime_error("Cannot serialise: not enough room.") ;
|
||||
if(tlvsize > *size) return failure(std::errc::no_buffer_space);
|
||||
|
||||
SerializeContext ctx(static_cast<uint8_t*>(data), tlvsize, mFlags);
|
||||
|
||||
if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER)
|
||||
ctx.mOffset = 0;
|
||||
else
|
||||
if(!(mFlags & RsSerializationFlags::SKIP_HEADER))
|
||||
{
|
||||
if(!setRsItemHeader(data, tlvsize, item->PacketId(), tlvsize))
|
||||
{
|
||||
std::cerr << "RsSerializer::serialise_item(): ERROR. Not enough size!" << std::endl;
|
||||
return false ;
|
||||
}
|
||||
return failure(std::errc::no_buffer_space);
|
||||
ctx.mOffset = 8;
|
||||
}
|
||||
|
||||
ctx.mSize = tlvsize;
|
||||
item->serial_process(RsGenericSerializer::SERIALIZE,ctx);
|
||||
|
||||
item->serial_process(RsGenericSerializer::SERIALIZE,ctx) ;
|
||||
if(ctx.mSize != ctx.mOffset) return failure(std::errc::message_size);
|
||||
|
||||
if(ctx.mSize != ctx.mOffset)
|
||||
{
|
||||
std::cerr << "RsSerializer::serialise(): ERROR. offset does not match expected size!" << std::endl;
|
||||
return false ;
|
||||
}
|
||||
*size = ctx.mOffset ;
|
||||
|
||||
return true ;
|
||||
*size = ctx.mOffset;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t RsGenericSerializer::size(RsItem *item)
|
||||
{
|
||||
SerializeContext ctx(NULL,0,mFormat,mFlags);
|
||||
SerializeContext ctx(nullptr, 0, mFlags);
|
||||
|
||||
if(mFlags & SERIALIZATION_FLAG_SKIP_HEADER)
|
||||
ctx.mOffset = 0;
|
||||
else
|
||||
ctx.mOffset = 8 ; // header size
|
||||
if(!!(mFlags & RsSerializationFlags::SKIP_HEADER)) ctx.mOffset = 0;
|
||||
else ctx.mOffset = 8; // header size
|
||||
item->serial_process(SIZE_ESTIMATE, ctx) ;
|
||||
|
||||
return ctx.mOffset ;
|
||||
|
@ -172,7 +170,7 @@ uint32_t RsGenericSerializer::size(RsItem *item)
|
|||
|
||||
void RsGenericSerializer::print(RsItem *item)
|
||||
{
|
||||
SerializeContext ctx(NULL,0,mFormat,mFlags);
|
||||
SerializeContext ctx(nullptr, 0, mFlags);
|
||||
|
||||
std::cerr << "***** RsItem class: \"" << typeid(*item).name() << "\" *****" << std::endl;
|
||||
item->serial_process(PRINT, ctx) ;
|
||||
|
@ -255,7 +253,7 @@ RsItem *RsRawSerialiser::deserialise(void *data, uint32_t *pktsize)
|
|||
|
||||
|
||||
RsGenericSerializer::SerializeContext::SerializeContext(
|
||||
uint8_t* data, uint32_t size, SerializationFlags flags,
|
||||
uint8_t* data, uint32_t size, RsSerializationFlags flags,
|
||||
RsJson::AllocatorType* allocator ) :
|
||||
mData(data), mSize(size), mOffset(0), mOk(true), mFlags(flags),
|
||||
mJson(rapidjson::kObjectType, allocator)
|
||||
|
@ -264,20 +262,23 @@ RsGenericSerializer::SerializeContext::SerializeContext(
|
|||
{
|
||||
if(size == 0)
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " data passed without "
|
||||
<< "size! This make no sense report to developers!"
|
||||
<< std::endl;
|
||||
RsFatal() << __PRETTY_FUNCTION__ << " data passed without "
|
||||
<< "size! This make no sense report to developers!"
|
||||
<< std::endl;
|
||||
print_stacktrace();
|
||||
exit(-EINVAL);
|
||||
}
|
||||
|
||||
if(flags & SERIALIZATION_FLAG_YIELDING)
|
||||
if(!!(flags & RsSerializationFlags::YIELDING))
|
||||
{
|
||||
std::cerr << __PRETTY_FUNCTION__ << " Attempt to create a "
|
||||
RsFatal() << __PRETTY_FUNCTION__
|
||||
<< " Attempt to create a "
|
||||
<< "binary serialization context with "
|
||||
<< "SERIALIZATION_FLAG_YIELDING! "
|
||||
<< "This make no sense report to developers!"
|
||||
<< std::endl;
|
||||
print_stacktrace();
|
||||
exit(-EINVAL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -238,58 +238,23 @@ struct RsGenericSerializer : RsSerialType
|
|||
FROM_JSON
|
||||
} SerializeJob;
|
||||
|
||||
|
||||
/** @deprecated use SerializeJob instead */
|
||||
RS_DEPRECATED typedef enum
|
||||
{
|
||||
FORMAT_BINARY = 0x01,
|
||||
FORMAT_JSON = 0x02
|
||||
} SerializationFormat;
|
||||
|
||||
struct SerializeContext
|
||||
{
|
||||
/** Allow shared allocator usage to avoid costly JSON deepcopy for
|
||||
* nested RsSerializable */
|
||||
SerializeContext(
|
||||
uint8_t* data = nullptr, uint32_t size = 0,
|
||||
SerializationFlags flags = SERIALIZATION_FLAG_NONE,
|
||||
RsSerializationFlags flags = RsSerializationFlags::NONE,
|
||||
RsJson::AllocatorType* allocator = nullptr);
|
||||
|
||||
RS_DEPRECATED SerializeContext(
|
||||
uint8_t *data, uint32_t size, SerializationFormat format,
|
||||
SerializationFlags flags,
|
||||
RsJson::AllocatorType* allocator = nullptr) :
|
||||
mData(data), mSize(size), mOffset(0), mOk(true), mFormat(format),
|
||||
mFlags{flags}, mJson(rapidjson::kObjectType, allocator) {}
|
||||
|
||||
unsigned char *mData;
|
||||
uint32_t mSize;
|
||||
uint32_t mOffset;
|
||||
bool mOk;
|
||||
RS_DEPRECATED SerializationFormat mFormat;
|
||||
SerializationFlags mFlags;
|
||||
RsSerializationFlags mFlags;
|
||||
RsJson mJson;
|
||||
};
|
||||
|
||||
/** These are convenience flags to be used by the items when processing the
|
||||
* data. The names of the flags are not very important. What matters is that
|
||||
* the serial_process() method of each item correctly deals with the data
|
||||
* when it sees the flags, if the serialiser sets them.
|
||||
* By default the flags are not set and shouldn't be handled.
|
||||
* When deriving a new serializer, the user can set his own flags, using
|
||||
* compatible values
|
||||
*/
|
||||
static const SerializationFlags SERIALIZATION_FLAG_NONE; // 0x0000
|
||||
static const SerializationFlags SERIALIZATION_FLAG_CONFIG; // 0x0001
|
||||
static const SerializationFlags SERIALIZATION_FLAG_SIGNATURE; // 0x0002
|
||||
static const SerializationFlags SERIALIZATION_FLAG_SKIP_HEADER; // 0x0004
|
||||
|
||||
/** Used for JSON deserialization in JSON API, it causes the deserialization
|
||||
* to continue even if some field is missing (or incorrect), this way the
|
||||
* API is more user friendly as some methods need just part of the structs
|
||||
* they take as parameters. */
|
||||
static const SerializationFlags SERIALIZATION_FLAG_YIELDING; // 0x0008
|
||||
|
||||
/**
|
||||
* The following functions overload RsSerialType.
|
||||
* They *should not* need to be further overloaded.
|
||||
|
@ -302,18 +267,15 @@ struct RsGenericSerializer : RsSerialType
|
|||
protected:
|
||||
RsGenericSerializer(
|
||||
uint8_t serial_class, uint8_t serial_type,
|
||||
SerializationFormat format, SerializationFlags flags ) :
|
||||
RsSerializationFlags flags ):
|
||||
RsSerialType( RS_PKT_VERSION1, serial_class, serial_type),
|
||||
mFormat(format), mFlags(flags) {}
|
||||
|
||||
RsGenericSerializer(
|
||||
uint16_t service, SerializationFormat format,
|
||||
SerializationFlags flags ) :
|
||||
RsSerialType( RS_PKT_VERSION_SERVICE, service ), mFormat(format),
|
||||
mFlags(flags) {}
|
||||
|
||||
SerializationFormat mFormat;
|
||||
SerializationFlags mFlags;
|
||||
RsGenericSerializer(
|
||||
uint16_t service, RsSerializationFlags flags ):
|
||||
RsSerialType( RS_PKT_VERSION_SERVICE, service ), mFlags(flags) {}
|
||||
|
||||
RsSerializationFlags mFlags;
|
||||
};
|
||||
|
||||
|
||||
|
@ -323,9 +285,9 @@ protected:
|
|||
struct RsServiceSerializer : RsGenericSerializer
|
||||
{
|
||||
RsServiceSerializer(
|
||||
uint16_t service_id, SerializationFormat format = FORMAT_BINARY,
|
||||
SerializationFlags flags = SERIALIZATION_FLAG_NONE ) :
|
||||
RsGenericSerializer(service_id, format, flags) {}
|
||||
uint16_t service_id,
|
||||
RsSerializationFlags flags = RsSerializationFlags::NONE ) :
|
||||
RsGenericSerializer(service_id, flags) {}
|
||||
|
||||
/*! should be overloaded to create the correct type of item depending on the
|
||||
* data */
|
||||
|
@ -342,11 +304,10 @@ struct RsServiceSerializer : RsGenericSerializer
|
|||
*/
|
||||
struct RsConfigSerializer : RsGenericSerializer
|
||||
{
|
||||
RsConfigSerializer(uint8_t config_class,
|
||||
uint8_t config_type,
|
||||
SerializationFormat format = FORMAT_BINARY,
|
||||
SerializationFlags flags = SERIALIZATION_FLAG_NONE) :
|
||||
RsGenericSerializer(config_class,config_type,format,flags) {}
|
||||
RsConfigSerializer(
|
||||
uint8_t config_class, uint8_t config_type,
|
||||
RsSerializationFlags flags = RsSerializationFlags::NONE ) :
|
||||
RsGenericSerializer(config_class, config_type, flags) {}
|
||||
|
||||
/*! should be overloaded to create the correct type of item depending on the
|
||||
* data */
|
||||
|
|
|
@ -542,7 +542,7 @@ void RsTypeSerializer::RawMemoryWrapper::serial_process(
|
|||
uint32_t serialSize = 0;
|
||||
RS_SERIAL_PROCESS(serialSize);
|
||||
if(!ctx.mOk) break;
|
||||
ctx.mOk = serialSize <= MAX_SERIALIZED_CHUNK_SIZE;
|
||||
ctx.mOk = serialSize <= MAX_SERIALIZED_CHUNK_SIZE;
|
||||
if(!ctx.mOk)
|
||||
{
|
||||
RsErr() << __PRETTY_FUNCTION__
|
||||
|
@ -555,6 +555,8 @@ void RsTypeSerializer::RawMemoryWrapper::serial_process(
|
|||
|
||||
if(!serialSize)
|
||||
{
|
||||
Dbg3() << __PRETTY_FUNCTION__ << " Deserialized empty memory chunk"
|
||||
<< std::endl;
|
||||
clear();
|
||||
break;
|
||||
}
|
||||
|
@ -573,7 +575,7 @@ void RsTypeSerializer::RawMemoryWrapper::serial_process(
|
|||
if(serialSize != second)
|
||||
{
|
||||
first = reinterpret_cast<uint8_t*>(realloc(first, serialSize));
|
||||
second = static_cast<uint32_t>(serialSize);
|
||||
second = serialSize;
|
||||
}
|
||||
|
||||
memcpy(first, ctx.mData + ctx.mOffset, second);
|
||||
|
@ -593,8 +595,8 @@ void RsTypeSerializer::RawMemoryWrapper::serial_process(
|
|||
}
|
||||
case RsGenericSerializer::FROM_JSON:
|
||||
{
|
||||
const bool yelding = !!( RsSerializationFlags::YIELDING &
|
||||
ctx.mFlags.toEFT<RsSerializationFlags>() );
|
||||
const bool yelding = !!(
|
||||
RsSerializationFlags::YIELDING & ctx.mFlags );
|
||||
if(!(ctx.mOk || yelding))
|
||||
{
|
||||
clear();
|
||||
|
|
|
@ -82,8 +82,7 @@ struct RsTypeSerializer
|
|||
INTT& member, const std::string& member_name )
|
||||
{
|
||||
const bool VLQ_ENCODING = !!(
|
||||
RsSerializationFlags::INTEGER_VLQ &
|
||||
ctx.mFlags.toEFT<RsSerializationFlags>() );
|
||||
RsSerializationFlags::INTEGER_VLQ & ctx.mFlags );
|
||||
|
||||
switch(j)
|
||||
{
|
||||
|
@ -149,8 +148,7 @@ struct RsTypeSerializer
|
|||
break;
|
||||
case RsGenericSerializer::FROM_JSON:
|
||||
ctx.mOk &= ( ctx.mOk ||
|
||||
!!( RsSerializationFlags::YIELDING &
|
||||
ctx.mFlags.toEFT<RsSerializationFlags>() ) )
|
||||
!!(RsSerializationFlags::YIELDING & ctx.mFlags) )
|
||||
&& from_JSON(member_name, member, ctx.mJson);
|
||||
break;
|
||||
default: fatalUnknownSerialJob(j);
|
||||
|
@ -198,7 +196,8 @@ struct RsTypeSerializer
|
|||
ctx.mOk = ctx.mOk && to_JSON(member_name, member, ctx.mJson);
|
||||
break;
|
||||
case RsGenericSerializer::FROM_JSON:
|
||||
ctx.mOk &= (ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING)
|
||||
ctx.mOk &= ( ctx.mOk ||
|
||||
!!(ctx.mFlags & RsSerializationFlags::YIELDING) )
|
||||
&& from_JSON(member_name, member, ctx.mJson);
|
||||
break;
|
||||
default: fatalUnknownSerialJob(j);
|
||||
|
@ -236,7 +235,7 @@ struct RsTypeSerializer
|
|||
break;
|
||||
case RsGenericSerializer::FROM_JSON:
|
||||
ctx.mOk &=
|
||||
(ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING)
|
||||
(ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING))
|
||||
&& from_JSON(member_name, type_id, member, ctx.mJson);
|
||||
break;
|
||||
default: fatalUnknownSerialJob(j);
|
||||
|
@ -324,7 +323,7 @@ struct RsTypeSerializer
|
|||
{
|
||||
using namespace rapidjson;
|
||||
|
||||
bool ok = ctx.mOk || ctx.mFlags & RsGenericSerializer::SERIALIZATION_FLAG_YIELDING;
|
||||
bool ok = ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING);
|
||||
Document& jDoc(ctx.mJson);
|
||||
Document::AllocatorType& allocator = jDoc.GetAllocator();
|
||||
|
||||
|
@ -424,8 +423,7 @@ struct RsTypeSerializer
|
|||
RsJson& jDoc(ctx.mJson);
|
||||
const char* mName = memberName.c_str();
|
||||
bool hasMember = jDoc.HasMember(mName);
|
||||
bool yielding = ctx.mFlags &
|
||||
RsGenericSerializer::SERIALIZATION_FLAG_YIELDING;
|
||||
bool yielding = !!(ctx.mFlags & RsSerializationFlags::YIELDING);
|
||||
|
||||
if(!hasMember)
|
||||
{
|
||||
|
@ -560,8 +558,7 @@ struct RsTypeSerializer
|
|||
{
|
||||
using namespace rapidjson;
|
||||
|
||||
bool ok = ctx.mOk || ctx.mFlags &
|
||||
RsGenericSerializer::SERIALIZATION_FLAG_YIELDING;
|
||||
bool ok = ctx.mOk || !!(ctx.mFlags & RsSerializationFlags::YIELDING);
|
||||
Document& jDoc(ctx.mJson);
|
||||
Document::AllocatorType& allocator = jDoc.GetAllocator();
|
||||
|
||||
|
@ -669,8 +666,8 @@ struct RsTypeSerializer
|
|||
break;
|
||||
case RsGenericSerializer::FROM_JSON:
|
||||
{
|
||||
bool ok = ctx.mOk || !!( ctx.mFlags.toEFT<RsSerializationFlags>()
|
||||
& RsSerializationFlags::YIELDING );
|
||||
bool ok = ctx.mOk || !!(
|
||||
ctx.mFlags & RsSerializationFlags::YIELDING );
|
||||
ctx.mOk = ok && from_JSON(memberName, member, ctx.mJson) && ctx.mOk;
|
||||
break;
|
||||
}
|
||||
|
@ -760,8 +757,7 @@ struct RsTypeSerializer
|
|||
RsJson& jDoc(ctx.mJson);
|
||||
const char* mName = memberName.c_str();
|
||||
bool hasMember = jDoc.HasMember(mName);
|
||||
bool yielding = ctx.mFlags &
|
||||
RsGenericSerializer::SERIALIZATION_FLAG_YIELDING;
|
||||
bool yielding = !!(ctx.mFlags & RsSerializationFlags::YIELDING);
|
||||
|
||||
if(!hasMember)
|
||||
{
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue