#include "Compression.h" #include "MultiThreading.h" // **************************************************************************************************************************** // Internal basic (de)compression routines * // **************************************************************************************************************************** // (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); double time0 = GetThreadCPUTime(); #ifndef FREEARC_DECOMPRESS_ONLY int result = (direction==COMPRESS? compressor->compress (callback, auxdata) : compressor->decompress (callback, auxdata)); #else int result = (direction==COMPRESS? FREEARC_ERRCODE_ONLY_DECOMPRESS : compressor->decompress (callback, auxdata)); #endif double time1 = GetThreadCPUTime(), t; if (time0>=0 && time1>=0 && compressor->addtime>=0) t = compressor->addtime + time1 - time0; else t = -1; callback ("time", &t, 0, auxdata); return result; } // (De)compress data with method encoded as string 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); delete compressor; return result;} else return FREEARC_ERRCODE_INVALID_COMPRESSOR; } // **************************************************************************************************************************** // Механизм передачи данных от одного алгоритма (рас)паковки другому * // **************************************************************************************************************************** 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() {} }; 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 // Послать _size байт из _buf int Write (BYTE *_buf, int _size) { buf = _buf; size = _size; read.Release(); // даём разрешение на чтение (в буфере появились данные) if (!NoMoreData) write.Wait(); // ожидаем разрешения на выход (после того, как все данные будут прочитаны) return NoMoreData? FREEARC_ERRCODE_NO_MORE_DATA_REQUIRED : _size; } // Получить _size байт в _buf. _size==0 используется для проверки готовности к чтению int Read (BYTE *_buf, int _size) { int prev=0; loop: //if (_size==0) return prev; read.Wait(); // ожидаем разрешения на чтение (появления данных в буфере) if (NoMoreData) // данных больше не будет - предыдущий тред завершён { read.Release(); // возвращаем разрешение на чтение return prev; } else if (_size <= size) // данных в буфере достаточно для выполнения чтения { memcpy (_buf, buf, _size); buf += _size; size -= _size; read.Release(); // возвращаем разрешение на чтение return prev+_size; } else // size < _size // данных в буфере недостаточно для выполнения чтения { memcpy (_buf, buf, size); _buf += size; _size -= size; prev += size; write.Release(); // даём разрешение на выход из записи (буфер пуст) goto loop; } } }; // **************************************************************************************************************************** // Multi-stream input/output classes ****************************************************************************************** // **************************************************************************************************************************** // Формат многопоточного файла: [Заголовок] [Данные] // Заголовок ::= [Смещение к началу потока 1]..[Смещение к началу потока N-1] // Данные ::= [Кусочек потока 0] [Кусочек потока i] [Кусочек потока j]... // Кусочек потока k ::= [Длина кусочка] [Данные кусочка] [Смещение к следующему кусочку того же потока] // Длины/смещения записываются 4-байтными числами в Intel-формате. 0 используется как спецфлаг, реальные значения перед записью увеличиваются на 10. // Спецфлаг в поле смещения озгначает, что кусочков этого потока больше нет. Спецфлаг в поле длины означает, что этот кусочек длится до конца файла. // Перейти на заданную позицию во входном/выходном файле #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; \ } \ } // Пара 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=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 || nStreams<=1) return FREEARC_ERRCODE_GENERAL; if (first_time) { // Initialization of multi-stream reading first_time = FALSE; curpos = 0; for (int i=1; iRead (n, buf, size);} }; 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);} }; // **************************************************************************************************************************** // Упаковка и распаковка данных деревом методов * // **************************************************************************************************************************** // Глобальные данные процесса (рас)паковки 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;} // Set overall multi_de_compress() exit code void SetExitCode (int code) { Lock _(retcode_cs); // 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) retcode = code; } }; // Локальные данные одного метода 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; }; 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); // Упаковать/распаковать данные деревом методов static int multi_de_compress (COMPRESSION direction, char *compressor, CALLBACK_FUNC *callback, void *auxdata) { // Мы будем работать с копией строки, потому что split() модифицирует её 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); // Разобьём компрессор на отдельные алгоритмы и запустим для каждого из них отдельный тред CMETHOD cm[MAX_METHODS_IN_COMPRESSOR]; 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; // Arrays storing inputs/ouputs for all methods 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;} // Сформируем взаимосвязи входов и выходов алгоритмов в соответствии с деревом методов for (int i=N-1; i>=0; i--) { int nStreams = CompressionService (cm[i], "NumOutStreams"); if (nStreams <= 0) return FREEARC_ERRCODE_INVALID_COMPRESSOR; if (direction==COMPRESS) // СОЗДАДИМ ДЕРЕВО ВЫХОДОВ ДЛЯ УПАКОВКИ ============================================================ { AllocInputs (Local[i], 1); Local[i].Input[0] = i>0? (AsInput*) &Channel[i-1] : &Reader[0]; AllocOutputs (Local[i], nStreams); Local[i].Output[0] = i0? (AsOutput*) &Channel[i-1] : &Writer[0]; AllocInputs (Local[i], nStreams); Local[i].Input[0] = iGlobal; // Не запускать этот 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; iNumInputs; i++) Local->Input[i] ->NoMoreReads(); // Tell the input threads that no more data are required for (int i=0; iNumOutputs; i++) Local->Output[i]->NoMoreWrites(); // Tell the output threads that no more data will be supplied to them return 0; } // Callback-функция для multi_de_compress_thread(), перехватывающая операции чтения и записи static int multi_de_compress_callback (const char *what, void *_buf, int size, void *paramPtr) { Locals *Local = (Locals*) paramPtr; Globals *Global = Local->Global; 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); } // 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); } // progress: пропустить поскольку мы пока не умеем комбинировать "progress" от цепочки упаковщиков else if (strequ(what,"progress")) { return FREEARC_ERRCODE_NOT_IMPLEMENTED; } // Все остальные запросы передаются на выполнение в оригинальный callback else return Global->callback (what, buf, size, Global->auxdata); } // **************************************************************************************************************************** // Compression * // **************************************************************************************************************************** #ifndef FREEARC_DECOMPRESS_ONLY // 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); } // Put to the output stream string describing the compressor and then the compressed data int CompressWithHeader (char *orig_compressor, CALLBACK_FUNC *callback, void *auxdata) { // Split compressor into methods char new_compressor [MAX_COMPRESSOR_STRLEN], canonical_methods [MAX_COMPRESSOR_STRLEN], *p = canonical_methods; CMETHOD method[MAX_METHODS_IN_COMPRESSOR]; 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=0) result = Compress (orig_compressor, callback, auxdata); // Use original compressor string since it may include hints omitted by CanonizeCompressionMethod(purify=TRUE) return result; } #endif // !defined (FREEARC_DECOMPRESS_ONLY) // **************************************************************************************************************************** // Decompression * // **************************************************************************************************************************** // 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); } // 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; iGETTER(); \ 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) #ifndef FREEARC_DECOMPRESS_ONLY // Информация о памяти, необходимой для упаковки/распаковки 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() { MemSize dict = GetDictionary(); if (dict) return dict; MemSize BlockSize = GetBlockSize(); if (BlockSize) return BlockSize; return 0; } // Универсальный метод. Параметры: // what: "compress", "decompress", "setCompressionMem", "limitDictionary"... // data: данные для операции в формате, зависящем от конкретной выполняемой операции // param&result: простой числовой параметр, что достаточно для многих информационных операций // Неиспользуемые параметры устанавливайте в NULL/0. result<0 - код ошибки int COMPRESSION_METHOD::doit (char *what, int param, void *data, CALLBACK_FUNC *callback) { if (strequ (what, "encryption?")) return 0; // Это алгоритм шифрования? #ifndef FREEARC_DECOMPRESS_ONLY 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; } // Вывести в canonical_method каноническое представление метода сжатия in_method int CanonizeCompressionMethod (char *method, char *canonical_method, int purify) { COMPRESSION_METHOD *compressor = ParseCompressionMethod (method); if (compressor){ compressor->ShowCompressionMethod (canonical_method, purify!=0); delete compressor; return FREEARC_OK;} else return FREEARC_ERRCODE_INVALID_COMPRESSOR; } // **************************************************************************************************************************** // (De)compress data from memory buffer (input) to another memory buffer (output) * // **************************************************************************************************************************** // Callback-функция чтения/записи для (рас)паковки в памяти int ReadWriteMem (const char *what, void *buf, int size, void *_membuf) { MemBuf *membuf = (MemBuf*)_membuf; if (strequ(what,"read") && membuf->readPtr) { // Скопировать данные из readPtr в buf и продвинуть указатель чтения int read_bytes = membuf->readLeftreadLeft : size; memcpy (buf, membuf->readPtr, read_bytes); membuf->readPtr += read_bytes; membuf->readLeft -= read_bytes; return read_bytes; } else if (strequ(what,"write") && membuf->writePtr) { // Скопировать данные из buf в writePtr и продвинуть указатель записи if (size > membuf->writeLeft) return FREEARC_ERRCODE_OUTBLOCK_TOO_SMALL; memcpy (membuf->writePtr, buf, size); membuf->writePtr += size; membuf->writeLeft -= size; return size; } else { // Остальные операции отдаём "наверх" return (membuf->callback? membuf->callback (what, buf, size, membuf->auxdata) : FREEARC_ERRCODE_NOT_IMPLEMENTED); } } // Реализация 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) { MemBuf membuf(input, inputSize, output, *outputSize, callback, auxdata); int result = (direction==COMPRESS? #ifndef FREEARC_DECOMPRESS_ONLY compress (ReadWriteMem, &membuf) #else FREEARC_ERRCODE_ONLY_DECOMPRESS #endif : decompress (ReadWriteMem, &membuf)); *outputSize = membuf.written(); return result; } // Распаковать данные в памяти, записав в выходной буфер не более outputSize байт. // Возвращает код ошибки или количество байт, записанных в выходной буфер int DecompressMem (char *compressor, void *input, int inputSize, void *output, int outputSize) { MemBuf membuf(input, inputSize, output, outputSize); int result = Decompress (compressor, ReadWriteMem, &membuf); return result<0 ? result : membuf.written(); } // Аналог DecompressMem с чтением алгоритма сжатия из самого входного буфера int DecompressMemWithHeader (void *input, int inputSize, void *output, int outputSize) { MemBuf membuf(input, inputSize, output, outputSize); int result = DecompressWithHeader (ReadWriteMem, &membuf); return result<0 ? result : membuf.written(); } #ifndef FREEARC_DECOMPRESS_ONLY // Упаковать данные в памяти, записав в выходной буфер не более outputSize байт. // Возвращает код ошибки или количество байт, записанных в выходной буфер int CompressMem (char *compressor, void *input, int inputSize, void *output, int outputSize) { MemBuf membuf(input, inputSize, output, outputSize); int result = Compress (compressor, ReadWriteMem, &membuf); return result<0 ? result : membuf.written(); } // Упаковать данные в памяти, записав в выходной буфер не более outputSize байт. // Возвращает код ошибки или количество байт, записанных в выходной буфер int CompressMemWithHeader (char *compressor, void *input, int inputSize, void *output, int outputSize) { MemBuf membuf(input, inputSize, output, outputSize); int result = CompressWithHeader (compressor, ReadWriteMem, &membuf); return result<0 ? result : membuf.written(); } #endif // !defined (FREEARC_DECOMPRESS_ONLY) // **************************************************************************************************************************** // УТИЛИТЫ * // **************************************************************************************************************************** // Разбить COMPRESSOR на отдельные алгоритмы сжатия/шифрования //void splitCompressor (COMPRESSOR c, ARRAY &cm) // Запросить сервис what метода сжатия method 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); delete compressor; return result;} else return FREEARC_ERRCODE_INVALID_COMPRESSOR; } // Проверить, что данный компрессор включает алгоритм шифрования int compressorIsEncrypted (COMPRESSOR c) { char compressor[MAX_COMPRESSOR_STRLEN]; strcpy (compressor, c); // Разобьём компрессор на отдельные алгоритмы и поищем среди них алгоритм шифрования CMETHOD arr[MAX_METHODS_IN_COMPRESSOR]; split (compressor, COMPRESSION_METHODS_DELIMITER, arr, MAX_METHODS_IN_COMPRESSOR); for (CMETHOD *cm=arr; *cm; cm++) if (CompressionService (*cm, "encryption?") == 1) return TRUE; return FALSE; } // Вычислить, сколько памяти нужно для распаковки данных, сжатых этим компрессором (портит c) MemSize compressorGetDecompressionMem (COMPRESSOR c) { // Разобьём компрессор на отдельные алгоритмы и просуммируем их требования к памяти CMETHOD arr[MAX_METHODS_IN_COMPRESSOR]; split (c, COMPRESSION_METHODS_DELIMITER, arr, MAX_METHODS_IN_COMPRESSOR); MemSize sum=0; for (CMETHOD *cm=arr; *cm; cm++) sum += CompressionService (*cm, "GetDecompressionMem"); return sum; } // **************************************************************************************************************************** // ПОДДЕРЖКА ТАБЛИЦЫ ЗАРЕГИСТРИРОВАННЫХ РАЗБОРЩИКОВ МЕТОДОВ СЖАТИЯ И ПОИСК В ЭТОЙ ТАБЛИЦЕ РЕАЛИЗАЦИИ ЧИСТО КОНКРЕТНОГО МЕТОДА * // **************************************************************************************************************************** template struct Parser { PARSER parser; void* data; }; int cmCount = 0; // Кол-во зарегистрированных методов сжатия Parser cmTable[MAX_COMPRESSION_METHODS]; // Таблица, в которую записываются все зарегистрированные парсеры методов сжатия // Добавить новый метод в список поддерживаемых методов сжатия int AddCompressionMethod (CM_PARSER parser) { CHECK (FREEARC_ERRCODE_INTERNAL, cmCount < elements(cmTable), (s,"INTERNAL ERROR: Overflow of compression methods table")); cmTable[cmCount++].parser = parser; return 0; } int cmExternalCount = 0; // Кол-во зарегистрированных внешних методов сжатия Parser cmExternalTable[MAX_COMPRESSION_METHODS]; // Таблица, в которую записываются все зарегистрированные парсеры внешних методов сжатия // Очистить таблицу внешних упаковщиков void ClearExternalCompressorsTable (void) { static int builtins = -1; if (builtins<0) builtins=cmExternalCount; cmExternalCount = builtins; // Оставим только встроенные описания внешних упаковщиков } // Добавить парсер метода с дополнительным параметром, который должен быть передан этому парсеру int AddExternalCompressionMethod (CM_PARSER2 parser, void *data) { CHECK (FREEARC_ERRCODE_GENERAL, cmExternalCount < elements(cmExternalTable), (s,"Too much external compression methods defined")); cmExternalTable[cmExternalCount].parser = parser; cmExternalTable[cmExternalCount].data = data; cmExternalCount++; return 0; } // Сконструировать объект класса 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; } iterate_var (i, cmCount) { COMPRESSION_METHOD *m = (*cmTable[i].parser) (parameters); if (m) return m; } return NULL; // Полученный метод сжатия не опознан ни одним из парсеров } // **************************************************************************************************************************** // Loading compression methods from external DLLs * // **************************************************************************************************************************** // Load accelerated function either from facompress.dll or facompress_mt.dll FARPROC LoadFromDLL (char *funcname, int only_facompress_mt) { return NULL; } // Other compression methods may chain-redefine this callback in order to perform their own cleanup procedures static void NOP(){} void (*BeforeUnloadDLL)() = &NOP; // This function unloads DLLs containing accelerated compression functions void UnloadDLL (void) { (*BeforeUnloadDLL)(); } // This function cleans up the Compression Library void compressionLib_cleanup (void) { removeTemporaryFiles(); } // **************************************************************************************************************************** // Compression Library global settings * // **************************************************************************************************************************** // Get/set number of threads used for (de)compression. 0 means "autodetect" static int CompressionThreads = 1; int __cdecl GetCompressionThreads (void) {return CompressionThreads;} void __cdecl SetCompressionThreads (int threads) { CompressionThreads = threads==0? 1 : threads; #ifndef FREEARC_COMPRESSION_DLL 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); #endif } // Used in 4x4 only: read entire input buffer before compression begins, allocate output buffer large enough to hold entire compressed output int compress_all_at_once = 0; void __cdecl Set_compress_all_at_once (int n) { compress_all_at_once = n; #ifndef FREEARC_COMPRESSION_DLL 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); #endif } // Enable debugging output int debug_mode = 0; void __cdecl Set_debug_mode (int n) { debug_mode = n; #ifndef FREEARC_COMPRESSION_DLL 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); #endif } // *********************************************************************************************************************** // Реализация класса STORING_METHOD * // *********************************************************************************************************************** // Функция "(рас)паковки", копирующая данные один в один 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; } return len; } // Функция распаковки int STORING_METHOD::decompress (CALLBACK_FUNC *callback, void *auxdata) { return copy_data (callback, auxdata); } #ifndef FREEARC_DECOMPRESS_ONLY // Функция упаковки int STORING_METHOD::compress (CALLBACK_FUNC *callback, void *auxdata) { return copy_data (callback, auxdata); } #endif // !defined (FREEARC_DECOMPRESS_ONLY) // Конструирует объект типа STORING_METHOD или возвращает NULL, если это другой метод сжатия COMPRESSION_METHOD* parse_STORING (char** parameters) { if (strcmp (parameters[0], "storing") == 0 && parameters[1]==NULL ) // Если название метода - "storing" и параметров у него нет, то это наш метод return new STORING_METHOD; else return NULL; // Это не метод storing } static int STORING_x = AddCompressionMethod (parse_STORING); // Зарегистрируем парсер метода STORING_METHOD // *********************************************************************************************************************** // Реализация класса CRC_METHOD * // *********************************************************************************************************************** #ifndef FREEARC_DECOMPRESS_ONLY // Функция упаковки, просто "съедающая" входные жанные int CRC_METHOD::compress (CALLBACK_FUNC *callback, void *auxdata) { char buf[BUFFER_SIZE]; int len; while ((len = callback ("read", buf, BUFFER_SIZE, auxdata)) > 0); return len; } #endif // !defined (FREEARC_DECOMPRESS_ONLY) // Конструирует объект типа CRC_METHOD или возвращает NULL, если это другой метод сжатия COMPRESSION_METHOD* parse_CRC (char** parameters) { if (strcmp (parameters[0], "crc") == 0 && parameters[1]==NULL ) // Если название метода - "crc" и параметров у него нет, то это наш метод return new CRC_METHOD; else return NULL; // Это не метод crc } static int CRC_x = AddCompressionMethod (parse_CRC); // Зарегистрируем парсер метода CRC_METHOD // *********************************************************************************************************************** // Реализация класса FAKE_METHOD * // *********************************************************************************************************************** // Конструирует объект типа FAKE_METHOD или возвращает NULL, если это другой метод сжатия COMPRESSION_METHOD* parse_FAKE (char** parameters) { if (strcmp (parameters[0], "fake") == 0 && parameters[1]==NULL ) // Если название метода - "fake" и параметров у него нет, то это наш метод return new FAKE_METHOD; else return NULL; // Это не метод fake } static int FAKE_x = AddCompressionMethod (parse_FAKE); // Зарегистрируем парсер метода FAKE_METHOD // **************************************************************************************************************************** // ВЫЧИСЛЕНИЕ CRC-32 * // **************************************************************************************************************************** #ifdef FREEARC_COMPRESSION_DLL // cdecl shells around 7-zip's fastcall assembler routines 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);} 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);} #else #define kCrcPoly 0xEDB88320 #define CRC_NUM_TABLES 8 uint32 g_CrcTable[256 * CRC_NUM_TABLES]; void CrcGenerateTable() { uint32 i; for (i = 0; i < 256; i++) { uint32 r = i; int j; for (j = 0; j < 8; j++) r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); g_CrcTable[i] = r; } #if CRC_NUM_TABLES > 1 for (; i < 256 * CRC_NUM_TABLES; i++) { uint32 r = g_CrcTable[i - 256]; g_CrcTable[i] = g_CrcTable[r & 0xFF] ^ (r >> 8); } #endif } #define CRC_UPDATE_BYTE_2(crc, b) (g_CrcTable[((crc) ^ (b)) & 0xFF] ^ ((crc) >> 8)) uint32 UpdateCRC (const void *data, size_t size, UInt32 v) { if (g_CrcTable[elements(g_CrcTable)-1] == 0) CrcGenerateTable(); static FARPROC f = LoadFromDLL ("CrcUpdate"); if (f) return ((uint32 (__cdecl *)(uint32, const void*, uint, const uint32*)) f) (v,data,size,g_CrcTable); const Byte *p = (const Byte *)data; #if defined(FREEARC_INTEL_BYTE_ORDER) for (; size > 0 && ((unsigned)(ptrdiff_t)p & 3) != 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); for (; size >= 4; size -= 4, p += 4) { v ^= *(const UInt32 *)p; v = g_CrcTable[0x300 + (v & 0xFF)] ^ g_CrcTable[0x200 + ((v >> 8) & 0xFF)] ^ g_CrcTable[0x100 + ((v >> 16) & 0xFF)] ^ g_CrcTable[0x000 + ((v >> 24))]; } #endif for (; size > 0; size--, p++) v = CRC_UPDATE_BYTE_2(v, *p); return v; } // Вычислить CRC блока данных uint32 CalcCRC (const void *Addr, size_t Size) { return UpdateCRC(Addr, Size, INIT_CRC) ^ INIT_CRC; } #endif