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 */
17 changes: 14 additions & 3 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 @@ -181,8 +182,12 @@ void JObj_dealloc(JPy_JObj* self)
if (array->buf != NULL) {
JArray_ReleaseJavaArrayElements(array, array->javaType);
}

}
} else if (jtype == JPy_JByteBuffer) {
JPy_JByteBufferWrapper* byteBufferWrapper;
byteBufferWrapper = (JPy_JByteBufferWrapper *) self;
PyBuffer_Release(byteBufferWrapper->pyBuffer);
PyMem_Free(byteBufferWrapper->pyBuffer);
}

jenv = JPy_GetJNIEnv();
if (jenv != NULL) {
Expand Down Expand Up @@ -727,7 +732,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
103 changes: 103 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 All @@ -42,6 +43,7 @@ void JType_InitMethodParamDescriptorFunctions(JPy_JType* type, JPy_JMethod* meth
int JType_ProcessField(JNIEnv* jenv, JPy_JType* declaringType, PyObject* fieldKey, const char* fieldName, jclass fieldClassRef, jboolean isStatic, jboolean isFinal, jfieldID fid);
void JType_DisposeLocalObjectRefArg(JNIEnv* jenv, jvalue* value, void* data);
void JType_DisposeReadOnlyBufferArg(JNIEnv* jenv, jvalue* value, void* data);
void JType_DisposeByteBufferArg(JNIEnv* jenv, jvalue* value, void* data);
void JType_DisposeWritableBufferArg(JNIEnv* jenv, jvalue* value, void* data);


Expand Down Expand Up @@ -1652,6 +1654,90 @@ 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;
}

if (PyObject_CheckBuffer(pyArg) == 1) {
return 100;
}

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 (PyObject_CheckBuffer(pyArg)) {
disposer->data = JType_CreateJavaByteBufferWrapper(jenv, pyArg);
disposer->DisposeArg = JType_DisposeByteBufferArg;
JPy_JObj* obj = (JPy_JObj*) disposer->data;
value->l = obj->objectRef;
} else 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 @@ -2368,6 +2454,20 @@ void JType_DisposeLocalObjectRefArg(JNIEnv* jenv, jvalue* value, void* data)
}
}

void JType_DisposeByteBufferArg(JNIEnv* jenv, jvalue* value, void* data)
{
PyObject* byteBufferWrapper = (PyObject*) data;
jobject jByteBuffer = (jobject) value->l;

JPy_DIAG_PRINT(JPy_DIAG_F_MEM, "JType_DisposeByteBufferArg: byteBufferWrapper=%p, jByteBuffer=%p\n", byteBufferWrapper, jByteBuffer);

if (jByteBuffer != NULL) {
JPy_DELETE_LOCAL_REF(jByteBuffer);
}

JPy_DECREF(byteBufferWrapper);
}

void JType_DisposeReadOnlyBufferArg(JNIEnv* jenv, jvalue* value, void* data)
{
Py_buffer* pyBuffer;
Expand Down Expand Up @@ -2451,6 +2551,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
45 changes: 44 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,9 @@ 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 referring to the Py_buffer provided by obj via its implemented Buffer Protocol. "},

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

Expand Down Expand Up @@ -126,7 +131,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 +233,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 +667,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 +959,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 +988,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 +1031,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 +1052,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 +1090,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 +1101,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 +1124,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