Bug 1910362 - Create new Nimbus helper r=aaronmt,ohorvath
[gecko.git] / xpcom / base / nsGZFileWriter.cpp
blobe688d640d061b9eadbb0d18e76972b54e7d11429
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsGZFileWriter.h"
8 #include "nsIFile.h"
9 #include "nsString.h"
10 #include "zlib.h"
11 #include "mozilla/ScopeExit.h"
13 #ifdef XP_WIN
14 # include <io.h>
15 # define _dup dup
16 #else
17 # include <unistd.h>
18 #endif
20 nsGZFileWriter::nsGZFileWriter()
21 : mInitialized(false), mFinished(false), mGZFile(nullptr) {
22 mZStream.avail_out = sizeof(mBuffer);
23 mZStream.next_out = mBuffer;
26 nsGZFileWriter::~nsGZFileWriter() {
27 if (mInitialized && !mFinished) {
28 Finish();
32 nsresult nsGZFileWriter::Init(nsIFile* aFile) {
33 if (NS_WARN_IF(mInitialized) || NS_WARN_IF(mFinished)) {
34 return NS_ERROR_FAILURE;
37 // Get a FILE out of our nsIFile. Convert that into a file descriptor which
38 // gzip can own. Then close our FILE, leaving only gzip's fd open.
40 FILE* file;
41 nsresult rv = aFile->OpenANSIFileDesc("wb", &file);
42 if (NS_WARN_IF(NS_FAILED(rv))) {
43 return rv;
45 return InitANSIFileDesc(file);
48 nsresult nsGZFileWriter::InitANSIFileDesc(FILE* aFile) {
49 if (NS_WARN_IF(mInitialized) || NS_WARN_IF(mFinished)) {
50 return NS_ERROR_FAILURE;
53 int err = deflateInit2(&mZStream, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
54 MAX_WBITS + /* gzip encoding */ 16,
55 /* DEF_MEM_LEVEL */ 8, Z_DEFAULT_STRATEGY);
56 if (err != Z_OK) {
57 return NS_ERROR_FAILURE;
59 mGZFile = aFile;
60 mInitialized = true;
62 return NS_OK;
65 nsresult nsGZFileWriter::Write(const nsACString& aStr) {
66 if (NS_WARN_IF(!mInitialized) || NS_WARN_IF(mFinished)) {
67 return NS_ERROR_FAILURE;
70 // gzwrite uses a return value of 0 to indicate failure. Otherwise, it
71 // returns the number of uncompressed bytes written. To ensure we can
72 // distinguish between success and failure, don't call gzwrite when we have 0
73 // bytes to write.
74 if (aStr.IsEmpty()) {
75 return NS_OK;
78 mZStream.avail_in = aStr.Length();
79 mZStream.next_in =
80 reinterpret_cast<Bytef*>(const_cast<char*>(aStr.BeginReading()));
82 auto cleanup = mozilla::MakeScopeExit([&] {
83 mZStream.avail_in = 0;
84 mZStream.next_in = nullptr;
85 });
86 auto onerror = mozilla::MakeScopeExit([&] {
87 mFinished = true;
88 fclose(mGZFile);
89 });
91 do {
92 if (mZStream.avail_out == 0) {
93 if (fwrite(mBuffer, 1, sizeof(mBuffer), mGZFile) != sizeof(mBuffer)) {
94 return NS_ERROR_FAILURE;
96 mZStream.avail_out = sizeof(mBuffer);
97 mZStream.next_out = mBuffer;
99 int err = deflate(&mZStream, Z_NO_FLUSH);
100 if (err == Z_STREAM_ERROR) {
101 return NS_ERROR_FAILURE;
103 } while (mZStream.avail_in);
105 onerror.release();
106 return NS_OK;
109 nsresult nsGZFileWriter::Finish() {
110 if (NS_WARN_IF(!mInitialized) || NS_WARN_IF(mFinished)) {
111 return NS_ERROR_FAILURE;
114 mZStream.avail_in = 0;
115 mZStream.next_in = nullptr;
117 auto cleanup = mozilla::MakeScopeExit([&] {
118 mFinished = true;
119 fclose(mGZFile);
122 int err;
123 do {
124 err = deflate(&mZStream, Z_FINISH);
125 if (err == Z_STREAM_ERROR) {
126 return NS_ERROR_FAILURE;
128 size_t length = sizeof(mBuffer) - mZStream.avail_out;
129 if (fwrite(mBuffer, 1, length, mGZFile) != length) {
130 return NS_ERROR_FAILURE;
132 mZStream.avail_out = sizeof(mBuffer);
133 mZStream.next_out = mBuffer;
134 } while (err != Z_STREAM_END);
136 // Ignore errors from fclose; it's not like there's anything we can do about
137 // it, at this point!
138 return NS_OK;