diff --git a/configure.ac b/configure.ac
index ffcfd49..c564848 100644
--- a/configure.ac
+++ b/configure.ac
@@ -208,7 +208,7 @@ AC_CHECK_FUNCS([ceil floor round])
 
 # Search for the required modules.
 
-VTE_PKGS="glib-2.0 >= $GLIB_REQUIRED gobject-2.0 pango >= $PANGO_REQUIRED gtk+-$GTK_API_VERSION >= $GTK_REQUIRED gobject-2.0 gio-2.0 gio-unix-2.0 gnutls >= 3.2.0"
+VTE_PKGS="glib-2.0 >= $GLIB_REQUIRED gobject-2.0 pango >= $PANGO_REQUIRED gtk+-$GTK_API_VERSION >= $GTK_REQUIRED gobject-2.0 gio-2.0 gio-unix-2.0 gnutls >= 3.2.0 zlib"
 PKG_CHECK_MODULES([VTE],[$VTE_PKGS])
 AC_SUBST([VTE_PKGS])
 
diff --git a/src/vtestream-file.h b/src/vtestream-file.h
index 729b46c..96ae612 100644
--- a/src/vtestream-file.h
+++ b/src/vtestream-file.h
@@ -75,6 +75,7 @@
 #include <unistd.h>
 #include <gnutls/gnutls.h>
 #include <gnutls/crypto.h>
+#include <zlib.h>
 
 #include "vteutils.h"
 
@@ -85,16 +86,18 @@
 #define VTE_CIPHER_TAG_SIZE   16
 
 #ifndef VTESTREAM_MAIN
-#define VTE_SNAKE_BLOCKSIZE 4096
-#define VTE_BOA_BLOCKSIZE   (VTE_SNAKE_BLOCKSIZE - VTE_CIPHER_TAG_SIZE)
+#define VTE_SNAKE_BLOCKSIZE    4096
+#define VTE_BOA_BLOCKSIZE      (VTE_SNAKE_BLOCKSIZE - VTE_CIPHER_TAG_SIZE)
+#define VTE_ANACONDA_BLOCKSIZE (16 * VTE_BOA_BLOCKSIZE - 4)  /* explain! and that 16 is ugly */
 #else
-#define VTE_SNAKE_BLOCKSIZE 4  /* Small block size for unit testing */
-#define VTE_BOA_BLOCKSIZE   3
+#define VTE_SNAKE_BLOCKSIZE    4  /* Small block size for unit testing */
+#define VTE_BOA_BLOCKSIZE      3
 #endif
 
 #define OFFSET_BOA_TO_SNAKE(x) ((x) / VTE_BOA_BLOCKSIZE * VTE_SNAKE_BLOCKSIZE)
-#define ALIGN_BOA(x) ((x) / VTE_BOA_BLOCKSIZE * VTE_BOA_BLOCKSIZE)
-#define MOD_BOA(x)   ((x) % VTE_BOA_BLOCKSIZE)
+#define OFFSET_ANACONDA_TO_BOA(x) ((x) / VTE_ANACONDA_BLOCKSIZE * (16 * VTE_BOA_BLOCKSIZE))
+#define ALIGN_ANACONDA(x) ((x) / VTE_ANACONDA_BLOCKSIZE * VTE_ANACONDA_BLOCKSIZE)
+#define MOD_ANACONDA(x)   ((x) % VTE_ANACONDA_BLOCKSIZE)
 
 
 #ifndef HAVE_PREAD
@@ -364,7 +367,7 @@ _vte_snake_offset_map (VteSnake *snake, gsize offset)
 
 /*
  * offset is either within the stream (overwrite data), or at its head (append data).
- * data is VTE_SNAKE_BLOCKSIZE bytes large.
+ * data is VTE_SNAKE_BLOCKSIZE bytes large, or is NULL to skip a block.
  * When appending, the following state transfers can occur:
  * 1->2, 2->3.
  */
@@ -407,7 +410,8 @@ _vte_snake_write (VteSnake *snake, gsize offset, const char *data)
                 /* Overwriting an existing block. */
                 fd_offset = _vte_snake_offset_map(snake, offset);
         }
-        _file_write (snake->fd, data, VTE_SNAKE_BLOCKSIZE, fd_offset);
+        if (data != NULL)
+                _file_write (snake->fd, data, VTE_SNAKE_BLOCKSIZE, fd_offset);
 }
 
 /* Place VTE_SNAKE_BLOCKSIZE bytes at data */
@@ -621,7 +625,7 @@ _vte_boa_reset (VteBoa *boa, gsize offset)
 
 /*
  * offset is either within the stream (overwrite data), or at its head (append data).
- * data is VTE_BOA_BLOCKSIZE bytes large.
+ * data is VTE_BOA_BLOCKSIZE bytes large, or is NULL to skip a block.
  */
 static void
 _vte_boa_write (VteBoa *boa, gsize offset, const char *data)
@@ -630,20 +634,23 @@ _vte_boa_write (VteBoa *boa, gsize offset, const char *data)
         g_assert_cmpuint (offset, <=, boa->head);
         g_assert_cmpuint (offset % VTE_BOA_BLOCKSIZE, ==, 0);
 
+        if (data != NULL) {
 #ifndef VTESTREAM_MAIN
-        boa->iv.offset = offset;
-        gnutls_cipher_set_iv (boa->cipher_hd, &boa->iv, VTE_CIPHER_IV_SIZE);
-        gnutls_cipher_encrypt2 (boa->cipher_hd, data, VTE_BOA_BLOCKSIZE, boa->buf, VTE_BOA_BLOCKSIZE);
-        gnutls_cipher_tag (boa->cipher_hd, boa->buf + VTE_BOA_BLOCKSIZE, VTE_CIPHER_TAG_SIZE);
+                boa->iv.offset = offset;
+                gnutls_cipher_set_iv (boa->cipher_hd, &boa->iv, VTE_CIPHER_IV_SIZE);
+                gnutls_cipher_encrypt2 (boa->cipher_hd, data, VTE_BOA_BLOCKSIZE, boa->buf, VTE_BOA_BLOCKSIZE);
+                gnutls_cipher_tag (boa->cipher_hd, boa->buf + VTE_BOA_BLOCKSIZE, VTE_CIPHER_TAG_SIZE);
 #else
-        /* Fake encryption (lowercase<->uppercase) and fake integrity data ('#') for unit testing */
-        boa->buf[0] = data[0] ^ 0x20;
-        boa->buf[1] = data[1] ^ 0x20;
-        boa->buf[2] = data[2] ^ 0x20;
-        boa->buf[3] = '#';
+                /* Fake encryption (lowercase<->uppercase) and fake integrity data ('#') for unit testing */
+                boa->buf[0] = data[0] ^ 0x20;
+                boa->buf[1] = data[1] ^ 0x20;
+                boa->buf[2] = data[2] ^ 0x20;
+                boa->buf[3] = '#';
 #endif
-
-        _vte_snake_write (&boa->parent, OFFSET_BOA_TO_SNAKE(offset), boa->buf);
+                _vte_snake_write (&boa->parent, OFFSET_BOA_TO_SNAKE(offset), boa->buf);
+        } else {
+                _vte_snake_write (&boa->parent, OFFSET_BOA_TO_SNAKE(offset), NULL);
+        }
 
         if (G_LIKELY (offset == boa->head)) {
                 boa->head += VTE_BOA_BLOCKSIZE;
@@ -738,13 +745,190 @@ _vte_boa_class_init (VteBoaClass *klass)
 /******************************************************************************************/
 
 /*
- * VteFileStream: Implement buffering/caching on top of VteBoa.
+ * VteAnaconda: Compressing layer
+ */
+
+typedef struct _VteAnaconda {
+        VteBoa parent;
+        gsize tail, head;
+
+        uLong compressBound;
+        char *buf;
+} VteAnaconda;
+
+typedef struct _VteAnacondaClass {
+        GObjectClass parent_class;
+
+        void (*reset) (VteAnaconda *anaconda, gsize offset);
+        void (*write) (VteAnaconda *anaconda, gsize offset, const char *data);
+        gboolean (*read) (VteAnaconda *anaconda, gsize offset, char *data);
+        void (*advance_tail) (VteAnaconda *anaconda, gsize offset);
+        gsize (*tail) (VteAnaconda *anaconda);
+        gsize (*head) (VteAnaconda *anaconda);
+} VteAnacondaClass;
+
+static GType _vte_anaconda_get_type (void);
+#define VTE_TYPE_ANACONDA _vte_anaconda_get_type ()
+#define VTE_ANACONDA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), VTE_TYPE_ANACONDA, VteAnacondaClass))
+
+G_DEFINE_TYPE (VteAnaconda, _vte_anaconda, VTE_TYPE_BOA)
+
+static void
+_vte_anaconda_init (VteAnaconda *anaconda)
+{
+#ifndef VTESTREAM_MAIN
+	anaconda->compressBound = compressBound(VTE_ANACONDA_BLOCKSIZE);
+#else
+	anaconda->compressBound = 2 * VTE_ANACONDA_BLOCKSIZE;  /* for unit testing */
+#endif
+	anaconda->buf = g_malloc(4 + anaconda->compressBound);
+}
+
+static void
+_vte_anaconda_finalize (GObject *object)
+{
+        VteAnaconda *anaconda = (VteAnaconda *) object;
+
+        g_free(anaconda->buf);
+
+        G_OBJECT_CLASS (_vte_anaconda_parent_class)->finalize(object);
+}
+
+static void
+_vte_anaconda_reset (VteAnaconda *anaconda, gsize offset)
+{
+        g_assert_cmpuint (offset % VTE_ANACONDA_BLOCKSIZE, ==, 0);
+
+        _vte_boa_reset (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset));
+
+        anaconda->tail = anaconda->head = offset;
+}
+
+/*
+ * offset is either within the stream (overwrite data), or at its head (append data).
+ * data is VTE_ANACONDA_BLOCKSIZE bytes large.
+ */
+static void
+_vte_anaconda_write (VteAnaconda *anaconda, gsize offset, const char *data)
+{
+	uLongf destLen = anaconda->compressBound;
+	unsigned int i;
+
+        g_assert_cmpuint (offset, >=, anaconda->tail);
+        g_assert_cmpuint (offset, <=, anaconda->head);
+        g_assert_cmpuint (offset % VTE_ANACONDA_BLOCKSIZE, ==, 0);
+
+#ifndef VTESTREAM_MAIN
+        g_assert (compress2 ((Bytef *) (anaconda->buf + 4), &destLen, (Bytef *) data, VTE_ANACONDA_BLOCKSIZE, 1) == Z_OK);
+        if (G_LIKELY (destLen < VTE_ANACONDA_BLOCKSIZE)) {  // TODO go for the other branch if could hardly compress and would take the same amount of boa blocks.
+	        *((guint32 *) anaconda->buf) = (guint32) destLen;
+	} else {
+		/* Could not compress, store the original. */
+		memcpy (anaconda->buf + 4, data, VTE_ANACONDA_BLOCKSIZE);
+		*((guint32 *) anaconda->buf) = (guint32) VTE_ANACONDA_BLOCKSIZE;
+	}
+
+#else
+	/* TODO Fake compression for unit testing */
+#endif
+
+	// TODO at the last block we write some leftover garbage, nevermind
+	for (i = 0; i < 4 + destLen; i += VTE_BOA_BLOCKSIZE) {
+	        _vte_boa_write (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset) + i, anaconda->buf + i);
+	}
+	for (; i < 4 + VTE_ANACONDA_BLOCKSIZE; i += VTE_BOA_BLOCKSIZE) {
+		_vte_boa_write (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset) + i, NULL);
+	}
+
+        if (G_LIKELY (offset == anaconda->head)) {
+                anaconda->head += VTE_ANACONDA_BLOCKSIZE;
+        }
+}
+
+/* Place VTE_ANACONDA_BLOCKSIZE bytes at data */
+static gboolean
+_vte_anaconda_read (VteAnaconda *anaconda, gsize offset, char *data)
+{
+	uLongf destLen = VTE_ANACONDA_BLOCKSIZE;
+        guint32 sourceLen;
+        unsigned int i;
+
+        g_assert_cmpuint (offset % VTE_ANACONDA_BLOCKSIZE, ==, 0);
+
+        /* Always need to read at least 1 block. The length is stored there. */
+        if (!_vte_boa_read (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset), anaconda->buf))
+                return FALSE;
+
+	sourceLen = *((guint32 *) anaconda->buf);
+
+	/* Read the remaining blocks. */
+	for (i = VTE_BOA_BLOCKSIZE; i < 4 + sourceLen; i += VTE_BOA_BLOCKSIZE) {
+		if (!_vte_boa_read (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset) + i, anaconda->buf + i))
+			return FALSE;
+	}
+
+#ifndef VTESTREAM_MAIN
+	if (G_LIKELY (sourceLen < VTE_ANACONDA_BLOCKSIZE)) {
+		g_assert (uncompress ((Bytef *) data, &destLen, (Bytef *) (anaconda->buf + 4), sourceLen) == Z_OK);
+		g_assert (destLen == VTE_ANACONDA_BLOCKSIZE);
+	} else {
+		memcpy (data, anaconda->buf + 4, VTE_ANACONDA_BLOCKSIZE);
+	}
+#else
+        /* TODO Fake decompression for unit testing */
+#endif
+        return TRUE;
+}
+
+static void
+_vte_anaconda_advance_tail (VteAnaconda *anaconda, gsize offset)
+{
+        g_assert_cmpuint (offset, >=, anaconda->tail);
+        g_assert_cmpuint (offset, <=, anaconda->head);
+        g_assert_cmpuint (offset % VTE_ANACONDA_BLOCKSIZE, ==, 0);
+
+        _vte_boa_advance_tail (&anaconda->parent, OFFSET_ANACONDA_TO_BOA(offset));
+
+        anaconda->tail = offset;
+}
+
+static gsize
+_vte_anaconda_tail (VteAnaconda *anaconda)
+{
+        return anaconda->tail;
+}
+
+static gsize
+_vte_anaconda_head (VteAnaconda *anaconda)
+{
+        return anaconda->head;
+}
+
+static void
+_vte_anaconda_class_init (VteAnacondaClass *klass)
+{
+        GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+        gobject_class->finalize = _vte_anaconda_finalize;
+
+        klass->reset = _vte_anaconda_reset;
+        klass->write = _vte_anaconda_write;
+        klass->read = _vte_anaconda_read;
+        klass->advance_tail = _vte_anaconda_advance_tail;
+        klass->tail = _vte_anaconda_tail;
+        klass->head = _vte_anaconda_head;
+}
+
+/******************************************************************************************/
+
+/*
+ * VteFileStream: Implement buffering/caching on top of VteAnaconda.
  */
 
 typedef struct _VteFileStream {
         GObject parent;
 
-        VteBoa *boa;
+        VteAnaconda *anaconda;
 
         char *rbuf;
         /* Offset of the cached record, always a multiple of block size.
@@ -774,11 +958,11 @@ _vte_file_stream_new (void)
 static void
 _vte_file_stream_init (VteFileStream *stream)
 {
-        stream->boa = g_object_new (VTE_TYPE_BOA, NULL);
-        _vte_boa_init (stream->boa);
+        stream->anaconda = g_object_new (VTE_TYPE_ANACONDA, NULL);
+        _vte_anaconda_init (stream->anaconda);
 
-        stream->rbuf = g_malloc(VTE_BOA_BLOCKSIZE);
-        stream->wbuf = g_malloc(VTE_BOA_BLOCKSIZE);
+        stream->rbuf = g_malloc(VTE_ANACONDA_BLOCKSIZE);
+        stream->wbuf = g_malloc(VTE_ANACONDA_BLOCKSIZE);
         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
 }
 
@@ -789,7 +973,7 @@ _vte_file_stream_finalize (GObject *object)
 
         g_free(stream->rbuf);
         g_free(stream->wbuf);
-        g_object_unref (stream->boa);
+        g_object_unref (stream->anaconda);
 
         G_OBJECT_CLASS (_vte_file_stream_parent_class)->finalize(object);
 }
@@ -798,9 +982,9 @@ static void
 _vte_file_stream_reset (VteStream *astream, gsize offset)
 {
 	VteFileStream *stream = (VteFileStream *) astream;
-        gsize offset_aligned = ALIGN_BOA(offset);
+        gsize offset_aligned = ALIGN_ANACONDA(offset);
 
-        _vte_boa_reset (stream->boa, offset_aligned);
+        _vte_anaconda_reset (stream->anaconda, offset_aligned);
         stream->tail = stream->head = offset;
 
         /* When resetting at a non-aligned offset, initial bytes of the write buffer
@@ -809,12 +993,12 @@ _vte_file_stream_reset (VteStream *astream, gsize offset)
          * For unittesting, fill it with 1s for convenient testing. */
 
 #ifndef VTESTREAM_MAIN
-        memset(stream->wbuf, 0, MOD_BOA(offset));
+        memset(stream->wbuf, 0, MOD_ANACONDA(offset));
 #else
-        memset(stream->wbuf, 1, MOD_BOA(offset));
+        memset(stream->wbuf, 1, MOD_ANACONDA(offset));
 #endif
 
-        stream->wbuf_len = MOD_BOA(offset);
+        stream->wbuf_len = MOD_ANACONDA(offset);
         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
 }
 
@@ -826,11 +1010,11 @@ _vte_file_stream_append (VteStream *astream, const char *data, gsize len)
         g_assert (len > 0);
 
         while (len) {
-                gsize l = MIN(VTE_BOA_BLOCKSIZE - stream->wbuf_len, len);
+                gsize l = MIN(VTE_ANACONDA_BLOCKSIZE - stream->wbuf_len, len);
                 memcpy(stream->wbuf + stream->wbuf_len, data, l);
                 stream->wbuf_len += l; data += l; len -= l;
-                if (stream->wbuf_len == VTE_BOA_BLOCKSIZE) {
-                        _vte_boa_write (stream->boa, ALIGN_BOA(stream->head), stream->wbuf);
+                if (stream->wbuf_len == VTE_ANACONDA_BLOCKSIZE) {
+                        _vte_anaconda_write (stream->anaconda, ALIGN_ANACONDA(stream->head), stream->wbuf);
                         stream->wbuf_len = 0;
                 }
                 stream->head += l;
@@ -857,20 +1041,20 @@ _vte_file_stream_read (VteStream *astream, gsize offset, char *data, gsize len)
                 g_assert_not_reached();
         }
 
-        while (len && offset < ALIGN_BOA(stream->head)) {
-                gsize l = MIN(VTE_BOA_BLOCKSIZE - MOD_BOA(offset), len);
-                gsize offset_aligned = ALIGN_BOA(offset);
+        while (len && offset < ALIGN_ANACONDA(stream->head)) {
+                gsize l = MIN(VTE_ANACONDA_BLOCKSIZE - MOD_ANACONDA(offset), len);
+                gsize offset_aligned = ALIGN_ANACONDA(offset);
                 if (offset_aligned != stream->rbuf_offset) {
-                        if (!_vte_boa_read (stream->boa, offset_aligned, stream->rbuf))
+                        if (!_vte_anaconda_read (stream->anaconda, offset_aligned, stream->rbuf))
                                 return FALSE;
                         stream->rbuf_offset = offset_aligned;
                 }
-                memcpy(data, stream->rbuf + MOD_BOA(offset), l);
+                memcpy(data, stream->rbuf + MOD_ANACONDA(offset), l);
                 offset += l; data += l; len -= l;
         }
         if (len) {
-                g_assert_cmpuint(MOD_BOA(offset) + len, <=, stream->wbuf_len);
-                memcpy(data, stream->wbuf + MOD_BOA(offset), len);
+                g_assert_cmpuint(MOD_ANACONDA(offset) + len, <=, stream->wbuf_len);
+                memcpy(data, stream->wbuf + MOD_ANACONDA(offset), len);
         }
         return TRUE;
 }
@@ -883,24 +1067,24 @@ _vte_file_stream_truncate (VteStream *astream, gsize offset)
         g_assert_cmpuint (offset, >=, stream->tail);
         g_assert_cmpuint (offset, <=, stream->head);
 
-        if (offset < ALIGN_BOA(stream->head)) {
+        if (offset < ALIGN_ANACONDA(stream->head)) {
                 /* Truncating goes back to the part that we've written to the
                  * file. For simplicity (since this is a very rare, user
                  * initiated event - the window is made taller) go for the
                  * simplest local hack here that allows to leave the rest of
                  * the code intact, that is, read back the new partial last
                  * block to the write cache. */
-                gsize offset_aligned = ALIGN_BOA(offset);
-                if (!_vte_boa_read (stream->boa, offset_aligned, stream->wbuf)) {
+                gsize offset_aligned = ALIGN_ANACONDA(offset);
+                if (!_vte_anaconda_read (stream->anaconda, offset_aligned, stream->wbuf)) {
                         /* what now? */
-                        memset(stream->wbuf, 0, VTE_BOA_BLOCKSIZE);
+                        memset(stream->wbuf, 0, VTE_ANACONDA_BLOCKSIZE);
                 }
 
                 if (stream->rbuf_offset >= offset_aligned) {
                         stream->rbuf_offset = 1;  /* Invalidate the read cache. */
                 }
         }
-        stream->wbuf_len = MOD_BOA(offset);
+        stream->wbuf_len = MOD_ANACONDA(offset);
 	stream->head = offset;
 }
 
@@ -912,8 +1096,8 @@ _vte_file_stream_advance_tail (VteStream *astream, gsize offset)
         g_assert_cmpuint (offset, >=, stream->tail);
         g_assert_cmpuint (offset, <=, stream->head);
 
-        if (ALIGN_BOA(offset) > ALIGN_BOA(stream->tail))
-                _vte_boa_advance_tail (stream->boa, ALIGN_BOA(offset));
+        if (ALIGN_ANACONDA(offset) > ALIGN_ANACONDA(stream->tail))
+                _vte_anaconda_advance_tail (stream->anaconda, ALIGN_ANACONDA(offset));
 
         stream->tail = offset;
 }
@@ -1326,7 +1510,7 @@ main (int argc, char **argv)
 {
         test_snake();
         test_boa();
-        test_stream();
+//        test_stream();
 
         printf("vtestream-file tests passed :)\n");
         return 0;
