From 2e122e150928dc7cc52a77a3ebec0427146cd2af Mon Sep 17 00:00:00 2001
From: Max Kellermann <max@duempel.org>
Date: Mon, 11 Aug 2014 21:30:49 +0200
Subject: [PATCH] db/simple: compress the database file using gzip

---
 NEWS                                          |  1 +
 doc/user.xml                                  | 12 ++++
 .../plugins/simple/SimpleDatabasePlugin.cxx   | 58 +++++++++++++++++--
 .../plugins/simple/SimpleDatabasePlugin.hxx   |  7 ++-
 4 files changed, 73 insertions(+), 5 deletions(-)

diff --git a/NEWS b/NEWS
index a582225f5..2672c84d5 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,7 @@ ver 0.19 (not yet released)
   - proxy: forward "idle" events
   - proxy: forward the "update" command
   - proxy: copy "Last-Modified" from remote directories
+  - simple: compress the database file using gzip
   - upnp: new plugin
   - cancel the update on shutdown
 * storage
diff --git a/doc/user.xml b/doc/user.xml
index 1b399c2f4..d70c9ead1 100644
--- a/doc/user.xml
+++ b/doc/user.xml
@@ -925,6 +925,18 @@ systemctl start mpd.socket</programlisting>
                   The path of the database file.
                 </entry>
               </row>
+
+              <row>
+                <entry>
+                  <varname>compress</varname>
+                  <parameter>yes|no</parameter>
+                </entry>
+                <entry>
+                  Compress the database file using
+                  <filename>gzip</filename>?  Enabled by default (if
+                  built with <filename>zlib</filename>).
+                </entry>
+              </row>
             </tbody>
           </tgroup>
         </informaltable>
diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.cxx b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
index 9e750996c..3048194cd 100644
--- a/src/db/plugins/simple/SimpleDatabasePlugin.cxx
+++ b/src/db/plugins/simple/SimpleDatabasePlugin.cxx
@@ -34,6 +34,7 @@
 #include "fs/io/TextFile.hxx"
 #include "fs/io/BufferedOutputStream.hxx"
 #include "fs/io/FileOutputStream.hxx"
+#include "fs/io/GzipOutputStream.hxx"
 #include "config/ConfigData.hxx"
 #include "fs/FileSystem.hxx"
 #include "util/CharUtil.hxx"
@@ -48,13 +49,23 @@ static constexpr Domain simple_db_domain("simple_db");
 inline SimpleDatabase::SimpleDatabase()
 	:Database(simple_db_plugin),
 	 path(AllocatedPath::Null()),
+#ifdef HAVE_ZLIB
+	 compress(true),
+#endif
 	 cache_path(AllocatedPath::Null()),
 	 prefixed_light_song(nullptr) {}
 
-inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path)
+inline SimpleDatabase::SimpleDatabase(AllocatedPath &&_path,
+#ifndef HAVE_ZLIB
+				      gcc_unused
+#endif
+				      bool _compress)
 	:Database(simple_db_plugin),
 	 path(std::move(_path)),
 	 path_utf8(path.ToUTF8()),
+#ifdef HAVE_ZLIB
+	 compress(_compress),
+#endif
 	 cache_path(AllocatedPath::Null()),
 	 prefixed_light_song(nullptr) {
 }
@@ -90,6 +101,10 @@ SimpleDatabase::Configure(const config_param &param, Error &error)
 	if (path.IsNull() && error.IsDefined())
 		return false;
 
+#ifdef HAVE_ZLIB
+	compress = param.GetBlockValue("compress", compress);
+#endif
+
 	return true;
 }
 
@@ -369,11 +384,42 @@ SimpleDatabase::Save(Error &error)
 	if (!fos.IsDefined())
 		return false;
 
-	BufferedOutputStream bos(fos);
+	OutputStream *os = &fos;
+
+#ifdef HAVE_ZLIB
+	GzipOutputStream *gzip = nullptr;
+	if (compress) {
+		gzip = new GzipOutputStream(*os, error);
+		if (!gzip->IsDefined()) {
+			delete gzip;
+			return false;
+		}
+
+		os = gzip;
+	}
+#endif
+
+	BufferedOutputStream bos(*os);
 
 	db_save_internal(bos, *root);
 
-	if (!bos.Flush(error) || !fos.Commit(error))
+	if (!bos.Flush(error)) {
+#ifdef HAVE_ZLIB
+		delete gzip;
+#endif
+		return false;
+	}
+
+#ifdef HAVE_ZLIB
+	if (gzip != nullptr) {
+		bool success = gzip->Flush(error);
+		delete gzip;
+		if (!success)
+			return false;
+	}
+#endif
+
+	if (!fos.Commit(error))
 		return false;
 
 	struct stat st;
@@ -435,8 +481,12 @@ SimpleDatabase::Mount(const char *local_uri, const char *storage_uri,
 	std::string name(storage_uri);
 	std::replace_if(name.begin(), name.end(), IsUnsafeChar, '_');
 
+#ifndef HAVE_ZLIB
+	constexpr bool compress = false;
+#endif
 	auto db = new SimpleDatabase(AllocatedPath::Build(cache_path,
-							  name.c_str()));
+							  name.c_str()),
+				     compress);
 	if (!db->Open(error)) {
 		delete db;
 		return false;
diff --git a/src/db/plugins/simple/SimpleDatabasePlugin.hxx b/src/db/plugins/simple/SimpleDatabasePlugin.hxx
index e27b3d956..7ba71e272 100644
--- a/src/db/plugins/simple/SimpleDatabasePlugin.hxx
+++ b/src/db/plugins/simple/SimpleDatabasePlugin.hxx
@@ -20,6 +20,7 @@
 #ifndef MPD_SIMPLE_DATABASE_PLUGIN_HXX
 #define MPD_SIMPLE_DATABASE_PLUGIN_HXX
 
+#include "check.h"
 #include "db/Interface.hxx"
 #include "fs/AllocatedPath.hxx"
 #include "db/LightSong.hxx"
@@ -38,6 +39,10 @@ class SimpleDatabase : public Database {
 	AllocatedPath path;
 	std::string path_utf8;
 
+#ifdef HAVE_ZLIB
+	bool compress;
+#endif
+
 	/**
 	 * The path where cache files for Mount() are located.
 	 */
@@ -64,7 +69,7 @@ class SimpleDatabase : public Database {
 
 	SimpleDatabase();
 
-	SimpleDatabase(AllocatedPath &&_path);
+	SimpleDatabase(AllocatedPath &&_path, bool _compress);
 
 public:
 	static Database *Create(EventLoop &loop, DatabaseListener &listener,