diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # lzlib
 
+## 1.0.7.5
+
+  * Bump C sources to lzlib 1.16
+
 ## 1.0.7.4
 
   * Bump C sources to lzlib 1.15
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,33 +1,9 @@
 # lzlib
 
-[![Hackage CI](https://matrix.hackage.haskell.org/api/v2/packages/lzlib/badge)](https://matrix.hackage.haskell.org/package/lzlib)
-[![Hackage](https://img.shields.io/hackage/v/lzlib.svg)](http://hackage.haskell.org/package/lzlib)
-
-[![Dependencies of latest version on Hackage](https://img.shields.io/hackage-deps/v/lzlib.svg)](https://hackage.haskell.org/package/lzlib)
-
 Haskell bindings to [lzlib](https://www.nongnu.org/lzip/lzlib.html).
 
-This package includes a high-level interface to lzlib. It provides performance
-comparable to the [lzip](https://www.nongnu.org/lzip/) command-line tool.
-
-## Comparison
-
-Compared to the [lzip](http://hackage.haskell.org/package/lzip) Haskell library:
-
-  * Uses `c2hs` instead of `hsc2hs`
-  * Provides a high-level (`ByteString`-based) API
-
-### Performance
-
-Performance should be comparable to the
-[lzip](http://download.savannah.gnu.org/releases/lzip/) C++ program.
-
-## Hacking
-
-Run
+This package includes a high-level interface to lzlib based on `ByteString`s.
 
-```bash
-make
-```
+## BUGS
 
-to download a few tarballs before running the test suite.
+The test suite segfaults when `-with-rtsopts=-N` is supplied.
diff --git a/cbits/cbuffer.c b/cbits/cbuffer.c
--- a/cbits/cbuffer.c
+++ b/cbits/cbuffer.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
diff --git a/cbits/decoder.c b/cbits/decoder.c
--- a/cbits/decoder.c
+++ b/cbits/decoder.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -65,7 +65,7 @@
       if( ( *state = St_set_char( *state ) ) < 4 )
         LZd_put_byte( d, Rd_decode_tree8( rdec, bm ) );
       else
-        LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, d->rep0 ) ) );
+        LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, d->dis0 ) ) );
       continue;
       }
     /* match or repeated match */
@@ -76,23 +76,23 @@
         {
         if( Rd_decode_bit( rdec, &d->bm_len[*state][pos_state] ) == 0 )	/* 4th bit */
           { *state = St_set_shortrep( *state );
-            LZd_put_byte( d, LZd_peek( d, d->rep0 ) ); continue; }
+            LZd_put_byte( d, LZd_peek( d, d->dis0 ) ); continue; }
         }
       else
         {
         unsigned distance;
         if( Rd_decode_bit( rdec, &d->bm_rep1[*state] ) == 0 )	/* 4th bit */
-          distance = d->rep1;
+          distance = d->dis1;
         else
           {
           if( Rd_decode_bit( rdec, &d->bm_rep2[*state] ) == 0 )	/* 5th bit */
-            distance = d->rep2;
+            distance = d->dis2;
           else
-            { distance = d->rep3; d->rep3 = d->rep2; }
-          d->rep2 = d->rep1;
+            { distance = d->dis3; d->dis3 = d->dis2; }
+          d->dis2 = d->dis1;
           }
-        d->rep1 = d->rep0;
-        d->rep0 = distance;
+        d->dis1 = d->dis0;
+        d->dis0 = distance;
         }
       *state = St_set_rep( *state );
       len = Rd_decode_len( rdec, &d->rep_len_model, pos_state );
@@ -100,21 +100,20 @@
     else					/* match */
       {
       len = Rd_decode_len( rdec, &d->match_len_model, pos_state );
-      unsigned distance = Rd_decode_tree6( rdec, d->bm_dis_slot[get_len_state(len)] );
-      if( distance >= start_dis_model )
+      unsigned dis0 = Rd_decode_tree6( rdec, d->bm_dis_slot[get_len_state(len)] );
+      if( dis0 >= start_dis_model )
         {
-        const unsigned dis_slot = distance;
+        const unsigned dis_slot = dis0;
         const int direct_bits = ( dis_slot >> 1 ) - 1;
-        distance = ( 2 | ( dis_slot & 1 ) ) << direct_bits;
+        dis0 = ( 2 | ( dis_slot & 1 ) ) << direct_bits;
         if( dis_slot < end_dis_model )
-          distance += Rd_decode_tree_reversed( rdec,
-                      d->bm_dis + ( distance - dis_slot ), direct_bits );
+          dis0 += Rd_decode_tree_reversed( rdec, d->bm_dis + ( dis0 - dis_slot ),
+                                           direct_bits );
         else
           {
-          distance +=
-            Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits;
-          distance += Rd_decode_tree_reversed4( rdec, d->bm_align );
-          if( distance == 0xFFFFFFFFU )		/* marker found */
+          dis0 += Rd_decode( rdec, direct_bits - dis_align_bits ) << dis_align_bits;
+          dis0 += Rd_decode_tree_reversed4( rdec, d->bm_align );
+          if( dis0 == 0xFFFFFFFFU )		/* marker found */
             {
             Rd_normalize( rdec );
             const unsigned mpos = rdec->member_position;
@@ -137,12 +136,12 @@
             }
           }
         }
-      d->rep3 = d->rep2; d->rep2 = d->rep1; d->rep1 = d->rep0; d->rep0 = distance;
+      d->dis3 = d->dis2; d->dis2 = d->dis1; d->dis1 = d->dis0; d->dis0 = dis0;
+      if( dis0 >= d->dictionary_size ||
+          ( dis0 >= d->cb.put && !d->pos_wrapped ) ) return 1;
       *state = St_set_match( *state );
-      if( d->rep0 >= d->dictionary_size ||
-          ( d->rep0 >= d->cb.put && !d->pos_wrapped ) ) return 1;
       }
-    LZd_copy_block( d, d->rep0, len );
+    LZd_copy_block( d, d->dis0, len );
     }
   return 2;
   }
diff --git a/cbits/decoder.h b/cbits/decoder.h
--- a/cbits/decoder.h
+++ b/cbits/decoder.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -17,7 +17,7 @@
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
 
-enum { rd_min_available_bytes = 10 };
+enum { rd_min_available_bytes = 11 };
 
 typedef struct Range_decoder
   {
@@ -155,13 +155,10 @@
     { rdec->range <<= 8; rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); }
   }
 
-static inline unsigned Rd_decode( Range_decoder * const rdec,
-                                  const int num_bits )
+static inline unsigned Rd_decode( Range_decoder * const rdec, int num_bits )
   {
   unsigned symbol = 0;
-  int i;
-  for( i = num_bits; i > 0; --i )
-    {
+  do {
     Rd_normalize( rdec );
     rdec->range >>= 1;
 /*    symbol <<= 1; */
@@ -169,7 +166,7 @@
     const bool bit = rdec->code >= rdec->range;
     symbol <<= 1; symbol += bit;
     rdec->code -= rdec->range & ( 0U - bit );
-    }
+    } while( --num_bits > 0 );
   return symbol;
   }
 
@@ -339,10 +336,10 @@
   bool check_trailer_pending;
   bool member_finished;
   bool pos_wrapped;
-  unsigned rep0;		/* rep[0-3] latest four distances */
-  unsigned rep1;		/* used for efficient coding of */
-  unsigned rep2;		/* repeated distances */
-  unsigned rep3;
+  unsigned dis0;		/* dis[0-3] latest four distances */
+  unsigned dis1;		/* used for efficient coding of */
+  unsigned dis2;		/* repeated distances */
+  unsigned dis3;
   State state;
 
   Bit_model bm_literal[1<<literal_context_bits][0x300];
@@ -385,16 +382,17 @@
 static inline void LZd_copy_block( LZ_decoder * const d,
                                    const unsigned distance, unsigned len )
   {
-  unsigned lpos = d->cb.put, i = lpos - distance - 1;
+  unsigned lpos = d->cb.put, i;
   bool fast, fast2;
   if( lpos > distance )
     {
+    i = lpos - distance - 1;
     fast = len < d->cb.buffer_size - lpos;
     fast2 = fast && len <= lpos - i;
     }
   else
     {
-    i += d->cb.buffer_size;
+    i = d->cb.buffer_size + lpos - distance - 1;
     fast = len < d->cb.buffer_size - i;		/* (i == pos) may happen */
     fast2 = fast && len <= i - lpos;
     }
@@ -427,12 +425,11 @@
   d->check_trailer_pending = false;
   d->member_finished = false;
   d->pos_wrapped = false;
-  /* prev_byte of first byte; also for LZd_peek( 0 ) on corrupt file */
-  d->cb.buffer[d->cb.buffer_size-1] = 0;
-  d->rep0 = 0;
-  d->rep1 = 0;
-  d->rep2 = 0;
-  d->rep3 = 0;
+  d->cb.buffer[d->cb.buffer_size-1] = 0;	/* prev_byte of first byte */
+  d->dis0 = 0;
+  d->dis1 = 0;
+  d->dis2 = 0;
+  d->dis3 = 0;
   d->state = 0;
 
   Bm_array_init( d->bm_literal[0], (1 << literal_context_bits) * 0x300 );
diff --git a/cbits/encoder.c b/cbits/encoder.c
--- a/cbits/encoder.c
+++ b/cbits/encoder.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -157,7 +157,7 @@
 
 /* Return the number of bytes advanced (ahead).
    trials[0]..trials[ahead-1] contain the steps to encode.
-   ( trials[0].dis4 == -1 ) means literal.
+   ( trials[0].cdis == -1 ) means literal.
    A match/rep longer or equal than match_len_limit finishes the sequence.
 */
 static int LZe_sequence_optimizer( LZ_encoder * const e,
@@ -186,7 +186,7 @@
   if( replens[rep_index] >= e->match_len_limit )
     {
     e->trials[0].price = replens[rep_index];
-    e->trials[0].dis4 = rep_index;
+    e->trials[0].cdis = rep_index;
     if( !LZe_move_and_update( e, replens[rep_index] ) ) return 0;
     return replens[rep_index];
     }
@@ -194,7 +194,7 @@
   if( main_len >= e->match_len_limit )
     {
     e->trials[0].price = main_len;
-    e->trials[0].dis4 = e->pairs[num_pairs-1].dis + num_rep_distances;
+    e->trials[0].cdis = e->pairs[num_pairs-1].dis + num_rep_distances;
     if( !LZe_move_and_update( e, main_len ) ) return 0;
     return main_len;
     }
@@ -209,7 +209,7 @@
     e->trials[1].price += LZeb_price_literal( &e->eb, prev_byte, cur_byte );
   else
     e->trials[1].price += LZeb_price_matched( &e->eb, prev_byte, cur_byte, match_byte );
-  e->trials[1].dis4 = -1;				/* literal */
+  e->trials[1].cdis = -1;				/* literal */
 
   const int match_price = price1( e->eb.bm_match[state][pos_state] );
   const int rep_match_price = match_price + price1( e->eb.bm_rep[state] );
@@ -223,7 +223,7 @@
   if( num_trials < min_match_len )
     {
     e->trials[0].price = 1;
-    e->trials[0].dis4 = e->trials[1].dis4;
+    e->trials[0].cdis = e->trials[1].cdis;
     if( !Mb_move_pos( &e->eb.mb ) ) return 0;
     return 1;
     }
@@ -282,7 +282,7 @@
     Trial * cur_trial = &e->trials[cur];
     State cur_state;
     {
-    const int dis4 = cur_trial->dis4;
+    const int cdis = cur_trial->cdis;
     int prev_index = cur_trial->prev_index;
     const int prev_index2 = cur_trial->prev_index2;
 
@@ -291,15 +291,15 @@
       cur_state = e->trials[prev_index].state;
       if( prev_index + 1 == cur )			/* len == 1 */
         {
-        if( dis4 == 0 ) cur_state = St_set_shortrep( cur_state );
+        if( cdis == 0 ) cur_state = St_set_shortrep( cur_state );
         else cur_state = St_set_char( cur_state );	/* literal */
         }
-      else if( dis4 < num_rep_distances ) cur_state = St_set_rep( cur_state );
+      else if( cdis < num_rep_distances ) cur_state = St_set_rep( cur_state );
       else cur_state = St_set_match( cur_state );
       }
     else
       {
-      if( prev_index2 == dual_step_trial )	/* dis4 == 0 (rep0) */
+      if( prev_index2 == dual_step_trial )	/* cdis == 0 (rep0) */
         --prev_index;
       else					/* prev_index2 >= 0 */
         prev_index = prev_index2;
@@ -308,7 +308,7 @@
     cur_trial->state = cur_state;
     for( i = 0; i < num_rep_distances; ++i )
       cur_trial->reps[i] = e->trials[prev_index].reps[i];
-    mtf_reps( dis4, cur_trial->reps );		/* literal is ignored */
+    mtf_reps( cdis, cur_trial->reps );		/* literal is ignored */
     }
 
     const int pos_state = Mb_data_position( &e->eb.mb ) & pos_state_mask;
@@ -328,18 +328,19 @@
 
     Tr_update( next_trial, next_price, -1, cur );	/* literal */
 
-    const int match_price = cur_trial->price + price1( e->eb.bm_match[cur_state][pos_state] );
+    const int match_price =
+      cur_trial->price + price1( e->eb.bm_match[cur_state][pos_state] );
     const int rep_match_price = match_price + price1( e->eb.bm_rep[cur_state] );
 
-    if( match_byte == cur_byte && next_trial->dis4 != 0 &&
+    if( match_byte == cur_byte && next_trial->cdis != 0 &&
         next_trial->prev_index2 == single_step_trial )
       {
-      const int price = rep_match_price +
-                        LZeb_price_shortrep( &e->eb, cur_state, pos_state );
+      const int price =
+        rep_match_price + LZeb_price_shortrep( &e->eb, cur_state, pos_state );
       if( price <= next_trial->price )
         {
         next_trial->price = price;
-        next_trial->dis4 = 0;				/* rep0 */
+        next_trial->cdis = 0;				/* rep0 */
         next_trial->prev_index = cur;
         }
       }
@@ -520,7 +521,7 @@
       const int pos_state =
         ( Mb_data_position( &e->eb.mb ) - ahead ) & pos_state_mask;
       const int len = e->trials[i].price;
-      int dis = e->trials[i].dis4;
+      int dis = e->trials[i].cdis;
 
       bool bit = dis < 0;
       Re_encode_bit( &e->eb.renc, &e->eb.bm_match[*state][pos_state], !bit );
diff --git a/cbits/encoder.h b/cbits/encoder.h
--- a/cbits/encoder.h
+++ b/cbits/encoder.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -100,19 +100,19 @@
   {
   State state;
   int price;		/* dual use var; cumulative price, match length */
-  int dis4;		/* -1 for literal, or rep, or match distance + 4 */
+  int cdis;		/* -1 for literal, or rep, or match distance + 4 */
   int prev_index;	/* index of prev trial in trials[] */
   int prev_index2;	/*   -2  trial is single step */
 			/*   -1  literal + rep0 */
 			/* >= 0  ( rep or match ) + literal + rep0 */
-  int reps[num_rep_distances];
+  int reps[num_rep_distances];		/* latest four distances */
   } Trial;
 
 static inline void Tr_update( Trial * const trial, const int pr,
-                              const int distance4, const int p_i )
+                              const int cdi, const int p_i )
   {
   if( pr < trial->price )
-    { trial->price = pr; trial->dis4 = distance4; trial->prev_index = p_i;
+    { trial->price = pr; trial->cdis = cdi; trial->prev_index = p_i;
       trial->prev_index2 = single_step_trial; }
   }
 
@@ -120,16 +120,16 @@
                                const int p_i )
   {
   if( pr < trial->price )
-    { trial->price = pr; trial->dis4 = 0; trial->prev_index = p_i;
+    { trial->price = pr; trial->cdis = 0; trial->prev_index = p_i;
       trial->prev_index2 = dual_step_trial; }
   }
 
 static inline void Tr_update3( Trial * const trial, const int pr,
-                               const int distance4, const int p_i,
+                               const int cdi, const int p_i,
                                const int p_i2 )
   {
   if( pr < trial->price )
-    { trial->price = pr; trial->dis4 = distance4; trial->prev_index = p_i;
+    { trial->price = pr; trial->cdis = cdi; trial->prev_index = p_i;
       trial->prev_index2 = p_i2; }
   }
 
@@ -166,18 +166,18 @@
 
 static int LZe_get_match_pairs( LZ_encoder * const e, Pair * pairs );
 
-       /* move-to-front dis in/into reps; do nothing if( dis4 <= 0 ) */
-static inline void mtf_reps( const int dis4, int reps[num_rep_distances] )
+       /* move-to-front dis in/into reps; do nothing if( cdis <= 0 ) */
+static inline void mtf_reps( const int cdis, int reps[num_rep_distances] )
   {
-  if( dis4 >= num_rep_distances )			/* match */
+  if( cdis >= num_rep_distances )			/* match */
     {
     reps[3] = reps[2]; reps[2] = reps[1]; reps[1] = reps[0];
-    reps[0] = dis4 - num_rep_distances;
+    reps[0] = cdis - num_rep_distances;
     }
-  else if( dis4 > 0 )				/* repeated match */
+  else if( cdis > 0 )				/* repeated match */
     {
-    const int distance = reps[dis4];
-    int i; for( i = dis4; i > 0; --i ) reps[i] = reps[i-1];
+    const int distance = reps[cdis];
+    int i; for( i = cdis; i > 0; --i ) reps[i] = reps[i-1];
     reps[0] = distance;
     }
   }
@@ -252,7 +252,7 @@
 
 static inline void LZe_backward( LZ_encoder * const e, int cur )
   {
-  int dis4 = e->trials[cur].dis4;
+  int cdis = e->trials[cur].cdis;
   while( cur > 0 )
     {
     const int prev_index = e->trials[cur].prev_index;
@@ -260,19 +260,19 @@
 
     if( e->trials[cur].prev_index2 != single_step_trial )
       {
-      prev_trial->dis4 = -1;					/* literal */
+      prev_trial->cdis = -1;					/* literal */
       prev_trial->prev_index = prev_index - 1;
       prev_trial->prev_index2 = single_step_trial;
       if( e->trials[cur].prev_index2 >= 0 )
         {
         Trial * const prev_trial2 = &e->trials[prev_index-1];
-        prev_trial2->dis4 = dis4; dis4 = 0;			/* rep0 */
+        prev_trial2->cdis = cdis; cdis = 0;			/* rep0 */
         prev_trial2->prev_index = e->trials[cur].prev_index2;
         prev_trial2->prev_index2 = single_step_trial;
         }
       }
     prev_trial->price = cur - prev_index;			/* len */
-    cur = dis4; dis4 = prev_trial->dis4; prev_trial->dis4 = cur;
+    cur = cdis; cdis = prev_trial->cdis; prev_trial->cdis = cur;
     cur = prev_index;
     }
   }
diff --git a/cbits/encoder_base.c b/cbits/encoder_base.c
--- a/cbits/encoder_base.c
+++ b/cbits/encoder_base.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -123,7 +123,7 @@
   }
 
 
-/* End Of Stream marker => (dis == 0xFFFFFFFFU, len == min_match_len) */
+/* End Of Stream marker => (dis == 0xFFFF_FFFF, len == min_match_len) */
 static void LZeb_try_full_flush( LZ_encoder_base * const eb )
   {
   if( eb->member_finished || Cb_free_bytes( &eb->renc.cb ) <
@@ -143,7 +143,7 @@
   }
 
 
-/* Sync Flush marker => (dis == 0xFFFFFFFFU, len == min_match_len + 1) */
+/* Sync Flush marker => (dis == 0xFFFF_FFFF, len == min_match_len + 1) */
 static void LZeb_try_sync_flush( LZ_encoder_base * const eb )
   {
   const unsigned min_size = eb->renc.ff_count + max_marker_size;
@@ -167,7 +167,7 @@
                         const unsigned long long member_size )
   {
   const unsigned long long min_member_size = min_dictionary_size;
-  const unsigned long long max_member_size = 0x0008000000000000ULL; /* 2 PiB */
+  const unsigned long long max_member_size = 1ULL << 51;	/* 2 PiB */
   Mb_reset( &eb->mb );
   eb->member_size_limit = min( max( min_member_size, member_size ),
                           max_member_size ) - Lt_size - max_marker_size;
diff --git a/cbits/encoder_base.h b/cbits/encoder_base.h
--- a/cbits/encoder_base.h
+++ b/cbits/encoder_base.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -509,7 +509,7 @@
   }
 
 
-enum { max_marker_size = 16,
+enum { max_marker_size = 16,		/* rd_min_available_bytes + 5 */
        num_rep_distances = 4 };		/* must be 4 */
 
 typedef struct LZ_encoder_base
@@ -531,7 +531,7 @@
   Len_model match_len_model;
   Len_model rep_len_model;
   Range_encoder renc;
-  int reps[num_rep_distances];
+  int reps[num_rep_distances];		/* latest four distances */
   State state;
   bool member_finished;
   } LZ_encoder_base;
diff --git a/cbits/fast_encoder.c b/cbits/fast_encoder.c
--- a/cbits/fast_encoder.c
+++ b/cbits/fast_encoder.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
diff --git a/cbits/fast_encoder.h b/cbits/fast_encoder.h
--- a/cbits/fast_encoder.h
+++ b/cbits/fast_encoder.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
diff --git a/cbits/lzip.h b/cbits/lzip.h
--- a/cbits/lzip.h
+++ b/cbits/lzip.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
diff --git a/cbits/lzlib.c b/cbits/lzlib.c
--- a/cbits/lzlib.c
+++ b/cbits/lzlib.c
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
diff --git a/cbits/lzlib.h b/cbits/lzlib.h
--- a/cbits/lzlib.h
+++ b/cbits/lzlib.h
@@ -1,5 +1,5 @@
 /* Lzlib - Compression library for the lzip format
-   Copyright (C) 2009-2025 Antonio Diaz Diaz.
+   Copyright (C) 2009-2026 Antonio Diaz Diaz.
 
    This library is free software. Redistribution and use in source and
    binary forms, with or without modification, are permitted provided
@@ -24,9 +24,9 @@
 /* LZ_API_VERSION was first defined in lzlib 1.8 to 1.
    Since lzlib 1.12, LZ_API_VERSION is defined as (major * 1000 + minor). */
 
-#define LZ_API_VERSION 1015
+#define LZ_API_VERSION 1016
 
-static const char * const LZ_version_string = "1.15";
+static const char * const LZ_version_string = "1.16";
 
 typedef enum LZ_Errno
   { LZ_ok = 0,         LZ_bad_argument, LZ_mem_error,
diff --git a/lzlib.cabal b/lzlib.cabal
--- a/lzlib.cabal
+++ b/lzlib.cabal
@@ -1,6 +1,6 @@
 cabal-version:      1.18
 name:               lzlib
-version:            1.0.7.4
+version:            1.0.7.5
 license:            BSD3
 license-file:       LICENSE
 copyright:          Copyright: (c) 2019-2021 Vanessa McHale
@@ -70,17 +70,17 @@
 
 test-suite lzlib-test
     type:             exitcode-stdio-1.0
-    main-is:          Spec.hs
+    main-is:          Z.hs
     hs-source-dirs:   test
     default-language: Haskell2010
-    ghc-options:      -threaded -rtsopts "-with-rtsopts=-N -K1K" -Wall
+    -- ghc-options:      -threaded -rtsopts "-with-rtsopts=-N -K1K" -Wall
+    ghc-options:      -threaded -rtsopts "-with-rtsopts=-K1K" -Wall
     build-depends:
         base,
         lzlib,
-        hspec,
-        bytestring,
-        directory,
-        pathological-bytestrings
+        pathological-bytestrings,
+        tasty,
+        tasty-hunit
 
     if impl(ghc >=8.0)
         ghc-options:
diff --git a/src/Codec/Lzip.hs b/src/Codec/Lzip.hs
--- a/src/Codec/Lzip.hs
+++ b/src/Codec/Lzip.hs
@@ -54,22 +54,9 @@
 import           System.IO                    (IOMode (ReadMode), hFileSize,
                                                withFile)
 
-data CompressionLevel = Zero
-    | One
-    | Two
-    | Three
-    | Four
-    | Five
-    | Six
-    | Seven
-    | Eight
-    | Nine
-    deriving (Enum, Bounded)
+data CompressionLevel = Zero | One | Two | Three | Four | Five | Six | Seven | Eight | Nine deriving (Enum, Bounded)
 
-data LzOptions = LzOptions
-    { dictionarySize :: !Int
-    , matchLenLimit  :: !Int
-    }
+data LzOptions = LzOptions { dictionarySize :: !Int, matchLenLimit  :: !Int }
 
 encoderOptions :: CompressionLevel -> LzOptions
 encoderOptions Zero  = LzOptions 65535 16
@@ -107,7 +94,10 @@
     where
 
         -- TODO: not a fan of this loop!
-        step :: LZDecoderPtr -> [BS.ByteString] -> (ForeignPtr UInt8, CInt) -> LazyST.ST s (Maybe BS.ByteString, [BS.ByteString])
+        step :: LZDecoderPtr
+             -> [BS.ByteString]
+             -> (ForeignPtr UInt8, CInt)
+             -> LazyST.ST s (Maybe BS.ByteString, [BS.ByteString])
         step decoder bss (buf, bufSz) = LazyST.unsafeIOToST $ do
             maxSz <- fromIntegral <$> lZDecompressWriteSize decoder
             bss' <- case bss of
@@ -123,6 +113,7 @@
                 (bs':bss') -> if BS.length bs' > maxSz
                     then
                         let (bs'', rest) = BS.splitAt maxSz bs' in
+                        -- TODO: this should be safe I think?
                         BS.unsafeUseAsCStringLen bs'' $ \(bytes, sz) ->
                             lZDecompressWrite decoder (castPtr bytes) (fromIntegral sz) $> rest:bss'
                     else
@@ -164,16 +155,16 @@
 --
 -- @since 1.0.2.0
 compressSzBest :: BSL.ByteString
-           -> Int -- ^ Size of input data, in bytes
-           -> BSL.ByteString
+               -> Int -- ^ Size of input data, in bytes
+               -> BSL.ByteString
 compressSzBest = compressWithSz maxBound
 
 -- | Alias for @'compressWithSz' 'minBound'@
 --
 -- @since 1.0.2.0
 compressSzFast :: BSL.ByteString
-           -> Int -- ^ Size of input data, in bytes
-           -> BSL.ByteString
+               -> Int -- ^ Size of input data, in bytes
+               -> BSL.ByteString
 compressSzFast = compressWithSz minBound
 
 -- | Alias for @'compressWith' 'maxBound'@
@@ -240,7 +231,10 @@
     BSL.fromChunks <$> loop (castForeignPtr enc) bss buf
 
     where
-        step :: LZEncoderPtr -> [BS.ByteString] -> ForeignPtr UInt8 -> LazyST.ST s (Bool, BS.ByteString, [BS.ByteString])
+        step :: LZEncoderPtr
+             -> [BS.ByteString]
+             -> ForeignPtr UInt8
+             -> LazyST.ST s (Bool, BS.ByteString, [BS.ByteString])
         step encoder bss buf = LazyST.unsafeIOToST $ do
             maxSz <- fromIntegral <$> lZCompressWriteSize encoder
             bss' <- case bss of
diff --git a/src/Codec/Lzip/Raw.chs b/src/Codec/Lzip/Raw.chs
--- a/src/Codec/Lzip/Raw.chs
+++ b/src/Codec/Lzip/Raw.chs
@@ -72,7 +72,7 @@
 
 {# pointer *LZ_Encoder as LZEncoderPtr foreign finalizer LZ_compress_close as ^ -> LZEncoder #}
 
-{# fun LZ_compress_open as ^ { `CInt', `CInt', id `CULLong' } -> `Ptr LZEncoder' id #}
+{# fun LZ_compress_open as ^ { `CInt', `CInt', `CULLong' } -> `Ptr LZEncoder' id #}
 {# fun LZ_compress_finish as ^ { `LZEncoderPtr' } -> `CInt' #}
 {# fun LZ_compress_read as ^ { `LZEncoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}
 {# fun LZ_compress_write as ^ { `LZEncoderPtr', `Ptr UInt8', `CInt' } -> `CInt' #}
diff --git a/test/Spec.hs b/test/Spec.hs
deleted file mode 100644
--- a/test/Spec.hs
+++ /dev/null
@@ -1,71 +0,0 @@
-module Main ( main ) where
-
-import           Codec.Lzip                   (compress, compressBest,
-                                               decompress)
-import           Control.Applicative
-import           Control.Concurrent           (forkIO, newEmptyMVar, putMVar,
-                                               readMVar)
-import           Control.Monad                (filterM)
-import qualified Data.ByteString              as BS
-import qualified Data.ByteString.Lazy         as BSL
-import           Data.ByteString.Pathological (nonstandardRead)
-import           Data.Foldable                (traverse_)
-import           System.Directory             (doesFileExist)
-import           Test.Hspec
-
-forceHead :: BSL.ByteString -> IO ()
-forceHead bsl = BS.length (head $ BSL.toChunks bsl) `seq` pure ()
-
-forceBSL :: BSL.ByteString -> IO ()
-forceBSL bsl = BS.length (last $ BSL.toChunks bsl) `seq` pure ()
-
-decompressMultithreaded :: FilePath -> IO ()
-decompressMultithreaded fp = do
-    bsl <- decompress <$> BSL.readFile fp
-    forceHead bsl
-    done <- newEmptyMVar
-    _ <- forkIO (forceBSL bsl *> putMVar done ())
-    _ <- forkIO (forceBSL bsl *> putMVar done ())
-    readMVar done
-
-compressFileGeneral :: (FilePath -> IO BSL.ByteString) -> FilePath -> Spec
-compressFileGeneral f fp = parallel $
-    it ("decompress . compress should be identity (" ++ fp ++ ")") $ do
-        str <- f fp
-        decompress (compress str) `shouldBe` str
-
-compressFileFreaky :: FilePath -> Spec
-compressFileFreaky = compressFileGeneral nonstandardRead
-
-compressFile :: FilePath -> Spec
-compressFile = compressFileGeneral BSL.readFile
-
-decompressFileGeneral :: (FilePath -> IO BSL.ByteString) -> FilePath -> Spec
-decompressFileGeneral f fp = parallel $
-    it ("compress . decompress should be identity (" ++ fp ++ ")") $ do
-        str <- f fp
-        compressBest (decompress str) `shouldBe` str
-
-decompressFileFreaky :: FilePath -> Spec
-decompressFileFreaky = decompressFileGeneral nonstandardRead
-
-decompressFile :: FilePath -> Spec
-decompressFile = decompressFileGeneral BSL.readFile
-
-main :: IO ()
-main = do
-    ex' <- filterM doesFileExist ["gmp-6.1.2.tar.lz", "lzlib-1.10.tar.lz"]
-    ex <- ("cbits/encoder.c" :)
-            <$> filterM doesFileExist ["gmp-6.1.2.tar", "lzlib-1.10.tar"]
-    hspec $ do
-        describe "roundtrip" $
-            traverse_ compressFile ex
-        describe "roundtrip" $
-            traverse_ decompressFile ex'
-        describe "roundtrip (sketchy)" $
-            traverse_ compressFileFreaky ex
-        describe "roundtrip (sketchy)" $
-            traverse_ decompressFileFreaky ex'
-        describe "decompress (multithreaded)" $
-            it "should not fail" $
-                traverse_ decompressMultithreaded ex'
diff --git a/test/Z.hs b/test/Z.hs
new file mode 100644
--- /dev/null
+++ b/test/Z.hs
@@ -0,0 +1,16 @@
+module Main ( main ) where
+
+import           Codec.Lzip                   (compressBest, decompress)
+import           Data.ByteString.Pathological (nonstandardRead)
+import           Test.Tasty                   (TestTree, defaultMain, testGroup)
+import           Test.Tasty.HUnit             (testCase, (@?=))
+
+compressFile :: FilePath -> TestTree
+compressFile fp = testCase fp $ do
+    d <- nonstandardRead fp
+    decompress (compressBest d) @?= d
+
+main :: IO ()
+main = defaultMain $
+    testGroup "roundtrip"
+        [ compressFile "test/data/7341e7190c2219dbc2641707c5c0e41e038f8a44.jpg" ]
