/* Emacs style mode select -*- C++ -*- *----------------------------------------------------------------------------- * * * PrBoom a Doom port merged with LxDoom and LSDLDoom * based on BOOM, a modified and improved DOOM engine * Copyright (C) 1999 by * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman * Copyright (C) 1999-2000 by * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * DESCRIPTION: * Zone Memory Allocation. Neat. * * Neat enough to be rewritten by Lee Killough... * * Must not have been real neat :) * * Made faster and more general, and added wrappers for all of Doom's * memory allocation functions, including malloc() and similar functions. * Added line and file numbers, in case of error. Added performance * statistics and tunables. *----------------------------------------------------------------------------- */ #include "z_zone.h" #include "z_bmalloc.h" #include "doomdef.h" #include "i_system.h" #include "rockmacros.h" #include "m_argv.h" // Tunables // Alignment of zone memory (benefit may be negated by HEADER_SIZE, CHUNK_SIZE) #define CACHE_ALIGN 32 // Minimum chunk size at which blocks are allocated #define CHUNK_SIZE 32 // Minimum size a block must be to become part of a split #define MIN_BLOCK_SPLIT (1024) // Minimum RAM machine is assumed to have /* cph - Select zone size. 6megs is usable, but with the SDL version * storing sounds in the zone, 8 is more sensible */ #define MIN_RAM (8*1024*1024) // Amount to subtract when retrying failed attempts to allocate initial pool #define RETRY_AMOUNT (256*1024) // signature for block header #define ZONEID 0x931d4a11 // Number of mallocs & frees kept in history buffer (must be a power of 2) #define ZONE_HISTORY 4 // End Tunables typedef struct memblock { #ifdef ZONEIDCHECK unsigned id; #endif struct memblock *next,*prev; size_t size; void **user; unsigned char tag,vm; #ifdef INSTRUMENTED unsigned short extra; const char *file; int line; #endif } memblock_t; /* size of block header * cph - base on sizeof(memblock_t), which can be larger than CHUNK_SIZE on * 64bit architectures */ static const size_t HEADER_SIZE IDATA_ATTR= (sizeof(memblock_t)+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); static memblock_t *rover IBSS_ATTR; // roving pointer to memory blocks static memblock_t *zone IBSS_ATTR; // pointer to first block static memblock_t *zonebase IBSS_ATTR; // pointer to entire zone memory static size_t zonebase_size IBSS_ATTR; // zone memory allocated size #ifdef INSTRUMENTED // statistics for evaluating performance static size_t free_memory; static size_t active_memory; static size_t purgable_memory; static size_t inactive_memory; static size_t virtual_memory; static void Z_PrintStats(void) // Print allocation statistics { unsigned long total_memory = free_memory + active_memory + purgable_memory + inactive_memory + virtual_memory; // double s = 100.0 / total_memory; int s = 100/total_memory; doom_printf("%-5u\t%6.01f%%\tstatic\n" "%-5u\t%6.01f%%\tpurgable\n" "%-5u\t%6.01f%%\tfree\n" "%-5u\t%6.01f%%\tfragmentary\n" "%-5u\t%6.01f%%\tvirtual\n" "%-5lu\t\ttotal\n", active_memory, active_memory*s, purgable_memory, purgable_memory*s, free_memory, free_memory*s, inactive_memory, inactive_memory*s, virtual_memory, virtual_memory*s, total_memory ); } #ifdef HEAPDUMP void W_PrintLump(FILE* fp, void* p); void Z_DumpMemory(void) { static int dump; memblock_t* block = zone; char buf[80]; FILE* fp; size_t total_cache = 0, total_free = 0, total_malloc = 0; sprintf(buf, "memdump.%d", dump++); fp = fopen(buf, "w"); do { switch (block->tag) { case PU_FREE: fprintf(fp, "free %d\n", block->size); total_free += block->size; break; case PU_CACHE: fprintf(fp, "cache %s:%d:%d\n", block->file, block->line, block->size); total_cache += block->size; break; case PU_LEVEL: fprintf(fp, "level %s:%d:%d\n", block->file, block->line, block->size); total_malloc += block->size; break; default: fprintf(fp, "malloc %s:%d:%d", block->file, block->line, block->size); total_malloc += block->size; if (!strcmp(block->file,"w_wad.c")) W_PrintLump(fp, (char*)block + HEADER_SIZE); fputc('\n', fp); break; } block=block->next; } while (block != zone); fprintf(fp, "malloc %d, cache %d, free %d, total %d\n", total_malloc, total_cache, total_free, total_malloc + total_cache + total_free); fclose(fp); } #endif #endif #ifdef INSTRUMENTED // killough 4/26/98: Add history information enum {malloc_history, free_history, NUM_HISTORY_TYPES}; static const char *file_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; static int line_history[NUM_HISTORY_TYPES][ZONE_HISTORY]; static int history_index[NUM_HISTORY_TYPES]; static const char *const desc[NUM_HISTORY_TYPES] = {"malloc()'s", "free()'s"}; void Z_DumpHistory(char *buf) { int i,j; char s[1024]; strcat(buf,"\n"); for (i=0;i= sizeof(memblock_t) && MIN_RAM > LEAVE_ASIDE)) I_Error("Z_Init: Sanity check failed"); #endif // atexit(Z_Close); // exit handler // Allocate the memory zonebase=rb->plugin_get_audio_buffer(&size); size-=2*(HEADER_SIZE + CACHE_ALIGN); // Leave space for header and CACHE_ALIGN size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size size += HEADER_SIZE + CACHE_ALIGN; zonebase_size=size; printf("Z_Init: Allocated %dKb zone memory\n", (long unsigned)size >> 10); // Align on cache boundary zone = (memblock_t *) ((unsigned long)zonebase + CACHE_ALIGN - ((unsigned long)zonebase & (CACHE_ALIGN-1))); rover = zone; // Rover points to base of zone mem zone->next = zone->prev = zone; // Single node zone->size = size; // All memory in one block zone->tag = PU_FREE; // A free block zone->vm = 0; #ifdef ZONEIDCHECK zone->id = 0; #endif #ifdef INSTRUMENTED free_memory = size; inactive_memory = zonebase_size - size; active_memory = purgable_memory = virtual_memory = 0; #endif } /* Z_Malloc * You can pass a NULL user if the tag is < PU_PURGELEVEL. * * cph - the algorithm here was a very simple first-fit round-robin * one - just keep looping around, freeing everything we can until * we get a large enough space * * This has been changed now; we still do the round-robin first-fit, * but we only free the blocks we actually end up using; we don't * free all the stuff we just pass on the way. */ void *(Z_Malloc)(size_t size, int tag, void **user #ifdef INSTRUMENTED , const char *file, int line #endif ) { register memblock_t *block; memblock_t *start, *first_of_free; register size_t contig_free; #ifdef INSTRUMENTED size_t size_orig = size; #ifdef CHECKHEAP Z_CheckHeap(); #endif file_history[malloc_history][history_index[malloc_history]] = file; line_history[malloc_history][history_index[malloc_history]++] = line; history_index[malloc_history] &= ZONE_HISTORY-1; #endif #ifdef ZONEIDCHECK if (tag >= PU_PURGELEVEL && !user) I_Error ("Z_Malloc: An owner is required for purgable blocks" #ifdef INSTRUMENTED "Source: %s:%d", file, line #endif ); #endif if (!size) return user ? *user = NULL : NULL; // malloc(0) returns NULL size = (size+CHUNK_SIZE-1) & ~(CHUNK_SIZE-1); // round to chunk size block = rover; if (block->prev->tag == PU_FREE) block = block->prev; start = block; first_of_free = NULL; contig_free = 0; do { /* If we just wrapped, we're not contiguous with the previous block */ if (block == zone) contig_free = 0; if (block->tag < PU_PURGELEVEL && block->tag != PU_FREE) { /* Not free(able), so no free space here */ contig_free = 0; } else { /* Add to contiguous chunk of free space */ if (!contig_free) first_of_free = block; contig_free += block->size; /* First fit */ if (contig_free >= size) break; } } while ((block = block->next) != start); // detect cycles as failure if (contig_free >= size) { /* We have a block of free(able) memory on the heap which will suffice */ block = first_of_free; /* If the previous block is adjacent and free, step back and include it */ if (block != zone && block->prev->tag == PU_FREE) block = block->prev; /* Free current block if needed */ if (block->tag != PU_FREE) Z_Free((char *) block + HEADER_SIZE); /* Note: guaranteed that block->prev is either * not free or not contiguous * * At every step, block->next must be not free, else it would * have been merged with our block * No range check needed because we know it works by the previous loop */ while (block->size < size) Z_Free((char *)(block->next) + HEADER_SIZE); /* Now, carve up the block */ { size_t extra = block->size - size; if (extra >= MIN_BLOCK_SPLIT + HEADER_SIZE) { memblock_t *newb = (memblock_t *)((char *) block + HEADER_SIZE + size); (newb->next = block->next)->prev = newb; (newb->prev = block)->next = newb; // Split up block block->size = size; newb->size = extra - HEADER_SIZE; newb->tag = PU_FREE; newb->vm = 0; #ifdef INSTRUMENTED inactive_memory += HEADER_SIZE; free_memory -= HEADER_SIZE; #endif } rover = block->next; // set roving pointer for next search #ifdef INSTRUMENTED inactive_memory += block->extra = block->size - size_orig; if (tag >= PU_PURGELEVEL) purgable_memory += size_orig; else active_memory += size_orig; free_memory -= block->size; #endif } } else { // We don't have enough contiguous free blocks I_Error ("Z_Malloc: Failure trying to allocate %d bytes",(unsigned long) size); rb->sleep(300); } #ifdef INSTRUMENTED block->file = file; block->line = line; #endif #ifdef ZONEIDCHECK block->id = ZONEID; // signature required in block header #endif block->tag = tag; // tag block->user = user; // user block = (memblock_t *)((char *) block + HEADER_SIZE); if (user) // if there is a user *user = block; // set user to point to new block #ifdef INSTRUMENTED Z_PrintStats(); // print memory allocation stats // scramble memory -- weed out any bugs memset(block, gametic & 0xff, size); #endif return block; } void (Z_Free)(void *p #ifdef INSTRUMENTED , const char *file, int line #endif ) { #ifdef INSTRUMENTED #ifdef CHECKHEAP Z_CheckHeap(); #endif file_history[free_history][history_index[free_history]] = file; line_history[free_history][history_index[free_history]++] = line; history_index[free_history] &= ZONE_HISTORY-1; #endif if (p) { memblock_t *other, *block = (memblock_t *)((char *) p - HEADER_SIZE); #ifdef ZONEIDCHECK if (block->id != ZONEID) I_Error("Z_Free: freed a pointer without ZONEID" #ifdef INSTRUMENTED "\nSource: %s:%d" "\nSource of malloc: %s:%d" , file, line, block->file, block->line #endif ); block->id = 0; // Nullify id so another free fails #endif #ifdef INSTRUMENTED /* scramble memory -- weed out any bugs */ memset(p, gametic & 0xff, block->size); #endif if (block->user) // Nullify user if one exists *block->user = NULL; { #ifdef INSTRUMENTED free_memory += block->size; inactive_memory -= block->extra; if (block->tag >= PU_PURGELEVEL) purgable_memory -= block->size - block->extra; else active_memory -= block->size - block->extra; #endif block->tag = PU_FREE; // Mark block freed if (block != zone) { other = block->prev; // Possibly merge with previous block if (other->tag == PU_FREE) { if (rover == block) // Move back rover if it points at block rover = other; (other->next = block->next)->prev = other; other->size += block->size + HEADER_SIZE; block = other; #ifdef INSTRUMENTED inactive_memory -= HEADER_SIZE; free_memory += HEADER_SIZE; #endif } } other = block->next; // Possibly merge with next block if (other->tag == PU_FREE && other != zone) { if (rover == other) // Move back rover if it points at next block rover = block; (block->next = other->next)->prev = block; block->size += other->size + HEADER_SIZE; #ifdef INSTRUMENTED inactive_memory -= HEADER_SIZE; free_memory += HEADER_SIZE; #endif } } #ifdef INSTRUMENTED Z_PrintStats(); // print memory allocation stats #endif } } void (Z_FreeTags)(int lowtag, int hightag #ifdef INSTRUMENTED , const char *file, int line #endif ) { /* cph - move rover to start of zone; we like to encourage static * data to stay in one place, at the start of the heap */ memblock_t *block = rover = zone; #ifdef HEAPDUMP Z_DumpMemory(); #endif if (lowtag <= PU_FREE) lowtag = PU_FREE+1; do // Scan through list, searching for tags in range if (block->tag >= lowtag && block->tag <= hightag) { memblock_t *prev = block->prev, *cur = block; #ifdef INSTRUMENTED (Z_Free)((char *) block + HEADER_SIZE, file, line); #else (Z_Free)((char *) block + HEADER_SIZE); #endif /* cph - be more careful here, we were skipping blocks! * If the current block was not merged with the previous, * cur is still a valid pointer, prev->next == cur, and cur is * already free so skip to the next. * If the current block was merged with the previous, * the next block to analyse is prev->next. * Note that the while() below does the actual step forward */ block = (prev->next == cur) ? cur : prev; } while ((block=block->next) != zone); } void (Z_ChangeTag)(void *ptr, int tag #ifdef INSTRUMENTED , const char *file, int line #endif ) { memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); #ifdef INSTRUMENTED #ifdef CHECKHEAP Z_CheckHeap(); #endif #endif #ifdef ZONEIDCHECK if (block->id != ZONEID) I_Error ("Z_ChangeTag: freed a pointer without ZONEID" #ifdef INSTRUMENTED "\nSource: %s:%d" "\nSource of malloc: %s:%d" , file, line, block->file, block->line #endif ); if (tag >= PU_PURGELEVEL && !block->user) I_Error ("Z_ChangeTag: an owner is required for purgable blocks\n" #ifdef INSTRUMENTED "Source: %s:%d" "\nSource of malloc: %s:%d" , file, line, block->file, block->line #endif ); #endif // ZONEIDCHECK { #ifdef INSTRUMENTED if (block->tag < PU_PURGELEVEL && tag >= PU_PURGELEVEL) { active_memory -= block->size - block->extra; purgable_memory += block->size - block->extra; } else if (block->tag >= PU_PURGELEVEL && tag < PU_PURGELEVEL) { active_memory += block->size - block->extra; purgable_memory -= block->size - block->extra; } #endif } block->tag = tag; } void *(Z_Realloc)(void *ptr, size_t n, int tag, void **user #ifdef INSTRUMENTED , const char *file, int line #endif ) { void *p = (Z_Malloc)(n, tag, user DA(file, line)); if (ptr) { memblock_t *block = (memblock_t *)((char *) ptr - HEADER_SIZE); memcpy(p, ptr, n <= block->size ? n : block->size); (Z_Free)(ptr DA(file, line)); if (user) // in case Z_Free nullified same user *user=p; } return p; } void *(Z_Calloc)(size_t n1, size_t n2, int tag, void **user #ifdef INSTRUMENTED , const char *file, int line #endif ) { return (n1*=n2) ? memset((Z_Malloc)(n1, tag, user DA(file, line)), 0, n1) : NULL; } char *(Z_Strdup)(const char *s, int tag, void **user #ifdef INSTRUMENTED , const char *file, int line #endif ) { return strcpy((Z_Malloc)(strlen(s)+1, tag, user DA(file, line)), s); } void (Z_CheckHeap)( #ifdef INSTRUMENTED const char *file, int line #endif ) { memblock_t *block = zone; // Start at base of zone mem do // Consistency check (last node treated special) if ((block->next != zone && (memblock_t *)((char *) block+HEADER_SIZE+block->size) != block->next) || block->next->prev != block || block->prev->next != block) I_Error("Z_ChkHp: B size %d touch %d\n", block+HEADER_SIZE+block->size, block->next #ifdef INSTRUMENTED "Source: %s:%d" "\nSource of offending block: %s:%d" , file, line, block->file, block->line #endif ); while ((block=block->next) != zone); } 492' href='#n492'>492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
////////////////////////////////////////////////////////////////////////////
//                           **** WAVPACK ****                            //
//                  Hybrid Lossless Wavefile Compressor                   //
//              Copyright (c) 1998 - 2004 Conifer Software.               //
//                          All Rights Reserved.                          //
////////////////////////////////////////////////////////////////////////////

// words.c

// This module provides entropy word encoding and decoding functions using
// a variation on the Rice method.  This was introduced in version 3.93
// because it allows splitting the data into a "lossy" stream and a
// "correction" stream in a very efficient manner and is therefore ideal
// for the "hybrid" mode.  For 4.0, the efficiency of this method was
// significantly improved by moving away from the normal Rice restriction of
// using powers of two for the modulus divisions and now the method can be
// used for both hybrid and pure lossless encoding.

// Samples are divided by median probabilities at 5/7 (71.43%), 10/49 (20.41%),
// and 20/343 (5.83%). Each zone has 3.5 times fewer samples than the
// previous. Using standard Rice coding on this data would result in 1.4
// bits per sample average (not counting sign bit). However, there is a
// very simple encoding that is over 99% efficient with this data and
// results in about 1.22 bits per sample.

#include "wavpack.h"

#include <string.h>

//////////////////////////////// local macros /////////////////////////////////

#define LIMIT_ONES 16   // maximum consecutive 1s sent for "div" data

// these control the time constant "slow_level" which is used for hybrid mode
// that controls bitrate as a function of residual level (HYBRID_BITRATE).
#define SLS 8
#define SLO ((1 << (SLS - 1)))

// these control the time constant of the 3 median level breakpoints
#define DIV0 128        // 5/7 of samples
#define DIV1 64         // 10/49 of samples
#define DIV2 32         // 20/343 of samples

// this macro retrieves the specified median breakpoint (without frac; min = 1)
#define GET_MED(med) (((c->median [med]) >> 4) + 1)

// These macros update the specified median breakpoints. Note that the median
// is incremented when the sample is higher than the median, else decremented.
// They are designed so that the median will never drop below 1 and the value
// is essentially stationary if there are 2 increments for every 5 decrements.

#define INC_MED0() (c->median [0] += ((c->median [0] + DIV0) / DIV0) * 5)
#define DEC_MED0() (c->median [0] -= ((c->median [0] + (DIV0-2)) / DIV0) * 2)
#define INC_MED1() (c->median [1] += ((c->median [1] + DIV1) / DIV1) * 5)
#define DEC_MED1() (c->median [1] -= ((c->median [1] + (DIV1-2)) / DIV1) * 2)
#define INC_MED2() (c->median [2] += ((c->median [2] + DIV2) / DIV2) * 5)
#define DEC_MED2() (c->median [2] -= ((c->median [2] + (DIV2-2)) / DIV2) * 2)

#define count_bits(av) ( \
 (av) < (1 << 8) ? nbits_table [av] : \
  ( \
   (av) < (1L << 16) ? nbits_table [(av) >> 8] + 8 : \
   ((av) < (1L << 24) ? nbits_table [(av) >> 16] + 16 : nbits_table [(av) >> 24] + 24) \
  ) \
)

///////////////////////////// local table storage ////////////////////////////

const char nbits_table [] = {
    0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,     // 0 - 15
    5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,     // 16 - 31
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,     // 32 - 47
    6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,     // 48 - 63
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,     // 64 - 79
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,     // 80 - 95
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,     // 96 - 111
    7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,     // 112 - 127
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 128 - 143
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 144 - 159
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 160 - 175
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 176 - 191
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 192 - 207
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 208 - 223
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,     // 224 - 239
    8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8      // 240 - 255
};

static const uchar log2_table [] = {
    0x00, 0x01, 0x03, 0x04, 0x06, 0x07, 0x09, 0x0a, 0x0b, 0x0d, 0x0e, 0x10, 0x11, 0x12, 0x14, 0x15,
    0x16, 0x18, 0x19, 0x1a, 0x1c, 0x1d, 0x1e, 0x20, 0x21, 0x22, 0x24, 0x25, 0x26, 0x28, 0x29, 0x2a,
    0x2c, 0x2d, 0x2e, 0x2f, 0x31, 0x32, 0x33, 0x34, 0x36, 0x37, 0x38, 0x39, 0x3b, 0x3c, 0x3d, 0x3e,
    0x3f, 0x41, 0x42, 0x43, 0x44, 0x45, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4d, 0x4e, 0x4f, 0x50, 0x51,
    0x52, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63,
    0x64, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x74, 0x75,
    0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85,
    0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
    0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4,
    0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 0xb2,
    0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc0,
    0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xce,
    0xcf, 0xd0, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd8, 0xd9, 0xda, 0xdb,
    0xdc, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe4, 0xe5, 0xe6, 0xe7, 0xe7,
    0xe8, 0xe9, 0xea, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xee, 0xef, 0xf0, 0xf1, 0xf1, 0xf2, 0xf3, 0xf4,
    0xf4, 0xf5, 0xf6, 0xf7, 0xf7, 0xf8, 0xf9, 0xf9, 0xfa, 0xfb, 0xfc, 0xfc, 0xfd, 0xfe, 0xff, 0xff
};

static const uchar exp2_table [] = {
    0x00, 0x01, 0x01, 0x02, 0x03, 0x03, 0x04, 0x05, 0x06, 0x06, 0x07, 0x08, 0x08, 0x09, 0x0a, 0x0b,
    0x0b, 0x0c, 0x0d, 0x0e, 0x0e, 0x0f, 0x10, 0x10, 0x11, 0x12, 0x13, 0x13, 0x14, 0x15, 0x16, 0x16,
    0x17, 0x18, 0x19, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1d, 0x1e, 0x1f, 0x20, 0x20, 0x21, 0x22, 0x23,
    0x24, 0x24, 0x25, 0x26, 0x27, 0x28, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3a, 0x3b, 0x3c, 0x3d,
    0x3e, 0x3f, 0x40, 0x41, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
    0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a,
    0x5b, 0x5c, 0x5d, 0x5e, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
    0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,
    0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x87, 0x88, 0x89, 0x8a,
    0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, 0x92, 0x93, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b,
    0x9c, 0x9d, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad,
    0xaf, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0,
    0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc8, 0xc9, 0xca, 0xcb, 0xcd, 0xce, 0xcf, 0xd0, 0xd2, 0xd3, 0xd4,
    0xd6, 0xd7, 0xd8, 0xd9, 0xdb, 0xdc, 0xdd, 0xde, 0xe0, 0xe1, 0xe2, 0xe4, 0xe5, 0xe6, 0xe8, 0xe9,
    0xea, 0xec, 0xed, 0xee, 0xf0, 0xf1, 0xf2, 0xf4, 0xf5, 0xf6, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xff
};

static const char ones_count_table [] = {
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,7,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,6,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,5,
    0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,8
};

///////////////////////////// executable code ////////////////////////////////

void init_words (WavpackStream *wps)
{
    CLEAR (wps->w);
}

static int mylog2 (unsigned int32_t avalue);

// Read the median log2 values from the specifed metadata structure, convert
// them back to 32-bit unsigned values and store them. If length is not
// exactly correct then we flag and return an error.

int read_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
{
    uchar *byteptr = wpmd->data;

    if (wpmd->byte_length != ((wps->wphdr.flags & MONO_DATA) ? 6 : 12))
        return FALSE;

    wps->w.c [0].median [0] = exp2s (byteptr [0] + (byteptr [1] << 8));
    wps->w.c [0].median [1] = exp2s (byteptr [2] + (byteptr [3] << 8));
    wps->w.c [0].median [2] = exp2s (byteptr [4] + (byteptr [5] << 8));

    if (!(wps->wphdr.flags & MONO_DATA)) {
        wps->w.c [1].median [0] = exp2s (byteptr [6] + (byteptr [7] << 8));
        wps->w.c [1].median [1] = exp2s (byteptr [8] + (byteptr [9] << 8));
        wps->w.c [1].median [2] = exp2s (byteptr [10] + (byteptr [11] << 8));
    }

    return TRUE;
}

// Allocates the correct space in the metadata structure and writes the
// current median values to it. Values are converted from 32-bit unsigned
// to our internal 16-bit mylog2 values, and read_entropy_vars () is called
// to read the values back because we must compensate for the loss through
// the log function.

void write_entropy_vars (WavpackStream *wps, WavpackMetadata *wpmd)
{
    uchar *byteptr;
    int temp;

    byteptr = wpmd->data = wpmd->temp_data;
    wpmd->id = ID_ENTROPY_VARS;

    *byteptr++ = temp = mylog2 (wps->w.c [0].median [0]);
    *byteptr++ = temp >> 8;
    *byteptr++ = temp = mylog2 (wps->w.c [0].median [1]);
    *byteptr++ = temp >> 8;
    *byteptr++ = temp = mylog2 (wps->w.c [0].median [2]);
    *byteptr++ = temp >> 8;

    if (!(wps->wphdr.flags & MONO_FLAG)) {
        *byteptr++ = temp = mylog2 (wps->w.c [1].median [0]);
        *byteptr++ = temp >> 8;
        *byteptr++ = temp = mylog2 (wps->w.c [1].median [1]);
        *byteptr++ = temp >> 8;
        *byteptr++ = temp = mylog2 (wps->w.c [1].median [2]);
        *byteptr++ = temp >> 8;
    }

    wpmd->byte_length = byteptr - (uchar *) wpmd->data;
    read_entropy_vars (wps, wpmd);
}

// Read the hybrid related values from the specifed metadata structure, convert
// them back to their internal formats and store them. The extended profile
// stuff is not implemented yet, so return an error if we get more data than
// we know what to do with.

int read_hybrid_profile (WavpackStream *wps, WavpackMetadata *wpmd)
{
    uchar *byteptr = wpmd->data;
    uchar *endptr = byteptr + wpmd->byte_length;

    if (wps->wphdr.flags & HYBRID_BITRATE) {
        wps->w.c [0].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8));
        byteptr += 2;

        if (!(wps->wphdr.flags & MONO_DATA)) {
            wps->w.c [1].slow_level = exp2s (byteptr [0] + (byteptr [1] << 8));
            byteptr += 2;
        }
    }

    wps->w.bitrate_acc [0] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
    byteptr += 2;

    if (!(wps->wphdr.flags & MONO_DATA)) {
        wps->w.bitrate_acc [1] = (int32_t)(byteptr [0] + (byteptr [1] << 8)) << 16;
        byteptr += 2;
    }

    if (byteptr < endptr) {
        wps->w.bitrate_delta [0] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
        byteptr += 2;

        if (!(wps->wphdr.flags & MONO_DATA)) {
            wps->w.bitrate_delta [1] = exp2s ((short)(byteptr [0] + (byteptr [1] << 8)));
            byteptr += 2;
        }

        if (byteptr < endptr)
            return FALSE;
    }
    else
        wps->w.bitrate_delta [0] = wps->w.bitrate_delta [1] = 0;

    return TRUE;
}

// This function is called during both encoding and decoding of hybrid data to
// update the "error_limit" variable which determines the maximum sample error
// allowed in the main bitstream. In the HYBRID_BITRATE mode (which is the only
// currently implemented) this is calculated from the slow_level values and the
// bitrate accumulators. Note that the bitrate accumulators can be changing.

static void update_error_limit (struct words_data *w, uint32_t flags)
{
    int bitrate_0 = (w->bitrate_acc [0] += w->bitrate_delta [0]) >> 16;

    if (flags & MONO_DATA) {
        if (flags & HYBRID_BITRATE) {
            int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS;

            if (slow_log_0 - bitrate_0 > -0x100)
                w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100);
            else
                w->c [0].error_limit = 0;
        }
        else
            w->c [0].error_limit = exp2s (bitrate_0);
    }
    else {
        int bitrate_1 = (w->bitrate_acc [1] += w->bitrate_delta [1]) >> 16;

        if (flags & HYBRID_BITRATE) {
            int slow_log_0 = (w->c [0].slow_level + SLO) >> SLS;
            int slow_log_1 = (w->c [1].slow_level + SLO) >> SLS;

            if (flags & HYBRID_BALANCE) {
                int balance = (slow_log_1 - slow_log_0 + bitrate_1 + 1) >> 1;

                if (balance > bitrate_0) {
                    bitrate_1 = bitrate_0 * 2;
                    bitrate_0 = 0;
                }
                else if (-balance > bitrate_0) {
                    bitrate_0 = bitrate_0 * 2;
                    bitrate_1 = 0;
                }
                else {
                    bitrate_1 = bitrate_0 + balance;
                    bitrate_0 = bitrate_0 - balance;
                }
            }

            if (slow_log_0 - bitrate_0 > -0x100)
                w->c [0].error_limit = exp2s (slow_log_0 - bitrate_0 + 0x100);
            else
                w->c [0].error_limit = 0;

            if (slow_log_1 - bitrate_1 > -0x100)
                w->c [1].error_limit = exp2s (slow_log_1 - bitrate_1 + 0x100);
            else
                w->c [1].error_limit = 0;
        }
        else {
            w->c [0].error_limit = exp2s (bitrate_0);
            w->c [1].error_limit = exp2s (bitrate_1);
        }
    }
}

static uint32_t read_code (Bitstream *bs, uint32_t maxcode);

// Read the next word from the bitstream "wvbits" and return the value. This
// function can be used for hybrid or lossless streams, but since an
// optimized version is available for lossless this function would normally
// be used for hybrid only. If a hybrid lossless stream is being read then
// the "correction" offset is written at the specified pointer. A return value
// of WORD_EOF indicates that the end of the bitstream was reached (all 1s) or
// some other error occurred.

int32_t get_words (int32_t *buffer, int nsamples, uint32_t flags,
                struct words_data *w, Bitstream *bs)
{
    register struct entropy_data *c = w->c;
    int csamples;

    if (!(flags & MONO_DATA))
        nsamples *= 2;

    for (csamples = 0; csamples < nsamples; ++csamples) {
        uint32_t ones_count, low, mid, high;

        if (!(flags & MONO_DATA))
            c = w->c + (csamples & 1);

        if (!(w->c [0].median [0] & ~1) && !w->holding_zero && !w->holding_one && !(w->c [1].median [0] & ~1)) {
            uint32_t mask;
            int cbits;

            if (w->zeros_acc) {
                if (--w->zeros_acc) {
                    c->slow_level -= (c->slow_level + SLO) >> SLS;
                    *buffer++ = 0;
                    continue;
                }
            }
            else {
                for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);

                if (cbits == 33)
                    break;

                if (cbits < 2)
                    w->zeros_acc = cbits;
                else {
                    for (mask = 1, w->zeros_acc = 0; --cbits; mask <<= 1)
                        if (getbit (bs))
                            w->zeros_acc |= mask;

                    w->zeros_acc |= mask;
                }

                if (w->zeros_acc) {
                    c->slow_level -= (c->slow_level + SLO) >> SLS;
                    CLEAR (w->c [0].median);
                    CLEAR (w->c [1].median);
                    *buffer++ = 0;
                    continue;
                }
            }
        }

        if (w->holding_zero)
            ones_count = w->holding_zero = 0;
        else {
            int next8;

            if (bs->bc < 8) {
                if (++(bs->ptr) == bs->end)
                    bs->wrap (bs);

                next8 = (bs->sr |= *(bs->ptr) << bs->bc) & 0xff;
                bs->bc += 8;
            }
            else
                next8 = bs->sr & 0xff;

            if (next8 == 0xff) {
                bs->bc -= 8;
                bs->sr >>= 8;

                for (ones_count = 8; ones_count < (LIMIT_ONES + 1) && getbit (bs); ++ones_count);

                if (ones_count == (LIMIT_ONES + 1))
                    break;

                if (ones_count == LIMIT_ONES) {
                    uint32_t mask;
                    int cbits;

                    for (cbits = 0; cbits < 33 && getbit (bs); ++cbits);

                    if (cbits == 33)
                        break;

                    if (cbits < 2)
                        ones_count = cbits;
                    else {
                        for (mask = 1, ones_count = 0; --cbits; mask <<= 1)
                            if (getbit (bs))
                                ones_count |= mask;

                        ones_count |= mask;
                    }

                    ones_count += LIMIT_ONES;
                }
            }
            else {
                bs->bc -= (ones_count = ones_count_table [next8]) + 1;
                bs->sr >>= ones_count + 1;
            }

            if (w->holding_one) {
                w->holding_one = ones_count & 1;
                ones_count = (ones_count >> 1) + 1;
            }
            else {
                w->holding_one = ones_count & 1;
                ones_count >>= 1;
            }

            w->holding_zero = ~w->holding_one & 1;
        }

        if ((flags & HYBRID_FLAG) && ((flags & MONO_DATA) || !(csamples & 1)))
            update_error_limit (w, flags);

        if (ones_count == 0) {
            low = 0;
            high = GET_MED (0) - 1;
            DEC_MED0 ();
        }
        else {
            low = GET_MED (0);
            INC_MED0 ();

            if (ones_count == 1) {
                high = low + GET_MED (1) - 1;
                DEC_MED1 ();
            }
            else {
                low += GET_MED (1);
                INC_MED1 ();

                if (ones_count == 2) {
                    high = low + GET_MED (2) - 1;
                    DEC_MED2 ();
                }
                else {
                    low += (ones_count - 2) * GET_MED (2);
                    high = low + GET_MED (2) - 1;
                    INC_MED2 ();
                }
            }
        }

        mid = (high + low + 1) >> 1;

        if (!c->error_limit)
            mid = read_code (bs, high - low) + low;
        else while (high - low > c->error_limit) {
            if (getbit (bs))
                mid = (high + (low = mid) + 1) >> 1;
            else
                mid = ((high = mid - 1) + low + 1) >> 1;
        }

        *buffer++ = getbit (bs) ? ~mid : mid;

        if (flags & HYBRID_BITRATE)
            c->slow_level = c->slow_level - ((c->slow_level + SLO) >> SLS) + mylog2 (mid);
    }

    return (flags & MONO_DATA) ? csamples : (csamples / 2);
}

// Read a single unsigned value from the specified bitstream with a value
// from 0 to maxcode. If there are exactly a power of two number of possible
// codes then this will read a fixed number of bits; otherwise it reads the
// minimum number of bits and then determines whether another bit is needed
// to define the code.

static uint32_t read_code (Bitstream *bs, uint32_t maxcode)
{
    int bitcount = count_bits (maxcode);
    uint32_t extras = (1L << bitcount) - maxcode - 1, code;

    if (!bitcount)
        return 0;

    getbits (&code, bitcount - 1, bs);
    code &= (1L << (bitcount - 1)) - 1;

    if (code >= extras) {
        code = (code << 1) - extras;

        if (getbit (bs))
            ++code;
    }

    return code;
}

void send_words (int32_t *buffer, int nsamples, uint32_t flags,
                 struct words_data *w, Bitstream *bs)
{
    register struct entropy_data *c = w->c;

    if (!(flags & MONO_FLAG))
        nsamples *= 2;

    while (nsamples--) {
        int32_t value = *buffer++;
        int sign = (value < 0) ? 1 : 0;
        uint32_t ones_count, low, high;

        if (!(flags & MONO_FLAG))
            c = w->c + (~nsamples & 1);

        if (!(w->c [0].median [0] & ~1) && !w->holding_zero && !(w->c [1].median [0] & ~1)) {
            if (w->zeros_acc) {
                if (value)
                    flush_word (w, bs);
                else {
                    w->zeros_acc++;
                    continue;
                }
            }
            else if (value) {
                putbit_0 (bs);
            }
            else {
                CLEAR (w->c [0].median);
                CLEAR (w->c [1].median);
                w->zeros_acc = 1;
                continue;
            }
        }

        if (sign)
            value = ~value;

        if ((unsigned int32_t) value < GET_MED (0)) {
            ones_count = low = 0;
            high = GET_MED (0) - 1;
            DEC_MED0 ();
        }
        else {
            low = GET_MED (0);
            INC_MED0 ();

            if (value - low < GET_MED (1)) {
                ones_count = 1;
                high = low + GET_MED (1) - 1;
                DEC_MED1 ();
            }
            else {
                low += GET_MED (1);
                INC_MED1 ();

                if (value - low < GET_MED (2)) {
                    ones_count = 2;
                    high = low + GET_MED (2) - 1;
                    DEC_MED2 ();
                }
                else {
                    ones_count = 2 + (value - low) / GET_MED (2);
                    low += (ones_count - 2) * GET_MED (2);
                    high = low + GET_MED (2) - 1;
                    INC_MED2 ();
                }
            }
        }

        if (w->holding_zero) {
            if (ones_count)
                w->holding_one++;

            flush_word (w, bs);

            if (ones_count) {
                w->holding_zero = 1;
                ones_count--;
            }
            else
                w->holding_zero = 0;
        }
        else
            w->holding_zero = 1;

        w->holding_one = ones_count * 2;

        if (high != low) {  
            uint32_t maxcode = high - low, code = value - low;
            int bitcount = count_bits (maxcode);
            uint32_t extras = (1L << bitcount) - maxcode - 1;

            if (code < extras) {
                w->pend_data |= code << w->pend_count;
                w->pend_count += bitcount - 1;
            }
            else {
                w->pend_data |= ((code + extras) >> 1) << w->pend_count;
                w->pend_count += bitcount - 1;
                w->pend_data |= ((code + extras) & 1) << w->pend_count++;
            }
        }

        w->pend_data |= ((int32_t) sign << w->pend_count++);

        if (!w->holding_zero)
            flush_word (w, bs);
    }
}

// Used by send_word() and send_word_lossless() to actually send most the
// accumulated data onto the bitstream. This is also called directly from
// clients when all words have been sent.

void flush_word (struct words_data *w, Bitstream *bs)
{
    int cbits;

    if (w->zeros_acc) {
        cbits = count_bits (w->zeros_acc);

        while (cbits--) {
            putbit_1 (bs);
        }

        putbit_0 (bs);

        while (w->zeros_acc > 1) {
            putbit (w->zeros_acc & 1, bs);
            w->zeros_acc >>= 1;
        }

        w->zeros_acc = 0;
    }

    if (w->holding_one) {
        if (w->holding_one >= LIMIT_ONES) {
            putbits ((1L << LIMIT_ONES) - 1, LIMIT_ONES + 1, bs);
            w->holding_one -= LIMIT_ONES;
            cbits = count_bits (w->holding_one);

            while (cbits--) {
                putbit_1 (bs);
            }

            putbit_0 (bs);

            while (w->holding_one > 1) {
                putbit (w->holding_one & 1, bs);
                w->holding_one >>= 1;
            }

            w->holding_zero = 0;
        }
        else
            putbits ((1L << w->holding_one) - 1, w->holding_one, bs);

        w->holding_one = 0;
    }

    if (w->holding_zero) {
        putbit_0 (bs);
        w->holding_zero = 0;
    }

    if (w->pend_count) {

        while (w->pend_count > 24) {
            putbit (w->pend_data & 1, bs);
            w->pend_data >>= 1;
            w->pend_count--;
        }

        putbits (w->pend_data, w->pend_count, bs);
        w->pend_data = w->pend_count = 0;
    }
}

// The concept of a base 2 logarithm is used in many parts of WavPack. It is
// a way of sufficiently accurately representing 32-bit signed and unsigned
// values storing only 16 bits (actually fewer). It is also used in the hybrid
// mode for quickly comparing the relative magnitude of large values (i.e.
// division) and providing smooth exponentials using only addition.

// These are not strict logarithms in that they become linear around zero and
// can therefore represent both zero and negative values. They have 8 bits
// of precision and in "roundtrip" conversions the total error never exceeds 1
// part in 225 except for the cases of +/-115 and +/-195 (which error by 1).


// This function returns the log2 for the specified 32-bit unsigned value.
// The maximum value allowed is about 0xff800000 and returns 8447.

static int mylog2 (unsigned int32_t avalue)
{
    int dbits;

    if ((avalue += avalue >> 9) < (1 << 8)) {
        dbits = nbits_table [avalue];
        return (dbits << 8) + log2_table [(avalue << (9 - dbits)) & 0xff];
    }
    else {
        if (avalue < (1L << 16))
            dbits = nbits_table [avalue >> 8] + 8;
        else if (avalue < (1L << 24))
            dbits = nbits_table [avalue >> 16] + 16;
        else
            dbits = nbits_table [avalue >> 24] + 24;

        return (dbits << 8) + log2_table [(avalue >> (dbits - 9)) & 0xff];
    }
}

// This function returns the log2 for the specified 32-bit signed value.
// All input values are valid and the return values are in the range of
// +/- 8192.

int log2s (int32_t value)
{
    return (value < 0) ? -mylog2 (-value) : mylog2 (value);
}

// This function returns the original integer represented by the supplied
// logarithm (at least within the provided accuracy). The log is signed,
// but since a full 32-bit value is returned this can be used for unsigned
// conversions as well (i.e. the input range is -8192 to +8447).

int32_t exp2s (int log)
{
    uint32_t value;

    if (log < 0)
        return -exp2s (-log);

    value = exp2_table [log & 0xff] | 0x100;

    if ((log >>= 8) <= 9)
        return value >> (9 - log);
    else
        return value << (log - 9);
}

// These two functions convert internal weights (which are normally +/-1024)
// to and from an 8-bit signed character version for storage in metadata. The
// weights are clipped here in the case that they are outside that range.

signed char store_weight (int weight)
{
    if (weight > 1024)
        weight = 1024;
    else if (weight < -1024)
        weight = -1024;

    if (weight > 0)
        weight -= (weight + 64) >> 7;

    return (weight + 4) >> 3;
}

int restore_weight (signed char weight)
{
    int result;

    if ((result = (int) weight << 3) > 0)
        result += (result + 64) >> 7;

    return result;
}