use std::{
borrow::{Borrow, Cow, ToOwned},
ffi::{CStr, CString},
os::raw::c_char,
};
use log::debug;
use simd_cesu8::mutf8;
#[cfg(doc)]
use crate::strings::MUTF8Chars;
#[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Hash, Clone)]
pub struct JNIString {
internal: CString,
}
impl PartialEq<&JNIStr> for JNIString {
#[inline]
fn eq(&self, other: &&JNIStr) -> bool {
self.internal.as_c_str() == &other.internal
}
}
impl From<&JNIStr> for JNIString {
fn from(other: &JNIStr) -> Self {
other.to_owned()
}
}
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(transparent)] pub struct JNIStr {
internal: CStr,
}
impl ::std::ops::Deref for JNIString {
type Target = JNIStr;
fn deref(&self) -> &Self::Target {
unsafe { JNIStr::from_ptr(self.internal.as_ptr()) }
}
}
impl PartialEq<JNIString> for &JNIStr {
#[inline]
fn eq(&self, other: &JNIString) -> bool {
self.internal.to_bytes() == other.internal.to_bytes()
}
}
impl<T> From<T> for JNIString
where
T: AsRef<str>,
{
fn from(other: T) -> Self {
let enc = mutf8::encode(other.as_ref()).into_owned();
JNIString {
internal: unsafe { CString::from_vec_unchecked(enc) },
}
}
}
impl From<JNIString> for CString {
fn from(string: JNIString) -> Self {
string.into_cstring()
}
}
impl<'str_ref> From<&'str_ref JNIStr> for Cow<'str_ref, str> {
fn from(other: &'str_ref JNIStr) -> Cow<'str_ref, str> {
let bytes = other.as_cstr().to_bytes();
match mutf8::decode(bytes) {
Ok(s) => s,
Err(e) => {
debug!("error decoding java cesu8: {:#?}", e);
String::from_utf8_lossy(bytes)
}
}
}
}
impl<'str_ref> From<&'str_ref JNIStr> for &'str_ref CStr {
fn from(value: &'str_ref JNIStr) -> Self {
&value.internal
}
}
impl<'str_ref> From<&'str_ref JNIString> for Cow<'str_ref, JNIStr> {
fn from(string: &'str_ref JNIString) -> Self {
Cow::Borrowed(string)
}
}
impl From<JNIString> for String {
fn from(other: JNIString) -> String {
other.to_str().into_owned()
}
}
impl std::fmt::Display for JNIStr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = self.to_str();
write!(f, "{}", s)
}
}
impl JNIString {
pub fn new(string: impl AsRef<str>) -> Self {
string.into()
}
pub const unsafe fn from_cstring(string: CString) -> Self {
Self { internal: string }
}
pub fn into_cstring(self) -> CString {
self.internal
}
pub fn borrowed(&self) -> &JNIStr {
self
}
}
const fn is_valid_mutf8_cstr(cstr: &CStr) -> bool {
let bytes = cstr.to_bytes();
let mut i = 0;
while i < bytes.len() {
let b0 = bytes[i];
if b0 < 0x80 {
i += 1;
continue;
}
if b0 == 0xC0 {
if i + 1 >= bytes.len() {
return false;
}
let b1 = bytes[i + 1];
if b1 != 0x80 {
return false;
}
i += 2;
continue;
}
if b0 >= 0xC2 && b0 <= 0xDF {
if i + 1 >= bytes.len() {
return false;
}
let b1 = bytes[i + 1];
if (b1 & 0xC0) != 0x80 {
return false;
}
i += 2;
continue;
}
if b0 >= 0xE0 && b0 <= 0xEF {
if i + 2 >= bytes.len() {
return false;
}
let b1 = bytes[i + 1];
let b2 = bytes[i + 2];
if b0 == 0xE0 {
if !(b1 >= 0xA0 && b1 <= 0xBF) || (b2 & 0xC0) != 0x80 {
return false;
}
} else {
if (b1 & 0xC0) != 0x80 || (b2 & 0xC0) != 0x80 {
return false;
}
}
i += 3;
continue;
}
return false;
}
true
}
impl JNIStr {
pub unsafe fn from_ptr<'a>(ptr: *const c_char) -> &'a JNIStr {
unsafe { &*(CStr::from_ptr(ptr) as *const CStr as *const JNIStr) }
}
pub const fn as_ptr(&self) -> *const c_char {
self.as_cstr().as_ptr()
}
pub const unsafe fn from_cstr_unchecked(cstr: &CStr) -> &JNIStr {
unsafe { &*(cstr as *const CStr as *const JNIStr) }
}
pub const fn from_cstr(cstr: &CStr) -> Option<&JNIStr> {
if !is_valid_mutf8_cstr(cstr) {
return None;
}
unsafe { Some(Self::from_cstr_unchecked(cstr)) }
}
pub const fn as_cstr(&self) -> &CStr {
&self.internal
}
pub fn to_str(&'_ self) -> Cow<'_, str> {
self.into()
}
pub fn to_bytes(&self) -> &[u8] {
self.as_cstr().to_bytes()
}
}
impl Borrow<JNIStr> for JNIString {
fn borrow(&self) -> &JNIStr {
self
}
}
impl ToOwned for JNIStr {
type Owned = JNIString;
fn to_owned(&self) -> JNIString {
JNIString {
internal: CString::from(self.as_cstr()),
}
}
}
impl AsRef<JNIStr> for JNIStr {
fn as_ref(&self) -> &JNIStr {
self
}
}
impl AsRef<JNIStr> for JNIString {
fn as_ref(&self) -> &JNIStr {
self
}
}