java/JavaOutputStream.h (55 lines of code) (raw):
/*-
* -\-\-
* voyager
* --
* Copyright (C) 2016 - 2023 Spotify AB
* --
* 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 <StreamUtils.h>
#include <jni.h>
class JavaOutputStream : public OutputStream {
static constexpr unsigned long long MAX_BUFFER_SIZE = 1024 * 1024 * 100;
public:
JavaOutputStream(JNIEnv *env, jobject outputStream)
: env(env), outputStream(outputStream) {
jclass outputStreamClass = env->FindClass("java/io/OutputStream");
if (!outputStreamClass) {
throw std::runtime_error(
"Native code failed to find OutputStream class!");
}
if (!env->IsInstanceOf(outputStream, outputStreamClass)) {
throw std::runtime_error(
"Provided Java object is not a java.io.OutputStream!");
}
};
virtual void flush() {
jmethodID flushMethod = env->GetMethodID(
env->FindClass("java/io/OutputStream"), "flush", "()V");
env->CallVoidMethod(outputStream, flushMethod);
if (env->ExceptionCheck()) {
throw std::runtime_error("JNI exception was thrown!");
}
}
virtual bool write(const char *ptr, unsigned long long numBytes) {
jmethodID writeMethod = env->GetMethodID(
env->FindClass("java/io/OutputStream"), "write", "([B)V");
if (!writeMethod) {
throw std::runtime_error("Native code failed to find "
"java.io.OutputStream#write(byte[]) method!");
}
for (unsigned long long i = 0; i < numBytes; i += MAX_BUFFER_SIZE) {
unsigned long long chunkSize = std::min(numBytes - i, MAX_BUFFER_SIZE);
jbyteArray byteArray = env->NewByteArray(chunkSize);
if (!byteArray) {
throw std::domain_error(
"Failed to instantiate Java byte array of size: " +
std::to_string(chunkSize));
}
env->SetByteArrayRegion(byteArray, 0, chunkSize, (const jbyte *)ptr);
env->CallVoidMethod(outputStream, writeMethod, byteArray);
env->DeleteLocalRef(byteArray);
if (env->ExceptionCheck()) {
return false;
}
ptr += chunkSize;
}
return true;
}
virtual ~JavaOutputStream(){};
private:
JNIEnv *env;
jobject outputStream;
};