diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d42d6bc --- /dev/null +++ b/.editorconfig @@ -0,0 +1,11 @@ +root = true + +[*] +indent_size = 2 +ij_continuation_indent_size = 2 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.{kt, kts}] +ij_kotlin_imports_layout = * diff --git a/.github/renovate.json5 b/.github/renovate.json5 new file mode 100644 index 0000000..869e597 --- /dev/null +++ b/.github/renovate.json5 @@ -0,0 +1,35 @@ +{ + $schema: 'https://docs.renovatebot.com/renovate-schema.json', + extends: [ + 'config:recommended', + ], + packageRules: [ + // First release used Kotlin version + project version which sorts higher than new versions. + { + matchPackageNames: [ + 'co.touchlab:cklib-gradle-plugin', + ], + allowedVersions: '!/1\\.5\\.31\\.2/', + }, + ], + ignorePresets: [ + // Ensure we get the latest version and are not pinned to old versions. + 'workarounds:javaLTSVersions', + ], + customManagers: [ + // Update .java-version file with the latest JDK version. + { + customType: 'regex', + fileMatch: [ + '\\.java-version$', + ], + matchStrings: [ + '(?.*)\\n', + ], + datasourceTemplate: 'java-version', + depNameTemplate: 'java', + // Only write the major version. + extractVersionTemplate: '^(?\\d+)', + }, + ] +} diff --git a/.github/workflows/.java-version b/.github/workflows/.java-version new file mode 100644 index 0000000..a45fd52 --- /dev/null +++ b/.github/workflows/.java-version @@ -0,0 +1 @@ +24 diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml new file mode 100644 index 0000000..201e8f4 --- /dev/null +++ b/.github/workflows/build.yaml @@ -0,0 +1,109 @@ +name: build + +on: + pull_request: {} + workflow_dispatch: {} + push: + branches: + - 'main' + tags-ignore: + - '**' + +env: + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false" + +jobs: + zig: + runs-on: macos-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + with: + submodules: true + + - uses: mlugg/setup-zig@475c97be87a204e6c57fe851f970bd02005a70f0 # ratchet:mlugg/setup-zig@v2 + with: + version: 0.14.0 + + - run: zig build -p src/jvmMain/resources/jni + working-directory: okio-zstd + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 + with: + name: jni-binaries + path: okio-zstd/src/jvmMain/resources/jni + if-no-files-found: error + + platform-tests: + needs: + - zig + strategy: + fail-fast: false + matrix: + platform: + - os: macos-latest + + runs-on: ${{ matrix.platform.os }} + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # ratchet:actions/setup-java@v4 + with: + distribution: 'zulu' + java-version-file: .github/workflows/.java-version + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # ratchet:actions/download-artifact@v4 + with: + name: jni-binaries + path: okio-zstd/src/jvmMain/resources/jni + + - run: ./gradlew jvmTest apiCheck --stacktrace + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 + if: ${{ always() }} + with: + name: test-report-${{ matrix.platform.os }} + path: '**/build/reports/tests/**' + retention-days: 1 + + build: + runs-on: macos-latest + needs: + - platform-tests + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # ratchet:actions/setup-java@v4 + with: + distribution: 'zulu' + java-version-file: .github/workflows/.java-version + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # ratchet:actions/download-artifact@v4 + with: + name: jni-binaries + path: okio-zstd/src/jvmMain/resources/jni + + - run: ./gradlew assemble :dokkaHtmlMultiModule + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # ratchet:actions/upload-artifact@v4 + with: + name: html-docs + path: build/dokka/htmlMultiModule/ + if-no-files-found: error + + - run: ./gradlew publishToMavenCentral + if: ${{ github.ref == 'refs/heads/main' && github.repository == 'square/okio-zstd' }} + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_CENTRAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_CENTRAL_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SECRET_PASSPHRASE }} + + - name: Deploy docs to website + if: ${{ github.ref == 'refs/heads/main' && github.repository == 'square/okio-zstd' }} + uses: JamesIves/github-pages-deploy-action@132898c54c57c7cc6b80eb3a89968de8fc283505 # ratchet:JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: site + FOLDER: build/dokka/htmlMultiModule/ + TARGET_FOLDER: docs/latest/ + CLEAN: true diff --git a/.github/workflows/gradle-wrapper.yaml b/.github/workflows/gradle-wrapper.yaml new file mode 100644 index 0000000..8391089 --- /dev/null +++ b/.github/workflows/gradle-wrapper.yaml @@ -0,0 +1,15 @@ +name: gradle-wrapper + +on: + pull_request: + paths: + - 'gradlew' + - 'gradlew.bat' + - 'gradle/wrapper/**' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + - uses: gradle/actions/wrapper-validation@ac638b010cf58a27ee6c972d7336334ccaf61c96 # ratchet:gradle/actions/wrapper-validation@v4 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..4a95deb --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,51 @@ +name: release + +on: + push: + tags: + - '**' + +env: + GRADLE_OPTS: "-Dorg.gradle.jvmargs=-Xmx4g -Dorg.gradle.daemon=false -Dkotlin.incremental=false" + +jobs: + publish: + runs-on: macos-latest + + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # ratchet:actions/checkout@v4 + with: + submodules: true + + - uses: actions/setup-java@c5195efecf7bdfc987ee8bae7a71cb8b11521c00 # ratchet:actions/setup-java@v4 + with: + distribution: 'zulu' + java-version-file: .github/workflows/.java-version + + - uses: mlugg/setup-zig@475c97be87a204e6c57fe851f970bd02005a70f0 # ratchet:mlugg/setup-zig@v2 + with: + version: 0.14.0 + + - run: zig build -p src/jvmMain/resources/jni + working-directory: okio-zstd + + - run: ./gradlew publishToMavenCentral :dokkaHtmlMultiModule + if: ${{ github.repository == 'square/okio-zstd' }} + env: + ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SONATYPE_CENTRAL_USERNAME }} + ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SONATYPE_CENTRAL_PASSWORD }} + ORG_GRADLE_PROJECT_signingInMemoryKey: ${{ secrets.GPG_SECRET_KEY }} + ORG_GRADLE_PROJECT_signingInMemoryKeyPassword: ${{ secrets.GPG_SECRET_PASSPHRASE }} + + - uses: ffurrer2/extract-release-notes@cae32133495112d23e3569ad04fef240ba4e7bc8 # ratchet:ffurrer2/extract-release-notes@v2 + id: release_notes + + - name: Deploy docs to website + if: ${{ github.repository == 'square/okio-zstd' }} + uses: JamesIves/github-pages-deploy-action@132898c54c57c7cc6b80eb3a89968de8fc283505 # ratchet:JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: site + FOLDER: build/dokka/htmlMultiModule/ + TARGET_FOLDER: docs/1.x/ + CLEAN: true diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..531dfd8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# IntelliJ IDEA +.idea +!/.idea/copyright +*.iml + +# Gradle +.gradle +build/ +local.properties +reports +jacoco.exec +.externalNativeBuild +.cxx +.kotlin + +# iOS +*.pbxuser +# Ignore generated Xcode projects +*.xcworkspace +xcuserdata +Pods + +# Release +docs/0.x + +# Zig +.zig-cache +zig-out + +# Binaries +okio-zstd/src/jvmMain/resources/jni/**/*.dylib +okio-zstd/src/jvmMain/resources/jni/**/*.so diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..8ef60ef --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "zstd"] + path = zstd + url = https://github.com/facebook/zstd.git diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b3543d9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,4 @@ +# Change Log + +## Unreleased + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 12c3c71..98a2b60 100644 --- a/README.md +++ b/README.md @@ -1 +1,18 @@ -Okio Zstd +# Okio Zstd + +Build the native libraries: + +``` +brew install zig +git submodule init +git submodule update +pushd okio-zstd +zig build -p src/jvmMain/resources/jni +popd +``` + +Test it: + +``` +./gradlew okio-zstd:jvmTest +``` diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..d99a994 --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,185 @@ +import com.diffplug.gradle.spotless.SpotlessExtension +import com.vanniktech.maven.publish.MavenPublishBaseExtension +import com.vanniktech.maven.publish.SonatypeHost +import java.net.URI +import java.net.URL +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.jetbrains.dokka.DokkaConfiguration.Visibility +import org.jetbrains.dokka.gradle.AbstractDokkaTask +import org.jetbrains.dokka.gradle.DokkaMultiModuleTask +import org.jetbrains.dokka.gradle.DokkaTaskPartial +import org.jetbrains.kotlin.gradle.dsl.KotlinJvmProjectExtension +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +buildscript { + repositories { + mavenCentral() + google() + gradlePluginPortal() + } + dependencies { + classpath(libs.binary.compatibility.validator.gradle.plugin) + classpath(libs.mavenPublish.gradle.plugin) + classpath(libs.kotlin.gradle.plugin) + classpath(libs.dokka.gradle.plugin) + classpath(libs.shadowJar.gradle.plugin) + } +} + +plugins { + id("com.github.gmazzo.buildconfig") version "5.6.7" apply false + alias(libs.plugins.spotless) +} + +apply(plugin = "org.jetbrains.dokka") + +apply(plugin = "com.vanniktech.maven.publish.base") + +configure { + kotlin { + target("**/*.kt") + ktlint() + .editorConfigOverride( + mapOf( + "ktlint_standard_comment-spacing" to "disabled", // TODO Re-enable + "ktlint_standard_filename" to "disabled", + "ktlint_standard_indent" to "disabled", // TODO Re-enable + ) + ) + } +} + +allprojects { + group = "com.squareup.okio-zstd" + version = project.property("VERSION_NAME") as String + + repositories { + mavenCentral() + google() + } +} + +subprojects { + tasks.withType(Test::class).configureEach { + testLogging { + if (System.getenv("CI") == "true") { + events = setOf(TestLogEvent.STARTED, TestLogEvent.FAILED, TestLogEvent.SKIPPED, TestLogEvent.PASSED) + } + exceptionFormat = TestExceptionFormat.FULL + } + } +} + +tasks.named("dokkaHtmlMultiModule", DokkaMultiModuleTask::class.java).configure { + moduleName.set("Okio-Zstd") +} + +allprojects { + tasks.withType().configureEach { + dokkaSourceSets.configureEach { + documentedVisibilities.set(setOf( + Visibility.PUBLIC, + Visibility.PROTECTED + )) + reportUndocumented.set(false) + jdkVersion.set(11) + + sourceLink { + localDirectory.set(rootProject.projectDir) + remoteUrl.set(URL("https://github.com/square/okio-zstd/tree/main/")) + remoteLineSuffix.set("#L") + } + } + } + + // Workaround for https://github.com/Kotlin/dokka/issues/2977. + // We disable the C Interop IDE metadata task when generating documentation using Dokka. + tasks.withType { + @Suppress("UNCHECKED_CAST") + val taskClass = Class.forName("org.jetbrains.kotlin.gradle.targets.native.internal.CInteropMetadataDependencyTransformationTask") as Class + parent?.subprojects?.forEach { + dependsOn(it.tasks.withType(taskClass)) + } + } + + // Don't attempt to sign anything if we don't have an in-memory key. Otherwise, the 'build' task + // triggers 'signJsPublication' even when we aren't publishing (and so don't have signing keys). + tasks.withType().configureEach { + enabled = project.findProperty("signingInMemoryKey") != null + } + + plugins.withId("org.jetbrains.kotlin.multiplatform") { + configure { + jvmToolchain(11) + } + } + + plugins.withId("org.jetbrains.kotlin.jvm") { + configure { + jvmToolchain(11) + } + } + + plugins.withId("com.vanniktech.maven.publish.base") { + configure { + repositories { + maven { + name = "testMaven" + url = rootProject.layout.buildDirectory.dir("testMaven").get().asFile.toURI() + } + + /* + * Want to push to an internal repository for testing? + * Set the following properties in ~/.gradle/gradle.properties. + * + * internalUrl=YOUR_INTERNAL_URL + * internalUsername=YOUR_USERNAME + * internalPassword=YOUR_PASSWORD + * + * Then run the following command to publish a new internal release: + * + * ./gradlew publishAllPublicationsToInternalRepository -DRELEASE_SIGNING_ENABLED=false + */ + val internalUrl = providers.gradleProperty("internalUrl").orNull + if (internalUrl != null) { + maven { + name = "internal" + url = URI(internalUrl) + credentials { + username = providers.gradleProperty("internalUsername").get() + password = providers.gradleProperty("internalPassword").get() + } + } + } + } + } + configure { + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL, automaticRelease = true) + signAllPublications() + pom { + description.set("ZStandard for Okio") + name.set(project.name) + url.set("https://github.com/square/okio-zstd/") + licenses { + license { + name.set("The Apache Software License, Version 2.0") + url.set("http://www.apache.org/licenses/LICENSE-2.0.txt") + distribution.set("repo") + } + } + developers { + developer { + id.set("square") + name.set("Square, Inc.") + } + } + scm { + url.set("https://github.com/square/okio-zstd/") + connection.set("scm:git:https://github.com/square/okio-zstd.git") + developerConnection.set("scm:git:ssh://git@github.com/square/okio-zstd.git") + } + } + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..15738eb --- /dev/null +++ b/gradle.properties @@ -0,0 +1,21 @@ +org.gradle.caching=true +org.gradle.jvmargs=-Xmx4096m -Dfile.encoding=UTF-8 +org.gradle.parallel=true + +VERSION_NAME=1.0.0-SNAPSHOT + +android.useAndroidX=true +android.enableJetifier=false +android.defaults.buildfeatures.buildconfig=false +android.defaults.buildfeatures.aidl=false +android.defaults.buildfeatures.renderscript=false +android.defaults.buildfeatures.resvalues=false +android.defaults.buildfeatures.shaders=false + +systemProp.org.gradle.internal.http.socketTimeout=120000 + +kotlin.native.ignoreDisabledTargets=true +kotlin.mpp.androidSourceSetLayoutVersion=2 +kotlin.mpp.stability.nowarn=true +kotlin.mpp.commonizerLogLevel=info +kotlin.mpp.enableCInteropCommonization=true diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 0000000..8ab82f9 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,18 @@ +[versions] +kotlin = "2.1.21" +okio = "3.13.0" + +[libraries] +assertk = "com.willowtreeapps.assertk:assertk:0.28.1" +binary-compatibility-validator-gradle-plugin = { module = "org.jetbrains.kotlinx.binary-compatibility-validator:org.jetbrains.kotlinx.binary-compatibility-validator.gradle.plugin", version = "0.17.0" } +cklib-gradle-plugin = { module = "co.touchlab:cklib-gradle-plugin", version = "0.3.4" } +dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version = "2.0.0" } +junit = { module = "junit:junit", version = "4.13.2" } +kotlin-gradle-plugin = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin", version.ref = "kotlin" } +mavenPublish-gradle-plugin = { module = "com.vanniktech:gradle-maven-publish-plugin", version = "0.33.0" } +okio-core = { module = "com.squareup.okio:okio", version.ref = "okio" } +shadowJar-gradle-plugin = { module = "gradle.plugin.com.github.johnrengelman:shadow", version = "8.0.0" } +lubenZstdJni = "com.github.luben:zstd-jni:1.5.7-4" + +[plugins] +spotless = { id = "com.diffplug.spotless", version = "7.0.4" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..1b33c55 Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..ff23a68 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 0000000..23d15a9 --- /dev/null +++ b/gradlew @@ -0,0 +1,251 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH="\\\"\\\"" + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 0000000..5eed7ee --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH= + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/lint.xml b/lint.xml new file mode 100644 index 0000000..bd596ee --- /dev/null +++ b/lint.xml @@ -0,0 +1,3 @@ + + + diff --git a/okio-zstd/api/okio-zstd.api b/okio-zstd/api/okio-zstd.api new file mode 100644 index 0000000..06dc7f3 --- /dev/null +++ b/okio-zstd/api/okio-zstd.api @@ -0,0 +1,5 @@ +public final class okio/zstd/Zstd { + public static final fun zstdCompress (Lokio/Sink;)Lokio/Sink; + public static final fun zstdDecompress (Lokio/Source;)Lokio/Source; +} + diff --git a/okio-zstd/build.gradle.kts b/okio-zstd/build.gradle.kts new file mode 100644 index 0000000..ea294ab --- /dev/null +++ b/okio-zstd/build.gradle.kts @@ -0,0 +1,51 @@ +import com.vanniktech.maven.publish.JavadocJar +import com.vanniktech.maven.publish.KotlinMultiplatform +import com.vanniktech.maven.publish.MavenPublishBaseExtension + +plugins { + kotlin("multiplatform") + id("org.jetbrains.dokka") + id("com.vanniktech.maven.publish.base") + id("com.github.gmazzo.buildconfig") + id("binary-compatibility-validator") +} + +kotlin { + jvm() + + applyDefaultHierarchyTemplate() + + sourceSets { + val commonMain by getting { + dependencies { + api(libs.okio.core) + } + } + val jvmMain by getting { + dependencies { + api(libs.okio.core) + } + } + + val commonTest by getting { + dependencies { + implementation(libs.assertk) + implementation(libs.lubenZstdJni) + implementation(kotlin("test")) + } + } + } +} + +buildConfig { + useKotlinOutput { + internalVisibility = true + topLevelConstants = true + } +} + +configure { + configure( + KotlinMultiplatform(javadocJar = JavadocJar.Empty()) + ) +} diff --git a/okio-zstd/build.zig b/okio-zstd/build.zig new file mode 100644 index 0000000..84554e0 --- /dev/null +++ b/okio-zstd/build.zig @@ -0,0 +1,93 @@ +const std = @import("std"); + +pub fn build(b: *std.Build) !void { + // The Windows builds create a .lib file in the lib/ directory which we don't need. + const deleteLib = b.addRemoveDirTree(.{ .cwd_relative = b.getInstallPath(.prefix, "lib") }); + b.getInstallStep().dependOn(&deleteLib.step); + + try setupTarget(b, &deleteLib.step, .linux, .aarch64, "aarch64"); + try setupTarget(b, &deleteLib.step, .linux, .x86_64, "amd64"); + try setupTarget(b, &deleteLib.step, .macos, .aarch64, "aarch64"); + try setupTarget(b, &deleteLib.step, .macos, .x86_64, "x86_64"); +} + +fn setupTarget(b: *std.Build, step: *std.Build.Step, tag: std.Target.Os.Tag, arch: std.Target.Cpu.Arch, dir: []const u8) !void { + const lib = b.addSharedLibrary(.{ + .name = "zstd", + .target = b.resolveTargetQuery(.{ + .cpu_arch = arch, + .os_tag = tag, + // We need to explicitly specify gnu for linux, as otherwise it defaults to musl. + // See https://github.com/ziglang/zig/issues/16624#issuecomment-1801175600. + .abi = if (tag == .linux) .gnu else null, + }), + .optimize = .ReleaseSmall, + }); + + lib.addIncludePath(b.path("native/include/share")); + lib.addIncludePath( + switch (tag) { + .windows => b.path("native/include/windows"), + else => b.path("native/include/unix"), + } + ); + lib.addIncludePath(b.path("../zstd/lib")); + + lib.linkLibC(); + // TODO Tree-walk these two dirs for all C files. + lib.addCSourceFiles(.{ + .files = &.{ + "../zstd/lib/common/debug.c", + "../zstd/lib/common/entropy_common.c", + "../zstd/lib/common/error_private.c", + "../zstd/lib/common/fse_decompress.c", + "../zstd/lib/common/pool.c", + "../zstd/lib/common/threading.c", + "../zstd/lib/common/xxhash.c", + "../zstd/lib/common/zstd_common.c", + "../zstd/lib/compress/fse_compress.c", + "../zstd/lib/compress/hist.c", + "../zstd/lib/compress/huf_compress.c", + "../zstd/lib/compress/zstd_compress.c", + "../zstd/lib/compress/zstd_compress_literals.c", + "../zstd/lib/compress/zstd_compress_sequences.c", + "../zstd/lib/compress/zstd_compress_superblock.c", + "../zstd/lib/compress/zstd_double_fast.c", + "../zstd/lib/compress/zstd_fast.c", + "../zstd/lib/compress/zstd_lazy.c", + "../zstd/lib/compress/zstd_ldm.c", + "../zstd/lib/compress/zstd_opt.c", + "../zstd/lib/compress/zstd_preSplit.c", + "../zstd/lib/compress/zstdmt_compress.c", + "../zstd/lib/decompress/huf_decompress.c", + "../zstd/lib/decompress/huf_decompress_amd64.S", + "../zstd/lib/decompress/zstd_ddict.c", + "../zstd/lib/decompress/zstd_decompress.c", + "../zstd/lib/decompress/zstd_decompress_block.c", + }, + .flags = &.{ + "-std=gnu99", + }, + }); + + lib.linkLibCpp(); + // TODO Tree-walk this dirs for all C++ files. + lib.addCSourceFiles(.{ + .files = &.{ + "native/OkioZstd.cpp", + }, + .flags = &.{ + "-std=c++11", + }, + }); + + const install = b.addInstallArtifact(lib, .{ + .dest_dir = .{ + .override = .{ + .custom = dir, + }, + }, + }); + + step.dependOn(&install.step); +} diff --git a/okio-zstd/native/OkioZstd.cpp b/okio-zstd/native/OkioZstd.cpp new file mode 100644 index 0000000..77c3cec --- /dev/null +++ b/okio-zstd/native/OkioZstd.cpp @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include +#include + +/** + * Support for operating on JVM objects from native code. + * + * Pass a pointer to this to all JNI functions that operate on JVM objects. + */ +class JniZstd { +public: + JniZstd(JNIEnv *env, jclass unsafeCursorClass, jclass zstdCompressorClass, jclass zstdDecompressorClass); + + jfieldID unsafeCursorData; + jfieldID unsafeCursorStart; + jfieldID unsafeCursorEnd; + jfieldID zstdCompressorOutputBytesProcessed; + jfieldID zstdCompressorInputBytesProcessed; + jfieldID zstdDecompressorOutputBytesProcessed; + jfieldID zstdDecompressorInputBytesProcessed; +}; + +JniZstd::JniZstd(JNIEnv *env, jclass unsafeCursorClass, jclass zstdCompressorClass, jclass zstdDecompressorClass) + : unsafeCursorData(env->GetFieldID(unsafeCursorClass, "data", "[B")), + unsafeCursorStart(env->GetFieldID(unsafeCursorClass, "start", "I")), + unsafeCursorEnd(env->GetFieldID(unsafeCursorClass, "end", "I")), + zstdCompressorOutputBytesProcessed(env->GetFieldID(zstdCompressorClass, "outputBytesProcessed", "I")), + zstdCompressorInputBytesProcessed(env->GetFieldID(zstdCompressorClass, "inputBytesProcessed", "I")), + zstdDecompressorOutputBytesProcessed(env->GetFieldID(zstdDecompressorClass, "outputBytesProcessed", "I")), + zstdDecompressorInputBytesProcessed(env->GetFieldID(zstdDecompressorClass, "inputBytesProcessed", "I")) { +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_okio_zstd_JniZstd_isError(JNIEnv* env, jobject type, jlong code) { + return ZSTD_isError(static_cast(code)); +} + +extern "C" JNIEXPORT jstring JNICALL +Java_okio_zstd_JniZstd_getErrorName(JNIEnv* env, jobject type, jlong code) { + auto codeSizeT = static_cast(code); + if (!ZSTD_isError(codeSizeT)) return NULL; + auto errorString = ZSTD_getErrorName(codeSizeT); + return env->NewStringUTF(errorString); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstd_createJniZstd(JNIEnv* env, jclass type) { + auto unsafeCursorClass = env->FindClass("okio/Buffer$UnsafeCursor"); + auto zstdCompressorClass = env->FindClass("okio/zstd/ZstdCompressor"); + auto zstdDecompressorClass = env->FindClass("okio/zstd/ZstdDecompressor"); + auto jniZstd = new JniZstd(env, unsafeCursorClass, zstdCompressorClass, zstdDecompressorClass); + return reinterpret_cast(jniZstd); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstd_createZstdCompressor(JNIEnv* env, jclass type) { + ZSTD_CCtx* cctx = ZSTD_createCCtx(); // Could be NULL. + return reinterpret_cast(cctx); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstdCompressor_setParameter(JNIEnv* env, jobject type, jlong cctxPointer, jint param, jint value) { + auto cctx = reinterpret_cast(cctxPointer); + return ZSTD_CCtx_setParameter(cctx, static_cast(param), value); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstdCompressor_compressStream2(JNIEnv* env, jobject type, jlong jniZstdPointer, jlong cctxPointer, jobject output, jobject input, jint mode) { + auto jniZstd = reinterpret_cast(jniZstdPointer); + auto cctx = reinterpret_cast(cctxPointer); + + auto inputByteArray = static_cast(env->GetObjectField(input, jniZstd->unsafeCursorData)); + auto inputByteArrayElements = env->GetByteArrayElements(inputByteArray, NULL); + auto inputEnd = static_cast(env->GetIntField(input, jniZstd->unsafeCursorEnd)); + auto inputStart = static_cast(env->GetIntField(input, jniZstd->unsafeCursorStart)); + ZSTD_inBuffer zstdInput = { inputByteArrayElements, inputEnd, inputStart }; + + auto outputByteArray = static_cast(env->GetObjectField(output, jniZstd->unsafeCursorData)); + auto outputByteArrayElements = env->GetByteArrayElements(outputByteArray, NULL); + auto outputEnd = static_cast(env->GetIntField(output, jniZstd->unsafeCursorEnd)); + auto outputStart = static_cast(env->GetIntField(output, jniZstd->unsafeCursorStart)); + ZSTD_outBuffer zstdOutput = { outputByteArrayElements, outputEnd, outputStart }; + + size_t result; + if (inputByteArrayElements != NULL && outputByteArrayElements != NULL) { + result = ZSTD_compressStream2(cctx, &zstdOutput, &zstdInput, + static_cast(mode)); + } else { + result = -ZSTD_error_GENERIC; + } + + env->SetIntField(type, jniZstd->zstdCompressorOutputBytesProcessed, static_cast(zstdOutput.pos) - outputStart); + env->SetIntField(type, jniZstd->zstdCompressorInputBytesProcessed, static_cast(zstdInput.pos) - inputStart); + + env->ReleaseByteArrayElements(inputByteArray, inputByteArrayElements, JNI_ABORT); + env->ReleaseByteArrayElements(outputByteArray, outputByteArrayElements, 0); + + return result; +} + +extern "C" JNIEXPORT void JNICALL +Java_okio_zstd_JniZstdCompressor_close(JNIEnv* env, jobject type, jlong cctxPointer) { + auto cctx = reinterpret_cast(cctxPointer); + ZSTD_freeCCtx(cctx); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstd_createZstdDecompressor(JNIEnv* env, jclass type) { + ZSTD_DCtx* dctx = ZSTD_createDCtx(); // Could be NULL. + return reinterpret_cast(dctx); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_okio_zstd_JniZstdDecompressor_decompressStream(JNIEnv* env, jobject type, jlong jniZstdPointer, jlong dctxPointer, jobject output, jobject input) { + auto jniZstd = reinterpret_cast(jniZstdPointer); + auto dctx = reinterpret_cast(dctxPointer); + + auto inputByteArray = static_cast(env->GetObjectField(input, jniZstd->unsafeCursorData)); + auto inputByteArrayElements = env->GetByteArrayElements(inputByteArray, NULL); + auto inputEnd = static_cast(env->GetIntField(input, jniZstd->unsafeCursorEnd)); + auto inputStart = static_cast(env->GetIntField(input, jniZstd->unsafeCursorStart)); + ZSTD_inBuffer zstdInput = { inputByteArrayElements, inputEnd, inputStart }; + + auto outputByteArray = static_cast(env->GetObjectField(output, jniZstd->unsafeCursorData)); + auto outputByteArrayElements = env->GetByteArrayElements(outputByteArray, NULL); + auto outputEnd = static_cast(env->GetIntField(output, jniZstd->unsafeCursorEnd)); + auto outputStart = static_cast(env->GetIntField(output, jniZstd->unsafeCursorStart)); + ZSTD_outBuffer zstdOutput = { outputByteArrayElements, outputEnd, outputStart }; + + size_t result; + if (inputByteArrayElements != NULL && outputByteArrayElements != NULL) { + result = ZSTD_decompressStream(dctx, &zstdOutput, &zstdInput); + } else { + result = -ZSTD_error_GENERIC; + } + + env->SetIntField(type, jniZstd->zstdDecompressorOutputBytesProcessed, static_cast(zstdOutput.pos) - outputStart); + env->SetIntField(type, jniZstd->zstdDecompressorInputBytesProcessed, static_cast(zstdInput.pos) - inputStart); + + env->ReleaseByteArrayElements(inputByteArray, inputByteArrayElements, JNI_ABORT); + env->ReleaseByteArrayElements(outputByteArray, outputByteArrayElements, 0); + + return result; +} + +extern "C" JNIEXPORT void JNICALL +Java_okio_zstd_JniZstdDecompressor_close(JNIEnv* env, jobject type, jlong dctxPointer) { + auto dctx = reinterpret_cast(dctxPointer); + ZSTD_freeDCtx(dctx); +} diff --git a/okio-zstd/native/include/LICENSE b/okio-zstd/native/include/LICENSE new file mode 100644 index 0000000..8b400c7 --- /dev/null +++ b/okio-zstd/native/include/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/okio-zstd/native/include/share/jni.h b/okio-zstd/native/include/share/jni.h new file mode 100644 index 0000000..c85da1b --- /dev/null +++ b/okio-zstd/native/include/share/jni.h @@ -0,0 +1,2001 @@ +/* + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * We used part of Netscape's Java Runtime Interface (JRI) as the starting + * point of our design and implementation. + */ + +/****************************************************************************** + * Java Runtime Interface + * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved. + *****************************************************************************/ + +#ifndef _JAVASOFT_JNI_H_ +#define _JAVASOFT_JNI_H_ + +#include +#include + +/* jni_md.h contains the machine-dependent typedefs for jbyte, jint + and jlong */ + +#include "jni_md.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* + * JNI Types + */ + +#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H + +typedef unsigned char jboolean; +typedef unsigned short jchar; +typedef short jshort; +typedef float jfloat; +typedef double jdouble; + +typedef jint jsize; + +#ifdef __cplusplus + +class _jobject {}; +class _jclass : public _jobject {}; +class _jthrowable : public _jobject {}; +class _jstring : public _jobject {}; +class _jarray : public _jobject {}; +class _jbooleanArray : public _jarray {}; +class _jbyteArray : public _jarray {}; +class _jcharArray : public _jarray {}; +class _jshortArray : public _jarray {}; +class _jintArray : public _jarray {}; +class _jlongArray : public _jarray {}; +class _jfloatArray : public _jarray {}; +class _jdoubleArray : public _jarray {}; +class _jobjectArray : public _jarray {}; + +typedef _jobject *jobject; +typedef _jclass *jclass; +typedef _jthrowable *jthrowable; +typedef _jstring *jstring; +typedef _jarray *jarray; +typedef _jbooleanArray *jbooleanArray; +typedef _jbyteArray *jbyteArray; +typedef _jcharArray *jcharArray; +typedef _jshortArray *jshortArray; +typedef _jintArray *jintArray; +typedef _jlongArray *jlongArray; +typedef _jfloatArray *jfloatArray; +typedef _jdoubleArray *jdoubleArray; +typedef _jobjectArray *jobjectArray; + +#else + +struct _jobject; + +typedef struct _jobject *jobject; +typedef jobject jclass; +typedef jobject jthrowable; +typedef jobject jstring; +typedef jobject jarray; +typedef jarray jbooleanArray; +typedef jarray jbyteArray; +typedef jarray jcharArray; +typedef jarray jshortArray; +typedef jarray jintArray; +typedef jarray jlongArray; +typedef jarray jfloatArray; +typedef jarray jdoubleArray; +typedef jarray jobjectArray; + +#endif + +typedef jobject jweak; + +typedef union jvalue { + jboolean z; + jbyte b; + jchar c; + jshort s; + jint i; + jlong j; + jfloat f; + jdouble d; + jobject l; +} jvalue; + +struct _jfieldID; +typedef struct _jfieldID *jfieldID; + +struct _jmethodID; +typedef struct _jmethodID *jmethodID; + +/* Return values from jobjectRefType */ +typedef enum _jobjectType { + JNIInvalidRefType = 0, + JNILocalRefType = 1, + JNIGlobalRefType = 2, + JNIWeakGlobalRefType = 3 +} jobjectRefType; + + +#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */ + +/* + * jboolean constants + */ + +#define JNI_FALSE 0 +#define JNI_TRUE 1 + +/* + * possible return values for JNI functions. + */ + +#define JNI_OK 0 /* success */ +#define JNI_ERR (-1) /* unknown error */ +#define JNI_EDETACHED (-2) /* thread detached from the VM */ +#define JNI_EVERSION (-3) /* JNI version error */ +#define JNI_ENOMEM (-4) /* not enough memory */ +#define JNI_EEXIST (-5) /* VM already created */ +#define JNI_EINVAL (-6) /* invalid arguments */ + +/* + * used in ReleaseScalarArrayElements + */ + +#define JNI_COMMIT 1 +#define JNI_ABORT 2 + +/* + * used in RegisterNatives to describe native method name, signature, + * and function pointer. + */ + +typedef struct { + char *name; + char *signature; + void *fnPtr; +} JNINativeMethod; + +/* + * JNI Native Method Interface. + */ + +struct JNINativeInterface_; + +struct JNIEnv_; + +#ifdef __cplusplus +typedef JNIEnv_ JNIEnv; +#else +typedef const struct JNINativeInterface_ *JNIEnv; +#endif + +/* + * JNI Invocation Interface. + */ + +struct JNIInvokeInterface_; + +struct JavaVM_; + +#ifdef __cplusplus +typedef JavaVM_ JavaVM; +#else +typedef const struct JNIInvokeInterface_ *JavaVM; +#endif + +struct JNINativeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + void *reserved3; + jint (JNICALL *GetVersion)(JNIEnv *env); + + jclass (JNICALL *DefineClass) + (JNIEnv *env, const char *name, jobject loader, const jbyte *buf, + jsize len); + jclass (JNICALL *FindClass) + (JNIEnv *env, const char *name); + + jmethodID (JNICALL *FromReflectedMethod) + (JNIEnv *env, jobject method); + jfieldID (JNICALL *FromReflectedField) + (JNIEnv *env, jobject field); + + jobject (JNICALL *ToReflectedMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic); + + jclass (JNICALL *GetSuperclass) + (JNIEnv *env, jclass sub); + jboolean (JNICALL *IsAssignableFrom) + (JNIEnv *env, jclass sub, jclass sup); + + jobject (JNICALL *ToReflectedField) + (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic); + + jint (JNICALL *Throw) + (JNIEnv *env, jthrowable obj); + jint (JNICALL *ThrowNew) + (JNIEnv *env, jclass clazz, const char *msg); + jthrowable (JNICALL *ExceptionOccurred) + (JNIEnv *env); + void (JNICALL *ExceptionDescribe) + (JNIEnv *env); + void (JNICALL *ExceptionClear) + (JNIEnv *env); + void (JNICALL *FatalError) + (JNIEnv *env, const char *msg); + + jint (JNICALL *PushLocalFrame) + (JNIEnv *env, jint capacity); + jobject (JNICALL *PopLocalFrame) + (JNIEnv *env, jobject result); + + jobject (JNICALL *NewGlobalRef) + (JNIEnv *env, jobject lobj); + void (JNICALL *DeleteGlobalRef) + (JNIEnv *env, jobject gref); + void (JNICALL *DeleteLocalRef) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsSameObject) + (JNIEnv *env, jobject obj1, jobject obj2); + jobject (JNICALL *NewLocalRef) + (JNIEnv *env, jobject ref); + jint (JNICALL *EnsureLocalCapacity) + (JNIEnv *env, jint capacity); + + jobject (JNICALL *AllocObject) + (JNIEnv *env, jclass clazz); + jobject (JNICALL *NewObject) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *NewObjectV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *NewObjectA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jclass (JNICALL *GetObjectClass) + (JNIEnv *env, jobject obj); + jboolean (JNICALL *IsInstanceOf) + (JNIEnv *env, jobject obj, jclass clazz); + + jmethodID (JNICALL *GetMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallObjectMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jobject (JNICALL *CallObjectMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jobject (JNICALL *CallObjectMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jboolean (JNICALL *CallBooleanMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jboolean (JNICALL *CallBooleanMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jboolean (JNICALL *CallBooleanMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jbyte (JNICALL *CallByteMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jbyte (JNICALL *CallByteMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jbyte (JNICALL *CallByteMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallCharMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jchar (JNICALL *CallCharMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jchar (JNICALL *CallCharMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallShortMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jshort (JNICALL *CallShortMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jshort (JNICALL *CallShortMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallIntMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jint (JNICALL *CallIntMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jint (JNICALL *CallIntMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallLongMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jlong (JNICALL *CallLongMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jlong (JNICALL *CallLongMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallFloatMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jfloat (JNICALL *CallFloatMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jfloat (JNICALL *CallFloatMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallDoubleMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + jdouble (JNICALL *CallDoubleMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + jdouble (JNICALL *CallDoubleMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallVoidMethod) + (JNIEnv *env, jobject obj, jmethodID methodID, ...); + void (JNICALL *CallVoidMethodV) + (JNIEnv *env, jobject obj, jmethodID methodID, va_list args); + void (JNICALL *CallVoidMethodA) + (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args); + + jobject (JNICALL *CallNonvirtualObjectMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallNonvirtualObjectMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jobject (JNICALL *CallNonvirtualObjectMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jboolean (JNICALL *CallNonvirtualBooleanMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallNonvirtualBooleanMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jboolean (JNICALL *CallNonvirtualBooleanMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jbyte (JNICALL *CallNonvirtualByteMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallNonvirtualByteMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jbyte (JNICALL *CallNonvirtualByteMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jchar (JNICALL *CallNonvirtualCharMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallNonvirtualCharMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jchar (JNICALL *CallNonvirtualCharMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jshort (JNICALL *CallNonvirtualShortMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallNonvirtualShortMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jshort (JNICALL *CallNonvirtualShortMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jint (JNICALL *CallNonvirtualIntMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallNonvirtualIntMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jint (JNICALL *CallNonvirtualIntMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jlong (JNICALL *CallNonvirtualLongMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallNonvirtualLongMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jlong (JNICALL *CallNonvirtualLongMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jfloat (JNICALL *CallNonvirtualFloatMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallNonvirtualFloatMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jfloat (JNICALL *CallNonvirtualFloatMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + jdouble (JNICALL *CallNonvirtualDoubleMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallNonvirtualDoubleMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + jdouble (JNICALL *CallNonvirtualDoubleMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue *args); + + void (JNICALL *CallNonvirtualVoidMethod) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...); + void (JNICALL *CallNonvirtualVoidMethodV) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + va_list args); + void (JNICALL *CallNonvirtualVoidMethodA) + (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, + const jvalue * args); + + jfieldID (JNICALL *GetFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *GetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jboolean (JNICALL *GetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jbyte (JNICALL *GetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jchar (JNICALL *GetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jshort (JNICALL *GetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jint (JNICALL *GetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jlong (JNICALL *GetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jfloat (JNICALL *GetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + jdouble (JNICALL *GetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID); + + void (JNICALL *SetObjectField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val); + void (JNICALL *SetBooleanField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val); + void (JNICALL *SetByteField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val); + void (JNICALL *SetCharField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val); + void (JNICALL *SetShortField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val); + void (JNICALL *SetIntField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jint val); + void (JNICALL *SetLongField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val); + void (JNICALL *SetFloatField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val); + void (JNICALL *SetDoubleField) + (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val); + + jmethodID (JNICALL *GetStaticMethodID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + + jobject (JNICALL *CallStaticObjectMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jobject (JNICALL *CallStaticObjectMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jobject (JNICALL *CallStaticObjectMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jboolean (JNICALL *CallStaticBooleanMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jboolean (JNICALL *CallStaticBooleanMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jboolean (JNICALL *CallStaticBooleanMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jbyte (JNICALL *CallStaticByteMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jbyte (JNICALL *CallStaticByteMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jbyte (JNICALL *CallStaticByteMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jchar (JNICALL *CallStaticCharMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jchar (JNICALL *CallStaticCharMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jchar (JNICALL *CallStaticCharMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jshort (JNICALL *CallStaticShortMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jshort (JNICALL *CallStaticShortMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jshort (JNICALL *CallStaticShortMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jint (JNICALL *CallStaticIntMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jint (JNICALL *CallStaticIntMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jint (JNICALL *CallStaticIntMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jlong (JNICALL *CallStaticLongMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jlong (JNICALL *CallStaticLongMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jlong (JNICALL *CallStaticLongMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jfloat (JNICALL *CallStaticFloatMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jfloat (JNICALL *CallStaticFloatMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jfloat (JNICALL *CallStaticFloatMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + jdouble (JNICALL *CallStaticDoubleMethod) + (JNIEnv *env, jclass clazz, jmethodID methodID, ...); + jdouble (JNICALL *CallStaticDoubleMethodV) + (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args); + jdouble (JNICALL *CallStaticDoubleMethodA) + (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args); + + void (JNICALL *CallStaticVoidMethod) + (JNIEnv *env, jclass cls, jmethodID methodID, ...); + void (JNICALL *CallStaticVoidMethodV) + (JNIEnv *env, jclass cls, jmethodID methodID, va_list args); + void (JNICALL *CallStaticVoidMethodA) + (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args); + + jfieldID (JNICALL *GetStaticFieldID) + (JNIEnv *env, jclass clazz, const char *name, const char *sig); + jobject (JNICALL *GetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jboolean (JNICALL *GetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jbyte (JNICALL *GetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jchar (JNICALL *GetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jshort (JNICALL *GetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jint (JNICALL *GetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jlong (JNICALL *GetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jfloat (JNICALL *GetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + jdouble (JNICALL *GetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID); + + void (JNICALL *SetStaticObjectField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value); + void (JNICALL *SetStaticBooleanField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value); + void (JNICALL *SetStaticByteField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value); + void (JNICALL *SetStaticCharField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value); + void (JNICALL *SetStaticShortField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value); + void (JNICALL *SetStaticIntField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value); + void (JNICALL *SetStaticLongField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value); + void (JNICALL *SetStaticFloatField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value); + void (JNICALL *SetStaticDoubleField) + (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value); + + jstring (JNICALL *NewString) + (JNIEnv *env, const jchar *unicode, jsize len); + jsize (JNICALL *GetStringLength) + (JNIEnv *env, jstring str); + const jchar *(JNICALL *GetStringChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringChars) + (JNIEnv *env, jstring str, const jchar *chars); + + jstring (JNICALL *NewStringUTF) + (JNIEnv *env, const char *utf); + jsize (JNICALL *GetStringUTFLength) + (JNIEnv *env, jstring str); + const char* (JNICALL *GetStringUTFChars) + (JNIEnv *env, jstring str, jboolean *isCopy); + void (JNICALL *ReleaseStringUTFChars) + (JNIEnv *env, jstring str, const char* chars); + + + jsize (JNICALL *GetArrayLength) + (JNIEnv *env, jarray array); + + jobjectArray (JNICALL *NewObjectArray) + (JNIEnv *env, jsize len, jclass clazz, jobject init); + jobject (JNICALL *GetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index); + void (JNICALL *SetObjectArrayElement) + (JNIEnv *env, jobjectArray array, jsize index, jobject val); + + jbooleanArray (JNICALL *NewBooleanArray) + (JNIEnv *env, jsize len); + jbyteArray (JNICALL *NewByteArray) + (JNIEnv *env, jsize len); + jcharArray (JNICALL *NewCharArray) + (JNIEnv *env, jsize len); + jshortArray (JNICALL *NewShortArray) + (JNIEnv *env, jsize len); + jintArray (JNICALL *NewIntArray) + (JNIEnv *env, jsize len); + jlongArray (JNICALL *NewLongArray) + (JNIEnv *env, jsize len); + jfloatArray (JNICALL *NewFloatArray) + (JNIEnv *env, jsize len); + jdoubleArray (JNICALL *NewDoubleArray) + (JNIEnv *env, jsize len); + + jboolean * (JNICALL *GetBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *isCopy); + jbyte * (JNICALL *GetByteArrayElements) + (JNIEnv *env, jbyteArray array, jboolean *isCopy); + jchar * (JNICALL *GetCharArrayElements) + (JNIEnv *env, jcharArray array, jboolean *isCopy); + jshort * (JNICALL *GetShortArrayElements) + (JNIEnv *env, jshortArray array, jboolean *isCopy); + jint * (JNICALL *GetIntArrayElements) + (JNIEnv *env, jintArray array, jboolean *isCopy); + jlong * (JNICALL *GetLongArrayElements) + (JNIEnv *env, jlongArray array, jboolean *isCopy); + jfloat * (JNICALL *GetFloatArrayElements) + (JNIEnv *env, jfloatArray array, jboolean *isCopy); + jdouble * (JNICALL *GetDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jboolean *isCopy); + + void (JNICALL *ReleaseBooleanArrayElements) + (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode); + void (JNICALL *ReleaseByteArrayElements) + (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode); + void (JNICALL *ReleaseCharArrayElements) + (JNIEnv *env, jcharArray array, jchar *elems, jint mode); + void (JNICALL *ReleaseShortArrayElements) + (JNIEnv *env, jshortArray array, jshort *elems, jint mode); + void (JNICALL *ReleaseIntArrayElements) + (JNIEnv *env, jintArray array, jint *elems, jint mode); + void (JNICALL *ReleaseLongArrayElements) + (JNIEnv *env, jlongArray array, jlong *elems, jint mode); + void (JNICALL *ReleaseFloatArrayElements) + (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode); + void (JNICALL *ReleaseDoubleArrayElements) + (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode); + + void (JNICALL *GetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf); + void (JNICALL *GetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf); + void (JNICALL *GetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf); + void (JNICALL *GetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf); + void (JNICALL *GetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf); + void (JNICALL *GetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf); + void (JNICALL *GetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf); + void (JNICALL *GetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf); + + void (JNICALL *SetBooleanArrayRegion) + (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf); + void (JNICALL *SetByteArrayRegion) + (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf); + void (JNICALL *SetCharArrayRegion) + (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf); + void (JNICALL *SetShortArrayRegion) + (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf); + void (JNICALL *SetIntArrayRegion) + (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf); + void (JNICALL *SetLongArrayRegion) + (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf); + void (JNICALL *SetFloatArrayRegion) + (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf); + void (JNICALL *SetDoubleArrayRegion) + (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf); + + jint (JNICALL *RegisterNatives) + (JNIEnv *env, jclass clazz, const JNINativeMethod *methods, + jint nMethods); + jint (JNICALL *UnregisterNatives) + (JNIEnv *env, jclass clazz); + + jint (JNICALL *MonitorEnter) + (JNIEnv *env, jobject obj); + jint (JNICALL *MonitorExit) + (JNIEnv *env, jobject obj); + + jint (JNICALL *GetJavaVM) + (JNIEnv *env, JavaVM **vm); + + void (JNICALL *GetStringRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf); + void (JNICALL *GetStringUTFRegion) + (JNIEnv *env, jstring str, jsize start, jsize len, char *buf); + + void * (JNICALL *GetPrimitiveArrayCritical) + (JNIEnv *env, jarray array, jboolean *isCopy); + void (JNICALL *ReleasePrimitiveArrayCritical) + (JNIEnv *env, jarray array, void *carray, jint mode); + + const jchar * (JNICALL *GetStringCritical) + (JNIEnv *env, jstring string, jboolean *isCopy); + void (JNICALL *ReleaseStringCritical) + (JNIEnv *env, jstring string, const jchar *cstring); + + jweak (JNICALL *NewWeakGlobalRef) + (JNIEnv *env, jobject obj); + void (JNICALL *DeleteWeakGlobalRef) + (JNIEnv *env, jweak ref); + + jboolean (JNICALL *ExceptionCheck) + (JNIEnv *env); + + jobject (JNICALL *NewDirectByteBuffer) + (JNIEnv* env, void* address, jlong capacity); + void* (JNICALL *GetDirectBufferAddress) + (JNIEnv* env, jobject buf); + jlong (JNICALL *GetDirectBufferCapacity) + (JNIEnv* env, jobject buf); + + /* New JNI 1.6 Features */ + + jobjectRefType (JNICALL *GetObjectRefType) + (JNIEnv* env, jobject obj); + + /* Module Features */ + + jobject (JNICALL *GetModule) + (JNIEnv* env, jclass clazz); + + /* Virtual threads */ + + jboolean (JNICALL *IsVirtualThread) + (JNIEnv* env, jobject obj); +}; + +/* + * We use inlined functions for C++ so that programmers can write: + * + * env->FindClass("java/lang/String") + * + * in C++ rather than: + * + * (*env)->FindClass(env, "java/lang/String") + * + * in C. + */ + +struct JNIEnv_ { + const struct JNINativeInterface_ *functions; +#ifdef __cplusplus + + jint GetVersion() { + return functions->GetVersion(this); + } + jclass DefineClass(const char *name, jobject loader, const jbyte *buf, + jsize len) { + return functions->DefineClass(this, name, loader, buf, len); + } + jclass FindClass(const char *name) { + return functions->FindClass(this, name); + } + jmethodID FromReflectedMethod(jobject method) { + return functions->FromReflectedMethod(this,method); + } + jfieldID FromReflectedField(jobject field) { + return functions->FromReflectedField(this,field); + } + + jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) { + return functions->ToReflectedMethod(this, cls, methodID, isStatic); + } + + jclass GetSuperclass(jclass sub) { + return functions->GetSuperclass(this, sub); + } + jboolean IsAssignableFrom(jclass sub, jclass sup) { + return functions->IsAssignableFrom(this, sub, sup); + } + + jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) { + return functions->ToReflectedField(this,cls,fieldID,isStatic); + } + + jint Throw(jthrowable obj) { + return functions->Throw(this, obj); + } + jint ThrowNew(jclass clazz, const char *msg) { + return functions->ThrowNew(this, clazz, msg); + } + jthrowable ExceptionOccurred() { + return functions->ExceptionOccurred(this); + } + void ExceptionDescribe() { + functions->ExceptionDescribe(this); + } + void ExceptionClear() { + functions->ExceptionClear(this); + } + void FatalError(const char *msg) { + functions->FatalError(this, msg); + } + + jint PushLocalFrame(jint capacity) { + return functions->PushLocalFrame(this,capacity); + } + jobject PopLocalFrame(jobject result) { + return functions->PopLocalFrame(this,result); + } + + jobject NewGlobalRef(jobject lobj) { + return functions->NewGlobalRef(this,lobj); + } + void DeleteGlobalRef(jobject gref) { + functions->DeleteGlobalRef(this,gref); + } + void DeleteLocalRef(jobject obj) { + functions->DeleteLocalRef(this, obj); + } + + jboolean IsSameObject(jobject obj1, jobject obj2) { + return functions->IsSameObject(this,obj1,obj2); + } + + jobject NewLocalRef(jobject ref) { + return functions->NewLocalRef(this,ref); + } + jint EnsureLocalCapacity(jint capacity) { + return functions->EnsureLocalCapacity(this,capacity); + } + + jobject AllocObject(jclass clazz) { + return functions->AllocObject(this,clazz); + } + jobject NewObject(jclass clazz, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args, methodID); + result = functions->NewObjectV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject NewObjectV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->NewObjectV(this,clazz,methodID,args); + } + jobject NewObjectA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->NewObjectA(this,clazz,methodID,args); + } + + jclass GetObjectClass(jobject obj) { + return functions->GetObjectClass(this,obj); + } + jboolean IsInstanceOf(jobject obj, jclass clazz) { + return functions->IsInstanceOf(this,obj,clazz); + } + + jmethodID GetMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetMethodID(this,clazz,name,sig); + } + + jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallObjectMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jobject CallObjectMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallObjectMethodV(this,obj,methodID,args); + } + jobject CallObjectMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallObjectMethodA(this,obj,methodID,args); + } + + jboolean CallBooleanMethod(jobject obj, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallBooleanMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jboolean CallBooleanMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallBooleanMethodV(this,obj,methodID,args); + } + jboolean CallBooleanMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallBooleanMethodA(this,obj,methodID, args); + } + + jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallByteMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jbyte CallByteMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallByteMethodV(this,obj,methodID,args); + } + jbyte CallByteMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallByteMethodA(this,obj,methodID,args); + } + + jchar CallCharMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallCharMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jchar CallCharMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallCharMethodV(this,obj,methodID,args); + } + jchar CallCharMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallCharMethodA(this,obj,methodID,args); + } + + jshort CallShortMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallShortMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jshort CallShortMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallShortMethodV(this,obj,methodID,args); + } + jshort CallShortMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallShortMethodA(this,obj,methodID,args); + } + + jint CallIntMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallIntMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jint CallIntMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallIntMethodV(this,obj,methodID,args); + } + jint CallIntMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallIntMethodA(this,obj,methodID,args); + } + + jlong CallLongMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallLongMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jlong CallLongMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallLongMethodV(this,obj,methodID,args); + } + jlong CallLongMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallLongMethodA(this,obj,methodID,args); + } + + jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallFloatMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jfloat CallFloatMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallFloatMethodV(this,obj,methodID,args); + } + jfloat CallFloatMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallFloatMethodA(this,obj,methodID,args); + } + + jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallDoubleMethodV(this,obj,methodID,args); + va_end(args); + return result; + } + jdouble CallDoubleMethodV(jobject obj, jmethodID methodID, + va_list args) { + return functions->CallDoubleMethodV(this,obj,methodID,args); + } + jdouble CallDoubleMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + return functions->CallDoubleMethodA(this,obj,methodID,args); + } + + void CallVoidMethod(jobject obj, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallVoidMethodV(this,obj,methodID,args); + va_end(args); + } + void CallVoidMethodV(jobject obj, jmethodID methodID, + va_list args) { + functions->CallVoidMethodV(this,obj,methodID,args); + } + void CallVoidMethodA(jobject obj, jmethodID methodID, + const jvalue * args) { + functions->CallVoidMethodA(this,obj,methodID,args); + } + + jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualObjectMethodV(this,obj,clazz, + methodID,args); + } + jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualObjectMethodA(this,obj,clazz, + methodID,args); + } + + jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualBooleanMethodV(this,obj,clazz, + methodID,args); + } + jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualBooleanMethodA(this,obj,clazz, + methodID, args); + } + + jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualByteMethodV(this,obj,clazz, + methodID,args); + } + jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualByteMethodA(this,obj,clazz, + methodID,args); + } + + jchar CallNonvirtualCharMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualCharMethodV(this,obj,clazz, + methodID,args); + } + jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualCharMethodA(this,obj,clazz, + methodID,args); + } + + jshort CallNonvirtualShortMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualShortMethodV(this,obj,clazz, + methodID,args); + } + jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualShortMethodA(this,obj,clazz, + methodID,args); + } + + jint CallNonvirtualIntMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jint CallNonvirtualIntMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualIntMethodV(this,obj,clazz, + methodID,args); + } + jint CallNonvirtualIntMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualIntMethodA(this,obj,clazz, + methodID,args); + } + + jlong CallNonvirtualLongMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallNonvirtualLongMethodV(this,obj,clazz, + methodID,args); + } + jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz, + jmethodID methodID, const jvalue * args) { + return functions->CallNonvirtualLongMethodA(this,obj,clazz, + methodID,args); + } + + jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualFloatMethodV(this,obj,clazz, + methodID,args); + } + jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualFloatMethodA(this,obj,clazz, + methodID,args); + } + + jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + va_end(args); + return result; + } + jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + return functions->CallNonvirtualDoubleMethodV(this,obj,clazz, + methodID,args); + } + jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + return functions->CallNonvirtualDoubleMethodA(this,obj,clazz, + methodID,args); + } + + void CallNonvirtualVoidMethod(jobject obj, jclass clazz, + jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + va_end(args); + } + void CallNonvirtualVoidMethodV(jobject obj, jclass clazz, + jmethodID methodID, + va_list args) { + functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args); + } + void CallNonvirtualVoidMethodA(jobject obj, jclass clazz, + jmethodID methodID, + const jvalue * args) { + functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args); + } + + jfieldID GetFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetFieldID(this,clazz,name,sig); + } + + jobject GetObjectField(jobject obj, jfieldID fieldID) { + return functions->GetObjectField(this,obj,fieldID); + } + jboolean GetBooleanField(jobject obj, jfieldID fieldID) { + return functions->GetBooleanField(this,obj,fieldID); + } + jbyte GetByteField(jobject obj, jfieldID fieldID) { + return functions->GetByteField(this,obj,fieldID); + } + jchar GetCharField(jobject obj, jfieldID fieldID) { + return functions->GetCharField(this,obj,fieldID); + } + jshort GetShortField(jobject obj, jfieldID fieldID) { + return functions->GetShortField(this,obj,fieldID); + } + jint GetIntField(jobject obj, jfieldID fieldID) { + return functions->GetIntField(this,obj,fieldID); + } + jlong GetLongField(jobject obj, jfieldID fieldID) { + return functions->GetLongField(this,obj,fieldID); + } + jfloat GetFloatField(jobject obj, jfieldID fieldID) { + return functions->GetFloatField(this,obj,fieldID); + } + jdouble GetDoubleField(jobject obj, jfieldID fieldID) { + return functions->GetDoubleField(this,obj,fieldID); + } + + void SetObjectField(jobject obj, jfieldID fieldID, jobject val) { + functions->SetObjectField(this,obj,fieldID,val); + } + void SetBooleanField(jobject obj, jfieldID fieldID, + jboolean val) { + functions->SetBooleanField(this,obj,fieldID,val); + } + void SetByteField(jobject obj, jfieldID fieldID, + jbyte val) { + functions->SetByteField(this,obj,fieldID,val); + } + void SetCharField(jobject obj, jfieldID fieldID, + jchar val) { + functions->SetCharField(this,obj,fieldID,val); + } + void SetShortField(jobject obj, jfieldID fieldID, + jshort val) { + functions->SetShortField(this,obj,fieldID,val); + } + void SetIntField(jobject obj, jfieldID fieldID, + jint val) { + functions->SetIntField(this,obj,fieldID,val); + } + void SetLongField(jobject obj, jfieldID fieldID, + jlong val) { + functions->SetLongField(this,obj,fieldID,val); + } + void SetFloatField(jobject obj, jfieldID fieldID, + jfloat val) { + functions->SetFloatField(this,obj,fieldID,val); + } + void SetDoubleField(jobject obj, jfieldID fieldID, + jdouble val) { + functions->SetDoubleField(this,obj,fieldID,val); + } + + jmethodID GetStaticMethodID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticMethodID(this,clazz,name,sig); + } + + jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID, + ...) { + va_list args; + jobject result; + va_start(args,methodID); + result = functions->CallStaticObjectMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID, + va_list args) { + return functions->CallStaticObjectMethodV(this,clazz,methodID,args); + } + jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID, + const jvalue *args) { + return functions->CallStaticObjectMethodA(this,clazz,methodID,args); + } + + jboolean CallStaticBooleanMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jboolean result; + va_start(args,methodID); + result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jboolean CallStaticBooleanMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticBooleanMethodV(this,clazz,methodID,args); + } + jboolean CallStaticBooleanMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticBooleanMethodA(this,clazz,methodID,args); + } + + jbyte CallStaticByteMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jbyte result; + va_start(args,methodID); + result = functions->CallStaticByteMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jbyte CallStaticByteMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticByteMethodV(this,clazz,methodID,args); + } + jbyte CallStaticByteMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticByteMethodA(this,clazz,methodID,args); + } + + jchar CallStaticCharMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jchar result; + va_start(args,methodID); + result = functions->CallStaticCharMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jchar CallStaticCharMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticCharMethodV(this,clazz,methodID,args); + } + jchar CallStaticCharMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticCharMethodA(this,clazz,methodID,args); + } + + jshort CallStaticShortMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jshort result; + va_start(args,methodID); + result = functions->CallStaticShortMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jshort CallStaticShortMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticShortMethodV(this,clazz,methodID,args); + } + jshort CallStaticShortMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticShortMethodA(this,clazz,methodID,args); + } + + jint CallStaticIntMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jint result; + va_start(args,methodID); + result = functions->CallStaticIntMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jint CallStaticIntMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticIntMethodV(this,clazz,methodID,args); + } + jint CallStaticIntMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticIntMethodA(this,clazz,methodID,args); + } + + jlong CallStaticLongMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jlong result; + va_start(args,methodID); + result = functions->CallStaticLongMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jlong CallStaticLongMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticLongMethodV(this,clazz,methodID,args); + } + jlong CallStaticLongMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticLongMethodA(this,clazz,methodID,args); + } + + jfloat CallStaticFloatMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jfloat result; + va_start(args,methodID); + result = functions->CallStaticFloatMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jfloat CallStaticFloatMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticFloatMethodV(this,clazz,methodID,args); + } + jfloat CallStaticFloatMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticFloatMethodA(this,clazz,methodID,args); + } + + jdouble CallStaticDoubleMethod(jclass clazz, + jmethodID methodID, ...) { + va_list args; + jdouble result; + va_start(args,methodID); + result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + va_end(args); + return result; + } + jdouble CallStaticDoubleMethodV(jclass clazz, + jmethodID methodID, va_list args) { + return functions->CallStaticDoubleMethodV(this,clazz,methodID,args); + } + jdouble CallStaticDoubleMethodA(jclass clazz, + jmethodID methodID, const jvalue *args) { + return functions->CallStaticDoubleMethodA(this,clazz,methodID,args); + } + + void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) { + va_list args; + va_start(args,methodID); + functions->CallStaticVoidMethodV(this,cls,methodID,args); + va_end(args); + } + void CallStaticVoidMethodV(jclass cls, jmethodID methodID, + va_list args) { + functions->CallStaticVoidMethodV(this,cls,methodID,args); + } + void CallStaticVoidMethodA(jclass cls, jmethodID methodID, + const jvalue * args) { + functions->CallStaticVoidMethodA(this,cls,methodID,args); + } + + jfieldID GetStaticFieldID(jclass clazz, const char *name, + const char *sig) { + return functions->GetStaticFieldID(this,clazz,name,sig); + } + jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticObjectField(this,clazz,fieldID); + } + jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticBooleanField(this,clazz,fieldID); + } + jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticByteField(this,clazz,fieldID); + } + jchar GetStaticCharField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticCharField(this,clazz,fieldID); + } + jshort GetStaticShortField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticShortField(this,clazz,fieldID); + } + jint GetStaticIntField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticIntField(this,clazz,fieldID); + } + jlong GetStaticLongField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticLongField(this,clazz,fieldID); + } + jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticFloatField(this,clazz,fieldID); + } + jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) { + return functions->GetStaticDoubleField(this,clazz,fieldID); + } + + void SetStaticObjectField(jclass clazz, jfieldID fieldID, + jobject value) { + functions->SetStaticObjectField(this,clazz,fieldID,value); + } + void SetStaticBooleanField(jclass clazz, jfieldID fieldID, + jboolean value) { + functions->SetStaticBooleanField(this,clazz,fieldID,value); + } + void SetStaticByteField(jclass clazz, jfieldID fieldID, + jbyte value) { + functions->SetStaticByteField(this,clazz,fieldID,value); + } + void SetStaticCharField(jclass clazz, jfieldID fieldID, + jchar value) { + functions->SetStaticCharField(this,clazz,fieldID,value); + } + void SetStaticShortField(jclass clazz, jfieldID fieldID, + jshort value) { + functions->SetStaticShortField(this,clazz,fieldID,value); + } + void SetStaticIntField(jclass clazz, jfieldID fieldID, + jint value) { + functions->SetStaticIntField(this,clazz,fieldID,value); + } + void SetStaticLongField(jclass clazz, jfieldID fieldID, + jlong value) { + functions->SetStaticLongField(this,clazz,fieldID,value); + } + void SetStaticFloatField(jclass clazz, jfieldID fieldID, + jfloat value) { + functions->SetStaticFloatField(this,clazz,fieldID,value); + } + void SetStaticDoubleField(jclass clazz, jfieldID fieldID, + jdouble value) { + functions->SetStaticDoubleField(this,clazz,fieldID,value); + } + + jstring NewString(const jchar *unicode, jsize len) { + return functions->NewString(this,unicode,len); + } + jsize GetStringLength(jstring str) { + return functions->GetStringLength(this,str); + } + const jchar *GetStringChars(jstring str, jboolean *isCopy) { + return functions->GetStringChars(this,str,isCopy); + } + void ReleaseStringChars(jstring str, const jchar *chars) { + functions->ReleaseStringChars(this,str,chars); + } + + jstring NewStringUTF(const char *utf) { + return functions->NewStringUTF(this,utf); + } + jsize GetStringUTFLength(jstring str) { + return functions->GetStringUTFLength(this,str); + } + const char* GetStringUTFChars(jstring str, jboolean *isCopy) { + return functions->GetStringUTFChars(this,str,isCopy); + } + void ReleaseStringUTFChars(jstring str, const char* chars) { + functions->ReleaseStringUTFChars(this,str,chars); + } + + jsize GetArrayLength(jarray array) { + return functions->GetArrayLength(this,array); + } + + jobjectArray NewObjectArray(jsize len, jclass clazz, + jobject init) { + return functions->NewObjectArray(this,len,clazz,init); + } + jobject GetObjectArrayElement(jobjectArray array, jsize index) { + return functions->GetObjectArrayElement(this,array,index); + } + void SetObjectArrayElement(jobjectArray array, jsize index, + jobject val) { + functions->SetObjectArrayElement(this,array,index,val); + } + + jbooleanArray NewBooleanArray(jsize len) { + return functions->NewBooleanArray(this,len); + } + jbyteArray NewByteArray(jsize len) { + return functions->NewByteArray(this,len); + } + jcharArray NewCharArray(jsize len) { + return functions->NewCharArray(this,len); + } + jshortArray NewShortArray(jsize len) { + return functions->NewShortArray(this,len); + } + jintArray NewIntArray(jsize len) { + return functions->NewIntArray(this,len); + } + jlongArray NewLongArray(jsize len) { + return functions->NewLongArray(this,len); + } + jfloatArray NewFloatArray(jsize len) { + return functions->NewFloatArray(this,len); + } + jdoubleArray NewDoubleArray(jsize len) { + return functions->NewDoubleArray(this,len); + } + + jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) { + return functions->GetBooleanArrayElements(this,array,isCopy); + } + jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) { + return functions->GetByteArrayElements(this,array,isCopy); + } + jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) { + return functions->GetCharArrayElements(this,array,isCopy); + } + jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) { + return functions->GetShortArrayElements(this,array,isCopy); + } + jint * GetIntArrayElements(jintArray array, jboolean *isCopy) { + return functions->GetIntArrayElements(this,array,isCopy); + } + jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) { + return functions->GetLongArrayElements(this,array,isCopy); + } + jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) { + return functions->GetFloatArrayElements(this,array,isCopy); + } + jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) { + return functions->GetDoubleArrayElements(this,array,isCopy); + } + + void ReleaseBooleanArrayElements(jbooleanArray array, + jboolean *elems, + jint mode) { + functions->ReleaseBooleanArrayElements(this,array,elems,mode); + } + void ReleaseByteArrayElements(jbyteArray array, + jbyte *elems, + jint mode) { + functions->ReleaseByteArrayElements(this,array,elems,mode); + } + void ReleaseCharArrayElements(jcharArray array, + jchar *elems, + jint mode) { + functions->ReleaseCharArrayElements(this,array,elems,mode); + } + void ReleaseShortArrayElements(jshortArray array, + jshort *elems, + jint mode) { + functions->ReleaseShortArrayElements(this,array,elems,mode); + } + void ReleaseIntArrayElements(jintArray array, + jint *elems, + jint mode) { + functions->ReleaseIntArrayElements(this,array,elems,mode); + } + void ReleaseLongArrayElements(jlongArray array, + jlong *elems, + jint mode) { + functions->ReleaseLongArrayElements(this,array,elems,mode); + } + void ReleaseFloatArrayElements(jfloatArray array, + jfloat *elems, + jint mode) { + functions->ReleaseFloatArrayElements(this,array,elems,mode); + } + void ReleaseDoubleArrayElements(jdoubleArray array, + jdouble *elems, + jint mode) { + functions->ReleaseDoubleArrayElements(this,array,elems,mode); + } + + void GetBooleanArrayRegion(jbooleanArray array, + jsize start, jsize len, jboolean *buf) { + functions->GetBooleanArrayRegion(this,array,start,len,buf); + } + void GetByteArrayRegion(jbyteArray array, + jsize start, jsize len, jbyte *buf) { + functions->GetByteArrayRegion(this,array,start,len,buf); + } + void GetCharArrayRegion(jcharArray array, + jsize start, jsize len, jchar *buf) { + functions->GetCharArrayRegion(this,array,start,len,buf); + } + void GetShortArrayRegion(jshortArray array, + jsize start, jsize len, jshort *buf) { + functions->GetShortArrayRegion(this,array,start,len,buf); + } + void GetIntArrayRegion(jintArray array, + jsize start, jsize len, jint *buf) { + functions->GetIntArrayRegion(this,array,start,len,buf); + } + void GetLongArrayRegion(jlongArray array, + jsize start, jsize len, jlong *buf) { + functions->GetLongArrayRegion(this,array,start,len,buf); + } + void GetFloatArrayRegion(jfloatArray array, + jsize start, jsize len, jfloat *buf) { + functions->GetFloatArrayRegion(this,array,start,len,buf); + } + void GetDoubleArrayRegion(jdoubleArray array, + jsize start, jsize len, jdouble *buf) { + functions->GetDoubleArrayRegion(this,array,start,len,buf); + } + + void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len, + const jboolean *buf) { + functions->SetBooleanArrayRegion(this,array,start,len,buf); + } + void SetByteArrayRegion(jbyteArray array, jsize start, jsize len, + const jbyte *buf) { + functions->SetByteArrayRegion(this,array,start,len,buf); + } + void SetCharArrayRegion(jcharArray array, jsize start, jsize len, + const jchar *buf) { + functions->SetCharArrayRegion(this,array,start,len,buf); + } + void SetShortArrayRegion(jshortArray array, jsize start, jsize len, + const jshort *buf) { + functions->SetShortArrayRegion(this,array,start,len,buf); + } + void SetIntArrayRegion(jintArray array, jsize start, jsize len, + const jint *buf) { + functions->SetIntArrayRegion(this,array,start,len,buf); + } + void SetLongArrayRegion(jlongArray array, jsize start, jsize len, + const jlong *buf) { + functions->SetLongArrayRegion(this,array,start,len,buf); + } + void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len, + const jfloat *buf) { + functions->SetFloatArrayRegion(this,array,start,len,buf); + } + void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len, + const jdouble *buf) { + functions->SetDoubleArrayRegion(this,array,start,len,buf); + } + + jint RegisterNatives(jclass clazz, const JNINativeMethod *methods, + jint nMethods) { + return functions->RegisterNatives(this,clazz,methods,nMethods); + } + jint UnregisterNatives(jclass clazz) { + return functions->UnregisterNatives(this,clazz); + } + + jint MonitorEnter(jobject obj) { + return functions->MonitorEnter(this,obj); + } + jint MonitorExit(jobject obj) { + return functions->MonitorExit(this,obj); + } + + jint GetJavaVM(JavaVM **vm) { + return functions->GetJavaVM(this,vm); + } + + void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) { + functions->GetStringRegion(this,str,start,len,buf); + } + void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) { + functions->GetStringUTFRegion(this,str,start,len,buf); + } + + void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) { + return functions->GetPrimitiveArrayCritical(this,array,isCopy); + } + void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) { + functions->ReleasePrimitiveArrayCritical(this,array,carray,mode); + } + + const jchar * GetStringCritical(jstring string, jboolean *isCopy) { + return functions->GetStringCritical(this,string,isCopy); + } + void ReleaseStringCritical(jstring string, const jchar *cstring) { + functions->ReleaseStringCritical(this,string,cstring); + } + + jweak NewWeakGlobalRef(jobject obj) { + return functions->NewWeakGlobalRef(this,obj); + } + void DeleteWeakGlobalRef(jweak ref) { + functions->DeleteWeakGlobalRef(this,ref); + } + + jboolean ExceptionCheck() { + return functions->ExceptionCheck(this); + } + + jobject NewDirectByteBuffer(void* address, jlong capacity) { + return functions->NewDirectByteBuffer(this, address, capacity); + } + void* GetDirectBufferAddress(jobject buf) { + return functions->GetDirectBufferAddress(this, buf); + } + jlong GetDirectBufferCapacity(jobject buf) { + return functions->GetDirectBufferCapacity(this, buf); + } + jobjectRefType GetObjectRefType(jobject obj) { + return functions->GetObjectRefType(this, obj); + } + + /* Module Features */ + + jobject GetModule(jclass clazz) { + return functions->GetModule(this, clazz); + } + + /* Virtual threads */ + + jboolean IsVirtualThread(jobject obj) { + return functions->IsVirtualThread(this, obj); + } + +#endif /* __cplusplus */ +}; + +/* + * optionString may be any option accepted by the JVM, or one of the + * following: + * + * -D= Set a system property. + * -verbose[:class|gc|jni] Enable verbose output, comma-separated. E.g. + * "-verbose:class" or "-verbose:gc,class" + * Standard names include: gc, class, and jni. + * All nonstandard (VM-specific) names must begin + * with "X". + * vfprintf extraInfo is a pointer to the vfprintf hook. + * exit extraInfo is a pointer to the exit hook. + * abort extraInfo is a pointer to the abort hook. + */ +typedef struct JavaVMOption { + char *optionString; + void *extraInfo; +} JavaVMOption; + +typedef struct JavaVMInitArgs { + jint version; + + jint nOptions; + JavaVMOption *options; + jboolean ignoreUnrecognized; +} JavaVMInitArgs; + +typedef struct JavaVMAttachArgs { + jint version; + + char *name; + jobject group; +} JavaVMAttachArgs; + +/* These will be VM-specific. */ + +#define JDK1_2 +#define JDK1_4 + +/* End VM-specific. */ + +struct JNIInvokeInterface_ { + void *reserved0; + void *reserved1; + void *reserved2; + + jint (JNICALL *DestroyJavaVM)(JavaVM *vm); + + jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args); + + jint (JNICALL *DetachCurrentThread)(JavaVM *vm); + + jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version); + + jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args); +}; + +struct JavaVM_ { + const struct JNIInvokeInterface_ *functions; +#ifdef __cplusplus + + jint DestroyJavaVM() { + return functions->DestroyJavaVM(this); + } + jint AttachCurrentThread(void **penv, void *args) { + return functions->AttachCurrentThread(this, penv, args); + } + jint DetachCurrentThread() { + return functions->DetachCurrentThread(this); + } + + jint GetEnv(void **penv, jint version) { + return functions->GetEnv(this, penv, version); + } + jint AttachCurrentThreadAsDaemon(void **penv, void *args) { + return functions->AttachCurrentThreadAsDaemon(this, penv, args); + } +#endif +}; + +#ifdef _JNI_IMPLEMENTATION_ +#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT +#else +#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT +#endif +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetDefaultJavaVMInitArgs(void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args); + +_JNI_IMPORT_OR_EXPORT_ jint JNICALL +JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *); + +/* Defined by native libraries. */ +JNIEXPORT jint JNICALL +JNI_OnLoad(JavaVM *vm, void *reserved); + +JNIEXPORT void JNICALL +JNI_OnUnload(JavaVM *vm, void *reserved); + +#define JNI_VERSION_1_1 0x00010001 +#define JNI_VERSION_1_2 0x00010002 +#define JNI_VERSION_1_4 0x00010004 +#define JNI_VERSION_1_6 0x00010006 +#define JNI_VERSION_1_8 0x00010008 +#define JNI_VERSION_9 0x00090000 +#define JNI_VERSION_10 0x000a0000 +#define JNI_VERSION_19 0x00130000 +#define JNI_VERSION_20 0x00140000 +#define JNI_VERSION_21 0x00150000 + +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ + +#endif /* !_JAVASOFT_JNI_H_ */ diff --git a/okio-zstd/native/include/unix/jni_md.h b/okio-zstd/native/include/unix/jni_md.h new file mode 100644 index 0000000..6e583da --- /dev/null +++ b/okio-zstd/native/include/unix/jni_md.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef __has_attribute + #define __has_attribute(x) 0 +#endif + +#ifndef JNIEXPORT + #if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #ifdef ARM + #define JNIEXPORT __attribute__((externally_visible,visibility("default"))) + #else + #define JNIEXPORT __attribute__((visibility("default"))) + #endif + #else + #define JNIEXPORT + #endif +#endif + +#if (defined(__GNUC__) && ((__GNUC__ > 4) || (__GNUC__ == 4) && (__GNUC_MINOR__ > 2))) || __has_attribute(visibility) + #ifdef ARM + #define JNIIMPORT __attribute__((externally_visible,visibility("default"))) + #else + #define JNIIMPORT __attribute__((visibility("default"))) + #endif +#else + #define JNIIMPORT +#endif + +#define JNICALL + +typedef int jint; +#ifdef _LP64 +typedef long jlong; +#else +typedef long long jlong; +#endif + +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/okio-zstd/native/include/windows/jni_md.h b/okio-zstd/native/include/windows/jni_md.h new file mode 100644 index 0000000..f795421 --- /dev/null +++ b/okio-zstd/native/include/windows/jni_md.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +#ifndef _JAVASOFT_JNI_MD_H_ +#define _JAVASOFT_JNI_MD_H_ + +#ifndef JNIEXPORT + #define JNIEXPORT __declspec(dllexport) +#endif +#define JNIIMPORT __declspec(dllimport) +#define JNICALL __stdcall + +typedef int jint; +typedef long long jlong; +typedef signed char jbyte; + +#endif /* !_JAVASOFT_JNI_MD_H_ */ diff --git a/okio-zstd/src/commonMain/kotlin/okio/zstd/Zstd.kt b/okio-zstd/src/commonMain/kotlin/okio/zstd/Zstd.kt new file mode 100644 index 0000000..67f304d --- /dev/null +++ b/okio-zstd/src/commonMain/kotlin/okio/zstd/Zstd.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:Suppress("ktlint:standard:property-naming") +@file:JvmName("Zstd") + +package okio.zstd + +import okio.IOException +import okio.Sink +import okio.Source +import okio.buffer + +/** Returns a [Sink] that compresses its data with ZStandard before forwarding to this. */ +fun Sink.zstdCompress(): Sink = ZstdCompressSink(this.buffer(), zstdCompressor()) + +/** Returns a [Source] that decompresses its data with ZStandard after reading from this. */ +fun Source.zstdDecompress(): Source = ZstdDecompressSource(this.buffer(), zstdDecompressor()) + +// From ZSTD_ErrorCode in zstd_errors.h. This is a small subset! +internal const val ZSTD_error_no_error = 0L +internal const val ZSTD_error_GENERIC = -1L + +// From ZSTD_cParameter in zstd.h. This is a small subset! +internal const val ZSTD_c_compressionLevel = 100 +internal const val ZSTD_c_checksumFlag = 201 + +// From ZSTD_EndDirective in zstd.h. +internal const val ZSTD_e_continue = 0 +internal const val ZSTD_e_flush = 1 +internal const val ZSTD_e_end = 2 + +@Throws(IOException::class) +internal fun Long.checkError(): Long { + val errorName = getErrorName(this) ?: return this + throw IOException(errorName) +} + +internal expect fun getErrorName(code: Long): String? + +/** Returns a new compressor. The caller must close it. */ +internal expect fun zstdCompressor(): ZstdCompressor + +/** Returns a new decompressor. The caller must close it. */ +internal expect fun zstdDecompressor(): ZstdDecompressor + +internal val emptyByteArray = ByteArray(0) diff --git a/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressSink.kt b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressSink.kt new file mode 100644 index 0000000..9ecbc47 --- /dev/null +++ b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressSink.kt @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer +import okio.Buffer.UnsafeCursor +import okio.BufferedSink +import okio.IOException +import okio.Sink +import okio.Timeout + +/** + * This stages all written bytes to [inputBuffer], and then emits to [sink] on these triggers: + * + * * When [Buffer.completeSegmentByteCount] is greater than 0. + * * On [flush]. + * * On [close]. + * + * This emits directly to [BufferedSink.buffer]. To avoid that from exhausting memory, this always + * calls [BufferedSink.emitCompleteSegments] after writing. + */ +internal class ZstdCompressSink internal constructor( + /** The destination for our compressed data. We output directly to this sink's buffer. */ + @JvmField val sink: BufferedSink, + private val compressor: ZstdCompressor, +) : Sink { + /** + * Raw data written to this sink, and not yet emitted. We emit on [flush], [close], and when + * [Buffer.completeSegmentByteCount] is non-zero. + */ + private val inputBuffer = Buffer() + + private val inputCursor = UnsafeCursor() + private val outputCursor = UnsafeCursor() + + @JvmField var closed = false + + @Throws(IOException::class) + override fun write(source: Buffer, byteCount: Long) { + check(!closed) { "closed" } + + inputBuffer.write(source, byteCount) + compress(ZSTD_e_continue) + } + + @Throws(IOException::class) + override fun flush() { + check(!closed) { "closed" } + + compress(ZSTD_e_flush) + sink.flush() + } + + @Throws(IOException::class) + override fun close() { + if (closed) return + closed = true + + sink.use { + compressor.use { + compress(ZSTD_e_end) + } + } + } + + private fun compress(mode: Int) { + // Decide how many bytes to write immediately. + var inputRemaining = when (mode) { + ZSTD_e_continue -> { + inputBuffer.completeSegmentByteCount() + .also { if (it == 0L) return@compress } // No bytes to write immediately. + } + + else -> inputBuffer.size + } + + do { + var result: Long + sink.buffer.readAndWriteUnsafe(outputCursor).use { outputCursor -> + val outputSizeBefore = sink.buffer.size + outputCursor.expandBuffer(1) + + if (inputRemaining > 0L) { + // Compress some input. This might not produce any output! + inputBuffer.readUnsafe(inputCursor).use { inputCursor -> + inputCursor.next() + result = compressor.compressStream2(outputCursor, inputCursor, mode) + } + inputRemaining -= compressor.inputBytesProcessed + inputBuffer.skip(compressor.inputBytesProcessed.toLong()) + } else { + // No more input, but possibly more output. + inputCursor.data = emptyByteArray + inputCursor.start = 0 + inputCursor.end = 0 + result = compressor.compressStream2(outputCursor, inputCursor, mode) + } + + outputCursor.resizeBuffer(outputSizeBefore + compressor.outputBytesProcessed) + } + + sink.emitCompleteSegments() + result.checkError() + + val finished = when (mode) { + ZSTD_e_continue -> inputRemaining == 0L + else -> inputRemaining == 0L && result == 0L + } + } while (!finished) + } + + override fun timeout(): Timeout = sink.timeout() +} diff --git a/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressor.kt b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressor.kt new file mode 100644 index 0000000..28837d6 --- /dev/null +++ b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdCompressor.kt @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer.UnsafeCursor +import okio.Closeable + +internal abstract class ZstdCompressor : Closeable { + /** The number of bytes consumed on the most recent call to [compressStream2]. */ + @JvmField + var inputBytesProcessed: Int = -1 + + /** The number of bytes produced on the most recent call to [compressStream2]. */ + @JvmField + var outputBytesProcessed: Int = -1 + + /** @param like [ZSTD_c_compressionLevel] or [ZSTD_c_checksumFlag]. */ + abstract fun setParameter(param: Int, value: Int): Long + + /** @param mode one of [ZSTD_e_continue], [ZSTD_e_flush], or [ZSTD_e_end]. */ + abstract fun compressStream2( + output: UnsafeCursor, + input: UnsafeCursor, + mode: Int, + ): Long +} diff --git a/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressSource.kt b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressSource.kt new file mode 100644 index 0000000..0649df6 --- /dev/null +++ b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressSource.kt @@ -0,0 +1,101 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer +import okio.Buffer.UnsafeCursor +import okio.BufferedSource +import okio.EOFException +import okio.Source +import okio.Timeout + +/** + * This satisfies reads with the following process: + * + * 1. Decompresses at least 1 byte from [source] into [outputBuffer]. + * 2. Serves the read request from [outputBuffer]. + * + * Each attempt to decompress returns 0 if the frame is complete, and non-zero otherwise. This + * tracks that most recent result so it can throw [EOFException] if the source is exhausted + * mid-frame. + */ +internal class ZstdDecompressSource internal constructor( + @JvmField val source: BufferedSource, + private val decompressor: ZstdDecompressor, +) : Source { + /** Compressed input. */ + private val inputCursor = UnsafeCursor() + + /** Decompressed output. */ + private val outputBuffer = Buffer() + private val outputCursor = UnsafeCursor() + + private var lastDecompressResult = 0L + + @JvmField + var closed = false + + override fun read(sink: Buffer, byteCount: Long): Long { + require(byteCount >= 0L) { "byteCount < 0: $byteCount" } + check(!closed) { "closed" } + if (byteCount == 0L) return 0L + + refillIfNecessary() + return outputBuffer.read(sink, byteCount) + } + + override fun close() { + if (closed) return + closed = true + + outputBuffer.clear() + + source.use { + decompressor.use { + } + } + } + + private fun refillIfNecessary() { + while (outputBuffer.exhausted()) { + // Attempt to load more data into source.buffer. + if (!source.request(1L)) { + if (lastDecompressResult != 0L) throw EOFException("EOF before end of stream") + return + } + + // Decompress from source.buffer into outputBuffer. + var result: Long + outputBuffer.readAndWriteUnsafe(outputCursor).use { outputCursor -> + val outputSizeBefore = outputBuffer.size + outputCursor.expandBuffer(1) + + source.buffer.readUnsafe(inputCursor).use { inputCursor -> + inputCursor.next() + result = decompressor.decompressStream(outputCursor, inputCursor) + } + source.skip(decompressor.inputBytesProcessed.toLong()) + + outputCursor.resizeBuffer(outputSizeBefore + decompressor.outputBytesProcessed) + } + + lastDecompressResult = result + result.checkError() + } + } + + override fun timeout(): Timeout = source.timeout() +} diff --git a/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressor.kt b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressor.kt new file mode 100644 index 0000000..ff7d46f --- /dev/null +++ b/okio-zstd/src/commonMain/kotlin/okio/zstd/ZstdDecompressor.kt @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2025 Cash App + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer.UnsafeCursor +import okio.Closeable + +internal abstract class ZstdDecompressor : Closeable { + /** The number of bytes consumed on the most recent call to [decompressStream]. */ + @JvmField + var inputBytesProcessed: Int = -1 + + /** The number of bytes produced on the most recent call to [decompressStream]. */ + @JvmField + var outputBytesProcessed: Int = -1 + + abstract fun decompressStream( + output: UnsafeCursor, + input: UnsafeCursor, + ): Long +} diff --git a/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstd.kt b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstd.kt new file mode 100644 index 0000000..e22378a --- /dev/null +++ b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstd.kt @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +@file:JvmName("JniZstd") + +package okio.zstd + +import java.nio.file.Files +import java.util.Locale.US +import okio.FileSystem +import okio.Path.Companion.toOkioPath +import okio.Path.Companion.toPath + +internal val jniZstdPointer: Long = run { + loadNativeLibrary() + createJniZstd() +} + +internal actual fun zstdCompressor(): ZstdCompressor = JniZstdCompressor() + +internal actual fun zstdDecompressor(): ZstdDecompressor = JniZstdDecompressor() + +@JvmName("isError") +internal external fun isError(code: Long): Boolean + +@JvmName("getErrorName") +internal actual external fun getErrorName(code: Long): String? + +@JvmName("createJniZstd") +internal external fun createJniZstd(): Long + +@JvmName("createZstdCompressor") +internal external fun createZstdCompressor(): Long + +@JvmName("createZstdDecompressor") +internal external fun createZstdDecompressor(): Long + +@Suppress("UnsafeDynamicallyLoadedCode") // Only loading from our own JAR contents. +internal fun loadNativeLibrary() { + val osName = System.getProperty("os.name").lowercase(US) + val osArch = System.getProperty("os.arch").lowercase(US) + val resourcePath = when { + osName.contains("linux") -> "/jni/$osArch/libzstd.so".toPath() + osName.contains("mac") -> "/jni/$osArch/libzstd.dylib".toPath() + else -> error("Unsupported OS: $osName") + } + + // File-based deleteOnExit() uses a special internal shutdown hook that always runs last. + val tempFile = Files.createTempFile("zstd", null) + tempFile.toFile().deleteOnExit() + + FileSystem.RESOURCES.read(resourcePath) { + FileSystem.SYSTEM.write(tempFile.toOkioPath()) { + writeAll(this@read) + } + } + + System.load(tempFile.toAbsolutePath().toString()) +} diff --git a/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdCompressor.kt b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdCompressor.kt new file mode 100644 index 0000000..022f27e --- /dev/null +++ b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdCompressor.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer.UnsafeCursor + +internal class JniZstdCompressor : ZstdCompressor() { + @JvmField + var cctxPointer = createZstdCompressor() + .also { + if (it == 0L) throw OutOfMemoryError("createZstdCompressor failed") + } + + override fun setParameter(param: Int, value: Int): Long = setParameter(cctxPointer, param, value) + + override fun compressStream2( + output: UnsafeCursor, + input: UnsafeCursor, + mode: Int, + ): Long = compressStream2(jniZstdPointer, cctxPointer, output, input, mode) + + override fun close() { + val cctxPointerToClose = cctxPointer + if (cctxPointerToClose != 0L) { + cctxPointer = 0L + close(cctxPointerToClose) + } + } + + private external fun setParameter(cctxPointer: Long, param: Int, value: Int): Long + + private external fun compressStream2( + jniPointer: Long, + cctxPointer: Long, + output: UnsafeCursor, + input: UnsafeCursor, + mode: Int, + ): Long + + private external fun close(cctxPointer: Long) +} diff --git a/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdDecompressor.kt b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdDecompressor.kt new file mode 100644 index 0000000..39d5a52 --- /dev/null +++ b/okio-zstd/src/jvmMain/kotlin/okio/zstd/JniZstdDecompressor.kt @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import okio.Buffer.UnsafeCursor + +internal class JniZstdDecompressor : ZstdDecompressor() { + @JvmField + var dctxPointer = createZstdDecompressor() + .also { + if (it == 0L) throw OutOfMemoryError("createZstdDecompressor failed") + } + + override fun decompressStream( + output: UnsafeCursor, + input: UnsafeCursor, + ): Long = decompressStream(jniZstdPointer, dctxPointer, output, input) + + override fun close() { + val cctxPointerToClose = dctxPointer + if (cctxPointerToClose != 0L) { + dctxPointer = 0L + close(cctxPointerToClose) + } + } + + private external fun decompressStream( + jniPointer: Long, + dctxPointer: Long, + output: UnsafeCursor, + input: UnsafeCursor, + ): Long + + private external fun close(cctxPointer: Long) +} diff --git a/okio-zstd/src/jvmTest/kotlin/okio/zstd/LubenZstdJniInteropTest.kt b/okio-zstd/src/jvmTest/kotlin/okio/zstd/LubenZstdJniInteropTest.kt new file mode 100644 index 0000000..c46ce8d --- /dev/null +++ b/okio-zstd/src/jvmTest/kotlin/okio/zstd/LubenZstdJniInteropTest.kt @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.luben.zstd.Zstd as LubenZstd +import kotlin.test.Test +import okio.Buffer +import okio.ByteString +import okio.ByteString.Companion.encodeUtf8 +import okio.ByteString.Companion.toByteString +import okio.buffer + +/** + * Everyone else uses the Zstd-jni library. Confirm that we can interoperate with it and don't have + * any runtime collisions on binary symbols. + * + * https://github.com/luben/zstd-jni + */ +internal class LubenZstdJniInteropTest { + + @Test + fun lubenCompressOkioDecompress() { + val original = "hello world".encodeUtf8() + val compressed = LubenZstd.compress(original.toByteArray()) + val decompressed = oneShotDecompress(compressed.toByteString()) + assertThat(decompressed).isEqualTo(original) + } + + @Test + fun okioCompressLubenDecompress() { + val original = "hello world".encodeUtf8() + val compressed = compress(original) + val decompressed = compressed.lubenDecompress() + assertThat(decompressed).isEqualTo(original) + } + + private fun compress(original: ByteString): Buffer { + val compressed = Buffer() + compressed.zstdCompress().buffer().use { sink -> + sink.write(original) + } + return compressed + } +} diff --git a/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdCompressSinkTest.kt b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdCompressSinkTest.kt new file mode 100644 index 0000000..e5fa1ee --- /dev/null +++ b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdCompressSinkTest.kt @@ -0,0 +1,160 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNotEqualTo +import assertk.assertions.isTrue +import com.github.luben.zstd.ZstdInputStream +import java.io.InterruptedIOException +import java.util.concurrent.TimeUnit +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertFailsWith +import okio.Buffer +import okio.IOException +import okio.Pipe +import okio.Sink +import okio.buffer +import okio.source + +class ZstdCompressSinkTest { + @Test + fun compressEmpty() { + testRoundTrip(0) + } + + @Test + fun compressSingleSegment() { + testRoundTrip(1024) + } + + @Test + fun compressMultipleSegments() { + testRoundTrip(1024 * 1024) + } + + private fun testRoundTrip(byteCount: Int) { + val compressed = Buffer() + compressed.zstdCompress().buffer().use { + it.writeAll(RandomSource(Random(1), byteCount)) + } + assertThat(compressed.lubenDecompress()) + .isEqualTo(RandomSource(Random(1), byteCount).buffer().readByteString()) + } + + /** Confirm we can clean up even if writes aren't working. */ + @Test + fun writeFailure() { + val delegate = Buffer() + var sinkClosed = false + val sink = object : Sink by delegate { + override fun write(source: Buffer, byteCount: Long) { + delegate.write(source, byteCount) + throw IOException("boom!") + } + + override fun close() { + delegate.close() + sinkClosed = true + } + } + + val compressor = JniZstdCompressor() + val zstdCompressSink = ZstdCompressSink(sink.buffer(), compressor) + + assertFailsWith { + zstdCompressSink.buffer().writeAll(RandomSource(Random(1), 1024 * 1024)) + } + assertThat(zstdCompressSink.closed).isFalse() + assertThat(compressor.cctxPointer).isNotEqualTo(0L) + assertThat(sinkClosed).isFalse() + + assertFailsWith { + zstdCompressSink.close() + } + assertThat(zstdCompressSink.closed).isTrue() + assertThat(compressor.cctxPointer).isEqualTo(0L) + assertThat(sinkClosed).isTrue() + } + + @Test + fun flushMakesDataImmediatelyReadable() { + val pipe = Pipe(1024L) + pipe.source.timeout().timeout(250, TimeUnit.MILLISECONDS) + + ZstdInputStream(pipe.source.buffer().inputStream()).source().buffer().use { source -> + pipe.sink.zstdCompress().buffer().use { sink -> + sink.writeUtf8("hello world") + sink.flush() + + assertThat(source.readUtf8(11)).isEqualTo("hello world") + } + } + } + + @Test + fun closeMakesDataImmediatelyReadable() { + val pipe = Pipe(1024L) + pipe.source.timeout().timeout(250, TimeUnit.MILLISECONDS) + + ZstdInputStream(pipe.source.buffer().inputStream()).source().buffer().use { source -> + pipe.sink.zstdCompress().buffer().use { sink -> + sink.writeUtf8("hello world") + } + + assertThat(source.readUtf8(11)).isEqualTo("hello world") + } + } + + @Test + fun dataIsNotReadableUntilFlush() { + val pipe = Pipe(1024L) + pipe.source.timeout().timeout(250, TimeUnit.MILLISECONDS) + + ZstdInputStream(pipe.source.buffer().inputStream()).source().buffer().use { source -> + pipe.sink.zstdCompress().buffer().use { sink -> + sink.writeUtf8("hello world") + + assertFailsWith { + source.readUtf8(11) + } + + sink.flush() + assertThat(source.readUtf8(11)).isEqualTo("hello world") + } + } + } + + @Test + fun dataIsNotReadableUntilClose() { + val pipe = Pipe(1024L) + pipe.source.timeout().timeout(250, TimeUnit.MILLISECONDS) + + ZstdInputStream(pipe.source.buffer().inputStream()).source().buffer().use { source -> + pipe.sink.zstdCompress().buffer().use { sink -> + sink.writeUtf8("hello world") + + assertFailsWith { + source.readUtf8(11) + } + } + assertThat(source.readUtf8(11)).isEqualTo("hello world") + } + } +} diff --git a/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdDecompressSourceTest.kt b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdDecompressSourceTest.kt new file mode 100644 index 0000000..587f432 --- /dev/null +++ b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdDecompressSourceTest.kt @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNotEqualTo +import assertk.assertions.isTrue +import com.github.luben.zstd.ZstdOutputStream +import kotlin.random.Random +import kotlin.test.Test +import kotlin.test.assertFailsWith +import okio.Buffer +import okio.ByteString +import okio.EOFException +import okio.IOException +import okio.Pipe +import okio.Source +import okio.buffer +import okio.sink + +class ZstdDecompressSourceTest { + @Test + fun decompressEmpty() { + testRoundTrip(0) + } + + @Test + fun decompressSingleSegment() { + testRoundTrip(1024) + } + + @Test + fun decompressMultipleSegments() { + testRoundTrip(1024 * 1024) + } + + private fun testRoundTrip(byteCount: Int) { + val compressed = RandomSource(Random(1), byteCount) + .lubenCompress() + + val decompressed = compressed.zstdDecompress().buffer().use { + it.readByteString() + } + + assertThat(decompressed) + .isEqualTo(RandomSource(Random(1), byteCount).buffer().readByteString()) + } + + @Test + fun flushedDataIsReadable() { + val pipe = Pipe(1024L) + + pipe.source.zstdDecompress().buffer().use { source -> + ZstdOutputStream(pipe.sink.buffer().outputStream()).sink().buffer().use { sink -> + sink.writeUtf8("hello world") + sink.flush() + + assertThat(source.readUtf8(11)).isEqualTo("hello world") + + sink.writeUtf8("hello again") + sink.flush() + + assertThat(source.readUtf8(11)).isEqualTo("hello again") + } + } + } + + /** Confirm we can clean up even if reads aren't working. */ + @Test + fun readFailure() { + val delegate = RandomSource(Random(1), 1024 * 1024) + .lubenCompress() + var sourceClosed = false + var explode = false + val source = object : Source by delegate { + override fun read(sink: Buffer, byteCount: Long): Long { + val result = delegate.read(sink, byteCount) + if (explode) throw IOException("boom!") + return result + } + + override fun close() { + sourceClosed = true + } + } + + val decompressor = JniZstdDecompressor() + val zstdDecompressSource = ZstdDecompressSource(source.buffer(), decompressor) + + explode = true + assertFailsWith { + zstdDecompressSource.buffer().require(1024L) + } + assertThat(zstdDecompressSource.closed).isFalse() + assertThat(decompressor.dctxPointer).isNotEqualTo(0L) + assertThat(sourceClosed).isFalse() + + zstdDecompressSource.close() + assertThat(zstdDecompressSource.closed).isTrue() + assertThat(decompressor.dctxPointer).isEqualTo(0L) + assertThat(sourceClosed).isTrue() + } + + @Test + fun sourceIsTruncated() { + val compressed = RandomSource(Random(1), 1024) + .lubenCompress() + val truncated = Buffer() + truncated.write(compressed, compressed.size - 1L) + + truncated.zstdDecompress().buffer().use { + assertFailsWith { + it.readByteString() + } + } + } + + @Test + fun sourceIsEmpty() { + Buffer().zstdDecompress().buffer().use { + assertThat(it.readByteString()).isEqualTo(ByteString.EMPTY) + } + } +} diff --git a/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTest.kt b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTest.kt new file mode 100644 index 0000000..6bd1891 --- /dev/null +++ b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTest.kt @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import assertk.assertThat +import assertk.assertions.isEqualTo +import assertk.assertions.isFalse +import assertk.assertions.isNull +import assertk.assertions.isTrue +import kotlin.test.Test +import okio.ByteString.Companion.decodeHex +import okio.ByteString.Companion.encodeUtf8 + +/** + * Exercise the native bindings. + */ +internal class ZstdTest { + private val helloWorld = "hello world".encodeUtf8() + private val helloWorldZstd = "28b52ffd200b59000068656c6c6f20776f726c64".decodeHex() + + @Test + fun isError() { + assertThat(isError(ZSTD_error_no_error)).isFalse() + assertThat(isError(ZSTD_error_GENERIC)).isTrue() + } + + @Test + fun getErrorName() { + assertThat(getErrorName(ZSTD_error_no_error)).isNull() + assertThat(getErrorName(ZSTD_error_GENERIC)) + .isEqualTo("Error (generic)") + } + + @Test + fun compress() { + assertThat(oneShotCompress(helloWorld)).isEqualTo(helloWorldZstd) + } + + @Test + fun customOptions() { + assertThat( + oneShotCompress( + original = helloWorld, + compressionLevel = 7, + ), + ).isEqualTo("28b52ffd200b59000068656c6c6f20776f726c64".decodeHex()) + + assertThat( + oneShotCompress( + original = helloWorld, + checksumFlag = 1, + ), + ).isEqualTo("28b52ffd240b59000068656c6c6f20776f726c6468691eb2".decodeHex()) + + assertThat( + oneShotCompress( + original = helloWorld, + compressionLevel = 7, + checksumFlag = 1, + ), + ).isEqualTo("28b52ffd240b59000068656c6c6f20776f726c6468691eb2".decodeHex()) + } + + @Test + fun decompress() { + assertThat(oneShotDecompress(helloWorldZstd)).isEqualTo(helloWorld) + } + + @Test + fun compressWithOffsets() { + assertThat( + oneShotCompress( + original = helloWorld, + inputOffset = 5, + inputPadding = 7, + outputOffset = 9, + ), + ).isEqualTo(helloWorldZstd) + } + + @Test + fun decompressWithOffsets() { + assertThat( + oneShotDecompress( + compressed = helloWorldZstd, + inputOffset = 5, + inputPadding = 7, + outputOffset = 9, + ), + ).isEqualTo(helloWorld) + } +} diff --git a/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTesting.kt b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTesting.kt new file mode 100644 index 0000000..9a7fb0f --- /dev/null +++ b/okio-zstd/src/jvmTest/kotlin/okio/zstd/ZstdTesting.kt @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2025 Square, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package okio.zstd + +import assertk.assertThat +import assertk.assertions.isEqualTo +import com.github.luben.zstd.ZstdInputStream +import com.github.luben.zstd.ZstdOutputStream +import kotlin.random.Random +import okio.Buffer +import okio.Buffer.UnsafeCursor +import okio.ByteString +import okio.ByteString.Companion.toByteString +import okio.Source +import okio.Timeout +import okio.buffer +import okio.sink +import okio.source + +/** Returns [byteCount] bytes from [random]. */ +class RandomSource( + val random: Random, + byteCount: Int, +) : Source { + private var remaining = byteCount.toLong() + + override fun read(sink: Buffer, byteCount: Long): Long { + val toRead = minOf(byteCount, remaining) + if (toRead == 0L) return -1L + sink.write(random.nextBytes(toRead.toInt())) + remaining -= toRead + return toRead + } + + override fun timeout() = Timeout.Companion.NONE + + override fun close() { + } +} + +/** + * Decompress this buffer using Zstd-jni and return the result. + * + * Note that this doesn't use [com.github.luben.zstd.Zstd.decompress] because those functions don't + * work on data that was compressed in a stream. + */ +fun Buffer.lubenDecompress(): ByteString { + val result = Buffer() + ZstdInputStream(inputStream()).source().use { + result.writeAll(it) + } + return result.readByteString() +} + +/** + * Compress this source using Zstd-jni and return the result. + */ +fun Source.lubenCompress(): Buffer { + val result = Buffer() + ZstdOutputStream(result.outputStream()).sink().buffer().use { + it.writeAll(this@lubenCompress) + } + return result +} + +fun oneShotCompress( + original: ByteString, + inputOffset: Int = 0, + inputPadding: Int = 0, + outputOffset: Int = 0, + compressionLevel: Int? = null, + checksumFlag: Int? = null, + outputArraySize: Int = 1024, +): ByteString { + JniZstdCompressor().use { compressor -> + compressionLevel?.let { + compressor.setParameter(ZSTD_c_compressionLevel, it).checkError() + } + checksumFlag?.let { + compressor.setParameter(ZSTD_c_checksumFlag, it).checkError() + } + + val inputArray = ByteArray(original.size + inputOffset + inputPadding) + original.copyInto(0, inputArray, inputOffset, original.size) + + val input = UnsafeCursor() + input.data = inputArray + input.start = inputOffset + input.end = inputOffset + original.size + + val output = UnsafeCursor() + output.data = ByteArray(outputArraySize) + output.start = outputOffset + output.end = outputArraySize + + val remaining = compressor.compressStream2(output, input, ZSTD_e_end).checkError() + assertThat(remaining).isEqualTo(0) + + return output.data!!.toByteString(outputOffset, compressor.outputBytesProcessed) + } +} + +fun oneShotDecompress( + compressed: ByteString, + inputOffset: Int = 0, + inputPadding: Int = 0, + outputOffset: Int = 0, + outputArraySize: Int = 1024, +): ByteString { + JniZstdDecompressor().use { decompressor -> + val inputArray = ByteArray(compressed.size + inputOffset + inputPadding) + compressed.copyInto(0, inputArray, inputOffset, compressed.size) + + val input = UnsafeCursor() + input.data = inputArray + input.start = inputOffset + input.end = inputOffset + compressed.size + + val output = UnsafeCursor() + output.data = ByteArray(outputArraySize) + output.start = outputOffset + output.end = outputArraySize + + val remaining = decompressor.decompressStream(output, input).checkError() + assertThat(remaining).isEqualTo(0) + + return output.data!!.toByteString(outputOffset, decompressor.outputBytesProcessed) + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..c175d52 --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,5 @@ +rootProject.name = "okio-zstd-root" + +include(":okio-zstd") + +enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS") diff --git a/zstd b/zstd new file mode 160000 index 0000000..1dbc2e0 --- /dev/null +++ b/zstd @@ -0,0 +1 @@ +Subproject commit 1dbc2e09084e843f6c0dcc2d0791610015c50979