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