java_spaghetti/
string_chars.rs

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