mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
685 lines
23 KiB
C
685 lines
23 KiB
C
/*H*************************************************************************************/
|
|
/*!
|
|
\File protobufwrite.c
|
|
|
|
\Description
|
|
Implementation of encoder for the Google Protobuf wire format
|
|
See: https://developers.google.com/protocol-buffers/docs/encoding
|
|
|
|
\Copyright
|
|
Copyright (c) Electronic Arts 2017-2018. ALL RIGHTS RESERVED.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************H*/
|
|
|
|
/*** Includes **************************************************************************/
|
|
|
|
#include <string.h>
|
|
|
|
#include "DirtySDK/platform.h"
|
|
#include "DirtySDK/dirtysock/dirtylib.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
#include "DirtySDK/dirtysock/dirtynet.h"
|
|
#include "DirtySDK/util/protobufcommon.h"
|
|
#include "DirtySDK/util/protobufwrite.h"
|
|
|
|
/*** Defines ***************************************************************************/
|
|
|
|
//! maximum octets used to encode a varint
|
|
#define PROTOBUF_VARINT_MAX (10)
|
|
|
|
//! max number of levels that we can embed messages
|
|
#define PROTOBUF_MAX_EMBEDDED_MSG (100)
|
|
|
|
/*** Macros ****************************************************************************/
|
|
|
|
//! encodes the field number and type into a tag
|
|
#define PROTOBUF_MakeTag(uField, uType) (((uField) << 3) | (uType))
|
|
|
|
/*** Type Definitions ******************************************************************/
|
|
|
|
//! module state
|
|
struct ProtobufWriteRefT
|
|
{
|
|
uint8_t *pBuffer; //!< pointer to the start of the buffer
|
|
int32_t iBufLen; //!< length of the buffer
|
|
int32_t iBufOff; //!< offset into the buffer
|
|
|
|
int32_t iMemGroup; //!< memory group identifier
|
|
void *pMemGroupUserdata; //!< memory group data
|
|
|
|
uint8_t *aMessagesBegin[PROTOBUF_MAX_EMBEDDED_MSG]; //!< stack of pointers to where each encoded messages begin
|
|
int32_t iNumMessages; //!< current number of messages we are embedding
|
|
|
|
uint8_t bEncodeSize; //!< should we encode the size of the message at the beginning of the buffer? (needed for grpc)
|
|
uint8_t _pad[3];
|
|
};
|
|
|
|
/*** Variables *************************************************************************/
|
|
|
|
// variable to test if something is set
|
|
static const uint8_t _aEmptyBuffer[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
|
|
|
|
/*** Private Functions *****************************************************************/
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtobufWriteVarint
|
|
|
|
\Description
|
|
Write a varint type to the buffer
|
|
|
|
\Input uValue - varint value to write
|
|
\Input *pBuffer - [out] destination we are writing to
|
|
\Input iBufLen - size of the output destination
|
|
\Input *pError - [out] where we write the result of the operation
|
|
|
|
\Output
|
|
int32_t - amount of bytes written to the buffer
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static int32_t _ProtobufWriteVarint(uint64_t uValue, uint8_t *pBuffer, int32_t iBufLen, int32_t *pError)
|
|
{
|
|
int32_t iWrite;
|
|
|
|
/* make we can at least write 1 byte for the cases
|
|
where we can fit it in one octet */
|
|
if (iBufLen <= 0)
|
|
{
|
|
*pError = PROTOBUFWRITE_ERROR_FULL;
|
|
return(0);
|
|
}
|
|
|
|
for (iWrite = 0; uValue >= 0x80; iWrite += 1)
|
|
{
|
|
// make sure we have space left
|
|
if (iWrite >= (iBufLen - 1))
|
|
{
|
|
*pError = PROTOBUFWRITE_ERROR_FULL;
|
|
return(0);
|
|
}
|
|
|
|
// set the lsb of the value and set the msb as a continuation flag
|
|
pBuffer[iWrite] = (uValue % 0x80) + 0x80;
|
|
uValue /= 0x80;
|
|
}
|
|
pBuffer[iWrite++] = (uint8_t)uValue;
|
|
|
|
return(iWrite);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function _ProtobufMemcpy
|
|
|
|
\Description
|
|
Copies data to the buffer
|
|
|
|
\Input *pDst - [out] pointer to output
|
|
\Input iDstLen - size of output
|
|
\Input *pSrc - pointer to input
|
|
\Input iSrcLen - size of input
|
|
\Input *pError - [out] where we write the result of the operation
|
|
|
|
\Output
|
|
int32_t - amount of bytes written to the buffer
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
static int32_t _ProtobufMemcpy(void *pDst, int32_t iDstLen, const void *pSrc, int32_t iSrcLen, int32_t *pError)
|
|
{
|
|
if ((iDstLen = DS_MIN(iDstLen, iSrcLen)) > 0)
|
|
{
|
|
ds_memcpy(pDst, pSrc, iDstLen);
|
|
}
|
|
else
|
|
{
|
|
*pError = PROTOBUFWRITE_ERROR_FULL;
|
|
}
|
|
return(iDstLen);
|
|
}
|
|
|
|
/*** Public Functions ******************************************************************/
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteCreate
|
|
|
|
\Description
|
|
Create and initialize the writer module ref
|
|
|
|
\Input *pBuffer - where we are writing to
|
|
\Input iBufLen - the size of the buffer we are writing to
|
|
\Input bEncodeSize - encode the size of the message at the beginning of the buffer?
|
|
|
|
\Output
|
|
ProtobufWriteRefT * - pointer to module ref or NULL if failure
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
ProtobufWriteRefT *ProtobufWriteCreate(uint8_t *pBuffer, int32_t iBufLen, uint8_t bEncodeSize)
|
|
{
|
|
ProtobufWriteRefT *pState;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserdata;
|
|
|
|
// query memory group information
|
|
DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserdata);
|
|
|
|
// allocate module state
|
|
if ((pState = (ProtobufWriteRefT *)DirtyMemAlloc(sizeof(*pState), PROTOBUF_MEMID, iMemGroup, pMemGroupUserdata)) == NULL)
|
|
{
|
|
NetPrintf(("protobufwriter: cannot allocate writer module state\n"));
|
|
return(NULL);
|
|
}
|
|
ds_memclr(pState, sizeof(*pState));
|
|
pState->pBuffer = pBuffer;
|
|
pState->iBufLen = iBufLen;
|
|
pState->iMemGroup = iMemGroup;
|
|
pState->pMemGroupUserdata = pMemGroupUserdata;
|
|
|
|
// assign the encode size setting, if true save space at the beginning for encoding size
|
|
if ((pState->bEncodeSize = bEncodeSize) == TRUE)
|
|
{
|
|
pState->iBufOff += 4;
|
|
}
|
|
|
|
return(pState);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteVarint
|
|
|
|
\Description
|
|
Write the non-zigzag encoded (sint32, sint64) versions of the varint types
|
|
|
|
\Input *pState - module state
|
|
\Input uValue - value we are writing
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
This function supports the following types: int32, uint32, uint64, bool and
|
|
enum.
|
|
|
|
If you pass in 0 for uField, nothing will be written to the buffer for the tag
|
|
which you only want to do when writing packed repeated fields.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteVarint(ProtobufWriteRefT *pState, uint64_t uValue, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
// don't write the defaults to save space when not in a repeated field
|
|
if ((uValue != 0) || (uField == 0))
|
|
{
|
|
if (uField > 0)
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteRepeatedVarint
|
|
|
|
\Description
|
|
Write the non-zigzag encoded (sint32, sint64) versions of the varint types
|
|
repeated
|
|
|
|
\Input *pState - module state
|
|
\Input *pValues - array of values we are writing
|
|
\Input iCount - number of entries in the array
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
This function supports the following types: int32, uint32, uint64, bool and
|
|
enum.
|
|
|
|
\Version 12/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteRepeatedVarint(ProtobufWriteRefT *pState, uint64_t *pValues, int32_t iCount, uint32_t uField)
|
|
{
|
|
int32_t iIndex, iError;
|
|
|
|
ProtobufWriteMessageBegin(pState, uField);
|
|
for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1)
|
|
{
|
|
iError = ProtobufWriteVarint(pState, pValues[iIndex], 0);
|
|
}
|
|
ProtobufWriteMessageEnd(pState);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteSint32
|
|
|
|
\Description
|
|
Write the zigzag encoded sint32 type
|
|
|
|
\Input *pState - module state
|
|
\Input iValue - value we are writing
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
If you pass in 0 for uField, nothing will be written for the tag
|
|
which you only want to do when writing packed repeated fields.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteSint32(ProtobufWriteRefT *pState, int32_t iValue, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
// zigzag encode the value
|
|
const uint64_t uValue = (iValue << 1) ^ (iValue >> 31);
|
|
|
|
// don't write the defaults to save space when not in a repeated field
|
|
if ((uValue != 0) || (uField == 0))
|
|
{
|
|
if (uField > 0)
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteRepeatedSint32
|
|
|
|
\Description
|
|
Write the zigzag encoded sint32 type repeated
|
|
|
|
\Input *pState - module state
|
|
\Input *pValues - array of values we are writing
|
|
\Input iCount - number of entries in the array
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 12/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteRepeatedSint32(ProtobufWriteRefT *pState, int32_t *pValues, int32_t iCount, uint32_t uField)
|
|
{
|
|
int32_t iIndex, iError;
|
|
|
|
ProtobufWriteMessageBegin(pState, uField);
|
|
for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1)
|
|
{
|
|
iError = ProtobufWriteSint32(pState, pValues[iIndex], 0);
|
|
}
|
|
ProtobufWriteMessageEnd(pState);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteSint64
|
|
|
|
\Description
|
|
Write the zigzag encoded sint64 type
|
|
|
|
\Input *pState - module state
|
|
\Input iValue - value we are writing
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
If you pass in 0 for uField, nothing will be written for the tag
|
|
which you only want to do when writing packed repeated fields.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteSint64(ProtobufWriteRefT *pState, int64_t iValue, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
// zigzag encode the value
|
|
const uint64_t uValue = (iValue << 1) ^ (iValue >> 63);
|
|
|
|
// don't write the defaults to save space when not in a repeated field
|
|
if ((uValue != 0) || (uField == 0))
|
|
{
|
|
if (uField > 0)
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_VARINT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
pState->iBufOff += _ProtobufWriteVarint(uValue, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteRepeatedSint64
|
|
|
|
\Description
|
|
Write the zigzag encoded sint64 type repeated
|
|
|
|
\Input *pState - module state
|
|
\Input *pValues - array of values we are writing
|
|
\Input iCount - number of entries in the array
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 12/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteRepeatedSint64(ProtobufWriteRefT *pState, int64_t *pValues, int32_t iCount, uint32_t uField)
|
|
{
|
|
int32_t iIndex, iError;
|
|
|
|
ProtobufWriteMessageBegin(pState, uField);
|
|
for (iIndex = 0, iError = PROTOBUFWRITE_ERROR_OK; (iIndex < iCount) && (iError == PROTOBUFWRITE_ERROR_OK); iIndex += 1)
|
|
{
|
|
iError = ProtobufWriteSint64(pState, pValues[iIndex], 0);
|
|
}
|
|
ProtobufWriteMessageEnd(pState);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteFixed32
|
|
|
|
\Description
|
|
Write the fixed32, float or sfixed32 type to the buffer
|
|
|
|
\Input *pState - module state
|
|
\Input *pValue - value we are writing
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
The fixed types are written in little endian so we use memcpy.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteFixed32(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
// don't write the defaults to save space
|
|
if (memcmp(pValue, _aEmptyBuffer, 4) != 0)
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_32BIT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, 4, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteRepeatedFixed32
|
|
|
|
\Description
|
|
Write the repeated fixed32, float or sfixed32 type to the buffer
|
|
|
|
\Input *pState - module state
|
|
\Input *pInput - array of values we are writing
|
|
\Input iInpLen - size (in bytes) of the array
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 12/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteRepeatedFixed32(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
ProtobufWriteMessageBegin(pState, uField);
|
|
pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pInput, iInpLen, &iError);
|
|
ProtobufWriteMessageEnd(pState);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteFixed64
|
|
|
|
\Description
|
|
Write the fixed64, double or sfixed64 type to the buffer
|
|
|
|
\Input *pState - module state
|
|
\Input *pValue - value we are writing
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
The fixed types are written in little endian so we use memcpy.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteFixed64(ProtobufWriteRefT *pState, const void *pValue, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
// don't write the defaults to save space
|
|
if (memcmp(pValue, _aEmptyBuffer, 8) != 0)
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_64BIT), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, 8, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteRepeatedFixed64
|
|
|
|
\Description
|
|
Write the repeated fixed64, double or sfixed64 type to the buffer
|
|
|
|
\Input *pState - module state
|
|
\Input *pInput - array of values we are writing
|
|
\Input iInpLen - size (in bytes) of the array
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 12/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteRepeatedFixed64(ProtobufWriteRefT *pState, const void *pInput, int32_t iInpLen, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
ProtobufWriteMessageBegin(pState, uField);
|
|
pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pInput, iInpLen, &iError);
|
|
ProtobufWriteMessageEnd(pState);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteLengthDelimited
|
|
|
|
\Description
|
|
Write the length delimited types
|
|
|
|
\Input *pState - module state
|
|
\Input *pValue - value we are writing
|
|
\Input iLength - size of the value
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteLengthDelimited(ProtobufWriteRefT *pState, const void *pValue, int32_t iLength, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
// don't write empty data to save space when not in a repeated field
|
|
if ((pValue != NULL) && (iLength > 0))
|
|
{
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_LENGTH_DELIMITED), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
pState->iBufOff += _ProtobufWriteVarint(iLength, pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
pState->iBufOff += _ProtobufMemcpy(pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, pValue, iLength, &iError);
|
|
}
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteMessageBegin
|
|
|
|
\Description
|
|
Begin encoding an embedded message in place
|
|
|
|
\Input *pState - module state
|
|
\Input uField - field identifier
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Notes
|
|
Embedded messages work like a stack (FILO) when nesting more messages.
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteMessageBegin(ProtobufWriteRefT *pState, uint32_t uField)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK;
|
|
|
|
// make sure that we have not reached our maximum
|
|
if (pState->iNumMessages == PROTOBUF_MAX_EMBEDDED_MSG)
|
|
{
|
|
return(PROTOBUFWRITE_ERROR_EMBED);
|
|
}
|
|
|
|
pState->iBufOff += _ProtobufWriteVarint(PROTOBUF_MakeTag(uField, PROTOBUF_TYPE_LENGTH_DELIMITED), pState->pBuffer+pState->iBufOff, pState->iBufLen-pState->iBufOff, &iError);
|
|
pState->aMessagesBegin[pState->iNumMessages++] = pState->pBuffer+pState->iBufOff;
|
|
|
|
if ((pState->iBufLen-pState->iBufOff) > PROTOBUF_VARINT_MAX)
|
|
{
|
|
pState->iBufOff += PROTOBUF_VARINT_MAX;
|
|
}
|
|
else
|
|
{
|
|
iError = PROTOBUFWRITE_ERROR_FULL;
|
|
}
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteMessageEnd
|
|
|
|
\Description
|
|
Finish encoding an embedded message in place
|
|
|
|
\Input *pState - module state
|
|
|
|
\Output
|
|
int32_t - 0 (PROTOBUFWRITE_ERROR_OK) on success, >0 on error
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteMessageEnd(ProtobufWriteRefT *pState)
|
|
{
|
|
int32_t iError = PROTOBUFWRITE_ERROR_OK, iInpLen, iBufOff;
|
|
uint8_t *pMsgBegin;
|
|
|
|
// make sure that we still have messages in the stack
|
|
if (pState->iNumMessages > 0)
|
|
{
|
|
pMsgBegin = pState->aMessagesBegin[--pState->iNumMessages];
|
|
}
|
|
else
|
|
{
|
|
return(PROTOBUFWRITE_ERROR_EMBED);
|
|
}
|
|
|
|
// calculate the size of the message and encode it to the buffer
|
|
iInpLen = (int32_t)((pState->pBuffer+pState->iBufOff)-pMsgBegin-PROTOBUF_VARINT_MAX);
|
|
iBufOff = _ProtobufWriteVarint(iInpLen, pMsgBegin, PROTOBUF_VARINT_MAX, &iError);
|
|
|
|
// fill the gap (if any)
|
|
memmove(pMsgBegin+iBufOff, pMsgBegin+PROTOBUF_VARINT_MAX, iInpLen);
|
|
pState->iBufOff -= (PROTOBUF_VARINT_MAX-iBufOff);
|
|
|
|
return(iError);
|
|
}
|
|
|
|
/*F*************************************************************************************/
|
|
/*!
|
|
\Function ProtobufWriteDestroy
|
|
|
|
\Description
|
|
Finish writing to the buffer and return the amount of data written
|
|
|
|
\Input *pState - module state
|
|
|
|
\Output
|
|
int32_t - amount of bytes written to the buffer
|
|
|
|
\Version 07/05/2017 (eesponda)
|
|
*/
|
|
/*************************************************************************************F*/
|
|
int32_t ProtobufWriteDestroy(ProtobufWriteRefT *pState)
|
|
{
|
|
const int32_t iResult = pState->iBufOff;
|
|
|
|
// encode the size if needed
|
|
if (pState->bEncodeSize == TRUE)
|
|
{
|
|
// calculate the size of the message without accounting for encoded size
|
|
const int32_t iMessageSize = SocketNtohl(iResult-4);
|
|
ds_memcpy(pState->pBuffer, (const void *)&iMessageSize, sizeof(iMessageSize));
|
|
}
|
|
|
|
DirtyMemFree(pState, PROTOBUF_MEMID, pState->iMemGroup, pState->pMemGroupUserdata);
|
|
return(iResult);
|
|
}
|