/* 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 "nsMsgCompressOStream.h" #include "prio.h" #include "prmem.h" #define BUFFER_SIZE 16384 nsMsgCompressOStream::nsMsgCompressOStream() : m_zbuf(nullptr) { } nsMsgCompressOStream::~nsMsgCompressOStream() { Close(); } NS_IMPL_ISUPPORTS(nsMsgCompressOStream, nsIOutputStream) nsresult nsMsgCompressOStream::InitOutputStream(nsIOutputStream *rawStream) { // protect against repeat calls if (m_oStream) return NS_ERROR_UNEXPECTED; // allocate some memory for a buffer m_zbuf = mozilla::MakeUnique(BUFFER_SIZE); if (!m_zbuf) return NS_ERROR_OUT_OF_MEMORY; // set up the zlib object m_zstream.zalloc = Z_NULL; m_zstream.zfree = Z_NULL; m_zstream.opaque = Z_NULL; // http://zlib.net/manual.html is rather silent on the topic, but // perl's Compress::Raw::Zlib manual says: // -WindowBits [...] // To compress an RFC 1951 data stream, set WindowBits to -MAX_WBITS. if (deflateInit2(&m_zstream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, -MAX_WBITS, MAX_MEM_LEVEL, Z_DEFAULT_STRATEGY) != Z_OK) return NS_ERROR_FAILURE; m_oStream = rawStream; return NS_OK; } /* void close (); */ NS_IMETHODIMP nsMsgCompressOStream::Close() { if (m_oStream) { m_oStream = nullptr; deflateEnd(&m_zstream); } m_zbuf = nullptr; return NS_OK; } NS_IMETHODIMP nsMsgCompressOStream::Write(const char *buf, uint32_t count, uint32_t *result) { if (!m_oStream) return NS_BASE_STREAM_CLOSED; m_zstream.next_in = (Bytef *) buf; m_zstream.avail_in = count; // keep looping until the buffer doesn't get filled do { m_zstream.next_out = (Bytef *) m_zbuf.get(); m_zstream.avail_out = BUFFER_SIZE; // Using "Z_SYNC_FLUSH" may cause excess flushes if the calling // code does a lot of small writes. An option with the IMAP // protocol is to check the buffer for "\n" at the end, but // in the interests of keeping this generic, don't optimise // yet. An alternative is to require ->Flush always, but that // is likely to break callers. int zr = deflate(&m_zstream, Z_SYNC_FLUSH); if (zr == Z_STREAM_END || zr == Z_BUF_ERROR) zr = Z_OK; // not an error for our purposes if (zr != Z_OK) return NS_ERROR_FAILURE; uint32_t out_size = BUFFER_SIZE - m_zstream.avail_out; const char *out_buf = m_zbuf.get(); // push everything in the buffer before repeating while (out_size) { uint32_t out_result; nsresult rv = m_oStream->Write(out_buf, out_size, &out_result); NS_ENSURE_SUCCESS(rv, rv); if (!out_result) return NS_BASE_STREAM_CLOSED; out_size -= out_result; out_buf += out_result; } // http://www.zlib.net/manual.html says: // If deflate returns with avail_out == 0, this function must be // called again with the same value of the flush parameter and // more output space (updated avail_out), until the flush is // complete (deflate returns with non-zero avail_out). } while (!m_zstream.avail_out); *result = count; return NS_OK; } NS_IMETHODIMP nsMsgCompressOStream::Flush(void) { if (!m_oStream) return NS_BASE_STREAM_CLOSED; return m_oStream->Flush(); } NS_IMETHODIMP nsMsgCompressOStream::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } NS_IMETHODIMP nsMsgCompressOStream::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval) { return NS_ERROR_NOT_IMPLEMENTED; } /* boolean isNonBlocking (); */ NS_IMETHODIMP nsMsgCompressOStream::IsNonBlocking(bool *aNonBlocking) { *aNonBlocking = false; return NS_OK; }