2023-01-10 10:50:12 +01:00
|
|
|
|
#include "Compression.h"
|
|
|
|
|
|
#include "MultiThreading.h"
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Internal basic (de)compression routines *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// (De)compress data with supplied method and report operation time via "time"
|
|
|
|
|
|
// callback
|
|
|
|
|
|
static int timed_de_compress(COMPRESSION direction,
|
|
|
|
|
|
COMPRESSION_METHOD *compressor,
|
|
|
|
|
|
CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
// SET_JMP_POINT( FREEARC_ERRCODE_GENERAL);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
double time0 = GetThreadCPUTime();
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result =
|
|
|
|
|
|
(direction == COMPRESS ? compressor->compress(callback, auxdata)
|
|
|
|
|
|
: compressor->decompress(callback, auxdata));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#else
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result =
|
|
|
|
|
|
(direction == COMPRESS ? FREEARC_ERRCODE_ONLY_DECOMPRESS
|
|
|
|
|
|
: compressor->decompress(callback, auxdata));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
double time1 = GetThreadCPUTime(), t;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (time0 >= 0 && time1 >= 0 && compressor->addtime >= 0)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
t = compressor->addtime + time1 - time0;
|
|
|
|
|
|
else
|
|
|
|
|
|
t = -1;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
callback("time", &t, 0, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// (De)compress data with method encoded as string
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static int single_de_compress(COMPRESSION direction, char *method,
|
|
|
|
|
|
CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
COMPRESSION_METHOD *compressor = ParseCompressionMethod(method);
|
|
|
|
|
|
if (compressor) {
|
|
|
|
|
|
int result = timed_de_compress(direction, compressor, callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
delete compressor;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return result;
|
|
|
|
|
|
} else
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Механизм передачи данных от одного алгоритма (рас)паковки другому *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct AsOutput {
|
|
|
|
|
|
virtual int Write(BYTE *buf, int size) = 0;
|
|
|
|
|
|
virtual void NoMoreWrites() {}
|
|
|
|
|
|
};
|
|
|
|
|
|
struct AsInput {
|
|
|
|
|
|
virtual int Read(BYTE *buf, int size) = 0;
|
|
|
|
|
|
virtual void NoMoreReads() {}
|
|
|
|
|
|
};
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct Channels : AsOutput, AsInput {
|
|
|
|
|
|
BYTE *buf; // Buffer pointing to the data sent between methods
|
|
|
|
|
|
int size; // Amount of data in the buf
|
|
|
|
|
|
Semaphore read; // Semaphores for inter-thread communication
|
|
|
|
|
|
Semaphore write;
|
|
|
|
|
|
bool NoMoreData;
|
|
|
|
|
|
|
|
|
|
|
|
Channels() {
|
|
|
|
|
|
NoMoreData = false;
|
|
|
|
|
|
read.Create(0, 1);
|
|
|
|
|
|
write.Create(0, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
void NoMoreReads() {
|
|
|
|
|
|
NoMoreData = true;
|
|
|
|
|
|
write.Release();
|
|
|
|
|
|
} // Tell the output thread that no more data required
|
|
|
|
|
|
void NoMoreWrites() {
|
|
|
|
|
|
NoMoreData = true;
|
|
|
|
|
|
read.Release();
|
|
|
|
|
|
} // Tell the input thread that no more data will be supplied to it
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Послать _size байт из _buf
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int Write(BYTE *_buf, int _size) {
|
|
|
|
|
|
buf = _buf;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
size = _size;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
read.Release(); // даём разрешение на чтение (в буфере появились данные)
|
|
|
|
|
|
if (!NoMoreData)
|
|
|
|
|
|
write.Wait(); // ожидаем разрешения на выход (после того, как все данные
|
|
|
|
|
|
// будут прочитаны)
|
|
|
|
|
|
return NoMoreData ? FREEARC_ERRCODE_NO_MORE_DATA_REQUIRED : _size;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Получить _size байт в _buf. _size==0 используется для проверки готовности к
|
|
|
|
|
|
// чтению
|
|
|
|
|
|
int Read(BYTE *_buf, int _size) {
|
|
|
|
|
|
int prev = 0;
|
|
|
|
|
|
loop:
|
|
|
|
|
|
// if (_size==0) return prev;
|
|
|
|
|
|
read.Wait(); // ожидаем разрешения на чтение (появления данных в буфере)
|
|
|
|
|
|
if (NoMoreData) // данных больше не будет - предыдущий тред завершён
|
2023-01-10 10:50:12 +01:00
|
|
|
|
{
|
2023-01-11 10:08:11 +01:00
|
|
|
|
read.Release(); // возвращаем разрешение на чтение
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return prev;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
} else if (_size <=
|
|
|
|
|
|
size) // данных в буфере достаточно для выполнения чтения
|
2023-01-10 10:50:12 +01:00
|
|
|
|
{
|
2023-01-11 10:08:11 +01:00
|
|
|
|
memcpy(_buf, buf, _size);
|
|
|
|
|
|
buf += _size;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
size -= _size;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
read.Release(); // возвращаем разрешение на чтение
|
|
|
|
|
|
return prev + _size;
|
|
|
|
|
|
} else // size < _size // данных в буфере недостаточно для выполнения
|
|
|
|
|
|
// чтения
|
2023-01-10 10:50:12 +01:00
|
|
|
|
{
|
2023-01-11 10:08:11 +01:00
|
|
|
|
memcpy(_buf, buf, size);
|
|
|
|
|
|
_buf += size;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
_size -= size;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
prev += size;
|
|
|
|
|
|
write.Release(); // даём разрешение на выход из записи (буфер пуст)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
goto loop;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Multi-stream input/output classes
|
|
|
|
|
|
// ******************************************************************************************
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Формат многопоточного файла: [Заголовок] [Данные]
|
|
|
|
|
|
// Заголовок ::= [Смещение к началу потока 1]..[Смещение к началу потока N-1]
|
|
|
|
|
|
// Данные ::= [Кусочек потока 0] [Кусочек потока i] [Кусочек потока j]...
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Кусочек потока k ::= [Длина кусочка] [Данные кусочка] [Смещение к
|
|
|
|
|
|
// следующему кусочку того же потока]
|
|
|
|
|
|
// Длины/смещения записываются 4-байтными числами в Intel-формате. 0
|
|
|
|
|
|
// используется как спецфлаг, реальные значения перед записью увеличиваются
|
|
|
|
|
|
// на 10. Спецфлаг в поле смещения озгначает, что кусочков этого потока больше
|
|
|
|
|
|
// нет. Спецфлаг в поле длины означает, что этот кусочек длится до конца файла.
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Перейти на заданную позицию во входном/выходном файле
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#define SEEK_OUTPUT(pos) SEEK("seek_output", pos)
|
|
|
|
|
|
#define SEEK_INPUT(pos) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
if (!(pos).empty && (pos).n != curpos) { \
|
|
|
|
|
|
SEEK("seek_input", (pos).n); \
|
|
|
|
|
|
curpos = (pos).n; \
|
|
|
|
|
|
} \
|
|
|
|
|
|
}
|
|
|
|
|
|
#define SEEK(request, pos) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
int64 localPos = (pos); \
|
|
|
|
|
|
int localErrCode; \
|
|
|
|
|
|
if ((localErrCode = callback(request, &localPos, (pos), auxdata)) < 0) { \
|
|
|
|
|
|
errcode = localErrCode; \
|
|
|
|
|
|
goto finished; \
|
|
|
|
|
|
} \
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Пара RESERVE/OVERWRITE резервирует в выходном потоке место под запись числа и
|
|
|
|
|
|
// затем заполняет его. Пока под запись любого числа используются ровно 4
|
|
|
|
|
|
// байта. LOAD загружает из входного потока записанное таким способом значение.
|
|
|
|
|
|
// Зарезервированное, но не заполненное значение считывается как
|
|
|
|
|
|
// place.empty==true.
|
|
|
|
|
|
#define RESERVE(place) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
(place).pos = curpos; \
|
|
|
|
|
|
WRITE4(SPECIAL_VALUE_EMPTY); \
|
|
|
|
|
|
curpos += (place).len = 4; \
|
|
|
|
|
|
}
|
|
|
|
|
|
#define OVERWRITE(place, base) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
if ((place).pos != INVALID_FILESIZE) { \
|
|
|
|
|
|
SEEK_OUTPUT((place).pos); \
|
|
|
|
|
|
WRITE4(SPECIAL_VALUES + (base) - (place).pos - (place).len); \
|
|
|
|
|
|
} \
|
|
|
|
|
|
}
|
|
|
|
|
|
#define LOAD(place) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
READ4((place).n); \
|
|
|
|
|
|
curpos += 4; \
|
|
|
|
|
|
if (0 < (place).n && (place).n < SPECIAL_VALUES) { \
|
|
|
|
|
|
errcode = FREEARC_ERRCODE_READ; \
|
|
|
|
|
|
goto finished; \
|
|
|
|
|
|
} \
|
|
|
|
|
|
(place).empty = ((place).n == SPECIAL_VALUE_EMPTY); \
|
|
|
|
|
|
(place).n -= SPECIAL_VALUES; \
|
|
|
|
|
|
}
|
|
|
|
|
|
#define SPECIAL_VALUES 10
|
|
|
|
|
|
#define SPECIAL_VALUE_EMPTY 0
|
|
|
|
|
|
#define INVALID_FILESIZE (-1)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Число, записанное в выходной файл - его позиция и кол-во использованных байт
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct PLACE {
|
|
|
|
|
|
FILESIZE pos;
|
|
|
|
|
|
int len;
|
|
|
|
|
|
};
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// FILESIZE or empty value
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct MAYBE_FILESIZE {
|
|
|
|
|
|
FILESIZE n;
|
|
|
|
|
|
bool empty;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация многопоточной записи в один файл путём интерливинга данных от
|
|
|
|
|
|
// отдельных потоков
|
|
|
|
|
|
struct MultiWriter {
|
|
|
|
|
|
bool first_time; // TRUE - structure wasn't yet initialized
|
|
|
|
|
|
Mutex exclusive_access; // Ensure single-threaded access to the structure and
|
|
|
|
|
|
// underlying output file
|
|
|
|
|
|
int nStreams; // Number of output streams going to be interleaved
|
|
|
|
|
|
CALLBACK_FUNC *callback; // Original callback (function that provides access
|
|
|
|
|
|
// to the underlying files)
|
|
|
|
|
|
void *auxdata; // Original callback parameter
|
|
|
|
|
|
FILESIZE curpos; // Current position in the output file
|
|
|
|
|
|
int last_n; // Номер текущего записываемого в файл потока (при переключении на
|
|
|
|
|
|
// другой поток мы должны завершить кусочек старого)
|
|
|
|
|
|
PLACE *ofs; // ofs[N] - позиция в файле, куда надо записать смещение к
|
|
|
|
|
|
// следующему кусочку потока N, как только мы его начнём
|
|
|
|
|
|
PLACE *len; // len[N] - позиция в файле, куда надо записать длину кусочка
|
|
|
|
|
|
// потока N, как только мы его завершим
|
|
|
|
|
|
|
|
|
|
|
|
MultiWriter(int _nStreams, CALLBACK_FUNC *_callback, void *_auxdata) {
|
|
|
|
|
|
nStreams = _nStreams;
|
|
|
|
|
|
callback = _callback;
|
|
|
|
|
|
auxdata = _auxdata;
|
|
|
|
|
|
first_time = TRUE;
|
|
|
|
|
|
ofs = new PLACE[nStreams];
|
|
|
|
|
|
len = new PLACE[nStreams];
|
|
|
|
|
|
}
|
|
|
|
|
|
int Write(int n, BYTE *buf, int size);
|
|
|
|
|
|
~MultiWriter() {
|
|
|
|
|
|
delete[] len;
|
|
|
|
|
|
delete[] ofs;
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация многопоточного чтения из одного файла путём деинтерливинга данных
|
|
|
|
|
|
// отдельных потоков
|
|
|
|
|
|
struct MultiReader {
|
|
|
|
|
|
bool first_time; // TRUE - structure wasn't yet initialized
|
|
|
|
|
|
Mutex exclusive_access; // Ensure single-threaded access to the structure and
|
|
|
|
|
|
// underlying input file
|
|
|
|
|
|
int nStreams; // Number of input streams going to be deinterleaved
|
|
|
|
|
|
CALLBACK_FUNC *callback; // Original callback (function that provides access
|
|
|
|
|
|
// to the underlying files)
|
|
|
|
|
|
void *auxdata; // Original callback parameter
|
|
|
|
|
|
FILESIZE curpos; // Current position in the input file
|
|
|
|
|
|
int last_n; // Номер текущего читаемого из файла потока (при переключении на
|
|
|
|
|
|
// другой поток мы должны сделать seek на его текущий кусочек)
|
|
|
|
|
|
MAYBE_FILESIZE *pos; // pos[N] - позиция во входном файле, откуда будут
|
|
|
|
|
|
// читаться следующие данные потока N
|
|
|
|
|
|
MAYBE_FILESIZE
|
|
|
|
|
|
*len; // len[N] - кол-во байт, оставшихся в текущем кусочке потока N
|
|
|
|
|
|
|
|
|
|
|
|
MultiReader(int _nStreams, CALLBACK_FUNC *_callback, void *_auxdata) {
|
|
|
|
|
|
nStreams = _nStreams;
|
|
|
|
|
|
callback = _callback;
|
|
|
|
|
|
auxdata = _auxdata;
|
|
|
|
|
|
first_time = TRUE;
|
|
|
|
|
|
pos = new MAYBE_FILESIZE[nStreams];
|
|
|
|
|
|
len = new MAYBE_FILESIZE[nStreams];
|
|
|
|
|
|
}
|
|
|
|
|
|
int Read(int n, BYTE *buf, int size);
|
|
|
|
|
|
~MultiReader() {
|
|
|
|
|
|
delete[] len;
|
|
|
|
|
|
delete[] pos;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Записать содержимое буфера в поток n
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int MultiWriter::Write(int n, BYTE *buf, int size) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// Writing to sole output don't require the interleaving
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (n == 0 && nStreams == 1)
|
|
|
|
|
|
return callback("write", buf, size, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
Lock _(exclusive_access); // Prohibit simultaneous writing from multiple OS
|
|
|
|
|
|
// threads
|
2023-01-10 10:50:12 +01:00
|
|
|
|
int errcode = FREEARC_OK;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (n < 0 || n >= nStreams || nStreams <= 1)
|
|
|
|
|
|
return FREEARC_ERRCODE_GENERAL;
|
|
|
|
|
|
|
|
|
|
|
|
if (first_time) { // Initialization of multi-stream writing
|
|
|
|
|
|
first_time = FALSE;
|
|
|
|
|
|
curpos = 0;
|
|
|
|
|
|
for (int i = 1; i < nStreams; i++) {
|
|
|
|
|
|
RESERVE(ofs[i]); // Зарезервируем в выходном файле место для записи
|
|
|
|
|
|
// смещения к первому кусочку потока i
|
2023-01-10 10:50:12 +01:00
|
|
|
|
len[i].pos = INVALID_FILESIZE;
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
last_n = 0; // Начнём запись в поток 0
|
2023-01-10 10:50:12 +01:00
|
|
|
|
ofs[0].pos = INVALID_FILESIZE;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
RESERVE(len[0]); // Зарезервируем в выходном файле место для записи длины
|
|
|
|
|
|
// начинающегося здесь кусочка потока 0
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (n != last_n) { // Switching from writing to stream last_n into writing to
|
|
|
|
|
|
// stream n
|
|
|
|
|
|
FILESIZE data_end = curpos;
|
|
|
|
|
|
RESERVE(ofs[last_n]); // Зарезервируем в выходном файле место для записи
|
|
|
|
|
|
// смещения к след. кусочку потока last_n
|
|
|
|
|
|
FILESIZE between_blocks = curpos;
|
|
|
|
|
|
RESERVE(len[n]); // Зарезервируем в выходном файле место для записи длины
|
|
|
|
|
|
// начинающегося здесь кусочка потока n
|
|
|
|
|
|
|
|
|
|
|
|
OVERWRITE(ofs[n],
|
|
|
|
|
|
between_blocks); // Вернёмся назад и запишем смещение из пред.
|
|
|
|
|
|
// кусочка потока n к вновь начинающемуся
|
|
|
|
|
|
OVERWRITE(len[last_n],
|
|
|
|
|
|
data_end); // Вернёмся назад и запишем длину только что
|
|
|
|
|
|
// закончившегося кусочка потока last_n
|
|
|
|
|
|
|
|
|
|
|
|
SEEK_OUTPUT(curpos);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
last_n = n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
WRITE(buf, size);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
curpos += size;
|
|
|
|
|
|
return FREEARC_OK;
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
finished:
|
|
|
|
|
|
return errcode;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Fill buf with data read from stream n
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int MultiReader::Read(int n, BYTE *buf, int size) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// Reading from sole input don't need the deinterleaving
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (n == 0 && nStreams == 1)
|
|
|
|
|
|
return callback("read", buf, size, auxdata);
|
|
|
|
|
|
|
|
|
|
|
|
Lock _(exclusive_access); // Prohibit simultaneous reading from multiple OS
|
|
|
|
|
|
// threads
|
|
|
|
|
|
int errcode = FREEARC_OK, orig_size = size;
|
|
|
|
|
|
if (n < 0 || n >= nStreams || nStreams <= 1)
|
|
|
|
|
|
return FREEARC_ERRCODE_GENERAL;
|
|
|
|
|
|
|
|
|
|
|
|
if (first_time) { // Initialization of multi-stream reading
|
|
|
|
|
|
first_time = FALSE;
|
|
|
|
|
|
curpos = 0;
|
|
|
|
|
|
for (int i = 1; i < nStreams; i++) { // Starting positions of streams 1..N-1
|
|
|
|
|
|
// were saved at the file beginning
|
|
|
|
|
|
LOAD(pos[i]);
|
|
|
|
|
|
pos[i].n += curpos; // Decode position of the first chunk of the stream i
|
|
|
|
|
|
len[i].n = 0;
|
|
|
|
|
|
len[i].empty = false;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
last_n = 0; // Начнём чтение из потока 0
|
|
|
|
|
|
pos[0].n = curpos;
|
|
|
|
|
|
pos[0].empty = false; // Stream 0 starts right after the saved positions of
|
|
|
|
|
|
// other streams
|
|
|
|
|
|
len[0].n = 0;
|
|
|
|
|
|
len[0].empty = false; // Length of first chunk of stream 0 isn't yet loaded
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (n !=
|
|
|
|
|
|
last_n) { // Switching from reading stream last_n to reading of stream n
|
|
|
|
|
|
SEEK_INPUT(pos[n]);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
last_n = n;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
while (size) { // While we need to read more data to the buf
|
|
|
|
|
|
if (!len[n].empty &&
|
|
|
|
|
|
len[n].n == 0) { // We should jump to the next chunk of stream n
|
|
|
|
|
|
if (pos[n].empty)
|
|
|
|
|
|
break; // End-Of-Stream: there are no more chunks in stream n
|
|
|
|
|
|
SEEK_INPUT(pos[n]);
|
|
|
|
|
|
LOAD(len[n]); // Read length of being started chunk of stream n
|
|
|
|
|
|
pos[n].n =
|
|
|
|
|
|
curpos; // Advance pos[n] to the first byte after the length encoding
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int b, bytes = len[n].empty
|
|
|
|
|
|
? size
|
|
|
|
|
|
: mymin(size, len[n].n); // How much bytes we can read
|
2023-01-10 10:50:12 +01:00
|
|
|
|
if (bytes) {
|
2023-01-11 10:08:11 +01:00
|
|
|
|
READ_LEN(b, buf, bytes);
|
|
|
|
|
|
if (b == 0)
|
|
|
|
|
|
break;
|
|
|
|
|
|
curpos += b;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
pos[n].n += b;
|
|
|
|
|
|
len[n].n -= b;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
buf += b;
|
|
|
|
|
|
size -= b;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (!len[n].empty && len[n].n == 0) { // At the end of chunk
|
|
|
|
|
|
LOAD(pos[n]);
|
|
|
|
|
|
pos[n].n +=
|
|
|
|
|
|
curpos; // Decode position of the next chunk of the same stream
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return orig_size - size;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
finished:
|
|
|
|
|
|
return errcode;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct MultiReaderAsInput : AsInput {
|
|
|
|
|
|
int n;
|
|
|
|
|
|
MultiReader *multi_reader;
|
|
|
|
|
|
void Bind(int _n, MultiReader *_multi_reader) {
|
|
|
|
|
|
n = _n;
|
|
|
|
|
|
multi_reader = _multi_reader;
|
|
|
|
|
|
}
|
|
|
|
|
|
int Read(BYTE *buf, int size) { return multi_reader->Read(n, buf, size); }
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct MultiWriterAsOutput : AsOutput {
|
|
|
|
|
|
int n;
|
|
|
|
|
|
MultiWriter *multi_writer;
|
|
|
|
|
|
void Bind(int _n, MultiWriter *_multi_writer) {
|
|
|
|
|
|
n = _n;
|
|
|
|
|
|
multi_writer = _multi_writer;
|
|
|
|
|
|
}
|
|
|
|
|
|
int Write(BYTE *buf, int size) { return multi_writer->Write(n, buf, size); }
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Упаковка и распаковка данных деревом методов *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Глобальные данные процесса (рас)паковки
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct Globals {
|
|
|
|
|
|
COMPRESSION direction; // COMPRESS or DECOMPRESS - that's the question!
|
|
|
|
|
|
CALLBACK_FUNC *callback; // Original callback (function that reads data in the
|
|
|
|
|
|
// first method and writes data in the last one)
|
|
|
|
|
|
void *auxdata; // Original callback parameter
|
|
|
|
|
|
int retcode; // multi_de_compress() return code
|
|
|
|
|
|
Mutex retcode_cs; // Ensure single-threaded access to retcode
|
|
|
|
|
|
|
|
|
|
|
|
Globals(COMPRESSION _direction, CALLBACK_FUNC *_callback, void *_auxdata) {
|
|
|
|
|
|
direction = _direction;
|
|
|
|
|
|
callback = _callback;
|
|
|
|
|
|
auxdata = _auxdata;
|
|
|
|
|
|
retcode = 0;
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// Set overall multi_de_compress() exit code
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void SetExitCode(int code) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
Lock _(retcode_cs);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Save into retcode first error code signalled (subsequent error codes may
|
|
|
|
|
|
// be sequels of the first one)
|
|
|
|
|
|
if (retcode == 0 && code != FREEARC_ERRCODE_NO_MORE_DATA_REQUIRED)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
retcode = code;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Локальные данные одного метода
|
2023-01-11 10:08:11 +01:00
|
|
|
|
struct Locals {
|
|
|
|
|
|
Globals *Global;
|
|
|
|
|
|
Thread thread; // OS thread executing this (de)compression algorithm
|
|
|
|
|
|
CMETHOD method; // String denoting (de)compression method with its parameters
|
|
|
|
|
|
int NumInputs;
|
|
|
|
|
|
int NumOutputs;
|
|
|
|
|
|
AsInput **Input;
|
|
|
|
|
|
AsOutput **Output;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE
|
|
|
|
|
|
multi_de_compress_thread(void *paramPtr);
|
|
|
|
|
|
static int multi_de_compress_callback(const char *what, void *buf, int size,
|
|
|
|
|
|
void *paramPtr);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Упаковать/распаковать данные деревом методов
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static int multi_de_compress(COMPRESSION direction, char *compressor,
|
|
|
|
|
|
CALLBACK_FUNC *callback, void *auxdata) {
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Мы будем работать с копией строки, потому что split() модифицирует её
|
2023-01-11 10:08:11 +01:00
|
|
|
|
char new_compressor[MAX_COMPRESSOR_STRLEN],
|
|
|
|
|
|
output_compressors_buf[MAX_COMPRESSOR_STRLEN * 2],
|
|
|
|
|
|
*out_ptr = output_compressors_buf;
|
|
|
|
|
|
strncopy(new_compressor, compressor, MAX_COMPRESSOR_STRLEN);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Разобьём компрессор на отдельные алгоритмы и запустим для каждого из них
|
|
|
|
|
|
// отдельный тред
|
2023-01-10 10:50:12 +01:00
|
|
|
|
CMETHOD cm[MAX_METHODS_IN_COMPRESSOR];
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int N = split(new_compressor, COMPRESSION_METHODS_DELIMITER, cm,
|
|
|
|
|
|
MAX_METHODS_IN_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
|
|
// Теперь мы знаем, что дерево включает N методов сжатия, создадим локальные
|
|
|
|
|
|
// структуры для них
|
|
|
|
|
|
Globals Global(direction, callback, auxdata);
|
|
|
|
|
|
Locals Local[MAX_METHODS_IN_COMPRESSOR];
|
|
|
|
|
|
Channels Channel[MAX_METHODS_IN_COMPRESSOR];
|
|
|
|
|
|
MultiReaderAsInput Reader[MAX_METHODS_IN_COMPRESSOR];
|
|
|
|
|
|
MultiWriterAsOutput Writer[MAX_METHODS_IN_COMPRESSOR];
|
|
|
|
|
|
int InChan = 1, OutChan = 1;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// Arrays storing inputs/ouputs for all methods
|
2023-01-11 10:08:11 +01:00
|
|
|
|
AsInput *LocalInputsArray[MAX_METHODS_IN_COMPRESSOR],
|
|
|
|
|
|
**LocalInputs = LocalInputsArray;
|
|
|
|
|
|
AsOutput *LocalOutputsArray[MAX_METHODS_IN_COMPRESSOR],
|
|
|
|
|
|
**LocalOutputs = LocalOutputsArray;
|
|
|
|
|
|
#define AllocInputs(Loc, n) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
Loc.Input = LocalInputs; \
|
|
|
|
|
|
Loc.NumInputs = n; \
|
|
|
|
|
|
LocalInputs += n; \
|
|
|
|
|
|
}
|
|
|
|
|
|
#define AllocOutputs(Loc, n) \
|
|
|
|
|
|
{ \
|
|
|
|
|
|
Loc.Output = LocalOutputs; \
|
|
|
|
|
|
Loc.NumOutputs = n; \
|
|
|
|
|
|
LocalOutputs += n; \
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Сформируем взаимосвязи входов и выходов алгоритмов в соответствии с деревом
|
|
|
|
|
|
// методов
|
|
|
|
|
|
for (int i = N - 1; i >= 0; i--) {
|
|
|
|
|
|
int nStreams = CompressionService(cm[i], "NumOutStreams");
|
|
|
|
|
|
if (nStreams <= 0)
|
|
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (direction ==
|
|
|
|
|
|
COMPRESS) // СОЗДАДИМ ДЕРЕВО ВЫХОДОВ ДЛЯ УПАКОВКИ
|
|
|
|
|
|
// ============================================================
|
2023-01-10 10:50:12 +01:00
|
|
|
|
{
|
2023-01-11 10:08:11 +01:00
|
|
|
|
AllocInputs(Local[i], 1);
|
|
|
|
|
|
Local[i].Input[0] = i > 0 ? (AsInput *)&Channel[i - 1] : &Reader[0];
|
|
|
|
|
|
AllocOutputs(Local[i], nStreams);
|
|
|
|
|
|
Local[i].Output[0] = i < N - 1 ? (AsOutput *)&Channel[i] : &Writer[0];
|
|
|
|
|
|
for (int j = 1; j < nStreams; j++) {
|
|
|
|
|
|
*out_ptr =
|
|
|
|
|
|
'\0'; // буфер для запроса метода упаковки j-го выхода алгоритма
|
|
|
|
|
|
if (CompressionService(cm[i], "GetOutStreamCompressor", j, out_ptr) < 0)
|
|
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
|
|
|
|
|
if (*out_ptr) { // ============================================ На
|
|
|
|
|
|
// выходе стоит метод сжатия ==========================
|
|
|
|
|
|
Local[i].Output[j] = &Channel[N]; // Используем канал N для передачи
|
|
|
|
|
|
// данных между этими методами
|
|
|
|
|
|
AllocInputs(Local[N], 1);
|
|
|
|
|
|
Local[N].Input[0] = &Channel[N];
|
|
|
|
|
|
AllocOutputs(Local[N], 1);
|
|
|
|
|
|
Local[N].Output[0] =
|
|
|
|
|
|
&Writer[OutChan++]; // Выход нового метода пойдёт в новый поток в
|
|
|
|
|
|
// выходном файле
|
|
|
|
|
|
cm[N++] = out_ptr;
|
|
|
|
|
|
out_ptr += strlen(out_ptr) + 1; // Добавим новый метод сжатия в дерево
|
|
|
|
|
|
} else { // ============================================ Выход напрямую
|
|
|
|
|
|
// записывается в выходной файл ===========
|
|
|
|
|
|
Local[i].Output[j] =
|
|
|
|
|
|
&Writer[OutChan++]; // Этот выход метода пойдёт в новый поток в
|
|
|
|
|
|
// выходном файле
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
} else // СОЗДАДИМ ДЕРЕВО ВХОДОВ ДЛЯ РАСПАКОВКИ
|
|
|
|
|
|
// ===============================================================================
|
2023-01-10 10:50:12 +01:00
|
|
|
|
{
|
2023-01-11 10:08:11 +01:00
|
|
|
|
AllocOutputs(Local[i], 1);
|
|
|
|
|
|
Local[i].Output[0] = i > 0 ? (AsOutput *)&Channel[i - 1] : &Writer[0];
|
|
|
|
|
|
AllocInputs(Local[i], nStreams);
|
|
|
|
|
|
Local[i].Input[0] = i < N - 1 ? (AsInput *)&Channel[i] : &Reader[0];
|
|
|
|
|
|
for (int j = 1; j < nStreams; j++) {
|
|
|
|
|
|
*out_ptr = '\0'; // буфер для запроса упаковщика j-го выхода алгоритма
|
|
|
|
|
|
if (CompressionService(cm[i], "GetOutStreamCompressor", j, out_ptr) < 0)
|
|
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
|
|
|
|
|
if (*out_ptr) { // ============================================ На входе
|
|
|
|
|
|
// стоит метод сжатия ===========================
|
|
|
|
|
|
Local[i].Input[j] = &Channel[N]; // Используем канал N для передачи
|
|
|
|
|
|
// данных между этими методами
|
|
|
|
|
|
AllocOutputs(Local[N], 1);
|
|
|
|
|
|
Local[N].Output[0] = &Channel[N];
|
|
|
|
|
|
AllocInputs(Local[N], 1);
|
|
|
|
|
|
Local[N].Input[0] = &Reader[InChan++]; // Выход нового метода пойдёт в
|
|
|
|
|
|
// новый поток в выходном файле
|
|
|
|
|
|
cm[N++] = out_ptr;
|
|
|
|
|
|
out_ptr += strlen(out_ptr) + 1; // Добавим новый метод сжатия в дерево
|
|
|
|
|
|
} else { // ============================================ Вход напрямую
|
|
|
|
|
|
// читается из выходного файла =============
|
|
|
|
|
|
Local[i].Input[j] = &Reader[InChan++]; // Этот выход метода пойдёт в
|
|
|
|
|
|
// новый поток в выходном файле
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (N == 1 && InChan == 1 && OutChan == 1)
|
|
|
|
|
|
return single_de_compress(direction, new_compressor, callback,
|
|
|
|
|
|
auxdata); // multi-threading isn't required
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Объекты ввода/вывода для входных и выходных потоков
|
2023-01-11 10:08:11 +01:00
|
|
|
|
MultiReader multi_reader(InChan, callback,
|
|
|
|
|
|
auxdata); // Worker object implementing multi-stream
|
|
|
|
|
|
// reading for decompression
|
|
|
|
|
|
MultiWriter multi_writer(OutChan, callback,
|
|
|
|
|
|
auxdata); // Worker object implementing multi-stream
|
|
|
|
|
|
// writing for compression
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
iterate(InChan, Reader[i].Bind(i, &multi_reader));
|
|
|
|
|
|
iterate(OutChan, Writer[i].Bind(i, &multi_writer));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// Start N threads
|
2023-01-11 10:08:11 +01:00
|
|
|
|
for (int i = 0; i < N; i++) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
Local[i].Global = &Global;
|
|
|
|
|
|
Local[i].method = cm[i];
|
2023-01-11 10:08:11 +01:00
|
|
|
|
Local[i].thread.Create(multi_de_compress_thread, &Local[i]);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Wait until all threads will be finished and return errcode or 0 at success
|
2023-01-11 10:08:11 +01:00
|
|
|
|
for (int i = 0; i < N; i++)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
Local[i].thread.Wait();
|
|
|
|
|
|
return Global.retcode;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Один тред распаковки в multi_de_compress
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static THREAD_FUNC_RET_TYPE THREAD_FUNC_CALL_TYPE
|
|
|
|
|
|
multi_de_compress_thread(void *paramPtr) {
|
|
|
|
|
|
Locals *Local = (Locals *)paramPtr;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
Globals *Global = Local->Global;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Не запускать этот thread, пока не начался вывод из предыдущего (для
|
|
|
|
|
|
// экономии памяти)
|
|
|
|
|
|
Local->Input[0]->Read(NULL, 0); // ожидаем появления данных в буфере
|
|
|
|
|
|
SetCompressionThreadPriority(); // понизить приоритет треда
|
|
|
|
|
|
int ret = single_de_compress(Global->direction, Local->method,
|
|
|
|
|
|
multi_de_compress_callback, paramPtr);
|
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
|
Global->SetExitCode(ret); // Set global error code if single_de_compress()
|
|
|
|
|
|
// returned with error
|
|
|
|
|
|
for (int i = 0; i < Local->NumInputs; i++)
|
|
|
|
|
|
Local->Input[i]->NoMoreReads(); // Tell the input threads that no more data
|
|
|
|
|
|
// are required
|
|
|
|
|
|
for (int i = 0; i < Local->NumOutputs; i++)
|
|
|
|
|
|
Local->Output[i]->NoMoreWrites(); // Tell the output threads that no more
|
|
|
|
|
|
// data will be supplied to them
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Callback-функция для multi_de_compress_thread(), перехватывающая операции
|
|
|
|
|
|
// чтения и записи
|
|
|
|
|
|
static int multi_de_compress_callback(const char *what, void *_buf, int size,
|
|
|
|
|
|
void *paramPtr) {
|
|
|
|
|
|
Locals *Local = (Locals *)paramPtr;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
Globals *Global = Local->Global;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
BYTE *buf = (BYTE *)_buf;
|
|
|
|
|
|
|
|
|
|
|
|
// readXXX: прочитать данные для обработки из входного потока номер XXX, 0 по
|
|
|
|
|
|
// умолчанию
|
|
|
|
|
|
if (start_with(what, "read")) {
|
|
|
|
|
|
int xxx = what[4] ? atoi(what + 4) : 0;
|
|
|
|
|
|
if (xxx >= Local->NumInputs)
|
|
|
|
|
|
return FREEARC_ERRCODE_READ;
|
|
|
|
|
|
return Local->Input[xxx]->Read(buf, size);
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// writeXXX: записать результат обработки в выходной поток номер XXX, 0 по
|
|
|
|
|
|
// умолчанию
|
|
|
|
|
|
else if (start_with(what, "write")) {
|
|
|
|
|
|
int xxx = what[5] ? atoi(what + 5) : 0;
|
|
|
|
|
|
if (xxx >= Local->NumOutputs)
|
|
|
|
|
|
return FREEARC_ERRCODE_WRITE;
|
|
|
|
|
|
return Local->Output[xxx]->Write(buf, size);
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// progress: пропустить поскольку мы пока не умеем комбинировать "progress" от
|
|
|
|
|
|
// цепочки упаковщиков
|
|
|
|
|
|
else if (strequ(what, "progress")) {
|
|
|
|
|
|
return FREEARC_ERRCODE_NOT_IMPLEMENTED;
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Все остальные запросы передаются на выполнение в оригинальный callback
|
2023-01-11 10:08:11 +01:00
|
|
|
|
else
|
|
|
|
|
|
return Global->callback(what, buf, size, Global->auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Compression *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Compress data with given compressor (compression method or chain of
|
|
|
|
|
|
// compression methods)
|
|
|
|
|
|
int Compress(char *compressor, CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
return multi_de_compress(COMPRESS, compressor, callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Put to the output stream string describing the compressor and then the
|
|
|
|
|
|
// compressed data
|
|
|
|
|
|
int CompressWithHeader(char *orig_compressor, CALLBACK_FUNC *callback,
|
|
|
|
|
|
void *auxdata) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// Split compressor into methods
|
2023-01-11 10:08:11 +01:00
|
|
|
|
char new_compressor[MAX_COMPRESSOR_STRLEN],
|
|
|
|
|
|
canonical_methods[MAX_COMPRESSOR_STRLEN], *p = canonical_methods;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
CMETHOD method[MAX_METHODS_IN_COMPRESSOR];
|
2023-01-11 10:08:11 +01:00
|
|
|
|
strncopy(new_compressor, orig_compressor, MAX_COMPRESSOR_STRLEN);
|
|
|
|
|
|
int N = split(new_compressor, COMPRESSION_METHODS_DELIMITER, method,
|
|
|
|
|
|
MAX_METHODS_IN_COMPRESSOR);
|
|
|
|
|
|
|
|
|
|
|
|
// Generate canonical, pure representation of each compression method (in
|
|
|
|
|
|
// order to improve compatibility with future decompressors and omit
|
|
|
|
|
|
// compression-stage specific hints
|
|
|
|
|
|
for (int i = 0; i < N; i++) {
|
|
|
|
|
|
CanonizeCompressionMethod(method[i], p, TRUE);
|
|
|
|
|
|
method[i] = p;
|
|
|
|
|
|
p += strlen(p) + 1;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
method[N] = NULL;
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Join canonized method strings back together and write canonicalized
|
|
|
|
|
|
// compressor string to output stream
|
|
|
|
|
|
join(method, COMPRESSION_METHODS_DELIMITER, new_compressor,
|
|
|
|
|
|
MAX_COMPRESSOR_STRLEN);
|
|
|
|
|
|
int result =
|
|
|
|
|
|
callback("write", new_compressor, strlen(new_compressor) + 1, auxdata);
|
|
|
|
|
|
|
|
|
|
|
|
if (result >= 0)
|
|
|
|
|
|
result = Compress(
|
|
|
|
|
|
orig_compressor, callback,
|
|
|
|
|
|
auxdata); // Use original compressor string since it may include hints
|
|
|
|
|
|
// omitted by CanonizeCompressionMethod(purify=TRUE)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return result;
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Decompression *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Decompress data with given compressor (compression method or chain of
|
|
|
|
|
|
// compression methods)
|
|
|
|
|
|
int Decompress(char *compressor, CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
return multi_de_compress(DECOMPRESS, compressor, callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Read string, describing the compressor, from input stream and decompress rest
|
|
|
|
|
|
// of stream with this compressor
|
|
|
|
|
|
int DecompressWithHeader(CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
char compressor[MAX_COMPRESSOR_STRLEN];
|
|
|
|
|
|
for (int i = 0; i < MAX_COMPRESSOR_STRLEN; i++) {
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Посимвольно читаем входные данные, пока не прочтём символ конца строки
|
2023-01-11 10:08:11 +01:00
|
|
|
|
callback("read", &compressor[i], 1, auxdata);
|
|
|
|
|
|
if (compressor[i] == '\0')
|
|
|
|
|
|
return Decompress(compressor, callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR; // Сюда мы попадаем, если в первых
|
|
|
|
|
|
// MAX_COMPRESSOR_STRLEN символах
|
|
|
|
|
|
// входных данных не нашлось
|
|
|
|
|
|
// символа '\0'
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Getters and Setters: query or set memory usage, dictionary, block size for
|
|
|
|
|
|
// compresssion method *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#define Generate_Getter(GETTER) \
|
|
|
|
|
|
MemSize GETTER(char *method) { \
|
|
|
|
|
|
COMPRESSION_METHOD *compressor = ParseCompressionMethod(method); \
|
|
|
|
|
|
if (compressor) { \
|
|
|
|
|
|
MemSize bytes = compressor->GETTER(); \
|
|
|
|
|
|
delete compressor; \
|
|
|
|
|
|
return bytes; \
|
|
|
|
|
|
} else \
|
|
|
|
|
|
return (MemSize)FREEARC_ERRCODE_INVALID_COMPRESSOR; \
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#define Generate_Setter(SETTER) \
|
|
|
|
|
|
int SETTER(char *in_method, MemSize bytes, char *out_method) { \
|
|
|
|
|
|
COMPRESSION_METHOD *compressor = ParseCompressionMethod(in_method); \
|
|
|
|
|
|
if (compressor) { \
|
|
|
|
|
|
compressor->SETTER(bytes); \
|
|
|
|
|
|
compressor->ShowCompressionMethod(out_method, FALSE); \
|
|
|
|
|
|
delete compressor; \
|
|
|
|
|
|
return FREEARC_OK; \
|
|
|
|
|
|
} else \
|
|
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR; \
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Информация о памяти, необходимой для упаковки/распаковки, размере словаря и
|
|
|
|
|
|
// размере блока
|
|
|
|
|
|
Generate_Getter(GetDictionary) Generate_Getter(GetBlockSize)
|
|
|
|
|
|
Generate_Getter(GetDecompressionMem) Generate_Setter(SetDecompressionMem)
|
|
|
|
|
|
Generate_Setter(LimitDecompressionMem)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Информация о памяти, необходимой для упаковки/распаковки
|
|
|
|
|
|
Generate_Getter(GetCompressionMem) Generate_Getter(GetMinCompressionMem)
|
|
|
|
|
|
Generate_Getter(GetMinDecompressionMem)
|
|
|
|
|
|
|
|
|
|
|
|
// Возвратить в out_method новый метод сжатия, настроенный на использование
|
|
|
|
|
|
// соответствующего количества памяти при упаковке/распаковке или
|
|
|
|
|
|
// словаря/размера блока
|
|
|
|
|
|
Generate_Setter(SetDictionary) Generate_Setter(SetBlockSize)
|
|
|
|
|
|
Generate_Setter(SetCompressionMem)
|
|
|
|
|
|
Generate_Setter(SetMinDecompressionMem)
|
|
|
|
|
|
|
|
|
|
|
|
// Возвратить в out_method новый метод сжатия, уменьшив, если необходимо,
|
|
|
|
|
|
// используемую алгоритмом память / его словарь / размер блока
|
|
|
|
|
|
Generate_Setter(LimitDictionary) Generate_Setter(LimitBlockSize)
|
|
|
|
|
|
Generate_Setter(LimitCompressionMem)
|
|
|
|
|
|
Generate_Setter(LimitMinDecompressionMem)
|
|
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
|
|
|
|
|
|
|
|
|
|
|
// Объём памяти, характеризующий алгоритм
|
|
|
|
|
|
MemSize COMPRESSION_METHOD::GetAlgoMem() {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemSize dict = GetDictionary();
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (dict)
|
|
|
|
|
|
return dict;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemSize BlockSize = GetBlockSize();
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (BlockSize)
|
|
|
|
|
|
return BlockSize;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Универсальный метод. Параметры:
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// what: "compress", "decompress", "setCompressionMem", "limitDictionary"...
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// data: данные для операции в формате, зависящем от конкретной выполняемой
|
|
|
|
|
|
// операции param&result: простой числовой параметр, что достаточно для многих
|
|
|
|
|
|
// информационных операций
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Неиспользуемые параметры устанавливайте в NULL/0. result<0 - код ошибки
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int COMPRESSION_METHOD::doit(char *what, int param, void *data,
|
|
|
|
|
|
CALLBACK_FUNC *callback) {
|
|
|
|
|
|
if (strequ(what, "encryption?"))
|
|
|
|
|
|
return 0; // Это алгоритм шифрования?
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-11 10:08:11 +01:00
|
|
|
|
else if (strequ(what, "GetCompressionMem"))
|
|
|
|
|
|
return GetCompressionMem(); // Объём памяти, необходимый для упаковки
|
|
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
|
|
|
|
|
else if (strequ(what, "GetDecompressionMem"))
|
|
|
|
|
|
return GetDecompressionMem(); // Объём памяти, необходимый для распаковки
|
|
|
|
|
|
else if (strequ(what, "NumOutStreams"))
|
|
|
|
|
|
return 1; // Кол-во выходных потоков упаковщика
|
|
|
|
|
|
else if (strequ(what, "GetAlgoMem"))
|
|
|
|
|
|
return GetAlgoMem(); // Объём памяти, характеризующий алгоритм
|
|
|
|
|
|
else
|
|
|
|
|
|
return FREEARC_ERRCODE_NOT_IMPLEMENTED;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Вывести в canonical_method каноническое представление метода сжатия in_method
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int CanonizeCompressionMethod(char *method, char *canonical_method,
|
|
|
|
|
|
int purify) {
|
|
|
|
|
|
COMPRESSION_METHOD *compressor = ParseCompressionMethod(method);
|
|
|
|
|
|
if (compressor) {
|
|
|
|
|
|
compressor->ShowCompressionMethod(canonical_method, purify != 0);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
delete compressor;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return FREEARC_OK;
|
|
|
|
|
|
} else
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// (De)compress data from memory buffer (input) to another memory buffer
|
|
|
|
|
|
// (output) *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Callback-функция чтения/записи для (рас)паковки в памяти
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int ReadWriteMem(const char *what, void *buf, int size, void *_membuf) {
|
|
|
|
|
|
MemBuf *membuf = (MemBuf *)_membuf;
|
|
|
|
|
|
if (strequ(what, "read") && membuf->readPtr) {
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Скопировать данные из readPtr в buf и продвинуть указатель чтения
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int read_bytes = membuf->readLeft < size ? membuf->readLeft : size;
|
|
|
|
|
|
memcpy(buf, membuf->readPtr, read_bytes);
|
|
|
|
|
|
membuf->readPtr += read_bytes;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
membuf->readLeft -= read_bytes;
|
|
|
|
|
|
return read_bytes;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
} else if (strequ(what, "write") && membuf->writePtr) {
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Скопировать данные из buf в writePtr и продвинуть указатель записи
|
2023-01-11 10:08:11 +01:00
|
|
|
|
if (size > membuf->writeLeft)
|
|
|
|
|
|
return FREEARC_ERRCODE_OUTBLOCK_TOO_SMALL;
|
|
|
|
|
|
memcpy(membuf->writePtr, buf, size);
|
|
|
|
|
|
membuf->writePtr += size;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
membuf->writeLeft -= size;
|
|
|
|
|
|
return size;
|
|
|
|
|
|
} else {
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Остальные операции отдаём "наверх"
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return (membuf->callback
|
|
|
|
|
|
? membuf->callback(what, buf, size, membuf->auxdata)
|
|
|
|
|
|
: FREEARC_ERRCODE_NOT_IMPLEMENTED);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация DeCompressMem по умолчанию: через compress/decompress и MemBuf.
|
|
|
|
|
|
// Затратный, но универсальный вариант
|
|
|
|
|
|
int COMPRESSION_METHOD::DeCompressMem(COMPRESSION direction, void *input,
|
|
|
|
|
|
int inputSize, void *output,
|
|
|
|
|
|
int *outputSize, CALLBACK_FUNC *callback,
|
|
|
|
|
|
void *auxdata, void **CodecState) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemBuf membuf(input, inputSize, output, *outputSize, callback, auxdata);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result = (direction == COMPRESS ?
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-11 10:08:11 +01:00
|
|
|
|
compress(ReadWriteMem, &membuf)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#else
|
2023-01-11 10:08:11 +01:00
|
|
|
|
FREEARC_ERRCODE_ONLY_DECOMPRESS
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#endif
|
2023-01-11 10:08:11 +01:00
|
|
|
|
: decompress(ReadWriteMem, &membuf));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
*outputSize = membuf.written();
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Распаковать данные в памяти, записав в выходной буфер не более outputSize
|
|
|
|
|
|
// байт. Возвращает код ошибки или количество байт, записанных в выходной буфер
|
|
|
|
|
|
int DecompressMem(char *compressor, void *input, int inputSize, void *output,
|
|
|
|
|
|
int outputSize) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemBuf membuf(input, inputSize, output, outputSize);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result = Decompress(compressor, ReadWriteMem, &membuf);
|
|
|
|
|
|
return result < 0 ? result : membuf.written();
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Аналог DecompressMem с чтением алгоритма сжатия из самого входного буфера
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int DecompressMemWithHeader(void *input, int inputSize, void *output,
|
|
|
|
|
|
int outputSize) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemBuf membuf(input, inputSize, output, outputSize);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result = DecompressWithHeader(ReadWriteMem, &membuf);
|
|
|
|
|
|
return result < 0 ? result : membuf.written();
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Упаковать данные в памяти, записав в выходной буфер не более outputSize байт.
|
|
|
|
|
|
// Возвращает код ошибки или количество байт, записанных в выходной буфер
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int CompressMem(char *compressor, void *input, int inputSize, void *output,
|
|
|
|
|
|
int outputSize) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemBuf membuf(input, inputSize, output, outputSize);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result = Compress(compressor, ReadWriteMem, &membuf);
|
|
|
|
|
|
return result < 0 ? result : membuf.written();
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Упаковать данные в памяти, записав в выходной буфер не более outputSize байт.
|
|
|
|
|
|
// Возвращает код ошибки или количество байт, записанных в выходной буфер
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int CompressMemWithHeader(char *compressor, void *input, int inputSize,
|
|
|
|
|
|
void *output, int outputSize) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
MemBuf membuf(input, inputSize, output, outputSize);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int result = CompressWithHeader(compressor, ReadWriteMem, &membuf);
|
|
|
|
|
|
return result < 0 ? result : membuf.written();
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// УТИЛИТЫ *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Разбить COMPRESSOR на отдельные алгоритмы сжатия/шифрования
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// void splitCompressor (COMPRESSOR c, ARRAY<CMETHOD> &cm)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Запросить сервис what метода сжатия method
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int CompressionService(char *method, char *what, int param, void *data,
|
|
|
|
|
|
CALLBACK_FUNC *callback) {
|
|
|
|
|
|
COMPRESSION_METHOD *compressor = ParseCompressionMethod(method);
|
|
|
|
|
|
if (compressor) {
|
|
|
|
|
|
int result = compressor->doit(what, param, data, callback);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
delete compressor;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return result;
|
|
|
|
|
|
} else
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return FREEARC_ERRCODE_INVALID_COMPRESSOR;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Проверить, что данный компрессор включает алгоритм шифрования
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int compressorIsEncrypted(COMPRESSOR c) {
|
|
|
|
|
|
char compressor[MAX_COMPRESSOR_STRLEN];
|
|
|
|
|
|
strcpy(compressor, c);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Разобьём компрессор на отдельные алгоритмы и поищем среди них алгоритм
|
|
|
|
|
|
// шифрования
|
2023-01-10 10:50:12 +01:00
|
|
|
|
CMETHOD arr[MAX_METHODS_IN_COMPRESSOR];
|
2023-01-11 10:08:11 +01:00
|
|
|
|
split(compressor, COMPRESSION_METHODS_DELIMITER, arr,
|
|
|
|
|
|
MAX_METHODS_IN_COMPRESSOR);
|
|
|
|
|
|
for (CMETHOD *cm = arr; *cm; cm++)
|
|
|
|
|
|
if (CompressionService(*cm, "encryption?") == 1)
|
|
|
|
|
|
return TRUE;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return FALSE;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Вычислить, сколько памяти нужно для распаковки данных, сжатых этим
|
|
|
|
|
|
// компрессором (портит c)
|
|
|
|
|
|
MemSize compressorGetDecompressionMem(COMPRESSOR c) {
|
|
|
|
|
|
// Разобьём компрессор на отдельные алгоритмы и просуммируем их требования к
|
|
|
|
|
|
// памяти
|
2023-01-10 10:50:12 +01:00
|
|
|
|
CMETHOD arr[MAX_METHODS_IN_COMPRESSOR];
|
2023-01-11 10:08:11 +01:00
|
|
|
|
split(c, COMPRESSION_METHODS_DELIMITER, arr, MAX_METHODS_IN_COMPRESSOR);
|
|
|
|
|
|
MemSize sum = 0;
|
|
|
|
|
|
for (CMETHOD *cm = arr; *cm; cm++)
|
|
|
|
|
|
sum += CompressionService(*cm, "GetDecompressionMem");
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return sum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// ПОДДЕРЖКА ТАБЛИЦЫ ЗАРЕГИСТРИРОВАННЫХ РАЗБОРЩИКОВ МЕТОДОВ СЖАТИЯ И ПОИСК В
|
|
|
|
|
|
// ЭТОЙ ТАБЛИЦЕ РЕАЛИЗАЦИИ ЧИСТО КОНКРЕТНОГО МЕТОДА *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
template <class PARSER> struct Parser {
|
|
|
|
|
|
PARSER parser;
|
|
|
|
|
|
void *data;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int cmCount = 0; // Кол-во зарегистрированных методов сжатия
|
|
|
|
|
|
Parser<CM_PARSER>
|
|
|
|
|
|
cmTable[MAX_COMPRESSION_METHODS]; // Таблица, в которую записываются все
|
|
|
|
|
|
// зарегистрированные парсеры методов
|
|
|
|
|
|
// сжатия
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Добавить новый метод в список поддерживаемых методов сжатия
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int AddCompressionMethod(CM_PARSER parser) {
|
|
|
|
|
|
CHECK(FREEARC_ERRCODE_INTERNAL, cmCount < elements(cmTable),
|
|
|
|
|
|
(s, "INTERNAL ERROR: Overflow of compression methods table"));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
cmTable[cmCount++].parser = parser;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int cmExternalCount = 0; // Кол-во зарегистрированных внешних методов сжатия
|
|
|
|
|
|
Parser<CM_PARSER2>
|
|
|
|
|
|
cmExternalTable[MAX_COMPRESSION_METHODS]; // Таблица, в которую записываются
|
|
|
|
|
|
// все зарегистрированные парсеры
|
|
|
|
|
|
// внешних методов сжатия
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Очистить таблицу внешних упаковщиков
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void ClearExternalCompressorsTable(void) {
|
|
|
|
|
|
static int builtins = -1;
|
|
|
|
|
|
if (builtins < 0)
|
|
|
|
|
|
builtins = cmExternalCount;
|
|
|
|
|
|
cmExternalCount =
|
|
|
|
|
|
builtins; // Оставим только встроенные описания внешних упаковщиков
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Добавить парсер метода с дополнительным параметром, который должен быть
|
|
|
|
|
|
// передан этому парсеру
|
|
|
|
|
|
int AddExternalCompressionMethod(CM_PARSER2 parser, void *data) {
|
|
|
|
|
|
CHECK(FREEARC_ERRCODE_GENERAL, cmExternalCount < elements(cmExternalTable),
|
|
|
|
|
|
(s, "Too much external compression methods defined"));
|
2023-01-10 10:50:12 +01:00
|
|
|
|
cmExternalTable[cmExternalCount].parser = parser;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
cmExternalTable[cmExternalCount].data = data;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
cmExternalCount++;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Сконструировать объект класса COMPRESSION_METHOD, реализующий метод, заданный
|
|
|
|
|
|
// в виде строки `method`
|
|
|
|
|
|
COMPRESSION_METHOD *ParseCompressionMethod(char *method) {
|
|
|
|
|
|
// Превратим строку метода сжатия в массив строк `parameters`, хранящий его
|
|
|
|
|
|
// название и параметры
|
|
|
|
|
|
char *parameters[MAX_PARAMETERS];
|
|
|
|
|
|
char local_method[MAX_METHOD_STRLEN];
|
|
|
|
|
|
strncopy(local_method, method, sizeof(local_method));
|
|
|
|
|
|
split(local_method, COMPRESSION_METHOD_PARAMETERS_DELIMITER, parameters,
|
|
|
|
|
|
MAX_PARAMETERS);
|
|
|
|
|
|
|
|
|
|
|
|
// Переберём все зарегистрированные парсеры методов сжатия и найдём тот,
|
|
|
|
|
|
// который сможет опознать `parameters`
|
|
|
|
|
|
iterate_var(i, cmExternalCount) {
|
|
|
|
|
|
COMPRESSION_METHOD *m =
|
|
|
|
|
|
(*cmExternalTable[i].parser)(parameters, cmExternalTable[i].data);
|
|
|
|
|
|
if (m)
|
|
|
|
|
|
return m;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
iterate_var(i, cmCount) {
|
|
|
|
|
|
COMPRESSION_METHOD *m = (*cmTable[i].parser)(parameters);
|
|
|
|
|
|
if (m)
|
|
|
|
|
|
return m;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return NULL; // Полученный метод сжатия не опознан ни одним из парсеров
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Loading compression methods from external DLLs *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
// Load accelerated function either from facompress.dll or facompress_mt.dll
|
2023-01-11 10:08:11 +01:00
|
|
|
|
FARPROC LoadFromDLL(char *funcname, int only_facompress_mt) { return NULL; }
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Other compression methods may chain-redefine this callback in order to
|
|
|
|
|
|
// perform their own cleanup procedures
|
|
|
|
|
|
static void NOP() {}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
void (*BeforeUnloadDLL)() = &NOP;
|
|
|
|
|
|
|
|
|
|
|
|
// This function unloads DLLs containing accelerated compression functions
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void UnloadDLL(void) { (*BeforeUnloadDLL)(); }
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// This function cleans up the Compression Library
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void compressionLib_cleanup(void) { removeTemporaryFiles(); }
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Compression Library global settings *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
// Get/set number of threads used for (de)compression. 0 means "autodetect"
|
|
|
|
|
|
static int CompressionThreads = 1;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int __cdecl GetCompressionThreads(void) { return CompressionThreads; }
|
|
|
|
|
|
void __cdecl SetCompressionThreads(int threads) {
|
|
|
|
|
|
CompressionThreads = threads == 0 ? 1 : threads;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#ifndef FREEARC_COMPRESSION_DLL
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static FARPROC f = LoadFromDLL("SetCompressionThreads");
|
|
|
|
|
|
static FARPROC f2 = LoadFromDLL("SetCompressionThreads", TRUE);
|
|
|
|
|
|
if (f)
|
|
|
|
|
|
((void(__cdecl *)(int))f)(threads);
|
|
|
|
|
|
if (f2)
|
|
|
|
|
|
((void(__cdecl *)(int))f2)(threads);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Used in 4x4 only: read entire input buffer before compression begins,
|
|
|
|
|
|
// allocate output buffer large enough to hold entire compressed output
|
2023-01-10 10:50:12 +01:00
|
|
|
|
int compress_all_at_once = 0;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void __cdecl Set_compress_all_at_once(int n) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
compress_all_at_once = n;
|
|
|
|
|
|
#ifndef FREEARC_COMPRESSION_DLL
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static FARPROC f = LoadFromDLL("Set_compress_all_at_once");
|
|
|
|
|
|
static FARPROC f2 = LoadFromDLL("Set_compress_all_at_once", TRUE);
|
|
|
|
|
|
if (f)
|
|
|
|
|
|
((void(__cdecl *)(int))f)(n);
|
|
|
|
|
|
if (f2)
|
|
|
|
|
|
((void(__cdecl *)(int))f2)(n);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Enable debugging output
|
|
|
|
|
|
int debug_mode = 0;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void __cdecl Set_debug_mode(int n) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
debug_mode = n;
|
|
|
|
|
|
#ifndef FREEARC_COMPRESSION_DLL
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static FARPROC f = LoadFromDLL("Set_debug_mode");
|
|
|
|
|
|
static FARPROC f2 = LoadFromDLL("Set_debug_mode", TRUE);
|
|
|
|
|
|
if (f)
|
|
|
|
|
|
((void(__cdecl *)(int))f)(n);
|
|
|
|
|
|
if (f2)
|
|
|
|
|
|
((void(__cdecl *)(int))f2)(n);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ***********************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация класса STORING_METHOD *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ***********************************************************************************************************************
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Функция "(рас)паковки", копирующая данные один в один
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int copy_data(CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
char buf[BUFFER_SIZE];
|
|
|
|
|
|
int len;
|
|
|
|
|
|
while ((len = callback("read", buf, BUFFER_SIZE, auxdata)) > 0) {
|
|
|
|
|
|
if ((len = callback("write", buf, len, auxdata)) < 0)
|
|
|
|
|
|
break;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
return len;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Функция распаковки
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int STORING_METHOD::decompress(CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
return copy_data(callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Функция упаковки
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int STORING_METHOD::compress(CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
return copy_data(callback, auxdata);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Конструирует объект типа STORING_METHOD или возвращает NULL, если это другой
|
|
|
|
|
|
// метод сжатия
|
|
|
|
|
|
COMPRESSION_METHOD *parse_STORING(char **parameters) {
|
|
|
|
|
|
if (strcmp(parameters[0], "storing") == 0 && parameters[1] == NULL)
|
|
|
|
|
|
// Если название метода - "storing" и параметров у него нет, то это наш
|
|
|
|
|
|
// метод
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return new STORING_METHOD;
|
|
|
|
|
|
else
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return NULL; // Это не метод storing
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static int STORING_x = AddCompressionMethod(
|
|
|
|
|
|
parse_STORING); // Зарегистрируем парсер метода STORING_METHOD
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ***********************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация класса CRC_METHOD *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ***********************************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef FREEARC_DECOMPRESS_ONLY
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Функция упаковки, просто "съедающая" входные жанные
|
2023-01-11 10:08:11 +01:00
|
|
|
|
int CRC_METHOD::compress(CALLBACK_FUNC *callback, void *auxdata) {
|
|
|
|
|
|
char buf[BUFFER_SIZE];
|
|
|
|
|
|
int len;
|
|
|
|
|
|
while ((len = callback("read", buf, BUFFER_SIZE, auxdata)) > 0)
|
|
|
|
|
|
;
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return len;
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#endif // !defined (FREEARC_DECOMPRESS_ONLY)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Конструирует объект типа CRC_METHOD или возвращает NULL, если это другой
|
|
|
|
|
|
// метод сжатия
|
|
|
|
|
|
COMPRESSION_METHOD *parse_CRC(char **parameters) {
|
|
|
|
|
|
if (strcmp(parameters[0], "crc") == 0 && parameters[1] == NULL)
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Если название метода - "crc" и параметров у него нет, то это наш метод
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return new CRC_METHOD;
|
|
|
|
|
|
else
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return NULL; // Это не метод crc
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static int CRC_x =
|
|
|
|
|
|
AddCompressionMethod(parse_CRC); // Зарегистрируем парсер метода CRC_METHOD
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ***********************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Реализация класса FAKE_METHOD *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ***********************************************************************************************************************
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// Конструирует объект типа FAKE_METHOD или возвращает NULL, если это другой
|
|
|
|
|
|
// метод сжатия
|
|
|
|
|
|
COMPRESSION_METHOD *parse_FAKE(char **parameters) {
|
|
|
|
|
|
if (strcmp(parameters[0], "fake") == 0 && parameters[1] == NULL)
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Если название метода - "fake" и параметров у него нет, то это наш метод
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return new FAKE_METHOD;
|
|
|
|
|
|
else
|
2023-01-11 10:08:11 +01:00
|
|
|
|
return NULL; // Это не метод fake
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static int FAKE_x = AddCompressionMethod(
|
|
|
|
|
|
parse_FAKE); // Зарегистрируем парсер метода FAKE_METHOD
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
// ****************************************************************************************************************************
|
2023-01-11 10:08:11 +01:00
|
|
|
|
// ВЫЧИСЛЕНИЕ CRC-32 *
|
2023-01-10 10:50:12 +01:00
|
|
|
|
// ****************************************************************************************************************************
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef FREEARC_COMPRESSION_DLL
|
|
|
|
|
|
|
|
|
|
|
|
// cdecl shells around 7-zip's fastcall assembler routines
|
2023-01-11 10:08:11 +01:00
|
|
|
|
extern "C" uint32 __fastcall CrcUpdateT8(uint32 StartCRC, void *data,
|
|
|
|
|
|
size_t size, const uint32 *table);
|
|
|
|
|
|
uint32 CrcUpdate(uint32 StartCRC, void *data, size_t size,
|
|
|
|
|
|
const uint32 *table) {
|
|
|
|
|
|
return CrcUpdateT8(StartCRC, data, size, table);
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
extern "C" void(__fastcall AesCtr_Code_Intel)(UInt32 *ivAes, Byte *data,
|
|
|
|
|
|
size_t numBlocks);
|
|
|
|
|
|
void Fast_AesCtr_Code(UInt32 *ivAes, Byte *data, size_t numBlocks) {
|
|
|
|
|
|
AesCtr_Code_Intel(ivAes, data, numBlocks);
|
|
|
|
|
|
}
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
|
|
|
|
#define kCrcPoly 0xEDB88320
|
|
|
|
|
|
#define CRC_NUM_TABLES 8
|
|
|
|
|
|
uint32 g_CrcTable[256 * CRC_NUM_TABLES];
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
void CrcGenerateTable() {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
uint32 i;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
for (i = 0; i < 256; i++) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
uint32 r = i;
|
|
|
|
|
|
int j;
|
|
|
|
|
|
for (j = 0; j < 8; j++)
|
|
|
|
|
|
r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
|
|
|
|
|
|
g_CrcTable[i] = r;
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#if CRC_NUM_TABLES > 1
|
|
|
|
|
|
for (; i < 256 * CRC_NUM_TABLES; i++) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
uint32 r = g_CrcTable[i - 256];
|
|
|
|
|
|
g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8);
|
|
|
|
|
|
}
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#endif
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
#define CRC_UPDATE_BYTE_2(crc, b) \
|
|
|
|
|
|
(g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8))
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
uint32 UpdateCRC(const void *data, size_t size, UInt32 v) {
|
|
|
|
|
|
if (g_CrcTable[elements(g_CrcTable) - 1] == 0)
|
2023-01-10 10:50:12 +01:00
|
|
|
|
CrcGenerateTable();
|
|
|
|
|
|
|
2023-01-11 10:08:11 +01:00
|
|
|
|
static FARPROC f = LoadFromDLL("CrcUpdate");
|
|
|
|
|
|
if (f)
|
|
|
|
|
|
return ((uint32(__cdecl *)(uint32, const void *, uint, const uint32 *))f)(
|
|
|
|
|
|
v, data, size, g_CrcTable);
|
2023-01-10 10:50:12 +01:00
|
|
|
|
|
|
|
|
|
|
const Byte *p = (const Byte *)data;
|
|
|
|
|
|
for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++)
|
|
|
|
|
|
v = CRC_UPDATE_BYTE_2(v, *p);
|
2023-01-11 10:08:11 +01:00
|
|
|
|
for (; size >= 4; size -= 4, p += 4) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
v ^= *(const UInt32 *)p;
|
2023-01-11 10:08:11 +01:00
|
|
|
|
v = g_CrcTable[0x300 + (v & 0xFF)] ^ g_CrcTable[0x200 + ((v >> 8) & 0xFF)] ^
|
|
|
|
|
|
g_CrcTable[0x100 + ((v >> 16) & 0xFF)] ^
|
|
|
|
|
|
g_CrcTable[0x000 + ((v >> 24))];
|
2023-01-10 10:50:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
for (; size > 0; size--, p++)
|
|
|
|
|
|
v = CRC_UPDATE_BYTE_2(v, *p);
|
|
|
|
|
|
return v;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-10 13:27:20 +01:00
|
|
|
|
// Вычислить CRC блока данных
|
2023-01-11 10:08:11 +01:00
|
|
|
|
uint32 CalcCRC(const void *Addr, size_t Size) {
|
2023-01-10 10:50:12 +01:00
|
|
|
|
return UpdateCRC(Addr, Size, INIT_CRC) ^ INIT_CRC;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif
|