Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add jpy.byte_buffer() function #112

Merged
1 change: 0 additions & 1 deletion src/main/c/jpy_conv.c
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,3 @@ int JPy_AsJString(JNIEnv* jenv, PyObject* arg, jstring* stringRef)

return 0;
}

24 changes: 24 additions & 0 deletions src/main/c/jpy_jbyte_buffer.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2023 JPY-CONSORTIUM Ltd.
*
* 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 "jpy_module.h"
#include "jpy_diag.h"
#include "jpy_jarray.h"
#include "jpy_jbyte_buffer.h"

/*
* This file for now is just a place-holder for future JPy_ByteBufferWrapper specific functions.
*/
44 changes: 44 additions & 0 deletions src/main/c/jpy_jbyte_buffer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* Copyright 2023 JPY-CONSORTIUM Ltd.
*
* 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.
*/

#ifndef JPY_JBYTE_BUFFER_H
#define JPY_JBYTE_BUFFER_H

#ifdef __cplusplus
extern "C" {
#endif

#include "jpy_compat.h"

/**
* The Java ByteBuffer representation in Python.
*
* IMPORTANT: JPy_JByteBufferWrapper must only differ from the JPy_JObj structure by the 'pyBuffer' member
* since we use the same basic type, name JPy_JType for it. DON'T ever change member positions!
* @see JPy_JObj
*/
typedef struct JPy_JByteBufferWrapper
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
{
PyObject_HEAD
jobject objectRef;
Py_buffer *pyBuffer;
}
JPy_JByteBufferWrapper;

#ifdef __cplusplus
} /* extern "C" */
#endif
#endif /* !JPY_JBYTE_BUFFER_H */
27 changes: 22 additions & 5 deletions src/main/c/jpy_jobj.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "jpy_module.h"
#include "jpy_diag.h"
#include "jpy_jarray.h"
#include "jpy_jbyte_buffer.h"
#include "jpy_jtype.h"
#include "jpy_jobj.h"
#include "jpy_jmethod.h"
Expand Down Expand Up @@ -63,9 +64,13 @@ PyObject* JObj_FromType(JNIEnv* jenv, JPy_JType* type, jobject objectRef)
array = (JPy_JArray*) obj;
array->bufferExportCount = 0;
array->buf = NULL;
} else if ((*jenv)->IsInstanceOf(jenv, objectRef, JPy_ByteBuffer_JClass)) {
JPy_JByteBufferWrapper *byteBufferWrapper = (JPy_JByteBufferWrapper *) obj;
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
byteBufferWrapper->pyBuffer = NULL;
}

// we check the type translations dictionary for a callable for this java type name,

// we check the type translations dictionary for a callable for this java type name,
// and apply the returned callable to the wrapped object
callable = PyDict_GetItemString(JPy_Type_Translations, type->javaName);
if (callable != NULL) {
Expand Down Expand Up @@ -171,6 +176,8 @@ void JObj_dealloc(JPy_JObj* self)
JNIEnv* jenv;
JPy_JType* jtype;

jenv = JPy_GetJNIEnv();

JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JObj_dealloc: releasing instance of %s, self->objectRef=%p\n", Py_TYPE(self)->tp_name, self->objectRef);

jtype = (JPy_JType *)Py_TYPE(self);
Expand All @@ -181,10 +188,14 @@ void JObj_dealloc(JPy_JObj* self)
if (array->buf != NULL) {
JArray_ReleaseJavaArrayElements(array, array->javaType);
}

}
} else if ((*jenv)->IsInstanceOf(jenv, self->objectRef, JPy_ByteBuffer_JClass)) {
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
JPy_JByteBufferWrapper *byteBufferWrapper = (JPy_JByteBufferWrapper *) self;
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
if (byteBufferWrapper->pyBuffer != NULL) {
PyBuffer_Release(byteBufferWrapper->pyBuffer);
PyMem_Free(byteBufferWrapper->pyBuffer);
}
}

jenv = JPy_GetJNIEnv();
if (jenv != NULL) {
if (self->objectRef != NULL) {
(*jenv)->DeleteGlobalRef(jenv, self->objectRef);
Expand Down Expand Up @@ -727,7 +738,13 @@ int JType_InitSlots(JPy_JType* type)
//Py_SET_TYPE(type, &JType_Type);
//Py_SET_SIZE(type, sizeof (JPy_JType));

typeObj->tp_basicsize = isPrimitiveArray ? sizeof (JPy_JArray) : sizeof (JPy_JObj);
if (isPrimitiveArray) {
typeObj->tp_basicsize = sizeof(JPy_JArray);
} else if (type == JPy_JByteBuffer) {
typeObj->tp_basicsize = sizeof(JPy_JByteBufferWrapper);
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
} else {
typeObj->tp_basicsize = sizeof(JPy_JObj);
}
typeObj->tp_itemsize = 0;
typeObj->tp_base = type->superType != NULL ? JTYPE_AS_PYTYPE(type->superType) : &JType_Type;
//typeObj->tp_base = (PyTypeObject*) type->superType;
Expand Down
79 changes: 79 additions & 0 deletions src/main/c/jpy_jtype.c
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "jpy_jfield.h"
#include "jpy_jmethod.h"
#include "jpy_jobj.h"
#include "jpy_jbyte_buffer.h"
#include "jpy_conv.h"
#include "jpy_compat.h"

Expand Down Expand Up @@ -1652,6 +1653,81 @@ int JType_ConvertPyArgToJStringArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescr
return JPy_AsJString(jenv, pyArg, &value->l);
}

int JType_MatchPyArgAsJByteBufferParam(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg)
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
{
if (pyArg == Py_None) {
// Signal it is possible, but give low priority since we cannot perform any type checks on 'None'
return 1;
}

return JType_MatchPyArgAsJObject(jenv, paramDescriptor->type, pyArg);
}

PyObject* JType_CreateJavaByteBufferWrapper(JNIEnv* jenv, PyObject* pyObj)
{
jobject byteBufferRef, tmpByteBufferRef;
Py_buffer *pyBuffer;

pyBuffer = (Py_buffer *)PyMem_Malloc(sizeof(Py_buffer));
if (pyBuffer == NULL) {
PyErr_NoMemory();
return NULL;
}

if (PyObject_GetBuffer(pyObj, pyBuffer, PyBUF_SIMPLE | PyBUF_C_CONTIGUOUS) != 0) {
PyErr_SetString(PyExc_ValueError, "JType_CreateJavaByteBufferWrapper: the Python object failed to return a contiguous buffer.");
PyMem_Free(pyBuffer);
return NULL;
}

tmpByteBufferRef = (*jenv)->NewDirectByteBuffer(jenv, pyBuffer->buf, pyBuffer->len);
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
if (tmpByteBufferRef == NULL) {
PyBuffer_Release(pyBuffer);
PyMem_Free(pyBuffer);
PyErr_NoMemory();
return NULL;
}

byteBufferRef = (*jenv)->CallObjectMethod(jenv, tmpByteBufferRef, JPy_ByteBuffer_AsReadOnlyBuffer_MID);
if (byteBufferRef == NULL) {
PyBuffer_Release(pyBuffer);
PyMem_Free(pyBuffer);
JPy_DELETE_LOCAL_REF(tmpByteBufferRef);
PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: failed to create a read-only ByteBuffer instance.");
return NULL;
}
JPy_DELETE_LOCAL_REF(tmpByteBufferRef);

PyObject *newPyObj = JObj_New(jenv, byteBufferRef);
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
if (newPyObj == NULL) {
PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: failed to create a ByteBufferWrapper instance.");
PyBuffer_Release(pyBuffer);
PyMem_Free(pyBuffer);
JPy_DELETE_LOCAL_REF(byteBufferRef);
return NULL;
}
JPy_DELETE_LOCAL_REF(byteBufferRef);

JPy_JByteBufferWrapper* byteBufferWrapper = (JPy_JByteBufferWrapper *) newPyObj;
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
byteBufferWrapper->pyBuffer = pyBuffer;
return (PyObject *)byteBufferWrapper;
}

int JType_ConvertPyArgToJByteBufferArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer)
{
if (JObj_Check(pyArg)) {
disposer->data = NULL;
// If it is a wrapped Java object, it is always a global reference, so don't dispose it
disposer->DisposeArg = NULL;
JPy_JObj* obj = (JPy_JObj*) pyArg;
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
value->l = obj->objectRef;
} else {
PyErr_SetString(PyExc_RuntimeError, "jpy: internal error: Python argument incorrectly matched to Java ByteBuffer parameter.");
return -1;
}
return 0;
}

int JType_ConvertPyArgToJPyObjectArg(JNIEnv* jenv, JPy_ParamDescriptor* paramDescriptor, PyObject* pyArg, jvalue* value, JPy_ArgDisposer* disposer)
{
disposer->data = NULL;
Expand Down Expand Up @@ -2451,6 +2527,9 @@ void JType_InitParamDescriptorFunctions(JPy_ParamDescriptor* paramDescriptor, jb
} else if (paramType == JPy_JString) {
paramDescriptor->MatchPyArg = JType_MatchPyArgAsJStringParam;
paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJStringArg;
} else if (paramType == JPy_JByteBuffer) {
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
paramDescriptor->MatchPyArg = JType_MatchPyArgAsJByteBufferParam;
paramDescriptor->ConvertPyArg = JType_ConvertPyArgToJByteBufferArg;
//} else if (paramType == JPy_JMap) {
//} else if (paramType == JPy_JList) {
//} else if (paramType == JPy_JSet) {
Expand Down
2 changes: 2 additions & 0 deletions src/main/c/jpy_jtype.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ int JType_CreateJavaPyObject(JNIEnv* jenv, JPy_JType* type, PyObject* pyArg, job

int JType_CreateJavaArray(JNIEnv* jenv, JPy_JType* componentType, PyObject* pyArg, jobject* objectRef, jboolean allowObjectWrapping);

PyObject* JType_CreateJavaByteBufferWrapper(JNIEnv* jenv, PyObject* pyObj);

// Non-API. Defined in jpy_jobj.c
int JType_InitSlots(JPy_JType* type);
// Non-API. Defined in jpy_jtype.c
Expand Down
46 changes: 45 additions & 1 deletion src/main/c/jpy_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
#include "jpy_jobj.h"
#include "jpy_conv.h"
#include "jpy_compat.h"
#include "jpy_jbyte_buffer.h"


#include <stdlib.h>
Expand All @@ -38,6 +39,7 @@ PyObject* JPy_destroy_jvm(PyObject* self, PyObject* args);
PyObject* JPy_get_type(PyObject* self, PyObject* args, PyObject* kwds);
PyObject* JPy_cast(PyObject* self, PyObject* args);
PyObject* JPy_array(PyObject* self, PyObject* args);
PyObject* JPy_byte_buffer(PyObject* self, PyObject* args);


static PyMethodDef JPy_Functions[] = {
Expand All @@ -63,6 +65,10 @@ static PyMethodDef JPy_Functions[] = {
"array(name, init) - Return a new Java array of given Java type (type name or type object) and initializer (array length or sequence). "
"Possible primitive types are 'boolean', 'byte', 'char', 'short', 'int', 'long', 'float', and 'double'."},

{"byte_buffer", JPy_byte_buffer, METH_VARARGS,
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
"byte_buffer(obj) - Return a new Java direct ByteBuffer sharing the same underlying buffer of obj via its implemented Buffer Protocol. The Java direct ByteBuffer is read-only"
"and is only safe to access in Java (e.g. as an argument to a Java method) when it is also referenced in Python."},

{NULL, NULL, 0, NULL} /*Sentinel*/
};

Expand Down Expand Up @@ -126,7 +132,7 @@ JPy_JType* JPy_JPyObject = NULL;
JPy_JType* JPy_JPyModule = NULL;
JPy_JType* JPy_JThrowable = NULL;
JPy_JType* JPy_JStackTraceElement = NULL;

JPy_JType* JPy_JByteBuffer = NULL;

// java.lang.Comparable
jclass JPy_Comparable_JClass = NULL;
Expand Down Expand Up @@ -228,6 +234,8 @@ jclass JPy_Void_JClass = NULL;
jclass JPy_String_JClass = NULL;
jclass JPy_PyObject_JClass = NULL;
jclass JPy_PyDictWrapper_JClass = NULL;
jclass JPy_ByteBuffer_JClass = NULL;
jmethodID JPy_ByteBuffer_AsReadOnlyBuffer_MID = NULL;

jmethodID JPy_PyObject_GetPointer_MID = NULL;
jmethodID JPy_PyObject_UnwrapProxy_SMID = NULL;
Expand Down Expand Up @@ -660,6 +668,31 @@ PyObject* JPy_array(PyObject* self, PyObject* args)
JPy_FRAME(PyObject*, NULL, JPy_array_internal(jenv, self, args), 16)
}

PyObject* JPy_byte_buffer_internal(JNIEnv* jenv, PyObject* self, PyObject* args)
{
jobject byteBufferRef;
PyObject* pyObj;
PyObject* newPyObj;
Py_buffer *pyBuffer;
JPy_JByteBufferWrapper* byteBufferWrapper;

if (!PyArg_ParseTuple(args, "O:byte_buffer", &pyObj)) {
return NULL;
}

if (PyObject_CheckBuffer(pyObj) == 0) {
PyErr_SetString(PyExc_ValueError, "byte_buffer: argument 1 must be a Python object that supports the buffer protocol.");
return NULL;
jmao-denver marked this conversation as resolved.
Show resolved Hide resolved
}

return JType_CreateJavaByteBufferWrapper(jenv, pyObj);
}

PyObject* JPy_byte_buffer(PyObject* self, PyObject* args)
{
JPy_FRAME(PyObject*, NULL, JPy_byte_buffer_internal(jenv, self, args), 16)
}

JPy_JType* JPy_GetNonObjectJType(JNIEnv* jenv, jclass classRef)
{
jclass primClassRef;
Expand Down Expand Up @@ -927,6 +960,9 @@ int JPy_InitGlobalVars(JNIEnv* jenv)
DEFINE_CLASS(JPy_String_JClass, "java/lang/String");
DEFINE_CLASS(JPy_Throwable_JClass, "java/lang/Throwable");
DEFINE_CLASS(JPy_StackTraceElement_JClass, "java/lang/StackTraceElement");
DEFINE_CLASS(JPy_ByteBuffer_JClass, "java/nio/ByteBuffer");
DEFINE_METHOD(JPy_ByteBuffer_AsReadOnlyBuffer_MID, JPy_ByteBuffer_JClass, "asReadOnlyBuffer", "()Ljava/nio/ByteBuffer;");


// Non-Object types: Primitive types and void.
DEFINE_NON_OBJECT_TYPE(JPy_JBoolean, JPy_Boolean_JClass);
Expand All @@ -953,6 +989,8 @@ int JPy_InitGlobalVars(JNIEnv* jenv)
DEFINE_OBJECT_TYPE(JPy_JDoubleObj, JPy_Double_JClass);
// Other objects.
DEFINE_OBJECT_TYPE(JPy_JString, JPy_String_JClass);
DEFINE_OBJECT_TYPE(JPy_JByteBuffer, JPy_ByteBuffer_JClass);

DEFINE_OBJECT_TYPE(JPy_JThrowable, JPy_Throwable_JClass);
DEFINE_OBJECT_TYPE(JPy_JStackTraceElement, JPy_StackTraceElement_JClass);
DEFINE_METHOD(JPy_Throwable_getCause_MID, JPy_Throwable_JClass, "getCause", "()Ljava/lang/Throwable;");
Expand Down Expand Up @@ -994,6 +1032,7 @@ void JPy_ClearGlobalVars(JNIEnv* jenv)
(*jenv)->DeleteGlobalRef(jenv, JPy_Number_JClass);
(*jenv)->DeleteGlobalRef(jenv, JPy_Void_JClass);
(*jenv)->DeleteGlobalRef(jenv, JPy_String_JClass);
(*jenv)->DeleteGlobalRef(jenv, JPy_ByteBuffer_JClass);
}

JPy_Comparable_JClass = NULL;
Expand All @@ -1014,6 +1053,7 @@ void JPy_ClearGlobalVars(JNIEnv* jenv)
JPy_Number_JClass = NULL;
JPy_Void_JClass = NULL;
JPy_String_JClass = NULL;
JPy_ByteBuffer_JClass = NULL;

JPy_Object_ToString_MID = NULL;
JPy_Object_HashCode_MID = NULL;
Expand Down Expand Up @@ -1051,6 +1091,7 @@ void JPy_ClearGlobalVars(JNIEnv* jenv)
JPy_Number_DoubleValue_MID = NULL;
JPy_PyObject_GetPointer_MID = NULL;
JPy_PyObject_UnwrapProxy_SMID = NULL;
JPy_ByteBuffer_AsReadOnlyBuffer_MID = NULL;

JPy_XDECREF(JPy_JBoolean);
JPy_XDECREF(JPy_JChar);
Expand All @@ -1061,6 +1102,8 @@ void JPy_ClearGlobalVars(JNIEnv* jenv)
JPy_XDECREF(JPy_JFloat);
JPy_XDECREF(JPy_JDouble);
JPy_XDECREF(JPy_JVoid);
JPy_XDECREF(JPy_JString);
JPy_XDECREF(JPy_JByteBuffer);
JPy_XDECREF(JPy_JBooleanObj);
JPy_XDECREF(JPy_JCharacterObj);
JPy_XDECREF(JPy_JByteObj);
Expand All @@ -1082,6 +1125,7 @@ void JPy_ClearGlobalVars(JNIEnv* jenv)
JPy_JDouble = NULL;
JPy_JVoid = NULL;
JPy_JString = NULL;
JPy_JByteBuffer = NULL;
JPy_JBooleanObj = NULL;
JPy_JCharacterObj = NULL;
JPy_JByteObj = NULL;
Expand Down
Loading
Loading