[PATCH] gzip: add support for compression levels 4-9

Aaro Koskinen aaro.koskinen at iki.fi
Fri Apr 24 17:41:35 UTC 2015


Add support for compression levels 4-9.

function                                             old     new   delta
gzip_main                                            191     306    +115
usage_messages                                     40461   40504     +43
pack_gzip                                           1924    1907     -17
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/1 up/down: 158/-17)           Total: 141 bytes

The rationale for this feature is that the current hardcoded 9 level
is painfully slow with big files. Below are some examples when
compressing a rootfs tarball (368.9M uncompressed) with different levels
(gzip 1.6 compression time for comparison in parenthesis):

		   	compression time	compressed size
Without the patch: 	1m22.534s		152.0M
With the patch, 9: 	1m15.351s (1m7.419s)	152.0M (old bb default level)
                8: 	0m46.763s (0m43.335s)	152.1M
                7: 	0m28.519s (0m27.076s)	152.4M
                6: 	0m22.960s (0m21.879s)	152.8M (gzip default level)
                5: 	0m16.058s (0m14.740s)	153.9M
                4: 	0m12.484s (0m11.167s)	156.4M

If the compression level support is enabled, we make 6 the default
as with gzip 1.6.

Signed-off-by: Aaro Koskinen <aaro.koskinen at iki.fi>
---
 archival/gzip.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 55 insertions(+), 1 deletion(-)

diff --git a/archival/gzip.c b/archival/gzip.c
index bc1f9c6..e6161e2 100644
--- a/archival/gzip.c
+++ b/archival/gzip.c
@@ -62,14 +62,26 @@ aa:      85.1% -- replaced with aa.gz
 //config:	  1: larger buffers, larger hash-tables
 //config:	  2: larger buffers, largest hash-tables
 //config:	  Larger models may give slightly better compression
+//config:
+//config:config FEATURE_GZIP_LEVELS
+//config:	bool "Enable compression levels"
+//config:	default y
+//config:	depends on GZIP
+//config:	help
+//config:	  Enable support for compression levels 4-9. The default level
+//config:	  is 6 (without this option 9). If levels 1-3 are specified, 4
+//config:	  is used.
 
 //applet:IF_GZIP(APPLET(gzip, BB_DIR_BIN, BB_SUID_DROP))
 //kbuild:lib-$(CONFIG_GZIP) += gzip.o
 
 //usage:#define gzip_trivial_usage
-//usage:       "[-cfd] [FILE]..."
+//usage:       "[-cfd" IF_FEATURE_GZIP_LEVELS("123456789") "] [FILE]..."
 //usage:#define gzip_full_usage "\n\n"
 //usage:       "Compress FILEs (or stdin)\n"
+//usage:        IF_FEATURE_GZIP_LEVELS(
+//usage:     "\n        -1..9   Compression level"
+//usage:        )
 //usage:     "\n	-d	Decompress"
 //usage:     "\n	-c	Write to stdout"
 //usage:     "\n	-f	Force"
@@ -252,6 +264,8 @@ enum {
  * input file length plus MIN_LOOKAHEAD.
  */
 
+#ifndef ENABLE_FEATURE_GZIP_LEVELS
+
 	max_chain_length = 4096,
 /* To speed up deflation, hash chains are never searched beyond this length.
  * A higher limit improves compression ratio but degrades the speed.
@@ -283,11 +297,23 @@ enum {
  * For deflate_fast() (levels <= 3) good is ignored and lazy has a different
  * meaning.
  */
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
 };
 
 
 struct globals {
 
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+	unsigned _max_chain_length;
+	unsigned _max_lazy_match;
+	unsigned _good_match;
+	unsigned _nice_match;
+#define max_chain_length (G1._max_chain_length)
+#define max_lazy_match   (G1._max_lazy_match)
+#define good_match	 (G1._good_match)
+#define nice_match	 (G1._nice_match)
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
+
 	lng block_start;
 
 /* window position at the beginning of the current output block. Gets
@@ -2161,6 +2187,22 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
 #endif
 {
 	unsigned opt;
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+	unsigned level;
+	const struct {
+		unsigned char good;
+		unsigned char chain_shift;
+		unsigned short lazy;
+		unsigned short nice;
+	} gzip_level_config[6] = {
+		{4,   4,   4,  16}, /* Level 4 */
+		{8,   5,  16,  32}, /* Level 5 */
+		{8,   7,  16, 128}, /* Level 6 */
+		{8,   8,  32, 128}, /* Level 7 */
+		{32, 10, 128, 258}, /* Level 8 */
+		{32, 12, 258, 258}, /* Level 9 */
+	};
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
 
 #if ENABLE_FEATURE_GZIP_LONG_OPTIONS
 	applet_long_options = gzip_longopts;
@@ -2171,6 +2213,11 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
 	if (opt & 0x18) // -d and/or -t
 		return gunzip_main(argc, argv);
 #endif
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+	/* Map 1..3 to 4 and make 6 a default. */
+	level = ffs(opt >> (4 + IF_GUNZIP(2)) & 0x1ff);
+	level = !level ? 6 : MAX(4, level);
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
 	option_mask32 &= 0x7; /* ignore -q, -0..9 */
 	//if (opt & 0x1) // -c
 	//if (opt & 0x2) // -f
@@ -2180,6 +2227,13 @@ int gzip_main(int argc UNUSED_PARAM, char **argv)
 	SET_PTR_TO_GLOBALS((char *)xzalloc(sizeof(struct globals)+sizeof(struct globals2))
 			+ sizeof(struct globals));
 
+#ifdef ENABLE_FEATURE_GZIP_LEVELS
+	max_chain_length = 1 << gzip_level_config[level - 4].chain_shift;
+	good_match	 = gzip_level_config[level - 4].good;
+	max_lazy_match	 = gzip_level_config[level - 4].lazy;
+	nice_match	 = gzip_level_config[level - 4].nice;
+#endif /* ENABLE_FEATURE_GZIP_LEVELS */
+
 	/* Allocate all global buffers (for DYN_ALLOC option) */
 	ALLOC(uch, G1.l_buf, INBUFSIZ);
 	ALLOC(uch, G1.outbuf, OUTBUFSIZ);
-- 
2.3.3



More information about the busybox mailing list