Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions 3rdparty/clipzipplugin/clipzipplugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,8 +190,12 @@ PluginFinishType CliPzipPlugin::addFiles(const QList<FileEntry> &files, const Co
// 静默模式
arguments << "-q";

arguments << "-l" << "1";
Q_UNUSED(options.iCompressionLevel);
// 压缩等级:0=Store(不压缩),1-9=deflate 压缩级别
int level = options.iCompressionLevel;
if (level < 0 || level > 9) {
level = 1;
}
arguments << "-l" << QString::number(level);

// 线程数:只有大于1时才指定,否则让 pzip 自动使用全部 CPU 核心
if (options.iCPUTheadNum > 1) {
Expand Down
2 changes: 1 addition & 1 deletion src/pzip/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ target_link_libraries(pzip_core_lib ${ZLIB_LIBRARIES} Threads::Threads)
target_compile_features(pzip_core_lib PUBLIC cxx_std_17)

if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64|arm")
target_compile_options(pzip_core_lib PRIVATE -O3 -funroll-loops)
target_compile_options(pzip_core_lib PRIVATE -O3 -funroll-loops -march=armv8-a+crc)
else()
target_compile_options(pzip_core_lib PRIVATE -O3 -mtune=generic -funroll-loops)
endif()
Expand Down
1 change: 1 addition & 0 deletions src/pzip/include/pzip/file_task.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ class FileTask {
ZipFileHeader header; // ZIP 头信息
bool isSymlink = false; // 是否是符号链接
std::string symlinkTarget; // 符号链接目标路径
bool streamFromSource = false; // Store 模式:写入时直接从源文件读取,跳过缓冲

// 压缩器(由 Archiver 管理)
z_stream* compressor = nullptr;
Expand Down
31 changes: 25 additions & 6 deletions src/pzip/src/archiver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
//
// SPDX-License-Identifier: GPL-3.0-or-later

#include "pzip/archiver.h"

Check warning on line 6 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "pzip/archiver.h" not found.
#include "pzip/fast_deflate.h"

Check warning on line 7 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "pzip/fast_deflate.h" not found.
#include "pzip/utils.h"

Check warning on line 8 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: "pzip/utils.h" not found.
#include <algorithm>

Check warning on line 9 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <algorithm> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <fstream>

Check warning on line 10 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <fstream> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <cstring>

Check warning on line 11 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <cstring> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <sys/stat.h>

Check warning on line 12 in src/pzip/src/archiver.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <sys/stat.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <zlib.h>

#ifdef USE_LIBDEFLATE
Expand Down Expand Up @@ -196,15 +197,28 @@
return Error(ErrorCode::FILE_OPEN_ERROR, "Cannot open file: " + task->path.string());
}

FlateWriter writer([task](const uint8_t* data, size_t size) {
task->write(data, size);
});
const bool useStore = (options_.compressionLevel == 0);

constexpr size_t BUFFER_SIZE = 32 * 1024;
std::vector<uint8_t> buf(BUFFER_SIZE);
uint32_t crc = 0;
uint64_t totalBytesRead = 0;

if (useStore) {
// Store 模式:跳过读文件,写入 ZIP 时直接从源文件流式读取并计算 CRC
file.close();
task->streamFromSource = true;
return Error();
}

// Deflate 模式:使用 FlateWriter 按指定等级压缩
auto level = static_cast<CompressionLevel>(
std::clamp(options_.compressionLevel, 1, 9));

FlateWriter writer([task](const uint8_t* data, size_t size) {
task->write(data, size);
}, level);

while (file.good() && !file.eof()) {
file.read(reinterpret_cast<char*>(buf.data()), buf.size());
auto bytesRead = file.gcount();
Expand All @@ -215,13 +229,13 @@
}
}

// 检查 I/O 错误(bad() 表示严重错误,fail() 在 eof 时也会设置所以需要排除)
writer.close();

if (file.bad()) {
file.close();
return Error(ErrorCode::FILE_READ_ERROR, "I/O error reading file: " + task->path.string());
}

// 检查是否读取了完整文件(防止静默截断)
if (totalBytesRead != task->fileSize) {
file.close();
return Error(ErrorCode::FILE_READ_ERROR,
Expand All @@ -232,7 +246,6 @@
file.close();

task->header.crc32 = crc;
writer.close();

return Error();
}
Expand Down Expand Up @@ -286,6 +299,12 @@
h.compressedSize = task->symlinkTarget.size();
// 设置 Unix 符号链接属性
h.externalAttr = static_cast<uint32_t>(S_IFLNK | 0777) << 16;
} else if (options_.compressionLevel == 0) {
// Store 模式:不压缩,CRC 在写入阶段边读边算,用 DATA_DESCRIPTOR 后置
h.method = ZIP_METHOD_STORE;
h.flags |= ZIP_FLAG_DATA_DESCRIPTOR;
h.uncompressedSize = task->fileSize;
h.compressedSize = task->fileSize;
} else {
h.method = ZIP_METHOD_DEFLATE;
h.flags |= ZIP_FLAG_DATA_DESCRIPTOR;
Expand Down
43 changes: 40 additions & 3 deletions src/pzip/src/file_task.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ FileTask::FileTask(FileTask&& other) noexcept
, status(other.status)
, fileSize(other.fileSize)
, header(std::move(other.header))
, isSymlink(other.isSymlink)
, symlinkTarget(std::move(other.symlinkTarget))
, streamFromSource(other.streamFromSource)
, compressor(other.compressor)
, buffer_(std::move(other.buffer_))
, bufferUsed_(other.bufferUsed_)
Expand All @@ -44,6 +47,8 @@ FileTask::FileTask(FileTask&& other) noexcept
, written_(other.written_)
{
other.compressor = nullptr;
other.isSymlink = false;
other.streamFromSource = false;
other.bufferUsed_ = 0;
other.written_ = 0;
}
Expand All @@ -54,6 +59,9 @@ FileTask& FileTask::operator=(FileTask&& other) noexcept {
status = other.status;
fileSize = other.fileSize;
header = std::move(other.header);
isSymlink = other.isSymlink;
symlinkTarget = std::move(other.symlinkTarget);
streamFromSource = other.streamFromSource;
compressor = other.compressor;
buffer_ = std::move(other.buffer_);
bufferUsed_ = other.bufferUsed_;
Expand All @@ -62,6 +70,8 @@ FileTask& FileTask::operator=(FileTask&& other) noexcept {
written_ = other.written_;

other.compressor = nullptr;
other.isSymlink = false;
other.streamFromSource = false;
other.bufferUsed_ = 0;
other.written_ = 0;
}
Expand All @@ -84,6 +94,7 @@ Error FileTask::reset(const fs::path& filePath, const fs::path& relativeTo) {
written_ = 0;
isSymlink = false;
symlinkTarget.clear();
streamFromSource = false;

// 设置新文件信息
path = filePath;
Expand Down Expand Up @@ -184,26 +195,52 @@ size_t FileTask::write(const uint8_t* data, size_t size) {
);

if (!overflow_->is_open()) {
overflow_.reset();
return totalWritten;
}
}

overflow_->write(reinterpret_cast<const char*>(data), size);
totalWritten += size;
if (overflow_ && overflow_->is_open()) {
overflow_->write(reinterpret_cast<const char*>(data), size);
if (overflow_->good()) {
totalWritten += size;
}
}
}

written_ += totalWritten;
return totalWritten;
}

void FileTask::readCompressedData(std::function<void(const uint8_t*, size_t)> callback) {
// Store 模式:直接从源文件流式读取,边读边算 CRC
if (streamFromSource) {
std::ifstream file(path, std::ios::binary);
if (!file.is_open()) return;

uint32_t crc = 0;
std::vector<uint8_t> readBuf(READ_BUFFER_SIZE);
while (file.good() && !file.eof()) {
file.read(reinterpret_cast<char*>(readBuf.data()), readBuf.size());
auto bytesRead = file.gcount();
if (bytesRead > 0) {
crc = utils::crc32Update(crc, readBuf.data(), bytesRead);
callback(readBuf.data(), bytesRead);
}
}
header.crc32 = crc;
return;
}

// 先读取内存缓冲区
if (bufferUsed_ > 0) {
callback(buffer_.data(), bufferUsed_);
}

// 再读取溢出文件
if (overflow_) {
if (overflow_ && overflow_->is_open()) {
overflow_->flush();
overflow_->clear();
overflow_->seekg(0, std::ios::beg);

std::vector<uint8_t> readBuf(READ_BUFFER_SIZE);
Expand Down
70 changes: 70 additions & 0 deletions src/pzip/src/utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,20 @@
#include <utime.h>
#include <sstream>
#include <iomanip>
#include <cstring>

Check warning on line 12 in src/pzip/src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <cstring> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <random>

Check warning on line 13 in src/pzip/src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <random> not found. Please note: Cppcheck does not need standard library headers to get proper results.

#if defined(__aarch64__)
#include <sys/auxv.h>

Check warning on line 16 in src/pzip/src/utils.cpp

View workflow job for this annotation

GitHub Actions / cppcheck

Include file: <sys/auxv.h> not found. Please note: Cppcheck does not need standard library headers to get proper results.
#include <asm/hwcap.h>
#ifdef __ARM_FEATURE_CRC32
#include <arm_acle.h>
#define PZIP_HAS_ARM_CRC32_COMPILE 1
#else
#define PZIP_HAS_ARM_CRC32_COMPILE 0
#endif
#endif

namespace pzip {
namespace utils {

Expand Down Expand Up @@ -97,11 +108,70 @@
return fs::path(result);
}

#if defined(__aarch64__) && PZIP_HAS_ARM_CRC32_COMPILE
static uint32_t crc32_arm_hw(uint32_t crc, const uint8_t* data, size_t size) {
uint32_t c = crc ^ 0xFFFFFFFF;

// 逐字节对齐到 8 字节边界
while (size > 0 && (reinterpret_cast<uintptr_t>(data) & 7)) {
c = __crc32b(c, *data++);
size--;
}

// 每次处理 8 字节
while (size >= 64) {
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 8));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 16));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 24));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 32));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 40));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 48));
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data + 56));
data += 64;
size -= 64;
}

while (size >= 8) {
c = __crc32d(c, *reinterpret_cast<const uint64_t*>(data));
data += 8;
size -= 8;
}

while (size > 0) {
c = __crc32b(c, *data++);
size--;
}

return c ^ 0xFFFFFFFF;
}

static bool hasArmCrc32() {
static bool checked = false;
static bool supported = false;
if (!checked) {
supported = (getauxval(AT_HWCAP) & HWCAP_CRC32) != 0;
checked = true;
}
return supported;
}
#endif

uint32_t crc32(const uint8_t* data, size_t size) {
#if defined(__aarch64__) && PZIP_HAS_ARM_CRC32_COMPILE
if (hasArmCrc32()) {
return crc32_arm_hw(0, data, size);
}
#endif
return ::crc32(0L, data, size);
}

uint32_t crc32Update(uint32_t crc, const uint8_t* data, size_t size) {
#if defined(__aarch64__) && PZIP_HAS_ARM_CRC32_COMPILE
if (hasArmCrc32()) {
return crc32_arm_hw(crc, data, size);
}
#endif
return ::crc32(crc, data, size);
}

Expand Down
14 changes: 8 additions & 6 deletions src/pzip/src/zip_writer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -257,16 +257,14 @@ Error ZipWriter::createRaw(const ZipFileHeader& header,
return Error(ErrorCode::FILE_OPEN_ERROR, "File not open");
}

// 保存本地文件头偏移
CentralDirEntry entry;
entry.header = header;
entry.localHeaderOffset = currentOffset_;
// 记录本地文件头偏移
uint64_t localHeaderOffset = currentOffset_;

// 写入本地文件头
Error err = writeLocalFileHeader(header);
if (err) return err;

// 写入压缩数据
// 写入压缩数据(dataProvider 可能会更新 header 中的 CRC 等字段)
dataProvider([this](const uint8_t* data, size_t size) {
file_.write(reinterpret_cast<const char*>(data), size);
currentOffset_ += size;
Expand All @@ -276,12 +274,16 @@ Error ZipWriter::createRaw(const ZipFileHeader& header,
return Error(ErrorCode::FILE_WRITE_ERROR, "Failed to write compressed data");
}

// 如果使用数据描述符,写入它
// 如果使用数据描述符,写入它(此时 header.crc32 已在 dataProvider 中更新)
if (header.flags & ZIP_FLAG_DATA_DESCRIPTOR) {
err = writeDataDescriptor(header);
if (err) return err;
}

// dataProvider 运行后再拷贝 header,确保 CRC 等字段是最新值
CentralDirEntry entry;
entry.header = header;
entry.localHeaderOffset = localHeaderOffset;
centralDir_.push_back(entry);
return Error();
}
Expand Down