383 lines
12 KiB
C
383 lines
12 KiB
C
/* Copyright (c) 2007-2011 Xiph.Org Foundation, Mozilla Corporation,
|
|
Gregory Maxwell
|
|
Written by Jean-Marc Valin, Gregory Maxwell, and Timothy B. Terriberry */
|
|
/*
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions
|
|
are met:
|
|
|
|
- Redistributions of source code must retain the above copyright
|
|
notice, this list of conditions and the following disclaimer.
|
|
|
|
- Redistributions in binary form must reproduce the above copyright
|
|
notice, this list of conditions and the following disclaimer in the
|
|
documentation and/or other materials provided with the distribution.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
|
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <math.h>
|
|
#include <time.h>
|
|
#include "entcode.h"
|
|
#include "entenc.h"
|
|
#include "entdec.h"
|
|
#include <string.h>
|
|
|
|
#include "entenc.c"
|
|
#include "entdec.c"
|
|
#include "entcode.c"
|
|
|
|
#ifndef M_LOG2E
|
|
# define M_LOG2E 1.4426950408889634074
|
|
#endif
|
|
#define DATA_SIZE 10000000
|
|
#define DATA_SIZE2 10000
|
|
|
|
int main(int _argc,char **_argv){
|
|
ec_enc enc;
|
|
ec_dec dec;
|
|
long nbits;
|
|
long nbits2;
|
|
double entropy;
|
|
int ft;
|
|
int ftb;
|
|
int sz;
|
|
int i;
|
|
int ret;
|
|
unsigned int sym;
|
|
unsigned int seed;
|
|
unsigned char *ptr;
|
|
const char *env_seed;
|
|
ret=0;
|
|
entropy=0;
|
|
if (_argc > 2) {
|
|
fprintf(stderr, "Usage: %s [<seed>]\n", _argv[0]);
|
|
return 1;
|
|
}
|
|
env_seed = getenv("SEED");
|
|
if (_argc > 1)
|
|
seed = atoi(_argv[1]);
|
|
else if (env_seed)
|
|
seed = atoi(env_seed);
|
|
else
|
|
seed = time(NULL);
|
|
/*Testing encoding of raw bit values.*/
|
|
ptr = (unsigned char *)malloc(DATA_SIZE);
|
|
ec_enc_init(&enc,ptr, DATA_SIZE);
|
|
for(ft=2;ft<1024;ft++){
|
|
for(i=0;i<ft;i++){
|
|
entropy+=log(ft)*M_LOG2E;
|
|
ec_enc_uint(&enc,i,ft);
|
|
}
|
|
}
|
|
/*Testing encoding of raw bit values.*/
|
|
for(ftb=1;ftb<16;ftb++){
|
|
for(i=0;i<(1<<ftb);i++){
|
|
entropy+=ftb;
|
|
nbits=ec_tell(&enc);
|
|
ec_enc_bits(&enc,i,ftb);
|
|
nbits2=ec_tell(&enc);
|
|
if(nbits2-nbits!=ftb){
|
|
fprintf(stderr,"Used %li bits to encode %i bits directly.\n",
|
|
nbits2-nbits,ftb);
|
|
ret=-1;
|
|
}
|
|
}
|
|
}
|
|
nbits=ec_tell_frac(&enc);
|
|
ec_enc_done(&enc);
|
|
fprintf(stderr,
|
|
"Encoded %0.2lf bits of entropy to %0.2lf bits (%0.3lf%% wasted).\n",
|
|
entropy,ldexp(nbits,-3),100*(nbits-ldexp(entropy,3))/nbits);
|
|
fprintf(stderr,"Packed to %li bytes.\n",(long)ec_range_bytes(&enc));
|
|
ec_dec_init(&dec,ptr,DATA_SIZE);
|
|
for(ft=2;ft<1024;ft++){
|
|
for(i=0;i<ft;i++){
|
|
sym=ec_dec_uint(&dec,ft);
|
|
if(sym!=(unsigned)i){
|
|
fprintf(stderr,"Decoded %i instead of %i with ft of %i.\n",sym,i,ft);
|
|
ret=-1;
|
|
}
|
|
}
|
|
}
|
|
for(ftb=1;ftb<16;ftb++){
|
|
for(i=0;i<(1<<ftb);i++){
|
|
sym=ec_dec_bits(&dec,ftb);
|
|
if(sym!=(unsigned)i){
|
|
fprintf(stderr,"Decoded %i instead of %i with ftb of %i.\n",sym,i,ftb);
|
|
ret=-1;
|
|
}
|
|
}
|
|
}
|
|
nbits2=ec_tell_frac(&dec);
|
|
if(nbits!=nbits2){
|
|
fprintf(stderr,
|
|
"Reported number of bits used was %0.2lf, should be %0.2lf.\n",
|
|
ldexp(nbits2,-3),ldexp(nbits,-3));
|
|
ret=-1;
|
|
}
|
|
/*Testing an encoder bust prefers range coder data over raw bits.
|
|
This isn't a general guarantee, will only work for data that is buffered in
|
|
the encoder state and not yet stored in the user buffer, and should never
|
|
get used in practice.
|
|
It's mostly here for code coverage completeness.*/
|
|
/*Start with a 16-bit buffer.*/
|
|
ec_enc_init(&enc,ptr,2);
|
|
/*Write 7 raw bits.*/
|
|
ec_enc_bits(&enc,0x55,7);
|
|
/*Write 12.3 bits of range coder data.*/
|
|
ec_enc_uint(&enc,1,2);
|
|
ec_enc_uint(&enc,1,3);
|
|
ec_enc_uint(&enc,1,4);
|
|
ec_enc_uint(&enc,1,5);
|
|
ec_enc_uint(&enc,2,6);
|
|
ec_enc_uint(&enc,6,7);
|
|
ec_enc_done(&enc);
|
|
ec_dec_init(&dec,ptr,2);
|
|
if(!enc.error
|
|
/*The raw bits should have been overwritten by the range coder data.*/
|
|
||ec_dec_bits(&dec,7)!=0x05
|
|
/*And all the range coder data should have been encoded correctly.*/
|
|
||ec_dec_uint(&dec,2)!=1
|
|
||ec_dec_uint(&dec,3)!=1
|
|
||ec_dec_uint(&dec,4)!=1
|
|
||ec_dec_uint(&dec,5)!=1
|
|
||ec_dec_uint(&dec,6)!=2
|
|
||ec_dec_uint(&dec,7)!=6){
|
|
fprintf(stderr,"Encoder bust overwrote range coder data with raw bits.\n");
|
|
ret=-1;
|
|
}
|
|
srand(seed);
|
|
fprintf(stderr,"Testing random streams... Random seed: %u (%.4X)\n", seed, rand() % 65536);
|
|
for(i=0;i<409600;i++){
|
|
unsigned *data;
|
|
unsigned *tell;
|
|
unsigned tell_bits;
|
|
int j;
|
|
int zeros;
|
|
ft=rand()/((RAND_MAX>>(rand()%11U))+1U)+10;
|
|
sz=rand()/((RAND_MAX>>(rand()%9U))+1U);
|
|
data=(unsigned *)malloc(sz*sizeof(*data));
|
|
tell=(unsigned *)malloc((sz+1)*sizeof(*tell));
|
|
ec_enc_init(&enc,ptr,DATA_SIZE2);
|
|
zeros = rand()%13==0;
|
|
tell[0]=ec_tell_frac(&enc);
|
|
for(j=0;j<sz;j++){
|
|
if (zeros)
|
|
data[j]=0;
|
|
else
|
|
data[j]=rand()%ft;
|
|
ec_enc_uint(&enc,data[j],ft);
|
|
tell[j+1]=ec_tell_frac(&enc);
|
|
}
|
|
if (rand()%2==0)
|
|
while(ec_tell(&enc)%8 != 0)
|
|
ec_enc_uint(&enc, rand()%2, 2);
|
|
tell_bits = ec_tell(&enc);
|
|
ec_enc_done(&enc);
|
|
if(tell_bits!=(unsigned)ec_tell(&enc)){
|
|
fprintf(stderr,"ec_tell() changed after ec_enc_done(): %i instead of %i (Random seed: %u)\n",
|
|
ec_tell(&enc),tell_bits,seed);
|
|
ret=-1;
|
|
}
|
|
if ((tell_bits+7)/8 < ec_range_bytes(&enc))
|
|
{
|
|
fprintf (stderr, "ec_tell() lied, there's %i bytes instead of %d (Random seed: %u)\n",
|
|
ec_range_bytes(&enc), (tell_bits+7)/8,seed);
|
|
ret=-1;
|
|
}
|
|
ec_dec_init(&dec,ptr,DATA_SIZE2);
|
|
if(ec_tell_frac(&dec)!=tell[0]){
|
|
fprintf(stderr,
|
|
"Tell mismatch between encoder and decoder at symbol %i: %i instead of %i (Random seed: %u).\n",
|
|
0,ec_tell_frac(&dec),tell[0],seed);
|
|
}
|
|
for(j=0;j<sz;j++){
|
|
sym=ec_dec_uint(&dec,ft);
|
|
if(sym!=data[j]){
|
|
fprintf(stderr,
|
|
"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_tell_frac(&dec)!=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_tell_frac(&dec),tell[j+1],seed);
|
|
}
|
|
}
|
|
free(tell);
|
|
free(data);
|
|
}
|
|
/*Test compatibility between multiple different encode/decode routines.*/
|
|
for(i=0;i<409600;i++){
|
|
unsigned *logp1;
|
|
unsigned *data;
|
|
unsigned *tell;
|
|
unsigned *enc_method;
|
|
int j;
|
|
sz=rand()/((RAND_MAX>>(rand()%9U))+1U);
|
|
logp1=(unsigned *)malloc(sz*sizeof(*logp1));
|
|
data=(unsigned *)malloc(sz*sizeof(*data));
|
|
tell=(unsigned *)malloc((sz+1)*sizeof(*tell));
|
|
enc_method=(unsigned *)malloc(sz*sizeof(*enc_method));
|
|
ec_enc_init(&enc,ptr,DATA_SIZE2);
|
|
tell[0]=ec_tell_frac(&enc);
|
|
for(j=0;j<sz;j++){
|
|
data[j]=rand()/((RAND_MAX>>1)+1);
|
|
logp1[j]=(rand()%15)+1;
|
|
enc_method[j]=rand()/((RAND_MAX>>2)+1);
|
|
switch(enc_method[j]){
|
|
case 0:{
|
|
ec_encode(&enc,data[j]?(1<<logp1[j])-1:0,
|
|
(1<<logp1[j])-(data[j]?0:1),1<<logp1[j]);
|
|
}break;
|
|
case 1:{
|
|
ec_encode_bin(&enc,data[j]?(1<<logp1[j])-1:0,
|
|
(1<<logp1[j])-(data[j]?0:1),logp1[j]);
|
|
}break;
|
|
case 2:{
|
|
ec_enc_bit_logp(&enc,data[j],logp1[j]);
|
|
}break;
|
|
case 3:{
|
|
unsigned char icdf[2];
|
|
icdf[0]=1;
|
|
icdf[1]=0;
|
|
ec_enc_icdf(&enc,data[j],icdf,logp1[j]);
|
|
}break;
|
|
}
|
|
tell[j+1]=ec_tell_frac(&enc);
|
|
}
|
|
ec_enc_done(&enc);
|
|
if((ec_tell(&enc)+7U)/8U<ec_range_bytes(&enc)){
|
|
fprintf(stderr,"tell() lied, there's %i bytes instead of %d (Random seed: %u)\n",
|
|
ec_range_bytes(&enc),(ec_tell(&enc)+7)/8,seed);
|
|
ret=-1;
|
|
}
|
|
ec_dec_init(&dec,ptr,DATA_SIZE2);
|
|
if(ec_tell_frac(&dec)!=tell[0]){
|
|
fprintf(stderr,
|
|
"Tell mismatch between encoder and decoder at symbol %i: %i instead of %i (Random seed: %u).\n",
|
|
0,ec_tell_frac(&dec),tell[0],seed);
|
|
}
|
|
for(j=0;j<sz;j++){
|
|
int fs;
|
|
int dec_method;
|
|
dec_method=rand()/((RAND_MAX>>2)+1);
|
|
switch(dec_method){
|
|
case 0:{
|
|
fs=ec_decode(&dec,1<<logp1[j]);
|
|
sym=fs>=(1<<logp1[j])-1;
|
|
ec_dec_update(&dec,sym?(1<<logp1[j])-1:0,
|
|
(1<<logp1[j])-(sym?0:1),1<<logp1[j]);
|
|
}break;
|
|
case 1:{
|
|
fs=ec_decode_bin(&dec,logp1[j]);
|
|
sym=fs>=(1<<logp1[j])-1;
|
|
ec_dec_update(&dec,sym?(1<<logp1[j])-1:0,
|
|
(1<<logp1[j])-(sym?0:1),1<<logp1[j]);
|
|
}break;
|
|
case 2:{
|
|
sym=ec_dec_bit_logp(&dec,logp1[j]);
|
|
}break;
|
|
case 3:{
|
|
unsigned char icdf[2];
|
|
icdf[0]=1;
|
|
icdf[1]=0;
|
|
sym=ec_dec_icdf(&dec,icdf,logp1[j]);
|
|
}break;
|
|
}
|
|
if(sym!=data[j]){
|
|
fprintf(stderr,
|
|
"Decoded %i instead of %i with logp1 of %i at position %i of %i (Random seed: %u).\n",
|
|
sym,data[j],logp1[j],j,sz,seed);
|
|
fprintf(stderr,"Encoding method: %i, decoding method: %i\n",
|
|
enc_method[j],dec_method);
|
|
ret=-1;
|
|
}
|
|
if(ec_tell_frac(&dec)!=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_tell_frac(&dec),tell[j+1],seed);
|
|
}
|
|
}
|
|
free(enc_method);
|
|
free(tell);
|
|
free(data);
|
|
free(logp1);
|
|
}
|
|
ec_enc_init(&enc,ptr,DATA_SIZE2);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,0,2);
|
|
ec_enc_patch_initial_bits(&enc,3,2);
|
|
if(enc.error){
|
|
fprintf(stderr,"patch_initial_bits failed");
|
|
ret=-1;
|
|
}
|
|
ec_enc_patch_initial_bits(&enc,0,5);
|
|
if(!enc.error){
|
|
fprintf(stderr,"patch_initial_bits didn't fail when it should have");
|
|
ret=-1;
|
|
}
|
|
ec_enc_done(&enc);
|
|
if(ec_range_bytes(&enc)!=1||ptr[0]!=192){
|
|
fprintf(stderr,"Got %d when expecting 192 for patch_initial_bits",ptr[0]);
|
|
ret=-1;
|
|
}
|
|
ec_enc_init(&enc,ptr,DATA_SIZE2);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,0,1);
|
|
ec_enc_bit_logp(&enc,1,6);
|
|
ec_enc_bit_logp(&enc,0,2);
|
|
ec_enc_patch_initial_bits(&enc,0,2);
|
|
if(enc.error){
|
|
fprintf(stderr,"patch_initial_bits failed");
|
|
ret=-1;
|
|
}
|
|
ec_enc_done(&enc);
|
|
if(ec_range_bytes(&enc)!=2||ptr[0]!=63){
|
|
fprintf(stderr,"Got %d when expecting 63 for patch_initial_bits",ptr[0]);
|
|
ret=-1;
|
|
}
|
|
ec_enc_init(&enc,ptr,2);
|
|
ec_enc_bit_logp(&enc,0,2);
|
|
for(i=0;i<48;i++){
|
|
ec_enc_bits(&enc,0,1);
|
|
}
|
|
ec_enc_done(&enc);
|
|
if(!enc.error){
|
|
fprintf(stderr,"Raw bits overfill didn't fail when it should have");
|
|
ret=-1;
|
|
}
|
|
ec_enc_init(&enc,ptr,2);
|
|
for(i=0;i<17;i++){
|
|
ec_enc_bits(&enc,0,1);
|
|
}
|
|
ec_enc_done(&enc);
|
|
if(!enc.error){
|
|
fprintf(stderr,"17 raw bits encoded in two bytes");
|
|
ret=-1;
|
|
}
|
|
free(ptr);
|
|
return ret;
|
|
}
|