382 lines
11 KiB
C++
382 lines
11 KiB
C++
// Copyright (C) 2005 Davis E. King (davis@dlib.net)
|
|
// License: Boost Software License See LICENSE.txt for the full license.
|
|
#ifndef DLIB_COMPRESS_STREAM_KERNEl_3_
|
|
#define DLIB_COMPRESS_STREAM_KERNEl_3_
|
|
|
|
#include "../algs.h"
|
|
#include "compress_stream_kernel_abstract.h"
|
|
#include "../assert.h"
|
|
|
|
namespace dlib
|
|
{
|
|
|
|
template <
|
|
typename lzp_buf,
|
|
typename crc32,
|
|
unsigned long buffer_size
|
|
>
|
|
class compress_stream_kernel_3
|
|
{
|
|
/*!
|
|
REQUIREMENTS ON lzp_buf
|
|
is an implementation of lzp_buffer/lzp_buffer_kernel_abstract.h
|
|
|
|
REQUIREMENTS ON buffer_size
|
|
10 < buffer_size < 32
|
|
|
|
REQUIREMENTS ON crc32
|
|
is an implementation of crc32/crc32_kernel_abstract.h
|
|
|
|
|
|
INITIAL VALUE
|
|
this object has no state
|
|
|
|
CONVENTION
|
|
this object has no state
|
|
|
|
|
|
This implementation uses the lzp_buffer and writes out matches
|
|
in a byte aligned format.
|
|
|
|
!*/
|
|
|
|
|
|
public:
|
|
|
|
class decompression_error : public dlib::error
|
|
{
|
|
public:
|
|
decompression_error(
|
|
const char* i
|
|
) :
|
|
dlib::error(std::string(i))
|
|
{}
|
|
|
|
decompression_error(
|
|
const std::string& i
|
|
) :
|
|
dlib::error(i)
|
|
{}
|
|
};
|
|
|
|
|
|
compress_stream_kernel_3 (
|
|
)
|
|
{
|
|
COMPILE_TIME_ASSERT(10 < buffer_size && buffer_size < 32);
|
|
}
|
|
|
|
~compress_stream_kernel_3 (
|
|
)
|
|
{}
|
|
|
|
void compress (
|
|
std::istream& in,
|
|
std::ostream& out
|
|
) const;
|
|
|
|
void decompress (
|
|
std::istream& in,
|
|
std::ostream& out
|
|
) const;
|
|
|
|
|
|
|
|
private:
|
|
|
|
inline void write (
|
|
unsigned char symbol
|
|
) const
|
|
{
|
|
if (out->sputn(reinterpret_cast<char*>(&symbol),1)==0)
|
|
throw std::ios_base::failure("error writing to output stream in compress_stream_kernel_3");
|
|
}
|
|
|
|
inline void decode (
|
|
unsigned char& symbol,
|
|
unsigned char& flag
|
|
) const
|
|
{
|
|
if (count == 0)
|
|
{
|
|
if (((size_t)in->sgetn(reinterpret_cast<char*>(buffer),sizeof(buffer)))!=sizeof(buffer))
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
count = 8;
|
|
}
|
|
--count;
|
|
symbol = buffer[8-count];
|
|
flag = buffer[0] >> 7;
|
|
buffer[0] <<= 1;
|
|
}
|
|
|
|
inline void encode (
|
|
unsigned char symbol,
|
|
unsigned char flag
|
|
) const
|
|
/*!
|
|
requires
|
|
- 0 <= flag <= 1
|
|
ensures
|
|
- writes symbol with the given one bit flag
|
|
!*/
|
|
{
|
|
// add this symbol and flag to the buffer
|
|
++count;
|
|
buffer[0] <<= 1;
|
|
buffer[count] = symbol;
|
|
buffer[0] |= flag;
|
|
|
|
if (count == 8)
|
|
{
|
|
if (((size_t)out->sputn(reinterpret_cast<char*>(buffer),sizeof(buffer)))!=sizeof(buffer))
|
|
throw std::ios_base::failure("error writing to output stream in compress_stream_kernel_3");
|
|
count = 0;
|
|
buffer[0] = 0;
|
|
}
|
|
}
|
|
|
|
void clear (
|
|
) const
|
|
/*!
|
|
ensures
|
|
- resets the buffers
|
|
!*/
|
|
{
|
|
count = 0;
|
|
}
|
|
|
|
void flush (
|
|
) const
|
|
/*!
|
|
ensures
|
|
- flushes any data in the buffers to out
|
|
!*/
|
|
{
|
|
if (count != 0)
|
|
{
|
|
buffer[0] <<= (8-count);
|
|
if (((size_t)out->sputn(reinterpret_cast<char*>(buffer),sizeof(buffer)))!=sizeof(buffer))
|
|
throw std::ios_base::failure("error writing to output stream in compress_stream_kernel_3");
|
|
}
|
|
}
|
|
|
|
mutable unsigned int count;
|
|
// count tells us how many bytes are buffered in buffer and how many flag
|
|
// bit are currently in buffer[0]
|
|
mutable unsigned char buffer[9];
|
|
// buffer[0] holds the flag bits to be writen.
|
|
// the rest of the buffer holds the bytes to be writen.
|
|
|
|
mutable std::streambuf* in;
|
|
mutable std::streambuf* out;
|
|
|
|
// restricted functions
|
|
compress_stream_kernel_3(compress_stream_kernel_3<lzp_buf,crc32,buffer_size>&); // copy constructor
|
|
compress_stream_kernel_3<lzp_buf,crc32,buffer_size>& operator=(compress_stream_kernel_3<lzp_buf,crc32,buffer_size>&); // assignment operator
|
|
|
|
};
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
// member function definitions
|
|
// ----------------------------------------------------------------------------------------
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename lzp_buf,
|
|
typename crc32,
|
|
unsigned long buffer_size
|
|
>
|
|
void compress_stream_kernel_3<lzp_buf,crc32,buffer_size>::
|
|
compress (
|
|
std::istream& in_,
|
|
std::ostream& out_
|
|
) const
|
|
{
|
|
in = in_.rdbuf();
|
|
out = out_.rdbuf();
|
|
clear();
|
|
|
|
crc32 crc;
|
|
|
|
lzp_buf buffer(buffer_size);
|
|
|
|
std::streambuf::int_type temp = in->sbumpc();
|
|
unsigned long index;
|
|
unsigned char symbol;
|
|
unsigned char length;
|
|
|
|
while (temp != EOF)
|
|
{
|
|
symbol = static_cast<unsigned char>(temp);
|
|
if (buffer.predict_match(index))
|
|
{
|
|
if (buffer[index] == symbol)
|
|
{
|
|
// this is a match so we must find out how long it is
|
|
length = 1;
|
|
|
|
buffer.add(symbol);
|
|
crc.add(symbol);
|
|
|
|
temp = in->sbumpc();
|
|
while (length < 255)
|
|
{
|
|
if (temp == EOF)
|
|
{
|
|
break;
|
|
}
|
|
else if (static_cast<unsigned long>(length) >= index)
|
|
{
|
|
break;
|
|
}
|
|
else if (static_cast<unsigned char>(temp) == buffer[index])
|
|
{
|
|
++length;
|
|
buffer.add(static_cast<unsigned char>(temp));
|
|
crc.add(static_cast<unsigned char>(temp));
|
|
temp = in->sbumpc();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
encode(length,1);
|
|
}
|
|
else
|
|
{
|
|
// this is also not a match
|
|
encode(symbol,0);
|
|
buffer.add(symbol);
|
|
crc.add(symbol);
|
|
|
|
// get the next symbol
|
|
temp = in->sbumpc();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// there wasn't a match so just write this symbol
|
|
encode(symbol,0);
|
|
buffer.add(symbol);
|
|
crc.add(symbol);
|
|
|
|
// get the next symbol
|
|
temp = in->sbumpc();
|
|
}
|
|
}
|
|
|
|
// use a match of zero length to indicate EOF
|
|
encode(0,1);
|
|
|
|
// now write the checksum
|
|
unsigned long checksum = crc.get_checksum();
|
|
unsigned char byte1 = static_cast<unsigned char>((checksum>>24)&0xFF);
|
|
unsigned char byte2 = static_cast<unsigned char>((checksum>>16)&0xFF);
|
|
unsigned char byte3 = static_cast<unsigned char>((checksum>>8)&0xFF);
|
|
unsigned char byte4 = static_cast<unsigned char>((checksum)&0xFF);
|
|
|
|
encode(byte1,0);
|
|
encode(byte2,0);
|
|
encode(byte3,0);
|
|
encode(byte4,0);
|
|
|
|
flush();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
template <
|
|
typename lzp_buf,
|
|
typename crc32,
|
|
unsigned long buffer_size
|
|
>
|
|
void compress_stream_kernel_3<lzp_buf,crc32,buffer_size>::
|
|
decompress (
|
|
std::istream& in_,
|
|
std::ostream& out_
|
|
) const
|
|
{
|
|
in = in_.rdbuf();
|
|
out = out_.rdbuf();
|
|
clear();
|
|
|
|
crc32 crc;
|
|
|
|
lzp_buf buffer(buffer_size);
|
|
|
|
|
|
unsigned long index = 0;
|
|
unsigned char symbol;
|
|
unsigned char length;
|
|
unsigned char flag;
|
|
|
|
decode(symbol,flag);
|
|
while (flag == 0 || symbol != 0)
|
|
{
|
|
buffer.predict_match(index);
|
|
|
|
if (flag == 1)
|
|
{
|
|
length = symbol;
|
|
do
|
|
{
|
|
--length;
|
|
symbol = buffer[index];
|
|
write(symbol);
|
|
buffer.add(symbol);
|
|
crc.add(symbol);
|
|
} while (length != 0);
|
|
}
|
|
else
|
|
{
|
|
// this is just a literal
|
|
write(symbol);
|
|
buffer.add(symbol);
|
|
crc.add(symbol);
|
|
}
|
|
decode(symbol,flag);
|
|
}
|
|
|
|
|
|
// now get the checksum and make sure it matches
|
|
unsigned char byte1;
|
|
unsigned char byte2;
|
|
unsigned char byte3;
|
|
unsigned char byte4;
|
|
|
|
decode(byte1,flag);
|
|
if (flag != 0)
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
decode(byte2,flag);
|
|
if (flag != 0)
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
decode(byte3,flag);
|
|
if (flag != 0)
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
decode(byte4,flag);
|
|
if (flag != 0)
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
|
|
unsigned long checksum = byte1;
|
|
checksum <<= 8;
|
|
checksum |= byte2;
|
|
checksum <<= 8;
|
|
checksum |= byte3;
|
|
checksum <<= 8;
|
|
checksum |= byte4;
|
|
|
|
if (checksum != crc.get_checksum())
|
|
throw decompression_error("Error detected in compressed data stream.");
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------------------------
|
|
|
|
}
|
|
|
|
#endif // DLIB_COMPRESS_STREAM_KERNEl_3_
|
|
|