Optimize 7z extraction performance and fix UTF-16LE filename handling#2
Open
SongOnWater wants to merge 2 commits into
Open
Optimize 7z extraction performance and fix UTF-16LE filename handling#2SongOnWater wants to merge 2 commits into
SongOnWater wants to merge 2 commits into
Conversation
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
There was a problem hiding this comment.
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 nativeextractFileToDirthat 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 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 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 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 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 on lines
+7
to
+9
| #include <sys/stat.h> | ||
| #include <string> | ||
| #include <vector> |
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 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; |
| List<FileExtractStat> get fileStats => List.unmodifiable(_fileStats); | ||
|
|
||
| /// Reset timing counters. | ||
| void resetTiming() { |
Comment on lines
55
to
60
| ~Archive() { | ||
| SzArEx_Free(&db, &allocImp); | ||
| File_Close(&archiveStream.file); | ||
| delete [] lookStream.buf; | ||
| _FreeImp(&allocImp, cachedBuffer); | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Performance optimization and bug fixes for the 7z extraction engine.
Changes
Performance (major speedup for solid archives)
blockIndex/cachedoutBufferfrom local variables toArchiveclass members. The 7z SDK uses both as cache keys — without this, eachextractToDircall re-decompressed the entire solid block. Measured improvement: ~17 min → 15.9 sec for a 192MB / 74-file archive on Android emulator.New API
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.SZArchive.extractToDir(int index, String outputDir)→bool(returnstrueif entry was a directory).Bug fixes
reinterpret_cast<const wchar_t*>(broken on Android/Linux wherewchar_tis 4 bytes) with proper UTF-16LE to UTF-8 conversion.system()with POSIXmkdir()(unavailable on iOS).File.createSync()inextractToFile(native code already opens the file for writing).CLANG_CXX_LANGUAGE_STANDARD => c++20to 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