diff options
| author | Joel Klinghed <the_jk@spawned.biz> | 2024-09-25 21:12:24 +0200 |
|---|---|---|
| committer | Joel Klinghed <the_jk@spawned.biz> | 2024-09-25 21:12:24 +0200 |
| commit | 28a55fdc69e31490a4086ecae8cc687f40ba0b94 (patch) | |
| tree | 9bde6e49eb091f912e8a9f8b2853d87f6a932d27 /libs/samba/src/main/cpp/jni.cpp | |
| parent | 07d35782b377a8b98cf8dbbb5734d3f2514bccd5 (diff) | |
Add libs:sftp
sftp implementation using libssh2 and openssl
Diffstat (limited to 'libs/samba/src/main/cpp/jni.cpp')
| -rw-r--r-- | libs/samba/src/main/cpp/jni.cpp | 134 |
1 files changed, 133 insertions, 1 deletions
diff --git a/libs/samba/src/main/cpp/jni.cpp b/libs/samba/src/main/cpp/jni.cpp index 5a69dc5..b7bbd1a 100644 --- a/libs/samba/src/main/cpp/jni.cpp +++ b/libs/samba/src/main/cpp/jni.cpp @@ -1,5 +1,8 @@ #include "jni.hpp" +#include <algorithm> +#include <optional> + #ifdef ANDROID #include <android/log.h> #else @@ -31,6 +34,103 @@ const char *_jni_error(jint err) { } } +const char* u8_read(const char* str, uint32_t& out) { + // Assume valid UTF-8 for speed and so it can be used to read modified utf-8 as well + switch (*str >> 4) { + case 0xf: // 4 byte + out = (static_cast<uint32_t>(str[0] & 0x7) << 18) | + (static_cast<uint32_t>(str[1] & 0x3f) << 12) | + (static_cast<uint32_t>(str[2] & 0x3f) << 6) | + static_cast<uint8_t>(str[3] & 0x3f); + return str + 4; + case 0xe: // 3 byte + out = (static_cast<uint32_t>(str[0] & 0xf) << 12) | + (static_cast<uint32_t>(str[1] & 0x3f) << 6) | + static_cast<uint8_t>(str[2] & 0x3f); + return str + 3; + case 0xd: + case 0xc: // 2 byte + out = (static_cast<uint32_t>(str[0] & 0x1f) << 6) | + static_cast<uint8_t>(str[1] & 0x3f); + return str + 2; + default: // 1 byte + out = static_cast<uint8_t>(str[0]); + return str + 1; + } +} + +void u8_write(std::string& ret, uint32_t c) { + if (c < 0x80) { + ret.push_back(static_cast<char>(c)); + } else if (c < 0x800) { + ret.push_back(static_cast<char>(0xc0 | (c >> 6))); + ret.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else if (c < 0x10000) { + ret.push_back(static_cast<char>(0xe0 | (c >> 12))); + ret.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3f))); + ret.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } else { + ret.push_back(static_cast<char>(0xf0 | (c >> 18))); + ret.push_back(static_cast<char>(0x80 | ((c >> 12) & 0x3f))); + ret.push_back(static_cast<char>(0x80 | ((c >> 6) & 0x3f))); + ret.push_back(static_cast<char>(0x80 | (c & 0x3f))); + } +} + +std::string MakeModifiedUTF8(const char* str, const char* ptr, uint32_t c) { + std::string ret; + while (true) { + ret.append(str, ptr - str); + str = ptr; + u8_write(ret, 0xd800 + ((c & 0xffff) >> 10)); + u8_write(ret, 0xdc00 + (c & 0x3ff)); + if (!*ptr) break; + do { + ptr = u8_read(ptr, c); + if (c > 0xffff) + break; + } while (*ptr); + } + return ret; +} + +std::optional<std::string> MakeModifiedUTF8IfNeeded(const char* str) { + auto* ptr = str; + while (*ptr) { + uint32_t c; + ptr = u8_read(ptr, c); + if (c > 0xffff) { + return MakeModifiedUTF8(str, ptr, c); + } + } + return std::nullopt; +} + +const char* splice(std::string& str, const char* start, const char* end, const char* insert, size_t size) { + auto pos = start - str.c_str(); + str.replace(pos, end - start, insert, size); + return str.c_str() + pos + size; +} + +void UnmodifyUTF8(std::string& str) { + auto* ptr = str.c_str(); + while (*ptr) { + uint32_t u; + auto* next = u8_read(ptr, u); + if (u == 0) { + next = splice(str, ptr, next, "\0", 1); + } else if (u >= 0xd800 && u <= 0xdfff) { + uint32_t v; + next = u8_read(next, v); + u = 0x10000 | ((u - 0xd800) << 10) | (v - 0xdc00); + std::string tmp; + u8_write(tmp, u); + next = splice(str, ptr, next, tmp.data(), tmp.size()); + } + ptr = next; + } +} + } // namespace namespace jni { @@ -132,14 +232,46 @@ std::string StringToUTF8(JNIEnv* env, const Ref<jstring>& str) { auto len = env->GetStringUTFLength(str.get()); std::string ret(len, ' '); env->GetStringUTFRegion(str.get(), 0, len, ret.data()); - // This returns modified UTF-8 encoding, don't care. + UnmodifyUTF8(ret); return ret; } LocalRef<jstring> UTF8ToString(JNIEnv* env, const std::string& str) { + auto ret = MakeModifiedUTF8IfNeeded(str.c_str()); + if (ret.has_value()) { + return {env, env->NewStringUTF(ret->c_str())}; + } return {env, env->NewStringUTF(str.c_str())}; } +LocalRef<jstring> UTF8ToString(JNIEnv* env, const char* str) { + if (str == nullptr) return nullptr; + auto ret = MakeModifiedUTF8IfNeeded(str); + if (ret.has_value()) { + return {env, env->NewStringUTF(ret->c_str())}; + } + return {env, env->NewStringUTF(str)}; +} + +LocalRef<jbyteArray> VectorToByteArray(JNIEnv* env, const std::vector<uint8_t>& data) { + auto len = static_cast<jsize>(data.size()); + auto ret = LocalRef<jbyteArray>(env, env->NewByteArray(len)); + ABORT_IF_NULL(env, ret); + auto* ptr = reinterpret_cast<jbyte*>(env->GetPrimitiveArrayCritical(ret.get(), nullptr)); + std::copy_n(data.data(), data.size(), ptr); + env->ReleasePrimitiveArrayCritical(ret.get(), ptr, JNI_COMMIT); + return ret; +} + +std::vector<uint8_t> ByteArrayToVector(JNIEnv* env, const Ref<jbyteArray>& data) { + if (!data) return {}; + auto len = env->GetArrayLength(data.get()); + std::vector<uint8_t> ret(len); + static_assert(sizeof(jbyte) == sizeof(uint8_t)); + env->GetByteArrayRegion(data.get(), 0, len, reinterpret_cast<jbyte*>(ret.data())); + return ret; +} + LocalRef<jobjectArray> CreateArray(JNIEnv* env, const Ref<jclass>& element_class, std::vector<LocalRef<jobject>> objects) { auto ret = LocalRef<jobjectArray>(env, env->NewObjectArray(static_cast<jsize>(objects.size()), element_class.get(), nullptr)); ABORT_IF_NULL(env, ret); |
