blob: 117669a39fd7c6aa3a9d4ae0f3033791848e68b5 [file] [log] [blame]
/*
*
* Copyright (c) 2013-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.
*/
/**
* @file
* This file implements objects commonly used for the
* processing of Weave messages.
*
*/
#include <Weave/Core/WeaveCore.h>
#include <Weave/Core/WeaveMessageLayer.h>
#include <Weave/Profiles/ProfileCommon.h>
#include <Weave/Profiles/service-directory/ServiceDirectory.h>
using namespace ::nl::Weave;
using namespace ::nl::Weave::TLV;
using namespace ::nl::Weave::Profiles;
using namespace ::nl::Weave::Profiles::Common;
//-------------------- definitions for the message iterator --------------------
/**
* The constructor method.
*
* @param aBuffer A message buffer to iterate over.
*/
MessageIterator::MessageIterator(PacketBuffer *aBuffer)
{
Retain(aBuffer);
thePoint = aBuffer->Start();
}
/**
* @param aDestination A place to put a byte read off the buffer.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::readByte(uint8_t *aDestination)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasData(1))
{
err = WEAVE_NO_ERROR;
*aDestination = READBYTE(thePoint);
}
return err;
}
/**
* @param aDestination A place to put a short read off the buffer.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::read16(uint16_t *aDestination)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasData(2))
{
err = WEAVE_NO_ERROR;
READ16(thePoint, *aDestination);
}
return err;
}
/**
* @param aDestination A place to put a 32-bit value read off the buffer.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::read32(uint32_t *aDestination)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasData(4))
{
err = WEAVE_NO_ERROR;
READ32(thePoint, *aDestination);
}
return err;
}
/**
* @param aDestination A place to put a 64-bit value read off the buffer.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::read64(uint64_t *aDestination)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
uint8_t *p;
if (hasData(8))
{
err = WEAVE_NO_ERROR;
p = (uint8_t *)aDestination;
for (int i = 0; i < 8; i++)
readByte(p++);
}
return err;
}
/**
* @param aLength The length of the string to be read.
* @param aString A place to put the string.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::readString(uint16_t aLength, char* aString)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasData(aLength))
{
err = WEAVE_NO_ERROR;
for (uint16_t i = 0; i < aLength; i++)
{
*aString = READBYTE(thePoint);
aString++;
}
}
return err;
}
/**
* @param aLength The length of the byte string to be read.
* @param aByteString A place to put the bytes.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::readBytes(uint16_t aLength, uint8_t* aByteString)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasData(aLength))
{
err = WEAVE_NO_ERROR;
for (uint16_t i = 0; i < aLength; i++)
{
*aByteString = READBYTE(thePoint);
aByteString++;
}
}
return err;
}
/**
* @param aValue A byte value to write out.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::writeByte(uint8_t aValue)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasRoom(1))
{
err = WEAVE_NO_ERROR;
WRITEBYTE(thePoint, aValue);
finishWriting();
}
return err;
}
/**
* @param aValue A short value to write out.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::write16(uint16_t aValue)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasRoom(2))
{
err = WEAVE_NO_ERROR;
WRITE16(thePoint, aValue);
finishWriting();
}
return err;
}
/**
* @param aValue A 32-bit value to write out.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::write32(uint32_t aValue)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasRoom(4))
{
err = WEAVE_NO_ERROR;
WRITE32(thePoint, aValue);
finishWriting();
}
return err;
}
/**
* @param aValue A 64-bit value to write out.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::write64(uint64_t aValue)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
uint8_t *p = (uint8_t *)&aValue;
if (hasRoom(8))
{
err = WEAVE_NO_ERROR;
for (int i = 0; i < 8; i++)
writeByte(*p++);
}
return err;
}
/**
* @param aLength The length of the string to write.
* @param aString The string itself.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::writeString(uint16_t aLength, char *aString)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasRoom(aLength))
{
err = WEAVE_NO_ERROR;
for (uint16_t i = 0; i < aLength; i++)
{
WRITEBYTE(thePoint, *aString);
aString++;
}
finishWriting();
}
return err;
}
/**
* @param aLength The length of the byte string to write.
* @param aString The byte string itself.
*
* @retval WEAVE_NO_ERROR If it's all OK.
* @retval WEAVE_ERROR_BUFFER_TOO_SMALL If we're running past the end of the buffer.
*/
WEAVE_ERROR MessageIterator::writeBytes(uint16_t aLength, uint8_t *aByteString)
{
WEAVE_ERROR err = WEAVE_ERROR_BUFFER_TOO_SMALL;
if (hasRoom(aLength))
{
err = WEAVE_NO_ERROR;
for (uint16_t i = 0; i < aLength; i++)
{
WRITEBYTE(thePoint, *aByteString);
aByteString++;
}
finishWriting();
}
return err;
}
/**
* Increment a message iterator by 1 if there's room.
*/
MessageIterator& MessageIterator::operator ++(void)
{
if (hasRoom(1))
++thePoint;
return *this;
}
/**
* @param inc An increment to apply to the message iterator.
*
* @return The iterator incremented by the given value if there's
* room, or else slammed right up against the end if there's not.
*/
MessageIterator& MessageIterator::operator +(uint16_t inc)
{
if (hasRoom(inc))
thePoint += inc;
else
thePoint += mBuffer->AvailableDataLength();
return *this;
}
/**
* @param dec A decrement to apply to the message iterator.
*
* @return The iterator decremented by the given value if there's
* room, or else slammed right up against the beginning if there's not.
*/
MessageIterator& MessageIterator::operator -(uint16_t dec)
{
if (mBuffer->DataLength() > dec)
thePoint -= dec;
else
thePoint = mBuffer->Start();
return *this;
}
/**
* @param aMessageIterator Another message iterator to compare with.
*/
bool MessageIterator::operator==(const MessageIterator &aMessageIterator)
{
return(thePoint == aMessageIterator.thePoint && mBuffer == aMessageIterator.mBuffer);
}
/**
* @param aMessageIterator Another message iterator to compare with.
*/
bool MessageIterator::operator!=(const MessageIterator &aMessageIterator)
{
return !(thePoint == aMessageIterator.thePoint && mBuffer == aMessageIterator.mBuffer);
}
/**
* @return What we're looking at in the buffer.
*/
uint8_t& MessageIterator::operator *(void)
{
return *thePoint;
}
/**
* Set the point to after any data currently in the buffer.
*/
void MessageIterator::append(void)
{
thePoint = mBuffer->Start() + mBuffer->DataLength();
}
/**
* @param inc An integer amount that may be read from the
* buffer.
*
* @retval true The buffer's current data length is greater than or equal
* to the given increment.
* @retval false Otherwise.
*/
bool MessageIterator::hasData(uint16_t inc)
{
return (thePoint + inc) <= (mBuffer->Start() + mBuffer->DataLength());
}
/**
* @param inc An integer amount that may be written to the
* buffer.
*
* @retval true The difference between the buffer's current data
* length and its maximum allowable data length, (its available data
* length), is less than or equal to the given increment.
* @retval false Otherwise.
*/
bool MessageIterator::hasRoom(uint16_t inc)
{
return inc <= (mBuffer->AvailableDataLength());
}
/**
* Adjust the buffer after writing.
*/
void MessageIterator::finishWriting(void)
{
mBuffer->SetDataLength((uint16_t)(thePoint - mBuffer->Start()));
}
// -------------------- definitions for referenced strings --------------------
/**
* The no-arg constructor for referenced strings.
*/
ReferencedString::ReferencedString(void) :
RetainedPacketBuffer()
{
theLength = 0;
theString = NULL;
isShort = false;
}
/***
* @fn WEAVE_ERROR ReferencedString::init(uint16_t aLength, char *aString, System::PacketBuffer *aBuffer)
* @brief Initialize a ReferencedString
*
* Initialize the ReferencedString with a string and a PacketBuffer backing a that string.
*
* @param[in] aLength A length for the referenced string
* @param[in] aString A pointer to the string data (in the buffer)
* @param[in] aBuffer A messsage buffer in which the string resides
*
* @retval #WEAVE_NO_ERROR On success
* @retval #WEAVE_ERROR_INVALID_STRING_LENGTH The supplied string is too long
*/
WEAVE_ERROR ReferencedString::init(uint16_t aLength, char *aString, System::PacketBuffer *aBuffer)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (aLength > (aBuffer->AvailableDataLength() - aBuffer->DataLength()))
err = WEAVE_ERROR_INVALID_STRING_LENGTH;
else
{
Retain(aBuffer);
theLength = aLength;
theString = aString;
isShort = false;
}
return err;
}
/***
* @fn WEAVE_ERROR ReferencedString::init(uint16_t aLength, char *aString)
* @brief Initialize a ReferencedString
*
* This initializer should be used if there's no message buffer because we're
* creating this object with an intention of immediately sending it.
*
* @note If the string passed in here is stack-allocated, any outgoing message
* created in this way must be sent before the stack context in which it was
* created is exited.
*
* @param[in] aLength A length for the referenced string
* @param[in] aString A pointer to the string data
*
* @retval #WEAVE_NO_ERROR Unconditionally
*/
WEAVE_ERROR ReferencedString::init(uint16_t aLength, char *aString)
{
theLength = aLength;
theString = aString;
Release();
isShort = false;
return WEAVE_NO_ERROR;
}
/***
* @fn WEAVE_ERROR ReferencedString::init(uint8_t aLength, char *aString, System::PacketBuffer *aBuffer)
* @overload
*/
WEAVE_ERROR ReferencedString::init(uint8_t aLength, char *aString, System::PacketBuffer *aBuffer)
{
if (aLength > (aBuffer->AvailableDataLength() - aBuffer->DataLength())) return WEAVE_ERROR_INVALID_STRING_LENGTH;
Retain(aBuffer);
theLength = (uint16_t)aLength;
theString = aString;
isShort = true;
return WEAVE_NO_ERROR;
}
/***
* @fn WEAVE_ERROR ReferencedString::init(uint8_t aLength, char *aString)
* @overload
*/
WEAVE_ERROR ReferencedString::init(uint8_t aLength, char *aString)
{
theLength = (uint16_t) aLength;
theString = aString;
Release();
isShort = true;
return WEAVE_NO_ERROR;
}
/**
* @param &i An iterator over the message being packed.
* @return WEAVE_ERROR
*/
WEAVE_ERROR ReferencedString::pack(MessageIterator &i)
{
WEAVE_ERROR e;
if (isShort)
e = i.writeByte((uint8_t)theLength);
else
e = i.write16(theLength);
if (e == WEAVE_NO_ERROR)
e = i.writeString(theLength, theString);
return e;
}
/**
* @param &i An iterator over the message being parsed.
* @param &aString A place to put the result of parsing.
*
* @retval WEAVE_NO_ERROR String parsed successfully.
* @retval WEAVE_ERROR_INVALID_STRING_LENGTH The string is too long for the buffer
* (this should never happen).
*/
WEAVE_ERROR ReferencedString::parse(MessageIterator &i, ReferencedString &aString)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
uint16_t len = 0;
if (aString.isShort)
i.readByte((uint8_t *)&len);
else
i.read16(&len);
if (i.hasRoom(len))
{
aString.theLength = len;
aString.theString = (char *)i.thePoint;
aString.Retain(i.GetBuffer());
// we need to skip over the string
(i.thePoint) += len;
}
else
err = WEAVE_ERROR_INVALID_STRING_LENGTH;
return err;
}
/**
* @param &aReferencedString A string to check against.
*
* @retval true The strings are equal.
* @retval false The strings are not equal.
*/
bool ReferencedString::operator== (const ReferencedString &aReferencedString) const
{
bool result = false;
if (theLength == aReferencedString.theLength)
{
for (int i = 0; i < theLength; i++)
{
if (theString[i] != aReferencedString.theString[i])
goto exit;
}
result = true;
}
exit:
return result;
}
/**
* @return A printable string
*/
char *ReferencedString::printString(void)
{
theString[theLength] = 0;
return theString;
}
/**
*-------------------- definitions for TLV data --------------------
*/
/**
* The no-arg constructor for TLV data. Delivers a free/uninitialized
* object which must be subjected to one of the init() methods defined
* here in order to be useful.
*/
ReferencedTLVData::ReferencedTLVData(void) :
RetainedPacketBuffer()
{
theLength = 0;
theMaxLength = 0;
theData = NULL;
theWriteCallback = NULL;
theAppState = NULL;
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::init(System::PacketBuffer *aBuffer)
*
* @brief Initialize the ReferencedTLVData object given a PacketBuffer
*
* Initialize a ReferencedTLVData object given a buffer full of
* TLV. This assumes that the buffer ONLY contains TLV.
*
* @param [in] aBuffer A message buffer in which the TLV resides.
*
* @retval #WEAVE_NO_ERROR Unconditionally
*/
WEAVE_ERROR ReferencedTLVData::init(System::PacketBuffer *aBuffer)
{
Retain(aBuffer);
theData = mBuffer->Start();
theLength = mBuffer->DataLength();
theMaxLength = mBuffer->MaxDataLength();
theWriteCallback = NULL;
theAppState = NULL;
return WEAVE_NO_ERROR;
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::init(MessageIterator &i)
*
* @brief Initialize a ReferencedTLVData object given a MessageIterator.
*
* Initialize a ReferencedTLVData object given a MessageIterator. In
* this case, the TLV is that last portion of the buffer and we pass in
* a message iterator that's pointing to it.
*
* @param [in] i A message iterator pointing to TLV to be extracted.
*
* @retval #WEAVE_NO_ERROR Unconditionally
*/
WEAVE_ERROR ReferencedTLVData::init(MessageIterator &i)
{
System::PacketBuffer *theBuffer = i.GetBuffer();
Retain(theBuffer);
theData = i.thePoint;
theLength = theBuffer->DataLength() - (i.thePoint - mBuffer->Start());
theMaxLength = theBuffer->MaxDataLength();
theWriteCallback = NULL;
theAppState = NULL;
return WEAVE_NO_ERROR;
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::init(uint16_t aLength, uint16_t aMaxLength, uint8_t *aByteString)
*
* @brief Initialize a ReferencedTLVObject given a byte string.
*
* Initialize ReferencedTLVData object with a byte string containing
* TLV. This initializer is the one we use if there's no PacketrBuffer
* because we're creating one of these to pack and send.
*
* @note if the string passed in here is stack-allocated, any
* outgoing message created in this way must be sent before the stack
* context in which it was created is exited.
*
* @param[in] aLength A length for the TLV data
* @param[in] aMaxLength The total length of the buffer
* @param[in] aByteString A pointer to the string data
*
* @retval #WEAVE_NO_ERROR Unconditionally
*/
WEAVE_ERROR ReferencedTLVData::init(uint16_t aLength, uint16_t aMaxLength, uint8_t *aByteString)
{
theLength = aLength;
theMaxLength = aMaxLength;
theData = aByteString;
Release();
theWriteCallback = NULL;
theAppState = NULL;
return WEAVE_NO_ERROR;
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::init(TLVWriteCallback aWriteCallback, void *anAppState)
*
* @brief Initialize a RefererencedTLVData object given a callback function.
*
* Initialize a ReferencedTLVData object. Instead of explicitly
* supplying the data, this version provides function, the write
* callback, and a reference object, which will be passed to it, along
* with a TLVWriter object, when the referenced data is supposed to be
* packed and sent. The signature of that callback is:
*
* @code
* typedef void (*TLVWriteCallback)(TLV::TLVWriter &aWriter, void *aAppState);
* @endcode
* @param [in] aWriteCallback the function to be
* called when it's time to write some TLV.
* @param [in] anAppState an application state object to be
* passed to the callback along with the writer.
*
* @retval #WEAVE_NO_ERROR On success.
* @retval #WEAVE_ERROR_INVALID_ARGUMENT If the write callback is not supplied.
*/
WEAVE_ERROR ReferencedTLVData::init(TLVWriteCallback aWriteCallback, void *anAppState)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
if (aWriteCallback != NULL)
{
theWriteCallback = aWriteCallback;
theAppState = anAppState;
}
else
err = WEAVE_ERROR_INVALID_ARGUMENT;
theLength = 0;
theMaxLength = 0;
theData = NULL;
mBuffer = NULL;
return err;
}
/**
* Free a ReferencedTLVData object, which is to say, undefine it.
*
*/
void ReferencedTLVData::free(void)
{
RetainedPacketBuffer::Release();
/**
* in this case you have to clear out the write callback and app
* state as well since that may be how the data is getting generated.
*/
theWriteCallback = NULL;
theAppState = NULL;
// and the rest of it for good measure
theLength = 0;
theMaxLength = 0;
theData = NULL;
}
/**
* Check if a ReferencedTLVData object is "free", i.e. undefined.
*
* @return true if the object is undefined, false otherwise.
*/
bool ReferencedTLVData::isFree(void)
{
return (mBuffer == NULL && theWriteCallback == NULL && theAppState == NULL);
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::pack(MessageIterator &i)
*
* @brief Pack a ReferencedTLVData object using a TLVWriter.
*
* @param [in] i An iterator over the message being packed.
* @param [in] maxLen The maximum number of bytes that should be written to the output buffer.
*
* @return a WEAVE_ERROR - WEAVE_NO_ERROR if all goes well, otherwise
* an error reflecting an inability of the writer to write the
* relevant bytes. Note that the write callback is not allowed to
* return an error and so fails silently.
*/
WEAVE_ERROR ReferencedTLVData::pack(MessageIterator &i, uint32_t maxLen)
{
WEAVE_ERROR err = WEAVE_NO_ERROR;
System::PacketBuffer *theBuffer = i.GetBuffer();
uint16_t oldDataLength = theBuffer->DataLength();
TLVWriter writer;
if (theWriteCallback != NULL)
{
theData = i.thePoint;
writer.Init(theBuffer, maxLen);
theWriteCallback(writer, theAppState);
theLength = theBuffer->DataLength() - oldDataLength;
i.thePoint += theLength;
}
else
err = i.writeBytes(theLength, theData);
return err;
}
/**
* @fn WEAVE_ERROR ReferencedTLVData::parse(MessageIterator &i, ReferencedTLVData &aTarget)
*
* @brief Parse a ReferencedTLVData object from a supplied MessageIterator
*
* Parse a ReferenceTLVData object from a MessageIterator object
* assumed to be pointing at the TLV portion of a message.
*
* Note that no actual "parsing" is done here since the TLV is left in
* the buffer and not manipulated at all. This method mainly just sets
* up the ReferencedTLVData structure for later use.
*
* @param [in] i An iterator over the message being
* parsed.
* @param [out] aTarget A place to put the result
* of parsing.
*
* @retval #WEAVE_NO_ERROR Unconditionally
*/
WEAVE_ERROR ReferencedTLVData::parse(MessageIterator &i, ReferencedTLVData &aTarget)
{
PacketBuffer *buff = i.GetBuffer();
aTarget.Retain(buff);
aTarget.theLength = buff->DataLength() - (i.thePoint - buff->Start());
if (aTarget.theLength != 0)
aTarget.theData = i.thePoint;
else
aTarget.theData = NULL;
// we need to skip over the data
(i.thePoint) += aTarget.theLength;
return WEAVE_NO_ERROR;
}
/**
* Check a ReferencedTLVData object against another for equality.
*
* Note that this only really makes sense in the case of two objects
* that have actual data in them backed by a buffer or string.
*
* @param [in] Another an object to check against
*
* @retval true The objects are equal.
* @retval false The objects strings are not equal.
*/
bool ReferencedTLVData::operator== (const ReferencedTLVData &another) const
{
bool result = false;
if (theLength == another.theLength)
{
for (int i = 0; i < theLength; i++)
{
if (theData[i] != another.theData[i])
goto exit;
}
result = true;
}
exit:
return result;
}