summaryrefslogtreecommitdiffstats
path: root/mailnews/base/util/nsMsgCompressOStream.cpp
blob: 0a0a69549117cc3157397638fe2b0f93a5585e44 (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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
/* 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;
}