diff --git a/.github/workflows/ci-test.yml b/.github/workflows/ci-test.yml index 2d017cfd..4384db86 100644 --- a/.github/workflows/ci-test.yml +++ b/.github/workflows/ci-test.yml @@ -21,8 +21,34 @@ jobs: - name: Check public API compatibility run: npm run check:api-compat - android-test: + android-unit-test: needs: lint + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + + - uses: jdx/mise-action@v4 + + - name: Cache npm packages + uses: actions/cache@v6 + with: + path: ~/.npm + key: ${{ runner.os }}-npm-${{ hashFiles('package-lock.json', 'test/test.ts') }} + restore-keys: | + ${{ runner.os }}-npm- + + - name: Install dependencies + run: npm install + + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v6 + + - name: Run Android unit tests + working-directory: android + run: ./gradlew :app:test + + android-test: + needs: [lint, android-unit-test] runs-on: bitrise-react-native-code-push-linux-runner strategy: matrix: @@ -49,6 +75,16 @@ jobs: - name: Install dependencies run: npm install + - name: Setup Gradle + uses: gradle/actions/setup-gradle@v6 + with: + # This job's Gradle build lives in a test app generated at runtime by the test harness, + # not a project checked into this repo, so there's nothing meaningful for it to + # contribute back to the cache. Read-only avoids each matrix variant (bare/expo) writing + # its own redundant entry; it still reuses whatever android-unit-test wrote, since + # setup-gradle shares its cache across jobs in the same workflow run. + cache-read-only: true + - name: Enable KVM run: | echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules @@ -67,7 +103,18 @@ jobs: target: google_apis arch: x86 disable-animations: true - script: npm run ${{ matrix.test-command }} + # connectedAndroidTest is included here (rather than a separate job or step) to reuse + # this job's already-booted emulator, even though it means it runs once per matrix + # variant. The android-emulator-runner action has no post-cleanup step, so a second + # step would boot and tear down a second emulator; ::group:: markers keep the two + # test runs visually separated in the Actions log instead. + script: | + echo "::group::Instrumented tests" + (cd android && ./gradlew :app:connectedAndroidTest) + echo "::endgroup::" + echo "::group::E2E tests" + npm run ${{ matrix.test-command }} + echo "::endgroup::" ios-test: needs: lint diff --git a/.gitignore b/.gitignore index 11742a75..f50a426d 100644 --- a/.gitignore +++ b/.gitignore @@ -196,6 +196,9 @@ Examples/testapp_rn # Android debug build files (conflict ignoring #Visual Studio files) !android/app/src/debug/ +# CMake/NDK build cache for android/app/src/main/cpp +android/app/.cxx + # iOS Simulator crash reports swept up from failed test runs (see test/test.ts) test/crash-logs/ diff --git a/.npmignore b/.npmignore index 47d26438..57e0aa55 100644 --- a/.npmignore +++ b/.npmignore @@ -42,8 +42,11 @@ test/ # Remove after this framework is published on NPM code-push-plugin-testing-framework/ -# Android build artifacts and Android Studio bits +# Android build artifacts, test sources and Android Studio bits android/app/build +android/app/.cxx +android/app/src/test +android/app/src/androidTest android/local.properties android/.gradle android/**/*.iml diff --git a/CLAUDE.md b/CLAUDE.md index 1e3d54df..02cdf64b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -7,7 +7,13 @@ React Native CodePush is a native module that enables over-the-air updates for R ## Development Commands ### Testing -- `npm test` - Run all tests with TypeScript compilation + +#### Unit tests + +- `cd android && ./gradlew :app:test` +- iOS: no unit tests yet. + +#### E2E Tests - `npm run test:android` - Run Android-specific tests - `npm run test:ios` - Run iOS-specific tests - `npm run test:setup-android` - Set up Android emulator for testing @@ -34,7 +40,7 @@ React Native CodePush is a native module that enables over-the-air updates for R ### Platform Structure - **iOS**: `ios/` - Objective-C implementation with CocoaPods integration -- **Android**: `android/` - Java implementation with Gradle plugin +- **Android**: `android/` - Java/Kotlin implementation with Gradle plugin - **Windows**: `windows/` - C++ implementation for Windows React Native - **JavaScript**: Root level - TypeScript definitions and bridge code @@ -48,7 +54,7 @@ React Native CodePush is a native module that enables over-the-air updates for R - **Custom Test Runner**: TypeScript-based test framework in `test/` - **Real App Testing**: Creates actual React Native apps for integration testing - **Scenario Testing**: Update, rollback, and error scenarios -- **No unit test infra yet**: this repo only has the mocha-based integration suite above. `src/acquisition-sdk/__tests__/` contains tests ported from upstream `microsoft/code-push`, kept for future reference - they are deliberately not wired into `npm test` or any runner. Don't assume they're dead/forgotten code, and don't wire them in without setting up real unit test infra first. +- **No unit test infra for JS/iOS yet**: JS/iOS only have the mocha-based integration suite above. `src/acquisition-sdk/__tests__/` contains tests ported from upstream `microsoft/code-push`, kept for future reference - they are deliberately not wired into `npm test` or any runner. Don't assume they're dead/forgotten code, and don't wire them in without setting up real unit test infra first. - **Templates**: `test/template/` holds native files (Podfile, AppDelegate, Android app files) and JS scenarios copied over top of a freshly generated RN/Expo app during test setup, overwriting its defaults — edit files here, not the generated project, for changes to persist - **`test:ios` vs `test:setup:ios` vs `test:fast:ios`**: `test:ios` is just `test:setup:ios` followed by `test:fast:ios` — the two are meant to be split apart for local iteration. - `test:setup:ios` (mocha `--ios --setup`) boots the simulator and provisions the test app once: copies templates, runs `pod install`, patches Info.plist/AppDelegate. It never builds or runs any test scenario. diff --git a/Examples/CodePushDemo/android/gradle.properties b/Examples/CodePushDemo/android/gradle.properties index 9afe6159..8dd6ab52 100644 --- a/Examples/CodePushDemo/android/gradle.properties +++ b/Examples/CodePushDemo/android/gradle.properties @@ -42,3 +42,9 @@ hermesEnabled=true # This allows your app to draw behind system bars for an immersive UI. # Note: Only works with ReactActivity and should not be used with custom Activity. edgeToEdgeEnabled=false + +# Opt out of AGP 9's built-in Kotlin support and new DSL, matching what RN's own 0.87 app template +# does. See the AGP v9 adoption RFC: +# https://github.com/react-native-community/discussions-and-proposals/pull/1006). +android.builtInKotlin=false +android.newDsl=false diff --git a/android/app/build.gradle b/android/app/build.gradle index 9e5eeac9..a1879ecc 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -1,4 +1,21 @@ -apply plugin: "com.android.library" +// No versions specified: both plugins are expected to already be resolved on the root project's +// buildscript classpath, which every RN app template declares (AGP for the app itself, Kotlin +// because RN ships Kotlin internally). +// Matches how other RN libraries (e.g. reanimated) apply these plugins. +plugins { + id "com.android.library" + id "org.jetbrains.kotlin.android" apply false +} + +// AGP 9 provides Kotlin support built in; applying the classic kotlin-android plugin on top of it +// leads to a configuration-time failure. Below AGP 9, and on AGP 9+ when `android.builtInKotlin=false` +// opts back out of built-in Kotlin, the classic plugin is still required. +// See React Native RFC about ecosystem migration details: https://github.com/react-native-community/discussions-and-proposals/pull/1006 +def agpMajor = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION.tokenize('.')[0].toInteger() +def builtInKotlinEnabled = agpMajor >= 9 && (!project.hasProperty("android.builtInKotlin") || Boolean.parseBoolean(project.property("android.builtInKotlin").toString())) +if (!builtInKotlinEnabled) { + apply plugin: "org.jetbrains.kotlin.android" +} def isNewArchitectureEnabled() { // To opt-in for the New Architecture, you can either: @@ -10,15 +27,18 @@ def isNewArchitectureEnabled() { def IS_NEW_ARCHITECTURE_ENABLED = isNewArchitectureEnabled() -def DEFAULT_COMPILE_SDK_VERSION = 26 -def DEFAULT_BUILD_TOOLS_VERSION = "26.0.3" -def DEFAULT_TARGET_SDK_VERSION = 26 -def DEFAULT_MIN_SDK_VERSION = 16 +// These are fallbacks only. Keep them aligned with RN's current app template +// so a consumer relying on the fallback still gets a build that actually works +// with a current RN version. +def DEFAULT_COMPILE_SDK_VERSION = 35 +def DEFAULT_BUILD_TOOLS_VERSION = "36.0.0" +def DEFAULT_TARGET_SDK_VERSION = 35 +def DEFAULT_MIN_SDK_VERSION = 24 android { namespace "com.microsoft.codepush.react" - compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION + compileSdk rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION defaultConfig { @@ -27,6 +47,21 @@ android { versionCode 1 versionName "1.0" buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", IS_NEW_ARCHITECTURE_ENABLED.toString() + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + externalNativeBuild { + cmake { + abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" + } + } + } + + externalNativeBuild { + cmake { + path "src/main/cpp/CMakeLists.txt" + version "3.22.1" + } } lintOptions { @@ -40,9 +75,29 @@ android { buildFeatures { buildConfig true } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } +} + +// Only set when we applied the classic kotlin-android plugin ourselves above: AGP 9's built-in +// Kotlin integration doesn't expose this extension at all, per Android's own built-in +// Kotlin migration guide: https://developer.android.com/build/migrate-to-built-in-kotlin +if (!builtInKotlinEnabled) { + android.kotlinOptions { + jvmTarget = "17" + } } dependencies { implementation 'com.facebook.react:react-android:0.82.1' implementation 'com.nimbusds:nimbus-jose-jwt:9.37.3' + + testImplementation 'junit:junit:4.13.2' + + androidTestImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.2.1' + androidTestImplementation 'androidx.test:runner:1.6.2' } diff --git a/android/app/src/androidTest/assets/bad_header/old.dat b/android/app/src/androidTest/assets/bad_header/old.dat new file mode 100644 index 00000000..f6bfa2d0 --- /dev/null +++ b/android/app/src/androidTest/assets/bad_header/old.dat @@ -0,0 +1,2 @@ +Irrelevant content - old.dat just needs to open successfully so the +test reaches the diff-header check this fixture is actually exercising. diff --git a/android/app/src/androidTest/assets/bad_header/patch.bsdiff b/android/app/src/androidTest/assets/bad_header/patch.bsdiff new file mode 100644 index 00000000..d574da6b Binary files /dev/null and b/android/app/src/androidTest/assets/bad_header/patch.bsdiff differ diff --git a/android/app/src/androidTest/assets/basic/new.dat b/android/app/src/androidTest/assets/basic/new.dat new file mode 100644 index 00000000..54241f83 --- /dev/null +++ b/android/app/src/androidTest/assets/basic/new.dat @@ -0,0 +1,25 @@ +function greet(name) { + console.log("Hello there, " + name + "!"); + return "Hello there, " + name + "!"; +} + +function farewell(name) { + console.log("Goodbye, " + name + "."); + return "Goodbye, " + name + "."; +} + +function shout(name) { + console.log("HEY, " + name.toUpperCase() + "!!!"); + return "HEY, " + name.toUpperCase() + "!!!"; +} + +var VERSION = "1.1.0"; +var BUILD_NUMBER = 43; + +module.exports = { + greet: greet, + farewell: farewell, + shout: shout, + VERSION: VERSION, + BUILD_NUMBER: BUILD_NUMBER, +}; diff --git a/android/app/src/androidTest/assets/basic/old.dat b/android/app/src/androidTest/assets/basic/old.dat new file mode 100644 index 00000000..b4667705 --- /dev/null +++ b/android/app/src/androidTest/assets/basic/old.dat @@ -0,0 +1,19 @@ +function greet(name) { + console.log("Hello, " + name + "!"); + return "Hello, " + name + "!"; +} + +function farewell(name) { + console.log("Goodbye, " + name + "."); + return "Goodbye, " + name + "."; +} + +var VERSION = "1.0.0"; +var BUILD_NUMBER = 42; + +module.exports = { + greet: greet, + farewell: farewell, + VERSION: VERSION, + BUILD_NUMBER: BUILD_NUMBER, +}; diff --git a/android/app/src/androidTest/assets/basic/patch.bsdiff b/android/app/src/androidTest/assets/basic/patch.bsdiff new file mode 100644 index 00000000..a0e9d31a Binary files /dev/null and b/android/app/src/androidTest/assets/basic/patch.bsdiff differ diff --git a/android/app/src/androidTest/assets/empty_old/new.dat b/android/app/src/androidTest/assets/empty_old/new.dat new file mode 100644 index 00000000..3559a9ff --- /dev/null +++ b/android/app/src/androidTest/assets/empty_old/new.dat @@ -0,0 +1,3 @@ +Everything in this file is new: the old side is a zero-byte file, so +the whole patch body is a literal insert with no copy-from-old control +entries at all. diff --git a/android/app/src/androidTest/assets/empty_old/old.dat b/android/app/src/androidTest/assets/empty_old/old.dat new file mode 100644 index 00000000..e69de29b diff --git a/android/app/src/androidTest/assets/empty_old/patch.bsdiff b/android/app/src/androidTest/assets/empty_old/patch.bsdiff new file mode 100644 index 00000000..4b376b26 Binary files /dev/null and b/android/app/src/androidTest/assets/empty_old/patch.bsdiff differ diff --git a/android/app/src/androidTest/assets/identical/new.dat b/android/app/src/androidTest/assets/identical/new.dat new file mode 100644 index 00000000..3e69fff2 --- /dev/null +++ b/android/app/src/androidTest/assets/identical/new.dat @@ -0,0 +1,4 @@ +This file is byte-for-byte identical on both sides of the patch. +It exercises the zero-delta path: a real BSDIFF40 patch whose only +control entry is a single full-length copy from the old file, with +no literal bytes and no seek. Nothing to add or skip. diff --git a/android/app/src/androidTest/assets/identical/old.dat b/android/app/src/androidTest/assets/identical/old.dat new file mode 100644 index 00000000..3e69fff2 --- /dev/null +++ b/android/app/src/androidTest/assets/identical/old.dat @@ -0,0 +1,4 @@ +This file is byte-for-byte identical on both sides of the patch. +It exercises the zero-delta path: a real BSDIFF40 patch whose only +control entry is a single full-length copy from the old file, with +no literal bytes and no seek. Nothing to add or skip. diff --git a/android/app/src/androidTest/assets/identical/patch.bsdiff b/android/app/src/androidTest/assets/identical/patch.bsdiff new file mode 100644 index 00000000..59b175d0 Binary files /dev/null and b/android/app/src/androidTest/assets/identical/patch.bsdiff differ diff --git a/android/app/src/androidTest/assets/wrong_old/old.dat b/android/app/src/androidTest/assets/wrong_old/old.dat new file mode 100644 index 00000000..9c90f626 --- /dev/null +++ b/android/app/src/androidTest/assets/wrong_old/old.dat @@ -0,0 +1,3 @@ +This is a completely unrelated old file, deliberately shaped so that +applying fixtures/basic/patch.bsdiff against it does not match the old +file bsdiff was actually built from. diff --git a/android/app/src/androidTest/java/com/microsoft/codepush/react/diffpatch/DiffPatchInstrumentedTest.kt b/android/app/src/androidTest/java/com/microsoft/codepush/react/diffpatch/DiffPatchInstrumentedTest.kt new file mode 100644 index 00000000..5ee0c8a2 --- /dev/null +++ b/android/app/src/androidTest/java/com/microsoft/codepush/react/diffpatch/DiffPatchInstrumentedTest.kt @@ -0,0 +1,133 @@ +package com.microsoft.codepush.react.diffpatch + +import android.content.res.AssetManager +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertArrayEquals +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import org.junit.runner.RunWith +import java.io.File + +@RunWith(AndroidJUnit4::class) +class DiffPatchInstrumentedTest { + + @get:Rule + val tempFolder = TemporaryFolder() + + private lateinit var assets: AssetManager + + @Before + fun setUp() { + assets = InstrumentationRegistry.getInstrumentation().context.assets + } + + // Assets are packed inside the APK, not addressable as filesystem paths, but + // DiffPatch.applyPatch() takes real file paths. So each fixture has to be + // copied out to a real file before it can be passed in. + private fun copyAssetToFile(assetPath: String, destination: File) { + assets.open(assetPath).use { input -> + destination.outputStream().use { output -> input.copyTo(output) } + } + } + + private fun newFileFor(assetPath: String): File = + File(tempFolder.newFolder(), File(assetPath).name) + + @Test + fun applyPatch_basicDiff_succeedsAndMatchesExpectedOutput() { + // An ordinary text-file diff, several inserted/changed/copied regions. + val oldFile = newFileFor("basic/old.dat").also { copyAssetToFile("basic/old.dat", it) } + val diffFile = newFileFor("basic/patch.bsdiff").also { copyAssetToFile("basic/patch.bsdiff", it) } + val expectedNewFile = newFileFor("basic/new.dat").also { copyAssetToFile("basic/new.dat", it) } + val outFile = File(tempFolder.root, "basic_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.OK, result) + assertArrayEquals(expectedNewFile.readBytes(), outFile.readBytes()) + } + + @Test + fun applyPatch_identicalOldAndNew_succeeds() { + // Real BSDIFF40 patch whose only control entry is a single full-length copy from the old file. + val oldFile = newFileFor("identical/old.dat").also { copyAssetToFile("identical/old.dat", it) } + val diffFile = newFileFor("identical/patch.bsdiff").also { copyAssetToFile("identical/patch.bsdiff", it) } + val expectedNewFile = newFileFor("identical/new.dat").also { copyAssetToFile("identical/new.dat", it) } + val outFile = File(tempFolder.root, "identical_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.OK, result) + assertArrayEquals(expectedNewFile.readBytes(), outFile.readBytes()) + } + + @Test + fun applyPatch_emptyOldFile_succeeds() { + val oldFile = newFileFor("empty_old/old.dat").also { copyAssetToFile("empty_old/old.dat", it) } + val diffFile = newFileFor("empty_old/patch.bsdiff").also { copyAssetToFile("empty_old/patch.bsdiff", it) } + val expectedNewFile = newFileFor("empty_old/new.dat").also { copyAssetToFile("empty_old/new.dat", it) } + val outFile = File(tempFolder.root, "empty_old_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.OK, result) + assertArrayEquals(expectedNewFile.readBytes(), outFile.readBytes()) + } + + @Test + fun applyPatch_badDiffHeader_returnsBadDiffHeader() { + // Well-formed length, wrong magic bytes (hand-written, not a real bsdiff output). + val oldFile = newFileFor("bad_header/old.dat").also { copyAssetToFile("bad_header/old.dat", it) } + val diffFile = newFileFor("bad_header/patch.bsdiff").also { copyAssetToFile("bad_header/patch.bsdiff", it) } + val outFile = File(tempFolder.root, "bad_header_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.BAD_DIFF_HEADER, result) + assertFalse("output file should not be left behind after a failed patch", outFile.exists()) + } + + @Test + fun applyPatch_mismatchedOldFile_returnsPatchFailed() { + // wrong_old/old.dat is unrelated to (and shorter than) basic/old.dat, so basic/patch.bsdiff's + // copy instructions reference offsets out of range for it - HDiffPatch's bounds checks must + // reject this rather than reading out of range or silently emitting corrupt output. + val oldFile = newFileFor("wrong_old/old.dat").also { copyAssetToFile("wrong_old/old.dat", it) } + val diffFile = newFileFor("basic/patch.bsdiff").also { copyAssetToFile("basic/patch.bsdiff", it) } + val outFile = File(tempFolder.root, "mismatched_old_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.PATCH_FAILED, result) + assertFalse("output file should not be left behind after a failed patch", outFile.exists()) + } + + @Test + fun applyPatch_missingOldFile_returnsOpenOldFailed() { + val diffFile = newFileFor("basic/patch.bsdiff").also { copyAssetToFile("basic/patch.bsdiff", it) } + val missingOldFile = File(tempFolder.root, "does_not_exist_old.dat") + val outFile = File(tempFolder.root, "missing_old_out.dat") + + val result = DiffPatch.applyPatch(missingOldFile.absolutePath, diffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.OPEN_OLD_FAILED, result) + assertFalse("output file should not be left behind after a failed patch", outFile.exists()) + } + + @Test + fun applyPatch_missingDiffFile_returnsOpenDiffFailed() { + val oldFile = newFileFor("basic/old.dat").also { copyAssetToFile("basic/old.dat", it) } + val missingDiffFile = File(tempFolder.root, "does_not_exist.bsdiff") + val outFile = File(tempFolder.root, "missing_diff_out.dat") + + val result = DiffPatch.applyPatch(oldFile.absolutePath, missingDiffFile.absolutePath, outFile.absolutePath) + + assertEquals(DiffPatch.PatchResult.OPEN_DIFF_FAILED, result) + assertFalse("output file should not be left behind after a failed patch", outFile.exists()) + } +} diff --git a/android/app/src/main/cpp/CMakeLists.txt b/android/app/src/main/cpp/CMakeLists.txt new file mode 100644 index 00000000..abb63e41 --- /dev/null +++ b/android/app/src/main/cpp/CMakeLists.txt @@ -0,0 +1,55 @@ +cmake_minimum_required(VERSION 3.22.1) +# C only for now, to keep libc++ and its bundle size out of the build. +# Vendored code is all in C, and our glue code is simple enough to be written in C, +# but this might need to be revisited in the future. +project(codepush_diffpatch C) + +# Single shared library: +# - vendored HDiffPatch (bsdiff-compatible patch applier only) +# - vendored bzip2 +# - our bridge from the HDiffPatch C API to the JNI layer +# - JNI glue +# See third_party/README.md for what we vendor and why. +add_library(codepush_diffpatch SHARED + hdiffpatch_jni.c + bspatch_bridge.c + + third_party/hdiffpatch/libHDiffPatch/HPatch/patch.c + third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.c + third_party/hdiffpatch/file_for_patch.c + + bzip2_error_stub.c + third_party/bzip2/bzlib.c + third_party/bzip2/decompress.c + third_party/bzip2/huffman.c + third_party/bzip2/crctable.c + third_party/bzip2/randtable.c +) + +target_include_directories(codepush_diffpatch PRIVATE + . + third_party/bzip2 +) + +target_compile_definitions(codepush_diffpatch PRIVATE + # We only ever patch regular files under app-private storage, never + # raw block devices. Skip file_for_patch.c's block-device probing + # (avoids depending on ioctls entirely). + _IS_NEED_BLOCK_DEV=0 + + # Defaults to 1, which pulls in hpatch_mt/*.h files. + # Not needed for our use case. + _IS_USED_MULTITHREAD=0 + + # Defaults 1, which compiles in hpatch_removeDir/hpatch_moveFile/hpatch_makeNewDir + # We only ever patch a single regular file. + _IS_NEED_DIR_DIFF_PATCH=0 + + # We only ever use bzip's decompression API. + # See third_party/README.md for details. + BZ_NO_COMPRESS=1 + BZ_NO_STDIO=1 +) + +# HDiffPatch uses +target_link_libraries(codepush_diffpatch log) diff --git a/android/app/src/main/cpp/DECISION_LOG.md b/android/app/src/main/cpp/DECISION_LOG.md new file mode 100644 index 00000000..361bd179 --- /dev/null +++ b/android/app/src/main/cpp/DECISION_LOG.md @@ -0,0 +1,299 @@ +# Decision log: Android delta-update native module + +Chronological record of the non-obvious calls made while prototyping BSDIFF40 +patch application for Android (see [third_party/README.md](third_party/README.md) +for vendoring specifics — this log is about the decisions around that code, +not the vendoring itself). + +## Scope: native scaffolding only, not wired into the update flow yet + +**Decision:** Build CMake + JNI + vendored patch-apply code as a standalone, +buildable unit. Don't touch `CodePushUpdateManager` or the download flow yet. + +**Why:** Proves the build pipeline and JNI boundary work in isolation before +taking on the bigger, separate design question of how the server signals +"this is a binary patch" vs. a full zip, and how that interacts with the +existing file-manifest diff mechanism already in `CodePushUpdateManager`. + +## Patch format: HDiffPatch's `bsdiff_wrapper`, not its own hpatch format + +**Decision:** Vendor `bsdiff_wrapper/bspatch_wrapper.c`, which decodes +BSDIFF40-compatible patches, not HDiffPatch's own (larger, more capable) +diff format. + +**Why:** The producer side uses `hdiffz -BSD` specifically, which emits +BSDIFF40, not HDiffPatch's native format. No reason to support a format +nothing produces. + +## Vendoring: trimmed copy, not a git submodule + +**Decision:** Copy the specific files needed into `third_party/`, pinned to +a commit noted in `third_party/README.md`, rather than adding HDiffPatch (or +bzip2) as a git submodule. + +**Why:** Full control over exactly what's compiled and easy to audit; +avoids dragging in HDiffPatch's unrelated CLI tools, test data, and other +language ports. Submodules also don't travel with an npm tarball, which +would break source consumers of this package. + +## bzip2's compress-side (`compress.c`/`blocksort.c`) — reversed: now excluded via source patch + +**Original decision (superseded):** Vendor bzip2's full compress+decompress +source set, even though we only ever call `BZ2_bzDecompress*`, because +`bzlib.c` bundles both directions' public API in one file with no split — +the linker demands a definition for every symbol referenced from a linked +object, regardless of whether our code ever reaches it at runtime — and +patching `bzlib.c` to strip the compress half would break the "vendored = +unmodified copy" property that keeps future upstream updates a clean file +swap. Independently confirmed: expo-updates vendors the identical file set +for the identical reason. + +**Reversed:** decided the size savings are worth trading away the +"unmodified copy" property, *if* the modification is small, mechanical, and +well-documented enough that a future bzip2 version bump is still a +clean(ish) re-apply rather than a from-scratch rewrite. See +`third_party/README.md`'s `bzip2/` section for exactly what was changed and +why it's expected to survive an upstream bump. + +**Change:** wrapped the compress-side functions in `bzlib.c` +(`BZ2_bzCompressInit`/`BZ2_bzCompress`/`BZ2_bzCompressEnd`, +`BZ2_bzBuffToBuffCompress`, and their private helpers) in `#ifndef +BZ_NO_COMPRESS`, and defined `BZ_NO_COMPRESS=1` plus the pre-existing +upstream `BZ_NO_STDIO=1` switch in `CMakeLists.txt`. `BZ_NO_STDIO` requires +the embedder to supply `bz_internal_error()` (upstream's documented +extension point for non-stdio builds); added a one-line `abort()` +implementation in the new `bzip2_error_stub.c`. With both symbols removed +from `bzlib.c`'s translation unit, nothing left in the link references +`BZ2_compressBlock`/`BZ2_blockSort`, so `compress.c` and `blocksort.c` were +dropped from `CMakeLists.txt`'s source list and deleted from the tree — no compiler flag can +do that; unreferenced-but-linked object files still contribute size because +CMake links whole objects, not per-symbol. + +**Measured, release/stripped (before → after):** +- arm64-v8a: 118,528 B → 79,792 B +- armeabi-v7a: 90,672 B → 60,120 B +- x86_64: 133,696 B → 90,944 B + +**Verified:** clean `assembleDebug`/`assembleRelease` builds succeed for all +three ABIs, and `./gradlew :app:connectedDebugAndroidTest` against the +`Pixel_7_API_34_default` emulator still passes all 7 +`DiffPatchInstrumentedTest` cases — including the bzip2-compressed-stream +fixtures — confirming `BZ2_bzDecompress*` is unaffected. + +## Multithreading and block-device support disabled + +**Decision:** `_IS_USED_MULTITHREAD=0` and `_IS_NEED_BLOCK_DEV=0` in +`CMakeLists.txt`. + +**Why:** `_IS_USED_MULTITHREAD` defaults to `1` in `patch_types.h`, which +would otherwise require vendoring `hpatch_mt/*.c` (multi-threaded patching — +not needed at single-file, one-patch-at-a-time scale). Block-device support +exists for patching raw disk partitions, which is irrelevant here — we only +ever patch regular files under app-private storage — and skipping it avoids +depending on `` ioctls entirely. + +## Raw JNI, not fbjni + +**Decision:** `hdiffpatch_jni.c` uses plain JNI calls +(`GetStringUTFChars`/`ReleaseStringUTFChars`), not Facebook's fbjni (which +React Native's own native modules typically use). + +**Why:** fbjni's real benefits — RAII-safe reference handling on early-return +paths, C++→Java exception translation, compile-time-checked method +registration instead of raw mangled-name symbols, ergonomic marshaling of +complex/callback types — don't apply to a `String × 3 → int`, no-callback, +no-exception, single-method surface. What it costs is real and immediate: +`libfbjni.so` (~177 KB/ABI) + `libc++_shared.so` (~1.2-1.3 MB/ABI) as hard +runtime dependencies, plus coupling this module's native packaging to RN's +own fbjni distribution — which would also break working standalone outside +an RN host. Revisit if the surface grows real justification for it: callbacks +into Java (e.g. progress reporting during a long patch), structured +exceptions instead of an int enum, or several more native methods. + +## API shape: file paths in and out, not streams + +**Decision:** `codepush_bspatch_apply(oldFilePath, diffFilePath, +outNewFilePath)` takes three file paths, not HDiffPatch's native +`hpatch_TStreamInput`/`Output` handed in from the Java/JNI side. + +**Why:** +- The *old* file can't be a stream regardless of what we do for the diff: + `bspatch_with_cache`'s access into `oldData` is random-access (it seeks + around per the control stream's instructions), not sequential. +- The *diff* file is already materialized on disk by the time this would be + called — the existing `CodePushUpdateManager` flow downloads a zip to a + temp file and fully unzips it to disk before touching any extracted + content. There's no live decompression stream to intercept even if we + wanted to. +- A stream-backed alternative would mean a custom `hpatch_TStreamInput` + whose `read()` thunks back into Java per chunk across JNI — trading one + redundant (and, for delta patches, small) file write for a chatty, + per-call JNI round-trip in a hot loop. Not a good trade for what it saves. + +## Language: everything we author is C — reversed: was C++ + +**Original decision (superseded):** `bspatch_bridge` was rewritten from `.c` +to `.cpp`, replacing a `goto cleanup`-based implementation with RAII wrapper +classes around HDiffPatch's file streams. The argument was that consistency +with one language throughout the code *we* author beats a "legible boundary" +confining C++ to the JNI file — the vendored third-party C being unaffected +either way. + +**What that cost, and the real root cause (not what it first looked like):** +enabling exceptions pulled libc++abi's full unwinder (`libunwind::*`) and +Itanium symbol demangler (`__cxa_demangle` and friends) into the `.so` +*statically*, because the NDK default (`c++_static`) links libc++ per-`.so` +rather than sharing one copy via `libc++_shared.so`. Measured at the time, +release/stripped, arm64-v8a: **119 KB → 356 KB** (roughly 3x). + +Removing the one `throw` site (`std::vector`'s allocation) **did not fix +it** — verified by rebuilding with `new (std::nothrow) unsigned char[size]` +and again with plain `malloc`/`free`; still 356 KB both times. Bisecting +which object file actually references `__gxx_personality_v0` found the +mechanism: any C++ translation unit containing a class with a non-trivial +destructor needs unwind/landing-pad support, full stop. The compiler must +assume some *other* frame could throw and unwind through ours, and if it +does, our destructors still have to run. `-fexceptions` is on by default, so +it always prepares for that regardless of whether any `throw` is reachable in +the real call graph. Worked around with `-fno-exceptions -fno-rtti` scoped to +`COMPILE_LANGUAGE:CXX`, which restored 119 KB/ABI. + +**Reversed:** converted both `bspatch_bridge.cpp` and `hdiffpatch_jni.cpp` +back to `.c`. Two things forced the re-examination: + +- **The stated reason for C++ in the JNI file was factually wrong.** It + claimed `hdiffpatch_jni.cpp` needed C++ "for JNI's calling convention." + JNI has a first-class C API — `jni.h` defines both, and the only difference + is `(*env)->GetStringUTFChars(env, ...)` instead of + `env->GetStringUTFChars(...)`. So the real choice was never "one C++ file + or two"; it was C++ anywhere, or nowhere. +- **The flag is one line; justifying it was a page.** `-fno-exceptions + -fno-rtti` is a standard, unremarkable pair for a leaf C++ library. The + twelve-line comment above it and most of this log section were the actual + cost — and both disappear in C. 140 lines of our own code, sitting on ~10k + lines of vendored C, called through a C API, doing manual resource + management: C *is* the one consistent language here. + +**What went away:** the two RAII classes, `-fno-exceptions -fno-rtti` plus +its comment, and libc++ from the link entirely — `CMakeLists.txt` now says +`project(codepush_diffpatch C)`, so CMake never enables a C++ compiler at +all. `readelf -d` lists only `liblog`/`libm`/`libdl`/`libc` as `DT_NEEDED`, +and the only `__cxa_*` symbols left are `__cxa_atexit`/`__cxa_finalize` from +`crtbegin_so.o`, which every `.so` carries. + +## Cleanup in the C bridge: `__attribute__((cleanup))`, not `goto` + +**Decision:** rather than return to the `goto cleanup` idiom the vendored +code itself uses (`patch.c` has 31 `goto _clear` sites behind a +`_clear_return` macro), `bspatch_bridge.c` uses Clang/GCC's scope-based +cleanup attribute. Handlers fire on every exit path in reverse declaration +order, so the early returns stay plain `return`s and there is no label. + +**Why it works here with no wrapper functions:** HDiffPatch's own API happens +to fit the attribute's shape exactly. `hpatch_TFileStreamInput_close` already +takes precisely `hpatch_TFileStreamInput*` (a non-`void` return is permitted +and ignored), `hpatch_TFileStreamInput/Output_init()` is a `memset(self,0,…)`, +and `_import_fileClose` is NULL-safe — so an unconditional close on a +never-opened stream is a genuine no-op. That last property is also what made +the `opened_` bools in the old C++ wrappers redundant. The scratch buffer +gets the same treatment via a three-line `freeTempCache` handler; the earlier +note that RAII wasn't worth it for the buffer was written when it had one +post-`malloc` early return, and mixing an automatic cleanup for the streams +with a manual `free` for the buffer in the same function is worse than being +uniform. + +**Caveat:** every resource must be declared *and* neutralized before the +first fallible call, because the handler runs on scope exit whether or not +the variable was ever initialized. The declarations are grouped at the top of +`applyPatchImpl` with a comment saying so. `codepush_bspatch_apply` also +still wraps `applyPatchImpl` rather than collapsing into one function: the +cleanup handlers must have run — closing the output file — before +`hpatch_removeFile` deletes it on the failure path. + +The attribute is a compiler extension, not standard C. Only Clang builds this +(NDK), so that's not a portability problem today; if it ever becomes one, the +fallback is the `goto` form, mechanically. + +**Measured, release/stripped (C++ → C):** +- arm64-v8a: 79,792 B → 79,712 B +- armeabi-v7a: 60,120 B → 60,040 B +- x86_64: 90,944 B → 90,880 B + +Effectively zero, as expected — `-fno-exceptions` had already bought back +everything C++ was costing, so **this was a complexity change, not a size +change.** The module's size wins came from the bzip2 source trim and the +`_IS_*`/`BZ_NO_*` defines, which are orthogonal to language. + +**Verified:** clean `assembleRelease` from a wiped `.cxx`/`cmake` output dir, +zero compiler warnings, and `./gradlew :app:connectedDebugAndroidTest` +against `Pixel_7_API_34_default` — all 7 `DiffPatchInstrumentedTest` cases +pass. + +## Size comparison against expo-updates (research, not yet acted on) + +expo-updates vendors the *same* bzip2 file set for the *same* reason +(confirmed by reading their source). But their patch applier is the +original 419-line Colin Percival reference `bspatch.c`, not HDiffPatch — +dramatically simpler, and their whole native module (`libexpo-updates.so`, +which does more than just patching) is 102 KB/ABI stripped, smaller than +just our patcher. They call it through fbjni, which costs them +`libfbjni.so` + `libc++_shared.so` as dependencies — but those are +effectively free in a real RN app, since RN itself already loads both. + +Not yet decided: whether to swap our HDiffPatch-based vendoring for the +same reference `bspatch.c` Expo uses, if binary size becomes a hard +constraint. Noted here so it isn't re-researched from scratch later. + +## CLI main() vs. direct library call: kept the direct call + +**Question:** expo-updates' JNI shim builds an argv array and calls +HDiffPatch's/bspatch's CLI `main()` directly +(https://github.com/expo/expo/blob/main/packages/expo-updates/android/src/main/cpp/BSPatchModule.cpp#L33) +- would that let us drop `bspatch_bridge` in favor of the same pattern? + +**Answer: no.** HDiffPatch's own CLI entry point (`hpatchz.c`'s `main()`) +does argv parsing, heavy stdout/stderr logging, and has an `exit()` call on +a multithread-error path - unsafe to embed in a shared library (an `exit()` +call kills the whole host app, not just one patch attempt). Our current +direct call to `bspatch_with_cache()` (`bsdiff_wrapper/bspatch_wrapper.c`) +is confirmed to have no `exit`/`abort`/`err`, no stdout/stderr I/O, and no +global/static mutable state - it's already the safe, embeddable primitive. +Expo's `bspatch_main()` only gets to be callable this way because it's a +heavily rewritten fork of the reference `bspatch.c` (itself full of +`err()`/`exit()` calls on failure paths) that strips all of that out and +replaces global state with a passed-around context struct. We don't need +to do that work because we already have an equivalently safe direct call. + +## Instrumented `androidTest` suite for `DiffPatch.kt` (JNI+Java boundary) + +**Decision:** added an `androidTest` source set (Gradle wiring in +`app/build.gradle`: `testInstrumentationRunner +"androidx.test.runner.AndroidJUnitRunner"`, `androidx.test.ext:junit:1.2.1` ++ `androidx.test:runner:1.6.2`) and a single Kotlin test class, +`DiffPatchInstrumentedTest`, run via `./gradlew :app:connectedAndroidTest` +on a real device/emulator. + +**Why:** the plain `:app:test` JUnit suite runs on a host JVM, not +Robolectric, and can't load `codepush_diffpatch.so` even in principle +(Android/bionic-only, links `liblog`) regardless of matching CPU +architecture. `DiffPatchInstrumentedTest` calls `DiffPatch.applyPatch()` +exactly as a real caller would, on-device, exercising `System.loadLibrary` ++ the actual JNI call - the only suite that reaches this surface. + +**Fixtures:** checked-in, real `BSDIFF40` patches under +`src/androidTest/assets/` (`basic/`, `identical/`, `empty_old/`, +`bad_header/`, `wrong_old/` - see the per-fixture comments in +`DiffPatchInstrumentedTest.kt` for how each was produced and what it's +for). `DiffPatch.applyPatch()` takes real filesystem paths, not asset +paths, so each test copies the fixture bytes it needs out of +`AssetManager` into a `TemporaryFolder`-managed file before calling it - +assets themselves are never passed to `applyPatch()`. + +**Verified:** `./gradlew :app:assembleDebugAndroidTest` succeeds and +produces an APK bundling `libcodepush_diffpatch.so` for all three ABIs +(`arm64-v8a`, `armeabi-v7a`, `x86_64`) plus the fixture assets, confirming +the existing `externalNativeBuild` wiring already covers the androidTest +variant with no extra Gradle config needed. Also ran +`./gradlew :app:connectedDebugAndroidTest` against a local +`Pixel_7_API_34_default` emulator: all 7 tests passed, 0 failures, 0 +errors. diff --git a/android/app/src/main/cpp/bspatch_bridge.c b/android/app/src/main/cpp/bspatch_bridge.c new file mode 100644 index 00000000..6f9c0b0f --- /dev/null +++ b/android/app/src/main/cpp/bspatch_bridge.c @@ -0,0 +1,87 @@ +#include "bspatch_bridge.h" + +#include "third_party/hdiffpatch/libHDiffPatch/HPatch/patch_types.h" +#include "third_party/hdiffpatch/file_for_patch.h" +#include "third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.h" + +#define _CompressPlugin_bz2 +// Despite the filename, this isn't sample code: it's upstream's only +// definition of _bz2DecompressPlugin_unsz. It's written to be #included +// (not compiled standalone): defining _CompressPlugin_bz2 before including it compiles +// just the bz2 decompressor variant into this translation unit. +#include "third_party/hdiffpatch/decompress_plugin_demo.h" + +#include + +// Clang/GCC's scope-based cleanup instead of `goto`s. (Android NDK uses clang) +#define CLEANUP(fn) __attribute__((cleanup(fn))) + +static void freeTempCache(unsigned char** pCache) { + free(*pCache); +} + +// Sliced up internally by bspatch_with_cache between the old-file cache and +// up to three decompression streams. +// Bigger just means fewer read() round-trips. +// TODO: benchmark and fine-tune this. +#define kTempCacheSize ((size_t)1 << 20) // 1 MiB + +static CodePushBSPatchResult applyPatchImpl(const char* oldFilePath, + const char* diffFilePath, + const char* outNewFilePath) { + // Every resource is declared and neutralized here, before the first + // fallible call: no early return may be introduced inside this block. + CLEANUP(hpatch_TFileStreamInput_close) hpatch_TFileStreamInput oldStream; + hpatch_TFileStreamInput_init(&oldStream); + CLEANUP(hpatch_TFileStreamInput_close) hpatch_TFileStreamInput diffStream; + hpatch_TFileStreamInput_init(&diffStream); + CLEANUP(hpatch_TFileStreamOutput_close) hpatch_TFileStreamOutput outStream; + hpatch_TFileStreamOutput_init(&outStream); + CLEANUP(freeTempCache) unsigned char* tempCache = NULL; + + hpatch_BsDiffInfo diffInfo; + + if (!hpatch_TFileStreamInput_open(&oldStream, oldFilePath)) + return CODEPUSH_BSPATCH_ERR_OPEN_OLD; + if (!hpatch_TFileStreamInput_open(&diffStream, diffFilePath)) + return CODEPUSH_BSPATCH_ERR_OPEN_DIFF; + + if (!getBsDiffInfo(&diffInfo, &diffStream.base)) + return CODEPUSH_BSPATCH_ERR_BAD_DIFF_HEADER; + + // Why open the output only at this point: maxLength isn't known until the diff header above has been parsed. + if (!hpatch_TFileStreamOutput_open(&outStream, outNewFilePath, diffInfo.newDataSize)) + return CODEPUSH_BSPATCH_ERR_OPEN_OUT; + + tempCache = (unsigned char*)malloc(kTempCacheSize); + if (!tempCache) return CODEPUSH_BSPATCH_ERR_OOM; + + // _bz2DecompressPlugin_unsz usage (not the plain bz2DecompressPlugin): bsdiff's + // three compressed sub-streams are back-to-back in one bzip2 stream + // without individually recorded output sizes, so the decompressor must + // tolerate reading past its logical end and zero-fill instead of + // erroring. That's what the "_unsz" (unknown size) variant does. + if (!bspatch_with_cache(&outStream.base, &oldStream.base, &diffStream.base, + &_bz2DecompressPlugin_unsz, + tempCache, tempCache + kTempCacheSize)) + return CODEPUSH_BSPATCH_ERR_PATCH_FAILED; + + // Flush here, so a failing final write (ENOSPC, EIO) is reported instead of + // leaving a truncated bundle on disk under a success code. + if (!hpatch_TFileStreamOutput_flush(&outStream)) + return CODEPUSH_BSPATCH_ERR_PATCH_FAILED; + + return CODEPUSH_BSPATCH_OK; +} + +CodePushBSPatchResult codepush_bspatch_apply(const char* oldFilePath, + const char* diffFilePath, + const char* outNewFilePath) { + CodePushBSPatchResult result = applyPatchImpl(oldFilePath, diffFilePath, outNewFilePath); + if (result != CODEPUSH_BSPATCH_OK) { + // Don't leave a corrupt, partially-written "new bundle" on disk. + // It's a no-op when outNewFilePath was never created (e.g. ERR_OPEN_OLD) + hpatch_removeFile(outNewFilePath); + } + return result; +} diff --git a/android/app/src/main/cpp/bspatch_bridge.h b/android/app/src/main/cpp/bspatch_bridge.h new file mode 100644 index 00000000..766f598d --- /dev/null +++ b/android/app/src/main/cpp/bspatch_bridge.h @@ -0,0 +1,31 @@ +// Thin wrapper around HDiffPatch's BSDIFF40 patch applier. +// The declarations below are C-linkage/C-callable. +#ifndef CODEPUSH_BSPATCH_BRIDGE_H +#define CODEPUSH_BSPATCH_BRIDGE_H + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum CodePushBSPatchResult { + CODEPUSH_BSPATCH_OK = 0, + CODEPUSH_BSPATCH_ERR_BAD_DIFF_HEADER = 1, + CODEPUSH_BSPATCH_ERR_OPEN_OLD = 2, + CODEPUSH_BSPATCH_ERR_OPEN_DIFF = 3, + CODEPUSH_BSPATCH_ERR_OPEN_OUT = 4, + CODEPUSH_BSPATCH_ERR_OOM = 5, + CODEPUSH_BSPATCH_ERR_PATCH_FAILED = 6, +} CodePushBSPatchResult; + +// Applies a BSDIFF40-format patch to oldFilePath, writing the result +// to outNewFilePath. All three paths must be plain regular files; +// outNewFilePath is created/truncated, and removed again if patching fails partway through. +CodePushBSPatchResult codepush_bspatch_apply(const char* oldFilePath, + const char* diffFilePath, + const char* outNewFilePath); + +#ifdef __cplusplus +} +#endif + +#endif // CODEPUSH_BSPATCH_BRIDGE_H diff --git a/android/app/src/main/cpp/bzip2_error_stub.c b/android/app/src/main/cpp/bzip2_error_stub.c new file mode 100644 index 00000000..0525f420 --- /dev/null +++ b/android/app/src/main/cpp/bzip2_error_stub.c @@ -0,0 +1,12 @@ +// bzlib_private.h requires the embedder to supply bz_internal_error() +// whenever BZ_NO_STDIO is defined (see third_party/bzip2/bzlib_private.h +// and third_party/README.md) - upstream's non-stdio build normally +// leaves this fprintf/exit(3) to the caller. It's only ever reached on +// an internal bzip2 consistency-check failure (a corrupt/malicious +// bzip2 stream), which should abort the patch operation immediately +// rather than continue with undefined state. +#include + +void bz_internal_error(int errcode) { + abort(); +} diff --git a/android/app/src/main/cpp/hdiffpatch_jni.c b/android/app/src/main/cpp/hdiffpatch_jni.c new file mode 100644 index 00000000..c9329979 --- /dev/null +++ b/android/app/src/main/cpp/hdiffpatch_jni.c @@ -0,0 +1,27 @@ +#include + +#include "bspatch_bridge.h" + +JNIEXPORT jint JNICALL +Java_com_microsoft_codepush_react_diffpatch_DiffPatch_nativeBSPatchApply( + JNIEnv* env, jclass clazz, + jstring oldFilePath, jstring diffFilePath, jstring outNewFilePath) { + const char* oldPath; + const char* diffPath; + const char* outPath; + CodePushBSPatchResult result; + + (void)clazz; + + oldPath = (*env)->GetStringUTFChars(env, oldFilePath, NULL); + diffPath = (*env)->GetStringUTFChars(env, diffFilePath, NULL); + outPath = (*env)->GetStringUTFChars(env, outNewFilePath, NULL); + + result = codepush_bspatch_apply(oldPath, diffPath, outPath); + + (*env)->ReleaseStringUTFChars(env, oldFilePath, oldPath); + (*env)->ReleaseStringUTFChars(env, diffFilePath, diffPath); + (*env)->ReleaseStringUTFChars(env, outNewFilePath, outPath); + + return (jint)result; +} diff --git a/android/app/src/main/cpp/third_party/README.md b/android/app/src/main/cpp/third_party/README.md new file mode 100644 index 00000000..3cbf62e2 --- /dev/null +++ b/android/app/src/main/cpp/third_party/README.md @@ -0,0 +1,56 @@ +# Vendored sources + +These directories contain trimmed copies of two upstream libraries, pinned +to a specific commit. Only the files needed to *apply* a BSDIFF40-style +patch (the `hdiffz -BSD` producer output) are vendored. `hdiffpatch/` is an +unmodified-source copy; `bzip2/bzlib.c` carries one small, documented patch +(see below). + +## hdiffpatch/ + +Source: https://github.com/sisong/HDiffPatch +Pinned commit: `3b9dca715ca492873bf2c49e22e5d5b7d2a78620` (2026-07-31) +License: MIT. + +Files were chosen by tracing the actual dependency graph of +`bsdiff_wrapper/bspatch_wrapper.c` (the BSDIFF40-compatible patch applier), +not by directory boundaries. In particular `libHDiffPatch/HPatch/patch.c` +(~157KB) is HDiffPatch's own diff-format decoder, but it's still required +here because `bspatch_wrapper.c` shares its low-level stream-cache helpers +(`_TOutStreamCache_*`, `getStreamClip`, `_patch_cache_all_old`, etc.). + +## bzip2/ + +Source: https://github.com/sisong/bzip2 (referenced directly by HDiffPatch) +Pinned commit: `fbc4b11da543753b3b803e5546f56e26ec90c2a7` (2024-04-09) +License: bzip2 license, a permissive BSD-style license. + +Classic bsdiff patches compress their control/diff/extra streams with +bzip2, so `BZ2_bzDecompress*` is needed to read them back. We only ever call +the decompress-side API. The `bzip2`/`bzip2recover` CLI sources are +unrelated to the library API and were not copied. Neither are the +compress-only `compress.c` and `blocksort.c`: after the local patch below, +nothing in the link references `BZ2_compressBlock`/`BZ2_blockSort`. + +### Local patch: compress-side code excluded from `bzlib.c` + +`bzlib.c` bundles both `BZ2_bzCompress*` and `BZ2_bzDecompress*` in one +file with no split, so an unpatched `bzlib.c` makes the linker demand +`compress.c`/`blocksort.c` for any binary that links it at all, even one +that never calls the compress-side API. + +Measured impact of removing it: 118,528 B → 79,792 B (arm64-v8a) + +**What was changed, precisely**, to make a future bzip2 version bump easier: +- In `bzlib.c`, wrapped in `#ifndef BZ_NO_COMPRESS` / `#endif`: + - Compress-only static helpers: `prepare_new_block`, `init_RL`, `isempty_RL` + - The streaming compress API and its private + helpers: `BZ2_bzCompressInit`, `add_pair_to_block`, `flush_RL`, the + `ADD_CHAR_TO_BLOCK` macro, `copy_input_until_stop`, + `copy_output_until_stop`, `handle_compress`, `BZ2_bzCompress`, + `BZ2_bzCompressEnd` + - The buffer-to-buffer compress convenience wrapper: `BZ2_bzBuffToBuffCompress` +- `CMakeLists.txt` defines `BZ_NO_STDIO=1`, a pre-existing upstream switch that excludes + the whole `FILE*`-based `bzopen`/`bzread`/`bzwrite` API, which also calls the now-excluded + compress functions. `BZ_NO_STDIO` requires the embedder to supply + `bz_internal_error()`; see `../bzip2_error_stub.c`. diff --git a/android/app/src/main/cpp/third_party/bzip2/LICENSE b/android/app/src/main/cpp/third_party/bzip2/LICENSE new file mode 100644 index 00000000..81a37eab --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/LICENSE @@ -0,0 +1,42 @@ + +-------------------------------------------------------------------------- + +This program, "bzip2", the associated library "libbzip2", and all +documentation, are copyright (C) 1996-2019 Julian R Seward. All +rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. The origin of this software must not be misrepresented; you must + not claim that you wrote the original software. If you use this + software in a product, an acknowledgment in the product + documentation would be appreciated but is not required. + +3. Altered source versions must be plainly marked as such, and must + not be misrepresented as being the original software. + +4. The name of the author may not be used to endorse or promote + products derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE +GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +Julian Seward, jseward@acm.org +bzip2/libbzip2 version 1.0.8 of 13 July 2019 + +-------------------------------------------------------------------------- diff --git a/android/app/src/main/cpp/third_party/bzip2/bzlib.c b/android/app/src/main/cpp/third_party/bzip2/bzlib.c new file mode 100644 index 00000000..b1d3569b --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/bzlib.c @@ -0,0 +1,1576 @@ + +/*-------------------------------------------------------------*/ +/*--- Library top-level functions. ---*/ +/*--- bzlib.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + +/* CHANGES + 0.9.0 -- original version. + 0.9.0a/b -- no changes in this file. + 0.9.0c -- made zero-length BZ_FLUSH work correctly in bzCompress(). + fixed bzWrite/bzRead to ignore zero-length requests. + fixed bzread to correctly handle read requests after EOF. + wrong parameter order in call to bzDecompressInit in + bzBuffToBuffDecompress. Fixed. +*/ + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +/*--- Compression stuff ---*/ +/*---------------------------------------------------*/ + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_STDIO +void BZ2_bz__AssertH__fail ( int errcode ) +{ + fprintf(stderr, + "\n\nbzip2/libbzip2: internal error number %d.\n" + "This is a bug in bzip2/libbzip2, %s.\n" + "Please report it to: bzip2-devel@sourceware.org. If this happened\n" + "when you were using some program which uses libbzip2 as a\n" + "component, you should also report this bug to the author(s)\n" + "of that program. Please make an effort to report this bug;\n" + "timely and accurate bug reports eventually lead to higher\n" + "quality software. Thanks.\n\n", + errcode, + BZ2_bzlibVersion() + ); + + if (errcode == 1007) { + fprintf(stderr, + "\n*** A special note about internal error number 1007 ***\n" + "\n" + "Experience suggests that a common cause of i.e. 1007\n" + "is unreliable memory or other hardware. The 1007 assertion\n" + "just happens to cross-check the results of huge numbers of\n" + "memory reads/writes, and so acts (unintendedly) as a stress\n" + "test of your memory system.\n" + "\n" + "I suggest the following: try compressing the file again,\n" + "possibly monitoring progress in detail with the -vv flag.\n" + "\n" + "* If the error cannot be reproduced, and/or happens at different\n" + " points in compression, you may have a flaky memory system.\n" + " Try a memory-test program. I have used Memtest86\n" + " (www.memtest86.com). At the time of writing it is free (GPLd).\n" + " Memtest86 tests memory much more thorougly than your BIOSs\n" + " power-on test, and may find failures that the BIOS doesn't.\n" + "\n" + "* If the error can be repeatably reproduced, this is a bug in\n" + " bzip2, and I would very much like to hear about it. Please\n" + " let me know, and, ideally, save a copy of the file causing the\n" + " problem -- without which I will be unable to investigate it.\n" + "\n" + ); + } + + exit(3); +} +#endif + + +/*---------------------------------------------------*/ +static +int bz_config_ok ( void ) +{ + if (sizeof(int) != 4) return 0; + if (sizeof(short) != 2) return 0; + if (sizeof(char) != 1) return 0; + return 1; +} + + +/*---------------------------------------------------*/ +static +void* default_bzalloc ( void* opaque, Int32 items, Int32 size ) +{ + void* v = malloc ( items * size ); + return v; +} + +static +void default_bzfree ( void* opaque, void* addr ) +{ + if (addr != NULL) free ( addr ); +} + + +/*---------------------------------------------------*/ +#ifndef BZ_NO_COMPRESS +static +void prepare_new_block ( EState* s ) +{ + Int32 i; + s->nblock = 0; + s->numZ = 0; + s->state_out_pos = 0; + BZ_INITIALISE_CRC ( s->blockCRC ); + for (i = 0; i < 256; i++) s->inUse[i] = False; + s->blockNo++; +} + + +/*---------------------------------------------------*/ +static +void init_RL ( EState* s ) +{ + s->state_in_ch = 256; + s->state_in_len = 0; +} + + +static +Bool isempty_RL ( EState* s ) +{ + if (s->state_in_ch < 256 && s->state_in_len > 0) + return False; else + return True; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressInit) + ( bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 n; + EState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL || + blockSize100k < 1 || blockSize100k > 9 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(EState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + + s->arr1 = NULL; + s->arr2 = NULL; + s->ftab = NULL; + + n = 100000 * blockSize100k; + s->arr1 = BZALLOC( n * sizeof(UInt32) ); + s->arr2 = BZALLOC( (n+BZ_N_OVERSHOOT) * sizeof(UInt32) ); + s->ftab = BZALLOC( 65537 * sizeof(UInt32) ); + + if (s->arr1 == NULL || s->arr2 == NULL || s->ftab == NULL) { + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + if (s != NULL) BZFREE(s); + return BZ_MEM_ERROR; + } + + s->blockNo = 0; + s->state = BZ_S_INPUT; + s->mode = BZ_M_RUNNING; + s->combinedCRC = 0; + s->blockSize100k = blockSize100k; + s->nblockMAX = 100000 * blockSize100k - 19; + s->verbosity = verbosity; + s->workFactor = workFactor; + + s->block = (UChar*)s->arr2; + s->mtfv = (UInt16*)s->arr1; + s->zbits = NULL; + s->ptr = (UInt32*)s->arr1; + + strm->state = s; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + init_RL ( s ); + prepare_new_block ( s ); + return BZ_OK; +} + + +/*---------------------------------------------------*/ +static +void add_pair_to_block ( EState* s ) +{ + Int32 i; + UChar ch = (UChar)(s->state_in_ch); + for (i = 0; i < s->state_in_len; i++) { + BZ_UPDATE_CRC( s->blockCRC, ch ); + } + s->inUse[s->state_in_ch] = True; + switch (s->state_in_len) { + case 1: + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 2: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + case 3: + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + break; + default: + s->inUse[s->state_in_len-4] = True; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = (UChar)ch; s->nblock++; + s->block[s->nblock] = ((UChar)(s->state_in_len-4)); + s->nblock++; + break; + } +} + + +/*---------------------------------------------------*/ +static +void flush_RL ( EState* s ) +{ + if (s->state_in_ch < 256) add_pair_to_block ( s ); + init_RL ( s ); +} + + +/*---------------------------------------------------*/ +#define ADD_CHAR_TO_BLOCK(zs,zchh0) \ +{ \ + UInt32 zchh = (UInt32)(zchh0); \ + /*-- fast track the common case --*/ \ + if (zchh != zs->state_in_ch && \ + zs->state_in_len == 1) { \ + UChar ch = (UChar)(zs->state_in_ch); \ + BZ_UPDATE_CRC( zs->blockCRC, ch ); \ + zs->inUse[zs->state_in_ch] = True; \ + zs->block[zs->nblock] = (UChar)ch; \ + zs->nblock++; \ + zs->state_in_ch = zchh; \ + } \ + else \ + /*-- general, uncommon cases --*/ \ + if (zchh != zs->state_in_ch || \ + zs->state_in_len == 255) { \ + if (zs->state_in_ch < 256) \ + add_pair_to_block ( zs ); \ + zs->state_in_ch = zchh; \ + zs->state_in_len = 1; \ + } else { \ + zs->state_in_len++; \ + } \ +} + + +/*---------------------------------------------------*/ +static +Bool copy_input_until_stop ( EState* s ) +{ + Bool progress_in = False; + + if (s->mode == BZ_M_RUNNING) { + + /*-- fast track the common case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + } + + } else { + + /*-- general, uncommon case --*/ + while (True) { + /*-- block full? --*/ + if (s->nblock >= s->nblockMAX) break; + /*-- no input? --*/ + if (s->strm->avail_in == 0) break; + /*-- flush/finish end? --*/ + if (s->avail_in_expect == 0) break; + progress_in = True; + ADD_CHAR_TO_BLOCK ( s, (UInt32)(*((UChar*)(s->strm->next_in))) ); + s->strm->next_in++; + s->strm->avail_in--; + s->strm->total_in_lo32++; + if (s->strm->total_in_lo32 == 0) s->strm->total_in_hi32++; + s->avail_in_expect--; + } + } + return progress_in; +} + + +/*---------------------------------------------------*/ +static +Bool copy_output_until_stop ( EState* s ) +{ + Bool progress_out = False; + + while (True) { + + /*-- no output space? --*/ + if (s->strm->avail_out == 0) break; + + /*-- block done? --*/ + if (s->state_out_pos >= s->numZ) break; + + progress_out = True; + *(s->strm->next_out) = s->zbits[s->state_out_pos]; + s->state_out_pos++; + s->strm->avail_out--; + s->strm->next_out++; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + return progress_out; +} + + +/*---------------------------------------------------*/ +static +Bool handle_compress ( bz_stream* strm ) +{ + Bool progress_in = False; + Bool progress_out = False; + EState* s = strm->state; + + while (True) { + + if (s->state == BZ_S_OUTPUT) { + progress_out |= copy_output_until_stop ( s ); + if (s->state_out_pos < s->numZ) break; + if (s->mode == BZ_M_FINISHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + prepare_new_block ( s ); + s->state = BZ_S_INPUT; + if (s->mode == BZ_M_FLUSHING && + s->avail_in_expect == 0 && + isempty_RL(s)) break; + } + + if (s->state == BZ_S_INPUT) { + progress_in |= copy_input_until_stop ( s ); + if (s->mode != BZ_M_RUNNING && s->avail_in_expect == 0) { + flush_RL ( s ); + BZ2_compressBlock ( s, (Bool)(s->mode == BZ_M_FINISHING) ); + s->state = BZ_S_OUTPUT; + } + else + if (s->nblock >= s->nblockMAX) { + BZ2_compressBlock ( s, False ); + s->state = BZ_S_OUTPUT; + } + else + if (s->strm->avail_in == 0) { + break; + } + } + + } + + return progress_in || progress_out; +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompress) ( bz_stream *strm, int action ) +{ + Bool progress; + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + preswitch: + switch (s->mode) { + + case BZ_M_IDLE: + return BZ_SEQUENCE_ERROR; + + case BZ_M_RUNNING: + if (action == BZ_RUN) { + progress = handle_compress ( strm ); + return progress ? BZ_RUN_OK : BZ_PARAM_ERROR; + } + else + if (action == BZ_FLUSH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FLUSHING; + goto preswitch; + } + else + if (action == BZ_FINISH) { + s->avail_in_expect = strm->avail_in; + s->mode = BZ_M_FINISHING; + goto preswitch; + } + else + return BZ_PARAM_ERROR; + + case BZ_M_FLUSHING: + if (action != BZ_FLUSH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FLUSH_OK; + s->mode = BZ_M_RUNNING; + return BZ_RUN_OK; + + case BZ_M_FINISHING: + if (action != BZ_FINISH) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect != s->strm->avail_in) + return BZ_SEQUENCE_ERROR; + progress = handle_compress ( strm ); + if (!progress) return BZ_SEQUENCE_ERROR; + if (s->avail_in_expect > 0 || !isempty_RL(s) || + s->state_out_pos < s->numZ) return BZ_FINISH_OK; + s->mode = BZ_M_IDLE; + return BZ_STREAM_END; + } + return BZ_OK; /*--not reached--*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzCompressEnd) ( bz_stream *strm ) +{ + EState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->arr1 != NULL) BZFREE(s->arr1); + if (s->arr2 != NULL) BZFREE(s->arr2); + if (s->ftab != NULL) BZFREE(s->ftab); + BZFREE(strm->state); + + strm->state = NULL; + + return BZ_OK; +} +#endif /* !BZ_NO_COMPRESS */ + + +/*---------------------------------------------------*/ +/*--- Decompression stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressInit) + ( bz_stream* strm, + int verbosity, + int small ) +{ + DState* s; + + if (!bz_config_ok()) return BZ_CONFIG_ERROR; + + if (strm == NULL) return BZ_PARAM_ERROR; + if (small != 0 && small != 1) return BZ_PARAM_ERROR; + if (verbosity < 0 || verbosity > 4) return BZ_PARAM_ERROR; + + if (strm->bzalloc == NULL) strm->bzalloc = default_bzalloc; + if (strm->bzfree == NULL) strm->bzfree = default_bzfree; + + s = BZALLOC( sizeof(DState) ); + if (s == NULL) return BZ_MEM_ERROR; + s->strm = strm; + strm->state = s; + s->state = BZ_X_MAGIC_1; + s->bsLive = 0; + s->bsBuff = 0; + s->calculatedCombinedCRC = 0; + strm->total_in_lo32 = 0; + strm->total_in_hi32 = 0; + strm->total_out_lo32 = 0; + strm->total_out_hi32 = 0; + s->smallDecompress = (Bool)small; + s->ll4 = NULL; + s->ll16 = NULL; + s->tt = NULL; + s->currBlockNo = 0; + s->verbosity = verbosity; + + return BZ_OK; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_FAST ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_FAST(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + /* restore */ + UInt32 c_calculatedBlockCRC = s->calculatedBlockCRC; + UChar c_state_out_ch = s->state_out_ch; + Int32 c_state_out_len = s->state_out_len; + Int32 c_nblock_used = s->nblock_used; + Int32 c_k0 = s->k0; + UInt32* c_tt = s->tt; + UInt32 c_tPos = s->tPos; + char* cs_next_out = s->strm->next_out; + unsigned int cs_avail_out = s->strm->avail_out; + Int32 ro_blockSize100k = s->blockSize100k; + /* end restore */ + + UInt32 avail_out_INIT = cs_avail_out; + Int32 s_save_nblockPP = s->save_nblock+1; + unsigned int total_out_lo32_old; + + while (True) { + + /* try to finish existing run */ + if (c_state_out_len > 0) { + while (True) { + if (cs_avail_out == 0) goto return_notr; + if (c_state_out_len == 1) break; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + c_state_out_len--; + cs_next_out++; + cs_avail_out--; + } + s_state_out_len_eq_one: + { + if (cs_avail_out == 0) { + c_state_out_len = 1; goto return_notr; + }; + *( (UChar*)(cs_next_out) ) = c_state_out_ch; + BZ_UPDATE_CRC ( c_calculatedBlockCRC, c_state_out_ch ); + cs_next_out++; + cs_avail_out--; + } + } + /* Only caused by corrupt data stream? */ + if (c_nblock_used > s_save_nblockPP) + return True; + + /* can a new run be started? */ + if (c_nblock_used == s_save_nblockPP) { + c_state_out_len = 0; goto return_notr; + }; + c_state_out_ch = c_k0; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (k1 != c_k0) { + c_k0 = k1; goto s_state_out_len_eq_one; + }; + if (c_nblock_used == s_save_nblockPP) + goto s_state_out_len_eq_one; + + c_state_out_len = 2; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + c_state_out_len = 3; + BZ_GET_FAST_C(k1); c_nblock_used++; + if (c_nblock_used == s_save_nblockPP) continue; + if (k1 != c_k0) { c_k0 = k1; continue; }; + + BZ_GET_FAST_C(k1); c_nblock_used++; + c_state_out_len = ((Int32)k1) + 4; + BZ_GET_FAST_C(c_k0); c_nblock_used++; + } + + return_notr: + total_out_lo32_old = s->strm->total_out_lo32; + s->strm->total_out_lo32 += (avail_out_INIT - cs_avail_out); + if (s->strm->total_out_lo32 < total_out_lo32_old) + s->strm->total_out_hi32++; + + /* save */ + s->calculatedBlockCRC = c_calculatedBlockCRC; + s->state_out_ch = c_state_out_ch; + s->state_out_len = c_state_out_len; + s->nblock_used = c_nblock_used; + s->k0 = c_k0; + s->tt = c_tt; + s->tPos = c_tPos; + s->strm->next_out = cs_next_out; + s->strm->avail_out = cs_avail_out; + /* end save */ + } + return False; +} + + + +/*---------------------------------------------------*/ +__inline__ Int32 BZ2_indexIntoF ( Int32 indx, Int32 *cftab ) +{ + Int32 nb, na, mid; + nb = 0; + na = 256; + do { + mid = (nb + na) >> 1; + if (indx >= cftab[mid]) nb = mid; else na = mid; + } + while (na - nb != 1); + return nb; +} + + +/*---------------------------------------------------*/ +/* Return True iff data corruption is discovered. + Returns False if there is no problem. +*/ +static +Bool unRLE_obuf_to_output_SMALL ( DState* s ) +{ + UChar k1; + + if (s->blockRandomised) { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); BZ_RAND_UPD_MASK; + k1 ^= BZ_RAND_MASK; s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); BZ_RAND_UPD_MASK; + s->k0 ^= BZ_RAND_MASK; s->nblock_used++; + } + + } else { + + while (True) { + /* try to finish existing run */ + while (True) { + if (s->strm->avail_out == 0) return False; + if (s->state_out_len == 0) break; + *( (UChar*)(s->strm->next_out) ) = s->state_out_ch; + BZ_UPDATE_CRC ( s->calculatedBlockCRC, s->state_out_ch ); + s->state_out_len--; + s->strm->next_out++; + s->strm->avail_out--; + s->strm->total_out_lo32++; + if (s->strm->total_out_lo32 == 0) s->strm->total_out_hi32++; + } + + /* can a new run be started? */ + if (s->nblock_used == s->save_nblock+1) return False; + + /* Only caused by corrupt data stream? */ + if (s->nblock_used > s->save_nblock+1) + return True; + + s->state_out_len = 1; + s->state_out_ch = s->k0; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 2; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + s->state_out_len = 3; + BZ_GET_SMALL(k1); s->nblock_used++; + if (s->nblock_used == s->save_nblock+1) continue; + if (k1 != s->k0) { s->k0 = k1; continue; }; + + BZ_GET_SMALL(k1); s->nblock_used++; + s->state_out_len = ((Int32)k1) + 4; + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompress) ( bz_stream *strm ) +{ + Bool corrupt; + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + while (True) { + if (s->state == BZ_X_IDLE) return BZ_SEQUENCE_ERROR; + if (s->state == BZ_X_OUTPUT) { + if (s->smallDecompress) + corrupt = unRLE_obuf_to_output_SMALL ( s ); else + corrupt = unRLE_obuf_to_output_FAST ( s ); + if (corrupt) return BZ_DATA_ERROR; + if (s->nblock_used == s->save_nblock+1 && s->state_out_len == 0) { + BZ_FINALISE_CRC ( s->calculatedBlockCRC ); + if (s->verbosity >= 3) + VPrintf2 ( " {0x%08x, 0x%08x}", s->storedBlockCRC, + s->calculatedBlockCRC ); + if (s->verbosity >= 2) VPrintf0 ( "]" ); + if (s->calculatedBlockCRC != s->storedBlockCRC) + return BZ_DATA_ERROR; + s->calculatedCombinedCRC + = (s->calculatedCombinedCRC << 1) | + (s->calculatedCombinedCRC >> 31); + s->calculatedCombinedCRC ^= s->calculatedBlockCRC; + s->state = BZ_X_BLKHDR_1; + } else { + return BZ_OK; + } + } + if (s->state >= BZ_X_MAGIC_1) { + Int32 r = BZ2_decompress ( s ); + if (r == BZ_STREAM_END) { + if (s->verbosity >= 3) + VPrintf2 ( "\n combined CRCs: stored = 0x%08x, computed = 0x%08x", + s->storedCombinedCRC, s->calculatedCombinedCRC ); + if (s->calculatedCombinedCRC != s->storedCombinedCRC) + return BZ_DATA_ERROR; + return r; + } + if (s->state != BZ_X_OUTPUT) return r; + } + } + + AssertH ( 0, 6001 ); + + return 0; /*NOTREACHED*/ +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzDecompressEnd) ( bz_stream *strm ) +{ + DState* s; + if (strm == NULL) return BZ_PARAM_ERROR; + s = strm->state; + if (s == NULL) return BZ_PARAM_ERROR; + if (s->strm != strm) return BZ_PARAM_ERROR; + + if (s->tt != NULL) BZFREE(s->tt); + if (s->ll16 != NULL) BZFREE(s->ll16); + if (s->ll4 != NULL) BZFREE(s->ll4); + + BZFREE(strm->state); + strm->state = NULL; + + return BZ_OK; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ +/*--- File I/O stuff ---*/ +/*---------------------------------------------------*/ + +#define BZ_SETERR(eee) \ +{ \ + if (bzerror != NULL) *bzerror = eee; \ + if (bzf != NULL) bzf->lastErr = eee; \ +} + +typedef + struct { + FILE* handle; + Char buf[BZ_MAX_UNUSED]; + Int32 bufN; + Bool writing; + bz_stream strm; + Int32 lastErr; + Bool initialisedOk; + } + bzFile; + + +/*---------------------------------------------*/ +static Bool myfeof ( FILE* f ) +{ + Int32 c = fgetc ( f ); + if (c == EOF) return True; + ungetc ( c, f ); + return False; +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzWriteOpen) + ( int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor ) +{ + Int32 ret; + bzFile* bzf = NULL; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (blockSize100k < 1 || blockSize100k > 9) || + (workFactor < 0 || workFactor > 250) || + (verbosity < 0 || verbosity > 4)) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + bzf->initialisedOk = False; + bzf->bufN = 0; + bzf->handle = f; + bzf->writing = True; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + if (workFactor == 0) workFactor = 30; + ret = BZ2_bzCompressInit ( &(bzf->strm), blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = 0; + bzf->initialisedOk = True; + return bzf; +} + + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWrite) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return; }; + + bzf->strm.avail_in = len; + bzf->strm.next_in = buf; + + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_RUN ); + if (ret != BZ_RUN_OK) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (bzf->strm.avail_in == 0) + { BZ_SETERR(BZ_OK); return; }; + } +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzWriteClose) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out ) +{ + BZ2_bzWriteClose64 ( bzerror, b, abandon, + nbytes_in, NULL, nbytes_out, NULL ); +} + + +void BZ_API(BZ2_bzWriteClose64) + ( int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 ) +{ + Int32 n, n2, ret; + bzFile* bzf = (bzFile*)b; + + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + if (!(bzf->writing)) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + + if (nbytes_in_lo32 != NULL) *nbytes_in_lo32 = 0; + if (nbytes_in_hi32 != NULL) *nbytes_in_hi32 = 0; + if (nbytes_out_lo32 != NULL) *nbytes_out_lo32 = 0; + if (nbytes_out_hi32 != NULL) *nbytes_out_hi32 = 0; + + if ((!abandon) && bzf->lastErr == BZ_OK) { + while (True) { + bzf->strm.avail_out = BZ_MAX_UNUSED; + bzf->strm.next_out = bzf->buf; + ret = BZ2_bzCompress ( &(bzf->strm), BZ_FINISH ); + if (ret != BZ_FINISH_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return; }; + + if (bzf->strm.avail_out < BZ_MAX_UNUSED) { + n = BZ_MAX_UNUSED - bzf->strm.avail_out; + n2 = fwrite ( (void*)(bzf->buf), sizeof(UChar), + n, bzf->handle ); + if (n != n2 || ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (ret == BZ_STREAM_END) break; + } + } + + if ( !abandon && !ferror ( bzf->handle ) ) { + fflush ( bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return; }; + } + + if (nbytes_in_lo32 != NULL) + *nbytes_in_lo32 = bzf->strm.total_in_lo32; + if (nbytes_in_hi32 != NULL) + *nbytes_in_hi32 = bzf->strm.total_in_hi32; + if (nbytes_out_lo32 != NULL) + *nbytes_out_lo32 = bzf->strm.total_out_lo32; + if (nbytes_out_hi32 != NULL) + *nbytes_out_hi32 = bzf->strm.total_out_hi32; + + BZ_SETERR(BZ_OK); + BZ2_bzCompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +BZFILE* BZ_API(BZ2_bzReadOpen) + ( int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused ) +{ + bzFile* bzf = NULL; + int ret; + + BZ_SETERR(BZ_OK); + + if (f == NULL || + (small != 0 && small != 1) || + (verbosity < 0 || verbosity > 4) || + (unused == NULL && nUnused != 0) || + (unused != NULL && (nUnused < 0 || nUnused > BZ_MAX_UNUSED))) + { BZ_SETERR(BZ_PARAM_ERROR); return NULL; }; + + if (ferror(f)) + { BZ_SETERR(BZ_IO_ERROR); return NULL; }; + + bzf = malloc ( sizeof(bzFile) ); + if (bzf == NULL) + { BZ_SETERR(BZ_MEM_ERROR); return NULL; }; + + BZ_SETERR(BZ_OK); + + bzf->initialisedOk = False; + bzf->handle = f; + bzf->bufN = 0; + bzf->writing = False; + bzf->strm.bzalloc = NULL; + bzf->strm.bzfree = NULL; + bzf->strm.opaque = NULL; + + while (nUnused > 0) { + bzf->buf[bzf->bufN] = *((UChar*)(unused)); bzf->bufN++; + unused = ((void*)( 1 + ((UChar*)(unused)) )); + nUnused--; + } + + ret = BZ2_bzDecompressInit ( &(bzf->strm), verbosity, small ); + if (ret != BZ_OK) + { BZ_SETERR(ret); free(bzf); return NULL; }; + + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + + bzf->initialisedOk = True; + return bzf; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadClose) ( int *bzerror, BZFILE *b ) +{ + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + if (bzf == NULL) + { BZ_SETERR(BZ_OK); return; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + + if (bzf->initialisedOk) + (void)BZ2_bzDecompressEnd ( &(bzf->strm) ); + free ( bzf ); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzRead) + ( int* bzerror, + BZFILE* b, + void* buf, + int len ) +{ + Int32 n, ret; + bzFile* bzf = (bzFile*)b; + + BZ_SETERR(BZ_OK); + + if (bzf == NULL || buf == NULL || len < 0) + { BZ_SETERR(BZ_PARAM_ERROR); return 0; }; + + if (bzf->writing) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return 0; }; + + if (len == 0) + { BZ_SETERR(BZ_OK); return 0; }; + + bzf->strm.avail_out = len; + bzf->strm.next_out = buf; + + while (True) { + + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + + if (bzf->strm.avail_in == 0 && !myfeof(bzf->handle)) { + n = fread ( bzf->buf, sizeof(UChar), + BZ_MAX_UNUSED, bzf->handle ); + if (ferror(bzf->handle)) + { BZ_SETERR(BZ_IO_ERROR); return 0; }; + bzf->bufN = n; + bzf->strm.avail_in = bzf->bufN; + bzf->strm.next_in = bzf->buf; + } + + ret = BZ2_bzDecompress ( &(bzf->strm) ); + + if (ret != BZ_OK && ret != BZ_STREAM_END) + { BZ_SETERR(ret); return 0; }; + + if (ret == BZ_OK && myfeof(bzf->handle) && + bzf->strm.avail_in == 0 && bzf->strm.avail_out > 0) + { BZ_SETERR(BZ_UNEXPECTED_EOF); return 0; }; + + if (ret == BZ_STREAM_END) + { BZ_SETERR(BZ_STREAM_END); + return len - bzf->strm.avail_out; }; + if (bzf->strm.avail_out == 0) + { BZ_SETERR(BZ_OK); return len; }; + + } + + return 0; /*not reached*/ +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzReadGetUnused) + ( int* bzerror, + BZFILE* b, + void** unused, + int* nUnused ) +{ + bzFile* bzf = (bzFile*)b; + if (bzf == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + if (bzf->lastErr != BZ_STREAM_END) + { BZ_SETERR(BZ_SEQUENCE_ERROR); return; }; + if (unused == NULL || nUnused == NULL) + { BZ_SETERR(BZ_PARAM_ERROR); return; }; + + BZ_SETERR(BZ_OK); + *nUnused = bzf->strm.avail_in; + *unused = bzf->strm.next_in; +} +#endif + + +/*---------------------------------------------------*/ +/*--- Misc convenience stuff ---*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +#ifndef BZ_NO_COMPRESS +int BZ_API(BZ2_bzBuffToBuffCompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + blockSize100k < 1 || blockSize100k > 9 || + verbosity < 0 || verbosity > 4 || + workFactor < 0 || workFactor > 250) + return BZ_PARAM_ERROR; + + if (workFactor == 0) workFactor = 30; + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzCompressInit ( &strm, blockSize100k, + verbosity, workFactor ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzCompress ( &strm, BZ_FINISH ); + if (ret == BZ_FINISH_OK) goto output_overflow; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzCompressEnd ( &strm ); + return BZ_OK; + + output_overflow: + BZ2_bzCompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + + errhandler: + BZ2_bzCompressEnd ( &strm ); + return ret; +} +#endif /* !BZ_NO_COMPRESS */ + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzBuffToBuffDecompress) + ( char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity ) +{ + bz_stream strm; + int ret; + + if (dest == NULL || destLen == NULL || + source == NULL || + (small != 0 && small != 1) || + verbosity < 0 || verbosity > 4) + return BZ_PARAM_ERROR; + + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + ret = BZ2_bzDecompressInit ( &strm, verbosity, small ); + if (ret != BZ_OK) return ret; + + strm.next_in = source; + strm.next_out = dest; + strm.avail_in = sourceLen; + strm.avail_out = *destLen; + + ret = BZ2_bzDecompress ( &strm ); + if (ret == BZ_OK) goto output_overflow_or_eof; + if (ret != BZ_STREAM_END) goto errhandler; + + /* normal termination */ + *destLen -= strm.avail_out; + BZ2_bzDecompressEnd ( &strm ); + return BZ_OK; + + output_overflow_or_eof: + if (strm.avail_out > 0) { + BZ2_bzDecompressEnd ( &strm ); + return BZ_UNEXPECTED_EOF; + } else { + BZ2_bzDecompressEnd ( &strm ); + return BZ_OUTBUFF_FULL; + }; + + errhandler: + BZ2_bzDecompressEnd ( &strm ); + return ret; +} + + +/*---------------------------------------------------*/ +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ +/*---------------------------------------------------*/ + +/*---------------------------------------------------*/ +/*-- + return version like "0.9.5d, 4-Sept-1999". +--*/ +const char * BZ_API(BZ2_bzlibVersion)(void) +{ + return BZ_VERSION; +} + + +#ifndef BZ_NO_STDIO +/*---------------------------------------------------*/ + +#if defined(_WIN32) || defined(OS2) || defined(MSDOS) +# include +# include +# define SET_BINARY_MODE(file) setmode(fileno(file),O_BINARY) +#else +# define SET_BINARY_MODE(file) +#endif +static +BZFILE * bzopen_or_bzdopen + ( const char *path, /* no use when bzdopen */ + int fd, /* no use when bzdopen */ + const char *mode, + int open_mode) /* bzopen: 0, bzdopen:1 */ +{ + int bzerr; + char unused[BZ_MAX_UNUSED]; + int blockSize100k = 9; + int writing = 0; + char mode2[10] = ""; + FILE *fp = NULL; + BZFILE *bzfp = NULL; + int verbosity = 0; + int workFactor = 30; + int smallMode = 0; + int nUnused = 0; + + if (mode == NULL) return NULL; + while (*mode) { + switch (*mode) { + case 'r': + writing = 0; break; + case 'w': + writing = 1; break; + case 's': + smallMode = 1; break; + default: + if (isdigit((unsigned char)(*mode))) { + blockSize100k = *mode-BZ_HDR_0; + } + } + mode++; + } + strcat(mode2, writing ? "w" : "r" ); + strcat(mode2,"b"); /* binary mode */ + + if (open_mode==0) { + if (path==NULL || strcmp(path,"")==0) { + fp = (writing ? stdout : stdin); + SET_BINARY_MODE(fp); + } else { + fp = fopen(path,mode2); + } + } else { +#ifdef BZ_STRICT_ANSI + fp = NULL; +#else + fp = fdopen(fd,mode2); +#endif + } + if (fp == NULL) return NULL; + + if (writing) { + /* Guard against total chaos and anarchy -- JRS */ + if (blockSize100k < 1) blockSize100k = 1; + if (blockSize100k > 9) blockSize100k = 9; + bzfp = BZ2_bzWriteOpen(&bzerr,fp,blockSize100k, + verbosity,workFactor); + } else { + bzfp = BZ2_bzReadOpen(&bzerr,fp,verbosity,smallMode, + unused,nUnused); + } + if (bzfp == NULL) { + if (fp != stdin && fp != stdout) fclose(fp); + return NULL; + } + return bzfp; +} + + +/*---------------------------------------------------*/ +/*-- + open file for read or write. + ex) bzopen("file","w9") + case path="" or NULL => use stdin or stdout. +--*/ +BZFILE * BZ_API(BZ2_bzopen) + ( const char *path, + const char *mode ) +{ + return bzopen_or_bzdopen(path,-1,mode,/*bzopen*/0); +} + + +/*---------------------------------------------------*/ +BZFILE * BZ_API(BZ2_bzdopen) + ( int fd, + const char *mode ) +{ + return bzopen_or_bzdopen(NULL,fd,mode,/*bzdopen*/1); +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzread) (BZFILE* b, void* buf, int len ) +{ + int bzerr, nread; + if (((bzFile*)b)->lastErr == BZ_STREAM_END) return 0; + nread = BZ2_bzRead(&bzerr,b,buf,len); + if (bzerr == BZ_OK || bzerr == BZ_STREAM_END) { + return nread; + } else { + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzwrite) (BZFILE* b, void* buf, int len ) +{ + int bzerr; + + BZ2_bzWrite(&bzerr,b,buf,len); + if(bzerr == BZ_OK){ + return len; + }else{ + return -1; + } +} + + +/*---------------------------------------------------*/ +int BZ_API(BZ2_bzflush) (BZFILE *b) +{ + /* do nothing now... */ + return 0; +} + + +/*---------------------------------------------------*/ +void BZ_API(BZ2_bzclose) (BZFILE* b) +{ + int bzerr; + FILE *fp; + + if (b==NULL) {return;} + fp = ((bzFile *)b)->handle; + if(((bzFile*)b)->writing){ + BZ2_bzWriteClose(&bzerr,b,0,NULL,NULL); + if(bzerr != BZ_OK){ + BZ2_bzWriteClose(NULL,b,1,NULL,NULL); + } + }else{ + BZ2_bzReadClose(&bzerr,b); + } + if(fp!=stdin && fp!=stdout){ + fclose(fp); + } +} + + +/*---------------------------------------------------*/ +/*-- + return last error code +--*/ +static const char *bzerrorstrings[] = { + "OK" + ,"SEQUENCE_ERROR" + ,"PARAM_ERROR" + ,"MEM_ERROR" + ,"DATA_ERROR" + ,"DATA_ERROR_MAGIC" + ,"IO_ERROR" + ,"UNEXPECTED_EOF" + ,"OUTBUFF_FULL" + ,"CONFIG_ERROR" + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ + ,"???" /* for future */ +}; + + +const char * BZ_API(BZ2_bzerror) (BZFILE *b, int *errnum) +{ + int err = ((bzFile *)b)->lastErr; + + if(err>0) err = 0; + *errnum = err; + return bzerrorstrings[err*-1]; +} +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/bzlib.h b/android/app/src/main/cpp/third_party/bzip2/bzlib.h new file mode 100644 index 00000000..8966a6c5 --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/bzlib.h @@ -0,0 +1,282 @@ + +/*-------------------------------------------------------------*/ +/*--- Public header file for the library. ---*/ +/*--- bzlib.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_H +#define _BZLIB_H + +#ifdef __cplusplus +extern "C" { +#endif + +#define BZ_RUN 0 +#define BZ_FLUSH 1 +#define BZ_FINISH 2 + +#define BZ_OK 0 +#define BZ_RUN_OK 1 +#define BZ_FLUSH_OK 2 +#define BZ_FINISH_OK 3 +#define BZ_STREAM_END 4 +#define BZ_SEQUENCE_ERROR (-1) +#define BZ_PARAM_ERROR (-2) +#define BZ_MEM_ERROR (-3) +#define BZ_DATA_ERROR (-4) +#define BZ_DATA_ERROR_MAGIC (-5) +#define BZ_IO_ERROR (-6) +#define BZ_UNEXPECTED_EOF (-7) +#define BZ_OUTBUFF_FULL (-8) +#define BZ_CONFIG_ERROR (-9) + +typedef + struct { + char *next_in; + unsigned int avail_in; + unsigned int total_in_lo32; + unsigned int total_in_hi32; + + char *next_out; + unsigned int avail_out; + unsigned int total_out_lo32; + unsigned int total_out_hi32; + + void *state; + + void *(*bzalloc)(void *,int,int); + void (*bzfree)(void *,void *); + void *opaque; + } + bz_stream; + + +#ifndef BZ_IMPORT +#define BZ_EXPORT +#endif + +#ifndef BZ_NO_STDIO +/* Need a definitition for FILE */ +#include +#endif + +#ifdef _WIN32 +# include +# ifdef small + /* windows.h define small to char */ +# undef small +# endif +# ifdef BZ_EXPORT +# define BZ_API(func) WINAPI func +# define BZ_EXTERN extern +# else + /* import windows dll dynamically */ +# define BZ_API(func) (WINAPI * func) +# define BZ_EXTERN +# endif +#else +# define BZ_API(func) func +# define BZ_EXTERN extern +#endif + + +/*-- Core (low-level) library functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzCompressInit) ( + bz_stream* strm, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompress) ( + bz_stream* strm, + int action + ); + +BZ_EXTERN int BZ_API(BZ2_bzCompressEnd) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressInit) ( + bz_stream *strm, + int verbosity, + int small + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompress) ( + bz_stream* strm + ); + +BZ_EXTERN int BZ_API(BZ2_bzDecompressEnd) ( + bz_stream *strm + ); + + + +/*-- High(er) level library functions --*/ + +#ifndef BZ_NO_STDIO +#define BZ_MAX_UNUSED 5000 + +typedef void BZFILE; + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzReadOpen) ( + int* bzerror, + FILE* f, + int verbosity, + int small, + void* unused, + int nUnused + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadClose) ( + int* bzerror, + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzReadGetUnused) ( + int* bzerror, + BZFILE* b, + void** unused, + int* nUnused + ); + +BZ_EXTERN int BZ_API(BZ2_bzRead) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN BZFILE* BZ_API(BZ2_bzWriteOpen) ( + int* bzerror, + FILE* f, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN void BZ_API(BZ2_bzWrite) ( + int* bzerror, + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in, + unsigned int* nbytes_out + ); + +BZ_EXTERN void BZ_API(BZ2_bzWriteClose64) ( + int* bzerror, + BZFILE* b, + int abandon, + unsigned int* nbytes_in_lo32, + unsigned int* nbytes_in_hi32, + unsigned int* nbytes_out_lo32, + unsigned int* nbytes_out_hi32 + ); +#endif + + +/*-- Utility functions --*/ + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffCompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int blockSize100k, + int verbosity, + int workFactor + ); + +BZ_EXTERN int BZ_API(BZ2_bzBuffToBuffDecompress) ( + char* dest, + unsigned int* destLen, + char* source, + unsigned int sourceLen, + int small, + int verbosity + ); + + +/*-- + Code contributed by Yoshioka Tsuneo (tsuneo@rr.iij4u.or.jp) + to support better zlib compatibility. + This code is not _officially_ part of libbzip2 (yet); + I haven't tested it, documented it, or considered the + threading-safeness of it. + If this code breaks, please contact both Yoshioka and me. +--*/ + +BZ_EXTERN const char * BZ_API(BZ2_bzlibVersion) ( + void + ); + +#ifndef BZ_NO_STDIO +BZ_EXTERN BZFILE * BZ_API(BZ2_bzopen) ( + const char *path, + const char *mode + ); + +BZ_EXTERN BZFILE * BZ_API(BZ2_bzdopen) ( + int fd, + const char *mode + ); + +BZ_EXTERN int BZ_API(BZ2_bzread) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzwrite) ( + BZFILE* b, + void* buf, + int len + ); + +BZ_EXTERN int BZ_API(BZ2_bzflush) ( + BZFILE* b + ); + +BZ_EXTERN void BZ_API(BZ2_bzclose) ( + BZFILE* b + ); + +BZ_EXTERN const char * BZ_API(BZ2_bzerror) ( + BZFILE *b, + int *errnum + ); +#endif + +#ifdef __cplusplus +} +#endif + +#endif + +/*-------------------------------------------------------------*/ +/*--- end bzlib.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/bzlib_private.h b/android/app/src/main/cpp/third_party/bzip2/bzlib_private.h new file mode 100644 index 00000000..3755a6f7 --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/bzlib_private.h @@ -0,0 +1,509 @@ + +/*-------------------------------------------------------------*/ +/*--- Private header file for the library. ---*/ +/*--- bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#ifndef _BZLIB_PRIVATE_H +#define _BZLIB_PRIVATE_H + +#include + +#ifndef BZ_NO_STDIO +#include +#include +#include +#endif + +#include "bzlib.h" + + + +/*-- General stuff. --*/ + +#define BZ_VERSION "1.0.8, 13-Jul-2019" + +typedef char Char; +typedef unsigned char Bool; +typedef unsigned char UChar; +typedef int Int32; +typedef unsigned int UInt32; +typedef short Int16; +typedef unsigned short UInt16; + +#define True ((Bool)1) +#define False ((Bool)0) + +#ifndef __GNUC__ +#define __inline__ /* */ +#endif + +#ifndef BZ_NO_STDIO + +extern void BZ2_bz__AssertH__fail ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) BZ2_bz__AssertH__fail ( errcode ); } + +#if BZ_DEBUG +#define AssertD(cond,msg) \ + { if (!(cond)) { \ + fprintf ( stderr, \ + "\n\nlibbzip2(debug build): internal error\n\t%s\n", msg );\ + exit(1); \ + }} +#else +#define AssertD(cond,msg) /* */ +#endif + +#define VPrintf0(zf) \ + fprintf(stderr,zf) +#define VPrintf1(zf,za1) \ + fprintf(stderr,zf,za1) +#define VPrintf2(zf,za1,za2) \ + fprintf(stderr,zf,za1,za2) +#define VPrintf3(zf,za1,za2,za3) \ + fprintf(stderr,zf,za1,za2,za3) +#define VPrintf4(zf,za1,za2,za3,za4) \ + fprintf(stderr,zf,za1,za2,za3,za4) +#define VPrintf5(zf,za1,za2,za3,za4,za5) \ + fprintf(stderr,zf,za1,za2,za3,za4,za5) + +#else + +extern void bz_internal_error ( int errcode ); +#define AssertH(cond,errcode) \ + { if (!(cond)) bz_internal_error ( errcode ); } +#define AssertD(cond,msg) do { } while (0) +#define VPrintf0(zf) do { } while (0) +#define VPrintf1(zf,za1) do { } while (0) +#define VPrintf2(zf,za1,za2) do { } while (0) +#define VPrintf3(zf,za1,za2,za3) do { } while (0) +#define VPrintf4(zf,za1,za2,za3,za4) do { } while (0) +#define VPrintf5(zf,za1,za2,za3,za4,za5) do { } while (0) + +#endif + + +#define BZALLOC(nnn) (strm->bzalloc)(strm->opaque,(nnn),1) +#define BZFREE(ppp) (strm->bzfree)(strm->opaque,(ppp)) + + +/*-- Header bytes. --*/ + +#define BZ_HDR_B 0x42 /* 'B' */ +#define BZ_HDR_Z 0x5a /* 'Z' */ +#define BZ_HDR_h 0x68 /* 'h' */ +#define BZ_HDR_0 0x30 /* '0' */ + +/*-- Constants for the back end. --*/ + +#define BZ_MAX_ALPHA_SIZE 258 +#define BZ_MAX_CODE_LEN 23 + +#define BZ_RUNA 0 +#define BZ_RUNB 1 + +#define BZ_N_GROUPS 6 +#define BZ_G_SIZE 50 +#define BZ_N_ITERS 4 + +#define BZ_MAX_SELECTORS (2 + (900000 / BZ_G_SIZE)) + + + +/*-- Stuff for randomising repetitive blocks. --*/ + +extern Int32 BZ2_rNums[512]; + +#define BZ_RAND_DECLS \ + Int32 rNToGo; \ + Int32 rTPos \ + +#define BZ_RAND_INIT_MASK \ + s->rNToGo = 0; \ + s->rTPos = 0 \ + +#define BZ_RAND_MASK ((s->rNToGo == 1) ? 1 : 0) + +#define BZ_RAND_UPD_MASK \ + if (s->rNToGo == 0) { \ + s->rNToGo = BZ2_rNums[s->rTPos]; \ + s->rTPos++; \ + if (s->rTPos == 512) s->rTPos = 0; \ + } \ + s->rNToGo--; + + + +/*-- Stuff for doing CRCs. --*/ + +extern UInt32 BZ2_crc32Table[256]; + +#define BZ_INITIALISE_CRC(crcVar) \ +{ \ + crcVar = 0xffffffffL; \ +} + +#define BZ_FINALISE_CRC(crcVar) \ +{ \ + crcVar = ~(crcVar); \ +} + +#define BZ_UPDATE_CRC(crcVar,cha) \ +{ \ + crcVar = (crcVar << 8) ^ \ + BZ2_crc32Table[(crcVar >> 24) ^ \ + ((UChar)cha)]; \ +} + + + +/*-- States and modes for compression. --*/ + +#define BZ_M_IDLE 1 +#define BZ_M_RUNNING 2 +#define BZ_M_FLUSHING 3 +#define BZ_M_FINISHING 4 + +#define BZ_S_OUTPUT 1 +#define BZ_S_INPUT 2 + +#define BZ_N_RADIX 2 +#define BZ_N_QSORT 12 +#define BZ_N_SHELL 18 +#define BZ_N_OVERSHOOT (BZ_N_RADIX + BZ_N_QSORT + BZ_N_SHELL + 2) + + + + +/*-- Structure holding all the compression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* mode this stream is in, and whether inputting */ + /* or outputting data */ + Int32 mode; + Int32 state; + + /* remembers avail_in when flush/finish requested */ + UInt32 avail_in_expect; + + /* for doing the block sorting */ + UInt32* arr1; + UInt32* arr2; + UInt32* ftab; + Int32 origPtr; + + /* aliases for arr1 and arr2 */ + UInt32* ptr; + UChar* block; + UInt16* mtfv; + UChar* zbits; + + /* for deciding when to use the fallback sorting algorithm */ + Int32 workFactor; + + /* run-length-encoding of the input */ + UInt32 state_in_ch; + Int32 state_in_len; + BZ_RAND_DECLS; + + /* input and output limits and current posns */ + Int32 nblock; + Int32 nblockMAX; + Int32 numZ; + Int32 state_out_pos; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + UChar unseqToSeq[256]; + + /* the buffer for bit stream creation */ + UInt32 bsBuff; + Int32 bsLive; + + /* block and combined CRCs */ + UInt32 blockCRC; + UInt32 combinedCRC; + + /* misc administratium */ + Int32 verbosity; + Int32 blockNo; + Int32 blockSize100k; + + /* stuff for coding the MTF values */ + Int32 nMTF; + Int32 mtfFreq [BZ_MAX_ALPHA_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 code [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 rfreq [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + /* second dimension: only 3 needed; 4 makes index calculations faster */ + UInt32 len_pack[BZ_MAX_ALPHA_SIZE][4]; + + } + EState; + + + +/*-- externs for compression. --*/ + +extern void +BZ2_blockSort ( EState* ); + +extern void +BZ2_compressBlock ( EState*, Bool ); + +extern void +BZ2_bsInitWrite ( EState* ); + +extern void +BZ2_hbAssignCodes ( Int32*, UChar*, Int32, Int32, Int32 ); + +extern void +BZ2_hbMakeCodeLengths ( UChar*, Int32*, Int32, Int32 ); + + + +/*-- states for decompression. --*/ + +#define BZ_X_IDLE 1 +#define BZ_X_OUTPUT 2 + +#define BZ_X_MAGIC_1 10 +#define BZ_X_MAGIC_2 11 +#define BZ_X_MAGIC_3 12 +#define BZ_X_MAGIC_4 13 +#define BZ_X_BLKHDR_1 14 +#define BZ_X_BLKHDR_2 15 +#define BZ_X_BLKHDR_3 16 +#define BZ_X_BLKHDR_4 17 +#define BZ_X_BLKHDR_5 18 +#define BZ_X_BLKHDR_6 19 +#define BZ_X_BCRC_1 20 +#define BZ_X_BCRC_2 21 +#define BZ_X_BCRC_3 22 +#define BZ_X_BCRC_4 23 +#define BZ_X_RANDBIT 24 +#define BZ_X_ORIGPTR_1 25 +#define BZ_X_ORIGPTR_2 26 +#define BZ_X_ORIGPTR_3 27 +#define BZ_X_MAPPING_1 28 +#define BZ_X_MAPPING_2 29 +#define BZ_X_SELECTOR_1 30 +#define BZ_X_SELECTOR_2 31 +#define BZ_X_SELECTOR_3 32 +#define BZ_X_CODING_1 33 +#define BZ_X_CODING_2 34 +#define BZ_X_CODING_3 35 +#define BZ_X_MTF_1 36 +#define BZ_X_MTF_2 37 +#define BZ_X_MTF_3 38 +#define BZ_X_MTF_4 39 +#define BZ_X_MTF_5 40 +#define BZ_X_MTF_6 41 +#define BZ_X_ENDHDR_2 42 +#define BZ_X_ENDHDR_3 43 +#define BZ_X_ENDHDR_4 44 +#define BZ_X_ENDHDR_5 45 +#define BZ_X_ENDHDR_6 46 +#define BZ_X_CCRC_1 47 +#define BZ_X_CCRC_2 48 +#define BZ_X_CCRC_3 49 +#define BZ_X_CCRC_4 50 + + + +/*-- Constants for the fast MTF decoder. --*/ + +#define MTFA_SIZE 4096 +#define MTFL_SIZE 16 + + + +/*-- Structure holding all the decompression-side stuff. --*/ + +typedef + struct { + /* pointer back to the struct bz_stream */ + bz_stream* strm; + + /* state indicator for this stream */ + Int32 state; + + /* for doing the final run-length decoding */ + UChar state_out_ch; + Int32 state_out_len; + Bool blockRandomised; + BZ_RAND_DECLS; + + /* the buffer for bit stream reading */ + UInt32 bsBuff; + Int32 bsLive; + + /* misc administratium */ + Int32 blockSize100k; + Bool smallDecompress; + Int32 currBlockNo; + Int32 verbosity; + + /* for undoing the Burrows-Wheeler transform */ + Int32 origPtr; + UInt32 tPos; + Int32 k0; + Int32 unzftab[256]; + Int32 nblock_used; + Int32 cftab[257]; + Int32 cftabCopy[257]; + + /* for undoing the Burrows-Wheeler transform (FAST) */ + UInt32 *tt; + + /* for undoing the Burrows-Wheeler transform (SMALL) */ + UInt16 *ll16; + UChar *ll4; + + /* stored and calculated CRCs */ + UInt32 storedBlockCRC; + UInt32 storedCombinedCRC; + UInt32 calculatedBlockCRC; + UInt32 calculatedCombinedCRC; + + /* map of bytes used in block */ + Int32 nInUse; + Bool inUse[256]; + Bool inUse16[16]; + UChar seqToUnseq[256]; + + /* for decoding the MTF values */ + UChar mtfa [MTFA_SIZE]; + Int32 mtfbase[256 / MTFL_SIZE]; + UChar selector [BZ_MAX_SELECTORS]; + UChar selectorMtf[BZ_MAX_SELECTORS]; + UChar len [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + + Int32 limit [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 base [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 perm [BZ_N_GROUPS][BZ_MAX_ALPHA_SIZE]; + Int32 minLens[BZ_N_GROUPS]; + + /* save area for scalars in the main decompress code */ + Int32 save_i; + Int32 save_j; + Int32 save_t; + Int32 save_alphaSize; + Int32 save_nGroups; + Int32 save_nSelectors; + Int32 save_EOB; + Int32 save_groupNo; + Int32 save_groupPos; + Int32 save_nextSym; + Int32 save_nblockMAX; + Int32 save_nblock; + Int32 save_es; + Int32 save_N; + Int32 save_curr; + Int32 save_zt; + Int32 save_zn; + Int32 save_zvec; + Int32 save_zj; + Int32 save_gSel; + Int32 save_gMinlen; + Int32* save_gLimit; + Int32* save_gBase; + Int32* save_gPerm; + + } + DState; + + + +/*-- Macros for decompression. --*/ + +#define BZ_GET_FAST(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + s->tPos = s->tt[s->tPos]; \ + cccc = (UChar)(s->tPos & 0xff); \ + s->tPos >>= 8; + +#define BZ_GET_FAST_C(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (c_tPos >= (UInt32)100000 * (UInt32)ro_blockSize100k) return True; \ + c_tPos = c_tt[c_tPos]; \ + cccc = (UChar)(c_tPos & 0xff); \ + c_tPos >>= 8; + +#define SET_LL4(i,n) \ + { if (((i) & 0x1) == 0) \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0xf0) | (n); else \ + s->ll4[(i) >> 1] = (s->ll4[(i) >> 1] & 0x0f) | ((n) << 4); \ + } + +#define GET_LL4(i) \ + ((((UInt32)(s->ll4[(i) >> 1])) >> (((i) << 2) & 0x4)) & 0xF) + +#define SET_LL(i,n) \ + { s->ll16[i] = (UInt16)(n & 0x0000ffff); \ + SET_LL4(i, n >> 16); \ + } + +#define GET_LL(i) \ + (((UInt32)s->ll16[i]) | (GET_LL4(i) << 16)) + +#define BZ_GET_SMALL(cccc) \ + /* c_tPos is unsigned, hence test < 0 is pointless. */ \ + if (s->tPos >= (UInt32)100000 * (UInt32)s->blockSize100k) return True; \ + cccc = BZ2_indexIntoF ( s->tPos, s->cftab ); \ + s->tPos = GET_LL(s->tPos); + + +/*-- externs for decompression. --*/ + +extern Int32 +BZ2_indexIntoF ( Int32, Int32* ); + +extern Int32 +BZ2_decompress ( DState* ); + +extern void +BZ2_hbCreateDecodeTables ( Int32*, Int32*, Int32*, UChar*, + Int32, Int32, Int32 ); + + +#endif + + +/*-- BZ_NO_STDIO seems to make NULL disappear on some platforms. --*/ + +#ifdef BZ_NO_STDIO +#ifndef NULL +#define NULL 0 +#endif +#endif + + +/*-------------------------------------------------------------*/ +/*--- end bzlib_private.h ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/crctable.c b/android/app/src/main/cpp/third_party/bzip2/crctable.c new file mode 100644 index 00000000..2b33c253 --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/crctable.c @@ -0,0 +1,104 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for doing CRCs ---*/ +/*--- crctable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*-- + I think this is an implementation of the AUTODIN-II, + Ethernet & FDDI 32-bit CRC standard. Vaguely derived + from code by Rob Warnock, in Section 51 of the + comp.compression FAQ. +--*/ + +UInt32 BZ2_crc32Table[256] = { + + /*-- Ugly, innit? --*/ + + 0x00000000L, 0x04c11db7L, 0x09823b6eL, 0x0d4326d9L, + 0x130476dcL, 0x17c56b6bL, 0x1a864db2L, 0x1e475005L, + 0x2608edb8L, 0x22c9f00fL, 0x2f8ad6d6L, 0x2b4bcb61L, + 0x350c9b64L, 0x31cd86d3L, 0x3c8ea00aL, 0x384fbdbdL, + 0x4c11db70L, 0x48d0c6c7L, 0x4593e01eL, 0x4152fda9L, + 0x5f15adacL, 0x5bd4b01bL, 0x569796c2L, 0x52568b75L, + 0x6a1936c8L, 0x6ed82b7fL, 0x639b0da6L, 0x675a1011L, + 0x791d4014L, 0x7ddc5da3L, 0x709f7b7aL, 0x745e66cdL, + 0x9823b6e0L, 0x9ce2ab57L, 0x91a18d8eL, 0x95609039L, + 0x8b27c03cL, 0x8fe6dd8bL, 0x82a5fb52L, 0x8664e6e5L, + 0xbe2b5b58L, 0xbaea46efL, 0xb7a96036L, 0xb3687d81L, + 0xad2f2d84L, 0xa9ee3033L, 0xa4ad16eaL, 0xa06c0b5dL, + 0xd4326d90L, 0xd0f37027L, 0xddb056feL, 0xd9714b49L, + 0xc7361b4cL, 0xc3f706fbL, 0xceb42022L, 0xca753d95L, + 0xf23a8028L, 0xf6fb9d9fL, 0xfbb8bb46L, 0xff79a6f1L, + 0xe13ef6f4L, 0xe5ffeb43L, 0xe8bccd9aL, 0xec7dd02dL, + 0x34867077L, 0x30476dc0L, 0x3d044b19L, 0x39c556aeL, + 0x278206abL, 0x23431b1cL, 0x2e003dc5L, 0x2ac12072L, + 0x128e9dcfL, 0x164f8078L, 0x1b0ca6a1L, 0x1fcdbb16L, + 0x018aeb13L, 0x054bf6a4L, 0x0808d07dL, 0x0cc9cdcaL, + 0x7897ab07L, 0x7c56b6b0L, 0x71159069L, 0x75d48ddeL, + 0x6b93dddbL, 0x6f52c06cL, 0x6211e6b5L, 0x66d0fb02L, + 0x5e9f46bfL, 0x5a5e5b08L, 0x571d7dd1L, 0x53dc6066L, + 0x4d9b3063L, 0x495a2dd4L, 0x44190b0dL, 0x40d816baL, + 0xaca5c697L, 0xa864db20L, 0xa527fdf9L, 0xa1e6e04eL, + 0xbfa1b04bL, 0xbb60adfcL, 0xb6238b25L, 0xb2e29692L, + 0x8aad2b2fL, 0x8e6c3698L, 0x832f1041L, 0x87ee0df6L, + 0x99a95df3L, 0x9d684044L, 0x902b669dL, 0x94ea7b2aL, + 0xe0b41de7L, 0xe4750050L, 0xe9362689L, 0xedf73b3eL, + 0xf3b06b3bL, 0xf771768cL, 0xfa325055L, 0xfef34de2L, + 0xc6bcf05fL, 0xc27dede8L, 0xcf3ecb31L, 0xcbffd686L, + 0xd5b88683L, 0xd1799b34L, 0xdc3abdedL, 0xd8fba05aL, + 0x690ce0eeL, 0x6dcdfd59L, 0x608edb80L, 0x644fc637L, + 0x7a089632L, 0x7ec98b85L, 0x738aad5cL, 0x774bb0ebL, + 0x4f040d56L, 0x4bc510e1L, 0x46863638L, 0x42472b8fL, + 0x5c007b8aL, 0x58c1663dL, 0x558240e4L, 0x51435d53L, + 0x251d3b9eL, 0x21dc2629L, 0x2c9f00f0L, 0x285e1d47L, + 0x36194d42L, 0x32d850f5L, 0x3f9b762cL, 0x3b5a6b9bL, + 0x0315d626L, 0x07d4cb91L, 0x0a97ed48L, 0x0e56f0ffL, + 0x1011a0faL, 0x14d0bd4dL, 0x19939b94L, 0x1d528623L, + 0xf12f560eL, 0xf5ee4bb9L, 0xf8ad6d60L, 0xfc6c70d7L, + 0xe22b20d2L, 0xe6ea3d65L, 0xeba91bbcL, 0xef68060bL, + 0xd727bbb6L, 0xd3e6a601L, 0xdea580d8L, 0xda649d6fL, + 0xc423cd6aL, 0xc0e2d0ddL, 0xcda1f604L, 0xc960ebb3L, + 0xbd3e8d7eL, 0xb9ff90c9L, 0xb4bcb610L, 0xb07daba7L, + 0xae3afba2L, 0xaafbe615L, 0xa7b8c0ccL, 0xa379dd7bL, + 0x9b3660c6L, 0x9ff77d71L, 0x92b45ba8L, 0x9675461fL, + 0x8832161aL, 0x8cf30badL, 0x81b02d74L, 0x857130c3L, + 0x5d8a9099L, 0x594b8d2eL, 0x5408abf7L, 0x50c9b640L, + 0x4e8ee645L, 0x4a4ffbf2L, 0x470cdd2bL, 0x43cdc09cL, + 0x7b827d21L, 0x7f436096L, 0x7200464fL, 0x76c15bf8L, + 0x68860bfdL, 0x6c47164aL, 0x61043093L, 0x65c52d24L, + 0x119b4be9L, 0x155a565eL, 0x18197087L, 0x1cd86d30L, + 0x029f3d35L, 0x065e2082L, 0x0b1d065bL, 0x0fdc1becL, + 0x3793a651L, 0x3352bbe6L, 0x3e119d3fL, 0x3ad08088L, + 0x2497d08dL, 0x2056cd3aL, 0x2d15ebe3L, 0x29d4f654L, + 0xc5a92679L, 0xc1683bceL, 0xcc2b1d17L, 0xc8ea00a0L, + 0xd6ad50a5L, 0xd26c4d12L, 0xdf2f6bcbL, 0xdbee767cL, + 0xe3a1cbc1L, 0xe760d676L, 0xea23f0afL, 0xeee2ed18L, + 0xf0a5bd1dL, 0xf464a0aaL, 0xf9278673L, 0xfde69bc4L, + 0x89b8fd09L, 0x8d79e0beL, 0x803ac667L, 0x84fbdbd0L, + 0x9abc8bd5L, 0x9e7d9662L, 0x933eb0bbL, 0x97ffad0cL, + 0xafb010b1L, 0xab710d06L, 0xa6322bdfL, 0xa2f33668L, + 0xbcb4666dL, 0xb8757bdaL, 0xb5365d03L, 0xb1f740b4L +}; + + +/*-------------------------------------------------------------*/ +/*--- end crctable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/decompress.c b/android/app/src/main/cpp/third_party/bzip2/decompress.c new file mode 100644 index 00000000..a1a0bac8 --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/decompress.c @@ -0,0 +1,652 @@ + +/*-------------------------------------------------------------*/ +/*--- Decompression machinery ---*/ +/*--- decompress.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------------*/ +static +void makeMaps_d ( DState* s ) +{ + Int32 i; + s->nInUse = 0; + for (i = 0; i < 256; i++) + if (s->inUse[i]) { + s->seqToUnseq[s->nInUse] = i; + s->nInUse++; + } +} + + +/*---------------------------------------------------*/ +#define RETURN(rrr) \ + { retVal = rrr; goto save_state_and_return; }; + +#define GET_BITS(lll,vvv,nnn) \ + case lll: s->state = lll; \ + while (True) { \ + if (s->bsLive >= nnn) { \ + UInt32 v; \ + v = (s->bsBuff >> \ + (s->bsLive-nnn)) & ((1 << nnn)-1); \ + s->bsLive -= nnn; \ + vvv = v; \ + break; \ + } \ + if (s->strm->avail_in == 0) RETURN(BZ_OK); \ + s->bsBuff \ + = (s->bsBuff << 8) | \ + ((UInt32) \ + (*((UChar*)(s->strm->next_in)))); \ + s->bsLive += 8; \ + s->strm->next_in++; \ + s->strm->avail_in--; \ + s->strm->total_in_lo32++; \ + if (s->strm->total_in_lo32 == 0) \ + s->strm->total_in_hi32++; \ + } + +#define GET_UCHAR(lll,uuu) \ + GET_BITS(lll,uuu,8) + +#define GET_BIT(lll,uuu) \ + GET_BITS(lll,uuu,1) + +/*---------------------------------------------------*/ +#define GET_MTF_VAL(label1,label2,lval) \ +{ \ + if (groupPos == 0) { \ + groupNo++; \ + if (groupNo >= nSelectors) \ + RETURN(BZ_DATA_ERROR); \ + groupPos = BZ_G_SIZE; \ + gSel = s->selector[groupNo]; \ + gMinlen = s->minLens[gSel]; \ + gLimit = &(s->limit[gSel][0]); \ + gPerm = &(s->perm[gSel][0]); \ + gBase = &(s->base[gSel][0]); \ + } \ + groupPos--; \ + zn = gMinlen; \ + GET_BITS(label1, zvec, zn); \ + while (1) { \ + if (zn > 20 /* the longest code */) \ + RETURN(BZ_DATA_ERROR); \ + if (zvec <= gLimit[zn]) break; \ + zn++; \ + GET_BIT(label2, zj); \ + zvec = (zvec << 1) | zj; \ + }; \ + if (zvec - gBase[zn] < 0 \ + || zvec - gBase[zn] >= BZ_MAX_ALPHA_SIZE) \ + RETURN(BZ_DATA_ERROR); \ + lval = gPerm[zvec - gBase[zn]]; \ +} + + +/*---------------------------------------------------*/ +Int32 BZ2_decompress ( DState* s ) +{ + UChar uc; + Int32 retVal; + Int32 minLen, maxLen; + bz_stream* strm = s->strm; + + /* stuff that needs to be saved/restored */ + Int32 i; + Int32 j; + Int32 t; + Int32 alphaSize; + Int32 nGroups; + Int32 nSelectors; + Int32 EOB; + Int32 groupNo; + Int32 groupPos; + Int32 nextSym; + Int32 nblockMAX; + Int32 nblock; + Int32 es; + Int32 N; + Int32 curr; + Int32 zt; + Int32 zn; + Int32 zvec; + Int32 zj; + Int32 gSel; + Int32 gMinlen; + Int32* gLimit; + Int32* gBase; + Int32* gPerm; + + if (s->state == BZ_X_MAGIC_1) { + /*initialise the save area*/ + s->save_i = 0; + s->save_j = 0; + s->save_t = 0; + s->save_alphaSize = 0; + s->save_nGroups = 0; + s->save_nSelectors = 0; + s->save_EOB = 0; + s->save_groupNo = 0; + s->save_groupPos = 0; + s->save_nextSym = 0; + s->save_nblockMAX = 0; + s->save_nblock = 0; + s->save_es = 0; + s->save_N = 0; + s->save_curr = 0; + s->save_zt = 0; + s->save_zn = 0; + s->save_zvec = 0; + s->save_zj = 0; + s->save_gSel = 0; + s->save_gMinlen = 0; + s->save_gLimit = NULL; + s->save_gBase = NULL; + s->save_gPerm = NULL; + } + + /*restore from the save area*/ + i = s->save_i; + j = s->save_j; + t = s->save_t; + alphaSize = s->save_alphaSize; + nGroups = s->save_nGroups; + nSelectors = s->save_nSelectors; + EOB = s->save_EOB; + groupNo = s->save_groupNo; + groupPos = s->save_groupPos; + nextSym = s->save_nextSym; + nblockMAX = s->save_nblockMAX; + nblock = s->save_nblock; + es = s->save_es; + N = s->save_N; + curr = s->save_curr; + zt = s->save_zt; + zn = s->save_zn; + zvec = s->save_zvec; + zj = s->save_zj; + gSel = s->save_gSel; + gMinlen = s->save_gMinlen; + gLimit = s->save_gLimit; + gBase = s->save_gBase; + gPerm = s->save_gPerm; + + retVal = BZ_OK; + + switch (s->state) { + + GET_UCHAR(BZ_X_MAGIC_1, uc); + if (uc != BZ_HDR_B) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_2, uc); + if (uc != BZ_HDR_Z) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_UCHAR(BZ_X_MAGIC_3, uc) + if (uc != BZ_HDR_h) RETURN(BZ_DATA_ERROR_MAGIC); + + GET_BITS(BZ_X_MAGIC_4, s->blockSize100k, 8) + if (s->blockSize100k < (BZ_HDR_0 + 1) || + s->blockSize100k > (BZ_HDR_0 + 9)) RETURN(BZ_DATA_ERROR_MAGIC); + s->blockSize100k -= BZ_HDR_0; + + if (s->smallDecompress) { + s->ll16 = BZALLOC( s->blockSize100k * 100000 * sizeof(UInt16) ); + s->ll4 = BZALLOC( + ((1 + s->blockSize100k * 100000) >> 1) * sizeof(UChar) + ); + if (s->ll16 == NULL || s->ll4 == NULL) RETURN(BZ_MEM_ERROR); + } else { + s->tt = BZALLOC( s->blockSize100k * 100000 * sizeof(Int32) ); + if (s->tt == NULL) RETURN(BZ_MEM_ERROR); + } + + GET_UCHAR(BZ_X_BLKHDR_1, uc); + + if (uc == 0x17) goto endhdr_2; + if (uc != 0x31) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_2, uc); + if (uc != 0x41) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_3, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_4, uc); + if (uc != 0x26) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_5, uc); + if (uc != 0x53) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_BLKHDR_6, uc); + if (uc != 0x59) RETURN(BZ_DATA_ERROR); + + s->currBlockNo++; + if (s->verbosity >= 2) + VPrintf1 ( "\n [%d: huff+mtf ", s->currBlockNo ); + + s->storedBlockCRC = 0; + GET_UCHAR(BZ_X_BCRC_1, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_2, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_3, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_BCRC_4, uc); + s->storedBlockCRC = (s->storedBlockCRC << 8) | ((UInt32)uc); + + GET_BITS(BZ_X_RANDBIT, s->blockRandomised, 1); + + s->origPtr = 0; + GET_UCHAR(BZ_X_ORIGPTR_1, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_2, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + GET_UCHAR(BZ_X_ORIGPTR_3, uc); + s->origPtr = (s->origPtr << 8) | ((Int32)uc); + + if (s->origPtr < 0) + RETURN(BZ_DATA_ERROR); + if (s->origPtr > 10 + 100000*s->blockSize100k) + RETURN(BZ_DATA_ERROR); + + /*--- Receive the mapping table ---*/ + for (i = 0; i < 16; i++) { + GET_BIT(BZ_X_MAPPING_1, uc); + if (uc == 1) + s->inUse16[i] = True; else + s->inUse16[i] = False; + } + + for (i = 0; i < 256; i++) s->inUse[i] = False; + + for (i = 0; i < 16; i++) + if (s->inUse16[i]) + for (j = 0; j < 16; j++) { + GET_BIT(BZ_X_MAPPING_2, uc); + if (uc == 1) s->inUse[i * 16 + j] = True; + } + makeMaps_d ( s ); + if (s->nInUse == 0) RETURN(BZ_DATA_ERROR); + alphaSize = s->nInUse+2; + + /*--- Now the selectors ---*/ + GET_BITS(BZ_X_SELECTOR_1, nGroups, 3); + if (nGroups < 2 || nGroups > BZ_N_GROUPS) RETURN(BZ_DATA_ERROR); + GET_BITS(BZ_X_SELECTOR_2, nSelectors, 15); + if (nSelectors < 1) RETURN(BZ_DATA_ERROR); + for (i = 0; i < nSelectors; i++) { + j = 0; + while (True) { + GET_BIT(BZ_X_SELECTOR_3, uc); + if (uc == 0) break; + j++; + if (j >= nGroups) RETURN(BZ_DATA_ERROR); + } + /* Having more than BZ_MAX_SELECTORS doesn't make much sense + since they will never be used, but some implementations might + "round up" the number of selectors, so just ignore those. */ + if (i < BZ_MAX_SELECTORS) + s->selectorMtf[i] = j; + } + if (nSelectors > BZ_MAX_SELECTORS) + nSelectors = BZ_MAX_SELECTORS; + + /*--- Undo the MTF values for the selectors. ---*/ + { + UChar pos[BZ_N_GROUPS], tmp, v; + for (v = 0; v < nGroups; v++) pos[v] = v; + + for (i = 0; i < nSelectors; i++) { + v = s->selectorMtf[i]; + tmp = pos[v]; + while (v > 0) { pos[v] = pos[v-1]; v--; } + pos[0] = tmp; + s->selector[i] = tmp; + } + } + + /*--- Now the coding tables ---*/ + for (t = 0; t < nGroups; t++) { + GET_BITS(BZ_X_CODING_1, curr, 5); + for (i = 0; i < alphaSize; i++) { + while (True) { + if (curr < 1 || curr > 20) RETURN(BZ_DATA_ERROR); + GET_BIT(BZ_X_CODING_2, uc); + if (uc == 0) break; + GET_BIT(BZ_X_CODING_3, uc); + if (uc == 0) curr++; else curr--; + } + s->len[t][i] = curr; + } + } + + /*--- Create the Huffman decoding tables ---*/ + for (t = 0; t < nGroups; t++) { + minLen = 32; + maxLen = 0; + for (i = 0; i < alphaSize; i++) { + if (s->len[t][i] > maxLen) maxLen = s->len[t][i]; + if (s->len[t][i] < minLen) minLen = s->len[t][i]; + } + BZ2_hbCreateDecodeTables ( + &(s->limit[t][0]), + &(s->base[t][0]), + &(s->perm[t][0]), + &(s->len[t][0]), + minLen, maxLen, alphaSize + ); + s->minLens[t] = minLen; + } + + /*--- Now the MTF values ---*/ + + EOB = s->nInUse+1; + nblockMAX = 100000 * s->blockSize100k; + groupNo = -1; + groupPos = 0; + + for (i = 0; i <= 255; i++) s->unzftab[i] = 0; + + /*-- MTF init --*/ + { + Int32 ii, jj, kk; + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE - 1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = (UChar)(ii * MTFL_SIZE + jj); + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + /*-- end MTF init --*/ + + nblock = 0; + GET_MTF_VAL(BZ_X_MTF_1, BZ_X_MTF_2, nextSym); + + while (True) { + + if (nextSym == EOB) break; + + if (nextSym == BZ_RUNA || nextSym == BZ_RUNB) { + + es = -1; + N = 1; + do { + /* Check that N doesn't get too big, so that es doesn't + go negative. The maximum value that can be + RUNA/RUNB encoded is equal to the block size (post + the initial RLE), viz, 900k, so bounding N at 2 + million should guard against overflow without + rejecting any legitimate inputs. */ + if (N >= 2*1024*1024) RETURN(BZ_DATA_ERROR); + if (nextSym == BZ_RUNA) es = es + (0+1) * N; else + if (nextSym == BZ_RUNB) es = es + (1+1) * N; + N = N * 2; + GET_MTF_VAL(BZ_X_MTF_3, BZ_X_MTF_4, nextSym); + } + while (nextSym == BZ_RUNA || nextSym == BZ_RUNB); + + es++; + uc = s->seqToUnseq[ s->mtfa[s->mtfbase[0]] ]; + s->unzftab[uc] += es; + + if (s->smallDecompress) + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->ll16[nblock] = (UInt16)uc; + nblock++; + es--; + } + else + while (es > 0) { + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + s->tt[nblock] = (UInt32)uc; + nblock++; + es--; + }; + + continue; + + } else { + + if (nblock >= nblockMAX) RETURN(BZ_DATA_ERROR); + + /*-- uc = MTF ( nextSym-1 ) --*/ + { + Int32 ii, jj, kk, pp, lno, off; + UInt32 nn; + nn = (UInt32)(nextSym - 1); + + if (nn < MTFL_SIZE) { + /* avoid general-case expense */ + pp = s->mtfbase[0]; + uc = s->mtfa[pp+nn]; + while (nn > 3) { + Int32 z = pp+nn; + s->mtfa[(z) ] = s->mtfa[(z)-1]; + s->mtfa[(z)-1] = s->mtfa[(z)-2]; + s->mtfa[(z)-2] = s->mtfa[(z)-3]; + s->mtfa[(z)-3] = s->mtfa[(z)-4]; + nn -= 4; + } + while (nn > 0) { + s->mtfa[(pp+nn)] = s->mtfa[(pp+nn)-1]; nn--; + }; + s->mtfa[pp] = uc; + } else { + /* general case */ + lno = nn / MTFL_SIZE; + off = nn % MTFL_SIZE; + pp = s->mtfbase[lno] + off; + uc = s->mtfa[pp]; + while (pp > s->mtfbase[lno]) { + s->mtfa[pp] = s->mtfa[pp-1]; pp--; + }; + s->mtfbase[lno]++; + while (lno > 0) { + s->mtfbase[lno]--; + s->mtfa[s->mtfbase[lno]] + = s->mtfa[s->mtfbase[lno-1] + MTFL_SIZE - 1]; + lno--; + } + s->mtfbase[0]--; + s->mtfa[s->mtfbase[0]] = uc; + if (s->mtfbase[0] == 0) { + kk = MTFA_SIZE-1; + for (ii = 256 / MTFL_SIZE-1; ii >= 0; ii--) { + for (jj = MTFL_SIZE-1; jj >= 0; jj--) { + s->mtfa[kk] = s->mtfa[s->mtfbase[ii] + jj]; + kk--; + } + s->mtfbase[ii] = kk + 1; + } + } + } + } + /*-- end uc = MTF ( nextSym-1 ) --*/ + + s->unzftab[s->seqToUnseq[uc]]++; + if (s->smallDecompress) + s->ll16[nblock] = (UInt16)(s->seqToUnseq[uc]); else + s->tt[nblock] = (UInt32)(s->seqToUnseq[uc]); + nblock++; + + GET_MTF_VAL(BZ_X_MTF_5, BZ_X_MTF_6, nextSym); + continue; + } + } + + /* Now we know what nblock is, we can do a better sanity + check on s->origPtr. + */ + if (s->origPtr < 0 || s->origPtr >= nblock) + RETURN(BZ_DATA_ERROR); + + /*-- Set up cftab to facilitate generation of T^(-1) --*/ + /* Check: unzftab entries in range. */ + for (i = 0; i <= 255; i++) { + if (s->unzftab[i] < 0 || s->unzftab[i] > nblock) + RETURN(BZ_DATA_ERROR); + } + /* Actually generate cftab. */ + s->cftab[0] = 0; + for (i = 1; i <= 256; i++) s->cftab[i] = s->unzftab[i-1]; + for (i = 1; i <= 256; i++) s->cftab[i] += s->cftab[i-1]; + /* Check: cftab entries in range. */ + for (i = 0; i <= 256; i++) { + if (s->cftab[i] < 0 || s->cftab[i] > nblock) { + /* s->cftab[i] can legitimately be == nblock */ + RETURN(BZ_DATA_ERROR); + } + } + /* Check: cftab entries non-descending. */ + for (i = 1; i <= 256; i++) { + if (s->cftab[i-1] > s->cftab[i]) { + RETURN(BZ_DATA_ERROR); + } + } + + s->state_out_len = 0; + s->state_out_ch = 0; + BZ_INITIALISE_CRC ( s->calculatedBlockCRC ); + s->state = BZ_X_OUTPUT; + if (s->verbosity >= 2) VPrintf0 ( "rt+rld" ); + + if (s->smallDecompress) { + + /*-- Make a copy of cftab, used in generation of T --*/ + for (i = 0; i <= 256; i++) s->cftabCopy[i] = s->cftab[i]; + + /*-- compute the T vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->ll16[i]); + SET_LL(i, s->cftabCopy[uc]); + s->cftabCopy[uc]++; + } + + /*-- Compute T^(-1) by pointer reversal on T --*/ + i = s->origPtr; + j = GET_LL(i); + do { + Int32 tmp = GET_LL(j); + SET_LL(j, i); + i = j; + j = tmp; + } + while (i != s->origPtr); + + s->tPos = s->origPtr; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_SMALL(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_SMALL(s->k0); s->nblock_used++; + } + + } else { + + /*-- compute the T^(-1) vector --*/ + for (i = 0; i < nblock; i++) { + uc = (UChar)(s->tt[i] & 0xff); + s->tt[s->cftab[uc]] |= (i << 8); + s->cftab[uc]++; + } + + s->tPos = s->tt[s->origPtr] >> 8; + s->nblock_used = 0; + if (s->blockRandomised) { + BZ_RAND_INIT_MASK; + BZ_GET_FAST(s->k0); s->nblock_used++; + BZ_RAND_UPD_MASK; s->k0 ^= BZ_RAND_MASK; + } else { + BZ_GET_FAST(s->k0); s->nblock_used++; + } + + } + + RETURN(BZ_OK); + + + + endhdr_2: + + GET_UCHAR(BZ_X_ENDHDR_2, uc); + if (uc != 0x72) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_3, uc); + if (uc != 0x45) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_4, uc); + if (uc != 0x38) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_5, uc); + if (uc != 0x50) RETURN(BZ_DATA_ERROR); + GET_UCHAR(BZ_X_ENDHDR_6, uc); + if (uc != 0x90) RETURN(BZ_DATA_ERROR); + + s->storedCombinedCRC = 0; + GET_UCHAR(BZ_X_CCRC_1, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_2, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_3, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + GET_UCHAR(BZ_X_CCRC_4, uc); + s->storedCombinedCRC = (s->storedCombinedCRC << 8) | ((UInt32)uc); + + s->state = BZ_X_IDLE; + RETURN(BZ_STREAM_END); + + default: AssertH ( False, 4001 ); + } + + AssertH ( False, 4002 ); + + save_state_and_return: + + s->save_i = i; + s->save_j = j; + s->save_t = t; + s->save_alphaSize = alphaSize; + s->save_nGroups = nGroups; + s->save_nSelectors = nSelectors; + s->save_EOB = EOB; + s->save_groupNo = groupNo; + s->save_groupPos = groupPos; + s->save_nextSym = nextSym; + s->save_nblockMAX = nblockMAX; + s->save_nblock = nblock; + s->save_es = es; + s->save_N = N; + s->save_curr = curr; + s->save_zt = zt; + s->save_zn = zn; + s->save_zvec = zvec; + s->save_zj = zj; + s->save_gSel = gSel; + s->save_gMinlen = gMinlen; + s->save_gLimit = gLimit; + s->save_gBase = gBase; + s->save_gPerm = gPerm; + + return retVal; +} + + +/*-------------------------------------------------------------*/ +/*--- end decompress.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/huffman.c b/android/app/src/main/cpp/third_party/bzip2/huffman.c new file mode 100644 index 00000000..43a1899e --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/huffman.c @@ -0,0 +1,205 @@ + +/*-------------------------------------------------------------*/ +/*--- Huffman coding low-level stuff ---*/ +/*--- huffman.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + +/*---------------------------------------------------*/ +#define WEIGHTOF(zz0) ((zz0) & 0xffffff00) +#define DEPTHOF(zz1) ((zz1) & 0x000000ff) +#define MYMAX(zz2,zz3) ((zz2) > (zz3) ? (zz2) : (zz3)) + +#define ADDWEIGHTS(zw1,zw2) \ + (WEIGHTOF(zw1)+WEIGHTOF(zw2)) | \ + (1 + MYMAX(DEPTHOF(zw1),DEPTHOF(zw2))) + +#define UPHEAP(z) \ +{ \ + Int32 zz, tmp; \ + zz = z; tmp = heap[zz]; \ + while (weight[tmp] < weight[heap[zz >> 1]]) { \ + heap[zz] = heap[zz >> 1]; \ + zz >>= 1; \ + } \ + heap[zz] = tmp; \ +} + +#define DOWNHEAP(z) \ +{ \ + Int32 zz, yy, tmp; \ + zz = z; tmp = heap[zz]; \ + while (True) { \ + yy = zz << 1; \ + if (yy > nHeap) break; \ + if (yy < nHeap && \ + weight[heap[yy+1]] < weight[heap[yy]]) \ + yy++; \ + if (weight[tmp] < weight[heap[yy]]) break; \ + heap[zz] = heap[yy]; \ + zz = yy; \ + } \ + heap[zz] = tmp; \ +} + + +/*---------------------------------------------------*/ +void BZ2_hbMakeCodeLengths ( UChar *len, + Int32 *freq, + Int32 alphaSize, + Int32 maxLen ) +{ + /*-- + Nodes and heap entries run from 1. Entry 0 + for both the heap and nodes is a sentinel. + --*/ + Int32 nNodes, nHeap, n1, n2, i, j, k; + Bool tooLong; + + Int32 heap [ BZ_MAX_ALPHA_SIZE + 2 ]; + Int32 weight [ BZ_MAX_ALPHA_SIZE * 2 ]; + Int32 parent [ BZ_MAX_ALPHA_SIZE * 2 ]; + + for (i = 0; i < alphaSize; i++) + weight[i+1] = (freq[i] == 0 ? 1 : freq[i]) << 8; + + while (True) { + + nNodes = alphaSize; + nHeap = 0; + + heap[0] = 0; + weight[0] = 0; + parent[0] = -2; + + for (i = 1; i <= alphaSize; i++) { + parent[i] = -1; + nHeap++; + heap[nHeap] = i; + UPHEAP(nHeap); + } + + AssertH( nHeap < (BZ_MAX_ALPHA_SIZE+2), 2001 ); + + while (nHeap > 1) { + n1 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + n2 = heap[1]; heap[1] = heap[nHeap]; nHeap--; DOWNHEAP(1); + nNodes++; + parent[n1] = parent[n2] = nNodes; + weight[nNodes] = ADDWEIGHTS(weight[n1], weight[n2]); + parent[nNodes] = -1; + nHeap++; + heap[nHeap] = nNodes; + UPHEAP(nHeap); + } + + AssertH( nNodes < (BZ_MAX_ALPHA_SIZE * 2), 2002 ); + + tooLong = False; + for (i = 1; i <= alphaSize; i++) { + j = 0; + k = i; + while (parent[k] >= 0) { k = parent[k]; j++; } + len[i-1] = j; + if (j > maxLen) tooLong = True; + } + + if (! tooLong) break; + + /* 17 Oct 04: keep-going condition for the following loop used + to be 'i < alphaSize', which missed the last element, + theoretically leading to the possibility of the compressor + looping. However, this count-scaling step is only needed if + one of the generated Huffman code words is longer than + maxLen, which up to and including version 1.0.2 was 20 bits, + which is extremely unlikely. In version 1.0.3 maxLen was + changed to 17 bits, which has minimal effect on compression + ratio, but does mean this scaling step is used from time to + time, enough to verify that it works. + + This means that bzip2-1.0.3 and later will only produce + Huffman codes with a maximum length of 17 bits. However, in + order to preserve backwards compatibility with bitstreams + produced by versions pre-1.0.3, the decompressor must still + handle lengths of up to 20. */ + + for (i = 1; i <= alphaSize; i++) { + j = weight[i] >> 8; + j = 1 + (j / 2); + weight[i] = j << 8; + } + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbAssignCodes ( Int32 *code, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 n, vec, i; + + vec = 0; + for (n = minLen; n <= maxLen; n++) { + for (i = 0; i < alphaSize; i++) + if (length[i] == n) { code[i] = vec; vec++; }; + vec <<= 1; + } +} + + +/*---------------------------------------------------*/ +void BZ2_hbCreateDecodeTables ( Int32 *limit, + Int32 *base, + Int32 *perm, + UChar *length, + Int32 minLen, + Int32 maxLen, + Int32 alphaSize ) +{ + Int32 pp, i, j, vec; + + pp = 0; + for (i = minLen; i <= maxLen; i++) + for (j = 0; j < alphaSize; j++) + if (length[j] == i) { perm[pp] = j; pp++; }; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) base[i] = 0; + for (i = 0; i < alphaSize; i++) base[length[i]+1]++; + + for (i = 1; i < BZ_MAX_CODE_LEN; i++) base[i] += base[i-1]; + + for (i = 0; i < BZ_MAX_CODE_LEN; i++) limit[i] = 0; + vec = 0; + + for (i = minLen; i <= maxLen; i++) { + vec += (base[i+1] - base[i]); + limit[i] = vec-1; + vec <<= 1; + } + for (i = minLen + 1; i <= maxLen; i++) + base[i] = ((limit[i-1] + 1) << 1) - base[i]; +} + + +/*-------------------------------------------------------------*/ +/*--- end huffman.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/bzip2/randtable.c b/android/app/src/main/cpp/third_party/bzip2/randtable.c new file mode 100644 index 00000000..bdc6d4a4 --- /dev/null +++ b/android/app/src/main/cpp/third_party/bzip2/randtable.c @@ -0,0 +1,84 @@ + +/*-------------------------------------------------------------*/ +/*--- Table for randomising repetitive blocks ---*/ +/*--- randtable.c ---*/ +/*-------------------------------------------------------------*/ + +/* ------------------------------------------------------------------ + This file is part of bzip2/libbzip2, a program and library for + lossless, block-sorting data compression. + + bzip2/libbzip2 version 1.0.8 of 13 July 2019 + Copyright (C) 1996-2019 Julian Seward + + Please read the WARNING, DISCLAIMER and PATENTS sections in the + README file. + + This program is released under the terms of the license contained + in the file LICENSE. + ------------------------------------------------------------------ */ + + +#include "bzlib_private.h" + + +/*---------------------------------------------*/ +Int32 BZ2_rNums[512] = { + 619, 720, 127, 481, 931, 816, 813, 233, 566, 247, + 985, 724, 205, 454, 863, 491, 741, 242, 949, 214, + 733, 859, 335, 708, 621, 574, 73, 654, 730, 472, + 419, 436, 278, 496, 867, 210, 399, 680, 480, 51, + 878, 465, 811, 169, 869, 675, 611, 697, 867, 561, + 862, 687, 507, 283, 482, 129, 807, 591, 733, 623, + 150, 238, 59, 379, 684, 877, 625, 169, 643, 105, + 170, 607, 520, 932, 727, 476, 693, 425, 174, 647, + 73, 122, 335, 530, 442, 853, 695, 249, 445, 515, + 909, 545, 703, 919, 874, 474, 882, 500, 594, 612, + 641, 801, 220, 162, 819, 984, 589, 513, 495, 799, + 161, 604, 958, 533, 221, 400, 386, 867, 600, 782, + 382, 596, 414, 171, 516, 375, 682, 485, 911, 276, + 98, 553, 163, 354, 666, 933, 424, 341, 533, 870, + 227, 730, 475, 186, 263, 647, 537, 686, 600, 224, + 469, 68, 770, 919, 190, 373, 294, 822, 808, 206, + 184, 943, 795, 384, 383, 461, 404, 758, 839, 887, + 715, 67, 618, 276, 204, 918, 873, 777, 604, 560, + 951, 160, 578, 722, 79, 804, 96, 409, 713, 940, + 652, 934, 970, 447, 318, 353, 859, 672, 112, 785, + 645, 863, 803, 350, 139, 93, 354, 99, 820, 908, + 609, 772, 154, 274, 580, 184, 79, 626, 630, 742, + 653, 282, 762, 623, 680, 81, 927, 626, 789, 125, + 411, 521, 938, 300, 821, 78, 343, 175, 128, 250, + 170, 774, 972, 275, 999, 639, 495, 78, 352, 126, + 857, 956, 358, 619, 580, 124, 737, 594, 701, 612, + 669, 112, 134, 694, 363, 992, 809, 743, 168, 974, + 944, 375, 748, 52, 600, 747, 642, 182, 862, 81, + 344, 805, 988, 739, 511, 655, 814, 334, 249, 515, + 897, 955, 664, 981, 649, 113, 974, 459, 893, 228, + 433, 837, 553, 268, 926, 240, 102, 654, 459, 51, + 686, 754, 806, 760, 493, 403, 415, 394, 687, 700, + 946, 670, 656, 610, 738, 392, 760, 799, 887, 653, + 978, 321, 576, 617, 626, 502, 894, 679, 243, 440, + 680, 879, 194, 572, 640, 724, 926, 56, 204, 700, + 707, 151, 457, 449, 797, 195, 791, 558, 945, 679, + 297, 59, 87, 824, 713, 663, 412, 693, 342, 606, + 134, 108, 571, 364, 631, 212, 174, 643, 304, 329, + 343, 97, 430, 751, 497, 314, 983, 374, 822, 928, + 140, 206, 73, 263, 980, 736, 876, 478, 430, 305, + 170, 514, 364, 692, 829, 82, 855, 953, 676, 246, + 369, 970, 294, 750, 807, 827, 150, 790, 288, 923, + 804, 378, 215, 828, 592, 281, 565, 555, 710, 82, + 896, 831, 547, 261, 524, 462, 293, 465, 502, 56, + 661, 821, 976, 991, 658, 869, 905, 758, 745, 193, + 768, 550, 608, 933, 378, 286, 215, 979, 792, 961, + 61, 688, 793, 644, 986, 403, 106, 366, 905, 644, + 372, 567, 466, 434, 645, 210, 389, 550, 919, 135, + 780, 773, 635, 389, 707, 100, 626, 958, 165, 504, + 920, 176, 193, 713, 857, 265, 203, 50, 668, 108, + 645, 990, 626, 197, 510, 357, 358, 850, 858, 364, + 936, 638 +}; + + +/*-------------------------------------------------------------*/ +/*--- end randtable.c ---*/ +/*-------------------------------------------------------------*/ diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/LICENSE b/android/app/src/main/cpp/third_party/hdiffpatch/LICENSE new file mode 100644 index 00000000..3c54019b --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/LICENSE @@ -0,0 +1,48 @@ +MIT License + +HDiffPatch +Copyright (c) 2012-2025 housisong + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +---------------------------------------------------------------------------------- + +libdivsufsort +Copyright (c) 2003-2008 Yuta Mori All Rights Reserved. + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the "Software"), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.c b/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.c new file mode 100644 index 00000000..62f9c4f8 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.c @@ -0,0 +1,263 @@ +// bspatch_wrapper.c +// HDiffPatch +/* + The MIT License (MIT) + Copyright (c) 2021 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#include "bspatch_wrapper.h" +#include "../libHDiffPatch/HPatch/patch_types.h" +#include "../libHDiffPatch/HPatch/patch_private.h" +#include +#define _hpatch_FALSE hpatch_FALSE +//hpatch_uint __debug_check_false_x=0; //for debug +//#define _hpatch_FALSE (1/__debug_check_false_x) + +#ifndef _IS_RUN_MEM_SAFE_CHECK +# define _IS_RUN_MEM_SAFE_CHECK 1 +#endif + +#if (_IS_RUN_MEM_SAFE_CHECK) +// __RUN_MEM_SAFE_CHECK : enables out-of-bounds memory access checking to protect against data that may be accidentally or intentionally corrupted. +# define __RUN_MEM_SAFE_CHECK +#endif + +static const char* kBsDiffVersionType = "BSDIFF40"; +#define kBsDiffVersionTypeLen 8 // ==strlen(kBsDiffVersionType); +#define kBsDiffHeadLen (kBsDiffVersionTypeLen+3*8) + +static const char* kEsBsDiffVersionType = "ENDSLEY/BSDIFF43"; +#define kEsBsDiffVersionTypeLen 16 // ==strlen(kEsBsDiffVersionType); +#define kEsBsDiffHeadLen (kEsBsDiffVersionTypeLen+8) + + +static hpatch_inline hpatch_uint32_t _readUInt32(const unsigned char* buf){ + return buf[0] | (((hpatch_uint32_t)buf[1])<<8) | + (((hpatch_uint32_t)buf[2])<<16) | (((hpatch_uint32_t)buf[3])<<24) ; +} +static hpatch_inline hpatch_uint64_t readUInt64(const unsigned char* buf){ + return _readUInt32(buf) | (((hpatch_uint64_t)_readUInt32(buf+4))<<32); +} + +#define _clip_readUInt64(_clip,_result) { \ + const unsigned char* buf=_TStreamCacheClip_readData(_clip,8); \ + if (buf!=0) *(_result)=readUInt64(buf); \ + else return _hpatch_FALSE; \ +} + +hpatch_BOOL getBsDiffInfo(hpatch_BsDiffInfo* out_diffinfo,const hpatch_TStreamInput* diffStream){ + unsigned char _buf[kBsDiffHeadLen]; + unsigned char* buf=&_buf[0]; + if (diffStream->streamSizeread(diffStream,0,buf,buf+kBsDiffHeadLen)) // must bz2 compressed size>=8 + return _hpatch_FALSE; + if (0==memcmp(buf,kBsDiffVersionType,kBsDiffVersionTypeLen)){ + out_diffinfo->isEndsleyBsdiff=hpatch_FALSE; + out_diffinfo->headSize=kBsDiffHeadLen; + }else if (0==memcmp(buf,kEsBsDiffVersionType,kEsBsDiffVersionTypeLen)){ + out_diffinfo->isEndsleyBsdiff=hpatch_TRUE; + out_diffinfo->headSize=kEsBsDiffHeadLen; + }else{ + return _hpatch_FALSE; + } + if (out_diffinfo->isEndsleyBsdiff){ + buf+=kEsBsDiffVersionTypeLen; + out_diffinfo->ctrlDataSize=0; + out_diffinfo->subDataSize =0; + out_diffinfo->newDataSize =readUInt64(buf); + }else{ + buf+=kBsDiffVersionTypeLen; + out_diffinfo->ctrlDataSize=readUInt64(buf); + out_diffinfo->subDataSize =readUInt64(buf+8); + out_diffinfo->newDataSize =readUInt64(buf+8*2); + } + return (out_diffinfo->ctrlDataSizestreamSize)&& + (out_diffinfo->subDataSizestreamSize)&& + (out_diffinfo->headSize+out_diffinfo->ctrlDataSize+out_diffinfo->subDataSize<=diffStream->streamSize); +} + +hpatch_BOOL getBsDiffInfo_mem(hpatch_BsDiffInfo* out_diffinfo,const unsigned char* diffData,const unsigned char* diffData_end){ + hpatch_TStreamInput diffStream; + mem_as_hStreamInput(&diffStream,diffData,diffData_end); + return getBsDiffInfo(out_diffinfo,&diffStream); +} + +hpatch_BOOL getIsBsDiff(const hpatch_TStreamInput* diffData,hpatch_BOOL* out_isSingleCompressedDiff){ + hpatch_BsDiffInfo diffinfo; + hpatch_BOOL result=getBsDiffInfo(&diffinfo,diffData); + if (result&&out_isSingleCompressedDiff) *out_isSingleCompressedDiff=diffinfo.isEndsleyBsdiff; + return result; +} + +hpatch_BOOL getIsBsDiff_mem(const unsigned char* diffData,const unsigned char* diffData_end,hpatch_BOOL* out_isSingleCompressedDiff){ + hpatch_TStreamInput diffStream; + mem_as_hStreamInput(&diffStream,diffData,diffData_end); + return getIsBsDiff(&diffStream,out_isSingleCompressedDiff); +} + +static hpatch_BOOL _patch_add_old_with_sub(_TOutStreamCache* outCache,TStreamCacheClip* subClip, + const hpatch_TStreamInput* old,hpatch_uint64_t oldPos, + hpatch_uint64_t addLength,unsigned char* aCache,hpatch_size_t aCacheSize){ + while (addLength>0){ + hpatch_size_t decodeStep=aCacheSize; + if (decodeStep>addLength) + decodeStep=(hpatch_size_t)addLength; + if (!old->read(old,oldPos,aCache,aCache+decodeStep)) return _hpatch_FALSE; + if (!_TStreamCacheClip_addDataTo(subClip,aCache,decodeStep)) return _hpatch_FALSE; + if (!_TOutStreamCache_write(outCache,aCache,decodeStep)) return _hpatch_FALSE; + oldPos+=decodeStep; + addLength-=decodeStep; + } + return hpatch_TRUE; +} + +hpatch_BOOL bspatchByClip(_TOutStreamCache* outCache,const hpatch_TStreamInput* oldData, + TStreamCacheClip* ctrlClip,TStreamCacheClip* subClip,TStreamCacheClip* newDataDiffClip, + unsigned char* temp_cache,hpatch_size_t cache_size){ + const hpatch_uint64_t newDataSize=_TOutStreamCache_leaveSize(outCache); +#ifdef __RUN_MEM_SAFE_CHECK + const hpatch_uint64_t oldDataSize=oldData->streamSize; +#endif + hpatch_uint64_t newPosBack=0; + hpatch_uint64_t oldPosBack=0; + assert(cache_size>=8); + + while (newPosBack(hpatch_uint64_t)(newDataSize-newPosBack)) return _hpatch_FALSE; + if (oldPosBack>oldDataSize) return _hpatch_FALSE; + if (coverLen>(hpatch_uint64_t)(oldDataSize-oldPosBack)) return _hpatch_FALSE; +#endif + if (!_patch_add_old_with_sub(outCache,subClip,oldData,oldPosBack,coverLen, + temp_cache,cache_size)) return _hpatch_FALSE; + oldPosBack+=((skipOldLen>>63)==0)?(coverLen+skipOldLen):(coverLen-(skipOldLen&((((hpatch_uint64_t)1)<<63)-1))); + newPosBack+=coverLen; + if (skipNewLen){ +#ifdef __RUN_MEM_SAFE_CHECK + if (skipNewLen>(hpatch_uint64_t)(newDataSize-newPosBack)) return _hpatch_FALSE; +#endif + if (!_TOutStreamCache_copyFromClip(outCache,newDataDiffClip,skipNewLen)) return _hpatch_FALSE; + newPosBack+=skipNewLen; + } + } + + if (!_TOutStreamCache_flush(outCache)) + return _hpatch_FALSE; + if (_TOutStreamCache_isFinish(outCache) + && (newPosBack==newDataSize) ) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + + +#define _kCacheBsDecCount 5 + +static const hpatch_uint64_t _kUnknowMaxSize=~(hpatch_uint64_t)0; +#define _clear_return(exitValue) { result=exitValue; goto clear; } + +#define _getStreamClip(_diffClip,_decompresser,_dataSize) \ + getStreamClip(_diffClip,_decompresser, \ + decompressPlugin?_kUnknowMaxSize:(_dataSize),decompressPlugin?(_dataSize):0, \ + compressedDiff,&diffPos0,decompressPlugin,temp_cache,cacheSize) + +hpatch_BOOL bspatch_with_cache(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin, + unsigned char* temp_cache,unsigned char* temp_cache_end){ + hpatch_BsDiffInfo diffInfo; + TStreamCacheClip ctrlClip; + TStreamCacheClip subClip; + TStreamCacheClip* _ctrlClip=&ctrlClip; + TStreamCacheClip* _subClip=&subClip; + TStreamCacheClip newDataDiffClip; + _TDecompressInputStream decompressers[3]; + hpatch_size_t i; + hpatch_BOOL result=hpatch_TRUE; + hpatch_StreamPos_t diffPos0; + hpatch_size_t cacheSize; + hpatch_BOOL isReadError=hpatch_FALSE; + //assert(decompressPlugin!=0); + assert(out_newData!=0); + assert(out_newData->write!=0); + assert(oldData!=0); + assert(oldData->read!=0); + assert(compressedDiff!=0); + assert(compressedDiff->read!=0); + if (!getBsDiffInfo(&diffInfo,compressedDiff)) + return _hpatch_FALSE; + if (out_newData->streamSize!=diffInfo.newDataSize) + return _hpatch_FALSE; + for (i=0;istreamSize-diffPos0)) _clear_return(_hpatch_FALSE); + temp_cache+=cacheSize; + _ctrlClip=&newDataDiffClip; + _subClip=&newDataDiffClip; + }else{ + if (!_getStreamClip(&ctrlClip,&decompressers[0],diffInfo.ctrlDataSize)) _clear_return(_hpatch_FALSE); + temp_cache+=cacheSize; + if (!_getStreamClip(&subClip,&decompressers[1],diffInfo.subDataSize)) _clear_return(_hpatch_FALSE); + temp_cache+=cacheSize; + if (!_getStreamClip(&newDataDiffClip,&decompressers[2],compressedDiff->streamSize-diffPos0)) _clear_return(_hpatch_FALSE); + temp_cache+=cacheSize; + } + assert(diffPos0==compressedDiff->streamSize); + + { + _TOutStreamCache outCache; + _TOutStreamCache_init(&outCache,out_newData,temp_cache,cacheSize); + temp_cache+=cacheSize; + result=bspatchByClip(&outCache,oldData,_ctrlClip,_subClip,&newDataDiffClip, + temp_cache,cacheSize); + } + +clear: + for (i=0;iclose(decompressPlugin,decompressers[i].decompressHandle)) + result=_hpatch_FALSE; + decompressers[i].decompressHandle=0; + } + } + return result; +} \ No newline at end of file diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.h b/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.h new file mode 100644 index 00000000..9af517ad --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/bsdiff_wrapper/bspatch_wrapper.h @@ -0,0 +1,58 @@ +// bspatch_wrapper.h +// HDiffPatch +/* + The MIT License (MIT) + Copyright (c) 2021 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef hpatch_bspatch_wrapper_h +#define hpatch_bspatch_wrapper_h +#include "../libHDiffPatch/HPatch/patch_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct hpatch_BsDiffInfo{ + hpatch_StreamPos_t headSize; + hpatch_StreamPos_t ctrlDataSize; + hpatch_StreamPos_t subDataSize; + hpatch_StreamPos_t newDataSize; + hpatch_BOOL isEndsleyBsdiff; +} hpatch_BsDiffInfo; + +hpatch_BOOL getIsBsDiff(const hpatch_TStreamInput* diffData,hpatch_BOOL* out_isSingleCompressedDiff); +hpatch_BOOL getIsBsDiff_mem(const unsigned char* diffData,const unsigned char* diffData_end,hpatch_BOOL* out_isSingleCompressedDiff); + +hpatch_BOOL getBsDiffInfo(hpatch_BsDiffInfo* out_diffinfo,const hpatch_TStreamInput* diffStream); +hpatch_BOOL getBsDiffInfo_mem(hpatch_BsDiffInfo* out_diffinfo,const unsigned char* diffData,const unsigned char* diffData_end); + +hpatch_BOOL bspatch_with_cache(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* compressedDiff, //create by bsdiff4 or hdiffz -BSD + hpatch_TDecompress* decompressPlugin, // ==&_bz2DecompressPlugin_unsz in "decompress_plugin_demo.h" + unsigned char* temp_cache,unsigned char* temp_cache_end); + +#ifdef __cplusplus +} +#endif +#endif \ No newline at end of file diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/decompress_plugin_demo.h b/android/app/src/main/cpp/third_party/hdiffpatch/decompress_plugin_demo.h new file mode 100644 index 00000000..b45f2146 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/decompress_plugin_demo.h @@ -0,0 +1,1916 @@ +//decompress_plugin_demo.h +// decompress plugin demo for HDiffz\HPatchz +/* + The MIT License (MIT) + Copyright (c) 2012-2017 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HPatch_decompress_plugin_demo_h +#define HPatch_decompress_plugin_demo_h +//decompress plugin demo: +// zlibDecompressPlugin; // support all deflate encoding by zlib +// ldefDecompressPlugin; // optimized deompress speed for deflate encoding +// bz2DecompressPlugin; +// lzmaDecompressPlugin; +// lzma2DecompressPlugin; +// lzma2mtDecompressPlugin; +// lz4DecompressPlugin; +// zstdDecompressPlugin; +// brotliDecompressPlugin; +// lzhamDecompressPlugin; +// tuzDecompressPlugin; + +// _bz2DecompressPlugin_unsz : support for bspatch_with_cache(), diffData created by bsdiff or "hdiffz -BSD ..." +// _lzma2DecompressPlugin_unsz : only for yourself code, support for bspatch_with_cache(), diffData compressed by lzma2 (lzma2CompressPlugin) +// _7zXZDecompressPlugin : support for vcpatch_with_cache(), diffData created by "xdelta3 -S lzma ..." +// _7zXZDecompressPlugin_a : support for vcpatch_with_cache(), diffData created by "hdiffz -VCD-compressLevel ..." +#include //malloc free +#include //fprintf +#include "libHDiffPatch/HPatch/patch_types.h" + +#ifndef kDecompressBufSize +# define kDecompressBufSize (1024*32) +#endif +#ifndef _IsNeedIncludeDefaultCompressHead +# define _IsNeedIncludeDefaultCompressHead 1 +#endif + +#define _dec_memErr() _hpatch_update_decError(decompressPlugin,hpatch_dec_mem_error) +#define _dec_memErr_rt() do { _dec_memErr(); return 0; } while(0) +#define _dec_openErr_rt() do { _hpatch_update_decError(decompressPlugin,hpatch_dec_open_error); return 0; } while(0) +#define _dec_close_check(value) { if (!(value)) { LOG_ERR("check "#value " ERROR!\n"); \ + result=hpatch_FALSE; _hpatch_update_decError(decompressPlugin,hpatch_dec_close_error); } } + +#define _dec_onDecErr_rt() do { if (!(self)->decError) (self)->decError=hpatch_dec_error; return 0; } while(0) +#define _dec_onDecErr_up() do { if ((self)->decError) _hpatch_update_decError(decompressPlugin,(self)->decError); } while(0) + +static void* _dec_malloc(hpatch_size_t size) { + void* result=malloc(size); + if (!result) LOG_ERRNO(errno); + return result; +} +#define __dec_Alloc_fun(_type_TDecompress,p,size) { \ + void* result=_dec_malloc(size); \ + if (!result) \ + ((_type_TDecompress*)p)->decError=hpatch_dec_mem_error; \ + return result; } + +static void __dec_free(void* _, void* address){ + if (address) free(address); } + +#ifdef _CompressPlugin_zlib +#if (_IsNeedIncludeDefaultCompressHead) +# include "zlib.h" // http://zlib.net/ https://github.com/madler/zlib +#endif + typedef struct _zlib_TDecompress{ + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + const struct hpatch_TStreamInput* codeStream; + + unsigned char* dec_buf; + size_t dec_buf_size; + z_stream d_stream; + signed char windowBits; + hpatch_dec_error_t decError; + } _zlib_TDecompress; + static void * __zlib_dec_Alloc(void* p,uInt items,uInt size) + __dec_Alloc_fun(_zlib_TDecompress,p,((items)*(size_t)(size))) + static hpatch_BOOL _zlib_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"zlib"))||(0==strcmp(compressType,"pzlib")); + } + + static _zlib_TDecompress* _zlib_decompress_open_at(hpatch_TDecompress* decompressPlugin, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end, + int isSavedWindowBits, + _zlib_TDecompress* self,size_t _self_and_buf_size){ + int ret; + signed char kWindowBits=-MAX_WBITS; + assert(_self_and_buf_size>sizeof(_zlib_TDecompress)); + if (isSavedWindowBits){//load kWindowBits + if (code_end-code_begin<1) _dec_openErr_rt(); + if (!codeStream->read(codeStream,code_begin,(unsigned char*)&kWindowBits, + (unsigned char*)&kWindowBits+1)) return 0; + ++code_begin; + } + + memset(self,0,sizeof(_zlib_TDecompress)); + self->dec_buf=((unsigned char*)self)+sizeof(_zlib_TDecompress); + self->dec_buf_size=_self_and_buf_size-sizeof(_zlib_TDecompress); + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->windowBits=kWindowBits; + self->d_stream.zalloc=__zlib_dec_Alloc; + self->d_stream.zfree=__dec_free; + self->d_stream.opaque=self; + ret = inflateInit2(&self->d_stream,self->windowBits); + if (ret!=Z_OK) { _dec_onDecErr_up(); _dec_openErr_rt(); } + return self; + } + static hpatch_decompressHandle _zlib_decompress_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _zlib_TDecompress* self=0; + unsigned char* _mem_buf=(unsigned char*)_dec_malloc(sizeof(_zlib_TDecompress)+kDecompressBufSize); + if (!_mem_buf) _dec_memErr_rt(); + self=_zlib_decompress_open_at(decompressPlugin,codeStream,code_begin,code_end,1, + (_zlib_TDecompress*)_mem_buf,sizeof(_zlib_TDecompress)+kDecompressBufSize); + if (!self) + free(_mem_buf); + return self; + } + static hpatch_decompressHandle _zlib_decompress_open_deflate(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _zlib_TDecompress* self=0; + unsigned char* _mem_buf=(unsigned char*)_dec_malloc(sizeof(_zlib_TDecompress)+kDecompressBufSize); + if (!_mem_buf) _dec_memErr_rt(); + self=_zlib_decompress_open_at(decompressPlugin,codeStream,code_begin,code_end,0, + (_zlib_TDecompress*)_mem_buf,sizeof(_zlib_TDecompress)+kDecompressBufSize); + if (!self) + free(_mem_buf); + return self; + } + + static _zlib_TDecompress* _zlib_decompress_open_by(hpatch_TDecompress* decompressPlugin, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end, + int isSavedWindowBits, + unsigned char* _mem_buf,size_t _mem_buf_size){ + #define __MAX_TS(a,b) ((a)>=(b)?(a):(b)) + const hpatch_size_t kZlibAlign=__MAX_TS(__MAX_TS(sizeof(hpatch_StreamPos_t),sizeof(void*)),sizeof(uLongf)); + #undef __MAX_TS + unsigned char* _mem_buf_end=_mem_buf+_mem_buf_size; + unsigned char* self_at=(unsigned char*)_hpatch_align_upper(_mem_buf,kZlibAlign); + if (self_at>=_mem_buf_end) return 0; + return _zlib_decompress_open_at(decompressPlugin,codeStream,code_begin,code_end,isSavedWindowBits, + (_zlib_TDecompress*)self_at,_mem_buf_end-self_at); + } + static hpatch_BOOL _zlib_decompress_close_by(struct hpatch_TDecompress* decompressPlugin, + _zlib_TDecompress* self){ + hpatch_BOOL result=hpatch_TRUE; + if (!self) return result; + _dec_onDecErr_up(); + if (self->d_stream.state!=0){ + _dec_close_check(Z_OK==inflateEnd(&self->d_stream)); + } + memset(self,0,sizeof(_zlib_TDecompress)); + return result; + } + + static hpatch_BOOL _zlib_decompress_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _zlib_TDecompress* self=(_zlib_TDecompress*)decompressHandle; + hpatch_BOOL result=_zlib_decompress_close_by(decompressPlugin,self); + if (self) free(self); + return result; + } + + static hpatch_BOOL _zlib_reset_for_next_node(z_stream* d_stream){ + //backup + Bytef* next_out_back=d_stream->next_out; + Bytef* next_in_back=d_stream->next_in; + unsigned int avail_out_back=d_stream->avail_out; + unsigned int avail_in_back=d_stream->avail_in; + //reset + if (Z_OK!=inflateReset(d_stream)) return hpatch_FALSE; + //restore + d_stream->next_out=next_out_back; + d_stream->next_in=next_in_back; + d_stream->avail_out=avail_out_back; + d_stream->avail_in=avail_in_back; + return hpatch_TRUE; + } + static hpatch_BOOL __zlib_do_inflate(hpatch_decompressHandle decompressHandle){ + _zlib_TDecompress* self=(_zlib_TDecompress*)decompressHandle; + uInt avail_out_back,avail_in_back; + int ret; + hpatch_StreamPos_t codeLen=(self->code_end - self->code_begin); + if ((self->d_stream.avail_in==0)&&(codeLen>0)) { + size_t readLen=self->dec_buf_size; + if (readLen>codeLen) readLen=(size_t)codeLen; + self->d_stream.next_in=self->dec_buf; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->dec_buf, + self->dec_buf+readLen)) return hpatch_FALSE;//error; + self->d_stream.avail_in=(uInt)readLen; + self->code_begin+=readLen; + codeLen-=readLen; + } + + avail_out_back=self->d_stream.avail_out; + avail_in_back=self->d_stream.avail_in; + ret=inflate(&self->d_stream,Z_NO_FLUSH); + if (ret==Z_OK){ + if ((self->d_stream.avail_in==avail_in_back)&&(self->d_stream.avail_out==avail_out_back)) + _dec_onDecErr_rt();//error; + }else if (ret==Z_STREAM_END){ + if (self->d_stream.avail_in+codeLen>0){ //next compress node! + if (!_zlib_reset_for_next_node(&self->d_stream)) + _dec_onDecErr_rt();//error; + }else{//all end + if (self->d_stream.avail_out!=0) + _dec_onDecErr_rt();//error; + } + }else{ + _dec_onDecErr_rt();//error; + } + return hpatch_TRUE; + } + static hpatch_BOOL _zlib_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _zlib_TDecompress* self=(_zlib_TDecompress*)decompressHandle; + assert(out_part_data<=out_part_data_end); + + self->d_stream.next_out = out_part_data; + self->d_stream.avail_out =(uInt)(out_part_data_end-out_part_data); + while (self->d_stream.avail_out>0) { + if (!__zlib_do_inflate(self)) + return hpatch_FALSE;//error; + } + return hpatch_TRUE; + } + static hpatch_inline int _zlib_is_decompress_finish(const hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _zlib_TDecompress* self=(_zlib_TDecompress*)decompressHandle; + unsigned char _empty=0; + while (self->code_begin!=self->code_end){ //for end tag code + self->d_stream.next_out = &_empty; + self->d_stream.avail_out=0; + if (!__zlib_do_inflate(self)){ + self->d_stream.next_out=0; + return hpatch_FALSE;//error; + } + } + self->d_stream.next_out=0; + return (self->code_begin==self->code_end) + &(self->d_stream.avail_in==0) + &(self->d_stream.avail_out==0); + } + static hpatch_TDecompress zlibDecompressPlugin={_zlib_is_can_open,_zlib_decompress_open, + _zlib_decompress_close,_zlib_decompress_part}; + static hpatch_TDecompress zlibDecompressPlugin_deflate={_zlib_is_can_open,_zlib_decompress_open_deflate, + _zlib_decompress_close,_zlib_decompress_part}; +#endif//_CompressPlugin_zlib + + +#ifdef _CompressPlugin_ldef +#if (_IsNeedIncludeDefaultCompressHead) +# include "libdeflate.h" // https://github.com/sisong/libdeflate/tree/stream-mt based on https://github.com/ebiggers/libdeflate +# if (_CompressPlugin_ldef_is_use_zlib) +# include "zlib.h" // https://github.com/sisong/zlib/tree/bit_pos_padding based on https://github.com/madler/zlib +# endif +#endif + static const size_t _de_ldef_kDictSize = 1024*32; + static const size_t _de_ldef_kMaxBlockSize =1024*32*19; + // used _de_ldef_kMaxBlockSize*2 memory; optimized speed for + // libdefalte & zlib's deflate code ..., when theirs input deflate code compress block size<=_de_ldef_kMaxBlockSize/2; + // if (_de_ldef_kMaxBlockSize>=compress block size>_de_ldef_kMaxBlockSize/2) speed will little slower; + // if (compress block size>_de_ldef_kMaxBlockSize) && if (_CompressPlugin_ldef_is_use_zlib!=0), + // then swap to zlib decompressor & very slower (slower than zlib); && if (_CompressPlugin_ldef_is_use_zlib==0) will decompress fail. + + typedef struct _ldef_TDecompress{ + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + const struct hpatch_TStreamInput* codeStream; + + unsigned char* data_buf; + size_t data_buf_size; + unsigned char* code_buf; + size_t code_buf_size; + struct libdeflate_decompressor* d; + hpatch_dec_error_t decError; + + size_t data_cur; + size_t out_cur; + size_t code_cur; + #if (_CompressPlugin_ldef_is_use_zlib) + hpatch_BOOL is_swap_to_zlib; + z_stream d_stream; + #endif + } _ldef_TDecompress; + + #if (_CompressPlugin_ldef_is_use_zlib) + static hpatch_inline hpatch_BOOL _ldef_is_swap_to_zlib(_ldef_TDecompress* self){ + return self->is_swap_to_zlib; + } + static hpatch_BOOL _ldef_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end); + static hpatch_BOOL _ldef_swap_from_zlib(_ldef_TDecompress* self){ + const size_t out_part_len=self->d_stream.avail_out; + self->is_swap_to_zlib=hpatch_FALSE; + libdeflate_deflate_decompress_block_reset(self->d); + { + unsigned long shift_v; + unsigned int shift_bit; + zlib_inflate_shift_value(&self->d_stream,&shift_v,&shift_bit); + if (shift_bit>>3) _dec_onDecErr_rt(); + libdeflate_deflate_decompress_set_state(self->d,(uint16_t)((shift_v<<3)|shift_bit)); + } + assert(self->data_cur==_de_ldef_kDictSize); + self->code_cur=self->code_buf_size-self->d_stream.avail_in; + assert(self->d_stream.next_in==self->code_buf+self->code_cur); + { + uInt dict_size=0; + if (Z_OK!=inflateGetDictionary(&self->d_stream,0,&dict_size)) _dec_onDecErr_rt(); + if (Z_OK!=inflateGetDictionary(&self->d_stream,self->data_buf+self->data_cur-dict_size,0)) _dec_onDecErr_rt(); + } + if (out_part_len>0) + return _ldef_decompress_part(self,self->d_stream.next_out,self->d_stream.next_out+out_part_len); + else + return hpatch_TRUE; + } + static hpatch_BOOL _ldef_decompress_part_by_zlib(_ldef_TDecompress* self, + unsigned char* out_part_data,size_t out_part_len){ + self->d_stream.next_out=out_part_data; + self->d_stream.avail_out=(uInt)out_part_len; + while (self->d_stream.avail_out){ + uInt avail_out_back,avail_in_back; + int ret; + hpatch_StreamPos_t codeLen=(self->code_end-self->code_begin); + if ((self->d_stream.avail_in==0)&&(codeLen>0)) { + size_t readLen=self->code_buf_size; + if (readLen>codeLen) readLen=(size_t)codeLen; + self->d_stream.next_in=self->code_buf+self->code_buf_size-readLen; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->d_stream.next_in, + self->d_stream.next_in+readLen)) return hpatch_FALSE;//error; + self->d_stream.avail_in=(uInt)readLen; + self->code_begin+=readLen; + codeLen-=readLen; + } + + avail_out_back=self->d_stream.avail_out; + avail_in_back=self->d_stream.avail_in; + ret=inflate(&self->d_stream,Z_BLOCK); + if (ret==Z_OK){ + if ((self->d_stream.avail_in==avail_in_back)&&(self->d_stream.avail_out==avail_out_back)) + _dec_onDecErr_rt();//error; + if (zlib_inflate_is_block_end(&self->d_stream)&&(!zlib_inflate_is_last_block_end(&self->d_stream))) + return _ldef_swap_from_zlib(self); + }else if (ret==Z_STREAM_END){ + if (self->d_stream.avail_in+codeLen>0){ //next compress node! + zlib_inflate_set_shift_value(&self->d_stream,0,0); + return _ldef_swap_from_zlib(self); + }else{//all end + if (self->d_stream.avail_out!=0) + _dec_onDecErr_rt();//error; + } + }else{ + _dec_onDecErr_rt();//error; + } + } + return hpatch_TRUE; + } + static hpatch_BOOL _ldef_swap_to_zlib(_ldef_TDecompress* self,uint16_t dec_state, + unsigned char* out_part_data,size_t out_part_len){ + self->is_swap_to_zlib=hpatch_TRUE; + if (self->d_stream.state==0){ + if (Z_OK!=inflateInit2(&self->d_stream,-15)){ + if (!self->decError) self->decError=hpatch_dec_open_error; + return hpatch_FALSE; + } + }else{ + if (Z_OK!=inflateReset(&self->d_stream)) _dec_onDecErr_rt(); + } + zlib_inflate_set_shift_value(&self->d_stream,(uLong)(dec_state>>3),(uInt)(dec_state&((1<<3)-1))); + if (Z_OK!=inflateSetDictionary(&self->d_stream,self->data_buf+self->data_cur-_de_ldef_kDictSize,(uInt)_de_ldef_kDictSize)) _dec_onDecErr_rt(); + self->d_stream.next_in=self->code_buf+self->code_cur; + self->d_stream.avail_in=(uInt)(self->code_buf_size-self->code_cur); + return _ldef_decompress_part_by_zlib(self,out_part_data,out_part_len); + } + #endif //_CompressPlugin_ldef_is_use_zlib + + static hpatch_BOOL _ldef_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"zlib"))||(0==strcmp(compressType,"pzlib")); + } + + static _ldef_TDecompress* _ldef_decompress_open_at(hpatch_TDecompress* decompressPlugin, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin,hpatch_StreamPos_t code_end, + int isSavedWindowBits,_ldef_TDecompress* self, + size_t data_buf_size,size_t code_buf_size){ + if (isSavedWindowBits){//load kWindowBits + signed char kWindowBits=0; + if (code_end-code_begin<1) _dec_openErr_rt(); + if (!codeStream->read(codeStream,code_begin,(unsigned char*)&kWindowBits, + (unsigned char*)&kWindowBits+1)) return 0; + ++code_begin; + if (!((-15<=kWindowBits)&&(kWindowBits<0))) + _dec_openErr_rt(); //now, unsupported these window bits + } + + memset(self,0,sizeof(_ldef_TDecompress)); + self->d=libdeflate_alloc_decompressor(); + if (!self->d) _dec_memErr_rt(); + self->data_buf=((unsigned char*)self)+sizeof(_ldef_TDecompress); + self->data_buf_size=data_buf_size; + self->code_buf=self->data_buf+data_buf_size; + self->code_buf_size=code_buf_size; + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + + self->data_cur=_de_ldef_kDictSize; //empty + self->out_cur=_de_ldef_kDictSize; + self->code_cur=code_buf_size; //empty + return self; + } + + static hpatch_decompressHandle _ldef_decompress_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _ldef_TDecompress* self=0; + const hpatch_StreamPos_t in_size=code_end-code_begin; + const size_t data_buf_size=_de_ldef_kDictSize+((_de_ldef_kMaxBlockSized_stream.state!=0){ + _dec_close_check(Z_OK==inflateEnd(&self->d_stream)); + } + #endif + if (self->d!=0) + libdeflate_free_decompressor(self->d); + memset(self,0,sizeof(_ldef_TDecompress)); + return result; + } + + static hpatch_BOOL _ldef_decompress_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _ldef_TDecompress* self=(_ldef_TDecompress*)decompressHandle; + hpatch_BOOL result=_ldef_decompress_close_by(decompressPlugin,self); + if (self) free(self); + return result; + } + + static hpatch_BOOL _ldef_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _ldef_TDecompress* self=(_ldef_TDecompress*)decompressHandle; + size_t out_part_len=out_part_data_end-out_part_data; + #if (_CompressPlugin_ldef_is_use_zlib) + if (_ldef_is_swap_to_zlib(self)&&out_part_len) + return _ldef_decompress_part_by_zlib(self,out_part_data,out_part_len); + #endif + const size_t kDictSize=_de_ldef_kDictSize; + // [ ( dict ) | dataBuf ] [ codeBuf ] + // ^ ^ ^ ^ ^ ^ ^ + // data_buf out_cur data_cur data_buf_size code_buf code_cur code_buf_size + while (out_part_len) { + {//write data out + size_t out_len=self->data_cur-self->out_cur; + if (out_len){ //have data + out_len=(out_lendata_buf+self->out_cur,out_len); + self->out_cur+=out_len; + out_part_data+=out_len; + out_part_len-=out_len; + continue; + } + } + + size_t kLimitDataSize=_de_ldef_kMaxBlockSize/2+kDictSize; + size_t kLimitCodeSize=self->code_buf_size/2; + __datas_prepare: + {//read code in + if (self->code_cur>kLimitCodeSize){ + const hpatch_StreamPos_t _codeSize=self->code_end-self->code_begin; + size_t read_len=(self->code_cur<_codeSize)?self->code_cur:(size_t)_codeSize; + if (read_len){ + size_t code_len=self->code_buf_size-self->code_cur; + unsigned char* pcode=self->code_buf+self->code_cur; + unsigned char* pdst=pcode-read_len; + memmove(pdst,pcode,code_len); + pdst+=code_len; + if (!self->codeStream->read(self->codeStream,self->code_begin,pdst,pdst+read_len)) + return 0; //read error + self->code_begin+=read_len; + self->code_cur-=read_len; + } + } + } + {//move dict + if (self->data_cur>kLimitDataSize){ + size_t move_offset=self->data_cur-kDictSize; + if (move_offset){ + memmove(self->data_buf,self->data_buf+move_offset,kDictSize); + self->data_cur=kDictSize; + self->out_cur=kDictSize; + } + } + } + {// decompress + int is_final_block_ret; + size_t actual_in_nbytes_ret; + size_t actual_out_nbytes_ret; + const uint16_t dec_state=libdeflate_deflate_decompress_get_state(self->d); + enum libdeflate_result ret=libdeflate_deflate_decompress_block(self->d, + self->code_buf+self->code_cur,self->code_buf_size-self->code_cur, + self->data_buf,self->data_cur,self->data_buf_size-self->data_cur, + &actual_in_nbytes_ret,&actual_out_nbytes_ret, + LIBDEFLATE_STOP_BY_ANY_BLOCK,&is_final_block_ret); + if (ret!=LIBDEFLATE_SUCCESS){ + if ((self->code_begin==self->code_end)&&(ret!=LIBDEFLATE_INSUFFICIENT_SPACE)) + _dec_onDecErr_rt(); + if ((self->data_cur>kDictSize)||((self->code_cur>0)&&(self->code_begincode_end))){ + kLimitDataSize=kDictSize; + kLimitCodeSize=0; + libdeflate_deflate_decompress_set_state(self->d,dec_state); + goto __datas_prepare; //retry by libdefalte + } + #if (_CompressPlugin_ldef_is_use_zlib) + return _ldef_swap_to_zlib(self,dec_state,out_part_data,out_part_len); //retry by zlib + #else + _dec_onDecErr_rt(); + #endif + } + self->code_cur+=actual_in_nbytes_ret; + self->data_cur+=actual_out_nbytes_ret; + if (is_final_block_ret) + libdeflate_deflate_decompress_block_reset(self->d); + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress ldefDecompressPlugin={_ldef_is_can_open,_ldef_decompress_open, + _ldef_decompress_close,_ldef_decompress_part}; +#endif//_CompressPlugin_ldef + + +#ifdef _CompressPlugin_bz2 +#if (_IsNeedIncludeDefaultCompressHead) +# include "bzlib.h" // http://www.bzip.org/ https://github.com/sisong/bzip2 +#endif + typedef struct _bz2_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + bz_stream d_stream; + hpatch_dec_error_t decError; + unsigned char dec_buf[kDecompressBufSize]; + } _bz2_TDecompress; + static hpatch_BOOL _bz2_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"bz2"))||(0==strcmp(compressType,"bzip2")) + ||(0==strcmp(compressType,"pbz2"))||(0==strcmp(compressType,"pbzip2")); + } + static hpatch_decompressHandle _bz2_open(struct hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + int ret; + _bz2_TDecompress* self=(_bz2_TDecompress*)_dec_malloc(sizeof(_bz2_TDecompress)); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_bz2_TDecompress)-kDecompressBufSize); + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + + ret=BZ2_bzDecompressInit(&self->d_stream,0,0); + if (ret!=BZ_OK){ free(self); _dec_openErr_rt(); } + return self; + } + static hpatch_BOOL _bz2_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + hpatch_BOOL result=hpatch_TRUE; + _bz2_TDecompress* self=(_bz2_TDecompress*)decompressHandle; + if (!self) return result; + _dec_onDecErr_up(); + _dec_close_check(BZ_OK==BZ2_bzDecompressEnd(&self->d_stream)); + free(self); + return result; + } + static hpatch_BOOL _bz2_reset_for_next_node(_bz2_TDecompress* self){ + //backup + char* next_out_back=self->d_stream.next_out; + char* next_in_back=self->d_stream.next_in; + unsigned int avail_out_back=self->d_stream.avail_out; + unsigned int avail_in_back=self->d_stream.avail_in; + //reset + if (BZ_OK!=BZ2_bzDecompressEnd(&self->d_stream)) _dec_onDecErr_rt(); + if (BZ_OK!=BZ2_bzDecompressInit(&self->d_stream,0,0)) _dec_onDecErr_rt(); + //restore + self->d_stream.next_out=next_out_back; + self->d_stream.next_in=next_in_back; + self->d_stream.avail_out=avail_out_back; + self->d_stream.avail_in=avail_in_back; + return hpatch_TRUE; + } + + static hpatch_BOOL _bz2_decompress_part_(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end, + hpatch_BOOL isMustOutData){ + _bz2_TDecompress* self=(_bz2_TDecompress*)decompressHandle; + assert(out_part_data<=out_part_data_end); + + self->d_stream.next_out =(char*)out_part_data; + self->d_stream.avail_out =(unsigned int)(out_part_data_end-out_part_data); + while (self->d_stream.avail_out>0) { + unsigned int avail_out_back,avail_in_back; + int ret; + hpatch_StreamPos_t codeLen=(self->code_end - self->code_begin); + if ((self->d_stream.avail_in==0)&&(codeLen>0)) { + size_t readLen=kDecompressBufSize; + self->d_stream.next_in=(char*)self->dec_buf; + if (readLen>codeLen) readLen=(size_t)codeLen; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->dec_buf, + self->dec_buf+readLen)) return hpatch_FALSE;//error; + self->d_stream.avail_in=(unsigned int)readLen; + self->code_begin+=readLen; + codeLen-=readLen; + } + + avail_out_back=self->d_stream.avail_out; + avail_in_back=self->d_stream.avail_in; + ret=BZ2_bzDecompress(&self->d_stream); + if (ret==BZ_OK){ + if ((self->d_stream.avail_in==avail_in_back)&&(self->d_stream.avail_out==avail_out_back)) + _dec_onDecErr_rt();//error; + }else if (ret==BZ_STREAM_END){ + if (self->d_stream.avail_in+codeLen>0){ //next compress node! + if (!_bz2_reset_for_next_node(self)) + return hpatch_FALSE;//error; + }else{//all end + if (self->d_stream.avail_out!=0){ + if (isMustOutData){ //fill out 0 + memset(self->d_stream.next_out,0,self->d_stream.avail_out); + self->d_stream.next_out+=self->d_stream.avail_out; + self->d_stream.avail_out=0; + }else{ + _dec_onDecErr_rt();//error; + } + } + } + }else{ + _dec_onDecErr_rt();//error; + } + } + return hpatch_TRUE; + } + static hpatch_BOOL _bz2_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + return _bz2_decompress_part_(decompressHandle,out_part_data,out_part_data_end,hpatch_FALSE); + } + static hpatch_BOOL _bz2_decompress_part_unsz(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + return _bz2_decompress_part_(decompressHandle,out_part_data,out_part_data_end,hpatch_TRUE); + } + + static hpatch_TDecompress bz2DecompressPlugin={_bz2_is_can_open,_bz2_open, + _bz2_close,_bz2_decompress_part}; + + //unkown uncompress data size + static hpatch_TDecompress _bz2DecompressPlugin_unsz={_bz2_is_can_open,_bz2_open, + _bz2_close,_bz2_decompress_part_unsz}; +#endif//_CompressPlugin_bz2 + + +#if (defined _CompressPlugin_lzma) || (defined _CompressPlugin_lzma2) +#if (_IsNeedIncludeDefaultCompressHead) +# include "LzmaDec.h" // "lzma/C/LzmaDec.h" https://github.com/sisong/lzma +# ifdef _CompressPlugin_lzma2 +# include "Lzma2Dec.h" +# if defined(_IS_USED_MULTITHREAD) && !defined(Z7_ST) +# include "Lzma2DecMt.h" +# include "libParallel/parallel_import_c.h" +# endif +# endif +#endif +#endif + +#ifdef _CompressPlugin_lzma + typedef struct _lzma_TDecompress{ + ISzAlloc memAllocBase; + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + CLzmaDec decEnv; + SizeT decCopyPos; + SizeT decReadPos; + hpatch_dec_error_t decError; + unsigned char dec_buf[kDecompressBufSize]; + } _lzma_TDecompress; + static void * __lzma1_dec_Alloc(ISzAllocPtr p, size_t size) + __dec_Alloc_fun(_lzma_TDecompress,p,size) + + static hpatch_BOOL _lzma_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"lzma")); + } + static hpatch_decompressHandle _lzma_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _lzma_TDecompress* self=0; + SRes ret; + unsigned char propsSize=0; + unsigned char props[256]; + //load propsSize + if (code_end-code_begin<1) _dec_openErr_rt(); + if (!codeStream->read(codeStream,code_begin,&propsSize,&propsSize+1)) return 0; + ++code_begin; + if (propsSize>(code_end-code_begin)) _dec_openErr_rt(); + //load props + if (!codeStream->read(codeStream,code_begin,props,props+propsSize)) return 0; + code_begin+=propsSize; + + self=(_lzma_TDecompress*)_dec_malloc(sizeof(_lzma_TDecompress)); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_lzma_TDecompress)-kDecompressBufSize); + self->memAllocBase.Alloc=__lzma1_dec_Alloc; + *((void**)&self->memAllocBase.Free)=(void*)__dec_free; + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + + self->decCopyPos=0; + self->decReadPos=kDecompressBufSize; + + LzmaDec_Construct(&self->decEnv); + ret=LzmaDec_Allocate(&self->decEnv,props,propsSize,&self->memAllocBase); + if (ret!=SZ_OK){ _dec_onDecErr_up(); free(self); _dec_openErr_rt(); } + LzmaDec_Init(&self->decEnv); + return self; + } + static hpatch_BOOL _lzma_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _lzma_TDecompress* self=(_lzma_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + LzmaDec_Free(&self->decEnv,&self->memAllocBase); + _dec_onDecErr_up(); + free(self); + return hpatch_TRUE; + } + static hpatch_BOOL _lzma_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _lzma_TDecompress* self=(_lzma_TDecompress*)decompressHandle; + unsigned char* out_cur=out_part_data; + assert(out_part_data<=out_part_data_end); + while (out_curdecEnv.dicPos-self->decCopyPos); + if (copyLen>0){ + if (copyLen>(size_t)(out_part_data_end-out_cur)) + copyLen=(out_part_data_end-out_cur); + memcpy(out_cur,self->decEnv.dic+self->decCopyPos,copyLen); + out_cur+=copyLen; + self->decCopyPos+=copyLen; + if ((self->decEnv.dicPos==self->decEnv.dicBufSize) + &&(self->decEnv.dicPos==self->decCopyPos)){ + self->decEnv.dicPos=0; + self->decCopyPos=0; + } + }else{ + ELzmaStatus status; + SizeT inSize,dicPos_back; + SRes res; + hpatch_StreamPos_t codeLen=(self->code_end - self->code_begin); + if ((self->decReadPos==kDecompressBufSize)&&(codeLen>0)) { + size_t readLen=kDecompressBufSize; + if (readLen>codeLen) readLen=(size_t)codeLen; + self->decReadPos=kDecompressBufSize-readLen; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->dec_buf+self->decReadPos, + self->dec_buf+self->decReadPos+readLen)) return hpatch_FALSE;//error; + self->code_begin+=readLen; + } + + inSize=kDecompressBufSize-self->decReadPos; + dicPos_back=self->decEnv.dicPos; + res=LzmaDec_DecodeToDic(&self->decEnv,self->decEnv.dicBufSize, + self->dec_buf+self->decReadPos,&inSize,LZMA_FINISH_ANY,&status); + if(res==SZ_OK){ + if ((inSize==0)&&(self->decEnv.dicPos==dicPos_back)) + _dec_onDecErr_rt();//error; + }else{ + _dec_onDecErr_rt();//error; + } + self->decReadPos+=inSize; + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress lzmaDecompressPlugin={_lzma_is_can_open,_lzma_open, + _lzma_close,_lzma_decompress_part}; +#endif//_CompressPlugin_lzma + +#ifdef _CompressPlugin_lzma2 + typedef struct _lzma2_TDecompress{ + ISzAlloc memAllocBase; + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + CLzma2Dec decEnv; + SizeT decCopyPos; + SizeT decReadPos; + hpatch_dec_error_t decError; + unsigned char dec_buf[kDecompressBufSize]; + } _lzma2_TDecompress; + static void * __lzma2_dec_Alloc(ISzAllocPtr p, size_t size) + __dec_Alloc_fun(_lzma2_TDecompress,p,size) + + static hpatch_BOOL _lzma2_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"lzma2")); + } + static hpatch_decompressHandle _lzma2_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _lzma2_TDecompress* self=0; + SRes ret; + unsigned char propsSize=0; + //load propsSize + if (code_end-code_begin<1) _dec_openErr_rt(); + if (!codeStream->read(codeStream,code_begin,&propsSize,&propsSize+1)) return 0; + ++code_begin; + + self=(_lzma2_TDecompress*)_dec_malloc(sizeof(_lzma2_TDecompress)); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_lzma2_TDecompress)-kDecompressBufSize); + self->memAllocBase.Alloc=__lzma2_dec_Alloc; + *((void**)&self->memAllocBase.Free)=(void*)__dec_free; + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + + self->decCopyPos=0; + self->decReadPos=kDecompressBufSize; + + Lzma2Dec_Construct(&self->decEnv); + ret=Lzma2Dec_Allocate(&self->decEnv,propsSize,&self->memAllocBase); + if (ret!=SZ_OK){ _dec_onDecErr_up(); free(self); _dec_openErr_rt(); } + Lzma2Dec_Init(&self->decEnv); + return self; + } + static hpatch_BOOL _lzma2_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _lzma2_TDecompress* self=(_lzma2_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + Lzma2Dec_Free(&self->decEnv,&self->memAllocBase); + _dec_onDecErr_up(); + free(self); + return hpatch_TRUE; + } + static hpatch_BOOL _lzma2_decompress_part_(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end, + hpatch_BOOL isMustOutData){ + _lzma2_TDecompress* self=(_lzma2_TDecompress*)decompressHandle; + unsigned char* out_cur=out_part_data; + assert(out_part_data<=out_part_data_end); + while (out_curdecEnv.decoder.dicPos-self->decCopyPos); + if (copyLen>0){ + if (copyLen>(size_t)(out_part_data_end-out_cur)) + copyLen=(out_part_data_end-out_cur); + memcpy(out_cur,self->decEnv.decoder.dic+self->decCopyPos,copyLen); + out_cur+=copyLen; + self->decCopyPos+=copyLen; + if ((self->decEnv.decoder.dicPos==self->decEnv.decoder.dicBufSize) + &&(self->decEnv.decoder.dicPos==self->decCopyPos)){ + self->decEnv.decoder.dicPos=0; + self->decCopyPos=0; + } + }else{ + ELzmaStatus status; + SizeT inSize,dicPos_back; + SRes res; + hpatch_StreamPos_t codeLen=(self->code_end - self->code_begin); + if ((self->decReadPos==kDecompressBufSize)&&(codeLen>0)) { + size_t readLen=kDecompressBufSize; + if (readLen>codeLen) readLen=(size_t)codeLen; + self->decReadPos=kDecompressBufSize-readLen; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->dec_buf+self->decReadPos, + self->dec_buf+self->decReadPos+readLen)) return hpatch_FALSE;//error; + self->code_begin+=readLen; + } + + inSize=kDecompressBufSize-self->decReadPos; + dicPos_back=self->decEnv.decoder.dicPos; + res=Lzma2Dec_DecodeToDic(&self->decEnv,self->decEnv.decoder.dicBufSize, + self->dec_buf+self->decReadPos,&inSize,LZMA_FINISH_ANY,&status); + if(res==SZ_OK){ + if ((inSize==0)&&(self->decEnv.decoder.dicPos==dicPos_back)){ + if (isMustOutData){ //fill out 0 + memset(out_cur,0,out_part_data_end-out_cur); + return hpatch_TRUE; + }else{ + _dec_onDecErr_rt();//error; + } + } + }else{ + _dec_onDecErr_rt();//error; + } + self->decReadPos+=inSize; + } + } + return hpatch_TRUE; + } + + static hpatch_BOOL _lzma2_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + return _lzma2_decompress_part_(decompressHandle,out_part_data,out_part_data_end,hpatch_FALSE); + } + static hpatch_BOOL _lzma2_decompress_part_unsz(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + return _lzma2_decompress_part_(decompressHandle,out_part_data,out_part_data_end,hpatch_TRUE); + } + + + + static hpatch_TDecompress lzma2DecompressPlugin={_lzma2_is_can_open,_lzma2_open, + _lzma2_close,_lzma2_decompress_part}; + //unkown uncompress data size + static hpatch_TDecompress _lzma2DecompressPlugin_unsz={_lzma2_is_can_open,_lzma2_open, + _lzma2_close,_lzma2_decompress_part_unsz}; +#endif//_CompressPlugin_lzma2 + + +//--- lzma2 multi-thread decompress --- +#if defined(_CompressPlugin_lzma2) && defined(_IS_USED_MULTITHREAD) && (!defined(Z7_ST)) +#ifndef _CompressPlugin_lzma2mt +# define _CompressPlugin_lzma2mt 1 +#endif +#endif + +#if (_CompressPlugin_lzma2mt) + +#define kLzma2mtRingBufSize (1<<22) // 4MB + +typedef struct { + unsigned char* buf; + size_t capacity; + size_t r, w, used; + HLocker locker; + HCondvar hasSpaceCond; + HCondvar hasDataCond; +} _lzma2mt_ringbuf; + +typedef struct { + ISeqInStream vt; + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t curPos, endPos; + hpatch_BOOL volatile* pIsClosing; +} _lzma2mt_InStream; + +typedef struct { + ISeqOutStream vt; + struct _lzma2mt_TDecompress* dec; +} _lzma2mt_OutStream; + +typedef struct _lzma2mt_TDecompress { + ISzAlloc memAllocBase; + _lzma2mt_ringbuf ring; + _lzma2mt_InStream inStream; + _lzma2mt_OutStream outStream; + CLzma2DecMtHandle decMtHandle; + CLzma2DecMtProps mtProps; + Byte propByte; + UInt64 dataSize; + size_t threadNum; + volatile hpatch_BOOL isDecodeFinished; + volatile hpatch_BOOL isClosing; + HCondvar finishedCond; + hpatch_dec_error_t decError; +} _lzma2mt_TDecompress; + +static void* __lzma2mt_dec_Alloc(ISzAllocPtr p, size_t size) + __dec_Alloc_fun(_lzma2mt_TDecompress,p,size) + +static SRes _lzma2mt_in_read(ISeqInStreamPtr p, void *buf, size_t *size){ + _lzma2mt_InStream* self=(_lzma2mt_InStream*)p; + if (*size==0) return SZ_OK; + if (*self->pIsClosing){ *size=0; return SZ_OK; } + { + hpatch_StreamPos_t remain=self->endPos-self->curPos; + if (*size>remain) *size=(size_t)remain; + } + if (*size==0) return SZ_OK; + if (!self->codeStream->read(self->codeStream,self->curPos, + (unsigned char*)buf,(unsigned char*)buf+*size)) + return SZ_ERROR_READ; + self->curPos+=*size; + return SZ_OK; +} + +static size_t _lzma2mt_out_write(ISeqOutStreamPtr p, const void *buf, size_t size){ + _lzma2mt_OutStream* out=(_lzma2mt_OutStream*)p; + _lzma2mt_TDecompress* self=out->dec; + const unsigned char* src=(const unsigned char*)buf; + size_t remaining=size; + while (remaining>0){ + c_locker_enter(self->ring.locker); + while ((self->ring.used==self->ring.capacity) && (!self->isClosing)){ + c_condvar_wait(self->ring.hasSpaceCond,self->ring.locker); + } + if (self->isClosing){ c_locker_leave(self->ring.locker); return 0; } + { + size_t freeSpace=self->ring.capacity-self->ring.used; + size_t toCopy=(remainingring.capacity-self->ring.w; + if (toCopy>atEnd) toCopy=atEnd; + memcpy(self->ring.buf+self->ring.w,src,toCopy); + self->ring.w=(self->ring.w+toCopy)%self->ring.capacity; + self->ring.used+=toCopy; + src+=toCopy; + remaining-=toCopy; + } + c_condvar_signal(self->ring.hasDataCond); + c_locker_leave(self->ring.locker); + } + return size; +} + +static void _lzma2mt_decode_thread(int threadIndex, void* workData){ + _lzma2mt_TDecompress* self=(_lzma2mt_TDecompress*)workData; + UInt64 inProcessed=0; + int isMT=0; + const UInt64* outSizePtr=(self->dataSize>0)?&self->dataSize:0; + SRes res=Lzma2DecMt_Decode(self->decMtHandle,self->propByte,&self->mtProps, + &self->outStream.vt,outSizePtr,1, + &self->inStream.vt,&inProcessed,&isMT,0); + if ((res!=SZ_OK) && (!self->isClosing)) + self->decError=hpatch_dec_error; + c_locker_enter(self->ring.locker); + self->isDecodeFinished=hpatch_TRUE; + c_condvar_broadcast(self->ring.hasDataCond); + c_condvar_broadcast(self->ring.hasSpaceCond); + c_condvar_signal(self->finishedCond); + c_locker_leave(self->ring.locker); +} + +static hpatch_BOOL _lzma2mt_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"lzma2")); +} + +static hpatch_decompressHandle _lzma2mt_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _lzma2mt_TDecompress* self=0; + unsigned char propsSize=0; + if (code_end-code_begin<1) _dec_openErr_rt(); + if (!codeStream->read(codeStream,code_begin,&propsSize,&propsSize+1)) return 0; + ++code_begin; + + self=(_lzma2mt_TDecompress*)_dec_malloc(sizeof(_lzma2mt_TDecompress)); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_lzma2mt_TDecompress)); + + self->memAllocBase.Alloc=__lzma2mt_dec_Alloc; + *((void**)&self->memAllocBase.Free)=(void*)__dec_free; + + self->ring.buf=(unsigned char*)_dec_malloc(kLzma2mtRingBufSize); + if (!self->ring.buf){ free(self); _dec_memErr_rt(); } + self->ring.capacity=kLzma2mtRingBufSize; + self->ring.r=self->ring.w=self->ring.used=0; + self->ring.locker=c_locker_new(); + self->ring.hasSpaceCond=c_condvar_new(); + self->ring.hasDataCond=c_condvar_new(); + if ((!self->ring.locker) || (!self->ring.hasSpaceCond) || (!self->ring.hasDataCond)) + goto _lzma2mt_open_err; + + self->inStream.vt.Read=_lzma2mt_in_read; + self->inStream.codeStream=codeStream; + self->inStream.curPos=code_begin; + self->inStream.endPos=code_end; + self->inStream.pIsClosing=&self->isClosing; + + self->outStream.vt.Write=_lzma2mt_out_write; + self->outStream.dec=self; + + self->decMtHandle=Lzma2DecMt_Create(&self->memAllocBase,&self->memAllocBase); + if (!self->decMtHandle) goto _lzma2mt_open_err; + + self->propByte=propsSize; + self->dataSize=dataSize; + self->threadNum=decompressPlugin->dec_threadNum; + if (self->threadNum<1) self->threadNum=1; + + Lzma2DecMtProps_Init(&self->mtProps); + self->mtProps.numThreads=(unsigned)self->threadNum; + self->isDecodeFinished=hpatch_FALSE; + self->isClosing=hpatch_FALSE; + self->finishedCond=c_condvar_new(); + if (!self->finishedCond) goto _lzma2mt_open_err; + + if (!c_thread_parallel(1,_lzma2mt_decode_thread,self,0,0)){ + c_condvar_delete(self->finishedCond); self->finishedCond=0; + goto _lzma2mt_open_err; + } + return self; + +_lzma2mt_open_err: + if (self->ring.locker) c_locker_delete(self->ring.locker); + if (self->ring.hasSpaceCond) c_condvar_delete(self->ring.hasSpaceCond); + if (self->ring.hasDataCond) c_condvar_delete(self->ring.hasDataCond); + if (self->ring.buf) free(self->ring.buf); + if (self->decMtHandle) Lzma2DecMt_Destroy(self->decMtHandle); + if (self->finishedCond) c_condvar_delete(self->finishedCond); + free(self); + _dec_openErr_rt(); + return 0; +} + +static hpatch_BOOL _lzma2mt_close(hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _lzma2mt_TDecompress* self=(_lzma2mt_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + self->isClosing=hpatch_TRUE; + *self->inStream.pIsClosing=hpatch_TRUE; + // wake up any blocked threads + c_locker_enter(self->ring.locker); + c_condvar_broadcast(self->ring.hasSpaceCond); + c_condvar_broadcast(self->ring.hasDataCond); + c_locker_leave(self->ring.locker); + // wait for decode thread to finish + if (!self->isDecodeFinished){ + c_locker_enter(self->ring.locker); + while (!self->isDecodeFinished) + c_condvar_wait(self->finishedCond,self->ring.locker); + c_locker_leave(self->ring.locker); + } + if (self->decMtHandle) + Lzma2DecMt_Destroy(self->decMtHandle); + if (self->ring.locker) c_locker_delete(self->ring.locker); + if (self->ring.hasSpaceCond) c_condvar_delete(self->ring.hasSpaceCond); + if (self->ring.hasDataCond) c_condvar_delete(self->ring.hasDataCond); + if (self->ring.buf) free(self->ring.buf); + if (self->finishedCond) c_condvar_delete(self->finishedCond); + _dec_onDecErr_up(); + free(self); + return hpatch_TRUE; +} + +static hpatch_BOOL _lzma2mt_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _lzma2mt_TDecompress* self=(_lzma2mt_TDecompress*)decompressHandle; + unsigned char* out_cur=out_part_data; + while (out_curring.locker); + while (self->ring.used==0 && !self->isDecodeFinished){ + c_condvar_wait(self->ring.hasDataCond,self->ring.locker); + } + if (self->ring.used>0){ + size_t need=(size_t)(out_part_data_end-out_cur); + size_t toCopy=needring.used?need:self->ring.used; + size_t atEnd=self->ring.capacity-self->ring.r; + if (toCopy>atEnd) toCopy=atEnd; + memcpy(out_cur,self->ring.buf+self->ring.r,toCopy); + self->ring.r=(self->ring.r+toCopy)%self->ring.capacity; + self->ring.used-=toCopy; + out_cur+=toCopy; + c_condvar_signal(self->ring.hasSpaceCond); + c_locker_leave(self->ring.locker); + }else{ + c_locker_leave(self->ring.locker); + _dec_onDecErr_rt(); + } + } + return hpatch_TRUE; +} + +static hpatch_TDecompress lzma2mtDecompressPlugin={_lzma2mt_is_can_open,_lzma2mt_open, + _lzma2mt_close,_lzma2mt_decompress_part}; +#endif//_CompressPlugin_lzma2mt + + +#ifdef _CompressPlugin_7zXZ +#if (_IsNeedIncludeDefaultCompressHead) +# include "Xz.h" // "lzma/C/Xz.h" https://github.com/sisong/lzma +# include "7zCrc.h" // CrcGenerateTable() +#endif + +#ifndef _init_CompressPlugin_7zXZ_DEF +# define _init_CompressPlugin_7zXZ_DEF + static int _init_CompressPlugin_7zXZ(){ + static hpatch_BOOL _isInit=hpatch_FALSE; + if (!_isInit){ + CrcGenerateTable(); + _isInit=hpatch_TRUE; + } + return 0; + } +#endif + + typedef struct _7zXZ_TDecompress{ + ISzAlloc memAllocBase; + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + hpatch_TDecompress* decompressPlugin; + hpatch_BOOL isResetState; + + CXzUnpacker decEnv; + SizeT decCopyPos; + SizeT decReadPos; + hpatch_dec_error_t decError; + unsigned char dec_buf[kDecompressBufSize]; + } _7zXZ_TDecompress; + static void * __7zXZ_dec_Alloc(ISzAllocPtr p, size_t size) + __dec_Alloc_fun(_7zXZ_TDecompress,p,size) + + static hpatch_BOOL _7zXZ_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"7zXZ")); + } + static void _7zXZ_open_at(_7zXZ_TDecompress* self, hpatch_TDecompress* decompressPlugin,hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream,hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end,hpatch_BOOL isResetState,hpatch_BOOL isParseHead){ + memset(self,0,sizeof(_7zXZ_TDecompress)-kDecompressBufSize); + self->memAllocBase.Alloc=__7zXZ_dec_Alloc; + *((void**)&self->memAllocBase.Free)=(void*)__dec_free; + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->decompressPlugin=decompressPlugin; + self->isResetState=isResetState; + + self->decCopyPos=0; + self->decReadPos=kDecompressBufSize; + + XzUnpacker_Construct(&self->decEnv,&self->memAllocBase); + XzUnpacker_Init(&self->decEnv); + if (!isParseHead) + self->decEnv.state=XZ_STATE_BLOCK_HEADER; + } + static void _7zXZ_close_at(hpatch_decompressHandle decompressHandle){ + _7zXZ_TDecompress* self=(_7zXZ_TDecompress*)decompressHandle; + if (self){ + hpatch_TDecompress* decompressPlugin=self->decompressPlugin; + XzUnpacker_Free(&self->decEnv); + _dec_onDecErr_up(); + } + } + static hpatch_decompressHandle _7zXZ_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _7zXZ_TDecompress* self=0; + + self=(_7zXZ_TDecompress*)_dec_malloc(sizeof(_7zXZ_TDecompress)); + if (!self) _dec_memErr_rt(); + _7zXZ_open_at(self,decompressPlugin,dataSize,codeStream, + code_begin,code_end,hpatch_FALSE,hpatch_TRUE); + return self; + } + static hpatch_decompressHandle _7zXZ_a_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _7zXZ_TDecompress* self=0; + + self=(_7zXZ_TDecompress*)_dec_malloc(sizeof(_7zXZ_TDecompress)); + if (!self) _dec_memErr_rt(); + _7zXZ_open_at(self,decompressPlugin,dataSize,codeStream, + code_begin,code_end,hpatch_TRUE,hpatch_TRUE); + return self; + } + static hpatch_BOOL _7zXZ_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + if (decompressHandle){ + _7zXZ_close_at(decompressHandle); + free(decompressHandle); + } + return hpatch_TRUE; + } + static hpatch_BOOL _7zXZ_reset_code(hpatch_decompressHandle decompressHandle, + hpatch_StreamPos_t dataSize, + const struct hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _7zXZ_TDecompress* self=(_7zXZ_TDecompress*)decompressHandle; + hpatch_BOOL isResetState=self->isResetState; + if (isResetState){ + hpatch_TDecompress* decompressPlugin=self->decompressPlugin; + _7zXZ_close_at(self); + _7zXZ_open_at(self,decompressPlugin,dataSize,codeStream, + code_begin,code_end,isResetState,hpatch_FALSE); + }else{ + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->decCopyPos=0; + self->decReadPos=kDecompressBufSize; + } + return hpatch_TRUE; + } + static hpatch_BOOL _7zXZ_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _7zXZ_TDecompress* self=(_7zXZ_TDecompress*)decompressHandle; + unsigned char* out_cur=out_part_data; + assert(out_part_data<=out_part_data_end); + while (out_curcode_end-self->code_begin); + if ((self->decReadPos==kDecompressBufSize)&&(codeLen>0)) { + size_t readLen=kDecompressBufSize; + if (readLen>codeLen) readLen=(size_t)codeLen; + self->decReadPos=kDecompressBufSize-readLen; + if (!self->codeStream->read(self->codeStream,self->code_begin,self->dec_buf+self->decReadPos, + self->dec_buf+self->decReadPos+readLen)) return hpatch_FALSE;//error; + self->code_begin+=readLen; + codeLen-=readLen; + } + + inSize=kDecompressBufSize-self->decReadPos; + res=XzUnpacker_Code(&self->decEnv,out_cur,&outSize,self->dec_buf+self->decReadPos,&inSize, + codeLen==0,CODER_FINISH_ANY,&status); + if(res==SZ_OK){ + if ((inSize==0)&&(outSize==0)) + _dec_onDecErr_rt();//error; + }else{ + _dec_onDecErr_rt();//error; + } + self->decReadPos+=inSize; + out_cur+=outSize; + } + return hpatch_TRUE; + } + static hpatch_TDecompress _7zXZDecompressPlugin={_7zXZ_is_can_open,_7zXZ_open, + _7zXZ_close,_7zXZ_decompress_part,_7zXZ_reset_code}; + static hpatch_TDecompress _7zXZDecompressPlugin_a={_7zXZ_is_can_open,_7zXZ_a_open, + _7zXZ_close,_7zXZ_decompress_part,_7zXZ_reset_code}; +#endif//_CompressPlugin_7zXZ + + +#if (defined(_CompressPlugin_lz4) || defined(_CompressPlugin_lz4hc)) +#if (_IsNeedIncludeDefaultCompressHead) +# include "lz4.h" // "lz4/lib/lz4.h" https://github.com/lz4/lz4 +#endif + typedef struct _lz4_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + LZ4_streamDecode_t *s; + int kLz4CompressBufSize; + int code_buf_size; + int data_begin; + int data_end; + hpatch_dec_error_t decError; + unsigned char buf[1]; + } _lz4_TDecompress; + static hpatch_BOOL _lz4_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"lz4")); + } + #define _lz4_read_len4(len,in_code,code_begin,code_end,__dec_err_rt) { \ + unsigned char _temp_buf4[4]; \ + if (4>code_end-code_begin) __dec_err_rt(); \ + if (!in_code->read(in_code,code_begin,_temp_buf4,_temp_buf4+4)) \ + return hpatch_FALSE; \ + len=_temp_buf4[0]|(_temp_buf4[1]<<8)|(_temp_buf4[2]<<16)|(_temp_buf4[3]<<24); \ + code_begin+=4; \ + } + static hpatch_decompressHandle _lz4_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + const int kMaxLz4CompressBufSize=(1<<20)*64; //defence attack + _lz4_TDecompress* self=0; + int kLz4CompressBufSize=0; + int code_buf_size=0; + assert(code_begin=kMaxLz4CompressBufSize)) _dec_openErr_rt(); + code_buf_size=LZ4_compressBound(kLz4CompressBufSize); + } + self=(_lz4_TDecompress*)_dec_malloc(sizeof(_lz4_TDecompress)+kLz4CompressBufSize+code_buf_size); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_lz4_TDecompress)); + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->kLz4CompressBufSize=kLz4CompressBufSize; + self->code_buf_size=code_buf_size; + self->data_begin=0; + self->data_end=0; + + self->s = LZ4_createStreamDecode(); + if (!self->s){ free(self); _dec_openErr_rt(); } + return self; + } + static hpatch_BOOL _lz4_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + hpatch_BOOL result=hpatch_TRUE; + _lz4_TDecompress* self=(_lz4_TDecompress*)decompressHandle; + if (!self) return result; + _dec_onDecErr_up(); + _dec_close_check(0==LZ4_freeStreamDecode(self->s)); + free(self); + return result; + } + static hpatch_BOOL _lz4_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _lz4_TDecompress* self=(_lz4_TDecompress*)decompressHandle; + unsigned char* data_buf=self->buf; + unsigned char* code_buf=self->buf+self->kLz4CompressBufSize; + + while (out_part_datadata_end-self->data_begin; + if (dataLen>0){ + if (dataLen>(out_part_data_end-out_part_data)) + dataLen=(out_part_data_end-out_part_data); + memcpy(out_part_data,data_buf+self->data_begin,dataLen); + out_part_data+=(size_t)dataLen; + self->data_begin+=dataLen; + }else{ + int codeLen; + _lz4_read_len4(codeLen,self->codeStream,self->code_begin,self->code_end,_dec_onDecErr_rt); + if ((codeLen<=0)||(codeLen>self->code_buf_size) + ||((size_t)codeLen>(self->code_end-self->code_begin))) _dec_onDecErr_rt(); + if (!self->codeStream->read(self->codeStream,self->code_begin, + code_buf,code_buf+codeLen)) return hpatch_FALSE; + self->code_begin+=codeLen; + self->data_begin=0; + self->data_end=LZ4_decompress_safe_continue(self->s,(const char*)code_buf,(char*)data_buf, + codeLen,self->kLz4CompressBufSize); + if (self->data_end<=0) _dec_onDecErr_rt(); + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress lz4DecompressPlugin={_lz4_is_can_open,_lz4_open, + _lz4_close,_lz4_decompress_part}; +#endif//_CompressPlugin_lz4 or _CompressPlugin_lz4hc + +#ifdef _CompressPlugin_zstd +#if (_IsNeedIncludeDefaultCompressHead) +//# define ZSTD_STATIC_LINKING_ONLY //for ZSTD_customMem +# include "zstd.h" // "zstd/lib/zstd.h" https://github.com/sisong/zstd +#endif + typedef struct _zstd_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + ZSTD_inBuffer s_input; + ZSTD_outBuffer s_output; + size_t data_begin; + ZSTD_DStream* s; + hpatch_dec_error_t decError; + unsigned char buf[1]; + } _zstd_TDecompress; + #ifdef ZSTD_STATIC_LINKING_ONLY + static void* __ZSTD_alloc(void* opaque, size_t size) + __dec_Alloc_fun(_zstd_TDecompress,opaque,size) + #endif + static hpatch_BOOL _zstd_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"zstd")); + } + static hpatch_decompressHandle _zstd_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + _zstd_TDecompress* self=0; + size_t ret; + size_t _input_size=ZSTD_DStreamInSize(); + size_t _output_size=ZSTD_DStreamOutSize(); + self=(_zstd_TDecompress*)_dec_malloc(sizeof(_zstd_TDecompress)+_input_size+_output_size); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_zstd_TDecompress)); + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->s_input.src=self->buf; + self->s_input.size=_input_size; + self->s_input.pos=_input_size; + self->s_output.dst=self->buf+_input_size; + self->s_output.size=_output_size; + self->s_output.pos=0; + self->data_begin=0; + #ifdef ZSTD_STATIC_LINKING_ONLY + { + ZSTD_customMem customMem={__ZSTD_alloc,__dec_free,self}; + self->s=ZSTD_createDStream_advanced(customMem); + } + #else + self->s=ZSTD_createDStream(); + #endif + if (!self->s){ _dec_onDecErr_up(); free(self); _dec_openErr_rt(); } + ret=ZSTD_initDStream(self->s); + if (ZSTD_isError(ret)) { ZSTD_freeDStream(self->s); _dec_onDecErr_up(); free(self); _dec_openErr_rt(); } + #define _ZSTD_WINDOWLOG_MAX 30 + ret=ZSTD_DCtx_setParameter(self->s,ZSTD_d_windowLogMax,_ZSTD_WINDOWLOG_MAX); + //if (ZSTD_isError(ret)) { printf("WARNING: ZSTD_DCtx_setMaxWindowSize() error!"); } + return self; + } + static hpatch_BOOL _zstd_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + hpatch_BOOL result=hpatch_TRUE; + _zstd_TDecompress* self=(_zstd_TDecompress*)decompressHandle; + if (!self) return result; + _dec_onDecErr_up(); + _dec_close_check(0==ZSTD_freeDStream(self->s)); + free(self); + return result; + } + static hpatch_BOOL _zstd_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _zstd_TDecompress* self=(_zstd_TDecompress*)decompressHandle; + while (out_part_datas_output.pos-self->data_begin); + if (dataLen>0){ + if (dataLen>(size_t)(out_part_data_end-out_part_data)) + dataLen=(out_part_data_end-out_part_data); + memcpy(out_part_data,(const unsigned char*)self->s_output.dst+self->data_begin,dataLen); + out_part_data+=dataLen; + self->data_begin+=dataLen; + }else{ + size_t ret; + if (self->s_input.pos==self->s_input.size) { + self->s_input.pos=0; + if (self->s_input.size>self->code_end-self->code_begin) + self->s_input.size=(size_t)(self->code_end-self->code_begin); + + if (self->s_input.size>0){ + if (!self->codeStream->read(self->codeStream,self->code_begin,(unsigned char*)self->s_input.src, + (unsigned char*)self->s_input.src+self->s_input.size)) + return hpatch_FALSE; + self->code_begin+=self->s_input.size; + } + } + self->s_output.pos=0; + self->data_begin=0; + ret=ZSTD_decompressStream(self->s,&self->s_output,&self->s_input); + if (ZSTD_isError(ret)) _dec_onDecErr_rt(); + if (self->s_output.pos==self->data_begin) _dec_onDecErr_rt(); + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress zstdDecompressPlugin={_zstd_is_can_open,_zstd_open, + _zstd_close,_zstd_decompress_part}; +#endif//_CompressPlugin_zstd + + +#ifdef _CompressPlugin_brotli +#if (_IsNeedIncludeDefaultCompressHead) +# include "brotli/decode.h" // "brotli/c/include/brotli/decode.h" https://github.com/google/brotli +#endif + typedef struct _brotli_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + unsigned char* input; + unsigned char* output; + size_t available_in; + size_t available_out; + const unsigned char* next_in; + unsigned char* next_out; + unsigned char* data_begin; + BrotliDecoderState* s; + hpatch_dec_error_t decError; + unsigned char buf[1]; + } _brotli_TDecompress; + static hpatch_BOOL _brotli_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"brotli")); + } + static hpatch_decompressHandle _brotli_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + const size_t kBufSize=kDecompressBufSize; + _brotli_TDecompress* self=0; + assert(code_begincodeStream=codeStream; + self->code_begin=code_begin; + self->input=self->buf; + self->output=self->buf+kBufSize; + self->code_end=code_end; + self->available_in = 0; + self->next_in = 0; + self->available_out = (self->output-self->input); + self->next_out =self->output; + self->data_begin=self->output; + + self->s = BrotliDecoderCreateInstance(0,0,0); + if (!self->s){ free(self); _dec_openErr_rt(); } + if (!BrotliDecoderSetParameter(self->s, BROTLI_DECODER_PARAM_LARGE_WINDOW, 1u)) + { BrotliDecoderDestroyInstance(self->s); free(self); _dec_openErr_rt(); } + return self; + } + static hpatch_BOOL _brotli_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _brotli_TDecompress* self=(_brotli_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + _dec_onDecErr_up(); + BrotliDecoderDestroyInstance(self->s); + free(self); + return hpatch_TRUE; + } + static hpatch_BOOL _brotli_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _brotli_TDecompress* self=(_brotli_TDecompress*)decompressHandle; + while (out_part_datanext_out-self->data_begin); + if (dataLen>0){ + if (dataLen>(size_t)(out_part_data_end-out_part_data)) + dataLen=(out_part_data_end-out_part_data); + memcpy(out_part_data,self->data_begin,dataLen); + out_part_data+=dataLen; + self->data_begin+=dataLen; + }else{ + BrotliDecoderResult ret; + if (self->available_in==0) { + self->available_in=(self->output-self->input); + if (self->available_in>self->code_end-self->code_begin) + self->available_in=(size_t)(self->code_end-self->code_begin); + if (self->available_in>0){ + if (!self->codeStream->read(self->codeStream,self->code_begin,(unsigned char*)self->input, + self->input+self->available_in)) + return hpatch_FALSE; + self->code_begin+=self->available_in; + } + self->next_in=self->input; + } + self->available_out = (self->output-self->input); + self->next_out =self->output; + self->data_begin=self->output; + ret=BrotliDecoderDecompressStream(self->s,&self->available_in,&self->next_in, + &self->available_out,&self->next_out, 0); + switch (ret){ + case BROTLI_DECODER_RESULT_SUCCESS: + case BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT: { + if (self->next_out==self->data_begin) _dec_onDecErr_rt(); + } break; + case BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT: { + if (self->code_end==self->code_begin) _dec_onDecErr_rt(); + } break; + default: + _dec_onDecErr_rt(); + } + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress brotliDecompressPlugin={_brotli_is_can_open,_brotli_open, + _brotli_close,_brotli_decompress_part}; +#endif//_CompressPlugin_brotli + + +#ifdef _CompressPlugin_lzham +#if (_IsNeedIncludeDefaultCompressHead) +# include "lzham.h" // "lzham_codec/include/lzham.h" https://github.com/richgel999/lzham_codec +#endif + typedef struct _lzham_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + + unsigned char* input; + unsigned char* output; + size_t available_in; + size_t available_out; + const unsigned char* next_in; + unsigned char* next_out; + unsigned char* data_begin; + lzham_decompress_state_ptr s; + hpatch_dec_error_t decError; + unsigned char buf[1]; + } _lzham_TDecompress; + static hpatch_BOOL _lzham_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"lzham")); + } + static hpatch_decompressHandle _lzham_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + const size_t kBufSize=kDecompressBufSize; + lzham_decompress_params params; + unsigned char dict_bits; + _lzham_TDecompress* self=0; + assert(code_beginread(codeStream,code_begin,&dict_bits,(&dict_bits)+1)) + return 0; + ++code_begin; + } + + self=(_lzham_TDecompress*)_dec_malloc(sizeof(_lzham_TDecompress)+kBufSize*2); + if (!self) _dec_memErr_rt(); + memset(self,0,sizeof(_lzham_TDecompress)); + self->codeStream=codeStream; + self->code_begin=code_begin; + self->input=self->buf; + self->output=self->buf+kBufSize; + self->code_end=code_end; + self->available_in = 0; + self->next_in = 0; + self->available_out = (self->output-self->input); + self->next_out =self->output; + self->data_begin=self->output; + + memset(¶ms, 0, sizeof(params)); + params.m_struct_size = sizeof(params); + params.m_dict_size_log2 = dict_bits; + + self->s = lzham_decompress_init(¶ms); + if (!self->s){ free(self); _dec_openErr_rt(); } + + return self; + } + static hpatch_BOOL _lzham_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _lzham_TDecompress* self=(_lzham_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + _dec_onDecErr_up(); + lzham_decompress_deinit(self->s); + free(self); + return hpatch_TRUE; + } + static hpatch_BOOL _lzham_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + _lzham_TDecompress* self=(_lzham_TDecompress*)decompressHandle; + while (out_part_datanext_out-self->data_begin); + if (dataLen>0){ + if (dataLen>(size_t)(out_part_data_end-out_part_data)) + dataLen=(out_part_data_end-out_part_data); + memcpy(out_part_data,self->data_begin,dataLen); + out_part_data+=dataLen; + self->data_begin+=dataLen; + }else{ + lzham_decompress_status_t ret; + if (self->available_in==0) { + self->available_in=(self->output-self->input); + if (self->available_in>self->code_end-self->code_begin) + self->available_in=(size_t)(self->code_end-self->code_begin); + if (self->available_in>0){ + if (!self->codeStream->read(self->codeStream,self->code_begin,(unsigned char*)self->input, + self->input+self->available_in)) + return hpatch_FALSE; + self->code_begin+=self->available_in; + } + self->next_in=self->input; + } + { + size_t available_in_back=self->available_in; + self->available_out = (self->output-self->input); + ret=lzham_decompress(self->s,self->next_in,&self->available_in, + self->output,&self->available_out,(self->code_begin==self->code_end)); + self->next_out=self->output+self->available_out; + self->next_in+=self->available_in; + self->available_in=available_in_back-self->available_in; + self->available_out=(self->output-self->input) - self->available_out; + self->data_begin=self->output; + } + switch (ret){ + case LZHAM_DECOMP_STATUS_SUCCESS: + case LZHAM_DECOMP_STATUS_HAS_MORE_OUTPUT: + case LZHAM_DECOMP_STATUS_NOT_FINISHED: { + if (self->next_out==self->data_begin) _dec_onDecErr_rt(); + } break; + case LZHAM_DECOMP_STATUS_NEEDS_MORE_INPUT: { + if (self->code_end==self->code_begin) _dec_onDecErr_rt(); + } break; + default: + _dec_onDecErr_rt(); + } + } + } + return hpatch_TRUE; + } + static hpatch_TDecompress lzhamDecompressPlugin={_lzham_is_can_open,_lzham_open, + _lzham_close,_lzham_decompress_part}; +#endif//_CompressPlugin_lzham + + +#ifdef _CompressPlugin_tuz +#if (_IsNeedIncludeDefaultCompressHead) +# include "tuz_dec.h" // "tinyuz/decompress/tuz_dec.h" https://github.com/sisong/tinyuz +#endif + typedef struct _tuz_TDecompress{ + const struct hpatch_TStreamInput* codeStream; + hpatch_StreamPos_t code_begin; + hpatch_StreamPos_t code_end; + tuz_byte* dec_mem; + tuz_TStream s; + hpatch_dec_error_t decError; + } _tuz_TDecompress; + + static tuz_BOOL _tuz_TDecompress_read_code(tuz_TInputStreamHandle listener, + tuz_byte* out_code,tuz_size_t* code_size){ + _tuz_TDecompress* self=(_tuz_TDecompress*)listener; + tuz_size_t r_size=*code_size; + hpatch_StreamPos_t s_size=self->code_end-self->code_begin; + if (r_size>s_size){ + r_size=(tuz_size_t)s_size; + *code_size=r_size; + } + if (!self->codeStream->read(self->codeStream,self->code_begin, + out_code,out_code+r_size)) return tuz_FALSE; + self->code_begin+=r_size; + return tuz_TRUE; + } + + static hpatch_BOOL _tuz_is_can_open(const char* compressType){ + return (0==strcmp(compressType,"tuz")); + } + static hpatch_decompressHandle _tuz_open(hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end){ + tuz_size_t dictSize; + _tuz_TDecompress* self=0; + self=(_tuz_TDecompress*)_dec_malloc(sizeof(_tuz_TDecompress)); + if (!self) _dec_memErr_rt(); + self->dec_mem=0; + self->codeStream=codeStream; + self->code_begin=code_begin; + self->code_end=code_end; + self->decError=hpatch_dec_ok; + dictSize=tuz_TStream_read_dict_size(self,_tuz_TDecompress_read_code); + if (((tuz_size_t)(dictSize-1))>=tuz_kMaxOfDictSize) { free(self); _dec_openErr_rt(); } + self->dec_mem=(tuz_byte*)_dec_malloc(dictSize+kDecompressBufSize); + if (self->dec_mem==0){ free(self); _dec_memErr_rt(); } + if (tuz_OK!=tuz_TStream_open(&self->s,self,_tuz_TDecompress_read_code, + self->dec_mem,dictSize,kDecompressBufSize)){ + free(self->dec_mem); free(self); _dec_openErr_rt(); } + return self; + } + static hpatch_BOOL _tuz_close(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle){ + _tuz_TDecompress* self=(_tuz_TDecompress*)decompressHandle; + if (!self) return hpatch_TRUE; + _dec_onDecErr_up(); + if (self->dec_mem) free(self->dec_mem); + free(self); + return hpatch_TRUE; + } + static hpatch_BOOL _tuz_decompress_part(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end){ + tuz_TResult ret; + _tuz_TDecompress* self=(_tuz_TDecompress*)decompressHandle; + size_t out_size=out_part_data_end-out_part_data; + tuz_size_t data_size=(tuz_size_t)out_size; + assert(data_size==out_size); + ret=tuz_TStream_decompress_partial(&self->s,out_part_data,&data_size); + if (!((ret<=tuz_STREAM_END)&&(data_size==out_size))) + _dec_onDecErr_rt(); + return hpatch_TRUE; + } + static hpatch_TDecompress tuzDecompressPlugin={_tuz_is_can_open,_tuz_open, + _tuz_close,_tuz_decompress_part}; +#endif//_CompressPlugin_tuz + +#endif diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/dirDiffPatch/dir_patch/dir_patch_types.h b/android/app/src/main/cpp/third_party/hdiffpatch/dirDiffPatch/dir_patch/dir_patch_types.h new file mode 100644 index 00000000..aafe93dd --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/dirDiffPatch/dir_patch/dir_patch_types.h @@ -0,0 +1,68 @@ +// dir_patch_types.h +// dir patch +// +/* + The MIT License (MIT) + Copyright (c) 2018-2019 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef DirPatch_dir_patch_types_h +#define DirPatch_dir_patch_types_h +#include "../../libHDiffPatch/HPatch/patch_types.h" +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _IS_NEED_DIR_DIFF_PATCH +# define _IS_NEED_DIR_DIFF_PATCH 1 +#endif +#ifndef _IS_NEED_SINGLE_STREAM_DIFF +# define _IS_NEED_SINGLE_STREAM_DIFF 1 +#endif +#ifndef _IS_NEED_WINDOW_DIFF +# define _IS_NEED_WINDOW_DIFF 1 +#endif + + +#if (_IS_NEED_DIR_DIFF_PATCH) +# define kMaxOpenFileNumber_limit_min 5 +# define kMaxOpenFileNumber_default_min 16 //must >= limit_min +# define kMaxOpenFileNumber_default_diff 64 +# define kMaxOpenFileNumber_default_patch 24 +#endif + +#ifdef _WIN32 +#define kPatch_dirSeparator '\\' +#else +#define kPatch_dirSeparator '/' +#endif +#define kPatch_dirSeparator_saved '/' + +static hpatch_inline //align upper +hpatch_StreamPos_t toAlignRangeSize(hpatch_StreamPos_t rangeSize,size_t kAlignSize) + { return (rangeSize+kAlignSize-1)/kAlignSize*kAlignSize; } + +#ifdef __cplusplus +} +#endif +#endif //DirPatch_dir_patch_types_h diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.c b/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.c new file mode 100644 index 00000000..a9501567 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.c @@ -0,0 +1,752 @@ +//file_for_patch.c +// patch demo file tool +// +/* + This is the HDiffPatch copyright. + + Copyright (c) 2012-2019 HouSisong All Rights Reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#define _LARGEFILE64_SOURCE +#define _FILE_OFFSET_BITS 64 +#include "file_for_patch.h" +#include // setlocale +#include //stat mkdir +#ifdef _WIN32 +# include //for file API, character encoding API +#endif +#ifndef _IS_FOR_WINXP +# ifdef _USING_V110_SDK71_ +# define _IS_FOR_WINXP 1 +# else +# define _IS_FOR_WINXP 0 +# endif +#endif +# ifdef _MSC_VER +# include //_chsize_s +# include // *mkdir *rmdir +# else +# include // rmdir close ftruncate +# endif + + +#if (_IS_NEED_BLOCK_DEV) +#include +#include +#include // ioctl +#include //BLKGETSIZE64 + +static hpatch_BOOL _get_block_dev_size(const char* blkdev,hpatch_uint64_t* bsize){ + int fd; + assert(blkdev&&bsize); + + fd=open(blkdev,O_RDONLY); + if (fd == -1){ + LOG_ERR("%s","_get_block_dev_size() open"); + return hpatch_FALSE; + } + + if (ioctl(fd,BLKGETSIZE64,bsize) == -1) { + LOG_ERR("%s","_get_block_dev_size() ioctl"); + close(fd); + return hpatch_FALSE; + } + + close(fd); + return hpatch_TRUE; +} +#endif + +#ifdef _WIN32 +#define _setFileErrNo_iconv() _set_errno_new(GetLastError()==ERROR_INSUFFICIENT_BUFFER?ENAMETOOLONG:EILSEQ) + +int _utf8FileName_to_w(const char* fileName_utf8,wchar_t* out_fileName_w,size_t out_wSize){ + int result=MultiByteToWideChar(_hpatch_kMultiBytePage,0,fileName_utf8,-1,out_fileName_w,(int)out_wSize); + if (result<=0) _setFileErrNo_iconv(); + return result; } +int _wFileName_to_utf8(const wchar_t* fileName_w,char* out_fileName_utf8,size_t out_bSize){ + int result=WideCharToMultiByte(_hpatch_kMultiBytePage,0,fileName_w,-1,out_fileName_utf8,(int)out_bSize,0,0); + if (result<=0) _setFileErrNo_iconv(); + return result; } + +hpatch_BOOL _wFileNames_to_utf8(const wchar_t** fileNames_w,size_t fileCount, + char** out_fileNames_utf8,size_t out_byteSize){ + char* _bufEnd=((char*)out_fileNames_utf8)+out_byteSize; + char* _bufCur=(char*)(&out_fileNames_utf8[fileCount]); + size_t i; + for (i=0; i=_bufEnd) { _set_errno_new(ENAMETOOLONG); return hpatch_FALSE; } //error + csize=_wFileName_to_utf8(fileNames_w[i],_bufCur,_bufEnd-_bufCur); + if (csize<=0) return hpatch_FALSE; //error + out_fileNames_utf8[i]=_bufCur; + _bufCur+=csize; + } + return hpatch_TRUE; +} +#endif + +#if (_IS_USED_WIN32_UTF8_WAPI) +void SetDefaultStringLocale(){ + setlocale(LC_CTYPE,""); +} +#endif + +int hpatch_printPath_utf8(const char* pathTxt_utf8){ +#if (_IS_USED_WIN32_UTF8_WAPI) + wchar_t pathTxt_w[hpatch_kPathMaxSize]; + int wsize=_utf8FileName_to_w(pathTxt_utf8,pathTxt_w,hpatch_kPathMaxSize); + if (wsize>0) + return printf("%ls",pathTxt_w); + else //view unknow + return printf("%s",pathTxt_utf8); +#else + return printf("%s",pathTxt_utf8); +#endif +} + +#if (_IS_USED_WIN32_UTF8_WAPI) +int _hpatch_printStdErrPath_wstr(const wchar_t* pathTxt_wchar){ + return LOG_ERR("%ls",pathTxt_wchar); +} +#endif + +int hpatch_printStdErrPath_utf8(const char* pathTxt_utf8){ +#if (_IS_USED_WIN32_UTF8_WAPI) + wchar_t pathTxt_w[hpatch_kPathMaxSize]; + int wsize=_utf8FileName_to_w(pathTxt_utf8,pathTxt_w,hpatch_kPathMaxSize); + if (wsize>0) + return _hpatch_printStdErrPath_wstr(pathTxt_w); + else //view unknow + return LOG_ERR("%s",pathTxt_utf8); +#else + return LOG_ERR("%s",pathTxt_utf8); +#endif +} + +#if (_IS_FOR_WINXP) + #define _getStatByAttributes(_path,s,rt,_getFileAttributesFunc) { \ + WIN32_FILE_ATTRIBUTE_DATA fad={0}; \ + BOOL ret=_getFileAttributesFunc(_path,GetFileExInfoStandard,&fad); \ + if (!ret) { \ + _set_errno_new(ENOENT); \ + rt=-1; \ + }else{ \ + s.st_mode=(fad.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)?S_IFDIR:S_IFREG; \ + s.st_size=fad.nFileSizeLow | (((hpatch_StreamPos_t)fad.nFileSizeHigh)<<32); \ + rt=0; \ + } \ + } +#endif + +hpatch_BOOL _hpatch_getPathStat_noEndDirSeparator(const char* path_utf8,hpatch_TPathType* out_type, + hpatch_StreamPos_t* out_fileSize,size_t* out_st_mode){ +#if (_IS_USED_WIN32_UTF8_WAPI) + int wsize; + wchar_t path_w[hpatch_kPathMaxSize]; + struct _stat64 s; +#else +# ifdef _MSC_VER + struct _stat64 s; +# else + struct stat s; +# endif +#endif + + int rt; + assert(out_type!=0); + memset(&s,0,sizeof(s)); +#if (_IS_USED_WIN32_UTF8_WAPI) + wsize=_utf8FileName_to_w(path_utf8,path_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + #if (_IS_FOR_WINXP) + _getStatByAttributes(path_w,s,rt,GetFileAttributesExW); + #else + rt = _wstat64(path_w,&s); + #endif +#else +# ifdef _MSC_VER + #if (_IS_FOR_WINXP) + _getStatByAttributes(path_utf8,s,rt,GetFileAttributesExA); + #else + rt = _stat64(path_utf8,&s); + #endif +# else + rt = stat(path_utf8,&s); +# endif +#endif + if (out_st_mode) *out_st_mode=s.st_mode; + + if(rt!=0){ + if (errno==ENOENT){ + *out_type=kPathType_notExist; + return hpatch_TRUE; + } + return hpatch_FALSE; //error + }else if ((s.st_mode&S_IFMT)==S_IFREG){ + *out_type=kPathType_file; + if (out_fileSize) *out_fileSize=s.st_size; + return hpatch_TRUE; + }else if ((s.st_mode&S_IFMT)==S_IFDIR){ + *out_type=kPathType_dir; + if (out_fileSize) *out_fileSize=0; + return hpatch_TRUE; +#if (_IS_NEED_BLOCK_DEV) + }else if ((s.st_mode&S_IFMT)==S_IFBLK){ + hpatch_uint64_t bsize=0; + if (!_get_block_dev_size(path_utf8, &bsize)) + return hpatch_FALSE; + *out_type=kPathType_file; + if (out_fileSize) *out_fileSize=bsize; + return hpatch_TRUE; +#endif + }else{ + return hpatch_FALSE; //as error; unknow how to dispose + } +} + + +hpatch_BOOL hpatch_getTempPathName(const char* path_utf8,char* out_tempPath_utf8,char* out_tempPath_end){ + //use tmpnam()? +#define _AddingLen 8 + size_t i; + size_t len=strlen(path_utf8); + if ((len>0)&&(path_utf8[len-1]==kPatch_dirSeparator)) --len; //without '/' + if (len+(4+_AddingLen)>(size_t)(out_tempPath_end-out_tempPath_utf8)) { _set_errno_new(ENAMETOOLONG); return hpatch_FALSE; } + memcpy(out_tempPath_utf8,path_utf8,len); + out_tempPath_utf8+=len; + for (i=1; i<1000; ++i) { + hpatch_TPathType tmpPathType; + char adding[_AddingLen]={'0','0','0','.','t','m','p','\0'}; + adding[2]+=i%10; adding[1]+=(i/10)%10; adding[0]+=(i/100)%10; + memcpy(out_tempPath_utf8,adding,sizeof(adding)); + if (!_hpatch_getPathStat_noEndDirSeparator(out_tempPath_utf8,&tmpPathType,0,0)) return hpatch_FALSE; + if (tmpPathType==kPathType_notExist) + return hpatch_TRUE; //ok + } + return hpatch_FALSE; +#undef _AddingLen +} + + +hpatch_BOOL hpatch_renamePath(const char* oldPath_utf8,const char* newPath_utf8){ + _path_noEndDirSeparator(oldPath,oldPath_utf8); { + _path_noEndDirSeparator(newPath,newPath_utf8); { +#if (_IS_USED_WIN32_UTF8_WAPI) + int wsize; + wchar_t oldPath_w[hpatch_kPathMaxSize]; + wchar_t newPath_w[hpatch_kPathMaxSize]; + wsize=_utf8FileName_to_w(oldPath,oldPath_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + wsize=_utf8FileName_to_w(newPath,newPath_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + return 0==_wrename(oldPath_w,newPath_w); +#else + return 0==rename(oldPath,newPath); +#endif + } + } +} + +hpatch_BOOL hpatch_removeFile(const char* fileName_utf8){ + _path_noEndDirSeparator(fileName,fileName_utf8);{ +#if (_IS_USED_WIN32_UTF8_WAPI) + int wsize; + wchar_t path_w[hpatch_kPathMaxSize]; + wsize=_utf8FileName_to_w(fileName,path_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + return 0==_wremove(path_w); +#else + return 0==remove(fileName); +#endif + } +} + +#if (_IS_NEED_DIR_DIFF_PATCH) + +hpatch_BOOL hpatch_removeDir(const char* dirName_utf8){ + _path_noEndDirSeparator(dirName,dirName_utf8);{ +#if (_IS_USED_WIN32_UTF8_WAPI) + int wsize; + wchar_t path_w[hpatch_kPathMaxSize]; + wsize=_utf8FileName_to_w(dirName,path_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + return 0==_wrmdir(path_w); +#else +# ifdef _MSC_VER + return 0==_rmdir(dirName); +# else + return 0==rmdir(dirName); +# endif +#endif + } +} + +hpatch_BOOL hpatch_moveFile(const char* oldPath_utf8,const char* newPath_utf8){ + return hpatch_renamePath(oldPath_utf8,newPath_utf8); +} + +hpatch_BOOL hpatch_makeNewDir(const char* dirName_utf8){ + hpatch_TPathType type; + _path_noEndDirSeparator(path,dirName_utf8); + if (!_hpatch_getPathStat_noEndDirSeparator(path,&type,0,0)) return hpatch_FALSE; //error + switch (type) { + case kPathType_dir :{ return hpatch_TRUE; } break; //exist + case kPathType_file:{ _set_errno_new(EPERM); return hpatch_FALSE; } break; //error, not overwite + case kPathType_notExist:{ +#if (_IS_USED_WIN32_UTF8_WAPI) + int wsize; + wchar_t path_w[hpatch_kPathMaxSize]; + wsize=_utf8FileName_to_w(path,path_w,hpatch_kPathMaxSize); + if (wsize<=0) return hpatch_FALSE; + return 0==_wmkdir(path_w); +#else +# ifdef _MSC_VER + return 0==_mkdir(path); +# else +# if defined(__MINGW32__) && defined(_WIN32) + return 0==mkdir(path); +# else + const mode_t kDefalutMode=S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;//0755 + return 0==mkdir(path,kDefalutMode); +# endif +# endif +#endif + } break; + } + return hpatch_FALSE; +} + +#endif //_IS_NEED_DIR_DIFF_PATCH + +hpatch_BOOL hpatch_getIsExecuteFile(const char* fileName){ +#ifdef _WIN32 + return hpatch_FALSE; // now, not need execute info +#else + hpatch_TPathType type; + size_t st_mode=0; + if (!_hpatch_getPathStat_noEndDirSeparator(fileName,&type,0,&st_mode)) return hpatch_FALSE; + if (type!=kPathType_file) return hpatch_FALSE;// now, not need execute info + return (((mode_t)st_mode&S_IXUSR)!=0); +#endif +} + +hpatch_BOOL hpatch_setIsExecuteFile(const char* fileName){ +#ifdef _WIN32 + return hpatch_TRUE; // now, not need execute info +#else + hpatch_TPathType type; + size_t st_mode=0; + if (!_hpatch_getPathStat_noEndDirSeparator(fileName,&type,0,&st_mode)) return hpatch_FALSE; + return 0==chmod(fileName,(mode_t)st_mode|S_IXUSR|S_IXGRP|S_IXOTH); +#endif +} + +hpatch_BOOL hpatch_getIsAtty(hpatch_FileHandle fileHandle){ +#ifdef _WIN32 + int fno=_fileno(fileHandle); + if (fno==-1) return hpatch_FALSE; + return (_isatty(fno)!=0); +#else + int fno=fileno(fileHandle); + if (fno==-1) return hpatch_FALSE; + return (isatty(fno)!=0); +#endif +} + +#if defined(ANDROID) && (__ANDROID_API__ < 24) +#include +#include +static off64_t _import_lseek64(hpatch_FileHandle file,hpatch_StreamPos_t seekPos,int whence){ + int fd; + if (feof(file)){ + if (whence==SEEK_CUR) + return -1; + rewind(file); + } + setbuf(file,NULL); + fd = fileno(file); + if (fd<0) return -1; + return lseek64(fd,seekPos,whence); +} +#endif + +hpatch_inline static +hpatch_BOOL _import_fileSeek64(hpatch_FileHandle file,hpatch_StreamPos_t seekPos,int whence){ +#ifdef _MSC_VER + return _fseeki64(file,seekPos,whence)==0; +#else +# ifdef ANDROID +# if __ANDROID_API__ >= 24 + return fseeko64(file,seekPos,whence)==0; +# else + if ((((off64_t)seekPos)==((long)seekPos))&&(whence==SEEK_SET)) return fseek(file,(long)seekPos,whence)==0; + else return _import_lseek64(file,seekPos,whence)>=0; +# endif +# else + return fseeko(file,seekPos,whence)==0; +# endif +#endif +} + +hpatch_inline static +hpatch_BOOL _import_fileSeek64To(hpatch_FileHandle file,hpatch_StreamPos_t seekPos){ + return _import_fileSeek64(file,seekPos,SEEK_SET); +} + +hpatch_inline static +hpatch_BOOL _import_fileTell64(hpatch_FileHandle file,hpatch_StreamPos_t* outPos){ +#ifdef _MSC_VER + __int64 pos=_ftelli64(file); +#else +# ifdef ANDROID +# if __ANDROID_API__ >= 24 + off64_t pos=ftello64(file); +# else + off64_t pos=_import_lseek64(file,0,SEEK_CUR); +# endif +# else + off_t pos=ftello(file); +# endif +#endif + *outPos=(hpatch_StreamPos_t)pos; + return (pos>=0); +} + +hpatch_inline static +hpatch_BOOL _import_fileClose(hpatch_FileHandle* pfile){ + hpatch_FileHandle file=*pfile; + if (file){ + *pfile=0; + if (0!=fclose(file)) + return hpatch_FALSE; + } + return hpatch_TRUE; +} +#if (_HPATCH_IS_USED_errno) + hpatch_inline static + hpatch_BOOL _import_fileClose_No_errno(hpatch_FileHandle* pfile){ + int err=errno; + hpatch_BOOL result=_import_fileClose(pfile); + if (err) _set_errno_new(err); + return result; + } +#else +# define _import_fileClose_No_errno(pfile) _import_fileClose(pfile) +#endif + +hpatch_BOOL _import_fileRead(hpatch_FileHandle file,TByte* buf,TByte* buf_end){ + while (bufhpatch_kFileIOBestMaxSize) readLen=hpatch_kFileIOBestMaxSize; + if (readLen!=fread(buf,1,readLen,file)) return hpatch_FALSE; + buf+=readLen; + } + return buf==buf_end; +} + +hpatch_BOOL _import_fileWrite(hpatch_FileHandle file,const TByte* data,const TByte* data_end){ + while (datahpatch_kFileIOBestMaxSize) writeLen=hpatch_kFileIOBestMaxSize; + if (writeLen!=fwrite(data,1,writeLen,file)) return hpatch_FALSE; + data+=writeLen; + } + return data==data_end; +} + +hpatch_inline static +hpatch_BOOL _import_fileFlush(hpatch_FileHandle writedFile){ + return (0==fflush(writedFile)); +} + +hpatch_BOOL _import_fileTruncate(hpatch_FileHandle file,hpatch_StreamPos_t new_file_length){ +#ifdef _MSC_VER + int fno=_fileno(file); + if (fno==-1) return hpatch_FALSE; + if (_chsize_s(fno,new_file_length)!=0) return hpatch_FALSE; +#else + int fno=fileno(file); + if (fno==-1) return hpatch_FALSE; + if (ftruncate(fno,new_file_length)!=0) return hpatch_FALSE; +#endif + return hpatch_TRUE; +} + +#if (_IS_USED_WIN32_UTF8_WAPI) +# define _FileModeType const wchar_t* +# define _kFileReadMode L"rb" +# define _kFileWriteMode L"wb+" +# define _kFileReadWriteMode L"rb+" +#else +# define _FileModeType const char* +# define _kFileReadMode "rb" +# define _kFileWriteMode "wb+" +# define _kFileReadWriteMode "rb+" +#endif + +#if (_IS_USED_WIN32_UTF8_WAPI) +hpatch_force_inline static +hpatch_FileHandle __import_fileOpen(const char* fileName_utf8,_FileModeType mode_w){ + wchar_t fileName_w[hpatch_kPathMaxSize]; + int wsize=_utf8FileName_to_w(fileName_utf8,fileName_w,hpatch_kPathMaxSize); + if (wsize>0) + return _wfsopen(fileName_w,mode_w,_SH_DENYNO); + else + return 0; +} +#else +hpatch_force_inline static +hpatch_FileHandle __import_fileOpen(const char* fileName_utf8,_FileModeType mode){ + return fopen(fileName_utf8,mode); } +#endif + +hpatch_force_inline static +hpatch_FileHandle _import_fileOpen(const char* fileName_utf8,_FileModeType mode){ + hpatch_FileHandle result=__import_fileOpen(fileName_utf8,mode); + if (result==0){ + #if (_IS_USED_WIN32_UTF8_WAPI) + LOG_ERR("fopen fail, errno:%d, openmode:\"",errno); + _hpatch_printStdErrPath_wstr(mode); + LOG_ERR("\", filename:\""); + hpatch_printStdErrPath_utf8(fileName_utf8); + LOG_ERR("\"\n"); + #else + LOG_ERR("fopen fail, errno:%d, openmode:\"%s\", filename:\"%s\"\n",errno,mode,fileName_utf8); + #endif + } + //used default vbuf? if (result) setvbuf(result,0,_IONBF,0); + return result; +} + +static hpatch_FileHandle _import_fileOpenWithSize(const char* fileName_utf8,_FileModeType mode,hpatch_StreamPos_t* out_fileSize){ + hpatch_FileHandle file=0; + file=_import_fileOpen(fileName_utf8,mode); + if ((out_fileSize==0)||(file==0)) return file; + + if (_import_fileSeek64(file,0,SEEK_END)){ + if (_import_fileTell64(file,out_fileSize)){ + if (_import_fileSeek64(file,0,SEEK_SET)){ + return file; + } + } + } + + //error clear + _import_fileClose(&file); + return 0; +} + +static hpatch_inline +hpatch_BOOL _import_fileOpenRead(const char* fileName_utf8,hpatch_FileHandle* out_fileHandle, + hpatch_StreamPos_t* out_fileSize){ + hpatch_FileHandle file=0; + assert(out_fileHandle!=0); + if (out_fileHandle==0) { _set_errno_new(EINVAL); return hpatch_FALSE; } + file=_import_fileOpenWithSize(fileName_utf8,_kFileReadMode,out_fileSize); + if (file==0) return hpatch_FALSE; + *out_fileHandle=file; + return hpatch_TRUE; +} + +static hpatch_inline +hpatch_BOOL _import_fileOpenCreateOrReWrite(const char* fileName_utf8,hpatch_FileHandle* out_fileHandle){ + hpatch_FileHandle file=0; + assert(out_fileHandle!=0); + if (out_fileHandle==0) { _set_errno_new(EINVAL); return hpatch_FALSE; } + file=_import_fileOpen(fileName_utf8,_kFileWriteMode); + if (file==0) return hpatch_FALSE; + *out_fileHandle=file; + return hpatch_TRUE; +} + +static +hpatch_BOOL _import_fileReopenWrite(const char* fileName_utf8,hpatch_FileHandle* out_fileHandle, + hpatch_StreamPos_t* out_curFileWritePos){ + hpatch_FileHandle file=0; + hpatch_StreamPos_t curFileSize=0; + assert(out_fileHandle!=0); + if (out_fileHandle==0) { _set_errno_new(EINVAL); return hpatch_FALSE; } + file=_import_fileOpenWithSize(fileName_utf8,_kFileReadWriteMode,&curFileSize); + if (file==0) return hpatch_FALSE; + if (out_curFileWritePos!=0) *out_curFileWritePos=curFileSize; + if (!_import_fileSeek64To(file,curFileSize)) + { _import_fileClose_No_errno(&file); return hpatch_FALSE; } + *out_fileHandle=file; + return hpatch_TRUE; +} + + +#define _ferr_return() { _update_ferr(self->fileError); return hpatch_FALSE; } +#define _ferr_returnv(v) { _update_ferrv(self->fileError,v); return hpatch_FALSE; } +#define _rw_ferr_return() { self->m_fpos=hpatch_kNullStreamPos; _ferr_return(); } +#define _rw_ferr_returnv(v) { self->m_fpos=hpatch_kNullStreamPos; _ferr_returnv(v); } + + static hpatch_BOOL _TFileStreamInput_read_file(const hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + TByte* out_data,TByte* out_data_end){ + size_t readLen; + hpatch_TFileStreamInput* self=(hpatch_TFileStreamInput*)stream->streamImport; + assert(out_data<=out_data_end); + readLen=(size_t)(out_data_end-out_data); + if (readLen==0) return hpatch_TRUE; + if ((readLen>self->base.streamSize) + ||(readFromPos>self->base.streamSize-readLen)) _ferr_returnv(EFBIG); + if (self->m_fpos!=readFromPos+self->m_offset){ + if (!_import_fileSeek64To(self->m_file,readFromPos+self->m_offset)) _rw_ferr_return(); + } + if (!_import_fileRead(self->m_file,out_data,out_data+readLen)) _rw_ferr_return(); + self->m_fpos=readFromPos+self->m_offset+readLen; + return hpatch_TRUE; + } + +hpatch_BOOL hpatch_TFileStreamInput_open(hpatch_TFileStreamInput* self,const char* fileName_utf8){ + assert(self->m_file==0); + self->fileError=hpatch_FALSE; + if (self->m_file) _ferr_returnv(EINVAL); + if (!_import_fileOpenRead(fileName_utf8,&self->m_file,&self->base.streamSize)) + _ferr_return(); + + self->base.streamImport=self; + self->base.read=_TFileStreamInput_read_file; + self->m_fpos=0; + self->m_offset=0; + return hpatch_TRUE; +} + +hpatch_BOOL hpatch_TFileStreamInput_setOffset(hpatch_TFileStreamInput* self,hpatch_StreamPos_t offset){ + if (self->base.streamSizem_offset+=offset; + self->base.streamSize-=offset; + return hpatch_TRUE; +} + +hpatch_BOOL hpatch_TFileStreamInput_close(hpatch_TFileStreamInput* self){ + if (!_import_fileClose(&self->m_file)) _ferr_return(); + return hpatch_TRUE; +} + + + static hpatch_BOOL _TFileStreamOutput_write_file(const hpatch_TStreamOutput* stream,hpatch_StreamPos_t writeToPos, + const TByte* data,const TByte* data_end){ + size_t writeLen; + hpatch_TFileStreamOutput* self=(hpatch_TFileStreamOutput*)stream->streamImport; + assert(data<=data_end); + assert(self->m_offset==0); + writeLen=(size_t)(data_end-data); + if (writeLen==0) return hpatch_TRUE; + if ((writeLen>self->base.streamSize) + ||(writeToPos>self->base.streamSize-writeLen)) _ferr_returnv(EFBIG); + if (self->is_in_readModel){ + self->is_in_readModel=hpatch_FALSE; + self->m_fpos=hpatch_kNullStreamPos; + } + if (writeToPos!=self->m_fpos){ + if (self->is_random_out){ + if (!_import_fileFlush(self->m_file)) _rw_ferr_return(); //for lseek64 safe + if (!_import_fileSeek64To(self->m_file,writeToPos)) _rw_ferr_return(); + self->m_fpos=writeToPos; + }else{ + _ferr_returnv(ERANGE); //must continue write at self->m_fpos + } + } + if (!_import_fileWrite(self->m_file,data,data+writeLen)) _rw_ferr_return(); + self->m_fpos=writeToPos+writeLen; + self->out_length=(self->out_length>=self->m_fpos)?self->out_length:self->m_fpos; + return hpatch_TRUE; + } + static hpatch_BOOL _hpatch_TFileStreamOutput_read_file(const hpatch_TStreamOutput* stream, + hpatch_StreamPos_t readFromPos, + TByte* out_data,TByte* out_data_end){ + //hpatch_TFileStreamOutput is A hpatch_TFileStreamInput ! + hpatch_TFileStreamOutput* self=(hpatch_TFileStreamOutput*)stream->streamImport; + const hpatch_TStreamInput* in_stream=(const hpatch_TStreamInput*)stream; + if (!self->is_in_readModel){ + if (!hpatch_TFileStreamOutput_flush(self)) _rw_ferr_return(); + self->is_in_readModel=hpatch_TRUE; + self->m_fpos=hpatch_kNullStreamPos; + } + return _TFileStreamInput_read_file(in_stream,readFromPos,out_data,out_data_end); + } +hpatch_BOOL hpatch_TFileStreamOutput_open(hpatch_TFileStreamOutput* self,const char* fileName_utf8, + hpatch_StreamPos_t max_file_length){ + assert(self->m_file==0); + self->fileError=hpatch_FALSE; + if (self->m_file) _ferr_returnv(EINVAL); + if (!_import_fileOpenCreateOrReWrite(fileName_utf8,&self->m_file)) + _ferr_return(); + + self->base.streamImport=self; + self->base.streamSize=max_file_length; + self->base.read_writed=_hpatch_TFileStreamOutput_read_file; + self->base.write=_TFileStreamOutput_write_file; + self->m_fpos=0; + self->m_offset=0; + self->is_in_readModel=hpatch_FALSE; + self->is_random_out=hpatch_FALSE; + self->out_length=0; + return hpatch_TRUE; +} +hpatch_BOOL hpatch_TFileStreamOutput_reopen(hpatch_TFileStreamOutput* self,const char* fileName_utf8, + hpatch_StreamPos_t max_file_length){ + hpatch_StreamPos_t curFileWritePos=0; + assert(self->m_file==0); + self->fileError=hpatch_FALSE; + if (self->m_file) _ferr_returnv(EINVAL); + if (!_import_fileReopenWrite(fileName_utf8,&self->m_file,&curFileWritePos)) + _ferr_return(); + if (curFileWritePos>max_file_length){ + //note: now not support reset file length to max_file_length + _import_fileClose(&self->m_file); + _ferr_returnv(EFBIG); + } + self->base.streamImport=self; + self->base.streamSize=max_file_length; + self->base.read_writed=_hpatch_TFileStreamOutput_read_file; + self->base.write=_TFileStreamOutput_write_file; + self->m_fpos=curFileWritePos; + self->m_offset=0; + self->is_in_readModel=hpatch_FALSE; + self->is_random_out=hpatch_FALSE; + self->out_length=curFileWritePos; + return hpatch_TRUE; +} + + +hpatch_BOOL hpatch_TFileStreamOutput_truncate(hpatch_TFileStreamOutput* self,hpatch_StreamPos_t new_file_length){ + if (!_import_fileTruncate(self->m_file,new_file_length)) + _ferr_return(); + return hpatch_TRUE; +} + +hpatch_BOOL hpatch_TFileStreamOutput_flush(hpatch_TFileStreamOutput* self){ + if (!_import_fileFlush(self->m_file)) + _ferr_return(); + return hpatch_TRUE; +} + +hpatch_BOOL hpatch_TFileStreamOutput_close(hpatch_TFileStreamOutput* self){ + if (!_import_fileClose(&self->m_file)) + _ferr_return(); + return hpatch_TRUE; +} diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.h b/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.h new file mode 100644 index 00000000..a196bc28 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/file_for_patch.h @@ -0,0 +1,240 @@ +//file_for_patch.h +// patch demo file tool +// +/* + This is the HDiffPatch copyright. + + Copyright (c) 2012-2019 HouSisong All Rights Reserved. + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HPatch_file_for_patch_h +#define HPatch_file_for_patch_h +#include //fprintf +#include // malloc free +#include //errno +#ifdef _WIN32 +# include +#endif +#include "dirDiffPatch/dir_patch/dir_patch_types.h" +#if (_HPATCH_IS_USED_errno) +# define _set_errno_new(v) do {errno=v;} while(0) +#else +# define _set_errno_new(v) +#endif + +# define set_ferr(_saved_errno,_throw_errno) /*save new errno*/\ + do { if (_throw_errno) _saved_errno=_throw_errno; } while(0) + +#if (_HPATCH_IS_USED_errno) +# define __mix_ferr_(_saved_errno,_throw_errno,_is_log) /*only save first errno*/ do { \ + if (((_saved_errno)!=(_throw_errno))&&(_throw_errno)){ \ + if (!(_saved_errno)) _saved_errno=_throw_errno; \ + if (_is_log) LOG_ERRNO(_throw_errno); } } while(0) +# define _update_ferr(fe) do { int v=errno; __mix_ferr_(fe,v,1); } while(0) +# define _update_ferrv(fe,v) do { _set_errno_new(v); __mix_ferr_(fe,v,1); } while(0) +# define mix_ferr(_saved_errno,_throw_errno) __mix_ferr_(_saved_errno,_throw_errno,0) +#else +# define _update_ferr(fe) do { fe=hpatch_TRUE; } while(0) +# define _update_ferrv(fe,v) _update_ferr(fe) +# define mix_ferr(_saved_errno,_throw_errno) set_ferr(_saved_errno,_throw_errno) +#endif + +#if ( __linux__ ) +# ifndef _IS_NEED_BLOCK_DEV +# define _IS_NEED_BLOCK_DEV 1 +# endif +#endif + +#ifndef _IS_USED_WIN32_UTF8_WAPI +# if (defined(_WIN32) && defined(_MSC_VER)) +# define _IS_USED_WIN32_UTF8_WAPI 1 // used utf8 string + wchar_t API +# endif +#endif +#if (_IS_USED_WIN32_UTF8_WAPI) +# define _hpatch_kMultiBytePage CP_UTF8 +#elif defined(_WIN32) +# define _hpatch_kMultiBytePage CP_ACP +#endif +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned char TByte; +#define hpatch_kFileIOBestMaxSize (1<<20) +#define hpatch_kPathMaxSize (1024*4) + +hpatch_inline static +hpatch_BOOL hpatch_getIsDirName(const char* path_utf8){ + size_t len=strlen(path_utf8); + return (len>0)&&(path_utf8[len-1]==kPatch_dirSeparator); +} + +hpatch_inline static const char* findUntilEnd(const char* str,char c){ + const char* result=strchr(str,c); + return (result!=0)?result:(str+strlen(str)); +} + + +#define _path_noEndDirSeparator(dst_path,src_path) \ + char dst_path[hpatch_kPathMaxSize]; \ + { size_t len=strlen(src_path); \ + if (len>=hpatch_kPathMaxSize) { _set_errno_new(ENAMETOOLONG); return hpatch_FALSE;} /* error */ \ + if ((len>0)&&(src_path[len-1]==kPatch_dirSeparator)) --len; /* without '/' */\ + memcpy(dst_path,src_path,len); \ + dst_path[len]='\0'; } /* safe */ + + +#ifdef _WIN32 +int _utf8FileName_to_w(const char* fileName_utf8,wchar_t* out_fileName_w,size_t out_wSize); +int _wFileName_to_utf8(const wchar_t* fileName_w,char* out_fileName_utf8,size_t out_bSize); +hpatch_BOOL _wFileNames_to_utf8(const wchar_t** fileNames_w,size_t fileCount, + char** out_fileNames_utf8,size_t out_byteSize); +#endif + +#if (_IS_USED_WIN32_UTF8_WAPI) +void SetDefaultStringLocale(); //for some locale Path character encoding view +#endif + +hpatch_inline static +hpatch_BOOL hpatch_getIsSamePath(const char* xPath_utf8,const char* yPath_utf8){ + _path_noEndDirSeparator(xPath,xPath_utf8); + { _path_noEndDirSeparator(yPath,yPath_utf8); + if (0==strcmp(xPath,yPath)){ + return hpatch_TRUE; + }else{ + // WARING!!! better return getCanonicalPath(xPath)==getCanonicalPath(yPath); + return hpatch_FALSE; + } + } +} + +int hpatch_printPath_utf8(const char* pathTxt_utf8); +int hpatch_printStdErrPath_utf8(const char* pathTxt_utf8); + + typedef enum hpatch_TPathType{ + kPathType_notExist, + kPathType_file, + kPathType_dir, + } hpatch_TPathType; + + +hpatch_BOOL _hpatch_getPathStat_noEndDirSeparator(const char* path_utf8,hpatch_TPathType* out_type, + hpatch_StreamPos_t* out_fileSize,size_t* out_st_mode); + +hpatch_inline static +hpatch_BOOL hpatch_getPathStat(const char* path_utf8,hpatch_TPathType* out_type, + hpatch_StreamPos_t* out_fileSize){ + if (!hpatch_getIsDirName(path_utf8)){ + return _hpatch_getPathStat_noEndDirSeparator(path_utf8,out_type,out_fileSize,0); + }else{//dir name + _path_noEndDirSeparator(path,path_utf8); + return _hpatch_getPathStat_noEndDirSeparator(path,out_type,out_fileSize,0); + } +} +hpatch_inline static +hpatch_BOOL hpatch_isPathNotExist(const char* pathName){ + hpatch_TPathType type; + if (pathName==0) { _set_errno_new(EINVAL); return hpatch_FALSE; } + if (!hpatch_getPathStat(pathName,&type,0)) return hpatch_FALSE; + return (kPathType_notExist==type); +} +hpatch_inline static +hpatch_BOOL hpatch_isPathExist(const char* pathName){ + hpatch_TPathType type; + if (pathName==0) { _set_errno_new(EINVAL); return hpatch_FALSE; } + if (!hpatch_getPathStat(pathName,&type,0)) return hpatch_FALSE; + return (kPathType_notExist!=type); +} + +hpatch_inline static +hpatch_BOOL hpatch_getFileSize(const char* fileName_utf8,hpatch_StreamPos_t* out_fileSize){ + hpatch_TPathType type; + if (!hpatch_getPathStat(fileName_utf8,&type,out_fileSize)) return hpatch_FALSE; + return (type==kPathType_file); +} + +hpatch_BOOL hpatch_getTempPathName(const char* path_utf8,char* out_tempPath_utf8,char* out_tempPath_end); +hpatch_BOOL hpatch_renamePath(const char* oldPath_utf8,const char* newPath_utf8); +hpatch_BOOL hpatch_removeFile(const char* fileName_utf8); +#if (_IS_NEED_DIR_DIFF_PATCH) +hpatch_BOOL hpatch_removeDir(const char* dirName_utf8); +hpatch_BOOL hpatch_moveFile(const char* oldPath_utf8,const char* newPath_utf8); +hpatch_BOOL hpatch_makeNewDir(const char* dirName_utf8); +#endif +hpatch_BOOL hpatch_getIsExecuteFile(const char* fileName); +hpatch_BOOL hpatch_setIsExecuteFile(const char* fileName); + +typedef FILE* hpatch_FileHandle; + +hpatch_BOOL hpatch_getIsAtty(hpatch_FileHandle fileHandle); //is device? + +typedef struct hpatch_TFileStreamInput{ + hpatch_TStreamInput base; + hpatch_FileHandle m_file; + hpatch_StreamPos_t m_fpos; + hpatch_StreamPos_t m_offset; + hpatch_FileError_t fileError; +} hpatch_TFileStreamInput; + +hpatch_inline +static void hpatch_TFileStreamInput_init(hpatch_TFileStreamInput* self){ + memset(self,0,sizeof(hpatch_TFileStreamInput)); +} +hpatch_BOOL hpatch_TFileStreamInput_open(hpatch_TFileStreamInput* self,const char* fileName_utf8); +hpatch_BOOL hpatch_TFileStreamInput_setOffset(hpatch_TFileStreamInput* self,hpatch_StreamPos_t offset); +hpatch_BOOL hpatch_TFileStreamInput_close(hpatch_TFileStreamInput* self); + +typedef struct hpatch_TFileStreamOutput{ //is hpatch_TFileStreamInput ! + hpatch_TStreamOutput base; //is hpatch_TStreamInput + write + hpatch_FileHandle m_file; + hpatch_StreamPos_t m_fpos; + hpatch_StreamPos_t m_offset; //now not used + hpatch_FileError_t fileError; // 0: no error; other: saved errno value; + // + hpatch_BOOL is_random_out; + hpatch_BOOL is_in_readModel; + hpatch_StreamPos_t out_length; +} hpatch_TFileStreamOutput; + +hpatch_inline +static void hpatch_TFileStreamOutput_init(hpatch_TFileStreamOutput* self){ + memset(self,0,sizeof(hpatch_TFileStreamOutput)); +} +hpatch_BOOL hpatch_TFileStreamOutput_open(hpatch_TFileStreamOutput* self,const char* fileName_utf8, + hpatch_StreamPos_t max_file_length); +hpatch_inline static +void hpatch_TFileStreamOutput_setRandomOut(hpatch_TFileStreamOutput* self,hpatch_BOOL is_random_out){ + self->is_random_out=is_random_out; +} + +hpatch_BOOL hpatch_TFileStreamOutput_flush(hpatch_TFileStreamOutput* self); +hpatch_BOOL hpatch_TFileStreamOutput_close(hpatch_TFileStreamOutput* self); + +hpatch_BOOL hpatch_TFileStreamOutput_reopen(hpatch_TFileStreamOutput* self,const char* fileName_utf8, + hpatch_StreamPos_t max_file_length); +hpatch_BOOL hpatch_TFileStreamOutput_truncate(hpatch_TFileStreamOutput* self,hpatch_StreamPos_t new_file_length); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/checksum_plugin.h b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/checksum_plugin.h new file mode 100644 index 00000000..18abb38f --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/checksum_plugin.h @@ -0,0 +1,52 @@ +//checksum_plugin.h +// checksum plugin type +/* + The MIT License (MIT) + Copyright (c) 2018-2019 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef HPatch_checksum_plugin_h +#define HPatch_checksum_plugin_h +#include "patch_types.h" +#ifdef __cplusplus +extern "C" { +#endif + + typedef void* hpatch_checksumHandle; + typedef struct hpatch_TChecksum{ + //return type tag; strlen(result)<=hpatch_kMaxPluginTypeLength; (Note:result lifetime) + const char* (*checksumType)(void); //ascii cstring,cannot contain '&' + hpatch_size_t (*checksumByteSize)(void); //0 //qsort +#endif +#include "patch_private.h" +#include "hpatch_mt/hpatch_mt.h" +#if (_HPATCH_IS_USED_MULTITHREAD) +# include "hpatch_mt/_hcache_window_old_mt.h" +# include "hpatch_mt/_hinput_mt.h" +#endif + +#ifndef _IS_RUN_MEM_SAFE_CHECK +# define _IS_RUN_MEM_SAFE_CHECK 1 +#endif + +#if (_IS_RUN_MEM_SAFE_CHECK) +// __RUN_MEM_SAFE_CHECK : enables bounds checking for memory access to defend against potentially corrupted or maliciously crafted data. +# define __RUN_MEM_SAFE_CHECK +#endif + +#ifdef __RUN_MEM_SAFE_CHECK +# define _SAFE_CHECK_DO(code) do{ if (!(code)) return _hpatch_FALSE; }while(0) +#else +# define _SAFE_CHECK_DO(code) do{ code; }while(0) +#endif + +#define _hpatch_FALSE hpatch_FALSE +//hpatch_uint __hpatch_debug_check_false_x=0; //for debug +//#define _hpatch_FALSE (1/__hpatch_debug_check_false_x) + +typedef unsigned char TByte; + + +//Variable-length positive integer encoding scheme (x bits for additional type flags, x<=7), outputs 1--n bytes from high: +// x0* 7-x bit +// x1* 0* 7+7-x bit +// x1* 1* 0* 7+7+7-x bit +// x1* 1* 1* 0* 7+7+7+7-x bit +// x1* 1* 1* 1* 0* 7+7+7+7+7-x bit +// ...... +hpatch_BOOL hpatch_packUIntWithTag(TByte** out_code,TByte* out_code_end, + hpatch_StreamPos_t uValue,hpatch_uint highTag, + const hpatch_uint kTagBit){//write out integer and advance pointer. + TByte* pcode=*out_code; + const hpatch_StreamPos_t kMaxValueWithTag=((hpatch_StreamPos_t)1<<(7-kTagBit))-1; + size_t bCount=0; + assert((0<=kTagBit)&&(kTagBit<=7)); + assert((highTag>>kTagBit)==0); + while ((uValue>>(7*bCount))>kMaxValueWithTag) + ++bCount; +#ifdef __RUN_MEM_SAFE_CHECK + if ((size_t)(out_code_end-pcode)<(1+bCount)) return _hpatch_FALSE; +#endif + *pcode++=(TByte)( (TByte)(uValue>>(7*bCount)) | (highTag<<(8-kTagBit)) + | (((bCount>0)?1:0)<<(7-kTagBit)) ); + while (bCount>0) { + --bCount; + *pcode++=((uValue>>(7*bCount))&((1<<7)-1)) | (TByte)(((bCount>0)?1:0)<<7); + } + *out_code=pcode; + return hpatch_TRUE; +} + +hpatch_uint hpatch_packUIntWithTag_size(hpatch_StreamPos_t uValue,const hpatch_uint kTagBit){ + const hpatch_StreamPos_t kMaxValueWithTag=((hpatch_StreamPos_t)1<<(7-kTagBit))-1; + hpatch_uint size=0; + while (uValue>kMaxValueWithTag) { + ++size; + uValue>>=7; + } + ++size; + return size; +} + +hpatch_BOOL hpatch_unpackUIntWithTag(const TByte** src_code,const TByte* src_code_end, + hpatch_StreamPos_t* result,const hpatch_uint kTagBit){//read integer and advance pointer. +#ifdef __RUN_MEM_SAFE_CHECK + //const hpatch_uint kPackMaxTagBit=7; +#endif + hpatch_StreamPos_t value; + TByte code; + const TByte* pcode=*src_code; + +#ifdef __RUN_MEM_SAFE_CHECK + //assert(kTagBit<=kPackMaxTagBit); + if (src_code_end<=pcode) return _hpatch_FALSE; +#endif + code=*pcode; ++pcode; + value=code&((1<<(7-kTagBit))-1); + if ((code&(1<<(7-kTagBit)))!=0){ + do { +#ifdef __RUN_MEM_SAFE_CHECK + if ((value>>(sizeof(value)*8-7))!=0) return _hpatch_FALSE;//cannot save 7bit + if (src_code_end==pcode) return _hpatch_FALSE; +#endif + code=*pcode; ++pcode; + value=(value<<7) | (code&((1<<7)-1)); + } while ((code&(1<<7))!=0); + } + (*src_code)=pcode; + *result=value; + return hpatch_TRUE; +} + + + static hpatch_BOOL _read_mem_stream(const hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + const unsigned char* src=(const unsigned char*)stream->streamImport; + hpatch_size_t readLen=out_data_end-out_data; +#ifdef __RUN_MEM_SAFE_CHECK + if (readFromPos>stream->streamSize) return _hpatch_FALSE; + if (readLen>(hpatch_StreamPos_t)(stream->streamSize-readFromPos)) return _hpatch_FALSE; +#endif + memcpy(out_data,src+readFromPos,readLen); + return hpatch_TRUE; + } +const hpatch_TStreamInput* mem_as_hStreamInput(hpatch_TStreamInput* out_stream, + const unsigned char* mem,const unsigned char* mem_end){ + assert(mem<=mem_end); + out_stream->streamImport=(void*)mem; + out_stream->streamSize=mem_end-mem; + out_stream->read=_read_mem_stream; + return out_stream; +} + + // Two-segment memory stream + typedef struct TMem2StreamImport{ + const unsigned char* seg1_begin; + const unsigned char* seg1_end; + const unsigned char* seg2_begin; + const unsigned char* seg2_end; + } TMem2StreamImport; + + static hpatch_BOOL _read_mem2_stream(const hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + const TMem2StreamImport* self=(const TMem2StreamImport*)stream->streamImport; + hpatch_size_t readLen=out_data_end-out_data; + const size_t seg1Len=(size_t)(self->seg1_end-self->seg1_begin); +#ifdef __RUN_MEM_SAFE_CHECK + if (readFromPos>stream->streamSize) return _hpatch_FALSE; + if (readLen>(hpatch_StreamPos_t)(stream->streamSize-readFromPos)) return _hpatch_FALSE; +#endif + while (readLen>0){ + const unsigned char* src; + size_t srcOffset; + size_t copyLen; + if ((size_t)readFromPosseg1_begin; + srcOffset=(size_t)readFromPos; + copyLen =seg1Len-srcOffset; + }else{ + src =self->seg2_begin; + srcOffset=(size_t)(readFromPos-seg1Len); + copyLen =(size_t)(self->seg2_end-self->seg2_begin)-srcOffset; + } + copyLen=copyLen<=readLen?copyLen:readLen; + memcpy(out_data,src+srcOffset,copyLen); + readFromPos+=(hpatch_StreamPos_t)copyLen; + out_data+=copyLen; + readLen-=copyLen; + } + return hpatch_TRUE; + } + + static hpatch_inline + const hpatch_TStreamInput* mem2_as_hStreamInput(hpatch_TStreamInput* out_stream,TMem2StreamImport* import, + const unsigned char* seg1_begin,const unsigned char* seg1_end, + const unsigned char* seg2_begin,const unsigned char* seg2_end){ + assert((seg1_begin<=seg1_end)&&(seg2_begin<=seg2_end)); + import->seg1_begin=seg1_begin; + import->seg1_end =seg1_end; + import->seg2_begin=seg2_begin; + import->seg2_end =seg2_end; + out_stream->streamImport=import; + out_stream->streamSize=(seg1_end-seg1_begin)+(seg2_end-seg2_begin); + out_stream->read=_read_mem2_stream; + return out_stream; + } + + static hpatch_BOOL _write_mem_stream(const hpatch_TStreamOutput* stream,hpatch_StreamPos_t writeToPos, + const unsigned char* data,const unsigned char* data_end){ + unsigned char* out_dst=(unsigned char*)stream->streamImport; + hpatch_size_t writeLen=data_end-data; +#ifdef __RUN_MEM_SAFE_CHECK + if (writeToPos>stream->streamSize) return _hpatch_FALSE; + if (writeLen>(hpatch_StreamPos_t)(stream->streamSize-writeToPos)) return _hpatch_FALSE; +#endif + memcpy(out_dst+writeToPos,data,writeLen); + return hpatch_TRUE; + } + typedef hpatch_BOOL (*_read_mem_stream_t)(const hpatch_TStreamOutput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end); +const hpatch_TStreamOutput* mem_as_hStreamOutput(hpatch_TStreamOutput* out_stream, + unsigned char* mem,unsigned char* mem_end){ + out_stream->streamImport=mem; + out_stream->streamSize=mem_end-mem; + out_stream->read_writed=(_read_mem_stream_t)_read_mem_stream; + out_stream->write=_write_mem_stream; + return out_stream; +} + +hpatch_BOOL hpatch_deccompress_mem(hpatch_TDecompress* decompressPlugin, + const unsigned char* code,const unsigned char* code_end, + unsigned char* out_data,unsigned char* out_data_end){ + hpatch_decompressHandle dec=0; + hpatch_BOOL result,colose_rt; + hpatch_TStreamInput codeStream; + mem_as_hStreamInput(&codeStream,code,code_end); + dec=decompressPlugin->open(decompressPlugin,(out_data_end-out_data), + &codeStream,0,codeStream.streamSize); + if (dec==0) return _hpatch_FALSE; + result=decompressPlugin->decompress_part(dec,out_data,out_data_end); + colose_rt=decompressPlugin->close(decompressPlugin,dec); + assert(colose_rt); + return result; +} + + +//////// +//patch by memory + +static const hpatch_uint kSignTagBit=1; + +static hpatch_BOOL _bytesRle_load(TByte* out_data,TByte* out_dataEnd, + const TByte* rle_code,const TByte* rle_code_end); +static void addData(TByte* dst,const TByte* src,hpatch_size_t length); +hpatch_inline +static hpatch_BOOL _unpackUIntWithTag(const TByte** src_code,const TByte* src_code_end, + hpatch_size_t* result,const hpatch_uint kTagBit){ + if (sizeof(hpatch_size_t)==sizeof(hpatch_StreamPos_t)){ + return hpatch_unpackUIntWithTag(src_code,src_code_end,(hpatch_StreamPos_t*)result,kTagBit); + }else{ + hpatch_StreamPos_t u64=0; + hpatch_BOOL rt=hpatch_unpackUIntWithTag(src_code,src_code_end,&u64,kTagBit); + hpatch_size_t u=(hpatch_size_t)u64; + *result=u; +#ifdef __RUN_MEM_SAFE_CHECK + return rt&(u==u64); +#else + return rt; +#endif + } +} + +#define unpackUIntWithTagTo(puint,src_code,src_code_end,kTagBit) \ + _SAFE_CHECK_DO(_unpackUIntWithTag(src_code,src_code_end,puint,kTagBit)) +#define unpackUIntTo(puint,src_code,src_code_end) \ + unpackUIntWithTagTo(puint,src_code,src_code_end,0) + +hpatch_BOOL patch(TByte* out_newData,TByte* out_newData_end, + const TByte* oldData,const TByte* oldData_end, + const TByte* serializedDiff,const TByte* serializedDiff_end){ + const TByte *code_lengths, *code_lengths_end, + *code_inc_newPos, *code_inc_newPos_end, + *code_inc_oldPos, *code_inc_oldPos_end, + *code_newDataDiff, *code_newDataDiff_end; + hpatch_size_t coverCount; + + assert(out_newData<=out_newData_end); + assert(oldData<=oldData_end); + assert(serializedDiff<=serializedDiff_end); + unpackUIntTo(&coverCount,&serializedDiff, serializedDiff_end); + { //head + hpatch_size_t lengthSize,inc_newPosSize,inc_oldPosSize,newDataDiffSize; + unpackUIntTo(&lengthSize,&serializedDiff, serializedDiff_end); + unpackUIntTo(&inc_newPosSize,&serializedDiff, serializedDiff_end); + unpackUIntTo(&inc_oldPosSize,&serializedDiff, serializedDiff_end); + unpackUIntTo(&newDataDiffSize,&serializedDiff, serializedDiff_end); +#ifdef __RUN_MEM_SAFE_CHECK + if (lengthSize>(hpatch_size_t)(serializedDiff_end-serializedDiff)) return _hpatch_FALSE; +#endif + code_lengths=serializedDiff; serializedDiff+=lengthSize; + code_lengths_end=serializedDiff; +#ifdef __RUN_MEM_SAFE_CHECK + if (inc_newPosSize>(hpatch_size_t)(serializedDiff_end-serializedDiff)) return _hpatch_FALSE; +#endif + code_inc_newPos=serializedDiff; serializedDiff+=inc_newPosSize; + code_inc_newPos_end=serializedDiff; +#ifdef __RUN_MEM_SAFE_CHECK + if (inc_oldPosSize>(hpatch_size_t)(serializedDiff_end-serializedDiff)) return _hpatch_FALSE; +#endif + code_inc_oldPos=serializedDiff; serializedDiff+=inc_oldPosSize; + code_inc_oldPos_end=serializedDiff; +#ifdef __RUN_MEM_SAFE_CHECK + if (newDataDiffSize>(hpatch_size_t)(serializedDiff_end-serializedDiff)) return _hpatch_FALSE; +#endif + code_newDataDiff=serializedDiff; serializedDiff+=newDataDiffSize; + code_newDataDiff_end=serializedDiff; + } + + //decode rle ; rle data begin==cur serializedDiff; + _SAFE_CHECK_DO(_bytesRle_load(out_newData, out_newData_end, serializedDiff, serializedDiff_end)); + + { //patch + const hpatch_size_t newDataSize=(hpatch_size_t)(out_newData_end-out_newData); + hpatch_size_t oldPosBack=0; + hpatch_size_t newPosBack=0; + hpatch_size_t i; + for (i=0; i=code_inc_oldPos_end) return _hpatch_FALSE; +#endif + inc_oldPos_sign=(*code_inc_oldPos)>>(8-kSignTagBit); + unpackUIntWithTagTo(&inc_oldPos,&code_inc_oldPos, code_inc_oldPos_end, kSignTagBit); + if (inc_oldPos_sign==0) + oldPos=oldPosBack+inc_oldPos; + else + oldPos=oldPosBack-inc_oldPos; + if (copyLength>0){ +#ifdef __RUN_MEM_SAFE_CHECK + if (copyLength>(hpatch_size_t)(newDataSize-newPosBack)) return _hpatch_FALSE; + if (copyLength>(hpatch_size_t)(code_newDataDiff_end-code_newDataDiff)) return _hpatch_FALSE; +#endif + memcpy(out_newData+newPosBack,code_newDataDiff,copyLength); + code_newDataDiff+=copyLength; + newPosBack+=copyLength; + } +#ifdef __RUN_MEM_SAFE_CHECK + if ( (addLength>(hpatch_size_t)(newDataSize-newPosBack)) ) return _hpatch_FALSE; + if ( (oldPos>(hpatch_size_t)(oldData_end-oldData)) || + (addLength>(hpatch_size_t)(oldData_end-oldData-oldPos)) ) return _hpatch_FALSE; +#endif + addData(out_newData+newPosBack,oldData+oldPos,addLength); + oldPosBack=oldPos; + newPosBack+=addLength; + } + + if (newPosBack(hpatch_size_t)(code_newDataDiff_end-code_newDataDiff)) return _hpatch_FALSE; +#endif + memcpy(out_newData+newPosBack,code_newDataDiff,copyLength); + code_newDataDiff+=copyLength; + //newPosBack=newDataSize; + } + } + + if ( (code_lengths==code_lengths_end) + &&(code_inc_newPos==code_inc_newPos_end) + &&(code_inc_oldPos==code_inc_oldPos_end) + &&(code_newDataDiff==code_newDataDiff_end)) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + +hpatch_inline static void addData(TByte* dst,const TByte* src,hpatch_size_t length){ + while (length--) { *dst++ += *src++; } +} + +static hpatch_BOOL _bytesRle_load(TByte* out_data,TByte* out_dataEnd, + const TByte* rle_code,const TByte* rle_code_end){ + const TByte* ctrlBuf,*ctrlBuf_end; + hpatch_size_t ctrlSize; + unpackUIntTo(&ctrlSize,&rle_code,rle_code_end); +#ifdef __RUN_MEM_SAFE_CHECK + if (ctrlSize>(hpatch_size_t)(rle_code_end-rle_code)) return _hpatch_FALSE; +#endif + ctrlBuf=rle_code; + rle_code+=ctrlSize; + ctrlBuf_end=rle_code; + while (ctrlBuf_end-ctrlBuf>0){ + enum TByteRleType type=(enum TByteRleType)((*ctrlBuf)>>(8-kByteRleType_bit)); + hpatch_size_t length; + unpackUIntWithTagTo(&length,&ctrlBuf,ctrlBuf_end,kByteRleType_bit); +#ifdef __RUN_MEM_SAFE_CHECK + if (length>=(hpatch_size_t)(out_dataEnd-out_data)) return _hpatch_FALSE; +#endif + ++length; + switch (type){ + case kByteRleType_rle0:{ + memset(out_data,0,length); + out_data+=length; + }break; + case kByteRleType_rle255:{ + memset(out_data,255,length); + out_data+=length; + }break; + case kByteRleType_rle:{ +#ifdef __RUN_MEM_SAFE_CHECK + if (1>(hpatch_size_t)(rle_code_end-rle_code)) return _hpatch_FALSE; +#endif + memset(out_data,*rle_code,length); + ++rle_code; + out_data+=length; + }break; + case kByteRleType_unrle:{ +#ifdef __RUN_MEM_SAFE_CHECK + if (length>(hpatch_size_t)(rle_code_end-rle_code)) return _hpatch_FALSE; +#endif + memcpy(out_data,rle_code,length); + rle_code+=length; + out_data+=length; + }break; + } + } + + if ( (ctrlBuf==ctrlBuf_end) + &&(rle_code==rle_code_end) + &&(out_data==out_dataEnd)) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + +//---------------------- +//patch by stream + + static hpatch_BOOL _TStreamInputClip_read(const hpatch_TStreamInput* stream, + hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + TStreamInputClip* self=(TStreamInputClip*)stream->streamImport; +#ifdef __RUN_MEM_SAFE_CHECK + if (readFromPos+(out_data_end-out_data)>self->base.streamSize) return _hpatch_FALSE; +#endif + return self->srcStream->read(self->srcStream,readFromPos+self->clipBeginPos,out_data,out_data_end); + } +void TStreamInputClip_init(TStreamInputClip* self,const hpatch_TStreamInput* srcStream, + hpatch_StreamPos_t clipBeginPos,hpatch_StreamPos_t clipEndPos){ + assert(self!=0); + assert(srcStream!=0); + assert(clipBeginPos<=clipEndPos); + assert(clipEndPos<=srcStream->streamSize); + self->srcStream=srcStream; + self->clipBeginPos=clipBeginPos; + self->base.streamImport=self; + self->base.streamSize=clipEndPos-clipBeginPos; + self->base.read=_TStreamInputClip_read; +} + + static hpatch_BOOL _TStreamOutputClip_write(const hpatch_TStreamOutput* stream, + hpatch_StreamPos_t writePos, + const unsigned char* data,const unsigned char* data_end){ + TStreamOutputClip* self=(TStreamOutputClip*)stream->streamImport; +#ifdef __RUN_MEM_SAFE_CHECK + if (writePos+(data_end-data)>self->base.streamSize) return _hpatch_FALSE; +#endif + return self->srcStream->write(self->srcStream,writePos+self->clipBeginPos,data,data_end); +} + +void TStreamOutputClip_init(TStreamOutputClip* self,const hpatch_TStreamOutput* srcStream, + hpatch_StreamPos_t clipBeginPos,hpatch_StreamPos_t clipEndPos){ + assert(self!=0); + assert(srcStream!=0); + assert(clipBeginPos<=clipEndPos); + assert(clipEndPos<=srcStream->streamSize); + self->srcStream=srcStream; + self->clipBeginPos=clipBeginPos; + self->base.streamImport=self; + self->base.streamSize=clipEndPos-clipBeginPos; + ((TStreamInputClip*)self)->base.read=_TStreamInputClip_read; + self->base.write=_TStreamOutputClip_write; +} + + + +//assert(hpatch_kStreamCacheSize>=hpatch_kMaxPluginTypeLength+1); +struct __private_hpatch_check_kMaxCompressTypeLength { + char _[(hpatch_kStreamCacheSize>=(hpatch_kMaxPluginTypeLength+1))?1:-1];}; + +hpatch_BOOL _TStreamCacheClip_readStr_end(TStreamCacheClip* sclip,TByte endTag, + char* out_type,size_t typeBufLen){ + const TByte* type_begin; + hpatch_size_t i; + hpatch_size_t readLen=typeBufLen; + if (readLen>_TStreamCacheClip_leaveSize(sclip)) + readLen=(hpatch_size_t)_TStreamCacheClip_leaveSize(sclip); + type_begin=_TStreamCacheClip_accessData(sclip,readLen); + if (type_begin==0) return _hpatch_FALSE;//not found + for (i=0; icacheBuf[0]; + const hpatch_StreamPos_t streamSize=sclip->streamPos_end-sclip->streamPos; + hpatch_size_t readSize=sclip->cacheBegin; + if (readSize>streamSize) + readSize=(hpatch_size_t)streamSize; + if (readSize==0) return hpatch_TRUE; + if (!_TStreamCacheClip_isCacheEmpty(sclip)){ + memmove(buf0+(hpatch_size_t)(sclip->cacheBegin-readSize), + buf0+sclip->cacheBegin,_TStreamCacheClip_cachedSize(sclip)); + } + if (!sclip->srcStream->read(sclip->srcStream,sclip->streamPos, + buf0+(sclip->cacheEnd-readSize),buf0+sclip->cacheEnd)) + return _hpatch_FALSE;//read error + sclip->cacheBegin-=readSize; + sclip->streamPos+=readSize; + return hpatch_TRUE; +} + +hpatch_BOOL _TStreamCacheClip_skipData(TStreamCacheClip* sclip,hpatch_StreamPos_t skipLongSize){ + while (skipLongSize>0) { + hpatch_size_t len=sclip->cacheEnd; + if (len>skipLongSize) + len=(hpatch_size_t)skipLongSize; + if (_TStreamCacheClip_accessData(sclip,len)){ + _TStreamCacheClip_skipData_noCheck(sclip,len); + skipLongSize-=len; + }else{ + return _hpatch_FALSE; + } + } + return hpatch_TRUE; +} + +//assert(hpatch_kStreamCacheSize>=hpatch_kMaxPackedUIntBytes); +struct __private_hpatch_check_hpatch_kMaxPackedUIntBytes { + char _[(hpatch_kStreamCacheSize>=hpatch_kMaxPackedUIntBytes)?1:-1]; }; + +hpatch_BOOL _TStreamCacheClip_unpackUIntWithTag(TStreamCacheClip* sclip,hpatch_StreamPos_t* result,const hpatch_uint kTagBit){ + TByte* curCode,*codeBegin; + hpatch_size_t readSize=hpatch_kMaxPackedUIntBytes; + const hpatch_StreamPos_t dataSize=_TStreamCacheClip_leaveSize(sclip); + if (readSize>dataSize) + readSize=(hpatch_size_t)dataSize; + codeBegin=_TStreamCacheClip_accessData(sclip,readSize); + if (codeBegin==0) return _hpatch_FALSE; + curCode=codeBegin; + _SAFE_CHECK_DO(hpatch_unpackUIntWithTag((const TByte**)&curCode,codeBegin+readSize,result,kTagBit)); + _TStreamCacheClip_skipData_noCheck(sclip,(hpatch_size_t)(curCode-codeBegin)); + return hpatch_TRUE; +} + +hpatch_BOOL _TStreamCacheClip_readDataTo(TStreamCacheClip* sclip,TByte* out_buf,TByte* bufEnd){ + hpatch_size_t readLen=_TStreamCacheClip_cachedSize(sclip); + hpatch_size_t outLen=bufEnd-out_buf; + if (readLen>=outLen) + readLen=outLen; + memcpy(out_buf,&sclip->cacheBuf[sclip->cacheBegin],readLen); + sclip->cacheBegin+=readLen; + outLen-=readLen; + if (outLen){ + out_buf += readLen; + if (outLen<(sclip->cacheEnd>>1)){ + if (!_TStreamCacheClip_updateCache(sclip)) return _hpatch_FALSE; +#ifdef __RUN_MEM_SAFE_CHECK + if (outLen>_TStreamCacheClip_cachedSize(sclip)) return _hpatch_FALSE; +#endif + return _TStreamCacheClip_readDataTo(sclip, out_buf, bufEnd); + }else{ + if (!sclip->srcStream->read(sclip->srcStream,sclip->streamPos, + out_buf,bufEnd)) return _hpatch_FALSE; + sclip->streamPos+=outLen; + } + } + return hpatch_TRUE; +} + +hpatch_BOOL _TStreamCacheClip_addDataTo(TStreamCacheClip* self,unsigned char* dst,hpatch_size_t addLen){ + const unsigned char* src=_TStreamCacheClip_readData(self,addLen); + if (src==0) return _hpatch_FALSE; + addData(dst,src,addLen); + return hpatch_TRUE; +} + + static hpatch_BOOL _decompress_read(const hpatch_TStreamInput* stream, + const hpatch_StreamPos_t readFromPos, + TByte* out_data,TByte* out_data_end){ + _TDecompressInputStream* self=(_TDecompressInputStream*)stream->streamImport; + return self->decompressPlugin->decompress_part(self->decompressHandle,out_data,out_data_end); + } +hpatch_BOOL getStreamClip(TStreamCacheClip* out_clip,_TDecompressInputStream* out_stream, + hpatch_StreamPos_t dataSize,hpatch_StreamPos_t compressedSize, + const hpatch_TStreamInput* stream,hpatch_StreamPos_t* pCurStreamPos, + hpatch_TDecompress* decompressPlugin,TByte* aCache,hpatch_size_t cacheSize){ + hpatch_StreamPos_t curStreamPos=*pCurStreamPos; + if (compressedSize==0){ +#ifdef __RUN_MEM_SAFE_CHECK + if ((curStreamPos+dataSize)stream->streamSize) return _hpatch_FALSE; +#endif + if (out_clip) + _TStreamCacheClip_init(out_clip,stream,curStreamPos,curStreamPos+dataSize,aCache,cacheSize); + curStreamPos+=dataSize; + }else{ +#ifdef __RUN_MEM_SAFE_CHECK + if ((curStreamPos+compressedSize)stream->streamSize) return _hpatch_FALSE; +#endif + if (out_clip){ + out_stream->IInputStream.streamImport=out_stream; + out_stream->IInputStream.streamSize=dataSize; + out_stream->IInputStream.read=_decompress_read; + out_stream->decompressPlugin=decompressPlugin; + if (out_stream->decompressHandle==0){ + out_stream->decompressHandle=decompressPlugin->open(decompressPlugin,dataSize,stream, + curStreamPos,curStreamPos+compressedSize); + if (!out_stream->decompressHandle) return _hpatch_FALSE; + }else{ + if (decompressPlugin->reset_code==0) return _hpatch_FALSE; + if (!decompressPlugin->reset_code(out_stream->decompressHandle,dataSize,stream,curStreamPos, + curStreamPos+compressedSize)) return _hpatch_FALSE; + } + _TStreamCacheClip_init(out_clip,&out_stream->IInputStream,0, + out_stream->IInputStream.streamSize,aCache,cacheSize); + } + curStreamPos+=compressedSize; + } + *pCurStreamPos=curStreamPos; + return hpatch_TRUE; +} + +/////// + +static hpatch_force_inline hpatch_BOOL __TOutStreamCache_writeStream(_TOutStreamCache* self,const TByte* data,hpatch_size_t dataSize){ + if (!self->dstStream->write(self->dstStream,self->writeToPos,data,data+dataSize)) + return _hpatch_FALSE; + self->writeToPos+=dataSize; + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_flush(_TOutStreamCache* self){ + hpatch_size_t curSize=self->cacheCur; + if (curSize>0){ + if (!__TOutStreamCache_writeStream(self,self->cacheBuf,curSize)) + return _hpatch_FALSE; + self->cacheCur=0; + } + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_write(_TOutStreamCache* self,const TByte* data,hpatch_size_t dataSize){ + while (dataSize>0) { + hpatch_size_t copyLen; + hpatch_size_t curSize=self->cacheCur; + if ((dataSize>=self->cacheEnd)&&(curSize==0)){ + return __TOutStreamCache_writeStream(self,data,dataSize); + } + copyLen=self->cacheEnd-curSize; + copyLen=(copyLen<=dataSize)?copyLen:dataSize; + memcpy(self->cacheBuf+curSize,data,copyLen); + self->cacheCur=curSize+copyLen; + data+=copyLen; + dataSize-=copyLen; + if (self->cacheCur==self->cacheEnd){ + if (!_TOutStreamCache_flush(self)) + return _hpatch_FALSE; + } + } + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_fill(_TOutStreamCache* self,hpatch_byte fillValue,hpatch_StreamPos_t fillLength){ + assert(self->cacheBuf); + if (self->cacheBuf==0) return _hpatch_FALSE; + while (fillLength>0){ + hpatch_size_t curSize=self->cacheCur; + hpatch_size_t runStep=self->cacheEnd-curSize; + runStep=(runStep<=fillLength)?runStep:(hpatch_size_t)fillLength; + memset(self->cacheBuf+curSize,fillValue,runStep); + self->cacheCur=curSize+runStep; + fillLength-=runStep; + if (self->cacheCur==self->cacheEnd){ + if (!_TOutStreamCache_flush(self)) + return _hpatch_FALSE; + } + } + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_copyFromStream(_TOutStreamCache* self,const hpatch_TStreamInput* src, + hpatch_StreamPos_t srcPos,hpatch_StreamPos_t copyLength){ + assert(self->cacheBuf); + if (self->cacheBuf==0) return _hpatch_FALSE; + while (copyLength>0){ + hpatch_size_t curSize=self->cacheCur; + hpatch_size_t runStep=self->cacheEnd-curSize; + hpatch_byte* buf=self->cacheBuf+curSize; + runStep=(runStep<=copyLength)?runStep:(hpatch_size_t)copyLength; + if (!src->read(src,srcPos,buf,buf+runStep)) + return _hpatch_FALSE; + srcPos+=runStep; + self->cacheCur=curSize+runStep; + copyLength-=runStep; + if (self->cacheCur==self->cacheEnd){ + if (!_TOutStreamCache_flush(self)) + return _hpatch_FALSE; + } + } + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_copyFromClip(_TOutStreamCache* self,TStreamCacheClip* src,hpatch_StreamPos_t copyLength){ + while (copyLength>0){ + const TByte* data; + hpatch_size_t runStep=(src->cacheEnd<=copyLength)?src->cacheEnd:(hpatch_size_t)copyLength; + data=_TStreamCacheClip_readData(src,runStep); + if (data==0) + return _hpatch_FALSE; + if (!_TOutStreamCache_write(self,data,runStep)) + return _hpatch_FALSE; + copyLength-=runStep; + } + return hpatch_TRUE; +} + +hpatch_BOOL _TOutStreamCache_copyFromSelf(_TOutStreamCache* self,hpatch_StreamPos_t aheadLength,hpatch_StreamPos_t copyLength){ + // [ writed ] + // [ cached buf | empty buf ] + const hpatch_TStreamInput* src=(const hpatch_TStreamInput*)self->dstStream; + hpatch_StreamPos_t srcPos=self->writeToPos+self->cacheCur-aheadLength; + if (src->read==0) //can't read + return _hpatch_FALSE; + if ((aheadLength<1)||(aheadLength>self->writeToPos+self->cacheCur)) + return _hpatch_FALSE; + + if (srcPos+copyLength<=self->writeToPos){//copy from stream + // [ copyLength ] +__copy_in_stream: + return _TOutStreamCache_copyFromStream(self,src,srcPos,copyLength); + }else if (srcPos>=self->writeToPos){ //copy in mem + // [ copyLength ] +__copy_in_mem: + while (copyLength>0){ + hpatch_byte* dstBuf=self->cacheBuf+self->cacheCur; + hpatch_byte* srcBuf=dstBuf-(hpatch_size_t)aheadLength; + hpatch_size_t runLen=(self->cacheCur+copyLength<=self->cacheEnd)?(hpatch_size_t)copyLength:(self->cacheEnd-self->cacheCur); + hpatch_size_t i; + for (i=0;icacheCur+=runLen; + if (self->cacheCur==self->cacheEnd){ + if (!_TOutStreamCache_flush(self)) + return _hpatch_FALSE; + runLen=(hpatch_size_t)((aheadLength<=copyLength)?aheadLength:copyLength); + memmove(self->cacheBuf,self->cacheBuf+self->cacheEnd-(hpatch_size_t)aheadLength,runLen); + self->cacheCur=runLen; + copyLength-=runLen; + }else{ + assert(copyLength==0); + } + } + return hpatch_TRUE; + }else if (self->writeToPos+self->cacheCur<=srcPos+self->cacheEnd){ + // small data in stream,can as copy in mem + hpatch_byte* dstBuf=self->cacheBuf+self->cacheCur; + hpatch_size_t runLen=(hpatch_size_t)(self->writeToPos-srcPos); + if (!src->read(src,srcPos,dstBuf,dstBuf+runLen)) + return _hpatch_FALSE; + //srcPos+=runLen; //not used + copyLength-=runLen; + self->cacheCur+=runLen; + if (self->cacheCur==self->cacheEnd){ + while (hpatch_TRUE){ + if (self->cacheCur==self->cacheEnd){ + if (!_TOutStreamCache_flush(self)) + return _hpatch_FALSE; + } + if (copyLength>0){ + runLen=(self->cacheEnd<=copyLength)?self->cacheEnd:(hpatch_size_t)copyLength; + //srcPos+=runLen; //not used + copyLength-=runLen; + self->cacheCur=runLen; + }else{ + return hpatch_TRUE; + } + } + }else{ + goto __copy_in_mem; + } + }else{ + goto __copy_in_stream; + } +} + + +typedef struct _TBytesRle_load_stream{ + hpatch_StreamPos_t memCopyLength; + hpatch_StreamPos_t memSetLength; + TByte memSetValue; + TStreamCacheClip ctrlClip; + TStreamCacheClip rleCodeClip; +} _TBytesRle_load_stream; + +hpatch_inline +static void _TBytesRle_load_stream_init(_TBytesRle_load_stream* loader){ + loader->memSetLength=0; + loader->memSetValue=0;//nil; + loader->memCopyLength=0; + _TStreamCacheClip_init(&loader->ctrlClip,0,0,0,0,0); + _TStreamCacheClip_init(&loader->rleCodeClip,0,0,0,0,0); +} + +hpatch_inline static void memSet_add(TByte* dst,const TByte src,hpatch_size_t length){ + while (length--) { (*dst++) += src; } +} + +static hpatch_BOOL _TBytesRle_load_stream_mem_add(_TBytesRle_load_stream* loader, + hpatch_size_t* _decodeSize,TByte** _out_data){ + hpatch_size_t decodeSize=*_decodeSize; + TByte* out_data=*_out_data; + TStreamCacheClip* rleCodeClip=&loader->rleCodeClip; + + hpatch_StreamPos_t memSetLength=loader->memSetLength; + if (memSetLength!=0){ + hpatch_size_t memSetStep=((memSetLength<=decodeSize)?(hpatch_size_t)memSetLength:decodeSize); + const TByte byteSetValue=loader->memSetValue; + if (out_data!=0){ + if (byteSetValue!=0) + memSet_add(out_data,byteSetValue,memSetStep); + out_data+=memSetStep; + } + decodeSize-=memSetStep; + loader->memSetLength=memSetLength-memSetStep; + } + while ((loader->memCopyLength>0)&&(decodeSize>0)) { + TByte* rleData; + hpatch_size_t decodeStep=rleCodeClip->cacheEnd; + if (decodeStep>loader->memCopyLength) + decodeStep=(hpatch_size_t)loader->memCopyLength; + if (decodeStep>decodeSize) + decodeStep=decodeSize; + rleData=_TStreamCacheClip_readData(rleCodeClip,decodeStep); + if (rleData==0) return _hpatch_FALSE; + if (out_data){ + addData(out_data,rleData,decodeStep); + out_data+=decodeStep; + } + decodeSize-=decodeStep; + loader->memCopyLength-=decodeStep; + } + *_decodeSize=decodeSize; + *_out_data=out_data; + return hpatch_TRUE; +} + +hpatch_inline +static hpatch_BOOL _TBytesRle_load_stream_isFinish(const _TBytesRle_load_stream* loader){ + return(loader->memSetLength==0) + &&(loader->memCopyLength==0) + &&(_TStreamCacheClip_isFinish(&loader->rleCodeClip)) + &&(_TStreamCacheClip_isFinish(&loader->ctrlClip)); +} + + + +#define _clip_unpackUIntWithTagTo(puint,sclip,kTagBit) \ + { if (!_TStreamCacheClip_unpackUIntWithTag(sclip,puint,kTagBit)) return _hpatch_FALSE; } +#define _clip_unpackUIntTo(puint,sclip) _clip_unpackUIntWithTagTo(puint,sclip,0) + +static hpatch_BOOL _TBytesRle_load_stream_decode_add(_TBytesRle_load_stream* loader, + TByte* out_data,hpatch_size_t decodeSize){ + if (!_TBytesRle_load_stream_mem_add(loader,&decodeSize,&out_data)) + return _hpatch_FALSE; + + while ((decodeSize>0)&&(!_TStreamCacheClip_isFinish(&loader->ctrlClip))){ + enum TByteRleType type; + hpatch_StreamPos_t length; + const TByte* pType=_TStreamCacheClip_accessData(&loader->ctrlClip,1); + if (pType==0) return _hpatch_FALSE; + type=(enum TByteRleType)((*pType)>>(8-kByteRleType_bit)); + _clip_unpackUIntWithTagTo(&length,&loader->ctrlClip,kByteRleType_bit); + ++length; + switch (type){ + case kByteRleType_rle0:{ + loader->memSetLength=length; + loader->memSetValue=0; + }break; + case kByteRleType_rle255:{ + loader->memSetLength=length; + loader->memSetValue=255; + }break; + case kByteRleType_rle:{ + const TByte* pSetValue=_TStreamCacheClip_readData(&loader->rleCodeClip,1); + if (pSetValue==0) return _hpatch_FALSE; + loader->memSetValue=*pSetValue; + loader->memSetLength=length; + }break; + case kByteRleType_unrle:{ + loader->memCopyLength=length; + }break; + } + if (!_TBytesRle_load_stream_mem_add(loader,&decodeSize,&out_data)) return _hpatch_FALSE; + } + + if (decodeSize==0) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + +#define _TBytesRle_load_stream_decode_skip(loader,decodeSize) \ + _TBytesRle_load_stream_decode_add(loader,0,decodeSize) + +static hpatch_BOOL _patch_add_old_with_rle(_TOutStreamCache* outCache,_TBytesRle_load_stream* rle_loader, + const hpatch_TStreamInput* old,hpatch_StreamPos_t oldPos, + hpatch_StreamPos_t addLength,TByte* aCache,hpatch_size_t aCacheSize){ + while (addLength>0){ + hpatch_size_t decodeStep=aCacheSize; + if (decodeStep>addLength) + decodeStep=(hpatch_size_t)addLength; + if (!old->read(old,oldPos,aCache,aCache+decodeStep)) return _hpatch_FALSE; + if (!_TBytesRle_load_stream_decode_add(rle_loader,aCache,decodeStep)) return _hpatch_FALSE; + if (!_TOutStreamCache_write(outCache,aCache,decodeStep)) return _hpatch_FALSE; + oldPos+=decodeStep; + addLength-=decodeStep; + } + return hpatch_TRUE; +} + +typedef struct _TCovers{ + hpatch_TCovers ICovers; + hpatch_StreamPos_t coverCount; + hpatch_StreamPos_t oldPosBack; + hpatch_StreamPos_t newPosBack; + TStreamCacheClip* code_inc_oldPosClip; + TStreamCacheClip* code_inc_newPosClip; + TStreamCacheClip* code_lengthsClip; + hpatch_BOOL isOldPosBackNeedAddLength; +} _TCovers; + +static hpatch_StreamPos_t _covers_leaveCoverCount(const hpatch_TCovers* covers){ + const _TCovers* self=(const _TCovers*)covers; + return self->coverCount; +} +static hpatch_BOOL _covers_close_nil(hpatch_TCovers* covers){ + //empty + return hpatch_TRUE; +} + + static hpatch_BOOL _read_sign_pos_byLastPos(TStreamCacheClip* clip,hpatch_StreamPos_t* pLastOldPos){ + hpatch_StreamPos_t absDelta; + const TByte* pSignByte=_TStreamCacheClip_accessData(clip,1); + if (!pSignByte) return _hpatch_FALSE; + hpatch_BOOL isNeg=((*pSignByte)>>(8-kSignTagBit)); + _clip_unpackUIntWithTagTo(&absDelta,clip,kSignTagBit); + if (isNeg) + *pLastOldPos-=absDelta; + else + *pLastOldPos+=absDelta; + return hpatch_TRUE; + } + +static hpatch_BOOL _covers_read_cover(hpatch_TCovers* covers,hpatch_TCover* out_cover){ + _TCovers* self=(_TCovers*)covers; + hpatch_StreamPos_t oldPosBack=self->oldPosBack; + hpatch_StreamPos_t newPosBack=self->newPosBack; + hpatch_StreamPos_t coverCount=self->coverCount; + if (coverCount>0) + self->coverCount=coverCount-1; + else + return _hpatch_FALSE; + + { + hpatch_StreamPos_t copyLength,coverLength; + if (!_read_sign_pos_byLastPos(self->code_inc_oldPosClip,&oldPosBack)) + return _hpatch_FALSE; + _clip_unpackUIntTo(©Length,self->code_inc_newPosClip); + _clip_unpackUIntTo(&coverLength,self->code_lengthsClip); + newPosBack+=copyLength; + + out_cover->oldPos=oldPosBack; + out_cover->newPos=newPosBack; + out_cover->length=coverLength; + oldPosBack+=(self->isOldPosBackNeedAddLength)?coverLength:0; + newPosBack+=coverLength; + } + self->oldPosBack=oldPosBack; + self->newPosBack=newPosBack; + return hpatch_TRUE; +} + +static hpatch_BOOL _covers_is_finish(const struct hpatch_TCovers* covers){ + _TCovers* self=(_TCovers*)covers; + return _TStreamCacheClip_isFinish(self->code_lengthsClip) + && _TStreamCacheClip_isFinish(self->code_inc_newPosClip) + && _TStreamCacheClip_isFinish(self->code_inc_oldPosClip); +} + + +static void _covers_init(_TCovers* covers,hpatch_StreamPos_t coverCount, + TStreamCacheClip* code_inc_oldPosClip, + TStreamCacheClip* code_inc_newPosClip, + TStreamCacheClip* code_lengthsClip, + hpatch_BOOL isOldPosBackNeedAddLength){ + covers->ICovers.leave_cover_count=_covers_leaveCoverCount; + covers->ICovers.read_cover=_covers_read_cover; + covers->ICovers.is_finish=_covers_is_finish; + covers->ICovers.close=_covers_close_nil; + covers->coverCount=coverCount; + covers->newPosBack=0; + covers->oldPosBack=0; + covers->code_inc_oldPosClip=code_inc_oldPosClip; + covers->code_inc_newPosClip=code_inc_newPosClip; + covers->code_lengthsClip=code_lengthsClip; + covers->isOldPosBackNeedAddLength=isOldPosBackNeedAddLength; +} + +static hpatch_BOOL _rle_decode_skip(struct _TBytesRle_load_stream* rle_loader,hpatch_StreamPos_t copyLength){ + while (copyLength>0) { + hpatch_size_t len=(~(hpatch_size_t)0); + if (len>copyLength) + len=(hpatch_size_t)copyLength; + if (!_TBytesRle_load_stream_decode_skip(rle_loader,len)) return _hpatch_FALSE; + copyLength-=len; + } + return hpatch_TRUE; +} + +static hpatch_BOOL patchByClip(_TOutStreamCache* outCache, + const hpatch_TStreamInput* oldData, + hpatch_TCovers* covers, + TStreamCacheClip* code_newDataDiffClip, + struct _TBytesRle_load_stream* rle_loader, + TByte* temp_cache,hpatch_size_t cache_size){ + const hpatch_StreamPos_t newDataSize=_TOutStreamCache_leaveSize(outCache); + const hpatch_StreamPos_t oldDataSize=oldData->streamSize; + hpatch_StreamPos_t coverCount=covers->leave_cover_count(covers); + hpatch_StreamPos_t newPosBack=0; + assert(cache_size>=hpatch_kMaxPackedUIntBytes); + + while (coverCount--){ + hpatch_TCover cover; + if(!covers->read_cover(covers,&cover)) return _hpatch_FALSE; +#ifdef __RUN_MEM_SAFE_CHECK + if (cover.newPos(hpatch_StreamPos_t)(newDataSize-cover.newPos)) return _hpatch_FALSE; + if (cover.oldPos>oldDataSize) return _hpatch_FALSE; + if (cover.length>(hpatch_StreamPos_t)(oldDataSize-cover.oldPos)) return _hpatch_FALSE; +#endif + if (newPosBackis_finish(covers) + && _TOutStreamCache_isFinish(outCache) + && _TStreamCacheClip_isFinish(code_newDataDiffClip) + && (newPosBack==newDataSize) ) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + + +#define _kCachePatCount 8 + +#define _cache_alloc(dst,dst_type,memSize,temp_cache,temp_cache_end){ \ + if ((hpatch_size_t)(temp_cache_end-temp_cache) < \ + sizeof(hpatch_StreamPos_t)+(memSize)) return hpatch_FALSE; \ + (dst)=(dst_type*)_hpatch_align_upper(temp_cache,sizeof(hpatch_StreamPos_t));\ + temp_cache=(TByte*)(dst)+(hpatch_size_t)(memSize); \ +} + +typedef struct _TPackedCovers{ + _TCovers base; + TStreamCacheClip code_inc_oldPosClip; + TStreamCacheClip code_inc_newPosClip; + TStreamCacheClip code_lengthsClip; +} _TPackedCovers; + +typedef struct _THDiffHead{ + hpatch_StreamPos_t coverCount; + hpatch_StreamPos_t lengthSize; + hpatch_StreamPos_t inc_newPosSize; + hpatch_StreamPos_t inc_oldPosSize; + hpatch_StreamPos_t newDataDiffSize; + hpatch_StreamPos_t headEndPos; + hpatch_StreamPos_t coverEndPos; +} _THDiffHead; + +static hpatch_BOOL read_diff_head(_THDiffHead* out_diffHead, + const hpatch_TStreamInput* serializedDiff){ + hpatch_StreamPos_t diffPos0; + const hpatch_StreamPos_t diffPos_end=serializedDiff->streamSize; + TByte temp_cache[hpatch_kStreamCacheSize]; + TStreamCacheClip diffHeadClip; + _TStreamCacheClip_init(&diffHeadClip,serializedDiff,0,diffPos_end,temp_cache,hpatch_kStreamCacheSize); + _clip_unpackUIntTo(&out_diffHead->coverCount,&diffHeadClip); + _clip_unpackUIntTo(&out_diffHead->lengthSize,&diffHeadClip); + _clip_unpackUIntTo(&out_diffHead->inc_newPosSize,&diffHeadClip); + _clip_unpackUIntTo(&out_diffHead->inc_oldPosSize,&diffHeadClip); + _clip_unpackUIntTo(&out_diffHead->newDataDiffSize,&diffHeadClip); + diffPos0=(hpatch_StreamPos_t)(_TStreamCacheClip_readPosOfSrcStream(&diffHeadClip)); + out_diffHead->headEndPos=diffPos0; +#ifdef __RUN_MEM_SAFE_CHECK + if (out_diffHead->lengthSize>(hpatch_StreamPos_t)(diffPos_end-diffPos0)) return _hpatch_FALSE; +#endif + diffPos0+=out_diffHead->lengthSize; +#ifdef __RUN_MEM_SAFE_CHECK + if (out_diffHead->inc_newPosSize>(hpatch_StreamPos_t)(diffPos_end-diffPos0)) return _hpatch_FALSE; +#endif + diffPos0+=out_diffHead->inc_newPosSize; +#ifdef __RUN_MEM_SAFE_CHECK + if (out_diffHead->inc_oldPosSize>(hpatch_StreamPos_t)(diffPos_end-diffPos0)) return _hpatch_FALSE; +#endif + diffPos0+=out_diffHead->inc_oldPosSize; + out_diffHead->coverEndPos=diffPos0; +#ifdef __RUN_MEM_SAFE_CHECK + if (out_diffHead->newDataDiffSize>(hpatch_StreamPos_t)(diffPos_end-diffPos0)) return _hpatch_FALSE; +#endif + return hpatch_TRUE; +} + +static hpatch_BOOL _packedCovers_open(_TPackedCovers** out_self, + _THDiffHead* out_diffHead, + const hpatch_TStreamInput* serializedDiff, + TByte* temp_cache,TByte* temp_cache_end){ + hpatch_size_t cacheSize; + _TPackedCovers* self=0; + _cache_alloc(self,_TPackedCovers,sizeof(_TPackedCovers),temp_cache,temp_cache_end); + cacheSize=(temp_cache_end-temp_cache)/3; + { + hpatch_StreamPos_t diffPos0; + if (!read_diff_head(out_diffHead,serializedDiff)) return _hpatch_FALSE; + diffPos0=out_diffHead->headEndPos; + _TStreamCacheClip_init(&self->code_lengthsClip,serializedDiff,diffPos0, + diffPos0+out_diffHead->lengthSize,temp_cache,cacheSize); + temp_cache+=cacheSize; + diffPos0+=out_diffHead->lengthSize; + _TStreamCacheClip_init(&self->code_inc_newPosClip,serializedDiff,diffPos0, + diffPos0+out_diffHead->inc_newPosSize,temp_cache,cacheSize); + temp_cache+=cacheSize; + diffPos0+=out_diffHead->inc_newPosSize; + _TStreamCacheClip_init(&self->code_inc_oldPosClip,serializedDiff,diffPos0, + diffPos0+out_diffHead->inc_oldPosSize,temp_cache,cacheSize); + } + + _covers_init(&self->base,out_diffHead->coverCount,&self->code_inc_oldPosClip, + &self->code_inc_newPosClip,&self->code_lengthsClip,hpatch_FALSE); + *out_self=self; + return hpatch_TRUE; +} + +static hpatch_BOOL _patch_stream_with_cache(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* serializedDiff, + hpatch_TCovers* cached_covers, + TByte* temp_cache,TByte* temp_cache_end){ + struct _THDiffHead diffHead; + TStreamCacheClip code_newDataDiffClip; + struct _TBytesRle_load_stream rle_loader; + hpatch_TCovers* pcovers=0; + hpatch_StreamPos_t diffPos0; + const hpatch_StreamPos_t diffPos_end=serializedDiff->streamSize; + const hpatch_size_t cacheSize=(temp_cache_end-temp_cache)/(cached_covers?(_kCachePatCount-3):_kCachePatCount); + + assert(out_newData!=0); + assert(out_newData->write!=0); + assert(oldData!=0); + assert(oldData->read!=0); + assert(serializedDiff!=0); + assert(serializedDiff->read!=0); + + //covers + if (cached_covers==0){ + struct _TPackedCovers* packedCovers; + if (!_packedCovers_open(&packedCovers,&diffHead,serializedDiff,temp_cache+cacheSize*(_kCachePatCount-3), + temp_cache_end)) return _hpatch_FALSE; + pcovers=&packedCovers->base.ICovers; //not need close before return + }else{ + pcovers=cached_covers; + if (!read_diff_head(&diffHead,serializedDiff)) return _hpatch_FALSE; + } + //newDataDiff + diffPos0=diffHead.coverEndPos; + _TStreamCacheClip_init(&code_newDataDiffClip,serializedDiff,diffPos0, + diffPos0+diffHead.newDataDiffSize,temp_cache,cacheSize); + temp_cache+=cacheSize; + diffPos0+=diffHead.newDataDiffSize; + + {//rle + hpatch_StreamPos_t rleCtrlSize; + hpatch_StreamPos_t rlePos0; + TStreamCacheClip* rleHeadClip=&rle_loader.ctrlClip;//rename, share address +#ifdef __RUN_MEM_SAFE_CHECK + if (cacheSize(hpatch_StreamPos_t)(diffPos_end-rlePos0)) return _hpatch_FALSE; +#endif + _TBytesRle_load_stream_init(&rle_loader); + _TStreamCacheClip_init(&rle_loader.ctrlClip,serializedDiff,rlePos0,rlePos0+rleCtrlSize, + temp_cache,cacheSize); + temp_cache+=cacheSize; + _TStreamCacheClip_init(&rle_loader.rleCodeClip,serializedDiff,rlePos0+rleCtrlSize,diffPos_end, + temp_cache,cacheSize); + temp_cache+=cacheSize; + } + { + _TOutStreamCache outCache; + _TOutStreamCache_init(&outCache,out_newData,temp_cache,cacheSize); + temp_cache+=cacheSize; + return patchByClip(&outCache,oldData,pcovers,&code_newDataDiffClip, + &rle_loader,temp_cache,cacheSize); + } +} + + +hpatch_BOOL read_diffz_head(hpatch_compressedDiffInfo* out_diffInfo,_THDiffzHead* out_head, + const hpatch_TStreamInput* compressedDiff){ + TStreamCacheClip _diffHeadClip; + TStreamCacheClip* diffHeadClip=&_diffHeadClip; + TByte temp_cache[hpatch_kStreamCacheSize]; + _TStreamCacheClip_init(&_diffHeadClip,compressedDiff,0,compressedDiff->streamSize, + temp_cache,hpatch_kStreamCacheSize); + {//type + const char* kVersionType="HDIFF13"; + char* tempType=out_diffInfo->compressType; + if (!_TStreamCacheClip_readType_end(diffHeadClip,'&',tempType)) return _hpatch_FALSE; + if (0!=strcmp(tempType,kVersionType)) return hpatch_FALSE; + } + {//read compressType + if (!_TStreamCacheClip_readType_end(diffHeadClip,'\0', + out_diffInfo->compressType)) return _hpatch_FALSE; + out_head->typesEndPos=_TStreamCacheClip_readPosOfSrcStream(diffHeadClip); + } + _clip_unpackUIntTo(&out_diffInfo->newDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->oldDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_head->coverCount,diffHeadClip); + out_head->compressSizeBeginPos=_TStreamCacheClip_readPosOfSrcStream(diffHeadClip); + _clip_unpackUIntTo(&out_head->cover_buf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->compress_cover_buf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->rle_ctrlBuf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->compress_rle_ctrlBuf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->rle_codeBuf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->compress_rle_codeBuf_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->newDataDiff_size,diffHeadClip); + _clip_unpackUIntTo(&out_head->compress_newDataDiff_size,diffHeadClip); + out_head->headEndPos=_TStreamCacheClip_readPosOfSrcStream(diffHeadClip); + + out_diffInfo->compressedCount=((out_head->compress_cover_buf_size)?1:0) + +((out_head->compress_rle_ctrlBuf_size)?1:0) + +((out_head->compress_rle_codeBuf_size)?1:0) + +((out_head->compress_newDataDiff_size)?1:0); + if (out_head->compress_cover_buf_size>0) + out_head->coverEndPos=out_head->headEndPos+out_head->compress_cover_buf_size; + else + out_head->coverEndPos=out_head->headEndPos+out_head->cover_buf_size; + return hpatch_TRUE; +} + +hpatch_BOOL getCompressedDiffInfo(hpatch_compressedDiffInfo* out_diffInfo, + const hpatch_TStreamInput* compressedDiff){ + _THDiffzHead head; + assert(out_diffInfo!=0); + assert(compressedDiff!=0); + assert(compressedDiff->read!=0); + return read_diffz_head(out_diffInfo,&head,compressedDiff); +} + +#define _clear_return(exitValue) { result=exitValue; goto clear; } + +#define _kCacheDecCount 6 + +static +hpatch_BOOL _patch_decompress_cache(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin, + hpatch_TCovers* cached_covers, + TByte* temp_cache, TByte* temp_cache_end){ + TStreamCacheClip coverClip; + TStreamCacheClip code_newDataDiffClip; + struct _TBytesRle_load_stream rle_loader; + _THDiffzHead head; + hpatch_compressedDiffInfo diffInfo; + _TDecompressInputStream decompressers[4]; + hpatch_size_t i; + hpatch_StreamPos_t coverCount; + hpatch_BOOL result=hpatch_TRUE; + hpatch_StreamPos_t diffPos0=0; + const hpatch_StreamPos_t diffPos_end=compressedDiff->streamSize; + const hpatch_size_t cacheSize=(temp_cache_end-temp_cache)/(cached_covers?(_kCacheDecCount-1):_kCacheDecCount); + if (cacheSize<=hpatch_kMaxPluginTypeLength) return _hpatch_FALSE; + assert(out_newData!=0); + assert(out_newData->write!=0); + assert(oldData!=0); + assert(oldData->read!=0); + assert(compressedDiff!=0); + assert(compressedDiff->read!=0); + {//head + if (!read_diffz_head(&diffInfo,&head,compressedDiff)) return _hpatch_FALSE; + if ((diffInfo.oldDataSize!=oldData->streamSize) + ||(diffInfo.newDataSize!=out_newData->streamSize)) return _hpatch_FALSE; + + if ((decompressPlugin==0)&&(diffInfo.compressedCount!=0)) return _hpatch_FALSE; + if ((decompressPlugin)&&(diffInfo.compressedCount>0)) + if (!decompressPlugin->is_can_open(diffInfo.compressType)) return _hpatch_FALSE; + diffPos0=head.headEndPos; + } + + for (i=0;iclose(pcovers))) result=_hpatch_FALSE; + } +clear: + for (i=0;iclose(decompressPlugin,decompressers[i].decompressHandle)) + result=_hpatch_FALSE; + decompressers[i].decompressHandle=0; + } + } + return result; +} + + +hpatch_inline static hpatch_BOOL _cache_load_all(const hpatch_TStreamInput* data, + TByte* cache,TByte* cache_end){ + assert((hpatch_size_t)(cache_end-cache)==data->streamSize); + return data->read(data,0,cache,cache_end); +} + +typedef struct _TCompressedCovers{ + _TCovers base; + TStreamCacheClip coverClip; + _TDecompressInputStream decompresser; +} _TCompressedCovers; + +static hpatch_BOOL _compressedCovers_close(hpatch_TCovers* covers){ + hpatch_BOOL result=hpatch_TRUE; + _TCompressedCovers* self=(_TCompressedCovers*)covers; + if (self){ + if (self->decompresser.decompressHandle){ + result=self->decompresser.decompressPlugin->close(self->decompresser.decompressPlugin, + self->decompresser.decompressHandle); + self->decompresser.decompressHandle=0; + } + } + return result; +} + +static hpatch_BOOL _compressedCovers_open(_TCompressedCovers** out_self, + hpatch_compressedDiffInfo* out_diffInfo, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin, + TByte* temp_cache,TByte* temp_cache_end){ + _THDiffzHead head; + hpatch_StreamPos_t diffPos0=0; + _TCompressedCovers* self=0; + _cache_alloc(self,_TCompressedCovers,sizeof(_TCompressedCovers),temp_cache,temp_cache_end); + if (!read_diffz_head(out_diffInfo,&head,compressedDiff)) return _hpatch_FALSE; + diffPos0=head.headEndPos; + if (head.compress_cover_buf_size>0){ + if (decompressPlugin==0) return _hpatch_FALSE; + if (!decompressPlugin->is_can_open(out_diffInfo->compressType)) return _hpatch_FALSE; + } + + _covers_init(&self->base,head.coverCount,&self->coverClip, + &self->coverClip,&self->coverClip,hpatch_TRUE); + self->base.ICovers.close=_compressedCovers_close; + memset(&self->decompresser,0, sizeof(self->decompresser)); + if (!getStreamClip(&self->coverClip,&self->decompresser, + head.cover_buf_size,head.compress_cover_buf_size, + compressedDiff,&diffPos0,decompressPlugin, + temp_cache,temp_cache_end-temp_cache)) { + return _hpatch_FALSE; + }; + *out_self=self; + return hpatch_TRUE; +} + + +//kMinSkipSpaceLen: skip space of length seekTime*speed if time-efficient, otherwise sequential access; +// SSD: 4K-64K HHD: 1M +const hpatch_size_t kMinSkipSpaceLen =1024*256; //default 256k + +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + +typedef struct _TArrayCovers{ + hpatch_TCovers ICovers; + void* pCCovers; + hpatch_size_t coverCount; + hpatch_size_t cur_index; + hpatch_BOOL is32; +} _TArrayCovers; + + +typedef struct hpatch_TCCover32{ + hpatch_uint32_t oldPos; + hpatch_uint32_t newPos; + hpatch_uint32_t length; + hpatch_uint32_t cachePos; //Consider moving to temporary memory and releasing after use? Logic would be more complex; +} hpatch_TCCover32; + +typedef struct hpatch_TCCover64{ + hpatch_StreamPos_t oldPos; + hpatch_StreamPos_t newPos; + hpatch_StreamPos_t length; + hpatch_StreamPos_t cachePos; +} hpatch_TCCover64; + +#define _arrayCovers_get(self,i,item) (((self)->is32)? \ + ((const hpatch_uint32_t*)(self)->pCCovers)[(i)*4+(item)]:\ + ((const hpatch_StreamPos_t*)(self)->pCCovers)[(i)*4+(item)]) +#define _arrayCovers_get_oldPos(self,i) _arrayCovers_get(self,i,0) +#define _arrayCovers_get_len(self,i) _arrayCovers_get(self,i,2) +#define _arrayCovers_get_cachePos(self,i) _arrayCovers_get(self,i,3) + +#define _arrayCovers_set(self,i,item,v) { if ((self)->is32){ \ + ((hpatch_uint32_t*)(self)->pCCovers)[(i)*4+(item)]=(hpatch_uint32_t)(v); }else{ \ + ((hpatch_StreamPos_t*)(self)->pCCovers)[(i)*4+(item)]=(v); } } +#define _arrayCovers_set_cachePos(self,i,v) _arrayCovers_set(self,i,3,v) + +hpatch_inline static hpatch_StreamPos_t arrayCovers_memSize(hpatch_StreamPos_t coverCount,hpatch_BOOL is32){ + return coverCount*(is32?sizeof(hpatch_TCCover32):sizeof(hpatch_TCCover64)); +} + +static hpatch_BOOL _arrayCovers_is_finish(const hpatch_TCovers* covers){ + const _TArrayCovers* self=(const _TArrayCovers*)covers; + return (self->coverCount==self->cur_index); +} +static hpatch_StreamPos_t _arrayCovers_leaveCoverCount(const hpatch_TCovers* covers){ + const _TArrayCovers* self=(const _TArrayCovers*)covers; + return self->coverCount-self->cur_index; +} +static hpatch_BOOL _arrayCovers_read_cover(struct hpatch_TCovers* covers,hpatch_TCover* out_cover){ + _TArrayCovers* self=(_TArrayCovers*)covers; + hpatch_size_t i=self->cur_index; + if (icoverCount){ + if (self->is32){ + const hpatch_TCCover32* pCover=((const hpatch_TCCover32*)self->pCCovers)+i; + out_cover->oldPos=pCover->oldPos; + out_cover->newPos=pCover->newPos; + out_cover->length=pCover->length; + }else{ + const hpatch_TCCover64* pCover=((const hpatch_TCCover64*)self->pCCovers)+i; + out_cover->oldPos=pCover->oldPos; + out_cover->newPos=pCover->newPos; + out_cover->length=pCover->length; + } + self->cur_index=i+1; + return hpatch_TRUE; + }else{ + return _hpatch_FALSE; + } +} + +static hpatch_force_inline void _arrayCovers_push_cover(_TArrayCovers* self,const hpatch_TCover* cover){ + hpatch_size_t i=self->coverCount; + if (self->is32){ + hpatch_TCCover32* pCover=((hpatch_TCCover32*)self->pCCovers)+i; + pCover->oldPos=(hpatch_uint32_t)cover->oldPos; + pCover->newPos=(hpatch_uint32_t)cover->newPos; + pCover->length=(hpatch_uint32_t)cover->length; + }else{ + hpatch_TCCover64* pCover=((hpatch_TCCover64*)self->pCCovers)+i; + pCover->oldPos=cover->oldPos; + pCover->newPos=cover->newPos; + pCover->length=cover->length; + } + self->coverCount=i+1; +} + +static hpatch_BOOL _arrayCovers_load(_TArrayCovers** out_self,hpatch_TCovers* src_covers, + hpatch_BOOL isUsedCover32,hpatch_BOOL* out_isReadError, + TByte** ptemp_cache,TByte* temp_cache_end){ + TByte* temp_cache=*ptemp_cache; + hpatch_StreamPos_t _coverCount=src_covers->leave_cover_count(src_covers); + hpatch_StreamPos_t memSize=arrayCovers_memSize(_coverCount,isUsedCover32); + hpatch_size_t i; + void* pCovers; + _TArrayCovers* self=0; + hpatch_size_t coverCount=(hpatch_size_t)_coverCount; + + *out_isReadError=hpatch_FALSE; + if (coverCount!=_coverCount) return hpatch_FALSE; + + _cache_alloc(self,_TArrayCovers,sizeof(_TArrayCovers),temp_cache,temp_cache_end); + _cache_alloc(pCovers,void,memSize,temp_cache,temp_cache_end); + if (isUsedCover32){ + hpatch_TCCover32* pdst=(hpatch_TCCover32*)pCovers; + for (i=0;iread_cover(src_covers,&cover)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + pdst->oldPos=(hpatch_uint32_t)cover.oldPos; + pdst->newPos=(hpatch_uint32_t)cover.newPos; + pdst->length=(hpatch_uint32_t)cover.length; + } + }else{ + hpatch_TCCover64* pdst=(hpatch_TCCover64*)pCovers; + for (i=0;iread_cover(src_covers,(hpatch_TCover*)pdst)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + } + } + if (!src_covers->is_finish(src_covers)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + + self->pCCovers=pCovers; + self->is32=isUsedCover32; + self->coverCount=coverCount; + self->cur_index=0; + self->ICovers.close=_covers_close_nil; + self->ICovers.is_finish=_arrayCovers_is_finish; + self->ICovers.leave_cover_count=_arrayCovers_leaveCoverCount; + self->ICovers.read_cover=_arrayCovers_read_cover; + *out_self=self; + *ptemp_cache=temp_cache; + return hpatch_TRUE; +} + +#define _arrayCovers_comp(_uint_t,_x,_y,item){ \ + _uint_t x=((const _uint_t*)_x)[item]; \ + _uint_t y=((const _uint_t*)_y)[item]; \ + return (xy)?1:0); \ +} +#ifdef _MSC_VER +# define __CALL_BACK_C __cdecl +#else +# define __CALL_BACK_C +#endif +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_old_32(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_uint32_t,_x,_y,0); +} +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_old(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_StreamPos_t,_x,_y,0); +} +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_new_32(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_uint32_t,_x,_y,1); +} +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_new(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_StreamPos_t,_x,_y,1); +} +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_len_32(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_uint32_t,_x,_y,2); +} +static hpatch_int __CALL_BACK_C _arrayCovers_comp_by_len(const void* _x, const void *_y){ + _arrayCovers_comp(hpatch_StreamPos_t,_x,_y,2); +} + +hpatch_force_inline +static void _arrayCovers_sort_by_old(_TArrayCovers* self){ + if (self->is32) + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover32),_arrayCovers_comp_by_old_32); + else + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover64),_arrayCovers_comp_by_old); +} +hpatch_force_inline +static void _arrayCovers_sort_by_new(_TArrayCovers* self){ + if (self->is32) + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover32),_arrayCovers_comp_by_new_32); + else + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover64),_arrayCovers_comp_by_new); +} +hpatch_force_inline +static void _arrayCovers_sort_by_len(_TArrayCovers* self){ + if (self->is32) + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover32),_arrayCovers_comp_by_len_32); + else + qsort(self->pCCovers,self->coverCount,sizeof(hpatch_TCCover64),_arrayCovers_comp_by_len); +} + +static hpatch_StreamPos_t _getCacheSumLen(const _TArrayCovers* src_covers,hpatch_StreamPos_t maxCachedLen){ + const hpatch_size_t coverCount=src_covers->coverCount; + hpatch_StreamPos_t sumLen=0; + hpatch_size_t i; + for (i=0; icoverCount; + hpatch_size_t i; + _arrayCovers_sort_by_len(arrayCovers); + + for (i=0; ikMaxCachedLen) + mlen=kMaxCachedLen; + _arrayCovers_sort_by_new(arrayCovers); + return (hpatch_size_t)mlen; +} + +static hpatch_size_t _set_cache_pos(_TArrayCovers* covers,hpatch_size_t maxCachedLen, + hpatch_StreamPos_t* poldPosBegin,hpatch_StreamPos_t* poldPosEnd, + hpatch_size_t kMinCacheCoverCount){ + const hpatch_size_t coverCount=covers->coverCount; + hpatch_StreamPos_t oldPosBegin=hpatch_kNullStreamPos; + hpatch_StreamPos_t oldPosEnd=0; + hpatch_size_t cacheCoverCount=0; + hpatch_size_t sum=0;//result + hpatch_size_t i; + for (i=0; ioldPosEnd) oldPosEnd=oldPos+clen; + } + } + if (cacheCoverCountcoverCount; + TByte* cache_buf=old_cache_end; + assert((hpatch_size_t)(old_cache_end-old_cache)>=sumCacheLen); + + if ((hpatch_size_t)(cache_buf_end-cache_buf)>=kAccessPageSize*2){ + cache_buf=(TByte*)_hpatch_align_upper(cache_buf,kAccessPageSize); + if ((hpatch_size_t)(cache_buf_end-cache_buf)>=(kMinSkipSpaceLen>>1)) + cache_buf_end=cache_buf+(kMinSkipSpaceLen>>1); + else + cache_buf_end=(TByte*)_hpatch_align_lower(cache_buf_end,kAccessPageSize); + } + oldPos=_hpatch_align_type_lower(hpatch_StreamPos_t,oldPos,kAccessPageSize); + if (oldPos(oldPosAllEnd-oldPos)) readLen=(hpatch_size_t)(oldPosAllEnd-oldPos); + if (!oldData->read(oldData,oldPos,cache_buf, + cache_buf+readLen)) { result=_hpatch_FALSE; break; } //error + oldPosEnd=oldPos+readLen; + for (i=cur_i;imaxCachedLen){//cover line too long to cache, proceed to next cover line; + if (i==cur_i) + ++cur_i; + continue; + } + ioldPos=_arrayCovers_get_oldPos(arrayCovers,i); + ioldPosEnd=ioldPos+ilen; + if (ioldPosEnd>oldPos){ + // [oldPos oldPosEnd] + // ioldPosEnd]----or----] + if (ioldPos=oldPos){ + // [ioldPos ioldPosEnd]----or----] + from=ioldPos; + }else{ + // [ioldPos ioldPosEnd]----or----] + from=oldPos; + dstPos+=(oldPos-ioldPos); + } + copyLen=(hpatch_size_t)(((ioldPosEnd<=oldPosEnd)?ioldPosEnd:oldPosEnd)-from); + //assert(dstPos+copyLen<=(hpatch_size_t)(old_cache_end-old_cache)); + //assert(sumCacheLen>=copyLen); + memcpy(old_cache+(hpatch_size_t)dstPos,cache_buf+(from-oldPos),copyLen); + sumCacheLen-=copyLen; + if ((i==cur_i)&&(oldPosEnd>=ioldPosEnd)) + ++cur_i; + }else{//no more intersections with current data for following cover lines, move to next data block; + // [oldPos oldPosEnd] + // [ioldPos ioldPosEnd] + if ((i==cur_i)&&(ioldPos-oldPosEnd>=kMinSkipSpaceLen)) + oldPosEnd=_hpatch_align_type_lower(hpatch_StreamPos_t,ioldPos,kAccessPageSize); + break; + } + }else{//current cover line is behind current data, proceed to next cover line; + // [oldPos oldPosEnd] + // [ioldPos ioldPosEnd] + if (i==cur_i) + ++cur_i; + } + } + oldPos=oldPosEnd; + } + _arrayCovers_sort_by_new(arrayCovers); + assert(sumCacheLen==0); + return result; +} + +typedef struct _cache_old_TStreamInput{ + _TArrayCovers arrayCovers; + hpatch_BOOL isInHitCache; + hpatch_size_t maxCachedLen; + hpatch_StreamPos_t readFromPos; + hpatch_StreamPos_t readFromPosEnd; + const TByte* caches; + const TByte* cachesEnd; + const hpatch_TStreamInput* oldData; + void* _cacheImport; + hpatch_BOOL (*_doUpdateCacheCovers)(void* _cacheImport); +} _cache_old_TStreamInput; + +static hpatch_BOOL _cache_old_StreamInput_read(const hpatch_TStreamInput* stream, + hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + _cache_old_TStreamInput* self=(_cache_old_TStreamInput*)stream->streamImport; + hpatch_size_t readLen; + hpatch_StreamPos_t dataLen=(hpatch_size_t)(self->readFromPosEnd-self->readFromPos); + if (dataLen==0){//next cover + hpatch_StreamPos_t oldPos; + hpatch_size_t i=self->arrayCovers.cur_index++; + if (i>=self->arrayCovers.coverCount){ + if ((self->_doUpdateCacheCovers)&&(self->_doUpdateCacheCovers(self->_cacheImport))){ + i=self->arrayCovers.cur_index++; + if (i>=self->arrayCovers.coverCount) + return _hpatch_FALSE;//update error; + }else + return _hpatch_FALSE;//error; + } + oldPos=_arrayCovers_get_oldPos(&self->arrayCovers,i); + dataLen=_arrayCovers_get_len(&self->arrayCovers,i); + self->isInHitCache=(dataLen<=self->maxCachedLen); + self->readFromPos=oldPos; + self->readFromPosEnd=oldPos+dataLen; + } + readLen=out_data_end-out_data; + if ((readLen>dataLen)||(self->readFromPos!=readFromPos)) return _hpatch_FALSE; //error + self->readFromPos=readFromPos+readLen; + if (self->isInHitCache){ + assert(readLen<=(hpatch_size_t)(self->cachesEnd-self->caches)); + memcpy(out_data,self->caches,readLen); + self->caches+=readLen; + return hpatch_TRUE; + }else{ + return self->oldData->read(self->oldData,readFromPos,out_data,out_data_end); + } +} + +static hpatch_BOOL _cache_old(hpatch_TStreamInput** out_cachedOld,const hpatch_TStreamInput* oldData, + _TArrayCovers* arrayCovers,hpatch_BOOL* out_isReadError, + TByte* temp_cache,TByte** ptemp_cache_end,TByte* cache_buf_end){ + _cache_old_TStreamInput* self; + TByte* temp_cache_end=*ptemp_cache_end; + hpatch_StreamPos_t oldPosBegin; + hpatch_StreamPos_t oldPosEnd; + hpatch_size_t sumCacheLen; + hpatch_size_t maxCachedLen; + const hpatch_size_t kMinCacheCoverCount=arrayCovers->coverCount/8+1; //control min cache count, otherwise caching becomes ineffective; + *out_isReadError=hpatch_FALSE; + _cache_alloc(*out_cachedOld,hpatch_TStreamInput,sizeof(hpatch_TStreamInput), + temp_cache,temp_cache_end); + _cache_alloc(self,_cache_old_TStreamInput,sizeof(_cache_old_TStreamInput), + temp_cache,temp_cache_end); + + maxCachedLen=_getMaxCachedLen(arrayCovers,temp_cache_end-temp_cache); + if (maxCachedLen==0) return hpatch_FALSE; + sumCacheLen=_set_cache_pos(arrayCovers,maxCachedLen,&oldPosBegin,&oldPosEnd,kMinCacheCoverCount); + if (sumCacheLen==0) return hpatch_FALSE; + temp_cache_end=temp_cache+sumCacheLen; + + if (!_cache_old_load(oldData,oldPosBegin,oldPosEnd,arrayCovers,maxCachedLen,sumCacheLen, + temp_cache,temp_cache_end,cache_buf_end)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + + {//out + self->arrayCovers=*arrayCovers; + self->arrayCovers.cur_index=0; + self->isInHitCache=hpatch_FALSE; + self->maxCachedLen=maxCachedLen; + self->caches=temp_cache; + self->cachesEnd=temp_cache_end; + self->readFromPos=0; + self->readFromPosEnd=0; + self->oldData=oldData; + (*out_cachedOld)->streamImport=self; + (*out_cachedOld)->streamSize=oldData->streamSize; + (*out_cachedOld)->read=_cache_old_StreamInput_read; + *ptemp_cache_end=temp_cache_end; + } + return hpatch_TRUE; +} + +#endif //_IS_NEED_CACHE_OLD_BY_COVERS + +#if (_IS_NEED_CACHE_OLD_ALL) +hpatch_BOOL _patch_cache_all_old(const hpatch_TStreamInput** poldData,size_t kMinTempCacheSize, + TByte** ptemp_cache,TByte** ptemp_cache_end,hpatch_BOOL* out_isReadError){ + const hpatch_TStreamInput* oldData=*poldData; + TByte* temp_cache=*ptemp_cache; + TByte* temp_cache_end=*ptemp_cache_end; + *out_isReadError=hpatch_FALSE; + if (_patch_is_can_cache_all_old(oldData->streamSize,kMinTempCacheSize,temp_cache_end-temp_cache)){//load all oldData + hpatch_TStreamInput* replace_oldData=0; + _cache_alloc(replace_oldData,hpatch_TStreamInput,sizeof(hpatch_TStreamInput), + temp_cache,temp_cache_end); + if (!_cache_load_all(oldData,temp_cache_end-oldData->streamSize, + temp_cache_end)){ *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + + mem_as_hStreamInput(replace_oldData,temp_cache_end-oldData->streamSize,temp_cache_end); + temp_cache_end-=oldData->streamSize; + // [ patch cache | oldData cache ] + // [ (cacheSize-oldData->streamSize) | (oldData->streamSize) ] + *poldData=replace_oldData; + *ptemp_cache=temp_cache; + *ptemp_cache_end=temp_cache_end; + return hpatch_TRUE; + } + return hpatch_FALSE; +} +#endif //_IS_NEED_CACHE_OLD_ALL + +static hpatch_BOOL _patch_cache(hpatch_TCovers** out_covers, + const hpatch_TStreamInput** poldData,hpatch_StreamPos_t newDataSize, + const hpatch_TStreamInput* diffData,hpatch_BOOL isCompressedDiff, + hpatch_TDecompress* decompressPlugin,size_t kCacheCount, + TByte** ptemp_cache,TByte** ptemp_cache_end,hpatch_BOOL* out_isReadError){ + const hpatch_TStreamInput* oldData=*poldData; +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + const hpatch_size_t kBestACacheSize=hpatch_kFileIOBufBetterSize; //optimal hpatch_kStreamCacheSize value when sufficient memory is available; + const hpatch_size_t _minActiveSize=(1<<20)*3+kBestACacheSize*kCacheCount*2; + const hpatch_StreamPos_t _betterActiveSize=oldData->streamSize/16+kBestACacheSize*kCacheCount*2; + const hpatch_size_t kActiveCacheOldMemorySize = //min memory threshold for attempting to activate CacheOld functionality; + (_minActiveSize>_betterActiveSize)?_minActiveSize:(hpatch_size_t)_betterActiveSize; +#endif //_IS_NEED_CACHE_OLD_BY_COVERS + TByte* temp_cache=*ptemp_cache; + TByte* temp_cache_end=*ptemp_cache_end; + *out_covers=0; + if (_patch_cache_all_old(poldData,kCacheCount*hpatch_kStreamCacheSize,ptemp_cache,ptemp_cache_end,out_isReadError)) + return hpatch_TRUE;//cache all oldData + if (*out_isReadError) return hpatch_FALSE; +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + if ((hpatch_size_t)(temp_cache_end-temp_cache)>=kActiveCacheOldMemorySize) {// try cache part of oldData + hpatch_BOOL isUsedCover32; + TByte* temp_cache_end_back=temp_cache_end; + _TArrayCovers* arrayCovers=0; + assert((hpatch_size_t)(temp_cache_end-temp_cache)>kBestACacheSize*kCacheCount); + assert(kBestACacheSize>sizeof(_TCompressedCovers)+sizeof(_TPackedCovers)); + if (isCompressedDiff){ + hpatch_compressedDiffInfo diffInfo; + _TCompressedCovers* compressedCovers=0; + if (!_compressedCovers_open(&compressedCovers,&diffInfo,diffData,decompressPlugin, + temp_cache_end-kBestACacheSize-sizeof(_TCompressedCovers),temp_cache_end)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + if ((oldData->streamSize!=diffInfo.oldDataSize)||(newDataSize!=diffInfo.newDataSize)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + temp_cache_end-=kBestACacheSize+sizeof(_TCompressedCovers); + // [ ... | compressedCovers cache ] + // [ (cacheSize-kBestACacheSize) | (kBestACacheSize) ] + *out_covers=&compressedCovers->base.ICovers; + isUsedCover32=(diffInfo.oldDataSize|diffInfo.newDataSize)<((hpatch_uint64_t)1<<32); + }else{ + _TPackedCovers* packedCovers=0; + _THDiffHead diffHead; + hpatch_StreamPos_t oldDataSize=oldData->streamSize; + if (!_packedCovers_open(&packedCovers,&diffHead,diffData, + temp_cache_end-kBestACacheSize*3-sizeof(_TPackedCovers),temp_cache_end)) + { *out_isReadError=hpatch_TRUE; return _hpatch_FALSE; } + temp_cache_end-=kBestACacheSize*3+sizeof(_TPackedCovers); + // [ ... | packedCovers cache ] + // [ (cacheSize-kBestACacheSize*3) | (kBestACacheSize*3) ] + *out_covers=&packedCovers->base.ICovers; + isUsedCover32=(oldDataSize|newDataSize)<((hpatch_uint64_t)1<<32); + } + + if (!_arrayCovers_load(&arrayCovers,*out_covers,isUsedCover32, + out_isReadError,&temp_cache,temp_cache_end-kBestACacheSize)){ + if (*out_isReadError) return _hpatch_FALSE; + // [ patch cache | *edCovers cache ] + // [ (cacheSize-kBestACacheSize*?) | (kBestACacheSize*?) ] + *ptemp_cache=temp_cache; + *ptemp_cache_end=temp_cache_end; + return hpatch_FALSE; + }else{ + // [ arrayCovers cache | ... ] + // [((new temp_cache)-(old temp_cache))| (cacheSize-(arrayCovers cache size)) ] + TByte* old_cache_end; + hpatch_TStreamInput* replace_oldData=0; + assert(!(*out_isReadError)); + if (!((*out_covers)->close(*out_covers))) return _hpatch_FALSE; + *out_covers=&arrayCovers->ICovers; + temp_cache_end=temp_cache_end_back; //free compressedCovers or packedCovers memory + old_cache_end=temp_cache_end-kBestACacheSize*kCacheCount; + // [ arrayCovers cache | ... | patch reserve cache ] + // [ | ... | (kBestACacheSize*kCacheCount) ] + if (((hpatch_size_t)(temp_cache_end-temp_cache)<=kBestACacheSize*kCacheCount) + ||(!_cache_old(&replace_oldData,oldData,arrayCovers,out_isReadError, + temp_cache,&old_cache_end,temp_cache_end))){ + if (*out_isReadError) return _hpatch_FALSE; + // [ arrayCovers cache | patch cache ] + *ptemp_cache=temp_cache; + *ptemp_cache_end=temp_cache_end; + return hpatch_FALSE; + }else{ + // [ arrayCovers cache | oldData cache | patch cache ] + // [ | |(temp_cache_end-(new old_cache_end))] + assert(!(*out_isReadError)); + assert((hpatch_size_t)(temp_cache_end-old_cache_end)>=kBestACacheSize*kCacheCount); + temp_cache=old_cache_end; + + *poldData=replace_oldData; + *ptemp_cache=temp_cache; + *ptemp_cache_end=temp_cache_end; + return hpatch_TRUE; + } + } + } +#endif//_IS_NEED_CACHE_OLD_BY_COVERS + return hpatch_FALSE;//not cache oldData +} + +hpatch_BOOL patch_stream_with_cache(const struct hpatch_TStreamOutput* out_newData, + const struct hpatch_TStreamInput* oldData, + const struct hpatch_TStreamInput* serializedDiff, + TByte* temp_cache,TByte* temp_cache_end){ + hpatch_BOOL result; + hpatch_TCovers* covers=0;//not need close before return + hpatch_BOOL isReadError=hpatch_FALSE; + _patch_cache(&covers,&oldData,out_newData->streamSize,serializedDiff,hpatch_FALSE,0, + _kCachePatCount,&temp_cache,&temp_cache_end,&isReadError); + if (isReadError) return _hpatch_FALSE; + result=_patch_stream_with_cache(out_newData,oldData,serializedDiff,covers, + temp_cache,temp_cache_end); + //if ((covers!=0)&&(!covers->close(covers))) result=_hpatch_FALSE; + return result; +} + +hpatch_BOOL patch_stream(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* serializedDiff){ + TByte temp_cache[hpatch_kStreamCacheSize*_kCachePatCount]; + return _patch_stream_with_cache(out_newData,oldData,serializedDiff,0, + temp_cache,temp_cache+sizeof(temp_cache)/sizeof(TByte)); +} + +hpatch_BOOL patch_decompress_with_cache(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin, + TByte* temp_cache,TByte* temp_cache_end){ + hpatch_BOOL result=hpatch_TRUE; + hpatch_TCovers* covers=0; //need close before return + hpatch_BOOL isReadError=hpatch_FALSE; +#if (_HPATCH_IS_USED_MULTITHREAD) + hinput_mt_safe_t _safeDiff; + hpatch_TStreamInput* psafeDiff=0; + if (decompressPlugin && (decompressPlugin->dec_threadNum>1)){ + psafeDiff=hinput_mt_safe_open(&_safeDiff,compressedDiff); + if (!psafeDiff){ result=_hpatch_FALSE; goto _clear; } + compressedDiff=psafeDiff; + } +#endif + _patch_cache(&covers,&oldData,out_newData->streamSize,compressedDiff,hpatch_TRUE, + decompressPlugin,_kCacheDecCount,&temp_cache,&temp_cache_end,&isReadError); + if (isReadError){ result=_hpatch_FALSE; goto _clear; } + result=_patch_decompress_cache(out_newData,oldData,compressedDiff,decompressPlugin, + covers,temp_cache,temp_cache_end); +_clear: + if ((covers!=0)&&(!covers->close(covers))) result=_hpatch_FALSE; +#if (_HPATCH_IS_USED_MULTITHREAD) + if (psafeDiff) hinput_mt_safe_close(psafeDiff); +#endif + return result; +} + +hpatch_BOOL patch_decompress(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin){ + TByte temp_cache[hpatch_kStreamCacheSize*_kCacheDecCount]; + return _patch_decompress_cache(out_newData,oldData,compressedDiff,decompressPlugin, + 0,temp_cache,temp_cache+sizeof(temp_cache)/sizeof(TByte)); +} + +hpatch_BOOL hpatch_coverList_open_serializedDiff(hpatch_TCoverList* out_coverList, + const hpatch_TStreamInput* serializedDiff){ + TByte* temp_cache; + TByte* temp_cache_end; + _TPackedCovers* packedCovers=0; + _THDiffHead diffHead; + assert((out_coverList!=0)&&(out_coverList->ICovers==0)); + temp_cache=out_coverList->_buf; + temp_cache_end=temp_cache+sizeof(out_coverList->_buf); + if (!_packedCovers_open(&packedCovers,&diffHead,serializedDiff, + temp_cache,temp_cache_end)) + return _hpatch_FALSE; + out_coverList->ICovers=&packedCovers->base.ICovers; + return hpatch_TRUE; +} + +hpatch_BOOL hpatch_coverList_open_compressedDiff(hpatch_TCoverList* out_coverList, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin){ + TByte* temp_cache; + TByte* temp_cache_end; + _TCompressedCovers* compressedCovers=0; + hpatch_compressedDiffInfo diffInfo; + assert((out_coverList!=0)&&(out_coverList->ICovers==0)); + temp_cache=out_coverList->_buf; + temp_cache_end=temp_cache+sizeof(out_coverList->_buf); + if (!_compressedCovers_open(&compressedCovers,&diffInfo,compressedDiff,decompressPlugin, + temp_cache,temp_cache_end)) + return _hpatch_FALSE; + out_coverList->ICovers=&compressedCovers->base.ICovers; + return hpatch_TRUE; +} + +// + +hpatch_BOOL _patch_single_compressed_diff_mt(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* singleCompressedDiff, + hpatch_StreamPos_t diffData_pos, + hpatch_StreamPos_t uncompressedSize, + hpatch_StreamPos_t compressedSize, + hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t coverCount,hpatch_size_t stepMemSize, + unsigned char* temp_cache,unsigned char* temp_cache_end, + sspatch_coversListener_t* coversListener, + size_t maxThreadNum,hpatchMTSets_t hpatchMTSets){ +#if (_HPATCH_IS_USED_MULTITHREAD) + struct hpatch_mt_manager_t* hpatch_mt_manager=0; + hpatchMTSets_t mtsets=hpatch_getMTSets(out_newData->streamSize,oldData->streamSize,singleCompressedDiff->streamSize-diffData_pos, + compressedSize?decompressPlugin:0,_kCacheSgCount,stepMemSize, + temp_cache_end-temp_cache,maxThreadNum,hpatchMTSets); +#endif + hpatch_BOOL result; + hpatch_BOOL isNeedOutCache=hpatch_TRUE; + size_t kCacheCount=_kCacheSgCount; + hpatch_TUncompresser_t uncompressedStream={0}; + hpatch_StreamPos_t diffData_posEnd; + if (compressedSize==0){ + decompressPlugin=0; + }else{ + if (decompressPlugin==0) return _hpatch_FALSE; + } + diffData_posEnd=(decompressPlugin?compressedSize:uncompressedSize)+diffData_pos; + if (diffData_posEnd>singleCompressedDiff->streamSize) return _hpatch_FALSE; +#if (_HPATCH_IS_USED_MULTITHREAD) + if (_hpatchMTSets_threadNum(mtsets)>1){ + isNeedOutCache=!mtsets.writeNew_isMT; + kCacheCount=_kCacheSgCount-(isNeedOutCache?0:1); + hpatch_mt_manager=hpatch_mt_manager_open(&out_newData,&oldData,&singleCompressedDiff, + &diffData_pos,&diffData_posEnd,uncompressedSize,&decompressPlugin, + stepMemSize,&temp_cache,&temp_cache_end,&coversListener,hpatch_TRUE, + kCacheCount,mtsets); + if (!hpatch_mt_manager) return _hpatch_FALSE; + } +#endif + if (decompressPlugin){ + if (!compressed_stream_as_uncompressed(&uncompressedStream,uncompressedSize,decompressPlugin,singleCompressedDiff, + diffData_pos,diffData_posEnd)) return _hpatch_FALSE; + singleCompressedDiff=&uncompressedStream.base; + diffData_pos=0; + diffData_posEnd=singleCompressedDiff->streamSize; + } + + result=patch_single_stream_diff(out_newData,oldData,singleCompressedDiff,diffData_pos,diffData_posEnd, + coverCount,stepMemSize,temp_cache,temp_cache_end,coversListener,isNeedOutCache); + + if (decompressPlugin) + close_compressed_stream_as_uncompressed(&uncompressedStream); +#if (_HPATCH_IS_USED_MULTITHREAD) + if (hpatch_mt_manager){ + if (!hpatch_mt_manager_close(hpatch_mt_manager,!result)) + result=_hpatch_FALSE; + hpatch_mt_manager=0; + } +#endif + return result; +} + +static const size_t _kStepMemSizeSafeLimit =(1<<20)*4; +hpatch_BOOL getSingleCompressedDiffInfo(hpatch_singleCompressedDiffInfo* out_diffInfo, + const hpatch_TStreamInput* singleCompressedDiff, + hpatch_StreamPos_t diffInfo_pos){ + TStreamCacheClip _diffHeadClip; + TStreamCacheClip* diffHeadClip=&_diffHeadClip; + TByte temp_cache[hpatch_kStreamCacheSize]; + _TStreamCacheClip_init(&_diffHeadClip,singleCompressedDiff,diffInfo_pos,singleCompressedDiff->streamSize, + temp_cache,hpatch_kStreamCacheSize); + {//type + const char* kVersionType="HDIFFSF20"; + char* tempType=out_diffInfo->compressType; + if (!_TStreamCacheClip_readType_end(diffHeadClip,'&',tempType)) return _hpatch_FALSE; + if (0!=strcmp(tempType,kVersionType)) return _hpatch_FALSE; + } + {//read compressType + if (!_TStreamCacheClip_readType_end(diffHeadClip,'\0', + out_diffInfo->compressType)) return _hpatch_FALSE; + } + _clip_unpackUIntTo(&out_diffInfo->newDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->oldDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->coverCount,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->stepMemSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->uncompressedSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->compressedSize,diffHeadClip); + out_diffInfo->diffDataPos=_TStreamCacheClip_readPosOfSrcStream(diffHeadClip)-diffInfo_pos; + if (out_diffInfo->compressedSize>out_diffInfo->uncompressedSize) + return _hpatch_FALSE; + if (out_diffInfo->stepMemSize>(out_diffInfo->newDataSize+_kStepMemSizeSafeLimit)) + return _hpatch_FALSE; + if (out_diffInfo->stepMemSize>out_diffInfo->uncompressedSize+_kStepMemSizeSafeLimit) + return _hpatch_FALSE; + return hpatch_TRUE; +} + +static hpatch_BOOL _TUncompresser_read(const struct hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + hpatch_TUncompresser_t* self=(hpatch_TUncompresser_t*)stream->streamImport; + return self->_decompressPlugin->decompress_part(self->_decompressHandle,out_data,out_data_end); +} + +hpatch_BOOL compressed_stream_as_uncompressed(hpatch_TUncompresser_t* uncompressedStream,hpatch_StreamPos_t uncompressedSize, + hpatch_TDecompress* decompressPlugin,const hpatch_TStreamInput* compressedStream, + hpatch_StreamPos_t compressed_pos,hpatch_StreamPos_t compressed_end){ + hpatch_TUncompresser_t* self=uncompressedStream; + assert(decompressPlugin!=0); + assert(self->_decompressHandle==0); + self->_decompressHandle=decompressPlugin->open(decompressPlugin,uncompressedSize,compressedStream, + compressed_pos,compressed_end); + if (self->_decompressHandle==0) return _hpatch_FALSE; + self->_decompressPlugin=decompressPlugin; + + self->base.streamImport=self; + self->base.streamSize=uncompressedSize; + self->base.read=_TUncompresser_read; + return hpatch_TRUE; +} + +void close_compressed_stream_as_uncompressed(hpatch_TUncompresser_t* uncompressedStream){ + hpatch_TUncompresser_t* self=uncompressedStream; + if (self==0) return; + if (self->_decompressHandle==0) return; + self->_decompressPlugin->close(self->_decompressPlugin,self->_decompressHandle); + self->_decompressHandle=0; +} + +typedef struct{ + const unsigned char* code; + const unsigned char* code_end; + hpatch_size_t len0; + hpatch_size_t lenv; + hpatch_BOOL isNeedDecode0; +} rle0_decoder_t; + +static void _rle0_decoder_init(rle0_decoder_t* self,const unsigned char* code,const unsigned char* code_end){ + self->code=code; + self->code_end=code_end; + self->len0=0; + self->lenv=0; + self->isNeedDecode0=hpatch_TRUE; +} + +static hpatch_BOOL _rle0_decoder_add(rle0_decoder_t* self,TByte* out_data,hpatch_size_t decodeSize){ + if (self->len0){ + _0_process: + if (self->len0>=decodeSize){ + self->len0-=decodeSize; + return hpatch_TRUE; + }else{ + decodeSize-=self->len0; + out_data+=self->len0; + self->len0=0; + goto _decode_v_process; + } + } + + if (self->lenv){ + _v_process: + if (self->lenv>=decodeSize){ + addData(out_data,self->code,decodeSize); + self->code+=decodeSize; + self->lenv-=decodeSize; + return hpatch_TRUE; + }else{ + addData(out_data,self->code,self->lenv); + out_data+=self->lenv; + decodeSize-=self->lenv; + self->code+=self->lenv; + self->lenv=0; + goto _decode_0_process; + } + } + + assert(decodeSize>0); + if (self->isNeedDecode0){ + hpatch_StreamPos_t len0; + _decode_0_process: + self->isNeedDecode0=hpatch_FALSE; + if (!hpatch_unpackUInt(&self->code,self->code_end,&len0)) return _hpatch_FALSE; + if (len0!=(hpatch_size_t)len0) return _hpatch_FALSE; + self->len0=(hpatch_size_t)len0; + goto _0_process; + }else{ + hpatch_StreamPos_t lenv; + _decode_v_process: + self->isNeedDecode0=hpatch_TRUE; + if (!hpatch_unpackUInt(&self->code,self->code_end,&lenv)) return _hpatch_FALSE; + if (lenv>(size_t)(self->code_end-self->code)) return _hpatch_FALSE; + self->lenv=(hpatch_size_t)lenv; + goto _v_process; + } +} + + +static hpatch_BOOL _patch_add_old_with_rle0(_TOutStreamCache* outCache,rle0_decoder_t* rle0_decoder, + const hpatch_TStreamInput* old,hpatch_StreamPos_t oldPos, + hpatch_StreamPos_t addLength,TByte* aCache,hpatch_size_t aCacheSize){ + while (addLength>0){ + hpatch_size_t decodeStep=aCacheSize; + if (decodeStep>addLength) + decodeStep=(hpatch_size_t)addLength; + if (!old->read(old,oldPos,aCache,aCache+decodeStep)) return _hpatch_FALSE; + if (!_rle0_decoder_add(rle0_decoder,aCache,decodeStep)) return _hpatch_FALSE; + if (!_TOutStreamCache_write(outCache,aCache,decodeStep)) return _hpatch_FALSE; + oldPos+=decodeStep; + addLength-=decodeStep; + } + return hpatch_TRUE; +} + +hpatch_BOOL sspatch_covers_nextCover(sspatch_covers_t* self){ + hpatch_BOOL inc_oldPos_sign=(*(self->covers_cache))>>(8-1); + self->lastOldEnd=self->cover.oldPos+self->cover.length; + self->lastNewEnd=self->cover.newPos+self->cover.length; + if (!hpatch_unpackUIntWithTag(&self->covers_cache,self->covers_cacheEnd,&self->cover.oldPos,1)) return _hpatch_FALSE; + if (inc_oldPos_sign==0) + self->cover.oldPos+=self->lastOldEnd; + else + self->cover.oldPos=self->lastOldEnd-self->cover.oldPos; + if (!hpatch_unpackUInt(&self->covers_cache,self->covers_cacheEnd,&self->cover.newPos)) return _hpatch_FALSE; + self->cover.newPos+=self->lastNewEnd; + if (!hpatch_unpackUInt(&self->covers_cache,self->covers_cacheEnd,&self->cover.length)) return _hpatch_FALSE; + return hpatch_TRUE; +} + + +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + +#define _kMaxCachedLen_min (32*1024) // 1M is big value, 256k is middle value, 32k is small value +#define _kMaxCachedLen_max (32*_kMaxCachedLen_min) +#define _kMemForReadOldSize (hpatch_kFileIOBufBetterSize*2) + +typedef struct{ + _cache_old_TStreamInput cache_old; + hpatch_TStreamInput base; + sspatch_covers_t covers; + hpatch_BOOL isHaveACover; + hpatch_size_t sumCacheLen; + hpatch_byte* cache_buf_end; + const hpatch_byte* covers_cache; + const hpatch_byte* covers_cacheEnd; +} _step_cache_old_t; + +static hpatch_force_inline hpatch_size_t _step_cache_old_sumBufSize(const _step_cache_old_t* self){ + return self->cache_old.cachesEnd-(const hpatch_byte*)self->cache_old.arrayCovers.pCCovers; } +static hpatch_force_inline hpatch_size_t _step_cache_old_coverBufSize(const _step_cache_old_t* self){ + return (hpatch_size_t)arrayCovers_memSize(self->cache_old.arrayCovers.coverCount,self->cache_old.arrayCovers.is32); } +static hpatch_force_inline hpatch_StreamPos_t _step_cache_old_incCoverBufSize(const _step_cache_old_t* self){ + return arrayCovers_memSize(self->cache_old.arrayCovers.coverCount+1,self->cache_old.arrayCovers.is32); } + + +static hpatch_BOOL _step_cache_old_addCover(_step_cache_old_t* self,const hpatch_TCover* cover){ + const hpatch_StreamPos_t _incCoverBufSize=_step_cache_old_incCoverBufSize(self); + while (hpatch_TRUE){ + const hpatch_size_t _addCachedLen=(cover->length<=self->cache_old.maxCachedLen)?(hpatch_size_t)cover->length:0; + if (_incCoverBufSize+self->sumCacheLen+_addCachedLen<=_step_cache_old_sumBufSize(self)){ + self->sumCacheLen+=_addCachedLen; + _arrayCovers_push_cover(&self->cache_old.arrayCovers,cover); + return hpatch_TRUE; + }else{ + assert(self->cache_old.maxCachedLen>=_kMaxCachedLen_min); + if (self->cache_old.maxCachedLen==_kMaxCachedLen_min) return hpatch_FALSE; + self->cache_old.maxCachedLen/=2; + self->sumCacheLen=(hpatch_size_t)_getCacheSumLen(&self->cache_old.arrayCovers,self->cache_old.maxCachedLen); + } + } +} + +static hpatch_BOOL _patch_step_cache_old_update(void* _self){ + _step_cache_old_t* self=(_step_cache_old_t*)_self; + hpatch_StreamPos_t oldPosBegin; + hpatch_StreamPos_t oldPosEnd; + const hpatch_size_t kMinCacheCoverCount=0; //no limit + assert(self->cache_old.arrayCovers.coverCount<=self->cache_old.arrayCovers.cur_index); + assert(self->cache_old.caches==self->cache_old.cachesEnd); + self->sumCacheLen=0; + self->cache_old.arrayCovers.coverCount=0; + self->cache_old.arrayCovers.cur_index=0; + self->cache_old.cachesEnd=self->cache_buf_end-_kMemForReadOldSize; //limit covers + cached data 's size + self->cache_old.maxCachedLen=_kMaxCachedLen_max; + while ((self->cache_old.maxCachedLen>=_step_cache_old_sumBufSize(self))&&(self->cache_old.maxCachedLen>_kMaxCachedLen_min)) + self->cache_old.maxCachedLen/=2; + + //read some cover from self->covers to self->cache_old.arrayCovers + if (self->isHaveACover){ + self->isHaveACover=hpatch_FALSE; + assert(self->covers.cover.length>0); + if (!_step_cache_old_addCover(self,&self->covers.cover)) assert(hpatch_FALSE); + } + while (sspatch_covers_isHaveNextCover(&self->covers)){ + if (!sspatch_covers_nextCover(&self->covers)) + return hpatch_FALSE; + if (self->covers.cover.length>0){ + if (_step_cache_old_addCover(self,&self->covers.cover)){ + //ok + }else{ + self->isHaveACover=hpatch_TRUE; + break; //arrayCovers full + } + } + } + + self->cache_old.maxCachedLen=_getMaxCachedLen(&self->cache_old.arrayCovers,_step_cache_old_sumBufSize(self)-_step_cache_old_coverBufSize(self)); + assert(self->cache_old.maxCachedLen>0); + self->sumCacheLen=_set_cache_pos(&self->cache_old.arrayCovers,self->cache_old.maxCachedLen,&oldPosBegin,&oldPosEnd,kMinCacheCoverCount); + self->cache_old.caches=((hpatch_byte*)self->cache_old.arrayCovers.pCCovers)+_step_cache_old_coverBufSize(self); + self->cache_old.cachesEnd=self->cache_old.caches+self->sumCacheLen; + if (self->sumCacheLen>0){ + if (!_cache_old_load(self->cache_old.oldData,oldPosBegin,oldPosEnd,&self->cache_old.arrayCovers, + self->cache_old.maxCachedLen,self->sumCacheLen, (hpatch_byte*)self->cache_old.caches, + (hpatch_byte*)self->cache_old.cachesEnd,self->cache_buf_end)) + return _hpatch_FALSE; + } + return hpatch_TRUE; +} + +static hpatch_inline +void _patch_step_cache_old_init(_step_cache_old_t* self,hpatch_size_t canUsedMemSize, + const hpatch_TStreamInput* oldData,hpatch_BOOL isUsedCover32){ + memset(self,0,sizeof(*self)); + assert(canUsedMemSize>=_kMemForReadOldSize*2); + self->base.streamImport=&self->cache_old; + self->base.streamSize=oldData->streamSize; + self->base.read=_cache_old_StreamInput_read; + //todo: _cache_old_StreamInput_read call back + self->cache_old.oldData=oldData; + self->cache_old._cacheImport=self; + self->cache_old._doUpdateCacheCovers=_patch_step_cache_old_update; + self->cache_buf_end=((hpatch_byte*)self) + canUsedMemSize; + + self->cache_old.arrayCovers.is32=isUsedCover32; + self->cache_old.arrayCovers.pCCovers=(void*)_hpatch_align_upper((self+1),sizeof(hpatch_StreamPos_t)); + self->cache_old.arrayCovers.ICovers.leave_cover_count=_arrayCovers_leaveCoverCount; + self->cache_old.arrayCovers.ICovers.read_cover=_arrayCovers_read_cover; +} + + +hpatch_BOOL _patch_step_cache_old_onStepCovers(const hpatch_TStreamInput* _self,const unsigned char* covers_cache,const unsigned char* covers_cacheEnd){ + _step_cache_old_t* self=(_step_cache_old_t*)_self->streamImport; + assert(!sspatch_covers_isHaveNextCover(&self->covers)); + assert(!self->isHaveACover); + self->covers_cache=covers_cache; + self->covers_cacheEnd=covers_cacheEnd; + sspatch_covers_setCoversCache(&self->covers,covers_cache,covers_cacheEnd); + return covers_cache?_patch_step_cache_old_update(self):hpatch_TRUE; +} + + +hpatch_size_t _patch_step_cache_old_canUsedSize(hpatch_size_t stepCoversMemSize,hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize){ + const hpatch_size_t kActiveCacheOldMemorySize=(1<<20)*3+_kMemForReadOldSize*2; + hpatch_size_t cacheStepSize,multiple; + if (tempCacheSize4)?(hpatch_size_t)(multiple-4):0)/8); + return cacheStepSize; +} + +hpatch_BOOL _patch_step_cache_old(const hpatch_TStreamInput** poldData,hpatch_StreamPos_t newDataSize,size_t stepCoversMemSize, + size_t kMinTempCacheSize,hpatch_byte** ptemp_cache,hpatch_byte** ptemp_cache_end){ + const hpatch_TStreamInput* oldData=*poldData; + _step_cache_old_t* self; + const hpatch_BOOL isUsedCover32=(oldData->streamSize|newDataSize)<((hpatch_uint64_t)1<<32); + hpatch_byte* temp_cache=*ptemp_cache; + hpatch_byte* const temp_cache_end=*ptemp_cache_end; + + hpatch_size_t canUsedMemSize=_patch_step_cache_old_canUsedSize(stepCoversMemSize,kMinTempCacheSize,temp_cache_end-temp_cache); + if (canUsedMemSize==0) + return hpatch_FALSE; //not enough memory for cache part of oldData + _cache_alloc(self,_step_cache_old_t,canUsedMemSize,temp_cache,temp_cache_end); + _patch_step_cache_old_init(self,canUsedMemSize,oldData,isUsedCover32); + + *poldData=&self->base; + *ptemp_cache=temp_cache; + return hpatch_TRUE; +} + +#endif // _IS_NEED_CACHE_OLD_BY_COVERS + + +hpatch_size_t _patch_is_can_cache_window_old_canUsedSize(hpatch_size_t windowOldBufSize,hpatch_size_t stepCoversMemSize, + hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize,hpatch_StreamPos_t oldDataSize){ + hpatch_size_t result=(windowOldBufSize*2onStepCovers); + + while (coverCount){ + rle0_decoder_t rle0_decoder; + {//read step info + unsigned char* covers_cacheEnd; + unsigned char* bufRle_cache_end; + { + hpatch_StreamPos_t bufCover_size; + hpatch_StreamPos_t bufRle_size; + _clip_unpackUIntTo(&bufCover_size,inClip); + _clip_unpackUIntTo(&bufRle_size,inClip); + #ifdef __RUN_MEM_SAFE_CHECK + if ((bufCover_size>stepMemSize)||(bufRle_size>stepMemSize)|| + (bufCover_size+bufRle_size>stepMemSize)) return _hpatch_FALSE; + #endif + covers_cacheEnd=step_cache+(size_t)bufCover_size; + bufRle_cache_end=covers_cacheEnd+(size_t)bufRle_size; + } + if (coversListener&&coversListener->onStepCoversReset) + coversListener->onStepCoversReset(coversListener,coverCount); + if (!_TStreamCacheClip_readDataTo(inClip,step_cache,bufRle_cache_end)) + return _hpatch_FALSE; + if (coversListener) + coversListener->onStepCovers(coversListener,step_cache,covers_cacheEnd); + #if (_IS_NEED_CACHE_OLD_BY_COVERS) + if (isCachedOldByStep) + _patch_step_cache_old_onStepCovers(oldData,step_cache,covers_cacheEnd); + #endif + sspatch_covers_setCoversCache(&covers,step_cache,covers_cacheEnd); + _rle0_decoder_init(&rle0_decoder,covers_cacheEnd,bufRle_cache_end); + } + while (sspatch_covers_isHaveNextCover(&covers)){ + if (!sspatch_covers_nextCover(&covers)) + return _hpatch_FALSE; + if (covers.cover.newPos>covers.lastNewEnd){ + if (!_TOutStreamCache_copyFromClip(outCache,inClip,covers.cover.newPos-covers.lastNewEnd)) + return _hpatch_FALSE; + } + + --coverCount; + if (covers.cover.length){ + #ifdef __RUN_MEM_SAFE_CHECK + if ((covers.cover.oldPos>oldData->streamSize)|| + (covers.cover.length>(hpatch_StreamPos_t)(oldData->streamSize-covers.cover.oldPos))) return _hpatch_FALSE; + #endif + if (!_patch_add_old_with_rle0(outCache,&rle0_decoder,oldData,covers.cover.oldPos,covers.cover.length, + temp_cache,cache_size)) return _hpatch_FALSE; + }else{ + #ifdef __RUN_MEM_SAFE_CHECK + if (coverCount!=0) return _hpatch_FALSE; + #endif + } + } + } + if (coversListener){ + if (coversListener->onStepCoversReset) + coversListener->onStepCoversReset(coversListener,0); + coversListener->onStepCovers(coversListener,0,0); + } + + return hpatch_TRUE; +} + + +hpatch_BOOL patch_single_stream_diff(const hpatch_TStreamOutput* out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* uncompressedDiffData, + hpatch_StreamPos_t diffData_pos, + hpatch_StreamPos_t diffData_posEnd, + hpatch_StreamPos_t coverCount,hpatch_size_t stepMemSize, + unsigned char* temp_cache,unsigned char* temp_cache_end, + sspatch_coversListener_t* coversListener,hpatch_BOOL isNeedOutCache){ + unsigned char* step_cache; + hpatch_size_t cache_size; + TStreamCacheClip inClip; + _TOutStreamCache outCache; + const size_t kCacheCount=_kCacheSgCount-(isNeedOutCache?0:1); +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + hpatch_BOOL isCachedOldByStep=hpatch_FALSE; +#endif + + step_cache=temp_cache; + assert(diffData_posEnd<=uncompressedDiffData->streamSize); + if (coversListener) assert(coversListener->onStepCovers); + {//cache + hpatch_BOOL isCachedAllOld; + hpatch_BOOL isReadError=hpatch_FALSE; + if ((size_t)(temp_cache_end-temp_cache)streamSize,stepMemSize, + kCacheCount*hpatch_kFileIOBufBetterSize,&temp_cache,&temp_cache_end); + #endif + cache_size=(temp_cache_end-temp_cache)/kCacheCount; + _TStreamCacheClip_init(&inClip,uncompressedDiffData,diffData_pos,diffData_posEnd, + temp_cache,cache_size); + temp_cache+=cache_size; + _TOutStreamCache_init(&outCache,out_newData,isNeedOutCache?temp_cache:0,isNeedOutCache?cache_size:0); + if (isNeedOutCache) temp_cache+=cache_size; + } + + if (!_patch_single_stream_loop(&inClip,&outCache,oldData,coverCount,stepMemSize, + step_cache,temp_cache,cache_size,coversListener + #if (_IS_NEED_CACHE_OLD_BY_COVERS) + ,isCachedOldByStep + #endif + )) + return _hpatch_FALSE; + + if (!_TOutStreamCache_flush(&outCache)) + return _hpatch_FALSE; + if (_TStreamCacheClip_isFinish(&inClip)&&_TOutStreamCache_isFinish(&outCache)) + return hpatch_TRUE; + else + return _hpatch_FALSE; +} + + +static hpatch_BOOL _TDiffToSingleStream_read(const struct hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + //[ |readedSize ] + // [ |cachedBufBegin _TDiffToSingleStream_kBufSize] + // readFromPos[out_data out_data_end] + TDiffToSingleStream* self=(TDiffToSingleStream*)stream->streamImport; + hpatch_StreamPos_t readedSize=self->readedSize; + while (1){ + size_t rLen=out_data_end-out_data; + if (readFromPos==readedSize){ + hpatch_BOOL result=self->diffStream->read(self->diffStream,readedSize,out_data,out_data_end); + self->readedSize=readedSize+rLen; + if ((self->isInSingleStream)||(rLen>_TDiffToSingleStream_kBufSize)){ + self->cachedBufBegin=_TDiffToSingleStream_kBufSize; + }else{ + //cache + if (rLen>=_TDiffToSingleStream_kBufSize){ + memcpy(self->buf,out_data_end-_TDiffToSingleStream_kBufSize,_TDiffToSingleStream_kBufSize); + self->cachedBufBegin = 0; + }else{ + size_t new_cachedBufBegin; + if (self->cachedBufBegin>=rLen){ + new_cachedBufBegin=self->cachedBufBegin-rLen; + memmove(self->buf+new_cachedBufBegin,self->buf+self->cachedBufBegin,_TDiffToSingleStream_kBufSize-self->cachedBufBegin); + }else{ + new_cachedBufBegin=0; + memmove(self->buf,self->buf+rLen,_TDiffToSingleStream_kBufSize-rLen); + } + memcpy(self->buf+(_TDiffToSingleStream_kBufSize-rLen),out_data,rLen); + self->cachedBufBegin=new_cachedBufBegin; + } + } + return result; + }else{ + size_t cachedSize=_TDiffToSingleStream_kBufSize-self->cachedBufBegin; + size_t bufSize=(size_t)(readedSize-readFromPos); + if ((readFromPosbufSize) + rLen=bufSize; + memcpy(out_data,self->buf+(_TDiffToSingleStream_kBufSize-bufSize),rLen); + out_data+=rLen; + readFromPos+=rLen; + if (out_data==out_data_end) + return hpatch_TRUE; + else + continue; + }else{ + return _hpatch_FALSE; + } + } + } +} +void TDiffToSingleStream_init(TDiffToSingleStream* self,const hpatch_TStreamInput* diffStream){ + self->base.streamImport=self; + self->base.streamSize=diffStream->streamSize; + self->base.read=_TDiffToSingleStream_read; + self->base._private_reserved=0; + self->diffStream=diffStream; + self->readedSize=0; + self->cachedBufBegin=_TDiffToSingleStream_kBufSize; + self->isInSingleStream=hpatch_FALSE; +} + +hpatch_BOOL _patch_single_stream_mt(sspatch_listener_t* listener, + const hpatch_TStreamOutput* __out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* singleCompressedDiff, + hpatch_StreamPos_t diffInfo_pos, + sspatch_coversListener_t* coversListener, + size_t maxThreadNum,hpatchMTSets_t hpatchMTSets){ + hpatch_BOOL result=hpatch_TRUE; + hpatch_TDecompress* decompressPlugin=0; + unsigned char* temp_cache=0; + unsigned char* temp_cacheEnd=0; + hpatch_singleCompressedDiffInfo diffInfo; + hpatch_TStreamOutput _out_newData=*__out_newData; + hpatch_TStreamOutput* out_newData=&_out_newData; + TDiffToSingleStream _toSStream; + assert((listener)&&(listener->onDiffInfo)); + TDiffToSingleStream_init(&_toSStream,singleCompressedDiff); + singleCompressedDiff=&_toSStream.base; + + if (!getSingleCompressedDiffInfo(&diffInfo,singleCompressedDiff,diffInfo_pos)) + return _hpatch_FALSE; + if (diffInfo.newDataSize>out_newData->streamSize) + return _hpatch_FALSE; + out_newData->streamSize=diffInfo.newDataSize; + if (diffInfo.oldDataSize!=oldData->streamSize) + return _hpatch_FALSE; + if (!listener->onDiffInfo(listener,&diffInfo,&decompressPlugin,&temp_cache,&temp_cacheEnd)) + return _hpatch_FALSE; + + if ((temp_cache==0)||(temp_cache>=temp_cacheEnd)) + result=_hpatch_FALSE; + if (result){ + result=_patch_single_compressed_diff_mt(out_newData,oldData,singleCompressedDiff,diffInfo.diffDataPos, + diffInfo.uncompressedSize,diffInfo.compressedSize,decompressPlugin, + diffInfo.coverCount,(size_t)diffInfo.stepMemSize, + temp_cache,temp_cacheEnd,coversListener,maxThreadNum,hpatchMTSets); + } + if (listener->onPatchFinish) + listener->onPatchFinish(listener,temp_cache,temp_cacheEnd); + return result; +} + + +static hpatch_BOOL _getWindowDiffInfo(hpatch_windowDiffInfo* out_diffInfo, + const hpatch_TStreamInput* windowDiff,hpatch_StreamPos_t diffInfo_pos, + unsigned char* headBuf, // [hpatch_kWindowDiffHeadMaxSize] + hpatch_size_t* headSize){ // in headBuf size, out headData size + const char* kVersionType="HDIFFW26"; + const size_t kVersionLen=strlen(kVersionType); + const size_t kHeadPrefixSize=kVersionLen+2; + hpatch_size_t headRemainingSize; + hpatch_TStreamInput headMemStream; + TStreamCacheClip _headClip; + TStreamCacheClip* diffHeadClip=&_headClip; + TByte _temp_cache[hpatch_kMaxPluginTypeLength]; + assert(out_diffInfo!=0); + assert(windowDiff!=0); + assert(windowDiff->read!=0); + assert(headBuf!=0); + assert(headSize!=0); + + //read "HDIFFW26" + headRemainingSize + if (kHeadPrefixSize>(*headSize)) return _hpatch_FALSE; + if (!windowDiff->read(windowDiff,diffInfo_pos,headBuf,headBuf+kHeadPrefixSize)) + return _hpatch_FALSE; + if (0!=memcmp(headBuf,"HDIFFW26",kVersionLen)) + return _hpatch_FALSE; + headRemainingSize=(hpatch_size_t)headBuf[kVersionLen] | ((hpatch_size_t)headBuf[kVersionLen+1]<<8); + if (headRemainingSize+kHeadPrefixSize>(*headSize)) + return _hpatch_FALSE; + + //read remaining head data + if (!windowDiff->read(windowDiff,diffInfo_pos+kHeadPrefixSize, + headBuf+kHeadPrefixSize,headBuf+kHeadPrefixSize+headRemainingSize)) + return _hpatch_FALSE; + *headSize=kHeadPrefixSize+headRemainingSize; + out_diffInfo->windowDataPos=kHeadPrefixSize+headRemainingSize; + + //parse from headBuf (skip version+size, parse type string + packUInts) + mem_as_hStreamInput(&headMemStream,headBuf+kHeadPrefixSize,headBuf+kHeadPrefixSize+headRemainingSize); + _TStreamCacheClip_init(diffHeadClip,&headMemStream,0,headRemainingSize,_temp_cache,sizeof(_temp_cache)); + if (!_TStreamCacheClip_readType_end(diffHeadClip,'&',out_diffInfo->compressType)) + return _hpatch_FALSE; + if (!_TStreamCacheClip_readType_end(diffHeadClip,'\0',out_diffInfo->checksumType)) + return _hpatch_FALSE; + + //read header fields + _clip_unpackUIntTo(&out_diffInfo->compressedSize,diffHeadClip); + out_diffInfo->_headFixedInfoPos=kHeadPrefixSize+_TStreamCacheClip_readPosOfSrcStream(diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->uncompressedSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->newDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->oldDataSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->coverCount,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->windowCount,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->windowMetaCount,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->maxStepMemSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->maxSubCoverCount,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->maxWindowOldSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->checksumByteSize,diffHeadClip); + _clip_unpackUIntTo(&out_diffInfo->extraDataSize,diffHeadClip); + + out_diffInfo->otherInfoPos=kHeadPrefixSize+_TStreamCacheClip_readPosOfSrcStream(diffHeadClip); + if (out_diffInfo->otherInfoPos+out_diffInfo->checksumByteSize*3>out_diffInfo->windowDataPos) + return _hpatch_FALSE; + out_diffInfo->otherInfoEndPos=out_diffInfo->windowDataPos-out_diffInfo->checksumByteSize*3; + + if ((out_diffInfo->windowMetaCount>hpatch_kMaxWindowMetaCount)||(out_diffInfo->windowMetaCount<2)) + return _hpatch_FALSE; + if ((out_diffInfo->windowMetaCount&(out_diffInfo->windowMetaCount-1))!=0) //2^N + return _hpatch_FALSE; + if (((out_diffInfo->checksumType[0]==0)&&(out_diffInfo->checksumByteSize!=0)) + ||((out_diffInfo->checksumType[0]!=0)&&(out_diffInfo->checksumByteSize==0))) + return _hpatch_FALSE; + if (out_diffInfo->maxWindowOldSize>out_diffInfo->oldDataSize) + return _hpatch_FALSE; + if ((out_diffInfo->maxWindowOldSize>0)&&(out_diffInfo->windowCount==0)) + return _hpatch_FALSE; + if (out_diffInfo->compressedSize>out_diffInfo->uncompressedSize) + return _hpatch_FALSE; + if (out_diffInfo->maxStepMemSize>(out_diffInfo->newDataSize+_kStepMemSizeSafeLimit)) + return _hpatch_FALSE; + if (out_diffInfo->maxStepMemSize>out_diffInfo->uncompressedSize+_kStepMemSizeSafeLimit) + return _hpatch_FALSE; + return hpatch_TRUE; +} + +hpatch_BOOL getWindowDiffInfo(hpatch_windowDiffInfo* out_diffInfo,const hpatch_TStreamInput* windowDiff, + hpatch_StreamPos_t diffInfo_pos){ + TByte headBuf[hpatch_kWindowDiffHeadMaxSize]; + hpatch_size_t headSize=sizeof(headBuf); + return _getWindowDiffInfo(out_diffInfo,windowDiff,diffInfo_pos,headBuf,&headSize); +} + +static const size_t _kWindowCacheCount=_kCacheSgCount; + + static hpatch_BOOL _cache_load_window_old(const hpatch_TStreamInput* oldData,unsigned char* oldBuf, + hpatch_StreamPos_t windowOldPos,hpatch_StreamPos_t windowOldLength, + hpatch_StreamPos_t* pLastOldPos,hpatch_StreamPos_t* pLastOldEnd){ + const hpatch_StreamPos_t lastOldPos=*pLastOldPos; + const hpatch_StreamPos_t lastOldEnd=*pLastOldEnd; + const hpatch_StreamPos_t windowOldEnd=windowOldPos+windowOldLength; + hpatch_StreamPos_t reuseLen,reuseOff,noOverlapLeft; + _compute_window_overlap(lastOldPos,lastOldEnd,windowOldPos,windowOldEnd,&reuseLen,&reuseOff,&noOverlapLeft); + memmove(oldBuf+(size_t)noOverlapLeft,oldBuf+(size_t)reuseOff,(size_t)reuseLen); + if ((noOverlapLeft>0)&&(!oldData->read(oldData,windowOldPos,oldBuf,oldBuf+(size_t)noOverlapLeft))) + return _hpatch_FALSE; + if ((noOverlapLeft+reuseLenread(oldData,windowOldPos+noOverlapLeft+reuseLen, + oldBuf+(size_t)(noOverlapLeft+reuseLen),oldBuf+(size_t)windowOldLength))) + return _hpatch_FALSE; + *pLastOldPos=windowOldPos; + *pLastOldEnd=windowOldPos+windowOldLength; + return hpatch_TRUE; + } + + static hpatch_BOOL _read_meta_batch(TStreamCacheClip* clip,hpatch_StreamPos_t* pLastOldPos, + hpatch_StreamPos_t maxWindowOldSize,hpatch_StreamPos_t oldSize, + _oldwin_info_t* winInfos,hpatch_size_t writeIdx,hpatch_size_t batchSize){ + hpatch_StreamPos_t lastOldPos=*pLastOldPos; + hpatch_size_t i; + for (i=0;imaxWindowOldSize) return _hpatch_FALSE; + if (len!=(hpatch_StreamPos_t)(hpatch_size_t)len) return _hpatch_FALSE; + if (lastOldPos>oldSize) return _hpatch_FALSE; + if (lastOldPos+len>oldSize) return _hpatch_FALSE; +#endif + winInfos[writeIdx+i].len=len; + winInfos[writeIdx+i].oldPos=lastOldPos; + lastOldPos+=len; + } + *pLastOldPos=lastOldPos; + return hpatch_TRUE; + } + + static hpatch_BOOL _readForSkipStream(const hpatch_TStreamInput* stream,hpatch_StreamPos_t readPos, + hpatch_StreamPos_t readEndPos,unsigned char* temp_cache,unsigned char* temp_cacheEnd){ + while (readPosread(stream,readPos,temp_cache,temp_cache+(size_t)rlen)) + return _hpatch_FALSE; + readPos+=rlen; + } + return hpatch_TRUE; + } + + static hpatch_inline hpatch_BOOL _read_sub_cover_count(TStreamCacheClip* clip, + hpatch_StreamPos_t maxSubCoverCount,hpatch_StreamPos_t* out_subCoverCount){ + _clip_unpackUIntTo(out_subCoverCount,clip); + #if (defined __RUN_MEM_SAFE_CHECK) + if ((*out_subCoverCount)>maxSubCoverCount) return _hpatch_FALSE; + #endif + return hpatch_TRUE; + } + +static hpatch_BOOL _patch_window_diff(const hpatch_TStreamOutput* out_newData,const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* diffStream,hpatch_StreamPos_t diffData_pos, + const hpatch_windowDiffInfo* diffInfo,hpatch_TDecompress* decompressPlugin, + hpatch_TChecksum* checksumPlugin,hpatch_checksumHandle checksumHandle_old, + unsigned char* temp_cache,unsigned char* temp_cache_end, + size_t maxThreadNum,hpatchMTSets_t hpatchMTSets){ + const hpatch_size_t windowOldBufSize=(hpatch_size_t)diffInfo->maxWindowOldSize; + const hpatch_size_t stepMemSize=(hpatch_size_t)diffInfo->maxStepMemSize; + const hpatch_StreamPos_t windowCount=diffInfo->windowCount; + const hpatch_StreamPos_t metaCount=diffInfo->windowMetaCount; + unsigned char* oldBuf; + TStreamCacheClip mainClip; + _TOutStreamCache outCache; + hpatch_StreamPos_t lastOldPos=0; + hpatch_StreamPos_t lastOldEnd=0; + hpatch_StreamPos_t lastOldPosForMeta=0; // running oldPos for delta decoding + hpatch_StreamPos_t loadedMetaEnd=0; + _oldwin_info_t winInfos[hpatch_kMaxWindowMetaCount]; + hpatch_StreamPos_t wi; + hpatch_BOOL result=hpatch_TRUE; + + hpatch_BOOL isNeedOutCache=hpatch_TRUE; + size_t kCacheCount=_kWindowCacheCount; + hpatch_size_t cacheSize; + hpatch_TUncompresser_t uncompressedStream={0}; + hpatch_StreamPos_t diffData_posEnd; + +#if (_HPATCH_IS_USED_MULTITHREAD) + struct hpatch_mt_manager_win_t* hpatch_mt_manager=0; + struct hcache_window_old_mt_t* oldCache_mt=0; + hpatchMTSets_t mtsets; + TMem2StreamImport mem2Import; +#endif + assert(out_newData!=0); + assert(out_newData->write!=0); + assert(oldData!=0); + assert(oldData->read!=0); + assert(diffStream!=0); + assert(diffStream->read!=0); + assert(diffInfo!=0); + assert(((checksumHandle_old==0)&&(checksumPlugin==0))||((checksumHandle_old!=0)&&(checksumPlugin!=0))); +#if (defined __RUN_MEM_SAFE_CHECK) + if (diffInfo->maxWindowOldSize!=(hpatch_StreamPos_t)(hpatch_size_t)diffInfo->maxWindowOldSize) return _hpatch_FALSE; + if (diffInfo->maxStepMemSize!=(hpatch_StreamPos_t)(hpatch_size_t)diffInfo->maxStepMemSize) return _hpatch_FALSE; +#endif + + if (diffInfo->compressedSize==0){ + decompressPlugin=0; + }else{ + if (decompressPlugin==0) return _hpatch_FALSE; + } + diffData_posEnd=(decompressPlugin?diffInfo->compressedSize:diffInfo->uncompressedSize)+diffData_pos; +#if (defined __RUN_MEM_SAFE_CHECK) + if (diffData_posEnd>diffStream->streamSize) return _hpatch_FALSE; +#endif +#if (_HPATCH_IS_USED_MULTITHREAD) + mtsets=hpatch_getMTSets_win(out_newData->streamSize,oldData->streamSize,diffStream->streamSize-diffData_pos, + decompressPlugin,_kWindowCacheCount,windowOldBufSize,stepMemSize, + (temp_cache_end-temp_cache),maxThreadNum,hpatchMTSets); + if (_hpatchMTSets_threadNum(mtsets)>1){ + isNeedOutCache=!mtsets.writeNew_isMT; + kCacheCount=_kWindowCacheCount-(isNeedOutCache?0:1); + hpatch_mt_manager=hpatch_mt_manager_win_open(&out_newData,&oldData,&diffStream,&diffData_pos,&diffData_posEnd, + diffInfo->uncompressedSize,&decompressPlugin,windowCount,windowOldBufSize, + stepMemSize,&temp_cache,&temp_cache_end,kCacheCount,mtsets); + if (!hpatch_mt_manager) { result=_hpatch_FALSE; goto _clear; } + oldCache_mt=hpatch_mt_manager_win_oldCache(hpatch_mt_manager); + } + if (oldCache_mt==0) +#endif + {// Single-threaded: allocate fixed-size oldBuf + assert((size_t)(temp_cache_end-temp_cache)>=windowOldBufSize+stepMemSize+hpatch_kStreamCacheSize*kCacheCount); + oldBuf=temp_cache; temp_cache+=windowOldBufSize; + } +#if (defined __RUN_MEM_SAFE_CHECK) + if ((size_t)(temp_cache_end-temp_cache)uncompressedSize,decompressPlugin,diffStream, + diffData_pos,diffData_posEnd)) { result=_hpatch_FALSE; goto _clear; } + diffStream=&uncompressedStream.base; + diffData_pos=0; + diffData_posEnd=diffStream->streamSize; + } + + if (!_readForSkipStream(diffStream,diffData_pos, //skip extraData + diffData_pos+diffInfo->extraDataSize,temp_cache,temp_cache_end)) + { result=_hpatch_FALSE; goto _clear; } + diffData_pos+=diffInfo->extraDataSize; + +#if (defined __RUN_MEM_SAFE_CHECK) + if (diffData_pos>diffData_posEnd) { result=_hpatch_FALSE; goto _clear; } + if (diffData_posEnd>diffStream->streamSize) { result=_hpatch_FALSE; goto _clear; } +#endif + + // Remaining temp_cache for stream caches + cacheSize=(hpatch_size_t)((temp_cache_end-temp_cache)-stepMemSize)/kCacheCount; + if (cacheSize>1) windows, read next metadata batch + if ((((wi)&((metaCount>>1)-1))==0) && (loadedMetaEnd>1); + hpatch_size_t writeIdx=(hpatch_size_t)(loadedMetaEnd & (metaCount-1)); + hpatch_StreamPos_t batchSize=windowCount-loadedMetaEnd; + if (batchSize>savedMetaCount) batchSize=savedMetaCount; + if (!_read_meta_batch(&mainClip,&lastOldPosForMeta,diffInfo->maxWindowOldSize,oldData->streamSize, + winInfos,writeIdx,(hpatch_size_t)batchSize)) + { result=_hpatch_FALSE; goto _clear; } + #if (_HPATCH_IS_USED_MULTITHREAD) + if (oldCache_mt) + hcache_window_old_prepareBatch(oldCache_mt,(hpatch_size_t)batchSize,winInfos,writeIdx,(hpatch_size_t)metaCount-1); + #endif + loadedMetaEnd+=batchSize; + } + + if (!_read_sub_cover_count(&mainClip,diffInfo->maxSubCoverCount,&subCoverCount)) + { result=_hpatch_FALSE; goto _clear; } + {//look up oldPos/oldLength from ring buffer + hpatch_size_t metaIdx=(hpatch_size_t)(wi & (metaCount-1)); + windowOldPos =winInfos[metaIdx].oldPos; + windowOldLength=winInfos[metaIdx].len; + } + + // Load old window data +#if (_HPATCH_IS_USED_MULTITHREAD) + if (oldCache_mt){ + unsigned char *seg1, *seg1End, *seg2, *seg2End; + if (!hcache_window_old_getWindow(oldCache_mt,windowOldPos,windowOldLength, + &seg1,&seg1End,&seg2,&seg2End)) + { result=_hpatch_FALSE; goto _clear; } + mem2_as_hStreamInput(&oldMemStream,&mem2Import,seg1,seg1End,seg2,seg2End); + if (checksumHandle_old!=0){ + checksumPlugin->append(checksumHandle_old,seg1,seg1End); + checksumPlugin->append(checksumHandle_old,seg2,seg2End); + } + } else +#endif + { + if (!_cache_load_window_old(oldData,oldBuf,windowOldPos,windowOldLength, &lastOldPos,&lastOldEnd)) + { result=_hpatch_FALSE; goto _clear; } + if (checksumHandle_old!=0) + checksumPlugin->append(checksumHandle_old,oldBuf,oldBuf+(hpatch_size_t)windowOldLength); + mem_as_hStreamInput(&oldMemStream,oldBuf,oldBuf+(hpatch_size_t)windowOldLength); + } + + if (!_patch_single_stream_loop(&mainClip,&outCache,&oldMemStream,subCoverCount,stepMemSize, + temp_cache,temp_cache+stepMemSize,cacheSize, 0//no coversListener + #if (_IS_NEED_CACHE_OLD_BY_COVERS) + ,hpatch_FALSE + #endif + )) + { result=_hpatch_FALSE; goto _clear; } + +#if (_HPATCH_IS_USED_MULTITHREAD) + if (oldCache_mt){ + hcache_window_old_finishWindow(oldCache_mt); + } +#endif + } + + //flush and verify + if ((!_TOutStreamCache_flush(&outCache)) + ||(!_TOutStreamCache_isFinish(&outCache)) + ||(!_TStreamCacheClip_isFinish(&mainClip))) + { result=_hpatch_FALSE; goto _clear; } + +_clear: + if (decompressPlugin) + close_compressed_stream_as_uncompressed(&uncompressedStream); +#if (_HPATCH_IS_USED_MULTITHREAD) + if (hpatch_mt_manager){ + if (!hpatch_mt_manager_win_close(hpatch_mt_manager,!result)) + result=_hpatch_FALSE; + hpatch_mt_manager=0; + } +#endif + return result; +} + + + //checksum stream wrappers + typedef struct _TChecksumInputStream{ + hpatch_TStreamInput base; + const hpatch_TStreamInput* realIn; + hpatch_TChecksum* checksumPlugin; + hpatch_checksumHandle checksumHandle; + hpatch_StreamPos_t readedEndPos; + } _TChecksumInputStream; + + static hpatch_BOOL _TChecksumInputStream_read(const hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end){ + _TChecksumInputStream* self=(_TChecksumInputStream*)stream->streamImport; + if (!self->realIn->read(self->realIn,readFromPos,out_data,out_data_end)) + return _hpatch_FALSE; + self->checksumPlugin->append(self->checksumHandle,out_data,out_data_end); + self->readedEndPos=readFromPos+(out_data_end-out_data); + return hpatch_TRUE; + } + + static void _TChecksumInputStream_init(_TChecksumInputStream* self,const hpatch_TStreamInput* realIn, + hpatch_TChecksum* checksumPlugin,hpatch_checksumHandle checksumHandle){ + assert(self); + assert(realIn); + assert(checksumPlugin); + assert(checksumHandle); + self->realIn=realIn; + self->checksumPlugin=checksumPlugin; + self->checksumHandle=checksumHandle; + self->readedEndPos=0; + self->base.streamImport=self; + self->base.streamSize=realIn->streamSize; + self->base.read=_TChecksumInputStream_read; + } + + typedef struct _TChecksumOutputStream{ + hpatch_TStreamOutput base; + const hpatch_TStreamOutput* realOut; + hpatch_TChecksum* checksumPlugin; + hpatch_checksumHandle checksumHandle; + hpatch_StreamPos_t writedEndPos; + } _TChecksumOutputStream; + + static hpatch_BOOL _TChecksumOutputStream_write(const hpatch_TStreamOutput* stream, + hpatch_StreamPos_t writeToPos, + const unsigned char* data,const unsigned char* data_end){ + _TChecksumOutputStream* self=(_TChecksumOutputStream*)stream->streamImport; + if (!self->realOut->write(self->realOut,writeToPos,data,data_end)) + return _hpatch_FALSE; + self->checksumPlugin->append(self->checksumHandle,data,data_end); + self->writedEndPos=writeToPos+(data_end-data); + return hpatch_TRUE; + } + + static void _TChecksumOutputStream_init(_TChecksumOutputStream* self,const hpatch_TStreamOutput* realOut, + hpatch_TChecksum* checksumPlugin,hpatch_checksumHandle checksumHandle){ + assert(self); + assert(realOut); + assert(checksumPlugin); + assert(checksumHandle); + self->realOut=realOut; + self->checksumPlugin=checksumPlugin; + self->checksumHandle=checksumHandle; + self->writedEndPos=0; + self->base.streamImport=self; + self->base.streamSize=realOut->streamSize; + self->base.write=_TChecksumOutputStream_write; + self->base.read_writed=0; + } + +TWindowPatchResult patch_window_diff(winpatch_listener_t* listener, + const hpatch_TStreamOutput* __out_newData, + const hpatch_TStreamInput* oldData, + const hpatch_TStreamInput* windowDiff, + hpatch_StreamPos_t diffInfo_pos,size_t threadNum){ + TWindowPatchResult result=kWindowPatch_ok; + hpatch_BOOL patch_result; + hpatch_windowDiffInfo diffInfo; + hpatch_TDecompress* decompressPlugin=0; + hpatch_TChecksum* checksumPlugin=0; + unsigned char* temp_cache_back=0; + unsigned char* temp_cache=0; + unsigned char* temp_cacheEnd=0; + hpatch_BOOL isChecksumNew; + hpatch_BOOL isChecksumOld=hpatch_FALSE; + hpatch_BOOL isChecksumDiff=hpatch_FALSE; + hpatch_checksumHandle checksumHandle_old=0; + hpatch_checksumHandle checksumHandle_new=0; + hpatch_checksumHandle checksumHandle_diff=0; + hpatch_size_t checksumByteSize; + const hpatch_TStreamInput* effectiveDiffStream; + _TChecksumInputStream checksumDiffInput; + _TChecksumOutputStream checksumNewOutput; + const hpatch_TStreamOutput* effectiveOutNew; + hpatch_TStreamOutput _out_newData=*__out_newData; + hpatch_TStreamOutput* out_newData=&_out_newData; + hpatch_byte headerCache[hpatch_kWindowDiffHeadMaxSize]; + hpatch_size_t headerSize=sizeof(headerCache); + + assert(listener!=0); + assert(listener->onDiffInfo!=0); + assert(out_newData->write!=0); + assert(oldData!=0); + assert(oldData->read!=0); + assert(windowDiff!=0); + assert(windowDiff->read!=0); + + if (!_getWindowDiffInfo(&diffInfo,windowDiff,diffInfo_pos,headerCache,&headerSize))//parse header + return kWindowPatch_load_head_error; + if (diffInfo.newDataSize>out_newData->streamSize)//check output limit + return kWindowPatch_new_size_error; + out_newData->streamSize=diffInfo.newDataSize;//adjust output stream size + + isChecksumNew=(diffInfo.checksumByteSize>0); + if (!listener->onDiffInfo(listener,&diffInfo,&decompressPlugin,&checksumPlugin, + &isChecksumNew,&isChecksumOld,&isChecksumDiff,&temp_cache,&temp_cacheEnd)) + return kWindowPatch_onDiffInfo_error; + if ((temp_cache==0)||(temp_cache>=temp_cacheEnd)) + return kWindowPatch_temp_mem_error; + if (diffInfo.maxWindowOldSize+diffInfo.maxStepMemSize+hpatch_kStreamCacheSize*_kWindowCacheCount>(size_t)(temp_cacheEnd-temp_cache)) + return kWindowPatch_temp_mem_error; + temp_cache_back=temp_cache; + + if ((diffInfo.checksumByteSize>0)&&(isChecksumNew||isChecksumOld||isChecksumDiff)){ + if ((!checksumPlugin)||(diffInfo.checksumByteSize!=checksumPlugin->checksumByteSize())) + return kWindowPatch_checksum_plugin_error; + checksumByteSize=(size_t)diffInfo.checksumByteSize; + }else{ + checksumByteSize=0; checksumPlugin=0; + isChecksumNew=hpatch_FALSE; isChecksumOld=hpatch_FALSE; isChecksumDiff=hpatch_FALSE; + } + + if (isChecksumOld){ + checksumHandle_old=checksumPlugin->open(checksumPlugin); + if (!checksumHandle_old) { result=kWindowPatch_checksum_open_error; goto clear; } + checksumPlugin->begin(checksumHandle_old); + } + if (isChecksumNew){ + checksumHandle_new=checksumPlugin->open(checksumPlugin); + if (!checksumHandle_new) { result=kWindowPatch_checksum_open_error; goto clear; } + checksumPlugin->begin(checksumHandle_new); + } + if (isChecksumDiff){ + checksumHandle_diff=checksumPlugin->open(checksumPlugin); + if (!checksumHandle_diff) { result=kWindowPatch_checksum_open_error; goto clear; } + checksumPlugin->begin(checksumHandle_diff); + } + + if (isChecksumDiff){//wrap diff stream input for checksum + _TChecksumInputStream_init(&checksumDiffInput,windowDiff,checksumPlugin,checksumHandle_diff); + effectiveDiffStream=&checksumDiffInput.base; + }else{ + effectiveDiffStream=windowDiff; + } + + if (isChecksumNew){//wrap new stream output for checksum + _TChecksumOutputStream_init(&checksumNewOutput,out_newData,checksumPlugin,checksumHandle_new); + effectiveOutNew=&checksumNewOutput.base; + }else{ + effectiveOutNew=out_newData; + } + + patch_result=_patch_window_diff(effectiveOutNew,oldData,effectiveDiffStream,diffInfo_pos+diffInfo.windowDataPos, + &diffInfo,decompressPlugin,checksumHandle_old?checksumPlugin:0,checksumHandle_old, + temp_cache,temp_cacheEnd, threadNum,hpatchMTSets_full); + + {//verify checksums + hpatch_byte* storedChecksumOld=headerCache+diffInfo.windowDataPos-3*checksumByteSize; + hpatch_byte* storedChecksumNew=storedChecksumOld+checksumByteSize; + hpatch_byte* storedChecksumDiff=storedChecksumNew+checksumByteSize; + hpatch_byte* computedChecksum=temp_cache; temp_cache+=checksumByteSize; assert(temp_cacheend(checksumHandle_new,computedChecksum,computedChecksum+checksumByteSize); + isChecksumNew_and_eq=(0==memcmp(computedChecksum,storedChecksumNew,checksumByteSize)); + if (isChecksumNew_and_eq){ + //assert(patch_result); //why fail? + patch_result=hpatch_TRUE;//note: if patch failed, but new data checksum is correct, we can consider it as success. + } + } + if (!isChecksumNew_and_eq){ + if (isChecksumDiff){//verify diff checksum + //read to diffData end for checksum + if (!_readForSkipStream(&checksumDiffInput.base,checksumDiffInput.readedEndPos, + checksumDiffInput.base.streamSize,temp_cache,temp_cacheEnd)) + { result=kWindowPatch_patch_error; goto clear; } + checksumPlugin->append(checksumHandle_diff,headerCache,headerCache+headerSize-checksumByteSize);//checksum head + checksumPlugin->end(checksumHandle_diff,computedChecksum,computedChecksum+checksumByteSize); + if (0!=memcmp(computedChecksum,storedChecksumDiff,checksumByteSize)) + { result=kWindowPatch_checksum_diff_error; goto clear; } + } + if ((!patch_result)&&(diffInfo.oldDataSize!=oldData->streamSize)) + { result=kWindowPatch_old_size_error; goto clear; } + if ((patch_result||(isChecksumNew&&(checksumNewOutput.writedEndPos==diffInfo.newDataSize)))//all old's data checked + &&isChecksumOld){//verify old data checksum + checksumPlugin->end(checksumHandle_old,computedChecksum,computedChecksum+checksumByteSize); + if (0!=memcmp(computedChecksum,storedChecksumOld,checksumByteSize)) + { result=kWindowPatch_checksum_old_error; goto clear; } + } + if (isChecksumNew) { result=kWindowPatch_checksum_new_error; goto clear; } + } + } + if (!patch_result) { result=kWindowPatch_patch_error; goto clear; } + +clear: + if (checksumHandle_old!=0) checksumPlugin->close(checksumPlugin,checksumHandle_old); + if (checksumHandle_new!=0) checksumPlugin->close(checksumPlugin,checksumHandle_new); + if (checksumHandle_diff!=0) checksumPlugin->close(checksumPlugin,checksumHandle_diff); + if (listener->onPatchFinish) + listener->onPatchFinish(listener,temp_cache_back,temp_cacheEnd); + return result; +} + diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch.h b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch.h new file mode 100644 index 00000000..825d5368 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch.h @@ -0,0 +1,268 @@ +//patch.h +// +/* + The MIT License (MIT) + Copyright (c) 2012-2018 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef HPatch_patch_h +#define HPatch_patch_h +#include "patch_types.h" +#include "hpatch_mt/hpatch_mt.h" +#include "checksum_plugin.h" + +#ifdef __cplusplus +extern "C" { +#endif + +//all patch*() functions do not allocate memory + +//optimize speed for patch_stream_with_cache() & patch_decompress_with_cache() +// & patch_single_stream(),patch_single_compressed_diff(),patch_single_stream_diff(): +// preload part of oldData into cache, +// cache memory size (temp_cache_end-temp_cache) the larger the better for large oldData file +#ifndef _IS_NEED_CACHE_OLD_BY_COVERS +# define _IS_NEED_CACHE_OLD_BY_COVERS 1 +#endif +#ifndef _IS_NEED_CACHE_OLD_ALL +# define _IS_NEED_CACHE_OLD_ALL 1 +#endif + + +//generate newData by patch(oldData + serializedDiff) +// serializedDiff create by create_diff() +// NOTE: create_diff() + patch() or patch_stream() or patch_stream_with_cache() is no longer recommended, +// recommend change to use create_single_compressed_diff() + patch_single_stream() or patch_single_compressed_diff() +hpatch_BOOL patch(unsigned char* out_newData,unsigned char* out_newData_end, + const unsigned char* oldData,const unsigned char* oldData_end, + const unsigned char* serializedDiff,const unsigned char* serializedDiff_end); + +//patch by stream, see patch() +// used (hpatch_kStreamCacheSize*8 stack memory) for I/O cache +// if use patch_stream_with_cache(), can passing larger memory cache to optimize speed +// serializedDiff create by create_diff() +// NOTE: create_diff() + patch() or patch_stream() or patch_stream_with_cache() is no longer recommended, +// recommend change to use create_single_compressed_diff() + patch_single_stream() or patch_single_compressed_diff() +hpatch_BOOL patch_stream(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* serializedDiff); //random read + +//see patch_stream() +// can passing more memory for I/O cache to optimize speed +// note: (temp_cache_end-temp_cache)>=2048 +// NOTE: create_diff() + patch() or patch_stream() or patch_stream_with_cache() is no longer recommended, +// recommend change to use create_single_compressed_diff() + patch_single_stream() or patch_single_compressed_diff() +hpatch_BOOL patch_stream_with_cache(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* serializedDiff, //random read + unsigned char* temp_cache,unsigned char* temp_cache_end); + + + + +//get compressedDiff info +// compressedDiff created by create_compressed_diff() or create_compressed_diff_stream() +hpatch_BOOL getCompressedDiffInfo(hpatch_compressedDiffInfo* out_diffInfo, + const hpatch_TStreamInput* compressedDiff); +//see getCompressedDiffInfo() +hpatch_inline static hpatch_BOOL + getCompressedDiffInfo_mem(hpatch_compressedDiffInfo* out_diffInfo, + const unsigned char* compressedDiff, + const unsigned char* compressedDiff_end){ + hpatch_TStreamInput diffStream; + mem_as_hStreamInput(&diffStream,compressedDiff,compressedDiff_end); + return getCompressedDiffInfo(out_diffInfo,&diffStream); + } + +//patch with decompress plugin +// used (hpatch_kStreamCacheSize*6 stack memory) + (decompress buffer*4) +// compressedDiff create by create_compressed_diff() or create_compressed_diff_stream() +// decompressPlugin can be null when there is no compressed data in compressedDiff +// if using patch_decompress_with_cache(), a larger memory cache can be passed to optimize speed +hpatch_BOOL patch_decompress(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* compressedDiff, //random read + hpatch_TDecompress* decompressPlugin); + +//see patch_decompress() +// can passing larger memory cache to optimize speed +// note: (temp_cache_end-temp_cache)>=2048 +hpatch_BOOL patch_decompress_with_cache(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* compressedDiff, //random read + hpatch_TDecompress* decompressPlugin, + unsigned char* temp_cache,unsigned char* temp_cache_end); + +//see patch_decompress() +hpatch_inline static hpatch_BOOL + patch_decompress_mem(unsigned char* out_newData,unsigned char* out_newData_end, + const unsigned char* oldData,const unsigned char* oldData_end, + const unsigned char* compressedDiff,const unsigned char* compressedDiff_end, + hpatch_TDecompress* decompressPlugin){ + hpatch_TStreamOutput out_newStream; + hpatch_TStreamInput oldStream; + hpatch_TStreamInput diffStream; + mem_as_hStreamOutput(&out_newStream,out_newData,out_newData_end); + mem_as_hStreamInput(&oldStream,oldData,oldData_end); + mem_as_hStreamInput(&diffStream,compressedDiff,compressedDiff_end); + return patch_decompress(&out_newStream,&oldStream,&diffStream,decompressPlugin); + } + + + + +// hpatch_TCoverList: open diffData and read coverList + typedef struct hpatch_TCoverList{ + hpatch_TCovers* ICovers; + //private: + unsigned char _buf[hpatch_kStreamCacheSize*4]; + } hpatch_TCoverList; + +hpatch_inline static +void hpatch_coverList_init(hpatch_TCoverList* coverList) { + assert(coverList!=0); memset(coverList,0,sizeof(*coverList)-sizeof(coverList->_buf)); } +// serializedDiff create by create_diff() +hpatch_BOOL hpatch_coverList_open_serializedDiff(hpatch_TCoverList* out_coverList, + const hpatch_TStreamInput* serializedDiff); +// compressedDiff create by create_compressed_diff() or create_compressed_diff_stream() +hpatch_BOOL hpatch_coverList_open_compressedDiff(hpatch_TCoverList* out_coverList, + const hpatch_TStreamInput* compressedDiff, + hpatch_TDecompress* decompressPlugin); +hpatch_inline static +hpatch_BOOL hpatch_coverList_close(hpatch_TCoverList* coverList) { + hpatch_BOOL result=hpatch_TRUE; + if ((coverList!=0)&&(coverList->ICovers)){ + result=coverList->ICovers->close(coverList->ICovers); + hpatch_coverList_init(coverList); } return result; } + + +//patch singleCompressedDiff with listener +// used (stepMemSize memory) + (I/O cache memory) + (decompress buffer*1) +// every byte in singleCompressedDiff will only be read once in order +// singleCompressedDiff create by create_single_compressed_diff() or create_single_compressed_diff_stream() or create_hdiff_by_sign() +// you can download&patch diffData at the same time, without saving it to disk +// same as calling getSingleCompressedDiffInfo() + listener->onDiffInfo() + patch_single_compressed_diff() +static hpatch_force_inline +hpatch_BOOL patch_single_stream(sspatch_listener_t* listener, //call back when got diffInfo + const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* singleCompressedDiff, //sequential read every byte + hpatch_StreamPos_t diffInfo_pos, //default 0, begin pos in singleCompressedDiff + sspatch_coversListener_t* coversListener, //default NULL, call by on got some covers + size_t threadNum){ // 1..5; if >1, multi-thread for I/O & decompress etc. + return _patch_single_stream_mt(listener,out_newData,oldData,singleCompressedDiff, + diffInfo_pos,coversListener,threadNum,hpatchMTSets_full); + } +static hpatch_force_inline +hpatch_BOOL patch_single_stream_mem(sspatch_listener_t* listener, + unsigned char* out_newData,unsigned char* out_newData_end, + const unsigned char* oldData,const unsigned char* oldData_end, + const unsigned char* diff,const unsigned char* diff_end, + sspatch_coversListener_t* coversListener,size_t threadNum){ + hpatch_TStreamOutput out_newStream; + hpatch_TStreamInput oldStream; + hpatch_TStreamInput diffStream; + const hpatchMTSets_t hpatchMTSets={0,0,0,1}; //all data in mem, I/O not need MT + mem_as_hStreamOutput(&out_newStream,out_newData,out_newData_end); + mem_as_hStreamInput(&oldStream,oldData,oldData_end); + mem_as_hStreamInput(&diffStream,diff,diff_end); + return _patch_single_stream_mt(listener,&out_newStream,&oldStream,&diffStream,0,coversListener, + threadNum,hpatchMTSets); + } + +//get singleCompressedDiff info +// singleCompressedDiff create by create_single_compressed_diff() or create_single_compressed_diff_stream() or create_hdiff_by_sign() +hpatch_BOOL getSingleCompressedDiffInfo(hpatch_singleCompressedDiffInfo* out_diffInfo, + const hpatch_TStreamInput* singleCompressedDiff, //sequential read + hpatch_StreamPos_t diffInfo_pos//default 0, begin pos in singleCompressedDiff + ); +static hpatch_force_inline +hpatch_BOOL getSingleCompressedDiffInfo_mem(hpatch_singleCompressedDiffInfo* out_diffInfo, + const unsigned char* singleCompressedDiff, + const unsigned char* singleCompressedDiff_end){ + hpatch_TStreamInput diffStream; + mem_as_hStreamInput(&diffStream,singleCompressedDiff,singleCompressedDiff_end); + return getSingleCompressedDiffInfo(out_diffInfo,&diffStream,0); + } + + +//patch singleCompressedDiff with diffInfo +// used (stepMemSize memory) + (I/O cache memory) + (decompress buffer*1) +// note: (I/O cache memory) >= hpatch_kStreamCacheSize*3 +// temp_cache_end-temp_cache == stepMemSize + (I/O cache memory) +// singleCompressedDiff create by create_single_compressed_diff() or create_single_compressed_diff_stream() or create_hdiff_by_sign() +// decompressPlugin can be null when there is no compressed data in singleCompressedDiff +// same as calling compressed_stream_as_uncompressed() + patch_single_stream_diff() +static hpatch_force_inline +hpatch_BOOL patch_single_compressed_diff(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* singleCompressedDiff, //sequential read + hpatch_StreamPos_t diffData_pos, //diffData begin pos in singleCompressedDiff + hpatch_StreamPos_t uncompressedSize, + hpatch_StreamPos_t compressedSize, + hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t coverCount,hpatch_size_t stepMemSize, + unsigned char* temp_cache,unsigned char* temp_cache_end, + sspatch_coversListener_t* coversListener, //default NULL, call by on got covers + size_t threadNum){ // 1..5; if >1, multi-thread for I/O & decompress etc. + return _patch_single_compressed_diff_mt(out_newData,oldData,singleCompressedDiff,diffData_pos,uncompressedSize,compressedSize, + decompressPlugin,coverCount,stepMemSize,temp_cache,temp_cache_end,coversListener, + threadNum,hpatchMTSets_full); + } + +hpatch_BOOL compressed_stream_as_uncompressed(hpatch_TUncompresser_t* uncompressedStream,hpatch_StreamPos_t uncompressedSize, + hpatch_TDecompress* decompressPlugin,const hpatch_TStreamInput* compressedStream, + hpatch_StreamPos_t compressed_pos,hpatch_StreamPos_t compressed_end); +void close_compressed_stream_as_uncompressed(hpatch_TUncompresser_t* uncompressedStream); + +hpatch_BOOL patch_single_stream_diff(const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* uncompressedDiffData, //sequential read + hpatch_StreamPos_t diffData_pos,//diffData begin pos in uncompressedDiffData + hpatch_StreamPos_t diffData_posEnd,//diffData end pos in uncompressedDiffData + hpatch_StreamPos_t coverCount,hpatch_size_t stepMemSize, + unsigned char* temp_cache,unsigned char* temp_cache_end, + sspatch_coversListener_t* coversListener, + hpatch_BOOL isNeedOutCache //default true: each time accumulating some data be write to out_newData; + ); + + +hpatch_BOOL getWindowDiffInfo(hpatch_windowDiffInfo* out_diffInfo, + const hpatch_TStreamInput* windowDiff, //sequential read + hpatch_StreamPos_t diffInfo_pos//default 0, begin pos in windowDiff + ); + +TWindowPatchResult patch_window_diff(struct winpatch_listener_t* listener, + const hpatch_TStreamOutput* out_newData, //sequential write + const hpatch_TStreamInput* oldData, //random read + const hpatch_TStreamInput* windowDiff, //sequential read + hpatch_StreamPos_t diffInfo_pos, //default 0, begin pos in windowDiff + size_t threadNum //1 for single-threaded, 2..5 for MT I/O + ); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_private.h b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_private.h new file mode 100644 index 00000000..31d8a935 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_private.h @@ -0,0 +1,297 @@ +// patch_private.h +// +/* + The MIT License (MIT) + Copyright (c) 2012-2018 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef HPatch_patch_private_h +#define HPatch_patch_private_h + +#include "patch.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +//byte rle type , ctrl code: high 2bit + packedLen(6bit+...) +typedef enum TByteRleType{ + kByteRleType_rle0 = 0, //00 rle 0 , data code:0 byte + kByteRleType_rle255= 1, //01 rle 255, data code:0 byte + kByteRleType_rle = 2, //10 rle x(1--254), data code:1 byte (save x) + kByteRleType_unrle = 3 //11 n byte data, data code:n byte(save no rle data) +} TByteRleType; + +static const hpatch_uint kByteRleType_bit=2; + + +typedef struct _THDiffzHead{ + hpatch_StreamPos_t coverCount; + + hpatch_StreamPos_t cover_buf_size; + hpatch_StreamPos_t compress_cover_buf_size; + hpatch_StreamPos_t rle_ctrlBuf_size; + hpatch_StreamPos_t compress_rle_ctrlBuf_size; + hpatch_StreamPos_t rle_codeBuf_size; + hpatch_StreamPos_t compress_rle_codeBuf_size; + hpatch_StreamPos_t newDataDiff_size; + hpatch_StreamPos_t compress_newDataDiff_size; + + hpatch_StreamPos_t typesEndPos; + hpatch_StreamPos_t compressSizeBeginPos; + hpatch_StreamPos_t headEndPos; + hpatch_StreamPos_t coverEndPos; +} _THDiffzHead; + +hpatch_BOOL read_diffz_head(hpatch_compressedDiffInfo* out_diffInfo,_THDiffzHead* out_head, + const hpatch_TStreamInput* compressedDiff); + +// Stream Clip cache +typedef struct TStreamCacheClip{ + hpatch_StreamPos_t streamPos; + hpatch_StreamPos_t streamPos_end; + const hpatch_TStreamInput* srcStream; + unsigned char* cacheBuf; + hpatch_size_t cacheBegin; + hpatch_size_t cacheEnd; +} TStreamCacheClip; + +hpatch_inline static +void _TStreamCacheClip_init(TStreamCacheClip* sclip,const hpatch_TStreamInput* srcStream, + hpatch_StreamPos_t streamPos,hpatch_StreamPos_t streamPos_end, + unsigned char* aCache,hpatch_size_t cacheSize){ + assert((streamPos<=streamPos_end)&&(streamPos_end<=(srcStream?srcStream->streamSize:0))); + sclip->streamPos=streamPos; + sclip->streamPos_end=streamPos_end; + sclip->srcStream=srcStream; + sclip->cacheBuf=aCache; + sclip->cacheBegin=cacheSize; + sclip->cacheEnd=cacheSize; +} + +#define _TStreamCacheClip_isFinish(sclip) ( 0==_TStreamCacheClip_leaveSize(sclip) ) +#define _TStreamCacheClip_isCacheEmpty(sclip) ( (sclip)->cacheBegin==(sclip)->cacheEnd ) +#define _TStreamCacheClip_cachedSize(sclip) ( (hpatch_size_t)((sclip)->cacheEnd-(sclip)->cacheBegin) ) +#define _TStreamCacheClip_leaveSize(sclip) \ + ( (hpatch_StreamPos_t)((sclip)->streamPos_end-(sclip)->streamPos) \ + + (hpatch_StreamPos_t)_TStreamCacheClip_cachedSize(sclip) ) +#define _TStreamCacheClip_readPosOfSrcStream(sclip) ( \ + (sclip)->streamPos - _TStreamCacheClip_cachedSize(sclip) ) + +hpatch_BOOL _TStreamCacheClip_updateCache(TStreamCacheClip* sclip); + +hpatch_inline static //error return 0 +unsigned char* _TStreamCacheClip_accessData(TStreamCacheClip* sclip,hpatch_size_t readSize){ + //assert(readSize<=sclip->cacheEnd); + if (readSize>_TStreamCacheClip_cachedSize(sclip)){ + if (!_TStreamCacheClip_updateCache(sclip)) return 0; + if (readSize>_TStreamCacheClip_cachedSize(sclip)) return 0; + } + return &sclip->cacheBuf[sclip->cacheBegin]; +} + +#define _TStreamCacheClip_skipData_noCheck(sclip,skipSize) ((sclip)->cacheBegin+=skipSize) +hpatch_BOOL _TStreamCacheClip_skipData(TStreamCacheClip* sclip,hpatch_StreamPos_t skipLongSize); + +hpatch_inline static //error return 0 +unsigned char* _TStreamCacheClip_readData(TStreamCacheClip* sclip,hpatch_size_t readSize){ + unsigned char* result=_TStreamCacheClip_accessData(sclip,readSize); + _TStreamCacheClip_skipData_noCheck(sclip,readSize); + return result; +} + +hpatch_BOOL _TStreamCacheClip_readDataTo(TStreamCacheClip* sclip, + unsigned char* out_buf,unsigned char* bufEnd); +hpatch_BOOL _TStreamCacheClip_addDataTo(TStreamCacheClip* self,unsigned char* dst,hpatch_size_t addLen); + +hpatch_BOOL _TStreamCacheClip_unpackUIntWithTag(TStreamCacheClip* sclip, + hpatch_StreamPos_t* result,const hpatch_uint kTagBit); + +hpatch_BOOL _TStreamCacheClip_readStr_end(TStreamCacheClip* sclip,hpatch_byte endTag, + char* out_str,size_t strBufLen); +hpatch_inline static +hpatch_BOOL _TStreamCacheClip_readType_end(TStreamCacheClip* sclip,hpatch_byte endTag, + char out_type[hpatch_kMaxPluginTypeLength+1]){ + return _TStreamCacheClip_readStr_end(sclip,endTag,out_type,hpatch_kMaxPluginTypeLength+1); } + +// Stream Clip cache +typedef struct { + hpatch_StreamPos_t writeToPos; + const hpatch_TStreamOutput* dstStream; + unsigned char* cacheBuf; + hpatch_size_t cacheCur; + hpatch_size_t cacheEnd; +} _TOutStreamCache; + +static hpatch_inline void _TOutStreamCache_init(_TOutStreamCache* self,const hpatch_TStreamOutput* dstStream, + unsigned char* aCache,hpatch_size_t aCacheSize){ + self->writeToPos=0; + self->cacheCur=0; + self->dstStream=dstStream; + self->cacheBuf=aCache; + self->cacheEnd=aCacheSize; +} +hpatch_inline static +void _TOutStreamCache_resetCache(_TOutStreamCache* self,unsigned char* aCache,hpatch_size_t aCacheSize){ + assert(0==self->cacheCur); + self->cacheBuf=aCache; + self->cacheEnd=aCacheSize; +} + +static hpatch_inline hpatch_StreamPos_t _TOutStreamCache_leaveSize(const _TOutStreamCache* self){ + return self->dstStream->streamSize-self->writeToPos; +} +static hpatch_inline hpatch_BOOL _TOutStreamCache_isFinish(const _TOutStreamCache* self){ + return self->writeToPos==self->dstStream->streamSize; +} +static hpatch_inline hpatch_size_t _TOutStreamCache_cachedDataSize(const _TOutStreamCache* self){ + return self->cacheCur; +} +hpatch_BOOL _TOutStreamCache_flush(_TOutStreamCache* self); +hpatch_BOOL _TOutStreamCache_write(_TOutStreamCache* self,const unsigned char* data,hpatch_size_t dataSize); +hpatch_BOOL _TOutStreamCache_fill(_TOutStreamCache* self,hpatch_byte fillValue,hpatch_StreamPos_t fillLength); + +hpatch_BOOL _TOutStreamCache_copyFromClip(_TOutStreamCache* self,TStreamCacheClip* src,hpatch_StreamPos_t copyLength); +hpatch_BOOL _TOutStreamCache_copyFromStream(_TOutStreamCache* self,const hpatch_TStreamInput* src, + hpatch_StreamPos_t srcPos,hpatch_StreamPos_t copyLength); +hpatch_BOOL _TOutStreamCache_copyFromSelf(_TOutStreamCache* self,hpatch_StreamPos_t aheadLength,hpatch_StreamPos_t copyLength); + + + typedef struct _TDecompressInputStream{ + hpatch_TStreamInput IInputStream; + hpatch_TDecompress* decompressPlugin; + hpatch_decompressHandle decompressHandle; + } _TDecompressInputStream; + +hpatch_BOOL getStreamClip(TStreamCacheClip* out_clip,_TDecompressInputStream* out_stream, + hpatch_StreamPos_t dataSize,hpatch_StreamPos_t compressedSize, + const hpatch_TStreamInput* stream,hpatch_StreamPos_t* pCurStreamPos, + hpatch_TDecompress* decompressPlugin,unsigned char* aCache,hpatch_size_t cacheSize); + + +#define _TDiffToSingleStream_kBufSize hpatch_kStreamCacheSize +typedef struct { + hpatch_TStreamInput base; + const hpatch_TStreamInput* diffStream; + hpatch_StreamPos_t readedSize; + hpatch_size_t cachedBufBegin; + hpatch_BOOL isInSingleStream; + unsigned char buf[_TDiffToSingleStream_kBufSize]; +} TDiffToSingleStream; + +void TDiffToSingleStream_init(TDiffToSingleStream* self,const hpatch_TStreamInput* diffStream); +hpatch_inline static +void TDiffToSingleStream_setInSingleStream(TDiffToSingleStream* self,hpatch_StreamPos_t singleStreamPos){ + self->isInSingleStream=hpatch_TRUE; } + +hpatch_inline static +void TDiffToSingleStream_resetStream(TDiffToSingleStream* self,const hpatch_TStreamInput* diffStream){ + self->diffStream=diffStream; } + +static hpatch_force_inline +hpatch_StreamPos_t _patch_cache_all_old_needSize(hpatch_StreamPos_t oldDataSize,hpatch_size_t kMinTempCacheSize){ + return oldDataSize+kMinTempCacheSize+sizeof(hpatch_TStreamInput)+sizeof(hpatch_StreamPos_t); } +#if (_IS_NEED_CACHE_OLD_ALL) +static hpatch_force_inline +hpatch_BOOL _patch_is_can_cache_all_old(hpatch_StreamPos_t oldDataSize,hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize){ + return tempCacheSize>=_patch_cache_all_old_needSize(oldDataSize,kMinTempCacheSize); } +hpatch_BOOL _patch_cache_all_old(const hpatch_TStreamInput** poldData,size_t kMinTempCacheSize, + hpatch_byte** ptemp_cache,hpatch_byte** ptemp_cache_end,hpatch_BOOL* out_isReadError);// try cache all oldData +#else +static hpatch_force_inline +hpatch_BOOL _patch_is_can_cache_all_old(hpatch_StreamPos_t oldDataSize,hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize){ + return hpatch_FALSE; } +static hpatch_force_inline +hpatch_BOOL _patch_cache_all_old(const hpatch_TStreamInput** poldData,size_t kMinTempCacheSize, + hpatch_byte** ptemp_cache,hpatch_byte** ptemp_cache_end,hpatch_BOOL* out_isReadError){ + *out_isReadError=hpatch_FALSE; return hpatch_FALSE; } +#endif + +#if (_IS_NEED_CACHE_OLD_BY_COVERS) + +hpatch_size_t _patch_step_cache_old_canUsedSize(hpatch_size_t stepCoversMemSize,hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize); + +// try cache part of oldData, used by patch_single_stream_diff() +hpatch_BOOL _patch_step_cache_old(const hpatch_TStreamInput** poldData,hpatch_StreamPos_t newDataSize,size_t stepCoversMemSize, + size_t kMinTempCacheSize,hpatch_byte** ptemp_cache,hpatch_byte** ptemp_cache_end); +hpatch_BOOL _patch_step_cache_old_onStepCovers(const hpatch_TStreamInput* self,const unsigned char* covers_cache,const unsigned char* covers_cacheEnd); +#endif // _IS_NEED_CACHE_OLD_BY_COVERS +hpatch_size_t _patch_is_can_cache_window_old_canUsedSize(hpatch_size_t windowOldBufSize,hpatch_size_t stepCoversMemSize, + hpatch_size_t kMinTempCacheSize,hpatch_size_t tempCacheSize,hpatch_StreamPos_t oldDataSize); + +// ---- window overlap computation (shared by ST and MT) ---- + + typedef struct _oldwin_info_t{ + hpatch_StreamPos_t oldPos; + hpatch_StreamPos_t len; + } _oldwin_info_t; + +typedef enum { + _kOverlapType_none = 0, // no overlap (or no reuse) + _kOverlapType_contained, // curr window fully contained in prev window + _kOverlapType_rightTail, // curr starts inside prev, extends past prev end (tail overlap) + _kOverlapType_leftHead, // curr starts before prev, ends inside prev (head overlap) + _kOverlapType_superset // curr fully contains prev +} _kOverlapType_t; + +static hpatch_inline _kOverlapType_t _compute_window_overlap(hpatch_StreamPos_t prevOldPos, hpatch_StreamPos_t prevOldEnd, + hpatch_StreamPos_t currOldPos, hpatch_StreamPos_t currOldEnd,hpatch_StreamPos_t* out_reuseLen, + hpatch_StreamPos_t* out_reuseOff,hpatch_StreamPos_t* out_noOverlapLeft){ + hpatch_StreamPos_t oStart=(prevOldPos>currOldPos)?prevOldPos:currOldPos; + hpatch_StreamPos_t oEnd=(prevOldEnd=oEnd){ + *out_reuseLen=0; + *out_reuseOff=0; // no value + *out_noOverlapLeft=0; // no value + return _kOverlapType_none; // no overlap + }else if ((currOldPos>=prevOldPos)&&(currOldEnd<=prevOldEnd)){// curr include prev (fully contained) + *out_reuseLen=currOldEnd-currOldPos; + *out_reuseOff=currOldPos-prevOldPos; + *out_noOverlapLeft=0; + return _kOverlapType_contained; + }else if ((currOldPos>=prevOldPos)&&(currOldPosprevOldPos)&&(currOldEnd<=prevOldEnd)){// left-side/head overlap + *out_reuseLen=currOldEnd-prevOldPos; + *out_reuseOff=0; + *out_noOverlapLeft=prevOldPos-currOldPos; + return _kOverlapType_leftHead; + }else{// curr contains prev (superset) + assert((currOldPosprevOldEnd)); + *out_reuseLen=prevOldEnd-prevOldPos; + *out_reuseOff=0; + *out_noOverlapLeft=prevOldPos-currOldPos; + return _kOverlapType_superset; + } +} + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_types.h b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_types.h new file mode 100644 index 00000000..e5ebaae5 --- /dev/null +++ b/android/app/src/main/cpp/third_party/hdiffpatch/libHDiffPatch/HPatch/patch_types.h @@ -0,0 +1,434 @@ +// patch_types.h +// +/* + The MIT License (MIT) + Copyright (c) 2012-2018 HouSisong + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation + files (the "Software"), to deal in the Software without + restriction, including without limitation the rights to use, + copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following + conditions: + + The above copyright notice and this permission notice shall be + included in all copies of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef HPatch_patch_types_h +#define HPatch_patch_types_h + +#include //for size_t memset memcpy memmove +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define HDIFFPATCH_VERSION_MAJOR 5 +#define HDIFFPATCH_VERSION_MINOR 1 +#define HDIFFPATCH_VERSION_RELEASE 3 + +#define _HDIFFPATCH_VERSION HDIFFPATCH_VERSION_MAJOR.HDIFFPATCH_VERSION_MINOR.HDIFFPATCH_VERSION_RELEASE +#define _HDIFFPATCH_QUOTE(str) #str +#define _HDIFFPATCH_EXPAND_AND_QUOTE(str) _HDIFFPATCH_QUOTE(str) +#define HDIFFPATCH_VERSION_STRING _HDIFFPATCH_EXPAND_AND_QUOTE(_HDIFFPATCH_VERSION) +#define HDIFFPATCH_VERSION_NUMBER ((HDIFFPATCH_VERSION_MAJOR*1000+HDIFFPATCH_VERSION_MINOR)*1000+HDIFFPATCH_VERSION_RELEASE) + +#ifndef _IS_USED_MULTITHREAD +# define _IS_USED_MULTITHREAD 1 +#endif +#ifndef _HPATCH_IS_USED_MULTITHREAD +# define _HPATCH_IS_USED_MULTITHREAD _IS_USED_MULTITHREAD +#endif + +#ifndef hpatch_int + typedef int hpatch_int; +#endif +#ifndef hpatch_uint + typedef unsigned int hpatch_uint; +#endif +#ifndef hpatch_size_t + typedef size_t hpatch_size_t; +#endif +#ifndef hpatch_uint32_t +#ifdef _MSC_VER +# if (_MSC_VER >= 1300) + typedef unsigned __int32 hpatch_uint32_t; +# else + typedef unsigned int hpatch_uint32_t; +# endif +#else + typedef unsigned int hpatch_uint32_t; +#endif +#endif +#ifndef hpatch_uint64_t +#ifdef _MSC_VER + typedef unsigned __int64 hpatch_uint64_t; +#else + typedef unsigned long long hpatch_uint64_t; +#endif +#endif +#ifndef hpatch_int64_t +#ifdef _MSC_VER + typedef __int64 hpatch_int64_t; +#else + typedef long long hpatch_int64_t; +#endif +#endif +#ifndef hpatch_StreamPos_t + typedef hpatch_uint64_t hpatch_StreamPos_t; // file size type +#endif +#define hpatch_kNullStreamPos (~(hpatch_StreamPos_t)0) + +#ifndef hpatch_BOOL + typedef unsigned int hpatch_BOOL; +#endif +#define hpatch_FALSE 0 +#define hpatch_TRUE ((hpatch_BOOL)(!hpatch_FALSE)) + +#ifndef hpatch_byte + typedef unsigned char hpatch_byte; +#endif + +#if (_HPATCH_IS_USED_errno) +typedef unsigned int hpatch_FileError_t;// 0: no error; other: saved errno value; +#else +typedef hpatch_BOOL hpatch_FileError_t;// 0: no error; other: error; +#endif + +#ifdef _MSC_VER +# define hpatch_inline _inline +#else +# define hpatch_inline inline +#endif + +#ifndef hpatch_force_inline +#if defined(_MSC_VER) +# define hpatch_force_inline __forceinline +#elif defined(__GNUC__) || defined(__clang__) || defined(__CC_ARM) +# define hpatch_force_inline __attribute__((always_inline)) inline +#elif defined(__ICCARM__) +# define hpatch_force_inline _Pragma("inline=forced") +#else +# define hpatch_force_inline hpatch_inline +#endif +#endif + +//PRIu64 for printf type hpatch_StreamPos_t +#ifndef PRIu64 +# ifdef _MSC_VER +# define PRIu64 "I64u" +# else +# define PRIu64 "llu" +# endif +#endif + +#ifdef ANDROID +# include +# define LOG_ERR(...) __android_log_print(ANDROID_LOG_ERROR, "hpatch", __VA_ARGS__) +#else +# include //for stderr +# define LOG_ERR(...) fprintf(stderr,__VA_ARGS__) +#endif +#ifndef _HPATCH_IS_USED_errno +# define _HPATCH_IS_USED_errno 1 +#endif +#define _hpatch_import_system_tag "call import system api" +#if (_HPATCH_IS_USED_errno) +# define LOG_ERRNO(_err_no) \ + LOG_ERR(_hpatch_import_system_tag" error! errno: %d, errmsg: %s.\n",_err_no,strerror(_err_no)) +#else +# define LOG_ERRNO(_err_no) LOG_ERR(_hpatch_import_system_tag" error!\n") +#endif + +#define _hpatch_align_type_lower(uint_type,p,align2pow) (((uint_type)(p)) & (~(uint_type)((align2pow)-1))) +#define _hpatch_align_lower(p,align2pow) _hpatch_align_type_lower(hpatch_size_t,p,align2pow) +#define _hpatch_align_upper(p,align2pow) _hpatch_align_lower(((hpatch_size_t)(p))+((align2pow)-1),align2pow) + +static hpatch_force_inline hpatch_StreamPos_t _hpatch_pos_min(hpatch_StreamPos_t a,hpatch_StreamPos_t b){ return (ab)?a:b; } + + + typedef void* hpatch_TStreamInputHandle; + typedef void* hpatch_TStreamOutputHandle; + + typedef struct hpatch_TStreamInput{ + void* streamImport; + hpatch_StreamPos_t streamSize; //stream size,max readable range; + //read() must read (out_data_end-out_data), otherwise error return hpatch_FALSE + hpatch_BOOL (*read)(const struct hpatch_TStreamInput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end); + void* _private_reserved; + } hpatch_TStreamInput; + + typedef struct hpatch_TStreamOutput{ + void* streamImport; + hpatch_StreamPos_t streamSize; //stream size,max writable range; not is write pos! + //read_writed for ReadWriteIO, can null! + hpatch_BOOL (*read_writed)(const struct hpatch_TStreamOutput* stream,hpatch_StreamPos_t readFromPos, + unsigned char* out_data,unsigned char* out_data_end); + //write() must wrote (out_data_end-out_data), otherwise error return hpatch_FALSE + hpatch_BOOL (*write)(const struct hpatch_TStreamOutput* stream,hpatch_StreamPos_t writeToPos, + const unsigned char* data,const unsigned char* data_end); + } hpatch_TStreamOutput; + + //default once I/O (read/write) byte size + #ifndef hpatch_kStreamCacheSize + # define hpatch_kStreamCacheSize 4096 + #endif + #ifndef hpatch_kFileIOBufBetterSize + # define hpatch_kFileIOBufBetterSize (1024*64) + #endif + + #ifndef hpatch_kMaxPluginTypeLength + # define hpatch_kMaxPluginTypeLength (256+8-1) + #endif + + #ifndef hpatch_kWindowDiffHeadMaxSize + # define hpatch_kWindowDiffHeadMaxSize hpatch_kStreamCacheSize + #endif + + #ifndef hpatch_kMaxWindowMetaCount + # define hpatch_kMaxWindowMetaCount 64 //must 2^N + #endif + + typedef struct hpatch_compressedDiffInfo{ + hpatch_StreamPos_t newDataSize; + hpatch_StreamPos_t oldDataSize; + hpatch_uint compressedCount;//number of decompress handles that must be opened simultaneously + char compressType[hpatch_kMaxPluginTypeLength+1]; //ascii cstring + } hpatch_compressedDiffInfo; + + typedef void* hpatch_decompressHandle; + typedef enum{ + hpatch_dec_ok=0, + hpatch_dec_mem_error, + hpatch_dec_open_error, + hpatch_dec_error, + hpatch_dec_close_error, + } hpatch_dec_error_t; + typedef struct hpatch_TDecompress{ + hpatch_BOOL (*is_can_open)(const char* compressType); + //error return 0. + hpatch_decompressHandle (*open)(struct hpatch_TDecompress* decompressPlugin, + hpatch_StreamPos_t dataSize, + const struct hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end);//codeSize==code_end-code_begin + hpatch_BOOL (*close)(struct hpatch_TDecompress* decompressPlugin, + hpatch_decompressHandle decompressHandle); + //decompress_part() must out (out_part_data_end-out_part_data), otherwise error return hpatch_FALSE + hpatch_BOOL (*decompress_part)(hpatch_decompressHandle decompressHandle, + unsigned char* out_part_data,unsigned char* out_part_data_end); + //reset_code add new compressed data; for support vcpatch, can NULL + hpatch_BOOL (*reset_code)(hpatch_decompressHandle decompressHandle, + hpatch_StreamPos_t dataSize, + const struct hpatch_TStreamInput* codeStream, + hpatch_StreamPos_t code_begin, + hpatch_StreamPos_t code_end); + volatile hpatch_dec_error_t decError; //if decError is read, each patch session must use its own hpatch_TDecompress instance + size_t dec_threadNum; //for multi-thread decompress, <=1 means single thread (default) + } hpatch_TDecompress; + #define _hpatch_update_decError(decompressPlugin,errorCode) \ + do { if ((decompressPlugin)->decError==hpatch_dec_ok) \ + (decompressPlugin)->decError=errorCode; } while(0) + + + const hpatch_TStreamInput* mem_as_hStreamInput(hpatch_TStreamInput* out_stream, + const unsigned char* mem,const unsigned char* mem_end); + const hpatch_TStreamOutput* mem_as_hStreamOutput(hpatch_TStreamOutput* out_stream, + unsigned char* mem,unsigned char* mem_end); + + hpatch_BOOL hpatch_deccompress_mem(hpatch_TDecompress* decompressPlugin, + const unsigned char* code,const unsigned char* code_end, + unsigned char* out_data,unsigned char* out_data_end); + + typedef struct{ + hpatch_TStreamInput base; + const hpatch_TStreamInput* srcStream; + hpatch_StreamPos_t clipBeginPos; + } TStreamInputClip; + //clip srcStream from clipBeginPos to clipEndPos as a new StreamInput; + void TStreamInputClip_init(TStreamInputClip* self,const hpatch_TStreamInput* srcStream, + hpatch_StreamPos_t clipBeginPos,hpatch_StreamPos_t clipEndPos); + typedef struct{ + hpatch_TStreamOutput base; + const hpatch_TStreamOutput* srcStream; + hpatch_StreamPos_t clipBeginPos; + } TStreamOutputClip; + //clip srcStream from clipBeginPos to clipEndPos as a new StreamInput; + void TStreamOutputClip_init(TStreamOutputClip* self,const hpatch_TStreamOutput* srcStream, + hpatch_StreamPos_t clipBeginPos,hpatch_StreamPos_t clipEndPos); + + + #define hpatch_kMaxPackedUIntBytes ((sizeof(hpatch_StreamPos_t)*8+6)/7+1) + hpatch_BOOL hpatch_packUIntWithTag(unsigned char** out_code,unsigned char* out_code_end, + hpatch_StreamPos_t uValue,hpatch_uint highTag,const hpatch_uint kTagBit); + hpatch_uint hpatch_packUIntWithTag_size(hpatch_StreamPos_t uValue,const hpatch_uint kTagBit); + #define hpatch_packUInt(out_code,out_code_end,uValue) \ + hpatch_packUIntWithTag(out_code,out_code_end,uValue,0,0) + #define hpatch_packUInt_size(uValue) hpatch_packUIntWithTag_size(uValue,0) + + hpatch_BOOL hpatch_unpackUIntWithTag(const unsigned char** src_code,const unsigned char* src_code_end, + hpatch_StreamPos_t* result,const hpatch_uint kTagBit); + #define hpatch_unpackUInt(src_code,src_code_end,result) \ + hpatch_unpackUIntWithTag(src_code,src_code_end,result,0) + + + typedef struct hpatch_TCover{ + hpatch_StreamPos_t oldPos; + hpatch_StreamPos_t newPos; + hpatch_StreamPos_t length; + } hpatch_TCover; + + //opened input covers + typedef struct hpatch_TCovers{ + hpatch_StreamPos_t (*leave_cover_count)(const struct hpatch_TCovers* covers); + //read out a cover,and to next cover pos; if error then return false + hpatch_BOOL (*read_cover)(struct hpatch_TCovers* covers,hpatch_TCover* out_cover); + hpatch_BOOL (*is_finish)(const struct hpatch_TCovers* covers); + hpatch_BOOL (*close)(struct hpatch_TCovers* covers); + } hpatch_TCovers; + + typedef struct{ + hpatch_StreamPos_t newDataSize; + hpatch_StreamPos_t oldDataSize; + hpatch_StreamPos_t uncompressedSize; + hpatch_StreamPos_t compressedSize; + hpatch_StreamPos_t diffDataPos; + hpatch_StreamPos_t coverCount; + hpatch_StreamPos_t stepMemSize; + char compressType[hpatch_kMaxPluginTypeLength+1]; //ascii cstring + } hpatch_singleCompressedDiffInfo; + + hpatch_inline static void _singleDiffInfoToHDiffInfo(hpatch_compressedDiffInfo* out_diffInfo,const hpatch_singleCompressedDiffInfo* singleDiffInfo){ + out_diffInfo->newDataSize=singleDiffInfo->newDataSize; + out_diffInfo->oldDataSize=singleDiffInfo->oldDataSize; + out_diffInfo->compressedCount=(singleDiffInfo->compressedSize>0)?1:0; + memcpy(out_diffInfo->compressType,singleDiffInfo->compressType,strlen(singleDiffInfo->compressType)+1); + } + + typedef struct sspatch_listener_t{ + void* import; + hpatch_BOOL (*onDiffInfo)(struct sspatch_listener_t* listener, + const hpatch_singleCompressedDiffInfo* info, + hpatch_TDecompress** out_decompressPlugin,//find decompressPlugin by info->compressType + unsigned char** out_temp_cache, //*out_temp_cacheEnd-*out_temp_cache == info->stepMemSize + (I/O cache memory) + unsigned char** out_temp_cacheEnd);// note: (I/O cache memory) >= hpatch_kStreamCacheSize*3 + void (*onPatchFinish)(struct sspatch_listener_t* listener, //onPatchFinish can null + unsigned char* temp_cache, unsigned char* temp_cacheEnd); + } sspatch_listener_t; + + typedef struct{ + hpatch_TStreamInput base; + hpatch_TDecompress* _decompressPlugin; + hpatch_decompressHandle _decompressHandle; + } hpatch_TUncompresser_t; + + typedef struct sspatch_coversListener_t{ + void* import; + void (*onStepCoversReset)(struct sspatch_coversListener_t* listener,hpatch_StreamPos_t leaveCoverCount);//can be NULL; data in covers_cache will become invalid; if leaveCoverCount==0, the step is finished + void (*onStepCovers)(struct sspatch_coversListener_t* listener, + const unsigned char* covers_cache,const unsigned char* covers_cacheEnd);//if covers_cache==covers_cacheEnd==0, step finish + } sspatch_coversListener_t; + + typedef struct{ + const unsigned char* covers_cache; + const unsigned char* covers_cacheEnd; + hpatch_StreamPos_t lastOldEnd; + hpatch_StreamPos_t lastNewEnd; + hpatch_TCover cover; + } sspatch_covers_t; + + hpatch_inline static void sspatch_covers_init(sspatch_covers_t* self) { memset(self,0,sizeof(*self)); } + hpatch_inline static void sspatch_covers_setCoversCache(sspatch_covers_t* self,const unsigned char* covers_cache,const unsigned char* covers_cacheEnd){ + self->covers_cache=covers_cache; self->covers_cacheEnd=covers_cacheEnd; } + hpatch_inline static hpatch_BOOL sspatch_covers_isHaveNextCover(const sspatch_covers_t* self) { return (self->covers_cache!=(self)->covers_cacheEnd); } + + hpatch_BOOL sspatch_covers_nextCover(sspatch_covers_t* self); + + + typedef struct{ + hpatch_StreamPos_t oldPos; + hpatch_StreamPos_t newPos; + hpatch_StreamPos_t oldLength; + hpatch_StreamPos_t newLength; + } hpatch_TWindow; + + + typedef struct hpatch_windowDiffInfo{ + hpatch_StreamPos_t newDataSize; + hpatch_StreamPos_t oldDataSize; + hpatch_StreamPos_t coverCount; + hpatch_StreamPos_t windowCount; + hpatch_StreamPos_t windowMetaCount; //2^N, >=2 & <= hpatch_kMaxWindowMetaCount + hpatch_StreamPos_t maxStepMemSize; + hpatch_StreamPos_t maxSubCoverCount; + hpatch_StreamPos_t maxWindowOldSize; + hpatch_StreamPos_t checksumByteSize; //0 no checksum + hpatch_StreamPos_t extraDataSize; + hpatch_StreamPos_t uncompressedSize; //windowDiffStreamSize + hpatch_StreamPos_t compressedSize; //0 uncompressed, >0 compressed + hpatch_StreamPos_t otherInfoPos; + hpatch_StreamPos_t otherInfoEndPos; + hpatch_StreamPos_t windowDataPos; //window data begin pos(compressed data begin pos); + //the checksum section for old/new/diff data begin pos = windowDataPos-3*checksumByteSize; + hpatch_StreamPos_t _headFixedInfoPos; + char compressType[hpatch_kMaxPluginTypeLength+1]; + char checksumType[hpatch_kMaxPluginTypeLength+1]; + } hpatch_windowDiffInfo; + + typedef enum TWindowPatchResult{ + kWindowPatch_ok=0, + kWindowPatch_load_head_error, + kWindowPatch_new_size_error, + kWindowPatch_old_size_error, + kWindowPatch_onDiffInfo_error, + kWindowPatch_temp_mem_error, + kWindowPatch_decompress_open_error, + kWindowPatch_patch_error, + kWindowPatch_checksum_plugin_error, + kWindowPatch_checksum_open_error, + kWindowPatch_checksum_old_error, + kWindowPatch_checksum_new_error, + kWindowPatch_checksum_diff_error, + } TWindowPatchResult; + + hpatch_inline static void _winDiffInfoToHDiffInfo(hpatch_compressedDiffInfo* out_diffInfo,const hpatch_windowDiffInfo* winDiffInfo){ + out_diffInfo->newDataSize=winDiffInfo->newDataSize; + out_diffInfo->oldDataSize=winDiffInfo->oldDataSize; + out_diffInfo->compressedCount=(winDiffInfo->compressedSize>0)?1:0; + memcpy(out_diffInfo->compressType,winDiffInfo->compressType,strlen(winDiffInfo->compressType)+1); + } + + struct hpatch_TChecksum; + typedef struct winpatch_listener_t{ + void* import; + hpatch_BOOL (*onDiffInfo)(struct winpatch_listener_t* listener, + const hpatch_windowDiffInfo* info, + hpatch_TDecompress** out_decompressPlugin,//find decompressPlugin by info->compressType + struct hpatch_TChecksum** out_checksumPlugin, //find checksumPlugin by info->checksumType + hpatch_BOOL* isChecksumNew, // *isChecksumNew default true when info->checksumByteSize>0 + hpatch_BOOL* isChecksumOld,hpatch_BOOL* isChecksumDiff,//*isChecksumOld & *isChecksumDiff default false + unsigned char** out_temp_cache, //*out_temp_cacheEnd-*out_temp_cache == info->maxWindowOldSize + info->stepMemSize + (I/O cache memory) + unsigned char** out_temp_cacheEnd);// note: (I/O cache memory) >= hpatch_kStreamCacheSize*3 + void (*onPatchFinish)(struct winpatch_listener_t* listener, //onPatchFinish can null + unsigned char* temp_cache, unsigned char* temp_cacheEnd); + } winpatch_listener_t; + +#ifdef __cplusplus +} +#endif +#endif diff --git a/android/app/src/main/java/com/microsoft/codepush/react/diffpatch/DiffPatch.kt b/android/app/src/main/java/com/microsoft/codepush/react/diffpatch/DiffPatch.kt new file mode 100644 index 00000000..b468ebdc --- /dev/null +++ b/android/app/src/main/java/com/microsoft/codepush/react/diffpatch/DiffPatch.kt @@ -0,0 +1,54 @@ +package com.microsoft.codepush.react.diffpatch + +object DiffPatch { + + /** + * Applies the BSDIFF40 patch at diffFilePath to oldFilePath, writing the + * patched result to newFilePath. newFilePath is created/overwritten, and + * removed again if patching fails partway through. + */ + fun applyPatch(oldFilePath: String, diffFilePath: String, newFilePath: String): PatchResult { + ensureLibraryLoaded() + return PatchResult.fromNativeCode(nativeBSPatchApply(oldFilePath, diffFilePath, newFilePath)) + } + + enum class PatchResult { + OK, + BAD_DIFF_HEADER, + OPEN_OLD_FAILED, + OPEN_DIFF_FAILED, + OPEN_OUT_FAILED, + OUT_OF_MEMORY, + PATCH_FAILED, + UNKNOWN; + + companion object { + // Mirrors CodePushBSPatchResult in cpp/bspatch_bridge.h - keep in sync. + fun fromNativeCode(code: Int): PatchResult = when (code) { + 0 -> OK + 1 -> BAD_DIFF_HEADER + 2 -> OPEN_OLD_FAILED + 3 -> OPEN_DIFF_FAILED + 4 -> OPEN_OUT_FAILED + 5 -> OUT_OF_MEMORY + 6 -> PATCH_FAILED + else -> UNKNOWN + } + } + } + + @Volatile + private var isLibraryLoaded = false + + @Synchronized + private fun ensureLibraryLoaded() { + if (!isLibraryLoaded) { + System.loadLibrary("codepush_diffpatch") + isLibraryLoaded = true + } + } + + + @JvmStatic + private external fun nativeBSPatchApply(oldFilePath: String, diffFilePath: String, newFilePath: String): Int +} diff --git a/android/app/src/test/java/com/microsoft/codepush/react/FileUtilsTest.kt b/android/app/src/test/java/com/microsoft/codepush/react/FileUtilsTest.kt new file mode 100644 index 00000000..971c68e8 --- /dev/null +++ b/android/app/src/test/java/com/microsoft/codepush/react/FileUtilsTest.kt @@ -0,0 +1,31 @@ +package com.microsoft.codepush.react + +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File + +class FileUtilsTest { + + @get:Rule + val tempFolder = TemporaryFolder() + + @Test + fun copyDirectoryContents_copiesNestedFilesAndSubdirectories() { + val sourceDir = tempFolder.newFolder("source") + File(sourceDir, "root.txt").writeText("root contents") + val nestedDir = File(sourceDir, "nested").apply { mkdir() } + File(nestedDir, "child.txt").writeText("child contents") + + val destinationDir = File(tempFolder.root, "destination") + + FileUtils.copyDirectoryContents(sourceDir.absolutePath, destinationDir.absolutePath) + + assertEquals("root contents", File(destinationDir, "root.txt").readText()) + val copiedNestedFile = File(destinationDir, "nested/child.txt") + assertTrue(copiedNestedFile.exists()) + assertEquals("child contents", copiedNestedFile.readText()) + } +} diff --git a/android/build.gradle b/android/build.gradle index 989373d7..a45423e2 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,12 @@ buildscript { mavenCentral() } dependencies { - classpath("com.android.tools.build:gradle:8.12.0") + // Must match (or exceed) the AGP version RN's own @react-native/gradle-plugin declares: that plugin is + // included as a composite build below and its own AGP dependency wins Gradle's classpath + // conflict resolution regardless of what's pinned here, so an out-of-date pin here is + // silently overridden rather than actually enforced. Bump this in lockstep with RN's own + // gradle-plugin version whenever bumping the react-native devDependency. + classpath("com.android.tools.build:gradle:9.2.1") classpath("com.facebook.react:react-native-gradle-plugin") // NOTE: Do not place your application dependencies here; they belong diff --git a/android/gradle.properties b/android/gradle.properties index 89e0d99e..d4d82b9d 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -16,3 +16,10 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +android.useAndroidX=true + +# https://github.com/react-native-community/discussions-and-proposals/pull/1006 +# Applies to this repo's own standalone build/test harness only; see android/app/build.gradle's comments +# for why downstream consumers may still land in either mode. +android.builtInKotlin=false +android.newDsl=false diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties index 74b269f3..da80f8d1 100644 --- a/android/gradle/wrapper/gradle-wrapper.properties +++ b/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-all.zip