Reece Wilson
f43251c8fc
[+] AuFS::UpdateTimes [+] AuFS::UpdateFileTimes [+] AuFS::CompressEx [*] AuFS::Compress now rejects files that look to be already compressed [+] AuFS::DecompressEx [+] AuFS::Create [+] AuFS::WriteNewFile [+] AuFS::WriteNewString [+] AuFs::FileAttrsList [+] AuFs::FileAttrsGet [+] AuFs::FileAttrsSet [+] DirectoryLogger::uMaxLogsOrZeroBeforeCompress [+] ISocketChannel.AddEventListener [+] ISocketChannel.AddEventListener [+] DirectoryLogger.uMaxLogsOrZeroBeforeCompress [*] Fix UNIX regression [*] Fix up stream socket channel realloc IPC [*] Fix shutdown regression in pretty much everything thanks to 8ff81df1's dumbass fix (fixes fence regression on shutdown) [*] Fix DirDeleterEx formatting of reported failed paths [*] Fix up file not truncated if already exists bugs. Extended and alternative apis added. [*] Fix ICompressionStream::ReadEx returning the wrong read value [+] Legacy compression API can now self-correct once newer stream processor objects are added
260 lines
7.8 KiB
C++
260 lines
7.8 KiB
C++
/***
|
|
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
|
|
|
|
File: FSCompress.cpp
|
|
Date: 2023-1-26
|
|
Author: Reece
|
|
***/
|
|
#include <Source/RuntimeInternal.hpp>
|
|
#include "FS.hpp"
|
|
#include "FS.Generic.hpp"
|
|
#include <Source/Time/Time.hpp>
|
|
|
|
namespace Aurora::IO::FS
|
|
{
|
|
static const AuUInt64 kFileCopyBlock = 0xFFFF * 4; // 64KiB
|
|
static auto const kCompressionType = AuCompression::ECompressionType::eZSTD;
|
|
static const AuString kStringSuffix = ".zst";
|
|
|
|
static bool CompressEx2(const AuString &path, const AuString &suffix, AuCompression::ECompressionType type, AuInt8 level, bool bCheck)
|
|
{
|
|
static const auto kCompressionReadChunks = kFileCopyBlock;
|
|
|
|
if (suffix.empty())
|
|
{
|
|
SysPushErrorInvalidArgPos2();
|
|
return {};
|
|
}
|
|
|
|
if (bCheck)
|
|
{
|
|
if (AuEndsWith(path, suffix))
|
|
{
|
|
SysPushErrorIO("File path (\"{}\") ends in {}, yet was requested to be compressed. A user or a developer is probably being stupid.", path, suffix);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
auto pFileSrc = OpenReadShared(path, EFileAdvisoryLockLevel::eBlockWrite);
|
|
if (!pFileSrc)
|
|
{
|
|
SysPushErrorIO("Couldn't open compression source path: {}", path);
|
|
return {};
|
|
}
|
|
|
|
auto pFileDest = OpenWriteShared(path + suffix, EFileAdvisoryLockLevel::eBlockReadWrite);
|
|
if (!pFileDest)
|
|
{
|
|
SysPushErrorIO("Couldn't open compression destination path: {}{}", path, suffix);
|
|
return {};
|
|
}
|
|
|
|
if (pFileDest->GetLength())
|
|
{
|
|
SysPushErrorIO("File ({}) exists", path);
|
|
return {};
|
|
}
|
|
|
|
auto qwLength = pFileSrc->GetLength();
|
|
|
|
auto pFileStream = AuMakeShared<AuIO::FS::FileReader>(pFileSrc);
|
|
if (!pFileStream)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
auto pDestStream = AuMakeShared<AuIO::FS::FileWriter>(pFileDest);
|
|
if (!pDestStream)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
AuCompression::CompressInfo compress { type };
|
|
compress.uCompressionLevel = level;
|
|
compress.uInternalStreamSize = kCompressionReadChunks * 2;
|
|
auto pCompressor = AuCompression::CompressorUnique(pFileStream, compress);
|
|
if (!pCompressor)
|
|
{
|
|
SysPushErrorMemory("no compressor");
|
|
return {};
|
|
}
|
|
|
|
AuUInt64 qwTotalRead {};
|
|
{
|
|
AuByteBuffer tempMemory(kCompressionReadChunks);
|
|
if (!tempMemory)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
for (AuUInt64 i = 0; i < qwLength; i += 0 /*kCompressionReadChunks*/)
|
|
{
|
|
auto [read, written] = pCompressor->Ingest(kCompressionReadChunks);
|
|
i += read;
|
|
|
|
if (read == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
qwTotalRead += read;
|
|
|
|
bool bAnyWritten {};
|
|
while (auto uBytes = pCompressor->Read(tempMemory))
|
|
{
|
|
tempMemory.writePtr += uBytes;
|
|
bAnyWritten = true;
|
|
|
|
AuUInt idc {};
|
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
|
{
|
|
SysPushErrorIO("AuIO::WriteAll failed");
|
|
return {};
|
|
}
|
|
|
|
tempMemory.writePtr = tempMemory.base;
|
|
tempMemory.readPtr = tempMemory.base;
|
|
}
|
|
|
|
// zstd u ok?
|
|
// if i remove this, we get no data at all.
|
|
// even if i try to displace this to above `->Finish()` we still get nothing
|
|
// preemptively flushing fixes everything /shrug
|
|
pCompressor->Flush();
|
|
}
|
|
|
|
pCompressor->Finish();
|
|
|
|
while (auto uBytes = pCompressor->Read(tempMemory))
|
|
{
|
|
tempMemory.writePtr += uBytes;
|
|
|
|
AuUInt idc {};
|
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
|
{
|
|
SysPushErrorIO("AuIO::WriteAll failed");
|
|
return {};
|
|
}
|
|
|
|
tempMemory.writePtr = tempMemory.base;
|
|
tempMemory.readPtr = tempMemory.base;
|
|
}
|
|
}
|
|
|
|
return qwTotalRead == qwLength;
|
|
}
|
|
|
|
AUKN_SYM bool CompressEx(const AuString &path, const AuString &suffix, AuCompression::ECompressionType type, AuInt8 level)
|
|
{
|
|
return CompressEx2(path, kStringSuffix, kCompressionType, level, false);
|
|
}
|
|
|
|
AUKN_SYM bool Compress(const AuString &path, AuInt8 level)
|
|
{
|
|
return CompressEx2(path, kStringSuffix, kCompressionType, level, true);
|
|
}
|
|
|
|
AUKN_SYM bool DecompressEx(const AuString &path, const AuString &suffix, AuCompression::ECompressionType type)
|
|
{
|
|
static const auto kCompressionReadChunks = kFileCopyBlock;
|
|
|
|
if (suffix.empty())
|
|
{
|
|
SysPushErrorInvalidArgPos2();
|
|
return {};
|
|
}
|
|
|
|
auto pFileSrc = OpenReadShared(path + suffix, EFileAdvisoryLockLevel::eBlockWrite);
|
|
if (!pFileSrc)
|
|
{
|
|
SysPushErrorIO("Couldn't open compression source path: {}{}", path, suffix);
|
|
return {};
|
|
}
|
|
|
|
auto pFileDest = OpenWriteShared(path, EFileAdvisoryLockLevel::eBlockReadWrite);
|
|
if (!pFileDest)
|
|
{
|
|
SysPushErrorIO("Couldn't open decompression destination path: {}", path);
|
|
return {};
|
|
}
|
|
|
|
if (pFileDest->GetLength())
|
|
{
|
|
SysPushErrorIO("File exists path: {}", path);
|
|
return {};
|
|
}
|
|
|
|
auto qwLength = pFileSrc->GetLength();
|
|
|
|
auto pFileStream = AuMakeShared<AuIO::FS::FileReader>(pFileSrc);
|
|
if (!pFileStream)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
auto pDestStream = AuMakeShared<AuIO::FS::FileWriter>(pFileDest);
|
|
if (!pDestStream)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
AuCompression::DecompressInfo decompress{ type };
|
|
decompress.uInternalStreamSize = kCompressionReadChunks * 5;
|
|
auto pDecompressor = AuCompression::DecompressorUnique(pFileStream, decompress);
|
|
if (!pDecompressor)
|
|
{
|
|
SysPushErrorMemory("no decompressor");
|
|
return {};
|
|
}
|
|
|
|
AuUInt64 qwTotalRead {};
|
|
{
|
|
AuByteBuffer tempMemory(kCompressionReadChunks);
|
|
if (!tempMemory)
|
|
{
|
|
SysPushErrorMemory();
|
|
return {};
|
|
}
|
|
|
|
for (AuUInt64 i = 0; i < qwLength; i += 0 /*kCompressionReadChunks*/)
|
|
{
|
|
auto [read, written] = pDecompressor->Ingest(kCompressionReadChunks);
|
|
i += read;
|
|
|
|
if (read == 0)
|
|
{
|
|
break;
|
|
}
|
|
|
|
qwTotalRead += read;
|
|
|
|
while (auto uBytes = pDecompressor->Read(tempMemory))
|
|
{
|
|
tempMemory.writePtr += uBytes;
|
|
|
|
AuUInt idc {};
|
|
if (AuIO::WriteAll(pDestStream.get(), { tempMemory, idc }) != AuIO::EStreamError::eErrorNone)
|
|
{
|
|
SysPushErrorIO("AuIO::WriteAll failed");
|
|
return {};
|
|
}
|
|
|
|
tempMemory.writePtr = tempMemory.base;
|
|
tempMemory.readPtr = tempMemory.base;
|
|
}
|
|
}
|
|
}
|
|
|
|
return qwTotalRead == qwLength;
|
|
}
|
|
|
|
AUKN_SYM bool Decompress(const AuString &path)
|
|
{
|
|
return DecompressEx(path, kStringSuffix, kCompressionType);
|
|
}
|
|
} |