#include "memdbg.h"
-/* Initial command byte to tell our peer if we compressed */
-#define LZ4_COMPRESS_BYTE 0x69
-
static void
lz4_compress_init (struct compress_context *compctx)
{
}
static void
-lz4_compress_uninit (struct compress_context *compctx)
+lz4v2_compress_init (struct compress_context *compctx)
{
+ msg (D_INIT_MEDIUM, "LZ4v2 compression initializing");
}
static void
-lz4_compress (struct buffer *buf, struct buffer work,
- struct compress_context *compctx,
- const struct frame* frame)
+lz4_compress_uninit (struct compress_context *compctx)
{
- bool compressed = false;
-
- if (buf->len <= 0)
- return;
+}
+static bool
+do_lz4_compress (struct buffer *buf,
+ struct buffer *work,
+ struct compress_context *compctx,
+ const struct frame* frame)
+{
/*
* In order to attempt compression, length must be at least COMPRESS_THRESHOLD.
*/
int zlen_max = ps + COMP_EXTRA_BUFFER (ps);
int zlen;
- ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
- ASSERT (buf_safe (&work, zlen_max));
+ ASSERT (buf_init (work, FRAME_HEADROOM (frame)));
+ ASSERT (buf_safe (work, zlen_max));
if (buf->len > ps)
{
dmsg (D_COMP_ERRORS, "LZ4 compression buffer overflow");
buf->len = 0;
- return;
+ return false;
}
- zlen = LZ4_compress_limitedOutput((const char *)BPTR(buf), (char *)BPTR(&work), BLEN(buf), zlen_max );
+ zlen = LZ4_compress_limitedOutput((const char *)BPTR(buf), (char *)BPTR(work), BLEN(buf), zlen_max );
if (zlen <= 0)
{
dmsg (D_COMP_ERRORS, "LZ4 compression error");
buf->len = 0;
- return;
+ return false;
}
- ASSERT (buf_safe (&work, zlen));
- work.len = zlen;
- compressed = true;
+ ASSERT (buf_safe (work, zlen));
+ work->len = zlen;
+
- dmsg (D_COMP, "LZ4 compress %d -> %d", buf->len, work.len);
+ dmsg (D_COMP, "LZ4 compress %d -> %d", buf->len, work->len);
compctx->pre_compress += buf->len;
- compctx->post_compress += work.len;
+ compctx->post_compress += work->len;
+ return true;
}
+ return false;
+}
+
+
+static void
+lz4_compress (struct buffer *buf, struct buffer work,
+ struct compress_context *compctx,
+ const struct frame* frame)
+{
+ bool compressed;
+ if (buf->len <= 0)
+ return;
+
+ compressed = do_lz4_compress(buf, &work, compctx, frame);
+
+ /* On error do_lz4_compress sets buf len to zero, just return */
+ if (buf->len == 0)
+ return;
/* did compression save us anything? */
{
}
}
+
+static void
+lz4v2_compress (struct buffer *buf, struct buffer work,
+ struct compress_context *compctx,
+ const struct frame* frame)
+{
+ bool compressed;
+ if (buf->len <= 0)
+ return;
+
+ compressed = do_lz4_compress(buf, &work, compctx, frame);
+
+ /* On Error just return */
+ if (buf->len == 0)
+ return;
+
+ /* did compression save us anything? Include 2 byte compression header
+ in calculation */
+ if (compressed && work.len + 2 < buf->len)
+ {
+ ASSERT(buf_prepend(&work, 2));
+ uint8_t *head = BPTR (&work);
+ head[0] = COMP_ALGV2_INDICATOR_BYTE;
+ head[1] = COMP_ALGV2_LZ4_BYTE;
+ *buf = work;
+ }
+ else
+ {
+ compv2_escape_data_ifneeded(buf);
+ }
+}
+
+void
+do_lz4_decompress(size_t zlen_max,
+ struct buffer *work,
+ struct buffer *buf,
+ struct compress_context *compctx)
+{
+ int uncomp_len;
+ ASSERT (buf_safe (work, zlen_max));
+ uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(work), (size_t)BLEN(buf), zlen_max);
+ if (uncomp_len <= 0)
+ {
+ dmsg (D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
+ buf->len = 0;
+ return;
+ }
+
+ ASSERT (buf_safe (work, uncomp_len));
+ work->len = uncomp_len;
+
+ dmsg (D_COMP, "LZ4 decompress %d -> %d", buf->len, work->len);
+ compctx->pre_decompress += buf->len;
+ compctx->post_decompress += work->len;
+
+ *buf = *work;
+}
+
static void
lz4_decompress (struct buffer *buf, struct buffer work,
- struct compress_context *compctx,
- const struct frame* frame)
+ struct compress_context *compctx,
+ const struct frame* frame)
{
size_t zlen_max = EXPANDED_SIZE (frame);
- int uncomp_len;
uint8_t c; /* flag indicating whether or not our peer compressed */
if (buf->len <= 0)
if (c == LZ4_COMPRESS_BYTE) /* packet was compressed */
{
- ASSERT (buf_safe (&work, zlen_max));
- uncomp_len = LZ4_decompress_safe((const char *)BPTR(buf), (char *)BPTR(&work), (size_t)BLEN(buf), zlen_max);
- if (uncomp_len <= 0)
- {
- dmsg (D_COMP_ERRORS, "LZ4 decompression error: %d", uncomp_len);
- buf->len = 0;
- return;
- }
-
- ASSERT (buf_safe (&work, uncomp_len));
- work.len = uncomp_len;
-
- dmsg (D_COMP, "LZ4 decompress %d -> %d", buf->len, work.len);
- compctx->pre_decompress += buf->len;
- compctx->post_decompress += work.len;
-
- *buf = work;
+ do_lz4_decompress(zlen_max, &work, buf, compctx);
}
else if (c == NO_COMPRESS_BYTE_SWAP) /* packet was not compressed */
{
}
}
+static void
+lz4v2_decompress (struct buffer *buf, struct buffer work,
+ struct compress_context *compctx,
+ const struct frame* frame)
+{
+ size_t zlen_max = EXPANDED_SIZE (frame);
+ uint8_t c; /* flag indicating whether or not our peer compressed */
+
+ if (buf->len <= 0)
+ return;
+
+ ASSERT (buf_init (&work, FRAME_HEADROOM (frame)));
+
+ /* do unframing/swap (assumes buf->len > 0) */
+ uint8_t *head = BPTR (buf);
+ c = *head;
+
+ /* Not compressed */
+ if (c != COMP_ALGV2_INDICATOR_BYTE) {
+ return;
+ }
+
+ /* Packet to short to make sense */
+ if (buf->len <= 1)
+ {
+ buf->len=0;
+ return;
+ }
+
+ c = head[1];
+ if (c == COMP_ALGV2_LZ4_BYTE) /* packet was compressed */
+ {
+ buf_advance(buf,2);
+ do_lz4_decompress(zlen_max, &work, buf, compctx);
+ }
+ else if (c == COMP_ALGV2_UNCOMPRESSED_BYTE)
+ {
+ buf_advance(buf,2);
+ }
+ else
+ {
+ dmsg (D_COMP_ERRORS, "Bad LZ4v2 decompression header byte: %d", c);
+ buf->len = 0;
+ }
+}
+
const struct compress_alg lz4_alg = {
"lz4",
lz4_compress_init,
lz4_decompress
};
+const struct compress_alg lz4v2_alg = {
+ "lz4v2",
+ lz4v2_compress_init,
+ lz4_compress_uninit,
+ lz4v2_compress,
+ lz4v2_decompress
+};
+
#else
static void dummy(void) {}
#endif /* ENABLE_LZ4 */
compctx->alg = comp_stub_alg;
(*compctx->alg.compress_init)(compctx);
break;
+ case COMP_ALGV2_UNCOMPRESSED:
+ ALLOC_OBJ_CLEAR (compctx, struct compress_context);
+ compctx->flags = opt->flags;
+ compctx->alg = compv2_stub_alg;
+ break;
#ifdef ENABLE_LZO
case COMP_ALG_LZO:
ALLOC_OBJ_CLEAR (compctx, struct compress_context);
compctx->alg = lz4_alg;
(*compctx->alg.compress_init)(compctx);
break;
+ case COMP_ALGV2_LZ4:
+ ALLOC_OBJ_CLEAR (compctx, struct compress_context);
+ compctx->flags = opt->flags;
+ compctx->alg = lz4v2_alg;
+ break;
#endif
}
return compctx;
}
+/* In the v2 compression schemes, an uncompressed packet has
+ * has no opcode in front, unless the first byte is 0x50. In this
+ * case the packet needs to be escaped */
+void
+compv2_escape_data_ifneeded (struct buffer *buf)
+{
+ uint8_t *head = BPTR (buf);
+ if (head[0] != COMP_ALGV2_INDICATOR_BYTE)
+ return;
+
+ /* Header is 0x50 */
+ ASSERT(buf_prepend(buf, 2));
+
+ head = BPTR (buf);
+ head[0] = COMP_ALGV2_INDICATOR_BYTE;
+ head[1] = COMP_ALGV2_UNCOMPRESSED;
+}
+
+
void
comp_uninit(struct compress_context *compctx)
{
{
#if defined(ENABLE_LZ4)
buf_printf (out, "IV_LZ4=1\n");
+ buf_printf (out, "IV_LZ4v2=1\n");
#endif
#if defined(ENABLE_LZO)
buf_printf (out, "IV_LZO=1\n");
if (!lzo_avail)
buf_printf (out, "IV_LZO_STUB=1\n");
buf_printf (out, "IV_COMP_STUB=1\n");
+ buf_printf (out, "IV_COMP_STUBv2=1\n");
}
}
#define COMP_ALG_SNAPPY 3 /* Snappy algorithm (no longer supported) */
#define COMP_ALG_LZ4 4 /* LZ4 algorithm */
+
+/* algorithm v2 */
+#define COMP_ALGV2_UNCOMPRESSED 10
+#define COMP_ALGV2_LZ4 11
+/*
+#define COMP_ALGV2_LZO 12
+#define COMP_ALGV2_SNAPPY 13
+*/
+
/* Compression flags */
#define COMP_F_ADAPTIVE (1<<0) /* COMP_ALG_LZO only */
#define COMP_F_ASYM (1<<1) /* only downlink is compressed, not uplink */
#define COMP_F_SWAP (1<<2) /* initial command byte is swapped with last byte in buffer to preserve payload alignment */
#define COMP_F_ADVERTISE_STUBS_ONLY (1<<3) /* tell server that we only support compression stubs */
+
/*
* Length of prepended prefix on compressed packets
*/
/*
* Prefix bytes
*/
+
+/* V1 on wire codes */
+/* Initial command byte to tell our peer if we compressed */
+#define LZO_COMPRESS_BYTE 0x66
+#define LZ4_COMPRESS_BYTE 0x69
#define NO_COMPRESS_BYTE 0xFA
#define NO_COMPRESS_BYTE_SWAP 0xFB /* to maintain payload alignment, replace this byte with last byte of packet */
+/* V2 on wire code */
+#define COMP_ALGV2_INDICATOR_BYTE 0x50
+#define COMP_ALGV2_UNCOMPRESSED_BYTE 0
+#define COMP_ALGV2_LZ4_BYTE 1
+#define COMP_ALGV2_LZO_BYTE 2
+#define COMP_ALGV2_SNAPPY_BYTE 3
+
/*
* Compress worst case size expansion (for any algorithm)
*
};
extern const struct compress_alg comp_stub_alg;
+extern const struct compress_alg compv2_stub_alg;
struct compress_context *comp_init(const struct compress_options *opt);
void comp_generate_peer_info_string(const struct compress_options *opt, struct buffer *out);
+void compv2_escape_data_ifneeded (struct buffer *buf);
+
static inline bool
comp_enabled(const struct compress_options *info)
{