From 587288a74e3e2d4a9c67c4cc138cf360e3f08ea8 Mon Sep 17 00:00:00 2001 From: Dmitry Nikolaev Date: Mon, 16 Mar 2026 12:59:30 +0100 Subject: [PATCH] Add big endian support This is a forward-port of the #601 PR to the `main` branch. Signed-off-by: Dmitry Nikolaev --- src/jni/refs.cpp | 16 +++++- src/jni/refs.hpp | 2 +- src/jni/util.cpp | 2 +- src/main/java/org/duckdb/DuckDBResultSet.java | 2 +- src/main/java/org/duckdb/DuckDBVector.java | 55 +++++++------------ 5 files changed, 37 insertions(+), 40 deletions(-) diff --git a/src/jni/refs.cpp b/src/jni/refs.cpp index f5a0a795e..1dc6cbafe 100644 --- a/src/jni/refs.cpp +++ b/src/jni/refs.cpp @@ -78,7 +78,7 @@ jmethodID J_DuckStruct_init; jclass J_ByteBuffer; jmethodID J_ByteBuffer_order; jclass J_ByteOrder; -jobject J_ByteOrder_LITTLE_ENDIAN; +jobject J_ByteOrder_NATIVE; jclass J_DuckMap; jmethodID J_DuckMap_getSQLTypeName; @@ -156,6 +156,18 @@ static jfieldID get_field_id(JNIEnv *env, jclass clazz, const std::string &name, return field_id; } +static jobject make_static_method_call_ref(JNIEnv *env, jclass clazz, const std::string &name, const std::string &sig) { + jmethodID method_id = get_static_method_id(env, clazz, name, sig); + jobject local_ref = env->CallStaticObjectMethod(clazz, method_id); + check_not_null(local_ref, "Static method returned null, name: [" + name + "], signature: [" + sig + "]"); + jobject global_ref = env->NewGlobalRef(local_ref); + check_not_null(global_ref, + "Cannot create global ref for static method result, name: [" + name + "], signature: [" + sig + "]"); + env->DeleteLocalRef(local_ref); + global_refs.emplace_back(global_ref); + return global_ref; +} + static jobject make_static_object_field_ref(JNIEnv *env, jclass clazz, const std::string &name, const std::string &sig) { jfieldID field_id = env->GetStaticFieldID(clazz, name.c_str(), sig.c_str()); @@ -278,7 +290,7 @@ void create_refs(JNIEnv *env) { J_ByteBuffer = make_class_ref(env, "java/nio/ByteBuffer"); J_ByteBuffer_order = get_method_id(env, J_ByteBuffer, "order", "(Ljava/nio/ByteOrder;)Ljava/nio/ByteBuffer;"); J_ByteOrder = make_class_ref(env, "java/nio/ByteOrder"); - J_ByteOrder_LITTLE_ENDIAN = make_static_object_field_ref(env, J_ByteOrder, "LITTLE_ENDIAN", "Ljava/nio/ByteOrder;"); + J_ByteOrder_NATIVE = make_static_method_call_ref(env, J_ByteOrder, "nativeOrder", "()Ljava/nio/ByteOrder;"); J_ProfilerPrintFormat = make_class_ref(env, "org/duckdb/ProfilerPrintFormat"); J_ProfilerPrintFormat_QUERY_TREE = diff --git a/src/jni/refs.hpp b/src/jni/refs.hpp index cd7b20121..cda859d33 100644 --- a/src/jni/refs.hpp +++ b/src/jni/refs.hpp @@ -75,7 +75,7 @@ extern jmethodID J_DuckStruct_init; extern jclass J_ByteBuffer; extern jmethodID J_ByteBuffer_order; extern jclass J_ByteOrder; -extern jobject J_ByteOrder_LITTLE_ENDIAN; +extern jobject J_ByteOrder_NATIVE; extern jclass J_DuckMap; extern jmethodID J_DuckMap_getSQLTypeName; diff --git a/src/jni/util.cpp b/src/jni/util.cpp index c22d03c65..8a8c34c30 100644 --- a/src/jni/util.cpp +++ b/src/jni/util.cpp @@ -144,7 +144,7 @@ jobject make_ptr_buf(JNIEnv *env, void *ptr) { jobject make_data_buf(JNIEnv *env, void *data, idx_t len) { if (data != nullptr) { jobject buf = env->NewDirectByteBuffer(data, uint64_to_jlong(len)); - env->CallObjectMethod(buf, J_ByteBuffer_order, J_ByteOrder_LITTLE_ENDIAN); + env->CallObjectMethod(buf, J_ByteBuffer_order, J_ByteOrder_NATIVE); return buf; } diff --git a/src/main/java/org/duckdb/DuckDBResultSet.java b/src/main/java/org/duckdb/DuckDBResultSet.java index 1d687c1ae..689126c35 100644 --- a/src/main/java/org/duckdb/DuckDBResultSet.java +++ b/src/main/java/org/duckdb/DuckDBResultSet.java @@ -465,7 +465,7 @@ public int read(byte[] bytes, int off, int len) throws IOException { public DuckDBBlobResult(ByteBuffer buffer_p) { buffer_p.position(0); - buffer_p.order(ByteOrder.LITTLE_ENDIAN); + buffer_p.order(ByteOrder.nativeOrder()); this.buffer = buffer_p; } diff --git a/src/main/java/org/duckdb/DuckDBVector.java b/src/main/java/org/duckdb/DuckDBVector.java index 82924d1ef..df8441b13 100644 --- a/src/main/java/org/duckdb/DuckDBVector.java +++ b/src/main/java/org/duckdb/DuckDBVector.java @@ -457,7 +457,7 @@ Boolean getBoolean(int idx) throws SQLException { protected ByteBuffer getbuf(int idx, int typeWidth) { ByteBuffer buf = constlen_data; - buf.order(ByteOrder.LITTLE_ENDIAN); + buf.order(ByteOrder.nativeOrder()); buf.position(idx * typeWidth); return buf; } @@ -505,9 +505,7 @@ short getUint8(int idx) throws SQLException { return 0; } if (isType(DuckDBColumnType.UTINYINT)) { - ByteBuffer buf = ByteBuffer.allocate(2); - getbuf(idx, 1).get(buf.array(), 1, 1); - return buf.getShort(); + return (short) Byte.toUnsignedInt(getbuf(idx, 1).get()); } throw new SQLFeatureNotSupportedException("getUint8"); } @@ -517,10 +515,7 @@ long getUint32(int idx) throws SQLException { return 0; } if (isType(DuckDBColumnType.UINTEGER)) { - ByteBuffer buf = ByteBuffer.allocate(8); - buf.order(ByteOrder.LITTLE_ENDIAN); - getbuf(idx, 4).get(buf.array(), 0, 4); - return buf.getLong(); + return Integer.toUnsignedLong(getbuf(idx, 4).getInt()); } throw new SQLFeatureNotSupportedException("getUint32"); } @@ -530,10 +525,7 @@ int getUint16(int idx) throws SQLException { return 0; } if (isType(DuckDBColumnType.USMALLINT)) { - ByteBuffer buf = ByteBuffer.allocate(4); - buf.order(ByteOrder.LITTLE_ENDIAN); - getbuf(idx, 2).get(buf.array(), 0, 2); - return buf.getInt(); + return Short.toUnsignedInt(getbuf(idx, 2).getShort()); } throw new SQLFeatureNotSupportedException("getUint16"); } @@ -543,13 +535,10 @@ BigInteger getUint64(int idx) throws SQLException { return BigInteger.ZERO; } if (isType(DuckDBColumnType.UBIGINT)) { - byte[] buf_res = new byte[16]; - byte[] buf = new byte[8]; - getbuf(idx, 8).get(buf); - for (int i = 0; i < 8; i++) { - buf_res[i + 8] = buf[7 - i]; - } - return new BigInteger(buf_res); + byte[] buf_res = new byte[8]; + long value = getbuf(idx, 8).getLong(); + ByteBuffer.wrap(buf_res).putLong(value); + return new BigInteger(1, buf_res); } throw new SQLFeatureNotSupportedException("getUint64"); } @@ -601,14 +590,12 @@ BigInteger getHugeint(int idx) throws SQLException { return BigInteger.ZERO; } if (isType(DuckDBColumnType.HUGEINT)) { - byte[] buf = new byte[16]; - getbuf(idx, 16).get(buf); - for (int i = 0; i < 8; i++) { - byte keep = buf[i]; - buf[i] = buf[15 - i]; - buf[15 - i] = keep; - } - return new BigInteger(buf); + byte[] buf_res = new byte[16]; + ByteBuffer buf = getbuf(idx, 16); + long lower = buf.getLong(); + long upper = buf.getLong(); + ByteBuffer.wrap(buf_res).putLong(upper).putLong(lower); + return new BigInteger(buf_res); } Object o = getObject(idx); return new BigInteger(o.toString()); @@ -619,14 +606,12 @@ BigInteger getUhugeint(int idx) throws SQLException { return BigInteger.ZERO; } if (isType(DuckDBColumnType.UHUGEINT)) { - byte[] buf = new byte[16]; - getbuf(idx, 16).get(buf); - for (int i = 0; i < 8; i++) { - byte keep = buf[i]; - buf[i] = buf[15 - i]; - buf[15 - i] = keep; - } - return new BigInteger(1, buf); + byte[] buf_res = new byte[16]; + ByteBuffer buf = getbuf(idx, 16); + long lower = buf.getLong(); + long upper = buf.getLong(); + ByteBuffer.wrap(buf_res).putLong(upper).putLong(lower); + return new BigInteger(1, buf_res); } Object o = getObject(idx); return new BigInteger(o.toString());