From 84cef96000c110d6f0d8889c4f6765dee76b5081 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Tue, 2 Oct 2012 21:19:11 -0400 Subject: [PATCH] Bug 788021 - Part 1: Add nsGZFileWriter. r=bsmedberg --- xpcom/base/Makefile.in | 3 ++ xpcom/base/nsGZFileWriter.cpp | 90 ++++++++++++++++++++++++++++++++++ xpcom/base/nsGZFileWriter.h | 41 ++++++++++++++++ xpcom/base/nsIGZFileWriter.idl | 73 +++++++++++++++++++++++++++ 4 files changed, 207 insertions(+) create mode 100644 xpcom/base/nsGZFileWriter.cpp create mode 100644 xpcom/base/nsGZFileWriter.h create mode 100644 xpcom/base/nsIGZFileWriter.idl diff --git a/xpcom/base/Makefile.in b/xpcom/base/Makefile.in index c064cec35785..f338d352eca0 100644 --- a/xpcom/base/Makefile.in +++ b/xpcom/base/Makefile.in @@ -40,6 +40,7 @@ CPPSRCS = \ ClearOnShutdown.cpp \ VisualEventTracer.cpp \ nsErrorAsserts.cpp \ + nsGZFileWriter.cpp \ $(NULL) ifeq ($(OS_ARCH),Linux) @@ -64,6 +65,7 @@ EXPORTS = \ nsWeakPtr.h \ nsInterfaceRequestorAgg.h \ dmd.h \ + nsGZFileWriter.h \ $(NULL) EXPORTS_NAMESPACES = mozilla @@ -130,6 +132,7 @@ XPIDLSRCS = \ nsIUUIDGenerator.idl \ nsIMutable.idl \ nsIMemoryReporter.idl \ + nsIGZFileWriter.idl \ $(NULL) ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT)) diff --git a/xpcom/base/nsGZFileWriter.cpp b/xpcom/base/nsGZFileWriter.cpp new file mode 100644 index 000000000000..5c5968ac098e --- /dev/null +++ b/xpcom/base/nsGZFileWriter.cpp @@ -0,0 +1,90 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsGZFileWriter.h" +#include "nsIFile.h" +#include "nsString.h" +#include "zlib.h" + +#ifdef XP_WIN +#include +#define _dup dup +#else +#include +#endif + +NS_IMPL_ISUPPORTS1(nsGZFileWriter, nsIGZFileWriter) + +nsGZFileWriter::nsGZFileWriter() + : mInitialized(false) + , mFinished(false) +{} + +nsGZFileWriter::~nsGZFileWriter() +{ + if (mInitialized && !mFinished) { + Finish(); + } +} + +NS_IMETHODIMP +nsGZFileWriter::Init(nsIFile* aFile) +{ + NS_ENSURE_FALSE(mInitialized, NS_ERROR_FAILURE); + NS_ENSURE_FALSE(mFinished, NS_ERROR_FAILURE); + + // Get a FILE out of our nsIFile. Convert that into a file descriptor which + // gzip can own. Then close our FILE, leaving only gzip's fd open. + + FILE* file; + nsresult rv = aFile->OpenANSIFileDesc("w", &file); + NS_ENSURE_SUCCESS(rv, rv); + + mGZFile = gzdopen(dup(fileno(file)), "w"); + fclose(file); + + // gzdopen returns NULL on error. + NS_ENSURE_TRUE(mGZFile, NS_ERROR_FAILURE); + mInitialized = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsGZFileWriter::Write(const nsACString& aStr) +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_FALSE(mFinished, NS_ERROR_FAILURE); + + // gzwrite uses a return value of 0 to indicate failure. Otherwise, it + // returns the number of uncompressed bytes written. To ensure we can + // distinguish between success and failure, don't call gzwrite when we have 0 + // bytes to write. + if (aStr.IsEmpty()) { + return NS_OK; + } + + // gzwrite never does a short write -- that is, the return value should + // always be either 0 or aStr.Length(), and we shouldn't have to call it + // multiple times in order to get it to read the whole buffer. + int rv = gzwrite(mGZFile, aStr.BeginReading(), aStr.Length()); + NS_ENSURE_TRUE(rv == static_cast(aStr.Length()), NS_ERROR_FAILURE); + + return NS_OK; +} + +NS_IMETHODIMP +nsGZFileWriter::Finish() +{ + NS_ENSURE_TRUE(mInitialized, NS_ERROR_NOT_INITIALIZED); + NS_ENSURE_FALSE(mFinished, NS_ERROR_FAILURE); + + mFinished = true; + gzclose(mGZFile); + + // Ignore errors from gzclose; it's not like there's anything we can do about + // it, at this point! + return NS_OK; +} diff --git a/xpcom/base/nsGZFileWriter.h b/xpcom/base/nsGZFileWriter.h new file mode 100644 index 000000000000..1bb671d87a15 --- /dev/null +++ b/xpcom/base/nsGZFileWriter.h @@ -0,0 +1,41 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsIGZFileWriter.h" +#include +#include "zlib.h" + +/** + * A simple class for writing .gz files. + */ +class nsGZFileWriter : public nsIGZFileWriter +{ +public: + nsGZFileWriter(); + virtual ~nsGZFileWriter(); + + NS_DECL_ISUPPORTS + NS_DECL_NSIGZFILEWRITER + + /** + * nsIGZFileWriter exposes two non-virtual overloads of Write(). We + * duplicate them here so that you can call these overloads on a pointer to + * the concrete nsGZFileWriter class. + */ + nsresult Write(const char* aStr) + { + return nsIGZFileWriter::Write(aStr); + } + + nsresult Write(const char* aStr, uint32_t aLen) + { + return nsIGZFileWriter::Write(aStr, aLen); + } + +private: + bool mInitialized; + bool mFinished; + gzFile mGZFile; +}; diff --git a/xpcom/base/nsIGZFileWriter.idl b/xpcom/base/nsIGZFileWriter.idl new file mode 100644 index 000000000000..ba4af4e6060a --- /dev/null +++ b/xpcom/base/nsIGZFileWriter.idl @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 50; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "nsISupports.idl" + +%{C++ +#include "nsDependentString.h" +%} + +interface nsIFile; + +/** + * A simple interface for writing to a .gz file. + * + * Note that the file that this interface produces has a different format than + * what you'd get if you compressed your data as a gzip stream and dumped the + * result to a file. + * + * The standard gunzip tool cannot decompress a raw gzip stream, but can handle + * the files produced by this interface. + */ +[scriptable, uuid(a256f26a-c603-459e-b5a4-53b4877f2cd8)] +interface nsIGZFileWriter : nsISupports +{ + /** + * Initialize this object. We'll write our gzip'ed data to the given file, + * overwriting its contents if the file exists. + * + * init() will return an error if called twice. It's an error to call any + * other method on this interface without first calling init(). + */ + void init(in nsIFile file); + + /** + * Write the given string to the file. + */ + void write(in AUTF8String str); + + /* + * The following two overloads of Write() are C++ because we can't overload + * methods in XPIDL. Anyway, they don't add much functionality for JS + * callers. + */ + %{C++ + /** + * Write the given char* to the file (not including the null-terminator). + */ + nsresult Write(const char* str) + { + return Write(str, strlen(str)); + } + + /** + * Write |length| bytes of |str| to the file. + */ + nsresult Write(const char* str, uint32_t len) + { + return Write(nsDependentCString(str, len)); + } + %} + + /** + * Close this nsIGZFileWriter. This method is run when the underlying object + * is destroyed, so it's not strictly necessary to explicitly call it from + * your code. + * + * It's an error to call this method twice, and it's an error to call write() + * after finish() has been called. + */ + void finish(); +};