Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / lib / core / CHIPCircularTLVBuffer.cpp
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
30 #ifndef __STDC_LIMIT_MACROS
31 #define __STDC_LIMIT_MACROS
32 #endif
33 #include <core/CHIPCircularTLVBuffer.h>
34
35 #include <core/CHIPCore.h>
36 #include <core/CHIPEncoding.h>
37 #include <core/CHIPTLV.h>
38
39 #include <support/CodeUtils.h>
40
41 #include <stdint.h>
42
43 namespace chip {
44 namespace TLV {
45
46 using namespace chip::Encoding;
47
48 /**
49  * @brief
50  *   CHIPCircularTLVBuffer constructor
51  *
52  * @param[in] inBuffer       A pointer to the backing store for the queue
53  *
54  * @param[in] inBufferLength Length, in bytes, of the backing store
55  *
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])
58  */
59 CHIPCircularTLVBuffer::CHIPCircularTLVBuffer(uint8_t * inBuffer, uint32_t inBufferLength, uint8_t * inHead)
60 {
61     mQueue       = inBuffer;
62     mQueueSize   = inBufferLength;
63     mQueueLength = 0;
64     mQueueHead   = inHead;
65
66     mProcessEvictedElement = nullptr;
67     mAppData               = nullptr;
68
69     // use common as opposed to unspecified, s.t. the reader that
70     // skips over the elements does not complain about implicit
71     // profile tags.
72     mImplicitProfileId = kCommonProfileId;
73 }
74
75 /**
76  * @brief
77  *   CHIPCircularTLVBuffer constructor
78  *
79  * @param[in] inBuffer       A pointer to the backing store for the queue
80  *
81  * @param[in] inBufferLength Length, in bytes, of the backing store
82  */
83 CHIPCircularTLVBuffer::CHIPCircularTLVBuffer(uint8_t * inBuffer, uint32_t inBufferLength)
84 {
85     mQueue       = inBuffer;
86     mQueueSize   = inBufferLength;
87     mQueueLength = 0;
88     mQueueHead   = mQueue;
89
90     mProcessEvictedElement = nullptr;
91     mAppData               = nullptr;
92
93     // use common as opposed to unspecified, s.t. the reader that
94     // skips over the elements does not complain about implicit
95     // profile tags.
96     mImplicitProfileId = kCommonProfileId;
97 }
98
99 /**
100  * @brief
101  *   Evicts the oldest top-level TLV element in the CHIPCircularTLVBuffer
102  *
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.
110  *
111  *  @retval #CHIP_NO_ERROR On success.
112  *
113  *  @retval other          On any other error returned either by the callback
114  *                         or by the TLVReader.
115  *
116  */
117 CHIP_ERROR CHIPCircularTLVBuffer::EvictHead()
118 {
119     CircularTLVReader reader;
120     uint8_t * newHead;
121     uint32_t newLen;
122
123     // find the boundaries of an event to throw away
124     reader.Init(*this);
125     reader.ImplicitProfileId = mImplicitProfileId;
126
127     // position the reader on the first element
128     ReturnErrorOnFailure(reader.Next());
129
130     // skip to the next element
131     ReturnErrorOnFailure(reader.Skip());
132
133     // record the state of the queue post-call
134     newLen  = mQueueLength - (reader.GetLengthRead());
135     newHead = const_cast<uint8_t *>(reader.GetReadPoint());
136
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)
140     {
141         // Reinitialize the reader
142         reader.Init(*this);
143         reader.ImplicitProfileId = mImplicitProfileId;
144
145         ReturnErrorOnFailure(mProcessEvictedElement(*this, mAppData, reader));
146     }
147
148     // update queue state
149     mQueueLength = newLen;
150     mQueueHead   = newHead;
151
152     return CHIP_NO_ERROR;
153 }
154
155 /**
156  * @brief
157  *  Implements TLVBackingStore::OnInit(TLVWriter) for circular buffers.
158  */
159 CHIP_ERROR CHIPCircularTLVBuffer::OnInit(TLVWriter & writer, uint8_t *& bufStart, uint32_t & bufLen)
160 {
161     return GetNewBuffer(writer, bufStart, bufLen);
162 }
163
164 /**
165  * @brief
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
169  *
170  * @param[in,out] ioWriter  TLVWriter calling this function
171  *
172  * @param[out] outBufStart The pointer to the new buffer
173  *
174  * @param[out] outBufLen   The available length for writing
175  *
176  * @retval #CHIP_NO_ERROR On success.
177  *
178  * @retval other           If the function was unable to elide a complete
179  *                         top-level TLV element.
180  */
181
182 CHIP_ERROR CHIPCircularTLVBuffer::GetNewBuffer(TLVWriter & ioWriter, uint8_t *& outBufStart, uint32_t & outBufLen)
183 {
184     uint8_t * tail = QueueTail();
185
186     if (mQueueLength >= mQueueSize)
187     {
188         // Queue is out of space, need to evict an element
189         ReturnErrorOnFailure(EvictHead());
190     }
191
192     // set the output values, returned buffer must be contiguous
193     outBufStart = tail;
194
195     if (tail >= mQueueHead)
196     {
197         outBufLen = mQueueSize - static_cast<uint32_t>(tail - mQueue);
198     }
199     else
200     {
201         outBufLen = static_cast<uint32_t>(mQueueHead - tail);
202     }
203
204     return CHIP_NO_ERROR;
205 }
206
207 /**
208  * @brief
209  *   FinalizeBuffer adjust the `CHIPCircularTLVBuffer` state on
210  *   completion of output from the TLVWriter.  This function affects
211  *   the position of the queue tail.
212  *
213  * @param[in,out] ioWriter TLVWriter calling this function
214  *
215  * @param[in] inBufStart pointer to the start of data (from `TLVWriter`
216  *                       perspective)
217  *
218  * @param[in] inBufLen   length of data in the buffer pointed to by
219  *                       `inbufStart`
220  *
221  * @retval #CHIP_NO_ERROR Unconditionally.
222  */
223
224 CHIP_ERROR CHIPCircularTLVBuffer::FinalizeBuffer(TLVWriter & ioWriter, uint8_t * inBufStart, uint32_t inBufLen)
225 {
226     CHIP_ERROR err = CHIP_NO_ERROR;
227     uint8_t * tail = inBufStart + inBufLen;
228     if (inBufLen)
229     {
230         if (tail <= mQueueHead)
231         {
232             mQueueLength = mQueueSize - static_cast<uint32_t>(mQueueHead - tail);
233         }
234         else
235         {
236             mQueueLength = static_cast<uint32_t>(tail - mQueueHead);
237         }
238     }
239     return err;
240 }
241
242 /**
243  * @brief
244  *  Implements TLVBackingStore::OnInit(TVLReader) for circular buffers.
245  */
246 CHIP_ERROR CHIPCircularTLVBuffer::OnInit(TLVReader & reader, const uint8_t *& bufStart, uint32_t & bufLen)
247 {
248     return GetNextBuffer(reader, bufStart, bufLen);
249 }
250
251 /**
252  * @brief
253  *   Get additional space for the TLVReader.
254  *
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.
260  *
261  * @param[in] ioReader         TLVReader calling this function.
262  *
263  * @param[in,out] outBufStart  The reference to the data buffer.  On
264  *                             return, it is set to a value within this
265  *                             buffer.
266  *
267  * @param[out] outBufLen       On return, set to the number of continuous
268  *                             bytes that could be read out of the buffer.
269  *
270  * @retval #CHIP_NO_ERROR      Succeeds unconditionally.
271  */
272 CHIP_ERROR CHIPCircularTLVBuffer::GetNextBuffer(TLVReader & ioReader, const uint8_t *& outBufStart, uint32_t & outBufLen)
273 {
274     CHIP_ERROR err              = CHIP_NO_ERROR;
275     uint8_t * tail              = QueueTail();
276     const uint8_t * readerStart = outBufStart;
277
278     if (readerStart == nullptr)
279     {
280         outBufStart = mQueueHead;
281
282         if (outBufStart == mQueue + mQueueSize)
283         {
284             outBufStart = mQueue;
285         }
286     }
287     else if (readerStart >= (mQueue + mQueueSize))
288     {
289         outBufStart = mQueue;
290     }
291     else
292     {
293         outBufLen = 0;
294         return err;
295     }
296
297     if ((mQueueLength != 0) && (tail <= outBufStart))
298     {
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))
306             outBufLen = 0;
307     }
308     else
309     {
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);
313     }
314     return err;
315 }
316
317 } // namespace TLV
318 } // namespace chip