[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

JFFS2: improving compression



Dear All,

David asked me to post this letter to this list, too.

The goal of our project (called BBC, http://www.inf.u-szeged.hu/jffs2 ) 
is to improve the compression performance of JFFS2. There was an e-mail 
conversation between me and David how to insert BBC into the official 
JFFS2, and this is my last letter - with a patch.

Regards,

Ferenc

P.S.: David: I'll send the seperated patches tomorrow.

-------- Original Message --------
Subject: 	BBC patch
Date: 	Wed, 21 Apr 2004 00:04:34 +0200
From: 	Ferenc Havasi <havasi@xxxxxxx.hu>
To: 	David Woodhouse <dwmw2@xxxxxxx.org>

Hi,

I rewrote the source of BBC for JFFS2.

This is the version I called "first step" in my last letter. I does not 
support model file yet, and this is the extended version of the sources 
you sended me some weeks ago. The patch for the latest CVS version is 
attached.

It supports 3 compression mode:
- none (no compression)
- priority compression (it tries the compressors
   in order of their priorities, and chooses the
   first successful - such the original does)
- size (it tries all the compressors and chooses
   the smallest result)

Compressors:
- zlib
- rtime
- dynrubin and rubinmips
- lzo (faster than zlib but has worse compression ratio)
- lzari (it has better compression ratio than zlib but
   much slower)

The two new ones are disabled for default.

The compressor interface support is in compr.h and compr.c,
which are used both in mkfs and kernel. (and compr.h are
used by compressor modules also, to make easier to alloc
memory, print error msgs...)

The mkfs.jffs2 has 5 new switches:
   -m, --compression-mode   Select compression mode (default: priortiry)
   -L, --list-compressors   Show the list of the avaiable compressors
   -x, --disable-compressor Disable a compressor
   -X, --enable-compressor  Enable a compressor
   -t, --test-compression   Call decompress and compare with the 
original (for test)

By default compressor lzo and lzari are disabled, and the compression 
mode is priority so if someone doesn't use the new swithes, the 
generated JFFS2 image will be exactly the same than using the mkfs 
without this patch.

In the kernel the compression support, the compressors plugins and
the default compression mode can be selected by "make menuconfig"/...

I tested both in kernel 2.6.x and mkfs and it seems that it works.

Using 2.4.x kernels it compiles and works as bad as the CVS version 
without BBC patch (so it doesn't work on my system with mtdram device, 
but it is true using the non-patched CVS version, too).

A short summary of the modified files:

fs:
   Config.in           config for compressor(s)
   Kconfig             config for compressor(s)
fs/jffs2:
   Makefile.common     support new kconfig
   compr.c
   compr.h
   compr_*.c           implement compression interface
   gc.c                jffs2_compress return value (preparing for models)
   nodelist.h          some function header to compr.h
   os-linux.h          compressor config now comes from kconfig
   read.c              jffs2_decompress parameter (preparing for models)
   super-v24.c         compressor_init/deinit
   super.c             compressor_init/deinit
   write.c             jffs2_compress return value (preparing for models)
include/linux:
   jffs2.h             new JFFS2_COMPR_XXX values
patches:
   patchin.sh          2.6.x kernel config patch
util:
   Makefile            new compr_*.c, compr.c, compr.h symliks...
   compr_zlib.c        implement compression interface
   mkfs.jffs2.1        document new switches of mkfs.jffs2
   mkfs.jffs2.c        compr_init, new switches, call compr.c...

There is only one #define that should be fixed in compr.h:
the value of JFFS2_PAGE_SIZE should be the maximal value of *cdatalen.
It is clear in mkfs.jffs2.c (and that is OK) but in the kernel I don't
where it is exatly comes, so I definied it to 4096 directly. It is used 
by the size compression mode and the lzo modul. (so the default config not)

If you like this patch and it is OK for you we'll continue the work with 
2-3 steps:

Step A
- some optional proc support to make is possible to switch
   compression mode, read some statistics, enable/disable compressors
- some additional compression mode based on estimator functions

Step B
- imlement model file support, and add our compressor: ARMLIB.
   Before it we should fix the technical details how to store the
   model files.

Step C
- documentation, howtos...

Bye,
Ferenc




diff --unified --recursive --new-file mtd/fs/Config.in mtd-new-4/fs/Config.in
--- mtd/fs/Config.in	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/Config.in	2004-04-20 22:43:51.000000000 +0200
@@ -7,4 +7,17 @@
 if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
    int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
    bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND
+   bool 'JFFS2 compression support (recommended)' CONFIG_JFFS2_COMPRESSION
+   if [ "$CONFIG_JFFS2_COMPRESSION" = "y" ] ; then
+   bool '  ZLIB compression support (recommended)' CONFIG_JFFS2_ZLIB
+   bool '  RTIME compression support (recommended)' CONFIG_JFFS2_RTIME
+   bool '  RUBIN compression support' CONFIG_JFFS2_RUBIN
+   bool '  LZO compression support (experimental)' CONFIG_JFFS2_LZO
+   bool '  LZARI compression support (experimental)' CONFIG_JFFS2_LZARI
+   
+   choice 'Default JFFS2 compression mode' \
+        "none                                   CONFIG_JFFS2_CMODE_NONE \
+         priority                               CONFIG_JFFS2_CMODE_PRIORITY \
+         size                                   CONFIG_JFFS2_CMODE_SIZE" priority
+   fi
 fi
diff --unified --recursive --new-file mtd/fs/Kconfig mtd-new-4/fs/Kconfig
--- mtd/fs/Kconfig	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/Kconfig	2004-04-20 12:13:22.000000000 +0200
@@ -66,3 +66,78 @@
 
 	  Say 'N' unless you have NAND flash and you are willing to test and
 	  develop JFFS2 support for it.
+
+config JFFS2_COMPRESSION
+	bool "JFFS2 compression support"
+	depends on JFFS2_FS
+	default y
+	help
+	  Transparent compression support. Say 'Y' if unsure.
+
+config JFFS2_ZLIB
+	bool "ZLIB compression support"
+	depends on JFFS2_COMPRESSION
+	default y
+        help
+          Zlib is designed to be a free, general-purpose, legally unencumbered,
+          lossless data-compression library for use on virtually any computer 
+          hardware and operating system. See http://www.gzip.org/zlib/ for
+          further information.
+          
+          Say 'Y' if unsure.
+
+config JFFS2_RTIME
+	bool "RTIME compression support"
+	depends on JFFS2_COMPRESSION
+	default y
+        help
+          Rtime does manage to recompress already-compressed data. Say 'Y' if unsure.
+
+config JFFS2_RUBIN
+	bool "RUBIN compression support"
+	depends on JFFS2_COMPRESSION
+	default n
+        help
+          RUBINMIPS and DYNRUBIN compressors. Say 'N' if unsure.
+
+config JFFS2_LZO
+	bool "LZO compression support (EXPERIMENTAL)"
+	depends on JFFS2_COMPRESSION && EXPERIMENTAL
+	default n
+        help
+          LZO1X-1 and LZO1X-999 compression support.
+
+config JFFS2_LZARI
+	bool "LZARI compression support (EXPERIMENTAL)"
+	depends on JFFS2_COMPRESSION && EXPERIMENTAL
+	default n
+        help
+          Lempel-Ziv-Storer-Sczymanski compression support with additional arithmetic coding.
+
+choice
+        prompt "Default compression mode"
+        default JFFS2_CMODE_PRIORITY
+        depends on JFFS2_COMPRESSION
+        help
+          You can set here the default compression mode of JFFS2 from 
+          the avaiable compression modes. Don't touch if unsure.
+
+config JFFS2_CMODE_NONE
+        bool "no compression"
+        help
+          Uses no compression.
+
+config JFFS2_CMODE_PRIORITY
+        bool "priority"
+        help
+          Tries the compressors in a predefinied order and chooses the first 
+          successful one.
+
+config JFFS2_CMODE_SIZE
+        bool "size (EXPERIMENTAL)"
+        help
+          Tries all compressors and chooses the one which has the smallest 
+          result.
+
+endchoice
+
diff --unified --recursive --new-file mtd/fs/jffs2/Makefile.common mtd-new-4/fs/jffs2/Makefile.common
--- mtd/fs/jffs2/Makefile.common	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/Makefile.common	2004-04-20 12:13:22.000000000 +0200
@@ -12,8 +12,12 @@
 
 obj-$(CONFIG_JFFS2_FS) := jffs2.o
 
-COMPR_OBJS	:= compr.o compr_rubin.o compr_rtime.o compr_zlib.o
-JFFS2_OBJS	:= dir.o file.o ioctl.o nodelist.o malloc.o \
+JRUBIN_OBJS-$(CONFIG_JFFS2_RUBIN) := compr_rubin.o
+JRTIME_OBJS-$(CONFIG_JFFS2_RTIME) := compr_rtime.o
+JZLIB_OBJS-$(CONFIG_JFFS2_ZLIB)  := compr_zlib.o
+JLZO_OBJS-$(CONFIG_JFFS2_LZO)  := compr_lzo.o
+JLZARI_OBJS-$(CONFIG_JFFS2_LZARI)  := compr_lzari.o
+JFFS2_OBJS	:= compr.o dir.o file.o ioctl.o nodelist.o malloc.o \
 	read.o nodemgmt.o readinode.o write.o scan.o gc.o \
 	symlink.o build.o erase.o background.o fs.o writev.o
 
@@ -28,8 +32,7 @@
 NAND_OBJS-$(CONFIG_JFFS2_FS_NAND)	:= wbuf.o
 
 jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
-	$(LINUX_OBJS)
-
+	$(LINUX_OBJS) $(JRUBIN_OBJS-y) $(JRTIME_OBJS-y) $(JZLIB_OBJS-y) $(JLZO_OBJS-y) $(JLZARI_OBJS-y)
 
 # 2.4 build compatibility
 ifeq ($(BELOW25),y)
diff --unified --recursive --new-file mtd/fs/jffs2/compr.c mtd-new-4/fs/jffs2/compr.c
--- mtd/fs/jffs2/compr.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/compr.c	2004-04-20 22:36:51.000000000 +0200
@@ -5,29 +5,193 @@
  *
  * Created by Arjan van de Ven <arjanv@xxxxxxx.com>
  *
+ * Compressor interface by Ferenc Havasi <havasi@xxxxxxx.hu>
+ *
  * For licensing information, see the file 'LICENCE' in this directory.
  *
  * $Id: compr.c,v 1.34 2004/03/08 15:29:09 dwmw2 Exp $
  *
  */
 
-#include <linux/kernel.h>
-#include <linux/string.h>
+/* KERNEL SPACE - USER SPACE SUPPORT */
+
+#include "compr.h"
 #include <linux/errno.h>
+
+#ifdef JFFS2_KERNEL
+
 #include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
 #include <linux/slab.h>
 #include <linux/jffs2.h>
-#include "nodelist.h"
 
-int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
-int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen);
+static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
+
+#define COMPRESSOR_LIST_LOCK   spin_lock(&jffs2_compressor_list_lock)
+#define COMPRESSOR_LIST_UNLOCK spin_unlock(&jffs2_compressor_list_lock)
+
+#else
+
+#include <string.h>
+#include <stdlib.h>
+#include <linux/jffs2.h>
+
+#define COMPRESSOR_LIST_LOCK
+#define COMPRESSOR_LIST_UNLOCK
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+        struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void __list_add(struct list_head *new,
+                              struct list_head *prev,
+                              struct list_head *next)
+{
+        next->prev = new;
+        new->next = next;
+        new->prev = prev;
+        prev->next = new;
+}
+
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+        __list_add(new, head, head->next);
+}
+
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+        __list_add(new, head->prev, head);
+}
+
+
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+        next->prev = prev;
+        prev->next = next;
+}
+
+static inline void list_del(struct list_head *entry)
+{
+        __list_del(entry->prev, entry->next);
+        entry->next = (void *) 0;
+        entry->prev = (void *) 0;
+}
+
+#define list_entry(ptr, type, member) \
+        ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+#define list_for_each_entry(pos, head, member)                          \
+        for (pos = list_entry((head)->next, typeof(*pos), member);      \
+             &pos->member != (head);                                    \
+             pos = list_entry(pos->member.next, typeof(*pos), member))
+
+#endif
+
 
+/* JFFS2 Compressor interface */
+
+static LIST_HEAD(jffs2_compressor_list);
+
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+
+void jffs2_set_compression_mode(int mode) 
+{
+        jffs2_compression_mode = mode;
+}
+
+int jffs2_get_compression_mode(void)
+{
+        return jffs2_compression_mode;
+}
+
+int jffs2_set_compression_mode_name(const char *name) 
+{
+        if (!strcmp("none",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strcmp("priority",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strcmp("size",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+        return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+        struct jffs2_compressor *this;
+        COMPRESSOR_LIST_LOCK;
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->disabled = disabled;
+                        COMPRESSOR_LIST_UNLOCK;
+                        return 0;                        
+                }
+        }
+        COMPRESSOR_LIST_UNLOCK;
+        return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 1);
+}
+
+
+/* Statistics for blocks without compression */
+#ifdef JFFS2_COMPR_STATS
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
+#endif
+
+static int jffs2_compression_check = 0;
+static unsigned char *jffs2_compression_check_buf = NULL;
+
+void jffs2_compression_check_set(int yesno)
+{
+        jffs2_compression_check = yesno;
+}
+
+static void jffs2_decompression_test(struct jffs2_compressor *compr, 
+                                     unsigned char *data_in, unsigned char *output_buf, 
+                                     uint32_t cdatalen, uint32_t datalen) 
+{
+        uint32_t i;
+
+        if (!jffs2_compression_check_buf) {
+                jffs2_compression_check_buf = jffs2_malloc_small(JFFS2_PAGE_SIZE);
+                if (!jffs2_compression_check_buf) {
+                        jffs2_print1("No memory for buffer allocation. Compression check disabled.\n");
+                        jffs2_compression_check = 0;
+                        return;
+                }
+        }
+        if (!compr->decompress) {
+                jffs2_print2("JFFS2 compression check: there is no decompress function at %s.\n", compr->name);
+                return;
+        }
+        if (compr->decompress(output_buf,jffs2_compression_check_buf,cdatalen,datalen)) {
+                jffs2_print2("JFFS2 compression check: decompression failed at %s.\n", compr->name);
+        }
+        else {
+                for (i=0;i<datalen;i++) {
+                        if (data_in[i]!=jffs2_compression_check_buf[i]) {
+                                jffs2_print3("JFFS2 compression check: data mismatch at %s (pos %d).\n", compr->name, i);
+                                break;
+                        }
+                }
+        }
+}
 
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
@@ -38,103 +202,358 @@
  *	data. On exit, expected to hold the actual size of the compressed
  *	data.
  *
- * Returns: Byte to be stored with data indicating compression type used.
+ * Returns: Lower byte to be stored with data indicating compression type used.
  * Zero is used to show that the data could not be compressed - the 
  * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
  *
  * If the cdata buffer isn't large enough to hold all the uncompressed data,
  * jffs2_compress should compress as much as will fit, and should set 
  * *datalen accordingly to show the amount of data which were compressed.
  */
-unsigned char jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 			     unsigned char *data_in, unsigned char **cpage_out, 
 			     uint32_t *datalen, uint32_t *cdatalen)
 {
+	int ret = JFFS2_COMPR_NONE;
 #ifdef JFFS2_COMPRESSION
-	int ret;
+        int compr_ret;
+        struct jffs2_compressor *this, *best=NULL;
+        unsigned char *output_buf = NULL;
+        uint32_t orig_slen, orig_dlen;
+        uint32_t best_slen=0, best_dlen=0;
 
-	*cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
-	if (!*cpage_out) {
-		printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
-		goto out;
-	}
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                break;
+        case JFFS2_COMPR_MODE_PRIORITY:
+                output_buf = jffs2_malloc_small(*cdatalen);
+                if (!output_buf) {
+                        jffs2_print1("No memory for compressor allocation. Compression failed.\n");
+                        goto out;
+                }
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                COMPRESSOR_LIST_LOCK;
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
 
-#ifdef JFFS2_USE_ZLIB
-	ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_ZLIB;
-	}
-#endif
-#ifdef JFFS2_USE_DYNRUBIN
-	ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_DYNRUBIN;
-	}
+                        this->usecount++;
+                        COMPRESSOR_LIST_UNLOCK;
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen);
+                        COMPRESSOR_LIST_LOCK;
+                        this->usecount--;
+                        if (!compr_ret) {
+                                ret = this->compr;
+#ifdef JFFS2_COMPR_STATS
+                                this->stat_compr_blocks++;
+                                this->stat_compr_orig_size += *datalen;
+                                this->stat_compr_new_size  += *cdatalen;
 #endif
-#ifdef JFFS2_USE_RUBINMIPS
-	ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_RUBINMIPS;
-	}
-#endif
-#ifdef JFFS2_USE_RTIME
-	/* rtime does manage to recompress already-compressed data */
-	ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_RTIME;
-	}
+                                if (jffs2_compression_check)
+                                        jffs2_decompression_test(this, data_in, output_buf, *cdatalen, *datalen);
+
+                                break;
+                        }
+                }
+                COMPRESSOR_LIST_UNLOCK;
+                break;
+        case JFFS2_COMPR_MODE_SIZE:
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                COMPRESSOR_LIST_LOCK;
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
+                        if (!this->compr_buf) {
+                                this->compr_buf = jffs2_malloc_small(JFFS2_PAGE_SIZE);
+                                if (!this->compr_buf) {
+                                        jffs2_print1("No memory for compressor allocation.\n");
+                                        continue;
+                                }
+                        }
+                        this->usecount++;
+                        COMPRESSOR_LIST_UNLOCK;
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen);
+                        COMPRESSOR_LIST_LOCK;
+                        this->usecount--;
+                        if (!compr_ret) {
+                                if (jffs2_compression_check)
+                                        jffs2_decompression_test(this, data_in, this->compr_buf, *cdatalen, *datalen);
+                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                        best_dlen = *cdatalen;
+                                        best_slen = *datalen;
+                                        best = this;
+                                }
+                        }
+                }
+                if (best_dlen) {
+                        *cdatalen = best_dlen;
+                        *datalen  = best_slen;
+                        output_buf = best->compr_buf;
+                        best->compr_buf = NULL;
+#ifdef JFFS2_COMPR_STATS
+                        best->stat_compr_blocks++;
+                        best->stat_compr_orig_size += best_slen;
+                        best->stat_compr_new_size  += best_dlen;
 #endif
-	kfree(*cpage_out);
+                        ret = best->compr;
+                }
+                COMPRESSOR_LIST_UNLOCK;
+                break;
+        default:
+                jffs2_print1("JFFS2: unknow compression mode.\n");
+        }
+
 #endif /* Compression */
  out:
-	*cpage_out = data_in;
-	*datalen = *cdatalen;
-	return JFFS2_COMPR_NONE; /* We failed to compress */
-}
-
-void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
-{
-	if (orig != comprbuf)
-		kfree(comprbuf);
+        if (ret == JFFS2_COMPR_NONE) {
+	        *cpage_out = data_in;
+	        *datalen = *cdatalen;
+#ifdef JFFS2_COMPR_STATS
+                none_stat_compr_blocks++;
+                none_stat_compr_size += *datalen;
+#endif
+        }
+        else {
+                *cpage_out = output_buf;
+        }
+	return ret;
 }
 
 int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-		     unsigned char comprtype, unsigned char *cdata_in, 
+		     uint16_t comprtype, unsigned char *cdata_in, 
 		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 {
-	switch (comprtype) {
+        struct jffs2_compressor *this;
+        int ret;
+
+	switch (comprtype & 0xff) {
 	case JFFS2_COMPR_NONE:
 		/* This should be special-cased elsewhere, but we might as well deal with it */
 		memcpy(data_out, cdata_in, datalen);
+#ifdef JFFS2_COMPR_STATS
+                none_stat_decompr_blocks++;
+#endif
 		break;
-
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
-#ifdef JFFS2_USE_ZLIB
-	case JFFS2_COMPR_ZLIB:
-		jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+	default:
+                COMPRESSOR_LIST_LOCK;
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        if (comprtype == this->compr) {
+                                this->usecount++;
+                                COMPRESSOR_LIST_UNLOCK;
+                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen);
+                                COMPRESSOR_LIST_LOCK;
+                                if (ret) {
+                                        jffs2_print3("Decompressor \"%s\" returned %d\n", this->name, ret);
+                                }
+#ifdef JFFS2_COMPR_STATS        
+                                else {
+                                        this->stat_decompr_blocks++;
+                                }
 #endif
-#ifdef JFFS2_USE_RTIME
-	case JFFS2_COMPR_RTIME:
-		jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+                                this->usecount--;
+                                COMPRESSOR_LIST_UNLOCK;
+                                return ret;
+                        }
+                }
+		jffs2_print2("JFFS2 compression type 0x%02x not avaiable.\n", comprtype);
+                COMPRESSOR_LIST_UNLOCK;
+		return -EIO;
+	}
+	return 0;
+}
+
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+        struct jffs2_compressor *this;
+
+        if (!comp->name) {
+                jffs2_print1("NULL compressor name at registering JFFS2 compressor. Failed.\n");
+                return -1;
+        }
+        comp->compr_buf=NULL;
+        comp->usecount=0;
+#ifdef JFFS2_COMPR_STATS
+        comp->stat_compr_orig_size=0;
+        comp->stat_compr_new_size=0;
+        comp->stat_compr_blocks=0;
+        comp->stat_decompr_blocks=0;
 #endif
-#ifdef JFFS2_USE_RUBINMIPS
-	case JFFS2_COMPR_RUBINMIPS:
-		jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+        D1(jffs2_print2("Registering JFFS2 compressor \"%s\"\n", comp->name));
+
+        COMPRESSOR_LIST_LOCK;
+
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        goto out;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                jffs2_print3("Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+
+        COMPRESSOR_LIST_UNLOCK;
+        return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+        D2(struct jffs2_compressor *this;)
+
+        D1(jffs2_print2("Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+
+        COMPRESSOR_LIST_LOCK;
+
+        if (comp->usecount) {
+                COMPRESSOR_LIST_UNLOCK;
+                jffs2_print1("Modul in use. Unregister failed.\n");
+                return -1;
+        }
+        list_del(&comp->list);
+
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                jffs2_print3("Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+        COMPRESSOR_LIST_UNLOCK;
+        return 0;
+}
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = jffs2_malloc_small(JFFS2_STAT_BUF_SIZE);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"DISABLED");
+                else
+                        act_buf += sprintf(act_buf,"ENABLED");
+                act_buf += sprintf(act_buf,"\n");
+        }
+        return buf;
+}
+
+char *jffs2_stats(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = jffs2_malloc_small(JFFS2_STAT_BUF_SIZE);
+
+        act_buf += sprintf(act_buf,"Compression mode: ");
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                act_buf += sprintf(act_buf,"none");
+                break;
+        case JFFS2_COMPR_MODE_PRIORITY:
+                act_buf += sprintf(act_buf,"priority");
+                break;
+        case JFFS2_COMPR_MODE_SIZE:
+                act_buf += sprintf(act_buf,"size");
+                break;
+        default:
+                act_buf += sprintf(act_buf,"unkown");
+                break;
+        }
+        act_buf += sprintf(act_buf,"\nCompressors:\n");
+#ifdef JFFS2_COMPR_STATS
+        act_buf += sprintf(act_buf,"%10s             ","none");
+        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
+                           none_stat_compr_size, none_stat_decompr_blocks);
 #endif
-#ifdef JFFS2_USE_DYNRUBIN
-	case JFFS2_COMPR_DYNRUBIN:
+        COMPRESSOR_LIST_LOCK;
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf,"%10s (prio:%d) ",this->name,this->priority);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"- ");
+                else
+                        act_buf += sprintf(act_buf,"+ ");
+#ifdef JFFS2_COMPR_STATS
+                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
+                                   this->stat_compr_new_size, this->stat_compr_orig_size, 
+                                   this->stat_decompr_blocks);
+#endif
+                act_buf += sprintf(act_buf,"\n");
+        }
+        COMPRESSOR_LIST_UNLOCK;
+        return buf;
+}
 
-		jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+        if (orig != comprbuf)
+                jffs2_free(comprbuf);
+}
+
+int jffs2_compressors_init(void) 
+{
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_init();
 #endif
-	default:
-		printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
-		return -EIO;
-	}
-	return 0;
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_rubinmips_init();
+        jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_init();
+#endif
+
+#ifdef CONFIG_JFFS2_CMODE_NONE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+        D1("JFFS2: default compression mode: none\n");
+#else
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+        D1("JFFS2: default compression mode: size\n");
+#else
+        D1("JFFS2: default compression mode: priority\n");
+#endif
+#endif
+
+        return 0;
+}
+
+int jffs2_compressors_exit(void) 
+{
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_dynrubin_exit();
+        jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_exit();
+#endif
+        return 0;
 }
diff --unified --recursive --new-file mtd/fs/jffs2/compr.h mtd-new-4/fs/jffs2/compr.h
--- mtd/fs/jffs2/compr.h	1970-01-01 01:00:00.000000000 +0100
+++ mtd-new-4/fs/jffs2/compr.h	2004-04-20 22:42:17.000000000 +0200
@@ -0,0 +1,194 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Ferenc Havasi <havasi@xxxxxxx.hu>
+ *
+ * For licensing information, see the file 'LICENCE' in the 
+ * jffs2 directory.
+ *
+ * $Id$
+ *
+ */
+
+#ifndef __JFFS2_COMPR_H__
+#define __JFFS2_COMPR_H__
+
+#ifdef __KERNEL__
+#define JFFS2_KERNEL
+#endif
+
+/* Configurations */
+
+#define JFFS2_COMPRESSION   /* use compression */
+#define JFFS2_COMPR_STATS   /* count some compression statitics */
+
+#ifndef JFFS2_KERNEL        /* in kernel it comes from the kernel config */
+#define CONFIG_JFFS2_ZLIB
+#define CONFIG_JFFS2_RTIME
+#define CONFIG_JFFS2_LZO
+#define CONFIG_JFFS2_LZARI
+#define CONFIG_JFFS2_RUBIN
+#define JFFS2_LZARI_DISABLED /* Experimental compressor, not used by mkfs by default */
+#define JFFS2_LZO_DISABLED   /* Experimental compressor, not used by mkfs by default */
+#endif
+
+#define JFFS2_RUBINMIPS_DISABLED /* RUBINs will be used only for decompression */
+#define JFFS2_DYNRUBIN_DISABLED
+
+/* KERNEL SPACE - USER SPACE SUPPORT (for compr.c and all compressor modules) */
+
+#ifdef JFFS2_KERNEL
+
+#include <linux/kernel.h>
+#include <linux/vmalloc.h>
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/jffs2.h>
+#include <linux/jffs2_fs_i.h>
+#include <linux/jffs2_fs_sb.h>
+#include "nodelist.h"
+
+/* FIX IT PLEASE! */
+#define JFFS2_PAGE_SIZE 4096
+
+#define jffs2_malloc_small(a) kmalloc(a,GFP_KERNEL)
+#define jffs2_free_small(a) kfree(a)
+#define jffs2_malloc(a) vmalloc(a)
+#define jffs2_free(a) vfree(a)
+
+#define jffs2_print1(a) printk(a)
+#define jffs2_print2(a,b) printk(a,b)
+#define jffs2_print3(a,b,c) printk(a,b,c)
+#define jffs2_print4(a,b,c,d) printk(a,b,c,d)
+#define jffs2_print5(a,b,c,d,e) printk(a,b,c,d,e)
+
+#else
+
+#include <stdio.h>
+#include <stdint.h>
+
+extern int page_size;
+#define JFFS2_PAGE_SIZE page_size
+
+struct jffs2_sb_info {};
+struct jffs2_inode_info {};
+
+#define jffs2_malloc_small(a) malloc(a)
+#define jffs2_free_small(a) free(a)
+
+#define jffs2_malloc(a) malloc(a)
+#define jffs2_free(a) free(a)
+
+#define jffs2_print1(a) fprintf(stderr,a)
+#define jffs2_print2(a,b) fprintf(stderr,a,b)
+#define jffs2_print3(a,b,c) fprintf(stderr,a,b,c)
+#define jffs2_print4(a,b,c,d) fprintf(stderr,a,b,c,d)
+#define jffs2_print5(a,b,c,d,e) fprintf(stderr,a,b,c,d,e)
+
+struct list_head {
+        struct list_head *next, *prev;
+};
+
+#define D1(x)
+#define D2(x)
+
+#endif
+
+
+/* JFFS2 compressor interface */
+
+#define JFFS2_COMPR_MODE_NONE       0
+#define JFFS2_COMPR_MODE_PRIORITY   1
+#define JFFS2_COMPR_MODE_SIZE       2
+
+void jffs2_set_compression_mode(int mode);
+int jffs2_get_compression_mode(void);
+int jffs2_set_compression_mode_name(const char *mode_name);
+
+int jffs2_enable_compressor_name(const char *name);
+int jffs2_disable_compressor_name(const char *name);
+
+struct jffs2_compressor {
+        struct list_head list;
+        int priority;
+        char *name;
+        char compr;                             /* JFFS2_COMPR_XXX */
+        int (*compress)(unsigned char *data_in, unsigned char *cpage_out,
+                        uint32_t *srclen, uint32_t *destlen);
+        int (*decompress)(unsigned char *cdata_in, unsigned char *data_out,
+                        uint32_t cdatalen, uint32_t datalen);
+        int (*estimate)(uint32_t *est_rtime, uint32_t *est_wtime, uint32_t *est_size,
+                        unsigned char *cdata_in, unsigned char *data_out,
+                        uint32_t cdatalen, uint32_t datalen);
+        int usecount;
+        int disabled;
+        unsigned char *compr_buf;
+#ifdef JFFS2_COMPR_STATS
+        uint32_t stat_compr_orig_size;
+        uint32_t stat_compr_new_size;
+        uint32_t stat_compr_blocks;
+        uint32_t stat_decompr_blocks;
+#endif
+};
+
+int jffs2_register_compressor(struct jffs2_compressor *comp);
+int jffs2_unregister_compressor(struct jffs2_compressor *comp);
+
+int jffs2_compressors_init(void);
+int jffs2_compressors_exit(void);
+
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                             unsigned char *data_in, unsigned char **cpage_out,
+                             uint32_t *datalen, uint32_t *cdatalen);
+
+int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+                     uint16_t comprtype, unsigned char *cdata_in,
+                     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
+
+/* If it is setted, a decompress will be called after every compress */
+void jffs2_compression_check_set(int yesno);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
+
+char *jffs2_list_compressors(void);
+char *jffs2_stats(void);
+
+
+/* Compressor modules */
+
+#define JFFS2_RUBINMIPS_PRIORITY 10
+#define JFFS2_DYNRUBIN_PRIORITY  20
+#define JFFS2_LZARI_PRIORITY     30
+#define JFFS2_LZO_PRIORITY       40
+#define JFFS2_RTIME_PRIORITY     50
+#define JFFS2_ZLIB_PRIORITY      60
+
+#ifdef CONFIG_JFFS2_RUBIN
+int jffs2_rubinmips_init(void);
+void jffs2_rubinmips_exit(void);
+int jffs2_dynrubin_init(void);
+void jffs2_dynrubin_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+int jffs2_rtime_init(void);
+void jffs2_rtime_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+int jffs2_zlib_init(void);
+void jffs2_zlib_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZO
+int jffs2_lzo_init(void);
+void jffs2_lzo_exit(void);
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+int jffs2_lzari_init(void);
+void jffs2_lzari_exit(void);
+#endif
+
+#endif /* __JFFS2_COMPR_H__ */
diff --unified --recursive --new-file mtd/fs/jffs2/compr_lzari.c mtd-new-4/fs/jffs2/compr_lzari.c
--- mtd/fs/jffs2/compr_lzari.c	1970-01-01 01:00:00.000000000 +0100
+++ mtd-new-4/fs/jffs2/compr_lzari.c	2004-04-20 22:40:10.000000000 +0200
@@ -0,0 +1,730 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Patrik Kluba
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id$
+ *
+ */
+
+/*
+   Lempel-Ziv-Arithmetic coding compression module for jffs2
+   Based on the LZARI source included in LDS (lossless datacompression sources)
+   Block-compression and bitstream modifications by Patrik Kluba
+*/
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+Original copyright follows:
+
+**************************************************************
+	LZARI.C -- A Data Compression Program
+	(tab = 4 spaces)
+**************************************************************
+	4/7/1989 Haruhiko Okumura
+	Use, distribute, and modify this program freely.
+	Please send me your improved versions.
+		PC-VAN		SCIENCE
+		NIFTY-Serve	PAF01022
+		CompuServe	74050,1022
+**************************************************************
+
+LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake.
+All rights reserved. Permission granted for non-commercial use.
+
+*/
+
+/*
+
+	2004-02-18  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Removed unused variables and fixed no return value
+
+	2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Initial release
+
+*/
+
+/* lzari.c */
+
+#define N		 4096	/* size of ring buffer */
+#define F		   60	/* upper limit for match_length */
+#define THRESHOLD	2   /* encode string into position and length
+						   if match_length is greater than this */
+#define NIL			N	/* index for root of binary search trees */
+
+static unsigned char
+		text_buf[N + F - 1];	/* ring buffer of size N,
+			with extra F-1 bytes to facilitate string comparison */
+static unsigned long		match_position, match_length,  /* of longest match.  These are
+			set by the InsertNode() procedure. */
+		lson[N + 1], rson[N + 257], dad[N + 1];  /* left & right children &
+			parents -- These constitute binary search trees. */
+
+static void InitTree(void)  /* Initialize trees */
+{
+	unsigned long  i;
+
+	/* For i = 0 to N - 1, rson[i] and lson[i] will be the right and
+	   left children of node i.  These nodes need not be initialized.
+	   Also, dad[i] is the parent of node i.  These are initialized to
+	   NIL (= N), which stands for 'not used.'
+	   For i = 0 to 255, rson[N + i + 1] is the root of the tree
+	   for strings that begin with character i.  These are initialized
+	   to NIL.  Note there are 256 trees. */
+
+	for (i = N + 1; i <= N + 256; i++) rson[i] = NIL;	/* root */
+	for (i = 0; i < N; i++) dad[i] = NIL;	/* node */
+}
+
+static void InsertNode(unsigned long r)
+	/* Inserts string of length F, text_buf[r..r+F-1], into one of the
+	   trees (text_buf[r]'th tree) and returns the longest-match position
+	   and length via the global variables match_position and match_length.
+	   If match_length = F, then removes the old node in favor of the new
+	   one, because the old one will be deleted sooner.
+	   Note r plays double role, as tree node and position in buffer. */
+{
+	unsigned long i, p, temp;
+	unsigned char *key;
+	signed long cmp;
+
+	cmp = 1;  key = &text_buf[r];  p = N + 1 + key[0];
+	rson[r] = lson[r] = NIL;  match_length = 0;
+	for ( ; ; ) {
+		if (cmp >= 0) {
+			if (rson[p] != NIL) p = rson[p];
+			else {  rson[p] = r;  dad[r] = p;  return;  }
+		} else {
+			if (lson[p] != NIL) p = lson[p];
+			else {  lson[p] = r;  dad[r] = p;  return;  }
+		}
+		for (i = 1; i < F; i++)
+			if ((cmp = key[i] - text_buf[p + i]) != 0)  break;
+		if (i > THRESHOLD) {
+			if (i > match_length) {
+				match_position = (r - p) & (N - 1);
+				if ((match_length = i) >= F) break;
+			} else if (i == match_length) {
+				if ((temp = (r - p) & (N - 1)) < match_position)
+					match_position = temp;
+			}
+		}
+	}
+	dad[r] = dad[p];  lson[r] = lson[p];  rson[r] = rson[p];
+	dad[lson[p]] = r;  dad[rson[p]] = r;
+	if (rson[dad[p]] == p) rson[dad[p]] = r;
+	else                   lson[dad[p]] = r;
+	dad[p] = NIL;  /* remove p */
+}
+
+static void DeleteNode(unsigned long p)  /* Delete node p from tree */
+{
+	unsigned long  q;
+	
+	if (dad[p] == NIL) return;  /* not in tree */
+	if (rson[p] == NIL) q = lson[p];
+	else if (lson[p] == NIL) q = rson[p];
+	else {
+		q = lson[p];
+		if (rson[q] != NIL) {
+			do {  q = rson[q];  } while (rson[q] != NIL);
+			rson[dad[q]] = lson[q];  dad[lson[q]] = dad[q];
+			lson[q] = lson[p];  dad[lson[p]] = q;
+		}
+		rson[q] = rson[p];  dad[rson[p]] = q;
+	}
+	dad[q] = dad[p];
+	if (rson[dad[p]] == p) rson[dad[p]] = q;
+	else                   lson[dad[p]] = q;
+	dad[p] = NIL;
+}
+
+/********** Arithmetic Compression **********/
+
+/*  If you are not familiar with arithmetic compression, you should read
+		I. E. Witten, R. M. Neal, and J. G. Cleary,
+			Communications of the ACM, Vol. 30, pp. 520-540 (1987),
+	from which much have been borrowed.  */
+
+#define M   15
+
+/*	Q1 (= 2 to the M) must be sufficiently large, but not so
+	large as the unsigned long 4 * Q1 * (Q1 - 1) overflows.  */
+
+#define Q1  (1UL << M)
+#define Q2  (2 * Q1)
+#define Q3  (3 * Q1)
+#define Q4  (4 * Q1)
+#define MAX_CUM (Q1 - 1)
+
+#define N_CHAR  (256 - THRESHOLD + F)
+	/* character code = 0, 1, ..., N_CHAR - 1 */
+
+static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1];
+static unsigned long
+	sym_freq[N_CHAR + 1],  /* frequency for symbols */
+	sym_cum[N_CHAR + 1],   /* cumulative freq for symbols */
+	position_cum[N + 1];   /* cumulative freq for positions */
+
+static void StartModel(void)  /* Initialize model */
+{
+	unsigned long ch, sym, i;
+	
+	sym_cum[N_CHAR] = 0;
+	for (sym = N_CHAR; sym >= 1; sym--) {
+		ch = sym - 1;
+		char_to_sym[ch] = sym;  sym_to_char[sym] = ch;
+		sym_freq[sym] = 1;
+		sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym];
+	}
+	sym_freq[0] = 0;  /* sentinel (!= sym_freq[1]) */
+	position_cum[N] = 0;
+	for (i = N; i >= 1; i--)
+		position_cum[i - 1] = position_cum[i] + 10000 / (i + 200);
+			/* empirical distribution function (quite tentative) */
+			/* Please devise a better mechanism! */
+}
+
+static void UpdateModel(unsigned long sym)
+{
+	unsigned long c, ch_i, ch_sym;
+	unsigned long i;
+	if (sym_cum[0] >= MAX_CUM) {
+		c = 0;
+		for (i = N_CHAR; i > 0; i--) {
+			sym_cum[i] = c;
+			c += (sym_freq[i] = (sym_freq[i] + 1) >> 1);
+		}
+		sym_cum[0] = c;
+	}
+	for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ;
+	if (i < sym) {
+		ch_i = sym_to_char[i];    ch_sym = sym_to_char[sym];
+		sym_to_char[i] = ch_sym;  sym_to_char[sym] = ch_i;
+		char_to_sym[ch_i] = sym;  char_to_sym[ch_sym] = i;
+	}
+	sym_freq[i]++;
+	while (--i > 0) sym_cum[i]++;
+	sym_cum[0]++;
+}
+
+static unsigned long BinarySearchSym(unsigned long x)
+	/* 1      if x >= sym_cum[1],
+	   N_CHAR if sym_cum[N_CHAR] > x,
+	   i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */
+{
+	unsigned long i, j, k;
+	
+	i = 1;  j = N_CHAR;
+	while (i < j) {
+		k = (i + j) / 2;
+		if (sym_cum[k] > x) i = k + 1;  else j = k;
+	}
+	return i;
+}
+
+unsigned long BinarySearchPos(unsigned long x)
+	/* 0 if x >= position_cum[1],
+	   N - 1 if position_cum[N] > x,
+	   i such that position_cum[i] > x >= position_cum[i + 1] otherwise */
+{
+	unsigned long i, j, k;
+	
+	i = 1;  j = N;
+	while (i < j) {
+		k = (i + j) / 2;
+		if (position_cum[k] > x) i = k + 1;  else j = k;
+	}
+	return i - 1;
+}
+
+/* modified for block compression */
+/* on return, srclen will contain the number of successfully compressed bytes
+   and dstlen will contain completed compressed bytes */
+
+static int Encode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long *srclen,
+			unsigned long *dstlen)
+{
+	unsigned long c, i, len, r, s, last_match_length, sym, range;
+	unsigned long low = 0;
+	unsigned long high = Q4;
+	unsigned long shifts = 0;  /* counts for magnifying low and high around Q2 */
+	unsigned char *ip, *op;
+	unsigned long written = 0;
+	unsigned long read = 0;
+	unsigned char buffer = 0;
+	unsigned char mask = 128;
+	unsigned char *srcend = srcbuf + *srclen;
+	unsigned char *dstend = dstbuf + *dstlen;
+	ip = srcbuf;
+	op = dstbuf;
+	StartModel();
+	InitTree();  /* initialize trees */
+	s = 0;  r = N - F;
+	for (i = s; i < r; i++) text_buf[i] = ' ';  /* Clear the buffer with
+		any character that will appear often. */
+	for (len = 0; (len < F) && (ip < srcend); len++)
+		text_buf[r + len] = *(ip++);  /* Read F bytes into the last F bytes of
+			the buffer */
+	read = len;
+	for (i = 1; i <= F; i++) InsertNode(r - i);  /* Insert the F strings,
+		each of which begins with one or more 'space' characters.  Note
+		the order in which these strings are inserted.  This way,
+		degenerate trees will be less likely to occur. */
+	InsertNode(r);  /* Finally, insert the whole string just read.  The
+		global variables match_length and match_position are set. */
+	do {
+		if (match_length > len) match_length = len;  /* match_length
+			may be spuriously long near the end of text. */
+		if (match_length <= THRESHOLD) {
+			match_length = 1;  /* Not long enough match.  Send one byte. */
+			sym = char_to_sym[text_buf[r]];
+			range = high - low;
+			high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+			low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else if (low >= Q2) {
+					buffer |= mask;
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+					low -= Q2;
+					high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					shifts++;
+					low -= Q1;
+					high -= Q1;
+				} else break;
+				low += low;  high += high;
+			}
+			UpdateModel(sym);
+		} else {
+			sym = char_to_sym[255 - THRESHOLD + match_length];
+			range = high - low;
+			high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+			low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else if (low >= Q2) {
+					buffer |= mask;
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+					low -= Q2;
+					high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					shifts++;
+					low -= Q1;
+					high -= Q1;
+				} else break;
+				low += low;  high += high;
+			}
+			UpdateModel(sym);
+			range = high - low;
+			high = low + (range * position_cum[match_position - 1]) / position_cum[0];
+			low +=       (range * position_cum[match_position    ]) / position_cum[0];
+			for ( ; ; ) {
+				if (high <= Q2) {
+					if ((mask >>= 1) == 0) {
+						if (op >= dstend) {
+							*dstlen = written;
+							return -1;
+						}
+						*(op++) = buffer;
+						buffer = 0;
+						mask = 128;
+						written++;
+						*srclen = read;
+					}
+					for ( ; shifts > 0; shifts--) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+					}
+				} else {
+					if (low >= Q2) {
+						buffer |= mask;
+						if ((mask >>= 1) == 0) {
+							if (op >= dstend) {
+								*dstlen = written;
+								return -1;
+							}
+							*(op++) = buffer;
+							buffer = 0;
+							mask = 128;
+							written++;
+							*srclen = read;
+						}
+						for ( ; shifts > 0; shifts--) {
+							if ((mask >>= 1) == 0) {
+								if (op >= dstend) {
+									*dstlen = written;
+									return -1;
+								}
+								*(op++) = buffer;
+								buffer = 0;
+								mask = 128;
+								written++;
+								*srclen = read;
+							}
+						}
+						low -= Q2;
+						high -= Q2;
+					} else {
+						if ((low >= Q1) && (high <= Q3)) {
+							shifts++;
+							low -= Q1;
+							high -= Q1;
+						} else {
+							break;
+						}
+					}
+				}
+				low += low;
+				high += high;
+			}
+		}
+		last_match_length = match_length;
+		for (i = 0; (i < last_match_length) && (ip < srcend); i++) {
+			c = *(ip++);
+			DeleteNode(s);
+			text_buf[s] = c;
+			if (s < F - 1)
+				text_buf[s + N] = c;
+			s = (s + 1) & (N - 1);
+			r = (r + 1) & (N - 1);
+			InsertNode(r);
+		}
+		read += i;
+		while (i++ < last_match_length) {
+			DeleteNode(s);
+			s = (s + 1) & (N - 1);
+			r = (r + 1) & (N - 1);
+			if (--len) InsertNode(r);
+		}
+	} while (len > 0);
+	shifts++;
+	if (low < Q1) {
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+		for ( ; shifts > 0; shifts--) {
+			buffer |= mask;
+			if ((mask >>= 1) == 0) {
+				if (op >= dstend) {
+					*dstlen = written;
+					return -1;
+				}
+				*(op++) = buffer;
+				buffer = 0;
+				mask = 128;
+				written++;
+				*srclen = read;
+			}
+		}
+	} else {
+		buffer |= mask;
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+		for ( ; shifts > 0; shifts--) {
+			if ((mask >>= 1) == 0) {
+				if (op >= dstend) {
+					*dstlen = written;
+					return -1;
+				}
+				*(op++) = buffer;
+				buffer = 0;
+				mask = 128;
+				written++;
+				*srclen = read;
+			}
+		}
+	}
+	for (i = 0; i < 7; i++) {
+		if ((mask >>= 1) == 0) {
+			if (op >= dstend) {
+				*dstlen = written;
+				return -1;
+			}
+			*(op++) = buffer;
+			buffer = 0;
+			mask = 128;
+			written++;
+			*srclen = read;
+		}
+	}
+	*dstlen = written;
+	return 0;
+}
+
+static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen,
+					unsigned long dstlen)	/* Just the reverse of Encode(). */
+{
+	unsigned long i, r, j, k, c, range, sym;
+	unsigned char *ip, *op;
+	unsigned char *srcend = srcbuf + srclen;
+	unsigned char *dstend = dstbuf + dstlen;
+	unsigned char buffer = 0;
+	unsigned char mask = 0;
+	unsigned long low = 0;
+	unsigned long high = Q4;
+	unsigned long value = 0;
+	ip = srcbuf;
+	op = dstbuf;
+	for (i = 0; i < M + 2; i++) {
+		value *= 2;
+		if ((mask >>= 1) == 0) {
+			buffer = (ip >= srcend) ? 0 : *(ip++);
+			mask = 128;
+		}
+		value += ((buffer & mask) != 0);
+	}
+	StartModel();
+	for (i = 0; i < N - F; i++) text_buf[i] = ' ';
+	r = N - F;
+	while (op < dstend) {
+		range = high - low;
+		sym = BinarySearchSym((unsigned long)
+				(((value - low + 1) * sym_cum[0] - 1) / range));
+		high = low + (range * sym_cum[sym - 1]) / sym_cum[0];
+		low +=       (range * sym_cum[sym    ]) / sym_cum[0];
+		for ( ; ; ) {
+			if (low >= Q2) {
+				value -= Q2;  low -= Q2;  high -= Q2;
+			} else if (low >= Q1 && high <= Q3) {
+				value -= Q1;  low -= Q1;  high -= Q1;
+			} else if (high > Q2) break;
+			low += low;  high += high;
+			value *= 2;
+			if ((mask >>= 1) == 0) {
+				buffer = (ip >= srcend) ? 0 : *(ip++);
+				mask = 128;
+			}
+			value += ((buffer & mask) != 0);
+		}
+		c = sym_to_char[sym];
+		UpdateModel(sym);
+		if (c < 256) {
+			if (op >= dstend) return -1;
+			*(op++) = c;
+			text_buf[r++] = c;
+			r &= (N - 1);
+		} else {
+			j = c - 255 + THRESHOLD;
+			range = high - low;
+			i = BinarySearchPos((unsigned long)
+				(((value - low + 1) * position_cum[0] - 1) / range));
+			high = low + (range * position_cum[i    ]) / position_cum[0];
+			low +=       (range * position_cum[i + 1]) / position_cum[0];
+			for ( ; ; ) {
+				if (low >= Q2) {
+					value -= Q2;  low -= Q2;  high -= Q2;
+				} else if (low >= Q1 && high <= Q3) {
+					value -= Q1;  low -= Q1;  high -= Q1;
+				} else if (high > Q2) break;
+				low += low;  high += high;
+				value *= 2;
+				if ((mask >>= 1) == 0) {
+					buffer = (ip >= srcend) ? 0 : *(ip++);
+					mask = 128;
+				}
+				value += ((buffer & mask) != 0);
+			}
+			i = (r - i - 1) & (N - 1);
+			for (k = 0; k < j; k++) {
+				c = text_buf[(i + k) & (N - 1)];
+				if (op >= dstend) return -1;
+				*(op++) = c;
+				text_buf[r++] = c;
+				r &= (N - 1);
+			}		
+		}
+	}
+	return 0;
+}
+
+/* interface to jffs2 follows */
+
+#include "compr.h"
+#include <linux/jffs2.h>
+
+int jffs2_lzari_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen);
+
+int jffs2_lzari_estimate (uint32_t *est_rtime, uint32_t *est_wtime, uint32_t *est_size,
+			unsigned char *cdata_in, unsigned char *data_out,
+			uint32_t cdatalen, uint32_t datalen);
+
+int jffs2_lzari_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen);
+
+struct jffs2_compressor jffs2_lzari_comp = {
+	.priority = JFFS2_LZARI_PRIORITY,
+	.name = "lzari",
+	.compr = JFFS2_COMPR_LZARI,
+	.compress = &jffs2_lzari_compress,
+	.decompress = &jffs2_lzari_decompress,
+#ifdef JFFS2_LZARI_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+int jffs2_lzari_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen)
+{
+	return Encode(input, output, (unsigned long *)sourcelen, (unsigned long *)dstlen);
+}
+
+int jffs2_lzari_estimate (uint32_t *est_rtime, uint32_t *est_wtime, uint32_t *est_size,
+			unsigned char *cdata_in, unsigned char *data_out,
+			uint32_t cdatalen, uint32_t datalen)
+{
+	return 0;
+}
+
+int jffs2_lzari_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen)
+{
+    return Decode(input, output, sourcelen, dstlen);
+}
+
+int jffs2_lzari_init (void)
+{
+    return jffs2_register_compressor(&jffs2_lzari_comp);
+}
+
+void jffs2_lzari_exit (void)
+{
+    jffs2_unregister_compressor (&jffs2_lzari_comp);
+}
diff --unified --recursive --new-file mtd/fs/jffs2/compr_lzo.c mtd-new-4/fs/jffs2/compr_lzo.c
--- mtd/fs/jffs2/compr_lzo.c	1970-01-01 01:00:00.000000000 +0100
+++ mtd-new-4/fs/jffs2/compr_lzo.c	2004-04-20 22:39:56.000000000 +0200
@@ -0,0 +1,2311 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by Patrik Kluba
+ *
+ * For licensing information, see the file 'LICENCE' in the
+ * jffs2 directory.
+ *
+ * $Id$
+ *
+ */
+
+/*
+   LZO1X-1 (and -999) compression module for jffs2
+   based on the original LZO sources
+*/
+
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
+
+/*
+   Original copyright notice follows:
+
+   lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm
+   lzo_ptr.h -- low-level pointer constructs
+   lzo_swd.ch -- sliding window dictionary
+   lzoconf.h -- configuration for the LZO real-time data compression library
+   lzo_mchw.ch -- matching functions using a window
+   minilzo.c -- mini subset of the LZO real-time data compression library
+   config1x.h -- configuration for the LZO1X algorithm
+   lzo1x.h -- public interface of the LZO1X compression algorithm
+
+   These files are part of the LZO real-time data compression library.
+
+   Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer
+   All Rights Reserved.
+
+   The LZO library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU General Public License as
+   published by the Free Software Foundation; either version 2 of
+   the License, or (at your option) any later version.
+
+   The LZO library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the LZO library; see the file COPYING.
+   If not, write to the Free Software Foundation, Inc.,
+   59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+   Markus F.X.J. Oberhumer
+   <markus@xxxxxxx.com>
+*/
+
+/*
+
+	2004-02-16  pajko <pajko(AT)halom(DOT)u-szeged(DOT)hu>
+				Initial release
+					-removed all 16 bit code
+					-all sensitive data will be on 4 byte boundary
+					-removed check parts for library use
+					-removed all but LZO1X-* compression
+					
+*/
+
+#ifndef __KERNEL__
+  #include <sys/types.h>
+  #include <stddef.h>
+  #include <string.h>
+  #include <limits.h>
+#else
+  #include <linux/kernel.h>
+  #include <linux/types.h>
+  #include <linux/stddef.h>
+  #include <linux/string.h>
+  #define USHRT_MAX     65535
+  /* #define UINT_MAX      4294967295U */
+#endif
+
+/* data type definitions */
+#define U32 unsigned long
+#define S32 signed long
+#define I32 long
+#define U16 unsigned short
+#define S16 signed short
+#define I16 short
+#define U8 unsigned char
+#define S8 signed char
+#define I8 char
+
+/*************************************/
+
+/* lzo_swd.ch */
+
+#define SWD_N				N
+#define SWD_F				F
+#define SWD_THRESHOLD		THRESHOLD
+
+/* shortest unsigned int that 2 * SWD_F + SWD_N (currently 53248) fits in */
+typedef unsigned short swd_uint;
+/* upper limit of that data type */
+#define SWD_UINT_MAX	USHRT_MAX
+
+/* minilzo.c */
+
+#define LZO_VERSION_DATE	"Jul 12 2002"
+#define LZO_VERSION_STRING	"1.08"
+#define LZO_VERSION			0x1080
+
+/* lzo_ptr.h */
+
+/* Integral types that have *exactly* the same number of bits as a lzo_voidp */
+typedef unsigned long lzo_ptr_t;
+typedef long lzo_sptr_t;
+
+
+/*************************************/
+
+/* config1x.h */
+
+#define M1_MAX_OFFSET	0x0400
+#define M2_MAX_OFFSET	0x0800
+#define M3_MAX_OFFSET	0x4000
+#define M4_MAX_OFFSET	0xbfff
+
+#define MX_MAX_OFFSET	(M1_MAX_OFFSET + M2_MAX_OFFSET)
+
+#define M1_MIN_LEN		2
+#define M1_MAX_LEN		2
+#define M2_MIN_LEN		3
+#define M2_MAX_LEN		8
+#define M3_MIN_LEN		3
+#define M3_MAX_LEN		33
+#define M4_MIN_LEN		3
+#define M4_MAX_LEN		9
+
+#define M1_MARKER		0
+#define M2_MARKER		64
+#define M3_MARKER		32
+#define M4_MARKER		16
+
+#define MIN_LOOKAHEAD		(M2_MAX_LEN + 1)
+
+/* minilzo.c */
+
+#define LZO_BYTE(x)       ((unsigned char) ((x) & 0xff))
+
+#define LZO_MAX(a,b)        ((a) >= (b) ? (a) : (b))
+#define LZO_MIN(a,b)        ((a) <= (b) ? (a) : (b))
+#define LZO_MAX3(a,b,c)     ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c))
+#define LZO_MIN3(a,b,c)     ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c))
+
+#define lzo_sizeof(type)    ((lzo_uint) (sizeof(type)))
+
+#define LZO_HIGH(array)     ((lzo_uint) (sizeof(array)/sizeof(*(array))))
+
+#define LZO_SIZE(bits)      (1u << (bits))
+#define LZO_MASK(bits)      (LZO_SIZE(bits) - 1)
+
+#define LZO_LSIZE(bits)     (1ul << (bits))
+#define LZO_LMASK(bits)     (LZO_LSIZE(bits) - 1)
+
+#define LZO_USIZE(bits)     ((lzo_uint) 1 << (bits))
+#define LZO_UMASK(bits)     (LZO_USIZE(bits) - 1)
+
+#define LZO_STYPE_MAX(b)    (((1l  << (8*(b)-2)) - 1l)  + (1l  << (8*(b)-2)))
+#define LZO_UTYPE_MAX(b)    (((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1)))
+
+#define _LZO_STRINGIZE(x)           #x
+#define _LZO_MEXPAND(x)             _LZO_STRINGIZE(x)
+
+#define _LZO_CONCAT2(a,b)           a ## b
+#define _LZO_CONCAT3(a,b,c)         a ## b ## c
+#define _LZO_CONCAT4(a,b,c,d)       a ## b ## c ## d
+#define _LZO_CONCAT5(a,b,c,d,e)     a ## b ## c ## d ## e
+
+#define _LZO_ECONCAT2(a,b)          _LZO_CONCAT2(a,b)
+#define _LZO_ECONCAT3(a,b,c)        _LZO_CONCAT3(a,b,c)
+#define _LZO_ECONCAT4(a,b,c,d)      _LZO_CONCAT4(a,b,c,d)
+#define _LZO_ECONCAT5(a,b,c,d,e)    _LZO_CONCAT5(a,b,c,d,e)
+
+#define lzo_dict_t    const lzo_bytep
+#define lzo_dict_p    lzo_dict_t *
+#define lzo_moff_t    lzo_uint
+
+#define MEMCPY8_DS(dest,src,len) \
+    memcpy(dest,src,len); \
+    dest += len; \
+    src += len
+
+#define MEMCPY_DS(dest,src,len) \
+    do *dest++ = *src++; \
+    while (--len > 0)
+
+#define MEMMOVE_DS(dest,src,len) \
+    do *dest++ = *src++; \
+    while (--len > 0)
+
+#define BZERO8_PTR(s,l,n)   memset((s),0,(lzo_uint)(l)*(n))
+
+#define LZO_BASE 65521u
+#define LZO_NMAX 5552
+
+#define LZO_DO1(buf,i)  {s1 += buf[i]; s2 += s1;}
+#define LZO_DO2(buf,i)  LZO_DO1(buf,i); LZO_DO1(buf,i+1);
+#define LZO_DO4(buf,i)  LZO_DO2(buf,i); LZO_DO2(buf,i+2);
+#define LZO_DO8(buf,i)  LZO_DO4(buf,i); LZO_DO4(buf,i+4);
+#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8);
+
+#define IS_SIGNED(type)       (((type) (-1)) < ((type) 0))
+#define IS_UNSIGNED(type)     (((type) (-1)) > ((type) 0))
+
+#define IS_POWER_OF_2(x)        (((x) & ((x) - 1)) == 0)
+
+#define D_BITS          14
+#define D_INDEX1(d,p)       d = DM((0x21*DX3(p,5,5,6)) >> 5)
+#define D_INDEX2(d,p)       d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f)
+
+#define LZO_HASH            LZO_HASH_LZO_INCREMENTAL_B
+
+#define DL_MIN_LEN          M2_MIN_LEN
+
+#define D_SIZE        LZO_SIZE(D_BITS)
+#define D_MASK        LZO_MASK(D_BITS)
+
+#define D_HIGH        ((D_MASK >> 1) + 1)
+
+#define DINDEX1             D_INDEX1
+#define DINDEX2             D_INDEX2
+
+#define DX2(p,s1,s2) \
+	(((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0])
+
+#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0])
+#define DMS(v,s)        ((lzo_uint) (((v) & (D_MASK >> (s))) << (s)))
+#define DM(v)           DMS(v,0)
+
+#define DENTRY(p,in)                          (p)
+#define GINDEX(m_pos,m_off,dict,dindex,in)    m_pos = dict[dindex]
+
+#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \
+	(m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset)
+
+#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \
+    (BOUNDS_CHECKING_OFF_IN_EXPR( \
+	(PTR_LT(m_pos,in) || \
+	 (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \
+	  m_off > max_offset) ))
+
+#define BOUNDS_CHECKING_OFF_IN_EXPR(expr)     (expr)
+
+#define DD_BITS			0
+#define DD_SIZE         LZO_SIZE(DD_BITS)
+#define DD_MASK         LZO_MASK(DD_BITS)
+
+#define DL_BITS        (D_BITS - DD_BITS)
+#define DL_SIZE        LZO_SIZE(DL_BITS)
+#define DL_MASK        LZO_MASK(DL_BITS)
+
+#define UPDATE_D(dict,drun,dv,p,in)       dict[ DINDEX(dv,p) ] = DENTRY(p,in)
+#define UPDATE_I(dict,drun,index,p,in)    dict[index] = DENTRY(p,in)
+#define UPDATE_P(ptr,drun,p,in)           (ptr)[0] = DENTRY(p,in)
+
+#define __COPY4(dst,src)  * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src)
+#define COPY4(dst,src)	__COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src))
+
+#define TEST_IP         (ip < ip_end)
+#define TEST_OP         (op <= op_end)
+
+#define NEED_IP(x) \
+            if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x))  goto input_overrun
+#define NEED_OP(x) \
+            if ((lzo_uint)(op_end - op) < (lzo_uint)(x))  goto output_overrun
+#define TEST_LOOKBEHIND(m_pos,out)    if (m_pos < out) goto lookbehind_overrun
+
+/* lzo1x_9x.c */
+
+#define LZO_UINT_MAX	UINT_MAX
+#define N			M4_MAX_OFFSET
+#define THRESHOLD	    1
+#define F		     2048
+
+#define SWD_BEST_OFF	(LZO_MAX3( M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN ) + 1)
+
+/* ../include/lzoconf.h */
+
+typedef U32 lzo_uint32;
+typedef I32 lzo_int32;
+typedef U32 lzo_uint;
+typedef I32 lzo_int;
+typedef int lzo_bool;
+
+#define lzo_byte                U8
+#define lzo_bytep               U8 *
+#define lzo_charp               char *
+#define lzo_voidp               void *
+#define lzo_shortp              short *
+#define lzo_ushortp             unsigned short *
+#define lzo_uint32p             lzo_uint32 *
+#define lzo_int32p              lzo_int32 *
+#define lzo_uintp               lzo_uint *
+#define lzo_intp                lzo_int *
+#define lzo_voidpp              lzo_voidp *
+#define lzo_bytepp              lzo_bytep *
+#define lzo_sizeof_dict_t		sizeof(lzo_bytep)
+
+#define LZO_E_OK                    0
+#define LZO_E_ERROR                 (-1)
+#define LZO_E_OUT_OF_MEMORY         (-2)	/* not used right now */
+#define LZO_E_NOT_COMPRESSIBLE      (-3)	/* not used right now */
+#define LZO_E_INPUT_OVERRUN         (-4)
+#define LZO_E_OUTPUT_OVERRUN        (-5)
+#define LZO_E_LOOKBEHIND_OVERRUN    (-6)
+#define LZO_E_EOF_NOT_FOUND         (-7)
+#define LZO_E_INPUT_NOT_CONSUMED    (-8)
+
+#define LZO_PTR_ALIGN_UP(_ptr,_size) \
+   ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size)))
+#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size)
+
+typedef int
+	(*lzo_compress_t) (const lzo_byte * src, lzo_uint src_len,
+			   lzo_byte * dst, lzo_uintp dst_len,
+			   lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_decompress_t) (const lzo_byte * src, lzo_uint src_len,
+			     lzo_byte * dst, lzo_uintp dst_len,
+			     lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_optimize_t) (lzo_byte * src, lzo_uint src_len,
+			   lzo_byte * dst, lzo_uintp dst_len,
+			   lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_compress_dict_t) (const lzo_byte * src, lzo_uint src_len,
+				lzo_byte * dst, lzo_uintp dst_len,
+				lzo_voidp wrkmem,
+				const lzo_byte * dict, lzo_uint dict_len);
+
+typedef int
+	(*lzo_decompress_dict_t) (const lzo_byte * src, lzo_uint src_len,
+				  lzo_byte * dst, lzo_uintp dst_len,
+				  lzo_voidp wrkmem,
+				  const lzo_byte * dict, lzo_uint dict_len);
+
+typedef int
+	(*lzo_compress_asm_t) (const lzo_byte * src, lzo_uint src_len,
+			       lzo_byte * dst, lzo_uintp dst_len,
+			       lzo_voidp wrkmem);
+
+typedef int
+	(*lzo_decompress_asm_t) (const lzo_byte * src, lzo_uint src_len,
+				 lzo_byte * dst, lzo_uintp dst_len,
+				 lzo_voidp wrkmem);
+
+typedef void (*lzo_progress_callback_t) (lzo_uint, lzo_uint);
+
+typedef union
+{
+	lzo_bytep p;
+	lzo_uint u;
+} __lzo_pu_u;
+typedef union
+{
+	lzo_bytep p;
+	lzo_uint32 u32;
+} __lzo_pu32_u;
+typedef union
+{
+	void *vp;
+	lzo_bytep bp;
+	lzo_uint32 u32;
+	long l;
+} lzo_align_t;
+
+/* lzo1x.h */
+
+#define LZO1X_1_MEM_COMPRESS    ((lzo_uint32) (16384L * lzo_sizeof_dict_t))
+#define LZO1X_999_MEM_COMPRESS  ((lzo_uint32) (14 * 16384L * sizeof(short)))
+
+/* lzo_ptr.h */
+
+#define PTR(a)				((lzo_ptr_t) (a))
+#define PTR_LINEAR(a)		PTR(a)
+#define PTR_ALIGNED_4(a)	((PTR_LINEAR(a) & 3) == 0)
+#define PTR_ALIGNED_8(a)	((PTR_LINEAR(a) & 7) == 0)
+#define PTR_ALIGNED2_4(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0)
+#define PTR_ALIGNED2_8(a,b)	(((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0)
+#define PTR_LT(a,b)			(PTR(a) < PTR(b))
+#define PTR_GE(a,b)			(PTR(a) >= PTR(b))
+#define PTR_DIFF(a,b)		((lzo_ptrdiff_t) (PTR(a) - PTR(b)))
+#define pd(a,b)		        ((lzo_uint) ((a)-(b)))
+
+typedef ptrdiff_t lzo_ptrdiff_t;
+
+typedef union
+{
+	char a_char;
+	unsigned char a_uchar;
+	short a_short;
+	unsigned short a_ushort;
+	int a_int;
+	unsigned int a_uint;
+	long a_long;
+	unsigned long a_ulong;
+	lzo_int a_lzo_int;
+	lzo_uint a_lzo_uint;
+	lzo_int32 a_lzo_int32;
+	lzo_uint32 a_lzo_uint32;
+	ptrdiff_t a_ptrdiff_t;
+	lzo_ptrdiff_t a_lzo_ptrdiff_t;
+	lzo_ptr_t a_lzo_ptr_t;
+	lzo_voidp a_lzo_voidp;
+	void *a_void_p;
+	lzo_bytep a_lzo_bytep;
+	lzo_bytepp a_lzo_bytepp;
+	lzo_uintp a_lzo_uintp;
+	lzo_uint *a_lzo_uint_p;
+	lzo_uint32p a_lzo_uint32p;
+	lzo_uint32 *a_lzo_uint32_p;
+	unsigned char *a_uchar_p;
+	char *a_char_p;
+}
+lzo_full_align_t;
+
+/* lzo_mchw.ch */
+
+typedef struct
+{
+	int init;
+
+	lzo_uint look;
+
+	lzo_uint m_len;
+	lzo_uint m_off;
+
+	lzo_uint last_m_len;
+	lzo_uint last_m_off;
+
+	const lzo_byte *bp;
+	const lzo_byte *ip;
+	const lzo_byte *in;
+	const lzo_byte *in_end;
+	lzo_byte *out;
+
+	lzo_progress_callback_t cb;
+
+	lzo_uint textsize;
+	lzo_uint codesize;
+	lzo_uint printcount;
+
+	unsigned long lit_bytes;
+	unsigned long match_bytes;
+	unsigned long rep_bytes;
+	unsigned long lazy;
+
+	lzo_uint r1_lit;
+	lzo_uint r1_m_len;
+
+	unsigned long m1a_m, m1b_m, m2_m, m3_m, m4_m;
+	unsigned long lit1_r, lit2_r, lit3_r;
+}
+lzo1x_999_t;
+
+#define getbyte(c) 	((c).ip < (c).in_end ? *((c).ip)++ : (-1))
+
+/* lzo_swd.ch */
+
+#define SWD_UINT(x)			((swd_uint)(x))
+#define SWD_HSIZE			16384
+#define SWD_MAX_CHAIN		2048
+#define HEAD3(b,p) \
+	(((0x9f5f*(((((lzo_uint32)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1))
+#define HEAD2(b,p)      (b[p] ^ ((unsigned)b[p+1]<<8))
+#define NIL2				SWD_UINT_MAX
+
+typedef struct
+{
+	lzo_uint n;
+	lzo_uint f;
+	lzo_uint threshold;
+
+	lzo_uint max_chain;
+	lzo_uint nice_length;
+	lzo_bool use_best_off;
+	lzo_uint lazy_insert;
+
+	lzo_uint m_len;
+	lzo_uint m_off;
+	lzo_uint look;
+	int b_char;
+
+	lzo_uint best_off[SWD_BEST_OFF];
+
+	lzo1x_999_t *c;
+	lzo_uint m_pos;
+
+	lzo_uint best_pos[SWD_BEST_OFF];
+
+	const lzo_byte *dict;
+	const lzo_byte *dict_end;
+	lzo_uint dict_len;
+
+	lzo_uint ip;
+	lzo_uint bp;
+	lzo_uint rp;
+	lzo_uint b_size;
+
+	unsigned char *b_wrap;
+
+	lzo_uint node_count;
+	lzo_uint first_rp;
+
+	unsigned char b[SWD_N + SWD_F + SWD_F];
+	swd_uint head3[SWD_HSIZE];
+	swd_uint succ3[SWD_N + SWD_F];
+	swd_uint best3[SWD_N + SWD_F];
+	swd_uint llen3[SWD_HSIZE];
+
+	swd_uint head2[65536L];
+}
+lzo1x_999_swd_t;
+
+#define s_head3(s,key)		s->head3[key]
+#define swd_pos2off(s,pos) \
+	(s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp))
+
+static __inline__ void
+swd_getbyte (lzo1x_999_swd_t * s)
+{
+	int c;
+
+	if ((c = getbyte (*(s->c))) < 0)
+	{
+		if (s->look > 0)
+			--s->look;
+	}
+	else
+	{
+		s->b[s->ip] = LZO_BYTE (c);
+		if (s->ip < s->f)
+			s->b_wrap[s->ip] = LZO_BYTE (c);
+	}
+	if (++s->ip == s->b_size)
+		s->ip = 0;
+	if (++s->bp == s->b_size)
+		s->bp = 0;
+	if (++s->rp == s->b_size)
+		s->rp = 0;
+}
+
+static void
+swd_initdict (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
+{
+	s->dict = s->dict_end = NULL;
+	s->dict_len = 0;
+
+	if (!dict || dict_len <= 0)
+		return;
+	if (dict_len > s->n)
+	{
+		dict += dict_len - s->n;
+		dict_len = s->n;
+	}
+
+	s->dict = dict;
+	s->dict_len = dict_len;
+	s->dict_end = dict + dict_len;
+	memcpy (s->b, dict, dict_len);
+	s->ip = dict_len;
+}
+
+static void
+swd_insertdict (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint len)
+{
+	lzo_uint key;
+
+	s->node_count = s->n - len;
+	s->first_rp = node;
+
+	while (len-- > 0)
+	{
+		key = HEAD3 (s->b, node);
+		s->succ3[node] = s_head3 (s, key);
+		s->head3[key] = SWD_UINT (node);
+		s->best3[node] = SWD_UINT (s->f + 1);
+		s->llen3[key]++;
+
+		key = HEAD2 (s->b, node);
+		s->head2[key] = SWD_UINT (node);
+
+		node++;
+	}
+}
+
+static int
+swd_init (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len)
+{
+
+	s->n = SWD_N;
+	s->f = SWD_F;
+	s->threshold = SWD_THRESHOLD;
+
+
+
+	s->max_chain = SWD_MAX_CHAIN;
+	s->nice_length = SWD_F;
+	s->use_best_off = 0;
+	s->lazy_insert = 0;
+
+	s->b_size = s->n + s->f;
+	if (2 * s->f >= s->n || s->b_size + s->f >= NIL2)
+		return LZO_E_ERROR;
+	s->b_wrap = s->b + s->b_size;
+	s->node_count = s->n;
+
+	memset (s->llen3, 0, sizeof (s->llen3[0]) * SWD_HSIZE);
+	memset (s->head2, 0xff, sizeof (s->head2[0]) * 65536L);
+
+	s->ip = 0;
+	swd_initdict (s, dict, dict_len);
+	s->bp = s->ip;
+	s->first_rp = s->ip;
+
+	s->look = (lzo_uint) (s->c->in_end - s->c->ip);
+	if (s->look > 0)
+	{
+		if (s->look > s->f)
+			s->look = s->f;
+		memcpy (&s->b[s->ip], s->c->ip, s->look);
+		s->c->ip += s->look;
+		s->ip += s->look;
+	}
+
+	if (s->ip == s->b_size)
+		s->ip = 0;
+
+	if (s->look >= 2 && s->dict_len > 0)
+		swd_insertdict (s, 0, s->dict_len);
+
+	s->rp = s->first_rp;
+	if (s->rp >= s->node_count)
+		s->rp -= s->node_count;
+	else
+		s->rp += s->b_size - s->node_count;
+
+	return LZO_E_OK;
+}
+
+static __inline__ void
+swd_remove_node (lzo1x_999_swd_t * s, lzo_uint node)
+{
+	if (s->node_count == 0)
+	{
+		lzo_uint key;
+
+		key = HEAD3 (s->b, node);
+
+		--s->llen3[key];
+
+		key = HEAD2 (s->b, node);
+
+		if ((lzo_uint) s->head2[key] == node)
+			s->head2[key] = NIL2;
+	}
+	else
+		--s->node_count;
+}
+
+static void
+swd_accept (lzo1x_999_swd_t * s, lzo_uint n)
+{
+
+	while (n--)
+	{
+		lzo_uint key;
+
+		swd_remove_node (s, s->rp);
+
+		key = HEAD3 (s->b, s->bp);
+		s->succ3[s->bp] = s_head3 (s, key);
+		s->head3[key] = SWD_UINT (s->bp);
+		s->best3[s->bp] = SWD_UINT (s->f + 1);
+		s->llen3[key]++;
+
+		key = HEAD2 (s->b, s->bp);
+		s->head2[key] = SWD_UINT (s->bp);;
+
+		swd_getbyte (s);
+	}
+}
+
+static void
+swd_search (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint cnt)
+{
+	const unsigned char *p1;
+	const unsigned char *p2;
+	const unsigned char *px;
+
+	lzo_uint m_len = s->m_len;
+	const unsigned char *b = s->b;
+	const unsigned char *bp = s->b + s->bp;
+	const unsigned char *bx = s->b + s->bp + s->look;
+	unsigned char scan_end1;
+
+	scan_end1 = bp[m_len - 1];
+	for (; cnt-- > 0; node = s->succ3[node])
+	{
+		p1 = bp;
+		p2 = b + node;
+		px = bx;
+
+		if (p2[m_len - 1] == scan_end1 &&
+		    p2[m_len] == p1[m_len] &&
+		    p2[0] == p1[0] && p2[1] == p1[1])
+		{
+			lzo_uint i;
+
+			p1 += 2;
+			p2 += 2;
+			do
+			{
+			}
+			while (++p1 < px && *p1 == *++p2);
+
+			i = p1 - bp;
+
+			if (i < SWD_BEST_OFF)
+			{
+				if (s->best_pos[i] == 0)
+					s->best_pos[i] = node + 1;
+			}
+
+			if (i > m_len)
+			{
+				s->m_len = m_len = i;
+				s->m_pos = node;
+				if (m_len == s->look)
+					return;
+				if (m_len >= s->nice_length)
+					return;
+				if (m_len > (lzo_uint) s->best3[node])
+					return;
+				scan_end1 = bp[m_len - 1];
+			}
+		}
+	}
+}
+
+static lzo_bool
+swd_search2 (lzo1x_999_swd_t * s)
+{
+	lzo_uint key;
+
+	key = s->head2[HEAD2 (s->b, s->bp)];
+	if (key == NIL2)
+		return 0;
+
+	if (s->best_pos[2] == 0)
+		s->best_pos[2] = key + 1;
+
+	if (s->m_len < 2)
+	{
+		s->m_len = 2;
+		s->m_pos = key;
+	}
+	return 1;
+}
+
+static void
+swd_findbest (lzo1x_999_swd_t * s)
+{
+	lzo_uint key;
+	lzo_uint cnt, node;
+	lzo_uint len;
+
+	key = HEAD3 (s->b, s->bp);
+	node = s->succ3[s->bp] = s_head3 (s, key);
+	cnt = s->llen3[key]++;
+
+	if (cnt > s->max_chain && s->max_chain > 0)
+		cnt = s->max_chain;
+	s->head3[key] = SWD_UINT (s->bp);
+
+	s->b_char = s->b[s->bp];
+	len = s->m_len;
+	if (s->m_len >= s->look)
+	{
+		if (s->look == 0)
+			s->b_char = -1;
+		s->m_off = 0;
+		s->best3[s->bp] = SWD_UINT (s->f + 1);
+	}
+	else
+	{
+
+		if (swd_search2 (s))
+
+			if (s->look >= 3)
+				swd_search (s, node, cnt);
+		if (s->m_len > len)
+			s->m_off = swd_pos2off (s, s->m_pos);
+		s->best3[s->bp] = SWD_UINT (s->m_len);
+
+		if (s->use_best_off)
+		{
+			int i;
+			for (i = 2; i < SWD_BEST_OFF; i++)
+				if (s->best_pos[i] > 0)
+					s->best_off[i] =
+						swd_pos2off (s,
+							     s->best_pos[i] -
+							     1);
+				else
+					s->best_off[i] = 0;
+		}
+
+	}
+
+	swd_remove_node (s, s->rp);
+
+	key = HEAD2 (s->b, s->bp);
+	s->head2[key] = SWD_UINT (s->bp);
+
+}
+
+/* lzo_mchw.ch */
+
+static int
+init_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
+	    const lzo_byte * dict, lzo_uint dict_len, lzo_uint32 flags)
+{
+	int r;
+
+	c->init = 1;
+
+	s->c = c;
+
+	c->last_m_len = c->last_m_off = 0;
+
+	c->textsize = c->codesize = c->printcount = 0;
+	c->lit_bytes = c->match_bytes = c->rep_bytes = 0;
+	c->lazy = 0;
+
+	r = swd_init (s, dict, dict_len);
+	if (r != 0)
+		return r;
+
+	s->use_best_off = (flags & 1) ? 1 : 0;
+	return r;
+}
+
+static int
+find_match (lzo1x_999_t * c, lzo1x_999_swd_t * s,
+	    lzo_uint this_len, lzo_uint skip)
+{
+	if (skip > 0)
+	{
+		swd_accept (s, this_len - skip);
+		c->textsize += this_len - skip + 1;
+	}
+	else
+	{
+		c->textsize += this_len - skip;
+	}
+
+	s->m_len = 1;
+	s->m_len = 1;
+
+	if (s->use_best_off)
+		memset (s->best_pos, 0, sizeof (s->best_pos));
+
+	swd_findbest (s);
+	c->m_len = s->m_len;
+	c->m_off = s->m_off;
+
+	swd_getbyte (s);
+
+	if (s->b_char < 0)
+	{
+		c->look = 0;
+		c->m_len = 0;
+	}
+	else
+	{
+		c->look = s->look + 1;
+	}
+	c->bp = c->ip - c->look;
+
+	if (c->cb && c->textsize > c->printcount)
+	{
+		(*c->cb) (c->textsize, c->codesize);
+		c->printcount += 1024;
+	}
+
+	return LZO_E_OK;
+}
+
+/* lzo1x_9x.c */
+
+static lzo_byte *
+code_match (lzo1x_999_t * c, lzo_byte * op, lzo_uint m_len, lzo_uint m_off)
+{
+	lzo_uint x_len = m_len;
+	lzo_uint x_off = m_off;
+
+	c->match_bytes += m_len;
+
+	if (m_len == 2)
+	{
+		m_off -= 1;
+
+		*op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
+		*op++ = LZO_BYTE (m_off >> 2);
+
+		c->m1a_m++;
+	}
+
+	else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
+
+	{
+
+		m_off -= 1;
+		*op++ = LZO_BYTE (((m_len - 1) << 5) | ((m_off & 7) << 2));
+		*op++ = LZO_BYTE (m_off >> 3);
+		c->m2_m++;
+	}
+	else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET
+		 && c->r1_lit >= 4)
+	{
+		m_off -= 1 + M2_MAX_OFFSET;
+
+		*op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2));
+		*op++ = LZO_BYTE (m_off >> 2);
+
+		c->m1b_m++;
+	}
+	else if (m_off <= M3_MAX_OFFSET)
+	{
+		m_off -= 1;
+		if (m_len <= M3_MAX_LEN)
+			*op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
+		else
+		{
+			m_len -= M3_MAX_LEN;
+			*op++ = M3_MARKER | 0;
+			while (m_len > 255)
+			{
+				m_len -= 255;
+				*op++ = 0;
+			}
+			*op++ = LZO_BYTE (m_len);
+		}
+
+		*op++ = LZO_BYTE (m_off << 2);
+		*op++ = LZO_BYTE (m_off >> 6);
+
+		c->m3_m++;
+	}
+	else
+	{
+		lzo_uint k;
+
+		m_off -= 0x4000;
+		k = (m_off & 0x4000) >> 11;
+		if (m_len <= M4_MAX_LEN)
+			*op++ = LZO_BYTE (M4_MARKER | k | (m_len - 2));
+		else
+		{
+			m_len -= M4_MAX_LEN;
+			*op++ = LZO_BYTE (M4_MARKER | k | 0);
+			while (m_len > 255)
+			{
+				m_len -= 255;
+				*op++ = 0;
+			}
+			*op++ = LZO_BYTE (m_len);
+		}
+
+		*op++ = LZO_BYTE (m_off << 2);
+		*op++ = LZO_BYTE (m_off >> 6);
+
+		c->m4_m++;
+	}
+
+	c->last_m_len = x_len;
+	c->last_m_off = x_off;
+	return op;
+}
+
+static lzo_byte *
+STORE_RUN (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii, lzo_uint t)
+{
+	c->lit_bytes += t;
+
+	if (op == c->out && t <= 238)
+	{
+		*op++ = LZO_BYTE (17 + t);
+	}
+	else if (t <= 3)
+	{
+		op[-2] |= LZO_BYTE (t);
+
+		c->lit1_r++;
+	}
+	else if (t <= 18)
+	{
+		*op++ = LZO_BYTE (t - 3);
+		c->lit2_r++;
+	}
+	else
+	{
+		lzo_uint tt = t - 18;
+
+		*op++ = 0;
+		while (tt > 255)
+		{
+			tt -= 255;
+			*op++ = 0;
+		}
+		*op++ = LZO_BYTE (tt);
+		c->lit3_r++;
+	}
+	do
+		*op++ = *ii++;
+	while (--t > 0);
+
+	return op;
+}
+
+static lzo_byte *
+code_run (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii,
+	  lzo_uint lit, lzo_uint m_len)
+{
+	if (lit > 0)
+	{
+		op = STORE_RUN (c, op, ii, lit);
+		c->r1_m_len = m_len;
+		c->r1_lit = lit;
+	}
+	else
+	{
+		c->r1_m_len = 0;
+		c->r1_lit = 0;
+	}
+
+	return op;
+}
+
+static int
+len_of_coded_match (lzo_uint m_len, lzo_uint m_off, lzo_uint lit)
+{
+	int n = 4;
+
+	if (m_len < 2)
+		return -1;
+	if (m_len == 2)
+		return (m_off <= M1_MAX_OFFSET && lit > 0
+			&& lit < 4) ? 2 : -1;
+	if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET)
+		return 2;
+	if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4)
+		return 2;
+	if (m_off <= M3_MAX_OFFSET)
+	{
+		if (m_len <= M3_MAX_LEN)
+			return 3;
+		m_len -= M3_MAX_LEN;
+		while (m_len > 255)
+		{
+			m_len -= 255;
+			n++;
+		}
+		return n;
+	}
+	if (m_off <= M4_MAX_OFFSET)
+	{
+		if (m_len <= M4_MAX_LEN)
+			return 3;
+		m_len -= M4_MAX_LEN;
+		while (m_len > 255)
+		{
+			m_len -= 255;
+			n++;
+		}
+		return n;
+	}
+	return -1;
+}
+
+static lzo_int
+min_gain (lzo_uint ahead, lzo_uint lit1, lzo_uint lit2, int l1, int l2,
+	  int l3)
+{
+	lzo_int lazy_match_min_gain = 0;
+
+	lazy_match_min_gain += ahead;
+
+	if (lit1 <= 3)
+		lazy_match_min_gain += (lit2 <= 3) ? 0 : 2;
+	else if (lit1 <= 18)
+		lazy_match_min_gain += (lit2 <= 18) ? 0 : 1;
+
+	lazy_match_min_gain += (l2 - l1) * 2;
+	if (l3 > 0)
+		lazy_match_min_gain -= (ahead - l3) * 2;
+
+	if (lazy_match_min_gain < 0)
+		lazy_match_min_gain = 0;
+
+	return lazy_match_min_gain;
+}
+
+static void
+better_match (const lzo1x_999_swd_t * swd, lzo_uint * m_len, lzo_uint * m_off)
+{
+	if (*m_len <= M2_MIN_LEN)
+		return;
+
+	if (*m_off <= M2_MAX_OFFSET)
+		return;
+
+	if (*m_off > M2_MAX_OFFSET &&
+	    *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 &&
+	    swd->best_off[*m_len - 1]
+	    && swd->best_off[*m_len - 1] <= M2_MAX_OFFSET)
+	{
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+
+	if (*m_off > M3_MAX_OFFSET &&
+	    *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 &&
+	    swd->best_off[*m_len - 2]
+	    && swd->best_off[*m_len - 2] <= M2_MAX_OFFSET)
+	{
+		*m_len = *m_len - 2;
+		*m_off = swd->best_off[*m_len];
+		return;
+	}
+
+	if (*m_off > M3_MAX_OFFSET &&
+	    *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 &&
+	    swd->best_off[*m_len - 1]
+	    && swd->best_off[*m_len - 1] <= M3_MAX_OFFSET)
+	{
+		*m_len = *m_len - 1;
+		*m_off = swd->best_off[*m_len];
+	}
+
+}
+
+/* minilzo.c */
+
+static lzo_bool
+lzo_assert (int expr)
+{
+	return (expr) ? 1 : 0;
+}
+
+/* lzo1x_9x.c */
+
+static int
+lzo1x_999_compress_internal (const lzo_byte * in, lzo_uint in_len,
+			     lzo_byte * out, lzo_uintp out_len,
+			     lzo_voidp wrkmem,
+			     const lzo_byte * dict, lzo_uint dict_len,
+			     lzo_progress_callback_t cb,
+			     int try_lazy,
+			     lzo_uint good_length,
+			     lzo_uint max_lazy,
+			     lzo_uint nice_length,
+			     lzo_uint max_chain, lzo_uint32 flags)
+{
+	lzo_byte *op;
+	const lzo_byte *ii;
+	lzo_uint lit;
+	lzo_uint m_len, m_off;
+	lzo1x_999_t cc;
+	lzo1x_999_t *const c = &cc;
+	lzo1x_999_swd_t *const swd = (lzo1x_999_swd_t *) wrkmem;
+	int r;
+
+	if (!lzo_assert
+	    (LZO1X_999_MEM_COMPRESS >= lzo_sizeof (lzo1x_999_swd_t)))
+		return LZO_E_ERROR;
+
+	if (try_lazy < 0)
+		try_lazy = 1;
+
+	if (good_length <= 0)
+		good_length = 32;
+
+	if (max_lazy <= 0)
+		max_lazy = 32;
+
+	if (nice_length <= 0)
+		nice_length = 0;
+
+	if (max_chain <= 0)
+		max_chain = SWD_MAX_CHAIN;
+
+	c->init = 0;
+	c->ip = c->in = in;
+	c->in_end = in + in_len;
+	c->out = out;
+	c->cb = cb;
+	c->m1a_m = c->m1b_m = c->m2_m = c->m3_m = c->m4_m = 0;
+	c->lit1_r = c->lit2_r = c->lit3_r = 0;
+
+	op = out;
+	ii = c->ip;
+	lit = 0;
+	c->r1_lit = c->r1_m_len = 0;
+
+	r = init_match (c, swd, dict, dict_len, flags);
+	if (r != 0)
+		return r;
+	if (max_chain > 0)
+		swd->max_chain = max_chain;
+	if (nice_length > 0)
+		swd->nice_length = nice_length;
+
+	r = find_match (c, swd, 0, 0);
+	if (r != 0)
+		return r;
+	while (c->look > 0)
+	{
+		lzo_uint ahead;
+		lzo_uint max_ahead;
+		int l1, l2, l3;
+
+		c->codesize = op - out;
+
+		m_len = c->m_len;
+		m_off = c->m_off;
+
+		if (lit == 0)
+			ii = c->bp;
+
+		if (m_len < 2 ||
+		    (m_len == 2
+		     && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4))
+		    || (m_len == 2 && op == out) || (op == out && lit == 0))
+		{
+
+			m_len = 0;
+		}
+		else if (m_len == M2_MIN_LEN)
+		{
+
+			if (m_off > MX_MAX_OFFSET && lit >= 4)
+				m_len = 0;
+		}
+
+		if (m_len == 0)
+		{
+
+			lit++;
+			swd->max_chain = max_chain;
+			r = find_match (c, swd, 1, 0);
+			continue;
+		}
+
+		if (swd->use_best_off)
+			better_match (swd, &m_len, &m_off);
+
+		ahead = 0;
+		if (try_lazy <= 0 || m_len >= max_lazy)
+		{
+
+			l1 = 0;
+			max_ahead = 0;
+		}
+		else
+		{
+
+			l1 = len_of_coded_match (m_len, m_off, lit);
+
+			max_ahead = LZO_MIN (try_lazy, l1 - 1);
+
+		}
+
+		while (ahead < max_ahead && c->look > m_len)
+		{
+			lzo_int lazy_match_min_gain;
+
+			if (m_len >= good_length)
+				swd->max_chain = max_chain >> 2;
+			else
+				swd->max_chain = max_chain;
+			r = find_match (c, swd, 1, 0);
+			ahead++;
+
+			if (c->m_len < m_len)
+				continue;
+
+			if (c->m_len == m_len && c->m_off >= m_off)
+				continue;
+
+			if (swd->use_best_off)
+				better_match (swd, &c->m_len, &c->m_off);
+
+			l2 = len_of_coded_match (c->m_len, c->m_off,
+						 lit + ahead);
+			if (l2 < 0)
+				continue;
+
+			l3 = (op == out) ? -1 : len_of_coded_match (ahead,
+								    m_off,
+								    lit);
+
+			lazy_match_min_gain =
+				min_gain (ahead, lit, lit + ahead, l1, l2,
+					  l3);
+			if (c->m_len >= m_len + lazy_match_min_gain)
+			{
+				c->lazy++;
+
+				if (l3 > 0)
+				{
+
+					op = code_run (c, op, ii, lit, ahead);
+					lit = 0;
+
+					op = code_match (c, op, ahead, m_off);
+				}
+				else
+				{
+					lit += ahead;
+				}
+				goto lazy_match_done;
+			}
+		}
+
+		op = code_run (c, op, ii, lit, m_len);
+		lit = 0;
+
+		op = code_match (c, op, m_len, m_off);
+		swd->max_chain = max_chain;
+		r = find_match (c, swd, m_len, 1 + ahead);
+
+	      lazy_match_done:;
+	}
+
+	if (lit > 0)
+		op = STORE_RUN (c, op, ii, lit);
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	c->codesize = op - out;
+
+	*out_len = op - out;
+
+	if (c->cb)
+		(*c->cb) (c->textsize, c->codesize);
+
+	return LZO_E_OK;
+}
+
+static int
+lzo1x_999_compress_level (const lzo_byte * in, lzo_uint in_len,
+			  lzo_byte * out, lzo_uintp out_len,
+			  lzo_voidp wrkmem,
+			  const lzo_byte * dict, lzo_uint dict_len,
+			  lzo_progress_callback_t cb, int compression_level)
+{
+	static const struct
+	{
+		int try_lazy;
+		lzo_uint good_length;
+		lzo_uint max_lazy;
+		lzo_uint nice_length;
+		lzo_uint max_chain;
+		lzo_uint32 flags;
+	} c[9] =
+	{
+		{
+		0, 0, 0, 8, 4, 0},
+		{
+		0, 0, 0, 16, 8, 0},
+		{
+		0, 0, 0, 32, 16, 0},
+		{
+		1, 4, 4, 16, 16, 0},
+		{
+		1, 8, 16, 32, 32, 0},
+		{
+		1, 8, 16, 128, 128, 0},
+		{
+		2, 8, 32, 128, 256, 0},
+		{
+		2, 32, 128, F, 2048, 1},
+		{
+		2, F, F, F, 4096, 1}
+	};
+
+	if (compression_level < 1 || compression_level > 9)
+		return LZO_E_ERROR;
+
+	compression_level -= 1;
+	return lzo1x_999_compress_internal (in, in_len, out, out_len, wrkmem,
+					    dict, dict_len, cb,
+					    c[compression_level].try_lazy,
+					    c[compression_level].good_length,
+					    c[compression_level].max_lazy,
+					    0,
+					    c[compression_level].max_chain,
+					    c[compression_level].flags);
+}
+
+static int
+lzo1x_999_compress (const lzo_byte * in, lzo_uint in_len,
+		    lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	return lzo1x_999_compress_level (in, in_len, out, out_len, wrkmem,
+					 NULL, 0, 0, 8);
+}
+
+/* minilzo.c */
+
+static const lzo_byte __lzo_copyright[] = LZO_VERSION_STRING;
+
+static lzo_uint
+_lzo1x_1_do_compress (const lzo_byte * in, lzo_uint in_len,
+		      lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+
+	register const lzo_byte *ip;
+
+	lzo_byte *op;
+	const lzo_byte *const in_end = in + in_len;
+	const lzo_byte *const ip_end = in + in_len - 8 - 5;
+	const lzo_byte *ii;
+	lzo_dict_p const dict = (lzo_dict_p) wrkmem;
+
+	op = out;
+	ip = in;
+	ii = ip;
+
+	ip += 4;
+	for (;;)
+	{
+		register const lzo_byte *m_pos;
+
+		lzo_uint m_off;
+		lzo_uint m_len;
+		lzo_uint dindex;
+
+		DINDEX1 (dindex, ip);
+		GINDEX (m_pos, m_off, dict, dindex, in);
+		if (LZO_CHECK_MPOS_NON_DET
+		    (m_pos, m_off, in, ip, M4_MAX_OFFSET))
+			goto literal;
+
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		DINDEX2 (dindex, ip);
+		GINDEX (m_pos, m_off, dict, dindex, in);
+
+		if (LZO_CHECK_MPOS_NON_DET
+		    (m_pos, m_off, in, ip, M4_MAX_OFFSET))
+			goto literal;
+		if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3])
+			goto try_match;
+		goto literal;
+
+	      try_match:
+		if (m_pos[0] != ip[0] || m_pos[1] != ip[1])
+		{
+		}
+		else
+		{
+			if (m_pos[2] == ip[2])
+			{
+				goto match;
+			}
+			else
+			{
+			}
+		}
+
+	      literal:
+		UPDATE_I (dict, 0, dindex, ip, in);
+		++ip;
+		if (ip >= ip_end)
+			break;
+		continue;
+
+	      match:
+		UPDATE_I (dict, 0, dindex, ip, in);
+
+		if (pd (ip, ii) > 0)
+		{
+			register lzo_uint t = pd (ip, ii);
+
+			if (t <= 3)
+			{
+				op[-2] |= LZO_BYTE (t);
+			}
+			else if (t <= 18)
+				*op++ = LZO_BYTE (t - 3);
+			else
+			{
+				register lzo_uint tt = t - 18;
+
+				*op++ = 0;
+				while (tt > 255)
+				{
+					tt -= 255;
+					*op++ = 0;
+				}
+				*op++ = LZO_BYTE (tt);;
+			}
+			do
+				*op++ = *ii++;
+			while (--t > 0);
+		}
+
+		ip += 3;
+		if (m_pos[3] != *ip++ || m_pos[4] != *ip++
+		    || m_pos[5] != *ip++ || m_pos[6] != *ip++
+		    || m_pos[7] != *ip++ || m_pos[8] != *ip++)
+		{
+			--ip;
+			m_len = ip - ii;
+
+			if (m_off <= M2_MAX_OFFSET)
+			{
+				m_off -= 1;
+
+				*op++ = LZO_BYTE (((m_len -
+						    1) << 5) | ((m_off & 7) <<
+								2));
+				*op++ = LZO_BYTE (m_off >> 3);
+			}
+			else if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				*op++ = LZO_BYTE (M3_MARKER | (m_len - 2));
+				goto m3_m4_offset;
+			}
+			else
+
+			{
+				m_off -= 0x4000;
+
+				*op++ = LZO_BYTE (M4_MARKER |
+						  ((m_off & 0x4000) >> 11) |
+						  (m_len - 2));
+				goto m3_m4_offset;
+			}
+		}
+		else
+		{
+			{
+				const lzo_byte *end = in_end;
+				const lzo_byte *m = m_pos + M2_MAX_LEN + 1;
+				while (ip < end && *m == *ip)
+					m++, ip++;
+				m_len = (ip - ii);
+			}
+
+
+			if (m_off <= M3_MAX_OFFSET)
+			{
+				m_off -= 1;
+				if (m_len <= 33)
+					*op++ = LZO_BYTE (M3_MARKER |
+							  (m_len - 2));
+				else
+				{
+					m_len -= 33;
+					*op++ = M3_MARKER | 0;
+					goto m3_m4_len;
+				}
+			}
+			else
+			{
+				m_off -= 0x4000;
+
+				if (m_len <= M4_MAX_LEN)
+					*op++ = LZO_BYTE (M4_MARKER |
+							  ((m_off & 0x4000) >>
+							   11) | (m_len - 2));
+
+				else
+				{
+					m_len -= M4_MAX_LEN;
+					*op++ = LZO_BYTE (M4_MARKER |
+							  ((m_off & 0x4000) >>
+							   11));
+				      m3_m4_len:
+					while (m_len > 255)
+					{
+						m_len -= 255;
+						*op++ = 0;
+					}
+
+					*op++ = LZO_BYTE (m_len);
+				}
+			}
+
+		      m3_m4_offset:
+			*op++ = LZO_BYTE ((m_off & 63) << 2);
+			*op++ = LZO_BYTE (m_off >> 6);
+		}
+		ii = ip;
+		if (ip >= ip_end)
+			break;
+	}
+
+	*out_len = op - out;
+	return pd (in_end, ii);
+}
+
+static int
+lzo1x_1_compress (const lzo_byte * in, lzo_uint in_len,
+		  lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	lzo_byte *op = out;
+	lzo_uint t;
+
+	if (in_len <= M2_MAX_LEN + 5)
+		t = in_len;
+	else
+	{
+		t = _lzo1x_1_do_compress (in, in_len, op, out_len, wrkmem);
+		op += *out_len;
+	}
+
+	if (t > 0)
+	{
+		const lzo_byte *ii = in + in_len - t;
+
+		if (op == out && t <= 238)
+			*op++ = LZO_BYTE (17 + t);
+		else if (t <= 3)
+			op[-2] |= LZO_BYTE (t);
+		else if (t <= 18)
+			*op++ = LZO_BYTE (t - 3);
+		else
+		{
+			lzo_uint tt = t - 18;
+
+			*op++ = 0;
+			while (tt > 255)
+			{
+				tt -= 255;
+				*op++ = 0;
+			}
+
+			*op++ = LZO_BYTE (tt);
+		}
+		do
+			*op++ = *ii++;
+		while (--t > 0);
+	}
+
+	*op++ = M4_MARKER | 1;
+	*op++ = 0;
+	*op++ = 0;
+
+	*out_len = op - out;
+	return 0;
+}
+
+static int
+lzo1x_decompress (const lzo_byte * in, lzo_uint in_len,
+		  lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	register lzo_byte *op;
+	register const lzo_byte *ip;
+	register lzo_uint t;
+
+	register const lzo_byte *m_pos;
+
+	const lzo_byte *const ip_end = in + in_len;
+	lzo_byte *const op_end = out + *out_len;
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17)
+	{
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		NEED_OP (t);
+		NEED_IP (t + 1);
+		do
+			*op++ = *ip++;
+		while (--t > 0);
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP)
+	{
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		if (t == 0)
+		{
+			NEED_IP (1);
+			while (*ip == 0)
+			{
+				t += 255;
+				ip++;
+				NEED_IP (1);
+			}
+			t += 15 + *ip++;
+		}
+		NEED_OP (t + 3);
+		NEED_IP (t + 4);
+		if (PTR_ALIGNED2_4 (op, ip))
+		{
+			COPY4 (op, ip);
+
+			op += 4;
+			ip += 4;
+			if (--t > 0)
+			{
+				if (t >= 4)
+				{
+					do
+					{
+						COPY4 (op, ip);
+						op += 4;
+						ip += 4;
+						t -= 4;
+					}
+					while (t >= 4);
+					if (t > 0)
+						do
+							*op++ = *ip++;
+						while (--t > 0);
+				}
+				else
+					do
+						*op++ = *ip++;
+					while (--t > 0);
+			}
+		}
+		else
+		{
+			*op++ = *ip++;
+			*op++ = *ip++;
+			*op++ = *ip++;
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+		}
+	      first_literal_run:
+
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+
+		m_pos = op - (1 + M2_MAX_OFFSET);
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+		TEST_LOOKBEHIND (m_pos, out);
+		NEED_OP (3);
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos;
+
+		goto match_done;
+
+		while (TEST_IP && TEST_OP)
+		{
+		      match:
+			if (t >= 64)
+			{
+				m_pos = op - 1;
+				m_pos -= (t >> 2) & 7;
+				m_pos -= *ip++ << 3;
+				t = (t >> 5) - 1;
+				TEST_LOOKBEHIND (m_pos, out);
+				NEED_OP (t + 3 - 1);
+				goto copy_match;
+
+			}
+			else if (t >= 32)
+			{
+				t &= 31;
+				if (t == 0)
+				{
+					NEED_IP (1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP (1);
+					}
+					t += 31 + *ip++;
+				}
+
+				m_pos = op - 1;
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+				ip += 2;
+			}
+			else if (t >= 16)
+			{
+				m_pos = op;
+				m_pos -= (t & 8) << 11;
+
+				t &= 7;
+				if (t == 0)
+				{
+					NEED_IP (1);
+					while (*ip == 0)
+					{
+						t += 255;
+						ip++;
+						NEED_IP (1);
+					}
+					t += 7 + *ip++;
+				}
+
+				m_pos -= (ip[0] >> 2) + (ip[1] << 6);
+
+				ip += 2;
+				if (m_pos == op)
+					goto eof_found;
+				m_pos -= 0x4000;
+			}
+			else
+			{
+
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+				TEST_LOOKBEHIND (m_pos, out);
+				NEED_OP (2);
+				*op++ = *m_pos++;
+				*op++ = *m_pos;
+
+				goto match_done;
+			}
+
+			TEST_LOOKBEHIND (m_pos, out);
+			NEED_OP (t + 3 - 1);
+			if (t >= 2 * 4 - (3 - 1)
+			    && PTR_ALIGNED2_4 (op, m_pos))
+			{
+				COPY4 (op, m_pos);
+				op += 4;
+				m_pos += 4;
+				t -= 4 - (3 - 1);
+				do
+				{
+					COPY4 (op, m_pos);
+					op += 4;
+					m_pos += 4;
+					t -= 4;
+				}
+				while (t >= 4);
+				if (t > 0)
+					do
+						*op++ = *m_pos++;
+					while (--t > 0);
+			}
+			else
+
+			{
+			      copy_match:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+				do
+					*op++ = *m_pos++;
+				while (--t > 0);
+			}
+
+		      match_done:
+			t = ip[-2] & 3;
+
+			if (t == 0)
+				break;
+
+		      match_next:
+			NEED_OP (t);
+			NEED_IP (t + 1);
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+			t = *ip++;
+		}
+	}
+	*out_len = op - out;
+	return LZO_E_EOF_NOT_FOUND;
+
+      eof_found:
+	*out_len = op - out;
+	return (ip == ip_end ? LZO_E_OK :
+		(ip <
+		 ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+
+      input_overrun:
+	*out_len = op - out;
+	return LZO_E_INPUT_OVERRUN;
+
+      output_overrun:
+	*out_len = op - out;
+	return LZO_E_OUTPUT_OVERRUN;
+
+      lookbehind_overrun:
+	*out_len = op - out;
+	return LZO_E_LOOKBEHIND_OVERRUN;
+}
+
+/* lzo1x_oo.ch */
+
+#define NO_LIT          LZO_UINT_MAX
+
+static void
+copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1)
+		ip[1] = m_pos[0];
+	else
+		ip[1] = m_pos[1];
+}
+
+static void
+copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off)
+{
+	ip[0] = m_pos[0];
+	if (off == 1)
+	{
+		ip[2] = ip[1] = m_pos[0];
+	}
+	else if (off == 2)
+	{
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[0];
+	}
+	else
+	{
+		ip[1] = m_pos[1];
+		ip[2] = m_pos[2];
+	}
+}
+
+static int
+lzo1x_optimize (lzo_byte * in, lzo_uint in_len,
+		lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem)
+{
+	register lzo_byte *op;
+	register lzo_byte *ip;
+	register lzo_uint t;
+	register lzo_byte *m_pos;
+	lzo_uint nl;
+	const lzo_byte *const ip_end = in + in_len;
+	const lzo_byte *const op_end = out + *out_len;
+	lzo_byte *litp = NULL;
+	lzo_uint lit = 0;
+	lzo_uint next_lit = NO_LIT;
+	long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0;
+
+	*out_len = 0;
+
+	op = out;
+	ip = in;
+
+	if (*ip > 17)
+	{
+		t = *ip++ - 17;
+		if (t < 4)
+			goto match_next;
+		goto first_literal_run;
+	}
+
+	while (TEST_IP && TEST_OP)
+	{
+		t = *ip++;
+		if (t >= 16)
+			goto match;
+		litp = ip - 1;
+		if (t == 0)
+		{
+			t = 15;
+			while (*ip == 0)
+				t += 255, ip++;
+			t += *ip++;
+		}
+		lit = t + 3;
+	      copy_literal_run:
+		*op++ = *ip++;
+		*op++ = *ip++;
+		*op++ = *ip++;
+	      first_literal_run:
+		do
+			*op++ = *ip++;
+		while (--t > 0);
+
+		t = *ip++;
+
+		if (t >= 16)
+			goto match;
+		m_pos = op - 1 - 0x800;
+		m_pos -= t >> 2;
+		m_pos -= *ip++ << 2;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		*op++ = *m_pos++;
+		lit = 0;
+		goto match_done;
+
+		while (TEST_IP && TEST_OP)
+		{
+			if (t < 16)
+			{
+				m_pos = op - 1;
+				m_pos -= t >> 2;
+				m_pos -= *ip++ << 2;
+
+				if (litp == NULL)
+					goto copy_m1;
+
+				nl = ip[-2] & 3;
+				if (nl == 0 && lit == 1 && ip[0] >= 16)
+				{
+					next_lit = nl;
+					lit += 2;
+					*litp = LZO_BYTE ((*litp & ~3) | lit);
+					copy2 (ip - 2, m_pos, op - m_pos);
+					o_m1_a++;
+				}
+				else if (nl == 0 && ip[0] < 16 && ip[0] != 0
+					 && (lit + 2 + ip[0] < 16))
+				{
+					t = *ip++;
+					*litp &= ~3;
+					copy2 (ip - 3 + 1, m_pos, op - m_pos);
+					litp += 2;
+					if (lit > 0)
+						memmove (litp + 1, litp, lit);
+					lit += 2 + t + 3;
+					*litp = LZO_BYTE (lit - 3);
+
+					o_m1_b++;
+					*op++ = *m_pos++;
+					*op++ = *m_pos++;
+					goto copy_literal_run;
+				}
+			      copy_m1:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+			}
+			else
+			{
+			      match:
+				if (t >= 64)
+				{
+					m_pos = op - 1;
+					m_pos -= (t >> 2) & 7;
+					m_pos -= *ip++ << 3;
+					t = (t >> 5) - 1;
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					if (t == 1 && lit > 3 && nl == 0 &&
+					    ip[0] < 16 && ip[0] != 0
+					    && (lit + 3 + ip[0] < 16))
+					{
+						t = *ip++;
+						copy3 (ip - 1 - 2, m_pos,
+						       op - m_pos);
+						lit += 3 + t + 3;
+						*litp = LZO_BYTE (lit - 3);
+						o_m2++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				}
+				else
+				{
+					if (t >= 32)
+					{
+						t &= 31;
+						if (t == 0)
+						{
+							t = 31;
+							while (*ip == 0)
+								t += 255,
+									ip++;
+							t += *ip++;
+						}
+						m_pos = op - 1;
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+					}
+					else
+					{
+						m_pos = op;
+						m_pos -= (t & 8) << 11;
+						t &= 7;
+						if (t == 0)
+						{
+							t = 7;
+							while (*ip == 0)
+								t += 255,
+									ip++;
+							t += *ip++;
+						}
+						m_pos -= *ip++ >> 2;
+						m_pos -= *ip++ << 6;
+						if (m_pos == op)
+							goto eof_found;
+						m_pos -= 0x4000;
+					}
+					if (litp == NULL)
+						goto copy_m;
+
+					nl = ip[-2] & 3;
+					if (t == 1 && lit == 0 && nl == 0
+					    && ip[0] >= 16)
+					{
+						next_lit = nl;
+						lit += 3;
+						*litp = LZO_BYTE ((*litp & ~3)
+								  | lit);
+						copy3 (ip - 3, m_pos,
+						       op - m_pos);
+						o_m3_a++;
+					}
+					else if (t == 1 && lit <= 3 && nl == 0
+						 && ip[0] < 16 && ip[0] != 0
+						 && (lit + 3 + ip[0] < 16))
+					{
+						t = *ip++;
+						*litp &= ~3;
+						copy3 (ip - 4 + 1, m_pos,
+						       op - m_pos);
+						litp += 2;
+						if (lit > 0)
+							memmove (litp + 1,
+								 litp, lit);
+						lit += 3 + t + 3;
+						*litp = LZO_BYTE (lit - 3);
+
+						o_m3_b++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						*op++ = *m_pos++;
+						goto copy_literal_run;
+					}
+				}
+			      copy_m:
+				*op++ = *m_pos++;
+				*op++ = *m_pos++;
+				do
+					*op++ = *m_pos++;
+				while (--t > 0);
+			}
+
+		      match_done:
+			if (next_lit == NO_LIT)
+			{
+				t = ip[-2] & 3;
+				lit = t;
+				litp = ip - 2;
+			}
+			else
+				t = next_lit;
+			next_lit = NO_LIT;
+			if (t == 0)
+				break;
+		      match_next:
+			do
+				*op++ = *ip++;
+			while (--t > 0);
+			t = *ip++;
+		}
+	}
+
+	*out_len = op - out;
+	return LZO_E_EOF_NOT_FOUND;
+
+      eof_found:
+	*out_len = op - out;
+	return (ip == ip_end ? LZO_E_OK :
+		(ip <
+		 ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN));
+}
+
+/* interface to jffs2 follows */
+
+#include "compr.h"
+#include <linux/jffs2.h>
+
+#define BLOCKSIZE		JFFS2_PAGE_SIZE
+#define OUTBLOCKSIZE	(BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)
+
+int jffs2_lzo_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen);
+
+int jffs2_lzo_estimate (uint32_t *est_rtime, uint32_t *est_wtime, uint32_t *est_size,
+			unsigned char *cdata_in, unsigned char *data_out,
+			uint32_t cdatalen, uint32_t datalen);
+
+int jffs2_lzo_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen);
+
+static struct jffs2_compressor jffs2_lzo_comp = {
+	.priority = JFFS2_LZO_PRIORITY,
+	.name = "lzo",
+	.compr = JFFS2_COMPR_LZO,
+	.compress = &jffs2_lzo_compress,
+	.decompress = &jffs2_lzo_decompress,
+#ifdef JFFS2_LZO_DISABLED
+	.disabled = 1,
+#else
+	.disabled = 0,
+#endif
+};
+
+static int
+no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len,
+		   lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem)
+{
+	return 0;
+}
+
+static lzo_compress_t lzo1x_compressor = lzo1x_1_compress;
+static lzo_optimize_t lzo1x_optimizer = no_lzo1x_optimize;
+static int lzo1x_compressor_type = 1;
+static int lzo1x_optimize_type = 0;
+static unsigned long lzo1x_compressor_memsize = LZO1X_1_MEM_COMPRESS;
+
+static lzo_bytep wrkmem = NULL;	/* temporary buffer for compression, used by lzo */
+static lzo_bytep cmprssmem = NULL;	/* temporary buffer for compression, used by interface */
+
+int jffs2_lzo_compress (unsigned char *input,
+			unsigned char *output, uint32_t *sourcelen,
+			uint32_t *dstlen)
+{
+	lzo_uint csize = BLOCKSIZE;
+	lzo_uint isize = *sourcelen;
+	int retval;
+	if ((retval =
+	     lzo1x_compressor (input, *sourcelen, cmprssmem, &csize,
+			       wrkmem)) != LZO_E_OK)
+	{
+		return retval;
+	}
+	else
+	{
+		retval = lzo1x_optimizer (cmprssmem, csize, input, &isize,
+					  NULL);
+		if (csize <= *dstlen) {
+			*dstlen = csize;
+			memcpy (output, cmprssmem, csize);
+			return retval;
+		} else {
+			return -1;
+		}
+	}
+}
+
+int jffs2_lzo_estimate (uint32_t *est_rtime, uint32_t *est_wtime, uint32_t *est_size,
+			unsigned char *cdata_in, unsigned char *data_out,
+			uint32_t cdatalen, uint32_t datalen)
+{
+	return 0;
+}
+
+int jffs2_lzo_decompress (unsigned char *input,
+			  unsigned char *output, uint32_t sourcelen,
+			  uint32_t dstlen)
+{
+	lzo_uint outlen = dstlen;
+	return lzo1x_decompress (input, sourcelen, output, &outlen, NULL);
+}
+
+int jffs2_lzo_init (void)
+{
+    wrkmem = (lzo_bytep) jffs2_malloc (lzo1x_compressor_memsize);
+    if (!wrkmem) return -1;
+    cmprssmem = (lzo_bytep) jffs2_malloc (OUTBLOCKSIZE);
+    if (!cmprssmem) {
+      jffs2_free(wrkmem);
+      return -1;
+    }
+    return jffs2_register_compressor(&jffs2_lzo_comp);
+}
+
+void jffs2_lzo_exit (void)
+{
+	jffs2_free (wrkmem);
+	jffs2_free (cmprssmem);
+	jffs2_unregister_compressor (&jffs2_lzo_comp);
+}
diff --unified --recursive --new-file mtd/fs/jffs2/compr_rtime.c mtd-new-4/fs/jffs2/compr_rtime.c
--- mtd/fs/jffs2/compr_rtime.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/compr_rtime.c	2004-04-20 12:13:22.000000000 +0200
@@ -25,6 +25,8 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/string.h> 
+#include <linux/jffs2.h> 
+#include "compr.h"
 
 /* _compress returns the compressed size, -1 if bigger */
 int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
@@ -67,7 +69,7 @@
 }		   
 
 
-void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
 		      uint32_t srclen, uint32_t destlen)
 {
 	short positions[256];
@@ -98,7 +100,29 @@
 				outpos+=repeat;		
 			}
 		}
-	}		
+	}
+        return 0;
 }		   
 
+static struct jffs2_compressor jffs2_rtime_comp = {
+    .priority = JFFS2_RTIME_PRIORITY,
+    .name = "rtime",
+    .compr = JFFS2_COMPR_RTIME,
+    .compress = &jffs2_rtime_compress,
+    .decompress = &jffs2_rtime_decompress,
+#ifdef JFFS2_RTIME_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rtime_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rtime_comp);
+}
 
+void jffs2_rtime_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
diff --unified --recursive --new-file mtd/fs/jffs2/compr_rubin.c mtd-new-4/fs/jffs2/compr_rubin.c
--- mtd/fs/jffs2/compr_rubin.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/compr_rubin.c	2004-04-20 13:28:39.000000000 +0200
@@ -14,10 +14,10 @@
  
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/jffs2.h>
 #include "compr_rubin.h"
 #include "histo_mips.h"
-
-
+#include "compr.h"
 
 static void init_rubin(struct rubin_state *rs, int div, int *bits)
 {	
@@ -306,13 +306,14 @@
 }		   
 
 
-void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
 		   uint32_t sourcelen, uint32_t dstlen)
 {
 	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+        return 0;
 }
 
-void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
 		   uint32_t sourcelen, uint32_t dstlen)
 {
 	int bits[8];
@@ -322,4 +323,51 @@
 		bits[c] = data_in[c];
 
 	rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_rubinmips_comp = {
+    .priority = JFFS2_RUBINMIPS_PRIORITY,
+    .name = "rubinmips",
+    .compr = JFFS2_COMPR_DYNRUBIN,
+    .compress = NULL, /*&jffs2_rubinmips_compress,*/
+    .decompress = &jffs2_rubinmips_decompress,
+#ifdef JFFS2_RUBINMIPS_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rubinmips_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
+void jffs2_rubinmips_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
+static struct jffs2_compressor jffs2_dynrubin_comp = {
+    .priority = JFFS2_DYNRUBIN_PRIORITY,
+    .name = "dynrubin",
+    .compr = JFFS2_COMPR_RUBINMIPS,
+    .compress = jffs2_dynrubin_compress,
+    .decompress = &jffs2_dynrubin_decompress,
+#ifdef JFFS2_DYNRUBIN_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_dynrubin_init(void)
+{
+    return jffs2_register_compressor(&jffs2_dynrubin_comp);
+}
+
+void jffs2_dynrubin_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
 }
diff --unified --recursive --new-file mtd/fs/jffs2/compr_zlib.c mtd-new-4/fs/jffs2/compr_zlib.c
--- mtd/fs/jffs2/compr_zlib.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/compr_zlib.c	2004-04-20 13:21:40.000000000 +0200
@@ -22,6 +22,7 @@
 #include <linux/zutil.h>
 #include <asm/semaphore.h>
 #include "nodelist.h"
+#include "compr.h"
 
 	/* Plan: call deflate() with avail_in == *sourcelen, 
 		avail_out = *dstlen - 12 and flush == Z_FINISH. 
@@ -40,7 +41,7 @@
 #include <linux/vmalloc.h>
 #include <linux/init.h>
 
-int __init jffs2_zlib_init(void)
+static int __init alloc_workspaces(void)
 {
 	def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
 	if (!def_strm.workspace) {
@@ -58,11 +59,14 @@
 	return 0;
 }
 
-void jffs2_zlib_exit(void)
+static void free_workspaces(void)
 {
 	vfree(def_strm.workspace);
 	vfree(inf_strm.workspace);
 }
+#else
+#define alloc_workspaces() (0)
+#define free_workspaces() do { } while(0)
 #endif /* __KERNEL__ */
 
 int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
@@ -131,7 +135,7 @@
 	return ret;
 }
 
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
 		      uint32_t srclen, uint32_t destlen)
 {
 	int ret;
@@ -166,7 +170,7 @@
 	if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
 		printk(KERN_WARNING "inflateInit failed\n");
 		up(&inflate_sem);
-		return;
+		return 1;
 	}
 
 	while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
@@ -176,4 +180,39 @@
 	}
 	zlib_inflateEnd(&inf_strm);
 	up(&inflate_sem);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+    .priority = JFFS2_ZLIB_PRIORITY,
+    .name = "zlib",
+    .compr = JFFS2_COMPR_ZLIB,
+    .compress = &jffs2_zlib_compress,
+    .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+    int ret;
+
+    ret = alloc_workspaces();
+    if (ret)
+        return ret;
+
+    ret = jffs2_register_compressor(&jffs2_zlib_comp);
+    if (ret)
+        free_workspaces();
+
+    return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_zlib_comp);
+    free_workspaces();
 }
diff --unified --recursive --new-file mtd/fs/jffs2/gc.c mtd-new-4/fs/jffs2/gc.c
--- mtd/fs/jffs2/gc.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/gc.c	2004-04-20 12:13:22.000000000 +0200
@@ -19,6 +19,7 @@
 #include <linux/compiler.h>
 #include <linux/stat.h>
 #include "nodelist.h"
+#include "compr.h"
 
 static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
 					  struct jffs2_inode_cache *ic,
@@ -1176,7 +1177,7 @@
 	while(offset < orig_end) {
 		uint32_t datalen;
 		uint32_t cdatalen;
-		char comprtype = JFFS2_COMPR_NONE;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
 
 		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
 
@@ -1209,7 +1210,8 @@
 		ri.offset = cpu_to_je32(offset);
 		ri.csize = cpu_to_je32(cdatalen);
 		ri.dsize = cpu_to_je32(datalen);
-		ri.compr = comprtype;
+		ri.compr = comprtype & 0xff;
+		ri.usercompr = (comprtype >> 8) & 0xff;
 		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 	
diff --unified --recursive --new-file mtd/fs/jffs2/nodelist.h mtd-new-4/fs/jffs2/nodelist.h
--- mtd/fs/jffs2/nodelist.h	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/nodelist.h	2004-04-20 12:49:25.000000000 +0200
@@ -439,15 +439,6 @@
 			   unsigned char *buf, uint32_t offset, uint32_t len);
 char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
-/* compr.c */
-unsigned char jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-			     unsigned char *data_in, unsigned char **cpage_out, 
-			     uint32_t *datalen, uint32_t *cdatalen);
-void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig);
-int jffs2_decompress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
-		     unsigned char comprtype, unsigned char *cdata_in, 
-		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
-
 /* scan.c */
 int jffs2_scan_medium(struct jffs2_sb_info *c);
 void jffs2_rotate_lists(struct jffs2_sb_info *c);
@@ -468,8 +459,4 @@
 int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 #endif
 
-/* compr_zlib.c */
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
-
 #endif /* __JFFS2_NODELIST_H__ */
diff --unified --recursive --new-file mtd/fs/jffs2/os-linux.h mtd-new-4/fs/jffs2/os-linux.h
--- mtd/fs/jffs2/os-linux.h	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/os-linux.h	2004-04-20 12:49:25.000000000 +0200
@@ -199,13 +199,6 @@
 int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, 
 		       unsigned long count, loff_t to, size_t *retlen);
 
-/* Compression config */
-#define JFFS2_COMPRESSION
-#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
-#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */
-#define JFFS2_USE_ZLIB
-#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */
-
 
 #endif /* __JFFS2_OS_LINUX_H__ */
 
diff --unified --recursive --new-file mtd/fs/jffs2/read.c mtd-new-4/fs/jffs2/read.c
--- mtd/fs/jffs2/read.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/read.c	2004-04-20 12:13:22.000000000 +0200
@@ -18,6 +18,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/compiler.h>
 #include "nodelist.h"
+#include "compr.h"
 
 int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
 		     struct jffs2_full_dnode *fd, unsigned char *buf,
@@ -129,7 +130,7 @@
 	if (ri->compr != JFFS2_COMPR_NONE) {
 		D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n",
 			  je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); 
-		ret = jffs2_decompress(c, f, ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
+		ret = jffs2_decompress(c, f, ri->compr | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
 		if (ret) {
 			printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
 			goto out_decomprbuf;
diff --unified --recursive --new-file mtd/fs/jffs2/super-v24.c mtd-new-4/fs/jffs2/super-v24.c
--- mtd/fs/jffs2/super-v24.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/super-v24.c	2004-04-20 12:13:22.000000000 +0200
@@ -22,6 +22,7 @@
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
+#include "compr.h"
 #include "nodelist.h"
 
 #ifndef MTD_BLOCK_MAJOR
@@ -125,15 +126,15 @@
 		return -EIO;
 	}
 #endif
-	ret = jffs2_zlib_init();
+	ret = jffs2_compressors_init();
 	if (ret) {
-		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
 		goto out;
 	}
 	ret = jffs2_create_slab_caches();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-		goto out_zlib;
+		goto out_compressors;
 	}
 	ret = register_filesystem(&jffs2_fs_type);
 	if (ret) {
@@ -144,8 +145,8 @@
 
  out_slab:
 	jffs2_destroy_slab_caches();
- out_zlib:
-	jffs2_zlib_exit();
+ out_compressors:
+	jffs2_compressors_exit();
  out:
 
 	return ret;
@@ -154,7 +155,7 @@
 static void __exit exit_jffs2_fs(void)
 {
 	jffs2_destroy_slab_caches();
-	jffs2_zlib_exit();
+	jffs2_compressors_exit();
 	unregister_filesystem(&jffs2_fs_type);
 }
 
diff --unified --recursive --new-file mtd/fs/jffs2/super.c mtd-new-4/fs/jffs2/super.c
--- mtd/fs/jffs2/super.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/super.c	2004-04-20 12:13:22.000000000 +0200
@@ -24,6 +24,7 @@
 #include <linux/mtd/mtd.h>
 #include <linux/ctype.h>
 #include <linux/namei.h>
+#include "compr.h"
 #include "nodelist.h"
 
 static void jffs2_put_super(struct super_block *);
@@ -307,15 +308,15 @@
 		printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n");
 		return -ENOMEM;
 	}
-	ret = jffs2_zlib_init();
+	ret = jffs2_compressors_init();
 	if (ret) {
-		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
 		goto out;
 	}
 	ret = jffs2_create_slab_caches();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-		goto out_zlib;
+		goto out_compressors;
 	}
 	ret = register_filesystem(&jffs2_fs_type);
 	if (ret) {
@@ -326,8 +327,8 @@
 
  out_slab:
 	jffs2_destroy_slab_caches();
- out_zlib:
-	jffs2_zlib_exit();
+ out_compressors:
+	jffs2_compressors_exit();
  out:
 	return ret;
 }
@@ -336,7 +337,7 @@
 {
 	unregister_filesystem(&jffs2_fs_type);
 	jffs2_destroy_slab_caches();
-	jffs2_zlib_exit();
+	jffs2_compressors_exit();
 	kmem_cache_destroy(jffs2_inode_cachep);
 }
 
diff --unified --recursive --new-file mtd/fs/jffs2/write.c mtd-new-4/fs/jffs2/write.c
--- mtd/fs/jffs2/write.c	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/fs/jffs2/write.c	2004-04-20 12:13:22.000000000 +0200
@@ -18,6 +18,7 @@
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
 #include "nodelist.h"
+#include "compr.h"
 
 
 int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
@@ -358,7 +359,7 @@
 	while(writelen) {
 		struct jffs2_full_dnode *fn;
 		unsigned char *comprbuf = NULL;
-		unsigned char comprtype = JFFS2_COMPR_NONE;
+		uint16_t comprtype = JFFS2_COMPR_NONE;
 		uint32_t phys_ofs, alloclen;
 		uint32_t datalen, cdatalen;
 		int retried = 0;
@@ -388,7 +389,8 @@
 		ri->offset = cpu_to_je32(offset);
 		ri->csize = cpu_to_je32(cdatalen);
 		ri->dsize = cpu_to_je32(datalen);
-		ri->compr = comprtype;
+		ri->compr = comprtype & 0xff;
+		ri->usercompr = (comprtype >> 8 ) & 0xff;
 		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 
diff --unified --recursive --new-file mtd/include/linux/jffs2.h mtd-new-4/include/linux/jffs2.h
--- mtd/include/linux/jffs2.h	2004-04-19 15:56:13.000000000 +0200
+++ mtd-new-4/include/linux/jffs2.h	2004-04-20 12:13:22.000000000 +0200
@@ -43,6 +43,9 @@
 #define JFFS2_COMPR_COPY	0x04
 #define JFFS2_COMPR_DYNRUBIN	0x05
 #define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO         0x07
+#define JFFS2_COMPR_LZARI       0x08
+
 /* Compatibility flags. */
 #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
 #define JFFS2_NODE_ACCURATE 0x2000
diff --unified --recursive --new-file mtd/patches/patchin.sh mtd-new-4/patches/patchin.sh
--- mtd/patches/patchin.sh	2004-04-19 15:56:14.000000000 +0200
+++ mtd-new-4/patches/patchin.sh	2004-04-20 12:13:22.000000000 +0200
@@ -240,7 +240,17 @@
 				rm -f include/linux/crc32.h
 				$LNCP $TOPDIR/fs/jffs2/crc32.h include/linux
 			fi
-		fi
+		
+                else
+			JFFS=`grep -n JFFS fs/Kconfig | head -n 1 | sed s/:.*//`
+			CRAMFS=`grep -n CRAMFS fs/Kconfig | head -n 1 | sed s/:.*//`
+			let JFFS=JFFS-1
+			let CRAMFS=CRAMFS-1
+			sed "$JFFS"q fs/Kconfig >Kconfig.tmp
+			cat $TOPDIR/fs/Kconfig >>Kconfig.tmp
+			sed 1,"$CRAMFS"d fs/Kconfig >>Kconfig.tmp
+			mv -f Kconfig.tmp fs/Kconfig
+                fi
 	fi
 fi
 
diff --unified --recursive --new-file mtd/util/Makefile mtd-new-4/util/Makefile
--- mtd/util/Makefile	2004-04-19 15:56:14.000000000 +0200
+++ mtd-new-4/util/Makefile	2004-04-20 12:13:22.000000000 +0200
@@ -12,19 +12,20 @@
 	einfo mtd_debug fcp nandwrite jffs2dump \
 	#jffs2reader nftldump nftl_format
 
-SYMLINKS = compr_rtime.c
+SYMLINKS = compr.h compr.c compr_rtime.c compr_lzo.c compr_rubin.c compr_rubin.h histo.h histo_mips.h pushpull.h compr_lzari.c
 
 all: $(TARGETS)
 
 clean:
 	rm -f *.o $(TARGETS) $(SYMLINKS)
 
-mkfs.jffs2.o eraseall.o crc32.o: crc32.h
+mkfs.jffs2.o eraseall.o crc32.o compr.o: crc32.h compr.h
+compr_rubin.o: compr_rubin.h histo.h histo_mips.h pushpull.h
 
 $(SYMLINKS):
 	ln -sf ../fs/jffs2/$@ $@
 
-mkfs.jffs2: crc32.o compr_rtime.o mkfs.jffs2.o compr_zlib.o
+mkfs.jffs2: crc32.o compr_rtime.o compr_rubin.o compr_lzo.o compr_lzari.o mkfs.jffs2.o compr.o compr_zlib.o
 	$(CC) $(LDFLAGS) -o $@ $^ -lz
 
 eraseall: crc32.o eraseall.o
diff --unified --recursive --new-file mtd/util/compr_zlib.c mtd-new-4/util/compr_zlib.c
--- mtd/util/compr_zlib.c	2004-04-19 15:56:14.000000000 +0200
+++ mtd-new-4/util/compr_zlib.c	2004-04-20 12:13:22.000000000 +0200
@@ -39,6 +39,8 @@
 #include <zlib.h>
 #include <stdio.h>
 #include <asm/types.h>
+#include <linux/jffs2.h>
+#include "compr.h"
 
 #define min(x,y) ((x)<(y)?(x):(y))
 
@@ -99,7 +101,7 @@
 	return 0;
 }
 
-void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
 		      uint32_t srclen, uint32_t destlen)
 {
 	z_stream strm;
@@ -109,7 +111,7 @@
 	strm.zfree = (void *)0;
 
 	if (Z_OK != inflateInit(&strm)) {
-		return;
+		return 1;
 	}
 	strm.next_in = data_in;
 	strm.avail_in = srclen;
@@ -123,4 +125,28 @@
 		;
 
 	inflateEnd(&strm);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+    .priority = JFFS2_ZLIB_PRIORITY,
+    .name = "zlib",
+#ifdef JFFS2_DISABLE_ZLIB
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+    .compr = JFFS2_COMPR_ZLIB,
+    .compress = &jffs2_zlib_compress,
+    .decompress = &jffs2_zlib_decompress,
+};
+
+int jffs2_zlib_init(void)
+{
+    return jffs2_register_compressor(&jffs2_zlib_comp);
+}
+
+void jffs2_zlib_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_zlib_comp);
 }
diff --unified --recursive --new-file mtd/util/mkfs.jffs2.1 mtd-new-4/util/mkfs.jffs2.1
--- mtd/util/mkfs.jffs2.1	2004-04-19 15:56:14.000000000 +0200
+++ mtd-new-4/util/mkfs.jffs2.1	2004-04-20 22:47:15.000000000 +0200
@@ -49,6 +49,21 @@
 .B -P,--squash-perms
 ]
 [
+.B -m,--compression-mode=MODE
+]
+[
+.B -x,--disable-compressor=NAME
+]
+[
+.B -X,--enable-compressor=NAME
+]
+[
+.B -L,--list-compressors
+]
+[
+.B -t,--test-compression
+]
+[
 .B -h,--help
 ]
 [
@@ -73,10 +88,9 @@
 .B -r
 option is not specified.
 
-Files to be placed into the file system image are compressed
-using the
-.B zlib
-compression library.
+Each block of the files to be placed into the file system image 
+are compressed using one of the avaiable compressors depending
+on the selected compression mode.
 
 File systems are created with the same endianness as the host,
 unless the
@@ -156,6 +170,32 @@
 .B -P, --squash-perms
 Squash permissions, removing write permission for \'group\' and \'other\'.
 .TP
+.B -m, --compression-mode=MODE
+Set the default compression mode. The default mode is 
+.B priority 
+which tries the compressors in a predefinied order and chooses the first
+successful one. Other alternatives are:
+.B none
+(mkfs will not compress) and
+.B size
+(mkfs will try all compressor and chooses the one which have the smallest result).
+.TP
+.B -x, --disable-compressor=NAME
+Disable a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default states.
+.TP
+.B -X, --enable-compressor=NAME
+Enable a compressor. Use
+.B -L
+to see the list of the avaiable compressors and their default states.
+.TP
+.B -L, --list-compressors
+Show the list of the avaiable compressors and their states.
+.TP
+.B -t, --test-compression
+Call decompress after every compress - and compare the result with the original data.
+.TP
 .B -h, --help
 Display help text.
 .TP
diff --unified --recursive --new-file mtd/util/mkfs.jffs2.c mtd-new-4/util/mkfs.jffs2.c
--- mtd/util/mkfs.jffs2.c	2004-04-19 15:56:14.000000000 +0200
+++ mtd-new-4/util/mkfs.jffs2.c	2004-04-20 22:37:38.000000000 +0200
@@ -679,29 +679,12 @@
 	{ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
 	0xff, 0xff, 0xff, 0xff, 0xff
 };
+
 /* We default to 4096, per x86.  When building a fs for 
  * 64-bit arches and whatnot, use the --pagesize=SIZE option */
-static int page_size = 4096;
-
-extern int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
-extern int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen);
+int page_size = 4096;
 
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		    uint32_t *datalen, uint32_t *cdatalen)
-{
-	int ret;
-
-	ret = jffs2_zlib_compress(data_in, cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_ZLIB;
-	}
-	/* rtime does manage to recompress already-compressed data */
-	ret = jffs2_rtime_compress(data_in, cpage_out, datalen, cdatalen);
-	if (!ret) {
-		return JFFS2_COMPR_RTIME;
-	}
-	return JFFS2_COMPR_NONE; /* We failed to compress */
-}
+#include "compr.h"
 
 static void full_write(int fd, const void *buf, int len)
 {
@@ -829,7 +812,7 @@
 	write_dirent(e);
 
 	buf = xmalloc(page_size);
-	cbuf = xmalloc(page_size);
+	cbuf = NULL;
 
 	ver = 0;
 	offset = 0;
@@ -856,7 +839,8 @@
 
 		while (len) {
 			uint32_t dsize, space;
-
+                        uint16_t compression;
+                        
 			pad_block_if_less_than(sizeof(ri) + JFFS2_MIN_DATA_LEN);
 
 			dsize = len;
@@ -866,7 +850,9 @@
 			if (space > dsize)
 				space = dsize;
 
-			ri.compr = jffs2_compress(tbuf, cbuf, &dsize, &space);
+			compression = jffs2_compress(NULL, NULL, tbuf, &cbuf, &dsize, &space);
+                        ri.compr = compression & 0xff;
+                        ri.usercompr = (compression >> 8) & 0xff;
 			if (ri.compr) {
 				wbuf = cbuf;
 			} else {
@@ -892,6 +878,8 @@
 			tbuf += dsize;
 			len -= dsize;
 			offset += dsize;
+
+                        if (tbuf!=cbuf) if (!cbuf) free(cbuf);
 		}
 	}
 	if (!je32_to_cpu(ri.version)) {
@@ -910,7 +898,6 @@
 		padword();
 	}
 	free(buf);
-	free(cbuf);
 	close(fd);
 }
 
@@ -1183,6 +1170,9 @@
 	{"squash-perms", 0, NULL, 'P'},
 	{"faketime", 0, NULL, 'f'},
 	{"devtable", 1, NULL, 'D'},
+	{"compression-mode", 1, NULL, 'm'},
+	{"disable-compressor", 1, NULL, 'x'},
+	{"test-compression", 0, NULL, 't'},
 	{NULL, 0, NULL, 0}
 };
 
@@ -1190,25 +1180,30 @@
 	"Usage: mkfs.jffs2 [OPTIONS]\n"
 	"Make a JFFS2 file system image from an existing directory tree\n\n"
 	"Options:\n"
-	"  -p, --pad[=SIZE]       Pad output to SIZE bytes with 0xFF. If SIZE is\n"
-	"                         not specified, the output is padded to the end of\n"
-	"                         the final erase block\n"
-	"  -r, -d, --root=DIR     Build file system from directory DIR (default: cwd)\n"
-	"  -s, --pagesize=SIZE    Use page size (max data node size) SIZE (default: 4KiB)\n"
-	"  -e, --eraseblock=SIZE  Use erase block size SIZE (default: 64KiB)\n"
-	"  -c, --cleanmarker=SIZE Size of cleanmarker (default 12)\n"
-	"  -n, --no-cleanmarkers  Don't add a cleanmarker to every eraseblock\n"
-	"  -o, --output=FILE      Output to FILE (default: stdout)\n"
-	"  -l, --little-endian    Create a little-endian filesystem\n"
-	"  -b, --big-endian       Create a big-endian filesystem\n"
-	"  -D, --devtable=FILE    Use the named FILE as a device table file\n"
-	"  -f, --faketime         Change all file times to '0' for regression testing\n"
-	"  -q, --squash           Squash permissions and owners making all files be owned by root\n"
-	"  -U, --squash-uids      Squash owners making all files be owned by root\n"
-	"  -P, --squash-perms     Squash permissions on all files\n"
-	"  -h, --help             Display this help text\n"
-	"  -v, --verbose          Verbose operation\n"
-	"  -V, --version          Display version information\n\n";
+	"  -p, --pad[=SIZE]         Pad output to SIZE bytes with 0xFF. If SIZE is\n"
+	"                           not specified, the output is padded to the end of\n"
+	"                           the final erase block\n"
+	"  -r, -d, --root=DIR       Build file system from directory DIR (default: cwd)\n"
+	"  -s, --pagesize=SIZE      Use page size (max data node size) SIZE (default: 4KiB)\n"
+	"  -e, --eraseblock=SIZE    Use erase block size SIZE (default: 64KiB)\n"
+	"  -c, --cleanmarker=SIZE   Size of cleanmarker (default 12)\n"
+	"  -m, --compression-mode   Select compression mode (default: priortiry)\n"
+	"  -x, --disable-compressor Disable a compressor\n"
+	"  -X, --enable-compressor  Enable a compressor\n"
+	"  -L, --list-compressors   Show the list of the avaiable compressors\n"
+	"  -t, --test-compression   Call decompress and compare with the original (for test)\n"
+	"  -n, --no-cleanmarkers    Don't add a cleanmarker to every eraseblock\n"
+	"  -o, --output=FILE        Output to FILE (default: stdout)\n"
+	"  -l, --little-endian      Create a little-endian filesystem\n"
+	"  -b, --big-endian         Create a big-endian filesystem\n"
+	"  -D, --devtable=FILE      Use the named FILE as a device table file\n"
+	"  -f, --faketime           Change all file times to '0' for regression testing\n"
+	"  -q, --squash             Squash permissions and owners making all files be owned by root\n"
+	"  -U, --squash-uids        Squash owners making all files be owned by root\n"
+	"  -P, --squash-perms       Squash permissions on all files\n"
+	"  -h, --help               Display this help text\n"
+	"  -v, --verbose            Verbose operation\n"
+	"  -V, --version            Display version information\n\n";
 
 
 static char *revtext = "$Revision: 1.40 $";
@@ -1221,8 +1216,10 @@
 	FILE *devtable = NULL;
 	struct filesystem_entry *root;
 
+        jffs2_compressors_init();
+
 	while ((opt = getopt_long(argc, argv, 
-					"D:d:r:s:o:qUPfh?vVe:lbp::nc:", long_options, &c)) >= 0) 
+					"D:d:r:s:o:qUPfh?vVe:lbp::nc:m:x:X:Lt", long_options, &c)) >= 0) 
 	{
 		switch (opt) {
 			case 'D':
@@ -1342,6 +1339,27 @@
 					error_msg_and_die("cleanmarker size must be < eraseblock size");
 				}
 				break;
+                        case 'm':
+                                if (jffs2_set_compression_mode_name(optarg)) {
+					error_msg_and_die("Unknown compression mode %s", optarg);
+				}
+                                break;
+                        case 'x':
+                                if (jffs2_disable_compressor_name(optarg)) {
+                                        error_msg_and_die("Unknown compressor name %s",optarg);
+                                }
+                                break;
+                        case 'X':
+                                if (jffs2_enable_compressor_name(optarg)) {
+                                        error_msg_and_die("Unknown compressor name %s",optarg);
+                                }
+                                break;
+                        case 'L':
+                                error_msg_and_die("\n%s",jffs2_list_compressors());
+                                break;
+                        case 't':
+                                jffs2_compression_check_set(1);
+                                break;
 		}
 	}
 	if (out_fd == -1) {
@@ -1372,5 +1390,14 @@
 		free(rootdir);
 
 	close(out_fd);
+
+        if (verbose) {
+                char *s = jffs2_stats();
+                fprintf(stderr,"\n\n%s",s);
+                free(s);
+        }
+
+        jffs2_compressors_exit();
+
 	return 0;
 }