diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
 # lzlib
 
+## 0.1.1.1
+  
+  * Fix distribution
+
 ## 0.1.1.0
 
   * Add a higher-level API
diff --git a/cbits/cbuffer.c b/cbits/cbuffer.c
new file mode 100644
--- /dev/null
+++ b/cbits/cbuffer.c
@@ -0,0 +1,143 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+struct Circular_buffer
+  {
+  uint8_t * buffer;
+  unsigned buffer_size;		/* capacity == buffer_size - 1 */
+  unsigned get;			/* buffer is empty when get == put */
+  unsigned put;
+  };
+
+static inline bool Cb_init( struct Circular_buffer * const cb,
+                            const unsigned buf_size )
+  {
+  cb->buffer_size = buf_size + 1;
+  cb->get = 0;
+  cb->put = 0;
+  cb->buffer =
+    ( cb->buffer_size > 1 ) ? (uint8_t *)malloc( cb->buffer_size ) : 0;
+  return ( cb->buffer != 0 );
+  }
+
+static inline void Cb_free( struct Circular_buffer * const cb )
+  { free( cb->buffer ); cb->buffer = 0; }
+
+static inline void Cb_reset( struct Circular_buffer * const cb )
+  { cb->get = 0; cb->put = 0; }
+
+static inline unsigned Cb_empty( const struct Circular_buffer * const cb )
+  { return cb->get == cb->put; }
+
+static inline unsigned Cb_used_bytes( const struct Circular_buffer * const cb )
+  { return ( (cb->get <= cb->put) ? 0 : cb->buffer_size ) + cb->put - cb->get; }
+
+static inline unsigned Cb_free_bytes( const struct Circular_buffer * const cb )
+  { return ( (cb->get <= cb->put) ? cb->buffer_size : 0 ) - cb->put + cb->get - 1; }
+
+static inline uint8_t Cb_get_byte( struct Circular_buffer * const cb )
+  {
+  const uint8_t b = cb->buffer[cb->get];
+  if( ++cb->get >= cb->buffer_size ) cb->get = 0;
+  return b;
+  }
+
+static inline void Cb_put_byte( struct Circular_buffer * const cb,
+                                const uint8_t b )
+  {
+  cb->buffer[cb->put] = b;
+  if( ++cb->put >= cb->buffer_size ) cb->put = 0;
+  }
+
+
+static bool Cb_unread_data( struct Circular_buffer * const cb,
+                            const unsigned size )
+  {
+  if( size > Cb_free_bytes( cb ) ) return false;
+  if( cb->get >= size ) cb->get -= size;
+  else cb->get = cb->buffer_size - size + cb->get;
+  return true;
+  }
+
+
+/* Copies up to 'out_size' bytes to 'out_buffer' and updates 'get'.
+   If 'out_buffer' is null, the bytes are discarded.
+   Returns the number of bytes copied or discarded.
+*/
+static unsigned Cb_read_data( struct Circular_buffer * const cb,
+                              uint8_t * const out_buffer,
+                              const unsigned out_size )
+  {
+  unsigned size = 0;
+  if( out_size == 0 ) return 0;
+  if( cb->get > cb->put )
+    {
+    size = min( cb->buffer_size - cb->get, out_size );
+    if( size > 0 )
+      {
+      if( out_buffer ) memcpy( out_buffer, cb->buffer + cb->get, size );
+      cb->get += size;
+      if( cb->get >= cb->buffer_size ) cb->get = 0;
+      }
+    }
+  if( cb->get < cb->put )
+    {
+    const unsigned size2 = min( cb->put - cb->get, out_size - size );
+    if( size2 > 0 )
+      {
+      if( out_buffer ) memcpy( out_buffer + size, cb->buffer + cb->get, size2 );
+      cb->get += size2;
+      size += size2;
+      }
+    }
+  return size;
+  }
+
+
+/* Copies up to 'in_size' bytes from 'in_buffer' and updates 'put'.
+   Returns the number of bytes copied.
+*/
+static unsigned Cb_write_data( struct Circular_buffer * const cb,
+                               const uint8_t * const in_buffer,
+                               const unsigned in_size )
+  {
+  unsigned size = 0;
+  if( in_size == 0 ) return 0;
+  if( cb->put >= cb->get )
+    {
+    size = min( cb->buffer_size - cb->put - (cb->get == 0), in_size );
+    if( size > 0 )
+      {
+      memcpy( cb->buffer + cb->put, in_buffer, size );
+      cb->put += size;
+      if( cb->put >= cb->buffer_size ) cb->put = 0;
+      }
+    }
+  if( cb->put < cb->get )
+    {
+    const unsigned size2 = min( cb->get - cb->put - 1, in_size - size );
+    if( size2 > 0 )
+      {
+      memcpy( cb->buffer + cb->put, in_buffer + size, size2 );
+      cb->put += size2;
+      size += size2;
+      }
+    }
+  return size;
+  }
diff --git a/cbits/decoder.c b/cbits/decoder.c
new file mode 100644
--- /dev/null
+++ b/cbits/decoder.c
@@ -0,0 +1,148 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+static int LZd_try_verify_trailer( struct LZ_decoder * const d )
+  {
+  Lzip_trailer trailer;
+  if( Rd_available_bytes( d->rdec ) < Lt_size && !d->rdec->at_stream_end )
+    return 0;
+  d->verify_trailer_pending = false;
+  d->member_finished = true;
+
+  if( Rd_read_data( d->rdec, trailer, Lt_size ) == Lt_size &&
+      Lt_get_data_crc( trailer ) == LZd_crc( d ) &&
+      Lt_get_data_size( trailer ) == LZd_data_position( d ) &&
+      Lt_get_member_size( trailer ) == d->rdec->member_position ) return 0;
+  return 3;
+  }
+
+
+/* Return value: 0 = OK, 1 = decoder error, 2 = unexpected EOF,
+                 3 = trailer error, 4 = unknown marker found,
+                 5 = library error. */
+static int LZd_decode_member( struct LZ_decoder * const d )
+  {
+  struct Range_decoder * const rdec = d->rdec;
+  State * const state = &d->state;
+  /* unsigned old_mpos = d->rdec->member_position; */
+
+  if( d->member_finished ) return 0;
+  if( !Rd_try_reload( rdec, false ) )
+    { if( !rdec->at_stream_end ) return 0; else return 2; }
+  if( d->verify_trailer_pending ) return LZd_try_verify_trailer( d );
+
+  while( !Rd_finished( rdec ) )
+    {
+    int len;
+    const int pos_state = LZd_data_position( d ) & pos_state_mask;
+    /* const unsigned mpos = d->rdec->member_position;
+    if( mpos - old_mpos > rd_min_available_bytes ) return 5;
+    old_mpos = mpos; */
+    if( !Rd_enough_available_bytes( rdec ) )	/* check unexpected EOF */
+      { if( !rdec->at_stream_end ) return 0;
+        if( Cb_empty( &rdec->cb ) ) break; }	/* decode until EOF */
+    if( !LZd_enough_free_bytes( d ) ) return 0;
+    if( Rd_decode_bit( rdec, &d->bm_match[*state][pos_state] ) == 0 ) /* 1st bit */
+      {
+      /* literal byte */
+      Bit_model * const bm = d->bm_literal[get_lit_state(LZd_peek_prev( d ))];
+      if( St_is_char( *state ) )
+        {
+        *state -= ( *state < 4 ) ? *state : 3;
+        LZd_put_byte( d, Rd_decode_tree8( rdec, bm ) );
+        }
+      else
+        {
+        *state -= ( *state < 10 ) ? 3 : 6;
+        LZd_put_byte( d, Rd_decode_matched( rdec, bm, LZd_peek( d, d->rep0 ) ) );
+        }
+      continue;
+      }
+    /* match or repeated match */
+    if( Rd_decode_bit( rdec, &d->bm_rep[*state] ) != 0 )	/* 2nd bit */
+      {
+      if( Rd_decode_bit( rdec, &d->bm_rep0[*state] ) == 0 )	/* 3rd bit */
+        {
+        if( Rd_decode_bit( rdec, &d->bm_len[*state][pos_state] ) == 0 )	/* 4th bit */
+          { *state = St_set_short_rep( *state );
+            LZd_put_byte( d, LZd_peek( d, d->rep0 ) ); continue; }
+        }
+      else
+        {
+        unsigned distance;
+        if( Rd_decode_bit( rdec, &d->bm_rep1[*state] ) == 0 )	/* 4th bit */
+          distance = d->rep1;
+        else
+          {
+          if( Rd_decode_bit( rdec, &d->bm_rep2[*state] ) == 0 )	/* 5th bit */
+            distance = d->rep2;
+          else
+            { distance = d->rep3; d->rep3 = d->rep2; }
+          d->rep2 = d->rep1;
+          }
+        d->rep1 = d->rep0;
+        d->rep0 = distance;
+        }
+      *state = St_set_rep( *state );
+      len = min_match_len + Rd_decode_len( rdec, &d->rep_len_model, pos_state );
+      }
+    else					/* match */
+      {
+      unsigned distance;
+      len = min_match_len + Rd_decode_len( rdec, &d->match_len_model, pos_state );
+      distance = Rd_decode_tree6( rdec, d->bm_dis_slot[get_len_state(len)] );
+      if( distance >= start_dis_model )
+        {
+        const unsigned dis_slot = distance;
+        const int direct_bits = ( dis_slot >> 1 ) - 1;
+        distance = ( 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 );
+        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 */
+            {
+            Rd_normalize( rdec );
+            if( len == min_match_len )		/* End Of Stream marker */
+              {
+              d->verify_trailer_pending = true;
+              return LZd_try_verify_trailer( d );
+              }
+            if( len == min_match_len + 1 )	/* Sync Flush marker */
+              {
+              if( Rd_try_reload( rdec, true ) ) { /*old_mpos += 5;*/ continue; }
+              else { if( !rdec->at_stream_end ) return 0; else break; }
+              }
+            return 4;
+            }
+          }
+        }
+      d->rep3 = d->rep2; d->rep2 = d->rep1; d->rep1 = d->rep0; d->rep0 = distance;
+      *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 );
+    }
+  return 2;
+  }
diff --git a/cbits/decoder.h b/cbits/decoder.h
new file mode 100644
--- /dev/null
+++ b/cbits/decoder.h
@@ -0,0 +1,422 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+enum { rd_min_available_bytes = 8 };
+
+struct Range_decoder
+  {
+  struct Circular_buffer cb;		/* input buffer */
+  unsigned long long member_position;
+  uint32_t code;
+  uint32_t range;
+  bool at_stream_end;
+  bool reload_pending;
+  };
+
+static inline bool Rd_init( struct Range_decoder * const rdec )
+  {
+  if( !Cb_init( &rdec->cb, 65536 + rd_min_available_bytes ) ) return false;
+  rdec->member_position = 0;
+  rdec->code = 0;
+  rdec->range = 0xFFFFFFFFU;
+  rdec->at_stream_end = false;
+  rdec->reload_pending = false;
+  return true;
+  }
+
+static inline void Rd_free( struct Range_decoder * const rdec )
+  { Cb_free( &rdec->cb ); }
+
+static inline bool Rd_finished( const struct Range_decoder * const rdec )
+  { return rdec->at_stream_end && Cb_empty( &rdec->cb ); }
+
+static inline void Rd_finish( struct Range_decoder * const rdec )
+  { rdec->at_stream_end = true; }
+
+static inline bool Rd_enough_available_bytes( const struct Range_decoder * const rdec )
+  { return ( Cb_used_bytes( &rdec->cb ) >= rd_min_available_bytes ); }
+
+static inline unsigned Rd_available_bytes( const struct Range_decoder * const rdec )
+  { return Cb_used_bytes( &rdec->cb ); }
+
+static inline unsigned Rd_free_bytes( const struct Range_decoder * const rdec )
+  { return rdec->at_stream_end ? 0 : Cb_free_bytes( &rdec->cb ); }
+
+static inline unsigned long long Rd_purge( struct Range_decoder * const rdec )
+  {
+  const unsigned long long size =
+    rdec->member_position + Cb_used_bytes( &rdec->cb );
+  Cb_reset( &rdec->cb );
+  rdec->member_position = 0; rdec->at_stream_end = true;
+  return size;
+  }
+
+static inline void Rd_reset( struct Range_decoder * const rdec )
+  { Cb_reset( &rdec->cb );
+    rdec->member_position = 0; rdec->at_stream_end = false; }
+
+
+/* Seeks a member header and updates 'get'. '*skippedp' is set to the
+   number of bytes skipped. Returns true if it finds a valid header.
+*/
+static bool Rd_find_header( struct Range_decoder * const rdec,
+                            unsigned * const skippedp )
+  {
+  *skippedp = 0;
+  while( rdec->cb.get != rdec->cb.put )
+    {
+    if( rdec->cb.buffer[rdec->cb.get] == lzip_magic[0] )
+      {
+      unsigned get = rdec->cb.get;
+      int i;
+      Lzip_header header;
+      for( i = 0; i < Lh_size; ++i )
+        {
+        if( get == rdec->cb.put ) return false;		/* not enough data */
+        header[i] = rdec->cb.buffer[get];
+        if( ++get >= rdec->cb.buffer_size ) get = 0;
+        }
+      if( Lh_verify( header ) ) return true;
+      }
+    if( ++rdec->cb.get >= rdec->cb.buffer_size ) rdec->cb.get = 0;
+    ++*skippedp;
+    }
+  return false;
+  }
+
+
+static inline int Rd_write_data( struct Range_decoder * const rdec,
+                                 const uint8_t * const inbuf, const int size )
+  {
+  if( rdec->at_stream_end || size <= 0 ) return 0;
+  return Cb_write_data( &rdec->cb, inbuf, size );
+  }
+
+static inline uint8_t Rd_get_byte( struct Range_decoder * const rdec )
+  {
+  /* 0xFF avoids decoder error if member is truncated at EOS marker */
+  if( Rd_finished( rdec ) ) return 0xFF;
+  ++rdec->member_position;
+  return Cb_get_byte( &rdec->cb );
+  }
+
+static inline int Rd_read_data( struct Range_decoder * const rdec,
+                                uint8_t * const outbuf, const int size )
+  {
+  const int sz = Cb_read_data( &rdec->cb, outbuf, size );
+  if( sz > 0 ) rdec->member_position += sz;
+  return sz;
+  }
+
+static inline bool Rd_unread_data( struct Range_decoder * const rdec,
+                                   const unsigned size )
+  {
+  if( size > rdec->member_position || !Cb_unread_data( &rdec->cb, size ) )
+    return false;
+  rdec->member_position -= size;
+  return true;
+  }
+
+static bool Rd_try_reload( struct Range_decoder * const rdec, const bool force )
+  {
+  if( force ) rdec->reload_pending = true;
+  if( rdec->reload_pending && Rd_available_bytes( rdec ) >= 5 )
+    {
+    int i;
+    rdec->reload_pending = false;
+    rdec->code = 0;
+    for( i = 0; i < 5; ++i )
+      rdec->code = (rdec->code << 8) | Rd_get_byte( rdec );
+    rdec->range = 0xFFFFFFFFU;
+    rdec->code &= rdec->range;	/* make sure that first byte is discarded */
+    }
+  return !rdec->reload_pending;
+  }
+
+static inline void Rd_normalize( struct Range_decoder * const rdec )
+  {
+  if( rdec->range <= 0x00FFFFFFU )
+    { rdec->range <<= 8; rdec->code = (rdec->code << 8) | Rd_get_byte( rdec ); }
+  }
+
+static inline unsigned Rd_decode( struct Range_decoder * const rdec,
+                                  const int num_bits )
+  {
+  unsigned symbol = 0;
+  int i;
+  for( i = num_bits; i > 0; --i )
+    {
+    bool bit;
+    Rd_normalize( rdec );
+    rdec->range >>= 1;
+/*    symbol <<= 1; */
+/*    if( rdec->code >= rdec->range ) { rdec->code -= rdec->range; symbol |= 1; } */
+    bit = ( rdec->code >= rdec->range );
+    symbol = ( symbol << 1 ) + bit;
+    rdec->code -= rdec->range & ( 0U - bit );
+    }
+  return symbol;
+  }
+
+static inline unsigned Rd_decode_bit( struct Range_decoder * const rdec,
+                                      Bit_model * const probability )
+  {
+  uint32_t bound;
+  Rd_normalize( rdec );
+  bound = ( rdec->range >> bit_model_total_bits ) * *probability;
+  if( rdec->code < bound )
+    {
+    rdec->range = bound;
+    *probability += (bit_model_total - *probability) >> bit_model_move_bits;
+    return 0;
+    }
+  else
+    {
+    rdec->range -= bound;
+    rdec->code -= bound;
+    *probability -= *probability >> bit_model_move_bits;
+    return 1;
+    }
+  }
+
+static inline unsigned Rd_decode_tree3( struct Range_decoder * const rdec,
+                                        Bit_model bm[] )
+  {
+  unsigned symbol = 1;
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  return symbol & 7;
+  }
+
+static inline unsigned Rd_decode_tree6( struct Range_decoder * const rdec,
+                                        Bit_model bm[] )
+  {
+  unsigned symbol = 1;
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  return symbol & 0x3F;
+  }
+
+static inline unsigned Rd_decode_tree8( struct Range_decoder * const rdec,
+                                        Bit_model bm[] )
+  {
+  unsigned symbol = 1;
+  int i;
+  for( i = 0; i < 8; ++i )
+    symbol = ( symbol << 1 ) | Rd_decode_bit( rdec, &bm[symbol] );
+  return symbol & 0xFF;
+  }
+
+static inline unsigned
+Rd_decode_tree_reversed( struct Range_decoder * const rdec,
+                         Bit_model bm[], const int num_bits )
+  {
+  unsigned model = 1;
+  unsigned symbol = 0;
+  int i;
+  for( i = 0; i < num_bits; ++i )
+    {
+    const unsigned bit = Rd_decode_bit( rdec, &bm[model] );
+    model = ( model << 1 ) + bit;
+    symbol |= ( bit << i );
+    }
+  return symbol;
+  }
+
+static inline unsigned
+Rd_decode_tree_reversed4( struct Range_decoder * const rdec, Bit_model bm[] )
+  {
+  unsigned symbol = Rd_decode_bit( rdec, &bm[1] );
+  unsigned model = 2 + symbol;
+  unsigned bit = Rd_decode_bit( rdec, &bm[model] );
+  model = ( model << 1 ) + bit; symbol |= ( bit << 1 );
+  bit = Rd_decode_bit( rdec, &bm[model] );
+  model = ( model << 1 ) + bit; symbol |= ( bit << 2 );
+  symbol |= ( Rd_decode_bit( rdec, &bm[model] ) << 3 );
+  return symbol;
+  }
+
+static inline unsigned Rd_decode_matched( struct Range_decoder * const rdec,
+                                          Bit_model bm[], unsigned match_byte )
+  {
+  unsigned symbol = 1;
+  unsigned mask = 0x100;
+  while( true )
+    {
+    const unsigned match_bit = ( match_byte <<= 1 ) & mask;
+    const unsigned bit = Rd_decode_bit( rdec, &bm[symbol+match_bit+mask] );
+    symbol = ( symbol << 1 ) + bit;
+    if( symbol > 0xFF ) return symbol & 0xFF;
+    mask &= ~(match_bit ^ (bit << 8));	/* if( match_bit != bit ) mask = 0; */
+    }
+  }
+
+static inline unsigned Rd_decode_len( struct Range_decoder * const rdec,
+                                      struct Len_model * const lm,
+                                      const int pos_state )
+  {
+  if( Rd_decode_bit( rdec, &lm->choice1 ) == 0 )
+    return Rd_decode_tree3( rdec, lm->bm_low[pos_state] );
+  if( Rd_decode_bit( rdec, &lm->choice2 ) == 0 )
+    return len_low_symbols + Rd_decode_tree3( rdec, lm->bm_mid[pos_state] );
+  return len_low_symbols + len_mid_symbols + Rd_decode_tree8( rdec, lm->bm_high );
+  }
+
+
+enum { lzd_min_free_bytes = max_match_len };
+
+struct LZ_decoder
+  {
+  struct Circular_buffer cb;
+  unsigned long long partial_data_pos;
+  struct Range_decoder * rdec;
+  unsigned dictionary_size;
+  uint32_t crc;
+  bool member_finished;
+  bool verify_trailer_pending;
+  bool pos_wrapped;
+  unsigned rep0;		/* rep[0-3] latest four distances */
+  unsigned rep1;		/* used for efficient coding of */
+  unsigned rep2;		/* repeated distances */
+  unsigned rep3;
+  State state;
+
+  Bit_model bm_literal[1<<literal_context_bits][0x300];
+  Bit_model bm_match[states][pos_states];
+  Bit_model bm_rep[states];
+  Bit_model bm_rep0[states];
+  Bit_model bm_rep1[states];
+  Bit_model bm_rep2[states];
+  Bit_model bm_len[states][pos_states];
+  Bit_model bm_dis_slot[len_states][1<<dis_slot_bits];
+  Bit_model bm_dis[modeled_distances-end_dis_model+1];
+  Bit_model bm_align[dis_align_size];
+
+  struct Len_model match_len_model;
+  struct Len_model rep_len_model;
+  };
+
+static inline bool LZd_enough_free_bytes( const struct LZ_decoder * const d )
+  { return Cb_free_bytes( &d->cb ) >= lzd_min_free_bytes; }
+
+static inline uint8_t LZd_peek_prev( const struct LZ_decoder * const d )
+  { return d->cb.buffer[((d->cb.put > 0) ? d->cb.put : d->cb.buffer_size)-1]; }
+
+static inline uint8_t LZd_peek( const struct LZ_decoder * const d,
+                                const unsigned distance )
+  {
+  const unsigned i = ( ( d->cb.put > distance ) ? 0 : d->cb.buffer_size ) +
+                     d->cb.put - distance - 1;
+  return d->cb.buffer[i];
+  }
+
+static inline void LZd_put_byte( struct LZ_decoder * const d, const uint8_t b )
+  {
+  CRC32_update_byte( &d->crc, b );
+  d->cb.buffer[d->cb.put] = b;
+  if( ++d->cb.put >= d->cb.buffer_size )
+    { d->partial_data_pos += d->cb.put; d->cb.put = 0; d->pos_wrapped = true; }
+  }
+
+static inline void LZd_copy_block( struct LZ_decoder * const d,
+                                   const unsigned distance, unsigned len )
+  {
+  unsigned lpos = d->cb.put, i = lpos - distance - 1;
+  bool fast, fast2;
+  if( lpos > distance )
+    {
+    fast = ( len < d->cb.buffer_size - lpos );
+    fast2 = ( fast && len <= lpos - i );
+    }
+  else
+    {
+    i += d->cb.buffer_size;
+    fast = ( len < d->cb.buffer_size - i );	/* (i == pos) may happen */
+    fast2 = ( fast && len <= i - lpos );
+    }
+  if( fast )					/* no wrap */
+    {
+    const unsigned tlen = len;
+    if( fast2 )					/* no wrap, no overlap */
+      memcpy( d->cb.buffer + lpos, d->cb.buffer + i, len );
+    else
+      for( ; len > 0; --len ) d->cb.buffer[lpos++] = d->cb.buffer[i++];
+    CRC32_update_buf( &d->crc, d->cb.buffer + d->cb.put, tlen );
+    d->cb.put += tlen;
+    }
+  else for( ; len > 0; --len )
+    {
+    LZd_put_byte( d, d->cb.buffer[i] );
+    if( ++i >= d->cb.buffer_size ) i = 0;
+    }
+  }
+
+static inline bool LZd_init( struct LZ_decoder * const d,
+                             struct Range_decoder * const rde,
+                             const unsigned dict_size )
+  {
+  if( !Cb_init( &d->cb, max( 65536, dict_size ) + lzd_min_free_bytes ) )
+    return false;
+  d->partial_data_pos = 0;
+  d->rdec = rde;
+  d->dictionary_size = dict_size;
+  d->crc = 0xFFFFFFFFU;
+  d->member_finished = false;
+  d->verify_trailer_pending = 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->state = 0;
+
+  Bm_array_init( d->bm_literal[0], (1 << literal_context_bits) * 0x300 );
+  Bm_array_init( d->bm_match[0], states * pos_states );
+  Bm_array_init( d->bm_rep, states );
+  Bm_array_init( d->bm_rep0, states );
+  Bm_array_init( d->bm_rep1, states );
+  Bm_array_init( d->bm_rep2, states );
+  Bm_array_init( d->bm_len[0], states * pos_states );
+  Bm_array_init( d->bm_dis_slot[0], len_states * (1 << dis_slot_bits) );
+  Bm_array_init( d->bm_dis, modeled_distances - end_dis_model + 1 );
+  Bm_array_init( d->bm_align, dis_align_size );
+  Lm_init( &d->match_len_model );
+  Lm_init( &d->rep_len_model );
+  return true;
+  }
+
+static inline void LZd_free( struct LZ_decoder * const d )
+  { Cb_free( &d->cb ); }
+
+static inline bool LZd_member_finished( const struct LZ_decoder * const d )
+  { return ( d->member_finished && Cb_empty( &d->cb ) ); }
+
+static inline unsigned LZd_crc( const struct LZ_decoder * const d )
+  { return d->crc ^ 0xFFFFFFFFU; }
+
+static inline unsigned long long
+LZd_data_position( const struct LZ_decoder * const d )
+  { return d->partial_data_pos + d->cb.put; }
diff --git a/cbits/encoder.c b/cbits/encoder.c
new file mode 100644
--- /dev/null
+++ b/cbits/encoder.c
@@ -0,0 +1,603 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+static int LZe_get_match_pairs( struct LZ_encoder * const e, struct Pair * pairs )
+  {
+  int32_t * ptr0 = e->eb.mb.pos_array + ( e->eb.mb.cyclic_pos << 1 );
+  int32_t * ptr1 = ptr0 + 1;
+  int32_t * newptr;
+  int len = 0, len0 = 0, len1 = 0;
+  int maxlen = 0;
+  int num_pairs = 0;
+  const int pos1 = e->eb.mb.pos + 1;
+  const int min_pos = ( e->eb.mb.pos > e->eb.mb.dictionary_size ) ?
+                        e->eb.mb.pos - e->eb.mb.dictionary_size : 0;
+  const uint8_t * const data = Mb_ptr_to_current_pos( &e->eb.mb );
+  int count, key2, key3, key4, newpos1;
+  unsigned tmp;
+  int len_limit = e->match_len_limit;
+
+  if( len_limit > Mb_available_bytes( &e->eb.mb ) )
+    {
+    e->been_flushed = true;
+    len_limit = Mb_available_bytes( &e->eb.mb );
+    if( len_limit < 4 ) { *ptr0 = *ptr1 = 0; return 0; }
+    }
+
+  tmp = crc32[data[0]] ^ data[1];
+  key2 = tmp & ( num_prev_positions2 - 1 );
+  tmp ^= (unsigned)data[2] << 8;
+  key3 = num_prev_positions2 + ( tmp & ( num_prev_positions3 - 1 ) );
+  key4 = num_prev_positions2 + num_prev_positions3 +
+         ( ( tmp ^ ( crc32[data[3]] << 5 ) ) & e->eb.mb.key4_mask );
+
+  if( pairs )
+    {
+    int np2 = e->eb.mb.prev_positions[key2];
+    int np3 = e->eb.mb.prev_positions[key3];
+    if( np2 > min_pos && e->eb.mb.buffer[np2-1] == data[0] )
+      {
+      pairs[0].dis = e->eb.mb.pos - np2;
+      pairs[0].len = maxlen = 2;
+      num_pairs = 1;
+      }
+    if( np2 != np3 && np3 > min_pos && e->eb.mb.buffer[np3-1] == data[0] )
+      {
+      maxlen = 3;
+      np2 = np3;
+      pairs[num_pairs].dis = e->eb.mb.pos - np2;
+      ++num_pairs;
+      }
+    if( num_pairs > 0 )
+      {
+      const int delta = pos1 - np2;
+      while( maxlen < len_limit && data[maxlen-delta] == data[maxlen] )
+        ++maxlen;
+      pairs[num_pairs-1].len = maxlen;
+      if( maxlen >= len_limit ) pairs = 0;	/* done. now just skip */
+      }
+    if( maxlen < 3 ) maxlen = 3;
+    }
+
+  e->eb.mb.prev_positions[key2] = pos1;
+  e->eb.mb.prev_positions[key3] = pos1;
+  newpos1 = e->eb.mb.prev_positions[key4];
+  e->eb.mb.prev_positions[key4] = pos1;
+
+  for( count = e->cycles; ; )
+    {
+    int delta;
+    if( newpos1 <= min_pos || --count < 0 ) { *ptr0 = *ptr1 = 0; break; }
+
+    if( e->been_flushed ) len = 0;
+    delta = pos1 - newpos1;
+    newptr = e->eb.mb.pos_array +
+      ( ( e->eb.mb.cyclic_pos - delta +
+          ( (e->eb.mb.cyclic_pos >= delta) ? 0 : e->eb.mb.dictionary_size + 1 ) ) << 1 );
+    if( data[len-delta] == data[len] )
+      {
+      while( ++len < len_limit && data[len-delta] == data[len] ) {}
+      if( pairs && maxlen < len )
+        {
+        pairs[num_pairs].dis = delta - 1;
+        pairs[num_pairs].len = maxlen = len;
+        ++num_pairs;
+        }
+      if( len >= len_limit )
+        {
+        *ptr0 = newptr[0];
+        *ptr1 = newptr[1];
+        break;
+        }
+      }
+    if( data[len-delta] < data[len] )
+      {
+      *ptr0 = newpos1;
+      ptr0 = newptr + 1;
+      newpos1 = *ptr0;
+      len0 = len; if( len1 < len ) len = len1;
+      }
+    else
+      {
+      *ptr1 = newpos1;
+      ptr1 = newptr;
+      newpos1 = *ptr1;
+      len1 = len; if( len0 < len ) len = len0;
+      }
+    }
+  return num_pairs;
+  }
+
+
+static void LZe_update_distance_prices( struct LZ_encoder * const e )
+  {
+  int dis, len_state;
+  for( dis = start_dis_model; dis < modeled_distances; ++dis )
+    {
+    const int dis_slot = dis_slots[dis];
+    const int direct_bits = ( dis_slot >> 1 ) - 1;
+    const int base = ( 2 | ( dis_slot & 1 ) ) << direct_bits;
+    const int price = price_symbol_reversed( e->eb.bm_dis + ( base - dis_slot ),
+                                             dis - base, direct_bits );
+    for( len_state = 0; len_state < len_states; ++len_state )
+      e->dis_prices[len_state][dis] = price;
+    }
+
+  for( len_state = 0; len_state < len_states; ++len_state )
+    {
+    int * const dsp = e->dis_slot_prices[len_state];
+    int * const dp = e->dis_prices[len_state];
+    const Bit_model * const bmds = e->eb.bm_dis_slot[len_state];
+    int slot = 0;
+    for( ; slot < end_dis_model; ++slot )
+      dsp[slot] = price_symbol6( bmds, slot );
+    for( ; slot < e->num_dis_slots; ++slot )
+      dsp[slot] = price_symbol6( bmds, slot ) +
+                  (((( slot >> 1 ) - 1 ) - dis_align_bits ) << price_shift_bits );
+
+    for( dis = 0; dis < start_dis_model; ++dis )
+      dp[dis] = dsp[dis];
+    for( ; dis < modeled_distances; ++dis )
+      dp[dis] += dsp[dis_slots[dis]];
+    }
+  }
+
+
+/* Returns the number of bytes advanced (ahead).
+   trials[0]..trials[ahead-1] contain the steps to encode.
+   ( trials[0].dis4 == -1 ) means literal.
+   A match/rep longer or equal than match_len_limit finishes the sequence.
+*/
+static int LZe_sequence_optimizer( struct LZ_encoder * const e,
+                                   const int reps[num_rep_distances],
+                                   const State state )
+  {
+  int main_len, num_pairs, i, rep, num_trials, len;
+  int rep_index = 0, cur = 0;
+  int replens[num_rep_distances];
+
+  if( e->pending_num_pairs > 0 )		/* from previous call */
+    {
+    num_pairs = e->pending_num_pairs;
+    e->pending_num_pairs = 0;
+    }
+  else
+    num_pairs = LZe_read_match_distances( e );
+  main_len = ( num_pairs > 0 ) ? e->pairs[num_pairs-1].len : 0;
+
+  for( i = 0; i < num_rep_distances; ++i )
+    {
+    replens[i] = Mb_true_match_len( &e->eb.mb, 0, reps[i] + 1 );
+    if( replens[i] > replens[rep_index] ) rep_index = i;
+    }
+  if( replens[rep_index] >= e->match_len_limit )
+    {
+    e->trials[0].price = replens[rep_index];
+    e->trials[0].dis4 = rep_index;
+    if( !LZe_move_and_update( e, replens[rep_index] ) ) return 0;
+    return replens[rep_index];
+    }
+
+  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;
+    if( !LZe_move_and_update( e, main_len ) ) return 0;
+    return main_len;
+    }
+
+  {
+  const int pos_state = Mb_data_position( &e->eb.mb ) & pos_state_mask;
+  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] );
+  const uint8_t prev_byte = Mb_peek( &e->eb.mb, 1 );
+  const uint8_t cur_byte = Mb_peek( &e->eb.mb, 0 );
+  const uint8_t match_byte = Mb_peek( &e->eb.mb, reps[0] + 1 );
+
+  e->trials[1].price = price0( e->eb.bm_match[state][pos_state] );
+  if( St_is_char( state ) )
+    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 */
+
+  if( match_byte == cur_byte )
+    Tr_update( &e->trials[1], rep_match_price +
+               LZeb_price_shortrep( &e->eb, state, pos_state ), 0, 0 );
+
+  num_trials = max( main_len, replens[rep_index] );
+
+  if( num_trials < min_match_len )
+    {
+    e->trials[0].price = 1;
+    e->trials[0].dis4 = e->trials[1].dis4;
+    if( !Mb_move_pos( &e->eb.mb ) ) return 0;
+    return 1;
+    }
+
+  e->trials[0].state = state;
+  for( i = 0; i < num_rep_distances; ++i )
+    e->trials[0].reps[i] = reps[i];
+
+  for( len = min_match_len; len <= num_trials; ++len )
+    e->trials[len].price = infinite_price;
+
+  for( rep = 0; rep < num_rep_distances; ++rep )
+    {
+    int price;
+    if( replens[rep] < min_match_len ) continue;
+    price = rep_match_price + LZeb_price_rep( &e->eb, rep, state, pos_state );
+    for( len = min_match_len; len <= replens[rep]; ++len )
+      Tr_update( &e->trials[len], price +
+                 Lp_price( &e->rep_len_prices, len, pos_state ), rep, 0 );
+    }
+
+  if( main_len > replens[0] )
+    {
+    const int normal_match_price = match_price + price0( e->eb.bm_rep[state] );
+    int i = 0, len = max( replens[0] + 1, min_match_len );
+    while( len > e->pairs[i].len ) ++i;
+    while( true )
+      {
+      const int dis = e->pairs[i].dis;
+      Tr_update( &e->trials[len], normal_match_price +
+                 LZe_price_pair( e, dis, len, pos_state ),
+                 dis + num_rep_distances, 0 );
+      if( ++len > e->pairs[i].len && ++i >= num_pairs ) break;
+      }
+    }
+  }
+
+  while( true )				/* price optimization loop */
+    {
+    struct Trial *cur_trial, *next_trial;
+    int newlen, pos_state, triable_bytes, len_limit;
+    int start_len = min_match_len;
+    int next_price, match_price, rep_match_price;
+    State cur_state;
+    uint8_t prev_byte, cur_byte, match_byte;
+
+    if( !Mb_move_pos( &e->eb.mb ) ) return 0;
+    if( ++cur >= num_trials )		/* no more initialized trials */
+      {
+      LZe_backward( e, cur );
+      return cur;
+      }
+
+    num_pairs = LZe_read_match_distances( e );
+    newlen = ( num_pairs > 0 ) ? e->pairs[num_pairs-1].len : 0;
+    if( newlen >= e->match_len_limit )
+      {
+      e->pending_num_pairs = num_pairs;
+      LZe_backward( e, cur );
+      return cur;
+      }
+
+    /* give final values to current trial */
+    cur_trial = &e->trials[cur];
+    {
+    const int dis4 = cur_trial->dis4;
+    int prev_index = cur_trial->prev_index;
+    const int prev_index2 = cur_trial->prev_index2;
+
+    if( prev_index2 == single_step_trial )
+      {
+      cur_state = e->trials[prev_index].state;
+      if( prev_index + 1 == cur )			/* len == 1 */
+        {
+        if( dis4 == 0 ) cur_state = St_set_short_rep( 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 cur_state = St_set_match( cur_state );
+      }
+    else
+      {
+      if( prev_index2 == dual_step_trial )	/* dis4 == 0 (rep0) */
+        --prev_index;
+      else					/* prev_index2 >= 0 */
+        prev_index = prev_index2;
+      cur_state = St_set_char_rep();
+      }
+    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 */
+    }
+
+    pos_state = Mb_data_position( &e->eb.mb ) & pos_state_mask;
+    prev_byte = Mb_peek( &e->eb.mb, 1 );
+    cur_byte = Mb_peek( &e->eb.mb, 0 );
+    match_byte = Mb_peek( &e->eb.mb, cur_trial->reps[0] + 1 );
+
+    next_price = cur_trial->price +
+                 price0( e->eb.bm_match[cur_state][pos_state] );
+    if( St_is_char( cur_state ) )
+      next_price += LZeb_price_literal( &e->eb, prev_byte, cur_byte );
+    else
+      next_price += LZeb_price_matched( &e->eb, prev_byte, cur_byte, match_byte );
+
+    /* try last updates to next trial */
+    next_trial = &e->trials[cur+1];
+
+    Tr_update( next_trial, next_price, -1, cur );	/* literal */
+
+    match_price = cur_trial->price + price1( e->eb.bm_match[cur_state][pos_state] );
+    rep_match_price = match_price + price1( e->eb.bm_rep[cur_state] );
+
+    if( match_byte == cur_byte && next_trial->dis4 != 0 &&
+        next_trial->prev_index2 == single_step_trial )
+      {
+      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->prev_index = cur;
+        }
+      }
+
+    triable_bytes =
+      min( Mb_available_bytes( &e->eb.mb ), max_num_trials - 1 - cur );
+    if( triable_bytes < min_match_len ) continue;
+
+    len_limit = min( e->match_len_limit, triable_bytes );
+
+    /* try literal + rep0 */
+    if( match_byte != cur_byte && next_trial->prev_index != cur )
+      {
+      const uint8_t * const data = Mb_ptr_to_current_pos( &e->eb.mb );
+      const int dis = cur_trial->reps[0] + 1;
+      const int limit = min( e->match_len_limit + 1, triable_bytes );
+      int len = 1;
+      while( len < limit && data[len-dis] == data[len] ) ++len;
+      if( --len >= min_match_len )
+        {
+        const int pos_state2 = ( pos_state + 1 ) & pos_state_mask;
+        const State state2 = St_set_char( cur_state );
+        const int price = next_price +
+                          price1( e->eb.bm_match[state2][pos_state2] ) +
+                          price1( e->eb.bm_rep[state2] ) +
+                          LZe_price_rep0_len( e, len, state2, pos_state2 );
+        while( num_trials < cur + 1 + len )
+          e->trials[++num_trials].price = infinite_price;
+        Tr_update2( &e->trials[cur+1+len], price, cur + 1 );
+        }
+      }
+
+    /* try rep distances */
+    for( rep = 0; rep < num_rep_distances; ++rep )
+      {
+      const uint8_t * const data = Mb_ptr_to_current_pos( &e->eb.mb );
+      const int dis = cur_trial->reps[rep] + 1;
+      int price;
+
+      if( data[0-dis] != data[0] || data[1-dis] != data[1] ) continue;
+      for( len = min_match_len; len < len_limit; ++len )
+        if( data[len-dis] != data[len] ) break;
+      while( num_trials < cur + len )
+        e->trials[++num_trials].price = infinite_price;
+      price = rep_match_price + LZeb_price_rep( &e->eb, rep, cur_state, pos_state );
+      for( i = min_match_len; i <= len; ++i )
+        Tr_update( &e->trials[cur+i], price +
+                   Lp_price( &e->rep_len_prices, i, pos_state ), rep, cur );
+
+      if( rep == 0 ) start_len = len + 1;	/* discard shorter matches */
+
+      /* try rep + literal + rep0 */
+      {
+      int len2 = len + 1;
+      const int limit = min( e->match_len_limit + len2, triable_bytes );
+      int pos_state2;
+      State state2;
+      while( len2 < limit && data[len2-dis] == data[len2] ) ++len2;
+      len2 -= len + 1;
+      if( len2 < min_match_len ) continue;
+
+      pos_state2 = ( pos_state + len ) & pos_state_mask;
+      state2 = St_set_rep( cur_state );
+      price += Lp_price( &e->rep_len_prices, len, pos_state ) +
+               price0( e->eb.bm_match[state2][pos_state2] ) +
+               LZeb_price_matched( &e->eb, data[len-1], data[len], data[len-dis] );
+      pos_state2 = ( pos_state2 + 1 ) & pos_state_mask;
+      state2 = St_set_char( state2 );
+      price += price1( e->eb.bm_match[state2][pos_state2] ) +
+               price1( e->eb.bm_rep[state2] ) +
+               LZe_price_rep0_len( e, len2, state2, pos_state2 );
+      while( num_trials < cur + len + 1 + len2 )
+        e->trials[++num_trials].price = infinite_price;
+      Tr_update3( &e->trials[cur+len+1+len2], price, rep, cur + len + 1, cur );
+      }
+      }
+
+    /* try matches */
+    if( newlen >= start_len && newlen <= len_limit )
+      {
+      int dis;
+      const int normal_match_price = match_price +
+                                     price0( e->eb.bm_rep[cur_state] );
+
+      while( num_trials < cur + newlen )
+        e->trials[++num_trials].price = infinite_price;
+
+      i = 0;
+      while( e->pairs[i].len < start_len ) ++i;
+      dis = e->pairs[i].dis;
+      for( len = start_len; ; ++len )
+        {
+        int price = normal_match_price + LZe_price_pair( e, dis, len, pos_state );
+        Tr_update( &e->trials[cur+len], price, dis + num_rep_distances, cur );
+
+        /* try match + literal + rep0 */
+        if( len == e->pairs[i].len )
+          {
+          const uint8_t * const data = Mb_ptr_to_current_pos( &e->eb.mb );
+          const int dis2 = dis + 1;
+          int len2 = len + 1;
+          const int limit = min( e->match_len_limit + len2, triable_bytes );
+          while( len2 < limit && data[len2-dis2] == data[len2] ) ++len2;
+          len2 -= len + 1;
+          if( len2 >= min_match_len )
+            {
+            int pos_state2 = ( pos_state + len ) & pos_state_mask;
+            State state2 = St_set_match( cur_state );
+            price += price0( e->eb.bm_match[state2][pos_state2] ) +
+                     LZeb_price_matched( &e->eb, data[len-1], data[len], data[len-dis2] );
+            pos_state2 = ( pos_state2 + 1 ) & pos_state_mask;
+            state2 = St_set_char( state2 );
+            price += price1( e->eb.bm_match[state2][pos_state2] ) +
+                     price1( e->eb.bm_rep[state2] ) +
+                     LZe_price_rep0_len( e, len2, state2, pos_state2 );
+
+            while( num_trials < cur + len + 1 + len2 )
+              e->trials[++num_trials].price = infinite_price;
+            Tr_update3( &e->trials[cur+len+1+len2], price,
+                        dis + num_rep_distances, cur + len + 1, cur );
+            }
+          if( ++i >= num_pairs ) break;
+          dis = e->pairs[i].dis;
+          }
+        }
+      }
+    }
+  }
+
+
+static bool LZe_encode_member( struct LZ_encoder * const e )
+  {
+  const bool best = ( e->match_len_limit > 12 );
+  const int dis_price_count = best ? 1 : 512;
+  const int align_price_count = best ? 1 : dis_align_size;
+  const int price_count = ( e->match_len_limit > 36 ) ? 1013 : 4093;
+  int ahead, i;
+  State * const state = &e->eb.state;
+
+  if( e->eb.member_finished ) return true;
+  if( Re_member_position( &e->eb.renc ) >= e->eb.member_size_limit )
+    { LZeb_try_full_flush( &e->eb ); return true; }
+
+  if( Mb_data_position( &e->eb.mb ) == 0 &&
+      !Mb_data_finished( &e->eb.mb ) )		/* encode first byte */
+    {
+    const uint8_t prev_byte = 0;
+    uint8_t cur_byte;
+    if( !Mb_enough_available_bytes( &e->eb.mb ) ||
+        !Re_enough_free_bytes( &e->eb.renc ) ) return true;
+    cur_byte = Mb_peek( &e->eb.mb, 0 );
+    Re_encode_bit( &e->eb.renc, &e->eb.bm_match[*state][0], 0 );
+    LZeb_encode_literal( &e->eb, prev_byte, cur_byte );
+    CRC32_update_byte( &e->eb.crc, cur_byte );
+    LZe_get_match_pairs( e, 0 );
+    if( !Mb_move_pos( &e->eb.mb ) ) return false;
+    }
+
+  while( !Mb_data_finished( &e->eb.mb ) )
+    {
+    if( !Mb_enough_available_bytes( &e->eb.mb ) ||
+        !Re_enough_free_bytes( &e->eb.renc ) ) return true;
+    if( e->price_counter <= 0 && e->pending_num_pairs == 0 )
+      {
+      e->price_counter = price_count;	/* recalculate prices every these bytes */
+      if( e->dis_price_counter <= 0 )
+        { e->dis_price_counter = dis_price_count; LZe_update_distance_prices( e ); }
+      if( e->align_price_counter <= 0 )
+        {
+        e->align_price_counter = align_price_count;
+        for( i = 0; i < dis_align_size; ++i )
+          e->align_prices[i] = price_symbol_reversed( e->eb.bm_align, i, dis_align_bits );
+        }
+      Lp_update_prices( &e->match_len_prices );
+      Lp_update_prices( &e->rep_len_prices );
+      }
+
+    ahead = LZe_sequence_optimizer( e, e->eb.reps, *state );
+    e->price_counter -= ahead;
+
+    for( i = 0; ahead > 0; )
+      {
+      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;
+
+      bool bit = ( dis < 0 );
+      Re_encode_bit( &e->eb.renc, &e->eb.bm_match[*state][pos_state], !bit );
+      if( bit )					/* literal byte */
+        {
+        const uint8_t prev_byte = Mb_peek( &e->eb.mb, ahead + 1 );
+        const uint8_t cur_byte = Mb_peek( &e->eb.mb, ahead );
+        CRC32_update_byte( &e->eb.crc, cur_byte );
+        if( St_is_char( *state ) )
+          LZeb_encode_literal( &e->eb, prev_byte, cur_byte );
+        else
+          {
+          const uint8_t match_byte = Mb_peek( &e->eb.mb, ahead + e->eb.reps[0] + 1 );
+          LZeb_encode_matched( &e->eb, prev_byte, cur_byte, match_byte );
+          }
+        *state = St_set_char( *state );
+        }
+      else					/* match or repeated match */
+        {
+        CRC32_update_buf( &e->eb.crc, Mb_ptr_to_current_pos( &e->eb.mb ) - ahead, len );
+        mtf_reps( dis, e->eb.reps );
+        bit = ( dis < num_rep_distances );
+        Re_encode_bit( &e->eb.renc, &e->eb.bm_rep[*state], bit );
+        if( bit )				/* repeated match */
+          {
+          bit = ( dis == 0 );
+          Re_encode_bit( &e->eb.renc, &e->eb.bm_rep0[*state], !bit );
+          if( bit )
+            Re_encode_bit( &e->eb.renc, &e->eb.bm_len[*state][pos_state], len > 1 );
+          else
+            {
+            Re_encode_bit( &e->eb.renc, &e->eb.bm_rep1[*state], dis > 1 );
+            if( dis > 1 )
+              Re_encode_bit( &e->eb.renc, &e->eb.bm_rep2[*state], dis > 2 );
+            }
+          if( len == 1 ) *state = St_set_short_rep( *state );
+          else
+            {
+            Re_encode_len( &e->eb.renc, &e->eb.rep_len_model, len, pos_state );
+            Lp_decrement_counter( &e->rep_len_prices, pos_state );
+            *state = St_set_rep( *state );
+            }
+          }
+        else					/* match */
+          {
+          dis -= num_rep_distances;
+          LZeb_encode_pair( &e->eb, dis, len, pos_state );
+          if( dis >= modeled_distances ) --e->align_price_counter;
+          --e->dis_price_counter;
+          Lp_decrement_counter( &e->match_len_prices, pos_state );
+          *state = St_set_match( *state );
+          }
+        }
+      ahead -= len; i += len;
+      if( Re_member_position( &e->eb.renc ) >= e->eb.member_size_limit )
+        {
+        if( !Mb_dec_pos( &e->eb.mb, ahead ) ) return false;
+        LZeb_try_full_flush( &e->eb );
+        return true;
+        }
+      }
+    }
+  LZeb_try_full_flush( &e->eb );
+  return true;
+  }
diff --git a/cbits/encoder.h b/cbits/encoder.h
new file mode 100644
--- /dev/null
+++ b/cbits/encoder.h
@@ -0,0 +1,327 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+struct Len_prices
+  {
+  const struct Len_model * lm;
+  int len_symbols;
+  int count;
+  int prices[pos_states][max_len_symbols];
+  int counters[pos_states];			/* may decrement below 0 */
+  };
+
+static inline void Lp_update_low_mid_prices( struct Len_prices * const lp,
+                                             const int pos_state )
+  {
+  int * const pps = lp->prices[pos_state];
+  int tmp = price0( lp->lm->choice1 );
+  int len = 0;
+  for( ; len < len_low_symbols && len < lp->len_symbols; ++len )
+    pps[len] = tmp + price_symbol3( lp->lm->bm_low[pos_state], len );
+  if( len >= lp->len_symbols ) return;
+  tmp = price1( lp->lm->choice1 ) + price0( lp->lm->choice2 );
+  for( ; len < len_low_symbols + len_mid_symbols && len < lp->len_symbols; ++len )
+    pps[len] = tmp +
+               price_symbol3( lp->lm->bm_mid[pos_state], len - len_low_symbols );
+    }
+
+static inline void Lp_update_high_prices( struct Len_prices * const lp )
+  {
+  const int tmp = price1( lp->lm->choice1 ) + price1( lp->lm->choice2 );
+  int len;
+  for( len = len_low_symbols + len_mid_symbols; len < lp->len_symbols; ++len )
+    /* using 4 slots per value makes "Lp_price" faster */
+    lp->prices[3][len] = lp->prices[2][len] =
+    lp->prices[1][len] = lp->prices[0][len] = tmp +
+      price_symbol8( lp->lm->bm_high, len - len_low_symbols - len_mid_symbols );
+  }
+
+static inline void Lp_reset( struct Len_prices * const lp )
+  { int i; for( i = 0; i < pos_states; ++i ) lp->counters[i] = 0; }
+
+static inline void Lp_init( struct Len_prices * const lp,
+                            const struct Len_model * const lm,
+                            const int match_len_limit )
+  {
+  lp->lm = lm;
+  lp->len_symbols = match_len_limit + 1 - min_match_len;
+  lp->count = ( match_len_limit > 12 ) ? 1 : lp->len_symbols;
+  Lp_reset( lp );
+  }
+
+static inline void Lp_decrement_counter( struct Len_prices * const lp,
+                                         const int pos_state )
+  { --lp->counters[pos_state]; }
+
+static inline void Lp_update_prices( struct Len_prices * const lp )
+  {
+  int pos_state;
+  bool high_pending = false;
+  for( pos_state = 0; pos_state < pos_states; ++pos_state )
+    if( lp->counters[pos_state] <= 0 )
+      { lp->counters[pos_state] = lp->count;
+        Lp_update_low_mid_prices( lp, pos_state ); high_pending = true; }
+  if( high_pending && lp->len_symbols > len_low_symbols + len_mid_symbols )
+    Lp_update_high_prices( lp );
+  }
+
+static inline int Lp_price( const struct Len_prices * const lp,
+                            const int len, const int pos_state )
+  { return lp->prices[pos_state][len - min_match_len]; }
+
+
+struct Pair			/* distance-length pair */
+  {
+  int dis;
+  int len;
+  };
+
+enum { infinite_price = 0x0FFFFFFF,
+       max_num_trials = 1 << 13,
+       single_step_trial = -2,
+       dual_step_trial = -1 };
+
+struct Trial
+  {
+  State state;
+  int price;		/* dual use var; cumulative price, match length */
+  int dis4;		/* -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];
+  };
+
+static inline void Tr_update( struct Trial * const trial, const int pr,
+                              const int distance4, const int p_i )
+  {
+  if( pr < trial->price )
+    { trial->price = pr; trial->dis4 = distance4; trial->prev_index = p_i;
+      trial->prev_index2 = single_step_trial; }
+  }
+
+static inline void Tr_update2( struct Trial * const trial, const int pr,
+                               const int p_i )
+  {
+  if( pr < trial->price )
+    { trial->price = pr; trial->dis4 = 0; trial->prev_index = p_i;
+      trial->prev_index2 = dual_step_trial; }
+  }
+
+static inline void Tr_update3( struct Trial * const trial, const int pr,
+                               const int distance4, const int p_i,
+                               const int p_i2 )
+  {
+  if( pr < trial->price )
+    { trial->price = pr; trial->dis4 = distance4; trial->prev_index = p_i;
+      trial->prev_index2 = p_i2; }
+  }
+
+
+struct LZ_encoder
+  {
+  struct LZ_encoder_base eb;
+  int cycles;
+  int match_len_limit;
+  struct Len_prices match_len_prices;
+  struct Len_prices rep_len_prices;
+  int pending_num_pairs;
+  struct Pair pairs[max_match_len+1];
+  struct Trial trials[max_num_trials];
+
+  int dis_slot_prices[len_states][2*max_dictionary_bits];
+  int dis_prices[len_states][modeled_distances];
+  int align_prices[dis_align_size];
+  int num_dis_slots;
+  int price_counter;		/* counters may decrement below 0 */
+  int dis_price_counter;
+  int align_price_counter;
+  bool been_flushed;
+  };
+
+static inline bool Mb_dec_pos( struct Matchfinder_base * const mb,
+                               const int ahead )
+  {
+  if( ahead < 0 || mb->pos < ahead ) return false;
+  mb->pos -= ahead;
+  if( mb->cyclic_pos < ahead ) mb->cyclic_pos += mb->dictionary_size + 1;
+  mb->cyclic_pos -= ahead;
+  return true;
+  }
+
+static int LZe_get_match_pairs( struct LZ_encoder * const e, struct 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] )
+  {
+  if( dis4 >= num_rep_distances )			/* match */
+    {
+    reps[3] = reps[2]; reps[2] = reps[1]; reps[1] = reps[0];
+    reps[0] = dis4 - num_rep_distances;
+    }
+  else if( dis4 > 0 )				/* repeated match */
+    {
+    const int distance = reps[dis4];
+    int i; for( i = dis4; i > 0; --i ) reps[i] = reps[i-1];
+    reps[0] = distance;
+    }
+  }
+
+static inline int LZeb_price_shortrep( const struct LZ_encoder_base * const eb,
+                                       const State state, const int pos_state )
+  {
+  return price0( eb->bm_rep0[state] ) + price0( eb->bm_len[state][pos_state] );
+  }
+
+static inline int LZeb_price_rep( const struct LZ_encoder_base * const eb,
+                                  const int rep, const State state,
+                                  const int pos_state )
+  {
+  int price;
+  if( rep == 0 ) return price0( eb->bm_rep0[state] ) +
+                        price1( eb->bm_len[state][pos_state] );
+  price = price1( eb->bm_rep0[state] );
+  if( rep == 1 )
+    price += price0( eb->bm_rep1[state] );
+  else
+    {
+    price += price1( eb->bm_rep1[state] );
+    price += price_bit( eb->bm_rep2[state], rep - 2 );
+    }
+  return price;
+  }
+
+static inline int LZe_price_rep0_len( const struct LZ_encoder * const e,
+                                      const int len, const State state,
+                                      const int pos_state )
+  {
+  return LZeb_price_rep( &e->eb, 0, state, pos_state ) +
+         Lp_price( &e->rep_len_prices, len, pos_state );
+  }
+
+static inline int LZe_price_pair( const struct LZ_encoder * const e,
+                                  const int dis, const int len,
+                                  const int pos_state )
+  {
+  const int price = Lp_price( &e->match_len_prices, len, pos_state );
+  const int len_state = get_len_state( len );
+  if( dis < modeled_distances )
+    return price + e->dis_prices[len_state][dis];
+  else
+    return price + e->dis_slot_prices[len_state][get_slot( dis )] +
+           e->align_prices[dis & (dis_align_size - 1)];
+  }
+
+static inline int LZe_read_match_distances( struct LZ_encoder * const e )
+  {
+  const int num_pairs = LZe_get_match_pairs( e, e->pairs );
+  if( num_pairs > 0 )
+    {
+    const int len = e->pairs[num_pairs-1].len;
+    if( len == e->match_len_limit && len < max_match_len )
+      e->pairs[num_pairs-1].len =
+        Mb_true_match_len( &e->eb.mb, len, e->pairs[num_pairs-1].dis + 1 );
+    }
+  return num_pairs;
+  }
+
+static inline bool LZe_move_and_update( struct LZ_encoder * const e, int n )
+  {
+  while( true )
+    {
+    if( !Mb_move_pos( &e->eb.mb ) ) return false;
+    if( --n <= 0 ) break;
+    LZe_get_match_pairs( e, 0 );
+    }
+  return true;
+  }
+
+static inline void LZe_backward( struct LZ_encoder * const e, int cur )
+  {
+  int dis4 = e->trials[cur].dis4;
+  while( cur > 0 )
+    {
+    const int prev_index = e->trials[cur].prev_index;
+    struct Trial * const prev_trial = &e->trials[prev_index];
+
+    if( e->trials[cur].prev_index2 != single_step_trial )
+      {
+      prev_trial->dis4 = -1;					/* literal */
+      prev_trial->prev_index = prev_index - 1;
+      prev_trial->prev_index2 = single_step_trial;
+      if( e->trials[cur].prev_index2 >= 0 )
+        {
+        struct Trial * const prev_trial2 = &e->trials[prev_index-1];
+        prev_trial2->dis4 = dis4; dis4 = 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 = prev_index;
+    }
+  }
+
+enum { num_prev_positions3 = 1 << 16,
+       num_prev_positions2 = 1 << 10 };
+
+static inline bool LZe_init( struct LZ_encoder * const e,
+                             const int dict_size, const int len_limit,
+                             const unsigned long long member_size )
+  {
+  enum { before_size = max_num_trials,
+         /* bytes to keep in buffer after pos */
+         after_size = max_num_trials + ( 2 * max_match_len ) + 1,
+         dict_factor = 2,
+         num_prev_positions23 = num_prev_positions2 + num_prev_positions3,
+         pos_array_factor = 2,
+         min_free_bytes = 2 * max_num_trials };
+
+  if( !LZeb_init( &e->eb, before_size, dict_size, after_size, dict_factor,
+                  num_prev_positions23, pos_array_factor, min_free_bytes,
+                  member_size ) ) return false;
+  e->cycles = ( len_limit < max_match_len ) ? 16 + ( len_limit / 2 ) : 256;
+  e->match_len_limit = len_limit;
+  Lp_init( &e->match_len_prices, &e->eb.match_len_model, e->match_len_limit );
+  Lp_init( &e->rep_len_prices, &e->eb.rep_len_model, e->match_len_limit );
+  e->pending_num_pairs = 0;
+  e->num_dis_slots = 2 * real_bits( e->eb.mb.dictionary_size - 1 );
+  e->trials[1].prev_index = 0;
+  e->trials[1].prev_index2 = single_step_trial;
+  e->price_counter = 0;
+  e->dis_price_counter = 0;
+  e->align_price_counter = 0;
+  e->been_flushed = false;
+  return true;
+  }
+
+static inline void LZe_reset( struct LZ_encoder * const e,
+                              const unsigned long long member_size )
+  {
+  LZeb_reset( &e->eb, member_size );
+  Lp_reset( &e->match_len_prices );
+  Lp_reset( &e->rep_len_prices );
+  e->pending_num_pairs = 0;
+  e->price_counter = 0;
+  e->dis_price_counter = 0;
+  e->align_price_counter = 0;
+  e->been_flushed = false;
+  }
diff --git a/cbits/encoder_base.c b/cbits/encoder_base.c
new file mode 100644
--- /dev/null
+++ b/cbits/encoder_base.c
@@ -0,0 +1,196 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+static bool Mb_normalize_pos( struct Matchfinder_base * const mb )
+  {
+  if( mb->pos > mb->stream_pos )
+    { mb->pos = mb->stream_pos; return false; }
+  if( !mb->at_stream_end )
+    {
+    int i;
+    /* offset is int32_t for the min below */
+    const int32_t offset = mb->pos - mb->before_size - mb->dictionary_size;
+    const int size = mb->stream_pos - offset;
+    memmove( mb->buffer, mb->buffer + offset, size );
+    mb->partial_data_pos += offset;
+    mb->pos -= offset;		/* pos = before_size + dictionary_size */
+    mb->stream_pos -= offset;
+    for( i = 0; i < mb->num_prev_positions; ++i )
+      mb->prev_positions[i] -= min( mb->prev_positions[i], offset );
+    for( i = 0; i < mb->pos_array_size; ++i )
+      mb->pos_array[i] -= min( mb->pos_array[i], offset );
+    }
+  return true;
+  }
+
+
+static bool Mb_init( struct Matchfinder_base * const mb, const int before_size,
+                     const int dict_size, const int after_size,
+                     const int dict_factor, const int num_prev_positions23,
+                     const int pos_array_factor )
+  {
+  const int buffer_size_limit =
+    ( dict_factor * dict_size ) + before_size + after_size;
+  unsigned size;
+  int i;
+
+  mb->partial_data_pos = 0;
+  mb->before_size = before_size;
+  mb->after_size = after_size;
+  mb->pos = 0;
+  mb->cyclic_pos = 0;
+  mb->stream_pos = 0;
+  mb->num_prev_positions23 = num_prev_positions23;
+  mb->at_stream_end = false;
+  mb->flushing = false;
+
+  mb->buffer_size = max( 65536, buffer_size_limit );
+  mb->buffer = (uint8_t *)malloc( mb->buffer_size );
+  if( !mb->buffer ) return false;
+  mb->saved_dictionary_size = dict_size;
+  mb->dictionary_size = dict_size;
+  mb->pos_limit = mb->buffer_size - after_size;
+  size = 1 << max( 16, real_bits( mb->dictionary_size - 1 ) - 2 );
+  if( mb->dictionary_size > 1 << 26 )		/* 64 MiB */
+    size >>= 1;
+  mb->key4_mask = size - 1;
+  size += num_prev_positions23;
+  mb->num_prev_positions = size;
+
+  mb->pos_array_size = pos_array_factor * ( mb->dictionary_size + 1 );
+  size += mb->pos_array_size;
+  if( size * sizeof mb->prev_positions[0] <= size ) mb->prev_positions = 0;
+  else mb->prev_positions =
+    (int32_t *)malloc( size * sizeof mb->prev_positions[0] );
+  if( !mb->prev_positions ) { free( mb->buffer ); return false; }
+  mb->pos_array = mb->prev_positions + mb->num_prev_positions;
+  for( i = 0; i < mb->num_prev_positions; ++i ) mb->prev_positions[i] = 0;
+  return true;
+  }
+
+
+static void Mb_adjust_array( struct Matchfinder_base * const mb )
+  {
+  int size = 1 << max( 16, real_bits( mb->dictionary_size - 1 ) - 2 );
+  if( mb->dictionary_size > 1 << 26 )		/* 64 MiB */
+    size >>= 1;
+  mb->key4_mask = size - 1;
+  size += mb->num_prev_positions23;
+  mb->num_prev_positions = size;
+  mb->pos_array = mb->prev_positions + mb->num_prev_positions;
+  }
+
+
+static void Mb_adjust_dictionary_size( struct Matchfinder_base * const mb )
+  {
+  if( mb->stream_pos < mb->dictionary_size )
+    {
+    mb->dictionary_size = max( min_dictionary_size, mb->stream_pos );
+    Mb_adjust_array( mb );
+    mb->pos_limit = mb->buffer_size;
+    }
+  }
+
+
+static void Mb_reset( struct Matchfinder_base * const mb )
+  {
+  int i;
+  if( mb->stream_pos > mb->pos )
+    memmove( mb->buffer, mb->buffer + mb->pos, mb->stream_pos - mb->pos );
+  mb->partial_data_pos = 0;
+  mb->stream_pos -= mb->pos;
+  mb->pos = 0;
+  mb->cyclic_pos = 0;
+  mb->at_stream_end = false;
+  mb->flushing = false;
+  mb->dictionary_size = mb->saved_dictionary_size;
+  Mb_adjust_array( mb );
+  mb->pos_limit = mb->buffer_size - mb->after_size;
+  for( i = 0; i < mb->num_prev_positions; ++i ) mb->prev_positions[i] = 0;
+  }
+
+
+     /* End Of Stream mark => (dis == 0xFFFFFFFFU, len == min_match_len) */
+static void LZeb_try_full_flush( struct LZ_encoder_base * const eb )
+  {
+  int i;
+  const int pos_state = Mb_data_position( &eb->mb ) & pos_state_mask;
+  const State state = eb->state;
+  Lzip_trailer trailer;
+  if( eb->member_finished ||
+      Cb_free_bytes( &eb->renc.cb ) < max_marker_size + eb->renc.ff_count + Lt_size )
+    return;
+  eb->member_finished = true;
+  Re_encode_bit( &eb->renc, &eb->bm_match[state][pos_state], 1 );
+  Re_encode_bit( &eb->renc, &eb->bm_rep[state], 0 );
+  LZeb_encode_pair( eb, 0xFFFFFFFFU, min_match_len, pos_state );
+  Re_flush( &eb->renc );
+  Lt_set_data_crc( trailer, LZeb_crc( eb ) );
+  Lt_set_data_size( trailer, Mb_data_position( &eb->mb ) );
+  Lt_set_member_size( trailer, Re_member_position( &eb->renc ) + Lt_size );
+  for( i = 0; i < Lt_size; ++i )
+    Cb_put_byte( &eb->renc.cb, trailer[i] );
+  }
+
+
+     /* Sync Flush mark => (dis == 0xFFFFFFFFU, len == min_match_len + 1) */
+static bool LZeb_sync_flush( struct LZ_encoder_base * const eb )
+  {
+  int i;
+  const int pos_state = Mb_data_position( &eb->mb ) & pos_state_mask;
+  const State state = eb->state;
+  if( eb->member_finished ||
+      Cb_free_bytes( &eb->renc.cb ) < (2 * max_marker_size) + eb->renc.ff_count )
+    return false;
+  for( i = 0; i < 2; ++i )	/* 2 consecutive markers guarantee decoding */
+    {
+    Re_encode_bit( &eb->renc, &eb->bm_match[state][pos_state], 1 );
+    Re_encode_bit( &eb->renc, &eb->bm_rep[state], 0 );
+    LZeb_encode_pair( eb, 0xFFFFFFFFU, min_match_len + 1, pos_state );
+    Re_flush( &eb->renc );
+    }
+  return true;
+  }
+
+
+static void LZeb_reset( struct LZ_encoder_base * const eb,
+                        const unsigned long long member_size )
+  {
+  int i;
+  Mb_reset( &eb->mb );
+  eb->member_size_limit =
+    min( member_size, 0x0008000000000000ULL ) - Lt_size - max_marker_size;
+  eb->crc = 0xFFFFFFFFU;
+  Bm_array_init( eb->bm_literal[0], (1 << literal_context_bits) * 0x300 );
+  Bm_array_init( eb->bm_match[0], states * pos_states );
+  Bm_array_init( eb->bm_rep, states );
+  Bm_array_init( eb->bm_rep0, states );
+  Bm_array_init( eb->bm_rep1, states );
+  Bm_array_init( eb->bm_rep2, states );
+  Bm_array_init( eb->bm_len[0], states * pos_states );
+  Bm_array_init( eb->bm_dis_slot[0], len_states * (1 << dis_slot_bits) );
+  Bm_array_init( eb->bm_dis, modeled_distances - end_dis_model + 1 );
+  Bm_array_init( eb->bm_align, dis_align_size );
+  Lm_init( &eb->match_len_model );
+  Lm_init( &eb->rep_len_model );
+  Re_reset( &eb->renc, eb->mb.dictionary_size );
+  for( i = 0; i < num_rep_distances; ++i ) eb->reps[i] = 0;
+  eb->state = 0;
+  eb->member_finished = false;
+  }
diff --git a/cbits/encoder_base.h b/cbits/encoder_base.h
new file mode 100644
--- /dev/null
+++ b/cbits/encoder_base.h
@@ -0,0 +1,615 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+enum { price_shift_bits = 6,
+       price_step_bits = 2 };
+
+static const uint8_t dis_slots[1<<10] =
+  {
+   0,  1,  2,  3,  4,  4,  5,  5,  6,  6,  6,  6,  7,  7,  7,  7,
+   8,  8,  8,  8,  8,  8,  8,  8,  9,  9,  9,  9,  9,  9,  9,  9,
+  10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
+  11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+  12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+  12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
+  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+  13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13,
+  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+  14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19,
+  19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19, 19 };
+
+static inline uint8_t get_slot( const unsigned dis )
+  {
+  if( dis < (1 << 10) ) return dis_slots[dis];
+  if( dis < (1 << 19) ) return dis_slots[dis>> 9] + 18;
+  if( dis < (1 << 28) ) return dis_slots[dis>>18] + 36;
+  return dis_slots[dis>>27] + 54;
+  }
+
+
+static const short prob_prices[bit_model_total >> price_step_bits] =
+{
+640, 539, 492, 461, 438, 419, 404, 390, 379, 369, 359, 351, 343, 336, 330, 323,
+318, 312, 307, 302, 298, 293, 289, 285, 281, 277, 274, 270, 267, 264, 261, 258,
+255, 252, 250, 247, 244, 242, 239, 237, 235, 232, 230, 228, 226, 224, 222, 220,
+218, 216, 214, 213, 211, 209, 207, 206, 204, 202, 201, 199, 198, 196, 195, 193,
+192, 190, 189, 188, 186, 185, 184, 182, 181, 180, 178, 177, 176, 175, 174, 172,
+171, 170, 169, 168, 167, 166, 165, 164, 163, 162, 161, 159, 158, 157, 157, 156,
+155, 154, 153, 152, 151, 150, 149, 148, 147, 146, 145, 145, 144, 143, 142, 141,
+140, 140, 139, 138, 137, 136, 136, 135, 134, 133, 133, 132, 131, 130, 130, 129,
+128, 127, 127, 126, 125, 125, 124, 123, 123, 122, 121, 121, 120, 119, 119, 118,
+117, 117, 116, 115, 115, 114, 114, 113, 112, 112, 111, 111, 110, 109, 109, 108,
+108, 107, 106, 106, 105, 105, 104, 104, 103, 103, 102, 101, 101, 100, 100,  99,
+ 99,  98,  98,  97,  97,  96,  96,  95,  95,  94,  94,  93,  93,  92,  92,  91,
+ 91,  90,  90,  89,  89,  88,  88,  88,  87,  87,  86,  86,  85,  85,  84,  84,
+ 83,  83,  83,  82,  82,  81,  81,  80,  80,  80,  79,  79,  78,  78,  77,  77,
+ 77,  76,  76,  75,  75,  75,  74,  74,  73,  73,  73,  72,  72,  71,  71,  71,
+ 70,  70,  70,  69,  69,  68,  68,  68,  67,  67,  67,  66,  66,  65,  65,  65,
+ 64,  64,  64,  63,  63,  63,  62,  62,  61,  61,  61,  60,  60,  60,  59,  59,
+ 59,  58,  58,  58,  57,  57,  57,  56,  56,  56,  55,  55,  55,  54,  54,  54,
+ 53,  53,  53,  53,  52,  52,  52,  51,  51,  51,  50,  50,  50,  49,  49,  49,
+ 48,  48,  48,  48,  47,  47,  47,  46,  46,  46,  45,  45,  45,  45,  44,  44,
+ 44,  43,  43,  43,  43,  42,  42,  42,  41,  41,  41,  41,  40,  40,  40,  40,
+ 39,  39,  39,  38,  38,  38,  38,  37,  37,  37,  37,  36,  36,  36,  35,  35,
+ 35,  35,  34,  34,  34,  34,  33,  33,  33,  33,  32,  32,  32,  32,  31,  31,
+ 31,  31,  30,  30,  30,  30,  29,  29,  29,  29,  28,  28,  28,  28,  27,  27,
+ 27,  27,  26,  26,  26,  26,  26,  25,  25,  25,  25,  24,  24,  24,  24,  23,
+ 23,  23,  23,  22,  22,  22,  22,  22,  21,  21,  21,  21,  20,  20,  20,  20,
+ 20,  19,  19,  19,  19,  18,  18,  18,  18,  18,  17,  17,  17,  17,  17,  16,
+ 16,  16,  16,  15,  15,  15,  15,  15,  14,  14,  14,  14,  14,  13,  13,  13,
+ 13,  13,  12,  12,  12,  12,  12,  11,  11,  11,  11,  10,  10,  10,  10,  10,
+  9,   9,   9,   9,   9,   9,   8,   8,   8,   8,   8,   7,   7,   7,   7,   7,
+  6,   6,   6,   6,   6,   5,   5,   5,   5,   5,   4,   4,   4,   4,   4,   4,
+  3,   3,   3,   3,   3,   2,   2,   2,   2,   2,   1,   1,   1,   1,   1,   1 };
+
+static inline int get_price( const int probability )
+  { return prob_prices[probability >> price_step_bits]; }
+
+
+static inline int price0( const Bit_model probability )
+  { return get_price( probability ); }
+
+static inline int price1( const Bit_model probability )
+  { return get_price( bit_model_total - probability ); }
+
+static inline int price_bit( const Bit_model bm, const bool bit )
+  { return ( bit ? price1( bm ) : price0( bm ) ); }
+
+
+static inline int price_symbol3( const Bit_model bm[], int symbol )
+  {
+  int price;
+  bool bit = symbol & 1;
+  symbol |= 8; symbol >>= 1;
+  price = price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  return price + price_bit( bm[1], symbol & 1 );
+  }
+
+
+static inline int price_symbol6( const Bit_model bm[], unsigned symbol )
+  {
+  int price;
+  bool bit = symbol & 1;
+  symbol |= 64; symbol >>= 1;
+  price = price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  return price + price_bit( bm[1], symbol & 1 );
+  }
+
+
+static inline int price_symbol8( const Bit_model bm[], int symbol )
+  {
+  int price;
+  bool bit = symbol & 1;
+  symbol |= 0x100; symbol >>= 1;
+  price = price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  bit = symbol & 1; symbol >>= 1; price += price_bit( bm[symbol], bit );
+  return price + price_bit( bm[1], symbol & 1 );
+  }
+
+
+static inline int price_symbol_reversed( const Bit_model bm[], int symbol,
+                                         const int num_bits )
+  {
+  int price = 0;
+  int model = 1;
+  int i;
+  for( i = num_bits; i > 0; --i )
+    {
+    const bool bit = symbol & 1;
+    symbol >>= 1;
+    price += price_bit( bm[model], bit );
+    model = ( model << 1 ) | bit;
+    }
+  return price;
+  }
+
+
+static inline int price_matched( const Bit_model bm[], unsigned symbol,
+                                 unsigned match_byte )
+  {
+  int price = 0;
+  unsigned mask = 0x100;
+  symbol |= mask;
+  while( true )
+    {
+    const unsigned match_bit = ( match_byte <<= 1 ) & mask;
+    const bool bit = ( symbol <<= 1 ) & 0x100;
+    price += price_bit( bm[(symbol>>9)+match_bit+mask], bit );
+    if( symbol >= 0x10000 ) return price;
+    mask &= ~(match_bit ^ symbol);	/* if( match_bit != bit ) mask = 0; */
+    }
+  }
+
+
+struct Matchfinder_base
+  {
+  unsigned long long partial_data_pos;
+  uint8_t * buffer;		/* input buffer */
+  int32_t * prev_positions;	/* 1 + last seen position of key. else 0 */
+  int32_t * pos_array;		/* may be tree or chain */
+  int before_size;		/* bytes to keep in buffer before dictionary */
+  int after_size;		/* bytes to keep in buffer after pos */
+  int buffer_size;
+  int dictionary_size;		/* bytes to keep in buffer before pos */
+  int pos;			/* current pos in buffer */
+  int cyclic_pos;		/* cycles through [0, dictionary_size] */
+  int stream_pos;		/* first byte not yet read from file */
+  int pos_limit;		/* when reached, a new block must be read */
+  int key4_mask;
+  int num_prev_positions23;
+  int num_prev_positions;	/* size of prev_positions */
+  int pos_array_size;
+  int saved_dictionary_size;	/* dictionary_size restored by Mb_reset */
+  bool at_stream_end;		/* stream_pos shows real end of file */
+  bool flushing;
+  };
+
+static bool Mb_normalize_pos( struct Matchfinder_base * const mb );
+
+static bool Mb_init( struct Matchfinder_base * const mb, const int before_size,
+                     const int dict_size, const int after_size,
+                     const int dict_factor, const int num_prev_positions23,
+                     const int pos_array_factor );
+
+static inline void Mb_free( struct Matchfinder_base * const mb )
+  { free( mb->prev_positions ); free( mb->buffer ); }
+
+static inline uint8_t Mb_peek( const struct Matchfinder_base * const mb,
+                               const int distance )
+  { return mb->buffer[mb->pos-distance]; }
+
+static inline int Mb_available_bytes( const struct Matchfinder_base * const mb )
+  { return mb->stream_pos - mb->pos; }
+
+static inline unsigned long long
+Mb_data_position( const struct Matchfinder_base * const mb )
+  { return mb->partial_data_pos + mb->pos; }
+
+static inline void Mb_finish( struct Matchfinder_base * const mb )
+  { mb->at_stream_end = true; mb->flushing = false; }
+
+static inline bool Mb_data_finished( const struct Matchfinder_base * const mb )
+  { return mb->at_stream_end && !mb->flushing && mb->pos >= mb->stream_pos; }
+
+static inline bool Mb_flushing_or_end( const struct Matchfinder_base * const mb )
+  { return mb->at_stream_end || mb->flushing; }
+
+static inline int Mb_free_bytes( const struct Matchfinder_base * const mb )
+  { if( Mb_flushing_or_end( mb ) ) return 0;
+    return mb->buffer_size - mb->stream_pos; }
+
+static inline bool Mb_enough_available_bytes( const struct Matchfinder_base * const mb )
+  { return ( mb->pos + mb->after_size <= mb->stream_pos ||
+             ( Mb_flushing_or_end( mb ) && mb->pos < mb->stream_pos ) ); }
+
+static inline const uint8_t *
+Mb_ptr_to_current_pos( const struct Matchfinder_base * const mb )
+  { return mb->buffer + mb->pos; }
+
+static int Mb_write_data( struct Matchfinder_base * const mb,
+                          const uint8_t * const inbuf, const int size )
+  {
+  const int sz = min( mb->buffer_size - mb->stream_pos, size );
+  if( Mb_flushing_or_end( mb ) || sz <= 0 ) return 0;
+  memcpy( mb->buffer + mb->stream_pos, inbuf, sz );
+  mb->stream_pos += sz;
+  return sz;
+  }
+
+static inline int Mb_true_match_len( const struct Matchfinder_base * const mb,
+                                     const int index, const int distance )
+  {
+  const uint8_t * const data = mb->buffer + mb->pos;
+  int i = index;
+  const int len_limit = min( Mb_available_bytes( mb ), max_match_len );
+  while( i < len_limit && data[i-distance] == data[i] ) ++i;
+  return i;
+  }
+
+static inline bool Mb_move_pos( struct Matchfinder_base * const mb )
+  {
+  if( ++mb->cyclic_pos > mb->dictionary_size ) mb->cyclic_pos = 0;
+  if( ++mb->pos >= mb->pos_limit ) return Mb_normalize_pos( mb );
+  return true;
+  }
+
+
+struct Range_encoder
+  {
+  struct Circular_buffer cb;
+  unsigned min_free_bytes;
+  uint64_t low;
+  unsigned long long partial_member_pos;
+  uint32_t range;
+  unsigned ff_count;
+  uint8_t cache;
+  Lzip_header header;
+  };
+
+static inline void Re_shift_low( struct Range_encoder * const renc )
+  {
+  if( renc->low >> 24 != 0xFF )
+    {
+    const bool carry = ( renc->low > 0xFFFFFFFFU );
+    Cb_put_byte( &renc->cb, renc->cache + carry );
+    for( ; renc->ff_count > 0; --renc->ff_count )
+      Cb_put_byte( &renc->cb, 0xFF + carry );
+    renc->cache = renc->low >> 24;
+    }
+  else ++renc->ff_count;
+  renc->low = ( renc->low & 0x00FFFFFFU ) << 8;
+  }
+
+static inline void Re_reset( struct Range_encoder * const renc,
+                             const unsigned dictionary_size )
+  {
+  int i;
+  Cb_reset( &renc->cb );
+  renc->low = 0;
+  renc->partial_member_pos = 0;
+  renc->range = 0xFFFFFFFFU;
+  renc->ff_count = 0;
+  renc->cache = 0;
+  Lh_set_dictionary_size( renc->header, dictionary_size );
+  for( i = 0; i < Lh_size; ++i )
+    Cb_put_byte( &renc->cb, renc->header[i] );
+  }
+
+static inline bool Re_init( struct Range_encoder * const renc,
+                            const unsigned dictionary_size,
+                            const unsigned min_free_bytes )
+  {
+  if( !Cb_init( &renc->cb, 65536 + min_free_bytes ) ) return false;
+  renc->min_free_bytes = min_free_bytes;
+  Lh_set_magic( renc->header );
+  Re_reset( renc, dictionary_size );
+  return true;
+  }
+
+static inline void Re_free( struct Range_encoder * const renc )
+  { Cb_free( &renc->cb ); }
+
+static inline unsigned long long
+Re_member_position( const struct Range_encoder * const renc )
+  { return renc->partial_member_pos + Cb_used_bytes( &renc->cb ) + renc->ff_count; }
+
+static inline bool Re_enough_free_bytes( const struct Range_encoder * const renc )
+  { return Cb_free_bytes( &renc->cb ) >= renc->min_free_bytes + renc->ff_count; }
+
+static inline int Re_read_data( struct Range_encoder * const renc,
+                                uint8_t * const out_buffer, const int out_size )
+  {
+  const int size = Cb_read_data( &renc->cb, out_buffer, out_size );
+  if( size > 0 ) renc->partial_member_pos += size;
+  return size;
+  }
+
+static inline void Re_flush( struct Range_encoder * const renc )
+  {
+  int i; for( i = 0; i < 5; ++i ) Re_shift_low( renc );
+  renc->low = 0;
+  renc->range = 0xFFFFFFFFU;
+  renc->ff_count = 0;
+  renc->cache = 0;
+  }
+
+static inline void Re_encode( struct Range_encoder * const renc,
+                              const int symbol, const int num_bits )
+  {
+  unsigned mask;
+  for( mask = 1 << ( num_bits - 1 ); mask > 0; mask >>= 1 )
+    {
+    renc->range >>= 1;
+    if( symbol & mask ) renc->low += renc->range;
+    if( renc->range <= 0x00FFFFFFU )
+      { renc->range <<= 8; Re_shift_low( renc ); }
+    }
+  }
+
+static inline void Re_encode_bit( struct Range_encoder * const renc,
+                                  Bit_model * const probability, const bool bit )
+  {
+  const uint32_t bound = ( renc->range >> bit_model_total_bits ) * *probability;
+  if( !bit )
+    {
+    renc->range = bound;
+    *probability += (bit_model_total - *probability) >> bit_model_move_bits;
+    }
+  else
+    {
+    renc->low += bound;
+    renc->range -= bound;
+    *probability -= *probability >> bit_model_move_bits;
+    }
+  if( renc->range <= 0x00FFFFFFU ) { renc->range <<= 8; Re_shift_low( renc ); }
+  }
+
+static inline void Re_encode_tree3( struct Range_encoder * const renc,
+                                    Bit_model bm[], const int symbol )
+  {
+  int model = 1;
+  bool bit = ( symbol >> 2 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  bit = ( symbol >> 1 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  Re_encode_bit( renc, &bm[model], symbol & 1 );
+  }
+
+static inline void Re_encode_tree6( struct Range_encoder * const renc,
+                                    Bit_model bm[], const unsigned symbol )
+  {
+  int model = 1;
+  bool bit = ( symbol >> 5 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  bit = ( symbol >> 4 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  bit = ( symbol >> 3 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  bit = ( symbol >> 2 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  bit = ( symbol >> 1 ) & 1;
+  Re_encode_bit( renc, &bm[model], bit ); model = ( model << 1 ) | bit;
+  Re_encode_bit( renc, &bm[model], symbol & 1 );
+  }
+
+static inline void Re_encode_tree8( struct Range_encoder * const renc,
+                                    Bit_model bm[], const int symbol )
+  {
+  int model = 1;
+  int i;
+  for( i = 7; i >= 0; --i )
+    {
+    const bool bit = ( symbol >> i ) & 1;
+    Re_encode_bit( renc, &bm[model], bit );
+    model = ( model << 1 ) | bit;
+    }
+  }
+
+static inline void Re_encode_tree_reversed( struct Range_encoder * const renc,
+                     Bit_model bm[], int symbol, const int num_bits )
+  {
+  int model = 1;
+  int i;
+  for( i = num_bits; i > 0; --i )
+    {
+    const bool bit = symbol & 1;
+    symbol >>= 1;
+    Re_encode_bit( renc, &bm[model], bit );
+    model = ( model << 1 ) | bit;
+    }
+  }
+
+static inline void Re_encode_matched( struct Range_encoder * const renc,
+                                      Bit_model bm[], unsigned symbol,
+                                      unsigned match_byte )
+  {
+  unsigned mask = 0x100;
+  symbol |= mask;
+  while( true )
+    {
+    const unsigned match_bit = ( match_byte <<= 1 ) & mask;
+    const bool bit = ( symbol <<= 1 ) & 0x100;
+    Re_encode_bit( renc, &bm[(symbol>>9)+match_bit+mask], bit );
+    if( symbol >= 0x10000 ) break;
+    mask &= ~(match_bit ^ symbol);	/* if( match_bit != bit ) mask = 0; */
+    }
+  }
+
+static inline void Re_encode_len( struct Range_encoder * const renc,
+                                  struct Len_model * const lm,
+                                  int symbol, const int pos_state )
+  {
+  bool bit = ( ( symbol -= min_match_len ) >= len_low_symbols );
+  Re_encode_bit( renc, &lm->choice1, bit );
+  if( !bit )
+    Re_encode_tree3( renc, lm->bm_low[pos_state], symbol );
+  else
+    {
+    bit = ( ( symbol -= len_low_symbols ) >= len_mid_symbols );
+    Re_encode_bit( renc, &lm->choice2, bit );
+    if( !bit )
+      Re_encode_tree3( renc, lm->bm_mid[pos_state], symbol );
+    else
+      Re_encode_tree8( renc, lm->bm_high, symbol - len_mid_symbols );
+    }
+  }
+
+
+enum { max_marker_size = 16,
+       num_rep_distances = 4 };		/* must be 4 */
+
+struct LZ_encoder_base
+  {
+  struct Matchfinder_base mb;
+  unsigned long long member_size_limit;
+  uint32_t crc;
+
+  Bit_model bm_literal[1<<literal_context_bits][0x300];
+  Bit_model bm_match[states][pos_states];
+  Bit_model bm_rep[states];
+  Bit_model bm_rep0[states];
+  Bit_model bm_rep1[states];
+  Bit_model bm_rep2[states];
+  Bit_model bm_len[states][pos_states];
+  Bit_model bm_dis_slot[len_states][1<<dis_slot_bits];
+  Bit_model bm_dis[modeled_distances-end_dis_model+1];
+  Bit_model bm_align[dis_align_size];
+  struct Len_model match_len_model;
+  struct Len_model rep_len_model;
+  struct Range_encoder renc;
+  int reps[num_rep_distances];
+  State state;
+  bool member_finished;
+  };
+
+static void LZeb_reset( struct LZ_encoder_base * const eb,
+                        const unsigned long long member_size );
+
+static inline bool LZeb_init( struct LZ_encoder_base * const eb,
+                              const int before_size, const int dict_size,
+                              const int after_size, const int dict_factor,
+                              const int num_prev_positions23,
+                              const int pos_array_factor,
+                              const unsigned min_free_bytes,
+                              const unsigned long long member_size )
+  {
+  if( !Mb_init( &eb->mb, before_size, dict_size, after_size, dict_factor,
+                num_prev_positions23, pos_array_factor ) ) return false;
+  if( !Re_init( &eb->renc, eb->mb.dictionary_size, min_free_bytes ) )
+    return false;
+  LZeb_reset( eb, member_size );
+  return true;
+  }
+
+static inline bool LZeb_member_finished( const struct LZ_encoder_base * const eb )
+  { return ( eb->member_finished && !Cb_used_bytes( &eb->renc.cb ) ); }
+
+static inline void LZeb_free( struct LZ_encoder_base * const eb )
+  { Re_free( &eb->renc ); Mb_free( &eb->mb ); }
+
+static inline unsigned LZeb_crc( const struct LZ_encoder_base * const eb )
+  { return eb->crc ^ 0xFFFFFFFFU; }
+
+static inline int LZeb_price_literal( const struct LZ_encoder_base * const eb,
+                            const uint8_t prev_byte, const uint8_t symbol )
+  { return price_symbol8( eb->bm_literal[get_lit_state(prev_byte)], symbol ); }
+
+static inline int LZeb_price_matched( const struct LZ_encoder_base * const eb,
+  const uint8_t prev_byte, const uint8_t symbol, const uint8_t match_byte )
+  { return price_matched( eb->bm_literal[get_lit_state(prev_byte)], symbol,
+                          match_byte ); }
+
+static inline void LZeb_encode_literal( struct LZ_encoder_base * const eb,
+                            const uint8_t prev_byte, const uint8_t symbol )
+  { Re_encode_tree8( &eb->renc, eb->bm_literal[get_lit_state(prev_byte)],
+                     symbol ); }
+
+static inline void LZeb_encode_matched( struct LZ_encoder_base * const eb,
+  const uint8_t prev_byte, const uint8_t symbol, const uint8_t match_byte )
+  { Re_encode_matched( &eb->renc, eb->bm_literal[get_lit_state(prev_byte)],
+                       symbol, match_byte ); }
+
+static inline void LZeb_encode_pair( struct LZ_encoder_base * const eb,
+                                     const unsigned dis, const int len,
+                                     const int pos_state )
+  {
+  const unsigned dis_slot = get_slot( dis );
+  Re_encode_len( &eb->renc, &eb->match_len_model, len, pos_state );
+  Re_encode_tree6( &eb->renc, eb->bm_dis_slot[get_len_state(len)], dis_slot );
+
+  if( dis_slot >= start_dis_model )
+    {
+    const int direct_bits = ( dis_slot >> 1 ) - 1;
+    const unsigned base = ( 2 | ( dis_slot & 1 ) ) << direct_bits;
+    const unsigned direct_dis = dis - base;
+
+    if( dis_slot < end_dis_model )
+      Re_encode_tree_reversed( &eb->renc, eb->bm_dis + ( base - dis_slot ),
+                               direct_dis, direct_bits );
+    else
+      {
+      Re_encode( &eb->renc, direct_dis >> dis_align_bits,
+                 direct_bits - dis_align_bits );
+      Re_encode_tree_reversed( &eb->renc, eb->bm_align, direct_dis, dis_align_bits );
+      }
+    }
+  }
diff --git a/cbits/fast_encoder.c b/cbits/fast_encoder.c
new file mode 100644
--- /dev/null
+++ b/cbits/fast_encoder.c
@@ -0,0 +1,182 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+int FLZe_longest_match_len( struct FLZ_encoder * const fe, int * const distance )
+  {
+  enum { len_limit = 16 };
+  const uint8_t * const data = Mb_ptr_to_current_pos( &fe->eb.mb );
+  int32_t * ptr0 = fe->eb.mb.pos_array + fe->eb.mb.cyclic_pos;
+  const int pos1 = fe->eb.mb.pos + 1;
+  int maxlen = 0, newpos1, count;
+  const int available = min( Mb_available_bytes( &fe->eb.mb ), max_match_len );
+  if( available < len_limit ) { *ptr0 = 0; return 0; }
+
+  fe->key4 = ( ( fe->key4 << 4 ) ^ data[3] ) & fe->eb.mb.key4_mask;
+  newpos1 = fe->eb.mb.prev_positions[fe->key4];
+  fe->eb.mb.prev_positions[fe->key4] = pos1;
+
+  for( count = 4; ; )
+    {
+    int32_t * newptr;
+    int delta;
+    if( newpos1 <= 0 || --count < 0 ||
+        ( delta = pos1 - newpos1 ) > fe->eb.mb.dictionary_size )
+      { *ptr0 = 0; break; }
+    newptr = fe->eb.mb.pos_array +
+      ( fe->eb.mb.cyclic_pos - delta +
+        ( ( fe->eb.mb.cyclic_pos >= delta ) ? 0 : fe->eb.mb.dictionary_size + 1 ) );
+
+    if( data[maxlen-delta] == data[maxlen] )
+      {
+      int len = 0;
+      while( len < available && data[len-delta] == data[len] ) ++len;
+      if( maxlen < len )
+        { maxlen = len; *distance = delta - 1;
+          if( maxlen >= len_limit ) { *ptr0 = *newptr; break; } }
+      }
+
+    *ptr0 = newpos1;
+    ptr0 = newptr;
+    newpos1 = *ptr0;
+    }
+  return maxlen;
+  }
+
+
+bool FLZe_encode_member( struct FLZ_encoder * const fe )
+  {
+  int rep = 0, i;
+  State * const state = &fe->eb.state;
+
+  if( fe->eb.member_finished ) return true;
+  if( Re_member_position( &fe->eb.renc ) >= fe->eb.member_size_limit )
+    { LZeb_try_full_flush( &fe->eb ); return true; }
+
+  if( Mb_data_position( &fe->eb.mb ) == 0 &&
+      !Mb_data_finished( &fe->eb.mb ) )		/* encode first byte */
+    {
+    const uint8_t prev_byte = 0;
+    uint8_t cur_byte;
+    if( !Mb_enough_available_bytes( &fe->eb.mb ) ||
+        !Re_enough_free_bytes( &fe->eb.renc ) ) return true;
+    cur_byte = Mb_peek( &fe->eb.mb, 0 );
+    Re_encode_bit( &fe->eb.renc, &fe->eb.bm_match[*state][0], 0 );
+    LZeb_encode_literal( &fe->eb, prev_byte, cur_byte );
+    CRC32_update_byte( &fe->eb.crc, cur_byte );
+    FLZe_reset_key4( fe );
+    if( !FLZe_update_and_move( fe, 1 ) ) return false;
+    }
+
+  while( !Mb_data_finished( &fe->eb.mb ) &&
+         Re_member_position( &fe->eb.renc ) < fe->eb.member_size_limit )
+    {
+    int match_distance;
+    int main_len, pos_state;
+    int len = 0;
+    if( !Mb_enough_available_bytes( &fe->eb.mb ) ||
+        !Re_enough_free_bytes( &fe->eb.renc ) ) return true;
+    main_len = FLZe_longest_match_len( fe, &match_distance );
+    pos_state = Mb_data_position( &fe->eb.mb ) & pos_state_mask;
+
+    for( i = 0; i < num_rep_distances; ++i )
+      {
+      const int tlen = Mb_true_match_len( &fe->eb.mb, 0, fe->eb.reps[i] + 1 );
+      if( tlen > len ) { len = tlen; rep = i; }
+      }
+    if( len > min_match_len && len + 3 > main_len )
+      {
+      CRC32_update_buf( &fe->eb.crc, Mb_ptr_to_current_pos( &fe->eb.mb ), len );
+      Re_encode_bit( &fe->eb.renc, &fe->eb.bm_match[*state][pos_state], 1 );
+      Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep[*state], 1 );
+      Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep0[*state], rep != 0 );
+      if( rep == 0 )
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_len[*state][pos_state], 1 );
+      else
+        {
+        int distance;
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep1[*state], rep > 1 );
+        if( rep > 1 )
+          Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep2[*state], rep > 2 );
+        distance = fe->eb.reps[rep];
+        for( i = rep; i > 0; --i ) fe->eb.reps[i] = fe->eb.reps[i-1];
+        fe->eb.reps[0] = distance;
+        }
+      *state = St_set_rep( *state );
+      Re_encode_len( &fe->eb.renc, &fe->eb.rep_len_model, len, pos_state );
+      if( !Mb_move_pos( &fe->eb.mb ) ) return false;
+      if( !FLZe_update_and_move( fe, len - 1 ) ) return false;
+      continue;
+      }
+
+    if( main_len > min_match_len )
+      {
+      CRC32_update_buf( &fe->eb.crc, Mb_ptr_to_current_pos( &fe->eb.mb ), main_len );
+      Re_encode_bit( &fe->eb.renc, &fe->eb.bm_match[*state][pos_state], 1 );
+      Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep[*state], 0 );
+      *state = St_set_match( *state );
+      for( i = num_rep_distances - 1; i > 0; --i ) fe->eb.reps[i] = fe->eb.reps[i-1];
+      fe->eb.reps[0] = match_distance;
+      LZeb_encode_pair( &fe->eb, match_distance, main_len, pos_state );
+      if( !Mb_move_pos( &fe->eb.mb ) ) return false;
+      if( !FLZe_update_and_move( fe, main_len - 1 ) ) return false;
+      continue;
+      }
+
+    {
+    const uint8_t prev_byte = Mb_peek( &fe->eb.mb, 1 );
+    const uint8_t cur_byte = Mb_peek( &fe->eb.mb, 0 );
+    const uint8_t match_byte = Mb_peek( &fe->eb.mb, fe->eb.reps[0] + 1 );
+    if( !Mb_move_pos( &fe->eb.mb ) ) return false;
+    CRC32_update_byte( &fe->eb.crc, cur_byte );
+
+    if( match_byte == cur_byte )
+      {
+      const int short_rep_price = price1( fe->eb.bm_match[*state][pos_state] ) +
+                                  price1( fe->eb.bm_rep[*state] ) +
+                                  price0( fe->eb.bm_rep0[*state] ) +
+                                  price0( fe->eb.bm_len[*state][pos_state] );
+      int price = price0( fe->eb.bm_match[*state][pos_state] );
+      if( St_is_char( *state ) )
+        price += LZeb_price_literal( &fe->eb, prev_byte, cur_byte );
+      else
+        price += LZeb_price_matched( &fe->eb, prev_byte, cur_byte, match_byte );
+      if( short_rep_price < price )
+        {
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_match[*state][pos_state], 1 );
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep[*state], 1 );
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_rep0[*state], 0 );
+        Re_encode_bit( &fe->eb.renc, &fe->eb.bm_len[*state][pos_state], 0 );
+        *state = St_set_short_rep( *state );
+        continue;
+        }
+      }
+
+    /* literal byte */
+    Re_encode_bit( &fe->eb.renc, &fe->eb.bm_match[*state][pos_state], 0 );
+    if( St_is_char( *state ) )
+      LZeb_encode_literal( &fe->eb, prev_byte, cur_byte );
+    else
+      LZeb_encode_matched( &fe->eb, prev_byte, cur_byte, match_byte );
+    *state = St_set_char( *state );
+    }
+    }
+
+  LZeb_try_full_flush( &fe->eb );
+  return true;
+  }
diff --git a/cbits/fast_encoder.h b/cbits/fast_encoder.h
new file mode 100644
--- /dev/null
+++ b/cbits/fast_encoder.h
@@ -0,0 +1,72 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+struct FLZ_encoder
+  {
+  struct LZ_encoder_base eb;
+  unsigned key4;			/* key made from latest 4 bytes */
+  };
+
+static inline void FLZe_reset_key4( struct FLZ_encoder * const fe )
+  {
+  int i;
+  fe->key4 = 0;
+  for( i = 0; i < 3 && i < Mb_available_bytes( &fe->eb.mb ); ++i )
+    fe->key4 = ( fe->key4 << 4 ) ^ fe->eb.mb.buffer[i];
+  }
+
+int FLZe_longest_match_len( struct FLZ_encoder * const fe, int * const distance );
+
+static inline bool FLZe_update_and_move( struct FLZ_encoder * const fe, int n )
+  {
+  while( --n >= 0 )
+    {
+    if( Mb_available_bytes( &fe->eb.mb ) >= 4 )
+      {
+      fe->key4 = ( ( fe->key4 << 4 ) ^ fe->eb.mb.buffer[fe->eb.mb.pos+3] ) &
+                 fe->eb.mb.key4_mask;
+      fe->eb.mb.pos_array[fe->eb.mb.cyclic_pos] = fe->eb.mb.prev_positions[fe->key4];
+      fe->eb.mb.prev_positions[fe->key4] = fe->eb.mb.pos + 1;
+      }
+    else fe->eb.mb.pos_array[fe->eb.mb.cyclic_pos] = 0;
+    if( !Mb_move_pos( &fe->eb.mb ) ) return false;
+    }
+  return true;
+  }
+
+static inline bool FLZe_init( struct FLZ_encoder * const fe,
+                              const unsigned long long member_size )
+  {
+  enum { before_size = 0,
+         dict_size = 65536,
+         /* bytes to keep in buffer after pos */
+         after_size = max_match_len,
+         dict_factor = 16,
+         min_free_bytes = max_marker_size,
+         num_prev_positions23 = 0,
+         pos_array_factor = 1 };
+
+  return LZeb_init( &fe->eb, before_size, dict_size, after_size, dict_factor,
+                    num_prev_positions23, pos_array_factor, min_free_bytes,
+                    member_size );
+  }
+
+static inline void FLZe_reset( struct FLZ_encoder * const fe,
+                               const unsigned long long member_size )
+  { LZeb_reset( &fe->eb, member_size ); }
diff --git a/cbits/lzip.h b/cbits/lzip.h
new file mode 100644
--- /dev/null
+++ b/cbits/lzip.h
@@ -0,0 +1,298 @@
+/*  Lzlib - Compression library for the lzip format
+    Copyright (C) 2009-2019 Antonio Diaz Diaz.
+
+    This library is free software. Redistribution and use in source and
+    binary forms, with or without modification, are permitted provided
+    that the following conditions are met:
+
+    1. Redistributions of source code must retain the above copyright
+    notice, this list of conditions and the following disclaimer.
+
+    2. Redistributions in binary form must reproduce the above copyright
+    notice, this list of conditions and the following disclaimer in the
+    documentation and/or other materials provided with the distribution.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+*/
+
+#ifndef max
+  #define max(x,y) ((x) >= (y) ? (x) : (y))
+#endif
+#ifndef min
+  #define min(x,y) ((x) <= (y) ? (x) : (y))
+#endif
+
+typedef int State;
+
+enum { states = 12 };
+
+static inline bool St_is_char( const State st ) { return st < 7; }
+
+static inline State St_set_char( const State st )
+  {
+  static const State next[states] = { 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5 };
+  return next[st];
+  }
+
+static inline State St_set_char_rep() { return 8; }
+
+static inline State St_set_match( const State st )
+  { return ( ( st < 7 ) ? 7 : 10 ); }
+
+static inline State St_set_rep( const State st )
+  { return ( ( st < 7 ) ? 8 : 11 ); }
+
+static inline State St_set_short_rep( const State st )
+  { return ( ( st < 7 ) ? 9 : 11 ); }
+
+
+enum {
+  min_dictionary_bits = 12,
+  min_dictionary_size = 1 << min_dictionary_bits,	/* >= modeled_distances */
+  max_dictionary_bits = 29,
+  max_dictionary_size = 1 << max_dictionary_bits,
+  literal_context_bits = 3,
+  literal_pos_state_bits = 0,				/* not used */
+  pos_state_bits = 2,
+  pos_states = 1 << pos_state_bits,
+  pos_state_mask = pos_states - 1,
+
+  len_states = 4,
+  dis_slot_bits = 6,
+  start_dis_model = 4,
+  end_dis_model = 14,
+  modeled_distances = 1 << (end_dis_model / 2),		/* 128 */
+  dis_align_bits = 4,
+  dis_align_size = 1 << dis_align_bits,
+
+  len_low_bits = 3,
+  len_mid_bits = 3,
+  len_high_bits = 8,
+  len_low_symbols = 1 << len_low_bits,
+  len_mid_symbols = 1 << len_mid_bits,
+  len_high_symbols = 1 << len_high_bits,
+  max_len_symbols = len_low_symbols + len_mid_symbols + len_high_symbols,
+
+  min_match_len = 2,					/* must be 2 */
+  max_match_len = min_match_len + max_len_symbols - 1,	/* 273 */
+  min_match_len_limit = 5 };
+
+static inline int get_len_state( const int len )
+  { return min( len - min_match_len, len_states - 1 ); }
+
+static inline int get_lit_state( const uint8_t prev_byte )
+  { return ( prev_byte >> ( 8 - literal_context_bits ) ); }
+
+
+enum { bit_model_move_bits = 5,
+       bit_model_total_bits = 11,
+       bit_model_total = 1 << bit_model_total_bits };
+
+typedef int Bit_model;
+
+static inline void Bm_init( Bit_model * const probability )
+  { *probability = bit_model_total / 2; }
+
+static inline void Bm_array_init( Bit_model bm[], const int size )
+  { int i; for( i = 0; i < size; ++i ) Bm_init( &bm[i] ); }
+
+struct Len_model
+  {
+  Bit_model choice1;
+  Bit_model choice2;
+  Bit_model bm_low[pos_states][len_low_symbols];
+  Bit_model bm_mid[pos_states][len_mid_symbols];
+  Bit_model bm_high[len_high_symbols];
+  };
+
+static inline void Lm_init( struct Len_model * const lm )
+  {
+  Bm_init( &lm->choice1 );
+  Bm_init( &lm->choice2 );
+  Bm_array_init( lm->bm_low[0], pos_states * len_low_symbols );
+  Bm_array_init( lm->bm_mid[0], pos_states * len_mid_symbols );
+  Bm_array_init( lm->bm_high, len_high_symbols );
+  }
+
+
+/* Table of CRCs of all 8-bit messages. */
+static const uint32_t crc32[256] =
+  {
+  0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F,
+  0xE963A535, 0x9E6495A3, 0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988,
+  0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 0x1DB71064, 0x6AB020F2,
+  0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7,
+  0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9,
+  0xFA0F3D63, 0x8D080DF5, 0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172,
+  0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 0x35B5A8FA, 0x42B2986C,
+  0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59,
+  0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423,
+  0xCFBA9599, 0xB8BDA50F, 0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924,
+  0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 0x76DC4190, 0x01DB7106,
+  0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433,
+  0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D,
+  0x91646C97, 0xE6635C01, 0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E,
+  0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 0x65B0D9C6, 0x12B7E950,
+  0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65,
+  0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7,
+  0xA4D1C46D, 0xD3D6F4FB, 0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0,
+  0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 0x5005713C, 0x270241AA,
+  0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F,
+  0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81,
+  0xB7BD5C3B, 0xC0BA6CAD, 0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A,
+  0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 0xE3630B12, 0x94643B84,
+  0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1,
+  0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB,
+  0x196C3671, 0x6E6B06E7, 0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC,
+  0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 0xD6D6A3E8, 0xA1D1937E,
+  0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B,
+  0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55,
+  0x316E8EEF, 0x4669BE79, 0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236,
+  0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 0xC5BA3BBE, 0xB2BD0B28,
+  0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D,
+  0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F,
+  0x72076785, 0x05005713, 0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38,
+  0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 0x86D3D2D4, 0xF1D4E242,
+  0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777,
+  0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69,
+  0x616BFFD3, 0x166CCF45, 0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2,
+  0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 0xAED16A4A, 0xD9D65ADC,
+  0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9,
+  0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693,
+  0x54DE5729, 0x23D967BF, 0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94,
+  0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D };
+
+
+static inline void CRC32_update_byte( uint32_t * const crc, const uint8_t byte )
+  { *crc = crc32[(*crc^byte)&0xFF] ^ ( *crc >> 8 ); }
+
+static inline void CRC32_update_buf( uint32_t * const crc,
+                                     const uint8_t * const buffer,
+                                     const int size )
+  {
+  int i;
+  uint32_t c = *crc;
+  for( i = 0; i < size; ++i )
+    c = crc32[(c^buffer[i])&0xFF] ^ ( c >> 8 );
+  *crc = c;
+  }
+
+
+static inline bool isvalid_ds( const unsigned dictionary_size )
+  { return ( dictionary_size >= min_dictionary_size &&
+             dictionary_size <= max_dictionary_size ); }
+
+
+static inline int real_bits( unsigned value )
+  {
+  int bits = 0;
+  while( value > 0 ) { value >>= 1; ++bits; }
+  return bits;
+  }
+
+
+static const uint8_t lzip_magic[4] = { 0x4C, 0x5A, 0x49, 0x50 }; /* "LZIP" */
+
+typedef uint8_t Lzip_header[6];		/* 0-3 magic bytes */
+					/*   4 version */
+					/*   5 coded_dict_size */
+enum { Lh_size = 6 };
+
+static inline void Lh_set_magic( Lzip_header data )
+  { memcpy( data, lzip_magic, 4 ); data[4] = 1; }
+
+static inline bool Lh_verify_magic( const Lzip_header data )
+  { return ( memcmp( data, lzip_magic, 4 ) == 0 ); }
+
+/* detect (truncated) header */
+static inline bool Lh_verify_prefix( const Lzip_header data, const int sz )
+  {
+  int i; for( i = 0; i < sz && i < 4; ++i )
+    if( data[i] != lzip_magic[i] ) return false;
+  return ( sz > 0 );
+  }
+
+/* detect corrupt header */
+static inline bool Lh_verify_corrupt( const Lzip_header data )
+  {
+  int matches = 0;
+  int i; for( i = 0; i < 4; ++i )
+    if( data[i] == lzip_magic[i] ) ++matches;
+  return ( matches > 1 && matches < 4 );
+  }
+
+static inline uint8_t Lh_version( const Lzip_header data )
+  { return data[4]; }
+
+static inline bool Lh_verify_version( const Lzip_header data )
+  { return ( data[4] == 1 ); }
+
+static inline unsigned Lh_get_dictionary_size( const Lzip_header data )
+  {
+  unsigned sz = ( 1 << ( data[5] & 0x1F ) );
+  if( sz > min_dictionary_size )
+    sz -= ( sz / 16 ) * ( ( data[5] >> 5 ) & 7 );
+  return sz;
+  }
+
+static inline bool Lh_set_dictionary_size( Lzip_header data, const unsigned sz )
+  {
+  if( !isvalid_ds( sz ) ) return false;
+  data[5] = real_bits( sz - 1 );
+  if( sz > min_dictionary_size )
+    {
+    const unsigned base_size = 1 << data[5];
+    const unsigned fraction = base_size / 16;
+    unsigned i;
+    for( i = 7; i >= 1; --i )
+      if( base_size - ( i * fraction ) >= sz )
+        { data[5] |= ( i << 5 ); break; }
+    }
+  return true;
+  }
+
+static inline bool Lh_verify( const Lzip_header data )
+  {
+  if( Lh_verify_magic( data ) && Lh_verify_version( data ) )
+    return isvalid_ds( Lh_get_dictionary_size( data ) );
+  return false;
+  }
+
+
+typedef uint8_t Lzip_trailer[20];
+			/*  0-3  CRC32 of the uncompressed data */
+			/*  4-11 size of the uncompressed data */
+			/* 12-19 member size including header and trailer */
+enum { Lt_size = 20 };
+
+static inline unsigned Lt_get_data_crc( const Lzip_trailer data )
+  {
+  unsigned tmp = 0;
+  int i; for( i = 3; i >= 0; --i ) { tmp <<= 8; tmp += data[i]; }
+  return tmp;
+  }
+
+static inline void Lt_set_data_crc( Lzip_trailer data, unsigned crc )
+  { int i; for( i = 0; i <= 3; ++i ) { data[i] = (uint8_t)crc; crc >>= 8; } }
+
+static inline unsigned long long Lt_get_data_size( const Lzip_trailer data )
+  {
+  unsigned long long tmp = 0;
+  int i; for( i = 11; i >= 4; --i ) { tmp <<= 8; tmp += data[i]; }
+  return tmp;
+  }
+
+static inline void Lt_set_data_size( Lzip_trailer data, unsigned long long sz )
+  { int i; for( i = 4; i <= 11; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } }
+
+static inline unsigned long long Lt_get_member_size( const Lzip_trailer data )
+  {
+  unsigned long long tmp = 0;
+  int i; for( i = 19; i >= 12; --i ) { tmp <<= 8; tmp += data[i]; }
+  return tmp;
+  }
+
+static inline void Lt_set_member_size( Lzip_trailer data, unsigned long long sz )
+  { int i; for( i = 12; i <= 19; ++i ) { data[i] = (uint8_t)sz; sz >>= 8; } }
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:            0.1.1.0
+version:            0.1.1.1
 license:            BSD3
 license-file:       LICENSE
 copyright:          Copyright: (c) 2019 Vanessa McHale
@@ -13,7 +13,20 @@
 
 category:           Codec, Compression
 build-type:         Simple
-extra-source-files: stack.yaml
+extra-source-files:
+    stack.yaml
+    cbits/cbuffer.c
+    cbits/decoder.c
+    cbits/decoder.h
+    cbits/encoder.c
+    cbits/encoder.h
+    cbits/encoder_base.c
+    cbits/encoder_base.h
+    cbits/fast_encoder.c
+    cbits/fast_encoder.h
+    cbits/lzip.h
+    cbits/lzlib.h
+
 extra-doc-files:
     README.md
     CHANGELOG.md
diff --git a/src/Codec/Lzip.hs b/src/Codec/Lzip.hs
--- a/src/Codec/Lzip.hs
+++ b/src/Codec/Lzip.hs
@@ -9,10 +9,9 @@
                   ) where
 
 import           Codec.Lzip.Raw
-import           Control.Monad         (unless, void, when)
+import           Control.Monad         (void)
 import           Data.Bits             (shiftL)
 import qualified Data.ByteString       as BS
-import           Data.Functor          (($>))
 import           Data.Int              (Int64)
 import           Data.Maybe            (fromMaybe)
 import           Data.Semigroup
