diff -Nru leanify-0.4.3/build_gcc.bat leanify-0.4.3+git20181014/build_gcc.bat --- leanify-0.4.3/build_gcc.bat 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/build_gcc.bat 2017-12-03 04:14:03.000000000 +0000 @@ -0,0 +1,10 @@ +@Echo off +Pushd %~dp0 +SetLocal EnableDelayedExpansion +Set Args=-std=c++14 -O3 -msse2 -mfpmath=sse -fno-exceptions -fno-rtti -flto -I./lib -s -o "Leanify" Leanify.res +For /r "%~dp0" %%i In (*.cpp *.c *.cc) Do (Set t=%%i && Set Args=!Args!!t:%~dp0=!) +For %%i In (fileio_linux.cpp lib\mozjpeg\jstdhuff.c) Do Set Args=!Args:%%i =! +windres --output-format=coff Leanify.rc Leanify.res +g++ %Args% +Del Leanify.res +Pause diff -Nru leanify-0.4.3/debian/changelog leanify-0.4.3+git20181014/debian/changelog --- leanify-0.4.3/debian/changelog 2018-05-12 16:21:35.000000000 +0000 +++ leanify-0.4.3+git20181014/debian/changelog 2018-12-13 22:34:17.000000000 +0000 @@ -1,7 +1,13 @@ -leanify (0.4.3-1sergeyd1.1~bionic1) bionic; urgency=medium +leanify (0.4.3+git20181014-1sergeyd1.1~bionic1) bionic; urgency=medium + + * Update from git by 2018-10-14 + + -- Sergey Dryabzhinsky Sat, 03 Nov 2018 20:01:47 +0300 + +leanify (0.4.3-1sergeyd1.1~trusty1) trusty; urgency=medium * Initial release * Fix LTO support * Add ARMhf support (launchpad) - -- Sergey Dryabzhinsky Tue, 11 Apr 2017 02:02:40 +0300 + -- Sergey Dryabzhinsky Tue, 11 Apr 2017 16:24:17 +0300 diff -Nru leanify-0.4.3/debian/control leanify-0.4.3+git20181014/debian/control --- leanify-0.4.3/debian/control 2017-04-10 23:10:05.000000000 +0000 +++ leanify-0.4.3+git20181014/debian/control 2018-11-03 17:18:50.000000000 +0000 @@ -2,7 +2,7 @@ Section: utils Priority: extra Maintainer: Sergey Dryabzhinsky -Build-Depends: debhelper (>= 8), quilt +Build-Depends: debhelper (>= 9), g++ (>= 4.9) Standards-Version: 3.9.8 Homepage: https://github.com/JayXon/Leanify/ diff -Nru leanify-0.4.3/debian/patches/01-fix-flto-armhf-support.diff leanify-0.4.3+git20181014/debian/patches/01-fix-flto-armhf-support.diff --- leanify-0.4.3/debian/patches/01-fix-flto-armhf-support.diff 2017-04-11 00:55:41.000000000 +0000 +++ leanify-0.4.3+git20181014/debian/patches/01-fix-flto-armhf-support.diff 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ ---- a/Makefile.orig 2015-11-26 09:11:59.000000000 +0300 -+++ b/Makefile 2017-04-11 03:53:45.000000000 +0300 -@@ -6,9 +6,19 @@ - ZOPFLI_SRC := lib/zopfli/hash.c lib/zopfli/squeeze.c lib/zopfli/gzip_container.c lib/zopfli/katajainen.c lib/zopfli/zopfli_lib.c lib/zopfli/cache.c lib/zopfli/zlib_container.c lib/zopfli/util.c lib/zopfli/tree.c lib/zopfli/deflate.c lib/zopfli/blocksplitter.c lib/zopfli/lz77.c - ZOPFLIPNG_SRC := lib/zopflipng/lodepng/lodepng.cpp lib/zopflipng/lodepng/lodepng_util.cpp lib/zopflipng/zopflipng_lib.cc - --CFLAGS += -Wall -O3 -msse2 -mfpmath=sse -+CFLAGS += -Wall -O3 -+ -+ifeq (,$(filter $(DEB_BUILD_ARCH),i386 i686 amd64 x86_64)) -+CFLAGS += -msse2 -mfpmath=sse -+endif -+ -+ifeq (,$(filter $(DEB_BUILD_ARCH),armhf)) -+CFLAGS += -mcpu=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard -+endif -+ - ifneq ($(CC),clang) - CFLAGS += -flto -+ LDFLAGS += -fuse-linker-plugin -fuse-ld=gold -flto - endif - - ifeq ($(shell uname -s),Darwin) -@@ -17,6 +27,9 @@ - LDFLAGS += -s - endif - -+CXXFLAGS = $(CFLAGS) -+ -+ - .PHONY: leanify clean - - leanify: lzma.a miniz.o mozjpeg.a tinyxml2.o zopfli.a zopflipng.a diff -Nru leanify-0.4.3/debian/patches/series leanify-0.4.3+git20181014/debian/patches/series --- leanify-0.4.3/debian/patches/series 2017-04-11 00:55:53.000000000 +0000 +++ leanify-0.4.3+git20181014/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01-fix-flto-armhf-support.diff diff -Nru leanify-0.4.3/debian/rules leanify-0.4.3+git20181014/debian/rules --- leanify-0.4.3/debian/rules 2017-04-11 00:28:58.000000000 +0000 +++ leanify-0.4.3+git20181014/debian/rules 2018-11-03 17:22:10.000000000 +0000 @@ -1,16 +1,17 @@ #!/usr/bin/make -f -export DEB_BUILD_MAINT_OPTIONS ?= hardening=+all +export DEB_BUILD_MAINT_OPTIONS ?= hardening=-pie DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk --include /usr/share/quilt/quilt.make +#-include /usr/share/quilt/quilt.make %: - dh $@ --parallel --with quilt + dh $@ --parallel +# dh $@ --parallel --with quilt override_dh_auto_build: # It's uses flto on linux - dh_auto_build -- LDFLAGS="$(LDFLAGS) -fuse-linker-plugin -fuse-ld=gold -flto" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS)" + dh_auto_build -- LDFLAGS="$(LDFLAGS) -fuse-linker-plugin -fuse-ld=gold -flto" CFLAGS="$(CFLAGS)" CPPFLAGS="$(CPPFLAGS) -I./lib -D _7ZIP_ST" override_dh_install: dh_testdir diff -Nru leanify-0.4.3/fileio.cpp leanify-0.4.3+git20181014/fileio.cpp --- leanify-0.4.3/fileio.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/fileio.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,210 +0,0 @@ -#include "fileio.h" - -#include -#include - -#ifndef _WIN32 -#include - -#include -#include -#include -#endif - -using std::cerr; -using std::endl; - -// traverse directory and call Callback() for each file -#ifdef _WIN32 -void TraverseDirectory(const wchar_t Dir[], int Callback(const wchar_t file_path[])) -{ - WIN32_FIND_DATA FindFileData; - wchar_t DirSpec[MAX_PATH]; - //DWORD dwError; - lstrcpy(DirSpec, Dir); - lstrcat(DirSpec, L"\\*"); - - HANDLE hFind = FindFirstFile(DirSpec, &FindFileData); - - if (hFind == INVALID_HANDLE_VALUE) - { - FindClose(hFind); - return; - } - - while (FindNextFile(hFind, &FindFileData) != 0) - { - if (FindFileData.cFileName[0] == '.') - { - if (FindFileData.cFileName[1] == 0 || // "." - lstrcmp(FindFileData.cFileName + 1, L".") == 0) // ".." - { - continue; - } - } - - wchar_t DirAdd[MAX_PATH]; - lstrcpy(DirAdd, Dir); - lstrcat(DirAdd, L"\\"); - lstrcat(DirAdd, FindFileData.cFileName); - - if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - // directory - TraverseDirectory(DirAdd, Callback); - } - else - { - // file - Callback(DirAdd); - } - } - - FindClose(hFind); -} -#else -void TraverseDirectory(const char Dir[], int Callback(const char file_path[], const struct stat *sb, int typeflag)) -{ - if (ftw(Dir, Callback, 16)) - { - perror("ftw"); - } -} -#endif // _WIN32 - - -#ifdef _WIN32 -bool IsDirectory(const wchar_t path[]) -{ - DWORD fa = GetFileAttributes(path); - if (fa == INVALID_FILE_ATTRIBUTES) - { - return false; - } - return (fa & FILE_ATTRIBUTE_DIRECTORY) != 0; -} -#else -bool IsDirectory(const char path[]) -{ - struct stat sb; - if (!stat(path, &sb)) - { - return (sb.st_mode & S_IFDIR) != 0; - } - return false; -} -#endif // _WIN32 - - -#ifdef _WIN32 -namespace -{ - -void PrintErrorMessage(const char *msg) -{ - char *error_msg = nullptr; - FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, GetLastError(), 0, reinterpret_cast(&error_msg), 0, nullptr); - cerr << msg << endl; - if (error_msg) - { - cerr << error_msg << endl; - LocalFree(error_msg); - } -} - -} // namespace - -File::File(const wchar_t *filepath) -{ - fp_ = nullptr; - hFile_ = CreateFile(filepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, nullptr); - if (hFile_ == INVALID_HANDLE_VALUE) - { - PrintErrorMessage("Open file error!"); - size_ = 0; - return; - } - size_ = GetFileSize(hFile_, nullptr); - hMap_ = CreateFileMapping(hFile_, nullptr, PAGE_READWRITE, 0, 0, nullptr); - if (hMap_ == INVALID_HANDLE_VALUE) - { - PrintErrorMessage("Map file error!"); - return; - } - fp_ = MapViewOfFile(hMap_, FILE_MAP_ALL_ACCESS, 0, 0, 0); -} - -void File::UnMapFile(size_t new_size) -{ - if (new_size < size_) - { - if (!FlushViewOfFile(fp_, 0)) - { - PrintErrorMessage("Write file error!"); - } - } - if (!UnmapViewOfFile(fp_)) - { - PrintErrorMessage("UnmapViewOfFile error!"); - } - CloseHandle(hMap_); - if (new_size) - { - SetFilePointer(hFile_, new_size, nullptr, FILE_BEGIN); - if (!SetEndOfFile(hFile_)) - { - PrintErrorMessage("SetEndOfFile error!"); - } - } - CloseHandle(hFile_); - fp_ = nullptr; -} -#else -File::File(const char *filepath) -{ - fp_ = nullptr; - fd_ = open(filepath, O_RDWR); - - if (fd_ == -1) - { - perror("Open file error"); - return; - } - - struct stat sb; - if (fstat(fd_, &sb) == -1) - { - perror("fstat"); - return; - } - size_ = sb.st_size; - - // map the file into memory - fp_ = mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); - if (fp_ == MAP_FAILED) - { - perror("Map file error"); - fp_ = nullptr; - } -} - -void File::UnMapFile(size_t new_size) -{ - if (munmap(fp_, size_) == -1) - { - perror("munmap"); - } - if (new_size) - { - if (ftruncate(fd_, new_size) == -1) - { - perror("ftruncate"); - } - } - - close(fd_); - fp_ = nullptr; -} - - -#endif // _WIN32 \ No newline at end of file diff -Nru leanify-0.4.3/fileio.h leanify-0.4.3+git20181014/fileio.h --- leanify-0.4.3/fileio.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/fileio.h 2018-09-22 02:17:15.000000000 +0000 @@ -1,66 +1,69 @@ -#ifndef FILEIO_H -#define FILEIO_H +#ifndef FILEIO_H_ +#define FILEIO_H_ #include #ifdef _WIN32 #ifndef UNICODE #define UNICODE -#endif +#endif // UNICODE #ifndef _UNICODE #define _UNICODE -#endif +#endif // _UNICODE #include #else #include #include -#endif // _WIN32 +#endif // _WIN32 - -class File -{ -public: +class File { + public: #ifdef _WIN32 - explicit File(const wchar_t *filepath); + explicit File(const wchar_t* filepath); #else - explicit File(const char *filepath); -#endif // _WIN32 + explicit File(const char* filepath); +#endif // _WIN32 + + ~File() { + if (fp_ != nullptr) + UnMapFile(size_); + } + + // Disallow copy and assign. + File(const File&) = delete; + File& operator=(const File&) = delete; + + void* GetFilePionter() const { + return fp_; + } - void *GetFilePionter() const - { - return fp_; - } - - unsigned int GetSize() const - { - return size_; - } - - bool IsOK() const - { - return fp_ != nullptr; - } + unsigned int GetSize() const { + return size_; + } - void UnMapFile(size_t new_size); + bool IsOK() const { + return fp_ != nullptr; + } -private: + void UnMapFile(size_t new_size); + + private: #ifdef _WIN32 - HANDLE hFile_, hMap_; + HANDLE hFile_ = INVALID_HANDLE_VALUE; + HANDLE hMap_ = INVALID_HANDLE_VALUE; #else - int fd_; -#endif // _WIN32 - void *fp_; - size_t size_; + int fd_ = -1; +#endif // _WIN32 + void* fp_ = nullptr; + size_t size_ = 0; }; - #ifdef _WIN32 -void TraverseDirectory(const wchar_t Dir[], int Callback(const wchar_t file_path[])); -bool IsDirectory(const wchar_t path[]); +void TraverseDirectory(const wchar_t* dir, int Callback(const wchar_t* file_path)); +bool IsDirectory(const wchar_t* path); #else -void TraverseDirectory(const char Dir[], int Callback(const char file_path[], const struct stat *sb, int typeflag)); -bool IsDirectory(const char path[]); -#endif // _WIN32 - +void TraverseDirectory(const char* dir, int Callback(const char* file_path, const struct stat* sb, int typeflag)); +bool IsDirectory(const char* path); +#endif // _WIN32 -#endif \ No newline at end of file +#endif // FILEIO_H_ diff -Nru leanify-0.4.3/fileio_linux.cpp leanify-0.4.3+git20181014/fileio_linux.cpp --- leanify-0.4.3/fileio_linux.cpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/fileio_linux.cpp 2018-09-22 02:17:15.000000000 +0000 @@ -0,0 +1,60 @@ +#include "fileio.h" + +#include +#include + +#include + +#include +#include +#include + +using std::cerr; +using std::endl; + +// traverse directory and call callback() for each file +void TraverseDirectory(const char* dir, int callback(const char* file_path, const struct stat* sb, int typeflag)) { + if (ftw(dir, callback, 16)) + perror("ftw"); +} + +bool IsDirectory(const char* path) { + struct stat sb; + if (!stat(path, &sb)) + return (sb.st_mode & S_IFDIR) != 0; + return false; +} + +File::File(const char* filepath) { + fd_ = open(filepath, O_RDWR); + + if (fd_ == -1) { + perror("Open file error"); + return; + } + + struct stat sb; + if (fstat(fd_, &sb) == -1) { + perror("fstat"); + return; + } + size_ = sb.st_size; + + // map the file into memory + fp_ = mmap(nullptr, size_, PROT_READ | PROT_WRITE, MAP_SHARED, fd_, 0); + if (fp_ == MAP_FAILED) { + perror("Map file error"); + fp_ = nullptr; + } +} + +void File::UnMapFile(size_t new_size) { + if (munmap(fp_, size_) == -1) + perror("munmap"); + if (new_size) + if (ftruncate(fd_, new_size) == -1) + perror("ftruncate"); + + close(fd_); + fp_ = nullptr; +} diff -Nru leanify-0.4.3/fileio_win.cpp leanify-0.4.3+git20181014/fileio_win.cpp --- leanify-0.4.3/fileio_win.cpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/fileio_win.cpp 2018-09-22 02:17:15.000000000 +0000 @@ -0,0 +1,101 @@ +#include "fileio.h" + +#include +#include + +using std::cerr; +using std::endl; + +namespace { + +void PrintErrorMessage(const char* msg) { + char* error_msg = nullptr; + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER, nullptr, GetLastError(), 0, + reinterpret_cast(&error_msg), 0, nullptr); + cerr << msg << endl; + if (error_msg) { + cerr << error_msg << endl; + LocalFree(error_msg); + } +} + +} // namespace + +// traverse directory and call Callback() for each file +void TraverseDirectory(const wchar_t* dir, int callback(const wchar_t* file_path)) { + WIN32_FIND_DATA FindFileData; + wchar_t DirSpec[MAX_PATH]; + lstrcpy(DirSpec, dir); + lstrcat(DirSpec, L"\\*"); + + HANDLE hFind = FindFirstFile(DirSpec, &FindFileData); + + if (hFind == INVALID_HANDLE_VALUE) { + PrintErrorMessage("FindFirstFile error!"); + return; + } + + while (FindNextFile(hFind, &FindFileData) != 0) { + if (FindFileData.cFileName[0] == '.') { + if (FindFileData.cFileName[1] == 0 || // "." + lstrcmp(FindFileData.cFileName + 1, L".") == 0) // ".." + continue; + } + + wchar_t DirAdd[MAX_PATH]; + lstrcpy(DirAdd, dir); + lstrcat(DirAdd, L"\\"); + lstrcat(DirAdd, FindFileData.cFileName); + + if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + // directory + TraverseDirectory(DirAdd, callback); + } else { + // file + callback(DirAdd); + } + } + + FindClose(hFind); +} + +bool IsDirectory(const wchar_t* path) { + DWORD fa = GetFileAttributes(path); + if (fa == INVALID_FILE_ATTRIBUTES) + return false; + return (fa & FILE_ATTRIBUTE_DIRECTORY) != 0; +} + +File::File(const wchar_t* filepath) { + hFile_ = CreateFile(filepath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, nullptr); + if (hFile_ == INVALID_HANDLE_VALUE) { + PrintErrorMessage("Open file error!"); + return; + } + size_ = GetFileSize(hFile_, nullptr); + hMap_ = CreateFileMapping(hFile_, nullptr, PAGE_READWRITE, 0, 0, nullptr); + if (hMap_ == INVALID_HANDLE_VALUE) { + PrintErrorMessage("Map file error!"); + return; + } + fp_ = MapViewOfFile(hMap_, FILE_MAP_ALL_ACCESS, 0, 0, 0); +} + +void File::UnMapFile(size_t new_size) { + if (new_size < size_) + if (!FlushViewOfFile(fp_, 0)) + PrintErrorMessage("Write file error!"); + + if (!UnmapViewOfFile(fp_)) + PrintErrorMessage("UnmapViewOfFile error!"); + + CloseHandle(hMap_); + if (new_size) { + SetFilePointer(hFile_, new_size, nullptr, FILE_BEGIN); + if (!SetEndOfFile(hFile_)) + PrintErrorMessage("SetEndOfFile error!"); + } + CloseHandle(hFile_); + fp_ = nullptr; +} diff -Nru leanify-0.4.3/formats/base64.cpp leanify-0.4.3+git20181014/formats/base64.cpp --- leanify-0.4.3/formats/base64.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/base64.cpp 2017-12-03 04:14:03.000000000 +0000 @@ -2,143 +2,121 @@ #include #include +#include #include "../leanify.h" -namespace -{ +namespace { -int Base64Decode(const uint8_t *in, size_t in_len, uint8_t *out, size_t *out_len) -{ - static const uint8_t d[] = - { - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, - 54, 55, 56, 57, 58, 59, 60, 61, 66, 66, 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 66, 66, 66, 66, 66, 66, 26, 27, 28, - 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, - 66, 66, 66, 66, 66, 66 - }; - - uint32_t buf = 1; - size_t len = 0; - - for (size_t i = 0; i < in_len; i++) - { - uint8_t c = d[in[i]]; - - switch (c) - { - case 66: - return 2; // invalid input, return error - case 65: - i = in_len; // pad character, end of data - case 64: - continue; // skip whitespace - default: - buf = buf << 6 | c; - - // If the buffer is full, split it into bytes - if (buf & 0x1000000) - { - if ((len += 3) > *out_len) return 1; - *out++ = buf >> 16; - *out++ = buf >> 8; - *out++ = buf; - buf = 1; - } +int Base64Decode(const uint8_t* in, size_t in_len, uint8_t* out, size_t* out_len) { + static const uint8_t d[] = { + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 64, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 62, 66, 66, 66, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, + 66, 66, 66, 65, 66, 66, 66, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, + 22, 23, 24, 25, 66, 66, 66, 66, 66, 66, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, + 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66 + }; + + uint32_t buf = 1; + size_t len = 0; + + for (size_t i = 0; i < in_len; i++) { + uint8_t c = d[in[i]]; + + switch (c) { + case 66: + return 2; // invalid input, return error + case 65: + i = in_len; // pad character, end of data + // Fall through. + case 64: + continue; // skip whitespace + default: + buf = buf << 6 | c; + + // If the buffer is full, split it into bytes + if (buf & 0x1000000) { + if ((len += 3) > *out_len) + return 1; + *out++ = buf >> 16; + *out++ = buf >> 8; + *out++ = buf; + buf = 1; } } + } - if (buf & 0x40000) - { - if ((len += 2) > *out_len) return 1; - *out++ = buf >> 10; - *out = buf >> 2; - } - else if (buf & 0x1000) - { - if (++len > *out_len) return 1; - *out = buf >> 4; - } + if (buf & 0x40000) { + if ((len += 2) > *out_len) + return 1; + *out++ = buf >> 10; + *out = buf >> 2; + } else if (buf & 0x1000) { + if (++len > *out_len) + return 1; + *out = buf >> 4; + } - *out_len = len; - return 0; + *out_len = len; + return 0; } -size_t Base64Encode(const uint8_t *in, size_t in_len, uint8_t *out) -{ - static const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; - size_t resultIndex = 0; - - // increment over the length of the string, three characters at a time - for (size_t x = 0; x < in_len; x += 3) - { - // these three 8-bit (ASCII) characters become one 24-bit number - uint32_t n = in[x] << 16; - - if ((x + 2) < in_len) - { - n += (in[x + 1] << 8) + in[x + 2]; - } - else if ((x + 1) < in_len) - { - n += in[x + 1] << 8; - } - - // if we have one byte available, then its encoding is spread out over two characters - out[resultIndex++] = base64chars[(n >> 18) & 63]; - out[resultIndex++] = base64chars[(n >> 12) & 63]; - - if ((x + 2) < in_len) - { - out[resultIndex++] = base64chars[(n >> 6) & 63]; - out[resultIndex++] = base64chars[n & 63]; - } - else - { - if ((x + 1) < in_len) - { - out[resultIndex++] = base64chars[(n >> 6) & 63]; - } - else - { - out[resultIndex++] = '='; - } - out[resultIndex++] = '='; - } - +size_t Base64Encode(const uint8_t* in, size_t in_len, uint8_t* out) { + static const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + size_t resultIndex = 0; + + // increment over the length of the string, three characters at a time + for (size_t x = 0; x < in_len; x += 3) { + // these three 8-bit (ASCII) characters become one 24-bit number + uint32_t n = in[x] << 16; + + if ((x + 2) < in_len) { + n += (in[x + 1] << 8) + in[x + 2]; + } else if ((x + 1) < in_len) { + n += in[x + 1] << 8; } - return resultIndex; -} -} // namespace - -size_t Base64::Leanify(size_t size_leanified /*= 0*/) -{ - // 4 base64 character contains information of 3 bytes - size_t binary_len = size_ * 3 / 4; - uint8_t *binary_data = new uint8_t[binary_len]; - - if (Base64Decode(fp_, size_, binary_data, &binary_len)) - { - std::cerr << "Base64 decode error." << std::endl; - delete[] binary_data; - return Format::Leanify(size_leanified); + // if we have one byte available, then its encoding is spread out over two characters + out[resultIndex++] = base64chars[(n >> 18) & 63]; + out[resultIndex++] = base64chars[(n >> 12) & 63]; + + if ((x + 2) < in_len) { + out[resultIndex++] = base64chars[(n >> 6) & 63]; + out[resultIndex++] = base64chars[n & 63]; + } else { + if ((x + 1) < in_len) { + out[resultIndex++] = base64chars[(n >> 6) & 63]; + } else { + out[resultIndex++] = '='; + } + out[resultIndex++] = '='; } + } + return resultIndex; +} - // Leanify embedded file - binary_len = LeanifyFile(binary_data, binary_len); +} // namespace - fp_ -= size_leanified; - // encode back - size_ = Base64Encode(binary_data, binary_len, fp_); +size_t Base64::Leanify(size_t size_leanified /*= 0*/) { + // 4 base64 character contains information of 3 bytes + size_t binary_len = size_ * 3 / 4; + std::vector binary_data(binary_len); + + if (Base64Decode(fp_, size_, binary_data.data(), &binary_len)) { + std::cerr << "Base64 decode error." << std::endl; + return Format::Leanify(size_leanified); + } + + // Leanify embedded file + binary_len = LeanifyFile(binary_data.data(), binary_len); + + fp_ -= size_leanified; + // encode back + size_ = Base64Encode(binary_data.data(), binary_len, fp_); - delete[] binary_data; - return size_; + return size_; } diff -Nru leanify-0.4.3/formats/base64.h leanify-0.4.3+git20181014/formats/base64.h --- leanify-0.4.3/formats/base64.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/base64.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,16 +1,13 @@ -#ifndef BASE64_H -#define BASE64_H +#ifndef FORMATS_BASE64_H_ +#define FORMATS_BASE64_H_ #include "format.h" -class Base64 : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; +class Base64 : public Format { + public: + using Format::Format; + size_t Leanify(size_t size_leanified = 0) override; }; - -#endif \ No newline at end of file +#endif // FORMATS_BASE64_H_ diff -Nru leanify-0.4.3/formats/bmp.cpp leanify-0.4.3+git20181014/formats/bmp.cpp --- leanify-0.4.3/formats/bmp.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/bmp.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -4,24 +4,21 @@ const uint8_t Bmp::header_magic[] = { 0x42, 0x4D }; -size_t Bmp::Leanify(size_t size_leanified /*= 0*/) -{ - // It seems higher version of BITMAPINFOHEADER is very rare. - // Eg: BITMAPV4HEADER, BITMAPV5HEADER - // So I'm not going to implement it for now. - uint32_t bmp_size = *(uint32_t *)(fp_ + 2); - if (bmp_size > size_) - { - std::cerr << "BMP corrupted." << std::endl; - return Format::Leanify(size_leanified); - } - size_ = bmp_size; +size_t Bmp::Leanify(size_t size_leanified /*= 0*/) { + // It seems higher version of BITMAPINFOHEADER is very rare. + // Eg: BITMAPV4HEADER, BITMAPV5HEADER + // So I'm not going to implement it for now. + uint32_t bmp_size = *(uint32_t*)(fp_ + 2); + if (bmp_size > size_) { + std::cerr << "BMP corrupted." << std::endl; + return Format::Leanify(size_leanified); + } + size_ = bmp_size; - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, size_); - fp_ -= size_leanified; - } + if (size_leanified) { + memmove(fp_ - size_leanified, fp_, size_); + fp_ -= size_leanified; + } - return size_; + return size_; } \ No newline at end of file diff -Nru leanify-0.4.3/formats/bmp.h leanify-0.4.3+git20181014/formats/bmp.h --- leanify-0.4.3/formats/bmp.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/bmp.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,21 +1,32 @@ -#ifndef BMP_H -#define BMP_H - +#ifndef FORMATS_BMP_H_ +#define FORMATS_BMP_H_ +#include "../utils.h" #include "format.h" // The support for this format is not yet implemented see bmp.cpp for details. -class Bmp : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[2]; - +class Bmp : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; + + static const uint8_t header_magic[2]; + + PACK(struct BITMAPINFOHEADER { + uint32_t biSize; + int32_t biWidth; + int32_t biHeight; + uint16_t biPlanes; + uint16_t biBitCount; + uint32_t biCompression; + uint32_t biSizeImage; + int32_t biXPelsPerMeter; + int32_t biYPelsPerMeter; + uint32_t biClrUsed; + uint32_t biClrImportant; + }); }; - -#endif \ No newline at end of file +#endif // FORMATS_BMP_H_ diff -Nru leanify-0.4.3/formats/data_uri.cpp leanify-0.4.3+git20181014/formats/data_uri.cpp --- leanify-0.4.3/formats/data_uri.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/data_uri.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -10,59 +10,56 @@ using std::endl; using std::string; -size_t DataURI::Leanify(size_t size_leanified /*= 0*/) -{ - uint8_t *p_read = fp_, *p_write = fp_ - size_leanified; - - while (p_read < fp_ + size_) - { - const string magic = "data:image/"; - uint8_t *new_p_read = std::search(p_read, fp_ + size_, magic.begin(), magic.end()); - // move anything in between - memmove(p_write, p_read, new_p_read - p_read); - p_write += new_p_read - p_read; - p_read = new_p_read; - - if (p_read >= fp_ + size_) - { - break; - } - - const string base64_sign = ";base64,"; - // data:image/png;base64, data:image/jpeg;base64, - // maximum gap is 4 - uint8_t *search_end = p_read + magic.size() + 4 + base64_sign.size(); - uint8_t *start = std::search(p_read + magic.size(), search_end, base64_sign.begin(), base64_sign.end()) + base64_sign.size(); - - memmove(p_write, p_read, start - p_read); - p_write += start - p_read; - p_read = start; - - if (p_read > search_end) - { - continue; - } - - const char quote[] = { '\'', '"', ')' }; - uint8_t *end = std::find_first_of(p_read, fp_ + size_, quote, quote + sizeof(quote)); - if (end < fp_ + size_) - { - if (is_verbose) - { - cout << string(reinterpret_cast(new_p_read), start + 8 - new_p_read) << "... found at offset 0x" << std::hex << new_p_read - fp_ << endl; - } - size_t new_size = Base64(p_read, end - p_read).Leanify(p_read - p_write); - p_write += new_size; - p_read = end; - } - else - { - memmove(p_write, p_read, fp_ + size_ - p_read); - p_write += fp_ + size_ - p_read; - p_read = fp_ + size_; - } +size_t DataURI::Leanify(size_t size_leanified /*= 0*/) { + uint8_t *p_read = fp_, *p_write = fp_ - size_leanified; + + while (p_read < fp_ + size_) { + const string magic = "data:image/"; + uint8_t* data_magic = std::search(p_read, fp_ + size_, magic.begin(), magic.end()); + + if (data_magic >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + + const string base64_sign = ";base64,"; + const size_t kMaxSearchGap = 64; + uint8_t* search_end = data_magic + magic.size() + kMaxSearchGap + base64_sign.size(); + search_end = std::min(search_end, fp_ + size_); + uint8_t* start = + std::search(data_magic + magic.size(), search_end, base64_sign.begin(), base64_sign.end()) + base64_sign.size(); + + memmove(p_write, p_read, start - p_read); + p_write += start - p_read; + p_read = start; + + if (p_read > search_end) + continue; + + const string quote = "'\")"; + uint8_t* end = fp_ + size_; + if (!single_mode_) { + end = std::find_first_of(p_read, fp_ + size_, quote.begin(), quote.end()); + if (end >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + } + + if (is_verbose) { + cout << string(reinterpret_cast(data_magic), start + 8 - data_magic) << "... found"; + if (!single_mode_) { + cout << " at offset 0x" << std::hex << data_magic - fp_ << std::dec; + } + cout << endl; } - fp_ -= size_leanified; - size_ = p_write - fp_; - return size_; + size_t new_size = Base64(p_read, end - p_read).Leanify(p_read - p_write); + p_write += new_size; + p_read = end; + } + fp_ -= size_leanified; + size_ = p_write - fp_; + return size_; } diff -Nru leanify-0.4.3/formats/data_uri.h leanify-0.4.3+git20181014/formats/data_uri.h --- leanify-0.4.3/formats/data_uri.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/data_uri.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,20 +1,23 @@ -#ifndef DATA_URI_H -#define DATA_URI_H - +#ifndef FORMATS_DATA_URI_H_ +#define FORMATS_DATA_URI_H_ #include "format.h" - extern bool is_verbose; - -class DataURI : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; +class DataURI : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; + + void SetSingleMode(bool single_mode) { + single_mode_ = single_mode; + } + + private: + // Single mode means DataURI is the whole file. + bool single_mode_ = false; }; - -#endif \ No newline at end of file +#endif // FORMATS_DATA_URI_H_ diff -Nru leanify-0.4.3/formats/dwf.cpp leanify-0.4.3+git20181014/formats/dwf.cpp --- leanify-0.4.3/formats/dwf.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/dwf.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -2,19 +2,15 @@ #include "zip.h" -// (DWF V06.00)PK -const uint8_t Dwf::header_magic[] = { 0x28, 0x44, 0x57, 0x46, 0x20, 0x56, 0x30, 0x36, 0x2E, 0x30, 0x30, 0x29, 0x50, 0x4B, 0x03, 0x04 }; +// "(DWF V06." +const uint8_t Dwf::header_magic[] = { 0x28, 0x44, 0x57, 0x46, 0x20, 0x56, 0x30, 0x36, 0x2E }; -size_t Dwf::Leanify(size_t size_leanified /*= 0*/) -{ - const size_t dwf_magic_len = 12; - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, dwf_magic_len); - } - Zip zip(fp_ + dwf_magic_len, size_ - dwf_magic_len); - size_ = zip.Leanify(size_leanified) + dwf_magic_len; - fp_ -= size_leanified; - return size_; +size_t Dwf::Leanify(size_t size_leanified /*= 0*/) { + const size_t dwf_header_len = 12; + if (memcmp(fp_ + dwf_header_len, Zip::header_magic, sizeof(Zip::header_magic))) { + return Format::Leanify(size_leanified); + } + size_ = Zip(fp_, size_).Leanify(size_leanified); + fp_ -= size_leanified; + return size_; } - diff -Nru leanify-0.4.3/formats/dwf.h leanify-0.4.3+git20181014/formats/dwf.h --- leanify-0.4.3/formats/dwf.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/dwf.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,21 +1,15 @@ -#ifndef DWF_H -#define DWF_H - +#ifndef FORMATS_DWF_H_ +#define FORMATS_DWF_H_ #include "format.h" +class Dwf : public Format { + public: + using Format::Format; + size_t Leanify(size_t size_leanified = 0) override; -class Dwf : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[16]; + static const uint8_t header_magic[9]; }; - - -#endif \ No newline at end of file +#endif // FORMATS_DWF_H_ diff -Nru leanify-0.4.3/formats/format.h leanify-0.4.3+git20181014/formats/format.h --- leanify-0.4.3/formats/format.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/format.h 2018-09-22 02:17:15.000000000 +0000 @@ -1,36 +1,36 @@ -#ifndef FORMAT_H -#define FORMAT_H - +#ifndef FORMATS_FORMAT_H_ +#define FORMATS_FORMAT_H_ #include #include #include // memmove +#include - -class Format -{ -public: - explicit Format(void *p, size_t s = 0) : fp_(static_cast(p)), size_(s) {}; - - virtual ~Format() {}; - - virtual size_t Leanify(size_t size_leanified = 0) - { - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, size_); - fp_ -= size_leanified; - } - - return size_; +class Format { + public: + explicit Format(std::vector& data) : fp_(data.data()), size_(data.size()) {} + explicit Format(void* p, size_t s = 0) : fp_(static_cast(p)), size_(s) {} + + virtual ~Format() = default; + + // Disallow copy and assign. + Format(const Format&) = delete; + Format& operator=(const Format&) = delete; + + virtual size_t Leanify(size_t size_leanified = 0) { + if (size_leanified) { + memmove(fp_ - size_leanified, fp_, size_); + fp_ -= size_leanified; } -protected: - // pointer to the file content - uint8_t *fp_; - // size of the file - size_t size_; -}; + return size_; + } + protected: + // pointer to the file content + uint8_t* fp_; + // size of the file + size_t size_; +}; -#endif \ No newline at end of file +#endif // FORMATS_FORMAT_H_ diff -Nru leanify-0.4.3/formats/gft.cpp leanify-0.4.3+git20181014/formats/gft.cpp --- leanify-0.4.3/formats/gft.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/gft.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -5,28 +5,21 @@ #include "../leanify.h" - const uint8_t Gft::header_magic[] = { 0x54, 0x47, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00 }; +size_t Gft::Leanify(size_t size_leanified /*= 0*/) { + // header + uint32_t header_size; + if (size_ < 0x14 || size_ <= (header_size = *(uint32_t*)(fp_ + 0x10))) { + std::cerr << "Not a valid GFT file." << std::endl; + return Format::Leanify(size_leanified); + } + + // move header + if (size_leanified) { + memmove(fp_ - size_leanified, fp_, header_size); + fp_ -= size_leanified; + } -size_t Gft::Leanify(size_t size_leanified /*= 0*/) -{ - // header - uint32_t header_size; - if (size_ < 0x14 || size_ <= (header_size = *(uint32_t *)(fp_ + 0x10))) - { - std::cerr << "Not a valid GFT file." << std::endl; - return Format::Leanify(size_leanified); - } - - - // move header - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, header_size); - fp_ -= size_leanified; - } - - - return header_size + LeanifyFile(fp_ + size_leanified + header_size, size_ - header_size, size_leanified); + return header_size + LeanifyFile(fp_ + size_leanified + header_size, size_ - header_size, size_leanified); } \ No newline at end of file diff -Nru leanify-0.4.3/formats/gft.h leanify-0.4.3+git20181014/formats/gft.h --- leanify-0.4.3/formats/gft.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/gft.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,21 +1,15 @@ -#ifndef GFT_H -#define GFT_H - +#ifndef FORMATS_GFT_H_ +#define FORMATS_GFT_H_ #include "format.h" +class Gft : public Format { + public: + using Format::Format; + size_t Leanify(size_t size_leanified = 0) override; -class Gft : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[8]; + static const uint8_t header_magic[8]; }; - - -#endif \ No newline at end of file +#endif // FORMATS_GFT_H_ diff -Nru leanify-0.4.3/formats/gz.cpp leanify-0.4.3+git20181014/formats/gz.cpp --- leanify-0.4.3/formats/gz.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/gz.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -4,8 +4,8 @@ #include #include -#include "../lib/miniz/miniz.h" -#include "../lib/zopfli/deflate.h" +#include +#include #include "../leanify.h" #include "../utils.h" @@ -18,116 +18,104 @@ // CM = 8 is deflate const uint8_t Gz::header_magic[] = { 0x1F, 0x8B, 0x08 }; - -size_t Gz::Leanify(size_t size_leanified /*= 0*/) -{ - // written according to this specification - // http://www.gzip.org/zlib/rfc-gzip.html - - if (size_ <= 18) - { - cerr << "Not a valid GZ file." << endl; - return Format::Leanify(size_leanified); - } - - depth++; - uint8_t flags = *(fp_ + 3); - // set the flags to 0, remove all unnecessary section - *(fp_ + 3 - size_leanified) = 0; - - uint8_t *p_read = fp_ + 10; - uint8_t *p_write = p_read - size_leanified; - - *(p_write - 2) = 2; // XFL - - if (flags & (1 << 2)) // FEXTRA - { - p_read += *(uint16_t *)p_read + 2; - } - - string filename; - if (flags & (1 << 3)) // FNAME - { - filename.assign(reinterpret_cast(p_read)); - PrintFileName(filename); - while (p_read < fp_ + size_ && *p_read++) - { - // skip string - } - } - - if (flags & (1 << 4)) // FCOMMENT - { - while (p_read < fp_ + size_ && *p_read++) - { - // skip string - } - } - - if (flags & (1 << 1)) // FHCRC - { - p_read += 2; - } - - if (p_read >= fp_ + size_) - { - return Format::Leanify(size_leanified); - } - - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, 10); - } - - if (is_fast) - { - memmove(p_write, p_read, fp_ + size_ - p_read); - return size_ - (p_read - p_write); - } - - uint32_t uncompressed_size = *(uint32_t *)(fp_ + size_ - 4); - uint32_t crc = *(uint32_t *)(fp_ + size_ - 8); - size_t original_size = fp_ + size_ - 8 - p_read; - - size_t s = 0; - uint8_t *buffer = static_cast(tinfl_decompress_mem_to_heap(p_read, original_size, &s, 0)); - - if (!buffer || - s != uncompressed_size || - crc != mz_crc32(0, buffer, uncompressed_size)) - { - cerr << "GZ corrupted!" << endl; - mz_free(buffer); - memmove(p_write, p_read, original_size + 8); - return size_ - (p_read - p_write); - } - - uncompressed_size = LeanifyFile(buffer, uncompressed_size, 0, filename); - - ZopfliOptions options; - ZopfliInitOptions(&options); - options.numiterations = iterations; - - uint8_t bp = 0, *out = nullptr; - size_t outsize = 0; - ZopfliDeflate(&options, 2, 1, buffer, uncompressed_size, &bp, &out, &outsize); - - - if (outsize < original_size) - { - memcpy(p_write, out, outsize); - p_write += outsize; - *(uint32_t *)p_write = mz_crc32(0, buffer, uncompressed_size); - *(uint32_t *)(p_write + 4) = uncompressed_size; - } - else - { - memmove(p_write, p_read, original_size + 8); - p_write += original_size; - } - mz_free(buffer); - delete[] out; - depth--; - fp_ -= size_leanified; - return p_write + 8 - fp_; +size_t Gz::Leanify(size_t size_leanified /*= 0*/) { + // written according to this specification + // http://www.gzip.org/zlib/rfc-gzip.html + + if (size_ <= 18) { + cerr << "Not a valid GZ file." << endl; + return Format::Leanify(size_leanified); + } + + depth++; + uint8_t flags = *(fp_ + 3); + + memmove(fp_ - size_leanified, fp_, 10); + + // set the flags to 0, remove all unnecessary section + *(fp_ + 3 - size_leanified) = 0; + + uint8_t* p_read = fp_ + 10; + uint8_t* p_write = p_read - size_leanified; + + // XFL + *(p_write - 2) = 2; + + // FEXTRA + if (flags & (1 << 2)) { + p_read += *(uint16_t*)p_read + 2; + } + + string filename; + // FNAME + if (flags & (1 << 3)) { + filename.assign(reinterpret_cast(p_read)); + PrintFileName(filename); + while (p_read < fp_ + size_ && *p_read++) { + // skip string + } + } + + // FCOMMENT + if (flags & (1 << 4)) { + while (p_read < fp_ + size_ && *p_read++) { + // skip string + } + } + + // FHCRC + if (flags & (1 << 1)) { + p_read += 2; + } + + if (p_read >= fp_ + size_) { + memmove(p_write, fp_ + 10, size_ - 10); + return size_; + } + + if (is_fast) { + memmove(p_write, p_read, fp_ + size_ - p_read); + return size_ - (p_read - p_write); + } + + uint32_t uncompressed_size = *(uint32_t*)(fp_ + size_ - 4); + uint32_t crc = *(uint32_t*)(fp_ + size_ - 8); + size_t original_size = fp_ + size_ - 8 - p_read; + + size_t actual_uncompressed_size = 0; + uint8_t* buffer = nullptr; + if (lodepng_inflate(&buffer, &actual_uncompressed_size, p_read, original_size, + &lodepng_default_decompress_settings) || + !buffer || actual_uncompressed_size != uncompressed_size || crc != lodepng_crc32(buffer, uncompressed_size)) { + cerr << "GZ corrupted!" << endl; + free(buffer); + memmove(p_write, p_read, original_size + 8); + return size_ - (p_read - p_write); + } + + uncompressed_size = LeanifyFile(buffer, uncompressed_size, 0, filename); + + ZopfliOptions options; + ZopfliInitOptions(&options); + options.numiterations = iterations; + + uint8_t bp = 0, *out = nullptr; + size_t outsize = 0; + ZopfliDeflate(&options, 2, 1, buffer, uncompressed_size, &bp, &out, &outsize); + + if (outsize < original_size) { + memcpy(p_write, out, outsize); + p_write += outsize; + *(uint32_t*)p_write = lodepng_crc32(buffer, uncompressed_size); + *(uint32_t*)(p_write + 4) = uncompressed_size; + } else { + memmove(p_write, p_read, original_size + 8); + p_write += original_size; + } + free(buffer); + free(out); + depth--; + fp_ -= size_leanified; + size_ = p_write + 8 - fp_; + return size_; } \ No newline at end of file diff -Nru leanify-0.4.3/formats/gz.h leanify-0.4.3+git20181014/formats/gz.h --- leanify-0.4.3/formats/gz.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/gz.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,22 +1,18 @@ -#ifndef GZ_H -#define GZ_H - +#ifndef FORMATS_GZ_H_ +#define FORMATS_GZ_H_ #include "format.h" - extern bool is_fast; extern int iterations; -class Gz : public Format -{ -public: - using Format::Format; +class Gz : public Format { + public: + using Format::Format; - size_t Leanify(size_t size_leanified = 0) override; + size_t Leanify(size_t size_leanified = 0) override; - static const uint8_t header_magic[3]; + static const uint8_t header_magic[3]; }; - -#endif \ No newline at end of file +#endif // FORMATS_GZ_H_ diff -Nru leanify-0.4.3/formats/ico.cpp leanify-0.4.3+git20181014/formats/ico.cpp --- leanify-0.4.3/formats/ico.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/ico.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,63 +1,129 @@ #include "ico.h" -#include +#include +#include +#include +#include + +#include "../utils.h" +#include "bmp.h" #include "png.h" +using std::cerr; +using std::endl; +using std::vector; const uint8_t Ico::header_magic[] = { 0x00, 0x00, 0x01, 0x00 }; +namespace { -size_t Ico::Leanify(size_t size_leanified /*= 0*/) -{ - // number of images inside ico file - uint16_t n = *(uint16_t *)(fp_ + 4); - uint8_t *p_index = fp_ - size_leanified + 6; - - // invalid Icon file - if (6 + n * 16U >= size_ || *(uint32_t *)(fp_ + 6 + 8) + *(uint32_t *)(fp_ + 6 + 12) > size_) - { - return Format::Leanify(size_leanified); +PACK( +struct IconDirEntry { + uint8_t bWidth; // Width, in pixels, of the image + uint8_t bHeight; // Height, in pixels, of the image + uint8_t bColorCount; // Number of colors in image (0 if >=8bpp) + uint8_t bReserved; // Reserved ( must be 0) + uint16_t wPlanes; // Color Planes + uint16_t wBitCount; // Bits per pixel + uint32_t dwBytesInRes; // How many bytes in this resource? + uint32_t dwImageOffset; // Where in the file is this image? +}); + +} // namespace + +size_t Ico::Leanify(size_t size_leanified /*= 0*/) { + // number of images inside ico file + const uint16_t num_of_img = *(uint16_t*)(fp_ + 4); + + // corrupt file: no image or file size too small + if (num_of_img == 0 || 6 + num_of_img * sizeof(IconDirEntry) >= size_) { + return Format::Leanify(size_leanified); + } + + // sort the entries by offset just in case it's not sorted before + IconDirEntry* entry_addr = reinterpret_cast(fp_ + 6); + vector entries(entry_addr, entry_addr + num_of_img); + std::sort(entries.begin(), entries.end(), + [](const IconDirEntry& a, const IconDirEntry& b) { return a.dwImageOffset < b.dwImageOffset; }); + + // check overlaps + for (size_t i = 1; i < entries.size(); i++) { + if (entries[i - 1].dwImageOffset + entries[i - 1].dwBytesInRes > entries[i].dwImageOffset) { + cerr << "Error: Found overlapping icon entries!" << endl; + return Format::Leanify(size_leanified); } + } - // move header - if (size_leanified) - { - memmove(fp_ - size_leanified, fp_, 6 + n * 16); + // is file size enough? + if (entries.back().dwImageOffset + entries.back().dwBytesInRes > size_) { + return Format::Leanify(size_leanified); + } + + for (size_t i = 0; i < entries.size(); i++) { + uint32_t old_offset = entries[i].dwImageOffset; + // write new offset + if (i != 0) { + entries[i].dwImageOffset = entries[i - 1].dwImageOffset + entries[i - 1].dwBytesInRes; + } else { + entries[i].dwImageOffset = 6 + num_of_img * sizeof(IconDirEntry); } - size_t new_size_leanified = 0; - while (n--) - { - uint32_t old_size = *(uint32_t *)(p_index + 8); - uint32_t offset = *(uint32_t *)(p_index + 12); - - // write new offset - if (new_size_leanified) - { - *(uint32_t *)(p_index + 12) -= new_size_leanified; - } + // Leanify PNG + if (memcmp(fp_ + old_offset, Png::header_magic, sizeof(Png::header_magic)) == 0) { + entries[i].dwBytesInRes = Png(fp_ + old_offset, entries[i].dwBytesInRes) + .Leanify(size_leanified + old_offset - entries[i].dwImageOffset); + continue; + } - // only Leanify PNG - if (memcmp(fp_ + offset, Png::header_magic, sizeof(Png::header_magic)) == 0) - { - uint32_t new_size = Png(fp_ + offset, old_size).Leanify(size_leanified + new_size_leanified); - if (new_size != old_size) - { - new_size_leanified += old_size - new_size; - *(uint32_t *)(p_index + 8) = new_size; - } + // Convert 256x256 BMP to PNG if possible + if (entries[i].bWidth == 0 && entries[i].bHeight == 0) { + auto dib = reinterpret_cast(fp_ + old_offset); + if (dib->biSize >= 40 && dib->biWidth == 256 && dib->biHeight == 512 && // DIB in ICO always has double height + dib->biPlanes == 1 && dib->biBitCount == 32 && // only support RGBA for now + dib->biCompression == 0 && // BI_RGB aka no compression + dib->biSize + std::max(dib->biSizeImage, 256 * 256 * 4U) <= entries[i].dwBytesInRes && + (dib->biSizeImage == 0 || dib->biSizeImage >= 256 * 256 * 4U) && dib->biClrUsed == 0) { + VerbosePrint("Converting 256x256 BMP to PNG..."); + // BMP stores ARGB in little endian, so it's actually BGRA, convert it to normal RGBA + // It also stores the pixels upside down for some reason, so reverse it. + uint8_t* bmp_row = fp_ + old_offset + dib->biSize + 256 * 256 * 4; + vector raw(256 * 256 * 4), png; + // TODO: detect 0RGB and convert it to RGBA using mask + for (size_t j = 0; j < 256; j++) { + bmp_row -= 256 * 4; + for (size_t k = 0; k < 256; k++) { + raw[(j * 256 + k) * 4 + 0] = bmp_row[k * 4 + 2]; + raw[(j * 256 + k) * 4 + 1] = bmp_row[k * 4 + 1]; + raw[(j * 256 + k) * 4 + 2] = bmp_row[k * 4 + 0]; + raw[(j * 256 + k) * 4 + 3] = bmp_row[k * 4 + 3]; + } } - else if (size_leanified + new_size_leanified) - { - memmove(fp_ + offset - size_leanified - new_size_leanified, fp_ + offset, old_size); + if (lodepng::encode(png, raw, 256, 256) == 0) { + // Optimize the new PNG + size_t png_size = Png(png).Leanify(); + if (png_size < entries[i].dwBytesInRes) { + entries[i].dwBytesInRes = png_size; + memcpy(fp_ + entries[i].dwImageOffset - size_leanified, png.data(), png_size); + continue; + } } - p_index += 16; + } } + memmove(fp_ + entries[i].dwImageOffset - size_leanified, fp_ + old_offset, entries[i].dwBytesInRes); + } - fp_ -= size_leanified; - // offset + size of last file - return *(uint32_t *)(p_index - 4) + *(uint32_t *)(p_index - 8); -} - + fp_ -= size_leanified; + // write headers if moved + if (size_leanified) { + memcpy(fp_, header_magic, sizeof(header_magic)); + *(uint16_t*)(fp_ + 4) = num_of_img; + } + // write new entries + memcpy(fp_ + 6, entries.data(), entries.size() * sizeof(IconDirEntry)); + + // offset + size of last img + size_ = entries.back().dwImageOffset + entries.back().dwBytesInRes; + return size_; +} diff -Nru leanify-0.4.3/formats/ico.h leanify-0.4.3+git20181014/formats/ico.h --- leanify-0.4.3/formats/ico.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/ico.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,17 +1,15 @@ -#ifndef ICO_H -#define ICO_H +#ifndef FORMATS_ICO_H_ +#define FORMATS_ICO_H_ #include "format.h" -class Ico : public Format -{ -public: - using Format::Format; +class Ico : public Format { + public: + using Format::Format; - size_t Leanify(size_t size_leanified = 0) override; + size_t Leanify(size_t size_leanified = 0) override; - static const uint8_t header_magic[4]; + static const uint8_t header_magic[4]; }; - -#endif \ No newline at end of file +#endif // FORMATS_ICO_H_ diff -Nru leanify-0.4.3/formats/jpeg.cpp leanify-0.4.3+git20181014/formats/jpeg.cpp --- leanify-0.4.3/formats/jpeg.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/jpeg.cpp 2018-11-03 16:56:30.000000000 +0000 @@ -1,118 +1,182 @@ #include "jpeg.h" +#include +#include // for mozjpeg error handling +#include + +#include const uint8_t Jpeg::header_magic[] = { 0xFF, 0xD8, 0xFF }; bool Jpeg::keep_exif_ = false; +bool Jpeg::keep_icc_profile_ = false; +bool Jpeg::keep_all_metadata_ = false; +bool Jpeg::force_arithmetic_coding_ = false; -jmp_buf Jpeg::setjmp_buffer_; - -void Jpeg::mozjpeg_error_handler(j_common_ptr cinfo) -{ - (*cinfo->err->output_message)(cinfo); - - longjmp(setjmp_buffer_, 1); -} - - +namespace { -size_t Jpeg::Leanify(size_t size_leanified /*= 0*/) -{ +jmp_buf setjmp_buffer; - struct jpeg_decompress_struct srcinfo; - struct jpeg_compress_struct dstinfo; - struct jpeg_error_mgr jsrcerr, jdsterr; - - srcinfo.err = jpeg_std_error(&jsrcerr); - jsrcerr.error_exit = mozjpeg_error_handler; - if (setjmp(setjmp_buffer_)) - { - jpeg_destroy_compress(&dstinfo); - jpeg_destroy_decompress(&srcinfo); +void mozjpeg_error_handler(j_common_ptr cinfo) { + (*cinfo->err->output_message)(cinfo); - return Format::Leanify(size_leanified); - } + if (cinfo->client_data != nullptr) + jpeg_destroy(static_cast(cinfo->client_data)); - jpeg_create_decompress(&srcinfo); + longjmp(setjmp_buffer, 1); +} - dstinfo.err = jpeg_std_error(&jdsterr); - jdsterr.error_exit = mozjpeg_error_handler; +void CompressJpeg(const j_decompress_ptr srcinfo, jvirt_barray_ptr* coef_arrays, bool baseline, bool arithmetic, + bool keep_exif, uint8_t** outbuffer, unsigned long* outsize) { + jpeg_compress_struct dstinfo; + dstinfo.client_data = &dstinfo; + + jpeg_error_mgr jdsterr; + dstinfo.err = jpeg_std_error(&jdsterr); + jdsterr.error_exit = mozjpeg_error_handler; + + jpeg_create_compress(&dstinfo); + + if (is_verbose) { + dstinfo.err->trace_level++; + } + if (baseline) { + jpeg_c_set_int_param(&dstinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST); + } + + /* Initialize destination compression parameters from source values */ + jpeg_copy_critical_parameters(srcinfo, &dstinfo); + + // use arithmetic coding if input file is arithmetic coded or if forced to + if (arithmetic) { + dstinfo.arith_code = true; + dstinfo.optimize_coding = false; + } else { + dstinfo.optimize_coding = true; + } + + /* Specify data destination for compression */ + jpeg_mem_dest(&dstinfo, outbuffer, outsize); + + /* Start compressor (note no image data is actually written here) */ + jpeg_write_coefficients(&dstinfo, coef_arrays); + + for (auto marker = srcinfo->marker_list; marker; marker = marker->next) { + if (keep_exif || marker->marker != JPEG_APP0 + 1) { + jpeg_write_marker(&dstinfo, marker->marker, marker->data, marker->data_length); + } + } + + /* Finish compression and release memory */ + jpeg_finish_compress(&dstinfo); + jpeg_destroy_compress(&dstinfo); +} - jpeg_create_compress(&dstinfo); +} // namespace - if (is_verbose) - { - dstinfo.err->trace_level++; - } - if (is_fast) - { - jpeg_c_set_int_param(&dstinfo, JINT_COMPRESS_PROFILE, JCP_FASTEST); - } +size_t Jpeg::Leanify(size_t size_leanified /*= 0*/) { + jpeg_decompress_struct srcinfo; + srcinfo.client_data = nullptr; - /* Specify data source for decompression */ - jpeg_mem_src(&srcinfo, fp_, size_); + jpeg_error_mgr jsrcerr; + srcinfo.err = jpeg_std_error(&jsrcerr); + jsrcerr.error_exit = mozjpeg_error_handler; - if (keep_exif_) - { - jpeg_save_markers(&srcinfo, JPEG_APP0 + 1, 0xFFFF); - } - - (void)jpeg_read_header(&srcinfo, true); + if (setjmp(setjmp_buffer)) { + jpeg_destroy_decompress(&srcinfo); - /* Read source file as DCT coefficients */ - auto coef_arrays = jpeg_read_coefficients(&srcinfo); + return Format::Leanify(size_leanified); + } - /* Initialize destination compression parameters from source values */ - jpeg_copy_critical_parameters(&srcinfo, &dstinfo); + jpeg_create_decompress(&srcinfo); - // use arithmetic coding if input file is arithmetic coded - if (srcinfo.arith_code) - { - dstinfo.arith_code = true; - dstinfo.optimize_coding = false; - } - else - { - dstinfo.optimize_coding = true; - } + /* Specify data source for decompression */ + jpeg_mem_src(&srcinfo, fp_, size_); - uint8_t *outbuffer = nullptr; - unsigned long outsize = 0; - /* Specify data destination for compression */ - jpeg_mem_dest(&dstinfo, &outbuffer, &outsize); - - /* Start compressor (note no image data is actually written here) */ - jpeg_write_coefficients(&dstinfo, coef_arrays); - - if (keep_exif_) - { - for (auto marker = srcinfo.marker_list; marker; marker = marker->next) - { - if (marker->marker == JPEG_APP0 + 1) - { - jpeg_write_marker(&dstinfo, marker->marker, marker->data, marker->data_length); - } + // Always save exif to show warning if orientation might change. + jpeg_save_markers(&srcinfo, JPEG_APP0 + 1, 0xFFFF); + if (keep_icc_profile_ || keep_all_metadata_) { + jpeg_save_markers(&srcinfo, JPEG_APP0 + 2, 0xFFFF); + } + if (keep_all_metadata_) { + // Save the rest APPn markers. + for (int i = 3; i < 16; i++) + jpeg_save_markers(&srcinfo, JPEG_APP0 + i, 0xFFFF); + // Save comments. + jpeg_save_markers(&srcinfo, JPEG_COM, 0xFFFF); + } + + jpeg_read_header(&srcinfo, true); + + if (!keep_exif_ && !keep_all_metadata_) { + for (auto marker = srcinfo.marker_list; marker; marker = marker->next) { + if (marker->marker == JPEG_APP0 + 1) { + // Tag number: 0x0112, data format: unsigned short(3), number of components: 1 + const uint8_t kExifOrientation[] = { 0x12, 0x01, 0x03, 0x00, 0x01, 0x00, 0x00, 0x00 }; + const uint8_t kExifOrientationMotorola[] = { 0x01, 0x12, 0x00, 0x03, 0x00, 0x00, 0x00, 0x01 }; + uint8_t* start = marker->data; + uint8_t* end = start + marker->data_length; + uint8_t* orientation_tag = std::search(start, end, kExifOrientation, std::end(kExifOrientation)); + bool big_endian = false; + if (orientation_tag == end) { + orientation_tag = std::search(start, end, kExifOrientationMotorola, std::end(kExifOrientationMotorola)); + big_endian = orientation_tag != end; + } + if (orientation_tag != end) { + uint16_t orientation = *reinterpret_cast(orientation_tag + sizeof(kExifOrientation)); + if (big_endian) + orientation = ((orientation >> 8) | (orientation << 8)) & 0xFFFF; + // Only show warning if it's not the default upper left. + if (orientation != 1) { + std::cout << "Warning: The Exif being removed contains orientation data, result image might have wrong " + "orientation, use --keep-exif to keep Exif." + << std::endl; + } } + break; + } } + } - /* Finish compression and release memory */ - jpeg_finish_compress(&dstinfo); + /* Read source file as DCT coefficients */ + auto coef_arrays = jpeg_read_coefficients(&srcinfo); - (void)jpeg_finish_decompress(&srcinfo); - jpeg_destroy_decompress(&srcinfo); + uint8_t* outbuffer = nullptr; + unsigned long outsize = 0; + + // Try progressive unless fast mode. + if (!is_fast) { + CompressJpeg(&srcinfo, coef_arrays, false, srcinfo.arith_code || force_arithmetic_coding_, + keep_all_metadata_ || keep_exif_, &outbuffer, &outsize); + } + + // Try baseline if fast mode or small file. + if (is_fast || size_ < 32768) { + uint8_t* baseline_buffer = nullptr; + unsigned long baseline_size = 0; + + CompressJpeg(&srcinfo, coef_arrays, true, srcinfo.arith_code || force_arithmetic_coding_, + keep_all_metadata_ || keep_exif_, &baseline_buffer, &baseline_size); + if (baseline_size < outsize) { + free(outbuffer); + outbuffer = baseline_buffer; + outsize = baseline_size; + } else { + free(baseline_buffer); + } + } + + (void)jpeg_finish_decompress(&srcinfo); + jpeg_destroy_decompress(&srcinfo); + + fp_ -= size_leanified; + // use mozjpeg result if it's smaller than original + if (outsize < size_) { + memcpy(fp_, outbuffer, outsize); + size_ = outsize; + } else { + memmove(fp_, fp_ + size_leanified, size_); + } + free(outbuffer); - fp_ -= size_leanified; - // use mozjpeg result if it's smaller than original - if (outsize < size_) - { - memcpy(fp_, outbuffer, outsize); - size_ = outsize; - } - else if (size_leanified) - { - memmove(fp_, fp_ + size_leanified, size_); - } - - jpeg_destroy_compress(&dstinfo); - - return size_; -} \ No newline at end of file + return size_; +} diff -Nru leanify-0.4.3/formats/jpeg.h leanify-0.4.3+git20181014/formats/jpeg.h --- leanify-0.4.3/formats/jpeg.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/jpeg.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,35 +1,22 @@ -#ifndef JPEG_H -#define JPEG_H - -#include // for mozjpeg error handling -#include - -#ifdef _WIN32 -#define XMD_H // prevents mozjpeg to redefine INT32 -#endif -#include "../lib/mozjpeg/jpeglib.h" +#ifndef FORMATS_JPEG_H_ +#define FORMATS_JPEG_H_ #include "format.h" - extern bool is_fast; extern bool is_verbose; -class Jpeg : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[3]; - static bool keep_exif_; - -private: - static jmp_buf setjmp_buffer_; - - static void mozjpeg_error_handler(j_common_ptr cinfo); +class Jpeg : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; + + static const uint8_t header_magic[3]; + static bool keep_exif_; + static bool keep_icc_profile_; + static bool keep_all_metadata_; + static bool force_arithmetic_coding_; }; - -#endif \ No newline at end of file +#endif // FORMATS_JPEG_H_ diff -Nru leanify-0.4.3/formats/lua.cpp leanify-0.4.3+git20181014/formats/lua.cpp --- leanify-0.4.3/formats/lua.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/lua.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -3,107 +3,87 @@ #include #include - const uint8_t Lua::header_magic[] = { 0x1B, 0x4C, 0x75, 0x61 }; - // copied from an old code, need to refactor someday -void Lua::FunctionParser() -{ - - uint32_t i; - uint8_t *oldp; - - // Function header - i = *(uint32_t *)p_read_; - p_read_ += i + 4; - - // remove Source name, Line defined and last line defined - memset(p_write_, 0, 12); - *(uint32_t *)(p_write_ + 12) = *(uint32_t *)(p_read_ + 8); - p_write_ += 0x10; - p_read_ += 0xC; - - - // Instruction list - i = *(uint32_t *)p_read_ * 4 + 4; - memmove(p_write_, p_read_, i); - p_write_ += i; - p_read_ += i; - - - oldp = p_read_; - // Constant list - i = *(uint32_t *)p_read_; - p_read_ += 4; - while (i--) - { - switch (*p_read_++) - { - // 1=LUA_TBOOLEAN - case 1: - p_read_++; - break; - // 3=LUA_TNUMBER - case 3: - p_read_ += 8; - break; - // 4=LUA_TSTRING - case 4: - p_read_ += *(uint32_t *)p_read_ + 4; - } - } - - - // Function prototype list - i = *(uint32_t *)p_read_; - p_read_ += 4; - - memmove(p_write_, oldp, p_read_ - oldp); - p_write_ += p_read_ - oldp; - while (i--) - { - FunctionParser(); +void Lua::FunctionParser() { + uint32_t i; + uint8_t* oldp; + + // Function header + i = *(uint32_t*)p_read_; + p_read_ += i + 4; + + // remove Source name, Line defined and last line defined + memset(p_write_, 0, 12); + *(uint32_t*)(p_write_ + 12) = *(uint32_t*)(p_read_ + 8); + p_write_ += 0x10; + p_read_ += 0xC; + + // Instruction list + i = *(uint32_t*)p_read_ * 4 + 4; + memmove(p_write_, p_read_, i); + p_write_ += i; + p_read_ += i; + + oldp = p_read_; + // Constant list + i = *(uint32_t*)p_read_; + p_read_ += 4; + while (i--) { + switch (*p_read_++) { + // 1=LUA_TBOOLEAN + case 1: + p_read_++; + break; + // 3=LUA_TNUMBER + case 3: + p_read_ += 8; + break; + // 4=LUA_TSTRING + case 4: + p_read_ += *(uint32_t*)p_read_ + 4; } + } + // Function prototype list + i = *(uint32_t*)p_read_; + p_read_ += 4; + + memmove(p_write_, oldp, p_read_ - oldp); + p_write_ += p_read_ - oldp; + while (i--) { + FunctionParser(); + } + // Source line position list (optional debug data) + p_read_ += *((uint32_t*)p_read_) * 4 + 4; - // Source line position list (optional debug data) - p_read_ += *((uint32_t *)p_read_) * 4 + 4; - - - - // Local list (optional debug data) - i = *(uint32_t *)p_read_; - p_read_ += 4; - while (i--) - { - p_read_ += *(uint32_t *)p_read_ + 12; - } - - // Upvalue list (optional debug data) - i = *(uint32_t *)p_read_; - p_read_ += 4; - while (i--) - { - p_read_ += *(uint32_t *)p_read_ + 4; - } - - // strip above optional debug data - memset(p_write_, 0, 12); - p_write_ += 12; - - + // Local list (optional debug data) + i = *(uint32_t*)p_read_; + p_read_ += 4; + while (i--) { + p_read_ += *(uint32_t*)p_read_ + 12; + } + + // Upvalue list (optional debug data) + i = *(uint32_t*)p_read_; + p_read_ += 4; + while (i--) { + p_read_ += *(uint32_t*)p_read_ + 4; + } + + // strip above optional debug data + memset(p_write_, 0, 12); + p_write_ += 12; } +size_t Lua::Leanify(size_t size_leanified /*= 0*/) { + // skip header + p_read_ += 0xC; + p_write_ = p_read_ - size_leanified; + fp_ -= size_leanified; + FunctionParser(); -size_t Lua::Leanify(size_t size_leanified /*= 0*/) -{ - // skip header - p_read_ += 0xC; - p_write_ = p_read_ - size_leanified; - fp_ -= size_leanified; - FunctionParser(); - - return p_write_ - fp_; + return p_write_ - fp_; } \ No newline at end of file diff -Nru leanify-0.4.3/formats/lua.h leanify-0.4.3+git20181014/formats/lua.h --- leanify-0.4.3/formats/lua.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/lua.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,23 +1,20 @@ -#ifndef LUA_H -#define LUA_H - +#ifndef FORMATS_LUA_H_ +#define FORMATS_LUA_H_ #include "format.h" - -class Lua : public Format -{ -public: - explicit Lua(void *p, size_t s = 0) : Format(p, s), p_read_(static_cast(p)), p_write_(static_cast(p)) {} - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[4]; - -private: - void FunctionParser(); - uint8_t *p_read_, *p_write_; +class Lua : public Format { + public: + explicit Lua(void* p, size_t s = 0) + : Format(p, s), p_read_(static_cast(p)), p_write_(static_cast(p)) {} + + size_t Leanify(size_t size_leanified = 0) override; + + static const uint8_t header_magic[4]; + + private: + void FunctionParser(); + uint8_t *p_read_, *p_write_; }; - -#endif \ No newline at end of file +#endif // FORMATS_LUA_H_ diff -Nru leanify-0.4.3/formats/mime.cpp leanify-0.4.3+git20181014/formats/mime.cpp --- leanify-0.4.3/formats/mime.cpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/mime.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,62 @@ +#include "mime.h" + +#include +#include +#include +#include + +#include "base64.h" + +using std::cout; +using std::endl; +using std::string; + +size_t Mime::Leanify(size_t size_leanified /*= 0*/) { + uint8_t *p_read = fp_, *p_write = fp_ - size_leanified; + + while (p_read < fp_ + size_) { + const string magic = "Content-Transfer-Encoding: base64"; + // Case insensitive search + uint8_t* encoding_magic = std::search(p_read, fp_ + size_, magic.begin(), magic.end(), + [](char a, char b) { return toupper(a) == toupper(b); }); + + if (encoding_magic >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + + const string double_crlf = "\r\n\r\n"; + uint8_t* start = std::search(encoding_magic + magic.size(), fp_ + size_, double_crlf.begin(), double_crlf.end()) + + double_crlf.size(); + + start = std::min(start, fp_ + size_); + + memmove(p_write, p_read, start - p_read); + p_write += start - p_read; + p_read = start; + + if (p_read >= fp_ + size_) { + break; + } + + const string dash_boundary = "\r\n--"; + uint8_t* end = std::search(start, fp_ + size_, dash_boundary.begin(), dash_boundary.end()); + if (end >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + + if (is_verbose) { + cout << string(reinterpret_cast(start), 8) << "... found at offset 0x" << std::hex << start - fp_ + << std::dec << endl; + } + size_t new_size = Base64(p_read, end - p_read).Leanify(p_read - p_write); + p_write += new_size; + p_read = end; + } + fp_ -= size_leanified; + size_ = p_write - fp_; + return size_; +} diff -Nru leanify-0.4.3/formats/mime.h leanify-0.4.3+git20181014/formats/mime.h --- leanify-0.4.3/formats/mime.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/mime.h 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,18 @@ +#ifndef FORMATS_MIME_H_ +#define FORMATS_MIME_H_ + +#include "format.h" + +extern bool is_verbose; + +// Multipurpose Internet Mail Extensions +// https://en.wikipedia.org/wiki/MIME + +class Mime : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; +}; + +#endif // FORMATS_MIME_H_ diff -Nru leanify-0.4.3/formats/pe.cpp leanify-0.4.3+git20181014/formats/pe.cpp --- leanify-0.4.3/formats/pe.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/pe.cpp 2017-12-03 04:14:03.000000000 +0000 @@ -9,484 +9,411 @@ #include "../utils.h" using std::cerr; -using std::cout; using std::endl; using std::string; const uint8_t Pe::header_magic[] = { 'M', 'Z' }; +namespace { -namespace -{ - -const string resource_types[] = -{ - // 0 - 9 - "", "CURSOR", "BITMAP", "ICON", "MENU", "DIALOG", "STRING", "FONTDIR", "FONT", "ACCELERATOR", - // 10 - 19 - "RCDATA", "MESSAGETABLE", "GROUP_CURSOR", "", "GROUP_ICON", "", "VERSION", "DLGINCLUDE", "", "PLUGPLAY", - // 20 - 24 - "VXD", "ANICURSOR", "ANIICON", "HTML", "MANIFEST" +const string resource_types[] = { + // 0 - 9 + "", "CURSOR", "BITMAP", "ICON", "MENU", "DIALOG", "STRING", "FONTDIR", "FONT", "ACCELERATOR", + // 10 - 19 + "RCDATA", "MESSAGETABLE", "GROUP_CURSOR", "", "GROUP_ICON", "", "VERSION", "DLGINCLUDE", "", "PLUGPLAY", + // 20 - 24 + "VXD", "ANICURSOR", "ANIICON", "HTML", "MANIFEST" }; -} // namespace +} // namespace +size_t Pe::Leanify(size_t size_leanified /*= 0*/) { + uint32_t pe_header_offset = *(uint32_t*)(fp_ + 0x3C); + ImageFileHeader* image_file_header = reinterpret_cast(fp_ + pe_header_offset + 4); + ImageOptionalHeader* optional_header = + reinterpret_cast((char*)image_file_header + sizeof(ImageFileHeader)); + size_t total_header_size = pe_header_offset + 4 + sizeof(ImageFileHeader); + size_t min_header_size = total_header_size + sizeof(ImageOptionalHeader); + + // check ImageFileHeader is not out of file range + // check PE signature: PE00 + // check Section Table is not out of file range + if (min_header_size > size_ || *(uint32_t*)(fp_ + pe_header_offset) != 0x00004550 || + (total_header_size += image_file_header->SizeOfOptionalHeader + + sizeof(ImageSectionHeader) * image_file_header->NumberOfSections) > size_ || + (optional_header->Magic != 0x10B && optional_header->Magic != 0x20B) || optional_header->FileAlignment == 0 || + optional_header->SectionAlignment == 0) { + cerr << "Not a valid PE file." << endl; + return Format::Leanify(size_leanified); + } + + if (optional_header->Subsystem == 1) { + VerbosePrint(".sys (driver file) detected, skip."); + return Format::Leanify(size_leanified); + } + + // make sure total_header_size will cover Optional Header + if (total_header_size < min_header_size) + total_header_size = min_header_size; + + const uint32_t new_pe_header_offset = 0x10; + + // if original file's PE header is not overlapped with DOS header + // then we can overlap it to save some space + if (pe_header_offset >= 0x40) { + total_header_size -= pe_header_offset - new_pe_header_offset; + memcpy(fp_ - size_leanified, header_magic, sizeof(header_magic)); + memset(fp_ - size_leanified + sizeof(header_magic), 0, new_pe_header_offset - sizeof(header_magic)); + memmove(fp_ - size_leanified + new_pe_header_offset, fp_ + pe_header_offset, + total_header_size - new_pe_header_offset); + image_file_header = reinterpret_cast(fp_ - size_leanified + 0x14); + optional_header = reinterpret_cast((char*)image_file_header + sizeof(ImageFileHeader)); + // set new PE Header offset + *(uint32_t*)(fp_ - size_leanified + 0x3C) = new_pe_header_offset; + } else { + // this file probably already packed with some extreme packer + VerbosePrint("PE Header already overlaps DOS Header."); + if (size_leanified) { + // move entire SizeOfHeaders to make sure nothing was missed + memmove(fp_ - size_leanified, fp_, *(uint32_t*)(fp_ + pe_header_offset + 0x54)); + image_file_header = reinterpret_cast(fp_ - size_leanified + pe_header_offset + 4); + } + } + + // Data Directories + // PE32: Magic number: 0x10B Offset: 0x60 + // PE32+: Magic number: 0x20B Offset: 0x70 + ImageDataDirectory* data_directories = + reinterpret_cast((char*)optional_header + (optional_header->Magic == 0x10B ? 0x60 : 0x70)); + + // Section Table + ImageSectionHeader* section_table = + reinterpret_cast((char*)optional_header + image_file_header->SizeOfOptionalHeader); + + // Base Relocation Table VirtualAddress + uint32_t reloc_virtual_address = data_directories[5].VirtualAddress; + uint32_t rsrc_virtual_address = data_directories[2].VirtualAddress; + + uint32_t reloc_raw_offset = 0; + uint32_t reloc_raw_size = 0; + uint32_t rsrc_raw_offset = 0; + uint32_t rsrc_virtual_size = 0; + + // 0x2000: IMAGE_FILE_DLL + // only remove reloc section if it's not dll + if (!(image_file_header->Characteristics & 0x2000)) { + // set IMAGE_FILE_RELOCS_STRIPPED + image_file_header->Characteristics |= 0x0001; + } + + // Some PE have wrong SizeOfHeaders, e.g. files packed by UPX + // I have to find the smallest PointerToRawData in all sections + // If that is smaller than SizeOfHeaders, then that's the correct SizeOfHeaders + uint32_t correct_size_of_headers = optional_header->SizeOfHeaders; + + // looking for reloc and rsrc in Section Table + for (int i = 0; i < image_file_header->NumberOfSections; i++) { + if (reloc_virtual_address && section_table[i].VirtualAddress == reloc_virtual_address) { + // if IMAGE_FILE_RELOCS_STRIPPED is set + if (image_file_header->Characteristics & 0x0001) { + reloc_raw_offset = section_table[i].PointerToRawData; + reloc_raw_size = section_table[i].SizeOfRawData; + + // remove Relocation Table in Data Directories + data_directories[5].VirtualAddress = 0; + data_directories[5].Size = 0; + + image_file_header->NumberOfSections--; + + // move sections after reloc forward + memmove(§ion_table[i], §ion_table[i + 1], + sizeof(ImageSectionHeader) * (image_file_header->NumberOfSections - i)); + + total_header_size -= sizeof(ImageSectionHeader); + i--; + + VerbosePrint("Relocation Table removed."); + } + } else if (rsrc_virtual_address && section_table[i].VirtualAddress == rsrc_virtual_address) { + rsrc_raw_offset = section_table[i].PointerToRawData; + rsrc_raw_size_ = section_table[i].SizeOfRawData; + rsrc_virtual_size = section_table[i].VirtualSize; + rsrc_ = fp_ + rsrc_raw_offset; + } + if (section_table[i].PointerToRawData < correct_size_of_headers && section_table[i].SizeOfRawData) { + correct_size_of_headers = section_table[i].PointerToRawData; + } + } + + if (correct_size_of_headers > size_) + correct_size_of_headers = size_; + + // has to be multiple of FileAlignment + uint32_t header_size_aligned = ((total_header_size - 1) | (optional_header->FileAlignment - 1)) + 1; + size_t pe_size_leanified = 0; + size_t header_size_leanified = 0; + + // if header size is reduced after aligned + if (header_size_aligned < correct_size_of_headers) { + header_size_leanified = pe_size_leanified = correct_size_of_headers - header_size_aligned; + optional_header->SizeOfHeaders = header_size_aligned; + } else if (total_header_size <= correct_size_of_headers) { + header_size_aligned = correct_size_of_headers; + } + + // fill the rest of the headers with 0 + // only if PE Header offset has changed + if (pe_header_offset >= 0x40) { + memset(fp_ - size_leanified + total_header_size, 0, header_size_aligned - total_header_size); + } + + // multiple of SectionAlignment + uint32_t reloc_virtual_size = ((reloc_raw_size - 1) | (optional_header->SectionAlignment - 1)) + 1; + + // set ASLR in DllCharacteristics to false + // it seems this isn't necessary + // *(uint16_t *)(optional_header + 0x46) &= ~0x0040; + + uint32_t reloc_end = reloc_raw_offset + reloc_raw_size; + size_t rsrc_size_leanified = 0; + if (rsrc_raw_size_) { + // decrease RVA inside rsrc section + if (rsrc_virtual_address > reloc_virtual_address) { + TraverseRSRC(reinterpret_cast(fp_ + rsrc_raw_offset), "", reloc_virtual_size); + rsrc_virtual_address -= reloc_virtual_size; + } else { + // only save all the data addresses to vector + TraverseRSRC(reinterpret_cast(fp_ + rsrc_raw_offset)); + } + + VerbosePrint(rsrc_data_.size(), " embedded resources found."); + // sort it according to it's data offset + std::sort(rsrc_data_.begin(), rsrc_data_.end(), + [](const RsrcEntry& a, const RsrcEntry& b) { return a.entry->OffsetToData < b.entry->OffsetToData; }); + + // detect non standard resource, maybe produced by some packer + if (rsrc_data_.empty() || !IsRSRCValid(rsrc_virtual_address, rsrc_virtual_size)) { + VerbosePrint("Non standard resource detected."); + if (reloc_raw_size) { + // move everything before reloc + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + reloc_raw_offset - header_size_aligned); -size_t Pe::Leanify(size_t size_leanified /*= 0*/) -{ + // move everything after reloc + memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_raw_offset); + pe_size_leanified += reloc_raw_size; + } else { + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + size_ - header_size_aligned - pe_size_leanified); + } + } else { + // should do this before memmove + uint32_t old_end = rsrc_data_.back().entry->OffsetToData + rsrc_data_.back().entry->Size; + uint32_t last_end = rsrc_data_[0].entry->OffsetToData; - uint32_t pe_header_offset = *(uint32_t *)(fp_ + 0x3C); - ImageFileHeader *image_file_header = reinterpret_cast(fp_ + pe_header_offset + 4); - ImageOptionalHeader *optional_header = reinterpret_cast((char *)image_file_header + sizeof(ImageFileHeader)); - size_t total_header_size = pe_header_offset + 4 + sizeof(ImageFileHeader); - size_t min_header_size = total_header_size + sizeof(ImageOptionalHeader); - - // check ImageFileHeader is not out of file range - // check PE signature: PE00 - // check Section Table is not out of file range - if (min_header_size > size_ || - *(uint32_t *)(fp_ + pe_header_offset) != 0x00004550 || - (total_header_size += image_file_header->SizeOfOptionalHeader + sizeof(ImageSectionHeader) * image_file_header->NumberOfSections) > size_ || - (optional_header->Magic != 0x10B && optional_header->Magic != 0x20B) || - optional_header->FileAlignment == 0 || - optional_header->SectionAlignment == 0) - { - cerr << "Not a valid PE file." << endl; - return Format::Leanify(size_leanified); - } - - if (optional_header->Subsystem == 1) - { - if (is_verbose) - { - cout << ".sys (driver file) detected, skip." << endl; - } - return Format::Leanify(size_leanified); - } + if (reloc_raw_size && reloc_raw_offset < rsrc_raw_offset) { + // move everything before reloc + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + reloc_raw_offset - header_size_aligned - pe_size_leanified); - // make sure total_header_size will cover Optional Header - if (total_header_size < min_header_size) - { - total_header_size = min_header_size; - } - - const uint32_t new_pe_header_offset = 0x10; - - // if original file's PE header is not overlapped with DOS header - // then we can overlap it to save some space - if (pe_header_offset >= 0x40) - { - total_header_size -= pe_header_offset - new_pe_header_offset; - memcpy(fp_ - size_leanified, header_magic, sizeof(header_magic)); - memset(fp_ - size_leanified + sizeof(header_magic), 0, new_pe_header_offset - sizeof(header_magic)); - memmove(fp_ - size_leanified + new_pe_header_offset, fp_ + pe_header_offset, total_header_size - new_pe_header_offset); - image_file_header = reinterpret_cast(fp_ - size_leanified + 0x14); - optional_header = reinterpret_cast((char *)image_file_header + sizeof(ImageFileHeader)); - // set new PE Header offset - *(uint32_t *)(fp_ - size_leanified + 0x3C) = new_pe_header_offset; - } - else - { - // this file probably already packed with some extreme packer - if (is_verbose) - { - cout << "PE Header already overlaps DOS Header." << endl; - } - if (size_leanified) - { - // move entire SizeOfHeaders to make sure nothing was missed - memmove(fp_ - size_leanified, fp_, *(uint32_t *)(fp_ + pe_header_offset + 0x54)); - image_file_header = reinterpret_cast(fp_ - size_leanified + pe_header_offset + 4); - } + // move things between reloc and first data of rsrc + memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, + rsrc_raw_offset - reloc_end + rsrc_data_[0].entry->OffsetToData - rsrc_virtual_address); + pe_size_leanified += reloc_raw_size; + + } else { + // move everything before first data of rsrc + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + rsrc_raw_offset - pe_size_leanified - header_size_aligned + rsrc_data_[0].entry->OffsetToData - + rsrc_virtual_address); + } + + depth++; + + for (auto& res : rsrc_data_) { + if (depth <= max_depth) { + // print resource name + PrintFileName(res.name); + } + + res.entry = reinterpret_cast(reinterpret_cast(res.entry) - pe_size_leanified - + size_leanified); + ImageResourceDataEntry* entry = res.entry; + + // it seems some of the resource has to be aligned to 4 bytes in order to work + // only align the resource if it is aligned before + if ((entry->OffsetToData & 3) == 0) { + // fill the gap with 0 + uint32_t gap = (-(int32_t)last_end) & 3; + memset(fp_ - size_leanified + rsrc_raw_offset + last_end - rsrc_virtual_address - pe_size_leanified, 0, gap); + last_end += gap; + } + + size_t new_size = LeanifyFile(fp_ + rsrc_raw_offset + entry->OffsetToData - rsrc_virtual_address, entry->Size, + entry->OffsetToData - last_end + pe_size_leanified + size_leanified, res.name); + entry->OffsetToData = last_end; + entry->Size = new_size; + last_end += new_size; + } + depth--; + rsrc_size_leanified = old_end - last_end; + uint32_t rsrc_new_end = rsrc_raw_offset + last_end - rsrc_virtual_address; + uint32_t rsrc_new_end_aligned = ((rsrc_new_end - 1) | (optional_header->FileAlignment - 1)) + 1; + uint32_t rsrc_correct_end_aligned = + ((rsrc_raw_offset + old_end - rsrc_virtual_address - 1) | (optional_header->FileAlignment - 1)) + 1; + uint32_t rsrc_end = rsrc_raw_offset + rsrc_raw_size_; + + // fill the rest of rsrc with 0 + memset(fp_ - size_leanified + rsrc_new_end - pe_size_leanified, 0, rsrc_size_leanified); + + // if rsrc_correct_end_aligned != rsrc_end then there are extra data in rsrc section + if (rsrc_new_end_aligned <= rsrc_end && rsrc_correct_end_aligned == rsrc_end) { + if (rsrc_new_end_aligned > rsrc_new_end + rsrc_size_leanified) { + memmove(fp_ - size_leanified + rsrc_new_end + rsrc_size_leanified - pe_size_leanified, + fp_ + rsrc_new_end + rsrc_size_leanified, rsrc_new_end_aligned - rsrc_new_end - rsrc_size_leanified); + } + rsrc_virtual_size = rsrc_new_end - rsrc_raw_offset; + rsrc_size_leanified = rsrc_end - rsrc_new_end_aligned; + pe_size_leanified += rsrc_size_leanified; + rsrc_raw_size_ = rsrc_new_end_aligned - rsrc_raw_offset; + } else { + if (rsrc_end > rsrc_new_end + rsrc_size_leanified) { + memmove(fp_ - size_leanified + rsrc_new_end + rsrc_size_leanified - pe_size_leanified, + fp_ + rsrc_new_end + rsrc_size_leanified, rsrc_end - rsrc_new_end - rsrc_size_leanified); + } + rsrc_size_leanified = 0; + } + if (reloc_raw_size && reloc_raw_offset > rsrc_raw_offset) { + memmove(fp_ - size_leanified + rsrc_end - pe_size_leanified, fp_ + rsrc_end, reloc_raw_offset - rsrc_end); + memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_end); + pe_size_leanified += reloc_raw_size; + } else { + memmove(fp_ - size_leanified + rsrc_end - pe_size_leanified, fp_ + rsrc_end, size_ - rsrc_end); + } } + } else if (reloc_raw_size) { + // no rsrc but have reloc to remove - // Data Directories - // PE32: Magic number: 0x10B Offset: 0x60 - // PE32+: Magic number: 0x20B Offset: 0x70 - ImageDataDirectory *data_directories = reinterpret_cast((char *)optional_header + (optional_header->Magic == 0x10B ? 0x60 : 0x70)); - - // Section Table - ImageSectionHeader *section_table = reinterpret_cast((char *)optional_header + image_file_header->SizeOfOptionalHeader); + // move everything before reloc + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + reloc_raw_offset - header_size_aligned); + // move everything after reloc + memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_raw_offset); + pe_size_leanified += reloc_raw_size; + } else { + // no rsrc and no reloc + memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, + size_ - header_size_aligned - pe_size_leanified); + } + // decrease SizeOfImage + int rsrc_decrease_size = ((data_directories[2].Size - 1) | (optional_header->SectionAlignment - 1)) - + ((rsrc_virtual_size - 1) | (optional_header->SectionAlignment - 1)); + if (rsrc_decrease_size < 0) + rsrc_decrease_size = 0; - // Base Relocation Table VirtualAddress - uint32_t reloc_virtual_address = data_directories[5].VirtualAddress; - uint32_t rsrc_virtual_address = data_directories[2].VirtualAddress; - - uint32_t reloc_raw_offset = 0; - uint32_t reloc_raw_size = 0; - uint32_t rsrc_raw_offset = 0; - uint32_t rsrc_virtual_size = 0; - - // 0x2000: IMAGE_FILE_DLL - // only remove reloc section if it's not dll - if (!(image_file_header->Characteristics & 0x2000)) - { - // set IMAGE_FILE_RELOCS_STRIPPED - image_file_header->Characteristics |= 0x0001; - } - - // Some PE have wrong SizeOfHeaders, e.g. files packed by UPX - // I have to find the smallest PointerToRawData in all sections - // If that is smaller than SizeOfHeaders, then that's the correct SizeOfHeaders - uint32_t correct_size_of_headers = optional_header->SizeOfHeaders; - - // looking for reloc and rsrc in Section Table - for (int i = 0; i < image_file_header->NumberOfSections; i++) - { - if (reloc_virtual_address && section_table[i].VirtualAddress == reloc_virtual_address) - { - // if IMAGE_FILE_RELOCS_STRIPPED is set - if (image_file_header->Characteristics & 0x0001) - { - reloc_raw_offset = section_table[i].PointerToRawData; - reloc_raw_size = section_table[i].SizeOfRawData; - - // remove Relocation Table in Data Directories - data_directories[5].VirtualAddress = 0; - data_directories[5].Size = 0; - - image_file_header->NumberOfSections--; - - // move sections after reloc forward - memmove(§ion_table[i], §ion_table[i + 1], sizeof(ImageSectionHeader) * (image_file_header->NumberOfSections - i)); - - total_header_size -= sizeof(ImageSectionHeader); - i--; - - if (is_verbose) - { - cout << "Relocation Table removed." << endl; - } - } - } - else if (rsrc_virtual_address && section_table[i].VirtualAddress == rsrc_virtual_address) - { - rsrc_raw_offset = section_table[i].PointerToRawData; - rsrc_raw_size_ = section_table[i].SizeOfRawData; - rsrc_virtual_size = section_table[i].VirtualSize; - rsrc_ = fp_ + rsrc_raw_offset; - } - if (section_table[i].PointerToRawData < correct_size_of_headers && section_table[i].SizeOfRawData) - { - correct_size_of_headers = section_table[i].PointerToRawData; - } - } + optional_header->SizeOfImage -= reloc_virtual_size + rsrc_decrease_size; - if (correct_size_of_headers > size_) - { - correct_size_of_headers = size_; - } - - // has to be multiple of FileAlignment - uint32_t header_size_aligned = ((total_header_size - 1) | (optional_header->FileAlignment - 1)) + 1; - size_t pe_size_leanified = 0; - size_t header_size_leanified = 0; - - // if header size is reduced after aligned - if (header_size_aligned < correct_size_of_headers) - { - header_size_leanified = pe_size_leanified = correct_size_of_headers - header_size_aligned; - optional_header->SizeOfHeaders = header_size_aligned; - } - else if (total_header_size <= correct_size_of_headers) - { - header_size_aligned = correct_size_of_headers; - } - - // fill the rest of the headers with 0 - // only if PE Header offset has changed - if (pe_header_offset >= 0x40) - { - memset(fp_ - size_leanified + total_header_size, 0, header_size_aligned - total_header_size); - } - - // multiple of SectionAlignment - uint32_t reloc_virtual_size = ((reloc_raw_size - 1) | (optional_header->SectionAlignment - 1)) + 1; - - // set ASLR in DllCharacteristics to false - // it seems this isn't necessary - // *(uint16_t *)(optional_header + 0x46) &= ~0x0040; - - uint32_t reloc_end = reloc_raw_offset + reloc_raw_size; - size_t rsrc_size_leanified = 0; - if (rsrc_raw_size_) - { - // decrease RVA inside rsrc section - if (rsrc_virtual_address > reloc_virtual_address) - { - TraverseRSRC(reinterpret_cast(fp_ + rsrc_raw_offset), "", reloc_virtual_size); - rsrc_virtual_address -= reloc_virtual_size; - } - else - { - // only save all the data addresses to vector - TraverseRSRC(reinterpret_cast(fp_ + rsrc_raw_offset)); - } - - if (is_verbose) - { - cout << rsrc_data_.size() << " embedded resources found." << endl; - } - // sort it according to it's data offset - std::sort(rsrc_data_.begin(), rsrc_data_.end(), [](const std::pair &a, const std::pair &b) - { - return *a.first < *b.first; - }); - uint32_t last_end = rsrc_data_[0].first[0]; - - // detect non standard resource, maybe produced by some packer - if (last_end < rsrc_virtual_address || last_end > rsrc_virtual_address + rsrc_virtual_size) - { - if (is_verbose) - { - cout << "Non standard resource detected." << endl; - } - if (reloc_raw_size) - { - // move everything before reloc - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, reloc_raw_offset - header_size_aligned); - - // move everything after reloc - memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_raw_offset); - pe_size_leanified += reloc_raw_size; - } - else - { - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, size_ - header_size_aligned - pe_size_leanified); - } - } - else - { - // should do this before memmove - uint32_t old_end = rsrc_data_.back().first[0] + rsrc_data_.back().first[1]; - - if (reloc_raw_size && reloc_raw_offset < rsrc_raw_offset) - { - // move everything before reloc - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, reloc_raw_offset - header_size_aligned - pe_size_leanified); - - // move things between reloc and first data of rsrc - memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, rsrc_raw_offset - reloc_end + rsrc_data_[0].first[0] - rsrc_virtual_address); - pe_size_leanified += reloc_raw_size; - - } - else - { - // move everything before first data of rsrc - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, rsrc_raw_offset - pe_size_leanified - header_size_aligned + rsrc_data_[0].first[0] - rsrc_virtual_address); - } - - depth++; - // res.first is address of IMAGE_RESOURCE_DATA_ENTRY - // res.second is the name of the resource - // p[0] is RVA, p[1] is size - for (auto &res : rsrc_data_) - { - if (depth <= max_depth) - { - // print resource name - PrintFileName(res.second); - } - - res.first = (uint32_t *)((char *)res.first - pe_size_leanified - size_leanified); - auto p = res.first; - - // it seems some of the resource has to be aligned to 4 bytes in order to work - // only align the resource if it is aligned before - if ((p[0] & 3) == 0) - { - // fill the gap with 0 - uint32_t gap = (-(int32_t)last_end) & 3; - memset(fp_ - size_leanified + rsrc_raw_offset + last_end - rsrc_virtual_address - pe_size_leanified, 0, gap); - last_end += gap; - } - - size_t new_size = LeanifyFile(fp_ + rsrc_raw_offset + p[0] - rsrc_virtual_address, p[1], p[0] - last_end + pe_size_leanified + size_leanified, res.second); - p[0] = last_end; - p[1] = new_size; - last_end += new_size; - } - depth--; - rsrc_size_leanified = old_end - last_end; - uint32_t rsrc_new_end = rsrc_raw_offset + last_end - rsrc_virtual_address; - uint32_t rsrc_new_end_aligned = ((rsrc_new_end - 1) | (optional_header->FileAlignment - 1)) + 1; - uint32_t rsrc_correct_end_aligned = ((rsrc_raw_offset + old_end - rsrc_virtual_address - 1) | (optional_header->FileAlignment - 1)) + 1; - uint32_t rsrc_end = rsrc_raw_offset + rsrc_raw_size_; - - // fill the rest of rsrc with 0 - memset(fp_ - size_leanified + rsrc_new_end - pe_size_leanified, 0, rsrc_size_leanified); - - // if rsrc_correct_end_aligned != rsrc_end then there are extra data in rsrc section - if (rsrc_new_end_aligned <= rsrc_end && rsrc_correct_end_aligned == rsrc_end) - { - if (rsrc_new_end_aligned > rsrc_new_end + rsrc_size_leanified) - { - memmove(fp_ - size_leanified + rsrc_new_end + rsrc_size_leanified - pe_size_leanified, fp_ + rsrc_new_end + rsrc_size_leanified, rsrc_new_end_aligned - rsrc_new_end - rsrc_size_leanified); - } - rsrc_virtual_size = rsrc_new_end - rsrc_raw_offset; - rsrc_size_leanified = rsrc_end - rsrc_new_end_aligned; - pe_size_leanified += rsrc_size_leanified; - rsrc_raw_size_ = rsrc_new_end_aligned - rsrc_raw_offset; - } - else - { - if (rsrc_end > rsrc_new_end + rsrc_size_leanified) - { - memmove(fp_ - size_leanified + rsrc_new_end + rsrc_size_leanified - pe_size_leanified, fp_ + rsrc_new_end + rsrc_size_leanified, rsrc_end - rsrc_new_end - rsrc_size_leanified); - } - rsrc_size_leanified = 0; - } - if (reloc_raw_size && reloc_raw_offset > rsrc_raw_offset) - { - memmove(fp_ - size_leanified + rsrc_end - pe_size_leanified, fp_ + rsrc_end, reloc_raw_offset - rsrc_end); - memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_end); - pe_size_leanified += reloc_raw_size; - } - else - { - memmove(fp_ - size_leanified + rsrc_end - pe_size_leanified, fp_ + rsrc_end, size_ - rsrc_end); - } - } + VerbosePrint("Update Section Table."); + for (int i = 0; i < image_file_header->NumberOfSections; i++) { + if (section_table[i].VirtualAddress > reloc_virtual_address) { + section_table[i].VirtualAddress -= reloc_virtual_size; } - else if (reloc_raw_size) // no rsrc but have reloc to remove - { - - // move everything before reloc - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, reloc_raw_offset - header_size_aligned); - - // move everything after reloc - memmove(fp_ - size_leanified + reloc_raw_offset - pe_size_leanified, fp_ + reloc_end, size_ - reloc_raw_offset); - pe_size_leanified += reloc_raw_size; + if (section_table[i].VirtualAddress > rsrc_virtual_address) { + section_table[i].VirtualAddress -= rsrc_decrease_size; + } else if (section_table[i].VirtualAddress == rsrc_virtual_address) { + section_table[i].SizeOfRawData = rsrc_raw_size_; + section_table[i].VirtualSize = rsrc_virtual_size; } - else // no rsrc and no reloc - { - memmove(fp_ - size_leanified + header_size_aligned, fp_ + header_size_aligned + pe_size_leanified, size_ - header_size_aligned - pe_size_leanified); + if (section_table[i].PointerToRawData > reloc_raw_offset) { + section_table[i].PointerToRawData -= reloc_raw_size; } - - - // decrease SizeOfImage - int rsrc_decrease_size = ((data_directories[2].Size - 1) | (optional_header->SectionAlignment - 1)) - ((rsrc_virtual_size - 1) | (optional_header->SectionAlignment - 1)); - if (rsrc_decrease_size < 0) - { - rsrc_decrease_size = 0; + if (section_table[i].PointerToRawData > rsrc_raw_offset) { + section_table[i].PointerToRawData -= rsrc_size_leanified; } - optional_header->SizeOfImage -= reloc_virtual_size + rsrc_decrease_size; + if (section_table[i].PointerToRawData > header_size_aligned) { + section_table[i].PointerToRawData -= header_size_leanified; + } + } + // read NumberOfRvaAndSizes + // this can work on both PE32 and PE32+ + uint32_t num_data_dir = *(uint32_t*)((char*)data_directories - 4); + if (num_data_dir > 16) + num_data_dir = 16; - if (is_verbose) - { - cout << "Update Section Table." << endl; + // update Data Directories too + for (uint32_t i = 0; i < num_data_dir; i++) { + if (data_directories[i].VirtualAddress > reloc_virtual_address) { + data_directories[i].VirtualAddress -= reloc_virtual_size; } - // update Section Table - for (int i = 0; i < image_file_header->NumberOfSections; i++) - { - if (section_table[i].VirtualAddress > reloc_virtual_address) - { - section_table[i].VirtualAddress -= reloc_virtual_size; - } - if (section_table[i].VirtualAddress > rsrc_virtual_address) - { - section_table[i].VirtualAddress -= rsrc_decrease_size; - } - else if (section_table[i].VirtualAddress == rsrc_virtual_address) - { - section_table[i].SizeOfRawData = rsrc_raw_size_; - section_table[i].VirtualSize = rsrc_virtual_size; - } - if (section_table[i].PointerToRawData > reloc_raw_offset) - { - section_table[i].PointerToRawData -= reloc_raw_size; - } - if (section_table[i].PointerToRawData > rsrc_raw_offset) - { - section_table[i].PointerToRawData -= rsrc_size_leanified; - } - if (section_table[i].PointerToRawData > header_size_aligned) - { - section_table[i].PointerToRawData -= header_size_leanified; - } - } - - // read NumberOfRvaAndSizes - // this can work on both PE32 and PE32+ - uint32_t num_data_dir = *(uint32_t *)((char *)data_directories - 4); - if (num_data_dir > 16) - { - num_data_dir = 16; - } - // update Data Directories too - for (uint32_t i = 0; i < num_data_dir; i++) - { - if (data_directories[i].VirtualAddress > reloc_virtual_address) - { - data_directories[i].VirtualAddress -= reloc_virtual_size; - } - if (data_directories[i].VirtualAddress > rsrc_virtual_address) - { - data_directories[i].VirtualAddress -= rsrc_decrease_size; - } + if (data_directories[i].VirtualAddress > rsrc_virtual_address) { + data_directories[i].VirtualAddress -= rsrc_decrease_size; } + } - // the size in Data Directory can not larger than VirtualSize in Section Table - // but for some of the packed file, it will be much smaller - // so only update size in Data Directory if exe is not packed by these kind of packer - if (data_directories[2].Size > rsrc_virtual_size) - { - data_directories[2].Size = rsrc_virtual_size; - } + // the size in Data Directory can not larger than VirtualSize in Section Table + // but for some of the packed file, it will be much smaller + // so only update size in Data Directory if exe is not packed by these kind of packer + if (data_directories[2].Size > rsrc_virtual_size) + data_directories[2].Size = rsrc_virtual_size; - fp_ -= size_leanified; - size_ -= pe_size_leanified; + fp_ -= size_leanified; + size_ -= pe_size_leanified; - return size_; + return size_; } - // decrease RVA inside rsrc section -void Pe::TraverseRSRC(ImageResourceDirectory *res_dir, string name /*= ""*/, const uint32_t move_size /*= 0*/) -{ - ImageResourceDirectoryEntry *entry = (ImageResourceDirectoryEntry *)((char *)res_dir + sizeof(ImageResourceDirectory)); - for (int i = 0; i < res_dir->NumberOfNamedEntries + res_dir->NumberOfIdEntries; i++) - { - string new_name; - if (entry[i].NameIsString) - { - // the name string has a 2 byte size preceding the UNICODE string - uint16_t len = *(uint16_t *)(rsrc_ + entry[i].NameOffset); - char mbs[256] = { 0 }; - UTF16toMBS((wchar_t *)(rsrc_ + entry[i].NameOffset + 2), len * 2, mbs, sizeof(mbs)); - new_name = name + mbs; - } - else if (name.empty() && entry[i].Name < sizeof(resource_types) / sizeof(string) && !resource_types[entry[i].Name].empty()) - { - // use Predefined Resource Types string instead of an ID - new_name = resource_types[entry[i].Name]; - } - else - { - new_name = name + std::to_string(entry[i].Name); - } - - if (entry[i].OffsetToDirectory > rsrc_raw_size_) - { - cerr << "Invalid resource address!" << endl; - return; - } - if (entry[i].DataIsDirectory) - { - TraverseRSRC(reinterpret_cast(rsrc_ + entry[i].OffsetToDirectory), new_name + '/', move_size); - } - else - { - *(uint32_t *)(rsrc_ + entry[i].OffsetToData) -= move_size; - // remember the address to Leanify resource file later - rsrc_data_.emplace_back((uint32_t *)(rsrc_ + entry[i].OffsetToData), new_name); - } +void Pe::TraverseRSRC(ImageResourceDirectory* res_dir, string name /*= ""*/, const uint32_t move_size /*= 0*/) { + ImageResourceDirectoryEntry* entry = (ImageResourceDirectoryEntry*)((char*)res_dir + sizeof(ImageResourceDirectory)); + for (int i = 0; i < res_dir->NumberOfNamedEntries + res_dir->NumberOfIdEntries; i++) { + string new_name; + if (entry[i].NameIsString) { + // the name string has a 2 byte size preceding the UNICODE string + uint16_t len = *(uint16_t*)(rsrc_ + entry[i].NameOffset); + char mbs[256] = { 0 }; + UTF16toMBS((wchar_t*)(rsrc_ + entry[i].NameOffset + 2), len * 2, mbs, sizeof(mbs)); + new_name = name + mbs; + } else if (name.empty() && entry[i].Name < sizeof(resource_types) / sizeof(string) && + !resource_types[entry[i].Name].empty()) { + // use Predefined Resource Types string instead of an ID + new_name = resource_types[entry[i].Name]; + } else { + new_name = name + std::to_string(entry[i].Name); + } + + if (entry[i].OffsetToDirectory > rsrc_raw_size_) { + cerr << "Invalid resource address!" << endl; + return; + } + if (entry[i].DataIsDirectory) { + TraverseRSRC(reinterpret_cast(rsrc_ + entry[i].OffsetToDirectory), new_name + '/', + move_size); + } else { + *(uint32_t*)(rsrc_ + entry[i].OffsetToData) -= move_size; + // remember the address to Leanify resource file later + rsrc_data_.push_back({ reinterpret_cast(rsrc_ + entry[i].OffsetToData), new_name }); } + } } - +bool Pe::IsRSRCValid(uint32_t rsrc_virtual_address, uint32_t rsrc_virtual_size) { + uint32_t prev_end = 0; + for (const RsrcEntry& entry : rsrc_data_) { + uint32_t offset = entry.entry->OffsetToData; + uint32_t size = entry.entry->Size; + + if (offset < rsrc_virtual_address || offset > rsrc_virtual_address + rsrc_virtual_size) + return false; + if (offset - rsrc_virtual_address + size > rsrc_raw_size_) + return false; + if (prev_end > offset) + return false; + prev_end = offset + size; + } + return true; +} diff -Nru leanify-0.4.3/formats/pe.h leanify-0.4.3+git20181014/formats/pe.h --- leanify-0.4.3/formats/pe.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/pe.h 2017-12-03 04:14:03.000000000 +0000 @@ -1,143 +1,140 @@ -#ifndef PE_H -#define PE_H +#ifndef FORMATS_PE_H_ +#define FORMATS_PE_H_ #include #include #include +#include "../utils.h" #include "format.h" - // PE format specification // http://msdn.microsoft.com/en-us/gg463119.aspx extern bool is_verbose; -class Pe : public Format -{ - -public: - - explicit Pe(void *p, size_t s = 0) : Format(p, s), rsrc_(nullptr), rsrc_raw_size_(0) {} - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[2]; - -private: - - // modified from winnt.h - - struct ImageFileHeader - { - uint16_t Machine; - uint16_t NumberOfSections; - uint32_t TimeDateStamp; - uint32_t PointerToSymbolTable; - uint32_t NumberOfSymbols; - uint16_t SizeOfOptionalHeader; - uint16_t Characteristics; - }; - - struct ImageOptionalHeader - { - uint16_t Magic; - uint8_t MajorLinkerVersion; - uint8_t MinorLinkerVersion; - uint32_t SizeOfCode; - uint32_t SizeOfInitializedData; - uint32_t SizeOfUninitializedData; - uint32_t AddressOfEntryPoint; - uint32_t BaseOfCode; - uint32_t BaseOfData; // PE32+ does not have BaseOfData - uint32_t ImageBase; // PE32+ has uint64_t ImageBase - uint32_t SectionAlignment; - uint32_t FileAlignment; - uint16_t MajorOperatingSystemVersion; - uint16_t MinorOperatingSystemVersion; - uint16_t MajorImageVersion; - uint16_t MinorImageVersion; - uint16_t MajorSubsystemVersion; - uint16_t MinorSubsystemVersion; - uint32_t Win32VersionValue; - uint32_t SizeOfImage; - uint32_t SizeOfHeaders; - uint32_t CheckSum; - uint16_t Subsystem; - uint16_t DllCharacteristics; - uint32_t SizeOfStackReserve; // do not use anything below if it is PE32+ - uint32_t SizeOfStackCommit; - uint32_t SizeOfHeapReserve; - uint32_t SizeOfHeapCommit; - uint32_t LoaderFlags; - uint32_t NumberOfRvaAndSizes; - }; - - struct ImageDataDirectory - { - uint32_t VirtualAddress; - uint32_t Size; - }; - - struct ImageSectionHeader - { - uint8_t Name[8]; - union - { - uint32_t PhysicalAddress; - uint32_t VirtualSize; - }; - uint32_t VirtualAddress; - uint32_t SizeOfRawData; - uint32_t PointerToRawData; - uint32_t PointerToRelocations; - uint32_t PointerToLinenumbers; - uint16_t NumberOfRelocations; - uint16_t NumberOfLinenumbers; - uint32_t Characteristics; - }; - - - struct ImageResourceDirectory - { - uint32_t Characteristics; - uint32_t TimeDateStamp; - uint16_t MajorVersion; - uint16_t MinorVersion; - uint16_t NumberOfNamedEntries; - uint16_t NumberOfIdEntries; - }; - - struct ImageResourceDirectoryEntry - { - union - { - struct - { - uint32_t NameOffset : 31; - uint32_t NameIsString : 1; - }; - uint32_t Name; - uint16_t Id; - }; - union - { - uint32_t OffsetToData; - struct - { - uint32_t OffsetToDirectory : 31; - uint32_t DataIsDirectory : 1; - }; - }; - }; - - // decrease RVA inside rsrc section - void TraverseRSRC(ImageResourceDirectory *res_dir, std::string name = "", const uint32_t move_size = 0); +class Pe : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; + + static const uint8_t header_magic[2]; + + private: + // modified from winnt.h + + PACK(struct ImageFileHeader { + uint16_t Machine; + uint16_t NumberOfSections; + uint32_t TimeDateStamp; + uint32_t PointerToSymbolTable; + uint32_t NumberOfSymbols; + uint16_t SizeOfOptionalHeader; + uint16_t Characteristics; + }); + + PACK(struct ImageOptionalHeader { + uint16_t Magic; + uint8_t MajorLinkerVersion; + uint8_t MinorLinkerVersion; + uint32_t SizeOfCode; + uint32_t SizeOfInitializedData; + uint32_t SizeOfUninitializedData; + uint32_t AddressOfEntryPoint; + uint32_t BaseOfCode; + uint32_t BaseOfData; // PE32+ does not have BaseOfData + uint32_t ImageBase; // PE32+ has uint64_t ImageBase + uint32_t SectionAlignment; + uint32_t FileAlignment; + uint16_t MajorOperatingSystemVersion; + uint16_t MinorOperatingSystemVersion; + uint16_t MajorImageVersion; + uint16_t MinorImageVersion; + uint16_t MajorSubsystemVersion; + uint16_t MinorSubsystemVersion; + uint32_t Win32VersionValue; + uint32_t SizeOfImage; + uint32_t SizeOfHeaders; + uint32_t CheckSum; + uint16_t Subsystem; + uint16_t DllCharacteristics; + uint32_t SizeOfStackReserve; // do not use anything below if it is PE32+ + uint32_t SizeOfStackCommit; + uint32_t SizeOfHeapReserve; + uint32_t SizeOfHeapCommit; + uint32_t LoaderFlags; + uint32_t NumberOfRvaAndSizes; + }); + + PACK(struct ImageDataDirectory { + uint32_t VirtualAddress; + uint32_t Size; + }); + + PACK(struct ImageSectionHeader { + uint8_t Name[8]; + union { + uint32_t PhysicalAddress; + uint32_t VirtualSize; + }; + uint32_t VirtualAddress; + uint32_t SizeOfRawData; + uint32_t PointerToRawData; + uint32_t PointerToRelocations; + uint32_t PointerToLinenumbers; + uint16_t NumberOfRelocations; + uint16_t NumberOfLinenumbers; + uint32_t Characteristics; + }); + + PACK(struct ImageResourceDirectory { + uint32_t Characteristics; + uint32_t TimeDateStamp; + uint16_t MajorVersion; + uint16_t MinorVersion; + uint16_t NumberOfNamedEntries; + uint16_t NumberOfIdEntries; + }); + + PACK(struct ImageResourceDirectoryEntry { + union { + struct { + uint32_t NameOffset : 31; + uint32_t NameIsString : 1; + }; + uint32_t Name; + uint16_t Id; + }; + union { + uint32_t OffsetToData; + struct { + uint32_t OffsetToDirectory : 31; + uint32_t DataIsDirectory : 1; + }; + }; + }); + + PACK(struct ImageResourceDataEntry { + uint32_t OffsetToData; + uint32_t Size; + uint32_t CodePage; + uint32_t Reserved; + }); + + struct RsrcEntry { + ImageResourceDataEntry* entry = nullptr; + std::string name; + }; + + // decrease RVA inside rsrc section + void TraverseRSRC(ImageResourceDirectory* res_dir, std::string name = "", const uint32_t move_size = 0); + bool IsRSRCValid(uint32_t rsrc_virtual_address, uint32_t rsrc_virtual_size); - uint8_t *rsrc_; - uint32_t rsrc_raw_size_; + uint8_t* rsrc_ = nullptr; + uint32_t rsrc_raw_size_ = 0; - std::vector> rsrc_data_; + std::vector rsrc_data_; }; -#endif \ No newline at end of file +#endif // FORMATS_PE_H_ diff -Nru leanify-0.4.3/formats/png.cpp leanify-0.4.3+git20181014/formats/png.cpp --- leanify-0.4.3/formats/png.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/png.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -4,164 +4,153 @@ #include #include -#include "../lib/miniz/miniz.h" -#include "../lib/zopflipng/zopflipng_lib.h" +#include +#include #include "../leanify.h" - -#ifdef _MSC_VER -# define bswap32(x) _byteswap_ulong(x) -#elif defined __GNUC__ -# define bswap32(x) __builtin_bswap32(x) -#else -# define bswap32(x) _bswap(x) -#endif +#include "../utils.h" using std::cerr; using std::cout; using std::endl; using std::vector; - const uint8_t Png::header_magic[] = { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A }; +bool Png::keep_icc_profile_ = false; +size_t Png::Leanify(size_t size_leanified /*= 0*/) { + // header + uint8_t* p_read = fp_; + uint8_t* p_write = p_read - size_leanified; + + memmove(p_write, p_read, sizeof(header_magic)); + + p_read += sizeof(header_magic); + p_write += sizeof(header_magic); + + // chunk + uint32_t chunk_type; + + uint8_t* idat_addr = nullptr; + + do { + // read chunk length + // use bswap to convert Big-Endian to Little-Endian + // 12 = length: 4 + type: 4 + crc: 4 + uint32_t chunk_length = BSWAP32(*(uint32_t*)p_read) + 12; + + // detect truncated file + if (p_read + chunk_length > fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + cerr << "PNG file corrupted!" << endl; + fp_ -= size_leanified; + size_ -= p_read - p_write - size_leanified; + return size_; + } + // read chunk type + chunk_type = *(uint32_t*)(p_read + 4); -size_t Png::Leanify(size_t size_leanified /*= 0*/) -{ - // header - uint8_t *p_read = fp_; - uint8_t *p_write = p_read - size_leanified; - - if (size_leanified) - { - memmove(p_write, p_read, sizeof(header_magic)); + if (chunk_type == 0x54414449) { + // save IDAT chunk address + idat_addr = p_write; } - p_read += sizeof(header_magic); - p_write += sizeof(header_magic); - - // chunk - uint32_t chunk_type; + bool should_remove = [&]() { + // Check the case of first letter, keep all critical chunks. + if ((chunk_type & 0x20) == 0) + return false; + + // Remove all ancillary chunks except the following. + switch (chunk_type) { + case 0x4C546361: // acTL APNG + case 0x4C546366: // fcTL APNG + case 0x54416466: // fdAT APNG TODO: use Zopfli to recompress fdAT + case 0x6354706E: // npTc Android 9Patch images (*.9.png) + return false; + + case 0x534E5274: // tRNS transparent + // tRNS must be before IDAT according to PNG spec + return idat_addr != nullptr; + case 0x50434369: // iCCP ICC profile + return !keep_icc_profile_; + default: + return true; + } + }(); + if (should_remove) { + if (is_verbose) { + // chunk name + for (int i = 4; i < 8; i++) + cout << static_cast(p_read[i]); + + cout << " chunk removed, " << chunk_length << " bytes." << endl; + } + // remove this chunk + p_read += chunk_length; + continue; + } - uint8_t *idat_addr = nullptr; + // move this chunk + memmove(p_write, p_read, chunk_length); - do - { - // read chunk length - // use bswap to convert Big-Endian to Little-Endian - // 12 = length: 4 + type: 4 + crc: 4 - uint32_t chunk_length = bswap32(*(uint32_t *)p_read) + 12; - - // detect truncated file - if (p_read + chunk_length > fp_ + size_) - { - memmove(p_write, p_read, fp_ + size_ - p_read); - cerr << "PNG file corrupted!" << endl; - fp_ -= size_leanified; - size_ -= p_read - p_write - size_leanified; - return size_; - } - - // read chunk type - chunk_type = *(uint32_t *)(p_read + 4); - - // judge the case of first letter - // remove all ancillary chunks except tRNS and APNG chunks and npTc - // tRNS has transparency information - if (chunk_type & 0x20) - { - - switch (chunk_type) - { - case 0x534E5274: // tRNS transparent - case 0x4C546361: // acTL APNG - case 0x4C546366: // fcTL APNG - case 0x54416466: // fdAT APNG TODO: use Zopfli to recompress fdAT - case 0x6354706E: // npTc Android 9Patch images (*.9.png) - break; - - default: - if (is_verbose) - { - // chunk name - for (int i = 4; i < 8; i++) - { - cout << static_cast(p_read[i]); - } - cout << " chunk removed, " << chunk_length << " bytes." << endl; - } - // remove this chunk - p_read += chunk_length; - continue; - } - - } - else if (chunk_type == 0x54414449) - { - // save IDAT chunk address - idat_addr = p_write; - } - - // move this chunk - if (p_write != p_read) - { - memmove(p_write, p_read, chunk_length); - } - - // skip whole chunk - p_write += chunk_length; - p_read += chunk_length; + // skip whole chunk + p_write += chunk_length; + p_read += chunk_length; - } - while (chunk_type != 0x444E4549); // IEND + } while (chunk_type != 0x444E4549); // IEND - fp_ -= size_leanified; - size_ = p_write - fp_; + fp_ -= size_leanified; + size_ = p_write - fp_; + size_t resultpng_size = 0; + { ZopfliPNGOptions zopflipng_options; zopflipng_options.use_zopfli = !is_fast; zopflipng_options.lossy_transparent = true; // see the switch above for information about these chunks zopflipng_options.keepchunks = { "acTL", "fcTL", "fdAT", "npTc" }; + if (keep_icc_profile_) + zopflipng_options.keepchunks.push_back("iCCP"); zopflipng_options.num_iterations = iterations; - zopflipng_options.num_iterations_large = iterations / 3 + 1; + zopflipng_options.num_iterations_large = iterations; const vector origpng(fp_, fp_ + size_); vector resultpng; - if (!ZopfliPNGOptimize(origpng, zopflipng_options, is_verbose, &resultpng)) - { - // only use the result PNG if it is smaller - // sometimes the original PNG is already highly optimized - // then maybe ZopfliPNG will produce bigger file - if (resultpng.size() < size_) - { - memcpy(fp_, resultpng.data(), resultpng.size()); - return resultpng.size(); - } + if (!ZopfliPNGOptimize(origpng, zopflipng_options, is_verbose, &resultpng)) { + // only use the result PNG if it is smaller + // sometimes the original PNG is already highly optimized + // then maybe ZopfliPNG will produce bigger file + resultpng_size = resultpng.size(); + if (resultpng.size() < size_) { + size_ = resultpng_size; + memcpy(fp_, resultpng.data(), resultpng_size); + return size_; + } + } else { + cerr << "ZopfliPNG failed!" << endl; } + } - if (idat_addr) - { - // sometimes the strategy chosen by ZopfliPNG is worse than original - // then try to recompress IDAT chunk using only Zopfli - if (is_verbose) - { - cout << "ZopfliPNG failed to reduce size, try Zopfli only." << endl; - } - uint32_t idat_length = bswap32(*(uint32_t *)idat_addr); - uint32_t new_idat_length = ZlibRecompress(idat_addr + 8, idat_length); - if (idat_length != new_idat_length) - { - *(uint32_t *)idat_addr = bswap32(new_idat_length); - *(uint32_t *)(idat_addr + new_idat_length + 8) = bswap32(mz_crc32(0, idat_addr + 4, new_idat_length + 4)); - uint8_t *idat_end = idat_addr + idat_length + 12; - memmove(idat_addr + new_idat_length + 12, idat_end, fp_ + size_ - idat_end); - size_ -= idat_length - new_idat_length; - } + // If the result is exactly the same size as before, chances are it was optimized by ZopfliPNG. + // So there's no need to try Zopfli only, but if it's very small file, that might be a coincidence, + // even if it's not, it won't take much time. + if (idat_addr && (resultpng_size != size_ || size_ < 32768)) { + // sometimes the strategy chosen by ZopfliPNG is worse than original + // then try to recompress IDAT chunk using only Zopfli + VerbosePrint("ZopfliPNG failed to reduce size, try Zopfli only."); + uint32_t idat_length = BSWAP32(*(uint32_t*)idat_addr); + uint32_t new_idat_length = ZlibRecompress(idat_addr + 8, idat_length); + if (idat_length != new_idat_length) { + *(uint32_t*)idat_addr = BSWAP32(new_idat_length); + *(uint32_t*)(idat_addr + new_idat_length + 8) = BSWAP32(lodepng_crc32(idat_addr + 4, new_idat_length + 4)); + uint8_t* idat_end = idat_addr + idat_length + 12; + memmove(idat_addr + new_idat_length + 12, idat_end, fp_ + size_ - idat_end); + size_ -= idat_length - new_idat_length; } + } - return size_; + return size_; } diff -Nru leanify-0.4.3/formats/png.h leanify-0.4.3+git20181014/formats/png.h --- leanify-0.4.3/formats/png.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/png.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,28 +1,20 @@ -#ifndef PNG_H -#define PNG_H - +#ifndef FORMATS_PNG_H_ +#define FORMATS_PNG_H_ #include "format.h" - extern bool is_fast; extern bool is_verbose; extern int iterations; +class Png : public Format { + public: + using Format::Format; -class Png : public Format -{ - -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[8]; + size_t Leanify(size_t size_leanified = 0) override; + static const uint8_t header_magic[8]; + static bool keep_icc_profile_; }; - - - -#endif \ No newline at end of file +#endif // FORMATS_PNG_H_ diff -Nru leanify-0.4.3/formats/rdb.cpp leanify-0.4.3+git20181014/formats/rdb.cpp --- leanify-0.4.3/formats/rdb.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/rdb.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -13,93 +13,80 @@ using std::string; // 531E98204F8542F0 -const uint8_t Rdb::header_magic[] = { 0x35, 0x33, 0x31, 0x45, 0x39, 0x38, 0x32, 0x30, 0x34, 0x46, 0x38, 0x35, 0x34, 0x32, 0x46, 0x30 }; +const uint8_t Rdb::header_magic[] = { 0x35, 0x33, 0x31, 0x45, 0x39, 0x38, 0x32, 0x30, + 0x34, 0x46, 0x38, 0x35, 0x34, 0x32, 0x46, 0x30 }; +size_t Rdb::Leanify(size_t size_leanified /*= 0*/) { + if (size_ <= 0x20) { + cerr << "Not a valid RDB file." << endl; + return Format::Leanify(size_leanified); + } -size_t Rdb::Leanify(size_t size_leanified /*= 0*/) -{ - if (size_ <= 0x20) - { - cerr << "Not a valid RDB file." << endl; - return Format::Leanify(size_leanified); - } + depth++; + uint8_t* p_read; + size_t rdb_size_leanified = 0; - depth++; - uint8_t *p_read; - size_t rdb_size_leanified = 0; + // header + p_read = fp_; + fp_ -= size_leanified; - // header - p_read = fp_; - fp_ -= size_leanified; + // total number of files including directory + uint32_t file_num = *(uint32_t*)(p_read + 0x10); - // total number of files including directory - uint32_t file_num = *(uint32_t *)(p_read + 0x10); + uint64_t index_offset = *(uint64_t*)(p_read + 0x14); - uint64_t index_offset = *(uint64_t *)(p_read + 0x14); + uint64_t content_offset = index_offset + *(uint64_t*)(p_read + 0x1C); - uint64_t content_offset = index_offset + *(uint64_t *)(p_read + 0x1C); + // move header and indexes + memmove(fp_, p_read, (size_t)content_offset); - // move header and indexes - if (size_leanified) - { - memmove(fp_, p_read, (size_t)content_offset); - } + uint8_t* p_index = fp_ + index_offset; + p_read += content_offset; - uint8_t *p_index = fp_ + index_offset; - p_read += content_offset; + for (uint32_t i = 0; i < file_num; i++) { + // index + wchar_t* file_name = (wchar_t*)p_index; + // note that on Linux wchar_t is 4 bytes instead of 2 + // so I can't use wcslen + // p_index += (wcslen(file_name) + 1) * 2; + while (*(uint16_t*)p_index) { + p_index += 2; + } + p_index += 2; - for (uint32_t i = 0; i < file_num; i++) - { - // index - wchar_t *file_name = (wchar_t *)p_index; - - // note that on Linux wchar_t is 4 bytes instead of 2 - // so I can't use wcslen - // p_index += (wcslen(file_name) + 1) * 2; - while (*(uint16_t *)p_index) - { - p_index += 2; - } - p_index += 2; - - uint64_t file_size = *(uint64_t *)(p_index + 8); - - *(uint64_t *)p_index -= rdb_size_leanified; - - // skip directories - if (!file_size) - { - p_index += 16; - continue; - } - - if (depth <= max_depth) - { - // output filename - char mbs[256] = { 0 }; - UTF16toMBS(file_name, p_index - reinterpret_cast(file_name), mbs, sizeof(mbs)); - PrintFileName(mbs); - - // Leanify inner file - size_t new_size = LeanifyFile(p_read, (size_t)file_size, rdb_size_leanified + size_leanified, string(mbs)); - if (new_size != file_size) - { - // update the size in index - *(uint64_t *)(p_index + 8) = new_size; - rdb_size_leanified += (size_t)file_size - new_size; - } - } - else - { - memmove(p_read - rdb_size_leanified - size_leanified, p_read, (size_t)file_size); - } + uint64_t file_size = *(uint64_t*)(p_index + 8); - p_read += file_size; + *(uint64_t*)p_index -= rdb_size_leanified; - p_index += 16; + // skip directories + if (!file_size) { + p_index += 16; + continue; } - size_ = p_read - fp_ - size_leanified - rdb_size_leanified; - depth--; - return size_; + + if (depth <= max_depth) { + // output filename + char mbs[256] = { 0 }; + UTF16toMBS(file_name, p_index - reinterpret_cast(file_name), mbs, sizeof(mbs)); + PrintFileName(mbs); + + // Leanify inner file + size_t new_size = LeanifyFile(p_read, (size_t)file_size, rdb_size_leanified + size_leanified, string(mbs)); + if (new_size != file_size) { + // update the size in index + *(uint64_t*)(p_index + 8) = new_size; + rdb_size_leanified += (size_t)file_size - new_size; + } + } else { + memmove(p_read - rdb_size_leanified - size_leanified, p_read, (size_t)file_size); + } + + p_read += file_size; + + p_index += 16; + } + size_ = p_read - fp_ - size_leanified - rdb_size_leanified; + depth--; + return size_; } \ No newline at end of file diff -Nru leanify-0.4.3/formats/rdb.h leanify-0.4.3+git20181014/formats/rdb.h --- leanify-0.4.3/formats/rdb.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/rdb.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,19 +1,15 @@ -#ifndef RDB_H -#define RDB_H - +#ifndef FORMATS_RDB_H_ +#define FORMATS_RDB_H_ #include "format.h" +class Rdb : public Format { + public: + using Format::Format; -class Rdb : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; + size_t Leanify(size_t size_leanified = 0) override; - static const uint8_t header_magic[16]; + static const uint8_t header_magic[16]; }; - -#endif \ No newline at end of file +#endif // FORMATS_RDB_H_ diff -Nru leanify-0.4.3/formats/swf.cpp leanify-0.4.3+git20181014/formats/swf.cpp --- leanify-0.4.3/formats/swf.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/swf.cpp 2017-12-03 04:14:03.000000000 +0000 @@ -1,265 +1,251 @@ #include "swf.h" +#include #include #include #include +#include -#include "../lib/LZMA/LzmaLib.h" -#include "../lib/miniz/miniz.h" +#include +#include +#include +#include #include "../leanify.h" +#include "../utils.h" using std::cerr; -using std::cout; using std::endl; +using std::vector; -const uint8_t Swf::header_magic[] = { 'F', 'W', 'S' }; +const uint8_t Swf::header_magic[] = { 'F', 'W', 'S' }; const uint8_t Swf::header_magic_deflate[] = { 'C', 'W', 'S' }; -const uint8_t Swf::header_magic_lzma[] = { 'Z', 'W', 'S' }; +const uint8_t Swf::header_magic_lzma[] = { 'Z', 'W', 'S' }; +namespace { -size_t Swf::Leanify(size_t size_leanified /*= 0*/) -{ - if (is_fast && *fp_ != 'F') - { - return Format::Leanify(size_leanified); - } +void UpdateTagLength(uint8_t* tag_content, size_t header_length, size_t new_length) { + if (header_length == 6) + *(uint32_t*)(tag_content - 4) = new_length; + else + *(tag_content - 2) += (new_length & 0x3F) - (*(tag_content - 2) & 0x3F); +} - uint8_t *in_buffer = fp_ + 8; - uint32_t in_len = *(uint32_t *)(fp_ + 4) - 8; +size_t GetRECTSize(uint8_t* rect) { + // The first 5 bits. + uint8_t nbits = *rect >> 3; + // Xmin, Xmax, Ymin, Ymax takes nbits each, round the sum to bytes. + return (5 + nbits * 4 + 7) / 8; +} - // if SWF is compressed, decompress it first - if (*fp_ == 'C') - { - // deflate - if (is_verbose) - { - cout << "SWF is compressed with deflate." << endl; - } - size_t s = 0; - in_buffer = static_cast(tinfl_decompress_mem_to_heap(in_buffer, size_ - 8, &s, TINFL_FLAG_PARSE_ZLIB_HEADER)); - if (!in_buffer || s != in_len) - { - cerr << "SWF file corrupted!" << endl; - mz_free(in_buffer); - return Format::Leanify(size_leanified); - } - } - else if (*fp_ == 'Z') - { - // LZMA - if (is_verbose) - { - cout << "SWF is compressed with LZMA." << endl; - } - // | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes | - // | 'ZWS' + version | scriptLen | compressedLen | LZMA props | LZMA data | LZMA end marker | - uint8_t *dst_buffer = new uint8_t[in_len]; - size_t s = in_len, len = size_ - 12 - LZMA_PROPS_SIZE; - // check compressed length - if (*(uint32_t *)in_buffer != len || - LzmaUncompress(dst_buffer, &s, in_buffer + 4 + LZMA_PROPS_SIZE, &len, in_buffer + 4, LZMA_PROPS_SIZE) || - s != in_len) - { - cerr << "SWF file corrupted!" << endl; - delete[] dst_buffer; - return Format::Leanify(size_leanified); - } - in_buffer = dst_buffer; - } - else if (is_verbose) - { - cout << "SWF is not compressed." << endl; - } +bool LZMACompress(const uint8_t* src, size_t src_len, vector* out) { + // Reserve enough space. + out->resize(src_len + src_len / 8); + + CLzmaEncProps props; + LzmaEncProps_Init(&props); + props.level = 9; + // Word size (the number of fast bytes) + props.fb = 256; + // MatchFinderCycles + props.mc = 1 << 30; + // We already know uncompressed data size, use it to reduce dictionary size. + props.reduceSize = src_len; + // SWF need end mark + props.writeEndMark = 1; + + size_t out_size = out->size() - LZMA_PROPS_SIZE, props_size = LZMA_PROPS_SIZE; + if (LzmaEncode(out->data() + LZMA_PROPS_SIZE, &out_size, src, src_len, &props, out->data(), &props_size, + props.writeEndMark, nullptr, &g_Alloc, &g_Alloc)) + return false; - // parsing SWF tags - uint8_t *p = in_buffer + 13; // skip FrameSize(9B) + FrameRate(2B) + FrameCount(2B) = 13B - size_t tag_size_leanified = 0; - do - { - uint16_t tag_type = *(uint16_t *)p >> 6; - uint32_t tag_length = *p & 0x3F; - size_t tag_header_length = 2; - if (tag_length == 0x3F) - { - tag_length = *(uint32_t *)(p + 2); - tag_header_length += 4; - } - - if (tag_size_leanified) - { - memmove(p - tag_size_leanified, p, tag_header_length); - } - - p += tag_header_length; - - switch (tag_type) - { - case 20: // DefineBitsLossless - case 36: // DefineBitsLossless2 - { - size_t header_size = 7 + (p[3] == 3); - if (is_verbose) - { - cout << "DefineBitsLossless tag found." << endl; - } - if (tag_size_leanified) - { - memmove(p - tag_size_leanified, p, header_size); - } - // recompress Zlib bitmap data - size_t new_data_size = ZlibRecompress(p + header_size, tag_length - header_size, tag_size_leanified); - - UpdateTagLength(p - tag_size_leanified, tag_header_length, header_size + new_data_size); - tag_size_leanified += tag_length - header_size - new_data_size; - break; - } - case 21: // DefineBitsJPEG2 - { - if (is_verbose) - { - cout << "DefineBitsJPEG2 tag found." << endl; - } - // copy id - *(uint16_t *)(p - tag_size_leanified) = *(uint16_t *)p; - - // Leanify embedded image - size_t new_size = LeanifyFile(p + 2, tag_length - 2, tag_size_leanified); - - UpdateTagLength(p - tag_size_leanified, tag_header_length, 2 + new_size); - tag_size_leanified += tag_length - 2 - new_size; - break; - } - case 35: // DefineBitsJPEG3 - case 90: // DefineBitsJPEG4 - { - // copy id - *(uint16_t *)(p - tag_size_leanified) = *(uint16_t *)p; - - uint32_t img_size = *(uint32_t *)(p + 2); - size_t header_size = tag_type == 90 ? 8 : 6; - - if (is_verbose) - { - cout << "DefineBitsJPEG" << header_size / 2 << " tag found." << endl; - } - // Leanify embedded image - size_t new_img_size = LeanifyFile(p + header_size, img_size, tag_size_leanified); - *(uint32_t *)(p + 2 - tag_size_leanified) = new_img_size; - - // recompress alpha data - size_t new_alpha_data_size = ZlibRecompress(p + header_size + img_size, tag_length - img_size - header_size, tag_size_leanified + img_size - new_img_size); - - size_t new_tag_size = new_img_size + new_alpha_data_size + header_size; - UpdateTagLength(p - tag_size_leanified, tag_header_length, new_tag_size); - tag_size_leanified += tag_length - new_tag_size; - break; - } - case 77: // Metadata - if (is_verbose) - { - cout << "Metadata removed." << endl; - } - tag_size_leanified += tag_length + tag_header_length; - break; - case 69: // FileAttributes - *p &= ~(1 << 4); // set HasMetadata bit to 0 - default: - if (tag_size_leanified) - { - memmove(p - tag_size_leanified, p, tag_length); - } - } - p += tag_length; - } - while (p < in_buffer + in_len); + out->resize(props_size + out_size); + return true; +} - in_len -= tag_size_leanified; +bool LZMADecompress(const uint8_t* src, size_t src_len, uint8_t* dst, size_t dst_len) { + size_t decompressed_size = dst_len, lzma_size = src_len - LZMA_PROPS_SIZE; + ELzmaStatus status; + if (LzmaDecode(dst, &decompressed_size, src + LZMA_PROPS_SIZE, &lzma_size, src, LZMA_PROPS_SIZE, LZMA_FINISH_END, + &status, &g_Alloc)) + return false; - if (is_fast) - { - // write header - fp_ -= size_leanified; - - // decompressed size (including header) - *(uint32_t *)(fp_ + 4) = size_ = in_len + 8; - - if (size_leanified) - { - memmove(fp_, fp_ + size_leanified, 4); - memmove(fp_ + 8, fp_ + 8 + size_leanified, in_len); - } - return size_; - } + return decompressed_size == dst_len; +} - // compress with LZMA - size_t s = in_len + in_len / 4, props = LZMA_PROPS_SIZE; - uint8_t *dst = new uint8_t[s + LZMA_PROPS_SIZE]; - // have to set writeEndMark to true - if (LzmaCompress(dst + LZMA_PROPS_SIZE, &s, in_buffer, in_len, dst, &props, iterations < 9 ? iterations : 9, 1 << 24, -1, -1, -1, 128, -1)) - { - cerr << "LZMA compression failed." << endl; - s = size_; - } +} // namespace - // free decompressed data - if (*fp_ == 'C') - { - mz_free(in_buffer); - } - else if (*fp_ == 'Z') - { - delete[] in_buffer; +size_t Swf::Leanify(size_t size_leanified /*= 0*/) { + if (is_fast && *fp_ != 'F') + return Format::Leanify(size_leanified); + + uint8_t* in_buffer = fp_ + 8; + uint32_t in_len = *(uint32_t*)(fp_ + 4) - 8; + + // if SWF is compressed, decompress it first + if (*fp_ == 'C') { + // deflate + VerbosePrint("SWF is compressed with deflate."); + size_t uncompressed_size = 0; + uint8_t* buffer = nullptr; + if (lodepng_zlib_decompress(&buffer, &uncompressed_size, in_buffer, size_ - 8, + &lodepng_default_decompress_settings) || + !buffer || uncompressed_size != in_len) { + cerr << "SWF file corrupted!" << endl; + free(buffer); + return Format::Leanify(size_leanified); + } + in_buffer = buffer; + } else if (*fp_ == 'Z') { + // LZMA + VerbosePrint("SWF is compressed with LZMA."); + // | 4 bytes | 4 bytes | 4 bytes | 5 bytes | n bytes | 6 bytes | + // | 'ZWS' + version | scriptLen | compressedLen | LZMA props | LZMA data | LZMA end marker | + uint8_t* dst_buffer = new uint8_t[in_len]; + // check compressed length + if (*(uint32_t*)in_buffer != size_ - 12 - LZMA_PROPS_SIZE || + !LZMADecompress(in_buffer + 4, size_ - 12, dst_buffer, in_len)) { + cerr << "SWF file corrupted!" << endl; + delete[] dst_buffer; + return Format::Leanify(size_leanified); + } + in_buffer = dst_buffer; + } else { + VerbosePrint("SWF is not compressed."); + } + + // parsing SWF tags + uint8_t* p = in_buffer + GetRECTSize(in_buffer); // skip FrameSize which is a RECT + p += 4; // skip FrameRate(2 Byte) + FrameCount(2 Byte) = 4 Byte + size_t tag_size_leanified = 0; + do { + uint16_t tag_type = *(uint16_t*)p >> 6; + uint32_t tag_length = *p & 0x3F; + size_t tag_header_length = 2; + if (tag_length == 0x3F) { + tag_length = *(uint32_t*)(p + 2); + tag_header_length += 4; + } + + memmove(p - tag_size_leanified, p, tag_header_length); + p += tag_header_length; + + switch (tag_type) { + // DefineBitsLossless + case 20: + // DefineBitsLossless2 + case 36: { + size_t header_size = 7 + (p[3] == 3); + VerbosePrint("DefineBitsLossless tag found."); + memmove(p - tag_size_leanified, p, header_size); + + // recompress Zlib bitmap data + size_t new_data_size = ZlibRecompress(p + header_size, tag_length - header_size, tag_size_leanified); + + UpdateTagLength(p - tag_size_leanified, tag_header_length, header_size + new_data_size); + tag_size_leanified += tag_length - header_size - new_data_size; + break; + } + // DefineBitsJPEG2 + case 21: { + VerbosePrint("DefineBitsJPEG2 tag found."); + // copy id + *(uint16_t*)(p - tag_size_leanified) = *(uint16_t*)p; + + // Leanify embedded image + size_t new_size = LeanifyFile(p + 2, tag_length - 2, tag_size_leanified); + + UpdateTagLength(p - tag_size_leanified, tag_header_length, 2 + new_size); + tag_size_leanified += tag_length - 2 - new_size; + break; + } + // DefineBitsJPEG3 + case 35: + // DefineBitsJPEG4 + case 90: { + // copy id + *(uint16_t*)(p - tag_size_leanified) = *(uint16_t*)p; + + uint32_t img_size = *(uint32_t*)(p + 2); + size_t header_size = tag_type == 90 ? 8 : 6; + + VerbosePrint("DefineBitsJPEG", header_size / 2, " tag found."); + // Leanify embedded image + size_t new_img_size = LeanifyFile(p + header_size, img_size, tag_size_leanified); + *(uint32_t*)(p + 2 - tag_size_leanified) = new_img_size; + + // recompress alpha data + size_t new_alpha_data_size = ZlibRecompress(p + header_size + img_size, tag_length - img_size - header_size, + tag_size_leanified + img_size - new_img_size); + + size_t new_tag_size = new_img_size + new_alpha_data_size + header_size; + UpdateTagLength(p - tag_size_leanified, tag_header_length, new_tag_size); + tag_size_leanified += tag_length - new_tag_size; + break; + } + // Metadata + case 77: + VerbosePrint("Metadata removed."); + tag_size_leanified += tag_length + tag_header_length; + break; + // FileAttributes + case 69: + *p &= ~(1 << 4); // set HasMetadata bit to 0 + // Fall through. + default: + memmove(p - tag_size_leanified, p, tag_length); } + p += tag_length; + } while (p < in_buffer + in_len); + in_len -= tag_size_leanified; + + if (is_fast) { + // write header fp_ -= size_leanified; + memmove(fp_, fp_ + size_leanified, 4); - s += LZMA_PROPS_SIZE; - if (s + 12 < size_) - { - size_ = s + 12; - - // write header - memcpy(fp_, header_magic_lzma, sizeof(header_magic_lzma)); - - // write SWF version, at least 13 to support LZMA - if (fp_[3 + size_leanified] < 13) - { - fp_[3] = 13; - } - else - { - fp_[3] = fp_[3 + size_leanified]; - } + // decompressed size (including header) + *(uint32_t*)(fp_ + 4) = size_ = in_len + 8; - // decompressed size (including header) - *(uint32_t *)(fp_ + 4) = in_len + 8; + memmove(fp_ + 8, fp_ + 8 + size_leanified, in_len); + return size_; + } - // compressed size: LZMA data + end mark - *(uint32_t *)(fp_ + 8) = s - LZMA_PROPS_SIZE; + // compress with LZMA + vector lzma_data; + if (!LZMACompress(in_buffer, in_len, &lzma_data)) { + cerr << "LZMA compression failed." << endl; + lzma_data.clear(); + } - memcpy(fp_ + 12, dst, s); - } - else if (size_leanified) - { - memmove(fp_, fp_ + size_leanified, size_); - } + // free decompressed data + if (*fp_ == 'C') + free(in_buffer); + else if (*fp_ == 'Z') + delete[] in_buffer; - delete[] dst; + fp_ -= size_leanified; - return size_; -} + if (!lzma_data.empty() && lzma_data.size() + 12 < size_) { + size_ = lzma_data.size() + 12; + // write header + memcpy(fp_, header_magic_lzma, sizeof(header_magic_lzma)); + // write SWF version, at least 13 to support LZMA + fp_[3] = std::max(static_cast(13), fp_[3 + size_leanified]); -void Swf::UpdateTagLength(uint8_t *tag_content, size_t header_length, size_t new_length) -{ - if (header_length == 6) - { - *(uint32_t *)(tag_content - 4) = new_length; - } - else - { - *(tag_content - 2) += (new_length & 0x3F) - (*(tag_content - 2) & 0x3F); - } + // decompressed size (including header) + *(uint32_t*)(fp_ + 4) = in_len + 8; + + // compressed size: LZMA data + end mark + *(uint32_t*)(fp_ + 8) = lzma_data.size() - LZMA_PROPS_SIZE; + + memcpy(fp_ + 12, lzma_data.data(), lzma_data.size()); + } else { + memmove(fp_, fp_ + size_leanified, size_); + } + + return size_; } diff -Nru leanify-0.4.3/formats/swf.h leanify-0.4.3+git20181014/formats/swf.h --- leanify-0.4.3/formats/swf.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/swf.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,27 +1,19 @@ -#ifndef SWF_H -#define SWF_H +#ifndef FORMATS_SWF_H_ +#define FORMATS_SWF_H_ #include "format.h" -extern bool is_verbose; extern bool is_fast; -extern int iterations; -class Swf : public Format -{ -public: - using Format::Format; - - size_t Leanify(size_t size_leanified = 0) override; - - static const uint8_t header_magic[3]; - static const uint8_t header_magic_deflate[3]; - static const uint8_t header_magic_lzma[3]; - -private: - - void UpdateTagLength(uint8_t *tag_content, size_t header_length, size_t new_length); +class Swf : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; + + static const uint8_t header_magic[3]; + static const uint8_t header_magic_deflate[3]; + static const uint8_t header_magic_lzma[3]; }; - -#endif \ No newline at end of file +#endif // FORMATS_SWF_H_ diff -Nru leanify-0.4.3/formats/tar.cpp leanify-0.4.3+git20181014/formats/tar.cpp --- leanify-0.4.3/formats/tar.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/tar.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -12,118 +13,102 @@ using std::endl; using std::string; -size_t Tar::Leanify(size_t size_leanified /*= 0*/) -{ - if (!is_valid_) - { - return Format::Leanify(size_leanified); - } +namespace { - uint8_t *p_read = fp_; - fp_ -= size_leanified; - uint8_t *p_write = fp_; - depth++; - - do - { - int checksum = CalcChecksum(p_read); - // 256 means the record is all 0 - if (checksum == 256) - { - break; - } - if (size_leanified) - { - memmove(p_write, p_read, 512); - } - p_read += 512; - if (checksum != strtol(reinterpret_cast(p_write) + 148, nullptr, 8)) - { - cerr << "Checksum does not match!" << endl; - p_write += 512; - continue; - } - char type = *(p_write + 156); - size_t original_size = strtol(reinterpret_cast(p_write) + 124, nullptr, 8); - // align to 512 - size_t size_aligned = (original_size + 0x1FF) & ~0x1FF; - if (original_size) - { - if ((type == 0 || type == '0') && depth <= max_depth) - { - // normal file - char *filename = reinterpret_cast(p_write); - PrintFileName(filename); - - size_t new_size = LeanifyFile(p_read, original_size, size_leanified, string(filename)); - if (new_size < original_size) - { - // write new size - sprintf(reinterpret_cast(p_write) + 124, "%011o", (unsigned int)new_size); - - // update checksum - sprintf(reinterpret_cast(p_write) + 148, "%06o", CalcChecksum(p_write)); - p_write[155] = ' '; - - // align to 512 - size_t new_size_aligned = (new_size + 0x1FF) & ~0x1FF; - - // make sure the rest space is all 0 - memset(p_write + new_size + 512, 0, new_size_aligned - new_size); - - p_write += new_size_aligned; - if (new_size_aligned != size_aligned) - { - size_leanified += size_aligned - new_size_aligned; - } - } - else - { - // update checksum - sprintf(reinterpret_cast(p_write) + 148, "%06o", CalcChecksum(p_write)); - p_write[155] = ' '; - - // make sure the rest space is all 0 - memset(p_write + new_size + 512, 0, size_aligned - new_size); - p_write += size_aligned; - } - } - else - { - // other type, just move it - memmove(p_write + 512, p_read, size_aligned); - p_write += size_aligned; - } - p_read += size_aligned; - } - p_write += 512; - - } - while (p_write < fp_ + size_); +int CalcChecksum(uint8_t* header) { + // The checksum bytes are treated as spaces + // ' ' = 32, 32x8 = 256 + int sum = 256; + for (int i = 0; i < 148; i++) + sum += header[i]; - depth--; + for (int i = 156; i < 512; i++) + sum += header[i]; - // write 2 more zero-filled records - memset(p_write, 0, 1024); - size_ = p_write + 1024 - fp_; - return size_; + return sum; } +} // namespace -int Tar::CalcChecksum(uint8_t *header) const -{ - // checksum bytes are taken to be spaces - // ' ' = 32, 32x8 = 256 - int s = 256; - for (int i = 0; i < 148; i++) - { - s += header[i]; +Tar::Tar(void* p, size_t s /*= 0*/) : Format(p, s) { + // check file size first + is_valid_ = s > 512 && s % 512 == 0 && CalcChecksum(fp_) == strtol(static_cast(p) + 148, nullptr, 8); +} + +size_t Tar::Leanify(size_t size_leanified /*= 0*/) { + if (!is_valid_) + return Format::Leanify(size_leanified); + + uint8_t* p_read = fp_; + fp_ -= size_leanified; + uint8_t* p_write = fp_; + depth++; + + do { + int checksum = CalcChecksum(p_read); + // 256 means the record is all 0 + if (checksum == 256) + break; + + memmove(p_write, p_read, 512); + + p_read += 512; + if (checksum != strtol(reinterpret_cast(p_write) + 148, nullptr, 8)) { + cerr << "Checksum does not match!" << endl; + p_write += 512; + continue; } - for (int i = 156; i < 512; i++) - { - s += header[i]; + char type = *(p_write + 156); + size_t original_size = strtol(reinterpret_cast(p_write) + 124, nullptr, 8); + // align to 512 + size_t size_aligned = (original_size + 0x1FF) & ~0x1FF; + if (original_size) { + if ((type == 0 || type == '0') && depth <= max_depth) { + // normal file + char* filename = reinterpret_cast(p_write); + PrintFileName(filename); + + size_t new_size = LeanifyFile(p_read, original_size, size_leanified, string(filename)); + if (new_size < original_size) { + // write new size + sprintf(reinterpret_cast(p_write) + 124, "%011o", (unsigned int)new_size); + + // update checksum + sprintf(reinterpret_cast(p_write) + 148, "%06o", CalcChecksum(p_write)); + p_write[155] = ' '; + + // align to 512 + size_t new_size_aligned = (new_size + 0x1FF) & ~0x1FF; + + // make sure the rest space is all 0 + memset(p_write + new_size + 512, 0, new_size_aligned - new_size); + + p_write += new_size_aligned; + size_leanified += size_aligned - new_size_aligned; + } else { + // update checksum + sprintf(reinterpret_cast(p_write) + 148, "%06o", CalcChecksum(p_write)); + p_write[155] = ' '; + + // make sure the rest space is all 0 + memset(p_write + new_size + 512, 0, size_aligned - new_size); + p_write += size_aligned; + } + } else { + // other type, just move it + memmove(p_write + 512, p_read, size_aligned); + p_write += size_aligned; + } + p_read += size_aligned; } - return s; -} + p_write += 512; + + } while (p_write < fp_ + size_); + depth--; + // write 2 more zero-filled records + memset(p_write, 0, 1024); + size_ = p_write + 1024 - fp_; + return size_; +} diff -Nru leanify-0.4.3/formats/tar.h leanify-0.4.3+git20181014/formats/tar.h --- leanify-0.4.3/formats/tar.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/tar.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,37 +1,22 @@ -#ifndef TAR_H -#define TAR_H - - -#include +#ifndef FORMATS_TAR_H_ +#define FORMATS_TAR_H_ #include "format.h" - extern bool is_verbose; -class Tar : public Format -{ -public: - explicit Tar(void *p, size_t s = 0) : Format(p, s) - { - // check file size first - is_valid_ = s > 512 && s % 512 == 0 && - CalcChecksum(fp_) == strtol(static_cast(p) + 148, nullptr, 8); - } - - size_t Leanify(size_t size_leanified = 0) override; - - bool IsValid() const - { - return is_valid_; - } - -private: - int CalcChecksum(uint8_t *header) const; - - bool is_valid_; +class Tar : public Format { + public: + explicit Tar(void* p, size_t s = 0); + + size_t Leanify(size_t size_leanified = 0) override; + + bool IsValid() const { + return is_valid_; + } + private: + bool is_valid_; }; - -#endif \ No newline at end of file +#endif // FORMATS_TAR_H_ diff -Nru leanify-0.4.3/formats/vcf.cpp leanify-0.4.3+git20181014/formats/vcf.cpp --- leanify-0.4.3/formats/vcf.cpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/vcf.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,78 @@ +#include "vcf.h" + +#include +#include +#include +#include + +#include "base64.h" + +using std::cout; +using std::endl; +using std::string; +using std::vector; + +size_t Vcf::Leanify(size_t size_leanified /*= 0*/) { + uint8_t *p_read = fp_, *p_write = fp_ - size_leanified; + + while (p_read < fp_ + size_) { + const string magic = "\nPHOTO;"; + uint8_t* photo_magic = std::search(p_read, fp_ + size_, magic.begin(), magic.end()) + 1; + + if (photo_magic >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + + uint8_t* line_end = std::find(photo_magic + magic.size(), fp_ + size_, '\n'); + const vector base64_sign = { "ENCODING=BASE64", "ENCODING=b" }; + + // Check if current line contains any of the |base64_sign|. + if (!std::any_of(base64_sign.begin(), base64_sign.end(), [&](const string& s) { + return std::search(photo_magic + magic.size() - 1, line_end, s.begin(), s.end()) < line_end; + })) { + // Not base64 encoded, skipping + memmove(p_write, p_read, line_end - p_read); + p_write += line_end - p_read; + p_read = line_end; + continue; + } + + uint8_t* start = std::find(photo_magic + magic.size(), line_end, ':') + 1; + + memmove(p_write, p_read, start - p_read); + p_write += start - p_read; + p_read = start; + + if (p_read > line_end) + continue; + + // Find the first line that doesn't start with a space or tab. + // https://tools.ietf.org/html/rfc6350#section-3.2 + uint8_t* end = std::find(start, fp_ + size_, '\n'); + while (end + 1 < fp_ + size_ && (end[1] == ' ' || end[1] == '\t')) { + end = std::find(end + 1, fp_ + size_, '\n'); + } + if (end >= fp_ + size_) { + memmove(p_write, p_read, fp_ + size_ - p_read); + p_write += fp_ + size_ - p_read; + break; + } + + // Don't remove this \r + if (*(end - 1) == '\r') + end--; + + if (is_verbose) { + cout << string(reinterpret_cast(photo_magic), start + 12 - photo_magic) << "... found at offset 0x" + << std::hex << photo_magic - fp_ << std::dec << endl; + } + size_t new_size = Base64(p_read, end - p_read).Leanify(p_read - p_write); + p_write += new_size; + p_read = end; + } + fp_ -= size_leanified; + size_ = p_write - fp_; + return size_; +} diff -Nru leanify-0.4.3/formats/vcf.h leanify-0.4.3+git20181014/formats/vcf.h --- leanify-0.4.3/formats/vcf.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/vcf.h 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,15 @@ +#ifndef FORMATS_VCF_H_ +#define FORMATS_VCF_H_ + +#include "format.h" + +extern bool is_verbose; + +class Vcf : public Format { + public: + using Format::Format; + + size_t Leanify(size_t size_leanified = 0) override; +}; + +#endif // FORMATS_VCF_H_ diff -Nru leanify-0.4.3/formats/xml.cpp leanify-0.4.3+git20181014/formats/xml.cpp --- leanify-0.4.3/formats/xml.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/xml.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,225 +1,305 @@ #include "xml.h" #include -#include +#include +#include #include +#include #include "../leanify.h" #include "../utils.h" #include "base64.h" +#include "data_uri.h" -using std::cout; -using std::endl; +using std::map; using std::string; +Xml::Xml(void* p, size_t s /*= 0*/) : Format(p, s) { + pugi::xml_parse_result result = doc_.load_buffer( + fp_, size_, pugi::parse_default | pugi::parse_declaration | pugi::parse_doctype | pugi::parse_ws_pcdata_single); + is_valid_ = result; + encoding_ = result.encoding; +} -Xml::Xml(void *p, size_t s /*= 0*/) : Format(p, s), doc_(true) -{ - uint8_t *q = static_cast(p); - - const uint8_t utf8_bom[] = { 0xEF, 0xBB, 0xBF }; - - // tinyxml2 does not support utf16 - /* - const uint8_t utf16be_bom[] = { 0xFE, 0xFF }; - const uint8_t utf16le_bom[] = { 0xFF, 0xFE }; - */ - // skip utf8 bom - if (memcmp(q, utf8_bom, sizeof(utf8_bom)) == 0) - { - q += sizeof(utf8_bom); - } - /* else if (!memcmp(q, utf16le_bom, sizeof(utf16le_bom)) || !memcmp(q, utf16be_bom, sizeof(utf16be_bom))) - { - q += sizeof(utf16le_bom); - } - */ - // skip spaces - while (isspace(*q) && q < static_cast(p) + s) - { - q++; - } - // only parse the file if it starts with '<' - if (*q == '<') - { - is_valid_ = (doc_.Parse(reinterpret_cast(fp_), size_) == 0); - } - else - { - is_valid_ = false; +namespace { +// https://github.com/svg/svgo/blob/master/plugins/_collections.js +const map kCommonDefaultAttributes = { { "x", "0" }, + { "y", "0" }, + { "clip", "auto" }, + { "clip-path", "none" }, + { "clip-rule", "nonzero" }, + { "mask", "none" }, + { "opacity", "1" }, + { "solid-color", "#000" }, + { "solid-opacity", "1" }, + { "stop-color", "#000" }, + { "stop-opacity", "1" }, + { "fill-opacity", "1" }, + { "fill-rule", "nonzero" }, + { "fill", "#000" }, + { "stroke", "none" }, + { "stroke-width", "1" }, + { "stroke-linecap", "butt" }, + { "stroke-linejoin", "miter" }, + { "stroke-miterlimit", "4" }, + { "stroke-dasharray", "none" }, + { "stroke-dashoffset", "0" }, + { "stroke-opacity", "1" }, + { "paint-order", "normal" }, + { "vector-effect", "none" }, + { "viewport-fill", "none" }, + { "viewport-fill-opacity", "1" }, + { "display", "inline" }, + { "visibility", "visible" }, + { "marker-start", "none" }, + { "marker-mid", "none" }, + { "marker-end", "none" }, + { "color-interpolation", "sRGB" }, + { "color-interpolation-filters", "linearRGB" }, + { "color-rendering", "auto" }, + { "shape-rendering", "auto" }, + { "text-rendering", "auto" }, + { "image-rendering", "auto" }, + { "buffered-rendering", "auto" }, + { "font-style", "normal" }, + { "font-variant", "normal" }, + { "font-weight", "normal" }, + { "font-stretch", "normal" }, + { "font-size", "medium" }, + { "font-size-adjust", "none" }, + { "kerning", "auto" }, + { "letter-spacing", "normal" }, + { "word-spacing", "normal" }, + { "text-decoration", "none" }, + { "text-anchor", "start" }, + { "text-overflow", "clip" }, + { "writing-mode", "lr-tb" }, + { "glyph-orientation-vertical", "auto" }, + { "glyph-orientation-horizontal", "0deg" }, + { "direction", "ltr" }, + { "unicode-bidi", "normal" }, + { "dominant-baseline", "auto" }, + { "alignment-baseline", "baseline" }, + { "baseline-shift", "baseline" }, + { "slope", "1" }, + { "intercept", "0" }, + { "amplitude", "1" }, + { "exponent", "1" }, + { "offset", "0" }, + { "preserveAspectRatio", "xMidYMid" } }; + +const map> kSingleDefaultAttributes = { + { "a", { { "target", "_self" } } }, + { "svg", + { { "width", "100%" }, + { "height", "100%" }, + { "zoomAndPan", "magnify" }, + { "baseProfile", "none" }, + { "contentScriptType", "application/ecmascript" }, + { "contentStyleType", "text/css" } } }, + { "filter", + { { "primitiveUnits", "userSpaceOnUse" }, + { "x", "-10%" }, + { "y", "-10%" }, + { "width", "120%" }, + { "height", "120%" } } }, + { "mask", + { { "maskUnits", "objectBoundingBox" }, + { "maskContentUnits", "userSpaceOnUse" }, + { "x", "-10%" }, + { "y", "-10%" }, + { "width", "120%" }, + { "height", "120%" } } } +}; + +void TraverseElements(pugi::xml_node node, std::function callback) { + // cannot use ranged loop here because we need to store the next_sibling before recursion so that if child was removed + // the loop won't be terminated + for (pugi::xml_node child = node.first_child(), next; child; child = next) { + next = child.next_sibling(); + TraverseElements(child, callback); + } + + callback(node); +} + +// Remove single PCData only contains whitespace if xml:space="preserve" is not set. +void RemovePCDataSingle(pugi::xml_node node, bool xml_space_preserve) { + xml_space_preserve |= strcmp(node.attribute("xml:space").value(), "preserve") == 0; + if (xml_space_preserve) + return; + + pugi::xml_node pcdata = node.first_child(); + if (pcdata.first_child() == nullptr && pcdata.type() == pugi::node_pcdata && pcdata.next_sibling() == nullptr) { + if (ShrinkSpace(pcdata.value()).empty()) + node.remove_child(pcdata); + return; + } + + for (pugi::xml_node child : node.children()) + RemovePCDataSingle(child, xml_space_preserve); +} + +// Check the parent and ancestor of the given node if the attribute is an override +bool IsOverride(pugi::xml_node node, const char* name, const string& value) { + for (pugi::xml_node n = node.parent(); n; n = n.parent()) { + pugi::xml_attribute n_attr = n.attribute(name); + // Stop at the first parent that has the same attribute, we can safely remove the attribute in the current node if + // this parent has the same value, even if there's another parent higher up in the tree which has a different value + // for this attribute (in which case we won't be able to remove the default attribute in this parent). + if (!n_attr.empty()) { + if (n_attr.value() != value) + return true; + break; } + } + return false; } +// Check if the given attribute is the default +bool IsDefaultAttribute(const map* single_default_attrs, const string& name, const string& value) { + if (single_default_attrs) { + auto it = single_default_attrs->find(name); + if (it != single_default_attrs->end()) + return it->second == value; + } + // Fallback to common + auto it = kCommonDefaultAttributes.find(name); + return it != kCommonDefaultAttributes.end() && it->second == value; +} + +struct xml_memory_writer : pugi::xml_writer { + uint8_t* p_write; + void write(const void* data, size_t size) override { + memcpy(p_write, data, size); + p_write += size; + } +}; +} // namespace + +size_t Xml::Leanify(size_t size_leanified /*= 0*/) { + RemovePCDataSingle(doc_, false); + + // if the XML is fb2 file + if (doc_.child("FictionBook")) { + VerbosePrint("FB2 detected."); + if (depth < max_depth) { + depth++; + + pugi::xml_node root = doc_.child("FictionBook"); + + // iterate through all binary element + for (pugi::xml_node binary = root.child("binary"), next; binary; binary = next) { + next = binary.next_sibling("binary"); + pugi::xml_attribute id = binary.attribute("id"); + if (id == nullptr || id.value() == nullptr || id.value()[0] == 0) { + root.remove_child(binary); + continue; + } + PrintFileName(id.value()); -size_t Xml::Leanify(size_t size_leanified /*= 0*/) -{ - if (doc_.RootElement()) - { - const char *root_name = doc_.RootElement()->Name(); - // if the XML is fb2 file - if (strcmp(root_name, "FictionBook") == 0) - { - if (is_verbose) - { - cout << "FB2 detected." << endl; - } - if (depth < max_depth) - { - depth++; - - // iterate through all binary element - for (auto e = doc_.RootElement()->FirstChildElement("binary"); e; e = e->NextSiblingElement("binary")) - { - const char *id = e->Attribute("id"); - if (id == nullptr || *id == 0) - { - doc_.RootElement()->DeleteChild(e); - continue; - } - - PrintFileName(id); - - const char *base64_data = e->GetText(); - if (base64_data == nullptr) - { - cout << "No data found." << endl; - continue; - } - size_t base64_len = strlen(base64_data); - // copy to a new location because base64_data is const - char *new_base64_data = new char[base64_len + 1]; - memcpy(new_base64_data, base64_data, base64_len); - - Base64 b64(new_base64_data, base64_len); - size_t new_base64_len = b64.Leanify(); - - if (new_base64_len < base64_len) - { - new_base64_data[new_base64_len] = 0; - e->SetText(new_base64_data); - } - - delete[] new_base64_data; - } - depth--; - } - } - else if (strcmp(root_name, "svg") == 0) - { - if (is_verbose) - { - cout << "SVG detected." << endl; - } - - // remove XML declaration and doctype - for (auto child = doc_.FirstChild(); child; child = child->NextSibling()) - { - if (child->ToDeclaration() || child->ToUnknown()) - { - doc_.DeleteChild(child); - } - } - - TraverseElements(doc_.RootElement(), [](tinyxml2::XMLElement *e) - { - for (auto attr = e->FirstAttribute(); attr; attr = attr->Next()) - { - auto value = attr->Value(); - // remove empty attribute - if (value == nullptr || *value == 0) - { - e->DeleteAttribute(attr->Name()); - continue; - } - - // shrink spaces and newlines in attribute - string s(value); - size_t ssize = 0; - for (size_t i = 0; i < s.size(); i++) - { - if (s[i] == ' ' || s[i] == '\n' || s[i] == '\t') - { - do - { - i++; - } - while (s[i] == ' ' || s[i] == '\n' || s[i] == '\t'); - s[ssize++] = ' '; - } - s[ssize++] = s[i]; - } - if (ssize && s[ssize - 1] == ' ') - { - ssize--; - } - s.resize(ssize); - e->SetAttribute(attr->Name(), s.c_str()); - } - - // remove empty text element and container element - auto name = e->Name(); - if (e->NoChildren()) - { - if (strcmp(name, "text") == 0 || - strcmp(name, "tspan") == 0 || - strcmp(name, "a") == 0 || - strcmp(name, "defs") == 0 || - strcmp(name, "g") == 0 || - strcmp(name, "marker") == 0 || - strcmp(name, "mask") == 0 || - strcmp(name, "missing-glyph") == 0 || - strcmp(name, "pattern") == 0 || - strcmp(name, "switch") == 0 || - strcmp(name, "symbol") == 0) - { - e->Parent()->DeleteChild(e); - return; - } - } - - if (strcmp(name, "tref") == 0 && e->Attribute("xlink:href") == nullptr) - { - e->Parent()->DeleteChild(e); - return; - } - - if (strcmp(name, "metadata") == 0) - { - e->Parent()->DeleteChild(e); - return; - } - }); + const char* base64_data = binary.child_value(); + if (base64_data == nullptr || base64_data[0] == 0) { + VerbosePrint("No data found."); + continue; } + size_t base64_len = strlen(base64_data); + // copy to a new location because base64_data is const + std::vector new_base64_data(base64_data, base64_data + base64_len + 1); + + size_t new_base64_len = Base64(new_base64_data.data(), base64_len).Leanify(); + + if (new_base64_len < base64_len) { + new_base64_data[new_base64_len] = 0; + binary.text() = new_base64_data.data(); + } + } + depth--; } + } else if (doc_.child("svg")) { + VerbosePrint("SVG detected."); - // print leanified XML to memory - tinyxml2::XMLPrinter printer(0, true); - doc_.Print(&printer); - - size_t new_size = printer.CStrSize() - 1; // -1 for the \0 - fp_ -= size_leanified; - if (new_size < size_) - { - memcpy(fp_, printer.CStr(), new_size); - return new_size; - } - else if (size_leanified) - { - memmove(fp_, fp_ + size_leanified, size_); - } - return size_; -} + // remove XML declaration and doctype + for (pugi::xml_node child = doc_.first_child(), next; child; child = next) { + next = child.next_sibling(); -void Xml::TraverseElements(tinyxml2::XMLElement *ele, std::function callback) -{ - for (auto e = ele->FirstChildElement(); e; e = e->NextSiblingElement()) - { - TraverseElements(e, callback); + if (child.type() == pugi::node_declaration || child.type() == pugi::node_doctype) + doc_.remove_child(child); } - callback(ele); -} + TraverseElements(doc_.child("svg"), [](pugi::xml_node node) { + auto single_default_attrs_iter = kSingleDefaultAttributes.find(node.name()); + const map* single_default_attrs = nullptr; + if (single_default_attrs_iter != kSingleDefaultAttributes.end()) + single_default_attrs = &single_default_attrs_iter->second; + + for (pugi::xml_attribute attr = node.first_attribute(), next; attr; attr = next) { + next = attr.next_attribute(); + + string value = ShrinkSpace(attr.value()); + // Remove empty attribute + if (value.empty()) { + node.remove_attribute(attr); + continue; + } + // the second parameter in preserveAspectRatio meet by default + if (value.size() > 5 && strcmp(attr.name(), "preserveAspectRatio") == 0 && value.substr(value.size() - 5) == " meet") + value.resize(value.size() - 5); + + // Remove default attribute + if (IsDefaultAttribute(single_default_attrs, attr.name(), value)) { + // Only remove it if it's not an override of a parent non-default attribute + if (!IsOverride(node, attr.name(), value)) { + node.remove_attribute(attr); + continue; + } + } + const string kDataURIMagic = "data:"; + if ((strcmp(attr.name(), "href") == 0 || strcmp(attr.name(), "xlink:href") == 0) && + value.size() > kDataURIMagic.size() && + memcmp(value.data(), kDataURIMagic.data(), kDataURIMagic.size()) == 0) { + DataURI data_uri(&value[0], value.size()); + data_uri.SetSingleMode(true); + size_t new_size = data_uri.Leanify(); + value.resize(new_size); + } + + attr = value.c_str(); + } + + // remove empty text element and container element + const char* name = node.name(); + if (node.first_child() == nullptr) { + if (strcmp(name, "text") == 0 || strcmp(name, "tspan") == 0 || strcmp(name, "a") == 0 || + strcmp(name, "defs") == 0 || strcmp(name, "g") == 0 || strcmp(name, "marker") == 0 || + strcmp(name, "mask") == 0 || strcmp(name, "missing-glyph") == 0 || strcmp(name, "pattern") == 0 || + strcmp(name, "switch") == 0 || strcmp(name, "symbol") == 0) { + node.parent().remove_child(node); + return; + } + } + + if (strcmp(name, "tref") == 0 && node.attribute("xlink:href").empty()) { + node.parent().remove_child(node); + return; + } + + if (strcmp(name, "metadata") == 0) { + node.parent().remove_child(node); + return; + } + }); + } + + // print leanified XML to memory + xml_memory_writer writer; + fp_ -= size_leanified; + writer.p_write = fp_; + doc_.save(writer, nullptr, pugi::format_raw | pugi::format_no_declaration, encoding_); + size_ = writer.p_write - fp_; + return size_; +} diff -Nru leanify-0.4.3/formats/xml.h leanify-0.4.3+git20181014/formats/xml.h --- leanify-0.4.3/formats/xml.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/xml.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,35 +1,26 @@ -#ifndef XML_H -#define XML_H +#ifndef FORMATS_XML_H_ +#define FORMATS_XML_H_ - -#include - -#include "../lib/tinyxml2/tinyxml2.h" +#include #include "format.h" - extern bool is_verbose; - -class Xml : public Format -{ -public: - explicit Xml(void *p, size_t s = 0); - - bool IsValid() const - { - return is_valid_; - } - - size_t Leanify(size_t size_leanified = 0) override; - -private: - bool is_valid_; - tinyxml2::XMLDocument doc_; - - void TraverseElements(tinyxml2::XMLElement *e, std::function callback); +class Xml : public Format { + public: + explicit Xml(void* p, size_t s = 0); + + bool IsValid() const { + return is_valid_; + } + + size_t Leanify(size_t size_leanified = 0) override; + + private: + bool is_valid_; + pugi::xml_document doc_; + pugi::xml_encoding encoding_; }; - -#endif \ No newline at end of file +#endif // FORMATS_XML_H_ diff -Nru leanify-0.4.3/formats/zip.cpp leanify-0.4.3+git20181014/formats/zip.cpp --- leanify-0.4.3/formats/zip.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/zip.cpp 2018-05-05 21:00:58.000000000 +0000 @@ -1,12 +1,12 @@ #include "zip.h" -#include // std::search +#include #include #include #include #include -#include "../lib/miniz/miniz.h" +#include #include "../leanify.h" #include "../utils.h" @@ -17,262 +17,308 @@ using std::vector; const uint8_t Zip::header_magic[] = { 0x50, 0x4B, 0x03, 0x04 }; +bool Zip::force_deflate_ = false; -size_t Zip::Leanify(size_t size_leanified /*= 0*/) -{ - depth++; - uint8_t *p_read = fp_; - fp_ -= size_leanified; - uint8_t *p_write = fp_; - - vector vector_local_header_offset; - // Local file header - while (memcmp(p_read, header_magic, sizeof(header_magic)) == 0) - { - vector_local_header_offset.push_back(p_write - fp_); - - uint16_t filename_length = *(uint16_t *)(p_read + 26); - - size_t header_size = 30 + filename_length; - // move header - if (p_read - p_write) - { - memmove(p_write, p_read, header_size); - } +namespace { - // if Extra field length is not 0, then skip it and set it to 0 - if (*(uint16_t *)(p_write + 28)) - { - p_read += *(uint16_t *)(p_write + 28); - *(uint16_t *)(p_write + 28) = 0; - } +PACK(struct LocalHeader { + uint8_t magic[4]; + uint16_t version_needed; + uint16_t flag; + uint16_t compression_method; + uint16_t last_mod_time; + uint16_t last_mod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_len; + uint16_t extra_field_len; +}); + +PACK(struct CDHeader { + uint8_t magic[4] = { 0x50, 0x4B, 0x01, 0x02 }; + uint16_t version_made_by; + uint16_t version_needed; + uint16_t flag; + uint16_t compression_method; + uint16_t last_mod_time; + uint16_t last_mod_date; + uint32_t crc32; + uint32_t compressed_size; + uint32_t uncompressed_size; + uint16_t filename_len; + uint16_t extra_field_len; + uint16_t comment_len; + uint16_t disk_file_start; + uint16_t internal_file_attributes; + uint32_t external_file_attributes; + uint32_t local_header_offset; +}); + +PACK(struct EOCD { + uint8_t magic[4] = { 0x50, 0x4B, 0x05, 0x06 }; + uint16_t disk_num; + uint16_t disk_cd_start; + uint16_t num_records; + uint16_t num_records_total; + uint32_t cd_size; + uint32_t cd_offset; + uint16_t comment_len; +}); + +bool GetCDHeaders(const uint8_t* fp, size_t size, const EOCD& eocd, size_t zip_offset, vector* out_cd_headers, + size_t* out_base_offset) { + vector cd_headers; + size_t base_offset = 0; + // Copy cd headers to vector + const uint8_t* p_cdheader = fp + eocd.cd_offset; + const uint8_t* cd_end = p_cdheader + eocd.cd_size; + for (int i = 0; i < eocd.num_records; i++) { + CDHeader cd_header; + if (p_cdheader + sizeof(CDHeader) > cd_end) + return false; + if (memcmp(p_cdheader, cd_header.magic, sizeof(cd_header.magic)) != 0) { + // The offset might be relative to the first local file header instead of the beginning of the file. + if (i != 0 || cd_end + zip_offset > fp + size || + memcmp(p_cdheader + zip_offset, cd_header.magic, sizeof(cd_header.magic)) != 0) { + return false; + } + // This is indeed the case, set the |base_offset| to |zip_offset| and move pointer forward. + base_offset = zip_offset; + p_cdheader += base_offset; + cd_end += base_offset; + } + memcpy(&cd_header, p_cdheader, sizeof(CDHeader)); + const uint8_t* p_local_header = fp + base_offset + cd_header.local_header_offset; - uint32_t *crc = (uint32_t *)(p_write + 14); - uint32_t *compressed_size = crc + 1; - uint32_t *uncompressed_size = compressed_size + 1; - - uint32_t orig_comp_size = *compressed_size; - - uint16_t flag = *(uint16_t *)(p_write + 6); - uint16_t *compression_method = (uint16_t *)(p_write + 8); - - string filename(reinterpret_cast(p_write) + 30, filename_length); - // do not output filename if it is a directory - if ((orig_comp_size || *compression_method || flag & 8) && depth <= max_depth) - { - PrintFileName(filename); - } + // Check if local header magic matches. + if (p_local_header + sizeof(LocalHeader) + cd_header.filename_len + cd_header.compressed_size > fp + size || + memcmp(p_local_header, Zip::header_magic, sizeof(Zip::header_magic)) != 0) { + return false; + } + const LocalHeader* local_header = reinterpret_cast(p_local_header); + // Check if file name matches. + if (local_header->filename_len != cd_header.filename_len || + memcmp(p_local_header + sizeof(LocalHeader), p_cdheader + sizeof(CDHeader), cd_header.filename_len) != 0) + return false; + + p_cdheader += sizeof(CDHeader) + cd_header.filename_len + cd_header.extra_field_len + cd_header.comment_len; + if (p_cdheader > cd_end) + return false; + cd_headers.push_back(cd_header); + } + std::sort(cd_headers.begin(), cd_headers.end(), + [](const CDHeader& a, const CDHeader& b) { return a.local_header_offset < b.local_header_offset; }); + // Check if there's any overlaps. + for (size_t i = 1; i < cd_headers.size(); i++) { + if (cd_headers[i - 1].local_header_offset + sizeof(LocalHeader) + cd_headers[i - 1].filename_len + + cd_headers[i - 1].compressed_size > + cd_headers[i].local_header_offset) { + return false; + } + } + *out_cd_headers = std::move(cd_headers); + *out_base_offset = base_offset; + return true; +} - // From Wikipedia: - // If bit 3 (0x08) of the general-purpose flags field is set, - // then the CRC-32 and file sizes are not known when the header is written. - // The fields in the local header are filled with zero, - // and the CRC-32 and size are appended in a 12-byte structure - // (optionally preceded by a 4-byte signature) immediately after the compressed data - if (flag & 8) - { - // set this bit to 0 - *(uint16_t *)(p_write + 6) &= ~8; - - // data descriptor signature - const uint8_t dd_sign[] = { 0x50, 0x4B, 0x07, 0x08 }; - // search for signature - uint8_t *dd = p_read + header_size; - do - { - dd = std::search(dd + 1, fp_ + size_ + size_leanified, dd_sign, dd_sign + 4); - if (dd == fp_ + size_ + size_leanified) - { - cerr << "data descriptor signature not found!" << endl; - // abort - // zip does not have 4-byte signature preceded - return size_; - } - } - while (*(uint32_t *)(dd + 8) != dd - p_read - header_size); - - *crc = *(uint32_t *)(dd + 4); - *compressed_size = orig_comp_size = *(uint32_t *)(dd + 8); - *uncompressed_size = *(uint32_t *)(dd + 12); - } +} // namespace - // if compression method is not deflate or fast mode - // then only Leanify embedded file if the method is store - // otherwise just memmove the compressed part - if (*compression_method != 8 || is_fast) - { - if (*compression_method == 0 && depth <= max_depth) - { - // method is store - if (orig_comp_size) - { - uint32_t new_size = LeanifyFile(p_read + header_size, orig_comp_size, p_read - p_write, filename); - p_read += header_size + orig_comp_size; - *compressed_size = *uncompressed_size = new_size; - *crc = mz_crc32(0, p_write + header_size, new_size); - } - else - { - p_read += header_size; - } - } - else - { - // unsupported compression method, move it - memmove(p_write + header_size, p_read + header_size, orig_comp_size); - p_read += header_size + orig_comp_size; +size_t Zip::Leanify(size_t size_leanified /*= 0*/) { + depth++; - } - p_write += header_size + *compressed_size; + uint8_t* first_local_header = std::search(fp_, fp_ + size_, header_magic, std::end(header_magic)); + // The offset of the first local header, we should keep everything before this offset. + size_t zip_offset = first_local_header - fp_; + if (zip_offset == size_) { + cerr << "ZIP header magic not found!" << endl; + return Format::Leanify(size_leanified); + } + // The offset that all the offsets in the zip file based on (relative to). + // Should be 0 by default except when we detected that the input file has a base offset. + size_t base_offset = 0; + + EOCD eocd; + vector cd_headers; + uint8_t* p_end = fp_ + size_; + // smallest possible location of EOCD if there's a 64K comment + uint8_t* p_searchstart = std::max(fp_, p_end - 65535 - sizeof(eocd.magic)); + uint8_t* p_eocd = nullptr; + while (true) { + if (p_eocd != nullptr) { + cerr << "Warning: Found EOCD at 0x" << std::hex << p_eocd - fp_ << std::dec << ", but it's invalid." << endl; + p_end = p_eocd; + } + p_eocd = std::find_end(p_searchstart, p_end, eocd.magic, std::end(eocd.magic)); + if (p_eocd == p_end) { + cerr << "EOCD not found!" << endl; + return Format::Leanify(size_leanified); + } - } - else - { - // the method is deflate, uncompress it and recompress with zopfli - - p_read += header_size; - p_write += header_size; - - if (*uncompressed_size) - { - // uncompress - size_t s = 0; - uint8_t *buffer = static_cast(tinfl_decompress_mem_to_heap(p_read, orig_comp_size, &s, 0)); - - if (!buffer || - s != *uncompressed_size || - *crc != mz_crc32(0, buffer, *uncompressed_size)) - { - cerr << "ZIP file corrupted!" << endl; - mz_free(buffer); - memmove(p_write, p_read, orig_comp_size); - p_read += orig_comp_size; - p_write += orig_comp_size; - continue; - } - - // Leanify uncompressed file - uint32_t new_uncomp_size = s; - // workaround of TinyXML2 not supporting xml:space="preserve" - if (filename_length != 17 || filename != "word/document.xml") - { - new_uncomp_size = LeanifyFile(buffer, s, 0, filename); - } - - // recompress - uint8_t bp = 0, *out = nullptr; - size_t new_comp_size = 0; - ZopfliDeflate(&zopfli_options_, 2, 1, buffer, new_uncomp_size, &bp, &out, &new_comp_size); - - // switch to store if deflate makes file larger - if (new_uncomp_size <= new_comp_size && new_uncomp_size <= orig_comp_size) - { - *compression_method = 0; - *crc = mz_crc32(0, buffer, new_uncomp_size); - *compressed_size = new_uncomp_size; - *uncompressed_size = new_uncomp_size; - memcpy(p_write, buffer, new_uncomp_size); - p_write += new_uncomp_size; - } - else if (new_comp_size < orig_comp_size) - { - *crc = mz_crc32(0, buffer, new_uncomp_size); - *compressed_size = new_comp_size; - *uncompressed_size = new_uncomp_size; - memcpy(p_write, out, new_comp_size); - p_write += new_comp_size; - } - else - { - memmove(p_write, p_read, orig_comp_size); - p_write += orig_comp_size; - } - p_read += orig_comp_size; - - mz_free(buffer); - delete[] out; - } - else - { - *compression_method = 0; - *compressed_size = 0; - p_read += orig_comp_size; - } - } + if (p_eocd + sizeof(EOCD) > p_end) + continue; - // we don't use data descriptor, so that can save more bytes (16 per file) - if (flag & 8) - { - p_read += 16; - } + memcpy(&eocd, p_eocd, sizeof(EOCD)); + uint8_t* cd_end = fp_ + eocd.cd_offset + eocd.cd_size; + if (cd_end > p_eocd) + continue; + + // Try to get all CD headers using this EOCD, if everything checks out then proceed. + if (GetCDHeaders(fp_, size_, eocd, zip_offset, &cd_headers, &base_offset)) { + break; } + } - uint8_t *central_directory = p_write; - // Central directory file header - const uint8_t cd_header_magic[] = { 0x50, 0x4B, 0x01, 0x02 }; - - for (int i = 0; memcmp(p_read, cd_header_magic, sizeof(cd_header_magic)) == 0; i++) - { - int header_size = 46 + *(uint16_t *)(p_read + 28); - // move header - if (p_read - p_write) - { - memmove(p_write, p_read, header_size); - } + uint8_t* fp_w = fp_ - size_leanified; + uint8_t* fp_w_base = fp_w + base_offset; + memmove(fp_w, fp_, zip_offset); + uint8_t* p_write = fp_w + zip_offset; + // Local file header + for (CDHeader& cd_header : cd_headers) { + uint8_t* p_read = fp_ + base_offset + cd_header.local_header_offset; + + cd_header.local_header_offset = p_write - fp_w_base; + + size_t header_size = sizeof(LocalHeader) + cd_header.filename_len; + // move header + memmove(p_write, p_read, header_size); + LocalHeader* local_header = reinterpret_cast(p_write); + + // if Extra field length is not 0, then skip it and set it to 0 + if (local_header->extra_field_len) { + p_read += local_header->extra_field_len; + local_header->extra_field_len = 0; + } - // set bit 3 of General purpose bit flag to 0 - *(uint16_t *)(p_write + 8) &= ~8; + if (local_header->flag & 8) { + // set this bit to 0, we don't use data descriptor to save 16 byte + local_header->flag &= ~8; + cd_header.flag &= ~8; + + // Use the correct value from central directory + local_header->crc32 = cd_header.crc32; + local_header->compressed_size = cd_header.compressed_size; + local_header->uncompressed_size = cd_header.uncompressed_size; + } - // if Extra field length is not 0, then skip it and set it to 0 - if (*(uint16_t *)(p_write + 30)) - { - p_read += *(uint16_t *)(p_write + 30); - *(uint16_t *)(p_write + 30) = 0; - } + string filename(reinterpret_cast(local_header) + sizeof(LocalHeader), local_header->filename_len); + // do not output filename if it is a directory + if ((local_header->compressed_size || local_header->compression_method) && depth <= max_depth) + PrintFileName(filename); + + p_read += header_size; + p_write += header_size; + + if (p_read + local_header->compressed_size > p_end) { + cerr << "Compressed size too large: " << local_header->compressed_size << endl; + break; + } - // if File comment length is not 0, then skip it and set it to 0 - if (*(uint16_t *)(p_write + 32)) - { - p_read += *(uint16_t *)(p_write + 32); - *(uint16_t *)(p_write + 32) = 0; + // If the method is store, just Leanify the embedded file + // don't try to change it to deflate, it might break some file. + if (local_header->compression_method == 0) { + // method is store + if (local_header->compressed_size) { + uint32_t new_size = LeanifyFile(p_read, local_header->compressed_size, p_read - p_write, filename); + cd_header.crc32 = local_header->crc32 = lodepng_crc32(p_write, new_size); + cd_header.compressed_size = local_header->compressed_size = new_size; + cd_header.uncompressed_size = local_header->uncompressed_size = new_size; + if (force_deflate_) { + uint8_t bp = 0, *compress_buf = nullptr; + size_t deflate_size = 0; + ZopfliDeflate(&zopfli_options_, 2, 1, p_write, new_size, &bp, &compress_buf, &deflate_size); + if (deflate_size < new_size) { + // switch to deflate + cd_header.compression_method = local_header->compression_method = 8; + cd_header.compressed_size = local_header->compressed_size = deflate_size; + memcpy(p_write, compress_buf, deflate_size); + } + delete[] compress_buf; } + p_write += local_header->compressed_size; + } + continue; + } - uint8_t *local_header = fp_ + vector_local_header_offset[i]; - - // copy new CRC-32, Compressed size, Uncompressed size_ - // from Local file header to Central directory file header - memcpy(p_write + 16, local_header + 14, 12); - - // update compression method - *(uint16_t *)(p_write + 10) = *(uint16_t *)(local_header + 8); + // If unsupported compression method or fast mode or encrypted, just move it. + if (local_header->compression_method != 8 || is_fast || local_header->flag & 1) { + memmove(p_write, p_read, local_header->compressed_size); + p_write += local_header->compressed_size; + continue; + } - // new Local file header offset - *(uint32_t *)(p_write + 42) = vector_local_header_offset[i]; + // Switch from deflate to store for empty file. + if (local_header->uncompressed_size == 0) { + cd_header.compression_method = local_header->compression_method = 0; + cd_header.compressed_size = local_header->compressed_size = 0; + continue; + } - p_read += header_size; - p_write += header_size; + // decompress + size_t decompressed_size = 0; + uint8_t* decompress_buf = nullptr; + if (lodepng_inflate(&decompress_buf, &decompressed_size, p_read, local_header->compressed_size, + &lodepng_default_decompress_settings) || + !decompress_buf || decompressed_size != local_header->uncompressed_size || + local_header->crc32 != lodepng_crc32(decompress_buf, local_header->uncompressed_size)) { + cerr << "Decompression failed or CRC32 mismatch, skipping this file." << endl; + free(decompress_buf); + memmove(p_write, p_read, local_header->compressed_size); + p_write += local_header->compressed_size; + continue; } - // End of central directory record - const uint8_t eocd_header_magic[] = { 0x50, 0x4B, 0x05, 0x06 }; - if (memcmp(p_read, eocd_header_magic, sizeof(eocd_header_magic))) - { - cerr << "EOCD not found!" << endl; + // Leanify uncompressed file + uint32_t new_uncomp_size = LeanifyFile(decompress_buf, decompressed_size, 0, filename); + // recompress + uint8_t bp = 0, *compress_buf = nullptr; + size_t new_comp_size = 0; + ZopfliDeflate(&zopfli_options_, 2, 1, decompress_buf, new_uncomp_size, &bp, &compress_buf, &new_comp_size); + + // switch to store if deflate makes file larger + if (new_uncomp_size <= new_comp_size && new_uncomp_size <= local_header->compressed_size) { + cd_header.compression_method = local_header->compression_method = 0; + cd_header.crc32 = local_header->crc32 = lodepng_crc32(decompress_buf, new_uncomp_size); + cd_header.compressed_size = local_header->compressed_size = new_uncomp_size; + cd_header.uncompressed_size = local_header->uncompressed_size = new_uncomp_size; + memcpy(p_write, decompress_buf, new_uncomp_size); + } else if (new_comp_size < local_header->compressed_size) { + cd_header.crc32 = local_header->crc32 = lodepng_crc32(decompress_buf, new_uncomp_size); + cd_header.compressed_size = local_header->compressed_size = new_comp_size; + cd_header.uncompressed_size = local_header->uncompressed_size = new_uncomp_size; + memcpy(p_write, compress_buf, new_comp_size); + } else { + memmove(p_write, p_read, local_header->compressed_size); } - if (p_read - p_write) - { - memmove(p_write, p_read, 12); - } - // central directory size - *(uint32_t *)(p_write + 12) = p_write - central_directory; - // central directory offset - *(uint32_t *)(p_write + 16) = central_directory - fp_; - // set comment length to 0 - *(uint16_t *)(p_write + 20) = 0; + p_write += local_header->compressed_size; - // 22 is the length of EOCD - return p_write + 22 - fp_; + free(decompress_buf); + delete[] compress_buf; + } + + // central directory offset + eocd.cd_offset = p_write - fp_w_base; + for (CDHeader& cd_header : cd_headers) { + cd_header.extra_field_len = cd_header.comment_len = 0; + + memcpy(p_write, &cd_header, sizeof(CDHeader)); + p_write += sizeof(CDHeader); + // Copy the filename from local file header to central directory, + // the old central directory might have been overwritten already because we sort them. + memcpy(p_write, fp_w_base + cd_header.local_header_offset + 30, cd_header.filename_len); + p_write += cd_header.filename_len; + } + + // Update end of central directory record + eocd.num_records = eocd.num_records_total = cd_headers.size(); + eocd.cd_size = p_write - fp_w_base - eocd.cd_offset; + eocd.comment_len = 0; + + memcpy(p_write, &eocd, sizeof(EOCD)); + + fp_ -= size_leanified; + size_ = p_write + sizeof(EOCD) - fp_; + return size_; } - diff -Nru leanify-0.4.3/formats/zip.h leanify-0.4.3+git20181014/formats/zip.h --- leanify-0.4.3/formats/zip.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/formats/zip.h 2018-05-05 21:00:58.000000000 +0000 @@ -1,37 +1,31 @@ -#ifndef ZIP_H -#define ZIP_H +#ifndef FORMATS_ZIP_H_ +#define FORMATS_ZIP_H_ - -#include "../lib/zopfli/deflate.h" +#include #include "format.h" - extern bool is_fast; extern int iterations; extern int depth; +class Zip : public Format { + public: + explicit Zip(void* p, size_t s = 0) : Format(p, s) { + ZopfliInitOptions(&zopfli_options_); + zopfli_options_.numiterations = iterations; + } + ~Zip() { + depth--; + } -class Zip : public Format -{ -public: - explicit Zip(void *p, size_t s = 0) : Format(p, s) - { - ZopfliInitOptions(&zopfli_options_); - zopfli_options_.numiterations = iterations; - } - ~Zip() - { - depth--; - } + size_t Leanify(size_t size_leanified = 0) override; - size_t Leanify(size_t size_leanified = 0) override; + static const uint8_t header_magic[4]; + static bool force_deflate_; - static const uint8_t header_magic[4]; - -private: - ZopfliOptions zopfli_options_; + private: + ZopfliOptions zopfli_options_; }; - -#endif \ No newline at end of file +#endif // FORMATS_ZIP_H_ diff -Nru leanify-0.4.3/leanify.cpp leanify-0.4.3+git20181014/leanify.cpp --- leanify-0.4.3/leanify.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/leanify.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,9 +1,10 @@ #include "leanify.h" +#include #include -#include "lib/miniz/miniz.h" -#include "lib/zopfli/zlib_container.h" +#include +#include #include "formats/data_uri.h" #include "formats/dwf.h" @@ -13,227 +14,159 @@ #include "formats/ico.h" #include "formats/jpeg.h" #include "formats/lua.h" +#include "formats/mime.h" #include "formats/pe.h" #include "formats/png.h" +#include "formats/rdb.h" #include "formats/swf.h" #include "formats/tar.h" -#include "formats/rdb.h" +#include "formats/vcf.h" #include "formats/xml.h" #include "formats/zip.h" +#include "utils.h" using std::cerr; -using std::cout; using std::endl; using std::string; -Format *GetType(void *file_pointer, size_t file_size, const string& filename) -{ - if (depth > max_depth) - { - return new Format(file_pointer, file_size); - } - - if (!filename.empty()) - { - size_t dot = filename.find_last_of('.'); - if (dot != string::npos) - { - string ext = filename.substr(dot + 1); - // toupper - for (auto &c : ext) - c &= ~0x20; - - if (ext == "HTML" || - ext == "HTM" || - ext == "JS" || - ext == "CSS") - { - if (is_verbose) - { - cout << ext << " detected." << endl; - } - return new DataURI(file_pointer, file_size); - } - } +Format* GetType(void* file_pointer, size_t file_size, const string& filename) { + if (depth > max_depth) + return new Format(file_pointer, file_size); - } - if (memcmp(file_pointer, Png::header_magic, sizeof(Png::header_magic)) == 0) - { - if (is_verbose) - { - cout << "PNG detected." << endl; - } - return new Png(file_pointer, file_size); - } - else if (memcmp(file_pointer, Jpeg::header_magic, sizeof(Jpeg::header_magic)) == 0) - { - if (is_verbose) - { - cout << "JPEG detected." << endl; - } - return new Jpeg(file_pointer, file_size); - } - else if (memcmp(file_pointer, Lua::header_magic, sizeof(Lua::header_magic)) == 0) - { - if (is_verbose) - { - cout << "Lua detected." << endl; - } - return new Lua(file_pointer, file_size); - } - else if (memcmp(file_pointer, Zip::header_magic, sizeof(Zip::header_magic)) == 0) - { - if (is_verbose) - { - cout << "ZIP detected." << endl; - } - return new Zip(file_pointer, file_size); - } - else if (memcmp(file_pointer, Pe::header_magic, sizeof(Pe::header_magic)) == 0) - { - if (is_verbose) - { - cout << "PE detected." << endl; - } - return new Pe(file_pointer, file_size); - } - else if (memcmp(file_pointer, Gz::header_magic, sizeof(Gz::header_magic)) == 0) - { - if (is_verbose) - { - cout << "GZ detected." << endl; - } - return new Gz(file_pointer, file_size); - } - else if (memcmp(file_pointer, Ico::header_magic, sizeof(Ico::header_magic)) == 0) - { - if (is_verbose) - { - cout << "ICO detected." << endl; - } - return new Ico(file_pointer, file_size); - } - else if (memcmp(file_pointer, Dwf::header_magic, sizeof(Dwf::header_magic)) == 0) - { - if (is_verbose) - { - cout << "DWF detected." << endl; - } - return new Dwf(file_pointer, file_size); - } - else if (memcmp(file_pointer, Gft::header_magic, sizeof(Gft::header_magic)) == 0) - { - if (is_verbose) - { - cout << "GFT detected." << endl; - } - return new Gft(file_pointer, file_size); - } - else if (memcmp(file_pointer, Rdb::header_magic, sizeof(Rdb::header_magic)) == 0) - { - if (is_verbose) - { - cout << "RDB detected." << endl; - } - return new Rdb(file_pointer, file_size); - } - else if (memcmp(file_pointer, Swf::header_magic, sizeof(Swf::header_magic)) == 0 || + if (!filename.empty()) { + size_t dot = filename.find_last_of('.'); + if (dot != string::npos) { + string ext = filename.substr(dot + 1); + // toupper + for (auto& c : ext) + c &= ~0x20; + + if (ext == "HTML" || ext == "HTM" || ext == "JS" || ext == "CSS") { + VerbosePrint(ext, " detected."); + return new DataURI(file_pointer, file_size); + } + if (ext == "VCF" || ext == "VCARD") { + VerbosePrint(ext, " detected."); + return new Vcf(file_pointer, file_size); + } + if (ext == "MHT" || ext == "MHTML" || ext == "MIM" || ext == "MIME" || ext == "EML") { + VerbosePrint(ext, " detected."); + return new Mime(file_pointer, file_size); + } + } + } + if (memcmp(file_pointer, Png::header_magic, sizeof(Png::header_magic)) == 0) { + VerbosePrint("PNG detected."); + return new Png(file_pointer, file_size); + } else if (memcmp(file_pointer, Jpeg::header_magic, sizeof(Jpeg::header_magic)) == 0) { + VerbosePrint("JPEG detected."); + return new Jpeg(file_pointer, file_size); + } else if (memcmp(file_pointer, Lua::header_magic, sizeof(Lua::header_magic)) == 0) { + VerbosePrint("Lua detected."); + return new Lua(file_pointer, file_size); + } else if (memcmp(file_pointer, Zip::header_magic, sizeof(Zip::header_magic)) == 0) { + VerbosePrint("ZIP detected."); + return new Zip(file_pointer, file_size); + } else if (memcmp(file_pointer, Pe::header_magic, sizeof(Pe::header_magic)) == 0) { + VerbosePrint("PE detected."); + return new Pe(file_pointer, file_size); + } else if (memcmp(file_pointer, Gz::header_magic, sizeof(Gz::header_magic)) == 0) { + VerbosePrint("GZ detected."); + return new Gz(file_pointer, file_size); + } else if (memcmp(file_pointer, Ico::header_magic, sizeof(Ico::header_magic)) == 0) { + VerbosePrint("ICO detected."); + return new Ico(file_pointer, file_size); + } else if (memcmp(file_pointer, Dwf::header_magic, sizeof(Dwf::header_magic)) == 0) { + VerbosePrint("DWF detected."); + return new Dwf(file_pointer, file_size); + } else if (memcmp(file_pointer, Gft::header_magic, sizeof(Gft::header_magic)) == 0) { + VerbosePrint("GFT detected."); + return new Gft(file_pointer, file_size); + } else if (memcmp(file_pointer, Rdb::header_magic, sizeof(Rdb::header_magic)) == 0) { + VerbosePrint("RDB detected."); + return new Rdb(file_pointer, file_size); + } else if (memcmp(file_pointer, Swf::header_magic, sizeof(Swf::header_magic)) == 0 || memcmp(file_pointer, Swf::header_magic_deflate, sizeof(Swf::header_magic_deflate)) == 0 || - memcmp(file_pointer, Swf::header_magic_lzma, sizeof(Swf::header_magic_lzma)) == 0) - { - if (is_verbose) - { - cout << "SWF detected." << endl; - } - return new Swf(file_pointer, file_size); - } - else - { - // tar file does not have header magic - // ustar is optional - { - Tar *t = new Tar(file_pointer, file_size); - // checking first record checksum - if (t->IsValid()) - { - if (is_verbose) - { - cout << "tar detected." << endl; - } - return t; - } - delete t; - } - - // XML file does not have header magic - // have to parse and see if there are any errors. - { - Xml *x = new Xml(file_pointer, file_size); - if (x->IsValid()) - { - if (is_verbose) - { - cout << "XML detected." << endl; - } - return x; - } - delete x; - } - } - - if (is_verbose) - { - cout << "Format not supported!" << endl; - } - // for unsupported format, just memmove it. - return new Format(file_pointer, file_size); + memcmp(file_pointer, Swf::header_magic_lzma, sizeof(Swf::header_magic_lzma)) == 0) { + VerbosePrint("SWF detected."); + return new Swf(file_pointer, file_size); + } else { + // Search for vcard magic which might not be at the very beginning. + const string vcard_magic = "BEGIN:VCARD"; + const char* fp = static_cast(file_pointer); + const char* search_end = fp + std::min(static_cast(1024), file_size); + if (std::search(fp, search_end, vcard_magic.begin(), vcard_magic.end()) < search_end) { + VerbosePrint("VCF detected."); + return new Vcf(file_pointer, file_size); + } + + // tar file does not have header magic + // ustar is optional + { + Tar* t = new Tar(file_pointer, file_size); + // checking first record checksum + if (t->IsValid()) { + VerbosePrint("tar detected."); + return t; + } + delete t; + } + + // XML file does not have header magic + // have to parse and see if there are any errors. + { + Xml* x = new Xml(file_pointer, file_size); + if (x->IsValid()) { + VerbosePrint("XML detected."); + return x; + } + delete x; + } + } + + VerbosePrint("Format not supported!"); + // for unsupported format, just memmove it. + return new Format(file_pointer, file_size); } - // Leanify the file // and move the file ahead size_leanified bytes // the new location of the file will be file_pointer - size_leanified // it's designed this way to avoid extra memmove or memcpy // return new size -size_t LeanifyFile(void *file_pointer, size_t file_size, size_t size_leanified /*= 0*/, const string& filename /*= ""*/) -{ - Format *f = GetType(file_pointer, file_size, filename); - size_t r = f->Leanify(size_leanified); - delete f; - return r; +size_t LeanifyFile(void* file_pointer, size_t file_size, size_t size_leanified /*= 0*/, + const string& filename /*= ""*/) { + Format* f = GetType(file_pointer, file_size, filename); + size_t r = f->Leanify(size_leanified); + delete f; + return r; } - -size_t ZlibRecompress(void *src, size_t src_len, size_t size_leanified /*= 0*/) -{ - if (!is_fast) - { - size_t s = 0; - uint8_t *buffer = static_cast(tinfl_decompress_mem_to_heap(src, src_len, &s, TINFL_FLAG_PARSE_ZLIB_HEADER)); - if (!buffer) - { - cerr << "Decompress Zlib data failed." << endl; - } - else - { - ZopfliOptions zopfli_options; - ZopfliInitOptions(&zopfli_options); - zopfli_options.numiterations = iterations; - - size_t new_size = 0; - uint8_t *out_buffer = nullptr; - ZopfliZlibCompress(&zopfli_options, buffer, s, &out_buffer, &new_size); - mz_free(buffer); - if (new_size < src_len) - { - memcpy(static_cast(src) - size_leanified, out_buffer, new_size); - delete[] out_buffer; - return new_size; - } - delete[] out_buffer; - } +size_t ZlibRecompress(uint8_t* src, size_t src_len, size_t size_leanified /*= 0*/) { + if (!is_fast) { + size_t uncompressed_size = 0; + uint8_t* buffer = nullptr; + if (lodepng_zlib_decompress(&buffer, &uncompressed_size, src, src_len, &lodepng_default_decompress_settings) || + !buffer) { + cerr << "Decompress Zlib data failed." << endl; + } else { + ZopfliOptions zopfli_options; + ZopfliInitOptions(&zopfli_options); + zopfli_options.numiterations = iterations; + + size_t new_size = 0; + uint8_t* out_buffer = nullptr; + ZopfliZlibCompress(&zopfli_options, buffer, uncompressed_size, &out_buffer, &new_size); + free(buffer); + if (new_size < src_len) { + memcpy(src - size_leanified, out_buffer, new_size); + free(out_buffer); + return new_size; + } + free(out_buffer); } + } - memmove(static_cast(src) - size_leanified, src, src_len); - return src_len; + memmove(src - size_leanified, src, src_len); + return src_len; } \ No newline at end of file diff -Nru leanify-0.4.3/leanify.h leanify-0.4.3+git20181014/leanify.h --- leanify-0.4.3/leanify.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/leanify.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,5 +1,5 @@ -#ifndef LEANIFY_H -#define LEANIFY_H +#ifndef LEANIFY_H_ +#define LEANIFY_H_ #include #include @@ -7,9 +7,8 @@ extern int depth; extern int max_depth; -size_t LeanifyFile(void *file_pointer, size_t file_size, size_t size_leanified = 0, const std::string& filename = ""); +size_t LeanifyFile(void* file_pointer, size_t file_size, size_t size_leanified = 0, const std::string& filename = ""); -size_t ZlibRecompress(void *src, size_t src_len, size_t size_leanified = 0); +size_t ZlibRecompress(uint8_t* src, size_t src_len, size_t size_leanified = 0); - -#endif \ No newline at end of file +#endif // LEANIFY_H_ diff -Nru leanify-0.4.3/Leanify.rc leanify-0.4.3+git20181014/Leanify.rc --- leanify-0.4.3/Leanify.rc 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/Leanify.rc 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,104 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +#include "version.h" + +#ifndef __GNUC__ +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS +#else +#include "winver.h" +#endif + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +#ifndef __GNUC__ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#endif + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_NUM + PRODUCTVERSION VERSION_NUM + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904b0" + BEGIN + VALUE "FileDescription", "lightweight lossless file minifier/optimizer" + VALUE "FileVersion", VERSION_STR + VALUE "InternalName", "Leanify.exe" + VALUE "LegalCopyright", "Copyright (C) 2016 JayXon" + VALUE "OriginalFilename", "Leanify.exe" + VALUE "ProductName", "Leanify" + VALUE "ProductVersion", VERSION_STR + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1200 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff -Nru leanify-0.4.3/Leanify.vcxproj leanify-0.4.3+git20181014/Leanify.vcxproj --- leanify-0.4.3/Leanify.vcxproj 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/Leanify.vcxproj 2018-11-03 16:56:30.000000000 +0000 @@ -0,0 +1,411 @@ + + + + + Clang-Release + Win32 + + + Clang-Release + x64 + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {1D2C64B0-04FE-46E2-BFF5-A2E8C5C4894E} + Win32Proj + Leanify + 10.0.17134.0 + + + + Application + true + Unicode + v141 + + + Application + true + Unicode + v141 + + + Application + false + true + Unicode + v141_xp + + + Application + false + true + Unicode + llvm + + + Application + false + true + Unicode + v141_xp + + + Application + false + true + Unicode + llvm + + + + + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + false + + + false + + + -flto + + + -flto + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + false + stdcpp14 + + + + Console + true + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + false + stdcpp14 + + + + Console + true + + + + + Level3 + + + Full + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + Speed + false + false + true + false + StreamingSIMDExtensions2 + Fast + false + 4530;4577 + true + true + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + None + true + false + stdcpp14 + + + + Console + false + true + true + UseFastLinkTimeCodeGeneration + + + + + Level3 + + + Full + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + Speed + false + false + true + false + StreamingSIMDExtensions2 + Fast + false + 4530;4577 + true + true + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + Default + stdcpp14 + None + true + false + + + + Console + false + true + true + Default + + + + + Level3 + + + Full + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + Speed + false + false + true + false + NotSet + Fast + false + 4530;4267;4244;4577 + true + true + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + None + true + false + stdcpp14 + + + + Console + false + true + true + UseFastLinkTimeCodeGeneration + + + + + Level3 + + + Full + true + true + WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions) + MultiThreaded + false + Speed + false + false + true + false + NotSet + Fast + false + 4530;4267;4244;4577 + true + true + $(ProjectDir)lib;%(AdditionalIncludeDirectories) + Default + stdcpp14 + None + true + false + + + + Console + false + true + true + Default + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff -Nru leanify-0.4.3/Leanify.vcxproj.filters leanify-0.4.3+git20181014/Leanify.vcxproj.filters --- leanify-0.4.3/Leanify.vcxproj.filters 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/Leanify.vcxproj.filters 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,335 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + {0515174f-ead6-4c34-bc6d-28caa6328b14} + + + {052bcb98-874f-4e68-814e-4db31fa3f8e7} + + + {c4770949-c1fa-4c7f-9e50-07450b5093df} + + + {4d3051da-2db0-41bc-92d9-2f4af769f9a4} + + + {29e470fe-f7bd-40f4-baa8-31effed6a3f9} + + + {fd445d3c-46b8-4e6e-9873-22d5ad8f9d09} + + + {f1b9091a-b86f-4412-9cbe-9fd2c017db97} + + + {d8af6712-5337-48d5-ad50-b247491708e5} + + + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files + + + Source Files\lib\zopflipng + + + Source Files\lib\zopflipng\lodepng + + + Source Files\lib\zopflipng\lodepng + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\lib\zopfli + + + Source Files\formats + + + Source Files\formats + + + Source Files + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\lib\mozjpeg + + + Source Files\formats + + + Source Files\lib\LZMA + + + Source Files\lib\LZMA + + + Source Files\lib\LZMA + + + Source Files\lib\LZMA + + + Source Files\lib\LZMA + + + Source Files\lib\LZMA + + + Source Files\formats + + + Source Files\formats + + + Source Files + + + Source Files\lib\mozjpeg + + + Source Files\formats + + + Source Files\formats + + + Source Files\formats + + + Source Files\lib + + + Source Files\formats + + + Source Files\formats + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files + + + Header Files + + + Header Files + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + Header Files\formats + + + + + Resource Files + + + \ No newline at end of file diff -Nru leanify-0.4.3/lib/LZMA/7zTypes.h leanify-0.4.3+git20181014/lib/LZMA/7zTypes.h --- leanify-0.4.3/lib/LZMA/7zTypes.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/7zTypes.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* 7zTypes.h -- Basic types -2013-11-12 : Igor Pavlov : Public domain */ +2017-07-17 : Igor Pavlov : Public domain */ #ifndef __7Z_TYPES_H #define __7Z_TYPES_H @@ -42,13 +42,23 @@ typedef int SRes; + #ifdef _WIN32 + /* typedef DWORD WRes; */ typedef unsigned WRes; +#define MY_SRes_HRESULT_FROM_WRes(x) HRESULT_FROM_WIN32(x) + #else + typedef int WRes; +#define MY__FACILITY_WIN32 7 +#define MY__FACILITY__WRes MY__FACILITY_WIN32 +#define MY_SRes_HRESULT_FROM_WRes(x) ((HRESULT)(x) <= 0 ? ((HRESULT)(x)) : ((HRESULT) (((x) & 0x0000FFFF) | (MY__FACILITY__WRes << 16) | 0x80000000))) + #endif + #ifndef RINOK #define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; } #endif @@ -112,48 +122,72 @@ #define MY_NO_INLINE #endif +#define MY_FORCE_INLINE __forceinline + #define MY_CDECL __cdecl #define MY_FAST_CALL __fastcall #else #define MY_NO_INLINE +#define MY_FORCE_INLINE #define MY_CDECL #define MY_FAST_CALL +/* inline keyword : for C++ / C99 */ + +/* GCC, clang: */ +/* +#if defined (__GNUC__) && (__GNUC__ >= 4) +#define MY_FORCE_INLINE __attribute__((always_inline)) +#define MY_NO_INLINE __attribute__((noinline)) +#endif +*/ + #endif /* The following interfaces use first parameter as pointer to structure */ -typedef struct +typedef struct IByteIn IByteIn; +struct IByteIn { - Byte (*Read)(void *p); /* reads one byte, returns 0 in case of EOF or error */ -} IByteIn; + Byte (*Read)(const IByteIn *p); /* reads one byte, returns 0 in case of EOF or error */ +}; +#define IByteIn_Read(p) (p)->Read(p) -typedef struct + +typedef struct IByteOut IByteOut; +struct IByteOut { - void (*Write)(void *p, Byte b); -} IByteOut; + void (*Write)(const IByteOut *p, Byte b); +}; +#define IByteOut_Write(p, b) (p)->Write(p, b) -typedef struct + +typedef struct ISeqInStream ISeqInStream; +struct ISeqInStream { - SRes (*Read)(void *p, void *buf, size_t *size); + SRes (*Read)(const ISeqInStream *p, void *buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) < input(*size)) is allowed */ -} ISeqInStream; +}; +#define ISeqInStream_Read(p, buf, size) (p)->Read(p, buf, size) /* it can return SZ_ERROR_INPUT_EOF */ -SRes SeqInStream_Read(ISeqInStream *stream, void *buf, size_t size); -SRes SeqInStream_Read2(ISeqInStream *stream, void *buf, size_t size, SRes errorType); -SRes SeqInStream_ReadByte(ISeqInStream *stream, Byte *buf); +SRes SeqInStream_Read(const ISeqInStream *stream, void *buf, size_t size); +SRes SeqInStream_Read2(const ISeqInStream *stream, void *buf, size_t size, SRes errorType); +SRes SeqInStream_ReadByte(const ISeqInStream *stream, Byte *buf); -typedef struct + +typedef struct ISeqOutStream ISeqOutStream; +struct ISeqOutStream { - size_t (*Write)(void *p, const void *buf, size_t size); + size_t (*Write)(const ISeqOutStream *p, const void *buf, size_t size); /* Returns: result - the number of actually written bytes. (result < size) means error */ -} ISeqOutStream; +}; +#define ISeqOutStream_Write(p, buf, size) (p)->Write(p, buf, size) typedef enum { @@ -162,78 +196,162 @@ SZ_SEEK_END = 2 } ESzSeek; -typedef struct + +typedef struct ISeekInStream ISeekInStream; +struct ISeekInStream { - SRes (*Read)(void *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ - SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); -} ISeekInStream; + SRes (*Read)(const ISeekInStream *p, void *buf, size_t *size); /* same as ISeqInStream::Read */ + SRes (*Seek)(const ISeekInStream *p, Int64 *pos, ESzSeek origin); +}; +#define ISeekInStream_Read(p, buf, size) (p)->Read(p, buf, size) +#define ISeekInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) -typedef struct + +typedef struct ILookInStream ILookInStream; +struct ILookInStream { - SRes (*Look)(void *p, const void **buf, size_t *size); + SRes (*Look)(const ILookInStream *p, const void **buf, size_t *size); /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream. (output(*size) > input(*size)) is not allowed (output(*size) < input(*size)) is allowed */ - SRes (*Skip)(void *p, size_t offset); + SRes (*Skip)(const ILookInStream *p, size_t offset); /* offset must be <= output(*size) of Look */ - SRes (*Read)(void *p, void *buf, size_t *size); + SRes (*Read)(const ILookInStream *p, void *buf, size_t *size); /* reads directly (without buffer). It's same as ISeqInStream::Read */ - SRes (*Seek)(void *p, Int64 *pos, ESzSeek origin); -} ILookInStream; + SRes (*Seek)(const ILookInStream *p, Int64 *pos, ESzSeek origin); +}; -SRes LookInStream_LookRead(ILookInStream *stream, void *buf, size_t *size); -SRes LookInStream_SeekTo(ILookInStream *stream, UInt64 offset); +#define ILookInStream_Look(p, buf, size) (p)->Look(p, buf, size) +#define ILookInStream_Skip(p, offset) (p)->Skip(p, offset) +#define ILookInStream_Read(p, buf, size) (p)->Read(p, buf, size) +#define ILookInStream_Seek(p, pos, origin) (p)->Seek(p, pos, origin) + + +SRes LookInStream_LookRead(const ILookInStream *stream, void *buf, size_t *size); +SRes LookInStream_SeekTo(const ILookInStream *stream, UInt64 offset); /* reads via ILookInStream::Read */ -SRes LookInStream_Read2(ILookInStream *stream, void *buf, size_t size, SRes errorType); -SRes LookInStream_Read(ILookInStream *stream, void *buf, size_t size); +SRes LookInStream_Read2(const ILookInStream *stream, void *buf, size_t size, SRes errorType); +SRes LookInStream_Read(const ILookInStream *stream, void *buf, size_t size); + -#define LookToRead_BUF_SIZE (1 << 14) typedef struct { - ILookInStream s; - ISeekInStream *realStream; + ILookInStream vt; + const ISeekInStream *realStream; + size_t pos; - size_t size; - Byte buf[LookToRead_BUF_SIZE]; -} CLookToRead; + size_t size; /* it's data size */ + + /* the following variables must be set outside */ + Byte *buf; + size_t bufSize; +} CLookToRead2; + +void LookToRead2_CreateVTable(CLookToRead2 *p, int lookahead); + +#define LookToRead2_Init(p) { (p)->pos = (p)->size = 0; } -void LookToRead_CreateVTable(CLookToRead *p, int lookahead); -void LookToRead_Init(CLookToRead *p); typedef struct { - ISeqInStream s; - ILookInStream *realStream; + ISeqInStream vt; + const ILookInStream *realStream; } CSecToLook; void SecToLook_CreateVTable(CSecToLook *p); + + typedef struct { - ISeqInStream s; - ILookInStream *realStream; + ISeqInStream vt; + const ILookInStream *realStream; } CSecToRead; void SecToRead_CreateVTable(CSecToRead *p); -typedef struct + +typedef struct ICompressProgress ICompressProgress; + +struct ICompressProgress { - SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize); + SRes (*Progress)(const ICompressProgress *p, UInt64 inSize, UInt64 outSize); /* Returns: result. (result != SZ_OK) means break. Value (UInt64)(Int64)-1 for size means unknown value. */ -} ICompressProgress; +}; +#define ICompressProgress_Progress(p, inSize, outSize) (p)->Progress(p, inSize, outSize) -typedef struct + + +typedef struct ISzAlloc ISzAlloc; +typedef const ISzAlloc * ISzAllocPtr; + +struct ISzAlloc { - void *(*Alloc)(void *p, size_t size); - void (*Free)(void *p, void *address); /* address can be 0 */ -} ISzAlloc; + void *(*Alloc)(ISzAllocPtr p, size_t size); + void (*Free)(ISzAllocPtr p, void *address); /* address can be 0 */ +}; + +#define ISzAlloc_Alloc(p, size) (p)->Alloc(p, size) +#define ISzAlloc_Free(p, a) (p)->Free(p, a) + +/* deprecated */ +#define IAlloc_Alloc(p, size) ISzAlloc_Alloc(p, size) +#define IAlloc_Free(p, a) ISzAlloc_Free(p, a) + + + + + +#ifndef MY_offsetof + #ifdef offsetof + #define MY_offsetof(type, m) offsetof(type, m) + /* + #define MY_offsetof(type, m) FIELD_OFFSET(type, m) + */ + #else + #define MY_offsetof(type, m) ((size_t)&(((type *)0)->m)) + #endif +#endif + + + +#ifndef MY_container_of + +/* +#define MY_container_of(ptr, type, m) container_of(ptr, type, m) +#define MY_container_of(ptr, type, m) CONTAINING_RECORD(ptr, type, m) +#define MY_container_of(ptr, type, m) ((type *)((char *)(ptr) - offsetof(type, m))) +#define MY_container_of(ptr, type, m) (&((type *)0)->m == (ptr), ((type *)(((char *)(ptr)) - MY_offsetof(type, m)))) +*/ + +/* + GCC shows warning: "perhaps the 'offsetof' macro was used incorrectly" + GCC 3.4.4 : classes with constructor + GCC 4.8.1 : classes with non-public variable members" +*/ + +#define MY_container_of(ptr, type, m) ((type *)((char *)(1 ? (ptr) : &((type *)0)->m) - MY_offsetof(type, m))) + + +#endif + +#define CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) ((type *)(ptr)) + +/* +#define CONTAINER_FROM_VTBL(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) +*/ +#define CONTAINER_FROM_VTBL(ptr, type, m) MY_container_of(ptr, type, m) + +#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL_SIMPLE(ptr, type, m) +/* +#define CONTAINER_FROM_VTBL_CLS(ptr, type, m) CONTAINER_FROM_VTBL(ptr, type, m) +*/ + -#define IAlloc_Alloc(p, size) (p)->Alloc((p), size) -#define IAlloc_Free(p, a) (p)->Free((p), a) #ifdef _WIN32 diff -Nru leanify-0.4.3/lib/LZMA/Alloc.c leanify-0.4.3+git20181014/lib/LZMA/Alloc.c --- leanify-0.4.3/lib/LZMA/Alloc.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/Alloc.c 2018-07-03 15:36:27.000000000 +0000 @@ -1,8 +1,10 @@ /* Alloc.c -- Memory allocation functions -2015-02-21 : Igor Pavlov : Public domain */ +2018-04-27 : Igor Pavlov : Public domain */ #include "Precomp.h" +#include + #ifdef _WIN32 #include #endif @@ -14,20 +16,127 @@ /* use _SZ_ALLOC_DEBUG to debug alloc/free operations */ #ifdef _SZ_ALLOC_DEBUG + #include int g_allocCount = 0; int g_allocCountMid = 0; int g_allocCountBig = 0; + + +#define CONVERT_INT_TO_STR(charType, tempSize) \ + unsigned char temp[tempSize]; unsigned i = 0; \ + while (val >= 10) { temp[i++] = (unsigned char)('0' + (unsigned)(val % 10)); val /= 10; } \ + *s++ = (charType)('0' + (unsigned)val); \ + while (i != 0) { i--; *s++ = temp[i]; } \ + *s = 0; + +static void ConvertUInt64ToString(UInt64 val, char *s) +{ + CONVERT_INT_TO_STR(char, 24); +} + +#define GET_HEX_CHAR(t) ((char)(((t < 10) ? ('0' + t) : ('A' + (t - 10))))) + +static void ConvertUInt64ToHex(UInt64 val, char *s) +{ + UInt64 v = val; + unsigned i; + for (i = 1;; i++) + { + v >>= 4; + if (v == 0) + break; + } + s[i] = 0; + do + { + unsigned t = (unsigned)(val & 0xF); + val >>= 4; + s[--i] = GET_HEX_CHAR(t); + } + while (i); +} + +#define DEBUG_OUT_STREAM stderr + +static void Print(const char *s) +{ + fputs(s, DEBUG_OUT_STREAM); +} + +static void PrintAligned(const char *s, size_t align) +{ + size_t len = strlen(s); + for(;;) + { + fputc(' ', DEBUG_OUT_STREAM); + if (len >= align) + break; + ++len; + } + Print(s); +} + +static void PrintLn() +{ + Print("\n"); +} + +static void PrintHex(UInt64 v, size_t align) +{ + char s[32]; + ConvertUInt64ToHex(v, s); + PrintAligned(s, align); +} + +static void PrintDec(UInt64 v, size_t align) +{ + char s[32]; + ConvertUInt64ToString(v, s); + PrintAligned(s, align); +} + +static void PrintAddr(void *p) +{ + PrintHex((UInt64)(size_t)(ptrdiff_t)p, 12); +} + + +#define PRINT_ALLOC(name, cnt, size, ptr) \ + Print(name " "); \ + PrintDec(cnt++, 10); \ + PrintHex(size, 10); \ + PrintAddr(ptr); \ + PrintLn(); + +#define PRINT_FREE(name, cnt, ptr) if (ptr) { \ + Print(name " "); \ + PrintDec(--cnt, 10); \ + PrintAddr(ptr); \ + PrintLn(); } + +#else + +#define PRINT_ALLOC(name, cnt, size, ptr) +#define PRINT_FREE(name, cnt, ptr) +#define Print(s) +#define PrintLn() +#define PrintHex(v, align) +#define PrintDec(v, align) +#define PrintAddr(p) + #endif + + void *MyAlloc(size_t size) { if (size == 0) - return 0; + return NULL; #ifdef _SZ_ALLOC_DEBUG { void *p = malloc(size); - fprintf(stderr, "\nAlloc %10d bytes, count = %10d, addr = %8X", size, g_allocCount++, (unsigned)p); + PRINT_ALLOC("Alloc ", g_allocCount, size, p); return p; } #else @@ -37,10 +146,8 @@ void MyFree(void *address) { - #ifdef _SZ_ALLOC_DEBUG - if (address != 0) - fprintf(stderr, "\nFree; count = %10d, addr = %8X", --g_allocCount, (unsigned)address); - #endif + PRINT_FREE("Free ", g_allocCount, address); + free(address); } @@ -49,20 +156,18 @@ void *MidAlloc(size_t size) { if (size == 0) - return 0; - #ifdef _SZ_ALLOC_DEBUG - fprintf(stderr, "\nAlloc_Mid %10d bytes; count = %10d", size, g_allocCountMid++); - #endif - return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); + return NULL; + + PRINT_ALLOC("Alloc-Mid", g_allocCountMid, size, NULL); + + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); } void MidFree(void *address) { - #ifdef _SZ_ALLOC_DEBUG - if (address != 0) - fprintf(stderr, "\nFree_Mid; count = %10d", --g_allocCountMid); - #endif - if (address == 0) + PRINT_FREE("Free-Mid", g_allocCountMid, address); + + if (!address) return; VirtualFree(address, 0, MEM_RELEASE); } @@ -79,10 +184,10 @@ void SetLargePageSize() { #ifdef _7ZIP_LARGE_PAGES - SIZE_T size = 0; + SIZE_T size; GetLargePageMinimumP largePageMinimum = (GetLargePageMinimumP) GetProcAddress(GetModuleHandle(TEXT("kernel32.dll")), "GetLargePageMinimum"); - if (largePageMinimum == 0) + if (!largePageMinimum) return; size = largePageMinimum(); if (size == 0 || (size & (size - 1)) != 0) @@ -95,31 +200,36 @@ void *BigAlloc(size_t size) { if (size == 0) - return 0; - #ifdef _SZ_ALLOC_DEBUG - fprintf(stderr, "\nAlloc_Big %10d bytes; count = %10d", size, g_allocCountBig++); - #endif + return NULL; + + PRINT_ALLOC("Alloc-Big", g_allocCountBig, size, NULL); #ifdef _7ZIP_LARGE_PAGES - if (g_LargePageSize != 0 && g_LargePageSize <= (1 << 30) && size >= (1 << 18)) { - void *res = VirtualAlloc(0, (size + g_LargePageSize - 1) & (~(g_LargePageSize - 1)), - MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); - if (res != 0) - return res; + SIZE_T ps = g_LargePageSize; + if (ps != 0 && ps <= (1 << 30) && size > (ps / 2)) + { + size_t size2; + ps--; + size2 = (size + ps) & ~ps; + if (size2 >= size) + { + void *res = VirtualAlloc(NULL, size2, MEM_COMMIT | MEM_LARGE_PAGES, PAGE_READWRITE); + if (res) + return res; + } + } } #endif - return VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); + + return VirtualAlloc(NULL, size, MEM_COMMIT, PAGE_READWRITE); } void BigFree(void *address) { - #ifdef _SZ_ALLOC_DEBUG - if (address != 0) - fprintf(stderr, "\nFree_Big; count = %10d", --g_allocCountBig); - #endif + PRINT_FREE("Free-Big", g_allocCountBig, address); - if (address == 0) + if (!address) return; VirtualFree(address, 0, MEM_RELEASE); } @@ -127,10 +237,219 @@ #endif -static void *SzAlloc(void *p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); } -static void SzFree(void *p, void *address) { UNUSED_VAR(p); MyFree(address); } -ISzAlloc g_Alloc = { SzAlloc, SzFree }; - -static void *SzBigAlloc(void *p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); } -static void SzBigFree(void *p, void *address) { UNUSED_VAR(p); BigFree(address); } -ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; +static void *SzAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MyAlloc(size); } +static void SzFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MyFree(address); } +const ISzAlloc g_Alloc = { SzAlloc, SzFree }; + +static void *SzMidAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return MidAlloc(size); } +static void SzMidFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); MidFree(address); } +const ISzAlloc g_MidAlloc = { SzMidAlloc, SzMidFree }; + +static void *SzBigAlloc(ISzAllocPtr p, size_t size) { UNUSED_VAR(p); return BigAlloc(size); } +static void SzBigFree(ISzAllocPtr p, void *address) { UNUSED_VAR(p); BigFree(address); } +const ISzAlloc g_BigAlloc = { SzBigAlloc, SzBigFree }; + + +/* + uintptr_t : C99 (optional) + : unsupported in VS6 +*/ + +#ifdef _WIN32 + typedef UINT_PTR UIntPtr; +#else + /* + typedef uintptr_t UIntPtr; + */ + typedef ptrdiff_t UIntPtr; +#endif + + +#define ADJUST_ALLOC_SIZE 0 +/* +#define ADJUST_ALLOC_SIZE (sizeof(void *) - 1) +*/ +/* + Use (ADJUST_ALLOC_SIZE = (sizeof(void *) - 1)), if + MyAlloc() can return address that is NOT multiple of sizeof(void *). +*/ + + +/* +#define MY_ALIGN_PTR_DOWN(p, align) ((void *)((char *)(p) - ((size_t)(UIntPtr)(p) & ((align) - 1)))) +*/ +#define MY_ALIGN_PTR_DOWN(p, align) ((void *)((((UIntPtr)(p)) & ~((UIntPtr)(align) - 1)))) + +#define MY_ALIGN_PTR_UP_PLUS(p, align) MY_ALIGN_PTR_DOWN(((char *)(p) + (align) + ADJUST_ALLOC_SIZE), align) + + +#if (_POSIX_C_SOURCE >= 200112L) && !defined(_WIN32) + #define USE_posix_memalign +#endif + +/* + This posix_memalign() is for test purposes only. + We also need special Free() function instead of free(), + if this posix_memalign() is used. +*/ + +/* +static int posix_memalign(void **ptr, size_t align, size_t size) +{ + size_t newSize = size + align; + void *p; + void *pAligned; + *ptr = NULL; + if (newSize < size) + return 12; // ENOMEM + p = MyAlloc(newSize); + if (!p) + return 12; // ENOMEM + pAligned = MY_ALIGN_PTR_UP_PLUS(p, align); + ((void **)pAligned)[-1] = p; + *ptr = pAligned; + return 0; +} +*/ + +/* + ALLOC_ALIGN_SIZE >= sizeof(void *) + ALLOC_ALIGN_SIZE >= cache_line_size +*/ + +#define ALLOC_ALIGN_SIZE ((size_t)1 << 7) + +static void *SzAlignedAlloc(ISzAllocPtr pp, size_t size) +{ + #ifndef USE_posix_memalign + + void *p; + void *pAligned; + size_t newSize; + UNUSED_VAR(pp); + + /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned + block to prevent cache line sharing with another allocated blocks */ + + newSize = size + ALLOC_ALIGN_SIZE * 1 + ADJUST_ALLOC_SIZE; + if (newSize < size) + return NULL; + + p = MyAlloc(newSize); + + if (!p) + return NULL; + pAligned = MY_ALIGN_PTR_UP_PLUS(p, ALLOC_ALIGN_SIZE); + + Print(" size="); PrintHex(size, 8); + Print(" a_size="); PrintHex(newSize, 8); + Print(" ptr="); PrintAddr(p); + Print(" a_ptr="); PrintAddr(pAligned); + PrintLn(); + + ((void **)pAligned)[-1] = p; + + return pAligned; + + #else + + void *p; + UNUSED_VAR(pp); + if (posix_memalign(&p, ALLOC_ALIGN_SIZE, size)) + return NULL; + + Print(" posix_memalign="); PrintAddr(p); + PrintLn(); + + return p; + + #endif +} + + +static void SzAlignedFree(ISzAllocPtr pp, void *address) +{ + UNUSED_VAR(pp); + #ifndef USE_posix_memalign + if (address) + MyFree(((void **)address)[-1]); + #else + free(address); + #endif +} + + +const ISzAlloc g_AlignedAlloc = { SzAlignedAlloc, SzAlignedFree }; + + + +#define MY_ALIGN_PTR_DOWN_1(p) MY_ALIGN_PTR_DOWN(p, sizeof(void *)) + +/* we align ptr to support cases where CAlignOffsetAlloc::offset is not multiply of sizeof(void *) */ +#define REAL_BLOCK_PTR_VAR(p) ((void **)MY_ALIGN_PTR_DOWN_1(p))[-1] +/* +#define REAL_BLOCK_PTR_VAR(p) ((void **)(p))[-1] +*/ + +static void *AlignOffsetAlloc_Alloc(ISzAllocPtr pp, size_t size) +{ + CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt); + void *adr; + void *pAligned; + size_t newSize; + size_t extra; + size_t alignSize = (size_t)1 << p->numAlignBits; + + if (alignSize < sizeof(void *)) + alignSize = sizeof(void *); + + if (p->offset >= alignSize) + return NULL; + + /* also we can allocate additional dummy ALLOC_ALIGN_SIZE bytes after aligned + block to prevent cache line sharing with another allocated blocks */ + extra = p->offset & (sizeof(void *) - 1); + newSize = size + alignSize + extra + ADJUST_ALLOC_SIZE; + if (newSize < size) + return NULL; + + adr = ISzAlloc_Alloc(p->baseAlloc, newSize); + + if (!adr) + return NULL; + + pAligned = (char *)MY_ALIGN_PTR_DOWN((char *)adr + + alignSize - p->offset + extra + ADJUST_ALLOC_SIZE, alignSize) + p->offset; + + PrintLn(); + Print("- Aligned: "); + Print(" size="); PrintHex(size, 8); + Print(" a_size="); PrintHex(newSize, 8); + Print(" ptr="); PrintAddr(adr); + Print(" a_ptr="); PrintAddr(pAligned); + PrintLn(); + + REAL_BLOCK_PTR_VAR(pAligned) = adr; + + return pAligned; +} + + +static void AlignOffsetAlloc_Free(ISzAllocPtr pp, void *address) +{ + if (address) + { + CAlignOffsetAlloc *p = CONTAINER_FROM_VTBL(pp, CAlignOffsetAlloc, vt); + PrintLn(); + Print("- Aligned Free: "); + PrintLn(); + ISzAlloc_Free(p->baseAlloc, REAL_BLOCK_PTR_VAR(address)); + } +} + + +void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p) +{ + p->vt.Alloc = AlignOffsetAlloc_Alloc; + p->vt.Free = AlignOffsetAlloc_Free; +} diff -Nru leanify-0.4.3/lib/LZMA/Alloc.h leanify-0.4.3+git20181014/lib/LZMA/Alloc.h --- leanify-0.4.3/lib/LZMA/Alloc.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/Alloc.h 2018-07-03 15:36:27.000000000 +0000 @@ -1,5 +1,5 @@ /* Alloc.h -- Memory allocation functions -2015-02-21 : Igor Pavlov : Public domain */ +2018-02-19 : Igor Pavlov : Public domain */ #ifndef __COMMON_ALLOC_H #define __COMMON_ALLOC_H @@ -29,8 +29,22 @@ #endif -extern ISzAlloc g_Alloc; -extern ISzAlloc g_BigAlloc; +extern const ISzAlloc g_Alloc; +extern const ISzAlloc g_BigAlloc; +extern const ISzAlloc g_MidAlloc; +extern const ISzAlloc g_AlignedAlloc; + + +typedef struct +{ + ISzAlloc vt; + ISzAllocPtr baseAlloc; + unsigned numAlignBits; /* ((1 << numAlignBits) >= sizeof(void *)) */ + size_t offset; /* (offset == (k * sizeof(void *)) && offset < (1 << numAlignBits) */ +} CAlignOffsetAlloc; + +void AlignOffsetAlloc_CreateVTable(CAlignOffsetAlloc *p); + EXTERN_C_END diff -Nru leanify-0.4.3/lib/LZMA/Compiler.h leanify-0.4.3+git20181014/lib/LZMA/Compiler.h --- leanify-0.4.3/lib/LZMA/Compiler.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/Compiler.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* Compiler.h -2015-08-02 : Igor Pavlov : Public domain */ +2017-04-03 : Igor Pavlov : Public domain */ #ifndef __7Z_COMPILER_H #define __7Z_COMPILER_H @@ -21,6 +21,7 @@ #pragma warning(disable : 4514) // unreferenced inline function has been removed #pragma warning(disable : 4702) // unreachable code #pragma warning(disable : 4710) // not inlined + #pragma warning(disable : 4714) // function marked as __forceinline not inlined #pragma warning(disable : 4786) // identifier was truncated to '255' characters in the debug information #endif diff -Nru leanify-0.4.3/lib/LZMA/LzFind.c leanify-0.4.3+git20181014/lib/LZMA/LzFind.c --- leanify-0.4.3/lib/LZMA/LzFind.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzFind.c 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFind.c -- Match finder for LZ algorithms -2015-10-15 : Igor Pavlov : Public domain */ +2017-06-10 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -16,18 +16,18 @@ #define kStartMaxLen 3 -static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc) +static void LzInWindow_Free(CMatchFinder *p, ISzAllocPtr alloc) { if (!p->directInput) { - alloc->Free(alloc, p->bufferBase); + ISzAlloc_Free(alloc, p->bufferBase); p->bufferBase = NULL; } } /* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */ -static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc) +static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAllocPtr alloc) { UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv; if (p->directInput) @@ -39,7 +39,7 @@ { LzInWindow_Free(p, alloc); p->blockSize = blockSize; - p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize); + p->bufferBase = (Byte *)ISzAlloc_Alloc(alloc, (size_t)blockSize); } return (p->bufferBase != NULL); } @@ -81,7 +81,7 @@ if (size == 0) return; - p->result = p->stream->Read(p->stream, dest, &size); + p->result = ISeqInStream_Read(p->stream, dest, &size); if (p->result != SZ_OK) return; if (size == 0) @@ -142,6 +142,7 @@ p->bufferBase = NULL; p->directInput = 0; p->hash = NULL; + p->expectedDataSize = (UInt64)(Int64)-1; MatchFinder_SetDefaultSettings(p); for (i = 0; i < 256; i++) @@ -149,34 +150,34 @@ UInt32 r = i; unsigned j; for (j = 0; j < 8; j++) - r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1)); + r = (r >> 1) ^ (kCrcPoly & ((UInt32)0 - (r & 1))); p->crc[i] = r; } } -static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc) +static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->hash); + ISzAlloc_Free(alloc, p->hash); p->hash = NULL; } -void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc) +void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc) { MatchFinder_FreeThisClassMemory(p, alloc); LzInWindow_Free(p, alloc); } -static CLzRef* AllocRefs(size_t num, ISzAlloc *alloc) +static CLzRef* AllocRefs(size_t num, ISzAllocPtr alloc) { size_t sizeInBytes = (size_t)num * sizeof(CLzRef); if (sizeInBytes / sizeof(CLzRef) != num) return NULL; - return (CLzRef *)alloc->Alloc(alloc, sizeInBytes); + return (CLzRef *)ISzAlloc_Alloc(alloc, sizeInBytes); } int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, - ISzAlloc *alloc) + ISzAllocPtr alloc) { UInt32 sizeReserv; @@ -208,7 +209,11 @@ hs = (1 << 16) - 1; else { - hs = historySize - 1; + hs = historySize; + if (hs > p->expectedDataSize) + hs = (UInt32)p->expectedDataSize; + if (hs != 0) + hs--; hs |= (hs >> 1); hs |= (hs >> 2); hs |= (hs >> 4); @@ -292,17 +297,33 @@ p->posLimit = p->pos + limit; } -void MatchFinder_Init_2(CMatchFinder *p, int readData) + +void MatchFinder_Init_LowHash(CMatchFinder *p) +{ + size_t i; + CLzRef *items = p->hash; + size_t numItems = p->fixedHashSize; + for (i = 0; i < numItems; i++) + items[i] = kEmptyHashValue; +} + + +void MatchFinder_Init_HighHash(CMatchFinder *p) +{ + size_t i; + CLzRef *items = p->hash + p->fixedHashSize; + size_t numItems = (size_t)p->hashMask + 1; + for (i = 0; i < numItems; i++) + items[i] = kEmptyHashValue; +} + + +void MatchFinder_Init_3(CMatchFinder *p, int readData) { - UInt32 i; - UInt32 *hash = p->hash; - UInt32 num = p->hashSizeSum; - for (i = 0; i < num; i++) - hash[i] = kEmptyHashValue; - p->cyclicBufferPos = 0; p->buffer = p->bufferBase; - p->pos = p->streamPos = p->cyclicBufferSize; + p->pos = + p->streamPos = p->cyclicBufferSize; p->result = SZ_OK; p->streamEndWasReached = 0; @@ -312,10 +333,14 @@ MatchFinder_SetLimits(p); } + void MatchFinder_Init(CMatchFinder *p) { - MatchFinder_Init_2(p, True); + MatchFinder_Init_HighHash(p); + MatchFinder_Init_LowHash(p); + MatchFinder_Init_3(p, True); } + static UInt32 MatchFinder_GetSubValue(CMatchFinder *p) { @@ -558,10 +583,10 @@ d2 = pos - hash[h2]; - curMatch = hash[kFix3HashSize + hv]; + curMatch = (hash + kFix3HashSize)[hv]; hash[h2] = pos; - hash[kFix3HashSize + hv] = pos; + (hash + kFix3HashSize)[hv] = pos; maxLen = 2; offset = 0; @@ -594,13 +619,13 @@ pos = p->pos; d2 = pos - hash[ h2]; - d3 = pos - hash[kFix3HashSize + h3]; + d3 = pos - (hash + kFix3HashSize)[h3]; - curMatch = hash[kFix4HashSize + hv]; + curMatch = (hash + kFix4HashSize)[hv]; hash[ h2] = pos; - hash[kFix3HashSize + h3] = pos; - hash[kFix4HashSize + hv] = pos; + (hash + kFix3HashSize)[h3] = pos; + (hash + kFix4HashSize)[hv] = pos; maxLen = 0; offset = 0; @@ -615,7 +640,7 @@ if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) { maxLen = 3; - distances[offset + 1] = d3 - 1; + distances[(size_t)offset + 1] = d3 - 1; offset += 2; d2 = d3; } @@ -623,7 +648,7 @@ if (offset != 0) { UPDATE_maxLen - distances[offset - 2] = maxLen; + distances[(size_t)offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); @@ -650,15 +675,15 @@ pos = p->pos; d2 = pos - hash[ h2]; - d3 = pos - hash[kFix3HashSize + h3]; - d4 = pos - hash[kFix4HashSize + h4]; + d3 = pos - (hash + kFix3HashSize)[h3]; + d4 = pos - (hash + kFix4HashSize)[h4]; - curMatch = hash[kFix5HashSize + hv]; + curMatch = (hash + kFix5HashSize)[hv]; hash[ h2] = pos; - hash[kFix3HashSize + h3] = pos; - hash[kFix4HashSize + h4] = pos; - hash[kFix5HashSize + hv] = pos; + (hash + kFix3HashSize)[h3] = pos; + (hash + kFix4HashSize)[h4] = pos; + (hash + kFix5HashSize)[hv] = pos; maxLen = 0; offset = 0; @@ -691,7 +716,7 @@ && *(cur - d4 + 3) == *(cur + 3)) { maxLen = 4; - distances[offset + 1] = d4 - 1; + distances[(size_t)offset + 1] = d4 - 1; offset += 2; d2 = d4; } @@ -699,7 +724,7 @@ if (offset != 0) { UPDATE_maxLen - distances[offset - 2] = maxLen; + distances[(size_t)offset - 2] = maxLen; if (maxLen == lenLimit) { SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); @@ -726,13 +751,13 @@ pos = p->pos; d2 = pos - hash[ h2]; - d3 = pos - hash[kFix3HashSize + h3]; + d3 = pos - (hash + kFix3HashSize)[h3]; - curMatch = hash[kFix4HashSize + hv]; + curMatch = (hash + kFix4HashSize)[hv]; hash[ h2] = pos; - hash[kFix3HashSize + h3] = pos; - hash[kFix4HashSize + hv] = pos; + (hash + kFix3HashSize)[h3] = pos; + (hash + kFix4HashSize)[hv] = pos; maxLen = 0; offset = 0; @@ -747,7 +772,7 @@ if (d2 != d3 && d3 < p->cyclicBufferSize && *(cur - d3) == *cur) { maxLen = 3; - distances[offset + 1] = d3 - 1; + distances[(size_t)offset + 1] = d3 - 1; offset += 2; d2 = d3; } @@ -755,7 +780,7 @@ if (offset != 0) { UPDATE_maxLen - distances[offset - 2] = maxLen; + distances[(size_t)offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; @@ -784,15 +809,15 @@ pos = p->pos; d2 = pos - hash[ h2]; - d3 = pos - hash[kFix3HashSize + h3]; - d4 = pos - hash[kFix4HashSize + h4]; + d3 = pos - (hash + kFix3HashSize)[h3]; + d4 = pos - (hash + kFix4HashSize)[h4]; - curMatch = hash[kFix5HashSize + hv]; + curMatch = (hash + kFix5HashSize)[hv]; hash[ h2] = pos; - hash[kFix3HashSize + h3] = pos; - hash[kFix4HashSize + h4] = pos; - hash[kFix5HashSize + hv] = pos; + (hash + kFix3HashSize)[h3] = pos; + (hash + kFix4HashSize)[h4] = pos; + (hash + kFix5HashSize)[hv] = pos; maxLen = 0; offset = 0; @@ -825,7 +850,7 @@ && *(cur - d4 + 3) == *(cur + 3)) { maxLen = 4; - distances[offset + 1] = d4 - 1; + distances[(size_t)offset + 1] = d4 - 1; offset += 2; d2 = d4; } @@ -833,7 +858,7 @@ if (offset != 0) { UPDATE_maxLen - distances[offset - 2] = maxLen; + distances[(size_t)offset - 2] = maxLen; if (maxLen == lenLimit) { p->son[p->cyclicBufferPos] = curMatch; @@ -897,9 +922,9 @@ SKIP_HEADER(3) HASH3_CALC; hash = p->hash; - curMatch = hash[kFix3HashSize + hv]; + curMatch = (hash + kFix3HashSize)[hv]; hash[h2] = - hash[kFix3HashSize + hv] = p->pos; + (hash + kFix3HashSize)[hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -914,10 +939,10 @@ SKIP_HEADER(4) HASH4_CALC; hash = p->hash; - curMatch = hash[kFix4HashSize + hv]; + curMatch = (hash + kFix4HashSize)[hv]; hash[ h2] = - hash[kFix3HashSize + h3] = - hash[kFix4HashSize + hv] = p->pos; + (hash + kFix3HashSize)[h3] = + (hash + kFix4HashSize)[hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -933,11 +958,11 @@ SKIP_HEADER(5) HASH5_CALC; hash = p->hash; - curMatch = hash[kFix5HashSize + hv]; + curMatch = (hash + kFix5HashSize)[hv]; hash[ h2] = - hash[kFix3HashSize + h3] = - hash[kFix4HashSize + h4] = - hash[kFix5HashSize + hv] = p->pos; + (hash + kFix3HashSize)[h3] = + (hash + kFix4HashSize)[h4] = + (hash + kFix5HashSize)[hv] = p->pos; SKIP_FOOTER } while (--num != 0); @@ -953,10 +978,10 @@ SKIP_HEADER(4) HASH4_CALC; hash = p->hash; - curMatch = hash[kFix4HashSize + hv]; + curMatch = (hash + kFix4HashSize)[hv]; hash[ h2] = - hash[kFix3HashSize + h3] = - hash[kFix4HashSize + hv] = p->pos; + (hash + kFix3HashSize)[h3] = + (hash + kFix4HashSize)[hv] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } @@ -973,11 +998,11 @@ SKIP_HEADER(5) HASH5_CALC; hash = p->hash; - curMatch = p->hash[kFix5HashSize + hv]; + curMatch = hash + kFix5HashSize)[hv]; hash[ h2] = - hash[kFix3HashSize + h3] = - hash[kFix4HashSize + h4] = - hash[kFix5HashSize + hv] = p->pos; + (hash + kFix3HashSize)[h3] = + (hash + kFix4HashSize)[h4] = + (hash + kFix5HashSize)[hv] = p->pos; p->son[p->cyclicBufferPos] = curMatch; MOVE_POS } diff -Nru leanify-0.4.3/lib/LZMA/LzFind.h leanify-0.4.3+git20181014/lib/LZMA/LzFind.h --- leanify-0.4.3/lib/LZMA/LzFind.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzFind.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFind.h -- Match finder for LZ algorithms -2015-10-15 : Igor Pavlov : Public domain */ +2017-06-10 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_H #define __LZ_FIND_H @@ -47,6 +47,8 @@ SRes result; UInt32 crc[256]; size_t numRefs; + + UInt64 expectedDataSize; } CMatchFinder; #define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer) @@ -71,8 +73,8 @@ */ int MatchFinder_Create(CMatchFinder *p, UInt32 historySize, UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter, - ISzAlloc *alloc); -void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc); + ISzAllocPtr alloc); +void MatchFinder_Free(CMatchFinder *p, ISzAllocPtr alloc); void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, size_t numItems); void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue); @@ -103,7 +105,9 @@ void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable); -void MatchFinder_Init_2(CMatchFinder *p, int readData); +void MatchFinder_Init_LowHash(CMatchFinder *p); +void MatchFinder_Init_HighHash(CMatchFinder *p); +void MatchFinder_Init_3(CMatchFinder *p, int readData); void MatchFinder_Init(CMatchFinder *p); UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances); diff -Nru leanify-0.4.3/lib/LZMA/LzFindMt.c leanify-0.4.3+git20181014/lib/LZMA/LzFindMt.c --- leanify-0.4.3/lib/LZMA/LzFindMt.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzFindMt.c 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFindMt.c -- multithreaded Match finder for LZ algorithms -2015-10-15 : Igor Pavlov : Public domain */ +2017-06-10 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -33,6 +33,8 @@ Event_Set(&p->canStart); Event_Wait(&p->wasStarted); + + // if (mt) MatchFinder_Init_LowHash(mt->MatchFinder); } else { @@ -155,6 +157,9 @@ UInt32 numProcessedBlocks = 0; Event_Wait(&p->canStart); Event_Set(&p->wasStarted); + + MatchFinder_Init_HighHash(mt->MatchFinder); + for (;;) { if (p->exit) @@ -205,7 +210,7 @@ if (num > kMtHashBlockSize - 2) num = kMtHashBlockSize - 2; mt->GetHeadsFunc(mf->buffer, mf->pos, mf->hash + mf->fixedHashSize, mf->hashMask, heads + 2, num, mf->crc); - heads[0] += num; + heads[0] = 2 + num; } mf->pos += num; mf->buffer += num; @@ -443,13 +448,13 @@ MtSync_Construct(&p->btSync); } -static void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAlloc *alloc) +static void MatchFinderMt_FreeMem(CMatchFinderMt *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->hashBuf); + ISzAlloc_Free(alloc, p->hashBuf); p->hashBuf = NULL; } -void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc) +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAllocPtr alloc) { MtSync_Destruct(&p->hashSync); MtSync_Destruct(&p->btSync); @@ -472,7 +477,7 @@ } SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, - UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc) + UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAllocPtr alloc) { CMatchFinder *mf = p->MatchFinder; p->historySize = historySize; @@ -480,7 +485,7 @@ return SZ_ERROR_PARAM; if (!p->hashBuf) { - p->hashBuf = (UInt32 *)alloc->Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); + p->hashBuf = (UInt32 *)ISzAlloc_Alloc(alloc, (kHashBufferSize + kBtBufferSize) * sizeof(UInt32)); if (!p->hashBuf) return SZ_ERROR_MEM; p->btBuf = p->hashBuf + kHashBufferSize; @@ -496,14 +501,18 @@ } /* Call it after ReleaseStream / SetStream */ -void MatchFinderMt_Init(CMatchFinderMt *p) +static void MatchFinderMt_Init(CMatchFinderMt *p) { CMatchFinder *mf = p->MatchFinder; - p->btBufPos = p->btBufPosLimit = 0; - p->hashBufPos = p->hashBufPosLimit = 0; + + p->btBufPos = + p->btBufPosLimit = 0; + p->hashBufPos = + p->hashBufPosLimit = 0; /* Init without data reading. We don't want to read data in this thread */ - MatchFinder_Init_2(mf, False); + MatchFinder_Init_3(mf, False); + MatchFinder_Init_LowHash(mf); p->pointerToCurPos = Inline_MatchFinder_GetPointerToCurrentPos(mf); p->btNumAvailBytes = 0; @@ -591,10 +600,10 @@ MT_HASH3_CALC curMatch2 = hash[ h2]; - curMatch3 = hash[kFix3HashSize + h3]; + curMatch3 = (hash + kFix3HashSize)[h3]; hash[ h2] = lzPos; - hash[kFix3HashSize + h3] = lzPos; + (hash + kFix3HashSize)[h3] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { @@ -627,12 +636,12 @@ MT_HASH4_CALC curMatch2 = hash[ h2]; - curMatch3 = hash[kFix3HashSize + h3]; - curMatch4 = hash[kFix4HashSize + h4]; + curMatch3 = (hash + kFix3HashSize)[h3]; + curMatch4 = (hash + kFix4HashSize)[h4]; hash[ h2] = lzPos; - hash[kFix3HashSize + h3] = lzPos; - hash[kFix4HashSize + h4] = lzPos; + (hash + kFix3HashSize)[h3] = lzPos; + (hash + kFix4HashSize)[h4] = lzPos; if (curMatch2 >= matchMinPos && cur[(ptrdiff_t)curMatch2 - lzPos] == cur[0]) { @@ -684,8 +693,12 @@ UInt32 i; for (i = 0; i < len; i += 2) { - *distances++ = *btBuf++; - *distances++ = *btBuf++; + UInt32 v0 = btBuf[0]; + UInt32 v1 = btBuf[1]; + btBuf += 2; + distances[0] = v0; + distances[1] = v1; + distances += 2; } } INCREASE_LZ_POS @@ -712,8 +725,12 @@ distances2 = p->MixMatchesFunc(p, p->lzPos - btBuf[1], distances); do { - *distances2++ = *btBuf++; - *distances2++ = *btBuf++; + UInt32 v0 = btBuf[0]; + UInt32 v1 = btBuf[1]; + btBuf += 2; + distances2[0] = v0; + distances2[1] = v1; + distances2 += 2; } while ((len -= 2) != 0); len = (UInt32)(distances2 - (distances)); @@ -746,7 +763,7 @@ SKIP_HEADER_MT(3) UInt32 h2, h3; MT_HASH3_CALC - hash[kFix3HashSize + h3] = + (hash + kFix3HashSize)[h3] = hash[ h2] = p->lzPos; SKIP_FOOTER_MT @@ -758,8 +775,8 @@ SKIP_HEADER_MT(4) UInt32 h2, h3, h4; MT_HASH4_CALC - hash[kFix4HashSize + h4] = - hash[kFix3HashSize + h3] = + (hash + kFix4HashSize)[h4] = + (hash + kFix3HashSize)[h3] = hash[ h2] = p->lzPos; SKIP_FOOTER_MT @@ -777,7 +794,7 @@ { case 2: p->GetHeadsFunc = GetHeads2; - p->MixMatchesFunc = (Mf_Mix_Matches)0; + p->MixMatchesFunc = (Mf_Mix_Matches)NULL; vTable->Skip = (Mf_Skip_Func)MatchFinderMt0_Skip; vTable->GetMatches = (Mf_GetMatches_Func)MatchFinderMt2_GetMatches; break; diff -Nru leanify-0.4.3/lib/LZMA/LzFindMt.h leanify-0.4.3+git20181014/lib/LZMA/LzFindMt.h --- leanify-0.4.3/lib/LZMA/LzFindMt.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzFindMt.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* LzFindMt.h -- multithreaded Match finder for LZ algorithms -2015-05-03 : Igor Pavlov : Public domain */ +2017-04-03 : Igor Pavlov : Public domain */ #ifndef __LZ_FIND_MT_H #define __LZ_FIND_MT_H @@ -90,9 +90,9 @@ } CMatchFinderMt; void MatchFinderMt_Construct(CMatchFinderMt *p); -void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAlloc *alloc); +void MatchFinderMt_Destruct(CMatchFinderMt *p, ISzAllocPtr alloc); SRes MatchFinderMt_Create(CMatchFinderMt *p, UInt32 historySize, UInt32 keepAddBufferBefore, - UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAlloc *alloc); + UInt32 matchMaxLen, UInt32 keepAddBufferAfter, ISzAllocPtr alloc); void MatchFinderMt_CreateVTable(CMatchFinderMt *p, IMatchFinder *vTable); void MatchFinderMt_ReleaseStream(CMatchFinderMt *p); diff -Nru leanify-0.4.3/lib/LZMA/LzmaDec.c leanify-0.4.3+git20181014/lib/LZMA/LzmaDec.c --- leanify-0.4.3/lib/LZMA/LzmaDec.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaDec.c 2018-07-03 15:36:27.000000000 +0000 @@ -1,8 +1,9 @@ /* LzmaDec.c -- LZMA Decoder -2015-06-23 : Igor Pavlov : Public domain */ +2018-02-28 : Igor Pavlov : Public domain */ #include "Precomp.h" +/* #include "CpuArch.h" */ #include "LzmaDec.h" #include @@ -24,9 +25,16 @@ #define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \ { UPDATE_0(p); i = (i + i); A0; } else \ { UPDATE_1(p); i = (i + i) + 1; A1; } -#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;) -#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); } +#define TREE_GET_BIT(probs, i) { GET_BIT2(probs + i, i, ;, ;); } + +#define REV_BIT(p, i, A0, A1) IF_BIT_0(p + i) \ + { UPDATE_0(p + i); A0; } else \ + { UPDATE_1(p + i); A1; } +#define REV_BIT_VAR( p, i, m) REV_BIT(p, i, i += m; m += m, m += m; i += m; ) +#define REV_BIT_CONST(p, i, m) REV_BIT(p, i, i += m; , i += m * 2; ) +#define REV_BIT_LAST( p, i, m) REV_BIT(p, i, i -= m , ; ) + #define TREE_DECODE(probs, limit, i) \ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; } @@ -46,12 +54,15 @@ i -= 0x40; } #endif -#define NORMAL_LITER_DEC GET_BIT(prob + symbol, symbol) +#define NORMAL_LITER_DEC TREE_GET_BIT(prob, symbol) #define MATCHED_LITER_DEC \ - matchByte <<= 1; \ - bit = (matchByte & offs); \ - probLit = prob + offs + bit + symbol; \ - GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit) + matchByte += matchByte; \ + bit = offs; \ + offs &= matchByte; \ + probLit = prob + (offs + bit + symbol); \ + GET_BIT2(probLit, symbol, offs ^= bit; , ;) + + #define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); } @@ -66,25 +77,28 @@ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while (i < limit); i -= limit; } +#define REV_BIT_CHECK(p, i, m) IF_BIT_0_CHECK(p + i) \ + { UPDATE_0_CHECK; i += m; m += m; } else \ + { UPDATE_1_CHECK; m += m; i += m; } + + #define kNumPosBitsMax 4 #define kNumPosStatesMax (1 << kNumPosBitsMax) #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) -#define kLenNumMidBits 3 -#define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) -#define LenChoice 0 -#define LenChoice2 (LenChoice + 1) -#define LenLow (LenChoice2 + 1) -#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits)) -#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits)) +#define LenLow 0 +#define LenHigh (LenLow + 2 * (kNumPosStatesMax << kLenNumLowBits)) #define kNumLenProbs (LenHigh + kLenNumHighSymbols) +#define LenChoice LenLow +#define LenChoice2 (LenLow + (1 << kLenNumLowBits)) #define kNumStates 12 +#define kNumStates2 16 #define kNumLitStates 7 #define kStartPosModelIndex 4 @@ -98,54 +112,117 @@ #define kAlignTableSize (1 << kNumAlignBits) #define kMatchMinLen 2 -#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) +#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols * 2 + kLenNumHighSymbols) + +/* External ASM code needs same CLzmaProb array layout. So don't change it. */ + +/* (probs_1664) is faster and better for code size at some platforms */ +/* +#ifdef MY_CPU_X86_OR_AMD64 +*/ +#define kStartOffset 1664 +#define GET_PROBS p->probs_1664 +/* +#define GET_PROBS p->probs + kStartOffset +#else +#define kStartOffset 0 +#define GET_PROBS p->probs +#endif +*/ -#define IsMatch 0 -#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax)) +#define SpecPos (-kStartOffset) +#define IsRep0Long (SpecPos + kNumFullDistances) +#define RepLenCoder (IsRep0Long + (kNumStates2 << kNumPosBitsMax)) +#define LenCoder (RepLenCoder + kNumLenProbs) +#define IsMatch (LenCoder + kNumLenProbs) +#define Align (IsMatch + (kNumStates2 << kNumPosBitsMax)) +#define IsRep (Align + kAlignTableSize) #define IsRepG0 (IsRep + kNumStates) #define IsRepG1 (IsRepG0 + kNumStates) #define IsRepG2 (IsRepG1 + kNumStates) -#define IsRep0Long (IsRepG2 + kNumStates) -#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax)) -#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) -#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex) -#define LenCoder (Align + kAlignTableSize) -#define RepLenCoder (LenCoder + kNumLenProbs) -#define Literal (RepLenCoder + kNumLenProbs) +#define PosSlot (IsRepG2 + kNumStates) +#define Literal (PosSlot + (kNumLenToPosStates << kNumPosSlotBits)) +#define NUM_BASE_PROBS (Literal + kStartOffset) -#define LZMA_BASE_SIZE 1846 -#define LZMA_LIT_SIZE 0x300 +#if Align != 0 && kStartOffset != 0 + #error Stop_Compiling_Bad_LZMA_kAlign +#endif -#if Literal != LZMA_BASE_SIZE -StopCompilingDueBUG +#if NUM_BASE_PROBS != 1984 + #error Stop_Compiling_Bad_LZMA_PROBS #endif -#define LzmaProps_GetNumProbs(p) (Literal + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + +#define LZMA_LIT_SIZE 0x300 + +#define LzmaProps_GetNumProbs(p) (NUM_BASE_PROBS + ((UInt32)LZMA_LIT_SIZE << ((p)->lc + (p)->lp))) + + +#define CALC_POS_STATE(processedPos, pbMask) (((processedPos) & (pbMask)) << 4) +#define COMBINED_PS_STATE (posState + state) +#define GET_LEN_STATE (posState) #define LZMA_DIC_MIN (1 << 12) -/* First LZMA-symbol is always decoded. -And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization +/* +p->remainLen : shows status of LZMA decoder: + < kMatchSpecLenStart : normal remain + = kMatchSpecLenStart : finished + = kMatchSpecLenStart + 1 : need init range coder + = kMatchSpecLenStart + 2 : need init range coder and state +*/ + +/* ---------- LZMA_DECODE_REAL ---------- */ +/* +LzmaDec_DecodeReal_3() can be implemented in external ASM file. +3 - is the code compatibility version of that function for check at link time. +*/ + +#define LZMA_DECODE_REAL LzmaDec_DecodeReal_3 + +/* +LZMA_DECODE_REAL() +In: + RangeCoder is normalized + if (p->dicPos == limit) + { + LzmaDec_TryDummy() was called before to exclude LITERAL and MATCH-REP cases. + So first symbol can be only MATCH-NON-REP. And if that MATCH-NON-REP symbol + is not END_OF_PAYALOAD_MARKER, then function returns error code. + } + +Processing: + first LZMA symbol will be decoded in any case + All checks for limits are at the end of main loop, + It will decode new LZMA-symbols while (p->buf < bufLimit && dicPos < limit), + RangeCoder is still without last normalization when (p->buf < bufLimit) is being checked. + Out: + RangeCoder is normalized Result: SZ_OK - OK SZ_ERROR_DATA - Error p->remainLen: < kMatchSpecLenStart : normal remain = kMatchSpecLenStart : finished - = kMatchSpecLenStart + 1 : Flush marker (unused now) - = kMatchSpecLenStart + 2 : State Init Marker (unused now) */ -static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit) -{ - CLzmaProb *probs = p->probs; - unsigned state = p->state; +#ifdef _LZMA_DEC_OPT + +int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit); + +#else + +static +int MY_FAST_CALL LZMA_DECODE_REAL(CLzmaDec *p, SizeT limit, const Byte *bufLimit) +{ + CLzmaProb *probs = GET_PROBS; + unsigned state = (unsigned)p->state; UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3]; unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1; - unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1; unsigned lc = p->prop.lc; + unsigned lpMask = ((unsigned)0x100 << p->prop.lp) - ((unsigned)0x100 >> lc); Byte *dic = p->dic; SizeT dicBufSize = p->dicBufSize; @@ -164,17 +241,16 @@ CLzmaProb *prob; UInt32 bound; unsigned ttt; - unsigned posState = processedPos & pbMask; + unsigned posState = CALC_POS_STATE(processedPos, pbMask); - prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0(prob) { unsigned symbol; UPDATE_0(prob); prob = probs + Literal; if (processedPos != 0 || checkDicSize != 0) - prob += ((UInt32)LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) + - (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc)))); + prob += (UInt32)3 * ((((processedPos << 8) + dic[(dicPos == 0 ? dicBufSize : dicPos) - 1]) & lpMask) << lc); processedPos++; if (state < kNumLitStates) @@ -240,13 +316,16 @@ else { UPDATE_1(prob); + /* + // that case was checked before with kBadRepCode if (checkDicSize == 0 && processedPos == 0) return SZ_ERROR_DATA; + */ prob = probs + IsRepG0 + state; IF_BIT_0(prob) { UPDATE_0(prob); - prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + prob = probs + IsRep0Long + COMBINED_PS_STATE; IF_BIT_0(prob) { UPDATE_0(prob); @@ -294,14 +373,14 @@ #ifdef _LZMA_SIZE_OPT { - unsigned limit, offset; + unsigned lim, offset; CLzmaProb *probLen = prob + LenChoice; IF_BIT_0(probLen) { UPDATE_0(probLen); - probLen = prob + LenLow + (posState << kLenNumLowBits); + probLen = prob + LenLow + GET_LEN_STATE; offset = 0; - limit = (1 << kLenNumLowBits); + lim = (1 << kLenNumLowBits); } else { @@ -310,19 +389,19 @@ IF_BIT_0(probLen) { UPDATE_0(probLen); - probLen = prob + LenMid + (posState << kLenNumMidBits); + probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); offset = kLenNumLowSymbols; - limit = (1 << kLenNumMidBits); + lim = (1 << kLenNumLowBits); } else { UPDATE_1(probLen); probLen = prob + LenHigh; - offset = kLenNumLowSymbols + kLenNumMidSymbols; - limit = (1 << kLenNumHighBits); + offset = kLenNumLowSymbols * 2; + lim = (1 << kLenNumHighBits); } } - TREE_DECODE(probLen, limit, len); + TREE_DECODE(probLen, lim, len); len += offset; } #else @@ -331,7 +410,7 @@ IF_BIT_0(probLen) { UPDATE_0(probLen); - probLen = prob + LenLow + (posState << kLenNumLowBits); + probLen = prob + LenLow + GET_LEN_STATE; len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); @@ -345,7 +424,7 @@ IF_BIT_0(probLen) { UPDATE_0(probLen); - probLen = prob + LenMid + (posState << kLenNumMidBits); + probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); len = 1; TREE_GET_BIT(probLen, len); TREE_GET_BIT(probLen, len); @@ -356,7 +435,7 @@ UPDATE_1(probLen); probLen = prob + LenHigh; TREE_DECODE(probLen, (1 << kLenNumHighBits), len); - len += kLenNumLowSymbols + kLenNumMidSymbols; + len += kLenNumLowSymbols * 2; } } } @@ -376,16 +455,16 @@ if (posSlot < kEndPosModelIndex) { distance <<= numDirectBits; - prob = probs + SpecPos + distance - posSlot - 1; + prob = probs + SpecPos; { - UInt32 mask = 1; - unsigned i = 1; + UInt32 m = 1; + distance++; do { - GET_BIT2(prob + i, i, ; , distance |= mask); - mask <<= 1; + REV_BIT_VAR(prob, distance, m); } - while (--numDirectBits != 0); + while (--numDirectBits); + distance -= m; } } else @@ -412,19 +491,20 @@ } */ } - while (--numDirectBits != 0); + while (--numDirectBits); prob = probs + Align; distance <<= kNumAlignBits; { unsigned i = 1; - GET_BIT2(prob + i, i, ; , distance |= 1); - GET_BIT2(prob + i, i, ; , distance |= 2); - GET_BIT2(prob + i, i, ; , distance |= 4); - GET_BIT2(prob + i, i, ; , distance |= 8); + REV_BIT_CONST(prob, i, 1); + REV_BIT_CONST(prob, i, 2); + REV_BIT_CONST(prob, i, 4); + REV_BIT_LAST (prob, i, 8); + distance |= i; } if (distance == (UInt32)0xFFFFFFFF) { - len += kMatchSpecLenStart; + len = kMatchSpecLenStart; state -= kNumStates; break; } @@ -435,20 +515,12 @@ rep2 = rep1; rep1 = rep0; rep0 = distance + 1; - if (checkDicSize == 0) - { - if (distance >= processedPos) - { - p->dicPos = dicPos; - return SZ_ERROR_DATA; - } - } - else if (distance >= checkDicSize) + state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; + if (distance >= (checkDicSize == 0 ? processedPos: checkDicSize)) { p->dicPos = dicPos; return SZ_ERROR_DATA; } - state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3; } len += kMatchMinLen; @@ -511,6 +583,7 @@ return SZ_OK; } +#endif static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit) { @@ -519,7 +592,7 @@ Byte *dic = p->dic; SizeT dicPos = p->dicPos; SizeT dicBufSize = p->dicBufSize; - unsigned len = p->remainLen; + unsigned len = (unsigned)p->remainLen; SizeT rep0 = p->reps[0]; /* we use SizeT to avoid the BUG of VC14 for AMD64 */ SizeT rem = limit - dicPos; if (rem < len) @@ -540,6 +613,14 @@ } } + +#define kRange0 0xFFFFFFFF +#define kBound0 ((kRange0 >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1)) +#define kBadRepCode (kBound0 + (((kRange0 - kBound0) >> kNumBitModelTotalBits) << (kNumBitModelTotalBits - 1))) +#if kBadRepCode != (0xC0000000 - 0x400) + #error Stop_Compiling_Bad_LZMA_Check +#endif + static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit) { do @@ -550,9 +631,13 @@ UInt32 rem = p->prop.dicSize - p->processedPos; if (limit - p->dicPos > rem) limit2 = p->dicPos + rem; + + if (p->processedPos == 0) + if (p->code >= kBadRepCode) + return SZ_ERROR_DATA; } - - RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit)); + + RINOK(LZMA_DECODE_REAL(p, limit2, bufLimit)); if (p->checkDicSize == 0 && p->processedPos >= p->prop.dicSize) p->checkDicSize = p->prop.dicSize; @@ -561,9 +646,6 @@ } while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart); - if (p->remainLen > kMatchSpecLenStart) - p->remainLen = kMatchSpecLenStart; - return 0; } @@ -580,17 +662,17 @@ UInt32 range = p->range; UInt32 code = p->code; const Byte *bufLimit = buf + inSize; - const CLzmaProb *probs = p->probs; - unsigned state = p->state; + const CLzmaProb *probs = GET_PROBS; + unsigned state = (unsigned)p->state; ELzmaDummy res; { const CLzmaProb *prob; UInt32 bound; unsigned ttt; - unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1); + unsigned posState = CALC_POS_STATE(p->processedPos, (1 << p->prop.pb) - 1); - prob = probs + IsMatch + (state << kNumPosBitsMax) + posState; + prob = probs + IsMatch + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK @@ -618,10 +700,11 @@ { unsigned bit; const CLzmaProb *probLit; - matchByte <<= 1; - bit = (matchByte & offs); - probLit = prob + offs + bit + symbol; - GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit) + matchByte += matchByte; + bit = offs; + offs &= matchByte; + probLit = prob + (offs + bit + symbol); + GET_BIT2_CHECK(probLit, symbol, offs ^= bit; , ; ) } while (symbol < 0x100); } @@ -648,7 +731,7 @@ IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; - prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState; + prob = probs + IsRep0Long + COMBINED_PS_STATE; IF_BIT_0_CHECK(prob) { UPDATE_0_CHECK; @@ -691,7 +774,7 @@ IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; - probLen = prob + LenLow + (posState << kLenNumLowBits); + probLen = prob + LenLow + GET_LEN_STATE; offset = 0; limit = 1 << kLenNumLowBits; } @@ -702,15 +785,15 @@ IF_BIT_0_CHECK(probLen) { UPDATE_0_CHECK; - probLen = prob + LenMid + (posState << kLenNumMidBits); + probLen = prob + LenLow + GET_LEN_STATE + (1 << kLenNumLowBits); offset = kLenNumLowSymbols; - limit = 1 << kLenNumMidBits; + limit = 1 << kLenNumLowBits; } else { UPDATE_1_CHECK; probLen = prob + LenHigh; - offset = kLenNumLowSymbols + kLenNumMidSymbols; + offset = kLenNumLowSymbols * 2; limit = 1 << kLenNumHighBits; } } @@ -722,7 +805,7 @@ { unsigned posSlot; prob = probs + PosSlot + - ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << + ((len < kNumLenToPosStates - 1 ? len : kNumLenToPosStates - 1) << kNumPosSlotBits); TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot); if (posSlot >= kStartPosModelIndex) @@ -733,7 +816,7 @@ if (posSlot < kEndPosModelIndex) { - prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1; + prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits); } else { @@ -745,17 +828,18 @@ code -= range & (((code - range) >> 31) - 1); /* if (code >= range) code -= range; */ } - while (--numDirectBits != 0); + while (--numDirectBits); prob = probs + Align; numDirectBits = kNumAlignBits; } { unsigned i = 1; + unsigned m = 1; do { - GET_BIT_CHECK(prob + i, i); + REV_BIT_CHECK(prob, i, m); } - while (--numDirectBits != 0); + while (--numDirectBits); } } } @@ -768,18 +852,17 @@ void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState) { - p->needFlush = 1; - p->remainLen = 0; + p->remainLen = kMatchSpecLenStart + 1; p->tempBufSize = 0; if (initDic) { p->processedPos = 0; p->checkDicSize = 0; - p->needInitState = 1; + p->remainLen = kMatchSpecLenStart + 2; } if (initState) - p->needInitState = 1; + p->remainLen = kMatchSpecLenStart + 2; } void LzmaDec_Init(CLzmaDec *p) @@ -788,53 +871,54 @@ LzmaDec_InitDicAndState(p, True, True); } -static void LzmaDec_InitStateReal(CLzmaDec *p) -{ - SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); - SizeT i; - CLzmaProb *probs = p->probs; - for (i = 0; i < numProbs; i++) - probs[i] = kBitModelTotal >> 1; - p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; - p->state = 0; - p->needInitState = 0; -} SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT inSize = *srcLen; (*srcLen) = 0; - LzmaDec_WriteRem(p, dicLimit); *status = LZMA_STATUS_NOT_SPECIFIED; - while (p->remainLen != kMatchSpecLenStart) + if (p->remainLen > kMatchSpecLenStart) { - int checkEndMarkNow; + for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) + p->tempBuf[p->tempBufSize++] = *src++; + if (p->tempBufSize != 0 && p->tempBuf[0] != 0) + return SZ_ERROR_DATA; + if (p->tempBufSize < RC_INIT_SIZE) + { + *status = LZMA_STATUS_NEEDS_MORE_INPUT; + return SZ_OK; + } + p->code = + ((UInt32)p->tempBuf[1] << 24) + | ((UInt32)p->tempBuf[2] << 16) + | ((UInt32)p->tempBuf[3] << 8) + | ((UInt32)p->tempBuf[4]); + p->range = 0xFFFFFFFF; + p->tempBufSize = 0; - if (p->needFlush) - { - for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--) - p->tempBuf[p->tempBufSize++] = *src++; - if (p->tempBufSize < RC_INIT_SIZE) - { - *status = LZMA_STATUS_NEEDS_MORE_INPUT; - return SZ_OK; - } - if (p->tempBuf[0] != 0) - return SZ_ERROR_DATA; - p->code = - ((UInt32)p->tempBuf[1] << 24) - | ((UInt32)p->tempBuf[2] << 16) - | ((UInt32)p->tempBuf[3] << 8) - | ((UInt32)p->tempBuf[4]); - p->range = 0xFFFFFFFF; - p->needFlush = 0; - p->tempBufSize = 0; - } + if (p->remainLen > kMatchSpecLenStart + 1) + { + SizeT numProbs = LzmaProps_GetNumProbs(&p->prop); + SizeT i; + CLzmaProb *probs = p->probs; + for (i = 0; i < numProbs; i++) + probs[i] = kBitModelTotal >> 1; + p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1; + p->state = 0; + } + + p->remainLen = 0; + } + + LzmaDec_WriteRem(p, dicLimit); + + while (p->remainLen != kMatchSpecLenStart) + { + int checkEndMarkNow = 0; - checkEndMarkNow = 0; if (p->dicPos >= dicLimit) { if (p->remainLen == 0 && p->code == 0) @@ -855,9 +939,6 @@ checkEndMarkNow = 1; } - if (p->needInitState) - LzmaDec_InitStateReal(p); - if (p->tempBufSize == 0) { SizeT processed; @@ -930,11 +1011,14 @@ p->tempBufSize = 0; } } - if (p->code == 0) - *status = LZMA_STATUS_FINISHED_WITH_MARK; - return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA; + + if (p->code != 0) + return SZ_ERROR_DATA; + *status = LZMA_STATUS_FINISHED_WITH_MARK; + return SZ_OK; } + SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status) { SizeT outSize = *destLen; @@ -975,19 +1059,19 @@ } } -void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc) +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->probs); + ISzAlloc_Free(alloc, p->probs); p->probs = NULL; } -static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc) +static void LzmaDec_FreeDict(CLzmaDec *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->dic); + ISzAlloc_Free(alloc, p->dic); p->dic = NULL; } -void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc) +void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc) { LzmaDec_FreeProbs(p, alloc); LzmaDec_FreeDict(p, alloc); @@ -1011,29 +1095,30 @@ if (d >= (9 * 5 * 5)) return SZ_ERROR_UNSUPPORTED; - p->lc = d % 9; + p->lc = (Byte)(d % 9); d /= 9; - p->pb = d / 5; - p->lp = d % 5; + p->pb = (Byte)(d / 5); + p->lp = (Byte)(d % 5); return SZ_OK; } -static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc) +static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAllocPtr alloc) { UInt32 numProbs = LzmaProps_GetNumProbs(propNew); if (!p->probs || numProbs != p->numProbs) { LzmaDec_FreeProbs(p, alloc); - p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb)); - p->numProbs = numProbs; + p->probs = (CLzmaProb *)ISzAlloc_Alloc(alloc, numProbs * sizeof(CLzmaProb)); if (!p->probs) return SZ_ERROR_MEM; + p->probs_1664 = p->probs + 1664; + p->numProbs = numProbs; } return SZ_OK; } -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc) { CLzmaProps propNew; RINOK(LzmaProps_Decode(&propNew, props, propsSize)); @@ -1042,7 +1127,7 @@ return SZ_OK; } -SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc) +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc) { CLzmaProps propNew; SizeT dicBufSize; @@ -1062,7 +1147,7 @@ if (!p->dic || dicBufSize != p->dicBufSize) { LzmaDec_FreeDict(p, alloc); - p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize); + p->dic = (Byte *)ISzAlloc_Alloc(alloc, dicBufSize); if (!p->dic) { LzmaDec_FreeProbs(p, alloc); @@ -1076,7 +1161,7 @@ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, - ELzmaStatus *status, ISzAlloc *alloc) + ELzmaStatus *status, ISzAllocPtr alloc) { CLzmaDec p; SRes res; diff -Nru leanify-0.4.3/lib/LZMA/LzmaDec.h leanify-0.4.3+git20181014/lib/LZMA/LzmaDec.h --- leanify-0.4.3/lib/LZMA/LzmaDec.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaDec.h 2018-07-03 15:36:27.000000000 +0000 @@ -1,5 +1,5 @@ /* LzmaDec.h -- LZMA Decoder -2013-01-18 : Igor Pavlov : Public domain */ +2018-04-21 : Igor Pavlov : Public domain */ #ifndef __LZMA_DEC_H #define __LZMA_DEC_H @@ -12,11 +12,13 @@ /* _LZMA_PROB32 can increase the speed on some CPUs, but memory usage for CLzmaDec::probs will be doubled in that case */ +typedef #ifdef _LZMA_PROB32 -#define CLzmaProb UInt32 + UInt32 #else -#define CLzmaProb UInt16 + UInt16 #endif + CLzmaProb; /* ---------- LZMA Properties ---------- */ @@ -25,7 +27,10 @@ typedef struct _CLzmaProps { - unsigned lc, lp, pb; + Byte lc; + Byte lp; + Byte pb; + Byte _pad_; UInt32 dicSize; } CLzmaProps; @@ -47,32 +52,34 @@ typedef struct { + /* Don't change this structure. ASM code can use it. */ CLzmaProps prop; CLzmaProb *probs; + CLzmaProb *probs_1664; Byte *dic; - const Byte *buf; - UInt32 range, code; - SizeT dicPos; SizeT dicBufSize; + SizeT dicPos; + const Byte *buf; + UInt32 range; + UInt32 code; UInt32 processedPos; UInt32 checkDicSize; - unsigned state; UInt32 reps[4]; - unsigned remainLen; - int needFlush; - int needInitState; + UInt32 state; + UInt32 remainLen; + UInt32 numProbs; unsigned tempBufSize; Byte tempBuf[LZMA_REQUIRED_INPUT_MAX]; } CLzmaDec; -#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; } +#define LzmaDec_Construct(p) { (p)->dic = NULL; (p)->probs = NULL; } void LzmaDec_Init(CLzmaDec *p); /* There are two types of LZMA streams: - 0) Stream with end mark. That end mark adds about 6 bytes to compressed size. - 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */ + - Stream with end mark. That end mark adds about 6 bytes to compressed size. + - Stream without end mark. You must know exact uncompressed size to decompress such stream. */ typedef enum { @@ -129,11 +136,11 @@ SZ_ERROR_UNSUPPORTED - Unsupported properties */ -SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc); -void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc); +SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc); +void LzmaDec_FreeProbs(CLzmaDec *p, ISzAllocPtr alloc); -SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc); -void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc); +SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAllocPtr alloc); +void LzmaDec_Free(CLzmaDec *p, ISzAllocPtr alloc); /* ---------- Dictionary Interface ---------- */ @@ -142,7 +149,7 @@ You must work with CLzmaDec variables directly in this interface. STEPS: - LzmaDec_Constr() + LzmaDec_Construct() LzmaDec_Allocate() for (each new stream) { @@ -220,7 +227,7 @@ SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode, - ELzmaStatus *status, ISzAlloc *alloc); + ELzmaStatus *status, ISzAllocPtr alloc); EXTERN_C_END diff -Nru leanify-0.4.3/lib/LZMA/LzmaEnc.c leanify-0.4.3+git20181014/lib/LZMA/LzmaEnc.c --- leanify-0.4.3/lib/LZMA/LzmaEnc.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaEnc.c 2018-07-03 15:36:27.000000000 +0000 @@ -1,5 +1,5 @@ /* LzmaEnc.c -- LZMA Encoder -2015-11-08 : Igor Pavlov : Public domain */ +2018-04-29 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -23,17 +23,8 @@ static unsigned g_STAT_OFFSET = 0; #endif -#define kMaxHistorySize ((UInt32)3 << 29) -/* #define kMaxHistorySize ((UInt32)7 << 29) */ - -#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1) - -#define kBlockSize (9 << 10) -#define kUnpackBlockSize (1 << 18) -#define kMatchArraySize (1 << 21) -#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX) - -#define kNumMaxDirectBits (31) +#define kLzmaMaxHistorySize ((UInt32)3 << 29) +/* #define kLzmaMaxHistorySize ((UInt32)7 << 29) */ #define kNumTopBits 24 #define kTopValue ((UInt32)1 << kNumTopBits) @@ -62,14 +53,15 @@ if (level < 0) level = 5; p->level = level; - if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26))); + if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level <= 7 ? (1 << 25) : (1 << 26))); if (p->dictSize > p->reduceSize) { unsigned i; + UInt32 reduceSize = (UInt32)p->reduceSize; for (i = 11; i <= 30; i++) { - if ((UInt32)p->reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } - if ((UInt32)p->reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } + if (reduceSize <= ((UInt32)2 << i)) { p->dictSize = ((UInt32)2 << i); break; } + if (reduceSize <= ((UInt32)3 << i)) { p->dictSize = ((UInt32)3 << i); break; } } } @@ -108,11 +100,11 @@ #define kDicLogSizeMaxCompress 32 -#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); } +#define BSR2_RET(pos, res) { unsigned long zz; _BitScanReverse(&zz, (pos)); res = (zz + zz) + ((pos >> (zz - 1)) & 1); } -static UInt32 GetPosSlot1(UInt32 pos) +static unsigned GetPosSlot1(UInt32 pos) { - UInt32 res; + unsigned res; BSR2_RET(pos, res); return res; } @@ -145,19 +137,19 @@ /* we can use ((limit - pos) >> 31) only if (pos < ((UInt32)1 << 31)) */ /* -#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ +#define BSR2_RET(pos, res) { unsigned zz = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \ - res = p->g_FastPos[pos >> i] + (i * 2); } + res = p->g_FastPos[pos >> zz] + (zz * 2); } */ /* -#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \ +#define BSR2_RET(pos, res) { unsigned zz = 6 + ((kNumLogBits - 1) & \ (0 - (((((UInt32)1 << (kNumLogBits)) - 1) - (pos >> 6)) >> 31))); \ - res = p->g_FastPos[pos >> i] + (i * 2); } + res = p->g_FastPos[pos >> zz] + (zz * 2); } */ -#define BSR2_RET(pos, res) { UInt32 i = (pos < (1 << (kNumLogBits + 6))) ? 6 : 6 + kNumLogBits - 1; \ - res = p->g_FastPos[pos >> i] + (i * 2); } +#define BSR2_RET(pos, res) { unsigned zz = (pos < (1 << (kNumLogBits + 6))) ? 6 : 6 + kNumLogBits - 1; \ + res = p->g_FastPos[pos >> zz] + (zz * 2); } /* #define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \ @@ -167,32 +159,32 @@ #define GetPosSlot1(pos) p->g_FastPos[pos] #define GetPosSlot2(pos, res) { BSR2_RET(pos, res); } -#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); } +#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos & (kNumFullDistances - 1)]; else BSR2_RET(pos, res); } #endif #define LZMA_NUM_REPS 4 -typedef unsigned CState; +typedef UInt16 CState; +typedef UInt16 CExtra; typedef struct { UInt32 price; - CState state; - int prev1IsChar; - int prev2; - - UInt32 posPrev2; - UInt32 backPrev2; - - UInt32 posPrev; - UInt32 backPrev; - UInt32 backs[LZMA_NUM_REPS]; + CExtra extra; + // 0 : normal + // 1 : LIT : MATCH + // > 1 : MATCH (extra-1) : LIT : REP0 (len) + UInt32 len; + UInt32 dist; + UInt32 reps[LZMA_NUM_REPS]; } COptimal; + #define kNumOpts (1 << 12) +#define kPackReserve (1 + kNumOpts * 2) #define kNumLenToPosStates 4 #define kNumPosSlotBits 6 @@ -200,22 +192,21 @@ #define kDicLogSizeMax 32 #define kDistTableSizeMax (kDicLogSizeMax * 2) - #define kNumAlignBits 4 #define kAlignTableSize (1 << kNumAlignBits) #define kAlignMask (kAlignTableSize - 1) #define kStartPosModelIndex 4 #define kEndPosModelIndex 14 -#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex) - #define kNumFullDistances (1 << (kEndPosModelIndex >> 1)) +typedef #ifdef _LZMA_PROB32 -#define CLzmaProb UInt32 + UInt32 #else -#define CLzmaProb UInt16 + UInt16 #endif + CLzmaProb; #define LZMA_PB_MAX 4 #define LZMA_LC_MAX 8 @@ -223,15 +214,11 @@ #define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX) - #define kLenNumLowBits 3 #define kLenNumLowSymbols (1 << kLenNumLowBits) -#define kLenNumMidBits 3 -#define kLenNumMidSymbols (1 << kLenNumMidBits) #define kLenNumHighBits 8 #define kLenNumHighSymbols (1 << kLenNumHighBits) - -#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols) +#define kLenNumSymbolsTotal (kLenNumLowSymbols * 2 + kLenNumHighSymbols) #define LZMA_MATCH_LEN_MIN 2 #define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1) @@ -241,27 +228,23 @@ typedef struct { - CLzmaProb choice; - CLzmaProb choice2; - CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits]; - CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits]; + CLzmaProb low[LZMA_NUM_PB_STATES_MAX << (kLenNumLowBits + 1)]; CLzmaProb high[kLenNumHighSymbols]; } CLenEnc; typedef struct { - CLenEnc p; - UInt32 tableSize; + unsigned tableSize; + unsigned counters[LZMA_NUM_PB_STATES_MAX]; UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal]; - UInt32 counters[LZMA_NUM_PB_STATES_MAX]; } CLenPriceEnc; typedef struct { UInt32 range; - Byte cache; + unsigned cache; UInt64 low; UInt64 cacheSize; Byte *buf; @@ -277,48 +260,54 @@ { CLzmaProb *litProbs; - UInt32 state; + unsigned state; UInt32 reps[LZMA_NUM_REPS]; - CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; - CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; - CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + CLzmaProb posEncoders[kNumFullDistances]; - CLenPriceEnc lenEnc; - CLenPriceEnc repLenEnc; + CLenEnc lenProbs; + CLenEnc repLenProbs; + } CSaveState; +typedef UInt32 CProbPrice; + + typedef struct { void *matchFinderObj; IMatchFinder matchFinder; - UInt32 optimumEndIndex; - UInt32 optimumCurrentIndex; + unsigned optCur; + unsigned optEnd; - UInt32 longestMatchLength; - UInt32 numPairs; + unsigned longestMatchLen; + unsigned numPairs; UInt32 numAvail; - UInt32 numFastBytes; - UInt32 additionalOffset; + unsigned state; + unsigned numFastBytes; + unsigned additionalOffset; UInt32 reps[LZMA_NUM_REPS]; - UInt32 state; + unsigned lpMask, pbMask; + CLzmaProb *litProbs; + CRangeEnc rc; + + UInt32 backRes; unsigned lc, lp, pb; - unsigned lpMask, pbMask; unsigned lclp; - CLzmaProb *litProbs; - Bool fastMode; Bool writeEndMark; Bool finished; @@ -327,19 +316,19 @@ UInt64 nowPos64; - UInt32 matchPriceCount; - UInt32 alignPriceCount; + unsigned matchPriceCount; + unsigned alignPriceCount; - UInt32 distTableSize; + unsigned distTableSize; UInt32 dictSize; SRes result; - CRangeEnc rc; - #ifndef _7ZIP_ST Bool mtMode; + // begin of CMatchFinderMt is used in LZ thread CMatchFinderMt matchFinderMt; + // end of CMatchFinderMt is used in BT and HASH threads #endif CMatchFinder matchFinderBase; @@ -348,33 +337,37 @@ Byte pad[128]; #endif - COptimal opt[kNumOpts]; - - #ifndef LZMA_LOG_BSR - Byte g_FastPos[1 << kNumLogBits]; - #endif + // LZ thread + CProbPrice ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; - UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits]; UInt32 matches[LZMA_MATCH_LEN_MAX * 2 + 2 + 1]; + UInt32 alignPrices[kAlignTableSize]; UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax]; UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances]; - UInt32 alignPrices[kAlignTableSize]; - CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; + CLzmaProb posAlignEncoder[1 << kNumAlignBits]; CLzmaProb isRep[kNumStates]; CLzmaProb isRepG0[kNumStates]; CLzmaProb isRepG1[kNumStates]; CLzmaProb isRepG2[kNumStates]; + CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX]; CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX]; - CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits]; - CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex]; - CLzmaProb posAlignEncoder[1 << kNumAlignBits]; + CLzmaProb posEncoders[kNumFullDistances]; + CLenEnc lenProbs; + CLenEnc repLenProbs; + + #ifndef LZMA_LOG_BSR + Byte g_FastPos[1 << kNumLogBits]; + #endif + CLenPriceEnc lenEnc; CLenPriceEnc repLenEnc; + COptimal opt[kNumOpts]; + CSaveState saveState; #ifndef _7ZIP_ST @@ -383,58 +376,62 @@ } CLzmaEnc; + +#define COPY_ARR(dest, src, arr) memcpy(dest->arr, src->arr, sizeof(src->arr)); + void LzmaEnc_SaveState(CLzmaEncHandle pp) { CLzmaEnc *p = (CLzmaEnc *)pp; CSaveState *dest = &p->saveState; - int i; - dest->lenEnc = p->lenEnc; - dest->repLenEnc = p->repLenEnc; + dest->state = p->state; + + dest->lenProbs = p->lenProbs; + dest->repLenProbs = p->repLenProbs; + + COPY_ARR(dest, p, reps); + + COPY_ARR(dest, p, posAlignEncoder); + COPY_ARR(dest, p, isRep); + COPY_ARR(dest, p, isRepG0); + COPY_ARR(dest, p, isRepG1); + COPY_ARR(dest, p, isRepG2); + COPY_ARR(dest, p, isMatch); + COPY_ARR(dest, p, isRep0Long); + COPY_ARR(dest, p, posSlotEncoder); + COPY_ARR(dest, p, posEncoders); - for (i = 0; i < kNumStates; i++) - { - memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); - memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); - } - for (i = 0; i < kNumLenToPosStates; i++) - memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); - memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); - memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); - memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); - memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); - memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); - memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); - memcpy(dest->reps, p->reps, sizeof(p->reps)); memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << p->lclp) * sizeof(CLzmaProb)); } + void LzmaEnc_RestoreState(CLzmaEncHandle pp) { CLzmaEnc *dest = (CLzmaEnc *)pp; const CSaveState *p = &dest->saveState; - int i; - dest->lenEnc = p->lenEnc; - dest->repLenEnc = p->repLenEnc; + dest->state = p->state; - for (i = 0; i < kNumStates; i++) - { - memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i])); - memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i])); - } - for (i = 0; i < kNumLenToPosStates; i++) - memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i])); - memcpy(dest->isRep, p->isRep, sizeof(p->isRep)); - memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0)); - memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1)); - memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2)); - memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders)); - memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder)); - memcpy(dest->reps, p->reps, sizeof(p->reps)); + dest->lenProbs = p->lenProbs; + dest->repLenProbs = p->repLenProbs; + + COPY_ARR(dest, p, reps); + + COPY_ARR(dest, p, posAlignEncoder); + COPY_ARR(dest, p, isRep); + COPY_ARR(dest, p, isRepG0); + COPY_ARR(dest, p, isRepG1); + COPY_ARR(dest, p, isRepG2); + COPY_ARR(dest, p, isMatch); + COPY_ARR(dest, p, isRep0Long); + COPY_ARR(dest, p, posSlotEncoder); + COPY_ARR(dest, p, posEncoders); + memcpy(dest->litProbs, p->litProbs, ((UInt32)0x300 << dest->lclp) * sizeof(CLzmaProb)); } + + SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2) { CLzmaEnc *p = (CLzmaEnc *)pp; @@ -445,7 +442,7 @@ || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX || props.dictSize > ((UInt64)1 << kDicLogSizeMaxCompress) - || props.dictSize > kMaxHistorySize) + || props.dictSize > kLzmaMaxHistorySize) return SZ_ERROR_PARAM; p->dictSize = props.dictSize; @@ -463,7 +460,7 @@ p->fastMode = (props.algo == 0); p->matchFinderBase.btMode = (Byte)(props.btMode ? 1 : 0); { - UInt32 numHashBytes = 4; + unsigned numHashBytes = 4; if (props.btMode) { if (props.numHashBytes < 2) @@ -492,13 +489,27 @@ return SZ_OK; } -static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; -static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; -static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; -static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; -#define IsCharState(s) ((s) < 7) +void LzmaEnc_SetDataSize(CLzmaEncHandle pp, UInt64 expectedDataSiize) +{ + CLzmaEnc *p = (CLzmaEnc *)pp; + p->matchFinderBase.expectedDataSize = expectedDataSiize; +} + + +#define kState_Start 0 +#define kState_LitAfterMatch 4 +#define kState_LitAfterRep 5 +#define kState_MatchAfterLit 7 +#define kState_RepAfterLit 8 + +static const Byte kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5}; +static const Byte kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10}; +static const Byte kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11}; +static const Byte kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11}; +#define IsLitState(s) ((s) < 7) +#define GetLenToPosState2(len) (((len) < kNumLenToPosStates - 1) ? (len) : kNumLenToPosStates - 1) #define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1) #define kInfinityPrice (1 << 30) @@ -509,14 +520,16 @@ p->bufBase = NULL; } -#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) +#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize) +#define RangeEnc_GetProcessed_sizet(p) ((size_t)(p)->processed + ((p)->buf - (p)->bufBase) + (size_t)(p)->cacheSize) #define RC_BUF_SIZE (1 << 16) -static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc) + +static int RangeEnc_Alloc(CRangeEnc *p, ISzAllocPtr alloc) { if (!p->bufBase) { - p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE); + p->bufBase = (Byte *)ISzAlloc_Alloc(alloc, RC_BUF_SIZE); if (!p->bufBase) return 0; p->bufLim = p->bufBase + RC_BUF_SIZE; @@ -524,19 +537,19 @@ return 1; } -static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc) +static void RangeEnc_Free(CRangeEnc *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->bufBase); + ISzAlloc_Free(alloc, p->bufBase); p->bufBase = 0; } static void RangeEnc_Init(CRangeEnc *p) { /* Stream.Init(); */ - p->low = 0; p->range = 0xFFFFFFFF; - p->cacheSize = 1; p->cache = 0; + p->low = 0; + p->cacheSize = 0; p->buf = p->bufBase; @@ -544,37 +557,48 @@ p->res = SZ_OK; } -static void RangeEnc_FlushStream(CRangeEnc *p) +MY_NO_INLINE static void RangeEnc_FlushStream(CRangeEnc *p) { size_t num; if (p->res != SZ_OK) return; num = p->buf - p->bufBase; - if (num != p->outStream->Write(p->outStream, p->bufBase, num)) + if (num != ISeqOutStream_Write(p->outStream, p->bufBase, num)) p->res = SZ_ERROR_WRITE; p->processed += num; p->buf = p->bufBase; } -static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) +MY_NO_INLINE static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p) { - if ((UInt32)p->low < (UInt32)0xFF000000 || (unsigned)(p->low >> 32) != 0) + UInt32 low = (UInt32)p->low; + unsigned high = (unsigned)(p->low >> 32); + p->low = (UInt32)(low << 8); + if (low < (UInt32)0xFF000000 || high != 0) { - Byte temp = p->cache; - do { Byte *buf = p->buf; - *buf++ = (Byte)(temp + (Byte)(p->low >> 32)); + *buf++ = (Byte)(p->cache + high); + p->cache = (unsigned)(low >> 24); p->buf = buf; if (buf == p->bufLim) RangeEnc_FlushStream(p); - temp = 0xFF; + if (p->cacheSize == 0) + return; + } + high += 0xFF; + for (;;) + { + Byte *buf = p->buf; + *buf++ = (Byte)(high); + p->buf = buf; + if (buf == p->bufLim) + RangeEnc_FlushStream(p); + if (--p->cacheSize == 0) + return; } - while (--p->cacheSize != 0); - p->cache = (Byte)((UInt32)p->low >> 24); } p->cacheSize++; - p->low = (UInt32)p->low << 8; } static void RangeEnc_FlushData(CRangeEnc *p) @@ -584,78 +608,121 @@ RangeEnc_ShiftLow(p); } -static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, unsigned numBits) -{ - do - { - p->range >>= 1; - p->low += p->range & (0 - ((value >> --numBits) & 1)); - if (p->range < kTopValue) - { - p->range <<= 8; - RangeEnc_ShiftLow(p); - } - } - while (numBits != 0); -} +#define RC_NORM(p) if (range < kTopValue) { range <<= 8; RangeEnc_ShiftLow(p); } -static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol) -{ - UInt32 ttt = *prob; - UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt; - if (symbol == 0) - { - p->range = newBound; - ttt += (kBitModelTotal - ttt) >> kNumMoveBits; - } - else - { - p->low += newBound; - p->range -= newBound; - ttt -= ttt >> kNumMoveBits; +#define RC_BIT_PRE(p, prob) \ + ttt = *(prob); \ + newBound = (range >> kNumBitModelTotalBits) * ttt; + +// #define _LZMA_ENC_USE_BRANCH + +#ifdef _LZMA_ENC_USE_BRANCH + +#define RC_BIT(p, prob, symbol) { \ + RC_BIT_PRE(p, prob) \ + if (symbol == 0) { range = newBound; ttt += (kBitModelTotal - ttt) >> kNumMoveBits; } \ + else { (p)->low += newBound; range -= newBound; ttt -= ttt >> kNumMoveBits; } \ + *(prob) = (CLzmaProb)ttt; \ + RC_NORM(p) \ } - *prob = (CLzmaProb)ttt; - if (p->range < kTopValue) - { - p->range <<= 8; - RangeEnc_ShiftLow(p); + +#else + +#define RC_BIT(p, prob, symbol) { \ + UInt32 mask; \ + RC_BIT_PRE(p, prob) \ + mask = 0 - (UInt32)symbol; \ + range &= mask; \ + mask &= newBound; \ + range -= mask; \ + (p)->low += mask; \ + mask = (UInt32)symbol - 1; \ + range += newBound & mask; \ + mask &= (kBitModelTotal - ((1 << kNumMoveBits) - 1)); \ + mask += ((1 << kNumMoveBits) - 1); \ + ttt += (Int32)(mask - ttt) >> kNumMoveBits; \ + *(prob) = (CLzmaProb)ttt; \ + RC_NORM(p) \ } + +#endif + + + + +#define RC_BIT_0_BASE(p, prob) \ + range = newBound; *(prob) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits)); + +#define RC_BIT_1_BASE(p, prob) \ + range -= newBound; (p)->low += newBound; *(prob) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits)); \ + +#define RC_BIT_0(p, prob) \ + RC_BIT_0_BASE(p, prob) \ + RC_NORM(p) + +#define RC_BIT_1(p, prob) \ + RC_BIT_1_BASE(p, prob) \ + RC_NORM(p) + +static void RangeEnc_EncodeBit_0(CRangeEnc *p, CLzmaProb *prob) +{ + UInt32 range, ttt, newBound; + range = p->range; + RC_BIT_PRE(p, prob) + RC_BIT_0(p, prob) + p->range = range; } static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol) { + UInt32 range = p->range; symbol |= 0x100; do { - RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + UInt32 ttt, newBound; + // RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1); + CLzmaProb *prob = probs + (symbol >> 8); + UInt32 bit = (symbol >> 7) & 1; symbol <<= 1; + RC_BIT(p, prob, bit); } while (symbol < 0x10000); + p->range = range; } static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte) { + UInt32 range = p->range; UInt32 offs = 0x100; symbol |= 0x100; do { + UInt32 ttt, newBound; + CLzmaProb *prob; + UInt32 bit; matchByte <<= 1; - RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + // RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1); + prob = probs + (offs + (matchByte & offs) + (symbol >> 8)); + bit = (symbol >> 7) & 1; symbol <<= 1; offs &= ~(matchByte ^ symbol); + RC_BIT(p, prob, bit); } while (symbol < 0x10000); + p->range = range; } -static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices) + + +static void LzmaEnc_InitPriceTables(CProbPrice *ProbPrices) { UInt32 i; - for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits)) + for (i = 0; i < (kBitModelTotal >> kNumMoveReducingBits); i++) { - const int kCyclesBits = kNumBitPriceShiftBits; - UInt32 w = i; - UInt32 bitCount = 0; - int j; + const unsigned kCyclesBits = kNumBitPriceShiftBits; + UInt32 w = (i << kNumMoveReducingBits) + (1 << (kNumMoveReducingBits - 1)); + unsigned bitCount = 0; + unsigned j; for (j = 0; j < kCyclesBits; j++) { w = w * w; @@ -666,37 +733,41 @@ bitCount++; } } - ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + ProbPrices[i] = (CProbPrice)((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount); + // printf("\n%3d: %5d", i, ProbPrices[i]); } } #define GET_PRICE(prob, symbol) \ - p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + p->ProbPrices[((prob) ^ (unsigned)(((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICEa(prob, symbol) \ - ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; + ProbPrices[((prob) ^ (unsigned)((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits]; #define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits] #define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] -#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits] -#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] +#define GET_PRICEa_0(prob) ProbPrices[(prob) >> kNumMoveReducingBits] +#define GET_PRICEa_1(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits] + -static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, const UInt32 *ProbPrices) +static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, const CProbPrice *ProbPrices) { UInt32 price = 0; symbol |= 0x100; do { - price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1); - symbol <<= 1; + unsigned bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[symbol], bit); } - while (symbol < 0x10000); + while (symbol >= 2); return price; } -static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, const UInt32 *ProbPrices) + +static UInt32 LitEnc_Matched_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, const CProbPrice *ProbPrices) { UInt32 price = 0; UInt32 offs = 0x100; @@ -713,512 +784,525 @@ } -static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) -{ - UInt32 m = 1; - int i; - for (i = numBitLevels; i != 0;) - { - UInt32 bit; - i--; - bit = (symbol >> i) & 1; - RangeEnc_EncodeBit(rc, probs + m, bit); - m = (m << 1) | bit; - } -} - -static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol) -{ - UInt32 m = 1; - int i; - for (i = 0; i < numBitLevels; i++) - { - UInt32 bit = symbol & 1; - RangeEnc_EncodeBit(rc, probs + m, bit); - m = (m << 1) | bit; - symbol >>= 1; - } -} - -static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) -{ - UInt32 price = 0; - symbol |= (1 << numBitLevels); - while (symbol != 1) - { - price += GET_PRICEa(probs[symbol >> 1], symbol & 1); - symbol >>= 1; - } - return price; -} - -static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, const UInt32 *ProbPrices) +static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, unsigned numBits, UInt32 symbol) { - UInt32 price = 0; - UInt32 m = 1; - int i; - for (i = numBitLevels; i != 0; i--) + UInt32 range = rc->range; + unsigned m = 1; + do { - UInt32 bit = symbol & 1; + UInt32 ttt, newBound; + unsigned bit = symbol & 1; + // RangeEnc_EncodeBit(rc, probs + m, bit); symbol >>= 1; - price += GET_PRICEa(probs[m], bit); + RC_BIT(rc, probs + m, bit); m = (m << 1) | bit; } - return price; + while (--numBits); + rc->range = range; } + static void LenEnc_Init(CLenEnc *p) { unsigned i; - p->choice = p->choice2 = kProbInitValue; - for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++) + for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << (kLenNumLowBits + 1)); i++) p->low[i] = kProbInitValue; - for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++) - p->mid[i] = kProbInitValue; for (i = 0; i < kLenNumHighSymbols; i++) p->high[i] = kProbInitValue; } -static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState) +static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, unsigned symbol, unsigned posState) { - if (symbol < kLenNumLowSymbols) - { - RangeEnc_EncodeBit(rc, &p->choice, 0); - RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol); + UInt32 range, ttt, newBound; + CLzmaProb *probs = p->low; + range = rc->range; + RC_BIT_PRE(rc, probs); + if (symbol >= kLenNumLowSymbols) + { + RC_BIT_1(rc, probs); + probs += kLenNumLowSymbols; + RC_BIT_PRE(rc, probs); + if (symbol >= kLenNumLowSymbols * 2) + { + RC_BIT_1(rc, probs); + rc->range = range; + // RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols * 2); + LitEnc_Encode(rc, p->high, symbol - kLenNumLowSymbols * 2); + return; + } + symbol -= kLenNumLowSymbols; } - else + + // RcTree_Encode(rc, probs + (posState << kLenNumLowBits), kLenNumLowBits, symbol); { - RangeEnc_EncodeBit(rc, &p->choice, 1); - if (symbol < kLenNumLowSymbols + kLenNumMidSymbols) - { - RangeEnc_EncodeBit(rc, &p->choice2, 0); - RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols); - } - else - { - RangeEnc_EncodeBit(rc, &p->choice2, 1); - RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols); - } + unsigned m; + unsigned bit; + RC_BIT_0(rc, probs); + probs += (posState << (1 + kLenNumLowBits)); + bit = (symbol >> 2) ; RC_BIT(rc, probs + 1, bit); m = (1 << 1) + bit; + bit = (symbol >> 1) & 1; RC_BIT(rc, probs + m, bit); m = (m << 1) + bit; + bit = symbol & 1; RC_BIT(rc, probs + m, bit); + rc->range = range; } } -static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, const UInt32 *ProbPrices) +static void SetPrices_3(const CLzmaProb *probs, UInt32 startPrice, UInt32 *prices, const CProbPrice *ProbPrices) { - UInt32 a0 = GET_PRICE_0a(p->choice); - UInt32 a1 = GET_PRICE_1a(p->choice); - UInt32 b0 = a1 + GET_PRICE_0a(p->choice2); - UInt32 b1 = a1 + GET_PRICE_1a(p->choice2); - UInt32 i = 0; - for (i = 0; i < kLenNumLowSymbols; i++) - { - if (i >= numSymbols) - return; - prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices); - } - for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++) + unsigned i; + for (i = 0; i < 8; i += 2) { - if (i >= numSymbols) - return; - prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices); + UInt32 price = startPrice; + UInt32 prob; + price += GET_PRICEa(probs[1 ], (i >> 2)); + price += GET_PRICEa(probs[2 + (i >> 2)], (i >> 1) & 1); + prob = probs[4 + (i >> 1)]; + prices[i ] = price + GET_PRICEa_0(prob); + prices[i + 1] = price + GET_PRICEa_1(prob); } - for (; i < numSymbols; i++) - prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices); } -static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, const UInt32 *ProbPrices) -{ - LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices); - p->counters[posState] = p->tableSize; -} -static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, const UInt32 *ProbPrices) +MY_NO_INLINE static void MY_FAST_CALL LenPriceEnc_UpdateTable( + CLenPriceEnc *p, unsigned posState, + const CLenEnc *enc, + const CProbPrice *ProbPrices) { - UInt32 posState; - for (posState = 0; posState < numPosStates; posState++) - LenPriceEnc_UpdateTable(p, posState, ProbPrices); + // int y; for (y = 0; y < 100; y++) { + UInt32 a; + unsigned i, numSymbols; + + UInt32 *prices = p->prices[posState]; + { + const CLzmaProb *probs = enc->low + (posState << (1 + kLenNumLowBits)); + SetPrices_3(probs, GET_PRICEa_0(enc->low[0]), prices, ProbPrices); + a = GET_PRICEa_1(enc->low[0]); + SetPrices_3(probs + kLenNumLowSymbols, a + GET_PRICEa_0(enc->low[kLenNumLowSymbols]), prices + kLenNumLowSymbols, ProbPrices); + a += GET_PRICEa_1(enc->low[kLenNumLowSymbols]); + } + numSymbols = p->tableSize; + p->counters[posState] = numSymbols; + for (i = kLenNumLowSymbols * 2; i < numSymbols; i += 1) + { + prices[i] = a + + // RcTree_GetPrice(enc->high, kLenNumHighBits, i - kLenNumLowSymbols * 2, ProbPrices); + LitEnc_GetPrice(enc->high, i - kLenNumLowSymbols * 2, ProbPrices); + /* + unsigned sym = (i - kLenNumLowSymbols * 2) >> 1; + UInt32 price = a + RcTree_GetPrice(enc->high, kLenNumHighBits - 1, sym, ProbPrices); + UInt32 prob = enc->high[(1 << 7) + sym]; + prices[i ] = price + GET_PRICEa_0(prob); + prices[i + 1] = price + GET_PRICEa_1(prob); + */ + } + // } } -static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, const UInt32 *ProbPrices) +static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, unsigned numPosStates, + const CLenEnc *enc, + const CProbPrice *ProbPrices) { - LenEnc_Encode(&p->p, rc, symbol, posState); - if (updatePrice) - if (--p->counters[posState] == 0) - LenPriceEnc_UpdateTable(p, posState, ProbPrices); + unsigned posState; + for (posState = 0; posState < numPosStates; posState++) + LenPriceEnc_UpdateTable(p, posState, enc, ProbPrices); } - - -static void MovePos(CLzmaEnc *p, UInt32 num) -{ +/* #ifdef SHOW_STAT g_STAT_OFFSET += num; printf("\n MovePos %u", num); #endif +*/ - if (num != 0) - { - p->additionalOffset += num; - p->matchFinder.Skip(p->matchFinderObj, num); - } -} +#define MOVE_POS(p, num) { \ + p->additionalOffset += (num); \ + p->matchFinder.Skip(p->matchFinderObj, (num)); } + -static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes) +static unsigned ReadMatchDistances(CLzmaEnc *p, unsigned *numPairsRes) { - UInt32 lenRes = 0, numPairs; + unsigned numPairs; + + p->additionalOffset++; p->numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj); numPairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matches); + *numPairsRes = numPairs; #ifdef SHOW_STAT printf("\n i = %u numPairs = %u ", g_STAT_OFFSET, numPairs / 2); g_STAT_OFFSET++; { - UInt32 i; + unsigned i; for (i = 0; i < numPairs; i += 2) printf("%2u %6u | ", p->matches[i], p->matches[i + 1]); } #endif - if (numPairs > 0) + if (numPairs == 0) + return 0; { - lenRes = p->matches[numPairs - 2]; - if (lenRes == p->numFastBytes) + unsigned len = p->matches[(size_t)numPairs - 2]; + if (len != p->numFastBytes) + return len; { UInt32 numAvail = p->numAvail; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; { - const Byte *pbyCur = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; - const Byte *pby = pbyCur + lenRes; - ptrdiff_t dif = (ptrdiff_t)-1 - p->matches[numPairs - 1]; - const Byte *pbyLim = pbyCur + numAvail; - for (; pby != pbyLim && *pby == pby[dif]; pby++); - lenRes = (UInt32)(pby - pbyCur); + const Byte *p1 = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + const Byte *p2 = p1 + len; + ptrdiff_t dif = (ptrdiff_t)-1 - p->matches[(size_t)numPairs - 1]; + const Byte *lim = p1 + numAvail; + for (; p2 != lim && *p2 == p2[dif]; p2++); + return (unsigned)(p2 - p1); } } } - p->additionalOffset++; - *numDistancePairsRes = numPairs; - return lenRes; } +#define MARK_LIT ((UInt32)(Int32)-1) -#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False; -#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False; -#define IsShortRep(p) ((p)->backPrev == 0) +#define MakeAs_Lit(p) { (p)->dist = MARK_LIT; (p)->extra = 0; } +#define MakeAs_ShortRep(p) { (p)->dist = 0; (p)->extra = 0; } +#define IsShortRep(p) ((p)->dist == 0) -static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState) -{ - return - GET_PRICE_0(p->isRepG0[state]) + - GET_PRICE_0(p->isRep0Long[state][posState]); -} -static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState) +#define GetPrice_ShortRep(p, state, posState) \ + ( GET_PRICE_0(p->isRepG0[state]) + GET_PRICE_0(p->isRep0Long[state][posState])) + +#define GetPrice_Rep_0(p, state, posState) ( \ + GET_PRICE_1(p->isMatch[state][posState]) \ + + GET_PRICE_1(p->isRep0Long[state][posState])) \ + + GET_PRICE_1(p->isRep[state]) \ + + GET_PRICE_0(p->isRepG0[state]) + + +static UInt32 GetPrice_PureRep(const CLzmaEnc *p, unsigned repIndex, size_t state, size_t posState) { UInt32 price; + UInt32 prob = p->isRepG0[state]; if (repIndex == 0) { - price = GET_PRICE_0(p->isRepG0[state]); + price = GET_PRICE_0(prob); price += GET_PRICE_1(p->isRep0Long[state][posState]); } else { - price = GET_PRICE_1(p->isRepG0[state]); + price = GET_PRICE_1(prob); + prob = p->isRepG1[state]; if (repIndex == 1) - price += GET_PRICE_0(p->isRepG1[state]); + price += GET_PRICE_0(prob); else { - price += GET_PRICE_1(p->isRepG1[state]); + price += GET_PRICE_1(prob); price += GET_PRICE(p->isRepG2[state], repIndex - 2); } } return price; } -static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState) -{ - return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] + - GetPureRepPrice(p, repIndex, state, posState); -} -static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur) +static unsigned Backward(CLzmaEnc *p, unsigned cur) { - UInt32 posMem = p->opt[cur].posPrev; - UInt32 backMem = p->opt[cur].backPrev; - p->optimumEndIndex = cur; - do + unsigned wr = cur + 1; + p->optEnd = wr; + + for (;;) { - if (p->opt[cur].prev1IsChar) - { - MakeAsChar(&p->opt[posMem]) - p->opt[posMem].posPrev = posMem - 1; - if (p->opt[cur].prev2) + UInt32 dist = p->opt[cur].dist; + UInt32 len = p->opt[cur].len; + UInt32 extra = p->opt[cur].extra; + cur -= len; + + if (extra) + { + wr--; + p->opt[wr].len = len; + cur -= extra; + len = extra; + if (extra == 1) { - p->opt[posMem - 1].prev1IsChar = False; - p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2; - p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2; + p->opt[wr].dist = dist; + dist = MARK_LIT; + } + else + { + p->opt[wr].dist = 0; + len--; + wr--; + p->opt[wr].dist = MARK_LIT; + p->opt[wr].len = 1; } } + + if (cur == 0) { - UInt32 posPrev = posMem; - UInt32 backCur = backMem; - - backMem = p->opt[posPrev].backPrev; - posMem = p->opt[posPrev].posPrev; - - p->opt[posPrev].backPrev = backCur; - p->opt[posPrev].posPrev = cur; - cur = posPrev; + p->backRes = dist; + p->optCur = wr; + return len; } + + wr--; + p->opt[wr].dist = dist; + p->opt[wr].len = len; } - while (cur != 0); - *backRes = p->opt[0].backPrev; - p->optimumCurrentIndex = p->opt[0].posPrev; - return p->optimumCurrentIndex; } -#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * (UInt32)0x300) -static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes) + +#define LIT_PROBS(pos, prevByte) \ + (p->litProbs + (UInt32)3 * (((((pos) << 8) + (prevByte)) & p->lpMask) << p->lc)) + + +static unsigned GetOptimum(CLzmaEnc *p, UInt32 position) { - UInt32 numAvail, mainLen, numPairs, repMaxIndex, i, posState, lenEnd, len, cur; - UInt32 matchPrice, repMatchPrice, normalMatchPrice; - UInt32 reps[LZMA_NUM_REPS], repLens[LZMA_NUM_REPS]; + unsigned last, cur; + UInt32 reps[LZMA_NUM_REPS]; + unsigned repLens[LZMA_NUM_REPS]; UInt32 *matches; - const Byte *data; - Byte curByte, matchByte; - if (p->optimumEndIndex != p->optimumCurrentIndex) - { - const COptimal *opt = &p->opt[p->optimumCurrentIndex]; - UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex; - *backRes = opt->backPrev; - p->optimumCurrentIndex = opt->posPrev; - return lenRes; - } - p->optimumCurrentIndex = p->optimumEndIndex = 0; - - if (p->additionalOffset == 0) - mainLen = ReadMatchDistances(p, &numPairs); - else - { - mainLen = p->longestMatchLength; - numPairs = p->numPairs; - } - numAvail = p->numAvail; - if (numAvail < 2) { - *backRes = (UInt32)(-1); - return 1; - } - if (numAvail > LZMA_MATCH_LEN_MAX) - numAvail = LZMA_MATCH_LEN_MAX; - - data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; - repMaxIndex = 0; - for (i = 0; i < LZMA_NUM_REPS; i++) - { - UInt32 lenTest; - const Byte *data2; - reps[i] = p->reps[i]; - data2 = data - reps[i] - 1; - if (data[0] != data2[0] || data[1] != data2[1]) + UInt32 numAvail; + unsigned numPairs, mainLen, repMaxIndex, i, posState; + UInt32 matchPrice, repMatchPrice; + const Byte *data; + Byte curByte, matchByte; + + p->optCur = p->optEnd = 0; + + if (p->additionalOffset == 0) + mainLen = ReadMatchDistances(p, &numPairs); + else { - repLens[i] = 0; - continue; + mainLen = p->longestMatchLen; + numPairs = p->numPairs; } - for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); - repLens[i] = lenTest; - if (lenTest > repLens[repMaxIndex]) - repMaxIndex = i; - } - if (repLens[repMaxIndex] >= p->numFastBytes) - { - UInt32 lenRes; - *backRes = repMaxIndex; - lenRes = repLens[repMaxIndex]; - MovePos(p, lenRes - 1); - return lenRes; - } - - matches = p->matches; - if (mainLen >= p->numFastBytes) - { - *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; - MovePos(p, mainLen - 1); - return mainLen; - } - curByte = *data; - matchByte = *(data - (reps[0] + 1)); - - if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) - { - *backRes = (UInt32)-1; - return 1; - } - - p->opt[0].state = (CState)p->state; - - posState = (position & p->pbMask); - - { - const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); - p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + - (!IsCharState(p->state) ? - LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : - LitEnc_GetPrice(probs, curByte, p->ProbPrices)); - } - - MakeAsChar(&p->opt[1]); - - matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); - repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); - - if (matchByte == curByte) - { - UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState); - if (shortRepPrice < p->opt[1].price) + + numAvail = p->numAvail; + if (numAvail < 2) { - p->opt[1].price = shortRepPrice; - MakeAsShortRep(&p->opt[1]); + p->backRes = MARK_LIT; + return 1; } - } - lenEnd = ((mainLen >= repLens[repMaxIndex]) ? mainLen : repLens[repMaxIndex]); - - if (lenEnd < 2) - { - *backRes = p->opt[1].backPrev; - return 1; - } - - p->opt[1].posPrev = 0; - for (i = 0; i < LZMA_NUM_REPS; i++) - p->opt[0].backs[i] = reps[i]; - - len = lenEnd; - do - p->opt[len--].price = kInfinityPrice; - while (len >= 2); - - for (i = 0; i < LZMA_NUM_REPS; i++) - { - UInt32 repLen = repLens[i]; - UInt32 price; - if (repLen < 2) - continue; - price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState); - do + if (numAvail > LZMA_MATCH_LEN_MAX) + numAvail = LZMA_MATCH_LEN_MAX; + + data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + repMaxIndex = 0; + + for (i = 0; i < LZMA_NUM_REPS; i++) { - UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2]; - COptimal *opt = &p->opt[repLen]; - if (curAndLenPrice < opt->price) + unsigned len; + const Byte *data2; + reps[i] = p->reps[i]; + data2 = data - reps[i]; + if (data[0] != data2[0] || data[1] != data2[1]) { - opt->price = curAndLenPrice; - opt->posPrev = 0; - opt->backPrev = i; - opt->prev1IsChar = False; + repLens[i] = 0; + continue; } + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + repLens[i] = len; + if (len > repLens[repMaxIndex]) + repMaxIndex = i; } - while (--repLen >= 2); - } - - normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); - - len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); - if (len <= mainLen) - { - UInt32 offs = 0; - while (len > matches[offs]) - offs += 2; - for (; ; len++) + + if (repLens[repMaxIndex] >= p->numFastBytes) + { + unsigned len; + p->backRes = repMaxIndex; + len = repLens[repMaxIndex]; + MOVE_POS(p, len - 1) + return len; + } + + matches = p->matches; + + if (mainLen >= p->numFastBytes) + { + p->backRes = matches[(size_t)numPairs - 1] + LZMA_NUM_REPS; + MOVE_POS(p, mainLen - 1) + return mainLen; + } + + curByte = *data; + matchByte = *(data - reps[0]); + + if (mainLen < 2 && curByte != matchByte && repLens[repMaxIndex] < 2) + { + p->backRes = MARK_LIT; + return 1; + } + + p->opt[0].state = (CState)p->state; + + posState = (position & p->pbMask); + + { + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) + + (!IsLitState(p->state) ? + LitEnc_Matched_GetPrice(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + } + + MakeAs_Lit(&p->opt[1]); + + matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]); + repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]); + + if (matchByte == curByte) + { + UInt32 shortRepPrice = repMatchPrice + GetPrice_ShortRep(p, p->state, posState); + if (shortRepPrice < p->opt[1].price) + { + p->opt[1].price = shortRepPrice; + MakeAs_ShortRep(&p->opt[1]); + } + } + + last = (mainLen >= repLens[repMaxIndex] ? mainLen : repLens[repMaxIndex]); + + if (last < 2) + { + p->backRes = p->opt[1].dist; + return 1; + } + + p->opt[1].len = 1; + + p->opt[0].reps[0] = reps[0]; + p->opt[0].reps[1] = reps[1]; + p->opt[0].reps[2] = reps[2]; + p->opt[0].reps[3] = reps[3]; + { - COptimal *opt; - UInt32 distance = matches[offs + 1]; + unsigned len = last; + do + p->opt[len--].price = kInfinityPrice; + while (len >= 2); + } - UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN]; - UInt32 lenToPosState = GetLenToPosState(len); - if (distance < kNumFullDistances) - curAndLenPrice += p->distancesPrices[lenToPosState][distance]; - else + // ---------- REP ---------- + + for (i = 0; i < LZMA_NUM_REPS; i++) + { + unsigned repLen = repLens[i]; + UInt32 price; + if (repLen < 2) + continue; + price = repMatchPrice + GetPrice_PureRep(p, i, p->state, posState); + do { - UInt32 slot; - GetPosSlot2(distance, slot); - curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot]; - } - opt = &p->opt[len]; - if (curAndLenPrice < opt->price) - { - opt->price = curAndLenPrice; - opt->posPrev = 0; - opt->backPrev = distance + LZMA_NUM_REPS; - opt->prev1IsChar = False; + UInt32 price2 = price + p->repLenEnc.prices[posState][(size_t)repLen - 2]; + COptimal *opt = &p->opt[repLen]; + if (price2 < opt->price) + { + opt->price = price2; + opt->len = repLen; + opt->dist = i; + opt->extra = 0; + } } - if (len == matches[offs]) + while (--repLen >= 2); + } + + + // ---------- MATCH ---------- + { + unsigned len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2); + if (len <= mainLen) { - offs += 2; - if (offs == numPairs) - break; + unsigned offs = 0; + UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]); + + while (len > matches[offs]) + offs += 2; + + for (; ; len++) + { + COptimal *opt; + UInt32 dist = matches[(size_t)offs + 1]; + UInt32 price2 = normalMatchPrice + p->lenEnc.prices[posState][(size_t)len - LZMA_MATCH_LEN_MIN]; + unsigned lenToPosState = GetLenToPosState(len); + + if (dist < kNumFullDistances) + price2 += p->distancesPrices[lenToPosState][dist & (kNumFullDistances - 1)]; + else + { + unsigned slot; + GetPosSlot2(dist, slot); + price2 += p->alignPrices[dist & kAlignMask]; + price2 += p->posSlotPrices[lenToPosState][slot]; + } + + opt = &p->opt[len]; + + if (price2 < opt->price) + { + opt->price = price2; + opt->len = len; + opt->dist = dist + LZMA_NUM_REPS; + opt->extra = 0; + } + + if (len == matches[offs]) + { + offs += 2; + if (offs == numPairs) + break; + } + } } } - } + - cur = 0; + cur = 0; #ifdef SHOW_STAT2 /* if (position >= 0) */ { unsigned i; printf("\n pos = %4X", position); - for (i = cur; i <= lenEnd; i++) + for (i = cur; i <= last; i++) printf("\nprice[%4X] = %u", position - cur + i, p->opt[i].price); } #endif + } + + + + // ---------- Optimal Parsing ---------- for (;;) { - UInt32 numAvailFull, newLen, numPairs, posPrev, state, posState, startLen; - UInt32 curPrice, curAnd1Price, matchPrice, repMatchPrice; - Bool nextIsChar; + UInt32 numAvail, numAvailFull; + unsigned newLen, numPairs, prev, state, posState, startLen; + UInt32 curPrice, litPrice, matchPrice, repMatchPrice; + Bool nextIsLit; Byte curByte, matchByte; const Byte *data; - COptimal *curOpt; - COptimal *nextOpt; + COptimal *curOpt, *nextOpt; - cur++; - if (cur == lenEnd) - return Backward(p, backRes, cur); + if (++cur == last) + return Backward(p, cur); newLen = ReadMatchDistances(p, &numPairs); + if (newLen >= p->numFastBytes) { p->numPairs = numPairs; - p->longestMatchLength = newLen; - return Backward(p, backRes, cur); + p->longestMatchLen = newLen; + return Backward(p, cur); } - position++; + curOpt = &p->opt[cur]; - posPrev = curOpt->posPrev; - if (curOpt->prev1IsChar) - { - posPrev--; - if (curOpt->prev2) - { - state = p->opt[curOpt->posPrev2].state; - if (curOpt->backPrev2 < LZMA_NUM_REPS) - state = kRepNextStates[state]; - else - state = kMatchNextStates[state]; - } - else - state = p->opt[posPrev].state; - state = kLiteralNextStates[state]; - } - else - state = p->opt[posPrev].state; - if (posPrev == cur - 1) + prev = cur - curOpt->len; + + if (curOpt->len == 1) { + state = p->opt[prev].state; if (IsShortRep(curOpt)) state = kShortRepNextStates[state]; else @@ -1226,92 +1310,136 @@ } else { - UInt32 pos; const COptimal *prevOpt; - if (curOpt->prev1IsChar && curOpt->prev2) + UInt32 b0; + UInt32 dist = curOpt->dist; + + if (curOpt->extra) { - posPrev = curOpt->posPrev2; - pos = curOpt->backPrev2; - state = kRepNextStates[state]; + prev -= curOpt->extra; + state = kState_RepAfterLit; + if (curOpt->extra == 1) + state = (dist < LZMA_NUM_REPS) ? kState_RepAfterLit : kState_MatchAfterLit; } else { - pos = curOpt->backPrev; - if (pos < LZMA_NUM_REPS) + state = p->opt[prev].state; + if (dist < LZMA_NUM_REPS) state = kRepNextStates[state]; else state = kMatchNextStates[state]; } - prevOpt = &p->opt[posPrev]; - if (pos < LZMA_NUM_REPS) + + prevOpt = &p->opt[prev]; + b0 = prevOpt->reps[0]; + + if (dist < LZMA_NUM_REPS) { - UInt32 i; - reps[0] = prevOpt->backs[pos]; - for (i = 1; i <= pos; i++) - reps[i] = prevOpt->backs[i - 1]; - for (; i < LZMA_NUM_REPS; i++) - reps[i] = prevOpt->backs[i]; + if (dist == 0) + { + reps[0] = b0; + reps[1] = prevOpt->reps[1]; + reps[2] = prevOpt->reps[2]; + reps[3] = prevOpt->reps[3]; + } + else + { + reps[1] = b0; + b0 = prevOpt->reps[1]; + if (dist == 1) + { + reps[0] = b0; + reps[2] = prevOpt->reps[2]; + reps[3] = prevOpt->reps[3]; + } + else + { + reps[2] = b0; + reps[0] = prevOpt->reps[dist]; + reps[3] = prevOpt->reps[dist ^ 1]; + } + } } else { - UInt32 i; - reps[0] = (pos - LZMA_NUM_REPS); - for (i = 1; i < LZMA_NUM_REPS; i++) - reps[i] = prevOpt->backs[i - 1]; + reps[0] = (dist - LZMA_NUM_REPS + 1); + reps[1] = b0; + reps[2] = prevOpt->reps[1]; + reps[3] = prevOpt->reps[2]; } } + curOpt->state = (CState)state; + curOpt->reps[0] = reps[0]; + curOpt->reps[1] = reps[1]; + curOpt->reps[2] = reps[2]; + curOpt->reps[3] = reps[3]; - curOpt->backs[0] = reps[0]; - curOpt->backs[1] = reps[1]; - curOpt->backs[2] = reps[2]; - curOpt->backs[3] = reps[3]; - - curPrice = curOpt->price; - nextIsChar = False; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; curByte = *data; - matchByte = *(data - (reps[0] + 1)); + matchByte = *(data - reps[0]); + position++; posState = (position & p->pbMask); - curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]); - { - const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); - curAnd1Price += - (!IsCharState(state) ? - LitEnc_GetPriceMatched(probs, curByte, matchByte, p->ProbPrices) : - LitEnc_GetPrice(probs, curByte, p->ProbPrices)); - } + /* + The order of Price checks: + < LIT + <= SHORT_REP + < LIT : REP_0 + < REP [ : LIT : REP_0 ] + < MATCH [ : LIT : REP_0 ] + */ + + curPrice = curOpt->price; + litPrice = curPrice + GET_PRICE_0(p->isMatch[state][posState]); - nextOpt = &p->opt[cur + 1]; + nextOpt = &p->opt[(size_t)cur + 1]; + nextIsLit = False; - if (curAnd1Price < nextOpt->price) + // if (litPrice >= nextOpt->price) litPrice = 0; else // 18.new { - nextOpt->price = curAnd1Price; - nextOpt->posPrev = cur; - MakeAsChar(nextOpt); - nextIsChar = True; + const CLzmaProb *probs = LIT_PROBS(position, *(data - 1)); + litPrice += (!IsLitState(state) ? + LitEnc_Matched_GetPrice(probs, curByte, matchByte, p->ProbPrices) : + LitEnc_GetPrice(probs, curByte, p->ProbPrices)); + + if (litPrice < nextOpt->price) + { + nextOpt->price = litPrice; + nextOpt->len = 1; + MakeAs_Lit(nextOpt); + nextIsLit = True; + } } matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]); repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]); - if (matchByte == curByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0)) - { - UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState); - if (shortRepPrice <= nextOpt->price) + // ---------- SHORT_REP ---------- + // if (IsLitState(state)) // 18.new + if (matchByte == curByte) + // if (repMatchPrice < nextOpt->price) // 18.new + if (nextOpt->len < 2 + || (nextOpt->dist != 0 + && nextOpt->extra <= 1 // 17.old + )) + { + UInt32 shortRepPrice = repMatchPrice + GetPrice_ShortRep(p, state, posState); + if (shortRepPrice <= nextOpt->price) // 17.old + // if (shortRepPrice < nextOpt->price) // 18.new { nextOpt->price = shortRepPrice; - nextOpt->posPrev = cur; - MakeAsShortRep(nextOpt); - nextIsChar = True; + nextOpt->len = 1; + MakeAs_ShortRep(nextOpt); + nextIsLit = False; } } + numAvailFull = p->numAvail; { UInt32 temp = kNumOpts - 1 - cur; - if (temp < numAvailFull) + if (numAvailFull > temp) numAvailFull = temp; } @@ -1319,41 +1447,53 @@ continue; numAvail = (numAvailFull <= p->numFastBytes ? numAvailFull : p->numFastBytes); - if (!nextIsChar && matchByte != curByte) /* speed optimization */ - { - /* try Literal + rep0 */ - UInt32 temp; - UInt32 lenTest2; - const Byte *data2 = data - reps[0] - 1; - UInt32 limit = p->numFastBytes + 1; - if (limit > numAvailFull) - limit = numAvailFull; - - for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++); - lenTest2 = temp - 1; - if (lenTest2 >= 2) - { - UInt32 state2 = kLiteralNextStates[state]; - UInt32 posStateNext = (position + 1) & p->pbMask; - UInt32 nextRepMatchPrice = curAnd1Price + - GET_PRICE_1(p->isMatch[state2][posStateNext]) + - GET_PRICE_1(p->isRep[state2]); - /* for (; lenTest2 >= 2; lenTest2--) */ + // numAvail <= p->numFastBytes + + // ---------- LIT : REP_0 ---------- + + if ( + // litPrice != 0 && // 18.new + !nextIsLit + && matchByte != curByte + && numAvailFull > 2) + { + const Byte *data2 = data - reps[0]; + if (data[1] == data2[1] && data[2] == data2[2]) + { + unsigned len; + unsigned limit = p->numFastBytes + 1; + if (limit > numAvailFull) + limit = numAvailFull; + for (len = 3; len < limit && data[len] == data2[len]; len++); + { - UInt32 curAndLenPrice; - COptimal *opt; - UInt32 offset = cur + 1 + lenTest2; - while (lenEnd < offset) - p->opt[++lenEnd].price = kInfinityPrice; - curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); - opt = &p->opt[offset]; - if (curAndLenPrice < opt->price) + unsigned state2 = kLiteralNextStates[state]; + unsigned posState2 = (position + 1) & p->pbMask; + UInt32 price = litPrice + GetPrice_Rep_0(p, state2, posState2); { - opt->price = curAndLenPrice; - opt->posPrev = cur + 1; - opt->backPrev = 0; - opt->prev1IsChar = True; - opt->prev2 = False; + unsigned offset = cur + len; + while (last < offset) + p->opt[++last].price = kInfinityPrice; + + // do + { + UInt32 price2; + COptimal *opt; + len--; + // price2 = price + GetPrice_Len_Rep_0(p, len, state2, posState2); + price2 = price + p->repLenEnc.prices[posState2][len - LZMA_MATCH_LEN_MIN]; + + opt = &p->opt[offset]; + // offset--; + if (price2 < opt->price) + { + opt->price = price2; + opt->len = len; + opt->dist = 0; + opt->extra = 1; + } + } + // while (len >= 3); } } } @@ -1361,87 +1501,105 @@ startLen = 2; /* speed optimization */ { - UInt32 repIndex; - for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++) - { - UInt32 lenTest; - UInt32 lenTestTemp; - UInt32 price; - const Byte *data2 = data - reps[repIndex] - 1; - if (data[0] != data2[0] || data[1] != data2[1]) - continue; - for (lenTest = 2; lenTest < numAvail && data[lenTest] == data2[lenTest]; lenTest++); - while (lenEnd < cur + lenTest) - p->opt[++lenEnd].price = kInfinityPrice; - lenTestTemp = lenTest; - price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState); - do - { - UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2]; - COptimal *opt = &p->opt[cur + lenTest]; - if (curAndLenPrice < opt->price) + // ---------- REP ---------- + unsigned repIndex = 0; // 17.old + // unsigned repIndex = IsLitState(state) ? 0 : 1; // 18.notused + for (; repIndex < LZMA_NUM_REPS; repIndex++) + { + unsigned len; + UInt32 price; + const Byte *data2 = data - reps[repIndex]; + if (data[0] != data2[0] || data[1] != data2[1]) + continue; + + for (len = 2; len < numAvail && data[len] == data2[len]; len++); + + // if (len < startLen) continue; // 18.new: speed optimization + + while (last < cur + len) + p->opt[++last].price = kInfinityPrice; { - opt->price = curAndLenPrice; - opt->posPrev = cur; - opt->backPrev = repIndex; - opt->prev1IsChar = False; + unsigned len2 = len; + price = repMatchPrice + GetPrice_PureRep(p, repIndex, state, posState); + do + { + UInt32 price2 = price + p->repLenEnc.prices[posState][(size_t)len2 - 2]; + COptimal *opt = &p->opt[cur + len2]; + if (price2 < opt->price) + { + opt->price = price2; + opt->len = len2; + opt->dist = repIndex; + opt->extra = 0; + } + } + while (--len2 >= 2); } - } - while (--lenTest >= 2); - lenTest = lenTestTemp; - - if (repIndex == 0) - startLen = lenTest + 1; - /* if (_maxMode) */ + if (repIndex == 0) startLen = len + 1; // 17.old + // startLen = len + 1; // 18.new + + /* if (_maxMode) */ { - UInt32 lenTest2 = lenTest + 1; - UInt32 limit = lenTest2 + p->numFastBytes; + // ---------- REP : LIT : REP_0 ---------- + // numFastBytes + 1 + numFastBytes + + unsigned len2 = len + 1; + unsigned limit = len2 + p->numFastBytes; if (limit > numAvailFull) limit = numAvailFull; - for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); - lenTest2 -= lenTest + 1; - if (lenTest2 >= 2) + + for (; len2 < limit && data[len2] == data2[len2]; len2++); + + len2 -= len; + if (len2 >= 3) { - UInt32 nextRepMatchPrice; - UInt32 state2 = kRepNextStates[state]; - UInt32 posStateNext = (position + lenTest) & p->pbMask; - UInt32 curAndLenCharPrice = - price + p->repLenEnc.prices[posState][lenTest - 2] + - GET_PRICE_0(p->isMatch[state2][posStateNext]) + - LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), - data[lenTest], data2[lenTest], p->ProbPrices); - state2 = kLiteralNextStates[state2]; - posStateNext = (position + lenTest + 1) & p->pbMask; - nextRepMatchPrice = curAndLenCharPrice + - GET_PRICE_1(p->isMatch[state2][posStateNext]) + - GET_PRICE_1(p->isRep[state2]); + unsigned state2 = kRepNextStates[state]; + unsigned posState2 = (position + len) & p->pbMask; + price += + p->repLenEnc.prices[posState][(size_t)len - 2] + + GET_PRICE_0(p->isMatch[state2][posState2]) + + LitEnc_Matched_GetPrice(LIT_PROBS(position + len, data[(size_t)len - 1]), + data[len], data2[len], p->ProbPrices); - /* for (; lenTest2 >= 2; lenTest2--) */ + // state2 = kLiteralNextStates[state2]; + state2 = kState_LitAfterRep; + posState2 = (posState2 + 1) & p->pbMask; + + + price += GetPrice_Rep_0(p, state2, posState2); { - UInt32 curAndLenPrice; - COptimal *opt; - UInt32 offset = cur + lenTest + 1 + lenTest2; - while (lenEnd < offset) - p->opt[++lenEnd].price = kInfinityPrice; - curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); - opt = &p->opt[offset]; - if (curAndLenPrice < opt->price) + unsigned offset = cur + len + len2; + while (last < offset) + p->opt[++last].price = kInfinityPrice; + // do { - opt->price = curAndLenPrice; - opt->posPrev = cur + lenTest + 1; - opt->backPrev = 0; - opt->prev1IsChar = True; - opt->prev2 = True; - opt->posPrev2 = cur; - opt->backPrev2 = repIndex; + unsigned price2; + COptimal *opt; + len2--; + // price2 = price + GetPrice_Len_Rep_0(p, len2, state2, posState2); + price2 = price + p->repLenEnc.prices[posState2][len2 - LZMA_MATCH_LEN_MIN]; + + opt = &p->opt[offset]; + // offset--; + if (price2 < opt->price) + { + opt->price = price2; + opt->len = len2; + opt->extra = (CExtra)(len + 1); + opt->dist = repIndex; + } } + // while (len2 >= 3); } } } + } } - } - /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */ + + + // ---------- MATCH ---------- + /* for (unsigned len = 2; len <= newLen; len++) */ if (newLen > numAvail) { newLen = numAvail; @@ -1449,132 +1607,148 @@ matches[numPairs] = newLen; numPairs += 2; } + if (newLen >= startLen) { UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]); - UInt32 offs, curBack, posSlot; - UInt32 lenTest; - while (lenEnd < cur + newLen) - p->opt[++lenEnd].price = kInfinityPrice; + UInt32 dist; + unsigned offs, posSlot, len; + while (last < cur + newLen) + p->opt[++last].price = kInfinityPrice; offs = 0; while (startLen > matches[offs]) offs += 2; - curBack = matches[offs + 1]; - GetPosSlot2(curBack, posSlot); - for (lenTest = /*2*/ startLen; ; lenTest++) - { - UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN]; - UInt32 lenToPosState = GetLenToPosState(lenTest); - COptimal *opt; - if (curBack < kNumFullDistances) - curAndLenPrice += p->distancesPrices[lenToPosState][curBack]; - else - curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask]; - - opt = &p->opt[cur + lenTest]; - if (curAndLenPrice < opt->price) + dist = matches[(size_t)offs + 1]; + + // if (dist >= kNumFullDistances) + GetPosSlot2(dist, posSlot); + + for (len = /*2*/ startLen; ; len++) + { + UInt32 price = normalMatchPrice + p->lenEnc.prices[posState][(size_t)len - LZMA_MATCH_LEN_MIN]; { - opt->price = curAndLenPrice; - opt->posPrev = cur; - opt->backPrev = curBack + LZMA_NUM_REPS; - opt->prev1IsChar = False; + COptimal *opt; + unsigned lenToPosState = len - 2; lenToPosState = GetLenToPosState2(lenToPosState); + if (dist < kNumFullDistances) + price += p->distancesPrices[lenToPosState][dist & (kNumFullDistances - 1)]; + else + price += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[dist & kAlignMask]; + + opt = &p->opt[cur + len]; + if (price < opt->price) + { + opt->price = price; + opt->len = len; + opt->dist = dist + LZMA_NUM_REPS; + opt->extra = 0; + } } - if (/*_maxMode && */lenTest == matches[offs]) + if (/*_maxMode && */ len == matches[offs]) { - /* Try Match + Literal + Rep0 */ - const Byte *data2 = data - curBack - 1; - UInt32 lenTest2 = lenTest + 1; - UInt32 limit = lenTest2 + p->numFastBytes; + // MATCH : LIT : REP_0 + + const Byte *data2 = data - dist - 1; + unsigned len2 = len + 1; + unsigned limit = len2 + p->numFastBytes; if (limit > numAvailFull) limit = numAvailFull; - for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++); - lenTest2 -= lenTest + 1; - if (lenTest2 >= 2) + + for (; len2 < limit && data[len2] == data2[len2]; len2++); + + len2 -= len; + + if (len2 >= 3) { - UInt32 nextRepMatchPrice; - UInt32 state2 = kMatchNextStates[state]; - UInt32 posStateNext = (position + lenTest) & p->pbMask; - UInt32 curAndLenCharPrice = curAndLenPrice + - GET_PRICE_0(p->isMatch[state2][posStateNext]) + - LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]), - data[lenTest], data2[lenTest], p->ProbPrices); - state2 = kLiteralNextStates[state2]; - posStateNext = (posStateNext + 1) & p->pbMask; - nextRepMatchPrice = curAndLenCharPrice + - GET_PRICE_1(p->isMatch[state2][posStateNext]) + - GET_PRICE_1(p->isRep[state2]); - - /* for (; lenTest2 >= 2; lenTest2--) */ + unsigned state2 = kMatchNextStates[state]; + unsigned posState2 = (position + len) & p->pbMask; + unsigned offset; + price += GET_PRICE_0(p->isMatch[state2][posState2]); + price += LitEnc_Matched_GetPrice(LIT_PROBS(position + len, data[(size_t)len - 1]), + data[len], data2[len], p->ProbPrices); + + // state2 = kLiteralNextStates[state2]; + state2 = kState_LitAfterMatch; + + posState2 = (posState2 + 1) & p->pbMask; + price += GetPrice_Rep_0(p, state2, posState2); + + offset = cur + len + len2; + while (last < offset) + p->opt[++last].price = kInfinityPrice; + // do { - UInt32 offset = cur + lenTest + 1 + lenTest2; - UInt32 curAndLenPrice; + UInt32 price2; COptimal *opt; - while (lenEnd < offset) - p->opt[++lenEnd].price = kInfinityPrice; - curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext); + len2--; + // price2 = price + GetPrice_Len_Rep_0(p, len2, state2, posState2); + price2 = price + p->repLenEnc.prices[posState2][len2 - LZMA_MATCH_LEN_MIN]; opt = &p->opt[offset]; - if (curAndLenPrice < opt->price) + // offset--; + if (price2 < opt->price) { - opt->price = curAndLenPrice; - opt->posPrev = cur + lenTest + 1; - opt->backPrev = 0; - opt->prev1IsChar = True; - opt->prev2 = True; - opt->posPrev2 = cur; - opt->backPrev2 = curBack + LZMA_NUM_REPS; + opt->price = price2; + opt->len = len2; + opt->extra = (CExtra)(len + 1); + opt->dist = dist + LZMA_NUM_REPS; } } + // while (len2 >= 3); } + offs += 2; if (offs == numPairs) break; - curBack = matches[offs + 1]; - if (curBack >= kNumFullDistances) - GetPosSlot2(curBack, posSlot); + dist = matches[(size_t)offs + 1]; + // if (dist >= kNumFullDistances) + GetPosSlot2(dist, posSlot); } } } } } + + #define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist)) -static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes) + + +static unsigned GetOptimumFast(CLzmaEnc *p) { - UInt32 numAvail, mainLen, mainDist, numPairs, repIndex, repLen, i; + UInt32 numAvail, mainDist; + unsigned mainLen, numPairs, repIndex, repLen, i; const Byte *data; - const UInt32 *matches; if (p->additionalOffset == 0) mainLen = ReadMatchDistances(p, &numPairs); else { - mainLen = p->longestMatchLength; + mainLen = p->longestMatchLen; numPairs = p->numPairs; } numAvail = p->numAvail; - *backRes = (UInt32)-1; + p->backRes = MARK_LIT; if (numAvail < 2) return 1; if (numAvail > LZMA_MATCH_LEN_MAX) numAvail = LZMA_MATCH_LEN_MAX; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; - repLen = repIndex = 0; + for (i = 0; i < LZMA_NUM_REPS; i++) { - UInt32 len; - const Byte *data2 = data - p->reps[i] - 1; + unsigned len; + const Byte *data2 = data - p->reps[i]; if (data[0] != data2[0] || data[1] != data2[1]) continue; for (len = 2; len < numAvail && data[len] == data2[len]; len++); if (len >= p->numFastBytes) { - *backRes = i; - MovePos(p, len - 1); + p->backRes = i; + MOVE_POS(p, len - 1) return len; } if (len > repLen) @@ -1584,84 +1758,152 @@ } } - matches = p->matches; if (mainLen >= p->numFastBytes) { - *backRes = matches[numPairs - 1] + LZMA_NUM_REPS; - MovePos(p, mainLen - 1); + p->backRes = p->matches[(size_t)numPairs - 1] + LZMA_NUM_REPS; + MOVE_POS(p, mainLen - 1) return mainLen; } mainDist = 0; /* for GCC */ + if (mainLen >= 2) { - mainDist = matches[numPairs - 1]; - while (numPairs > 2 && mainLen == matches[numPairs - 4] + 1) + mainDist = p->matches[(size_t)numPairs - 1]; + while (numPairs > 2) { - if (!ChangePair(matches[numPairs - 3], mainDist)) + UInt32 dist2; + if (mainLen != p->matches[(size_t)numPairs - 4] + 1) + break; + dist2 = p->matches[(size_t)numPairs - 3]; + if (!ChangePair(dist2, mainDist)) break; numPairs -= 2; - mainLen = matches[numPairs - 2]; - mainDist = matches[numPairs - 1]; + mainLen--; + mainDist = dist2; } if (mainLen == 2 && mainDist >= 0x80) mainLen = 1; } - if (repLen >= 2 && ( - (repLen + 1 >= mainLen) || - (repLen + 2 >= mainLen && mainDist >= (1 << 9)) || - (repLen + 3 >= mainLen && mainDist >= (1 << 15)))) + if (repLen >= 2) + if ( repLen + 1 >= mainLen + || (repLen + 2 >= mainLen && mainDist >= (1 << 9)) + || (repLen + 3 >= mainLen && mainDist >= (1 << 15))) { - *backRes = repIndex; - MovePos(p, repLen - 1); + p->backRes = repIndex; + MOVE_POS(p, repLen - 1) return repLen; } if (mainLen < 2 || numAvail <= 2) return 1; - p->longestMatchLength = ReadMatchDistances(p, &p->numPairs); - if (p->longestMatchLength >= 2) { - UInt32 newDistance = matches[p->numPairs - 1]; - if ((p->longestMatchLength >= mainLen && newDistance < mainDist) || - (p->longestMatchLength == mainLen + 1 && !ChangePair(mainDist, newDistance)) || - (p->longestMatchLength > mainLen + 1) || - (p->longestMatchLength + 1 >= mainLen && mainLen >= 3 && ChangePair(newDistance, mainDist))) - return 1; + unsigned len1 = ReadMatchDistances(p, &p->numPairs); + p->longestMatchLen = len1; + + if (len1 >= 2) + { + UInt32 newDist = p->matches[(size_t)p->numPairs - 1]; + if ( (len1 >= mainLen && newDist < mainDist) + || (len1 == mainLen + 1 && !ChangePair(mainDist, newDist)) + || (len1 > mainLen + 1) + || (len1 + 1 >= mainLen && mainLen >= 3 && ChangePair(newDist, mainDist))) + return 1; + } } data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1; + for (i = 0; i < LZMA_NUM_REPS; i++) { - UInt32 len, limit; - const Byte *data2 = data - p->reps[i] - 1; + unsigned len, limit; + const Byte *data2 = data - p->reps[i]; if (data[0] != data2[0] || data[1] != data2[1]) continue; limit = mainLen - 1; - for (len = 2; len < limit && data[len] == data2[len]; len++); - if (len >= limit) - return 1; + for (len = 2;; len++) + { + if (len >= limit) + return 1; + if (data[len] != data2[len]) + break; + } + } + + p->backRes = mainDist + LZMA_NUM_REPS; + if (mainLen != 2) + { + MOVE_POS(p, mainLen - 2) } - *backRes = mainDist + LZMA_NUM_REPS; - MovePos(p, mainLen - 2); return mainLen; } -static void WriteEndMarker(CLzmaEnc *p, UInt32 posState) + + + +static void WriteEndMarker(CLzmaEnc *p, unsigned posState) { - UInt32 len; - RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); - RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + UInt32 range; + range = p->rc.range; + { + UInt32 ttt, newBound; + CLzmaProb *prob = &p->isMatch[p->state][posState]; + RC_BIT_PRE(&p->rc, prob) + RC_BIT_1(&p->rc, prob) + prob = &p->isRep[p->state]; + RC_BIT_PRE(&p->rc, prob) + RC_BIT_0(&p->rc, prob) + } p->state = kMatchNextStates[p->state]; - len = LZMA_MATCH_LEN_MIN; - LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); - RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1); - RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits); - RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); + + p->rc.range = range; + LenEnc_Encode(&p->lenProbs, &p->rc, 0, posState); + range = p->rc.range; + + { + // RcTree_Encode_PosSlot(&p->rc, p->posSlotEncoder[0], (1 << kNumPosSlotBits) - 1); + CLzmaProb *probs = p->posSlotEncoder[0]; + unsigned m = 1; + do + { + UInt32 ttt, newBound; + RC_BIT_PRE(p, probs + m) + RC_BIT_1(&p->rc, probs + m); + m = (m << 1) + 1; + } + while (m < (1 << kNumPosSlotBits)); + } + { + // RangeEnc_EncodeDirectBits(&p->rc, ((UInt32)1 << (30 - kNumAlignBits)) - 1, 30 - kNumAlignBits); UInt32 range = p->range; + unsigned numBits = 30 - kNumAlignBits; + do + { + range >>= 1; + p->rc.low += range; + RC_NORM(&p->rc) + } + while (--numBits); + } + + { + // RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask); + CLzmaProb *probs = p->posAlignEncoder; + unsigned m = 1; + do + { + UInt32 ttt, newBound; + RC_BIT_PRE(p, probs + m) + RC_BIT_1(&p->rc, probs + m); + m = (m << 1) + 1; + } + while (m < kAlignTableSize); + } + p->rc.range = range; } + static SRes CheckErrors(CLzmaEnc *p) { if (p->result != SZ_OK) @@ -1675,7 +1917,8 @@ return p->result; } -static SRes Flush(CLzmaEnc *p, UInt32 nowPos) + +MY_NO_INLINE static SRes Flush(CLzmaEnc *p, UInt32 nowPos) { /* ReleaseMFStream(); */ p->finished = True; @@ -1686,48 +1929,108 @@ return CheckErrors(p); } + + static void FillAlignPrices(CLzmaEnc *p) { - UInt32 i; - for (i = 0; i < kAlignTableSize; i++) - p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + unsigned i; + const CProbPrice *ProbPrices = p->ProbPrices; + const CLzmaProb *probs = p->posAlignEncoder; p->alignPriceCount = 0; + for (i = 0; i < kAlignTableSize / 2; i++) + { + UInt32 price = 0; + unsigned symbol = i; + unsigned m = 1; + unsigned bit; + UInt32 prob; + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) + bit; + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) + bit; + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[m], bit); m = (m << 1) + bit; + prob = probs[m]; + p->alignPrices[i ] = price + GET_PRICEa_0(prob); + p->alignPrices[i + 8] = price + GET_PRICEa_1(prob); + // p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices); + } } + static void FillDistancesPrices(CLzmaEnc *p) { UInt32 tempPrices[kNumFullDistances]; - UInt32 i, lenToPosState; + unsigned i, lenToPosState; + + const CProbPrice *ProbPrices = p->ProbPrices; + p->matchPriceCount = 0; + for (i = kStartPosModelIndex; i < kNumFullDistances; i++) { - UInt32 posSlot = GetPosSlot1(i); - UInt32 footerBits = ((posSlot >> 1) - 1); - UInt32 base = ((2 | (posSlot & 1)) << footerBits); - tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices); + unsigned posSlot = GetPosSlot1(i); + unsigned footerBits = ((posSlot >> 1) - 1); + unsigned base = ((2 | (posSlot & 1)) << footerBits); + // tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base, footerBits, i - base, p->ProbPrices); + + const CLzmaProb *probs = p->posEncoders + base; + UInt32 price = 0; + unsigned m = 1; + unsigned symbol = i - base; + do + { + unsigned bit = symbol & 1; + symbol >>= 1; + price += GET_PRICEa(probs[m], bit); + m = (m << 1) + bit; + } + while (--footerBits); + tempPrices[i] = price; } for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++) { - UInt32 posSlot; + unsigned posSlot; const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState]; UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState]; - for (posSlot = 0; posSlot < p->distTableSize; posSlot++) - posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); - for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++) - posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); + unsigned distTableSize = p->distTableSize; + const CLzmaProb *probs = encoder; + for (posSlot = 0; posSlot < distTableSize; posSlot += 2) + { + // posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices); + UInt32 price = 0; + unsigned bit; + unsigned symbol = (posSlot >> 1) + (1 << (kNumPosSlotBits - 1)); + UInt32 prob; + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[symbol], bit); + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[symbol], bit); + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[symbol], bit); + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[symbol], bit); + bit = symbol & 1; symbol >>= 1; price += GET_PRICEa(probs[symbol], bit); + prob = probs[(posSlot >> 1) + (1 << (kNumPosSlotBits - 1))]; + posSlotPrices[posSlot ] = price + GET_PRICEa_0(prob); + posSlotPrices[posSlot + 1] = price + GET_PRICEa_1(prob); + } + for (posSlot = kEndPosModelIndex; posSlot < distTableSize; posSlot++) + posSlotPrices[posSlot] += ((UInt32)(((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits); { UInt32 *distancesPrices = p->distancesPrices[lenToPosState]; - UInt32 i; - for (i = 0; i < kStartPosModelIndex; i++) - distancesPrices[i] = posSlotPrices[i]; - for (; i < kNumFullDistances; i++) - distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i]; + { + distancesPrices[0] = posSlotPrices[0]; + distancesPrices[1] = posSlotPrices[1]; + distancesPrices[2] = posSlotPrices[2]; + distancesPrices[3] = posSlotPrices[3]; + } + for (i = 4; i < kNumFullDistances; i += 2) + { + UInt32 slotPrice = posSlotPrices[GetPosSlot1(i)]; + distancesPrices[i ] = slotPrice + tempPrices[i]; + distancesPrices[i + 1] = slotPrice + tempPrices[i + 1]; + } } } - p->matchPriceCount = 0; } + + void LzmaEnc_Construct(CLzmaEnc *p) { RangeEnc_Construct(&p->rc); @@ -1751,26 +2054,27 @@ LzmaEnc_InitPriceTables(p->ProbPrices); p->litProbs = NULL; p->saveState.litProbs = NULL; + } -CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc) +CLzmaEncHandle LzmaEnc_Create(ISzAllocPtr alloc) { void *p; - p = alloc->Alloc(alloc, sizeof(CLzmaEnc)); + p = ISzAlloc_Alloc(alloc, sizeof(CLzmaEnc)); if (p) LzmaEnc_Construct((CLzmaEnc *)p); return p; } -void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc) +void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAllocPtr alloc) { - alloc->Free(alloc, p->litProbs); - alloc->Free(alloc, p->saveState.litProbs); + ISzAlloc_Free(alloc, p->litProbs); + ISzAlloc_Free(alloc, p->saveState.litProbs); p->litProbs = NULL; p->saveState.litProbs = NULL; } -void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig) +void LzmaEnc_Destruct(CLzmaEnc *p, ISzAllocPtr alloc, ISzAllocPtr allocBig) { #ifndef _7ZIP_ST MatchFinderMt_Destruct(&p->matchFinderMt, allocBig); @@ -1781,13 +2085,14 @@ RangeEnc_Free(&p->rc, alloc); } -void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig) +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAllocPtr alloc, ISzAllocPtr allocBig) { LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig); - alloc->Free(alloc, p); + ISzAlloc_Free(alloc, p); } -static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize) + +static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, UInt32 maxPackSize, UInt32 maxUnpackSize) { UInt32 nowPos32, startPos32; if (p->needInit) @@ -1805,13 +2110,13 @@ if (p->nowPos64 == 0) { - UInt32 numPairs; + unsigned numPairs; Byte curByte; if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) return Flush(p, nowPos32); ReadMatchDistances(p, &numPairs); - RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0); - p->state = kLiteralNextStates[p->state]; + RangeEnc_EncodeBit_0(&p->rc, &p->isMatch[kState_Start][0]); + // p->state = kLiteralNextStates[p->state]; curByte = *(p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset); LitEnc_Encode(&p->rc, p->litProbs, curByte); p->additionalOffset--; @@ -1819,109 +2124,225 @@ } if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0) + for (;;) { - UInt32 pos, len, posState; - + UInt32 dist; + unsigned len, posState; + UInt32 range, ttt, newBound; + CLzmaProb *probs; + if (p->fastMode) - len = GetOptimumFast(p, &pos); + len = GetOptimumFast(p); else - len = GetOptimum(p, nowPos32, &pos); + { + unsigned oci = p->optCur; + if (p->optEnd == oci) + len = GetOptimum(p, nowPos32); + else + { + const COptimal *opt = &p->opt[oci]; + len = opt->len; + p->backRes = opt->dist; + p->optCur = oci + 1; + } + } + + posState = (unsigned)nowPos32 & p->pbMask; + range = p->rc.range; + probs = &p->isMatch[p->state][posState]; + + RC_BIT_PRE(&p->rc, probs) + + dist = p->backRes; #ifdef SHOW_STAT2 - printf("\n pos = %4X, len = %u pos = %u", nowPos32, len, pos); + printf("\n pos = %6X, len = %3u pos = %6u", nowPos32, len, dist); #endif - posState = nowPos32 & p->pbMask; - if (len == 1 && pos == (UInt32)-1) + if (dist == MARK_LIT) { Byte curByte; - CLzmaProb *probs; const Byte *data; + unsigned state; - RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0); + RC_BIT_0(&p->rc, probs); + p->rc.range = range; data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset; - curByte = *data; probs = LIT_PROBS(nowPos32, *(data - 1)); - if (IsCharState(p->state)) + curByte = *data; + state = p->state; + p->state = kLiteralNextStates[state]; + if (IsLitState(state)) LitEnc_Encode(&p->rc, probs, curByte); else - LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1)); - p->state = kLiteralNextStates[p->state]; + LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0])); } else { - RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1); - if (pos < LZMA_NUM_REPS) + RC_BIT_1(&p->rc, probs); + probs = &p->isRep[p->state]; + RC_BIT_PRE(&p->rc, probs) + + if (dist < LZMA_NUM_REPS) { - RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1); - if (pos == 0) + RC_BIT_1(&p->rc, probs); + probs = &p->isRepG0[p->state]; + RC_BIT_PRE(&p->rc, probs) + if (dist == 0) { - RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0); - RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1)); + RC_BIT_0(&p->rc, probs); + probs = &p->isRep0Long[p->state][posState]; + RC_BIT_PRE(&p->rc, probs) + if (len != 1) + { + RC_BIT_1_BASE(&p->rc, probs); + } + else + { + RC_BIT_0_BASE(&p->rc, probs); + p->state = kShortRepNextStates[p->state]; + } } else { - UInt32 distance = p->reps[pos]; - RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1); - if (pos == 1) - RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0); + RC_BIT_1(&p->rc, probs); + probs = &p->isRepG1[p->state]; + RC_BIT_PRE(&p->rc, probs) + if (dist == 1) + { + RC_BIT_0_BASE(&p->rc, probs); + dist = p->reps[1]; + } else { - RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1); - RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2); - if (pos == 3) + RC_BIT_1(&p->rc, probs); + probs = &p->isRepG2[p->state]; + RC_BIT_PRE(&p->rc, probs) + if (dist == 2) + { + RC_BIT_0_BASE(&p->rc, probs); + dist = p->reps[2]; + } + else + { + RC_BIT_1_BASE(&p->rc, probs); + dist = p->reps[3]; p->reps[3] = p->reps[2]; + } p->reps[2] = p->reps[1]; } p->reps[1] = p->reps[0]; - p->reps[0] = distance; + p->reps[0] = dist; } - if (len == 1) - p->state = kShortRepNextStates[p->state]; - else + + RC_NORM(&p->rc) + + p->rc.range = range; + + if (len != 1) { - LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); + LenEnc_Encode(&p->repLenProbs, &p->rc, len - LZMA_MATCH_LEN_MIN, posState); + if (!p->fastMode) + if (--p->repLenEnc.counters[posState] == 0) + LenPriceEnc_UpdateTable(&p->repLenEnc, posState, &p->repLenProbs, p->ProbPrices); + p->state = kRepNextStates[p->state]; } } else { - UInt32 posSlot; - RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0); + unsigned posSlot; + RC_BIT_0(&p->rc, probs); + p->rc.range = range; p->state = kMatchNextStates[p->state]; - LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices); - pos -= LZMA_NUM_REPS; - GetPosSlot(pos, posSlot); - RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot); + + LenEnc_Encode(&p->lenProbs, &p->rc, len - LZMA_MATCH_LEN_MIN, posState); + if (!p->fastMode) + if (--p->lenEnc.counters[posState] == 0) + LenPriceEnc_UpdateTable(&p->lenEnc, posState, &p->lenProbs, p->ProbPrices); + + dist -= LZMA_NUM_REPS; + p->reps[3] = p->reps[2]; + p->reps[2] = p->reps[1]; + p->reps[1] = p->reps[0]; + p->reps[0] = dist + 1; - if (posSlot >= kStartPosModelIndex) + p->matchPriceCount++; + GetPosSlot(dist, posSlot); + // RcTree_Encode_PosSlot(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], posSlot); + { + UInt32 symbol = posSlot + (1 << kNumPosSlotBits); + range = p->rc.range; + probs = p->posSlotEncoder[GetLenToPosState(len)]; + do + { + CLzmaProb *prob = probs + (symbol >> kNumPosSlotBits); + UInt32 bit = (symbol >> (kNumPosSlotBits - 1)) & 1; + symbol <<= 1; + RC_BIT(&p->rc, prob, bit); + } + while (symbol < (1 << kNumPosSlotBits * 2)); + p->rc.range = range; + } + + if (dist >= kStartPosModelIndex) { - UInt32 footerBits = ((posSlot >> 1) - 1); - UInt32 base = ((2 | (posSlot & 1)) << footerBits); - UInt32 posReduced = pos - base; + unsigned footerBits = ((posSlot >> 1) - 1); - if (posSlot < kEndPosModelIndex) - RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced); + if (dist < kNumFullDistances) + { + unsigned base = ((2 | (posSlot & 1)) << footerBits); + RcTree_ReverseEncode(&p->rc, p->posEncoders + base, footerBits, dist - base); + } else { - RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); - RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); - p->alignPriceCount++; + UInt32 pos2 = (dist | 0xF) << (32 - footerBits); + range = p->rc.range; + // RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits); + /* + do + { + range >>= 1; + p->rc.low += range & (0 - ((dist >> --footerBits) & 1)); + RC_NORM(&p->rc) + } + while (footerBits > kNumAlignBits); + */ + do + { + range >>= 1; + p->rc.low += range & (0 - (pos2 >> 31)); + pos2 += pos2; + RC_NORM(&p->rc) + } + while (pos2 != 0xF0000000); + + + // RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask); + + { + unsigned m = 1; + unsigned bit; + bit = dist & 1; dist >>= 1; RC_BIT(&p->rc, p->posAlignEncoder + m, bit); m = (m << 1) + bit; + bit = dist & 1; dist >>= 1; RC_BIT(&p->rc, p->posAlignEncoder + m, bit); m = (m << 1) + bit; + bit = dist & 1; dist >>= 1; RC_BIT(&p->rc, p->posAlignEncoder + m, bit); m = (m << 1) + bit; + bit = dist & 1; RC_BIT(&p->rc, p->posAlignEncoder + m, bit); + p->rc.range = range; + p->alignPriceCount++; + } } } - p->reps[3] = p->reps[2]; - p->reps[2] = p->reps[1]; - p->reps[1] = p->reps[0]; - p->reps[0] = pos; - p->matchPriceCount++; } } - p->additionalOffset -= len; + nowPos32 += len; + p->additionalOffset -= len; + if (p->additionalOffset == 0) { UInt32 processed; + if (!p->fastMode) { if (p->matchPriceCount >= (1 << 7)) @@ -1929,13 +2350,15 @@ if (p->alignPriceCount >= kAlignTableSize) FillAlignPrices(p); } + if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0) break; processed = nowPos32 - startPos32; - if (useLimits) + + if (maxPackSize) { - if (processed + kNumOpts + 300 >= maxUnpackSize || - RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize) + if (processed + kNumOpts + 300 >= maxUnpackSize + || RangeEnc_GetProcessed_sizet(&p->rc) + kPackReserve >= maxPackSize) break; } else if (processed >= (1 << 17)) @@ -1945,13 +2368,16 @@ } } } + p->nowPos64 += nowPos32 - startPos32; return Flush(p, nowPos32); } + + #define kBigHashDicLimit ((UInt32)1 << 24) -static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig) { UInt32 beforeSize = kNumOpts; if (!RangeEnc_Alloc(&p->rc, alloc)) @@ -1966,8 +2392,8 @@ if (!p->litProbs || !p->saveState.litProbs || p->lclp != lclp) { LzmaEnc_FreeLits(p, alloc); - p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); - p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + p->litProbs = (CLzmaProb *)ISzAlloc_Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); + p->saveState.litProbs = (CLzmaProb *)ISzAlloc_Alloc(alloc, ((UInt32)0x300 << lclp) * sizeof(CLzmaProb)); if (!p->litProbs || !p->saveState.litProbs) { LzmaEnc_FreeLits(p, alloc); @@ -1985,8 +2411,13 @@ #ifndef _7ZIP_ST if (p->mtMode) { - RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig)); + RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, + LZMA_MATCH_LEN_MAX + + 1 /* 18.04 */ + , allocBig)); p->matchFinderObj = &p->matchFinderMt; + p->matchFinderBase.bigHash = (Byte)( + (p->dictSize > kBigHashDicLimit && p->matchFinderBase.hashMask >= 0xFFFFFF) ? 1 : 0); MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder); } else @@ -2003,17 +2434,21 @@ void LzmaEnc_Init(CLzmaEnc *p) { - UInt32 i; + unsigned i; p->state = 0; - for (i = 0 ; i < LZMA_NUM_REPS; i++) - p->reps[i] = 0; + p->reps[0] = + p->reps[1] = + p->reps[2] = + p->reps[3] = 1; RangeEnc_Init(&p->rc); + for (i = 0; i < (1 << kNumAlignBits); i++) + p->posAlignEncoder[i] = kProbInitValue; for (i = 0; i < kNumStates; i++) { - UInt32 j; + unsigned j; for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++) { p->isMatch[i][j] = kProbInitValue; @@ -2026,38 +2461,37 @@ } { - UInt32 num = (UInt32)0x300 << (p->lp + p->lc); - CLzmaProb *probs = p->litProbs; - for (i = 0; i < num; i++) - probs[i] = kProbInitValue; - } - - { for (i = 0; i < kNumLenToPosStates; i++) { CLzmaProb *probs = p->posSlotEncoder[i]; - UInt32 j; + unsigned j; for (j = 0; j < (1 << kNumPosSlotBits); j++) probs[j] = kProbInitValue; } } { - for (i = 0; i < kNumFullDistances - kEndPosModelIndex; i++) + for (i = 0; i < kNumFullDistances; i++) p->posEncoders[i] = kProbInitValue; } - LenEnc_Init(&p->lenEnc.p); - LenEnc_Init(&p->repLenEnc.p); + { + UInt32 num = (UInt32)0x300 << (p->lp + p->lc); + UInt32 k; + CLzmaProb *probs = p->litProbs; + for (k = 0; k < num; k++) + probs[k] = kProbInitValue; + } - for (i = 0; i < (1 << kNumAlignBits); i++) - p->posAlignEncoder[i] = kProbInitValue; - p->optimumEndIndex = 0; - p->optimumCurrentIndex = 0; + LenEnc_Init(&p->lenProbs); + LenEnc_Init(&p->repLenProbs); + + p->optEnd = 0; + p->optCur = 0; p->additionalOffset = 0; p->pbMask = (1 << p->pb) - 1; - p->lpMask = (1 << p->lp) - 1; + p->lpMask = ((UInt32)0x100 << p->lp) - ((unsigned)0x100 >> p->lc); } void LzmaEnc_InitPrices(CLzmaEnc *p) @@ -2071,14 +2505,14 @@ p->lenEnc.tableSize = p->repLenEnc.tableSize = p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN; - LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices); - LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, &p->lenProbs, p->ProbPrices); + LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, &p->repLenProbs, p->ProbPrices); } -static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) +static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig) { - UInt32 i; - for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++) + unsigned i; + for (i = kEndPosModelIndex / 2; i < kDicLogSizeMax; i++) if (p->dictSize <= ((UInt32)1 << i)) break; p->distTableSize = i * 2; @@ -2093,7 +2527,7 @@ } static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, - ISzAlloc *alloc, ISzAlloc *allocBig) + ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; @@ -2104,7 +2538,7 @@ SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp, ISeqInStream *inStream, UInt32 keepWindowSize, - ISzAlloc *alloc, ISzAlloc *allocBig) + ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; p->matchFinderBase.stream = inStream; @@ -2120,12 +2554,13 @@ } SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen, - UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig) + UInt32 keepWindowSize, ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)pp; LzmaEnc_SetInputBuf(p, src, srcLen); p->needInit = 1; + LzmaEnc_SetDataSize(pp, srcLen); return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig); } @@ -2143,15 +2578,15 @@ typedef struct { - ISeqOutStream funcTable; + ISeqOutStream vt; Byte *data; SizeT rem; Bool overflow; -} CSeqOutStreamBuf; +} CLzmaEnc_SeqOutStreamBuf; -static size_t MyWrite(void *pp, const void *data, size_t size) +static size_t SeqOutStreamBuf_Write(const ISeqOutStream *pp, const void *data, size_t size) { - CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp; + CLzmaEnc_SeqOutStreamBuf *p = CONTAINER_FROM_VTBL(pp, CLzmaEnc_SeqOutStreamBuf, vt); if (p->rem < size) { size = p->rem; @@ -2184,9 +2619,9 @@ CLzmaEnc *p = (CLzmaEnc *)pp; UInt64 nowPos64; SRes res; - CSeqOutStreamBuf outStream; + CLzmaEnc_SeqOutStreamBuf outStream; - outStream.funcTable.Write = MyWrite; + outStream.vt.Write = SeqOutStreamBuf_Write; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; @@ -2198,11 +2633,15 @@ if (reInit) LzmaEnc_Init(p); LzmaEnc_InitPrices(p); + nowPos64 = p->nowPos64; RangeEnc_Init(&p->rc); - p->rc.outStream = &outStream.funcTable; + p->rc.outStream = &outStream.vt; + + if (desiredPackSize == 0) + return SZ_ERROR_OUTPUT_EOF; - res = LzmaEnc_CodeOneBlock(p, True, desiredPackSize, *unpackSize); + res = LzmaEnc_CodeOneBlock(p, desiredPackSize, *unpackSize); *unpackSize = (UInt32)(p->nowPos64 - nowPos64); *destLen -= outStream.rem; @@ -2225,12 +2664,12 @@ for (;;) { - res = LzmaEnc_CodeOneBlock(p, False, 0, 0); + res = LzmaEnc_CodeOneBlock(p, 0, 0); if (res != SZ_OK || p->finished) break; if (progress) { - res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); + res = ICompressProgress_Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc)); if (res != SZ_OK) { res = SZ_ERROR_PROGRESS; @@ -2242,7 +2681,7 @@ LzmaEnc_Finish(p); /* - if (res == S_OK && !Inline_MatchFinder_IsFinishedOK(&p->matchFinderBase)) + if (res == SZ_OK && !Inline_MatchFinder_IsFinishedOK(&p->matchFinderBase)) res = SZ_ERROR_FAIL; } */ @@ -2252,7 +2691,7 @@ SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress, - ISzAlloc *alloc, ISzAlloc *allocBig) + ISzAllocPtr alloc, ISzAllocPtr allocBig) { RINOK(LzmaEnc_Prepare(pp, outStream, inStream, alloc, allocBig)); return LzmaEnc_Encode2((CLzmaEnc *)pp, progress); @@ -2287,21 +2726,27 @@ } +unsigned LzmaEnc_IsWriteEndMark(CLzmaEncHandle pp) +{ + return ((CLzmaEnc *)pp)->writeEndMark; +} + + SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, - int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) + int writeEndMark, ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig) { SRes res; CLzmaEnc *p = (CLzmaEnc *)pp; - CSeqOutStreamBuf outStream; + CLzmaEnc_SeqOutStreamBuf outStream; - outStream.funcTable.Write = MyWrite; + outStream.vt.Write = SeqOutStreamBuf_Write; outStream.data = dest; outStream.rem = *destLen; outStream.overflow = False; p->writeEndMark = writeEndMark; - p->rc.outStream = &outStream.funcTable; + p->rc.outStream = &outStream.vt; res = LzmaEnc_MemPrepare(pp, src, srcLen, 0, alloc, allocBig); @@ -2321,7 +2766,7 @@ SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, - ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig) + ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig) { CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc); SRes res; diff -Nru leanify-0.4.3/lib/LZMA/LzmaEnc.h leanify-0.4.3+git20181014/lib/LZMA/LzmaEnc.h --- leanify-0.4.3/lib/LZMA/LzmaEnc.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaEnc.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* LzmaEnc.h -- LZMA Encoder -2013-01-18 : Igor Pavlov : Public domain */ +2017-07-27 : Igor Pavlov : Public domain */ #ifndef __LZMA_ENC_H #define __LZMA_ENC_H @@ -12,12 +12,10 @@ typedef struct _CLzmaEncProps { - int level; /* 0 <= level <= 9 */ + int level; /* 0 <= level <= 9 */ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version - (1 << 12) <= dictSize <= (1 << 30) for 64-bit version - default = (1 << 24) */ - UInt64 reduceSize; /* estimated size of data that will be compressed. default = 0xFFFFFFFF. - Encoder uses this value to reduce dictionary size */ + (1 << 12) <= dictSize <= (3 << 29) for 64-bit version + default = (1 << 24) */ int lc; /* 0 <= lc <= 8, default = 3 */ int lp; /* 0 <= lp <= 4, default = 0 */ int pb; /* 0 <= pb <= 4, default = 2 */ @@ -25,9 +23,12 @@ int fb; /* 5 <= fb <= 273, default = 32 */ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */ int numHashBytes; /* 2, 3 or 4, default = 4 */ - UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ + UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */ int numThreads; /* 1 or 2, default = 2 */ + + UInt64 reduceSize; /* estimated size of data that will be compressed. default = (UInt64)(Int64)-1. + Encoder uses this value to reduce dictionary size */ } CLzmaEncProps; void LzmaEncProps_Init(CLzmaEncProps *p); @@ -37,41 +38,38 @@ /* ---------- CLzmaEncHandle Interface ---------- */ -/* LzmaEnc_* functions can return the following exit codes: -Returns: +/* LzmaEnc* functions can return the following exit codes: +SRes: SZ_OK - OK SZ_ERROR_MEM - Memory allocation error SZ_ERROR_PARAM - Incorrect paramater in props - SZ_ERROR_WRITE - Write callback error. + SZ_ERROR_WRITE - ISeqOutStream write callback error + SZ_ERROR_OUTPUT_EOF - output buffer overflow - version with (Byte *) output SZ_ERROR_PROGRESS - some break from progress callback - SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) + SZ_ERROR_THREAD - error in multithreading functions (only for Mt version) */ typedef void * CLzmaEncHandle; -CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc); -void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig); +CLzmaEncHandle LzmaEnc_Create(ISzAllocPtr alloc); +void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAllocPtr alloc, ISzAllocPtr allocBig); + SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props); +void LzmaEnc_SetDataSize(CLzmaEncHandle p, UInt64 expectedDataSiize); SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size); +unsigned LzmaEnc_IsWriteEndMark(CLzmaEncHandle p); + SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream, - ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig); SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, - int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + int writeEndMark, ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig); -/* ---------- One Call Interface ---------- */ -/* LzmaEncode -Return code: - SZ_OK - OK - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_PARAM - Incorrect paramater - SZ_ERROR_OUTPUT_EOF - output buffer overflow - SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) -*/ +/* ---------- One Call Interface ---------- */ SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen, const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark, - ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig); + ICompressProgress *progress, ISzAllocPtr alloc, ISzAllocPtr allocBig); EXTERN_C_END diff -Nru leanify-0.4.3/lib/LZMA/LzmaLib.c leanify-0.4.3+git20181014/lib/LZMA/LzmaLib.c --- leanify-0.4.3/lib/LZMA/LzmaLib.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaLib.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* LzmaLib.c -- LZMA library wrapper -2015-06-13 : Igor Pavlov : Public domain */ - -#include "Alloc.h" -#include "LzmaDec.h" -#include "LzmaEnc.h" -#include "LzmaLib.h" - -MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, - unsigned char *outProps, size_t *outPropsSize, - int level, /* 0 <= level <= 9, default = 5 */ - unsigned dictSize, /* use (1 << N) or (3 << N). 4 KB < dictSize <= 128 MB */ - int lc, /* 0 <= lc <= 8, default = 3 */ - int lp, /* 0 <= lp <= 4, default = 0 */ - int pb, /* 0 <= pb <= 4, default = 2 */ - int fb, /* 5 <= fb <= 273, default = 32 */ - int numThreads /* 1 or 2, default = 2 */ -) -{ - CLzmaEncProps props; - LzmaEncProps_Init(&props); - props.level = level; - props.dictSize = dictSize; - props.lc = lc; - props.lp = lp; - props.pb = pb; - props.fb = fb; - props.numThreads = numThreads; - - return LzmaEncode(dest, destLen, src, srcLen, &props, outProps, outPropsSize, 1, - NULL, &g_Alloc, &g_Alloc); -} - - -MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t *srcLen, - const unsigned char *props, size_t propsSize) -{ - ELzmaStatus status; - return LzmaDecode(dest, destLen, src, srcLen, props, (unsigned)propsSize, LZMA_FINISH_ANY, &status, &g_Alloc); -} diff -Nru leanify-0.4.3/lib/LZMA/LzmaLib.h leanify-0.4.3+git20181014/lib/LZMA/LzmaLib.h --- leanify-0.4.3/lib/LZMA/LzmaLib.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/LzmaLib.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,131 +0,0 @@ -/* LzmaLib.h -- LZMA library interface -2013-01-18 : Igor Pavlov : Public domain */ - -#ifndef __LZMA_LIB_H -#define __LZMA_LIB_H - -#include "7zTypes.h" - -EXTERN_C_BEGIN - -#define MY_STDAPI int MY_STD_CALL - -#define LZMA_PROPS_SIZE 5 - -/* -RAM requirements for LZMA: - for compression: (dictSize * 11.5 + 6 MB) + state_size - for decompression: dictSize + state_size - state_size = (4 + (1.5 << (lc + lp))) KB - by default (lc=3, lp=0), state_size = 16 KB. - -LZMA properties (5 bytes) format - Offset Size Description - 0 1 lc, lp and pb in encoded form. - 1 4 dictSize (little endian). -*/ - -/* -LzmaCompress ------------- - -outPropsSize - - In: the pointer to the size of outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. - Out: the pointer to the size of written properties in outProps buffer; *outPropsSize = LZMA_PROPS_SIZE = 5. - - LZMA Encoder will use defult values for any parameter, if it is - -1 for any from: level, loc, lp, pb, fb, numThreads - 0 for dictSize - -level - compression level: 0 <= level <= 9; - - level dictSize algo fb - 0: 16 KB 0 32 - 1: 64 KB 0 32 - 2: 256 KB 0 32 - 3: 1 MB 0 32 - 4: 4 MB 0 32 - 5: 16 MB 1 32 - 6: 32 MB 1 32 - 7+: 64 MB 1 64 - - The default value for "level" is 5. - - algo = 0 means fast method - algo = 1 means normal method - -dictSize - The dictionary size in bytes. The maximum value is - 128 MB = (1 << 27) bytes for 32-bit version - 1 GB = (1 << 30) bytes for 64-bit version - The default value is 16 MB = (1 << 24) bytes. - It's recommended to use the dictionary that is larger than 4 KB and - that can be calculated as (1 << N) or (3 << N) sizes. - -lc - The number of literal context bits (high bits of previous literal). - It can be in the range from 0 to 8. The default value is 3. - Sometimes lc=4 gives the gain for big files. - -lp - The number of literal pos bits (low bits of current position for literals). - It can be in the range from 0 to 4. The default value is 0. - The lp switch is intended for periodical data when the period is equal to 2^lp. - For example, for 32-bit (4 bytes) periodical data you can use lp=2. Often it's - better to set lc=0, if you change lp switch. - -pb - The number of pos bits (low bits of current position). - It can be in the range from 0 to 4. The default value is 2. - The pb switch is intended for periodical data when the period is equal 2^pb. - -fb - Word size (the number of fast bytes). - It can be in the range from 5 to 273. The default value is 32. - Usually, a big number gives a little bit better compression ratio and - slower compression process. - -numThreads - The number of thereads. 1 or 2. The default value is 2. - Fast mode (algo = 0) can use only 1 thread. - -Out: - destLen - processed output size -Returns: - SZ_OK - OK - SZ_ERROR_MEM - Memory allocation error - SZ_ERROR_PARAM - Incorrect paramater - SZ_ERROR_OUTPUT_EOF - output buffer overflow - SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version) -*/ - -MY_STDAPI LzmaCompress(unsigned char *dest, size_t *destLen, const unsigned char *src, size_t srcLen, - unsigned char *outProps, size_t *outPropsSize, /* *outPropsSize must be = 5 */ - int level, /* 0 <= level <= 9, default = 5 */ - unsigned dictSize, /* default = (1 << 24) */ - int lc, /* 0 <= lc <= 8, default = 3 */ - int lp, /* 0 <= lp <= 4, default = 0 */ - int pb, /* 0 <= pb <= 4, default = 2 */ - int fb, /* 5 <= fb <= 273, default = 32 */ - int numThreads /* 1 or 2, default = 2 */ - ); - -/* -LzmaUncompress --------------- -In: - dest - output data - destLen - output data size - src - input data - srcLen - input data size -Out: - destLen - processed output size - srcLen - processed input size -Returns: - SZ_OK - OK - SZ_ERROR_DATA - Data error - SZ_ERROR_MEM - Memory allocation arror - SZ_ERROR_UNSUPPORTED - Unsupported properties - SZ_ERROR_INPUT_EOF - it needs more bytes in input buffer (src) -*/ - -MY_STDAPI LzmaUncompress(unsigned char *dest, size_t *destLen, const unsigned char *src, SizeT *srcLen, - const unsigned char *props, size_t propsSize); - -EXTERN_C_END - -#endif diff -Nru leanify-0.4.3/lib/LZMA/Threads.c leanify-0.4.3+git20181014/lib/LZMA/Threads.c --- leanify-0.4.3/lib/LZMA/Threads.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/Threads.c 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* Threads.c -- multithreading library -2014-09-21 : Igor Pavlov : Public domain */ +2017-06-26 : Igor Pavlov : Public domain */ #include "Precomp.h" @@ -12,18 +12,20 @@ static WRes GetError() { DWORD res = GetLastError(); - return (res) ? (WRes)(res) : 1; + return res ? (WRes)res : 1; } -WRes HandleToWRes(HANDLE h) { return (h != 0) ? 0 : GetError(); } -WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); } +static WRes HandleToWRes(HANDLE h) { return (h != NULL) ? 0 : GetError(); } +static WRes BOOLToWRes(BOOL v) { return v ? 0 : GetError(); } WRes HandlePtr_Close(HANDLE *p) { if (*p != NULL) + { if (!CloseHandle(*p)) return GetError(); - *p = NULL; + *p = NULL; + } return 0; } @@ -49,7 +51,7 @@ return HandleToWRes(*p); } -WRes Event_Create(CEvent *p, BOOL manualReset, int signaled) +static WRes Event_Create(CEvent *p, BOOL manualReset, int signaled) { *p = CreateEvent(NULL, manualReset, (signaled ? TRUE : FALSE), NULL); return HandleToWRes(*p); diff -Nru leanify-0.4.3/lib/LZMA/Threads.h leanify-0.4.3+git20181014/lib/LZMA/Threads.h --- leanify-0.4.3/lib/LZMA/Threads.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/LZMA/Threads.h 2018-01-31 07:20:54.000000000 +0000 @@ -1,5 +1,5 @@ /* Threads.h -- multithreading library -2013-11-12 : Igor Pavlov : Public domain */ +2017-06-18 : Igor Pavlov : Public domain */ #ifndef __7Z_THREADS_H #define __7Z_THREADS_H @@ -49,7 +49,8 @@ WRes AutoResetEvent_CreateNotSignaled(CAutoResetEvent *p); typedef HANDLE CSemaphore; -#define Semaphore_Construct(p) (*p) = NULL +#define Semaphore_Construct(p) *(p) = NULL +#define Semaphore_IsCreated(p) (*(p) != NULL) #define Semaphore_Close(p) HandlePtr_Close(p) #define Semaphore_Wait(p) Handle_WaitObject(*(p)) WRes Semaphore_Create(CSemaphore *p, UInt32 initCount, UInt32 maxCount); diff -Nru leanify-0.4.3/lib/miniz/miniz.c leanify-0.4.3+git20181014/lib/miniz/miniz.c --- leanify-0.4.3/lib/miniz/miniz.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/miniz/miniz.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,4920 +0,0 @@ -#define MINIZ_NO_STDIO -#define MINIZ_NO_ARCHIVE_APIS -#define MINIZ_NO_ZLIB_APIS - -/* miniz.c v1.15 - public domain deflate/inflate, zlib-subset, ZIP reading/writing/appending, PNG writing - See "unlicense" statement at the end of this file. - Rich Geldreich , last updated Oct. 13, 2013 - Implements RFC 1950: http://www.ietf.org/rfc/rfc1950.txt and RFC 1951: http://www.ietf.org/rfc/rfc1951.txt - - Most API's defined in miniz.c are optional. For example, to disable the archive related functions just define - MINIZ_NO_ARCHIVE_APIS, or to get rid of all stdio usage define MINIZ_NO_STDIO (see the list below for more macros). - - * Change History - 10/13/13 v1.15 r4 - Interim bugfix release while I work on the next major release with Zip64 support (almost there!): - - Critical fix for the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY bug (thanks kahmyong.moon@hp.com) which could cause locate files to not find files. This bug - would only have occured in earlier versions if you explicitly used this flag, OR if you used mz_zip_extract_archive_file_to_heap() or mz_zip_add_mem_to_archive_file_in_place() - (which used this flag). If you can't switch to v1.15 but want to fix this bug, just remove the uses of this flag from both helper funcs (and of course don't use the flag). - - Bugfix in mz_zip_reader_extract_to_mem_no_alloc() from kymoon when pUser_read_buf is not NULL and compressed size is > uncompressed size - - Fixing mz_zip_reader_extract_*() funcs so they don't try to extract compressed data from directory entries, to account for weird zipfiles which contain zero-size compressed data on dir entries. - Hopefully this fix won't cause any issues on weird zip archives, because it assumes the low 16-bits of zip external attributes are DOS attributes (which I believe they always are in practice). - - Fixing mz_zip_reader_is_file_a_directory() so it doesn't check the internal attributes, just the filename and external attributes - - mz_zip_reader_init_file() - missing MZ_FCLOSE() call if the seek failed - - Added cmake support for Linux builds which builds all the examples, tested with clang v3.3 and gcc v4.6. - - Clang fix for tdefl_write_image_to_png_file_in_memory() from toffaletti - - Merged MZ_FORCEINLINE fix from hdeanclark - - Fix include before config #ifdef, thanks emil.brink - - Added tdefl_write_image_to_png_file_in_memory_ex(): supports Y flipping (super useful for OpenGL apps), and explicit control over the compression level (so you can - set it to 1 for real-time compression). - - Merged in some compiler fixes from paulharris's github repro. - - Retested this build under Windows (VS 2010, including static analysis), tcc 0.9.26, gcc v4.6 and clang v3.3. - - Added example6.c, which dumps an image of the mandelbrot set to a PNG file. - - Modified example2 to help test the MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY flag more. - - In r3: Bugfix to mz_zip_writer_add_file() found during merge: Fix possible src file fclose() leak if alignment bytes+local header file write faiiled - - In r4: Minor bugfix to mz_zip_writer_add_from_zip_reader(): Was pushing the wrong central dir header offset, appears harmless in this release, but it became a problem in the zip64 branch - 5/20/12 v1.14 - MinGW32/64 GCC 4.6.1 compiler fixes: added MZ_FORCEINLINE, #include (thanks fermtect). - 5/19/12 v1.13 - From jason@cornsyrup.org and kelwert@mtu.edu - Fix mz_crc32() so it doesn't compute the wrong CRC-32's when mz_ulong is 64-bit. - - Temporarily/locally slammed in "typedef unsigned long mz_ulong" and re-ran a randomized regression test on ~500k files. - - Eliminated a bunch of warnings when compiling with GCC 32-bit/64. - - Ran all examples, miniz.c, and tinfl.c through MSVC 2008's /analyze (static analysis) option and fixed all warnings (except for the silly - "Use of the comma-operator in a tested expression.." analysis warning, which I purposely use to work around a MSVC compiler warning). - - Created 32-bit and 64-bit Codeblocks projects/workspace. Built and tested Linux executables. The codeblocks workspace is compatible with Linux+Win32/x64. - - Added miniz_tester solution/project, which is a useful little app derived from LZHAM's tester app that I use as part of the regression test. - - Ran miniz.c and tinfl.c through another series of regression testing on ~500,000 files and archives. - - Modified example5.c so it purposely disables a bunch of high-level functionality (MINIZ_NO_STDIO, etc.). (Thanks to corysama for the MINIZ_NO_STDIO bug report.) - - Fix ftell() usage in examples so they exit with an error on files which are too large (a limitation of the examples, not miniz itself). - 4/12/12 v1.12 - More comments, added low-level example5.c, fixed a couple minor level_and_flags issues in the archive API's. - level_and_flags can now be set to MZ_DEFAULT_COMPRESSION. Thanks to Bruce Dawson for the feedback/bug report. - 5/28/11 v1.11 - Added statement from unlicense.org - 5/27/11 v1.10 - Substantial compressor optimizations: - - Level 1 is now ~4x faster than before. The L1 compressor's throughput now varies between 70-110MB/sec. on a - - Core i7 (actual throughput varies depending on the type of data, and x64 vs. x86). - - Improved baseline L2-L9 compression perf. Also, greatly improved compression perf. issues on some file types. - - Refactored the compression code for better readability and maintainability. - - Added level 10 compression level (L10 has slightly better ratio than level 9, but could have a potentially large - drop in throughput on some files). - 5/15/11 v1.09 - Initial stable release. - - * Low-level Deflate/Inflate implementation notes: - - Compression: Use the "tdefl" API's. The compressor supports raw, static, and dynamic blocks, lazy or - greedy parsing, match length filtering, RLE-only, and Huffman-only streams. It performs and compresses - approximately as well as zlib. - - Decompression: Use the "tinfl" API's. The entire decompressor is implemented as a single function - coroutine: see tinfl_decompress(). It supports decompression into a 32KB (or larger power of 2) wrapping buffer, or into a memory - block large enough to hold the entire file. - - The low-level tdefl/tinfl API's do not make any use of dynamic memory allocation. - - * zlib-style API notes: - - miniz.c implements a fairly large subset of zlib. There's enough functionality present for it to be a drop-in - zlib replacement in many apps: - The z_stream struct, optional memory allocation callbacks - deflateInit/deflateInit2/deflate/deflateReset/deflateEnd/deflateBound - inflateInit/inflateInit2/inflate/inflateEnd - compress, compress2, compressBound, uncompress - CRC-32, Adler-32 - Using modern, minimal code size, CPU cache friendly routines. - Supports raw deflate streams or standard zlib streams with adler-32 checking. - - Limitations: - The callback API's are not implemented yet. No support for gzip headers or zlib static dictionaries. - I've tried to closely emulate zlib's various flavors of stream flushing and return status codes, but - there are no guarantees that miniz.c pulls this off perfectly. - - * PNG writing: See the tdefl_write_image_to_png_file_in_memory() function, originally written by - Alex Evans. Supports 1-4 bytes/pixel images. - - * ZIP archive API notes: - - The ZIP archive API's where designed with simplicity and efficiency in mind, with just enough abstraction to - get the job done with minimal fuss. There are simple API's to retrieve file information, read files from - existing archives, create new archives, append new files to existing archives, or clone archive data from - one archive to another. It supports archives located in memory or the heap, on disk (using stdio.h), - or you can specify custom file read/write callbacks. - - - Archive reading: Just call this function to read a single file from a disk archive: - - void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, - size_t *pSize, mz_uint zip_flags); - - For more complex cases, use the "mz_zip_reader" functions. Upon opening an archive, the entire central - directory is located and read as-is into memory, and subsequent file access only occurs when reading individual files. - - - Archives file scanning: The simple way is to use this function to scan a loaded archive for a specific file: - - int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - - The locate operation can optionally check file comments too, which (as one example) can be used to identify - multiple versions of the same file in an archive. This function uses a simple linear search through the central - directory, so it's not very fast. - - Alternately, you can iterate through all the files in an archive (using mz_zip_reader_get_num_files()) and - retrieve detailed info on each file by calling mz_zip_reader_file_stat(). - - - Archive creation: Use the "mz_zip_writer" functions. The ZIP writer immediately writes compressed file data - to disk and builds an exact image of the central directory in memory. The central directory image is written - all at once at the end of the archive file when the archive is finalized. - - The archive writer can optionally align each file's local header and file data to any power of 2 alignment, - which can be useful when the archive will be read from optical media. Also, the writer supports placing - arbitrary data blobs at the very beginning of ZIP archives. Archives written using either feature are still - readable by any ZIP tool. - - - Archive appending: The simple way to add a single file to an archive is to call this function: - - mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, - const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - - The archive will be created if it doesn't already exist, otherwise it'll be appended to. - Note the appending is done in-place and is not an atomic operation, so if something goes wrong - during the operation it's possible the archive could be left without a central directory (although the local - file headers and file data will be fine, so the archive will be recoverable). - - For more complex archive modification scenarios: - 1. The safest way is to use a mz_zip_reader to read the existing archive, cloning only those bits you want to - preserve into a new archive using using the mz_zip_writer_add_from_zip_reader() function (which compiles the - compressed file data as-is). When you're done, delete the old archive and rename the newly written archive, and - you're done. This is safe but requires a bunch of temporary disk space or heap memory. - - 2. Or, you can convert an mz_zip_reader in-place to an mz_zip_writer using mz_zip_writer_init_from_reader(), - append new files as needed, then finalize the archive which will write an updated central directory to the - original archive. (This is basically what mz_zip_add_mem_to_archive_file_in_place() does.) There's a - possibility that the archive's central directory could be lost with this method if anything goes wrong, though. - - - ZIP archive support limitations: - No zip64 or spanning support. Extraction functions can only handle unencrypted, stored or deflated files. - Requires streams capable of seeking. - - * This is a header file library, like stb_image.c. To get only a header file, either cut and paste the - below header, or create miniz.h, #define MINIZ_HEADER_FILE_ONLY, and then include miniz.c from it. - - * Important: For best perf. be sure to customize the below macros for your target platform: - #define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 - #define MINIZ_LITTLE_ENDIAN 1 - #define MINIZ_HAS_64BIT_REGISTERS 1 - - * On platforms using glibc, Be sure to "#define _LARGEFILE64_SOURCE 1" before including miniz.c to ensure miniz - uses the 64-bit variants: fopen64(), stat64(), etc. Otherwise you won't be able to process large files - (i.e. 32-bit stat() fails for me on files > 0x7FFFFFFF bytes). -*/ - -#ifndef MINIZ_HEADER_INCLUDED -#define MINIZ_HEADER_INCLUDED - -#include - -// Defines to completely disable specific portions of miniz.c: -// If all macros here are defined the only functionality remaining will be CRC-32, adler-32, tinfl, and tdefl. - -// Define MINIZ_NO_STDIO to disable all usage and any functions which rely on stdio for file I/O. -//#define MINIZ_NO_STDIO - -// If MINIZ_NO_TIME is specified then the ZIP archive functions will not be able to get the current time, or -// get/set file times, and the C run-time funcs that get/set times won't be called. -// The current downside is the times written to your archives will be from 1979. -//#define MINIZ_NO_TIME - -// Define MINIZ_NO_ARCHIVE_APIS to disable all ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_APIS - -// Define MINIZ_NO_ARCHIVE_APIS to disable all writing related ZIP archive API's. -//#define MINIZ_NO_ARCHIVE_WRITING_APIS - -// Define MINIZ_NO_ZLIB_APIS to remove all ZLIB-style compression/decompression API's. -//#define MINIZ_NO_ZLIB_APIS - -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAME to disable zlib names, to prevent conflicts against stock zlib. -//#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -// Define MINIZ_NO_MALLOC to disable all calls to malloc, free, and realloc. -// Note if MINIZ_NO_MALLOC is defined then the user must always provide custom user alloc/free/realloc -// callbacks to the zlib and archive API's, and a few stand-alone helper API's which don't provide custom user -// functions (such as tdefl_compress_mem_to_heap() and tinfl_decompress_mem_to_heap()) won't work. -//#define MINIZ_NO_MALLOC - -#if defined(__TINYC__) && (defined(__linux) || defined(__linux__)) - // TODO: Work around "error: include file 'sys\utime.h' when compiling with tcc on Linux - #define MINIZ_NO_TIME -#endif - -#if !defined(MINIZ_NO_TIME) && !defined(MINIZ_NO_ARCHIVE_APIS) - #include -#endif - -#if defined(_M_IX86) || defined(_M_X64) || defined(__i386__) || defined(__i386) || defined(__i486__) || defined(__i486) || defined(i386) || defined(__ia64__) || defined(__x86_64__) -// MINIZ_X86_OR_X64_CPU is only used to help set the below macros. -#define MINIZ_X86_OR_X64_CPU 1 -#endif - -#if (__BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU -// Set MINIZ_LITTLE_ENDIAN to 1 if the processor is little endian. -#define MINIZ_LITTLE_ENDIAN 1 -#endif - -#if MINIZ_X86_OR_X64_CPU -// Set MINIZ_USE_UNALIGNED_LOADS_AND_STORES to 1 on CPU's that permit efficient integer loads and stores from unaligned addresses. -#define MINIZ_USE_UNALIGNED_LOADS_AND_STORES 1 -#endif - -#if defined(_M_X64) || defined(_WIN64) || defined(__MINGW64__) || defined(_LP64) || defined(__LP64__) || defined(__ia64__) || defined(__x86_64__) -// Set MINIZ_HAS_64BIT_REGISTERS to 1 if operations on 64-bit integers are reasonably fast (and don't involve compiler generated calls to helper functions). -#define MINIZ_HAS_64BIT_REGISTERS 1 -#endif - -#ifdef __cplusplus -extern "C" { -#endif - -// ------------------- zlib-style API Definitions. - -// For more compatibility with zlib, miniz.c uses unsigned long for some parameters/struct members. Beware: mz_ulong can be either 32 or 64-bits! -typedef unsigned long mz_ulong; - -// mz_free() internally uses the MZ_FREE() macro (which by default calls free() unless you've modified the MZ_MALLOC macro) to release a block allocated from the heap. -void mz_free(void *p); - -#define MZ_ADLER32_INIT (1) -// mz_adler32() returns the initial adler-32 value to use when called with ptr==NULL. -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len); - -#define MZ_CRC32_INIT (0) -// mz_crc32() returns the initial CRC-32 value to use when called with ptr==NULL. -mz_ulong mz_crc32(mz_ulong crc, const unsigned char *ptr, size_t buf_len); - -// Compression strategies. -enum { MZ_DEFAULT_STRATEGY = 0, MZ_FILTERED = 1, MZ_HUFFMAN_ONLY = 2, MZ_RLE = 3, MZ_FIXED = 4 }; - -// Method -#define MZ_DEFLATED 8 - -#ifndef MINIZ_NO_ZLIB_APIS - -// Heap allocation callbacks. -// Note that mz_alloc_func parameter types purpsosely differ from zlib's: items/size is size_t, not unsigned long. -typedef void *(*mz_alloc_func)(void *opaque, size_t items, size_t size); -typedef void (*mz_free_func)(void *opaque, void *address); -typedef void *(*mz_realloc_func)(void *opaque, void *address, size_t items, size_t size); - -#define MZ_VERSION "9.1.15" -#define MZ_VERNUM 0x91F0 -#define MZ_VER_MAJOR 9 -#define MZ_VER_MINOR 1 -#define MZ_VER_REVISION 15 -#define MZ_VER_SUBREVISION 0 - -// Flush values. For typical usage you only need MZ_NO_FLUSH and MZ_FINISH. The other values are for advanced use (refer to the zlib docs). -enum { MZ_NO_FLUSH = 0, MZ_PARTIAL_FLUSH = 1, MZ_SYNC_FLUSH = 2, MZ_FULL_FLUSH = 3, MZ_FINISH = 4, MZ_BLOCK = 5 }; - -// Return status codes. MZ_PARAM_ERROR is non-standard. -enum { MZ_OK = 0, MZ_STREAM_END = 1, MZ_NEED_DICT = 2, MZ_ERRNO = -1, MZ_STREAM_ERROR = -2, MZ_DATA_ERROR = -3, MZ_MEM_ERROR = -4, MZ_BUF_ERROR = -5, MZ_VERSION_ERROR = -6, MZ_PARAM_ERROR = -10000 }; - -// Compression levels: 0-9 are the standard zlib-style levels, 10 is best possible compression (not zlib compatible, and may be very slow), MZ_DEFAULT_COMPRESSION=MZ_DEFAULT_LEVEL. -enum { MZ_NO_COMPRESSION = 0, MZ_BEST_SPEED = 1, MZ_BEST_COMPRESSION = 9, MZ_UBER_COMPRESSION = 10, MZ_DEFAULT_LEVEL = 6, MZ_DEFAULT_COMPRESSION = -1 }; - -// Window bits -#define MZ_DEFAULT_WINDOW_BITS 15 - -struct mz_internal_state; - -// Compression/decompression stream struct. -typedef struct mz_stream_s -{ - const unsigned char *next_in; // pointer to next byte to read - unsigned int avail_in; // number of bytes available at next_in - mz_ulong total_in; // total number of bytes consumed so far - - unsigned char *next_out; // pointer to next byte to write - unsigned int avail_out; // number of bytes that can be written to next_out - mz_ulong total_out; // total number of bytes produced so far - - char *msg; // error msg (unused) - struct mz_internal_state *state; // internal state, allocated by zalloc/zfree - - mz_alloc_func zalloc; // optional heap allocation function (defaults to malloc) - mz_free_func zfree; // optional heap free function (defaults to free) - void *opaque; // heap alloc function user pointer - - int data_type; // data_type (unused) - mz_ulong adler; // adler32 of the source or uncompressed data - mz_ulong reserved; // not used -} mz_stream; - -typedef mz_stream *mz_streamp; - -// Returns the version string of miniz.c. -const char *mz_version(void); - -// mz_deflateInit() initializes a compressor with default options: -// Parameters: -// pStream must point to an initialized mz_stream struct. -// level must be between [MZ_NO_COMPRESSION, MZ_BEST_COMPRESSION]. -// level 1 enables a specially optimized compression function that's been optimized purely for performance, not ratio. -// (This special func. is currently only enabled when MINIZ_USE_UNALIGNED_LOADS_AND_STORES and MINIZ_LITTLE_ENDIAN are defined.) -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if the input parameters are bogus. -// MZ_MEM_ERROR on out of memory. -int mz_deflateInit(mz_streamp pStream, int level); - -// mz_deflateInit2() is like mz_deflate(), except with more control: -// Additional parameters: -// method must be MZ_DEFLATED -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to wrap the deflate stream with zlib header/adler-32 footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate/no header or footer) -// mem_level must be between [1, 9] (it's checked but ignored by miniz.c) -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy); - -// Quickly resets a compressor without having to reallocate anything. Same as calling mz_deflateEnd() followed by mz_deflateInit()/mz_deflateInit2(). -int mz_deflateReset(mz_streamp pStream); - -// mz_deflate() compresses the input to output, consuming as much of the input and producing as much output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_PARTIAL_FLUSH/MZ_SYNC_FLUSH, MZ_FULL_FLUSH, or MZ_FINISH. -// Return values: -// MZ_OK on success (when flushing, or if more input is needed but not available, and/or there's more output to be written but the output buffer is full). -// MZ_STREAM_END if all input has been consumed and all output bytes have been written. Don't call mz_deflate() on the stream anymore. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input and/or output buffers are empty. (Fill up the input buffer or free up some output space and try again.) -int mz_deflate(mz_streamp pStream, int flush); - -// mz_deflateEnd() deinitializes a compressor: -// Return values: -// MZ_OK on success. -// MZ_STREAM_ERROR if the stream is bogus. -int mz_deflateEnd(mz_streamp pStream); - -// mz_deflateBound() returns a (very) conservative upper bound on the amount of data that could be generated by deflate(), assuming flush is set to only MZ_NO_FLUSH or MZ_FINISH. -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len); - -// Single-call compression functions mz_compress() and mz_compress2(): -// Returns MZ_OK on success, or one of the error codes from mz_deflate() on failure. -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level); - -// mz_compressBound() returns a (very) conservative upper bound on the amount of data that could be generated by calling mz_compress(). -mz_ulong mz_compressBound(mz_ulong source_len); - -// Initializes a decompressor. -int mz_inflateInit(mz_streamp pStream); - -// mz_inflateInit2() is like mz_inflateInit() with an additional option that controls the window size and whether or not the stream has been wrapped with a zlib header/footer: -// window_bits must be MZ_DEFAULT_WINDOW_BITS (to parse zlib header/footer) or -MZ_DEFAULT_WINDOW_BITS (raw deflate). -int mz_inflateInit2(mz_streamp pStream, int window_bits); - -// Decompresses the input stream to the output, consuming only as much of the input as needed, and writing as much to the output as possible. -// Parameters: -// pStream is the stream to read from and write to. You must initialize/update the next_in, avail_in, next_out, and avail_out members. -// flush may be MZ_NO_FLUSH, MZ_SYNC_FLUSH, or MZ_FINISH. -// On the first call, if flush is MZ_FINISH it's assumed the input and output buffers are both sized large enough to decompress the entire stream in a single call (this is slightly faster). -// MZ_FINISH implies that there are no more source bytes available beside what's already in the input buffer, and that the output buffer is large enough to hold the rest of the decompressed data. -// Return values: -// MZ_OK on success. Either more input is needed but not available, and/or there's more output to be written but the output buffer is full. -// MZ_STREAM_END if all needed input has been consumed and all output bytes have been written. For zlib streams, the adler-32 of the decompressed data has also been verified. -// MZ_STREAM_ERROR if the stream is bogus. -// MZ_DATA_ERROR if the deflate stream is invalid. -// MZ_PARAM_ERROR if one of the parameters is invalid. -// MZ_BUF_ERROR if no forward progress is possible because the input buffer is empty but the inflater needs more input to continue, or if the output buffer is not large enough. Call mz_inflate() again -// with more input data, or with more room in the output buffer (except when using single call decompression, described above). -int mz_inflate(mz_streamp pStream, int flush); - -// Deinitializes a decompressor. -int mz_inflateEnd(mz_streamp pStream); - -// Single-call decompression. -// Returns MZ_OK on success, or one of the error codes from mz_inflate() on failure. -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len); - -// Returns a string description of the specified error code, or NULL if the error code is invalid. -const char *mz_error(int err); - -// Redefine zlib-compatible names to miniz equivalents, so miniz.c can be used as a drop-in replacement for the subset of zlib that miniz.c supports. -// Define MINIZ_NO_ZLIB_COMPATIBLE_NAMES to disable zlib-compatibility if you use zlib in the same project. -#ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - typedef unsigned char Byte; - typedef unsigned int uInt; - typedef mz_ulong uLong; - typedef Byte Bytef; - typedef uInt uIntf; - typedef char charf; - typedef int intf; - typedef void *voidpf; - typedef uLong uLongf; - typedef void *voidp; - typedef void *const voidpc; - #define Z_NULL 0 - #define Z_NO_FLUSH MZ_NO_FLUSH - #define Z_PARTIAL_FLUSH MZ_PARTIAL_FLUSH - #define Z_SYNC_FLUSH MZ_SYNC_FLUSH - #define Z_FULL_FLUSH MZ_FULL_FLUSH - #define Z_FINISH MZ_FINISH - #define Z_BLOCK MZ_BLOCK - #define Z_OK MZ_OK - #define Z_STREAM_END MZ_STREAM_END - #define Z_NEED_DICT MZ_NEED_DICT - #define Z_ERRNO MZ_ERRNO - #define Z_STREAM_ERROR MZ_STREAM_ERROR - #define Z_DATA_ERROR MZ_DATA_ERROR - #define Z_MEM_ERROR MZ_MEM_ERROR - #define Z_BUF_ERROR MZ_BUF_ERROR - #define Z_VERSION_ERROR MZ_VERSION_ERROR - #define Z_PARAM_ERROR MZ_PARAM_ERROR - #define Z_NO_COMPRESSION MZ_NO_COMPRESSION - #define Z_BEST_SPEED MZ_BEST_SPEED - #define Z_BEST_COMPRESSION MZ_BEST_COMPRESSION - #define Z_DEFAULT_COMPRESSION MZ_DEFAULT_COMPRESSION - #define Z_DEFAULT_STRATEGY MZ_DEFAULT_STRATEGY - #define Z_FILTERED MZ_FILTERED - #define Z_HUFFMAN_ONLY MZ_HUFFMAN_ONLY - #define Z_RLE MZ_RLE - #define Z_FIXED MZ_FIXED - #define Z_DEFLATED MZ_DEFLATED - #define Z_DEFAULT_WINDOW_BITS MZ_DEFAULT_WINDOW_BITS - #define alloc_func mz_alloc_func - #define free_func mz_free_func - #define internal_state mz_internal_state - #define z_stream mz_stream - #define deflateInit mz_deflateInit - #define deflateInit2 mz_deflateInit2 - #define deflateReset mz_deflateReset - #define deflate mz_deflate - #define deflateEnd mz_deflateEnd - #define deflateBound mz_deflateBound - #define compress mz_compress - #define compress2 mz_compress2 - #define compressBound mz_compressBound - #define inflateInit mz_inflateInit - #define inflateInit2 mz_inflateInit2 - #define inflate mz_inflate - #define inflateEnd mz_inflateEnd - #define uncompress mz_uncompress - #define crc32 mz_crc32 - #define adler32 mz_adler32 - #define MAX_WBITS 15 - #define MAX_MEM_LEVEL 9 - #define zError mz_error - #define ZLIB_VERSION MZ_VERSION - #define ZLIB_VERNUM MZ_VERNUM - #define ZLIB_VER_MAJOR MZ_VER_MAJOR - #define ZLIB_VER_MINOR MZ_VER_MINOR - #define ZLIB_VER_REVISION MZ_VER_REVISION - #define ZLIB_VER_SUBREVISION MZ_VER_SUBREVISION - #define zlibVersion mz_version - #define zlib_version mz_version() -#endif // #ifndef MINIZ_NO_ZLIB_COMPATIBLE_NAMES - -#endif // MINIZ_NO_ZLIB_APIS - -// ------------------- Types and macros - -typedef unsigned char mz_uint8; -typedef signed short mz_int16; -typedef unsigned short mz_uint16; -typedef unsigned int mz_uint32; -typedef unsigned int mz_uint; -typedef long long mz_int64; -typedef unsigned long long mz_uint64; -typedef int mz_bool; - -#define MZ_FALSE (0) -#define MZ_TRUE (1) - -// An attempt to work around MSVC's spammy "warning C4127: conditional expression is constant" message. -#ifdef _MSC_VER - #define MZ_MACRO_END while (0, 0) -#else - #define MZ_MACRO_END while (0) -#endif - -// ------------------- ZIP archive reading/writing - -#ifndef MINIZ_NO_ARCHIVE_APIS - -enum -{ - MZ_ZIP_MAX_IO_BUF_SIZE = 64*1024, - MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE = 260, - MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE = 256 -}; - -typedef struct -{ - mz_uint32 m_file_index; - mz_uint32 m_central_dir_ofs; - mz_uint16 m_version_made_by; - mz_uint16 m_version_needed; - mz_uint16 m_bit_flag; - mz_uint16 m_method; -#ifndef MINIZ_NO_TIME - time_t m_time; -#endif - mz_uint32 m_crc32; - mz_uint64 m_comp_size; - mz_uint64 m_uncomp_size; - mz_uint16 m_internal_attr; - mz_uint32 m_external_attr; - mz_uint64 m_local_header_ofs; - mz_uint32 m_comment_size; - char m_filename[MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE]; - char m_comment[MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE]; -} mz_zip_archive_file_stat; - -typedef size_t (*mz_file_read_func)(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n); -typedef size_t (*mz_file_write_func)(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n); - -struct mz_zip_internal_state_tag; -typedef struct mz_zip_internal_state_tag mz_zip_internal_state; - -typedef enum -{ - MZ_ZIP_MODE_INVALID = 0, - MZ_ZIP_MODE_READING = 1, - MZ_ZIP_MODE_WRITING = 2, - MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED = 3 -} mz_zip_mode; - -typedef struct mz_zip_archive_tag -{ - mz_uint64 m_archive_size; - mz_uint64 m_central_directory_file_ofs; - mz_uint m_total_files; - mz_zip_mode m_zip_mode; - - mz_uint m_file_offset_alignment; - - mz_alloc_func m_pAlloc; - mz_free_func m_pFree; - mz_realloc_func m_pRealloc; - void *m_pAlloc_opaque; - - mz_file_read_func m_pRead; - mz_file_write_func m_pWrite; - void *m_pIO_opaque; - - mz_zip_internal_state *m_pState; - -} mz_zip_archive; - -typedef enum -{ - MZ_ZIP_FLAG_CASE_SENSITIVE = 0x0100, - MZ_ZIP_FLAG_IGNORE_PATH = 0x0200, - MZ_ZIP_FLAG_COMPRESSED_DATA = 0x0400, - MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY = 0x0800 -} mz_zip_flags; - -// ZIP archive reading - -// Inits a ZIP archive reader. -// These functions read and validate the archive's central directory. -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags); -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags); -#endif - -// Returns the total number of files in the archive. -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip); - -// Returns detailed information about an archive file entry. -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat); - -// Determines if an archive file entry is a directory entry. -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index); -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index); - -// Retrieves the filename of an archive file entry. -// Returns the number of bytes written to pFilename, or if filename_buf_size is 0 this function returns the number of bytes needed to fully store the filename. -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size); - -// Attempts to locates a file in the archive's central directory. -// Valid flags: MZ_ZIP_FLAG_CASE_SENSITIVE, MZ_ZIP_FLAG_IGNORE_PATH -// Returns -1 if the file cannot be found. -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags); - -// Extracts a archive file to a memory buffer using no memory allocation. -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size); - -// Extracts a archive file to a memory buffer. -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags); - -// Extracts a archive file to a dynamically allocated heap buffer. -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags); -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags); - -// Extracts a archive file using a callback function to output the file's data. -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags); - -#ifndef MINIZ_NO_STDIO -// Extracts a archive file to a disk file and sets its last accessed and modified times. -// This function only extracts files, not archive directory records. -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags); -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags); -#endif - -// Ends archive reading, freeing all allocations, and closing the input archive file if mz_zip_reader_init_file() was used. -mz_bool mz_zip_reader_end(mz_zip_archive *pZip); - -// ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -// Inits a ZIP archive writer. -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size); -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size); - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning); -#endif - -// Converts a ZIP archive reader object into a writer object, to allow efficient in-place file appends to occur on an existing archive. -// For archives opened using mz_zip_reader_init_file, pFilename must be the archive's filename so it can be reopened for writing. If the file can't be reopened, mz_zip_reader_end() will be called. -// For archives opened using mz_zip_reader_init_mem, the memory block must be growable using the realloc callback (which defaults to realloc unless you've overridden it). -// Finally, for archives opened using mz_zip_reader_init, the mz_zip_archive's user provided m_pWrite function cannot be NULL. -// Note: In-place archive modification is not recommended unless you know what you're doing, because if execution stops or something goes wrong before -// the archive is finalized the file's central directory will be hosed. -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename); - -// Adds the contents of a memory buffer to an archive. These functions record the current local time into the archive. -// To add a directory entry, call this method with an archive name ending in a forwardslash with empty buffer. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags); -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32); - -#ifndef MINIZ_NO_STDIO -// Adds the contents of a disk file to an archive. This function also records the disk file's modified time into the archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); -#endif - -// Adds a file to an archive by fully cloning the data from another archive. -// This function fully clones the source file's compressed data (no recompression), along with its full filename, extra data, and comment fields. -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index); - -// Finalizes the archive by writing the central directory records followed by the end of central directory record. -// After an archive is finalized, the only valid call on the mz_zip_archive struct is mz_zip_writer_end(). -// An archive must be manually finalized by calling this function for it to be valid. -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip); -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize); - -// Ends archive writing, freeing all allocations, and closing the output file if mz_zip_writer_init_file() was used. -// Note for the archive to be valid, it must have been finalized before ending. -mz_bool mz_zip_writer_end(mz_zip_archive *pZip); - -// Misc. high-level helper functions: - -// mz_zip_add_mem_to_archive_file_in_place() efficiently (but not atomically) appends a memory blob to a ZIP archive. -// level_and_flags - compression level (0-10, see MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc.) logically OR'd with zero or more mz_zip_flags, or just set to MZ_DEFAULT_COMPRESSION. -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags); - -// Reads a single file from an archive into a heap block. -// Returns NULL on failure. -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint zip_flags); - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - -// ------------------- Low-level Decompression API Definitions - -// Decompression flags used by tinfl_decompress(). -// TINFL_FLAG_PARSE_ZLIB_HEADER: If set, the input has a valid zlib header and ends with an adler32 checksum (it's a valid zlib stream). Otherwise, the input is a raw deflate stream. -// TINFL_FLAG_HAS_MORE_INPUT: If set, there are more input bytes available beyond the end of the supplied input buffer. If clear, the input buffer contains all remaining input. -// TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF: If set, the output buffer is large enough to hold the entire decompressed stream. If clear, the output buffer is at least the size of the dictionary (typically 32KB). -// TINFL_FLAG_COMPUTE_ADLER32: Force adler-32 checksum computation of the decompressed bytes. -enum -{ - TINFL_FLAG_PARSE_ZLIB_HEADER = 1, - TINFL_FLAG_HAS_MORE_INPUT = 2, - TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF = 4, - TINFL_FLAG_COMPUTE_ADLER32 = 8 -}; - -// High level decompression functions: -// tinfl_decompress_mem_to_heap() decompresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of the Deflate or zlib source data to decompress. -// On return: -// Function returns a pointer to the decompressed data, or NULL on failure. -// *pOut_len will be set to the decompressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must call mz_free() on the returned block when it's no longer needed. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tinfl_decompress_mem_to_mem() decompresses a block in memory to another block in memory. -// Returns TINFL_DECOMPRESS_MEM_TO_MEM_FAILED on failure, or the number of bytes written on success. -#define TINFL_DECOMPRESS_MEM_TO_MEM_FAILED ((size_t)(-1)) -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// tinfl_decompress_mem_to_callback() decompresses a block in memory to an internal 32KB buffer, and a user provided callback function will be called to flush the buffer. -// Returns 1 on success or 0 on failure. -typedef int (*tinfl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -struct tinfl_decompressor_tag; typedef struct tinfl_decompressor_tag tinfl_decompressor; - -// Max size of LZ dictionary. -#define TINFL_LZ_DICT_SIZE 32768 - -// Return status. -typedef enum -{ - TINFL_STATUS_BAD_PARAM = -3, - TINFL_STATUS_ADLER32_MISMATCH = -2, - TINFL_STATUS_FAILED = -1, - TINFL_STATUS_DONE = 0, - TINFL_STATUS_NEEDS_MORE_INPUT = 1, - TINFL_STATUS_HAS_MORE_OUTPUT = 2 -} tinfl_status; - -// Initializes the decompressor to its initial state. -#define tinfl_init(r) do { (r)->m_state = 0; } MZ_MACRO_END -#define tinfl_get_adler32(r) (r)->m_check_adler32 - -// Main low-level decompressor coroutine function. This is the only function actually needed for decompression. All the other functions are just high-level helpers for improved usability. -// This is a universal API, i.e. it can be used as a building block to build any desired higher level decompression API. In the limit case, it can be called once per every byte input or output. -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags); - -// Internal/private bits follow. -enum -{ - TINFL_MAX_HUFF_TABLES = 3, TINFL_MAX_HUFF_SYMBOLS_0 = 288, TINFL_MAX_HUFF_SYMBOLS_1 = 32, TINFL_MAX_HUFF_SYMBOLS_2 = 19, - TINFL_FAST_LOOKUP_BITS = 10, TINFL_FAST_LOOKUP_SIZE = 1 << TINFL_FAST_LOOKUP_BITS -}; - -typedef struct -{ - mz_uint8 m_code_size[TINFL_MAX_HUFF_SYMBOLS_0]; - mz_int16 m_look_up[TINFL_FAST_LOOKUP_SIZE], m_tree[TINFL_MAX_HUFF_SYMBOLS_0 * 2]; -} tinfl_huff_table; - -#if MINIZ_HAS_64BIT_REGISTERS - #define TINFL_USE_64BIT_BITBUF 1 -#endif - -#if TINFL_USE_64BIT_BITBUF - typedef mz_uint64 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (64) -#else - typedef mz_uint32 tinfl_bit_buf_t; - #define TINFL_BITBUF_SIZE (32) -#endif - -struct tinfl_decompressor_tag -{ - mz_uint32 m_state, m_num_bits, m_zhdr0, m_zhdr1, m_z_adler32, m_final, m_type, m_check_adler32, m_dist, m_counter, m_num_extra, m_table_sizes[TINFL_MAX_HUFF_TABLES]; - tinfl_bit_buf_t m_bit_buf; - size_t m_dist_from_out_buf_start; - tinfl_huff_table m_tables[TINFL_MAX_HUFF_TABLES]; - mz_uint8 m_raw_header[4], m_len_codes[TINFL_MAX_HUFF_SYMBOLS_0 + TINFL_MAX_HUFF_SYMBOLS_1 + 137]; -}; - -// ------------------- Low-level Compression API Definitions - -// Set TDEFL_LESS_MEMORY to 1 to use less memory (compression will be slightly slower, and raw/dynamic blocks will be output more frequently). -#define TDEFL_LESS_MEMORY 0 - -// tdefl_init() compression flags logically OR'd together (low 12 bits contain the max. number of probes per dictionary search): -// TDEFL_DEFAULT_MAX_PROBES: The compressor defaults to 128 dictionary probes per dictionary search. 0=Huffman only, 1=Huffman+LZ (fastest/crap compression), 4095=Huffman+LZ (slowest/best compression). -enum -{ - TDEFL_HUFFMAN_ONLY = 0, TDEFL_DEFAULT_MAX_PROBES = 128, TDEFL_MAX_PROBES_MASK = 0xFFF -}; - -// TDEFL_WRITE_ZLIB_HEADER: If set, the compressor outputs a zlib header before the deflate data, and the Adler-32 of the source data at the end. Otherwise, you'll get raw deflate data. -// TDEFL_COMPUTE_ADLER32: Always compute the adler-32 of the input data (even when not writing zlib headers). -// TDEFL_GREEDY_PARSING_FLAG: Set to use faster greedy parsing, instead of more efficient lazy parsing. -// TDEFL_NONDETERMINISTIC_PARSING_FLAG: Enable to decrease the compressor's initialization time to the minimum, but the output may vary from run to run given the same input (depending on the contents of memory). -// TDEFL_RLE_MATCHES: Only look for RLE matches (matches with a distance of 1) -// TDEFL_FILTER_MATCHES: Discards matches <= 5 chars if enabled. -// TDEFL_FORCE_ALL_STATIC_BLOCKS: Disable usage of optimized Huffman tables. -// TDEFL_FORCE_ALL_RAW_BLOCKS: Only use raw (uncompressed) deflate blocks. -// The low 12 bits are reserved to control the max # of hash probes per dictionary lookup (see TDEFL_MAX_PROBES_MASK). -enum -{ - TDEFL_WRITE_ZLIB_HEADER = 0x01000, - TDEFL_COMPUTE_ADLER32 = 0x02000, - TDEFL_GREEDY_PARSING_FLAG = 0x04000, - TDEFL_NONDETERMINISTIC_PARSING_FLAG = 0x08000, - TDEFL_RLE_MATCHES = 0x10000, - TDEFL_FILTER_MATCHES = 0x20000, - TDEFL_FORCE_ALL_STATIC_BLOCKS = 0x40000, - TDEFL_FORCE_ALL_RAW_BLOCKS = 0x80000 -}; - -// High level compression functions: -// tdefl_compress_mem_to_heap() compresses a block in memory to a heap block allocated via malloc(). -// On entry: -// pSrc_buf, src_buf_len: Pointer and size of source block to compress. -// flags: The max match finder probes (default is 128) logically OR'd against the above flags. Higher probes are slower but improve compression. -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pOut_len will be set to the compressed data's size, which could be larger than src_buf_len on uncompressible data. -// The caller must free() the returned block when it's no longer needed. -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags); - -// tdefl_compress_mem_to_mem() compresses a block in memory to another block in memory. -// Returns 0 on failure. -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags); - -// Compresses an image to a compressed PNG file in memory. -// On entry: -// pImage, w, h, and num_chans describe the image to compress. num_chans may be 1, 2, 3, or 4. -// The image pitch in bytes per scanline will be w*num_chans. The leftmost pixel on the top scanline is stored first in memory. -// level may range from [0,10], use MZ_NO_COMPRESSION, MZ_BEST_SPEED, MZ_BEST_COMPRESSION, etc. or a decent default is MZ_DEFAULT_LEVEL -// If flip is true, the image will be flipped on the Y axis (useful for OpenGL apps). -// On return: -// Function returns a pointer to the compressed data, or NULL on failure. -// *pLen_out will be set to the size of the PNG image file. -// The caller must mz_free() the returned heap block (which will typically be larger than *pLen_out) when it's no longer needed. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip); -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out); - -// Output stream interface. The compressor uses this interface to write compressed data. It'll typically be called TDEFL_OUT_BUF_SIZE at a time. -typedef mz_bool (*tdefl_put_buf_func_ptr)(const void* pBuf, int len, void *pUser); - -// tdefl_compress_mem_to_output() compresses a block to an output stream. The above helpers use this function internally. -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -enum { TDEFL_MAX_HUFF_TABLES = 3, TDEFL_MAX_HUFF_SYMBOLS_0 = 288, TDEFL_MAX_HUFF_SYMBOLS_1 = 32, TDEFL_MAX_HUFF_SYMBOLS_2 = 19, TDEFL_LZ_DICT_SIZE = 32768, TDEFL_LZ_DICT_SIZE_MASK = TDEFL_LZ_DICT_SIZE - 1, TDEFL_MIN_MATCH_LEN = 3, TDEFL_MAX_MATCH_LEN = 258 }; - -// TDEFL_OUT_BUF_SIZE MUST be large enough to hold a single entire compressed output block (using static/fixed Huffman codes). -#if TDEFL_LESS_MEMORY -enum { TDEFL_LZ_CODE_BUF_SIZE = 24 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 12, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#else -enum { TDEFL_LZ_CODE_BUF_SIZE = 64 * 1024, TDEFL_OUT_BUF_SIZE = (TDEFL_LZ_CODE_BUF_SIZE * 13 ) / 10, TDEFL_MAX_HUFF_SYMBOLS = 288, TDEFL_LZ_HASH_BITS = 15, TDEFL_LEVEL1_HASH_SIZE_MASK = 4095, TDEFL_LZ_HASH_SHIFT = (TDEFL_LZ_HASH_BITS + 2) / 3, TDEFL_LZ_HASH_SIZE = 1 << TDEFL_LZ_HASH_BITS }; -#endif - -// The low-level tdefl functions below may be used directly if the above helper functions aren't flexible enough. The low-level functions don't make any heap allocations, unlike the above helper functions. -typedef enum -{ - TDEFL_STATUS_BAD_PARAM = -2, - TDEFL_STATUS_PUT_BUF_FAILED = -1, - TDEFL_STATUS_OKAY = 0, - TDEFL_STATUS_DONE = 1, -} tdefl_status; - -// Must map to MZ_NO_FLUSH, MZ_SYNC_FLUSH, etc. enums -typedef enum -{ - TDEFL_NO_FLUSH = 0, - TDEFL_SYNC_FLUSH = 2, - TDEFL_FULL_FLUSH = 3, - TDEFL_FINISH = 4 -} tdefl_flush; - -// tdefl's compression state structure. -typedef struct -{ - tdefl_put_buf_func_ptr m_pPut_buf_func; - void *m_pPut_buf_user; - mz_uint m_flags, m_max_probes[2]; - int m_greedy_parsing; - mz_uint m_adler32, m_lookahead_pos, m_lookahead_size, m_dict_size; - mz_uint8 *m_pLZ_code_buf, *m_pLZ_flags, *m_pOutput_buf, *m_pOutput_buf_end; - mz_uint m_num_flags_left, m_total_lz_bytes, m_lz_code_buf_dict_pos, m_bits_in, m_bit_buffer; - mz_uint m_saved_match_dist, m_saved_match_len, m_saved_lit, m_output_flush_ofs, m_output_flush_remaining, m_finished, m_block_index, m_wants_to_finish; - tdefl_status m_prev_return_status; - const void *m_pIn_buf; - void *m_pOut_buf; - size_t *m_pIn_buf_size, *m_pOut_buf_size; - tdefl_flush m_flush; - const mz_uint8 *m_pSrc; - size_t m_src_buf_left, m_out_buf_ofs; - mz_uint8 m_dict[TDEFL_LZ_DICT_SIZE + TDEFL_MAX_MATCH_LEN - 1]; - mz_uint16 m_huff_count[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint16 m_huff_codes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_huff_code_sizes[TDEFL_MAX_HUFF_TABLES][TDEFL_MAX_HUFF_SYMBOLS]; - mz_uint8 m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE]; - mz_uint16 m_next[TDEFL_LZ_DICT_SIZE]; - mz_uint16 m_hash[TDEFL_LZ_HASH_SIZE]; - mz_uint8 m_output_buf[TDEFL_OUT_BUF_SIZE]; -} tdefl_compressor; - -// Initializes the compressor. -// There is no corresponding deinit() function because the tdefl API's do not dynamically allocate memory. -// pBut_buf_func: If NULL, output data will be supplied to the specified callback. In this case, the user should call the tdefl_compress_buffer() API for compression. -// If pBut_buf_func is NULL the user should always call the tdefl_compress() API. -// flags: See the above enums (TDEFL_HUFFMAN_ONLY, TDEFL_WRITE_ZLIB_HEADER, etc.) -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags); - -// Compresses a block of data, consuming as much of the specified input buffer as possible, and writing as much compressed data to the specified output buffer as possible. -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush); - -// tdefl_compress_buffer() is only usable when the tdefl_init() is called with a non-NULL tdefl_put_buf_func_ptr. -// tdefl_compress_buffer() always consumes the entire input buffer. -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush); - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d); -mz_uint32 tdefl_get_adler32(tdefl_compressor *d); - -// Can't use tdefl_create_comp_flags_from_zip_params if MINIZ_NO_ZLIB_APIS isn't defined, because it uses some of its macros. -#ifndef MINIZ_NO_ZLIB_APIS -// Create tdefl_compress() flags given zlib-style compression parameters. -// level may range from [0,10] (where 10 is absolute max compression, but may be much slower on some files) -// window_bits may be -15 (raw deflate) or 15 (zlib) -// strategy may be either MZ_DEFAULT_STRATEGY, MZ_FILTERED, MZ_HUFFMAN_ONLY, MZ_RLE, or MZ_FIXED -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy); -#endif // #ifndef MINIZ_NO_ZLIB_APIS - -#ifdef __cplusplus -} -#endif - -#endif // MINIZ_HEADER_INCLUDED - -// ------------------- End of Header: Implementation follows. (If you only want the header, define MINIZ_HEADER_FILE_ONLY.) - -#ifndef MINIZ_HEADER_FILE_ONLY - -typedef unsigned char mz_validate_uint16[sizeof(mz_uint16)==2 ? 1 : -1]; -typedef unsigned char mz_validate_uint32[sizeof(mz_uint32)==4 ? 1 : -1]; -typedef unsigned char mz_validate_uint64[sizeof(mz_uint64)==8 ? 1 : -1]; - -#include -#include - -#define MZ_ASSERT(x) assert(x) - -#ifdef MINIZ_NO_MALLOC - #define MZ_MALLOC(x) NULL - #define MZ_FREE(x) (void)x, ((void)0) - #define MZ_REALLOC(p, x) NULL -#else - #define MZ_MALLOC(x) malloc(x) - #define MZ_FREE(x) free(x) - #define MZ_REALLOC(p, x) realloc(p, x) -#endif - -#define MZ_MAX(a,b) (((a)>(b))?(a):(b)) -#define MZ_MIN(a,b) (((a)<(b))?(a):(b)) -#define MZ_CLEAR_OBJ(obj) memset(&(obj), 0, sizeof(obj)) - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - #define MZ_READ_LE16(p) *((const mz_uint16 *)(p)) - #define MZ_READ_LE32(p) *((const mz_uint32 *)(p)) -#else - #define MZ_READ_LE16(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U)) - #define MZ_READ_LE32(p) ((mz_uint32)(((const mz_uint8 *)(p))[0]) | ((mz_uint32)(((const mz_uint8 *)(p))[1]) << 8U) | ((mz_uint32)(((const mz_uint8 *)(p))[2]) << 16U) | ((mz_uint32)(((const mz_uint8 *)(p))[3]) << 24U)) -#endif - -#ifdef _MSC_VER - #define MZ_FORCEINLINE __forceinline -#elif defined(__GNUC__) - #define MZ_FORCEINLINE inline __attribute__((__always_inline__)) -#else - #define MZ_FORCEINLINE inline -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -// ------------------- zlib-style API's - -mz_ulong mz_adler32(mz_ulong adler, const unsigned char *ptr, size_t buf_len) -{ - mz_uint32 i, s1 = (mz_uint32)(adler & 0xffff), s2 = (mz_uint32)(adler >> 16); size_t block_len = buf_len % 5552; - if (!ptr) return MZ_ADLER32_INIT; - while (buf_len) { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - return (s2 << 16) + s1; -} - -// Karl Malbrain's compact CRC-32. See "A compact CCITT crc16 and crc32 C implementation that balances processor cache usage against speed": http://www.geocities.com/malbrain/ -mz_ulong mz_crc32(mz_ulong crc, const mz_uint8 *ptr, size_t buf_len) -{ - static const mz_uint32 s_crc32[16] = { 0, 0x1db71064, 0x3b6e20c8, 0x26d930ac, 0x76dc4190, 0x6b6b51f4, 0x4db26158, 0x5005713c, - 0xedb88320, 0xf00f9344, 0xd6d6a3e8, 0xcb61b38c, 0x9b64c2b0, 0x86d3d2d4, 0xa00ae278, 0xbdbdf21c }; - mz_uint32 crcu32 = (mz_uint32)crc; - if (!ptr) return MZ_CRC32_INIT; - crcu32 = ~crcu32; while (buf_len--) { mz_uint8 b = *ptr++; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b & 0xF)]; crcu32 = (crcu32 >> 4) ^ s_crc32[(crcu32 & 0xF) ^ (b >> 4)]; } - return ~crcu32; -} - -void mz_free(void *p) -{ - MZ_FREE(p); -} - -#ifndef MINIZ_NO_ZLIB_APIS - -static void *def_alloc_func(void *opaque, size_t items, size_t size) { (void)opaque, (void)items, (void)size; return MZ_MALLOC(items * size); } -static void def_free_func(void *opaque, void *address) { (void)opaque, (void)address; MZ_FREE(address); } -static void *def_realloc_func(void *opaque, void *address, size_t items, size_t size) { (void)opaque, (void)address, (void)items, (void)size; return MZ_REALLOC(address, items * size); } - -const char *mz_version(void) -{ - return MZ_VERSION; -} - -int mz_deflateInit(mz_streamp pStream, int level) -{ - return mz_deflateInit2(pStream, level, MZ_DEFLATED, MZ_DEFAULT_WINDOW_BITS, 9, MZ_DEFAULT_STRATEGY); -} - -int mz_deflateInit2(mz_streamp pStream, int level, int method, int window_bits, int mem_level, int strategy) -{ - tdefl_compressor *pComp; - mz_uint comp_flags = TDEFL_COMPUTE_ADLER32 | tdefl_create_comp_flags_from_zip_params(level, window_bits, strategy); - - if (!pStream) return MZ_STREAM_ERROR; - if ((method != MZ_DEFLATED) || ((mem_level < 1) || (mem_level > 9)) || ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS))) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = MZ_ADLER32_INIT; - pStream->msg = NULL; - pStream->reserved = 0; - pStream->total_in = 0; - pStream->total_out = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pComp = (tdefl_compressor *)pStream->zalloc(pStream->opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pComp; - - if (tdefl_init(pComp, NULL, NULL, comp_flags) != TDEFL_STATUS_OKAY) - { - mz_deflateEnd(pStream); - return MZ_PARAM_ERROR; - } - - return MZ_OK; -} - -int mz_deflateReset(mz_streamp pStream) -{ - if ((!pStream) || (!pStream->state) || (!pStream->zalloc) || (!pStream->zfree)) return MZ_STREAM_ERROR; - pStream->total_in = pStream->total_out = 0; - tdefl_init((tdefl_compressor*)pStream->state, NULL, NULL, ((tdefl_compressor*)pStream->state)->m_flags); - return MZ_OK; -} - -int mz_deflate(mz_streamp pStream, int flush) -{ - size_t in_bytes, out_bytes; - mz_ulong orig_total_in, orig_total_out; - int mz_status = MZ_OK; - - if ((!pStream) || (!pStream->state) || (flush < 0) || (flush > MZ_FINISH) || (!pStream->next_out)) return MZ_STREAM_ERROR; - if (!pStream->avail_out) return MZ_BUF_ERROR; - - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - - if (((tdefl_compressor*)pStream->state)->m_prev_return_status == TDEFL_STATUS_DONE) - return (flush == MZ_FINISH) ? MZ_STREAM_END : MZ_BUF_ERROR; - - orig_total_in = pStream->total_in; orig_total_out = pStream->total_out; - for ( ; ; ) - { - tdefl_status defl_status; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - - defl_status = tdefl_compress((tdefl_compressor*)pStream->state, pStream->next_in, &in_bytes, pStream->next_out, &out_bytes, (tdefl_flush)flush); - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tdefl_get_adler32((tdefl_compressor*)pStream->state); - - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; - pStream->total_out += (mz_uint)out_bytes; - - if (defl_status < 0) - { - mz_status = MZ_STREAM_ERROR; - break; - } - else if (defl_status == TDEFL_STATUS_DONE) - { - mz_status = MZ_STREAM_END; - break; - } - else if (!pStream->avail_out) - break; - else if ((!pStream->avail_in) && (flush != MZ_FINISH)) - { - if ((flush) || (pStream->total_in != orig_total_in) || (pStream->total_out != orig_total_out)) - break; - return MZ_BUF_ERROR; // Can't make forward progress without some input. - } - } - return mz_status; -} - -int mz_deflateEnd(mz_streamp pStream) -{ - if (!pStream) return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -mz_ulong mz_deflateBound(mz_streamp pStream, mz_ulong source_len) -{ - (void)pStream; - // This is really over conservative. (And lame, but it's actually pretty tricky to compute a true upper bound given the way tdefl's blocking works.) - return MZ_MAX(128 + (source_len * 110) / 100, 128 + source_len + ((source_len / (31 * 1024)) + 1) * 5); -} - -int mz_compress2(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len, int level) -{ - int status; - mz_stream stream; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_deflateInit(&stream, level); - if (status != MZ_OK) return status; - - status = mz_deflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_deflateEnd(&stream); - return (status == MZ_OK) ? MZ_BUF_ERROR : status; - } - - *pDest_len = stream.total_out; - return mz_deflateEnd(&stream); -} - -int mz_compress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - return mz_compress2(pDest, pDest_len, pSource, source_len, MZ_DEFAULT_COMPRESSION); -} - -mz_ulong mz_compressBound(mz_ulong source_len) -{ - return mz_deflateBound(NULL, source_len); -} - -typedef struct -{ - tinfl_decompressor m_decomp; - mz_uint m_dict_ofs, m_dict_avail, m_first_call, m_has_flushed; int m_window_bits; - mz_uint8 m_dict[TINFL_LZ_DICT_SIZE]; - tinfl_status m_last_status; -} inflate_state; - -int mz_inflateInit2(mz_streamp pStream, int window_bits) -{ - inflate_state *pDecomp; - if (!pStream) return MZ_STREAM_ERROR; - if ((window_bits != MZ_DEFAULT_WINDOW_BITS) && (-window_bits != MZ_DEFAULT_WINDOW_BITS)) return MZ_PARAM_ERROR; - - pStream->data_type = 0; - pStream->adler = 0; - pStream->msg = NULL; - pStream->total_in = 0; - pStream->total_out = 0; - pStream->reserved = 0; - if (!pStream->zalloc) pStream->zalloc = def_alloc_func; - if (!pStream->zfree) pStream->zfree = def_free_func; - - pDecomp = (inflate_state*)pStream->zalloc(pStream->opaque, 1, sizeof(inflate_state)); - if (!pDecomp) return MZ_MEM_ERROR; - - pStream->state = (struct mz_internal_state *)pDecomp; - - tinfl_init(&pDecomp->m_decomp); - pDecomp->m_dict_ofs = 0; - pDecomp->m_dict_avail = 0; - pDecomp->m_last_status = TINFL_STATUS_NEEDS_MORE_INPUT; - pDecomp->m_first_call = 1; - pDecomp->m_has_flushed = 0; - pDecomp->m_window_bits = window_bits; - - return MZ_OK; -} - -int mz_inflateInit(mz_streamp pStream) -{ - return mz_inflateInit2(pStream, MZ_DEFAULT_WINDOW_BITS); -} - -int mz_inflate(mz_streamp pStream, int flush) -{ - inflate_state* pState; - mz_uint n, first_call, decomp_flags = TINFL_FLAG_COMPUTE_ADLER32; - size_t in_bytes, out_bytes, orig_avail_in; - tinfl_status status; - - if ((!pStream) || (!pStream->state)) return MZ_STREAM_ERROR; - if (flush == MZ_PARTIAL_FLUSH) flush = MZ_SYNC_FLUSH; - if ((flush) && (flush != MZ_SYNC_FLUSH) && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - - pState = (inflate_state*)pStream->state; - if (pState->m_window_bits > 0) decomp_flags |= TINFL_FLAG_PARSE_ZLIB_HEADER; - orig_avail_in = pStream->avail_in; - - first_call = pState->m_first_call; pState->m_first_call = 0; - if (pState->m_last_status < 0) return MZ_DATA_ERROR; - - if (pState->m_has_flushed && (flush != MZ_FINISH)) return MZ_STREAM_ERROR; - pState->m_has_flushed |= (flush == MZ_FINISH); - - if ((flush == MZ_FINISH) && (first_call)) - { - // MZ_FINISH on the first call implies that the input and output buffers are large enough to hold the entire compressed/decompressed file. - decomp_flags |= TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF; - in_bytes = pStream->avail_in; out_bytes = pStream->avail_out; - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pStream->next_out, pStream->next_out, &out_bytes, decomp_flags); - pState->m_last_status = status; - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; pStream->total_in += (mz_uint)in_bytes; - pStream->adler = tinfl_get_adler32(&pState->m_decomp); - pStream->next_out += (mz_uint)out_bytes; pStream->avail_out -= (mz_uint)out_bytes; pStream->total_out += (mz_uint)out_bytes; - - if (status < 0) - return MZ_DATA_ERROR; - else if (status != TINFL_STATUS_DONE) - { - pState->m_last_status = TINFL_STATUS_FAILED; - return MZ_BUF_ERROR; - } - return MZ_STREAM_END; - } - // flush != MZ_FINISH then we must assume there's more input. - if (flush != MZ_FINISH) decomp_flags |= TINFL_FLAG_HAS_MORE_INPUT; - - if (pState->m_dict_avail) - { - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - return ((pState->m_last_status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; - } - - for ( ; ; ) - { - in_bytes = pStream->avail_in; - out_bytes = TINFL_LZ_DICT_SIZE - pState->m_dict_ofs; - - status = tinfl_decompress(&pState->m_decomp, pStream->next_in, &in_bytes, pState->m_dict, pState->m_dict + pState->m_dict_ofs, &out_bytes, decomp_flags); - pState->m_last_status = status; - - pStream->next_in += (mz_uint)in_bytes; pStream->avail_in -= (mz_uint)in_bytes; - pStream->total_in += (mz_uint)in_bytes; pStream->adler = tinfl_get_adler32(&pState->m_decomp); - - pState->m_dict_avail = (mz_uint)out_bytes; - - n = MZ_MIN(pState->m_dict_avail, pStream->avail_out); - memcpy(pStream->next_out, pState->m_dict + pState->m_dict_ofs, n); - pStream->next_out += n; pStream->avail_out -= n; pStream->total_out += n; - pState->m_dict_avail -= n; pState->m_dict_ofs = (pState->m_dict_ofs + n) & (TINFL_LZ_DICT_SIZE - 1); - - if (status < 0) - return MZ_DATA_ERROR; // Stream is corrupted (there could be some uncompressed data left in the output dictionary - oh well). - else if ((status == TINFL_STATUS_NEEDS_MORE_INPUT) && (!orig_avail_in)) - return MZ_BUF_ERROR; // Signal caller that we can't make forward progress without supplying more input or by setting flush to MZ_FINISH. - else if (flush == MZ_FINISH) - { - // The output buffer MUST be large to hold the remaining uncompressed data when flush==MZ_FINISH. - if (status == TINFL_STATUS_DONE) - return pState->m_dict_avail ? MZ_BUF_ERROR : MZ_STREAM_END; - // status here must be TINFL_STATUS_HAS_MORE_OUTPUT, which means there's at least 1 more byte on the way. If there's no more room left in the output buffer then something is wrong. - else if (!pStream->avail_out) - return MZ_BUF_ERROR; - } - else if ((status == TINFL_STATUS_DONE) || (!pStream->avail_in) || (!pStream->avail_out) || (pState->m_dict_avail)) - break; - } - - return ((status == TINFL_STATUS_DONE) && (!pState->m_dict_avail)) ? MZ_STREAM_END : MZ_OK; -} - -int mz_inflateEnd(mz_streamp pStream) -{ - if (!pStream) - return MZ_STREAM_ERROR; - if (pStream->state) - { - pStream->zfree(pStream->opaque, pStream->state); - pStream->state = NULL; - } - return MZ_OK; -} - -int mz_uncompress(unsigned char *pDest, mz_ulong *pDest_len, const unsigned char *pSource, mz_ulong source_len) -{ - mz_stream stream; - int status; - memset(&stream, 0, sizeof(stream)); - - // In case mz_ulong is 64-bits (argh I hate longs). - if ((source_len | *pDest_len) > 0xFFFFFFFFU) return MZ_PARAM_ERROR; - - stream.next_in = pSource; - stream.avail_in = (mz_uint32)source_len; - stream.next_out = pDest; - stream.avail_out = (mz_uint32)*pDest_len; - - status = mz_inflateInit(&stream); - if (status != MZ_OK) - return status; - - status = mz_inflate(&stream, MZ_FINISH); - if (status != MZ_STREAM_END) - { - mz_inflateEnd(&stream); - return ((status == MZ_BUF_ERROR) && (!stream.avail_in)) ? MZ_DATA_ERROR : status; - } - *pDest_len = stream.total_out; - - return mz_inflateEnd(&stream); -} - -const char *mz_error(int err) -{ - static struct { int m_err; const char *m_pDesc; } s_error_descs[] = - { - { MZ_OK, "" }, { MZ_STREAM_END, "stream end" }, { MZ_NEED_DICT, "need dictionary" }, { MZ_ERRNO, "file error" }, { MZ_STREAM_ERROR, "stream error" }, - { MZ_DATA_ERROR, "data error" }, { MZ_MEM_ERROR, "out of memory" }, { MZ_BUF_ERROR, "buf error" }, { MZ_VERSION_ERROR, "version error" }, { MZ_PARAM_ERROR, "parameter error" } - }; - mz_uint i; for (i = 0; i < sizeof(s_error_descs) / sizeof(s_error_descs[0]); ++i) if (s_error_descs[i].m_err == err) return s_error_descs[i].m_pDesc; - return NULL; -} - -#endif //MINIZ_NO_ZLIB_APIS - -// ------------------- Low-level Decompression (completely independent from all compression API's) - -#define TINFL_MEMCPY(d, s, l) memcpy(d, s, l) -#define TINFL_MEMSET(p, c, l) memset(p, c, l) - -#define TINFL_CR_BEGIN switch(r->m_state) { case 0: -#define TINFL_CR_RETURN(state_index, result) do { status = result; r->m_state = state_index; goto common_exit; case state_index:; } MZ_MACRO_END -#define TINFL_CR_RETURN_FOREVER(state_index, result) do { for ( ; ; ) { TINFL_CR_RETURN(state_index, result); } } MZ_MACRO_END -#define TINFL_CR_FINISH } - -// TODO: If the caller has indicated that there's no more input, and we attempt to read beyond the input buf, then something is wrong with the input because the inflator never -// reads ahead more than it needs to. Currently TINFL_GET_BYTE() pads the end of the stream with 0's in this scenario. -#define TINFL_GET_BYTE(state_index, c) do { \ - if (pIn_buf_cur >= pIn_buf_end) { \ - for ( ; ; ) { \ - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) { \ - TINFL_CR_RETURN(state_index, TINFL_STATUS_NEEDS_MORE_INPUT); \ - if (pIn_buf_cur < pIn_buf_end) { \ - c = *pIn_buf_cur++; \ - break; \ - } \ - } else { \ - c = 0; \ - break; \ - } \ - } \ - } else c = *pIn_buf_cur++; } MZ_MACRO_END - -#define TINFL_NEED_BITS(state_index, n) do { mz_uint c; TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; } while (num_bits < (mz_uint)(n)) -#define TINFL_SKIP_BITS(state_index, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END -#define TINFL_GET_BITS(state_index, b, n) do { if (num_bits < (mz_uint)(n)) { TINFL_NEED_BITS(state_index, n); } b = bit_buf & ((1 << (n)) - 1); bit_buf >>= (n); num_bits -= (n); } MZ_MACRO_END - -// TINFL_HUFF_BITBUF_FILL() is only used rarely, when the number of bytes remaining in the input buffer falls below 2. -// It reads just enough bytes from the input stream that are needed to decode the next Huffman code (and absolutely no more). It works by trying to fully decode a -// Huffman code by using whatever bits are currently present in the bit buffer. If this fails, it reads another byte, and tries again until it succeeds or until the -// bit buffer contains >=15 bits (deflate's max. Huffman code size). -#define TINFL_HUFF_BITBUF_FILL(state_index, pHuff) \ - do { \ - temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]; \ - if (temp >= 0) { \ - code_len = temp >> 9; \ - if ((code_len) && (num_bits >= code_len)) \ - break; \ - } else if (num_bits > TINFL_FAST_LOOKUP_BITS) { \ - code_len = TINFL_FAST_LOOKUP_BITS; \ - do { \ - temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; \ - } while ((temp < 0) && (num_bits >= (code_len + 1))); if (temp >= 0) break; \ - } TINFL_GET_BYTE(state_index, c); bit_buf |= (((tinfl_bit_buf_t)c) << num_bits); num_bits += 8; \ - } while (num_bits < 15); - -// TINFL_HUFF_DECODE() decodes the next Huffman coded symbol. It's more complex than you would initially expect because the zlib API expects the decompressor to never read -// beyond the final byte of the deflate stream. (In other words, when this macro wants to read another byte from the input, it REALLY needs another byte in order to fully -// decode the next Huffman code.) Handling this properly is particularly important on raw deflate (non-zlib) streams, which aren't followed by a byte aligned adler-32. -// The slow path is only executed at the very end of the input buffer. -#define TINFL_HUFF_DECODE(state_index, sym, pHuff) do { \ - int temp; mz_uint code_len, c; \ - if (num_bits < 15) { \ - if ((pIn_buf_end - pIn_buf_cur) < 2) { \ - TINFL_HUFF_BITBUF_FILL(state_index, pHuff); \ - } else { \ - bit_buf |= (((tinfl_bit_buf_t)pIn_buf_cur[0]) << num_bits) | (((tinfl_bit_buf_t)pIn_buf_cur[1]) << (num_bits + 8)); pIn_buf_cur += 2; num_bits += 16; \ - } \ - } \ - if ((temp = (pHuff)->m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) \ - code_len = temp >> 9, temp &= 511; \ - else { \ - code_len = TINFL_FAST_LOOKUP_BITS; do { temp = (pHuff)->m_tree[~temp + ((bit_buf >> code_len++) & 1)]; } while (temp < 0); \ - } sym = temp; bit_buf >>= code_len; num_bits -= code_len; } MZ_MACRO_END - -tinfl_status tinfl_decompress(tinfl_decompressor *r, const mz_uint8 *pIn_buf_next, size_t *pIn_buf_size, mz_uint8 *pOut_buf_start, mz_uint8 *pOut_buf_next, size_t *pOut_buf_size, const mz_uint32 decomp_flags) -{ - static const int s_length_base[31] = { 3,4,5,6,7,8,9,10,11,13, 15,17,19,23,27,31,35,43,51,59, 67,83,99,115,131,163,195,227,258,0,0 }; - static const int s_length_extra[31]= { 0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0,0,0 }; - static const int s_dist_base[32] = { 1,2,3,4,5,7,9,13,17,25,33,49,65,97,129,193, 257,385,513,769,1025,1537,2049,3073,4097,6145,8193,12289,16385,24577,0,0}; - static const int s_dist_extra[32] = { 0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13}; - static const mz_uint8 s_length_dezigzag[19] = { 16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15 }; - static const int s_min_table_sizes[3] = { 257, 1, 4 }; - - tinfl_status status = TINFL_STATUS_FAILED; mz_uint32 num_bits, dist, counter, num_extra; tinfl_bit_buf_t bit_buf; - const mz_uint8 *pIn_buf_cur = pIn_buf_next, *const pIn_buf_end = pIn_buf_next + *pIn_buf_size; - mz_uint8 *pOut_buf_cur = pOut_buf_next, *const pOut_buf_end = pOut_buf_next + *pOut_buf_size; - size_t out_buf_size_mask = (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF) ? (size_t)-1 : ((pOut_buf_next - pOut_buf_start) + *pOut_buf_size) - 1, dist_from_out_buf_start; - - // Ensure the output buffer's size is a power of 2, unless the output buffer is large enough to hold the entire output file (in which case it doesn't matter). - if (((out_buf_size_mask + 1) & out_buf_size_mask) || (pOut_buf_next < pOut_buf_start)) { *pIn_buf_size = *pOut_buf_size = 0; return TINFL_STATUS_BAD_PARAM; } - - num_bits = r->m_num_bits; bit_buf = r->m_bit_buf; dist = r->m_dist; counter = r->m_counter; num_extra = r->m_num_extra; dist_from_out_buf_start = r->m_dist_from_out_buf_start; - TINFL_CR_BEGIN - - bit_buf = num_bits = dist = counter = num_extra = r->m_zhdr0 = r->m_zhdr1 = 0; r->m_z_adler32 = r->m_check_adler32 = 1; - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_GET_BYTE(1, r->m_zhdr0); TINFL_GET_BYTE(2, r->m_zhdr1); - counter = (((r->m_zhdr0 * 256 + r->m_zhdr1) % 31 != 0) || (r->m_zhdr1 & 32) || ((r->m_zhdr0 & 15) != 8)); - if (!(decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) counter |= (((1U << (8U + (r->m_zhdr0 >> 4))) > 32768U) || ((out_buf_size_mask + 1) < (size_t)(1U << (8U + (r->m_zhdr0 >> 4))))); - if (counter) { TINFL_CR_RETURN_FOREVER(36, TINFL_STATUS_FAILED); } - } - - do - { - TINFL_GET_BITS(3, r->m_final, 3); r->m_type = r->m_final >> 1; - if (r->m_type == 0) - { - TINFL_SKIP_BITS(5, num_bits & 7); - for (counter = 0; counter < 4; ++counter) { if (num_bits) TINFL_GET_BITS(6, r->m_raw_header[counter], 8); else TINFL_GET_BYTE(7, r->m_raw_header[counter]); } - if ((counter = (r->m_raw_header[0] | (r->m_raw_header[1] << 8))) != (mz_uint)(0xFFFF ^ (r->m_raw_header[2] | (r->m_raw_header[3] << 8)))) { TINFL_CR_RETURN_FOREVER(39, TINFL_STATUS_FAILED); } - while ((counter) && (num_bits)) - { - TINFL_GET_BITS(51, dist, 8); - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(52, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)dist; - counter--; - } - while (counter) - { - size_t n; while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(9, TINFL_STATUS_HAS_MORE_OUTPUT); } - while (pIn_buf_cur >= pIn_buf_end) - { - if (decomp_flags & TINFL_FLAG_HAS_MORE_INPUT) - { - TINFL_CR_RETURN(38, TINFL_STATUS_NEEDS_MORE_INPUT); - } - else - { - TINFL_CR_RETURN_FOREVER(40, TINFL_STATUS_FAILED); - } - } - n = MZ_MIN(MZ_MIN((size_t)(pOut_buf_end - pOut_buf_cur), (size_t)(pIn_buf_end - pIn_buf_cur)), counter); - TINFL_MEMCPY(pOut_buf_cur, pIn_buf_cur, n); pIn_buf_cur += n; pOut_buf_cur += n; counter -= (mz_uint)n; - } - } - else if (r->m_type == 3) - { - TINFL_CR_RETURN_FOREVER(10, TINFL_STATUS_FAILED); - } - else - { - if (r->m_type == 1) - { - mz_uint8 *p = r->m_tables[0].m_code_size; mz_uint i; - r->m_table_sizes[0] = 288; r->m_table_sizes[1] = 32; TINFL_MEMSET(r->m_tables[1].m_code_size, 5, 32); - for ( i = 0; i <= 143; ++i) *p++ = 8; for ( ; i <= 255; ++i) *p++ = 9; for ( ; i <= 279; ++i) *p++ = 7; for ( ; i <= 287; ++i) *p++ = 8; - } - else - { - for (counter = 0; counter < 3; counter++) { TINFL_GET_BITS(11, r->m_table_sizes[counter], "\05\05\04"[counter]); r->m_table_sizes[counter] += s_min_table_sizes[counter]; } - MZ_CLEAR_OBJ(r->m_tables[2].m_code_size); for (counter = 0; counter < r->m_table_sizes[2]; counter++) { mz_uint s; TINFL_GET_BITS(14, s, 3); r->m_tables[2].m_code_size[s_length_dezigzag[counter]] = (mz_uint8)s; } - r->m_table_sizes[2] = 19; - } - for ( ; (int)r->m_type >= 0; r->m_type--) - { - int tree_next, tree_cur; tinfl_huff_table *pTable; - mz_uint i, j, used_syms, total, sym_index, next_code[17], total_syms[16]; pTable = &r->m_tables[r->m_type]; MZ_CLEAR_OBJ(total_syms); MZ_CLEAR_OBJ(pTable->m_look_up); MZ_CLEAR_OBJ(pTable->m_tree); - for (i = 0; i < r->m_table_sizes[r->m_type]; ++i) total_syms[pTable->m_code_size[i]]++; - used_syms = 0, total = 0; next_code[0] = next_code[1] = 0; - for (i = 1; i <= 15; ++i) { used_syms += total_syms[i]; next_code[i + 1] = (total = ((total + total_syms[i]) << 1)); } - if ((65536 != total) && (used_syms > 1)) - { - TINFL_CR_RETURN_FOREVER(35, TINFL_STATUS_FAILED); - } - for (tree_next = -1, sym_index = 0; sym_index < r->m_table_sizes[r->m_type]; ++sym_index) - { - mz_uint rev_code = 0, l, cur_code, code_size = pTable->m_code_size[sym_index]; if (!code_size) continue; - cur_code = next_code[code_size]++; for (l = code_size; l > 0; l--, cur_code >>= 1) rev_code = (rev_code << 1) | (cur_code & 1); - if (code_size <= TINFL_FAST_LOOKUP_BITS) { mz_int16 k = (mz_int16)((code_size << 9) | sym_index); while (rev_code < TINFL_FAST_LOOKUP_SIZE) { pTable->m_look_up[rev_code] = k; rev_code += (1 << code_size); } continue; } - if (0 == (tree_cur = pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)])) { pTable->m_look_up[rev_code & (TINFL_FAST_LOOKUP_SIZE - 1)] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } - rev_code >>= (TINFL_FAST_LOOKUP_BITS - 1); - for (j = code_size; j > (TINFL_FAST_LOOKUP_BITS + 1); j--) - { - tree_cur -= ((rev_code >>= 1) & 1); - if (!pTable->m_tree[-tree_cur - 1]) { pTable->m_tree[-tree_cur - 1] = (mz_int16)tree_next; tree_cur = tree_next; tree_next -= 2; } else tree_cur = pTable->m_tree[-tree_cur - 1]; - } - tree_cur -= ((rev_code >>= 1) & 1); pTable->m_tree[-tree_cur - 1] = (mz_int16)sym_index; - } - if (r->m_type == 2) - { - for (counter = 0; counter < (r->m_table_sizes[0] + r->m_table_sizes[1]); ) - { - mz_uint s; TINFL_HUFF_DECODE(16, dist, &r->m_tables[2]); if (dist < 16) { r->m_len_codes[counter++] = (mz_uint8)dist; continue; } - if ((dist == 16) && (!counter)) - { - TINFL_CR_RETURN_FOREVER(17, TINFL_STATUS_FAILED); - } - num_extra = "\02\03\07"[dist - 16]; TINFL_GET_BITS(18, s, num_extra); s += "\03\03\013"[dist - 16]; - TINFL_MEMSET(r->m_len_codes + counter, (dist == 16) ? r->m_len_codes[counter - 1] : 0, s); counter += s; - } - if ((r->m_table_sizes[0] + r->m_table_sizes[1]) != counter) - { - TINFL_CR_RETURN_FOREVER(21, TINFL_STATUS_FAILED); - } - TINFL_MEMCPY(r->m_tables[0].m_code_size, r->m_len_codes, r->m_table_sizes[0]); TINFL_MEMCPY(r->m_tables[1].m_code_size, r->m_len_codes + r->m_table_sizes[0], r->m_table_sizes[1]); - } - } - for ( ; ; ) - { - mz_uint8 *pSrc; - for ( ; ; ) - { - if (((pIn_buf_end - pIn_buf_cur) < 4) || ((pOut_buf_end - pOut_buf_cur) < 2)) - { - TINFL_HUFF_DECODE(23, counter, &r->m_tables[0]); - if (counter >= 256) - break; - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(24, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = (mz_uint8)counter; - } - else - { - int sym2; mz_uint code_len; -#if TINFL_USE_64BIT_BITBUF - if (num_bits < 30) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE32(pIn_buf_cur)) << num_bits); pIn_buf_cur += 4; num_bits += 32; } -#else - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - counter = sym2; bit_buf >>= code_len; num_bits -= code_len; - if (counter & 256) - break; - -#if !TINFL_USE_64BIT_BITBUF - if (num_bits < 15) { bit_buf |= (((tinfl_bit_buf_t)MZ_READ_LE16(pIn_buf_cur)) << num_bits); pIn_buf_cur += 2; num_bits += 16; } -#endif - if ((sym2 = r->m_tables[0].m_look_up[bit_buf & (TINFL_FAST_LOOKUP_SIZE - 1)]) >= 0) - code_len = sym2 >> 9; - else - { - code_len = TINFL_FAST_LOOKUP_BITS; do { sym2 = r->m_tables[0].m_tree[~sym2 + ((bit_buf >> code_len++) & 1)]; } while (sym2 < 0); - } - bit_buf >>= code_len; num_bits -= code_len; - - pOut_buf_cur[0] = (mz_uint8)counter; - if (sym2 & 256) - { - pOut_buf_cur++; - counter = sym2; - break; - } - pOut_buf_cur[1] = (mz_uint8)sym2; - pOut_buf_cur += 2; - } - } - if ((counter &= 511) == 256) break; - - num_extra = s_length_extra[counter - 257]; counter = s_length_base[counter - 257]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(25, extra_bits, num_extra); counter += extra_bits; } - - TINFL_HUFF_DECODE(26, dist, &r->m_tables[1]); - num_extra = s_dist_extra[dist]; dist = s_dist_base[dist]; - if (num_extra) { mz_uint extra_bits; TINFL_GET_BITS(27, extra_bits, num_extra); dist += extra_bits; } - - dist_from_out_buf_start = pOut_buf_cur - pOut_buf_start; - if ((dist > dist_from_out_buf_start) && (decomp_flags & TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF)) - { - TINFL_CR_RETURN_FOREVER(37, TINFL_STATUS_FAILED); - } - - pSrc = pOut_buf_start + ((dist_from_out_buf_start - dist) & out_buf_size_mask); - - if ((MZ_MAX(pOut_buf_cur, pSrc) + counter) > pOut_buf_end) - { - while (counter--) - { - while (pOut_buf_cur >= pOut_buf_end) { TINFL_CR_RETURN(53, TINFL_STATUS_HAS_MORE_OUTPUT); } - *pOut_buf_cur++ = pOut_buf_start[(dist_from_out_buf_start++ - dist) & out_buf_size_mask]; - } - continue; - } -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - else if ((counter >= 9) && (counter <= dist)) - { - const mz_uint8 *pSrc_end = pSrc + (counter & ~7); - do - { - ((mz_uint32 *)pOut_buf_cur)[0] = ((const mz_uint32 *)pSrc)[0]; - ((mz_uint32 *)pOut_buf_cur)[1] = ((const mz_uint32 *)pSrc)[1]; - pOut_buf_cur += 8; - } while ((pSrc += 8) < pSrc_end); - if ((counter &= 7) < 3) - { - if (counter) - { - pOut_buf_cur[0] = pSrc[0]; - if (counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - continue; - } - } -#endif - do - { - pOut_buf_cur[0] = pSrc[0]; - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur[2] = pSrc[2]; - pOut_buf_cur += 3; pSrc += 3; - } while ((int)(counter -= 3) > 2); - if ((int)counter > 0) - { - pOut_buf_cur[0] = pSrc[0]; - if ((int)counter > 1) - pOut_buf_cur[1] = pSrc[1]; - pOut_buf_cur += counter; - } - } - } - } while (!(r->m_final & 1)); - if (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) - { - TINFL_SKIP_BITS(32, num_bits & 7); for (counter = 0; counter < 4; ++counter) { mz_uint s; if (num_bits) TINFL_GET_BITS(41, s, 8); else TINFL_GET_BYTE(42, s); r->m_z_adler32 = (r->m_z_adler32 << 8) | s; } - } - TINFL_CR_RETURN_FOREVER(34, TINFL_STATUS_DONE); - TINFL_CR_FINISH - -common_exit: - r->m_num_bits = num_bits; r->m_bit_buf = bit_buf; r->m_dist = dist; r->m_counter = counter; r->m_num_extra = num_extra; r->m_dist_from_out_buf_start = dist_from_out_buf_start; - *pIn_buf_size = pIn_buf_cur - pIn_buf_next; *pOut_buf_size = pOut_buf_cur - pOut_buf_next; - if ((decomp_flags & (TINFL_FLAG_PARSE_ZLIB_HEADER | TINFL_FLAG_COMPUTE_ADLER32)) && (status >= 0)) - { - const mz_uint8 *ptr = pOut_buf_next; size_t buf_len = *pOut_buf_size; - mz_uint32 i, s1 = r->m_check_adler32 & 0xffff, s2 = r->m_check_adler32 >> 16; size_t block_len = buf_len % 5552; - while (buf_len) - { - for (i = 0; i + 7 < block_len; i += 8, ptr += 8) - { - s1 += ptr[0], s2 += s1; s1 += ptr[1], s2 += s1; s1 += ptr[2], s2 += s1; s1 += ptr[3], s2 += s1; - s1 += ptr[4], s2 += s1; s1 += ptr[5], s2 += s1; s1 += ptr[6], s2 += s1; s1 += ptr[7], s2 += s1; - } - for ( ; i < block_len; ++i) s1 += *ptr++, s2 += s1; - s1 %= 65521U, s2 %= 65521U; buf_len -= block_len; block_len = 5552; - } - r->m_check_adler32 = (s2 << 16) + s1; if ((status == TINFL_STATUS_DONE) && (decomp_flags & TINFL_FLAG_PARSE_ZLIB_HEADER) && (r->m_check_adler32 != r->m_z_adler32)) status = TINFL_STATUS_ADLER32_MISMATCH; - } - return status; -} - -// Higher level helper functions. -void *tinfl_decompress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tinfl_decompressor decomp; void *pBuf = NULL, *pNew_buf; size_t src_buf_ofs = 0, out_buf_capacity = 0; - *pOut_len = 0; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t src_buf_size = src_buf_len - src_buf_ofs, dst_buf_size = out_buf_capacity - *pOut_len, new_out_buf_capacity; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf + src_buf_ofs, &src_buf_size, (mz_uint8*)pBuf, pBuf ? (mz_uint8*)pBuf + *pOut_len : NULL, &dst_buf_size, - (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - if ((status < 0) || (status == TINFL_STATUS_NEEDS_MORE_INPUT)) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - src_buf_ofs += src_buf_size; - *pOut_len += dst_buf_size; - if (status == TINFL_STATUS_DONE) break; - new_out_buf_capacity = out_buf_capacity * 2; if (new_out_buf_capacity < 128) new_out_buf_capacity = 128; - pNew_buf = MZ_REALLOC(pBuf, new_out_buf_capacity); - if (!pNew_buf) - { - MZ_FREE(pBuf); *pOut_len = 0; return NULL; - } - pBuf = pNew_buf; out_buf_capacity = new_out_buf_capacity; - } - return pBuf; -} - -size_t tinfl_decompress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tinfl_decompressor decomp; tinfl_status status; tinfl_init(&decomp); - status = tinfl_decompress(&decomp, (const mz_uint8*)pSrc_buf, &src_buf_len, (mz_uint8*)pOut_buf, (mz_uint8*)pOut_buf, &out_buf_len, (flags & ~TINFL_FLAG_HAS_MORE_INPUT) | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF); - return (status != TINFL_STATUS_DONE) ? TINFL_DECOMPRESS_MEM_TO_MEM_FAILED : out_buf_len; -} - -int tinfl_decompress_mem_to_callback(const void *pIn_buf, size_t *pIn_buf_size, tinfl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - int result = 0; - tinfl_decompressor decomp; - mz_uint8 *pDict = (mz_uint8*)MZ_MALLOC(TINFL_LZ_DICT_SIZE); size_t in_buf_ofs = 0, dict_ofs = 0; - if (!pDict) - return TINFL_STATUS_FAILED; - tinfl_init(&decomp); - for ( ; ; ) - { - size_t in_buf_size = *pIn_buf_size - in_buf_ofs, dst_buf_size = TINFL_LZ_DICT_SIZE - dict_ofs; - tinfl_status status = tinfl_decompress(&decomp, (const mz_uint8*)pIn_buf + in_buf_ofs, &in_buf_size, pDict, pDict + dict_ofs, &dst_buf_size, - (flags & ~(TINFL_FLAG_HAS_MORE_INPUT | TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF))); - in_buf_ofs += in_buf_size; - if ((dst_buf_size) && (!(*pPut_buf_func)(pDict + dict_ofs, (int)dst_buf_size, pPut_buf_user))) - break; - if (status != TINFL_STATUS_HAS_MORE_OUTPUT) - { - result = (status == TINFL_STATUS_DONE); - break; - } - dict_ofs = (dict_ofs + dst_buf_size) & (TINFL_LZ_DICT_SIZE - 1); - } - MZ_FREE(pDict); - *pIn_buf_size = in_buf_ofs; - return result; -} - -// ------------------- Low-level Compression (independent from all decompression API's) - -// Purposely making these tables static for faster init and thread safety. -static const mz_uint16 s_tdefl_len_sym[256] = { - 257,258,259,260,261,262,263,264,265,265,266,266,267,267,268,268,269,269,269,269,270,270,270,270,271,271,271,271,272,272,272,272, - 273,273,273,273,273,273,273,273,274,274,274,274,274,274,274,274,275,275,275,275,275,275,275,275,276,276,276,276,276,276,276,276, - 277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,277,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278,278, - 279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,279,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280,280, - 281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281,281, - 282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282,282, - 283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283,283, - 284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,284,285 }; - -static const mz_uint8 s_tdefl_len_extra[256] = { - 0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3, - 4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,0 }; - -static const mz_uint8 s_tdefl_small_dist_sym[512] = { - 0,1,2,3,4,4,5,5,6,6,6,6,7,7,7,7,8,8,8,8,8,8,8,8,9,9,9,9,9,9,9,9,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,11,11,11,11,11,11, - 11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14,14, - 14,14,14,14,14,14,14,14,14,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15, - 15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16, - 16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17, - 17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17 }; - -static const mz_uint8 s_tdefl_small_dist_extra[512] = { - 0,0,0,0,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5, - 5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, - 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, - 7,7,7,7,7,7,7,7 }; - -static const mz_uint8 s_tdefl_large_dist_sym[128] = { - 0,0,18,19,20,20,21,21,22,22,22,22,23,23,23,23,24,24,24,24,24,24,24,24,25,25,25,25,25,25,25,25,26,26,26,26,26,26,26,26,26,26,26,26, - 26,26,26,26,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28,28, - 28,28,28,28,28,28,28,28,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29,29 }; - -static const mz_uint8 s_tdefl_large_dist_extra[128] = { - 0,0,8,8,9,9,9,9,10,10,10,10,10,10,10,10,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,11,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12, - 12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,12,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13, - 13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13,13 }; - -// Radix sorts tdefl_sym_freq[] array by 16-bit key m_key. Returns ptr to sorted values. -typedef struct { mz_uint16 m_key, m_sym_index; } tdefl_sym_freq; -static tdefl_sym_freq* tdefl_radix_sort_syms(mz_uint num_syms, tdefl_sym_freq* pSyms0, tdefl_sym_freq* pSyms1) -{ - mz_uint32 total_passes = 2, pass_shift, pass, i, hist[256 * 2]; tdefl_sym_freq* pCur_syms = pSyms0, *pNew_syms = pSyms1; MZ_CLEAR_OBJ(hist); - for (i = 0; i < num_syms; i++) { mz_uint freq = pSyms0[i].m_key; hist[freq & 0xFF]++; hist[256 + ((freq >> 8) & 0xFF)]++; } - while ((total_passes > 1) && (num_syms == hist[(total_passes - 1) * 256])) total_passes--; - for (pass_shift = 0, pass = 0; pass < total_passes; pass++, pass_shift += 8) - { - const mz_uint32* pHist = &hist[pass << 8]; - mz_uint offsets[256], cur_ofs = 0; - for (i = 0; i < 256; i++) { offsets[i] = cur_ofs; cur_ofs += pHist[i]; } - for (i = 0; i < num_syms; i++) pNew_syms[offsets[(pCur_syms[i].m_key >> pass_shift) & 0xFF]++] = pCur_syms[i]; - { tdefl_sym_freq* t = pCur_syms; pCur_syms = pNew_syms; pNew_syms = t; } - } - return pCur_syms; -} - -// tdefl_calculate_minimum_redundancy() originally written by: Alistair Moffat, alistair@cs.mu.oz.au, Jyrki Katajainen, jyrki@diku.dk, November 1996. -static void tdefl_calculate_minimum_redundancy(tdefl_sym_freq *A, int n) -{ - int root, leaf, next, avbl, used, dpth; - if (n==0) return; else if (n==1) { A[0].m_key = 1; return; } - A[0].m_key += A[1].m_key; root = 0; leaf = 2; - for (next=1; next < n-1; next++) - { - if (leaf>=n || A[root].m_key=n || (root=0; next--) A[next].m_key = A[A[next].m_key].m_key+1; - avbl = 1; used = dpth = 0; root = n-2; next = n-1; - while (avbl>0) - { - while (root>=0 && (int)A[root].m_key==dpth) { used++; root--; } - while (avbl>used) { A[next--].m_key = (mz_uint16)(dpth); avbl--; } - avbl = 2*used; dpth++; used = 0; - } -} - -// Limits canonical Huffman code table's max code size. -enum { TDEFL_MAX_SUPPORTED_HUFF_CODESIZE = 32 }; -static void tdefl_huffman_enforce_max_code_size(int *pNum_codes, int code_list_len, int max_code_size) -{ - int i; mz_uint32 total = 0; if (code_list_len <= 1) return; - for (i = max_code_size + 1; i <= TDEFL_MAX_SUPPORTED_HUFF_CODESIZE; i++) pNum_codes[max_code_size] += pNum_codes[i]; - for (i = max_code_size; i > 0; i--) total += (((mz_uint32)pNum_codes[i]) << (max_code_size - i)); - while (total != (1UL << max_code_size)) - { - pNum_codes[max_code_size]--; - for (i = max_code_size - 1; i > 0; i--) if (pNum_codes[i]) { pNum_codes[i]--; pNum_codes[i + 1] += 2; break; } - total--; - } -} - -static void tdefl_optimize_huffman_table(tdefl_compressor *d, int table_num, int table_len, int code_size_limit, int static_table) -{ - int i, j, l, num_codes[1 + TDEFL_MAX_SUPPORTED_HUFF_CODESIZE]; mz_uint next_code[TDEFL_MAX_SUPPORTED_HUFF_CODESIZE + 1]; MZ_CLEAR_OBJ(num_codes); - if (static_table) - { - for (i = 0; i < table_len; i++) num_codes[d->m_huff_code_sizes[table_num][i]]++; - } - else - { - tdefl_sym_freq syms0[TDEFL_MAX_HUFF_SYMBOLS], syms1[TDEFL_MAX_HUFF_SYMBOLS], *pSyms; - int num_used_syms = 0; - const mz_uint16 *pSym_count = &d->m_huff_count[table_num][0]; - for (i = 0; i < table_len; i++) if (pSym_count[i]) { syms0[num_used_syms].m_key = (mz_uint16)pSym_count[i]; syms0[num_used_syms++].m_sym_index = (mz_uint16)i; } - - pSyms = tdefl_radix_sort_syms(num_used_syms, syms0, syms1); tdefl_calculate_minimum_redundancy(pSyms, num_used_syms); - - for (i = 0; i < num_used_syms; i++) num_codes[pSyms[i].m_key]++; - - tdefl_huffman_enforce_max_code_size(num_codes, num_used_syms, code_size_limit); - - MZ_CLEAR_OBJ(d->m_huff_code_sizes[table_num]); MZ_CLEAR_OBJ(d->m_huff_codes[table_num]); - for (i = 1, j = num_used_syms; i <= code_size_limit; i++) - for (l = num_codes[i]; l > 0; l--) d->m_huff_code_sizes[table_num][pSyms[--j].m_sym_index] = (mz_uint8)(i); - } - - next_code[1] = 0; for (j = 0, i = 2; i <= code_size_limit; i++) next_code[i] = j = ((j + num_codes[i - 1]) << 1); - - for (i = 0; i < table_len; i++) - { - mz_uint rev_code = 0, code, code_size; if ((code_size = d->m_huff_code_sizes[table_num][i]) == 0) continue; - code = next_code[code_size]++; for (l = code_size; l > 0; l--, code >>= 1) rev_code = (rev_code << 1) | (code & 1); - d->m_huff_codes[table_num][i] = (mz_uint16)rev_code; - } -} - -#define TDEFL_PUT_BITS(b, l) do { \ - mz_uint bits = b; mz_uint len = l; MZ_ASSERT(bits <= ((1U << len) - 1U)); \ - d->m_bit_buffer |= (bits << d->m_bits_in); d->m_bits_in += len; \ - while (d->m_bits_in >= 8) { \ - if (d->m_pOutput_buf < d->m_pOutput_buf_end) \ - *d->m_pOutput_buf++ = (mz_uint8)(d->m_bit_buffer); \ - d->m_bit_buffer >>= 8; \ - d->m_bits_in -= 8; \ - } \ -} MZ_MACRO_END - -#define TDEFL_RLE_PREV_CODE_SIZE() { if (rle_repeat_count) { \ - if (rle_repeat_count < 3) { \ - d->m_huff_count[2][prev_code_size] = (mz_uint16)(d->m_huff_count[2][prev_code_size] + rle_repeat_count); \ - while (rle_repeat_count--) packed_code_sizes[num_packed_code_sizes++] = prev_code_size; \ - } else { \ - d->m_huff_count[2][16] = (mz_uint16)(d->m_huff_count[2][16] + 1); packed_code_sizes[num_packed_code_sizes++] = 16; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_repeat_count - 3); \ -} rle_repeat_count = 0; } } - -#define TDEFL_RLE_ZERO_CODE_SIZE() { if (rle_z_count) { \ - if (rle_z_count < 3) { \ - d->m_huff_count[2][0] = (mz_uint16)(d->m_huff_count[2][0] + rle_z_count); while (rle_z_count--) packed_code_sizes[num_packed_code_sizes++] = 0; \ - } else if (rle_z_count <= 10) { \ - d->m_huff_count[2][17] = (mz_uint16)(d->m_huff_count[2][17] + 1); packed_code_sizes[num_packed_code_sizes++] = 17; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 3); \ - } else { \ - d->m_huff_count[2][18] = (mz_uint16)(d->m_huff_count[2][18] + 1); packed_code_sizes[num_packed_code_sizes++] = 18; packed_code_sizes[num_packed_code_sizes++] = (mz_uint8)(rle_z_count - 11); \ -} rle_z_count = 0; } } - -static mz_uint8 s_tdefl_packed_code_size_syms_swizzle[] = { 16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15 }; - -static void tdefl_start_dynamic_block(tdefl_compressor *d) -{ - int num_lit_codes, num_dist_codes, num_bit_lengths; mz_uint i, total_code_sizes_to_pack, num_packed_code_sizes, rle_z_count, rle_repeat_count, packed_code_sizes_index; - mz_uint8 code_sizes_to_pack[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], packed_code_sizes[TDEFL_MAX_HUFF_SYMBOLS_0 + TDEFL_MAX_HUFF_SYMBOLS_1], prev_code_size = 0xFF; - - d->m_huff_count[0][256] = 1; - - tdefl_optimize_huffman_table(d, 0, TDEFL_MAX_HUFF_SYMBOLS_0, 15, MZ_FALSE); - tdefl_optimize_huffman_table(d, 1, TDEFL_MAX_HUFF_SYMBOLS_1, 15, MZ_FALSE); - - for (num_lit_codes = 286; num_lit_codes > 257; num_lit_codes--) if (d->m_huff_code_sizes[0][num_lit_codes - 1]) break; - for (num_dist_codes = 30; num_dist_codes > 1; num_dist_codes--) if (d->m_huff_code_sizes[1][num_dist_codes - 1]) break; - - memcpy(code_sizes_to_pack, &d->m_huff_code_sizes[0][0], num_lit_codes); - memcpy(code_sizes_to_pack + num_lit_codes, &d->m_huff_code_sizes[1][0], num_dist_codes); - total_code_sizes_to_pack = num_lit_codes + num_dist_codes; num_packed_code_sizes = 0; rle_z_count = 0; rle_repeat_count = 0; - - memset(&d->m_huff_count[2][0], 0, sizeof(d->m_huff_count[2][0]) * TDEFL_MAX_HUFF_SYMBOLS_2); - for (i = 0; i < total_code_sizes_to_pack; i++) - { - mz_uint8 code_size = code_sizes_to_pack[i]; - if (!code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - if (++rle_z_count == 138) { TDEFL_RLE_ZERO_CODE_SIZE(); } - } - else - { - TDEFL_RLE_ZERO_CODE_SIZE(); - if (code_size != prev_code_size) - { - TDEFL_RLE_PREV_CODE_SIZE(); - d->m_huff_count[2][code_size] = (mz_uint16)(d->m_huff_count[2][code_size] + 1); packed_code_sizes[num_packed_code_sizes++] = code_size; - } - else if (++rle_repeat_count == 6) - { - TDEFL_RLE_PREV_CODE_SIZE(); - } - } - prev_code_size = code_size; - } - if (rle_repeat_count) { TDEFL_RLE_PREV_CODE_SIZE(); } else { TDEFL_RLE_ZERO_CODE_SIZE(); } - - tdefl_optimize_huffman_table(d, 2, TDEFL_MAX_HUFF_SYMBOLS_2, 7, MZ_FALSE); - - TDEFL_PUT_BITS(2, 2); - - TDEFL_PUT_BITS(num_lit_codes - 257, 5); - TDEFL_PUT_BITS(num_dist_codes - 1, 5); - - for (num_bit_lengths = 18; num_bit_lengths >= 0; num_bit_lengths--) if (d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[num_bit_lengths]]) break; - num_bit_lengths = MZ_MAX(4, (num_bit_lengths + 1)); TDEFL_PUT_BITS(num_bit_lengths - 4, 4); - for (i = 0; (int)i < num_bit_lengths; i++) TDEFL_PUT_BITS(d->m_huff_code_sizes[2][s_tdefl_packed_code_size_syms_swizzle[i]], 3); - - for (packed_code_sizes_index = 0; packed_code_sizes_index < num_packed_code_sizes; ) - { - mz_uint code = packed_code_sizes[packed_code_sizes_index++]; MZ_ASSERT(code < TDEFL_MAX_HUFF_SYMBOLS_2); - TDEFL_PUT_BITS(d->m_huff_codes[2][code], d->m_huff_code_sizes[2][code]); - if (code >= 16) TDEFL_PUT_BITS(packed_code_sizes[packed_code_sizes_index++], "\02\03\07"[code - 16]); - } -} - -static void tdefl_start_static_block(tdefl_compressor *d) -{ - mz_uint i; - mz_uint8 *p = &d->m_huff_code_sizes[0][0]; - - for (i = 0; i <= 143; ++i) *p++ = 8; - for ( ; i <= 255; ++i) *p++ = 9; - for ( ; i <= 279; ++i) *p++ = 7; - for ( ; i <= 287; ++i) *p++ = 8; - - memset(d->m_huff_code_sizes[1], 5, 32); - - tdefl_optimize_huffman_table(d, 0, 288, 15, MZ_TRUE); - tdefl_optimize_huffman_table(d, 1, 32, 15, MZ_TRUE); - - TDEFL_PUT_BITS(1, 2); -} - -static const mz_uint mz_bitmasks[17] = { 0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F, 0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF, 0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF }; - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - mz_uint8 *pOutput_buf = d->m_pOutput_buf; - mz_uint8 *pLZ_code_buf_end = d->m_pLZ_code_buf; - mz_uint64 bit_buffer = d->m_bit_buffer; - mz_uint bits_in = d->m_bits_in; - -#define TDEFL_PUT_BITS_FAST(b, l) { bit_buffer |= (((mz_uint64)(b)) << bits_in); bits_in += (l); } - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < pLZ_code_buf_end; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - - if (flags & 1) - { - mz_uint s0, s1, n0, n1, sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = *(const mz_uint16 *)(pLZ_codes + 1); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS_FAST(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - // This sequence coaxes MSVC into using cmov's vs. jmp's. - s0 = s_tdefl_small_dist_sym[match_dist & 511]; - n0 = s_tdefl_small_dist_extra[match_dist & 511]; - s1 = s_tdefl_large_dist_sym[match_dist >> 8]; - n1 = s_tdefl_large_dist_extra[match_dist >> 8]; - sym = (match_dist < 512) ? s0 : s1; - num_extra_bits = (match_dist < 512) ? n0 : n1; - - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS_FAST(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - - if (((flags & 2) == 0) && (pLZ_codes < pLZ_code_buf_end)) - { - flags >>= 1; - lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS_FAST(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - } - - if (pOutput_buf >= d->m_pOutput_buf_end) - return MZ_FALSE; - - *(mz_uint64*)pOutput_buf = bit_buffer; - pOutput_buf += (bits_in >> 3); - bit_buffer >>= (bits_in & ~7); - bits_in &= 7; - } - -#undef TDEFL_PUT_BITS_FAST - - d->m_pOutput_buf = pOutput_buf; - d->m_bits_in = 0; - d->m_bit_buffer = 0; - - while (bits_in) - { - mz_uint32 n = MZ_MIN(bits_in, 16); - TDEFL_PUT_BITS((mz_uint)bit_buffer & mz_bitmasks[n], n); - bit_buffer >>= n; - bits_in -= n; - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#else -static mz_bool tdefl_compress_lz_codes(tdefl_compressor *d) -{ - mz_uint flags; - mz_uint8 *pLZ_codes; - - flags = 1; - for (pLZ_codes = d->m_lz_code_buf; pLZ_codes < d->m_pLZ_code_buf; flags >>= 1) - { - if (flags == 1) - flags = *pLZ_codes++ | 0x100; - if (flags & 1) - { - mz_uint sym, num_extra_bits; - mz_uint match_len = pLZ_codes[0], match_dist = (pLZ_codes[1] | (pLZ_codes[2] << 8)); pLZ_codes += 3; - - MZ_ASSERT(d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(d->m_huff_codes[0][s_tdefl_len_sym[match_len]], d->m_huff_code_sizes[0][s_tdefl_len_sym[match_len]]); - TDEFL_PUT_BITS(match_len & mz_bitmasks[s_tdefl_len_extra[match_len]], s_tdefl_len_extra[match_len]); - - if (match_dist < 512) - { - sym = s_tdefl_small_dist_sym[match_dist]; num_extra_bits = s_tdefl_small_dist_extra[match_dist]; - } - else - { - sym = s_tdefl_large_dist_sym[match_dist >> 8]; num_extra_bits = s_tdefl_large_dist_extra[match_dist >> 8]; - } - MZ_ASSERT(d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(d->m_huff_codes[1][sym], d->m_huff_code_sizes[1][sym]); - TDEFL_PUT_BITS(match_dist & mz_bitmasks[num_extra_bits], num_extra_bits); - } - else - { - mz_uint lit = *pLZ_codes++; - MZ_ASSERT(d->m_huff_code_sizes[0][lit]); - TDEFL_PUT_BITS(d->m_huff_codes[0][lit], d->m_huff_code_sizes[0][lit]); - } - } - - TDEFL_PUT_BITS(d->m_huff_codes[0][256], d->m_huff_code_sizes[0][256]); - - return (d->m_pOutput_buf < d->m_pOutput_buf_end); -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN && MINIZ_HAS_64BIT_REGISTERS - -static mz_bool tdefl_compress_block(tdefl_compressor *d, mz_bool static_block) -{ - if (static_block) - tdefl_start_static_block(d); - else - tdefl_start_dynamic_block(d); - return tdefl_compress_lz_codes(d); -} - -static int tdefl_flush_block(tdefl_compressor *d, int flush) -{ - mz_uint saved_bit_buf, saved_bits_in; - mz_uint8 *pSaved_output_buf; - mz_bool comp_block_succeeded = MZ_FALSE; - int n, use_raw_block = ((d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS) != 0) && (d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size; - mz_uint8 *pOutput_buf_start = ((d->m_pPut_buf_func == NULL) && ((*d->m_pOut_buf_size - d->m_out_buf_ofs) >= TDEFL_OUT_BUF_SIZE)) ? ((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs) : d->m_output_buf; - - d->m_pOutput_buf = pOutput_buf_start; - d->m_pOutput_buf_end = d->m_pOutput_buf + TDEFL_OUT_BUF_SIZE - 16; - - MZ_ASSERT(!d->m_output_flush_remaining); - d->m_output_flush_ofs = 0; - d->m_output_flush_remaining = 0; - - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> d->m_num_flags_left); - d->m_pLZ_code_buf -= (d->m_num_flags_left == 8); - - if ((d->m_flags & TDEFL_WRITE_ZLIB_HEADER) && (!d->m_block_index)) - { - TDEFL_PUT_BITS(0x78, 8); TDEFL_PUT_BITS(0x01, 8); - } - - TDEFL_PUT_BITS(flush == TDEFL_FINISH, 1); - - pSaved_output_buf = d->m_pOutput_buf; saved_bit_buf = d->m_bit_buffer; saved_bits_in = d->m_bits_in; - - if (!use_raw_block) - comp_block_succeeded = tdefl_compress_block(d, (d->m_flags & TDEFL_FORCE_ALL_STATIC_BLOCKS) || (d->m_total_lz_bytes < 48)); - - // If the block gets expanded, forget the current contents of the output buffer and send a raw block instead. - if ( ((use_raw_block) || ((d->m_total_lz_bytes) && ((d->m_pOutput_buf - pSaved_output_buf + 1U) >= d->m_total_lz_bytes))) && - ((d->m_lookahead_pos - d->m_lz_code_buf_dict_pos) <= d->m_dict_size) ) - { - mz_uint i; d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - TDEFL_PUT_BITS(0, 2); - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - for (i = 2; i; --i, d->m_total_lz_bytes ^= 0xFFFF) - { - TDEFL_PUT_BITS(d->m_total_lz_bytes & 0xFFFF, 16); - } - for (i = 0; i < d->m_total_lz_bytes; ++i) - { - TDEFL_PUT_BITS(d->m_dict[(d->m_lz_code_buf_dict_pos + i) & TDEFL_LZ_DICT_SIZE_MASK], 8); - } - } - // Check for the extremely unlikely (if not impossible) case of the compressed block not fitting into the output buffer when using dynamic codes. - else if (!comp_block_succeeded) - { - d->m_pOutput_buf = pSaved_output_buf; d->m_bit_buffer = saved_bit_buf, d->m_bits_in = saved_bits_in; - tdefl_compress_block(d, MZ_TRUE); - } - - if (flush) - { - if (flush == TDEFL_FINISH) - { - if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } - if (d->m_flags & TDEFL_WRITE_ZLIB_HEADER) { mz_uint i, a = d->m_adler32; for (i = 0; i < 4; i++) { TDEFL_PUT_BITS((a >> 24) & 0xFF, 8); a <<= 8; } } - } - else - { - mz_uint i, z = 0; TDEFL_PUT_BITS(0, 3); if (d->m_bits_in) { TDEFL_PUT_BITS(0, 8 - d->m_bits_in); } for (i = 2; i; --i, z ^= 0xFFFF) { TDEFL_PUT_BITS(z & 0xFFFF, 16); } - } - } - - MZ_ASSERT(d->m_pOutput_buf < d->m_pOutput_buf_end); - - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; d->m_lz_code_buf_dict_pos += d->m_total_lz_bytes; d->m_total_lz_bytes = 0; d->m_block_index++; - - if ((n = (int)(d->m_pOutput_buf - pOutput_buf_start)) != 0) - { - if (d->m_pPut_buf_func) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - if (!(*d->m_pPut_buf_func)(d->m_output_buf, n, d->m_pPut_buf_user)) - return (d->m_prev_return_status = TDEFL_STATUS_PUT_BUF_FAILED); - } - else if (pOutput_buf_start == d->m_output_buf) - { - int bytes_to_copy = (int)MZ_MIN((size_t)n, (size_t)(*d->m_pOut_buf_size - d->m_out_buf_ofs)); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf, bytes_to_copy); - d->m_out_buf_ofs += bytes_to_copy; - if ((n -= bytes_to_copy) != 0) - { - d->m_output_flush_ofs = bytes_to_copy; - d->m_output_flush_remaining = n; - } - } - else - { - d->m_out_buf_ofs += n; - } - } - - return d->m_output_flush_remaining; -} - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES -#define TDEFL_READ_UNALIGNED_WORD(p) *(const mz_uint16*)(p) -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint16 *s = (const mz_uint16*)(d->m_dict + pos), *p, *q; - mz_uint16 c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]), s01 = TDEFL_READ_UNALIGNED_WORD(s); - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) - { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if (TDEFL_READ_UNALIGNED_WORD(&d->m_dict[probe_pos + match_len - 1]) == c01) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; q = (const mz_uint16*)(d->m_dict + probe_pos); if (TDEFL_READ_UNALIGNED_WORD(q) != s01) continue; p = s; probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - if (!probe_len) - { - *pMatch_dist = dist; *pMatch_len = MZ_MIN(max_match_len, TDEFL_MAX_MATCH_LEN); break; - } - else if ((probe_len = ((mz_uint)(p - s) * 2) + (mz_uint)(*(const mz_uint8*)p == *(const mz_uint8*)q)) > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = MZ_MIN(max_match_len, probe_len)) == max_match_len) break; - c01 = TDEFL_READ_UNALIGNED_WORD(&d->m_dict[pos + match_len - 1]); - } - } -} -#else -static MZ_FORCEINLINE void tdefl_find_match(tdefl_compressor *d, mz_uint lookahead_pos, mz_uint max_dist, mz_uint max_match_len, mz_uint *pMatch_dist, mz_uint *pMatch_len) -{ - mz_uint dist, pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK, match_len = *pMatch_len, probe_pos = pos, next_probe_pos, probe_len; - mz_uint num_probes_left = d->m_max_probes[match_len >= 32]; - const mz_uint8 *s = d->m_dict + pos, *p, *q; - mz_uint8 c0 = d->m_dict[pos + match_len], c1 = d->m_dict[pos + match_len - 1]; - MZ_ASSERT(max_match_len <= TDEFL_MAX_MATCH_LEN); if (max_match_len <= match_len) return; - for ( ; ; ) - { - for ( ; ; ) - { - if (--num_probes_left == 0) return; - #define TDEFL_PROBE \ - next_probe_pos = d->m_next[probe_pos]; \ - if ((!next_probe_pos) || ((dist = (mz_uint16)(lookahead_pos - next_probe_pos)) > max_dist)) return; \ - probe_pos = next_probe_pos & TDEFL_LZ_DICT_SIZE_MASK; \ - if ((d->m_dict[probe_pos + match_len] == c0) && (d->m_dict[probe_pos + match_len - 1] == c1)) break; - TDEFL_PROBE; TDEFL_PROBE; TDEFL_PROBE; - } - if (!dist) break; p = s; q = d->m_dict + probe_pos; for (probe_len = 0; probe_len < max_match_len; probe_len++) if (*p++ != *q++) break; - if (probe_len > match_len) - { - *pMatch_dist = dist; if ((*pMatch_len = match_len = probe_len) == max_match_len) return; - c0 = d->m_dict[pos + match_len]; c1 = d->m_dict[pos + match_len - 1]; - } - } -} -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN -static mz_bool tdefl_compress_fast(tdefl_compressor *d) -{ - // Faster, minimally featured LZRW1-style match+parse loop with better register utilization. Intended for applications where raw throughput is valued more highly than ratio. - mz_uint lookahead_pos = d->m_lookahead_pos, lookahead_size = d->m_lookahead_size, dict_size = d->m_dict_size, total_lz_bytes = d->m_total_lz_bytes, num_flags_left = d->m_num_flags_left; - mz_uint8 *pLZ_code_buf = d->m_pLZ_code_buf, *pLZ_flags = d->m_pLZ_flags; - mz_uint cur_pos = lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - - while ((d->m_src_buf_left) || ((d->m_flush) && (lookahead_size))) - { - const mz_uint TDEFL_COMP_FAST_LOOKAHEAD_SIZE = 4096; - mz_uint dst_pos = (lookahead_pos + lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(d->m_src_buf_left, TDEFL_COMP_FAST_LOOKAHEAD_SIZE - lookahead_size); - d->m_src_buf_left -= num_bytes_to_process; - lookahead_size += num_bytes_to_process; - - while (num_bytes_to_process) - { - mz_uint32 n = MZ_MIN(TDEFL_LZ_DICT_SIZE - dst_pos, num_bytes_to_process); - memcpy(d->m_dict + dst_pos, d->m_pSrc, n); - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - memcpy(d->m_dict + TDEFL_LZ_DICT_SIZE + dst_pos, d->m_pSrc, MZ_MIN(n, (TDEFL_MAX_MATCH_LEN - 1) - dst_pos)); - d->m_pSrc += n; - dst_pos = (dst_pos + n) & TDEFL_LZ_DICT_SIZE_MASK; - num_bytes_to_process -= n; - } - - dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - lookahead_size, dict_size); - if ((!d->m_flush) && (lookahead_size < TDEFL_COMP_FAST_LOOKAHEAD_SIZE)) break; - - while (lookahead_size >= 4) - { - mz_uint cur_match_dist, cur_match_len = 1; - mz_uint8 *pCur_dict = d->m_dict + cur_pos; - mz_uint first_trigram = (*(const mz_uint32 *)pCur_dict) & 0xFFFFFF; - mz_uint hash = (first_trigram ^ (first_trigram >> (24 - (TDEFL_LZ_HASH_BITS - 8)))) & TDEFL_LEVEL1_HASH_SIZE_MASK; - mz_uint probe_pos = d->m_hash[hash]; - d->m_hash[hash] = (mz_uint16)lookahead_pos; - - if (((cur_match_dist = (mz_uint16)(lookahead_pos - probe_pos)) <= dict_size) && ((*(const mz_uint32 *)(d->m_dict + (probe_pos &= TDEFL_LZ_DICT_SIZE_MASK)) & 0xFFFFFF) == first_trigram)) - { - const mz_uint16 *p = (const mz_uint16 *)pCur_dict; - const mz_uint16 *q = (const mz_uint16 *)(d->m_dict + probe_pos); - mz_uint32 probe_len = 32; - do { } while ( (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && - (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (TDEFL_READ_UNALIGNED_WORD(++p) == TDEFL_READ_UNALIGNED_WORD(++q)) && (--probe_len > 0) ); - cur_match_len = ((mz_uint)(p - (const mz_uint16 *)pCur_dict) * 2) + (mz_uint)(*(const mz_uint8 *)p == *(const mz_uint8 *)q); - if (!probe_len) - cur_match_len = cur_match_dist ? TDEFL_MAX_MATCH_LEN : 0; - - if ((cur_match_len < TDEFL_MIN_MATCH_LEN) || ((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U))) - { - cur_match_len = 1; - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - else - { - mz_uint32 s0, s1; - cur_match_len = MZ_MIN(cur_match_len, lookahead_size); - - MZ_ASSERT((cur_match_len >= TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 1) && (cur_match_dist <= TDEFL_LZ_DICT_SIZE)); - - cur_match_dist--; - - pLZ_code_buf[0] = (mz_uint8)(cur_match_len - TDEFL_MIN_MATCH_LEN); - *(mz_uint16 *)(&pLZ_code_buf[1]) = (mz_uint16)cur_match_dist; - pLZ_code_buf += 3; - *pLZ_flags = (mz_uint8)((*pLZ_flags >> 1) | 0x80); - - s0 = s_tdefl_small_dist_sym[cur_match_dist & 511]; - s1 = s_tdefl_large_dist_sym[cur_match_dist >> 8]; - d->m_huff_count[1][(cur_match_dist < 512) ? s0 : s1]++; - - d->m_huff_count[0][s_tdefl_len_sym[cur_match_len - TDEFL_MIN_MATCH_LEN]]++; - } - } - else - { - *pLZ_code_buf++ = (mz_uint8)first_trigram; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - d->m_huff_count[0][(mz_uint8)first_trigram]++; - } - - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - total_lz_bytes += cur_match_len; - lookahead_pos += cur_match_len; - dict_size = MZ_MIN(dict_size + cur_match_len, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + cur_match_len) & TDEFL_LZ_DICT_SIZE_MASK; - MZ_ASSERT(lookahead_size >= cur_match_len); - lookahead_size -= cur_match_len; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - - while (lookahead_size) - { - mz_uint8 lit = d->m_dict[cur_pos]; - - total_lz_bytes++; - *pLZ_code_buf++ = lit; - *pLZ_flags = (mz_uint8)(*pLZ_flags >> 1); - if (--num_flags_left == 0) { num_flags_left = 8; pLZ_flags = pLZ_code_buf++; } - - d->m_huff_count[0][lit]++; - - lookahead_pos++; - dict_size = MZ_MIN(dict_size + 1, TDEFL_LZ_DICT_SIZE); - cur_pos = (cur_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; - lookahead_size--; - - if (pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) - { - int n; - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - total_lz_bytes = d->m_total_lz_bytes; pLZ_code_buf = d->m_pLZ_code_buf; pLZ_flags = d->m_pLZ_flags; num_flags_left = d->m_num_flags_left; - } - } - } - - d->m_lookahead_pos = lookahead_pos; d->m_lookahead_size = lookahead_size; d->m_dict_size = dict_size; - d->m_total_lz_bytes = total_lz_bytes; d->m_pLZ_code_buf = pLZ_code_buf; d->m_pLZ_flags = pLZ_flags; d->m_num_flags_left = num_flags_left; - return MZ_TRUE; -} -#endif // MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - -static MZ_FORCEINLINE void tdefl_record_literal(tdefl_compressor *d, mz_uint8 lit) -{ - d->m_total_lz_bytes++; - *d->m_pLZ_code_buf++ = lit; - *d->m_pLZ_flags = (mz_uint8)(*d->m_pLZ_flags >> 1); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - d->m_huff_count[0][lit]++; -} - -static MZ_FORCEINLINE void tdefl_record_match(tdefl_compressor *d, mz_uint match_len, mz_uint match_dist) -{ - mz_uint32 s0, s1; - - MZ_ASSERT((match_len >= TDEFL_MIN_MATCH_LEN) && (match_dist >= 1) && (match_dist <= TDEFL_LZ_DICT_SIZE)); - - d->m_total_lz_bytes += match_len; - - d->m_pLZ_code_buf[0] = (mz_uint8)(match_len - TDEFL_MIN_MATCH_LEN); - - match_dist -= 1; - d->m_pLZ_code_buf[1] = (mz_uint8)(match_dist & 0xFF); - d->m_pLZ_code_buf[2] = (mz_uint8)(match_dist >> 8); d->m_pLZ_code_buf += 3; - - *d->m_pLZ_flags = (mz_uint8)((*d->m_pLZ_flags >> 1) | 0x80); if (--d->m_num_flags_left == 0) { d->m_num_flags_left = 8; d->m_pLZ_flags = d->m_pLZ_code_buf++; } - - s0 = s_tdefl_small_dist_sym[match_dist & 511]; s1 = s_tdefl_large_dist_sym[(match_dist >> 8) & 127]; - d->m_huff_count[1][(match_dist < 512) ? s0 : s1]++; - - if (match_len >= TDEFL_MIN_MATCH_LEN) d->m_huff_count[0][s_tdefl_len_sym[match_len - TDEFL_MIN_MATCH_LEN]]++; -} - -static mz_bool tdefl_compress_normal(tdefl_compressor *d) -{ - const mz_uint8 *pSrc = d->m_pSrc; size_t src_buf_left = d->m_src_buf_left; - tdefl_flush flush = d->m_flush; - - while ((src_buf_left) || ((flush) && (d->m_lookahead_size))) - { - mz_uint len_to_move, cur_match_dist, cur_match_len, cur_pos; - // Update dictionary and hash chains. Keeps the lookahead size equal to TDEFL_MAX_MATCH_LEN. - if ((d->m_lookahead_size + d->m_dict_size) >= (TDEFL_MIN_MATCH_LEN - 1)) - { - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK, ins_pos = d->m_lookahead_pos + d->m_lookahead_size - 2; - mz_uint hash = (d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK]; - mz_uint num_bytes_to_process = (mz_uint)MZ_MIN(src_buf_left, TDEFL_MAX_MATCH_LEN - d->m_lookahead_size); - const mz_uint8 *pSrc_end = pSrc + num_bytes_to_process; - src_buf_left -= num_bytes_to_process; - d->m_lookahead_size += num_bytes_to_process; - while (pSrc != pSrc_end) - { - mz_uint8 c = *pSrc++; d->m_dict[dst_pos] = c; if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - hash = ((hash << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - dst_pos = (dst_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK; ins_pos++; - } - } - else - { - while ((src_buf_left) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - { - mz_uint8 c = *pSrc++; - mz_uint dst_pos = (d->m_lookahead_pos + d->m_lookahead_size) & TDEFL_LZ_DICT_SIZE_MASK; - src_buf_left--; - d->m_dict[dst_pos] = c; - if (dst_pos < (TDEFL_MAX_MATCH_LEN - 1)) - d->m_dict[TDEFL_LZ_DICT_SIZE + dst_pos] = c; - if ((++d->m_lookahead_size + d->m_dict_size) >= TDEFL_MIN_MATCH_LEN) - { - mz_uint ins_pos = d->m_lookahead_pos + (d->m_lookahead_size - 1) - 2; - mz_uint hash = ((d->m_dict[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] << (TDEFL_LZ_HASH_SHIFT * 2)) ^ (d->m_dict[(ins_pos + 1) & TDEFL_LZ_DICT_SIZE_MASK] << TDEFL_LZ_HASH_SHIFT) ^ c) & (TDEFL_LZ_HASH_SIZE - 1); - d->m_next[ins_pos & TDEFL_LZ_DICT_SIZE_MASK] = d->m_hash[hash]; d->m_hash[hash] = (mz_uint16)(ins_pos); - } - } - } - d->m_dict_size = MZ_MIN(TDEFL_LZ_DICT_SIZE - d->m_lookahead_size, d->m_dict_size); - if ((!flush) && (d->m_lookahead_size < TDEFL_MAX_MATCH_LEN)) - break; - - // Simple lazy/greedy parsing state machine. - len_to_move = 1; cur_match_dist = 0; cur_match_len = d->m_saved_match_len ? d->m_saved_match_len : (TDEFL_MIN_MATCH_LEN - 1); cur_pos = d->m_lookahead_pos & TDEFL_LZ_DICT_SIZE_MASK; - if (d->m_flags & (TDEFL_RLE_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS)) - { - if ((d->m_dict_size) && (!(d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) - { - mz_uint8 c = d->m_dict[(cur_pos - 1) & TDEFL_LZ_DICT_SIZE_MASK]; - cur_match_len = 0; while (cur_match_len < d->m_lookahead_size) { if (d->m_dict[cur_pos + cur_match_len] != c) break; cur_match_len++; } - if (cur_match_len < TDEFL_MIN_MATCH_LEN) cur_match_len = 0; else cur_match_dist = 1; - } - } - else - { - tdefl_find_match(d, d->m_lookahead_pos, d->m_dict_size, d->m_lookahead_size, &cur_match_dist, &cur_match_len); - } - if (((cur_match_len == TDEFL_MIN_MATCH_LEN) && (cur_match_dist >= 8U*1024U)) || (cur_pos == cur_match_dist) || ((d->m_flags & TDEFL_FILTER_MATCHES) && (cur_match_len <= 5))) - { - cur_match_dist = cur_match_len = 0; - } - if (d->m_saved_match_len) - { - if (cur_match_len > d->m_saved_match_len) - { - tdefl_record_literal(d, (mz_uint8)d->m_saved_lit); - if (cur_match_len >= 128) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - d->m_saved_match_len = 0; len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[cur_pos]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - } - else - { - tdefl_record_match(d, d->m_saved_match_len, d->m_saved_match_dist); - len_to_move = d->m_saved_match_len - 1; d->m_saved_match_len = 0; - } - } - else if (!cur_match_dist) - tdefl_record_literal(d, d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]); - else if ((d->m_greedy_parsing) || (d->m_flags & TDEFL_RLE_MATCHES) || (cur_match_len >= 128)) - { - tdefl_record_match(d, cur_match_len, cur_match_dist); - len_to_move = cur_match_len; - } - else - { - d->m_saved_lit = d->m_dict[MZ_MIN(cur_pos, sizeof(d->m_dict) - 1)]; d->m_saved_match_dist = cur_match_dist; d->m_saved_match_len = cur_match_len; - } - // Move the lookahead forward by len_to_move bytes. - d->m_lookahead_pos += len_to_move; - MZ_ASSERT(d->m_lookahead_size >= len_to_move); - d->m_lookahead_size -= len_to_move; - d->m_dict_size = MZ_MIN(d->m_dict_size + len_to_move, TDEFL_LZ_DICT_SIZE); - // Check if it's time to flush the current LZ codes to the internal output buffer. - if ( (d->m_pLZ_code_buf > &d->m_lz_code_buf[TDEFL_LZ_CODE_BUF_SIZE - 8]) || - ( (d->m_total_lz_bytes > 31*1024) && (((((mz_uint)(d->m_pLZ_code_buf - d->m_lz_code_buf) * 115) >> 7) >= d->m_total_lz_bytes) || (d->m_flags & TDEFL_FORCE_ALL_RAW_BLOCKS))) ) - { - int n; - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - if ((n = tdefl_flush_block(d, 0)) != 0) - return (n < 0) ? MZ_FALSE : MZ_TRUE; - } - } - - d->m_pSrc = pSrc; d->m_src_buf_left = src_buf_left; - return MZ_TRUE; -} - -static tdefl_status tdefl_flush_output_buffer(tdefl_compressor *d) -{ - if (d->m_pIn_buf_size) - { - *d->m_pIn_buf_size = d->m_pSrc - (const mz_uint8 *)d->m_pIn_buf; - } - - if (d->m_pOut_buf_size) - { - size_t n = MZ_MIN(*d->m_pOut_buf_size - d->m_out_buf_ofs, d->m_output_flush_remaining); - memcpy((mz_uint8 *)d->m_pOut_buf + d->m_out_buf_ofs, d->m_output_buf + d->m_output_flush_ofs, n); - d->m_output_flush_ofs += (mz_uint)n; - d->m_output_flush_remaining -= (mz_uint)n; - d->m_out_buf_ofs += n; - - *d->m_pOut_buf_size = d->m_out_buf_ofs; - } - - return (d->m_finished && !d->m_output_flush_remaining) ? TDEFL_STATUS_DONE : TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_compress(tdefl_compressor *d, const void *pIn_buf, size_t *pIn_buf_size, void *pOut_buf, size_t *pOut_buf_size, tdefl_flush flush) -{ - if (!d) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return TDEFL_STATUS_BAD_PARAM; - } - - d->m_pIn_buf = pIn_buf; d->m_pIn_buf_size = pIn_buf_size; - d->m_pOut_buf = pOut_buf; d->m_pOut_buf_size = pOut_buf_size; - d->m_pSrc = (const mz_uint8 *)(pIn_buf); d->m_src_buf_left = pIn_buf_size ? *pIn_buf_size : 0; - d->m_out_buf_ofs = 0; - d->m_flush = flush; - - if ( ((d->m_pPut_buf_func != NULL) == ((pOut_buf != NULL) || (pOut_buf_size != NULL))) || (d->m_prev_return_status != TDEFL_STATUS_OKAY) || - (d->m_wants_to_finish && (flush != TDEFL_FINISH)) || (pIn_buf_size && *pIn_buf_size && !pIn_buf) || (pOut_buf_size && *pOut_buf_size && !pOut_buf) ) - { - if (pIn_buf_size) *pIn_buf_size = 0; - if (pOut_buf_size) *pOut_buf_size = 0; - return (d->m_prev_return_status = TDEFL_STATUS_BAD_PARAM); - } - d->m_wants_to_finish |= (flush == TDEFL_FINISH); - - if ((d->m_output_flush_remaining) || (d->m_finished)) - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); - -#if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - if (((d->m_flags & TDEFL_MAX_PROBES_MASK) == 1) && - ((d->m_flags & TDEFL_GREEDY_PARSING_FLAG) != 0) && - ((d->m_flags & (TDEFL_FILTER_MATCHES | TDEFL_FORCE_ALL_RAW_BLOCKS | TDEFL_RLE_MATCHES)) == 0)) - { - if (!tdefl_compress_fast(d)) - return d->m_prev_return_status; - } - else -#endif // #if MINIZ_USE_UNALIGNED_LOADS_AND_STORES && MINIZ_LITTLE_ENDIAN - { - if (!tdefl_compress_normal(d)) - return d->m_prev_return_status; - } - - if ((d->m_flags & (TDEFL_WRITE_ZLIB_HEADER | TDEFL_COMPUTE_ADLER32)) && (pIn_buf)) - d->m_adler32 = (mz_uint32)mz_adler32(d->m_adler32, (const mz_uint8 *)pIn_buf, d->m_pSrc - (const mz_uint8 *)pIn_buf); - - if ((flush) && (!d->m_lookahead_size) && (!d->m_src_buf_left) && (!d->m_output_flush_remaining)) - { - if (tdefl_flush_block(d, flush) < 0) - return d->m_prev_return_status; - d->m_finished = (flush == TDEFL_FINISH); - if (flush == TDEFL_FULL_FLUSH) { MZ_CLEAR_OBJ(d->m_hash); MZ_CLEAR_OBJ(d->m_next); d->m_dict_size = 0; } - } - - return (d->m_prev_return_status = tdefl_flush_output_buffer(d)); -} - -tdefl_status tdefl_compress_buffer(tdefl_compressor *d, const void *pIn_buf, size_t in_buf_size, tdefl_flush flush) -{ - MZ_ASSERT(d->m_pPut_buf_func); return tdefl_compress(d, pIn_buf, &in_buf_size, NULL, NULL, flush); -} - -tdefl_status tdefl_init(tdefl_compressor *d, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - d->m_pPut_buf_func = pPut_buf_func; d->m_pPut_buf_user = pPut_buf_user; - d->m_flags = (mz_uint)(flags); d->m_max_probes[0] = 1 + ((flags & 0xFFF) + 2) / 3; d->m_greedy_parsing = (flags & TDEFL_GREEDY_PARSING_FLAG) != 0; - d->m_max_probes[1] = 1 + (((flags & 0xFFF) >> 2) + 2) / 3; - if (!(flags & TDEFL_NONDETERMINISTIC_PARSING_FLAG)) MZ_CLEAR_OBJ(d->m_hash); - d->m_lookahead_pos = d->m_lookahead_size = d->m_dict_size = d->m_total_lz_bytes = d->m_lz_code_buf_dict_pos = d->m_bits_in = 0; - d->m_output_flush_ofs = d->m_output_flush_remaining = d->m_finished = d->m_block_index = d->m_bit_buffer = d->m_wants_to_finish = 0; - d->m_pLZ_code_buf = d->m_lz_code_buf + 1; d->m_pLZ_flags = d->m_lz_code_buf; d->m_num_flags_left = 8; - d->m_pOutput_buf = d->m_output_buf; d->m_pOutput_buf_end = d->m_output_buf; d->m_prev_return_status = TDEFL_STATUS_OKAY; - d->m_saved_match_dist = d->m_saved_match_len = d->m_saved_lit = 0; d->m_adler32 = 1; - d->m_pIn_buf = NULL; d->m_pOut_buf = NULL; - d->m_pIn_buf_size = NULL; d->m_pOut_buf_size = NULL; - d->m_flush = TDEFL_NO_FLUSH; d->m_pSrc = NULL; d->m_src_buf_left = 0; d->m_out_buf_ofs = 0; - memset(&d->m_huff_count[0][0], 0, sizeof(d->m_huff_count[0][0]) * TDEFL_MAX_HUFF_SYMBOLS_0); - memset(&d->m_huff_count[1][0], 0, sizeof(d->m_huff_count[1][0]) * TDEFL_MAX_HUFF_SYMBOLS_1); - return TDEFL_STATUS_OKAY; -} - -tdefl_status tdefl_get_prev_return_status(tdefl_compressor *d) -{ - return d->m_prev_return_status; -} - -mz_uint32 tdefl_get_adler32(tdefl_compressor *d) -{ - return d->m_adler32; -} - -mz_bool tdefl_compress_mem_to_output(const void *pBuf, size_t buf_len, tdefl_put_buf_func_ptr pPut_buf_func, void *pPut_buf_user, int flags) -{ - tdefl_compressor *pComp; mz_bool succeeded; if (((buf_len) && (!pBuf)) || (!pPut_buf_func)) return MZ_FALSE; - pComp = (tdefl_compressor*)MZ_MALLOC(sizeof(tdefl_compressor)); if (!pComp) return MZ_FALSE; - succeeded = (tdefl_init(pComp, pPut_buf_func, pPut_buf_user, flags) == TDEFL_STATUS_OKAY); - succeeded = succeeded && (tdefl_compress_buffer(pComp, pBuf, buf_len, TDEFL_FINISH) == TDEFL_STATUS_DONE); - MZ_FREE(pComp); return succeeded; -} - -typedef struct -{ - size_t m_size, m_capacity; - mz_uint8 *m_pBuf; - mz_bool m_expandable; -} tdefl_output_buffer; - -static mz_bool tdefl_output_buffer_putter(const void *pBuf, int len, void *pUser) -{ - tdefl_output_buffer *p = (tdefl_output_buffer *)pUser; - size_t new_size = p->m_size + len; - if (new_size > p->m_capacity) - { - size_t new_capacity = p->m_capacity; mz_uint8 *pNew_buf; if (!p->m_expandable) return MZ_FALSE; - do { new_capacity = MZ_MAX(128U, new_capacity << 1U); } while (new_size > new_capacity); - pNew_buf = (mz_uint8*)MZ_REALLOC(p->m_pBuf, new_capacity); if (!pNew_buf) return MZ_FALSE; - p->m_pBuf = pNew_buf; p->m_capacity = new_capacity; - } - memcpy((mz_uint8*)p->m_pBuf + p->m_size, pBuf, len); p->m_size = new_size; - return MZ_TRUE; -} - -void *tdefl_compress_mem_to_heap(const void *pSrc_buf, size_t src_buf_len, size_t *pOut_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_len) return MZ_FALSE; else *pOut_len = 0; - out_buf.m_expandable = MZ_TRUE; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return NULL; - *pOut_len = out_buf.m_size; return out_buf.m_pBuf; -} - -size_t tdefl_compress_mem_to_mem(void *pOut_buf, size_t out_buf_len, const void *pSrc_buf, size_t src_buf_len, int flags) -{ - tdefl_output_buffer out_buf; MZ_CLEAR_OBJ(out_buf); - if (!pOut_buf) return 0; - out_buf.m_pBuf = (mz_uint8*)pOut_buf; out_buf.m_capacity = out_buf_len; - if (!tdefl_compress_mem_to_output(pSrc_buf, src_buf_len, tdefl_output_buffer_putter, &out_buf, flags)) return 0; - return out_buf.m_size; -} - -#ifndef MINIZ_NO_ZLIB_APIS -static const mz_uint s_tdefl_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - -// level may actually range from [0,10] (10 is a "hidden" max level, where we want a bit more compression and it's fine if throughput to fall off a cliff on some files). -mz_uint tdefl_create_comp_flags_from_zip_params(int level, int window_bits, int strategy) -{ - mz_uint comp_flags = s_tdefl_num_probes[(level >= 0) ? MZ_MIN(10, level) : MZ_DEFAULT_LEVEL] | ((level <= 3) ? TDEFL_GREEDY_PARSING_FLAG : 0); - if (window_bits > 0) comp_flags |= TDEFL_WRITE_ZLIB_HEADER; - - if (!level) comp_flags |= TDEFL_FORCE_ALL_RAW_BLOCKS; - else if (strategy == MZ_FILTERED) comp_flags |= TDEFL_FILTER_MATCHES; - else if (strategy == MZ_HUFFMAN_ONLY) comp_flags &= ~TDEFL_MAX_PROBES_MASK; - else if (strategy == MZ_FIXED) comp_flags |= TDEFL_FORCE_ALL_STATIC_BLOCKS; - else if (strategy == MZ_RLE) comp_flags |= TDEFL_RLE_MATCHES; - - return comp_flags; -} -#endif //MINIZ_NO_ZLIB_APIS - -#ifdef _MSC_VER -#pragma warning (push) -#pragma warning (disable:4204) // nonstandard extension used : non-constant aggregate initializer (also supported by GNU C and C99, so no big deal) -#endif - -// Simple PNG writer function by Alex Evans, 2011. Released into the public domain: https://gist.github.com/908299, more context at -// http://altdevblogaday.org/2011/04/06/a-smaller-jpg-encoder/. -// This is actually a modification of Alex's original code so PNG files generated by this function pass pngcheck. -void *tdefl_write_image_to_png_file_in_memory_ex(const void *pImage, int w, int h, int num_chans, size_t *pLen_out, mz_uint level, mz_bool flip) -{ - // Using a local copy of this array here in case MINIZ_NO_ZLIB_APIS was defined. - static const mz_uint s_tdefl_png_num_probes[11] = { 0, 1, 6, 32, 16, 32, 128, 256, 512, 768, 1500 }; - tdefl_compressor *pComp = (tdefl_compressor *)MZ_MALLOC(sizeof(tdefl_compressor)); tdefl_output_buffer out_buf; int i, bpl = w * num_chans, y, z; mz_uint32 c; *pLen_out = 0; - if (!pComp) return NULL; - MZ_CLEAR_OBJ(out_buf); out_buf.m_expandable = MZ_TRUE; out_buf.m_capacity = 57+MZ_MAX(64, (1+bpl)*h); if (NULL == (out_buf.m_pBuf = (mz_uint8*)MZ_MALLOC(out_buf.m_capacity))) { MZ_FREE(pComp); return NULL; } - // write dummy header - for (z = 41; z; --z) tdefl_output_buffer_putter(&z, 1, &out_buf); - // compress image data - tdefl_init(pComp, tdefl_output_buffer_putter, &out_buf, s_tdefl_png_num_probes[MZ_MIN(10, level)] | TDEFL_WRITE_ZLIB_HEADER); - for (y = 0; y < h; ++y) { tdefl_compress_buffer(pComp, &z, 1, TDEFL_NO_FLUSH); tdefl_compress_buffer(pComp, (mz_uint8*)pImage + (flip ? (h - 1 - y) : y) * bpl, bpl, TDEFL_NO_FLUSH); } - if (tdefl_compress_buffer(pComp, NULL, 0, TDEFL_FINISH) != TDEFL_STATUS_DONE) { MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - // write real header - *pLen_out = out_buf.m_size-41; - { - static const mz_uint8 chans[] = {0x00, 0x00, 0x04, 0x02, 0x06}; - mz_uint8 pnghdr[41]={0x89,0x50,0x4e,0x47,0x0d,0x0a,0x1a,0x0a,0x00,0x00,0x00,0x0d,0x49,0x48,0x44,0x52, - 0,0,(mz_uint8)(w>>8),(mz_uint8)w,0,0,(mz_uint8)(h>>8),(mz_uint8)h,8,chans[num_chans],0,0,0,0,0,0,0, - (mz_uint8)(*pLen_out>>24),(mz_uint8)(*pLen_out>>16),(mz_uint8)(*pLen_out>>8),(mz_uint8)*pLen_out,0x49,0x44,0x41,0x54}; - c=(mz_uint32)mz_crc32(MZ_CRC32_INIT,pnghdr+12,17); for (i=0; i<4; ++i, c<<=8) ((mz_uint8*)(pnghdr+29))[i]=(mz_uint8)(c>>24); - memcpy(out_buf.m_pBuf, pnghdr, 41); - } - // write footer (IDAT CRC-32, followed by IEND chunk) - if (!tdefl_output_buffer_putter("\0\0\0\0\0\0\0\0\x49\x45\x4e\x44\xae\x42\x60\x82", 16, &out_buf)) { *pLen_out = 0; MZ_FREE(pComp); MZ_FREE(out_buf.m_pBuf); return NULL; } - c = (mz_uint32)mz_crc32(MZ_CRC32_INIT,out_buf.m_pBuf+41-4, *pLen_out+4); for (i=0; i<4; ++i, c<<=8) (out_buf.m_pBuf+out_buf.m_size-16)[i] = (mz_uint8)(c >> 24); - // compute final size of file, grab compressed data buffer and return - *pLen_out += 57; MZ_FREE(pComp); return out_buf.m_pBuf; -} -void *tdefl_write_image_to_png_file_in_memory(const void *pImage, int w, int h, int num_chans, size_t *pLen_out) -{ - // Level 6 corresponds to TDEFL_DEFAULT_MAX_PROBES or MZ_DEFAULT_LEVEL (but we can't depend on MZ_DEFAULT_LEVEL being available in case the zlib API's where #defined out) - return tdefl_write_image_to_png_file_in_memory_ex(pImage, w, h, num_chans, pLen_out, 6, MZ_FALSE); -} - -#ifdef _MSC_VER -#pragma warning (pop) -#endif - -// ------------------- .ZIP archive reading - -#ifndef MINIZ_NO_ARCHIVE_APIS - -#ifdef MINIZ_NO_STDIO - #define MZ_FILE void * -#else - #include - #include - - #if defined(_MSC_VER) || defined(__MINGW64__) - static FILE *mz_fopen(const char *pFilename, const char *pMode) - { - FILE* pFile = NULL; - fopen_s(&pFile, pFilename, pMode); - return pFile; - } - static FILE *mz_freopen(const char *pPath, const char *pMode, FILE *pStream) - { - FILE* pFile = NULL; - if (freopen_s(&pFile, pPath, pMode, pStream)) - return NULL; - return pFile; - } - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN mz_fopen - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 _ftelli64 - #define MZ_FSEEK64 _fseeki64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN mz_freopen - #define MZ_DELETE_FILE remove - #elif defined(__MINGW32__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT _stat - #define MZ_FILE_STAT _stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__TINYC__) - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftell - #define MZ_FSEEK64 fseek - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #elif defined(__GNUC__) && _LARGEFILE64_SOURCE - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen64(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello64 - #define MZ_FSEEK64 fseeko64 - #define MZ_FILE_STAT_STRUCT stat64 - #define MZ_FILE_STAT stat64 - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(p, m, s) freopen64(p, m, s) - #define MZ_DELETE_FILE remove - #else - #ifndef MINIZ_NO_TIME - #include - #endif - #define MZ_FILE FILE - #define MZ_FOPEN(f, m) fopen(f, m) - #define MZ_FCLOSE fclose - #define MZ_FREAD fread - #define MZ_FWRITE fwrite - #define MZ_FTELL64 ftello - #define MZ_FSEEK64 fseeko - #define MZ_FILE_STAT_STRUCT stat - #define MZ_FILE_STAT stat - #define MZ_FFLUSH fflush - #define MZ_FREOPEN(f, m, s) freopen(f, m, s) - #define MZ_DELETE_FILE remove - #endif // #ifdef _MSC_VER -#endif // #ifdef MINIZ_NO_STDIO - -#define MZ_TOLOWER(c) ((((c) >= 'A') && ((c) <= 'Z')) ? ((c) - 'A' + 'a') : (c)) - -// Various ZIP archive enums. To completely avoid cross platform compiler alignment and platform endian issues, miniz.c doesn't use structs for any of this stuff. -enum -{ - // ZIP archive identifiers and record sizes - MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG = 0x06054b50, MZ_ZIP_CENTRAL_DIR_HEADER_SIG = 0x02014b50, MZ_ZIP_LOCAL_DIR_HEADER_SIG = 0x04034b50, - MZ_ZIP_LOCAL_DIR_HEADER_SIZE = 30, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE = 46, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE = 22, - // Central directory header record offsets - MZ_ZIP_CDH_SIG_OFS = 0, MZ_ZIP_CDH_VERSION_MADE_BY_OFS = 4, MZ_ZIP_CDH_VERSION_NEEDED_OFS = 6, MZ_ZIP_CDH_BIT_FLAG_OFS = 8, - MZ_ZIP_CDH_METHOD_OFS = 10, MZ_ZIP_CDH_FILE_TIME_OFS = 12, MZ_ZIP_CDH_FILE_DATE_OFS = 14, MZ_ZIP_CDH_CRC32_OFS = 16, - MZ_ZIP_CDH_COMPRESSED_SIZE_OFS = 20, MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS = 24, MZ_ZIP_CDH_FILENAME_LEN_OFS = 28, MZ_ZIP_CDH_EXTRA_LEN_OFS = 30, - MZ_ZIP_CDH_COMMENT_LEN_OFS = 32, MZ_ZIP_CDH_DISK_START_OFS = 34, MZ_ZIP_CDH_INTERNAL_ATTR_OFS = 36, MZ_ZIP_CDH_EXTERNAL_ATTR_OFS = 38, MZ_ZIP_CDH_LOCAL_HEADER_OFS = 42, - // Local directory header offsets - MZ_ZIP_LDH_SIG_OFS = 0, MZ_ZIP_LDH_VERSION_NEEDED_OFS = 4, MZ_ZIP_LDH_BIT_FLAG_OFS = 6, MZ_ZIP_LDH_METHOD_OFS = 8, MZ_ZIP_LDH_FILE_TIME_OFS = 10, - MZ_ZIP_LDH_FILE_DATE_OFS = 12, MZ_ZIP_LDH_CRC32_OFS = 14, MZ_ZIP_LDH_COMPRESSED_SIZE_OFS = 18, MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS = 22, - MZ_ZIP_LDH_FILENAME_LEN_OFS = 26, MZ_ZIP_LDH_EXTRA_LEN_OFS = 28, - // End of central directory offsets - MZ_ZIP_ECDH_SIG_OFS = 0, MZ_ZIP_ECDH_NUM_THIS_DISK_OFS = 4, MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS = 6, MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS = 8, - MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS = 10, MZ_ZIP_ECDH_CDIR_SIZE_OFS = 12, MZ_ZIP_ECDH_CDIR_OFS_OFS = 16, MZ_ZIP_ECDH_COMMENT_SIZE_OFS = 20, -}; - -typedef struct -{ - void *m_p; - size_t m_size, m_capacity; - mz_uint m_element_size; -} mz_zip_array; - -struct mz_zip_internal_state_tag -{ - mz_zip_array m_central_dir; - mz_zip_array m_central_dir_offsets; - mz_zip_array m_sorted_central_dir_offsets; - MZ_FILE *m_pFile; - void *m_pMem; - size_t m_mem_size; - size_t m_mem_capacity; -}; - -#define MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(array_ptr, element_size) (array_ptr)->m_element_size = element_size -#define MZ_ZIP_ARRAY_ELEMENT(array_ptr, element_type, index) ((element_type *)((array_ptr)->m_p))[index] - -static MZ_FORCEINLINE void mz_zip_array_clear(mz_zip_archive *pZip, mz_zip_array *pArray) -{ - pZip->m_pFree(pZip->m_pAlloc_opaque, pArray->m_p); - memset(pArray, 0, sizeof(mz_zip_array)); -} - -static mz_bool mz_zip_array_ensure_capacity(mz_zip_archive *pZip, mz_zip_array *pArray, size_t min_new_capacity, mz_uint growing) -{ - void *pNew_p; size_t new_capacity = min_new_capacity; MZ_ASSERT(pArray->m_element_size); if (pArray->m_capacity >= min_new_capacity) return MZ_TRUE; - if (growing) { new_capacity = MZ_MAX(1, pArray->m_capacity); while (new_capacity < min_new_capacity) new_capacity *= 2; } - if (NULL == (pNew_p = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pArray->m_p, pArray->m_element_size, new_capacity))) return MZ_FALSE; - pArray->m_p = pNew_p; pArray->m_capacity = new_capacity; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_reserve(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_capacity, mz_uint growing) -{ - if (new_capacity > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_capacity, growing)) return MZ_FALSE; } - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_resize(mz_zip_archive *pZip, mz_zip_array *pArray, size_t new_size, mz_uint growing) -{ - if (new_size > pArray->m_capacity) { if (!mz_zip_array_ensure_capacity(pZip, pArray, new_size, growing)) return MZ_FALSE; } - pArray->m_size = new_size; - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_ensure_room(mz_zip_archive *pZip, mz_zip_array *pArray, size_t n) -{ - return mz_zip_array_reserve(pZip, pArray, pArray->m_size + n, MZ_TRUE); -} - -static MZ_FORCEINLINE mz_bool mz_zip_array_push_back(mz_zip_archive *pZip, mz_zip_array *pArray, const void *pElements, size_t n) -{ - size_t orig_size = pArray->m_size; if (!mz_zip_array_resize(pZip, pArray, orig_size + n, MZ_TRUE)) return MZ_FALSE; - memcpy((mz_uint8*)pArray->m_p + orig_size * pArray->m_element_size, pElements, n * pArray->m_element_size); - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME -static time_t mz_zip_dos_to_time_t(int dos_time, int dos_date) -{ - struct tm tm; - memset(&tm, 0, sizeof(tm)); tm.tm_isdst = -1; - tm.tm_year = ((dos_date >> 9) & 127) + 1980 - 1900; tm.tm_mon = ((dos_date >> 5) & 15) - 1; tm.tm_mday = dos_date & 31; - tm.tm_hour = (dos_time >> 11) & 31; tm.tm_min = (dos_time >> 5) & 63; tm.tm_sec = (dos_time << 1) & 62; - return mktime(&tm); -} - -static void mz_zip_time_to_dos_time(time_t time, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef _MSC_VER - struct tm tm_struct; - struct tm *tm = &tm_struct; - errno_t err = localtime_s(tm, &time); - if (err) - { - *pDOS_date = 0; *pDOS_time = 0; - return; - } -#else - struct tm *tm = localtime(&time); -#endif - *pDOS_time = (mz_uint16)(((tm->tm_hour) << 11) + ((tm->tm_min) << 5) + ((tm->tm_sec) >> 1)); - *pDOS_date = (mz_uint16)(((tm->tm_year + 1900 - 1980) << 9) + ((tm->tm_mon + 1) << 5) + tm->tm_mday); -} -#endif - -#ifndef MINIZ_NO_STDIO -static mz_bool mz_zip_get_file_modified_time(const char *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date) -{ -#ifdef MINIZ_NO_TIME - (void)pFilename; *pDOS_date = *pDOS_time = 0; -#else - struct MZ_FILE_STAT_STRUCT file_stat; - // On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh. - if (MZ_FILE_STAT(pFilename, &file_stat) != 0) - return MZ_FALSE; - mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date); -#endif // #ifdef MINIZ_NO_TIME - return MZ_TRUE; -} - -#ifndef MINIZ_NO_TIME -static mz_bool mz_zip_set_file_times(const char *pFilename, time_t access_time, time_t modified_time) -{ - struct utimbuf t; t.actime = access_time; t.modtime = modified_time; - return !utime(pFilename, &t); -} -#endif // #ifndef MINIZ_NO_TIME -#endif // #ifndef MINIZ_NO_STDIO - -static mz_bool mz_zip_reader_init_internal(mz_zip_archive *pZip, mz_uint32 flags) -{ - (void)flags; - if ((!pZip) || (pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_READING; - pZip->m_archive_size = 0; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_filename_less(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, mz_uint r_index) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - const mz_uint8 *pR = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, r_index)); - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS), r_len = MZ_READ_LE16(pR + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; pR += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (l_len < r_len) : (l < r); -} - -#define MZ_SWAP_UINT32(a, b) do { mz_uint32 t = a; a = b; b = t; } MZ_MACRO_END - -// Heap sort of lowercased filenames, used to help accelerate plain central directory searches by mz_zip_reader_locate_file(). (Could also use qsort(), but it could allocate memory.) -static void mz_zip_reader_sort_central_dir_offsets_by_filename(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - int start = (size - 2) >> 1, end; - while (start >= 0) - { - int child, root = start; - for ( ; ; ) - { - if ((child = (root << 1) + 1) >= size) - break; - child += (((child + 1) < size) && (mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1]))); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - start--; - } - - end = size - 1; - while (end > 0) - { - int child, root = 0; - MZ_SWAP_UINT32(pIndices[end], pIndices[0]); - for ( ; ; ) - { - if ((child = (root << 1) + 1) >= end) - break; - child += (((child + 1) < end) && mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[child], pIndices[child + 1])); - if (!mz_zip_reader_filename_less(pCentral_dir, pCentral_dir_offsets, pIndices[root], pIndices[child])) - break; - MZ_SWAP_UINT32(pIndices[root], pIndices[child]); root = child; - } - end--; - } -} - -static mz_bool mz_zip_reader_read_central_dir(mz_zip_archive *pZip, mz_uint32 flags) -{ - mz_uint cdir_size, num_this_disk, cdir_disk_index; - mz_uint64 cdir_ofs; - mz_int64 cur_file_ofs; - const mz_uint8 *p; - mz_uint32 buf_u32[4096 / sizeof(mz_uint32)]; mz_uint8 *pBuf = (mz_uint8 *)buf_u32; - mz_bool sort_central_dir = ((flags & MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY) == 0); - // Basic sanity checks - reject files which are too small, and check the first 4 bytes of the file to make sure a local header is there. - if (pZip->m_archive_size < MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - // Find the end of central directory record by scanning the file from the end towards the beginning. - cur_file_ofs = MZ_MAX((mz_int64)pZip->m_archive_size - (mz_int64)sizeof(buf_u32), 0); - for ( ; ; ) - { - int i, n = (int)MZ_MIN(sizeof(buf_u32), pZip->m_archive_size - cur_file_ofs); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, n) != (mz_uint)n) - return MZ_FALSE; - for (i = n - 4; i >= 0; --i) - if (MZ_READ_LE32(pBuf + i) == MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) - break; - if (i >= 0) - { - cur_file_ofs += i; - break; - } - if ((!cur_file_ofs) || ((pZip->m_archive_size - cur_file_ofs) >= (0xFFFF + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE))) - return MZ_FALSE; - cur_file_ofs = MZ_MAX(cur_file_ofs - (sizeof(buf_u32) - 3), 0); - } - // Read and verify the end of central directory record. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if ((MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_SIG_OFS) != MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG) || - ((pZip->m_total_files = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS)) != MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS))) - return MZ_FALSE; - - num_this_disk = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_THIS_DISK_OFS); - cdir_disk_index = MZ_READ_LE16(pBuf + MZ_ZIP_ECDH_NUM_DISK_CDIR_OFS); - if (((num_this_disk | cdir_disk_index) != 0) && ((num_this_disk != 1) || (cdir_disk_index != 1))) - return MZ_FALSE; - - if ((cdir_size = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_SIZE_OFS)) < pZip->m_total_files * MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) - return MZ_FALSE; - - cdir_ofs = MZ_READ_LE32(pBuf + MZ_ZIP_ECDH_CDIR_OFS_OFS); - if ((cdir_ofs + (mz_uint64)cdir_size) > pZip->m_archive_size) - return MZ_FALSE; - - pZip->m_central_directory_file_ofs = cdir_ofs; - - if (pZip->m_total_files) - { - mz_uint i, n; - - // Read the entire central directory into a heap block, and allocate another heap block to hold the unsorted central dir file record offsets, and another to hold the sorted indices. - if ((!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir, cdir_size, MZ_FALSE)) || - (!mz_zip_array_resize(pZip, &pZip->m_pState->m_central_dir_offsets, pZip->m_total_files, MZ_FALSE))) - return MZ_FALSE; - - if (sort_central_dir) - { - if (!mz_zip_array_resize(pZip, &pZip->m_pState->m_sorted_central_dir_offsets, pZip->m_total_files, MZ_FALSE)) - return MZ_FALSE; - } - - if (pZip->m_pRead(pZip->m_pIO_opaque, cdir_ofs, pZip->m_pState->m_central_dir.m_p, cdir_size) != cdir_size) - return MZ_FALSE; - - // Now create an index into the central directory file records, do some basic sanity checking on each record, and check for zip64 entries (which are not yet supported). - p = (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p; - for (n = cdir_size, i = 0; i < pZip->m_total_files; ++i) - { - mz_uint total_header_size, comp_size, decomp_size, disk_index; - if ((n < MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) || (MZ_READ_LE32(p) != MZ_ZIP_CENTRAL_DIR_HEADER_SIG)) - return MZ_FALSE; - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, i) = (mz_uint32)(p - (const mz_uint8 *)pZip->m_pState->m_central_dir.m_p); - if (sort_central_dir) - MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_sorted_central_dir_offsets, mz_uint32, i) = i; - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - decomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - if (((!MZ_READ_LE32(p + MZ_ZIP_CDH_METHOD_OFS)) && (decomp_size != comp_size)) || (decomp_size && !comp_size) || (decomp_size == 0xFFFFFFFF) || (comp_size == 0xFFFFFFFF)) - return MZ_FALSE; - disk_index = MZ_READ_LE16(p + MZ_ZIP_CDH_DISK_START_OFS); - if ((disk_index != num_this_disk) && (disk_index != 1)) - return MZ_FALSE; - if (((mz_uint64)MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS) + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + comp_size) > pZip->m_archive_size) - return MZ_FALSE; - if ((total_header_size = MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS)) > n) - return MZ_FALSE; - n -= total_header_size; p += total_header_size; - } - } - - if (sort_central_dir) - mz_zip_reader_sort_central_dir_offsets_by_filename(pZip); - - return MZ_TRUE; -} - -mz_bool mz_zip_reader_init(mz_zip_archive *pZip, mz_uint64 size, mz_uint32 flags) -{ - if ((!pZip) || (!pZip->m_pRead)) - return MZ_FALSE; - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -static size_t mz_zip_mem_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - size_t s = (file_ofs >= pZip->m_archive_size) ? 0 : (size_t)MZ_MIN(pZip->m_archive_size - file_ofs, n); - memcpy(pBuf, (const mz_uint8 *)pZip->m_pState->m_pMem + file_ofs, s); - return s; -} - -mz_bool mz_zip_reader_init_mem(mz_zip_archive *pZip, const void *pMem, size_t size, mz_uint32 flags) -{ - if (!mz_zip_reader_init_internal(pZip, flags)) - return MZ_FALSE; - pZip->m_archive_size = size; - pZip->m_pRead = mz_zip_mem_read_func; - pZip->m_pIO_opaque = pZip; -#ifdef __cplusplus - pZip->m_pState->m_pMem = const_cast(pMem); -#else - pZip->m_pState->m_pMem = (void *)pMem; -#endif - pZip->m_pState->m_mem_size = size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_read_func(void *pOpaque, mz_uint64 file_ofs, void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FREAD(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_reader_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint32 flags) -{ - mz_uint64 file_size; - MZ_FILE *pFile = MZ_FOPEN(pFilename, "rb"); - if (!pFile) - return MZ_FALSE; - if (MZ_FSEEK64(pFile, 0, SEEK_END)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - file_size = MZ_FTELL64(pFile); - if (!mz_zip_reader_init_internal(pZip, flags)) - { - MZ_FCLOSE(pFile); - return MZ_FALSE; - } - pZip->m_pRead = mz_zip_file_read_func; - pZip->m_pIO_opaque = pZip; - pZip->m_pState->m_pFile = pFile; - pZip->m_archive_size = file_size; - if (!mz_zip_reader_read_central_dir(pZip, flags)) - { - mz_zip_reader_end(pZip); - return MZ_FALSE; - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_uint mz_zip_reader_get_num_files(mz_zip_archive *pZip) -{ - return pZip ? pZip->m_total_files : 0; -} - -static MZ_FORCEINLINE const mz_uint8 *mz_zip_reader_get_cdh(mz_zip_archive *pZip, mz_uint file_index) -{ - if ((!pZip) || (!pZip->m_pState) || (file_index >= pZip->m_total_files) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return NULL; - return &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); -} - -mz_bool mz_zip_reader_is_file_encrypted(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint m_bit_flag; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - return (m_bit_flag & 1); -} - -mz_bool mz_zip_reader_is_file_a_directory(mz_zip_archive *pZip, mz_uint file_index) -{ - mz_uint filename_len, external_attr; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) - return MZ_FALSE; - - // First see if the filename ends with a '/' character. - filename_len = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_len) - { - if (*(p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_len - 1) == '/') - return MZ_TRUE; - } - - // Bugfix: This code was also checking if the internal attribute was non-zero, which wasn't correct. - // Most/all zip writers (hopefully) set DOS file/directory attributes in the low 16-bits, so check for the DOS directory flag and ignore the source OS ID in the created by field. - // FIXME: Remove this check? Is it necessary - we already check the filename. - external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - if ((external_attr & 0x10) != 0) - return MZ_TRUE; - - return MZ_FALSE; -} - -mz_bool mz_zip_reader_file_stat(mz_zip_archive *pZip, mz_uint file_index, mz_zip_archive_file_stat *pStat) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if ((!p) || (!pStat)) - return MZ_FALSE; - - // Unpack the central directory record. - pStat->m_file_index = file_index; - pStat->m_central_dir_ofs = MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index); - pStat->m_version_made_by = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_MADE_BY_OFS); - pStat->m_version_needed = MZ_READ_LE16(p + MZ_ZIP_CDH_VERSION_NEEDED_OFS); - pStat->m_bit_flag = MZ_READ_LE16(p + MZ_ZIP_CDH_BIT_FLAG_OFS); - pStat->m_method = MZ_READ_LE16(p + MZ_ZIP_CDH_METHOD_OFS); -#ifndef MINIZ_NO_TIME - pStat->m_time = mz_zip_dos_to_time_t(MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_TIME_OFS), MZ_READ_LE16(p + MZ_ZIP_CDH_FILE_DATE_OFS)); -#endif - pStat->m_crc32 = MZ_READ_LE32(p + MZ_ZIP_CDH_CRC32_OFS); - pStat->m_comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - pStat->m_uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - pStat->m_internal_attr = MZ_READ_LE16(p + MZ_ZIP_CDH_INTERNAL_ATTR_OFS); - pStat->m_external_attr = MZ_READ_LE32(p + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS); - pStat->m_local_header_ofs = MZ_READ_LE32(p + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - - // Copy as much of the filename and comment as possible. - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILENAME_SIZE - 1); - memcpy(pStat->m_filename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); pStat->m_filename[n] = '\0'; - - n = MZ_READ_LE16(p + MZ_ZIP_CDH_COMMENT_LEN_OFS); n = MZ_MIN(n, MZ_ZIP_MAX_ARCHIVE_FILE_COMMENT_SIZE - 1); - pStat->m_comment_size = n; - memcpy(pStat->m_comment, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(p + MZ_ZIP_CDH_EXTRA_LEN_OFS), n); pStat->m_comment[n] = '\0'; - - return MZ_TRUE; -} - -mz_uint mz_zip_reader_get_filename(mz_zip_archive *pZip, mz_uint file_index, char *pFilename, mz_uint filename_buf_size) -{ - mz_uint n; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - if (!p) { if (filename_buf_size) pFilename[0] = '\0'; return 0; } - n = MZ_READ_LE16(p + MZ_ZIP_CDH_FILENAME_LEN_OFS); - if (filename_buf_size) - { - n = MZ_MIN(n, filename_buf_size - 1); - memcpy(pFilename, p + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n); - pFilename[n] = '\0'; - } - return n + 1; -} - -static MZ_FORCEINLINE mz_bool mz_zip_reader_string_equal(const char *pA, const char *pB, mz_uint len, mz_uint flags) -{ - mz_uint i; - if (flags & MZ_ZIP_FLAG_CASE_SENSITIVE) - return 0 == memcmp(pA, pB, len); - for (i = 0; i < len; ++i) - if (MZ_TOLOWER(pA[i]) != MZ_TOLOWER(pB[i])) - return MZ_FALSE; - return MZ_TRUE; -} - -static MZ_FORCEINLINE int mz_zip_reader_filename_compare(const mz_zip_array *pCentral_dir_array, const mz_zip_array *pCentral_dir_offsets, mz_uint l_index, const char *pR, mz_uint r_len) -{ - const mz_uint8 *pL = &MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_array, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(pCentral_dir_offsets, mz_uint32, l_index)), *pE; - mz_uint l_len = MZ_READ_LE16(pL + MZ_ZIP_CDH_FILENAME_LEN_OFS); - mz_uint8 l = 0, r = 0; - pL += MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - pE = pL + MZ_MIN(l_len, r_len); - while (pL < pE) - { - if ((l = MZ_TOLOWER(*pL)) != (r = MZ_TOLOWER(*pR))) - break; - pL++; pR++; - } - return (pL == pE) ? (int)(l_len - r_len) : (l - r); -} - -static int mz_zip_reader_locate_file_binary_search(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState = pZip->m_pState; - const mz_zip_array *pCentral_dir_offsets = &pState->m_central_dir_offsets; - const mz_zip_array *pCentral_dir = &pState->m_central_dir; - mz_uint32 *pIndices = &MZ_ZIP_ARRAY_ELEMENT(&pState->m_sorted_central_dir_offsets, mz_uint32, 0); - const int size = pZip->m_total_files; - const mz_uint filename_len = (mz_uint)strlen(pFilename); - int l = 0, h = size - 1; - while (l <= h) - { - int m = (l + h) >> 1, file_index = pIndices[m], comp = mz_zip_reader_filename_compare(pCentral_dir, pCentral_dir_offsets, file_index, pFilename, filename_len); - if (!comp) - return file_index; - else if (comp < 0) - l = m + 1; - else - h = m - 1; - } - return -1; -} - -int mz_zip_reader_locate_file(mz_zip_archive *pZip, const char *pName, const char *pComment, mz_uint flags) -{ - mz_uint file_index; size_t name_len, comment_len; - if ((!pZip) || (!pZip->m_pState) || (!pName) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return -1; - if (((flags & (MZ_ZIP_FLAG_IGNORE_PATH | MZ_ZIP_FLAG_CASE_SENSITIVE)) == 0) && (!pComment) && (pZip->m_pState->m_sorted_central_dir_offsets.m_size)) - return mz_zip_reader_locate_file_binary_search(pZip, pName); - name_len = strlen(pName); if (name_len > 0xFFFF) return -1; - comment_len = pComment ? strlen(pComment) : 0; if (comment_len > 0xFFFF) return -1; - for (file_index = 0; file_index < pZip->m_total_files; file_index++) - { - const mz_uint8 *pHeader = &MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir, mz_uint8, MZ_ZIP_ARRAY_ELEMENT(&pZip->m_pState->m_central_dir_offsets, mz_uint32, file_index)); - mz_uint filename_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_FILENAME_LEN_OFS); - const char *pFilename = (const char *)pHeader + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE; - if (filename_len < name_len) - continue; - if (comment_len) - { - mz_uint file_extra_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_EXTRA_LEN_OFS), file_comment_len = MZ_READ_LE16(pHeader + MZ_ZIP_CDH_COMMENT_LEN_OFS); - const char *pFile_comment = pFilename + filename_len + file_extra_len; - if ((file_comment_len != comment_len) || (!mz_zip_reader_string_equal(pComment, pFile_comment, file_comment_len, flags))) - continue; - } - if ((flags & MZ_ZIP_FLAG_IGNORE_PATH) && (filename_len)) - { - int ofs = filename_len - 1; - do - { - if ((pFilename[ofs] == '/') || (pFilename[ofs] == '\\') || (pFilename[ofs] == ':')) - break; - } while (--ofs >= 0); - ofs++; - pFilename += ofs; filename_len -= ofs; - } - if ((filename_len == name_len) && (mz_zip_reader_string_equal(pName, pFilename, filename_len, flags))) - return file_index; - } - return -1; -} - -mz_bool mz_zip_reader_extract_to_mem_no_alloc(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int status = TINFL_STATUS_DONE; - mz_uint64 needed_size, cur_file_ofs, comp_remaining, out_buf_ofs = 0, read_buf_size, read_buf_ofs = 0, read_buf_avail; - mz_zip_archive_file_stat file_stat; - void *pRead_buf; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - tinfl_decompressor inflator; - - if ((buf_size) && (!pBuf)) - return MZ_FALSE; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Ensure supplied output buffer is large enough. - needed_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? file_stat.m_comp_size : file_stat.m_uncomp_size; - if (buf_size < needed_size) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pBuf, (size_t)needed_size) != needed_size) - return MZ_FALSE; - return ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) != 0) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) == file_stat.m_crc32); - } - - // Decompress the file either directly from memory or from a file input buffer. - tinfl_init(&inflator); - - if (pZip->m_pState->m_pMem) - { - // Read directly from the archive in memory. - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else if (pUser_read_buf) - { - // Use a user provided read buffer. - if (!user_read_buf_size) - return MZ_FALSE; - pRead_buf = (mz_uint8 *)pUser_read_buf; - read_buf_size = user_read_buf_size; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - else - { - // Temporarily allocate a read buffer. - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (read_buf_size > 0x7FFFFFFF)) -#endif - return MZ_FALSE; - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - do - { - size_t in_buf_size, out_buf_size = (size_t)(file_stat.m_uncomp_size - out_buf_ofs); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pBuf, (mz_uint8 *)pBuf + out_buf_ofs, &out_buf_size, TINFL_FLAG_USING_NON_WRAPPING_OUTPUT_BUF | (comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0)); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - out_buf_ofs += out_buf_size; - } while (status == TINFL_STATUS_NEEDS_MORE_INPUT); - - if (status == TINFL_STATUS_DONE) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (mz_crc32(MZ_CRC32_INIT, (const mz_uint8 *)pBuf, (size_t)file_stat.m_uncomp_size) != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if ((!pZip->m_pState->m_pMem) && (!pUser_read_buf)) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_mem_no_alloc(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags, void *pUser_read_buf, size_t user_read_buf_size) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, pUser_read_buf, user_read_buf_size); -} - -mz_bool mz_zip_reader_extract_to_mem(mz_zip_archive *pZip, mz_uint file_index, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_to_mem_no_alloc(pZip, file_index, pBuf, buf_size, flags, NULL, 0); -} - -mz_bool mz_zip_reader_extract_file_to_mem(mz_zip_archive *pZip, const char *pFilename, void *pBuf, size_t buf_size, mz_uint flags) -{ - return mz_zip_reader_extract_file_to_mem_no_alloc(pZip, pFilename, pBuf, buf_size, flags, NULL, 0); -} - -void *mz_zip_reader_extract_to_heap(mz_zip_archive *pZip, mz_uint file_index, size_t *pSize, mz_uint flags) -{ - mz_uint64 comp_size, uncomp_size, alloc_size; - const mz_uint8 *p = mz_zip_reader_get_cdh(pZip, file_index); - void *pBuf; - - if (pSize) - *pSize = 0; - if (!p) - return NULL; - - comp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - uncomp_size = MZ_READ_LE32(p + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS); - - alloc_size = (flags & MZ_ZIP_FLAG_COMPRESSED_DATA) ? comp_size : uncomp_size; -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (alloc_size > 0x7FFFFFFF)) -#endif - return NULL; - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)alloc_size))) - return NULL; - - if (!mz_zip_reader_extract_to_mem(pZip, file_index, pBuf, (size_t)alloc_size, flags)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return NULL; - } - - if (pSize) *pSize = (size_t)alloc_size; - return pBuf; -} - -void *mz_zip_reader_extract_file_to_heap(mz_zip_archive *pZip, const char *pFilename, size_t *pSize, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - { - if (pSize) *pSize = 0; - return MZ_FALSE; - } - return mz_zip_reader_extract_to_heap(pZip, file_index, pSize, flags); -} - -mz_bool mz_zip_reader_extract_to_callback(mz_zip_archive *pZip, mz_uint file_index, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int status = TINFL_STATUS_DONE; mz_uint file_crc32 = MZ_CRC32_INIT; - mz_uint64 read_buf_size, read_buf_ofs = 0, read_buf_avail, comp_remaining, out_buf_ofs = 0, cur_file_ofs; - mz_zip_archive_file_stat file_stat; - void *pRead_buf = NULL; void *pWrite_buf = NULL; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - - // Empty file, or a directory (but not always a directory - I've seen odd zips with directories that have compressed data which inflates to 0 bytes) - if (!file_stat.m_comp_size) - return MZ_TRUE; - - // Entry is a subdirectory (I've seen old zips with dir entries which have compressed deflate data which inflates to 0 bytes, but these entries claim to uncompress to 512 bytes in the headers). - // I'm torn how to handle this case - should it fail instead? - if (mz_zip_reader_is_file_a_directory(pZip, file_index)) - return MZ_TRUE; - - // Encryption and patch files are not supported. - if (file_stat.m_bit_flag & (1 | 32)) - return MZ_FALSE; - - // This function only supports stored and deflate. - if ((!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (file_stat.m_method != 0) && (file_stat.m_method != MZ_DEFLATED)) - return MZ_FALSE; - - // Read and parse the local directory entry. - cur_file_ofs = file_stat.m_local_header_ofs; - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - - cur_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - if ((cur_file_ofs + file_stat.m_comp_size) > pZip->m_archive_size) - return MZ_FALSE; - - // Decompress the file either directly from memory or from a file input buffer. - if (pZip->m_pState->m_pMem) - { - pRead_buf = (mz_uint8 *)pZip->m_pState->m_pMem + cur_file_ofs; - read_buf_size = read_buf_avail = file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - read_buf_size = MZ_MIN(file_stat.m_comp_size, MZ_ZIP_MAX_IO_BUF_SIZE); - if (NULL == (pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)read_buf_size))) - return MZ_FALSE; - read_buf_avail = 0; - comp_remaining = file_stat.m_comp_size; - } - - if ((flags & MZ_ZIP_FLAG_COMPRESSED_DATA) || (!file_stat.m_method)) - { - // The file is stored or the caller has requested the compressed data. - if (pZip->m_pState->m_pMem) - { -#ifdef _MSC_VER - if (((0, sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#else - if (((sizeof(size_t) == sizeof(mz_uint32))) && (file_stat.m_comp_size > 0xFFFFFFFF)) -#endif - return MZ_FALSE; - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)file_stat.m_comp_size) != file_stat.m_comp_size) - status = TINFL_STATUS_FAILED; - else if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)file_stat.m_comp_size); - cur_file_ofs += file_stat.m_comp_size; - out_buf_ofs += file_stat.m_comp_size; - comp_remaining = 0; - } - else - { - while (comp_remaining) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - - if (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - file_crc32 = (mz_uint32)mz_crc32(file_crc32, (const mz_uint8 *)pRead_buf, (size_t)read_buf_avail); - - if (pCallback(pOpaque, out_buf_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - out_buf_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - } - } - } - else - { - tinfl_decompressor inflator; - tinfl_init(&inflator); - - if (NULL == (pWrite_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, TINFL_LZ_DICT_SIZE))) - status = TINFL_STATUS_FAILED; - else - { - do - { - mz_uint8 *pWrite_buf_cur = (mz_uint8 *)pWrite_buf + (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - size_t in_buf_size, out_buf_size = TINFL_LZ_DICT_SIZE - (out_buf_ofs & (TINFL_LZ_DICT_SIZE - 1)); - if ((!read_buf_avail) && (!pZip->m_pState->m_pMem)) - { - read_buf_avail = MZ_MIN(read_buf_size, comp_remaining); - if (pZip->m_pRead(pZip->m_pIO_opaque, cur_file_ofs, pRead_buf, (size_t)read_buf_avail) != read_buf_avail) - { - status = TINFL_STATUS_FAILED; - break; - } - cur_file_ofs += read_buf_avail; - comp_remaining -= read_buf_avail; - read_buf_ofs = 0; - } - - in_buf_size = (size_t)read_buf_avail; - status = tinfl_decompress(&inflator, (const mz_uint8 *)pRead_buf + read_buf_ofs, &in_buf_size, (mz_uint8 *)pWrite_buf, pWrite_buf_cur, &out_buf_size, comp_remaining ? TINFL_FLAG_HAS_MORE_INPUT : 0); - read_buf_avail -= in_buf_size; - read_buf_ofs += in_buf_size; - - if (out_buf_size) - { - if (pCallback(pOpaque, out_buf_ofs, pWrite_buf_cur, out_buf_size) != out_buf_size) - { - status = TINFL_STATUS_FAILED; - break; - } - file_crc32 = (mz_uint32)mz_crc32(file_crc32, pWrite_buf_cur, out_buf_size); - if ((out_buf_ofs += out_buf_size) > file_stat.m_uncomp_size) - { - status = TINFL_STATUS_FAILED; - break; - } - } - } while ((status == TINFL_STATUS_NEEDS_MORE_INPUT) || (status == TINFL_STATUS_HAS_MORE_OUTPUT)); - } - } - - if ((status == TINFL_STATUS_DONE) && (!(flags & MZ_ZIP_FLAG_COMPRESSED_DATA))) - { - // Make sure the entire file was decompressed, and check its CRC. - if ((out_buf_ofs != file_stat.m_uncomp_size) || (file_crc32 != file_stat.m_crc32)) - status = TINFL_STATUS_FAILED; - } - - if (!pZip->m_pState->m_pMem) - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - if (pWrite_buf) - pZip->m_pFree(pZip->m_pAlloc_opaque, pWrite_buf); - - return status == TINFL_STATUS_DONE; -} - -mz_bool mz_zip_reader_extract_file_to_callback(mz_zip_archive *pZip, const char *pFilename, mz_file_write_func pCallback, void *pOpaque, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pFilename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_callback(pZip, file_index, pCallback, pOpaque, flags); -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_callback(void *pOpaque, mz_uint64 ofs, const void *pBuf, size_t n) -{ - (void)ofs; return MZ_FWRITE(pBuf, 1, n, (MZ_FILE*)pOpaque); -} - -mz_bool mz_zip_reader_extract_to_file(mz_zip_archive *pZip, mz_uint file_index, const char *pDst_filename, mz_uint flags) -{ - mz_bool status; - mz_zip_archive_file_stat file_stat; - MZ_FILE *pFile; - if (!mz_zip_reader_file_stat(pZip, file_index, &file_stat)) - return MZ_FALSE; - pFile = MZ_FOPEN(pDst_filename, "wb"); - if (!pFile) - return MZ_FALSE; - status = mz_zip_reader_extract_to_callback(pZip, file_index, mz_zip_file_write_callback, pFile, flags); - if (MZ_FCLOSE(pFile) == EOF) - return MZ_FALSE; -#ifndef MINIZ_NO_TIME - if (status) - mz_zip_set_file_times(pDst_filename, file_stat.m_time, file_stat.m_time); -#endif - return status; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_reader_end(mz_zip_archive *pZip) -{ - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - - if (pZip->m_pState) - { - mz_zip_internal_state *pState = pZip->m_pState; pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - } - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_reader_extract_file_to_file(mz_zip_archive *pZip, const char *pArchive_filename, const char *pDst_filename, mz_uint flags) -{ - int file_index = mz_zip_reader_locate_file(pZip, pArchive_filename, NULL, flags); - if (file_index < 0) - return MZ_FALSE; - return mz_zip_reader_extract_to_file(pZip, file_index, pDst_filename, flags); -} -#endif - -// ------------------- .ZIP archive writing - -#ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -static void mz_write_le16(mz_uint8 *p, mz_uint16 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); } -static void mz_write_le32(mz_uint8 *p, mz_uint32 v) { p[0] = (mz_uint8)v; p[1] = (mz_uint8)(v >> 8); p[2] = (mz_uint8)(v >> 16); p[3] = (mz_uint8)(v >> 24); } -#define MZ_WRITE_LE16(p, v) mz_write_le16((mz_uint8 *)(p), (mz_uint16)(v)) -#define MZ_WRITE_LE32(p, v) mz_write_le32((mz_uint8 *)(p), (mz_uint32)(v)) - -mz_bool mz_zip_writer_init(mz_zip_archive *pZip, mz_uint64 existing_size) -{ - if ((!pZip) || (pZip->m_pState) || (!pZip->m_pWrite) || (pZip->m_zip_mode != MZ_ZIP_MODE_INVALID)) - return MZ_FALSE; - - if (pZip->m_file_offset_alignment) - { - // Ensure user specified file offset alignment is a power of 2. - if (pZip->m_file_offset_alignment & (pZip->m_file_offset_alignment - 1)) - return MZ_FALSE; - } - - if (!pZip->m_pAlloc) pZip->m_pAlloc = def_alloc_func; - if (!pZip->m_pFree) pZip->m_pFree = def_free_func; - if (!pZip->m_pRealloc) pZip->m_pRealloc = def_realloc_func; - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_archive_size = existing_size; - pZip->m_central_directory_file_ofs = 0; - pZip->m_total_files = 0; - - if (NULL == (pZip->m_pState = (mz_zip_internal_state *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(mz_zip_internal_state)))) - return MZ_FALSE; - memset(pZip->m_pState, 0, sizeof(mz_zip_internal_state)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir, sizeof(mz_uint8)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_central_dir_offsets, sizeof(mz_uint32)); - MZ_ZIP_ARRAY_SET_ELEMENT_SIZE(&pZip->m_pState->m_sorted_central_dir_offsets, sizeof(mz_uint32)); - return MZ_TRUE; -} - -static size_t mz_zip_heap_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint64 new_size = MZ_MAX(file_ofs + n, pState->m_mem_size); -#ifdef _MSC_VER - if ((!n) || ((0, sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#else - if ((!n) || ((sizeof(size_t) == sizeof(mz_uint32)) && (new_size > 0x7FFFFFFF))) -#endif - return 0; - if (new_size > pState->m_mem_capacity) - { - void *pNew_block; - size_t new_capacity = MZ_MAX(64, pState->m_mem_capacity); while (new_capacity < new_size) new_capacity *= 2; - if (NULL == (pNew_block = pZip->m_pRealloc(pZip->m_pAlloc_opaque, pState->m_pMem, 1, new_capacity))) - return 0; - pState->m_pMem = pNew_block; pState->m_mem_capacity = new_capacity; - } - memcpy((mz_uint8 *)pState->m_pMem + file_ofs, pBuf, n); - pState->m_mem_size = (size_t)new_size; - return n; -} - -mz_bool mz_zip_writer_init_heap(mz_zip_archive *pZip, size_t size_to_reserve_at_beginning, size_t initial_allocation_size) -{ - pZip->m_pWrite = mz_zip_heap_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (0 != (initial_allocation_size = MZ_MAX(initial_allocation_size, size_to_reserve_at_beginning))) - { - if (NULL == (pZip->m_pState->m_pMem = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, initial_allocation_size))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_mem_capacity = initial_allocation_size; - } - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -static size_t mz_zip_file_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n) -{ - mz_zip_archive *pZip = (mz_zip_archive *)pOpaque; - mz_int64 cur_ofs = MZ_FTELL64(pZip->m_pState->m_pFile); - if (((mz_int64)file_ofs < 0) || (((cur_ofs != (mz_int64)file_ofs)) && (MZ_FSEEK64(pZip->m_pState->m_pFile, (mz_int64)file_ofs, SEEK_SET)))) - return 0; - return MZ_FWRITE(pBuf, 1, n, pZip->m_pState->m_pFile); -} - -mz_bool mz_zip_writer_init_file(mz_zip_archive *pZip, const char *pFilename, mz_uint64 size_to_reserve_at_beginning) -{ - MZ_FILE *pFile; - pZip->m_pWrite = mz_zip_file_write_func; - pZip->m_pIO_opaque = pZip; - if (!mz_zip_writer_init(pZip, size_to_reserve_at_beginning)) - return MZ_FALSE; - if (NULL == (pFile = MZ_FOPEN(pFilename, "wb"))) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - pZip->m_pState->m_pFile = pFile; - if (size_to_reserve_at_beginning) - { - mz_uint64 cur_ofs = 0; char buf[4096]; MZ_CLEAR_OBJ(buf); - do - { - size_t n = (size_t)MZ_MIN(sizeof(buf), size_to_reserve_at_beginning); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_ofs, buf, n) != n) - { - mz_zip_writer_end(pZip); - return MZ_FALSE; - } - cur_ofs += n; size_to_reserve_at_beginning -= n; - } while (size_to_reserve_at_beginning); - } - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_init_from_reader(mz_zip_archive *pZip, const char *pFilename) -{ - mz_zip_internal_state *pState; - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_READING)) - return MZ_FALSE; - // No sense in trying to write to an archive that's already at the support max size - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + MZ_ZIP_LOCAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if (pState->m_pFile) - { -#ifdef MINIZ_NO_STDIO - pFilename; return MZ_FALSE; -#else - // Archive is being read from stdio - try to reopen as writable. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - if (!pFilename) - return MZ_FALSE; - pZip->m_pWrite = mz_zip_file_write_func; - if (NULL == (pState->m_pFile = MZ_FREOPEN(pFilename, "r+b", pState->m_pFile))) - { - // The mz_zip_archive is now in a bogus state because pState->m_pFile is NULL, so just close it. - mz_zip_reader_end(pZip); - return MZ_FALSE; - } -#endif // #ifdef MINIZ_NO_STDIO - } - else if (pState->m_pMem) - { - // Archive lives in a memory block. Assume it's from the heap that we can resize using the realloc callback. - if (pZip->m_pIO_opaque != pZip) - return MZ_FALSE; - pState->m_mem_capacity = pState->m_mem_size; - pZip->m_pWrite = mz_zip_heap_write_func; - } - // Archive is being read via a user provided read function - make sure the user has specified a write function too. - else if (!pZip->m_pWrite) - return MZ_FALSE; - - // Start writing new files at the archive's current central directory location. - pZip->m_archive_size = pZip->m_central_directory_file_ofs; - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING; - pZip->m_central_directory_file_ofs = 0; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, mz_uint level_and_flags) -{ - return mz_zip_writer_add_mem_ex(pZip, pArchive_name, pBuf, buf_size, NULL, 0, level_and_flags, 0, 0); -} - -typedef struct -{ - mz_zip_archive *m_pZip; - mz_uint64 m_cur_archive_file_ofs; - mz_uint64 m_comp_size; -} mz_zip_writer_add_state; - -static mz_bool mz_zip_writer_add_put_buf_callback(const void* pBuf, int len, void *pUser) -{ - mz_zip_writer_add_state *pState = (mz_zip_writer_add_state *)pUser; - if ((int)pState->m_pZip->m_pWrite(pState->m_pZip->m_pIO_opaque, pState->m_cur_archive_file_ofs, pBuf, len) != len) - return MZ_FALSE; - pState->m_cur_archive_file_ofs += len; - pState->m_comp_size += len; - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_local_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_LOCAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_SIG_OFS, MZ_ZIP_LOCAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_LDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_LDH_EXTRA_LEN_OFS, extra_size); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_create_central_dir_header(mz_zip_archive *pZip, mz_uint8 *pDst, mz_uint16 filename_size, mz_uint16 extra_size, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - (void)pZip; - memset(pDst, 0, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_SIG_OFS, MZ_ZIP_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_VERSION_NEEDED_OFS, method ? 20 : 0); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_BIT_FLAG_OFS, bit_flags); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_METHOD_OFS, method); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_TIME_OFS, dos_time); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILE_DATE_OFS, dos_date); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_CRC32_OFS, uncomp_crc32); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS, comp_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_DECOMPRESSED_SIZE_OFS, uncomp_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_FILENAME_LEN_OFS, filename_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_EXTRA_LEN_OFS, extra_size); - MZ_WRITE_LE16(pDst + MZ_ZIP_CDH_COMMENT_LEN_OFS, comment_size); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_EXTERNAL_ATTR_OFS, ext_attributes); - MZ_WRITE_LE32(pDst + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_header_ofs); - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_add_to_central_dir(mz_zip_archive *pZip, const char *pFilename, mz_uint16 filename_size, const void *pExtra, mz_uint16 extra_size, const void *pComment, mz_uint16 comment_size, mz_uint64 uncomp_size, mz_uint64 comp_size, mz_uint32 uncomp_crc32, mz_uint16 method, mz_uint16 bit_flags, mz_uint16 dos_time, mz_uint16 dos_date, mz_uint64 local_header_ofs, mz_uint32 ext_attributes) -{ - mz_zip_internal_state *pState = pZip->m_pState; - mz_uint32 central_dir_ofs = (mz_uint32)pState->m_central_dir.m_size; - size_t orig_central_dir_size = pState->m_central_dir.m_size; - mz_uint8 central_dir_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - - // No zip64 support yet - if ((local_header_ofs > 0xFFFFFFFF) || (((mz_uint64)pState->m_central_dir.m_size + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + filename_size + extra_size + comment_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_central_dir_header(pZip, central_dir_header, filename_size, extra_size, comment_size, uncomp_size, comp_size, uncomp_crc32, method, bit_flags, dos_time, dos_date, local_header_ofs, ext_attributes)) - return MZ_FALSE; - - if ((!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_dir_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pFilename, filename_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pExtra, extra_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pComment, comment_size)) || - (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, ¢ral_dir_ofs, 1))) - { - // Try to push the central directory array back into its original state. - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - return MZ_TRUE; -} - -static mz_bool mz_zip_writer_validate_archive_name(const char *pArchive_name) -{ - // Basic ZIP archive filename validity checks: Valid filenames cannot start with a forward slash, cannot contain a drive letter, and cannot use DOS-style backward slashes. - if (*pArchive_name == '/') - return MZ_FALSE; - while (*pArchive_name) - { - if ((*pArchive_name == '\\') || (*pArchive_name == ':')) - return MZ_FALSE; - pArchive_name++; - } - return MZ_TRUE; -} - -static mz_uint mz_zip_writer_compute_padding_needed_for_file_alignment(mz_zip_archive *pZip) -{ - mz_uint32 n; - if (!pZip->m_file_offset_alignment) - return 0; - n = (mz_uint32)(pZip->m_archive_size & (pZip->m_file_offset_alignment - 1)); - return (pZip->m_file_offset_alignment - n) & (pZip->m_file_offset_alignment - 1); -} - -static mz_bool mz_zip_writer_write_zeros(mz_zip_archive *pZip, mz_uint64 cur_file_ofs, mz_uint32 n) -{ - char buf[4096]; - memset(buf, 0, MZ_MIN(sizeof(buf), n)); - while (n) - { - mz_uint32 s = MZ_MIN(sizeof(buf), n); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_file_ofs, buf, s) != s) - return MZ_FALSE; - cur_file_ofs += s; n -= s; - } - return MZ_TRUE; -} - -mz_bool mz_zip_writer_add_mem_ex(mz_zip_archive *pZip, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags, mz_uint64 uncomp_size, mz_uint32 uncomp_crc32) -{ - mz_uint16 method = 0, dos_time = 0, dos_date = 0; - mz_uint level, ext_attributes = 0, num_alignment_padding_bytes; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - tdefl_compressor *pComp = NULL; - mz_bool store_data_uncompressed; - mz_zip_internal_state *pState; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - store_data_uncompressed = ((!level) || (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)); - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || ((buf_size) && (!pBuf)) || (!pArchive_name) || ((comment_size) && (!pComment)) || (pZip->m_total_files == 0xFFFF) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - - pState = pZip->m_pState; - - if ((!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) && (uncomp_size)) - return MZ_FALSE; - // No zip64 support yet - if ((buf_size > 0xFFFFFFFF) || (uncomp_size > 0xFFFFFFFF)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - -#ifndef MINIZ_NO_TIME - { - time_t cur_time; time(&cur_time); - mz_zip_time_to_dos_time(cur_time, &dos_time, &dos_date); - } -#endif // #ifndef MINIZ_NO_TIME - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if ((archive_name_size) && (pArchive_name[archive_name_size - 1] == '/')) - { - // Set DOS Subdirectory attribute bit. - ext_attributes |= 0x10; - // Subdirectories cannot contain data. - if ((buf_size) || (uncomp_size)) - return MZ_FALSE; - } - - // Try to do any allocations before writing to the archive, so if an allocation fails the file remains unmodified. (A good idea if we're doing an in-place modification.) - if ((!mz_zip_array_ensure_room(pZip, &pState->m_central_dir, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + archive_name_size + comment_size)) || (!mz_zip_array_ensure_room(pZip, &pState->m_central_dir_offsets, 1))) - return MZ_FALSE; - - if ((!store_data_uncompressed) && (buf_size)) - { - if (NULL == (pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)))) - return MZ_FALSE; - } - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (!(level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)) - { - uncomp_crc32 = (mz_uint32)mz_crc32(MZ_CRC32_INIT, (const mz_uint8*)pBuf, buf_size); - uncomp_size = buf_size; - if (uncomp_size <= 3) - { - level = 0; - store_data_uncompressed = MZ_TRUE; - } - } - - if (store_data_uncompressed) - { - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pBuf, buf_size) != buf_size) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - cur_archive_file_ofs += buf_size; - comp_size = buf_size; - - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - method = MZ_DEFLATED; - } - else if (buf_size) - { - mz_zip_writer_add_state state; - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if ((tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) || - (tdefl_compress_buffer(pComp, pBuf, buf_size, TDEFL_FINISH) != TDEFL_STATUS_DONE)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pComp = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const char *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes; - mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0; - mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0; - size_t archive_name_size; - mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE]; - MZ_FILE *pSrc_file = NULL; - - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - level = level_and_flags & 0xF; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - - archive_name_size = strlen(pArchive_name); - if (archive_name_size > 0xFFFF) - return MZ_FALSE; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date)) - return MZ_FALSE; - - pSrc_file = MZ_FOPEN(pSrc_filename, "rb"); - if (!pSrc_file) - return MZ_FALSE; - MZ_FSEEK64(pSrc_file, 0, SEEK_END); - uncomp_size = MZ_FTELL64(pSrc_file); - MZ_FSEEK64(pSrc_file, 0, SEEK_SET); - - if (uncomp_size > 0xFFFFFFFF) - { - // No zip64 support yet - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - if (uncomp_size <= 3) - level = 0; - - if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes + sizeof(local_dir_header))) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - local_dir_header_ofs += num_alignment_padding_bytes; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header); - - MZ_CLEAR_OBJ(local_dir_header); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - cur_archive_file_ofs += archive_name_size; - - if (uncomp_size) - { - mz_uint64 uncomp_remaining = uncomp_size; - void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE); - if (!pRead_buf) - { - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - if (!level) - { - while (uncomp_remaining) - { - mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining); - if ((MZ_FREAD(pRead_buf, 1, n, pSrc_file) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n); - uncomp_remaining -= n; - cur_archive_file_ofs += n; - } - comp_size = uncomp_size; - } - else - { - mz_bool result = MZ_FALSE; - mz_zip_writer_add_state state; - tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor)); - if (!pComp) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - state.m_pZip = pZip; - state.m_cur_archive_file_ofs = cur_archive_file_ofs; - state.m_comp_size = 0; - - if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - for ( ; ; ) - { - size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE); - tdefl_status status; - - if (MZ_FREAD(pRead_buf, 1, in_buf_size, pSrc_file) != in_buf_size) - break; - - uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size); - uncomp_remaining -= in_buf_size; - - status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH); - if (status == TDEFL_STATUS_DONE) - { - result = MZ_TRUE; - break; - } - else if (status != TDEFL_STATUS_OKAY) - break; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pComp); - - if (!result) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - MZ_FCLOSE(pSrc_file); - return MZ_FALSE; - } - - comp_size = state.m_comp_size; - cur_archive_file_ofs = state.m_cur_archive_file_ofs; - - method = MZ_DEFLATED; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf); - } - - MZ_FCLOSE(pSrc_file); pSrc_file = NULL; - - // no zip64 support yet - if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF)) - return MZ_FALSE; - - if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date)) - return MZ_FALSE; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header)) - return MZ_FALSE; - - if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size, uncomp_size, comp_size, uncomp_crc32, method, 0, dos_time, dos_date, local_dir_header_ofs, ext_attributes)) - return MZ_FALSE; - - pZip->m_total_files++; - pZip->m_archive_size = cur_archive_file_ofs; - - return MZ_TRUE; -} -#endif // #ifndef MINIZ_NO_STDIO - -mz_bool mz_zip_writer_add_from_zip_reader(mz_zip_archive *pZip, mz_zip_archive *pSource_zip, mz_uint file_index) -{ - mz_uint n, bit_flags, num_alignment_padding_bytes; - mz_uint64 comp_bytes_remaining, local_dir_header_ofs; - mz_uint64 cur_src_file_ofs, cur_dst_file_ofs; - mz_uint32 local_header_u32[(MZ_ZIP_LOCAL_DIR_HEADER_SIZE + sizeof(mz_uint32) - 1) / sizeof(mz_uint32)]; mz_uint8 *pLocal_header = (mz_uint8 *)local_header_u32; - mz_uint8 central_header[MZ_ZIP_CENTRAL_DIR_HEADER_SIZE]; - size_t orig_central_dir_size; - mz_zip_internal_state *pState; - void *pBuf; const mz_uint8 *pSrc_central_header; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - if (NULL == (pSrc_central_header = mz_zip_reader_get_cdh(pSource_zip, file_index))) - return MZ_FALSE; - pState = pZip->m_pState; - - num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip); - - // no zip64 support yet - if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - cur_src_file_ofs = MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS); - cur_dst_file_ofs = pZip->m_archive_size; - - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - if (MZ_READ_LE32(pLocal_header) != MZ_ZIP_LOCAL_DIR_HEADER_SIG) - return MZ_FALSE; - cur_src_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - if (!mz_zip_writer_write_zeros(pZip, cur_dst_file_ofs, num_alignment_padding_bytes)) - return MZ_FALSE; - cur_dst_file_ofs += num_alignment_padding_bytes; - local_dir_header_ofs = cur_dst_file_ofs; - if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); } - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pLocal_header, MZ_ZIP_LOCAL_DIR_HEADER_SIZE) != MZ_ZIP_LOCAL_DIR_HEADER_SIZE) - return MZ_FALSE; - cur_dst_file_ofs += MZ_ZIP_LOCAL_DIR_HEADER_SIZE; - - n = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_EXTRA_LEN_OFS); - comp_bytes_remaining = n + MZ_READ_LE32(pSrc_central_header + MZ_ZIP_CDH_COMPRESSED_SIZE_OFS); - - if (NULL == (pBuf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, (size_t)MZ_MAX(sizeof(mz_uint32) * 4, MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining))))) - return MZ_FALSE; - - while (comp_bytes_remaining) - { - n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, comp_bytes_remaining); - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_src_file_ofs += n; - - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - cur_dst_file_ofs += n; - - comp_bytes_remaining -= n; - } - - bit_flags = MZ_READ_LE16(pLocal_header + MZ_ZIP_LDH_BIT_FLAG_OFS); - if (bit_flags & 8) - { - // Copy data descriptor - if (pSource_zip->m_pRead(pSource_zip->m_pIO_opaque, cur_src_file_ofs, pBuf, sizeof(mz_uint32) * 4) != sizeof(mz_uint32) * 4) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - n = sizeof(mz_uint32) * ((MZ_READ_LE32(pBuf) == 0x08074b50) ? 4 : 3); - if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_dst_file_ofs, pBuf, n) != n) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - return MZ_FALSE; - } - - cur_src_file_ofs += n; - cur_dst_file_ofs += n; - } - pZip->m_pFree(pZip->m_pAlloc_opaque, pBuf); - - // no zip64 support yet - if (cur_dst_file_ofs > 0xFFFFFFFF) - return MZ_FALSE; - - orig_central_dir_size = pState->m_central_dir.m_size; - - memcpy(central_header, pSrc_central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE); - MZ_WRITE_LE32(central_header + MZ_ZIP_CDH_LOCAL_HEADER_OFS, local_dir_header_ofs); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, central_header, MZ_ZIP_CENTRAL_DIR_HEADER_SIZE)) - return MZ_FALSE; - - n = MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_FILENAME_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_EXTRA_LEN_OFS) + MZ_READ_LE16(pSrc_central_header + MZ_ZIP_CDH_COMMENT_LEN_OFS); - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir, pSrc_central_header + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE, n)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - if (pState->m_central_dir.m_size > 0xFFFFFFFF) - return MZ_FALSE; - n = (mz_uint32)orig_central_dir_size; - if (!mz_zip_array_push_back(pZip, &pState->m_central_dir_offsets, &n, 1)) - { - mz_zip_array_resize(pZip, &pState->m_central_dir, orig_central_dir_size, MZ_FALSE); - return MZ_FALSE; - } - - pZip->m_total_files++; - pZip->m_archive_size = cur_dst_file_ofs; - - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_archive(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_uint64 central_dir_ofs, central_dir_size; - mz_uint8 hdr[MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE]; - - if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING)) - return MZ_FALSE; - - pState = pZip->m_pState; - - // no zip64 support yet - if ((pZip->m_total_files > 0xFFFF) || ((pZip->m_archive_size + pState->m_central_dir.m_size + MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIZE) > 0xFFFFFFFF)) - return MZ_FALSE; - - central_dir_ofs = 0; - central_dir_size = 0; - if (pZip->m_total_files) - { - // Write central directory - central_dir_ofs = pZip->m_archive_size; - central_dir_size = pState->m_central_dir.m_size; - pZip->m_central_directory_file_ofs = central_dir_ofs; - if (pZip->m_pWrite(pZip->m_pIO_opaque, central_dir_ofs, pState->m_central_dir.m_p, (size_t)central_dir_size) != central_dir_size) - return MZ_FALSE; - pZip->m_archive_size += central_dir_size; - } - - // Write end of central directory record - MZ_CLEAR_OBJ(hdr); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_SIG_OFS, MZ_ZIP_END_OF_CENTRAL_DIR_HEADER_SIG); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_NUM_ENTRIES_ON_DISK_OFS, pZip->m_total_files); - MZ_WRITE_LE16(hdr + MZ_ZIP_ECDH_CDIR_TOTAL_ENTRIES_OFS, pZip->m_total_files); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_SIZE_OFS, central_dir_size); - MZ_WRITE_LE32(hdr + MZ_ZIP_ECDH_CDIR_OFS_OFS, central_dir_ofs); - - if (pZip->m_pWrite(pZip->m_pIO_opaque, pZip->m_archive_size, hdr, sizeof(hdr)) != sizeof(hdr)) - return MZ_FALSE; -#ifndef MINIZ_NO_STDIO - if ((pState->m_pFile) && (MZ_FFLUSH(pState->m_pFile) == EOF)) - return MZ_FALSE; -#endif // #ifndef MINIZ_NO_STDIO - - pZip->m_archive_size += sizeof(hdr); - - pZip->m_zip_mode = MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_finalize_heap_archive(mz_zip_archive *pZip, void **pBuf, size_t *pSize) -{ - if ((!pZip) || (!pZip->m_pState) || (!pBuf) || (!pSize)) - return MZ_FALSE; - if (pZip->m_pWrite != mz_zip_heap_write_func) - return MZ_FALSE; - if (!mz_zip_writer_finalize_archive(pZip)) - return MZ_FALSE; - - *pBuf = pZip->m_pState->m_pMem; - *pSize = pZip->m_pState->m_mem_size; - pZip->m_pState->m_pMem = NULL; - pZip->m_pState->m_mem_size = pZip->m_pState->m_mem_capacity = 0; - return MZ_TRUE; -} - -mz_bool mz_zip_writer_end(mz_zip_archive *pZip) -{ - mz_zip_internal_state *pState; - mz_bool status = MZ_TRUE; - if ((!pZip) || (!pZip->m_pState) || (!pZip->m_pAlloc) || (!pZip->m_pFree) || ((pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) && (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING_HAS_BEEN_FINALIZED))) - return MZ_FALSE; - - pState = pZip->m_pState; - pZip->m_pState = NULL; - mz_zip_array_clear(pZip, &pState->m_central_dir); - mz_zip_array_clear(pZip, &pState->m_central_dir_offsets); - mz_zip_array_clear(pZip, &pState->m_sorted_central_dir_offsets); - -#ifndef MINIZ_NO_STDIO - if (pState->m_pFile) - { - MZ_FCLOSE(pState->m_pFile); - pState->m_pFile = NULL; - } -#endif // #ifndef MINIZ_NO_STDIO - - if ((pZip->m_pWrite == mz_zip_heap_write_func) && (pState->m_pMem)) - { - pZip->m_pFree(pZip->m_pAlloc_opaque, pState->m_pMem); - pState->m_pMem = NULL; - } - - pZip->m_pFree(pZip->m_pAlloc_opaque, pState); - pZip->m_zip_mode = MZ_ZIP_MODE_INVALID; - return status; -} - -#ifndef MINIZ_NO_STDIO -mz_bool mz_zip_add_mem_to_archive_file_in_place(const char *pZip_filename, const char *pArchive_name, const void *pBuf, size_t buf_size, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags) -{ - mz_bool status, created_new_archive = MZ_FALSE; - mz_zip_archive zip_archive; - struct MZ_FILE_STAT_STRUCT file_stat; - MZ_CLEAR_OBJ(zip_archive); - if ((int)level_and_flags < 0) - level_and_flags = MZ_DEFAULT_LEVEL; - if ((!pZip_filename) || (!pArchive_name) || ((buf_size) && (!pBuf)) || ((comment_size) && (!pComment)) || ((level_and_flags & 0xF) > MZ_UBER_COMPRESSION)) - return MZ_FALSE; - if (!mz_zip_writer_validate_archive_name(pArchive_name)) - return MZ_FALSE; - if (MZ_FILE_STAT(pZip_filename, &file_stat) != 0) - { - // Create a new archive. - if (!mz_zip_writer_init_file(&zip_archive, pZip_filename, 0)) - return MZ_FALSE; - created_new_archive = MZ_TRUE; - } - else - { - // Append to an existing archive. - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, level_and_flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return MZ_FALSE; - if (!mz_zip_writer_init_from_reader(&zip_archive, pZip_filename)) - { - mz_zip_reader_end(&zip_archive); - return MZ_FALSE; - } - } - status = mz_zip_writer_add_mem_ex(&zip_archive, pArchive_name, pBuf, buf_size, pComment, comment_size, level_and_flags, 0, 0); - // Always finalize, even if adding failed for some reason, so we have a valid central directory. (This may not always succeed, but we can try.) - if (!mz_zip_writer_finalize_archive(&zip_archive)) - status = MZ_FALSE; - if (!mz_zip_writer_end(&zip_archive)) - status = MZ_FALSE; - if ((!status) && (created_new_archive)) - { - // It's a new archive and something went wrong, so just delete it. - int ignoredStatus = MZ_DELETE_FILE(pZip_filename); - (void)ignoredStatus; - } - return status; -} - -void *mz_zip_extract_archive_file_to_heap(const char *pZip_filename, const char *pArchive_name, size_t *pSize, mz_uint flags) -{ - int file_index; - mz_zip_archive zip_archive; - void *p = NULL; - - if (pSize) - *pSize = 0; - - if ((!pZip_filename) || (!pArchive_name)) - return NULL; - - MZ_CLEAR_OBJ(zip_archive); - if (!mz_zip_reader_init_file(&zip_archive, pZip_filename, flags | MZ_ZIP_FLAG_DO_NOT_SORT_CENTRAL_DIRECTORY)) - return NULL; - - if ((file_index = mz_zip_reader_locate_file(&zip_archive, pArchive_name, NULL, flags)) >= 0) - p = mz_zip_reader_extract_to_heap(&zip_archive, file_index, pSize, flags); - - mz_zip_reader_end(&zip_archive); - return p; -} - -#endif // #ifndef MINIZ_NO_STDIO - -#endif // #ifndef MINIZ_NO_ARCHIVE_WRITING_APIS - -#endif // #ifndef MINIZ_NO_ARCHIVE_APIS - -#ifdef __cplusplus -} -#endif - -#endif // MINIZ_HEADER_FILE_ONLY - -/* - This is free and unencumbered software released into the public domain. - - Anyone is free to copy, modify, publish, use, compile, sell, or - distribute this software, either in source code form or as a compiled - binary, for any purpose, commercial or non-commercial, and by any - means. - - In jurisdictions that recognize copyright laws, the author or authors - of this software dedicate any and all copyright interest in the - software to the public domain. We make this dedication for the benefit - of the public at large and to the detriment of our heirs and - successors. We intend this dedication to be an overt act of - relinquishment in perpetuity of all present and future rights to this - software under copyright law. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR - OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - OTHER DEALINGS IN THE SOFTWARE. - - For more information, please refer to -*/ diff -Nru leanify-0.4.3/lib/miniz/miniz.h leanify-0.4.3+git20181014/lib/miniz/miniz.h --- leanify-0.4.3/lib/miniz/miniz.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/miniz/miniz.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -#ifndef MINIZ_H -#define MINIZ_H - -#define MINIZ_HEADER_FILE_ONLY - -#include "miniz.c" - -#endif \ No newline at end of file diff -Nru leanify-0.4.3/lib/mozjpeg/jaricom.c leanify-0.4.3+git20181014/lib/mozjpeg/jaricom.c --- leanify-0.4.3/lib/mozjpeg/jaricom.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jaricom.c 2017-08-01 20:51:09.000000000 +0000 @@ -1,9 +1,12 @@ /* * jaricom.c * + * This file was part of the Independent JPEG Group's software: * Developed 1997-2009 by Guido Vollbeding. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2015, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains probability estimation tables for common use in * arithmetic entropy encoding and decoding routines. @@ -18,7 +21,7 @@ #include "jpeglib.h" /* The following #define specifies the packing of the four components - * into the compact INT32 representation. + * into the compact JLONG representation. * Note that this formula must match the actual arithmetic encoder * and decoder implementation. The implementation has to be changed * if this formula is changed. @@ -26,9 +29,9 @@ * implementation (jbig_tab.c). */ -#define V(i,a,b,c,d) (((INT32)a << 16) | ((INT32)c << 8) | ((INT32)d << 7) | b) +#define V(i,a,b,c,d) (((JLONG)a << 16) | ((JLONG)c << 8) | ((JLONG)d << 7) | b) -const INT32 jpeg_aritab[113+1] = { +const JLONG jpeg_aritab[113+1] = { /* * Index, Qe_Value, Next_Index_LPS, Next_Index_MPS, Switch_MPS */ diff -Nru leanify-0.4.3/lib/mozjpeg/jcapimin.c leanify-0.4.3+git20181014/lib/mozjpeg/jcapimin.c --- leanify-0.4.3/lib/mozjpeg/jcapimin.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcapimin.c 2017-08-01 20:51:09.000000000 +0000 @@ -51,8 +51,8 @@ * complain here. */ { - struct jpeg_error_mgr * err = cinfo->err; - void * client_data = cinfo->client_data; /* ignore Purify complaint here */ + struct jpeg_error_mgr *err = cinfo->err; + void *client_data = cinfo->client_data; /* ignore Purify complaint here */ MEMZERO(cinfo, sizeof(struct jpeg_compress_struct)); cinfo->err = err; cinfo->client_data = client_data; @@ -145,8 +145,8 @@ jpeg_suppress_tables (j_compress_ptr cinfo, boolean suppress) { int i; - JQUANT_TBL * qtbl; - JHUFF_TBL * htbl; + JQUANT_TBL *qtbl; + JHUFF_TBL *htbl; for (i = 0; i < NUM_QUANT_TBLS; i++) { if ((qtbl = cinfo->quant_tbl_ptrs[i]) != NULL) diff -Nru leanify-0.4.3/lib/mozjpeg/jcarith.c leanify-0.4.3+git20181014/lib/mozjpeg/jcarith.c --- leanify-0.4.3/lib/mozjpeg/jcarith.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcarith.c 2017-08-01 20:51:09.000000000 +0000 @@ -25,10 +25,10 @@ typedef struct { struct jpeg_entropy_encoder pub; /* public fields */ - INT32 c; /* C register, base of coding interval, layout as in sec. D.1.3 */ - INT32 a; /* A register, normalized size of coding interval */ - INT32 sc; /* counter for stacked 0xFF values which might overflow */ - INT32 zc; /* counter for pending 0x00 output values which might * + JLONG c; /* C register, base of coding interval, layout as in sec. D.1.3 */ + JLONG a; /* A register, normalized size of coding interval */ + JLONG sc; /* counter for stacked 0xFF values which might overflow */ + JLONG zc; /* counter for pending 0x00 output values which might * * be discarded at the end ("Pacman" termination) */ int ct; /* bit shift counter, determines when next byte will be written */ int buffer; /* buffer for most recent output byte != 0xFF */ @@ -40,14 +40,14 @@ int next_restart_num; /* next restart number to write (0-7) */ /* Pointers to statistics areas (these workspaces have image lifespan) */ - unsigned char * dc_stats[NUM_ARITH_TBLS]; - unsigned char * ac_stats[NUM_ARITH_TBLS]; + unsigned char *dc_stats[NUM_ARITH_TBLS]; + unsigned char *ac_stats[NUM_ARITH_TBLS]; /* Statistics bin for coding with fixed probability 0.5 */ unsigned char fixed_bin[4]; } arith_entropy_encoder; -typedef arith_entropy_encoder * arith_entropy_ptr; +typedef arith_entropy_encoder *arith_entropy_ptr; /* The following two definitions specify the allocation chunk size * for the statistics area. @@ -97,8 +97,8 @@ #define CALCULATE_SPECTRAL_CONDITIONING */ -/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. - * We assume that int right shift is unsigned if INT32 right shift is, +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than JLONG. + * We assume that int right shift is unsigned if JLONG right shift is, * which should be safe. */ @@ -118,7 +118,7 @@ emit_byte (int val, j_compress_ptr cinfo) /* Write next output byte; we do not support suspension in this module. */ { - struct jpeg_destination_mgr * dest = cinfo->dest; + struct jpeg_destination_mgr *dest = cinfo->dest; /* Do not emit bytes during trellis passes */ if (cinfo->master->trellis_passes) @@ -139,7 +139,7 @@ finish_pass (j_compress_ptr cinfo) { arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; - INT32 temp; + JLONG temp; /* Section D.1.8: Termination of encoding */ @@ -226,7 +226,7 @@ { register arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; register unsigned char nl, nm; - register INT32 qe, temp; + register JLONG qe, temp; register int sv; /* Fetch values from our compact representation of Table D.2: @@ -326,7 +326,7 @@ { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; int ci; - jpeg_component_info * compptr; + jpeg_component_info *compptr; finish_pass(cinfo); @@ -686,7 +686,7 @@ encode_mcu (j_compress_ptr cinfo, JBLOCKROW *MCU_data) { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; - jpeg_component_info * compptr; + jpeg_component_info *compptr; JBLOCKROW block; unsigned char *st; int blkn, ci, tbl, k, ke; @@ -829,7 +829,7 @@ { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; int ci, tbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; boolean progressive_mode; if (gather_statistics) diff -Nru leanify-0.4.3/lib/mozjpeg/jchuff.c leanify-0.4.3+git20181014/lib/mozjpeg/jchuff.c --- leanify-0.4.3/lib/mozjpeg/jchuff.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jchuff.c 2017-08-01 20:51:09.000000000 +0000 @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2014 D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2009-2011, 2014-2016, D. R. Commander. + * Copyright (C) 2015, Matthieu Darbois. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains Huffman entropy encoding routines. * @@ -19,7 +21,8 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" -#include "jchuff.h" /* Declarations shared with jcphuff.c */ +#include "jsimd.h" +#include "jconfigint.h" #include /* @@ -99,23 +102,25 @@ int next_restart_num; /* next restart number to write (0-7) */ /* Pointers to derived tables (these workspaces have image lifespan) */ - c_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - c_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl *dc_derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl *ac_derived_tbls[NUM_HUFF_TBLS]; #ifdef ENTROPY_OPT_SUPPORTED /* Statistics tables for optimization */ - long * dc_count_ptrs[NUM_HUFF_TBLS]; - long * ac_count_ptrs[NUM_HUFF_TBLS]; + long *dc_count_ptrs[NUM_HUFF_TBLS]; + long *ac_count_ptrs[NUM_HUFF_TBLS]; #endif + + int simd; } huff_entropy_encoder; -typedef huff_entropy_encoder * huff_entropy_ptr; +typedef huff_entropy_encoder *huff_entropy_ptr; /* Working state while writing an MCU. * This struct contains all the fields that are needed by subroutines. */ typedef struct { - JOCTET * next_output_byte; /* => next byte to write in buffer */ + JOCTET *next_output_byte; /* => next byte to write in buffer */ size_t free_in_buffer; /* # of byte spaces remaining in buffer */ savable_state cur; /* Current bit buffer & DC state */ j_compress_ptr cinfo; /* dump_buffer needs access to this */ @@ -143,7 +148,7 @@ { huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; int ci, dctbl, actbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; if (gather_statistics) { #ifdef ENTROPY_OPT_SUPPORTED @@ -157,6 +162,8 @@ entropy->pub.finish_pass = finish_pass_huff; } + entropy->simd = jsimd_can_huff_encode_one_block(); + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; dctbl = compptr->dc_tbl_no; @@ -213,7 +220,7 @@ GLOBAL(void) jpeg_make_c_derived_tbl (j_compress_ptr cinfo, boolean isDC, int tblno, - c_derived_tbl ** pdtbl) + c_derived_tbl **pdtbl) { JHUFF_TBL *htbl; c_derived_tbl *dtbl; @@ -268,7 +275,7 @@ /* code is now 1 more than the last code used for codelength si; but * it must still fit in si bits, since no code is allowed to be all ones. */ - if (((INT32) code) >= (((INT32) 1) << si)) + if (((JLONG) code) >= (((JLONG) 1) << si)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); code <<= 1; si++; @@ -311,10 +318,10 @@ LOCAL(boolean) -dump_buffer (working_state * state) +dump_buffer (working_state *state) /* Empty the output buffer; return TRUE if successful, FALSE if must suspend */ { - struct jpeg_destination_mgr * dest = state->cinfo->dest; + struct jpeg_destination_mgr *dest = state->cinfo->dest; if (! (*dest->empty_output_buffer) (state->cinfo)) return FALSE; @@ -376,7 +383,11 @@ } \ } -#if __WORDSIZE==64 || defined(_WIN64) +#if !defined(_WIN32) && !defined(SIZEOF_SIZE_T) +#error Cannot determine word size +#endif + +#if SIZEOF_SIZE_T==8 || defined(_WIN64) #define EMIT_BITS(code, size) { \ CHECKBUF47() \ @@ -384,7 +395,7 @@ } #define EMIT_CODE(code, size) { \ - temp2 &= (((INT32) 1)<ehufco[nbits]; size = dctbl->ehufsi[nbits]; - PUT_BITS(code, size) - CHECKBUF15() + EMIT_BITS(code, size) /* Mask off any extra bits in code */ - temp2 &= (((INT32) 1)<entropy; working_state state; int blkn, ci; - jpeg_component_info * compptr; + jpeg_component_info *compptr; /* Load up working state */ state.next_output_byte = cinfo->dest->next_output_byte; @@ -636,16 +662,30 @@ } /* Encode the MCU data blocks */ - for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { - ci = cinfo->MCU_membership[blkn]; - compptr = cinfo->cur_comp_info[ci]; - if (! encode_one_block(&state, - MCU_data[blkn][0], state.cur.last_dc_val[ci], - entropy->dc_derived_tbls[compptr->dc_tbl_no], - entropy->ac_derived_tbls[compptr->ac_tbl_no])) - return FALSE; - /* Update last_dc_val */ - state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + if (entropy->simd) { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block_simd(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } + } else { + for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { + ci = cinfo->MCU_membership[blkn]; + compptr = cinfo->cur_comp_info[ci]; + if (! encode_one_block(&state, + MCU_data[blkn][0], state.cur.last_dc_val[ci], + entropy->dc_derived_tbls[compptr->dc_tbl_no], + entropy->ac_derived_tbls[compptr->ac_tbl_no])) + return FALSE; + /* Update last_dc_val */ + state.cur.last_dc_val[ci] = MCU_data[blkn][0][0]; + } } /* Completed MCU, so update state */ @@ -788,7 +828,7 @@ { huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; int blkn, ci; - jpeg_component_info * compptr; + jpeg_component_info *compptr; /* Take care of restart intervals if needed */ if (cinfo->restart_interval) { @@ -844,7 +884,7 @@ */ GLOBAL(void) -jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]) +jpeg_gen_optimal_table (j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]) { #define MAX_CLEN 32 /* assumed maximum initial code length */ UINT8 bits[MAX_CLEN+1]; /* bits[k] = # of symbols with code length k */ @@ -989,7 +1029,7 @@ { huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; int ci, dctbl, actbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; JHUFF_TBL **htblptr; boolean did_dc[NUM_HUFF_TBLS]; boolean did_ac[NUM_HUFF_TBLS]; diff -Nru leanify-0.4.3/lib/mozjpeg/jchuff.h leanify-0.4.3+git20181014/lib/mozjpeg/jchuff.h --- leanify-0.4.3/lib/mozjpeg/jchuff.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jchuff.h 2017-08-01 20:51:09.000000000 +0000 @@ -41,7 +41,7 @@ /* Generate an optimal table definition given the specified counts */ EXTERN(void) jpeg_gen_optimal_table - (j_compress_ptr cinfo, JHUFF_TBL * htbl, long freq[]); + (j_compress_ptr cinfo, JHUFF_TBL *htbl, long freq[]); EXTERN(void) quantize_trellis (j_compress_ptr cinfo, c_derived_tbl *dctbl, c_derived_tbl *actbl, JBLOCKROW coef_blocks, JBLOCKROW src, JDIMENSION num_blocks, diff -Nru leanify-0.4.3/lib/mozjpeg/jcmarker.c leanify-0.4.3+git20181014/lib/mozjpeg/jcmarker.c --- leanify-0.4.3/lib/mozjpeg/jcmarker.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcmarker.c 2017-08-01 20:51:09.000000000 +0000 @@ -6,7 +6,8 @@ * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: * Copyright (C) 2010, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains routines to write JPEG datastream markers. */ @@ -93,7 +94,7 @@ unsigned int last_restart_interval; /* last DRI value emitted; 0 after SOI */ } my_marker_writer; -typedef my_marker_writer * my_marker_ptr; +typedef my_marker_writer *my_marker_ptr; /* @@ -112,7 +113,7 @@ emit_byte (j_compress_ptr cinfo, int val) /* Emit a byte */ { - struct jpeg_destination_mgr * dest = cinfo->dest; + struct jpeg_destination_mgr *dest = cinfo->dest; *(dest->next_output_byte)++ = (JOCTET) val; if (--dest->free_in_buffer == 0) { @@ -149,7 +150,7 @@ /* Emit a DQT marker */ /* Returns the precision used (0 = 8bits, 1 = 16bits) for baseline checking */ { - JQUANT_TBL * qtbl = cinfo->quant_tbl_ptrs[index]; + JQUANT_TBL *qtbl = cinfo->quant_tbl_ptrs[index]; int prec; int i; @@ -254,7 +255,7 @@ emit_dht (j_compress_ptr cinfo, int index, boolean is_ac) /* Emit a DHT marker */ { - JHUFF_TBL * htbl; + JHUFF_TBL *htbl; int length, i; if (is_ac) { @@ -681,11 +682,11 @@ */ prec = emit_multi_dqt(cinfo); if (prec == -1) { - prec = 0; - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - prec += emit_dqt(cinfo, compptr->quant_tbl_no); - } + prec = 0; + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + prec += emit_dqt(cinfo, compptr->quant_tbl_no); + } } /* now prec is nonzero iff there are any 16-bit quant tables. */ @@ -750,17 +751,17 @@ * Note that emit_dht() suppresses any duplicate tables. */ if (!emit_multi_dht(cinfo)) { - for (i = 0; i < cinfo->comps_in_scan; i++) { - compptr = cinfo->cur_comp_info[i]; - /* DC needs no table for refinement scan */ - if (cinfo->Ss == 0 && cinfo->Ah == 0) - emit_dht(cinfo, compptr->dc_tbl_no, FALSE); - /* AC needs no table when not present */ - if (cinfo->Se) - emit_dht(cinfo, compptr->ac_tbl_no, TRUE); - } + for (i = 0; i < cinfo->comps_in_scan; i++) { + compptr = cinfo->cur_comp_info[i]; + /* DC needs no table for refinement scan */ + if (cinfo->Ss == 0 && cinfo->Ah == 0) + emit_dht(cinfo, compptr->dc_tbl_no, FALSE); + /* AC needs no table when not present */ + if (cinfo->Se) + emit_dht(cinfo, compptr->ac_tbl_no, TRUE); } } + } /* Emit DRI if required --- note that DRI value could change for each scan. * We avoid wasting space with unnecessary DRIs, however. diff -Nru leanify-0.4.3/lib/mozjpeg/jcmaster.c leanify-0.4.3+git20181014/lib/mozjpeg/jcmaster.c --- leanify-0.4.3/lib/mozjpeg/jcmaster.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcmaster.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,7 @@ * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 2003-2010 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2014, D. R. Commander. + * Copyright (C) 2010, 2016, D. R. Commander. * mozjpeg Modifications: * Copyright (C) 2014, Mozilla Corporation. * For conditions of distribution and use, see the accompanying README file. @@ -20,11 +20,12 @@ #include "jinclude.h" #include "jpeglib.h" #include "jpegcomp.h" +#include "jconfigint.h" #include "jmemsys.h" #include "jcmaster.h" -/* + /* * Support routines that do various essential calculations. */ @@ -149,12 +150,12 @@ * determine whether it uses progressive JPEG, and set cinfo->progressive_mode. */ { - const jpeg_scan_info * scanptr; + const jpeg_scan_info *scanptr; int scanno, ncomps, ci, coefi, thisi; int Ss, Se, Ah, Al; boolean component_sent[MAX_COMPONENTS]; #ifdef C_PROGRESSIVE_SUPPORTED - int * last_bitpos_ptr; + int *last_bitpos_ptr; int last_bitpos[MAX_COMPONENTS][DCTSIZE2]; /* -1 until that coefficient has been seen; then last Al for it */ #endif @@ -316,7 +317,7 @@ } else if (cinfo->scan_info != NULL) { /* Prepare for current scan --- the script is already validated */ - const jpeg_scan_info * scanptr = cinfo->scan_info + master->scan_number; + const jpeg_scan_info *scanptr = cinfo->scan_info + master->scan_number; cinfo->comps_in_scan = scanptr->comps_in_scan; for (ci = 0; ci < scanptr->comps_in_scan; ci++) { @@ -339,7 +340,7 @@ (6 * cinfo->master->Al_max_chroma + 4) && master->scan_number < cinfo->num_scans) cinfo->Al = master->best_Al_chroma; - } + } /* save value for later retrieval during printout of scans */ master->actual_Al[master->scan_number] = cinfo->Al; } @@ -517,7 +518,7 @@ master->saved_dest = cinfo->dest; cinfo->dest = NULL; master->scan_size[master->scan_number] = 0; - jpeg_mem_dest(cinfo, &master->scan_buffer[master->scan_number], &master->scan_size[master->scan_number]); + jpeg_mem_dest_internal(cinfo, &master->scan_buffer[master->scan_number], &master->scan_size[master->scan_number], JPOOL_IMAGE); (*cinfo->dest->init_destination)(cinfo); } (*cinfo->entropy->start_pass) (cinfo, FALSE); @@ -829,9 +830,9 @@ if (cinfo->master->trellis_quant) master->pass_type = trellis_pass; else { - master->pass_type = output_pass; - if (! cinfo->optimize_coding) - master->scan_number++; + master->pass_type = output_pass; + if (! cinfo->optimize_coding) + master->scan_number++; } break; case huff_opt_pass: @@ -869,7 +870,7 @@ if (q > 254) q = 254; if (q < 1) q = 1; cinfo->quant_tbl_ptrs[i]->quantval[j] = q; - } + } } } } @@ -909,7 +910,7 @@ cinfo->num_scans = 1; } - if (cinfo->progressive_mode && !cinfo->arith_code) /* TEMPORARY HACK ??? */ + if (cinfo->progressive_mode && !cinfo->arith_code) /* TEMPORARY HACK ??? */ cinfo->optimize_coding = TRUE; /* assume default tables no good for progressive mode */ /* Initialize my private state */ @@ -929,6 +930,8 @@ master->total_passes = cinfo->num_scans * 2; else master->total_passes = cinfo->num_scans; + + master->jpeg_version = PACKAGE_NAME " version " VERSION " (build " BUILD ")"; master->pass_number_scan_opt_base = 0; if (cinfo->master->trellis_quant) { @@ -941,7 +944,7 @@ ((cinfo->master->use_scans_in_trellis) ? 2 : 1) * cinfo->num_components * cinfo->master->trellis_num_loops + 1; master->total_passes += master->pass_number_scan_opt_base; - } +} if (cinfo->master->optimize_scans) { int i; diff -Nru leanify-0.4.3/lib/mozjpeg/jcmaster.h leanify-0.4.3+git20181014/lib/mozjpeg/jcmaster.h --- leanify-0.4.3/lib/mozjpeg/jcmaster.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcmaster.h 2017-08-01 20:51:09.000000000 +0000 @@ -42,6 +42,15 @@ int best_Al_chroma; /* best value for Al found in scan search (luma) */ boolean interleave_chroma_dc; /* indicate whether to interleave chroma DC scans */ struct jpeg_destination_mgr * saved_dest; /* saved value of cinfo->dest */ + + /* + * This is here so we can add libjpeg-turbo version/build information to the + * global string table without introducing a new global symbol. Adding this + * information to the global string table allows one to examine a binary + * object and determine which version of libjpeg-turbo it was built from or + * linked against. + */ + const char *jpeg_version; } my_comp_master; typedef my_comp_master * my_master_ptr; diff -Nru leanify-0.4.3/lib/mozjpeg/jcomapi.c leanify-0.4.3+git20181014/lib/mozjpeg/jcomapi.c --- leanify-0.4.3/lib/mozjpeg/jcomapi.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcomapi.c 2017-08-01 20:51:09.000000000 +0000 @@ -2,10 +2,11 @@ * jcomapi.c * * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1997, Thomas G. Lane.0 + * Copyright (C) 1994-1997, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code relevant * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains application interface routines that are used for both * compression and decompression. diff -Nru leanify-0.4.3/lib/mozjpeg/jconfig.h leanify-0.4.3+git20181014/lib/mozjpeg/jconfig.h --- leanify-0.4.3/lib/mozjpeg/jconfig.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jconfig.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,12 +1,18 @@ -#define JPEG_LIB_VERSION 80 +/* Version ID for the JPEG library. + * Might be useful for tests like "#if JPEG_LIB_VERSION >= 60". + */ +#define JPEG_LIB_VERSION 62 /* Version 6b */ /* libjpeg-turbo version */ #define LIBJPEG_TURBO_VERSION 0 -/* Support in-memory source/destination managers */ -#define MEM_SRCDST_SUPPORTED +/* libjpeg-turbo version in integer form */ +#define LIBJPEG_TURBO_VERSION_NUMBER 0 +/* Support arithmetic encoding */ #define C_ARITH_CODING_SUPPORTED + +/* Support arithmetic decoding */ #define D_ARITH_CODING_SUPPORTED /* @@ -20,31 +26,53 @@ #define BITS_IN_JSAMPLE 8 /* use 8 or 12 */ -/* Does your compiler support function prototypes? - * (If not, you also need to use ansi2knr, see install.txt) - */ -#define HAVE_PROTOTYPES - -/* Compiler supports 'unsigned char'. */ -#define HAVE_UNSIGNED_CHAR +/* Define to 1 if you have the header file. */ +#define HAVE_LOCALE_H 1 -/* Compiler supports 'unsigned short'. */ -#define HAVE_UNSIGNED_SHORT +/* Define to 1 if you have the header file. */ +#define HAVE_STDDEF_H 1 -/* Define this if your system has an ANSI-conforming file. - */ -#define HAVE_STDDEF_H +/* Define to 1 if you have the header file. */ +#define HAVE_STDLIB_H 1 -/* Define this if your system has an ANSI-conforming file. - */ -#define HAVE_STDLIB_H +/* Define to 1 if the system has the type `unsigned char'. */ +#define HAVE_UNSIGNED_CHAR 1 -/* Define "boolean" as unsigned char, not int, on Windows systems. - */ -#ifdef _WIN32 -#ifndef __RPCNDR_H__ /* don't conflict if rpcndr.h already read */ -typedef unsigned char boolean; -#endif -#define HAVE_BOOLEAN /* prevent jmorecfg.h from redefining it */ +/* Define to 1 if the system has the type `unsigned short'. */ +#define HAVE_UNSIGNED_SHORT 1 + +/* Compiler does not support pointers to undefined structures. */ +#undef INCOMPLETE_TYPES_BROKEN + +/* Support in-memory source/destination managers */ +#define MEM_SRCDST_SUPPORTED + +/* Define if you have BSD-like bzero and bcopy in rather than + memset/memcpy in . */ +#undef NEED_BSD_STRINGS + +/* Define if you need to include to get size_t. */ +#undef NEED_SYS_TYPES_H + +/* Define if your (broken) compiler shifts signed values as if they were + unsigned. */ +#undef RIGHT_SHIFT_IS_UNSIGNED + +/* Use accelerated SIMD routines. */ +#undef WITH_SIMD + +/* Define to 1 if type `char' is unsigned and you are not using gcc. */ +#ifndef __CHAR_UNSIGNED__ +# undef __CHAR_UNSIGNED__ #endif +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `unsigned int' if does not define. */ +#undef size_t + +/* The size of `size_t', as computed by sizeof. */ +#ifdef __SIZEOF_SIZE_T__ +#define SIZEOF_SIZE_T __SIZEOF_SIZE_T__ +#endif diff -Nru leanify-0.4.3/lib/mozjpeg/jconfigint.h leanify-0.4.3+git20181014/lib/mozjpeg/jconfigint.h --- leanify-0.4.3/lib/mozjpeg/jconfigint.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jconfigint.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,3 +1,7 @@ +/* libjpeg-turbo build number */ +#define BUILD + +/* How to obtain function inlining. */ #ifndef INLINE #if defined(__GNUC__) #define INLINE inline __attribute__((always_inline)) @@ -6,4 +10,9 @@ #else #define INLINE #endif -#endif \ No newline at end of file +#endif +/* Define to the full name of this package. */ +#define PACKAGE_NAME + +/* Version number of package */ +#define VERSION \ No newline at end of file diff -Nru leanify-0.4.3/lib/mozjpeg/jcparam.c leanify-0.4.3+git20181014/lib/mozjpeg/jcparam.c --- leanify-0.4.3/lib/mozjpeg/jcparam.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcparam.c 2018-03-21 11:12:12.000000000 +0000 @@ -35,7 +35,7 @@ * are limited to 1..255 for JPEG baseline compatibility. */ { - JQUANT_TBL ** qtblptr; + JQUANT_TBL **qtblptr; int i; long temp; @@ -74,14 +74,14 @@ { /* JPEG Annex K */ - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 56, 68, 109, 103, 77, - 24, 35, 55, 64, 81, 104, 113, 92, - 49, 64, 78, 87, 103, 121, 120, 101, - 72, 92, 95, 98, 112, 100, 103, 99 + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 56, 68, 109, 103, 77, + 24, 35, 55, 64, 81, 104, 113, 92, + 49, 64, 78, 87, 103, 121, 120, 101, + 72, 92, 95, 98, 112, 100, 103, 99 }, { /* flat @@ -181,14 +181,14 @@ { /* JPEG Annex K */ - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 }, { /* flat @@ -432,8 +432,8 @@ /* By default, do extra passes to optimize entropy coding */ cinfo->optimize_coding = TRUE; else - /* By default, don't do extra passes to optimize entropy coding */ - cinfo->optimize_coding = FALSE; + /* By default, don't do extra passes to optimize entropy coding */ + cinfo->optimize_coding = FALSE; #else /* By default, don't do extra passes to optimize entropy coding */ cinfo->optimize_coding = FALSE; @@ -563,7 +563,7 @@ GLOBAL(void) jpeg_set_colorspace (j_compress_ptr cinfo, J_COLOR_SPACE colorspace) { - jpeg_component_info * compptr; + jpeg_component_info *compptr; int ci; #define SET_COMP(index,id,hsamp,vsamp,quant,dctbl,actbl) \ @@ -645,7 +645,7 @@ #ifdef C_PROGRESSIVE_SUPPORTED LOCAL(jpeg_scan_info *) -fill_a_scan (jpeg_scan_info * scanptr, int ci, +fill_a_scan (jpeg_scan_info *scanptr, int ci, int Ss, int Se, int Ah, int Al) /* Support routine: generate one scan for specified component */ { @@ -676,7 +676,7 @@ } LOCAL(jpeg_scan_info *) -fill_scans (jpeg_scan_info * scanptr, int ncomps, +fill_scans (jpeg_scan_info *scanptr, int ncomps, int Ss, int Se, int Ah, int Al) /* Support routine: generate one scan for each component */ { @@ -695,7 +695,7 @@ } LOCAL(jpeg_scan_info *) -fill_dc_scans (jpeg_scan_info * scanptr, int ncomps, int Ah, int Al) +fill_dc_scans (jpeg_scan_info *scanptr, int ncomps, int Ah, int Al) /* Support routine: generate interleaved DC scan if possible, else N scans */ { int ci; @@ -853,7 +853,7 @@ { int ncomps; int nscans; - jpeg_scan_info * scanptr; + jpeg_scan_info *scanptr; if (cinfo->master->optimize_scans) { if (jpeg_search_progression(cinfo) == TRUE) @@ -864,24 +864,34 @@ if (cinfo->global_state != CSTATE_START) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - /* Figure space needed for script. Calculation must match code below! */ + /* Figure space needed for script. Calculation must match code below! */ ncomps = cinfo->num_components; if (ncomps == 3 && cinfo->jpeg_color_space == JCS_YCbCr) { /* Custom script for YCbCr color images. */ - nscans = 10; + if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { + if (cinfo->master->dc_scan_opt_mode == 0) { + nscans = 9; /* 1 DC scan for all components */ + } else if (cinfo->master->dc_scan_opt_mode == 1) { + nscans = 11; /* 1 DC scan for each component */ + } else { + nscans = 10; /* 1 DC scan for luminance and 1 DC scan for chroma */ + } + } else { + nscans = 10; /* 2 DC scans and 8 AC scans */ + } } else { /* All-purpose script for other color spaces. */ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { - if (ncomps > MAX_COMPS_IN_SCAN) + if (ncomps > MAX_COMPS_IN_SCAN) nscans = 5 * ncomps; /* 2 DC + 4 AC scans per component */ else nscans = 1 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ } else { if (ncomps > MAX_COMPS_IN_SCAN) - nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ - else - nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ - } + nscans = 6 * ncomps; /* 2 DC + 4 AC scans per component */ + else + nscans = 2 + 4 * ncomps; /* 2 DC scans; 4 AC scans per component */ + } } /* Allocate space for script. @@ -906,14 +916,16 @@ if (cinfo->master->compress_profile == JCP_MAX_COMPRESSION) { /* scan defined in jpeg_scan_rgb.txt in jpgcrush */ /* Initial DC scan */ - if (cinfo->master->dc_scan_opt_mode == 0) + if (cinfo->master->dc_scan_opt_mode == 0) { + /* 1 DC scan for all components */ scanptr = fill_dc_scans(scanptr, ncomps, 0, 0); - else if (cinfo->master->dc_scan_opt_mode == 1) { + } else if (cinfo->master->dc_scan_opt_mode == 1) { + /* 1 DC scan for each component */ scanptr = fill_a_scan(scanptr, 0, 0, 0, 0, 0); scanptr = fill_a_scan(scanptr, 1, 0, 0, 0, 0); scanptr = fill_a_scan(scanptr, 2, 0, 0, 0, 0); - } - else { + } else { + /* 1 DC scan for luminance and 1 DC scan for chroma */ scanptr = fill_dc_scans(scanptr, 1, 0, 0); scanptr = fill_a_scan_pair(scanptr, 1, 0, 0, 0, 0); } @@ -931,23 +943,23 @@ scanptr = fill_a_scan(scanptr, 2, 9, 63, 0, 0); } else { /* Initial DC scan */ - scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); - /* Initial AC scan: get some luma data out in a hurry */ - scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); - /* Chroma data is too small to be worth expending many scans on */ - scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); - scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); - /* Complete spectral selection for luma AC */ - scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); - /* Refine next bit of luma AC */ - scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); - /* Finish DC successive approximation */ - scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); - /* Finish AC successive approximation */ - scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); - scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); - /* Luma bottom bit comes last since it's usually largest scan */ - scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + /* Initial AC scan: get some luma data out in a hurry */ + scanptr = fill_a_scan(scanptr, 0, 1, 5, 0, 2); + /* Chroma data is too small to be worth expending many scans on */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 0, 1); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 0, 1); + /* Complete spectral selection for luma AC */ + scanptr = fill_a_scan(scanptr, 0, 6, 63, 0, 2); + /* Refine next bit of luma AC */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 2, 1); + /* Finish DC successive approximation */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + /* Finish AC successive approximation */ + scanptr = fill_a_scan(scanptr, 2, 1, 63, 1, 0); + scanptr = fill_a_scan(scanptr, 1, 1, 63, 1, 0); + /* Luma bottom bit comes last since it's usually largest scan */ + scanptr = fill_a_scan(scanptr, 0, 1, 63, 1, 0); } } else { /* All-purpose script for other color spaces. */ @@ -955,7 +967,7 @@ /* scan defined in jpeg_scan_bw.txt in jpgcrush */ /* DC component, no successive approximation */ scanptr = fill_dc_scans(scanptr, ncomps, 0, 0); - /* Successive approximation first pass */ + /* Successive approximation first pass */ scanptr = fill_scans(scanptr, ncomps, 1, 8, 0, 2); scanptr = fill_scans(scanptr, ncomps, 9, 63, 0, 2); /* Successive approximation second pass */ @@ -964,16 +976,16 @@ scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); } else { /* Successive approximation first pass */ - scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); - scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); - scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); - /* Successive approximation second pass */ - scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); - /* Successive approximation final pass */ - scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); - scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); - } + scanptr = fill_dc_scans(scanptr, ncomps, 0, 1); + scanptr = fill_scans(scanptr, ncomps, 1, 5, 0, 2); + scanptr = fill_scans(scanptr, ncomps, 6, 63, 0, 2); + /* Successive approximation second pass */ + scanptr = fill_scans(scanptr, ncomps, 1, 63, 2, 1); + /* Successive approximation final pass */ + scanptr = fill_dc_scans(scanptr, ncomps, 1, 0); + scanptr = fill_scans(scanptr, ncomps, 1, 63, 1, 0); } } +} #endif /* C_PROGRESSIVE_SUPPORTED */ diff -Nru leanify-0.4.3/lib/mozjpeg/jcphuff.c leanify-0.4.3+git20181014/lib/mozjpeg/jcphuff.c --- leanify-0.4.3/lib/mozjpeg/jcphuff.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jcphuff.c 2017-08-01 20:51:09.000000000 +0000 @@ -34,9 +34,9 @@ /* Bit-level coding status. * next_output_byte/free_in_buffer are local copies of cinfo->dest fields. */ - JOCTET * next_output_byte; /* => next byte to write in buffer */ + JOCTET *next_output_byte; /* => next byte to write in buffer */ size_t free_in_buffer; /* # of byte spaces remaining in buffer */ - INT32 put_buffer; /* current bit-accumulation buffer */ + size_t put_buffer; /* current bit-accumulation buffer */ int put_bits; /* # of bits now in it */ j_compress_ptr cinfo; /* link to cinfo (needed for dump_buffer) */ @@ -47,7 +47,7 @@ int ac_tbl_no; /* the table number of the single component */ unsigned int EOBRUN; /* run length of EOBs */ unsigned int BE; /* # of buffered correction bits before MCU */ - char * bit_buffer; /* buffer for correction bits (1 per char) */ + char *bit_buffer; /* buffer for correction bits (1 per char) */ /* packing correction bits tightly would save some space but cost time... */ unsigned int restarts_to_go; /* MCUs left in this restart interval */ @@ -57,13 +57,13 @@ * Since any one scan codes only DC or only AC, we only need one set * of tables, not one for DC and one for AC. */ - c_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + c_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; /* Statistics tables for optimization; again, one set is enough */ - long * count_ptrs[NUM_HUFF_TBLS]; + long *count_ptrs[NUM_HUFF_TBLS]; } phuff_entropy_encoder; -typedef phuff_entropy_encoder * phuff_entropy_ptr; +typedef phuff_entropy_encoder *phuff_entropy_ptr; /* MAX_CORR_BITS is the number of bits the AC refinement correction-bit * buffer can hold. Larger sizes may slightly improve compression, but @@ -73,8 +73,8 @@ #define MAX_CORR_BITS 1000 /* Max # of correction bits I can buffer */ -/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than INT32. - * We assume that int right shift is unsigned if INT32 right shift is, +/* IRIGHT_SHIFT is like RIGHT_SHIFT, but works on int rather than JLONG. + * We assume that int right shift is unsigned if JLONG right shift is, * which should be safe. */ @@ -112,7 +112,7 @@ phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; entropy->cinfo = cinfo; entropy->gather_statistics = gather_statistics; @@ -217,7 +217,7 @@ dump_buffer (phuff_entropy_ptr entropy) /* Empty the output buffer; we do not support suspension in this module. */ { - struct jpeg_destination_mgr * dest = entropy->cinfo->dest; + struct jpeg_destination_mgr *dest = entropy->cinfo->dest; if (! (*dest->empty_output_buffer) (entropy->cinfo)) ERREXIT(entropy->cinfo, JERR_CANT_SUSPEND); @@ -240,7 +240,7 @@ /* Emit some bits, unless we are in gather mode */ { /* This routine is heavily used, so it's worth coding tightly. */ - register INT32 put_buffer = (INT32) code; + register size_t put_buffer = (size_t) code; register int put_bits = entropy->put_bits; /* if size is 0, caller used an invalid Huffman table entry */ @@ -250,7 +250,7 @@ if (entropy->gather_statistics) return; /* do nothing if we're only getting stats */ - put_buffer &= (((INT32) 1)<gather_statistics) entropy->count_ptrs[tbl_no][symbol]++; else { - c_derived_tbl * tbl = entropy->derived_tbls[tbl_no]; + c_derived_tbl *tbl = entropy->derived_tbls[tbl_no]; emit_bits(entropy, tbl->ehufco[symbol], tbl->ehufsi[symbol]); } } @@ -304,7 +304,7 @@ */ LOCAL(void) -emit_buffered_bits (phuff_entropy_ptr entropy, char * bufstart, +emit_buffered_bits (phuff_entropy_ptr entropy, char *bufstart, unsigned int nbits) { if (entropy->gather_statistics) @@ -392,7 +392,7 @@ int blkn, ci; int Al = cinfo->Al; JBLOCKROW block; - jpeg_component_info * compptr; + jpeg_component_info *compptr; ISHIFT_TEMPS entropy->next_output_byte = cinfo->dest->next_output_byte; @@ -776,7 +776,7 @@ phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band; int ci, tbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; JHUFF_TBL **htblptr; boolean did[NUM_HUFF_TBLS]; diff -Nru leanify-0.4.3/lib/mozjpeg/jctrans.c leanify-0.4.3+git20181014/lib/mozjpeg/jctrans.c --- leanify-0.4.3/lib/mozjpeg/jctrans.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jctrans.c 2017-08-01 20:51:09.000000000 +0000 @@ -22,9 +22,9 @@ /* Forward declarations */ LOCAL(void) transencode_master_selection - (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays); + (j_compress_ptr cinfo, jvirt_barray_ptr *coef_arrays); LOCAL(void) transencode_coef_controller - (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays); + (j_compress_ptr cinfo, jvirt_barray_ptr *coef_arrays); /* @@ -40,7 +40,7 @@ */ GLOBAL(void) -jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr * coef_arrays) +jpeg_write_coefficients (j_compress_ptr cinfo, jvirt_barray_ptr *coef_arrays) { /* setting up scan optimisation pattern failed, disable scan optimisation */ if (cinfo->master->num_scans_luma == 0) @@ -72,7 +72,7 @@ jpeg_copy_critical_parameters (const j_decompress_ptr srcinfo, j_compress_ptr dstinfo) { - JQUANT_TBL ** qtblptr; + JQUANT_TBL **qtblptr; jpeg_component_info *incomp, *outcomp; JQUANT_TBL *c_quant, *slot_quant; int tblno, ci, coefi; @@ -173,7 +173,7 @@ LOCAL(void) transencode_master_selection (j_compress_ptr cinfo, - jvirt_barray_ptr * coef_arrays) + jvirt_barray_ptr *coef_arrays) { /* Although we don't actually use input_components for transcoding, * jcmaster.c's initial_setup will complain if input_components is 0. @@ -235,13 +235,13 @@ int MCU_rows_per_iMCU_row; /* number of such rows needed */ /* Virtual block array for each component. */ - jvirt_barray_ptr * whole_image; + jvirt_barray_ptr *whole_image; /* Workspace for constructing dummy blocks at right/bottom edges. */ JBLOCKROW dummy_buffer[C_MAX_BLOCKS_IN_MCU]; } my_coef_controller; -typedef my_coef_controller * my_coef_ptr; +typedef my_coef_controller *my_coef_ptr; LOCAL(void) @@ -382,7 +382,7 @@ LOCAL(void) transencode_coef_controller (j_compress_ptr cinfo, - jvirt_barray_ptr * coef_arrays) + jvirt_barray_ptr *coef_arrays) { my_coef_ptr coef; JBLOCKROW buffer; diff -Nru leanify-0.4.3/lib/mozjpeg/jdapimin.c leanify-0.4.3+git20181014/lib/mozjpeg/jdapimin.c --- leanify-0.4.3/lib/mozjpeg/jdapimin.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdapimin.c 2017-08-01 20:51:09.000000000 +0000 @@ -3,9 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1998, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains application interface code for the decompression half * of the JPEG library. These are the "minimum" API routines that may be @@ -21,6 +22,7 @@ #define JPEG_INTERNALS #include "jinclude.h" #include "jpeglib.h" +#include "jdmaster.h" /* @@ -82,6 +84,14 @@ /* OK, I'm ready */ cinfo->global_state = DSTATE_START; + + /* The master struct is used to store extension parameters, so we allocate it + * here. + */ + cinfo->master = (struct jpeg_decomp_master *) + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + sizeof(my_decomp_master)); + MEMZERO(cinfo->master, sizeof(my_decomp_master)); } diff -Nru leanify-0.4.3/lib/mozjpeg/jdarith.c leanify-0.4.3+git20181014/lib/mozjpeg/jdarith.c --- leanify-0.4.3/lib/mozjpeg/jdarith.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdarith.c 2018-03-21 11:12:12.000000000 +0000 @@ -2,10 +2,11 @@ * jdarith.c * * This file was part of the Independent JPEG Group's software: - * Developed 1997-2009 by Guido Vollbeding. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * Developed 1997-2015 by Guido Vollbeding. + * libjpeg-turbo Modifications: + * Copyright (C) 2015-2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains portable arithmetic entropy decoding routines for JPEG * (implementing the ISO/IEC IS 10918-1 and CCITT Recommendation ITU-T T.81). @@ -20,13 +21,16 @@ #include "jpeglib.h" +#define NEG_1 ((unsigned int)-1) + + /* Expanded entropy decoder object for arithmetic decoding. */ typedef struct { struct jpeg_entropy_decoder pub; /* public fields */ - INT32 c; /* C register, base of coding interval + input bit buffer */ - INT32 a; /* A register, normalized size of coding interval */ + JLONG c; /* C register, base of coding interval + input bit buffer */ + JLONG a; /* A register, normalized size of coding interval */ int ct; /* bit shift counter, # of bits left in bit buffer part of C */ /* init: ct = -16 */ /* run: ct = 0..7 */ @@ -37,14 +41,14 @@ unsigned int restarts_to_go; /* MCUs left in this restart interval */ /* Pointers to statistics areas (these workspaces have image lifespan) */ - unsigned char * dc_stats[NUM_ARITH_TBLS]; - unsigned char * ac_stats[NUM_ARITH_TBLS]; + unsigned char *dc_stats[NUM_ARITH_TBLS]; + unsigned char *ac_stats[NUM_ARITH_TBLS]; /* Statistics bin for coding with fixed probability 0.5 */ unsigned char fixed_bin[4]; } arith_entropy_decoder; -typedef arith_entropy_decoder * arith_entropy_ptr; +typedef arith_entropy_decoder *arith_entropy_ptr; /* The following two definitions specify the allocation chunk size * for the statistics area. @@ -67,7 +71,7 @@ get_byte (j_decompress_ptr cinfo) /* Read next input byte; we do not support suspension in this module. */ { - struct jpeg_source_mgr * src = cinfo->src; + struct jpeg_source_mgr *src = cinfo->src; if (src->bytes_in_buffer == 0) if (! (*src->fill_input_buffer) (cinfo)) @@ -96,7 +100,7 @@ * (instead of fixed) with the bit shift counter CT. * Thus, we also need only one (variable instead of * fixed size) shift for the LPS/MPS decision, and - * we can get away with any renormalization update + * we can do away with any renormalization update * of C (except for new data insertion, of course). * * I've also introduced a new scheme for accessing @@ -109,7 +113,7 @@ { register arith_entropy_ptr e = (arith_entropy_ptr) cinfo->entropy; register unsigned char nl, nm; - register INT32 qe, temp; + register JLONG qe, temp; register int sv, data; /* Renormalization & data input per section D.2.6 */ @@ -193,7 +197,7 @@ { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; int ci; - jpeg_component_info * compptr; + jpeg_component_info *compptr; /* Advance past the RSTn marker */ if (! (*cinfo->marker->read_restart_marker) (cinfo)) @@ -202,13 +206,13 @@ /* Re-initialize statistics areas */ for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; - if (! cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { + if (!cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { MEMZERO(entropy->dc_stats[compptr->dc_tbl_no], DC_STAT_BINS); /* Reset DC predictions to 0 */ entropy->last_dc_val[ci] = 0; entropy->dc_context[ci] = 0; } - if (! cinfo->progressive_mode || cinfo->Ss) { + if (!cinfo->progressive_mode || cinfo->Ss) { MEMZERO(entropy->ac_stats[compptr->ac_tbl_no], AC_STAT_BINS); } } @@ -306,7 +310,7 @@ } /* Scale and output the DC coefficient (assumes jpeg_natural_order[0]=0) */ - (*block)[0] = (JCOEF) (entropy->last_dc_val[ci] << cinfo->Al); + (*block)[0] = (JCOEF) LEFT_SHIFT(entropy->last_dc_val[ci], cinfo->Al); } return TRUE; @@ -381,7 +385,7 @@ if (arith_decode(cinfo, st)) v |= m; v += 1; if (sign) v = -v; /* Scale and output coefficient in natural (dezigzagged) order */ - (*block)[jpeg_natural_order[k]] = (JCOEF) (v << cinfo->Al); + (*block)[jpeg_natural_order[k]] = (JCOEF) ((unsigned)v << cinfo->Al); } return TRUE; @@ -449,7 +453,7 @@ tbl = cinfo->cur_comp_info[0]->ac_tbl_no; p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ - m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + m1 = (NEG_1) << cinfo->Al; /* -1 in the bit position being coded */ /* Establish EOBx (previous stage end-of-block) index */ for (kex = cinfo->Se; kex > 0; kex--) @@ -498,7 +502,7 @@ decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; - jpeg_component_info * compptr; + jpeg_component_info *compptr; JBLOCKROW block; unsigned char *st; int blkn, ci, tbl, sign, k; @@ -516,7 +520,7 @@ /* Outer loop handles each block in the MCU */ for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { - block = MCU_data[blkn]; + block = MCU_data ? MCU_data[blkn] : NULL; ci = cinfo->MCU_membership[blkn]; compptr = cinfo->cur_comp_info[ci]; @@ -563,7 +567,8 @@ entropy->last_dc_val[ci] += v; } - (*block)[0] = (JCOEF) entropy->last_dc_val[ci]; + if (block) + (*block)[0] = (JCOEF) entropy->last_dc_val[ci]; /* Sections F.2.4.2 & F.1.4.4.2: Decoding of AC coefficients */ @@ -607,7 +612,8 @@ while (m >>= 1) if (arith_decode(cinfo, st)) v |= m; v += 1; if (sign) v = -v; - (*block)[jpeg_natural_order[k]] = (JCOEF) v; + if (block) + (*block)[jpeg_natural_order[k]] = (JCOEF) v; } } @@ -624,7 +630,7 @@ { arith_entropy_ptr entropy = (arith_entropy_ptr) cinfo->entropy; int ci, tbl; - jpeg_component_info * compptr; + jpeg_component_info *compptr; if (cinfo->progressive_mode) { /* Validate progressive scan parameters */ @@ -691,7 +697,7 @@ /* Allocate & initialize requested statistics areas */ for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; - if (! cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { + if (!cinfo->progressive_mode || (cinfo->Ss == 0 && cinfo->Ah == 0)) { tbl = compptr->dc_tbl_no; if (tbl < 0 || tbl >= NUM_ARITH_TBLS) ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); @@ -703,7 +709,7 @@ entropy->last_dc_val[ci] = 0; entropy->dc_context[ci] = 0; } - if (! cinfo->progressive_mode || cinfo->Ss) { + if (!cinfo->progressive_mode || cinfo->Ss) { tbl = compptr->ac_tbl_no; if (tbl < 0 || tbl >= NUM_ARITH_TBLS) ERREXIT1(cinfo, JERR_NO_ARITH_TABLE, tbl); diff -Nru leanify-0.4.3/lib/mozjpeg/jdatadst.c leanify-0.4.3+git20181014/lib/mozjpeg/jdatadst.c --- leanify-0.4.3/lib/mozjpeg/jdatadst.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdatadst.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,8 +5,9 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * Modified 2009-2012 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2013, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2013, 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains compression data destination routines for the case of * emitting JPEG data to memory or to a file (or any stdio stream). @@ -23,7 +24,7 @@ #include "jerror.h" #ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ -extern void * malloc (size_t size); +extern void *malloc (size_t size); extern void free (void *ptr); #endif @@ -33,11 +34,11 @@ typedef struct { struct jpeg_destination_mgr pub; /* public fields */ - FILE * outfile; /* target stream */ - JOCTET * buffer; /* start of buffer */ + FILE *outfile; /* target stream */ + JOCTET *buffer; /* start of buffer */ } my_destination_mgr; -typedef my_destination_mgr * my_dest_ptr; +typedef my_destination_mgr *my_dest_ptr; #define OUTPUT_BUF_SIZE 4096 /* choose an efficiently fwrite'able size */ @@ -48,14 +49,14 @@ typedef struct { struct jpeg_destination_mgr pub; /* public fields */ - unsigned char ** outbuffer; /* target buffer */ - unsigned long * outsize; - unsigned char * newbuffer; /* newly allocated buffer */ - JOCTET * buffer; /* start of buffer */ + unsigned char **outbuffer; /* target buffer */ + unsigned long *outsize; + unsigned char *newbuffer; /* newly allocated buffer */ + JOCTET *buffer; /* start of buffer */ size_t bufsize; } my_mem_destination_mgr; -typedef my_mem_destination_mgr * my_mem_dest_ptr; +typedef my_mem_destination_mgr *my_mem_dest_ptr; #endif @@ -130,7 +131,7 @@ empty_mem_output_buffer (j_compress_ptr cinfo) { size_t nextsize; - JOCTET * nextbuffer; + JOCTET *nextbuffer; my_mem_dest_ptr dest = (my_mem_dest_ptr) cinfo->dest; /* Try to allocate new buffer with double size */ @@ -203,20 +204,25 @@ */ GLOBAL(void) -jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile) +jpeg_stdio_dest (j_compress_ptr cinfo, FILE *outfile) { my_dest_ptr dest; - /* The destination object is made permanent so that multiple JPEG images - * can be written to the same file without re-executing jpeg_stdio_dest. - * This makes it dangerous to use this manager and a different destination - * manager serially with the same JPEG object, because their private object - * sizes may be different. Caveat programmer. - */ + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + */ if (cinfo->dest == NULL) { /* first time for this JPEG object? */ cinfo->dest = (struct jpeg_destination_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(my_destination_mgr)); + } else if (cinfo->dest->init_destination != init_destination) { + /* It is unsafe to reuse the existing destination manager unless it was + * created by this function. Otherwise, there is no guarantee that the + * opaque structure is the right size. Note that we could just create a + * new structure, but the old structure would not be freed until + * jpeg_destroy_compress() was called. + */ + ERREXIT(cinfo, JERR_BUFFER_SIZE); } dest = (my_dest_ptr) cinfo->dest; @@ -237,11 +243,14 @@ * larger memory, so the buffer is available to the application after * finishing compression, and then the application is responsible for * freeing the requested memory. + * Note: An initial buffer supplied by the caller is expected to be + * managed by the application. The library does not free such buffer + * when allocating a larger buffer. */ GLOBAL(void) -jpeg_mem_dest (j_compress_ptr cinfo, - unsigned char ** outbuffer, unsigned long * outsize) +jpeg_mem_dest_internal (j_compress_ptr cinfo, + unsigned char **outbuffer, unsigned long *outsize, int pool_id) { my_mem_dest_ptr dest; @@ -253,8 +262,13 @@ */ if (cinfo->dest == NULL) { /* first time for this JPEG object? */ cinfo->dest = (struct jpeg_destination_mgr *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, + (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, pool_id, sizeof(my_mem_destination_mgr)); + } else if (cinfo->dest->init_destination != init_mem_destination) { + /* It is unsafe to reuse the existing destination manager unless it was + * created by this function. + */ + ERREXIT(cinfo, JERR_BUFFER_SIZE); } dest = (my_mem_dest_ptr) cinfo->dest; @@ -276,4 +290,15 @@ dest->pub.next_output_byte = dest->buffer = *outbuffer; dest->pub.free_in_buffer = dest->bufsize = *outsize; } + +GLOBAL(void) +jpeg_mem_dest (j_compress_ptr cinfo, + unsigned char **outbuffer, unsigned long *outsize) +{ + /* The destination object is made permanent so that multiple JPEG images + * can be written to the same file without re-executing jpeg_stdio_dest. + */ + jpeg_mem_dest_internal(cinfo, outbuffer, outsize, JPOOL_PERMANENT); +} + #endif diff -Nru leanify-0.4.3/lib/mozjpeg/jdatasrc.c leanify-0.4.3+git20181014/lib/mozjpeg/jdatasrc.c --- leanify-0.4.3/lib/mozjpeg/jdatasrc.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdatasrc.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,8 +5,9 @@ * Copyright (C) 1994-1996, Thomas G. Lane. * Modified 2009-2011 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2013, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2013, 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains decompression data source routines for the case of * reading JPEG data from memory or from a file (or any stdio stream). @@ -28,12 +29,12 @@ typedef struct { struct jpeg_source_mgr pub; /* public fields */ - FILE * infile; /* source stream */ - JOCTET * buffer; /* start of buffer */ + FILE *infile; /* source stream */ + JOCTET *buffer; /* start of buffer */ boolean start_of_file; /* have we gotten any data yet? */ } my_source_mgr; -typedef my_source_mgr * my_src_ptr; +typedef my_source_mgr *my_src_ptr; #define INPUT_BUF_SIZE 4096 /* choose an efficiently fread'able size */ @@ -161,7 +162,7 @@ METHODDEF(void) skip_input_data (j_decompress_ptr cinfo, long num_bytes) { - struct jpeg_source_mgr * src = cinfo->src; + struct jpeg_source_mgr *src = cinfo->src; /* Just a dumb implementation for now. Could use fseek() except * it doesn't work on pipes. Not clear that being smart is worth @@ -213,7 +214,7 @@ */ GLOBAL(void) -jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile) +jpeg_stdio_src (j_decompress_ptr cinfo, FILE *infile) { my_src_ptr src; @@ -221,8 +222,6 @@ * of JPEG images can be read from the same file by calling jpeg_stdio_src * only before the first one. (If we discarded the buffer at the end of * one image, we'd likely lose the start of the next one.) - * This makes it unsafe to use this manager and a different source - * manager serially with the same JPEG object. Caveat programmer. */ if (cinfo->src == NULL) { /* first time for this JPEG object? */ cinfo->src = (struct jpeg_source_mgr *) @@ -232,6 +231,14 @@ src->buffer = (JOCTET *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, INPUT_BUF_SIZE * sizeof(JOCTET)); + } else if (cinfo->src->init_source != init_source) { + /* It is unsafe to reuse the existing source manager unless it was created + * by this function. Otherwise, there is no guarantee that the opaque + * structure is the right size. Note that we could just create a new + * structure, but the old structure would not be freed until + * jpeg_destroy_decompress() was called. + */ + ERREXIT(cinfo, JERR_BUFFER_SIZE); } src = (my_src_ptr) cinfo->src; @@ -254,9 +261,9 @@ GLOBAL(void) jpeg_mem_src (j_decompress_ptr cinfo, - const unsigned char * inbuffer, unsigned long insize) + const unsigned char *inbuffer, unsigned long insize) { - struct jpeg_source_mgr * src; + struct jpeg_source_mgr *src; if (inbuffer == NULL || insize == 0) /* Treat empty input as fatal error */ ERREXIT(cinfo, JERR_INPUT_EMPTY); @@ -269,6 +276,11 @@ cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr)); + } else if (cinfo->src->init_source != init_mem_source) { + /* It is unsafe to reuse the existing source manager unless it was created + * by this function. + */ + ERREXIT(cinfo, JERR_BUFFER_SIZE); } src = cinfo->src; diff -Nru leanify-0.4.3/lib/mozjpeg/jdcoefct.c leanify-0.4.3+git20181014/lib/mozjpeg/jdcoefct.c --- leanify-0.4.3/lib/mozjpeg/jdcoefct.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdcoefct.c 2017-08-01 20:51:09.000000000 +0000 @@ -4,8 +4,11 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright 2009 Pierre Ossman for Cendio AB + * Copyright (C) 2010, 2015-2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains the coefficient buffer controller for decompression. * This controller is the top level of the JPEG decompressor proper. @@ -16,53 +19,10 @@ * Also, the input side (only) is used when reading a file for transcoding. */ -#define JPEG_INTERNALS #include "jinclude.h" -#include "jpeglib.h" +#include "jdcoefct.h" #include "jpegcomp.h" -/* Block smoothing is only applicable for progressive JPEG, so: */ -#ifndef D_PROGRESSIVE_SUPPORTED -#undef BLOCK_SMOOTHING_SUPPORTED -#endif - -/* Private buffer controller object */ - -typedef struct { - struct jpeg_d_coef_controller pub; /* public fields */ - - /* These variables keep track of the current location of the input side. */ - /* cinfo->input_iMCU_row is also used for this. */ - JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ - int MCU_vert_offset; /* counts MCU rows within iMCU row */ - int MCU_rows_per_iMCU_row; /* number of such rows needed */ - - /* The output side's location is represented by cinfo->output_iMCU_row. */ - - /* In single-pass modes, it's sufficient to buffer just one MCU. - * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, - * and let the entropy decoder write into that workspace each time. - * In multi-pass modes, this array points to the current MCU's blocks - * within the virtual arrays; it is used only by the input side. - */ - JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; - - /* Temporary workspace for one MCU */ - JCOEF * workspace; - -#ifdef D_MULTISCAN_FILES_SUPPORTED - /* In multi-pass modes, we need a virtual block array for each component. */ - jvirt_barray_ptr whole_image[MAX_COMPONENTS]; -#endif - -#ifdef BLOCK_SMOOTHING_SUPPORTED - /* When doing block smoothing, we latch coefficient Al values here */ - int * coef_bits_latch; -#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ -#endif -} my_coef_controller; - -typedef my_coef_controller * my_coef_ptr; /* Forward declarations */ METHODDEF(int) decompress_onepass @@ -78,30 +38,6 @@ #endif -LOCAL(void) -start_iMCU_row (j_decompress_ptr cinfo) -/* Reset within-iMCU-row counters for a new row (input side) */ -{ - my_coef_ptr coef = (my_coef_ptr) cinfo->coef; - - /* In an interleaved scan, an MCU row is the same as an iMCU row. - * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. - * But at the bottom of the image, process only what's left. - */ - if (cinfo->comps_in_scan > 1) { - coef->MCU_rows_per_iMCU_row = 1; - } else { - if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) - coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; - else - coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; - } - - coef->MCU_ctr = 0; - coef->MCU_vert_offset = 0; -} - - /* * Initialize for an input processing pass. */ @@ -173,38 +109,46 @@ coef->MCU_ctr = MCU_col_num; return JPEG_SUSPENDED; } - /* Determine where data should go in output_buf and do the IDCT thing. - * We skip dummy blocks at the right and bottom edges (but blkn gets - * incremented past them!). Note the inner loop relies on having - * allocated the MCU_buffer[] blocks sequentially. + + /* Only perform the IDCT on blocks that are contained within the desired + * cropping region. */ - blkn = 0; /* index of current DCT block within MCU */ - for (ci = 0; ci < cinfo->comps_in_scan; ci++) { - compptr = cinfo->cur_comp_info[ci]; - /* Don't bother to IDCT an uninteresting component. */ - if (! compptr->component_needed) { - blkn += compptr->MCU_blocks; - continue; - } - inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; - useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width - : compptr->last_col_width; - output_ptr = output_buf[compptr->component_index] + - yoffset * compptr->_DCT_scaled_size; - start_col = MCU_col_num * compptr->MCU_sample_width; - for (yindex = 0; yindex < compptr->MCU_height; yindex++) { - if (cinfo->input_iMCU_row < last_iMCU_row || - yoffset+yindex < compptr->last_row_height) { - output_col = start_col; - for (xindex = 0; xindex < useful_width; xindex++) { - (*inverse_DCT) (cinfo, compptr, - (JCOEFPTR) coef->MCU_buffer[blkn+xindex], - output_ptr, output_col); - output_col += compptr->_DCT_scaled_size; + if (MCU_col_num >= cinfo->master->first_iMCU_col && + MCU_col_num <= cinfo->master->last_iMCU_col) { + /* Determine where data should go in output_buf and do the IDCT thing. + * We skip dummy blocks at the right and bottom edges (but blkn gets + * incremented past them!). Note the inner loop relies on having + * allocated the MCU_buffer[] blocks sequentially. + */ + blkn = 0; /* index of current DCT block within MCU */ + for (ci = 0; ci < cinfo->comps_in_scan; ci++) { + compptr = cinfo->cur_comp_info[ci]; + /* Don't bother to IDCT an uninteresting component. */ + if (! compptr->component_needed) { + blkn += compptr->MCU_blocks; + continue; + } + inverse_DCT = cinfo->idct->inverse_DCT[compptr->component_index]; + useful_width = (MCU_col_num < last_MCU_col) ? compptr->MCU_width + : compptr->last_col_width; + output_ptr = output_buf[compptr->component_index] + + yoffset * compptr->_DCT_scaled_size; + start_col = (MCU_col_num - cinfo->master->first_iMCU_col) * + compptr->MCU_sample_width; + for (yindex = 0; yindex < compptr->MCU_height; yindex++) { + if (cinfo->input_iMCU_row < last_iMCU_row || + yoffset+yindex < compptr->last_row_height) { + output_col = start_col; + for (xindex = 0; xindex < useful_width; xindex++) { + (*inverse_DCT) (cinfo, compptr, + (JCOEFPTR) coef->MCU_buffer[blkn+xindex], + output_ptr, output_col); + output_col += compptr->_DCT_scaled_size; + } } + blkn += compptr->MCU_width; + output_ptr += compptr->_DCT_scaled_size; } - blkn += compptr->MCU_width; - output_ptr += compptr->_DCT_scaled_size; } } } @@ -359,9 +303,10 @@ output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { - buffer_ptr = buffer[block_row]; + buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; output_col = 0; - for (block_num = 0; block_num < compptr->width_in_blocks; block_num++) { + for (block_num = cinfo->master->first_MCU_col[ci]; + block_num <= cinfo->master->last_MCU_col[ci]; block_num++) { (*inverse_DCT) (cinfo, compptr, (JCOEFPTR) buffer_ptr, output_ptr, output_col); buffer_ptr++; @@ -411,9 +356,9 @@ boolean smoothing_useful = FALSE; int ci, coefi; jpeg_component_info *compptr; - JQUANT_TBL * qtable; - int * coef_bits; - int * coef_bits_latch; + JQUANT_TBL *qtable; + int *coef_bits; + int *coef_bits_latch; if (! cinfo->progressive_mode || cinfo->coef_bits == NULL) return FALSE; @@ -474,10 +419,10 @@ jpeg_component_info *compptr; inverse_DCT_method_ptr inverse_DCT; boolean first_row, last_row; - JCOEF * workspace; + JCOEF *workspace; int *coef_bits; JQUANT_TBL *quanttbl; - INT32 Q00,Q01,Q02,Q10,Q11,Q20, num; + JLONG Q00,Q01,Q02,Q10,Q11,Q20, num; int DC1,DC2,DC3,DC4,DC5,DC6,DC7,DC8,DC9; int Al, pred; @@ -547,7 +492,7 @@ output_ptr = output_buf[ci]; /* Loop over all DCT blocks to be processed. */ for (block_row = 0; block_row < block_rows; block_row++) { - buffer_ptr = buffer[block_row]; + buffer_ptr = buffer[block_row] + cinfo->master->first_MCU_col[ci]; if (first_row && block_row == 0) prev_block_row = buffer_ptr; else @@ -564,7 +509,8 @@ DC7 = DC8 = DC9 = (int) next_block_row[0][0]; output_col = 0; last_block_column = compptr->width_in_blocks - 1; - for (block_num = 0; block_num <= last_block_column; block_num++) { + for (block_num = cinfo->master->first_MCU_col[ci]; + block_num <= cinfo->master->last_MCU_col[ci]; block_num++) { /* Fetch current DCT block into workspace so we can modify it. */ jcopy_block_row(buffer_ptr, (JBLOCKROW) workspace, (JDIMENSION) 1); /* Update DC values */ diff -Nru leanify-0.4.3/lib/mozjpeg/jdcoefct.h leanify-0.4.3+git20181014/lib/mozjpeg/jdcoefct.h --- leanify-0.4.3/lib/mozjpeg/jdcoefct.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdcoefct.h 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,82 @@ +/* + * jdcoefct.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1997, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright 2009 Pierre Ossman for Cendio AB + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#define JPEG_INTERNALS +#include "jpeglib.h" + + +/* Block smoothing is only applicable for progressive JPEG, so: */ +#ifndef D_PROGRESSIVE_SUPPORTED +#undef BLOCK_SMOOTHING_SUPPORTED +#endif + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_coef_controller pub; /* public fields */ + + /* These variables keep track of the current location of the input side. */ + /* cinfo->input_iMCU_row is also used for this. */ + JDIMENSION MCU_ctr; /* counts MCUs processed in current row */ + int MCU_vert_offset; /* counts MCU rows within iMCU row */ + int MCU_rows_per_iMCU_row; /* number of such rows needed */ + + /* The output side's location is represented by cinfo->output_iMCU_row. */ + + /* In single-pass modes, it's sufficient to buffer just one MCU. + * We allocate a workspace of D_MAX_BLOCKS_IN_MCU coefficient blocks, + * and let the entropy decoder write into that workspace each time. + * In multi-pass modes, this array points to the current MCU's blocks + * within the virtual arrays; it is used only by the input side. + */ + JBLOCKROW MCU_buffer[D_MAX_BLOCKS_IN_MCU]; + + /* Temporary workspace for one MCU */ + JCOEF *workspace; + +#ifdef D_MULTISCAN_FILES_SUPPORTED + /* In multi-pass modes, we need a virtual block array for each component. */ + jvirt_barray_ptr whole_image[MAX_COMPONENTS]; +#endif + +#ifdef BLOCK_SMOOTHING_SUPPORTED + /* When doing block smoothing, we latch coefficient Al values here */ + int *coef_bits_latch; +#define SAVED_COEFS 6 /* we save coef_bits[0..5] */ +#endif +} my_coef_controller; + +typedef my_coef_controller *my_coef_ptr; + + +LOCAL(void) +start_iMCU_row (j_decompress_ptr cinfo) +/* Reset within-iMCU-row counters for a new row (input side) */ +{ + my_coef_ptr coef = (my_coef_ptr) cinfo->coef; + + /* In an interleaved scan, an MCU row is the same as an iMCU row. + * In a noninterleaved scan, an iMCU row has v_samp_factor MCU rows. + * But at the bottom of the image, process only what's left. + */ + if (cinfo->comps_in_scan > 1) { + coef->MCU_rows_per_iMCU_row = 1; + } else { + if (cinfo->input_iMCU_row < (cinfo->total_iMCU_rows-1)) + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->v_samp_factor; + else + coef->MCU_rows_per_iMCU_row = cinfo->cur_comp_info[0]->last_row_height; + } + + coef->MCU_ctr = 0; + coef->MCU_vert_offset = 0; +} diff -Nru leanify-0.4.3/lib/mozjpeg/jdcol565.c leanify-0.4.3+git20181014/lib/mozjpeg/jdcol565.c --- leanify-0.4.3/lib/mozjpeg/jdcol565.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdcol565.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,383 +0,0 @@ -/* - * jdcol565.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1997, Thomas G. Lane. - * Modifications: - * Copyright (C) 2013, Linaro Limited. - * Copyright (C) 2014, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains output colorspace conversion routines. - */ - -/* This file is included by jdcolor.c */ - - -INLINE -LOCAL(void) -ycc_rgb565_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - register int * Crrtab = cconvert->Cr_r_tab; - register int * Cbbtab = cconvert->Cb_b_tab; - register INT32 * Crgtab = cconvert->Cr_g_tab; - register INT32 * Cbgtab = cconvert->Cb_g_tab; - SHIFT_TEMPS - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int r, g, b; - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - - if (PACK_NEED_ALIGNMENT(outptr)) { - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[y + Crrtab[cr]]; - g = range_limit[y + ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - b = range_limit[y + Cbbtab[cb]]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[y + Crrtab[cr]]; - g = range_limit[y + ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - b = range_limit[y + Cbbtab[cb]]; - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[y + Crrtab[cr]]; - g = range_limit[y + ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - b = range_limit[y + Cbbtab[cb]]; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - y = GETJSAMPLE(*inptr0); - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - r = range_limit[y + Crrtab[cr]]; - g = range_limit[y + ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - b = range_limit[y + Cbbtab[cb]]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } - } -} - - -INLINE -LOCAL(void) -ycc_rgb565D_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - register int * Crrtab = cconvert->Cr_r_tab; - register int * Cbbtab = cconvert->Cb_b_tab; - register INT32 * Crgtab = cconvert->Cr_g_tab; - register INT32 * Cbgtab = cconvert->Cb_g_tab; - INT32 d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; - SHIFT_TEMPS - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int r, g, b; - - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - if (PACK_NEED_ALIGNMENT(outptr)) { - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[DITHER_565_R(y + Crrtab[cr], d0)]; - g = range_limit[DITHER_565_G(y + - ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS)), d0)]; - b = range_limit[DITHER_565_B(y + Cbbtab[cb], d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[DITHER_565_R(y + Crrtab[cr], d0)]; - g = range_limit[DITHER_565_G(y + - ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS)), d0)]; - b = range_limit[DITHER_565_B(y + Cbbtab[cb], d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr0++); - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - r = range_limit[DITHER_565_R(y + Crrtab[cr], d0)]; - g = range_limit[DITHER_565_G(y + - ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS)), d0)]; - b = range_limit[DITHER_565_B(y + Cbbtab[cb], d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - y = GETJSAMPLE(*inptr0); - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - r = range_limit[DITHER_565_R(y + Crrtab[cr], d0)]; - g = range_limit[DITHER_565_G(y + - ((int)RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS)), d0)]; - b = range_limit[DITHER_565_B(y + Cbbtab[cb], d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } - } -} - - -INLINE -LOCAL(void) -rgb_rgb565_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - SHIFT_TEMPS - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int r, g, b; - - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - if (PACK_NEED_ALIGNMENT(outptr)) { - r = GETJSAMPLE(*inptr0++); - g = GETJSAMPLE(*inptr1++); - b = GETJSAMPLE(*inptr2++); - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - r = GETJSAMPLE(*inptr0++); - g = GETJSAMPLE(*inptr1++); - b = GETJSAMPLE(*inptr2++); - rgb = PACK_SHORT_565(r, g, b); - - r = GETJSAMPLE(*inptr0++); - g = GETJSAMPLE(*inptr1++); - b = GETJSAMPLE(*inptr2++); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - r = GETJSAMPLE(*inptr0); - g = GETJSAMPLE(*inptr1); - b = GETJSAMPLE(*inptr2); - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } - } -} - - -INLINE -LOCAL(void) -rgb_rgb565D_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - register JSAMPLE * range_limit = cinfo->sample_range_limit; - JDIMENSION num_cols = cinfo->output_width; - INT32 d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; - SHIFT_TEMPS - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int r, g, b; - - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - if (PACK_NEED_ALIGNMENT(outptr)) { - r = range_limit[DITHER_565_R(GETJSAMPLE(*inptr0++), d0)]; - g = range_limit[DITHER_565_G(GETJSAMPLE(*inptr1++), d0)]; - b = range_limit[DITHER_565_B(GETJSAMPLE(*inptr2++), d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - r = range_limit[DITHER_565_R(GETJSAMPLE(*inptr0++), d0)]; - g = range_limit[DITHER_565_G(GETJSAMPLE(*inptr1++), d0)]; - b = range_limit[DITHER_565_B(GETJSAMPLE(*inptr2++), d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_SHORT_565(r, g, b); - - r = range_limit[DITHER_565_R(GETJSAMPLE(*inptr0++), d0)]; - g = range_limit[DITHER_565_G(GETJSAMPLE(*inptr1++), d0)]; - b = range_limit[DITHER_565_B(GETJSAMPLE(*inptr2++), d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - r = range_limit[DITHER_565_R(GETJSAMPLE(*inptr0), d0)]; - g = range_limit[DITHER_565_G(GETJSAMPLE(*inptr1), d0)]; - b = range_limit[DITHER_565_B(GETJSAMPLE(*inptr2), d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } - } -} - - -INLINE -LOCAL(void) -gray_rgb565_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW inptr, outptr; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int g; - - inptr = input_buf[0][input_row++]; - outptr = *output_buf++; - if (PACK_NEED_ALIGNMENT(outptr)) { - g = *inptr++; - rgb = PACK_SHORT_565(g, g, g); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - g = *inptr++; - rgb = PACK_SHORT_565(g, g, g); - g = *inptr++; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(g, g, g)); - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - g = *inptr; - rgb = PACK_SHORT_565(g, g, g); - *(INT16*)outptr = rgb; - } - } -} - - -INLINE -LOCAL(void) -gray_rgb565D_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW inptr, outptr; - register JDIMENSION col; - register JSAMPLE * range_limit = cinfo->sample_range_limit; - JDIMENSION num_cols = cinfo->output_width; - INT32 d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; - - while (--num_rows >= 0) { - INT32 rgb; - unsigned int g; - - inptr = input_buf[0][input_row++]; - outptr = *output_buf++; - if (PACK_NEED_ALIGNMENT(outptr)) { - g = *inptr++; - g = range_limit[DITHER_565_R(g, d0)]; - rgb = PACK_SHORT_565(g, g, g); - *(INT16*)outptr = rgb; - outptr += 2; - num_cols--; - } - for (col = 0; col < (num_cols >> 1); col++) { - g = *inptr++; - g = range_limit[DITHER_565_R(g, d0)]; - rgb = PACK_SHORT_565(g, g, g); - d0 = DITHER_ROTATE(d0); - - g = *inptr++; - g = range_limit[DITHER_565_R(g, d0)]; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(g, g, g)); - d0 = DITHER_ROTATE(d0); - - WRITE_TWO_ALIGNED_PIXELS(outptr, rgb); - outptr += 4; - } - if (num_cols & 1) { - g = *inptr; - g = range_limit[DITHER_565_R(g, d0)]; - rgb = PACK_SHORT_565(g, g, g); - *(INT16*)outptr = rgb; - } - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdcolext.c leanify-0.4.3+git20181014/lib/mozjpeg/jdcolext.c --- leanify-0.4.3/lib/mozjpeg/jdcolext.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdcolext.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -/* - * jdcolext.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1997, Thomas G. Lane. - * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains output colorspace conversion routines. - */ - - -/* This file is included by jdcolor.c */ - - -/* - * Convert some rows of samples to the output colorspace. - * - * Note that we change from noninterleaved, one-plane-per-component format - * to interleaved-pixel format. The output buffer is therefore three times - * as wide as the input buffer. - * A starting row offset is provided only for the input buffer. The caller - * can easily adjust the passed output_buf value to accommodate any row - * offset required on that side. - */ - -INLINE -LOCAL(void) -ycc_rgb_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - register int * Crrtab = cconvert->Cr_r_tab; - register int * Cbbtab = cconvert->Cb_b_tab; - register INT32 * Crgtab = cconvert->Cr_g_tab; - register INT32 * Cbgtab = cconvert->Cb_g_tab; - SHIFT_TEMPS - - while (--num_rows >= 0) { - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - y = GETJSAMPLE(inptr0[col]); - cb = GETJSAMPLE(inptr1[col]); - cr = GETJSAMPLE(inptr2[col]); - /* Range-limiting is essential due to noise introduced by DCT losses. */ - outptr[RGB_RED] = range_limit[y + Crrtab[cr]]; - outptr[RGB_GREEN] = range_limit[y + - ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS))]; - outptr[RGB_BLUE] = range_limit[y + Cbbtab[cb]]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - outptr += RGB_PIXELSIZE; - } - } -} - - -/* - * Convert grayscale to RGB: just duplicate the graylevel three times. - * This is provided to support applications that don't want to cope - * with grayscale as a separate case. - */ - -INLINE -LOCAL(void) -gray_rgb_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW inptr, outptr; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - - while (--num_rows >= 0) { - inptr = input_buf[0][input_row++]; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - /* We can dispense with GETJSAMPLE() here */ - outptr[RGB_RED] = outptr[RGB_GREEN] = outptr[RGB_BLUE] = inptr[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - outptr += RGB_PIXELSIZE; - } - } -} - - -/* - * Convert RGB to extended RGB: just swap the order of source pixels - */ - -INLINE -LOCAL(void) -rgb_rgb_convert_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW inptr0, inptr1, inptr2; - register JSAMPROW outptr; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - - while (--num_rows >= 0) { - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - /* We can dispense with GETJSAMPLE() here */ - outptr[RGB_RED] = inptr0[col]; - outptr[RGB_GREEN] = inptr1[col]; - outptr[RGB_BLUE] = inptr2[col]; - /* Set unused byte to 0xFF so it can be interpreted as an opaque */ - /* alpha channel value */ -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - outptr += RGB_PIXELSIZE; - } - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdcolor.c leanify-0.4.3+git20181014/lib/mozjpeg/jdcolor.c --- leanify-0.4.3/lib/mozjpeg/jdcolor.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdcolor.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,866 +0,0 @@ -/* - * jdcolor.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1997, Thomas G. Lane. - * Modified 2011 by Guido Vollbeding. - * libjpeg-turbo Modifications: - * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2009, 2011-2012, 2014, D. R. Commander. - * Copyright (C) 2013, Linaro Limited. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains output colorspace conversion routines. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jsimd.h" -#include "jconfigint.h" - - -/* Private subobject */ - -typedef struct { - struct jpeg_color_deconverter pub; /* public fields */ - - /* Private state for YCC->RGB conversion */ - int * Cr_r_tab; /* => table for Cr to R conversion */ - int * Cb_b_tab; /* => table for Cb to B conversion */ - INT32 * Cr_g_tab; /* => table for Cr to G conversion */ - INT32 * Cb_g_tab; /* => table for Cb to G conversion */ - - /* Private state for RGB->Y conversion */ - INT32 * rgb_y_tab; /* => table for RGB to Y conversion */ -} my_color_deconverter; - -typedef my_color_deconverter * my_cconvert_ptr; - - -/**************** YCbCr -> RGB conversion: most common case **************/ -/**************** RGB -> Y conversion: less common case **************/ - -/* - * YCbCr is defined per CCIR 601-1, except that Cb and Cr are - * normalized to the range 0..MAXJSAMPLE rather than -0.5 .. 0.5. - * The conversion equations to be implemented are therefore - * - * R = Y + 1.40200 * Cr - * G = Y - 0.34414 * Cb - 0.71414 * Cr - * B = Y + 1.77200 * Cb - * - * Y = 0.29900 * R + 0.58700 * G + 0.11400 * B - * - * where Cb and Cr represent the incoming values less CENTERJSAMPLE. - * (These numbers are derived from TIFF 6.0 section 21, dated 3-June-92.) - * - * To avoid floating-point arithmetic, we represent the fractional constants - * as integers scaled up by 2^16 (about 4 digits precision); we have to divide - * the products by 2^16, with appropriate rounding, to get the correct answer. - * Notice that Y, being an integral input, does not contribute any fraction - * so it need not participate in the rounding. - * - * For even more speed, we avoid doing any multiplications in the inner loop - * by precalculating the constants times Cb and Cr for all possible values. - * For 8-bit JSAMPLEs this is very reasonable (only 256 entries per table); - * for 12-bit samples it is still acceptable. It's not very reasonable for - * 16-bit samples, but if you want lossless storage you shouldn't be changing - * colorspace anyway. - * The Cr=>R and Cb=>B values can be rounded to integers in advance; the - * values for the G calculation are left scaled up, since we must add them - * together before rounding. - */ - -#define SCALEBITS 16 /* speediest right-shift on some machines */ -#define ONE_HALF ((INT32) 1 << (SCALEBITS-1)) -#define FIX(x) ((INT32) ((x) * (1L<Y conversion and divide it up into - * three parts, instead of doing three alloc_small requests. This lets us - * use a single table base address, which can be held in a register in the - * inner loops on many machines (more than can hold all three addresses, - * anyway). - */ - -#define R_Y_OFF 0 /* offset to R => Y section */ -#define G_Y_OFF (1*(MAXJSAMPLE+1)) /* offset to G => Y section */ -#define B_Y_OFF (2*(MAXJSAMPLE+1)) /* etc. */ -#define TABLE_SIZE (3*(MAXJSAMPLE+1)) - - -/* Include inline routines for colorspace extensions */ - -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_PIXELSIZE - -#define RGB_RED EXT_RGB_RED -#define RGB_GREEN EXT_RGB_GREEN -#define RGB_BLUE EXT_RGB_BLUE -#define RGB_PIXELSIZE EXT_RGB_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extrgb_convert_internal -#define gray_rgb_convert_internal gray_extrgb_convert_internal -#define rgb_rgb_convert_internal rgb_extrgb_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - -#define RGB_RED EXT_RGBX_RED -#define RGB_GREEN EXT_RGBX_GREEN -#define RGB_BLUE EXT_RGBX_BLUE -#define RGB_ALPHA 3 -#define RGB_PIXELSIZE EXT_RGBX_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extrgbx_convert_internal -#define gray_rgb_convert_internal gray_extrgbx_convert_internal -#define rgb_rgb_convert_internal rgb_extrgbx_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_ALPHA -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - -#define RGB_RED EXT_BGR_RED -#define RGB_GREEN EXT_BGR_GREEN -#define RGB_BLUE EXT_BGR_BLUE -#define RGB_PIXELSIZE EXT_BGR_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extbgr_convert_internal -#define gray_rgb_convert_internal gray_extbgr_convert_internal -#define rgb_rgb_convert_internal rgb_extbgr_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - -#define RGB_RED EXT_BGRX_RED -#define RGB_GREEN EXT_BGRX_GREEN -#define RGB_BLUE EXT_BGRX_BLUE -#define RGB_ALPHA 3 -#define RGB_PIXELSIZE EXT_BGRX_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extbgrx_convert_internal -#define gray_rgb_convert_internal gray_extbgrx_convert_internal -#define rgb_rgb_convert_internal rgb_extbgrx_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_ALPHA -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - -#define RGB_RED EXT_XBGR_RED -#define RGB_GREEN EXT_XBGR_GREEN -#define RGB_BLUE EXT_XBGR_BLUE -#define RGB_ALPHA 0 -#define RGB_PIXELSIZE EXT_XBGR_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extxbgr_convert_internal -#define gray_rgb_convert_internal gray_extxbgr_convert_internal -#define rgb_rgb_convert_internal rgb_extxbgr_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_ALPHA -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - -#define RGB_RED EXT_XRGB_RED -#define RGB_GREEN EXT_XRGB_GREEN -#define RGB_BLUE EXT_XRGB_BLUE -#define RGB_ALPHA 0 -#define RGB_PIXELSIZE EXT_XRGB_PIXELSIZE -#define ycc_rgb_convert_internal ycc_extxrgb_convert_internal -#define gray_rgb_convert_internal gray_extxrgb_convert_internal -#define rgb_rgb_convert_internal rgb_extxrgb_convert_internal -#include "jdcolext.c" -#undef RGB_RED -#undef RGB_GREEN -#undef RGB_BLUE -#undef RGB_ALPHA -#undef RGB_PIXELSIZE -#undef ycc_rgb_convert_internal -#undef gray_rgb_convert_internal -#undef rgb_rgb_convert_internal - - -/* - * Initialize tables for YCC->RGB colorspace conversion. - */ - -LOCAL(void) -build_ycc_rgb_table (j_decompress_ptr cinfo) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - int i; - INT32 x; - SHIFT_TEMPS - - cconvert->Cr_r_tab = (int *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (MAXJSAMPLE+1) * sizeof(int)); - cconvert->Cb_b_tab = (int *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (MAXJSAMPLE+1) * sizeof(int)); - cconvert->Cr_g_tab = (INT32 *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (MAXJSAMPLE+1) * sizeof(INT32)); - cconvert->Cb_g_tab = (INT32 *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (MAXJSAMPLE+1) * sizeof(INT32)); - - for (i = 0, x = -CENTERJSAMPLE; i <= MAXJSAMPLE; i++, x++) { - /* i is the actual input pixel value, in the range 0..MAXJSAMPLE */ - /* The Cb or Cr value we are thinking of is x = i - CENTERJSAMPLE */ - /* Cr=>R value is nearest int to 1.40200 * x */ - cconvert->Cr_r_tab[i] = (int) - RIGHT_SHIFT(FIX(1.40200) * x + ONE_HALF, SCALEBITS); - /* Cb=>B value is nearest int to 1.77200 * x */ - cconvert->Cb_b_tab[i] = (int) - RIGHT_SHIFT(FIX(1.77200) * x + ONE_HALF, SCALEBITS); - /* Cr=>G value is scaled-up -0.71414 * x */ - cconvert->Cr_g_tab[i] = (- FIX(0.71414)) * x; - /* Cb=>G value is scaled-up -0.34414 * x */ - /* We also add in ONE_HALF so that need not do it in inner loop */ - cconvert->Cb_g_tab[i] = (- FIX(0.34414)) * x + ONE_HALF; - } -} - - -/* - * Convert some rows of samples to the output colorspace. - */ - -METHODDEF(void) -ycc_rgb_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - switch (cinfo->out_color_space) { - case JCS_EXT_RGB: - ycc_extrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_RGBX: - case JCS_EXT_RGBA: - ycc_extrgbx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGR: - ycc_extbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGRX: - case JCS_EXT_BGRA: - ycc_extbgrx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XBGR: - case JCS_EXT_ABGR: - ycc_extxbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XRGB: - case JCS_EXT_ARGB: - ycc_extxrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - default: - ycc_rgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - } -} - - -/**************** Cases other than YCbCr -> RGB **************/ - - -/* - * Initialize for RGB->grayscale colorspace conversion. - */ - -LOCAL(void) -build_rgb_y_table (j_decompress_ptr cinfo) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - INT32 * rgb_y_tab; - INT32 i; - - /* Allocate and fill in the conversion tables. */ - cconvert->rgb_y_tab = rgb_y_tab = (INT32 *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (TABLE_SIZE * sizeof(INT32))); - - for (i = 0; i <= MAXJSAMPLE; i++) { - rgb_y_tab[i+R_Y_OFF] = FIX(0.29900) * i; - rgb_y_tab[i+G_Y_OFF] = FIX(0.58700) * i; - rgb_y_tab[i+B_Y_OFF] = FIX(0.11400) * i + ONE_HALF; - } -} - - -/* - * Convert RGB to grayscale. - */ - -METHODDEF(void) -rgb_gray_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int r, g, b; - register INT32 * ctab = cconvert->rgb_y_tab; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - - while (--num_rows >= 0) { - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - input_row++; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - r = GETJSAMPLE(inptr0[col]); - g = GETJSAMPLE(inptr1[col]); - b = GETJSAMPLE(inptr2[col]); - /* Y */ - outptr[col] = (JSAMPLE) - ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF]) - >> SCALEBITS); - } - } -} - - -/* - * Color conversion for no colorspace change: just copy the data, - * converting from separate-planes to interleaved representation. - */ - -METHODDEF(void) -null_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - register JSAMPROW inptr, outptr; - register JDIMENSION count; - register int num_components = cinfo->num_components; - JDIMENSION num_cols = cinfo->output_width; - int ci; - - while (--num_rows >= 0) { - for (ci = 0; ci < num_components; ci++) { - inptr = input_buf[ci][input_row]; - outptr = output_buf[0] + ci; - for (count = num_cols; count > 0; count--) { - *outptr = *inptr++; /* needn't bother with GETJSAMPLE() here */ - outptr += num_components; - } - } - input_row++; - output_buf++; - } -} - - -/* - * Color conversion for grayscale: just copy the data. - * This also works for YCbCr -> grayscale conversion, in which - * we just copy the Y (luminance) component and ignore chrominance. - */ - -METHODDEF(void) -grayscale_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - jcopy_sample_rows(input_buf[0], (int) input_row, output_buf, 0, - num_rows, cinfo->output_width); -} - - -/* - * Convert grayscale to RGB - */ - -METHODDEF(void) -gray_rgb_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - switch (cinfo->out_color_space) { - case JCS_EXT_RGB: - gray_extrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_RGBX: - case JCS_EXT_RGBA: - gray_extrgbx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGR: - gray_extbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGRX: - case JCS_EXT_BGRA: - gray_extbgrx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XBGR: - case JCS_EXT_ABGR: - gray_extxbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XRGB: - case JCS_EXT_ARGB: - gray_extxrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - default: - gray_rgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - } -} - - -/* - * Convert plain RGB to extended RGB - */ - -METHODDEF(void) -rgb_rgb_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - switch (cinfo->out_color_space) { - case JCS_EXT_RGB: - rgb_extrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_RGBX: - case JCS_EXT_RGBA: - rgb_extrgbx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGR: - rgb_extbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_BGRX: - case JCS_EXT_BGRA: - rgb_extbgrx_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XBGR: - case JCS_EXT_ABGR: - rgb_extxbgr_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - case JCS_EXT_XRGB: - case JCS_EXT_ARGB: - rgb_extxrgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - default: - rgb_rgb_convert_internal(cinfo, input_buf, input_row, output_buf, - num_rows); - break; - } -} - - -/* - * Adobe-style YCCK->CMYK conversion. - * We convert YCbCr to R=1-C, G=1-M, and B=1-Y using the same - * conversion as above, while passing K (black) unchanged. - * We assume build_ycc_rgb_table has been called. - */ - -METHODDEF(void) -ycck_cmyk_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert; - register int y, cb, cr; - register JSAMPROW outptr; - register JSAMPROW inptr0, inptr1, inptr2, inptr3; - register JDIMENSION col; - JDIMENSION num_cols = cinfo->output_width; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - register int * Crrtab = cconvert->Cr_r_tab; - register int * Cbbtab = cconvert->Cb_b_tab; - register INT32 * Crgtab = cconvert->Cr_g_tab; - register INT32 * Cbgtab = cconvert->Cb_g_tab; - SHIFT_TEMPS - - while (--num_rows >= 0) { - inptr0 = input_buf[0][input_row]; - inptr1 = input_buf[1][input_row]; - inptr2 = input_buf[2][input_row]; - inptr3 = input_buf[3][input_row]; - input_row++; - outptr = *output_buf++; - for (col = 0; col < num_cols; col++) { - y = GETJSAMPLE(inptr0[col]); - cb = GETJSAMPLE(inptr1[col]); - cr = GETJSAMPLE(inptr2[col]); - /* Range-limiting is essential due to noise introduced by DCT losses. */ - outptr[0] = range_limit[MAXJSAMPLE - (y + Crrtab[cr])]; /* red */ - outptr[1] = range_limit[MAXJSAMPLE - (y + /* green */ - ((int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], - SCALEBITS)))]; - outptr[2] = range_limit[MAXJSAMPLE - (y + Cbbtab[cb])]; /* blue */ - /* K passes through unchanged */ - outptr[3] = inptr3[col]; /* don't need GETJSAMPLE here */ - outptr += 4; - } - } -} - - -/* - * RGB565 conversion - */ - -#define PACK_SHORT_565_LE(r, g, b) ((((r) << 8) & 0xF800) | \ - (((g) << 3) & 0x7E0) | ((b) >> 3)) -#define PACK_SHORT_565_BE(r, g, b) (((r) & 0xF8) | ((g) >> 5) | \ - (((g) << 11) & 0xE000) | \ - (((b) << 5) & 0x1F00)) - -#define PACK_TWO_PIXELS_LE(l, r) ((r << 16) | l) -#define PACK_TWO_PIXELS_BE(l, r) ((l << 16) | r) - -#define PACK_NEED_ALIGNMENT(ptr) (((size_t)(ptr)) & 3) - -#define WRITE_TWO_ALIGNED_PIXELS(addr, pixels) ((*(int *)(addr)) = pixels) - -#define DITHER_565_R(r, dither) ((r) + ((dither) & 0xFF)) -#define DITHER_565_G(g, dither) ((g) + (((dither) & 0xFF) >> 1)) -#define DITHER_565_B(b, dither) ((b) + ((dither) & 0xFF)) - - -/* Declarations for ordered dithering - * - * We use a 4x4 ordered dither array packed into 32 bits. This array is - * sufficent for dithering RGB888 to RGB565. - */ - -#define DITHER_MASK 0x3 -#define DITHER_ROTATE(x) (((x) << 24) | (((x) >> 8) & 0x00FFFFFF)) -static const INT32 dither_matrix[4] = { - 0x0008020A, - 0x0C040E06, - 0x030B0109, - 0x0F070D05 -}; - - -static INLINE boolean is_big_endian(void) -{ - int test_value = 1; - if(*(char *)&test_value != 1) - return TRUE; - return FALSE; -} - - -/* Include inline routines for RGB565 conversion */ - -#define PACK_SHORT_565 PACK_SHORT_565_LE -#define PACK_TWO_PIXELS PACK_TWO_PIXELS_LE -#define ycc_rgb565_convert_internal ycc_rgb565_convert_le -#define ycc_rgb565D_convert_internal ycc_rgb565D_convert_le -#define rgb_rgb565_convert_internal rgb_rgb565_convert_le -#define rgb_rgb565D_convert_internal rgb_rgb565D_convert_le -#define gray_rgb565_convert_internal gray_rgb565_convert_le -#define gray_rgb565D_convert_internal gray_rgb565D_convert_le -#include "jdcol565.c" -#undef PACK_SHORT_565 -#undef PACK_TWO_PIXELS -#undef ycc_rgb565_convert_internal -#undef ycc_rgb565D_convert_internal -#undef rgb_rgb565_convert_internal -#undef rgb_rgb565D_convert_internal -#undef gray_rgb565_convert_internal -#undef gray_rgb565D_convert_internal - -#define PACK_SHORT_565 PACK_SHORT_565_BE -#define PACK_TWO_PIXELS PACK_TWO_PIXELS_BE -#define ycc_rgb565_convert_internal ycc_rgb565_convert_be -#define ycc_rgb565D_convert_internal ycc_rgb565D_convert_be -#define rgb_rgb565_convert_internal rgb_rgb565_convert_be -#define rgb_rgb565D_convert_internal rgb_rgb565D_convert_be -#define gray_rgb565_convert_internal gray_rgb565_convert_be -#define gray_rgb565D_convert_internal gray_rgb565D_convert_be -#include "jdcol565.c" -#undef PACK_SHORT_565 -#undef PACK_TWO_PIXELS -#undef ycc_rgb565_convert_internal -#undef ycc_rgb565D_convert_internal -#undef rgb_rgb565_convert_internal -#undef rgb_rgb565D_convert_internal -#undef gray_rgb565_convert_internal -#undef gray_rgb565D_convert_internal - - -METHODDEF(void) -ycc_rgb565_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - ycc_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - ycc_rgb565_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -METHODDEF(void) -ycc_rgb565D_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - ycc_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - ycc_rgb565D_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -METHODDEF(void) -rgb_rgb565_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - rgb_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - rgb_rgb565_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -METHODDEF(void) -rgb_rgb565D_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - rgb_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - rgb_rgb565D_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -METHODDEF(void) -gray_rgb565_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - gray_rgb565_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - gray_rgb565_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -METHODDEF(void) -gray_rgb565D_convert (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION input_row, - JSAMPARRAY output_buf, int num_rows) -{ - if (is_big_endian()) - gray_rgb565D_convert_be(cinfo, input_buf, input_row, output_buf, num_rows); - else - gray_rgb565D_convert_le(cinfo, input_buf, input_row, output_buf, num_rows); -} - - -/* - * Empty method for start_pass. - */ - -METHODDEF(void) -start_pass_dcolor (j_decompress_ptr cinfo) -{ - /* no work needed */ -} - - -/* - * Module initialization routine for output colorspace conversion. - */ - -GLOBAL(void) -jinit_color_deconverter (j_decompress_ptr cinfo) -{ - my_cconvert_ptr cconvert; - int ci; - - cconvert = (my_cconvert_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_color_deconverter)); - cinfo->cconvert = (struct jpeg_color_deconverter *) cconvert; - cconvert->pub.start_pass = start_pass_dcolor; - - /* Make sure num_components agrees with jpeg_color_space */ - switch (cinfo->jpeg_color_space) { - case JCS_GRAYSCALE: - if (cinfo->num_components != 1) - ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); - break; - - case JCS_RGB: - case JCS_YCbCr: - if (cinfo->num_components != 3) - ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); - break; - - case JCS_CMYK: - case JCS_YCCK: - if (cinfo->num_components != 4) - ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); - break; - - default: /* JCS_UNKNOWN can be anything */ - if (cinfo->num_components < 1) - ERREXIT(cinfo, JERR_BAD_J_COLORSPACE); - break; - } - - /* Set out_color_components and conversion method based on requested space. - * Also clear the component_needed flags for any unused components, - * so that earlier pipeline stages can avoid useless computation. - */ - - switch (cinfo->out_color_space) { - case JCS_GRAYSCALE: - cinfo->out_color_components = 1; - if (cinfo->jpeg_color_space == JCS_GRAYSCALE || - cinfo->jpeg_color_space == JCS_YCbCr) { - cconvert->pub.color_convert = grayscale_convert; - /* For color->grayscale conversion, only the Y (0) component is needed */ - for (ci = 1; ci < cinfo->num_components; ci++) - cinfo->comp_info[ci].component_needed = FALSE; - } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_gray_convert; - build_rgb_y_table(cinfo); - } else - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - break; - - case JCS_RGB: - case JCS_EXT_RGB: - case JCS_EXT_RGBX: - case JCS_EXT_BGR: - case JCS_EXT_BGRX: - case JCS_EXT_XBGR: - case JCS_EXT_XRGB: - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_EXT_ABGR: - case JCS_EXT_ARGB: - cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; - if (cinfo->jpeg_color_space == JCS_YCbCr) { - if (jsimd_can_ycc_rgb()) - cconvert->pub.color_convert = jsimd_ycc_rgb_convert; - else { - cconvert->pub.color_convert = ycc_rgb_convert; - build_ycc_rgb_table(cinfo); - } - } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb_convert; - } else if (cinfo->jpeg_color_space == JCS_RGB) { - if (rgb_red[cinfo->out_color_space] == 0 && - rgb_green[cinfo->out_color_space] == 1 && - rgb_blue[cinfo->out_color_space] == 2 && - rgb_pixelsize[cinfo->out_color_space] == 3) - cconvert->pub.color_convert = null_convert; - else - cconvert->pub.color_convert = rgb_rgb_convert; - } else - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - break; - - case JCS_RGB565: - cinfo->out_color_components = 3; - if (cinfo->dither_mode == JDITHER_NONE) { - if (cinfo->jpeg_color_space == JCS_YCbCr) { - if (jsimd_can_ycc_rgb565()) - cconvert->pub.color_convert = jsimd_ycc_rgb565_convert; - else { - cconvert->pub.color_convert = ycc_rgb565_convert; - build_ycc_rgb_table(cinfo); - } - } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb565_convert; - } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_rgb565_convert; - } else - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - } else { - /* only ordered dithering is supported */ - if (cinfo->jpeg_color_space == JCS_YCbCr) { - cconvert->pub.color_convert = ycc_rgb565D_convert; - build_ycc_rgb_table(cinfo); - } else if (cinfo->jpeg_color_space == JCS_GRAYSCALE) { - cconvert->pub.color_convert = gray_rgb565D_convert; - } else if (cinfo->jpeg_color_space == JCS_RGB) { - cconvert->pub.color_convert = rgb_rgb565D_convert; - } else - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - } - break; - - case JCS_CMYK: - cinfo->out_color_components = 4; - if (cinfo->jpeg_color_space == JCS_YCCK) { - cconvert->pub.color_convert = ycck_cmyk_convert; - build_ycc_rgb_table(cinfo); - } else if (cinfo->jpeg_color_space == JCS_CMYK) { - cconvert->pub.color_convert = null_convert; - } else - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - break; - - default: - /* Permit null conversion to same output space */ - if (cinfo->out_color_space == cinfo->jpeg_color_space) { - cinfo->out_color_components = cinfo->num_components; - cconvert->pub.color_convert = null_convert; - } else /* unsupported non-null conversion */ - ERREXIT(cinfo, JERR_CONVERSION_NOTIMPL); - break; - } - - if (cinfo->quantize_colors) - cinfo->output_components = 1; /* single colormapped output component */ - else - cinfo->output_components = cinfo->out_color_components; -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdct.h leanify-0.4.3+git20181014/lib/mozjpeg/jdct.h --- leanify-0.4.3/lib/mozjpeg/jdct.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdct.h 2017-08-01 20:51:09.000000000 +0000 @@ -3,9 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2015, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This include file contains common declarations for the forward and * inverse DCT modules. These declarations are private to the DCT managers @@ -18,7 +19,7 @@ /* * A forward DCT routine is given a pointer to a work area of type DCTELEM[]; * the DCT is to be performed in-place in that buffer. Type DCTELEM is int - * for 8-bit samples, INT32 for 12-bit samples. (NOTE: Floating-point DCT + * for 8-bit samples, JLONG for 12-bit samples. (NOTE: Floating-point DCT * implementations use an array of type FAST_FLOAT, instead.) * The DCT inputs are expected to be signed (range +-CENTERJSAMPLE). * The DCT outputs are returned scaled up by a factor of 8; they therefore @@ -40,7 +41,7 @@ typedef unsigned int UDCTELEM2; #endif #else -typedef INT32 DCTELEM; /* must have 32 bits */ +typedef JLONG DCTELEM; /* must have 32 bits */ typedef unsigned long long UDCTELEM2; #endif @@ -67,7 +68,7 @@ typedef MULTIPLIER IFAST_MULT_TYPE; /* 16 bits is OK, use short if faster */ #define IFAST_SCALE_BITS 2 /* fractional bits in scale factors */ #else -typedef INT32 IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ +typedef JLONG IFAST_MULT_TYPE; /* need 32 bits for scaled quantizers */ #define IFAST_SCALE_BITS 13 /* fractional bits in scale factors */ #endif typedef FAST_FLOAT FLOAT_MULT_TYPE; /* preferred floating type */ @@ -89,63 +90,63 @@ /* Extern declarations for the forward and inverse DCT routines. */ -EXTERN(void) jpeg_fdct_islow (DCTELEM * data); -EXTERN(void) jpeg_fdct_ifast (DCTELEM * data); -EXTERN(void) jpeg_fdct_float (FAST_FLOAT * data); +EXTERN(void) jpeg_fdct_islow (DCTELEM *data); +EXTERN(void) jpeg_fdct_ifast (DCTELEM *data); +EXTERN(void) jpeg_fdct_float (FAST_FLOAT *data); EXTERN(void) jpeg_idct_islow - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_ifast - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_float - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_7x7 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_6x6 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_5x5 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_4x4 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_3x3 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_2x2 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_1x1 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_9x9 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_10x10 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_11x11 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_12x12 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_13x13 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_14x14 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_15x15 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jpeg_idct_16x16 - (j_decompress_ptr cinfo, jpeg_component_info * compptr, + (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); @@ -153,13 +154,13 @@ * Macros for handling fixed-point arithmetic; these are used by many * but not all of the DCT/IDCT modules. * - * All values are expected to be of type INT32. + * All values are expected to be of type JLONG. * Fractional constants are scaled left by CONST_BITS bits. * CONST_BITS is defined within each module using these macros, * and may differ from one module to the next. */ -#define ONE ((INT32) 1) +#define ONE ((JLONG) 1) #define CONST_SCALE (ONE << CONST_BITS) /* Convert a positive real constant to an integer scaled by CONST_SCALE. @@ -167,16 +168,16 @@ * thus causing a lot of useless floating-point operations at run time. */ -#define FIX(x) ((INT32) ((x) * CONST_SCALE + 0.5)) +#define FIX(x) ((JLONG) ((x) * CONST_SCALE + 0.5)) -/* Descale and correctly round an INT32 value that's scaled by N bits. +/* Descale and correctly round a JLONG value that's scaled by N bits. * We assume RIGHT_SHIFT rounds towards minus infinity, so adding * the fudge factor is correct for either sign of X. */ #define DESCALE(x,n) RIGHT_SHIFT((x) + (ONE << ((n)-1)), n) -/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. +/* Multiply a JLONG variable by a JLONG constant to yield a JLONG result. * This macro is used only when the two inputs will actually be no more than * 16 bits wide, so that a 16x16->32 bit multiply can be used instead of a * full 32x32 multiply. This provides a useful speedup on many machines. @@ -189,7 +190,7 @@ #define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT16) (const))) #endif #ifdef SHORTxLCONST_32 /* known to work with Microsoft C 6.0 */ -#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((INT32) (const))) +#define MULTIPLY16C16(var,const) (((INT16) (var)) * ((JLONG) (const))) #endif #ifndef MULTIPLY16C16 /* default definition */ diff -Nru leanify-0.4.3/lib/mozjpeg/jddctmgr.c leanify-0.4.3+git20181014/lib/mozjpeg/jddctmgr.c --- leanify-0.4.3/lib/mozjpeg/jddctmgr.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jddctmgr.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,349 +0,0 @@ -/* - * jddctmgr.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1996, Thomas G. Lane. - * Modified 2002-2010 by Guido Vollbeding. - * libjpeg-turbo Modifications: - * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, D. R. Commander. - * Copyright (C) 2013, MIPS Technologies, Inc., California - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the inverse-DCT management logic. - * This code selects a particular IDCT implementation to be used, - * and it performs related housekeeping chores. No code in this file - * is executed per IDCT step, only during output pass setup. - * - * Note that the IDCT routines are responsible for performing coefficient - * dequantization as well as the IDCT proper. This module sets up the - * dequantization multiplier table needed by the IDCT routine. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jdct.h" /* Private declarations for DCT subsystem */ -#include "jsimddct.h" -#include "jpegcomp.h" - - -/* - * The decompressor input side (jdinput.c) saves away the appropriate - * quantization table for each component at the start of the first scan - * involving that component. (This is necessary in order to correctly - * decode files that reuse Q-table slots.) - * When we are ready to make an output pass, the saved Q-table is converted - * to a multiplier table that will actually be used by the IDCT routine. - * The multiplier table contents are IDCT-method-dependent. To support - * application changes in IDCT method between scans, we can remake the - * multiplier tables if necessary. - * In buffered-image mode, the first output pass may occur before any data - * has been seen for some components, and thus before their Q-tables have - * been saved away. To handle this case, multiplier tables are preset - * to zeroes; the result of the IDCT will be a neutral gray level. - */ - - -/* Private subobject for this module */ - -typedef struct { - struct jpeg_inverse_dct pub; /* public fields */ - - /* This array contains the IDCT method code that each multiplier table - * is currently set up for, or -1 if it's not yet set up. - * The actual multiplier tables are pointed to by dct_table in the - * per-component comp_info structures. - */ - int cur_method[MAX_COMPONENTS]; -} my_idct_controller; - -typedef my_idct_controller * my_idct_ptr; - - -/* Allocated multiplier tables: big enough for any supported variant */ - -typedef union { - ISLOW_MULT_TYPE islow_array[DCTSIZE2]; -#ifdef DCT_IFAST_SUPPORTED - IFAST_MULT_TYPE ifast_array[DCTSIZE2]; -#endif -#ifdef DCT_FLOAT_SUPPORTED - FLOAT_MULT_TYPE float_array[DCTSIZE2]; -#endif -} multiplier_table; - - -/* The current scaled-IDCT routines require ISLOW-style multiplier tables, - * so be sure to compile that code if either ISLOW or SCALING is requested. - */ -#ifdef DCT_ISLOW_SUPPORTED -#define PROVIDE_ISLOW_TABLES -#else -#ifdef IDCT_SCALING_SUPPORTED -#define PROVIDE_ISLOW_TABLES -#endif -#endif - - -/* - * Prepare for an output pass. - * Here we select the proper IDCT routine for each component and build - * a matching multiplier table. - */ - -METHODDEF(void) -start_pass (j_decompress_ptr cinfo) -{ - my_idct_ptr idct = (my_idct_ptr) cinfo->idct; - int ci, i; - jpeg_component_info *compptr; - int method = 0; - inverse_DCT_method_ptr method_ptr = NULL; - JQUANT_TBL * qtbl; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Select the proper IDCT routine for this component's scaling */ - switch (compptr->_DCT_scaled_size) { -#ifdef IDCT_SCALING_SUPPORTED - case 1: - method_ptr = jpeg_idct_1x1; - method = JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case 2: - if (jsimd_can_idct_2x2()) - method_ptr = jsimd_idct_2x2; - else - method_ptr = jpeg_idct_2x2; - method = JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case 3: - method_ptr = jpeg_idct_3x3; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 4: - if (jsimd_can_idct_4x4()) - method_ptr = jsimd_idct_4x4; - else - method_ptr = jpeg_idct_4x4; - method = JDCT_ISLOW; /* jidctred uses islow-style table */ - break; - case 5: - method_ptr = jpeg_idct_5x5; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 6: -#if defined(__mips__) - if (jsimd_can_idct_6x6()) - method_ptr = jsimd_idct_6x6; - else -#endif - method_ptr = jpeg_idct_6x6; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 7: - method_ptr = jpeg_idct_7x7; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; -#endif - case DCTSIZE: - switch (cinfo->dct_method) { -#ifdef DCT_ISLOW_SUPPORTED - case JDCT_ISLOW: - if (jsimd_can_idct_islow()) - method_ptr = jsimd_idct_islow; - else - method_ptr = jpeg_idct_islow; - method = JDCT_ISLOW; - break; -#endif -#ifdef DCT_IFAST_SUPPORTED - case JDCT_IFAST: - if (jsimd_can_idct_ifast()) - method_ptr = jsimd_idct_ifast; - else - method_ptr = jpeg_idct_ifast; - method = JDCT_IFAST; - break; -#endif -#ifdef DCT_FLOAT_SUPPORTED - case JDCT_FLOAT: - if (jsimd_can_idct_float()) - method_ptr = jsimd_idct_float; - else - method_ptr = jpeg_idct_float; - method = JDCT_FLOAT; - break; -#endif - default: - ERREXIT(cinfo, JERR_NOT_COMPILED); - break; - } - break; - case 9: - method_ptr = jpeg_idct_9x9; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 10: - method_ptr = jpeg_idct_10x10; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 11: - method_ptr = jpeg_idct_11x11; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 12: -#if defined(__mips__) - if (jsimd_can_idct_12x12()) - method_ptr = jsimd_idct_12x12; - else -#endif - method_ptr = jpeg_idct_12x12; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 13: - method_ptr = jpeg_idct_13x13; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 14: - method_ptr = jpeg_idct_14x14; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 15: - method_ptr = jpeg_idct_15x15; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - case 16: - method_ptr = jpeg_idct_16x16; - method = JDCT_ISLOW; /* jidctint uses islow-style table */ - break; - default: - ERREXIT1(cinfo, JERR_BAD_DCTSIZE, compptr->_DCT_scaled_size); - break; - } - idct->pub.inverse_DCT[ci] = method_ptr; - /* Create multiplier table from quant table. - * However, we can skip this if the component is uninteresting - * or if we already built the table. Also, if no quant table - * has yet been saved for the component, we leave the - * multiplier table all-zero; we'll be reading zeroes from the - * coefficient controller's buffer anyway. - */ - if (! compptr->component_needed || idct->cur_method[ci] == method) - continue; - qtbl = compptr->quant_table; - if (qtbl == NULL) /* happens if no data yet for component */ - continue; - idct->cur_method[ci] = method; - switch (method) { -#ifdef PROVIDE_ISLOW_TABLES - case JDCT_ISLOW: - { - /* For LL&M IDCT method, multipliers are equal to raw quantization - * coefficients, but are stored as ints to ensure access efficiency. - */ - ISLOW_MULT_TYPE * ismtbl = (ISLOW_MULT_TYPE *) compptr->dct_table; - for (i = 0; i < DCTSIZE2; i++) { - ismtbl[i] = (ISLOW_MULT_TYPE) qtbl->quantval[i]; - } - } - break; -#endif -#ifdef DCT_IFAST_SUPPORTED - case JDCT_IFAST: - { - /* For AA&N IDCT method, multipliers are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - * For integer operation, the multiplier table is to be scaled by - * IFAST_SCALE_BITS. - */ - IFAST_MULT_TYPE * ifmtbl = (IFAST_MULT_TYPE *) compptr->dct_table; -#define CONST_BITS 14 - static const INT16 aanscales[DCTSIZE2] = { - /* precomputed values scaled up by 14 bits */ - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, - 22725, 31521, 29692, 26722, 22725, 17855, 12299, 6270, - 21407, 29692, 27969, 25172, 21407, 16819, 11585, 5906, - 19266, 26722, 25172, 22654, 19266, 15137, 10426, 5315, - 16384, 22725, 21407, 19266, 16384, 12873, 8867, 4520, - 12873, 17855, 16819, 15137, 12873, 10114, 6967, 3552, - 8867, 12299, 11585, 10426, 8867, 6967, 4799, 2446, - 4520, 6270, 5906, 5315, 4520, 3552, 2446, 1247 - }; - SHIFT_TEMPS - - for (i = 0; i < DCTSIZE2; i++) { - ifmtbl[i] = (IFAST_MULT_TYPE) - DESCALE(MULTIPLY16V16((INT32) qtbl->quantval[i], - (INT32) aanscales[i]), - CONST_BITS-IFAST_SCALE_BITS); - } - } - break; -#endif -#ifdef DCT_FLOAT_SUPPORTED - case JDCT_FLOAT: - { - /* For float AA&N IDCT method, multipliers are equal to quantization - * coefficients scaled by scalefactor[row]*scalefactor[col], where - * scalefactor[0] = 1 - * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 - */ - FLOAT_MULT_TYPE * fmtbl = (FLOAT_MULT_TYPE *) compptr->dct_table; - int row, col; - static const double aanscalefactor[DCTSIZE] = { - 1.0, 1.387039845, 1.306562965, 1.175875602, - 1.0, 0.785694958, 0.541196100, 0.275899379 - }; - - i = 0; - for (row = 0; row < DCTSIZE; row++) { - for (col = 0; col < DCTSIZE; col++) { - fmtbl[i] = (FLOAT_MULT_TYPE) - ((double) qtbl->quantval[i] * - aanscalefactor[row] * aanscalefactor[col]); - i++; - } - } - } - break; -#endif - default: - ERREXIT(cinfo, JERR_NOT_COMPILED); - break; - } - } -} - - -/* - * Initialize IDCT manager. - */ - -GLOBAL(void) -jinit_inverse_dct (j_decompress_ptr cinfo) -{ - my_idct_ptr idct; - int ci; - jpeg_component_info *compptr; - - idct = (my_idct_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_idct_controller)); - cinfo->idct = (struct jpeg_inverse_dct *) idct; - idct->pub.start_pass = start_pass; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Allocate and pre-zero a multiplier table for each component */ - compptr->dct_table = - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(multiplier_table)); - MEMZERO(compptr->dct_table, sizeof(multiplier_table)); - /* Mark multiplier table not yet set up for any method */ - idct->cur_method[ci] = -1; - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdhuff.c leanify-0.4.3+git20181014/lib/mozjpeg/jdhuff.c --- leanify-0.4.3/lib/mozjpeg/jdhuff.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdhuff.c 2018-03-21 11:12:12.000000000 +0000 @@ -4,8 +4,9 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2009-2011, 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains Huffman entropy decoding routines. * @@ -66,20 +67,20 @@ unsigned int restarts_to_go; /* MCUs left in this restart interval */ /* Pointers to derived tables (these workspaces have image lifespan) */ - d_derived_tbl * dc_derived_tbls[NUM_HUFF_TBLS]; - d_derived_tbl * ac_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl *dc_derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl *ac_derived_tbls[NUM_HUFF_TBLS]; /* Precalculated info set up by start_pass for use in decode_mcu: */ /* Pointers to derived tables to be used for each block within an MCU */ - d_derived_tbl * dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; - d_derived_tbl * ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl *dc_cur_tbls[D_MAX_BLOCKS_IN_MCU]; + d_derived_tbl *ac_cur_tbls[D_MAX_BLOCKS_IN_MCU]; /* Whether we care about the DC and AC coefficient values for each block */ boolean dc_needed[D_MAX_BLOCKS_IN_MCU]; boolean ac_needed[D_MAX_BLOCKS_IN_MCU]; } huff_entropy_decoder; -typedef huff_entropy_decoder * huff_entropy_ptr; +typedef huff_entropy_decoder *huff_entropy_ptr; /* @@ -91,7 +92,8 @@ { huff_entropy_ptr entropy = (huff_entropy_ptr) cinfo->entropy; int ci, blkn, dctbl, actbl; - jpeg_component_info * compptr; + d_derived_tbl **pdtbl; + jpeg_component_info *compptr; /* Check that the scan parameters Ss, Se, Ah/Al are OK for sequential JPEG. * This ought to be an error condition, but we make it a warning because @@ -107,10 +109,10 @@ actbl = compptr->ac_tbl_no; /* Compute derived values for Huffman tables */ /* We may do this more than once for a table, but it's not expensive */ - jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, - & entropy->dc_derived_tbls[dctbl]); - jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, - & entropy->ac_derived_tbls[actbl]); + pdtbl = (d_derived_tbl **)(entropy->dc_derived_tbls) + dctbl; + jpeg_make_d_derived_tbl(cinfo, TRUE, dctbl, pdtbl); + pdtbl = (d_derived_tbl **)(entropy->ac_derived_tbls) + actbl; + jpeg_make_d_derived_tbl(cinfo, FALSE, actbl, pdtbl); /* Initialize DC predictions to 0 */ entropy->saved.last_dc_val[ci] = 0; } @@ -151,7 +153,7 @@ GLOBAL(void) jpeg_make_d_derived_tbl (j_decompress_ptr cinfo, boolean isDC, int tblno, - d_derived_tbl ** pdtbl) + d_derived_tbl **pdtbl) { JHUFF_TBL *htbl; d_derived_tbl *dtbl; @@ -208,7 +210,7 @@ /* code is now 1 more than the last code used for codelength si; but * it must still fit in si bits, since no code is allowed to be all ones. */ - if (((INT32) code) >= (((INT32) 1) << si)) + if (((JLONG) code) >= (((JLONG) 1) << si)) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); code <<= 1; si++; @@ -222,7 +224,7 @@ /* valoffset[l] = huffval[] index of 1st symbol of code length l, * minus the minimum code of length l */ - dtbl->valoffset[l] = (INT32) p - (INT32) huffcode[p]; + dtbl->valoffset[l] = (JLONG) p - (JLONG) huffcode[p]; p += htbl->bits[l]; dtbl->maxcode[l] = huffcode[p-1]; /* maximum code of length l */ } else { @@ -294,13 +296,13 @@ GLOBAL(boolean) -jpeg_fill_bit_buffer (bitread_working_state * state, +jpeg_fill_bit_buffer (bitread_working_state *state, register bit_buf_type get_buffer, register int bits_left, int nbits) /* Load up the bit buffer to a depth of at least nbits */ { /* Copy heavily used state fields into locals (hopefully registers) */ - register const JOCTET * next_input_byte = state->next_input_byte; + register const JOCTET *next_input_byte = state->next_input_byte; register size_t bytes_in_buffer = state->bytes_in_buffer; j_decompress_ptr cinfo = state->cinfo; @@ -419,11 +421,11 @@ } \ } -#if __WORDSIZE == 64 || defined(_WIN64) +#if SIZEOF_SIZE_T==8 || defined(_WIN64) /* Pre-fetch 48 bytes, because the holding register is 64-bit */ #define FILL_BIT_BUFFER_FAST \ - if (bits_left < 16) { \ + if (bits_left <= 16) { \ GET_BYTE GET_BYTE GET_BYTE GET_BYTE GET_BYTE GET_BYTE \ } @@ -431,7 +433,7 @@ /* Pre-fetch 16 bytes, because the holding register is 32-bit */ #define FILL_BIT_BUFFER_FAST \ - if (bits_left < 16) { \ + if (bits_left <= 16) { \ GET_BYTE GET_BYTE \ } @@ -444,12 +446,12 @@ */ GLOBAL(int) -jpeg_huff_decode (bitread_working_state * state, +jpeg_huff_decode (bitread_working_state *state, register bit_buf_type get_buffer, register int bits_left, - d_derived_tbl * htbl, int min_bits) + d_derived_tbl *htbl, int min_bits) { register int l = min_bits; - register INT32 code; + register JLONG code; /* HUFF_DECODE has determined that the code is at least min_bits */ /* bits long, so fetch that many bits in one swoop. */ @@ -490,7 +492,8 @@ #define AVOID_TABLES #ifdef AVOID_TABLES -#define HUFF_EXTEND(x,s) ((x) + ((((x) - (1<<((s)-1))) >> 31) & (((-1)<<(s)) + 1))) +#define NEG_1 ((unsigned int)-1) +#define HUFF_EXTEND(x,s) ((x) + ((((x) - (1<<((s)-1))) >> 31) & (((NEG_1)<<(s)) + 1))) #else @@ -562,9 +565,9 @@ ASSIGN_STATE(state, entropy->saved); for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { - JBLOCKROW block = MCU_data[blkn]; - d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; - d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + JBLOCKROW block = MCU_data ? MCU_data[blkn] : NULL; + d_derived_tbl *dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl *actbl = entropy->ac_cur_tbls[blkn]; register int s, k, r; /* Decode a single block's worth of coefficients */ @@ -582,11 +585,13 @@ int ci = cinfo->MCU_membership[blkn]; s += state.last_dc_val[ci]; state.last_dc_val[ci] = s; - /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ - (*block)[0] = (JCOEF) s; + if (block) { + /* Output the DC coefficient (assumes jpeg_natural_order[0] = 0) */ + (*block)[0] = (JCOEF) s; + } } - if (entropy->ac_needed[blkn]) { + if (entropy->ac_needed[blkn] && block) { /* Section F.2.2.2: decode the AC coefficients */ /* Since zeroes are skipped, output area must be cleared beforehand */ @@ -659,9 +664,9 @@ ASSIGN_STATE(state, entropy->saved); for (blkn = 0; blkn < cinfo->blocks_in_MCU; blkn++) { - JBLOCKROW block = MCU_data[blkn]; - d_derived_tbl * dctbl = entropy->dc_cur_tbls[blkn]; - d_derived_tbl * actbl = entropy->ac_cur_tbls[blkn]; + JBLOCKROW block = MCU_data ? MCU_data[blkn] : NULL; + d_derived_tbl *dctbl = entropy->dc_cur_tbls[blkn]; + d_derived_tbl *actbl = entropy->ac_cur_tbls[blkn]; register int s, k, r, l; HUFF_DECODE_FAST(s, l, dctbl); @@ -675,10 +680,11 @@ int ci = cinfo->MCU_membership[blkn]; s += state.last_dc_val[ci]; state.last_dc_val[ci] = s; - (*block)[0] = (JCOEF) s; + if (block) + (*block)[0] = (JCOEF) s; } - if (entropy->ac_needed[blkn]) { + if (entropy->ac_needed[blkn] && block) { for (k = 1; k < DCTSIZE2; k++) { HUFF_DECODE_FAST(s, l, actbl); @@ -744,7 +750,7 @@ * this module, since we'll just re-assign them on the next call.) */ -#define BUFSIZE (DCTSIZE2 * 2) +#define BUFSIZE (DCTSIZE2 * 8) METHODDEF(boolean) decode_mcu (j_decompress_ptr cinfo, JBLOCKROW *MCU_data) diff -Nru leanify-0.4.3/lib/mozjpeg/jdhuff.h leanify-0.4.3+git20181014/lib/mozjpeg/jdhuff.h --- leanify-0.4.3/lib/mozjpeg/jdhuff.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdhuff.h 2017-08-01 20:51:09.000000000 +0000 @@ -4,14 +4,17 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010-2011, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2010-2011, 2015-2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains declarations for Huffman entropy decoding routines * that are shared between the sequential decoder (jdhuff.c) and the * progressive decoder (jdphuff.c). No other modules need to see these. */ +#include "jconfigint.h" + /* Derived data constructed for each Huffman table */ @@ -19,9 +22,9 @@ typedef struct { /* Basic tables: (element [0] of each array is unused) */ - INT32 maxcode[18]; /* largest code of length k (-1 if none) */ + JLONG maxcode[18]; /* largest code of length k (-1 if none) */ /* (maxcode[17] is a sentinel to ensure jpeg_huff_decode terminates) */ - INT32 valoffset[18]; /* huffval[] offset for codes of length k */ + JLONG valoffset[18]; /* huffval[] offset for codes of length k */ /* valoffset[k] = huffval[] index of 1st symbol of code length k, less * the smallest code of length k; so given a code of length k, the * corresponding symbol is huffval[code + valoffset[k]] @@ -67,14 +70,18 @@ * necessary. */ -#if __WORDSIZE == 64 || defined(_WIN64) +#if !defined(_WIN32) && !defined(SIZEOF_SIZE_T) +#error Cannot determine word size +#endif + +#if SIZEOF_SIZE_T==8 || defined(_WIN64) -typedef size_t bit_buf_type; /* type of bit-extraction buffer */ +typedef size_t bit_buf_type; /* type of bit-extraction buffer */ #define BIT_BUF_SIZE 64 /* size of buffer in bits */ #else -typedef INT32 bit_buf_type; /* type of bit-extraction buffer */ +typedef unsigned long bit_buf_type; /* type of bit-extraction buffer */ #define BIT_BUF_SIZE 32 /* size of buffer in bits */ #endif @@ -94,7 +101,7 @@ typedef struct { /* Bitreading working state within an MCU */ /* Current data source location */ /* We need a copy, rather than munging the original, in case of suspension */ - const JOCTET * next_input_byte; /* => next byte to read from source */ + const JOCTET *next_input_byte; /* => next byte to read from source */ size_t bytes_in_buffer; /* # of bytes remaining in source buffer */ /* Bit input buffer --- note these values are kept in register variables, * not in this struct, inside the inner loops. @@ -159,7 +166,7 @@ /* Load up the bit buffer to a depth of at least nbits */ EXTERN(boolean) jpeg_fill_bit_buffer - (bitread_working_state * state, register bit_buf_type get_buffer, + (bitread_working_state *state, register bit_buf_type get_buffer, register int bits_left, int nbits); @@ -223,5 +230,5 @@ /* Out-of-line case for Huffman code fetching */ EXTERN(int) jpeg_huff_decode - (bitread_working_state * state, register bit_buf_type get_buffer, - register int bits_left, d_derived_tbl * htbl, int min_bits); + (bitread_working_state *state, register bit_buf_type get_buffer, + register int bits_left, d_derived_tbl *htbl, int min_bits); diff -Nru leanify-0.4.3/lib/mozjpeg/jdinput.c leanify-0.4.3+git20181014/lib/mozjpeg/jdinput.c --- leanify-0.4.3/lib/mozjpeg/jdinput.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdinput.c 2017-08-01 20:51:09.000000000 +0000 @@ -4,8 +4,10 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2010, 2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains input control logic for the JPEG decompressor. * These routines are concerned with controlling the decompressor's input @@ -27,7 +29,7 @@ boolean inheaders; /* TRUE until first SOS is reached */ } my_input_controller; -typedef my_input_controller * my_inputctl_ptr; +typedef my_input_controller *my_inputctl_ptr; /* Forward declarations */ @@ -104,6 +106,11 @@ compptr->height_in_blocks = (JDIMENSION) jdiv_round_up((long) cinfo->image_height * (long) compptr->v_samp_factor, (long) (cinfo->max_v_samp_factor * DCTSIZE)); + /* Set the first and last MCU columns to decompress from multi-scan images. + * By default, decompress all of the MCU columns. + */ + cinfo->master->first_MCU_col[ci] = 0; + cinfo->master->last_MCU_col[ci] = compptr->width_in_blocks - 1; /* downsampled_width and downsampled_height will also be overridden by * jdmaster.c if we are doing full decompression. The transcoder library * doesn't use these values, but the calling application might. @@ -238,7 +245,7 @@ { int ci, qtblno; jpeg_component_info *compptr; - JQUANT_TBL * qtbl; + JQUANT_TBL *qtbl; for (ci = 0; ci < cinfo->comps_in_scan; ci++) { compptr = cinfo->cur_comp_info[ci]; diff -Nru leanify-0.4.3/lib/mozjpeg/jdmainct.c leanify-0.4.3+git20181014/lib/mozjpeg/jdmainct.c --- leanify-0.4.3/lib/mozjpeg/jdmainct.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmainct.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,515 +0,0 @@ -/* - * jdmainct.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1996, Thomas G. Lane. - * libjpeg-turbo Modifications: - * Copyright (C) 2010, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the main buffer controller for decompression. - * The main buffer lies between the JPEG decompressor proper and the - * post-processor; it holds downsampled data in the JPEG colorspace. - * - * Note that this code is bypassed in raw-data mode, since the application - * supplies the equivalent of the main buffer in that case. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jpegcomp.h" - - -/* - * In the current system design, the main buffer need never be a full-image - * buffer; any full-height buffers will be found inside the coefficient or - * postprocessing controllers. Nonetheless, the main controller is not - * trivial. Its responsibility is to provide context rows for upsampling/ - * rescaling, and doing this in an efficient fashion is a bit tricky. - * - * Postprocessor input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - * sample rows of each component. (We require DCT_scaled_size values to be - * chosen such that these numbers are integers. In practice DCT_scaled_size - * values will likely be powers of two, so we actually have the stronger - * condition that DCT_scaled_size / min_DCT_scaled_size is an integer.) - * Upsampling will typically produce max_v_samp_factor pixel rows from each - * row group (times any additional scale factor that the upsampler is - * applying). - * - * The coefficient controller will deliver data to us one iMCU row at a time; - * each iMCU row contains v_samp_factor * DCT_scaled_size sample rows, or - * exactly min_DCT_scaled_size row groups. (This amount of data corresponds - * to one row of MCUs when the image is fully interleaved.) Note that the - * number of sample rows varies across components, but the number of row - * groups does not. Some garbage sample rows may be included in the last iMCU - * row at the bottom of the image. - * - * Depending on the vertical scaling algorithm used, the upsampler may need - * access to the sample row(s) above and below its current input row group. - * The upsampler is required to set need_context_rows TRUE at global selection - * time if so. When need_context_rows is FALSE, this controller can simply - * obtain one iMCU row at a time from the coefficient controller and dole it - * out as row groups to the postprocessor. - * - * When need_context_rows is TRUE, this controller guarantees that the buffer - * passed to postprocessing contains at least one row group's worth of samples - * above and below the row group(s) being processed. Note that the context - * rows "above" the first passed row group appear at negative row offsets in - * the passed buffer. At the top and bottom of the image, the required - * context rows are manufactured by duplicating the first or last real sample - * row; this avoids having special cases in the upsampling inner loops. - * - * The amount of context is fixed at one row group just because that's a - * convenient number for this controller to work with. The existing - * upsamplers really only need one sample row of context. An upsampler - * supporting arbitrary output rescaling might wish for more than one row - * group of context when shrinking the image; tough, we don't handle that. - * (This is justified by the assumption that downsizing will be handled mostly - * by adjusting the DCT_scaled_size values, so that the actual scale factor at - * the upsample step needn't be much less than one.) - * - * To provide the desired context, we have to retain the last two row groups - * of one iMCU row while reading in the next iMCU row. (The last row group - * can't be processed until we have another row group for its below-context, - * and so we have to save the next-to-last group too for its above-context.) - * We could do this most simply by copying data around in our buffer, but - * that'd be very slow. We can avoid copying any data by creating a rather - * strange pointer structure. Here's how it works. We allocate a workspace - * consisting of M+2 row groups (where M = min_DCT_scaled_size is the number - * of row groups per iMCU row). We create two sets of redundant pointers to - * the workspace. Labeling the physical row groups 0 to M+1, the synthesized - * pointer lists look like this: - * M+1 M-1 - * master pointer --> 0 master pointer --> 0 - * 1 1 - * ... ... - * M-3 M-3 - * M-2 M - * M-1 M+1 - * M M-2 - * M+1 M-1 - * 0 0 - * We read alternate iMCU rows using each master pointer; thus the last two - * row groups of the previous iMCU row remain un-overwritten in the workspace. - * The pointer lists are set up so that the required context rows appear to - * be adjacent to the proper places when we pass the pointer lists to the - * upsampler. - * - * The above pictures describe the normal state of the pointer lists. - * At top and bottom of the image, we diddle the pointer lists to duplicate - * the first or last sample row as necessary (this is cheaper than copying - * sample rows around). - * - * This scheme breaks down if M < 2, ie, min_DCT_scaled_size is 1. In that - * situation each iMCU row provides only one row group so the buffering logic - * must be different (eg, we must read two iMCU rows before we can emit the - * first row group). For now, we simply do not support providing context - * rows when min_DCT_scaled_size is 1. That combination seems unlikely to - * be worth providing --- if someone wants a 1/8th-size preview, they probably - * want it quick and dirty, so a context-free upsampler is sufficient. - */ - - -/* Private buffer controller object */ - -typedef struct { - struct jpeg_d_main_controller pub; /* public fields */ - - /* Pointer to allocated workspace (M or M+2 row groups). */ - JSAMPARRAY buffer[MAX_COMPONENTS]; - - boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ - JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ - - /* Remaining fields are only used in the context case. */ - - /* These are the master pointers to the funny-order pointer lists. */ - JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ - - int whichptr; /* indicates which pointer set is now in use */ - int context_state; /* process_data state machine status */ - JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ - JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ -} my_main_controller; - -typedef my_main_controller * my_main_ptr; - -/* context_state values: */ -#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ -#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ -#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ - - -/* Forward declarations */ -METHODDEF(void) process_data_simple_main - (j_decompress_ptr cinfo, JSAMPARRAY output_buf, - JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); -METHODDEF(void) process_data_context_main - (j_decompress_ptr cinfo, JSAMPARRAY output_buf, - JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); -#ifdef QUANT_2PASS_SUPPORTED -METHODDEF(void) process_data_crank_post - (j_decompress_ptr cinfo, JSAMPARRAY output_buf, - JDIMENSION *out_row_ctr, JDIMENSION out_rows_avail); -#endif - - -LOCAL(void) -alloc_funny_pointers (j_decompress_ptr cinfo) -/* Allocate space for the funny pointer lists. - * This is done only once, not once per pass. - */ -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - int ci, rgroup; - int M = cinfo->_min_DCT_scaled_size; - jpeg_component_info *compptr; - JSAMPARRAY xbuf; - - /* Get top-level space for component array pointers. - * We alloc both arrays with one call to save a few cycles. - */ - main_ptr->xbuffer[0] = (JSAMPIMAGE) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - cinfo->num_components * 2 * sizeof(JSAMPARRAY)); - main_ptr->xbuffer[1] = main_ptr->xbuffer[0] + cinfo->num_components; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - /* Get space for pointer lists --- M+4 row groups in each list. - * We alloc both pointer lists with one call to save a few cycles. - */ - xbuf = (JSAMPARRAY) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - 2 * (rgroup * (M + 4)) * sizeof(JSAMPROW)); - xbuf += rgroup; /* want one row group at negative offsets */ - main_ptr->xbuffer[0][ci] = xbuf; - xbuf += rgroup * (M + 4); - main_ptr->xbuffer[1][ci] = xbuf; - } -} - - -LOCAL(void) -make_funny_pointers (j_decompress_ptr cinfo) -/* Create the funny pointer lists discussed in the comments above. - * The actual workspace is already allocated (in main_ptr->buffer), - * and the space for the pointer lists is allocated too. - * This routine just fills in the curiously ordered lists. - * This will be repeated at the beginning of each pass. - */ -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - int ci, i, rgroup; - int M = cinfo->_min_DCT_scaled_size; - jpeg_component_info *compptr; - JSAMPARRAY buf, xbuf0, xbuf1; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - xbuf0 = main_ptr->xbuffer[0][ci]; - xbuf1 = main_ptr->xbuffer[1][ci]; - /* First copy the workspace pointers as-is */ - buf = main_ptr->buffer[ci]; - for (i = 0; i < rgroup * (M + 2); i++) { - xbuf0[i] = xbuf1[i] = buf[i]; - } - /* In the second list, put the last four row groups in swapped order */ - for (i = 0; i < rgroup * 2; i++) { - xbuf1[rgroup*(M-2) + i] = buf[rgroup*M + i]; - xbuf1[rgroup*M + i] = buf[rgroup*(M-2) + i]; - } - /* The wraparound pointers at top and bottom will be filled later - * (see set_wraparound_pointers, below). Initially we want the "above" - * pointers to duplicate the first actual data line. This only needs - * to happen in xbuffer[0]. - */ - for (i = 0; i < rgroup; i++) { - xbuf0[i - rgroup] = xbuf0[0]; - } - } -} - - -LOCAL(void) -set_wraparound_pointers (j_decompress_ptr cinfo) -/* Set up the "wraparound" pointers at top and bottom of the pointer lists. - * This changes the pointer list state from top-of-image to the normal state. - */ -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - int ci, i, rgroup; - int M = cinfo->_min_DCT_scaled_size; - jpeg_component_info *compptr; - JSAMPARRAY xbuf0, xbuf1; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - xbuf0 = main_ptr->xbuffer[0][ci]; - xbuf1 = main_ptr->xbuffer[1][ci]; - for (i = 0; i < rgroup; i++) { - xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; - xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; - xbuf0[rgroup*(M+2) + i] = xbuf0[i]; - xbuf1[rgroup*(M+2) + i] = xbuf1[i]; - } - } -} - - -LOCAL(void) -set_bottom_pointers (j_decompress_ptr cinfo) -/* Change the pointer lists to duplicate the last sample row at the bottom - * of the image. whichptr indicates which xbuffer holds the final iMCU row. - * Also sets rowgroups_avail to indicate number of nondummy row groups in row. - */ -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - int ci, i, rgroup, iMCUheight, rows_left; - jpeg_component_info *compptr; - JSAMPARRAY xbuf; - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Count sample rows in one iMCU row and in one row group */ - iMCUheight = compptr->v_samp_factor * compptr->_DCT_scaled_size; - rgroup = iMCUheight / cinfo->_min_DCT_scaled_size; - /* Count nondummy sample rows remaining for this component */ - rows_left = (int) (compptr->downsampled_height % (JDIMENSION) iMCUheight); - if (rows_left == 0) rows_left = iMCUheight; - /* Count nondummy row groups. Should get same answer for each component, - * so we need only do it once. - */ - if (ci == 0) { - main_ptr->rowgroups_avail = (JDIMENSION) ((rows_left-1) / rgroup + 1); - } - /* Duplicate the last real sample row rgroup*2 times; this pads out the - * last partial rowgroup and ensures at least one full rowgroup of context. - */ - xbuf = main_ptr->xbuffer[main_ptr->whichptr][ci]; - for (i = 0; i < rgroup * 2; i++) { - xbuf[rows_left + i] = xbuf[rows_left-1]; - } - } -} - - -/* - * Initialize for a processing pass. - */ - -METHODDEF(void) -start_pass_main (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - - switch (pass_mode) { - case JBUF_PASS_THRU: - if (cinfo->upsample->need_context_rows) { - main_ptr->pub.process_data = process_data_context_main; - make_funny_pointers(cinfo); /* Create the xbuffer[] lists */ - main_ptr->whichptr = 0; /* Read first iMCU row into xbuffer[0] */ - main_ptr->context_state = CTX_PREPARE_FOR_IMCU; - main_ptr->iMCU_row_ctr = 0; - } else { - /* Simple case with no context needed */ - main_ptr->pub.process_data = process_data_simple_main; - } - main_ptr->buffer_full = FALSE; /* Mark buffer empty */ - main_ptr->rowgroup_ctr = 0; - break; -#ifdef QUANT_2PASS_SUPPORTED - case JBUF_CRANK_DEST: - /* For last pass of 2-pass quantization, just crank the postprocessor */ - main_ptr->pub.process_data = process_data_crank_post; - break; -#endif - default: - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - break; - } -} - - -/* - * Process some data. - * This handles the simple case where no context is required. - */ - -METHODDEF(void) -process_data_simple_main (j_decompress_ptr cinfo, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - JDIMENSION rowgroups_avail; - - /* Read input data if we haven't filled the main buffer yet */ - if (! main_ptr->buffer_full) { - if (! (*cinfo->coef->decompress_data) (cinfo, main_ptr->buffer)) - return; /* suspension forced, can do nothing more */ - main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ - } - - /* There are always min_DCT_scaled_size row groups in an iMCU row. */ - rowgroups_avail = (JDIMENSION) cinfo->_min_DCT_scaled_size; - /* Note: at the bottom of the image, we may pass extra garbage row groups - * to the postprocessor. The postprocessor has to check for bottom - * of image anyway (at row resolution), so no point in us doing it too. - */ - - /* Feed the postprocessor */ - (*cinfo->post->post_process_data) (cinfo, main_ptr->buffer, - &main_ptr->rowgroup_ctr, rowgroups_avail, - output_buf, out_row_ctr, out_rows_avail); - - /* Has postprocessor consumed all the data yet? If so, mark buffer empty */ - if (main_ptr->rowgroup_ctr >= rowgroups_avail) { - main_ptr->buffer_full = FALSE; - main_ptr->rowgroup_ctr = 0; - } -} - - -/* - * Process some data. - * This handles the case where context rows must be provided. - */ - -METHODDEF(void) -process_data_context_main (j_decompress_ptr cinfo, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_main_ptr main_ptr = (my_main_ptr) cinfo->main; - - /* Read input data if we haven't filled the main buffer yet */ - if (! main_ptr->buffer_full) { - if (! (*cinfo->coef->decompress_data) (cinfo, - main_ptr->xbuffer[main_ptr->whichptr])) - return; /* suspension forced, can do nothing more */ - main_ptr->buffer_full = TRUE; /* OK, we have an iMCU row to work with */ - main_ptr->iMCU_row_ctr++; /* count rows received */ - } - - /* Postprocessor typically will not swallow all the input data it is handed - * in one call (due to filling the output buffer first). Must be prepared - * to exit and restart. This switch lets us keep track of how far we got. - * Note that each case falls through to the next on successful completion. - */ - switch (main_ptr->context_state) { - case CTX_POSTPONED_ROW: - /* Call postprocessor using previously set pointers for postponed row */ - (*cinfo->post->post_process_data) (cinfo, main_ptr->xbuffer[main_ptr->whichptr], - &main_ptr->rowgroup_ctr, main_ptr->rowgroups_avail, - output_buf, out_row_ctr, out_rows_avail); - if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) - return; /* Need to suspend */ - main_ptr->context_state = CTX_PREPARE_FOR_IMCU; - if (*out_row_ctr >= out_rows_avail) - return; /* Postprocessor exactly filled output buf */ - /*FALLTHROUGH*/ - case CTX_PREPARE_FOR_IMCU: - /* Prepare to process first M-1 row groups of this iMCU row */ - main_ptr->rowgroup_ctr = 0; - main_ptr->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size - 1); - /* Check for bottom of image: if so, tweak pointers to "duplicate" - * the last sample row, and adjust rowgroups_avail to ignore padding rows. - */ - if (main_ptr->iMCU_row_ctr == cinfo->total_iMCU_rows) - set_bottom_pointers(cinfo); - main_ptr->context_state = CTX_PROCESS_IMCU; - /*FALLTHROUGH*/ - case CTX_PROCESS_IMCU: - /* Call postprocessor using previously set pointers */ - (*cinfo->post->post_process_data) (cinfo, main_ptr->xbuffer[main_ptr->whichptr], - &main_ptr->rowgroup_ctr, main_ptr->rowgroups_avail, - output_buf, out_row_ctr, out_rows_avail); - if (main_ptr->rowgroup_ctr < main_ptr->rowgroups_avail) - return; /* Need to suspend */ - /* After the first iMCU, change wraparound pointers to normal state */ - if (main_ptr->iMCU_row_ctr == 1) - set_wraparound_pointers(cinfo); - /* Prepare to load new iMCU row using other xbuffer list */ - main_ptr->whichptr ^= 1; /* 0=>1 or 1=>0 */ - main_ptr->buffer_full = FALSE; - /* Still need to process last row group of this iMCU row, */ - /* which is saved at index M+1 of the other xbuffer */ - main_ptr->rowgroup_ctr = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 1); - main_ptr->rowgroups_avail = (JDIMENSION) (cinfo->_min_DCT_scaled_size + 2); - main_ptr->context_state = CTX_POSTPONED_ROW; - } -} - - -/* - * Process some data. - * Final pass of two-pass quantization: just call the postprocessor. - * Source data will be the postprocessor controller's internal buffer. - */ - -#ifdef QUANT_2PASS_SUPPORTED - -METHODDEF(void) -process_data_crank_post (j_decompress_ptr cinfo, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - (*cinfo->post->post_process_data) (cinfo, (JSAMPIMAGE) NULL, - (JDIMENSION *) NULL, (JDIMENSION) 0, - output_buf, out_row_ctr, out_rows_avail); -} - -#endif /* QUANT_2PASS_SUPPORTED */ - - -/* - * Initialize main buffer controller. - */ - -GLOBAL(void) -jinit_d_main_controller (j_decompress_ptr cinfo, boolean need_full_buffer) -{ - my_main_ptr main_ptr; - int ci, rgroup, ngroups; - jpeg_component_info *compptr; - - main_ptr = (my_main_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_main_controller)); - cinfo->main = (struct jpeg_d_main_controller *) main_ptr; - main_ptr->pub.start_pass = start_pass_main; - - if (need_full_buffer) /* shouldn't happen */ - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - - /* Allocate the workspace. - * ngroups is the number of row groups we need. - */ - if (cinfo->upsample->need_context_rows) { - if (cinfo->_min_DCT_scaled_size < 2) /* unsupported, see comments above */ - ERREXIT(cinfo, JERR_NOTIMPL); - alloc_funny_pointers(cinfo); /* Alloc space for xbuffer[] lists */ - ngroups = cinfo->_min_DCT_scaled_size + 2; - } else { - ngroups = cinfo->_min_DCT_scaled_size; - } - - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; /* height of a row group of component */ - main_ptr->buffer[ci] = (*cinfo->mem->alloc_sarray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - compptr->width_in_blocks * compptr->_DCT_scaled_size, - (JDIMENSION) (rgroup * ngroups)); - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdmainct.h leanify-0.4.3+git20181014/lib/mozjpeg/jdmainct.h --- leanify-0.4.3/lib/mozjpeg/jdmainct.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmainct.h 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,71 @@ +/* + * jdmainct.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1994-1996, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + */ + +#define JPEG_INTERNALS +#include "jpeglib.h" +#include "jpegcomp.h" + + +/* Private buffer controller object */ + +typedef struct { + struct jpeg_d_main_controller pub; /* public fields */ + + /* Pointer to allocated workspace (M or M+2 row groups). */ + JSAMPARRAY buffer[MAX_COMPONENTS]; + + boolean buffer_full; /* Have we gotten an iMCU row from decoder? */ + JDIMENSION rowgroup_ctr; /* counts row groups output to postprocessor */ + + /* Remaining fields are only used in the context case. */ + + /* These are the master pointers to the funny-order pointer lists. */ + JSAMPIMAGE xbuffer[2]; /* pointers to weird pointer lists */ + + int whichptr; /* indicates which pointer set is now in use */ + int context_state; /* process_data state machine status */ + JDIMENSION rowgroups_avail; /* row groups available to postprocessor */ + JDIMENSION iMCU_row_ctr; /* counts iMCU rows to detect image top/bot */ +} my_main_controller; + +typedef my_main_controller *my_main_ptr; + + +/* context_state values: */ +#define CTX_PREPARE_FOR_IMCU 0 /* need to prepare for MCU row */ +#define CTX_PROCESS_IMCU 1 /* feeding iMCU to postprocessor */ +#define CTX_POSTPONED_ROW 2 /* feeding postponed row group */ + + +LOCAL(void) +set_wraparound_pointers (j_decompress_ptr cinfo) +/* Set up the "wraparound" pointers at top and bottom of the pointer lists. + * This changes the pointer list state from top-of-image to the normal state. + */ +{ + my_main_ptr main_ptr = (my_main_ptr) cinfo->main; + int ci, i, rgroup; + int M = cinfo->_min_DCT_scaled_size; + jpeg_component_info *compptr; + JSAMPARRAY xbuf0, xbuf1; + + for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; + ci++, compptr++) { + rgroup = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / + cinfo->_min_DCT_scaled_size; /* height of a row group of component */ + xbuf0 = main_ptr->xbuffer[0][ci]; + xbuf1 = main_ptr->xbuffer[1][ci]; + for (i = 0; i < rgroup; i++) { + xbuf0[i - rgroup] = xbuf0[rgroup*(M+1) + i]; + xbuf1[i - rgroup] = xbuf1[rgroup*(M+1) + i]; + xbuf0[rgroup*(M+2) + i] = xbuf0[i]; + xbuf1[rgroup*(M+2) + i] = xbuf1[i]; + } + } +} diff -Nru leanify-0.4.3/lib/mozjpeg/jdmarker.c leanify-0.4.3+git20181014/lib/mozjpeg/jdmarker.c --- leanify-0.4.3/lib/mozjpeg/jdmarker.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmarker.c 2017-08-01 20:51:09.000000000 +0000 @@ -4,8 +4,9 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1998, Thomas G. Lane. * libjpeg-turbo Modifications: - * Copyright (C) 2012, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2012, 2015, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains routines to decode JPEG datastream markers. * Most of the complexity arises from our desire to support input @@ -106,7 +107,7 @@ /* Note: cur_marker is not linked into marker_list until it's all read. */ } my_marker_reader; -typedef my_marker_reader * my_marker_ptr; +typedef my_marker_reader *my_marker_ptr; /* @@ -119,8 +120,8 @@ /* Declare and initialize local copies of input pointer/count */ #define INPUT_VARS(cinfo) \ - struct jpeg_source_mgr * datasrc = (cinfo)->src; \ - const JOCTET * next_input_byte = datasrc->next_input_byte; \ + struct jpeg_source_mgr *datasrc = (cinfo)->src; \ + const JOCTET *next_input_byte = datasrc->next_input_byte; \ size_t bytes_in_buffer = datasrc->bytes_in_buffer /* Unload the local copies --- do this only at a restart boundary */ @@ -153,7 +154,7 @@ V = GETJOCTET(*next_input_byte++); ) /* As above, but read two bytes interpreted as an unsigned 16-bit integer. - * V should be declared unsigned int or perhaps INT32. + * V should be declared unsigned int or perhaps JLONG. */ #define INPUT_2BYTES(cinfo,V,action) \ MAKESTMT( MAKE_BYTE_AVAIL(cinfo,action); \ @@ -239,9 +240,9 @@ get_sof (j_decompress_ptr cinfo, boolean is_prog, boolean is_arith) /* Process a SOFn marker */ { - INT32 length; + JLONG length; int c, ci; - jpeg_component_info * compptr; + jpeg_component_info *compptr; INPUT_VARS(cinfo); cinfo->progressive_mode = is_prog; @@ -303,9 +304,9 @@ get_sos (j_decompress_ptr cinfo) /* Process a SOS marker */ { - INT32 length; + JLONG length; int i, ci, n, c, cc, pi; - jpeg_component_info * compptr; + jpeg_component_info *compptr; INPUT_VARS(cinfo); if (! cinfo->marker->saw_SOF) @@ -386,7 +387,7 @@ get_dac (j_decompress_ptr cinfo) /* Process a DAC marker */ { - INT32 length; + JLONG length; int index, val; INPUT_VARS(cinfo); @@ -432,7 +433,7 @@ get_dht (j_decompress_ptr cinfo) /* Process a DHT marker */ { - INT32 length; + JLONG length; UINT8 bits[17]; UINT8 huffval[256]; int i, index, count; @@ -466,7 +467,7 @@ /* Here we just do minimal validation of the counts to avoid walking * off the end of our table space. jdhuff.c will check more carefully. */ - if (count > 256 || ((INT32) count) > length) + if (count > 256 || ((JLONG) count) > length) ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); for (i = 0; i < count; i++) @@ -506,7 +507,7 @@ get_dqt (j_decompress_ptr cinfo) /* Process a DQT marker */ { - INT32 length; + JLONG length; int n, i, prec; unsigned int tmp; JQUANT_TBL *quant_ptr; @@ -564,7 +565,7 @@ get_dri (j_decompress_ptr cinfo) /* Process a DRI marker */ { - INT32 length; + JLONG length; unsigned int tmp; INPUT_VARS(cinfo); @@ -597,14 +598,14 @@ LOCAL(void) -examine_app0 (j_decompress_ptr cinfo, JOCTET * data, - unsigned int datalen, INT32 remaining) +examine_app0 (j_decompress_ptr cinfo, JOCTET *data, + unsigned int datalen, JLONG remaining) /* Examine first few bytes from an APP0. * Take appropriate action if it is a JFIF marker. * datalen is # of bytes at data[], remaining is length of rest of marker data. */ { - INT32 totallen = (INT32) datalen + remaining; + JLONG totallen = (JLONG) datalen + remaining; if (datalen >= APP0_DATA_LEN && GETJOCTET(data[0]) == 0x4A && @@ -638,7 +639,7 @@ GETJOCTET(data[12]), GETJOCTET(data[13])); totallen -= APP0_DATA_LEN; if (totallen != - ((INT32)GETJOCTET(data[12]) * (INT32)GETJOCTET(data[13]) * (INT32) 3)) + ((JLONG)GETJOCTET(data[12]) * (JLONG)GETJOCTET(data[13]) * (JLONG) 3)) TRACEMS1(cinfo, 1, JTRC_JFIF_BADTHUMBNAILSIZE, (int) totallen); } else if (datalen >= 6 && GETJOCTET(data[0]) == 0x4A && @@ -673,8 +674,8 @@ LOCAL(void) -examine_app14 (j_decompress_ptr cinfo, JOCTET * data, - unsigned int datalen, INT32 remaining) +examine_app14 (j_decompress_ptr cinfo, JOCTET *data, + unsigned int datalen, JLONG remaining) /* Examine first few bytes from an APP14. * Take appropriate action if it is an Adobe marker. * datalen is # of bytes at data[], remaining is length of rest of marker data. @@ -707,7 +708,7 @@ get_interesting_appn (j_decompress_ptr cinfo) /* Process an APP0 or APP14 marker without saving it */ { - INT32 length; + JLONG length; JOCTET b[APPN_DATA_LEN]; unsigned int i, numtoread; INPUT_VARS(cinfo); @@ -758,8 +759,8 @@ my_marker_ptr marker = (my_marker_ptr) cinfo->marker; jpeg_saved_marker_ptr cur_marker = marker->cur_marker; unsigned int bytes_read, data_length; - JOCTET * data; - INT32 length = 0; + JOCTET *data; + JLONG length = 0; INPUT_VARS(cinfo); if (cur_marker == NULL) { @@ -861,7 +862,7 @@ skip_variable (j_decompress_ptr cinfo) /* Skip over an unknown or uninteresting variable-length marker */ { - INT32 length; + JLONG length; INPUT_VARS(cinfo); INPUT_2BYTES(cinfo, length, return FALSE); diff -Nru leanify-0.4.3/lib/mozjpeg/jdmaster.c leanify-0.4.3+git20181014/lib/mozjpeg/jdmaster.c --- leanify-0.4.3/lib/mozjpeg/jdmaster.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmaster.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,737 +0,0 @@ -/* - * jdmaster.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1997, Thomas G. Lane. - * Modified 2002-2009 by Guido Vollbeding. - * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, D. R. Commander. - * Copyright (C) 2013, Linaro Limited. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains master control logic for the JPEG decompressor. - * These routines are concerned with selecting the modules to be executed - * and with determining the number of passes and the work to be done in each - * pass. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jpegcomp.h" - - -/* Private state */ - -typedef struct { - struct jpeg_decomp_master pub; /* public fields */ - - int pass_number; /* # of passes completed */ - - boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ - - /* Saved references to initialized quantizer modules, - * in case we need to switch modes. - */ - struct jpeg_color_quantizer * quantizer_1pass; - struct jpeg_color_quantizer * quantizer_2pass; -} my_decomp_master; - -typedef my_decomp_master * my_master_ptr; - - -/* - * Determine whether merged upsample/color conversion should be used. - * CRUCIAL: this must match the actual capabilities of jdmerge.c! - */ - -LOCAL(boolean) -use_merged_upsample (j_decompress_ptr cinfo) -{ -#ifdef UPSAMPLE_MERGING_SUPPORTED - /* Merging is the equivalent of plain box-filter upsampling */ - if (cinfo->do_fancy_upsampling || cinfo->CCIR601_sampling) - return FALSE; - /* jdmerge.c only supports YCC=>RGB and YCC=>RGB565 color conversion */ - if (cinfo->jpeg_color_space != JCS_YCbCr || cinfo->num_components != 3 || - (cinfo->out_color_space != JCS_RGB && - cinfo->out_color_space != JCS_RGB565 && - cinfo->out_color_space != JCS_EXT_RGB && - cinfo->out_color_space != JCS_EXT_RGBX && - cinfo->out_color_space != JCS_EXT_BGR && - cinfo->out_color_space != JCS_EXT_BGRX && - cinfo->out_color_space != JCS_EXT_XBGR && - cinfo->out_color_space != JCS_EXT_XRGB && - cinfo->out_color_space != JCS_EXT_RGBA && - cinfo->out_color_space != JCS_EXT_BGRA && - cinfo->out_color_space != JCS_EXT_ABGR && - cinfo->out_color_space != JCS_EXT_ARGB)) - return FALSE; - if ((cinfo->out_color_space == JCS_RGB565 && - cinfo->out_color_components != 3) || - (cinfo->out_color_space != JCS_RGB565 && - cinfo->out_color_components != rgb_pixelsize[cinfo->out_color_space])) - return FALSE; - /* and it only handles 2h1v or 2h2v sampling ratios */ - if (cinfo->comp_info[0].h_samp_factor != 2 || - cinfo->comp_info[1].h_samp_factor != 1 || - cinfo->comp_info[2].h_samp_factor != 1 || - cinfo->comp_info[0].v_samp_factor > 2 || - cinfo->comp_info[1].v_samp_factor != 1 || - cinfo->comp_info[2].v_samp_factor != 1) - return FALSE; - /* furthermore, it doesn't work if we've scaled the IDCTs differently */ - if (cinfo->comp_info[0]._DCT_scaled_size != cinfo->_min_DCT_scaled_size || - cinfo->comp_info[1]._DCT_scaled_size != cinfo->_min_DCT_scaled_size || - cinfo->comp_info[2]._DCT_scaled_size != cinfo->_min_DCT_scaled_size) - return FALSE; - /* ??? also need to test for upsample-time rescaling, when & if supported */ - return TRUE; /* by golly, it'll work... */ -#else - return FALSE; -#endif -} - - -/* - * Compute output image dimensions and related values. - * NOTE: this is exported for possible use by application. - * Hence it mustn't do anything that can't be done twice. - */ - -#if JPEG_LIB_VERSION >= 80 -GLOBAL(void) -#else -LOCAL(void) -#endif -jpeg_core_output_dimensions (j_decompress_ptr cinfo) -/* Do computations that are needed before master selection phase. - * This function is used for transcoding and full decompression. - */ -{ -#ifdef IDCT_SCALING_SUPPORTED - int ci; - jpeg_component_info *compptr; - - /* Compute actual output image dimensions and DCT scaling choices. */ - if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom) { - /* Provide 1/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 1; - cinfo->_min_DCT_v_scaled_size = 1; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 2) { - /* Provide 2/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 2L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 2L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 2; - cinfo->_min_DCT_v_scaled_size = 2; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 3) { - /* Provide 3/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 3L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 3L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 3; - cinfo->_min_DCT_v_scaled_size = 3; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 4) { - /* Provide 4/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 4L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 4L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 4; - cinfo->_min_DCT_v_scaled_size = 4; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 5) { - /* Provide 5/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 5L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 5L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 5; - cinfo->_min_DCT_v_scaled_size = 5; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 6) { - /* Provide 6/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 6L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 6L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 6; - cinfo->_min_DCT_v_scaled_size = 6; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 7) { - /* Provide 7/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 7L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 7L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 7; - cinfo->_min_DCT_v_scaled_size = 7; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 8) { - /* Provide 8/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 8L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 8L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 8; - cinfo->_min_DCT_v_scaled_size = 8; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 9) { - /* Provide 9/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 9L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 9L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 9; - cinfo->_min_DCT_v_scaled_size = 9; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 10) { - /* Provide 10/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 10L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 10L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 10; - cinfo->_min_DCT_v_scaled_size = 10; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 11) { - /* Provide 11/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 11L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 11L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 11; - cinfo->_min_DCT_v_scaled_size = 11; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 12) { - /* Provide 12/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 12L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 12L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 12; - cinfo->_min_DCT_v_scaled_size = 12; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 13) { - /* Provide 13/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 13L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 13L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 13; - cinfo->_min_DCT_v_scaled_size = 13; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 14) { - /* Provide 14/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 14L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 14L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 14; - cinfo->_min_DCT_v_scaled_size = 14; - } else if (cinfo->scale_num * DCTSIZE <= cinfo->scale_denom * 15) { - /* Provide 15/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 15L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 15L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 15; - cinfo->_min_DCT_v_scaled_size = 15; - } else { - /* Provide 16/block_size scaling */ - cinfo->output_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * 16L, (long) DCTSIZE); - cinfo->output_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * 16L, (long) DCTSIZE); - cinfo->_min_DCT_h_scaled_size = 16; - cinfo->_min_DCT_v_scaled_size = 16; - } - - /* Recompute dimensions of components */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - compptr->_DCT_h_scaled_size = cinfo->_min_DCT_h_scaled_size; - compptr->_DCT_v_scaled_size = cinfo->_min_DCT_v_scaled_size; - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ -} - - -/* - * Compute output image dimensions and related values. - * NOTE: this is exported for possible use by application. - * Hence it mustn't do anything that can't be done twice. - * Also note that it may be called before the master module is initialized! - */ - -GLOBAL(void) -jpeg_calc_output_dimensions (j_decompress_ptr cinfo) -/* Do computations that are needed before master selection phase */ -{ -#ifdef IDCT_SCALING_SUPPORTED - int ci; - jpeg_component_info *compptr; -#endif - - /* Prevent application from calling me at wrong times */ - if (cinfo->global_state != DSTATE_READY) - ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - - /* Compute core output image dimensions and DCT scaling choices. */ - jpeg_core_output_dimensions(cinfo); - -#ifdef IDCT_SCALING_SUPPORTED - - /* In selecting the actual DCT scaling for each component, we try to - * scale up the chroma components via IDCT scaling rather than upsampling. - * This saves time if the upsampler gets to use 1:1 scaling. - * Note this code adapts subsampling ratios which are powers of 2. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - int ssize = cinfo->_min_DCT_scaled_size; - while (ssize < DCTSIZE && - ((cinfo->max_h_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->h_samp_factor * ssize * 2) == 0) && - ((cinfo->max_v_samp_factor * cinfo->_min_DCT_scaled_size) % - (compptr->v_samp_factor * ssize * 2) == 0)) { - ssize = ssize * 2; - } -#if JPEG_LIB_VERSION >= 70 - compptr->DCT_h_scaled_size = compptr->DCT_v_scaled_size = ssize; -#else - compptr->DCT_scaled_size = ssize; -#endif - } - - /* Recompute downsampled dimensions of components; - * application needs to know these if using raw downsampled data. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Size in samples, after IDCT scaling */ - compptr->downsampled_width = (JDIMENSION) - jdiv_round_up((long) cinfo->image_width * - (long) (compptr->h_samp_factor * compptr->_DCT_scaled_size), - (long) (cinfo->max_h_samp_factor * DCTSIZE)); - compptr->downsampled_height = (JDIMENSION) - jdiv_round_up((long) cinfo->image_height * - (long) (compptr->v_samp_factor * compptr->_DCT_scaled_size), - (long) (cinfo->max_v_samp_factor * DCTSIZE)); - } - -#else /* !IDCT_SCALING_SUPPORTED */ - - /* Hardwire it to "no scaling" */ - cinfo->output_width = cinfo->image_width; - cinfo->output_height = cinfo->image_height; - /* jdinput.c has already initialized DCT_scaled_size to DCTSIZE, - * and has computed unscaled downsampled_width and downsampled_height. - */ - -#endif /* IDCT_SCALING_SUPPORTED */ - - /* Report number of components in selected colorspace. */ - /* Probably this should be in the color conversion module... */ - switch (cinfo->out_color_space) { - case JCS_GRAYSCALE: - cinfo->out_color_components = 1; - break; - case JCS_RGB: - case JCS_EXT_RGB: - case JCS_EXT_RGBX: - case JCS_EXT_BGR: - case JCS_EXT_BGRX: - case JCS_EXT_XBGR: - case JCS_EXT_XRGB: - case JCS_EXT_RGBA: - case JCS_EXT_BGRA: - case JCS_EXT_ABGR: - case JCS_EXT_ARGB: - cinfo->out_color_components = rgb_pixelsize[cinfo->out_color_space]; - break; - case JCS_YCbCr: - case JCS_RGB565: - cinfo->out_color_components = 3; - break; - case JCS_CMYK: - case JCS_YCCK: - cinfo->out_color_components = 4; - break; - default: /* else must be same colorspace as in file */ - cinfo->out_color_components = cinfo->num_components; - break; - } - cinfo->output_components = (cinfo->quantize_colors ? 1 : - cinfo->out_color_components); - - /* See if upsampler will want to emit more than one row at a time */ - if (use_merged_upsample(cinfo)) - cinfo->rec_outbuf_height = cinfo->max_v_samp_factor; - else - cinfo->rec_outbuf_height = 1; -} - - -/* - * Several decompression processes need to range-limit values to the range - * 0..MAXJSAMPLE; the input value may fall somewhat outside this range - * due to noise introduced by quantization, roundoff error, etc. These - * processes are inner loops and need to be as fast as possible. On most - * machines, particularly CPUs with pipelines or instruction prefetch, - * a (subscript-check-less) C table lookup - * x = sample_range_limit[x]; - * is faster than explicit tests - * if (x < 0) x = 0; - * else if (x > MAXJSAMPLE) x = MAXJSAMPLE; - * These processes all use a common table prepared by the routine below. - * - * For most steps we can mathematically guarantee that the initial value - * of x is within MAXJSAMPLE+1 of the legal range, so a table running from - * -(MAXJSAMPLE+1) to 2*MAXJSAMPLE+1 is sufficient. But for the initial - * limiting step (just after the IDCT), a wildly out-of-range value is - * possible if the input data is corrupt. To avoid any chance of indexing - * off the end of memory and getting a bad-pointer trap, we perform the - * post-IDCT limiting thus: - * x = range_limit[x & MASK]; - * where MASK is 2 bits wider than legal sample data, ie 10 bits for 8-bit - * samples. Under normal circumstances this is more than enough range and - * a correct output will be generated; with bogus input data the mask will - * cause wraparound, and we will safely generate a bogus-but-in-range output. - * For the post-IDCT step, we want to convert the data from signed to unsigned - * representation by adding CENTERJSAMPLE at the same time that we limit it. - * So the post-IDCT limiting table ends up looking like this: - * CENTERJSAMPLE,CENTERJSAMPLE+1,...,MAXJSAMPLE, - * MAXJSAMPLE (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), - * 0 (repeat 2*(MAXJSAMPLE+1)-CENTERJSAMPLE times), - * 0,1,...,CENTERJSAMPLE-1 - * Negative inputs select values from the upper half of the table after - * masking. - * - * We can save some space by overlapping the start of the post-IDCT table - * with the simpler range limiting table. The post-IDCT table begins at - * sample_range_limit + CENTERJSAMPLE. - */ - -LOCAL(void) -prepare_range_limit_table (j_decompress_ptr cinfo) -/* Allocate and fill in the sample_range_limit table */ -{ - JSAMPLE * table; - int i; - - table = (JSAMPLE *) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - (5 * (MAXJSAMPLE+1) + CENTERJSAMPLE) * sizeof(JSAMPLE)); - table += (MAXJSAMPLE+1); /* allow negative subscripts of simple table */ - cinfo->sample_range_limit = table; - /* First segment of "simple" table: limit[x] = 0 for x < 0 */ - MEMZERO(table - (MAXJSAMPLE+1), (MAXJSAMPLE+1) * sizeof(JSAMPLE)); - /* Main part of "simple" table: limit[x] = x */ - for (i = 0; i <= MAXJSAMPLE; i++) - table[i] = (JSAMPLE) i; - table += CENTERJSAMPLE; /* Point to where post-IDCT table starts */ - /* End of simple table, rest of first half of post-IDCT table */ - for (i = CENTERJSAMPLE; i < 2*(MAXJSAMPLE+1); i++) - table[i] = MAXJSAMPLE; - /* Second half of post-IDCT table */ - MEMZERO(table + (2 * (MAXJSAMPLE+1)), - (2 * (MAXJSAMPLE+1) - CENTERJSAMPLE) * sizeof(JSAMPLE)); - MEMCOPY(table + (4 * (MAXJSAMPLE+1) - CENTERJSAMPLE), - cinfo->sample_range_limit, CENTERJSAMPLE * sizeof(JSAMPLE)); -} - - -/* - * Master selection of decompression modules. - * This is done once at jpeg_start_decompress time. We determine - * which modules will be used and give them appropriate initialization calls. - * We also initialize the decompressor input side to begin consuming data. - * - * Since jpeg_read_header has finished, we know what is in the SOF - * and (first) SOS markers. We also have all the application parameter - * settings. - */ - -LOCAL(void) -master_selection (j_decompress_ptr cinfo) -{ - my_master_ptr master = (my_master_ptr) cinfo->master; - boolean use_c_buffer; - long samplesperrow; - JDIMENSION jd_samplesperrow; - - /* Initialize dimensions and other stuff */ - jpeg_calc_output_dimensions(cinfo); - prepare_range_limit_table(cinfo); - - /* Width of an output scanline must be representable as JDIMENSION. */ - samplesperrow = (long) cinfo->output_width * (long) cinfo->out_color_components; - jd_samplesperrow = (JDIMENSION) samplesperrow; - if ((long) jd_samplesperrow != samplesperrow) - ERREXIT(cinfo, JERR_WIDTH_OVERFLOW); - - /* Initialize my private state */ - master->pass_number = 0; - master->using_merged_upsample = use_merged_upsample(cinfo); - - /* Color quantizer selection */ - master->quantizer_1pass = NULL; - master->quantizer_2pass = NULL; - /* No mode changes if not using buffered-image mode. */ - if (! cinfo->quantize_colors || ! cinfo->buffered_image) { - cinfo->enable_1pass_quant = FALSE; - cinfo->enable_external_quant = FALSE; - cinfo->enable_2pass_quant = FALSE; - } - if (cinfo->quantize_colors) { - if (cinfo->raw_data_out) - ERREXIT(cinfo, JERR_NOTIMPL); - /* 2-pass quantizer only works in 3-component color space. */ - if (cinfo->out_color_components != 3) { - cinfo->enable_1pass_quant = TRUE; - cinfo->enable_external_quant = FALSE; - cinfo->enable_2pass_quant = FALSE; - cinfo->colormap = NULL; - } else if (cinfo->colormap != NULL) { - cinfo->enable_external_quant = TRUE; - } else if (cinfo->two_pass_quantize) { - cinfo->enable_2pass_quant = TRUE; - } else { - cinfo->enable_1pass_quant = TRUE; - } - - if (cinfo->enable_1pass_quant) { -#ifdef QUANT_1PASS_SUPPORTED - jinit_1pass_quantizer(cinfo); - master->quantizer_1pass = cinfo->cquantize; -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } - - /* We use the 2-pass code to map to external colormaps. */ - if (cinfo->enable_2pass_quant || cinfo->enable_external_quant) { -#ifdef QUANT_2PASS_SUPPORTED - jinit_2pass_quantizer(cinfo); - master->quantizer_2pass = cinfo->cquantize; -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } - /* If both quantizers are initialized, the 2-pass one is left active; - * this is necessary for starting with quantization to an external map. - */ - } - - /* Post-processing: in particular, color conversion first */ - if (! cinfo->raw_data_out) { - if (master->using_merged_upsample) { -#ifdef UPSAMPLE_MERGING_SUPPORTED - jinit_merged_upsampler(cinfo); /* does color conversion too */ -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else { - jinit_color_deconverter(cinfo); - jinit_upsampler(cinfo); - } - jinit_d_post_controller(cinfo, cinfo->enable_2pass_quant); - } - /* Inverse DCT */ - jinit_inverse_dct(cinfo); - /* Entropy decoding: either Huffman or arithmetic coding. */ - if (cinfo->arith_code) { -#ifdef D_ARITH_CODING_SUPPORTED - jinit_arith_decoder(cinfo); -#else - ERREXIT(cinfo, JERR_ARITH_NOTIMPL); -#endif - } else { - if (cinfo->progressive_mode) { -#ifdef D_PROGRESSIVE_SUPPORTED - jinit_phuff_decoder(cinfo); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif - } else - jinit_huff_decoder(cinfo); - } - - /* Initialize principal buffer controllers. */ - use_c_buffer = cinfo->inputctl->has_multiple_scans || cinfo->buffered_image; - jinit_d_coef_controller(cinfo, use_c_buffer); - - if (! cinfo->raw_data_out) - jinit_d_main_controller(cinfo, FALSE /* never need full buffer here */); - - /* We can now tell the memory manager to allocate virtual arrays. */ - (*cinfo->mem->realize_virt_arrays) ((j_common_ptr) cinfo); - - /* Initialize input side of decompressor to consume first scan. */ - (*cinfo->inputctl->start_input_pass) (cinfo); - -#ifdef D_MULTISCAN_FILES_SUPPORTED - /* If jpeg_start_decompress will read the whole file, initialize - * progress monitoring appropriately. The input step is counted - * as one pass. - */ - if (cinfo->progress != NULL && ! cinfo->buffered_image && - cinfo->inputctl->has_multiple_scans) { - int nscans; - /* Estimate number of scans to set pass_limit. */ - if (cinfo->progressive_mode) { - /* Arbitrarily estimate 2 interleaved DC scans + 3 AC scans/component. */ - nscans = 2 + 3 * cinfo->num_components; - } else { - /* For a nonprogressive multiscan file, estimate 1 scan per component. */ - nscans = cinfo->num_components; - } - cinfo->progress->pass_counter = 0L; - cinfo->progress->pass_limit = (long) cinfo->total_iMCU_rows * nscans; - cinfo->progress->completed_passes = 0; - cinfo->progress->total_passes = (cinfo->enable_2pass_quant ? 3 : 2); - /* Count the input pass as done */ - master->pass_number++; - } -#endif /* D_MULTISCAN_FILES_SUPPORTED */ -} - - -/* - * Per-pass setup. - * This is called at the beginning of each output pass. We determine which - * modules will be active during this pass and give them appropriate - * start_pass calls. We also set is_dummy_pass to indicate whether this - * is a "real" output pass or a dummy pass for color quantization. - * (In the latter case, jdapistd.c will crank the pass to completion.) - */ - -METHODDEF(void) -prepare_for_output_pass (j_decompress_ptr cinfo) -{ - my_master_ptr master = (my_master_ptr) cinfo->master; - - if (master->pub.is_dummy_pass) { -#ifdef QUANT_2PASS_SUPPORTED - /* Final pass of 2-pass quantization */ - master->pub.is_dummy_pass = FALSE; - (*cinfo->cquantize->start_pass) (cinfo, FALSE); - (*cinfo->post->start_pass) (cinfo, JBUF_CRANK_DEST); - (*cinfo->main->start_pass) (cinfo, JBUF_CRANK_DEST); -#else - ERREXIT(cinfo, JERR_NOT_COMPILED); -#endif /* QUANT_2PASS_SUPPORTED */ - } else { - if (cinfo->quantize_colors && cinfo->colormap == NULL) { - /* Select new quantization method */ - if (cinfo->two_pass_quantize && cinfo->enable_2pass_quant) { - cinfo->cquantize = master->quantizer_2pass; - master->pub.is_dummy_pass = TRUE; - } else if (cinfo->enable_1pass_quant) { - cinfo->cquantize = master->quantizer_1pass; - } else { - ERREXIT(cinfo, JERR_MODE_CHANGE); - } - } - (*cinfo->idct->start_pass) (cinfo); - (*cinfo->coef->start_output_pass) (cinfo); - if (! cinfo->raw_data_out) { - if (! master->using_merged_upsample) - (*cinfo->cconvert->start_pass) (cinfo); - (*cinfo->upsample->start_pass) (cinfo); - if (cinfo->quantize_colors) - (*cinfo->cquantize->start_pass) (cinfo, master->pub.is_dummy_pass); - (*cinfo->post->start_pass) (cinfo, - (master->pub.is_dummy_pass ? JBUF_SAVE_AND_PASS : JBUF_PASS_THRU)); - (*cinfo->main->start_pass) (cinfo, JBUF_PASS_THRU); - } - } - - /* Set up progress monitor's pass info if present */ - if (cinfo->progress != NULL) { - cinfo->progress->completed_passes = master->pass_number; - cinfo->progress->total_passes = master->pass_number + - (master->pub.is_dummy_pass ? 2 : 1); - /* In buffered-image mode, we assume one more output pass if EOI not - * yet reached, but no more passes if EOI has been reached. - */ - if (cinfo->buffered_image && ! cinfo->inputctl->eoi_reached) { - cinfo->progress->total_passes += (cinfo->enable_2pass_quant ? 2 : 1); - } - } -} - - -/* - * Finish up at end of an output pass. - */ - -METHODDEF(void) -finish_output_pass (j_decompress_ptr cinfo) -{ - my_master_ptr master = (my_master_ptr) cinfo->master; - - if (cinfo->quantize_colors) - (*cinfo->cquantize->finish_pass) (cinfo); - master->pass_number++; -} - - -#ifdef D_MULTISCAN_FILES_SUPPORTED - -/* - * Switch to a new external colormap between output passes. - */ - -GLOBAL(void) -jpeg_new_colormap (j_decompress_ptr cinfo) -{ - my_master_ptr master = (my_master_ptr) cinfo->master; - - /* Prevent application from calling me at wrong times */ - if (cinfo->global_state != DSTATE_BUFIMAGE) - ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); - - if (cinfo->quantize_colors && cinfo->enable_external_quant && - cinfo->colormap != NULL) { - /* Select 2-pass quantizer for external colormap use */ - cinfo->cquantize = master->quantizer_2pass; - /* Notify quantizer of colormap change */ - (*cinfo->cquantize->new_color_map) (cinfo); - master->pub.is_dummy_pass = FALSE; /* just in case */ - } else - ERREXIT(cinfo, JERR_MODE_CHANGE); -} - -#endif /* D_MULTISCAN_FILES_SUPPORTED */ - - -/* - * Initialize master decompression control and select active modules. - * This is performed at the start of jpeg_start_decompress. - */ - -GLOBAL(void) -jinit_master_decompress (j_decompress_ptr cinfo) -{ - my_master_ptr master; - - master = (my_master_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_decomp_master)); - cinfo->master = (struct jpeg_decomp_master *) master; - master->pub.prepare_for_output_pass = prepare_for_output_pass; - master->pub.finish_output_pass = finish_output_pass; - - master->pub.is_dummy_pass = FALSE; - - master_selection(cinfo); -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdmaster.h leanify-0.4.3+git20181014/lib/mozjpeg/jdmaster.h --- leanify-0.4.3/lib/mozjpeg/jdmaster.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmaster.h 2017-08-01 20:51:09.000000000 +0000 @@ -0,0 +1,28 @@ +/* + * jdmaster.h + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1995, Thomas G. Lane. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains the master control structure for the JPEG decompressor. + */ + +/* Private state */ + +typedef struct { + struct jpeg_decomp_master pub; /* public fields */ + + int pass_number; /* # of passes completed */ + + boolean using_merged_upsample; /* TRUE if using merged upsample/cconvert */ + + /* Saved references to initialized quantizer modules, + * in case we need to switch modes. + */ + struct jpeg_color_quantizer *quantizer_1pass; + struct jpeg_color_quantizer *quantizer_2pass; +} my_decomp_master; + +typedef my_decomp_master *my_master_ptr; diff -Nru leanify-0.4.3/lib/mozjpeg/jdmrg565.c leanify-0.4.3+git20181014/lib/mozjpeg/jdmrg565.c --- leanify-0.4.3/lib/mozjpeg/jdmrg565.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmrg565.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,355 +0,0 @@ -/* - * jdmrg565.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1996, Thomas G. Lane. - * libjpeg-turbo Modifications: - * Copyright (C) 2013, Linaro Limited. - * Copyright (C) 2014, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains code for merged upsampling/color conversion. - */ - - -INLINE -LOCAL(void) -h2v1_merged_upsample_565_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - unsigned int r, g, b; - INT32 rgb; - SHIFT_TEMPS - - inptr0 = input_buf[0][in_row_group_ctr]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr = output_buf[0]; - - /* Loop for each pair of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - /* Fetch 2 Y values and emit 2 pixels */ - y = GETJSAMPLE(*inptr0++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr0++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr, rgb); - outptr += 4; - } - - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr0); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } - } - - -INLINE -LOCAL(void) -h2v1_merged_upsample_565D_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - INT32 d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; - unsigned int r, g, b; - INT32 rgb; - SHIFT_TEMPS - - inptr0 = input_buf[0][in_row_group_ctr]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr = output_buf[0]; - - /* Loop for each pair of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - /* Fetch 2 Y values and emit 2 pixels */ - y = GETJSAMPLE(*inptr0++); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr0++); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr, rgb); - outptr += 4; - } - - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr0); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr = rgb; - } -} - - -INLINE -LOCAL(void) -h2v2_merged_upsample_565_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - unsigned int r, g, b; - INT32 rgb; - SHIFT_TEMPS - - inptr00 = input_buf[0][in_row_group_ctr * 2]; - inptr01 = input_buf[0][in_row_group_ctr * 2 + 1]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr0 = output_buf[0]; - outptr1 = output_buf[1]; - - /* Loop for each group of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - /* Fetch 4 Y values and emit 4 pixels */ - y = GETJSAMPLE(*inptr00++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr00++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr0, rgb); - outptr0 += 4; - - y = GETJSAMPLE(*inptr01++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr01++); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr1, rgb); - outptr1 += 4; - } - - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - y = GETJSAMPLE(*inptr00); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr0 = rgb; - - y = GETJSAMPLE(*inptr01); - r = range_limit[y + cred]; - g = range_limit[y + cgreen]; - b = range_limit[y + cblue]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr1 = rgb; - } -} - - -INLINE -LOCAL(void) -h2v2_merged_upsample_565D_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - INT32 d0 = dither_matrix[cinfo->output_scanline & DITHER_MASK]; - INT32 d1 = dither_matrix[(cinfo->output_scanline+1) & DITHER_MASK]; - unsigned int r, g, b; - INT32 rgb; - SHIFT_TEMPS - - inptr00 = input_buf[0][in_row_group_ctr*2]; - inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr0 = output_buf[0]; - outptr1 = output_buf[1]; - - /* Loop for each group of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - /* Fetch 4 Y values and emit 4 pixels */ - y = GETJSAMPLE(*inptr00++); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr00++); - r = range_limit[DITHER_565_R(y + cred, d1)]; - g = range_limit[DITHER_565_G(y + cgreen, d1)]; - b = range_limit[DITHER_565_B(y + cblue, d1)]; - d1 = DITHER_ROTATE(d1); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr0, rgb); - outptr0 += 4; - - y = GETJSAMPLE(*inptr01++); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - d0 = DITHER_ROTATE(d0); - rgb = PACK_SHORT_565(r, g, b); - - y = GETJSAMPLE(*inptr01++); - r = range_limit[DITHER_565_R(y + cred, d1)]; - g = range_limit[DITHER_565_G(y + cgreen, d1)]; - b = range_limit[DITHER_565_B(y + cblue, d1)]; - d1 = DITHER_ROTATE(d1); - rgb = PACK_TWO_PIXELS(rgb, PACK_SHORT_565(r, g, b)); - - WRITE_TWO_PIXELS(outptr1, rgb); - outptr1 += 4; - } - - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - - y = GETJSAMPLE(*inptr00); - r = range_limit[DITHER_565_R(y + cred, d0)]; - g = range_limit[DITHER_565_G(y + cgreen, d0)]; - b = range_limit[DITHER_565_B(y + cblue, d0)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr0 = rgb; - - y = GETJSAMPLE(*inptr01); - r = range_limit[DITHER_565_R(y + cred, d1)]; - g = range_limit[DITHER_565_G(y + cgreen, d1)]; - b = range_limit[DITHER_565_B(y + cblue, d1)]; - rgb = PACK_SHORT_565(r, g, b); - *(INT16*)outptr1 = rgb; - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdmrgext.c leanify-0.4.3+git20181014/lib/mozjpeg/jdmrgext.c --- leanify-0.4.3/lib/mozjpeg/jdmrgext.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdmrgext.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -/* - * jdmrgext.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1996, Thomas G. Lane. - * libjpeg-turbo Modifications: - * Copyright (C) 2011, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains code for merged upsampling/color conversion. - */ - - -/* This file is included by jdmerge.c */ - - -/* - * Upsample and color convert for the case of 2:1 horizontal and 1:1 vertical. - */ - -INLINE -LOCAL(void) -h2v1_merged_upsample_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr; - JSAMPROW inptr0, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - SHIFT_TEMPS - - inptr0 = input_buf[0][in_row_group_ctr]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr = output_buf[0]; - /* Loop for each pair of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - /* Fetch 2 Y values and emit 2 pixels */ - y = GETJSAMPLE(*inptr0++); - outptr[RGB_RED] = range_limit[y + cred]; - outptr[RGB_GREEN] = range_limit[y + cgreen]; - outptr[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - outptr += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr0++); - outptr[RGB_RED] = range_limit[y + cred]; - outptr[RGB_GREEN] = range_limit[y + cgreen]; - outptr[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - outptr += RGB_PIXELSIZE; - } - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr0); - outptr[RGB_RED] = range_limit[y + cred]; - outptr[RGB_GREEN] = range_limit[y + cgreen]; - outptr[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr[RGB_ALPHA] = 0xFF; -#endif - } -} - - -/* - * Upsample and color convert for the case of 2:1 horizontal and 2:1 vertical. - */ - -INLINE -LOCAL(void) -h2v2_merged_upsample_internal (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, - JDIMENSION in_row_group_ctr, - JSAMPARRAY output_buf) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - register int y, cred, cgreen, cblue; - int cb, cr; - register JSAMPROW outptr0, outptr1; - JSAMPROW inptr00, inptr01, inptr1, inptr2; - JDIMENSION col; - /* copy these pointers into registers if possible */ - register JSAMPLE * range_limit = cinfo->sample_range_limit; - int * Crrtab = upsample->Cr_r_tab; - int * Cbbtab = upsample->Cb_b_tab; - INT32 * Crgtab = upsample->Cr_g_tab; - INT32 * Cbgtab = upsample->Cb_g_tab; - SHIFT_TEMPS - - inptr00 = input_buf[0][in_row_group_ctr*2]; - inptr01 = input_buf[0][in_row_group_ctr*2 + 1]; - inptr1 = input_buf[1][in_row_group_ctr]; - inptr2 = input_buf[2][in_row_group_ctr]; - outptr0 = output_buf[0]; - outptr1 = output_buf[1]; - /* Loop for each group of output pixels */ - for (col = cinfo->output_width >> 1; col > 0; col--) { - /* Do the chroma part of the calculation */ - cb = GETJSAMPLE(*inptr1++); - cr = GETJSAMPLE(*inptr2++); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - /* Fetch 4 Y values and emit 4 pixels */ - y = GETJSAMPLE(*inptr00++); - outptr0[RGB_RED] = range_limit[y + cred]; - outptr0[RGB_GREEN] = range_limit[y + cgreen]; - outptr0[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; -#endif - outptr0 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr00++); - outptr0[RGB_RED] = range_limit[y + cred]; - outptr0[RGB_GREEN] = range_limit[y + cgreen]; - outptr0[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; -#endif - outptr0 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr01++); - outptr1[RGB_RED] = range_limit[y + cred]; - outptr1[RGB_GREEN] = range_limit[y + cgreen]; - outptr1[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; -#endif - outptr1 += RGB_PIXELSIZE; - y = GETJSAMPLE(*inptr01++); - outptr1[RGB_RED] = range_limit[y + cred]; - outptr1[RGB_GREEN] = range_limit[y + cgreen]; - outptr1[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; -#endif - outptr1 += RGB_PIXELSIZE; - } - /* If image width is odd, do the last output column separately */ - if (cinfo->output_width & 1) { - cb = GETJSAMPLE(*inptr1); - cr = GETJSAMPLE(*inptr2); - cred = Crrtab[cr]; - cgreen = (int) RIGHT_SHIFT(Cbgtab[cb] + Crgtab[cr], SCALEBITS); - cblue = Cbbtab[cb]; - y = GETJSAMPLE(*inptr00); - outptr0[RGB_RED] = range_limit[y + cred]; - outptr0[RGB_GREEN] = range_limit[y + cgreen]; - outptr0[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr0[RGB_ALPHA] = 0xFF; -#endif - y = GETJSAMPLE(*inptr01); - outptr1[RGB_RED] = range_limit[y + cred]; - outptr1[RGB_GREEN] = range_limit[y + cgreen]; - outptr1[RGB_BLUE] = range_limit[y + cblue]; -#ifdef RGB_ALPHA - outptr1[RGB_ALPHA] = 0xFF; -#endif - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdphuff.c leanify-0.4.3+git20181014/lib/mozjpeg/jdphuff.c --- leanify-0.4.3/lib/mozjpeg/jdphuff.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdphuff.c 2018-03-21 11:12:12.000000000 +0000 @@ -3,9 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1995-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2015-2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains Huffman entropy decoding routines for progressive JPEG. * @@ -68,12 +69,12 @@ unsigned int restarts_to_go; /* MCUs left in this restart interval */ /* Pointers to derived tables (these workspaces have image lifespan) */ - d_derived_tbl * derived_tbls[NUM_HUFF_TBLS]; + d_derived_tbl *derived_tbls[NUM_HUFF_TBLS]; - d_derived_tbl * ac_derived_tbl; /* active table during an AC scan */ + d_derived_tbl *ac_derived_tbl; /* active table during an AC scan */ } phuff_entropy_decoder; -typedef phuff_entropy_decoder * phuff_entropy_ptr; +typedef phuff_entropy_decoder *phuff_entropy_ptr; /* Forward declarations */ METHODDEF(boolean) decode_mcu_DC_first (j_decompress_ptr cinfo, @@ -96,8 +97,9 @@ phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; boolean is_DC_band, bad; int ci, coefi, tbl; + d_derived_tbl **pdtbl; int *coef_bit_ptr; - jpeg_component_info * compptr; + jpeg_component_info *compptr; is_DC_band = (cinfo->Ss == 0); @@ -168,13 +170,13 @@ if (is_DC_band) { if (cinfo->Ah == 0) { /* DC refinement needs no table */ tbl = compptr->dc_tbl_no; - jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, - & entropy->derived_tbls[tbl]); + pdtbl = (d_derived_tbl **)(entropy->derived_tbls) + tbl; + jpeg_make_d_derived_tbl(cinfo, TRUE, tbl, pdtbl); } } else { tbl = compptr->ac_tbl_no; - jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, - & entropy->derived_tbls[tbl]); + pdtbl = (d_derived_tbl **)(entropy->derived_tbls) + tbl; + jpeg_make_d_derived_tbl(cinfo, FALSE, tbl, pdtbl); /* remember the single active table */ entropy->ac_derived_tbl = entropy->derived_tbls[tbl]; } @@ -203,7 +205,8 @@ #define AVOID_TABLES #ifdef AVOID_TABLES -#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((-1)<<(s)) + 1) : (x)) +#define NEG_1 ((unsigned)-1) +#define HUFF_EXTEND(x,s) ((x) < (1<<((s)-1)) ? (x) + (((NEG_1)<<(s)) + 1) : (x)) #else @@ -295,8 +298,8 @@ JBLOCKROW block; BITREAD_STATE_VARS; savable_state state; - d_derived_tbl * tbl; - jpeg_component_info * compptr; + d_derived_tbl *tbl; + jpeg_component_info *compptr; /* Process restart marker if needed; may have to suspend */ if (cinfo->restart_interval) { @@ -336,7 +339,7 @@ s += state.last_dc_val[ci]; state.last_dc_val[ci] = s; /* Scale and output the coefficient (assumes jpeg_natural_order[0]=0) */ - (*block)[0] = (JCOEF) (s << Al); + (*block)[0] = (JCOEF) LEFT_SHIFT(s, Al); } /* Completed MCU, so update state */ @@ -366,7 +369,7 @@ unsigned int EOBRUN; JBLOCKROW block; BITREAD_STATE_VARS; - d_derived_tbl * tbl; + d_derived_tbl *tbl; /* Process restart marker if needed; may have to suspend */ if (cinfo->restart_interval) { @@ -404,7 +407,7 @@ r = GET_BITS(s); s = HUFF_EXTEND(r, s); /* Scale and output coefficient in natural (dezigzagged) order */ - (*block)[jpeg_natural_order[k]] = (JCOEF) (s << Al); + (*block)[jpeg_natural_order[k]] = (JCOEF) LEFT_SHIFT(s, Al); } else { if (r == 15) { /* ZRL */ k += 15; /* skip 15 zeroes in band */ @@ -495,14 +498,14 @@ { phuff_entropy_ptr entropy = (phuff_entropy_ptr) cinfo->entropy; int Se = cinfo->Se; - int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ - int m1 = (-1) << cinfo->Al; /* -1 in the bit position being coded */ + int p1 = 1 << cinfo->Al; /* 1 in the bit position being coded */ + int m1 = (NEG_1) << cinfo->Al; /* -1 in the bit position being coded */ register int s, k, r; unsigned int EOBRUN; JBLOCKROW block; JCOEFPTR thiscoef; BITREAD_STATE_VARS; - d_derived_tbl * tbl; + d_derived_tbl *tbl; int num_newnz; int newnz_pos[DCTSIZE2]; diff -Nru leanify-0.4.3/lib/mozjpeg/jdpostct.c leanify-0.4.3+git20181014/lib/mozjpeg/jdpostct.c --- leanify-0.4.3/lib/mozjpeg/jdpostct.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdpostct.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,289 +0,0 @@ -/* - * jdpostct.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains the decompression postprocessing controller. - * This controller manages the upsampling, color conversion, and color - * quantization/reduction steps; specifically, it controls the buffering - * between upsample/color conversion and color quantization/reduction. - * - * If no color quantization/reduction is required, then this module has no - * work to do, and it just hands off to the upsample/color conversion code. - * An integrated upsample/convert/quantize process would replace this module - * entirely. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" - - -/* Private buffer controller object */ - -typedef struct { - struct jpeg_d_post_controller pub; /* public fields */ - - /* Color quantization source buffer: this holds output data from - * the upsample/color conversion step to be passed to the quantizer. - * For two-pass color quantization, we need a full-image buffer; - * for one-pass operation, a strip buffer is sufficient. - */ - jvirt_sarray_ptr whole_image; /* virtual array, or NULL if one-pass */ - JSAMPARRAY buffer; /* strip buffer, or current strip of virtual */ - JDIMENSION strip_height; /* buffer size in rows */ - /* for two-pass mode only: */ - JDIMENSION starting_row; /* row # of first row in current strip */ - JDIMENSION next_row; /* index of next row to fill/empty in strip */ -} my_post_controller; - -typedef my_post_controller * my_post_ptr; - - -/* Forward declarations */ -METHODDEF(void) post_process_1pass - (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail); -#ifdef QUANT_2PASS_SUPPORTED -METHODDEF(void) post_process_prepass - (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail); -METHODDEF(void) post_process_2pass - (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, - JDIMENSION *in_row_group_ctr, JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail); -#endif - - -/* - * Initialize for a processing pass. - */ - -METHODDEF(void) -start_pass_dpost (j_decompress_ptr cinfo, J_BUF_MODE pass_mode) -{ - my_post_ptr post = (my_post_ptr) cinfo->post; - - switch (pass_mode) { - case JBUF_PASS_THRU: - if (cinfo->quantize_colors) { - /* Single-pass processing with color quantization. */ - post->pub.post_process_data = post_process_1pass; - /* We could be doing buffered-image output before starting a 2-pass - * color quantization; in that case, jinit_d_post_controller did not - * allocate a strip buffer. Use the virtual-array buffer as workspace. - */ - if (post->buffer == NULL) { - post->buffer = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, post->whole_image, - (JDIMENSION) 0, post->strip_height, TRUE); - } - } else { - /* For single-pass processing without color quantization, - * I have no work to do; just call the upsampler directly. - */ - post->pub.post_process_data = cinfo->upsample->upsample; - } - break; -#ifdef QUANT_2PASS_SUPPORTED - case JBUF_SAVE_AND_PASS: - /* First pass of 2-pass quantization */ - if (post->whole_image == NULL) - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - post->pub.post_process_data = post_process_prepass; - break; - case JBUF_CRANK_DEST: - /* Second pass of 2-pass quantization */ - if (post->whole_image == NULL) - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - post->pub.post_process_data = post_process_2pass; - break; -#endif /* QUANT_2PASS_SUPPORTED */ - default: - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); - break; - } - post->starting_row = post->next_row = 0; -} - - -/* - * Process some data in the one-pass (strip buffer) case. - * This is used for color precision reduction as well as one-pass quantization. - */ - -METHODDEF(void) -post_process_1pass (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_post_ptr post = (my_post_ptr) cinfo->post; - JDIMENSION num_rows, max_rows; - - /* Fill the buffer, but not more than what we can dump out in one go. */ - /* Note we rely on the upsampler to detect bottom of image. */ - max_rows = out_rows_avail - *out_row_ctr; - if (max_rows > post->strip_height) - max_rows = post->strip_height; - num_rows = 0; - (*cinfo->upsample->upsample) (cinfo, - input_buf, in_row_group_ctr, in_row_groups_avail, - post->buffer, &num_rows, max_rows); - /* Quantize and emit data. */ - (*cinfo->cquantize->color_quantize) (cinfo, - post->buffer, output_buf + *out_row_ctr, (int) num_rows); - *out_row_ctr += num_rows; -} - - -#ifdef QUANT_2PASS_SUPPORTED - -/* - * Process some data in the first pass of 2-pass quantization. - */ - -METHODDEF(void) -post_process_prepass (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_post_ptr post = (my_post_ptr) cinfo->post; - JDIMENSION old_next_row, num_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (post->next_row == 0) { - post->buffer = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, post->whole_image, - post->starting_row, post->strip_height, TRUE); - } - - /* Upsample some data (up to a strip height's worth). */ - old_next_row = post->next_row; - (*cinfo->upsample->upsample) (cinfo, - input_buf, in_row_group_ctr, in_row_groups_avail, - post->buffer, &post->next_row, post->strip_height); - - /* Allow quantizer to scan new data. No data is emitted, */ - /* but we advance out_row_ctr so outer loop can tell when we're done. */ - if (post->next_row > old_next_row) { - num_rows = post->next_row - old_next_row; - (*cinfo->cquantize->color_quantize) (cinfo, post->buffer + old_next_row, - (JSAMPARRAY) NULL, (int) num_rows); - *out_row_ctr += num_rows; - } - - /* Advance if we filled the strip. */ - if (post->next_row >= post->strip_height) { - post->starting_row += post->strip_height; - post->next_row = 0; - } -} - - -/* - * Process some data in the second pass of 2-pass quantization. - */ - -METHODDEF(void) -post_process_2pass (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_post_ptr post = (my_post_ptr) cinfo->post; - JDIMENSION num_rows, max_rows; - - /* Reposition virtual buffer if at start of strip. */ - if (post->next_row == 0) { - post->buffer = (*cinfo->mem->access_virt_sarray) - ((j_common_ptr) cinfo, post->whole_image, - post->starting_row, post->strip_height, FALSE); - } - - /* Determine number of rows to emit. */ - num_rows = post->strip_height - post->next_row; /* available in strip */ - max_rows = out_rows_avail - *out_row_ctr; /* available in output area */ - if (num_rows > max_rows) - num_rows = max_rows; - /* We have to check bottom of image here, can't depend on upsampler. */ - max_rows = cinfo->output_height - post->starting_row; - if (num_rows > max_rows) - num_rows = max_rows; - - /* Quantize and emit data. */ - (*cinfo->cquantize->color_quantize) (cinfo, - post->buffer + post->next_row, output_buf + *out_row_ctr, - (int) num_rows); - *out_row_ctr += num_rows; - - /* Advance if we filled the strip. */ - post->next_row += num_rows; - if (post->next_row >= post->strip_height) { - post->starting_row += post->strip_height; - post->next_row = 0; - } -} - -#endif /* QUANT_2PASS_SUPPORTED */ - - -/* - * Initialize postprocessing controller. - */ - -GLOBAL(void) -jinit_d_post_controller (j_decompress_ptr cinfo, boolean need_full_buffer) -{ - my_post_ptr post; - - post = (my_post_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_post_controller)); - cinfo->post = (struct jpeg_d_post_controller *) post; - post->pub.start_pass = start_pass_dpost; - post->whole_image = NULL; /* flag for no virtual arrays */ - post->buffer = NULL; /* flag for no strip buffer */ - - /* Create the quantization buffer, if needed */ - if (cinfo->quantize_colors) { - /* The buffer strip height is max_v_samp_factor, which is typically - * an efficient number of rows for upsampling to return. - * (In the presence of output rescaling, we might want to be smarter?) - */ - post->strip_height = (JDIMENSION) cinfo->max_v_samp_factor; - if (need_full_buffer) { - /* Two-pass color quantization: need full-image storage. */ - /* We round up the number of rows to a multiple of the strip height. */ -#ifdef QUANT_2PASS_SUPPORTED - post->whole_image = (*cinfo->mem->request_virt_sarray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE, - cinfo->output_width * cinfo->out_color_components, - (JDIMENSION) jround_up((long) cinfo->output_height, - (long) post->strip_height), - post->strip_height); -#else - ERREXIT(cinfo, JERR_BAD_BUFFER_MODE); -#endif /* QUANT_2PASS_SUPPORTED */ - } else { - /* One-pass color quantization: just make a strip buffer. */ - post->buffer = (*cinfo->mem->alloc_sarray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - cinfo->output_width * cinfo->out_color_components, - post->strip_height); - } - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdsample.c leanify-0.4.3+git20181014/lib/mozjpeg/jdsample.c --- leanify-0.4.3/lib/mozjpeg/jdsample.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdsample.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,504 +0,0 @@ -/* - * jdsample.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1991-1996, Thomas G. Lane. - * libjpeg-turbo Modifications: - * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright (C) 2010, D. R. Commander. - * Copyright (C) 2014, MIPS Technologies, Inc., California - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains upsampling routines. - * - * Upsampling input data is counted in "row groups". A row group - * is defined to be (v_samp_factor * DCT_scaled_size / min_DCT_scaled_size) - * sample rows of each component. Upsampling will normally produce - * max_v_samp_factor pixel rows from each row group (but this could vary - * if the upsampler is applying a scale factor of its own). - * - * An excellent reference for image resampling is - * Digital Image Warping, George Wolberg, 1990. - * Pub. by IEEE Computer Society Press, Los Alamitos, CA. ISBN 0-8186-8944-7. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jsimd.h" -#include "jpegcomp.h" - - -/* Pointer to routine to upsample a single component */ -typedef void (*upsample1_ptr) (j_decompress_ptr cinfo, - jpeg_component_info * compptr, - JSAMPARRAY input_data, - JSAMPARRAY * output_data_ptr); - -/* Private subobject */ - -typedef struct { - struct jpeg_upsampler pub; /* public fields */ - - /* Color conversion buffer. When using separate upsampling and color - * conversion steps, this buffer holds one upsampled row group until it - * has been color converted and output. - * Note: we do not allocate any storage for component(s) which are full-size, - * ie do not need rescaling. The corresponding entry of color_buf[] is - * simply set to point to the input data array, thereby avoiding copying. - */ - JSAMPARRAY color_buf[MAX_COMPONENTS]; - - /* Per-component upsampling method pointers */ - upsample1_ptr methods[MAX_COMPONENTS]; - - int next_row_out; /* counts rows emitted from color_buf */ - JDIMENSION rows_to_go; /* counts rows remaining in image */ - - /* Height of an input row group for each component. */ - int rowgroup_height[MAX_COMPONENTS]; - - /* These arrays save pixel expansion factors so that int_expand need not - * recompute them each time. They are unused for other upsampling methods. - */ - UINT8 h_expand[MAX_COMPONENTS]; - UINT8 v_expand[MAX_COMPONENTS]; -} my_upsampler; - -typedef my_upsampler * my_upsample_ptr; - - -/* - * Initialize for an upsampling pass. - */ - -METHODDEF(void) -start_pass_upsample (j_decompress_ptr cinfo) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - - /* Mark the conversion buffer empty */ - upsample->next_row_out = cinfo->max_v_samp_factor; - /* Initialize total-height counter for detecting bottom of image */ - upsample->rows_to_go = cinfo->output_height; -} - - -/* - * Control routine to do upsampling (and color conversion). - * - * In this version we upsample each component independently. - * We upsample one row group into the conversion buffer, then apply - * color conversion a row at a time. - */ - -METHODDEF(void) -sep_upsample (j_decompress_ptr cinfo, - JSAMPIMAGE input_buf, JDIMENSION *in_row_group_ctr, - JDIMENSION in_row_groups_avail, - JSAMPARRAY output_buf, JDIMENSION *out_row_ctr, - JDIMENSION out_rows_avail) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - int ci; - jpeg_component_info * compptr; - JDIMENSION num_rows; - - /* Fill the conversion buffer, if it's empty */ - if (upsample->next_row_out >= cinfo->max_v_samp_factor) { - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Invoke per-component upsample method. Notice we pass a POINTER - * to color_buf[ci], so that fullsize_upsample can change it. - */ - (*upsample->methods[ci]) (cinfo, compptr, - input_buf[ci] + (*in_row_group_ctr * upsample->rowgroup_height[ci]), - upsample->color_buf + ci); - } - upsample->next_row_out = 0; - } - - /* Color-convert and emit rows */ - - /* How many we have in the buffer: */ - num_rows = (JDIMENSION) (cinfo->max_v_samp_factor - upsample->next_row_out); - /* Not more than the distance to the end of the image. Need this test - * in case the image height is not a multiple of max_v_samp_factor: - */ - if (num_rows > upsample->rows_to_go) - num_rows = upsample->rows_to_go; - /* And not more than what the client can accept: */ - out_rows_avail -= *out_row_ctr; - if (num_rows > out_rows_avail) - num_rows = out_rows_avail; - - (*cinfo->cconvert->color_convert) (cinfo, upsample->color_buf, - (JDIMENSION) upsample->next_row_out, - output_buf + *out_row_ctr, - (int) num_rows); - - /* Adjust counts */ - *out_row_ctr += num_rows; - upsample->rows_to_go -= num_rows; - upsample->next_row_out += num_rows; - /* When the buffer is emptied, declare this input row group consumed */ - if (upsample->next_row_out >= cinfo->max_v_samp_factor) - (*in_row_group_ctr)++; -} - - -/* - * These are the routines invoked by sep_upsample to upsample pixel values - * of a single component. One row group is processed per call. - */ - - -/* - * For full-size components, we just make color_buf[ci] point at the - * input buffer, and thus avoid copying any data. Note that this is - * safe only because sep_upsample doesn't declare the input row group - * "consumed" until we are done color converting and emitting it. - */ - -METHODDEF(void) -fullsize_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - *output_data_ptr = input_data; -} - - -/* - * This is a no-op version used for "uninteresting" components. - * These components will not be referenced by color conversion. - */ - -METHODDEF(void) -noop_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - *output_data_ptr = NULL; /* safety check */ -} - - -/* - * This version handles any integral sampling ratios. - * This is not used for typical JPEG files, so it need not be fast. - * Nor, for that matter, is it particularly accurate: the algorithm is - * simple replication of the input pixel onto the corresponding output - * pixels. The hi-falutin sampling literature refers to this as a - * "box filter". A box filter tends to introduce visible artifacts, - * so if you are actually going to use 3:1 or 4:1 sampling ratios - * you would be well advised to improve this code. - */ - -METHODDEF(void) -int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - my_upsample_ptr upsample = (my_upsample_ptr) cinfo->upsample; - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; - register int h; - JSAMPROW outend; - int h_expand, v_expand; - int inrow, outrow; - - h_expand = upsample->h_expand[compptr->component_index]; - v_expand = upsample->v_expand[compptr->component_index]; - - inrow = outrow = 0; - while (outrow < cinfo->max_v_samp_factor) { - /* Generate one output row with proper horizontal expansion */ - inptr = input_data[inrow]; - outptr = output_data[outrow]; - outend = outptr + cinfo->output_width; - while (outptr < outend) { - invalue = *inptr++; /* don't need GETJSAMPLE() here */ - for (h = h_expand; h > 0; h--) { - *outptr++ = invalue; - } - } - /* Generate any additional output rows by duplicating the first one */ - if (v_expand > 1) { - jcopy_sample_rows(output_data, outrow, output_data, outrow+1, - v_expand-1, cinfo->output_width); - } - inrow++; - outrow += v_expand; - } -} - - -/* - * Fast processing for the common case of 2:1 horizontal and 1:1 vertical. - * It's still a box filter. - */ - -METHODDEF(void) -h2v1_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; - JSAMPROW outend; - int inrow; - - for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { - inptr = input_data[inrow]; - outptr = output_data[inrow]; - outend = outptr + cinfo->output_width; - while (outptr < outend) { - invalue = *inptr++; /* don't need GETJSAMPLE() here */ - *outptr++ = invalue; - *outptr++ = invalue; - } - } -} - - -/* - * Fast processing for the common case of 2:1 horizontal and 2:1 vertical. - * It's still a box filter. - */ - -METHODDEF(void) -h2v2_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register JSAMPLE invalue; - JSAMPROW outend; - int inrow, outrow; - - inrow = outrow = 0; - while (outrow < cinfo->max_v_samp_factor) { - inptr = input_data[inrow]; - outptr = output_data[outrow]; - outend = outptr + cinfo->output_width; - while (outptr < outend) { - invalue = *inptr++; /* don't need GETJSAMPLE() here */ - *outptr++ = invalue; - *outptr++ = invalue; - } - jcopy_sample_rows(output_data, outrow, output_data, outrow+1, - 1, cinfo->output_width); - inrow++; - outrow += 2; - } -} - - -/* - * Fancy processing for the common case of 2:1 horizontal and 1:1 vertical. - * - * The upsampling algorithm is linear interpolation between pixel centers, - * also known as a "triangle filter". This is a good compromise between - * speed and visual quality. The centers of the output pixels are 1/4 and 3/4 - * of the way between input pixel centers. - * - * A note about the "bias" calculations: when rounding fractional values to - * integer, we do not want to always round 0.5 up to the next integer. - * If we did that, we'd introduce a noticeable bias towards larger values. - * Instead, this code is arranged so that 0.5 will be rounded up or down at - * alternate pixel locations (a simple ordered dither pattern). - */ - -METHODDEF(void) -h2v1_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr, outptr; - register int invalue; - register JDIMENSION colctr; - int inrow; - - for (inrow = 0; inrow < cinfo->max_v_samp_factor; inrow++) { - inptr = input_data[inrow]; - outptr = output_data[inrow]; - /* Special case for first column */ - invalue = GETJSAMPLE(*inptr++); - *outptr++ = (JSAMPLE) invalue; - *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(*inptr) + 2) >> 2); - - for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel */ - invalue = GETJSAMPLE(*inptr++) * 3; - *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(inptr[-2]) + 1) >> 2); - *outptr++ = (JSAMPLE) ((invalue + GETJSAMPLE(*inptr) + 2) >> 2); - } - - /* Special case for last column */ - invalue = GETJSAMPLE(*inptr); - *outptr++ = (JSAMPLE) ((invalue * 3 + GETJSAMPLE(inptr[-1]) + 1) >> 2); - *outptr++ = (JSAMPLE) invalue; - } -} - - -/* - * Fancy processing for the common case of 2:1 horizontal and 2:1 vertical. - * Again a triangle filter; see comments for h2v1 case, above. - * - * It is OK for us to reference the adjacent input rows because we demanded - * context from the main buffer controller (see initialization code). - */ - -METHODDEF(void) -h2v2_fancy_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) -{ - JSAMPARRAY output_data = *output_data_ptr; - register JSAMPROW inptr0, inptr1, outptr; -#if BITS_IN_JSAMPLE == 8 - register int thiscolsum, lastcolsum, nextcolsum; -#else - register INT32 thiscolsum, lastcolsum, nextcolsum; -#endif - register JDIMENSION colctr; - int inrow, outrow, v; - - inrow = outrow = 0; - while (outrow < cinfo->max_v_samp_factor) { - for (v = 0; v < 2; v++) { - /* inptr0 points to nearest input row, inptr1 points to next nearest */ - inptr0 = input_data[inrow]; - if (v == 0) /* next nearest is row above */ - inptr1 = input_data[inrow-1]; - else /* next nearest is row below */ - inptr1 = input_data[inrow+1]; - outptr = output_data[outrow++]; - - /* Special case for first column */ - thiscolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); - nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); - *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 8) >> 4); - *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); - lastcolsum = thiscolsum; thiscolsum = nextcolsum; - - for (colctr = compptr->downsampled_width - 2; colctr > 0; colctr--) { - /* General case: 3/4 * nearer pixel + 1/4 * further pixel in each */ - /* dimension, thus 9/16, 3/16, 3/16, 1/16 overall */ - nextcolsum = GETJSAMPLE(*inptr0++) * 3 + GETJSAMPLE(*inptr1++); - *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); - *outptr++ = (JSAMPLE) ((thiscolsum * 3 + nextcolsum + 7) >> 4); - lastcolsum = thiscolsum; thiscolsum = nextcolsum; - } - - /* Special case for last column */ - *outptr++ = (JSAMPLE) ((thiscolsum * 3 + lastcolsum + 8) >> 4); - *outptr++ = (JSAMPLE) ((thiscolsum * 4 + 7) >> 4); - } - inrow++; - } -} - - -/* - * Module initialization routine for upsampling. - */ - -GLOBAL(void) -jinit_upsampler (j_decompress_ptr cinfo) -{ - my_upsample_ptr upsample; - int ci; - jpeg_component_info * compptr; - boolean need_buffer, do_fancy; - int h_in_group, v_in_group, h_out_group, v_out_group; - - upsample = (my_upsample_ptr) - (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, - sizeof(my_upsampler)); - cinfo->upsample = (struct jpeg_upsampler *) upsample; - upsample->pub.start_pass = start_pass_upsample; - upsample->pub.upsample = sep_upsample; - upsample->pub.need_context_rows = FALSE; /* until we find out differently */ - - if (cinfo->CCIR601_sampling) /* this isn't supported */ - ERREXIT(cinfo, JERR_CCIR601_NOTIMPL); - - /* jdmainct.c doesn't support context rows when min_DCT_scaled_size = 1, - * so don't ask for it. - */ - do_fancy = cinfo->do_fancy_upsampling && cinfo->_min_DCT_scaled_size > 1; - - /* Verify we can handle the sampling factors, select per-component methods, - * and create storage as needed. - */ - for (ci = 0, compptr = cinfo->comp_info; ci < cinfo->num_components; - ci++, compptr++) { - /* Compute size of an "input group" after IDCT scaling. This many samples - * are to be converted to max_h_samp_factor * max_v_samp_factor pixels. - */ - h_in_group = (compptr->h_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; - v_in_group = (compptr->v_samp_factor * compptr->_DCT_scaled_size) / - cinfo->_min_DCT_scaled_size; - h_out_group = cinfo->max_h_samp_factor; - v_out_group = cinfo->max_v_samp_factor; - upsample->rowgroup_height[ci] = v_in_group; /* save for use later */ - need_buffer = TRUE; - if (! compptr->component_needed) { - /* Don't bother to upsample an uninteresting component. */ - upsample->methods[ci] = noop_upsample; - need_buffer = FALSE; - } else if (h_in_group == h_out_group && v_in_group == v_out_group) { - /* Fullsize components can be processed without any work. */ - upsample->methods[ci] = fullsize_upsample; - need_buffer = FALSE; - } else if (h_in_group * 2 == h_out_group && - v_in_group == v_out_group) { - /* Special cases for 2h1v upsampling */ - if (do_fancy && compptr->downsampled_width > 2) { - if (jsimd_can_h2v1_fancy_upsample()) - upsample->methods[ci] = jsimd_h2v1_fancy_upsample; - else - upsample->methods[ci] = h2v1_fancy_upsample; - } else { - if (jsimd_can_h2v1_upsample()) - upsample->methods[ci] = jsimd_h2v1_upsample; - else - upsample->methods[ci] = h2v1_upsample; - } - } else if (h_in_group * 2 == h_out_group && - v_in_group * 2 == v_out_group) { - /* Special cases for 2h2v upsampling */ - if (do_fancy && compptr->downsampled_width > 2) { - if (jsimd_can_h2v2_fancy_upsample()) - upsample->methods[ci] = jsimd_h2v2_fancy_upsample; - else - upsample->methods[ci] = h2v2_fancy_upsample; - upsample->pub.need_context_rows = TRUE; - } else { - if (jsimd_can_h2v2_upsample()) - upsample->methods[ci] = jsimd_h2v2_upsample; - else - upsample->methods[ci] = h2v2_upsample; - } - } else if ((h_out_group % h_in_group) == 0 && - (v_out_group % v_in_group) == 0) { - /* Generic integral-factors upsampling method */ -#if defined(__mips__) - if (jsimd_can_int_upsample()) - upsample->methods[ci] = jsimd_int_upsample; - else -#endif - upsample->methods[ci] = int_upsample; - upsample->h_expand[ci] = (UINT8) (h_out_group / h_in_group); - upsample->v_expand[ci] = (UINT8) (v_out_group / v_in_group); - } else - ERREXIT(cinfo, JERR_FRACT_SAMPLE_NOTIMPL); - if (need_buffer) { - upsample->color_buf[ci] = (*cinfo->mem->alloc_sarray) - ((j_common_ptr) cinfo, JPOOL_IMAGE, - (JDIMENSION) jround_up((long) cinfo->output_width, - (long) cinfo->max_h_samp_factor), - (JDIMENSION) cinfo->max_v_samp_factor); - } - } -} diff -Nru leanify-0.4.3/lib/mozjpeg/jdtrans.c leanify-0.4.3+git20181014/lib/mozjpeg/jdtrans.c --- leanify-0.4.3/lib/mozjpeg/jdtrans.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jdtrans.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,8 @@ * Copyright (C) 1995-1997, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code relevant * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains library routines for transcoding decompression, * that is, reading raw DCT coefficient arrays from an input JPEG file. diff -Nru leanify-0.4.3/lib/mozjpeg/jerror.c leanify-0.4.3+git20181014/lib/mozjpeg/jerror.c --- leanify-0.4.3/lib/mozjpeg/jerror.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jerror.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,8 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code relevant * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains simple error-reporting and trace-message routines. * These are suitable for Unix-like systems and others where writing to @@ -125,7 +126,7 @@ METHODDEF(void) emit_message (j_common_ptr cinfo, int msg_level) { - struct jpeg_error_mgr * err = cinfo->err; + struct jpeg_error_mgr *err = cinfo->err; if (msg_level < 0) { /* It's a warning message. Since corrupt files may generate many warnings, @@ -152,12 +153,12 @@ */ METHODDEF(void) -format_message (j_common_ptr cinfo, char * buffer) +format_message (j_common_ptr cinfo, char *buffer) { - struct jpeg_error_mgr * err = cinfo->err; + struct jpeg_error_mgr *err = cinfo->err; int msg_code = err->msg_code; - const char * msgtext = NULL; - const char * msgptr; + const char *msgtext = NULL; + const char *msgptr; char ch; boolean isstring; @@ -226,7 +227,7 @@ */ GLOBAL(struct jpeg_error_mgr *) -jpeg_std_error (struct jpeg_error_mgr * err) +jpeg_std_error (struct jpeg_error_mgr *err) { err->error_exit = error_exit; err->emit_message = emit_message; diff -Nru leanify-0.4.3/lib/mozjpeg/jerror.h leanify-0.4.3+git20181014/lib/mozjpeg/jerror.h --- leanify-0.4.3/lib/mozjpeg/jerror.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jerror.h 2017-08-01 20:51:09.000000000 +0000 @@ -6,7 +6,8 @@ * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: * Copyright (C) 2014, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file defines the error and message codes for the JPEG library. * Edit this file to add new codes, or to translate the message strings to diff -Nru leanify-0.4.3/lib/mozjpeg/jidctflt.c leanify-0.4.3+git20181014/lib/mozjpeg/jidctflt.c --- leanify-0.4.3/lib/mozjpeg/jidctflt.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jidctflt.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,239 +0,0 @@ -/* - * jidctflt.c - * - * This file was part of the Independent JPEG Group's software: - * Copyright (C) 1994-1998, Thomas G. Lane. - * Modified 2010 by Guido Vollbeding. - * libjpeg-turbo Modifications: - * Copyright (C) 2014, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains a floating-point implementation of the - * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine - * must also perform dequantization of the input coefficients. - * - * This implementation should be more accurate than either of the integer - * IDCT implementations. However, it may not give the same results on all - * machines because of differences in roundoff behavior. Speed will depend - * on the hardware's floating point capacity. - * - * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - * on each row (or vice versa, but it's more convenient to emit a row at - * a time). Direct algorithms are also available, but they are much more - * complex and seem not to be any faster when reduced to code. - * - * This implementation is based on Arai, Agui, and Nakajima's algorithm for - * scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in - * Japanese, but the algorithm is described in the Pennebaker & Mitchell - * JPEG textbook (see REFERENCES section in file README). The following code - * is based directly on figure 4-8 in P&M. - * While an 8-point DCT cannot be done in less than 11 multiplies, it is - * possible to arrange the computation so that many of the multiplies are - * simple scalings of the final outputs. These multiplies can then be - * folded into the multiplications or divisions by the JPEG quantization - * table entries. The AA&N method leaves only 5 multiplies and 29 adds - * to be done in the DCT itself. - * The primary disadvantage of this method is that with a fixed-point - * implementation, accuracy is lost due to imprecise representation of the - * scaled quantization values. However, that problem does not arise if - * we use floating point arithmetic. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jdct.h" /* Private declarations for DCT subsystem */ - -#ifdef DCT_FLOAT_SUPPORTED - - -/* - * This module is specialized to the case DCTSIZE = 8. - */ - -#if DCTSIZE != 8 - Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ -#endif - - -/* Dequantize a coefficient by multiplying it by the multiplier-table - * entry; produce a float result. - */ - -#define DEQUANTIZE(coef,quantval) (((FAST_FLOAT) (coef)) * (quantval)) - - -/* - * Perform dequantization and inverse DCT on one block of coefficients. - */ - -GLOBAL(void) -jpeg_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - FAST_FLOAT tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; - FAST_FLOAT tmp10, tmp11, tmp12, tmp13; - FAST_FLOAT z5, z10, z11, z12, z13; - JCOEFPTR inptr; - FLOAT_MULT_TYPE * quantptr; - FAST_FLOAT * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = cinfo->sample_range_limit; - int ctr; - FAST_FLOAT workspace[DCTSIZE2]; /* buffers data between passes */ - #define _0_125 ((FLOAT_MULT_TYPE)0.125) - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (FLOAT_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = DCTSIZE; ctr > 0; ctr--) { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && - inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && - inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && - inptr[DCTSIZE*7] == 0) { - /* AC terms all zero */ - FAST_FLOAT dcval = DEQUANTIZE(inptr[DCTSIZE*0], - quantptr[DCTSIZE*0] * _0_125); - - wsptr[DCTSIZE*0] = dcval; - wsptr[DCTSIZE*1] = dcval; - wsptr[DCTSIZE*2] = dcval; - wsptr[DCTSIZE*3] = dcval; - wsptr[DCTSIZE*4] = dcval; - wsptr[DCTSIZE*5] = dcval; - wsptr[DCTSIZE*6] = dcval; - wsptr[DCTSIZE*7] = dcval; - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - continue; - } - - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0] * _0_125); - tmp1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2] * _0_125); - tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4] * _0_125); - tmp3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6] * _0_125); - - tmp10 = tmp0 + tmp2; /* phase 3 */ - tmp11 = tmp0 - tmp2; - - tmp13 = tmp1 + tmp3; /* phases 5-3 */ - tmp12 = (tmp1 - tmp3) * ((FAST_FLOAT) 1.414213562) - tmp13; /* 2*c4 */ - - tmp0 = tmp10 + tmp13; /* phase 2 */ - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - tmp4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1] * _0_125); - tmp5 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3] * _0_125); - tmp6 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5] * _0_125); - tmp7 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7] * _0_125); - - z13 = tmp6 + tmp5; /* phase 6 */ - z10 = tmp6 - tmp5; - z11 = tmp4 + tmp7; - z12 = tmp4 - tmp7; - - tmp7 = z11 + z13; /* phase 5 */ - tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); /* 2*c4 */ - - z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ - tmp10 = z5 - z12 * ((FAST_FLOAT) 1.082392200); /* 2*(c2-c6) */ - tmp12 = z5 - z10 * ((FAST_FLOAT) 2.613125930); /* 2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; /* phase 2 */ - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 - tmp5; - - wsptr[DCTSIZE*0] = tmp0 + tmp7; - wsptr[DCTSIZE*7] = tmp0 - tmp7; - wsptr[DCTSIZE*1] = tmp1 + tmp6; - wsptr[DCTSIZE*6] = tmp1 - tmp6; - wsptr[DCTSIZE*2] = tmp2 + tmp5; - wsptr[DCTSIZE*5] = tmp2 - tmp5; - wsptr[DCTSIZE*3] = tmp3 + tmp4; - wsptr[DCTSIZE*4] = tmp3 - tmp4; - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - } - - /* Pass 2: process rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < DCTSIZE; ctr++) { - outptr = output_buf[ctr] + output_col; - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * And testing floats for zero is relatively expensive, so we don't bother. - */ - - /* Even part */ - - /* Apply signed->unsigned and prepare float->int conversion */ - z5 = wsptr[0] + ((FAST_FLOAT) CENTERJSAMPLE + (FAST_FLOAT) 0.5); - tmp10 = z5 + wsptr[4]; - tmp11 = z5 - wsptr[4]; - - tmp13 = wsptr[2] + wsptr[6]; - tmp12 = (wsptr[2] - wsptr[6]) * ((FAST_FLOAT) 1.414213562) - tmp13; - - tmp0 = tmp10 + tmp13; - tmp3 = tmp10 - tmp13; - tmp1 = tmp11 + tmp12; - tmp2 = tmp11 - tmp12; - - /* Odd part */ - - z13 = wsptr[5] + wsptr[3]; - z10 = wsptr[5] - wsptr[3]; - z11 = wsptr[1] + wsptr[7]; - z12 = wsptr[1] - wsptr[7]; - - tmp7 = z11 + z13; - tmp11 = (z11 - z13) * ((FAST_FLOAT) 1.414213562); - - z5 = (z10 + z12) * ((FAST_FLOAT) 1.847759065); /* 2*c2 */ - tmp10 = z5 - z12 * ((FAST_FLOAT) 1.082392200); /* 2*(c2-c6) */ - tmp12 = z5 - z10 * ((FAST_FLOAT) 2.613125930); /* 2*(c2+c6) */ - - tmp6 = tmp12 - tmp7; - tmp5 = tmp11 - tmp6; - tmp4 = tmp10 - tmp5; - - /* Final output stage: float->int conversion and range-limit */ - - outptr[0] = range_limit[((int) (tmp0 + tmp7)) & RANGE_MASK]; - outptr[7] = range_limit[((int) (tmp0 - tmp7)) & RANGE_MASK]; - outptr[1] = range_limit[((int) (tmp1 + tmp6)) & RANGE_MASK]; - outptr[6] = range_limit[((int) (tmp1 - tmp6)) & RANGE_MASK]; - outptr[2] = range_limit[((int) (tmp2 + tmp5)) & RANGE_MASK]; - outptr[5] = range_limit[((int) (tmp2 - tmp5)) & RANGE_MASK]; - outptr[3] = range_limit[((int) (tmp3 + tmp4)) & RANGE_MASK]; - outptr[4] = range_limit[((int) (tmp3 - tmp4)) & RANGE_MASK]; - - wsptr += DCTSIZE; /* advance pointer to next row */ - } -} - -#endif /* DCT_FLOAT_SUPPORTED */ diff -Nru leanify-0.4.3/lib/mozjpeg/jidctint.c leanify-0.4.3+git20181014/lib/mozjpeg/jidctint.c --- leanify-0.4.3/lib/mozjpeg/jidctint.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jidctint.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,2623 +0,0 @@ -/* - * jidctint.c - * - * Copyright (C) 1991-1998, Thomas G. Lane. - * Modification developed 2002-2009 by Guido Vollbeding. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains a slow-but-accurate integer implementation of the - * inverse DCT (Discrete Cosine Transform). In the IJG code, this routine - * must also perform dequantization of the input coefficients. - * - * A 2-D IDCT can be done by 1-D IDCT on each column followed by 1-D IDCT - * on each row (or vice versa, but it's more convenient to emit a row at - * a time). Direct algorithms are also available, but they are much more - * complex and seem not to be any faster when reduced to code. - * - * This implementation is based on an algorithm described in - * C. Loeffler, A. Ligtenberg and G. Moschytz, "Practical Fast 1-D DCT - * Algorithms with 11 Multiplications", Proc. Int'l. Conf. on Acoustics, - * Speech, and Signal Processing 1989 (ICASSP '89), pp. 988-991. - * The primary algorithm described there uses 11 multiplies and 29 adds. - * We use their alternate method with 12 multiplies and 32 adds. - * The advantage of this method is that no data path contains more than one - * multiplication; this allows a very simple and accurate implementation in - * scaled fixed-point arithmetic, with a minimal number of shifts. - * - * We also provide IDCT routines with various output sample block sizes for - * direct resolution reduction or enlargement without additional resampling: - * NxN (N=1...16) pixels for one 8x8 input DCT block. - * - * For N<8 we simply take the corresponding low-frequency coefficients of - * the 8x8 input DCT block and apply an NxN point IDCT on the sub-block - * to yield the downscaled outputs. - * This can be seen as direct low-pass downsampling from the DCT domain - * point of view rather than the usual spatial domain point of view, - * yielding significant computational savings and results at least - * as good as common bilinear (averaging) spatial downsampling. - * - * For N>8 we apply a partial NxN IDCT on the 8 input coefficients as - * lower frequencies and higher frequencies assumed to be zero. - * It turns out that the computational effort is similar to the 8x8 IDCT - * regarding the output size. - * Furthermore, the scaling and descaling is the same for all IDCT sizes. - * - * CAUTION: We rely on the FIX() macro except for the N=1,2,4,8 cases - * since there would be too many additional constants to pre-calculate. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jdct.h" /* Private declarations for DCT subsystem */ - -#ifdef DCT_ISLOW_SUPPORTED - - -/* - * This module is specialized to the case DCTSIZE = 8. - */ - -#if DCTSIZE != 8 - Sorry, this code only copes with 8x8 DCT blocks. /* deliberate syntax err */ -#endif - - -/* - * The poop on this scaling stuff is as follows: - * - * Each 1-D IDCT step produces outputs which are a factor of sqrt(N) - * larger than the true IDCT outputs. The final outputs are therefore - * a factor of N larger than desired; since N=8 this can be cured by - * a simple right shift at the end of the algorithm. The advantage of - * this arrangement is that we save two multiplications per 1-D IDCT, - * because the y0 and y4 inputs need not be divided by sqrt(N). - * - * We have to do addition and subtraction of the integer inputs, which - * is no problem, and multiplication by fractional constants, which is - * a problem to do in integer arithmetic. We multiply all the constants - * by CONST_SCALE and convert them to integer constants (thus retaining - * CONST_BITS bits of precision in the constants). After doing a - * multiplication we have to divide the product by CONST_SCALE, with proper - * rounding, to produce the correct output. This division can be done - * cheaply as a right shift of CONST_BITS bits. We postpone shifting - * as long as possible so that partial sums can be added together with - * full fractional precision. - * - * The outputs of the first pass are scaled up by PASS1_BITS bits so that - * they are represented to better-than-integral precision. These outputs - * require BITS_IN_JSAMPLE + PASS1_BITS + 3 bits; this fits in a 16-bit word - * with the recommended scaling. (To scale up 12-bit sample data further, an - * intermediate INT32 array would be needed.) - * - * To avoid overflow of the 32-bit intermediate results in pass 2, we must - * have BITS_IN_JSAMPLE + CONST_BITS + PASS1_BITS <= 26. Error analysis - * shows that the values given below are the most effective. - */ - -#if BITS_IN_JSAMPLE == 8 -#define CONST_BITS 13 -#define PASS1_BITS 2 -#else -#define CONST_BITS 13 -#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ -#endif - -/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus - * causing a lot of useless floating-point operations at run time. - * To get around this we use the following pre-calculated constants. - * If you change CONST_BITS you may want to add appropriate values. - * (With a reasonable C compiler, you can just rely on the FIX() macro...) - */ - -#if CONST_BITS == 13 -#define FIX_0_298631336 ((INT32) 2446) /* FIX(0.298631336) */ -#define FIX_0_390180644 ((INT32) 3196) /* FIX(0.390180644) */ -#define FIX_0_541196100 ((INT32) 4433) /* FIX(0.541196100) */ -#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ -#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ -#define FIX_1_175875602 ((INT32) 9633) /* FIX(1.175875602) */ -#define FIX_1_501321110 ((INT32) 12299) /* FIX(1.501321110) */ -#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ -#define FIX_1_961570560 ((INT32) 16069) /* FIX(1.961570560) */ -#define FIX_2_053119869 ((INT32) 16819) /* FIX(2.053119869) */ -#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ -#define FIX_3_072711026 ((INT32) 25172) /* FIX(3.072711026) */ -#else -#define FIX_0_298631336 FIX(0.298631336) -#define FIX_0_390180644 FIX(0.390180644) -#define FIX_0_541196100 FIX(0.541196100) -#define FIX_0_765366865 FIX(0.765366865) -#define FIX_0_899976223 FIX(0.899976223) -#define FIX_1_175875602 FIX(1.175875602) -#define FIX_1_501321110 FIX(1.501321110) -#define FIX_1_847759065 FIX(1.847759065) -#define FIX_1_961570560 FIX(1.961570560) -#define FIX_2_053119869 FIX(2.053119869) -#define FIX_2_562915447 FIX(2.562915447) -#define FIX_3_072711026 FIX(3.072711026) -#endif - - -/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. - * For 8-bit samples with the recommended scaling, all the variable - * and constant values involved are no more than 16 bits wide, so a - * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. - * For 12-bit samples, a full 32-bit multiplication will be needed. - */ - -#if BITS_IN_JSAMPLE == 8 -#define MULTIPLY(var,const) MULTIPLY16C16(var,const) -#else -#define MULTIPLY(var,const) ((var) * (const)) -#endif - - -/* Dequantize a coefficient by multiplying it by the multiplier-table - * entry; produce an int result. In this module, both inputs and result - * are 16 bits or less, so either int or short multiply will work. - */ - -#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) - - -/* - * Perform dequantization and inverse DCT on one block of coefficients. - */ - -GLOBAL(void) -jpeg_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp2, tmp3; - INT32 tmp10, tmp11, tmp12, tmp13; - INT32 z1, z2, z3, z4, z5; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[DCTSIZE2]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - /* Note results are scaled up by sqrt(8) compared to a true IDCT; */ - /* furthermore, we scale the results by 2**PASS1_BITS. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = DCTSIZE; ctr > 0; ctr--) { - /* Due to quantization, we will usually find that many of the input - * coefficients are zero, especially the AC terms. We can exploit this - * by short-circuiting the IDCT calculation for any column in which all - * the AC terms are zero. In that case each output is equal to the - * DC coefficient (with scale factor as needed). - * With typical images and quantization tables, half or more of the - * column DCT calculations can be simplified this way. - */ - - if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && - inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*4] == 0 && - inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*6] == 0 && - inptr[DCTSIZE*7] == 0) { - /* AC terms all zero */ - int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; - - wsptr[DCTSIZE*0] = dcval; - wsptr[DCTSIZE*1] = dcval; - wsptr[DCTSIZE*2] = dcval; - wsptr[DCTSIZE*3] = dcval; - wsptr[DCTSIZE*4] = dcval; - wsptr[DCTSIZE*5] = dcval; - wsptr[DCTSIZE*6] = dcval; - wsptr[DCTSIZE*7] = dcval; - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - continue; - } - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - - z2 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - - tmp0 = (z2 + z3) << CONST_BITS; - tmp1 = (z2 - z3) << CONST_BITS; - - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. - */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - tmp1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - tmp2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - tmp3 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - z4 = tmp1 + tmp3; - z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ - - tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp3, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*7] = (int) DESCALE(tmp10 - tmp3, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*1] = (int) DESCALE(tmp11 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*6] = (int) DESCALE(tmp11 - tmp2, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*5] = (int) DESCALE(tmp12 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*3] = (int) DESCALE(tmp13 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[DCTSIZE*4] = (int) DESCALE(tmp13 - tmp0, CONST_BITS-PASS1_BITS); - - inptr++; /* advance pointers to next column */ - quantptr++; - wsptr++; - } - - /* Pass 2: process rows from work array, store into output array. */ - /* Note that we must descale the results by a factor of 8 == 2**3, */ - /* and also undo the PASS1_BITS scaling. */ - - wsptr = workspace; - for (ctr = 0; ctr < DCTSIZE; ctr++) { - outptr = output_buf[ctr] + output_col; - /* Rows of zeroes can be exploited in the same way as we did with columns. - * However, the column calculation has created many nonzero AC terms, so - * the simplification applies less often (typically 5% to 10% of the time). - * On machines with very fast multiplication, it's possible that the - * test takes more time than it's worth. In that case this section - * may be commented out. - */ - -#ifndef NO_ZERO_ROW_TEST - if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && wsptr[4] == 0 && - wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { - /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) - & RANGE_MASK]; - - outptr[0] = dcval; - outptr[1] = dcval; - outptr[2] = dcval; - outptr[3] = dcval; - outptr[4] = dcval; - outptr[5] = dcval; - outptr[6] = dcval; - outptr[7] = dcval; - - wsptr += DCTSIZE; /* advance pointer to next row */ - continue; - } -#endif - - /* Even part: reverse the even part of the forward DCT. */ - /* The rotator is sqrt(2)*c(-6). */ - - z2 = (INT32) wsptr[2]; - z3 = (INT32) wsptr[6]; - - z1 = MULTIPLY(z2 + z3, FIX_0_541196100); - tmp2 = z1 + MULTIPLY(z3, - FIX_1_847759065); - tmp3 = z1 + MULTIPLY(z2, FIX_0_765366865); - - tmp0 = ((INT32) wsptr[0] + (INT32) wsptr[4]) << CONST_BITS; - tmp1 = ((INT32) wsptr[0] - (INT32) wsptr[4]) << CONST_BITS; - - tmp10 = tmp0 + tmp3; - tmp13 = tmp0 - tmp3; - tmp11 = tmp1 + tmp2; - tmp12 = tmp1 - tmp2; - - /* Odd part per figure 8; the matrix is unitary and hence its - * transpose is its inverse. i0..i3 are y7,y5,y3,y1 respectively. - */ - - tmp0 = (INT32) wsptr[7]; - tmp1 = (INT32) wsptr[5]; - tmp2 = (INT32) wsptr[3]; - tmp3 = (INT32) wsptr[1]; - - z1 = tmp0 + tmp3; - z2 = tmp1 + tmp2; - z3 = tmp0 + tmp2; - z4 = tmp1 + tmp3; - z5 = MULTIPLY(z3 + z4, FIX_1_175875602); /* sqrt(2) * c3 */ - - tmp0 = MULTIPLY(tmp0, FIX_0_298631336); /* sqrt(2) * (-c1+c3+c5-c7) */ - tmp1 = MULTIPLY(tmp1, FIX_2_053119869); /* sqrt(2) * ( c1+c3-c5+c7) */ - tmp2 = MULTIPLY(tmp2, FIX_3_072711026); /* sqrt(2) * ( c1+c3+c5-c7) */ - tmp3 = MULTIPLY(tmp3, FIX_1_501321110); /* sqrt(2) * ( c1+c3-c5-c7) */ - z1 = MULTIPLY(z1, - FIX_0_899976223); /* sqrt(2) * (c7-c3) */ - z2 = MULTIPLY(z2, - FIX_2_562915447); /* sqrt(2) * (-c1-c3) */ - z3 = MULTIPLY(z3, - FIX_1_961570560); /* sqrt(2) * (-c3-c5) */ - z4 = MULTIPLY(z4, - FIX_0_390180644); /* sqrt(2) * (c5-c3) */ - - z3 += z5; - z4 += z5; - - tmp0 += z1 + z3; - tmp1 += z2 + z4; - tmp2 += z2 + z3; - tmp3 += z1 + z4; - - /* Final output stage: inputs are tmp10..tmp13, tmp0..tmp3 */ - - outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) DESCALE(tmp10 - tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) DESCALE(tmp11 + tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) DESCALE(tmp11 - tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) DESCALE(tmp12 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) DESCALE(tmp12 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) DESCALE(tmp13 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) DESCALE(tmp13 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += DCTSIZE; /* advance pointer to next row */ - } -} - -#ifdef IDCT_SCALING_SUPPORTED - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 7x7 output block. - * - * Optimized algorithm with 12 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/14). - */ - -GLOBAL(void) -jpeg_idct_7x7 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12, tmp13; - INT32 z1, z2, z3; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[7*7]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 7; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp13 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp13 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp13 += ONE << (CONST_BITS-PASS1_BITS-1); - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp10 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ - tmp12 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ - tmp11 = tmp10 + tmp12 + tmp13 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ - tmp0 = z1 + z3; - z2 -= tmp0; - tmp0 = MULTIPLY(tmp0, FIX(1.274162392)) + tmp13; /* c2 */ - tmp10 += tmp0 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ - tmp12 += tmp0 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ - tmp13 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - - tmp1 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ - tmp2 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ - tmp0 = tmp1 - tmp2; - tmp1 += tmp2; - tmp2 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ - tmp1 += tmp2; - z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ - tmp0 += z2; - tmp2 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ - - /* Final output stage */ - - wsptr[7*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[7*6] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[7*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[7*5] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[7*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[7*4] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); - wsptr[7*3] = (int) RIGHT_SHIFT(tmp13, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 7 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 7; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp13 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp13 <<= CONST_BITS; - - z1 = (INT32) wsptr[2]; - z2 = (INT32) wsptr[4]; - z3 = (INT32) wsptr[6]; - - tmp10 = MULTIPLY(z2 - z3, FIX(0.881747734)); /* c4 */ - tmp12 = MULTIPLY(z1 - z2, FIX(0.314692123)); /* c6 */ - tmp11 = tmp10 + tmp12 + tmp13 - MULTIPLY(z2, FIX(1.841218003)); /* c2+c4-c6 */ - tmp0 = z1 + z3; - z2 -= tmp0; - tmp0 = MULTIPLY(tmp0, FIX(1.274162392)) + tmp13; /* c2 */ - tmp10 += tmp0 - MULTIPLY(z3, FIX(0.077722536)); /* c2-c4-c6 */ - tmp12 += tmp0 - MULTIPLY(z1, FIX(2.470602249)); /* c2+c4+c6 */ - tmp13 += MULTIPLY(z2, FIX(1.414213562)); /* c0 */ - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - - tmp1 = MULTIPLY(z1 + z2, FIX(0.935414347)); /* (c3+c1-c5)/2 */ - tmp2 = MULTIPLY(z1 - z2, FIX(0.170262339)); /* (c3+c5-c1)/2 */ - tmp0 = tmp1 - tmp2; - tmp1 += tmp2; - tmp2 = MULTIPLY(z2 + z3, - FIX(1.378756276)); /* -c1 */ - tmp1 += tmp2; - z2 = MULTIPLY(z1 + z3, FIX(0.613604268)); /* c5 */ - tmp0 += z2; - tmp2 += z2 + MULTIPLY(z3, FIX(1.870828693)); /* c3+c1-c5 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 7; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 6x6 output block. - * - * Optimized algorithm with 3 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/12). - */ - -GLOBAL(void) -jpeg_idct_6x6 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp2, tmp10, tmp11, tmp12; - INT32 z1, z2, z3; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[6*6]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 6; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp0 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); - tmp2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ - tmp1 = tmp0 + tmp10; - tmp11 = RIGHT_SHIFT(tmp0 - tmp10 - tmp10, CONST_BITS-PASS1_BITS); - tmp10 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ - tmp10 = tmp1 + tmp0; - tmp12 = tmp1 - tmp0; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ - tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); - tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); - tmp1 = (z1 - z2 - z3) << PASS1_BITS; - - /* Final output stage */ - - wsptr[6*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[6*5] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[6*1] = (int) (tmp11 + tmp1); - wsptr[6*4] = (int) (tmp11 - tmp1); - wsptr[6*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[6*3] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 6 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 6; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp0 <<= CONST_BITS; - tmp2 = (INT32) wsptr[4]; - tmp10 = MULTIPLY(tmp2, FIX(0.707106781)); /* c4 */ - tmp1 = tmp0 + tmp10; - tmp11 = tmp0 - tmp10 - tmp10; - tmp10 = (INT32) wsptr[2]; - tmp0 = MULTIPLY(tmp10, FIX(1.224744871)); /* c2 */ - tmp10 = tmp1 + tmp0; - tmp12 = tmp1 - tmp0; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - tmp1 = MULTIPLY(z1 + z3, FIX(0.366025404)); /* c5 */ - tmp0 = tmp1 + ((z1 + z2) << CONST_BITS); - tmp2 = tmp1 + ((z3 - z2) << CONST_BITS); - tmp1 = (z1 - z2 - z3) << CONST_BITS; - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 6; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 5x5 output block. - * - * Optimized algorithm with 5 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/10). - */ - -GLOBAL(void) -jpeg_idct_5x5 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp10, tmp11, tmp12; - INT32 z1, z2, z3; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[5*5]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 5; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp12 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp12 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp12 += ONE << (CONST_BITS-PASS1_BITS-1); - tmp0 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - tmp1 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z1 = MULTIPLY(tmp0 + tmp1, FIX(0.790569415)); /* (c2+c4)/2 */ - z2 = MULTIPLY(tmp0 - tmp1, FIX(0.353553391)); /* (c2-c4)/2 */ - z3 = tmp12 + z2; - tmp10 = z3 + z1; - tmp11 = z3 - z1; - tmp12 -= z2 << 2; - - /* Odd part */ - - z2 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - - z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ - tmp0 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ - tmp1 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ - - /* Final output stage */ - - wsptr[5*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[5*4] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[5*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[5*3] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[5*2] = (int) RIGHT_SHIFT(tmp12, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 5 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 5; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp12 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp12 <<= CONST_BITS; - tmp0 = (INT32) wsptr[2]; - tmp1 = (INT32) wsptr[4]; - z1 = MULTIPLY(tmp0 + tmp1, FIX(0.790569415)); /* (c2+c4)/2 */ - z2 = MULTIPLY(tmp0 - tmp1, FIX(0.353553391)); /* (c2-c4)/2 */ - z3 = tmp12 + z2; - tmp10 = z3 + z1; - tmp11 = z3 - z1; - tmp12 -= z2 << 2; - - /* Odd part */ - - z2 = (INT32) wsptr[1]; - z3 = (INT32) wsptr[3]; - - z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c3 */ - tmp0 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c1-c3 */ - tmp1 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c1+c3 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 5; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 3x3 output block. - * - * Optimized algorithm with 2 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/6). - */ - -GLOBAL(void) -jpeg_idct_3x3 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp2, tmp10, tmp12; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[3*3]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 3; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp0 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); - tmp2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ - tmp10 = tmp0 + tmp12; - tmp2 = tmp0 - tmp12 - tmp12; - - /* Odd part */ - - tmp12 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ - - /* Final output stage */ - - wsptr[3*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[3*2] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[3*1] = (int) RIGHT_SHIFT(tmp2, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 3 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 3; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp0 <<= CONST_BITS; - tmp2 = (INT32) wsptr[2]; - tmp12 = MULTIPLY(tmp2, FIX(0.707106781)); /* c2 */ - tmp10 = tmp0 + tmp12; - tmp2 = tmp0 - tmp12 - tmp12; - - /* Odd part */ - - tmp12 = (INT32) wsptr[1]; - tmp0 = MULTIPLY(tmp12, FIX(1.224744871)); /* c1 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 3; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 9x9 output block. - * - * Optimized algorithm with 10 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/18). - */ - -GLOBAL(void) -jpeg_idct_9x9 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13, tmp14; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*9]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp0 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp0 += ONE << (CONST_BITS-PASS1_BITS-1); - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp3 = MULTIPLY(z3, FIX(0.707106781)); /* c6 */ - tmp1 = tmp0 + tmp3; - tmp2 = tmp0 - tmp3 - tmp3; - - tmp0 = MULTIPLY(z1 - z2, FIX(0.707106781)); /* c6 */ - tmp11 = tmp2 + tmp0; - tmp14 = tmp2 - tmp0 - tmp0; - - tmp0 = MULTIPLY(z1 + z2, FIX(1.328926049)); /* c2 */ - tmp2 = MULTIPLY(z1, FIX(1.083350441)); /* c4 */ - tmp3 = MULTIPLY(z2, FIX(0.245575608)); /* c8 */ - - tmp10 = tmp1 + tmp0 - tmp3; - tmp12 = tmp1 - tmp0 + tmp2; - tmp13 = tmp1 - tmp2 + tmp3; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - z2 = MULTIPLY(z2, - FIX(1.224744871)); /* -c3 */ - - tmp2 = MULTIPLY(z1 + z3, FIX(0.909038955)); /* c5 */ - tmp3 = MULTIPLY(z1 + z4, FIX(0.483689525)); /* c7 */ - tmp0 = tmp2 + tmp3 - z2; - tmp1 = MULTIPLY(z3 - z4, FIX(1.392728481)); /* c1 */ - tmp2 += z2 - tmp1; - tmp3 += z2 + tmp1; - tmp1 = MULTIPLY(z1 - z3 - z4, FIX(1.224744871)); /* c3 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp10 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp10 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp11 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp11 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp12 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp12 - tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp13 + tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp13 - tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp14, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 9 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 9; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp0 <<= CONST_BITS; - - z1 = (INT32) wsptr[2]; - z2 = (INT32) wsptr[4]; - z3 = (INT32) wsptr[6]; - - tmp3 = MULTIPLY(z3, FIX(0.707106781)); /* c6 */ - tmp1 = tmp0 + tmp3; - tmp2 = tmp0 - tmp3 - tmp3; - - tmp0 = MULTIPLY(z1 - z2, FIX(0.707106781)); /* c6 */ - tmp11 = tmp2 + tmp0; - tmp14 = tmp2 - tmp0 - tmp0; - - tmp0 = MULTIPLY(z1 + z2, FIX(1.328926049)); /* c2 */ - tmp2 = MULTIPLY(z1, FIX(1.083350441)); /* c4 */ - tmp3 = MULTIPLY(z2, FIX(0.245575608)); /* c8 */ - - tmp10 = tmp1 + tmp0 - tmp3; - tmp12 = tmp1 - tmp0 + tmp2; - tmp13 = tmp1 - tmp2 + tmp3; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - - z2 = MULTIPLY(z2, - FIX(1.224744871)); /* -c3 */ - - tmp2 = MULTIPLY(z1 + z3, FIX(0.909038955)); /* c5 */ - tmp3 = MULTIPLY(z1 + z4, FIX(0.483689525)); /* c7 */ - tmp0 = tmp2 + tmp3 - z2; - tmp1 = MULTIPLY(z3 - z4, FIX(1.392728481)); /* c1 */ - tmp2 += z2 - tmp1; - tmp3 += z2 + tmp1; - tmp1 = MULTIPLY(z1 - z3 - z4, FIX(1.224744871)); /* c3 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp11 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp11 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp12 + tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp12 - tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp13 + tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp13 - tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 10x10 output block. - * - * Optimized algorithm with 12 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/20). - */ - -GLOBAL(void) -jpeg_idct_10x10 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24; - INT32 z1, z2, z3, z4, z5; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*10]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z3 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - z3 += ONE << (CONST_BITS-PASS1_BITS-1); - z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ - z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ - tmp10 = z3 + z1; - tmp11 = z3 - z2; - - tmp22 = RIGHT_SHIFT(z3 - ((z1 - z2) << 1), /* c0 = (c4-c8)*2 */ - CONST_BITS-PASS1_BITS); - - z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ - tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ - tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ - - tmp20 = tmp10 + tmp12; - tmp24 = tmp10 - tmp12; - tmp21 = tmp11 + tmp13; - tmp23 = tmp11 - tmp13; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp11 = z2 + z4; - tmp13 = z2 - z4; - - tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ - z5 = z3 << CONST_BITS; - - z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ - z4 = z5 + tmp12; - - tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ - tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ - - z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ - z4 = z5 - tmp12 - (tmp13 << (CONST_BITS - 1)); - - tmp12 = (z1 - tmp13 - z3) << PASS1_BITS; - - tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ - tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) (tmp22 + tmp12); - wsptr[8*7] = (int) (tmp22 - tmp12); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 10 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 10; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - z3 <<= CONST_BITS; - z4 = (INT32) wsptr[4]; - z1 = MULTIPLY(z4, FIX(1.144122806)); /* c4 */ - z2 = MULTIPLY(z4, FIX(0.437016024)); /* c8 */ - tmp10 = z3 + z1; - tmp11 = z3 - z2; - - tmp22 = z3 - ((z1 - z2) << 1); /* c0 = (c4-c8)*2 */ - - z2 = (INT32) wsptr[2]; - z3 = (INT32) wsptr[6]; - - z1 = MULTIPLY(z2 + z3, FIX(0.831253876)); /* c6 */ - tmp12 = z1 + MULTIPLY(z2, FIX(0.513743148)); /* c2-c6 */ - tmp13 = z1 - MULTIPLY(z3, FIX(2.176250899)); /* c2+c6 */ - - tmp20 = tmp10 + tmp12; - tmp24 = tmp10 - tmp12; - tmp21 = tmp11 + tmp13; - tmp23 = tmp11 - tmp13; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z3 <<= CONST_BITS; - z4 = (INT32) wsptr[7]; - - tmp11 = z2 + z4; - tmp13 = z2 - z4; - - tmp12 = MULTIPLY(tmp13, FIX(0.309016994)); /* (c3-c7)/2 */ - - z2 = MULTIPLY(tmp11, FIX(0.951056516)); /* (c3+c7)/2 */ - z4 = z3 + tmp12; - - tmp10 = MULTIPLY(z1, FIX(1.396802247)) + z2 + z4; /* c1 */ - tmp14 = MULTIPLY(z1, FIX(0.221231742)) - z2 + z4; /* c9 */ - - z2 = MULTIPLY(tmp11, FIX(0.587785252)); /* (c1-c9)/2 */ - z4 = z3 - tmp12 - (tmp13 << (CONST_BITS - 1)); - - tmp12 = ((z1 - tmp13) << CONST_BITS) - z3; - - tmp11 = MULTIPLY(z1, FIX(1.260073511)) - z2 - z4; /* c3 */ - tmp13 = MULTIPLY(z1, FIX(0.642039522)) - z2 + z4; /* c7 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 11x11 output block. - * - * Optimized algorithm with 24 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/22). - */ - -GLOBAL(void) -jpeg_idct_11x11 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*11]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp10 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp10 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp10 += ONE << (CONST_BITS-PASS1_BITS-1); - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z2 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp20 = MULTIPLY(z2 - z3, FIX(2.546640132)); /* c2+c4 */ - tmp23 = MULTIPLY(z2 - z1, FIX(0.430815045)); /* c2-c6 */ - z4 = z1 + z3; - tmp24 = MULTIPLY(z4, - FIX(1.155664402)); /* -(c2-c10) */ - z4 -= z2; - tmp25 = tmp10 + MULTIPLY(z4, FIX(1.356927976)); /* c2 */ - tmp21 = tmp20 + tmp23 + tmp25 - - MULTIPLY(z2, FIX(1.821790775)); /* c2+c4+c10-c6 */ - tmp20 += tmp25 + MULTIPLY(z3, FIX(2.115825087)); /* c4+c6 */ - tmp23 += tmp25 - MULTIPLY(z1, FIX(1.513598477)); /* c6+c8 */ - tmp24 += tmp25; - tmp22 = tmp24 - MULTIPLY(z3, FIX(0.788749120)); /* c8+c10 */ - tmp24 += MULTIPLY(z2, FIX(1.944413522)) - /* c2+c8 */ - MULTIPLY(z1, FIX(1.390975730)); /* c4+c10 */ - tmp25 = tmp10 - MULTIPLY(z4, FIX(1.414213562)); /* c0 */ - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp11 = z1 + z2; - tmp14 = MULTIPLY(tmp11 + z3 + z4, FIX(0.398430003)); /* c9 */ - tmp11 = MULTIPLY(tmp11, FIX(0.887983902)); /* c3-c9 */ - tmp12 = MULTIPLY(z1 + z3, FIX(0.670361295)); /* c5-c9 */ - tmp13 = tmp14 + MULTIPLY(z1 + z4, FIX(0.366151574)); /* c7-c9 */ - tmp10 = tmp11 + tmp12 + tmp13 - - MULTIPLY(z1, FIX(0.923107866)); /* c7+c5+c3-c1-2*c9 */ - z1 = tmp14 - MULTIPLY(z2 + z3, FIX(1.163011579)); /* c7+c9 */ - tmp11 += z1 + MULTIPLY(z2, FIX(2.073276588)); /* c1+c7+3*c9-c3 */ - tmp12 += z1 - MULTIPLY(z3, FIX(1.192193623)); /* c3+c5-c7-c9 */ - z1 = MULTIPLY(z2 + z4, - FIX(1.798248910)); /* -(c1+c9) */ - tmp11 += z1; - tmp13 += z1 + MULTIPLY(z4, FIX(2.102458632)); /* c1+c5+c9-c7 */ - tmp14 += MULTIPLY(z2, - FIX(1.467221301)) + /* -(c5+c9) */ - MULTIPLY(z3, FIX(1.001388905)) - /* c1-c9 */ - MULTIPLY(z4, FIX(1.684843907)); /* c3+c9 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*10] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 11 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 11; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp10 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp10 <<= CONST_BITS; - - z1 = (INT32) wsptr[2]; - z2 = (INT32) wsptr[4]; - z3 = (INT32) wsptr[6]; - - tmp20 = MULTIPLY(z2 - z3, FIX(2.546640132)); /* c2+c4 */ - tmp23 = MULTIPLY(z2 - z1, FIX(0.430815045)); /* c2-c6 */ - z4 = z1 + z3; - tmp24 = MULTIPLY(z4, - FIX(1.155664402)); /* -(c2-c10) */ - z4 -= z2; - tmp25 = tmp10 + MULTIPLY(z4, FIX(1.356927976)); /* c2 */ - tmp21 = tmp20 + tmp23 + tmp25 - - MULTIPLY(z2, FIX(1.821790775)); /* c2+c4+c10-c6 */ - tmp20 += tmp25 + MULTIPLY(z3, FIX(2.115825087)); /* c4+c6 */ - tmp23 += tmp25 - MULTIPLY(z1, FIX(1.513598477)); /* c6+c8 */ - tmp24 += tmp25; - tmp22 = tmp24 - MULTIPLY(z3, FIX(0.788749120)); /* c8+c10 */ - tmp24 += MULTIPLY(z2, FIX(1.944413522)) - /* c2+c8 */ - MULTIPLY(z1, FIX(1.390975730)); /* c4+c10 */ - tmp25 = tmp10 - MULTIPLY(z4, FIX(1.414213562)); /* c0 */ - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - - tmp11 = z1 + z2; - tmp14 = MULTIPLY(tmp11 + z3 + z4, FIX(0.398430003)); /* c9 */ - tmp11 = MULTIPLY(tmp11, FIX(0.887983902)); /* c3-c9 */ - tmp12 = MULTIPLY(z1 + z3, FIX(0.670361295)); /* c5-c9 */ - tmp13 = tmp14 + MULTIPLY(z1 + z4, FIX(0.366151574)); /* c7-c9 */ - tmp10 = tmp11 + tmp12 + tmp13 - - MULTIPLY(z1, FIX(0.923107866)); /* c7+c5+c3-c1-2*c9 */ - z1 = tmp14 - MULTIPLY(z2 + z3, FIX(1.163011579)); /* c7+c9 */ - tmp11 += z1 + MULTIPLY(z2, FIX(2.073276588)); /* c1+c7+3*c9-c3 */ - tmp12 += z1 - MULTIPLY(z3, FIX(1.192193623)); /* c3+c5-c7-c9 */ - z1 = MULTIPLY(z2 + z4, - FIX(1.798248910)); /* -(c1+c9) */ - tmp11 += z1; - tmp13 += z1 + MULTIPLY(z4, FIX(2.102458632)); /* c1+c5+c9-c7 */ - tmp14 += MULTIPLY(z2, - FIX(1.467221301)) + /* -(c5+c9) */ - MULTIPLY(z3, FIX(1.001388905)) - /* c1-c9 */ - MULTIPLY(z4, FIX(1.684843907)); /* c3+c9 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 12x12 output block. - * - * Optimized algorithm with 15 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/24). - */ - -GLOBAL(void) -jpeg_idct_12x12 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*12]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - z3 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z3 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - z3 += ONE << (CONST_BITS-PASS1_BITS-1); - - z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ - - tmp10 = z3 + z4; - tmp11 = z3 - z4; - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ - z1 <<= CONST_BITS; - z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - z2 <<= CONST_BITS; - - tmp12 = z1 - z2; - - tmp21 = z3 + tmp12; - tmp24 = z3 - tmp12; - - tmp12 = z4 + z2; - - tmp20 = tmp10 + tmp12; - tmp25 = tmp10 - tmp12; - - tmp12 = z4 - z1 - z2; - - tmp22 = tmp11 + tmp12; - tmp23 = tmp11 - tmp12; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ - tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ - - tmp10 = z1 + z3; - tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ - tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ - tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ - tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ - tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ - tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ - tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ - MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ - - z1 -= z4; - z2 -= z3; - z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ - tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ - tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*11] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*10] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 12 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 12; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - z3 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - z3 <<= CONST_BITS; - - z4 = (INT32) wsptr[4]; - z4 = MULTIPLY(z4, FIX(1.224744871)); /* c4 */ - - tmp10 = z3 + z4; - tmp11 = z3 - z4; - - z1 = (INT32) wsptr[2]; - z4 = MULTIPLY(z1, FIX(1.366025404)); /* c2 */ - z1 <<= CONST_BITS; - z2 = (INT32) wsptr[6]; - z2 <<= CONST_BITS; - - tmp12 = z1 - z2; - - tmp21 = z3 + tmp12; - tmp24 = z3 - tmp12; - - tmp12 = z4 + z2; - - tmp20 = tmp10 + tmp12; - tmp25 = tmp10 - tmp12; - - tmp12 = z4 - z1 - z2; - - tmp22 = tmp11 + tmp12; - tmp23 = tmp11 - tmp12; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - - tmp11 = MULTIPLY(z2, FIX(1.306562965)); /* c3 */ - tmp14 = MULTIPLY(z2, - FIX_0_541196100); /* -c9 */ - - tmp10 = z1 + z3; - tmp15 = MULTIPLY(tmp10 + z4, FIX(0.860918669)); /* c7 */ - tmp12 = tmp15 + MULTIPLY(tmp10, FIX(0.261052384)); /* c5-c7 */ - tmp10 = tmp12 + tmp11 + MULTIPLY(z1, FIX(0.280143716)); /* c1-c5 */ - tmp13 = MULTIPLY(z3 + z4, - FIX(1.045510580)); /* -(c7+c11) */ - tmp12 += tmp13 + tmp14 - MULTIPLY(z3, FIX(1.478575242)); /* c1+c5-c7-c11 */ - tmp13 += tmp15 - tmp11 + MULTIPLY(z4, FIX(1.586706681)); /* c1+c11 */ - tmp15 += tmp14 - MULTIPLY(z1, FIX(0.676326758)) - /* c7-c11 */ - MULTIPLY(z4, FIX(1.982889723)); /* c5+c7 */ - - z1 -= z4; - z2 -= z3; - z3 = MULTIPLY(z1 + z2, FIX_0_541196100); /* c9 */ - tmp11 = z3 + MULTIPLY(z1, FIX_0_765366865); /* c3-c9 */ - tmp14 = z3 - MULTIPLY(z2, FIX_1_847759065); /* c3+c9 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 13x13 output block. - * - * Optimized algorithm with 29 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/26). - */ - -GLOBAL(void) -jpeg_idct_13x13 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*13]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z1 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - z1 += ONE << (CONST_BITS-PASS1_BITS-1); - - z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z4 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp10 = z3 + z4; - tmp11 = z3 - z4; - - tmp12 = MULTIPLY(tmp10, FIX(1.155388986)); /* (c4+c6)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.096834934)) + z1; /* (c4-c6)/2 */ - - tmp20 = MULTIPLY(z2, FIX(1.373119086)) + tmp12 + tmp13; /* c2 */ - tmp22 = MULTIPLY(z2, FIX(0.501487041)) - tmp12 + tmp13; /* c10 */ - - tmp12 = MULTIPLY(tmp10, FIX(0.316450131)); /* (c8-c12)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.486914739)) + z1; /* (c8+c12)/2 */ - - tmp21 = MULTIPLY(z2, FIX(1.058554052)) - tmp12 + tmp13; /* c6 */ - tmp25 = MULTIPLY(z2, - FIX(1.252223920)) + tmp12 + tmp13; /* c4 */ - - tmp12 = MULTIPLY(tmp10, FIX(0.435816023)); /* (c2-c10)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.937303064)) - z1; /* (c2+c10)/2 */ - - tmp23 = MULTIPLY(z2, - FIX(0.170464608)) - tmp12 - tmp13; /* c12 */ - tmp24 = MULTIPLY(z2, - FIX(0.803364869)) + tmp12 - tmp13; /* c8 */ - - tmp26 = MULTIPLY(tmp11 - z2, FIX(1.414213562)) + z1; /* c0 */ - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp11 = MULTIPLY(z1 + z2, FIX(1.322312651)); /* c3 */ - tmp12 = MULTIPLY(z1 + z3, FIX(1.163874945)); /* c5 */ - tmp15 = z1 + z4; - tmp13 = MULTIPLY(tmp15, FIX(0.937797057)); /* c7 */ - tmp10 = tmp11 + tmp12 + tmp13 - - MULTIPLY(z1, FIX(2.020082300)); /* c7+c5+c3-c1 */ - tmp14 = MULTIPLY(z2 + z3, - FIX(0.338443458)); /* -c11 */ - tmp11 += tmp14 + MULTIPLY(z2, FIX(0.837223564)); /* c5+c9+c11-c3 */ - tmp12 += tmp14 - MULTIPLY(z3, FIX(1.572116027)); /* c1+c5-c9-c11 */ - tmp14 = MULTIPLY(z2 + z4, - FIX(1.163874945)); /* -c5 */ - tmp11 += tmp14; - tmp13 += tmp14 + MULTIPLY(z4, FIX(2.205608352)); /* c3+c5+c9-c7 */ - tmp14 = MULTIPLY(z3 + z4, - FIX(0.657217813)); /* -c9 */ - tmp12 += tmp14; - tmp13 += tmp14; - tmp15 = MULTIPLY(tmp15, FIX(0.338443458)); /* c11 */ - tmp14 = tmp15 + MULTIPLY(z1, FIX(0.318774355)) - /* c9-c11 */ - MULTIPLY(z2, FIX(0.466105296)); /* c1-c7 */ - z1 = MULTIPLY(z3 - z2, FIX(0.937797057)); /* c7 */ - tmp14 += z1; - tmp15 += z1 + MULTIPLY(z3, FIX(0.384515595)) - /* c3-c7 */ - MULTIPLY(z4, FIX(1.742345811)); /* c1+c11 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*12] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*11] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*10] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp26, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 13 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 13; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - z1 <<= CONST_BITS; - - z2 = (INT32) wsptr[2]; - z3 = (INT32) wsptr[4]; - z4 = (INT32) wsptr[6]; - - tmp10 = z3 + z4; - tmp11 = z3 - z4; - - tmp12 = MULTIPLY(tmp10, FIX(1.155388986)); /* (c4+c6)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.096834934)) + z1; /* (c4-c6)/2 */ - - tmp20 = MULTIPLY(z2, FIX(1.373119086)) + tmp12 + tmp13; /* c2 */ - tmp22 = MULTIPLY(z2, FIX(0.501487041)) - tmp12 + tmp13; /* c10 */ - - tmp12 = MULTIPLY(tmp10, FIX(0.316450131)); /* (c8-c12)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.486914739)) + z1; /* (c8+c12)/2 */ - - tmp21 = MULTIPLY(z2, FIX(1.058554052)) - tmp12 + tmp13; /* c6 */ - tmp25 = MULTIPLY(z2, - FIX(1.252223920)) + tmp12 + tmp13; /* c4 */ - - tmp12 = MULTIPLY(tmp10, FIX(0.435816023)); /* (c2-c10)/2 */ - tmp13 = MULTIPLY(tmp11, FIX(0.937303064)) - z1; /* (c2+c10)/2 */ - - tmp23 = MULTIPLY(z2, - FIX(0.170464608)) - tmp12 - tmp13; /* c12 */ - tmp24 = MULTIPLY(z2, - FIX(0.803364869)) + tmp12 - tmp13; /* c8 */ - - tmp26 = MULTIPLY(tmp11 - z2, FIX(1.414213562)) + z1; /* c0 */ - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - - tmp11 = MULTIPLY(z1 + z2, FIX(1.322312651)); /* c3 */ - tmp12 = MULTIPLY(z1 + z3, FIX(1.163874945)); /* c5 */ - tmp15 = z1 + z4; - tmp13 = MULTIPLY(tmp15, FIX(0.937797057)); /* c7 */ - tmp10 = tmp11 + tmp12 + tmp13 - - MULTIPLY(z1, FIX(2.020082300)); /* c7+c5+c3-c1 */ - tmp14 = MULTIPLY(z2 + z3, - FIX(0.338443458)); /* -c11 */ - tmp11 += tmp14 + MULTIPLY(z2, FIX(0.837223564)); /* c5+c9+c11-c3 */ - tmp12 += tmp14 - MULTIPLY(z3, FIX(1.572116027)); /* c1+c5-c9-c11 */ - tmp14 = MULTIPLY(z2 + z4, - FIX(1.163874945)); /* -c5 */ - tmp11 += tmp14; - tmp13 += tmp14 + MULTIPLY(z4, FIX(2.205608352)); /* c3+c5+c9-c7 */ - tmp14 = MULTIPLY(z3 + z4, - FIX(0.657217813)); /* -c9 */ - tmp12 += tmp14; - tmp13 += tmp14; - tmp15 = MULTIPLY(tmp15, FIX(0.338443458)); /* c11 */ - tmp14 = tmp15 + MULTIPLY(z1, FIX(0.318774355)) - /* c9-c11 */ - MULTIPLY(z2, FIX(0.466105296)); /* c1-c7 */ - z1 = MULTIPLY(z3 - z2, FIX(0.937797057)); /* c7 */ - tmp14 += z1; - tmp15 += z1 + MULTIPLY(z3, FIX(0.384515595)) - /* c3-c7 */ - MULTIPLY(z4, FIX(1.742345811)); /* c1+c11 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 14x14 output block. - * - * Optimized algorithm with 20 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/28). - */ - -GLOBAL(void) -jpeg_idct_14x14 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*14]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z1 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - z1 += ONE << (CONST_BITS-PASS1_BITS-1); - z4 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ - z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ - z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ - - tmp10 = z1 + z2; - tmp11 = z1 + z3; - tmp12 = z1 - z4; - - tmp23 = RIGHT_SHIFT(z1 - ((z2 + z3 - z4) << 1), /* c0 = (c4+c12-c8)*2 */ - CONST_BITS-PASS1_BITS); - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ - - tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ - tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ - tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ - MULTIPLY(z2, FIX(1.378756276)); /* c2 */ - - tmp20 = tmp10 + tmp13; - tmp26 = tmp10 - tmp13; - tmp21 = tmp11 + tmp14; - tmp25 = tmp11 - tmp14; - tmp22 = tmp12 + tmp15; - tmp24 = tmp12 - tmp15; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - tmp13 = z4 << CONST_BITS; - - tmp14 = z1 + z3; - tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ - tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ - tmp10 = tmp11 + tmp12 + tmp13 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ - tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ - tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ - z1 -= z2; - tmp15 = MULTIPLY(z1, FIX(0.467085129)) - tmp13; /* c11 */ - tmp16 += tmp15; - z1 += z4; - z4 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - tmp13; /* -c13 */ - tmp11 += z4 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ - tmp12 += z4 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ - z4 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ - tmp14 += z4 + tmp13 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ - tmp15 += z4 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ - - tmp13 = (z1 - z3) << PASS1_BITS; - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*13] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*12] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*11] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) (tmp23 + tmp13); - wsptr[8*10] = (int) (tmp23 - tmp13); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp16, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp26 - tmp16, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 14 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 14; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - z1 <<= CONST_BITS; - z4 = (INT32) wsptr[4]; - z2 = MULTIPLY(z4, FIX(1.274162392)); /* c4 */ - z3 = MULTIPLY(z4, FIX(0.314692123)); /* c12 */ - z4 = MULTIPLY(z4, FIX(0.881747734)); /* c8 */ - - tmp10 = z1 + z2; - tmp11 = z1 + z3; - tmp12 = z1 - z4; - - tmp23 = z1 - ((z2 + z3 - z4) << 1); /* c0 = (c4+c12-c8)*2 */ - - z1 = (INT32) wsptr[2]; - z2 = (INT32) wsptr[6]; - - z3 = MULTIPLY(z1 + z2, FIX(1.105676686)); /* c6 */ - - tmp13 = z3 + MULTIPLY(z1, FIX(0.273079590)); /* c2-c6 */ - tmp14 = z3 - MULTIPLY(z2, FIX(1.719280954)); /* c6+c10 */ - tmp15 = MULTIPLY(z1, FIX(0.613604268)) - /* c10 */ - MULTIPLY(z2, FIX(1.378756276)); /* c2 */ - - tmp20 = tmp10 + tmp13; - tmp26 = tmp10 - tmp13; - tmp21 = tmp11 + tmp14; - tmp25 = tmp11 - tmp14; - tmp22 = tmp12 + tmp15; - tmp24 = tmp12 - tmp15; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - z4 <<= CONST_BITS; - - tmp14 = z1 + z3; - tmp11 = MULTIPLY(z1 + z2, FIX(1.334852607)); /* c3 */ - tmp12 = MULTIPLY(tmp14, FIX(1.197448846)); /* c5 */ - tmp10 = tmp11 + tmp12 + z4 - MULTIPLY(z1, FIX(1.126980169)); /* c3+c5-c1 */ - tmp14 = MULTIPLY(tmp14, FIX(0.752406978)); /* c9 */ - tmp16 = tmp14 - MULTIPLY(z1, FIX(1.061150426)); /* c9+c11-c13 */ - z1 -= z2; - tmp15 = MULTIPLY(z1, FIX(0.467085129)) - z4; /* c11 */ - tmp16 += tmp15; - tmp13 = MULTIPLY(z2 + z3, - FIX(0.158341681)) - z4; /* -c13 */ - tmp11 += tmp13 - MULTIPLY(z2, FIX(0.424103948)); /* c3-c9-c13 */ - tmp12 += tmp13 - MULTIPLY(z3, FIX(2.373959773)); /* c3+c5-c13 */ - tmp13 = MULTIPLY(z3 - z2, FIX(1.405321284)); /* c1 */ - tmp14 += tmp13 + z4 - MULTIPLY(z3, FIX(1.6906431334)); /* c1+c9-c11 */ - tmp15 += tmp13 + MULTIPLY(z2, FIX(0.674957567)); /* c1+c11-c5 */ - - tmp13 = ((z1 - z3) << CONST_BITS) + z4; - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp16, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp16, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 15x15 output block. - * - * Optimized algorithm with 22 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/30). - */ - -GLOBAL(void) -jpeg_idct_15x15 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp10, tmp11, tmp12, tmp13, tmp14, tmp15, tmp16; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*15]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - z1 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - z1 += ONE << (CONST_BITS-PASS1_BITS-1); - - z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z3 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - z4 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp10 = MULTIPLY(z4, FIX(0.437016024)); /* c12 */ - tmp11 = MULTIPLY(z4, FIX(1.144122806)); /* c6 */ - - tmp12 = z1 - tmp10; - tmp13 = z1 + tmp11; - z1 -= (tmp11 - tmp10) << 1; /* c0 = (c6-c12)*2 */ - - z4 = z2 - z3; - z3 += z2; - tmp10 = MULTIPLY(z3, FIX(1.337628990)); /* (c2+c4)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.045680613)); /* (c2-c4)/2 */ - z2 = MULTIPLY(z2, FIX(1.439773946)); /* c4+c14 */ - - tmp20 = tmp13 + tmp10 + tmp11; - tmp23 = tmp12 - tmp10 + tmp11 + z2; - - tmp10 = MULTIPLY(z3, FIX(0.547059574)); /* (c8+c14)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.399234004)); /* (c8-c14)/2 */ - - tmp25 = tmp13 - tmp10 - tmp11; - tmp26 = tmp12 + tmp10 - tmp11 - z2; - - tmp10 = MULTIPLY(z3, FIX(0.790569415)); /* (c6+c12)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.353553391)); /* (c6-c12)/2 */ - - tmp21 = tmp12 + tmp10 + tmp11; - tmp24 = tmp13 - tmp10 + tmp11; - tmp11 += tmp11; - tmp22 = z1 + tmp11; /* c10 = c6-c12 */ - tmp27 = z1 - tmp11 - tmp11; /* c0 = (c6-c12)*2 */ - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z4 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z3 = MULTIPLY(z4, FIX(1.224744871)); /* c5 */ - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp13 = z2 - z4; - tmp15 = MULTIPLY(z1 + tmp13, FIX(0.831253876)); /* c9 */ - tmp11 = tmp15 + MULTIPLY(z1, FIX(0.513743148)); /* c3-c9 */ - tmp14 = tmp15 - MULTIPLY(tmp13, FIX(2.176250899)); /* c3+c9 */ - - tmp13 = MULTIPLY(z2, - FIX(0.831253876)); /* -c9 */ - tmp15 = MULTIPLY(z2, - FIX(1.344997024)); /* -c3 */ - z2 = z1 - z4; - tmp12 = z3 + MULTIPLY(z2, FIX(1.406466353)); /* c1 */ - - tmp10 = tmp12 + MULTIPLY(z4, FIX(2.457431844)) - tmp15; /* c1+c7 */ - tmp16 = tmp12 - MULTIPLY(z1, FIX(1.112434820)) + tmp13; /* c1-c13 */ - tmp12 = MULTIPLY(z2, FIX(1.224744871)) - z3; /* c5 */ - z2 = MULTIPLY(z1 + z4, FIX(0.575212477)); /* c11 */ - tmp13 += z2 + MULTIPLY(z1, FIX(0.475753014)) - z3; /* c7-c11 */ - tmp15 += z2 - MULTIPLY(z4, FIX(0.869244010)) + z3; /* c11+c13 */ - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*14] = (int) RIGHT_SHIFT(tmp20 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*13] = (int) RIGHT_SHIFT(tmp21 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*12] = (int) RIGHT_SHIFT(tmp22 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*11] = (int) RIGHT_SHIFT(tmp23 - tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*10] = (int) RIGHT_SHIFT(tmp24 - tmp14, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp25 - tmp15, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp16, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp26 - tmp16, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp27, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 15 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 15; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - z1 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - z1 <<= CONST_BITS; - - z2 = (INT32) wsptr[2]; - z3 = (INT32) wsptr[4]; - z4 = (INT32) wsptr[6]; - - tmp10 = MULTIPLY(z4, FIX(0.437016024)); /* c12 */ - tmp11 = MULTIPLY(z4, FIX(1.144122806)); /* c6 */ - - tmp12 = z1 - tmp10; - tmp13 = z1 + tmp11; - z1 -= (tmp11 - tmp10) << 1; /* c0 = (c6-c12)*2 */ - - z4 = z2 - z3; - z3 += z2; - tmp10 = MULTIPLY(z3, FIX(1.337628990)); /* (c2+c4)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.045680613)); /* (c2-c4)/2 */ - z2 = MULTIPLY(z2, FIX(1.439773946)); /* c4+c14 */ - - tmp20 = tmp13 + tmp10 + tmp11; - tmp23 = tmp12 - tmp10 + tmp11 + z2; - - tmp10 = MULTIPLY(z3, FIX(0.547059574)); /* (c8+c14)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.399234004)); /* (c8-c14)/2 */ - - tmp25 = tmp13 - tmp10 - tmp11; - tmp26 = tmp12 + tmp10 - tmp11 - z2; - - tmp10 = MULTIPLY(z3, FIX(0.790569415)); /* (c6+c12)/2 */ - tmp11 = MULTIPLY(z4, FIX(0.353553391)); /* (c6-c12)/2 */ - - tmp21 = tmp12 + tmp10 + tmp11; - tmp24 = tmp13 - tmp10 + tmp11; - tmp11 += tmp11; - tmp22 = z1 + tmp11; /* c10 = c6-c12 */ - tmp27 = z1 - tmp11 - tmp11; /* c0 = (c6-c12)*2 */ - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z4 = (INT32) wsptr[5]; - z3 = MULTIPLY(z4, FIX(1.224744871)); /* c5 */ - z4 = (INT32) wsptr[7]; - - tmp13 = z2 - z4; - tmp15 = MULTIPLY(z1 + tmp13, FIX(0.831253876)); /* c9 */ - tmp11 = tmp15 + MULTIPLY(z1, FIX(0.513743148)); /* c3-c9 */ - tmp14 = tmp15 - MULTIPLY(tmp13, FIX(2.176250899)); /* c3+c9 */ - - tmp13 = MULTIPLY(z2, - FIX(0.831253876)); /* -c9 */ - tmp15 = MULTIPLY(z2, - FIX(1.344997024)); /* -c3 */ - z2 = z1 - z4; - tmp12 = z3 + MULTIPLY(z2, FIX(1.406466353)); /* c1 */ - - tmp10 = tmp12 + MULTIPLY(z4, FIX(2.457431844)) - tmp15; /* c1+c7 */ - tmp16 = tmp12 - MULTIPLY(z1, FIX(1.112434820)) + tmp13; /* c1-c13 */ - tmp12 = MULTIPLY(z2, FIX(1.224744871)) - z3; /* c5 */ - z2 = MULTIPLY(z1 + z4, FIX(0.575212477)); /* c11 */ - tmp13 += z2 + MULTIPLY(z1, FIX(0.475753014)) - z3; /* c7-c11 */ - tmp15 += z2 - MULTIPLY(z4, FIX(0.869244010)) + z3; /* c11+c13 */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[14] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp14, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp15, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp16, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp16, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp27, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a 16x16 output block. - * - * Optimized algorithm with 28 multiplications in the 1-D kernel. - * cK represents sqrt(2) * cos(K*pi/32). - */ - -GLOBAL(void) -jpeg_idct_16x16 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp1, tmp2, tmp3, tmp10, tmp11, tmp12, tmp13; - INT32 tmp20, tmp21, tmp22, tmp23, tmp24, tmp25, tmp26, tmp27; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[8*16]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = 0; ctr < 8; ctr++, inptr++, quantptr++, wsptr++) { - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp0 <<= CONST_BITS; - /* Add fudge factor here for final descale. */ - tmp0 += 1 << (CONST_BITS-PASS1_BITS-1); - - z1 = DEQUANTIZE(inptr[DCTSIZE*4], quantptr[DCTSIZE*4]); - tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ - tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ - - tmp10 = tmp0 + tmp1; - tmp11 = tmp0 - tmp1; - tmp12 = tmp0 + tmp2; - tmp13 = tmp0 - tmp2; - - z1 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z2 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - z3 = z1 - z2; - z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ - z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ - - tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ - tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ - tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ - tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ - - tmp20 = tmp10 + tmp0; - tmp27 = tmp10 - tmp0; - tmp21 = tmp12 + tmp1; - tmp26 = tmp12 - tmp1; - tmp22 = tmp13 + tmp2; - tmp25 = tmp13 - tmp2; - tmp23 = tmp11 + tmp3; - tmp24 = tmp11 - tmp3; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - z2 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z3 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z4 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - - tmp11 = z1 + z3; - - tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ - tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ - tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ - tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ - tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ - tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ - tmp0 = tmp1 + tmp2 + tmp3 - - MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ - tmp13 = tmp10 + tmp11 + tmp12 - - MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ - z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ - tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ - tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ - z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ - tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ - tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ - z2 += z4; - z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ - tmp1 += z1; - tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ - z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ - tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ - tmp12 += z2; - z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ - tmp2 += z2; - tmp3 += z2; - z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ - tmp10 += z2; - tmp11 += z2; - - /* Final output stage */ - - wsptr[8*0] = (int) RIGHT_SHIFT(tmp20 + tmp0, CONST_BITS-PASS1_BITS); - wsptr[8*15] = (int) RIGHT_SHIFT(tmp20 - tmp0, CONST_BITS-PASS1_BITS); - wsptr[8*1] = (int) RIGHT_SHIFT(tmp21 + tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*14] = (int) RIGHT_SHIFT(tmp21 - tmp1, CONST_BITS-PASS1_BITS); - wsptr[8*2] = (int) RIGHT_SHIFT(tmp22 + tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*13] = (int) RIGHT_SHIFT(tmp22 - tmp2, CONST_BITS-PASS1_BITS); - wsptr[8*3] = (int) RIGHT_SHIFT(tmp23 + tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*12] = (int) RIGHT_SHIFT(tmp23 - tmp3, CONST_BITS-PASS1_BITS); - wsptr[8*4] = (int) RIGHT_SHIFT(tmp24 + tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*11] = (int) RIGHT_SHIFT(tmp24 - tmp10, CONST_BITS-PASS1_BITS); - wsptr[8*5] = (int) RIGHT_SHIFT(tmp25 + tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*10] = (int) RIGHT_SHIFT(tmp25 - tmp11, CONST_BITS-PASS1_BITS); - wsptr[8*6] = (int) RIGHT_SHIFT(tmp26 + tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*9] = (int) RIGHT_SHIFT(tmp26 - tmp12, CONST_BITS-PASS1_BITS); - wsptr[8*7] = (int) RIGHT_SHIFT(tmp27 + tmp13, CONST_BITS-PASS1_BITS); - wsptr[8*8] = (int) RIGHT_SHIFT(tmp27 - tmp13, CONST_BITS-PASS1_BITS); - } - - /* Pass 2: process 16 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 16; ctr++) { - outptr = output_buf[ctr] + output_col; - - /* Even part */ - - /* Add fudge factor here for final descale. */ - tmp0 = (INT32) wsptr[0] + (ONE << (PASS1_BITS+2)); - tmp0 <<= CONST_BITS; - - z1 = (INT32) wsptr[4]; - tmp1 = MULTIPLY(z1, FIX(1.306562965)); /* c4[16] = c2[8] */ - tmp2 = MULTIPLY(z1, FIX_0_541196100); /* c12[16] = c6[8] */ - - tmp10 = tmp0 + tmp1; - tmp11 = tmp0 - tmp1; - tmp12 = tmp0 + tmp2; - tmp13 = tmp0 - tmp2; - - z1 = (INT32) wsptr[2]; - z2 = (INT32) wsptr[6]; - z3 = z1 - z2; - z4 = MULTIPLY(z3, FIX(0.275899379)); /* c14[16] = c7[8] */ - z3 = MULTIPLY(z3, FIX(1.387039845)); /* c2[16] = c1[8] */ - - tmp0 = z3 + MULTIPLY(z2, FIX_2_562915447); /* (c6+c2)[16] = (c3+c1)[8] */ - tmp1 = z4 + MULTIPLY(z1, FIX_0_899976223); /* (c6-c14)[16] = (c3-c7)[8] */ - tmp2 = z3 - MULTIPLY(z1, FIX(0.601344887)); /* (c2-c10)[16] = (c1-c5)[8] */ - tmp3 = z4 - MULTIPLY(z2, FIX(0.509795579)); /* (c10-c14)[16] = (c5-c7)[8] */ - - tmp20 = tmp10 + tmp0; - tmp27 = tmp10 - tmp0; - tmp21 = tmp12 + tmp1; - tmp26 = tmp12 - tmp1; - tmp22 = tmp13 + tmp2; - tmp25 = tmp13 - tmp2; - tmp23 = tmp11 + tmp3; - tmp24 = tmp11 - tmp3; - - /* Odd part */ - - z1 = (INT32) wsptr[1]; - z2 = (INT32) wsptr[3]; - z3 = (INT32) wsptr[5]; - z4 = (INT32) wsptr[7]; - - tmp11 = z1 + z3; - - tmp1 = MULTIPLY(z1 + z2, FIX(1.353318001)); /* c3 */ - tmp2 = MULTIPLY(tmp11, FIX(1.247225013)); /* c5 */ - tmp3 = MULTIPLY(z1 + z4, FIX(1.093201867)); /* c7 */ - tmp10 = MULTIPLY(z1 - z4, FIX(0.897167586)); /* c9 */ - tmp11 = MULTIPLY(tmp11, FIX(0.666655658)); /* c11 */ - tmp12 = MULTIPLY(z1 - z2, FIX(0.410524528)); /* c13 */ - tmp0 = tmp1 + tmp2 + tmp3 - - MULTIPLY(z1, FIX(2.286341144)); /* c7+c5+c3-c1 */ - tmp13 = tmp10 + tmp11 + tmp12 - - MULTIPLY(z1, FIX(1.835730603)); /* c9+c11+c13-c15 */ - z1 = MULTIPLY(z2 + z3, FIX(0.138617169)); /* c15 */ - tmp1 += z1 + MULTIPLY(z2, FIX(0.071888074)); /* c9+c11-c3-c15 */ - tmp2 += z1 - MULTIPLY(z3, FIX(1.125726048)); /* c5+c7+c15-c3 */ - z1 = MULTIPLY(z3 - z2, FIX(1.407403738)); /* c1 */ - tmp11 += z1 - MULTIPLY(z3, FIX(0.766367282)); /* c1+c11-c9-c13 */ - tmp12 += z1 + MULTIPLY(z2, FIX(1.971951411)); /* c1+c5+c13-c7 */ - z2 += z4; - z1 = MULTIPLY(z2, - FIX(0.666655658)); /* -c11 */ - tmp1 += z1; - tmp3 += z1 + MULTIPLY(z4, FIX(1.065388962)); /* c3+c11+c15-c7 */ - z2 = MULTIPLY(z2, - FIX(1.247225013)); /* -c5 */ - tmp10 += z2 + MULTIPLY(z4, FIX(3.141271809)); /* c1+c5+c9-c13 */ - tmp12 += z2; - z2 = MULTIPLY(z3 + z4, - FIX(1.353318001)); /* -c3 */ - tmp2 += z2; - tmp3 += z2; - z2 = MULTIPLY(z4 - z3, FIX(0.410524528)); /* c13 */ - tmp10 += z2; - tmp11 += z2; - - /* Final output stage */ - - outptr[0] = range_limit[(int) RIGHT_SHIFT(tmp20 + tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[15] = range_limit[(int) RIGHT_SHIFT(tmp20 - tmp0, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[1] = range_limit[(int) RIGHT_SHIFT(tmp21 + tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[14] = range_limit[(int) RIGHT_SHIFT(tmp21 - tmp1, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[2] = range_limit[(int) RIGHT_SHIFT(tmp22 + tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[13] = range_limit[(int) RIGHT_SHIFT(tmp22 - tmp2, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[3] = range_limit[(int) RIGHT_SHIFT(tmp23 + tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[12] = range_limit[(int) RIGHT_SHIFT(tmp23 - tmp3, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[4] = range_limit[(int) RIGHT_SHIFT(tmp24 + tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[11] = range_limit[(int) RIGHT_SHIFT(tmp24 - tmp10, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[5] = range_limit[(int) RIGHT_SHIFT(tmp25 + tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[10] = range_limit[(int) RIGHT_SHIFT(tmp25 - tmp11, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[6] = range_limit[(int) RIGHT_SHIFT(tmp26 + tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[9] = range_limit[(int) RIGHT_SHIFT(tmp26 - tmp12, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[7] = range_limit[(int) RIGHT_SHIFT(tmp27 + tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - outptr[8] = range_limit[(int) RIGHT_SHIFT(tmp27 - tmp13, - CONST_BITS+PASS1_BITS+3) - & RANGE_MASK]; - - wsptr += 8; /* advance pointer to next row */ - } -} - -#endif /* IDCT_SCALING_SUPPORTED */ -#endif /* DCT_ISLOW_SUPPORTED */ diff -Nru leanify-0.4.3/lib/mozjpeg/jidctred.c leanify-0.4.3+git20181014/lib/mozjpeg/jidctred.c --- leanify-0.4.3/lib/mozjpeg/jidctred.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jidctred.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,398 +0,0 @@ -/* - * jidctred.c - * - * Copyright (C) 1994-1998, Thomas G. Lane. - * This file is part of the Independent JPEG Group's software. - * For conditions of distribution and use, see the accompanying README file. - * - * This file contains inverse-DCT routines that produce reduced-size output: - * either 4x4, 2x2, or 1x1 pixels from an 8x8 DCT block. - * - * The implementation is based on the Loeffler, Ligtenberg and Moschytz (LL&M) - * algorithm used in jidctint.c. We simply replace each 8-to-8 1-D IDCT step - * with an 8-to-4 step that produces the four averages of two adjacent outputs - * (or an 8-to-2 step producing two averages of four outputs, for 2x2 output). - * These steps were derived by computing the corresponding values at the end - * of the normal LL&M code, then simplifying as much as possible. - * - * 1x1 is trivial: just take the DC coefficient divided by 8. - * - * See jidctint.c for additional comments. - */ - -#define JPEG_INTERNALS -#include "jinclude.h" -#include "jpeglib.h" -#include "jdct.h" /* Private declarations for DCT subsystem */ - -#ifdef IDCT_SCALING_SUPPORTED - - -/* - * This module is specialized to the case DCTSIZE = 8. - */ - -#if DCTSIZE != 8 - Sorry, this code only copes with 8x8 DCTs. /* deliberate syntax err */ -#endif - - -/* Scaling is the same as in jidctint.c. */ - -#if BITS_IN_JSAMPLE == 8 -#define CONST_BITS 13 -#define PASS1_BITS 2 -#else -#define CONST_BITS 13 -#define PASS1_BITS 1 /* lose a little precision to avoid overflow */ -#endif - -/* Some C compilers fail to reduce "FIX(constant)" at compile time, thus - * causing a lot of useless floating-point operations at run time. - * To get around this we use the following pre-calculated constants. - * If you change CONST_BITS you may want to add appropriate values. - * (With a reasonable C compiler, you can just rely on the FIX() macro...) - */ - -#if CONST_BITS == 13 -#define FIX_0_211164243 ((INT32) 1730) /* FIX(0.211164243) */ -#define FIX_0_509795579 ((INT32) 4176) /* FIX(0.509795579) */ -#define FIX_0_601344887 ((INT32) 4926) /* FIX(0.601344887) */ -#define FIX_0_720959822 ((INT32) 5906) /* FIX(0.720959822) */ -#define FIX_0_765366865 ((INT32) 6270) /* FIX(0.765366865) */ -#define FIX_0_850430095 ((INT32) 6967) /* FIX(0.850430095) */ -#define FIX_0_899976223 ((INT32) 7373) /* FIX(0.899976223) */ -#define FIX_1_061594337 ((INT32) 8697) /* FIX(1.061594337) */ -#define FIX_1_272758580 ((INT32) 10426) /* FIX(1.272758580) */ -#define FIX_1_451774981 ((INT32) 11893) /* FIX(1.451774981) */ -#define FIX_1_847759065 ((INT32) 15137) /* FIX(1.847759065) */ -#define FIX_2_172734803 ((INT32) 17799) /* FIX(2.172734803) */ -#define FIX_2_562915447 ((INT32) 20995) /* FIX(2.562915447) */ -#define FIX_3_624509785 ((INT32) 29692) /* FIX(3.624509785) */ -#else -#define FIX_0_211164243 FIX(0.211164243) -#define FIX_0_509795579 FIX(0.509795579) -#define FIX_0_601344887 FIX(0.601344887) -#define FIX_0_720959822 FIX(0.720959822) -#define FIX_0_765366865 FIX(0.765366865) -#define FIX_0_850430095 FIX(0.850430095) -#define FIX_0_899976223 FIX(0.899976223) -#define FIX_1_061594337 FIX(1.061594337) -#define FIX_1_272758580 FIX(1.272758580) -#define FIX_1_451774981 FIX(1.451774981) -#define FIX_1_847759065 FIX(1.847759065) -#define FIX_2_172734803 FIX(2.172734803) -#define FIX_2_562915447 FIX(2.562915447) -#define FIX_3_624509785 FIX(3.624509785) -#endif - - -/* Multiply an INT32 variable by an INT32 constant to yield an INT32 result. - * For 8-bit samples with the recommended scaling, all the variable - * and constant values involved are no more than 16 bits wide, so a - * 16x16->32 bit multiply can be used instead of a full 32x32 multiply. - * For 12-bit samples, a full 32-bit multiplication will be needed. - */ - -#if BITS_IN_JSAMPLE == 8 -#define MULTIPLY(var,const) MULTIPLY16C16(var,const) -#else -#define MULTIPLY(var,const) ((var) * (const)) -#endif - - -/* Dequantize a coefficient by multiplying it by the multiplier-table - * entry; produce an int result. In this module, both inputs and result - * are 16 bits or less, so either int or short multiply will work. - */ - -#define DEQUANTIZE(coef,quantval) (((ISLOW_MULT_TYPE) (coef)) * (quantval)) - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 4x4 output block. - */ - -GLOBAL(void) -jpeg_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp2, tmp10, tmp12; - INT32 z1, z2, z3, z4; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[DCTSIZE*4]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { - /* Don't bother to process column 4, because second pass won't use it */ - if (ctr == DCTSIZE-4) - continue; - if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*2] == 0 && - inptr[DCTSIZE*3] == 0 && inptr[DCTSIZE*5] == 0 && - inptr[DCTSIZE*6] == 0 && inptr[DCTSIZE*7] == 0) { - /* AC terms all zero; we need not examine term 4 for 4x4 output */ - int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; - - wsptr[DCTSIZE*0] = dcval; - wsptr[DCTSIZE*1] = dcval; - wsptr[DCTSIZE*2] = dcval; - wsptr[DCTSIZE*3] = dcval; - - continue; - } - - /* Even part */ - - tmp0 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp0 <<= (CONST_BITS+1); - - z2 = DEQUANTIZE(inptr[DCTSIZE*2], quantptr[DCTSIZE*2]); - z3 = DEQUANTIZE(inptr[DCTSIZE*6], quantptr[DCTSIZE*6]); - - tmp2 = MULTIPLY(z2, FIX_1_847759065) + MULTIPLY(z3, - FIX_0_765366865); - - tmp10 = tmp0 + tmp2; - tmp12 = tmp0 - tmp2; - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - z2 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - z3 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - z4 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - - tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ - + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ - + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ - + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ - - tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ - + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ - + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ - + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ - - /* Final output stage */ - - wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp2, CONST_BITS-PASS1_BITS+1); - wsptr[DCTSIZE*3] = (int) DESCALE(tmp10 - tmp2, CONST_BITS-PASS1_BITS+1); - wsptr[DCTSIZE*1] = (int) DESCALE(tmp12 + tmp0, CONST_BITS-PASS1_BITS+1); - wsptr[DCTSIZE*2] = (int) DESCALE(tmp12 - tmp0, CONST_BITS-PASS1_BITS+1); - } - - /* Pass 2: process 4 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 4; ctr++) { - outptr = output_buf[ctr] + output_col; - /* It's not clear whether a zero row test is worthwhile here ... */ - -#ifndef NO_ZERO_ROW_TEST - if (wsptr[1] == 0 && wsptr[2] == 0 && wsptr[3] == 0 && - wsptr[5] == 0 && wsptr[6] == 0 && wsptr[7] == 0) { - /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) - & RANGE_MASK]; - - outptr[0] = dcval; - outptr[1] = dcval; - outptr[2] = dcval; - outptr[3] = dcval; - - wsptr += DCTSIZE; /* advance pointer to next row */ - continue; - } -#endif - - /* Even part */ - - tmp0 = ((INT32) wsptr[0]) << (CONST_BITS+1); - - tmp2 = MULTIPLY((INT32) wsptr[2], FIX_1_847759065) - + MULTIPLY((INT32) wsptr[6], - FIX_0_765366865); - - tmp10 = tmp0 + tmp2; - tmp12 = tmp0 - tmp2; - - /* Odd part */ - - z1 = (INT32) wsptr[7]; - z2 = (INT32) wsptr[5]; - z3 = (INT32) wsptr[3]; - z4 = (INT32) wsptr[1]; - - tmp0 = MULTIPLY(z1, - FIX_0_211164243) /* sqrt(2) * (c3-c1) */ - + MULTIPLY(z2, FIX_1_451774981) /* sqrt(2) * (c3+c7) */ - + MULTIPLY(z3, - FIX_2_172734803) /* sqrt(2) * (-c1-c5) */ - + MULTIPLY(z4, FIX_1_061594337); /* sqrt(2) * (c5+c7) */ - - tmp2 = MULTIPLY(z1, - FIX_0_509795579) /* sqrt(2) * (c7-c5) */ - + MULTIPLY(z2, - FIX_0_601344887) /* sqrt(2) * (c5-c1) */ - + MULTIPLY(z3, FIX_0_899976223) /* sqrt(2) * (c3-c7) */ - + MULTIPLY(z4, FIX_2_562915447); /* sqrt(2) * (c1+c3) */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp2, - CONST_BITS+PASS1_BITS+3+1) - & RANGE_MASK]; - outptr[3] = range_limit[(int) DESCALE(tmp10 - tmp2, - CONST_BITS+PASS1_BITS+3+1) - & RANGE_MASK]; - outptr[1] = range_limit[(int) DESCALE(tmp12 + tmp0, - CONST_BITS+PASS1_BITS+3+1) - & RANGE_MASK]; - outptr[2] = range_limit[(int) DESCALE(tmp12 - tmp0, - CONST_BITS+PASS1_BITS+3+1) - & RANGE_MASK]; - - wsptr += DCTSIZE; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 2x2 output block. - */ - -GLOBAL(void) -jpeg_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - INT32 tmp0, tmp10, z1; - JCOEFPTR inptr; - ISLOW_MULT_TYPE * quantptr; - int * wsptr; - JSAMPROW outptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - int ctr; - int workspace[DCTSIZE*2]; /* buffers data between passes */ - SHIFT_TEMPS - - /* Pass 1: process columns from input, store into work array. */ - - inptr = coef_block; - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - wsptr = workspace; - for (ctr = DCTSIZE; ctr > 0; inptr++, quantptr++, wsptr++, ctr--) { - /* Don't bother to process columns 2,4,6 */ - if (ctr == DCTSIZE-2 || ctr == DCTSIZE-4 || ctr == DCTSIZE-6) - continue; - if (inptr[DCTSIZE*1] == 0 && inptr[DCTSIZE*3] == 0 && - inptr[DCTSIZE*5] == 0 && inptr[DCTSIZE*7] == 0) { - /* AC terms all zero; we need not examine terms 2,4,6 for 2x2 output */ - int dcval = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]) << PASS1_BITS; - - wsptr[DCTSIZE*0] = dcval; - wsptr[DCTSIZE*1] = dcval; - - continue; - } - - /* Even part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*0], quantptr[DCTSIZE*0]); - tmp10 = z1 << (CONST_BITS+2); - - /* Odd part */ - - z1 = DEQUANTIZE(inptr[DCTSIZE*7], quantptr[DCTSIZE*7]); - tmp0 = MULTIPLY(z1, - FIX_0_720959822); /* sqrt(2) * (c7-c5+c3-c1) */ - z1 = DEQUANTIZE(inptr[DCTSIZE*5], quantptr[DCTSIZE*5]); - tmp0 += MULTIPLY(z1, FIX_0_850430095); /* sqrt(2) * (-c1+c3+c5+c7) */ - z1 = DEQUANTIZE(inptr[DCTSIZE*3], quantptr[DCTSIZE*3]); - tmp0 += MULTIPLY(z1, - FIX_1_272758580); /* sqrt(2) * (-c1+c3-c5-c7) */ - z1 = DEQUANTIZE(inptr[DCTSIZE*1], quantptr[DCTSIZE*1]); - tmp0 += MULTIPLY(z1, FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ - - /* Final output stage */ - - wsptr[DCTSIZE*0] = (int) DESCALE(tmp10 + tmp0, CONST_BITS-PASS1_BITS+2); - wsptr[DCTSIZE*1] = (int) DESCALE(tmp10 - tmp0, CONST_BITS-PASS1_BITS+2); - } - - /* Pass 2: process 2 rows from work array, store into output array. */ - - wsptr = workspace; - for (ctr = 0; ctr < 2; ctr++) { - outptr = output_buf[ctr] + output_col; - /* It's not clear whether a zero row test is worthwhile here ... */ - -#ifndef NO_ZERO_ROW_TEST - if (wsptr[1] == 0 && wsptr[3] == 0 && wsptr[5] == 0 && wsptr[7] == 0) { - /* AC terms all zero */ - JSAMPLE dcval = range_limit[(int) DESCALE((INT32) wsptr[0], PASS1_BITS+3) - & RANGE_MASK]; - - outptr[0] = dcval; - outptr[1] = dcval; - - wsptr += DCTSIZE; /* advance pointer to next row */ - continue; - } -#endif - - /* Even part */ - - tmp10 = ((INT32) wsptr[0]) << (CONST_BITS+2); - - /* Odd part */ - - tmp0 = MULTIPLY((INT32) wsptr[7], - FIX_0_720959822) /* sqrt(2) * (c7-c5+c3-c1) */ - + MULTIPLY((INT32) wsptr[5], FIX_0_850430095) /* sqrt(2) * (-c1+c3+c5+c7) */ - + MULTIPLY((INT32) wsptr[3], - FIX_1_272758580) /* sqrt(2) * (-c1+c3-c5-c7) */ - + MULTIPLY((INT32) wsptr[1], FIX_3_624509785); /* sqrt(2) * (c1+c3+c5+c7) */ - - /* Final output stage */ - - outptr[0] = range_limit[(int) DESCALE(tmp10 + tmp0, - CONST_BITS+PASS1_BITS+3+2) - & RANGE_MASK]; - outptr[1] = range_limit[(int) DESCALE(tmp10 - tmp0, - CONST_BITS+PASS1_BITS+3+2) - & RANGE_MASK]; - - wsptr += DCTSIZE; /* advance pointer to next row */ - } -} - - -/* - * Perform dequantization and inverse DCT on one block of coefficients, - * producing a reduced-size 1x1 output block. - */ - -GLOBAL(void) -jpeg_idct_1x1 (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JCOEFPTR coef_block, - JSAMPARRAY output_buf, JDIMENSION output_col) -{ - int dcval; - ISLOW_MULT_TYPE * quantptr; - JSAMPLE *range_limit = IDCT_range_limit(cinfo); - SHIFT_TEMPS - - /* We hardly need an inverse DCT routine for this: just take the - * average pixel value, which is one-eighth of the DC coefficient. - */ - quantptr = (ISLOW_MULT_TYPE *) compptr->dct_table; - dcval = DEQUANTIZE(coef_block[0], quantptr[0]); - dcval = (int) DESCALE((INT32) dcval, 3); - - output_buf[0][output_col] = range_limit[dcval & RANGE_MASK]; -} - -#endif /* IDCT_SCALING_SUPPORTED */ diff -Nru leanify-0.4.3/lib/mozjpeg/jinclude.h leanify-0.4.3+git20181014/lib/mozjpeg/jinclude.h --- leanify-0.4.3/lib/mozjpeg/jinclude.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jinclude.h 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,8 @@ * Copyright (C) 1991-1994, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code relevant * to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file exists to provide a single place to fix any problems with * including the wrong system include files. (Common problems are taken diff -Nru leanify-0.4.3/lib/mozjpeg/jmemmgr.c leanify-0.4.3+git20181014/lib/mozjpeg/jmemmgr.c --- leanify-0.4.3/lib/mozjpeg/jmemmgr.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jmemmgr.c 2018-03-21 11:12:12.000000000 +0000 @@ -3,9 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code and - * information relevant to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2016, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains the JPEG system-independent memory management * routines. This code is usable across a wide variety of machines; most @@ -31,10 +32,14 @@ #include "jinclude.h" #include "jpeglib.h" #include "jmemsys.h" /* import the system-dependent declarations */ +#ifndef _WIN32 +#include +#endif +#include #ifndef NO_GETENV #ifndef HAVE_STDLIB_H /* should declare getenv() */ -extern char * getenv (const char * name); +extern char *getenv (const char *name); #endif #endif @@ -69,9 +74,9 @@ * There isn't any really portable way to determine the worst-case alignment * requirement. This module assumes that the alignment requirement is * multiples of ALIGN_SIZE. - * By default, we define ALIGN_SIZE as sizeof(double). This is necessary on some - * workstations (where doubles really do need 8-byte alignment) and will work - * fine on nearly everything. If your machine has lesser alignment needs, + * By default, we define ALIGN_SIZE as sizeof(double). This is necessary on + * some workstations (where doubles really do need 8-byte alignment) and will + * work fine on nearly everything. If your machine has lesser alignment needs, * you can save a few bytes by making ALIGN_SIZE smaller. * The only place I know of where this will NOT work is certain Macintosh * 680x0 compilers that define double as a 10-byte IEEE extended float. @@ -96,7 +101,7 @@ * Small and large pool headers are identical. */ -typedef struct small_pool_struct * small_pool_ptr; +typedef struct small_pool_struct *small_pool_ptr; typedef struct small_pool_struct { small_pool_ptr next; /* next in list of pools */ @@ -104,7 +109,7 @@ size_t bytes_left; /* bytes still available in this pool */ } small_pool_hdr; -typedef struct large_pool_struct * large_pool_ptr; +typedef struct large_pool_struct *large_pool_ptr; typedef struct large_pool_struct { large_pool_ptr next; /* next in list of pools */ @@ -140,7 +145,7 @@ JDIMENSION last_rowsperchunk; /* from most recent alloc_sarray/barray */ } my_memory_mgr; -typedef my_memory_mgr * my_mem_ptr; +typedef my_memory_mgr *my_mem_ptr; /* @@ -266,7 +271,7 @@ { my_mem_ptr mem = (my_mem_ptr) cinfo->mem; small_pool_ptr hdr_ptr, prev_hdr_ptr; - char * data_ptr; + char *data_ptr; size_t min_request, slop; /* @@ -275,10 +280,16 @@ * and so that algorithms can straddle outside the proper area up * to the next alignment. */ + if (sizeofobject > MAX_ALLOC_CHUNK) { + /* This prevents overflow/wrap-around in round_up_pow2() if sizeofobject + is close to SIZE_MAX. */ + out_of_memory(cinfo, 7); + } sizeofobject = round_up_pow2(sizeofobject, ALIGN_SIZE); /* Check for unsatisfiable request (do now to ensure no overflow below) */ - if ((sizeof(small_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > MAX_ALLOC_CHUNK) + if ((sizeof(small_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > + MAX_ALLOC_CHUNK) out_of_memory(cinfo, 1); /* request exceeds malloc's ability */ /* See if space is available in any existing pool */ @@ -356,17 +367,23 @@ { my_mem_ptr mem = (my_mem_ptr) cinfo->mem; large_pool_ptr hdr_ptr; - char * data_ptr; + char *data_ptr; /* * Round up the requested size to a multiple of ALIGN_SIZE so that * algorithms can straddle outside the proper area up to the next * alignment. */ + if (sizeofobject > MAX_ALLOC_CHUNK) { + /* This prevents overflow/wrap-around in round_up_pow2() if sizeofobject + is close to SIZE_MAX. */ + out_of_memory(cinfo, 8); + } sizeofobject = round_up_pow2(sizeofobject, ALIGN_SIZE); /* Check for unsatisfiable request (do now to ensure no overflow below) */ - if ((sizeof(large_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > MAX_ALLOC_CHUNK) + if ((sizeof(large_pool_hdr) + sizeofobject + ALIGN_SIZE - 1) > + MAX_ALLOC_CHUNK) out_of_memory(cinfo, 3); /* request exceeds malloc's ability */ /* Always make a new pool */ @@ -378,7 +395,8 @@ ALIGN_SIZE - 1); if (hdr_ptr == NULL) out_of_memory(cinfo, 4); /* jpeg_get_large failed */ - mem->total_space_allocated += sizeofobject + sizeof(large_pool_hdr) + ALIGN_SIZE - 1; + mem->total_space_allocated += sizeofobject + sizeof(large_pool_hdr) + + ALIGN_SIZE - 1; /* Success, initialize the new pool header and add to list */ hdr_ptr->next = mem->large_list[pool_id]; @@ -428,7 +446,14 @@ /* Make sure each row is properly aligned */ if ((ALIGN_SIZE % sizeof(JSAMPLE)) != 0) out_of_memory(cinfo, 5); /* safety check */ - samplesperrow = (JDIMENSION)round_up_pow2(samplesperrow, (2 * ALIGN_SIZE) / sizeof(JSAMPLE)); + + if (samplesperrow > MAX_ALLOC_CHUNK) { + /* This prevents overflow/wrap-around in round_up_pow2() if sizeofobject + is close to SIZE_MAX. */ + out_of_memory(cinfo, 9); + } + samplesperrow = (JDIMENSION)round_up_pow2(samplesperrow, (2 * ALIGN_SIZE) / + sizeof(JSAMPLE)); /* Calculate max # of rows allowed in one allocation chunk */ ltemp = (MAX_ALLOC_CHUNK-sizeof(large_pool_hdr)) / @@ -629,18 +654,26 @@ maximum_space = 0; for (sptr = mem->virt_sarray_list; sptr != NULL; sptr = sptr->next) { if (sptr->mem_buffer == NULL) { /* if not realized yet */ + size_t new_space = (long) sptr->rows_in_array * + (long) sptr->samplesperrow * sizeof(JSAMPLE); + space_per_minheight += (long) sptr->maxaccess * (long) sptr->samplesperrow * sizeof(JSAMPLE); - maximum_space += (long) sptr->rows_in_array * - (long) sptr->samplesperrow * sizeof(JSAMPLE); + if (SIZE_MAX - maximum_space < new_space) + out_of_memory(cinfo, 10); + maximum_space += new_space; } } for (bptr = mem->virt_barray_list; bptr != NULL; bptr = bptr->next) { if (bptr->mem_buffer == NULL) { /* if not realized yet */ + size_t new_space = (long) bptr->rows_in_array * + (long) bptr->blocksperrow * sizeof(JBLOCK); + space_per_minheight += (long) bptr->maxaccess * (long) bptr->blocksperrow * sizeof(JBLOCK); - maximum_space += (long) bptr->rows_in_array * - (long) bptr->blocksperrow * sizeof(JBLOCK); + if (SIZE_MAX - maximum_space < new_space) + out_of_memory(cinfo, 11); + maximum_space += new_space; } } @@ -1133,7 +1166,7 @@ * this feature. */ #ifndef NO_GETENV - { char * memenv; + { char *memenv; if ((memenv = getenv("JPEGMEM")) != NULL) { char ch = 'x'; diff -Nru leanify-0.4.3/lib/mozjpeg/jmemnobs.c leanify-0.4.3+git20181014/lib/mozjpeg/jmemnobs.c --- leanify-0.4.3/lib/mozjpeg/jmemnobs.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jmemnobs.c 2018-03-21 11:12:12.000000000 +0000 @@ -3,9 +3,10 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1992-1996, Thomas G. Lane. - * It was modified by The libjpeg-turbo Project to include only code and - * information relevant to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * libjpeg-turbo Modifications: + * Copyright (C) 2017, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file provides a really simple implementation of the system- * dependent portion of the JPEG memory manager. This implementation @@ -14,7 +15,6 @@ * This is very portable in the sense that it'll compile on almost anything, * but you'd better have lots of main memory (or virtual memory) if you want * to process big images. - * Note that the max_memory_to_use option is ignored by this implementation. */ #define JPEG_INTERNALS @@ -23,7 +23,7 @@ #include "jmemsys.h" /* import the system-dependent declarations */ #ifndef HAVE_STDLIB_H /* should declare malloc(),free() */ -extern void * malloc (size_t size); +extern void *malloc (size_t size); extern void free (void *ptr); #endif @@ -40,7 +40,7 @@ } GLOBAL(void) -jpeg_free_small (j_common_ptr cinfo, void * object, size_t sizeofobject) +jpeg_free_small (j_common_ptr cinfo, void *object, size_t sizeofobject) { free(object); } @@ -57,7 +57,7 @@ } GLOBAL(void) -jpeg_free_large (j_common_ptr cinfo, void * object, size_t sizeofobject) +jpeg_free_large (j_common_ptr cinfo, void *object, size_t sizeofobject) { free(object); } @@ -65,14 +65,21 @@ /* * This routine computes the total memory space available for allocation. - * Here we always say, "we got all you want bud!" */ GLOBAL(size_t) jpeg_mem_available (j_common_ptr cinfo, size_t min_bytes_needed, size_t max_bytes_needed, size_t already_allocated) { - return max_bytes_needed; + if (cinfo->mem->max_memory_to_use) { + if (cinfo->mem->max_memory_to_use > already_allocated) + return cinfo->mem->max_memory_to_use - already_allocated; + else + return 0; + } else { + /* Here we always say, "we got all you want bud!" */ + return max_bytes_needed; + } } diff -Nru leanify-0.4.3/lib/mozjpeg/jmemsys.h leanify-0.4.3+git20181014/lib/mozjpeg/jmemsys.h --- leanify-0.4.3/lib/mozjpeg/jmemsys.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jmemsys.h 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,8 @@ * Copyright (C) 1992-1997, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code and * information relevant to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This include file defines the interface between the system-independent * and system-dependent portions of the JPEG memory manager. No other @@ -31,7 +32,7 @@ */ EXTERN(void *) jpeg_get_small (j_common_ptr cinfo, size_t sizeofobject); -EXTERN(void) jpeg_free_small (j_common_ptr cinfo, void * object, +EXTERN(void) jpeg_free_small (j_common_ptr cinfo, void *object, size_t sizeofobject); /* @@ -43,7 +44,7 @@ */ EXTERN(void *) jpeg_get_large (j_common_ptr cinfo, size_t sizeofobject); -EXTERN(void) jpeg_free_large (j_common_ptr cinfo, void * object, +EXTERN(void) jpeg_free_large (j_common_ptr cinfo, void *object, size_t sizeofobject); /* @@ -116,15 +117,15 @@ #endif /* USE_MAC_MEMMGR */ -typedef struct backing_store_struct * backing_store_ptr; +typedef struct backing_store_struct *backing_store_ptr; typedef struct backing_store_struct { /* Methods for reading/writing/closing this backing-store object */ void (*read_backing_store) (j_common_ptr cinfo, backing_store_ptr info, - void * buffer_address, long file_offset, + void *buffer_address, long file_offset, long byte_count); void (*write_backing_store) (j_common_ptr cinfo, backing_store_ptr info, - void * buffer_address, long file_offset, + void *buffer_address, long file_offset, long byte_count); void (*close_backing_store) (j_common_ptr cinfo, backing_store_ptr info); @@ -141,7 +142,7 @@ char temp_name[TEMP_NAME_LENGTH]; /* name if it's a file */ #else /* For a typical implementation with temp files, we need: */ - FILE * temp_file; /* stdio reference to temp file */ + FILE *temp_file; /* stdio reference to temp file */ char temp_name[TEMP_NAME_LENGTH]; /* name of temp file */ #endif #endif diff -Nru leanify-0.4.3/lib/mozjpeg/jmorecfg.h leanify-0.4.3+git20181014/lib/mozjpeg/jmorecfg.h --- leanify-0.4.3/lib/mozjpeg/jmorecfg.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jmorecfg.h 2017-08-01 20:51:09.000000000 +0000 @@ -3,9 +3,11 @@ * * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. + * Modified 1997-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009, 2011, 2014, D. R. Commander. - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2009, 2011, 2014-2015, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains additional configuration options that customize the * JPEG software for special applications or support machine-dependent @@ -145,17 +147,48 @@ typedef short INT16; #endif -/* INT32 must hold at least signed 32-bit values. */ +/* INT32 must hold at least signed 32-bit values. + * + * NOTE: The INT32 typedef dates back to libjpeg v5 (1994.) Integers were + * sometimes 16-bit back then (MS-DOS), which is why INT32 is typedef'd to + * long. It also wasn't common (or at least as common) in 1994 for INT32 to be + * defined by platform headers. Since then, however, INT32 is defined in + * several other common places: + * + * Xmd.h (X11 header) typedefs INT32 to int on 64-bit platforms and long on + * 32-bit platforms (i.e always a 32-bit signed type.) + * + * basetsd.h (Win32 header) typedefs INT32 to int (always a 32-bit signed type + * on modern platforms.) + * + * qglobal.h (Qt header) typedefs INT32 to int (always a 32-bit signed type on + * modern platforms.) + * + * This is a recipe for conflict, since "long" and "int" aren't always + * compatible types. Since the definition of INT32 has technically been part + * of the libjpeg API for more than 20 years, we can't remove it, but we do not + * use it internally any longer. We instead define a separate type (JLONG) + * for internal use, which ensures that internal behavior will always be the + * same regardless of any external headers that may be included. + */ #ifndef XMD_H /* X11/xmd.h correctly defines INT32 */ +#ifndef _BASETSD_H_ /* Microsoft defines it in basetsd.h */ +#ifndef _BASETSD_H /* MinGW is slightly different */ +#ifndef QGLOBAL_H /* Qt defines it in qglobal.h */ typedef long INT32; #endif +#endif +#endif +#endif /* Datatype used for image dimensions. The JPEG standard only supports * images up to 64K*64K due to 16-bit fields in SOF markers. Therefore * "unsigned int" is sufficient on all machines. However, if you need to * handle larger images and you don't mind deviating from the spec, you - * can change this datatype. + * can change this datatype. (Note that changing this datatype will + * potentially require modifying the SIMD code. The x86-64 SIMD extensions, + * in particular, assume a 32-bit JDIMENSION.) */ typedef unsigned int JDIMENSION; @@ -240,9 +273,9 @@ /* Capability options common to encoder and decoder: */ -#define DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ +#undef DCT_ISLOW_SUPPORTED /* slow but accurate integer algorithm */ #undef DCT_IFAST_SUPPORTED /* faster, less accurate integer method */ -#define DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ +#undef DCT_FLOAT_SUPPORTED /* floating-point: accurate, fast on fast HW */ /* Encoder capability options: */ @@ -265,7 +298,7 @@ #define D_PROGRESSIVE_SUPPORTED /* Progressive JPEG? (Requires MULTISCAN)*/ #define SAVE_MARKERS_SUPPORTED /* jpeg_save_markers() needed? */ #undef BLOCK_SMOOTHING_SUPPORTED /* Block smoothing? (Progressive only) */ -#define IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ +#undef IDCT_SCALING_SUPPORTED /* Output rescaling via IDCT? */ #undef UPSAMPLE_SCALING_SUPPORTED /* Output rescaling at upsample stage? */ #undef UPSAMPLE_MERGING_SUPPORTED /* Fast path for sloppy upsampling? */ #undef QUANT_1PASS_SUPPORTED /* 1-pass color quantization? */ diff -Nru leanify-0.4.3/lib/mozjpeg/jpegcomp.h leanify-0.4.3+git20181014/lib/mozjpeg/jpegcomp.h --- leanify-0.4.3/lib/mozjpeg/jpegcomp.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jpegcomp.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,8 +1,9 @@ /* * jpegcomp.h * - * Copyright (C) 2010, D. R. Commander - * For conditions of distribution and use, see the accompanying README file. + * Copyright (C) 2010, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * JPEG compatibility macros * These declarations are considered internal to the JPEG library; most diff -Nru leanify-0.4.3/lib/mozjpeg/jpegint.h leanify-0.4.3+git20181014/lib/mozjpeg/jpegint.h --- leanify-0.4.3/lib/mozjpeg/jpegint.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jpegint.h 2018-03-21 11:12:12.000000000 +0000 @@ -4,11 +4,13 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-1997, Thomas G. Lane. * Modified 1997-2009 by Guido Vollbeding. - * It was modified by The libjpeg-turbo Project to include only code relevant - * to libjpeg-turbo. + * libjpeg-turbo Modifications: + * Copyright (C) 2015-2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. * mozjpeg Modifications: * Copyright (C) 2014, Mozilla Corporation. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file provides common declarations for the various JPEG modules. * These declarations are considered internal to the JPEG library; most @@ -45,6 +47,18 @@ #define DSTATE_STOPPING 210 /* looking for EOI in jpeg_finish_decompress */ +/* JLONG must hold at least signed 32-bit values. */ +typedef long JLONG; + + +/* + * Left shift macro that handles a negative operand without causing any + * sanitizer warnings + */ + +#define LEFT_SHIFT(a, b) ((JLONG)((unsigned long)(a) << (b))) + + /* Declarations for compression modules */ /* Master control module */ @@ -162,7 +176,7 @@ struct jpeg_forward_dct { void (*start_pass) (j_compress_ptr cinfo); /* perhaps this should be an array??? */ - void (*forward_DCT) (j_compress_ptr cinfo, jpeg_component_info * compptr, + void (*forward_DCT) (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY sample_data, JBLOCKROW coef_blocks, JDIMENSION start_row, JDIMENSION start_col, JDIMENSION num_blocks, JBLOCKROW dst); @@ -199,6 +213,13 @@ /* State variables made visible to other modules */ boolean is_dummy_pass; /* True during 1st pass for 2-pass quant */ + + /* Partial decompression variables */ + JDIMENSION first_iMCU_col; + JDIMENSION last_iMCU_col; + JDIMENSION first_MCU_col[MAX_COMPONENTS]; + JDIMENSION last_MCU_col[MAX_COMPONENTS]; + boolean jinit_upsampler_no_alloc; }; /* Input control module */ @@ -272,7 +293,7 @@ /* Inverse DCT (also performs dequantization) */ typedef void (*inverse_DCT_method_ptr) (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); @@ -325,16 +346,16 @@ * shift" instructions that shift in copies of the sign bit. But some * C compilers implement >> with an unsigned shift. For these machines you * must define RIGHT_SHIFT_IS_UNSIGNED. - * RIGHT_SHIFT provides a proper signed right shift of an INT32 quantity. + * RIGHT_SHIFT provides a proper signed right shift of a JLONG quantity. * It is only applied with constant shift counts. SHIFT_TEMPS must be * included in the variables of any routine using RIGHT_SHIFT. */ #ifdef RIGHT_SHIFT_IS_UNSIGNED -#define SHIFT_TEMPS INT32 shift_temp; +#define SHIFT_TEMPS JLONG shift_temp; #define RIGHT_SHIFT(x,shft) \ ((shift_temp = (x)) < 0 ? \ - (shift_temp >> (shft)) | ((~((INT32) 0)) << (32-(shft))) : \ + (shift_temp >> (shft)) | ((~((JLONG) 0)) << (32-(shft))) : \ (shift_temp >> (shft))) #else #define SHIFT_TEMPS @@ -381,6 +402,12 @@ /* Memory manager initialization */ EXTERN(void) jinit_memory_mgr (j_common_ptr cinfo); +#if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) +EXTERN(void) +jpeg_mem_dest_internal (j_compress_ptr cinfo, + unsigned char **outbuffer, unsigned long *outsize, int pool_id); +#endif + /* Utility routines in jutils.c */ EXTERN(long) jdiv_round_up (long a, long b); EXTERN(long) jround_up (long a, long b); @@ -389,7 +416,7 @@ int num_rows, JDIMENSION num_cols); EXTERN(void) jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks); -EXTERN(void) jzero_far (void * target, size_t bytestozero); +EXTERN(void) jzero_far (void *target, size_t bytestozero); #ifdef C_ARITH_CODING_SUPPORTED EXTERN(void) jget_arith_rates (j_compress_ptr cinfo, int dc_tbl_no, int ac_tbl_no, arith_rates *r); @@ -407,7 +434,7 @@ extern const int jpeg_natural_order[]; /* zigzag coef order to natural order */ /* Arithmetic coding probability estimation tables in jaricom.c */ -extern const INT32 jpeg_aritab[]; +extern const JLONG jpeg_aritab[]; /* Suppress undefined-structure complaints if necessary. */ diff -Nru leanify-0.4.3/lib/mozjpeg/jpeglib.h leanify-0.4.3+git20181014/lib/mozjpeg/jpeglib.h --- leanify-0.4.3/lib/mozjpeg/jpeglib.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jpeglib.h 2017-08-01 20:51:09.000000000 +0000 @@ -5,10 +5,12 @@ * Copyright (C) 1991-1998, Thomas G. Lane. * Modified 2002-2009 by Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2009-2011, 2013-2014, D. R. Commander. + * Copyright (C) 2009-2011, 2013-2014, 2016, D. R. Commander. + * Copyright (C) 2015, Google, Inc. * mozjpeg Modifications: * Copyright (C) 2014, Mozilla Corporation. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file defines the application interface for the JPEG library. * Most applications using the library need only include this file, @@ -180,10 +182,10 @@ * See jdinput.c comments about the need for this information. * This field is currently used only for decompression. */ - JQUANT_TBL * quant_table; + JQUANT_TBL *quant_table; /* Private per-component storage for DCT or IDCT subsystem. */ - void * dct_table; + void *dct_table; } jpeg_component_info; @@ -198,14 +200,14 @@ /* The decompressor can save APPn and COM markers in a list of these: */ -typedef struct jpeg_marker_struct * jpeg_saved_marker_ptr; +typedef struct jpeg_marker_struct *jpeg_saved_marker_ptr; struct jpeg_marker_struct { jpeg_saved_marker_ptr next; /* next in list, or NULL */ UINT8 marker; /* marker code: JPEG_COM, or JPEG_APP0+n */ unsigned int original_length; /* # bytes of data in the file */ unsigned int data_length; /* # bytes of data saved at data[] */ - JOCTET * data; /* the data contained in the marker */ + JOCTET *data; /* the data contained in the marker */ /* the marker length word is not counted in data_length or original_length */ }; @@ -314,10 +316,10 @@ /* Common fields between JPEG compression and decompression master structs. */ #define jpeg_common_fields \ - struct jpeg_error_mgr * err; /* Error handler module */\ - struct jpeg_memory_mgr * mem; /* Memory manager module */\ - struct jpeg_progress_mgr * progress; /* Progress monitor, or NULL if none */\ - void * client_data; /* Available for use by application */\ + struct jpeg_error_mgr *err; /* Error handler module */\ + struct jpeg_memory_mgr *mem; /* Memory manager module */\ + struct jpeg_progress_mgr *progress; /* Progress monitor, or NULL if none */\ + void *client_data; /* Available for use by application */\ boolean is_decompressor; /* So common code can tell which is which */\ int global_state /* For checking call sequence validity */ @@ -333,9 +335,9 @@ */ }; -typedef struct jpeg_common_struct * j_common_ptr; -typedef struct jpeg_compress_struct * j_compress_ptr; -typedef struct jpeg_decompress_struct * j_decompress_ptr; +typedef struct jpeg_common_struct *j_common_ptr; +typedef struct jpeg_compress_struct *j_compress_ptr; +typedef struct jpeg_decompress_struct *j_decompress_ptr; /* Master record for a compression instance */ @@ -344,7 +346,7 @@ jpeg_common_fields; /* Fields shared with jpeg_decompress_struct */ /* Destination for compressed data */ - struct jpeg_destination_mgr * dest; + struct jpeg_destination_mgr *dest; /* Description of source image --- these fields must be filled in by * outer application before starting compression. in_color_space must @@ -384,10 +386,10 @@ int num_components; /* # of color components in JPEG image */ J_COLOR_SPACE jpeg_color_space; /* colorspace of JPEG image */ - jpeg_component_info * comp_info; + jpeg_component_info *comp_info; /* comp_info[i] describes component that appears i'th in SOF */ - JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; #if JPEG_LIB_VERSION >= 70 int q_scale_factor[NUM_QUANT_TBLS]; #endif @@ -395,8 +397,8 @@ * and corresponding scale factors (percentage, initialized 100). */ - JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; - JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; /* ptrs to Huffman coding tables, or NULL if not defined */ UINT8 arith_dc_L[NUM_ARITH_TBLS]; /* L values for DC arith-coding tables */ @@ -404,7 +406,7 @@ UINT8 arith_ac_K[NUM_ARITH_TBLS]; /* Kx values for AC arith-coding tables */ int num_scans; /* # of entries in scan_info array */ - const jpeg_scan_info * scan_info; /* script for multi-scan file, or NULL */ + const jpeg_scan_info *scan_info; /* script for multi-scan file, or NULL */ /* The default value of scan_info is NULL, which causes a single-scan * sequential JPEG file to be emitted. To create a multi-scan file, * set num_scans and scan_info to point to an array of scan definitions. @@ -477,7 +479,7 @@ * They describe the components and MCUs actually appearing in the scan. */ int comps_in_scan; /* # of JPEG components in this scan */ - jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; /* *cur_comp_info[i] describes component that appears i'th in SOS */ JDIMENSION MCUs_per_row; /* # of MCUs across the image */ @@ -492,23 +494,23 @@ #if JPEG_LIB_VERSION >= 80 int block_size; /* the basic DCT block size: 1..16 */ - const int * natural_order; /* natural-order position array */ + const int *natural_order; /* natural-order position array */ int lim_Se; /* min( Se, DCTSIZE2-1 ) */ #endif /* * Links to compression subobjects (methods and private variables of modules) */ - struct jpeg_comp_master * master; - struct jpeg_c_main_controller * main; - struct jpeg_c_prep_controller * prep; - struct jpeg_c_coef_controller * coef; - struct jpeg_marker_writer * marker; - struct jpeg_color_converter * cconvert; - struct jpeg_downsampler * downsample; - struct jpeg_forward_dct * fdct; - struct jpeg_entropy_encoder * entropy; - jpeg_scan_info * script_space; /* workspace for jpeg_simple_progression */ + struct jpeg_comp_master *master; + struct jpeg_c_main_controller *main; + struct jpeg_c_prep_controller *prep; + struct jpeg_c_coef_controller *coef; + struct jpeg_marker_writer *marker; + struct jpeg_color_converter *cconvert; + struct jpeg_downsampler *downsample; + struct jpeg_forward_dct *fdct; + struct jpeg_entropy_encoder *entropy; + jpeg_scan_info *script_space; /* workspace for jpeg_simple_progression */ int script_space_size; }; @@ -519,7 +521,7 @@ jpeg_common_fields; /* Fields shared with jpeg_compress_struct */ /* Source of compressed data */ - struct jpeg_source_mgr * src; + struct jpeg_source_mgr *src; /* Basic description of image --- filled in by jpeg_read_header(). */ /* Application may inspect these values to decide how to process image. */ @@ -626,11 +628,11 @@ * datastreams when processing abbreviated JPEG datastreams. */ - JQUANT_TBL * quant_tbl_ptrs[NUM_QUANT_TBLS]; + JQUANT_TBL *quant_tbl_ptrs[NUM_QUANT_TBLS]; /* ptrs to coefficient quantization tables, or NULL if not defined */ - JHUFF_TBL * dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; - JHUFF_TBL * ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *dc_huff_tbl_ptrs[NUM_HUFF_TBLS]; + JHUFF_TBL *ac_huff_tbl_ptrs[NUM_HUFF_TBLS]; /* ptrs to Huffman coding tables, or NULL if not defined */ /* These parameters are never carried across datastreams, since they @@ -639,7 +641,7 @@ int data_precision; /* bits of precision in image data */ - jpeg_component_info * comp_info; + jpeg_component_info *comp_info; /* comp_info[i] describes component that appears i'th in SOF */ #if JPEG_LIB_VERSION >= 80 @@ -701,7 +703,7 @@ * v_samp_factor*DCT_[v_]scaled_size sample rows of a component per iMCU row. */ - JSAMPLE * sample_range_limit; /* table for fast range-limiting */ + JSAMPLE *sample_range_limit; /* table for fast range-limiting */ /* * These fields are valid during any one scan. @@ -709,7 +711,7 @@ * Note that the decompressor output side must not use these fields. */ int comps_in_scan; /* # of JPEG components in this scan */ - jpeg_component_info * cur_comp_info[MAX_COMPS_IN_SCAN]; + jpeg_component_info *cur_comp_info[MAX_COMPS_IN_SCAN]; /* *cur_comp_info[i] describes component that appears i'th in SOS */ JDIMENSION MCUs_per_row; /* # of MCUs across the image */ @@ -726,7 +728,7 @@ /* These fields are derived from Se of first SOS marker. */ int block_size; /* the basic DCT block size: 1..16 */ - const int * natural_order; /* natural-order position array for entropy decode */ + const int *natural_order; /* natural-order position array for entropy decode */ int lim_Se; /* min( Se, DCTSIZE2-1 ) for entropy decode */ #endif @@ -739,17 +741,17 @@ /* * Links to decompression subobjects (methods, private variables of modules) */ - struct jpeg_decomp_master * master; - struct jpeg_d_main_controller * main; - struct jpeg_d_coef_controller * coef; - struct jpeg_d_post_controller * post; - struct jpeg_input_controller * inputctl; - struct jpeg_marker_reader * marker; - struct jpeg_entropy_decoder * entropy; - struct jpeg_inverse_dct * idct; - struct jpeg_upsampler * upsample; - struct jpeg_color_deconverter * cconvert; - struct jpeg_color_quantizer * cquantize; + struct jpeg_decomp_master *master; + struct jpeg_d_main_controller *main; + struct jpeg_d_coef_controller *coef; + struct jpeg_d_post_controller *post; + struct jpeg_input_controller *inputctl; + struct jpeg_marker_reader *marker; + struct jpeg_entropy_decoder *entropy; + struct jpeg_inverse_dct *idct; + struct jpeg_upsampler *upsample; + struct jpeg_color_deconverter *cconvert; + struct jpeg_color_quantizer *cquantize; }; @@ -771,7 +773,7 @@ /* Routine that actually outputs a trace or error message */ void (*output_message) (j_common_ptr cinfo); /* Format a message string for the most recent JPEG error or message */ - void (*format_message) (j_common_ptr cinfo, char * buffer); + void (*format_message) (j_common_ptr cinfo, char *buffer); #define JMSG_LENGTH_MAX 200 /* recommended size of format_message buffer */ /* Reset error state variables at start of a new image */ void (*reset_error_mgr) (j_common_ptr cinfo); @@ -808,12 +810,12 @@ * First table includes all errors generated by JPEG library itself. * Error code 0 is reserved for a "no such error string" message. */ - const char * const * jpeg_message_table; /* Library errors */ + const char * const *jpeg_message_table; /* Library errors */ int last_jpeg_message; /* Table contains strings 0..last_jpeg_message */ /* Second table can be added by application (see cjpeg/djpeg for example). * It contains strings numbered first_addon_message..last_addon_message. */ - const char * const * addon_message_table; /* Non-library errors */ + const char * const *addon_message_table; /* Non-library errors */ int first_addon_message; /* code for first string in addon table */ int last_addon_message; /* code for last string in addon table */ }; @@ -834,7 +836,7 @@ /* Data destination object for compression */ struct jpeg_destination_mgr { - JOCTET * next_output_byte; /* => next byte to write in buffer */ + JOCTET *next_output_byte; /* => next byte to write in buffer */ size_t free_in_buffer; /* # of byte spaces remaining in buffer */ void (*init_destination) (j_compress_ptr cinfo); @@ -846,7 +848,7 @@ /* Data source object for decompression */ struct jpeg_source_mgr { - const JOCTET * next_input_byte; /* => next byte to read from buffer */ + const JOCTET *next_input_byte; /* => next byte to read from buffer */ size_t bytes_in_buffer; /* # of bytes remaining in buffer */ void (*init_source) (j_decompress_ptr cinfo); @@ -872,15 +874,15 @@ #define JPOOL_IMAGE 1 /* lasts until done with image/datastream */ #define JPOOL_NUMPOOLS 2 -typedef struct jvirt_sarray_control * jvirt_sarray_ptr; -typedef struct jvirt_barray_control * jvirt_barray_ptr; +typedef struct jvirt_sarray_control *jvirt_sarray_ptr; +typedef struct jvirt_barray_control *jvirt_barray_ptr; struct jpeg_memory_mgr { /* Method pointers */ - void * (*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); - void * (*alloc_large) (j_common_ptr cinfo, int pool_id, - size_t sizeofobject); + void *(*alloc_small) (j_common_ptr cinfo, int pool_id, size_t sizeofobject); + void *(*alloc_large) (j_common_ptr cinfo, int pool_id, + size_t sizeofobject); JSAMPARRAY (*alloc_sarray) (j_common_ptr cinfo, int pool_id, JDIMENSION samplesperrow, JDIMENSION numrows); JBLOCKARRAY (*alloc_barray) (j_common_ptr cinfo, int pool_id, @@ -934,7 +936,7 @@ /* Default error-management setup */ -EXTERN(struct jpeg_error_mgr *) jpeg_std_error (struct jpeg_error_mgr * err); +EXTERN(struct jpeg_error_mgr *) jpeg_std_error (struct jpeg_error_mgr *err); /* Initialization of JPEG compression objects. * jpeg_create_compress() and jpeg_create_decompress() are the exported @@ -959,14 +961,15 @@ /* Standard data source and destination managers: stdio streams. */ /* Caller is responsible for opening the file before and closing after. */ -EXTERN(void) jpeg_stdio_dest (j_compress_ptr cinfo, FILE * outfile); -EXTERN(void) jpeg_stdio_src (j_decompress_ptr cinfo, FILE * infile); +EXTERN(void) jpeg_stdio_dest (j_compress_ptr cinfo, FILE *outfile); +EXTERN(void) jpeg_stdio_src (j_decompress_ptr cinfo, FILE *infile); #if JPEG_LIB_VERSION >= 80 || defined(MEM_SRCDST_SUPPORTED) /* Data source and destination managers: memory buffers. */ -EXTERN(void) jpeg_mem_dest (j_compress_ptr cinfo, unsigned char ** outbuffer, - unsigned long * outsize); -EXTERN(void) jpeg_mem_src (j_decompress_ptr cinfo, const unsigned char * inbuffer, +EXTERN(void) jpeg_mem_dest (j_compress_ptr cinfo, unsigned char **outbuffer, + unsigned long *outsize); +EXTERN(void) jpeg_mem_src (j_decompress_ptr cinfo, + const unsigned char *inbuffer, unsigned long insize); #endif @@ -1013,7 +1016,7 @@ /* Write a special marker. See libjpeg.txt concerning safe usage. */ EXTERN(void) jpeg_write_marker (j_compress_ptr cinfo, int marker, - const JOCTET * dataptr, unsigned int datalen); + const JOCTET *dataptr, unsigned int datalen); /* Same, but piecemeal. */ EXTERN(void) jpeg_write_m_header (j_compress_ptr cinfo, int marker, unsigned int datalen); @@ -1039,6 +1042,10 @@ EXTERN(JDIMENSION) jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines, JDIMENSION max_lines); +EXTERN(JDIMENSION) jpeg_skip_scanlines (j_decompress_ptr cinfo, + JDIMENSION num_lines); +EXTERN(void) jpeg_crop_scanline (j_decompress_ptr cinfo, JDIMENSION *xoffset, + JDIMENSION *width); EXTERN(boolean) jpeg_finish_decompress (j_decompress_ptr cinfo); /* Replaces jpeg_read_scanlines when reading raw downsampled data. */ @@ -1077,7 +1084,7 @@ /* Read or write raw DCT coefficients --- useful for lossless transcoding. */ EXTERN(jvirt_barray_ptr *) jpeg_read_coefficients (j_decompress_ptr cinfo); EXTERN(void) jpeg_write_coefficients (j_compress_ptr cinfo, - jvirt_barray_ptr * coef_arrays); + jvirt_barray_ptr *coef_arrays); EXTERN(void) jpeg_copy_critical_parameters (const j_decompress_ptr srcinfo, j_compress_ptr dstinfo); @@ -1100,6 +1107,7 @@ EXTERN(boolean) jpeg_resync_to_restart (j_decompress_ptr cinfo, int desired); /* Accessor functions for extension parameters */ +#define JPEG_C_PARAM_SUPPORTED 1 EXTERN(boolean) jpeg_c_bool_param_supported (const j_compress_ptr cinfo, J_BOOLEAN_PARAM param); EXTERN(void) jpeg_c_set_bool_param (j_compress_ptr cinfo, diff -Nru leanify-0.4.3/lib/mozjpeg/jsimddct.h leanify-0.4.3+git20181014/lib/mozjpeg/jsimddct.h --- leanify-0.4.3/lib/mozjpeg/jsimddct.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jsimddct.h 2017-08-01 20:51:09.000000000 +0000 @@ -13,26 +13,26 @@ EXTERN(int) jsimd_can_convsamp_float (void); EXTERN(void) jsimd_convsamp (JSAMPARRAY sample_data, JDIMENSION start_col, - DCTELEM * workspace); + DCTELEM *workspace); EXTERN(void) jsimd_convsamp_float (JSAMPARRAY sample_data, JDIMENSION start_col, - FAST_FLOAT * workspace); + FAST_FLOAT *workspace); EXTERN(int) jsimd_can_fdct_islow (void); EXTERN(int) jsimd_can_fdct_ifast (void); EXTERN(int) jsimd_can_fdct_float (void); -EXTERN(void) jsimd_fdct_islow (DCTELEM * data); -EXTERN(void) jsimd_fdct_ifast (DCTELEM * data); -EXTERN(void) jsimd_fdct_float (FAST_FLOAT * data); +EXTERN(void) jsimd_fdct_islow (DCTELEM *data); +EXTERN(void) jsimd_fdct_ifast (DCTELEM *data); +EXTERN(void) jsimd_fdct_float (FAST_FLOAT *data); EXTERN(int) jsimd_can_quantize (void); EXTERN(int) jsimd_can_quantize_float (void); -EXTERN(void) jsimd_quantize (JCOEFPTR coef_block, DCTELEM * divisors, - DCTELEM * workspace); -EXTERN(void) jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT * divisors, - FAST_FLOAT * workspace); +EXTERN(void) jsimd_quantize (JCOEFPTR coef_block, DCTELEM *divisors, + DCTELEM *workspace); +EXTERN(void) jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT *divisors, + FAST_FLOAT *workspace); EXTERN(int) jsimd_can_idct_2x2 (void); EXTERN(int) jsimd_can_idct_4x4 (void); @@ -40,19 +40,19 @@ EXTERN(int) jsimd_can_idct_12x12 (void); EXTERN(void) jsimd_idct_2x2 (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jsimd_idct_4x4 (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jsimd_idct_6x6 (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jsimd_idct_12x12 (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); @@ -61,14 +61,14 @@ EXTERN(int) jsimd_can_idct_float (void); EXTERN(void) jsimd_idct_islow (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jsimd_idct_ifast (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); EXTERN(void) jsimd_idct_float (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col); diff -Nru leanify-0.4.3/lib/mozjpeg/jsimd.h leanify-0.4.3+git20181014/lib/mozjpeg/jsimd.h --- leanify-0.4.3/lib/mozjpeg/jsimd.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jsimd.h 2017-08-01 20:51:09.000000000 +0000 @@ -2,7 +2,8 @@ * jsimd.h * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2011, 2014 D. R. Commander + * Copyright (C) 2011, 2014, D. R. Commander. + * Copyright (C) 2015, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -10,6 +11,8 @@ * */ +#include "jchuff.h" /* Declarations shared with jcphuff.c */ + EXTERN(int) jsimd_can_rgb_ycc (void); EXTERN(int) jsimd_can_rgb_gray (void); EXTERN(int) jsimd_can_ycc_rgb (void); @@ -36,17 +39,17 @@ EXTERN(int) jsimd_can_h2v1_downsample (void); EXTERN(void) jsimd_h2v2_downsample - (j_compress_ptr cinfo, jpeg_component_info * compptr, + (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data); EXTERN(int) jsimd_can_h2v2_smooth_downsample (void); EXTERN(void) jsimd_h2v2_smooth_downsample - (j_compress_ptr cinfo, jpeg_component_info * compptr, + (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data); EXTERN(void) jsimd_h2v1_downsample - (j_compress_ptr cinfo, jpeg_component_info * compptr, + (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data); EXTERN(int) jsimd_can_h2v2_upsample (void); @@ -54,24 +57,24 @@ EXTERN(int) jsimd_can_int_upsample (void); EXTERN(void) jsimd_h2v2_upsample - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr); + (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr); EXTERN(void) jsimd_h2v1_upsample - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr); + (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr); EXTERN(void) jsimd_int_upsample - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr); + (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr); EXTERN(int) jsimd_can_h2v2_fancy_upsample (void); EXTERN(int) jsimd_can_h2v1_fancy_upsample (void); EXTERN(void) jsimd_h2v2_fancy_upsample - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr); + (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr); EXTERN(void) jsimd_h2v1_fancy_upsample - (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr); + (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr); EXTERN(int) jsimd_can_h2v2_merged_upsample (void); EXTERN(int) jsimd_can_h2v1_merged_upsample (void); @@ -82,3 +85,9 @@ EXTERN(void) jsimd_h2v1_merged_upsample (j_decompress_ptr cinfo, JSAMPIMAGE input_buf, JDIMENSION in_row_group_ctr, JSAMPARRAY output_buf); + +EXTERN(int) jsimd_can_huff_encode_one_block (void); + +EXTERN(JOCTET*) jsimd_huff_encode_one_block + (void *state, JOCTET *buffer, JCOEFPTR block, int last_dc_val, + c_derived_tbl *dctbl, c_derived_tbl *actbl); diff -Nru leanify-0.4.3/lib/mozjpeg/jsimd_none.c leanify-0.4.3+git20181014/lib/mozjpeg/jsimd_none.c --- leanify-0.4.3/lib/mozjpeg/jsimd_none.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jsimd_none.c 2017-08-01 20:51:09.000000000 +0000 @@ -2,7 +2,8 @@ * jsimd_none.c * * Copyright 2009 Pierre Ossman for Cendio AB - * Copyright 2009-2011, 2014 D. R. Commander + * Copyright (C) 2009-2011, 2014, D. R. Commander. + * Copyright (C) 2015, Matthieu Darbois. * * Based on the x86 SIMD extension for IJG JPEG library, * Copyright (C) 1999-2006, MIYASAKA Masaru. @@ -102,19 +103,20 @@ } GLOBAL(void) -jsimd_h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, +jsimd_h2v2_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { } GLOBAL(void) -jsimd_h2v2_smooth_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY output_data) +jsimd_h2v2_smooth_downsample (j_compress_ptr cinfo, + jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY output_data) { } GLOBAL(void) -jsimd_h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info * compptr, +jsimd_h2v1_downsample (j_compress_ptr cinfo, jpeg_component_info *compptr, JSAMPARRAY input_data, JSAMPARRAY output_data) { } @@ -138,24 +140,24 @@ } GLOBAL(void) -jsimd_int_upsample (j_decompress_ptr cinfo, jpeg_component_info * compptr, - JSAMPARRAY input_data, JSAMPARRAY * output_data_ptr) +jsimd_int_upsample (j_decompress_ptr cinfo, jpeg_component_info *compptr, + JSAMPARRAY input_data, JSAMPARRAY *output_data_ptr) { } GLOBAL(void) jsimd_h2v2_upsample (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JSAMPARRAY input_data, - JSAMPARRAY * output_data_ptr) + JSAMPARRAY *output_data_ptr) { } GLOBAL(void) jsimd_h2v1_upsample (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JSAMPARRAY input_data, - JSAMPARRAY * output_data_ptr) + JSAMPARRAY *output_data_ptr) { } @@ -173,17 +175,17 @@ GLOBAL(void) jsimd_h2v2_fancy_upsample (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JSAMPARRAY input_data, - JSAMPARRAY * output_data_ptr) + JSAMPARRAY *output_data_ptr) { } GLOBAL(void) jsimd_h2v1_fancy_upsample (j_decompress_ptr cinfo, - jpeg_component_info * compptr, + jpeg_component_info *compptr, JSAMPARRAY input_data, - JSAMPARRAY * output_data_ptr) + JSAMPARRAY *output_data_ptr) { } @@ -229,13 +231,13 @@ GLOBAL(void) jsimd_convsamp (JSAMPARRAY sample_data, JDIMENSION start_col, - DCTELEM * workspace) + DCTELEM *workspace) { } GLOBAL(void) jsimd_convsamp_float (JSAMPARRAY sample_data, JDIMENSION start_col, - FAST_FLOAT * workspace) + FAST_FLOAT *workspace) { } @@ -258,17 +260,17 @@ } GLOBAL(void) -jsimd_fdct_islow (DCTELEM * data) +jsimd_fdct_islow (DCTELEM *data) { } GLOBAL(void) -jsimd_fdct_ifast (DCTELEM * data) +jsimd_fdct_ifast (DCTELEM *data) { } GLOBAL(void) -jsimd_fdct_float (FAST_FLOAT * data) +jsimd_fdct_float (FAST_FLOAT *data) { } @@ -285,14 +287,14 @@ } GLOBAL(void) -jsimd_quantize (JCOEFPTR coef_block, DCTELEM * divisors, - DCTELEM * workspace) +jsimd_quantize (JCOEFPTR coef_block, DCTELEM *divisors, + DCTELEM *workspace) { } GLOBAL(void) -jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT * divisors, - FAST_FLOAT * workspace) +jsimd_quantize_float (JCOEFPTR coef_block, FAST_FLOAT *divisors, + FAST_FLOAT *workspace) { } @@ -321,28 +323,28 @@ } GLOBAL(void) -jsimd_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_2x2 (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } GLOBAL(void) -jsimd_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_4x4 (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } GLOBAL(void) -jsimd_idct_6x6 (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_6x6 (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } GLOBAL(void) -jsimd_idct_12x12 (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_12x12 (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { @@ -367,23 +369,36 @@ } GLOBAL(void) -jsimd_idct_islow (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_islow (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } GLOBAL(void) -jsimd_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_ifast (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } GLOBAL(void) -jsimd_idct_float (j_decompress_ptr cinfo, jpeg_component_info * compptr, +jsimd_idct_float (j_decompress_ptr cinfo, jpeg_component_info *compptr, JCOEFPTR coef_block, JSAMPARRAY output_buf, JDIMENSION output_col) { } +GLOBAL(int) +jsimd_can_huff_encode_one_block (void) +{ + return 0; +} + +GLOBAL(JOCTET*) +jsimd_huff_encode_one_block (void *state, JOCTET *buffer, JCOEFPTR block, + int last_dc_val, c_derived_tbl *dctbl, + c_derived_tbl *actbl) +{ + return NULL; +} diff -Nru leanify-0.4.3/lib/mozjpeg/jstdhuff.c leanify-0.4.3+git20181014/lib/mozjpeg/jstdhuff.c --- leanify-0.4.3/lib/mozjpeg/jstdhuff.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jstdhuff.c 2017-08-01 20:51:09.000000000 +0000 @@ -1,15 +1,16 @@ /* -* jstdhuff.c -* -* This file was part of the Independent JPEG Group's software: -* Copyright (C) 1991-1998, Thomas G. Lane. -* libjpeg-turbo Modifications: -* Copyright (C) 2013, D. R. Commander. -* For conditions of distribution and use, see the accompanying README file. -* -* This file contains routines to set the default Huffman tables, if they are -* not already set. -*/ + * jstdhuff.c + * + * This file was part of the Independent JPEG Group's software: + * Copyright (C) 1991-1998, Thomas G. Lane. + * libjpeg-turbo Modifications: + * Copyright (C) 2013, D. R. Commander. + * For conditions of distribution and use, see the accompanying README.ijg + * file. + * + * This file contains routines to set the default Huffman tables, if they are + * not already set. + */ /* * Huffman table setup routines @@ -41,6 +42,7 @@ ERREXIT(cinfo, JERR_BAD_HUFF_TABLE); MEMCOPY((*htblptr)->huffval, val, nsymbols * sizeof(UINT8)); + MEMZERO(&((*htblptr)->huffval[nsymbols]), (256 - nsymbols) * sizeof(UINT8)); /* Initialize sent_table FALSE so table will be written to JPEG file. */ (*htblptr)->sent_table = FALSE; diff -Nru leanify-0.4.3/lib/mozjpeg/jutils.c leanify-0.4.3+git20181014/lib/mozjpeg/jutils.c --- leanify-0.4.3/lib/mozjpeg/jutils.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jutils.c 2017-08-01 20:51:09.000000000 +0000 @@ -5,7 +5,8 @@ * Copyright (C) 1991-1996, Thomas G. Lane. * It was modified by The libjpeg-turbo Project to include only code * relevant to libjpeg-turbo. - * For conditions of distribution and use, see the accompanying README file. + * For conditions of distribution and use, see the accompanying README.ijg + * file. * * This file contains tables and miscellaneous utility routines needed * for both compression and decompression. @@ -124,7 +125,7 @@ GLOBAL(void) -jzero_far (void * target, size_t bytestozero) +jzero_far (void *target, size_t bytestozero) /* Zero out a chunk of memory. */ /* This might be sample-array data, block-array data, or alloc_large data. */ { diff -Nru leanify-0.4.3/lib/mozjpeg/jversion.h leanify-0.4.3+git20181014/lib/mozjpeg/jversion.h --- leanify-0.4.3/lib/mozjpeg/jversion.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/mozjpeg/jversion.h 2018-03-21 11:12:12.000000000 +0000 @@ -4,7 +4,7 @@ * This file was part of the Independent JPEG Group's software: * Copyright (C) 1991-2012, Thomas G. Lane, Guido Vollbeding. * libjpeg-turbo Modifications: - * Copyright (C) 2010, 2012-2014, D. R. Commander. + * Copyright (C) 2010, 2012-2017, D. R. Commander. * mozjpeg Modifications: * Copyright (C) 2014, Mozilla Corporation. * For conditions of distribution and use, see the accompanying README file. @@ -27,13 +27,25 @@ #endif -#define JCOPYRIGHT "Copyright (C) 1991-2012 Thomas G. Lane, Guido Vollbeding\n" \ - "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ - "Copyright (C) 2009 Pierre Ossman for Cendio AB\n" \ - "Copyright (C) 2009-2014 D. R. Commander\n" \ - "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ +/* + * NOTE: It is our convention to place the authors in the following order: + * - libjpeg-turbo authors (2009-) in descending order of the date of their + * most recent contribution to the project, then in ascending order of the + * date of their first contribution to the project + * - Upstream authors in descending order of the date of the first inclusion of + * their code + */ + +#define JCOPYRIGHT "Copyright (C) 2009-2017 D. R. Commander\n" \ + "Copyright (C) 2011-2016 Siarhei Siamashka\n" \ + "Copyright (C) 2015-2016 Matthieu Darbois\n" \ + "Copyright (C) 2015 Google, Inc.\n" \ "Copyright (C) 2014 Mozilla Corporation\n" \ "Copyright (C) 2013-2014 MIPS Technologies, Inc.\n" \ - "Copyright (C) 2013 Linaro Limited" + "Copyright (C) 2013 Linaro Limited\n" \ + "Copyright (C) 2009-2011 Nokia Corporation and/or its subsidiary(-ies)\n" \ + "Copyright (C) 2009 Pierre Ossman for Cendio AB\n" \ + "Copyright (C) 1999-2006 MIYASAKA Masaru\n" \ + "Copyright (C) 1991-2016 Thomas G. Lane, Guido Vollbeding" \ -#define JCOPYRIGHT_SHORT "Copyright (C) 1991-2014 The libjpeg-turbo Project and many others" +#define JCOPYRIGHT_SHORT "Copyright (C) 1991-2017 The libjpeg-turbo Project and many others" diff -Nru leanify-0.4.3/lib/pugixml/pugiconfig.hpp leanify-0.4.3+git20181014/lib/pugixml/pugiconfig.hpp --- leanify-0.4.3/lib/pugixml/pugiconfig.hpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/pugixml/pugiconfig.hpp 2018-05-05 21:00:58.000000000 +0000 @@ -0,0 +1,74 @@ +/** + * pugixml parser - version 1.9 + * -------------------------------------------------------- + * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef HEADER_PUGICONFIG_HPP +#define HEADER_PUGICONFIG_HPP + +// Uncomment this to enable wchar_t mode +// #define PUGIXML_WCHAR_MODE + +// Uncomment this to enable compact mode +// #define PUGIXML_COMPACT + +// Uncomment this to disable XPath +#define PUGIXML_NO_XPATH + +// Uncomment this to disable STL +// #define PUGIXML_NO_STL + +// Uncomment this to disable exceptions +#define PUGIXML_NO_EXCEPTIONS + +// Set this to control attributes for public classes/functions, i.e.: +// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL +// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL +// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall +// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead + +// Tune these constants to adjust memory-related behavior +// #define PUGIXML_MEMORY_PAGE_SIZE 32768 +// #define PUGIXML_MEMORY_OUTPUT_STACK 10240 +// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096 + +// Uncomment this to switch to header-only version +// #define PUGIXML_HEADER_ONLY + +// Uncomment this to enable long long support +// #define PUGIXML_HAS_LONG_LONG + +#endif + +/** + * Copyright (c) 2006-2018 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff -Nru leanify-0.4.3/lib/pugixml/pugixml.cpp leanify-0.4.3+git20181014/lib/pugixml/pugixml.cpp --- leanify-0.4.3/lib/pugixml/pugixml.cpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/pugixml/pugixml.cpp 2018-05-05 21:00:58.000000000 +0000 @@ -0,0 +1,12796 @@ +/** + * pugixml parser - version 1.9 + * -------------------------------------------------------- + * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef SOURCE_PUGIXML_CPP +#define SOURCE_PUGIXML_CPP + +#include "pugixml.hpp" + +#include +#include +#include +#include +#include + +#ifdef PUGIXML_WCHAR_MODE +# include +#endif + +#ifndef PUGIXML_NO_XPATH +# include +# include +#endif + +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// For placement new +#include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4127) // conditional expression is constant +# pragma warning(disable: 4324) // structure was padded due to __declspec(align()) +# pragma warning(disable: 4702) // unreachable code +# pragma warning(disable: 4996) // this function or variable may be unsafe +#endif + +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wdeprecated" // this function or variable may be unsafe +#endif + +#ifdef __INTEL_COMPILER +# pragma warning(disable: 177) // function was declared but never referenced +# pragma warning(disable: 279) // controlling expression is constant +# pragma warning(disable: 1478 1786) // function was declared "deprecated" +# pragma warning(disable: 1684) // conversion from pointer to same-sized integral type +#endif + +#if defined(__BORLANDC__) && defined(PUGIXML_HEADER_ONLY) +# pragma warn -8080 // symbol is declared but never used; disabling this inside push/pop bracket does not make the warning go away +#endif + +#ifdef __BORLANDC__ +# pragma option push +# pragma warn -8008 // condition is always false +# pragma warn -8066 // unreachable code +#endif + +#ifdef __SNC__ +// Using diag_push/diag_pop does not disable the warnings inside templates due to a compiler bug +# pragma diag_suppress=178 // function was declared but never referenced +# pragma diag_suppress=237 // controlling expression is constant +#endif + +#ifdef __TI_COMPILER_VERSION__ +# pragma diag_suppress 179 // function was declared but never referenced +#endif + +// Inlining controls +#if defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGI__NO_INLINE __declspec(noinline) +#elif defined(__GNUC__) +# define PUGI__NO_INLINE __attribute__((noinline)) +#else +# define PUGI__NO_INLINE +#endif + +// Branch weight controls +#if defined(__GNUC__) && !defined(__c2__) +# define PUGI__UNLIKELY(cond) __builtin_expect(cond, 0) +#else +# define PUGI__UNLIKELY(cond) (cond) +#endif + +// Simple static assertion +#define PUGI__STATIC_ASSERT(cond) { static const char condition_failed[(cond) ? 1 : -1] = {0}; (void)condition_failed[0]; } + +// Digital Mars C++ bug workaround for passing char loaded from memory via stack +#ifdef __DMC__ +# define PUGI__DMC_VOLATILE volatile +#else +# define PUGI__DMC_VOLATILE +#endif + +// Integer sanitizer workaround; we only apply this for clang since gcc8 has no_sanitize but not unsigned-integer-overflow and produces "attribute directive ignored" warnings +#if defined(__clang__) && defined(__has_attribute) +# if __has_attribute(no_sanitize) +# define PUGI__UNSIGNED_OVERFLOW __attribute__((no_sanitize("unsigned-integer-overflow"))) +# else +# define PUGI__UNSIGNED_OVERFLOW +# endif +#else +# define PUGI__UNSIGNED_OVERFLOW +#endif + +// Borland C++ bug workaround for not defining ::memcpy depending on header include order (can't always use std::memcpy because some compilers don't have it at all) +#if defined(__BORLANDC__) && !defined(__MEM_H_USING_LIST) +using std::memcpy; +using std::memmove; +using std::memset; +#endif + +// Some MinGW/GCC versions have headers that erroneously omit LLONG_MIN/LLONG_MAX/ULLONG_MAX definitions from limits.h in some configurations +#if defined(PUGIXML_HAS_LONG_LONG) && defined(__GNUC__) && !defined(LLONG_MAX) && !defined(LLONG_MIN) && !defined(ULLONG_MAX) +# define LLONG_MIN (-LLONG_MAX - 1LL) +# define LLONG_MAX __LONG_LONG_MAX__ +# define ULLONG_MAX (LLONG_MAX * 2ULL + 1ULL) +#endif + +// In some environments MSVC is a compiler but the CRT lacks certain MSVC-specific features +#if defined(_MSC_VER) && !defined(__S3E__) +# define PUGI__MSVC_CRT_VERSION _MSC_VER +#endif + +// Not all platforms have snprintf; we define a wrapper that uses snprintf if possible. This only works with buffers with a known size. +#if __cplusplus >= 201103 +# define PUGI__SNPRINTF(buf, ...) snprintf(buf, sizeof(buf), __VA_ARGS__) +#elif defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 +# define PUGI__SNPRINTF(buf, ...) _snprintf_s(buf, _countof(buf), _TRUNCATE, __VA_ARGS__) +#else +# define PUGI__SNPRINTF sprintf +#endif + +// We put implementation details into an anonymous namespace in source mode, but have to keep it in non-anonymous namespace in header-only mode to prevent binary bloat. +#ifdef PUGIXML_HEADER_ONLY +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# define PUGI__FN inline +# define PUGI__FN_NO_INLINE inline +#else +# if defined(_MSC_VER) && _MSC_VER < 1300 // MSVC6 seems to have an amusing bug with anonymous namespaces inside namespaces +# define PUGI__NS_BEGIN namespace pugi { namespace impl { +# define PUGI__NS_END } } +# else +# define PUGI__NS_BEGIN namespace pugi { namespace impl { namespace { +# define PUGI__NS_END } } } +# endif +# define PUGI__FN +# define PUGI__FN_NO_INLINE PUGI__NO_INLINE +#endif + +// uintptr_t +#if (defined(_MSC_VER) && _MSC_VER < 1600) || (defined(__BORLANDC__) && __BORLANDC__ < 0x561) +namespace pugi +{ +# ifndef _UINTPTR_T_DEFINED + typedef size_t uintptr_t; +# endif + + typedef unsigned __int8 uint8_t; + typedef unsigned __int16 uint16_t; + typedef unsigned __int32 uint32_t; +} +#else +# include +#endif + +// Memory allocation +PUGI__NS_BEGIN + PUGI__FN void* default_allocate(size_t size) + { + return malloc(size); + } + + PUGI__FN void default_deallocate(void* ptr) + { + free(ptr); + } + + template + struct xml_memory_management_function_storage + { + static allocation_function allocate; + static deallocation_function deallocate; + }; + + // Global allocation functions are stored in class statics so that in header mode linker deduplicates them + // Without a template<> we'll get multiple definitions of the same static + template allocation_function xml_memory_management_function_storage::allocate = default_allocate; + template deallocation_function xml_memory_management_function_storage::deallocate = default_deallocate; + + typedef xml_memory_management_function_storage xml_memory; +PUGI__NS_END + +// String utilities +PUGI__NS_BEGIN + // Get string length + PUGI__FN size_t strlength(const char_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + return strlen(s); + #endif + } + + // Compare two strings + PUGI__FN bool strequal(const char_t* src, const char_t* dst) + { + assert(src && dst); + + #ifdef PUGIXML_WCHAR_MODE + return wcscmp(src, dst) == 0; + #else + return strcmp(src, dst) == 0; + #endif + } + + // Compare lhs with [rhs_begin, rhs_end) + PUGI__FN bool strequalrange(const char_t* lhs, const char_t* rhs, size_t count) + { + for (size_t i = 0; i < count; ++i) + if (lhs[i] != rhs[i]) + return false; + + return lhs[count] == 0; + } + + // Get length of wide string, even if CRT lacks wide character support + PUGI__FN size_t strlength_wide(const wchar_t* s) + { + assert(s); + + #ifdef PUGIXML_WCHAR_MODE + return wcslen(s); + #else + const wchar_t* end = s; + while (*end) end++; + return static_cast(end - s); + #endif + } +PUGI__NS_END + +// auto_ptr-like object for exception recovery +PUGI__NS_BEGIN + template struct auto_deleter + { + typedef void (*D)(T*); + + T* data; + D deleter; + + auto_deleter(T* data_, D deleter_): data(data_), deleter(deleter_) + { + } + + ~auto_deleter() + { + if (data) deleter(data); + } + + T* release() + { + T* result = data; + data = 0; + return result; + } + }; +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + class compact_hash_table + { + public: + compact_hash_table(): _items(0), _capacity(0), _count(0) + { + } + + void clear() + { + if (_items) + { + xml_memory::deallocate(_items); + _items = 0; + _capacity = 0; + _count = 0; + } + } + + void* find(const void* key) + { + if (_capacity == 0) return 0; + + item_t* item = get_item(key); + assert(item); + assert(item->key == key || (item->key == 0 && item->value == 0)); + + return item->value; + } + + void insert(const void* key, void* value) + { + assert(_capacity != 0 && _count < _capacity - _capacity / 4); + + item_t* item = get_item(key); + assert(item); + + if (item->key == 0) + { + _count++; + item->key = key; + } + + item->value = value; + } + + bool reserve(size_t extra = 16) + { + if (_count + extra >= _capacity - _capacity / 4) + return rehash(_count + extra); + + return true; + } + + private: + struct item_t + { + const void* key; + void* value; + }; + + item_t* _items; + size_t _capacity; + + size_t _count; + + bool rehash(size_t count); + + item_t* get_item(const void* key) + { + assert(key); + assert(_capacity > 0); + + size_t hashmod = _capacity - 1; + size_t bucket = hash(key) & hashmod; + + for (size_t probe = 0; probe <= hashmod; ++probe) + { + item_t& probe_item = _items[bucket]; + + if (probe_item.key == key || probe_item.key == 0) + return &probe_item; + + // hash collision, quadratic probing + bucket = (bucket + probe + 1) & hashmod; + } + + assert(false && "Hash table is full"); // unreachable + return 0; + } + + static PUGI__UNSIGNED_OVERFLOW unsigned int hash(const void* key) + { + unsigned int h = static_cast(reinterpret_cast(key)); + + // MurmurHash3 32-bit finalizer + h ^= h >> 16; + h *= 0x85ebca6bu; + h ^= h >> 13; + h *= 0xc2b2ae35u; + h ^= h >> 16; + + return h; + } + }; + + PUGI__FN_NO_INLINE bool compact_hash_table::rehash(size_t count) + { + size_t capacity = 32; + while (count >= capacity - capacity / 4) + capacity *= 2; + + compact_hash_table rt; + rt._capacity = capacity; + rt._items = static_cast(xml_memory::allocate(sizeof(item_t) * capacity)); + + if (!rt._items) + return false; + + memset(rt._items, 0, sizeof(item_t) * capacity); + + for (size_t i = 0; i < _capacity; ++i) + if (_items[i].key) + rt.insert(_items[i].key, _items[i].value); + + if (_items) + xml_memory::deallocate(_items); + + _capacity = capacity; + _items = rt._items; + + assert(_count == rt._count); + + return true; + } + +PUGI__NS_END +#endif + +PUGI__NS_BEGIN +#ifdef PUGIXML_COMPACT + static const uintptr_t xml_memory_block_alignment = 4; +#else + static const uintptr_t xml_memory_block_alignment = sizeof(void*); +#endif + + // extra metadata bits + static const uintptr_t xml_memory_page_contents_shared_mask = 64; + static const uintptr_t xml_memory_page_name_allocated_mask = 32; + static const uintptr_t xml_memory_page_value_allocated_mask = 16; + static const uintptr_t xml_memory_page_type_mask = 15; + + // combined masks for string uniqueness + static const uintptr_t xml_memory_page_name_allocated_or_shared_mask = xml_memory_page_name_allocated_mask | xml_memory_page_contents_shared_mask; + static const uintptr_t xml_memory_page_value_allocated_or_shared_mask = xml_memory_page_value_allocated_mask | xml_memory_page_contents_shared_mask; + +#ifdef PUGIXML_COMPACT + #define PUGI__GETHEADER_IMPL(object, page, flags) // unused + #define PUGI__GETPAGE_IMPL(header) (header).get_page() +#else + #define PUGI__GETHEADER_IMPL(object, page, flags) (((reinterpret_cast(object) - reinterpret_cast(page)) << 8) | (flags)) + // this macro casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + #define PUGI__GETPAGE_IMPL(header) static_cast(const_cast(static_cast(reinterpret_cast(&header) - (header >> 8)))) +#endif + + #define PUGI__GETPAGE(n) PUGI__GETPAGE_IMPL((n)->header) + #define PUGI__NODETYPE(n) static_cast((n)->header & impl::xml_memory_page_type_mask) + + struct xml_allocator; + + struct xml_memory_page + { + static xml_memory_page* construct(void* memory) + { + xml_memory_page* result = static_cast(memory); + + result->allocator = 0; + result->prev = 0; + result->next = 0; + result->busy_size = 0; + result->freed_size = 0; + + #ifdef PUGIXML_COMPACT + result->compact_string_base = 0; + result->compact_shared_parent = 0; + result->compact_page_marker = 0; + #endif + + return result; + } + + xml_allocator* allocator; + + xml_memory_page* prev; + xml_memory_page* next; + + size_t busy_size; + size_t freed_size; + + #ifdef PUGIXML_COMPACT + char_t* compact_string_base; + void* compact_shared_parent; + uint32_t* compact_page_marker; + #endif + }; + + static const size_t xml_memory_page_size = + #ifdef PUGIXML_MEMORY_PAGE_SIZE + (PUGIXML_MEMORY_PAGE_SIZE) + #else + 32768 + #endif + - sizeof(xml_memory_page); + + struct xml_memory_string_header + { + uint16_t page_offset; // offset from page->data + uint16_t full_size; // 0 if string occupies whole page + }; + + struct xml_allocator + { + xml_allocator(xml_memory_page* root): _root(root), _busy_size(root->busy_size) + { + #ifdef PUGIXML_COMPACT + _hash = 0; + #endif + } + + xml_memory_page* allocate_page(size_t data_size) + { + size_t size = sizeof(xml_memory_page) + data_size; + + // allocate block with some alignment, leaving memory for worst-case padding + void* memory = xml_memory::allocate(size); + if (!memory) return 0; + + // prepare page structure + xml_memory_page* page = xml_memory_page::construct(memory); + assert(page); + + page->allocator = _root->allocator; + + return page; + } + + static void deallocate_page(xml_memory_page* page) + { + xml_memory::deallocate(page); + } + + void* allocate_memory_oob(size_t size, xml_memory_page*& out_page); + + void* allocate_memory(size_t size, xml_memory_page*& out_page) + { + if (PUGI__UNLIKELY(_busy_size + size > xml_memory_page_size)) + return allocate_memory_oob(size, out_page); + + void* buf = reinterpret_cast(_root) + sizeof(xml_memory_page) + _busy_size; + + _busy_size += size; + + out_page = _root; + + return buf; + } + + #ifdef PUGIXML_COMPACT + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + void* result = allocate_memory(size + sizeof(uint32_t), out_page); + if (!result) return 0; + + // adjust for marker + ptrdiff_t offset = static_cast(result) - reinterpret_cast(out_page->compact_page_marker); + + if (PUGI__UNLIKELY(static_cast(offset) >= 256 * xml_memory_block_alignment)) + { + // insert new marker + uint32_t* marker = static_cast(result); + + *marker = static_cast(reinterpret_cast(marker) - reinterpret_cast(out_page)); + out_page->compact_page_marker = marker; + + // since we don't reuse the page space until we reallocate it, we can just pretend that we freed the marker block + // this will make sure deallocate_memory correctly tracks the size + out_page->freed_size += sizeof(uint32_t); + + return marker + 1; + } + else + { + // roll back uint32_t part + _busy_size -= sizeof(uint32_t); + + return result; + } + } + #else + void* allocate_object(size_t size, xml_memory_page*& out_page) + { + return allocate_memory(size, out_page); + } + #endif + + void deallocate_memory(void* ptr, size_t size, xml_memory_page* page) + { + if (page == _root) page->busy_size = _busy_size; + + assert(ptr >= reinterpret_cast(page) + sizeof(xml_memory_page) && ptr < reinterpret_cast(page) + sizeof(xml_memory_page) + page->busy_size); + (void)!ptr; + + page->freed_size += size; + assert(page->freed_size <= page->busy_size); + + if (page->freed_size == page->busy_size) + { + if (page->next == 0) + { + assert(_root == page); + + // top page freed, just reset sizes + page->busy_size = 0; + page->freed_size = 0; + + #ifdef PUGIXML_COMPACT + // reset compact state to maximize efficiency + page->compact_string_base = 0; + page->compact_shared_parent = 0; + page->compact_page_marker = 0; + #endif + + _busy_size = 0; + } + else + { + assert(_root != page); + assert(page->prev); + + // remove from the list + page->prev->next = page->next; + page->next->prev = page->prev; + + // deallocate + deallocate_page(page); + } + } + } + + char_t* allocate_string(size_t length) + { + static const size_t max_encoded_offset = (1 << 16) * xml_memory_block_alignment; + + PUGI__STATIC_ASSERT(xml_memory_page_size <= max_encoded_offset); + + // allocate memory for string and header block + size_t size = sizeof(xml_memory_string_header) + length * sizeof(char_t); + + // round size up to block alignment boundary + size_t full_size = (size + (xml_memory_block_alignment - 1)) & ~(xml_memory_block_alignment - 1); + + xml_memory_page* page; + xml_memory_string_header* header = static_cast(allocate_memory(full_size, page)); + + if (!header) return 0; + + // setup header + ptrdiff_t page_offset = reinterpret_cast(header) - reinterpret_cast(page) - sizeof(xml_memory_page); + + assert(page_offset % xml_memory_block_alignment == 0); + assert(page_offset >= 0 && static_cast(page_offset) < max_encoded_offset); + header->page_offset = static_cast(static_cast(page_offset) / xml_memory_block_alignment); + + // full_size == 0 for large strings that occupy the whole page + assert(full_size % xml_memory_block_alignment == 0); + assert(full_size < max_encoded_offset || (page->busy_size == full_size && page_offset == 0)); + header->full_size = static_cast(full_size < max_encoded_offset ? full_size / xml_memory_block_alignment : 0); + + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + // header is guaranteed a pointer-sized alignment, which should be enough for char_t + return static_cast(static_cast(header + 1)); + } + + void deallocate_string(char_t* string) + { + // this function casts pointers through void* to avoid 'cast increases required alignment of target type' warnings + // we're guaranteed the proper (pointer-sized) alignment on the input string if it was allocated via allocate_string + + // get header + xml_memory_string_header* header = static_cast(static_cast(string)) - 1; + assert(header); + + // deallocate + size_t page_offset = sizeof(xml_memory_page) + header->page_offset * xml_memory_block_alignment; + xml_memory_page* page = reinterpret_cast(static_cast(reinterpret_cast(header) - page_offset)); + + // if full_size == 0 then this string occupies the whole page + size_t full_size = header->full_size == 0 ? page->busy_size : header->full_size * xml_memory_block_alignment; + + deallocate_memory(header, full_size, page); + } + + bool reserve() + { + #ifdef PUGIXML_COMPACT + return _hash->reserve(); + #else + return true; + #endif + } + + xml_memory_page* _root; + size_t _busy_size; + + #ifdef PUGIXML_COMPACT + compact_hash_table* _hash; + #endif + }; + + PUGI__FN_NO_INLINE void* xml_allocator::allocate_memory_oob(size_t size, xml_memory_page*& out_page) + { + const size_t large_allocation_threshold = xml_memory_page_size / 4; + + xml_memory_page* page = allocate_page(size <= large_allocation_threshold ? xml_memory_page_size : size); + out_page = page; + + if (!page) return 0; + + if (size <= large_allocation_threshold) + { + _root->busy_size = _busy_size; + + // insert page at the end of linked list + page->prev = _root; + _root->next = page; + _root = page; + + _busy_size = size; + } + else + { + // insert page before the end of linked list, so that it is deleted as soon as possible + // the last page is not deleted even if it's empty (see deallocate_memory) + assert(_root->prev); + + page->prev = _root->prev; + page->next = _root; + + _root->prev->next = page; + _root->prev = page; + + page->busy_size = size; + } + + return reinterpret_cast(page) + sizeof(xml_memory_page); + } +PUGI__NS_END + +#ifdef PUGIXML_COMPACT +PUGI__NS_BEGIN + static const uintptr_t compact_alignment_log2 = 2; + static const uintptr_t compact_alignment = 1 << compact_alignment_log2; + + class compact_header + { + public: + compact_header(xml_memory_page* page, unsigned int flags) + { + PUGI__STATIC_ASSERT(xml_memory_block_alignment == compact_alignment); + + ptrdiff_t offset = (reinterpret_cast(this) - reinterpret_cast(page->compact_page_marker)); + assert(offset % compact_alignment == 0 && static_cast(offset) < 256 * compact_alignment); + + _page = static_cast(offset >> compact_alignment_log2); + _flags = static_cast(flags); + } + + void operator&=(uintptr_t mod) + { + _flags &= static_cast(mod); + } + + void operator|=(uintptr_t mod) + { + _flags |= static_cast(mod); + } + + uintptr_t operator&(uintptr_t mod) const + { + return _flags & mod; + } + + xml_memory_page* get_page() const + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const char* page_marker = reinterpret_cast(this) - (_page << compact_alignment_log2); + const char* page = page_marker - *reinterpret_cast(static_cast(page_marker)); + + return const_cast(reinterpret_cast(static_cast(page))); + } + + private: + unsigned char _page; + unsigned char _flags; + }; + + PUGI__FN xml_memory_page* compact_get_page(const void* object, int header_offset) + { + const compact_header* header = reinterpret_cast(static_cast(object) - header_offset); + + return header->get_page(); + } + + template PUGI__FN_NO_INLINE T* compact_get_value(const void* object) + { + return static_cast(compact_get_page(object, header_offset)->allocator->_hash->find(object)); + } + + template PUGI__FN_NO_INLINE void compact_set_value(const void* object, T* value) + { + compact_get_page(object, header_offset)->allocator->_hash->insert(object, value); + } + + template class compact_pointer + { + public: + compact_pointer(): _data(0) + { + } + + void operator=(const compact_pointer& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift rounding for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) - start; + + if (static_cast(offset) <= 253) + _data = static_cast(offset + 1); + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + _data = 0; + } + + operator T*() const + { + if (_data) + { + if (_data < 255) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + (_data - 1 + start) * compact_alignment); + } + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + unsigned char _data; + }; + + template class compact_pointer_parent + { + public: + compact_pointer_parent(): _data(0) + { + } + + void operator=(const compact_pointer_parent& rhs) + { + *this = rhs + 0; + } + + void operator=(T* value) + { + if (value) + { + // value is guaranteed to be compact-aligned; 'this' is not + // our decoding is based on 'this' aligned to compact alignment downwards (see operator T*) + // so for negative offsets (e.g. -3) we need to adjust the diff by compact_alignment - 1 to + // compensate for arithmetic shift behavior for negative values + ptrdiff_t diff = reinterpret_cast(value) - reinterpret_cast(this); + ptrdiff_t offset = ((diff + int(compact_alignment - 1)) >> compact_alignment_log2) + 65533; + + if (static_cast(offset) <= 65533) + { + _data = static_cast(offset + 1); + } + else + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_shared_parent == 0)) + page->compact_shared_parent = value; + + if (page->compact_shared_parent == value) + { + _data = 65534; + } + else + { + compact_set_value(this, value); + + _data = 65535; + } + } + } + else + { + _data = 0; + } + } + + operator T*() const + { + if (_data) + { + if (_data < 65534) + { + uintptr_t base = reinterpret_cast(this) & ~(compact_alignment - 1); + + return reinterpret_cast(base + (_data - 1 - 65533) * compact_alignment); + } + else if (_data == 65534) + return static_cast(compact_get_page(this, header_offset)->compact_shared_parent); + else + return compact_get_value(this); + } + else + return 0; + } + + T* operator->() const + { + return *this; + } + + private: + uint16_t _data; + }; + + template class compact_string + { + public: + compact_string(): _data(0) + { + } + + void operator=(const compact_string& rhs) + { + *this = rhs + 0; + } + + void operator=(char_t* value) + { + if (value) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + if (PUGI__UNLIKELY(page->compact_string_base == 0)) + page->compact_string_base = value; + + ptrdiff_t offset = value - page->compact_string_base; + + if (static_cast(offset) < (65535 << 7)) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + + if (*base == 0) + { + *base = static_cast((offset >> 7) + 1); + _data = static_cast((offset & 127) + 1); + } + else + { + ptrdiff_t remainder = offset - ((*base - 1) << 7); + + if (static_cast(remainder) <= 253) + { + _data = static_cast(remainder + 1); + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + } + else + { + compact_set_value(this, value); + + _data = 255; + } + } + else + { + _data = 0; + } + } + + operator char_t*() const + { + if (_data) + { + if (_data < 255) + { + xml_memory_page* page = compact_get_page(this, header_offset); + + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + const uint16_t* base = reinterpret_cast(static_cast(reinterpret_cast(this) - base_offset)); + assert(*base); + + ptrdiff_t offset = ((*base - 1) << 7) + (_data - 1); + + return page->compact_string_base + offset; + } + else + { + return compact_get_value(this); + } + } + else + return 0; + } + + private: + unsigned char _data; + }; +PUGI__NS_END +#endif + +#ifdef PUGIXML_COMPACT +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): header(page, 0), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_attribute_struct) == 8); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer prev_attribute_c; + impl::compact_pointer next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): header(page, type), namevalue_base(0) + { + PUGI__STATIC_ASSERT(sizeof(xml_node_struct) == 12); + } + + impl::compact_header header; + + uint16_t namevalue_base; + + impl::compact_string<4, 2> name; + impl::compact_string<5, 3> value; + + impl::compact_pointer_parent parent; + + impl::compact_pointer first_child; + + impl::compact_pointer prev_sibling_c; + impl::compact_pointer next_sibling; + + impl::compact_pointer first_attribute; + }; +} +#else +namespace pugi +{ + struct xml_attribute_struct + { + xml_attribute_struct(impl::xml_memory_page* page): name(0), value(0), prev_attribute_c(0), next_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, 0); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_attribute_struct* prev_attribute_c; + xml_attribute_struct* next_attribute; + }; + + struct xml_node_struct + { + xml_node_struct(impl::xml_memory_page* page, xml_node_type type): name(0), value(0), parent(0), first_child(0), prev_sibling_c(0), next_sibling(0), first_attribute(0) + { + header = PUGI__GETHEADER_IMPL(this, page, type); + } + + uintptr_t header; + + char_t* name; + char_t* value; + + xml_node_struct* parent; + + xml_node_struct* first_child; + + xml_node_struct* prev_sibling_c; + xml_node_struct* next_sibling; + + xml_attribute_struct* first_attribute; + }; +} +#endif + +PUGI__NS_BEGIN + struct xml_extra_buffer + { + char_t* buffer; + xml_extra_buffer* next; + }; + + struct xml_document_struct: public xml_node_struct, public xml_allocator + { + xml_document_struct(xml_memory_page* page): xml_node_struct(page, node_document), xml_allocator(page), buffer(0), extra_buffers(0) + { + } + + const char_t* buffer; + + xml_extra_buffer* extra_buffers; + + #ifdef PUGIXML_COMPACT + compact_hash_table hash; + #endif + }; + + template inline xml_allocator& get_allocator(const Object* object) + { + assert(object); + + return *PUGI__GETPAGE(object)->allocator; + } + + template inline xml_document_struct& get_document(const Object* object) + { + assert(object); + + return *static_cast(PUGI__GETPAGE(object)->allocator); + } +PUGI__NS_END + +// Low-level DOM operations +PUGI__NS_BEGIN + inline xml_attribute_struct* allocate_attribute(xml_allocator& alloc) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_attribute_struct), page); + if (!memory) return 0; + + return new (memory) xml_attribute_struct(page); + } + + inline xml_node_struct* allocate_node(xml_allocator& alloc, xml_node_type type) + { + xml_memory_page* page; + void* memory = alloc.allocate_object(sizeof(xml_node_struct), page); + if (!memory) return 0; + + return new (memory) xml_node_struct(page, type); + } + + inline void destroy_attribute(xml_attribute_struct* a, xml_allocator& alloc) + { + if (a->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(a->name); + + if (a->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(a->value); + + alloc.deallocate_memory(a, sizeof(xml_attribute_struct), PUGI__GETPAGE(a)); + } + + inline void destroy_node(xml_node_struct* n, xml_allocator& alloc) + { + if (n->header & impl::xml_memory_page_name_allocated_mask) + alloc.deallocate_string(n->name); + + if (n->header & impl::xml_memory_page_value_allocated_mask) + alloc.deallocate_string(n->value); + + for (xml_attribute_struct* attr = n->first_attribute; attr; ) + { + xml_attribute_struct* next = attr->next_attribute; + + destroy_attribute(attr, alloc); + + attr = next; + } + + for (xml_node_struct* child = n->first_child; child; ) + { + xml_node_struct* next = child->next_sibling; + + destroy_node(child, alloc); + + child = next; + } + + alloc.deallocate_memory(n, sizeof(xml_node_struct), PUGI__GETPAGE(n)); + } + + inline void append_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + xml_node_struct* tail = head->prev_sibling_c; + + tail->next_sibling = child; + child->prev_sibling_c = tail; + head->prev_sibling_c = child; + } + else + { + node->first_child = child; + child->prev_sibling_c = child; + } + } + + inline void prepend_node(xml_node_struct* child, xml_node_struct* node) + { + child->parent = node; + + xml_node_struct* head = node->first_child; + + if (head) + { + child->prev_sibling_c = head->prev_sibling_c; + head->prev_sibling_c = child; + } + else + child->prev_sibling_c = child; + + child->next_sibling = head; + node->first_child = child; + } + + inline void insert_node_after(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = child; + else + parent->first_child->prev_sibling_c = child; + + child->next_sibling = node->next_sibling; + child->prev_sibling_c = node; + + node->next_sibling = child; + } + + inline void insert_node_before(xml_node_struct* child, xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + child->parent = parent; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = child; + else + parent->first_child = child; + + child->prev_sibling_c = node->prev_sibling_c; + child->next_sibling = node; + + node->prev_sibling_c = child; + } + + inline void remove_node(xml_node_struct* node) + { + xml_node_struct* parent = node->parent; + + if (node->next_sibling) + node->next_sibling->prev_sibling_c = node->prev_sibling_c; + else + parent->first_child->prev_sibling_c = node->prev_sibling_c; + + if (node->prev_sibling_c->next_sibling) + node->prev_sibling_c->next_sibling = node->next_sibling; + else + parent->first_child = node->next_sibling; + + node->parent = 0; + node->prev_sibling_c = 0; + node->next_sibling = 0; + } + + inline void append_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + xml_attribute_struct* tail = head->prev_attribute_c; + + tail->next_attribute = attr; + attr->prev_attribute_c = tail; + head->prev_attribute_c = attr; + } + else + { + node->first_attribute = attr; + attr->prev_attribute_c = attr; + } + } + + inline void prepend_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + xml_attribute_struct* head = node->first_attribute; + + if (head) + { + attr->prev_attribute_c = head->prev_attribute_c; + head->prev_attribute_c = attr; + } + else + attr->prev_attribute_c = attr; + + attr->next_attribute = head; + node->first_attribute = attr; + } + + inline void insert_attribute_after(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->next_attribute) + place->next_attribute->prev_attribute_c = attr; + else + node->first_attribute->prev_attribute_c = attr; + + attr->next_attribute = place->next_attribute; + attr->prev_attribute_c = place; + place->next_attribute = attr; + } + + inline void insert_attribute_before(xml_attribute_struct* attr, xml_attribute_struct* place, xml_node_struct* node) + { + if (place->prev_attribute_c->next_attribute) + place->prev_attribute_c->next_attribute = attr; + else + node->first_attribute = attr; + + attr->prev_attribute_c = place->prev_attribute_c; + attr->next_attribute = place; + place->prev_attribute_c = attr; + } + + inline void remove_attribute(xml_attribute_struct* attr, xml_node_struct* node) + { + if (attr->next_attribute) + attr->next_attribute->prev_attribute_c = attr->prev_attribute_c; + else + node->first_attribute->prev_attribute_c = attr->prev_attribute_c; + + if (attr->prev_attribute_c->next_attribute) + attr->prev_attribute_c->next_attribute = attr->next_attribute; + else + node->first_attribute = attr->next_attribute; + + attr->prev_attribute_c = 0; + attr->next_attribute = 0; + } + + PUGI__FN_NO_INLINE xml_node_struct* append_new_node(xml_node_struct* node, xml_allocator& alloc, xml_node_type type = node_element) + { + if (!alloc.reserve()) return 0; + + xml_node_struct* child = allocate_node(alloc, type); + if (!child) return 0; + + append_node(child, node); + + return child; + } + + PUGI__FN_NO_INLINE xml_attribute_struct* append_new_attribute(xml_node_struct* node, xml_allocator& alloc) + { + if (!alloc.reserve()) return 0; + + xml_attribute_struct* attr = allocate_attribute(alloc); + if (!attr) return 0; + + append_attribute(attr, node); + + return attr; + } +PUGI__NS_END + +// Helper classes for code generation +PUGI__NS_BEGIN + struct opt_false + { + enum { value = 0 }; + }; + + struct opt_true + { + enum { value = 1 }; + }; +PUGI__NS_END + +// Unicode utilities +PUGI__NS_BEGIN + inline uint16_t endian_swap(uint16_t value) + { + return static_cast(((value & 0xff) << 8) | (value >> 8)); + } + + inline uint32_t endian_swap(uint32_t value) + { + return ((value & 0xff) << 24) | ((value & 0xff00) << 8) | ((value & 0xff0000) >> 8) | (value >> 24); + } + + struct utf8_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) return result + 1; + // U+0080..U+07FF + else if (ch < 0x800) return result + 2; + // U+0800..U+FFFF + else return result + 3; + } + + static value_type high(value_type result, uint32_t) + { + // U+10000..U+10FFFF + return result + 4; + } + }; + + struct utf8_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + // U+0000..U+007F + if (ch < 0x80) + { + *result = static_cast(ch); + return result + 1; + } + // U+0080..U+07FF + else if (ch < 0x800) + { + result[0] = static_cast(0xC0 | (ch >> 6)); + result[1] = static_cast(0x80 | (ch & 0x3F)); + return result + 2; + } + // U+0800..U+FFFF + else + { + result[0] = static_cast(0xE0 | (ch >> 12)); + result[1] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[2] = static_cast(0x80 | (ch & 0x3F)); + return result + 3; + } + } + + static value_type high(value_type result, uint32_t ch) + { + // U+10000..U+10FFFF + result[0] = static_cast(0xF0 | (ch >> 18)); + result[1] = static_cast(0x80 | ((ch >> 12) & 0x3F)); + result[2] = static_cast(0x80 | ((ch >> 6) & 0x3F)); + result[3] = static_cast(0x80 | (ch & 0x3F)); + return result + 4; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf16_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 2; + } + }; + + struct utf16_writer + { + typedef uint16_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + uint32_t msh = static_cast(ch - 0x10000) >> 10; + uint32_t lsh = static_cast(ch - 0x10000) & 0x3ff; + + result[0] = static_cast(0xD800 + msh); + result[1] = static_cast(0xDC00 + lsh); + + return result + 2; + } + + static value_type any(value_type result, uint32_t ch) + { + return (ch < 0x10000) ? low(result, ch) : high(result, ch); + } + }; + + struct utf32_counter + { + typedef size_t value_type; + + static value_type low(value_type result, uint32_t) + { + return result + 1; + } + + static value_type high(value_type result, uint32_t) + { + return result + 1; + } + }; + + struct utf32_writer + { + typedef uint32_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + + static value_type any(value_type result, uint32_t ch) + { + *result = ch; + + return result + 1; + } + }; + + struct latin1_writer + { + typedef uint8_t* value_type; + + static value_type low(value_type result, uint32_t ch) + { + *result = static_cast(ch > 255 ? '?' : ch); + + return result + 1; + } + + static value_type high(value_type result, uint32_t ch) + { + (void)ch; + + *result = '?'; + + return result + 1; + } + }; + + struct utf8_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + const uint8_t utf8_byte_mask = 0x3f; + + while (size) + { + uint8_t lead = *data; + + // 0xxxxxxx -> U+0000..U+007F + if (lead < 0x80) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + + // process aligned single-byte (ascii) blocks + if ((reinterpret_cast(data) & 3) == 0) + { + // round-trip through void* to silence 'cast increases required alignment of target type' warnings + while (size >= 4 && (*static_cast(static_cast(data)) & 0x80808080) == 0) + { + result = Traits::low(result, data[0]); + result = Traits::low(result, data[1]); + result = Traits::low(result, data[2]); + result = Traits::low(result, data[3]); + data += 4; + size -= 4; + } + } + } + // 110xxxxx -> U+0080..U+07FF + else if (static_cast(lead - 0xC0) < 0x20 && size >= 2 && (data[1] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xC0) << 6) | (data[1] & utf8_byte_mask)); + data += 2; + size -= 2; + } + // 1110xxxx -> U+0800-U+FFFF + else if (static_cast(lead - 0xE0) < 0x10 && size >= 3 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80) + { + result = Traits::low(result, ((lead & ~0xE0) << 12) | ((data[1] & utf8_byte_mask) << 6) | (data[2] & utf8_byte_mask)); + data += 3; + size -= 3; + } + // 11110xxx -> U+10000..U+10FFFF + else if (static_cast(lead - 0xF0) < 0x08 && size >= 4 && (data[1] & 0xc0) == 0x80 && (data[2] & 0xc0) == 0x80 && (data[3] & 0xc0) == 0x80) + { + result = Traits::high(result, ((lead & ~0xF0) << 18) | ((data[1] & utf8_byte_mask) << 12) | ((data[2] & utf8_byte_mask) << 6) | (data[3] & utf8_byte_mask)); + data += 4; + size -= 4; + } + // 10xxxxxx or 11111xxx -> invalid + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf16_decoder + { + typedef uint16_t type; + + template static inline typename Traits::value_type process(const uint16_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint16_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+D7FF + if (lead < 0xD800) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+E000..U+FFFF + else if (static_cast(lead - 0xE000) < 0x2000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // surrogate pair lead + else if (static_cast(lead - 0xD800) < 0x400 && size >= 2) + { + uint16_t next = opt_swap::value ? endian_swap(data[1]) : data[1]; + + if (static_cast(next - 0xDC00) < 0x400) + { + result = Traits::high(result, 0x10000 + ((lead & 0x3ff) << 10) + (next & 0x3ff)); + data += 2; + size -= 2; + } + else + { + data += 1; + size -= 1; + } + } + else + { + data += 1; + size -= 1; + } + } + + return result; + } + }; + + template struct utf32_decoder + { + typedef uint32_t type; + + template static inline typename Traits::value_type process(const uint32_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + uint32_t lead = opt_swap::value ? endian_swap(*data) : *data; + + // U+0000..U+FFFF + if (lead < 0x10000) + { + result = Traits::low(result, lead); + data += 1; + size -= 1; + } + // U+10000..U+10FFFF + else + { + result = Traits::high(result, lead); + data += 1; + size -= 1; + } + } + + return result; + } + }; + + struct latin1_decoder + { + typedef uint8_t type; + + template static inline typename Traits::value_type process(const uint8_t* data, size_t size, typename Traits::value_type result, Traits) + { + while (size) + { + result = Traits::low(result, *data); + data += 1; + size -= 1; + } + + return result; + } + }; + + template struct wchar_selector; + + template <> struct wchar_selector<2> + { + typedef uint16_t type; + typedef utf16_counter counter; + typedef utf16_writer writer; + typedef utf16_decoder decoder; + }; + + template <> struct wchar_selector<4> + { + typedef uint32_t type; + typedef utf32_counter counter; + typedef utf32_writer writer; + typedef utf32_decoder decoder; + }; + + typedef wchar_selector::counter wchar_counter; + typedef wchar_selector::writer wchar_writer; + + struct wchar_decoder + { + typedef wchar_t type; + + template static inline typename Traits::value_type process(const wchar_t* data, size_t size, typename Traits::value_type result, Traits traits) + { + typedef wchar_selector::decoder decoder; + + return decoder::process(reinterpret_cast(data), size, result, traits); + } + }; + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN void convert_wchar_endian_swap(wchar_t* result, const wchar_t* data, size_t length) + { + for (size_t i = 0; i < length; ++i) + result[i] = static_cast(endian_swap(static_cast::type>(data[i]))); + } +#endif +PUGI__NS_END + +PUGI__NS_BEGIN + enum chartype_t + { + ct_parse_pcdata = 1, // \0, &, \r, < + ct_parse_attr = 2, // \0, &, \r, ', " + ct_parse_attr_ws = 4, // \0, &, \r, ', ", \n, tab + ct_space = 8, // \r, \n, space, tab + ct_parse_cdata = 16, // \0, ], >, \r + ct_parse_comment = 32, // \0, -, >, \r + ct_symbol = 64, // Any symbol > 127, a-z, A-Z, 0-9, _, :, -, . + ct_start_symbol = 128 // Any symbol > 127, a-z, A-Z, _, : + }; + + static const unsigned char chartype_table[256] = + { + 55, 0, 0, 0, 0, 0, 0, 0, 0, 12, 12, 0, 0, 63, 0, 0, // 0-15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16-31 + 8, 0, 6, 0, 0, 0, 7, 6, 0, 0, 0, 0, 0, 96, 64, 0, // 32-47 + 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 192, 0, 1, 0, 48, 0, // 48-63 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 64-79 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 16, 0, 192, // 80-95 + 0, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 96-111 + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 0, 0, 0, 0, 0, // 112-127 + + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, // 128+ + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, + 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192 + }; + + enum chartypex_t + { + ctx_special_pcdata = 1, // Any symbol >= 0 and < 32 (except \t, \r, \n), &, <, > + ctx_special_attr = 2, // Any symbol >= 0 and < 32 (except \t), &, <, >, " + ctx_start_symbol = 4, // Any symbol > 127, a-z, A-Z, _ + ctx_digit = 8, // 0-9 + ctx_symbol = 16 // Any symbol > 127, a-z, A-Z, 0-9, _, -, . + }; + + static const unsigned char chartypex_table[256] = + { + 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 2, 3, 3, 2, 3, 3, // 0-15 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 16-31 + 0, 0, 2, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 16, 16, 0, // 32-47 + 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 0, 0, 3, 0, 3, 0, // 48-63 + + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 64-79 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 20, // 80-95 + 0, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 96-111 + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 0, 0, 0, 0, 0, // 112-127 + + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // 128+ + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, + 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 + }; + +#ifdef PUGIXML_WCHAR_MODE + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) ((static_cast(c) < 128 ? table[static_cast(c)] : table[128]) & (ct)) +#else + #define PUGI__IS_CHARTYPE_IMPL(c, ct, table) (table[static_cast(c)] & (ct)) +#endif + + #define PUGI__IS_CHARTYPE(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartype_table) + #define PUGI__IS_CHARTYPEX(c, ct) PUGI__IS_CHARTYPE_IMPL(c, ct, chartypex_table) + + PUGI__FN bool is_little_endian() + { + unsigned int ui = 1; + + return *reinterpret_cast(&ui) == 1; + } + + PUGI__FN xml_encoding get_wchar_encoding() + { + PUGI__STATIC_ASSERT(sizeof(wchar_t) == 2 || sizeof(wchar_t) == 4); + + if (sizeof(wchar_t) == 2) + return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + else + return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + } + + PUGI__FN bool parse_declaration_encoding(const uint8_t* data, size_t size, const uint8_t*& out_encoding, size_t& out_length) + { + #define PUGI__SCANCHAR(ch) { if (offset >= size || data[offset] != ch) return false; offset++; } + #define PUGI__SCANCHARTYPE(ct) { while (offset < size && PUGI__IS_CHARTYPE(data[offset], ct)) offset++; } + + // check if we have a non-empty XML declaration + if (size < 6 || !((data[0] == '<') & (data[1] == '?') & (data[2] == 'x') & (data[3] == 'm') & (data[4] == 'l') && PUGI__IS_CHARTYPE(data[5], ct_space))) + return false; + + // scan XML declaration until the encoding field + for (size_t i = 6; i + 1 < size; ++i) + { + // declaration can not contain ? in quoted values + if (data[i] == '?') + return false; + + if (data[i] == 'e' && data[i + 1] == 'n') + { + size_t offset = i; + + // encoding follows the version field which can't contain 'en' so this has to be the encoding if XML is well formed + PUGI__SCANCHAR('e'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('c'); PUGI__SCANCHAR('o'); + PUGI__SCANCHAR('d'); PUGI__SCANCHAR('i'); PUGI__SCANCHAR('n'); PUGI__SCANCHAR('g'); + + // S? = S? + PUGI__SCANCHARTYPE(ct_space); + PUGI__SCANCHAR('='); + PUGI__SCANCHARTYPE(ct_space); + + // the only two valid delimiters are ' and " + uint8_t delimiter = (offset < size && data[offset] == '"') ? '"' : '\''; + + PUGI__SCANCHAR(delimiter); + + size_t start = offset; + + out_encoding = data + offset; + + PUGI__SCANCHARTYPE(ct_symbol); + + out_length = offset - start; + + PUGI__SCANCHAR(delimiter); + + return true; + } + } + + return false; + + #undef PUGI__SCANCHAR + #undef PUGI__SCANCHARTYPE + } + + PUGI__FN xml_encoding guess_buffer_encoding(const uint8_t* data, size_t size) + { + // skip encoding autodetection if input buffer is too small + if (size < 4) return encoding_utf8; + + uint8_t d0 = data[0], d1 = data[1], d2 = data[2], d3 = data[3]; + + // look for BOM in first few bytes + if (d0 == 0 && d1 == 0 && d2 == 0xfe && d3 == 0xff) return encoding_utf32_be; + if (d0 == 0xff && d1 == 0xfe && d2 == 0 && d3 == 0) return encoding_utf32_le; + if (d0 == 0xfe && d1 == 0xff) return encoding_utf16_be; + if (d0 == 0xff && d1 == 0xfe) return encoding_utf16_le; + if (d0 == 0xef && d1 == 0xbb && d2 == 0xbf) return encoding_utf8; + + // look for <, (contents); + + return guess_buffer_encoding(data, size); + } + + PUGI__FN bool get_mutable_buffer(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + out_buffer = static_cast(const_cast(contents)); + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + if (contents) + memcpy(buffer, contents, length * sizeof(char_t)); + else + assert(length == 0); + + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN bool need_endian_swap_utf(xml_encoding le, xml_encoding re) + { + return (le == encoding_utf16_be && re == encoding_utf16_le) || (le == encoding_utf16_le && re == encoding_utf16_be) || + (le == encoding_utf32_be && re == encoding_utf32_le) || (le == encoding_utf32_le && re == encoding_utf32_be); + } + + PUGI__FN bool convert_buffer_endian_swap(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const char_t* data = static_cast(contents); + size_t length = size / sizeof(char_t); + + if (is_mutable) + { + char_t* buffer = const_cast(data); + + convert_wchar_endian_swap(buffer, data, length); + + out_buffer = buffer; + out_length = length; + } + else + { + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + convert_wchar_endian_swap(buffer, data, length); + buffer[length] = 0; + + out_buffer = buffer; + out_length = length + 1; + } + + return true; + } + + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in wchar_t units + size_t length = D::process(data, data_length, 0, wchar_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to wchar_t + wchar_writer::value_type obegin = reinterpret_cast(buffer); + wchar_writer::value_type oend = D::process(data, data_length, obegin, wchar_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // get native encoding + xml_encoding wchar_encoding = get_wchar_encoding(); + + // fast path: no conversion required + if (encoding == wchar_encoding) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // only endian-swapping is required + if (need_endian_swap_utf(encoding, wchar_encoding)) + return convert_buffer_endian_swap(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf8 + if (encoding == encoding_utf8) + return convert_buffer_generic(out_buffer, out_length, contents, size, utf8_decoder()); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_generic(out_buffer, out_length, contents, size, latin1_decoder()); + + assert(false && "Invalid encoding"); // unreachable + return false; + } +#else + template PUGI__FN bool convert_buffer_generic(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, D) + { + const typename D::type* data = static_cast(contents); + size_t data_length = size / sizeof(typename D::type); + + // first pass: get length in utf8 units + size_t length = D::process(data, data_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert utf16 input to utf8 + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = D::process(data, data_length, obegin, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN size_t get_latin1_7bit_prefix_length(const uint8_t* data, size_t size) + { + for (size_t i = 0; i < size; ++i) + if (data[i] > 127) + return i; + + return size; + } + + PUGI__FN bool convert_buffer_latin1(char_t*& out_buffer, size_t& out_length, const void* contents, size_t size, bool is_mutable) + { + const uint8_t* data = static_cast(contents); + size_t data_length = size; + + // get size of prefix that does not need utf8 conversion + size_t prefix_length = get_latin1_7bit_prefix_length(data, data_length); + assert(prefix_length <= data_length); + + const uint8_t* postfix = data + prefix_length; + size_t postfix_length = data_length - prefix_length; + + // if no conversion is needed, just return the original buffer + if (postfix_length == 0) return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // first pass: get length in utf8 units + size_t length = prefix_length + latin1_decoder::process(postfix, postfix_length, 0, utf8_counter()); + + // allocate buffer of suitable length + char_t* buffer = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!buffer) return false; + + // second pass: convert latin1 input to utf8 + memcpy(buffer, data, prefix_length); + + uint8_t* obegin = reinterpret_cast(buffer); + uint8_t* oend = latin1_decoder::process(postfix, postfix_length, obegin + prefix_length, utf8_writer()); + + assert(oend == obegin + length); + *oend = 0; + + out_buffer = buffer; + out_length = length + 1; + + return true; + } + + PUGI__FN bool convert_buffer(char_t*& out_buffer, size_t& out_length, xml_encoding encoding, const void* contents, size_t size, bool is_mutable) + { + // fast path: no conversion required + if (encoding == encoding_utf8) + return get_mutable_buffer(out_buffer, out_length, contents, size, is_mutable); + + // source encoding is utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf16_decoder()); + } + + // source encoding is utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return (native_encoding == encoding) ? + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()) : + convert_buffer_generic(out_buffer, out_length, contents, size, utf32_decoder()); + } + + // source encoding is latin1 + if (encoding == encoding_latin1) + return convert_buffer_latin1(out_buffer, out_length, contents, size, is_mutable); + + assert(false && "Invalid encoding"); // unreachable + return false; + } +#endif + + PUGI__FN size_t as_utf8_begin(const wchar_t* str, size_t length) + { + // get length in utf8 characters + return wchar_decoder::process(str, length, 0, utf8_counter()); + } + + PUGI__FN void as_utf8_end(char* buffer, size_t size, const wchar_t* str, size_t length) + { + // convert to utf8 + uint8_t* begin = reinterpret_cast(buffer); + uint8_t* end = wchar_decoder::process(str, length, begin, utf8_writer()); + + assert(begin + size == end); + (void)!end; + (void)!size; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string as_utf8_impl(const wchar_t* str, size_t length) + { + // first pass: get length in utf8 characters + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + std::string result; + result.resize(size); + + // second pass: convert to utf8 + if (size > 0) as_utf8_end(&result[0], size, str, length); + + return result; + } + + PUGI__FN std::basic_string as_wide_impl(const char* str, size_t size) + { + const uint8_t* data = reinterpret_cast(str); + + // first pass: get length in wchar_t units + size_t length = utf8_decoder::process(data, size, 0, wchar_counter()); + + // allocate resulting string + std::basic_string result; + result.resize(length); + + // second pass: convert to wchar_t + if (length > 0) + { + wchar_writer::value_type begin = reinterpret_cast(&result[0]); + wchar_writer::value_type end = utf8_decoder::process(data, size, begin, wchar_writer()); + + assert(begin + length == end); + (void)!end; + } + + return result; + } +#endif + + template + inline bool strcpy_insitu_allow(size_t length, const Header& header, uintptr_t header_mask, char_t* target) + { + // never reuse shared memory + if (header & xml_memory_page_contents_shared_mask) return false; + + size_t target_length = strlength(target); + + // always reuse document buffer memory if possible + if ((header & header_mask) == 0) return target_length >= length; + + // reuse heap memory if waste is not too great + const size_t reuse_threshold = 32; + + return target_length >= length && (target_length < reuse_threshold || target_length - length < target_length / 2); + } + + template + PUGI__FN bool strcpy_insitu(String& dest, Header& header, uintptr_t header_mask, const char_t* source, size_t source_length) + { + if (source_length == 0) + { + // empty string and null pointer are equivalent, so just deallocate old memory + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (header & header_mask) alloc->deallocate_string(dest); + + // mark the string as not allocated + dest = 0; + header &= ~header_mask; + + return true; + } + else if (dest && strcpy_insitu_allow(source_length, header, header_mask, dest)) + { + // we can reuse old buffer, so just copy the new data (including zero terminator) + memcpy(dest, source, source_length * sizeof(char_t)); + dest[source_length] = 0; + + return true; + } + else + { + xml_allocator* alloc = PUGI__GETPAGE_IMPL(header)->allocator; + + if (!alloc->reserve()) return false; + + // allocate new buffer + char_t* buf = alloc->allocate_string(source_length + 1); + if (!buf) return false; + + // copy the string (including zero terminator) + memcpy(buf, source, source_length * sizeof(char_t)); + buf[source_length] = 0; + + // deallocate old buffer (*after* the above to protect against overlapping memory and/or allocation failures) + if (header & header_mask) alloc->deallocate_string(dest); + + // the string is now allocated, so set the flag + dest = buf; + header |= header_mask; + + return true; + } + } + + struct gap + { + char_t* end; + size_t size; + + gap(): end(0), size(0) + { + } + + // Push new gap, move s count bytes further (skipping the gap). + // Collapse previous gap. + void push(char_t*& s, size_t count) + { + if (end) // there was a gap already; collapse it + { + // Move [old_gap_end, new_gap_start) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + } + + s += count; // end of current gap + + // "merge" two gaps + end = s; + size += count; + } + + // Collapse all gaps, return past-the-end pointer + char_t* flush(char_t* s) + { + if (end) + { + // Move [old_gap_end, current_pos) to [old_gap_start, ...) + assert(s >= end); + memmove(end - size, end, reinterpret_cast(s) - reinterpret_cast(end)); + + return s - size; + } + else return s; + } + }; + + PUGI__FN char_t* strconv_escape(char_t* s, gap& g) + { + char_t* stre = s + 1; + + switch (*stre) + { + case '#': // &#... + { + unsigned int ucsc = 0; + + if (stre[1] == 'x') // &#x... (hex code) + { + stre += 2; + + char_t ch = *stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(ch - '0') <= 9) + ucsc = 16 * ucsc + (ch - '0'); + else if (static_cast((ch | ' ') - 'a') <= 5) + ucsc = 16 * ucsc + ((ch | ' ') - 'a' + 10); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + else // &#... (dec code) + { + char_t ch = *++stre; + + if (ch == ';') return stre; + + for (;;) + { + if (static_cast(ch - '0') <= 9) + ucsc = 10 * ucsc + (ch - '0'); + else if (ch == ';') + break; + else // cancel + return stre; + + ch = *++stre; + } + + ++stre; + } + + #ifdef PUGIXML_WCHAR_MODE + s = reinterpret_cast(wchar_writer::any(reinterpret_cast(s), ucsc)); + #else + s = reinterpret_cast(utf8_writer::any(reinterpret_cast(s), ucsc)); + #endif + + g.push(s, stre - s); + return stre; + } + + case 'a': // &a + { + ++stre; + + if (*stre == 'm') // &am + { + if (*++stre == 'p' && *++stre == ';') // & + { + *s++ = '&'; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + else if (*stre == 'p') // &ap + { + if (*++stre == 'o' && *++stre == 's' && *++stre == ';') // ' + { + *s++ = '\''; + ++stre; + + g.push(s, stre - s); + return stre; + } + } + break; + } + + case 'g': // &g + { + if (*++stre == 't' && *++stre == ';') // > + { + *s++ = '>'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'l': // &l + { + if (*++stre == 't' && *++stre == ';') // < + { + *s++ = '<'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + case 'q': // &q + { + if (*++stre == 'u' && *++stre == 'o' && *++stre == 't' && *++stre == ';') // " + { + *s++ = '"'; + ++stre; + + g.push(s, stre - s); + return stre; + } + break; + } + + default: + break; + } + + return stre; + } + + // Parser utilities + #define PUGI__ENDSWITH(c, e) ((c) == (e) || ((c) == 0 && endch == (e))) + #define PUGI__SKIPWS() { while (PUGI__IS_CHARTYPE(*s, ct_space)) ++s; } + #define PUGI__OPTSET(OPT) ( optmsk & (OPT) ) + #define PUGI__PUSHNODE(TYPE) { cursor = append_new_node(cursor, *alloc, TYPE); if (!cursor) PUGI__THROW_ERROR(status_out_of_memory, s); } + #define PUGI__POPNODE() { cursor = cursor->parent; } + #define PUGI__SCANFOR(X) { while (*s != 0 && !(X)) ++s; } + #define PUGI__SCANWHILE(X) { while (X) ++s; } + #define PUGI__SCANWHILE_UNROLL(X) { for (;;) { char_t ss = s[0]; if (PUGI__UNLIKELY(!(X))) { break; } ss = s[1]; if (PUGI__UNLIKELY(!(X))) { s += 1; break; } ss = s[2]; if (PUGI__UNLIKELY(!(X))) { s += 2; break; } ss = s[3]; if (PUGI__UNLIKELY(!(X))) { s += 3; break; } s += 4; } } + #define PUGI__ENDSEG() { ch = *s; *s = 0; ++s; } + #define PUGI__THROW_ERROR(err, m) return error_offset = m, error_status = err, static_cast(0) + #define PUGI__CHECK_ERROR(err, m) { if (*s == 0) PUGI__THROW_ERROR(err, m); } + + PUGI__FN char_t* strconv_comment(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_comment)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')) // comment ends here + { + *g.flush(s) = 0; + + return s + (s[2] == '>' ? 3 : 2); + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + PUGI__FN char_t* strconv_cdata(char_t* s, char_t endch) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_cdata)); + + if (*s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')) // CDATA ends here + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == 0) + { + return 0; + } + else ++s; + } + } + + typedef char_t* (*strconv_pcdata_t)(char_t*); + + template struct strconv_pcdata_impl + { + static char_t* parse(char_t* s) + { + gap g; + + char_t* begin = s; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_pcdata)); + + if (*s == '<') // PCDATA ends here + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s + 1; + } + else if (opt_eol::value && *s == '\r') // Either a single 0x0d or 0x0d 0x0a pair + { + *s++ = '\n'; // replace first one with 0x0a + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (*s == 0) + { + char_t* end = g.flush(s); + + if (opt_trim::value) + while (end > begin && PUGI__IS_CHARTYPE(end[-1], ct_space)) + --end; + + *end = 0; + + return s; + } + else ++s; + } + } + }; + + PUGI__FN strconv_pcdata_t get_strconv_pcdata(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_trim_pcdata == 0x0800); + + switch (((optmask >> 4) & 3) | ((optmask >> 9) & 4)) // get bitmask for flags (eol escapes trim) + { + case 0: return strconv_pcdata_impl::parse; + case 1: return strconv_pcdata_impl::parse; + case 2: return strconv_pcdata_impl::parse; + case 3: return strconv_pcdata_impl::parse; + case 4: return strconv_pcdata_impl::parse; + case 5: return strconv_pcdata_impl::parse; + case 6: return strconv_pcdata_impl::parse; + case 7: return strconv_pcdata_impl::parse; + default: assert(false); return 0; // unreachable + } + } + + typedef char_t* (*strconv_attribute_t)(char_t*, char_t); + + template struct strconv_attribute_impl + { + static char_t* parse_wnorm(char_t* s, char_t end_quote) + { + gap g; + + // trim leading whitespaces + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s; + + do ++str; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + g.push(s, str - s); + } + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws | ct_space)); + + if (*s == end_quote) + { + char_t* str = g.flush(s); + + do *str-- = 0; + while (PUGI__IS_CHARTYPE(*str, ct_space)); + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + *s++ = ' '; + + if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + char_t* str = s + 1; + while (PUGI__IS_CHARTYPE(*str, ct_space)) ++str; + + g.push(s, str - s); + } + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_wconv(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr_ws)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (PUGI__IS_CHARTYPE(*s, ct_space)) + { + if (*s == '\r') + { + *s++ = ' '; + + if (*s == '\n') g.push(s, 1); + } + else *s++ = ' '; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_eol(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (*s == '\r') + { + *s++ = '\n'; + + if (*s == '\n') g.push(s, 1); + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + + static char_t* parse_simple(char_t* s, char_t end_quote) + { + gap g; + + while (true) + { + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPE(ss, ct_parse_attr)); + + if (*s == end_quote) + { + *g.flush(s) = 0; + + return s + 1; + } + else if (opt_escape::value && *s == '&') + { + s = strconv_escape(s, g); + } + else if (!*s) + { + return 0; + } + else ++s; + } + } + }; + + PUGI__FN strconv_attribute_t get_strconv_attribute(unsigned int optmask) + { + PUGI__STATIC_ASSERT(parse_escapes == 0x10 && parse_eol == 0x20 && parse_wconv_attribute == 0x40 && parse_wnorm_attribute == 0x80); + + switch ((optmask >> 4) & 15) // get bitmask for flags (wconv wnorm eol escapes) + { + case 0: return strconv_attribute_impl::parse_simple; + case 1: return strconv_attribute_impl::parse_simple; + case 2: return strconv_attribute_impl::parse_eol; + case 3: return strconv_attribute_impl::parse_eol; + case 4: return strconv_attribute_impl::parse_wconv; + case 5: return strconv_attribute_impl::parse_wconv; + case 6: return strconv_attribute_impl::parse_wconv; + case 7: return strconv_attribute_impl::parse_wconv; + case 8: return strconv_attribute_impl::parse_wnorm; + case 9: return strconv_attribute_impl::parse_wnorm; + case 10: return strconv_attribute_impl::parse_wnorm; + case 11: return strconv_attribute_impl::parse_wnorm; + case 12: return strconv_attribute_impl::parse_wnorm; + case 13: return strconv_attribute_impl::parse_wnorm; + case 14: return strconv_attribute_impl::parse_wnorm; + case 15: return strconv_attribute_impl::parse_wnorm; + default: assert(false); return 0; // unreachable + } + } + + inline xml_parse_result make_parse_result(xml_parse_status status, ptrdiff_t offset = 0) + { + xml_parse_result result; + result.status = status; + result.offset = offset; + + return result; + } + + struct xml_parser + { + xml_allocator* alloc; + char_t* error_offset; + xml_parse_status error_status; + + xml_parser(xml_allocator* alloc_): alloc(alloc_), error_offset(0), error_status(status_ok) + { + } + + // DOCTYPE consists of nested sections of the following possible types: + // , , "...", '...' + // + // + // First group can not contain nested groups + // Second group can contain nested groups of the same type + // Third group can contain all other groups + char_t* parse_doctype_primitive(char_t* s) + { + if (*s == '"' || *s == '\'') + { + // quoted string + char_t ch = *s++; + PUGI__SCANFOR(*s == ch); + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s++; + } + else if (s[0] == '<' && s[1] == '?') + { + // + s += 2; + PUGI__SCANFOR(s[0] == '?' && s[1] == '>'); // no need for ENDSWITH because ?> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 2; + } + else if (s[0] == '<' && s[1] == '!' && s[2] == '-' && s[3] == '-') + { + s += 4; + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && s[2] == '>'); // no need for ENDSWITH because --> can't terminate proper doctype + if (!*s) PUGI__THROW_ERROR(status_bad_doctype, s); + + s += 3; + } + else PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_doctype_ignore(char_t* s) + { + size_t depth = 0; + + assert(s[0] == '<' && s[1] == '!' && s[2] == '['); + s += 3; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] == '[') + { + // nested ignore section + s += 3; + depth++; + } + else if (s[0] == ']' && s[1] == ']' && s[2] == '>') + { + // ignore section end + s += 3; + + if (depth == 0) + return s; + + depth--; + } + else s++; + } + + PUGI__THROW_ERROR(status_bad_doctype, s); + } + + char_t* parse_doctype_group(char_t* s, char_t endch) + { + size_t depth = 0; + + assert((s[0] == '<' || s[0] == 0) && s[1] == '!'); + s += 2; + + while (*s) + { + if (s[0] == '<' && s[1] == '!' && s[2] != '-') + { + if (s[2] == '[') + { + // ignore + s = parse_doctype_ignore(s); + if (!s) return s; + } + else + { + // some control group + s += 2; + depth++; + } + } + else if (s[0] == '<' || s[0] == '"' || s[0] == '\'') + { + // unknown tag (forbidden), or some primitive group + s = parse_doctype_primitive(s); + if (!s) return s; + } + else if (*s == '>') + { + if (depth == 0) + return s; + + depth--; + s++; + } + else s++; + } + + if (depth != 0 || endch != '>') PUGI__THROW_ERROR(status_bad_doctype, s); + + return s; + } + + char_t* parse_exclamation(char_t* s, xml_node_struct* cursor, unsigned int optmsk, char_t endch) + { + // parse node contents, starting with exclamation mark + ++s; + + if (*s == '-') // 'value = s; // Save the offset. + } + + if (PUGI__OPTSET(parse_eol) && PUGI__OPTSET(parse_comments)) + { + s = strconv_comment(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_comment, cursor->value); + } + else + { + // Scan for terminating '-->'. + PUGI__SCANFOR(s[0] == '-' && s[1] == '-' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_comment, s); + + if (PUGI__OPTSET(parse_comments)) + *s = 0; // Zero-terminate this segment at the first terminating '-'. + + s += (s[2] == '>' ? 3 : 2); // Step over the '\0->'. + } + } + else PUGI__THROW_ERROR(status_bad_comment, s); + } + else if (*s == '[') + { + // 'value = s; // Save the offset. + + if (PUGI__OPTSET(parse_eol)) + { + s = strconv_cdata(s, endch); + + if (!s) PUGI__THROW_ERROR(status_bad_cdata, cursor->value); + } + else + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + *s++ = 0; // Zero-terminate this segment. + } + } + else // Flagged for discard, but we still have to scan for the terminator. + { + // Scan for terminating ']]>'. + PUGI__SCANFOR(s[0] == ']' && s[1] == ']' && PUGI__ENDSWITH(s[2], '>')); + PUGI__CHECK_ERROR(status_bad_cdata, s); + + ++s; + } + + s += (s[1] == '>' ? 2 : 1); // Step over the last ']>'. + } + else PUGI__THROW_ERROR(status_bad_cdata, s); + } + else if (s[0] == 'D' && s[1] == 'O' && s[2] == 'C' && s[3] == 'T' && s[4] == 'Y' && s[5] == 'P' && PUGI__ENDSWITH(s[6], 'E')) + { + s -= 2; + + if (cursor->parent) PUGI__THROW_ERROR(status_bad_doctype, s); + + char_t* mark = s + 9; + + s = parse_doctype_group(s, endch); + if (!s) return s; + + assert((*s == 0 && endch == '>') || *s == '>'); + if (*s) *s++ = 0; + + if (PUGI__OPTSET(parse_doctype)) + { + while (PUGI__IS_CHARTYPE(*mark, ct_space)) ++mark; + + PUGI__PUSHNODE(node_doctype); + + cursor->value = mark; + } + } + else if (*s == 0 && endch == '-') PUGI__THROW_ERROR(status_bad_comment, s); + else if (*s == 0 && endch == '[') PUGI__THROW_ERROR(status_bad_cdata, s); + else PUGI__THROW_ERROR(status_unrecognized_tag, s); + + return s; + } + + char_t* parse_question(char_t* s, xml_node_struct*& ref_cursor, unsigned int optmsk, char_t endch) + { + // load into registers + xml_node_struct* cursor = ref_cursor; + char_t ch = 0; + + // parse node contents, starting with question mark + ++s; + + // read PI target + char_t* target = s; + + if (!PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__SCANWHILE(PUGI__IS_CHARTYPE(*s, ct_symbol)); + PUGI__CHECK_ERROR(status_bad_pi, s); + + // determine node type; stricmp / strcasecmp is not portable + bool declaration = (target[0] | ' ') == 'x' && (target[1] | ' ') == 'm' && (target[2] | ' ') == 'l' && target + 3 == s; + + if (declaration ? PUGI__OPTSET(parse_declaration) : PUGI__OPTSET(parse_pi)) + { + if (declaration) + { + // disallow non top-level declarations + if (cursor->parent) PUGI__THROW_ERROR(status_bad_pi, s); + + PUGI__PUSHNODE(node_declaration); + } + else + { + PUGI__PUSHNODE(node_pi); + } + + cursor->name = target; + + PUGI__ENDSEG(); + + // parse value/attributes + if (ch == '?') + { + // empty node + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_pi, s); + s += (*s == '>'); + + PUGI__POPNODE(); + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); + + // scan for tag end + char_t* value = s; + + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + if (declaration) + { + // replace ending ? with / so that 'element' terminates properly + *s = '/'; + + // we exit from this function with cursor at node_declaration, which is a signal to parse() to go to LOC_ATTRIBUTES + s = value; + } + else + { + // store value and step over > + cursor->value = value; + + PUGI__POPNODE(); + + PUGI__ENDSEG(); + + s += (*s == '>'); + } + } + else PUGI__THROW_ERROR(status_bad_pi, s); + } + else + { + // scan for tag end + PUGI__SCANFOR(s[0] == '?' && PUGI__ENDSWITH(s[1], '>')); + PUGI__CHECK_ERROR(status_bad_pi, s); + + s += (s[1] == '>' ? 2 : 1); + } + + // store from registers + ref_cursor = cursor; + + return s; + } + + char_t* parse_tree(char_t* s, xml_node_struct* root, unsigned int optmsk, char_t endch) + { + strconv_attribute_t strconv_attribute = get_strconv_attribute(optmsk); + strconv_pcdata_t strconv_pcdata = get_strconv_pcdata(optmsk); + + char_t ch = 0; + xml_node_struct* cursor = root; + char_t* mark = s; + + while (*s != 0) + { + if (*s == '<') + { + ++s; + + LOC_TAG: + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // '<#...' + { + PUGI__PUSHNODE(node_element); // Append a new node to the tree. + + cursor->name = s; + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (ch == '>') + { + // end of tag + } + else if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + LOC_ATTRIBUTES: + while (true) + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) // <... #... + { + xml_attribute_struct* a = append_new_attribute(cursor, *alloc); // Make space for this attribute. + if (!a) PUGI__THROW_ERROR(status_out_of_memory, s); + + a->name = s; // Save the offset. + + PUGI__SCANWHILE_UNROLL(PUGI__IS_CHARTYPE(ss, ct_symbol)); // Scan for a terminator. + PUGI__ENDSEG(); // Save char in 'ch', terminate & step over. + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + PUGI__SKIPWS(); // Eat any whitespace. + + ch = *s; + ++s; + } + + if (ch == '=') // '<... #=...' + { + PUGI__SKIPWS(); // Eat any whitespace. + + if (*s == '"' || *s == '\'') // '<... #="...' + { + ch = *s; // Save quote char to avoid breaking on "''" -or- '""'. + ++s; // Step over the quote. + a->value = s; // Save the offset. + + s = strconv_attribute(s, ch); + + if (!s) PUGI__THROW_ERROR(status_bad_attribute, a->value); + + // After this line the loop continues from the start; + // Whitespaces, / and > are ok, symbols and EOF are wrong, + // everything else will be detected + if (PUGI__IS_CHARTYPE(*s, ct_start_symbol)) PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else PUGI__THROW_ERROR(status_bad_attribute, s); + } + else if (*s == '/') + { + ++s; + + if (*s == '>') + { + PUGI__POPNODE(); + s++; + break; + } + else if (*s == 0 && endch == '>') + { + PUGI__POPNODE(); + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '>') + { + ++s; + + break; + } + else if (*s == 0 && endch == '>') + { + break; + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + + // !!! + } + else if (ch == '/') // '<#.../' + { + if (!PUGI__ENDSWITH(*s, '>')) PUGI__THROW_ERROR(status_bad_start_element, s); + + PUGI__POPNODE(); // Pop. + + s += (*s == '>'); + } + else if (ch == 0) + { + // we stepped over null terminator, backtrack & handle closing tag + --s; + + if (endch != '>') PUGI__THROW_ERROR(status_bad_start_element, s); + } + else PUGI__THROW_ERROR(status_bad_start_element, s); + } + else if (*s == '/') + { + ++s; + + mark = s; + + char_t* name = cursor->name; + if (!name) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + + while (PUGI__IS_CHARTYPE(*s, ct_symbol)) + { + if (*s++ != *name++) PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + if (*name) + { + if (*s == 0 && name[0] == endch && name[1] == 0) PUGI__THROW_ERROR(status_bad_end_element, s); + else PUGI__THROW_ERROR(status_end_element_mismatch, mark); + } + + PUGI__POPNODE(); // Pop. + + PUGI__SKIPWS(); + + if (*s == 0) + { + if (endch != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + } + else + { + if (*s != '>') PUGI__THROW_ERROR(status_bad_end_element, s); + ++s; + } + } + else if (*s == '?') // 'first_child) continue; + } + } + + if (!PUGI__OPTSET(parse_trim_pcdata)) + s = mark; + + if (cursor->parent || PUGI__OPTSET(parse_fragment)) + { + if (PUGI__OPTSET(parse_embed_pcdata) && cursor->parent && !cursor->first_child && !cursor->value) + { + cursor->value = s; // Save the offset. + } + else + { + PUGI__PUSHNODE(node_pcdata); // Append a new node on the tree. + + cursor->value = s; // Save the offset. + + PUGI__POPNODE(); // Pop since this is a standalone. + } + + s = strconv_pcdata(s); + + if (!*s) break; + } + else + { + PUGI__SCANFOR(*s == '<'); // '...<' + if (!*s) break; + + ++s; + } + + // We're after '<' + goto LOC_TAG; + } + } + + // check that last tag is closed + if (cursor != root) PUGI__THROW_ERROR(status_end_element_mismatch, s); + + return s; + } + + #ifdef PUGIXML_WCHAR_MODE + static char_t* parse_skip_bom(char_t* s) + { + unsigned int bom = 0xfeff; + return (s[0] == static_cast(bom)) ? s + 1 : s; + } + #else + static char_t* parse_skip_bom(char_t* s) + { + return (s[0] == '\xef' && s[1] == '\xbb' && s[2] == '\xbf') ? s + 3 : s; + } + #endif + + static bool has_element_node_siblings(xml_node_struct* node) + { + while (node) + { + if (PUGI__NODETYPE(node) == node_element) return true; + + node = node->next_sibling; + } + + return false; + } + + static xml_parse_result parse(char_t* buffer, size_t length, xml_document_struct* xmldoc, xml_node_struct* root, unsigned int optmsk) + { + // early-out for empty documents + if (length == 0) + return make_parse_result(PUGI__OPTSET(parse_fragment) ? status_ok : status_no_document_element); + + // get last child of the root before parsing + xml_node_struct* last_root_child = root->first_child ? root->first_child->prev_sibling_c + 0 : 0; + + // create parser on stack + xml_parser parser(static_cast(xmldoc)); + + // save last character and make buffer zero-terminated (speeds up parsing) + char_t endch = buffer[length - 1]; + buffer[length - 1] = 0; + + // skip BOM to make sure it does not end up as part of parse output + char_t* buffer_data = parse_skip_bom(buffer); + + // perform actual parsing + parser.parse_tree(buffer_data, root, optmsk, endch); + + xml_parse_result result = make_parse_result(parser.error_status, parser.error_offset ? parser.error_offset - buffer : 0); + assert(result.offset >= 0 && static_cast(result.offset) <= length); + + if (result) + { + // since we removed last character, we have to handle the only possible false positive (stray <) + if (endch == '<') + return make_parse_result(status_unrecognized_tag, length - 1); + + // check if there are any element nodes parsed + xml_node_struct* first_root_child_parsed = last_root_child ? last_root_child->next_sibling + 0 : root->first_child+ 0; + + if (!PUGI__OPTSET(parse_fragment) && !has_element_node_siblings(first_root_child_parsed)) + return make_parse_result(status_no_document_element, length - 1); + } + else + { + // roll back offset if it occurs on a null terminator in the source buffer + if (result.offset > 0 && static_cast(result.offset) == length - 1 && endch == 0) + result.offset--; + } + + return result; + } + }; + + // Output facilities + PUGI__FN xml_encoding get_write_native_encoding() + { + #ifdef PUGIXML_WCHAR_MODE + return get_wchar_encoding(); + #else + return encoding_utf8; + #endif + } + + PUGI__FN xml_encoding get_write_encoding(xml_encoding encoding) + { + // replace wchar encoding with utf implementation + if (encoding == encoding_wchar) return get_wchar_encoding(); + + // replace utf16 encoding with utf16 with specific endianness + if (encoding == encoding_utf16) return is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + // replace utf32 encoding with utf32 with specific endianness + if (encoding == encoding_utf32) return is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + // only do autodetection if no explicit encoding is requested + if (encoding != encoding_auto) return encoding; + + // assume utf8 encoding + return encoding_utf8; + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + return static_cast(end - dest) * sizeof(*dest); + } + + template PUGI__FN size_t convert_buffer_output_generic(typename T::value_type dest, const char_t* data, size_t length, D, T, bool opt_swap) + { + PUGI__STATIC_ASSERT(sizeof(char_t) == sizeof(typename D::type)); + + typename T::value_type end = D::process(reinterpret_cast(data), length, dest, T()); + + if (opt_swap) + { + for (typename T::value_type i = dest; i != end; ++i) + *i = endian_swap(*i); + } + + return static_cast(end - dest) * sizeof(*dest); + } + +#ifdef PUGIXML_WCHAR_MODE + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 1) return 0; + + // discard last character if it's the lead of a surrogate pair + return (sizeof(wchar_t) == 2 && static_cast(static_cast(data[length - 1]) - 0xD800) < 0x400) ? length - 1 : length; + } + + PUGI__FN size_t convert_buffer_output(char_t* r_char, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + // only endian-swapping is required + if (need_endian_swap_utf(encoding, get_wchar_encoding())) + { + convert_wchar_endian_swap(r_char, data, length); + + return length * sizeof(char_t); + } + + // convert to utf8 + if (encoding == encoding_utf8) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), utf8_writer()); + + // convert to utf16 + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, wchar_decoder(), utf16_writer(), native_encoding != encoding); + } + + // convert to utf32 + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, wchar_decoder(), utf32_writer(), native_encoding != encoding); + } + + // convert to latin1 + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, wchar_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); // unreachable + return 0; + } +#else + PUGI__FN size_t get_valid_length(const char_t* data, size_t length) + { + if (length < 5) return 0; + + for (size_t i = 1; i <= 4; ++i) + { + uint8_t ch = static_cast(data[length - i]); + + // either a standalone character or a leading one + if ((ch & 0xc0) != 0x80) return length - i; + } + + // there are four non-leading characters at the end, sequence tail is broken so might as well process the whole chunk + return length; + } + + PUGI__FN size_t convert_buffer_output(char_t* /* r_char */, uint8_t* r_u8, uint16_t* r_u16, uint32_t* r_u32, const char_t* data, size_t length, xml_encoding encoding) + { + if (encoding == encoding_utf16_be || encoding == encoding_utf16_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf16_le : encoding_utf16_be; + + return convert_buffer_output_generic(r_u16, data, length, utf8_decoder(), utf16_writer(), native_encoding != encoding); + } + + if (encoding == encoding_utf32_be || encoding == encoding_utf32_le) + { + xml_encoding native_encoding = is_little_endian() ? encoding_utf32_le : encoding_utf32_be; + + return convert_buffer_output_generic(r_u32, data, length, utf8_decoder(), utf32_writer(), native_encoding != encoding); + } + + if (encoding == encoding_latin1) + return convert_buffer_output_generic(r_u8, data, length, utf8_decoder(), latin1_writer()); + + assert(false && "Invalid encoding"); // unreachable + return 0; + } +#endif + + class xml_buffered_writer + { + xml_buffered_writer(const xml_buffered_writer&); + xml_buffered_writer& operator=(const xml_buffered_writer&); + + public: + xml_buffered_writer(xml_writer& writer_, xml_encoding user_encoding): writer(writer_), bufsize(0), encoding(get_write_encoding(user_encoding)) + { + PUGI__STATIC_ASSERT(bufcapacity >= 8); + } + + size_t flush() + { + flush(buffer, bufsize); + bufsize = 0; + return 0; + } + + void flush(const char_t* data, size_t size) + { + if (size == 0) return; + + // fast path, just write data + if (encoding == get_write_native_encoding()) + writer.write(data, size * sizeof(char_t)); + else + { + // convert chunk + size_t result = convert_buffer_output(scratch.data_char, scratch.data_u8, scratch.data_u16, scratch.data_u32, data, size, encoding); + assert(result <= sizeof(scratch)); + + // write data + writer.write(scratch.data_u8, result); + } + } + + void write_direct(const char_t* data, size_t length) + { + // flush the remaining buffer contents + flush(); + + // handle large chunks + if (length > bufcapacity) + { + if (encoding == get_write_native_encoding()) + { + // fast path, can just write data chunk + writer.write(data, length * sizeof(char_t)); + return; + } + + // need to convert in suitable chunks + while (length > bufcapacity) + { + // get chunk size by selecting such number of characters that are guaranteed to fit into scratch buffer + // and form a complete codepoint sequence (i.e. discard start of last codepoint if necessary) + size_t chunk_size = get_valid_length(data, bufcapacity); + assert(chunk_size); + + // convert chunk and write + flush(data, chunk_size); + + // iterate + data += chunk_size; + length -= chunk_size; + } + + // small tail is copied below + bufsize = 0; + } + + memcpy(buffer + bufsize, data, length * sizeof(char_t)); + bufsize += length; + } + + void write_buffer(const char_t* data, size_t length) + { + size_t offset = bufsize; + + if (offset + length <= bufcapacity) + { + memcpy(buffer + offset, data, length * sizeof(char_t)); + bufsize = offset + length; + } + else + { + write_direct(data, length); + } + } + + void write_string(const char_t* data) + { + // write the part of the string that fits in the buffer + size_t offset = bufsize; + + while (*data && offset < bufcapacity) + buffer[offset++] = *data++; + + // write the rest + if (offset < bufcapacity) + { + bufsize = offset; + } + else + { + // backtrack a bit if we have split the codepoint + size_t length = offset - bufsize; + size_t extra = length - get_valid_length(data - length, length); + + bufsize = offset - extra; + + write_direct(data - extra, strlength(data) + extra); + } + } + + void write(char_t d0) + { + size_t offset = bufsize; + if (offset > bufcapacity - 1) offset = flush(); + + buffer[offset + 0] = d0; + bufsize = offset + 1; + } + + void write(char_t d0, char_t d1) + { + size_t offset = bufsize; + if (offset > bufcapacity - 2) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + bufsize = offset + 2; + } + + void write(char_t d0, char_t d1, char_t d2) + { + size_t offset = bufsize; + if (offset > bufcapacity - 3) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + bufsize = offset + 3; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3) + { + size_t offset = bufsize; + if (offset > bufcapacity - 4) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + bufsize = offset + 4; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4) + { + size_t offset = bufsize; + if (offset > bufcapacity - 5) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + bufsize = offset + 5; + } + + void write(char_t d0, char_t d1, char_t d2, char_t d3, char_t d4, char_t d5) + { + size_t offset = bufsize; + if (offset > bufcapacity - 6) offset = flush(); + + buffer[offset + 0] = d0; + buffer[offset + 1] = d1; + buffer[offset + 2] = d2; + buffer[offset + 3] = d3; + buffer[offset + 4] = d4; + buffer[offset + 5] = d5; + bufsize = offset + 6; + } + + // utf8 maximum expansion: x4 (-> utf32) + // utf16 maximum expansion: x2 (-> utf32) + // utf32 maximum expansion: x1 + enum + { + bufcapacitybytes = + #ifdef PUGIXML_MEMORY_OUTPUT_STACK + PUGIXML_MEMORY_OUTPUT_STACK + #else + 10240 + #endif + , + bufcapacity = bufcapacitybytes / (sizeof(char_t) + 4) + }; + + char_t buffer[bufcapacity]; + + union + { + uint8_t data_u8[4 * bufcapacity]; + uint16_t data_u16[2 * bufcapacity]; + uint32_t data_u32[bufcapacity]; + char_t data_char[bufcapacity]; + } scratch; + + xml_writer& writer; + size_t bufsize; + xml_encoding encoding; + }; + + PUGI__FN void text_output_escaped(xml_buffered_writer& writer, const char_t* s, chartypex_t type) + { + while (*s) + { + const char_t* prev = s; + + // While *s is a usual symbol + PUGI__SCANWHILE_UNROLL(!PUGI__IS_CHARTYPEX(ss, type)); + + writer.write_buffer(prev, static_cast(s - prev)); + + switch (*s) + { + case 0: break; + case '&': + writer.write('&', 'a', 'm', 'p', ';'); + ++s; + break; + case '<': + writer.write('&', 'l', 't', ';'); + ++s; + break; + case '>': + writer.write('&', 'g', 't', ';'); + ++s; + break; + case '"': + writer.write('&', 'q', 'u', 'o', 't', ';'); + ++s; + break; + default: // s is not a usual symbol + { + unsigned int ch = static_cast(*s++); + assert(ch < 32); + + writer.write('&', '#', static_cast((ch / 10) + '0'), static_cast((ch % 10) + '0'), ';'); + } + } + } + } + + PUGI__FN void text_output(xml_buffered_writer& writer, const char_t* s, chartypex_t type, unsigned int flags) + { + if (flags & format_no_escapes) + writer.write_string(s); + else + text_output_escaped(writer, s, type); + } + + PUGI__FN void text_output_cdata(xml_buffered_writer& writer, const char_t* s) + { + do + { + writer.write('<', '!', '[', 'C', 'D'); + writer.write('A', 'T', 'A', '['); + + const char_t* prev = s; + + // look for ]]> sequence - we can't output it as is since it terminates CDATA + while (*s && !(s[0] == ']' && s[1] == ']' && s[2] == '>')) ++s; + + // skip ]] if we stopped at ]]>, > will go to the next CDATA section + if (*s) s += 2; + + writer.write_buffer(prev, static_cast(s - prev)); + + writer.write(']', ']', '>'); + } + while (*s); + } + + PUGI__FN void text_output_indent(xml_buffered_writer& writer, const char_t* indent, size_t indent_length, unsigned int depth) + { + switch (indent_length) + { + case 1: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0]); + break; + } + + case 2: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1]); + break; + } + + case 3: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2]); + break; + } + + case 4: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write(indent[0], indent[1], indent[2], indent[3]); + break; + } + + default: + { + for (unsigned int i = 0; i < depth; ++i) + writer.write_buffer(indent, indent_length); + } + } + } + + PUGI__FN void node_output_comment(xml_buffered_writer& writer, const char_t* s) + { + writer.write('<', '!', '-', '-'); + + while (*s) + { + const char_t* prev = s; + + // look for -\0 or -- sequence - we can't output it since -- is illegal in comment body + while (*s && !(s[0] == '-' && (s[1] == '-' || s[1] == 0))) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(*s == '-'); + + writer.write('-', ' '); + ++s; + } + } + + writer.write('-', '-', '>'); + } + + PUGI__FN void node_output_pi_value(xml_buffered_writer& writer, const char_t* s) + { + while (*s) + { + const char_t* prev = s; + + // look for ?> sequence - we can't output it since ?> terminates PI + while (*s && !(s[0] == '?' && s[1] == '>')) ++s; + + writer.write_buffer(prev, static_cast(s - prev)); + + if (*s) + { + assert(s[0] == '?' && s[1] == '>'); + + writer.write('?', ' ', '>'); + s += 2; + } + } + } + + PUGI__FN void node_output_attributes(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + { + if ((flags & (format_indent_attributes | format_raw)) == format_indent_attributes) + { + writer.write('\n'); + + text_output_indent(writer, indent, indent_length, depth + 1); + } + else + { + writer.write(' '); + } + + writer.write_string(a->name ? a->name + 0 : default_name); + writer.write('=', '"'); + + if (a->value) + text_output(writer, a->value, ctx_special_attr, flags); + + writer.write('"'); + } + } + + PUGI__FN bool node_output_start(xml_buffered_writer& writer, xml_node_struct* node, const char_t* indent, size_t indent_length, unsigned int flags, unsigned int depth) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<'); + writer.write_string(name); + + if (node->first_attribute) + node_output_attributes(writer, node, indent, indent_length, flags, depth); + + // element nodes can have value if parse_embed_pcdata was used + if (!node->value) + { + if (!node->first_child) + { + if (flags & format_no_empty_element_tags) + { + writer.write('>', '<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + if ((flags & format_raw) == 0) + writer.write(' '); + + writer.write('/', '>'); + + return false; + } + } + else + { + writer.write('>'); + + return true; + } + } + else + { + writer.write('>'); + + text_output(writer, node->value, ctx_special_pcdata, flags); + + if (!node->first_child) + { + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + + return false; + } + else + { + return true; + } + } + } + + PUGI__FN void node_output_end(xml_buffered_writer& writer, xml_node_struct* node) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + const char_t* name = node->name ? node->name + 0 : default_name; + + writer.write('<', '/'); + writer.write_string(name); + writer.write('>'); + } + + PUGI__FN void node_output_simple(xml_buffered_writer& writer, xml_node_struct* node, unsigned int flags) + { + const char_t* default_name = PUGIXML_TEXT(":anonymous"); + + switch (PUGI__NODETYPE(node)) + { + case node_pcdata: + text_output(writer, node->value ? node->value + 0 : PUGIXML_TEXT(""), ctx_special_pcdata, flags); + break; + + case node_cdata: + text_output_cdata(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_comment: + node_output_comment(writer, node->value ? node->value + 0 : PUGIXML_TEXT("")); + break; + + case node_pi: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + + if (node->value) + { + writer.write(' '); + node_output_pi_value(writer, node->value); + } + + writer.write('?', '>'); + break; + + case node_declaration: + writer.write('<', '?'); + writer.write_string(node->name ? node->name + 0 : default_name); + node_output_attributes(writer, node, PUGIXML_TEXT(""), 0, flags | format_raw, 0); + writer.write('?', '>'); + break; + + case node_doctype: + writer.write('<', '!', 'D', 'O', 'C'); + writer.write('T', 'Y', 'P', 'E'); + + if (node->value) + { + writer.write(' '); + writer.write_string(node->value); + } + + writer.write('>'); + break; + + default: + assert(false && "Invalid node type"); // unreachable + } + } + + enum indent_flags_t + { + indent_newline = 1, + indent_indent = 2 + }; + + PUGI__FN void node_output(xml_buffered_writer& writer, xml_node_struct* root, const char_t* indent, unsigned int flags, unsigned int depth) + { + size_t indent_length = ((flags & (format_indent | format_indent_attributes)) && (flags & format_raw) == 0) ? strlength(indent) : 0; + unsigned int indent_flags = indent_indent; + + xml_node_struct* node = root; + + do + { + assert(node); + + // begin writing current node + if (PUGI__NODETYPE(node) == node_pcdata || PUGI__NODETYPE(node) == node_cdata) + { + node_output_simple(writer, node, flags); + + indent_flags = 0; + } + else + { + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + if (PUGI__NODETYPE(node) == node_element) + { + indent_flags = indent_newline | indent_indent; + + if (node_output_start(writer, node, indent, indent_length, flags, depth)) + { + // element nodes can have value if parse_embed_pcdata was used + if (node->value) + indent_flags = 0; + + node = node->first_child; + depth++; + continue; + } + } + else if (PUGI__NODETYPE(node) == node_document) + { + indent_flags = indent_indent; + + if (node->first_child) + { + node = node->first_child; + continue; + } + } + else + { + node_output_simple(writer, node, flags); + + indent_flags = indent_newline | indent_indent; + } + } + + // continue to the next node + while (node != root) + { + if (node->next_sibling) + { + node = node->next_sibling; + break; + } + + node = node->parent; + + // write closing node + if (PUGI__NODETYPE(node) == node_element) + { + depth--; + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + + if ((indent_flags & indent_indent) && indent_length) + text_output_indent(writer, indent, indent_length, depth); + + node_output_end(writer, node); + + indent_flags = indent_newline | indent_indent; + } + } + } + while (node != root); + + if ((indent_flags & indent_newline) && (flags & format_raw) == 0) + writer.write('\n'); + } + + PUGI__FN bool has_declaration(xml_node_struct* node) + { + for (xml_node_struct* child = node->first_child; child; child = child->next_sibling) + { + xml_node_type type = PUGI__NODETYPE(child); + + if (type == node_declaration) return true; + if (type == node_element) return false; + } + + return false; + } + + PUGI__FN bool is_attribute_of(xml_attribute_struct* attr, xml_node_struct* node) + { + for (xml_attribute_struct* a = node->first_attribute; a; a = a->next_attribute) + if (a == attr) + return true; + + return false; + } + + PUGI__FN bool allow_insert_attribute(xml_node_type parent) + { + return parent == node_element || parent == node_declaration; + } + + PUGI__FN bool allow_insert_child(xml_node_type parent, xml_node_type child) + { + if (parent != node_document && parent != node_element) return false; + if (child == node_document || child == node_null) return false; + if (parent != node_document && (child == node_declaration || child == node_doctype)) return false; + + return true; + } + + PUGI__FN bool allow_move(xml_node parent, xml_node child) + { + // check that child can be a child of parent + if (!allow_insert_child(parent.type(), child.type())) + return false; + + // check that node is not moved between documents + if (parent.root() != child.root()) + return false; + + // check that new parent is not in the child subtree + xml_node cur = parent; + + while (cur) + { + if (cur == child) + return false; + + cur = cur.parent(); + } + + return true; + } + + template + PUGI__FN void node_copy_string(String& dest, Header& header, uintptr_t header_mask, char_t* source, Header& source_header, xml_allocator* alloc) + { + assert(!dest && (header & header_mask) == 0); + + if (source) + { + if (alloc && (source_header & header_mask) == 0) + { + dest = source; + + // since strcpy_insitu can reuse document buffer memory we need to mark both source and dest as shared + header |= xml_memory_page_contents_shared_mask; + source_header |= xml_memory_page_contents_shared_mask; + } + else + strcpy_insitu(dest, header, header_mask, source, strlength(source)); + } + } + + PUGI__FN void node_copy_contents(xml_node_struct* dn, xml_node_struct* sn, xml_allocator* shared_alloc) + { + node_copy_string(dn->name, dn->header, xml_memory_page_name_allocated_mask, sn->name, sn->header, shared_alloc); + node_copy_string(dn->value, dn->header, xml_memory_page_value_allocated_mask, sn->value, sn->header, shared_alloc); + + for (xml_attribute_struct* sa = sn->first_attribute; sa; sa = sa->next_attribute) + { + xml_attribute_struct* da = append_new_attribute(dn, get_allocator(dn)); + + if (da) + { + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + } + } + + PUGI__FN void node_copy_tree(xml_node_struct* dn, xml_node_struct* sn) + { + xml_allocator& alloc = get_allocator(dn); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sn)) ? &alloc : 0; + + node_copy_contents(dn, sn, shared_alloc); + + xml_node_struct* dit = dn; + xml_node_struct* sit = sn->first_child; + + while (sit && sit != sn) + { + // when a tree is copied into one of the descendants, we need to skip that subtree to avoid an infinite loop + if (sit != dn) + { + xml_node_struct* copy = append_new_node(dit, alloc, PUGI__NODETYPE(sit)); + + if (copy) + { + node_copy_contents(copy, sit, shared_alloc); + + if (sit->first_child) + { + dit = copy; + sit = sit->first_child; + continue; + } + } + } + + // continue to the next node + do + { + if (sit->next_sibling) + { + sit = sit->next_sibling; + break; + } + + sit = sit->parent; + dit = dit->parent; + } + while (sit != sn); + } + } + + PUGI__FN void node_copy_attribute(xml_attribute_struct* da, xml_attribute_struct* sa) + { + xml_allocator& alloc = get_allocator(da); + xml_allocator* shared_alloc = (&alloc == &get_allocator(sa)) ? &alloc : 0; + + node_copy_string(da->name, da->header, xml_memory_page_name_allocated_mask, sa->name, sa->header, shared_alloc); + node_copy_string(da->value, da->header, xml_memory_page_value_allocated_mask, sa->value, sa->header, shared_alloc); + } + + inline bool is_text_node(xml_node_struct* node) + { + xml_node_type type = PUGI__NODETYPE(node); + + return type == node_pcdata || type == node_cdata; + } + + // get value with conversion functions + template PUGI__FN PUGI__UNSIGNED_OVERFLOW U string_to_integer(const char_t* value, U minv, U maxv) + { + U result = 0; + const char_t* s = value; + + while (PUGI__IS_CHARTYPE(*s, ct_space)) + s++; + + bool negative = (*s == '-'); + + s += (*s == '+' || *s == '-'); + + bool overflow = false; + + if (s[0] == '0' && (s[1] | ' ') == 'x') + { + s += 2; + + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 16 + (*s - '0'); + else if (static_cast((*s | ' ') - 'a') < 6) + result = result * 16 + ((*s | ' ') - 'a' + 10); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + overflow = digits > sizeof(U) * 2; + } + else + { + // since overflow detection relies on length of the sequence skip leading zeros + while (*s == '0') + s++; + + const char_t* start = s; + + for (;;) + { + if (static_cast(*s - '0') < 10) + result = result * 10 + (*s - '0'); + else + break; + + s++; + } + + size_t digits = static_cast(s - start); + + PUGI__STATIC_ASSERT(sizeof(U) == 8 || sizeof(U) == 4 || sizeof(U) == 2); + + const size_t max_digits10 = sizeof(U) == 8 ? 20 : sizeof(U) == 4 ? 10 : 5; + const char_t max_lead = sizeof(U) == 8 ? '1' : sizeof(U) == 4 ? '4' : '6'; + const size_t high_bit = sizeof(U) * 8 - 1; + + overflow = digits >= max_digits10 && !(digits == max_digits10 && (*start < max_lead || (*start == max_lead && result >> high_bit))); + } + + if (negative) + { + // Workaround for crayc++ CC-3059: Expected no overflow in routine. + #ifdef _CRAYC + return (overflow || result > ~minv + 1) ? minv : ~result + 1; + #else + return (overflow || result > 0 - minv) ? minv : 0 - result; + #endif + } + else + return (overflow || result > maxv) ? maxv : result; + } + + PUGI__FN int get_value_int(const char_t* value) + { + return string_to_integer(value, static_cast(INT_MIN), INT_MAX); + } + + PUGI__FN unsigned int get_value_uint(const char_t* value) + { + return string_to_integer(value, 0, UINT_MAX); + } + + PUGI__FN double get_value_double(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return wcstod(value, 0); + #else + return strtod(value, 0); + #endif + } + + PUGI__FN float get_value_float(const char_t* value) + { + #ifdef PUGIXML_WCHAR_MODE + return static_cast(wcstod(value, 0)); + #else + return static_cast(strtod(value, 0)); + #endif + } + + PUGI__FN bool get_value_bool(const char_t* value) + { + // only look at first char + char_t first = *value; + + // 1*, t* (true), T* (True), y* (yes), Y* (YES) + return (first == '1' || first == 't' || first == 'T' || first == 'y' || first == 'Y'); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long get_value_llong(const char_t* value) + { + return string_to_integer(value, static_cast(LLONG_MIN), LLONG_MAX); + } + + PUGI__FN unsigned long long get_value_ullong(const char_t* value) + { + return string_to_integer(value, 0, ULLONG_MAX); + } +#endif + + template PUGI__FN PUGI__UNSIGNED_OVERFLOW char_t* integer_to_string(char_t* begin, char_t* end, U value, bool negative) + { + char_t* result = end - 1; + U rest = negative ? 0 - value : value; + + do + { + *result-- = static_cast('0' + (rest % 10)); + rest /= 10; + } + while (rest); + + assert(result >= begin); + (void)begin; + + *result = '-'; + + return result + !negative; + } + + // set value with conversion functions + template + PUGI__FN bool set_value_ascii(String& dest, Header& header, uintptr_t header_mask, char* buf) + { + #ifdef PUGIXML_WCHAR_MODE + char_t wbuf[128]; + assert(strlen(buf) < sizeof(wbuf) / sizeof(wbuf[0])); + + size_t offset = 0; + for (; buf[offset]; ++offset) wbuf[offset] = buf[offset]; + + return strcpy_insitu(dest, header, header_mask, wbuf, offset); + #else + return strcpy_insitu(dest, header, header_mask, buf, strlen(buf)); + #endif + } + + template + PUGI__FN bool set_value_integer(String& dest, Header& header, uintptr_t header_mask, U value, bool negative) + { + char_t buf[64]; + char_t* end = buf + sizeof(buf) / sizeof(buf[0]); + char_t* begin = integer_to_string(buf, end, value, negative); + + return strcpy_insitu(dest, header, header_mask, begin, end - begin); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, float value) + { + char buf[128]; + PUGI__SNPRINTF(buf, "%.9g", value); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_convert(String& dest, Header& header, uintptr_t header_mask, double value) + { + char buf[128]; + PUGI__SNPRINTF(buf, "%.17g", value); + + return set_value_ascii(dest, header, header_mask, buf); + } + + template + PUGI__FN bool set_value_bool(String& dest, Header& header, uintptr_t header_mask, bool value) + { + return strcpy_insitu(dest, header, header_mask, value ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false"), value ? 4 : 5); + } + + PUGI__FN xml_parse_result load_buffer_impl(xml_document_struct* doc, xml_node_struct* root, void* contents, size_t size, unsigned int options, xml_encoding encoding, bool is_mutable, bool own, char_t** out_buffer) + { + // check input buffer + if (!contents && size) return make_parse_result(status_io_error); + + // get actual encoding + xml_encoding buffer_encoding = impl::get_buffer_encoding(encoding, contents, size); + + // get private buffer + char_t* buffer = 0; + size_t length = 0; + + if (!impl::convert_buffer(buffer, length, buffer_encoding, contents, size, is_mutable)) return impl::make_parse_result(status_out_of_memory); + + // delete original buffer if we performed a conversion + if (own && buffer != contents && contents) impl::xml_memory::deallocate(contents); + + // grab onto buffer if it's our buffer, user is responsible for deallocating contents himself + if (own || buffer != contents) *out_buffer = buffer; + + // store buffer for offset_debug + doc->buffer = buffer; + + // parse + xml_parse_result res = impl::xml_parser::parse(buffer, length, doc, root, options); + + // remember encoding + res.encoding = buffer_encoding; + + return res; + } + + // we need to get length of entire file to load it in memory; the only (relatively) sane way to do it is via seek/tell trick + PUGI__FN xml_parse_status get_file_size(FILE* file, size_t& out_result) + { + #if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + // there are 64-bit versions of fseek/ftell, let's use them + typedef __int64 length_type; + + _fseeki64(file, 0, SEEK_END); + length_type length = _ftelli64(file); + _fseeki64(file, 0, SEEK_SET); + #elif defined(__MINGW32__) && !defined(__NO_MINGW_LFS) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR)) + // there are 64-bit versions of fseek/ftell, let's use them + typedef off64_t length_type; + + fseeko64(file, 0, SEEK_END); + length_type length = ftello64(file); + fseeko64(file, 0, SEEK_SET); + #else + // if this is a 32-bit OS, long is enough; if this is a unix system, long is 64-bit, which is enough; otherwise we can't do anything anyway. + typedef long length_type; + + fseek(file, 0, SEEK_END); + length_type length = ftell(file); + fseek(file, 0, SEEK_SET); + #endif + + // check for I/O errors + if (length < 0) return status_io_error; + + // check for overflow + size_t result = static_cast(length); + + if (static_cast(result) != length) return status_out_of_memory; + + // finalize + out_result = result; + + return status_ok; + } + + // This function assumes that buffer has extra sizeof(char_t) writable bytes after size + PUGI__FN size_t zero_terminate_buffer(void* buffer, size_t size, xml_encoding encoding) + { + // We only need to zero-terminate if encoding conversion does not do it for us + #ifdef PUGIXML_WCHAR_MODE + xml_encoding wchar_encoding = get_wchar_encoding(); + + if (encoding == wchar_encoding || need_endian_swap_utf(encoding, wchar_encoding)) + { + size_t length = size / sizeof(char_t); + + static_cast(buffer)[length] = 0; + return (length + 1) * sizeof(char_t); + } + #else + if (encoding == encoding_utf8) + { + static_cast(buffer)[size] = 0; + return size + 1; + } + #endif + + return size; + } + + PUGI__FN xml_parse_result load_file_impl(xml_document_struct* doc, FILE* file, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + if (!file) return make_parse_result(status_file_not_found); + + // get file size (can result in I/O errors) + size_t size = 0; + xml_parse_status size_status = get_file_size(file, size); + if (size_status != status_ok) return make_parse_result(size_status); + + size_t max_suffix_size = sizeof(char_t); + + // allocate buffer for the whole file + char* contents = static_cast(xml_memory::allocate(size + max_suffix_size)); + if (!contents) return make_parse_result(status_out_of_memory); + + // read file in memory + size_t read_size = fread(contents, 1, size, file); + + if (read_size != size) + { + xml_memory::deallocate(contents); + return make_parse_result(status_io_error); + } + + xml_encoding real_encoding = get_buffer_encoding(encoding, contents, size); + + return load_buffer_impl(doc, doc, contents, zero_terminate_buffer(contents, size, real_encoding), options, real_encoding, true, true, out_buffer); + } + + PUGI__FN void close_file(FILE* file) + { + fclose(file); + } + +#ifndef PUGIXML_NO_STL + template struct xml_stream_chunk + { + static xml_stream_chunk* create() + { + void* memory = xml_memory::allocate(sizeof(xml_stream_chunk)); + if (!memory) return 0; + + return new (memory) xml_stream_chunk(); + } + + static void destroy(xml_stream_chunk* chunk) + { + // free chunk chain + while (chunk) + { + xml_stream_chunk* next_ = chunk->next; + + xml_memory::deallocate(chunk); + + chunk = next_; + } + } + + xml_stream_chunk(): next(0), size(0) + { + } + + xml_stream_chunk* next; + size_t size; + + T data[xml_memory_page_size / sizeof(T)]; + }; + + template PUGI__FN xml_parse_status load_stream_data_noseek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + auto_deleter > chunks(0, xml_stream_chunk::destroy); + + // read file to a chunk list + size_t total = 0; + xml_stream_chunk* last = 0; + + while (!stream.eof()) + { + // allocate new chunk + xml_stream_chunk* chunk = xml_stream_chunk::create(); + if (!chunk) return status_out_of_memory; + + // append chunk to list + if (last) last = last->next = chunk; + else chunks.data = last = chunk; + + // read data to chunk + stream.read(chunk->data, static_cast(sizeof(chunk->data) / sizeof(T))); + chunk->size = static_cast(stream.gcount()) * sizeof(T); + + // read may set failbit | eofbit in case gcount() is less than read length, so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // guard against huge files (chunk size is small enough to make this overflow check work) + if (total + chunk->size < total) return status_out_of_memory; + total += chunk->size; + } + + size_t max_suffix_size = sizeof(char_t); + + // copy chunk list to a contiguous buffer + char* buffer = static_cast(xml_memory::allocate(total + max_suffix_size)); + if (!buffer) return status_out_of_memory; + + char* write = buffer; + + for (xml_stream_chunk* chunk = chunks.data; chunk; chunk = chunk->next) + { + assert(write + chunk->size <= buffer + total); + memcpy(write, chunk->data, chunk->size); + write += chunk->size; + } + + assert(write == buffer + total); + + // return buffer + *out_buffer = buffer; + *out_size = total; + + return status_ok; + } + + template PUGI__FN xml_parse_status load_stream_data_seek(std::basic_istream& stream, void** out_buffer, size_t* out_size) + { + // get length of remaining data in stream + typename std::basic_istream::pos_type pos = stream.tellg(); + stream.seekg(0, std::ios::end); + std::streamoff length = stream.tellg() - pos; + stream.seekg(pos); + + if (stream.fail() || pos < 0) return status_io_error; + + // guard against huge files + size_t read_length = static_cast(length); + + if (static_cast(read_length) != length || length < 0) return status_out_of_memory; + + size_t max_suffix_size = sizeof(char_t); + + // read stream data into memory (guard against stream exceptions with buffer holder) + auto_deleter buffer(xml_memory::allocate(read_length * sizeof(T) + max_suffix_size), xml_memory::deallocate); + if (!buffer.data) return status_out_of_memory; + + stream.read(static_cast(buffer.data), static_cast(read_length)); + + // read may set failbit | eofbit in case gcount() is less than read_length (i.e. line ending conversion), so check for other I/O errors + if (stream.bad() || (!stream.eof() && stream.fail())) return status_io_error; + + // return buffer + size_t actual_length = static_cast(stream.gcount()); + assert(actual_length <= read_length); + + *out_buffer = buffer.release(); + *out_size = actual_length * sizeof(T); + + return status_ok; + } + + template PUGI__FN xml_parse_result load_stream_impl(xml_document_struct* doc, std::basic_istream& stream, unsigned int options, xml_encoding encoding, char_t** out_buffer) + { + void* buffer = 0; + size_t size = 0; + xml_parse_status status = status_ok; + + // if stream has an error bit set, bail out (otherwise tellg() can fail and we'll clear error bits) + if (stream.fail()) return make_parse_result(status_io_error); + + // load stream to memory (using seek-based implementation if possible, since it's faster and takes less memory) + if (stream.tellg() < 0) + { + stream.clear(); // clear error flags that could be set by a failing tellg + status = load_stream_data_noseek(stream, &buffer, &size); + } + else + status = load_stream_data_seek(stream, &buffer, &size); + + if (status != status_ok) return make_parse_result(status); + + xml_encoding real_encoding = get_buffer_encoding(encoding, buffer, size); + + return load_buffer_impl(doc, doc, buffer, zero_terminate_buffer(buffer, size, real_encoding), options, real_encoding, true, true, out_buffer); + } +#endif + +#if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) || (defined(__MINGW32__) && (!defined(__STRICT_ANSI__) || defined(__MINGW64_VERSION_MAJOR))) + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + return _wfopen(path, mode); + } +#else + PUGI__FN char* convert_path_heap(const wchar_t* str) + { + assert(str); + + // first pass: get length in utf8 characters + size_t length = strlength_wide(str); + size_t size = as_utf8_begin(str, length); + + // allocate resulting string + char* result = static_cast(xml_memory::allocate(size + 1)); + if (!result) return 0; + + // second pass: convert to utf8 + as_utf8_end(result, size, str, length); + + // zero-terminate + result[size] = 0; + + return result; + } + + PUGI__FN FILE* open_file_wide(const wchar_t* path, const wchar_t* mode) + { + // there is no standard function to open wide paths, so our best bet is to try utf8 path + char* path_utf8 = convert_path_heap(path); + if (!path_utf8) return 0; + + // convert mode to ASCII (we mirror _wfopen interface) + char mode_ascii[4] = {0}; + for (size_t i = 0; mode[i]; ++i) mode_ascii[i] = static_cast(mode[i]); + + // try to open the utf8 path + FILE* result = fopen(path_utf8, mode_ascii); + + // free dummy buffer + xml_memory::deallocate(path_utf8); + + return result; + } +#endif + + PUGI__FN bool save_file_impl(const xml_document& doc, FILE* file, const char_t* indent, unsigned int flags, xml_encoding encoding) + { + if (!file) return false; + + xml_writer_file writer(file); + doc.save(writer, indent, flags, encoding); + + return ferror(file) == 0; + } + + struct name_null_sentry + { + xml_node_struct* node; + char_t* name; + + name_null_sentry(xml_node_struct* node_): node(node_), name(node_->name) + { + node->name = 0; + } + + ~name_null_sentry() + { + node->name = name; + } + }; +PUGI__NS_END + +namespace pugi +{ + PUGI__FN xml_writer_file::xml_writer_file(void* file_): file(file_) + { + } + + PUGI__FN void xml_writer_file::write(const void* data, size_t size) + { + size_t result = fwrite(data, 1, size, static_cast(file)); + (void)!result; // unfortunately we can't do proper error handling here + } + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(&stream), wide_stream(0) + { + } + + PUGI__FN xml_writer_stream::xml_writer_stream(std::basic_ostream >& stream): narrow_stream(0), wide_stream(&stream) + { + } + + PUGI__FN void xml_writer_stream::write(const void* data, size_t size) + { + if (narrow_stream) + { + assert(!wide_stream); + narrow_stream->write(reinterpret_cast(data), static_cast(size)); + } + else + { + assert(wide_stream); + assert(size % sizeof(wchar_t) == 0); + + wide_stream->write(reinterpret_cast(data), static_cast(size / sizeof(wchar_t))); + } + } +#endif + + PUGI__FN xml_tree_walker::xml_tree_walker(): _depth(0) + { + } + + PUGI__FN xml_tree_walker::~xml_tree_walker() + { + } + + PUGI__FN int xml_tree_walker::depth() const + { + return _depth; + } + + PUGI__FN bool xml_tree_walker::begin(xml_node&) + { + return true; + } + + PUGI__FN bool xml_tree_walker::end(xml_node&) + { + return true; + } + + PUGI__FN xml_attribute::xml_attribute(): _attr(0) + { + } + + PUGI__FN xml_attribute::xml_attribute(xml_attribute_struct* attr): _attr(attr) + { + } + + PUGI__FN static void unspecified_bool_xml_attribute(xml_attribute***) + { + } + + PUGI__FN xml_attribute::operator xml_attribute::unspecified_bool_type() const + { + return _attr ? unspecified_bool_xml_attribute : 0; + } + + PUGI__FN bool xml_attribute::operator!() const + { + return !_attr; + } + + PUGI__FN bool xml_attribute::operator==(const xml_attribute& r) const + { + return (_attr == r._attr); + } + + PUGI__FN bool xml_attribute::operator!=(const xml_attribute& r) const + { + return (_attr != r._attr); + } + + PUGI__FN bool xml_attribute::operator<(const xml_attribute& r) const + { + return (_attr < r._attr); + } + + PUGI__FN bool xml_attribute::operator>(const xml_attribute& r) const + { + return (_attr > r._attr); + } + + PUGI__FN bool xml_attribute::operator<=(const xml_attribute& r) const + { + return (_attr <= r._attr); + } + + PUGI__FN bool xml_attribute::operator>=(const xml_attribute& r) const + { + return (_attr >= r._attr); + } + + PUGI__FN xml_attribute xml_attribute::next_attribute() const + { + return _attr ? xml_attribute(_attr->next_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_attribute::previous_attribute() const + { + return _attr && _attr->prev_attribute_c->next_attribute ? xml_attribute(_attr->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN const char_t* xml_attribute::as_string(const char_t* def) const + { + return (_attr && _attr->value) ? _attr->value + 0 : def; + } + + PUGI__FN int xml_attribute::as_int(int def) const + { + return (_attr && _attr->value) ? impl::get_value_int(_attr->value) : def; + } + + PUGI__FN unsigned int xml_attribute::as_uint(unsigned int def) const + { + return (_attr && _attr->value) ? impl::get_value_uint(_attr->value) : def; + } + + PUGI__FN double xml_attribute::as_double(double def) const + { + return (_attr && _attr->value) ? impl::get_value_double(_attr->value) : def; + } + + PUGI__FN float xml_attribute::as_float(float def) const + { + return (_attr && _attr->value) ? impl::get_value_float(_attr->value) : def; + } + + PUGI__FN bool xml_attribute::as_bool(bool def) const + { + return (_attr && _attr->value) ? impl::get_value_bool(_attr->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_attribute::as_llong(long long def) const + { + return (_attr && _attr->value) ? impl::get_value_llong(_attr->value) : def; + } + + PUGI__FN unsigned long long xml_attribute::as_ullong(unsigned long long def) const + { + return (_attr && _attr->value) ? impl::get_value_ullong(_attr->value) : def; + } +#endif + + PUGI__FN bool xml_attribute::empty() const + { + return !_attr; + } + + PUGI__FN const char_t* xml_attribute::name() const + { + return (_attr && _attr->name) ? _attr->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_attribute::value() const + { + return (_attr && _attr->value) ? _attr->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN size_t xml_attribute::hash_value() const + { + return static_cast(reinterpret_cast(_attr) / sizeof(xml_attribute_struct)); + } + + PUGI__FN xml_attribute_struct* xml_attribute::internal_object() const + { + return _attr; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(const char_t* rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned int rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(double rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(float rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(bool rhs) + { + set_value(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_attribute& xml_attribute::operator=(long long rhs) + { + set_value(rhs); + return *this; + } + + PUGI__FN xml_attribute& xml_attribute::operator=(unsigned long long rhs) + { + set_value(rhs); + return *this; + } +#endif + + PUGI__FN bool xml_attribute::set_name(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->name, _attr->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(const char_t* rhs) + { + if (!_attr) return false; + + return impl::strcpy_insitu(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_attribute::set_value(int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned int rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } + + PUGI__FN bool xml_attribute::set_value(double rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(float rhs) + { + if (!_attr) return false; + + return impl::set_value_convert(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + + PUGI__FN bool xml_attribute::set_value(bool rhs) + { + if (!_attr) return false; + + return impl::set_value_bool(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs); + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_attribute::set_value(long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0); + } + + PUGI__FN bool xml_attribute::set_value(unsigned long long rhs) + { + if (!_attr) return false; + + return impl::set_value_integer(_attr->value, _attr->header, impl::xml_memory_page_value_allocated_mask, rhs, false); + } +#endif + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_attribute& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node::xml_node(): _root(0) + { + } + + PUGI__FN xml_node::xml_node(xml_node_struct* p): _root(p) + { + } + + PUGI__FN static void unspecified_bool_xml_node(xml_node***) + { + } + + PUGI__FN xml_node::operator xml_node::unspecified_bool_type() const + { + return _root ? unspecified_bool_xml_node : 0; + } + + PUGI__FN bool xml_node::operator!() const + { + return !_root; + } + + PUGI__FN xml_node::iterator xml_node::begin() const + { + return iterator(_root ? _root->first_child + 0 : 0, _root); + } + + PUGI__FN xml_node::iterator xml_node::end() const + { + return iterator(0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_begin() const + { + return attribute_iterator(_root ? _root->first_attribute + 0 : 0, _root); + } + + PUGI__FN xml_node::attribute_iterator xml_node::attributes_end() const + { + return attribute_iterator(0, _root); + } + + PUGI__FN xml_object_range xml_node::children() const + { + return xml_object_range(begin(), end()); + } + + PUGI__FN xml_object_range xml_node::children(const char_t* name_) const + { + return xml_object_range(xml_named_node_iterator(child(name_)._root, _root, name_), xml_named_node_iterator(0, _root, name_)); + } + + PUGI__FN xml_object_range xml_node::attributes() const + { + return xml_object_range(attributes_begin(), attributes_end()); + } + + PUGI__FN bool xml_node::operator==(const xml_node& r) const + { + return (_root == r._root); + } + + PUGI__FN bool xml_node::operator!=(const xml_node& r) const + { + return (_root != r._root); + } + + PUGI__FN bool xml_node::operator<(const xml_node& r) const + { + return (_root < r._root); + } + + PUGI__FN bool xml_node::operator>(const xml_node& r) const + { + return (_root > r._root); + } + + PUGI__FN bool xml_node::operator<=(const xml_node& r) const + { + return (_root <= r._root); + } + + PUGI__FN bool xml_node::operator>=(const xml_node& r) const + { + return (_root >= r._root); + } + + PUGI__FN bool xml_node::empty() const + { + return !_root; + } + + PUGI__FN const char_t* xml_node::name() const + { + return (_root && _root->name) ? _root->name + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node_type xml_node::type() const + { + return _root ? PUGI__NODETYPE(_root) : node_null; + } + + PUGI__FN const char_t* xml_node::value() const + { + return (_root && _root->value) ? _root->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN xml_node xml_node::child(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute_struct* i = _root->first_attribute; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + return xml_attribute(i); + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::next_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->next_sibling; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_node xml_node::next_sibling() const + { + return _root ? xml_node(_root->next_sibling) : xml_node(); + } + + PUGI__FN xml_node xml_node::previous_sibling(const char_t* name_) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->prev_sibling_c; i->next_sibling; i = i->prev_sibling_c) + if (i->name && impl::strequal(name_, i->name)) return xml_node(i); + + return xml_node(); + } + + PUGI__FN xml_attribute xml_node::attribute(const char_t* name_, xml_attribute& hint_) const + { + xml_attribute_struct* hint = hint_._attr; + + // if hint is not an attribute of node, behavior is not defined + assert(!hint || (_root && impl::is_attribute_of(hint, _root))); + + if (!_root) return xml_attribute(); + + // optimistically search from hint up until the end + for (xml_attribute_struct* i = hint; i; i = i->next_attribute) + if (i->name && impl::strequal(name_, i->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = i->next_attribute; + + return xml_attribute(i); + } + + // wrap around and search from the first attribute until the hint + // 'j' null pointer check is technically redundant, but it prevents a crash in case the assertion above fails + for (xml_attribute_struct* j = _root->first_attribute; j && j != hint; j = j->next_attribute) + if (j->name && impl::strequal(name_, j->name)) + { + // update hint to maximize efficiency of searching for consecutive attributes + hint_._attr = j->next_attribute; + + return xml_attribute(j); + } + + return xml_attribute(); + } + + PUGI__FN xml_node xml_node::previous_sibling() const + { + if (!_root) return xml_node(); + + if (_root->prev_sibling_c->next_sibling) return xml_node(_root->prev_sibling_c); + else return xml_node(); + } + + PUGI__FN xml_node xml_node::parent() const + { + return _root ? xml_node(_root->parent) : xml_node(); + } + + PUGI__FN xml_node xml_node::root() const + { + return _root ? xml_node(&impl::get_document(_root)) : xml_node(); + } + + PUGI__FN xml_text xml_node::text() const + { + return xml_text(_root); + } + + PUGI__FN const char_t* xml_node::child_value() const + { + if (!_root) return PUGIXML_TEXT(""); + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root->value; + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (impl::is_text_node(i) && i->value) + return i->value; + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_node::child_value(const char_t* name_) const + { + return child(name_).child_value(); + } + + PUGI__FN xml_attribute xml_node::first_attribute() const + { + return _root ? xml_attribute(_root->first_attribute) : xml_attribute(); + } + + PUGI__FN xml_attribute xml_node::last_attribute() const + { + return _root && _root->first_attribute ? xml_attribute(_root->first_attribute->prev_attribute_c) : xml_attribute(); + } + + PUGI__FN xml_node xml_node::first_child() const + { + return _root ? xml_node(_root->first_child) : xml_node(); + } + + PUGI__FN xml_node xml_node::last_child() const + { + return _root && _root->first_child ? xml_node(_root->first_child->prev_sibling_c) : xml_node(); + } + + PUGI__FN bool xml_node::set_name(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_element && type_ != node_pi && type_ != node_declaration) + return false; + + return impl::strcpy_insitu(_root->name, _root->header, impl::xml_memory_page_name_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN bool xml_node::set_value(const char_t* rhs) + { + xml_node_type type_ = _root ? PUGI__NODETYPE(_root) : node_null; + + if (type_ != node_pcdata && type_ != node_cdata && type_ != node_comment && type_ != node_pi && type_ != node_doctype) + return false; + + return impl::strcpy_insitu(_root->value, _root->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)); + } + + PUGI__FN xml_attribute xml_node::append_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_attribute(const char_t* name_) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_after(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_attribute_before(const char_t* name_, const xml_attribute& attr) + { + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + + a.set_name(name_); + + return a; + } + + PUGI__FN xml_attribute xml_node::append_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::append_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::prepend_copy(const xml_attribute& proto) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::prepend_attribute(a._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_after(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_after(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_attribute xml_node::insert_copy_before(const xml_attribute& proto, const xml_attribute& attr) + { + if (!proto) return xml_attribute(); + if (!impl::allow_insert_attribute(type())) return xml_attribute(); + if (!attr || !impl::is_attribute_of(attr._attr, _root)) return xml_attribute(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_attribute(); + + xml_attribute a(impl::allocate_attribute(alloc)); + if (!a) return xml_attribute(); + + impl::insert_attribute_before(a._attr, attr._attr, _root); + impl::node_copy_attribute(a._attr, proto._attr); + + return a; + } + + PUGI__FN xml_node xml_node::append_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_child(xml_node_type type_) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_before(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::insert_child_after(xml_node_type type_, const xml_node& node) + { + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + + if (type_ == node_declaration) n.set_name(PUGIXML_TEXT("xml")); + + return n; + } + + PUGI__FN xml_node xml_node::append_child(const char_t* name_) + { + xml_node result = append_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::prepend_child(const char_t* name_) + { + xml_node result = prepend_child(node_element); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_after(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_after(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::insert_child_before(const char_t* name_, const xml_node& node) + { + xml_node result = insert_child_before(node_element, node); + + result.set_name(name_); + + return result; + } + + PUGI__FN xml_node xml_node::append_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::append_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::prepend_copy(const xml_node& proto) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::prepend_node(n._root, _root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_after(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_after(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::insert_copy_before(const xml_node& proto, const xml_node& node) + { + xml_node_type type_ = proto.type(); + if (!impl::allow_insert_child(type(), type_)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + xml_node n(impl::allocate_node(alloc, type_)); + if (!n) return xml_node(); + + impl::insert_node_before(n._root, node._root); + impl::node_copy_tree(n._root, proto._root); + + return n; + } + + PUGI__FN xml_node xml_node::append_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::append_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::prepend_move(const xml_node& moved) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::prepend_node(moved._root, _root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_after(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_after(moved._root, node._root); + + return moved; + } + + PUGI__FN xml_node xml_node::insert_move_before(const xml_node& moved, const xml_node& node) + { + if (!impl::allow_move(*this, moved)) return xml_node(); + if (!node._root || node._root->parent != _root) return xml_node(); + if (moved._root == node._root) return xml_node(); + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return xml_node(); + + // disable document_buffer_order optimization since moving nodes around changes document order without changing buffer pointers + impl::get_document(_root).header |= impl::xml_memory_page_contents_shared_mask; + + impl::remove_node(moved._root); + impl::insert_node_before(moved._root, node._root); + + return moved; + } + + PUGI__FN bool xml_node::remove_attribute(const char_t* name_) + { + return remove_attribute(attribute(name_)); + } + + PUGI__FN bool xml_node::remove_attribute(const xml_attribute& a) + { + if (!_root || !a._attr) return false; + if (!impl::is_attribute_of(a._attr, _root)) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_attribute(a._attr, _root); + impl::destroy_attribute(a._attr, alloc); + + return true; + } + + PUGI__FN bool xml_node::remove_child(const char_t* name_) + { + return remove_child(child(name_)); + } + + PUGI__FN bool xml_node::remove_child(const xml_node& n) + { + if (!_root || !n._root || n._root->parent != _root) return false; + + impl::xml_allocator& alloc = impl::get_allocator(_root); + if (!alloc.reserve()) return false; + + impl::remove_node(n._root); + impl::destroy_node(n._root, alloc); + + return true; + } + + PUGI__FN xml_parse_result xml_node::append_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + // append_buffer is only valid for elements/documents + if (!impl::allow_insert_child(type(), node_element)) return impl::make_parse_result(status_append_invalid_root); + + // get document node + impl::xml_document_struct* doc = &impl::get_document(_root); + + // disable document_buffer_order optimization since in a document with multiple buffers comparing buffer pointers does not make sense + doc->header |= impl::xml_memory_page_contents_shared_mask; + + // get extra buffer element (we'll store the document fragment buffer there so that we can deallocate it later) + impl::xml_memory_page* page = 0; + impl::xml_extra_buffer* extra = static_cast(doc->allocate_memory(sizeof(impl::xml_extra_buffer) + sizeof(void*), page)); + (void)page; + + if (!extra) return impl::make_parse_result(status_out_of_memory); + + #ifdef PUGIXML_COMPACT + // align the memory block to a pointer boundary; this is required for compact mode where memory allocations are only 4b aligned + // note that this requires up to sizeof(void*)-1 additional memory, which the allocation above takes into account + extra = reinterpret_cast((reinterpret_cast(extra) + (sizeof(void*) - 1)) & ~(sizeof(void*) - 1)); + #endif + + // add extra buffer to the list + extra->buffer = 0; + extra->next = doc->extra_buffers; + doc->extra_buffers = extra; + + // name of the root has to be NULL before parsing - otherwise closing node mismatches will not be detected at the top level + impl::name_null_sentry sentry(_root); + + return impl::load_buffer_impl(doc, _root, const_cast(contents), size, options, encoding, false, false, &extra->buffer); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* name_, const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (i->name && impl::strequal(name_, i->name)) + { + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + } + + return xml_node(); + } + + PUGI__FN xml_node xml_node::find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const + { + if (!_root) return xml_node(); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + for (xml_attribute_struct* a = i->first_attribute; a; a = a->next_attribute) + if (a->name && impl::strequal(attr_name, a->name) && impl::strequal(attr_value, a->value ? a->value + 0 : PUGIXML_TEXT(""))) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xml_node::path(char_t delimiter) const + { + if (!_root) return string_t(); + + size_t offset = 0; + + for (xml_node_struct* i = _root; i; i = i->parent) + { + offset += (i != _root); + offset += i->name ? impl::strlength(i->name) : 0; + } + + string_t result; + result.resize(offset); + + for (xml_node_struct* j = _root; j; j = j->parent) + { + if (j != _root) + result[--offset] = delimiter; + + if (j->name) + { + size_t length = impl::strlength(j->name); + + offset -= length; + memcpy(&result[offset], j->name, length * sizeof(char_t)); + } + } + + assert(offset == 0); + + return result; + } +#endif + + PUGI__FN xml_node xml_node::first_element_by_path(const char_t* path_, char_t delimiter) const + { + xml_node found = *this; // Current search context. + + if (!_root || !path_[0]) return found; + + if (path_[0] == delimiter) + { + // Absolute path; e.g. '/foo/bar' + found = found.root(); + ++path_; + } + + const char_t* path_segment = path_; + + while (*path_segment == delimiter) ++path_segment; + + const char_t* path_segment_end = path_segment; + + while (*path_segment_end && *path_segment_end != delimiter) ++path_segment_end; + + if (path_segment == path_segment_end) return found; + + const char_t* next_segment = path_segment_end; + + while (*next_segment == delimiter) ++next_segment; + + if (*path_segment == '.' && path_segment + 1 == path_segment_end) + return found.first_element_by_path(next_segment, delimiter); + else if (*path_segment == '.' && *(path_segment+1) == '.' && path_segment + 2 == path_segment_end) + return found.parent().first_element_by_path(next_segment, delimiter); + else + { + for (xml_node_struct* j = found._root->first_child; j; j = j->next_sibling) + { + if (j->name && impl::strequalrange(j->name, path_segment, static_cast(path_segment_end - path_segment))) + { + xml_node subsearch = xml_node(j).first_element_by_path(next_segment, delimiter); + + if (subsearch) return subsearch; + } + } + + return xml_node(); + } + } + + PUGI__FN bool xml_node::traverse(xml_tree_walker& walker) + { + walker._depth = -1; + + xml_node arg_begin(_root); + if (!walker.begin(arg_begin)) return false; + + xml_node_struct* cur = _root ? _root->first_child + 0 : 0; + + if (cur) + { + ++walker._depth; + + do + { + xml_node arg_for_each(cur); + if (!walker.for_each(arg_for_each)) + return false; + + if (cur->first_child) + { + ++walker._depth; + cur = cur->first_child; + } + else if (cur->next_sibling) + cur = cur->next_sibling; + else + { + while (!cur->next_sibling && cur != _root && cur->parent) + { + --walker._depth; + cur = cur->parent; + } + + if (cur != _root) + cur = cur->next_sibling; + } + } + while (cur && cur != _root); + } + + assert(walker._depth == -1); + + xml_node arg_end(_root); + return walker.end(arg_end); + } + + PUGI__FN size_t xml_node::hash_value() const + { + return static_cast(reinterpret_cast(_root) / sizeof(xml_node_struct)); + } + + PUGI__FN xml_node_struct* xml_node::internal_object() const + { + return _root; + } + + PUGI__FN void xml_node::print(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + if (!_root) return; + + impl::xml_buffered_writer buffered_writer(writer, encoding); + + impl::node_output(buffered_writer, _root, indent, flags, depth); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding, depth); + } + + PUGI__FN void xml_node::print(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, unsigned int depth) const + { + xml_writer_stream writer(stream); + + print(writer, indent, flags, encoding_wchar, depth); + } +#endif + + PUGI__FN ptrdiff_t xml_node::offset_debug() const + { + if (!_root) return -1; + + impl::xml_document_struct& doc = impl::get_document(_root); + + // we can determine the offset reliably only if there is exactly once parse buffer + if (!doc.buffer || doc.extra_buffers) return -1; + + switch (type()) + { + case node_document: + return 0; + + case node_element: + case node_declaration: + case node_pi: + return _root->name && (_root->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0 ? _root->name - doc.buffer : -1; + + case node_pcdata: + case node_cdata: + case node_comment: + case node_doctype: + return _root->value && (_root->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0 ? _root->value - doc.buffer : -1; + + default: + assert(false && "Invalid node type"); // unreachable + return -1; + } + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_text::xml_text(xml_node_struct* root): _root(root) + { + } + + PUGI__FN xml_node_struct* xml_text::_data() const + { + if (!_root || impl::is_text_node(_root)) return _root; + + // element nodes can have value if parse_embed_pcdata was used + if (PUGI__NODETYPE(_root) == node_element && _root->value) + return _root; + + for (xml_node_struct* node = _root->first_child; node; node = node->next_sibling) + if (impl::is_text_node(node)) + return node; + + return 0; + } + + PUGI__FN xml_node_struct* xml_text::_data_new() + { + xml_node_struct* d = _data(); + if (d) return d; + + return xml_node(_root).append_child(node_pcdata).internal_object(); + } + + PUGI__FN xml_text::xml_text(): _root(0) + { + } + + PUGI__FN static void unspecified_bool_xml_text(xml_text***) + { + } + + PUGI__FN xml_text::operator xml_text::unspecified_bool_type() const + { + return _data() ? unspecified_bool_xml_text : 0; + } + + PUGI__FN bool xml_text::operator!() const + { + return !_data(); + } + + PUGI__FN bool xml_text::empty() const + { + return _data() == 0; + } + + PUGI__FN const char_t* xml_text::get() const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* xml_text::as_string(const char_t* def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? d->value + 0 : def; + } + + PUGI__FN int xml_text::as_int(int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_int(d->value) : def; + } + + PUGI__FN unsigned int xml_text::as_uint(unsigned int def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_uint(d->value) : def; + } + + PUGI__FN double xml_text::as_double(double def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_double(d->value) : def; + } + + PUGI__FN float xml_text::as_float(float def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_float(d->value) : def; + } + + PUGI__FN bool xml_text::as_bool(bool def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_bool(d->value) : def; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN long long xml_text::as_llong(long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_llong(d->value) : def; + } + + PUGI__FN unsigned long long xml_text::as_ullong(unsigned long long def) const + { + xml_node_struct* d = _data(); + + return (d && d->value) ? impl::get_value_ullong(d->value) : def; + } +#endif + + PUGI__FN bool xml_text::set(const char_t* rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::strcpy_insitu(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, impl::strlength(rhs)) : false; + } + + PUGI__FN bool xml_text::set(int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned int rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } + + PUGI__FN bool xml_text::set(float rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(double rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_convert(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + + PUGI__FN bool xml_text::set(bool rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_bool(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs) : false; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN bool xml_text::set(long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, rhs < 0) : false; + } + + PUGI__FN bool xml_text::set(unsigned long long rhs) + { + xml_node_struct* dn = _data_new(); + + return dn ? impl::set_value_integer(dn->value, dn->header, impl::xml_memory_page_value_allocated_mask, rhs, false) : false; + } +#endif + + PUGI__FN xml_text& xml_text::operator=(const char_t* rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned int rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(double rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(float rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(bool rhs) + { + set(rhs); + return *this; + } + +#ifdef PUGIXML_HAS_LONG_LONG + PUGI__FN xml_text& xml_text::operator=(long long rhs) + { + set(rhs); + return *this; + } + + PUGI__FN xml_text& xml_text::operator=(unsigned long long rhs) + { + set(rhs); + return *this; + } +#endif + + PUGI__FN xml_node xml_text::data() const + { + return xml_node(_data()); + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xml_text& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xml_text& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN xml_node_iterator::xml_node_iterator() + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(const xml_node& node): _wrap(node), _parent(node.parent()) + { + } + + PUGI__FN xml_node_iterator::xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_node_iterator::operator==(const xml_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_node_iterator::operator!=(const xml_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator++() + { + assert(_wrap._root); + _wrap._root = _wrap._root->next_sibling; + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator++(int) + { + xml_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_node_iterator& xml_node_iterator::operator--() + { + _wrap = _wrap._root ? _wrap.previous_sibling() : _parent.last_child(); + return *this; + } + + PUGI__FN xml_node_iterator xml_node_iterator::operator--(int) + { + xml_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator() + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent): _wrap(attr), _parent(parent) + { + } + + PUGI__FN xml_attribute_iterator::xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent): _wrap(ref), _parent(parent) + { + } + + PUGI__FN bool xml_attribute_iterator::operator==(const xml_attribute_iterator& rhs) const + { + return _wrap._attr == rhs._wrap._attr && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_attribute_iterator::operator!=(const xml_attribute_iterator& rhs) const + { + return _wrap._attr != rhs._wrap._attr || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_attribute& xml_attribute_iterator::operator*() const + { + assert(_wrap._attr); + return _wrap; + } + + PUGI__FN xml_attribute* xml_attribute_iterator::operator->() const + { + assert(_wrap._attr); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator++() + { + assert(_wrap._attr); + _wrap._attr = _wrap._attr->next_attribute; + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator++(int) + { + xml_attribute_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_attribute_iterator& xml_attribute_iterator::operator--() + { + _wrap = _wrap._attr ? _wrap.previous_attribute() : _parent.last_attribute(); + return *this; + } + + PUGI__FN xml_attribute_iterator xml_attribute_iterator::operator--(int) + { + xml_attribute_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(): _name(0) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(const xml_node& node, const char_t* name): _wrap(node), _parent(node.parent()), _name(name) + { + } + + PUGI__FN xml_named_node_iterator::xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name): _wrap(ref), _parent(parent), _name(name) + { + } + + PUGI__FN bool xml_named_node_iterator::operator==(const xml_named_node_iterator& rhs) const + { + return _wrap._root == rhs._wrap._root && _parent._root == rhs._parent._root; + } + + PUGI__FN bool xml_named_node_iterator::operator!=(const xml_named_node_iterator& rhs) const + { + return _wrap._root != rhs._wrap._root || _parent._root != rhs._parent._root; + } + + PUGI__FN xml_node& xml_named_node_iterator::operator*() const + { + assert(_wrap._root); + return _wrap; + } + + PUGI__FN xml_node* xml_named_node_iterator::operator->() const + { + assert(_wrap._root); + return const_cast(&_wrap); // BCC5 workaround + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator++() + { + assert(_wrap._root); + _wrap = _wrap.next_sibling(_name); + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator++(int) + { + xml_named_node_iterator temp = *this; + ++*this; + return temp; + } + + PUGI__FN const xml_named_node_iterator& xml_named_node_iterator::operator--() + { + if (_wrap._root) + _wrap = _wrap.previous_sibling(_name); + else + { + _wrap = _parent.last_child(); + + if (!impl::strequal(_wrap.name(), _name)) + _wrap = _wrap.previous_sibling(_name); + } + + return *this; + } + + PUGI__FN xml_named_node_iterator xml_named_node_iterator::operator--(int) + { + xml_named_node_iterator temp = *this; + --*this; + return temp; + } + + PUGI__FN xml_parse_result::xml_parse_result(): status(status_internal_error), offset(0), encoding(encoding_auto) + { + } + + PUGI__FN xml_parse_result::operator bool() const + { + return status == status_ok; + } + + PUGI__FN const char* xml_parse_result::description() const + { + switch (status) + { + case status_ok: return "No error"; + + case status_file_not_found: return "File was not found"; + case status_io_error: return "Error reading from file/stream"; + case status_out_of_memory: return "Could not allocate memory"; + case status_internal_error: return "Internal error occurred"; + + case status_unrecognized_tag: return "Could not determine tag type"; + + case status_bad_pi: return "Error parsing document declaration/processing instruction"; + case status_bad_comment: return "Error parsing comment"; + case status_bad_cdata: return "Error parsing CDATA section"; + case status_bad_doctype: return "Error parsing document type declaration"; + case status_bad_pcdata: return "Error parsing PCDATA section"; + case status_bad_start_element: return "Error parsing start element tag"; + case status_bad_attribute: return "Error parsing element attribute"; + case status_bad_end_element: return "Error parsing end element tag"; + case status_end_element_mismatch: return "Start-end tags mismatch"; + + case status_append_invalid_root: return "Unable to append nodes: root is not an element or document"; + + case status_no_document_element: return "No document element found"; + + default: return "Unknown error"; + } + } + + PUGI__FN xml_document::xml_document(): _buffer(0) + { + _create(); + } + + PUGI__FN xml_document::~xml_document() + { + _destroy(); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xml_document::xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT: _buffer(0) + { + _create(); + _move(rhs); + } + + PUGI__FN xml_document& xml_document::operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + if (this == &rhs) return *this; + + _destroy(); + _create(); + _move(rhs); + + return *this; + } +#endif + + PUGI__FN void xml_document::reset() + { + _destroy(); + _create(); + } + + PUGI__FN void xml_document::reset(const xml_document& proto) + { + reset(); + + for (xml_node cur = proto.first_child(); cur; cur = cur.next_sibling()) + append_copy(cur); + } + + PUGI__FN void xml_document::_create() + { + assert(!_root); + + #ifdef PUGIXML_COMPACT + // space for page marker for the first page (uint32_t), rounded up to pointer size; assumes pointers are at least 32-bit + const size_t page_offset = sizeof(void*); + #else + const size_t page_offset = 0; + #endif + + // initialize sentinel page + PUGI__STATIC_ASSERT(sizeof(impl::xml_memory_page) + sizeof(impl::xml_document_struct) + page_offset <= sizeof(_memory)); + + // prepare page structure + impl::xml_memory_page* page = impl::xml_memory_page::construct(_memory); + assert(page); + + page->busy_size = impl::xml_memory_page_size; + + // setup first page marker + #ifdef PUGIXML_COMPACT + // round-trip through void* to avoid 'cast increases required alignment of target type' warning + page->compact_page_marker = reinterpret_cast(static_cast(reinterpret_cast(page) + sizeof(impl::xml_memory_page))); + *page->compact_page_marker = sizeof(impl::xml_memory_page); + #endif + + // allocate new root + _root = new (reinterpret_cast(page) + sizeof(impl::xml_memory_page) + page_offset) impl::xml_document_struct(page); + _root->prev_sibling_c = _root; + + // setup sentinel page + page->allocator = static_cast(_root); + + // setup hash table pointer in allocator + #ifdef PUGIXML_COMPACT + page->allocator->_hash = &static_cast(_root)->hash; + #endif + + // verify the document allocation + assert(reinterpret_cast(_root) + sizeof(impl::xml_document_struct) <= _memory + sizeof(_memory)); + } + + PUGI__FN void xml_document::_destroy() + { + assert(_root); + + // destroy static storage + if (_buffer) + { + impl::xml_memory::deallocate(_buffer); + _buffer = 0; + } + + // destroy extra buffers (note: no need to destroy linked list nodes, they're allocated using document allocator) + for (impl::xml_extra_buffer* extra = static_cast(_root)->extra_buffers; extra; extra = extra->next) + { + if (extra->buffer) impl::xml_memory::deallocate(extra->buffer); + } + + // destroy dynamic storage, leave sentinel page (it's in static memory) + impl::xml_memory_page* root_page = PUGI__GETPAGE(_root); + assert(root_page && !root_page->prev); + assert(reinterpret_cast(root_page) >= _memory && reinterpret_cast(root_page) < _memory + sizeof(_memory)); + + for (impl::xml_memory_page* page = root_page->next; page; ) + { + impl::xml_memory_page* next = page->next; + + impl::xml_allocator::deallocate_page(page); + + page = next; + } + + #ifdef PUGIXML_COMPACT + // destroy hash table + static_cast(_root)->hash.clear(); + #endif + + _root = 0; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xml_document::_move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT + { + impl::xml_document_struct* doc = static_cast(_root); + impl::xml_document_struct* other = static_cast(rhs._root); + + // save first child pointer for later; this needs hash access + xml_node_struct* other_first_child = other->first_child; + + #ifdef PUGIXML_COMPACT + // reserve space for the hash table up front; this is the only operation that can fail + // if it does, we have no choice but to throw (if we have exceptions) + if (other_first_child) + { + size_t other_children = 0; + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + other_children++; + + // in compact mode, each pointer assignment could result in a hash table request + // during move, we have to relocate document first_child and parents of all children + // normally there's just one child and its parent has a pointerless encoding but + // we assume the worst here + if (!other->_hash->reserve(other_children + 1)) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + } + #endif + + // move allocation state + doc->_root = other->_root; + doc->_busy_size = other->_busy_size; + + // move buffer state + doc->buffer = other->buffer; + doc->extra_buffers = other->extra_buffers; + _buffer = rhs._buffer; + + #ifdef PUGIXML_COMPACT + // move compact hash; note that the hash table can have pointers to other but they will be "inactive", similarly to nodes removed with remove_child + doc->hash = other->hash; + doc->_hash = &doc->hash; + + // make sure we don't access other hash up until the end when we reinitialize other document + other->_hash = 0; + #endif + + // move page structure + impl::xml_memory_page* doc_page = PUGI__GETPAGE(doc); + assert(doc_page && !doc_page->prev && !doc_page->next); + + impl::xml_memory_page* other_page = PUGI__GETPAGE(other); + assert(other_page && !other_page->prev); + + // relink pages since root page is embedded into xml_document + if (impl::xml_memory_page* page = other_page->next) + { + assert(page->prev == other_page); + + page->prev = doc_page; + + doc_page->next = page; + other_page->next = 0; + } + + // make sure pages point to the correct document state + for (impl::xml_memory_page* page = doc_page->next; page; page = page->next) + { + assert(page->allocator == other); + + page->allocator = doc; + + #ifdef PUGIXML_COMPACT + // this automatically migrates most children between documents and prevents ->parent assignment from allocating + if (page->compact_shared_parent == other) + page->compact_shared_parent = doc; + #endif + } + + // move tree structure + assert(!doc->first_child); + + doc->first_child = other_first_child; + + for (xml_node_struct* node = other_first_child; node; node = node->next_sibling) + { + #ifdef PUGIXML_COMPACT + // most children will have migrated when we reassigned compact_shared_parent + assert(node->parent == other || node->parent == doc); + + node->parent = doc; + #else + assert(node->parent == other); + node->parent = doc; + #endif + } + + // reset other document + new (other) impl::xml_document_struct(PUGI__GETPAGE(other)); + rhs._buffer = 0; + } +#endif + +#ifndef PUGIXML_NO_STL + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load(std::basic_istream >& stream, unsigned int options) + { + reset(); + + return impl::load_stream_impl(static_cast(_root), stream, options, encoding_wchar, &_buffer); + } +#endif + + PUGI__FN xml_parse_result xml_document::load_string(const char_t* contents, unsigned int options) + { + // Force native encoding (skip autodetection) + #ifdef PUGIXML_WCHAR_MODE + xml_encoding encoding = encoding_wchar; + #else + xml_encoding encoding = encoding_utf8; + #endif + + return load_buffer(contents, impl::strlength(contents) * sizeof(char_t), options, encoding); + } + + PUGI__FN xml_parse_result xml_document::load(const char_t* contents, unsigned int options) + { + return load_string(contents, options); + } + + PUGI__FN xml_parse_result xml_document::load_file(const char* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, "rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_file(const wchar_t* path_, unsigned int options, xml_encoding encoding) + { + reset(); + + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, L"rb"), impl::close_file); + + return impl::load_file_impl(static_cast(_root), file.data, options, encoding, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer(const void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, const_cast(contents), size, options, encoding, false, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, false, &_buffer); + } + + PUGI__FN xml_parse_result xml_document::load_buffer_inplace_own(void* contents, size_t size, unsigned int options, xml_encoding encoding) + { + reset(); + + return impl::load_buffer_impl(static_cast(_root), _root, contents, size, options, encoding, true, true, &_buffer); + } + + PUGI__FN void xml_document::save(xml_writer& writer, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + impl::xml_buffered_writer buffered_writer(writer, encoding); + + if ((flags & format_write_bom) && encoding != encoding_latin1) + { + // BOM always represents the codepoint U+FEFF, so just write it in native encoding + #ifdef PUGIXML_WCHAR_MODE + unsigned int bom = 0xfeff; + buffered_writer.write(static_cast(bom)); + #else + buffered_writer.write('\xef', '\xbb', '\xbf'); + #endif + } + + if (!(flags & format_no_declaration) && !impl::has_declaration(_root)) + { + buffered_writer.write_string(PUGIXML_TEXT("'); + if (!(flags & format_raw)) buffered_writer.write('\n'); + } + + impl::node_output(buffered_writer, _root, indent, flags, 0); + + buffered_writer.flush(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding); + } + + PUGI__FN void xml_document::save(std::basic_ostream >& stream, const char_t* indent, unsigned int flags) const + { + xml_writer_stream writer(stream); + + save(writer, indent, flags, encoding_wchar); + } +#endif + + PUGI__FN bool xml_document::save_file(const char* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(fopen(path_, (flags & format_save_file_text) ? "w" : "wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN bool xml_document::save_file(const wchar_t* path_, const char_t* indent, unsigned int flags, xml_encoding encoding) const + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter file(impl::open_file_wide(path_, (flags & format_save_file_text) ? L"w" : L"wb"), impl::close_file); + + return impl::save_file_impl(*this, file.data, indent, flags, encoding); + } + + PUGI__FN xml_node xml_document::document_element() const + { + assert(_root); + + for (xml_node_struct* i = _root->first_child; i; i = i->next_sibling) + if (PUGI__NODETYPE(i) == node_element) + return xml_node(i); + + return xml_node(); + } + +#ifndef PUGIXML_NO_STL + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const wchar_t* str) + { + assert(str); + + return impl::as_utf8_impl(str, impl::strlength_wide(str)); + } + + PUGI__FN std::string PUGIXML_FUNCTION as_utf8(const std::basic_string& str) + { + return impl::as_utf8_impl(str.c_str(), str.size()); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const char* str) + { + assert(str); + + return impl::as_wide_impl(str, strlen(str)); + } + + PUGI__FN std::basic_string PUGIXML_FUNCTION as_wide(const std::string& str) + { + return impl::as_wide_impl(str.c_str(), str.size()); + } +#endif + + PUGI__FN void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate) + { + impl::xml_memory::allocate = allocate; + impl::xml_memory::deallocate = deallocate; + } + + PUGI__FN allocation_function PUGIXML_FUNCTION get_memory_allocation_function() + { + return impl::xml_memory::allocate; + } + + PUGI__FN deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function() + { + return impl::xml_memory::deallocate; + } +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag _Iter_cat(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_attribute_iterator&) + { + return std::bidirectional_iterator_tag(); + } + + PUGI__FN std::bidirectional_iterator_tag __iterator_category(const pugi::xml_named_node_iterator&) + { + return std::bidirectional_iterator_tag(); + } +} +#endif + +#ifndef PUGIXML_NO_XPATH +// STL replacements +PUGI__NS_BEGIN + struct equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs == rhs; + } + }; + + struct not_equal_to + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs != rhs; + } + }; + + struct less + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs < rhs; + } + }; + + struct less_equal + { + template bool operator()(const T& lhs, const T& rhs) const + { + return lhs <= rhs; + } + }; + + template void swap(T& lhs, T& rhs) + { + T temp = lhs; + lhs = rhs; + rhs = temp; + } + + template I min_element(I begin, I end, const Pred& pred) + { + I result = begin; + + for (I it = begin + 1; it != end; ++it) + if (pred(*it, *result)) + result = it; + + return result; + } + + template void reverse(I begin, I end) + { + while (end - begin > 1) swap(*begin++, *--end); + } + + template I unique(I begin, I end) + { + // fast skip head + while (end - begin > 1 && *begin != *(begin + 1)) begin++; + + if (begin == end) return begin; + + // last written element + I write = begin++; + + // merge unique elements + while (begin != end) + { + if (*begin != *write) + *++write = *begin++; + else + begin++; + } + + // past-the-end (write points to live element) + return write + 1; + } + + template void insertion_sort(T* begin, T* end, const Pred& pred) + { + if (begin == end) + return; + + for (T* it = begin + 1; it != end; ++it) + { + T val = *it; + T* hole = it; + + // move hole backwards + while (hole > begin && pred(val, *(hole - 1))) + { + *hole = *(hole - 1); + hole--; + } + + // fill hole with element + *hole = val; + } + } + + template I median3(I first, I middle, I last, const Pred& pred) + { + if (pred(*middle, *first)) swap(middle, first); + if (pred(*last, *middle)) swap(last, middle); + if (pred(*middle, *first)) swap(middle, first); + + return middle; + } + + template void partition3(T* begin, T* end, T pivot, const Pred& pred, T** out_eqbeg, T** out_eqend) + { + // invariant: array is split into 4 groups: = < ? > (each variable denotes the boundary between the groups) + T* eq = begin; + T* lt = begin; + T* gt = end; + + while (lt < gt) + { + if (pred(*lt, pivot)) + lt++; + else if (*lt == pivot) + swap(*eq++, *lt++); + else + swap(*lt, *--gt); + } + + // we now have just 4 groups: = < >; move equal elements to the middle + T* eqbeg = gt; + + for (T* it = begin; it != eq; ++it) + swap(*it, *--eqbeg); + + *out_eqbeg = eqbeg; + *out_eqend = gt; + } + + template void sort(I begin, I end, const Pred& pred) + { + // sort large chunks + while (end - begin > 16) + { + // find median element + I middle = begin + (end - begin) / 2; + I median = median3(begin, middle, end - 1, pred); + + // partition in three chunks (< = >) + I eqbeg, eqend; + partition3(begin, end, *median, pred, &eqbeg, &eqend); + + // loop on larger half + if (eqbeg - begin > end - eqend) + { + sort(eqend, end, pred); + end = eqbeg; + } + else + { + sort(begin, eqbeg, pred); + begin = eqend; + } + } + + // insertion sort small chunk + insertion_sort(begin, end, pred); + } +PUGI__NS_END + +// Allocator used for AST and evaluation stacks +PUGI__NS_BEGIN + static const size_t xpath_memory_page_size = + #ifdef PUGIXML_MEMORY_XPATH_PAGE_SIZE + PUGIXML_MEMORY_XPATH_PAGE_SIZE + #else + 4096 + #endif + ; + + static const uintptr_t xpath_memory_block_alignment = sizeof(double) > sizeof(void*) ? sizeof(double) : sizeof(void*); + + struct xpath_memory_block + { + xpath_memory_block* next; + size_t capacity; + + union + { + char data[xpath_memory_page_size]; + double alignment; + }; + }; + + struct xpath_allocator + { + xpath_memory_block* _root; + size_t _root_size; + bool* _error; + + xpath_allocator(xpath_memory_block* root, bool* error = 0): _root(root), _root_size(0), _error(error) + { + } + + void* allocate(size_t size) + { + // round size up to block alignment boundary + size = (size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + if (_root_size + size <= _root->capacity) + { + void* buf = &_root->data[0] + _root_size; + _root_size += size; + return buf; + } + else + { + // make sure we have at least 1/4th of the page free after allocation to satisfy subsequent allocation requests + size_t block_capacity_base = sizeof(_root->data); + size_t block_capacity_req = size + block_capacity_base / 4; + size_t block_capacity = (block_capacity_base > block_capacity_req) ? block_capacity_base : block_capacity_req; + + size_t block_size = block_capacity + offsetof(xpath_memory_block, data); + + xpath_memory_block* block = static_cast(xml_memory::allocate(block_size)); + if (!block) + { + if (_error) *_error = true; + return 0; + } + + block->next = _root; + block->capacity = block_capacity; + + _root = block; + _root_size = size; + + return block->data; + } + } + + void* reallocate(void* ptr, size_t old_size, size_t new_size) + { + // round size up to block alignment boundary + old_size = (old_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + new_size = (new_size + xpath_memory_block_alignment - 1) & ~(xpath_memory_block_alignment - 1); + + // we can only reallocate the last object + assert(ptr == 0 || static_cast(ptr) + old_size == &_root->data[0] + _root_size); + + // try to reallocate the object inplace + if (ptr && _root_size - old_size + new_size <= _root->capacity) + { + _root_size = _root_size - old_size + new_size; + return ptr; + } + + // allocate a new block + void* result = allocate(new_size); + if (!result) return 0; + + // we have a new block + if (ptr) + { + // copy old data (we only support growing) + assert(new_size >= old_size); + memcpy(result, ptr, old_size); + + // free the previous page if it had no other objects + assert(_root->data == result); + assert(_root->next); + + if (_root->next->data == ptr) + { + // deallocate the whole page, unless it was the first one + xpath_memory_block* next = _root->next->next; + + if (next) + { + xml_memory::deallocate(_root->next); + _root->next = next; + } + } + } + + return result; + } + + void revert(const xpath_allocator& state) + { + // free all new pages + xpath_memory_block* cur = _root; + + while (cur != state._root) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + + // restore state + _root = state._root; + _root_size = state._root_size; + } + + void release() + { + xpath_memory_block* cur = _root; + assert(cur); + + while (cur->next) + { + xpath_memory_block* next = cur->next; + + xml_memory::deallocate(cur); + + cur = next; + } + } + }; + + struct xpath_allocator_capture + { + xpath_allocator_capture(xpath_allocator* alloc): _target(alloc), _state(*alloc) + { + } + + ~xpath_allocator_capture() + { + _target->revert(_state); + } + + xpath_allocator* _target; + xpath_allocator _state; + }; + + struct xpath_stack + { + xpath_allocator* result; + xpath_allocator* temp; + }; + + struct xpath_stack_data + { + xpath_memory_block blocks[2]; + xpath_allocator result; + xpath_allocator temp; + xpath_stack stack; + bool oom; + + xpath_stack_data(): result(blocks + 0, &oom), temp(blocks + 1, &oom), oom(false) + { + blocks[0].next = blocks[1].next = 0; + blocks[0].capacity = blocks[1].capacity = sizeof(blocks[0].data); + + stack.result = &result; + stack.temp = &temp; + } + + ~xpath_stack_data() + { + result.release(); + temp.release(); + } + }; +PUGI__NS_END + +// String class +PUGI__NS_BEGIN + class xpath_string + { + const char_t* _buffer; + bool _uses_heap; + size_t _length_heap; + + static char_t* duplicate_string(const char_t* string, size_t length, xpath_allocator* alloc) + { + char_t* result = static_cast(alloc->allocate((length + 1) * sizeof(char_t))); + if (!result) return 0; + + memcpy(result, string, length * sizeof(char_t)); + result[length] = 0; + + return result; + } + + xpath_string(const char_t* buffer, bool uses_heap_, size_t length_heap): _buffer(buffer), _uses_heap(uses_heap_), _length_heap(length_heap) + { + } + + public: + static xpath_string from_const(const char_t* str) + { + return xpath_string(str, false, 0); + } + + static xpath_string from_heap_preallocated(const char_t* begin, const char_t* end) + { + assert(begin <= end && *end == 0); + + return xpath_string(begin, true, static_cast(end - begin)); + } + + static xpath_string from_heap(const char_t* begin, const char_t* end, xpath_allocator* alloc) + { + assert(begin <= end); + + if (begin == end) + return xpath_string(); + + size_t length = static_cast(end - begin); + const char_t* data = duplicate_string(begin, length, alloc); + + return data ? xpath_string(data, true, length) : xpath_string(); + } + + xpath_string(): _buffer(PUGIXML_TEXT("")), _uses_heap(false), _length_heap(0) + { + } + + void append(const xpath_string& o, xpath_allocator* alloc) + { + // skip empty sources + if (!*o._buffer) return; + + // fast append for constant empty target and constant source + if (!*_buffer && !_uses_heap && !o._uses_heap) + { + _buffer = o._buffer; + } + else + { + // need to make heap copy + size_t target_length = length(); + size_t source_length = o.length(); + size_t result_length = target_length + source_length; + + // allocate new buffer + char_t* result = static_cast(alloc->reallocate(_uses_heap ? const_cast(_buffer) : 0, (target_length + 1) * sizeof(char_t), (result_length + 1) * sizeof(char_t))); + if (!result) return; + + // append first string to the new buffer in case there was no reallocation + if (!_uses_heap) memcpy(result, _buffer, target_length * sizeof(char_t)); + + // append second string to the new buffer + memcpy(result + target_length, o._buffer, source_length * sizeof(char_t)); + result[result_length] = 0; + + // finalize + _buffer = result; + _uses_heap = true; + _length_heap = result_length; + } + } + + const char_t* c_str() const + { + return _buffer; + } + + size_t length() const + { + return _uses_heap ? _length_heap : strlength(_buffer); + } + + char_t* data(xpath_allocator* alloc) + { + // make private heap copy + if (!_uses_heap) + { + size_t length_ = strlength(_buffer); + const char_t* data_ = duplicate_string(_buffer, length_, alloc); + + if (!data_) return 0; + + _buffer = data_; + _uses_heap = true; + _length_heap = length_; + } + + return const_cast(_buffer); + } + + bool empty() const + { + return *_buffer == 0; + } + + bool operator==(const xpath_string& o) const + { + return strequal(_buffer, o._buffer); + } + + bool operator!=(const xpath_string& o) const + { + return !strequal(_buffer, o._buffer); + } + + bool uses_heap() const + { + return _uses_heap; + } + }; +PUGI__NS_END + +PUGI__NS_BEGIN + PUGI__FN bool starts_with(const char_t* string, const char_t* pattern) + { + while (*pattern && *string == *pattern) + { + string++; + pattern++; + } + + return *pattern == 0; + } + + PUGI__FN const char_t* find_char(const char_t* s, char_t c) + { + #ifdef PUGIXML_WCHAR_MODE + return wcschr(s, c); + #else + return strchr(s, c); + #endif + } + + PUGI__FN const char_t* find_substring(const char_t* s, const char_t* p) + { + #ifdef PUGIXML_WCHAR_MODE + // MSVC6 wcsstr bug workaround (if s is empty it always returns 0) + return (*p == 0) ? s : wcsstr(s, p); + #else + return strstr(s, p); + #endif + } + + // Converts symbol to lower case, if it is an ASCII one + PUGI__FN char_t tolower_ascii(char_t ch) + { + return static_cast(ch - 'A') < 26 ? static_cast(ch | ' ') : ch; + } + + PUGI__FN xpath_string string_value(const xpath_node& na, xpath_allocator* alloc) + { + if (na.attribute()) + return xpath_string::from_const(na.attribute().value()); + else + { + xml_node n = na.node(); + + switch (n.type()) + { + case node_pcdata: + case node_cdata: + case node_comment: + case node_pi: + return xpath_string::from_const(n.value()); + + case node_document: + case node_element: + { + xpath_string result; + + // element nodes can have value if parse_embed_pcdata was used + if (n.value()[0]) + result.append(xpath_string::from_const(n.value()), alloc); + + xml_node cur = n.first_child(); + + while (cur && cur != n) + { + if (cur.type() == node_pcdata || cur.type() == node_cdata) + result.append(xpath_string::from_const(cur.value()), alloc); + + if (cur.first_child()) + cur = cur.first_child(); + else if (cur.next_sibling()) + cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur != n) + cur = cur.parent(); + + if (cur != n) cur = cur.next_sibling(); + } + } + + return result; + } + + default: + return xpath_string(); + } + } + } + + PUGI__FN bool node_is_before_sibling(xml_node_struct* ln, xml_node_struct* rn) + { + assert(ln->parent == rn->parent); + + // there is no common ancestor (the shared parent is null), nodes are from different documents + if (!ln->parent) return ln < rn; + + // determine sibling order + xml_node_struct* ls = ln; + xml_node_struct* rs = rn; + + while (ls && rs) + { + if (ls == rn) return true; + if (rs == ln) return false; + + ls = ls->next_sibling; + rs = rs->next_sibling; + } + + // if rn sibling chain ended ln must be before rn + return !rs; + } + + PUGI__FN bool node_is_before(xml_node_struct* ln, xml_node_struct* rn) + { + // find common ancestor at the same depth, if any + xml_node_struct* lp = ln; + xml_node_struct* rp = rn; + + while (lp && rp && lp->parent != rp->parent) + { + lp = lp->parent; + rp = rp->parent; + } + + // parents are the same! + if (lp && rp) return node_is_before_sibling(lp, rp); + + // nodes are at different depths, need to normalize heights + bool left_higher = !lp; + + while (lp) + { + lp = lp->parent; + ln = ln->parent; + } + + while (rp) + { + rp = rp->parent; + rn = rn->parent; + } + + // one node is the ancestor of the other + if (ln == rn) return left_higher; + + // find common ancestor... again + while (ln->parent != rn->parent) + { + ln = ln->parent; + rn = rn->parent; + } + + return node_is_before_sibling(ln, rn); + } + + PUGI__FN bool node_is_ancestor(xml_node_struct* parent, xml_node_struct* node) + { + while (node && node != parent) node = node->parent; + + return parent && node == parent; + } + + PUGI__FN const void* document_buffer_order(const xpath_node& xnode) + { + xml_node_struct* node = xnode.node().internal_object(); + + if (node) + { + if ((get_document(node).header & xml_memory_page_contents_shared_mask) == 0) + { + if (node->name && (node->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return node->name; + if (node->value && (node->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return node->value; + } + + return 0; + } + + xml_attribute_struct* attr = xnode.attribute().internal_object(); + + if (attr) + { + if ((get_document(attr).header & xml_memory_page_contents_shared_mask) == 0) + { + if ((attr->header & impl::xml_memory_page_name_allocated_or_shared_mask) == 0) return attr->name; + if ((attr->header & impl::xml_memory_page_value_allocated_or_shared_mask) == 0) return attr->value; + } + + return 0; + } + + return 0; + } + + struct document_order_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + // optimized document order based check + const void* lo = document_buffer_order(lhs); + const void* ro = document_buffer_order(rhs); + + if (lo && ro) return lo < ro; + + // slow comparison + xml_node ln = lhs.node(), rn = rhs.node(); + + // compare attributes + if (lhs.attribute() && rhs.attribute()) + { + // shared parent + if (lhs.parent() == rhs.parent()) + { + // determine sibling order + for (xml_attribute a = lhs.attribute(); a; a = a.next_attribute()) + if (a == rhs.attribute()) + return true; + + return false; + } + + // compare attribute parents + ln = lhs.parent(); + rn = rhs.parent(); + } + else if (lhs.attribute()) + { + // attributes go after the parent element + if (lhs.parent() == rhs.node()) return false; + + ln = lhs.parent(); + } + else if (rhs.attribute()) + { + // attributes go after the parent element + if (rhs.parent() == lhs.node()) return true; + + rn = rhs.parent(); + } + + if (ln == rn) return false; + + if (!ln || !rn) return ln < rn; + + return node_is_before(ln.internal_object(), rn.internal_object()); + } + }; + + struct duplicate_comparator + { + bool operator()(const xpath_node& lhs, const xpath_node& rhs) const + { + if (lhs.attribute()) return rhs.attribute() ? lhs.attribute() < rhs.attribute() : true; + else return rhs.attribute() ? false : lhs.node() < rhs.node(); + } + }; + + PUGI__FN double gen_nan() + { + #if defined(__STDC_IEC_559__) || ((FLT_RADIX - 0 == 2) && (FLT_MAX_EXP - 0 == 128) && (FLT_MANT_DIG - 0 == 24)) + PUGI__STATIC_ASSERT(sizeof(float) == sizeof(uint32_t)); + typedef uint32_t UI; // BCC5 workaround + union { float f; UI i; } u; + u.i = 0x7fc00000; + return u.f; + #else + // fallback + const volatile double zero = 0.0; + return zero / zero; + #endif + } + + PUGI__FN bool is_nan(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + return !!_isnan(value); + #elif defined(fpclassify) && defined(FP_NAN) + return fpclassify(value) == FP_NAN; + #else + // fallback + const volatile double v = value; + return v != v; + #endif + } + + PUGI__FN const char_t* convert_number_to_string_special(double value) + { + #if defined(PUGI__MSVC_CRT_VERSION) || defined(__BORLANDC__) + if (_finite(value)) return (value == 0) ? PUGIXML_TEXT("0") : 0; + if (_isnan(value)) return PUGIXML_TEXT("NaN"); + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + #elif defined(fpclassify) && defined(FP_NAN) && defined(FP_INFINITE) && defined(FP_ZERO) + switch (fpclassify(value)) + { + case FP_NAN: + return PUGIXML_TEXT("NaN"); + + case FP_INFINITE: + return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + + case FP_ZERO: + return PUGIXML_TEXT("0"); + + default: + return 0; + } + #else + // fallback + const volatile double v = value; + + if (v == 0) return PUGIXML_TEXT("0"); + if (v != v) return PUGIXML_TEXT("NaN"); + if (v * 2 == v) return value > 0 ? PUGIXML_TEXT("Infinity") : PUGIXML_TEXT("-Infinity"); + return 0; + #endif + } + + PUGI__FN bool convert_number_to_boolean(double value) + { + return (value != 0 && !is_nan(value)); + } + + PUGI__FN void truncate_zeros(char* begin, char* end) + { + while (begin != end && end[-1] == '0') end--; + + *end = 0; + } + + // gets mantissa digits in the form of 0.xxxxx with 0. implied and the exponent +#if defined(PUGI__MSVC_CRT_VERSION) && PUGI__MSVC_CRT_VERSION >= 1400 && !defined(_WIN32_WCE) + PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) + { + // get base values + int sign, exponent; + _ecvt_s(buffer, sizeof(buffer), value, DBL_DIG + 1, &exponent, &sign); + + // truncate redundant zeros + truncate_zeros(buffer, buffer + strlen(buffer)); + + // fill results + *out_mantissa = buffer; + *out_exponent = exponent; + } +#else + PUGI__FN void convert_number_to_mantissa_exponent(double value, char (&buffer)[32], char** out_mantissa, int* out_exponent) + { + // get a scientific notation value with IEEE DBL_DIG decimals + PUGI__SNPRINTF(buffer, "%.*e", DBL_DIG, value); + + // get the exponent (possibly negative) + char* exponent_string = strchr(buffer, 'e'); + assert(exponent_string); + + int exponent = atoi(exponent_string + 1); + + // extract mantissa string: skip sign + char* mantissa = buffer[0] == '-' ? buffer + 1 : buffer; + assert(mantissa[0] != '0' && mantissa[1] == '.'); + + // divide mantissa by 10 to eliminate integer part + mantissa[1] = mantissa[0]; + mantissa++; + exponent++; + + // remove extra mantissa digits and zero-terminate mantissa + truncate_zeros(mantissa, exponent_string); + + // fill results + *out_mantissa = mantissa; + *out_exponent = exponent; + } +#endif + + PUGI__FN xpath_string convert_number_to_string(double value, xpath_allocator* alloc) + { + // try special number conversion + const char_t* special = convert_number_to_string_special(value); + if (special) return xpath_string::from_const(special); + + // get mantissa + exponent form + char mantissa_buffer[32]; + + char* mantissa; + int exponent; + convert_number_to_mantissa_exponent(value, mantissa_buffer, &mantissa, &exponent); + + // allocate a buffer of suitable length for the number + size_t result_size = strlen(mantissa_buffer) + (exponent > 0 ? exponent : -exponent) + 4; + char_t* result = static_cast(alloc->allocate(sizeof(char_t) * result_size)); + if (!result) return xpath_string(); + + // make the number! + char_t* s = result; + + // sign + if (value < 0) *s++ = '-'; + + // integer part + if (exponent <= 0) + { + *s++ = '0'; + } + else + { + while (exponent > 0) + { + assert(*mantissa == 0 || static_cast(*mantissa - '0') <= 9); + *s++ = *mantissa ? *mantissa++ : '0'; + exponent--; + } + } + + // fractional part + if (*mantissa) + { + // decimal point + *s++ = '.'; + + // extra zeroes from negative exponent + while (exponent < 0) + { + *s++ = '0'; + exponent++; + } + + // extra mantissa digits + while (*mantissa) + { + assert(static_cast(*mantissa - '0') <= 9); + *s++ = *mantissa++; + } + } + + // zero-terminate + assert(s < result + result_size); + *s = 0; + + return xpath_string::from_heap_preallocated(result, s); + } + + PUGI__FN bool check_string_to_number_format(const char_t* string) + { + // parse leading whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + // parse sign + if (*string == '-') ++string; + + if (!*string) return false; + + // if there is no integer part, there should be a decimal part with at least one digit + if (!PUGI__IS_CHARTYPEX(string[0], ctx_digit) && (string[0] != '.' || !PUGI__IS_CHARTYPEX(string[1], ctx_digit))) return false; + + // parse integer part + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + + // parse decimal part + if (*string == '.') + { + ++string; + + while (PUGI__IS_CHARTYPEX(*string, ctx_digit)) ++string; + } + + // parse trailing whitespace + while (PUGI__IS_CHARTYPE(*string, ct_space)) ++string; + + return *string == 0; + } + + PUGI__FN double convert_string_to_number(const char_t* string) + { + // check string format + if (!check_string_to_number_format(string)) return gen_nan(); + + // parse string + #ifdef PUGIXML_WCHAR_MODE + return wcstod(string, 0); + #else + return strtod(string, 0); + #endif + } + + PUGI__FN bool convert_string_to_number_scratch(char_t (&buffer)[32], const char_t* begin, const char_t* end, double* out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform conversion + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = convert_string_to_number(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } + + PUGI__FN double round_nearest(double value) + { + return floor(value + 0.5); + } + + PUGI__FN double round_nearest_nzero(double value) + { + // same as round_nearest, but returns -0 for [-0.5, -0] + // ceil is used to differentiate between +0 and -0 (we return -0 for [-0.5, -0] and +0 for +0) + return (value >= -0.5 && value <= 0) ? ceil(value) : floor(value + 0.5); + } + + PUGI__FN const char_t* qualified_name(const xpath_node& node) + { + return node.attribute() ? node.attribute().name() : node.node().name(); + } + + PUGI__FN const char_t* local_name(const xpath_node& node) + { + const char_t* name = qualified_name(node); + const char_t* p = find_char(name, ':'); + + return p ? p + 1 : name; + } + + struct namespace_uri_predicate + { + const char_t* prefix; + size_t prefix_length; + + namespace_uri_predicate(const char_t* name) + { + const char_t* pos = find_char(name, ':'); + + prefix = pos ? name : 0; + prefix_length = pos ? static_cast(pos - name) : 0; + } + + bool operator()(xml_attribute a) const + { + const char_t* name = a.name(); + + if (!starts_with(name, PUGIXML_TEXT("xmlns"))) return false; + + return prefix ? name[5] == ':' && strequalrange(name + 6, prefix, prefix_length) : name[5] == 0; + } + }; + + PUGI__FN const char_t* namespace_uri(xml_node node) + { + namespace_uri_predicate pred = node.name(); + + xml_node p = node; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(xml_attribute attr, xml_node parent) + { + namespace_uri_predicate pred = attr.name(); + + // Default namespace does not apply to attributes + if (!pred.prefix) return PUGIXML_TEXT(""); + + xml_node p = parent; + + while (p) + { + xml_attribute a = p.find_attribute(pred); + + if (a) return a.value(); + + p = p.parent(); + } + + return PUGIXML_TEXT(""); + } + + PUGI__FN const char_t* namespace_uri(const xpath_node& node) + { + return node.attribute() ? namespace_uri(node.attribute(), node.parent()) : namespace_uri(node.node()); + } + + PUGI__FN char_t* normalize_space(char_t* buffer) + { + char_t* write = buffer; + + for (char_t* it = buffer; *it; ) + { + char_t ch = *it++; + + if (PUGI__IS_CHARTYPE(ch, ct_space)) + { + // replace whitespace sequence with single space + while (PUGI__IS_CHARTYPE(*it, ct_space)) it++; + + // avoid leading spaces + if (write != buffer) *write++ = ' '; + } + else *write++ = ch; + } + + // remove trailing space + if (write != buffer && PUGI__IS_CHARTYPE(write[-1], ct_space)) write--; + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN char_t* translate(char_t* buffer, const char_t* from, const char_t* to, size_t to_length) + { + char_t* write = buffer; + + while (*buffer) + { + PUGI__DMC_VOLATILE char_t ch = *buffer++; + + const char_t* pos = find_char(from, ch); + + if (!pos) + *write++ = ch; // do not process + else if (static_cast(pos - from) < to_length) + *write++ = to[pos - from]; // replace + } + + // zero-terminate + *write = 0; + + return write; + } + + PUGI__FN unsigned char* translate_table_generate(xpath_allocator* alloc, const char_t* from, const char_t* to) + { + unsigned char table[128] = {0}; + + while (*from) + { + unsigned int fc = static_cast(*from); + unsigned int tc = static_cast(*to); + + if (fc >= 128 || tc >= 128) + return 0; + + // code=128 means "skip character" + if (!table[fc]) + table[fc] = static_cast(tc ? tc : 128); + + from++; + if (tc) to++; + } + + for (int i = 0; i < 128; ++i) + if (!table[i]) + table[i] = static_cast(i); + + void* result = alloc->allocate(sizeof(table)); + if (!result) return 0; + + memcpy(result, table, sizeof(table)); + + return static_cast(result); + } + + PUGI__FN char_t* translate_table(char_t* buffer, const unsigned char* table) + { + char_t* write = buffer; + + while (*buffer) + { + char_t ch = *buffer++; + unsigned int index = static_cast(ch); + + if (index < 128) + { + unsigned char code = table[index]; + + // code=128 means "skip character" (table size is 128 so 128 can be a special value) + // this code skips these characters without extra branches + *write = static_cast(code); + write += 1 - (code >> 7); + } + else + { + *write++ = ch; + } + } + + // zero-terminate + *write = 0; + + return write; + } + + inline bool is_xpath_attribute(const char_t* name) + { + return !(starts_with(name, PUGIXML_TEXT("xmlns")) && (name[5] == 0 || name[5] == ':')); + } + + struct xpath_variable_boolean: xpath_variable + { + xpath_variable_boolean(): xpath_variable(xpath_type_boolean), value(false) + { + } + + bool value; + char_t name[1]; + }; + + struct xpath_variable_number: xpath_variable + { + xpath_variable_number(): xpath_variable(xpath_type_number), value(0) + { + } + + double value; + char_t name[1]; + }; + + struct xpath_variable_string: xpath_variable + { + xpath_variable_string(): xpath_variable(xpath_type_string), value(0) + { + } + + ~xpath_variable_string() + { + if (value) xml_memory::deallocate(value); + } + + char_t* value; + char_t name[1]; + }; + + struct xpath_variable_node_set: xpath_variable + { + xpath_variable_node_set(): xpath_variable(xpath_type_node_set) + { + } + + xpath_node_set value; + char_t name[1]; + }; + + static const xpath_node_set dummy_node_set; + + PUGI__FN PUGI__UNSIGNED_OVERFLOW unsigned int hash_string(const char_t* str) + { + // Jenkins one-at-a-time hash (http://en.wikipedia.org/wiki/Jenkins_hash_function#one-at-a-time) + unsigned int result = 0; + + while (*str) + { + result += static_cast(*str++); + result += result << 10; + result ^= result >> 6; + } + + result += result << 3; + result ^= result >> 11; + result += result << 15; + + return result; + } + + template PUGI__FN T* new_xpath_variable(const char_t* name) + { + size_t length = strlength(name); + if (length == 0) return 0; // empty variable names are invalid + + // $$ we can't use offsetof(T, name) because T is non-POD, so we just allocate additional length characters + void* memory = xml_memory::allocate(sizeof(T) + length * sizeof(char_t)); + if (!memory) return 0; + + T* result = new (memory) T(); + + memcpy(result->name, name, (length + 1) * sizeof(char_t)); + + return result; + } + + PUGI__FN xpath_variable* new_xpath_variable(xpath_value_type type, const char_t* name) + { + switch (type) + { + case xpath_type_node_set: + return new_xpath_variable(name); + + case xpath_type_number: + return new_xpath_variable(name); + + case xpath_type_string: + return new_xpath_variable(name); + + case xpath_type_boolean: + return new_xpath_variable(name); + + default: + return 0; + } + } + + template PUGI__FN void delete_xpath_variable(T* var) + { + var->~T(); + xml_memory::deallocate(var); + } + + PUGI__FN void delete_xpath_variable(xpath_value_type type, xpath_variable* var) + { + switch (type) + { + case xpath_type_node_set: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_number: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_string: + delete_xpath_variable(static_cast(var)); + break; + + case xpath_type_boolean: + delete_xpath_variable(static_cast(var)); + break; + + default: + assert(false && "Invalid variable type"); // unreachable + } + } + + PUGI__FN bool copy_xpath_variable(xpath_variable* lhs, const xpath_variable* rhs) + { + switch (rhs->type()) + { + case xpath_type_node_set: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_number: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_string: + return lhs->set(static_cast(rhs)->value); + + case xpath_type_boolean: + return lhs->set(static_cast(rhs)->value); + + default: + assert(false && "Invalid variable type"); // unreachable + return false; + } + } + + PUGI__FN bool get_variable_scratch(char_t (&buffer)[32], xpath_variable_set* set, const char_t* begin, const char_t* end, xpath_variable** out_result) + { + size_t length = static_cast(end - begin); + char_t* scratch = buffer; + + if (length >= sizeof(buffer) / sizeof(buffer[0])) + { + // need to make dummy on-heap copy + scratch = static_cast(xml_memory::allocate((length + 1) * sizeof(char_t))); + if (!scratch) return false; + } + + // copy string to zero-terminated buffer and perform lookup + memcpy(scratch, begin, length * sizeof(char_t)); + scratch[length] = 0; + + *out_result = set->get(scratch); + + // free dummy buffer + if (scratch != buffer) xml_memory::deallocate(scratch); + + return true; + } +PUGI__NS_END + +// Internal node set class +PUGI__NS_BEGIN + PUGI__FN xpath_node_set::type_t xpath_get_order(const xpath_node* begin, const xpath_node* end) + { + if (end - begin < 2) + return xpath_node_set::type_sorted; + + document_order_comparator cmp; + + bool first = cmp(begin[0], begin[1]); + + for (const xpath_node* it = begin + 1; it + 1 < end; ++it) + if (cmp(it[0], it[1]) != first) + return xpath_node_set::type_unsorted; + + return first ? xpath_node_set::type_sorted : xpath_node_set::type_sorted_reverse; + } + + PUGI__FN xpath_node_set::type_t xpath_sort(xpath_node* begin, xpath_node* end, xpath_node_set::type_t type, bool rev) + { + xpath_node_set::type_t order = rev ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + if (type == xpath_node_set::type_unsorted) + { + xpath_node_set::type_t sorted = xpath_get_order(begin, end); + + if (sorted == xpath_node_set::type_unsorted) + { + sort(begin, end, document_order_comparator()); + + type = xpath_node_set::type_sorted; + } + else + type = sorted; + } + + if (type != order) reverse(begin, end); + + return order; + } + + PUGI__FN xpath_node xpath_first(const xpath_node* begin, const xpath_node* end, xpath_node_set::type_t type) + { + if (begin == end) return xpath_node(); + + switch (type) + { + case xpath_node_set::type_sorted: + return *begin; + + case xpath_node_set::type_sorted_reverse: + return *(end - 1); + + case xpath_node_set::type_unsorted: + return *min_element(begin, end, document_order_comparator()); + + default: + assert(false && "Invalid node set type"); // unreachable + return xpath_node(); + } + } + + class xpath_node_set_raw + { + xpath_node_set::type_t _type; + + xpath_node* _begin; + xpath_node* _end; + xpath_node* _eos; + + public: + xpath_node_set_raw(): _type(xpath_node_set::type_unsorted), _begin(0), _end(0), _eos(0) + { + } + + xpath_node* begin() const + { + return _begin; + } + + xpath_node* end() const + { + return _end; + } + + bool empty() const + { + return _begin == _end; + } + + size_t size() const + { + return static_cast(_end - _begin); + } + + xpath_node first() const + { + return xpath_first(_begin, _end, _type); + } + + void push_back_grow(const xpath_node& node, xpath_allocator* alloc); + + void push_back(const xpath_node& node, xpath_allocator* alloc) + { + if (_end != _eos) + *_end++ = node; + else + push_back_grow(node, alloc); + } + + void append(const xpath_node* begin_, const xpath_node* end_, xpath_allocator* alloc) + { + if (begin_ == end_) return; + + size_t size_ = static_cast(_end - _begin); + size_t capacity = static_cast(_eos - _begin); + size_t count = static_cast(end_ - begin_); + + if (size_ + count > capacity) + { + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), (size_ + count) * sizeof(xpath_node))); + if (!data) return; + + // finalize + _begin = data; + _end = data + size_; + _eos = data + size_ + count; + } + + memcpy(_end, begin_, count * sizeof(xpath_node)); + _end += count; + } + + void sort_do() + { + _type = xpath_sort(_begin, _end, _type, false); + } + + void truncate(xpath_node* pos) + { + assert(_begin <= pos && pos <= _end); + + _end = pos; + } + + void remove_duplicates() + { + if (_type == xpath_node_set::type_unsorted) + sort(_begin, _end, duplicate_comparator()); + + _end = unique(_begin, _end); + } + + xpath_node_set::type_t type() const + { + return _type; + } + + void set_type(xpath_node_set::type_t value) + { + _type = value; + } + }; + + PUGI__FN_NO_INLINE void xpath_node_set_raw::push_back_grow(const xpath_node& node, xpath_allocator* alloc) + { + size_t capacity = static_cast(_eos - _begin); + + // get new capacity (1.5x rule) + size_t new_capacity = capacity + capacity / 2 + 1; + + // reallocate the old array or allocate a new one + xpath_node* data = static_cast(alloc->reallocate(_begin, capacity * sizeof(xpath_node), new_capacity * sizeof(xpath_node))); + if (!data) return; + + // finalize + _begin = data; + _end = data + capacity; + _eos = data + new_capacity; + + // push + *_end++ = node; + } +PUGI__NS_END + +PUGI__NS_BEGIN + struct xpath_context + { + xpath_node n; + size_t position, size; + + xpath_context(const xpath_node& n_, size_t position_, size_t size_): n(n_), position(position_), size(size_) + { + } + }; + + enum lexeme_t + { + lex_none = 0, + lex_equal, + lex_not_equal, + lex_less, + lex_greater, + lex_less_or_equal, + lex_greater_or_equal, + lex_plus, + lex_minus, + lex_multiply, + lex_union, + lex_var_ref, + lex_open_brace, + lex_close_brace, + lex_quoted_string, + lex_number, + lex_slash, + lex_double_slash, + lex_open_square_brace, + lex_close_square_brace, + lex_string, + lex_comma, + lex_axis_attribute, + lex_dot, + lex_double_dot, + lex_double_colon, + lex_eof + }; + + struct xpath_lexer_string + { + const char_t* begin; + const char_t* end; + + xpath_lexer_string(): begin(0), end(0) + { + } + + bool operator==(const char_t* other) const + { + size_t length = static_cast(end - begin); + + return strequalrange(other, begin, length); + } + }; + + class xpath_lexer + { + const char_t* _cur; + const char_t* _cur_lexeme_pos; + xpath_lexer_string _cur_lexeme_contents; + + lexeme_t _cur_lexeme; + + public: + explicit xpath_lexer(const char_t* query): _cur(query) + { + next(); + } + + const char_t* state() const + { + return _cur; + } + + void next() + { + const char_t* cur = _cur; + + while (PUGI__IS_CHARTYPE(*cur, ct_space)) ++cur; + + // save lexeme position for error reporting + _cur_lexeme_pos = cur; + + switch (*cur) + { + case 0: + _cur_lexeme = lex_eof; + break; + + case '>': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_greater_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_greater; + } + break; + + case '<': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_less_or_equal; + } + else + { + cur += 1; + _cur_lexeme = lex_less; + } + break; + + case '!': + if (*(cur+1) == '=') + { + cur += 2; + _cur_lexeme = lex_not_equal; + } + else + { + _cur_lexeme = lex_none; + } + break; + + case '=': + cur += 1; + _cur_lexeme = lex_equal; + + break; + + case '+': + cur += 1; + _cur_lexeme = lex_plus; + + break; + + case '-': + cur += 1; + _cur_lexeme = lex_minus; + + break; + + case '*': + cur += 1; + _cur_lexeme = lex_multiply; + + break; + + case '|': + cur += 1; + _cur_lexeme = lex_union; + + break; + + case '$': + cur += 1; + + if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':' && PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_var_ref; + } + else + { + _cur_lexeme = lex_none; + } + + break; + + case '(': + cur += 1; + _cur_lexeme = lex_open_brace; + + break; + + case ')': + cur += 1; + _cur_lexeme = lex_close_brace; + + break; + + case '[': + cur += 1; + _cur_lexeme = lex_open_square_brace; + + break; + + case ']': + cur += 1; + _cur_lexeme = lex_close_square_brace; + + break; + + case ',': + cur += 1; + _cur_lexeme = lex_comma; + + break; + + case '/': + if (*(cur+1) == '/') + { + cur += 2; + _cur_lexeme = lex_double_slash; + } + else + { + cur += 1; + _cur_lexeme = lex_slash; + } + break; + + case '.': + if (*(cur+1) == '.') + { + cur += 2; + _cur_lexeme = lex_double_dot; + } + else if (PUGI__IS_CHARTYPEX(*(cur+1), ctx_digit)) + { + _cur_lexeme_contents.begin = cur; // . + + ++cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else + { + cur += 1; + _cur_lexeme = lex_dot; + } + break; + + case '@': + cur += 1; + _cur_lexeme = lex_axis_attribute; + + break; + + case '"': + case '\'': + { + char_t terminator = *cur; + + ++cur; + + _cur_lexeme_contents.begin = cur; + while (*cur && *cur != terminator) cur++; + _cur_lexeme_contents.end = cur; + + if (!*cur) + _cur_lexeme = lex_none; + else + { + cur += 1; + _cur_lexeme = lex_quoted_string; + } + + break; + } + + case ':': + if (*(cur+1) == ':') + { + cur += 2; + _cur_lexeme = lex_double_colon; + } + else + { + _cur_lexeme = lex_none; + } + break; + + default: + if (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + + if (*cur == '.') + { + cur++; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_digit)) cur++; + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_number; + } + else if (PUGI__IS_CHARTYPEX(*cur, ctx_start_symbol)) + { + _cur_lexeme_contents.begin = cur; + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + + if (cur[0] == ':') + { + if (cur[1] == '*') // namespace test ncname:* + { + cur += 2; // :* + } + else if (PUGI__IS_CHARTYPEX(cur[1], ctx_symbol)) // namespace test qname + { + cur++; // : + + while (PUGI__IS_CHARTYPEX(*cur, ctx_symbol)) cur++; + } + } + + _cur_lexeme_contents.end = cur; + + _cur_lexeme = lex_string; + } + else + { + _cur_lexeme = lex_none; + } + } + + _cur = cur; + } + + lexeme_t current() const + { + return _cur_lexeme; + } + + const char_t* current_pos() const + { + return _cur_lexeme_pos; + } + + const xpath_lexer_string& contents() const + { + assert(_cur_lexeme == lex_var_ref || _cur_lexeme == lex_number || _cur_lexeme == lex_string || _cur_lexeme == lex_quoted_string); + + return _cur_lexeme_contents; + } + }; + + enum ast_type_t + { + ast_unknown, + ast_op_or, // left or right + ast_op_and, // left and right + ast_op_equal, // left = right + ast_op_not_equal, // left != right + ast_op_less, // left < right + ast_op_greater, // left > right + ast_op_less_or_equal, // left <= right + ast_op_greater_or_equal, // left >= right + ast_op_add, // left + right + ast_op_subtract, // left - right + ast_op_multiply, // left * right + ast_op_divide, // left / right + ast_op_mod, // left % right + ast_op_negate, // left - right + ast_op_union, // left | right + ast_predicate, // apply predicate to set; next points to next predicate + ast_filter, // select * from left where right + ast_string_constant, // string constant + ast_number_constant, // number constant + ast_variable, // variable + ast_func_last, // last() + ast_func_position, // position() + ast_func_count, // count(left) + ast_func_id, // id(left) + ast_func_local_name_0, // local-name() + ast_func_local_name_1, // local-name(left) + ast_func_namespace_uri_0, // namespace-uri() + ast_func_namespace_uri_1, // namespace-uri(left) + ast_func_name_0, // name() + ast_func_name_1, // name(left) + ast_func_string_0, // string() + ast_func_string_1, // string(left) + ast_func_concat, // concat(left, right, siblings) + ast_func_starts_with, // starts_with(left, right) + ast_func_contains, // contains(left, right) + ast_func_substring_before, // substring-before(left, right) + ast_func_substring_after, // substring-after(left, right) + ast_func_substring_2, // substring(left, right) + ast_func_substring_3, // substring(left, right, third) + ast_func_string_length_0, // string-length() + ast_func_string_length_1, // string-length(left) + ast_func_normalize_space_0, // normalize-space() + ast_func_normalize_space_1, // normalize-space(left) + ast_func_translate, // translate(left, right, third) + ast_func_boolean, // boolean(left) + ast_func_not, // not(left) + ast_func_true, // true() + ast_func_false, // false() + ast_func_lang, // lang(left) + ast_func_number_0, // number() + ast_func_number_1, // number(left) + ast_func_sum, // sum(left) + ast_func_floor, // floor(left) + ast_func_ceiling, // ceiling(left) + ast_func_round, // round(left) + ast_step, // process set left with step + ast_step_root, // select root node + + ast_opt_translate_table, // translate(left, right, third) where right/third are constants + ast_opt_compare_attribute // @name = 'string' + }; + + enum axis_t + { + axis_ancestor, + axis_ancestor_or_self, + axis_attribute, + axis_child, + axis_descendant, + axis_descendant_or_self, + axis_following, + axis_following_sibling, + axis_namespace, + axis_parent, + axis_preceding, + axis_preceding_sibling, + axis_self + }; + + enum nodetest_t + { + nodetest_none, + nodetest_name, + nodetest_type_node, + nodetest_type_comment, + nodetest_type_pi, + nodetest_type_text, + nodetest_pi, + nodetest_all, + nodetest_all_in_namespace + }; + + enum predicate_t + { + predicate_default, + predicate_posinv, + predicate_constant, + predicate_constant_one + }; + + enum nodeset_eval_t + { + nodeset_eval_all, + nodeset_eval_any, + nodeset_eval_first + }; + + template struct axis_to_type + { + static const axis_t axis; + }; + + template const axis_t axis_to_type::axis = N; + + class xpath_ast_node + { + private: + // node type + char _type; + char _rettype; + + // for ast_step + char _axis; + + // for ast_step/ast_predicate/ast_filter + char _test; + + // tree node structure + xpath_ast_node* _left; + xpath_ast_node* _right; + xpath_ast_node* _next; + + union + { + // value for ast_string_constant + const char_t* string; + // value for ast_number_constant + double number; + // variable for ast_variable + xpath_variable* variable; + // node test for ast_step (node name/namespace/node type/pi target) + const char_t* nodetest; + // table for ast_opt_translate_table + const unsigned char* table; + } _data; + + xpath_ast_node(const xpath_ast_node&); + xpath_ast_node& operator=(const xpath_ast_node&); + + template static bool compare_eq(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + { + if (lt == xpath_type_boolean || rt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number || rt == xpath_type_number) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_string || rt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string ls = lhs->eval_string(c, stack); + xpath_string rs = rhs->eval_string(c, stack); + + return comp(ls, rs); + } + } + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(string_value(*li, stack.result), string_value(*ri, stack.result))) + return true; + } + + return false; + } + else + { + if (lt == xpath_type_node_set) + { + swap(lhs, rhs); + swap(lt, rt); + } + + if (lt == xpath_type_boolean) + return comp(lhs->eval_boolean(c, stack), rhs->eval_boolean(c, stack)); + else if (lt == xpath_type_number) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_string) + { + xpath_allocator_capture cr(stack.result); + + xpath_string l = lhs->eval_string(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, string_value(*ri, stack.result))) + return true; + } + + return false; + } + } + + assert(false && "Wrong types"); // unreachable + return false; + } + + static bool eval_once(xpath_node_set::type_t type, nodeset_eval_t eval) + { + return type == xpath_node_set::type_sorted ? eval != nodeset_eval_all : eval == nodeset_eval_any; + } + + template static bool compare_rel(xpath_ast_node* lhs, xpath_ast_node* rhs, const xpath_context& c, const xpath_stack& stack, const Comp& comp) + { + xpath_value_type lt = lhs->rettype(), rt = rhs->rettype(); + + if (lt != xpath_type_node_set && rt != xpath_type_node_set) + return comp(lhs->eval_number(c, stack), rhs->eval_number(c, stack)); + else if (lt == xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + double l = convert_string_to_number(string_value(*li, stack.result).c_str()); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture crii(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + } + + return false; + } + else if (lt != xpath_type_node_set && rt == xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + double l = lhs->eval_number(c, stack); + xpath_node_set_raw rs = rhs->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* ri = rs.begin(); ri != rs.end(); ++ri) + { + xpath_allocator_capture cri(stack.result); + + if (comp(l, convert_string_to_number(string_value(*ri, stack.result).c_str()))) + return true; + } + + return false; + } + else if (lt == xpath_type_node_set && rt != xpath_type_node_set) + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ls = lhs->eval_node_set(c, stack, nodeset_eval_all); + double r = rhs->eval_number(c, stack); + + for (const xpath_node* li = ls.begin(); li != ls.end(); ++li) + { + xpath_allocator_capture cri(stack.result); + + if (comp(convert_string_to_number(string_value(*li, stack.result).c_str()), r)) + return true; + } + + return false; + } + else + { + assert(false && "Wrong types"); // unreachable + return false; + } + } + + static void apply_predicate_boolean(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() != xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_boolean(c, stack)) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack, bool once) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t i = 1; + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + // remove_if... or well, sort of + for (xpath_node* it = last; it != ns.end(); ++it, ++i) + { + xpath_context c(*it, i, size); + + if (expr->eval_number(c, stack) == i) + { + *last++ = *it; + + if (once) break; + } + } + + ns.truncate(last); + } + + static void apply_predicate_number_const(xpath_node_set_raw& ns, size_t first, xpath_ast_node* expr, const xpath_stack& stack) + { + assert(ns.size() >= first); + assert(expr->rettype() == xpath_type_number); + + size_t size = ns.size() - first; + + xpath_node* last = ns.begin() + first; + + xpath_context c(xpath_node(), 1, size); + + double er = expr->eval_number(c, stack); + + if (er >= 1.0 && er <= size) + { + size_t eri = static_cast(er); + + if (er == eri) + { + xpath_node r = last[eri - 1]; + + *last++ = r; + } + } + + ns.truncate(last); + } + + void apply_predicate(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, bool once) + { + if (ns.size() == first) return; + + assert(_type == ast_filter || _type == ast_predicate); + + if (_test == predicate_constant || _test == predicate_constant_one) + apply_predicate_number_const(ns, first, _right, stack); + else if (_right->rettype() == xpath_type_number) + apply_predicate_number(ns, first, _right, stack, once); + else + apply_predicate_boolean(ns, first, _right, stack, once); + } + + void apply_predicates(xpath_node_set_raw& ns, size_t first, const xpath_stack& stack, nodeset_eval_t eval) + { + if (ns.size() == first) return; + + bool last_once = eval_once(ns.type(), eval); + + for (xpath_ast_node* pred = _right; pred; pred = pred->_next) + pred->apply_predicate(ns, first, stack, !pred->_next && last_once); + } + + bool step_push(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* parent, xpath_allocator* alloc) + { + assert(a); + + const char_t* name = a->name ? a->name + 0 : PUGIXML_TEXT(""); + + switch (_test) + { + case nodetest_name: + if (strequal(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_type_node: + case nodetest_all: + if (is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (starts_with(name, _data.nodetest) && is_xpath_attribute(name)) + { + ns.push_back(xpath_node(xml_attribute(a), xml_node(parent)), alloc); + return true; + } + break; + + default: + ; + } + + return false; + } + + bool step_push(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc) + { + assert(n); + + xml_node_type type = PUGI__NODETYPE(n); + + switch (_test) + { + case nodetest_name: + if (type == node_element && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_node: + ns.push_back(xml_node(n), alloc); + return true; + + case nodetest_type_comment: + if (type == node_comment) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_text: + if (type == node_pcdata || type == node_cdata) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_type_pi: + if (type == node_pi) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_pi: + if (type == node_pi && n->name && strequal(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all: + if (type == node_element) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + case nodetest_all_in_namespace: + if (type == node_element && n->name && starts_with(n->name, _data.nodetest)) + { + ns.push_back(xml_node(n), alloc); + return true; + } + break; + + default: + assert(false && "Unknown axis"); // unreachable + } + + return false; + } + + template void step_fill(xpath_node_set_raw& ns, xml_node_struct* n, xpath_allocator* alloc, bool once, T) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_attribute: + { + for (xml_attribute_struct* a = n->first_attribute; a; a = a->next_attribute) + if (step_push(ns, a, n, alloc) & once) + return; + + break; + } + + case axis_child: + { + for (xml_node_struct* c = n->first_child; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_descendant: + case axis_descendant_or_self: + { + if (axis == axis_descendant_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->first_child; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (cur == n) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_following_sibling: + { + for (xml_node_struct* c = n->next_sibling; c; c = c->next_sibling) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_preceding_sibling: + { + for (xml_node_struct* c = n->prev_sibling_c; c->next_sibling; c = c->prev_sibling_c) + if (step_push(ns, c, alloc) & once) + return; + + break; + } + + case axis_following: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + } + + break; + } + + case axis_preceding: + { + xml_node_struct* cur = n; + + // exit from this node so that we don't include descendants + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->prev_sibling_c; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child->prev_sibling_c; + else + { + // leaf node, can't be ancestor + if (step_push(ns, cur, alloc) & once) + return; + + while (!cur->prev_sibling_c->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + + if (!node_is_ancestor(cur, n)) + if (step_push(ns, cur, alloc) & once) + return; + } + + cur = cur->prev_sibling_c; + } + } + + break; + } + + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self) + if (step_push(ns, n, alloc) & once) + return; + + xml_node_struct* cur = n->parent; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_self: + { + step_push(ns, n, alloc); + + break; + } + + case axis_parent: + { + if (n->parent) + step_push(ns, n->parent, alloc); + + break; + } + + default: + assert(false && "Unimplemented axis"); // unreachable + } + } + + template void step_fill(xpath_node_set_raw& ns, xml_attribute_struct* a, xml_node_struct* p, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + + switch (axis) + { + case axis_ancestor: + case axis_ancestor_or_self: + { + if (axis == axis_ancestor_or_self && _test == nodetest_type_node) // reject attributes based on principal node type test + if (step_push(ns, a, p, alloc) & once) + return; + + xml_node_struct* cur = p; + + while (cur) + { + if (step_push(ns, cur, alloc) & once) + return; + + cur = cur->parent; + } + + break; + } + + case axis_descendant_or_self: + case axis_self: + { + if (_test == nodetest_type_node) // reject attributes based on principal node type test + step_push(ns, a, p, alloc); + + break; + } + + case axis_following: + { + xml_node_struct* cur = p; + + while (cur) + { + if (cur->first_child) + cur = cur->first_child; + else + { + while (!cur->next_sibling) + { + cur = cur->parent; + + if (!cur) return; + } + + cur = cur->next_sibling; + } + + if (step_push(ns, cur, alloc) & once) + return; + } + + break; + } + + case axis_parent: + { + step_push(ns, p, alloc); + + break; + } + + case axis_preceding: + { + // preceding:: axis does not include attribute nodes and attribute ancestors (they are the same as parent's ancestors), so we can reuse node preceding + step_fill(ns, p, alloc, once, v); + break; + } + + default: + assert(false && "Unimplemented axis"); // unreachable + } + } + + template void step_fill(xpath_node_set_raw& ns, const xpath_node& xn, xpath_allocator* alloc, bool once, T v) + { + const axis_t axis = T::axis; + const bool axis_has_attributes = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_descendant_or_self || axis == axis_following || axis == axis_parent || axis == axis_preceding || axis == axis_self); + + if (xn.node()) + step_fill(ns, xn.node().internal_object(), alloc, once, v); + else if (axis_has_attributes && xn.attribute() && xn.parent()) + step_fill(ns, xn.attribute().internal_object(), xn.parent().internal_object(), alloc, once, v); + } + + template xpath_node_set_raw step_do(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval, T v) + { + const axis_t axis = T::axis; + const bool axis_reverse = (axis == axis_ancestor || axis == axis_ancestor_or_self || axis == axis_preceding || axis == axis_preceding_sibling); + const xpath_node_set::type_t axis_type = axis_reverse ? xpath_node_set::type_sorted_reverse : xpath_node_set::type_sorted; + + bool once = + (axis == axis_attribute && _test == nodetest_name) || + (!_right && eval_once(axis_type, eval)) || + (_right && !_right->_next && _right->_test == predicate_constant_one); + + xpath_node_set_raw ns; + ns.set_type(axis_type); + + if (_left) + { + xpath_node_set_raw s = _left->eval_node_set(c, stack, nodeset_eval_all); + + // self axis preserves the original order + if (axis == axis_self) ns.set_type(s.type()); + + for (const xpath_node* it = s.begin(); it != s.end(); ++it) + { + size_t size = ns.size(); + + // in general, all axes generate elements in a particular order, but there is no order guarantee if axis is applied to two nodes + if (axis != axis_self && size != 0) ns.set_type(xpath_node_set::type_unsorted); + + step_fill(ns, *it, stack.result, once, v); + if (_right) apply_predicates(ns, size, stack, eval); + } + } + else + { + step_fill(ns, c.n, stack.result, once, v); + if (_right) apply_predicates(ns, 0, stack, eval); + } + + // child, attribute and self axes always generate unique set of nodes + // for other axis, if the set stayed sorted, it stayed unique because the traversal algorithms do not visit the same node twice + if (axis != axis_child && axis != axis_attribute && axis != axis_self && ns.type() == xpath_node_set::type_unsorted) + ns.remove_duplicates(); + + return ns; + } + + public: + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, const char_t* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_string_constant); + _data.string = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, double value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_number_constant); + _data.number = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_variable* value): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(0), _right(0), _next(0) + { + assert(type == ast_variable); + _data.variable = value; + } + + xpath_ast_node(ast_type_t type, xpath_value_type rettype_, xpath_ast_node* left = 0, xpath_ast_node* right = 0): + _type(static_cast(type)), _rettype(static_cast(rettype_)), _axis(0), _test(0), _left(left), _right(right), _next(0) + { + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(static_cast(axis)), _test(static_cast(test)), _left(left), _right(0), _next(0) + { + assert(type == ast_step); + _data.nodetest = contents; + } + + xpath_ast_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test): + _type(static_cast(type)), _rettype(xpath_type_node_set), _axis(0), _test(static_cast(test)), _left(left), _right(right), _next(0) + { + assert(type == ast_filter || type == ast_predicate); + } + + void set_next(xpath_ast_node* value) + { + _next = value; + } + + void set_right(xpath_ast_node* value) + { + _right = value; + } + + bool eval_boolean(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_or: + return _left->eval_boolean(c, stack) || _right->eval_boolean(c, stack); + + case ast_op_and: + return _left->eval_boolean(c, stack) && _right->eval_boolean(c, stack); + + case ast_op_equal: + return compare_eq(_left, _right, c, stack, equal_to()); + + case ast_op_not_equal: + return compare_eq(_left, _right, c, stack, not_equal_to()); + + case ast_op_less: + return compare_rel(_left, _right, c, stack, less()); + + case ast_op_greater: + return compare_rel(_right, _left, c, stack, less()); + + case ast_op_less_or_equal: + return compare_rel(_left, _right, c, stack, less_equal()); + + case ast_op_greater_or_equal: + return compare_rel(_right, _left, c, stack, less_equal()); + + case ast_func_starts_with: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return starts_with(lr.c_str(), rr.c_str()); + } + + case ast_func_contains: + { + xpath_allocator_capture cr(stack.result); + + xpath_string lr = _left->eval_string(c, stack); + xpath_string rr = _right->eval_string(c, stack); + + return find_substring(lr.c_str(), rr.c_str()) != 0; + } + + case ast_func_boolean: + return _left->eval_boolean(c, stack); + + case ast_func_not: + return !_left->eval_boolean(c, stack); + + case ast_func_true: + return true; + + case ast_func_false: + return false; + + case ast_func_lang: + { + if (c.n.attribute()) return false; + + xpath_allocator_capture cr(stack.result); + + xpath_string lang = _left->eval_string(c, stack); + + for (xml_node n = c.n.node(); n; n = n.parent()) + { + xml_attribute a = n.attribute(PUGIXML_TEXT("xml:lang")); + + if (a) + { + const char_t* value = a.value(); + + // strnicmp / strncasecmp is not portable + for (const char_t* lit = lang.c_str(); *lit; ++lit) + { + if (tolower_ascii(*lit) != tolower_ascii(*value)) return false; + ++value; + } + + return *value == 0 || *value == '-'; + } + } + + return false; + } + + case ast_opt_compare_attribute: + { + const char_t* value = (_right->_type == ast_string_constant) ? _right->_data.string : _right->_data.variable->get_string(); + + xml_attribute attr = c.n.node().attribute(_left->_data.nodetest); + + return attr && strequal(attr.value(), value) && is_xpath_attribute(attr.name()); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_boolean) + return _data.variable->get_boolean(); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_number: + return convert_number_to_boolean(eval_number(c, stack)); + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return !eval_string(c, stack).empty(); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return !eval_node_set(c, stack, nodeset_eval_any).empty(); + } + + default: + assert(false && "Wrong expression for return type boolean"); // unreachable + return false; + } + } + } + } + + double eval_number(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_op_add: + return _left->eval_number(c, stack) + _right->eval_number(c, stack); + + case ast_op_subtract: + return _left->eval_number(c, stack) - _right->eval_number(c, stack); + + case ast_op_multiply: + return _left->eval_number(c, stack) * _right->eval_number(c, stack); + + case ast_op_divide: + return _left->eval_number(c, stack) / _right->eval_number(c, stack); + + case ast_op_mod: + return fmod(_left->eval_number(c, stack), _right->eval_number(c, stack)); + + case ast_op_negate: + return -_left->eval_number(c, stack); + + case ast_number_constant: + return _data.number; + + case ast_func_last: + return static_cast(c.size); + + case ast_func_position: + return static_cast(c.position); + + case ast_func_count: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_node_set(c, stack, nodeset_eval_all).size()); + } + + case ast_func_string_length_0: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(string_value(c.n, stack.result).length()); + } + + case ast_func_string_length_1: + { + xpath_allocator_capture cr(stack.result); + + return static_cast(_left->eval_string(c, stack).length()); + } + + case ast_func_number_0: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(string_value(c.n, stack.result).c_str()); + } + + case ast_func_number_1: + return _left->eval_number(c, stack); + + case ast_func_sum: + { + xpath_allocator_capture cr(stack.result); + + double r = 0; + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_all); + + for (const xpath_node* it = ns.begin(); it != ns.end(); ++it) + { + xpath_allocator_capture cri(stack.result); + + r += convert_string_to_number(string_value(*it, stack.result).c_str()); + } + + return r; + } + + case ast_func_floor: + { + double r = _left->eval_number(c, stack); + + return r == r ? floor(r) : r; + } + + case ast_func_ceiling: + { + double r = _left->eval_number(c, stack); + + return r == r ? ceil(r) : r; + } + + case ast_func_round: + return round_nearest_nzero(_left->eval_number(c, stack)); + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_number) + return _data.variable->get_number(); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return eval_boolean(c, stack) ? 1 : 0; + + case xpath_type_string: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.result); + + return convert_string_to_number(eval_string(c, stack).c_str()); + } + + default: + assert(false && "Wrong expression for return type number"); // unreachable + return 0; + } + + } + } + } + + xpath_string eval_string_concat(const xpath_context& c, const xpath_stack& stack) + { + assert(_type == ast_func_concat); + + xpath_allocator_capture ct(stack.temp); + + // count the string number + size_t count = 1; + for (xpath_ast_node* nc = _right; nc; nc = nc->_next) count++; + + // allocate a buffer for temporary string objects + xpath_string* buffer = static_cast(stack.temp->allocate(count * sizeof(xpath_string))); + if (!buffer) return xpath_string(); + + // evaluate all strings to temporary stack + xpath_stack swapped_stack = {stack.temp, stack.result}; + + buffer[0] = _left->eval_string(c, swapped_stack); + + size_t pos = 1; + for (xpath_ast_node* n = _right; n; n = n->_next, ++pos) buffer[pos] = n->eval_string(c, swapped_stack); + assert(pos == count); + + // get total length + size_t length = 0; + for (size_t i = 0; i < count; ++i) length += buffer[i].length(); + + // create final string + char_t* result = static_cast(stack.result->allocate((length + 1) * sizeof(char_t))); + if (!result) return xpath_string(); + + char_t* ri = result; + + for (size_t j = 0; j < count; ++j) + for (const char_t* bi = buffer[j].c_str(); *bi; ++bi) + *ri++ = *bi; + + *ri = 0; + + return xpath_string::from_heap_preallocated(result, ri); + } + + xpath_string eval_string(const xpath_context& c, const xpath_stack& stack) + { + switch (_type) + { + case ast_string_constant: + return xpath_string::from_const(_data.string); + + case ast_func_local_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_local_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(local_name(na)); + } + + case ast_func_name_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_name_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(qualified_name(na)); + } + + case ast_func_namespace_uri_0: + { + xpath_node na = c.n; + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_namespace_uri_1: + { + xpath_allocator_capture cr(stack.result); + + xpath_node_set_raw ns = _left->eval_node_set(c, stack, nodeset_eval_first); + xpath_node na = ns.first(); + + return xpath_string::from_const(namespace_uri(na)); + } + + case ast_func_string_0: + return string_value(c.n, stack.result); + + case ast_func_string_1: + return _left->eval_string(c, stack); + + case ast_func_concat: + return eval_string_concat(c, stack); + + case ast_func_substring_before: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + + return pos ? xpath_string::from_heap(s.c_str(), pos, stack.result) : xpath_string(); + } + + case ast_func_substring_after: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + xpath_string p = _right->eval_string(c, swapped_stack); + + const char_t* pos = find_substring(s.c_str(), p.c_str()); + if (!pos) return xpath_string(); + + const char_t* rbegin = pos + p.length(); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_2: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + + if (is_nan(first)) return xpath_string(); // NaN + else if (first >= s_length + 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + assert(1 <= pos && pos <= s_length + 1); + + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + s.length(); + + return s.uses_heap() ? xpath_string::from_heap(rbegin, rend, stack.result) : xpath_string::from_const(rbegin); + } + + case ast_func_substring_3: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, swapped_stack); + size_t s_length = s.length(); + + double first = round_nearest(_right->eval_number(c, stack)); + double last = first + round_nearest(_right->_next->eval_number(c, stack)); + + if (is_nan(first) || is_nan(last)) return xpath_string(); + else if (first >= s_length + 1) return xpath_string(); + else if (first >= last) return xpath_string(); + else if (last < 1) return xpath_string(); + + size_t pos = first < 1 ? 1 : static_cast(first); + size_t end = last >= s_length + 1 ? s_length + 1 : static_cast(last); + + assert(1 <= pos && pos <= end && end <= s_length + 1); + const char_t* rbegin = s.c_str() + (pos - 1); + const char_t* rend = s.c_str() + (end - 1); + + return (end == s_length + 1 && !s.uses_heap()) ? xpath_string::from_const(rbegin) : xpath_string::from_heap(rbegin, rend, stack.result); + } + + case ast_func_normalize_space_0: + { + xpath_string s = string_value(c.n, stack.result); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_normalize_space_1: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = normalize_space(begin); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_func_translate: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_string s = _left->eval_string(c, stack); + xpath_string from = _right->eval_string(c, swapped_stack); + xpath_string to = _right->_next->eval_string(c, swapped_stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = translate(begin, from.c_str(), to.c_str(), to.length()); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_opt_translate_table: + { + xpath_string s = _left->eval_string(c, stack); + + char_t* begin = s.data(stack.result); + if (!begin) return xpath_string(); + + char_t* end = translate_table(begin, _data.table); + + return xpath_string::from_heap_preallocated(begin, end); + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_string) + return xpath_string::from_const(_data.variable->get_string()); + } + + // fallthrough + default: + { + switch (_rettype) + { + case xpath_type_boolean: + return xpath_string::from_const(eval_boolean(c, stack) ? PUGIXML_TEXT("true") : PUGIXML_TEXT("false")); + + case xpath_type_number: + return convert_number_to_string(eval_number(c, stack), stack.result); + + case xpath_type_node_set: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ns = eval_node_set(c, swapped_stack, nodeset_eval_first); + return ns.empty() ? xpath_string() : string_value(ns.first(), stack.result); + } + + default: + assert(false && "Wrong expression for return type string"); // unreachable + return xpath_string(); + } + } + } + } + + xpath_node_set_raw eval_node_set(const xpath_context& c, const xpath_stack& stack, nodeset_eval_t eval) + { + switch (_type) + { + case ast_op_union: + { + xpath_allocator_capture cr(stack.temp); + + xpath_stack swapped_stack = {stack.temp, stack.result}; + + xpath_node_set_raw ls = _left->eval_node_set(c, swapped_stack, eval); + xpath_node_set_raw rs = _right->eval_node_set(c, stack, eval); + + // we can optimize merging two sorted sets, but this is a very rare operation, so don't bother + rs.set_type(xpath_node_set::type_unsorted); + + rs.append(ls.begin(), ls.end(), stack.result); + rs.remove_duplicates(); + + return rs; + } + + case ast_filter: + { + xpath_node_set_raw set = _left->eval_node_set(c, stack, _test == predicate_constant_one ? nodeset_eval_first : nodeset_eval_all); + + // either expression is a number or it contains position() call; sort by document order + if (_test != predicate_posinv) set.sort_do(); + + bool once = eval_once(set.type(), eval); + + apply_predicate(set, 0, stack, once); + + return set; + } + + case ast_func_id: + return xpath_node_set_raw(); + + case ast_step: + { + switch (_axis) + { + case axis_ancestor: + return step_do(c, stack, eval, axis_to_type()); + + case axis_ancestor_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_attribute: + return step_do(c, stack, eval, axis_to_type()); + + case axis_child: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant: + return step_do(c, stack, eval, axis_to_type()); + + case axis_descendant_or_self: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following: + return step_do(c, stack, eval, axis_to_type()); + + case axis_following_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_namespace: + // namespaced axis is not supported + return xpath_node_set_raw(); + + case axis_parent: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding: + return step_do(c, stack, eval, axis_to_type()); + + case axis_preceding_sibling: + return step_do(c, stack, eval, axis_to_type()); + + case axis_self: + return step_do(c, stack, eval, axis_to_type()); + + default: + assert(false && "Unknown axis"); // unreachable + return xpath_node_set_raw(); + } + } + + case ast_step_root: + { + assert(!_right); // root step can't have any predicates + + xpath_node_set_raw ns; + + ns.set_type(xpath_node_set::type_sorted); + + if (c.n.node()) ns.push_back(c.n.node().root(), stack.result); + else if (c.n.attribute()) ns.push_back(c.n.parent().root(), stack.result); + + return ns; + } + + case ast_variable: + { + assert(_rettype == _data.variable->type()); + + if (_rettype == xpath_type_node_set) + { + const xpath_node_set& s = _data.variable->get_node_set(); + + xpath_node_set_raw ns; + + ns.set_type(s.type()); + ns.append(s.begin(), s.end(), stack.result); + + return ns; + } + } + + // fallthrough + default: + assert(false && "Wrong expression for return type node set"); // unreachable + return xpath_node_set_raw(); + } + } + + void optimize(xpath_allocator* alloc) + { + if (_left) + _left->optimize(alloc); + + if (_right) + _right->optimize(alloc); + + if (_next) + _next->optimize(alloc); + + optimize_self(alloc); + } + + void optimize_self(xpath_allocator* alloc) + { + // Rewrite [position()=expr] with [expr] + // Note that this step has to go before classification to recognize [position()=1] + if ((_type == ast_filter || _type == ast_predicate) && + _right->_type == ast_op_equal && _right->_left->_type == ast_func_position && _right->_right->_rettype == xpath_type_number) + { + _right = _right->_right; + } + + // Classify filter/predicate ops to perform various optimizations during evaluation + if (_type == ast_filter || _type == ast_predicate) + { + assert(_test == predicate_default); + + if (_right->_type == ast_number_constant && _right->_data.number == 1.0) + _test = predicate_constant_one; + else if (_right->_rettype == xpath_type_number && (_right->_type == ast_number_constant || _right->_type == ast_variable || _right->_type == ast_func_last)) + _test = predicate_constant; + else if (_right->_rettype != xpath_type_number && _right->is_posinv_expr()) + _test = predicate_posinv; + } + + // Rewrite descendant-or-self::node()/child::foo with descendant::foo + // The former is a full form of //foo, the latter is much faster since it executes the node test immediately + // Do a similar kind of rewrite for self/descendant/descendant-or-self axes + // Note that we only rewrite positionally invariant steps (//foo[1] != /descendant::foo[1]) + if (_type == ast_step && (_axis == axis_child || _axis == axis_self || _axis == axis_descendant || _axis == axis_descendant_or_self) && _left && + _left->_type == ast_step && _left->_axis == axis_descendant_or_self && _left->_test == nodetest_type_node && !_left->_right && + is_posinv_step()) + { + if (_axis == axis_child || _axis == axis_descendant) + _axis = axis_descendant; + else + _axis = axis_descendant_or_self; + + _left = _left->_left; + } + + // Use optimized lookup table implementation for translate() with constant arguments + if (_type == ast_func_translate && _right->_type == ast_string_constant && _right->_next->_type == ast_string_constant) + { + unsigned char* table = translate_table_generate(alloc, _right->_data.string, _right->_next->_data.string); + + if (table) + { + _type = ast_opt_translate_table; + _data.table = table; + } + } + + // Use optimized path for @attr = 'value' or @attr = $value + if (_type == ast_op_equal && + _left->_type == ast_step && _left->_axis == axis_attribute && _left->_test == nodetest_name && !_left->_left && !_left->_right && + (_right->_type == ast_string_constant || (_right->_type == ast_variable && _right->_rettype == xpath_type_string))) + { + _type = ast_opt_compare_attribute; + } + } + + bool is_posinv_expr() const + { + switch (_type) + { + case ast_func_position: + case ast_func_last: + return false; + + case ast_string_constant: + case ast_number_constant: + case ast_variable: + return true; + + case ast_step: + case ast_step_root: + return true; + + case ast_predicate: + case ast_filter: + return true; + + default: + if (_left && !_left->is_posinv_expr()) return false; + + for (xpath_ast_node* n = _right; n; n = n->_next) + if (!n->is_posinv_expr()) return false; + + return true; + } + } + + bool is_posinv_step() const + { + assert(_type == ast_step); + + for (xpath_ast_node* n = _right; n; n = n->_next) + { + assert(n->_type == ast_predicate); + + if (n->_test != predicate_posinv) + return false; + } + + return true; + } + + xpath_value_type rettype() const + { + return static_cast(_rettype); + } + }; + + struct xpath_parser + { + xpath_allocator* _alloc; + xpath_lexer _lexer; + + const char_t* _query; + xpath_variable_set* _variables; + + xpath_parse_result* _result; + + char_t _scratch[32]; + + xpath_ast_node* error(const char* message) + { + _result->error = message; + _result->offset = _lexer.current_pos() - _query; + + return 0; + } + + xpath_ast_node* error_oom() + { + assert(_alloc->_error); + *_alloc->_error = true; + + return 0; + } + + void* alloc_node() + { + return _alloc->allocate(sizeof(xpath_ast_node)); + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, const char_t* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, double value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_variable* value) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, value) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_value_type rettype, xpath_ast_node* left = 0, xpath_ast_node* right = 0) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, rettype, left, right) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, axis_t axis, nodetest_t test, const char_t* contents) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, axis, test, contents) : 0; + } + + xpath_ast_node* alloc_node(ast_type_t type, xpath_ast_node* left, xpath_ast_node* right, predicate_t test) + { + void* memory = alloc_node(); + return memory ? new (memory) xpath_ast_node(type, left, right, test) : 0; + } + + const char_t* alloc_string(const xpath_lexer_string& value) + { + if (!value.begin) + return PUGIXML_TEXT(""); + + size_t length = static_cast(value.end - value.begin); + + char_t* c = static_cast(_alloc->allocate((length + 1) * sizeof(char_t))); + if (!c) return 0; + + memcpy(c, value.begin, length * sizeof(char_t)); + c[length] = 0; + + return c; + } + + xpath_ast_node* parse_function(const xpath_lexer_string& name, size_t argc, xpath_ast_node* args[2]) + { + switch (name.begin[0]) + { + case 'b': + if (name == PUGIXML_TEXT("boolean") && argc == 1) + return alloc_node(ast_func_boolean, xpath_type_boolean, args[0]); + + break; + + case 'c': + if (name == PUGIXML_TEXT("count") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_count, xpath_type_number, args[0]); + } + else if (name == PUGIXML_TEXT("contains") && argc == 2) + return alloc_node(ast_func_contains, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("concat") && argc >= 2) + return alloc_node(ast_func_concat, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("ceiling") && argc == 1) + return alloc_node(ast_func_ceiling, xpath_type_number, args[0]); + + break; + + case 'f': + if (name == PUGIXML_TEXT("false") && argc == 0) + return alloc_node(ast_func_false, xpath_type_boolean); + else if (name == PUGIXML_TEXT("floor") && argc == 1) + return alloc_node(ast_func_floor, xpath_type_number, args[0]); + + break; + + case 'i': + if (name == PUGIXML_TEXT("id") && argc == 1) + return alloc_node(ast_func_id, xpath_type_node_set, args[0]); + + break; + + case 'l': + if (name == PUGIXML_TEXT("last") && argc == 0) + return alloc_node(ast_func_last, xpath_type_number); + else if (name == PUGIXML_TEXT("lang") && argc == 1) + return alloc_node(ast_func_lang, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("local-name") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_local_name_0 : ast_func_local_name_1, xpath_type_string, args[0]); + } + + break; + + case 'n': + if (name == PUGIXML_TEXT("name") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_name_0 : ast_func_name_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("namespace-uri") && argc <= 1) + { + if (argc == 1 && args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(argc == 0 ? ast_func_namespace_uri_0 : ast_func_namespace_uri_1, xpath_type_string, args[0]); + } + else if (name == PUGIXML_TEXT("normalize-space") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_normalize_space_0 : ast_func_normalize_space_1, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("not") && argc == 1) + return alloc_node(ast_func_not, xpath_type_boolean, args[0]); + else if (name == PUGIXML_TEXT("number") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_number_0 : ast_func_number_1, xpath_type_number, args[0]); + + break; + + case 'p': + if (name == PUGIXML_TEXT("position") && argc == 0) + return alloc_node(ast_func_position, xpath_type_number); + + break; + + case 'r': + if (name == PUGIXML_TEXT("round") && argc == 1) + return alloc_node(ast_func_round, xpath_type_number, args[0]); + + break; + + case 's': + if (name == PUGIXML_TEXT("string") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_0 : ast_func_string_1, xpath_type_string, args[0]); + else if (name == PUGIXML_TEXT("string-length") && argc <= 1) + return alloc_node(argc == 0 ? ast_func_string_length_0 : ast_func_string_length_1, xpath_type_number, args[0]); + else if (name == PUGIXML_TEXT("starts-with") && argc == 2) + return alloc_node(ast_func_starts_with, xpath_type_boolean, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-before") && argc == 2) + return alloc_node(ast_func_substring_before, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring-after") && argc == 2) + return alloc_node(ast_func_substring_after, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("substring") && (argc == 2 || argc == 3)) + return alloc_node(argc == 2 ? ast_func_substring_2 : ast_func_substring_3, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("sum") && argc == 1) + { + if (args[0]->rettype() != xpath_type_node_set) return error("Function has to be applied to node set"); + return alloc_node(ast_func_sum, xpath_type_number, args[0]); + } + + break; + + case 't': + if (name == PUGIXML_TEXT("translate") && argc == 3) + return alloc_node(ast_func_translate, xpath_type_string, args[0], args[1]); + else if (name == PUGIXML_TEXT("true") && argc == 0) + return alloc_node(ast_func_true, xpath_type_boolean); + + break; + + default: + break; + } + + return error("Unrecognized function or wrong parameter count"); + } + + axis_t parse_axis_name(const xpath_lexer_string& name, bool& specified) + { + specified = true; + + switch (name.begin[0]) + { + case 'a': + if (name == PUGIXML_TEXT("ancestor")) + return axis_ancestor; + else if (name == PUGIXML_TEXT("ancestor-or-self")) + return axis_ancestor_or_self; + else if (name == PUGIXML_TEXT("attribute")) + return axis_attribute; + + break; + + case 'c': + if (name == PUGIXML_TEXT("child")) + return axis_child; + + break; + + case 'd': + if (name == PUGIXML_TEXT("descendant")) + return axis_descendant; + else if (name == PUGIXML_TEXT("descendant-or-self")) + return axis_descendant_or_self; + + break; + + case 'f': + if (name == PUGIXML_TEXT("following")) + return axis_following; + else if (name == PUGIXML_TEXT("following-sibling")) + return axis_following_sibling; + + break; + + case 'n': + if (name == PUGIXML_TEXT("namespace")) + return axis_namespace; + + break; + + case 'p': + if (name == PUGIXML_TEXT("parent")) + return axis_parent; + else if (name == PUGIXML_TEXT("preceding")) + return axis_preceding; + else if (name == PUGIXML_TEXT("preceding-sibling")) + return axis_preceding_sibling; + + break; + + case 's': + if (name == PUGIXML_TEXT("self")) + return axis_self; + + break; + + default: + break; + } + + specified = false; + return axis_child; + } + + nodetest_t parse_node_test_type(const xpath_lexer_string& name) + { + switch (name.begin[0]) + { + case 'c': + if (name == PUGIXML_TEXT("comment")) + return nodetest_type_comment; + + break; + + case 'n': + if (name == PUGIXML_TEXT("node")) + return nodetest_type_node; + + break; + + case 'p': + if (name == PUGIXML_TEXT("processing-instruction")) + return nodetest_type_pi; + + break; + + case 't': + if (name == PUGIXML_TEXT("text")) + return nodetest_type_text; + + break; + + default: + break; + } + + return nodetest_none; + } + + // PrimaryExpr ::= VariableReference | '(' Expr ')' | Literal | Number | FunctionCall + xpath_ast_node* parse_primary_expression() + { + switch (_lexer.current()) + { + case lex_var_ref: + { + xpath_lexer_string name = _lexer.contents(); + + if (!_variables) + return error("Unknown variable: variable set is not provided"); + + xpath_variable* var = 0; + if (!get_variable_scratch(_scratch, _variables, name.begin, name.end, &var)) + return error_oom(); + + if (!var) + return error("Unknown variable: variable set does not contain the given name"); + + _lexer.next(); + + return alloc_node(ast_variable, var->type(), var); + } + + case lex_open_brace: + { + _lexer.next(); + + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + if (_lexer.current() != lex_close_brace) + return error("Expected ')' to match an opening '('"); + + _lexer.next(); + + return n; + } + + case lex_quoted_string: + { + const char_t* value = alloc_string(_lexer.contents()); + if (!value) return 0; + + _lexer.next(); + + return alloc_node(ast_string_constant, xpath_type_string, value); + } + + case lex_number: + { + double value = 0; + + if (!convert_string_to_number_scratch(_scratch, _lexer.contents().begin, _lexer.contents().end, &value)) + return error_oom(); + + _lexer.next(); + + return alloc_node(ast_number_constant, xpath_type_number, value); + } + + case lex_string: + { + xpath_ast_node* args[2] = {0}; + size_t argc = 0; + + xpath_lexer_string function = _lexer.contents(); + _lexer.next(); + + xpath_ast_node* last_arg = 0; + + if (_lexer.current() != lex_open_brace) + return error("Unrecognized function call"); + _lexer.next(); + + while (_lexer.current() != lex_close_brace) + { + if (argc > 0) + { + if (_lexer.current() != lex_comma) + return error("No comma between function arguments"); + _lexer.next(); + } + + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + if (argc < 2) args[argc] = n; + else last_arg->set_next(n); + + argc++; + last_arg = n; + } + + _lexer.next(); + + return parse_function(function, argc, args); + } + + default: + return error("Unrecognizable primary expression"); + } + } + + // FilterExpr ::= PrimaryExpr | FilterExpr Predicate + // Predicate ::= '[' PredicateExpr ']' + // PredicateExpr ::= Expr + xpath_ast_node* parse_filter_expression() + { + xpath_ast_node* n = parse_primary_expression(); + if (!n) return 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + if (n->rettype() != xpath_type_node_set) + return error("Predicate has to be applied to node set"); + + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; + + n = alloc_node(ast_filter, n, expr, predicate_default); + if (!n) return 0; + + if (_lexer.current() != lex_close_square_brace) + return error("Expected ']' to match an opening '['"); + + _lexer.next(); + } + + return n; + } + + // Step ::= AxisSpecifier NodeTest Predicate* | AbbreviatedStep + // AxisSpecifier ::= AxisName '::' | '@'? + // NodeTest ::= NameTest | NodeType '(' ')' | 'processing-instruction' '(' Literal ')' + // NameTest ::= '*' | NCName ':' '*' | QName + // AbbreviatedStep ::= '.' | '..' + xpath_ast_node* parse_step(xpath_ast_node* set) + { + if (set && set->rettype() != xpath_type_node_set) + return error("Step has to be applied to node set"); + + bool axis_specified = false; + axis_t axis = axis_child; // implied child axis + + if (_lexer.current() == lex_axis_attribute) + { + axis = axis_attribute; + axis_specified = true; + + _lexer.next(); + } + else if (_lexer.current() == lex_dot) + { + _lexer.next(); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_self, nodetest_type_node, 0); + } + else if (_lexer.current() == lex_double_dot) + { + _lexer.next(); + + if (_lexer.current() == lex_open_square_brace) + return error("Predicates are not allowed after an abbreviated step"); + + return alloc_node(ast_step, set, axis_parent, nodetest_type_node, 0); + } + + nodetest_t nt_type = nodetest_none; + xpath_lexer_string nt_name; + + if (_lexer.current() == lex_string) + { + // node name test + nt_name = _lexer.contents(); + _lexer.next(); + + // was it an axis name? + if (_lexer.current() == lex_double_colon) + { + // parse axis name + if (axis_specified) + return error("Two axis specifiers in one step"); + + axis = parse_axis_name(nt_name, axis_specified); + + if (!axis_specified) + return error("Unknown axis"); + + // read actual node test + _lexer.next(); + + if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + nt_name = xpath_lexer_string(); + _lexer.next(); + } + else if (_lexer.current() == lex_string) + { + nt_name = _lexer.contents(); + _lexer.next(); + } + else + { + return error("Unrecognized node test"); + } + } + + if (nt_type == nodetest_none) + { + // node type test or processing-instruction + if (_lexer.current() == lex_open_brace) + { + _lexer.next(); + + if (_lexer.current() == lex_close_brace) + { + _lexer.next(); + + nt_type = parse_node_test_type(nt_name); + + if (nt_type == nodetest_none) + return error("Unrecognized node type"); + + nt_name = xpath_lexer_string(); + } + else if (nt_name == PUGIXML_TEXT("processing-instruction")) + { + if (_lexer.current() != lex_quoted_string) + return error("Only literals are allowed as arguments to processing-instruction()"); + + nt_type = nodetest_pi; + nt_name = _lexer.contents(); + _lexer.next(); + + if (_lexer.current() != lex_close_brace) + return error("Unmatched brace near processing-instruction()"); + _lexer.next(); + } + else + { + return error("Unmatched brace near node type test"); + } + } + // QName or NCName:* + else + { + if (nt_name.end - nt_name.begin > 2 && nt_name.end[-2] == ':' && nt_name.end[-1] == '*') // NCName:* + { + nt_name.end--; // erase * + + nt_type = nodetest_all_in_namespace; + } + else + { + nt_type = nodetest_name; + } + } + } + } + else if (_lexer.current() == lex_multiply) + { + nt_type = nodetest_all; + _lexer.next(); + } + else + { + return error("Unrecognized node test"); + } + + const char_t* nt_name_copy = alloc_string(nt_name); + if (!nt_name_copy) return 0; + + xpath_ast_node* n = alloc_node(ast_step, set, axis, nt_type, nt_name_copy); + if (!n) return 0; + + xpath_ast_node* last = 0; + + while (_lexer.current() == lex_open_square_brace) + { + _lexer.next(); + + xpath_ast_node* expr = parse_expression(); + if (!expr) return 0; + + xpath_ast_node* pred = alloc_node(ast_predicate, 0, expr, predicate_default); + if (!pred) return 0; + + if (_lexer.current() != lex_close_square_brace) + return error("Expected ']' to match an opening '['"); + _lexer.next(); + + if (last) last->set_next(pred); + else n->set_right(pred); + + last = pred; + } + + return n; + } + + // RelativeLocationPath ::= Step | RelativeLocationPath '/' Step | RelativeLocationPath '//' Step + xpath_ast_node* parse_relative_location_path(xpath_ast_node* set) + { + xpath_ast_node* n = parse_step(set); + if (!n) return 0; + + while (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + { + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + } + + n = parse_step(n); + if (!n) return 0; + } + + return n; + } + + // LocationPath ::= RelativeLocationPath | AbsoluteLocationPath + // AbsoluteLocationPath ::= '/' RelativeLocationPath? | '//' RelativeLocationPath + xpath_ast_node* parse_location_path() + { + if (_lexer.current() == lex_slash) + { + _lexer.next(); + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; + + // relative location path can start from axis_attribute, dot, double_dot, multiply and string lexemes; any other lexeme means standalone root path + lexeme_t l = _lexer.current(); + + if (l == lex_string || l == lex_axis_attribute || l == lex_dot || l == lex_double_dot || l == lex_multiply) + return parse_relative_location_path(n); + else + return n; + } + else if (_lexer.current() == lex_double_slash) + { + _lexer.next(); + + xpath_ast_node* n = alloc_node(ast_step_root, xpath_type_node_set); + if (!n) return 0; + + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + + return parse_relative_location_path(n); + } + + // else clause moved outside of if because of bogus warning 'control may reach end of non-void function being inlined' in gcc 4.0.1 + return parse_relative_location_path(0); + } + + // PathExpr ::= LocationPath + // | FilterExpr + // | FilterExpr '/' RelativeLocationPath + // | FilterExpr '//' RelativeLocationPath + // UnionExpr ::= PathExpr | UnionExpr '|' PathExpr + // UnaryExpr ::= UnionExpr | '-' UnaryExpr + xpath_ast_node* parse_path_or_unary_expression() + { + // Clarification. + // PathExpr begins with either LocationPath or FilterExpr. + // FilterExpr begins with PrimaryExpr + // PrimaryExpr begins with '$' in case of it being a variable reference, + // '(' in case of it being an expression, string literal, number constant or + // function call. + if (_lexer.current() == lex_var_ref || _lexer.current() == lex_open_brace || + _lexer.current() == lex_quoted_string || _lexer.current() == lex_number || + _lexer.current() == lex_string) + { + if (_lexer.current() == lex_string) + { + // This is either a function call, or not - if not, we shall proceed with location path + const char_t* state = _lexer.state(); + + while (PUGI__IS_CHARTYPE(*state, ct_space)) ++state; + + if (*state != '(') + return parse_location_path(); + + // This looks like a function call; however this still can be a node-test. Check it. + if (parse_node_test_type(_lexer.contents()) != nodetest_none) + return parse_location_path(); + } + + xpath_ast_node* n = parse_filter_expression(); + if (!n) return 0; + + if (_lexer.current() == lex_slash || _lexer.current() == lex_double_slash) + { + lexeme_t l = _lexer.current(); + _lexer.next(); + + if (l == lex_double_slash) + { + if (n->rettype() != xpath_type_node_set) + return error("Step has to be applied to node set"); + + n = alloc_node(ast_step, n, axis_descendant_or_self, nodetest_type_node, 0); + if (!n) return 0; + } + + // select from location path + return parse_relative_location_path(n); + } + + return n; + } + else if (_lexer.current() == lex_minus) + { + _lexer.next(); + + // precedence 7+ - only parses union expressions + xpath_ast_node* n = parse_expression(7); + if (!n) return 0; + + return alloc_node(ast_op_negate, xpath_type_number, n); + } + else + { + return parse_location_path(); + } + } + + struct binary_op_t + { + ast_type_t asttype; + xpath_value_type rettype; + int precedence; + + binary_op_t(): asttype(ast_unknown), rettype(xpath_type_none), precedence(0) + { + } + + binary_op_t(ast_type_t asttype_, xpath_value_type rettype_, int precedence_): asttype(asttype_), rettype(rettype_), precedence(precedence_) + { + } + + static binary_op_t parse(xpath_lexer& lexer) + { + switch (lexer.current()) + { + case lex_string: + if (lexer.contents() == PUGIXML_TEXT("or")) + return binary_op_t(ast_op_or, xpath_type_boolean, 1); + else if (lexer.contents() == PUGIXML_TEXT("and")) + return binary_op_t(ast_op_and, xpath_type_boolean, 2); + else if (lexer.contents() == PUGIXML_TEXT("div")) + return binary_op_t(ast_op_divide, xpath_type_number, 6); + else if (lexer.contents() == PUGIXML_TEXT("mod")) + return binary_op_t(ast_op_mod, xpath_type_number, 6); + else + return binary_op_t(); + + case lex_equal: + return binary_op_t(ast_op_equal, xpath_type_boolean, 3); + + case lex_not_equal: + return binary_op_t(ast_op_not_equal, xpath_type_boolean, 3); + + case lex_less: + return binary_op_t(ast_op_less, xpath_type_boolean, 4); + + case lex_greater: + return binary_op_t(ast_op_greater, xpath_type_boolean, 4); + + case lex_less_or_equal: + return binary_op_t(ast_op_less_or_equal, xpath_type_boolean, 4); + + case lex_greater_or_equal: + return binary_op_t(ast_op_greater_or_equal, xpath_type_boolean, 4); + + case lex_plus: + return binary_op_t(ast_op_add, xpath_type_number, 5); + + case lex_minus: + return binary_op_t(ast_op_subtract, xpath_type_number, 5); + + case lex_multiply: + return binary_op_t(ast_op_multiply, xpath_type_number, 6); + + case lex_union: + return binary_op_t(ast_op_union, xpath_type_node_set, 7); + + default: + return binary_op_t(); + } + } + }; + + xpath_ast_node* parse_expression_rec(xpath_ast_node* lhs, int limit) + { + binary_op_t op = binary_op_t::parse(_lexer); + + while (op.asttype != ast_unknown && op.precedence >= limit) + { + _lexer.next(); + + xpath_ast_node* rhs = parse_path_or_unary_expression(); + if (!rhs) return 0; + + binary_op_t nextop = binary_op_t::parse(_lexer); + + while (nextop.asttype != ast_unknown && nextop.precedence > op.precedence) + { + rhs = parse_expression_rec(rhs, nextop.precedence); + if (!rhs) return 0; + + nextop = binary_op_t::parse(_lexer); + } + + if (op.asttype == ast_op_union && (lhs->rettype() != xpath_type_node_set || rhs->rettype() != xpath_type_node_set)) + return error("Union operator has to be applied to node sets"); + + lhs = alloc_node(op.asttype, op.rettype, lhs, rhs); + if (!lhs) return 0; + + op = binary_op_t::parse(_lexer); + } + + return lhs; + } + + // Expr ::= OrExpr + // OrExpr ::= AndExpr | OrExpr 'or' AndExpr + // AndExpr ::= EqualityExpr | AndExpr 'and' EqualityExpr + // EqualityExpr ::= RelationalExpr + // | EqualityExpr '=' RelationalExpr + // | EqualityExpr '!=' RelationalExpr + // RelationalExpr ::= AdditiveExpr + // | RelationalExpr '<' AdditiveExpr + // | RelationalExpr '>' AdditiveExpr + // | RelationalExpr '<=' AdditiveExpr + // | RelationalExpr '>=' AdditiveExpr + // AdditiveExpr ::= MultiplicativeExpr + // | AdditiveExpr '+' MultiplicativeExpr + // | AdditiveExpr '-' MultiplicativeExpr + // MultiplicativeExpr ::= UnaryExpr + // | MultiplicativeExpr '*' UnaryExpr + // | MultiplicativeExpr 'div' UnaryExpr + // | MultiplicativeExpr 'mod' UnaryExpr + xpath_ast_node* parse_expression(int limit = 0) + { + xpath_ast_node* n = parse_path_or_unary_expression(); + if (!n) return 0; + + return parse_expression_rec(n, limit); + } + + xpath_parser(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result): _alloc(alloc), _lexer(query), _query(query), _variables(variables), _result(result) + { + } + + xpath_ast_node* parse() + { + xpath_ast_node* n = parse_expression(); + if (!n) return 0; + + // check if there are unparsed tokens left + if (_lexer.current() != lex_eof) + return error("Incorrect query"); + + return n; + } + + static xpath_ast_node* parse(const char_t* query, xpath_variable_set* variables, xpath_allocator* alloc, xpath_parse_result* result) + { + xpath_parser parser(query, variables, alloc, result); + + return parser.parse(); + } + }; + + struct xpath_query_impl + { + static xpath_query_impl* create() + { + void* memory = xml_memory::allocate(sizeof(xpath_query_impl)); + if (!memory) return 0; + + return new (memory) xpath_query_impl(); + } + + static void destroy(xpath_query_impl* impl) + { + // free all allocated pages + impl->alloc.release(); + + // free allocator memory (with the first page) + xml_memory::deallocate(impl); + } + + xpath_query_impl(): root(0), alloc(&block, &oom), oom(false) + { + block.next = 0; + block.capacity = sizeof(block.data); + } + + xpath_ast_node* root; + xpath_allocator alloc; + xpath_memory_block block; + bool oom; + }; + + PUGI__FN impl::xpath_ast_node* evaluate_node_set_prepare(xpath_query_impl* impl) + { + if (!impl) return 0; + + if (impl->root->rettype() != xpath_type_node_set) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return 0; + #else + xpath_parse_result res; + res.error = "Expression does not evaluate to node set"; + + throw xpath_exception(res); + #endif + } + + return impl->root; + } +PUGI__NS_END + +namespace pugi +{ +#ifndef PUGIXML_NO_EXCEPTIONS + PUGI__FN xpath_exception::xpath_exception(const xpath_parse_result& result_): _result(result_) + { + assert(_result.error); + } + + PUGI__FN const char* xpath_exception::what() const throw() + { + return _result.error; + } + + PUGI__FN const xpath_parse_result& xpath_exception::result() const + { + return _result; + } +#endif + + PUGI__FN xpath_node::xpath_node() + { + } + + PUGI__FN xpath_node::xpath_node(const xml_node& node_): _node(node_) + { + } + + PUGI__FN xpath_node::xpath_node(const xml_attribute& attribute_, const xml_node& parent_): _node(attribute_ ? parent_ : xml_node()), _attribute(attribute_) + { + } + + PUGI__FN xml_node xpath_node::node() const + { + return _attribute ? xml_node() : _node; + } + + PUGI__FN xml_attribute xpath_node::attribute() const + { + return _attribute; + } + + PUGI__FN xml_node xpath_node::parent() const + { + return _attribute ? _node : _node.parent(); + } + + PUGI__FN static void unspecified_bool_xpath_node(xpath_node***) + { + } + + PUGI__FN xpath_node::operator xpath_node::unspecified_bool_type() const + { + return (_node || _attribute) ? unspecified_bool_xpath_node : 0; + } + + PUGI__FN bool xpath_node::operator!() const + { + return !(_node || _attribute); + } + + PUGI__FN bool xpath_node::operator==(const xpath_node& n) const + { + return _node == n._node && _attribute == n._attribute; + } + + PUGI__FN bool xpath_node::operator!=(const xpath_node& n) const + { + return _node != n._node || _attribute != n._attribute; + } + +#ifdef __BORLANDC__ + PUGI__FN bool operator&&(const xpath_node& lhs, bool rhs) + { + return (bool)lhs && rhs; + } + + PUGI__FN bool operator||(const xpath_node& lhs, bool rhs) + { + return (bool)lhs || rhs; + } +#endif + + PUGI__FN void xpath_node_set::_assign(const_iterator begin_, const_iterator end_, type_t type_) + { + assert(begin_ <= end_); + + size_t size_ = static_cast(end_ - begin_); + + if (size_ <= 1) + { + // deallocate old buffer + if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + + // use internal buffer + if (begin_ != end_) _storage = *begin_; + + _begin = &_storage; + _end = &_storage + size_; + _type = type_; + } + else + { + // make heap copy + xpath_node* storage = static_cast(impl::xml_memory::allocate(size_ * sizeof(xpath_node))); + + if (!storage) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return; + #else + throw std::bad_alloc(); + #endif + } + + memcpy(storage, begin_, size_ * sizeof(xpath_node)); + + // deallocate old buffer + if (_begin != &_storage) impl::xml_memory::deallocate(_begin); + + // finalize + _begin = storage; + _end = storage + size_; + _type = type_; + } + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN void xpath_node_set::_move(xpath_node_set& rhs) PUGIXML_NOEXCEPT + { + _type = rhs._type; + _storage = rhs._storage; + _begin = (rhs._begin == &rhs._storage) ? &_storage : rhs._begin; + _end = _begin + (rhs._end - rhs._begin); + + rhs._type = type_unsorted; + rhs._begin = &rhs._storage; + rhs._end = rhs._begin; + } +#endif + + PUGI__FN xpath_node_set::xpath_node_set(): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + } + + PUGI__FN xpath_node_set::xpath_node_set(const_iterator begin_, const_iterator end_, type_t type_): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _assign(begin_, end_, type_); + } + + PUGI__FN xpath_node_set::~xpath_node_set() + { + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + } + + PUGI__FN xpath_node_set::xpath_node_set(const xpath_node_set& ns): _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _assign(ns._begin, ns._end, ns._type); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(const xpath_node_set& ns) + { + if (this == &ns) return *this; + + _assign(ns._begin, ns._end, ns._type); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_node_set::xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT: _type(type_unsorted), _begin(&_storage), _end(&_storage) + { + _move(rhs); + } + + PUGI__FN xpath_node_set& xpath_node_set::operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT + { + if (this == &rhs) return *this; + + if (_begin != &_storage) + impl::xml_memory::deallocate(_begin); + + _move(rhs); + + return *this; + } +#endif + + PUGI__FN xpath_node_set::type_t xpath_node_set::type() const + { + return _type; + } + + PUGI__FN size_t xpath_node_set::size() const + { + return _end - _begin; + } + + PUGI__FN bool xpath_node_set::empty() const + { + return _begin == _end; + } + + PUGI__FN const xpath_node& xpath_node_set::operator[](size_t index) const + { + assert(index < size()); + return _begin[index]; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::begin() const + { + return _begin; + } + + PUGI__FN xpath_node_set::const_iterator xpath_node_set::end() const + { + return _end; + } + + PUGI__FN void xpath_node_set::sort(bool reverse) + { + _type = impl::xpath_sort(_begin, _end, _type, reverse); + } + + PUGI__FN xpath_node xpath_node_set::first() const + { + return impl::xpath_first(_begin, _end, _type); + } + + PUGI__FN xpath_parse_result::xpath_parse_result(): error("Internal error"), offset(0) + { + } + + PUGI__FN xpath_parse_result::operator bool() const + { + return error == 0; + } + + PUGI__FN const char* xpath_parse_result::description() const + { + return error ? error : "No error"; + } + + PUGI__FN xpath_variable::xpath_variable(xpath_value_type type_): _type(type_), _next(0) + { + } + + PUGI__FN const char_t* xpath_variable::name() const + { + switch (_type) + { + case xpath_type_node_set: + return static_cast(this)->name; + + case xpath_type_number: + return static_cast(this)->name; + + case xpath_type_string: + return static_cast(this)->name; + + case xpath_type_boolean: + return static_cast(this)->name; + + default: + assert(false && "Invalid variable type"); // unreachable + return 0; + } + } + + PUGI__FN xpath_value_type xpath_variable::type() const + { + return _type; + } + + PUGI__FN bool xpath_variable::get_boolean() const + { + return (_type == xpath_type_boolean) ? static_cast(this)->value : false; + } + + PUGI__FN double xpath_variable::get_number() const + { + return (_type == xpath_type_number) ? static_cast(this)->value : impl::gen_nan(); + } + + PUGI__FN const char_t* xpath_variable::get_string() const + { + const char_t* value = (_type == xpath_type_string) ? static_cast(this)->value : 0; + return value ? value : PUGIXML_TEXT(""); + } + + PUGI__FN const xpath_node_set& xpath_variable::get_node_set() const + { + return (_type == xpath_type_node_set) ? static_cast(this)->value : impl::dummy_node_set; + } + + PUGI__FN bool xpath_variable::set(bool value) + { + if (_type != xpath_type_boolean) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(double value) + { + if (_type != xpath_type_number) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN bool xpath_variable::set(const char_t* value) + { + if (_type != xpath_type_string) return false; + + impl::xpath_variable_string* var = static_cast(this); + + // duplicate string + size_t size = (impl::strlength(value) + 1) * sizeof(char_t); + + char_t* copy = static_cast(impl::xml_memory::allocate(size)); + if (!copy) return false; + + memcpy(copy, value, size); + + // replace old string + if (var->value) impl::xml_memory::deallocate(var->value); + var->value = copy; + + return true; + } + + PUGI__FN bool xpath_variable::set(const xpath_node_set& value) + { + if (_type != xpath_type_node_set) return false; + + static_cast(this)->value = value; + return true; + } + + PUGI__FN xpath_variable_set::xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + } + + PUGI__FN xpath_variable_set::~xpath_variable_set() + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _destroy(_data[i]); + } + + PUGI__FN xpath_variable_set::xpath_variable_set(const xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + _data[i] = 0; + + _assign(rhs); + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(const xpath_variable_set& rhs) + { + if (this == &rhs) return *this; + + _assign(rhs); + + return *this; + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_variable_set::xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + } + + PUGI__FN xpath_variable_set& xpath_variable_set::operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + _destroy(_data[i]); + + _data[i] = rhs._data[i]; + rhs._data[i] = 0; + } + + return *this; + } +#endif + + PUGI__FN void xpath_variable_set::_assign(const xpath_variable_set& rhs) + { + xpath_variable_set temp; + + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + if (rhs._data[i] && !_clone(rhs._data[i], &temp._data[i])) + return; + + _swap(temp); + } + + PUGI__FN void xpath_variable_set::_swap(xpath_variable_set& rhs) + { + for (size_t i = 0; i < sizeof(_data) / sizeof(_data[0]); ++i) + { + xpath_variable* chain = _data[i]; + + _data[i] = rhs._data[i]; + rhs._data[i] = chain; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::_find(const char_t* name) const + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var; + + return 0; + } + + PUGI__FN bool xpath_variable_set::_clone(xpath_variable* var, xpath_variable** out_result) + { + xpath_variable* last = 0; + + while (var) + { + // allocate storage for new variable + xpath_variable* nvar = impl::new_xpath_variable(var->_type, var->name()); + if (!nvar) return false; + + // link the variable to the result immediately to handle failures gracefully + if (last) + last->_next = nvar; + else + *out_result = nvar; + + last = nvar; + + // copy the value; this can fail due to out-of-memory conditions + if (!impl::copy_xpath_variable(nvar, var)) return false; + + var = var->_next; + } + + return true; + } + + PUGI__FN void xpath_variable_set::_destroy(xpath_variable* var) + { + while (var) + { + xpath_variable* next = var->_next; + + impl::delete_xpath_variable(var->_type, var); + + var = next; + } + } + + PUGI__FN xpath_variable* xpath_variable_set::add(const char_t* name, xpath_value_type type) + { + const size_t hash_size = sizeof(_data) / sizeof(_data[0]); + size_t hash = impl::hash_string(name) % hash_size; + + // look for existing variable + for (xpath_variable* var = _data[hash]; var; var = var->_next) + if (impl::strequal(var->name(), name)) + return var->type() == type ? var : 0; + + // add new variable + xpath_variable* result = impl::new_xpath_variable(type, name); + + if (result) + { + result->_next = _data[hash]; + + _data[hash] = result; + } + + return result; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, bool value) + { + xpath_variable* var = add(name, xpath_type_boolean); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, double value) + { + xpath_variable* var = add(name, xpath_type_number); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const char_t* value) + { + xpath_variable* var = add(name, xpath_type_string); + return var ? var->set(value) : false; + } + + PUGI__FN bool xpath_variable_set::set(const char_t* name, const xpath_node_set& value) + { + xpath_variable* var = add(name, xpath_type_node_set); + return var ? var->set(value) : false; + } + + PUGI__FN xpath_variable* xpath_variable_set::get(const char_t* name) + { + return _find(name); + } + + PUGI__FN const xpath_variable* xpath_variable_set::get(const char_t* name) const + { + return _find(name); + } + + PUGI__FN xpath_query::xpath_query(const char_t* query, xpath_variable_set* variables): _impl(0) + { + impl::xpath_query_impl* qimpl = impl::xpath_query_impl::create(); + + if (!qimpl) + { + #ifdef PUGIXML_NO_EXCEPTIONS + _result.error = "Out of memory"; + #else + throw std::bad_alloc(); + #endif + } + else + { + using impl::auto_deleter; // MSVC7 workaround + auto_deleter impl(qimpl, impl::xpath_query_impl::destroy); + + qimpl->root = impl::xpath_parser::parse(query, variables, &qimpl->alloc, &_result); + + if (qimpl->root) + { + qimpl->root->optimize(&qimpl->alloc); + + _impl = impl.release(); + _result.error = 0; + } + else + { + #ifdef PUGIXML_NO_EXCEPTIONS + if (qimpl->oom) _result.error = "Out of memory"; + #else + if (qimpl->oom) throw std::bad_alloc(); + throw xpath_exception(_result); + #endif + } + } + } + + PUGI__FN xpath_query::xpath_query(): _impl(0) + { + } + + PUGI__FN xpath_query::~xpath_query() + { + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + } + +#ifdef PUGIXML_HAS_MOVE + PUGI__FN xpath_query::xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT + { + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + } + + PUGI__FN xpath_query& xpath_query::operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT + { + if (this == &rhs) return *this; + + if (_impl) + impl::xpath_query_impl::destroy(static_cast(_impl)); + + _impl = rhs._impl; + _result = rhs._result; + rhs._impl = 0; + rhs._result = xpath_parse_result(); + + return *this; + } +#endif + + PUGI__FN xpath_value_type xpath_query::return_type() const + { + if (!_impl) return xpath_type_none; + + return static_cast(_impl)->root->rettype(); + } + + PUGI__FN bool xpath_query::evaluate_boolean(const xpath_node& n) const + { + if (!_impl) return false; + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + bool r = static_cast(_impl)->root->eval_boolean(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return false; + #else + throw std::bad_alloc(); + #endif + } + + return r; + } + + PUGI__FN double xpath_query::evaluate_number(const xpath_node& n) const + { + if (!_impl) return impl::gen_nan(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + double r = static_cast(_impl)->root->eval_number(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return impl::gen_nan(); + #else + throw std::bad_alloc(); + #endif + } + + return r; + } + +#ifndef PUGIXML_NO_STL + PUGI__FN string_t xpath_query::evaluate_string(const xpath_node& n) const + { + if (!_impl) return string_t(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_string r = static_cast(_impl)->root->eval_string(c, sd.stack); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return string_t(); + #else + throw std::bad_alloc(); + #endif + } + + return string_t(r.c_str(), r.length()); + } +#endif + + PUGI__FN size_t xpath_query::evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const + { + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_string r = _impl ? static_cast(_impl)->root->eval_string(c, sd.stack) : impl::xpath_string(); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + r = impl::xpath_string(); + #else + throw std::bad_alloc(); + #endif + } + + size_t full_size = r.length() + 1; + + if (capacity > 0) + { + size_t size = (full_size < capacity) ? full_size : capacity; + assert(size > 0); + + memcpy(buffer, r.c_str(), (size - 1) * sizeof(char_t)); + buffer[size - 1] = 0; + } + + return full_size; + } + + PUGI__FN xpath_node_set xpath_query::evaluate_node_set(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node_set(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_all); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node_set(); + #else + throw std::bad_alloc(); + #endif + } + + return xpath_node_set(r.begin(), r.end(), r.type()); + } + + PUGI__FN xpath_node xpath_query::evaluate_node(const xpath_node& n) const + { + impl::xpath_ast_node* root = impl::evaluate_node_set_prepare(static_cast(_impl)); + if (!root) return xpath_node(); + + impl::xpath_context c(n, 1, 1); + impl::xpath_stack_data sd; + + impl::xpath_node_set_raw r = root->eval_node_set(c, sd.stack, impl::nodeset_eval_first); + + if (sd.oom) + { + #ifdef PUGIXML_NO_EXCEPTIONS + return xpath_node(); + #else + throw std::bad_alloc(); + #endif + } + + return r.first(); + } + + PUGI__FN const xpath_parse_result& xpath_query::result() const + { + return _result; + } + + PUGI__FN static void unspecified_bool_xpath_query(xpath_query***) + { + } + + PUGI__FN xpath_query::operator xpath_query::unspecified_bool_type() const + { + return _impl ? unspecified_bool_xpath_query : 0; + } + + PUGI__FN bool xpath_query::operator!() const + { + return !_impl; + } + + PUGI__FN xpath_node xml_node::select_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node(*this); + } + + PUGI__FN xpath_node xml_node::select_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node_set(*this); + } + + PUGI__FN xpath_node_set xml_node::select_nodes(const xpath_query& query) const + { + return query.evaluate_node_set(*this); + } + + PUGI__FN xpath_node xml_node::select_single_node(const char_t* query, xpath_variable_set* variables) const + { + xpath_query q(query, variables); + return q.evaluate_node(*this); + } + + PUGI__FN xpath_node xml_node::select_single_node(const xpath_query& query) const + { + return query.evaluate_node(*this); + } +} + +#endif + +#ifdef __BORLANDC__ +# pragma option pop +#endif + +// Intel C++ does not properly keep warning state for function templates, +// so popping warning state at the end of translation unit leads to warnings in the middle. +#if defined(_MSC_VER) && !defined(__INTEL_COMPILER) +# pragma warning(pop) +#endif + +#if defined(_MSC_VER) && defined(__c2__) +# pragma clang diagnostic pop +#endif + +// Undefine all local macros (makes sure we're not leaking macros in header-only mode) +#undef PUGI__NO_INLINE +#undef PUGI__UNLIKELY +#undef PUGI__STATIC_ASSERT +#undef PUGI__DMC_VOLATILE +#undef PUGI__UNSIGNED_OVERFLOW +#undef PUGI__MSVC_CRT_VERSION +#undef PUGI__SNPRINTF +#undef PUGI__NS_BEGIN +#undef PUGI__NS_END +#undef PUGI__FN +#undef PUGI__FN_NO_INLINE +#undef PUGI__GETHEADER_IMPL +#undef PUGI__GETPAGE_IMPL +#undef PUGI__GETPAGE +#undef PUGI__NODETYPE +#undef PUGI__IS_CHARTYPE_IMPL +#undef PUGI__IS_CHARTYPE +#undef PUGI__IS_CHARTYPEX +#undef PUGI__ENDSWITH +#undef PUGI__SKIPWS +#undef PUGI__OPTSET +#undef PUGI__PUSHNODE +#undef PUGI__POPNODE +#undef PUGI__SCANFOR +#undef PUGI__SCANWHILE +#undef PUGI__SCANWHILE_UNROLL +#undef PUGI__ENDSEG +#undef PUGI__THROW_ERROR +#undef PUGI__CHECK_ERROR + +#endif + +/** + * Copyright (c) 2006-2018 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff -Nru leanify-0.4.3/lib/pugixml/pugixml.hpp leanify-0.4.3+git20181014/lib/pugixml/pugixml.hpp --- leanify-0.4.3/lib/pugixml/pugixml.hpp 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/pugixml/pugixml.hpp 2018-05-05 21:00:58.000000000 +0000 @@ -0,0 +1,1461 @@ +/** + * pugixml parser - version 1.9 + * -------------------------------------------------------- + * Copyright (C) 2006-2018, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com) + * Report bugs and download new versions at http://pugixml.org/ + * + * This library is distributed under the MIT License. See notice at the end + * of this file. + * + * This work is based on the pugxml parser, which is: + * Copyright (C) 2003, by Kristen Wegner (kristen@tima.net) + */ + +#ifndef PUGIXML_VERSION +// Define version macro; evaluates to major * 100 + minor so that it's safe to use in less-than comparisons +# define PUGIXML_VERSION 190 +#endif + +// Include user configuration file (this can define various configuration macros) +#include "pugiconfig.hpp" + +#ifndef HEADER_PUGIXML_HPP +#define HEADER_PUGIXML_HPP + +// Include stddef.h for size_t and ptrdiff_t +#include + +// Include exception header for XPath +#if !defined(PUGIXML_NO_XPATH) && !defined(PUGIXML_NO_EXCEPTIONS) +# include +#endif + +// Include STL headers +#ifndef PUGIXML_NO_STL +# include +# include +# include +#endif + +// Macro for deprecated features +#ifndef PUGIXML_DEPRECATED +# if defined(__GNUC__) +# define PUGIXML_DEPRECATED __attribute__((deprecated)) +# elif defined(_MSC_VER) && _MSC_VER >= 1300 +# define PUGIXML_DEPRECATED __declspec(deprecated) +# else +# define PUGIXML_DEPRECATED +# endif +#endif + +// If no API is defined, assume default +#ifndef PUGIXML_API +# define PUGIXML_API +#endif + +// If no API for classes is defined, assume default +#ifndef PUGIXML_CLASS +# define PUGIXML_CLASS PUGIXML_API +#endif + +// If no API for functions is defined, assume default +#ifndef PUGIXML_FUNCTION +# define PUGIXML_FUNCTION PUGIXML_API +#endif + +// If the platform is known to have long long support, enable long long functions +#ifndef PUGIXML_HAS_LONG_LONG +# if __cplusplus >= 201103 +# define PUGIXML_HAS_LONG_LONG +# elif defined(_MSC_VER) && _MSC_VER >= 1400 +# define PUGIXML_HAS_LONG_LONG +# endif +#endif + +// If the platform is known to have move semantics support, compile move ctor/operator implementation +#ifndef PUGIXML_HAS_MOVE +# if __cplusplus >= 201103 +# define PUGIXML_HAS_MOVE +# elif defined(_MSC_VER) && _MSC_VER >= 1600 +# define PUGIXML_HAS_MOVE +# endif +#endif + +// If C++ is 2011 or higher, add 'noexcept' specifiers +#ifndef PUGIXML_NOEXCEPT +# if __cplusplus >= 201103 +# define PUGIXML_NOEXCEPT noexcept +# elif defined(_MSC_VER) && _MSC_VER >= 1900 +# define PUGIXML_NOEXCEPT noexcept +# else +# define PUGIXML_NOEXCEPT +# endif +#endif + +// Some functions can not be noexcept in compact mode +#ifdef PUGIXML_COMPACT +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT +#else +# define PUGIXML_NOEXCEPT_IF_NOT_COMPACT PUGIXML_NOEXCEPT +#endif + +// If C++ is 2011 or higher, add 'override' qualifiers +#ifndef PUGIXML_OVERRIDE +# if __cplusplus >= 201103 +# define PUGIXML_OVERRIDE override +# elif defined(_MSC_VER) && _MSC_VER >= 1700 +# define PUGIXML_OVERRIDE override +# else +# define PUGIXML_OVERRIDE +# endif +#endif + +// Character interface macros +#ifdef PUGIXML_WCHAR_MODE +# define PUGIXML_TEXT(t) L ## t +# define PUGIXML_CHAR wchar_t +#else +# define PUGIXML_TEXT(t) t +# define PUGIXML_CHAR char +#endif + +namespace pugi +{ + // Character type used for all internal storage and operations; depends on PUGIXML_WCHAR_MODE + typedef PUGIXML_CHAR char_t; + +#ifndef PUGIXML_NO_STL + // String type used for operations that work with STL string; depends on PUGIXML_WCHAR_MODE + typedef std::basic_string, std::allocator > string_t; +#endif +} + +// The PugiXML namespace +namespace pugi +{ + // Tree node types + enum xml_node_type + { + node_null, // Empty (null) node handle + node_document, // A document tree's absolute root + node_element, // Element tag, i.e. '' + node_pcdata, // Plain character data, i.e. 'text' + node_cdata, // Character data, i.e. '' + node_comment, // Comment tag, i.e. '' + node_pi, // Processing instruction, i.e. '' + node_declaration, // Document declaration, i.e. '' + node_doctype // Document type declaration, i.e. '' + }; + + // Parsing options + + // Minimal parsing mode (equivalent to turning all other flags off). + // Only elements and PCDATA sections are added to the DOM tree, no text conversions are performed. + const unsigned int parse_minimal = 0x0000; + + // This flag determines if processing instructions (node_pi) are added to the DOM tree. This flag is off by default. + const unsigned int parse_pi = 0x0001; + + // This flag determines if comments (node_comment) are added to the DOM tree. This flag is off by default. + const unsigned int parse_comments = 0x0002; + + // This flag determines if CDATA sections (node_cdata) are added to the DOM tree. This flag is on by default. + const unsigned int parse_cdata = 0x0004; + + // This flag determines if plain character data (node_pcdata) that consist only of whitespace are added to the DOM tree. + // This flag is off by default; turning it on usually results in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata = 0x0008; + + // This flag determines if character and entity references are expanded during parsing. This flag is on by default. + const unsigned int parse_escapes = 0x0010; + + // This flag determines if EOL characters are normalized (converted to #xA) during parsing. This flag is on by default. + const unsigned int parse_eol = 0x0020; + + // This flag determines if attribute values are normalized using CDATA normalization rules during parsing. This flag is on by default. + const unsigned int parse_wconv_attribute = 0x0040; + + // This flag determines if attribute values are normalized using NMTOKENS normalization rules during parsing. This flag is off by default. + const unsigned int parse_wnorm_attribute = 0x0080; + + // This flag determines if document declaration (node_declaration) is added to the DOM tree. This flag is off by default. + const unsigned int parse_declaration = 0x0100; + + // This flag determines if document type declaration (node_doctype) is added to the DOM tree. This flag is off by default. + const unsigned int parse_doctype = 0x0200; + + // This flag determines if plain character data (node_pcdata) that is the only child of the parent node and that consists only + // of whitespace is added to the DOM tree. + // This flag is off by default; turning it on may result in slower parsing and more memory consumption. + const unsigned int parse_ws_pcdata_single = 0x0400; + + // This flag determines if leading and trailing whitespace is to be removed from plain character data. This flag is off by default. + const unsigned int parse_trim_pcdata = 0x0800; + + // This flag determines if plain character data that does not have a parent node is added to the DOM tree, and if an empty document + // is a valid document. This flag is off by default. + const unsigned int parse_fragment = 0x1000; + + // This flag determines if plain character data is be stored in the parent element's value. This significantly changes the structure of + // the document; this flag is only recommended for parsing documents with many PCDATA nodes in memory-constrained environments. + // This flag is off by default. + const unsigned int parse_embed_pcdata = 0x2000; + + // The default parsing mode. + // Elements, PCDATA and CDATA sections are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_default = parse_cdata | parse_escapes | parse_wconv_attribute | parse_eol; + + // The full parsing mode. + // Nodes of all types are added to the DOM tree, character/reference entities are expanded, + // End-of-Line characters are normalized, attribute values are normalized using CDATA normalization rules. + const unsigned int parse_full = parse_default | parse_pi | parse_comments | parse_declaration | parse_doctype; + + // These flags determine the encoding of input data for XML document + enum xml_encoding + { + encoding_auto, // Auto-detect input encoding using BOM or < / class xml_object_range + { + public: + typedef It const_iterator; + typedef It iterator; + + xml_object_range(It b, It e): _begin(b), _end(e) + { + } + + It begin() const { return _begin; } + It end() const { return _end; } + + private: + It _begin, _end; + }; + + // Writer interface for node printing (see xml_node::print) + class PUGIXML_CLASS xml_writer + { + public: + virtual ~xml_writer() {} + + // Write memory chunk into stream/file/whatever + virtual void write(const void* data, size_t size) = 0; + }; + + // xml_writer implementation for FILE* + class PUGIXML_CLASS xml_writer_file: public xml_writer + { + public: + // Construct writer from a FILE* object; void* is used to avoid header dependencies on stdio + xml_writer_file(void* file); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + void* file; + }; + + #ifndef PUGIXML_NO_STL + // xml_writer implementation for streams + class PUGIXML_CLASS xml_writer_stream: public xml_writer + { + public: + // Construct writer from an output stream object + xml_writer_stream(std::basic_ostream >& stream); + xml_writer_stream(std::basic_ostream >& stream); + + virtual void write(const void* data, size_t size) PUGIXML_OVERRIDE; + + private: + std::basic_ostream >* narrow_stream; + std::basic_ostream >* wide_stream; + }; + #endif + + // A light-weight handle for manipulating attributes in DOM tree + class PUGIXML_CLASS xml_attribute + { + friend class xml_attribute_iterator; + friend class xml_node; + + private: + xml_attribute_struct* _attr; + + typedef void (*unspecified_bool_type)(xml_attribute***); + + public: + // Default constructor. Constructs an empty attribute. + xml_attribute(); + + // Constructs attribute from internal pointer + explicit xml_attribute(xml_attribute_struct* attr); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped attribute pointers) + bool operator==(const xml_attribute& r) const; + bool operator!=(const xml_attribute& r) const; + bool operator<(const xml_attribute& r) const; + bool operator>(const xml_attribute& r) const; + bool operator<=(const xml_attribute& r) const; + bool operator>=(const xml_attribute& r) const; + + // Check if attribute is empty + bool empty() const; + + // Get attribute name/value, or "" if attribute is empty + const char_t* name() const; + const char_t* value() const; + + // Get attribute value, or the default value if attribute is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get attribute value as a number, or the default value if conversion did not succeed or attribute is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get attribute value as bool (returns true if first character is in '1tTyY' set), or the default value if attribute is empty + bool as_bool(bool def = false) const; + + // Set attribute name/value (returns false if attribute is empty or there is not enough memory) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Set attribute value with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set_value(int rhs); + bool set_value(unsigned int rhs); + bool set_value(long rhs); + bool set_value(unsigned long rhs); + bool set_value(double rhs); + bool set_value(float rhs); + bool set_value(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set_value(long long rhs); + bool set_value(unsigned long long rhs); + #endif + + // Set attribute value (equivalent to set_value without error checking) + xml_attribute& operator=(const char_t* rhs); + xml_attribute& operator=(int rhs); + xml_attribute& operator=(unsigned int rhs); + xml_attribute& operator=(long rhs); + xml_attribute& operator=(unsigned long rhs); + xml_attribute& operator=(double rhs); + xml_attribute& operator=(float rhs); + xml_attribute& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_attribute& operator=(long long rhs); + xml_attribute& operator=(unsigned long long rhs); + #endif + + // Get next/previous attribute in the attribute list of the parent node + xml_attribute next_attribute() const; + xml_attribute previous_attribute() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_attribute_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_attribute& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_attribute& lhs, bool rhs); +#endif + + // A light-weight handle for manipulating nodes in DOM tree + class PUGIXML_CLASS xml_node + { + friend class xml_attribute_iterator; + friend class xml_node_iterator; + friend class xml_named_node_iterator; + + protected: + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_node***); + + public: + // Default constructor. Constructs an empty node. + xml_node(); + + // Constructs node from internal pointer + explicit xml_node(xml_node_struct* p); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators (compares wrapped node pointers) + bool operator==(const xml_node& r) const; + bool operator!=(const xml_node& r) const; + bool operator<(const xml_node& r) const; + bool operator>(const xml_node& r) const; + bool operator<=(const xml_node& r) const; + bool operator>=(const xml_node& r) const; + + // Check if node is empty. + bool empty() const; + + // Get node type + xml_node_type type() const; + + // Get node name, or "" if node is empty or it has no name + const char_t* name() const; + + // Get node value, or "" if node is empty or it has no value + // Note: For text node.value() does not return "text"! Use child_value() or text() methods to access text inside nodes. + const char_t* value() const; + + // Get attribute list + xml_attribute first_attribute() const; + xml_attribute last_attribute() const; + + // Get children list + xml_node first_child() const; + xml_node last_child() const; + + // Get next/previous sibling in the children list of the parent node + xml_node next_sibling() const; + xml_node previous_sibling() const; + + // Get parent node + xml_node parent() const; + + // Get root of DOM tree this node belongs to + xml_node root() const; + + // Get text object for the current node + xml_text text() const; + + // Get child, attribute or next/previous sibling with the specified name + xml_node child(const char_t* name) const; + xml_attribute attribute(const char_t* name) const; + xml_node next_sibling(const char_t* name) const; + xml_node previous_sibling(const char_t* name) const; + + // Get attribute, starting the search from a hint (and updating hint so that searching for a sequence of attributes is fast) + xml_attribute attribute(const char_t* name, xml_attribute& hint) const; + + // Get child value of current node; that is, value of the first child node of type PCDATA/CDATA + const char_t* child_value() const; + + // Get child value of child with specified name. Equivalent to child(name).child_value(). + const char_t* child_value(const char_t* name) const; + + // Set node name/value (returns false if node is empty, there is not enough memory, or node can not have name/value) + bool set_name(const char_t* rhs); + bool set_value(const char_t* rhs); + + // Add attribute with specified name. Returns added attribute, or empty attribute on errors. + xml_attribute append_attribute(const char_t* name); + xml_attribute prepend_attribute(const char_t* name); + xml_attribute insert_attribute_after(const char_t* name, const xml_attribute& attr); + xml_attribute insert_attribute_before(const char_t* name, const xml_attribute& attr); + + // Add a copy of the specified attribute. Returns added attribute, or empty attribute on errors. + xml_attribute append_copy(const xml_attribute& proto); + xml_attribute prepend_copy(const xml_attribute& proto); + xml_attribute insert_copy_after(const xml_attribute& proto, const xml_attribute& attr); + xml_attribute insert_copy_before(const xml_attribute& proto, const xml_attribute& attr); + + // Add child node with specified type. Returns added node, or empty node on errors. + xml_node append_child(xml_node_type type = node_element); + xml_node prepend_child(xml_node_type type = node_element); + xml_node insert_child_after(xml_node_type type, const xml_node& node); + xml_node insert_child_before(xml_node_type type, const xml_node& node); + + // Add child element with specified name. Returns added node, or empty node on errors. + xml_node append_child(const char_t* name); + xml_node prepend_child(const char_t* name); + xml_node insert_child_after(const char_t* name, const xml_node& node); + xml_node insert_child_before(const char_t* name, const xml_node& node); + + // Add a copy of the specified node as a child. Returns added node, or empty node on errors. + xml_node append_copy(const xml_node& proto); + xml_node prepend_copy(const xml_node& proto); + xml_node insert_copy_after(const xml_node& proto, const xml_node& node); + xml_node insert_copy_before(const xml_node& proto, const xml_node& node); + + // Move the specified node to become a child of this node. Returns moved node, or empty node on errors. + xml_node append_move(const xml_node& moved); + xml_node prepend_move(const xml_node& moved); + xml_node insert_move_after(const xml_node& moved, const xml_node& node); + xml_node insert_move_before(const xml_node& moved, const xml_node& node); + + // Remove specified attribute + bool remove_attribute(const xml_attribute& a); + bool remove_attribute(const char_t* name); + + // Remove specified child + bool remove_child(const xml_node& n); + bool remove_child(const char_t* name); + + // Parses buffer as an XML document fragment and appends all nodes as children of the current node. + // Copies/converts the buffer, so it may be deleted or changed after the function returns. + // Note: append_buffer allocates memory that has the lifetime of the owning document; removing the appended nodes does not immediately reclaim that memory. + xml_parse_result append_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Find attribute using predicate. Returns first attribute for which predicate returned true. + template xml_attribute find_attribute(Predicate pred) const + { + if (!_root) return xml_attribute(); + + for (xml_attribute attrib = first_attribute(); attrib; attrib = attrib.next_attribute()) + if (pred(attrib)) + return attrib; + + return xml_attribute(); + } + + // Find child node using predicate. Returns first child for which predicate returned true. + template xml_node find_child(Predicate pred) const + { + if (!_root) return xml_node(); + + for (xml_node node = first_child(); node; node = node.next_sibling()) + if (pred(node)) + return node; + + return xml_node(); + } + + // Find node from subtree using predicate. Returns first node from subtree (depth-first), for which predicate returned true. + template xml_node find_node(Predicate pred) const + { + if (!_root) return xml_node(); + + xml_node cur = first_child(); + + while (cur._root && cur._root != _root) + { + if (pred(cur)) return cur; + + if (cur.first_child()) cur = cur.first_child(); + else if (cur.next_sibling()) cur = cur.next_sibling(); + else + { + while (!cur.next_sibling() && cur._root != _root) cur = cur.parent(); + + if (cur._root != _root) cur = cur.next_sibling(); + } + } + + return xml_node(); + } + + // Find child node by attribute name/value + xml_node find_child_by_attribute(const char_t* name, const char_t* attr_name, const char_t* attr_value) const; + xml_node find_child_by_attribute(const char_t* attr_name, const char_t* attr_value) const; + + #ifndef PUGIXML_NO_STL + // Get the absolute node path from root as a text string. + string_t path(char_t delimiter = '/') const; + #endif + + // Search for a node by path consisting of node names and . or .. elements. + xml_node first_element_by_path(const char_t* path, char_t delimiter = '/') const; + + // Recursively traverse subtree with xml_tree_walker + bool traverse(xml_tree_walker& walker); + + #ifndef PUGIXML_NO_XPATH + // Select single node by evaluating XPath query. Returns first node from the resulting node set. + xpath_node select_node(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node select_node(const xpath_query& query) const; + + // Select node set by evaluating XPath query + xpath_node_set select_nodes(const char_t* query, xpath_variable_set* variables = 0) const; + xpath_node_set select_nodes(const xpath_query& query) const; + + // (deprecated: use select_node instead) Select single node by evaluating XPath query. + PUGIXML_DEPRECATED xpath_node select_single_node(const char_t* query, xpath_variable_set* variables = 0) const; + PUGIXML_DEPRECATED xpath_node select_single_node(const xpath_query& query) const; + + #endif + + // Print subtree using a writer object + void print(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + + #ifndef PUGIXML_NO_STL + // Print subtree to stream + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto, unsigned int depth = 0) const; + void print(std::basic_ostream >& os, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, unsigned int depth = 0) const; + #endif + + // Child nodes iterators + typedef xml_node_iterator iterator; + + iterator begin() const; + iterator end() const; + + // Attribute iterators + typedef xml_attribute_iterator attribute_iterator; + + attribute_iterator attributes_begin() const; + attribute_iterator attributes_end() const; + + // Range-based for support + xml_object_range children() const; + xml_object_range children(const char_t* name) const; + xml_object_range attributes() const; + + // Get node offset in parsed file/string (in char_t units) for debugging purposes + ptrdiff_t offset_debug() const; + + // Get hash value (unique for handles to the same object) + size_t hash_value() const; + + // Get internal pointer + xml_node_struct* internal_object() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_node& lhs, bool rhs); +#endif + + // A helper for working with text inside PCDATA nodes + class PUGIXML_CLASS xml_text + { + friend class xml_node; + + xml_node_struct* _root; + + typedef void (*unspecified_bool_type)(xml_text***); + + explicit xml_text(xml_node_struct* root); + + xml_node_struct* _data_new(); + xml_node_struct* _data() const; + + public: + // Default constructor. Constructs an empty object. + xml_text(); + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Check if text object is empty + bool empty() const; + + // Get text, or "" if object is empty + const char_t* get() const; + + // Get text, or the default value if object is empty + const char_t* as_string(const char_t* def = PUGIXML_TEXT("")) const; + + // Get text as a number, or the default value if conversion did not succeed or object is empty + int as_int(int def = 0) const; + unsigned int as_uint(unsigned int def = 0) const; + double as_double(double def = 0) const; + float as_float(float def = 0) const; + + #ifdef PUGIXML_HAS_LONG_LONG + long long as_llong(long long def = 0) const; + unsigned long long as_ullong(unsigned long long def = 0) const; + #endif + + // Get text as bool (returns true if first character is in '1tTyY' set), or the default value if object is empty + bool as_bool(bool def = false) const; + + // Set text (returns false if object is empty or there is not enough memory) + bool set(const char_t* rhs); + + // Set text with type conversion (numbers are converted to strings, boolean is converted to "true"/"false") + bool set(int rhs); + bool set(unsigned int rhs); + bool set(long rhs); + bool set(unsigned long rhs); + bool set(double rhs); + bool set(float rhs); + bool set(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + bool set(long long rhs); + bool set(unsigned long long rhs); + #endif + + // Set text (equivalent to set without error checking) + xml_text& operator=(const char_t* rhs); + xml_text& operator=(int rhs); + xml_text& operator=(unsigned int rhs); + xml_text& operator=(long rhs); + xml_text& operator=(unsigned long rhs); + xml_text& operator=(double rhs); + xml_text& operator=(float rhs); + xml_text& operator=(bool rhs); + + #ifdef PUGIXML_HAS_LONG_LONG + xml_text& operator=(long long rhs); + xml_text& operator=(unsigned long long rhs); + #endif + + // Get the data node (node_pcdata or node_cdata) for this object + xml_node data() const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xml_text& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xml_text& lhs, bool rhs); +#endif + + // Child node iterator (a bidirectional iterator over a collection of xml_node) + class PUGIXML_CLASS xml_node_iterator + { + friend class xml_node; + + private: + mutable xml_node _wrap; + xml_node _parent; + + xml_node_iterator(xml_node_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_node_iterator(); + + // Construct an iterator which points to the specified node + xml_node_iterator(const xml_node& node); + + // Iterator operators + bool operator==(const xml_node_iterator& rhs) const; + bool operator!=(const xml_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_node_iterator& operator++(); + xml_node_iterator operator++(int); + + const xml_node_iterator& operator--(); + xml_node_iterator operator--(int); + }; + + // Attribute iterator (a bidirectional iterator over a collection of xml_attribute) + class PUGIXML_CLASS xml_attribute_iterator + { + friend class xml_node; + + private: + mutable xml_attribute _wrap; + xml_node _parent; + + xml_attribute_iterator(xml_attribute_struct* ref, xml_node_struct* parent); + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_attribute value_type; + typedef xml_attribute* pointer; + typedef xml_attribute& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_attribute_iterator(); + + // Construct an iterator which points to the specified attribute + xml_attribute_iterator(const xml_attribute& attr, const xml_node& parent); + + // Iterator operators + bool operator==(const xml_attribute_iterator& rhs) const; + bool operator!=(const xml_attribute_iterator& rhs) const; + + xml_attribute& operator*() const; + xml_attribute* operator->() const; + + const xml_attribute_iterator& operator++(); + xml_attribute_iterator operator++(int); + + const xml_attribute_iterator& operator--(); + xml_attribute_iterator operator--(int); + }; + + // Named node range helper + class PUGIXML_CLASS xml_named_node_iterator + { + friend class xml_node; + + public: + // Iterator traits + typedef ptrdiff_t difference_type; + typedef xml_node value_type; + typedef xml_node* pointer; + typedef xml_node& reference; + + #ifndef PUGIXML_NO_STL + typedef std::bidirectional_iterator_tag iterator_category; + #endif + + // Default constructor + xml_named_node_iterator(); + + // Construct an iterator which points to the specified node + xml_named_node_iterator(const xml_node& node, const char_t* name); + + // Iterator operators + bool operator==(const xml_named_node_iterator& rhs) const; + bool operator!=(const xml_named_node_iterator& rhs) const; + + xml_node& operator*() const; + xml_node* operator->() const; + + const xml_named_node_iterator& operator++(); + xml_named_node_iterator operator++(int); + + const xml_named_node_iterator& operator--(); + xml_named_node_iterator operator--(int); + + private: + mutable xml_node _wrap; + xml_node _parent; + const char_t* _name; + + xml_named_node_iterator(xml_node_struct* ref, xml_node_struct* parent, const char_t* name); + }; + + // Abstract tree walker class (see xml_node::traverse) + class PUGIXML_CLASS xml_tree_walker + { + friend class xml_node; + + private: + int _depth; + + protected: + // Get current traversal depth + int depth() const; + + public: + xml_tree_walker(); + virtual ~xml_tree_walker(); + + // Callback that is called when traversal begins + virtual bool begin(xml_node& node); + + // Callback that is called for each node traversed + virtual bool for_each(xml_node& node) = 0; + + // Callback that is called when traversal ends + virtual bool end(xml_node& node); + }; + + // Parsing status, returned as part of xml_parse_result object + enum xml_parse_status + { + status_ok = 0, // No error + + status_file_not_found, // File was not found during load_file() + status_io_error, // Error reading from file/stream + status_out_of_memory, // Could not allocate memory + status_internal_error, // Internal error occurred + + status_unrecognized_tag, // Parser could not determine tag type + + status_bad_pi, // Parsing error occurred while parsing document declaration/processing instruction + status_bad_comment, // Parsing error occurred while parsing comment + status_bad_cdata, // Parsing error occurred while parsing CDATA section + status_bad_doctype, // Parsing error occurred while parsing document type declaration + status_bad_pcdata, // Parsing error occurred while parsing PCDATA section + status_bad_start_element, // Parsing error occurred while parsing start element tag + status_bad_attribute, // Parsing error occurred while parsing element attribute + status_bad_end_element, // Parsing error occurred while parsing end element tag + status_end_element_mismatch,// There was a mismatch of start-end tags (closing tag had incorrect name, some tag was not closed or there was an excessive closing tag) + + status_append_invalid_root, // Unable to append nodes since root type is not node_element or node_document (exclusive to xml_node::append_buffer) + + status_no_document_element // Parsing resulted in a document without element nodes + }; + + // Parsing result + struct PUGIXML_CLASS xml_parse_result + { + // Parsing status (see xml_parse_status) + xml_parse_status status; + + // Last parsed offset (in char_t units from start of input data) + ptrdiff_t offset; + + // Source document encoding + xml_encoding encoding; + + // Default constructor, initializes object to failed state + xml_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // Document class (DOM tree root) + class PUGIXML_CLASS xml_document: public xml_node + { + private: + char_t* _buffer; + + char _memory[192]; + + // Non-copyable semantics + xml_document(const xml_document&); + xml_document& operator=(const xml_document&); + + void _create(); + void _destroy(); + void _move(xml_document& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + + public: + // Default constructor, makes empty document + xml_document(); + + // Destructor, invalidates all node/attribute handles to this document + ~xml_document(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xml_document(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + xml_document& operator=(xml_document&& rhs) PUGIXML_NOEXCEPT_IF_NOT_COMPACT; + #endif + + // Removes all nodes, leaving the empty document + void reset(); + + // Removes all nodes, then copies the entire contents of the specified document + void reset(const xml_document& proto); + + #ifndef PUGIXML_NO_STL + // Load document from stream. + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load(std::basic_istream >& stream, unsigned int options = parse_default); + #endif + + // (deprecated: use load_string instead) Load document from zero-terminated string. No encoding conversions are applied. + PUGIXML_DEPRECATED xml_parse_result load(const char_t* contents, unsigned int options = parse_default); + + // Load document from zero-terminated string. No encoding conversions are applied. + xml_parse_result load_string(const char_t* contents, unsigned int options = parse_default); + + // Load document from file + xml_parse_result load_file(const char* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + xml_parse_result load_file(const wchar_t* path, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer. Copies/converts the buffer, so it may be deleted or changed after the function returns. + xml_parse_result load_buffer(const void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should ensure that buffer data will persist throughout the document's lifetime, and free the buffer memory manually once document is destroyed. + xml_parse_result load_buffer_inplace(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Load document from buffer, using the buffer for in-place parsing (the buffer is modified and used for storage of document data). + // You should allocate the buffer with pugixml allocation function; document will free the buffer when it is no longer needed (you can't use it anymore). + xml_parse_result load_buffer_inplace_own(void* contents, size_t size, unsigned int options = parse_default, xml_encoding encoding = encoding_auto); + + // Save XML document to writer (semantics is slightly different from xml_node::print, see documentation for details). + void save(xml_writer& writer, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + #ifndef PUGIXML_NO_STL + // Save XML document to stream (semantics is slightly different from xml_node::print, see documentation for details). + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + void save(std::basic_ostream >& stream, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default) const; + #endif + + // Save XML to file + bool save_file(const char* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + bool save_file(const wchar_t* path, const char_t* indent = PUGIXML_TEXT("\t"), unsigned int flags = format_default, xml_encoding encoding = encoding_auto) const; + + // Get document element + xml_node document_element() const; + }; + +#ifndef PUGIXML_NO_XPATH + // XPath query return type + enum xpath_value_type + { + xpath_type_none, // Unknown type (query failed to compile) + xpath_type_node_set, // Node set (xpath_node_set) + xpath_type_number, // Number + xpath_type_string, // String + xpath_type_boolean // Boolean + }; + + // XPath parsing result + struct PUGIXML_CLASS xpath_parse_result + { + // Error message (0 if no error) + const char* error; + + // Last parsed offset (in char_t units from string start) + ptrdiff_t offset; + + // Default constructor, initializes object to failed state + xpath_parse_result(); + + // Cast to bool operator + operator bool() const; + + // Get error description + const char* description() const; + }; + + // A single XPath variable + class PUGIXML_CLASS xpath_variable + { + friend class xpath_variable_set; + + protected: + xpath_value_type _type; + xpath_variable* _next; + + xpath_variable(xpath_value_type type); + + // Non-copyable semantics + xpath_variable(const xpath_variable&); + xpath_variable& operator=(const xpath_variable&); + + public: + // Get variable name + const char_t* name() const; + + // Get variable type + xpath_value_type type() const; + + // Get variable value; no type conversion is performed, default value (false, NaN, empty string, empty node set) is returned on type mismatch error + bool get_boolean() const; + double get_number() const; + const char_t* get_string() const; + const xpath_node_set& get_node_set() const; + + // Set variable value; no type conversion is performed, false is returned on type mismatch error + bool set(bool value); + bool set(double value); + bool set(const char_t* value); + bool set(const xpath_node_set& value); + }; + + // A set of XPath variables + class PUGIXML_CLASS xpath_variable_set + { + private: + xpath_variable* _data[64]; + + void _assign(const xpath_variable_set& rhs); + void _swap(xpath_variable_set& rhs); + + xpath_variable* _find(const char_t* name) const; + + static bool _clone(xpath_variable* var, xpath_variable** out_result); + static void _destroy(xpath_variable* var); + + public: + // Default constructor/destructor + xpath_variable_set(); + ~xpath_variable_set(); + + // Copy constructor/assignment operator + xpath_variable_set(const xpath_variable_set& rhs); + xpath_variable_set& operator=(const xpath_variable_set& rhs); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_variable_set(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + xpath_variable_set& operator=(xpath_variable_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Add a new variable or get the existing one, if the types match + xpath_variable* add(const char_t* name, xpath_value_type type); + + // Set value of an existing variable; no type conversion is performed, false is returned if there is no such variable or if types mismatch + bool set(const char_t* name, bool value); + bool set(const char_t* name, double value); + bool set(const char_t* name, const char_t* value); + bool set(const char_t* name, const xpath_node_set& value); + + // Get existing variable by name + xpath_variable* get(const char_t* name); + const xpath_variable* get(const char_t* name) const; + }; + + // A compiled XPath query object + class PUGIXML_CLASS xpath_query + { + private: + void* _impl; + xpath_parse_result _result; + + typedef void (*unspecified_bool_type)(xpath_query***); + + // Non-copyable semantics + xpath_query(const xpath_query&); + xpath_query& operator=(const xpath_query&); + + public: + // Construct a compiled object from XPath expression. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on compilation errors. + explicit xpath_query(const char_t* query, xpath_variable_set* variables = 0); + + // Constructor + xpath_query(); + + // Destructor + ~xpath_query(); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_query(xpath_query&& rhs) PUGIXML_NOEXCEPT; + xpath_query& operator=(xpath_query&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get query expression return type + xpath_value_type return_type() const; + + // Evaluate expression as boolean value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + bool evaluate_boolean(const xpath_node& n) const; + + // Evaluate expression as double value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + double evaluate_number(const xpath_node& n) const; + + #ifndef PUGIXML_NO_STL + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + string_t evaluate_string(const xpath_node& n) const; + #endif + + // Evaluate expression as string value in the specified context; performs type conversion if necessary. + // At most capacity characters are written to the destination buffer, full result size is returned (includes terminating zero). + // If PUGIXML_NO_EXCEPTIONS is not defined, throws std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty set instead. + size_t evaluate_string(char_t* buffer, size_t capacity, const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node set instead. + xpath_node_set evaluate_node_set(const xpath_node& n) const; + + // Evaluate expression as node set in the specified context. + // Return first node in document order, or empty node if node set is empty. + // If PUGIXML_NO_EXCEPTIONS is not defined, throws xpath_exception on type mismatch and std::bad_alloc on out of memory errors. + // If PUGIXML_NO_EXCEPTIONS is defined, returns empty node instead. + xpath_node evaluate_node(const xpath_node& n) const; + + // Get parsing result (used to get compilation errors in PUGIXML_NO_EXCEPTIONS mode) + const xpath_parse_result& result() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + }; + + #ifndef PUGIXML_NO_EXCEPTIONS + // XPath exception class + class PUGIXML_CLASS xpath_exception: public std::exception + { + private: + xpath_parse_result _result; + + public: + // Construct exception from parse result + explicit xpath_exception(const xpath_parse_result& result); + + // Get error message + virtual const char* what() const throw() PUGIXML_OVERRIDE; + + // Get parse result + const xpath_parse_result& result() const; + }; + #endif + + // XPath node class (either xml_node or xml_attribute) + class PUGIXML_CLASS xpath_node + { + private: + xml_node _node; + xml_attribute _attribute; + + typedef void (*unspecified_bool_type)(xpath_node***); + + public: + // Default constructor; constructs empty XPath node + xpath_node(); + + // Construct XPath node from XML node/attribute + xpath_node(const xml_node& node); + xpath_node(const xml_attribute& attribute, const xml_node& parent); + + // Get node/attribute, if any + xml_node node() const; + xml_attribute attribute() const; + + // Get parent of contained node/attribute + xml_node parent() const; + + // Safe bool conversion operator + operator unspecified_bool_type() const; + + // Borland C++ workaround + bool operator!() const; + + // Comparison operators + bool operator==(const xpath_node& n) const; + bool operator!=(const xpath_node& n) const; + }; + +#ifdef __BORLANDC__ + // Borland C++ workaround + bool PUGIXML_FUNCTION operator&&(const xpath_node& lhs, bool rhs); + bool PUGIXML_FUNCTION operator||(const xpath_node& lhs, bool rhs); +#endif + + // A fixed-size collection of XPath nodes + class PUGIXML_CLASS xpath_node_set + { + public: + // Collection type + enum type_t + { + type_unsorted, // Not ordered + type_sorted, // Sorted by document order (ascending) + type_sorted_reverse // Sorted by document order (descending) + }; + + // Constant iterator type + typedef const xpath_node* const_iterator; + + // We define non-constant iterator to be the same as constant iterator so that various generic algorithms (i.e. boost foreach) work + typedef const xpath_node* iterator; + + // Default constructor. Constructs empty set. + xpath_node_set(); + + // Constructs a set from iterator range; data is not checked for duplicates and is not sorted according to provided type, so be careful + xpath_node_set(const_iterator begin, const_iterator end, type_t type = type_unsorted); + + // Destructor + ~xpath_node_set(); + + // Copy constructor/assignment operator + xpath_node_set(const xpath_node_set& ns); + xpath_node_set& operator=(const xpath_node_set& ns); + + #ifdef PUGIXML_HAS_MOVE + // Move semantics support + xpath_node_set(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + xpath_node_set& operator=(xpath_node_set&& rhs) PUGIXML_NOEXCEPT; + #endif + + // Get collection type + type_t type() const; + + // Get collection size + size_t size() const; + + // Indexing operator + const xpath_node& operator[](size_t index) const; + + // Collection iterators + const_iterator begin() const; + const_iterator end() const; + + // Sort the collection in ascending/descending order by document order + void sort(bool reverse = false); + + // Get first node in the collection by document order + xpath_node first() const; + + // Check if collection is empty + bool empty() const; + + private: + type_t _type; + + xpath_node _storage; + + xpath_node* _begin; + xpath_node* _end; + + void _assign(const_iterator begin, const_iterator end, type_t type); + void _move(xpath_node_set& rhs) PUGIXML_NOEXCEPT; + }; +#endif + +#ifndef PUGIXML_NO_STL + // Convert wide string to UTF8 + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const wchar_t* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_utf8(const std::basic_string, std::allocator >& str); + + // Convert UTF8 to wide string + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const char* str); + std::basic_string, std::allocator > PUGIXML_FUNCTION as_wide(const std::basic_string, std::allocator >& str); +#endif + + // Memory allocation function interface; returns pointer to allocated memory or NULL on failure + typedef void* (*allocation_function)(size_t size); + + // Memory deallocation function interface + typedef void (*deallocation_function)(void* ptr); + + // Override default memory management functions. All subsequent allocations/deallocations will be performed via supplied functions. + void PUGIXML_FUNCTION set_memory_management_functions(allocation_function allocate, deallocation_function deallocate); + + // Get current memory management functions + allocation_function PUGIXML_FUNCTION get_memory_allocation_function(); + deallocation_function PUGIXML_FUNCTION get_memory_deallocation_function(); +} + +#if !defined(PUGIXML_NO_STL) && (defined(_MSC_VER) || defined(__ICC)) +namespace std +{ + // Workarounds for (non-standard) iterator category detection for older versions (MSVC7/IC8 and earlier) + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION _Iter_cat(const pugi::xml_named_node_iterator&); +} +#endif + +#if !defined(PUGIXML_NO_STL) && defined(__SUNPRO_CC) +namespace std +{ + // Workarounds for (non-standard) iterator category detection + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_node_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_attribute_iterator&); + std::bidirectional_iterator_tag PUGIXML_FUNCTION __iterator_category(const pugi::xml_named_node_iterator&); +} +#endif + +#endif + +// Make sure implementation is included in header-only mode +// Use macro expansion in #include to work around QMake (QTBUG-11923) +#if defined(PUGIXML_HEADER_ONLY) && !defined(PUGIXML_SOURCE) +# define PUGIXML_SOURCE "pugixml.cpp" +# include PUGIXML_SOURCE +#endif + +/** + * Copyright (c) 2006-2018 Arseny Kapoulkine + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ diff -Nru leanify-0.4.3/lib/tinyxml2/tinyxml2.cpp leanify-0.4.3+git20181014/lib/tinyxml2/tinyxml2.cpp --- leanify-0.4.3/lib/tinyxml2/tinyxml2.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/tinyxml2/tinyxml2.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,2344 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#include "tinyxml2.h" - -#include // yes, this one new style header, is in the Android SDK. -#if defined(ANDROID_NDK) || defined(__QNXNTO__) -# include -#else -# include -#endif - -static const char LINE_FEED = (char)0x0a; // all line endings are normalized to LF -static const char LF = LINE_FEED; -static const char CARRIAGE_RETURN = (char)0x0d; // CR gets filtered out -static const char CR = CARRIAGE_RETURN; -static const char SINGLE_QUOTE = '\''; -static const char DOUBLE_QUOTE = '\"'; - -// Bunch of unicode info at: -// http://www.unicode.org/faq/utf_bom.html -// ef bb bf (Microsoft "lead bytes") - designates UTF-8 - -static const unsigned char TIXML_UTF_LEAD_0 = 0xefU; -static const unsigned char TIXML_UTF_LEAD_1 = 0xbbU; -static const unsigned char TIXML_UTF_LEAD_2 = 0xbfU; - -namespace tinyxml2 -{ - -struct Entity { - const char* pattern; - int length; - char value; -}; - -static const int NUM_ENTITIES = 5; -static const Entity entities[NUM_ENTITIES] = { - { "quot", 4, DOUBLE_QUOTE }, - { "amp", 3, '&' }, - { "apos", 4, SINGLE_QUOTE }, - { "lt", 2, '<' }, - { "gt", 2, '>' } -}; - - -StrPair::~StrPair() -{ - Reset(); -} - - -void StrPair::TransferTo( StrPair* other ) -{ - if ( this == other ) { - return; - } - // This in effect implements the assignment operator by "moving" - // ownership (as in auto_ptr). - - TIXMLASSERT( other->_flags == 0 ); - TIXMLASSERT( other->_start == 0 ); - TIXMLASSERT( other->_end == 0 ); - - other->Reset(); - - other->_flags = _flags; - other->_start = _start; - other->_end = _end; - - _flags = 0; - _start = 0; - _end = 0; -} - -void StrPair::Reset() -{ - if ( _flags & NEEDS_DELETE ) { - delete [] _start; - } - _flags = 0; - _start = 0; - _end = 0; -} - - -void StrPair::SetStr( const char* str, int flags ) -{ - Reset(); - size_t len = strlen( str ); - _start = new char[ len+1 ]; - memcpy( _start, str, len+1 ); - _end = _start + len; - _flags = flags | NEEDS_DELETE; -} - - -char* StrPair::ParseText( char* p, const char* endTag, int strFlags ) -{ - TIXMLASSERT( endTag && *endTag ); - - char* start = p; - char endChar = *endTag; - size_t length = strlen( endTag ); - - // Inner loop of text parsing. - while ( *p ) { - if ( *p == endChar && strncmp( p, endTag, length ) == 0 ) { - Set( start, p, strFlags ); - return p + length; - } - ++p; - } - return 0; -} - - -char* StrPair::ParseName( char* p ) -{ - if ( !p || !(*p) ) { - return 0; - } - if ( !XMLUtil::IsNameStartChar( *p ) ) { - return 0; - } - - char* const start = p; - ++p; - while ( *p && XMLUtil::IsNameChar( *p ) ) { - ++p; - } - - Set( start, p, 0 ); - return p; -} - - -void StrPair::CollapseWhitespace() -{ - // Adjusting _start would cause undefined behavior on delete[] - TIXMLASSERT( ( _flags & NEEDS_DELETE ) == 0 ); - // Trim leading space. - _start = XMLUtil::SkipWhiteSpace( _start ); - - if ( *_start ) { - char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( *p ) { - if ( XMLUtil::IsWhiteSpace( *p )) { - p = XMLUtil::SkipWhiteSpace( p ); - if ( *p == 0 ) { - break; // don't write to q; this trims the trailing space. - } - *q = ' '; - ++q; - } - *q = *p; - ++q; - ++p; - } - *q = 0; - } -} - - -const char* StrPair::GetStr() -{ - TIXMLASSERT( _start ); - TIXMLASSERT( _end ); - if ( _flags & NEEDS_FLUSH ) { - *_end = 0; - _flags ^= NEEDS_FLUSH; - - if ( _flags ) { - char* p = _start; // the read pointer - char* q = _start; // the write pointer - - while( p < _end ) { - if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == CR ) { - // CR-LF pair becomes LF - // CR alone becomes LF - // LF-CR becomes LF - if ( *(p+1) == LF ) { - p += 2; - } - else { - ++p; - } - *q++ = LF; - } - else if ( (_flags & NEEDS_NEWLINE_NORMALIZATION) && *p == LF ) { - if ( *(p+1) == CR ) { - p += 2; - } - else { - ++p; - } - *q++ = LF; - } - else if ( (_flags & NEEDS_ENTITY_PROCESSING) && *p == '&' ) { - // Entities handled by tinyXML2: - // - special entities in the entity table [in/out] - // - numeric character reference [in] - // 中 or 中 - - if ( *(p+1) == '#' ) { - const int buflen = 10; - char buf[buflen] = { 0 }; - int len = 0; - char* adjusted = const_cast( XMLUtil::GetCharacterRef( p, buf, &len ) ); - if ( adjusted == 0 ) { - *q = *p; - ++p; - ++q; - } - else { - TIXMLASSERT( 0 <= len && len <= buflen ); - TIXMLASSERT( q + len <= adjusted ); - p = adjusted; - memcpy( q, buf, len ); - q += len; - } - } - else { - int i=0; - for(; i(p); - // Check for BOM: - if ( *(pu+0) == TIXML_UTF_LEAD_0 - && *(pu+1) == TIXML_UTF_LEAD_1 - && *(pu+2) == TIXML_UTF_LEAD_2 ) { - *bom = true; - p += 3; - } - TIXMLASSERT( p ); - return p; -} - - -void XMLUtil::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length ) -{ - const unsigned long BYTE_MASK = 0xBF; - const unsigned long BYTE_MARK = 0x80; - const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - - if (input < 0x80) { - *length = 1; - } - else if ( input < 0x800 ) { - *length = 2; - } - else if ( input < 0x10000 ) { - *length = 3; - } - else if ( input < 0x200000 ) { - *length = 4; - } - else { - *length = 0; // This code won't covert this correctly anyway. - return; - } - - output += *length; - - // Scary scary fall throughs. - switch (*length) { - case 4: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 3: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 2: - --output; - *output = (char)((input | BYTE_MARK) & BYTE_MASK); - input >>= 6; - case 1: - --output; - *output = (char)(input | FIRST_BYTE_MARK[*length]); - break; - default: - TIXMLASSERT( false ); - } -} - - -const char* XMLUtil::GetCharacterRef( const char* p, char* value, int* length ) -{ - // Presume an entity, and pull it out. - *length = 0; - - if ( *(p+1) == '#' && *(p+2) ) { - unsigned long ucs = 0; - TIXMLASSERT( sizeof( ucs ) >= 4 ); - ptrdiff_t delta = 0; - unsigned mult = 1; - static const char SEMICOLON = ';'; - - if ( *(p+2) == 'x' ) { - // Hexadecimal. - const char* q = p+3; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != 'x' ) { - unsigned int digit = 0; - - if ( *q >= '0' && *q <= '9' ) { - digit = *q - '0'; - } - else if ( *q >= 'a' && *q <= 'f' ) { - digit = *q - 'a' + 10; - } - else if ( *q >= 'A' && *q <= 'F' ) { - digit = *q - 'A' + 10; - } - else { - return 0; - } - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - TIXMLASSERT( digit >= 0 && digit < 16); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - TIXMLASSERT( mult <= UINT_MAX / 16 ); - mult *= 16; - --q; - } - } - else { - // Decimal. - const char* q = p+2; - if ( !(*q) ) { - return 0; - } - - q = strchr( q, SEMICOLON ); - - if ( !q ) { - return 0; - } - TIXMLASSERT( *q == SEMICOLON ); - - delta = q-p; - --q; - - while ( *q != '#' ) { - if ( *q >= '0' && *q <= '9' ) { - const unsigned int digit = *q - '0'; - TIXMLASSERT( digit == 0 || mult <= UINT_MAX / digit ); - const unsigned int digitScaled = mult * digit; - TIXMLASSERT( ucs <= ULONG_MAX - digitScaled ); - ucs += digitScaled; - } - else { - return 0; - } - TIXMLASSERT( mult <= UINT_MAX / 10 ); - mult *= 10; - --q; - } - } - // convert the UCS to UTF-8 - ConvertUTF32ToUTF8( ucs, value, length ); - return p + delta + 1; - } - return p+1; -} - - -void XMLUtil::ToStr( int v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%d", v ); -} - - -void XMLUtil::ToStr( unsigned v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%u", v ); -} - - -void XMLUtil::ToStr( bool v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%d", v ? 1 : 0 ); -} - -/* - ToStr() of a number is a very tricky topic. - https://github.com/leethomason/tinyxml2/issues/106 -*/ -void XMLUtil::ToStr( float v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.8g", v ); -} - - -void XMLUtil::ToStr( double v, char* buffer, int bufferSize ) -{ - TIXML_SNPRINTF( buffer, bufferSize, "%.17g", v ); -} - - -bool XMLUtil::ToInt( const char* str, int* value ) -{ - if ( TIXML_SSCANF( str, "%d", value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToUnsigned( const char* str, unsigned *value ) -{ - if ( TIXML_SSCANF( str, "%u", value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToBool( const char* str, bool* value ) -{ - int ival = 0; - if ( ToInt( str, &ival )) { - *value = (ival==0) ? false : true; - return true; - } - if ( StringEqual( str, "true" ) ) { - *value = true; - return true; - } - else if ( StringEqual( str, "false" ) ) { - *value = false; - return true; - } - return false; -} - - -bool XMLUtil::ToFloat( const char* str, float* value ) -{ - if ( TIXML_SSCANF( str, "%f", value ) == 1 ) { - return true; - } - return false; -} - -bool XMLUtil::ToDouble( const char* str, double* value ) -{ - if ( TIXML_SSCANF( str, "%lf", value ) == 1 ) { - return true; - } - return false; -} - - -char* XMLDocument::Identify( char* p, XMLNode** node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( p ); - char* const start = p; - p = XMLUtil::SkipWhiteSpace( p ); - if( !*p ) { - *node = 0; - TIXMLASSERT( p ); - return p; - } - - // What is this thing? - // These strings define the matching patters: - static const char* xmlHeader = { "_memPool = &_commentPool; - p += xmlHeaderLen; - } - else if ( XMLUtil::StringEqual( p, commentHeader, commentHeaderLen ) ) { - TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); - returnNode = new (_commentPool.Alloc()) XMLComment( this ); - returnNode->_memPool = &_commentPool; - p += commentHeaderLen; - } - else if ( XMLUtil::StringEqual( p, cdataHeader, cdataHeaderLen ) ) { - TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); - XMLText* text = new (_textPool.Alloc()) XMLText( this ); - returnNode = text; - returnNode->_memPool = &_textPool; - p += cdataHeaderLen; - text->SetCData( true ); - } - else if ( XMLUtil::StringEqual( p, dtdHeader, dtdHeaderLen ) ) { - TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); - returnNode = new (_commentPool.Alloc()) XMLUnknown( this ); - returnNode->_memPool = &_commentPool; - p += dtdHeaderLen; - } - else if ( XMLUtil::StringEqual( p, elementHeader, elementHeaderLen ) ) { - TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); - returnNode = new (_elementPool.Alloc()) XMLElement( this ); - returnNode->_memPool = &_elementPool; - p += elementHeaderLen; - } - else { - TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); - returnNode = new (_textPool.Alloc()) XMLText( this ); - returnNode->_memPool = &_textPool; - p = start; // Back it up, all the text counts. - } - - TIXMLASSERT( returnNode ); - TIXMLASSERT( p ); - *node = returnNode; - return p; -} - - -bool XMLDocument::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLNode ----------- // - -XMLNode::XMLNode( XMLDocument* doc ) : - _document( doc ), - _parent( 0 ), - _firstChild( 0 ), _lastChild( 0 ), - _prev( 0 ), _next( 0 ), - _memPool( 0 ) -{ -} - - -XMLNode::~XMLNode() -{ - DeleteChildren(); - if ( _parent ) { - _parent->Unlink( this ); - } -} - -const char* XMLNode::Value() const -{ - return _value.GetStr(); -} - -void XMLNode::SetValue( const char* str, bool staticMem ) -{ - if ( staticMem ) { - _value.SetInternedStr( str ); - } - else { - _value.SetStr( str ); - } -} - - -void XMLNode::DeleteChildren() -{ - while( _firstChild ) { - TIXMLASSERT( _firstChild->_document == _document ); - XMLNode* node = _firstChild; - Unlink( node ); - - DeleteNode( node ); - } - _firstChild = _lastChild = 0; -} - - -void XMLNode::Unlink( XMLNode* child ) -{ - TIXMLASSERT( child ); - TIXMLASSERT( child->_document == _document ); - if ( child == _firstChild ) { - _firstChild = _firstChild->_next; - } - if ( child == _lastChild ) { - _lastChild = _lastChild->_prev; - } - - if ( child->_prev ) { - child->_prev->_next = child->_next; - } - if ( child->_next ) { - child->_next->_prev = child->_prev; - } - child->_parent = 0; -} - - -void XMLNode::DeleteChild( XMLNode* node ) -{ - TIXMLASSERT( node ); - TIXMLASSERT( node->_document == _document ); - TIXMLASSERT( node->_parent == this ); - DeleteNode( node ); -} - - -XMLNode* XMLNode::InsertEndChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _lastChild ) { - TIXMLASSERT( _firstChild ); - TIXMLASSERT( _lastChild->_next == 0 ); - _lastChild->_next = addThis; - addThis->_prev = _lastChild; - _lastChild = addThis; - - addThis->_next = 0; - } - else { - TIXMLASSERT( _firstChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertFirstChild( XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - InsertChildPreamble( addThis ); - - if ( _firstChild ) { - TIXMLASSERT( _lastChild ); - TIXMLASSERT( _firstChild->_prev == 0 ); - - _firstChild->_prev = addThis; - addThis->_next = _firstChild; - _firstChild = addThis; - - addThis->_prev = 0; - } - else { - TIXMLASSERT( _lastChild == 0 ); - _firstChild = _lastChild = addThis; - - addThis->_prev = 0; - addThis->_next = 0; - } - addThis->_parent = this; - return addThis; -} - - -XMLNode* XMLNode::InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ) -{ - TIXMLASSERT( addThis ); - if ( addThis->_document != _document ) { - TIXMLASSERT( false ); - return 0; - } - - TIXMLASSERT( afterThis ); - - if ( afterThis->_parent != this ) { - TIXMLASSERT( false ); - return 0; - } - - if ( afterThis->_next == 0 ) { - // The last node or the only node. - return InsertEndChild( addThis ); - } - InsertChildPreamble( addThis ); - addThis->_prev = afterThis; - addThis->_next = afterThis->_next; - afterThis->_next->_prev = addThis; - afterThis->_next = addThis; - addThis->_parent = this; - return addThis; -} - - - - -const XMLElement* XMLNode::FirstChildElement( const char* value ) const -{ - for( XMLNode* node=_firstChild; node; node=node->_next ) { - XMLElement* element = node->ToElement(); - if ( element ) { - if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { - return element; - } - } - } - return 0; -} - - -const XMLElement* XMLNode::LastChildElement( const char* value ) const -{ - for( XMLNode* node=_lastChild; node; node=node->_prev ) { - XMLElement* element = node->ToElement(); - if ( element ) { - if ( !value || XMLUtil::StringEqual( element->Name(), value ) ) { - return element; - } - } - } - return 0; -} - - -const XMLElement* XMLNode::NextSiblingElement( const char* value ) const -{ - for( XMLNode* node=this->_next; node; node = node->_next ) { - const XMLElement* element = node->ToElement(); - if ( element - && (!value || XMLUtil::StringEqual( value, node->Value() ))) { - return element; - } - } - return 0; -} - - -const XMLElement* XMLNode::PreviousSiblingElement( const char* value ) const -{ - for( XMLNode* node=_prev; node; node = node->_prev ) { - const XMLElement* element = node->ToElement(); - if ( element - && (!value || XMLUtil::StringEqual( value, node->Value() ))) { - return element; - } - } - return 0; -} - - -char* XMLNode::ParseDeep( char* p, StrPair* parentEnd ) -{ - // This is a recursive method, but thinking about it "at the current level" - // it is a pretty simple flat list: - // - // - // - // With a special case: - // - // - // - // - // Where the closing element (/foo) *must* be the next thing after the opening - // element, and the names must match. BUT the tricky bit is that the closing - // element will be read by the child. - // - // 'endTag' is the end tag for this node, it is returned by a call to a child. - // 'parentEnd' is the end tag for the parent, which is filled in and returned. - - while( p && *p ) { - XMLNode* node = 0; - - p = _document->Identify( p, &node ); - if ( node == 0 ) { - break; - } - - StrPair endTag; - p = node->ParseDeep( p, &endTag ); - if ( !p ) { - DeleteNode( node ); - if ( !_document->Error() ) { - _document->SetError( XML_ERROR_PARSING, 0, 0 ); - } - break; - } - - XMLElement* ele = node->ToElement(); - if ( ele ) { - // We read the end tag. Return it to the parent. - if ( ele->ClosingType() == XMLElement::CLOSING ) { - if ( parentEnd ) { - ele->_value.TransferTo( parentEnd ); - } - node->_memPool->SetTracked(); // created and then immediately deleted. - DeleteNode( node ); - return p; - } - - // Handle an end tag returned to this level. - // And handle a bunch of annoying errors. - bool mismatch = false; - if ( endTag.Empty() ) { - if ( ele->ClosingType() == XMLElement::OPEN ) { - mismatch = true; - } - } - else { - if ( ele->ClosingType() != XMLElement::OPEN ) { - mismatch = true; - } - else if ( !XMLUtil::StringEqual( endTag.GetStr(), node->Value() ) ) { - mismatch = true; - } - } - if ( mismatch ) { - _document->SetError( XML_ERROR_MISMATCHED_ELEMENT, node->Value(), 0 ); - DeleteNode( node ); - break; - } - } - InsertEndChild( node ); - } - return 0; -} - -void XMLNode::DeleteNode( XMLNode* node ) -{ - if ( node == 0 ) { - return; - } - MemPool* pool = node->_memPool; - node->~XMLNode(); - pool->Free( node ); -} - -void XMLNode::InsertChildPreamble( XMLNode* insertThis ) const -{ - TIXMLASSERT( insertThis ); - TIXMLASSERT( insertThis->_document == _document ); - - if ( insertThis->_parent ) - insertThis->_parent->Unlink( insertThis ); - else - insertThis->_memPool->SetTracked(); -} - -// --------- XMLText ---------- // -char* XMLText::ParseDeep( char* p, StrPair* ) -{ - const char* start = p; - if ( this->CData() ) { - p = _value.ParseText( p, "]]>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_CDATA, start, 0 ); - } - return p; - } - else { - int flags = _document->ProcessEntities() ? StrPair::TEXT_ELEMENT : StrPair::TEXT_ELEMENT_LEAVE_ENTITIES; - if ( _document->WhitespaceMode() == COLLAPSE_WHITESPACE ) { - flags |= StrPair::COLLAPSE_WHITESPACE; - } - - p = _value.ParseText( p, "<", flags ); - if ( p && *p ) { - return p-1; - } - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_TEXT, start, 0 ); - } - } - return 0; -} - - -XMLNode* XMLText::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLText* text = doc->NewText( Value() ); // fixme: this will always allocate memory. Intern? - text->SetCData( this->CData() ); - return text; -} - - -bool XMLText::ShallowEqual( const XMLNode* compare ) const -{ - const XMLText* text = compare->ToText(); - return ( text && XMLUtil::StringEqual( text->Value(), Value() ) ); -} - - -bool XMLText::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLComment ---------- // - -XMLComment::XMLComment( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLComment::~XMLComment() -{ -} - - -char* XMLComment::ParseDeep( char* p, StrPair* ) -{ - // Comment parses as text. - const char* start = p; - p = _value.ParseText( p, "-->", StrPair::COMMENT ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_COMMENT, start, 0 ); - } - return p; -} - - -XMLNode* XMLComment::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLComment* comment = doc->NewComment( Value() ); // fixme: this will always allocate memory. Intern? - return comment; -} - - -bool XMLComment::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLComment* comment = compare->ToComment(); - return ( comment && XMLUtil::StringEqual( comment->Value(), Value() )); -} - - -bool XMLComment::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - - -// --------- XMLDeclaration ---------- // - -XMLDeclaration::XMLDeclaration( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLDeclaration::~XMLDeclaration() -{ - //printf( "~XMLDeclaration\n" ); -} - - -char* XMLDeclaration::ParseDeep( char* p, StrPair* ) -{ - // Declaration parses as text. - const char* start = p; - p = _value.ParseText( p, "?>", StrPair::NEEDS_NEWLINE_NORMALIZATION ); - if ( p == 0 ) { - _document->SetError( XML_ERROR_PARSING_DECLARATION, start, 0 ); - } - return p; -} - - -XMLNode* XMLDeclaration::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLDeclaration* dec = doc->NewDeclaration( Value() ); // fixme: this will always allocate memory. Intern? - return dec; -} - - -bool XMLDeclaration::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLDeclaration* declaration = compare->ToDeclaration(); - return ( declaration && XMLUtil::StringEqual( declaration->Value(), Value() )); -} - - - -bool XMLDeclaration::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLUnknown ---------- // - -XMLUnknown::XMLUnknown( XMLDocument* doc ) : XMLNode( doc ) -{ -} - - -XMLUnknown::~XMLUnknown() -{ -} - - -char* XMLUnknown::ParseDeep( char* p, StrPair* ) -{ - // Unknown parses as text. - const char* start = p; - - p = _value.ParseText( p, ">", StrPair::NEEDS_NEWLINE_NORMALIZATION ); - if ( !p ) { - _document->SetError( XML_ERROR_PARSING_UNKNOWN, start, 0 ); - } - return p; -} - - -XMLNode* XMLUnknown::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLUnknown* text = doc->NewUnknown( Value() ); // fixme: this will always allocate memory. Intern? - return text; -} - - -bool XMLUnknown::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLUnknown* unknown = compare->ToUnknown(); - return ( unknown && XMLUtil::StringEqual( unknown->Value(), Value() )); -} - - -bool XMLUnknown::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - return visitor->Visit( *this ); -} - -// --------- XMLAttribute ---------- // - -const char* XMLAttribute::Name() const -{ - return _name.GetStr(); -} - -const char* XMLAttribute::Value() const -{ - return _value.GetStr(); -} - -char* XMLAttribute::ParseDeep( char* p, bool processEntities ) -{ - // Parse using the name rules: bug fix, was using ParseText before - p = _name.ParseName( p ); - if ( !p || !*p ) { - return 0; - } - - // Skip white space before = - p = XMLUtil::SkipWhiteSpace( p ); - if ( *p != '=' ) { - return 0; - } - - ++p; // move up to opening quote - p = XMLUtil::SkipWhiteSpace( p ); - if ( *p != '\"' && *p != '\'' ) { - return 0; - } - - char endTag[2] = { *p, 0 }; - ++p; // move past opening quote - - p = _value.ParseText( p, endTag, processEntities ? StrPair::ATTRIBUTE_VALUE : StrPair::ATTRIBUTE_VALUE_LEAVE_ENTITIES ); - return p; -} - - -void XMLAttribute::SetName( const char* n ) -{ - _name.SetStr( n ); -} - - -XMLError XMLAttribute::QueryIntValue( int* value ) const -{ - if ( XMLUtil::ToInt( Value(), value )) { - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryUnsignedValue( unsigned int* value ) const -{ - if ( XMLUtil::ToUnsigned( Value(), value )) { - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryBoolValue( bool* value ) const -{ - if ( XMLUtil::ToBool( Value(), value )) { - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryFloatValue( float* value ) const -{ - if ( XMLUtil::ToFloat( Value(), value )) { - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -XMLError XMLAttribute::QueryDoubleValue( double* value ) const -{ - if ( XMLUtil::ToDouble( Value(), value )) { - return XML_NO_ERROR; - } - return XML_WRONG_ATTRIBUTE_TYPE; -} - - -void XMLAttribute::SetAttribute( const char* v ) -{ - _value.SetStr( v ); -} - - -void XMLAttribute::SetAttribute( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -void XMLAttribute::SetAttribute( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - -void XMLAttribute::SetAttribute( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - _value.SetStr( buf ); -} - - -// --------- XMLElement ---------- // -XMLElement::XMLElement( XMLDocument* doc ) : XMLNode( doc ), - _closingType( 0 ), - _rootAttribute( 0 ) -{ -} - - -XMLElement::~XMLElement() -{ - while( _rootAttribute ) { - XMLAttribute* next = _rootAttribute->_next; - DeleteAttribute( _rootAttribute ); - _rootAttribute = next; - } -} - - -const XMLAttribute* XMLElement::FindAttribute( const char* name ) const -{ - for( XMLAttribute* a = _rootAttribute; a; a = a->_next ) { - if ( XMLUtil::StringEqual( a->Name(), name ) ) { - return a; - } - } - return 0; -} - - -const char* XMLElement::Attribute( const char* name, const char* value ) const -{ - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return 0; - } - if ( !value || XMLUtil::StringEqual( a->Value(), value )) { - return a->Value(); - } - return 0; -} - - -const char* XMLElement::GetText() const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - return FirstChild()->Value(); - } - return 0; -} - - -void XMLElement::SetText( const char* inText ) -{ - if ( FirstChild() && FirstChild()->ToText() ) - FirstChild()->SetValue( inText ); - else { - XMLText* theText = GetDocument()->NewText( inText ); - InsertFirstChild( theText ); - } -} - - -void XMLElement::SetText( int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( float v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -void XMLElement::SetText( double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - SetText( buf ); -} - - -XMLError XMLElement::QueryIntText( int* ival ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToInt( t, ival ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryUnsignedText( unsigned* uval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToUnsigned( t, uval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryBoolText( bool* bval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToBool( t, bval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryDoubleText( double* dval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToDouble( t, dval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - -XMLError XMLElement::QueryFloatText( float* fval ) const -{ - if ( FirstChild() && FirstChild()->ToText() ) { - const char* t = FirstChild()->Value(); - if ( XMLUtil::ToFloat( t, fval ) ) { - return XML_SUCCESS; - } - return XML_CAN_NOT_CONVERT_TEXT; - } - return XML_NO_TEXT_NODE; -} - - - -XMLAttribute* XMLElement::FindOrCreateAttribute( const char* name ) -{ - XMLAttribute* last = 0; - XMLAttribute* attrib = 0; - for( attrib = _rootAttribute; - attrib; - last = attrib, attrib = attrib->_next ) { - if ( XMLUtil::StringEqual( attrib->Name(), name ) ) { - break; - } - } - if ( !attrib ) { - TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); - attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); - attrib->_memPool = &_document->_attributePool; - if ( last ) { - last->_next = attrib; - } - else { - _rootAttribute = attrib; - } - attrib->SetName( name ); - attrib->_memPool->SetTracked(); // always created and linked. - } - return attrib; -} - - -void XMLElement::DeleteAttribute( const char* name ) -{ - XMLAttribute* prev = 0; - for( XMLAttribute* a=_rootAttribute; a; a=a->_next ) { - if ( XMLUtil::StringEqual( name, a->Name() ) ) { - if ( prev ) { - prev->_next = a->_next; - } - else { - _rootAttribute = a->_next; - } - DeleteAttribute( a ); - break; - } - prev = a; - } -} - - -char* XMLElement::ParseAttributes( char* p ) -{ - const char* start = p; - XMLAttribute* prevAttribute = 0; - - // Read the attributes. - while( p ) { - p = XMLUtil::SkipWhiteSpace( p ); - if ( !(*p) ) { - _document->SetError( XML_ERROR_PARSING_ELEMENT, start, Name() ); - return 0; - } - - // attribute. - if (XMLUtil::IsNameStartChar( *p ) ) { - TIXMLASSERT( sizeof( XMLAttribute ) == _document->_attributePool.ItemSize() ); - XMLAttribute* attrib = new (_document->_attributePool.Alloc() ) XMLAttribute(); - attrib->_memPool = &_document->_attributePool; - attrib->_memPool->SetTracked(); - - p = attrib->ParseDeep( p, _document->ProcessEntities() ); - if ( !p || Attribute( attrib->Name() ) ) { - DeleteAttribute( attrib ); - _document->SetError( XML_ERROR_PARSING_ATTRIBUTE, start, p ); - return 0; - } - // There is a minor bug here: if the attribute in the source xml - // document is duplicated, it will not be detected and the - // attribute will be doubly added. However, tracking the 'prevAttribute' - // avoids re-scanning the attribute list. Preferring performance for - // now, may reconsider in the future. - if ( prevAttribute ) { - prevAttribute->_next = attrib; - } - else { - _rootAttribute = attrib; - } - prevAttribute = attrib; - } - // end of the tag - else if ( *p == '/' && *(p+1) == '>' ) { - _closingType = CLOSED; - return p+2; // done; sealed element. - } - // end of the tag - else if ( *p == '>' ) { - ++p; - break; - } - else { - _document->SetError( XML_ERROR_PARSING_ELEMENT, start, p ); - return 0; - } - } - return p; -} - -void XMLElement::DeleteAttribute( XMLAttribute* attribute ) -{ - if ( attribute == 0 ) { - return; - } - MemPool* pool = attribute->_memPool; - attribute->~XMLAttribute(); - pool->Free( attribute ); -} - -// -// -// foobar -// -char* XMLElement::ParseDeep( char* p, StrPair* strPair ) -{ - // Read the element name. - p = XMLUtil::SkipWhiteSpace( p ); - - // The closing element is the form. It is - // parsed just like a regular element then deleted from - // the DOM. - if ( *p == '/' ) { - _closingType = CLOSING; - ++p; - } - - p = _value.ParseName( p ); - if ( _value.Empty() ) { - return 0; - } - - p = ParseAttributes( p ); - if ( !p || !*p || _closingType ) { - return p; - } - - p = XMLNode::ParseDeep( p, strPair ); - return p; -} - - - -XMLNode* XMLElement::ShallowClone( XMLDocument* doc ) const -{ - if ( !doc ) { - doc = _document; - } - XMLElement* element = doc->NewElement( Value() ); // fixme: this will always allocate memory. Intern? - for( const XMLAttribute* a=FirstAttribute(); a; a=a->Next() ) { - element->SetAttribute( a->Name(), a->Value() ); // fixme: this will always allocate memory. Intern? - } - return element; -} - - -bool XMLElement::ShallowEqual( const XMLNode* compare ) const -{ - TIXMLASSERT( compare ); - const XMLElement* other = compare->ToElement(); - if ( other && XMLUtil::StringEqual( other->Value(), Value() )) { - - const XMLAttribute* a=FirstAttribute(); - const XMLAttribute* b=other->FirstAttribute(); - - while ( a && b ) { - if ( !XMLUtil::StringEqual( a->Value(), b->Value() ) ) { - return false; - } - a = a->Next(); - b = b->Next(); - } - if ( a || b ) { - // different count - return false; - } - return true; - } - return false; -} - - -bool XMLElement::Accept( XMLVisitor* visitor ) const -{ - TIXMLASSERT( visitor ); - if ( visitor->VisitEnter( *this, _rootAttribute ) ) { - for ( const XMLNode* node=FirstChild(); node; node=node->NextSibling() ) { - if ( !node->Accept( visitor ) ) { - break; - } - } - } - return visitor->VisitExit( *this ); -} - - -// --------- XMLDocument ----------- // - -// Warning: List must match 'enum XMLError' -const char* XMLDocument::_errorNames[XML_ERROR_COUNT] = { - "XML_SUCCESS", - "XML_NO_ATTRIBUTE", - "XML_WRONG_ATTRIBUTE_TYPE", - "XML_ERROR_FILE_NOT_FOUND", - "XML_ERROR_FILE_COULD_NOT_BE_OPENED", - "XML_ERROR_FILE_READ_ERROR", - "XML_ERROR_ELEMENT_MISMATCH", - "XML_ERROR_PARSING_ELEMENT", - "XML_ERROR_PARSING_ATTRIBUTE", - "XML_ERROR_IDENTIFYING_TAG", - "XML_ERROR_PARSING_TEXT", - "XML_ERROR_PARSING_CDATA", - "XML_ERROR_PARSING_COMMENT", - "XML_ERROR_PARSING_DECLARATION", - "XML_ERROR_PARSING_UNKNOWN", - "XML_ERROR_EMPTY_DOCUMENT", - "XML_ERROR_MISMATCHED_ELEMENT", - "XML_ERROR_PARSING", - "XML_CAN_NOT_CONVERT_TEXT", - "XML_NO_TEXT_NODE" -}; - - -XMLDocument::XMLDocument( bool processEntities, Whitespace whitespace ) : - XMLNode( 0 ), - _writeBOM( false ), - _processEntities( processEntities ), - _errorID( XML_NO_ERROR ), - _whitespace( whitespace ), - _errorStr1( 0 ), - _errorStr2( 0 ), - _charBuffer( 0 ) -{ - _document = this; // avoid warning about 'this' in initializer list -} - - -XMLDocument::~XMLDocument() -{ - Clear(); -} - - -void XMLDocument::Clear() -{ - DeleteChildren(); - -#ifdef DEBUG - const bool hadError = Error(); -#endif - _errorID = XML_NO_ERROR; - _errorStr1 = 0; - _errorStr2 = 0; - - delete [] _charBuffer; - _charBuffer = 0; - -#if 0 - _textPool.Trace( "text" ); - _elementPool.Trace( "element" ); - _commentPool.Trace( "comment" ); - _attributePool.Trace( "attribute" ); -#endif - -#ifdef DEBUG - if ( !hadError ) { - TIXMLASSERT( _elementPool.CurrentAllocs() == _elementPool.Untracked() ); - TIXMLASSERT( _attributePool.CurrentAllocs() == _attributePool.Untracked() ); - TIXMLASSERT( _textPool.CurrentAllocs() == _textPool.Untracked() ); - TIXMLASSERT( _commentPool.CurrentAllocs() == _commentPool.Untracked() ); - } -#endif -} - - -XMLElement* XMLDocument::NewElement( const char* name ) -{ - TIXMLASSERT( sizeof( XMLElement ) == _elementPool.ItemSize() ); - XMLElement* ele = new (_elementPool.Alloc()) XMLElement( this ); - ele->_memPool = &_elementPool; - ele->SetName( name ); - return ele; -} - - -XMLComment* XMLDocument::NewComment( const char* str ) -{ - TIXMLASSERT( sizeof( XMLComment ) == _commentPool.ItemSize() ); - XMLComment* comment = new (_commentPool.Alloc()) XMLComment( this ); - comment->_memPool = &_commentPool; - comment->SetValue( str ); - return comment; -} - - -XMLText* XMLDocument::NewText( const char* str ) -{ - TIXMLASSERT( sizeof( XMLText ) == _textPool.ItemSize() ); - XMLText* text = new (_textPool.Alloc()) XMLText( this ); - text->_memPool = &_textPool; - text->SetValue( str ); - return text; -} - - -XMLDeclaration* XMLDocument::NewDeclaration( const char* str ) -{ - TIXMLASSERT( sizeof( XMLDeclaration ) == _commentPool.ItemSize() ); - XMLDeclaration* dec = new (_commentPool.Alloc()) XMLDeclaration( this ); - dec->_memPool = &_commentPool; - dec->SetValue( str ? str : "xml version=\"1.0\" encoding=\"UTF-8\"" ); - return dec; -} - - -XMLUnknown* XMLDocument::NewUnknown( const char* str ) -{ - TIXMLASSERT( sizeof( XMLUnknown ) == _commentPool.ItemSize() ); - XMLUnknown* unk = new (_commentPool.Alloc()) XMLUnknown( this ); - unk->_memPool = &_commentPool; - unk->SetValue( str ); - return unk; -} - -static FILE* callfopen( const char* filepath, const char* mode ) -{ - TIXMLASSERT( filepath ); - TIXMLASSERT( mode ); -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) - FILE* fp = 0; - errno_t err = fopen_s( &fp, filepath, mode ); - if ( err ) { - return 0; - } -#else - FILE* fp = fopen( filepath, mode ); -#endif - return fp; -} - -void XMLDocument::DeleteNode( XMLNode* node ) { - TIXMLASSERT( node ); - TIXMLASSERT(node->_document == this ); - if (node->_parent) { - node->_parent->DeleteChild( node ); - } - else { - // Isn't in the tree. - // Use the parent delete. - // Also, we need to mark it tracked: we 'know' - // it was never used. - node->_memPool->SetTracked(); - // Call the static XMLNode version: - XMLNode::DeleteNode(node); - } -} - - -XMLError XMLDocument::LoadFile( const char* filename ) -{ - Clear(); - FILE* fp = callfopen( filename, "rb" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_NOT_FOUND, filename, 0 ); - return _errorID; - } - LoadFile( fp ); - fclose( fp ); - return _errorID; -} - - -XMLError XMLDocument::LoadFile( FILE* fp ) -{ - Clear(); - - fseek( fp, 0, SEEK_SET ); - if ( fgetc( fp ) == EOF && ferror( fp ) != 0 ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - fseek( fp, 0, SEEK_END ); - const long filelength = ftell( fp ); - fseek( fp, 0, SEEK_SET ); - if ( filelength == -1L ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - const size_t size = filelength; - if ( size == 0 ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - - _charBuffer = new char[size+1]; - size_t read = fread( _charBuffer, 1, size, fp ); - if ( read != size ) { - SetError( XML_ERROR_FILE_READ_ERROR, 0, 0 ); - return _errorID; - } - - _charBuffer[size] = 0; - - Parse(); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( const char* filename, bool compact ) -{ - FILE* fp = callfopen( filename, "w" ); - if ( !fp ) { - SetError( XML_ERROR_FILE_COULD_NOT_BE_OPENED, filename, 0 ); - return _errorID; - } - SaveFile(fp, compact); - fclose( fp ); - return _errorID; -} - - -XMLError XMLDocument::SaveFile( FILE* fp, bool compact ) -{ - XMLPrinter stream( fp, compact ); - Print( &stream ); - return _errorID; -} - - -XMLError XMLDocument::Parse( const char* p, size_t len ) -{ - Clear(); - - if ( len == 0 || !p || !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return _errorID; - } - if ( len == (size_t)(-1) ) { - len = strlen( p ); - } - _charBuffer = new char[ len+1 ]; - memcpy( _charBuffer, p, len ); - _charBuffer[len] = 0; - - Parse(); - if ( Error() ) { - // clean up now essentially dangling memory. - // and the parse fail can put objects in the - // pools that are dead and inaccessible. - DeleteChildren(); - _elementPool.Clear(); - _attributePool.Clear(); - _textPool.Clear(); - _commentPool.Clear(); - } - return _errorID; -} - - -void XMLDocument::Print( XMLPrinter* streamer ) const -{ - XMLPrinter stdStreamer( stdout ); - if ( !streamer ) { - streamer = &stdStreamer; - } - Accept( streamer ); -} - - -void XMLDocument::SetError( XMLError error, const char* str1, const char* str2 ) -{ - TIXMLASSERT( error >= 0 && error < XML_ERROR_COUNT ); - _errorID = error; - _errorStr1 = str1; - _errorStr2 = str2; -} - -const char* XMLDocument::ErrorName() const -{ - TIXMLASSERT( _errorID >= 0 && _errorID < XML_ERROR_COUNT ); - return _errorNames[_errorID]; -} - -void XMLDocument::PrintError() const -{ - if ( Error() ) { - static const int LEN = 20; - char buf1[LEN] = { 0 }; - char buf2[LEN] = { 0 }; - - if ( _errorStr1 ) { - TIXML_SNPRINTF( buf1, LEN, "%s", _errorStr1 ); - } - if ( _errorStr2 ) { - TIXML_SNPRINTF( buf2, LEN, "%s", _errorStr2 ); - } - - printf( "XMLDocument error id=%d '%s' str1=%s str2=%s\n", - _errorID, ErrorName(), buf1, buf2 ); - } -} - -void XMLDocument::Parse() -{ - TIXMLASSERT( NoChildren() ); // Clear() must have been called previously - TIXMLASSERT( _charBuffer ); - char* p = _charBuffer; - p = XMLUtil::SkipWhiteSpace( p ); - p = const_cast( XMLUtil::ReadBOM( p, &_writeBOM ) ); - if ( !*p ) { - SetError( XML_ERROR_EMPTY_DOCUMENT, 0, 0 ); - return; - } - ParseDeep(p, 0 ); -} - -XMLPrinter::XMLPrinter( FILE* file, bool compact, int depth ) : - _elementJustOpened( false ), - _firstElement( true ), - _fp( file ), - _depth( depth ), - _textDepth( -1 ), - _processEntities( true ), - _compactMode( compact ) -{ - for( int i=0; i'] = true; // not required, but consistency is nice - _buffer.Push( 0 ); -} - - -void XMLPrinter::Print( const char* format, ... ) -{ - va_list va; - va_start( va, format ); - - if ( _fp ) { - vfprintf( _fp, format, va ); - } - else { -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - #if defined(WINCE) - int len = 512; - do { - len = len*2; - char* str = new char[len](); - len = _vsnprintf(str, len, format, va); - delete[] str; - }while (len < 0); - #else - int len = _vscprintf( format, va ); - #endif -#else - int len = vsnprintf( 0, 0, format, va ); -#endif - // Close out and re-start the va-args - va_end( va ); - va_start( va, format ); - TIXMLASSERT( _buffer.Size() > 0 && _buffer[_buffer.Size() - 1] == 0 ); - char* p = _buffer.PushArr( len ) - 1; // back up over the null terminator. -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) - #if defined(WINCE) - _vsnprintf( p, len+1, format, va ); - #else - vsnprintf_s( p, len+1, _TRUNCATE, format, va ); - #endif -#else - vsnprintf( p, len+1, format, va ); -#endif - } - va_end( va ); -} - - -void XMLPrinter::PrintSpace( int depth ) -{ - for( int i=0; i 0 && *q < ENTITY_RANGE ) { - // Check for entities. If one is found, flush - // the stream up until the entity, write the - // entity, and keep looking. - if ( flag[(unsigned char)(*q)] ) { - while ( p < q ) { - Print( "%c", *p ); - ++p; - } - for( int i=0; i 0) ) { - Print( "%s", p ); - } -} - - -void XMLPrinter::PushHeader( bool writeBOM, bool writeDec ) -{ - if ( writeBOM ) { - static const unsigned char bom[] = { TIXML_UTF_LEAD_0, TIXML_UTF_LEAD_1, TIXML_UTF_LEAD_2, 0 }; - Print( "%s", bom ); - } - if ( writeDec ) { - PushDeclaration( "xml version=\"1.0\"" ); - } -} - - -void XMLPrinter::OpenElement( const char* name, bool compactMode ) -{ - SealElementIfJustOpened(); - _stack.Push( name ); - - if ( _textDepth < 0 && !_firstElement && !compactMode ) { - Print( "\n" ); - } - if ( !compactMode ) { - PrintSpace( _depth ); - } - - Print( "<%s", name ); - _elementJustOpened = true; - _firstElement = false; - ++_depth; -} - - -void XMLPrinter::PushAttribute( const char* name, const char* value ) -{ - TIXMLASSERT( _elementJustOpened ); - Print( " %s=\"", name ); - PrintString( value, false ); - Print( "\"" ); -} - - -void XMLPrinter::PushAttribute( const char* name, int v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, unsigned v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, bool v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::PushAttribute( const char* name, double v ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( v, buf, BUF_SIZE ); - PushAttribute( name, buf ); -} - - -void XMLPrinter::CloseElement( bool compactMode ) -{ - --_depth; - const char* name = _stack.Pop(); - - if ( _elementJustOpened ) { - Print( "/>" ); - } - else { - if ( _textDepth < 0 && !compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - Print( "", name ); - } - - if ( _textDepth == _depth ) { - _textDepth = -1; - } - if ( _depth == 0 && !compactMode) { - Print( "\n" ); - } - _elementJustOpened = false; -} - - -void XMLPrinter::SealElementIfJustOpened() -{ - if ( !_elementJustOpened ) { - return; - } - _elementJustOpened = false; - Print( ">" ); -} - - -void XMLPrinter::PushText( const char* text, bool cdata ) -{ - _textDepth = _depth-1; - - SealElementIfJustOpened(); - if ( cdata ) { - Print( "" ); - } - else { - PrintString( text, true ); - } -} - -void XMLPrinter::PushText( int value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( unsigned value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( bool value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( float value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushText( double value ) -{ - char buf[BUF_SIZE]; - XMLUtil::ToStr( value, buf, BUF_SIZE ); - PushText( buf, false ); -} - - -void XMLPrinter::PushComment( const char* comment ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - // Print( "", comment ); -} - - -void XMLPrinter::PushDeclaration( const char* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - Print( "", value ); -} - - -void XMLPrinter::PushUnknown( const char* value ) -{ - SealElementIfJustOpened(); - if ( _textDepth < 0 && !_firstElement && !_compactMode) { - Print( "\n" ); - PrintSpace( _depth ); - } - _firstElement = false; - Print( "", value ); -} - - -bool XMLPrinter::VisitEnter( const XMLDocument& doc ) -{ - _processEntities = doc.ProcessEntities(); - if ( doc.HasBOM() ) { - PushHeader( true, false ); - } - return true; -} - - -bool XMLPrinter::VisitEnter( const XMLElement& element, const XMLAttribute* attribute ) -{ - const XMLElement* parentElem = element.Parent()->ToElement(); - bool compactMode = parentElem ? CompactMode(*parentElem) : _compactMode; - OpenElement( element.Name(), compactMode ); - while ( attribute ) { - PushAttribute( attribute->Name(), attribute->Value() ); - attribute = attribute->Next(); - } - return true; -} - - -bool XMLPrinter::VisitExit( const XMLElement& element ) -{ - CloseElement( CompactMode(element) ); - return true; -} - - -bool XMLPrinter::Visit( const XMLText& text ) -{ - PushText( text.Value(), text.CData() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLComment& comment ) -{ - PushComment( comment.Value() ); - return true; -} - -bool XMLPrinter::Visit( const XMLDeclaration& declaration ) -{ - PushDeclaration( declaration.Value() ); - return true; -} - - -bool XMLPrinter::Visit( const XMLUnknown& unknown ) -{ - PushUnknown( unknown.Value() ); - return true; -} - -} // namespace tinyxml2 - diff -Nru leanify-0.4.3/lib/tinyxml2/tinyxml2.h leanify-0.4.3+git20181014/lib/tinyxml2/tinyxml2.h --- leanify-0.4.3/lib/tinyxml2/tinyxml2.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/tinyxml2/tinyxml2.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,2130 +0,0 @@ -/* -Original code by Lee Thomason (www.grinninglizard.com) - -This software is provided 'as-is', without any express or implied -warranty. In no event will the authors be held liable for any -damages arising from the use of this software. - -Permission is granted to anyone to use this software for any -purpose, including commercial applications, and to alter it and -redistribute it freely, subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must -not claim that you wrote the original software. If you use this -software in a product, an acknowledgment in the product documentation -would be appreciated but is not required. - -2. Altered source versions must be plainly marked as such, and -must not be misrepresented as being the original software. - -3. This notice may not be removed or altered from any source -distribution. -*/ - -#ifndef TINYXML2_INCLUDED -#define TINYXML2_INCLUDED - -#if defined(ANDROID_NDK) || defined(__BORLANDC__) || defined(__QNXNTO__) -# include -# include -# include -# include -# include -# include -#else -# include -# include -# include -# include -# include -# include -#endif - -/* - TODO: intern strings instead of allocation. -*/ -/* - gcc: - g++ -Wall -DDEBUG tinyxml2.cpp xmltest.cpp -o gccxmltest.exe - - Formatting, Artistic Style: - AStyle.exe --style=1tbs --indent-switches --break-closing-brackets --indent-preprocessor tinyxml2.cpp tinyxml2.h -*/ - -#if defined( _DEBUG ) || defined( DEBUG ) || defined (__DEBUG__) -# ifndef DEBUG -# define DEBUG -# endif -#endif - -#ifdef _MSC_VER -# pragma warning(push) -# pragma warning(disable: 4251) -#endif - -#ifdef _WIN32 -# ifdef TINYXML2_EXPORT -# define TINYXML2_LIB __declspec(dllexport) -# elif defined(TINYXML2_IMPORT) -# define TINYXML2_LIB __declspec(dllimport) -# else -# define TINYXML2_LIB -# endif -#else -# define TINYXML2_LIB -#endif - - -#if defined(DEBUG) -# if defined(_MSC_VER) -# // "(void)0," is for suppressing C4127 warning in "assert(false)", "assert(true)" and the like -# define TIXMLASSERT( x ) if ( !((void)0,(x))) { __debugbreak(); } //if ( !(x)) WinDebugBreak() -# elif defined (ANDROID_NDK) -# include -# define TIXMLASSERT( x ) if ( !(x)) { __android_log_assert( "assert", "grinliz", "ASSERT in '%s' at %d.", __FILE__, __LINE__ ); } -# else -# include -# define TIXMLASSERT assert -# endif -# else -# define TIXMLASSERT( x ) {} -#endif - - -#if defined(_MSC_VER) && (_MSC_VER >= 1400 ) && (!defined WINCE) -// Microsoft visual studio, version 2005 and higher. -/*int _snprintf_s( - char *buffer, - size_t sizeOfBuffer, - size_t count, - const char *format [, - argument] ... -);*/ -inline int TIXML_SNPRINTF( char* buffer, size_t size, const char* format, ... ) -{ - va_list va; - va_start( va, format ); - int result = vsnprintf_s( buffer, size, _TRUNCATE, format, va ); - va_end( va ); - return result; -} -#define TIXML_SSCANF sscanf_s -#elif defined WINCE -#define TIXML_SNPRINTF _snprintf -#define TIXML_SSCANF sscanf -#else -// GCC version 3 and higher -//#warning( "Using sn* functions." ) -#define TIXML_SNPRINTF snprintf -#define TIXML_SSCANF sscanf -#endif - -/* Versioning, past 1.0.14: - http://semver.org/ -*/ -static const int TIXML2_MAJOR_VERSION = 3; -static const int TIXML2_MINOR_VERSION = 0; -static const int TIXML2_PATCH_VERSION = 0; - -namespace tinyxml2 -{ -class XMLDocument; -class XMLElement; -class XMLAttribute; -class XMLComment; -class XMLText; -class XMLDeclaration; -class XMLUnknown; -class XMLPrinter; - -/* - A class that wraps strings. Normally stores the start and end - pointers into the XML file itself, and will apply normalization - and entity translation if actually read. Can also store (and memory - manage) a traditional char[] -*/ -class StrPair -{ -public: - enum { - NEEDS_ENTITY_PROCESSING = 0x01, - NEEDS_NEWLINE_NORMALIZATION = 0x02, - COLLAPSE_WHITESPACE = 0x04, - - TEXT_ELEMENT = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - TEXT_ELEMENT_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_NAME = 0, - ATTRIBUTE_VALUE = NEEDS_ENTITY_PROCESSING | NEEDS_NEWLINE_NORMALIZATION, - ATTRIBUTE_VALUE_LEAVE_ENTITIES = NEEDS_NEWLINE_NORMALIZATION, - COMMENT = NEEDS_NEWLINE_NORMALIZATION - }; - - StrPair() : _flags( 0 ), _start( 0 ), _end( 0 ) {} - ~StrPair(); - - void Set( char* start, char* end, int flags ) { - Reset(); - _start = start; - _end = end; - _flags = flags | NEEDS_FLUSH; - } - - const char* GetStr(); - - bool Empty() const { - return _start == _end; - } - - void SetInternedStr( const char* str ) { - Reset(); - _start = const_cast(str); - } - - void SetStr( const char* str, int flags=0 ); - - char* ParseText( char* in, const char* endTag, int strFlags ); - char* ParseName( char* in ); - - void TransferTo( StrPair* other ); - -private: - void Reset(); - void CollapseWhitespace(); - - enum { - NEEDS_FLUSH = 0x100, - NEEDS_DELETE = 0x200 - }; - - // After parsing, if *_end != 0, it can be set to zero. - int _flags; - char* _start; - char* _end; - - StrPair( const StrPair& other ); // not supported - void operator=( StrPair& other ); // not supported, use TransferTo() -}; - - -/* - A dynamic array of Plain Old Data. Doesn't support constructors, etc. - Has a small initial memory pool, so that low or no usage will not - cause a call to new/delete -*/ -template -class DynArray -{ -public: - DynArray() { - _mem = _pool; - _allocated = INIT; - _size = 0; - } - - ~DynArray() { - if ( _mem != _pool ) { - delete [] _mem; - } - } - - void Clear() { - _size = 0; - } - - void Push( T t ) { - TIXMLASSERT( _size < INT_MAX ); - EnsureCapacity( _size+1 ); - _mem[_size++] = t; - } - - T* PushArr( int count ) { - TIXMLASSERT( count >= 0 ); - TIXMLASSERT( _size <= INT_MAX - count ); - EnsureCapacity( _size+count ); - T* ret = &_mem[_size]; - _size += count; - return ret; - } - - T Pop() { - TIXMLASSERT( _size > 0 ); - return _mem[--_size]; - } - - void PopArr( int count ) { - TIXMLASSERT( _size >= count ); - _size -= count; - } - - bool Empty() const { - return _size == 0; - } - - T& operator[](int i) { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& operator[](int i) const { - TIXMLASSERT( i>= 0 && i < _size ); - return _mem[i]; - } - - const T& PeekTop() const { - TIXMLASSERT( _size > 0 ); - return _mem[ _size - 1]; - } - - int Size() const { - TIXMLASSERT( _size >= 0 ); - return _size; - } - - int Capacity() const { - return _allocated; - } - - const T* Mem() const { - return _mem; - } - - T* Mem() { - return _mem; - } - -private: - DynArray( const DynArray& ); // not supported - void operator=( const DynArray& ); // not supported - - void EnsureCapacity( int cap ) { - TIXMLASSERT( cap > 0 ); - if ( cap > _allocated ) { - TIXMLASSERT( cap <= INT_MAX / 2 ); - int newAllocated = cap * 2; - T* newMem = new T[newAllocated]; - memcpy( newMem, _mem, sizeof(T)*_size ); // warning: not using constructors, only works for PODs - if ( _mem != _pool ) { - delete [] _mem; - } - _mem = newMem; - _allocated = newAllocated; - } - } - - T* _mem; - T _pool[INIT]; - int _allocated; // objects allocated - int _size; // number objects in use -}; - - -/* - Parent virtual class of a pool for fast allocation - and deallocation of objects. -*/ -class MemPool -{ -public: - MemPool() {} - virtual ~MemPool() {} - - virtual int ItemSize() const = 0; - virtual void* Alloc() = 0; - virtual void Free( void* ) = 0; - virtual void SetTracked() = 0; - virtual void Clear() = 0; -}; - - -/* - Template child class to create pools of the correct type. -*/ -template< int SIZE > -class MemPoolT : public MemPool -{ -public: - MemPoolT() : _root(0), _currentAllocs(0), _nAllocs(0), _maxAllocs(0), _nUntracked(0) {} - ~MemPoolT() { - Clear(); - } - - void Clear() { - // Delete the blocks. - while( !_blockPtrs.Empty()) { - Block* b = _blockPtrs.Pop(); - delete b; - } - _root = 0; - _currentAllocs = 0; - _nAllocs = 0; - _maxAllocs = 0; - _nUntracked = 0; - } - - virtual int ItemSize() const { - return SIZE; - } - int CurrentAllocs() const { - return _currentAllocs; - } - - virtual void* Alloc() { - if ( !_root ) { - // Need a new block. - Block* block = new Block(); - _blockPtrs.Push( block ); - - for( int i=0; ichunk[i].next = &block->chunk[i+1]; - } - block->chunk[COUNT-1].next = 0; - _root = block->chunk; - } - void* result = _root; - _root = _root->next; - - ++_currentAllocs; - if ( _currentAllocs > _maxAllocs ) { - _maxAllocs = _currentAllocs; - } - _nAllocs++; - _nUntracked++; - return result; - } - - virtual void Free( void* mem ) { - if ( !mem ) { - return; - } - --_currentAllocs; - Chunk* chunk = static_cast( mem ); -#ifdef DEBUG - memset( chunk, 0xfe, sizeof(Chunk) ); -#endif - chunk->next = _root; - _root = chunk; - } - void Trace( const char* name ) { - printf( "Mempool %s watermark=%d [%dk] current=%d size=%d nAlloc=%d blocks=%d\n", - name, _maxAllocs, _maxAllocs*SIZE/1024, _currentAllocs, SIZE, _nAllocs, _blockPtrs.Size() ); - } - - void SetTracked() { - _nUntracked--; - } - - int Untracked() const { - return _nUntracked; - } - - // This number is perf sensitive. 4k seems like a good tradeoff on my machine. - // The test file is large, 170k. - // Release: VS2010 gcc(no opt) - // 1k: 4000 - // 2k: 4000 - // 4k: 3900 21000 - // 16k: 5200 - // 32k: 4300 - // 64k: 4000 21000 - enum { COUNT = (4*1024)/SIZE }; // Some compilers do not accept to use COUNT in private part if COUNT is private - -private: - MemPoolT( const MemPoolT& ); // not supported - void operator=( const MemPoolT& ); // not supported - - union Chunk { - Chunk* next; - char mem[SIZE]; - }; - struct Block { - Chunk chunk[COUNT]; - }; - DynArray< Block*, 10 > _blockPtrs; - Chunk* _root; - - int _currentAllocs; - int _nAllocs; - int _maxAllocs; - int _nUntracked; -}; - - - -/** - Implements the interface to the "Visitor pattern" (see the Accept() method.) - If you call the Accept() method, it requires being passed a XMLVisitor - class to handle callbacks. For nodes that contain other nodes (Document, Element) - you will get called with a VisitEnter/VisitExit pair. Nodes that are always leafs - are simply called with Visit(). - - If you return 'true' from a Visit method, recursive parsing will continue. If you return - false, no children of this node or its siblings will be visited. - - All flavors of Visit methods have a default implementation that returns 'true' (continue - visiting). You need to only override methods that are interesting to you. - - Generally Accept() is called on the XMLDocument, although all nodes support visiting. - - You should never change the document from a callback. - - @sa XMLNode::Accept() -*/ -class TINYXML2_LIB XMLVisitor -{ -public: - virtual ~XMLVisitor() {} - - /// Visit a document. - virtual bool VisitEnter( const XMLDocument& /*doc*/ ) { - return true; - } - /// Visit a document. - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - /// Visit an element. - virtual bool VisitEnter( const XMLElement& /*element*/, const XMLAttribute* /*firstAttribute*/ ) { - return true; - } - /// Visit an element. - virtual bool VisitExit( const XMLElement& /*element*/ ) { - return true; - } - - /// Visit a declaration. - virtual bool Visit( const XMLDeclaration& /*declaration*/ ) { - return true; - } - /// Visit a text node. - virtual bool Visit( const XMLText& /*text*/ ) { - return true; - } - /// Visit a comment node. - virtual bool Visit( const XMLComment& /*comment*/ ) { - return true; - } - /// Visit an unknown node. - virtual bool Visit( const XMLUnknown& /*unknown*/ ) { - return true; - } -}; - -// WARNING: must match XMLDocument::_errorNames[] -enum XMLError { - XML_SUCCESS = 0, - XML_NO_ERROR = 0, - XML_NO_ATTRIBUTE, - XML_WRONG_ATTRIBUTE_TYPE, - XML_ERROR_FILE_NOT_FOUND, - XML_ERROR_FILE_COULD_NOT_BE_OPENED, - XML_ERROR_FILE_READ_ERROR, - XML_ERROR_ELEMENT_MISMATCH, - XML_ERROR_PARSING_ELEMENT, - XML_ERROR_PARSING_ATTRIBUTE, - XML_ERROR_IDENTIFYING_TAG, - XML_ERROR_PARSING_TEXT, - XML_ERROR_PARSING_CDATA, - XML_ERROR_PARSING_COMMENT, - XML_ERROR_PARSING_DECLARATION, - XML_ERROR_PARSING_UNKNOWN, - XML_ERROR_EMPTY_DOCUMENT, - XML_ERROR_MISMATCHED_ELEMENT, - XML_ERROR_PARSING, - XML_CAN_NOT_CONVERT_TEXT, - XML_NO_TEXT_NODE, - - XML_ERROR_COUNT -}; - - -/* - Utility functionality. -*/ -class XMLUtil -{ -public: - static const char* SkipWhiteSpace( const char* p ) { - TIXMLASSERT( p ); - while( IsWhiteSpace(*p) ) { - ++p; - } - TIXMLASSERT( p ); - return p; - } - static char* SkipWhiteSpace( char* p ) { - return const_cast( SkipWhiteSpace( const_cast(p) ) ); - } - - // Anything in the high order range of UTF-8 is assumed to not be whitespace. This isn't - // correct, but simple, and usually works. - static bool IsWhiteSpace( char p ) { - return !IsUTF8Continuation(p) && isspace( static_cast(p) ); - } - - inline static bool IsNameStartChar( unsigned char ch ) { - if ( ch >= 128 ) { - // This is a heuristic guess in attempt to not implement Unicode-aware isalpha() - return true; - } - if ( isalpha( ch ) ) { - return true; - } - return ch == ':' || ch == '_'; - } - - inline static bool IsNameChar( unsigned char ch ) { - return IsNameStartChar( ch ) - || isdigit( ch ) - || ch == '.' - || ch == '-'; - } - - inline static bool StringEqual( const char* p, const char* q, int nChar=INT_MAX ) { - if ( p == q ) { - return true; - } - int n = 0; - while( *p && *q && *p == *q && n(const_cast(this)->FirstChildElement( value )); - } - - /// Get the last child node, or null if none exists. - const XMLNode* LastChild() const { - return _lastChild; - } - - XMLNode* LastChild() { - return const_cast(const_cast(this)->LastChild() ); - } - - /** Get the last child element or optionally the last child - element with the specified name. - */ - const XMLElement* LastChildElement( const char* value=0 ) const; - - XMLElement* LastChildElement( const char* value=0 ) { - return const_cast(const_cast(this)->LastChildElement(value) ); - } - - /// Get the previous (left) sibling node of this node. - const XMLNode* PreviousSibling() const { - return _prev; - } - - XMLNode* PreviousSibling() { - return _prev; - } - - /// Get the previous (left) sibling element of this node, with an optionally supplied name. - const XMLElement* PreviousSiblingElement( const char* value=0 ) const ; - - XMLElement* PreviousSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->PreviousSiblingElement( value ) ); - } - - /// Get the next (right) sibling node of this node. - const XMLNode* NextSibling() const { - return _next; - } - - XMLNode* NextSibling() { - return _next; - } - - /// Get the next (right) sibling element of this node, with an optionally supplied name. - const XMLElement* NextSiblingElement( const char* value=0 ) const; - - XMLElement* NextSiblingElement( const char* value=0 ) { - return const_cast(const_cast(this)->NextSiblingElement( value ) ); - } - - /** - Add a child node as the last (right) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertEndChild( XMLNode* addThis ); - - XMLNode* LinkEndChild( XMLNode* addThis ) { - return InsertEndChild( addThis ); - } - /** - Add a child node as the first (left) child. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the node does not - belong to the same document. - */ - XMLNode* InsertFirstChild( XMLNode* addThis ); - /** - Add a node after the specified child node. - If the child node is already part of the document, - it is moved from its old location to the new location. - Returns the addThis argument or 0 if the afterThis node - is not a child of this node, or if the node does not - belong to the same document. - */ - XMLNode* InsertAfterChild( XMLNode* afterThis, XMLNode* addThis ); - - /** - Delete all the children of this node. - */ - void DeleteChildren(); - - /** - Delete a child of this node. - */ - void DeleteChild( XMLNode* node ); - - /** - Make a copy of this node, but not its children. - You may pass in a Document pointer that will be - the owner of the new Node. If the 'document' is - null, then the node returned will be allocated - from the current Document. (this->GetDocument()) - - Note: if called on a XMLDocument, this will return null. - */ - virtual XMLNode* ShallowClone( XMLDocument* document ) const = 0; - - /** - Test if 2 nodes are the same, but don't test children. - The 2 nodes do not need to be in the same Document. - - Note: if called on a XMLDocument, this will return false. - */ - virtual bool ShallowEqual( const XMLNode* compare ) const = 0; - - /** Accept a hierarchical visit of the nodes in the TinyXML-2 DOM. Every node in the - XML tree will be conditionally visited and the host will be called back - via the XMLVisitor interface. - - This is essentially a SAX interface for TinyXML-2. (Note however it doesn't re-parse - the XML for the callbacks, so the performance of TinyXML-2 is unchanged by using this - interface versus any other.) - - The interface has been based on ideas from: - - - http://www.saxproject.org/ - - http://c2.com/cgi/wiki?HierarchicalVisitorPattern - - Which are both good references for "visiting". - - An example of using Accept(): - @verbatim - XMLPrinter printer; - tinyxmlDoc.Accept( &printer ); - const char* xmlcstr = printer.CStr(); - @endverbatim - */ - virtual bool Accept( XMLVisitor* visitor ) const = 0; - - // internal - virtual char* ParseDeep( char*, StrPair* ); - -protected: - XMLNode( XMLDocument* ); - virtual ~XMLNode(); - - XMLDocument* _document; - XMLNode* _parent; - mutable StrPair _value; - - XMLNode* _firstChild; - XMLNode* _lastChild; - - XMLNode* _prev; - XMLNode* _next; - -private: - MemPool* _memPool; - void Unlink( XMLNode* child ); - static void DeleteNode( XMLNode* node ); - void InsertChildPreamble( XMLNode* insertThis ) const; - - XMLNode( const XMLNode& ); // not supported - XMLNode& operator=( const XMLNode& ); // not supported -}; - - -/** XML text. - - Note that a text node can have child element nodes, for example: - @verbatim - This is bold - @endverbatim - - A text node can have 2 ways to output the next. "normal" output - and CDATA. It will default to the mode it was parsed from the XML file and - you generally want to leave it alone, but you can change the output mode with - SetCData() and query it with CData(). -*/ -class TINYXML2_LIB XMLText : public XMLNode -{ - friend class XMLBase; - friend class XMLDocument; -public: - virtual bool Accept( XMLVisitor* visitor ) const; - - virtual XMLText* ToText() { - return this; - } - virtual const XMLText* ToText() const { - return this; - } - - /// Declare whether this should be CDATA or standard text. - void SetCData( bool isCData ) { - _isCData = isCData; - } - /// Returns true if this is a CDATA text element. - bool CData() const { - return _isCData; - } - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLText( XMLDocument* doc ) : XMLNode( doc ), _isCData( false ) {} - virtual ~XMLText() {} - -private: - bool _isCData; - - XMLText( const XMLText& ); // not supported - XMLText& operator=( const XMLText& ); // not supported -}; - - -/** An XML Comment. */ -class TINYXML2_LIB XMLComment : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLComment* ToComment() { - return this; - } - virtual const XMLComment* ToComment() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLComment( XMLDocument* doc ); - virtual ~XMLComment(); - -private: - XMLComment( const XMLComment& ); // not supported - XMLComment& operator=( const XMLComment& ); // not supported -}; - - -/** In correct XML the declaration is the first entry in the file. - @verbatim - - @endverbatim - - TinyXML-2 will happily read or write files without a declaration, - however. - - The text of the declaration isn't interpreted. It is parsed - and written as a string. -*/ -class TINYXML2_LIB XMLDeclaration : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLDeclaration* ToDeclaration() { - return this; - } - virtual const XMLDeclaration* ToDeclaration() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLDeclaration( XMLDocument* doc ); - virtual ~XMLDeclaration(); - -private: - XMLDeclaration( const XMLDeclaration& ); // not supported - XMLDeclaration& operator=( const XMLDeclaration& ); // not supported -}; - - -/** Any tag that TinyXML-2 doesn't recognize is saved as an - unknown. It is a tag of text, but should not be modified. - It will be written back to the XML, unchanged, when the file - is saved. - - DTD tags get thrown into XMLUnknowns. -*/ -class TINYXML2_LIB XMLUnknown : public XMLNode -{ - friend class XMLDocument; -public: - virtual XMLUnknown* ToUnknown() { - return this; - } - virtual const XMLUnknown* ToUnknown() const { - return this; - } - - virtual bool Accept( XMLVisitor* visitor ) const; - - char* ParseDeep( char*, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -protected: - XMLUnknown( XMLDocument* doc ); - virtual ~XMLUnknown(); - -private: - XMLUnknown( const XMLUnknown& ); // not supported - XMLUnknown& operator=( const XMLUnknown& ); // not supported -}; - - - -/** An attribute is a name-value pair. Elements have an arbitrary - number of attributes, each with a unique name. - - @note The attributes are not XMLNodes. You may only query the - Next() attribute in a list. -*/ -class TINYXML2_LIB XMLAttribute -{ - friend class XMLElement; -public: - /// The name of the attribute. - const char* Name() const; - - /// The value of the attribute. - const char* Value() const; - - /// The next attribute in the list. - const XMLAttribute* Next() const { - return _next; - } - - /** IntValue interprets the attribute as an integer, and returns the value. - If the value isn't an integer, 0 will be returned. There is no error checking; - use QueryIntValue() if you need error checking. - */ - int IntValue() const { - int i=0; - QueryIntValue( &i ); - return i; - } - /// Query as an unsigned integer. See IntValue() - unsigned UnsignedValue() const { - unsigned i=0; - QueryUnsignedValue( &i ); - return i; - } - /// Query as a boolean. See IntValue() - bool BoolValue() const { - bool b=false; - QueryBoolValue( &b ); - return b; - } - /// Query as a double. See IntValue() - double DoubleValue() const { - double d=0; - QueryDoubleValue( &d ); - return d; - } - /// Query as a float. See IntValue() - float FloatValue() const { - float f=0; - QueryFloatValue( &f ); - return f; - } - - /** QueryIntValue interprets the attribute as an integer, and returns the value - in the provided parameter. The function will return XML_NO_ERROR on success, - and XML_WRONG_ATTRIBUTE_TYPE if the conversion is not successful. - */ - XMLError QueryIntValue( int* value ) const; - /// See QueryIntValue - XMLError QueryUnsignedValue( unsigned int* value ) const; - /// See QueryIntValue - XMLError QueryBoolValue( bool* value ) const; - /// See QueryIntValue - XMLError QueryDoubleValue( double* value ) const; - /// See QueryIntValue - XMLError QueryFloatValue( float* value ) const; - - /// Set the attribute to a string value. - void SetAttribute( const char* value ); - /// Set the attribute to value. - void SetAttribute( int value ); - /// Set the attribute to value. - void SetAttribute( unsigned value ); - /// Set the attribute to value. - void SetAttribute( bool value ); - /// Set the attribute to value. - void SetAttribute( double value ); - /// Set the attribute to value. - void SetAttribute( float value ); - -private: - enum { BUF_SIZE = 200 }; - - XMLAttribute() : _next( 0 ), _memPool( 0 ) {} - virtual ~XMLAttribute() {} - - XMLAttribute( const XMLAttribute& ); // not supported - void operator=( const XMLAttribute& ); // not supported - void SetName( const char* name ); - - char* ParseDeep( char* p, bool processEntities ); - - mutable StrPair _name; - mutable StrPair _value; - XMLAttribute* _next; - MemPool* _memPool; -}; - - -/** The element is a container class. It has a value, the element name, - and can contain other elements, text, comments, and unknowns. - Elements also contain an arbitrary number of attributes. -*/ -class TINYXML2_LIB XMLElement : public XMLNode -{ - friend class XMLBase; - friend class XMLDocument; -public: - /// Get the name of an element (which is the Value() of the node.) - const char* Name() const { - return Value(); - } - /// Set the name of the element. - void SetName( const char* str, bool staticMem=false ) { - SetValue( str, staticMem ); - } - - virtual XMLElement* ToElement() { - return this; - } - virtual const XMLElement* ToElement() const { - return this; - } - virtual bool Accept( XMLVisitor* visitor ) const; - - /** Given an attribute name, Attribute() returns the value - for the attribute of that name, or null if none - exists. For example: - - @verbatim - const char* value = ele->Attribute( "foo" ); - @endverbatim - - The 'value' parameter is normally null. However, if specified, - the attribute will only be returned if the 'name' and 'value' - match. This allow you to write code: - - @verbatim - if ( ele->Attribute( "foo", "bar" ) ) callFooIsBar(); - @endverbatim - - rather than: - @verbatim - if ( ele->Attribute( "foo" ) ) { - if ( strcmp( ele->Attribute( "foo" ), "bar" ) == 0 ) callFooIsBar(); - } - @endverbatim - */ - const char* Attribute( const char* name, const char* value=0 ) const; - - /** Given an attribute name, IntAttribute() returns the value - of the attribute interpreted as an integer. 0 will be - returned if there is an error. For a method with error - checking, see QueryIntAttribute() - */ - int IntAttribute( const char* name ) const { - int i=0; - QueryIntAttribute( name, &i ); - return i; - } - /// See IntAttribute() - unsigned UnsignedAttribute( const char* name ) const { - unsigned i=0; - QueryUnsignedAttribute( name, &i ); - return i; - } - /// See IntAttribute() - bool BoolAttribute( const char* name ) const { - bool b=false; - QueryBoolAttribute( name, &b ); - return b; - } - /// See IntAttribute() - double DoubleAttribute( const char* name ) const { - double d=0; - QueryDoubleAttribute( name, &d ); - return d; - } - /// See IntAttribute() - float FloatAttribute( const char* name ) const { - float f=0; - QueryFloatAttribute( name, &f ); - return f; - } - - /** Given an attribute name, QueryIntAttribute() returns - XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryIntAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - XMLError QueryIntAttribute( const char* name, int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryIntValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryUnsignedAttribute( const char* name, unsigned int* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryUnsignedValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryBoolAttribute( const char* name, bool* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryBoolValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryDoubleAttribute( const char* name, double* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryDoubleValue( value ); - } - /// See QueryIntAttribute() - XMLError QueryFloatAttribute( const char* name, float* value ) const { - const XMLAttribute* a = FindAttribute( name ); - if ( !a ) { - return XML_NO_ATTRIBUTE; - } - return a->QueryFloatValue( value ); - } - - - /** Given an attribute name, QueryAttribute() returns - XML_NO_ERROR, XML_WRONG_ATTRIBUTE_TYPE if the conversion - can't be performed, or XML_NO_ATTRIBUTE if the attribute - doesn't exist. It is overloaded for the primitive types, - and is a generally more convenient replacement of - QueryIntAttribute() and related functions. - - If successful, the result of the conversion - will be written to 'value'. If not successful, nothing will - be written to 'value'. This allows you to provide default - value: - - @verbatim - int value = 10; - QueryAttribute( "foo", &value ); // if "foo" isn't found, value will still be 10 - @endverbatim - */ - int QueryAttribute( const char* name, int* value ) const { - return QueryIntAttribute( name, value ); - } - - int QueryAttribute( const char* name, unsigned int* value ) const { - return QueryUnsignedAttribute( name, value ); - } - - int QueryAttribute( const char* name, bool* value ) const { - return QueryBoolAttribute( name, value ); - } - - int QueryAttribute( const char* name, double* value ) const { - return QueryDoubleAttribute( name, value ); - } - - int QueryAttribute( const char* name, float* value ) const { - return QueryFloatAttribute( name, value ); - } - - /// Sets the named attribute to value. - void SetAttribute( const char* name, const char* value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, int value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, unsigned value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, bool value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, double value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - /// Sets the named attribute to value. - void SetAttribute( const char* name, float value ) { - XMLAttribute* a = FindOrCreateAttribute( name ); - a->SetAttribute( value ); - } - - /** - Delete an attribute. - */ - void DeleteAttribute( const char* name ); - - /// Return the first attribute in the list. - const XMLAttribute* FirstAttribute() const { - return _rootAttribute; - } - /// Query a specific attribute in the list. - const XMLAttribute* FindAttribute( const char* name ) const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, GetText() is limited compared to getting the XMLText child - and accessing it directly. - - If the first child of 'this' is a XMLText, the GetText() - returns the character string of the Text node, else null is returned. - - This is a convenient method for getting the text of simple contained text: - @verbatim - This is text - const char* str = fooElement->GetText(); - @endverbatim - - 'str' will be a pointer to "This is text". - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then the value of str would be null. The first child node isn't a text node, it is - another element. From this XML: - @verbatim - This is text - @endverbatim - GetText() will return "This is ". - */ - const char* GetText() const; - - /** Convenience function for easy access to the text inside an element. Although easy - and concise, SetText() is limited compared to creating an XMLText child - and mutating it directly. - - If the first child of 'this' is a XMLText, SetText() sets its value to - the given string, otherwise it will create a first child that is an XMLText. - - This is a convenient method for setting the text of simple contained text: - @verbatim - This is text - fooElement->SetText( "Hullaballoo!" ); - Hullaballoo! - @endverbatim - - Note that this function can be misleading. If the element foo was created from - this XML: - @verbatim - This is text - @endverbatim - - then it will not change "This is text", but rather prefix it with a text element: - @verbatim - Hullaballoo!This is text - @endverbatim - - For this XML: - @verbatim - - @endverbatim - SetText() will generate - @verbatim - Hullaballoo! - @endverbatim - */ - void SetText( const char* inText ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. - void SetText( int value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. - void SetText( unsigned value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. - void SetText( bool value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. - void SetText( double value ); - /// Convenience method for setting text inside and element. See SetText() for important limitations. - void SetText( float value ); - - /** - Convenience method to query the value of a child text node. This is probably best - shown by example. Given you have a document is this form: - @verbatim - - 1 - 1.4 - - @endverbatim - - The QueryIntText() and similar functions provide a safe and easier way to get to the - "value" of x and y. - - @verbatim - int x = 0; - float y = 0; // types of x and y are contrived for example - const XMLElement* xElement = pointElement->FirstChildElement( "x" ); - const XMLElement* yElement = pointElement->FirstChildElement( "y" ); - xElement->QueryIntText( &x ); - yElement->QueryFloatText( &y ); - @endverbatim - - @returns XML_SUCCESS (0) on success, XML_CAN_NOT_CONVERT_TEXT if the text cannot be converted - to the requested type, and XML_NO_TEXT_NODE if there is no child text to query. - - */ - XMLError QueryIntText( int* ival ) const; - /// See QueryIntText() - XMLError QueryUnsignedText( unsigned* uval ) const; - /// See QueryIntText() - XMLError QueryBoolText( bool* bval ) const; - /// See QueryIntText() - XMLError QueryDoubleText( double* dval ) const; - /// See QueryIntText() - XMLError QueryFloatText( float* fval ) const; - - // internal: - enum { - OPEN, // - CLOSED, // - CLOSING // - }; - int ClosingType() const { - return _closingType; - } - char* ParseDeep( char* p, StrPair* endTag ); - virtual XMLNode* ShallowClone( XMLDocument* document ) const; - virtual bool ShallowEqual( const XMLNode* compare ) const; - -private: - XMLElement( XMLDocument* doc ); - virtual ~XMLElement(); - XMLElement( const XMLElement& ); // not supported - void operator=( const XMLElement& ); // not supported - - XMLAttribute* FindAttribute( const char* name ) { - return const_cast(const_cast(this)->FindAttribute( name )); - } - XMLAttribute* FindOrCreateAttribute( const char* name ); - //void LinkAttribute( XMLAttribute* attrib ); - char* ParseAttributes( char* p ); - static void DeleteAttribute( XMLAttribute* attribute ); - - enum { BUF_SIZE = 200 }; - int _closingType; - // The attribute list is ordered; there is no 'lastAttribute' - // because the list needs to be scanned for dupes before adding - // a new attribute. - XMLAttribute* _rootAttribute; -}; - - -enum Whitespace { - PRESERVE_WHITESPACE, - COLLAPSE_WHITESPACE -}; - - -/** A Document binds together all the functionality. - It can be saved, loaded, and printed to the screen. - All Nodes are connected and allocated to a Document. - If the Document is deleted, all its Nodes are also deleted. -*/ -class TINYXML2_LIB XMLDocument : public XMLNode -{ - friend class XMLElement; -public: - /// constructor - XMLDocument( bool processEntities = true, Whitespace = PRESERVE_WHITESPACE ); - ~XMLDocument(); - - virtual XMLDocument* ToDocument() { - return this; - } - virtual const XMLDocument* ToDocument() const { - return this; - } - - /** - Parse an XML file from a character string. - Returns XML_NO_ERROR (0) on success, or - an errorID. - - You may optionally pass in the 'nBytes', which is - the number of bytes which will be parsed. If not - specified, TinyXML-2 will assume 'xml' points to a - null terminated string. - */ - XMLError Parse( const char* xml, size_t nBytes=(size_t)(-1) ); - - /** - Load an XML file from disk. - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError LoadFile( const char* filename ); - - /** - Load an XML file from disk. You are responsible - for providing and closing the FILE*. - - NOTE: The file should be opened as binary ("rb") - not text in order for TinyXML-2 to correctly - do newline normalization. - - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError LoadFile( FILE* ); - - /** - Save the XML file to disk. - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError SaveFile( const char* filename, bool compact = false ); - - /** - Save the XML file to disk. You are responsible - for providing and closing the FILE*. - - Returns XML_NO_ERROR (0) on success, or - an errorID. - */ - XMLError SaveFile( FILE* fp, bool compact = false ); - - bool ProcessEntities() const { - return _processEntities; - } - Whitespace WhitespaceMode() const { - return _whitespace; - } - - /** - Returns true if this document has a leading Byte Order Mark of UTF8. - */ - bool HasBOM() const { - return _writeBOM; - } - /** Sets whether to write the BOM when writing the file. - */ - void SetBOM( bool useBOM ) { - _writeBOM = useBOM; - } - - /** Return the root element of DOM. Equivalent to FirstChildElement(). - To get the first node, use FirstChild(). - */ - XMLElement* RootElement() { - return FirstChildElement(); - } - const XMLElement* RootElement() const { - return FirstChildElement(); - } - - /** Print the Document. If the Printer is not provided, it will - print to stdout. If you provide Printer, this can print to a file: - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Or you can use a printer to print to memory: - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - // printer.CStr() has a const char* to the XML - @endverbatim - */ - void Print( XMLPrinter* streamer=0 ) const; - virtual bool Accept( XMLVisitor* visitor ) const; - - /** - Create a new Element associated with - this Document. The memory for the Element - is managed by the Document. - */ - XMLElement* NewElement( const char* name ); - /** - Create a new Comment associated with - this Document. The memory for the Comment - is managed by the Document. - */ - XMLComment* NewComment( const char* comment ); - /** - Create a new Text associated with - this Document. The memory for the Text - is managed by the Document. - */ - XMLText* NewText( const char* text ); - /** - Create a new Declaration associated with - this Document. The memory for the object - is managed by the Document. - - If the 'text' param is null, the standard - declaration is used.: - @verbatim - - @endverbatim - */ - XMLDeclaration* NewDeclaration( const char* text=0 ); - /** - Create a new Unknown associated with - this Document. The memory for the object - is managed by the Document. - */ - XMLUnknown* NewUnknown( const char* text ); - - /** - Delete a node associated with this document. - It will be unlinked from the DOM. - */ - void DeleteNode( XMLNode* node ); - - void SetError( XMLError error, const char* str1, const char* str2 ); - - /// Return true if there was an error parsing the document. - bool Error() const { - return _errorID != XML_NO_ERROR; - } - /// Return the errorID. - XMLError ErrorID() const { - return _errorID; - } - const char* ErrorName() const; - - /// Return a possibly helpful diagnostic location or string. - const char* GetErrorStr1() const { - return _errorStr1; - } - /// Return a possibly helpful secondary diagnostic location or string. - const char* GetErrorStr2() const { - return _errorStr2; - } - /// If there is an error, print it to stdout. - void PrintError() const; - - /// Clear the document, resetting it to the initial state. - void Clear(); - - // internal - char* Identify( char* p, XMLNode** node ); - - virtual XMLNode* ShallowClone( XMLDocument* /*document*/ ) const { - return 0; - } - virtual bool ShallowEqual( const XMLNode* /*compare*/ ) const { - return false; - } - -private: - XMLDocument( const XMLDocument& ); // not supported - void operator=( const XMLDocument& ); // not supported - - bool _writeBOM; - bool _processEntities; - XMLError _errorID; - Whitespace _whitespace; - const char* _errorStr1; - const char* _errorStr2; - char* _charBuffer; - - MemPoolT< sizeof(XMLElement) > _elementPool; - MemPoolT< sizeof(XMLAttribute) > _attributePool; - MemPoolT< sizeof(XMLText) > _textPool; - MemPoolT< sizeof(XMLComment) > _commentPool; - - static const char* _errorNames[XML_ERROR_COUNT]; - - void Parse(); -}; - - -/** - A XMLHandle is a class that wraps a node pointer with null checks; this is - an incredibly useful thing. Note that XMLHandle is not part of the TinyXML-2 - DOM structure. It is a separate utility class. - - Take an example: - @verbatim - - - - - - - @endverbatim - - Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very - easy to write a *lot* of code that looks like: - - @verbatim - XMLElement* root = document.FirstChildElement( "Document" ); - if ( root ) - { - XMLElement* element = root->FirstChildElement( "Element" ); - if ( element ) - { - XMLElement* child = element->FirstChildElement( "Child" ); - if ( child ) - { - XMLElement* child2 = child->NextSiblingElement( "Child" ); - if ( child2 ) - { - // Finally do something useful. - @endverbatim - - And that doesn't even cover "else" cases. XMLHandle addresses the verbosity - of such code. A XMLHandle checks for null pointers so it is perfectly safe - and correct to use: - - @verbatim - XMLHandle docHandle( &document ); - XMLElement* child2 = docHandle.FirstChildElement( "Document" ).FirstChildElement( "Element" ).FirstChildElement().NextSiblingElement(); - if ( child2 ) - { - // do something useful - @endverbatim - - Which is MUCH more concise and useful. - - It is also safe to copy handles - internally they are nothing more than node pointers. - @verbatim - XMLHandle handleCopy = handle; - @endverbatim - - See also XMLConstHandle, which is the same as XMLHandle, but operates on const objects. -*/ -class TINYXML2_LIB XMLHandle -{ -public: - /// Create a handle from any node (at any depth of the tree.) This can be a null pointer. - XMLHandle( XMLNode* node ) { - _node = node; - } - /// Create a handle from a node. - XMLHandle( XMLNode& node ) { - _node = &node; - } - /// Copy constructor - XMLHandle( const XMLHandle& ref ) { - _node = ref._node; - } - /// Assignment - XMLHandle& operator=( const XMLHandle& ref ) { - _node = ref._node; - return *this; - } - - /// Get the first child of this handle. - XMLHandle FirstChild() { - return XMLHandle( _node ? _node->FirstChild() : 0 ); - } - /// Get the first child element of this handle. - XMLHandle FirstChildElement( const char* value=0 ) { - return XMLHandle( _node ? _node->FirstChildElement( value ) : 0 ); - } - /// Get the last child of this handle. - XMLHandle LastChild() { - return XMLHandle( _node ? _node->LastChild() : 0 ); - } - /// Get the last child element of this handle. - XMLHandle LastChildElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->LastChildElement( _value ) : 0 ); - } - /// Get the previous sibling of this handle. - XMLHandle PreviousSibling() { - return XMLHandle( _node ? _node->PreviousSibling() : 0 ); - } - /// Get the previous sibling element of this handle. - XMLHandle PreviousSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); - } - /// Get the next sibling of this handle. - XMLHandle NextSibling() { - return XMLHandle( _node ? _node->NextSibling() : 0 ); - } - /// Get the next sibling element of this handle. - XMLHandle NextSiblingElement( const char* _value=0 ) { - return XMLHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); - } - - /// Safe cast to XMLNode. This can return null. - XMLNode* ToNode() { - return _node; - } - /// Safe cast to XMLElement. This can return null. - XMLElement* ToElement() { - return ( ( _node == 0 ) ? 0 : _node->ToElement() ); - } - /// Safe cast to XMLText. This can return null. - XMLText* ToText() { - return ( ( _node == 0 ) ? 0 : _node->ToText() ); - } - /// Safe cast to XMLUnknown. This can return null. - XMLUnknown* ToUnknown() { - return ( ( _node == 0 ) ? 0 : _node->ToUnknown() ); - } - /// Safe cast to XMLDeclaration. This can return null. - XMLDeclaration* ToDeclaration() { - return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() ); - } - -private: - XMLNode* _node; -}; - - -/** - A variant of the XMLHandle class for working with const XMLNodes and Documents. It is the - same in all regards, except for the 'const' qualifiers. See XMLHandle for API. -*/ -class TINYXML2_LIB XMLConstHandle -{ -public: - XMLConstHandle( const XMLNode* node ) { - _node = node; - } - XMLConstHandle( const XMLNode& node ) { - _node = &node; - } - XMLConstHandle( const XMLConstHandle& ref ) { - _node = ref._node; - } - - XMLConstHandle& operator=( const XMLConstHandle& ref ) { - _node = ref._node; - return *this; - } - - const XMLConstHandle FirstChild() const { - return XMLConstHandle( _node ? _node->FirstChild() : 0 ); - } - const XMLConstHandle FirstChildElement( const char* value=0 ) const { - return XMLConstHandle( _node ? _node->FirstChildElement( value ) : 0 ); - } - const XMLConstHandle LastChild() const { - return XMLConstHandle( _node ? _node->LastChild() : 0 ); - } - const XMLConstHandle LastChildElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->LastChildElement( _value ) : 0 ); - } - const XMLConstHandle PreviousSibling() const { - return XMLConstHandle( _node ? _node->PreviousSibling() : 0 ); - } - const XMLConstHandle PreviousSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->PreviousSiblingElement( _value ) : 0 ); - } - const XMLConstHandle NextSibling() const { - return XMLConstHandle( _node ? _node->NextSibling() : 0 ); - } - const XMLConstHandle NextSiblingElement( const char* _value=0 ) const { - return XMLConstHandle( _node ? _node->NextSiblingElement( _value ) : 0 ); - } - - - const XMLNode* ToNode() const { - return _node; - } - const XMLElement* ToElement() const { - return ( ( _node == 0 ) ? 0 : _node->ToElement() ); - } - const XMLText* ToText() const { - return ( ( _node == 0 ) ? 0 : _node->ToText() ); - } - const XMLUnknown* ToUnknown() const { - return ( ( _node == 0 ) ? 0 : _node->ToUnknown() ); - } - const XMLDeclaration* ToDeclaration() const { - return ( ( _node == 0 ) ? 0 : _node->ToDeclaration() ); - } - -private: - const XMLNode* _node; -}; - - -/** - Printing functionality. The XMLPrinter gives you more - options than the XMLDocument::Print() method. - - It can: - -# Print to memory. - -# Print to a file you provide. - -# Print XML without a XMLDocument. - - Print to Memory - - @verbatim - XMLPrinter printer; - doc.Print( &printer ); - SomeFunction( printer.CStr() ); - @endverbatim - - Print to a File - - You provide the file pointer. - @verbatim - XMLPrinter printer( fp ); - doc.Print( &printer ); - @endverbatim - - Print without a XMLDocument - - When loading, an XML parser is very useful. However, sometimes - when saving, it just gets in the way. The code is often set up - for streaming, and constructing the DOM is just overhead. - - The Printer supports the streaming case. The following code - prints out a trivially simple XML file without ever creating - an XML document. - - @verbatim - XMLPrinter printer( fp ); - printer.OpenElement( "foo" ); - printer.PushAttribute( "foo", "bar" ); - printer.CloseElement(); - @endverbatim -*/ -class TINYXML2_LIB XMLPrinter : public XMLVisitor -{ -public: - /** Construct the printer. If the FILE* is specified, - this will print to the FILE. Else it will print - to memory, and the result is available in CStr(). - If 'compact' is set to true, then output is created - with only required whitespace and newlines. - */ - XMLPrinter( FILE* file=0, bool compact = false, int depth = 0 ); - virtual ~XMLPrinter() {} - - /** If streaming, write the BOM and declaration. */ - void PushHeader( bool writeBOM, bool writeDeclaration ); - /** If streaming, start writing an element. - The element must be closed with CloseElement() - */ - void OpenElement( const char* name, bool compactMode=false ); - /// If streaming, add an attribute to an open element. - void PushAttribute( const char* name, const char* value ); - void PushAttribute( const char* name, int value ); - void PushAttribute( const char* name, unsigned value ); - void PushAttribute( const char* name, bool value ); - void PushAttribute( const char* name, double value ); - /// If streaming, close the Element. - virtual void CloseElement( bool compactMode=false ); - - /// Add a text node. - void PushText( const char* text, bool cdata=false ); - /// Add a text node from an integer. - void PushText( int value ); - /// Add a text node from an unsigned. - void PushText( unsigned value ); - /// Add a text node from a bool. - void PushText( bool value ); - /// Add a text node from a float. - void PushText( float value ); - /// Add a text node from a double. - void PushText( double value ); - - /// Add a comment - void PushComment( const char* comment ); - - void PushDeclaration( const char* value ); - void PushUnknown( const char* value ); - - virtual bool VisitEnter( const XMLDocument& /*doc*/ ); - virtual bool VisitExit( const XMLDocument& /*doc*/ ) { - return true; - } - - virtual bool VisitEnter( const XMLElement& element, const XMLAttribute* attribute ); - virtual bool VisitExit( const XMLElement& element ); - - virtual bool Visit( const XMLText& text ); - virtual bool Visit( const XMLComment& comment ); - virtual bool Visit( const XMLDeclaration& declaration ); - virtual bool Visit( const XMLUnknown& unknown ); - - /** - If in print to memory mode, return a pointer to - the XML file in memory. - */ - const char* CStr() const { - return _buffer.Mem(); - } - /** - If in print to memory mode, return the size - of the XML file in memory. (Note the size returned - includes the terminating null.) - */ - int CStrSize() const { - return _buffer.Size(); - } - /** - If in print to memory mode, reset the buffer to the - beginning. - */ - void ClearBuffer() { - _buffer.Clear(); - _buffer.Push(0); - } - -protected: - virtual bool CompactMode( const XMLElement& ) { return _compactMode; } - - /** Prints out the space before an element. You may override to change - the space and tabs used. A PrintSpace() override should call Print(). - */ - virtual void PrintSpace( int depth ); - void Print( const char* format, ... ); - - void SealElementIfJustOpened(); - bool _elementJustOpened; - DynArray< const char*, 10 > _stack; - -private: - void PrintString( const char*, bool restrictedEntitySet ); // prints out, after detecting entities. - - bool _firstElement; - FILE* _fp; - int _depth; - int _textDepth; - bool _processEntities; - bool _compactMode; - - enum { - ENTITY_RANGE = 64, - BUF_SIZE = 200 - }; - bool _entityFlag[ENTITY_RANGE]; - bool _restrictedEntityFlag[ENTITY_RANGE]; - - DynArray< char, 20 > _buffer; -}; - - -} // tinyxml2 - -#if defined(_MSC_VER) -# pragma warning(pop) -#endif - -#endif // TINYXML2_INCLUDED diff -Nru leanify-0.4.3/lib/zopfli/blocksplitter.c leanify-0.4.3+git20181014/lib/zopfli/blocksplitter.c --- leanify-0.4.3/lib/zopfli/blocksplitter.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/blocksplitter.c 2017-08-01 20:51:09.000000000 +0000 @@ -24,7 +24,6 @@ #include #include "deflate.h" -#include "lz77.h" #include "squeeze.h" #include "tree.h" #include "util.h" @@ -39,9 +38,10 @@ /* Finds minimum of function f(i) where is is of type size_t, f(i) is of type double, i is in range start-end (excluding end). +Outputs the minimum value in *smallest and returns the index of this value. */ static size_t FindMinimum(FindMinimumFun f, void* context, - size_t start, size_t end) { + size_t start, size_t end, double* smallest) { if (end - start < 1024) { double best = ZOPFLI_LARGE_FLOAT; size_t result = start; @@ -53,6 +53,7 @@ result = i; } } + *smallest = best; return result; } else { /* Try to find minimum faster by recursively checking multiple points. */ @@ -88,6 +89,7 @@ pos = p[besti]; lastbest = best; } + *smallest = lastbest; return pos; #undef NUM } @@ -103,16 +105,13 @@ lstart: start of block lend: end of block (not inclusive) */ -static double EstimateCost(const unsigned short* litlens, - const unsigned short* dists, +static double EstimateCost(const ZopfliLZ77Store* lz77, size_t lstart, size_t lend) { - return ZopfliCalculateBlockSize(litlens, dists, lstart, lend, 2); + return ZopfliCalculateBlockSizeAutoType(lz77, lstart, lend); } typedef struct SplitCostContext { - const unsigned short* litlens; - const unsigned short* dists; - size_t llsize; + const ZopfliLZ77Store* lz77; size_t start; size_t end; } SplitCostContext; @@ -125,8 +124,7 @@ */ static double SplitCost(size_t i, void* context) { SplitCostContext* c = (SplitCostContext*)context; - return EstimateCost(c->litlens, c->dists, c->start, i) + - EstimateCost(c->litlens, c->dists, i, c->end); + return EstimateCost(c->lz77, c->start, i) + EstimateCost(c->lz77, i, c->end); } static void AddSorted(size_t value, size_t** out, size_t* outsize) { @@ -147,9 +145,8 @@ /* Prints the block split points as decimal and hex values in the terminal. */ -static void PrintBlockSplitPoints(const unsigned short* litlens, - const unsigned short* dists, - size_t llsize, const size_t* lz77splitpoints, +static void PrintBlockSplitPoints(const ZopfliLZ77Store* lz77, + const size_t* lz77splitpoints, size_t nlz77points) { size_t* splitpoints = 0; size_t npoints = 0; @@ -158,8 +155,8 @@ index values. */ size_t pos = 0; if (nlz77points > 0) { - for (i = 0; i < llsize; i++) { - size_t length = dists[i] == 0 ? 1 : litlens[i]; + for (i = 0; i < lz77->size; i++) { + size_t length = lz77->dists[i] == 0 ? 1 : lz77->litlens[i]; if (lz77splitpoints[npoints] == i) { ZOPFLI_APPEND_DATA(pos, &splitpoints, &npoints); if (npoints == nlz77points) break; @@ -186,7 +183,7 @@ Finds next block to try to split, the largest of the available ones. The largest is chosen to make sure that if only a limited amount of blocks is requested, their sizes are spread evenly. -llsize: the size of the LL77 data, which is the size of the done array here. +lz77size: the size of the LL77 data, which is the size of the done array here. done: array indicating which blocks starting at that position are no longer splittable (splitting them increases rather than decreases cost). splitpoints: the splitpoints found so far. @@ -196,7 +193,7 @@ returns 1 if a block was found, 0 if no block found (all are done). */ static int FindLargestSplittableBlock( - size_t llsize, const unsigned char* done, + size_t lz77size, const unsigned char* done, const size_t* splitpoints, size_t npoints, size_t* lstart, size_t* lend) { size_t longest = 0; @@ -204,7 +201,7 @@ size_t i; for (i = 0; i <= npoints; i++) { size_t start = i == 0 ? 0 : splitpoints[i - 1]; - size_t end = i == npoints ? llsize - 1 : splitpoints[i]; + size_t end = i == npoints ? lz77size - 1 : splitpoints[i]; if (!done[start] && end - start > longest) { *lstart = start; *lend = end; @@ -216,9 +213,7 @@ } void ZopfliBlockSplitLZ77(const ZopfliOptions* options, - const unsigned short* litlens, - const unsigned short* dists, - size_t llsize, size_t maxblocks, + const ZopfliLZ77Store* lz77, size_t maxblocks, size_t** splitpoints, size_t* npoints) { size_t lstart, lend; size_t i; @@ -227,14 +222,14 @@ unsigned char* done; double splitcost, origcost; - if (llsize < 10) return; /* This code fails on tiny files. */ + if (lz77->size < 10) return; /* This code fails on tiny files. */ - done = (unsigned char*)malloc(llsize); + done = (unsigned char*)malloc(lz77->size); if (!done) exit(-1); /* Allocation failed. */ - for (i = 0; i < llsize; i++) done[i] = 0; + for (i = 0; i < lz77->size; i++) done[i] = 0; lstart = 0; - lend = llsize; + lend = lz77->size; for (;;) { SplitCostContext c; @@ -242,20 +237,16 @@ break; } - c.litlens = litlens; - c.dists = dists; - c.llsize = llsize; + c.lz77 = lz77; c.start = lstart; c.end = lend; assert(lstart < lend); - llpos = FindMinimum(SplitCost, &c, lstart + 1, lend); + llpos = FindMinimum(SplitCost, &c, lstart + 1, lend, &splitcost); assert(llpos > lstart); assert(llpos < lend); - splitcost = EstimateCost(litlens, dists, lstart, llpos) + - EstimateCost(litlens, dists, llpos, lend); - origcost = EstimateCost(litlens, dists, lstart, lend); + origcost = EstimateCost(lz77, lstart, lend); if (splitcost > origcost || llpos == lstart + 1 || llpos == lend) { done[lstart] = 1; @@ -265,7 +256,7 @@ } if (!FindLargestSplittableBlock( - llsize, done, *splitpoints, *npoints, &lstart, &lend)) { + lz77->size, done, *splitpoints, *npoints, &lstart, &lend)) { break; /* No further split will probably reduce compression. */ } @@ -275,7 +266,7 @@ } if (options->verbose) { - PrintBlockSplitPoints(litlens, dists, llsize, *splitpoints, *npoints); + PrintBlockSplitPoints(lz77, *splitpoints, *npoints); } free(done); @@ -290,25 +281,22 @@ size_t* lz77splitpoints = 0; size_t nlz77points = 0; ZopfliLZ77Store store; + ZopfliHash hash; + ZopfliHash* h = &hash; - ZopfliInitLZ77Store(&store); - - s.options = options; - s.blockstart = instart; - s.blockend = inend; -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - s.lmc = 0; -#endif + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, instart, inend, 0, &s); + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); *npoints = 0; *splitpoints = 0; /* Unintuitively, Using a simple LZ77 method here instead of ZopfliLZ77Optimal results in better blocks. */ - ZopfliLZ77Greedy(&s, in, instart, inend, &store); + ZopfliLZ77Greedy(&s, in, instart, inend, &store, h); ZopfliBlockSplitLZ77(options, - store.litlens, store.dists, store.size, maxblocks, + &store, maxblocks, &lz77splitpoints, &nlz77points); /* Convert LZ77 positions to positions in the uncompressed input. */ @@ -326,7 +314,9 @@ assert(*npoints == nlz77points); free(lz77splitpoints); + ZopfliCleanBlockState(&s); ZopfliCleanLZ77Store(&store); + ZopfliCleanHash(h); } void ZopfliBlockSplitSimple(const unsigned char* in, diff -Nru leanify-0.4.3/lib/zopfli/blocksplitter.h leanify-0.4.3+git20181014/lib/zopfli/blocksplitter.h --- leanify-0.4.3/lib/zopfli/blocksplitter.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/blocksplitter.h 2017-08-01 20:51:09.000000000 +0000 @@ -30,21 +30,17 @@ #include +#include "lz77.h" #include "zopfli.h" /* Does blocksplitting on LZ77 data. The output splitpoints are indices in the LZ77 data. -litlens: lz77 lit/lengths -dists: lz77 distances -llsize: size of litlens and dists maxblocks: set a limit to the amount of blocks. Set to 0 to mean no limit. */ void ZopfliBlockSplitLZ77(const ZopfliOptions* options, - const unsigned short* litlens, - const unsigned short* dists, - size_t llsize, size_t maxblocks, + const ZopfliLZ77Store* lz77, size_t maxblocks, size_t** splitpoints, size_t* npoints); /* diff -Nru leanify-0.4.3/lib/zopfli/cache.c leanify-0.4.3+git20181014/lib/zopfli/cache.c --- leanify-0.4.3/lib/zopfli/cache.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/cache.c 2017-08-01 20:51:09.000000000 +0000 @@ -32,7 +32,9 @@ /* Rather large amount of memory. */ lmc->sublen = (unsigned char*)malloc(ZOPFLI_CACHE_LENGTH * 3 * blocksize); if(lmc->sublen == NULL) { - fprintf(stderr,"Error: Out of memory. Tried allocating %lu bytes of memory.\n",(unsigned long)(ZOPFLI_CACHE_LENGTH * 3 * blocksize)); + fprintf(stderr, + "Error: Out of memory. Tried allocating %lu bytes of memory.\n", + ZOPFLI_CACHE_LENGTH * 3 * blocksize); exit (EXIT_FAILURE); } diff -Nru leanify-0.4.3/lib/zopfli/deflate.c leanify-0.4.3+git20181014/lib/zopfli/deflate.c --- leanify-0.4.3/lib/zopfli/deflate.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/deflate.c 2018-07-03 15:36:27.000000000 +0000 @@ -24,8 +24,8 @@ #include #include "blocksplitter.h" -#include "lz77.h" #include "squeeze.h" +#include "symbols.h" #include "tree.h" /* @@ -294,8 +294,7 @@ end code 256. expected_data_size is the uncompressed block size, used for assert, but you can set it to 0 to not do the assertion. */ -static void AddLZ77Data(const unsigned short* litlens, - const unsigned short* dists, +static void AddLZ77Data(const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, size_t expected_data_size, const unsigned* ll_symbols, const unsigned* ll_lengths, @@ -306,8 +305,8 @@ size_t i; for (i = lstart; i < lend; i++) { - unsigned dist = dists[i]; - unsigned litlen = litlens[i]; + unsigned dist = lz77->dists[i]; + unsigned litlen = lz77->litlens[i]; if (dist == 0) { assert(litlen < 256); assert(ll_lengths[litlen] > 0); @@ -343,29 +342,83 @@ } /* -Calculates size of the part after the header and tree of an LZ77 block, in bits. +Same as CalculateBlockSymbolSize, but for block size smaller than histogram +size. */ -static size_t CalculateBlockSymbolSize(const unsigned* ll_lengths, - const unsigned* d_lengths, - const unsigned short* litlens, - const unsigned short* dists, - size_t lstart, size_t lend) { +static size_t CalculateBlockSymbolSizeSmall(const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { size_t result = 0; size_t i; for (i = lstart; i < lend; i++) { - if (dists[i] == 0) { - result += ll_lengths[litlens[i]]; + assert(i < lz77->size); + assert(lz77->litlens[i] < 259); + if (lz77->dists[i] == 0) { + result += ll_lengths[lz77->litlens[i]]; } else { - result += ll_lengths[ZopfliGetLengthSymbol(litlens[i])]; - result += d_lengths[ZopfliGetDistSymbol(dists[i])]; - result += ZopfliGetLengthExtraBits(litlens[i]); - result += ZopfliGetDistExtraBits(dists[i]); + int ll_symbol = ZopfliGetLengthSymbol(lz77->litlens[i]); + int d_symbol = ZopfliGetDistSymbol(lz77->dists[i]); + result += ll_lengths[ll_symbol]; + result += d_lengths[d_symbol]; + result += ZopfliGetLengthSymbolExtraBits(ll_symbol); + result += ZopfliGetDistSymbolExtraBits(d_symbol); } } result += ll_lengths[256]; /*end symbol*/ return result; } +/* +Same as CalculateBlockSymbolSize, but with the histogram provided by the caller. +*/ +static size_t CalculateBlockSymbolSizeGivenCounts(const size_t* ll_counts, + const size_t* d_counts, + const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + size_t result = 0; + size_t i; + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + return CalculateBlockSymbolSizeSmall( + ll_lengths, d_lengths, lz77, lstart, lend); + } else { + for (i = 0; i < 256; i++) { + result += ll_lengths[i] * ll_counts[i]; + } + for (i = 257; i < 286; i++) { + result += ll_lengths[i] * ll_counts[i]; + result += ZopfliGetLengthSymbolExtraBits(i) * ll_counts[i]; + } + for (i = 0; i < 30; i++) { + result += d_lengths[i] * d_counts[i]; + result += ZopfliGetDistSymbolExtraBits(i) * d_counts[i]; + } + result += ll_lengths[256]; /*end symbol*/ + return result; + } +} + +/* +Calculates size of the part after the header and tree of an LZ77 block, in bits. +*/ +static size_t CalculateBlockSymbolSize(const unsigned* ll_lengths, + const unsigned* d_lengths, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + return CalculateBlockSymbolSizeSmall( + ll_lengths, d_lengths, lz77, lstart, lend); + } else { + size_t ll_counts[ZOPFLI_NUM_LL]; + size_t d_counts[ZOPFLI_NUM_D]; + ZopfliLZ77GetHistogram(lz77, lstart, lend, ll_counts, d_counts); + return CalculateBlockSymbolSizeGivenCounts( + ll_counts, d_counts, ll_lengths, d_lengths, lz77, lstart, lend); + } +} + static size_t AbsDiff(size_t x, size_t y) { if (x > y) return x - y; @@ -374,9 +427,9 @@ } /* -Change the population counts in a way that the consequent Hufmann tree -compression, especially its rle-part will be more likely to compress this data -more efficiently. length containts the size of the histogram. +Changes the population counts in a way that the consequent Huffman tree +compression, especially its rle-part, will be more likely to compress this data +more efficiently. length contains the size of the histogram. */ void OptimizeHuffmanForRle(int length, size_t* counts) { int i, k, stride; @@ -465,49 +518,150 @@ } /* +Tries out OptimizeHuffmanForRle for this block, if the result is smaller, +uses it, otherwise keeps the original. Returns size of encoded tree and data in +bits, not including the 3-bit block header. +*/ +static double TryOptimizeHuffmanForRle( + const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, + const size_t* ll_counts, const size_t* d_counts, + unsigned* ll_lengths, unsigned* d_lengths) { + size_t ll_counts2[ZOPFLI_NUM_LL]; + size_t d_counts2[ZOPFLI_NUM_D]; + unsigned ll_lengths2[ZOPFLI_NUM_LL]; + unsigned d_lengths2[ZOPFLI_NUM_D]; + double treesize; + double datasize; + double treesize2; + double datasize2; + + treesize = CalculateTreeSize(ll_lengths, d_lengths); + datasize = CalculateBlockSymbolSizeGivenCounts(ll_counts, d_counts, + ll_lengths, d_lengths, lz77, lstart, lend); + + memcpy(ll_counts2, ll_counts, sizeof(ll_counts2)); + memcpy(d_counts2, d_counts, sizeof(d_counts2)); + OptimizeHuffmanForRle(ZOPFLI_NUM_LL, ll_counts2); + OptimizeHuffmanForRle(ZOPFLI_NUM_D, d_counts2); + ZopfliCalculateBitLengths(ll_counts2, ZOPFLI_NUM_LL, 15, ll_lengths2); + ZopfliCalculateBitLengths(d_counts2, ZOPFLI_NUM_D, 15, d_lengths2); + PatchDistanceCodesForBuggyDecoders(d_lengths2); + + treesize2 = CalculateTreeSize(ll_lengths2, d_lengths2); + datasize2 = CalculateBlockSymbolSizeGivenCounts(ll_counts, d_counts, + ll_lengths2, d_lengths2, lz77, lstart, lend); + + if (treesize2 + datasize2 < treesize + datasize) { + memcpy(ll_lengths, ll_lengths2, sizeof(ll_lengths2)); + memcpy(d_lengths, d_lengths2, sizeof(d_lengths2)); + return treesize2 + datasize2; + } + return treesize + datasize; +} + +/* Calculates the bit lengths for the symbols for dynamic blocks. Chooses bit lengths that give the smallest size of tree encoding + encoding of all the symbols to have smallest output size. This are not necessarily the ideal Huffman -bit lengths. +bit lengths. Returns size of encoded tree and data in bits, not including the +3-bit block header. */ -static void GetDynamicLengths(const unsigned short* litlens, - const unsigned short* dists, - size_t lstart, size_t lend, - unsigned* ll_lengths, unsigned* d_lengths) { - size_t ll_counts[288]; - size_t d_counts[32]; - - ZopfliLZ77Counts(litlens, dists, lstart, lend, ll_counts, d_counts); - OptimizeHuffmanForRle(288, ll_counts); - OptimizeHuffmanForRle(32, d_counts); - ZopfliCalculateBitLengths(ll_counts, 288, 15, ll_lengths); - ZopfliCalculateBitLengths(d_counts, 32, 15, d_lengths); +static double GetDynamicLengths(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + unsigned* ll_lengths, unsigned* d_lengths) { + size_t ll_counts[ZOPFLI_NUM_LL]; + size_t d_counts[ZOPFLI_NUM_D]; + + ZopfliLZ77GetHistogram(lz77, lstart, lend, ll_counts, d_counts); + ll_counts[256] = 1; /* End symbol. */ + ZopfliCalculateBitLengths(ll_counts, ZOPFLI_NUM_LL, 15, ll_lengths); + ZopfliCalculateBitLengths(d_counts, ZOPFLI_NUM_D, 15, d_lengths); PatchDistanceCodesForBuggyDecoders(d_lengths); + return TryOptimizeHuffmanForRle( + lz77, lstart, lend, ll_counts, d_counts, ll_lengths, d_lengths); } -double ZopfliCalculateBlockSize(const unsigned short* litlens, - const unsigned short* dists, +double ZopfliCalculateBlockSize(const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, int btype) { - unsigned ll_lengths[288]; - unsigned d_lengths[32]; + unsigned ll_lengths[ZOPFLI_NUM_LL]; + unsigned d_lengths[ZOPFLI_NUM_D]; double result = 3; /* bfinal and btype bits */ - assert(btype == 1 || btype == 2); /* This is not for uncompressed blocks. */ - - if(btype == 1) { + if (btype == 0) { + size_t length = ZopfliLZ77GetByteRange(lz77, lstart, lend); + size_t rem = length % 65535; + size_t blocks = length / 65535 + (rem ? 1 : 0); + /* An uncompressed block must actually be split into multiple blocks if it's + larger than 65535 bytes long. Eeach block header is 5 bytes: 3 bits, + padding, LEN and NLEN (potential less padding for first one ignored). */ + return blocks * 5 * 8 + length * 8; + } if (btype == 1) { GetFixedTree(ll_lengths, d_lengths); + result += CalculateBlockSymbolSize( + ll_lengths, d_lengths, lz77, lstart, lend); } else { - GetDynamicLengths(litlens, dists, lstart, lend, ll_lengths, d_lengths); - result += CalculateTreeSize(ll_lengths, d_lengths); + result += GetDynamicLengths(lz77, lstart, lend, ll_lengths, d_lengths); } - result += CalculateBlockSymbolSize( - ll_lengths, d_lengths, litlens, dists, lstart, lend); - return result; } +double ZopfliCalculateBlockSizeAutoType(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + double uncompressedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 0); + /* Don't do the expensive fixed cost calculation for larger blocks that are + unlikely to use it. */ + double fixedcost = (lz77->size > 1000) ? + uncompressedcost : ZopfliCalculateBlockSize(lz77, lstart, lend, 1); + double dyncost = ZopfliCalculateBlockSize(lz77, lstart, lend, 2); + return (uncompressedcost < fixedcost && uncompressedcost < dyncost) + ? uncompressedcost + : (fixedcost < dyncost ? fixedcost : dyncost); +} + +/* Since an uncompressed block can be max 65535 in size, it actually adds +multible blocks if needed. */ +static void AddNonCompressedBlock(const ZopfliOptions* options, int final, + const unsigned char* in, size_t instart, + size_t inend, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + size_t pos = instart; + (void)options; + for (;;) { + size_t i; + unsigned short blocksize = 65535; + unsigned short nlen; + int currentfinal; + + if (pos + blocksize > inend) blocksize = inend - pos; + currentfinal = pos + blocksize >= inend; + + nlen = ~blocksize; + + AddBit(final && currentfinal, bp, out, outsize); + /* BTYPE 00 */ + AddBit(0, bp, out, outsize); + AddBit(0, bp, out, outsize); + + /* Any bits of input up to the next byte boundary are ignored. */ + *bp = 0; + + ZOPFLI_APPEND_DATA(blocksize % 256, out, outsize); + ZOPFLI_APPEND_DATA((blocksize / 256) % 256, out, outsize); + ZOPFLI_APPEND_DATA(nlen % 256, out, outsize); + ZOPFLI_APPEND_DATA((nlen / 256) % 256, out, outsize); + + for (i = 0; i < blocksize; i++) { + ZOPFLI_APPEND_DATA(in[pos + i], out, outsize); + } + + if (currentfinal) break; + pos += blocksize; + } +} + /* Adds a deflate block with the given LZ77 data to the output. options: global program options @@ -526,20 +680,27 @@ outsize: dynamic output array size */ static void AddLZ77Block(const ZopfliOptions* options, int btype, int final, - const unsigned short* litlens, - const unsigned short* dists, + const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, size_t expected_data_size, unsigned char* bp, unsigned char** out, size_t* outsize) { - unsigned ll_lengths[288]; - unsigned d_lengths[32]; - unsigned ll_symbols[288]; - unsigned d_symbols[32]; - size_t detect_block_size; + unsigned ll_lengths[ZOPFLI_NUM_LL]; + unsigned d_lengths[ZOPFLI_NUM_D]; + unsigned ll_symbols[ZOPFLI_NUM_LL]; + unsigned d_symbols[ZOPFLI_NUM_D]; + size_t detect_block_size = *outsize; size_t compressed_size; size_t uncompressed_size = 0; size_t i; + if (btype == 0) { + size_t length = ZopfliLZ77GetByteRange(lz77, lstart, lend); + size_t pos = lstart == lend ? 0 : lz77->pos[lstart]; + size_t end = pos + length; + AddNonCompressedBlock(options, final, + lz77->data, pos, end, bp, out, outsize); + return; + } AddBit(final, bp, out, outsize); AddBit(btype & 1, bp, out, outsize); @@ -553,7 +714,7 @@ unsigned detect_tree_size; assert(btype == 2); - GetDynamicLengths(litlens, dists, lstart, lend, ll_lengths, d_lengths); + GetDynamicLengths(lz77, lstart, lend, ll_lengths, d_lengths); detect_tree_size = *outsize; AddDynamicTree(ll_lengths, d_lengths, bp, out, outsize); @@ -562,18 +723,18 @@ } } - ZopfliLengthsToSymbols(ll_lengths, 288, 15, ll_symbols); - ZopfliLengthsToSymbols(d_lengths, 32, 15, d_symbols); + ZopfliLengthsToSymbols(ll_lengths, ZOPFLI_NUM_LL, 15, ll_symbols); + ZopfliLengthsToSymbols(d_lengths, ZOPFLI_NUM_D, 15, d_symbols); detect_block_size = *outsize; - AddLZ77Data(litlens, dists, lstart, lend, expected_data_size, + AddLZ77Data(lz77, lstart, lend, expected_data_size, ll_symbols, ll_lengths, d_symbols, d_lengths, bp, out, outsize); /* End symbol. */ AddHuffmanBits(ll_symbols[256], ll_lengths[256], bp, out, outsize); for (i = lstart; i < lend; i++) { - uncompressed_size += dists[i] == 0 ? 1 : litlens[i]; + uncompressed_size += lz77->dists[i] == 0 ? 1 : lz77->litlens[i]; } compressed_size = *outsize - detect_block_size; if (options->verbose) { @@ -583,262 +744,165 @@ } } -static void DeflateDynamicBlock(const ZopfliOptions* options, int final, - const unsigned char* in, - size_t instart, size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { - ZopfliBlockState s; - size_t blocksize = inend - instart; - ZopfliLZ77Store store; - int btype = 2; - - ZopfliInitLZ77Store(&store); - - s.options = options; - s.blockstart = instart; - s.blockend = inend; -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - s.lmc = (ZopfliLongestMatchCache*)malloc(sizeof(ZopfliLongestMatchCache)); - ZopfliInitCache(blocksize, s.lmc); -#endif - - ZopfliLZ77Optimal(&s, in, instart, inend, &store); - - /* For small block, encoding with fixed tree can be smaller. For large block, - don't bother doing this expensive test, dynamic tree will be better.*/ - if (store.size < 1000) { - double dyncost, fixedcost; - ZopfliLZ77Store fixedstore; - ZopfliInitLZ77Store(&fixedstore); - ZopfliLZ77OptimalFixed(&s, in, instart, inend, &fixedstore); - dyncost = ZopfliCalculateBlockSize(store.litlens, store.dists, - 0, store.size, 2); - fixedcost = ZopfliCalculateBlockSize(fixedstore.litlens, fixedstore.dists, - 0, fixedstore.size, 1); - if (fixedcost < dyncost) { - btype = 1; - ZopfliCleanLZ77Store(&store); - store = fixedstore; +static void AddLZ77BlockAutoType(const ZopfliOptions* options, int final, + const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t expected_data_size, + unsigned char* bp, + unsigned char** out, size_t* outsize) { + double uncompressedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 0); + double fixedcost = ZopfliCalculateBlockSize(lz77, lstart, lend, 1); + double dyncost = ZopfliCalculateBlockSize(lz77, lstart, lend, 2); + + /* Whether to perform the expensive calculation of creating an optimal block + with fixed huffman tree to check if smaller. Only do this for small blocks or + blocks which already are pretty good with fixed huffman tree. */ + int expensivefixed = (lz77->size < 1000) || fixedcost <= dyncost * 1.1; + + ZopfliLZ77Store fixedstore; + if (lstart == lend) { + /* Smallest empty block is represented by fixed block */ + AddBits(final, 1, bp, out, outsize); + AddBits(1, 2, bp, out, outsize); /* btype 01 */ + AddBits(0, 7, bp, out, outsize); /* end symbol has code 0000000 */ + return; + } + ZopfliInitLZ77Store(lz77->data, &fixedstore); + if (expensivefixed) { + /* Recalculate the LZ77 with ZopfliLZ77OptimalFixed */ + size_t instart = lz77->pos[lstart]; + size_t inend = instart + ZopfliLZ77GetByteRange(lz77, lstart, lend); + + ZopfliBlockState s; + ZopfliInitBlockState(options, instart, inend, 1, &s); + ZopfliLZ77OptimalFixed(&s, lz77->data, instart, inend, &fixedstore); + fixedcost = ZopfliCalculateBlockSize(&fixedstore, 0, fixedstore.size, 1); + ZopfliCleanBlockState(&s); + } + + if (uncompressedcost < fixedcost && uncompressedcost < dyncost) { + AddLZ77Block(options, 0, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); + } else if (fixedcost < dyncost) { + if (expensivefixed) { + AddLZ77Block(options, 1, final, &fixedstore, 0, fixedstore.size, + expected_data_size, bp, out, outsize); } else { - ZopfliCleanLZ77Store(&fixedstore); + AddLZ77Block(options, 1, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); } - } - - AddLZ77Block(s.options, btype, final, - store.litlens, store.dists, 0, store.size, - blocksize, bp, out, outsize); - -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - ZopfliCleanCache(s.lmc); - free(s.lmc); -#endif - ZopfliCleanLZ77Store(&store); -} - -static void DeflateFixedBlock(const ZopfliOptions* options, int final, - const unsigned char* in, - size_t instart, size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { - ZopfliBlockState s; - size_t blocksize = inend - instart; - ZopfliLZ77Store store; - - ZopfliInitLZ77Store(&store); - - s.options = options; - s.blockstart = instart; - s.blockend = inend; -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - s.lmc = (ZopfliLongestMatchCache*)malloc(sizeof(ZopfliLongestMatchCache)); - ZopfliInitCache(blocksize, s.lmc); -#endif - - ZopfliLZ77OptimalFixed(&s, in, instart, inend, &store); - - AddLZ77Block(s.options, 1, final, store.litlens, store.dists, 0, store.size, - blocksize, bp, out, outsize); - -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - ZopfliCleanCache(s.lmc); - free(s.lmc); -#endif - ZopfliCleanLZ77Store(&store); -} - -static void DeflateNonCompressedBlock(const ZopfliOptions* options, int final, - const unsigned char* in, size_t instart, - size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { - size_t i; - size_t blocksize = inend - instart; - unsigned short nlen = ~blocksize; - - (void)options; - assert(blocksize < 65536); /* Non compressed blocks are max this size. */ - - AddBit(final, bp, out, outsize); - /* BTYPE 00 */ - AddBit(0, bp, out, outsize); - AddBit(0, bp, out, outsize); - - /* Any bits of input up to the next byte boundary are ignored. */ - *bp = 0; - - ZOPFLI_APPEND_DATA(blocksize % 256, out, outsize); - ZOPFLI_APPEND_DATA((blocksize / 256) % 256, out, outsize); - ZOPFLI_APPEND_DATA(nlen % 256, out, outsize); - ZOPFLI_APPEND_DATA((nlen / 256) % 256, out, outsize); - - for (i = instart; i < inend; i++) { - ZOPFLI_APPEND_DATA(in[i], out, outsize); - } -} - -static void DeflateBlock(const ZopfliOptions* options, - int btype, int final, - const unsigned char* in, size_t instart, size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { - if (btype == 0) { - DeflateNonCompressedBlock( - options, final, in, instart, inend, bp, out, outsize); - } else if (btype == 1) { - DeflateFixedBlock(options, final, in, instart, inend, bp, out, outsize); } else { - assert (btype == 2); - DeflateDynamicBlock(options, final, in, instart, inend, bp, out, outsize); + AddLZ77Block(options, 2, final, lz77, lstart, lend, + expected_data_size, bp, out, outsize); } + + ZopfliCleanLZ77Store(&fixedstore); } /* -Does squeeze strategy where first block splitting is done, then each block is -squeezed. -Parameters: see description of the ZopfliDeflate function. +Deflate a part, to allow ZopfliDeflate() to use multiple master blocks if +needed. +It is possible to call this function multiple times in a row, shifting +instart and inend to next bytes of the data. If instart is larger than 0, then +previous bytes are used as the initial dictionary for LZ77. +This function will usually output multiple deflate blocks. If final is 1, then +the final bit will be set on the last block. */ -static void DeflateSplittingFirst(const ZopfliOptions* options, - int btype, int final, - const unsigned char* in, - size_t instart, size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { +void ZopfliDeflatePart(const ZopfliOptions* options, int btype, int final, + const unsigned char* in, size_t instart, size_t inend, + unsigned char* bp, unsigned char** out, + size_t* outsize) { size_t i; - size_t* splitpoints = 0; + /* byte coordinates rather than lz77 index */ + size_t* splitpoints_uncompressed = 0; size_t npoints = 0; + size_t* splitpoints = 0; + double totalcost = 0; + ZopfliLZ77Store lz77; + + /* If btype=2 is specified, it tries all block types. If a lesser btype is + given, then however it forces that one. Neither of the lesser types needs + block splitting as they have no dynamic huffman trees. */ if (btype == 0) { - ZopfliBlockSplitSimple(in, instart, inend, 65535, &splitpoints, &npoints); + AddNonCompressedBlock(options, final, in, instart, inend, bp, out, outsize); + return; } else if (btype == 1) { - /* If all blocks are fixed tree, splitting into separate blocks only - increases the total size. Leave npoints at 0, this represents 1 block. */ - } else { - ZopfliBlockSplit(options, in, instart, inend, - options->blocksplittingmax, &splitpoints, &npoints); - } + ZopfliLZ77Store store; + ZopfliBlockState s; + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, instart, inend, 1, &s); - for (i = 0; i <= npoints; i++) { - size_t start = i == 0 ? instart : splitpoints[i - 1]; - size_t end = i == npoints ? inend : splitpoints[i]; - DeflateBlock(options, btype, i == npoints && final, in, start, end, + ZopfliLZ77OptimalFixed(&s, in, instart, inend, &store); + AddLZ77Block(options, btype, final, &store, 0, store.size, 0, bp, out, outsize); - } - free(splitpoints); -} - -/* -Does squeeze strategy where first the best possible lz77 is done, and then based -on that data, block splitting is done. -Parameters: see description of the ZopfliDeflate function. -*/ -static void DeflateSplittingLast(const ZopfliOptions* options, - int btype, int final, - const unsigned char* in, - size_t instart, size_t inend, - unsigned char* bp, - unsigned char** out, size_t* outsize) { - size_t i; - ZopfliBlockState s; - ZopfliLZ77Store store; - size_t* splitpoints = 0; - size_t npoints = 0; + ZopfliCleanBlockState(&s); + ZopfliCleanLZ77Store(&store); + return; + } - if (btype == 0) { - /* This function only supports LZ77 compression. DeflateSplittingFirst - supports the special case of noncompressed data. Punt it to that one. */ - DeflateSplittingFirst(options, btype, final, - in, instart, inend, - bp, out, outsize); - } - assert(btype == 1 || btype == 2); - - ZopfliInitLZ77Store(&store); - - s.options = options; - s.blockstart = instart; - s.blockend = inend; -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - s.lmc = (ZopfliLongestMatchCache*)malloc(sizeof(ZopfliLongestMatchCache)); - ZopfliInitCache(inend - instart, s.lmc); -#endif - if (btype == 2) { - ZopfliLZ77Optimal(&s, in, instart, inend, &store); - } else { - assert (btype == 1); - ZopfliLZ77OptimalFixed(&s, in, instart, inend, &store); + if (options->blocksplitting) { + ZopfliBlockSplit(options, in, instart, inend, + options->blocksplittingmax, + &splitpoints_uncompressed, &npoints); + splitpoints = (size_t*)malloc(sizeof(*splitpoints) * npoints); } - if (btype == 1) { - /* If all blocks are fixed tree, splitting into separate blocks only - increases the total size. Leave npoints at 0, this represents 1 block. */ - } else { - ZopfliBlockSplitLZ77(options, store.litlens, store.dists, store.size, - options->blocksplittingmax, &splitpoints, &npoints); + ZopfliInitLZ77Store(in, &lz77); + + for (i = 0; i <= npoints; i++) { + size_t start = i == 0 ? instart : splitpoints_uncompressed[i - 1]; + size_t end = i == npoints ? inend : splitpoints_uncompressed[i]; + ZopfliBlockState s; + ZopfliLZ77Store store; + ZopfliInitLZ77Store(in, &store); + ZopfliInitBlockState(options, start, end, 1, &s); + ZopfliLZ77Optimal(&s, in, start, end, options->numiterations, &store); + totalcost += ZopfliCalculateBlockSizeAutoType(&store, 0, store.size); + + ZopfliAppendLZ77Store(&store, &lz77); + if (i < npoints) splitpoints[i] = lz77.size; + + ZopfliCleanBlockState(&s); + ZopfliCleanLZ77Store(&store); + } + + /* Second block splitting attempt */ + if (options->blocksplitting && npoints > 1) { + size_t* splitpoints2 = 0; + size_t npoints2 = 0; + double totalcost2 = 0; + + ZopfliBlockSplitLZ77(options, &lz77, + options->blocksplittingmax, &splitpoints2, &npoints2); + + for (i = 0; i <= npoints2; i++) { + size_t start = i == 0 ? 0 : splitpoints2[i - 1]; + size_t end = i == npoints2 ? lz77.size : splitpoints2[i]; + totalcost2 += ZopfliCalculateBlockSizeAutoType(&lz77, start, end); + } + + if (totalcost2 < totalcost) { + free(splitpoints); + splitpoints = splitpoints2; + npoints = npoints2; + } else { + free(splitpoints2); + } } for (i = 0; i <= npoints; i++) { size_t start = i == 0 ? 0 : splitpoints[i - 1]; - size_t end = i == npoints ? store.size : splitpoints[i]; - AddLZ77Block(options, btype, i == npoints && final, - store.litlens, store.dists, start, end, 0, - bp, out, outsize); + size_t end = i == npoints ? lz77.size : splitpoints[i]; + AddLZ77BlockAutoType(options, i == npoints && final, + &lz77, start, end, 0, + bp, out, outsize); } -#ifdef ZOPFLI_LONGEST_MATCH_CACHE - ZopfliCleanCache(s.lmc); - free(s.lmc); -#endif - - ZopfliCleanLZ77Store(&store); + ZopfliCleanLZ77Store(&lz77); free(splitpoints); -} - -/* -Deflate a part, to allow ZopfliDeflate() to use multiple master blocks if -needed. -It is possible to call this function multiple times in a row, shifting -instart and inend to next bytes of the data. If instart is larger than 0, then -previous bytes are used as the initial dictionary for LZ77. -This function will usually output multiple deflate blocks. If final is 1, then -the final bit will be set on the last block. -*/ -void ZopfliDeflatePart(const ZopfliOptions* options, int btype, int final, - const unsigned char* in, size_t instart, size_t inend, - unsigned char* bp, unsigned char** out, - size_t* outsize) { - if (options->blocksplitting) { - if (options->blocksplittinglast) { - DeflateSplittingLast(options, btype, final, in, instart, inend, - bp, out, outsize); - } else { - DeflateSplittingFirst(options, btype, final, in, instart, inend, - bp, out, outsize); - } - } else { - DeflateBlock(options, btype, final, in, instart, inend, bp, out, outsize); - } + free(splitpoints_uncompressed); } void ZopfliDeflate(const ZopfliOptions* options, int btype, int final, @@ -849,19 +913,19 @@ ZopfliDeflatePart(options, btype, final, in, 0, insize, bp, out, outsize); #else size_t i = 0; - while (i < insize) { + do { int masterfinal = (i + ZOPFLI_MASTER_BLOCK_SIZE >= insize); int final2 = final && masterfinal; size_t size = masterfinal ? insize - i : ZOPFLI_MASTER_BLOCK_SIZE; ZopfliDeflatePart(options, btype, final2, in, i, i + size, bp, out, outsize); i += size; - } + } while (i < insize); #endif if (options->verbose) { fprintf(stderr, - "Original Size: %d, Deflate: %d, Compression: %f%% Removed\n", - (int)insize, (int)(*outsize - offset), + "Original Size: %lu, Deflate: %lu, Compression: %f%% Removed\n", + (unsigned long)insize, (unsigned long)(*outsize - offset), 100.0 * (double)(insize - (*outsize - offset)) / (double)insize); } } diff -Nru leanify-0.4.3/lib/zopfli/deflate.h leanify-0.4.3+git20181014/lib/zopfli/deflate.h --- leanify-0.4.3/lib/zopfli/deflate.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/deflate.h 2017-08-01 20:51:09.000000000 +0000 @@ -25,6 +25,7 @@ "squeeze" LZ77 compression backend. */ +#include "lz77.h" #include "zopfli.h" #ifdef __cplusplus @@ -75,10 +76,15 @@ lstart: start of block lend: end of block (not inclusive) */ -double ZopfliCalculateBlockSize(const unsigned short* litlens, - const unsigned short* dists, +double ZopfliCalculateBlockSize(const ZopfliLZ77Store* lz77, size_t lstart, size_t lend, int btype); +/* +Calculates block size in bits, automatically using the best btype. +*/ +double ZopfliCalculateBlockSizeAutoType(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend); + #ifdef __cplusplus } // extern "C" #endif diff -Nru leanify-0.4.3/lib/zopfli/gzip_container.c leanify-0.4.3+git20181014/lib/zopfli/gzip_container.c --- leanify-0.4.3/lib/zopfli/gzip_container.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/gzip_container.c 2017-08-01 20:51:09.000000000 +0000 @@ -24,56 +24,63 @@ #include "deflate.h" -/* Table of CRCs of all 8-bit messages. */ -static unsigned long crc_table[256]; - -/* Flag: has the table been computed? Initially false. */ -static int crc_table_computed = 0; - -/* Makes the table for a fast CRC. */ -static void MakeCRCTable() { - unsigned long c; - int n, k; - for (n = 0; n < 256; n++) { - c = (unsigned long) n; - for (k = 0; k < 8; k++) { - if (c & 1) { - c = 0xedb88320L ^ (c >> 1); - } else { - c = c >> 1; - } - } - crc_table[n] = c; +/* CRC polynomial: 0xedb88320 */ +static const unsigned long crc32_table[256] = { + 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, + 3915621685u, 2657392035u, 249268274u, 2044508324u, 3772115230u, 2547177864u, + 162941995u, 2125561021u, 3887607047u, 2428444049u, 498536548u, 1789927666u, + 4089016648u, 2227061214u, 450548861u, 1843258603u, 4107580753u, 2211677639u, + 325883990u, 1684777152u, 4251122042u, 2321926636u, 335633487u, 1661365465u, + 4195302755u, 2366115317u, 997073096u, 1281953886u, 3579855332u, 2724688242u, + 1006888145u, 1258607687u, 3524101629u, 2768942443u, 901097722u, 1119000684u, + 3686517206u, 2898065728u, 853044451u, 1172266101u, 3705015759u, 2882616665u, + 651767980u, 1373503546u, 3369554304u, 3218104598u, 565507253u, 1454621731u, + 3485111705u, 3099436303u, 671266974u, 1594198024u, 3322730930u, 2970347812u, + 795835527u, 1483230225u, 3244367275u, 3060149565u, 1994146192u, 31158534u, + 2563907772u, 4023717930u, 1907459465u, 112637215u, 2680153253u, 3904427059u, + 2013776290u, 251722036u, 2517215374u, 3775830040u, 2137656763u, 141376813u, + 2439277719u, 3865271297u, 1802195444u, 476864866u, 2238001368u, 4066508878u, + 1812370925u, 453092731u, 2181625025u, 4111451223u, 1706088902u, 314042704u, + 2344532202u, 4240017532u, 1658658271u, 366619977u, 2362670323u, 4224994405u, + 1303535960u, 984961486u, 2747007092u, 3569037538u, 1256170817u, 1037604311u, + 2765210733u, 3554079995u, 1131014506u, 879679996u, 2909243462u, 3663771856u, + 1141124467u, 855842277u, 2852801631u, 3708648649u, 1342533948u, 654459306u, + 3188396048u, 3373015174u, 1466479909u, 544179635u, 3110523913u, 3462522015u, + 1591671054u, 702138776u, 2966460450u, 3352799412u, 1504918807u, 783551873u, + 3082640443u, 3233442989u, 3988292384u, 2596254646u, 62317068u, 1957810842u, + 3939845945u, 2647816111u, 81470997u, 1943803523u, 3814918930u, 2489596804u, + 225274430u, 2053790376u, 3826175755u, 2466906013u, 167816743u, 2097651377u, + 4027552580u, 2265490386u, 503444072u, 1762050814u, 4150417245u, 2154129355u, + 426522225u, 1852507879u, 4275313526u, 2312317920u, 282753626u, 1742555852u, + 4189708143u, 2394877945u, 397917763u, 1622183637u, 3604390888u, 2714866558u, + 953729732u, 1340076626u, 3518719985u, 2797360999u, 1068828381u, 1219638859u, + 3624741850u, 2936675148u, 906185462u, 1090812512u, 3747672003u, 2825379669u, + 829329135u, 1181335161u, 3412177804u, 3160834842u, 628085408u, 1382605366u, + 3423369109u, 3138078467u, 570562233u, 1426400815u, 3317316542u, 2998733608u, + 733239954u, 1555261956u, 3268935591u, 3050360625u, 752459403u, 1541320221u, + 2607071920u, 3965973030u, 1969922972u, 40735498u, 2617837225u, 3943577151u, + 1913087877u, 83908371u, 2512341634u, 3803740692u, 2075208622u, 213261112u, + 2463272603u, 3855990285u, 2094854071u, 198958881u, 2262029012u, 4057260610u, + 1759359992u, 534414190u, 2176718541u, 4139329115u, 1873836001u, 414664567u, + 2282248934u, 4279200368u, 1711684554u, 285281116u, 2405801727u, 4167216745u, + 1634467795u, 376229701u, 2685067896u, 3608007406u, 1308918612u, 956543938u, + 2808555105u, 3495958263u, 1231636301u, 1047427035u, 2932959818u, 3654703836u, + 1088359270u, 936918000u, 2847714899u, 3736837829u, 1202900863u, 817233897u, + 3183342108u, 3401237130u, 1404277552u, 615818150u, 3134207493u, 3453421203u, + 1423857449u, 601450431u, 3009837614u, 3294710456u, 1567103746u, 711928724u, + 3020668471u, 3272380065u, 1510334235u, 755167117u +}; + +/* Returns the CRC32 */ +static unsigned long CRC(const unsigned char* data, size_t size) { + unsigned long result = 0xffffffffu; + for (; size > 0; size--) { + result = crc32_table[(result ^ *(data++)) & 0xff] ^ (result >> 8); } - crc_table_computed = 1; -} - - -/* -Updates a running crc with the bytes buf[0..len-1] and returns -the updated crc. The crc should be initialized to zero. -*/ -static unsigned long UpdateCRC(unsigned long crc, - const unsigned char *buf, size_t len) { - unsigned long c = crc ^ 0xffffffffL; - unsigned n; - - if (!crc_table_computed) - MakeCRCTable(); - for (n = 0; n < len; n++) { - c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - return c ^ 0xffffffffL; -} - -/* Returns the CRC of the bytes buf[0..len-1]. */ -static unsigned long CRC(const unsigned char* buf, int len) { - return UpdateCRC(0L, buf, len); + return result ^ 0xffffffffu; } -/* -Compresses the data according to the gzip specification. -*/ +/* Compresses the data according to the gzip specification, RFC 1952. */ void ZopfliGzipCompress(const ZopfliOptions* options, const unsigned char* in, size_t insize, unsigned char** out, size_t* outsize) { diff -Nru leanify-0.4.3/lib/zopfli/hash.c leanify-0.4.3+git20181014/lib/zopfli/hash.c --- leanify-0.4.3/lib/zopfli/hash.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/hash.c 2017-08-01 20:51:09.000000000 +0000 @@ -26,13 +26,26 @@ #define HASH_SHIFT 5 #define HASH_MASK 32767 -void ZopfliInitHash(size_t window_size, ZopfliHash* h) { - size_t i; - - h->val = 0; +void ZopfliAllocHash(size_t window_size, ZopfliHash* h) { h->head = (int*)malloc(sizeof(*h->head) * 65536); h->prev = (unsigned short*)malloc(sizeof(*h->prev) * window_size); h->hashval = (int*)malloc(sizeof(*h->hashval) * window_size); + +#ifdef ZOPFLI_HASH_SAME + h->same = (unsigned short*)malloc(sizeof(*h->same) * window_size); +#endif + +#ifdef ZOPFLI_HASH_SAME_HASH + h->head2 = (int*)malloc(sizeof(*h->head2) * 65536); + h->prev2 = (unsigned short*)malloc(sizeof(*h->prev2) * window_size); + h->hashval2 = (int*)malloc(sizeof(*h->hashval2) * window_size); +#endif +} + +void ZopfliResetHash(size_t window_size, ZopfliHash* h) { + size_t i; + + h->val = 0; for (i = 0; i < 65536; i++) { h->head[i] = -1; /* -1 indicates no head so far. */ } @@ -42,7 +55,6 @@ } #ifdef ZOPFLI_HASH_SAME - h->same = (unsigned short*)malloc(sizeof(*h->same) * window_size); for (i = 0; i < window_size; i++) { h->same[i] = 0; } @@ -50,9 +62,6 @@ #ifdef ZOPFLI_HASH_SAME_HASH h->val2 = 0; - h->head2 = (int*)malloc(sizeof(*h->head2) * 65536); - h->prev2 = (unsigned short*)malloc(sizeof(*h->prev2) * window_size); - h->hashval2 = (int*)malloc(sizeof(*h->hashval2) * window_size); for (i = 0; i < 65536; i++) { h->head2[i] = -1; } @@ -129,7 +138,6 @@ void ZopfliWarmupHash(const unsigned char* array, size_t pos, size_t end, ZopfliHash* h) { - (void)end; UpdateHashValue(h, array[pos + 0]); - UpdateHashValue(h, array[pos + 1]); + if (pos + 1 < end) UpdateHashValue(h, array[pos + 1]); } diff -Nru leanify-0.4.3/lib/zopfli/hash.h leanify-0.4.3+git20181014/lib/zopfli/hash.h --- leanify-0.4.3/lib/zopfli/hash.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/hash.h 2017-08-01 20:51:09.000000000 +0000 @@ -46,10 +46,13 @@ #endif } ZopfliHash; -/* Allocates and initializes all fields of ZopfliHash. */ -void ZopfliInitHash(size_t window_size, ZopfliHash* h); +/* Allocates ZopfliHash memory. */ +void ZopfliAllocHash(size_t window_size, ZopfliHash* h); -/* Frees all fields of ZopfliHash. */ +/* Resets all fields of ZopfliHash. */ +void ZopfliResetHash(size_t window_size, ZopfliHash* h); + +/* Frees ZopfliHash memory. */ void ZopfliCleanHash(ZopfliHash* h); /* diff -Nru leanify-0.4.3/lib/zopfli/katajainen.c leanify-0.4.3+git20181014/lib/zopfli/katajainen.c --- leanify-0.4.3/lib/zopfli/katajainen.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/katajainen.c 2018-07-03 15:36:27.000000000 +0000 @@ -26,6 +26,7 @@ #include "katajainen.h" #include #include +#include typedef struct Node Node; @@ -36,16 +37,13 @@ size_t weight; /* Total weight (symbol count) of this chain. */ Node* tail; /* Previous node(s) of this chain, or 0 if none. */ int count; /* Leaf symbol index, or number of leaves before this chain. */ - char inuse; /* Tracking for garbage collection. */ }; /* Memory pool for nodes. */ typedef struct NodePool { - Node* nodes; /* The pool. */ - Node* next; /* Pointer to a possibly free node in the pool. */ - int size; /* Size of the memory pool. */ + Node* next; /* Pointer to a free node in the pool. */ } NodePool; /* @@ -55,41 +53,9 @@ node->weight = weight; node->count = count; node->tail = tail; - node->inuse = 1; } /* -Finds a free location in the memory pool. Performs garbage collection if needed. -lists: If given, used to mark in-use nodes during garbage collection. -maxbits: Size of lists. -pool: Memory pool to get free node from. -*/ -static Node* GetFreeNode(Node* (*lists)[2], int maxbits, NodePool* pool) { - for (;;) { - if (pool->next >= &pool->nodes[pool->size]) { - /* Garbage collection. */ - int i; - for (i = 0; i < pool->size; i++) { - pool->nodes[i].inuse = 0; - } - if (lists) { - for (i = 0; i < maxbits * 2; i++) { - Node* node; - for (node = lists[i / 2][i % 2]; node; node = node->tail) { - node->inuse = 1; - } - } - } - pool->next = &pool->nodes[0]; - } - if (!pool->next->inuse) break; /* Found one. */ - pool->next++; - } - return pool->next++; -} - - -/* Performs a Boundary Package-Merge step. Puts a new chain in the given list. The new chain is, depending on the weights, a leaf or a combination of two chains from the previous list. @@ -99,18 +65,16 @@ numsymbols: Number of leaves. pool: the node memory pool. index: The index of the list in which a new chain or leaf is required. -final: Whether this is the last time this function is called. If it is then it - is no more needed to recursively call self. */ -static void BoundaryPM(Node* (*lists)[2], int maxbits, - Node* leaves, int numsymbols, NodePool* pool, int index, char final) { +static void BoundaryPM(Node* (*lists)[2], Node* leaves, int numsymbols, + NodePool* pool, int index) { Node* newchain; Node* oldchain; int lastcount = lists[index][1]->count; /* Count of last chain of list. */ if (index == 0 && lastcount >= numsymbols) return; - newchain = GetFreeNode(lists, maxbits, pool); + newchain = pool->next++; oldchain = lists[index][1]; /* These are set up before the recursive calls below, so that there is a list @@ -129,15 +93,31 @@ newchain); } else { InitNode(sum, lastcount, lists[index - 1][1], newchain); - if (!final) { - /* Two lookahead chains of previous list used up, create new ones. */ - BoundaryPM(lists, maxbits, leaves, numsymbols, pool, index - 1, 0); - BoundaryPM(lists, maxbits, leaves, numsymbols, pool, index - 1, 0); - } + /* Two lookahead chains of previous list used up, create new ones. */ + BoundaryPM(lists, leaves, numsymbols, pool, index - 1); + BoundaryPM(lists, leaves, numsymbols, pool, index - 1); } } } +static void BoundaryPMFinal(Node* (*lists)[2], + Node* leaves, int numsymbols, NodePool* pool, int index) { + int lastcount = lists[index][1]->count; /* Count of last chain of list. */ + + size_t sum = lists[index - 1][0]->weight + lists[index - 1][1]->weight; + + if (lastcount < numsymbols && sum > leaves[lastcount].weight) { + Node* newchain = pool->next; + Node* oldchain = lists[index][1]->tail; + + lists[index][1] = newchain; + newchain->count = lastcount + 1; + newchain->tail = oldchain; + } else { + lists[index][1]->tail = lists[index - 1][1]; + } +} + /* Initializes each list with as lookahead chains the two leaves with lowest weights. @@ -145,8 +125,8 @@ static void InitLists( NodePool* pool, const Node* leaves, int maxbits, Node* (*lists)[2]) { int i; - Node* node0 = GetFreeNode(0, maxbits, pool); - Node* node1 = GetFreeNode(0, maxbits, pool); + Node* node0 = pool->next++; + Node* node1 = pool->next++; InitNode(leaves[0].weight, 1, 0, node0); InitNode(leaves[1].weight, 2, 0, node1); for (i = 0; i < maxbits; i++) { @@ -161,12 +141,24 @@ chain: Chain to extract the bit length from (last chain from last list). */ static void ExtractBitLengths(Node* chain, Node* leaves, unsigned* bitlengths) { + int counts[16] = {0}; + unsigned end = 16; + unsigned ptr = 15; + unsigned value = 1; Node* node; + int val; + for (node = chain; node; node = node->tail) { - int i; - for (i = 0; i < node->count; i++) { - bitlengths[leaves[i].count]++; + counts[--end] = node->count; + } + + val = counts[15]; + while (ptr >= end) { + for (; val > counts[ptr - 1]; val--) { + bitlengths[leaves[val - 1].count] = value; } + ptr--; + value++; } } @@ -183,6 +175,7 @@ int i; int numsymbols = 0; /* Amount of symbols with frequency > 0. */ int numBoundaryPMRuns; + Node* nodes; /* Array of lists of chains. Each list requires only two lookahead chains at a time, so each list is a array of two Node*'s. */ @@ -219,33 +212,51 @@ free(leaves); return 0; /* Only one symbol, give it bitlength 1, not 0. OK. */ } + if (numsymbols == 2) { + bitlengths[leaves[0].count]++; + bitlengths[leaves[1].count]++; + free(leaves); + return 0; + } - /* Sort the leaves from lightest to heaviest. */ + /* Sort the leaves from lightest to heaviest. Add count into the same + variable for stable sorting. */ + for (i = 0; i < numsymbols; i++) { + if (leaves[i].weight >= + ((size_t)1 << (sizeof(leaves[0].weight) * CHAR_BIT - 9))) { + free(leaves); + return 1; /* Error, we need 9 bits for the count. */ + } + leaves[i].weight = (leaves[i].weight << 9) | leaves[i].count; + } qsort(leaves, numsymbols, sizeof(Node), LeafComparator); + for (i = 0; i < numsymbols; i++) { + leaves[i].weight >>= 9; + } - /* Initialize node memory pool. */ - pool.size = 2 * maxbits * (maxbits + 1); - pool.nodes = (Node*)malloc(pool.size * sizeof(*pool.nodes)); - pool.next = pool.nodes; - for (i = 0; i < pool.size; i++) { - pool.nodes[i].inuse = 0; + if (numsymbols - 1 < maxbits) { + maxbits = numsymbols - 1; } + /* Initialize node memory pool. */ + nodes = (Node*)malloc(maxbits * 2 * numsymbols * sizeof(Node)); + pool.next = nodes; + lists = (Node* (*)[2])malloc(maxbits * sizeof(*lists)); InitLists(&pool, leaves, maxbits, lists); /* In the last list, 2 * numsymbols - 2 active chains need to be created. Two are already created in the initialization. Each BoundaryPM run creates one. */ numBoundaryPMRuns = 2 * numsymbols - 4; - for (i = 0; i < numBoundaryPMRuns; i++) { - char final = i == numBoundaryPMRuns - 1; - BoundaryPM(lists, maxbits, leaves, numsymbols, &pool, maxbits - 1, final); + for (i = 0; i < numBoundaryPMRuns - 1; i++) { + BoundaryPM(lists, leaves, numsymbols, &pool, maxbits - 1); } + BoundaryPMFinal(lists, leaves, numsymbols, &pool, maxbits - 1); ExtractBitLengths(lists[maxbits - 1][1], leaves, bitlengths); free(lists); free(leaves); - free(pool.nodes); + free(nodes); return 0; /* OK. */ } diff -Nru leanify-0.4.3/lib/zopfli/lz77.c leanify-0.4.3+git20181014/lib/zopfli/lz77.c --- leanify-0.4.3/lib/zopfli/lz77.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/lz77.c 2017-08-01 20:51:09.000000000 +0000 @@ -18,37 +18,76 @@ */ #include "lz77.h" +#include "symbols.h" #include "util.h" #include #include #include -void ZopfliInitLZ77Store(ZopfliLZ77Store* store) { +void ZopfliInitLZ77Store(const unsigned char* data, ZopfliLZ77Store* store) { store->size = 0; store->litlens = 0; store->dists = 0; + store->pos = 0; + store->data = data; + store->ll_symbol = 0; + store->d_symbol = 0; + store->ll_counts = 0; + store->d_counts = 0; } void ZopfliCleanLZ77Store(ZopfliLZ77Store* store) { free(store->litlens); free(store->dists); + free(store->pos); + free(store->ll_symbol); + free(store->d_symbol); + free(store->ll_counts); + free(store->d_counts); +} + +static size_t CeilDiv(size_t a, size_t b) { + return (a + b - 1) / b; } void ZopfliCopyLZ77Store( const ZopfliLZ77Store* source, ZopfliLZ77Store* dest) { size_t i; + size_t llsize = ZOPFLI_NUM_LL * CeilDiv(source->size, ZOPFLI_NUM_LL); + size_t dsize = ZOPFLI_NUM_D * CeilDiv(source->size, ZOPFLI_NUM_D); ZopfliCleanLZ77Store(dest); + ZopfliInitLZ77Store(source->data, dest); dest->litlens = (unsigned short*)malloc(sizeof(*dest->litlens) * source->size); dest->dists = (unsigned short*)malloc(sizeof(*dest->dists) * source->size); - - if (!dest->litlens || !dest->dists) exit(-1); /* Allocation failed. */ + dest->pos = (size_t*)malloc(sizeof(*dest->pos) * source->size); + dest->ll_symbol = + (unsigned short*)malloc(sizeof(*dest->ll_symbol) * source->size); + dest->d_symbol = + (unsigned short*)malloc(sizeof(*dest->d_symbol) * source->size); + dest->ll_counts = (size_t*)malloc(sizeof(*dest->ll_counts) * llsize); + dest->d_counts = (size_t*)malloc(sizeof(*dest->d_counts) * dsize); + + /* Allocation failed. */ + if (!dest->litlens || !dest->dists) exit(-1); + if (!dest->pos) exit(-1); + if (!dest->ll_symbol || !dest->d_symbol) exit(-1); + if (!dest->ll_counts || !dest->d_counts) exit(-1); dest->size = source->size; for (i = 0; i < source->size; i++) { dest->litlens[i] = source->litlens[i]; dest->dists[i] = source->dists[i]; + dest->pos[i] = source->pos[i]; + dest->ll_symbol[i] = source->ll_symbol[i]; + dest->d_symbol[i] = source->d_symbol[i]; + } + for (i = 0; i < llsize; i++) { + dest->ll_counts[i] = source->ll_counts[i]; + } + for (i = 0; i < dsize; i++) { + dest->d_counts[i] = source->d_counts[i]; } } @@ -57,10 +96,149 @@ context must be a ZopfliLZ77Store*. */ void ZopfliStoreLitLenDist(unsigned short length, unsigned short dist, - ZopfliLZ77Store* store) { - size_t size2 = store->size; /* Needed for using ZOPFLI_APPEND_DATA twice. */ + size_t pos, ZopfliLZ77Store* store) { + size_t i; + /* Needed for using ZOPFLI_APPEND_DATA multiple times. */ + size_t origsize = store->size; + size_t llstart = ZOPFLI_NUM_LL * (origsize / ZOPFLI_NUM_LL); + size_t dstart = ZOPFLI_NUM_D * (origsize / ZOPFLI_NUM_D); + + /* Everytime the index wraps around, a new cumulative histogram is made: we're + keeping one histogram value per LZ77 symbol rather than a full histogram for + each to save memory. */ + if (origsize % ZOPFLI_NUM_LL == 0) { + size_t llsize = origsize; + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ZOPFLI_APPEND_DATA( + origsize == 0 ? 0 : store->ll_counts[origsize - ZOPFLI_NUM_LL + i], + &store->ll_counts, &llsize); + } + } + if (origsize % ZOPFLI_NUM_D == 0) { + size_t dsize = origsize; + for (i = 0; i < ZOPFLI_NUM_D; i++) { + ZOPFLI_APPEND_DATA( + origsize == 0 ? 0 : store->d_counts[origsize - ZOPFLI_NUM_D + i], + &store->d_counts, &dsize); + } + } + ZOPFLI_APPEND_DATA(length, &store->litlens, &store->size); - ZOPFLI_APPEND_DATA(dist, &store->dists, &size2); + store->size = origsize; + ZOPFLI_APPEND_DATA(dist, &store->dists, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(pos, &store->pos, &store->size); + assert(length < 259); + + if (dist == 0) { + store->size = origsize; + ZOPFLI_APPEND_DATA(length, &store->ll_symbol, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(0, &store->d_symbol, &store->size); + store->ll_counts[llstart + length]++; + } else { + store->size = origsize; + ZOPFLI_APPEND_DATA(ZopfliGetLengthSymbol(length), + &store->ll_symbol, &store->size); + store->size = origsize; + ZOPFLI_APPEND_DATA(ZopfliGetDistSymbol(dist), + &store->d_symbol, &store->size); + store->ll_counts[llstart + ZopfliGetLengthSymbol(length)]++; + store->d_counts[dstart + ZopfliGetDistSymbol(dist)]++; + } +} + +void ZopfliAppendLZ77Store(const ZopfliLZ77Store* store, + ZopfliLZ77Store* target) { + size_t i; + for (i = 0; i < store->size; i++) { + ZopfliStoreLitLenDist(store->litlens[i], store->dists[i], + store->pos[i], target); + } +} + +size_t ZopfliLZ77GetByteRange(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend) { + size_t l = lend - 1; + if (lstart == lend) return 0; + return lz77->pos[l] + ((lz77->dists[l] == 0) ? + 1 : lz77->litlens[l]) - lz77->pos[lstart]; +} + +static void ZopfliLZ77GetHistogramAt(const ZopfliLZ77Store* lz77, size_t lpos, + size_t* ll_counts, size_t* d_counts) { + /* The real histogram is created by using the histogram for this chunk, but + all superfluous values of this chunk subtracted. */ + size_t llpos = ZOPFLI_NUM_LL * (lpos / ZOPFLI_NUM_LL); + size_t dpos = ZOPFLI_NUM_D * (lpos / ZOPFLI_NUM_D); + size_t i; + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ll_counts[i] = lz77->ll_counts[llpos + i]; + } + for (i = lpos + 1; i < llpos + ZOPFLI_NUM_LL && i < lz77->size; i++) { + ll_counts[lz77->ll_symbol[i]]--; + } + for (i = 0; i < ZOPFLI_NUM_D; i++) { + d_counts[i] = lz77->d_counts[dpos + i]; + } + for (i = lpos + 1; i < dpos + ZOPFLI_NUM_D && i < lz77->size; i++) { + if (lz77->dists[i] != 0) d_counts[lz77->d_symbol[i]]--; + } +} + +void ZopfliLZ77GetHistogram(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t* ll_counts, size_t* d_counts) { + size_t i; + if (lstart + ZOPFLI_NUM_LL * 3 > lend) { + memset(ll_counts, 0, sizeof(*ll_counts) * ZOPFLI_NUM_LL); + memset(d_counts, 0, sizeof(*d_counts) * ZOPFLI_NUM_D); + for (i = lstart; i < lend; i++) { + ll_counts[lz77->ll_symbol[i]]++; + if (lz77->dists[i] != 0) d_counts[lz77->d_symbol[i]]++; + } + } else { + /* Subtract the cumulative histograms at the end and the start to get the + histogram for this range. */ + ZopfliLZ77GetHistogramAt(lz77, lend - 1, ll_counts, d_counts); + if (lstart > 0) { + size_t ll_counts2[ZOPFLI_NUM_LL]; + size_t d_counts2[ZOPFLI_NUM_D]; + ZopfliLZ77GetHistogramAt(lz77, lstart - 1, ll_counts2, d_counts2); + + for (i = 0; i < ZOPFLI_NUM_LL; i++) { + ll_counts[i] -= ll_counts2[i]; + } + for (i = 0; i < ZOPFLI_NUM_D; i++) { + d_counts[i] -= d_counts2[i]; + } + } + } +} + +void ZopfliInitBlockState(const ZopfliOptions* options, + size_t blockstart, size_t blockend, int add_lmc, + ZopfliBlockState* s) { + s->options = options; + s->blockstart = blockstart; + s->blockend = blockend; +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + if (add_lmc) { + s->lmc = (ZopfliLongestMatchCache*)malloc(sizeof(ZopfliLongestMatchCache)); + ZopfliInitCache(blockend - blockstart, s->lmc); + } else { + s->lmc = 0; + } +#endif +} + +void ZopfliCleanBlockState(ZopfliBlockState* s) { +#ifdef ZOPFLI_LONGEST_MATCH_CACHE + if (s->lmc) { + ZopfliCleanCache(s->lmc); + free(s->lmc); + } +#endif } /* @@ -365,7 +543,7 @@ void ZopfliLZ77Greedy(ZopfliBlockState* s, const unsigned char* in, size_t instart, size_t inend, - ZopfliLZ77Store* store) { + ZopfliLZ77Store* store, ZopfliHash* h) { size_t i = 0, j; unsigned short leng; unsigned short dist; @@ -374,9 +552,6 @@ ? instart - ZOPFLI_WINDOW_SIZE : 0; unsigned short dummysublen[259]; - ZopfliHash hash; - ZopfliHash* h = &hash; - #ifdef ZOPFLI_LAZY_MATCHING /* Lazy matching. */ unsigned prev_length = 0; @@ -387,7 +562,7 @@ if (instart == inend) return; - ZopfliInitHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); ZopfliWarmupHash(in, windowstart, inend, h); for (i = windowstart; i < instart; i++) { ZopfliUpdateHash(in, i, inend, h); @@ -406,7 +581,7 @@ if (match_available) { match_available = 0; if (lengthscore > prevlengthscore + 1) { - ZopfliStoreLitLenDist(in[i - 1], 0, store); + ZopfliStoreLitLenDist(in[i - 1], 0, i - 1, store); if (lengthscore >= ZOPFLI_MIN_MATCH && leng < ZOPFLI_MAX_MATCH) { match_available = 1; prev_length = leng; @@ -420,7 +595,7 @@ lengthscore = prevlengthscore; /* Add to output. */ ZopfliVerifyLenDist(in, inend, i - 1, dist, leng); - ZopfliStoreLitLenDist(leng, dist, store); + ZopfliStoreLitLenDist(leng, dist, i - 1, store); for (j = 2; j < leng; j++) { assert(i < inend); i++; @@ -441,10 +616,10 @@ /* Add to output. */ if (lengthscore >= ZOPFLI_MIN_MATCH) { ZopfliVerifyLenDist(in, inend, i, dist, leng); - ZopfliStoreLitLenDist(leng, dist, store); + ZopfliStoreLitLenDist(leng, dist, i, store); } else { leng = 1; - ZopfliStoreLitLenDist(in[i], 0, store); + ZopfliStoreLitLenDist(in[i], 0, i, store); } for (j = 1; j < leng; j++) { assert(i < inend); @@ -452,31 +627,4 @@ ZopfliUpdateHash(in, i, inend, h); } } - - ZopfliCleanHash(h); -} - -void ZopfliLZ77Counts(const unsigned short* litlens, - const unsigned short* dists, - size_t start, size_t end, - size_t* ll_count, size_t* d_count) { - size_t i; - - for (i = 0; i < 288; i++) { - ll_count[i] = 0; - } - for (i = 0; i < 32; i++) { - d_count[i] = 0; - } - - for (i = start; i < end; i++) { - if (dists[i] == 0) { - ll_count[litlens[i]]++; - } else { - ll_count[ZopfliGetLengthSymbol(litlens[i])]++; - d_count[ZopfliGetDistSymbol(dists[i])]++; - } - } - - ll_count[256] = 1; /* End symbol. */ } diff -Nru leanify-0.4.3/lib/zopfli/lz77.h leanify-0.4.3+git20181014/lib/zopfli/lz77.h --- leanify-0.4.3/lib/zopfli/lz77.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/lz77.h 2017-08-01 20:51:09.000000000 +0000 @@ -46,13 +46,37 @@ unsigned short* dists; /* If 0: indicates literal in corresponding litlens, if > 0: length in corresponding litlens, this is the distance. */ size_t size; + + const unsigned char* data; /* original data */ + size_t* pos; /* position in data where this LZ77 command begins */ + + unsigned short* ll_symbol; + unsigned short* d_symbol; + + /* Cumulative histograms wrapping around per chunk. Each chunk has the amount + of distinct symbols as length, so using 1 value per LZ77 symbol, we have a + precise histogram at every N symbols, and the rest can be calculated by + looping through the actual symbols of this chunk. */ + size_t* ll_counts; + size_t* d_counts; } ZopfliLZ77Store; -void ZopfliInitLZ77Store(ZopfliLZ77Store* store); +void ZopfliInitLZ77Store(const unsigned char* data, ZopfliLZ77Store* store); void ZopfliCleanLZ77Store(ZopfliLZ77Store* store); void ZopfliCopyLZ77Store(const ZopfliLZ77Store* source, ZopfliLZ77Store* dest); void ZopfliStoreLitLenDist(unsigned short length, unsigned short dist, - ZopfliLZ77Store* store); + size_t pos, ZopfliLZ77Store* store); +void ZopfliAppendLZ77Store(const ZopfliLZ77Store* store, + ZopfliLZ77Store* target); +/* Gets the amount of raw bytes that this range of LZ77 symbols spans. */ +size_t ZopfliLZ77GetByteRange(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend); +/* Gets the histogram of lit/len and dist symbols in the given range, using the +cumulative histograms, so faster than adding one by one for large range. Does +not add the one end symbol of value 256. */ +void ZopfliLZ77GetHistogram(const ZopfliLZ77Store* lz77, + size_t lstart, size_t lend, + size_t* ll_counts, size_t* d_counts); /* Some state information for compressing a block. @@ -72,6 +96,11 @@ size_t blockend; } ZopfliBlockState; +void ZopfliInitBlockState(const ZopfliOptions* options, + size_t blockstart, size_t blockend, int add_lmc, + ZopfliBlockState* s); +void ZopfliCleanBlockState(ZopfliBlockState* s); + /* Finds the longest match (length and corresponding distance) for LZ77 compression. @@ -100,22 +129,6 @@ unsigned short dist, unsigned short length); /* -Counts the number of literal, length and distance symbols in the given lz77 -arrays. -litlens: lz77 lit/lengths -dists: ll77 distances -start: where to begin counting in litlens and dists -end: where to stop counting in litlens and dists (not inclusive) -ll_count: count of each lit/len symbol, must have size 288 (see deflate - standard) -d_count: count of each dist symbol, must have size 32 (see deflate standard) -*/ -void ZopfliLZ77Counts(const unsigned short* litlens, - const unsigned short* dists, - size_t start, size_t end, - size_t* ll_count, size_t* d_count); - -/* Does LZ77 using an algorithm similar to gzip, with lazy matching, rather than with the slow but better "squeeze" implementation. The result is placed in the ZopfliLZ77Store. @@ -124,6 +137,6 @@ */ void ZopfliLZ77Greedy(ZopfliBlockState* s, const unsigned char* in, size_t instart, size_t inend, - ZopfliLZ77Store* store); + ZopfliLZ77Store* store, ZopfliHash* h); #endif /* ZOPFLI_LZ77_H_ */ diff -Nru leanify-0.4.3/lib/zopfli/squeeze.c leanify-0.4.3+git20181014/lib/zopfli/squeeze.c --- leanify-0.4.3/lib/zopfli/squeeze.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/squeeze.c 2017-08-01 20:51:09.000000000 +0000 @@ -25,35 +25,40 @@ #include "blocksplitter.h" #include "deflate.h" +#include "symbols.h" #include "tree.h" #include "util.h" typedef struct SymbolStats { /* The literal and length symbols. */ - size_t litlens[288]; + size_t litlens[ZOPFLI_NUM_LL]; /* The 32 unique dist symbols, not the 32768 possible dists. */ - size_t dists[32]; + size_t dists[ZOPFLI_NUM_D]; - double ll_symbols[288]; /* Length of each lit/len symbol in bits. */ - double d_symbols[32]; /* Length of each dist symbol in bits. */ + /* Length of each lit/len symbol in bits. */ + double ll_symbols[ZOPFLI_NUM_LL]; + /* Length of each dist symbol in bits. */ + double d_symbols[ZOPFLI_NUM_D]; } SymbolStats; /* Sets everything to 0. */ static void InitStats(SymbolStats* stats) { - memset(stats->litlens, 0, 288 * sizeof(stats->litlens[0])); - memset(stats->dists, 0, 32 * sizeof(stats->dists[0])); + memset(stats->litlens, 0, ZOPFLI_NUM_LL * sizeof(stats->litlens[0])); + memset(stats->dists, 0, ZOPFLI_NUM_D * sizeof(stats->dists[0])); - memset(stats->ll_symbols, 0, 288 * sizeof(stats->ll_symbols[0])); - memset(stats->d_symbols, 0, 32 * sizeof(stats->d_symbols[0])); + memset(stats->ll_symbols, 0, ZOPFLI_NUM_LL * sizeof(stats->ll_symbols[0])); + memset(stats->d_symbols, 0, ZOPFLI_NUM_D * sizeof(stats->d_symbols[0])); } static void CopyStats(SymbolStats* source, SymbolStats* dest) { - memcpy(dest->litlens, source->litlens, 288 * sizeof(dest->litlens[0])); - memcpy(dest->dists, source->dists, 32 * sizeof(dest->dists[0])); + memcpy(dest->litlens, source->litlens, + ZOPFLI_NUM_LL * sizeof(dest->litlens[0])); + memcpy(dest->dists, source->dists, ZOPFLI_NUM_D * sizeof(dest->dists[0])); memcpy(dest->ll_symbols, source->ll_symbols, - 288 * sizeof(dest->ll_symbols[0])); - memcpy(dest->d_symbols, source->d_symbols, 32 * sizeof(dest->d_symbols[0])); + ZOPFLI_NUM_LL * sizeof(dest->ll_symbols[0])); + memcpy(dest->d_symbols, source->d_symbols, + ZOPFLI_NUM_D * sizeof(dest->d_symbols[0])); } /* Adds the bit lengths. */ @@ -61,11 +66,11 @@ const SymbolStats* stats2, double w2, SymbolStats* result) { size_t i; - for (i = 0; i < 288; i++) { + for (i = 0; i < ZOPFLI_NUM_LL; i++) { result->litlens[i] = (size_t) (stats1->litlens[i] * w1 + stats2->litlens[i] * w2); } - for (i = 0; i < 32; i++) { + for (i = 0; i < ZOPFLI_NUM_D; i++) { result->dists[i] = (size_t) (stats1->dists[i] * w1 + stats2->dists[i] * w2); } @@ -96,15 +101,15 @@ } static void RandomizeStatFreqs(RanState* state, SymbolStats* stats) { - RandomizeFreqs(state, stats->litlens, 288); - RandomizeFreqs(state, stats->dists, 32); + RandomizeFreqs(state, stats->litlens, ZOPFLI_NUM_LL); + RandomizeFreqs(state, stats->dists, ZOPFLI_NUM_D); stats->litlens[256] = 1; /* End symbol. */ } static void ClearStatFreqs(SymbolStats* stats) { size_t i; - for (i = 0; i < 288; i++) stats->litlens[i] = 0; - for (i = 0; i < 32; i++) stats->dists[i] = 0; + for (i = 0; i < ZOPFLI_NUM_LL; i++) stats->litlens[i] = 0; + for (i = 0; i < ZOPFLI_NUM_D; i++) stats->dists[i] = 0; } /* @@ -126,7 +131,7 @@ int dbits = ZopfliGetDistExtraBits(dist); int lbits = ZopfliGetLengthExtraBits(litlen); int lsym = ZopfliGetLengthSymbol(litlen); - double cost = 0; + int cost = 0; if (lsym <= 279) cost += 7; else cost += 8; cost += 5; /* Every dist symbol has length 5. */ @@ -147,7 +152,7 @@ int lbits = ZopfliGetLengthExtraBits(litlen); int dsym = ZopfliGetDistSymbol(dist); int dbits = ZopfliGetDistExtraBits(dist); - return stats->ll_symbols[lsym] + lbits + stats->d_symbols[dsym] + dbits; + return lbits + dbits + stats->ll_symbols[lsym] + stats->d_symbols[dsym]; } } @@ -192,6 +197,10 @@ return costmodel(bestlength, bestdist, costcontext); } +static size_t zopfli_min(size_t a, size_t b) { + return a < b ? a : b; +} + /* Performs the forward pass for "squeeze". Gets the most optimal length to reach every byte from a previous byte, using cost calculations. @@ -209,27 +218,23 @@ const unsigned char* in, size_t instart, size_t inend, CostModelFun* costmodel, void* costcontext, - unsigned short* length_array) { + unsigned short* length_array, + ZopfliHash* h, float* costs) { /* Best cost to get here so far. */ size_t blocksize = inend - instart; - float* costs; - size_t i = 0, k; + size_t i = 0, k, kend; unsigned short leng; unsigned short dist; unsigned short sublen[259]; size_t windowstart = instart > ZOPFLI_WINDOW_SIZE ? instart - ZOPFLI_WINDOW_SIZE : 0; - ZopfliHash hash; - ZopfliHash* h = &hash; double result; double mincost = GetCostModelMinCost(costmodel, costcontext); + double mincostaddcostj; if (instart == inend) return 0; - costs = (float*)malloc(sizeof(float) * (blocksize + 1)); - if (!costs) exit(-1); /* Allocation failed. */ - - ZopfliInitHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); ZopfliWarmupHash(in, windowstart, inend, h); for (i = windowstart; i < instart; i++) { ZopfliUpdateHash(in, i, inend, h); @@ -270,7 +275,7 @@ /* Literal. */ if (i + 1 <= inend) { - double newCost = costs[j] + costmodel(in[i], 0, costcontext); + double newCost = costmodel(in[i], 0, costcontext) + costs[j]; assert(newCost >= 0); if (newCost < costs[j + 1]) { costs[j + 1] = newCost; @@ -278,14 +283,16 @@ } } /* Lengths. */ - for (k = 3; k <= leng && i + k <= inend; k++) { + kend = zopfli_min(leng, inend-i); + mincostaddcostj = mincost + costs[j]; + for (k = 3; k <= kend; k++) { double newCost; /* Calling the cost model is expensive, avoid this if we are already at the minimum possible cost that it can return. */ - if (costs[j + k] - costs[j] <= mincost) continue; + if (costs[j + k] <= mincostaddcostj) continue; - newCost = costs[j] + costmodel(k, sublen[k], costcontext); + newCost = costmodel(k, sublen[k], costcontext) + costs[j]; assert(newCost >= 0); if (newCost < costs[j + k]) { assert(k <= ZOPFLI_MAX_MATCH); @@ -298,9 +305,6 @@ assert(costs[blocksize] >= 0); result = costs[blocksize]; - ZopfliCleanHash(h); - free(costs); - return result; } @@ -334,19 +338,16 @@ static void FollowPath(ZopfliBlockState* s, const unsigned char* in, size_t instart, size_t inend, unsigned short* path, size_t pathsize, - ZopfliLZ77Store* store) { + ZopfliLZ77Store* store, ZopfliHash *h) { size_t i, j, pos = 0; size_t windowstart = instart > ZOPFLI_WINDOW_SIZE ? instart - ZOPFLI_WINDOW_SIZE : 0; size_t total_length_test = 0; - ZopfliHash hash; - ZopfliHash* h = &hash; - if (instart == inend) return; - ZopfliInitHash(ZOPFLI_WINDOW_SIZE, h); + ZopfliResetHash(ZOPFLI_WINDOW_SIZE, h); ZopfliWarmupHash(in, windowstart, inend, h); for (i = windowstart; i < instart; i++) { ZopfliUpdateHash(in, i, inend, h); @@ -369,11 +370,11 @@ &dist, &dummy_length); assert(!(dummy_length != length && length > 2 && dummy_length > 2)); ZopfliVerifyLenDist(in, inend, pos, dist, length); - ZopfliStoreLitLenDist(length, dist, store); + ZopfliStoreLitLenDist(length, dist, pos, store); total_length_test += length; } else { length = 1; - ZopfliStoreLitLenDist(in[pos], 0, store); + ZopfliStoreLitLenDist(in[pos], 0, pos, store); total_length_test++; } @@ -385,14 +386,12 @@ pos += length; } - - ZopfliCleanHash(h); } /* Calculates the entropy of the statistics */ static void CalculateStatistics(SymbolStats* stats) { - ZopfliCalculateEntropy(stats->litlens, 288, stats->ll_symbols); - ZopfliCalculateEntropy(stats->dists, 32, stats->d_symbols); + ZopfliCalculateEntropy(stats->litlens, ZOPFLI_NUM_LL, stats->ll_symbols); + ZopfliCalculateEntropy(stats->dists, ZOPFLI_NUM_D, stats->d_symbols); } /* Appends the symbol statistics from the store. */ @@ -414,14 +413,13 @@ /* Does a single run for ZopfliLZ77Optimal. For good compression, repeated runs with updated statistics should be performed. - s: the block state in: the input data array instart: where to start inend: where to stop (not inclusive) path: pointer to dynamically allocated memory to store the path pathsize: pointer to the size of the dynamic path array -length_array: array if size (inend - instart) used to store lengths +length_array: array of size (inend - instart) used to store lengths costmodel: function to use as the cost model for this squeeze run costcontext: abstract context for the costmodel function store: place to output the LZ77 data @@ -432,20 +430,22 @@ const unsigned char* in, size_t instart, size_t inend, unsigned short** path, size_t* pathsize, unsigned short* length_array, CostModelFun* costmodel, - void* costcontext, ZopfliLZ77Store* store) { - double cost = GetBestLengths( - s, in, instart, inend, costmodel, costcontext, length_array); + void* costcontext, ZopfliLZ77Store* store, + ZopfliHash* h, float* costs) { + double cost = GetBestLengths(s, in, instart, inend, costmodel, + costcontext, length_array, h, costs); free(*path); *path = 0; *pathsize = 0; TraceBackwards(inend - instart, length_array, path, pathsize); - FollowPath(s, in, instart, inend, *path, *pathsize, store); + FollowPath(s, in, instart, inend, *path, *pathsize, store, h); assert(cost < ZOPFLI_LARGE_FLOAT); return cost; } void ZopfliLZ77Optimal(ZopfliBlockState *s, const unsigned char* in, size_t instart, size_t inend, + int numiterations, ZopfliLZ77Store* store) { /* Dist to get to here with smallest cost. */ size_t blocksize = inend - instart; @@ -454,8 +454,11 @@ unsigned short* path = 0; size_t pathsize = 0; ZopfliLZ77Store currentstore; + ZopfliHash hash; + ZopfliHash* h = &hash; SymbolStats stats, beststats, laststats; int i; + float* costs = (float*)malloc(sizeof(float) * (blocksize + 1)); double cost; double bestcost = ZOPFLI_LARGE_FLOAT; double lastcost = 0; @@ -463,29 +466,30 @@ RanState ran_state; int lastrandomstep = -1; + if (!costs) exit(-1); /* Allocation failed. */ if (!length_array) exit(-1); /* Allocation failed. */ InitRanState(&ran_state); InitStats(&stats); - ZopfliInitLZ77Store(¤tstore); + ZopfliInitLZ77Store(in, ¤tstore); + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); /* Do regular deflate, then loop multiple shortest path runs, each time using the statistics of the previous run. */ /* Initial run. */ - ZopfliLZ77Greedy(s, in, instart, inend, ¤tstore); + ZopfliLZ77Greedy(s, in, instart, inend, ¤tstore, h); GetStatistics(¤tstore, &stats); /* Repeat statistics with each time the cost model from the previous stat run. */ - for (i = 0; i < s->options->numiterations; i++) { + for (i = 0; i < numiterations; i++) { ZopfliCleanLZ77Store(¤tstore); - ZopfliInitLZ77Store(¤tstore); + ZopfliInitLZ77Store(in, ¤tstore); LZ77OptimalRun(s, in, instart, inend, &path, &pathsize, length_array, GetCostStat, (void*)&stats, - ¤tstore); - cost = ZopfliCalculateBlockSize(currentstore.litlens, currentstore.dists, - 0, currentstore.size, 2); + ¤tstore, h, costs); + cost = ZopfliCalculateBlockSize(¤tstore, 0, currentstore.size, 2); if (s->options->verbose_more || (s->options->verbose && cost < bestcost)) { fprintf(stderr, "Iteration %d: %d bit\n", i, (int) cost); } @@ -516,7 +520,9 @@ free(length_array); free(path); + free(costs); ZopfliCleanLZ77Store(¤tstore); + ZopfliCleanHash(h); } void ZopfliLZ77OptimalFixed(ZopfliBlockState *s, @@ -530,17 +536,25 @@ (unsigned short*)malloc(sizeof(unsigned short) * (blocksize + 1)); unsigned short* path = 0; size_t pathsize = 0; + ZopfliHash hash; + ZopfliHash* h = &hash; + float* costs = (float*)malloc(sizeof(float) * (blocksize + 1)); + if (!costs) exit(-1); /* Allocation failed. */ if (!length_array) exit(-1); /* Allocation failed. */ + ZopfliAllocHash(ZOPFLI_WINDOW_SIZE, h); + s->blockstart = instart; s->blockend = inend; /* Shortest path for fixed tree This one should give the shortest possible result for fixed tree, no repeated runs are needed since the tree is known. */ LZ77OptimalRun(s, in, instart, inend, &path, &pathsize, - length_array, GetCostFixed, 0, store); + length_array, GetCostFixed, 0, store, h, costs); free(length_array); free(path); + free(costs); + ZopfliCleanHash(h); } diff -Nru leanify-0.4.3/lib/zopfli/squeeze.h leanify-0.4.3+git20181014/lib/zopfli/squeeze.h --- leanify-0.4.3/lib/zopfli/squeeze.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/squeeze.h 2017-08-01 20:51:09.000000000 +0000 @@ -40,6 +40,7 @@ */ void ZopfliLZ77Optimal(ZopfliBlockState *s, const unsigned char* in, size_t instart, size_t inend, + int numiterations, ZopfliLZ77Store* store); /* diff -Nru leanify-0.4.3/lib/zopfli/symbols.h leanify-0.4.3+git20181014/lib/zopfli/symbols.h --- leanify-0.4.3/lib/zopfli/symbols.h 1970-01-01 00:00:00.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/symbols.h 2018-07-03 15:36:27.000000000 +0000 @@ -0,0 +1,248 @@ +/* +Copyright 2016 Google Inc. All Rights Reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Author: lode.vandevenne@gmail.com (Lode Vandevenne) +Author: jyrki.alakuijala@gmail.com (Jyrki Alakuijala) +*/ + +/* +Utilities for using the lz77 symbols of the deflate spec. +*/ + +#ifndef ZOPFLI_SYMBOLS_H_ +#define ZOPFLI_SYMBOLS_H_ + +/* __has_builtin available in clang */ +#ifdef __has_builtin +# if __has_builtin(__builtin_clz) +# define ZOPFLI_HAS_BUILTIN_CLZ +# endif +/* __builtin_clz available beginning with GCC 3.4 */ +#elif __GNUC__ * 100 + __GNUC_MINOR__ >= 304 +# define ZOPFLI_HAS_BUILTIN_CLZ +/* _BitScanReverse available beginning with Visual Studio 2005 */ +#elif _MSC_VER >= 1400 +# include +# define ZOPFLI_HAS_BUILTIN_CLZ +static int __inline __builtin_clz(unsigned int x) { + unsigned long r; + _BitScanReverse(&r, x); + return 31 ^ r; +} +#endif + +/* Gets the amount of extra bits for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistExtraBits(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) return 0; + return (31 ^ __builtin_clz(dist - 1)) - 1; /* log2(dist - 1) - 1 */ +#else + if (dist < 5) return 0; + else if (dist < 9) return 1; + else if (dist < 17) return 2; + else if (dist < 33) return 3; + else if (dist < 65) return 4; + else if (dist < 129) return 5; + else if (dist < 257) return 6; + else if (dist < 513) return 7; + else if (dist < 1025) return 8; + else if (dist < 2049) return 9; + else if (dist < 4097) return 10; + else if (dist < 8193) return 11; + else if (dist < 16385) return 12; + else return 13; +#endif +} + +/* Gets value of the extra bits for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistExtraBitsValue(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) { + return 0; + } else { + int l = 31 ^ __builtin_clz(dist - 1); /* log2(dist - 1) */ + return (dist - (1 + (1 << l))) & ((1 << (l - 1)) - 1); + } +#else + if (dist < 5) return 0; + else if (dist < 9) return (dist - 5) & 1; + else if (dist < 17) return (dist - 9) & 3; + else if (dist < 33) return (dist - 17) & 7; + else if (dist < 65) return (dist - 33) & 15; + else if (dist < 129) return (dist - 65) & 31; + else if (dist < 257) return (dist - 129) & 63; + else if (dist < 513) return (dist - 257) & 127; + else if (dist < 1025) return (dist - 513) & 255; + else if (dist < 2049) return (dist - 1025) & 511; + else if (dist < 4097) return (dist - 2049) & 1023; + else if (dist < 8193) return (dist - 4097) & 2047; + else if (dist < 16385) return (dist - 8193) & 4095; + else return (dist - 16385) & 8191; +#endif +} + +/* Gets the symbol for the given dist, cfr. the DEFLATE spec. */ +static int ZopfliGetDistSymbol(int dist) { +#ifdef ZOPFLI_HAS_BUILTIN_CLZ + if (dist < 5) { + return dist - 1; + } else { + int l = (31 ^ __builtin_clz(dist - 1)); /* log2(dist - 1) */ + int r = ((dist - 1) >> (l - 1)) & 1; + return l * 2 + r; + } +#else + if (dist < 193) { + if (dist < 13) { /* dist 0..13. */ + if (dist < 5) return dist - 1; + else if (dist < 7) return 4; + else if (dist < 9) return 5; + else return 6; + } else { /* dist 13..193. */ + if (dist < 17) return 7; + else if (dist < 25) return 8; + else if (dist < 33) return 9; + else if (dist < 49) return 10; + else if (dist < 65) return 11; + else if (dist < 97) return 12; + else if (dist < 129) return 13; + else return 14; + } + } else { + if (dist < 2049) { /* dist 193..2049. */ + if (dist < 257) return 15; + else if (dist < 385) return 16; + else if (dist < 513) return 17; + else if (dist < 769) return 18; + else if (dist < 1025) return 19; + else if (dist < 1537) return 20; + else return 21; + } else { /* dist 2049..32768. */ + if (dist < 3073) return 22; + else if (dist < 4097) return 23; + else if (dist < 6145) return 24; + else if (dist < 8193) return 25; + else if (dist < 12289) return 26; + else if (dist < 16385) return 27; + else if (dist < 24577) return 28; + else return 29; + } + } +#endif +} + +/* Gets the amount of extra bits for the given length, cfr. the DEFLATE spec. */ +static int ZopfliGetLengthExtraBits(int l) { + static const int table[259] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 + }; + return table[l]; +} + +/* Gets value of the extra bits for the given length, cfr. the DEFLATE spec. */ +static int ZopfliGetLengthExtraBitsValue(int l) { + static const int table[259] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 0, + 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, + 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, + 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, + 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, + 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, + 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0 + }; + return table[l]; +} + +/* +Gets the symbol for the given length, cfr. the DEFLATE spec. +Returns the symbol in the range [257-285] (inclusive) +*/ +static int ZopfliGetLengthSymbol(int l) { + static const int table[259] = { + 0, 0, 0, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 265, 266, 266, 267, 267, 268, 268, + 269, 269, 269, 269, 270, 270, 270, 270, + 271, 271, 271, 271, 272, 272, 272, 272, + 273, 273, 273, 273, 273, 273, 273, 273, + 274, 274, 274, 274, 274, 274, 274, 274, + 275, 275, 275, 275, 275, 275, 275, 275, + 276, 276, 276, 276, 276, 276, 276, 276, + 277, 277, 277, 277, 277, 277, 277, 277, + 277, 277, 277, 277, 277, 277, 277, 277, + 278, 278, 278, 278, 278, 278, 278, 278, + 278, 278, 278, 278, 278, 278, 278, 278, + 279, 279, 279, 279, 279, 279, 279, 279, + 279, 279, 279, 279, 279, 279, 279, 279, + 280, 280, 280, 280, 280, 280, 280, 280, + 280, 280, 280, 280, 280, 280, 280, 280, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 281, 281, 281, 281, 281, 281, 281, 281, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 282, 282, 282, 282, 282, 282, 282, 282, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 283, 283, 283, 283, 283, 283, 283, 283, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 284, + 284, 284, 284, 284, 284, 284, 284, 285 + }; + return table[l]; +} + +/* Gets the amount of extra bits for the given length symbol. */ +static int ZopfliGetLengthSymbolExtraBits(int s) { + static const int table[29] = { + 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, + 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 + }; + return table[s - 257]; +} + +/* Gets the amount of extra bits for the given distance symbol. */ +static int ZopfliGetDistSymbolExtraBits(int s) { + static const int table[30] = { + 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 + }; + return table[s]; +} + +#endif /* ZOPFLI_SYMBOLS_H_ */ diff -Nru leanify-0.4.3/lib/zopfli/util.c leanify-0.4.3+git20181014/lib/zopfli/util.c --- leanify-0.4.3/lib/zopfli/util.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/util.c 2017-08-01 20:51:09.000000000 +0000 @@ -25,184 +25,6 @@ #include #include -int ZopfliGetDistExtraBits(int dist) { -#ifdef __GNUC__ - if (dist < 5) return 0; - return (31 ^ __builtin_clz(dist - 1)) - 1; /* log2(dist - 1) - 1 */ -#else - if (dist < 5) return 0; - else if (dist < 9) return 1; - else if (dist < 17) return 2; - else if (dist < 33) return 3; - else if (dist < 65) return 4; - else if (dist < 129) return 5; - else if (dist < 257) return 6; - else if (dist < 513) return 7; - else if (dist < 1025) return 8; - else if (dist < 2049) return 9; - else if (dist < 4097) return 10; - else if (dist < 8193) return 11; - else if (dist < 16385) return 12; - else return 13; -#endif -} - -int ZopfliGetDistExtraBitsValue(int dist) { -#ifdef __GNUC__ - if (dist < 5) { - return 0; - } else { - int l = 31 ^ __builtin_clz(dist - 1); /* log2(dist - 1) */ - return (dist - (1 + (1 << l))) & ((1 << (l - 1)) - 1); - } -#else - if (dist < 5) return 0; - else if (dist < 9) return (dist - 5) & 1; - else if (dist < 17) return (dist - 9) & 3; - else if (dist < 33) return (dist - 17) & 7; - else if (dist < 65) return (dist - 33) & 15; - else if (dist < 129) return (dist - 65) & 31; - else if (dist < 257) return (dist - 129) & 63; - else if (dist < 513) return (dist - 257) & 127; - else if (dist < 1025) return (dist - 513) & 255; - else if (dist < 2049) return (dist - 1025) & 511; - else if (dist < 4097) return (dist - 2049) & 1023; - else if (dist < 8193) return (dist - 4097) & 2047; - else if (dist < 16385) return (dist - 8193) & 4095; - else return (dist - 16385) & 8191; -#endif -} - -int ZopfliGetDistSymbol(int dist) { -#ifdef __GNUC__ - if (dist < 5) { - return dist - 1; - } else { - int l = (31 ^ __builtin_clz(dist - 1)); /* log2(dist - 1) */ - int r = ((dist - 1) >> (l - 1)) & 1; - return l * 2 + r; - } -#else - if (dist < 193) { - if (dist < 13) { /* dist 0..13. */ - if (dist < 5) return dist - 1; - else if (dist < 7) return 4; - else if (dist < 9) return 5; - else return 6; - } else { /* dist 13..193. */ - if (dist < 17) return 7; - else if (dist < 25) return 8; - else if (dist < 33) return 9; - else if (dist < 49) return 10; - else if (dist < 65) return 11; - else if (dist < 97) return 12; - else if (dist < 129) return 13; - else return 14; - } - } else { - if (dist < 2049) { /* dist 193..2049. */ - if (dist < 257) return 15; - else if (dist < 385) return 16; - else if (dist < 513) return 17; - else if (dist < 769) return 18; - else if (dist < 1025) return 19; - else if (dist < 1537) return 20; - else return 21; - } else { /* dist 2049..32768. */ - if (dist < 3073) return 22; - else if (dist < 4097) return 23; - else if (dist < 6145) return 24; - else if (dist < 8193) return 25; - else if (dist < 12289) return 26; - else if (dist < 16385) return 27; - else if (dist < 24577) return 28; - else return 29; - } - } -#endif -} - -int ZopfliGetLengthExtraBits(int l) { - static const int table[259] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 0 - }; - return table[l]; -} - -int ZopfliGetLengthExtraBitsValue(int l) { - static const int table[259] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 2, 3, 0, - 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, - 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, - 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, - 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, - 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, - 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, - 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, - 27, 28, 29, 30, 31, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, - 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 0 - }; - return table[l]; -} - -/* -Returns symbol in range [257-285] (inclusive). -*/ -int ZopfliGetLengthSymbol(int l) { - static const int table[259] = { - 0, 0, 0, 257, 258, 259, 260, 261, 262, 263, 264, - 265, 265, 266, 266, 267, 267, 268, 268, - 269, 269, 269, 269, 270, 270, 270, 270, - 271, 271, 271, 271, 272, 272, 272, 272, - 273, 273, 273, 273, 273, 273, 273, 273, - 274, 274, 274, 274, 274, 274, 274, 274, - 275, 275, 275, 275, 275, 275, 275, 275, - 276, 276, 276, 276, 276, 276, 276, 276, - 277, 277, 277, 277, 277, 277, 277, 277, - 277, 277, 277, 277, 277, 277, 277, 277, - 278, 278, 278, 278, 278, 278, 278, 278, - 278, 278, 278, 278, 278, 278, 278, 278, - 279, 279, 279, 279, 279, 279, 279, 279, - 279, 279, 279, 279, 279, 279, 279, 279, - 280, 280, 280, 280, 280, 280, 280, 280, - 280, 280, 280, 280, 280, 280, 280, 280, - 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, - 281, 281, 281, 281, 281, 281, 281, 281, - 282, 282, 282, 282, 282, 282, 282, 282, - 282, 282, 282, 282, 282, 282, 282, 282, - 282, 282, 282, 282, 282, 282, 282, 282, - 282, 282, 282, 282, 282, 282, 282, 282, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 283, 283, 283, 283, 283, 283, 283, 283, - 284, 284, 284, 284, 284, 284, 284, 284, - 284, 284, 284, 284, 284, 284, 284, 284, - 284, 284, 284, 284, 284, 284, 284, 284, - 284, 284, 284, 284, 284, 284, 284, 285 - }; - return table[l]; -} - void ZopfliInitOptions(ZopfliOptions* options) { options->verbose = 0; options->verbose_more = 0; diff -Nru leanify-0.4.3/lib/zopfli/util.h leanify-0.4.3+git20181014/lib/zopfli/util.h --- leanify-0.4.3/lib/zopfli/util.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/util.h 2017-08-01 20:51:09.000000000 +0000 @@ -32,6 +32,10 @@ #define ZOPFLI_MAX_MATCH 258 #define ZOPFLI_MIN_MATCH 3 +/* Number of distinct literal/length and distance symbols in DEFLATE */ +#define ZOPFLI_NUM_LL 288 +#define ZOPFLI_NUM_D 32 + /* The window size for deflate. Must be a power of two. This should be 32768, the maximum possible by the deflate spec. Anything less hurts compression more than @@ -51,9 +55,9 @@ The whole compression algorithm, including the smarter block splitting, will be executed independently on each huge block. Dividing into huge blocks hurts compression, but not much relative to the size. -Set this to, for example, 20MB (20000000). Set it to 0 to disable master blocks. +Set it to 0 to disable master blocks. */ -#define ZOPFLI_MASTER_BLOCK_SIZE 20000000 +#define ZOPFLI_MASTER_BLOCK_SIZE 1000000 /* Used to initialize costs for example @@ -117,27 +121,6 @@ #define ZOPFLI_LAZY_MATCHING /* -Gets the symbol for the given length, cfr. the DEFLATE spec. -Returns the symbol in the range [257-285] (inclusive) -*/ -int ZopfliGetLengthSymbol(int l); - -/* Gets the amount of extra bits for the given length, cfr. the DEFLATE spec. */ -int ZopfliGetLengthExtraBits(int l); - -/* Gets value of the extra bits for the given length, cfr. the DEFLATE spec. */ -int ZopfliGetLengthExtraBitsValue(int l); - -/* Gets the symbol for the given dist, cfr. the DEFLATE spec. */ -int ZopfliGetDistSymbol(int dist); - -/* Gets the amount of extra bits for the given dist, cfr. the DEFLATE spec. */ -int ZopfliGetDistExtraBits(int dist); - -/* Gets value of the extra bits for the given dist, cfr. the DEFLATE spec. */ -int ZopfliGetDistExtraBitsValue(int dist); - -/* Appends value to dynamically allocated memory, doubling its allocation size whenever needed. diff -Nru leanify-0.4.3/lib/zopfli/zlib_container.c leanify-0.4.3+git20181014/lib/zopfli/zlib_container.c --- leanify-0.4.3/lib/zopfli/zlib_container.c 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/zlib_container.c 2017-08-01 20:51:09.000000000 +0000 @@ -53,7 +53,7 @@ unsigned char bitpointer = 0; unsigned checksum = adler32(in, (unsigned)insize); unsigned cmf = 120; /* CM 8, CINFO 7. See zlib spec.*/ - unsigned flevel = 0; + unsigned flevel = 3; unsigned fdict = 0; unsigned cmfflg = 256 * cmf + fdict * 32 + flevel * 64; unsigned fcheck = 31 - cmfflg % 31; diff -Nru leanify-0.4.3/lib/zopfli/zopfli.h leanify-0.4.3+git20181014/lib/zopfli/zopfli.h --- leanify-0.4.3/lib/zopfli/zopfli.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopfli/zopfli.h 2017-08-01 20:51:09.000000000 +0000 @@ -52,10 +52,7 @@ int blocksplitting; /* - If true, chooses the optimal block split points only after doing the iterative - LZ77 compression. If false, chooses the block split points first, then does - iterative LZ77 on each individual block. Depending on the file, either first - or last gives the best compression. Default: false (0). + No longer used, left for compatibility. */ int blocksplittinglast; diff -Nru leanify-0.4.3/lib/zopflipng/lodepng/lodepng.cpp leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng.cpp --- leanify-0.4.3/lib/zopflipng/lodepng/lodepng.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,7 +1,7 @@ /* -LodePNG version 20131222 +LodePNG version 20160409 -Copyright (c) 2005-2013 Lode Vandevenne +Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -30,14 +30,16 @@ #include "lodepng.h" +#include #include #include -#ifdef LODEPNG_COMPILE_CPP -#include -#endif /*LODEPNG_COMPILE_CPP*/ +#if defined(_MSC_VER) && (_MSC_VER >= 1310) /*Visual Studio: A few warning types are not desired here.*/ +#pragma warning( disable : 4244 ) /*implicit conversions: not warned by gcc -Wall -Wextra and requires too much casts*/ +#pragma warning( disable : 4996 ) /*VS does not like fopen, but fopen_s is not standard C so unusable here*/ +#endif /*_MSC_VER */ -#define VERSION_STRING "20131222" +const char* LODEPNG_VERSION_STRING = "20160409"; /* This source file is built up in the following large parts. The code sections @@ -114,6 +116,13 @@ if(error) return error;\ } +/*Set error var to the error code, and return from the void function.*/ +#define CERROR_RETURN(errorvar, code)\ +{\ + errorvar = code;\ + return;\ +} + /* About uivector, ucvector and string: -All of them wrap dynamic arrays or text strings in a similar way. @@ -140,30 +149,36 @@ } /*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_resize(uivector* p, size_t size) +static unsigned uivector_reserve(uivector* p, size_t allocsize) { - if(size * sizeof(unsigned) > p->allocsize) + if(allocsize > p->allocsize) { - size_t newsize = size * sizeof(unsigned) * 2; + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned*)data; - p->size = size; } - else return 0; + else return 0; /*error: not enough memory*/ } - else p->size = size; return 1; } +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned uivector_resize(uivector* p, size_t size) +{ + if(!uivector_reserve(p, size * sizeof(unsigned))) return 0; + p->size = size; + return 1; /*success*/ +} + /*resize and give all new elements the value*/ static unsigned uivector_resizev(uivector* p, size_t size, unsigned value) { size_t oldsize = p->size, i; if(!uivector_resize(p, size)) return 0; - for(i = oldsize; i < size; i++) p->data[i] = value; + for(i = oldsize; i < size; ++i) p->data[i] = value; return 1; } @@ -181,15 +196,6 @@ p->data[p->size - 1] = c; return 1; } - -/*copy q to p, returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned uivector_copy(uivector* p, const uivector* q) -{ - size_t i; - if(!uivector_resize(p, q->size)) return 0; - for(i = 0; i < q->size; i++) p->data[i] = q->data[i]; - return 1; -} #endif /*LODEPNG_COMPILE_ENCODER*/ #endif /*LODEPNG_COMPILE_ZLIB*/ @@ -204,24 +210,30 @@ } ucvector; /*returns 1 if success, 0 if failure ==> nothing done*/ -static unsigned ucvector_resize(ucvector* p, size_t size) +static unsigned ucvector_reserve(ucvector* p, size_t allocsize) { - if(size * sizeof(unsigned char) > p->allocsize) + if(allocsize > p->allocsize) { - size_t newsize = size * sizeof(unsigned char) * 2; + size_t newsize = (allocsize > p->allocsize * 2) ? allocsize : (allocsize * 3 / 2); void* data = lodepng_realloc(p->data, newsize); if(data) { p->allocsize = newsize; p->data = (unsigned char*)data; - p->size = size; } else return 0; /*error: not enough memory*/ } - else p->size = size; return 1; } +/*returns 1 if success, 0 if failure ==> nothing done*/ +static unsigned ucvector_resize(ucvector* p, size_t size) +{ + if(!ucvector_reserve(p, size * sizeof(unsigned char))) return 0; + p->size = size; + return 1; /*success*/ +} + #ifdef LODEPNG_COMPILE_PNG static void ucvector_cleanup(void* p) @@ -236,17 +248,6 @@ p->data = NULL; p->size = p->allocsize = 0; } - -#ifdef LODEPNG_COMPILE_DECODER -/*resize and give all new elements the value*/ -static unsigned ucvector_resizev(ucvector* p, size_t size, unsigned char value) -{ - size_t oldsize = p->size, i; - if(!ucvector_resize(p, size)) return 0; - for(i = oldsize; i < size; i++) p->data[i] = value; - return 1; -} -#endif /*LODEPNG_COMPILE_DECODER*/ #endif /*LODEPNG_COMPILE_PNG*/ #ifdef LODEPNG_COMPILE_ZLIB @@ -302,10 +303,10 @@ static void string_set(char** out, const char* in) { - size_t insize = strlen(in), i = 0; + size_t insize = strlen(in), i; if(string_resize(out, insize)) { - for(i = 0; i < insize; i++) + for(i = 0; i != insize; ++i) { (*out)[i] = in[i]; } @@ -318,7 +319,7 @@ unsigned lodepng_read32bitInt(const unsigned char* buffer) { - return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + return (unsigned)((buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]); } #if defined(LODEPNG_COMPILE_PNG) || defined(LODEPNG_COMPILE_ENCODER) @@ -346,31 +347,53 @@ #ifdef LODEPNG_COMPILE_DISK -unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +/* returns negative value on error. This should be pure C compatible, so no fstat. */ +static long lodepng_filesize(const char* filename) { FILE* file; long size; + file = fopen(filename, "rb"); + if(!file) return -1; - /*provide some proper output values if error will happen*/ - *out = 0; - *outsize = 0; + if(fseek(file, 0, SEEK_END) != 0) + { + fclose(file); + return -1; + } + + size = ftell(file); + /* It may give LONG_MAX as directory size, this is invalid for us. */ + if(size == LONG_MAX) size = -1; + fclose(file); + return size; +} + +/* load file into buffer that already has the correct allocated size. Returns error code.*/ +static unsigned lodepng_buffer_file(unsigned char* out, size_t size, const char* filename) +{ + FILE* file; + size_t readsize; file = fopen(filename, "rb"); if(!file) return 78; - /*get filesize:*/ - fseek(file , 0 , SEEK_END); - size = ftell(file); - rewind(file); + readsize = fread(out, 1, size, file); + fclose(file); + + if (readsize != size) return 78; + return 0; +} + +unsigned lodepng_load_file(unsigned char** out, size_t* outsize, const char* filename) +{ + long size = lodepng_filesize(filename); + if (size < 0) return 78; + *outsize = (size_t)size; - /*read contents of the file into the vector*/ - *outsize = 0; *out = (unsigned char*)lodepng_malloc((size_t)size); - if(size && (*out)) (*outsize) = fread(*out, 1, (size_t)size, file); + if(!(*out) && size > 0) return 83; /*the above malloc failed*/ - fclose(file); - if(!(*out) && size) return 83; /*the above malloc failed*/ - return 0; + return lodepng_buffer_file(*out, (size_t)size, filename); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ @@ -401,19 +424,19 @@ if(((*bitpointer) & 7) == 0) ucvector_push_back(bitstream, (unsigned char)0);\ /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/\ (bitstream->data[bitstream->size - 1]) |= (bit << ((*bitpointer) & 0x7));\ - (*bitpointer)++;\ + ++(*bitpointer);\ } static void addBitsToStream(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; - for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> i) & 1)); } static void addBitsToStreamReversed(size_t* bitpointer, ucvector* bitstream, unsigned value, size_t nbits) { size_t i; - for(i = 0; i < nbits; i++) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); + for(i = 0; i != nbits; ++i) addBitToStream(bitpointer, bitstream, (unsigned char)((value >> (nbits - 1 - i)) & 1)); } #endif /*LODEPNG_COMPILE_ENCODER*/ @@ -424,17 +447,17 @@ static unsigned char readBitFromStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)(READBIT(*bitpointer, bitstream)); - (*bitpointer)++; + ++(*bitpointer); return result; } static unsigned readBitsFromStream(size_t* bitpointer, const unsigned char* bitstream, size_t nbits) { unsigned result = 0, i; - for(i = 0; i < nbits; i++) + for(i = 0; i != nbits; ++i) { result += ((unsigned)READBIT(*bitpointer, bitstream)) << i; - (*bitpointer)++; + ++(*bitpointer); } return result; } @@ -497,7 +520,7 @@ static void HuffmanTree_draw(HuffmanTree* tree) { std::cout << "tree. length: " << tree->numcodes << " maxbitlen: " << tree->maxbitlen << std::endl; - for(size_t i = 0; i < tree->tree1d.size; i++) + for(size_t i = 0; i != tree->tree1d.size; ++i) { if(tree->lengths.data[i]) std::cout << i << " " << tree->tree1d.data[i] << " " << tree->lengths.data[i] << std::endl; @@ -534,22 +557,23 @@ uninited, a value >= numcodes is an address to another bit, a value < numcodes is a code. The 2 rows are the 2 possible bit values (0 or 1), there are as many columns as codes - 1. - A good huffmann tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. + A good huffman tree has N * 2 - 1 nodes, of which N - 1 are internal nodes. Here, the internal nodes are stored (what their 0 and 1 option point to). There is only memory for such good tree currently, if there are more nodes (due to too long length codes), error 55 will happen */ - for(n = 0; n < tree->numcodes * 2; n++) + for(n = 0; n < tree->numcodes * 2; ++n) { tree->tree2d[n] = 32767; /*32767 here means the tree2d isn't filled there yet*/ } - for(n = 0; n < tree->numcodes; n++) /*the codes*/ + for(n = 0; n < tree->numcodes; ++n) /*the codes*/ { - for(i = 0; i < tree->lengths[n]; i++) /*the bits for this code*/ + for(i = 0; i != tree->lengths[n]; ++i) /*the bits for this code*/ { unsigned char bit = (unsigned char)((tree->tree1d[n] >> (tree->lengths[n] - i - 1)) & 1); - if(treepos > tree->numcodes - 2) return 55; /*oversubscribed, see comment in lodepng_error_text*/ + /*oversubscribed, see comment in lodepng_error_text*/ + if(treepos > 2147483647 || treepos + 2 > tree->numcodes) return 55; if(tree->tree2d[2 * treepos + bit] == 32767) /*not yet filled in*/ { if(i + 1 == tree->lengths[n]) /*last bit*/ @@ -561,7 +585,7 @@ { /*put address of the next step in here, first that address has to be found of course (it's just nodefilled + 1)...*/ - nodefilled++; + ++nodefilled; /*addresses encoded with numcodes added to it*/ tree->tree2d[2 * treepos + bit] = nodefilled + tree->numcodes; treepos = nodefilled; @@ -571,7 +595,7 @@ } } - for(n = 0; n < tree->numcodes * 2; n++) + for(n = 0; n < tree->numcodes * 2; ++n) { if(tree->tree2d[n] == 32767) tree->tree2d[n] = 0; /*remove possible remaining 32767's*/ } @@ -588,7 +612,8 @@ { uivector blcount; uivector nextcode; - unsigned bits, n, error = 0; + unsigned error = 0; + unsigned bits, n; uivector_init(&blcount); uivector_init(&nextcode); @@ -603,14 +628,14 @@ if(!error) { /*step 1: count number of instances of each code length*/ - for(bits = 0; bits < tree->numcodes; bits++) blcount.data[tree->lengths[bits]]++; + for(bits = 0; bits != tree->numcodes; ++bits) ++blcount.data[tree->lengths[bits]]; /*step 2: generate the nextcode values*/ - for(bits = 1; bits <= tree->maxbitlen; bits++) + for(bits = 1; bits <= tree->maxbitlen; ++bits) { nextcode.data[bits] = (nextcode.data[bits - 1] + blcount.data[bits - 1]) << 1; } /*step 3: generate all the codes*/ - for(n = 0; n < tree->numcodes; n++) + for(n = 0; n != tree->numcodes; ++n) { if(tree->lengths[n] != 0) tree->tree1d[n] = nextcode.data[tree->lengths[n]]++; } @@ -634,7 +659,7 @@ unsigned i; tree->lengths = (unsigned*)lodepng_malloc(numcodes * sizeof(unsigned)); if(!tree->lengths) return 83; /*alloc fail*/ - for(i = 0; i < numcodes; i++) tree->lengths[i] = bitlen[i]; + for(i = 0; i != numcodes; ++i) tree->lengths[i] = bitlen[i]; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->maxbitlen = maxbitlen; return HuffmanTree_makeFromLengths2(tree); @@ -642,101 +667,136 @@ #ifdef LODEPNG_COMPILE_ENCODER -/* -A coin, this is the terminology used for the package-merge algorithm and the -coin collector's problem. This is used to generate the huffman tree. -A coin can be multiple coins (when they're merged) -*/ -typedef struct Coin -{ - uivector symbols; - float weight; /*the sum of all weights in this coin*/ -} Coin; +/*BPM: Boundary Package Merge, see "A Fast and Space-Economical Algorithm for Length-Limited Coding", +Jyrki Katajainen, Alistair Moffat, Andrew Turpin, 1995.*/ -static void coin_init(Coin* c) +/*chain node for boundary package merge*/ +typedef struct BPMNode { - uivector_init(&c->symbols); -} + int weight; /*the sum of all weights in this chain*/ + unsigned index; /*index of this leaf node (called "count" in the paper)*/ + struct BPMNode* tail; /*the next nodes in this chain (null if last)*/ + int in_use; +} BPMNode; + +/*lists of chains*/ +typedef struct BPMLists +{ + /*memory pool*/ + unsigned memsize; + BPMNode* memory; + unsigned numfree; + unsigned nextfree; + BPMNode** freelist; + /*two heads of lookahead chains per list*/ + unsigned listsize; + BPMNode** chains0; + BPMNode** chains1; +} BPMLists; -/*argument c is void* so that this dtor can be given as function pointer to the vector resize function*/ -static void coin_cleanup(void* c) +/*creates a new chain node with the given parameters, from the memory in the lists */ +static BPMNode* bpmnode_create(BPMLists* lists, int weight, unsigned index, BPMNode* tail) { - uivector_cleanup(&((Coin*)c)->symbols); -} + unsigned i; + BPMNode* result; -static void coin_copy(Coin* c1, const Coin* c2) -{ - c1->weight = c2->weight; - uivector_copy(&c1->symbols, &c2->symbols); -} + /*memory full, so garbage collect*/ + if(lists->nextfree >= lists->numfree) + { + /*mark only those that are in use*/ + for(i = 0; i != lists->memsize; ++i) lists->memory[i].in_use = 0; + for(i = 0; i != lists->listsize; ++i) + { + BPMNode* node; + for(node = lists->chains0[i]; node != 0; node = node->tail) node->in_use = 1; + for(node = lists->chains1[i]; node != 0; node = node->tail) node->in_use = 1; + } + /*collect those that are free*/ + lists->numfree = 0; + for(i = 0; i != lists->memsize; ++i) + { + if(!lists->memory[i].in_use) lists->freelist[lists->numfree++] = &lists->memory[i]; + } + lists->nextfree = 0; + } -static void add_coins(Coin* c1, const Coin* c2) -{ - size_t i; - for(i = 0; i < c2->symbols.size; i++) uivector_push_back(&c1->symbols, c2->symbols.data[i]); - c1->weight += c2->weight; + result = lists->freelist[lists->nextfree++]; + result->weight = weight; + result->index = index; + result->tail = tail; + return result; } -static void init_coins(Coin* coins, size_t num) +static int bpmnode_compare(const void* a, const void* b) { - size_t i; - for(i = 0; i < num; i++) coin_init(&coins[i]); + int wa = ((const BPMNode*)a)->weight; + int wb = ((const BPMNode*)b)->weight; + if(wa < wb) return -1; + if(wa > wb) return 1; + /*make the qsort a stable sort*/ + return ((const BPMNode*)a)->index < ((const BPMNode*)b)->index ? 1 : -1; } -static void cleanup_coins(Coin* coins, size_t num) +/*Boundary Package Merge step, numpresent is the amount of leaves, and c is the current chain.*/ +static void boundaryPM(BPMLists* lists, BPMNode* leaves, size_t numpresent, int c, int num) { - size_t i; - for(i = 0; i < num; i++) coin_cleanup(&coins[i]); -} - -static int coin_compare(const void* a, const void* b) { - float wa = ((const Coin*)a)->weight; - float wb = ((const Coin*)b)->weight; - return wa > wb ? 1 : wa < wb ? -1 : 0; -} + unsigned lastindex = lists->chains1[c]->index; -static unsigned append_symbol_coins(Coin* coins, const unsigned* frequencies, unsigned numcodes, size_t sum) -{ - unsigned i; - unsigned j = 0; /*index of present symbols*/ - for(i = 0; i < numcodes; i++) + if(c == 0) + { + if(lastindex >= numpresent) return; + lists->chains0[c] = lists->chains1[c]; + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, 0); + } + else { - if(frequencies[i] != 0) /*only include symbols that are present*/ + /*sum of the weights of the head nodes of the previous lookahead chains.*/ + int sum = lists->chains0[c - 1]->weight + lists->chains1[c - 1]->weight; + lists->chains0[c] = lists->chains1[c]; + if(lastindex < numpresent && sum > leaves[lastindex].weight) + { + lists->chains1[c] = bpmnode_create(lists, leaves[lastindex].weight, lastindex + 1, lists->chains1[c]->tail); + return; + } + lists->chains1[c] = bpmnode_create(lists, sum, lastindex, lists->chains1[c - 1]); + /*in the end we are only interested in the chain of the last list, so no + need to recurse if we're at the last one (this gives measurable speedup)*/ + if(num + 1 < (int)(2 * numpresent - 2)) { - coins[j].weight = frequencies[i] / (float)sum; - uivector_push_back(&coins[j].symbols, i); - j++; + boundaryPM(lists, leaves, numpresent, c - 1, num); + boundaryPM(lists, leaves, numpresent, c - 1, num); } } - return 0; } unsigned lodepng_huffman_code_lengths(unsigned* lengths, const unsigned* frequencies, size_t numcodes, unsigned maxbitlen) { - unsigned i, j; - size_t sum = 0, numpresent = 0; unsigned error = 0; - Coin* coins; /*the coins of the currently calculated row*/ - Coin* prev_row; /*the previous row of coins*/ - unsigned numcoins; - unsigned coinmem; + unsigned i; + size_t numpresent = 0; /*number of symbols with non-zero frequency*/ + BPMNode* leaves; /*the symbols, only those with > 0 frequency*/ if(numcodes == 0) return 80; /*error: a tree of 0 symbols is not supposed to be made*/ + if((1u << maxbitlen) < numcodes) return 80; /*error: represent all symbols*/ + + leaves = (BPMNode*)lodepng_malloc(numcodes * sizeof(*leaves)); + if(!leaves) return 83; /*alloc fail*/ - for(i = 0; i < numcodes; i++) + for(i = 0; i != numcodes; ++i) { if(frequencies[i] > 0) { - numpresent++; - sum += frequencies[i]; + leaves[numpresent].weight = (int)frequencies[i]; + leaves[numpresent].index = i; + ++numpresent; } } - for(i = 0; i < numcodes; i++) lengths[i] = 0; + for(i = 0; i != numcodes; ++i) lengths[i] = 0; /*ensure at least two present symbols. There should be at least one symbol - according to RFC 1951 section 3.2.7. To decoders incorrectly require two. To + according to RFC 1951 section 3.2.7. Some decoders incorrectly require two. To make these work as well ensure there are at least two symbols. The Package-Merge code below also doesn't work correctly if there's only one symbol, it'd give it the theoritical 0 bits but in practice zlib wants 1 bit*/ @@ -746,87 +806,55 @@ } else if(numpresent == 1) { - for(i = 0; i < numcodes; i++) - { - if(frequencies[i]) - { - lengths[i] = 1; - lengths[i == 0 ? 1 : 0] = 1; - break; - } - } + lengths[leaves[0].index] = 1; + lengths[leaves[0].index == 0 ? 1 : 0] = 1; } else { - /*Package-Merge algorithm represented by coin collector's problem - For every symbol, maxbitlen coins will be created*/ + BPMLists lists; + BPMNode* node; + + qsort(leaves, numpresent, sizeof(BPMNode), bpmnode_compare); + + lists.listsize = maxbitlen; + lists.memsize = 2 * maxbitlen * (maxbitlen + 1); + lists.nextfree = 0; + lists.numfree = lists.memsize; + lists.memory = (BPMNode*)lodepng_malloc(lists.memsize * sizeof(*lists.memory)); + lists.freelist = (BPMNode**)lodepng_malloc(lists.memsize * sizeof(BPMNode*)); + lists.chains0 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + lists.chains1 = (BPMNode**)lodepng_malloc(lists.listsize * sizeof(BPMNode*)); + if(!lists.memory || !lists.freelist || !lists.chains0 || !lists.chains1) error = 83; /*alloc fail*/ - coinmem = numpresent * 2; /*max amount of coins needed with the current algo*/ - coins = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem); - prev_row = (Coin*)lodepng_malloc(sizeof(Coin) * coinmem); - if(!coins || !prev_row) - { - lodepng_free(coins); - lodepng_free(prev_row); - return 83; /*alloc fail*/ - } - init_coins(coins, coinmem); - init_coins(prev_row, coinmem); - - /*first row, lowest denominator*/ - error = append_symbol_coins(coins, frequencies, numcodes, sum); - numcoins = numpresent; - qsort(coins, numcoins, sizeof(Coin), coin_compare); if(!error) { - unsigned numprev = 0; - for(j = 1; j <= maxbitlen && !error; j++) /*each of the remaining rows*/ - { - unsigned tempnum; - Coin* tempcoins; - /*swap prev_row and coins, and their amounts*/ - tempcoins = prev_row; prev_row = coins; coins = tempcoins; - tempnum = numprev; numprev = numcoins; numcoins = tempnum; + for(i = 0; i != lists.memsize; ++i) lists.freelist[i] = &lists.memory[i]; - cleanup_coins(coins, numcoins); - init_coins(coins, numcoins); + bpmnode_create(&lists, leaves[0].weight, 1, 0); + bpmnode_create(&lists, leaves[1].weight, 2, 0); - numcoins = 0; - - /*fill in the merged coins of the previous row*/ - for(i = 0; i + 1 < numprev; i += 2) - { - /*merge prev_row[i] and prev_row[i + 1] into new coin*/ - Coin* coin = &coins[numcoins++]; - coin_copy(coin, &prev_row[i]); - add_coins(coin, &prev_row[i + 1]); - } - /*fill in all the original symbols again*/ - if(j < maxbitlen) - { - error = append_symbol_coins(coins + numcoins, frequencies, numcodes, sum); - numcoins += numpresent; - } - qsort(coins, numcoins, sizeof(Coin), coin_compare); + for(i = 0; i != lists.listsize; ++i) + { + lists.chains0[i] = &lists.memory[0]; + lists.chains1[i] = &lists.memory[1]; } - } - if(!error) - { - /*calculate the lenghts of each symbol, as the amount of times a coin of each symbol is used*/ - for(i = 0; i < numpresent - 1; i++) + /*each boundaryPM call adds one chain to the last list, and we need 2 * numpresent - 2 chains.*/ + for(i = 2; i != 2 * numpresent - 2; ++i) boundaryPM(&lists, leaves, numpresent, (int)maxbitlen - 1, (int)i); + + for(node = lists.chains1[maxbitlen - 1]; node; node = node->tail) { - Coin* coin = &coins[i]; - for(j = 0; j < coin->symbols.size; j++) lengths[coin->symbols.data[j]]++; + for(i = 0; i != node->index; ++i) ++lengths[leaves[i].index]; } } - cleanup_coins(coins, coinmem); - lodepng_free(coins); - cleanup_coins(prev_row, coinmem); - lodepng_free(prev_row); + lodepng_free(lists.memory); + lodepng_free(lists.freelist); + lodepng_free(lists.chains0); + lodepng_free(lists.chains1); } + lodepng_free(leaves); return error; } @@ -835,7 +863,7 @@ size_t mincodes, size_t numcodes, unsigned maxbitlen) { unsigned error = 0; - while(!frequencies[numcodes - 1] && numcodes > mincodes) numcodes--; /*trim zeroes*/ + while(!frequencies[numcodes - 1] && numcodes > mincodes) --numcodes; /*trim zeroes*/ tree->maxbitlen = maxbitlen; tree->numcodes = (unsigned)numcodes; /*number of symbols*/ tree->lengths = (unsigned*)lodepng_realloc(tree->lengths, numcodes * sizeof(unsigned)); @@ -867,10 +895,10 @@ if(!bitlen) return 83; /*alloc fail*/ /*288 possible codes: 0-255=literals, 256=endcode, 257-285=lengthcodes, 286-287=unused*/ - for(i = 0; i <= 143; i++) bitlen[i] = 8; - for(i = 144; i <= 255; i++) bitlen[i] = 9; - for(i = 256; i <= 279; i++) bitlen[i] = 7; - for(i = 280; i <= 287; i++) bitlen[i] = 8; + for(i = 0; i <= 143; ++i) bitlen[i] = 8; + for(i = 144; i <= 255; ++i) bitlen[i] = 9; + for(i = 256; i <= 279; ++i) bitlen[i] = 7; + for(i = 280; i <= 287; ++i) bitlen[i] = 8; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DEFLATE_CODE_SYMBOLS, 15); @@ -886,7 +914,7 @@ if(!bitlen) return 83; /*alloc fail*/ /*there are 32 distance codes, but 30-31 are unused*/ - for(i = 0; i < NUM_DISTANCE_SYMBOLS; i++) bitlen[i] = 5; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen[i] = 5; error = HuffmanTree_makeFromLengths(tree, bitlen, NUM_DISTANCE_SYMBOLS, 15); lodepng_free(bitlen); @@ -911,7 +939,7 @@ the expression below because this is the biggest bottleneck while decoding */ ct = codetree->tree2d[(treepos << 1) + READBIT(*bp, in)]; - (*bp)++; + ++(*bp); if(ct < codetree->numcodes) return ct; /*the symbol is decoded, return it*/ else treepos = ct - codetree->numcodes; /*symbol not yet decoded, instead move tree position*/ @@ -950,7 +978,7 @@ unsigned* bitlen_cl = 0; HuffmanTree tree_cl; /*the code tree for code length codes (the huffman tree for compressed huffman trees)*/ - if((*bp) >> 3 >= inlength - 2) return 49; /*error: the bit pointer is or will go past the memory*/ + if((*bp) + 14 > (inlength << 3)) return 49; /*error: the bit pointer is or will go past the memory*/ /*number of literal/length codes + 257. Unlike the spec, the value 257 is added to it here already*/ HLIT = readBitsFromStream(bp, in, 5) + 257; @@ -959,6 +987,8 @@ /*number of code length codes. Unlike the spec, the value 4 is added to it here already*/ HCLEN = readBitsFromStream(bp, in, 4) + 4; + if((*bp) + HCLEN * 3 > (inlength << 3)) return 50; /*error: the bit pointer is or will go past the memory*/ + HuffmanTree_init(&tree_cl); while(!error) @@ -968,7 +998,7 @@ bitlen_cl = (unsigned*)lodepng_malloc(NUM_CODE_LENGTH_CODES * sizeof(unsigned)); if(!bitlen_cl) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < NUM_CODE_LENGTH_CODES; i++) + for(i = 0; i != NUM_CODE_LENGTH_CODES; ++i) { if(i < HCLEN) bitlen_cl[CLCL_ORDER[i]] = readBitsFromStream(bp, in, 3); else bitlen_cl[CLCL_ORDER[i]] = 0; /*if not, it must stay 0*/ @@ -981,8 +1011,8 @@ bitlen_ll = (unsigned*)lodepng_malloc(NUM_DEFLATE_CODE_SYMBOLS * sizeof(unsigned)); bitlen_d = (unsigned*)lodepng_malloc(NUM_DISTANCE_SYMBOLS * sizeof(unsigned)); if(!bitlen_ll || !bitlen_d) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < NUM_DEFLATE_CODE_SYMBOLS; i++) bitlen_ll[i] = 0; - for(i = 0; i < NUM_DISTANCE_SYMBOLS; i++) bitlen_d[i] = 0; + for(i = 0; i != NUM_DEFLATE_CODE_SYMBOLS; ++i) bitlen_ll[i] = 0; + for(i = 0; i != NUM_DISTANCE_SYMBOLS; ++i) bitlen_d[i] = 0; /*i is the current symbol we're reading in the part that contains the code lengths of lit/len and dist codes*/ i = 0; @@ -993,61 +1023,59 @@ { if(i < HLIT) bitlen_ll[i] = code; else bitlen_d[i - HLIT] = code; - i++; + ++i; } else if(code == 16) /*repeat previous*/ { unsigned replength = 3; /*read in the 2 bits that indicate repeat length (3-6)*/ unsigned value; /*set value to the previous code*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - if (i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + if(i == 0) ERROR_BREAK(54); /*can't repeat previous if i is 0*/ + if((*bp + 2) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 2); if(i < HLIT + 1) value = bitlen_ll[i - 1]; else value = bitlen_d[i - HLIT - 1]; /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(13); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = value; else bitlen_d[i - HLIT] = value; - i++; + ++i; } } else if(code == 17) /*repeat "0" 3-10 times*/ { unsigned replength = 3; /*read in the bits that indicate repeat length*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - + if((*bp + 3) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 3); /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(14); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; - i++; + ++i; } } else if(code == 18) /*repeat "0" 11-138 times*/ { unsigned replength = 11; /*read in the bits that indicate repeat length*/ - if(*bp >= inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ - + if((*bp + 7) > inbitlength) ERROR_BREAK(50); /*error, bit pointer jumps past memory*/ replength += readBitsFromStream(bp, in, 7); /*repeat this value in the next lengths*/ - for(n = 0; n < replength; n++) + for(n = 0; n < replength; ++n) { if(i >= HLIT + HDIST) ERROR_BREAK(15); /*error: i is larger than the amount of codes*/ if(i < HLIT) bitlen_ll[i] = 0; else bitlen_d[i - HLIT] = 0; - i++; + ++i; } } else /*if(code == (unsigned)(-1))*/ /*huffmanDecodeSymbol returns (unsigned)(-1) in case of error*/ @@ -1103,13 +1131,10 @@ unsigned code_ll = huffmanDecodeSymbol(in, bp, &tree_ll, inbitlength); if(code_ll <= 255) /*literal symbol*/ { - if((*pos) >= out->size) - { - /*reserve more room at once*/ - if(!ucvector_resize(out, ((*pos) + 1) * 2)) ERROR_BREAK(83 /*alloc fail*/); - } - out->data[(*pos)] = (unsigned char)(code_ll); - (*pos)++; + /*ucvector_push_back would do the same, but for some reason the two lines below run 10% faster*/ + if(!ucvector_resize(out, (*pos) + 1)) ERROR_BREAK(83 /*alloc fail*/); + out->data[*pos] = (unsigned char)code_ll; + ++(*pos); } else if(code_ll >= FIRST_LENGTH_CODE_INDEX && code_ll <= LAST_LENGTH_CODE_INDEX) /*length code*/ { @@ -1122,7 +1147,7 @@ /*part 2: get extra bits and add the value of that to length*/ numextrabits_l = LENGTHEXTRA[code_ll - FIRST_LENGTH_CODE_INDEX]; - if(*bp >= inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ + if((*bp + numextrabits_l) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ length += readBitsFromStream(bp, in, numextrabits_l); /*part 3: get distance code*/ @@ -1142,26 +1167,23 @@ /*part 4: get extra bits from distance*/ numextrabits_d = DISTANCEEXTRA[code_d]; - if(*bp >= inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ - + if((*bp + numextrabits_d) > inbitlength) ERROR_BREAK(51); /*error, bit pointer will jump past memory*/ distance += readBitsFromStream(bp, in, numextrabits_d); /*part 5: fill in all the out[n] values based on the length and dist*/ start = (*pos); if(distance > start) ERROR_BREAK(52); /*too long backward distance*/ backward = start - distance; - if((*pos) + length >= out->size) - { - /*reserve more room at once*/ - if(!ucvector_resize(out, ((*pos) + length) * 2)) ERROR_BREAK(83 /*alloc fail*/); - } - for(forward = 0; forward < length; forward++) - { - out->data[(*pos)] = out->data[backward]; - (*pos)++; - backward++; - if(backward >= start) backward = start - distance; + if(!ucvector_resize(out, (*pos) + length)) ERROR_BREAK(83 /*alloc fail*/); + if (distance < length) { + for(forward = 0; forward < length; ++forward) + { + out->data[(*pos)++] = out->data[backward++]; + } + } else { + memcpy(out->data + *pos, out->data + backward, length); + *pos += length; } } else if(code_ll == 256) @@ -1172,7 +1194,7 @@ { /*return error code 10 or 11 depending on the situation that happened in huffmanDecodeSymbol (10=no endcode, 11=wrong jump outside of tree)*/ - error = (*bp) > inlength * 8 ? 10 : 11; + error = ((*bp) > inlength * 8) ? 10 : 11; break; } } @@ -1185,28 +1207,26 @@ static unsigned inflateNoCompression(ucvector* out, const unsigned char* in, size_t* bp, size_t* pos, size_t inlength) { - /*go to first boundary of byte*/ size_t p; unsigned LEN, NLEN, n, error = 0; - while(((*bp) & 0x7) != 0) (*bp)++; + + /*go to first boundary of byte*/ + while(((*bp) & 0x7) != 0) ++(*bp); p = (*bp) / 8; /*byte position*/ /*read LEN (2 bytes) and NLEN (2 bytes)*/ - if(p >= inlength - 4) return 52; /*error, bit pointer will jump past memory*/ - LEN = in[p] + 256 * in[p + 1]; p += 2; - NLEN = in[p] + 256 * in[p + 1]; p += 2; + if(p + 4 >= inlength) return 52; /*error, bit pointer will jump past memory*/ + LEN = in[p] + 256u * in[p + 1]; p += 2; + NLEN = in[p] + 256u * in[p + 1]; p += 2; /*check if 16-bit NLEN is really the one's complement of LEN*/ if(LEN + NLEN != 65535) return 21; /*error: NLEN is not one's complement of LEN*/ - if((*pos) + LEN >= out->size) - { - if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ - } + if(!ucvector_resize(out, (*pos) + LEN)) return 83; /*alloc fail*/ /*read the literal data: LEN bytes are now stored in the out buffer*/ if(p + LEN > inlength) return 23; /*error: reading outside of in buffer*/ - for(n = 0; n < LEN; n++) out->data[(*pos)++] = in[p++]; + for(n = 0; n < LEN; ++n) out->data[(*pos)++] = in[p++]; (*bp) = p * 8; @@ -1221,7 +1241,6 @@ size_t bp = 0; unsigned BFINAL = 0; size_t pos = 0; /*byte position in the out buffer*/ - unsigned error = 0; (void)settings; @@ -1231,8 +1250,8 @@ unsigned BTYPE; if(bp + 2 >= insize * 8) return 52; /*error, bit pointer will jump past memory*/ BFINAL = readBitFromStream(&bp, in); - BTYPE = 1 * readBitFromStream(&bp, in); - BTYPE += 2 * readBitFromStream(&bp, in); + BTYPE = 1u * readBitFromStream(&bp, in); + BTYPE += 2u * readBitFromStream(&bp, in); if(BTYPE == 3) return 20; /*error: invalid BTYPE*/ else if(BTYPE == 0) error = inflateNoCompression(out, in, &bp, &pos, insize); /*no compression*/ @@ -1241,9 +1260,6 @@ if(error) return error; } - /*Only now we know the true size of out, resize it to that*/ - if(!ucvector_resize(out, pos)) error = 83; /*alloc fail*/ - return error; } @@ -1294,21 +1310,17 @@ given array must be sorted (if no value is smaller, it returns the size of the given array)*/ static size_t searchCodeIndex(const unsigned* array, size_t array_size, size_t value) { - /*linear search implementation*/ - /*for(size_t i = 1; i < array_size; i++) if(array[i] > value) return i - 1; - return array_size - 1;*/ - - /*binary search implementation (not that much faster) (precondition: array_size > 0)*/ - size_t left = 1; + /*binary search (only small gain over linear). TODO: use CPU log2 instruction for getting symbols instead*/ + size_t left = 1; size_t right = array_size - 1; - while(left <= right) - { - size_t mid = (left + right) / 2; - if(array[mid] <= value) left = mid + 1; /*the value to find is more to the right*/ - else if(array[mid - 1] > value) right = mid - 1; /*the value to find is more to the left*/ - else return mid - 1; + + while(left <= right) { + size_t mid = (left + right) >> 1; + if (array[mid] >= value) right = mid - 1; + else left = mid + 1; } - return array_size - 1; + if(left >= array_size || array[left] > value) left--; + return left; } static void addLengthDistance(uivector* values, size_t length, size_t distance) @@ -1330,31 +1342,23 @@ uivector_push_back(values, extra_distance); } -static const unsigned HASH_BIT_MASK = 65535; +/*3 bytes of data get encoded into two bytes. The hash cannot use more than 3 +bytes as input because 3 is the minimum match length for deflate*/ static const unsigned HASH_NUM_VALUES = 65536; -static const unsigned HASH_NUM_CHARACTERS = 3; -static const unsigned HASH_SHIFT = 2; -/* -The HASH_NUM_CHARACTERS value is used to make encoding faster by using longer -sequences to generate a hash value from the stream bytes. Setting it to 3 -gives exactly the same compression as the brute force method, since deflate's -run length encoding starts with lengths of 3. Setting it to higher values, -like 6, can make the encoding faster (not always though!), but will cause the -encoding to miss any length between 3 and this value, so that the compression -may be worse (but this can vary too depending on the image, sometimes it is -even a bit better instead). -The HASH_NUM_VALUES is the amount of unique possible hash values that -combinations of bytes can give, the higher it is the more memory is needed, but -if it's too low the advantage of hashing is gone. -*/ +static const unsigned HASH_BIT_MASK = 65535; /*HASH_NUM_VALUES - 1, but C90 does not like that as initializer*/ typedef struct Hash { - int* head; /*hash value to head circular pos*/ - int* val; /*circular pos to hash value*/ + int* head; /*hash value to head circular pos - can be outdated if went around window*/ /*circular pos to prev circular pos*/ unsigned short* chain; - unsigned short* zeros; + int* val; /*circular pos to hash value*/ + + /*TODO: do this not only for zeros but for any repeated byte. However for PNG + it's always going to be the zeros that dominate, so not important for PNG*/ + int* headz; /*similar to head, but for chainz*/ + unsigned short* chainz; /*those with same amount of zeros*/ + unsigned short* zeros; /*length of zeros streak, used as a second hash chain*/ } Hash; static unsigned hash_init(Hash* hash, unsigned windowsize) @@ -1363,14 +1367,23 @@ hash->head = (int*)lodepng_malloc(sizeof(int) * HASH_NUM_VALUES); hash->val = (int*)lodepng_malloc(sizeof(int) * windowsize); hash->chain = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->zeros = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); + hash->headz = (int*)lodepng_malloc(sizeof(int) * (MAX_SUPPORTED_DEFLATE_LENGTH + 1)); + hash->chainz = (unsigned short*)lodepng_malloc(sizeof(unsigned short) * windowsize); - if(!hash->head || !hash->val || !hash->chain || !hash->zeros) return 83; /*alloc fail*/ + if(!hash->head || !hash->chain || !hash->val || !hash->headz|| !hash->chainz || !hash->zeros) + { + return 83; /*alloc fail*/ + } /*initialize hash table*/ - for(i = 0; i < HASH_NUM_VALUES; i++) hash->head[i] = -1; - for(i = 0; i < windowsize; i++) hash->val[i] = -1; - for(i = 0; i < windowsize; i++) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + for(i = 0; i != HASH_NUM_VALUES; ++i) hash->head[i] = -1; + for(i = 0; i != windowsize; ++i) hash->val[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chain[i] = i; /*same value as index indicates uninitialized*/ + + for(i = 0; i <= MAX_SUPPORTED_DEFLATE_LENGTH; ++i) hash->headz[i] = -1; + for(i = 0; i != windowsize; ++i) hash->chainz[i] = i; /*same value as index indicates uninitialized*/ return 0; } @@ -1380,22 +1393,31 @@ lodepng_free(hash->head); lodepng_free(hash->val); lodepng_free(hash->chain); + lodepng_free(hash->zeros); + lodepng_free(hash->headz); + lodepng_free(hash->chainz); } + + static unsigned getHash(const unsigned char* data, size_t size, size_t pos) { unsigned result = 0; - if (HASH_NUM_CHARACTERS == 3 && pos + 2 < size) { - result ^= (data[pos + 0] << (0 * HASH_SHIFT)); - result ^= (data[pos + 1] << (1 * HASH_SHIFT)); - result ^= (data[pos + 2] << (2 * HASH_SHIFT)); + if(pos + 2 < size) + { + /*A simple shift and xor hash is used. Since the data of PNGs is dominated + by zeroes due to the filters, a better hash does not have a significant + effect on speed in traversing the chain, and causes more time spend on + calculating the hash.*/ + result ^= (unsigned)(data[pos + 0] << 0u); + result ^= (unsigned)(data[pos + 1] << 4u); + result ^= (unsigned)(data[pos + 2] << 8u); } else { size_t amount, i; if(pos >= size) return 0; - amount = HASH_NUM_CHARACTERS; - if(pos + amount >= size) amount = size - pos; - for(i = 0; i < amount; i++) result ^= (data[pos + i] << (i * HASH_SHIFT)); + amount = size - pos; + for(i = 0; i != amount; ++i) result ^= (unsigned)(data[pos + i] << (i * 8u)); } return result & HASH_BIT_MASK; } @@ -1406,17 +1428,21 @@ const unsigned char* end = start + MAX_SUPPORTED_DEFLATE_LENGTH; if(end > data + size) end = data + size; data = start; - while (data != end && *data == 0) data++; + while(data != end && *data == 0) ++data; /*subtracting two addresses returned as 32-bit number (max value is MAX_SUPPORTED_DEFLATE_LENGTH)*/ return (unsigned)(data - start); } /*wpos = pos & (windowsize - 1)*/ -static void updateHashChain(Hash* hash, size_t wpos, int hashval) +static void updateHashChain(Hash* hash, size_t wpos, unsigned hashval, unsigned short numzeros) { - hash->val[wpos] = hashval; + hash->val[wpos] = (int)hashval; if(hash->head[hashval] != -1) hash->chain[wpos] = hash->head[hashval]; hash->head[hashval] = wpos; + + hash->zeros[wpos] = numzeros; + if(hash->headz[numzeros] != -1) hash->chainz[wpos] = hash->headz[numzeros]; + hash->headz[numzeros] = wpos; } /* @@ -1432,7 +1458,8 @@ const unsigned char* in, size_t inpos, size_t insize, unsigned windowsize, unsigned minmatch, unsigned nicematch, unsigned lazymatching) { - unsigned pos, i, error = 0; + size_t pos; + unsigned i, error = 0; /*for large window lengths, assume the user wants no compression loss. Otherwise, max hash chain length speedup.*/ unsigned maxchainlength = windowsize >= 8192 ? windowsize : windowsize / 8; unsigned maxlazymatch = windowsize >= 8192 ? MAX_SUPPORTED_DEFLATE_LENGTH : 64; @@ -1446,51 +1473,51 @@ unsigned lazylength = 0, lazyoffset = 0; unsigned hashval; unsigned current_offset, current_length; + unsigned prev_offset; const unsigned char *lastptr, *foreptr, *backptr; - unsigned hashpos, prevpos; + unsigned hashpos; - if(windowsize <= 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ + if(windowsize == 0 || windowsize > 32768) return 60; /*error: windowsize smaller/larger than allowed*/ if((windowsize & (windowsize - 1)) != 0) return 90; /*error: must be power of two*/ if(nicematch > MAX_SUPPORTED_DEFLATE_LENGTH) nicematch = MAX_SUPPORTED_DEFLATE_LENGTH; - for(pos = inpos; pos < insize; pos++) + for(pos = inpos; pos < insize; ++pos) { size_t wpos = pos & (windowsize - 1); /*position for in 'circular' hash buffers*/ unsigned chainlength = 0; hashval = getHash(in, insize, pos); - updateHashChain(hash, wpos, hashval); if(usezeros && hashval == 0) { - if (numzeros == 0) numzeros = countZeros(in, insize, pos); - else if (pos + numzeros >= insize || in[pos + numzeros - 1] != 0) numzeros--; - hash->zeros[wpos] = numzeros; + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } + updateHashChain(hash, wpos, hashval, numzeros); + /*the length and offset found for the current position*/ length = 0; offset = 0; - prevpos = hash->head[hashval]; - hashpos = hash->chain[prevpos]; + hashpos = hash->chain[wpos]; lastptr = &in[insize < pos + MAX_SUPPORTED_DEFLATE_LENGTH ? insize : pos + MAX_SUPPORTED_DEFLATE_LENGTH]; /*search for the longest string*/ + prev_offset = 0; for(;;) { - /*stop when went completely around the circular buffer*/ - if(prevpos < wpos && hashpos > prevpos && hashpos <= wpos) break; - if(prevpos > wpos && (hashpos <= wpos || hashpos > prevpos)) break; if(chainlength++ >= maxchainlength) break; - current_offset = hashpos <= wpos ? wpos - hashpos : wpos - hashpos + windowsize; + + if(current_offset < prev_offset) break; /*stop when went completely around the circular buffer*/ + prev_offset = current_offset; if(current_offset > 0) { /*test the next characters*/ @@ -1498,7 +1525,7 @@ backptr = &in[pos - current_offset]; /*common case in PNGs is lots of zeros. Quickly skip over them as a speedup*/ - if(usezeros && hashval == 0 && hash->val[hashpos] == 0 /*hashval[hashpos] may be out of date*/) + if(numzeros >= 3) { unsigned skip = hash->zeros[hashpos]; if(skip > numzeros) skip = numzeros; @@ -1525,8 +1552,17 @@ if(hashpos == hash->chain[hashpos]) break; - prevpos = hashpos; - hashpos = hash->chain[hashpos]; + if(numzeros >= 3 && length > numzeros) + { + hashpos = hash->chainz[hashpos]; + if(hash->zeros[hashpos] != numzeros) break; + } + else + { + hashpos = hash->chain[hashpos]; + /*outdated hash value, happens if particular value was not encountered in whole last window*/ + if(hash->val[hashpos] != (int)hashval) break; + } } if(lazymatching) @@ -1552,7 +1588,8 @@ length = lazylength; offset = lazyoffset; hash->head[hashval] = -1; /*the same hashchain update will be done, this ensures no wrong alteration*/ - pos--; + hash->headz[numzeros] = -1; /*idem*/ + --pos; } } } @@ -1572,22 +1609,21 @@ else { addLengthDistance(out, length, offset); - for(i = 1; i < length; i++) + for(i = 1; i < length; ++i) { - pos++; + ++pos; wpos = pos & (windowsize - 1); hashval = getHash(in, insize, pos); - updateHashChain(hash, wpos, hashval); if(usezeros && hashval == 0) { - if (numzeros == 0) numzeros = countZeros(in, insize, pos); - else if (pos + numzeros >= insize || in[pos + numzeros - 1] != 0) numzeros--; - hash->zeros[wpos] = numzeros; + if(numzeros == 0) numzeros = countZeros(in, insize, pos); + else if(pos + numzeros > insize || in[pos + numzeros - 1] != 0) --numzeros; } else { numzeros = 0; } + updateHashChain(hash, wpos, hashval, numzeros); } } } /*end of the loop through each character of input*/ @@ -1604,7 +1640,7 @@ size_t i, j, numdeflateblocks = (datasize + 65534) / 65535; unsigned datapos = 0; - for(i = 0; i < numdeflateblocks; i++) + for(i = 0; i != numdeflateblocks; ++i) { unsigned BFINAL, BTYPE, LEN, NLEN; unsigned char firstbyte; @@ -1619,13 +1655,13 @@ if(datasize - datapos < 65535) LEN = (unsigned)datasize - datapos; NLEN = 65535 - LEN; - ucvector_push_back(out, (unsigned char)(LEN % 256)); - ucvector_push_back(out, (unsigned char)(LEN / 256)); - ucvector_push_back(out, (unsigned char)(NLEN % 256)); - ucvector_push_back(out, (unsigned char)(NLEN / 256)); + ucvector_push_back(out, (unsigned char)(LEN & 255)); + ucvector_push_back(out, (unsigned char)(LEN >> 8)); + ucvector_push_back(out, (unsigned char)(NLEN & 255)); + ucvector_push_back(out, (unsigned char)(NLEN >> 8)); /*Decompressed data*/ - for(j = 0; j < 65535 && datapos < datasize; j++) + for(j = 0; j < 65535 && datapos < datasize; ++j) { ucvector_push_back(out, data[datapos++]); } @@ -1643,7 +1679,7 @@ const HuffmanTree* tree_ll, const HuffmanTree* tree_d) { size_t i = 0; - for(i = 0; i < lz77_encoded->size; i++) + for(i = 0; i != lz77_encoded->size; ++i) { unsigned val = lz77_encoded->data[i]; addHuffmanSymbol(bp, out, HuffmanTree_getCode(tree_ll, val), HuffmanTree_getLength(tree_ll, val)); @@ -1670,7 +1706,7 @@ /*Deflate for a block of type "dynamic", that is, with freely, optimally, created huffman trees*/ static unsigned deflateDynamic(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, - const LodePNGCompressSettings* settings, int final) + const LodePNGCompressSettings* settings, unsigned final) { unsigned error = 0; @@ -1736,21 +1772,21 @@ else { if(!uivector_resize(&lz77_encoded, datasize)) ERROR_BREAK(83 /*alloc fail*/); - for(i = datapos; i < dataend; i++) lz77_encoded.data[i] = data[i]; /*no LZ77, but still will be Huffman compressed*/ + for(i = datapos; i < dataend; ++i) lz77_encoded.data[i - datapos] = data[i]; /*no LZ77, but still will be Huffman compressed*/ } if(!uivector_resizev(&frequencies_ll, 286, 0)) ERROR_BREAK(83 /*alloc fail*/); if(!uivector_resizev(&frequencies_d, 30, 0)) ERROR_BREAK(83 /*alloc fail*/); /*Count the frequencies of lit, len and dist codes*/ - for(i = 0; i < lz77_encoded.size; i++) + for(i = 0; i != lz77_encoded.size; ++i) { unsigned symbol = lz77_encoded.data[i]; - frequencies_ll.data[symbol]++; + ++frequencies_ll.data[symbol]; if(symbol > 256) { unsigned dist = lz77_encoded.data[i + 2]; - frequencies_d.data[dist]++; + ++frequencies_d.data[dist]; i += 3; } } @@ -1766,19 +1802,19 @@ numcodes_ll = tree_ll.numcodes; if(numcodes_ll > 286) numcodes_ll = 286; numcodes_d = tree_d.numcodes; if(numcodes_d > 30) numcodes_d = 30; /*store the code lengths of both generated trees in bitlen_lld*/ - for(i = 0; i < numcodes_ll; i++) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); - for(i = 0; i < numcodes_d; i++) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); + for(i = 0; i != numcodes_ll; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_ll, (unsigned)i)); + for(i = 0; i != numcodes_d; ++i) uivector_push_back(&bitlen_lld, HuffmanTree_getLength(&tree_d, (unsigned)i)); /*run-length compress bitlen_ldd into bitlen_lld_e by using repeat codes 16 (copy length 3-6 times), 17 (3-10 zeroes), 18 (11-138 zeroes)*/ - for(i = 0; i < (unsigned)bitlen_lld.size; i++) + for(i = 0; i != (unsigned)bitlen_lld.size; ++i) { unsigned j = 0; /*amount of repititions*/ - while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) j++; + while(i + j + 1 < (unsigned)bitlen_lld.size && bitlen_lld.data[i + j + 1] == bitlen_lld.data[i]) ++j; if(bitlen_lld.data[i] == 0 && j >= 2) /*repeat code for zeroes*/ { - j++; /*include the first zero*/ + ++j; /*include the first zero*/ if(j <= 10) /*repeat code 17 supports max 10 zeroes*/ { uivector_push_back(&bitlen_lld_e, 17); @@ -1797,7 +1833,7 @@ size_t k; unsigned num = j / 6, rest = j % 6; uivector_push_back(&bitlen_lld_e, bitlen_lld.data[i]); - for(k = 0; k < num; k++) + for(k = 0; k < num; ++k) { uivector_push_back(&bitlen_lld_e, 16); uivector_push_back(&bitlen_lld_e, 6 - 3); @@ -1819,12 +1855,12 @@ /*generate tree_cl, the huffmantree of huffmantrees*/ if(!uivector_resizev(&frequencies_cl, NUM_CODE_LENGTH_CODES, 0)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < bitlen_lld_e.size; i++) + for(i = 0; i != bitlen_lld_e.size; ++i) { - frequencies_cl.data[bitlen_lld_e.data[i]]++; + ++frequencies_cl.data[bitlen_lld_e.data[i]]; /*after a repeat code come the bits that specify the number of repetitions, those don't need to be in the frequencies_cl calculation*/ - if(bitlen_lld_e.data[i] >= 16) i++; + if(bitlen_lld_e.data[i] >= 16) ++i; } error = HuffmanTree_makeFromFrequencies(&tree_cl, frequencies_cl.data, @@ -1832,7 +1868,7 @@ if(error) break; if(!uivector_resize(&bitlen_cl, tree_cl.numcodes)) ERROR_BREAK(83 /*alloc fail*/); - for(i = 0; i < tree_cl.numcodes; i++) + for(i = 0; i != tree_cl.numcodes; ++i) { /*lenghts of code length tree is in the order as specified by deflate*/ bitlen_cl.data[i] = HuffmanTree_getLength(&tree_cl, CLCL_ORDER[i]); @@ -1868,16 +1904,16 @@ HDIST = (unsigned)(numcodes_d - 1); HCLEN = (unsigned)bitlen_cl.size - 4; /*trim zeroes for HCLEN. HLIT and HDIST were already trimmed at tree creation*/ - while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) HCLEN--; + while(!bitlen_cl.data[HCLEN + 4 - 1] && HCLEN > 0) --HCLEN; addBitsToStream(bp, out, HLIT, 5); addBitsToStream(bp, out, HDIST, 5); addBitsToStream(bp, out, HCLEN, 4); /*write the code lenghts of the code length alphabet*/ - for(i = 0; i < HCLEN + 4; i++) addBitsToStream(bp, out, bitlen_cl.data[i], 3); + for(i = 0; i != HCLEN + 4; ++i) addBitsToStream(bp, out, bitlen_cl.data[i], 3); /*write the lenghts of the lit/len AND the dist alphabet*/ - for(i = 0; i < bitlen_lld_e.size; i++) + for(i = 0; i != bitlen_lld_e.size; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_cl, bitlen_lld_e.data[i]), HuffmanTree_getLength(&tree_cl, bitlen_lld_e.data[i])); @@ -1916,7 +1952,7 @@ static unsigned deflateFixed(ucvector* out, size_t* bp, Hash* hash, const unsigned char* data, size_t datapos, size_t dataend, - const LodePNGCompressSettings* settings, int final) + const LodePNGCompressSettings* settings, unsigned final) { HuffmanTree tree_ll; /*tree for literal values and length codes*/ HuffmanTree tree_d; /*tree for distance codes*/ @@ -1946,7 +1982,7 @@ } else /*no LZ77, but still will be Huffman compressed*/ { - for(i = datapos; i < dataend; i++) + for(i = datapos; i < dataend; ++i) { addHuffmanSymbol(bp, out, HuffmanTree_getCode(&tree_ll, data[i]), HuffmanTree_getLength(&tree_ll, data[i])); } @@ -1974,8 +2010,10 @@ else if(settings->btype == 1) blocksize = insize; else /*if(settings->btype == 2)*/ { + /*on PNGs, deflate blocks of 65-262k seem to give most dense encoding*/ blocksize = insize / 8 + 8; - if(blocksize < 65535) blocksize = 65535; + if(blocksize < 65536) blocksize = 65536; + if(blocksize > 262144) blocksize = 262144; } numdeflateblocks = (insize + blocksize - 1) / blocksize; @@ -1984,9 +2022,9 @@ error = hash_init(&hash, settings->windowsize); if(error) return error; - for(i = 0; i < numdeflateblocks && !error; i++) + for(i = 0; i != numdeflateblocks && !error; ++i) { - int final = i == numdeflateblocks - 1; + unsigned final = (i == numdeflateblocks - 1); size_t start = i * blocksize; size_t end = start + blocksize; if(end > insize) end = insize; @@ -2047,7 +2085,7 @@ { s1 += (*data++); s2 += s1; - amount--; + --amount; } s1 %= 65521; s2 %= 65521; @@ -2141,7 +2179,6 @@ unsigned char* deflatedata = 0; size_t deflatesize = 0; - unsigned ADLER32; /*zlib data: 1 byte CMF (CM+CINFO), 1 byte FLG, deflate data, 4 byte ADLER32 checksum of the Decompressed data*/ unsigned CMF = 120; /*0b01111000: CM 8, CINFO 7. With CINFO 7, any window size up to 32768 can be used.*/ unsigned FLEVEL = 0; @@ -2153,15 +2190,15 @@ /*ucvector-controlled version of the output buffer, for dynamic array*/ ucvector_init_buffer(&outv, *out, *outsize); - ucvector_push_back(&outv, (unsigned char)(CMFFLG / 256)); - ucvector_push_back(&outv, (unsigned char)(CMFFLG % 256)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG >> 8)); + ucvector_push_back(&outv, (unsigned char)(CMFFLG & 255)); error = deflate(&deflatedata, &deflatesize, in, insize, settings); if(!error) { - ADLER32 = adler32(in, (unsigned)insize); - for(i = 0; i < deflatesize; i++) ucvector_push_back(&outv, deflatedata[i]); + unsigned ADLER32 = adler32(in, (unsigned)insize); + for(i = 0; i != deflatesize; ++i) ucvector_push_back(&outv, deflatedata[i]); lodepng_free(deflatedata); lodepng_add32bitInt(&outv, ADLER32); } @@ -2194,7 +2231,7 @@ static unsigned zlib_decompress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGDecompressSettings* settings) { - if (!settings->custom_zlib) return 87; /*no custom zlib function provided */ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2202,7 +2239,7 @@ static unsigned zlib_compress(unsigned char** out, size_t* outsize, const unsigned char* in, size_t insize, const LodePNGCompressSettings* settings) { - if (!settings->custom_zlib) return 87; /*no custom zlib function provided */ + if(!settings->custom_zlib) return 87; /*no custom zlib function provided */ return settings->custom_zlib(out, outsize, in, insize, settings); } #endif /*LODEPNG_COMPILE_ENCODER*/ @@ -2263,6 +2300,8 @@ /* / CRC32 / */ /* ////////////////////////////////////////////////////////////////////////// */ + +#ifndef LODEPNG_NO_COMPILE_CRC /* CRC polynomial: 0xedb88320 */ static unsigned lodepng_crc32_table[256] = { 0u, 1996959894u, 3993919788u, 2567524794u, 124634137u, 1886057615u, 3915621685u, 2657392035u, @@ -2300,17 +2339,19 @@ }; /*Return the CRC of the bytes buf[0..len-1].*/ -unsigned lodepng_crc32(const unsigned char* buf, size_t len) +unsigned lodepng_crc32(const unsigned char* data, size_t length) { - unsigned c = 0xffffffffL; - size_t n; - - for(n = 0; n < len; n++) + unsigned r = 0xffffffffu; + size_t i; + for(i = 0; i < length; ++i) { - c = lodepng_crc32_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + r = lodepng_crc32_table[(r ^ data[i]) & 0xff] ^ (r >> 8); } - return c ^ 0xffffffffL; + return r ^ 0xffffffffu; } +#else /* !LODEPNG_NO_COMPILE_CRC */ +unsigned lodepng_crc32(const unsigned char* data, size_t length); +#endif /* !LODEPNG_NO_COMPILE_CRC */ /* ////////////////////////////////////////////////////////////////////////// */ /* / Reading and writing single bits and bytes from/to stream for LodePNG / */ @@ -2319,7 +2360,7 @@ static unsigned char readBitFromReversedStream(size_t* bitpointer, const unsigned char* bitstream) { unsigned char result = (unsigned char)((bitstream[(*bitpointer) >> 3] >> (7 - ((*bitpointer) & 0x7))) & 1); - (*bitpointer)++; + ++(*bitpointer); return result; } @@ -2327,7 +2368,7 @@ { unsigned result = 0; size_t i; - for(i = nbits - 1; i < nbits; i--) + for(i = nbits - 1; i < nbits; --i) { result += (unsigned)readBitFromReversedStream(bitpointer, bitstream) << i; } @@ -2343,7 +2384,7 @@ /*earlier bit of huffman code is in a lesser significant bit of an earlier byte*/ bitstream[(*bitpointer) >> 3] |= (bit << (7 - ((*bitpointer) & 0x7))); } - (*bitpointer)++; + ++(*bitpointer); } #endif /*LODEPNG_COMPILE_DECODER*/ @@ -2352,7 +2393,7 @@ /*the current bit in bitstream may be 0 or 1 for this to work*/ if(bit == 0) bitstream[(*bitpointer) >> 3] &= (unsigned char)(~(1 << (7 - ((*bitpointer) & 0x7)))); else bitstream[(*bitpointer) >> 3] |= (1 << (7 - ((*bitpointer) & 0x7))); - (*bitpointer)++; + ++(*bitpointer); } /* ////////////////////////////////////////////////////////////////////////// */ @@ -2367,7 +2408,7 @@ void lodepng_chunk_type(char type[5], const unsigned char* chunk) { unsigned i; - for(i = 0; i < 4; i++) type[i] = chunk[4 + i]; + for(i = 0; i != 4; ++i) type[i] = (char)chunk[4 + i]; type[4] = 0; /*null termination char*/ } @@ -2445,7 +2486,7 @@ (*outlength) = new_length; chunk_start = &(*out)[new_length - total_chunk_length]; - for(i = 0; i < total_chunk_length; i++) chunk_start[i] = chunk[i]; + for(i = 0; i != total_chunk_length; ++i) chunk_start[i] = chunk[i]; return 0; } @@ -2467,13 +2508,13 @@ lodepng_set32bitInt(chunk, (unsigned)length); /*2: chunk name (4 letters)*/ - chunk[4] = type[0]; - chunk[5] = type[1]; - chunk[6] = type[2]; - chunk[7] = type[3]; + chunk[4] = (unsigned char)type[0]; + chunk[5] = (unsigned char)type[1]; + chunk[6] = (unsigned char)type[2]; + chunk[7] = (unsigned char)type[3]; /*3: the data*/ - for(i = 0; i < length; i++) chunk[8 + i] = data[i]; + for(i = 0; i != length; ++i) chunk[8 + i] = data[i]; /*4: CRC (of the chunkname characters and the data)*/ lodepng_chunk_generate_crc(chunk); @@ -2545,7 +2586,7 @@ { dest->palette = (unsigned char*)lodepng_malloc(1024); if(!dest->palette && source->palettesize) return 83; /*alloc fail*/ - for(i = 0; i < source->palettesize * 4; i++) dest->palette[i] = source->palette[i]; + for(i = 0; i != source->palettesize * 4; ++i) dest->palette[i] = source->palette[i]; } return 0; } @@ -2562,10 +2603,15 @@ if(a->key_g != b->key_g) return 0; if(a->key_b != b->key_b) return 0; } - if(a->palettesize != b->palettesize) return 0; - for(i = 0; i < a->palettesize * 4; i++) - { - if(a->palette[i] != b->palette[i]) return 0; + /*if one of the palette sizes is 0, then we consider it to be the same as the + other: it means that e.g. the palette was not given by the user and should be + considered the same as the palette inside the PNG.*/ + if(1/*a->palettesize != 0 && b->palettesize != 0*/) { + if(a->palettesize != b->palettesize) return 0; + for(i = 0; i != a->palettesize * 4; ++i) + { + if(a->palette[i] != b->palette[i]) return 0; + } } return 1; } @@ -2594,7 +2640,7 @@ info->palette[4 * info->palettesize + 1] = g; info->palette[4 * info->palettesize + 2] = b; info->palette[4 * info->palettesize + 3] = a; - info->palettesize++; + ++info->palettesize; return 0; } @@ -2627,7 +2673,7 @@ unsigned lodepng_has_palette_alpha(const LodePNGColorMode* info) { size_t i; - for(i = 0; i < info->palettesize; i++) + for(i = 0; i != info->palettesize; ++i) { if(info->palette[i * 4 + 3] < 255) return 1; } @@ -2643,27 +2689,47 @@ size_t lodepng_get_raw_size(unsigned w, unsigned h, const LodePNGColorMode* color) { - return (w * h * lodepng_get_bpp(color) + 7) / 8; + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } size_t lodepng_get_raw_size_lct(unsigned w, unsigned h, LodePNGColorType colortype, unsigned bitdepth) { - return (w * h * lodepng_get_bpp_lct(colortype, bitdepth) + 7) / 8; + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp_lct(colortype, bitdepth); + size_t n = w * h; + return ((n / 8) * bpp) + ((n & 7) * bpp + 7) / 8; } + +#ifdef LODEPNG_COMPILE_PNG +#ifdef LODEPNG_COMPILE_DECODER +/*in an idat chunk, each scanline is a multiple of 8 bits, unlike the lodepng output buffer*/ +static size_t lodepng_get_raw_size_idat(unsigned w, unsigned h, const LodePNGColorMode* color) +{ + /*will not overflow for any color type if roughly w * h < 268435455*/ + size_t bpp = lodepng_get_bpp(color); + size_t line = ((w / 8) * bpp) + ((w & 7) * bpp + 7) / 8; + return h * line; +} +#endif /*LODEPNG_COMPILE_DECODER*/ +#endif /*LODEPNG_COMPILE_PNG*/ + #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS static void LodePNGUnknownChunks_init(LodePNGInfo* info) { unsigned i; - for(i = 0; i < 3; i++) info->unknown_chunks_data[i] = 0; - for(i = 0; i < 3; i++) info->unknown_chunks_size[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_data[i] = 0; + for(i = 0; i != 3; ++i) info->unknown_chunks_size[i] = 0; } static void LodePNGUnknownChunks_cleanup(LodePNGInfo* info) { unsigned i; - for(i = 0; i < 3; i++) lodepng_free(info->unknown_chunks_data[i]); + for(i = 0; i != 3; ++i) lodepng_free(info->unknown_chunks_data[i]); } static unsigned LodePNGUnknownChunks_copy(LodePNGInfo* dest, const LodePNGInfo* src) @@ -2672,13 +2738,13 @@ LodePNGUnknownChunks_cleanup(dest); - for(i = 0; i < 3; i++) + for(i = 0; i != 3; ++i) { size_t j; dest->unknown_chunks_size[i] = src->unknown_chunks_size[i]; dest->unknown_chunks_data[i] = (unsigned char*)lodepng_malloc(src->unknown_chunks_size[i]); if(!dest->unknown_chunks_data[i] && dest->unknown_chunks_size[i]) return 83; /*alloc fail*/ - for(j = 0; j < src->unknown_chunks_size[i]; j++) + for(j = 0; j < src->unknown_chunks_size[i]; ++j) { dest->unknown_chunks_data[i][j] = src->unknown_chunks_data[i][j]; } @@ -2699,7 +2765,7 @@ static void LodePNGText_cleanup(LodePNGInfo* info) { size_t i; - for(i = 0; i < info->text_num; i++) + for(i = 0; i != info->text_num; ++i) { string_cleanup(&info->text_keys[i]); string_cleanup(&info->text_strings[i]); @@ -2714,7 +2780,7 @@ dest->text_keys = 0; dest->text_strings = 0; dest->text_num = 0; - for(i = 0; i < source->text_num; i++) + for(i = 0; i != source->text_num; ++i) { CERROR_TRY_RETURN(lodepng_add_text(dest, source->text_keys[i], source->text_strings[i])); } @@ -2737,7 +2803,7 @@ return 83; /*alloc fail*/ } - info->text_num++; + ++info->text_num; info->text_keys = new_keys; info->text_strings = new_strings; @@ -2764,7 +2830,7 @@ static void LodePNGIText_cleanup(LodePNGInfo* info) { size_t i; - for(i = 0; i < info->itext_num; i++) + for(i = 0; i != info->itext_num; ++i) { string_cleanup(&info->itext_keys[i]); string_cleanup(&info->itext_langtags[i]); @@ -2785,7 +2851,7 @@ dest->itext_transkeys = 0; dest->itext_strings = 0; dest->itext_num = 0; - for(i = 0; i < source->itext_num; i++) + for(i = 0; i != source->itext_num; ++i) { CERROR_TRY_RETURN(lodepng_add_itext(dest, source->itext_keys[i], source->itext_langtags[i], source->itext_transkeys[i], source->itext_strings[i])); @@ -2814,7 +2880,7 @@ return 83; /*alloc fail*/ } - info->itext_num++; + ++info->itext_num; info->itext_keys = new_keys; info->itext_langtags = new_langtags; info->itext_transkeys = new_transkeys; @@ -2899,7 +2965,7 @@ unsigned m = bits == 1 ? 7 : bits == 2 ? 3 : 1; /*8 / bits - 1*/ /*p = the partial index in the byte, e.g. with 4 palettebits it is 0 for first half or 1 for second half*/ unsigned p = index & m; - in &= (1 << bits) - 1; /*filter out any other bits of the input value*/ + in &= (1u << bits) - 1u; /*filter out any other bits of the input value*/ in = in << (bits * (m - p)); if(p == 0) out[index * bits / 8] = in; else out[index * bits / 8] |= in; @@ -2922,14 +2988,14 @@ static void color_tree_init(ColorTree* tree) { int i; - for(i = 0; i < 16; i++) tree->children[i] = 0; + for(i = 0; i != 16; ++i) tree->children[i] = 0; tree->index = -1; } static void color_tree_cleanup(ColorTree* tree) { int i; - for(i = 0; i < 16; i++) + for(i = 0; i != 16; ++i) { if(tree->children[i]) { @@ -2943,7 +3009,7 @@ static int color_tree_get(ColorTree* tree, unsigned char r, unsigned char g, unsigned char b, unsigned char a) { int bit = 0; - for(bit = 0; bit < 8; bit++) + for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) return -1; @@ -2962,10 +3028,10 @@ /*color is not allowed to already exist. Index should be >= 0 (it's signed to be compatible with using -1 for "doesn't exist")*/ static void color_tree_add(ColorTree* tree, - unsigned char r, unsigned char g, unsigned char b, unsigned char a, int index) + unsigned char r, unsigned char g, unsigned char b, unsigned char a, unsigned index) { int bit; - for(bit = 0; bit < 8; bit++) + for(bit = 0; bit < 8; ++bit) { int i = 8 * ((r >> bit) & 1) + 4 * ((g >> bit) & 1) + 2 * ((b >> bit) & 1) + 1 * ((a >> bit) & 1); if(!tree->children[i]) @@ -2975,7 +3041,7 @@ } tree = tree->children[i]; } - tree->index = index; + tree->index = (int)index; } /*put a pixel, given its RGBA color, into image of any color type*/ @@ -3015,7 +3081,7 @@ int index = color_tree_get(tree, r, g, b, a); if(index < 0) return 82; /*color not in palette*/ if(mode->bitdepth == 8) out[i] = index; - else addColorBits(out, i, mode->bitdepth, index); + else addColorBits(out, i, mode->bitdepth, (unsigned)index); } else if(mode->colortype == LCT_GREY_ALPHA) { @@ -3053,11 +3119,10 @@ } /*put a pixel, given its RGBA16 color, into image of any color 16-bitdepth type*/ -static unsigned rgba16ToPixel(unsigned char* out, size_t i, - const LodePNGColorMode* mode, - unsigned short r, unsigned short g, unsigned short b, unsigned short a) +static void rgba16ToPixel(unsigned char* out, size_t i, + const LodePNGColorMode* mode, + unsigned short r, unsigned short g, unsigned short b, unsigned short a) { - if(mode->bitdepth != 16) return 85; /*must be 16 for this function*/ if(mode->colortype == LCT_GREY) { unsigned short grey = r; /*((unsigned)r + g + b) / 3*/; @@ -3092,16 +3157,13 @@ out[i * 8 + 6] = (a >> 8) & 255; out[i * 8 + 7] = a & 255; } - - return 0; /*no error*/ } /*Get RGBA8 color of pixel with index i (y * width + x) from the raw image with given color type.*/ -static unsigned getPixelColorRGBA8(unsigned char* r, unsigned char* g, - unsigned char* b, unsigned char* a, - const unsigned char* in, size_t i, - const LodePNGColorMode* mode, - unsigned fix_png) +static void getPixelColorRGBA8(unsigned char* r, unsigned char* g, + unsigned char* b, unsigned char* a, + const unsigned char* in, size_t i, + const LodePNGColorMode* mode) { if(mode->colortype == LCT_GREY) { @@ -3158,8 +3220,8 @@ if(index >= mode->palettesize) { - /*This is an error according to the PNG spec, but fix_png can ignore it*/ - if(!fix_png) return (mode->bitdepth == 8 ? 46 : 47); /*index out of palette*/ + /*This is an error according to the PNG spec, but common PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ *r = *g = *b = 0; *a = 255; } @@ -3201,8 +3263,6 @@ *a = in[i * 8 + 6]; } } - - return 0; /*no error*/ } /*Similar to getPixelColorRGBA8, but with all the for loops inside of the color @@ -3210,10 +3270,9 @@ to RGBA or RGB with 8 bit per cannel. buffer must be RGBA or RGB output with enough memory, if has_alpha is true the output is RGBA. mode has the color mode of the input buffer.*/ -static unsigned getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, - unsigned has_alpha, const unsigned char* in, - const LodePNGColorMode* mode, - unsigned fix_png) +static void getPixelColorsRGBA8(unsigned char* buffer, size_t numpixels, + unsigned has_alpha, const unsigned char* in, + const LodePNGColorMode* mode) { unsigned num_channels = has_alpha ? 4 : 3; size_t i; @@ -3221,7 +3280,7 @@ { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i]; if(has_alpha) buffer[3] = mode->key_defined && in[i] == mode->key_r ? 0 : 255; @@ -3229,7 +3288,7 @@ } else if(mode->bitdepth == 16) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2]; if(has_alpha) buffer[3] = mode->key_defined && 256U * in[i * 2 + 0] + in[i * 2 + 1] == mode->key_r ? 0 : 255; @@ -3239,7 +3298,7 @@ { unsigned highest = ((1U << mode->bitdepth) - 1U); /*highest possible value for this bit depth*/ size_t j = 0; - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { unsigned value = readBitsFromReversedStream(&j, in, mode->bitdepth); buffer[0] = buffer[1] = buffer[2] = (value * 255) / highest; @@ -3251,7 +3310,7 @@ { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 3 + 0]; buffer[1] = in[i * 3 + 1]; @@ -3262,7 +3321,7 @@ } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 6 + 0]; buffer[1] = in[i * 6 + 2]; @@ -3278,15 +3337,15 @@ { unsigned index; size_t j = 0; - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { if(mode->bitdepth == 8) index = in[i]; else index = readBitsFromReversedStream(&j, in, mode->bitdepth); if(index >= mode->palettesize) { - /*This is an error according to the PNG spec, but fix_png can ignore it*/ - if(!fix_png) return (mode->bitdepth == 8 ? 46 : 47); /*index out of palette*/ + /*This is an error according to the PNG spec, but most PNG decoders make it black instead. + Done here too, slightly faster due to no error handling needed.*/ buffer[0] = buffer[1] = buffer[2] = 0; if(has_alpha) buffer[3] = 255; } @@ -3303,7 +3362,7 @@ { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 2 + 0]; if(has_alpha) buffer[3] = in[i * 2 + 1]; @@ -3311,7 +3370,7 @@ } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = buffer[1] = buffer[2] = in[i * 4 + 0]; if(has_alpha) buffer[3] = in[i * 4 + 2]; @@ -3322,7 +3381,7 @@ { if(mode->bitdepth == 8) { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 4 + 0]; buffer[1] = in[i * 4 + 1]; @@ -3332,7 +3391,7 @@ } else { - for(i = 0; i < numpixels; i++, buffer += num_channels) + for(i = 0; i != numpixels; ++i, buffer += num_channels) { buffer[0] = in[i * 8 + 0]; buffer[1] = in[i * 8 + 2]; @@ -3341,17 +3400,13 @@ } } } - - return 0; /*no error*/ } /*Get RGBA16 color of pixel with index i (y * width + x) from the raw image with given color type, but the given color type must be 16-bit itself.*/ -static unsigned getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, - const unsigned char* in, size_t i, const LodePNGColorMode* mode) +static void getPixelColorRGBA16(unsigned short* r, unsigned short* g, unsigned short* b, unsigned short* a, + const unsigned char* in, size_t i, const LodePNGColorMode* mode) { - if(mode->bitdepth != 16) return 85; /*error: this function only supports 16-bit input*/ - if(mode->colortype == LCT_GREY) { *r = *g = *b = 256 * in[i * 2 + 0] + in[i * 2 + 1]; @@ -3360,41 +3415,33 @@ } else if(mode->colortype == LCT_RGB) { - *r = 256 * in[i * 6 + 0] + in[i * 6 + 1]; - *g = 256 * in[i * 6 + 2] + in[i * 6 + 3]; - *b = 256 * in[i * 6 + 4] + in[i * 6 + 5]; - if(mode->key_defined && 256U * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r - && 256U * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g - && 256U * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; + *r = 256u * in[i * 6 + 0] + in[i * 6 + 1]; + *g = 256u * in[i * 6 + 2] + in[i * 6 + 3]; + *b = 256u * in[i * 6 + 4] + in[i * 6 + 5]; + if(mode->key_defined + && 256u * in[i * 6 + 0] + in[i * 6 + 1] == mode->key_r + && 256u * in[i * 6 + 2] + in[i * 6 + 3] == mode->key_g + && 256u * in[i * 6 + 4] + in[i * 6 + 5] == mode->key_b) *a = 0; else *a = 65535; } else if(mode->colortype == LCT_GREY_ALPHA) { - *r = *g = *b = 256 * in[i * 4 + 0] + in[i * 4 + 1]; - *a = 256 * in[i * 4 + 2] + in[i * 4 + 3]; + *r = *g = *b = 256u * in[i * 4 + 0] + in[i * 4 + 1]; + *a = 256u * in[i * 4 + 2] + in[i * 4 + 3]; } else if(mode->colortype == LCT_RGBA) { - *r = 256 * in[i * 8 + 0] + in[i * 8 + 1]; - *g = 256 * in[i * 8 + 2] + in[i * 8 + 3]; - *b = 256 * in[i * 8 + 4] + in[i * 8 + 5]; - *a = 256 * in[i * 8 + 6] + in[i * 8 + 7]; + *r = 256u * in[i * 8 + 0] + in[i * 8 + 1]; + *g = 256u * in[i * 8 + 2] + in[i * 8 + 3]; + *b = 256u * in[i * 8 + 4] + in[i * 8 + 5]; + *a = 256u * in[i * 8 + 6] + in[i * 8 + 7]; } - else return 85; /*error: this function only supports 16-bit input, not palettes*/ - - return 0; /*no error*/ } -/* -converts from any color type to 24-bit or 32-bit (later maybe more supported). return value = LodePNG error code -the out buffer must have (w * h * bpp + 7) / 8 bytes, where bpp is the bits per pixel of the output color type -(lodepng_get_bpp) for < 8 bpp images, there may _not_ be padding bits at the end of scanlines. -*/ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, - unsigned w, unsigned h, unsigned fix_png) + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h) { - unsigned error = 0; size_t i; ColorTree tree; size_t numpixels = w * h; @@ -3402,50 +3449,56 @@ if(lodepng_color_mode_equal(mode_out, mode_in)) { size_t numbytes = lodepng_get_raw_size(w, h, mode_in); - for(i = 0; i < numbytes; i++) out[i] = in[i]; - return error; + for(i = 0; i != numbytes; ++i) out[i] = in[i]; + return 0; } if(mode_out->colortype == LCT_PALETTE) { - size_t palsize = 1 << mode_out->bitdepth; - if(mode_out->palettesize < palsize) palsize = mode_out->palettesize; + size_t palettesize = mode_out->palettesize; + const unsigned char* palette = mode_out->palette; + size_t palsize = 1u << mode_out->bitdepth; + /*if the user specified output palette but did not give the values, assume + they want the values of the input color type (assuming that one is palette). + Note that we never create a new palette ourselves.*/ + if(palettesize == 0) + { + palettesize = mode_in->palettesize; + palette = mode_in->palette; + } + if(palettesize < palsize) palsize = palettesize; color_tree_init(&tree); - for(i = 0; i < palsize; i++) + for(i = 0; i != palsize; ++i) { - unsigned char* p = &mode_out->palette[i * 4]; + const unsigned char* p = &palette[i * 4]; color_tree_add(&tree, p[0], p[1], p[2], p[3], i); } } if(mode_in->bitdepth == 16 && mode_out->bitdepth == 16) { - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { unsigned short r = 0, g = 0, b = 0, a = 0; - error = getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); - if(error) break; - error = rgba16ToPixel(out, i, mode_out, r, g, b, a); - if(error) break; + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode_in); + rgba16ToPixel(out, i, mode_out, r, g, b, a); } } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGBA) { - error = getPixelColorsRGBA8(out, numpixels, 1, in, mode_in, fix_png); + getPixelColorsRGBA8(out, numpixels, 1, in, mode_in); } else if(mode_out->bitdepth == 8 && mode_out->colortype == LCT_RGB) { - error = getPixelColorsRGBA8(out, numpixels, 0, in, mode_in, fix_png); + getPixelColorsRGBA8(out, numpixels, 0, in, mode_in); } else { unsigned char r = 0, g = 0, b = 0, a = 0; - for(i = 0; i < numpixels; i++) + for(i = 0; i != numpixels; ++i) { - error = getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in, fix_png); - if(error) break; - error = rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a); - if(error) break; + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode_in); + CERROR_TRY_RETURN(rgba8ToPixel(out, i, mode_out, &tree, r, g, b, a)); } } @@ -3454,93 +3507,36 @@ color_tree_cleanup(&tree); } - return error; + return 0; /*no error*/ } #ifdef LODEPNG_COMPILE_ENCODER -typedef struct ColorProfile -{ - unsigned char sixteenbit; /*needs more than 8 bits per channel*/ - unsigned char sixteenbit_done; - - - unsigned char colored; /*not greyscale*/ - unsigned char colored_done; - - unsigned char key; /*a color key is required, or more*/ - unsigned short key_r; /*these values are always in 16-bit bitdepth in the profile*/ - unsigned short key_g; - unsigned short key_b; - unsigned char alpha; /*alpha channel, or alpha palette, required*/ - unsigned char alpha_done; - - unsigned numcolors; - ColorTree tree; /*for listing the counted colors, up to 256*/ - unsigned char* palette; /*size 1024. Remember up to the first 256 RGBA colors*/ - unsigned maxnumcolors; /*if more than that amount counted*/ - unsigned char numcolors_done; - - unsigned greybits; /*amount of bits required for greyscale (1, 2, 4, 8). Does not take 16 bit into account.*/ - unsigned char greybits_done; - -} ColorProfile; - -static void color_profile_init(ColorProfile* profile, const LodePNGColorMode* mode) +void lodepng_color_profile_init(LodePNGColorProfile* profile) { - profile->sixteenbit = 0; - profile->sixteenbit_done = mode->bitdepth == 16 ? 0 : 1; - profile->colored = 0; - profile->colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; - profile->key = 0; profile->alpha = 0; - profile->alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; - + profile->key_r = profile->key_g = profile->key_b = 0; profile->numcolors = 0; - color_tree_init(&profile->tree); - profile->palette = (unsigned char*)lodepng_malloc(1024); - profile->maxnumcolors = 257; - if(lodepng_get_bpp(mode) <= 8) - { - int bpp = lodepng_get_bpp(mode); - profile->maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); - } - profile->numcolors_done = 0; - - profile->greybits = 1; - profile->greybits_done = lodepng_get_bpp(mode) == 1 ? 1 : 0; -} - -static void color_profile_cleanup(ColorProfile* profile) -{ - color_tree_cleanup(&profile->tree); - lodepng_free(profile->palette); + profile->bits = 1; } /*function used for debug purposes with C++*/ -/*void printColorProfile(ColorProfile* p) +/*void printColorProfile(LodePNGColorProfile* p) { - std::cout << "sixteenbit: " << (int)p->sixteenbit << std::endl; - std::cout << "sixteenbit_done: " << (int)p->sixteenbit_done << std::endl; - std::cout << "colored: " << (int)p->colored << std::endl; - std::cout << "colored_done: " << (int)p->colored_done << std::endl; - std::cout << "key: " << (int)p->key << std::endl; - std::cout << "key_r: " << (int)p->key_r << std::endl; - std::cout << "key_g: " << (int)p->key_g << std::endl; - std::cout << "key_b: " << (int)p->key_b << std::endl; - std::cout << "alpha: " << (int)p->alpha << std::endl; - std::cout << "alpha_done: " << (int)p->alpha_done << std::endl; - std::cout << "numcolors: " << (int)p->numcolors << std::endl; - std::cout << "maxnumcolors: " << (int)p->maxnumcolors << std::endl; - std::cout << "numcolors_done: " << (int)p->numcolors_done << std::endl; - std::cout << "greybits: " << (int)p->greybits << std::endl; - std::cout << "greybits_done: " << (int)p->greybits_done << std::endl; + std::cout << "colored: " << (int)p->colored << ", "; + std::cout << "key: " << (int)p->key << ", "; + std::cout << "key_r: " << (int)p->key_r << ", "; + std::cout << "key_g: " << (int)p->key_g << ", "; + std::cout << "key_b: " << (int)p->key_b << ", "; + std::cout << "alpha: " << (int)p->alpha << ", "; + std::cout << "numcolors: " << (int)p->numcolors << ", "; + std::cout << "bits: " << (int)p->bits << std::endl; }*/ /*Returns how many bits needed to represent given value (max 8 bit)*/ -unsigned getValueRequiredBits(unsigned short value) +static unsigned getValueRequiredBits(unsigned char value) { if(value == 0 || value == 255) return 1; /*The scaling of 2-bit and 4-bit values uses multiples of 85 and 17*/ @@ -3550,326 +3546,258 @@ /*profile must already have been inited with mode. It's ok to set some parameters of profile to done already.*/ -static unsigned get_color_profile(ColorProfile* profile, - const unsigned char* in, - size_t numpixels /*must be full image size, for certain filesize based choices*/, - const LodePNGColorMode* mode, - unsigned fix_png) +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* in, unsigned w, unsigned h, + const LodePNGColorMode* mode) { unsigned error = 0; size_t i; + ColorTree tree; + size_t numpixels = w * h; + + unsigned colored_done = lodepng_is_greyscale_type(mode) ? 1 : 0; + unsigned alpha_done = lodepng_can_have_alpha(mode) ? 0 : 1; + unsigned numcolors_done = 0; + unsigned bpp = lodepng_get_bpp(mode); + unsigned bits_done = bpp == 1 ? 1 : 0; + unsigned maxnumcolors = 257; + unsigned sixteen = 0; + if(bpp <= 8) maxnumcolors = bpp == 1 ? 2 : (bpp == 2 ? 4 : (bpp == 4 ? 16 : 256)); + + color_tree_init(&tree); + /*Check if the 16-bit input is truly 16-bit*/ if(mode->bitdepth == 16) { - for(i = 0; i < numpixels; i++) + unsigned short r, g, b, a; + for(i = 0; i != numpixels; ++i) { - unsigned short r, g, b, a; - error = getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - if(error) break; - - /*a color is considered good for 8-bit if the first byte and the second byte are equal, - (so if it's divisible through 257), NOT necessarily if the second byte is 0*/ - if(!profile->sixteenbit_done - && (((r & 255) != ((r >> 8) & 255)) - || ((g & 255) != ((g >> 8) & 255)) - || ((b & 255) != ((b >> 8) & 255)))) - { - profile->sixteenbit = 1; - profile->sixteenbit_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore at 16-bit*/ - profile->numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if((r & 255) != ((r >> 8) & 255) || (g & 255) != ((g >> 8) & 255) || + (b & 255) != ((b >> 8) & 255) || (a & 255) != ((a >> 8) & 255)) /*first and second byte differ*/ + { + sixteen = 1; + break; } + } + } + + if(sixteen) + { + unsigned short r = 0, g = 0, b = 0, a = 0; + profile->bits = 16; + bits_done = numcolors_done = 1; /*counting colors no longer useful, palette doesn't support 16-bit*/ + + for(i = 0; i != numpixels; ++i) + { + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); - if(!profile->colored_done && (r != g || r != b)) + if(!colored_done && (r != g || r != b)) { profile->colored = 1; - profile->colored_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ + colored_done = 1; } - if(!profile->alpha_done && a != 65535) + if(!alpha_done) { - /*only use color key if numpixels large enough to justify tRNS chunk size*/ - if(a == 0 && numpixels > 16 && !(profile->key && (r != profile->key_r || g != profile->key_g || b != profile->key_b))) + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 65535 && (a != 0 || (profile->key && !matchkey))) { - if(!profile->alpha && !profile->key) - { - profile->key = 1; - profile->key_r = r; - profile->key_g = g; - profile->key_b = b; - } + profile->alpha = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } - else + else if(a == 0 && !profile->alpha && !profile->key) { - profile->alpha = 1; - profile->alpha_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; } - } - - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - if(!profile->alpha_done && a == 65535 && profile->key - && r == profile->key_r && g == profile->key_g && b == profile->key_b) - { + else if(a == 65535 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; - profile->alpha_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ - } - - if(!profile->greybits_done) - { - /*assuming 8-bit r, this test does not care about 16-bit*/ - unsigned bits = getValueRequiredBits(r); - if(bits > profile->greybits) profile->greybits = bits; - if(profile->greybits >= 8) profile->greybits_done = 1; + alpha_done = 1; + } } + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } - if(!profile->numcolors_done) + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) { - /*assuming 8-bit rgba, this test does not care about 16-bit*/ - if(!color_tree_has(&profile->tree, (unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a)) + getPixelColorRGBA16(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) { - color_tree_add(&profile->tree, (unsigned char)r, (unsigned char)g, (unsigned char)b, (unsigned char)a, - profile->numcolors); - if(profile->numcolors < 256) - { - unsigned char* p = profile->palette; - unsigned i = profile->numcolors; - p[i * 4 + 0] = (unsigned char)r; - p[i * 4 + 1] = (unsigned char)g; - p[i * 4 + 2] = (unsigned char)b; - p[i * 4 + 3] = (unsigned char)a; - } - profile->numcolors++; - if(profile->numcolors >= profile->maxnumcolors) profile->numcolors_done = 1; + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; } } - - if(profile->alpha_done && profile->numcolors_done - && profile->colored_done && profile->sixteenbit_done && profile->greybits_done) - { - break; - } - }; + } } else /* < 16-bit */ { - for(i = 0; i < numpixels; i++) + unsigned char r = 0, g = 0, b = 0, a = 0; + for(i = 0; i != numpixels; ++i) { - unsigned char r = 0, g = 0, b = 0, a = 0; - error = getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode, fix_png); - if(error) break; + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); - if(!profile->colored_done && (r != g || r != b)) + if(!bits_done && profile->bits < 8) + { + /*only r is checked, < 8 bits is only relevant for greyscale*/ + unsigned bits = getValueRequiredBits(r); + if(bits > profile->bits) profile->bits = bits; + } + bits_done = (profile->bits >= bpp); + + if(!colored_done && (r != g || r != b)) { profile->colored = 1; - profile->colored_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ + colored_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no colored modes with less than 8-bit per channel*/ } - if(!profile->alpha_done && a != 255) + if(!alpha_done) { - if(a == 0 && !(profile->key && (r != profile->key_r || g != profile->key_g || b != profile->key_b))) + unsigned matchkey = (r == profile->key_r && g == profile->key_g && b == profile->key_b); + if(a != 255 && (a != 0 || (profile->key && !matchkey))) { - if(!profile->key) - { - profile->key = 1; - profile->key_r = r; - profile->key_g = g; - profile->key_b = b; - } + profile->alpha = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } - else + else if(a == 0 && !profile->alpha && !profile->key) { - profile->alpha = 1; - profile->alpha_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ + profile->key = 1; + profile->key_r = r; + profile->key_g = g; + profile->key_b = b; } - } - - /* Color key cannot be used if an opaque pixel also has that RGB color. */ - if(!profile->alpha_done && a == 255 && profile->key - && r == profile->key_r && g == profile->key_g && b == profile->key_b) - { + else if(a == 255 && profile->key && matchkey) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ profile->alpha = 1; - profile->alpha_done = 1; - profile->greybits_done = 1; /*greybits is not applicable anymore*/ - } - - if(!profile->greybits_done) - { - unsigned bits = getValueRequiredBits(r); - if(bits > profile->greybits) profile->greybits = bits; - if(profile->greybits >= 8) profile->greybits_done = 1; + alpha_done = 1; + if(profile->bits < 8) profile->bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ + } } - if(!profile->numcolors_done) + if(!numcolors_done) { - if(!color_tree_has(&profile->tree, r, g, b, a)) + if(!color_tree_has(&tree, r, g, b, a)) { - - color_tree_add(&profile->tree, r, g, b, a, profile->numcolors); + color_tree_add(&tree, r, g, b, a, profile->numcolors); if(profile->numcolors < 256) { unsigned char* p = profile->palette; - unsigned i = profile->numcolors; - p[i * 4 + 0] = r; - p[i * 4 + 1] = g; - p[i * 4 + 2] = b; - p[i * 4 + 3] = a; + unsigned n = profile->numcolors; + p[n * 4 + 0] = r; + p[n * 4 + 1] = g; + p[n * 4 + 2] = b; + p[n * 4 + 3] = a; } - profile->numcolors++; - if(profile->numcolors >= profile->maxnumcolors) profile->numcolors_done = 1; + ++profile->numcolors; + numcolors_done = profile->numcolors >= maxnumcolors; } } - if(profile->alpha_done && profile->numcolors_done && profile->colored_done && profile->greybits_done) + if(alpha_done && numcolors_done && colored_done && bits_done) break; + } + + if(profile->key && !profile->alpha) + { + for(i = 0; i != numpixels; ++i) { - break; + getPixelColorRGBA8(&r, &g, &b, &a, in, i, mode); + if(a != 0 && r == profile->key_r && g == profile->key_g && b == profile->key_b) + { + /* Color key cannot be used if an opaque pixel also has that RGB color. */ + profile->alpha = 1; + alpha_done = 1; + } } - }; - } + } - /*make the profile's key always 16-bit for consistency*/ - if(mode->bitdepth < 16) - { - /*repeat each byte twice*/ - profile->key_r *= 257; - profile->key_g *= 257; - profile->key_b *= 257; + /*make the profile's key always 16-bit for consistency - repeat each byte twice*/ + profile->key_r += (profile->key_r << 8); + profile->key_g += (profile->key_g << 8); + profile->key_b += (profile->key_b << 8); } + color_tree_cleanup(&tree); return error; } -static void setColorKeyFrom16bit(LodePNGColorMode* mode_out, unsigned r, unsigned g, unsigned b, unsigned bitdepth) -{ - unsigned mask = (1 << bitdepth) - 1; - mode_out->key_defined = 1; - mode_out->key_r = r & mask; - mode_out->key_g = g & mask; - mode_out->key_b = b & mask; -} - -/*updates values of mode with a potentially smaller color model. mode_out should +/*Automatically chooses color type that gives smallest amount of bits in the +output image, e.g. grey if there are only greyscale pixels, palette if there +are less than 256 colors, ... +Updates values of mode with a potentially smaller color model. mode_out should contain the user chosen color model, but will be overwritten with the new chosen one.*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in, - LodePNGAutoConvert auto_convert) + const LodePNGColorMode* mode_in) { - ColorProfile profile; + LodePNGColorProfile prof; unsigned error = 0; - int no_nibbles = auto_convert == LAC_AUTO_NO_NIBBLES || auto_convert == LAC_AUTO_NO_NIBBLES_NO_PALETTE; - int no_palette = auto_convert == LAC_AUTO_NO_PALETTE || auto_convert == LAC_AUTO_NO_NIBBLES_NO_PALETTE; + unsigned i, n, palettebits, grey_ok, palette_ok; - if(auto_convert == LAC_ALPHA) - { - if(mode_out->colortype != LCT_RGBA && mode_out->colortype != LCT_GREY_ALPHA) return 0; - } + lodepng_color_profile_init(&prof); + error = lodepng_get_color_profile(&prof, image, w, h, mode_in); + if(error) return error; + mode_out->key_defined = 0; - color_profile_init(&profile, mode_in); - if(auto_convert == LAC_ALPHA) + if(prof.key && w * h <= 16) { - profile.colored_done = 1; - profile.greybits_done = 1; - profile.numcolors_done = 1; - profile.sixteenbit_done = 1; + prof.alpha = 1; /*too few pixels to justify tRNS chunk overhead*/ + if(prof.bits < 8) prof.bits = 8; /*PNG has no alphachannel modes with less than 8-bit per channel*/ } - error = get_color_profile(&profile, image, w * h, mode_in, 0 /*fix_png*/); - if(!error && auto_convert == LAC_ALPHA) + grey_ok = !prof.colored && !prof.alpha; /*grey without alpha, with potentially low bits*/ + n = prof.numcolors; + palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); + palette_ok = n <= 256 && (n * 2 < w * h) && prof.bits <= 8; + if(w * h < n * 2) palette_ok = 0; /*don't add palette overhead if image has only a few pixels*/ + if(grey_ok && prof.bits <= palettebits) palette_ok = 0; /*grey is less overhead*/ + + if(palette_ok) { - if(!profile.alpha) + unsigned char* p = prof.palette; + lodepng_palette_clear(mode_out); /*remove potential earlier palette*/ + for(i = 0; i != prof.numcolors; ++i) { - mode_out->colortype = (mode_out->colortype == LCT_RGBA ? LCT_RGB : LCT_GREY); - if(profile.key) setColorKeyFrom16bit(mode_out, profile.key_r, profile.key_g, profile.key_b, mode_out->bitdepth); + error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); + if(error) break; } - } - else if(!error && auto_convert != LAC_ALPHA) - { - mode_out->key_defined = 0; - if(profile.sixteenbit) - { - mode_out->bitdepth = 16; - if(profile.alpha) - { - mode_out->colortype = profile.colored ? LCT_RGBA : LCT_GREY_ALPHA; - } - else - { - mode_out->colortype = profile.colored ? LCT_RGB : LCT_GREY; - if(profile.key) setColorKeyFrom16bit(mode_out, profile.key_r, profile.key_g, profile.key_b, mode_out->bitdepth); - } - } - else /*less than 16 bits per channel*/ - { - /*don't add palette overhead if image hasn't got a lot of pixels*/ - unsigned n = profile.numcolors; - int palette_ok = !no_palette && n <= 256 && (n * 2 < w * h); - unsigned palettebits = n <= 2 ? 1 : (n <= 4 ? 2 : (n <= 16 ? 4 : 8)); - int grey_ok = !profile.colored && !profile.alpha; /*grey without alpha, with potentially low bits*/ - if(palette_ok || grey_ok) - { - if(!palette_ok || (grey_ok && profile.greybits <= palettebits)) - { - unsigned grey = profile.key_r; - mode_out->colortype = LCT_GREY; - mode_out->bitdepth = profile.greybits; - if(profile.key) setColorKeyFrom16bit(mode_out, grey, grey, grey, mode_out->bitdepth); - } - else - { - /*fill in the palette*/ - unsigned i; - unsigned char* p = profile.palette; - /*remove potential earlier palette*/ - lodepng_palette_clear(mode_out); - for(i = 0; i < profile.numcolors; i++) - { - error = lodepng_palette_add(mode_out, p[i * 4 + 0], p[i * 4 + 1], p[i * 4 + 2], p[i * 4 + 3]); - if(error) break; - } + mode_out->colortype = LCT_PALETTE; + mode_out->bitdepth = palettebits; - mode_out->colortype = LCT_PALETTE; - mode_out->bitdepth = palettebits; - } - } - else /*8-bit per channel*/ - { - mode_out->bitdepth = 8; - if(profile.alpha) - { - mode_out->colortype = profile.colored ? LCT_RGBA : LCT_GREY_ALPHA; - } - else - { - mode_out->colortype = profile.colored ? LCT_RGB : LCT_GREY /*LCT_GREY normally won't occur, already done earlier*/; - if(profile.key) setColorKeyFrom16bit(mode_out, profile.key_r, profile.key_g, profile.key_b, mode_out->bitdepth); - } - } + if(mode_in->colortype == LCT_PALETTE && mode_in->palettesize >= mode_out->palettesize + && mode_in->bitdepth == mode_out->bitdepth) + { + /*If input should have same palette colors, keep original to preserve its order and prevent conversion*/ + lodepng_color_mode_cleanup(mode_out); + lodepng_color_mode_copy(mode_out, mode_in); } } - - color_profile_cleanup(&profile); - - if(mode_out->colortype == LCT_PALETTE && mode_in->palettesize == mode_out->palettesize) + else /*8-bit or 16-bit per channel*/ { - /*In this case keep the palette order of the input, so that the user can choose an optimal one*/ - size_t i; - for(i = 0; i < mode_in->palettesize * 4; i++) + mode_out->bitdepth = prof.bits; + mode_out->colortype = prof.alpha ? (prof.colored ? LCT_RGBA : LCT_GREY_ALPHA) + : (prof.colored ? LCT_RGB : LCT_GREY); + + if(prof.key && !prof.alpha) { - mode_out->palette[i] = mode_in->palette[i]; + unsigned mask = (1u << mode_out->bitdepth) - 1u; /*profile always uses 16-bit, mask converts it*/ + mode_out->key_r = prof.key_r & mask; + mode_out->key_g = prof.key_g & mask; + mode_out->key_b = prof.key_b & mask; + mode_out->key_defined = 1; } } - if(no_nibbles && mode_out->bitdepth < 8) - { - /*palette can keep its small amount of colors, as long as no indices use it*/ - mode_out->bitdepth = 8; - } - return error; } @@ -3920,7 +3848,7 @@ unsigned i; /*calculate width and height in pixels of each pass*/ - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { passw[i] = (w + ADAM7_DX[i] - ADAM7_IX[i] - 1) / ADAM7_DX[i]; passh[i] = (h + ADAM7_DY[i] - ADAM7_IY[i] - 1) / ADAM7_DY[i]; @@ -3929,7 +3857,7 @@ } filter_passstart[0] = padded_passstart[0] = passstart[0] = 0; - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { /*if passw[i] is 0, it's 0 bytes, not 1 (no filtertype-byte)*/ filter_passstart[i + 1] = filter_passstart[i] @@ -3956,7 +3884,7 @@ { CERROR_RETURN_ERROR(state->error, 48); /*error: the given data is empty*/ } - if(insize < 29) + if(insize < 33) { CERROR_RETURN_ERROR(state->error, 27); /*error: the data length is smaller than the length of a PNG header*/ } @@ -3970,7 +3898,11 @@ { CERROR_RETURN_ERROR(state->error, 28); /*error: the first 8 bytes are not the correct PNG signature*/ } - if(in[12] != 'I' || in[13] != 'H' || in[14] != 'D' || in[15] != 'R') + if(lodepng_chunk_length(in + 8) != 13) + { + CERROR_RETURN_ERROR(state->error, 94); /*error: header size must be 13 bytes*/ + } + if(!lodepng_chunk_type_equals(in + 8, "IHDR")) { CERROR_RETURN_ERROR(state->error, 29); /*error: it doesn't start with a IHDR chunk!*/ } @@ -3984,6 +3916,11 @@ info->filter_method = in[27]; info->interlace_method = in[28]; + if(*w == 0 || *h == 0) + { + CERROR_RETURN_ERROR(state->error, 93); + } + if(!state->decoder.ignore_crc) { unsigned CRC = lodepng_read32bitInt(&in[29]); @@ -4021,53 +3958,53 @@ switch(filterType) { case 0: - for(i = 0; i < length; i++) recon[i] = scanline[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i]; break; case 1: - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth]; + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + recon[i - bytewidth]; break; case 2: if(precon) { - for(i = 0; i < length; i++) recon[i] = scanline[i] + precon[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i] + precon[i]; } else { - for(i = 0; i < length; i++) recon[i] = scanline[i]; + for(i = 0; i != length; ++i) recon[i] = scanline[i]; } break; case 3: if(precon) { - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i] + precon[i] / 2; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) / 2); + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i] + (precon[i] >> 1); + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + ((recon[i - bytewidth] + precon[i]) >> 1); } else { - for(i = 0; i < bytewidth; i++) recon[i] = scanline[i]; - for(i = bytewidth; i < length; i++) recon[i] = scanline[i] + recon[i - bytewidth] / 2; + for(i = 0; i != bytewidth; ++i) recon[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) recon[i] = scanline[i] + (recon[i - bytewidth] >> 1); } break; case 4: if(precon) { - for(i = 0; i < bytewidth; i++) + for(i = 0; i != bytewidth; ++i) { recon[i] = (scanline[i] + precon[i]); /*paethPredictor(0, precon[i], 0) is always precon[i]*/ } - for(i = bytewidth; i < length; i++) + for(i = bytewidth; i < length; ++i) { recon[i] = (scanline[i] + paethPredictor(recon[i - bytewidth], precon[i], precon[i - bytewidth])); } } else { - for(i = 0; i < bytewidth; i++) + for(i = 0; i != bytewidth; ++i) { recon[i] = scanline[i]; } - for(i = bytewidth; i < length; i++) + for(i = bytewidth; i < length; ++i) { /*paethPredictor(recon[i - bytewidth], 0, 0) is always recon[i - bytewidth]*/ recon[i] = (scanline[i] + recon[i - bytewidth]); @@ -4096,7 +4033,7 @@ size_t bytewidth = (bpp + 7) / 8; size_t linebytes = (w * bpp + 7) / 8; - for(y = 0; y < h; y++) + for(y = 0; y < h; ++y) { size_t outindex = linebytes * y; size_t inindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ @@ -4131,16 +4068,16 @@ if(bpp >= 8) { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = passstart[i] + (y * passw[i] + x) * bytewidth; size_t pixeloutstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; - for(b = 0; b < bytewidth; b++) + for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } @@ -4149,18 +4086,18 @@ } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { ibp = (8 * passstart[i]) + (y * ilinebits + x * bpp); obp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; - for(b = 0; b < bpp; b++) + for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); /*note that this function assumes the out buffer is completely 0, use setBitOfReversedStream otherwise*/ @@ -4186,10 +4123,10 @@ unsigned y; size_t diff = ilinebits - olinebits; size_t ibp = 0, obp = 0; /*input and output bit pointers*/ - for(y = 0; y < h; y++) + for(y = 0; y < h; ++y) { size_t x; - for(x = 0; x < olinebits; x++) + for(x = 0; x < olinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); @@ -4221,7 +4158,7 @@ CERROR_TRY_RETURN(unfilter(in, in, w, h, bpp)); removePaddingBits(out, in, w * bpp, ((w * bpp + 7) / 8) * 8, h); } - /*we can immediatly filter into the out buffer, no other steps needed*/ + /*we can immediately filter into the out buffer, no other steps needed*/ else CERROR_TRY_RETURN(unfilter(out, in, w, h, bpp)); } else /*interlace_method is 1 (Adam7)*/ @@ -4231,7 +4168,7 @@ Adam7_getpassvalues(passw, passh, filter_passstart, padded_passstart, passstart, w, h, bpp); - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { CERROR_TRY_RETURN(unfilter(&in[padded_passstart[i]], &in[filter_passstart[i]], passw[i], passh[i], bpp)); /*TODO: possible efficiency improvement: if in this reduced image the bits fit nicely in 1 scanline, @@ -4264,7 +4201,7 @@ } if(color->palettesize > 256) return 38; /*error: palette too big*/ - for(i = 0; i < color->palettesize; i++) + for(i = 0; i != color->palettesize; ++i) { color->palette[4 * i + 0] = data[pos++]; /*R*/ color->palette[4 * i + 1] = data[pos++]; /*G*/ @@ -4283,7 +4220,7 @@ /*error: more alpha values given than there are palette entries*/ if(chunkLength > color->palettesize) return 38; - for(i = 0; i < chunkLength; i++) color->palette[4 * i + 3] = data[i]; + for(i = 0; i != chunkLength; ++i) color->palette[4 * i + 3] = data[i]; } else if(color->colortype == LCT_GREY) { @@ -4291,7 +4228,7 @@ if(chunkLength != 2) return 30; color->key_defined = 1; - color->key_r = color->key_g = color->key_b = 256 * data[0] + data[1]; + color->key_r = color->key_g = color->key_b = 256u * data[0] + data[1]; } else if(color->colortype == LCT_RGB) { @@ -4299,9 +4236,9 @@ if(chunkLength != 6) return 41; color->key_defined = 1; - color->key_r = 256 * data[0] + data[1]; - color->key_g = 256 * data[2] + data[3]; - color->key_b = 256 * data[4] + data[5]; + color->key_r = 256u * data[0] + data[1]; + color->key_g = 256u * data[2] + data[3]; + color->key_b = 256u * data[4] + data[5]; } else return 42; /*error: tRNS chunk not allowed for other color models*/ @@ -4327,8 +4264,7 @@ if(chunkLength != 2) return 44; info->background_defined = 1; - info->background_r = info->background_g = info->background_b - = 256 * data[0] + data[1]; + info->background_r = info->background_g = info->background_b = 256u * data[0] + data[1]; } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { @@ -4336,9 +4272,9 @@ if(chunkLength != 6) return 45; info->background_defined = 1; - info->background_r = 256 * data[0] + data[1]; - info->background_g = 256 * data[2] + data[3]; - info->background_b = 256 * data[4] + data[5]; + info->background_r = 256u * data[0] + data[1]; + info->background_g = 256u * data[2] + data[3]; + info->background_b = 256u * data[4] + data[5]; } return 0; /* OK */ @@ -4356,7 +4292,7 @@ unsigned length, string2_begin; length = 0; - while(length < chunkLength && data[length] != 0) length++; + while(length < chunkLength && data[length] != 0) ++length; /*even though it's not allowed by the standard, no error is thrown if there's no null termination char, if the text is empty*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4365,7 +4301,7 @@ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; string2_begin = length + 1; /*skip keyword null terminator*/ @@ -4374,7 +4310,7 @@ if(!str) CERROR_BREAK(error, 83); /*alloc fail*/ str[length] = 0; - for(i = 0; i < length; i++) str[i] = data[string2_begin + i]; + for(i = 0; i != length; ++i) str[i] = (char)data[string2_begin + i]; error = lodepng_add_text(info, key, str); @@ -4402,7 +4338,7 @@ while(!error) /*not really a while loop, only used to break on error*/ { - for(length = 0; length < chunkLength && data[length] != 0; length++) ; + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 2 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4410,7 +4346,7 @@ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; if(data[length + 1] != 0) CERROR_BREAK(error, 72); /*the 0 byte indicating compression must be 0*/ @@ -4455,7 +4391,7 @@ if(chunkLength < 5) CERROR_BREAK(error, 30); /*iTXt chunk too short*/ /*read the key*/ - for(length = 0; length < chunkLength && data[length] != 0; length++) ; + for(length = 0; length < chunkLength && data[length] != 0; ++length) ; if(length + 3 >= chunkLength) CERROR_BREAK(error, 75); /*no null termination char, corrupt?*/ if(length < 1 || length > 79) CERROR_BREAK(error, 89); /*keyword too short or long*/ @@ -4463,7 +4399,7 @@ if(!key) CERROR_BREAK(error, 83); /*alloc fail*/ key[length] = 0; - for(i = 0; i < length; i++) key[i] = data[i]; + for(i = 0; i != length; ++i) key[i] = (char)data[i]; /*read the compression method*/ compressed = data[length + 1]; @@ -4475,24 +4411,24 @@ /*read the langtag*/ begin = length + 3; length = 0; - for(i = begin; i < chunkLength && data[i] != 0; i++) length++; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; langtag = (char*)lodepng_malloc(length + 1); if(!langtag) CERROR_BREAK(error, 83); /*alloc fail*/ langtag[length] = 0; - for(i = 0; i < length; i++) langtag[i] = data[begin + i]; + for(i = 0; i != length; ++i) langtag[i] = (char)data[begin + i]; /*read the transkey*/ begin += length + 1; length = 0; - for(i = begin; i < chunkLength && data[i] != 0; i++) length++; + for(i = begin; i < chunkLength && data[i] != 0; ++i) ++length; transkey = (char*)lodepng_malloc(length + 1); if(!transkey) CERROR_BREAK(error, 83); /*alloc fail*/ transkey[length] = 0; - for(i = 0; i < length; i++) transkey[i] = data[begin + i]; + for(i = 0; i != length; ++i) transkey[i] = (char)data[begin + i]; /*read the actual text*/ begin += length + 1; @@ -4514,7 +4450,7 @@ if(!ucvector_resize(&decoded, length + 1)) CERROR_BREAK(error, 83 /*alloc fail*/); decoded.data[length] = 0; - for(i = 0; i < length; i++) decoded.data[i] = data[begin + i]; + for(i = 0; i != length; ++i) decoded.data[i] = data[begin + i]; } error = lodepng_add_itext(info, key, langtag, transkey, (char*)decoded.data); @@ -4535,7 +4471,7 @@ if(chunkLength != 7) return 73; /*invalid tIME chunk size*/ info->time_defined = 1; - info->time.year = 256 * data[0] + data[+ 1]; + info->time.year = 256u * data[0] + data[1]; info->time.month = data[2]; info->time.day = data[3]; info->time.hour = data[4]; @@ -4550,8 +4486,8 @@ if(chunkLength != 9) return 74; /*invalid pHYs chunk size*/ info->phys_defined = 1; - info->phys_x = 16777216 * data[0] + 65536 * data[1] + 256 * data[2] + data[3]; - info->phys_y = 16777216 * data[4] + 65536 * data[5] + 256 * data[6] + data[7]; + info->phys_x = 16777216u * data[0] + 65536u * data[1] + 256u * data[2] + data[3]; + info->phys_y = 16777216u * data[4] + 65536u * data[5] + 256u * data[6] + data[7]; info->phys_unit = data[8]; return 0; /* OK */ @@ -4568,6 +4504,9 @@ size_t i; ucvector idat; /*the data from idat chunks*/ ucvector scanlines; + size_t predict; + size_t numpixels; + size_t outsize; /*for unknown chunk order*/ unsigned unknown = 0; @@ -4581,6 +4520,14 @@ state->error = lodepng_inspect(w, h, state, in, insize); /*reads header and resets other parameters in state->info_png*/ if(state->error) return; + numpixels = *w * *h; + + /*multiplication overflow*/ + if(*h != 0 && numpixels / *h != *w) CERROR_RETURN(state->error, 92); + /*multiplication overflow possible further below. Allows up to 2^31-1 pixel + bytes with 16-bit RGBA, the rest is room for filter bytes.*/ + if(numpixels > 268435455) CERROR_RETURN(state->error, 92); + ucvector_init(&idat); chunk = &in[33]; /*first byte of the first chunk after the header*/ @@ -4611,7 +4558,7 @@ { size_t oldsize = idat.size; if(!ucvector_resize(&idat, oldsize + chunkLength)) CERROR_BREAK(state->error, 83 /*alloc fail*/); - for(i = 0; i < chunkLength; i++) idat.data[oldsize + i] = data[i]; + for(i = 0; i != chunkLength; ++i) idat.data[oldsize + i] = data[i]; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS critical_pos = 3; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ @@ -4706,30 +4653,45 @@ } ucvector_init(&scanlines); - if(!state->error) + /*predict output size, to allocate exact size for output buffer to avoid more dynamic allocation. + If the decompressed size does not match the prediction, the image must be corrupt.*/ + if(state->info_png.interlace_method == 0) { - /*maximum final image length is already reserved in the vector's length - this is not really necessary*/ - if(!ucvector_resize(&scanlines, lodepng_get_raw_size(*w, *h, &state->info_png.color) + *h)) - { - state->error = 83; /*alloc fail*/ - } + /*The extra *h is added because this are the filter bytes every scanline starts with*/ + predict = lodepng_get_raw_size_idat(*w, *h, &state->info_png.color) + *h; + } + else + { + /*Adam-7 interlaced: predicted size is the sum of the 7 sub-images sizes*/ + const LodePNGColorMode* color = &state->info_png.color; + predict = 0; + predict += lodepng_get_raw_size_idat((*w + 7) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + if(*w > 4) predict += lodepng_get_raw_size_idat((*w + 3) >> 3, (*h + 7) >> 3, color) + ((*h + 7) >> 3); + predict += lodepng_get_raw_size_idat((*w + 3) >> 2, (*h + 3) >> 3, color) + ((*h + 3) >> 3); + if(*w > 2) predict += lodepng_get_raw_size_idat((*w + 1) >> 2, (*h + 3) >> 2, color) + ((*h + 3) >> 2); + predict += lodepng_get_raw_size_idat((*w + 1) >> 1, (*h + 1) >> 2, color) + ((*h + 1) >> 2); + if(*w > 1) predict += lodepng_get_raw_size_idat((*w + 0) >> 1, (*h + 1) >> 1, color) + ((*h + 1) >> 1); + predict += lodepng_get_raw_size_idat((*w + 0), (*h + 0) >> 1, color) + ((*h + 0) >> 1); } + if(!state->error && !ucvector_reserve(&scanlines, predict)) state->error = 83; /*alloc fail*/ if(!state->error) { - /*decompress with the Zlib decompressor*/ state->error = zlib_decompress(&scanlines.data, &scanlines.size, idat.data, idat.size, &state->decoder.zlibsettings); + if(!state->error && scanlines.size != predict) state->error = 91; /*decompressed size doesn't match prediction*/ } ucvector_cleanup(&idat); if(!state->error) { - ucvector outv; - ucvector_init(&outv); - if(!ucvector_resizev(&outv, - lodepng_get_raw_size(*w, *h, &state->info_png.color), 0)) state->error = 83; /*alloc fail*/ - if(!state->error) state->error = postProcessScanlines(outv.data, scanlines.data, *w, *h, &state->info_png); - *out = outv.data; + outsize = lodepng_get_raw_size(*w, *h, &state->info_png.color); + *out = (unsigned char*)lodepng_malloc(outsize); + if(!*out) state->error = 83; /*alloc fail*/ + } + if(!state->error) + { + for(i = 0; i < outsize; i++) (*out)[i] = 0; + state->error = postProcessScanlines(*out, scanlines.data, *w, *h, &state->info_png); } ucvector_cleanup(&scanlines); } @@ -4772,7 +4734,8 @@ { state->error = 83; /*alloc fail*/ } - else state->error = lodepng_convert(*out, data, &state->info_raw, &state->info_png.color, *w, *h, state->decoder.fix_png); + else state->error = lodepng_convert(*out, data, &state->info_raw, + &state->info_png.color, *w, *h); lodepng_free(data); } return state->error; @@ -4805,7 +4768,7 @@ unsigned lodepng_decode_file(unsigned char** out, unsigned* w, unsigned* h, const char* filename, LodePNGColorType colortype, unsigned bitdepth) { - unsigned char* buffer; + unsigned char* buffer = 0; size_t buffersize; unsigned error; error = lodepng_load_file(&buffer, &buffersize, filename); @@ -4833,7 +4796,6 @@ settings->remember_unknown_chunks = 0; #endif /*LODEPNG_COMPILE_ANCILLARY_CHUNKS*/ settings->ignore_crc = 0; - settings->fix_png = 0; lodepng_decompress_settings_init(&settings->zlibsettings); } @@ -4926,7 +4888,7 @@ size_t i; ucvector PLTE; ucvector_init(&PLTE); - for(i = 0; i < info->palettesize * 4; i++) + for(i = 0; i != info->palettesize * 4; ++i) { /*add all channels except alpha channel*/ if(i % 4 != 3) ucvector_push_back(&PLTE, info->palette[i]); @@ -4947,32 +4909,32 @@ { size_t amount = info->palettesize; /*the tail of palette values that all have 255 as alpha, does not have to be encoded*/ - for(i = info->palettesize; i > 0; i--) + for(i = info->palettesize; i != 0; --i) { - if(info->palette[4 * (i - 1) + 3] == 255) amount--; + if(info->palette[4 * (i - 1) + 3] == 255) --amount; else break; } /*add only alpha channel*/ - for(i = 0; i < amount; i++) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); + for(i = 0; i != amount; ++i) ucvector_push_back(&tRNS, info->palette[4 * i + 3]); } else if(info->colortype == LCT_GREY) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); } } else if(info->colortype == LCT_RGB) { if(info->key_defined) { - ucvector_push_back(&tRNS, (unsigned char)(info->key_r / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_r % 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_g % 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b / 256)); - ucvector_push_back(&tRNS, (unsigned char)(info->key_b % 256)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_r & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_g & 255)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b >> 8)); + ucvector_push_back(&tRNS, (unsigned char)(info->key_b & 255)); } } @@ -5012,10 +4974,10 @@ size_t i; ucvector text; ucvector_init(&text); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&text, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&text, 0); /*0 termination char*/ - for(i = 0; textstring[i] != 0; i++) ucvector_push_back(&text, (unsigned char)textstring[i]); + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&text, (unsigned char)textstring[i]); error = addChunk(out, "tEXt", text.data, text.size); ucvector_cleanup(&text); @@ -5031,7 +4993,7 @@ ucvector_init(&data); ucvector_init(&compressed); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&data, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*0 termination char*/ ucvector_push_back(&data, 0); /*compression method: 0*/ @@ -5040,7 +5002,7 @@ (unsigned char*)textstring, textsize, zlibsettings); if(!error) { - for(i = 0; i < compressed.size; i++) ucvector_push_back(&data, compressed.data[i]); + for(i = 0; i != compressed.size; ++i) ucvector_push_back(&data, compressed.data[i]); error = addChunk(out, "zTXt", data.data, data.size); } @@ -5058,14 +5020,14 @@ ucvector_init(&data); - for(i = 0; keyword[i] != 0; i++) ucvector_push_back(&data, (unsigned char)keyword[i]); + for(i = 0; keyword[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)keyword[i]); if(i < 1 || i > 79) return 89; /*error: invalid keyword size*/ ucvector_push_back(&data, 0); /*null termination char*/ ucvector_push_back(&data, compressed ? 1 : 0); /*compression flag*/ ucvector_push_back(&data, 0); /*compression method*/ - for(i = 0; langtag[i] != 0; i++) ucvector_push_back(&data, (unsigned char)langtag[i]); + for(i = 0; langtag[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)langtag[i]); ucvector_push_back(&data, 0); /*null termination char*/ - for(i = 0; transkey[i] != 0; i++) ucvector_push_back(&data, (unsigned char)transkey[i]); + for(i = 0; transkey[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)transkey[i]); ucvector_push_back(&data, 0); /*null termination char*/ if(compressed) @@ -5076,13 +5038,13 @@ (unsigned char*)textstring, textsize, zlibsettings); if(!error) { - for(i = 0; i < compressed_data.size; i++) ucvector_push_back(&data, compressed_data.data[i]); + for(i = 0; i != compressed_data.size; ++i) ucvector_push_back(&data, compressed_data.data[i]); } ucvector_cleanup(&compressed_data); } else /*not compressed*/ { - for(i = 0; textstring[i] != 0; i++) ucvector_push_back(&data, (unsigned char)textstring[i]); + for(i = 0; textstring[i] != 0; ++i) ucvector_push_back(&data, (unsigned char)textstring[i]); } if(!error) error = addChunk(out, "iTXt", data.data, data.size); @@ -5097,21 +5059,21 @@ ucvector_init(&bKGD); if(info->color.colortype == LCT_GREY || info->color.colortype == LCT_GREY_ALPHA) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); } else if(info->color.colortype == LCT_RGB || info->color.colortype == LCT_RGBA) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_g % 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b / 256)); - ucvector_push_back(&bKGD, (unsigned char)(info->background_b % 256)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_g & 255)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b >> 8)); + ucvector_push_back(&bKGD, (unsigned char)(info->background_b & 255)); } else if(info->color.colortype == LCT_PALETTE) { - ucvector_push_back(&bKGD, (unsigned char)(info->background_r % 256)); /*palette index*/ + ucvector_push_back(&bKGD, (unsigned char)(info->background_r & 255)); /*palette index*/ } error = addChunk(out, "bKGD", bKGD.data, bKGD.size); @@ -5125,13 +5087,13 @@ unsigned error = 0; unsigned char* data = (unsigned char*)lodepng_malloc(7); if(!data) return 83; /*alloc fail*/ - data[0] = (unsigned char)(time->year / 256); - data[1] = (unsigned char)(time->year % 256); - data[2] = time->month; - data[3] = time->day; - data[4] = time->hour; - data[5] = time->minute; - data[6] = time->second; + data[0] = (unsigned char)(time->year >> 8); + data[1] = (unsigned char)(time->year & 255); + data[2] = (unsigned char)time->month; + data[3] = (unsigned char)time->day; + data[4] = (unsigned char)time->hour; + data[5] = (unsigned char)time->minute; + data[6] = (unsigned char)time->second; error = addChunk(out, "tIME", data, 7); lodepng_free(data); return error; @@ -5162,49 +5124,49 @@ switch(filterType) { case 0: /*None*/ - for(i = 0; i < length; i++) out[i] = scanline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i]; break; case 1: /*Sub*/ - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth]; + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - scanline[i - bytewidth]; break; case 2: /*Up*/ if(prevline) { - for(i = 0; i < length; i++) out[i] = scanline[i] - prevline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i] - prevline[i]; } else { - for(i = 0; i < length; i++) out[i] = scanline[i]; + for(i = 0; i != length; ++i) out[i] = scanline[i]; } break; case 3: /*Average*/ if(prevline) { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i] - prevline[i] / 2; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) / 2); + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i] - (prevline[i] >> 1); + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - ((scanline[i - bytewidth] + prevline[i]) >> 1); } else { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; - for(i = bytewidth; i < length; i++) out[i] = scanline[i] - scanline[i - bytewidth] / 2; + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; + for(i = bytewidth; i < length; ++i) out[i] = scanline[i] - (scanline[i - bytewidth] >> 1); } break; case 4: /*Paeth*/ if(prevline) { /*paethPredictor(0, prevline[i], 0) is always prevline[i]*/ - for(i = 0; i < bytewidth; i++) out[i] = (scanline[i] - prevline[i]); - for(i = bytewidth; i < length; i++) + for(i = 0; i != bytewidth; ++i) out[i] = (scanline[i] - prevline[i]); + for(i = bytewidth; i < length; ++i) { out[i] = (scanline[i] - paethPredictor(scanline[i - bytewidth], prevline[i], prevline[i - bytewidth])); } } else { - for(i = 0; i < bytewidth; i++) out[i] = scanline[i]; + for(i = 0; i != bytewidth; ++i) out[i] = scanline[i]; /*paethPredictor(scanline[i - bytewidth], 0, 0) is always scanline[i - bytewidth]*/ - for(i = bytewidth; i < length; i++) out[i] = (scanline[i] - scanline[i - bytewidth]); + for(i = bytewidth; i < length; ++i) out[i] = (scanline[i] - scanline[i - bytewidth]); } break; default: return; /*unexisting filter type given*/ @@ -5216,7 +5178,7 @@ { float result = 0; while(f > 32) { result += 4; f /= 16; } - while(f > 2) { result++; f /= 2; } + while(f > 2) { ++result; f /= 2; } return result + 1.442695f * (f * f * f / 3 - 3 * f * f / 2 + 3 * f - 1.83333f); } @@ -5259,7 +5221,7 @@ if(strategy == LFS_ZERO) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; @@ -5272,40 +5234,40 @@ { /*adaptive filtering*/ size_t sum[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; - unsigned type, bestType = 0; + unsigned char type, bestType = 0; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - if(!ucvector_resize(&attempt[type], linebytes)) return 83; /*alloc fail*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } if(!error) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { /*try the 5 filter types*/ - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); /*calculate the sum of the result*/ sum[type] = 0; if(type == 0) { - for(x = 0; x < linebytes; x++) sum[type] += (unsigned char)(attempt[type].data[x]); + for(x = 0; x != linebytes; ++x) sum[type] += (unsigned char)(attempt[type][x]); } else { - for(x = 0; x < linebytes; x++) + for(x = 0; x != linebytes; ++x) { /*For differences, each byte should be treated as signed, values above 127 are negative (converted to signed char). Filtertype 0 isn't a difference though, so use unsigned there. This means filtertype 0 is almost never chosen, but that is justified.*/ - signed char s = (signed char)(attempt[type].data[x]); - sum[type] += s < 0 ? -s : s; + unsigned char s = attempt[type][x]; + sum[type] += s < 128 ? s : (255U - s); } } @@ -5321,37 +5283,37 @@ /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_ENTROPY) { float sum[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ float smallest = 0; unsigned type, bestType = 0; unsigned count[256]; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - if(!ucvector_resize(&attempt[type], linebytes)) return 83; /*alloc fail*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { /*try the 5 filter types*/ - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); - for(x = 0; x < 256; x++) count[x] = 0; - for(x = 0; x < linebytes; x++) count[attempt[type].data[x]]++; - count[type]++; /*the filter type itself is part of the scanline*/ + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); + for(x = 0; x != 256; ++x) count[x] = 0; + for(x = 0; x != linebytes; ++x) ++count[attempt[type][x]]; + ++count[type]; /*the filter type itself is part of the scanline*/ sum[type] = 0; - for(x = 0; x < 256; x++) + for(x = 0; x != 256; ++x) { float p = count[x] / (float)(linebytes + 1); sum[type] += count[x] == 0 ? 0 : flog2(1 / p) * p; @@ -5368,18 +5330,18 @@ /*now fill the out values*/ out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else if(strategy == LFS_PREDEFINED) { - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t outindex = (1 + linebytes) * y; /*the extra filterbyte added to each row*/ size_t inindex = linebytes * y; - unsigned type = settings->predefined_filters[y]; + unsigned char type = settings->predefined_filters[y]; out[outindex] = type; /*filter type byte*/ filterScanline(&out[outindex + 1], &in[inindex], prevline, linebytes, bytewidth, type); prevline = &in[inindex]; @@ -5391,7 +5353,7 @@ deflate the scanline after every filter attempt to see which one deflates best. This is very slow and gives only slightly smaller, sometimes even larger, result*/ size_t size[5]; - ucvector attempt[5]; /*five filtering attempts, one for each filter type*/ + unsigned char* attempt[5]; /*five filtering attempts, one for each filter type*/ size_t smallest = 0; unsigned type = 0, bestType = 0; unsigned char* dummy; @@ -5405,22 +5367,22 @@ images only, so disable it*/ zlibsettings.custom_zlib = 0; zlibsettings.custom_deflate = 0; - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - ucvector_init(&attempt[type]); - ucvector_resize(&attempt[type], linebytes); /*todo: give error if resize failed*/ + attempt[type] = (unsigned char*)lodepng_malloc(linebytes); + if(!attempt[type]) return 83; /*alloc fail*/ } - for(y = 0; y < h; y++) /*try the 5 filter types*/ + for(y = 0; y != h; ++y) /*try the 5 filter types*/ { - for(type = 0; type < 5; type++) + for(type = 0; type != 5; ++type) { - unsigned testsize = attempt[type].size; + unsigned testsize = linebytes; /*if(testsize > 8) testsize /= 8;*/ /*it already works good enough by testing a part of the row*/ - filterScanline(attempt[type].data, &in[y * linebytes], prevline, linebytes, bytewidth, type); + filterScanline(attempt[type], &in[y * linebytes], prevline, linebytes, bytewidth, type); size[type] = 0; dummy = 0; - zlib_compress(&dummy, &size[type], attempt[type].data, testsize, &zlibsettings); + zlib_compress(&dummy, &size[type], attempt[type], testsize, &zlibsettings); lodepng_free(dummy); /*check if this is smallest size (or if type == 0 it's the first case so always store the values)*/ if(type == 0 || size[type] < smallest) @@ -5431,9 +5393,9 @@ } prevline = &in[y * linebytes]; out[y * (linebytes + 1)] = bestType; /*the first byte of a scanline will be the filter type*/ - for(x = 0; x < linebytes; x++) out[y * (linebytes + 1) + 1 + x] = attempt[bestType].data[x]; + for(x = 0; x != linebytes; ++x) out[y * (linebytes + 1) + 1 + x] = attempt[bestType][x]; } - for(type = 0; type < 5; type++) ucvector_cleanup(&attempt[type]); + for(type = 0; type != 5; ++type) lodepng_free(attempt[type]); } else return 88; /* unknown filter strategy */ @@ -5448,17 +5410,17 @@ unsigned y; size_t diff = olinebits - ilinebits; size_t obp = 0, ibp = 0; /*bit pointers*/ - for(y = 0; y < h; y++) + for(y = 0; y != h; ++y) { size_t x; - for(x = 0; x < ilinebits; x++) + for(x = 0; x < ilinebits; ++x) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); } /*obp += diff; --> no, fill in some value in the padding bits too, to avoid "Use of uninitialised value of size ###" warning from valgrind*/ - for(x = 0; x < diff; x++) setBitOfReversedStream(&obp, out, 0); + for(x = 0; x != diff; ++x) setBitOfReversedStream(&obp, out, 0); } } @@ -5483,16 +5445,16 @@ if(bpp >= 8) { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; size_t bytewidth = bpp / 8; - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { size_t pixelinstart = ((ADAM7_IY[i] + y * ADAM7_DY[i]) * w + ADAM7_IX[i] + x * ADAM7_DX[i]) * bytewidth; size_t pixeloutstart = passstart[i] + (y * passw[i] + x) * bytewidth; - for(b = 0; b < bytewidth; b++) + for(b = 0; b < bytewidth; ++b) { out[pixeloutstart + b] = in[pixelinstart + b]; } @@ -5501,18 +5463,18 @@ } else /*bpp < 8: Adam7 with pixels < 8 bit is a bit trickier: with bit pointers*/ { - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { unsigned x, y, b; unsigned ilinebits = bpp * passw[i]; unsigned olinebits = bpp * w; size_t obp, ibp; /*bit pointers (for out and in buffer)*/ - for(y = 0; y < passh[i]; y++) - for(x = 0; x < passw[i]; x++) + for(y = 0; y < passh[i]; ++y) + for(x = 0; x < passw[i]; ++x) { ibp = (ADAM7_IY[i] + y * ADAM7_DY[i]) * olinebits + (ADAM7_IX[i] + x * ADAM7_DX[i]) * bpp; obp = (8 * passstart[i]) + (y * ilinebits + x * bpp); - for(b = 0; b < bpp; b++) + for(b = 0; b < bpp; ++b) { unsigned char bit = readBitFromReversedStream(&ibp, in); setBitOfReversedStream(&obp, out, bit); @@ -5558,7 +5520,7 @@ } else { - /*we can immediatly filter into the out buffer, no other steps needed*/ + /*we can immediately filter into the out buffer, no other steps needed*/ error = filter(*out, in, w, h, &info_png->color, settings); } } @@ -5583,7 +5545,7 @@ unsigned i; Adam7_interlace(adam7, in, w, h, bpp); - for(i = 0; i < 7; i++) + for(i = 0; i != 7; ++i) { if(bpp < 8) { @@ -5619,9 +5581,10 @@ */ static unsigned getPaletteTranslucency(const unsigned char* palette, size_t palettesize) { - size_t i, key = 0; + size_t i; + unsigned key = 0; unsigned r = 0, g = 0, b = 0; /*the value of the color with alpha 0, so long as color keying is possible*/ - for(i = 0; i < palettesize; i++) + for(i = 0; i != palettesize; ++i) { if(!key && palette[4 * i + 3] == 0) { @@ -5674,10 +5637,9 @@ return state->error; } - if(state->encoder.auto_convert != LAC_NO) + if(state->encoder.auto_convert) { - state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw, - state->encoder.auto_convert); + state->error = lodepng_auto_choose_color(&info.color, image, w, h, &state->info_raw); } if(state->error) return state->error; @@ -5704,7 +5666,7 @@ if(!converted && size) state->error = 83; /*alloc fail*/ if(!state->error) { - state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h, 0 /*fix_png*/); + state->error = lodepng_convert(converted, image, &info.color, &state->info_raw, w, h); } if(!state->error) preProcessScanlines(&data, &datasize, converted, w, h, &info, &state->encoder); lodepng_free(converted); @@ -5767,7 +5729,7 @@ /*tIME*/ if(info.time_defined) addChunk_tIME(&outv, &info.time); /*tEXt and/or zTXt*/ - for(i = 0; i < info.text_num; i++) + for(i = 0; i != info.text_num; ++i) { if(strlen(info.text_keys[i]) > 79) { @@ -5792,7 +5754,7 @@ if(state->encoder.add_id) { unsigned alread_added_id_text = 0; - for(i = 0; i < info.text_num; i++) + for(i = 0; i != info.text_num; ++i) { if(!strcmp(info.text_keys[i], "LodePNG")) { @@ -5802,11 +5764,11 @@ } if(alread_added_id_text == 0) { - addChunk_tEXt(&outv, "LodePNG", VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ + addChunk_tEXt(&outv, "LodePNG", LODEPNG_VERSION_STRING); /*it's shorter as tEXt than as zTXt chunk*/ } } /*iTXt*/ - for(i = 0; i < info.itext_num; i++) + for(i = 0; i != info.itext_num; ++i) { if(strlen(info.itext_keys[i]) > 79) { @@ -5898,7 +5860,7 @@ lodepng_compress_settings_init(&settings->zlibsettings); settings->filter_palette_zero = 1; settings->filter_strategy = LFS_MINSUM; - settings->auto_convert = LAC_AUTO; + settings->auto_convert = 1; settings->force_palette = 0; settings->predefined_filters = 0; #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS @@ -5961,12 +5923,7 @@ case 43: return "bKGD chunk has wrong size for palette image"; case 44: return "bKGD chunk has wrong size for greyscale image"; case 45: return "bKGD chunk has wrong size for RGB image"; - /*Is the palette too small?*/ - case 46: return "a value in indexed image is larger than the palette size (bitdepth = 8)"; - /*Is the palette too small?*/ - case 47: return "a value in indexed image is larger than the palette size (bitdepth < 8)"; - /*the input data is empty, maybe a PNG file doesn't exist or is in the wrong path*/ - case 48: return "empty input or file doesn't exist"; + case 48: return "empty input buffer given to decoder. Maybe caused by non-existing file?"; case 49: return "jumped past memory while generating dynamic huffman tree"; case 50: return "jumped past memory while generating dynamic huffman tree"; case 51: return "jumped past memory while inflating huffman block"; @@ -6007,13 +5964,16 @@ case 82: return "color conversion to palette requested while a color isn't in palette"; case 83: return "memory allocation failed"; case 84: return "given image too small to contain all pixels to be encoded"; - case 85: return "internal color conversion bug"; case 86: return "impossible offset in lz77 encoding (internal bug)"; case 87: return "must provide custom zlib function pointer if LODEPNG_COMPILE_ZLIB is not defined"; case 88: return "invalid filter strategy given for LodePNGEncoderSettings.filter_strategy"; case 89: return "text chunk keyword too short or long: must have size 1-79"; /*the windowsize in the LodePNGCompressSettings. Requiring POT(==> & instead of %) makes encoding 12% faster.*/ case 90: return "windowsize must be a power of two"; + case 91: return "invalid decompressed idat size"; + case 92: return "too many pixels, not supported"; + case 93: return "zero width or height is invalid"; + case 94: return "header chunk must have a size of 13 bytes"; } return "unknown error code"; } @@ -6030,27 +5990,20 @@ { #ifdef LODEPNG_COMPILE_DISK -void load_file(std::vector& buffer, const std::string& filename) +unsigned load_file(std::vector& buffer, const std::string& filename) { - std::ifstream file(filename.c_str(), std::ios::in|std::ios::binary|std::ios::ate); - - /*get filesize*/ - std::streamsize size = 0; - if(file.seekg(0, std::ios::end).good()) size = file.tellg(); - if(file.seekg(0, std::ios::beg).good()) size -= file.tellg(); - - /*read contents of the file into the vector*/ - buffer.resize(size_t(size)); - if(size > 0) file.read((char*)(&buffer[0]), size); + long size = lodepng_filesize(filename.c_str()); + if(size < 0) return 78; + buffer.resize((size_t)size); + return size == 0 ? 0 : lodepng_buffer_file(&buffer[0], (size_t)size, filename.c_str()); } /*write given buffer to the file, overwriting the file, it doesn't append to it.*/ -void save_file(const std::vector& buffer, const std::string& filename) +unsigned save_file(const std::vector& buffer, const std::string& filename) { - std::ofstream file(filename.c_str(), std::ios::out|std::ios::binary); - file.write(buffer.empty() ? 0 : (char*)&buffer[0], std::streamsize(buffer.size())); + return lodepng_save_file(buffer.empty() ? 0 : &buffer[0], buffer.size(), filename.c_str()); } -#endif //LODEPNG_COMPILE_DISK +#endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER @@ -6073,7 +6026,7 @@ { return decompress(out, in.empty() ? 0 : &in[0], in.size(), settings); } -#endif //LODEPNG_COMPILE_DECODER +#endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER unsigned compress(std::vector& out, const unsigned char* in, size_t insize, @@ -6095,8 +6048,8 @@ { return compress(out, in.empty() ? 0 : &in[0], in.size(), settings); } -#endif //LODEPNG_COMPILE_ENCODER -#endif //LODEPNG_COMPILE_ZLIB +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ #ifdef LODEPNG_COMPILE_PNG @@ -6175,11 +6128,12 @@ LodePNGColorType colortype, unsigned bitdepth) { std::vector buffer; - load_file(buffer, filename); + unsigned error = load_file(buffer, filename); + if(error) return error; return decode(out, w, h, buffer, colortype, bitdepth); } -#endif //LODEPNG_COMPILE_DECODER -#endif //LODEPNG_COMPILE_DISK +#endif /* LODEPNG_COMPILE_DECODER */ +#endif /* LODEPNG_COMPILE_DISK */ #ifdef LODEPNG_COMPILE_ENCODER unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, @@ -6234,7 +6188,7 @@ { std::vector buffer; unsigned error = encode(buffer, in, w, h, colortype, bitdepth); - if(!error) save_file(buffer, filename); + if(!error) error = save_file(buffer, filename); return error; } @@ -6245,8 +6199,8 @@ if(lodepng_get_raw_size_lct(w, h, colortype, bitdepth) > in.size()) return 84; return encode(filename, in.empty() ? 0 : &in[0], w, h, colortype, bitdepth); } -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_ENCODER -#endif //LODEPNG_COMPILE_PNG -} //namespace lodepng +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_PNG */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ diff -Nru leanify-0.4.3/lib/zopflipng/lodepng/lodepng.h leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng.h --- leanify-0.4.3/lib/zopflipng/lodepng/lodepng.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,7 +1,7 @@ /* -LodePNG version 20131222 +LodePNG version 20160409 -Copyright (c) 2005-2013 Lode Vandevenne +Copyright (c) 2005-2016 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -28,10 +28,7 @@ #include /*for size_t*/ -#ifdef __cplusplus -#include -#include -#endif /*__cplusplus*/ +extern const char* LODEPNG_VERSION_STRING; /* The following #defines are used to create code sections. They can be disabled @@ -39,6 +36,8 @@ The "NO_COMPILE" defines are designed to be used to pass as defines to the compiler command to disable them without modifying this header, e.g. -DLODEPNG_NO_COMPILE_ZLIB for gcc. +In addition to those below, you can also define LODEPNG_NO_COMPILE_CRC to +allow implementing a custom lodepng_crc32. */ /*deflate & zlib. If disabled, you must specify alternative zlib functions in the custom_zlib field of the compress and decompress settings*/ @@ -82,6 +81,11 @@ #endif #endif +#ifdef LODEPNG_COMPILE_CPP +#include +#include +#endif /*LODEPNG_COMPILE_CPP*/ + #ifdef LODEPNG_COMPILE_PNG /*The PNG color types (also used for raw).*/ typedef enum LodePNGColorType @@ -195,7 +199,8 @@ namespace lodepng { #ifdef LODEPNG_COMPILE_DECODER -/*Same as lodepng_decode_memory, but decodes to an std::vector.*/ +/*Same as lodepng_decode_memory, but decodes to an std::vector. The colortype +is the format to output the pixels to. Default is RGBA 8-bit per channel.*/ unsigned decode(std::vector& out, unsigned& w, unsigned& h, const unsigned char* in, size_t insize, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); @@ -210,11 +215,12 @@ unsigned decode(std::vector& out, unsigned& w, unsigned& h, const std::string& filename, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_DECODER +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER -/*Same as lodepng_encode_memory, but encodes to an std::vector.*/ +/*Same as lodepng_encode_memory, but encodes to an std::vector. colortype +is that of the raw input data. The output PNG color type will be auto chosen.*/ unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); @@ -233,9 +239,9 @@ unsigned encode(const std::string& filename, const std::vector& in, unsigned w, unsigned h, LodePNGColorType colortype = LCT_RGBA, unsigned bitdepth = 8); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_ENCODER -} //namespace lodepng +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_ENCODER */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ #endif /*LODEPNG_COMPILE_PNG*/ @@ -280,7 +286,7 @@ /*LZ77 related settings*/ unsigned btype; /*the block type for LZ (0, 1, 2 or 3, see zlib standard). Should be 2 for proper compression.*/ unsigned use_lz77; /*whether or not to use LZ77. Should be 1 for proper compression.*/ - unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Typical value: 2048.*/ + unsigned windowsize; /*must be a power of two <= 32768. higher compresses more but is slower. Default value: 2048.*/ unsigned minmatch; /*mininum lz77 length. 3 is normally best, 6 can be better for some PNGs. Default: 0*/ unsigned nicematch; /*stop searching if >= this length found. Set to 258 for best compression. Default: 128*/ unsigned lazymatching; /*use lazy matching: better compression but a bit slower. Default: true*/ @@ -496,13 +502,14 @@ See the reference manual at the end of this header file to see which color conversions are supported. return value = LodePNG error code (0 if all went ok, an error if the conversion isn't supported) The out buffer must have size (w * h * bpp + 7) / 8, where bpp is the bits per pixel -of the output color type (lodepng_get_bpp) -The fix_png value works as described in struct LodePNGDecoderSettings. -Note: for 16-bit per channel colors, uses big endian format like PNG does. +of the output color type (lodepng_get_bpp). +For < 8 bpp images, there should not be padding bits at the end of scanlines. +For 16-bit per channel colors, uses big endian format like PNG does. +Return value is LodePNG error code */ unsigned lodepng_convert(unsigned char* out, const unsigned char* in, - LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, - unsigned w, unsigned h, unsigned fix_png); + const LodePNGColorMode* mode_out, const LodePNGColorMode* mode_in, + unsigned w, unsigned h); #ifdef LODEPNG_COMPILE_DECODER /* @@ -514,16 +521,7 @@ LodePNGDecompressSettings zlibsettings; /*in here is the setting to ignore Adler32 checksums*/ unsigned ignore_crc; /*ignore CRC checksums*/ - /* - The fix_png setting, if 1, makes the decoder tolerant towards some PNG images - that do not correctly follow the PNG specification. This only supports errors - that are fixable, were found in images that are actually used on the web, and - are silently tolerated by other decoders as well. Currently only one such fix - is implemented: if a palette index is out of bounds given the palette size, - interpret it as opaque black. - By default this value is 0, which makes it stop with an error on such images. - */ - unsigned fix_png; + unsigned color_convert; /*whether to convert the PNG to the color type you want. Default: yes*/ #ifdef LODEPNG_COMPILE_ANCILLARY_CHUNKS @@ -542,7 +540,7 @@ { /*every filter at zero*/ LFS_ZERO, - /*Use filter that gives minumum sum, as described in the official PNG filter heuristic.*/ + /*Use filter that gives minimum sum, as described in the official PNG filter heuristic.*/ LFS_MINSUM, /*Use the filter type that gives smallest Shannon entropy for this scanline. Depending on the image, this is better or worse than minsum.*/ @@ -556,46 +554,39 @@ LFS_PREDEFINED } LodePNGFilterStrategy; -/*automatically use color type with less bits per pixel if losslessly possible. Default: LAC_AUTO*/ -typedef enum LodePNGAutoConvert +/*Gives characteristics about the colors of the image, which helps decide which color model to use for encoding. +Used internally by default if "auto_convert" is enabled. Public because it's useful for custom algorithms.*/ +typedef struct LodePNGColorProfile { - LAC_NO, /*use color type user requested*/ - LAC_ALPHA, /*use color type user requested, but if only opaque pixels and RGBA or grey+alpha, use RGB or grey*/ - LAC_AUTO, /*use PNG color type that can losslessly represent the uncompressed image the smallest possible*/ - /* - like AUTO, but do not choose 1, 2 or 4 bit per pixel types. - sometimes a PNG image compresses worse if less than 8 bits per pixels. - */ - LAC_AUTO_NO_NIBBLES, - /* - like AUTO, but never choose palette color type. For small images, encoding - the palette may take more bytes than what is gained. Note that AUTO also - already prevents encoding the palette for extremely small images, but that may - not be sufficient because due to the compression it cannot predict when to - switch. - */ - LAC_AUTO_NO_PALETTE, - LAC_AUTO_NO_NIBBLES_NO_PALETTE -} LodePNGAutoConvert; + unsigned colored; /*not greyscale*/ + unsigned key; /*if true, image is not opaque. Only if true and alpha is false, color key is possible.*/ + unsigned short key_r; /*these values are always in 16-bit bitdepth in the profile*/ + unsigned short key_g; + unsigned short key_b; + unsigned alpha; /*alpha channel or alpha palette required*/ + unsigned numcolors; /*amount of colors, up to 257. Not valid if bits == 16.*/ + unsigned char palette[1024]; /*Remembers up to the first 256 RGBA colors, in no particular order*/ + unsigned bits; /*bits per channel (not for palette). 1,2 or 4 for greyscale only. 16 if 16-bit per channel required.*/ +} LodePNGColorProfile; +void lodepng_color_profile_init(LodePNGColorProfile* profile); -/* -Automatically chooses color type that gives smallest amount of bits in the -output image, e.g. grey if there are only greyscale pixels, palette if there -are less than 256 colors, ... -The auto_convert parameter allows limiting it to not use palette, ... -*/ +/*Get a LodePNGColorProfile of the image.*/ +unsigned lodepng_get_color_profile(LodePNGColorProfile* profile, + const unsigned char* image, unsigned w, unsigned h, + const LodePNGColorMode* mode_in); +/*The function LodePNG uses internally to decide the PNG color with auto_convert. +Chooses an optimal color model, e.g. grey if only grey pixels, palette if < 256 colors, ...*/ unsigned lodepng_auto_choose_color(LodePNGColorMode* mode_out, const unsigned char* image, unsigned w, unsigned h, - const LodePNGColorMode* mode_in, - LodePNGAutoConvert auto_convert); + const LodePNGColorMode* mode_in); /*Settings for the encoder.*/ typedef struct LodePNGEncoderSettings { LodePNGCompressSettings zlibsettings; /*settings for the zlib encoder, such as window size, ...*/ - LodePNGAutoConvert auto_convert; /*how to automatically choose output PNG color type, if at all*/ + unsigned auto_convert; /*automatically choose output PNG color type. Default: true*/ /*If true, follows the official PNG heuristic: if the PNG uses a palette or lower than 8 bit depth, set all filters to zero. Otherwise use the filter_strategy. Note that to @@ -640,7 +631,7 @@ LodePNGInfo info_png; /*info of the PNG image obtained after decoding*/ unsigned error; #ifdef LODEPNG_COMPILE_CPP - //For the lodepng::State subclass. + /* For the lodepng::State subclass. */ virtual ~LodePNGState(){} #endif } LodePNGState; @@ -690,7 +681,11 @@ Fourth byte: uppercase = unsafe to copy, lowercase = safe to copy */ -/*get the length of the data of the chunk. Total chunk length has 12 bytes more.*/ +/* +Gets the length of the data of the chunk. Total chunk length has 12 bytes more. +There must be at least 4 bytes to read from. If the result value is too large, +it may be corrupt data. +*/ unsigned lodepng_chunk_length(const unsigned char* chunk); /*puts the 4-byte type in null terminated string*/ @@ -818,7 +813,7 @@ #endif /*LODEPNG_COMPILE_DISK*/ #ifdef LODEPNG_COMPILE_CPP -//The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. +/* The LodePNG C++ wrapper uses std::vectors instead of manually allocated memory buffers. */ namespace lodepng { #ifdef LODEPNG_COMPILE_PNG @@ -832,7 +827,7 @@ }; #ifdef LODEPNG_COMPILE_DECODER -//Same as other lodepng::decode, but using a State for more settings and information. +/* Same as other lodepng::decode, but using a State for more settings and information. */ unsigned decode(std::vector& out, unsigned& w, unsigned& h, State& state, const unsigned char* in, size_t insize); @@ -842,7 +837,7 @@ #endif /*LODEPNG_COMPILE_DECODER*/ #ifdef LODEPNG_COMPILE_ENCODER -//Same as other lodepng::encode, but using a State for more settings and information. +/* Same as other lodepng::encode, but using a State for more settings and information. */ unsigned encode(std::vector& out, const unsigned char* in, unsigned w, unsigned h, State& state); @@ -853,47 +848,47 @@ #ifdef LODEPNG_COMPILE_DISK /* -Load a file from disk into an std::vector. If the vector is empty, then either -the file doesn't exist or is an empty file. +Load a file from disk into an std::vector. +return value: error code (0 means ok) */ -void load_file(std::vector& buffer, const std::string& filename); +unsigned load_file(std::vector& buffer, const std::string& filename); /* Save the binary data in an std::vector to a file on disk. The file is overwritten without warning. */ -void save_file(const std::vector& buffer, const std::string& filename); -#endif //LODEPNG_COMPILE_DISK -#endif //LODEPNG_COMPILE_PNG +unsigned save_file(const std::vector& buffer, const std::string& filename); +#endif /* LODEPNG_COMPILE_DISK */ +#endif /* LODEPNG_COMPILE_PNG */ #ifdef LODEPNG_COMPILE_ZLIB #ifdef LODEPNG_COMPILE_DECODER -//Zlib-decompress an unsigned char buffer +/* Zlib-decompress an unsigned char buffer */ unsigned decompress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); -//Zlib-decompress an std::vector +/* Zlib-decompress an std::vector */ unsigned decompress(std::vector& out, const std::vector& in, const LodePNGDecompressSettings& settings = lodepng_default_decompress_settings); -#endif //LODEPNG_COMPILE_DECODER +#endif /* LODEPNG_COMPILE_DECODER */ #ifdef LODEPNG_COMPILE_ENCODER -//Zlib-compress an unsigned char buffer +/* Zlib-compress an unsigned char buffer */ unsigned compress(std::vector& out, const unsigned char* in, size_t insize, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); -//Zlib-compress an std::vector +/* Zlib-compress an std::vector */ unsigned compress(std::vector& out, const std::vector& in, const LodePNGCompressSettings& settings = lodepng_default_compress_settings); -#endif //LODEPNG_COMPILE_ENCODER -#endif //LODEPNG_COMPILE_ZLIB -} //namespace lodepng +#endif /* LODEPNG_COMPILE_ENCODER */ +#endif /* LODEPNG_COMPILE_ZLIB */ +} /* namespace lodepng */ #endif /*LODEPNG_COMPILE_CPP*/ /* TODO: [.] test if there are no memory leaks or security exploits - done a lot but needs to be checked often -[.] check compatibility with vareous compilers - done but needs to be redone for every newer version +[.] check compatibility with various compilers - done but needs to be redone for every newer version [X] converting color to 16-bit per channel types [ ] read all public PNG chunk types (but never let the color profile and gamma ones touch RGB values) [ ] make sure encoder generates no chunks with size > (2^31)-1 @@ -901,8 +896,9 @@ [X] let the "isFullyOpaque" function check color keys and transparent palettes too [X] better name for the variables "codes", "codesD", "codelengthcodes", "clcl" and "lldl" [ ] don't stop decoding on errors like 69, 57, 58 (make warnings) -[ ] make option to choose if the raw image with non multiple of 8 bits per scanline should have padding bits or not [ ] let the C++ wrapper catch exceptions coming from the standard library and return LodePNG error codes +[ ] allow user to provide custom color conversion functions, e.g. for premultiplied alpha, padding bits or not, ... +[ ] allow user to give data (void*) to custom allocator */ #endif /*LODEPNG_H inclusion guard*/ @@ -932,8 +928,9 @@ 10. examples 10.1. decoder C++ example 10.2. decoder C example - 11. changes - 12. contact information + 11. state settings reference + 12. changes + 13. contact information 1. about @@ -1240,20 +1237,22 @@ If, when decoding, you want the raw image to be something else than the default, you need to set the color type and bit depth you want in the LodePNGColorMode, -or the parameters of the simple function of LodePNG you're using. +or the parameters colortype and bitdepth of the simple decoding function. -If, when encoding, you use another color type than the default in the input +If, when encoding, you use another color type than the default in the raw input image, you need to specify its color type and bit depth in the LodePNGColorMode -of the raw image, or use the parameters of the simplefunction of LodePNG you're -using. +of the raw image, or use the parameters colortype and bitdepth of the simple +encoding function. If, when encoding, you don't want LodePNG to choose the output PNG color type but control it yourself, you need to set auto_convert in the encoder settings -to LAC_NONE, and specify the color type you want in the LodePNGInfo of the -encoder. - -If you do any of the above, LodePNG may need to do a color conversion, which -follows the rules below, and may sometimes not be allowed. +to false, and specify the color type you want in the LodePNGInfo of the +encoder (including palette: it can generate a palette if auto_convert is true, +otherwise not). + +If the input and output color type differ (whether user chosen or auto chosen), +LodePNG will do a color conversion, which follows the rules below, and may +sometimes result in an error. To avoid some confusion: -the decoder converts from PNG to raw image @@ -1275,7 +1274,7 @@ Non supported color conversions: -color to greyscale: no error is thrown, but the result will look ugly because only the red channel is taken --anything, to palette when that palette does not have that color in it: in this +-anything to palette when that palette does not have that color in it: in this case an error is thrown Supported color conversions: @@ -1285,10 +1284,10 @@ -removing alpha channel -higher to smaller bitdepth, and vice versa -If you want no color conversion to be done: +If you want no color conversion to be done (e.g. for speed or control): -In the encoder, you can make it save a PNG with any color type by giving the raw color mode and LodePNGInfo the same color mode, and setting auto_convert to -LAC_NO. +false. -In the decoder, you can make it store the pixel data in the same color type as the PNG has, by setting the color_convert setting to false. Settings in info_raw are then ignored. @@ -1455,6 +1454,8 @@ Add the files lodepng.c(pp) and lodepng.h to your project, include lodepng.h where needed, and your program can read/write PNG files. +It is compatible with C90 and up, and C++03 and up. + If performance is important, use optimization when compiling! For both the encoder and decoder, this makes a large difference. @@ -1470,49 +1471,40 @@ warnings with compiler options "-Wall -Wextra -pedantic -ansi", with gcc and g++ version 4.7.1 on Linux, 32-bit and 64-bit. +*) Clang + +Fully supported and warning-free. + *) Mingw -The Mingw compiler (a port of gcc) for Windows is fully supported by LodePNG. +The Mingw compiler (a port of gcc for Windows) should be fully supported by +LodePNG. -*) Visual Studio 2005 and up, Visual C++ Express Edition 2005 and up +*) Visual Studio and Visual C++ Express Edition -Visual Studio may give warnings about 'fopen' being deprecated. A multiplatform library -can't support the proposed Visual Studio alternative however, so LodePNG keeps using -fopen. If you don't want to see the deprecated warnings, put this on top of lodepng.h -before the inclusions: -#define _CRT_SECURE_NO_DEPRECATE - -Other than the above warnings, LodePNG should be warning-free with warning -level 3 (W3). Warning level 4 (W4) will give warnings about integer conversions. -I'm not planning to resolve these warnings. To get rid of them, let Visual -Studio use warning level W3 for lodepng.cpp only: right click lodepng.cpp, -Properties, C/C++, General, Warning Level: Level 3 (/W3). +LodePNG should be warning-free with warning level W4. Two warnings were disabled +with pragmas though: warning 4244 about implicit conversions, and warning 4996 +where it wants to use a non-standard function fopen_s instead of the standard C +fopen. Visual Studio may want "stdafx.h" files to be included in each source file and give an error "unexpected end of file while looking for precompiled header". -That is not standard C++ and will not be added to the stock LodePNG. You can +This is not standard C++ and will not be added to the stock LodePNG. You can disable it for lodepng.cpp only by right clicking it, Properties, C/C++, Precompiled Headers, and set it to Not Using Precompiled Headers there. -*) Visual Studio 6.0 - -LodePNG support for Visual Studio 6.0 is not guaranteed because VS6 doesn't -follow the C++ standard correctly. - -*) Comeau C/C++ - -Vesion 20070107 compiles without problems on the Comeau C/C++ Online Test Drive -at http://www.comeaucomputing.com/tryitout in both C90 and C++ mode. +NOTE: Modern versions of VS should be fully supported, but old versions, e.g. +VS6, are not guaranteed to work. *) Compilers on Macintosh -LodePNG has been reported to work both with the gcc and LLVM for Macintosh, both -for C and C++. +LodePNG has been reported to work both with gcc and LLVM for Macintosh, both for +C and C++. *) Other Compilers -If you encounter problems on other compilers, feel free to let me know and I may -try to fix it if the compiler is modern standards complient. +If you encounter problems on any compilers, feel free to let me know and I may +try to fix it if the compiler is modern and standards complient. 10. examples @@ -1564,8 +1556,49 @@ return 0; } +11. state settings reference +---------------------------- + +A quick reference of some settings to set on the LodePNGState + +For decoding: + +state.decoder.zlibsettings.ignore_adler32: ignore ADLER32 checksums +state.decoder.zlibsettings.custom_...: use custom inflate function +state.decoder.ignore_crc: ignore CRC checksums +state.decoder.color_convert: convert internal PNG color to chosen one +state.decoder.read_text_chunks: whether to read in text metadata chunks +state.decoder.remember_unknown_chunks: whether to read in unknown chunks +state.info_raw.colortype: desired color type for decoded image +state.info_raw.bitdepth: desired bit depth for decoded image +state.info_raw....: more color settings, see struct LodePNGColorMode +state.info_png....: no settings for decoder but ouput, see struct LodePNGInfo + +For encoding: + +state.encoder.zlibsettings.btype: disable compression by setting it to 0 +state.encoder.zlibsettings.use_lz77: use LZ77 in compression +state.encoder.zlibsettings.windowsize: tweak LZ77 windowsize +state.encoder.zlibsettings.minmatch: tweak min LZ77 length to match +state.encoder.zlibsettings.nicematch: tweak LZ77 match where to stop searching +state.encoder.zlibsettings.lazymatching: try one more LZ77 matching +state.encoder.zlibsettings.custom_...: use custom deflate function +state.encoder.auto_convert: choose optimal PNG color type, if 0 uses info_png +state.encoder.filter_palette_zero: PNG filter strategy for palette +state.encoder.filter_strategy: PNG filter strategy to encode with +state.encoder.force_palette: add palette even if not encoding to one +state.encoder.add_id: add LodePNG identifier and version as a text chunk +state.encoder.text_compression: use compressed text chunks for metadata +state.info_raw.colortype: color type of raw input image you provide +state.info_raw.bitdepth: bit depth of raw input image you provide +state.info_raw: more color settings, see struct LodePNGColorMode +state.info_png.color.colortype: desired color type if auto_convert is false +state.info_png.color.bitdepth: desired bit depth if auto_convert is false +state.info_png.color....: more color settings, see struct LodePNGColorMode +state.info_png....: more PNG related settings, see struct LodePNGInfo + -11. changes +12. changes ----------- The version number of LodePNG is the date of the change given in the format @@ -1574,6 +1607,15 @@ Some changes aren't backwards compatible. Those are indicated with a (!) symbol. +*) 09 apr 2016: Fixed colorkey usage detection, and better file loading (within + the limits of pure C90). +*) 08 dec 2015: Made load_file function return error if file can't be opened. +*) 24 okt 2015: Bugfix with decoding to palette output. +*) 18 apr 2015: Boundary PM instead of just package-merge for faster encoding. +*) 23 aug 2014: Reduced needless memory usage of decoder. +*) 28 jun 2014: Removed fix_png setting, always support palette OOB for + simplicity. Made ColorProfile public. +*) 09 jun 2014: Faster encoder by fixing hash bug and more zeros optimization. *) 22 dec 2013: Power of two windowsize required for optimization. *) 15 apr 2013: Fixed bug with LAC_ALPHA and color key. *) 25 mar 2013: Added an optional feature to ignore some PNG errors (fix_png). @@ -1594,7 +1636,7 @@ *) 22 apr 2012 (!): Made interface more consistent, renaming a lot. Removed redundant C++ codec classes. Reduced amount of structs. Everything changed, but it is cleaner now imho and functionality remains the same. Also fixed - several bugs and shrinked the implementation code. Made new samples. + several bugs and shrunk the implementation code. Made new samples. *) 6 nov 2011 (!): By default, the encoder now automatically chooses the best PNG color model and bit depth, based on the amount and type of colors of the raw image. For this, autoLeaveOutAlphaChannel replaced by auto_choose_color. @@ -1629,7 +1671,7 @@ *) 20 jan 2008: support for unknown chunks allowing using LodePNG for an editor. *) 18 jan 2008: support for tIME and pHYs chunks added to encoder and decoder. *) 17 jan 2008: ability to encode and decode compressed zTXt chunks added - Also vareous fixes, such as in the deflate and the padding bits code. + Also various fixes, such as in the deflate and the padding bits code. *) 13 jan 2008: Added ability to encode Adam7-interlaced images. Improved filtering code of encoder. *) 07 jan 2008: (!) changed LodePNG to use ISO C90 instead of C++. A @@ -1700,7 +1742,7 @@ *) 12 aug 2005: Initial release (C++, decoder only) -12. contact information +13. contact information ----------------------- Feel free to contact me with suggestions, problems, comments, ... concerning @@ -1712,5 +1754,5 @@ Account: lode dot vandevenne. -Copyright (c) 2005-2013 Lode Vandevenne +Copyright (c) 2005-2016 Lode Vandevenne */ diff -Nru leanify-0.4.3/lib/zopflipng/lodepng/lodepng_util.cpp leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng_util.cpp --- leanify-0.4.3/lib/zopflipng/lodepng/lodepng_util.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng_util.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,7 +1,7 @@ /* LodePNG Utils -Copyright (c) 2005-2012 Lode Vandevenne +Copyright (c) 2005-2014 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages @@ -41,7 +41,7 @@ const std::vector& png) { // Listing chunks is based on the original file, not the decoded png info. - const unsigned char *chunk, *begin, *end; + const unsigned char *chunk, *begin, *end, *next; end = &png.back() + 1; begin = chunk = &png.front() + 8; @@ -51,10 +51,14 @@ lodepng_chunk_type(type, chunk); if(std::string(type).size() != 4) return 1; + unsigned length = lodepng_chunk_length(chunk); + if(chunk + length + 12 > end) return 1; names.push_back(type); - sizes.push_back(lodepng_chunk_length(chunk)); + sizes.push_back(length); - chunk = lodepng_chunk_next_const(chunk); + next = lodepng_chunk_next_const(chunk); + if (next <= chunk) return 1; // integer overflow + chunk = next; } return 0; } @@ -77,6 +81,7 @@ if(name.size() != 4) return 1; next = lodepng_chunk_next_const(chunk); + if (next <= chunk) return 1; // integer overflow if(name == "IHDR") { @@ -90,8 +95,13 @@ { location = 2; } - else if(name != "IEND") + else if(name == "IEND") + { + break; // anything after IEND is not part of the PNG or the 3 groups here. + } + else { + if(next > end) return 1; // invalid chunk, content too far names[location].push_back(name); chunks[location].push_back(std::vector(chunk, next)); } @@ -109,9 +119,9 @@ end = &png.back() + 1; begin = chunk = &png.front() + 8; - size_t l0 = 0; //location 0: IHDR-l0-PLTE (or IHDR-l0-l1-IDAT) - size_t l1 = 0; //location 1: PLTE-l1-IDAT (or IHDR-l0-l1-IDAT) - size_t l2 = 0; //location 2: IDAT-l2-IEND + long l0 = 0; //location 0: IHDR-l0-PLTE (or IHDR-l0-l1-IDAT) + long l1 = 0; //location 1: PLTE-l1-IDAT (or IHDR-l0-l1-IDAT) + long l2 = 0; //location 2: IDAT-l2-IEND while(chunk + 8 < end && chunk >= begin) { @@ -121,6 +131,7 @@ if(name.size() != 4) return 1; next = lodepng_chunk_next_const(chunk); + if (next <= chunk) return 1; // integer overflow if(name == "PLTE") { @@ -164,7 +175,7 @@ if(error) return 1; //Read literal data from all IDAT chunks - const unsigned char *chunk, *begin, *end; + const unsigned char *chunk, *begin, *end, *next; end = &png.back() + 1; begin = chunk = &png.front() + 8; @@ -180,6 +191,10 @@ { const unsigned char* cdata = lodepng_chunk_data_const(chunk); unsigned clength = lodepng_chunk_length(chunk); + if(chunk + clength + 12 > end || clength > png.size() || chunk + clength + 12 < begin) { + // corrupt chunk length + return 1; + } for(unsigned i = 0; i < clength; i++) { @@ -187,7 +202,9 @@ } } - chunk = lodepng_chunk_next_const(chunk); + next = lodepng_chunk_next_const(chunk); + if (next <= chunk) return 1; // integer overflow + chunk = next; } //Decompress all IDAT data @@ -217,11 +234,12 @@ static const unsigned ADAM7_DX[7] = { 8, 8, 4, 4, 2, 2, 1 }; /*x delta values*/ static const unsigned ADAM7_DY[7] = { 8, 8, 8, 4, 4, 2, 2 }; /*y delta values*/ size_t pos = 0; - for(int j = 0; j < 7; j++) + for(size_t j = 0; j < 7; j++) { unsigned w2 = (w - ADAM7_IX[j] + ADAM7_DX[j] - 1) / ADAM7_DX[j]; unsigned h2 = (h - ADAM7_IY[j] + ADAM7_DY[j] - 1) / ADAM7_DY[j]; - if(ADAM7_IX[j] >= w || ADAM7_IY[j] >= h) w2 = h2 = 0; + if(ADAM7_IX[j] >= w) w2 = 0; + if(ADAM7_IY[j] >= h) h2 = 0; size_t linebytes = 1 + lodepng_get_raw_size(w2, 1, &state.info_png.color); for(size_t i = 0; i < h2; i++) { @@ -287,10 +305,9 @@ struct ExtractZlib // Zlib decompression and information extraction { std::vector* zlibinfo; + ExtractZlib(std::vector* info) : zlibinfo(info) {}; int error; - ExtractZlib(std::vector* output) : zlibinfo(output) {}; - unsigned long readBitFromStream(size_t& bitp, const unsigned char* bits) { unsigned long result = (bits[bitp >> 3] >> (bitp & 0x7)) & 1; @@ -391,14 +408,14 @@ //the code tree for Huffman codes, dist codes, and code length codes HuffmanTree codetree, codetreeD, codelengthcodetree; - unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& codetree, size_t inlength) + unsigned long huffmanDecodeSymbol(const unsigned char* in, size_t& bp, const HuffmanTree& tree, size_t inlength) { //decode a single symbol from given list of bits with given code tree. return value is the symbol bool decoded; unsigned long ct; for(size_t treepos = 0;;) { if((bp & 0x07) == 0 && (bp >> 3) > inlength) { error = 10; return 0; } //error: end reached without endcode - error = codetree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); + error = tree.decode(decoded, ct, treepos, readBitFromStream(bp, in)); if(error) return 0; //stop, an error happened if(decoded) return ct; } @@ -472,9 +489,9 @@ if(error) return; zlibinfo->back().treebits = bp - bpstart; //lit/len/end symbol lengths - for(size_t i = 0; i < bitlen.size(); i++) zlibinfo->back().litlenlengths.push_back(bitlen[i]); + for(size_t j = 0; j < bitlen.size(); j++) zlibinfo->back().litlenlengths.push_back(bitlen[j]); //dist lengths - for(size_t i = 0; i < bitlenD.size(); i++) zlibinfo->back().distlengths.push_back(bitlenD[i]); + for(size_t j = 0; j < bitlenD.size(); j++) zlibinfo->back().distlengths.push_back(bitlenD[j]); } void inflateHuffmanBlock(std::vector& out, @@ -536,7 +553,7 @@ while((bp & 0x7) != 0) bp++; //go to first boundary of byte size_t p = bp / 8; if(p >= inlength - 4) { error = 52; return; } //error, bit pointer will jump past memory - unsigned long LEN = in[p] + 256 * in[p + 1], NLEN = in[p + 2] + 256 * in[p + 3]; p += 4; + unsigned long LEN = in[p] + 256u * in[p + 1], NLEN = in[p + 2] + 256u * in[p + 3]; p += 4; if(LEN + NLEN != 65535) { error = 21; return; } //error: NLEN is not one's complement of LEN if(p + LEN > inlength) { error = 23; return; } //error: reading outside of in buffer for(unsigned long n = 0; n < LEN; n++) @@ -565,10 +582,8 @@ struct ExtractPNG //PNG decoding and information extraction { std::vector* zlibinfo; + ExtractPNG(std::vector* info) : zlibinfo(info) {}; int error; - - ExtractPNG(std::vector* output) : zlibinfo(output) {}; - void decode(const unsigned char* in, size_t size) { error = 0; @@ -641,7 +656,7 @@ unsigned long read32bitInt(const unsigned char* buffer) { - return (buffer[0] << 24) | (buffer[1] << 16) | (buffer[2] << 8) | buffer[3]; + return (unsigned int)((buffer[0] << 24u) | (buffer[1] << 16u) | (buffer[2] << 8u) | buffer[3]); } }; diff -Nru leanify-0.4.3/lib/zopflipng/lodepng/lodepng_util.h leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng_util.h --- leanify-0.4.3/lib/zopflipng/lodepng/lodepng_util.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/lodepng/lodepng_util.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,7 +1,7 @@ /* LodePNG Utils -Copyright (c) 2005-2012 Lode Vandevenne +Copyright (c) 2005-2014 Lode Vandevenne This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff -Nru leanify-0.4.3/lib/zopflipng/zopflipng_lib.cc leanify-0.4.3+git20181014/lib/zopflipng/zopflipng_lib.cc --- leanify-0.4.3/lib/zopflipng/zopflipng_lib.cc 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/zopflipng_lib.cc 2017-08-01 20:51:09.000000000 +0000 @@ -56,30 +56,7 @@ options.numiterations = insize < 200000 ? png_options->num_iterations : png_options->num_iterations_large; - if (png_options->block_split_strategy == 3) { - // Try both block splitting first and last. - unsigned char* out2 = 0; - size_t outsize2 = 0; - options.blocksplittinglast = 0; - ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize); - bp = 0; - options.blocksplittinglast = 1; - ZopfliDeflate(&options, 2 /* Dynamic */, 1, - in, insize, &bp, &out2, &outsize2); - - if (outsize2 < *outsize) { - free(*out); - *out = out2; - *outsize = outsize2; - printf("Block splitting last was better\n"); - } else { - free(out2); - } - } else { - if (png_options->block_split_strategy == 0) options.blocksplitting = 0; - options.blocksplittinglast = png_options->block_split_strategy == 2; - ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize); - } + ZopfliDeflate(&options, 2 /* Dynamic */, 1, in, insize, &bp, out, outsize); return 0; // OK } @@ -181,7 +158,7 @@ // Returns 0 if ok, other value for error unsigned TryOptimize( const std::vector& image, unsigned w, unsigned h, - const lodepng::State& inputstate, bool bit16, + const lodepng::State& inputstate, bool bit16, bool keep_colortype, const std::vector& origfile, ZopfliPNGFilterStrategy filterstrategy, bool use_zopfli, int windowsize, const ZopfliPNGOptions* png_options, @@ -195,6 +172,10 @@ state.encoder.zlibsettings.custom_context = png_options; } + if (keep_colortype) { + state.encoder.auto_convert = 0; + lodepng_color_mode_copy(&state.info_png.color, &inputstate.info_png.color); + } if (inputstate.info_png.color.colortype == LCT_PALETTE) { // Make it preserve the original palette order lodepng_color_mode_copy(&state.info_raw, &inputstate.info_png.color); @@ -232,6 +213,7 @@ break; case kStrategyPredefined: lodepng::getFilterTypes(filters, origfile); + if (filters.size() != h) return 1; // Error getting filters state.encoder.filter_strategy = LFS_PREDEFINED; state.encoder.predefined_filters = &filters[0]; break; @@ -246,24 +228,28 @@ // For very small output, also try without palette, it may be smaller thanks // to no palette storage overhead. - if (!error && out->size() < 4096) { + if (!error && out->size() < 4096 && !keep_colortype) { lodepng::State teststate; std::vector temp; lodepng::decode(temp, w, h, teststate, *out); - LodePNGColorMode& color = teststate.info_png.color; - if (color.colortype == LCT_PALETTE) { - std::vector out2; - state.encoder.auto_convert = LAC_ALPHA; - bool grey = true; - for (size_t i = 0; i < color.palettesize; i++) { - if (color.palette[i * 4 + 0] != color.palette[i * 4 + 2] - || color.palette[i * 4 + 1] != color.palette[i * 4 + 2]) { - grey = false; - break; - } + if (teststate.info_png.color.colortype == LCT_PALETTE) { + LodePNGColorProfile profile; + lodepng_color_profile_init(&profile); + lodepng_get_color_profile(&profile, &image[0], w, h, &state.info_raw); + // Too small for tRNS chunk overhead. + if (w * h <= 16 && profile.key) profile.alpha = 1; + state.encoder.auto_convert = 0; + state.info_png.color.colortype = (profile.alpha ? LCT_RGBA : LCT_RGB); + state.info_png.color.bitdepth = 8; + state.info_png.color.key_defined = (profile.key && !profile.alpha); + if (state.info_png.color.key_defined) { + state.info_png.color.key_defined = 1; + state.info_png.color.key_r = (profile.key_r & 255u); + state.info_png.color.key_g = (profile.key_g & 255u); + state.info_png.color.key_b = (profile.key_b & 255u); } - if (grey) state.info_png.color.colortype = LCT_GREY_ALPHA; + std::vector out2; error = lodepng::encode(out2, image, w, h, state); if (out2.size() < out->size()) out->swap(out2); } @@ -282,7 +268,8 @@ // filter type. unsigned AutoChooseFilterStrategy(const std::vector& image, unsigned w, unsigned h, - const lodepng::State& inputstate, bool bit16, + const lodepng::State& inputstate, + bool bit16, bool keep_colortype, const std::vector& origfile, int numstrategies, ZopfliPNGFilterStrategy* strategies, @@ -299,8 +286,9 @@ for (int i = 0; i < numstrategies; i++) { out.clear(); - unsigned error = TryOptimize(image, w, h, inputstate, bit16, origfile, - strategies[i], false, windowsize, 0, &out); + unsigned error = TryOptimize(image, w, h, inputstate, bit16, keep_colortype, + origfile, strategies[i], false, windowsize, 0, + &out); if (error) return error; if (bestsize == 0 || out.size() < bestsize) { bestsize = out.size(); @@ -315,6 +303,27 @@ return 0; /* OK */ } +// Outputs the intersection of keepnames and non-essential chunks which are in +// the PNG image. +void ChunksToKeep(const std::vector& origpng, + const std::vector& keepnames, + std::set* result) { + std::vector names[3]; + std::vector > chunks[3]; + + lodepng::getChunks(names, chunks, origpng); + + for (size_t i = 0; i < 3; i++) { + for (size_t j = 0; j < names[i].size(); j++) { + for (size_t k = 0; k < keepnames.size(); k++) { + if (keepnames[k] == names[i][j]) { + result->insert(names[i][j]); + } + } + } + } +} + // Keeps chunks with given names from the original png by literally copying them // into the new png void KeepChunks(const std::vector& origpng, @@ -370,15 +379,39 @@ lodepng::State inputstate; error = lodepng::decode(image, w, h, inputstate, origpng); + bool keep_colortype = false; + + if (!png_options.keepchunks.empty()) { + // If the user wants to keep the non-essential chunks bKGD or sBIT, the + // input color type has to be kept since the chunks format depend on it. + // This may severely hurt compression if it is not an ideal color type. + // Ideally these chunks should not be kept for web images. Handling of bKGD + // chunks could be improved by changing its color type but not done yet due + // to its additional complexity, for sBIT such improvement is usually not + // possible. + std::set keepchunks; + ChunksToKeep(origpng, png_options.keepchunks, &keepchunks); + keep_colortype = keepchunks.count("bKGD") || keepchunks.count("sBIT"); + if (keep_colortype && verbose) { + printf("Forced to keep original color type due to keeping bKGD or sBIT" + " chunk.\n"); + } + } + if (error) { if (verbose) { - printf("Decoding error %u: %s\n", error, lodepng_error_text(error)); + if (error == 1) { + printf("Decoding error\n"); + } else { + printf("Decoding error %u: %s\n", error, lodepng_error_text(error)); + } } return error; } bool bit16 = false; // Using 16-bit per channel raw image - if (inputstate.info_png.color.bitdepth == 16 && !png_options.lossy_8bit) { + if (inputstate.info_png.color.bitdepth == 16 && + (keep_colortype || !png_options.lossy_8bit)) { // Decode as 16-bit image.clear(); error = lodepng::decode(image, w, h, origpng, LCT_RGBA, 16); @@ -393,7 +426,7 @@ if (png_options.auto_filter_strategy) { error = AutoChooseFilterStrategy(image, w, h, inputstate, bit16, - origpng, + keep_colortype, origpng, /* Don't try brute force */ kNumFilterStrategies - 1, filterstrategies, strategy_enable); @@ -407,8 +440,8 @@ if (!strategy_enable[i]) continue; std::vector temp; - error = TryOptimize(image, w, h, inputstate, bit16, origpng, - filterstrategies[i], true /* use_zopfli */, + error = TryOptimize(image, w, h, inputstate, bit16, keep_colortype, + origpng, filterstrategies[i], true /* use_zopfli */, windowsize, &png_options, &temp); if (!error) { if (verbose) { @@ -431,7 +464,7 @@ } extern "C" void CZopfliPNGSetDefaults(CZopfliPNGOptions* png_options) { - + memset(png_options, 0, sizeof(*png_options)); // Constructor sets the defaults ZopfliPNGOptions opts; diff -Nru leanify-0.4.3/lib/zopflipng/zopflipng_lib.h leanify-0.4.3+git20181014/lib/zopflipng/zopflipng_lib.h --- leanify-0.4.3/lib/zopflipng/zopflipng_lib.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/lib/zopflipng/zopflipng_lib.h 2017-08-01 20:51:09.000000000 +0000 @@ -84,7 +84,7 @@ #ifdef __cplusplus } // extern "C" -#endif +#endif // C++ API #ifdef __cplusplus @@ -118,7 +118,7 @@ // Zopfli number of iterations on large images int num_iterations_large; - // 0=none, 1=first, 2=last, 3=both + // Unused, left for backwards compatiblity. int block_split_strategy; }; diff -Nru leanify-0.4.3/main.cpp leanify-0.4.3+git20181014/main.cpp --- leanify-0.4.3/main.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/main.cpp 2018-05-05 21:00:58.000000000 +0000 @@ -9,259 +9,230 @@ #include #endif -#include "leanify.h" #include "fileio.h" +#include "leanify.h" #include "version.h" #include "formats/jpeg.h" +#include "formats/png.h" +#include "formats/zip.h" using std::cerr; using std::cout; using std::endl; using std::string; -void PrintSize(size_t size) -{ - if (size < 1024) - { - cout << size << " B"; - } - else if (size < 1024 * 1024) - { - cout << size / 1024.0 << " KB"; - } - else - { - cout << size / 1024.0 / 1024.0 << " MB"; - } +void PrintSize(size_t size) { + if (size < 1024) + cout << size << " B"; + else if (size < 1024 * 1024) + cout << size / 1024.0 << " KB"; + else + cout << size / 1024.0 / 1024.0 << " MB"; } - #ifdef _WIN32 -int ProcessFile(const wchar_t *file_path) -{ - char mbs[MAX_PATH] = { 0 }; - WideCharToMultiByte(CP_ACP, 0, file_path, -1, mbs, sizeof(mbs) - 1, nullptr, nullptr); - cout << "Processing: " << mbs << endl; - string filename(mbs); +int ProcessFile(const wchar_t* file_path) { + char mbs[MAX_PATH] = { 0 }; + WideCharToMultiByte(CP_ACP, 0, file_path, -1, mbs, sizeof(mbs) - 1, nullptr, nullptr); + string filename(mbs); #else -// written like this in order to be callback funtion of ftw() -int ProcessFile(const char file_path[], const struct stat *sb = nullptr, int typeflag = FTW_F) -{ - if (typeflag != FTW_F) - { - return 0; - } - cout << "Processing: " << file_path << endl; - string filename(file_path); -#endif // _WIN32 - +// written like this in order to be callback function of ftw() +int ProcessFile(const char* file_path, const struct stat* sb = nullptr, int typeflag = FTW_F) { + if (typeflag != FTW_F) + return 0; + string filename(file_path); +#endif // _WIN32 - File input_file(file_path); + cout << "Processing: " << filename << endl; + File input_file(file_path); - if (input_file.IsOK()) - { - size_t original_size = input_file.GetSize(); + if (input_file.IsOK()) { + size_t original_size = input_file.GetSize(); - size_t new_size = LeanifyFile(input_file.GetFilePionter(), original_size, 0, filename); + size_t new_size = LeanifyFile(input_file.GetFilePionter(), original_size, 0, filename); - PrintSize(original_size); - cout << " -> "; - PrintSize(new_size); - cout << "\tLeanified: "; - PrintSize(original_size - new_size); + PrintSize(original_size); + cout << " -> "; + PrintSize(new_size); + cout << "\tLeanified: "; + PrintSize(original_size - new_size); - cout << " (" << 100 - 100.0 * new_size / original_size << "%)" << endl; + cout << " (" << 100 - 100.0 * new_size / original_size << "%)" << endl; - input_file.UnMapFile(new_size); - } + input_file.UnMapFile(new_size); + } - return 0; + return 0; } - -void PauseIfNotTerminal() -{ - // pause if Leanify is not started in terminal - // so that user can see the output instead of just a flash of a black box -#ifdef _WIN32 - if (is_pause) - { - system("pause"); - } -#endif // _WIN32 +void PauseIfNotTerminal() { +// pause if Leanify is not started in terminal +// so that user can see the output instead of just a flash of a black box +#ifdef _WIN32 + if (is_pause) + system("pause"); +#endif // _WIN32 } +void PrintInfo() { + cerr << "Leanify\t" << VERSION_STR << endl << endl; + cerr << "Usage: leanify [options] paths\n" + " -i, --iteration More iterations may produce better result, but\n" + " use more time, default is 15.\n" + " -d, --max_depth Maximum recursive depth, unlimited by default.\n" + " Set to 1 will disable recursive minifying.\n" + " -f, --fastmode Fast mode, no recompression.\n" + " -q, --quiet No output to stdout.\n" + " -v, --verbose Verbose output.\n" + " --keep-exif Do not remove Exif.\n" + " --keep-icc-profile Do not remove ICC profile.\n" + "\n" + "JPEG specific option:\n" + " --jpeg-keep-all-metadata Do not remove any metadata or comments in JPEG.\n" + " --jpeg-arithmetic-coding Use arithmetic coding for JPEG.\n" + "\n" + "ZIP specific option:\n" + " --zip-force-deflate Try deflate even if not compressed originally.\n"; - -void PrintInfo() -{ - cerr << "Leanify\t" << VERSION_STR << endl << endl; - cerr << "Usage: leanify [options] paths\n" - " -i, --iteration More iterations produce better result, but\n" - " use more time, default is 15.\n" - " -d, --max_depth Maximum recursive depth, unlimited by default.\n" - " Set to 1 will disable recursive minifying.\n" - " -f, --fastmode Fast mode, no recompression.\n" - " -q, --quiet No output to stdout.\n" - " -v, --verbose Verbose output.\n" - " --keep-exif Do not remove Exif.\n"; - PauseIfNotTerminal(); + PauseIfNotTerminal(); } - #ifdef _WIN32 -int main() -{ - int argc; - wchar_t **argv = CommandLineToArgvW(GetCommandLineW(), &argc); +int main() { + int argc; + wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc); #else -int main(int argc, char *argv[]) -{ -#endif // _WIN32 - - is_fast = false; - is_verbose = false; - iterations = 15; - depth = 1; - max_depth = INT_MAX; +int main(int argc, char** argv) { +#endif // _WIN32 -#ifdef _WIN32 - is_pause = !getenv("PROMPT"); -#endif // _WIN32 - - int i; - for (i = 1; i < argc && argv[i][0] == L'-'; i++) - { -#ifdef _WIN32 - // do not pause if any options are given - is_pause = false; -#endif // _WIN32 - int num_optargs = 0; - for (int j = 1; argv[i][j]; j++) - { - switch (argv[i][j]) - { - case 'f': - is_fast = true; - break; - case 'i': - if (i < argc - 1) - { - iterations = STRTOL(argv[i + ++num_optargs], nullptr, 10); - // strtol will return 0 on fail - if (iterations == 0) - { - cerr << "There should be a positive number after -i option." << endl; - PrintInfo(); - return 1; - } - } - break; - case 'd': - if (i < argc - 1) - { - max_depth = STRTOL(argv[i + ++num_optargs], nullptr, 10); - // strtol will return 0 on fail - if (max_depth == 0) - { - cerr << "There should be a positive number after -d option." << endl; - PrintInfo(); - return 1; - } - } - break; - case 'q': - cout.setstate(std::ios::failbit); - is_verbose = false; - break; - case 'v': - cout.clear(); - is_verbose = true; - break; - case '-': - if (STRCMP(argv[i] + j + 1, "fastmode") == 0) - { - j += 7; - argv[i][j + 1] = 'f'; - } - else if (STRCMP(argv[i] + j + 1, "iteration") == 0) - { - j += 8; - argv[i][j + 1] = 'i'; - } - else if (STRCMP(argv[i] + j + 1, "max_depth") == 0) - { - j += 8; - argv[i][j + 1] = 'd'; - } - else if (STRCMP(argv[i] + j + 1, "quiet") == 0) - { - j += 4; - argv[i][j + 1] = 'q'; - } - else if (STRCMP(argv[i] + j + 1, "verbose") == 0) - { - j += 6; - argv[i][j + 1] = 'v'; - } - else if (STRCMP(argv[i] + j + 1, "keep-exif") == 0) - { - j += 9; - Jpeg::keep_exif_ = true; - } - else - { -#ifdef _WIN32 - char mbs[64] = { 0 }; - WideCharToMultiByte(CP_ACP, 0, argv[i] + j + 1, -1, mbs, sizeof(mbs) - 1, nullptr, nullptr); - cerr << "Unknown option: " << mbs << endl; -#else - cerr << "Unknown option: " << argv[i] + j + 1 << endl; -#endif // _WIN32 - PrintInfo(); - return 1; - } - break; - default: - cerr << "Unknown option: " << (char)argv[i][j] << endl; - PrintInfo(); - return 1; + is_fast = false; + is_verbose = false; + iterations = 15; + depth = 1; + max_depth = INT_MAX; + +#ifdef _WIN32 + is_pause = !getenv("PROMPT"); +#endif // _WIN32 + + int i; + for (i = 1; i < argc && argv[i][0] == L'-'; i++) { +#ifdef _WIN32 + // do not pause if any options are given + is_pause = false; +#endif // _WIN32 + int num_optargs = 0; + for (int j = 1; argv[i][j]; j++) { + switch (argv[i][j]) { + case 'f': + is_fast = true; + break; + case 'i': + if (i < argc - 1) { + iterations = STRTOL(argv[i + ++num_optargs], nullptr, 10); + // strtol will return 0 on fail + if (iterations == 0) { + cerr << "There should be a positive number after -i option." << endl; + PrintInfo(); + return 1; } - } - i += num_optargs; - } - - if (i == argc) - { - cerr << "No file path provided." << endl; - PrintInfo(); - return 1; - } - - - cout << std::fixed; - cout.precision(2); - - // support multiple input file - do - { - if (IsDirectory(argv[i])) - { - // directory - TraverseDirectory(argv[i], ProcessFile); - } - else - { - // file - ProcessFile(argv[i]); - } - + } + break; + case 'd': + if (i < argc - 1) { + max_depth = STRTOL(argv[i + ++num_optargs], nullptr, 10); + // strtol will return 0 on fail + if (max_depth == 0) { + cerr << "There should be a positive number after -d option." << endl; + PrintInfo(); + return 1; + } + } + break; + case 'q': + cout.setstate(std::ios::failbit); + is_verbose = false; + break; + case 'v': + cout.clear(); + is_verbose = true; + break; + case '-': + if (STRCMP(argv[i] + j + 1, "fastmode") == 0) { + j += 7; + argv[i][j + 1] = 'f'; + } else if (STRCMP(argv[i] + j + 1, "iteration") == 0) { + j += 8; + argv[i][j + 1] = 'i'; + } else if (STRCMP(argv[i] + j + 1, "max_depth") == 0) { + j += 8; + argv[i][j + 1] = 'd'; + } else if (STRCMP(argv[i] + j + 1, "quiet") == 0) { + j += 4; + argv[i][j + 1] = 'q'; + } else if (STRCMP(argv[i] + j + 1, "verbose") == 0) { + j += 6; + argv[i][j + 1] = 'v'; + } else if (STRCMP(argv[i] + j + 1, "keep-exif") == 0) { + j += 9; + Jpeg::keep_exif_ = true; + } else if (STRCMP(argv[i] + j + 1, "keep-icc-profile") == 0) { + j += 16; + Jpeg::keep_icc_profile_ = true; + Png::keep_icc_profile_ = true; + } else if (STRCMP(argv[i] + j + 1, "jpeg-keep-all-metadata") == 0) { + j += 22; + Jpeg::keep_all_metadata_ = true; + } else if (STRCMP(argv[i] + j + 1, "jpeg-arithmetic-coding") == 0) { + j += 22; + Jpeg::force_arithmetic_coding_ = true; + } else if (STRCMP(argv[i] + j + 1, "zip-force-deflate") == 0) { + j += 17; + Zip::force_deflate_ = true; + } else { +#ifdef _WIN32 + char mbs[64] = { 0 }; + WideCharToMultiByte(CP_ACP, 0, argv[i] + j + 1, -1, mbs, sizeof(mbs) - 1, nullptr, nullptr); + cerr << "Unknown option: " << mbs << endl; +#else + cerr << "Unknown option: " << argv[i] + j + 1 << endl; +#endif // _WIN32 + PrintInfo(); + return 1; + } + break; + default: + cerr << "Unknown option: " << (char)argv[i][j] << endl; + PrintInfo(); + return 1; + } + } + i += num_optargs; + } + + if (i == argc) { + cerr << "No file path provided." << endl; + PrintInfo(); + return 1; + } + + cout << std::fixed; + cout.precision(2); + + // support multiple input file + do { + if (IsDirectory(argv[i])) { + // directory + TraverseDirectory(argv[i], ProcessFile); + } else { + // file + ProcessFile(argv[i]); } - while (++i < argc); + } while (++i < argc); - PauseIfNotTerminal(); + PauseIfNotTerminal(); - return 0; -} \ No newline at end of file + return 0; +} diff -Nru leanify-0.4.3/main.h leanify-0.4.3+git20181014/main.h --- leanify-0.4.3/main.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/main.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,20 +1,19 @@ -#ifndef MAIN_H -#define MAIN_H - +#ifndef MAIN_H_ +#define MAIN_H_ #ifdef _WIN32 #define STRTOL wcstol -#define STRCMP(X, Y) wcscmp(X, L ## Y) +#define STRCMP(X, Y) wcscmp(X, L##Y) #else #define STRTOL strtol #define STRCMP strcmp -#endif // _WIN32 +#endif // _WIN32 bool is_fast; bool is_verbose; #ifdef _WIN32 bool is_pause; -#endif // _WIN32 +#endif // _WIN32 // iteration of zopfli int iterations; @@ -24,7 +23,4 @@ int depth; int max_depth; - - - -#endif \ No newline at end of file +#endif // MAIN_H_ diff -Nru leanify-0.4.3/Makefile leanify-0.4.3+git20181014/Makefile --- leanify-0.4.3/Makefile 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/Makefile 2018-07-03 15:36:27.000000000 +0000 @@ -1,48 +1,52 @@ -LEANIFY_SRC := $(wildcard *.cpp formats/*.cpp) -LZMA_SRC := lib/LZMA/Alloc.c lib/LZMA/LzFind.c lib/LZMA/LzmaDec.c lib/LZMA/LzmaEnc.c lib/LZMA/LzmaLib.c -MINIZ_SRC := lib/miniz/miniz.c -MOZJPEG_SRC := lib/mozjpeg/jaricom.c lib/mozjpeg/jcapimin.c lib/mozjpeg/jcarith.c lib/mozjpeg/jcext.c lib/mozjpeg/jchuff.c lib/mozjpeg/jcmarker.c lib/mozjpeg/jcmaster.c lib/mozjpeg/jcomapi.c lib/mozjpeg/jcparam.c lib/mozjpeg/jcphuff.c lib/mozjpeg/jctrans.c lib/mozjpeg/jdapimin.c lib/mozjpeg/jdarith.c lib/mozjpeg/jdatadst.c lib/mozjpeg/jdatasrc.c lib/mozjpeg/jdcoefct.c lib/mozjpeg/jdcolor.c lib/mozjpeg/jddctmgr.c lib/mozjpeg/jdhuff.c lib/mozjpeg/jdinput.c lib/mozjpeg/jdmainct.c lib/mozjpeg/jdmarker.c lib/mozjpeg/jdmaster.c lib/mozjpeg/jdphuff.c lib/mozjpeg/jdpostct.c lib/mozjpeg/jdsample.c lib/mozjpeg/jdtrans.c lib/mozjpeg/jerror.c lib/mozjpeg/jidctflt.c lib/mozjpeg/jidctint.c lib/mozjpeg/jidctred.c lib/mozjpeg/jmemmgr.c lib/mozjpeg/jmemnobs.c lib/mozjpeg/jsimd_none.c lib/mozjpeg/jutils.c -TINYXML_SRC := lib/tinyxml2/tinyxml2.cpp -ZOPFLI_SRC := lib/zopfli/hash.c lib/zopfli/squeeze.c lib/zopfli/gzip_container.c lib/zopfli/katajainen.c lib/zopfli/zopfli_lib.c lib/zopfli/cache.c lib/zopfli/zlib_container.c lib/zopfli/util.c lib/zopfli/tree.c lib/zopfli/deflate.c lib/zopfli/blocksplitter.c lib/zopfli/lz77.c -ZOPFLIPNG_SRC := lib/zopflipng/lodepng/lodepng.cpp lib/zopflipng/lodepng/lodepng_util.cpp lib/zopflipng/zopflipng_lib.cc - -CFLAGS += -Wall -O3 -msse2 -mfpmath=sse -ifneq ($(CC),clang) - CFLAGS += -flto +LEANIFY_SRC := leanify.cpp main.cpp utils.cpp $(wildcard formats/*.cpp) +LZMA_OBJ := lib/LZMA/Alloc.o lib/LZMA/LzFind.o lib/LZMA/LzmaDec.o lib/LZMA/LzmaEnc.o +MOZJPEG_OBJ := lib/mozjpeg/jaricom.o lib/mozjpeg/jcapimin.o lib/mozjpeg/jcarith.o lib/mozjpeg/jcext.o lib/mozjpeg/jchuff.o lib/mozjpeg/jcmarker.o lib/mozjpeg/jcmaster.o lib/mozjpeg/jcomapi.o lib/mozjpeg/jcparam.o lib/mozjpeg/jcphuff.o lib/mozjpeg/jctrans.o lib/mozjpeg/jdapimin.o lib/mozjpeg/jdarith.o lib/mozjpeg/jdatadst.o lib/mozjpeg/jdatasrc.o lib/mozjpeg/jdcoefct.o lib/mozjpeg/jdhuff.o lib/mozjpeg/jdinput.o lib/mozjpeg/jdmarker.o lib/mozjpeg/jdphuff.o lib/mozjpeg/jdtrans.o lib/mozjpeg/jerror.o lib/mozjpeg/jmemmgr.o lib/mozjpeg/jmemnobs.o lib/mozjpeg/jsimd_none.o lib/mozjpeg/jutils.o +PUGIXML_OBJ := lib/pugixml/pugixml.o +ZOPFLI_OBJ := lib/zopfli/hash.o lib/zopfli/squeeze.o lib/zopfli/gzip_container.o lib/zopfli/katajainen.o lib/zopfli/zopfli_lib.o lib/zopfli/cache.o lib/zopfli/zlib_container.o lib/zopfli/util.o lib/zopfli/tree.o lib/zopfli/deflate.o lib/zopfli/blocksplitter.o lib/zopfli/lz77.o +ZOPFLIPNG_OBJ := lib/zopflipng/lodepng/lodepng.o lib/zopflipng/lodepng/lodepng_util.o lib/zopflipng/zopflipng_lib.o + +CFLAGS += -Wall -Wextra -Wno-unused-parameter -Werror -O3 -msse2 -mfpmath=sse -fno-exceptions -flto +CPPFLAGS += -I./lib +CXXFLAGS += $(CFLAGS) -std=c++14 -fno-rtti +LDFLAGS += -flto + +ifeq ($(OS), Windows_NT) + SYSTEM := Windows +else + SYSTEM := $(shell uname -s) +endif + +# Gold linker only supports Linux +ifeq ($(SYSTEM), Linux) + LDFLAGS += -fuse-ld=gold endif -ifeq ($(shell uname -s),Darwin) - LDFLAGS += -liconv +ifeq ($(SYSTEM), Darwin) + LDLIBS += -liconv else + # -s is "obsolete" on mac LDFLAGS += -s endif +# Multithread in LZMA SDK is only supported on Windows +ifeq ($(SYSTEM), Windows) + LEANIFY_SRC += fileio_win.cpp + LZMA_OBJ += lib/LZMA/LzFindMt.o lib/LZMA/Threads.o +else + LEANIFY_SRC += fileio_linux.cpp + LZMA_CFLAGS := -D _7ZIP_ST +endif + .PHONY: leanify clean -leanify: lzma.a miniz.o mozjpeg.a tinyxml2.o zopfli.a zopflipng.a - $(CXX) $(CFLAGS) --std=c++11 $(LEANIFY_SRC) $^ $(LDFLAGS) -o $@ +leanify: $(LEANIFY_SRC) $(LZMA_OBJ) $(MOZJPEG_OBJ) $(PUGIXML_OBJ) $(ZOPFLI_OBJ) $(ZOPFLIPNG_OBJ) + $(CXX) $(CPPFLAGS) $(CXXFLAGS) $^ $(LDFLAGS) $(LDLIBS) -o $@ -miniz.o: $(MINIZ_SRC) - $(CC) $(CFLAGS) -Wno-strict-aliasing -c $? +$(LZMA_OBJ): CFLAGS += $(LZMA_CFLAGS) -Wno-empty-body -Wno-misleading-indentation -Wno-unknown-warning-option -tinyxml2.o: $(TINYXML_SRC) - $(CXX) $(CFLAGS) -c $? +$(MOZJPEG_OBJ): CFLAGS := $(filter-out -Wextra,$(CFLAGS)) -lzma.a: $(LZMA_SRC) - $(CC) $(CFLAGS) -D _7ZIP_ST -c $? - ar rcs $@ $(patsubst lib/LZMA/%.c,%.o,$^) - -mozjpeg.a: $(MOZJPEG_SRC) - $(CC) $(CFLAGS) -c $? - ar rcs $@ $(patsubst lib/mozjpeg/%.c,%.o,$^) - -zopfli.a: $(ZOPFLI_SRC) - $(CC) $(filter-out -flto -O3,$(CFLAGS)) -O2 -c $? - ar rcs $@ $(patsubst lib/zopfli/%.c,%.o,$^) - -zopflipng.a:$(ZOPFLIPNG_SRC) - $(CXX) $(CFLAGS) -c $? - ar rcs $@ lodepng.o lodepng_util.o zopflipng_lib.o +$(ZOPFLI_OBJ): CFLAGS += -Wno-unused-function clean: - rm -f *.o *.a leanify + rm -f $(LZMA_OBJ) $(MOZJPEG_OBJ) $(PUGIXML_OBJ) $(ZOPFLI_OBJ) $(ZOPFLIPNG_OBJ) leanify diff -Nru leanify-0.4.3/README.md leanify-0.4.3+git20181014/README.md --- leanify-0.4.3/README.md 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/README.md 2017-12-03 04:14:03.000000000 +0000 @@ -1,10 +1,10 @@ -Leanify [![Build Status](https://travis-ci.org/JayXon/Leanify.svg)](https://travis-ci.org/JayXon/Leanify) [![Windows Build status](https://ci.appveyor.com/api/projects/status/2p9i3ru8apwq2uic?svg=true)](https://ci.appveyor.com/project/JayXon/leanify) [![Download](https://img.shields.io/github/downloads/JayXon/Leanify/latest/total.svg)](https://github.com/JayXon/Leanify/releases) [![Latest Release](https://img.shields.io/github/release/JayXon/Leanify.svg)](https://github.com/JayXon/Leanify/releases/latest) [![GitHub license](https://img.shields.io/github/license/JayXon/Leanify.svg)](LICENSE) +Leanify [![Build Status](https://travis-ci.org/JayXon/Leanify.svg)](https://travis-ci.org/JayXon/Leanify) [![Windows Build status](https://ci.appveyor.com/api/projects/status/2p9i3ru8apwq2uic?svg=true)](https://ci.appveyor.com/project/JayXon/leanify) [![Download](https://img.shields.io/github/downloads/JayXon/Leanify/total.svg)](https://github.com/JayXon/Leanify/releases) [![Latest Release](https://img.shields.io/github/release/JayXon/Leanify.svg)](https://github.com/JayXon/Leanify/releases/latest) [![GitHub license](https://img.shields.io/github/license/JayXon/Leanify.svg)](LICENSE) ======= Leanify is a lightweight lossless file minifier/optimizer. It removes unnecessary data (debug information, comments, metadata, etc.) and recompress the file to reduce file size. It will not reduce image quality at all. -##Features +## Features * Support recursive minifying. (e.g. a [PNG] inside an [APK] inside a [ZIP]) * Support a wide variety of [file formats](#file-formats). @@ -15,87 +15,94 @@ * Ability to identify file format by its data instead of name. -##Disclaimer +## Disclaimer I'm not respossible for any consequence of using Leanify. **PLEASE BACKUP THE FILE BEFORE USING LEANIFY!** -##File Formats +## File Formats -####APK file (.apk) +#### APK file (.apk) It is based on [ZIP]. - + Note that modifying files inside `APK` will break digital signature. To install it, you'll have to sign it again. If you don't want to modify any files inside `APK`, use `-d 1` option. -####Comic book archive (.cbt, .cbz) +#### Comic book archive (.cbt, .cbz) `cbt` is based on [tar]. `cbz` is based on [ZIP]. -####Microsoft Office document 2007-2013 (.docx, .xlsx, .pptx) +#### Microsoft Office document 2007-2013 (.docx, .xlsx, .pptx) It is based on [XML] and [ZIP]. Office document 1997-2003 (.doc, .xls, .ppt) is not supported. -####Design Web Format (.dwf, dwfx) +#### Data URI (.html .htm .js .css) + +Looks for `data:image/*;base64` and leanify base64 encoded embedded image. + + +#### Design Web Format (.dwf, dwfx) It is based on [ZIP]. -####EPUB file (.epub) +#### EPUB file (.epub) It is based on [ZIP]. -####FictionBook (.fb2, .fb2.zip) +#### FictionBook (.fb2, .fb2.zip) It is based on [XML]. Leanify embedded images. -####GFT file (.gft) +#### GFT file (.gft) It's an image container format found in Tencent QQ. Leanify the image inside. -####gzip file (.gz, .tgz) +#### gzip file (.gz, .tgz) Leanify file inside and recompress deflate stream. - + Remove all optional section: `FEXTRA`, `FNAME`, `FCOMMENT`, `FHCRC`. -####Icon file (.ico) +#### Icon file (.ico) + +Convert 256x256 BMP to [PNG]. Leanify [PNG] inside, if any. -####Java archive (.jar) +#### Java archive (.jar) It is based on [ZIP]. -####JPEG image (.jpeg, .jpg, .jpe, .jif, .jfif, .jfi, .thm) +#### JPEG image (.jpeg, .jpg, .jpe, .jif, .jfif, .jfi, .thm) -Remove all application markers (e.g. `Exif`) and comments. +Remove all application markers (e.g. `Exif` (use `--keep-exif` to keep it), `ICC profile`, `XMP`) and comments. Optimize with `mozjpeg`. -####Lua object file (.lua, .luac) +#### Lua object file (.lua, .luac) Remove all debugging information: @@ -106,12 +113,12 @@ * Upvalue list -####OpenDocument (.odt, .ods, .odp, .odb, .odg, .odf) +#### OpenDocument (.odt, .ods, .odp, .odb, .odg, .odf) It is based on [XML] and [ZIP]. -####PE file (.exe, .dll, .sys, .ocx, .scr, .cpl) +#### PE file (.exe, .dll, .ocx, .scr, .cpl) Leanify embedded resource. @@ -122,10 +129,10 @@ Overlap `PE Header` and `DOS Header`. -####PNG image (.png, .apng) +#### PNG image (.png, .apng) Remove all ancillary chunks except for: - + * `tRNS`: transparent information * `fdAT`, `fcTL`, `acTL`: These chunks are used by `APNG` * `npTc`: Android 9Patch images (*.9.png) @@ -133,57 +140,63 @@ Optimize with `ZopfliPNG`. -####RDB archive (.rdb) +#### RDB archive (.rdb) It is an archive format found in Tencent QQ. Leanify all files inside. -####Flash file (.swf) +#### Flash file (.swf) Leanify embedded images. Recompress it with `LZMA`. - + Remove Metadata Tag. -####SVG image (.svg, .svgz) - +#### SVG image (.svg, .svgz) + It is based on [XML]. - + Remove metadata. +Shrink spaces in attributes. + Remove empty attributes. -####tar archive (.tar) +Remove empty text element and container element. + +#### tar archive (.tar) Leanify all files inside. -####XML document (.xml, .xsl, .xslt) +#### XML document (.xml, .xsl, .xslt) Remove all comments, unnecessary spaces, tabs, line breaks. -####XPInstall (.xpi) +#### XPInstall (.xpi) It is based on [ZIP]. -Note that modifying files inside `xpi` will break digital signature if exist. -To install it, you'll have to either delete the META-INF folder inside `xpi` or sign it again. +Note that modifying files inside `xpi` will break digital signature. +To install it, you'll have to sign it again. -####XPS document (.xps, .oxps) +#### XPS document (.xps, .oxps) It is based on [XML] and [ZIP]. -####ZIP archive (.zip) +#### ZIP archive (.zip) Leanify all files inside and recompress deflate stream using [Zopfli](https://github.com/google/zopfli). +Use `STORE` method if `DEFLATE` makes file larger. + Remove extra field in `Local file header`. Remove `Data descriptor structure`, write those information to `Local file header`. @@ -194,32 +207,15 @@ -##To do list - - -####BMP image (.bmp, .dib) - - -####GIF image (.gif) - - -####Microsoft Compound File Binary - - -####PDF document (.pdf) - - -##Downloads +## Downloads [Stable Releases](https://github.com/JayXon/Leanify/releases/) [Windows Nightly Build](https://ci.appveyor.com/project/JayXon/leanify) -[Linux Nightly Build](https://drone.io/github.com/JayXon/Leanify/files) - -##Usage +## Usage ``` Usage: leanify [options] paths @@ -234,21 +230,22 @@ ``` -##Compiling +## Compiling -####Windows +#### Windows -* Visual Studio 2015 or up +* Visual Studio 2015+ Use Leanify.vcxproj -* gcc 4.7 or up +* gcc 5+ - run build_gcc.bat + `build_gcc.bat` or `mingw32-make` -####Linux, Mac +#### Linux, Mac + gcc 5+ or clang 3.6+ ``` make ``` diff -Nru leanify-0.4.3/utils.cpp leanify-0.4.3+git20181014/utils.cpp --- leanify-0.4.3/utils.cpp 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/utils.cpp 2017-08-01 20:51:09.000000000 +0000 @@ -1,13 +1,10 @@ #include "utils.h" - -#include - #ifdef _WIN32 -#include // WideCharToMultiByte +#include // WideCharToMultiByte #else +#include // convert UTF16 to UTF8 on non Windows #include -#include // convert UTF16 to UTF8 on non Windows #endif using std::cout; @@ -16,31 +13,39 @@ // convert UNICODE string aka UTF16 string // to multi byte string aka UTF8 string -void UTF16toMBS(const wchar_t *u, size_t srclen, char *mbs, size_t dstlen) -{ +void UTF16toMBS(const wchar_t* u, size_t srclen, char* mbs, size_t dstlen) { #ifdef _WIN32 - WideCharToMultiByte(CP_ACP, 0, u, srclen / 2, mbs, dstlen, nullptr, nullptr); + WideCharToMultiByte(CP_ACP, 0, u, srclen / 2, mbs, dstlen, nullptr, nullptr); #else - iconv_t conv = iconv_open("UTF-8", "UTF-16"); - if (iconv(conv, (char **)&u, &srclen, &mbs, &dstlen) == (size_t) - 1) - { - perror("iconv"); - } - iconv_close(conv); -#endif // _WIN32 + iconv_t conv = iconv_open("UTF-8", "UTF-16"); + if (iconv(conv, (char**)&u, &srclen, &mbs, &dstlen) == (size_t)-1) + perror("iconv"); + iconv_close(conv); +#endif // _WIN32 } - -void PrintFileName(const char *name) -{ - for (int i = 1; i < depth; i++) - { - cout << "-> "; - } - cout << name << endl; +void PrintFileName(const string& name) { + for (int i = 1; i < depth; i++) + cout << "-> "; + cout << name << endl; } -void PrintFileName(const string& name) -{ - PrintFileName(name.c_str()); +// Shrink consecutive space, newline and tab in the given string to one space +// also removes preceding and trailing spaces +string ShrinkSpace(const char* str) { + string out_str; + while (*str) { + if (*str == ' ' || *str == '\n' || *str == '\t') { + do { + str++; + } while (*str == ' ' || *str == '\n' || *str == '\t'); + + if (*str == 0) + break; + if (!out_str.empty()) + out_str += ' '; + } + out_str += *str++; + } + return out_str; } diff -Nru leanify-0.4.3/utils.h leanify-0.4.3+git20181014/utils.h --- leanify-0.4.3/utils.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/utils.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,16 +1,49 @@ -#ifndef UTILS_H -#define UTILS_H - +#ifndef UTILS_H_ +#define UTILS_H_ #include #include +#include #include extern int depth; +extern bool is_verbose; + +#ifdef _MSC_VER +#define BSWAP32(x) _byteswap_ulong(x) +#elif defined __GNUC__ +#define BSWAP32(x) __builtin_bswap32(x) +#else +#define BSWAP32(x) _bswap(x) +#endif + +#ifdef _MSC_VER +#define PACK(...) __pragma(pack(push, 1)) __VA_ARGS__ __pragma(pack(pop)) +#elif defined __GNUC__ +#define PACK(...) __VA_ARGS__ __attribute__((__packed__)) +#endif -void UTF16toMBS(const wchar_t *u, size_t srclen, char *mbs, size_t dstlen); +void UTF16toMBS(const wchar_t* u, size_t srclen, char* mbs, size_t dstlen); -void PrintFileName(const char *name); void PrintFileName(const std::string& name); -#endif \ No newline at end of file +std::string ShrinkSpace(const char* value); + +template +void VerbosePrint(const T& t) { + if (!is_verbose) + return; + + std::cout << t << std::endl; +} + +template +void VerbosePrint(const T& t, const Args&... args) { + if (!is_verbose) + return; + + std::cout << t; + VerbosePrint(args...); +} + +#endif // UTILS_H_ diff -Nru leanify-0.4.3/version.h leanify-0.4.3+git20181014/version.h --- leanify-0.4.3/version.h 2015-11-26 06:11:59.000000000 +0000 +++ leanify-0.4.3+git20181014/version.h 2017-08-01 20:51:09.000000000 +0000 @@ -1,13 +1,13 @@ -#ifndef VERSION_H -#define VERSION_H +#ifndef VERSION_H_ +#define VERSION_H_ -#define VERSION_MAJOR 0 -#define VERSION_MINOR 4 -#define VERSION_BUILD 3 +#define VERSION_MAJOR 0 +#define VERSION_MINOR 4 +#define VERSION_BUILD 3 #define _STR(x) #x #define STR(x) _STR(x) #define VERSION_STR STR(VERSION_MAJOR) "." STR(VERSION_MINOR) "." STR(VERSION_BUILD) #define VERSION_NUM VERSION_MAJOR, VERSION_MINOR, VERSION_BUILD -#endif +#endif // VERSION_H_