shithub: opus

Download patch

ref: 30df6cf3f84c81eb0d3e6940670fda729e22d873
parent: 59858633fb4ffc5fe797e885be058e6270a32aef
author: Timothy B. Terriberry <[email protected]>
date: Tue Dec 21 03:42:26 EST 2010

Entropy coder clean-up.

This simplifies a good bit of the error handling, and should make it
 impossible to overrun the buffer in the encoder or decoder, while
 still allowing tell() to operate correctly after a bust.
The encoder now tries to keep the range coder data intact after a
 bust instead of corrupting it with extra bits data, though this is
 not a guarantee (too many extra bits may have already been flushed).
It also now correctly reports errors when the bust occurs merging the
 last byte of range coder and extra bits.

A number of abstraction barrier violations were cleaned up, as well.
This patch also includes a number of minor performance improvements:
 ec_{enc|dec}_bits() in particular should be much faster.

Finally, tf_select was changed to be coded with the range coder
 rather than extra bits, so that it is at the front of the packet
 (for unequal error protection robustness).

--- a/libcelt/celt.c
+++ b/libcelt/celt.c
@@ -600,7 +600,7 @@
       curr = tf_res[i];
    }
    if (LM!=0)
-      ec_enc_bits(enc, tf_select, 1);
+      ec_enc_bit_logp(enc, tf_select, 1);
    for (i=start;i<end;i++)
       tf_res[i] = tf_select_table[LM][4*isTransient+2*tf_select+tf_res[i]];
    /*printf("%d %d ", isTransient, tf_select); for(i=0;i<end;i++)printf("%d ", tf_res[i]);printf("\n");*/
@@ -617,7 +617,7 @@
       curr = tf_res[i];
    }
    if (LM!=0)
-      tf_select = ec_dec_bits(dec, 1);
+      tf_select = ec_dec_bit_logp(dec, 1);
    else
       tf_select = 0;
    for (i=start;i<end;i++)
@@ -1953,7 +1953,7 @@
    deemphasis(out_syn, pcm, N, C, st->mode->preemph, st->preemph_memD);
    st->loss_count = 0;
    RESTORE_STACK;
-   if (ec_dec_get_error(dec))
+   if (ec_dec_tell(dec,0) > 8*len || ec_dec_get_error(dec))
       return CELT_CORRUPTED_DATA;
    else
       return CELT_OK;
--- a/libcelt/entcode.h
+++ b/libcelt/entcode.h
@@ -34,28 +34,31 @@
 #if !defined(_entcode_H)
 # define _entcode_H (1)
 # include <limits.h>
+# include <stddef.h>
 # include "ecintrin.h"
 
 
 
-typedef celt_int32 ec_int32;
-typedef celt_uint32 ec_uint32;
+typedef celt_int32            ec_int32;
+typedef celt_uint32           ec_uint32;
+typedef size_t                ec_window;
 typedef struct ec_byte_buffer ec_byte_buffer;
 
 
 
-/*The number of bits to code at a time when coding bits directly.*/
-# define EC_UNIT_BITS  (8)
-/*The mask for the given bits.*/
-# define EC_UNIT_MASK  ((1U<<EC_UNIT_BITS)-1)
+/*This must be at least 32 bits.*/
+# define EC_WINDOW_SIZE ((int)sizeof(ec_window)*CHAR_BIT)
 
+/*The number of bits to use for the range-coded part of unsigned integers.*/
+# define EC_UINT_BITS   (8)
 
 
+
 /*Simple libogg1-style buffer.*/
 struct ec_byte_buffer{
   unsigned char *buf;
-  unsigned char *ptr;
-  unsigned char *end_ptr;
+  ec_uint32      offs;
+  ec_uint32      end_offs;
   ec_uint32      storage;
 };
 
@@ -62,26 +65,25 @@
 /*Encoding functions.*/
 void ec_byte_writeinit_buffer(ec_byte_buffer *_b, unsigned char *_buf, ec_uint32 _size);
 void ec_byte_shrink(ec_byte_buffer *_b, ec_uint32 _size);
-int ec_byte_write1(ec_byte_buffer *_b,unsigned _value);
+int ec_byte_write(ec_byte_buffer *_b,unsigned _value);
 int ec_byte_write_at_end(ec_byte_buffer *_b,unsigned _value);
+int ec_byte_write_done(ec_byte_buffer *_b,int _start_bits_available,
+ unsigned _end_byte,int _end_bits_used);
 /*Decoding functions.*/
 void ec_byte_readinit(ec_byte_buffer *_b,unsigned char *_buf,ec_uint32 _bytes);
-unsigned char ec_byte_look_at_end(ec_byte_buffer *_b);
-int ec_byte_look4(ec_byte_buffer *_b,ec_uint32 *_val);
-void ec_byte_adv1(ec_byte_buffer *_b);
-void ec_byte_adv4(ec_byte_buffer *_b);
-int ec_byte_read1(ec_byte_buffer *_b);
+int ec_byte_read(ec_byte_buffer *_b);
+unsigned char ec_byte_read_from_end(ec_byte_buffer *_b);
 /*Shared functions.*/
 static inline void ec_byte_reset(ec_byte_buffer *_b){
-   _b->ptr=_b->buf;
+  _b->offs=_b->end_offs=0;
 }
 
 static inline ec_uint32 ec_byte_bytes(ec_byte_buffer *_b){
-   return _b->ptr-_b->buf;
+  return _b->offs;
 }
 
 static inline unsigned char *ec_byte_get_buffer(ec_byte_buffer *_b){
-   return _b->buf;
+  return _b->buf;
 }
 
 int ec_ilog(ec_uint32 _v);
--- a/libcelt/entdec.c
+++ b/libcelt/entdec.c
@@ -39,50 +39,39 @@
 #include "arch.h"
 
 void ec_byte_readinit(ec_byte_buffer *_b,unsigned char *_buf,ec_uint32 _bytes){
-  _b->buf=_b->ptr=_buf;
+  _b->buf=_buf;
+  _b->offs=_b->end_offs=0;
   _b->storage=_bytes;
-  _b->end_ptr=_b->buf+_bytes-1;
 }
 
-unsigned char ec_byte_look_at_end(ec_byte_buffer *_b){
-  celt_assert2 (_b->end_ptr >= _b->buf, "Trying to read raw bits before the beginning of the stream");
-  return *(_b->end_ptr--);
+int ec_byte_read(ec_byte_buffer *_b){
+  return _b->offs<_b->storage?_b->buf[_b->offs++]:0;
 }
 
-void ec_byte_adv1(ec_byte_buffer *_b){
-  _b->ptr++;
+unsigned char ec_byte_read_from_end(ec_byte_buffer *_b){
+  return _b->end_offs<_b->storage?_b->buf[_b->storage-++(_b->end_offs)]:0;
 }
 
-int ec_byte_read1(ec_byte_buffer *_b){
-  ptrdiff_t endbyte;
-  endbyte=_b->ptr-_b->buf;
-  if(endbyte>=_b->storage)return -1;
-  else return *(_b->ptr++);
-}
-
 ec_uint32 ec_dec_uint(ec_dec *_this,ec_uint32 _ft){
-  unsigned  ft;
-  unsigned  s;
-  int       ftb;
+  unsigned ft;
+  unsigned s;
+  int      ftb;
   /*In order to optimize EC_ILOG(), it is undefined for the value 0.*/
   celt_assert(_ft>1);
   _ft--;
   ftb=EC_ILOG(_ft);
-  if(ftb>EC_UNIT_BITS){
+  if(ftb>EC_UINT_BITS){
     ec_uint32 t;
-    ftb-=EC_UNIT_BITS;
+    ftb-=EC_UINT_BITS;
     ft=(unsigned)(_ft>>ftb)+1;
     s=ec_decode(_this,ft);
     ec_dec_update(_this,s,s+1,ft);
-    t=s;
-    t = t<<ftb|ec_dec_bits(_this,ftb&(1<<ftb)-1);
-    if (t>_ft)
-    {
-      _this->error |= 1;
-      t = _ft;
-    }
-    return t;
-  } else {
+    t=s<<ftb|ec_dec_bits(_this,ftb);
+    if(t<=_ft)return t;
+    _this->error=1;
+    return _ft;
+  }
+  else{
     _ft++;
     s=ec_decode(_this,(unsigned)_ft);
     ec_dec_update(_this,s,s+1,(unsigned)_ft);
@@ -90,7 +79,6 @@
   }
 }
 
-int ec_dec_get_error(ec_dec *_this)
-{
-  return _this->error || (ec_dec_tell(_this,0) > 8*_this->buf->storage);
+int ec_dec_get_error(ec_dec *_this){
+  return _this->error;
 }
--- a/libcelt/entdec.h
+++ b/libcelt/entdec.h
@@ -31,6 +31,7 @@
 
 #if !defined(_entdec_H)
 # define _entdec_H (1)
+# include <limits.h>
 # include "entcode.h"
 
 
@@ -52,12 +53,13 @@
    ec_uint32       dif;
    /*Normalization factor.*/
    ec_uint32       nrm;
-   /*Byte that will be written at the end*/
-   unsigned char   end_byte;
-   /*Number of valid bits in end_byte*/
-   int             end_bits_left;
-   int             nb_end_bits;
-   /*Nonzero if an error occurred*/
+   /*Bits that were written at the end.*/
+   ec_window       end_window;
+   /*Number of valid bits in end_window.*/
+   int             nend_bits;
+   /*The total number of whole bits read.*/
+   int             nbits_total;
+   /*Nonzero if an error occurred.*/
    int             error;
 };
 
@@ -101,7 +103,7 @@
   The bits must have been encoded with ec_enc_bits().
   No call to ec_dec_update() is necessary after this call.
   _ftb: The number of bits to extract.
-        This must be at least one, and no more than 32.
+        This must be between 0 and 25, inclusive.
   Return: The decoded bits.*/
 ec_uint32 ec_dec_bits(ec_dec *_this,unsigned _ftb);
 /*Extracts a raw unsigned integer with a non-power-of-2 range from the stream.
@@ -137,7 +139,7 @@
            rounding error is in the positive direction).*/
 ec_uint32 ec_dec_tell(ec_dec *_this,int _b);
 
-/*Returns a nonzero value if any error has been detected during decoding*/
+/*Return: A nonzero value if any error has been detected during decoding.*/
 int ec_dec_get_error(ec_dec *_this);
 
 #endif
--- a/libcelt/entenc.c
+++ b/libcelt/entenc.c
@@ -29,53 +29,60 @@
    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
+#if defined(HAVE_CONFIG_H)
+# include "config.h"
 #endif
-
 #include "os_support.h"
 #include "entenc.h"
 #include "arch.h"
 
 
-void ec_byte_writeinit_buffer(ec_byte_buffer *_b, unsigned char *_buf, ec_uint32 _size){
-  _b->ptr=_b->buf=_buf;
-  _b->end_ptr=_b->buf+_size-1;
+void ec_byte_writeinit_buffer(ec_byte_buffer *_b,
+ unsigned char *_buf,ec_uint32 _size){
+  _b->buf=_buf;
+  _b->end_offs=_b->offs=0;
   _b->storage=_size;
 }
 
-void ec_byte_shrink(ec_byte_buffer *_b, ec_uint32 _size){
-   int i;
-   int d;
-   int N;
-   d = _b->storage-_size;
-   N = _b->storage-(_b->end_ptr-_b->buf)-1;
-   /* Copy "raw bytes" */
-   _b->end_ptr=_b->buf+_size-1-N;
-   for (i=0;i<N;i++)
-      _b->end_ptr[i+1] = _b->end_ptr[i+1+d];
-   _b->storage=_size;
+void ec_byte_shrink(ec_byte_buffer *_b,ec_uint32 _size){
+  celt_assert(_b->offs+_b->end_offs<=_size);
+  CELT_MOVE(_b->buf+_size-_b->end_offs,
+   _b->buf+_b->storage-_b->end_offs,_b->end_offs);
+  _b->storage=_size;
 }
 
-int ec_byte_write1(ec_byte_buffer *_b,unsigned _value){
-  ptrdiff_t endbyte;
-  endbyte=_b->ptr-_b->buf;
-  if(endbyte>=_b->storage){
-    return 1;
-  } else {
-    *(_b->ptr++)=(unsigned char)_value;
-    return 0;
-  }
+int ec_byte_write(ec_byte_buffer *_b,unsigned _value){
+  if(_b->offs+_b->end_offs>=_b->storage)return -1;
+  _b->buf[_b->offs++]=(unsigned char)_value;
+  return 0;
 }
 
 int ec_byte_write_at_end(ec_byte_buffer *_b,unsigned _value){
-  if (_b->end_ptr < _b->ptr)
-  {
-    return 1;
-  } else {
-    *(_b->end_ptr--)=(unsigned char)_value;
-    return 0;
+  if(_b->offs+_b->end_offs>=_b->storage)return -1;
+  _b->buf[_b->storage-++(_b->end_offs)]=(unsigned char)_value;
+  return 0;
+}
+
+int ec_byte_write_done(ec_byte_buffer *_b,int _start_bits_available,
+ unsigned _end_byte,int _end_bits_used){
+  int ret;
+  CELT_MEMSET(_b->buf+_b->offs,0,_b->storage-_b->offs-_b->end_offs);
+  ret=0;
+  if(_end_bits_used>0){
+    if(_b->offs+_b->end_offs>=_b->storage){
+      /*If there's no range coder data at all, give up.*/
+      if(_b->end_offs>=_b->storage)return -1;
+      /*If we've busted, don't add too many extra bits to the last byte; it
+         would corrupt the range coder data, and that's more important.*/
+      if(_start_bits_available<_end_bits_used){
+        _end_bits_used=_start_bits_available;
+        _end_byte&=(1<_start_bits_available)-1;
+        ret=-1;
+      }
+    }
+    _b->buf[_b->storage-_b->end_offs-1]|=_end_byte;
   }
+  return ret;
 }
 
 void ec_enc_uint(ec_enc *_this,ec_uint32 _fl,ec_uint32 _ft){
@@ -86,18 +93,16 @@
   celt_assert(_ft>1);
   _ft--;
   ftb=EC_ILOG(_ft);
-  if(ftb>EC_UNIT_BITS){
-    ftb-=EC_UNIT_BITS;
+  if(ftb>EC_UINT_BITS){
+    ftb-=EC_UINT_BITS;
     ft=(_ft>>ftb)+1;
     fl=(unsigned)(_fl>>ftb);
     ec_encode(_this,fl,fl+1,ft);
-    ec_enc_bits(_this,_fl&(1<<ftb)-1,ftb);
-  } else {
-    ec_encode(_this,_fl,_fl+1,_ft+1);
+    ec_enc_bits(_this,_fl&((ec_uint32)1<<ftb)-1,ftb);
   }
+  else ec_encode(_this,_fl,_fl+1,_ft+1);
 }
 
-int ec_enc_get_error(ec_enc *_this)
-{
+int ec_enc_get_error(ec_enc *_this){
   return _this->error;
 }
--- a/libcelt/entenc.h
+++ b/libcelt/entenc.h
@@ -52,12 +52,13 @@
    ec_uint32       rng;
    /*The low end of the current range (inclusive).*/
    ec_uint32       low;
-   /*Byte that will be written at the end*/
-   unsigned char   end_byte;
-   /*Number of valid bits in end_byte*/
-   int             end_bits_left;
-   int             nb_end_bits;
-   /*Nonzero if an error occurred*/
+   /*Bits that will be written at the end.*/
+   ec_window       end_window;
+   /*Number of valid bits in end_window.*/
+   int             nend_bits;
+   /*The total number of whole bits written.*/
+   int             nbits_total;
+   /*Nonzero if an error occurred.*/
    int             error;
 };
 
@@ -84,7 +85,7 @@
 /*Encodes a sequence of raw bits in the stream.
   _fl:  The bits to encode.
   _ftb: The number of bits to encode.
-        This must be at least one, and no more than 32.*/
+        This must be between 0 and 25, inclusive.*/
 void ec_enc_bits(ec_enc *_this,ec_uint32 _fl,unsigned _ftb);
 
 /*Encodes a raw unsigned integer in the stream.
--- a/libcelt/quant_bands.c
+++ b/libcelt/quant_bands.c
@@ -207,7 +207,6 @@
          bits_left = budget-(int)ec_enc_tell(enc, 0)-3*C*(end-i);
          if (i!=start && bits_left < 30)
          {
-            qi = IMAX(-1,qi);
             if (bits_left < 24)
                qi = IMIN(1, qi);
             if (bits_left < 16)
@@ -274,6 +273,8 @@
       ec_enc enc_intra_state;
       ec_byte_buffer buf_intra_state;
       int tell_intra;
+      ec_uint32 nstart_bytes;
+      ec_uint32 nintra_bytes;
       VARDECL(unsigned char, intra_bits);
 
       tell_intra = ec_enc_tell(enc, 3);
@@ -281,9 +282,13 @@
       enc_intra_state = *enc;
       buf_intra_state = *(enc->buf);
 
-      ALLOC(intra_bits, buf_intra_state.ptr-buf_start_state.ptr, unsigned char);
+      nstart_bytes = ec_byte_bytes(&buf_start_state);
+      nintra_bytes = ec_byte_bytes(&buf_intra_state);
+      ALLOC(intra_bits, nintra_bytes-nstart_bytes, unsigned char);
       /* Copy bits from intra bit-stream */
-      CELT_COPY(intra_bits, buf_start_state.ptr, buf_intra_state.ptr-buf_start_state.ptr);
+      CELT_COPY(intra_bits,
+            ec_byte_get_buffer(&buf_intra_state) + nstart_bytes,
+            nintra_bytes - nstart_bytes);
 
       *enc = enc_start_state;
       *(enc->buf) = buf_start_state;
@@ -295,8 +300,9 @@
       {
          *enc = enc_intra_state;
          *(enc->buf) = buf_intra_state;
-         /* Copy bits from to bit-stream */
-         CELT_COPY(buf_start_state.ptr, intra_bits, buf_intra_state.ptr-buf_start_state.ptr);
+         /* Copy intra bits to bit-stream */
+         CELT_COPY(ec_byte_get_buffer(&buf_intra_state) + nstart_bytes,
+               intra_bits, nintra_bytes - nstart_bytes);
          CELT_COPY(oldEBands, oldEBands_intra, C*end);
          CELT_COPY(error, error_intra, C*end);
       }
--- a/libcelt/rangedec.c
+++ b/libcelt/rangedec.c
@@ -97,22 +97,6 @@
   }*/
 
 
-/*Gets the next byte of input.
-  After all the bytes in the current packet have been consumed, and the extra
-   end code returned if needed, this function will continue to return zero each
-   time it is called.
-  Return: The next byte of input.*/
-static int ec_dec_in(ec_dec *_this){
-  int ret;
-  ret=ec_byte_read1(_this->buf);
-  if(ret<0){
-    ret=0;
-    /*Needed to keep oc_dec_tell() operating correctly.*/
-    ec_byte_adv1(_this->buf);
-  }
-  return ret;
-}
-
 /*Normalizes the contents of dif and rng so that rng lies entirely in the
    high-order symbol.*/
 static inline void ec_dec_normalize(ec_dec *_this){
@@ -119,13 +103,14 @@
   /*If the range is too small, rescale it and input some bits.*/
   while(_this->rng<=EC_CODE_BOT){
     int sym;
+    _this->nbits_total+=EC_SYM_BITS;
     _this->rng<<=EC_SYM_BITS;
     /*Use up the remaining bits from our last symbol.*/
-    sym=_this->rem<<EC_CODE_EXTRA;
+    sym=_this->rem;
     /*Read the next value from the input.*/
-    _this->rem=ec_dec_in(_this);
+    _this->rem=ec_byte_read(_this->buf);
     /*Take the rest of the bits we need from this new symbol.*/
-    sym|=_this->rem>>EC_SYM_BITS-EC_CODE_EXTRA;
+    sym=(sym<<EC_SYM_BITS|_this->rem)>>EC_SYM_BITS-EC_CODE_EXTRA;
     /*And subtract them from dif, capped to be less than EC_CODE_TOP.*/
     _this->dif=(_this->dif<<EC_SYM_BITS)+(EC_SYM_MAX&~sym)&EC_CODE_TOP-1;
   }
@@ -133,14 +118,17 @@
 
 void ec_dec_init(ec_dec *_this,ec_byte_buffer *_buf){
   _this->buf=_buf;
-  _this->rem=ec_dec_in(_this);
+  _this->rem=ec_byte_read(_buf);
   _this->rng=1U<<EC_CODE_EXTRA;
   _this->dif=_this->rng-1-(_this->rem>>EC_SYM_BITS-EC_CODE_EXTRA);
   /*Normalize the interval.*/
   ec_dec_normalize(_this);
-  _this->end_byte=0; /* Required for platforms that have chars > 8 bits */
-  _this->end_bits_left=0;
-  _this->nb_end_bits=0;
+  _this->end_window=0;
+  _this->nend_bits=0;
+  /*This is the offset from which ec_enc_tell() will subtract partial bits.
+    This must be after the initial ec_dec_normalize(), or you will have to
+     compensate for the bits that are read there.*/
+  _this->nbits_total=EC_CODE_BITS+1;
   _this->error=0;
 }
 
@@ -159,28 +147,11 @@
    return (1<<_bits)-EC_MINI(s+1,1<<_bits);
 }
 
-unsigned ec_dec_bits(ec_dec *_this,unsigned bits){
-  unsigned value=0;
-  int count=0;
-  _this->nb_end_bits += bits;
-  while (bits>=_this->end_bits_left)
-  {
-    value |= _this->end_byte>>(8-_this->end_bits_left)<<count;
-    count += _this->end_bits_left;
-    bits -= _this->end_bits_left;
-    _this->end_byte=ec_byte_look_at_end(_this->buf);
-    _this->end_bits_left = 8;
-  }
-  value |= ((_this->end_byte>>(8-_this->end_bits_left))&((1<<bits)-1))<<count;
-  _this->end_bits_left -= bits;
-  return value;
-}
-
 void ec_dec_update(ec_dec *_this,unsigned _fl,unsigned _fh,unsigned _ft){
   ec_uint32 s;
-  s=IMUL32(_this->nrm,(_ft-_fh));
+  s=IMUL32(_this->nrm,_ft-_fh);
   _this->dif-=s;
-  _this->rng=_fl>0?IMUL32(_this->nrm,(_fh-_fl)):_this->rng-s;
+  _this->rng=_fl>0?IMUL32(_this->nrm,_fh-_fl):_this->rng-s;
   ec_dec_normalize(_this);
 }
 
@@ -237,17 +208,45 @@
   return val-1;
 }
 
+ec_uint32 ec_dec_bits(ec_dec *_this,unsigned _bits){
+  ec_window window;
+  int       available;
+  int       ret;
+  window=_this->end_window;
+  available=_this->nend_bits;
+  if(available<_bits){
+    do{
+      window|=(ec_window)ec_byte_read_from_end(_this->buf)<<available;
+      available+=EC_SYM_BITS;
+    }
+    while(available<=EC_WINDOW_SIZE-EC_SYM_BITS);
+  }
+  ret=(ec_uint32)window&((ec_uint32)1<<_bits)-1;
+  window>>=_bits;
+  available-=_bits;
+  _this->end_window=window;
+  _this->nend_bits=available;
+  _this->nbits_total+=_bits;
+  return ret;
+}
+
 ec_uint32 ec_dec_tell(ec_dec *_this,int _b){
+  ec_uint32 nbits;
   ec_uint32 r;
   int       l;
-  ec_uint32      nbits;
-  nbits=(ec_byte_bytes(_this->buf)-(EC_CODE_BITS+EC_SYM_BITS-1)/EC_SYM_BITS)*
-   EC_SYM_BITS;
   /*To handle the non-integral number of bits still left in the decoder state,
-     we compute the number of bits of low that must be encoded to ensure that
-     the value is inside the range for any possible subsequent bits.*/
-  nbits+=EC_CODE_BITS+1+_this->nb_end_bits;
-  nbits<<=_b;
+     we compute the worst-case number of bits of low that must be encoded to
+     ensure that the value is inside the range for any possible subsequent
+     bits.
+    The computation here is independent of low itself (the decoder does not
+     even track that value), even though the real number of bits used after
+     ec_enc_done() may be 1 smaller if rng is a power of two and the
+     corresponding trailing bits of low are all zeros.
+    If we did try to track that special case, then coding a value with a
+     probability of 1/(1<<n) might sometimes appear to use more than n bits.
+    This may help explain the surprising result that a newly initialized
+     decoder claims to have used 1 bit.*/
+  nbits=_this->nbits_total<<_b;
   l=EC_ILOG(_this->rng);
   r=_this->rng>>l-16;
   while(_b-->0){
--- a/libcelt/rangeenc.c
+++ b/libcelt/rangeenc.c
@@ -83,11 +83,11 @@
     carry=_c>>EC_SYM_BITS;
     /*Don't output a byte on the first write.
       This compare should be taken care of by branch-prediction thereafter.*/
-    if(_this->rem>=0)_this->error|=ec_byte_write1(_this->buf,_this->rem+carry);
+    if(_this->rem>=0)_this->error|=ec_byte_write(_this->buf,_this->rem+carry);
     if(_this->ext>0){
       unsigned sym;
       sym=EC_SYM_MAX+carry&EC_SYM_MAX;
-      do _this->error|=ec_byte_write1(_this->buf,sym);
+      do _this->error|=ec_byte_write(_this->buf,sym);
       while(--(_this->ext)>0);
     }
     _this->rem=_c&EC_SYM_MAX;
@@ -102,6 +102,7 @@
     /*Move the next-to-high-order symbol into the high-order position.*/
     _this->low=_this->low<<EC_SYM_BITS&EC_CODE_TOP-1;
     _this->rng<<=EC_SYM_BITS;
+    _this->nbits_total+=EC_SYM_BITS;
   }
 }
 
@@ -111,9 +112,10 @@
   _this->ext=0;
   _this->low=0;
   _this->rng=EC_CODE_TOP;
-  _this->end_byte=0;
-  _this->end_bits_left=8;
-  _this->nb_end_bits=0;
+  _this->end_window=0;
+  _this->nend_bits=0;
+  /*This is the offset from which ec_enc_tell() will subtract partial bits.*/
+  _this->nbits_total=EC_CODE_BITS+1;
   _this->error=0;
 }
 
@@ -129,69 +131,82 @@
 }
 
 void ec_encode_bin(ec_enc *_this,unsigned _fl,unsigned _fh,unsigned _bits){
-   ec_uint32 r;
-   r=_this->rng>>_bits;
-   if(_fl>0){
-      _this->low+=_this->rng-IMUL32(r,((1<<_bits)-_fl));
-      _this->rng=IMUL32(r,(_fh-_fl));
-   }
-   else _this->rng-=IMUL32(r,((1<<_bits)-_fh));
-   ec_enc_normalize(_this);
+  ec_uint32 r;
+  r=_this->rng>>_bits;
+  if(_fl>0){
+    _this->low+=_this->rng-IMUL32(r,((1<<_bits)-_fl));
+    _this->rng=IMUL32(r,(_fh-_fl));
+  }
+  else _this->rng-=IMUL32(r,((1<<_bits)-_fh));
+  ec_enc_normalize(_this);
 }
 
 /*The probability of having a "one" is given in 1/65536.*/
 void ec_enc_bit_prob(ec_enc *_this,int _val,unsigned _prob){
-   ec_uint32 r;
-   ec_uint32 s;
-   ec_uint32 l;
-   r=_this->rng;
-   l=_this->low;
-   s=(r>>16)*_prob;
-   r-=s;
-   if(_val)_this->low=l+r;
-   _this->rng=_val?s:r;
-   ec_enc_normalize(_this);
+  ec_uint32 r;
+  ec_uint32 s;
+  ec_uint32 l;
+  r=_this->rng;
+  l=_this->low;
+  s=(r>>16)*_prob;
+  r-=s;
+  if(_val)_this->low=l+r;
+  _this->rng=_val?s:r;
+  ec_enc_normalize(_this);
 }
 
 /*The probability of having a "one" is 1/(1<<_logp).*/
 void ec_enc_bit_logp(ec_enc *_this,int _val,unsigned _logp){
-   ec_uint32 r;
-   ec_uint32 s;
-   ec_uint32 l;
-   r=_this->rng;
-   l=_this->low;
-   s=r>>_logp;
-   r-=s;
-   if(_val)_this->low=l+r;
-   _this->rng=_val?s:r;
-   ec_enc_normalize(_this);
+  ec_uint32 r;
+  ec_uint32 s;
+  ec_uint32 l;
+  r=_this->rng;
+  l=_this->low;
+  s=r>>_logp;
+  r-=s;
+  if(_val)_this->low=l+r;
+  _this->rng=_val?s:r;
+  ec_enc_normalize(_this);
 }
 
-void ec_enc_bits(ec_enc *_this,unsigned _fl,unsigned bits){
-  _this->nb_end_bits += bits;
-  while (bits >= _this->end_bits_left)
-  {
-    _this->end_byte |= (_fl<<(8-_this->end_bits_left)) & 0xff;
-    _fl >>= _this->end_bits_left;
-    _this->error|=ec_byte_write_at_end(_this->buf, _this->end_byte);
-    _this->end_byte = 0;
-    bits -= _this->end_bits_left;
-    _this->end_bits_left = 8;
+void ec_enc_bits(ec_enc *_this,ec_uint32 _fl,unsigned _bits){
+  ec_window window;
+  int       used;
+  window=_this->end_window;
+  used=_this->nend_bits;
+  if(used+_bits>EC_WINDOW_SIZE){
+    do{
+      _this->error|=
+       ec_byte_write_at_end(_this->buf,(unsigned)window&EC_SYM_MAX);
+      window>>=EC_SYM_BITS;
+      used-=EC_SYM_BITS;
+    }
+    while(used>=EC_SYM_BITS);
   }
-  _this->end_byte |= (_fl<<(8-_this->end_bits_left)) & 0xff;
-  _this->end_bits_left -= bits;
+  window|=(ec_window)_fl<<used;
+  used+=_bits;
+  _this->end_window=window;
+  _this->nend_bits=used;
+  _this->nbits_total+=_bits;
 }
 
 ec_uint32 ec_enc_tell(ec_enc *_this,int _b){
+  ec_uint32 nbits;
   ec_uint32 r;
   int       l;
-  ec_uint32      nbits;
-  nbits=(ec_byte_bytes(_this->buf)+(_this->rem>=0)+_this->ext)*EC_SYM_BITS;
-  /*To handle the non-integral number of bits still left in the encoder state,
-     we compute the number of bits of low that must be encoded to ensure that
-     the value is inside the range for any possible subsequent bits.*/
-  nbits+=EC_CODE_BITS+1+_this->nb_end_bits;
-  nbits<<=_b;
+  /*To handle the non-integral number of bits still left in the decoder state,
+     we compute the worst-case number of bits of low that must be encoded to
+     ensure that the value is inside the range for any possible subsequent
+     bits.
+    The computation here is independent of low itself (the decoder does not
+     even track that value), even though the real number of bits used after
+     ec_enc_done() may be 1 smaller if rng is a power of two and the
+     corresponding trailing bits of low are all zeros.
+    If we did try to track that special case, then coding a value with a
+     probability of 1/(1<<n) might sometimes appear to use more than n bits.
+    This may help explain the surprising result that a newly initialized
+     encoder claims to have used 1 bit.*/
+  nbits=_this->nbits_total<<_b;
   l=EC_ILOG(_this->rng);
   r=_this->rng>>l-16;
   while(_b-->0){
@@ -205,6 +220,8 @@
 }
 
 void ec_enc_done(ec_enc *_this){
+  ec_window window;
+  int       used;
   ec_uint32 msk;
   ec_uint32 end;
   int       l;
@@ -224,15 +241,16 @@
     l-=EC_SYM_BITS;
   }
   /*If we have a buffered byte flush it into the output buffer.*/
-  if(_this->rem>=0||_this->ext>0){
-    ec_enc_carry_out(_this,0);
-    _this->rem=-1;
+  if(_this->rem>=0||_this->ext>0)ec_enc_carry_out(_this,0);
+  /*If we have buffered extra bits, flush them as well.*/
+  window=_this->end_window;
+  used=_this->nend_bits;
+  while(used>=EC_SYM_BITS){
+    _this->error|=
+     ec_byte_write_at_end(_this->buf,(unsigned)window&EC_SYM_MAX);
+    window>>=EC_SYM_BITS;
+    used-=EC_SYM_BITS;
   }
-  {
-    unsigned char *ptr = _this->buf->ptr;
-    while (ptr<= _this->buf->end_ptr)
-      *ptr++ = 0;
-    if (_this->end_bits_left != 8)
-      *_this->buf->end_ptr |= _this->end_byte;
-  }
+  /*Clear any excess space and add any remaining extra bits to the last byte.*/
+  if(!_this->error)_this->error=ec_byte_write_done(_this->buf,-l,window,used);
 }
--- a/tests/ectest.c
+++ b/tests/ectest.c
@@ -77,7 +77,7 @@
   fprintf(stderr,
    "Encoded %0.2lf bits of entropy to %0.2lf bits (%0.3lf%% wasted).\n",
    entropy,ldexp(nbits,-4),100*(nbits-ldexp(entropy,4))/nbits);
-  fprintf(stderr,"Packed to %li bytes.\n",(long)(buf.ptr-buf.buf));
+  fprintf(stderr,"Packed to %li bytes.\n",(long)ec_byte_bytes(&buf));
   ec_byte_readinit(&buf,ptr,DATA_SIZE);
   ec_dec_init(&dec,&buf);
   for(ft=2;ft<1024;ft++){
@@ -109,6 +109,7 @@
   fprintf(stderr,"Testing random streams... Random seed: %u (%.4X)\n", seed, rand() % 65536);
   for(i=0;i<409600;i++){
     unsigned *data;
+    unsigned *tell;
     int       j;
     int tell_bits;
     int zeros;
@@ -115,9 +116,11 @@
     ft=rand()/((RAND_MAX>>(rand()%11))+1)+10;
     sz=rand()/((RAND_MAX>>(rand()%9))+1);
     data=(unsigned *)malloc(sz*sizeof(*data));
+    tell=(unsigned *)malloc((sz+1)*sizeof(*tell));
     ec_byte_writeinit_buffer(&buf, ptr, DATA_SIZE2);
     ec_enc_init(&enc,&buf);
     zeros = rand()%13==0;
+    tell[0]=ec_enc_tell(&enc, 3);
     for(j=0;j<sz;j++){
       if (zeros)
         data[j]=0;
@@ -124,6 +127,7 @@
       else
         data[j]=rand()%ft;
       ec_enc_uint(&enc,data[j],ft);
+      tell[j+1]=ec_enc_tell(&enc, 3);
     }
     if (rand()%2==0)
       while(ec_enc_tell(&enc, 0)%8 != 0)
@@ -130,6 +134,11 @@
         ec_enc_uint(&enc, rand()%2, 2);
     tell_bits = ec_enc_tell(&enc, 0);
     ec_enc_done(&enc);
+    if(tell_bits!=ec_enc_tell(&enc,0)){
+      fprintf(stderr,"tell() changed after ec_enc_done(): %i instead of %i (Random seed: %u)\n",
+       ec_enc_tell(&enc,0),tell_bits,seed);
+      ret=-1;
+    }
     if ((tell_bits+7)/8 < ec_byte_bytes(&buf))
     {
       fprintf (stderr, "tell() lied, there's %i bytes instead of %d (Random seed: %u)\n",
@@ -139,6 +148,11 @@
     tell_bits -= 8*ec_byte_bytes(&buf);
     ec_byte_readinit(&buf,ptr,DATA_SIZE2);
     ec_dec_init(&dec,&buf);
+    if(ec_dec_tell(&dec,3)!=tell[0]){
+      fprintf(stderr,
+       "Tell mismatch between encoder and decoder at symbol %i: %i instead of %i (Random seed: %u).\n",
+       0,ec_dec_tell(&dec,3),tell[0],seed);
+    }
     for(j=0;j<sz;j++){
       sym=ec_dec_uint(&dec,ft);
       if(sym!=data[j]){
@@ -146,6 +160,11 @@
          "Decoded %i instead of %i with ft of %i at position %i of %i (Random seed: %u).\n",
          sym,data[j],ft,j,sz,seed);
         ret=-1;
+      }
+      if(ec_dec_tell(&dec,3)!=tell[j+1]){
+        fprintf(stderr,
+         "Tell mismatch between encoder and decoder at symbol %i: %i instead of %i (Random seed: %u).\n",
+         j+1,ec_dec_tell(&dec,3),tell[j+1],seed);
       }
     }
     free(data);