blob: 9df0a8e3070e423f09b128fac3c91f12f8f62d9e [file] [log] [blame]
/*
*
* Copyright (c) 2017 Nest Labs, Inc.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <Weave/Support/CodeUtils.h>
#include <Weave/Support/logging/WeaveLogging.h>
#include <Weave/Support/SerializationUtils.h>
namespace nl {
using namespace nl::Weave::TLV;
//
// Some notes on memory management.
//
// No dynamic memory allocation is necessary for serializing a structure,
// but it is REQUIRED for de-serializing. If your platform needs to
// de-serialize, you can:
//
// (1) Pass TLVReaderToDeserializedData() and
// TLVReaderToDeserializedDataHelper() a MemoryManagement
// structure with any implementations of malloc(), free(),
// and realloc() you choose, OR
//
// (2) Pass no MemoryManagement at all, in which case a default
// MemoryManagement will be used.
//
// If option (2) is chosen, you must turn on
// WEAVE_CONFIG_SERIALIZATION_USE_MALLOC,
// and the stdlib versions of malloc(), free(), and realloc() will be
// used. Otherwise the unsupported_*() functions will be used, which
// do nothing except log an error.
//
// The assumption is that de-serialization will likely be performed
// only on resource-rich platforms where dynamic memory allocation is
// supported.
//
#if WEAVE_CONFIG_SERIALIZATION_USE_MALLOC
#if HAVE_MALLOC && HAVE_FREE && HAVE_REALLOC
static MemoryManagement sDefaultMemoryManagement = { malloc, free, realloc };
#else // HAVE_MALLOC && HAVE_FREE && HAVE_REALLOC
#error "Implementations of malloc, free, and realloc must be present in order to use stdlib memory management."
#endif // HAVE_MALLOC && HAVE_FREE && HAVE_REALLOC
#else // WEAVE_CONFIG_SERIALIZATION_USE_MALLOC
static void *unsupported_malloc(size_t size)
{
WeaveLogError(Support, "malloc() not supported");
return NULL;
}
static void unsupported_free(void *ptr)
{
WeaveLogError(Support, "free() not supported");
}
static void *unsupported_realloc(void *ptr, size_t size)
{
WeaveLogError(Support, "realloc() not supported");
return NULL;
}
static MemoryManagement sDefaultMemoryManagement = { unsupported_malloc, unsupported_free, unsupported_realloc };
#endif // WEAVE_CONFIG_SERIALIZATION_USE_MALLOC
static WEAVE_ERROR WriteArrayData(TLVWriter &aWriter,
void *aStructureData,
const FieldDescriptor * aFieldPtr);
static WEAVE_ERROR WriteDataForType(TLVWriter &aWriter,
void *aStructureData,
const FieldDescriptor *&aFieldPtr,
SerializedFieldType aType,
bool aInArray);
static WEAVE_ERROR ReadArrayData(TLVReader &aReader,
void *aStructureData,
const FieldDescriptor * aFieldPtr,
SerializationContext *aContext = NULL);
static WEAVE_ERROR ReadDataForType(TLVReader &aReader,
void *aStructureData,
const FieldDescriptor *&aFieldPtr,
SerializedFieldType aType,
bool aInArray,
SerializationContext *aContext = NULL);
static WEAVE_ERROR CheckForEndOfTLV(TLVReader &aReader, bool &aEndOfTLV);
static WEAVE_ERROR DeallocateDeserializedArray(void *aArrayData,
const SchemaFieldDescriptor *aFieldDescriptors,
SerializationContext *aContext = NULL);
#if WEAVE_CONFIG_SERIALIZATION_DEBUG_LOGGING
static int32_t sIndentationLevel = 0;
#define LogReadWrite(aFormat, ...) WeaveLogDetail(Support, "%*s" aFormat, sIndentationLevel, "", __VA_ARGS__)
#define LogReadWriteStartContainer(aFormat, ...) do { LogReadWrite(aFormat, __VA_ARGS__); sIndentationLevel += 2; } while (0);
#define LogReadWriteEndContainer(aFormat, ...) do { sIndentationLevel -= 2; LogReadWrite(aFormat, __VA_ARGS__); } while (0);
#else // WEAVE_CONFIG_SERIALIZATION_DEBUG_LOGGING
#define LogReadWrite(aFormat, ...)
#define LogReadWriteStartContainer(aFormat, ...)
#define LogReadWriteEndContainer(aFormat, ...)
#endif // WEAVE_CONFIG_SERIALIZATION_DEBUG_LOGGING
static bool IsValidFieldType(SerializedFieldType aType)
{
return (aType <= SerializedFieldTypeArray);
}
const static uint8_t sElementSize[] =
{
sizeof(bool), //SerializedFieldTypeBoolean
sizeof(uint8_t), //SerializedFieldTypeUInt8,
sizeof(uint16_t), //SerializedFieldTypeUInt16,
sizeof(uint32_t), //SerializedFieldTypeUInt32,
sizeof(uint64_t), //SerializedFieldTypeUInt64,
sizeof(int8_t), //SerializedFieldTypeInt8,
sizeof(int16_t), //SerializedFieldTypeInt16,
sizeof(int32_t), //SerializedFieldTypeInt32,
sizeof(int64_t), //SerializedFieldTypeInt64,
sizeof(float), //SerializedFieldTypeFloatingPoint32,
sizeof(double), //SerializedFieldTypeFloatingPoint64,
sizeof(char *), //SerializedFieldTypeUTF8String,
sizeof(SerializedByteString), //SerializedFieldTypeByteString,
sizeof(void *), //SerializedFieldTypeStructure,
sizeof(void *) //SerializedFieldTypeArray,
};
static WEAVE_ERROR GetArrayElementSize(uint32_t &aOutSize, const FieldDescriptor * aFieldPtr, SerializedFieldType aType)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
VerifyOrExit(aFieldPtr != NULL, err = WEAVE_ERROR_INVALID_ARGUMENT);
if (aFieldPtr->mNestedFieldDescriptors)
{
VerifyOrExit(aType == SerializedFieldTypeStructure, err = WEAVE_ERROR_INCORRECT_STATE);
LogReadWrite("%s element is a structure", __FUNCTION__);
aOutSize = aFieldPtr->mNestedFieldDescriptors->mSize;
}
else
{
LogReadWrite("%s element is a primitive size", __FUNCTION__);
aOutSize = sElementSize[aType];
}
exit:
return err;
}
/**
* @brief
* A writer function that writes an array structure.
*
* @param aWriter[in] The writer to use for writing out the structure
*
* @param aStructureData[in] A pointer to the c-structure data to write based on the FieldDescriptor
*
* @param aFieldPtr[in] FieldDescriptor to describe the array c struct + TLV
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aWriter.
*
*/
WEAVE_ERROR WriteArrayData(TLVWriter &aWriter, void *aStructureData, const FieldDescriptor * aFieldPtr)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const bool writingInArray = true;
SerializedFieldType type;
ArrayLengthAndBuffer *array;
const FieldDescriptor *fieldPtr = aFieldPtr;
uint32_t elementSize = 0;
// aStructureData should be pointing to the wrapped length and buffer structure
array = static_cast<ArrayLengthAndBuffer *>(aStructureData);
// the type of the array is next in the FieldDescriptors list
fieldPtr++;
// Error check to see that this returns a valid type
type = fieldPtr->GetType();
VerifyOrExit(IsValidFieldType(type), err = WEAVE_ERROR_INVALID_ARGUMENT);
err = GetArrayElementSize(elementSize, fieldPtr, type);
LogReadWrite("%s elementSize %d", "W", elementSize);
for (uint32_t idx = 0; idx < array->mNumElements; idx++)
{
// Increment based on type
aFieldPtr = fieldPtr;
LogReadWrite("%s aStructureData 0x%x array 0x%x array->mNumElements %d array->mElementBuffer 0x%x idx %d idx * elementSize 0x%x", "W", aStructureData, array, array->mNumElements, array->mElementBuffer, idx, idx * elementSize);
err = WriteDataForType(aWriter, static_cast<char *>(array->mElementBuffer) + idx * elementSize, aFieldPtr, type, writingInArray);
SuccessOrExit(err);
}
exit:
return err;
}
/**
* @brief
* A writer function to check whether data is nullable/nullified before
* writing to the TLV.
*
* @param aWriter[in] The writer to use for writing out the structure
*
* @param aStructureData[in] A pointer to the c-structure data to read
*
* @param aFieldPtr[inout] FieldDescriptor to describe the fields and
* TLV tag. The function will increment
* the pointer s.t. it will point to the
* next element in the FieldDescriptor array
*
* @param aType[in] The SerializedFieldType of the field
*
* @param aIsNullified[in] The TLV tag will be nullified if this is true.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other TLV errors while writing.
*
*/
WEAVE_ERROR WriteNullableDataForType(TLVWriter &aWriter, void *aStructureData, const FieldDescriptor *&aFieldPtr, SerializedFieldType aType, bool aIsNullified)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (aIsNullified)
{
LogReadWrite("%s nullified", "W");
err = aWriter.PutNull(ContextTag(aFieldPtr->mTVDContextTag));
SuccessOrExit(err);
aFieldPtr++;
}
else
{
err = WriteDataForType(aWriter, aStructureData, aFieldPtr, aType, false);
}
exit:
return err;
}
/**
* @brief
* A writer function write a specific entry into the TLV based on structure data.
*
* @param aWriter[in] The writer to use for writing out the structure
*
* @param aStructureData[in] A pointer to the c-structure data to write
*
* @param aFieldPtr[inout] FieldDescriptor to describe the fields and
* TLV tag. The function will increment
* the pointer s.t. it will point to the
* next element in the FieldDescritor array
*
* @param aType[in] The SerializedFieldType of the field
*
* @param aInArray[in] True if we're writing an array (use anonymous tag)
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aWriter.
*
*/
WEAVE_ERROR WriteDataForType(TLVWriter &aWriter, void *aStructureData, const FieldDescriptor *&aFieldPtr, SerializedFieldType aType, bool aInArray)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVType containerType;
uint64_t tag = aInArray ? AnonymousTag : ContextTag(aFieldPtr->mTVDContextTag);
LogReadWrite("%s aStructureData 0x%x", "W", aStructureData);
switch (aType)
{
case SerializedFieldTypeBoolean:
{
bool v = *static_cast<bool *>(aStructureData);
LogReadWrite("%s boolean '%s'", "W", v ? "true" : "false");
err = aWriter.PutBoolean(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUInt8:
{
uint8_t v = *static_cast<uint8_t *>(aStructureData);
LogReadWrite("%s uint8 %u", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUInt16:
{
uint16_t v = *static_cast<uint16_t *>(aStructureData);
LogReadWrite("%s uint16 %u", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUInt32:
{
uint32_t v = *static_cast<uint32_t *>(aStructureData);
LogReadWrite("%s uint32 %u", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUInt64:
{
uint64_t v = *static_cast<uint64_t *>(aStructureData);
LogReadWrite("%s uint64 %llu", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeInt8:
{
int8_t v = *static_cast<int8_t *>(aStructureData);
LogReadWrite("%s int8 %d", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeInt16:
{
int16_t v = *static_cast<int16_t *>(aStructureData);
LogReadWrite("%s int16 %d", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeInt32:
{
int32_t v = *static_cast<int32_t *>(aStructureData);
LogReadWrite("%s int32 %d", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeInt64:
{
int64_t v = *static_cast<int64_t *>(aStructureData);
LogReadWrite("%s int64 %d", "W", v);
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeFloatingPoint32:
{
float v = *static_cast<float *>(aStructureData);
#if WEAVE_CONFIG_SERIALIZATION_LOG_FLOATS
LogReadWrite("%s float %f", "W", v);
#endif
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeFloatingPoint64:
{
double v = *static_cast<double *>(aStructureData);
#if WEAVE_CONFIG_SERIALIZATION_LOG_FLOATS
LogReadWrite("%s double %f", "W", v);
#endif
err = aWriter.Put(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUTF8String:
{
const char *v = *static_cast<const char**>(aStructureData);
LogReadWrite("%s utf8string '%s'", "W", v);
err = aWriter.PutString(tag, v);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeByteString:
{
SerializedByteString v = *static_cast<SerializedByteString *>(aStructureData);
LogReadWrite("%s bytestring len: %u", "W", v.mLen);
err = aWriter.PutBytes(tag, v.mBuf, v.mLen);
SuccessOrExit(err);
break;
}
// We can hit this case when we have an array of structures.
case SerializedFieldTypeStructure:
{
err = aWriter.StartContainer(tag,
kTLVType_Structure,
containerType);
SuccessOrExit(err);
LogReadWriteStartContainer("%s Structure Start", "W");
err = SerializedDataToTLVWriter(aWriter, aStructureData, aFieldPtr->mNestedFieldDescriptors);
SuccessOrExit(err);
LogReadWriteEndContainer("%s Structure End", "W");
err = aWriter.EndContainer(containerType);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeArray:
{
err = aWriter.StartContainer(tag,
kTLVType_Array,
containerType);
SuccessOrExit(err);
LogReadWriteStartContainer("%s Array Start", "W");
err = WriteArrayData(aWriter, aStructureData, aFieldPtr);
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
}
SuccessOrExit(err);
LogReadWriteEndContainer("%s Array End", "W");
err = aWriter.EndContainer(containerType);
SuccessOrExit(err);
// Skip over the elements.
aFieldPtr++;
break;
}
default:
{
err = WEAVE_ERROR_INVALID_ARGUMENT;
break;
}
}
aFieldPtr++;
exit:
return err;
}
/**
* @brief
* A reader function that reads an array structure.
*
* @param aReader[in] The reader to use for reading in the structure
*
* @param aStructureData[in] A pointer to the c-structure data to read based on the FieldDescriptor
*
* @param aFieldPtr[in] FieldDescriptor to describe the array c struct + TLV
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aWriter.
*
*/
WEAVE_ERROR ReadArrayData(TLVReader &aReader, void *aStructureData, const FieldDescriptor * aFieldPtr, SerializationContext *aContext)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
const bool readingOutArray = true;
SerializedFieldType type;
ArrayLengthAndBuffer *array;
const FieldDescriptor *fieldPtr = aFieldPtr;
size_t count = 0;
char *outputBuffer = NULL;
size_t outputBufferNumItems = 0;
size_t outputBufferNumResizes = 0;
MemoryManagement *memMgmt = NULL;
uint32_t elementSize = 0;
bool endOfTLV = false;
if ((aContext == NULL) || !(aContext->memMgmt.mem_alloc && aContext->memMgmt.mem_free && aContext->memMgmt.mem_realloc))
{
memMgmt = &sDefaultMemoryManagement;
}
else
{
memMgmt = &aContext->memMgmt;
}
// aStructureData should be pointing to the wrapped length and buffer structure
array = static_cast<ArrayLengthAndBuffer *>(aStructureData);
// Initialize.
array->mNumElements = 0;
array->mElementBuffer = NULL;
// the type of the array is next in the FieldDescriptors list
fieldPtr++;
// Error check to see that this returns a valid type
type = fieldPtr->GetType();
VerifyOrExit(IsValidFieldType(type), err = WEAVE_ERROR_INVALID_ARGUMENT);
err = GetArrayElementSize(elementSize, fieldPtr, type);
LogReadWrite("%s elementSize %d", "R", elementSize);
VerifyOrExit(array && (elementSize > 0), err = WEAVE_ERROR_INCORRECT_STATE);
// Check to see whether there are any elements to read in.
err = CheckForEndOfTLV(aReader, endOfTLV);
SuccessOrExit(err);
VerifyOrExit(endOfTLV == false, LogReadWrite("%s array contains no elements", "R"));
// Read in each element.
while (err == WEAVE_NO_ERROR)
{
// See whether the output buffer needs to be resized.
if (count >= outputBufferNumItems)
{
outputBufferNumItems = (1 << ++outputBufferNumResizes);
outputBuffer = (char *)memMgmt->mem_realloc((void *)outputBuffer, outputBufferNumItems*elementSize);
VerifyOrExit(outputBuffer != NULL, err = WEAVE_ERROR_NO_MEMORY);
LogReadWrite("%s allocating array memory at 0x%x", "R", outputBuffer);
}
// Increment based on type
aFieldPtr = fieldPtr;
LogReadWrite("%s aStructureData 0x%x array 0x%x count %d outputBuffer 0x%x count*elementSize 0x%x", "R", aStructureData, array, count, outputBuffer, count, count*elementSize);
err = ReadDataForType(aReader, outputBuffer + count*elementSize, aFieldPtr, type, readingOutArray, aContext);
if (err == WEAVE_END_OF_TLV)
{
count++;
array->mNumElements = count;
array->mElementBuffer = outputBuffer;
err = WEAVE_NO_ERROR;
break;
}
count++;
}
exit:
if (err != WEAVE_NO_ERROR)
{
if (outputBuffer != NULL)
{
memMgmt->mem_free(outputBuffer);
}
}
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
/**
* @brief
* A reader function to see whether the next element in a TLVReader is the end of TLV.
*
* @param aReader[in] The reader
*
* @param aEndOfTLV[inout] A bool set to true if the next element in the reader is
* the end of TLV, false otherwise
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that may be returned from the aReader.
*
*/
static WEAVE_ERROR CheckForEndOfTLV(TLVReader &aReader, bool &aEndOfTLV)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVReader reader;
// Initialize.
aEndOfTLV = false;
// Initialize a new reader, at aReader's position.
reader.Init(aReader);
// Position it the next element.
err = reader.Next();
VerifyOrExit(err == WEAVE_NO_ERROR || err == WEAVE_END_OF_TLV, );
// If err was WEAVE_END_OF_TLV, we've the end of TLV and we can
// safely suppress the error.
if (err == WEAVE_END_OF_TLV)
{
aEndOfTLV = true;
err = WEAVE_NO_ERROR;
}
exit:
return err;
}
/**
* @brief
* A reader function to check whether data is nullable/nullified before
* reading from the TLV.
*
* @param aReader[in] The reader to use for reading in the structure
*
* @param aStructureData[in] A pointer to the c-structure data to read
*
* @param aFieldPtr[inout] FieldDescriptor to describe the fields and
* TLV tag. The function will increment
* the pointer s.t. it will point to the
* next element in the FieldDescriptor array
*
* @param aType[in] The SerializedFieldType of the field
*
* @param aIsNullified[out] Set to indicate that a field is nullified.
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other TLV errors while writing.
*
*/
WEAVE_ERROR ReadNullableDataForType(TLVReader &aReader, void *aStructureData, const FieldDescriptor *&aFieldPtr, SerializedFieldType aType, bool &aIsNullified, SerializationContext *aContext)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if ((aFieldPtr->IsNullable()) && (aReader.GetType() == kTLVType_Null))
{
aIsNullified = true;
aFieldPtr++;
err = aReader.Next();
LogReadWrite("%s nullified", "R");
}
else
{
aIsNullified = false;
err = ReadDataForType(aReader, aStructureData, aFieldPtr, aType, false, aContext);
}
return err;
}
/**
* @brief
* A reader function to read a specific entry from the TLV based on structure data.
*
* @param aReader[in] The reader to use for reading in the structure
*
* @param aStructureData[in] A pointer to the c-structure data to read
*
* @param aFieldPtr[inout] FieldDescriptor to describe the fields and
* TLV tag. The function will increment
* the pointer s.t. it will point to the
* next element in the FieldDescritor array
*
* @param aType[in] The SerializedFieldType of the field
*
* @param aInArray[in] True if we're reading an array (use anonymous tag)
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aReader.
*
*/
WEAVE_ERROR ReadDataForType(TLVReader &aReader, void *aStructureData, const FieldDescriptor *&aFieldPtr, SerializedFieldType aType, bool aInArray, SerializationContext *aContext)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
TLVType containerType;
MemoryManagement *memMgmt = NULL;
if ((aContext == NULL) || !(aContext->memMgmt.mem_alloc && aContext->memMgmt.mem_free && aContext->memMgmt.mem_realloc))
{
memMgmt = &sDefaultMemoryManagement;
}
else
{
memMgmt = &aContext->memMgmt;
}
LogReadWrite("%s aStructureData 0x%x", "R", aStructureData);
switch (aType)
{
case SerializedFieldTypeBoolean:
{
bool v = false;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s boolean '%s'", "R", v ? "true" : "false");
*static_cast<bool *>(aStructureData) = v;
break;
}
case SerializedFieldTypeUInt8:
{
uint8_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s uint8 %d", "R", v);
*static_cast<uint8_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeUInt16:
{
uint16_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s unt16 %u", "R", v);
*static_cast<uint16_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeUInt32:
{
uint32_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s uint32 %u", "R", v);
*static_cast<uint32_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeUInt64:
{
uint64_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s uint64 %llu", "R", v);
*static_cast<uint64_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeInt8:
{
int8_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s int8 %d", "R", v);
*static_cast<int8_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeInt16:
{
int16_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s int16 %d", "R", v);
*static_cast<int16_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeInt32:
{
int32_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s int32 %d", "R", v);
*static_cast<int32_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeInt64:
{
int64_t v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
LogReadWrite("%s int64 %d", "R", v);
*static_cast<int8_t *>(aStructureData) = v;
break;
}
case SerializedFieldTypeFloatingPoint32:
{
double v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
#if WEAVE_CONFIG_SERIALIZATION_LOG_FLOATS
LogReadWrite("%s float %f", "R", v);
#endif
*static_cast<float *>(aStructureData) = (float)v;
break;
}
case SerializedFieldTypeFloatingPoint64:
{
double v = 0;
err = aReader.Get(v);
SuccessOrExit(err);
#if WEAVE_CONFIG_SERIALIZATION_LOG_FLOATS
LogReadWrite("%s double %f", "R", v);
#endif
*static_cast<double *>(aStructureData) = v;
break;
}
case SerializedFieldTypeUTF8String:
{
char *dst = NULL;
// TLV Strings are not null terminated
uint32_t length = aReader.GetLength() + 1;
dst = (char *)memMgmt->mem_alloc(length);
VerifyOrExit(dst != NULL, err = WEAVE_ERROR_NO_MEMORY);
err = aReader.GetString(dst, length);
SuccessOrExit(err);
LogReadWrite("%s utf8string '%s' allocating %d bytes at %p", "R", dst, length, dst);
*static_cast<char**>(aStructureData) = dst;
break;
}
case SerializedFieldTypeByteString:
{
SerializedByteString byteString;
byteString.mLen = aReader.GetLength();
byteString.mBuf = static_cast<uint8_t *>(memMgmt->mem_alloc(byteString.mLen));
VerifyOrExit(byteString.mBuf != NULL, err = WEAVE_ERROR_NO_MEMORY);
aReader.GetBytes(byteString.mBuf, byteString.mLen);
LogReadWrite("%s bytestring allocated %d bytes at %p", "R", byteString.mLen, byteString.mBuf);
*static_cast<SerializedByteString *>(aStructureData) = byteString;
break;
}
// We can hit this case when we have an array of structures.
case SerializedFieldTypeStructure:
err = aReader.EnterContainer(containerType);
SuccessOrExit(err);
VerifyOrExit(aReader.GetContainerType() == nl::Weave::TLV::kTLVType_Structure, err = WEAVE_ERROR_WRONG_TLV_TYPE);
err = aReader.Next();
if (err == WEAVE_END_OF_TLV)
err = WEAVE_NO_ERROR;
SuccessOrExit(err);
LogReadWriteStartContainer("%s Structure Start", "R");
err = TLVReaderToDeserializedData(aReader, aStructureData, aFieldPtr->mNestedFieldDescriptors, aContext);
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
}
SuccessOrExit(err);
LogReadWriteEndContainer("%s Structure End", "R");
err = aReader.ExitContainer(containerType);
break;
case SerializedFieldTypeArray:
{
err = aReader.EnterContainer(containerType);
SuccessOrExit(err);
VerifyOrExit(aReader.GetContainerType() == nl::Weave::TLV::kTLVType_Array, err = WEAVE_ERROR_WRONG_TLV_TYPE);
err = aReader.Next();
if (err == WEAVE_END_OF_TLV)
err = WEAVE_NO_ERROR;
SuccessOrExit(err);
LogReadWriteStartContainer("%s Array Start", "R");
err = ReadArrayData(aReader, aStructureData, aFieldPtr, aContext);
SuccessOrExit(err);
LogReadWriteEndContainer("%s Array End", "R");
err = aReader.ExitContainer(containerType);
// Skip over elements.
aFieldPtr++;
break;
}
default:
err = WEAVE_ERROR_INVALID_ARGUMENT;
break;
}
aFieldPtr++;
err = aReader.Next();
exit:
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
/**
* @brief
* A helper function to find the location of the nullified fields array
* located at the end of the C struct.
*
* The __nullified_fields__ member of the C struct is expected to be located
* directly after the last member described by the array of FieldDescriptors.
* It is not in the list of field descriptors, as it's meant to be a hidden
* utility for creators and consumers of nullable events. This struct member
* does not exist for events with no nullable fields, however by construction
* of setters and getters, no out of bounds accesses should occur.
*
* @param aStructureData[in] A pointer to the c-struct
*
* @param aFieldDescriptors[in] SchemaFieldDescriptors to describe the c struct
*
* @param aNullifiedFields[out] A pointer to __nullified_fields__ member of
* the c struct
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval #WEAVE_ERROR_INVALID_ARGUMENT If the field descriptor pointer is NULL.
*
* @retval #WEAVE_ERROR_INCORRECT_STATE If the format of the field descriptors
* doesn't match expectation.
*
*
*/
static WEAVE_ERROR FindNullifiedFieldsArray(void *aStructureData, const SchemaFieldDescriptor *aSchemaDescriptor, uint8_t *&aNullifiedFields)
{
uint32_t offset;
WEAVE_ERROR err;
const FieldDescriptor *lastFieldDescriptor = &(aSchemaDescriptor->mFields[aSchemaDescriptor->mNumFieldDescriptorElements - 1]);
err = GetArrayElementSize(offset, lastFieldDescriptor, lastFieldDescriptor->GetType());
SuccessOrExit(err);
aNullifiedFields = static_cast<uint8_t *>(aStructureData) + lastFieldDescriptor->mOffset + offset;
exit:
return err;
}
/**
* @brief
* A writer function to convert a data structure into a TLV structure. Uses
* a SchemaFieldDescriptor to interpret the data structure and write to the TLV.
*
* @param aWriter[in] The writer to use for writing out the structure
*
* @param aStructureData[in] A pointer to the c-structure data to write based on the SchemaFieldDescriptor
*
* @param aFieldDescriptors[in] SchemaFieldDescriptors to describe the c struct + TLV
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aWriter.
*
*/
WEAVE_ERROR SerializedDataToTLVWriter(TLVWriter &aWriter, void *aStructureData, const SchemaFieldDescriptor *aFieldDescriptors)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
const FieldDescriptor *fieldPtr = aFieldDescriptors->mFields;
const FieldDescriptor *endFieldPtr = &(aFieldDescriptors->mFields[aFieldDescriptors->mNumFieldDescriptorElements]);
int nullifiedBitIdx = 0;
uint8_t *nullifiedFields = NULL;
err = FindNullifiedFieldsArray(aStructureData, aFieldDescriptors, nullifiedFields);
SuccessOrExit(err);
while (fieldPtr < endFieldPtr)
{
bool aIsNullified = fieldPtr->IsNullable() &&
GET_FIELD_NULLIFIED_BIT(nullifiedFields, nullifiedBitIdx);
if (fieldPtr->IsNullable())
{
nullifiedBitIdx++;
}
err = WriteNullableDataForType(aWriter,
static_cast<char *>(aStructureData) + fieldPtr->mOffset,
fieldPtr,
fieldPtr->GetType(),
aIsNullified);
SuccessOrExit(err);
}
exit:
return err;
}
/**
* @brief
* A wrapper writer function that surrounds the SerializedDataToTLVWriter with a container.
* Also splits up an StructureSchemaPointerPair into structure data and descriptors to pass through.
*
* @param aWriter[in] The writer to use for writing out the structure
*
* @param aAppData[in] StructureSchemaPointerPair that contains a pointer to
* structure data and field descriptors. void* due to prototype
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aWriter.
*
*/
WEAVE_ERROR SerializedDataToTLVWriterHelper(TLVWriter &aWriter, uint8_t aDataTag, void *aAppData)
{
StructureSchemaPointerPair *structureSchemaPair = static_cast<StructureSchemaPointerPair *>(aAppData);
const bool inArray = false;
FieldDescriptor descriptor = { structureSchemaPair->mFieldSchema,
0,
static_cast<uint8_t>(SerializedFieldTypeStructure),
aDataTag };
const FieldDescriptor * pDescriptor = & descriptor;
#if WEAVE_CONFIG_SERIALIZATION_DEBUG_LOGGING
sIndentationLevel = 0;
#endif
return WriteDataForType(aWriter, structureSchemaPair->mStructureData, pDescriptor, SerializedFieldTypeStructure, inArray);
}
/**
* @brief
* A reader function to convert TLV into a C-struct. Uses
* a SchemaFieldDescriptor to interpret the data structure.
*
* It must be robust both to encoutering unknown fields and to not
* encountering an expected field.
*
* @param aReader[in] The reader to use for reading in the data
*
* @param aStructureData[in] A pointer to the destination c-structure data into which we'll read
* based on the SchemaFieldDescriptor
*
* @param aFieldDescriptors[in] SchemaFieldDescriptors to describe the c struct + TLV
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that may be returned from the aReader.
*
*/
WEAVE_ERROR TLVReaderToDeserializedData(nl::Weave::TLV::TLVReader &aReader,
void *aStructureData,
const SchemaFieldDescriptor *aFieldDescriptors,
SerializationContext *aContext)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
const FieldDescriptor *fieldPtr = aFieldDescriptors->mFields;
const FieldDescriptor *endFieldPtr = &(aFieldDescriptors->mFields[aFieldDescriptors->mNumFieldDescriptorElements]);
int nullifiedBitIdx = 0;
uint8_t *nullifiedFields = NULL;
err = FindNullifiedFieldsArray(aStructureData, aFieldDescriptors, nullifiedFields);
SuccessOrExit(err);
// while there are remaining fields to be parsed
while (fieldPtr < endFieldPtr)
{
// Tentatively searches for the next schema field matching the TLV tag in head.
const FieldDescriptor *searchFieldPtr = fieldPtr;
int searchNullifiedBitIdx = nullifiedBitIdx;
while (searchFieldPtr < endFieldPtr
&& TagNumFromTag(aReader.GetTag()) != searchFieldPtr->mTVDContextTag)
{
// Sets any nullable skipped fields to NULL
if (searchFieldPtr->IsNullable())
{
SET_FIELD_NULLIFIED_BIT(nullifiedFields, searchNullifiedBitIdx);
searchNullifiedBitIdx++;
}
searchFieldPtr++;
}
// If schema found for TLV tag
if (searchFieldPtr != endFieldPtr)
{
// Commit to found schema
fieldPtr = searchFieldPtr;
nullifiedBitIdx = searchNullifiedBitIdx;
bool isNullified = false;
bool isNullable = fieldPtr->IsNullable();
err = ReadNullableDataForType(aReader,
static_cast<char *>(aStructureData) + fieldPtr->mOffset,
fieldPtr,
fieldPtr->GetType(),
isNullified,
aContext);
if (isNullable)
{
if (isNullified)
{
SET_FIELD_NULLIFIED_BIT(nullifiedFields, nullifiedBitIdx);
}
else
{
CLEAR_FIELD_NULLIFIED_BIT(nullifiedFields, nullifiedBitIdx);
}
nullifiedBitIdx++;
}
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
break;
}
SuccessOrExit(err);
}
else
{
// If schema not found, move TLV forward to skip unknown field.
err = aReader.Next();
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
break;
}
}
}
// Sets any unparsed field (TLV ended prematurely) to NULL
while (fieldPtr < endFieldPtr)
{
if (fieldPtr->IsNullable())
{
SET_FIELD_NULLIFIED_BIT(nullifiedFields, nullifiedBitIdx);
nullifiedBitIdx++;
}
fieldPtr++;
}
exit:
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
/**
* @brief
* A wrapper reader function that surrounds the TLVReaderToDeserializedData with a container.
* Also splits up an StructureSchemaPointerPair into structure data and descriptors to pass through.
*
* @param aReader[in] The reader to use for writing out the structure
*
* @param aProfileId[in] Unused for the moment
*
* @param aStructureType[in] Unused for the moment
*
* @param aAppData[in] StructureSchemaPointerPair that contains a pointer to
* structure data and field descriptors. void* due to prototype
*
* @retval #WEAVE_NO_ERROR On success.
*
* @retval other Other errors that mey be returned from the aReader.
*
*/
WEAVE_ERROR TLVReaderToDeserializedDataHelper(nl::Weave::TLV::TLVReader &aReader,
uint8_t aDataTag,
void *aAppData,
SerializationContext *aContext)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
StructureSchemaPointerPair *structureSchemaPair = static_cast<StructureSchemaPointerPair *>(aAppData);
const bool inArray = false;
FieldDescriptor descriptor = { structureSchemaPair->mFieldSchema,
0,
static_cast<uint8_t>(SerializedFieldTypeStructure),
aDataTag };
const FieldDescriptor * pDescriptor = & descriptor;
#if WEAVE_CONFIG_SERIALIZATION_DEBUG_LOGGING
sIndentationLevel = 0;
#endif
err = ReadDataForType(aReader, structureSchemaPair->mStructureData, pDescriptor, SerializedFieldTypeStructure, inArray, aContext);
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
}
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
WEAVE_ERROR DeallocateDeserializedArray(void *aArrayData,
const SchemaFieldDescriptor *aFieldDescriptors,
SerializationContext *aContext/* = NULL*/)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
MemoryManagement *memMgmt = NULL;
ArrayLengthAndBuffer *array = NULL;
// aStructureData should be pointing to the wrapped length and buffer structure
array = static_cast<ArrayLengthAndBuffer *>(aArrayData);
if ((aContext == NULL) || !(aContext->memMgmt.mem_alloc && aContext->memMgmt.mem_free && aContext->memMgmt.mem_realloc))
{
memMgmt = &sDefaultMemoryManagement;
}
else
{
memMgmt = &aContext->memMgmt;
}
if (aFieldDescriptors == NULL)
{
LogReadWrite("%s Freeing array of primitive type at 0x%x", "R", array->mElementBuffer);
// The elements are of a primitive type, we can free the array now.
memMgmt->mem_free(array->mElementBuffer);
}
else
{
// The elements are structures, and we need to deallocate each one.
for (uint32_t i = 0; i < array->mNumElements; i++)
{
char *element = static_cast<char *>(array->mElementBuffer) + i*aFieldDescriptors->mSize;
err = DeallocateDeserializedStructure(element, aFieldDescriptors, aContext);
SuccessOrExit(err);
}
LogReadWrite("%s Freeing array of structures at 0x%x", "R", array->mElementBuffer);
// Free the array now.
memMgmt->mem_free(array->mElementBuffer);
}
exit:
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
WEAVE_ERROR DeallocateDeserializedStructure(void *aStructureData,
const SchemaFieldDescriptor *aFieldDescriptors,
SerializationContext *aContext/* = NULL*/)
{
#if WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
WEAVE_ERROR err = WEAVE_NO_ERROR;
const FieldDescriptor *fieldPtr = aFieldDescriptors->mFields;
const FieldDescriptor *endFieldPtr = &(aFieldDescriptors->mFields[aFieldDescriptors->mNumFieldDescriptorElements]);
MemoryManagement *memMgmt = NULL;
if ((aContext == NULL) || !(aContext->memMgmt.mem_alloc && aContext->memMgmt.mem_free && aContext->memMgmt.mem_realloc))
{
memMgmt = &sDefaultMemoryManagement;
}
else
{
memMgmt = &aContext->memMgmt;
}
while (fieldPtr < endFieldPtr)
{
char *currentFieldData = static_cast<char *>(aStructureData) + fieldPtr->mOffset;
switch (fieldPtr->GetType())
{
case SerializedFieldTypeStructure:
{
err = DeallocateDeserializedStructure(currentFieldData,
fieldPtr->mNestedFieldDescriptors,
aContext);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeArray:
{
// fieldPtr is telling us we have an array, but the
// elements reside at the next field. Increment
// fieldPtr to get us to the elements, but leave
// currentFieldData where it is.
fieldPtr++;
err = DeallocateDeserializedArray(currentFieldData,
fieldPtr->mNestedFieldDescriptors,
aContext);
SuccessOrExit(err);
break;
}
case SerializedFieldTypeUTF8String:
{
char *str = *((char **)currentFieldData);
LogReadWrite("%s Freeing UTF8String '%s' at 0x%x", "R", str, str);
// Go ahead and free it here.
memMgmt->mem_free(str);
break;
}
default:
{
// A primitive type, doesn't need to be deallocated.
break;
}
}
fieldPtr++;
}
exit:
if (err == WEAVE_END_OF_TLV)
{
err = WEAVE_NO_ERROR;
}
return err;
#else // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
return WEAVE_ERROR_NOT_IMPLEMENTED;
#endif // WEAVE_CONFIG_SERIALIZATION_ENABLE_DESERIALIZATION
}
} // nl