1 /*
2 * Copyright (C) 2024 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 #include <cerrno>
17 #include <cstring>
18 #include <fcntl.h>
19 #include <new>
20 #include <string>
21 #include <sys/mman.h>
22 #include <sys/stat.h>
23 #include <unistd.h>
24
25 #include "buffer_metadata_stream.h"
26 #include "image_log.h"
27 #include "image_utils.h"
28 #include "metadata_stream.h"
29 #include "securec.h"
30
31 #undef LOG_DOMAIN
32 #define LOG_DOMAIN LOG_TAG_DOMAIN_ID_IMAGE
33
34 #undef LOG_TAG
35 #define LOG_TAG "BufferMetadataStream"
36
37 namespace OHOS {
38 namespace Media {
BufferMetadataStream()39 BufferMetadataStream::BufferMetadataStream()
40 {
41 buffer_ = nullptr;
42 capacity_ = 0;
43 bufferSize_ = 0;
44 currentOffset_ = 0;
45 memoryMode_ = Dynamic;
46 originData_ = nullptr;
47 }
48
BufferMetadataStream(byte * originData,size_t size,MemoryMode mode,int originalFd,const std::string & originalPath)49 BufferMetadataStream::BufferMetadataStream(byte *originData, size_t size, MemoryMode mode,
50 int originalFd, const std::string &originalPath)
51 {
52 buffer_ = originData;
53 this->originData_ = originData;
54 capacity_ = static_cast<long>(size);
55 bufferSize_ = static_cast<long>(size);
56 currentOffset_ = 0;
57 memoryMode_ = mode;
58 originalFd_ = originalFd;
59 originalPath_ = originalPath;
60 }
61
~BufferMetadataStream()62 BufferMetadataStream::~BufferMetadataStream()
63 {
64 Close();
65 }
66
Write(uint8_t * data,ssize_t size)67 ssize_t BufferMetadataStream::Write(uint8_t *data, ssize_t size)
68 {
69 // Check if the new data will fit into the current buffer
70 if (currentOffset_ + static_cast<long>(size) > capacity_) {
71 CHECK_ERROR_RETURN_RET_LOG(memoryMode_ == Fix, -1,
72 "BufferMetadataStream::Write failed, data size exceeds buffer capacity, "
73 "currentOffset:%{public}ld, size:%{public}ld, capacity:%{public}ld",
74 currentOffset_, static_cast<long>(size), capacity_);
75
76 // Calculate the new capacity, ensuring it is a multiple of
77 // BUFFER_IMAGE_STREAM_PAGE_SIZE
78 long newCapacity = CalculateNewCapacity(currentOffset_, size);
79 CHECK_ERROR_RETURN_RET_LOG(newCapacity > METADATA_STREAM_MAX_CAPACITY, -1,
80 "BufferMetadataStream::Write failed, new capacity exceeds maximum capacity, "
81 "newCapacity:%{public}ld, maxCapacity:%{public}d",
82 newCapacity, METADATA_STREAM_MAX_CAPACITY);
83
84 // Allocate the new buffer
85 byte *newBuffer = new (std::nothrow) byte[newCapacity];
86
87 // Check if the allocation was successful
88 CHECK_ERROR_RETURN_RET_LOG(newBuffer == nullptr,
89 -1, "BufferMetadataStream::Write failed, unable to allocate new buffer");
90
91 // Removed std::fill_n for efficiency. If zero-initialization is needed,
92 // consider doing it manually where necessary.
93 // If there is existing data, copy it to the new buffer
94 if (buffer_ != nullptr) {
95 if (EOK != memcpy_s(newBuffer, newCapacity, buffer_, bufferSize_)) {
96 IMAGE_LOGE("BufferMetadataStream::Write failed, memcpy error");
97 delete[] newBuffer;
98 return -1;
99 }
100
101 // If the old buffer was not externally allocated, delete it
102 if (originData_ != buffer_) {
103 delete[] buffer_;
104 buffer_ = nullptr;
105 }
106 }
107
108 // Update the buffer and capacity
109 buffer_ = newBuffer;
110 capacity_ = newCapacity;
111
112 // Update expansion count
113 if (memoryMode_ == Dynamic) {
114 expandCount_++;
115 }
116 }
117
118 // Copy the new data into the buffer
119 if (data != nullptr) {
120 CHECK_ERROR_RETURN_RET_LOG(EOK != memcpy_s(buffer_ + currentOffset_, capacity_ - currentOffset_, data, size),
121 -1, "BufferMetadataStream::Write failed, memcpy error");
122 }
123
124 // Update the current offset and buffer size
125 currentOffset_ += size;
126 bufferSize_ = std::max(currentOffset_, bufferSize_);
127
128 return size;
129 }
130
Read(uint8_t * buf,ssize_t size)131 ssize_t BufferMetadataStream::Read(uint8_t *buf, ssize_t size)
132 {
133 if (buffer_ == nullptr || currentOffset_ == bufferSize_) {
134 return -1;
135 }
136
137 CHECK_ERROR_RETURN_RET_LOG(currentOffset_ > bufferSize_, -1,
138 "BufferMetadataStream::Read failed, current offset exceeds buffer size, "
139 "currentOffset:%{public}ld, bufferSize:%{public}ld", currentOffset_, bufferSize_);
140
141 long bytesToRead = std::min(static_cast<long>(size), bufferSize_ - currentOffset_);
142 CHECK_ERROR_RETURN_RET(IsFileSizeChanged(), -1);
143 memcpy_s(buf, size, buffer_ + currentOffset_, bytesToRead);
144 currentOffset_ += bytesToRead;
145 return bytesToRead;
146 }
147
ReadByte()148 int BufferMetadataStream::ReadByte()
149 {
150 CHECK_ERROR_RETURN_RET(buffer_ == nullptr, -1);
151
152 CHECK_DEBUG_RETURN_RET_LOG(currentOffset_ >= bufferSize_, -1,
153 "BufferMetadataStream::ReadByte failed, current offset exceeds buffer size, "
154 "currentOffset:%{public}ld, bufferSize:%{public}ld",
155 currentOffset_, bufferSize_);
156
157 if (currentOffset_ < bufferSize_) {
158 return buffer_[currentOffset_++];
159 }
160 return -1;
161 }
162
Seek(long offset,SeekPos pos)163 long BufferMetadataStream::Seek(long offset, SeekPos pos)
164 {
165 switch (pos) {
166 case SeekPos::BEGIN:
167 currentOffset_ = offset;
168 break;
169 case SeekPos::CURRENT:
170 currentOffset_ += offset;
171 break;
172 case SeekPos::END:
173 currentOffset_ = bufferSize_ + offset;
174 break;
175 default:
176 return -1;
177 }
178
179 if (currentOffset_ > bufferSize_) {
180 currentOffset_ = bufferSize_;
181 }
182
183 return currentOffset_;
184 }
185
Tell()186 long BufferMetadataStream::Tell()
187 {
188 return currentOffset_;
189 }
190
IsEof()191 bool BufferMetadataStream::IsEof()
192 {
193 return currentOffset_ >= bufferSize_;
194 }
195
IsOpen()196 bool BufferMetadataStream::IsOpen()
197 {
198 return true;
199 }
200
Close()201 void BufferMetadataStream::Close()
202 {
203 if (memoryMode_ == Dynamic && buffer_ != originData_ && buffer_ != nullptr) {
204 delete[] buffer_;
205 buffer_ = nullptr;
206 }
207 currentOffset_ = 0;
208 }
209
Open(OpenMode mode)210 bool BufferMetadataStream::Open(OpenMode mode)
211 {
212 return true;
213 }
214
Flush()215 bool BufferMetadataStream::Flush()
216 {
217 return true;
218 }
219
GetAddr(bool isWriteable)220 byte *BufferMetadataStream::GetAddr(bool isWriteable)
221 {
222 return buffer_;
223 }
224
CopyFrom(MetadataStream & src)225 bool BufferMetadataStream::CopyFrom(MetadataStream &src)
226 {
227 CHECK_ERROR_RETURN_RET_LOG(!src.IsOpen(),
228 false, "BufferMetadataStream::CopyFrom failed, source stream is not open");
229
230 CHECK_ERROR_RETURN_RET(src.GetSize() == 0, true);
231
232 if (memoryMode_ == Fix) {
233 // If the memory is fixed and the source size is too large, do not copy the data
234 CHECK_ERROR_RETURN_RET_LOG(src.GetSize() > static_cast<ssize_t>(capacity_),
235 false, "BufferMetadataStream::CopyFrom failed, source size is larger than capacity, "
236 "source size:%{public}zu, capacity:%{public}ld",
237 src.GetSize(), capacity_);
238 }
239
240 // Clear the current buffer
241 if (memoryMode_ == Dynamic) {
242 if (buffer_ != nullptr && buffer_ != originData_) {
243 delete[] buffer_;
244 buffer_ = nullptr;
245 }
246 ssize_t estimatedSize = ((src.GetSize() + METADATA_STREAM_PAGE_SIZE - 1) / METADATA_STREAM_PAGE_SIZE) *
247 METADATA_STREAM_PAGE_SIZE; // Ensure it is a multiple of 32k
248 buffer_ = new (std::nothrow) byte[estimatedSize];
249 CHECK_ERROR_RETURN_RET_LOG(buffer_ == nullptr,
250 false, "BufferMetadataStream::CopyFrom failed, insufficient memory for buffer allocation");
251 capacity_ = estimatedSize;
252 }
253
254 currentOffset_ = 0;
255 bufferSize_ = 0;
256
257 // Read data from the source ImageStream and write it to the current buffer
258 CHECK_ERROR_RETURN_RET(!ReadAndWriteData(src), false);
259 return true;
260 }
261
ReadAndWriteData(MetadataStream & src)262 bool BufferMetadataStream::ReadAndWriteData(MetadataStream &src)
263 {
264 src.Seek(0, SeekPos::BEGIN);
265 ssize_t buffer_size = std::min((ssize_t)METADATA_STREAM_PAGE_SIZE, src.GetSize());
266 if (buffer_size > METADATA_STREAM_PAGE_SIZE) {
267 return false;
268 }
269 byte *tempBuffer = new (std::nothrow) byte[buffer_size];
270 CHECK_ERROR_RETURN_RET_LOG(tempBuffer == nullptr,
271 false, "BufferMetadataStream::ReadAndWriteData failed, insufficient memory for temporary buffer");
272
273 while (!src.IsEof()) {
274 ssize_t bytesRead = src.Read(tempBuffer, buffer_size);
275 if (bytesRead > 0) {
276 if (Write(tempBuffer, bytesRead) == -1) {
277 IMAGE_LOGE("BufferMetadataStream::ReadAndWriteData failed, unable to write to buffer");
278 HandleWriteFailure();
279 delete[] tempBuffer;
280 return false;
281 }
282 }
283 }
284 delete[] tempBuffer;
285 return true;
286 }
287
HandleWriteFailure()288 void BufferMetadataStream::HandleWriteFailure()
289 {
290 if (memoryMode_ == Dynamic && buffer_ != originData_) {
291 delete[] buffer_;
292 buffer_ = nullptr;
293 capacity_ = 0;
294 }
295 bufferSize_ = 0;
296 currentOffset_ = 0;
297 }
298
GetSize()299 ssize_t BufferMetadataStream::GetSize()
300 {
301 return bufferSize_;
302 }
303
Release()304 byte *BufferMetadataStream::Release()
305 {
306 byte *ret = buffer_;
307 buffer_ = nullptr;
308 capacity_ = 0;
309 bufferSize_ = 0;
310 currentOffset_ = 0;
311 return ret;
312 }
313
CalculateNewCapacity(long currentOffset,ssize_t size)314 long BufferMetadataStream::CalculateNewCapacity(long currentOffset, ssize_t size)
315 {
316 long newCapacity;
317 switch (expandCount_) {
318 case INITIAL_EXPANSION:
319 newCapacity =
320 ((currentOffset + size + METADATA_STREAM_INITIAL_CAPACITY - 1) / METADATA_STREAM_INITIAL_CAPACITY) *
321 METADATA_STREAM_INITIAL_CAPACITY;
322 break;
323 case SECOND_EXPANSION:
324 newCapacity =
325 ((currentOffset + size + METADATA_STREAM_CAPACITY_512KB - 1) / METADATA_STREAM_CAPACITY_512KB) *
326 METADATA_STREAM_CAPACITY_512KB;
327 break;
328 case THIRD_EXPANSION:
329 newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_2MB - 1) / METADATA_STREAM_CAPACITY_2MB) *
330 METADATA_STREAM_CAPACITY_2MB;
331 break;
332 case FOURTH_EXPANSION:
333 newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_5MB - 1) / METADATA_STREAM_CAPACITY_5MB) *
334 METADATA_STREAM_CAPACITY_5MB;
335 break;
336 case FIFTH_EXPANSION:
337 newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_15MB - 1) / METADATA_STREAM_CAPACITY_15MB) *
338 METADATA_STREAM_CAPACITY_15MB;
339 break;
340 default:
341 newCapacity = ((currentOffset + size + METADATA_STREAM_CAPACITY_30MB - 1) / METADATA_STREAM_CAPACITY_30MB) *
342 METADATA_STREAM_CAPACITY_30MB;
343 break;
344 }
345 return newCapacity;
346 }
347 } // namespace Media
348 } // namespace OHOS
349