Bug 1708422: part 8) Move `mozInlineSpellChecker::CheckWordsAndAddRangesForMisspellin...
[gecko.git] / netwerk / cache2 / CacheFileOutputStream.cpp
blob93517650385b0e40700a0c0dae5f2aa3920a02d1
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "CacheLog.h"
6 #include "CacheFileOutputStream.h"
8 #include "CacheFile.h"
9 #include "CacheEntry.h"
10 #include "nsStreamUtils.h"
11 #include "nsThreadUtils.h"
12 #include "mozilla/DebugOnly.h"
13 #include <algorithm>
15 namespace mozilla {
16 namespace net {
18 NS_IMPL_ADDREF(CacheFileOutputStream)
19 NS_IMETHODIMP_(MozExternalRefCountType)
20 CacheFileOutputStream::Release() {
21 MOZ_ASSERT(0 != mRefCnt, "dup release");
22 nsrefcnt count = --mRefCnt;
23 NS_LOG_RELEASE(this, count, "CacheFileOutputStream");
25 if (0 == count) {
26 mRefCnt = 1;
28 CacheFileAutoLock lock(mFile);
29 mFile->RemoveOutput(this, mStatus);
31 delete (this);
32 return 0;
35 return count;
38 NS_INTERFACE_MAP_BEGIN(CacheFileOutputStream)
39 NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
40 NS_INTERFACE_MAP_ENTRY(nsIAsyncOutputStream)
41 NS_INTERFACE_MAP_ENTRY(nsISeekableStream)
42 NS_INTERFACE_MAP_ENTRY(nsITellableStream)
43 NS_INTERFACE_MAP_ENTRY(mozilla::net::CacheFileChunkListener)
44 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
45 NS_INTERFACE_MAP_END
47 CacheFileOutputStream::CacheFileOutputStream(
48 CacheFile* aFile, CacheOutputCloseListener* aCloseListener,
49 bool aAlternativeData)
50 : mFile(aFile),
51 mCloseListener(aCloseListener),
52 mPos(0),
53 mClosed(false),
54 mAlternativeData(aAlternativeData),
55 mStatus(NS_OK),
56 mCallbackFlags(0) {
57 LOG(("CacheFileOutputStream::CacheFileOutputStream() [this=%p]", this));
59 if (mAlternativeData) {
60 mPos = mFile->mAltDataOffset;
64 CacheFileOutputStream::~CacheFileOutputStream() {
65 LOG(("CacheFileOutputStream::~CacheFileOutputStream() [this=%p]", this));
68 // nsIOutputStream
69 NS_IMETHODIMP
70 CacheFileOutputStream::Close() {
71 LOG(("CacheFileOutputStream::Close() [this=%p]", this));
72 return CloseWithStatus(NS_OK);
75 NS_IMETHODIMP
76 CacheFileOutputStream::Flush() {
77 // TODO do we need to implement flush ???
78 LOG(("CacheFileOutputStream::Flush() [this=%p]", this));
79 return NS_OK;
82 NS_IMETHODIMP
83 CacheFileOutputStream::Write(const char* aBuf, uint32_t aCount,
84 uint32_t* _retval) {
85 CacheFileAutoLock lock(mFile);
87 LOG(("CacheFileOutputStream::Write() [this=%p, count=%d]", this, aCount));
89 if (mClosed) {
90 LOG(
91 ("CacheFileOutputStream::Write() - Stream is closed. [this=%p, "
92 "status=0x%08" PRIx32 "]",
93 this, static_cast<uint32_t>(mStatus)));
95 return NS_FAILED(mStatus) ? mStatus : NS_BASE_STREAM_CLOSED;
98 if (!mFile->mSkipSizeCheck &&
99 CacheObserver::EntryIsTooBig(mPos + aCount, !mFile->mMemoryOnly)) {
100 LOG(("CacheFileOutputStream::Write() - Entry is too big. [this=%p]", this));
102 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
103 return NS_ERROR_FILE_TOO_BIG;
106 // We use 64-bit offset when accessing the file, unfortunately we use 32-bit
107 // metadata offset, so we cannot handle data bigger than 4GB.
108 if (mPos + aCount > PR_UINT32_MAX) {
109 LOG(("CacheFileOutputStream::Write() - Entry's size exceeds 4GB. [this=%p]",
110 this));
112 CloseWithStatusLocked(NS_ERROR_FILE_TOO_BIG);
113 return NS_ERROR_FILE_TOO_BIG;
116 *_retval = aCount;
118 while (aCount) {
119 EnsureCorrectChunk(false);
120 if (NS_FAILED(mStatus)) {
121 return mStatus;
124 FillHole();
125 if (NS_FAILED(mStatus)) {
126 return mStatus;
129 uint32_t chunkOffset = mPos - (mPos / kChunkSize) * kChunkSize;
130 uint32_t canWrite = kChunkSize - chunkOffset;
131 uint32_t thisWrite = std::min(static_cast<uint32_t>(canWrite), aCount);
133 CacheFileChunkWriteHandle hnd =
134 mChunk->GetWriteHandle(chunkOffset + thisWrite);
135 if (!hnd.Buf()) {
136 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY);
137 return NS_ERROR_OUT_OF_MEMORY;
140 memcpy(hnd.Buf() + chunkOffset, aBuf, thisWrite);
141 hnd.UpdateDataSize(chunkOffset, thisWrite);
143 mPos += thisWrite;
144 aBuf += thisWrite;
145 aCount -= thisWrite;
148 EnsureCorrectChunk(true);
150 LOG(("CacheFileOutputStream::Write() - Wrote %d bytes [this=%p]", *_retval,
151 this));
153 return NS_OK;
156 NS_IMETHODIMP
157 CacheFileOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
158 uint32_t* _retval) {
159 LOG(
160 ("CacheFileOutputStream::WriteFrom() - NOT_IMPLEMENTED [this=%p, from=%p"
161 ", count=%d]",
162 this, aFromStream, aCount));
164 return NS_ERROR_NOT_IMPLEMENTED;
167 NS_IMETHODIMP
168 CacheFileOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
169 uint32_t aCount, uint32_t* _retval) {
170 LOG(
171 ("CacheFileOutputStream::WriteSegments() - NOT_IMPLEMENTED [this=%p, "
172 "count=%d]",
173 this, aCount));
175 return NS_ERROR_NOT_IMPLEMENTED;
178 NS_IMETHODIMP
179 CacheFileOutputStream::IsNonBlocking(bool* _retval) {
180 *_retval = false;
181 return NS_OK;
184 // nsIAsyncOutputStream
185 NS_IMETHODIMP
186 CacheFileOutputStream::CloseWithStatus(nsresult aStatus) {
187 CacheFileAutoLock lock(mFile);
189 LOG(("CacheFileOutputStream::CloseWithStatus() [this=%p, aStatus=0x%08" PRIx32
190 "]",
191 this, static_cast<uint32_t>(aStatus)));
193 return CloseWithStatusLocked(aStatus);
196 nsresult CacheFileOutputStream::CloseWithStatusLocked(nsresult aStatus) {
197 LOG(
198 ("CacheFileOutputStream::CloseWithStatusLocked() [this=%p, "
199 "aStatus=0x%08" PRIx32 "]",
200 this, static_cast<uint32_t>(aStatus)));
202 if (mClosed) {
203 MOZ_ASSERT(!mCallback);
204 return NS_OK;
207 mClosed = true;
208 mStatus = NS_FAILED(aStatus) ? aStatus : NS_BASE_STREAM_CLOSED;
210 if (mChunk) {
211 ReleaseChunk();
214 if (mCallback) {
215 NotifyListener();
218 mFile->RemoveOutput(this, mStatus);
220 return NS_OK;
223 NS_IMETHODIMP
224 CacheFileOutputStream::AsyncWait(nsIOutputStreamCallback* aCallback,
225 uint32_t aFlags, uint32_t aRequestedCount,
226 nsIEventTarget* aEventTarget) {
227 CacheFileAutoLock lock(mFile);
229 LOG(
230 ("CacheFileOutputStream::AsyncWait() [this=%p, callback=%p, flags=%d, "
231 "requestedCount=%d, eventTarget=%p]",
232 this, aCallback, aFlags, aRequestedCount, aEventTarget));
234 mCallback = aCallback;
235 mCallbackFlags = aFlags;
236 mCallbackTarget = aEventTarget;
238 if (!mCallback) return NS_OK;
240 // The stream is blocking so it is writable at any time
241 if (mClosed || !(aFlags & WAIT_CLOSURE_ONLY)) NotifyListener();
243 return NS_OK;
246 // nsISeekableStream
247 NS_IMETHODIMP
248 CacheFileOutputStream::Seek(int32_t whence, int64_t offset) {
249 CacheFileAutoLock lock(mFile);
251 LOG(("CacheFileOutputStream::Seek() [this=%p, whence=%d, offset=%" PRId64 "]",
252 this, whence, offset));
254 if (mClosed) {
255 LOG(("CacheFileOutputStream::Seek() - Stream is closed. [this=%p]", this));
256 return NS_BASE_STREAM_CLOSED;
259 int64_t newPos = offset;
260 switch (whence) {
261 case NS_SEEK_SET:
262 if (mAlternativeData) {
263 newPos += mFile->mAltDataOffset;
265 break;
266 case NS_SEEK_CUR:
267 newPos += mPos;
268 break;
269 case NS_SEEK_END:
270 if (mAlternativeData) {
271 newPos += mFile->mDataSize;
272 } else {
273 newPos += mFile->mAltDataOffset;
275 break;
276 default:
277 NS_ERROR("invalid whence");
278 return NS_ERROR_INVALID_ARG;
280 mPos = newPos;
281 EnsureCorrectChunk(true);
283 LOG(("CacheFileOutputStream::Seek() [this=%p, pos=%" PRId64 "]", this, mPos));
284 return NS_OK;
287 NS_IMETHODIMP
288 CacheFileOutputStream::SetEOF() {
289 MOZ_ASSERT(false, "CacheFileOutputStream::SetEOF() not implemented");
290 // Right now we don't use SetEOF(). If we ever need this method, we need
291 // to think about what to do with input streams that already points beyond
292 // new EOF.
293 return NS_ERROR_NOT_IMPLEMENTED;
296 // nsITellableStream
297 NS_IMETHODIMP
298 CacheFileOutputStream::Tell(int64_t* _retval) {
299 CacheFileAutoLock lock(mFile);
301 if (mClosed) {
302 LOG(("CacheFileOutputStream::Tell() - Stream is closed. [this=%p]", this));
303 return NS_BASE_STREAM_CLOSED;
306 *_retval = mPos;
308 if (mAlternativeData) {
309 *_retval -= mFile->mAltDataOffset;
312 LOG(("CacheFileOutputStream::Tell() [this=%p, retval=%" PRId64 "]", this,
313 *_retval));
314 return NS_OK;
317 // CacheFileChunkListener
318 nsresult CacheFileOutputStream::OnChunkRead(nsresult aResult,
319 CacheFileChunk* aChunk) {
320 MOZ_CRASH("CacheFileOutputStream::OnChunkRead should not be called!");
321 return NS_ERROR_UNEXPECTED;
324 nsresult CacheFileOutputStream::OnChunkWritten(nsresult aResult,
325 CacheFileChunk* aChunk) {
326 MOZ_CRASH("CacheFileOutputStream::OnChunkWritten should not be called!");
327 return NS_ERROR_UNEXPECTED;
330 nsresult CacheFileOutputStream::OnChunkAvailable(nsresult aResult,
331 uint32_t aChunkIdx,
332 CacheFileChunk* aChunk) {
333 MOZ_CRASH("CacheFileOutputStream::OnChunkAvailable should not be called!");
334 return NS_ERROR_UNEXPECTED;
337 nsresult CacheFileOutputStream::OnChunkUpdated(CacheFileChunk* aChunk) {
338 MOZ_CRASH("CacheFileOutputStream::OnChunkUpdated should not be called!");
339 return NS_ERROR_UNEXPECTED;
342 void CacheFileOutputStream::NotifyCloseListener() {
343 RefPtr<CacheOutputCloseListener> listener;
344 listener.swap(mCloseListener);
345 if (!listener) return;
347 listener->OnOutputClosed();
350 void CacheFileOutputStream::ReleaseChunk() {
351 LOG(("CacheFileOutputStream::ReleaseChunk() [this=%p, idx=%d]", this,
352 mChunk->Index()));
354 // If the chunk didn't write any data we need to remove hash for this chunk
355 // that was added when the chunk was created in CacheFile::GetChunkLocked.
356 if (mChunk->DataSize() == 0) {
357 // It must be due to a failure, we don't create a new chunk when we don't
358 // have data to write.
359 MOZ_ASSERT(NS_FAILED(mChunk->GetStatus()));
360 mFile->mMetadata->RemoveHash(mChunk->Index());
363 mFile->ReleaseOutsideLock(std::move(mChunk));
366 void CacheFileOutputStream::EnsureCorrectChunk(bool aReleaseOnly) {
367 mFile->AssertOwnsLock();
369 LOG(("CacheFileOutputStream::EnsureCorrectChunk() [this=%p, releaseOnly=%d]",
370 this, aReleaseOnly));
372 uint32_t chunkIdx = mPos / kChunkSize;
374 if (mChunk) {
375 if (mChunk->Index() == chunkIdx) {
376 // we have a correct chunk
377 LOG(
378 ("CacheFileOutputStream::EnsureCorrectChunk() - Have correct chunk "
379 "[this=%p, idx=%d]",
380 this, chunkIdx));
382 return;
384 ReleaseChunk();
387 if (aReleaseOnly) return;
389 nsresult rv;
390 rv = mFile->GetChunkLocked(chunkIdx, CacheFile::WRITER, nullptr,
391 getter_AddRefs(mChunk));
392 if (NS_FAILED(rv)) {
393 LOG(
394 ("CacheFileOutputStream::EnsureCorrectChunk() - GetChunkLocked failed. "
395 "[this=%p, idx=%d, rv=0x%08" PRIx32 "]",
396 this, chunkIdx, static_cast<uint32_t>(rv)));
397 CloseWithStatusLocked(rv);
401 void CacheFileOutputStream::FillHole() {
402 mFile->AssertOwnsLock();
404 MOZ_ASSERT(mChunk);
405 MOZ_ASSERT(mPos / kChunkSize == mChunk->Index());
407 uint32_t pos = mPos - (mPos / kChunkSize) * kChunkSize;
408 if (mChunk->DataSize() >= pos) return;
410 LOG(
411 ("CacheFileOutputStream::FillHole() - Zeroing hole in chunk %d, range "
412 "%d-%d [this=%p]",
413 mChunk->Index(), mChunk->DataSize(), pos - 1, this));
415 CacheFileChunkWriteHandle hnd = mChunk->GetWriteHandle(pos);
416 if (!hnd.Buf()) {
417 CloseWithStatusLocked(NS_ERROR_OUT_OF_MEMORY);
418 return;
421 uint32_t offset = hnd.DataSize();
422 memset(hnd.Buf() + offset, 0, pos - offset);
423 hnd.UpdateDataSize(offset, pos - offset);
426 void CacheFileOutputStream::NotifyListener() {
427 mFile->AssertOwnsLock();
429 LOG(("CacheFileOutputStream::NotifyListener() [this=%p]", this));
431 MOZ_ASSERT(mCallback);
433 if (!mCallbackTarget) {
434 mCallbackTarget = CacheFileIOManager::IOTarget();
435 if (!mCallbackTarget) {
436 LOG(
437 ("CacheFileOutputStream::NotifyListener() - Cannot get Cache I/O "
438 "thread! Using main thread for callback."));
439 mCallbackTarget = GetMainThreadEventTarget();
443 nsCOMPtr<nsIOutputStreamCallback> asyncCallback =
444 NS_NewOutputStreamReadyEvent(mCallback, mCallbackTarget);
446 mCallback = nullptr;
447 mCallbackTarget = nullptr;
449 asyncCallback->OnOutputStreamReady(this);
452 // Memory reporting
454 size_t CacheFileOutputStream::SizeOfIncludingThis(
455 mozilla::MallocSizeOf mallocSizeOf) const {
456 // Everything the stream keeps a reference to is already reported somewhere
457 // else.
458 // mFile reports itself.
459 // mChunk reported as part of CacheFile.
460 // mCloseListener is CacheEntry, already reported.
461 // mCallback is usually CacheFile or a class that is reported elsewhere.
462 return mallocSizeOf(this);
465 } // namespace net
466 } // namespace mozilla