/************
*
* This file is part of a tool for producing 3D content in the PRC format.
* Copyright (C) 2008 Orest Shardt <shardtor (at) gmail dot com> and
* Michail Vidiassov <
[email protected]>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <
http://www.gnu.org/licenses/>.
*
*************/
#include "prc/PRCbitStream.h"
#include "prc/PRCdouble.h"
#include <cassert>
#include <iostream>
#include <stdlib.h>
#include <string.h>
#include <zlib.h>
using std::string;
using std::cerr;
using std::endl;
void PRCbitStream::compress()
{
const int CHUNK= 1024; // is this reasonable?
compressedDataSize = 0;
z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
if(deflateInit(&strm,Z_DEFAULT_COMPRESSION) != Z_OK)
{
cerr << "Compression initialization failed" << endl;
return;
}
unsigned int sizeAvailable = deflateBound(&strm,getSize());
uint8_t *compressedData = (uint8_t*) malloc(sizeAvailable);
strm.avail_in = getSize();
strm.next_in = (unsigned char*)data;
strm.next_out = (unsigned char*)compressedData;
strm.avail_out = sizeAvailable;
int code;
unsigned int chunks = 0;
while((code = deflate(&strm,Z_FINISH)) == Z_OK)
{
++chunks;
// strm.avail_out should be 0 if we got Z_OK
compressedDataSize = sizeAvailable - strm.avail_out;
compressedData = (uint8_t*) realloc(compressedData,CHUNK*chunks);
strm.next_out = (Bytef*)(compressedData + compressedDataSize);
strm.avail_out += CHUNK;
sizeAvailable += CHUNK;
}
compressedDataSize = sizeAvailable-strm.avail_out;
if(code != Z_STREAM_END)
{
cerr << "Compression error" << endl;
deflateEnd(&strm);
free(compressedData);
return;
}
compressed = true;
free(data);
data = compressedData;
deflateEnd(&strm);
}
void PRCbitStream::write(std::ostream &out) const
{
if(compressed)
{
out.write((char*)data,compressedDataSize);
}
else
{
cerr << "Attempt to write stream before compression." << endl;
exit(1);
}
}
unsigned int PRCbitStream::getSize() const
{
if(compressed)
return compressedDataSize;
else
return byteIndex+1;
}
uint8_t* PRCbitStream::getData()
{
return data;
}
PRCbitStream& PRCbitStream::operator <<(bool b)
{
writeBit(b);
return *this;
}
PRCbitStream& PRCbitStream::operator <<(uint32_t u)
{
while(u != 0)
{
writeBit(1);
writeByte(u & 0xFF);
u >>= 8;
}
writeBit(0);
return *this;
}
PRCbitStream& PRCbitStream::operator <<(uint8_t u)
{
writeByte(u);
return *this;
}
PRCbitStream& PRCbitStream::operator <<(int32_t i)
{
uint8_t lastByte = 0;
//while(!((current value is 0 and last byte was positive) OR (current value is -1 and last value was negative)))
while(!(((i == 0)&&((lastByte & 0x80)==0))||((i == -1)&&((lastByte & 0x80) != 0))))
{
writeBit(1);
lastByte = i & 0xFF;
writeByte(lastByte);
i >>= 8;
}
writeBit(0);
return *this;
}
PRCbitStream& PRCbitStream::operator <<(double value)
{
// write a double
if(compressed)
{
cerr << "Cannot write to a stream that has been compressed." << endl;
return *this;
}
union ieee754_double *pid=(union ieee754_double *)&value;
int
i,
fSaveAtEnd;
PRCbyte
*pb,
*pbStart,
*pbStop,
*pbEnd,
*pbResult,
bSaveAtEnd = 0;
struct sCodageOfFrequentDoubleOrExponent
cofdoe,
*pcofdoe;
cofdoe.u2uod.Value=value;
pcofdoe = (struct sCodageOfFrequentDoubleOrExponent *)bsearch(
&cofdoe,
acofdoe,
sizeof(acofdoe)/sizeof(pcofdoe[0]),
sizeof(pcofdoe[0]),
stCOFDOECompare);
while(pcofdoe>acofdoe && EXPONENT(pcofdoe->u2uod.Value)==EXPONENT((pcofdoe-1)->u2uod.Value))
pcofdoe--;
assert(pcofdoe);
while(pcofdoe->Type==VT_double)
{
if(fabs(value)==pcofdoe->u2uod.Value)
break;
pcofdoe++;
}
for(i=1<<(pcofdoe->NumberOfBits-1);i>=1;i>>=1)
writeBit((pcofdoe->Bits&i)!=0);
if
(
!memcmp(&value,stadwZero,sizeof(value))
|| !memcmp(&value,stadwNegativeZero,sizeof(value))
)
return *this;
writeBit(pid->ieee.negative);
if(pcofdoe->Type==VT_double)
return *this;
if(pid->ieee.mantissa0==0 && pid->ieee.mantissa1==0)
{
writeBit(0);
return *this;
}
writeBit(1);
#ifdef WORDS_BIGENDIAN
pb=((PRCbyte *)&value)+1;
#else
pb=((PRCbyte *)&value)+6;
#endif
//add_bits((*pb)&0x0f,4 STAT_V STAT_DOUBLE);
writeBits((*pb)&0x0F,4);
NEXTBYTE(pb);
pbStart=pb;
#ifdef WORDS_BIGENDIAN
pbEnd=
pbStop= ((PRCbyte *)(&value+1))-1;
#else
pbEnd=
pbStop= ((PRCbyte *)&value);
#endif
if((fSaveAtEnd=(*pbStop!=*BEFOREBYTE(pbStop)))!=0)
bSaveAtEnd=*pbEnd;
PREVIOUSBYTE(pbStop);
while(*pbStop==*BEFOREBYTE(pbStop))
PREVIOUSBYTE(pbStop);
for(;MOREBYTE(pb,pbStop);NEXTBYTE(pb))
{
if(pb!=pbStart && (pbResult=SEARCHBYTE(BEFOREBYTE(pb),*pb,DIFFPOINTERS(pb,pbStart)))!=NULL)
{
writeBit(0);
writeBits(DIFFPOINTERS(pb,pbResult),3);
}
else
{
writeBit(1);
writeByte(*pb);
}
}
if(!MOREBYTE(BEFOREBYTE(pbEnd),pbStop))
{
if(fSaveAtEnd)
{
writeBit(0);
writeBits(6,3);
writeByte(bSaveAtEnd);
}
else
{
writeBit(0);
writeBits(0,3);
}
}
else
{
if((pbResult=SEARCHBYTE(BEFOREBYTE(pb),*pb,DIFFPOINTERS(pb,pbStart)))!=NULL)
{
writeBit(0);
writeBits(DIFFPOINTERS(pb,pbResult),3);
}
else
{
writeBit(1);
writeByte(*pb);
}
}
return *this;
}
PRCbitStream& PRCbitStream::operator <<(const char* s)
{
if (s == NULL)
{
writeBit(false); // string is NULL
return *this;
}
string str(s);
*this << str;
return *this;
}
PRCbitStream& PRCbitStream::operator <<(const string& s)
{
if(s == "")
{
writeBit(false); // string is NULL
return *this;
}
writeBit(true);
size_t l = s.length();
*this << static_cast<uint32_t>(l);
for(size_t i = 0; i < l; ++i)
writeByte(s[i]);
return *this;
}
void PRCbitStream::writeBit(bool b)
{
if(compressed)
{
cerr << "Cannot write to a stream that has been compressed." << endl;
return;
}
if(b)
{
data[byteIndex] |= (0x80 >> bitIndex);
}
nextBit();
}
void PRCbitStream::writeBits(uint32_t u, uint8_t bits)
{
if(bits > 32)
return;
else
{
for(uint32_t mask = (1 << (bits-1)); mask != 0; mask >>= 1)
{
writeBit((u&mask) != 0);
}
}
}
void PRCbitStream::writeByte(uint8_t u)
{
if(compressed)
{
cerr << "Cannot write to a stream that has been compressed." << endl;
return;
}
if(bitIndex == 0)
{
data[byteIndex] = u;
nextByte();
}
else
{
data[byteIndex] |= (u >> bitIndex);
unsigned int obi = bitIndex;
nextByte();
data[byteIndex] |= (u << (8-obi));
bitIndex = obi; // bit index is not changed by writing 8 bits
}
}
void PRCbitStream::nextBit()
{
++bitIndex;
if(bitIndex == 8)
{
nextByte();
}
}
void PRCbitStream::nextByte()
{
++byteIndex;
if(byteIndex >= allocatedLength)
getAChunk();
data[byteIndex] = 0; // clear the garbage data
bitIndex = 0;
}
void PRCbitStream::getAChunk()
{
if(allocatedLength==0)
data = (uint8_t*)realloc((void*)data,CHUNK_SIZE);
else
data = (uint8_t*)realloc((void*)data,2*allocatedLength);
if(data != NULL)
{
if(allocatedLength==0)
{
allocatedLength = CHUNK_SIZE;
*data = 0; // clear first byte
}
else
allocatedLength *= 2;
}
else
{
// warn about memory problem!
cerr << "Memory allocation error." << endl;
exit(1);
}
}