flatten 20260225

This commit is contained in:
Timothy Prepscius
2026-02-25 12:39:24 -05:00
commit fa54be052a
315 changed files with 49791 additions and 0 deletions

425
tjp/core/zip/Compress+zlib.cpp Executable file
View File

@@ -0,0 +1,425 @@
// TJP COPYRIGHT HEADER
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
#ifndef WIN32
# include <unistd.h>
# include <utime.h>
# include <sys/types.h>
# include <sys/stat.h>
#else
# include <direct.h>
# include <io.h>
#endif
#include <zlib/zlib.h>
#include "minizip/zip.h"
#ifdef WIN32
#include "minizip/iowin32.h"
#endif
#include <fstream>
#include <list>
#include <string>
#include "Compress.h"
#include "../file/File.h"
#include "../file/Path.h"
#include "../endian/Endian.h"
#include "MemoryBuffer.hpp"
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
#define WRITEBUFFERSIZE (16384)
#define MAXFILENAME (256)
using namespace tjp;
using namespace core::zip;
using namespace core;
#ifdef WIN32
uLong filetime(const std::string &f, tm_zip *tmzip, uLong *dt)
{
int ret = -1;
{
FILETIME ftLocal;
HANDLE hFind;
WIN32_FIND_DATA ff32;
hFind = FindFirstFile(f.c_str(),&ff32);
if (hFind != INVALID_HANDLE_VALUE)
{
FileTimeToLocalFileTime(&(ff32.ftLastWriteTime),&ftLocal);
FileTimeToDosDateTime(&ftLocal,((LPWORD)dt)+1,((LPWORD)dt)+0);
FindClose(hFind);
ret = 0;
}
}
return ret;
}
#else
uLong filetime(const char *f, tm_zip *tmzip, uLong *dt)
{
struct stat s; /* results of stat() */
struct tm* filedate;
time_t tm_t=0;
if (strcmp(f,"-")!=0)
{
char name[MAXFILENAME+1];
auto len = strlen(f);
if (len > MAXFILENAME)
len = MAXFILENAME;
strncpy(name, f,MAXFILENAME-1);
/* strncpy doesnt append the trailing NULL, of the string is too long. */
name[ MAXFILENAME ] = '\0';
if (name[len - 1] == '/')
name[len - 1] = '\0';
/* not all systems allow stat'ing a file with / appended */
if (stat(name,&s)==0)
{
tm_t = s.st_mtime;
}
else
return 1;
}
filedate = localtime(&tm_t);
tmzip->tm_sec = filedate->tm_sec;
tmzip->tm_min = filedate->tm_min;
tmzip->tm_hour = filedate->tm_hour;
tmzip->tm_mday = filedate->tm_mday;
tmzip->tm_mon = filedate->tm_mon ;
tmzip->tm_year = filedate->tm_year;
return 0;
}
#endif
/* calculate the CRC32 of a file,
because to encrypt a file, we need known the CRC32 of the file before */
int getFileCrc(const std::string &filenameinzip,unsigned long &result_crc)
{
const int size_buf = 16384;
char *buf = new char[size_buf];
uLong calculate_crc=0;
int err=ZIP_OK;
FILE * fin = fopen(filenameinzip.c_str(),"rb");
uInt size_read = 0;
unsigned long total_read = 0;
if (fin==NULL)
{
err = ZIP_ERRNO;
}
if (err == ZIP_OK)
do
{
err = ZIP_OK;
size_read = (int)fread(buf,1,size_buf,fin);
if (size_read < size_buf)
if (feof(fin)==0)
{
err = ZIP_ERRNO;
}
if (size_read>0)
calculate_crc = crc32(calculate_crc,(const Bytef *)buf,size_read);
total_read += size_read;
} while ((err == ZIP_OK) && (size_read>0));
if (fin)
fclose(fin);
result_crc=calculate_crc;
return err;
}
void compressFile(
zipFile zf,
const std::string &path,
const std::string &filename,
const char *password,
int opt_compress_level=Z_DEFAULT_COMPRESSION
)
{
std::string filenameinzip = path;
zip_fileinfo zi;
unsigned long crcFile=0;
zi.tmz_date.tm_sec = zi.tmz_date.tm_min = zi.tmz_date.tm_hour =
zi.tmz_date.tm_mday = zi.tmz_date.tm_mon = zi.tmz_date.tm_year = 0;
zi.dosDate = 0;
zi.internal_fa = 0;
zi.external_fa = 0;
if (filetime(filenameinzip.c_str(),&zi.tmz_date,&zi.dosDate) != ZIP_OK)
throw zip::Exception ("filetime failed " + filenameinzip);
if (password != NULL)
{
if (getFileCrc(filenameinzip,crcFile) != ZIP_OK)
throw zip::Exception ("getFileCrc");
}
std::string unixFileName = file::convertDelimiter(filename, file::LocalDirectoryDelimiter, file::ServerDirectoryDelimiter);
if (zipOpenNewFileInZip3(zf,unixFileName.c_str(),&zi,
NULL,0,NULL,0,NULL /* comment*/,
(opt_compress_level != 0) ? Z_DEFLATED : 0,
opt_compress_level,0,
/* -MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY, */
-MAX_WBITS, DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY,
password,crcFile)
!= ZIP_OK)
throw zip::Exception ("zipOpenNewFileInZip");
const int buffer_size = 16384;
char *buffer = new char[buffer_size];
try
{
std::ifstream fin (filenameinzip.c_str(), std::ios::binary);
if (!fin)
throw zip::Exception ("error opening file to compress");
int size_read;
do
{
fin.read (buffer, buffer_size);
size_read = (int)fin.gcount();
if (size_read>0)
{
if (zipWriteInFileInZip (zf,buffer,size_read) < 0)
throw zip::Exception ("zipWriteInFileInZip");
}
} while (fin);
}
catch (zip::Exception &e)
{
delete[] buffer;
zipCloseFileInZip(zf);
throw e;
}
if (zipCloseFileInZip(zf) != ZIP_OK)
throw zip::Exception ("zipCloseFileInZip");
}
void zip::compress (const std::string &file, const std::string &path, const std::list<std::string> &files)
{
zipFile zf;
#ifdef WIN32
zlib_filefunc_def ffunc;
fill_win32_filefunc(&ffunc);
zf = zipOpen2(file.c_str(),0,NULL,&ffunc);
#else
zf = zipOpen(file.c_str(),0);
#endif
if (!zf)
throw zip::Exception ("zipOpen");
std::list<std::string>::const_iterator i;
for (i=files.begin(); i!=files.end(); ++i)
{
compressFile (zf, path + *i, *i, NULL);
}
if (zipClose(zf,NULL) != ZIP_OK)
throw zip::Exception ("zipClose");
}
void zip::compress (const std::string &file, const std::map<std::string, std::string> &zipFileNameToDiskFileName)
{
zipFile zf;
#ifdef WIN32
zlib_filefunc_def ffunc;
fill_win32_filefunc(&ffunc);
zf = zipOpen2(file.c_str(),0,NULL,&ffunc);
#else
zf = zipOpen(file.c_str(),0);
#endif
if (!zf)
throw zip::Exception ("zipOpen");
std::map<std::string, std::string>::const_iterator i;
for (i=zipFileNameToDiskFileName.begin(); i!=zipFileNameToDiskFileName.end(); ++i)
{
compressFile (zf, i->second, i->first, NULL);
}
if (zipClose(zf,NULL) != ZIP_OK)
throw zip::Exception ("zipClose");
}
void zip::compress (const std::string &from, const std::string &to)
{
std::ifstream infile (from.c_str(), std::ios::binary);
int size_buf = 16384;
char *buf = new char[16384];
try
{
file::ensurePath (to);
gzFile gz = gzopen (to.c_str(), "wb");
if (gz == NULL)
throw zip::Exception ("gzopen");
int read = 0;
while (infile)
{
infile.read (buf, size_buf);
read = (int)infile.gcount();
if (gzwrite (gz, buf, read))
throw zip::Exception ("gzwrite");
}
}
catch (zip::Exception &e)
{
delete[] buf;
file::deleteFile (to);
throw e;
}
}
typedef unsigned int Header;
bool zip::compress (const char *in, int insize, char *&out, int &outsize, int level)
{
uLongf ulInSize = insize;
uLongf ulOutSize = insize - sizeof(Header);
char *block = new char[insize];
Header *header = (Header *)block;
char *data = block + sizeof (Header);
int error = ::compress2((Bytef*)data, &ulOutSize, (const Bytef*)in, ulInSize, level);
if ((error!=Z_OK))
{
delete[] block;
return false;
}
*header = (Header)ulInSize;
endian::swap (*header);
out = block;
outsize = int(ulOutSize + sizeof(Header));
//-----------------------------------------------------
/*
char *temp;
int tempsize;
decompress (out, outsize, temp, tempsize);
assert (tempsize == insize);
assert (memcmp (in, temp, tempsize)==0);
delete[] temp;
*/
//-----------------------------------------------------
return true;
}
bool zip::compress (const char *in, int insize, std::vector<char> &out, int level)
{
uLongf ulInSize = insize;
uLongf ulOutSize = insize - sizeof(Header);
out.resize(insize);
char *block = out.data();
Header *header = (Header *)block;
char *data = block + sizeof (Header);
int error = ::compress2((Bytef*)data, &ulOutSize, (const Bytef*)in, ulInSize, level);
if ((error!=Z_OK))
{
delete[] block;
return false;
}
*header = (Header)ulInSize;
endian::swap (*header);
out.resize(int(ulOutSize + sizeof(Header)));
//-----------------------------------------------------
/*
char *temp;
int tempsize;
decompress (out, outsize, temp, tempsize);
assert (tempsize == insize);
assert (memcmp (in, temp, tempsize)==0);
delete[] temp;
*/
//-----------------------------------------------------
return true;
}
std::vector<char> zip::compress(const char *in, int insize, int level)
{
std::vector<char> result;
zip::compress(in, insize, result, level);
return result;
}
Vector<char> zip::compress (const std::string &path, const std::list<std::string> &files)
{
MemoryBuffer buffer({});
zipFile zf = zipOpen2("dummy.zip", APPEND_STATUS_CREATE, nullptr, &buffer.funcs);
if (!zf)
throw zip::Exception ("zipOpen");
std::list<std::string>::const_iterator i;
for (i=files.begin(); i!=files.end(); ++i)
{
auto file = path + *i;
sLogDebug("debug", logVar(file));
compressFile (zf, file, *i, NULL);
}
if (zipClose(zf,NULL) != ZIP_OK)
throw zip::Exception ("zipClose");
return buffer.buffer;
}

56
tjp/core/zip/Compress+zstd.cpp Executable file
View File

@@ -0,0 +1,56 @@
// TJP COPYRIGHT HEADER
#include "Compress.h"
#include "Decompress.h"
#include <zstd/zstd.h>
using namespace tjp;
using namespace core::zstd;
using namespace core;
std::vector<char> zstd::compress(const char *in, int insize, int level)
{
// Get maximum compressed size
size_t maxCompressedSize = ZSTD_compressBound(insize);
// Allocate output buffer
std::vector<char> result(maxCompressedSize);
// Compress the input
size_t compressedSize = ZSTD_compress(result.data(), maxCompressedSize, in, insize, level);
// Check for compression error
if (ZSTD_isError(compressedSize)) {
result.clear();
return result;
}
// Resize result to actual compressed size
result.resize(compressedSize);
return result;
}
std::vector<char> zstd::decompress(const char *in, int insize)
{
// Get decompressed size from the frame header
unsigned long long decompressedSize = ZSTD_getFrameContentSize(in, insize);
if (decompressedSize == ZSTD_CONTENTSIZE_ERROR || decompressedSize == ZSTD_CONTENTSIZE_UNKNOWN) {
return {};
}
// Allocate output buffer
std::vector<char> result(decompressedSize);
// Decompress
size_t actualSize = ZSTD_decompress(result.data(), decompressedSize, in, insize);
// Check for decompression error
if (ZSTD_isError(actualSize)) {
return {};
}
return result;
}

40
tjp/core/zip/Compress.h Executable file
View File

@@ -0,0 +1,40 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Exception.h"
#include <tjp/core/containers/Vector.h>
#include <list>
#include <vector>
#include <map>
namespace tjp {
namespace core {
namespace zip {
/**
* decompresses the given file to the given path, sets the files to the set of files
* found
*/
void compress (const std::string &file, const std::string &path, const std::list<std::string> &files);
void compress (const std::string &file, const std::map<std::string, std::string> &zipFileNameToDiskFileName);
void compress (const std::string &from, const std::string &to);
Vector<char> compress (const std::string &path, const std::list<std::string> &files);
bool compress (const char *from, int fromSize, char *&to, int &toSize, int level=9);
bool compress (const char *from, int fromSize, std::vector<char> &to, int level=9);
std::vector<char> compress (const char *from, int fromSize, int level=9);
} // namespace
namespace zstd {
std::vector<char> compress (const char *from, int fromSize, int level=9);
}
} //
} //

276
tjp/core/zip/Decompress.cpp Executable file
View File

@@ -0,0 +1,276 @@
// TJP COPYRIGHT HEADER
#include "Decompress.h"
#include <assert.h>
#include <list>
#include "../endian/Endian.h"
#include "../file/File.h"
#include <fstream>
#include <tjp/core/log/Log.h>
#include "../file/Path.h"
#include <cstring>
#include <zlib/zlib.h>
#include "minizip/ioapi.h"
#ifdef WIN32
#include "minizip/iowin32.h"
#endif
using namespace tjp;
using namespace core;
using namespace core::zip;
#include "minizip/unzip.h"
void extractFile(
unzFile uf,
const char *password,
const std::string &basedirectory,
std::string &relativefilename
)
{
unz_file_info file_info;
char filename_inzip[256];
int err;
err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
if (err!=UNZ_OK)
{
throw zip::Exception ("unzGetCurrentFileInfo");
}
relativefilename = filename_inzip;
std::string filename = basedirectory + file::convertDelimiter (relativefilename);
file::ensurePath (filename);
const int size_buf = 16384;
char *buf = new char [size_buf];
try
{
std::ofstream outfile (filename.c_str(), std::ios::binary);
if (unzOpenCurrentFilePassword(uf,password)!=UNZ_OK)
{
throw zip::Exception ("unzOpenCurrentFilePassword");
}
int len;
do
{
len = unzReadCurrentFile(uf,buf,size_buf);
if (len<0)
{
throw zip::Exception("unzReadCurrentFile");
}
else
if (len>0)
{
if (!outfile.write (buf, len))
{
throw zip::Exception("error in writing extracted file\n");
}
}
}
while (len>0);
}
catch (zip::Exception &e)
{
file::deleteFile(filename);
unzCloseCurrentFile (uf);
delete[] buf;
throw e;
}
delete[] buf;
// change_file_date(filename,file_info.dosDate,
// file_info.tmu_date);
err = unzCloseCurrentFile (uf);
if (err!=UNZ_OK)
{
throw zip::Exception("error %d with zipfile in unzCloseCurrentFile");
}
}
void extractZip(
unzFile uf,
const char* password,
const std::string &basedirectory,
std::list<std::string> &list
)
{
uLong i;
unz_global_info gi;
int err;
err = unzGetGlobalInfo (uf,&gi);
if (err!=UNZ_OK)
throw zip::Exception("unzGetGlobalInfo");
for (i=0;i<gi.number_entry;i++)
{
std::string filename;
extractFile(uf, password, basedirectory, filename);
list.push_back(filename);
if ((i+1)<gi.number_entry)
{
err = unzGoToNextFile(uf);
if (err!=UNZ_OK)
throw zip::Exception ("unzGoToNextFile");
}
}
}
void extractOneFile(
unzFile uf,
const std::string &filename,
const char* password,
const std::string &basedirectory
)
{
if (unzLocateFile(uf,filename.c_str(),false)!=UNZ_OK)
throw zip::Exception ("unzLocateFile");
std::string dummyfilename;
extractFile(uf,password,basedirectory,dummyfilename);
}
void zip::decompress (const std::string &file, const std::string &path, std::list<std::string> &files)
{
#ifdef WIN32
zlib_filefunc_def ffunc;
#endif
char filename_try[256+1];
strncpy(filename_try, file.c_str(),256-1);
/* strncpy doesnt append the trailing NULL, if the string is too long. */
filename_try[256] = '\0';
#ifdef WIN32
fill_win32_filefunc(&ffunc);
unzFile uf = unzOpen2(filename_try,&ffunc);
#else
unzFile uf = unzOpen(filename_try);
#endif
if (uf==NULL)
{
throw zip::Exception ("unzOpen");
}
try
{
extractZip (uf,NULL, path, files);
}
catch (zip::Exception &)
{
unzCloseCurrentFile(uf);
throw;
}
unzCloseCurrentFile(uf);
}
void zip::decompress (const std::string &from, const std::string &to)
{
gzFile gz = gzopen (from.c_str(), "rb");
if (gz == NULL)
throw zip::Exception ("gzopen");
int size_buf = 16384;
char *buf = new char[16384];
try
{
file::ensurePath (to);
std::ofstream outfile (to.c_str(), std::ios::binary);
int read = 0;
while ( (read = gzread (gz, buf, size_buf) > 0) && outfile)
{
outfile.write (buf, read);
}
if (read < 0 && !outfile)
{
throw zip::Exception ("gzread");
}
}
catch (zip::Exception &e)
{
delete[] buf;
file::deleteFile (to);
throw e;
}
}
typedef unsigned int Header;
bool zip::decompress (const char *in, int insize, char *&out, int &outsize)
{
const char *block = in;
const Header *header = (const Header *)block;
const char *data = block + sizeof (Header);
endian::swap (*header);
uLongf ulInSize = insize - sizeof (Header);
uLongf ulOutSize = *header;
char *uncompressed = new char[ulOutSize];
int error = ::uncompress((Bytef *)uncompressed, &ulOutSize, (const Bytef*)data, ulInSize);
if (error!=Z_OK)
{
delete[] uncompressed;
return false;
}
out = uncompressed;
outsize = (int)ulOutSize;
return true;
}
bool zip::decompress (const char *in, int insize, std::vector<char> &out)
{
const char *block = in;
const Header *header = (const Header *)block;
const char *data = block + sizeof (Header);
endian::swap (*header);
uLongf ulInSize = insize - sizeof (Header);
uLongf ulOutSize = *header;
out.resize(ulOutSize);
char *uncompressed = out.data();
int error = ::uncompress((Bytef *)uncompressed, &ulOutSize, (const Bytef*)data, ulInSize);
if (error!=Z_OK)
{
delete[] uncompressed;
return false;
}
return true;
}
std::vector<char> zip::decompress (const char *in, int insize)
{
std::vector<char> out;
zip::decompress(in, insize, out);
return out;
}

38
tjp/core/zip/Decompress.h Executable file
View File

@@ -0,0 +1,38 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Exception.h"
#include <list>
#include <vector>
namespace tjp {
namespace core {
namespace zip {
/**
* decompresses the given file to the given path, sets the files to the set of files
* found
*/
void decompress (const std::string &file, const std::string &path, std::list<std::string> &files);
void decompress (const std::string &from, const std::string &to);
bool decompress (const char *from, int fromSize, char *&to, int &toSize);
bool decompress (const char *from, int fromSize, std::vector<char> &to);
std::vector<char> decompress (const char *from, int fromSize);
} // namespace
namespace zstd {
std::vector<char> decompress (const char *from, int fromSize);
}
} // namespace
} // namespace

13
tjp/core/zip/Exception.h Executable file
View File

@@ -0,0 +1,13 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <tjp/core/exception/Exception.hpp>
namespace tjp::core::zip {
DECLARE_EXCEPTION (Exception);
} // namespace Zip

101
tjp/core/zip/MemoryBuffer.cpp Executable file
View File

@@ -0,0 +1,101 @@
// TJP COPYRIGHT HEADER
#include "MemoryBuffer.hpp"
#include <cstring>
namespace tjp::core::zip {
// Custom read function for zlib
static voidpf ZCALLBACK mem_open(voidpf opaque, const char* filename, int mode) {
return opaque; // Opaque is our MemoryBuffer
}
int ZCALLBACK mem_close(voidpf opaque, voidpf stream) {
// Nothing to do here since std::vector manages memory automatically
return ZIP_OK;
}
static uLong ZCALLBACK mem_read(voidpf opaque, voidpf stream, void* buf, uLong size) {
MemoryBuffer* mem = static_cast<MemoryBuffer*>(opaque);
size_t remaining = mem->buffer.size() - mem->position;
size_t toRead = (remaining < size) ? remaining : size;
if (toRead > 0) {
std::memcpy(buf, mem->buffer.data() + mem->position, toRead);
mem->position += toRead;
}
return static_cast<uLong>(toRead);
}
static long ZCALLBACK mem_tell(voidpf opaque, voidpf stream) {
MemoryBuffer* mem = static_cast<MemoryBuffer*>(opaque);
return static_cast<long>(mem->position);
}
static long ZCALLBACK mem_seek(voidpf opaque, voidpf stream, uLong offset, int origin) {
MemoryBuffer* mem = static_cast<MemoryBuffer*>(opaque);
size_t new_pos = mem->position;
if (origin == ZLIB_FILEFUNC_SEEK_CUR) {
new_pos += offset;
} else if (origin == ZLIB_FILEFUNC_SEEK_END) {
new_pos = mem->buffer.size() + offset;
} else if (origin == ZLIB_FILEFUNC_SEEK_SET) {
new_pos = offset;
}
if (new_pos > mem->buffer.size()) {
return -1; // Seeking beyond the buffer
}
mem->position = new_pos;
return 0; // Success
}
uLong ZCALLBACK mem_write(voidpf opaque, voidpf stream, const void* buf, uLong size)
{
MemoryBuffer* mem = static_cast<MemoryBuffer*>(opaque);
const char *data = (const char *)buf;
if (mem->buffer.size() < mem->position)
return -1;
auto remaining = mem->buffer.size() - mem->position;
auto wm = std::min(remaining, size);
if (wm > 0)
std::memcpy(
mem->buffer.data() + mem->position,
data,
wm
);
if (size > wm)
mem->buffer.insert(
mem->buffer.end(),
data + wm,
data + (size - wm)
);
mem->position += size;
return size;
}
MemoryBuffer::MemoryBuffer(Vector<char> &&contents) :
buffer(std::move(contents)),
funcs{0}
{
funcs.zopen_file = mem_open;
funcs.zwrite_file = mem_write;
funcs.ztell_file = mem_tell;
funcs.zclose_file = mem_close;
funcs.zread_file = mem_read;
// funcs.zerror_file = mem_error;
funcs.zseek_file = mem_seek;
funcs.opaque = this;
} //
} // namespace

11
tjp/core/zip/MemoryBuffer.h Executable file
View File

@@ -0,0 +1,11 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp::core::zip {
// Custom memory structure
struct MemoryBuffer;
} //

25
tjp/core/zip/MemoryBuffer.hpp Executable file
View File

@@ -0,0 +1,25 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "Exception.h"
#include <tjp/core/containers/Vector.h>
#include <zlib/zlib.h>
#include "minizip/zip.h"
namespace tjp::core::zip {
// Custom memory structure
struct MemoryBuffer
{
Vector<char> buffer;
size_t position = 0;
zlib_filefunc_def funcs;
MemoryBuffer(Vector<char> &&);
};
} //

View File

@@ -0,0 +1,138 @@
// TJP COPYRIGHT HEADER
#include "VirtualFileSystem.hpp"
#include <zlib/zlib.h>
#include "minizip/ioapi.h"
#ifdef WIN32
#include "minizip/iowin32.h"
#endif
#include "minizip/unzip.h"
#include "../zip/MemoryBuffer.hpp"
#include <tjp/core/exception/Exception.hpp>
#include <tjp/core/debug/debug_break.h>
namespace tjp::core::zip {
using Contents = file::Contents;
using ContentsView = file::ContentsView;
struct VirtualFileSystem::I
{
zip::MemoryBuffer memoryBuffer;
unzFile uf = nullptr;
I(Contents &&contents);
~I();
Contents get(const StringView &fileName);
void put(const StringView &fileName, ContentsView);
bool has(const StringView &fileName);
} ;
VirtualFileSystem::I::I(Contents &&contents) :
memoryBuffer(std::move(contents))
{
uf = unzOpen2("dummy.zip", &memoryBuffer.funcs);
}
VirtualFileSystem::I::~I()
{
if (uf)
unzClose(uf);
uf = nullptr;
}
Contents VirtualFileSystem::I::get(const StringView &fileName)
{
// debug_break();
if (unzLocateFile(uf,fileName.data(),false)!=UNZ_OK)
throw Exception { "File does not exist" };
unz_file_info file_info;
char filename_inzip[256];
int err;
err = unzGetCurrentFileInfo(uf,&file_info,filename_inzip,sizeof(filename_inzip),NULL,0,NULL,0);
if (err!=UNZ_OK)
{
throw Exception ("unzGetCurrentFileInfo");
}
Contents contents;
contents.resize(file_info.uncompressed_size);
try
{
if (unzOpenCurrentFile(uf) != UNZ_OK)
throw Exception { "Open failure" };
auto len = unzReadCurrentFile(uf, contents.data(), contents.size());
if (len != contents.size())
throw Exception { "Read error" };
unzCloseCurrentFile (uf);
return contents;
}
catch (Exception &e)
{
unzCloseCurrentFile (uf);
throw e;
}
}
bool VirtualFileSystem::I::has(const StringView &fileName)
{
return unzLocateFile(uf,fileName.data(),false) == UNZ_OK;
}
void VirtualFileSystem::I::put(const StringView &fileName, ContentsView)
{
throw Exception { "Not implemented" };
}
VirtualFileSystem::VirtualFileSystem(Contents &&contents) :
i(new I(std::move(contents)))
{
}
Contents VirtualFileSystem::get(const StringView &path) const
{
return i->get(path);
}
Optional<Contents> VirtualFileSystem::get_if(const StringView &path) const
{
try
{
return i->get(path);
}
catch(...)
{
}
return {};
}
void VirtualFileSystem::put(const StringView &path, ContentsView contents)
{
return i->put(path, contents);
}
bool VirtualFileSystem::has(const StringView &path) const
{
return i->has(path);
}
} // namespace

View File

@@ -0,0 +1,9 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp::core::zip {
struct VirtualFileSystem;
} // namespace

View File

@@ -0,0 +1,32 @@
// TJP COPYRIGHT HEADER
#pragma once
#include "../file/VirtualFileSystem.hpp"
#include <tjp/core/exception/Exception.hpp>
namespace tjp::core::zip {
struct VirtualFileSystem : file::VirtualFileSystem
{
using Super = file::VirtualFileSystem;
struct I;
mutable I *i;
VirtualFileSystem(file::Contents &&contents);
inline
String pathFor(const StringView &path) const override
{
throw Exception { "No Implemented" };
}
file::Contents get(const StringView &path) const override;
Optional<file::Contents> get_if(const StringView &path) const override;
void put(const StringView &path, file::ContentsView contents) override;
bool has(const StringView &fileName) const override;
} ;
} // namespace

View File

@@ -0,0 +1,232 @@
// TJP COPYRIGHT HEADER
#include "Compression.hpp"
#include <zlib/zlib.h>
#include <tjp/core/threads/Lock.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/algorithm/mem_copy.hpp>
namespace tjp::core::zip {
// ----------------------
CompressorOneShot::CompressorOneShot(int level_) :
level(level_)
{
}
bool CompressorOneShot::operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
const Bytef *source = (const Bytef *)from;
uLongf sourceLen = (uLongf)fromSize;
Bytef *dest = (Bytef *)to;
uLongf destLen = (uLongf)toSize;
auto result = compress2(dest, &destLen, source, sourceLen, level) == Z_OK;
toSize = destLen;
return result;
}
bool DecompressorOneShot::operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
const Bytef *source = (const Bytef *)from;
uLongf sourceLen = fromSize;
Bytef *dest = (Bytef *)to;
uLongf destLen = toSize;
auto result = ::uncompress(dest, &destLen, source, sourceLen) == Z_OK;
toSize = destLen;
return result;
}
// ---------------------
Compressor::Compressor(int level_) :
level(level_),
i(nullptr)
{
}
void *Compressor::allocateStream()
{
auto z = (z_streamp)calloc(1, sizeof(z_stream));
deflateInit(z, level);
return z;
}
void Compressor::deallocateStream(void *z)
{
free(z);
}
bool Compressor::operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
auto strm = (z_streamp)i;
strm->next_in = (Bytef*)from;
strm->avail_in = (uInt)fromSize;
strm->next_out = (Bytef*)to;
strm->avail_out = (uInt)toSize;
auto result = deflate(strm, Z_FINISH);
toSize = strm->total_out;
return result == Z_STREAM_END;
}
// ------------
Decompressor::Decompressor() :
i(nullptr)
{
}
void *Decompressor::allocateStream()
{
auto z = (z_streamp)calloc(1, sizeof(z_stream));
inflateInit(z);
return z;
}
void Decompressor::deallocateStream(void *z)
{
free(z);
}
bool Decompressor::operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
auto strm = (z_streamp)i;
strm->next_in = (Bytef*)from;
strm->avail_in = (uInt)fromSize;
strm->next_out = (Bytef*)to;
strm->avail_out = (uInt)toSize;
auto result = inflate(strm, Z_FINISH);
toSize = strm->total_out;
return result == Z_STREAM_END;
}
// ------------
struct Pool
{
Mutex mutex;
Vector<z_stream *> pooled;
z_stream *allocate();
void release(z_stream *);
} ;
z_stream * Pool::allocate()
{
auto lock = lock_of(mutex);
if (pooled.empty())
return nullptr;
auto result = pooled.back();
pooled.pop_back();
return result;
}
void Pool::release(z_stream *z)
{
auto lock = lock_of(mutex);
pooled.push_back(z);
}
static Pool deflators[10];
static Pool inflators;
void *getPoolStream(int level)
{
if (level == -1)
return (void *)inflators.allocate();
if (level > 9)
level = 9;
return (void *)deflators[level].allocate();
}
void putPoolStream(int level, void *s)
{
if (level == -1)
return inflators.release((z_streamp)s);
if (level > 9)
level = 9;
return deflators[level].release((z_streamp)s);
}
// --------------
bool setDict(void *p, int level, char *dict, size_t size)
{
auto z = (z_streamp)p;
int error = 0;
if (level < 0)
error = inflateSetDictionary(z, (const Bytef *)dict, (uInt)size);
else
error = deflateSetDictionary(z, (const Bytef *)dict, (uInt)size);
return error == Z_OK;
}
bool getDict(void *p, int level, char *dict, size_t size)
{
auto z = (z_streamp)p;
auto size_ = (uInt)size;
char store[32768];
int error = 0;
if (level < 0)
error = inflateGetDictionary(z, (Bytef *)&store[0], &size_);
else
error = deflateGetDictionary(z, (Bytef *)&store[0], &size_);
// zlib seems to store from end to front
auto offset =
size_ > size ?
size_ - size :
0;
mem_copy(dict, &store[offset], size);
return error == Z_OK;
}
// ----------------
bool clearStream(void *p, int level)
{
auto z = (z_streamp)p;
if (level < 0)
return (inflateReset(z) == Z_OK);
else
return (deflateReset(z) == Z_OK);
}
} // namespace

View File

@@ -0,0 +1,18 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp {
namespace core {
namespace zip {
/**
* decompresses the given file to the given path, sets the files to the set of files
* found
*/
struct Compressor;
} // namespace
} //
} //

View File

@@ -0,0 +1,267 @@
// TJP COPYRIGHT HEADER
#pragma once
#include <stddef.h>
#include <utility>
#include <string.h>
namespace tjp::core::zip {
using Data = char;
using Size = size_t;
/**
* decompresses the given file to the given path, sets the files to the set of files
* found
*/
struct Operator
{
virtual ~Operator() {}
virtual char getID() = 0;
virtual bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize) = 0;
} ;
template<typename T>
struct VirtualOperator : Operator
{
using Operator = T;
Operator op;
template<typename ... Args>
VirtualOperator(Args&& ... args) :
op(std::forward<Args>(args)...)
{}
char getID() override
{
return op.getID();
}
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize) override
{
return op.operate(from, fromSize, to, toSize);
}
} ;
struct CompressorOneShot
{
CompressorOneShot(int level_);
int level;
bool enabled();
char getID();
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize);
} ;
struct DecompressorOneShot
{
char getID();
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize);
} ;
struct Compressor
{
Compressor(int level_);
void setExecutor(void *);
void *getExecutor();
void *i;
int level;
int getLevel();
bool enabled();
char getID();
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize);
void *allocateStream();
void deallocateStream(void *);
} ;
struct Decompressor
{
Decompressor();
void *i;
void setExecutor(void *);
void *getExecutor();
char getID();
int getLevel();
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize);
void *allocateStream();
void deallocateStream(void *);
} ;
template<typename T>
struct Chain
{
using Next = T;
Next next;
template<typename ... Args>
Chain(Args&& ... args) :
next(std::forward<Args>(args)...)
{}
auto setExecutor(void *i)
{
next.setExecutor(i);
}
auto getExecutor()
{
return next.getExecutor();
}
auto enabled()
{
return next.enabled();
}
auto getLevel()
{
return next.getLevel();
}
auto getID()
{
return next.getID();
}
auto operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
return next.operate(from, fromSize, to, toSize);
}
void *allocateStream()
{
return next.allocateStream();
}
void deallocateStream(void *s)
{
next.deallocateStream(s);
}
} ;
template<typename T>
struct RetainMemory : Chain<T>
{
using Super = Chain<T>;
template<typename ... Args>
RetainMemory(Args&& ... args) :
Super(std::forward<Args>(args)...)
{
Super::setExecutor(Super::allocateStream());
}
~RetainMemory()
{
Super::deallocateStream(Super::getExecutor());
}
} ;
bool clearStream(void *p, int level);
template<typename T>
struct Clear : Chain<T>
{
using Super = Chain<T>;
template<typename ... Args>
Clear(Args&& ... args) :
Super(std::forward<Args>(args)...)
{
}
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
if (!clearStream(Super::getExecutor(), Super::getLevel()))
return false;
return Super::operate(from, fromSize, to, toSize);
}
} ;
void *getPoolStream(int level);
void putPoolStream(int level, void *);
template<typename T>
struct MemoryPool : Chain<T>
{
using Super = Chain<T>;
template<typename ... Args>
MemoryPool(Args&& ... args) :
Super(std::forward<Args>(args)...)
{
}
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
auto l = Super::getLevel();
auto s = getPoolStream(l);
if (!s)
s = Super::allocateStream();
Super::setExecutor(s);
auto result = Super::operate(from, fromSize, to, toSize);
Super::setExecutor(nullptr);
putPoolStream(l, s);
return result;
}
} ;
bool setDict(void *p, int level, char *dict, size_t size);
bool getDict(void *p, int level, char *dict, size_t size);
template<typename T>
struct MiniDict : Chain<T>
{
using Super = Chain<T>;
static constexpr size_t DictSize = 256;
using Dict = char[DictSize];
Dict dict;
template<typename ... Args>
MiniDict(Args&& ... args) :
Super(std::forward<Args>(args)...)
{
memset(dict, 0, sizeof(dict));
}
bool operate(const Data *from, Size fromSize, Data *&to, Size &toSize)
{
if (!setDict(Super::getExecutor(), Super::getLevel(), dict, DictSize))
return false;
auto result = Super::operate(from, fromSize, to, toSize);
if (!getDict(Super::getExecutor(), Super::getLevel(), dict, DictSize))
return false;
return result;
}
} ;
using StandardCompressor = MemoryPool<Clear<Compressor>>;
using StandardDecompressor = MemoryPool<Clear<Decompressor>>;
} // namespace
#include "Compression.inl"

View File

@@ -0,0 +1,82 @@
// TJP COPYRIGHT HEADER
#pragma once
namespace tjp::core::zip {
inline
bool CompressorOneShot::enabled()
{
return level > 0;
}
inline
bool Compressor::enabled()
{
return level > 0;
}
inline
char CompressorOneShot::getID()
{
return 1;
}
inline
char DecompressorOneShot::getID()
{
return 1;
}
inline
char Compressor::getID()
{
return 2;
}
inline
int Compressor::getLevel()
{
return level;
}
inline
void Compressor::setExecutor(void *i_)
{
i = i_;
}
inline
void *Compressor::getExecutor()
{
return i;
}
inline
char Decompressor::getID()
{
return 2;
}
inline
void Decompressor::setExecutor(void *i_)
{
i = i_;
}
inline
void *Decompressor::getExecutor()
{
return i;
}
inline
int Decompressor::getLevel()
{
return -1;
}
} // namespace

View File

@@ -0,0 +1,165 @@
// TJP COPYRIGHT HEADER
#include <tjp/core/zip/_delete/Compression.hpp>
#include <tjp/core/testing/catch.hpp>
#include <tjp/core/containers/Vector.hpp>
#include <tjp/core/random/Random.h>
#include <tjp/core/testing/NO_WHEN.h>
#include <tjp/core/log/Log.h>
#include <tjp/core/log/LogOf.h>
namespace tjp::core::zip {
SCENARIO("zip::compression")
{
GIVEN("CompressorOneShot")
{
Vector<char> source, compressed, uncompressed;
source.resize(90);
compressed.resize(100);
uncompressed.resize(100,1);
CompressorOneShot compressor(5);
DecompressorOneShot decompressor;
auto compressedSize = compressed.size();
auto compressedData = compressed.data();
auto didCompress = compressor.operate(
source.data(), source.size(),
compressedData, compressedSize
);
REQUIRE(didCompress);
auto uncompressedSize = uncompressed.size();
auto uncompressedData = uncompressed.data();
auto didDecompress = decompressor.operate(
compressed.data(), compressedSize,
uncompressedData, uncompressedSize
);
REQUIRE(didDecompress);
uncompressed.resize(uncompressedSize);
auto same = source == uncompressed;
REQUIRE(uncompressedSize == source.size());
REQUIRE(same);
}
GIVEN("RetainMemory")
{
Vector<char> source, compressed, uncompressed;
source.resize(90);
compressed.resize(100);
uncompressed.resize(100,1);
RetainMemory<Compressor> compressor(5);
RetainMemory<Decompressor> decompressor;
auto compressedSize = compressed.size();
auto compressedData = compressed.data();
auto didCompress = compressor.operate(
source.data(), source.size(),
compressedData, compressedSize
);
REQUIRE(didCompress);
auto uncompressedSize = uncompressed.size();
auto uncompressedData = uncompressed.data();
auto didDecompress = decompressor.operate(
compressed.data(), compressedSize,
uncompressedData, uncompressedSize
);
REQUIRE(didDecompress);
uncompressed.resize(uncompressedSize);
auto same = source == uncompressed;
REQUIRE(uncompressedSize == source.size());
REQUIRE(same);
}
GIVEN("MemoryPool")
{
Vector<char> source, compressed, uncompressed;
source.resize(90);
compressed.resize(100);
for (auto i=0; i<16; ++i)
{
uncompressed.clear();
uncompressed.resize(100,1);
MemoryPool<Clear<Compressor>> compressor((i % 4) + 1);
MemoryPool<Clear<Decompressor>> decompressor;
auto compressedSize = compressed.size();
auto compressedData = compressed.data();
auto didCompress = compressor.operate(
source.data(), source.size(),
compressedData, compressedSize
);
REQUIRE(didCompress);
auto uncompressedSize = uncompressed.size();
auto uncompressedData = uncompressed.data();
auto didDecompress = decompressor.operate(
compressed.data(), compressedSize,
uncompressedData, uncompressedSize
);
REQUIRE(didDecompress);
uncompressed.resize(uncompressedSize);
auto same = source == uncompressed;
REQUIRE(uncompressedSize == source.size());
REQUIRE(same);
}
}
NO_GIVEN("MiniDict")
{
Vector<char> source, compressed, uncompressed;
source.resize(90);
compressed.resize(100);
for (auto i=0; i<90; ++i)
source[i] = i%13;
MemoryPool<Clear<MiniDict<Compressor>>> compressor(5);
MemoryPool<Clear<MiniDict<Decompressor>>> decompressor;
int lastCompression = 1000000;
for (auto i=0; i<16; ++i)
{
uncompressed.clear();
uncompressed.resize(100,1);
auto compressedSize = compressed.size();
auto compressedData = compressed.data();
auto didCompress = compressor.operate(
source.data(), source.size(),
compressedData, compressedSize
);
REQUIRE(didCompress);
sLogTest("testing", logVar(compressedSize) << logVar(lastCompression));
REQUIRE(compressedSize < lastCompression);
lastCompression = compressedSize;
auto uncompressedSize = uncompressed.size();
auto uncompressedData = uncompressed.data();
auto didDecompress = decompressor.operate(
compressed.data(), compressedSize,
uncompressedData, uncompressedSize
);
REQUIRE(didDecompress);
uncompressed.resize(uncompressedSize);
auto same = source == uncompressed;
REQUIRE(uncompressedSize == source.size());
REQUIRE(same);
}
}
}
} // namespace