AuroraRuntime/Source/IO/FS/FSCompress.cpp
Reece Wilson f43251c8fc [+] AuNet::ISocketChannelEventListener
[+] 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
2023-02-04 19:43:01 +00:00

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);
}
}