Hdlc: Change the encoder buffer model. (#263)

This commit makes the following changes to Hdlc implementation:

- It introduces a buffer write iterator class (`BufferWriteIterator`)
  which is used by the encoder methods.

- It modifies the Hdlc encoding to keep the state valid in case of
  failure (i.e., no buffer space), so the encoding can continue from
  where we left off.
This commit is contained in:
Abtin Keshavarzian
2016-07-14 20:57:39 -07:00
committed by Jonathan Hui
parent 6e40260418
commit dc489cbbf8
4 changed files with 176 additions and 71 deletions
+54 -28
View File
@@ -40,7 +40,7 @@ namespace Hdlc {
* This method updates an FCS.
*
* @param[in] aFcs The FCS to update.
* @param[in] aByte The intput byte value.
* @param[in] aByte The input byte value.
*
* @returns The updated FCS.
*
@@ -103,76 +103,102 @@ uint16_t UpdateFcs(uint16_t aFcs, uint8_t aByte)
return (aFcs >> 8) ^ sFcsTable[(aFcs ^ aByte) & 0xff];
}
ThreadError Encoder::Init(uint8_t *aOutBuf, uint16_t &aOutLength)
Encoder::BufferWriteIterator::BufferWriteIterator(void)
{
mWritePointer = NULL;
mRemainingLength = 0;
}
ThreadError Encoder::BufferWriteIterator::WriteByte(uint8_t aByte)
{
ThreadError error = kThreadError_None;
mFcs = kInitFcs;
VerifyOrExit(mRemainingLength > 0, error = kThreadError_NoBufs);
VerifyOrExit(aOutLength > 0, error = kThreadError_NoBufs);
aOutBuf[0] = kFlagSequence;
aOutLength = 1;
*mWritePointer++ = aByte;
mRemainingLength--;
exit:
return error;
}
ThreadError Encoder::Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength)
bool Encoder::BufferWriteIterator::CanWrite(uint16_t aWriteLength) const
{
return (mRemainingLength >= aWriteLength);
}
ThreadError Encoder::Init(BufferWriteIterator &aIterator)
{
mFcs = kInitFcs;
return aIterator.WriteByte(kFlagSequence);
}
ThreadError Encoder::Encode(uint8_t aInByte, BufferWriteIterator &aIterator)
{
ThreadError error = kThreadError_None;
mFcs = UpdateFcs(mFcs, aInByte);
if (aInByte == kFlagSequence || aInByte == kEscapeSequence)
{
VerifyOrExit(mOutOffset + 2 < aOutLength, error = kThreadError_NoBufs);
aOutBuf[mOutOffset++] = kEscapeSequence;
aOutBuf[mOutOffset++] = aInByte ^ 0x20;
VerifyOrExit(aIterator.CanWrite(2) , error = kThreadError_NoBufs);
aIterator.WriteByte(kEscapeSequence);
aIterator.WriteByte(aInByte ^ 0x20);
}
else
{
VerifyOrExit(mOutOffset + 1 < aOutLength, error = kThreadError_NoBufs);
aOutBuf[mOutOffset++] = aInByte;
SuccessOrExit(error = aIterator.WriteByte(aInByte));
}
mFcs = UpdateFcs(mFcs, aInByte);
exit:
return error;
}
ThreadError Encoder::Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength)
ThreadError Encoder::Encode(const uint8_t *aInBuf, uint16_t aInLength, BufferWriteIterator &aIterator)
{
ThreadError error = kThreadError_None;
mOutOffset = 0;
BufferWriteIterator oldIterator(aIterator);
uint16_t oldFcs = mFcs;
for (int i = 0; i < aInLength; i++)
{
SuccessOrExit(error = Encode(aInBuf[i], aOutBuf, aOutLength));
SuccessOrExit(error = Encode(aInBuf[i], aIterator));
}
exit:
aOutLength = mOutOffset;
if (error != kThreadError_None)
{
aIterator = oldIterator;
mFcs = oldFcs;
}
return error;
}
ThreadError Encoder::Finalize(uint8_t *aOutBuf, uint16_t &aOutLength)
ThreadError Encoder::Finalize(BufferWriteIterator &aIterator)
{
ThreadError error = kThreadError_None;
BufferWriteIterator oldIterator(aIterator);
uint16_t oldFcs = mFcs;
uint16_t fcs = mFcs;
mOutOffset = 0;
fcs ^= 0xffff;
SuccessOrExit(error = Encode(fcs, aOutBuf, aOutLength));
SuccessOrExit(error = Encode(fcs >> 8, aOutBuf, aOutLength));
SuccessOrExit(error = Encode(fcs, aIterator));
SuccessOrExit(error = Encode(fcs >> 8, aIterator));
VerifyOrExit(mOutOffset < aOutLength, error = kThreadError_NoBufs);
aOutBuf[mOutOffset++] = kFlagSequence;
aOutLength = mOutOffset;
SuccessOrExit(error = aIterator.WriteByte(kFlagSequence));
exit:
if (error != kThreadError_None)
{
aIterator = oldIterator;
mFcs = oldFcs;
}
return error;
}
+67 -18
View File
@@ -27,7 +27,7 @@
/**
* @file
* This file includes definitions for an HDLC-lite encoder and decoer.
* This file includes definitions for an HDLC-lite encoder and decoder.
*/
#ifndef HDLC_HPP_
@@ -54,48 +54,97 @@ namespace Hdlc {
class Encoder
{
public:
/**
* This method begins an HDLC frame and puts the initial bytes into @p aOutBuf.
* This class defines a write iterator into a buffer used by Encoder.
*
* @param[in] aOutBuf A pointer to the output buffer.
* @param[inout] aOutLength On entry, the output buffer size; On exit, the output length.
* Hdlc users should sub-class this to add the actual buffer space.
*/
class BufferWriteIterator
{
public:
/**
* This method writes a byte to the buffer and updates the iterator (if space is available).
*
* @retval kThreadError_None Successfully wrote the byte and updates the iterator.
* @retval kThreadError_NoBufs Insufficient buffer space.
*
*/
ThreadError WriteByte(uint8_t aByte);
/**
* This method checks if there is buffer space available to write @p aWriteLength bytes.
*
* param[in] aWriteLength Number of bytes to write.
*
* @retval true Enough buffer space available to write the requested number of bytes.
* @retval false Insufficient buffer space to write the requested number of bytes.
*
*/
bool CanWrite(uint16_t aWriteLength) const;
protected:
BufferWriteIterator(void); ///< Protected constructor to ensure no direct instantiation.
uint8_t *mWritePointer; ///< A pointer to current write position in the buffer.
uint16_t mRemainingLength; ///< Number of remaining bytes available to write.
};
/**
* This method begins an HDLC frame and puts the initial bytes into a buffer at the given @p aIterator.
*
* @param[inout] aIterator A reference to a buffer write iterator. On successful exit, the iterator is updated.
*
* @retval kThreadError_None Successfully started the HDLC frame.
* @retval kThreadError_NoBufs Insufficient buffer space available to start the HDLC frame.
*
*/
ThreadError Init(uint8_t *aOutBuf, uint16_t &aOutLength);
ThreadError Init(BufferWriteIterator &aIterator);
/**
* This method encodes the frame.
* This method encodes a single byte into a buffer at @p aIterator.
*
* @param[in] aInBuf A pointer to the input buffer.
* @param[in] aInLength The number of bytes in @p aInBuf to encode.
* @param[out] aOutBuf A pointer to the output buffer.
* @param[out] aOutLength On exit, the number of bytes placed in @p aOutBuf.
* If there is no space to add the byte, the write iterator remains the same.
*
* @param[in] aInByte A byte to encode and add.
* @param[inout] aIterator A reference to a write buffer iterator. On successful exit, the iterator is updated.
*
* @retval kThreadError_None Successfully encoded and added the byte.
* @retval kThreadError_NoBufs Insufficient buffer space available to encode and add the byte.
*
*/
ThreadError Encode(uint8_t aInByte, BufferWriteIterator &aIterator);
/**
* This method encodes the frame into a buffer at @p aIterator.
*
* This method returns success only if there is space in buffer to encode the entire frame. If there is no space
* to encode the entire frame, the write iterator remains the same.
*
* @param[in] aInBuf A pointer to the input buffer.
* @param[in] aInLength The number of bytes in @p aInBuf to encode.
* @param[inout] aIterator A reference to a write buffer iterator. On successful exit, the iterator is updated.
*
* @retval kThreadError_None Successfully encoded the HDLC frame.
* @retval kThreadError_NoBufs Insufficient buffer space available to encode the HDLC frame.
*
*/
ThreadError Encode(const uint8_t *aInBuf, uint16_t aInLength, uint8_t *aOutBuf, uint16_t &aOutLength);
ThreadError Encode(const uint8_t *aInBuf, uint16_t aInLength, BufferWriteIterator &aIterator);
/**
* This method ends an HDLC frame and puts the initial bytes into @p aOutBuf.
* This method finalizes an HDLC frame.
*
* @param[in] aOutBuf A pointer to the output buffer.
* @param[inout] aOutLength On entry, the output buffer size; On exit, the output length.
* @param[inout] aIterator A reference to a write buffer iterator. On successful exit, the iterator is updated.
*
* @retval kThreadError_None Successfully ended the HDLC frame.
* @retval kThreadError_NoBufs Insufficient buffer space available to end the HDLC frame.
*
*/
ThreadError Finalize(uint8_t *aOutBuf, uint16_t &aOutLength);
ThreadError Finalize(BufferWriteIterator &aIterator);
private:
ThreadError Encode(uint8_t aInByte, uint8_t *aOutBuf, uint16_t aOutLength);
uint16_t mOutOffset;
uint16_t mFcs;
};
+39 -22
View File
@@ -47,33 +47,58 @@ extern "C" void otNcpInit(void)
sNcpUart = new(&sNcpRaw) NcpUart;
}
NcpUart::SendHdlcBuffer::SendHdlcBuffer(void)
: BufferWriteIterator()
{
Reset();
}
void
NcpUart::SendHdlcBuffer::Reset(void)
{
mWritePointer = mBuffer;
mRemainingLength = sizeof(mBuffer);
}
uint16_t
NcpUart::SendHdlcBuffer::GetLength(void) const
{
return static_cast<uint16_t>(mWritePointer - mBuffer);
}
const uint8_t *
NcpUart::SendHdlcBuffer::GetBuffer(void) const
{
return mBuffer;
}
uint16_t
NcpUart::SendHdlcBuffer::GetRemainingLength(void) const
{
return mRemainingLength;
}
NcpUart::NcpUart():
NcpBase(),
mFrameDecoder(mReceiveFrame, sizeof(mReceiveFrame), &HandleFrame, this)
mFrameDecoder(mReceiveFrame, sizeof(mReceiveFrame), &HandleFrame, this),
mSendFrame()
{
}
uint16_t
NcpUart::OutboundFrameGetRemaining(void)
{
return static_cast<int16_t>(sizeof(mSendFrame) - (mSendFrameIter - mSendFrame));
return mSendFrame.GetRemainingLength();
}
ThreadError
NcpUart::OutboundFrameBegin(void)
{
ThreadError errorCode;
uint16_t outLength;
mSendFrameIter = mSendFrame;
outLength = OutboundFrameGetRemaining();
mSendFrame.Reset();
errorCode = mFrameEncoder.Init(mSendFrameIter, outLength);
if (errorCode == kThreadError_None)
{
mSendFrameIter += outLength;
}
errorCode = mFrameEncoder.Init(mSendFrame);
return errorCode;
}
@@ -82,14 +107,8 @@ ThreadError
NcpUart::OutboundFrameFeedData(const uint8_t *frame, uint16_t frameLength)
{
ThreadError errorCode;
uint16_t outLength(OutboundFrameGetRemaining());
errorCode = mFrameEncoder.Encode(frame, frameLength, mSendFrameIter, outLength);
if (errorCode == kThreadError_None)
{
mSendFrameIter += outLength;
}
errorCode = mFrameEncoder.Encode(frame, frameLength, mSendFrame);
return errorCode;
}
@@ -122,14 +141,12 @@ ThreadError
NcpUart::OutboundFrameSend(void)
{
ThreadError errorCode;
uint16_t outLength(OutboundFrameGetRemaining());
errorCode = mFrameEncoder.Finalize(mSendFrameIter, outLength);
errorCode = mFrameEncoder.Finalize(mSendFrame);
if (errorCode == kThreadError_None)
{
mSendFrameIter += outLength;
errorCode = otPlatUartSend(mSendFrame, mSendFrameIter - mSendFrame);
errorCode = otPlatUartSend(mSendFrame.GetBuffer(), mSendFrame.GetLength());
}
if (errorCode == kThreadError_None)
+16 -3
View File
@@ -62,9 +62,22 @@ private:
Hdlc::Encoder mFrameEncoder;
Hdlc::Decoder mFrameDecoder;
uint8_t mSendFrame[1500];
uint8_t mReceiveFrame[1500];
uint8_t *mSendFrameIter;
class SendHdlcBuffer : public Hdlc::Encoder::BufferWriteIterator
{
public:
SendHdlcBuffer(void);
void Reset(void);
uint16_t GetLength(void) const;
uint16_t GetRemainingLength(void) const;
const uint8_t *GetBuffer(void) const;
private:
uint8_t mBuffer[1500];
};
SendHdlcBuffer mSendFrame;
uint8_t mReceiveFrame[1500];
};
} // namespace Thread