java_oxide/
string_chars.rs

1use crate::Env;
2use jni_sys::*;
3use std::{char, iter, slice};
4
5/// Represents a JNI `GetStringChars` + `GetStringLength` query.
6/// It will call `ReleaseStringChars` automatically when dropped.
7pub struct StringChars<'env> {
8    env: Env<'env>,
9    string: jstring,
10    chars: *const jchar,
11    length: jsize, // in characters
12}
13
14impl<'env> StringChars<'env> {
15    /// Construct a `StringChars` from an [Env] + [jstring].
16    ///
17    /// # Safety
18    ///
19    /// The Java string object referenced by `string` must remain available before the created
20    /// `StringChars` is dropped. This should be true if the JNI reference `string` is not deleted.
21    ///
22    /// This function is supposed to be used in generated bindings.
23    pub unsafe fn from_env_jstring(env: Env<'env>, string: jstring) -> Self {
24        debug_assert!(!string.is_null());
25
26        let chars = unsafe { env.get_string_chars(string) };
27        let length = unsafe { env.get_string_length(string) };
28
29        Self {
30            env,
31            string,
32            chars,
33            length,
34        }
35    }
36
37    /// Get an array of [jchar]s.  Generally UTF16, but not guaranteed to be valid UTF16.
38    pub fn chars(&self) -> &[jchar] {
39        unsafe { slice::from_raw_parts(self.chars, self.length as usize) }
40    }
41
42    /// [std::char::decode_utf16]\(...\)s these string characters.
43    pub fn decode(&self) -> char::DecodeUtf16<iter::Cloned<slice::Iter<'_, u16>>> {
44        char::decode_utf16(self.chars().iter().cloned())
45    }
46
47    /// Returns a new [Ok]\([String]\), or an [Err]\([DecodeUtf16Error](char::DecodeUtf16Error)\) if if it contained any invalid UTF16.
48    pub fn to_string(&self) -> Result<String, char::DecodeUtf16Error> {
49        self.decode().collect()
50    }
51
52    /// Returns a new [String] with any invalid UTF16 characters replaced with [REPLACEMENT_CHARACTER](char::REPLACEMENT_CHARACTER)s (`'\u{FFFD}'`.)
53    pub fn to_string_lossy(&self) -> String {
54        self.decode()
55            .map(|r| r.unwrap_or(char::REPLACEMENT_CHARACTER))
56            .collect()
57    }
58}
59
60impl<'env> Drop for StringChars<'env> {
61    fn drop(&mut self) {
62        unsafe { self.env.release_string_chars(self.string, self.chars) };
63    }
64}