3 * Copyright (c) 2020-2021 Project CHIP Authors
4 * Copyright (c) 2016-2017 Nest Labs, Inc.
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
22 * This file implements the circular buffer for TLV
23 * elements. When used as the backing store for the TLVReader and
24 * TLVWriter, those classes will work with the wraparound of data
25 * within the buffer. Additionally, the TLVWriter will be able
26 * to continually add top-level TLV elements by evicting
27 * pre-existing elements.
30 #ifndef __STDC_LIMIT_MACROS
31 #define __STDC_LIMIT_MACROS
33 #include <core/CHIPCircularTLVBuffer.h>
35 #include <core/CHIPCore.h>
36 #include <core/CHIPEncoding.h>
37 #include <core/CHIPTLV.h>
39 #include <support/CodeUtils.h>
46 using namespace chip::Encoding;
50 * CHIPCircularTLVBuffer constructor
52 * @param[in] inBuffer A pointer to the backing store for the queue
54 * @param[in] inBufferLength Length, in bytes, of the backing store
56 * @param[in] inHead Initial point for the head. The @a inHead pointer is must fall within the backing store for the
57 * circular buffer, i.e. within @a inBuffer and &(@a inBuffer[@a inBufferLength])
59 CHIPCircularTLVBuffer::CHIPCircularTLVBuffer(uint8_t * inBuffer, uint32_t inBufferLength, uint8_t * inHead)
62 mQueueSize = inBufferLength;
66 mProcessEvictedElement = nullptr;
69 // use common as opposed to unspecified, s.t. the reader that
70 // skips over the elements does not complain about implicit
72 mImplicitProfileId = kCommonProfileId;
77 * CHIPCircularTLVBuffer constructor
79 * @param[in] inBuffer A pointer to the backing store for the queue
81 * @param[in] inBufferLength Length, in bytes, of the backing store
83 CHIPCircularTLVBuffer::CHIPCircularTLVBuffer(uint8_t * inBuffer, uint32_t inBufferLength)
86 mQueueSize = inBufferLength;
90 mProcessEvictedElement = nullptr;
93 // use common as opposed to unspecified, s.t. the reader that
94 // skips over the elements does not complain about implicit
96 mImplicitProfileId = kCommonProfileId;
101 * Evicts the oldest top-level TLV element in the CHIPCircularTLVBuffer
103 * This function removes the oldest top level TLV element in the
104 * buffer. The function will call the callback registered at
105 * #mProcessEvictedElement to process the element prior to removal.
106 * If the callback returns anything but #CHIP_NO_ERROR, the element
107 * is not removed. Similarly, if any other error occurs -- no
108 * elements within the buffer, etc -- the underlying
109 * #CHIPCircularTLVBuffer remains unchanged.
111 * @retval #CHIP_NO_ERROR On success.
113 * @retval other On any other error returned either by the callback
114 * or by the TLVReader.
117 CHIP_ERROR CHIPCircularTLVBuffer::EvictHead()
119 CircularTLVReader reader;
123 // find the boundaries of an event to throw away
125 reader.ImplicitProfileId = mImplicitProfileId;
127 // position the reader on the first element
128 ReturnErrorOnFailure(reader.Next());
130 // skip to the next element
131 ReturnErrorOnFailure(reader.Skip());
133 // record the state of the queue post-call
134 newLen = mQueueLength - (reader.GetLengthRead());
135 newHead = const_cast<uint8_t *>(reader.GetReadPoint());
137 // if a custom handler is installed, give it a chance to
138 // process the element before we evict it from the buffer.
139 if (mProcessEvictedElement != nullptr)
141 // Reinitialize the reader
143 reader.ImplicitProfileId = mImplicitProfileId;
145 ReturnErrorOnFailure(mProcessEvictedElement(*this, mAppData, reader));
148 // update queue state
149 mQueueLength = newLen;
150 mQueueHead = newHead;
152 return CHIP_NO_ERROR;
157 * Implements TLVBackingStore::OnInit(TLVWriter) for circular buffers.
159 CHIP_ERROR CHIPCircularTLVBuffer::OnInit(TLVWriter & writer, uint8_t *& bufStart, uint32_t & bufLen)
161 return GetNewBuffer(writer, bufStart, bufLen);
166 * Get additional space for the TLVWriter. In actuality, the
167 * function evicts an element from the circular buffer, and adjusts
168 * the head of this buffer queue
170 * @param[in,out] ioWriter TLVWriter calling this function
172 * @param[out] outBufStart The pointer to the new buffer
174 * @param[out] outBufLen The available length for writing
176 * @retval #CHIP_NO_ERROR On success.
178 * @retval other If the function was unable to elide a complete
179 * top-level TLV element.
182 CHIP_ERROR CHIPCircularTLVBuffer::GetNewBuffer(TLVWriter & ioWriter, uint8_t *& outBufStart, uint32_t & outBufLen)
184 uint8_t * tail = QueueTail();
186 if (mQueueLength >= mQueueSize)
188 // Queue is out of space, need to evict an element
189 ReturnErrorOnFailure(EvictHead());
192 // set the output values, returned buffer must be contiguous
195 if (tail >= mQueueHead)
197 outBufLen = mQueueSize - static_cast<uint32_t>(tail - mQueue);
201 outBufLen = static_cast<uint32_t>(mQueueHead - tail);
204 return CHIP_NO_ERROR;
209 * FinalizeBuffer adjust the `CHIPCircularTLVBuffer` state on
210 * completion of output from the TLVWriter. This function affects
211 * the position of the queue tail.
213 * @param[in,out] ioWriter TLVWriter calling this function
215 * @param[in] inBufStart pointer to the start of data (from `TLVWriter`
218 * @param[in] inBufLen length of data in the buffer pointed to by
221 * @retval #CHIP_NO_ERROR Unconditionally.
224 CHIP_ERROR CHIPCircularTLVBuffer::FinalizeBuffer(TLVWriter & ioWriter, uint8_t * inBufStart, uint32_t inBufLen)
226 CHIP_ERROR err = CHIP_NO_ERROR;
227 uint8_t * tail = inBufStart + inBufLen;
230 if (tail <= mQueueHead)
232 mQueueLength = mQueueSize - static_cast<uint32_t>(mQueueHead - tail);
236 mQueueLength = static_cast<uint32_t>(tail - mQueueHead);
244 * Implements TLVBackingStore::OnInit(TVLReader) for circular buffers.
246 CHIP_ERROR CHIPCircularTLVBuffer::OnInit(TLVReader & reader, const uint8_t *& bufStart, uint32_t & bufLen)
248 return GetNextBuffer(reader, bufStart, bufLen);
253 * Get additional space for the TLVReader.
255 * The storage provided by the CHIPCircularTLVBuffer may be
256 * wraparound within the buffer. This function provides us with an
257 * ability to match the buffering of the circular buffer to the
258 * TLVReader constraints. The reader will read at most `mQueueSize`
259 * bytes from the buffer.
261 * @param[in] ioReader TLVReader calling this function.
263 * @param[in,out] outBufStart The reference to the data buffer. On
264 * return, it is set to a value within this
267 * @param[out] outBufLen On return, set to the number of continuous
268 * bytes that could be read out of the buffer.
270 * @retval #CHIP_NO_ERROR Succeeds unconditionally.
272 CHIP_ERROR CHIPCircularTLVBuffer::GetNextBuffer(TLVReader & ioReader, const uint8_t *& outBufStart, uint32_t & outBufLen)
274 CHIP_ERROR err = CHIP_NO_ERROR;
275 uint8_t * tail = QueueTail();
276 const uint8_t * readerStart = outBufStart;
278 if (readerStart == nullptr)
280 outBufStart = mQueueHead;
282 if (outBufStart == mQueue + mQueueSize)
284 outBufStart = mQueue;
287 else if (readerStart >= (mQueue + mQueueSize))
289 outBufStart = mQueue;
297 if ((mQueueLength != 0) && (tail <= outBufStart))
299 // the buffer is non-empty and data wraps around the end
300 // point. The returned buffer conceptually spans from
301 // outBufStart until the end of the underlying storage buffer
302 // (i.e. mQueue+mQueueSize). This case tail == outBufStart
303 // indicates that the buffer is completely full
304 outBufLen = mQueueSize - static_cast<uint32_t>(outBufStart - mQueue);
305 if ((tail == outBufStart) && (readerStart != nullptr))
310 // the buffer length is the distance between head and tail;
311 // tail is either strictly larger or the buffer is empty
312 outBufLen = static_cast<uint32_t>(tail - outBufStart);