summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/src/nsMsgCompressOStream.cpp
blob: fd490274ca8d1611dc552b06619a516a08546cd0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/* 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<char[]>(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;
}

NS_IMETHODIMP nsMsgCompressOStream::StreamStatus() {
  return m_oStream->StreamStatus();
}