Line data Source code
1 : /*
2 : *
3 : * Copyright (c) 2020-2021 Project CHIP Authors
4 : * Copyright (c) 2016-2017 Nest Labs, Inc.
5 : * All rights reserved.
6 : *
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
10 : *
11 : * http://www.apache.org/licenses/LICENSE-2.0
12 : *
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.
18 : */
19 :
20 : /**
21 : * @file
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.
28 : */
29 : #include <lib/core/TLVCircularBuffer.h>
30 :
31 : #ifndef __STDC_LIMIT_MACROS
32 : #define __STDC_LIMIT_MACROS
33 : #endif
34 :
35 : #include <lib/core/CHIPError.h>
36 : #include <lib/core/TLVReader.h>
37 : #include <lib/core/TLVTags.h>
38 : #include <lib/core/TLVWriter.h>
39 : #include <lib/support/BufferWriter.h>
40 : #include <lib/support/CodeUtils.h>
41 :
42 : #include <stdint.h>
43 :
44 : namespace chip {
45 : namespace TLV {
46 :
47 : using namespace chip::Encoding;
48 :
49 : /**
50 : * @brief
51 : * TLVCircularBuffer constructor
52 : *
53 : * @param[in] inBuffer A pointer to the backing store for the queue
54 : *
55 : * @param[in] inBufferLength Length, in bytes, of the backing store
56 : *
57 : * @param[in] inHead Initial point for the head. The @a inHead pointer is must fall within the backing store for the
58 : * circular buffer, i.e. within @a inBuffer and &(@a inBuffer[@a inBufferLength])
59 : */
60 1 : TLVCircularBuffer::TLVCircularBuffer(uint8_t * inBuffer, uint32_t inBufferLength, uint8_t * inHead)
61 : {
62 1 : mQueue = inBuffer;
63 1 : mQueueSize = inBufferLength;
64 1 : mQueueLength = 0;
65 1 : mQueueHead = inHead;
66 :
67 1 : mProcessEvictedElement = nullptr;
68 1 : mAppData = nullptr;
69 :
70 : // use common as opposed to unspecified, s.t. the reader that
71 : // skips over the elements does not complain about implicit
72 : // profile tags.
73 1 : mImplicitProfileId = kCommonProfileId;
74 1 : }
75 :
76 : /**
77 : * @brief
78 : * TLVCircularBuffer constructor
79 : *
80 : * @param[in] inBuffer A pointer to the backing store for the queue
81 : *
82 : * @param[in] inBufferLength Length, in bytes, of the backing store
83 : */
84 1072 : TLVCircularBuffer::TLVCircularBuffer(uint8_t * inBuffer, uint32_t inBufferLength)
85 : {
86 1072 : Init(inBuffer, inBufferLength);
87 1072 : }
88 :
89 : /**
90 : * @brief
91 : * TLVCircularBuffer Init function
92 : *
93 : * @param[in] inBuffer A pointer to the backing store for the queue
94 : *
95 : * @param[in] inBufferLength Length, in bytes, of the backing store
96 : */
97 1441 : void TLVCircularBuffer::Init(uint8_t * inBuffer, uint32_t inBufferLength)
98 : {
99 1441 : mQueue = inBuffer;
100 1441 : mQueueSize = inBufferLength;
101 1441 : mQueueLength = 0;
102 1441 : mQueueHead = mQueue;
103 :
104 1441 : mProcessEvictedElement = nullptr;
105 1441 : mAppData = nullptr;
106 :
107 : // use common as opposed to unspecified, s.t. the reader that
108 : // skips over the elements does not complain about implicit
109 : // profile tags.
110 1441 : mImplicitProfileId = kCommonProfileId;
111 1441 : }
112 :
113 : /**
114 : * @brief
115 : * Evicts the oldest top-level TLV element in the TLVCircularBuffer
116 : *
117 : * This function removes the oldest top level TLV element in the
118 : * buffer. The function will call the callback registered at
119 : * #mProcessEvictedElement to process the element prior to removal.
120 : * If the callback returns anything but #CHIP_NO_ERROR, the element
121 : * is not removed. Similarly, if any other error occurs -- no
122 : * elements within the buffer, etc -- the underlying
123 : * #TLVCircularBuffer remains unchanged.
124 : *
125 : * @retval #CHIP_NO_ERROR On success.
126 : *
127 : * @retval other On any other error returned either by the callback
128 : * or by the TLVReader.
129 : *
130 : */
131 1545 : CHIP_ERROR TLVCircularBuffer::EvictHead()
132 : {
133 : CircularTLVReader reader;
134 : uint8_t * newHead;
135 : uint32_t newLen;
136 :
137 : // find the boundaries of an event to throw away
138 1545 : reader.Init(*this);
139 1545 : reader.ImplicitProfileId = mImplicitProfileId;
140 :
141 : // position the reader on the first element
142 1545 : ReturnErrorOnFailure(reader.Next());
143 :
144 : // skip to the next element
145 1545 : ReturnErrorOnFailure(reader.Skip());
146 :
147 : // record the state of the queue post-call
148 1544 : newLen = mQueueLength - (reader.GetLengthRead());
149 1544 : newHead = const_cast<uint8_t *>(reader.GetReadPoint());
150 :
151 : // if a custom handler is installed, give it a chance to
152 : // process the element before we evict it from the buffer.
153 1544 : if (mProcessEvictedElement != nullptr)
154 : {
155 : // Reinitialize the reader
156 1116 : reader.Init(*this);
157 1116 : reader.ImplicitProfileId = mImplicitProfileId;
158 :
159 1116 : ReturnErrorOnFailure(mProcessEvictedElement(*this, mAppData, reader));
160 : }
161 :
162 : // update queue state
163 809 : mQueueLength = newLen;
164 809 : mQueueHead = newHead;
165 :
166 809 : return CHIP_NO_ERROR;
167 : }
168 :
169 : /**
170 : * @brief
171 : * Implements TLVBackingStore::OnInit(TLVWriter) for circular buffers.
172 : */
173 10 : CHIP_ERROR TLVCircularBuffer::OnInit(TLVWriter & writer, uint8_t *& bufStart, uint32_t & bufLen)
174 : {
175 10 : return GetNewBuffer(writer, bufStart, bufLen);
176 : }
177 :
178 : /**
179 : * @brief
180 : * Get additional space for the TLVWriter. In actuality, the
181 : * function evicts an element from the circular buffer, and adjusts
182 : * the head of this buffer queue
183 : *
184 : * @param[in,out] ioWriter TLVWriter calling this function
185 : *
186 : * @param[out] outBufStart The pointer to the new buffer
187 : *
188 : * @param[out] outBufLen The available length for writing
189 : *
190 : * @retval #CHIP_NO_ERROR On success.
191 : *
192 : * @retval other If the function was unable to elide a complete
193 : * top-level TLV element.
194 : */
195 :
196 508 : CHIP_ERROR TLVCircularBuffer::GetNewBuffer(TLVWriter & ioWriter, uint8_t *& outBufStart, uint32_t & outBufLen)
197 : {
198 508 : if (mQueueLength >= mQueueSize)
199 : {
200 : // Queue is out of space, need to evict an element
201 18 : ReturnErrorOnFailure(EvictHead());
202 : }
203 :
204 507 : GetCurrentWritableBuffer(outBufStart, outBufLen);
205 507 : return CHIP_NO_ERROR;
206 : }
207 :
208 1591 : void TLVCircularBuffer::GetCurrentWritableBuffer(uint8_t *& outBufStart, uint32_t & outBufLen) const
209 : {
210 1591 : uint8_t * tail = QueueTail();
211 :
212 : // set the output values, returned buffer must be contiguous
213 1591 : outBufStart = tail;
214 :
215 1591 : if (tail >= mQueueHead)
216 : {
217 351 : outBufLen = mQueueSize - static_cast<uint32_t>(tail - mQueue);
218 : }
219 : else
220 : {
221 1240 : outBufLen = static_cast<uint32_t>(mQueueHead - tail);
222 : }
223 1591 : }
224 :
225 : /**
226 : * @brief
227 : * FinalizeBuffer adjust the `TLVCircularBuffer` state on
228 : * completion of output from the TLVWriter. This function affects
229 : * the position of the queue tail.
230 : *
231 : * @param[in,out] ioWriter TLVWriter calling this function
232 : *
233 : * @param[in] inBufStart pointer to the start of data (from `TLVWriter`
234 : * perspective)
235 : *
236 : * @param[in] inBufLen length of data in the buffer pointed to by
237 : * `inbufStart`
238 : *
239 : * @retval #CHIP_NO_ERROR Unconditionally.
240 : */
241 :
242 1605 : CHIP_ERROR TLVCircularBuffer::FinalizeBuffer(TLVWriter & ioWriter, uint8_t * inBufStart, uint32_t inBufLen)
243 : {
244 1605 : CHIP_ERROR err = CHIP_NO_ERROR;
245 1605 : uint8_t * tail = inBufStart + inBufLen;
246 1605 : if (inBufLen)
247 : {
248 1605 : if (tail <= mQueueHead)
249 : {
250 1242 : mQueueLength = mQueueSize - static_cast<uint32_t>(mQueueHead - tail);
251 : }
252 : else
253 : {
254 363 : mQueueLength = static_cast<uint32_t>(tail - mQueueHead);
255 : }
256 : }
257 1605 : return err;
258 : }
259 :
260 : /**
261 : * @brief
262 : * Implements TLVBackingStore::OnInit(TVLReader) for circular buffers.
263 : */
264 4041 : CHIP_ERROR TLVCircularBuffer::OnInit(TLVReader & reader, const uint8_t *& bufStart, uint32_t & bufLen)
265 : {
266 4041 : return GetNextBuffer(reader, bufStart, bufLen);
267 : }
268 :
269 : /**
270 : * @brief
271 : * Get additional space for the TLVReader.
272 : *
273 : * The storage provided by the TLVCircularBuffer may be
274 : * wraparound within the buffer. This function provides us with an
275 : * ability to match the buffering of the circular buffer to the
276 : * TLVReader constraints. The reader will read at most `mQueueSize`
277 : * bytes from the buffer.
278 : *
279 : * @param[in] ioReader TLVReader calling this function.
280 : *
281 : * @param[in,out] outBufStart The reference to the data buffer. On
282 : * return, it is set to a value within this
283 : * buffer.
284 : *
285 : * @param[out] outBufLen On return, set to the number of continuous
286 : * bytes that could be read out of the buffer.
287 : *
288 : * @retval #CHIP_NO_ERROR Succeeds unconditionally.
289 : */
290 6141 : CHIP_ERROR TLVCircularBuffer::GetNextBuffer(TLVReader & ioReader, const uint8_t *& outBufStart, uint32_t & outBufLen)
291 : {
292 6141 : CHIP_ERROR err = CHIP_NO_ERROR;
293 6141 : uint8_t * tail = QueueTail();
294 6141 : const uint8_t * readerStart = outBufStart;
295 :
296 6141 : if (readerStart == nullptr)
297 : {
298 5855 : outBufStart = mQueueHead;
299 :
300 5855 : if (outBufStart == mQueue + mQueueSize)
301 : {
302 10 : outBufStart = mQueue;
303 : }
304 : }
305 286 : else if (readerStart >= (mQueue + mQueueSize))
306 : {
307 238 : outBufStart = mQueue;
308 : }
309 : else
310 : {
311 48 : outBufLen = 0;
312 48 : return err;
313 : }
314 :
315 6093 : if ((mQueueLength != 0) && (tail <= outBufStart))
316 : {
317 : // the buffer is non-empty and data wraps around the end
318 : // point. The returned buffer conceptually spans from
319 : // outBufStart until the end of the underlying storage buffer
320 : // (i.e. mQueue+mQueueSize). This case tail == outBufStart
321 : // indicates that the buffer is completely full
322 3077 : outBufLen = mQueueSize - static_cast<uint32_t>(outBufStart - mQueue);
323 3077 : if ((tail == outBufStart) && (readerStart != nullptr))
324 0 : outBufLen = 0;
325 : }
326 : else
327 : {
328 : // the buffer length is the distance between head and tail;
329 : // tail is either strictly larger or the buffer is empty
330 3016 : outBufLen = static_cast<uint32_t>(tail - outBufStart);
331 : }
332 6093 : return err;
333 : }
334 :
335 : } // namespace TLV
336 : } // namespace chip
|