Memory use reduction:
authorMonty <xiphmont@xiph.org>
Sat, 19 Jan 2002 04:52:40 +0000 (04:52 +0000)
committerMonty <xiphmont@xiph.org>
Sat, 19 Jan 2002 04:52:40 +0000 (04:52 +0000)
eliminate huffman decode tree
eliminate need to keep static codebook for decode
compact sparse codebooks to reduce mem usage

experimental, test it well.

svn path=/trunk/vorbis/; revision=2959

lib/block.c
lib/codebook.c
lib/codebook.h
lib/sharedbook.c

index 5e7c887..4453367 100644 (file)
@@ -5,13 +5,13 @@
  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
  *                                                                  *
  ********************************************************************
 
  function: PCM data vector blocking, windowing and dis/reassembly
- last mod: $Id: block.c,v 1.55 2001/12/23 11:53:52 xiphmont Exp $
+ last mod: $Id: block.c,v 1.56 2002/01/19 04:52:39 xiphmont Exp $
 
  Handle windowing, overlap-add, etc of the PCM vectors.  This is made
  more amusing by Vorbis' current two allowed block sizes.
@@ -221,8 +221,12 @@ static int _vds_shared_init(vorbis_dsp_state *v,vorbis_info *vi,int encp){
   }else{
     /* finish the codebooks */
     b->fullbooks=_ogg_calloc(ci->books,sizeof(*b->fullbooks));
-    for(i=0;i<ci->books;i++)
+    for(i=0;i<ci->books;i++){
       vorbis_book_init_decode(b->fullbooks+i,ci->book_param[i]);
+      /* decode codebooks are now standalone after init */
+      vorbis_staticbook_destroy(ci->book_param[i]);
+      ci->book_param[i]=NULL;
+    }
   }
 
   /* initialize the storage vectors to a decent size greater than the
index 593a72f..b89f077 100644 (file)
@@ -5,13 +5,13 @@
  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
  *                                                                  *
  ********************************************************************
 
  function: basic codebook pack/unpack/code/decode operations
- last mod: $Id: codebook.c,v 1.35 2001/12/21 14:52:35 segher Exp $
+ last mod: $Id: codebook.c,v 1.36 2002/01/19 04:52:39 xiphmont Exp $
 
  ********************************************************************/
 
@@ -299,6 +299,68 @@ int vorbis_book_encodevs(codebook *book,float *a,oggpack_buffer *b,
   return(vorbis_book_encode(book,best,b));
 }
 
+/* the 'eliminate the decode tree' optimization actually requires the
+   codewords to be MSb first, not LSb.  This is an annoying inelegancy
+   (and one of the first places where carefully thought out design
+   turned out to be wrong; Vorbis II and future Ogg codecs should go
+   to an MSb bitpacker), but not actually the huge hit it appears to
+   be.  The first-stage decode table catches most words so that
+   bitreverse is not in the main execution path. */
+
+static ogg_uint32_t bitreverse(ogg_uint32_t x){
+  x=    ((x>>16)&0x0000ffff) | ((x<<16)&0xffff0000);
+  x=    ((x>> 8)&0x00ff00ff) | ((x<< 8)&0xff00ff00);
+  x=    ((x>> 4)&0x0f0f0f0f) | ((x<< 4)&0xf0f0f0f0);
+  x=    ((x>> 2)&0x33333333) | ((x<< 2)&0xcccccccc);
+  return((x>> 1)&0x55555555) | ((x<< 1)&0xaaaaaaaa);
+}
+
+static long decode_packed_entry_number(codebook *book, oggpack_buffer *b){
+  int read;
+  long lok = oggpack_look(b, book->dec_firsttablen);
+
+  if (lok >= 0) {
+    long entry = book->dec_firsttable[lok];
+    if(entry>=0){
+      oggpack_adv(b, book->dec_codelengths[entry]);
+      return(entry);
+    }
+  }
+
+  read=book->dec_maxlength;
+  lok = oggpack_look(b, read);
+
+  while(lok<0 && read>1)
+    lok = oggpack_look(b, --read);
+
+  if(lok<0)return -1;
+
+  /* bisect search for the codeword in the ordered list */
+  {
+    ogg_uint32_t testword=bitreverse((ogg_uint32_t)lok);
+
+    long lo=0;
+    long hi=book->used_entries;
+    long p=(lo+hi)>>1;
+
+    while(hi-lo>1){
+      if(book->codelist[p]<=testword)
+       lo=p;
+      else
+       hi=p;
+      p=(lo+hi)>>1;
+    }
+
+    if(book->dec_codelengths[p]<=read){
+      oggpack_adv(b, book->dec_codelengths[p]);
+      return(p);
+    }
+  }
+  
+  oggpack_adv(b, read);
+  return(-1);
+}
+
 /* Decode side is specced and easier, because we don't need to find
    matches using different criteria; we simply read and map.  There are
    two things we need to do 'depending':
@@ -313,32 +375,14 @@ int vorbis_book_encodevs(codebook *book,float *a,oggpack_buffer *b,
    addmul==1 -> additive
    addmul==2 -> multiplicitive */
 
-/* returns the entry number or -1 on eof *************************************/
+/* returns the [original, not compacted] entry number or -1 on eof *********/
 long vorbis_book_decode(codebook *book, oggpack_buffer *b){
-  long ptr=0;
-  decode_aux *t=book->decode_tree;
-  int lok = oggpack_look(b, t->tabn);
-
-  if (lok >= 0) {
-    ptr = t->tab[lok];
-    oggpack_adv(b, t->tabl[lok]);
-    if (ptr <= 0)
-      return -ptr;
-  }
-
-  do{
-    switch((int)oggpack_read1(b)){
-    case 0:
-      ptr=t->ptr0[ptr];
-      break;
-    case 1:
-      ptr=t->ptr1[ptr];
-      break;
-    case -1:
-      return(-1);
-    }
-  }while(ptr>0);
-  return(-ptr);
+  long packed_entry=decode_packed_entry_number(book,b);
+  if(packed_entry>=0)
+    return(book->dec_index[packed_entry]);
+  
+  /* if there's no dec_index, the codebook unpacking isn't collapsed */
+  return(packed_entry);
 }
 
 /* returns 0 on OK or -1 on eof *************************************/
@@ -349,7 +393,7 @@ long vorbis_book_decodevs_add(codebook *book,float *a,oggpack_buffer *b,int n){
   int i,j,o;
 
   for (i = 0; i < step; i++) {
-    entry[i]=vorbis_book_decode(book,b);
+    entry[i]=decode_packed_entry_number(book,b);
     if(entry[i]==-1)return(-1);
     t[i] = book->valuelist+entry[i]*book->dim;
   }
@@ -365,7 +409,7 @@ long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){
 
   if(book->dim>8){
     for(i=0;i<n;){
-      entry = vorbis_book_decode(book,b);
+      entry = decode_packed_entry_number(book,b);
       if(entry==-1)return(-1);
       t     = book->valuelist+entry*book->dim;
       for (j=0;j<book->dim;)
@@ -373,7 +417,7 @@ long vorbis_book_decodev_add(codebook *book,float *a,oggpack_buffer *b,int n){
     }
   }else{
     for(i=0;i<n;){
-      entry = vorbis_book_decode(book,b);
+      entry = decode_packed_entry_number(book,b);
       if(entry==-1)return(-1);
       t     = book->valuelist+entry*book->dim;
       j=0;
@@ -407,7 +451,7 @@ long vorbis_book_decodev_set(codebook *book,float *a,oggpack_buffer *b,int n){
   float *t;
 
   for(i=0;i<n;){
-    entry = vorbis_book_decode(book,b);
+    entry = decode_packed_entry_number(book,b);
     if(entry==-1)return(-1);
     t     = book->valuelist+entry*book->dim;
     for (j=0;j<book->dim;)
@@ -422,7 +466,7 @@ long vorbis_book_decodevv_add(codebook *book,float **a,long offset,int ch,
   int chptr=0;
 
   for(i=offset/ch;i<(offset+n)/ch;){
-    entry = vorbis_book_decode(book,b);
+    entry = decode_packed_entry_number(book,b);
     if(entry==-1)return(-1);
     {
       const float *t = book->valuelist+entry*book->dim;
index b4c7ac2..538169f 100644 (file)
@@ -5,13 +5,13 @@
  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
  *                                                                  *
  ********************************************************************
 
  function: basic shared codebook operations
- last mod: $Id: codebook.h,v 1.10 2001/12/20 01:00:26 segher Exp $
+ last mod: $Id: codebook.h,v 1.11 2002/01/19 04:52:40 xiphmont Exp $
 
  ********************************************************************/
 
@@ -96,26 +96,24 @@ typedef struct encode_aux_pigeonhole{
   long *fitlength;
 } encode_aux_pigeonhole;
 
-typedef struct decode_aux{
-  long   *tab;
-  int    *tabl;
-  int    tabn;
-
-  long   *ptr0;
-  long   *ptr1;
-  long   aux;        /* number of tree entries */
-} decode_aux;
-
 typedef struct codebook{
   long dim;           /* codebook dimensions (elements per vector) */
   long entries;       /* codebook entries */
+  long used_entries;  /* populated codebook entries */
   const static_codebook *c;
 
-  float  *valuelist;  /* list of dim*entries actual entry values */
-  long   *codelist;   /* list of bitstream codewords for each entry */
-  struct decode_aux *decode_tree;
+  /* for encode, the below are entry-ordered, fully populated */
+  /* for decode, the below are ordered by bitreversed codeword and only
+     used entries are populated */
+  float        *valuelist;  /* list of dim*entries actual entry values */  
+  ogg_uint32_t *codelist;   /* list of bitstream codewords for each entry */
+
+  int          *dec_index;  /* only used if sparseness collapsed */
+  char         *dec_codelengths;
+  int          *dec_firsttable;
+  int           dec_firsttablen;
+  int           dec_maxlength;
 
-  long zeroentry;
 } codebook;
 
 extern void vorbis_staticbook_clear(static_codebook *b);
@@ -124,7 +122,7 @@ extern int vorbis_book_init_encode(codebook *dest,const static_codebook *source)
 extern int vorbis_book_init_decode(codebook *dest,const static_codebook *source);
 extern void vorbis_book_clear(codebook *b);
 
-extern float *_book_unquantize(const static_codebook *b);
+extern float *_book_unquantize(const static_codebook *b,int n,int *map);
 extern float *_book_logdist(const static_codebook *b,float *vals);
 extern float _float32_unpack(long val);
 extern long   _float32_pack(float val);
index 4a57ea6..69aab3b 100644 (file)
@@ -5,13 +5,13 @@
  * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
  * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
  *                                                                  *
- * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2001             *
+ * THE OggVorbis SOURCE CODE IS (C) COPYRIGHT 1994-2002             *
  * by the XIPHOPHORUS Company http://www.xiph.org/                  *
  *                                                                  *
  ********************************************************************
 
  function: basic shared codebook operations
- last mod: $Id: sharedbook.c,v 1.22 2001/12/21 14:52:36 segher Exp $
+ last mod: $Id: sharedbook.c,v 1.23 2002/01/19 04:52:40 xiphmont Exp $
 
  ********************************************************************/
 
@@ -69,16 +69,16 @@ float _float32_unpack(long val){
 /* given a list of word lengths, generate a list of codewords.  Works
    for length ordered or unordered, always assigns the lowest valued
    codewords first.  Extended to handle unused entries (length 0) */
-long *_make_words(long *l,long n){
-  long i,j;
-  long marker[33];
-  long *r=_ogg_malloc(n*sizeof(*r));
+ogg_uint32_t *_make_words(long *l,long n,long sparsecount){
+  long i,j,count=0;
+  ogg_uint32_t marker[33];
+  ogg_uint32_t *r=_ogg_malloc((sparsecount?sparsecount:n)*sizeof(*r));
   memset(marker,0,sizeof(marker));
 
   for(i=0;i<n;i++){
     long length=l[i];
     if(length>0){
-      long entry=marker[length];
+      ogg_uint32_t entry=marker[length];
       
       /* when we claim a node for an entry, we also claim the nodes
         below it (pruning off the imagined tree that may have dangled
@@ -91,7 +91,7 @@ long *_make_words(long *l,long n){
        _ogg_free(r);
        return(NULL);
       }
-      r[i]=entry;
+      r[count++]=entry;
     
       /* Look to see if the next shorter marker points to the node
         above. if so, update it and repeat.  */
@@ -120,79 +120,26 @@ long *_make_words(long *l,long n){
          marker[j]=marker[j-1]<<1;
        }else
          break;
-    }    
+    }else
+      if(sparsecount==0)count++;
   }
     
   /* bitreverse the words because our bitwise packer/unpacker is LSb
      endian */
-  for(i=0;i<n;i++){
-    long temp=0;
+  for(i=0,count=0;i<n;i++){
+    ogg_uint32_t temp=0;
     for(j=0;j<l[i];j++){
       temp<<=1;
-      temp|=(r[i]>>j)&1;
+      temp|=(r[count]>>j)&1;
     }
-    r[i]=temp;
+    r[count]=temp;
+    if(l[i] || sparsecount==0)
+      count++;
   }
 
   return(r);
 }
 
-/* build the decode helper tree from the codewords */
-decode_aux *_make_decode_tree(codebook *c){
-  const static_codebook *s=c->c;
-  long top=0,i,j,n;
-  decode_aux *t=_ogg_malloc(sizeof(*t));
-  long *ptr0=t->ptr0=_ogg_calloc(c->entries*2,sizeof(*ptr0));
-  long *ptr1=t->ptr1=_ogg_calloc(c->entries*2,sizeof(*ptr1));
-  long *codelist=_make_words(s->lengthlist,s->entries);
-
-  if(codelist==NULL)return(NULL);
-  t->aux=c->entries*2;
-
-  for(i=0;i<c->entries;i++){
-    if(s->lengthlist[i]>0){
-      long ptr=0;
-      for(j=0;j<s->lengthlist[i]-1;j++){
-       int bit=(codelist[i]>>j)&1;
-       if(!bit){
-         if(!ptr0[ptr])
-           ptr0[ptr]= ++top;
-         ptr=ptr0[ptr];
-       }else{
-         if(!ptr1[ptr])
-           ptr1[ptr]= ++top;
-         ptr=ptr1[ptr];
-       }
-      }
-      if(!((codelist[i]>>j)&1))
-       ptr0[ptr]=-i;
-      else
-       ptr1[ptr]=-i;
-    }
-  }
-  _ogg_free(codelist);
-
-  t->tabn = _ilog(c->entries)-4; /* this is magic */
-  if(t->tabn<5)t->tabn=5;
-  n = 1<<t->tabn;
-  t->tab = _ogg_malloc(n*sizeof(*t->tab));
-  t->tabl = _ogg_malloc(n*sizeof(*t->tabl));
-  for (i = 0; i < n; i++) {
-    long p = 0;
-    for (j = 0; j < t->tabn && (p > 0 || j == 0); j++) {
-      if (i & (1 << j))
-       p = ptr1[p];
-      else
-       p = ptr0[p];
-    }
-    /* now j == length, and p == -code */
-    t->tab[i] = p;
-    t->tabl[i] = j;
-  }
-
-  return(t);
-}
-
 /* there might be a straightforward one-line way to do the below
    that's portable and totally safe against roundoff, but I haven't
    thought of it.  Therefore, we opt on the side of caution */
@@ -229,13 +176,13 @@ long _book_maptype1_quantvals(const static_codebook *b){
    generated algorithmically (each column of the vector counts through
    the values in the quant vector). in map type 2, all the values came
    in in an explicit list.  Both value lists must be unpacked */
-float *_book_unquantize(const static_codebook *b){
-  long j,k;
+float *_book_unquantize(const static_codebook *b,int n,int *sparsemap){
+  long j,k,count=0;
   if(b->maptype==1 || b->maptype==2){
     int quantvals;
     float mindel=_float32_unpack(b->q_min);
     float delta=_float32_unpack(b->q_delta);
-    float *r=_ogg_calloc(b->entries*b->dim,sizeof(*r));
+    float *r=_ogg_calloc(n*b->dim,sizeof(*r));
 
     /* maptype 1 and 2 both use a quantized value vector, but
        different sizes */
@@ -250,26 +197,40 @@ float *_book_unquantize(const static_codebook *b){
         that */
       quantvals=_book_maptype1_quantvals(b);
       for(j=0;j<b->entries;j++){
-       float last=0.f;
-       int indexdiv=1;
-       for(k=0;k<b->dim;k++){
-         int index= (j/indexdiv)%quantvals;
-         float val=b->quantlist[index];
-         val=fabs(val)*delta+mindel+last;
-         if(b->q_sequencep)last=val;     
-         r[j*b->dim+k]=val;
-         indexdiv*=quantvals;
+       if((sparsemap && b->lengthlist[j]) || !sparsemap){
+         float last=0.f;
+         int indexdiv=1;
+         for(k=0;k<b->dim;k++){
+           int index= (j/indexdiv)%quantvals;
+           float val=b->quantlist[index];
+           val=fabs(val)*delta+mindel+last;
+           if(b->q_sequencep)last=val;   
+           if(sparsemap)
+             r[sparsemap[count]*b->dim+k]=val;
+           else
+             r[count*b->dim+k]=val;
+           indexdiv*=quantvals;
+         }
+         count++;
        }
+
       }
       break;
     case 2:
       for(j=0;j<b->entries;j++){
-       float last=0.f;
-       for(k=0;k<b->dim;k++){
-         float val=b->quantlist[j*b->dim+k];
-         val=fabs(val)*delta+mindel+last;
-         if(b->q_sequencep)last=val;     
-         r[j*b->dim+k]=val;
+       if((sparsemap && b->lengthlist[j]) || !sparsemap){
+         float last=0.f;
+         
+         for(k=0;k<b->dim;k++){
+           float val=b->quantlist[j*b->dim+k];
+           val=fabs(val)*delta+mindel+last;
+           if(b->q_sequencep)last=val;   
+           if(sparsemap)
+             r[sparsemap[count]*b->dim+k]=val;
+           else
+             r[count*b->dim+k]=val;
+         }
+         count++;
        }
       }
       break;
@@ -313,56 +274,125 @@ void vorbis_staticbook_destroy(static_codebook *b){
 void vorbis_book_clear(codebook *b){
   /* static book is not cleared; we're likely called on the lookup and
      the static codebook belongs to the info struct */
-  if(b->decode_tree){
-    _ogg_free(b->decode_tree->tab);
-    _ogg_free(b->decode_tree->tabl);
-
-    _ogg_free(b->decode_tree->ptr0);
-    _ogg_free(b->decode_tree->ptr1);
-    memset(b->decode_tree,0,sizeof(*b->decode_tree));
-    _ogg_free(b->decode_tree);
-  }
   if(b->valuelist)_ogg_free(b->valuelist);
   if(b->codelist)_ogg_free(b->codelist);
+
+  if(b->dec_index)_ogg_free(b->dec_index);
+  if(b->dec_codelengths)_ogg_free(b->dec_codelengths);
+  if(b->dec_firsttable)_ogg_free(b->dec_firsttable);
+
   memset(b,0,sizeof(*b));
 }
 
 int vorbis_book_init_encode(codebook *c,const static_codebook *s){
-  long j,k;
+
   memset(c,0,sizeof(*c));
   c->c=s;
   c->entries=s->entries;
+  c->used_entries=s->entries;
   c->dim=s->dim;
-  c->codelist=_make_words(s->lengthlist,s->entries);
-  c->valuelist=_book_unquantize(s);
-
-  /* set the 'zero entry' */
-  c->zeroentry=-1;
-  if(c->valuelist){
-    for(j=0;j<s->entries;j++){
-      int flag=1;
-      for(k=0;k<s->dim;k++){
-       if(fabs(c->valuelist[j*s->dim+k])>1e-12f){
-         flag=0;
-         break;
-       }
-      }
-      if(flag)
-       c->zeroentry=j;
-    }
-  }
+  c->codelist=_make_words(s->lengthlist,s->entries,0);
+  c->valuelist=_book_unquantize(s,s->entries,NULL);
 
   return(0);
 }
 
+static ogg_uint32_t bitreverse(ogg_uint32_t x){
+  x=    ((x>>16)&0x0000ffffUL) | ((x<<16)&0xffff0000UL);
+  x=    ((x>> 8)&0x00ff00ffUL) | ((x<< 8)&0xff00ff00UL);
+  x=    ((x>> 4)&0x0f0f0f0fUL) | ((x<< 4)&0xf0f0f0f0UL);
+  x=    ((x>> 2)&0x33333333UL) | ((x<< 2)&0xccccccccUL);
+  return((x>> 1)&0x55555555UL) | ((x<< 1)&0xaaaaaaaaUL);
+}
+
+static int sort32a(const void *a,const void *b){
+  return ( (**(ogg_uint32_t **)a>**(ogg_uint32_t **)b)<<1)-1;
+}
+
+
+/* decode codebook arrangement is more heavily optimized than encode */
 int vorbis_book_init_decode(codebook *c,const static_codebook *s){
+  int i,j,n=0,tabn;
+  int *sortindex;
   memset(c,0,sizeof(*c));
-  c->c=s;
+  
+  /* count actually used entries */
+  for(i=0;i<s->entries;i++)
+    if(s->lengthlist[i]>0)
+      n++;
+
   c->entries=s->entries;
+  c->used_entries=n;
   c->dim=s->dim;
-  c->valuelist=_book_unquantize(s);
-  c->decode_tree=_make_decode_tree(c);
-  if(c->decode_tree==NULL)goto err_out;
+
+  /* two different remappings go on here.  
+
+     First, we collapse the likely sparse codebook down only to
+     actually represented values/words.  This collapsing needs to be
+     indexed as map-valueless books are used to encode original entry
+     positions as integers.
+
+     Second, we reorder all vectors, including the entry index above,
+     by sorted bitreversed codeword to allow treeless decode. */
+
+  {
+    /* perform sort */
+    ogg_uint32_t *codes=_make_words(s->lengthlist,s->entries,c->used_entries);
+    ogg_uint32_t **codep=alloca(sizeof(*codep)*n);
+    
+    if(codes==NULL)goto err_out;
+
+    for(i=0;i<n;i++){
+      codes[i]=bitreverse(codes[i]);
+      codep[i]=codes+i;
+    }
+
+    qsort(codep,n,sizeof(*codep),sort32a);
+
+    sortindex=alloca(n*sizeof(*sortindex));
+    c->codelist=_ogg_malloc(n*sizeof(*c->codelist));
+    /* the index is a reverse index */
+    for(i=0;i<n;i++){
+      int position=codep[i]-codes;
+      sortindex[position]=i;
+    }
+
+    for(i=0;i<n;i++)
+      c->codelist[sortindex[i]]=codes[i];
+    _ogg_free(codes);
+  }
+
+  c->valuelist=_book_unquantize(s,n,sortindex);
+  c->dec_index=_ogg_malloc(n*sizeof(*c->dec_index));
+
+  for(n=0,i=0;i<s->entries;i++)
+    if(s->lengthlist[i]>0)
+      c->dec_index[sortindex[n++]]=i;
+  
+  c->dec_codelengths=_ogg_malloc(n*sizeof(*c->dec_codelengths));
+  for(n=0,i=0;i<s->entries;i++)
+    if(s->lengthlist[i]>0)
+      c->dec_codelengths[sortindex[n++]]=s->lengthlist[i];
+
+  c->dec_firsttablen=_ilog(c->used_entries)-4; /* this is magic */
+  if(c->dec_firsttablen<5)c->dec_firsttablen=5;
+  if(c->dec_firsttablen>8)c->dec_firsttablen=8;
+
+  tabn=1<<c->dec_firsttablen;
+  c->dec_firsttable=_ogg_malloc(tabn*sizeof(*c->dec_firsttable));
+  for(i=0;i<tabn;i++)c->dec_firsttable[i]=-1;
+  c->dec_maxlength=0;
+
+  for(i=0;i<n;i++){
+    if(c->dec_maxlength<c->dec_codelengths[i])
+      c->dec_maxlength=c->dec_codelengths[i];
+    if(c->dec_codelengths[i]<=c->dec_firsttablen){
+      ogg_uint32_t orig=bitreverse(c->codelist[i]);
+      for(j=0;j<(1<<(c->dec_firsttablen-c->dec_codelengths[i]));j++)
+       c->dec_firsttable[orig|(j<<c->dec_codelengths[i])]=i;
+    }
+  }
+
   return(0);
  err_out:
   vorbis_book_clear(c);
@@ -531,11 +561,17 @@ int vorbis_book_besterror(codebook *book,float *a,int step,int addmul){
 }
 
 long vorbis_book_codeword(codebook *book,int entry){
-  return book->codelist[entry];
+  if(book->c) /* only use with encode; decode optimizations are
+                 allowed to break this */
+    return book->codelist[entry];
+  return -1;
 }
 
 long vorbis_book_codelen(codebook *book,int entry){
-  return book->c->lengthlist[entry];
+  if(book->c) /* only use with encode; decode optimizations are
+                 allowed to break this */
+    return book->c->lengthlist[entry];
+  return -1;
 }
 
 #ifdef _V_SELFTEST
@@ -631,7 +667,7 @@ static float test5_result[]={-3,-6,-9, 4, 1,-2, -1,-4,-7,
                              -3,-4,-5, 4, 3, 2, -1,-2,-3};
 
 void run_test(static_codebook *b,float *comp){
-  float *out=_book_unquantize(b);
+  float *out=_book_unquantize(b,b->entries,NULL);
   int i;
 
   if(comp){