mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-24 13:21:05 +00:00
Bug 1791725 - Add FixedBufferOutputStream; r=xpcom-reviewers,nika
Differential Revision: https://phabricator.services.mozilla.com/D157790
This commit is contained in:
parent
b70caf6522
commit
8747eec0d4
155
xpcom/io/FixedBufferOutputStream.cpp
Normal file
155
xpcom/io/FixedBufferOutputStream.cpp
Normal file
@ -0,0 +1,155 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "FixedBufferOutputStream.h"
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "mozilla/StreamBufferSinkImpl.h"
|
||||
#include "nsStreamUtils.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
FixedBufferOutputStream::FixedBufferOutputStream(
|
||||
UniquePtr<StreamBufferSink>&& aSink)
|
||||
: mSink(std::move(aSink)),
|
||||
mMutex("FixedBufferOutputStream::mMutex"),
|
||||
mOffset(0),
|
||||
mWriting(false),
|
||||
mClosed(false) {}
|
||||
|
||||
// static
|
||||
RefPtr<FixedBufferOutputStream> FixedBufferOutputStream::Create(
|
||||
size_t aLength) {
|
||||
MOZ_ASSERT(aLength);
|
||||
|
||||
return MakeRefPtr<FixedBufferOutputStream>(MakeUnique<BufferSink>(aLength));
|
||||
}
|
||||
|
||||
// static
|
||||
RefPtr<FixedBufferOutputStream> FixedBufferOutputStream::Create(
|
||||
size_t aLength, const mozilla::fallible_t&) {
|
||||
MOZ_ASSERT(aLength);
|
||||
|
||||
auto sink = BufferSink::Alloc(aLength);
|
||||
|
||||
if (NS_WARN_IF(!sink)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeRefPtr<FixedBufferOutputStream>(std::move(sink));
|
||||
}
|
||||
|
||||
// static
|
||||
RefPtr<FixedBufferOutputStream> FixedBufferOutputStream::Create(
|
||||
mozilla::Span<char> aBuffer) {
|
||||
return MakeRefPtr<FixedBufferOutputStream>(
|
||||
MakeUnique<nsBorrowedSink>(aBuffer));
|
||||
}
|
||||
|
||||
// static
|
||||
RefPtr<FixedBufferOutputStream> FixedBufferOutputStream::Create(
|
||||
UniquePtr<StreamBufferSink>&& aSink) {
|
||||
return MakeRefPtr<FixedBufferOutputStream>(std::move(aSink));
|
||||
}
|
||||
|
||||
nsDependentCSubstring FixedBufferOutputStream::WrittenData() {
|
||||
MutexAutoLock autoLock(mMutex);
|
||||
|
||||
return mSink->Slice(mOffset);
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(FixedBufferOutputStream, nsIOutputStream)
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::Close() {
|
||||
MutexAutoLock autoLock(mMutex);
|
||||
|
||||
if (mWriting) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
mClosed = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::Flush() { return NS_OK; }
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::Write(const char* aBuf, uint32_t aCount,
|
||||
uint32_t* _retval) {
|
||||
return WriteSegments(NS_CopyBufferToSegment, const_cast<char*>(aBuf), aCount,
|
||||
_retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::WriteFrom(nsIInputStream* aFromStream, uint32_t aCount,
|
||||
uint32_t* _retval) {
|
||||
return WriteSegments(NS_CopyStreamToSegment, aFromStream, aCount, _retval);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::WriteSegments(nsReadSegmentFun aReader, void* aClosure,
|
||||
uint32_t aCount, uint32_t* _retval) {
|
||||
MOZ_ASSERT(_retval);
|
||||
|
||||
MutexAutoLock autoLock(mMutex);
|
||||
|
||||
if (mWriting) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
if (mClosed) {
|
||||
return NS_BASE_STREAM_CLOSED;
|
||||
}
|
||||
|
||||
size_t length = mSink->Data().Length();
|
||||
size_t offset = mOffset;
|
||||
|
||||
MOZ_ASSERT(length >= offset, "Bad stream state!");
|
||||
|
||||
size_t maxCount = length - offset;
|
||||
if (maxCount == 0) {
|
||||
*_retval = 0;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (aCount > maxCount) {
|
||||
aCount = maxCount;
|
||||
}
|
||||
|
||||
mWriting = true;
|
||||
|
||||
nsresult rv;
|
||||
|
||||
{
|
||||
MutexAutoUnlock autoUnlock(mMutex);
|
||||
|
||||
rv = aReader(this, aClosure, mSink->Data().Elements() + offset, 0, aCount,
|
||||
_retval);
|
||||
}
|
||||
|
||||
mWriting = false;
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(*_retval <= aCount,
|
||||
"Reader should not read more than we asked it to read!");
|
||||
mOffset += *_retval;
|
||||
}
|
||||
|
||||
// As defined in nsIOutputStream.idl, do not pass reader func errors.
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
FixedBufferOutputStream::IsNonBlocking(bool* _retval) {
|
||||
*_retval = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
78
xpcom/io/FixedBufferOutputStream.h
Normal file
78
xpcom/io/FixedBufferOutputStream.h
Normal file
@ -0,0 +1,78 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_FixedBufferOutputStream_h
|
||||
#define mozilla_FixedBufferOutputStream_h
|
||||
|
||||
#include <cstddef>
|
||||
#include "mozilla/fallible.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/Span.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
template <class T>
|
||||
class RefPtr;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class StreamBufferSink;
|
||||
|
||||
// An output stream so you can read your potentially-async input stream into
|
||||
// a contiguous buffer using NS_AsyncCopy. Back when streams were more
|
||||
// synchronous and people didn't know blocking I/O was bad, if you wanted to
|
||||
// read a stream into a flat buffer, you could use NS_ReadInputStreamToString
|
||||
// or NS_ReadInputStreamToBuffer. But those don't work with async streams.
|
||||
// This can be used to replace hand-rolled Read/AsyncWait() loops. Because you
|
||||
// specify the expected size up front, the buffer is pre-allocated so wasteful
|
||||
// reallocations can be avoided.
|
||||
class FixedBufferOutputStream final : public nsIOutputStream {
|
||||
template <typename T, typename... Args>
|
||||
friend RefPtr<T> MakeRefPtr(Args&&... aArgs);
|
||||
|
||||
public:
|
||||
// Factory method to get a FixedBufferOutputStream by allocating a char buffer
|
||||
// with the given length.
|
||||
static RefPtr<FixedBufferOutputStream> Create(size_t aLength);
|
||||
|
||||
// Factory method to get a FixedBufferOutputStream by allocating a char buffer
|
||||
// with the given length, fallible version.
|
||||
static RefPtr<FixedBufferOutputStream> Create(size_t aLength,
|
||||
const mozilla::fallible_t&);
|
||||
|
||||
// Factory method to get a FixedBufferOutputStream from a preallocated char
|
||||
// buffer. The output stream doesn't take ownership of the char buffer, so the
|
||||
// char buffer must outlive the output stream (to avoid a use-after-free).
|
||||
static RefPtr<FixedBufferOutputStream> Create(mozilla::Span<char> aBuffer);
|
||||
|
||||
// Factory method to get a FixedBufferOutputStream from an arbitrary
|
||||
// StreamBufferSink.
|
||||
static RefPtr<FixedBufferOutputStream> Create(
|
||||
UniquePtr<StreamBufferSink>&& aSink);
|
||||
|
||||
nsDependentCSubstring WrittenData();
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOUTPUTSTREAM
|
||||
|
||||
private:
|
||||
explicit FixedBufferOutputStream(UniquePtr<StreamBufferSink>&& aSink);
|
||||
|
||||
~FixedBufferOutputStream() = default;
|
||||
|
||||
const UniquePtr<StreamBufferSink> mSink;
|
||||
|
||||
Mutex mMutex;
|
||||
|
||||
size_t mOffset MOZ_GUARDED_BY(mMutex);
|
||||
bool mWriting MOZ_GUARDED_BY(mMutex);
|
||||
bool mClosed MOZ_GUARDED_BY(mMutex);
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_FixedBufferOutputStream_h
|
29
xpcom/io/StreamBufferSink.h
Normal file
29
xpcom/io/StreamBufferSink.h
Normal file
@ -0,0 +1,29 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_StreamBufferSink_h
|
||||
#define mozilla_StreamBufferSink_h
|
||||
|
||||
#include <cstddef>
|
||||
#include "mozilla/Span.h"
|
||||
#include "nsString.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class StreamBufferSink {
|
||||
public:
|
||||
virtual mozilla::Span<char> Data() = 0;
|
||||
|
||||
nsDependentCSubstring Slice(size_t aOffset) {
|
||||
return nsDependentCSubstring(Data().First(aOffset));
|
||||
}
|
||||
|
||||
virtual ~StreamBufferSink() = default;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_StreamBufferSink_h
|
49
xpcom/io/StreamBufferSinkImpl.h
Normal file
49
xpcom/io/StreamBufferSinkImpl.h
Normal file
@ -0,0 +1,49 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_StreamBufferSinkImpl_h
|
||||
#define mozilla_StreamBufferSinkImpl_h
|
||||
|
||||
#include "mozilla/Buffer.h"
|
||||
#include "mozilla/StreamBufferSink.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class BufferSink final : public StreamBufferSink {
|
||||
public:
|
||||
explicit BufferSink(Buffer<char>&& aBuffer) : mBuffer(std::move(aBuffer)) {}
|
||||
|
||||
explicit BufferSink(size_t aLength) : mBuffer(aLength) {}
|
||||
|
||||
static UniquePtr<BufferSink> Alloc(size_t aLength) {
|
||||
auto maybeBuffer = Buffer<char>::Alloc(aLength);
|
||||
if (!maybeBuffer) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return MakeUnique<BufferSink>(maybeBuffer.extract());
|
||||
}
|
||||
|
||||
mozilla::Span<char> Data() override { return mBuffer.AsWritableSpan(); }
|
||||
|
||||
private:
|
||||
Buffer<char> mBuffer;
|
||||
};
|
||||
|
||||
class nsBorrowedSink final : public StreamBufferSink {
|
||||
public:
|
||||
explicit nsBorrowedSink(mozilla::Span<char> aBuffer) : mBuffer(aBuffer) {}
|
||||
|
||||
mozilla::Span<char> Data() override { return mBuffer; }
|
||||
|
||||
private:
|
||||
mozilla::Span<char> mBuffer;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_StreamBufferSinkImpl_h
|
@ -92,6 +92,7 @@ EXPORTS += [
|
||||
EXPORTS.mozilla += [
|
||||
"Base64.h",
|
||||
"FilePreferences.h",
|
||||
"FixedBufferOutputStream.h",
|
||||
"InputStreamLengthHelper.h",
|
||||
"InputStreamLengthWrapper.h",
|
||||
"NonBlockingAsyncInputStream.h",
|
||||
@ -99,6 +100,8 @@ EXPORTS.mozilla += [
|
||||
"SnappyCompressOutputStream.h",
|
||||
"SnappyFrameUtils.h",
|
||||
"SnappyUncompressInputStream.h",
|
||||
"StreamBufferSink.h",
|
||||
"StreamBufferSinkImpl.h",
|
||||
"StreamBufferSource.h",
|
||||
]
|
||||
|
||||
@ -107,6 +110,7 @@ UNIFIED_SOURCES += [
|
||||
"crc32c.c",
|
||||
"FileDescriptorFile.cpp",
|
||||
"FilePreferences.cpp",
|
||||
"FixedBufferOutputStream.cpp",
|
||||
"InputStreamLengthHelper.cpp",
|
||||
"InputStreamLengthWrapper.cpp",
|
||||
"NonBlockingAsyncInputStream.cpp",
|
||||
|
Loading…
Reference in New Issue
Block a user