[tor-commits] [stegotorus/master] Compression API compression.

zwol at torproject.org zwol at torproject.org
Fri Jul 20 23:17:07 UTC 2012


commit 2e75ae19337994837e61c741fa1c6866a58230bf
Author: Zack Weinberg <zackw at cmu.edu>
Date:   Mon Apr 30 15:29:58 2012 -0700

    Compression API compression.
    
    * collapse def() and gzDeflate() together
    * ditto inf() and gzInflate()
    * new functions are named compress() and decompress()
    * don't expose CRC generation
    * don't set mtimes in deflate format
    * zpack.{cc,h} renamed compression.{cc,h}
    
    Mysteriously, this caused the 'crypt' unittest to start throwing bogus
    warnings, so squelch those en passant.
---
 Makefile.am                      |    8 +-
 src/compression.cc               |  102 ++++++++
 src/compression.h                |   35 +++
 src/crypt.cc                     |    6 +-
 src/steg/jsSteg.cc               |    9 +-
 src/steg/jsSteg.h                |    1 -
 src/steg/swfSteg.cc              |   25 +--
 src/test/unittest_compression.cc |  437 ++++++++++++++++++++++++++++++++
 src/test/unittest_zlib.cc        |  507 --------------------------------------
 src/zpack.cc                     |  172 -------------
 src/zpack.h                      |   21 --
 11 files changed, 597 insertions(+), 726 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index 3ded164..88939fd 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -30,6 +30,7 @@ STEGANOGRAPHERS = \
 
 libstegotorus_a_SOURCES = \
 	src/base64.cc \
+	src/compression.cc \
 	src/connections.cc \
 	src/crypt.cc \
 	src/network.cc \
@@ -38,7 +39,6 @@ libstegotorus_a_SOURCES = \
 	src/socks.cc \
 	src/steg.cc \
 	src/util.cc \
-	src/zpack.cc \
 	$(PROTOCOLS) $(STEGANOGRAPHERS)
 
 nodist_libstegotorus_a_SOURCES = protolist.cc steglist.cc
@@ -47,12 +47,12 @@ stegotorus_SOURCES = \
 	src/main.cc
 
 UTGROUPS = \
+	src/test/unittest_compression.cc \
+	src/test/unittest_config.cc \
 	src/test/unittest_crypt.cc \
 	src/test/unittest_pdfsteg.cc \
 	src/test/unittest_socks.cc \
-	src/test/unittest_config.cc \
-	src/test/unittest_transfer.cc \
-	src/test/unittest_zlib.cc
+	src/test/unittest_transfer.cc
 
 unittests_SOURCES = \
 	src/test/tinytest.cc \
diff --git a/src/compression.cc b/src/compression.cc
new file mode 100644
index 0000000..2a0b2ef
--- /dev/null
+++ b/src/compression.cc
@@ -0,0 +1,102 @@
+/* Copyright 2011, 2012 SRI International
+ * See LICENSE for other credits and copying information
+ */
+#include "util.h"
+#include "compression.h"
+
+#include <zlib.h>
+#include <limits>
+
+// zlib doesn't believe in size_t. When size_t is bigger than uInt, we
+// theoretically could break operations up into uInt-sized chunks to
+// support the full range of size_t, but I doubt we will ever need to
+// compress, decompress, or crc32 more than 2^32 bytes in one
+// operation, so I'm not bothering.  -- zw, 2012
+//
+// The indirection through ZLIB_UINT_MAX makes some versions of gcc
+// not produce a 'comparison is always (true/false)' warning.
+const size_t ZLIB_UINT_MAX = std::numeric_limits<uInt>::max();
+const size_t ZLIB_CEILING = (SIZE_T_CEILING > ZLIB_UINT_MAX
+                             ? ZLIB_UINT_MAX : SIZE_T_CEILING);
+
+ssize_t
+compress(const uint8_t *source, size_t slen,
+         uint8_t *dest, size_t dlen,
+         compression_format fmt)
+{
+  log_assert(fmt == c_format_zlib || fmt == c_format_gzip);
+
+  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
+    return -1;
+
+  z_stream strm;
+  memset(&strm, 0, sizeof strm);
+
+  int wbits = MAX_WBITS;
+  if (fmt == c_format_gzip)
+    wbits |= 16; // magic number 16 = compress as gzip
+
+  int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
+                         wbits, 8, Z_DEFAULT_STRATEGY);
+  if (ret != Z_OK) {
+    log_warn("compression failure (initialization): %s", strm.msg);
+    return -1;
+  }
+
+  if (fmt == c_format_gzip) {
+    gz_header gzh;
+    memset(&gzh, 0, sizeof gzh);
+    gzh.os = 0xFF; // "unknown"
+    ret = deflateSetHeader(&strm, &gzh);
+    if (ret != Z_OK) {
+      log_warn("compression failure (initialization): %s", strm.msg);
+      return -1;
+    }
+  }
+
+  strm.next_in = const_cast<Bytef*>(source);
+  strm.avail_in = slen;
+  strm.next_out = dest;
+  strm.avail_out = dlen;
+
+  ret = deflate(&strm, Z_FINISH);
+  if (ret != Z_STREAM_END) {
+    log_warn("compression failure: %s", strm.msg);
+    deflateEnd(&strm);
+    return -1;
+  }
+
+  deflateEnd(&strm);
+  return strm.total_out;
+}
+
+ssize_t
+decompress(const uint8_t *source, size_t slen, uint8_t *dest, size_t dlen)
+{
+  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
+    return -1;
+
+  /* allocate inflate state */
+  z_stream strm;
+  memset(&strm, 0, sizeof strm);
+  int ret = inflateInit2(&strm, MAX_WBITS|32); /* autodetect gzip/zlib */
+  if (ret != Z_OK) {
+    log_warn("decompression failure (initialization): %s", strm.msg);
+    return -1;
+  }
+
+  strm.next_in = const_cast<Bytef*>(source);
+  strm.avail_in = slen;
+  strm.next_out = dest;
+  strm.avail_out = dlen;
+
+  ret = inflate(&strm, Z_FINISH);
+  if (ret != Z_STREAM_END) {
+    log_warn("decompression failure: %s", strm.msg);
+    inflateEnd(&strm);
+    return -1;
+  }
+
+  inflateEnd(&strm);
+  return strm.total_out;
+}
diff --git a/src/compression.h b/src/compression.h
new file mode 100644
index 0000000..ef1f75e
--- /dev/null
+++ b/src/compression.h
@@ -0,0 +1,35 @@
+/* Copyright 2011, 2012 SRI International
+ * See LICENSE for other credits and copying information
+ */
+#ifndef _COMPRESSION_H
+#define _COMPRESSION_H
+
+enum compression_format {
+  c_format_zlib = 0,
+  c_format_gzip = 1
+};
+
+/**
+ * Compress SLEN bytes of data from the buffer at SOURCE into the
+ * buffer at DEST.  There are DLEN bytes of available space at the
+ * destination.  FMT specifies the desired format of the compressed
+ * data: currently 'zlib' (RFC 1950) and 'gzip' (RFC 1952) formats are
+ * supported.
+ *
+ * Returns the amount of data actually written to DEST, or -1 on error.
+ */
+ssize_t compress(const uint8_t *source, size_t slen,
+                 uint8_t *dest, size_t dlen,
+                 compression_format fmt);
+
+/**
+ * Decompress SLEN bytes of data from the buffer at SOURCE into the
+ * buffer at DEST.  There are DLEN bytes of available space at the
+ * destination.  Automatically detects the compression format in use.
+ *
+ * Returns the amount of data actually written to DEST, or -1 on error.
+ */
+ssize_t decompress(const uint8_t *source, size_t slen,
+                   uint8_t *dest, size_t dlen);
+
+#endif
diff --git a/src/crypt.cc b/src/crypt.cc
index e60cc41..053f7e6 100644
--- a/src/crypt.cc
+++ b/src/crypt.cc
@@ -384,8 +384,12 @@ gcm_decryptor_impl::decrypt(uint8_t *out, const uint8_t *in, size_t inlen,
   if (!EVP_DecryptUpdate(&ctx, out, &olen, in, inlen) || size_t(olen) != inlen)
     return log_crypto_warn("gcm_encryptor::decrypt");
 
-  if (!EVP_DecryptFinal_ex(&ctx, out + inlen, &olen) || olen != 0)
+  if (!EVP_DecryptFinal_ex(&ctx, out + inlen, &olen) || olen != 0) {
+    /* don't warn for simple MAC failures */
+    if (!ERR_peek_error())
+      return -1;
     return log_crypto_warn("gcm_decryptor::check tag");
+  }
 
   return 0;
 }
diff --git a/src/steg/jsSteg.cc b/src/steg/jsSteg.cc
index 8fee7cb..1d71918 100644
--- a/src/steg/jsSteg.cc
+++ b/src/steg/jsSteg.cc
@@ -5,6 +5,7 @@
 #include "payloads.h"
 #include "jsSteg.h"
 #include "cookies.h"
+#include "compression.h"
 
 void buf_dump(unsigned char* buf, int len, FILE *out);
 
@@ -828,8 +829,8 @@ http_server_JS_transmit (payloads& pl, struct evbuffer *source, conn_t *conn,
     // sizeof outbuf2 = cLen + 10-byte for gzip header + 8-byte for crc 
     outbuf2 = (char *)xmalloc(cLen+18);  
 
-    outbuf2len = gzDeflate((const uint8_t *)outbuf, cLen,
-                           (uint8_t *)outbuf2, cLen+18, time(NULL));
+    outbuf2len = compress((const uint8_t *)outbuf, cLen,
+                          (uint8_t *)outbuf2, cLen+18, c_format_gzip);
 
     if (outbuf2len <= 0) {
       log_warn("gzDeflate for outbuf fails");
@@ -992,8 +993,8 @@ http_handle_client_JS_receive(steg_t *, conn_t *conn, struct evbuffer *dest, str
   gzipMode = isGzipContent(respMsg);
   if (gzipMode) {
     log_debug("gzip content encoding detected");
-    buf2len = gzInflate((const uint8_t *)httpBody, httpBodyLen,
-                        (uint8_t *)buf2, HTTP_MSG_BUF_SIZE);
+    buf2len = decompress((const uint8_t *)httpBody, httpBodyLen,
+                         (uint8_t *)buf2, HTTP_MSG_BUF_SIZE);
     if (buf2len <= 0) {
       log_warn("gzInflate for httpBody fails");
       fprintf(stderr, "gzInflate for httpBody fails");
diff --git a/src/steg/jsSteg.h b/src/steg/jsSteg.h
index 576f91f..40ec611 100644
--- a/src/steg/jsSteg.h
+++ b/src/steg/jsSteg.h
@@ -13,7 +13,6 @@
 #include "connections.h"
 #include "steg.h"
 #include <event2/buffer.h>
-#include "zpack.h"
 
 // error codes
 #define INVALID_BUF_SIZE	-1
diff --git a/src/steg/swfSteg.cc b/src/steg/swfSteg.cc
index 77c425b..1510043 100644
--- a/src/steg/swfSteg.cc
+++ b/src/steg/swfSteg.cc
@@ -3,10 +3,9 @@
  */
 #include "util.h"
 #include "swfSteg.h"
+#include "compression.h"
 #include "connections.h"
 #include "payloads.h"
-#include "zlib.h"
-#include "zpack.h"
 
 #include <event2/buffer.h>
 
@@ -62,18 +61,12 @@ swf_wrap(payloads& pl, char* inbuf, int in_len, char* outbuf, int out_sz) {
   memcpy(tmp_buf, swf+8, SWF_SAVE_HEADER_LEN);
   memcpy(tmp_buf+SWF_SAVE_HEADER_LEN, inbuf, in_len);
   memcpy(tmp_buf+SWF_SAVE_HEADER_LEN+in_len, swf + in_swf_len - SWF_SAVE_FOOTER_LEN, SWF_SAVE_FOOTER_LEN);
-  out_swf_len = def((const uint8_t *)tmp_buf,
-                    SWF_SAVE_HEADER_LEN + in_len + SWF_SAVE_FOOTER_LEN,
-                    (uint8_t *)tmp_buf2+8, 
-		    in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512-8, 
-		    Z_DEFAULT_COMPRESSION);
-
-  //  sprintf(hdr, "%s%d\r\n\r\n", http_response_1, out_swf_len + 8);
-
-
-
-  //  fprintf(stderr, "out_swf_len = %d\n", out_swf_len);
-
+  out_swf_len =
+    compress((const uint8_t *)tmp_buf,
+             SWF_SAVE_HEADER_LEN + in_len + SWF_SAVE_FOOTER_LEN,
+             (uint8_t *)tmp_buf2+8,
+             in_len + SWF_SAVE_HEADER_LEN + SWF_SAVE_FOOTER_LEN + 512-8,
+             c_format_zlib);
 
   hdr_len =   gen_response_header((char*) "application/x-shockwave-flash", 0, out_swf_len + 8, hdr, sizeof(hdr));
 
@@ -100,8 +93,8 @@ swf_unwrap(char* inbuf, int in_len, char* outbuf, int out_sz) {
 
   tmp_buf = (char *)xmalloc(in_len * 8);
 
-  inf_len = inf((const uint8_t *)inbuf + 8, in_len - 8,
-                (uint8_t *)tmp_buf, in_len * 8); 
+  inf_len = decompress((const uint8_t *)inbuf + 8, in_len - 8,
+                       (uint8_t *)tmp_buf, in_len * 8); 
 
   //  fprintf(stderr, "in_swf_len = %d\n", in_len -8 );
 
diff --git a/src/test/unittest_compression.cc b/src/test/unittest_compression.cc
new file mode 100644
index 0000000..e624d28
--- /dev/null
+++ b/src/test/unittest_compression.cc
@@ -0,0 +1,437 @@
+/* Copyright 2012 SRI International
+ * See LICENSE for other credits and copying information
+ */
+
+#include "util.h"
+#include "unittest.h"
+
+#include "compression.h"
+
+// Smoke tests for zlib.
+// Compressed strings generated with Python's 'zlib' and 'gzip'
+// modules, which wrap zlib, so they only constitute a round-trip
+// test, not a proper test.  Z_DEFAULT_COMPRESSION (6) used in all
+// cases.
+//
+// If anyone knows a reliable source for "official" test vectors for
+// RFC 1950 through 1952, please let us know.
+
+struct zlib_testvec
+{
+  const uint8_t *text;
+  size_t tlen;
+  const uint8_t *zlibbed;
+  size_t zlen;
+  const uint8_t *gzipped;
+  size_t glen;
+};
+
+#define GZH(g) \
+  "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff" g
+
+#define T(t, z, g)                              \
+  { (const uint8_t *)t, sizeof t - 1,           \
+    (const uint8_t *)z, sizeof z - 1,           \
+    (const uint8_t *)GZH(g), sizeof GZH(g) - 1 }
+
+const zlib_testvec testvecs[] = {
+  // the empty string
+  T("",
+    "\x78\x9c\x03\x00\x00\x00\x00\x01",
+    "\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
+
+  // 4-byte messages
+  T("\x00\x00\x00\x00",
+    "\x78\x9c\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01",
+    "\x63\x60\x60\x60\x00\x00\x1c\xdf\x44\x21\x04\x00\x00\x00"),
+  T("\x11\x11\x11\x11",
+    "\x78\x9c\x13\x14\x14\x14\x04\x00\x00\xae\x00\x45",
+    "\x13\x14\x14\x14\x04\x00\x43\x25\x6c\xed\x04\x00\x00\x00"),
+  T("\x22\x22\x22\x22",
+    "\x78\x9c\x53\x52\x52\x52\x02\x00\x01\x58\x00\x89",
+    "\x53\x52\x52\x52\x02\x00\xe3\x2d\x64\x62\x04\x00\x00\x00"),
+  T("\x33\x33\x33\x33",
+    "\x78\x9c\x33\x36\x36\x36\x06\x00\x02\x02\x00\xcd",
+    "\x33\x36\x36\x36\x06\x00\xbc\xd7\x4c\xae\x04\x00\x00\x00"),
+  T("\x44\x44\x44\x44",
+    "\x78\x9c\x73\x71\x71\x71\x01\x00\x02\xac\x01\x11",
+    "\x73\x71\x71\x71\x01\x00\xe2\x3a\x05\xa7\x04\x00\x00\x00"),
+  T("\x55\x55\x55\x55",
+    "\x78\x9c\x0b\x0d\x0d\x0d\x05\x00\x03\x56\x01\x55",
+    "\x0b\x0d\x0d\x0d\x05\x00\xbd\xc0\x2d\x6b\x04\x00\x00\x00"),
+  T("\x66\x66\x66\x66",
+    "\x78\x9c\x4b\x4b\x4b\x4b\x03\x00\x04\x00\x01\x99",
+    "\x4b\x4b\x4b\x4b\x03\x00\x1d\xc8\x25\xe4\x04\x00\x00\x00"),
+  T("\x77\x77\x77\x77",
+    "\x78\x9c\x2b\x2f\x2f\x2f\x07\x00\x04\xaa\x01\xdd",
+    "\x2b\x2f\x2f\x2f\x07\x00\x42\x32\x0d\x28\x04\x00\x00\x00"),
+  T("\x88\x88\x88\x88",
+    "\x78\x9c\xeb\xe8\xe8\xe8\x00\x00\x05\x54\x02\x21",
+    "\xeb\xe8\xe8\xe8\x00\x00\xa1\x12\xb6\xf6\x04\x00\x00\x00"),
+  T("\x99\x99\x99\x99",
+    "\x78\x9c\x9b\x39\x73\xe6\x4c\x00\x05\xfe\x02\x65",
+    "\x9b\x39\x73\xe6\x4c\x00\xfe\xe8\x9e\x3a\x04\x00\x00\x00"),
+  T("\xaa\xaa\xaa\xaa",
+    "\x78\x9c\x5b\xb5\x6a\xd5\x2a\x00\x06\xa8\x02\xa9",
+    "\x5b\xb5\x6a\xd5\x2a\x00\x5e\xe0\x96\xb5\x04\x00\x00\x00"),
+  T("\xbb\xbb\xbb\xbb",
+    "\x78\x9c\xdb\xbd\x7b\xf7\x6e\x00\x07\x52\x02\xed",
+    "\xdb\xbd\x7b\xf7\x6e\x00\x01\x1a\xbe\x79\x04\x00\x00\x00"),
+  T("\xcc\xcc\xcc\xcc",
+    "\x78\x9c\x3b\x73\xe6\xcc\x19\x00\x07\xfc\x03\x31",
+    "\x3b\x73\xe6\xcc\x19\x00\x5f\xf7\xf7\x70\x04\x00\x00\x00"),
+  T("\xdd\xdd\xdd\xdd",
+    "\x78\x9c\xbb\x7b\xf7\xee\x5d\x00\x08\xa6\x03\x75",
+    "\xbb\x7b\xf7\xee\x5d\x00\x00\x0d\xdf\xbc\x04\x00\x00\x00"),
+  T("\xee\xee\xee\xee",
+    "\x78\x9c\x7b\xf7\xee\xdd\x3b\x00\x09\x50\x03\xb9",
+    "\x7b\xf7\xee\xdd\x3b\x00\xa0\x05\xd7\x33\x04\x00\x00\x00"),
+  T("\xff\xff\xff\xff",
+    "\x78\x9c\xfb\xff\xff\xff\x7f\x00\x09\xfa\x03\xfd",
+    "\xfb\xff\xff\xff\x7f\x00\xff\xff\xff\xff\x04\x00\x00\x00"),
+  T("\x40\x49\x0f\xdb", // pi (IEEE single)
+    "\x78\x9c\x73\xf0\xe4\xbf\x0d\x00\x02\xd8\x01\x74",
+    "\x73\xf0\xe4\xbf\x0d\x00\xfd\x26\x82\x53\x04\x00\x00\x00"),
+
+  // 8-byte messages
+  T("\x00\x00\x00\x00\x00\x00\x00\x00",
+    "\x78\x9c\x63\x60\x80\x00\x00\x00\x08\x00\x01",
+    "\x63\x60\x80\x00\x00\x69\xdf\x22\x65\x08\x00\x00\x00"),
+  T("\x00\x00\x00\x00\xff\xff\xff\xff",
+    "\x78\x9c\x63\x60\x60\x60\xf8\x0f\x04\x00\x09\xfe\x03\xfd",
+    "\x63\x60\x60\x60\xf8\x0f\x04\x00\x8a\xff\x99\xbb\x08\x00\x00\x00"),
+  T("\xff\xff\xff\xff\x00\x00\x00\x00",
+    "\x78\x9c\xfb\xff\xff\xff\x7f\x06\x20\x00\x00\x19\xee\x03\xfd",
+    "\xfb\xff\xff\xff\x7f\x06\x20\x00\x00\xff\xff\xff\xff\x08\x00\x00\x00"),
+  T("\xff\xff\xff\xff\xff\xff\xff\xff",
+    "\x78\x9c\xfb\xff\x1f\x02\x00\x23\xe4\x07\xf9",
+    "\xfb\xff\x1f\x02\x00\x1c\xdf\x44\x21\x08\x00\x00\x00"),
+  T("\x55\x55\x55\x55\x55\x55\x55\x55",
+    "\x78\x9c\x0b\x0d\x85\x00\x00\x0b\xfc\x02\xa9",
+    "\x0b\x0d\x85\x00\x00\x85\x22\xd0\xef\x08\x00\x00\x00"),
+  T("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
+    "\x78\x9c\x5b\xb5\x0a\x02\x00\x17\xf0\x05\x51",
+    "\x5b\xb5\x0a\x02\x00\xf0\x22\xb6\xab\x08\x00\x00\x00"),
+  T("\x40\x09\x21\xfb\x54\x44\x2d\x18", // pi (IEEE double)
+    "\x78\x9c\x73\xe0\x54\xfc\x1d\xe2\xa2\x2b\x01\x00\x0a\x82\x02\x43",
+    "\x73\xe0\x54\xfc\x1d\xe2\xa2\x2b\x01\x00\xd8\x90\x89\x2a\x08\x00\x00\x00"),
+
+  // 32-byte messages: all possible bytes occur once
+  T("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
+    "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
+    "\x78\x9c\x63\x60\x64\x62\x66\x61\x65\x63\xe7\xe0\xe4\xe2\xe6\xe1"
+    "\xe5\xe3\x17\x10\x14\x12\x16\x11\x15\x13\x97\x90\x94\x92\x96\x91"
+    "\x95\x93\x07\x00\x15\x70\x01\xf1",
+    "\x63\x60\x64\x62\x66\x61\x65\x63\xe7\xe0\xe4\xe2\xe6\xe1\xe5\xe3"
+    "\x17\x10\x14\x12\x16\x11\x15\x13\x97\x90\x94\x92\x96\x91\x95\x93"
+    "\x07\x00\x8a\x7e\x26\x91\x20\x00\x00\x00"),
+  T("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
+    "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
+    "\x78\x9c\x53\x50\x54\x52\x56\x51\x55\x53\xd7\xd0\xd4\xd2\xd6\xd1"
+    "\xd5\xd3\x37\x30\x34\x32\x36\x31\x35\x33\xb7\xb0\xb4\xb2\xb6\xb1"
+    "\xb5\xb3\x07\x00\x57\x70\x05\xf1",
+    "\x53\x50\x54\x52\x56\x51\x55\x53\xd7\xd0\xd4\xd2\xd6\xd1\xd5\xd3"
+    "\x37\x30\x34\x32\x36\x31\x35\x33\xb7\xb0\xb4\xb2\xb6\xb1\xb5\xb3"
+    "\x07\x00\xe3\x06\xa6\xf6\x20\x00\x00\x00"),
+  T("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
+    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
+    "\x78\x9c\x73\x70\x74\x72\x76\x71\x75\x73\xf7\xf0\xf4\xf2\xf6\xf1"
+    "\xf5\xf3\x0f\x08\x0c\x0a\x0e\x09\x0d\x0b\x8f\x88\x8c\x8a\x8e\x89"
+    "\x8d\x8b\x07\x00\x99\x70\x09\xf1",
+    "\x73\x70\x74\x72\x76\x71\x75\x73\xf7\xf0\xf4\xf2\xf6\xf1\xf5\xf3"
+    "\x0f\x08\x0c\x0a\x0e\x09\x0d\x0b\x8f\x88\x8c\x8a\x8e\x89\x8d\x8b"
+    "\x07\x00\x58\x8e\x26\x5e\x20\x00\x00\x00"),
+  T("\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
+    "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
+    "\x78\x9c\x4b\x48\x4c\x4a\x4e\x49\x4d\x4b\xcf\xc8\xcc\xca\xce\xc9"
+    "\xcd\xcb\x2f\x28\x2c\x2a\x2e\x29\x2d\x2b\xaf\xa8\xac\xaa\xae\xa9"
+    "\xad\xab\x07\x00\xdb\x70\x0d\xf1",
+    "\x4b\x48\x4c\x4a\x4e\x49\x4d\x4b\xcf\xc8\xcc\xca\xce\xc9\xcd\xcb"
+    "\x2f\x28\x2c\x2a\x2e\x29\x2d\x2b\xaf\xa8\xac\xaa\xae\xa9\xad\xab"
+    "\x07\x00\x31\xf6\xa6\x39\x20\x00\x00\x00"),
+  T("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+    "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+    "\x78\x9c\x01\x20\x00\xdf\xff\x80\x81\x82\x83\x84\x85\x86\x87\x88"
+    "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98"
+    "\x99\x9a\x9b\x9c\x9d\x9e\x9f\x1d\x7f\x11\xf1",
+    "\x01\x20\x00\xdf\xff\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
+    "\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a"
+    "\x9b\x9c\x9d\x9e\x9f\x6f\x99\x56\xd4\x20\x00\x00\x00"),
+  T("\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
+    "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
+    "\x78\x9c\x01\x20\x00\xdf\xff\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
+    "\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8"
+    "\xb9\xba\xbb\xbc\xbd\xbe\xbf\x5f\x7f\x15\xf1",
+    "\x01\x20\x00\xdf\xff\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa"
+    "\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba"
+    "\xbb\xbc\xbd\xbe\xbf\x06\xe1\xd6\xb3\x20\x00\x00\x00"),
+  T("\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
+    "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
+    "\x78\x9c\x01\x20\x00\xdf\xff\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8"
+    "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
+    "\xd9\xda\xdb\xdc\xdd\xde\xdf\xa1\x7f\x19\xf1",
+    "\x01\x20\x00\xdf\xff\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca"
+    "\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda"
+    "\xdb\xdc\xdd\xde\xdf\xbd\x69\x56\x1b\x20\x00\x00\x00"),
+  T("\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
+    "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
+    "\x78\x9c\x01\x20\x00\xdf\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
+    "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"
+    "\xf9\xfa\xfb\xfc\xfd\xfe\xff\xe3\x7f\x1d\xf1",
+    "\x01\x20\x00\xdf\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea"
+    "\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa"
+    "\xfb\xfc\xfd\xfe\xff\xd4\x11\xd6\x7c\x20\x00\x00\x00"),
+
+  // 26- to 40-letter pangrams from Wikipedia, all nonletters removed,
+  // all lowercase
+  T("cwmfjordbankglyphsvextquiz",
+    "\x78\x9c\x4b\x2e\xcf\x4d\xcb\xca\x2f\x4a\x49\x4a\xcc\xcb\x4e\xcf"
+    "\xa9\x2c\xc8\x28\x2e\x4b\xad\x28\x29\x2c\xcd\xac\x02\x00\x93\xd9"
+    "\x0b\x20",
+    "\x4b\x2e\xcf\x4d\xcb\xca\x2f\x4a\x49\x4a\xcc\xcb\x4e\xcf\xa9\x2c"
+    "\xc8\x28\x2e\x4b\xad\x28\x29\x2c\xcd\xac\x02\x00\x10\x89\x30\x81"
+    "\x1a\x00\x00\x00"),
+  T("nymphsblitzquickvexdwarfjog",
+    "\x78\x9c\xcb\xab\xcc\x2d\xc8\x28\x4e\xca\xc9\x2c\xa9\x2a\x2c\xcd"
+    "\x4c\xce\x2e\x4b\xad\x48\x29\x4f\x2c\x4a\xcb\xca\x4f\x07\x00\xa2"
+    "\xd1\x0b\x89",
+    "\xcb\xab\xcc\x2d\xc8\x28\x4e\xca\xc9\x2c\xa9\x2a\x2c\xcd\x4c\xce"
+    "\x2e\x4b\xad\x48\x29\x4f\x2c\x4a\xcb\xca\x4f\x07\x00\xae\x01\xb2"
+    "\xc9\x1b\x00\x00\x00"),
+  T("brickquizwhangsjumpyveldtfox",
+    "\x78\x9c\x4b\x2a\xca\x4c\xce\x2e\x2c\xcd\xac\x2a\xcf\x48\xcc\x4b"
+    "\x2f\xce\x2a\xcd\x2d\xa8\x2c\x4b\xcd\x49\x29\x49\xcb\xaf\x00\x00"
+    "\xac\xcf\x0b\xfe",
+    "\x4b\x2a\xca\x4c\xce\x2e\x2c\xcd\xac\x2a\xcf\x48\xcc\x4b\x2f\xce"
+    "\x2a\xcd\x2d\xa8\x2c\x4b\xcd\x49\x29\x49\xcb\xaf\x00\x00\xeb\x36"
+    "\x78\xc1\x1c\x00\x00\x00"),
+  T("vexednymphsgoforquickwaltzjob",
+    "\x78\x9c\x2b\x4b\xad\x48\x4d\xc9\xab\xcc\x2d\xc8\x28\x4e\xcf\x4f"
+    "\xcb\x2f\x2a\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\xca\xca\x4f\x02"
+    "\x00\xba\x43\x0c\x63",
+    "\x2b\x4b\xad\x48\x4d\xc9\xab\xcc\x2d\xc8\x28\x4e\xcf\x4f\xcb\x2f"
+    "\x2a\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\xca\xca\x4f\x02\x00\x60"
+    "\x23\x8c\xea\x1d\x00\x00\x00"),
+  T("bothfickledwarvesjinxmypigquiz",
+    "\x78\x9c\x4b\xca\x2f\xc9\x48\xcb\x4c\xce\xce\x49\x4d\x29\x4f\x2c"
+    "\x2a\x4b\x2d\xce\xca\xcc\xab\xc8\xad\x2c\xc8\x4c\x2f\x2c\xcd\xac"
+    "\x02\x00\xc2\xd3\x0c\xc0",
+    "\x4b\xca\x2f\xc9\x48\xcb\x4c\xce\xce\x49\x4d\x29\x4f\x2c\x2a\x4b"
+    "\x2d\xce\xca\xcc\xab\xc8\xad\x2c\xc8\x4c\x2f\x2c\xcd\xac\x02\x00"
+    "\x5f\x5f\x7c\x3c\x1e\x00\x00\x00"),
+  T("ficklejinxbogdwarvesspymathquiz",
+    "\x78\x9c\x4b\xcb\x4c\xce\xce\x49\xcd\xca\xcc\xab\x48\xca\x4f\x4f"
+    "\x29\x4f\x2c\x2a\x4b\x2d\x2e\x2e\xa8\xcc\x4d\x2c\xc9\x28\x2c\xcd"
+    "\xac\x02\x00\xcf\x8d\x0d\x2b",
+    "\x4b\xcb\x4c\xce\xce\x49\xcd\xca\xcc\xab\x48\xca\x4f\x4f\x29\x4f"
+    "\x2c\x2a\x4b\x2d\x2e\x2e\xa8\xcc\x4d\x2c\xc9\x28\x2c\xcd\xac\x02"
+    "\x00\x07\x28\x86\x75\x1f\x00\x00\x00"),
+  T("averybadquackmightjinxzippyfowls",
+    "\x78\x9c\x4b\x2c\x4b\x2d\xaa\x4c\x4a\x4c\x29\x2c\x4d\x4c\xce\xce"
+    "\xcd\x4c\xcf\x28\xc9\xca\xcc\xab\xa8\xca\x2c\x28\xa8\x4c\xcb\x2f"
+    "\xcf\x29\x06\x00\xdd\xb8\x0d\x9d",
+    "\x4b\x2c\x4b\x2d\xaa\x4c\x4a\x4c\x29\x2c\x4d\x4c\xce\xce\xcd\x4c"
+    "\xcf\x28\xc9\xca\xcc\xab\xa8\xca\x2c\x28\xa8\x4c\xcb\x2f\xcf\x29"
+    "\x06\x00\xaa\x73\x33\x56\x20\x00\x00\x00"),
+  T("aquickbrownfoxjumpsoverthelazydog",
+    "\x78\x9c\x4b\x2c\x2c\xcd\x4c\xce\x4e\x2a\xca\x2f\xcf\x4b\xcb\xaf"
+    "\xc8\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49\xac\xaa"
+    "\x4c\xc9\x4f\x07\x00\xef\x3d\x0e\x1a",
+    "\x4b\x2c\x2c\xcd\x4c\xce\x4e\x2a\xca\x2f\xcf\x4b\xcb\xaf\xc8\x2a"
+    "\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49\xac\xaa\x4c\xc9"
+    "\x4f\x07\x00\x00\x66\xdf\x56\x21\x00\x00\x00"),
+  T("quizzicaltwinsprovedmyhijackbugfix",
+    "\x78\x9c\x2b\x2c\xcd\xac\xaa\xca\x4c\x4e\xcc\x29\x29\xcf\xcc\x2b"
+    "\x2e\x28\xca\x2f\x4b\x4d\xc9\xad\xcc\xc8\xcc\x4a\x4c\xce\x4e\x2a"
+    "\x4d\x4f\xcb\xac\x00\x00\xff\xe8\x0e\x77",
+    "\x2b\x2c\xcd\xac\xaa\xca\x4c\x4e\xcc\x29\x29\xcf\xcc\x2b\x2e\x28"
+    "\xca\x2f\x4b\x4d\xc9\xad\xcc\xc8\xcc\x4a\x4c\xce\x4e\x2a\x4d\x4f"
+    "\xcb\xac\x00\x00\x01\x07\x14\xb7\x22\x00\x00\x00"),
+  T("whenzombiesarrivequicklyfaxjudgepat",
+    "\x78\x9c\x2b\xcf\x48\xcd\xab\xca\xcf\x4d\xca\x4c\x2d\x4e\x2c\x2a"
+    "\xca\x2c\x4b\x2d\x2c\xcd\x4c\xce\xce\xa9\x4c\x4b\xac\xc8\x2a\x4d"
+    "\x49\x4f\x2d\x48\x2c\x01\x00\x0b\x5d\x0e\xca",
+    "\x2b\xcf\x48\xcd\xab\xca\xcf\x4d\xca\x4c\x2d\x4e\x2c\x2a\xca\x2c"
+    "\x4b\x2d\x2c\xcd\x4c\xce\xce\xa9\x4c\x4b\xac\xc8\x2a\x4d\x49\x4f"
+    "\x2d\x48\x2c\x01\x00\xae\xef\x98\xc3\x23\x00\x00\x00"),
+  T("heavyboxesperformquickwaltzesandjigs",
+    "\x78\x9c\xcb\x48\x4d\x2c\xab\x4c\xca\xaf\x48\x2d\x2e\x48\x2d\x4a"
+    "\xcb\x2f\xca\x2d\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\x4a\x2d\x4e"
+    "\xcc\x4b\xc9\xca\x4c\x2f\x06\x00\x1a\xc0\x0f\x41",
+    "\xcb\x48\x4d\x2c\xab\x4c\xca\xaf\x48\x2d\x2e\x48\x2d\x4a\xcb\x2f"
+    "\xca\x2d\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\x4a\x2d\x4e\xcc\x4b"
+    "\xc9\xca\x4c\x2f\x06\x00\x71\xc5\x60\xbb\x24\x00\x00\x00"),
+  T("fakebugsputinwaxjonquilsdrivehimcrazy",
+    "\x78\x9c\x4b\x4b\xcc\x4e\x4d\x2a\x4d\x2f\x2e\x28\x2d\xc9\xcc\x2b"
+    "\x4f\xac\xc8\xca\xcf\x2b\x2c\xcd\xcc\x29\x4e\x29\xca\x2c\x4b\xcd"
+    "\xc8\xcc\x4d\x2e\x4a\xac\xaa\x04\x00\x29\x9e\x0f\xbf",
+    "\x4b\x4b\xcc\x4e\x4d\x2a\x4d\x2f\x2e\x28\x2d\xc9\xcc\x2b\x4f\xac"
+    "\xc8\xca\xcf\x2b\x2c\xcd\xcc\x29\x4e\x29\xca\x2c\x4b\xcd\xc8\xcc"
+    "\x4d\x2e\x4a\xac\xaa\x04\x00\xf0\x24\xc5\xa1\x25\x00\x00\x00"),
+  T("wovensilkpyjamasexchangedforbluequartz",
+    "\x78\x9c\x2b\xcf\x2f\x4b\xcd\x2b\xce\xcc\xc9\x2e\xa8\xcc\x4a\xcc"
+    "\x4d\x2c\x4e\xad\x48\xce\x48\xcc\x4b\x4f\x4d\x49\xcb\x2f\x4a\xca"
+    "\x29\x4d\x2d\x2c\x4d\x2c\x2a\xa9\x02\x00\x39\xef\x10\x15",
+    "\x2b\xcf\x2f\x4b\xcd\x2b\xce\xcc\xc9\x2e\xa8\xcc\x4a\xcc\x4d\x2c"
+    "\x4e\xad\x48\xce\x48\xcc\x4b\x4f\x4d\x49\xcb\x2f\x4a\xca\x29\x4d"
+    "\x2d\x2c\x4d\x2c\x2a\xa9\x02\x00\x4e\x06\x7f\x9a\x26\x00\x00\x00"),
+  T("thequickonyxgoblinjumpsoverthelazydwarf",
+    "\x78\x9c\x2b\xc9\x48\x2d\x2c\xcd\x4c\xce\xce\xcf\xab\xac\x48\xcf"
+    "\x4f\xca\xc9\xcc\xcb\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48"
+    "\xcd\x49\xac\xaa\x4c\x29\x4f\x2c\x4a\x03\x00\x4d\x76\x10\xa8",
+    "\x2b\xc9\x48\x2d\x2c\xcd\x4c\xce\xce\xcf\xab\xac\x48\xcf\x4f\xca"
+    "\xc9\xcc\xcb\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49"
+    "\xac\xaa\x4c\x29\x4f\x2c\x4a\x03\x00\x12\x87\x47\x3b\x27\x00\x00\x00"),
+  T("amazinglyfewdiscothequesprovidejukeboxes",
+    "\x78\x9c\x4b\xcc\x4d\xac\xca\xcc\x4b\xcf\xa9\x4c\x4b\x2d\x4f\xc9"
+    "\x2c\x4e\xce\x2f\xc9\x48\x2d\x2c\x4d\x2d\x2e\x28\xca\x2f\xcb\x4c"
+    "\x49\xcd\x2a\xcd\x4e\x4d\xca\xaf\x48\x2d\x06\x00\x59\xbe\x10\xe9",
+    "\x4b\xcc\x4d\xac\xca\xcc\x4b\xcf\xa9\x4c\x4b\x2d\x4f\xc9\x2c\x4e"
+    "\xce\x2f\xc9\x48\x2d\x2c\x4d\x2d\x2e\x28\xca\x2f\xcb\x4c\x49\xcd"
+    "\x2a\xcd\x4e\x4d\xca\xaf\x48\x2d\x06\x00\xa3\xe4\xdd\x7d\x28\x00\x00\x00"),
+
+  // self-describing pangram by Lee Sallows (SciAm, 1984)
+  T("This Pangram contains four as, one b, two cs, one d, thirty es, "
+    "six fs, five gs, seven hs, eleven is, one j, one k, two ls, two ms, "
+    "eighteen ns, fifteen os, two ps, one q, five rs, twenty-seven ss, "
+    "eighteen ts, two us, seven vs, eight ws, two xs, three ys, & one z.",
+    "\x78\x9c\x4d\x4e\xcd\x1a\xc2\x20\x0c\x7b\x95\x9c\x3c\x4d\x9f\xc5"
+    "\x83\x2f\x80\xb3\x8c\xea\x56\x14\xd8\x0f\x3e\xbd\x0c\xaa\x9f\xa7"
+    "\xa4\x69\x92\xf6\xe2\x38\xe2\x6c\x64\x08\x66\x42\xef\x25\x19\x96"
+    "\x08\xeb\xe7\x00\x13\x3b\x78\x21\x5c\x3b\xa4\xd5\xa3\xd7\xf1\x56"
+    "\x46\xc7\x21\x65\x50\x51\x22\x6f\xb0\x05\x2d\x2f\x84\x61\x17\x68"
+    "\x21\x81\x2b\x8c\xc6\x4a\x59\x73\xf7\x06\x8f\xd6\x36\xc6\x86\xd3"
+    "\x6e\xe4\xc1\x25\x2a\x56\xa9\x45\xb6\x72\xaf\x86\xa7\xc6\x5f\x7a"
+    "\x23\x54\x9d\x24\xe5\x63\x3b\x15\xff\x1b\x92\xa6\xe6\xdf\x27\xcb"
+    "\x77\x8d\x55\x77\xdb\x8e\x2e\x10\x21\x17\x76\xa8\xed\xef\xd3\x07"
+    "\xaa\x0c\x59\x5f",
+    "\x4d\x4e\xcd\x1a\xc2\x20\x0c\x7b\x95\x9c\x3c\x4d\x9f\xc5\x83\x2f"
+    "\x80\xb3\x8c\xea\x56\x14\xd8\x0f\x3e\xbd\x0c\xaa\x9f\xa7\xa4\x69"
+    "\x92\xf6\xe2\x38\xe2\x6c\x64\x08\x66\x42\xef\x25\x19\x96\x08\xeb"
+    "\xe7\x00\x13\x3b\x78\x21\x5c\x3b\xa4\xd5\xa3\xd7\xf1\x56\x46\xc7"
+    "\x21\x65\x50\x51\x22\x6f\xb0\x05\x2d\x2f\x84\x61\x17\x68\x21\x81"
+    "\x2b\x8c\xc6\x4a\x59\x73\xf7\x06\x8f\xd6\x36\xc6\x86\xd3\x6e\xe4"
+    "\xc1\x25\x2a\x56\xa9\x45\xb6\x72\xaf\x86\xa7\xc6\x5f\x7a\x23\x54"
+    "\x9d\x24\xe5\x63\x3b\x15\xff\x1b\x92\xa6\xe6\xdf\x27\xcb\x77\x8d"
+    "\x55\x77\xdb\x8e\x2e\x10\x21\x17\x76\xa8\xed\xef\xd3\x07\x11\xb4"
+    "\xb5\x3a\x09\x01\x00\x00"),
+
+  // "Lion-Eating Poet in the Stone Den" by Chao Yuen Ren,
+  // Simplified Chinese text in UTF-8
+  T("\xe3\x80\x8a\xe6\x96\xbd\xe6\xb0\x8f\xe9\xa3\x9f\xe7\x8b\xae\xe5\x8f"
+    "\xb2\xe3\x80\x8b \xe7\x9f\xb3\xe5\xae\xa4\xe8\xaf\x97\xe5\xa3\xab\xe6"
+    "\x96\xbd\xe6\xb0\x8f\xef\xbc\x8c\xe5\x97\x9c\xe7\x8b\xae\xef\xbc\x8c"
+    "\xe8\xaa\x93\xe9\xa3\x9f\xe5\x8d\x81\xe7\x8b\xae\xe3\x80\x82 \xe6\xb0"
+    "\x8f\xe6\x97\xb6\xe6\x97\xb6\xe9\x80\x82\xe5\xb8\x82\xe8\xa7\x86\xe7"
+    "\x8b\xae\xe3\x80\x82 \xe5\x8d\x81\xe6\x97\xb6\xef\xbc\x8c\xe9\x80\x82"
+    "\xe5\x8d\x81\xe7\x8b\xae\xe9\x80\x82\xe5\xb8\x82\xe3\x80\x82 \xe6\x98"
+    "\xaf\xe6\x97\xb6\xef\xbc\x8c\xe9\x80\x82\xe6\x96\xbd\xe6\xb0\x8f\xe9"
+    "\x80\x82\xe5\xb8\x82\xe3\x80\x82 \xe6\xb0\x8f\xe8\xa7\x86\xe6\x98\xaf"
+    "\xe5\x8d\x81\xe7\x8b\xae\xef\xbc\x8c\xe6\x81\x83\xe7\x9f\xa2\xe5\x8a"
+    "\xbf\xef\xbc\x8c\xe4\xbd\xbf\xe6\x98\xaf\xe5\x8d\x81\xe7\x8b\xae\xe9"
+    "\x80\x9d\xe4\xb8\x96\xe3\x80\x82 \xe6\xb0\x8f\xe6\x8b\xbe\xe6\x98\xaf"
+    "\xe5\x8d\x81\xe7\x8b\xae\xe5\xb0\xb8\xef\xbc\x8c\xe9\x80\x82\xe7\x9f"
+    "\xb3\xe5\xae\xa4\xe3\x80\x82 \xe7\x9f\xb3\xe5\xae\xa4\xe6\xb9\xbf\xef"
+    "\xbc\x8c\xe6\xb0\x8f\xe4\xbd\xbf\xe4\xbe\x8d\xe6\x8b\xad\xe7\x9f\xb3"
+    "\xe5\xae\xa4\xe3\x80\x82 \xe7\x9f\xb3\xe5\xae\xa4\xe6\x8b\xad\xef\xbc"
+    "\x8c\xe6\xb0\x8f\xe5\xa7\x8b\xe8\xaf\x95\xe9\xa3\x9f\xe6\x98\xaf\xe5"
+    "\x8d\x81\xe7\x8b\xae\xe3\x80\x82 \xe9\xa3\x9f\xe6\x97\xb6\xef\xbc\x8c"
+    "\xe5\xa7\x8b\xe8\xaf\x86\xe6\x98\xaf\xe5\x8d\x81\xe7\x8b\xae\xe5\xb0"
+    "\xb8\xef\xbc\x8c\xe5\xae\x9e\xe5\x8d\x81\xe7\x9f\xb3\xe7\x8b\xae\xe5"
+    "\xb0\xb8\xe3\x80\x82 \xe8\xaf\x95\xe9\x87\x8a\xe6\x98\xaf\xe4\xba\x8b"
+    "\xe3\x80\x82",
+    "\x78\x9c\x65\x8f\xdd\x0a\x82\x40\x10\x46\x5f\xc5\x47\x5d\x0d\x0c"
+    "\xdc\xc4\x8b\x48\xbb\xa8\x84\x0c\x84\xc2\x1f\x12\xc2\x22\xf5\x61"
+    "\x74\x56\xbd\xea\x15\xfa\x76\xd7\x0c\x0a\xf6\x62\x66\xcf\x99\x6f"
+    "\x67\x5b\xe6\x08\xbf\x12\xb9\x37\x46\x61\xcf\x53\xf2\xae\x2d\xe3"
+    "\x46\x1f\x16\x94\x9e\x86\x2c\xa0\xe8\xa2\xf1\xeb\xb9\xa2\x60\x07"
+    "\x03\xc5\x70\x5e\xc3\x26\xd7\x44\xdb\x32\xcb\x00\x16\xc1\x0d\x67"
+    "\x64\x16\x95\xd6\x10\xdb\x13\x80\x82\x5b\x4c\x48\xa0\x74\x6d\xa8"
+    "\xa1\x6d\x36\xb3\x69\x83\x2f\xcb\x3d\x84\xc0\xd0\x43\x90\x84\xb9"
+    "\xe8\xc3\x23\x39\x0d\xea\xae\x6a\x66\x34\xb2\x7d\x57\xfa\x9f\x25"
+    "\x78\x3d\x03\xca\x4b\x9d\xad\xbf\x22\x0d\x5d\x89\xbb\xcc\x80\x8d"
+    "\x98\xae\x76\x05\x4f\xfe\x0c\x9e\x68\x83\x62\x3e\x64\x1b\x7c\x75"
+    "\x4e\x95\x92\xec\xd5\xe2\x0a\xdb\x3f\x2f\x52\x7a\x90\x6d\x58\xe8"
+    "\x1b\x39\x20\x33\x96\x0e\xbc\xee\xc1\xd1\xbf\x01\xae\x11\xff\x53",
+    "\x65\x8f\xdd\x0a\x82\x40\x10\x46\x5f\xc5\x47\x5d\x0d\x0c\xdc\xc4"
+    "\x8b\x48\xbb\xa8\x84\x0c\x84\xc2\x1f\x12\xc2\x22\xf5\x61\x74\x56"
+    "\xbd\xea\x15\xfa\x76\xd7\x0c\x0a\xf6\x62\x66\xcf\x99\x6f\x67\x5b"
+    "\xe6\x08\xbf\x12\xb9\x37\x46\x61\xcf\x53\xf2\xae\x2d\xe3\x46\x1f"
+    "\x16\x94\x9e\x86\x2c\xa0\xe8\xa2\xf1\xeb\xb9\xa2\x60\x07\x03\xc5"
+    "\x70\x5e\xc3\x26\xd7\x44\xdb\x32\xcb\x00\x16\xc1\x0d\x67\x64\x16"
+    "\x95\xd6\x10\xdb\x13\x80\x82\x5b\x4c\x48\xa0\x74\x6d\xa8\xa1\x6d"
+    "\x36\xb3\x69\x83\x2f\xcb\x3d\x84\xc0\xd0\x43\x90\x84\xb9\xe8\xc3"
+    "\x23\x39\x0d\xea\xae\x6a\x66\x34\xb2\x7d\x57\xfa\x9f\x25\x78\x3d"
+    "\x03\xca\x4b\x9d\xad\xbf\x22\x0d\x5d\x89\xbb\xcc\x80\x8d\x98\xae"
+    "\x76\x05\x4f\xfe\x0c\x9e\x68\x83\x62\x3e\x64\x1b\x7c\x75\x4e\x95"
+    "\x92\xec\xd5\xe2\x0a\xdb\x3f\x2f\x52\x7a\x90\x6d\x58\xe8\x1b\x39"
+    "\x20\x33\x96\x0e\xbc\xee\xc1\xd1\xbf\x01\xe5\x06\xa6\x1f\x72\x01"
+    "\x00\x00"),
+
+  { 0, 0, 0, 0, 0, 0 }
+};
+
+#undef T
+#undef GZH
+
+static void
+test_compress_zlib(void *)
+{
+  uint8_t obuf[1024];
+  for (const zlib_testvec *t = testvecs; t->text; t++) {
+    ssize_t n = compress(t->text, t->tlen, obuf, sizeof obuf, c_format_zlib);
+    tt_uint_op(n, ==, t->zlen);
+    tt_mem_op(obuf, ==, t->zlibbed, t->zlen);
+  }
+
+ end:;
+}
+
+static void
+test_decompress_zlib(void *)
+{
+  uint8_t obuf[1024];
+  for (const zlib_testvec *t = testvecs; t->text; t++) {
+    ssize_t n = decompress(t->zlibbed, t->zlen, obuf, sizeof obuf);
+    tt_uint_op(n, ==, t->tlen);
+    tt_mem_op(obuf, ==, t->text, t->tlen);
+  }
+
+ end:;
+}
+
+static void
+test_compress_gzip(void *)
+{
+  uint8_t obuf[1024];
+  for (const zlib_testvec *t = testvecs; t->text; t++) {
+    ssize_t n = compress(t->text, t->tlen, obuf, sizeof obuf, c_format_gzip);
+    tt_uint_op(n, ==, t->glen);
+    tt_mem_op(obuf, ==, t->gzipped, t->glen);
+  }
+
+ end:;
+}
+static void
+test_decompress_gzip(void *)
+{
+  uint8_t obuf[1024];
+  for (const zlib_testvec *t = testvecs; t->text; t++) {
+    ssize_t n = decompress(t->gzipped, t->glen, obuf, sizeof obuf);
+    tt_uint_op(n, ==, t->tlen);
+    tt_mem_op(obuf, ==, t->text, t->tlen);
+  }
+
+ end:;
+}
+
+#define T(name) \
+  { #name, test_##name, 0, 0, 0 }
+
+struct testcase_t zlib_tests[] = {
+  T(compress_zlib),
+  T(decompress_zlib),
+  T(compress_gzip),
+  T(decompress_gzip),
+  END_OF_TESTCASES
+};
diff --git a/src/test/unittest_zlib.cc b/src/test/unittest_zlib.cc
deleted file mode 100644
index 2d655b2..0000000
--- a/src/test/unittest_zlib.cc
+++ /dev/null
@@ -1,507 +0,0 @@
-/* Copyright 2012 SRI International
- * See LICENSE for other credits and copying information
- */
-
-#include "util.h"
-#include "unittest.h"
-
-#include "zlib.h"
-#include "zpack.h"
-
-// Smoke tests for zlib.
-//
-// CRC values generated using a standalone crc32 implementation of
-// unknown provenance, and then validated with the implementation
-// built into zlib.
-//
-// Compressed strings generated with Python's 'zlib' and 'gzip'
-// modules, which wrap zlib, so they only constitute a round-trip
-// test, not a proper test.  Z_DEFAULT_COMPRESSION (6) used in all cases.
-//
-// If anyone knows a reliable source for "official" test vectors for
-// CRC polynomial 04C11DB7 and/or RFC 1950 through 1952, please let us
-// know.
-
-struct zlib_testvec
-{
-  const uint8_t *text;
-  size_t tlen;
-  const uint8_t *zlibbed;
-  size_t zlen;
-  const uint8_t *gzipped;
-  size_t glen;
-  uint32_t crc32;
-};
-
-#define GZH(g) \
-  "\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff" g
-
-#define T(t, z, g, c)                           \
-  { (const uint8_t *)t, sizeof t - 1,           \
-    (const uint8_t *)z, sizeof z - 1,           \
-    (const uint8_t *)GZH(g), sizeof GZH(g) - 1, \
-    0x##c }
-
-const zlib_testvec testvecs[] = {
-  // the empty string
-  T("",
-    "\x78\x9c\x03\x00\x00\x00\x00\x01",
-    "\x03\x00\x00\x00\x00\x00\x00\x00\x00\x00",
-    00000000),
-
-  // 4-byte messages
-  T("\x00\x00\x00\x00",
-    "\x78\x9c\x63\x60\x60\x60\x00\x00\x00\x04\x00\x01",
-    "\x63\x60\x60\x60\x00\x00\x1c\xdf\x44\x21\x04\x00\x00\x00",
-    2144df1c),
-  T("\x11\x11\x11\x11",
-    "\x78\x9c\x13\x14\x14\x14\x04\x00\x00\xae\x00\x45",
-    "\x13\x14\x14\x14\x04\x00\x43\x25\x6c\xed\x04\x00\x00\x00",
-    ed6c2543),
-  T("\x22\x22\x22\x22",
-    "\x78\x9c\x53\x52\x52\x52\x02\x00\x01\x58\x00\x89",
-    "\x53\x52\x52\x52\x02\x00\xe3\x2d\x64\x62\x04\x00\x00\x00",
-    62642de3),
-  T("\x33\x33\x33\x33",
-    "\x78\x9c\x33\x36\x36\x36\x06\x00\x02\x02\x00\xcd",
-    "\x33\x36\x36\x36\x06\x00\xbc\xd7\x4c\xae\x04\x00\x00\x00",
-    ae4cd7bc),
-  T("\x44\x44\x44\x44",
-    "\x78\x9c\x73\x71\x71\x71\x01\x00\x02\xac\x01\x11",
-    "\x73\x71\x71\x71\x01\x00\xe2\x3a\x05\xa7\x04\x00\x00\x00",
-    a7053ae2),
-  T("\x55\x55\x55\x55",
-    "\x78\x9c\x0b\x0d\x0d\x0d\x05\x00\x03\x56\x01\x55",
-    "\x0b\x0d\x0d\x0d\x05\x00\xbd\xc0\x2d\x6b\x04\x00\x00\x00",
-    6b2dc0bd),
-  T("\x66\x66\x66\x66",
-    "\x78\x9c\x4b\x4b\x4b\x4b\x03\x00\x04\x00\x01\x99",
-    "\x4b\x4b\x4b\x4b\x03\x00\x1d\xc8\x25\xe4\x04\x00\x00\x00",
-    e425c81d),
-  T("\x77\x77\x77\x77",
-    "\x78\x9c\x2b\x2f\x2f\x2f\x07\x00\x04\xaa\x01\xdd",
-    "\x2b\x2f\x2f\x2f\x07\x00\x42\x32\x0d\x28\x04\x00\x00\x00",
-    280d3242),
-  T("\x88\x88\x88\x88",
-    "\x78\x9c\xeb\xe8\xe8\xe8\x00\x00\x05\x54\x02\x21",
-    "\xeb\xe8\xe8\xe8\x00\x00\xa1\x12\xb6\xf6\x04\x00\x00\x00",
-    f6b612a1),
-  T("\x99\x99\x99\x99",
-    "\x78\x9c\x9b\x39\x73\xe6\x4c\x00\x05\xfe\x02\x65",
-    "\x9b\x39\x73\xe6\x4c\x00\xfe\xe8\x9e\x3a\x04\x00\x00\x00",
-    3a9ee8fe),
-  T("\xaa\xaa\xaa\xaa",
-    "\x78\x9c\x5b\xb5\x6a\xd5\x2a\x00\x06\xa8\x02\xa9",
-    "\x5b\xb5\x6a\xd5\x2a\x00\x5e\xe0\x96\xb5\x04\x00\x00\x00",
-    b596e05e),
-  T("\xbb\xbb\xbb\xbb",
-    "\x78\x9c\xdb\xbd\x7b\xf7\x6e\x00\x07\x52\x02\xed",
-    "\xdb\xbd\x7b\xf7\x6e\x00\x01\x1a\xbe\x79\x04\x00\x00\x00",
-    79be1a01),
-  T("\xcc\xcc\xcc\xcc",
-    "\x78\x9c\x3b\x73\xe6\xcc\x19\x00\x07\xfc\x03\x31",
-    "\x3b\x73\xe6\xcc\x19\x00\x5f\xf7\xf7\x70\x04\x00\x00\x00",
-    70f7f75f),
-  T("\xdd\xdd\xdd\xdd",
-    "\x78\x9c\xbb\x7b\xf7\xee\x5d\x00\x08\xa6\x03\x75",
-    "\xbb\x7b\xf7\xee\x5d\x00\x00\x0d\xdf\xbc\x04\x00\x00\x00",
-    bcdf0d00),
-  T("\xee\xee\xee\xee",
-    "\x78\x9c\x7b\xf7\xee\xdd\x3b\x00\x09\x50\x03\xb9",
-    "\x7b\xf7\xee\xdd\x3b\x00\xa0\x05\xd7\x33\x04\x00\x00\x00",
-    33d705a0),
-  T("\xff\xff\xff\xff",
-    "\x78\x9c\xfb\xff\xff\xff\x7f\x00\x09\xfa\x03\xfd",
-    "\xfb\xff\xff\xff\x7f\x00\xff\xff\xff\xff\x04\x00\x00\x00",
-    ffffffff),
-  T("\x40\x49\x0f\xdb",
-    "\x78\x9c\x73\xf0\xe4\xbf\x0d\x00\x02\xd8\x01\x74",
-    "\x73\xf0\xe4\xbf\x0d\x00\xfd\x26\x82\x53\x04\x00\x00\x00",
-    538226fd), // pi (IEEE single)
-
-  // 8-byte messages
-  T("\x00\x00\x00\x00\x00\x00\x00\x00",
-    "\x78\x9c\x63\x60\x80\x00\x00\x00\x08\x00\x01",
-    "\x63\x60\x80\x00\x00\x69\xdf\x22\x65\x08\x00\x00\x00",
-    6522df69),
-  T("\x00\x00\x00\x00\xff\xff\xff\xff",
-    "\x78\x9c\x63\x60\x60\x60\xf8\x0f\x04\x00\x09\xfe\x03\xfd",
-    "\x63\x60\x60\x60\xf8\x0f\x04\x00\x8a\xff\x99\xbb\x08\x00\x00\x00",
-    bb99ff8a),
-  T("\xff\xff\xff\xff\x00\x00\x00\x00",
-    "\x78\x9c\xfb\xff\xff\xff\x7f\x06\x20\x00\x00\x19\xee\x03\xfd",
-    "\xfb\xff\xff\xff\x7f\x06\x20\x00\x00\xff\xff\xff\xff\x08\x00\x00\x00",
-    ffffffff),
-  T("\xff\xff\xff\xff\xff\xff\xff\xff",
-    "\x78\x9c\xfb\xff\x1f\x02\x00\x23\xe4\x07\xf9",
-    "\xfb\xff\x1f\x02\x00\x1c\xdf\x44\x21\x08\x00\x00\x00",
-    2144df1c),
-  T("\x55\x55\x55\x55\x55\x55\x55\x55",
-    "\x78\x9c\x0b\x0d\x85\x00\x00\x0b\xfc\x02\xa9",
-    "\x0b\x0d\x85\x00\x00\x85\x22\xd0\xef\x08\x00\x00\x00",
-    efd02285),
-  T("\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa",
-    "\x78\x9c\x5b\xb5\x0a\x02\x00\x17\xf0\x05\x51",
-    "\x5b\xb5\x0a\x02\x00\xf0\x22\xb6\xab\x08\x00\x00\x00",
-    abb622f0),
-  T("\x40\x09\x21\xfb\x54\x44\x2d\x18",
-    "\x78\x9c\x73\xe0\x54\xfc\x1d\xe2\xa2\x2b\x01\x00\x0a\x82\x02\x43",
-    "\x73\xe0\x54\xfc\x1d\xe2\xa2\x2b\x01\x00\xd8\x90\x89\x2a\x08\x00\x00\x00",
-    2a8990d8), // pi (IEEE double)
-
-  // 32-byte messages: all possible bytes occur once
-  T("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f"
-    "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f",
-    "\x78\x9c\x63\x60\x64\x62\x66\x61\x65\x63\xe7\xe0\xe4\xe2\xe6\xe1"
-    "\xe5\xe3\x17\x10\x14\x12\x16\x11\x15\x13\x97\x90\x94\x92\x96\x91"
-    "\x95\x93\x07\x00\x15\x70\x01\xf1",
-    "\x63\x60\x64\x62\x66\x61\x65\x63\xe7\xe0\xe4\xe2\xe6\xe1\xe5\xe3"
-    "\x17\x10\x14\x12\x16\x11\x15\x13\x97\x90\x94\x92\x96\x91\x95\x93"
-    "\x07\x00\x8a\x7e\x26\x91\x20\x00\x00\x00",
-    91267e8a),
-  T("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f"
-    "\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d\x3e\x3f",
-    "\x78\x9c\x53\x50\x54\x52\x56\x51\x55\x53\xd7\xd0\xd4\xd2\xd6\xd1"
-    "\xd5\xd3\x37\x30\x34\x32\x36\x31\x35\x33\xb7\xb0\xb4\xb2\xb6\xb1"
-    "\xb5\xb3\x07\x00\x57\x70\x05\xf1",
-    "\x53\x50\x54\x52\x56\x51\x55\x53\xd7\xd0\xd4\xd2\xd6\xd1\xd5\xd3"
-    "\x37\x30\x34\x32\x36\x31\x35\x33\xb7\xb0\xb4\xb2\xb6\xb1\xb5\xb3"
-    "\x07\x00\xe3\x06\xa6\xf6\x20\x00\x00\x00",
-    f6a606e3),
-  T("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f"
-    "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f",
-    "\x78\x9c\x73\x70\x74\x72\x76\x71\x75\x73\xf7\xf0\xf4\xf2\xf6\xf1"
-    "\xf5\xf3\x0f\x08\x0c\x0a\x0e\x09\x0d\x0b\x8f\x88\x8c\x8a\x8e\x89"
-    "\x8d\x8b\x07\x00\x99\x70\x09\xf1",
-    "\x73\x70\x74\x72\x76\x71\x75\x73\xf7\xf0\xf4\xf2\xf6\xf1\xf5\xf3"
-    "\x0f\x08\x0c\x0a\x0e\x09\x0d\x0b\x8f\x88\x8c\x8a\x8e\x89\x8d\x8b"
-    "\x07\x00\x58\x8e\x26\x5e\x20\x00\x00\x00",
-    5e268e58),
-  T("\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f"
-    "\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b\x7c\x7d\x7e\x7f",
-    "\x78\x9c\x4b\x48\x4c\x4a\x4e\x49\x4d\x4b\xcf\xc8\xcc\xca\xce\xc9"
-    "\xcd\xcb\x2f\x28\x2c\x2a\x2e\x29\x2d\x2b\xaf\xa8\xac\xaa\xae\xa9"
-    "\xad\xab\x07\x00\xdb\x70\x0d\xf1",
-    "\x4b\x48\x4c\x4a\x4e\x49\x4d\x4b\xcf\xc8\xcc\xca\xce\xc9\xcd\xcb"
-    "\x2f\x28\x2c\x2a\x2e\x29\x2d\x2b\xaf\xa8\xac\xaa\xae\xa9\xad\xab"
-    "\x07\x00\x31\xf6\xa6\x39\x20\x00\x00\x00",
-    39a6f631),
-  T("\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
-    "\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
-    "\x78\x9c\x01\x20\x00\xdf\xff\x80\x81\x82\x83\x84\x85\x86\x87\x88"
-    "\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98"
-    "\x99\x9a\x9b\x9c\x9d\x9e\x9f\x1d\x7f\x11\xf1",
-    "\x01\x20\x00\xdf\xff\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a"
-    "\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a"
-    "\x9b\x9c\x9d\x9e\x9f\x6f\x99\x56\xd4\x20\x00\x00\x00",
-    d456996f),
-  T("\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf"
-    "\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba\xbb\xbc\xbd\xbe\xbf",
-    "\x78\x9c\x01\x20\x00\xdf\xff\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8"
-    "\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8"
-    "\xb9\xba\xbb\xbc\xbd\xbe\xbf\x5f\x7f\x15\xf1",
-    "\x01\x20\x00\xdf\xff\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa"
-    "\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9\xba"
-    "\xbb\xbc\xbd\xbe\xbf\x06\xe1\xd6\xb3\x20\x00\x00\x00",
-    b3d6e106),
-  T("\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf"
-    "\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda\xdb\xdc\xdd\xde\xdf",
-    "\x78\x9c\x01\x20\x00\xdf\xff\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8"
-    "\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8"
-    "\xd9\xda\xdb\xdc\xdd\xde\xdf\xa1\x7f\x19\xf1",
-    "\x01\x20\x00\xdf\xff\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca"
-    "\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8\xd9\xda"
-    "\xdb\xdc\xdd\xde\xdf\xbd\x69\x56\x1b\x20\x00\x00\x00",
-    1b5669bd),
-  T("\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef"
-    "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff",
-    "\x78\x9c\x01\x20\x00\xdf\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8"
-    "\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8"
-    "\xf9\xfa\xfb\xfc\xfd\xfe\xff\xe3\x7f\x1d\xf1",
-    "\x01\x20\x00\xdf\xff\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea"
-    "\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa"
-    "\xfb\xfc\xfd\xfe\xff\xd4\x11\xd6\x7c\x20\x00\x00\x00",
-    7cd611d4),
-
-  // 26- to 40-letter pangrams from Wikipedia, all nonletters removed,
-  // all lowercase
-  T("cwmfjordbankglyphsvextquiz",
-    "\x78\x9c\x4b\x2e\xcf\x4d\xcb\xca\x2f\x4a\x49\x4a\xcc\xcb\x4e\xcf"
-    "\xa9\x2c\xc8\x28\x2e\x4b\xad\x28\x29\x2c\xcd\xac\x02\x00\x93\xd9"
-    "\x0b\x20",
-    "\x4b\x2e\xcf\x4d\xcb\xca\x2f\x4a\x49\x4a\xcc\xcb\x4e\xcf\xa9\x2c"
-    "\xc8\x28\x2e\x4b\xad\x28\x29\x2c\xcd\xac\x02\x00\x10\x89\x30\x81"
-    "\x1a\x00\x00\x00",
-    81308910),
-  T("nymphsblitzquickvexdwarfjog",
-    "\x78\x9c\xcb\xab\xcc\x2d\xc8\x28\x4e\xca\xc9\x2c\xa9\x2a\x2c\xcd"
-    "\x4c\xce\x2e\x4b\xad\x48\x29\x4f\x2c\x4a\xcb\xca\x4f\x07\x00\xa2"
-    "\xd1\x0b\x89",
-    "\xcb\xab\xcc\x2d\xc8\x28\x4e\xca\xc9\x2c\xa9\x2a\x2c\xcd\x4c\xce"
-    "\x2e\x4b\xad\x48\x29\x4f\x2c\x4a\xcb\xca\x4f\x07\x00\xae\x01\xb2"
-    "\xc9\x1b\x00\x00\x00",
-    c9b201ae),
-  T("brickquizwhangsjumpyveldtfox",
-    "\x78\x9c\x4b\x2a\xca\x4c\xce\x2e\x2c\xcd\xac\x2a\xcf\x48\xcc\x4b"
-    "\x2f\xce\x2a\xcd\x2d\xa8\x2c\x4b\xcd\x49\x29\x49\xcb\xaf\x00\x00"
-    "\xac\xcf\x0b\xfe",
-    "\x4b\x2a\xca\x4c\xce\x2e\x2c\xcd\xac\x2a\xcf\x48\xcc\x4b\x2f\xce"
-    "\x2a\xcd\x2d\xa8\x2c\x4b\xcd\x49\x29\x49\xcb\xaf\x00\x00\xeb\x36"
-    "\x78\xc1\x1c\x00\x00\x00",
-    c17836eb),
-  T("vexednymphsgoforquickwaltzjob",
-    "\x78\x9c\x2b\x4b\xad\x48\x4d\xc9\xab\xcc\x2d\xc8\x28\x4e\xcf\x4f"
-    "\xcb\x2f\x2a\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\xca\xca\x4f\x02"
-    "\x00\xba\x43\x0c\x63",
-    "\x2b\x4b\xad\x48\x4d\xc9\xab\xcc\x2d\xc8\x28\x4e\xcf\x4f\xcb\x2f"
-    "\x2a\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\xca\xca\x4f\x02\x00\x60"
-    "\x23\x8c\xea\x1d\x00\x00\x00",
-    ea8c2360),
-  T("bothfickledwarvesjinxmypigquiz",
-    "\x78\x9c\x4b\xca\x2f\xc9\x48\xcb\x4c\xce\xce\x49\x4d\x29\x4f\x2c"
-    "\x2a\x4b\x2d\xce\xca\xcc\xab\xc8\xad\x2c\xc8\x4c\x2f\x2c\xcd\xac"
-    "\x02\x00\xc2\xd3\x0c\xc0",
-    "\x4b\xca\x2f\xc9\x48\xcb\x4c\xce\xce\x49\x4d\x29\x4f\x2c\x2a\x4b"
-    "\x2d\xce\xca\xcc\xab\xc8\xad\x2c\xc8\x4c\x2f\x2c\xcd\xac\x02\x00"
-    "\x5f\x5f\x7c\x3c\x1e\x00\x00\x00",
-    3c7c5f5f),
-  T("ficklejinxbogdwarvesspymathquiz",
-    "\x78\x9c\x4b\xcb\x4c\xce\xce\x49\xcd\xca\xcc\xab\x48\xca\x4f\x4f"
-    "\x29\x4f\x2c\x2a\x4b\x2d\x2e\x2e\xa8\xcc\x4d\x2c\xc9\x28\x2c\xcd"
-    "\xac\x02\x00\xcf\x8d\x0d\x2b",
-    "\x4b\xcb\x4c\xce\xce\x49\xcd\xca\xcc\xab\x48\xca\x4f\x4f\x29\x4f"
-    "\x2c\x2a\x4b\x2d\x2e\x2e\xa8\xcc\x4d\x2c\xc9\x28\x2c\xcd\xac\x02"
-    "\x00\x07\x28\x86\x75\x1f\x00\x00\x00",
-    75862807),
-  T("averybadquackmightjinxzippyfowls",
-    "\x78\x9c\x4b\x2c\x4b\x2d\xaa\x4c\x4a\x4c\x29\x2c\x4d\x4c\xce\xce"
-    "\xcd\x4c\xcf\x28\xc9\xca\xcc\xab\xa8\xca\x2c\x28\xa8\x4c\xcb\x2f"
-    "\xcf\x29\x06\x00\xdd\xb8\x0d\x9d",
-    "\x4b\x2c\x4b\x2d\xaa\x4c\x4a\x4c\x29\x2c\x4d\x4c\xce\xce\xcd\x4c"
-    "\xcf\x28\xc9\xca\xcc\xab\xa8\xca\x2c\x28\xa8\x4c\xcb\x2f\xcf\x29"
-    "\x06\x00\xaa\x73\x33\x56\x20\x00\x00\x00",
-    563373aa),
-  T("aquickbrownfoxjumpsoverthelazydog",
-    "\x78\x9c\x4b\x2c\x2c\xcd\x4c\xce\x4e\x2a\xca\x2f\xcf\x4b\xcb\xaf"
-    "\xc8\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49\xac\xaa"
-    "\x4c\xc9\x4f\x07\x00\xef\x3d\x0e\x1a",
-    "\x4b\x2c\x2c\xcd\x4c\xce\x4e\x2a\xca\x2f\xcf\x4b\xcb\xaf\xc8\x2a"
-    "\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49\xac\xaa\x4c\xc9"
-    "\x4f\x07\x00\x00\x66\xdf\x56\x21\x00\x00\x00",
-    56df6600),
-  T("quizzicaltwinsprovedmyhijackbugfix",
-    "\x78\x9c\x2b\x2c\xcd\xac\xaa\xca\x4c\x4e\xcc\x29\x29\xcf\xcc\x2b"
-    "\x2e\x28\xca\x2f\x4b\x4d\xc9\xad\xcc\xc8\xcc\x4a\x4c\xce\x4e\x2a"
-    "\x4d\x4f\xcb\xac\x00\x00\xff\xe8\x0e\x77",
-    "\x2b\x2c\xcd\xac\xaa\xca\x4c\x4e\xcc\x29\x29\xcf\xcc\x2b\x2e\x28"
-    "\xca\x2f\x4b\x4d\xc9\xad\xcc\xc8\xcc\x4a\x4c\xce\x4e\x2a\x4d\x4f"
-    "\xcb\xac\x00\x00\x01\x07\x14\xb7\x22\x00\x00\x00",
-    b7140701),
-  T("whenzombiesarrivequicklyfaxjudgepat",
-    "\x78\x9c\x2b\xcf\x48\xcd\xab\xca\xcf\x4d\xca\x4c\x2d\x4e\x2c\x2a"
-    "\xca\x2c\x4b\x2d\x2c\xcd\x4c\xce\xce\xa9\x4c\x4b\xac\xc8\x2a\x4d"
-    "\x49\x4f\x2d\x48\x2c\x01\x00\x0b\x5d\x0e\xca",
-    "\x2b\xcf\x48\xcd\xab\xca\xcf\x4d\xca\x4c\x2d\x4e\x2c\x2a\xca\x2c"
-    "\x4b\x2d\x2c\xcd\x4c\xce\xce\xa9\x4c\x4b\xac\xc8\x2a\x4d\x49\x4f"
-    "\x2d\x48\x2c\x01\x00\xae\xef\x98\xc3\x23\x00\x00\x00",
-    c398efae),
-  T("heavyboxesperformquickwaltzesandjigs",
-    "\x78\x9c\xcb\x48\x4d\x2c\xab\x4c\xca\xaf\x48\x2d\x2e\x48\x2d\x4a"
-    "\xcb\x2f\xca\x2d\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\x4a\x2d\x4e"
-    "\xcc\x4b\xc9\xca\x4c\x2f\x06\x00\x1a\xc0\x0f\x41",
-    "\xcb\x48\x4d\x2c\xab\x4c\xca\xaf\x48\x2d\x2e\x48\x2d\x4a\xcb\x2f"
-    "\xca\x2d\x2c\xcd\x4c\xce\x2e\x4f\xcc\x29\xa9\x4a\x2d\x4e\xcc\x4b"
-    "\xc9\xca\x4c\x2f\x06\x00\x71\xc5\x60\xbb\x24\x00\x00\x00",
-    bb60c571),
-  T("fakebugsputinwaxjonquilsdrivehimcrazy",
-    "\x78\x9c\x4b\x4b\xcc\x4e\x4d\x2a\x4d\x2f\x2e\x28\x2d\xc9\xcc\x2b"
-    "\x4f\xac\xc8\xca\xcf\x2b\x2c\xcd\xcc\x29\x4e\x29\xca\x2c\x4b\xcd"
-    "\xc8\xcc\x4d\x2e\x4a\xac\xaa\x04\x00\x29\x9e\x0f\xbf",
-    "\x4b\x4b\xcc\x4e\x4d\x2a\x4d\x2f\x2e\x28\x2d\xc9\xcc\x2b\x4f\xac"
-    "\xc8\xca\xcf\x2b\x2c\xcd\xcc\x29\x4e\x29\xca\x2c\x4b\xcd\xc8\xcc"
-    "\x4d\x2e\x4a\xac\xaa\x04\x00\xf0\x24\xc5\xa1\x25\x00\x00\x00",
-    a1c524f0),
-  T("wovensilkpyjamasexchangedforbluequartz",
-    "\x78\x9c\x2b\xcf\x2f\x4b\xcd\x2b\xce\xcc\xc9\x2e\xa8\xcc\x4a\xcc"
-    "\x4d\x2c\x4e\xad\x48\xce\x48\xcc\x4b\x4f\x4d\x49\xcb\x2f\x4a\xca"
-    "\x29\x4d\x2d\x2c\x4d\x2c\x2a\xa9\x02\x00\x39\xef\x10\x15",
-    "\x2b\xcf\x2f\x4b\xcd\x2b\xce\xcc\xc9\x2e\xa8\xcc\x4a\xcc\x4d\x2c"
-    "\x4e\xad\x48\xce\x48\xcc\x4b\x4f\x4d\x49\xcb\x2f\x4a\xca\x29\x4d"
-    "\x2d\x2c\x4d\x2c\x2a\xa9\x02\x00\x4e\x06\x7f\x9a\x26\x00\x00\x00",
-    9a7f064e),
-  T("thequickonyxgoblinjumpsoverthelazydwarf",
-    "\x78\x9c\x2b\xc9\x48\x2d\x2c\xcd\x4c\xce\xce\xcf\xab\xac\x48\xcf"
-    "\x4f\xca\xc9\xcc\xcb\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48"
-    "\xcd\x49\xac\xaa\x4c\x29\x4f\x2c\x4a\x03\x00\x4d\x76\x10\xa8",
-    "\x2b\xc9\x48\x2d\x2c\xcd\x4c\xce\xce\xcf\xab\xac\x48\xcf\x4f\xca"
-    "\xc9\xcc\xcb\x2a\xcd\x2d\x28\xce\x2f\x4b\x2d\x2a\xc9\x48\xcd\x49"
-    "\xac\xaa\x4c\x29\x4f\x2c\x4a\x03\x00\x12\x87\x47\x3b\x27\x00\x00\x00",
-    3b478712),
-  T("amazinglyfewdiscothequesprovidejukeboxes",
-    "\x78\x9c\x4b\xcc\x4d\xac\xca\xcc\x4b\xcf\xa9\x4c\x4b\x2d\x4f\xc9"
-    "\x2c\x4e\xce\x2f\xc9\x48\x2d\x2c\x4d\x2d\x2e\x28\xca\x2f\xcb\x4c"
-    "\x49\xcd\x2a\xcd\x4e\x4d\xca\xaf\x48\x2d\x06\x00\x59\xbe\x10\xe9",
-    "\x4b\xcc\x4d\xac\xca\xcc\x4b\xcf\xa9\x4c\x4b\x2d\x4f\xc9\x2c\x4e"
-    "\xce\x2f\xc9\x48\x2d\x2c\x4d\x2d\x2e\x28\xca\x2f\xcb\x4c\x49\xcd"
-    "\x2a\xcd\x4e\x4d\xca\xaf\x48\x2d\x06\x00\xa3\xe4\xdd\x7d\x28\x00\x00\x00",
-    7ddde4a3),
-
-  // self-describing pangram by Lee Sallows (SciAm, 1984)
-  T("This Pangram contains four as, one b, two cs, one d, thirty es, "
-    "six fs, five gs, seven hs, eleven is, one j, one k, two ls, two ms, "
-    "eighteen ns, fifteen os, two ps, one q, five rs, twenty-seven ss, "
-    "eighteen ts, two us, seven vs, eight ws, two xs, three ys, & one z.",
-    "\x78\x9c\x4d\x4e\xcd\x1a\xc2\x20\x0c\x7b\x95\x9c\x3c\x4d\x9f\xc5"
-    "\x83\x2f\x80\xb3\x8c\xea\x56\x14\xd8\x0f\x3e\xbd\x0c\xaa\x9f\xa7"
-    "\xa4\x69\x92\xf6\xe2\x38\xe2\x6c\x64\x08\x66\x42\xef\x25\x19\x96"
-    "\x08\xeb\xe7\x00\x13\x3b\x78\x21\x5c\x3b\xa4\xd5\xa3\xd7\xf1\x56"
-    "\x46\xc7\x21\x65\x50\x51\x22\x6f\xb0\x05\x2d\x2f\x84\x61\x17\x68"
-    "\x21\x81\x2b\x8c\xc6\x4a\x59\x73\xf7\x06\x8f\xd6\x36\xc6\x86\xd3"
-    "\x6e\xe4\xc1\x25\x2a\x56\xa9\x45\xb6\x72\xaf\x86\xa7\xc6\x5f\x7a"
-    "\x23\x54\x9d\x24\xe5\x63\x3b\x15\xff\x1b\x92\xa6\xe6\xdf\x27\xcb"
-    "\x77\x8d\x55\x77\xdb\x8e\x2e\x10\x21\x17\x76\xa8\xed\xef\xd3\x07"
-    "\xaa\x0c\x59\x5f",
-    "\x4d\x4e\xcd\x1a\xc2\x20\x0c\x7b\x95\x9c\x3c\x4d\x9f\xc5\x83\x2f"
-    "\x80\xb3\x8c\xea\x56\x14\xd8\x0f\x3e\xbd\x0c\xaa\x9f\xa7\xa4\x69"
-    "\x92\xf6\xe2\x38\xe2\x6c\x64\x08\x66\x42\xef\x25\x19\x96\x08\xeb"
-    "\xe7\x00\x13\x3b\x78\x21\x5c\x3b\xa4\xd5\xa3\xd7\xf1\x56\x46\xc7"
-    "\x21\x65\x50\x51\x22\x6f\xb0\x05\x2d\x2f\x84\x61\x17\x68\x21\x81"
-    "\x2b\x8c\xc6\x4a\x59\x73\xf7\x06\x8f\xd6\x36\xc6\x86\xd3\x6e\xe4"
-    "\xc1\x25\x2a\x56\xa9\x45\xb6\x72\xaf\x86\xa7\xc6\x5f\x7a\x23\x54"
-    "\x9d\x24\xe5\x63\x3b\x15\xff\x1b\x92\xa6\xe6\xdf\x27\xcb\x77\x8d"
-    "\x55\x77\xdb\x8e\x2e\x10\x21\x17\x76\xa8\xed\xef\xd3\x07\x11\xb4"
-    "\xb5\x3a\x09\x01\x00\x00",
-    3ab5b411),
-
-  // "Lion-Eating Poet in the Stone Den" by Chao Yuen Ren,
-  // Simplified Chinese text in UTF-8
-  T("\xe3\x80\x8a\xe6\x96\xbd\xe6\xb0\x8f\xe9\xa3\x9f\xe7\x8b\xae\xe5\x8f"
-    "\xb2\xe3\x80\x8b \xe7\x9f\xb3\xe5\xae\xa4\xe8\xaf\x97\xe5\xa3\xab\xe6"
-    "\x96\xbd\xe6\xb0\x8f\xef\xbc\x8c\xe5\x97\x9c\xe7\x8b\xae\xef\xbc\x8c"
-    "\xe8\xaa\x93\xe9\xa3\x9f\xe5\x8d\x81\xe7\x8b\xae\xe3\x80\x82 \xe6\xb0"
-    "\x8f\xe6\x97\xb6\xe6\x97\xb6\xe9\x80\x82\xe5\xb8\x82\xe8\xa7\x86\xe7"
-    "\x8b\xae\xe3\x80\x82 \xe5\x8d\x81\xe6\x97\xb6\xef\xbc\x8c\xe9\x80\x82"
-    "\xe5\x8d\x81\xe7\x8b\xae\xe9\x80\x82\xe5\xb8\x82\xe3\x80\x82 \xe6\x98"
-    "\xaf\xe6\x97\xb6\xef\xbc\x8c\xe9\x80\x82\xe6\x96\xbd\xe6\xb0\x8f\xe9"
-    "\x80\x82\xe5\xb8\x82\xe3\x80\x82 \xe6\xb0\x8f\xe8\xa7\x86\xe6\x98\xaf"
-    "\xe5\x8d\x81\xe7\x8b\xae\xef\xbc\x8c\xe6\x81\x83\xe7\x9f\xa2\xe5\x8a"
-    "\xbf\xef\xbc\x8c\xe4\xbd\xbf\xe6\x98\xaf\xe5\x8d\x81\xe7\x8b\xae\xe9"
-    "\x80\x9d\xe4\xb8\x96\xe3\x80\x82 \xe6\xb0\x8f\xe6\x8b\xbe\xe6\x98\xaf"
-    "\xe5\x8d\x81\xe7\x8b\xae\xe5\xb0\xb8\xef\xbc\x8c\xe9\x80\x82\xe7\x9f"
-    "\xb3\xe5\xae\xa4\xe3\x80\x82 \xe7\x9f\xb3\xe5\xae\xa4\xe6\xb9\xbf\xef"
-    "\xbc\x8c\xe6\xb0\x8f\xe4\xbd\xbf\xe4\xbe\x8d\xe6\x8b\xad\xe7\x9f\xb3"
-    "\xe5\xae\xa4\xe3\x80\x82 \xe7\x9f\xb3\xe5\xae\xa4\xe6\x8b\xad\xef\xbc"
-    "\x8c\xe6\xb0\x8f\xe5\xa7\x8b\xe8\xaf\x95\xe9\xa3\x9f\xe6\x98\xaf\xe5"
-    "\x8d\x81\xe7\x8b\xae\xe3\x80\x82 \xe9\xa3\x9f\xe6\x97\xb6\xef\xbc\x8c"
-    "\xe5\xa7\x8b\xe8\xaf\x86\xe6\x98\xaf\xe5\x8d\x81\xe7\x8b\xae\xe5\xb0"
-    "\xb8\xef\xbc\x8c\xe5\xae\x9e\xe5\x8d\x81\xe7\x9f\xb3\xe7\x8b\xae\xe5"
-    "\xb0\xb8\xe3\x80\x82 \xe8\xaf\x95\xe9\x87\x8a\xe6\x98\xaf\xe4\xba\x8b"
-    "\xe3\x80\x82",
-    "\x78\x9c\x65\x8f\xdd\x0a\x82\x40\x10\x46\x5f\xc5\x47\x5d\x0d\x0c"
-    "\xdc\xc4\x8b\x48\xbb\xa8\x84\x0c\x84\xc2\x1f\x12\xc2\x22\xf5\x61"
-    "\x74\x56\xbd\xea\x15\xfa\x76\xd7\x0c\x0a\xf6\x62\x66\xcf\x99\x6f"
-    "\x67\x5b\xe6\x08\xbf\x12\xb9\x37\x46\x61\xcf\x53\xf2\xae\x2d\xe3"
-    "\x46\x1f\x16\x94\x9e\x86\x2c\xa0\xe8\xa2\xf1\xeb\xb9\xa2\x60\x07"
-    "\x03\xc5\x70\x5e\xc3\x26\xd7\x44\xdb\x32\xcb\x00\x16\xc1\x0d\x67"
-    "\x64\x16\x95\xd6\x10\xdb\x13\x80\x82\x5b\x4c\x48\xa0\x74\x6d\xa8"
-    "\xa1\x6d\x36\xb3\x69\x83\x2f\xcb\x3d\x84\xc0\xd0\x43\x90\x84\xb9"
-    "\xe8\xc3\x23\x39\x0d\xea\xae\x6a\x66\x34\xb2\x7d\x57\xfa\x9f\x25"
-    "\x78\x3d\x03\xca\x4b\x9d\xad\xbf\x22\x0d\x5d\x89\xbb\xcc\x80\x8d"
-    "\x98\xae\x76\x05\x4f\xfe\x0c\x9e\x68\x83\x62\x3e\x64\x1b\x7c\x75"
-    "\x4e\x95\x92\xec\xd5\xe2\x0a\xdb\x3f\x2f\x52\x7a\x90\x6d\x58\xe8"
-    "\x1b\x39\x20\x33\x96\x0e\xbc\xee\xc1\xd1\xbf\x01\xae\x11\xff\x53",
-    "\x65\x8f\xdd\x0a\x82\x40\x10\x46\x5f\xc5\x47\x5d\x0d\x0c\xdc\xc4"
-    "\x8b\x48\xbb\xa8\x84\x0c\x84\xc2\x1f\x12\xc2\x22\xf5\x61\x74\x56"
-    "\xbd\xea\x15\xfa\x76\xd7\x0c\x0a\xf6\x62\x66\xcf\x99\x6f\x67\x5b"
-    "\xe6\x08\xbf\x12\xb9\x37\x46\x61\xcf\x53\xf2\xae\x2d\xe3\x46\x1f"
-    "\x16\x94\x9e\x86\x2c\xa0\xe8\xa2\xf1\xeb\xb9\xa2\x60\x07\x03\xc5"
-    "\x70\x5e\xc3\x26\xd7\x44\xdb\x32\xcb\x00\x16\xc1\x0d\x67\x64\x16"
-    "\x95\xd6\x10\xdb\x13\x80\x82\x5b\x4c\x48\xa0\x74\x6d\xa8\xa1\x6d"
-    "\x36\xb3\x69\x83\x2f\xcb\x3d\x84\xc0\xd0\x43\x90\x84\xb9\xe8\xc3"
-    "\x23\x39\x0d\xea\xae\x6a\x66\x34\xb2\x7d\x57\xfa\x9f\x25\x78\x3d"
-    "\x03\xca\x4b\x9d\xad\xbf\x22\x0d\x5d\x89\xbb\xcc\x80\x8d\x98\xae"
-    "\x76\x05\x4f\xfe\x0c\x9e\x68\x83\x62\x3e\x64\x1b\x7c\x75\x4e\x95"
-    "\x92\xec\xd5\xe2\x0a\xdb\x3f\x2f\x52\x7a\x90\x6d\x58\xe8\x1b\x39"
-    "\x20\x33\x96\x0e\xbc\xee\xc1\xd1\xbf\x01\xe5\x06\xa6\x1f\x72\x01"
-    "\x00\x00",
-    1fa606e5),
-
-  { 0, 0, 0, 0, 0, 0, 0 }
-};
-
-#undef T
-#undef GZH
-
-static void
-test_zlib_crc32(void *)
-{
-  for (const zlib_testvec *t = testvecs; t->text; t++) {
-    uint32_t h = generate_crc32c(t->text, t->tlen);
-    tt_assert_op_type(h, ==, t->crc32, unsigned int, "%08x");
-  }
-
- end:;
-}
-
-static void
-test_zlib_compress_zlib(void *)
-{
-  uint8_t obuf[1024];
-  for (const zlib_testvec *t = testvecs; t->text; t++) {
-    ssize_t n = def(t->text, t->tlen, obuf, sizeof obuf, 6);
-    tt_uint_op(n, ==, t->zlen);
-    tt_mem_op(obuf, ==, t->zlibbed, t->zlen);
-  }
-
- end:;
-}
-
-static void
-test_zlib_decompress_zlib(void *)
-{
-  uint8_t obuf[1024];
-  for (const zlib_testvec *t = testvecs; t->text; t++) {
-    ssize_t n = inf(t->zlibbed, t->zlen, obuf, sizeof obuf);
-    tt_uint_op(n, ==, t->tlen);
-    tt_mem_op(obuf, ==, t->text, t->tlen);
-  }
-
- end:;
-}
-
-static void
-test_zlib_compress_gzip(void *)
-{
-  uint8_t obuf[1024];
-  for (const zlib_testvec *t = testvecs; t->text; t++) {
-    ssize_t n = gzDeflate(t->text, t->tlen, obuf, sizeof obuf, 0);
-    tt_uint_op(n, ==, t->glen);
-    tt_mem_op(obuf, ==, t->gzipped, t->glen);
-  }
-
- end:;
-}
-static void
-test_zlib_decompress_gzip(void *)
-{
-  uint8_t obuf[1024];
-  for (const zlib_testvec *t = testvecs; t->text; t++) {
-    ssize_t n = gzInflate(t->gzipped, t->glen, obuf, sizeof obuf);
-    tt_uint_op(n, ==, t->tlen);
-    tt_mem_op(obuf, ==, t->text, t->tlen);
-  }
-
- end:;
-}
-
-#define T(name) \
-  { #name, test_zlib_##name, 0, 0, 0 }
-
-struct testcase_t zlib_tests[] = {
-  T(crc32),
-  T(compress_zlib),
-  T(decompress_zlib),
-  T(compress_gzip),
-  T(decompress_gzip),
-  END_OF_TESTCASES
-};
diff --git a/src/zpack.cc b/src/zpack.cc
deleted file mode 100644
index 3ffd065..0000000
--- a/src/zpack.cc
+++ /dev/null
@@ -1,172 +0,0 @@
-/* Copyright 2011, 2012 SRI International
- * See LICENSE for other credits and copying information
- */
-#include "util.h"
-#include "zpack.h"
-#include "zlib.h"
-
-#include <limits>
-
-// zlib doesn't believe in size_t. When size_t is bigger than uInt, we
-// theoretically could break operations up into uInt-sized chunks to
-// support the full range of size_t, but I doubt we will ever need to
-// compress, decompress, or crc32 more than 2^32 bytes in one
-// operation, so I'm not bothering.  -- zw, 2012
-//
-// The indirection through ZLIB_UINT_MAX makes some versions of gcc
-// not produce a 'comparison is always (true/false)' warning.
-const size_t ZLIB_UINT_MAX = std::numeric_limits<uInt>::max();
-const size_t ZLIB_CEILING = (SIZE_T_CEILING > ZLIB_UINT_MAX
-                             ? ZLIB_UINT_MAX : SIZE_T_CEILING);
-
-// Compress from 'source' to 'dest', producing 'zlib' (RFC 1950) format,
-// with compression level 'level'.
-ssize_t
-def(const uint8_t *source, size_t slen, uint8_t *dest, size_t dlen, int level)
-{
-  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
-    return -1;
-
-  z_stream strm;
-  memset(&strm, 0, sizeof strm);
-  int ret = deflateInit(&strm, level);
-  if (ret != Z_OK) {
-    log_warn("compression failure (initialization): %s", strm.msg);
-    return -1;
-  }
-
-  strm.next_in = const_cast<Bytef*>(source);
-  strm.avail_in = slen;
-  strm.next_out = dest;
-  strm.avail_out = dlen;
-
-  ret = deflate(&strm, Z_FINISH);
-  if (ret != Z_STREAM_END) {
-    log_warn("compression failure: %s", strm.msg);
-    deflateEnd(&strm);
-    return -1;
-  }
-
-  deflateEnd(&strm);
-  return strm.total_out;
-}
-
-// Decompress 'zlib'-format data from 'source' to 'dest'.
-ssize_t
-inf(const uint8_t *source, size_t slen, uint8_t *dest, size_t dlen)
-{
-  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
-    return -1;
-
-  /* allocate inflate state */
-  z_stream strm;
-  memset(&strm, 0, sizeof strm);
-  int ret = inflateInit(&strm);
-  if (ret != Z_OK) {
-    log_warn("decompression failure (initialization): %s", strm.msg);
-    return -1;
-  }
-
-  strm.next_in = const_cast<Bytef*>(source);
-  strm.avail_in = slen;
-  strm.next_out = dest;
-  strm.avail_out = dlen;
-
-  ret = inflate(&strm, Z_FINISH);
-  if (ret != Z_STREAM_END) {
-    log_warn("decompression failure: %s", strm.msg);
-    inflateEnd(&strm);
-    return -1;
-  }
-
-  inflateEnd(&strm);
-  return strm.total_out;
-}
-
-ssize_t
-gzInflate(const uint8_t *source, size_t slen, uint8_t *dest, size_t dlen)
-{
-  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
-    return -1;
-
-  z_stream strm;
-  memset(&strm, 0, sizeof strm);
-  int ret = inflateInit2(&strm, MAX_WBITS|16); // 16 = decode gzip (only)
-
-  if (ret != Z_OK) {
-    log_warn("decompression failure (initialization): %s", strm.msg);
-    return -1;
-  }
-
-  strm.next_in = const_cast<Bytef*>(source);
-  strm.avail_in = slen;
-  strm.next_out = dest;
-  strm.avail_out = dlen;
-
-  ret = inflate(&strm, Z_FINISH);
-  if (ret != Z_STREAM_END) {
-    log_warn("decompression failure: %s", strm.msg);
-    inflateEnd(&strm);
-    return -1;
-  }
-
-  inflateEnd(&strm);
-  return strm.total_out;
-}
-
-ssize_t
-gzDeflate(const uint8_t *source, size_t slen, uint8_t *dest, size_t dlen,
-          time_t mtime)
-{
-  if (slen > ZLIB_CEILING || dlen > ZLIB_CEILING)
-    return -1;
-
-  z_stream strm;
-  memset(&strm, 0, sizeof strm);
-  int ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED,
-                         MAX_WBITS|16, // compress as gzip
-                         8, Z_DEFAULT_STRATEGY);
-  if (ret != Z_OK) {
-    log_warn("compression failure (initialization): %s", strm.msg);
-    return -1;
-  }
-
-  gz_header gzh;
-  memset(&gzh, 0, sizeof gzh);
-  gzh.time = mtime;
-  gzh.os = 0xFF; // "unknown"
-  ret = deflateSetHeader(&strm, &gzh);
-  if (ret != Z_OK) {
-    log_warn("compression failure (initialization): %s", strm.msg);
-    return -1;
-  }
-
-  strm.next_in = const_cast<Bytef*>(source);
-  strm.avail_in = slen;
-  strm.next_out = dest;
-  strm.avail_out = dlen;
-
-  ret = deflate(&strm, Z_FINISH);
-  if (ret != Z_STREAM_END) {
-    log_warn("compression failure: %s", strm.msg);
-    deflateEnd(&strm);
-    return -1;
-  }
-
-  deflateEnd(&strm);
-  return strm.total_out;
-}
-
-uint32_t
-generate_crc32c(const uint8_t *string, size_t length)
-{
-  log_assert(length <= std::numeric_limits<uInt>::max());
-
-  uLong crc = crc32(crc32(0, 0, 0), string, length);
-
-  // zlib also doesn't believe 'long' can be more than 32 bits wide.
-  // This shouldn't ever fire unless there is a bug in zlib.
-  log_assert(crc <= std::numeric_limits<uint32_t>::max());
-
-  return crc;
-}
diff --git a/src/zpack.h b/src/zpack.h
deleted file mode 100644
index 438c9dc..0000000
--- a/src/zpack.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/* Copyright 2011, 2012 SRI International
- * See LICENSE for other credits and copying information
- */
-#ifndef _ZPACK_H
-#define _ZPACK_H
-
-ssize_t def(const uint8_t *source, size_t slen,
-            uint8_t *dest, size_t dlen,
-            int level);
-ssize_t inf(const uint8_t *source, size_t slen,
-            uint8_t *dest, size_t dlen);
-
-ssize_t gzInflate(const uint8_t *source, size_t slen,
-                  uint8_t *dest, size_t dlen);
-ssize_t gzDeflate(const uint8_t *source, size_t slen,
-                  uint8_t *dest, size_t dlen,
-                  time_t mtime);
-
-uint32_t generate_crc32c(const uint8_t *string, size_t length);
-
-#endif





More information about the tor-commits mailing list