Skip to content

Optimize 7z extraction performance and fix UTF-16LE filename handling#2

Open
SongOnWater wants to merge 2 commits into
wgh136:masterfrom
SongOnWater:master
Open

Optimize 7z extraction performance and fix UTF-16LE filename handling#2
SongOnWater wants to merge 2 commits into
wgh136:masterfrom
SongOnWater:master

Conversation

@SongOnWater
Copy link
Copy Markdown

Summary

Performance optimization and bug fixes for the 7z extraction engine.

Changes

Performance (major speedup for solid archives)

  • C++: Promoted blockIndex/cached outBuffer from local variables to Archive class members. The 7z SDK uses both as cache keys — without this, each extractToDir call re-decompressed the entire solid block. Measured improvement: ~17 min → 15.9 sec for a 192MB / 74-file archive on Android emulator.

New API

  • C: Added extractFileToDir(void* archive, uint32_t index, const char* outputDir) — combines file info lookup, directory creation, and extraction into one native call, eliminating redundant FFI round-trips.
  • Dart: Added SZArchive.extractToDir(int index, String outputDir)bool (returns true if entry was a directory).

Bug fixes

  • UTF-16LE → UTF-8: Replaced reinterpret_cast<const wchar_t*> (broken on Android/Linux where wchar_t is 4 bytes) with proper UTF-16LE to UTF-8 conversion.
  • iOS compat: Replaced system() with POSIX mkdir() (unavailable on iOS).
  • Dart: Removed redundant File.createSync() in extractToFile (native code already opens the file for writing).
  • Podspecs: Added CLANG_CXX_LANGUAGE_STANDARD => c++20 to iOS/macOS podspecs to match Android CMake.

C++17 → C++11

Rewrote all C++17 features (if-init statements, structured bindings) to C++11 for broader compiler compatibility.

Testing

  • Android (Pixel 6 emulator): 192MB Economist audio edition — 100% / 74 files / 15.9 sec
  • iOS (iPhone 17 Pro simulator): Same file — ~15-20 sec
  • All extracted files verified as valid MP3 with ID3 tags
  • Both platforms build without warnings/errors

Developer added 2 commits May 24, 2026 19:27
…ession blocks

- Add extractToDir C API that combines file info + extraction + mkdir into one FFI call
- Promote blockIndex/outBuffer to member variables so 7z SDK caches solid blocks across files
- Fix UTF-16LE to UTF-8 filename conversion (was broken on Android/Linux where wchar_t is 4 bytes)
- Replace system() with POSIX mkdir() for iOS compatibility
- Remove redundant Dart-side File.createSync() in extractToFile
- Add c++20 standard to iOS/macOS podspecs
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to speed up 7z extraction (especially solid archives) by reusing 7z SDK extraction buffers across calls, while also adding a native “extract by index to directory” API and updating the Dart wrapper to use it.

Changes:

  • C++: Persisted blockIndex/output buffer across extractions; added native extractFileToDir that creates subdirectories and writes the extracted file.
  • Dart: Added SZArchive.extractToDir(...) and updated archive-wide extraction paths to use the new native API; added basic timing/result types.
  • iOS/macOS: Podspecs now force a C++ language standard setting.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/flutter_7zip.h Adds new exported C API extractFileToDir.
src/flutter_7zip.cpp Implements caching for solid archives and introduces native extract-to-directory logic.
lib/src/flutter_7zip_bindings_generated.dart Adds FFI binding for extractFileToDir.
lib/flutter_7zip.dart Adds Dart API for directory extraction, updates extraction flows, and introduces timing/result types.
ios/flutter_7zip.podspec Sets CLANG_CXX_LANGUAGE_STANDARD to c++20.
macos/flutter_7zip.podspec Sets CLANG_CXX_LANGUAGE_STANDARD to c++20.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/flutter_7zip.cpp
Comment on lines +105 to 111
unsigned char* readFile(const uint32_t index) {
const ArchiveFile archiveFile = getFileByIndex(index);
if (archiveFile.is_dir) {
return nullptr;
}
auto* buffer = new unsigned char[archiveFile.size];
size_t read = 0;
Comment thread src/flutter_7zip.cpp
Comment on lines +128 to 133
ArchiveStatus extractFileToPath(const uint32_t index, const char* path) {
const ArchiveFile archiveFile = getFileByIndex(index);
if (archiveFile.is_dir) {
return kArchiveReadError;
}
size_t read = 0;
Comment thread src/flutter_7zip.cpp
Comment on lines +161 to +173
const uint16_t* src = file.name;
while (*src) {
if (*src < 0x80) {
outPath += static_cast<char>(*src);
} else if (*src < 0x800) {
outPath += static_cast<char>(0xC0 | (*src >> 6));
outPath += static_cast<char>(0x80 | (*src & 0x3F));
} else {
outPath += static_cast<char>(0xE0 | (*src >> 12));
outPath += static_cast<char>(0x80 | ((*src >> 6) & 0x3F));
outPath += static_cast<char>(0x80 | (*src & 0x3F));
}
src++;
Comment thread src/flutter_7zip.cpp
Comment on lines +160 to +175
std::string outPath = std::string(outputDir) + "/";
const uint16_t* src = file.name;
while (*src) {
if (*src < 0x80) {
outPath += static_cast<char>(*src);
} else if (*src < 0x800) {
outPath += static_cast<char>(0xC0 | (*src >> 6));
outPath += static_cast<char>(0x80 | (*src & 0x3F));
} else {
outPath += static_cast<char>(0xE0 | (*src >> 12));
outPath += static_cast<char>(0x80 | ((*src >> 6) & 0x3F));
outPath += static_cast<char>(0x80 | (*src & 0x3F));
}
src++;
}
delete[] file.name;
Comment thread src/flutter_7zip.cpp
Comment on lines +7 to +9
#include <sys/stat.h>
#include <string>
#include <vector>
Comment thread ios/flutter_7zip.podspec
Comment on lines 23 to 27
s.platform = :ios, '12.0'

# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386', 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20' }
s.swift_version = '5.0'
Comment on lines 29 to 33
s.dependency 'FlutterMacOS'

s.platform = :osx, '10.11'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'CLANG_CXX_LANGUAGE_STANDARD' => 'c++20' }
s.swift_version = '5.0'
Comment thread lib/flutter_7zip.dart
Comment on lines +164 to +176
bool extractToDir(int index, String outputDir) {
_stopwatch.start();
final before = _stopwatch.elapsedMilliseconds;
var p = outputDir.toNativeUtf8();
final result = _bindings.extractFileToDir(_archive, index, p.cast());
malloc.free(p);
if (result < 0 || result > 1) {
_stopwatch.stop();
throw Exception('Failed to extract file index $index (code=$result)');
}
final after = _stopwatch.elapsedMilliseconds;
_fileStats.add(FileExtractStat(index, result == 1, after - before));
return result == 1;
Comment thread lib/flutter_7zip.dart
List<FileExtractStat> get fileStats => List.unmodifiable(_fileStats);

/// Reset timing counters.
void resetTiming() {
Comment thread src/flutter_7zip.cpp
Comment on lines 55 to 60
~Archive() {
SzArEx_Free(&db, &allocImp);
File_Close(&archiveStream.file);
delete [] lookStream.buf;
_FreeImp(&allocImp, cachedBuffer);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants