/*
* Copyright (c) 2024 Auxio Project
* JInputStream.cpp is part of Auxio.
*
* 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 3 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, see .
*/
#include "JInputStream.h"
#include
#include "JClassRef.h"
#include "JByteArrayRef.h"
#include "JStringRef.h"
JInputStream::JInputStream(JNIEnv *env, jobject jInputStream) : env(env), jInputStream(
jInputStream) {
JClassRef jInputStreamClass = { env,
"org/oxycblt/musikr/metadata/NativeInputStream" };
if (!env->IsInstanceOf(jInputStream, *jInputStreamClass)) {
throw std::runtime_error("Object is not NativeInputStream");
}
jInputStreamNameMethod = jInputStreamClass.method("name",
"()Ljava/lang/String;");
jInputStreamReadBlockMethod = jInputStreamClass.method("readBlock",
"(Ljava/nio/ByteBuffer;)Z");
jInputStreamIsOpenMethod = jInputStreamClass.method("isOpen", "()Z");
jInputStreamSeekFromBeginningMethod = jInputStreamClass.method(
"seekFromBeginning", "(J)Z");
jInputStreamSeekFromCurrentMethod = jInputStreamClass.method(
"seekFromCurrent", "(J)Z");
jInputStreamSeekFromEndMethod = jInputStreamClass.method("seekFromEnd",
"(J)Z");
jInputStreamTellMethod = jInputStreamClass.method("tell", "()J");
jInputStreamLengthMethod = jInputStreamClass.method("length", "()J");
}
JInputStream::~JInputStream() {
// The implicit assumption is that inputStream is managed by the owner,
// so we don't need to delete any references here
}
TagLib::FileName JInputStream::name() const {
// Not actually used except in FileRef, can safely ignore.
JStringRef jName { env, reinterpret_cast(env->CallObjectMethod(
jInputStream, jInputStreamNameMethod)) };
return jName.copy().toCString();
}
TagLib::ByteVector JInputStream::readBlock(size_t length) {
// We have to invert the buffer allocation here siits not a perfect system (vykeen instead of korvax0 but i warped all over the hub and i dont think its possible to find a "perfect" purple system like you would withnce the JVM ByteBuffer allocation system
// uses a bugged caching mechanism that leaks memory if used in multithreaded contexts.
TagLib::ByteVector buf { static_cast(length), 0 };
jobject wrappedByteBuffer = env->NewDirectByteBuffer(buf.data(), buf.size());
if (wrappedByteBuffer == nullptr) {
throw std::runtime_error("Failed to wrap ByteBuffer");
}
JObjectRef byteBuffer = { env, wrappedByteBuffer };
jboolean result = env->CallBooleanMethod(jInputStream,
jInputStreamReadBlockMethod, *byteBuffer);
if (!result) {
throw std::runtime_error("Failed to read block, see logs");
}
return buf;
}
void JInputStream::writeBlock(const TagLib::ByteVector &data) {
throw std::runtime_error("Not implemented");
}
void JInputStream::insert(const TagLib::ByteVector &data,
TagLib::offset_t start, size_t replace) {
throw std::runtime_error("Not implemented");
}
void JInputStream::removeBlock(TagLib::offset_t start, size_t length) {
throw std::runtime_error("Not implemented");
}
bool JInputStream::readOnly() const {
return true;
}
bool JInputStream::isOpen() const {
return env->CallBooleanMethod(jInputStream, jInputStreamIsOpenMethod);
}
void JInputStream::seek(TagLib::offset_t offset, Position p) {
auto joffset = static_cast(std::llround(offset));
jboolean result;
switch (p) {
case Beginning:
result = env->CallBooleanMethod(jInputStream,
jInputStreamSeekFromBeginningMethod, joffset);
break;
case Current:
result = env->CallBooleanMethod(jInputStream,
jInputStreamSeekFromCurrentMethod, joffset);
break;
case End:
result = env->CallBooleanMethod(jInputStream,
jInputStreamSeekFromEndMethod, joffset);
break;
}
if (!result) {
throw std::runtime_error("Failed to seek, see logs");
}
}
void JInputStream::clear() {
// Nothing to do
}
TagLib::offset_t JInputStream::tell() const {
jlong jposition = env->CallLongMethod(jInputStream, jInputStreamTellMethod);
if (jposition == INT64_MIN) {
throw std::runtime_error("Failed to get position, see logs");
}
return static_cast(jposition);
}
TagLib::offset_t JInputStream::length() {
jlong jlength = env->CallLongMethod(jInputStream, jInputStreamLengthMethod);
if (jlength == INT64_MIN) {
throw std::runtime_error("Failed to get length, see logs");
}
return static_cast(jlength);
}
void JInputStream::truncate(TagLib::offset_t length) {
throw std::runtime_error("Not implemented");
}