/src/mozilla-central/xpcom/io/nsStorageStream.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
2 | | /* vim: set ts=8 sts=2 et sw=2 tw=80: */ |
3 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
4 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
5 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
6 | | |
7 | | /* |
8 | | * The storage stream provides an internal buffer that can be filled by a |
9 | | * client using a single output stream. One or more independent input streams |
10 | | * can be created to read the data out non-destructively. The implementation |
11 | | * uses a segmented buffer internally to avoid realloc'ing of large buffers, |
12 | | * with the attendant performance loss and heap fragmentation. |
13 | | */ |
14 | | |
15 | | #include "nsAlgorithm.h" |
16 | | #include "nsStorageStream.h" |
17 | | #include "nsSegmentedBuffer.h" |
18 | | #include "nsStreamUtils.h" |
19 | | #include "nsCOMPtr.h" |
20 | | #include "nsICloneableInputStream.h" |
21 | | #include "nsIInputStream.h" |
22 | | #include "nsIIPCSerializableInputStream.h" |
23 | | #include "nsISeekableStream.h" |
24 | | #include "mozilla/Logging.h" |
25 | | #include "mozilla/Attributes.h" |
26 | | #include "mozilla/Likely.h" |
27 | | #include "mozilla/MathAlgorithms.h" |
28 | | #include "mozilla/ipc/InputStreamUtils.h" |
29 | | |
30 | | using mozilla::ipc::InputStreamParams; |
31 | | using mozilla::ipc::StringInputStreamParams; |
32 | | using mozilla::Maybe; |
33 | | using mozilla::Some; |
34 | | |
35 | | // |
36 | | // Log module for StorageStream logging... |
37 | | // |
38 | | // To enable logging (see prlog.h for full details): |
39 | | // |
40 | | // set MOZ_LOG=StorageStreamLog:5 |
41 | | // set MOZ_LOG_FILE=storage.log |
42 | | // |
43 | | // This enables LogLevel::Debug level information and places all output in |
44 | | // the file storage.log. |
45 | | // |
46 | | static LazyLogModule sStorageStreamLog("nsStorageStream"); |
47 | | #ifdef LOG |
48 | | #undef LOG |
49 | | #endif |
50 | 0 | #define LOG(args) MOZ_LOG(sStorageStreamLog, mozilla::LogLevel::Debug, args) |
51 | | |
52 | | nsStorageStream::nsStorageStream() |
53 | | : mSegmentedBuffer(0), mSegmentSize(0), mSegmentSizeLog2(0), |
54 | | mWriteInProgress(false), mLastSegmentNum(-1), mWriteCursor(0), |
55 | | mSegmentEnd(0), mLogicalLength(0) |
56 | 0 | { |
57 | 0 | LOG(("Creating nsStorageStream [%p].\n", this)); |
58 | 0 | } |
59 | | |
60 | | nsStorageStream::~nsStorageStream() |
61 | 0 | { |
62 | 0 | delete mSegmentedBuffer; |
63 | 0 | } |
64 | | |
65 | | NS_IMPL_ISUPPORTS(nsStorageStream, |
66 | | nsIStorageStream, |
67 | | nsIOutputStream) |
68 | | |
69 | | NS_IMETHODIMP |
70 | | nsStorageStream::Init(uint32_t aSegmentSize, uint32_t aMaxSize) |
71 | 0 | { |
72 | 0 | mSegmentedBuffer = new nsSegmentedBuffer(); |
73 | 0 | mSegmentSize = aSegmentSize; |
74 | 0 | mSegmentSizeLog2 = mozilla::FloorLog2(aSegmentSize); |
75 | 0 |
|
76 | 0 | // Segment size must be a power of two |
77 | 0 | if (mSegmentSize != ((uint32_t)1 << mSegmentSizeLog2)) { |
78 | 0 | return NS_ERROR_INVALID_ARG; |
79 | 0 | } |
80 | 0 | |
81 | 0 | return mSegmentedBuffer->Init(aSegmentSize, aMaxSize); |
82 | 0 | } |
83 | | |
84 | | NS_IMETHODIMP |
85 | | nsStorageStream::GetOutputStream(int32_t aStartingOffset, |
86 | | nsIOutputStream** aOutputStream) |
87 | 0 | { |
88 | 0 | if (NS_WARN_IF(!aOutputStream)) { |
89 | 0 | return NS_ERROR_INVALID_ARG; |
90 | 0 | } |
91 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
92 | 0 | return NS_ERROR_NOT_INITIALIZED; |
93 | 0 | } |
94 | 0 | |
95 | 0 | if (mWriteInProgress) { |
96 | 0 | return NS_ERROR_NOT_AVAILABLE; |
97 | 0 | } |
98 | 0 | |
99 | 0 | nsresult rv = Seek(aStartingOffset); |
100 | 0 | if (NS_FAILED(rv)) { |
101 | 0 | return rv; |
102 | 0 | } |
103 | 0 | |
104 | 0 | // Enlarge the last segment in the buffer so that it is the same size as |
105 | 0 | // all the other segments in the buffer. (It may have been realloc'ed |
106 | 0 | // smaller in the Close() method.) |
107 | 0 | if (mLastSegmentNum >= 0) |
108 | 0 | if (mSegmentedBuffer->ReallocLastSegment(mSegmentSize)) { |
109 | 0 | // Need to re-Seek, since realloc changed segment base pointer |
110 | 0 | rv = Seek(aStartingOffset); |
111 | 0 | if (NS_FAILED(rv)) { |
112 | 0 | return rv; |
113 | 0 | } |
114 | 0 | } |
115 | 0 | |
116 | 0 | NS_ADDREF(this); |
117 | 0 | *aOutputStream = static_cast<nsIOutputStream*>(this); |
118 | 0 | mWriteInProgress = true; |
119 | 0 | return NS_OK; |
120 | 0 | } |
121 | | |
122 | | NS_IMETHODIMP |
123 | | nsStorageStream::Close() |
124 | 0 | { |
125 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
126 | 0 | return NS_ERROR_NOT_INITIALIZED; |
127 | 0 | } |
128 | 0 | |
129 | 0 | mWriteInProgress = false; |
130 | 0 |
|
131 | 0 | int32_t segmentOffset = SegOffset(mLogicalLength); |
132 | 0 |
|
133 | 0 | // Shrink the final segment in the segmented buffer to the minimum size |
134 | 0 | // needed to contain the data, so as to conserve memory. |
135 | 0 | if (segmentOffset) { |
136 | 0 | mSegmentedBuffer->ReallocLastSegment(segmentOffset); |
137 | 0 | } |
138 | 0 |
|
139 | 0 | mWriteCursor = 0; |
140 | 0 | mSegmentEnd = 0; |
141 | 0 |
|
142 | 0 | LOG(("nsStorageStream [%p] Close mWriteCursor=%p mSegmentEnd=%p\n", |
143 | 0 | this, mWriteCursor, mSegmentEnd)); |
144 | 0 |
|
145 | 0 | return NS_OK; |
146 | 0 | } |
147 | | |
148 | | NS_IMETHODIMP |
149 | | nsStorageStream::Flush() |
150 | 0 | { |
151 | 0 | return NS_OK; |
152 | 0 | } |
153 | | |
154 | | NS_IMETHODIMP |
155 | | nsStorageStream::Write(const char* aBuffer, uint32_t aCount, |
156 | | uint32_t* aNumWritten) |
157 | 0 | { |
158 | 0 | if (NS_WARN_IF(!aNumWritten) || NS_WARN_IF(!aBuffer)) { |
159 | 0 | return NS_ERROR_INVALID_ARG; |
160 | 0 | } |
161 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
162 | 0 | return NS_ERROR_NOT_INITIALIZED; |
163 | 0 | } |
164 | 0 | |
165 | 0 | const char* readCursor; |
166 | 0 | uint32_t count, availableInSegment, remaining; |
167 | 0 | nsresult rv = NS_OK; |
168 | 0 |
|
169 | 0 | LOG(("nsStorageStream [%p] Write mWriteCursor=%p mSegmentEnd=%p aCount=%d\n", |
170 | 0 | this, mWriteCursor, mSegmentEnd, aCount)); |
171 | 0 |
|
172 | 0 | remaining = aCount; |
173 | 0 | readCursor = aBuffer; |
174 | 0 | // If no segments have been created yet, create one even if we don't have |
175 | 0 | // to write any data; this enables creating an input stream which reads from |
176 | 0 | // the very end of the data for any amount of data in the stream (i.e. |
177 | 0 | // this stream contains N bytes of data and newInputStream(N) is called), |
178 | 0 | // even for N=0 (with the caveat that we require .write("", 0) be called to |
179 | 0 | // initialize internal buffers). |
180 | 0 | bool firstTime = mSegmentedBuffer->GetSegmentCount() == 0; |
181 | 0 | while (remaining || MOZ_UNLIKELY(firstTime)) { |
182 | 0 | firstTime = false; |
183 | 0 | availableInSegment = mSegmentEnd - mWriteCursor; |
184 | 0 | if (!availableInSegment) { |
185 | 0 | mWriteCursor = mSegmentedBuffer->AppendNewSegment(); |
186 | 0 | if (!mWriteCursor) { |
187 | 0 | mSegmentEnd = 0; |
188 | 0 | rv = NS_ERROR_OUT_OF_MEMORY; |
189 | 0 | goto out; |
190 | 0 | } |
191 | 0 | mLastSegmentNum++; |
192 | 0 | mSegmentEnd = mWriteCursor + mSegmentSize; |
193 | 0 | availableInSegment = mSegmentEnd - mWriteCursor; |
194 | 0 | LOG(("nsStorageStream [%p] Write (new seg) mWriteCursor=%p mSegmentEnd=%p\n", |
195 | 0 | this, mWriteCursor, mSegmentEnd)); |
196 | 0 | } |
197 | 0 |
|
198 | 0 | count = XPCOM_MIN(availableInSegment, remaining); |
199 | 0 | memcpy(mWriteCursor, readCursor, count); |
200 | 0 | remaining -= count; |
201 | 0 | readCursor += count; |
202 | 0 | mWriteCursor += count; |
203 | 0 | LOG(("nsStorageStream [%p] Writing mWriteCursor=%p mSegmentEnd=%p count=%d\n", |
204 | 0 | this, mWriteCursor, mSegmentEnd, count)); |
205 | 0 | } |
206 | 0 |
|
207 | 0 | out: |
208 | 0 | *aNumWritten = aCount - remaining; |
209 | 0 | mLogicalLength += *aNumWritten; |
210 | 0 |
|
211 | 0 | LOG(("nsStorageStream [%p] Wrote mWriteCursor=%p mSegmentEnd=%p numWritten=%d\n", |
212 | 0 | this, mWriteCursor, mSegmentEnd, *aNumWritten)); |
213 | 0 | return rv; |
214 | 0 | } |
215 | | |
216 | | NS_IMETHODIMP |
217 | | nsStorageStream::WriteFrom(nsIInputStream* aInStr, uint32_t aCount, |
218 | | uint32_t* aResult) |
219 | 0 | { |
220 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
221 | 0 | } |
222 | | |
223 | | NS_IMETHODIMP |
224 | | nsStorageStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure, |
225 | | uint32_t aCount, uint32_t* aResult) |
226 | 0 | { |
227 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
228 | 0 | } |
229 | | |
230 | | NS_IMETHODIMP |
231 | | nsStorageStream::IsNonBlocking(bool* aNonBlocking) |
232 | 0 | { |
233 | 0 | *aNonBlocking = false; |
234 | 0 | return NS_OK; |
235 | 0 | } |
236 | | |
237 | | NS_IMETHODIMP |
238 | | nsStorageStream::GetLength(uint32_t* aLength) |
239 | 0 | { |
240 | 0 | *aLength = mLogicalLength; |
241 | 0 | return NS_OK; |
242 | 0 | } |
243 | | |
244 | | // Truncate the buffer by deleting the end segments |
245 | | NS_IMETHODIMP |
246 | | nsStorageStream::SetLength(uint32_t aLength) |
247 | 0 | { |
248 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
249 | 0 | return NS_ERROR_NOT_INITIALIZED; |
250 | 0 | } |
251 | 0 | |
252 | 0 | if (mWriteInProgress) { |
253 | 0 | return NS_ERROR_NOT_AVAILABLE; |
254 | 0 | } |
255 | 0 | |
256 | 0 | if (aLength > mLogicalLength) { |
257 | 0 | return NS_ERROR_INVALID_ARG; |
258 | 0 | } |
259 | 0 | |
260 | 0 | int32_t newLastSegmentNum = SegNum(aLength); |
261 | 0 | int32_t segmentOffset = SegOffset(aLength); |
262 | 0 | if (segmentOffset == 0) { |
263 | 0 | newLastSegmentNum--; |
264 | 0 | } |
265 | 0 |
|
266 | 0 | while (newLastSegmentNum < mLastSegmentNum) { |
267 | 0 | mSegmentedBuffer->DeleteLastSegment(); |
268 | 0 | mLastSegmentNum--; |
269 | 0 | } |
270 | 0 |
|
271 | 0 | mLogicalLength = aLength; |
272 | 0 | return NS_OK; |
273 | 0 | } |
274 | | |
275 | | NS_IMETHODIMP |
276 | | nsStorageStream::GetWriteInProgress(bool* aWriteInProgress) |
277 | 0 | { |
278 | 0 | *aWriteInProgress = mWriteInProgress; |
279 | 0 | return NS_OK; |
280 | 0 | } |
281 | | |
282 | | nsresult |
283 | | nsStorageStream::Seek(int32_t aPosition) |
284 | 0 | { |
285 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
286 | 0 | return NS_ERROR_NOT_INITIALIZED; |
287 | 0 | } |
288 | 0 | |
289 | 0 | // An argument of -1 means "seek to end of stream" |
290 | 0 | if (aPosition == -1) { |
291 | 0 | aPosition = mLogicalLength; |
292 | 0 | } |
293 | 0 |
|
294 | 0 | // Seeking beyond the buffer end is illegal |
295 | 0 | if ((uint32_t)aPosition > mLogicalLength) { |
296 | 0 | return NS_ERROR_INVALID_ARG; |
297 | 0 | } |
298 | 0 | |
299 | 0 | // Seeking backwards in the write stream results in truncation |
300 | 0 | SetLength(aPosition); |
301 | 0 |
|
302 | 0 | // Special handling for seek to start-of-buffer |
303 | 0 | if (aPosition == 0) { |
304 | 0 | mWriteCursor = 0; |
305 | 0 | mSegmentEnd = 0; |
306 | 0 | LOG(("nsStorageStream [%p] Seek mWriteCursor=%p mSegmentEnd=%p\n", |
307 | 0 | this, mWriteCursor, mSegmentEnd)); |
308 | 0 | return NS_OK; |
309 | 0 | } |
310 | 0 |
|
311 | 0 | // Segment may have changed, so reset pointers |
312 | 0 | mWriteCursor = mSegmentedBuffer->GetSegment(mLastSegmentNum); |
313 | 0 | NS_ASSERTION(mWriteCursor, "null mWriteCursor"); |
314 | 0 | mSegmentEnd = mWriteCursor + mSegmentSize; |
315 | 0 |
|
316 | 0 | // Adjust write cursor for current segment offset. This test is necessary |
317 | 0 | // because SegNum may reference the next-to-be-allocated segment, in which |
318 | 0 | // case we need to be pointing at the end of the last segment. |
319 | 0 | int32_t segmentOffset = SegOffset(aPosition); |
320 | 0 | if (segmentOffset == 0 && (SegNum(aPosition) > (uint32_t) mLastSegmentNum)) { |
321 | 0 | mWriteCursor = mSegmentEnd; |
322 | 0 | } else { |
323 | 0 | mWriteCursor += segmentOffset; |
324 | 0 | } |
325 | 0 |
|
326 | 0 | LOG(("nsStorageStream [%p] Seek mWriteCursor=%p mSegmentEnd=%p\n", |
327 | 0 | this, mWriteCursor, mSegmentEnd)); |
328 | 0 | return NS_OK; |
329 | 0 | } |
330 | | |
331 | | //////////////////////////////////////////////////////////////////////////////// |
332 | | |
333 | | // There can be many nsStorageInputStreams for a single nsStorageStream |
334 | | class nsStorageInputStream final |
335 | | : public nsIInputStream |
336 | | , public nsISeekableStream |
337 | | , public nsIIPCSerializableInputStream |
338 | | , public nsICloneableInputStream |
339 | | { |
340 | | public: |
341 | | nsStorageInputStream(nsStorageStream* aStorageStream, uint32_t aSegmentSize) |
342 | | : mStorageStream(aStorageStream), mReadCursor(0), |
343 | | mSegmentEnd(0), mSegmentNum(0), |
344 | | mSegmentSize(aSegmentSize), mLogicalCursor(0), |
345 | | mStatus(NS_OK) |
346 | 0 | { |
347 | 0 | } |
348 | | |
349 | | NS_DECL_THREADSAFE_ISUPPORTS |
350 | | NS_DECL_NSIINPUTSTREAM |
351 | | NS_DECL_NSISEEKABLESTREAM |
352 | | NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM |
353 | | NS_DECL_NSICLONEABLEINPUTSTREAM |
354 | | |
355 | | private: |
356 | | ~nsStorageInputStream() |
357 | 0 | { |
358 | 0 | } |
359 | | |
360 | | protected: |
361 | | nsresult Seek(uint32_t aPosition); |
362 | | |
363 | | friend class nsStorageStream; |
364 | | |
365 | | private: |
366 | | RefPtr<nsStorageStream> mStorageStream; |
367 | | uint32_t mReadCursor; // Next memory location to read byte, or 0 |
368 | | uint32_t mSegmentEnd; // One byte past end of current buffer segment |
369 | | uint32_t mSegmentNum; // Segment number containing read cursor |
370 | | uint32_t mSegmentSize; // All segments, except the last, are of this size |
371 | | uint32_t mLogicalCursor; // Logical offset into stream |
372 | | nsresult mStatus; |
373 | | |
374 | | uint32_t SegNum(uint32_t aPosition) |
375 | 0 | { |
376 | 0 | return aPosition >> mStorageStream->mSegmentSizeLog2; |
377 | 0 | } |
378 | | uint32_t SegOffset(uint32_t aPosition) |
379 | 0 | { |
380 | 0 | return aPosition & (mSegmentSize - 1); |
381 | 0 | } |
382 | | }; |
383 | | |
384 | | NS_IMPL_ISUPPORTS(nsStorageInputStream, |
385 | | nsIInputStream, |
386 | | nsISeekableStream, |
387 | | nsIIPCSerializableInputStream, |
388 | | nsICloneableInputStream) |
389 | | |
390 | | NS_IMETHODIMP |
391 | | nsStorageStream::NewInputStream(int32_t aStartingOffset, |
392 | | nsIInputStream** aInputStream) |
393 | 0 | { |
394 | 0 | if (NS_WARN_IF(!mSegmentedBuffer)) { |
395 | 0 | return NS_ERROR_NOT_INITIALIZED; |
396 | 0 | } |
397 | 0 | |
398 | 0 | RefPtr<nsStorageInputStream> inputStream = |
399 | 0 | new nsStorageInputStream(this, mSegmentSize); |
400 | 0 |
|
401 | 0 | nsresult rv = inputStream->Seek(aStartingOffset); |
402 | 0 | if (NS_FAILED(rv)) { |
403 | 0 | return rv; |
404 | 0 | } |
405 | 0 | |
406 | 0 | inputStream.forget(aInputStream); |
407 | 0 | return NS_OK; |
408 | 0 | } |
409 | | |
410 | | NS_IMETHODIMP |
411 | | nsStorageInputStream::Close() |
412 | 0 | { |
413 | 0 | mStatus = NS_BASE_STREAM_CLOSED; |
414 | 0 | return NS_OK; |
415 | 0 | } |
416 | | |
417 | | NS_IMETHODIMP |
418 | | nsStorageInputStream::Available(uint64_t* aAvailable) |
419 | 0 | { |
420 | 0 | if (NS_FAILED(mStatus)) { |
421 | 0 | return mStatus; |
422 | 0 | } |
423 | 0 | |
424 | 0 | *aAvailable = mStorageStream->mLogicalLength - mLogicalCursor; |
425 | 0 | return NS_OK; |
426 | 0 | } |
427 | | |
428 | | NS_IMETHODIMP |
429 | | nsStorageInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aNumRead) |
430 | 0 | { |
431 | 0 | return ReadSegments(NS_CopySegmentToBuffer, aBuffer, aCount, aNumRead); |
432 | 0 | } |
433 | | |
434 | | NS_IMETHODIMP |
435 | | nsStorageInputStream::ReadSegments(nsWriteSegmentFun aWriter, void* aClosure, |
436 | | uint32_t aCount, uint32_t* aNumRead) |
437 | 0 | { |
438 | 0 | *aNumRead = 0; |
439 | 0 | if (mStatus == NS_BASE_STREAM_CLOSED) { |
440 | 0 | return NS_OK; |
441 | 0 | } |
442 | 0 | if (NS_FAILED(mStatus)) { |
443 | 0 | return mStatus; |
444 | 0 | } |
445 | 0 | |
446 | 0 | uint32_t count, availableInSegment, remainingCapacity, bytesConsumed; |
447 | 0 | nsresult rv; |
448 | 0 |
|
449 | 0 | remainingCapacity = aCount; |
450 | 0 | while (remainingCapacity) { |
451 | 0 | availableInSegment = mSegmentEnd - mReadCursor; |
452 | 0 | if (!availableInSegment) { |
453 | 0 | uint32_t available = mStorageStream->mLogicalLength - mLogicalCursor; |
454 | 0 | if (!available) { |
455 | 0 | goto out; |
456 | 0 | } |
457 | 0 | |
458 | 0 | // We have data in the stream, but if mSegmentEnd is zero, then we |
459 | 0 | // were likely constructed prior to any data being written into |
460 | 0 | // the stream. Therefore, if mSegmentEnd is non-zero, we should |
461 | 0 | // move into the next segment; otherwise, we should stay in this |
462 | 0 | // segment so our input state can be updated and we can properly |
463 | 0 | // perform the initial read. |
464 | 0 | if (mSegmentEnd > 0) { |
465 | 0 | mSegmentNum++; |
466 | 0 | } |
467 | 0 | mReadCursor = 0; |
468 | 0 | mSegmentEnd = XPCOM_MIN(mSegmentSize, available); |
469 | 0 | availableInSegment = mSegmentEnd; |
470 | 0 | } |
471 | 0 | const char* cur = mStorageStream->mSegmentedBuffer->GetSegment(mSegmentNum); |
472 | 0 |
|
473 | 0 | count = XPCOM_MIN(availableInSegment, remainingCapacity); |
474 | 0 | rv = aWriter(this, aClosure, cur + mReadCursor, aCount - remainingCapacity, |
475 | 0 | count, &bytesConsumed); |
476 | 0 | if (NS_FAILED(rv) || (bytesConsumed == 0)) { |
477 | 0 | break; |
478 | 0 | } |
479 | 0 | remainingCapacity -= bytesConsumed; |
480 | 0 | mReadCursor += bytesConsumed; |
481 | 0 | mLogicalCursor += bytesConsumed; |
482 | 0 | } |
483 | 0 |
|
484 | 0 | out: |
485 | 0 | *aNumRead = aCount - remainingCapacity; |
486 | 0 |
|
487 | 0 | bool isWriteInProgress = false; |
488 | 0 | if (NS_FAILED(mStorageStream->GetWriteInProgress(&isWriteInProgress))) { |
489 | 0 | isWriteInProgress = false; |
490 | 0 | } |
491 | 0 |
|
492 | 0 | if (*aNumRead == 0 && isWriteInProgress) { |
493 | 0 | return NS_BASE_STREAM_WOULD_BLOCK; |
494 | 0 | } |
495 | 0 | |
496 | 0 | return NS_OK; |
497 | 0 | } |
498 | | |
499 | | NS_IMETHODIMP |
500 | | nsStorageInputStream::IsNonBlocking(bool* aNonBlocking) |
501 | 0 | { |
502 | 0 | // TODO: This class should implement nsIAsyncInputStream so that callers |
503 | 0 | // have some way of dealing with NS_BASE_STREAM_WOULD_BLOCK errors. |
504 | 0 |
|
505 | 0 | *aNonBlocking = true; |
506 | 0 | return NS_OK; |
507 | 0 | } |
508 | | |
509 | | NS_IMETHODIMP |
510 | | nsStorageInputStream::Seek(int32_t aWhence, int64_t aOffset) |
511 | 0 | { |
512 | 0 | if (NS_FAILED(mStatus)) { |
513 | 0 | return mStatus; |
514 | 0 | } |
515 | 0 | |
516 | 0 | int64_t pos = aOffset; |
517 | 0 |
|
518 | 0 | switch (aWhence) { |
519 | 0 | case NS_SEEK_SET: |
520 | 0 | break; |
521 | 0 | case NS_SEEK_CUR: |
522 | 0 | pos += mLogicalCursor; |
523 | 0 | break; |
524 | 0 | case NS_SEEK_END: |
525 | 0 | pos += mStorageStream->mLogicalLength; |
526 | 0 | break; |
527 | 0 | default: |
528 | 0 | MOZ_ASSERT_UNREACHABLE("unexpected whence value"); |
529 | 0 | return NS_ERROR_UNEXPECTED; |
530 | 0 | } |
531 | 0 | if (pos == int64_t(mLogicalCursor)) { |
532 | 0 | return NS_OK; |
533 | 0 | } |
534 | 0 | |
535 | 0 | return Seek(pos); |
536 | 0 | } |
537 | | |
538 | | NS_IMETHODIMP |
539 | | nsStorageInputStream::Tell(int64_t* aResult) |
540 | 0 | { |
541 | 0 | if (NS_FAILED(mStatus)) { |
542 | 0 | return mStatus; |
543 | 0 | } |
544 | 0 | |
545 | 0 | *aResult = mLogicalCursor; |
546 | 0 | return NS_OK; |
547 | 0 | } |
548 | | |
549 | | NS_IMETHODIMP |
550 | | nsStorageInputStream::SetEOF() |
551 | 0 | { |
552 | 0 | MOZ_ASSERT_UNREACHABLE("nsStorageInputStream::SetEOF"); |
553 | 0 | return NS_ERROR_NOT_IMPLEMENTED; |
554 | 0 | } |
555 | | |
556 | | nsresult |
557 | | nsStorageInputStream::Seek(uint32_t aPosition) |
558 | 0 | { |
559 | 0 | uint32_t length = mStorageStream->mLogicalLength; |
560 | 0 | if (aPosition > length) { |
561 | 0 | return NS_ERROR_INVALID_ARG; |
562 | 0 | } |
563 | 0 | |
564 | 0 | if (length == 0) { |
565 | 0 | return NS_OK; |
566 | 0 | } |
567 | 0 | |
568 | 0 | mSegmentNum = SegNum(aPosition); |
569 | 0 | mReadCursor = SegOffset(aPosition); |
570 | 0 | uint32_t available = length - aPosition; |
571 | 0 | mSegmentEnd = mReadCursor + XPCOM_MIN(mSegmentSize - mReadCursor, available); |
572 | 0 | mLogicalCursor = aPosition; |
573 | 0 | return NS_OK; |
574 | 0 | } |
575 | | |
576 | | void |
577 | | nsStorageInputStream::Serialize(InputStreamParams& aParams, FileDescriptorArray&) |
578 | 0 | { |
579 | 0 | nsCString combined; |
580 | 0 | int64_t offset; |
581 | 0 | nsresult rv = Tell(&offset); |
582 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
583 | 0 |
|
584 | 0 | uint64_t remaining; |
585 | 0 | rv = Available(&remaining); |
586 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
587 | 0 |
|
588 | 0 | auto handle = combined.BulkWrite(remaining, 0, false, rv); |
589 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
590 | 0 |
|
591 | 0 | uint32_t numRead = 0; |
592 | 0 |
|
593 | 0 | rv = Read(handle.Elements(), remaining, &numRead); |
594 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
595 | 0 |
|
596 | 0 | MOZ_ASSERT(numRead == remaining); |
597 | 0 | handle.Finish(numRead, false); |
598 | 0 |
|
599 | 0 | rv = Seek(NS_SEEK_SET, offset); |
600 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
601 | 0 |
|
602 | 0 | StringInputStreamParams params; |
603 | 0 | params.data() = combined; |
604 | 0 | aParams = params; |
605 | 0 | } |
606 | | |
607 | | Maybe<uint64_t> |
608 | | nsStorageInputStream::ExpectedSerializedLength() |
609 | 0 | { |
610 | 0 | uint64_t remaining = 0; |
611 | 0 | DebugOnly<nsresult> rv = Available(&remaining); |
612 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv)); |
613 | 0 | return Some(remaining); |
614 | 0 | } |
615 | | |
616 | | bool |
617 | | nsStorageInputStream::Deserialize(const InputStreamParams& aParams, |
618 | | const FileDescriptorArray&) |
619 | 0 | { |
620 | 0 | MOZ_ASSERT_UNREACHABLE("We should never attempt to deserialize a storage " |
621 | 0 | "input stream."); |
622 | 0 | return false; |
623 | 0 | } |
624 | | |
625 | | NS_IMETHODIMP |
626 | | nsStorageInputStream::GetCloneable(bool* aCloneableOut) |
627 | 0 | { |
628 | 0 | *aCloneableOut = true; |
629 | 0 | return NS_OK; |
630 | 0 | } |
631 | | |
632 | | NS_IMETHODIMP |
633 | | nsStorageInputStream::Clone(nsIInputStream** aCloneOut) |
634 | 0 | { |
635 | 0 | return mStorageStream->NewInputStream(mLogicalCursor, aCloneOut); |
636 | 0 | } |
637 | | |
638 | | nsresult |
639 | | NS_NewStorageStream(uint32_t aSegmentSize, uint32_t aMaxSize, |
640 | | nsIStorageStream** aResult) |
641 | 0 | { |
642 | 0 | RefPtr<nsStorageStream> storageStream = new nsStorageStream(); |
643 | 0 | nsresult rv = storageStream->Init(aSegmentSize, aMaxSize); |
644 | 0 | if (NS_FAILED(rv)) { |
645 | 0 | return rv; |
646 | 0 | } |
647 | 0 | storageStream.forget(aResult); |
648 | 0 | return NS_OK; |
649 | 0 | } |
650 | | |
651 | | // Undefine LOG, so that other .cpp files (or their includes) won't complain |
652 | | // about it already being defined, when we build in unified mode. |
653 | | #undef LOG |