/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Eclipse Public License (EPL);
* You may not use this file except in compliance with the License. You
* may obtain a copy of the License at
*
* http://www.opensource.org/licenses/eclipse-1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.jni;
import org.jikesrvm.VM;
import static org.jikesrvm.SizeConstants.BYTES_IN_ADDRESS;
import org.jikesrvm.classloader.UTF8Convert;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.runtime.Memory;
import org.jikesrvm.util.StringUtilities;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.Word;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.nio.charset.CodingErrorAction;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
/**
* Platform independent utility functions called from JNIFunctions
* (cannot be placed in JNIFunctions because methods
* there are specially compiled to be called from native).
*
* @see JNIFunctions
*/
public abstract class JNIGenericHelpers {
/**
* Compute the length of the given null-terminated string
*
* @param ptr address of string in memory
* @return the length of the string in bytes
*/
public static int strlen(Address ptr) {
int length=0;
// align address to size of machine
while (!ptr.toWord().and(Word.fromIntZeroExtend(BYTES_IN_ADDRESS - 1)).isZero()) {
byte bits = ptr.loadByte(Offset.fromIntZeroExtend(length));
if (bits == 0) {
return length;
}
length++;
}
// Ascii characters are normally in the range 1 to 128, if we subtract 1
// from each byte and look if the top bit of the byte is set then if it is
// the chances are the byte's value is 0. Loop over words doing this quick
// test and then do byte by byte tests when we think we have the 0
Word onesToSubtract;
Word maskToTestHighBits;
if (VM.BuildFor32Addr) {
onesToSubtract = Word.fromIntZeroExtend(0x01010101);
maskToTestHighBits = Word.fromIntZeroExtend(0x80808080);
} else {
onesToSubtract = Word.fromLong(0x0101010101010101L);
maskToTestHighBits = Word.fromLong(0x8080808080808080L);
}
while (true) {
Word bytes = ptr.loadWord(Offset.fromIntZeroExtend(length));
if(!bytes.minus(onesToSubtract).and(maskToTestHighBits).isZero()) {
if (VM.LittleEndian) {
for(int byteOff=0; byteOff < BYTES_IN_ADDRESS; byteOff++) {
if(bytes.and(Word.fromIntZeroExtend(0xFF)).isZero()) {
return length + byteOff;
}
bytes = bytes.rshl(8);
}
} else {
for(int byteOff=BYTES_IN_ADDRESS-1; byteOff >= 0; byteOff--) {
if(bytes.rshl(byteOff*8).and(Word.fromIntZeroExtend(0xFF)).isZero()) {
return length + (BYTES_IN_ADDRESS - 1 - byteOff);
}
}
}
}
length += BYTES_IN_ADDRESS;
}
}
/**
* Given an address in C that points to a null-terminated string,
* create a new Java byte[] with a copy of the string.
*
* @param stringAddress an address in C space for a string
* @return a new Java byte[]
*/
public static byte[] createByteArrayFromC(Address stringAddress) {
int length = strlen(stringAddress);
byte[] contents = new byte[length];
Memory.memcopy(Magic.objectAsAddress(contents), stringAddress, length);
return contents;
}
/**
* Create a string from the given charset decoder and bytebuffer
*/
private static String createString(CharsetDecoder csd, ByteBuffer bbuf) throws CharacterCodingException {
char[] v;
int o;
int c;
CharBuffer cbuf = csd.decode(bbuf);
if(cbuf.hasArray()) {
v = cbuf.array();
o = cbuf.position();
c = cbuf.remaining();
} else {
// Doubt this will happen. But just in case.
v = new char[cbuf.remaining()];
cbuf.get(v);
o = 0;
c = v.length;
}
return java.lang.JikesRVMSupport.newStringWithoutCopy(v, o, c);
}
/**
* Given an address in C that points to a null-terminated string,
* create a new Java String with a copy of the string.
*
* @param stringAddress an address in C space for a string
* @return a new Java String
*/
public static String createStringFromC(Address stringAddress) {
if (VM.fullyBooted) {
try {
String encoding = System.getProperty("file.encoding");
CharsetDecoder csd = Charset.forName(encoding).newDecoder();
csd.onMalformedInput(CodingErrorAction.REPLACE);
csd.onUnmappableCharacter(CodingErrorAction.REPLACE);
ByteBuffer bbuf =
java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress,
strlen(stringAddress));
return createString(csd, bbuf);
} catch(Exception ex){
// Any problems fall through to default encoding
}
}
// Can't do real Char encoding until VM is fully booted.
// All Strings encountered during booting must be ascii
byte[] tmp = createByteArrayFromC(stringAddress);
return StringUtilities.asciiBytesToString(tmp);
}
/**
* Given an address in C that points to a null-terminated string,
* create a new UTF encoded Java String with a copy of the string.
*
* @param stringAddress an address in C space for a string
* @return a new Java String
*/
public static String createUTFStringFromC(Address stringAddress) {
final boolean USE_LIBRARY_CODEC = false;
byte[] tmp;
ByteBuffer bbuf;
if (VM.fullyBooted) {
try {
bbuf = java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress,
strlen(stringAddress));
if (USE_LIBRARY_CODEC) {
CharsetDecoder csd = Charset.forName("UTF8").newDecoder();
return createString(csd, bbuf);
} else {
return UTF8Convert.fromUTF8(bbuf);
}
} catch(Exception ex){
// Any problems fall through to default encoding
}
}
// Can't do real Char encoding until VM is fully booted.
// All Strings encountered during booting must be ascii
tmp = createByteArrayFromC(stringAddress);
return StringUtilities.asciiBytesToString(tmp);
}
/**
* Convert a String into a a malloced region
*/
public static void createUTFForCFromString(String str, Address copyBuffer, int len) {
ByteBuffer bbuf =
java.nio.JikesRVMSupport.newDirectByteBuffer(copyBuffer, len);
final boolean USE_LIBRARY_CODEC = false;
if (USE_LIBRARY_CODEC) {
char[] strChars = java.lang.JikesRVMSupport.getBackingCharArray(str);
int strOffset = java.lang.JikesRVMSupport.getStringOffset(str);
int strLen = java.lang.JikesRVMSupport.getStringLength(str);
CharBuffer cbuf = CharBuffer.wrap(strChars, strOffset, strLen);
CharsetEncoder cse = Charset.forName("UTF8").newEncoder();
cse.encode(cbuf, bbuf, true);
} else {
UTF8Convert.toUTF8(str, bbuf);
}
// store terminating zero
copyBuffer.store((byte)0, Offset.fromIntZeroExtend(len-1));
}
/**
* A JNI helper function, to set the value pointed to by a C pointer
* of type (jboolean *).
* @param boolPtr Native pointer to a jboolean variable to be set. May be
* the NULL pointer, in which case we do nothing.
* @param val Value to set it to (usually TRUE)
*
*/
static void setBoolStar(Address boolPtr, boolean val) {
if (boolPtr.isZero()) {
return;
}
if (val) {
boolPtr.store((byte)1);
} else {
boolPtr.store((byte)0);
}
}
}