JNI:String
lastmod 2018-08-17created 2016-06-213 edits
A Java String can be used in JNI code as the jstring type. This jstring type cannot be used directly in C or C++ code. Trying to print a jstring with printf, the C output function, can crash the JVM. To use a jstring, you must first convert it to a C-string type using the string conversion functions provided by JNI.
When a string is passed from a Java program to the native environment, it is converted to UTF8 format. The following figure shows how UTF-8 encoding is structured in memory.
| |
If the high bit of a byte is 1, the byte is part of a multibyte character. This means the values 1 to 127 used in ASCII are used as is, and if every character of a jstring falls in this range, it can be used directly in C/C++ code without conversion.
Java does not use multibyte UTF-8 encodings larger than 3 bytes, and the NULL character (ASCII 0) is represented with 2 bytes. This means there can be no character whose bits are all 0. There is one more set of routines for handling strings, which use 2-byte encoded Unicode. If a program uses localized strings (??), they should always be handled as Unicode strings, because UTF-8 does not support internationalization.
There are 5 functions that handle UTF-8, and these functions have counterparts that can count the 2-byte Unicode encoding. There are also 2 functions that release strings (probably, hmm... something like free or delete). And there are 2 methods responsible for locking and releasing strings, for the synchronized feature used in Java.
Each function takes a pointer to the Java environment as its first argument. Java passes this argument automatically when it calls a native method, so it is easy to use.
The first function above, NewString, takes a sequence of jchar (that is, a run of jchar-type characters using 2 bytes each) and the character length as arguments. The second, UTF version simply takes a sequence of char (that is, byte). Each byte may be part of a multibyte character of 1, 2, or 3 bytes. The end of the string is marked by a 2-byte NULL character.
The two methods above take a jstring as an argument and return the number of characters in Unicode and UTF format.
const jchar *GetStringChars(jstring string, jboolean *isCopy);
const char *GetStringUTFChars(jstring string, jboolean *isCopy);
The two methods above return a pointer to the given jstring. They are frequently used to convert a jstring into a string that is easy to use in the native environment. The pointer returned by these functions remains valid until the matching version of the ReleaseStringChars function is called. The first version returns a pointer of type jchar, and the second, UTF version returns a pointer of type jbyte (1 byte.. the function's return type is char *). Passing JNI_TRUE as the isCopy argument makes a copy of the string and returns a pointer to it; passing NULL or JNI_FALSE returns a pointer to the original without copying.
The following ReleaseStringChars functions tell the VM that the native environment no longer uses the string. (Seems like a free concept, making it a garbage-collection target.. lol)
void ReleaseStringChars(jstring string, const jchar *chars);
void ReleaseStringUTFChars(jstring string, const char *utf);
After the function above is called, the string pointed to by the argument passed as the jstring is no longer valid.
The next two functions put the substring of the jstring argument, starting at start up to len-1, into buf.
void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf);
void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf);
The two functions above can throw StringIndexOutOfBoundsException.
The following GetStringCritical function returns a jchar * pointer for the given jstring. This function acts as a lock on strings used in a multithreaded environment.
If needed, the string can also be copied via the isCopy argument. When you are done using the string, you can call the ReleaseStringCritical function to release the lock.
The ReleaseStringCritical function releases the pointer obtained by calling the GetStringCritical function.
An example program using strings is attached.
// JNIStringFunctions.java
public class JNIStringFunctions
{
public native String replaceString(String srcStr, String strToReplce, String replaceStr);
static
{
System.loadLibrary("JNIStringFunctions");
}
public static void main(String[] args)
{
JNIStringFunctions test = new JNIStringFunctions();
String one = "manghon";
String temp = test.replaceString(one, "n", "Abc");
System.out.println( temp );
}
}
Part of JNIStringFunctions.cpp is shown below.
JNIEXPORT jstring JNICALL Java_JNIStringFunctions_replaceString(JNIEnv *env, jobject obj, jstring _srcString, jstring _strToReplace, jstring _replString)
{
const char *searchStr, *findStr, *replStr, *found;
jstring newString = NULL;
int index;
searchStr = env->GetStringChars(_srcString, NULL);
findStr = env->GetStringChars(_strToReplace, NULL);
replStr = env->GetStringChars(_replString, NULL);
found = strstr(searchStr, findStr);
if( found != NULL )
{
char *newStringTemp;
index = found - searchStr;
newStringTemp = new char[strlen(searchStr) + strlen(replStr) + 1];
strcpy(newStringTemp, searchStr);
newStringTemp[index] = 0;
strcat(newStringTemp, replStr);
strcat(newStringTemp, &searchStr[index+strlen(findStr)]);
newString = env->NewStringUTF( (const char*)newStringTemp);
delete[] newStringTemp;
}
env->ReleaseStringUTFChars(_srcString, searchStr);
env->ReleaseStringUTFChars(_strToReplace, findStr);
env->ReleaseStringUTFChars(_replString, replStr);
return (newString);
}