Bug 788021 - Part 1: Add nsGZFileWriter. r=bsmedberg
authorJustin Lebar <justin.lebar@gmail.com>
Tue, 02 Oct 2012 21:19:11 -0400
changeset 115272 bab50a5494bbeb876221eed7a93f5a166130f4ea
parent 115271 a7e4753e3c3c29151f28696ecbd445bfab1568c2
child 115273 deb340fb0b07cd36090f245080ada96d76fee869
push id1708
push userakeybl@mozilla.com
push dateMon, 19 Nov 2012 21:10:21 +0000
treeherdermozilla-beta@27b14fe50103 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbsmedberg
bugs788021
milestone18.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 788021 - Part 1: Add nsGZFileWriter. r=bsmedberg
xpcom/base/Makefile.in
xpcom/base/nsGZFileWriter.cpp
xpcom/base/nsGZFileWriter.h
xpcom/base/nsIGZFileWriter.idl
--- a/xpcom/base/Makefile.in
+++ b/xpcom/base/Makefile.in
@@ -35,16 +35,17 @@ CPPSRCS		= \
 		nsUUIDGenerator.cpp \
 		nsSystemInfo.cpp \
 		nsCycleCollector.cpp \
 		nsStackWalk.cpp \
 		nsMemoryReporterManager.cpp \
 		ClearOnShutdown.cpp \
 		VisualEventTracer.cpp \
 		nsErrorAsserts.cpp \
+		nsGZFileWriter.cpp \
 		$(NULL)
 
 ifeq ($(OS_ARCH),Linux)
 CPPSRCS += MapsMemoryReporter.cpp
 endif
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 CPPSRCS		+= nsMacUtilsImpl.cpp
@@ -59,16 +60,17 @@ EXPORTS		= \
 		nsIID.h \
 		nsISizeOf.h \
 		nsISupportsObsolete.h \
 		nsStackWalk.h \
 		nsTraceRefcntImpl.h \
 		nsWeakPtr.h \
 		nsInterfaceRequestorAgg.h \
 		dmd.h \
+		nsGZFileWriter.h \
 		$(NULL)
 
 EXPORTS_NAMESPACES = mozilla
 
 EXPORTS_mozilla = \
 	MapsMemoryReporter.h \
 	ClearOnShutdown.h \
 	StaticPtr.h \
@@ -125,16 +127,17 @@ XPIDLSRCS	= \
 		nsIDebug2.idl \
 		nsIErrorService.idl \
 		nsIException.idl \
 		nsIExceptionService.idl \
 		nsIVersionComparator.idl \
 		nsIUUIDGenerator.idl \
 		nsIMutable.idl \
 		nsIMemoryReporter.idl \
+		nsIGZFileWriter.idl \
 		$(NULL)
 
 ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
 XPIDLSRCS	+= nsIMacUtils.idl
 endif
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
new file mode 100644
--- /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 <io.h>
+#define _dup dup
+#else
+#include <unistd.h>
+#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<int>(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;
+}
new file mode 100644
--- /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 <stdio.h>
+#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;
+};
new file mode 100644
--- /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();
+};