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