// Copyright (c) 2019 Nicholas Corgan // SPDX-License-Identifier: BSL-1.0 #include "JavaProxy.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include static Pothos::Proxy convertBufferChunkToJavaByteBuffer( Pothos::ProxyEnvironment::Sptr env, const Pothos::BufferChunk& buffer) { auto javaProxyEnvironment = std::dynamic_pointer_cast(env); jobject jniByteBuffer = javaProxyEnvironment->env->NewDirectByteBuffer( reinterpret_cast(buffer.address), static_cast(buffer.length)); auto bufferProxy = javaProxyEnvironment->makeHandle(jniByteBuffer); // Cast the return buffer type to the correct subclass based on the // BufferChunk's dtype. static const std::unordered_map dtypeToJavaFunc = { {"int8", "asCharBuffer"}, {"int16", "asShortBuffer"}, {"int32", "asIntBuffer"}, {"int64", "asLongBuffer"}, {"float32", "asFloatBuffer"}, {"float64", "asDoubleBuffer"} }; auto mapIter = dtypeToJavaFunc.find(buffer.dtype.name()); if(mapIter != dtypeToJavaFunc.end()) { const std::string& castFunc = mapIter->second; bufferProxy = bufferProxy.call(castFunc); } else { throw Pothos::InvalidArgumentException("Invalid or unsupported DType: "+buffer.dtype.name()); } return bufferProxy; } static bool isJavaProxySubclass( const Pothos::Proxy& proxy, const std::string& destClass) { auto env = proxy.getEnvironment(); auto proxyJavaClass = proxy.call("class"); auto destJavaClass = env->findProxy(destClass); return destJavaClass.call("isAssignableFrom", proxyJavaClass); } static Pothos::BufferChunk convertJavaByteBufferToBufferChunk(const Pothos::Proxy& byteBuffer) { // The given byte buffer must be direct (meaning it has a "backing" array // that can be accessed by native code). if(!byteBuffer.call("isDirect")) { throw Pothos::InvalidArgumentException("The given "+byteBuffer.getClassName()+" cannot be accessed by native code."); } auto javaProxyEnvironment = std::dynamic_pointer_cast( byteBuffer.getEnvironment()); jobject jniByteBuffer = javaProxyEnvironment->getHandle(byteBuffer)->toJobject(); void* address = javaProxyEnvironment->env->GetDirectBufferAddress(jniByteBuffer); jlong capacity = javaProxyEnvironment->env->GetDirectBufferCapacity(jniByteBuffer); static const std::unordered_map javaClassToDType = { {"java.nio.ByteBuffer", "int8"}, {"java.nio.CharBuffer", "int8"}, {"java.nio.ShortBuffer", "int16"}, {"java.nio.IntBuffer", "int32"}, {"java.nio.LongBuffer", "int64"}, {"java.nio.FloatBuffer", "float32"}, {"java.nio.DoubleBuffer", "float64"}, }; using MapPair = std::unordered_map::value_type; auto applicableClassIter = std::find_if( javaClassToDType.begin(), javaClassToDType.end(), [&byteBuffer](const MapPair& mapPair) { return isJavaProxySubclass(byteBuffer, mapPair.first); }); if(applicableClassIter == javaClassToDType.end()) { throw Pothos::InvalidArgumentException("Could not find valid DType for "+byteBuffer.getClassName()); } Pothos::DType dtype(applicableClassIter->second); auto sharedBuff = Pothos::SharedBuffer( reinterpret_cast(address), static_cast(capacity * dtype.elemSize()), byteBuffer.getHandle()); auto chunk = Pothos::BufferChunk(sharedBuff); chunk.dtype = dtype; return chunk; } pothos_static_block(pothosRegisterJavaByteBufferConversions) { Pothos::PluginRegistry::addCall( "/proxy/converters/java/bufferchunk_to_java_bytebuffer", &convertBufferChunkToJavaByteBuffer); const std::vector compatibleByteBufferClasses = { "ByteBuffer", "CharBuffer", "ShortBuffer", "IntBuffer", "LongBuffer", "FloatBuffer", "DoubleBuffer" }; for(const std::string& byteBufferClass: compatibleByteBufferClasses) { const std::string lowerName = Poco::toLower(byteBufferClass); Pothos::PluginRegistry::add( "/proxy/converters/java/java_"+lowerName+"_to_bufferchunk", Pothos::ProxyConvertPair( "java.nio."+byteBufferClass, &convertJavaByteBufferToBufferChunk)); } }