• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022-2023 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #ifndef DFX_RING_BUFFER_H
17 #define DFX_RING_BUFFER_H
18 
19 #include "dfx_ring_buffer_block.h"
20 
21 /**
22  * @brief            A ring buffer is a FIFO structure that can be used to
23  *                     spool data between devices.
24  *
25  *                     There is a Skip() function that allows the client to
26  *                     control when the read cursor is changed. This is so the
27  *                     client can perform an action after Read() without the
28  *                     write cursor overwriting data while the read block is used.
29  *
30  *                     For e.g with the sequence of events:
31  *                         1.    Read(1000, false)
32  *                         2.    Busy writing to sd card for 5 seconds
33  *                         3.    Skip()
34  *
35  *                     Because the skip isn't called until the writing
36  *                     has finished, another thread can .Append() without
37  *                     corrupting the data being written.
38  *
39  *
40  * @attention        The ring buffer can only contain Length-1 number of entries,
41  *                   because the last index is reserved for overrun checks.
42  *
43  * @tparam Length    The length of the backing store array.
44  * @tparam T         The type of data stored.
45  */
46 template<unsigned int LENGTH, class T>
47 class DfxRingBuffer {
48 public:
DfxRingBuffer()49     DfxRingBuffer() : readPosition(0), writePosition(0), data{{T()}}, overrunFlag(false)
50     {
51     }
52 
~DfxRingBuffer()53     ~DfxRingBuffer()
54     {
55     }
56 
57     /**
58      * @brief     Appends a value the end of the
59      *            buffer.
60      */
Append(T & value)61     void Append(T& value)
62     {
63         /*
64          * If the next position is where the read cursor
65          * is then we have a full buffer.
66          */
67         bool bufferFull;
68 
69         bufferFull = ((writePosition + 1U) % LENGTH) == readPosition;
70 
71         if (bufferFull) {
72             /*
73              * Tried to append a value while the buffer is full.
74              */
75             overrunFlag = true;
76         } else {
77             /*
78              * Buffer isn't full yet, write to the curr write position
79              * and increment it by 1.
80              */
81             overrunFlag         = false;
82             data[writePosition] = value;
83             writePosition       = (writePosition + 1U) % LENGTH;
84         }
85     }
86 
87     /**
88      * @brief                        Retrieve a continuous block of
89      *                               valid buffered data.
90      * @param numReadsRequested    How many reads are required.
91      * @return                       A block of items containing the maximum
92      *                               number the buffer can provide at this time.
93      */
Read(unsigned int numReadsRequested)94     DfxRingBufferBlock<T> Read(unsigned int numReadsRequested)
95     {
96         bool bridgesZero;
97         DfxRingBufferBlock<T> block;
98 
99         /*
100           * Make sure the number of reads does not bridge the 0 index.
101           * This is because we can only provide 1 contiguous block at
102           * a time.
103           */
104         bridgesZero = (readPosition > writePosition);
105 
106         if (bridgesZero) {
107             unsigned int readsToEnd;
108             bool reqSurpassesBufferEnd;
109 
110             readsToEnd = LENGTH - readPosition;
111             reqSurpassesBufferEnd = numReadsRequested > readsToEnd;
112 
113             if (reqSurpassesBufferEnd) {
114                 /*
115                  * If the block requested exceeds the buffer end. Then
116                  * return a block that reaches the end and no more.
117                  */
118                 block.SetStart(&(data[readPosition]));
119                 block.SetLength(readsToEnd);
120             } else {
121                 /*
122                  * If the block requested does not exceed 0
123                  * then return a block that reaches the number of reads required.
124                  */
125                 block.SetStart(&(data[readPosition]));
126                 block.SetLength(numReadsRequested);
127             }
128         } else {
129             /*
130              * If the block doesn't bridge the zero then
131              * return the maximum number of reads to the write
132              * cursor.
133              */
134             unsigned int maxNumReads;
135             unsigned int numReadsToWritePosition;
136 
137             numReadsToWritePosition = (writePosition - readPosition);
138 
139             if (numReadsRequested > numReadsToWritePosition) {
140                 /*
141                  * If the block length requested exceeds the
142                  * number of items available, then restrict
143                  * the block length to the distance to the write position.
144                  */
145                 maxNumReads = numReadsToWritePosition;
146             } else {
147                 /*
148                  * If the block length requested does not exceed the
149                  * number of items available then the entire
150                  * block is valid.
151                  */
152                 maxNumReads = numReadsRequested;
153             }
154 
155             block.SetStart(&(data[readPosition]));
156             block.SetLength(maxNumReads);
157         }
158 
159         return block;
160     }
161 
162     /**
163      * @brief    Advances the read position.
164      *
165      */
Skip(unsigned int numReads)166     void Skip(unsigned int numReads)
167     {
168         readPosition = (readPosition + numReads) % LENGTH;
169     }
170 
Overrun()171     bool Overrun() const
172     {
173         return overrunFlag;
174     }
175 
176     /**
177      * @brief    The total size of the ring buffer including the full position.
178      *
179      */
Length()180     unsigned int Length() const
181     {
182         return LENGTH;
183     }
184 
185     /**
186      * @brief    Returns the number of reads available.
187      *
188      */
Available()189     unsigned int Available()
190     {
191         bool bridgesZero;
192         unsigned availableReads;
193 
194         bridgesZero = readPosition > writePosition;
195         availableReads = 0;
196 
197         if (bridgesZero) {
198             /* Add the number of reads to zero, and number of reads from 0 to the write cursor */
199             unsigned int numReadsToZero;
200             unsigned int numReadsToWritePosition;
201 
202             numReadsToZero = LENGTH - readPosition;
203             numReadsToWritePosition = writePosition;
204 
205             availableReads = numReadsToZero + numReadsToWritePosition;
206         } else {
207             /* The number of available reads is between the write position and the read position. */
208             availableReads = writePosition - readPosition;
209         }
210 
211         return availableReads;
212     }
213 
214 private:
215     volatile unsigned int readPosition;
216     volatile unsigned int writePosition;
217 
218     T data[LENGTH] = {T()};
219 
220     bool overrunFlag = false;
221 };
222 
223 #endif
224