diff options
Diffstat (limited to 'modules/libjar/zipwriter/nsDeflateConverter.cpp')
-rw-r--r-- | modules/libjar/zipwriter/nsDeflateConverter.cpp | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/modules/libjar/zipwriter/nsDeflateConverter.cpp b/modules/libjar/zipwriter/nsDeflateConverter.cpp new file mode 100644 index 0000000000..32dc3c7ebf --- /dev/null +++ b/modules/libjar/zipwriter/nsDeflateConverter.cpp @@ -0,0 +1,178 @@ +/* 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 "StreamFunctions.h" +#include "nsDeflateConverter.h" +#include "nsStringStream.h" +#include "nsComponentManagerUtils.h" +#include "nsMemory.h" +#include "plstr.h" +#include "mozilla/UniquePtr.h" + +#define ZLIB_TYPE "deflate" +#define GZIP_TYPE "gzip" +#define X_GZIP_TYPE "x-gzip" + +using namespace mozilla; + +/** + * nsDeflateConverter is a stream converter applies the deflate compression + * method to the data. + */ +NS_IMPL_ISUPPORTS(nsDeflateConverter, nsIStreamConverter, nsIStreamListener, + nsIRequestObserver) + +nsresult nsDeflateConverter::Init() { + int zerr; + + mOffset = 0; + + mZstream.zalloc = Z_NULL; + mZstream.zfree = Z_NULL; + mZstream.opaque = Z_NULL; + + int32_t window = MAX_WBITS; + switch (mWrapMode) { + case WRAP_NONE: + window = -window; + break; + case WRAP_GZIP: + window += 16; + break; + default: + break; + } + + zerr = deflateInit2(&mZstream, mLevel, Z_DEFLATED, window, 8, + Z_DEFAULT_STRATEGY); + if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY; + + mZstream.next_out = mWriteBuffer; + mZstream.avail_out = sizeof(mWriteBuffer); + + // mark the input buffer as empty. + mZstream.avail_in = 0; + mZstream.next_in = Z_NULL; + + return NS_OK; +} + +NS_IMETHODIMP nsDeflateConverter::Convert(nsIInputStream* aFromStream, + const char* aFromType, + const char* aToType, + nsISupports* aCtxt, + nsIInputStream** _retval) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsDeflateConverter::AsyncConvertData(const char* aFromType, + const char* aToType, + nsIStreamListener* aListener, + nsISupports* aCtxt) { + if (mListener) return NS_ERROR_ALREADY_INITIALIZED; + + NS_ENSURE_ARG_POINTER(aListener); + + if (!PL_strncasecmp(aToType, ZLIB_TYPE, sizeof(ZLIB_TYPE) - 1)) + mWrapMode = WRAP_ZLIB; + else if (!PL_strcasecmp(aToType, GZIP_TYPE) || + !PL_strcasecmp(aToType, X_GZIP_TYPE)) + mWrapMode = WRAP_GZIP; + else + mWrapMode = WRAP_NONE; + + nsresult rv = Init(); + NS_ENSURE_SUCCESS(rv, rv); + + mListener = aListener; + mContext = aCtxt; + return rv; +} + +NS_IMETHODIMP +nsDeflateConverter::GetConvertedType(const nsACString& aFromType, + nsIChannel* aChannel, + nsACString& aToType) { + return NS_ERROR_NOT_IMPLEMENTED; +} + +NS_IMETHODIMP nsDeflateConverter::OnDataAvailable(nsIRequest* aRequest, + nsIInputStream* aInputStream, + uint64_t aOffset, + uint32_t aCount) { + if (!mListener) return NS_ERROR_NOT_INITIALIZED; + + auto buffer = MakeUnique<char[]>(aCount); + NS_ENSURE_TRUE(buffer, NS_ERROR_OUT_OF_MEMORY); + + nsresult rv = ZW_ReadData(aInputStream, buffer.get(), aCount); + NS_ENSURE_SUCCESS(rv, rv); + + // make sure we aren't reading too much + mZstream.avail_in = aCount; + mZstream.next_in = (unsigned char*)buffer.get(); + + int zerr = Z_OK; + // deflate loop + while (mZstream.avail_in > 0 && zerr == Z_OK) { + zerr = deflate(&mZstream, Z_NO_FLUSH); + + while (mZstream.avail_out == 0) { + // buffer is full, push the data out to the listener + rv = PushAvailableData(aRequest, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + zerr = deflate(&mZstream, Z_NO_FLUSH); + } + } + + return NS_OK; +} + +NS_IMETHODIMP nsDeflateConverter::OnStartRequest(nsIRequest* aRequest) { + if (!mListener) return NS_ERROR_NOT_INITIALIZED; + + return mListener->OnStartRequest(aRequest); +} + +NS_IMETHODIMP nsDeflateConverter::OnStopRequest(nsIRequest* aRequest, + nsresult aStatusCode) { + if (!mListener) return NS_ERROR_NOT_INITIALIZED; + + nsresult rv; + + int zerr; + do { + zerr = deflate(&mZstream, Z_FINISH); + rv = PushAvailableData(aRequest, nullptr); + NS_ENSURE_SUCCESS(rv, rv); + } while (zerr == Z_OK); + + deflateEnd(&mZstream); + + return mListener->OnStopRequest(aRequest, aStatusCode); +} + +nsresult nsDeflateConverter::PushAvailableData(nsIRequest* aRequest, + nsISupports* aContext) { + uint32_t bytesToWrite = sizeof(mWriteBuffer) - mZstream.avail_out; + // We don't need to do anything if there isn't any data + if (bytesToWrite == 0) return NS_OK; + + MOZ_ASSERT(bytesToWrite <= INT32_MAX); + nsCOMPtr<nsIInputStream> stream; + nsresult rv = NS_NewByteInputStream(getter_AddRefs(stream), + Span((char*)mWriteBuffer, bytesToWrite), + NS_ASSIGNMENT_DEPEND); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mListener->OnDataAvailable(aRequest, stream, mOffset, bytesToWrite); + + // now set the state for 'deflate' + mZstream.next_out = mWriteBuffer; + mZstream.avail_out = sizeof(mWriteBuffer); + + mOffset += bytesToWrite; + return rv; +} |