1 /*
2  * Copyright (c) 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 #ifndef LOG_TAG
16 #define LOG_TAG "AudioRingCache"
17 #endif
18 
19 #include "audio_ring_cache.h"
20 #include "audio_service_log.h"
21 
22 #include "securec.h"
23 
24 namespace OHOS {
25 namespace AudioStandard {
26 namespace {
27 static const size_t MAX_CACHE_SIZE = 16 * 1024 * 1024; // 16M
28 static const size_t BASE_INDEX_FENCE = SIZE_MAX - 2 * MAX_CACHE_SIZE;
29 }
AudioRingCache(size_t cacheSize)30 AudioRingCache::AudioRingCache(size_t cacheSize) : cacheTotalSize_(cacheSize)
31 {
32     AUDIO_INFO_LOG("AudioRingCache() with cacheSize:%{public}zu", cacheSize);
33 }
34 
~AudioRingCache()35 AudioRingCache::~AudioRingCache()
36 {
37     AUDIO_DEBUG_LOG("~AudioRingCache()");
38 }
39 
40 // Init is private and called in Create, not need lock. Call Init with lock in ReConfig
Init()41 bool AudioRingCache::Init()
42 {
43     if (cacheTotalSize_ > MAX_CACHE_SIZE) {
44         AUDIO_ERR_LOG("Init failed: size too large:%{public}zu", cacheTotalSize_);
45         return false;
46     }
47     baseIndex_ = 0;
48     writeIndex_ = 0;
49     readIndex_ = 0;
50     basePtr_ = std::make_unique<uint8_t[]>(cacheTotalSize_);
51     if (basePtr_ == nullptr) {
52         AUDIO_ERR_LOG("Init failed, get memory failed size is:%{public}zu", cacheTotalSize_);
53         return false;
54     }
55     if (memset_s(basePtr_.get(), cacheTotalSize_, 0, cacheTotalSize_) != EOK) {
56         AUDIO_ERR_LOG("Init call memeset_s failed.");
57         return false;
58     }
59     return true;
60 }
61 
Create(size_t cacheSize)62 std::unique_ptr<AudioRingCache> AudioRingCache::Create(size_t cacheSize)
63 {
64     if (cacheSize > MAX_CACHE_SIZE) {
65         AUDIO_ERR_LOG("Create failed: size too large:%{public}zu", cacheSize);
66         return nullptr;
67     }
68     std::unique_ptr<AudioRingCache> ringCache = std::make_unique<AudioRingCache>(cacheSize);
69 
70     if (ringCache->Init() != true) {
71         AUDIO_ERR_LOG("Create failed: Init failed");
72         return nullptr;
73     }
74     return ringCache;
75 }
76 
ReConfig(size_t cacheSize,bool copyRemained)77 OptResult AudioRingCache::ReConfig(size_t cacheSize, bool copyRemained)
78 {
79     AUDIO_INFO_LOG("ReConfig with cacheSize:%{public}zu", cacheSize);
80     OptResult result;
81     result.ret = OPERATION_SUCCESS;
82     result.size = cacheSize;
83     if (cacheSize > MAX_CACHE_SIZE) {
84         result.ret = INDEX_OUT_OF_RANGE;
85         AUDIO_ERR_LOG("ReConfig failed: size too large:%{public}zu", cacheSize);
86         return result;
87     }
88     if (!copyRemained) {
89         cacheTotalSize_ = cacheSize;
90         std::lock_guard<std::mutex> lock(cacheMutex_); // need lock as we operation buffer in Init
91         if (Init() != true) {
92             result.ret = OPERATION_FAILED;
93             return result;
94         }
95         AUDIO_INFO_LOG("ReConfig success cacheSize:%{public}zu", cacheSize);
96         return result;
97     }
98     // if need copyRemained, we should check the cacheSize >= remained size.
99     result = GetReadableSize();
100     if (result.ret != OPERATION_SUCCESS || result.size > cacheSize) {
101         AUDIO_ERR_LOG("ReConfig in copyRemained failed ret:%{public}d size :%{public}zu", result.ret, cacheSize);
102         return result;
103     }
104     std::unique_ptr<uint8_t[]> temp = std::make_unique<uint8_t[]>(result.size);
105     result = Dequeue({temp.get(), result.size});
106     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result,
107         "ReConfig dequeue failed ret:%{public}d", result.ret);
108     std::unique_lock<std::mutex> uniqueLock(cacheMutex_);
109     cacheTotalSize_ = cacheSize;
110     if (Init() != true) {
111         result.ret = OPERATION_FAILED;
112         return result;
113     }
114     uniqueLock.unlock(); // unlock as Enqueue will lock
115     result = Enqueue({temp.get(), result.size});
116 
117     return result;
118 }
119 
ResetBuffer()120 void AudioRingCache::ResetBuffer()
121 {
122     std::lock_guard<std::mutex> lock(cacheMutex_);
123     baseIndex_ = 0;
124     writeIndex_ = 0;
125     readIndex_ = 0;
126     if (memset_s(basePtr_.get(), cacheTotalSize_, 0, cacheTotalSize_) != EOK) {
127         AUDIO_ERR_LOG("ResetBuffer call memeset_s failed.");
128     }
129 }
130 
GetCahceSize()131 size_t AudioRingCache::GetCahceSize()
132 {
133     std::lock_guard<std::mutex> lock(cacheMutex_);
134     return cacheTotalSize_;
135 }
136 
GetWritableSize()137 OptResult AudioRingCache::GetWritableSize()
138 {
139     std::lock_guard<std::mutex> lock(cacheMutex_);
140     return GetWritableSizeNoLock();
141 }
142 
143 // call this with cacheMutex_
GetWritableSizeNoLock()144 OptResult AudioRingCache::GetWritableSizeNoLock()
145 {
146     OptResult result;
147     if (writeIndex_ < readIndex_ || writeIndex_ - readIndex_ > cacheTotalSize_) {
148         result.ret = INVALID_STATUS;
149         result.size = 0;
150         AUDIO_ERR_LOG("GetWritableSize failed: writeIndex_[%{public}zu] readIndex_[%{public}zu]",
151             writeIndex_, readIndex_);
152         return result;
153     }
154     result.size = cacheTotalSize_ - (writeIndex_ - readIndex_);
155     result.ret = OPERATION_SUCCESS;
156     return result;
157 }
158 
GetReadableSize()159 OptResult AudioRingCache::GetReadableSize()
160 {
161     std::lock_guard<std::mutex> lock(cacheMutex_);
162     return GetReadableSizeNoLock();
163 }
164 
165 // call this with cacheMutex_
GetReadableSizeNoLock()166 OptResult AudioRingCache::GetReadableSizeNoLock()
167 {
168     OptResult result;
169     if (writeIndex_ < readIndex_ || writeIndex_ - readIndex_ > cacheTotalSize_) {
170         result.ret = INVALID_STATUS;
171         result.size = 0;
172         AUDIO_ERR_LOG("GetReadableSize failed: writeIndex_[%{public}zu] readIndex_[%{public}zu]",
173             writeIndex_, readIndex_);
174         return result;
175     }
176     result.size = writeIndex_ - readIndex_;
177     result.ret = OPERATION_SUCCESS;
178     return result;
179 }
180 
Enqueue(const BufferWrap & buffer)181 OptResult AudioRingCache::Enqueue(const BufferWrap &buffer)
182 {
183     std::lock_guard<std::mutex> lock(cacheMutex_);
184     OptResult result;
185     // params check
186     if (buffer.dataPtr == nullptr || buffer.dataSize > MAX_CACHE_SIZE || buffer.dataSize == 0) {
187         result.ret = INVALID_PARAMS;
188         AUDIO_ERR_LOG("Enqueue failed: BufferWrap is null or size %{public}zu is too large", buffer.dataSize);
189         return result;
190     }
191 
192     // Get writable size here,do not directly call GetWriteableSize() as it will cause a deadlock.
193     result = GetWritableSizeNoLock();
194     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result, "Enqueue failed to get writeable size.");
195     size_t writableSize = result.size;
196 
197     if (buffer.dataSize > writableSize) {
198         result = {INDEX_OUT_OF_RANGE, writableSize};
199         AUDIO_WARNING_LOG("Enqueue find buffer not enough, writableSize:%{public}zu , enqueue size:%{public}zu",
200             writableSize, buffer.dataSize);
201         return result;
202     }
203     // buffer.dataSize <= writableSize, let's do memory copy.
204     // judge if cross buffer
205     size_t tempWriteIndex = writeIndex_ + buffer.dataSize;
206     if (writeIndex_ < baseIndex_ + cacheTotalSize_ && tempWriteIndex > baseIndex_ + cacheTotalSize_) {
207         size_t headSize = baseIndex_ + cacheTotalSize_ - writeIndex_;
208         size_t tailSize = tempWriteIndex - (baseIndex_ + cacheTotalSize_);
209         void *headPtr = static_cast<void *>(basePtr_.get() + (writeIndex_ - baseIndex_));
210         void *tailPtr = static_cast<void *>(basePtr_.get());
211         if ((memcpy_s(headPtr, headSize, static_cast<void *>(buffer.dataPtr), headSize)) == EOK &&
212             memcpy_s(tailPtr, tailSize, static_cast<void *>(buffer.dataPtr + headSize), tailSize) == EOK) {
213             writeIndex_ = tempWriteIndex; // move write index
214             result = {OPERATION_SUCCESS, buffer.dataSize};
215             return result;
216         }
217         result = {OPERATION_FAILED, writableSize};
218         AUDIO_ERR_LOG("Enqueue memcpy_s failed: writeIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize"
219             "[%{public}zu]", writeIndex_, baseIndex_, buffer.dataSize);
220         return result;
221     }
222     // not cross
223     size_t offset = writeIndex_ >= baseIndex_ + cacheTotalSize_ ? (writeIndex_ - baseIndex_ - cacheTotalSize_) :
224         (writeIndex_ - baseIndex_);
225     void *writePtr = static_cast<void *>(basePtr_.get() + offset);
226     if ((memcpy_s(writePtr, cacheTotalSize_ - offset, static_cast<void *>(buffer.dataPtr), buffer.dataSize)) == EOK) {
227         writeIndex_ = tempWriteIndex; // move write index
228         result = {OPERATION_SUCCESS, buffer.dataSize};
229         return result;
230     }
231     AUDIO_ERR_LOG("Enqueue memcpy_s failed: writeIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize"
232         "[%{public}zu]", writeIndex_, baseIndex_, buffer.dataSize);
233     result = {OPERATION_FAILED, writableSize};
234     return result;
235 }
236 
ReIndex()237 void AudioRingCache::ReIndex()
238 {
239     AUDIO_INFO_LOG("ReIndex baseIndex[%{public}zu] readIndex[%{public}zu] writeIndex[%{public}zu]", baseIndex_,
240         readIndex_, writeIndex_);
241     writeIndex_ -= baseIndex_;
242     readIndex_ -= baseIndex_;
243     baseIndex_ = 0;
244 }
HandleCrossDequeue(size_t tempReadIndex,size_t readableSize,const BufferWrap & buffer)245 OptResult AudioRingCache::HandleCrossDequeue(size_t tempReadIndex, size_t readableSize, const BufferWrap &buffer)
246 {
247     OptResult result;
248     // cross
249     size_t headSize = baseIndex_ + cacheTotalSize_ - readIndex_;
250     size_t tailSize = tempReadIndex - (baseIndex_ + cacheTotalSize_);
251     void *headPtr = static_cast<void *>(basePtr_.get() + (readIndex_ - baseIndex_));
252     void *tailPtr = static_cast<void *>(basePtr_.get());
253     if (memcpy_s(static_cast<void *>(buffer.dataPtr), headSize, headPtr, headSize) != EOK ||
254         memcpy_s(static_cast<void *>(buffer.dataPtr + headSize), tailSize, tailPtr, tailSize) != EOK) {
255         result = {OPERATION_FAILED, readableSize};
256         AUDIO_ERR_LOG("Dequeue memcpy_s failed: readIndex[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize"
257             "[%{public}zu]", readIndex_, baseIndex_, buffer.dataSize);
258         return result;
259     }
260     if (memset_s(headPtr, headSize, 0, headSize) != EOK) {
261         AUDIO_ERR_LOG("reset headPtr fail.");
262     }
263     if (memset_s(tailPtr, tailSize, 0, tailSize) != EOK) {
264         AUDIO_ERR_LOG("reset headPtr fail.");
265     }
266 
267     readIndex_ = tempReadIndex; // move write index
268     baseIndex_ += cacheTotalSize_; // move base index
269     if (baseIndex_ >= BASE_INDEX_FENCE) {
270         ReIndex();
271     }
272     result = {OPERATION_SUCCESS, buffer.dataSize};
273     return result;
274 }
275 
Dequeue(const BufferWrap & buffer)276 OptResult AudioRingCache::Dequeue(const BufferWrap &buffer)
277 {
278     std::lock_guard<std::mutex> lock(cacheMutex_);
279     OptResult result;
280     // params check
281     if (buffer.dataPtr == nullptr || buffer.dataSize > MAX_CACHE_SIZE) {
282         result.ret = INVALID_PARAMS;
283         AUDIO_ERR_LOG("Dequeue failed: BufferWrap is null or size %{public}zu is too large", buffer.dataSize);
284         return result;
285     }
286 
287     result = GetReadableSizeNoLock();
288     CHECK_AND_RETURN_RET_LOG(result.ret == OPERATION_SUCCESS, result, "Dequeue failed to get readable size.");
289     size_t readableSize = result.size;
290     if (buffer.dataSize > readableSize) {
291         result = {INVALID_OPERATION, readableSize};
292         AUDIO_WARNING_LOG("Dequeue find buffer not enough, readableSize:%{public}zu , Dequeue size:%{public}zu",
293             readableSize, buffer.dataSize);
294         return result;
295     }
296 
297     // buffer.dataSize <= readableSize, let's do memory copy.
298     // judge if cross buffer
299     size_t tempReadIndex = readIndex_ + buffer.dataSize;
300     if (tempReadIndex > baseIndex_ + cacheTotalSize_) {
301         return HandleCrossDequeue(tempReadIndex, readableSize, buffer);
302     }
303 
304     // not cross
305     void *readPtr = static_cast<void *>(basePtr_.get() + readIndex_ - baseIndex_);
306     if ((memcpy_s(static_cast<void *>(buffer.dataPtr), buffer.dataSize, readPtr, buffer.dataSize)) != EOK) {
307         AUDIO_ERR_LOG("Dequeue memcpy_s failed: readIndex_[%{public}zu] baseIndex_[%{public}zu] buffer.dataSize"
308             "[%{public}zu]", readIndex_, baseIndex_, buffer.dataSize);
309         result = {OPERATION_FAILED, readableSize};
310         return result;
311     }
312     if (memset_s(readPtr, buffer.dataSize, 0, buffer.dataSize) != EOK) {
313         AUDIO_ERR_LOG("reset readPtr fail.");
314     }
315     if (tempReadIndex - baseIndex_ == cacheTotalSize_) {
316         baseIndex_ += cacheTotalSize_;
317     }
318     readIndex_ = tempReadIndex; // move read index
319     if (baseIndex_ >= BASE_INDEX_FENCE) {
320         ReIndex();
321     }
322     result = {OPERATION_SUCCESS, buffer.dataSize};
323     return result;
324 }
325 } // namespace AudioStandard
326 } // namespace OHOS
327