001    /*
002     *  This file is part of the Jikes RVM project (http://jikesrvm.org).
003     *
004     *  This file is licensed to You under the Eclipse Public License (EPL);
005     *  You may not use this file except in compliance with the License. You
006     *  may obtain a copy of the License at
007     *
008     *      http://www.opensource.org/licenses/eclipse-1.0.php
009     *
010     *  See the COPYRIGHT.txt file distributed with this work for information
011     *  regarding copyright ownership.
012     */
013    package org.jikesrvm.jni;
014    
015    import org.jikesrvm.VM;
016    import static org.jikesrvm.SizeConstants.BYTES_IN_ADDRESS;
017    import org.jikesrvm.classloader.UTF8Convert;
018    import org.jikesrvm.runtime.Magic;
019    import org.jikesrvm.runtime.Memory;
020    import org.jikesrvm.util.StringUtilities;
021    import org.vmmagic.unboxed.Address;
022    import org.vmmagic.unboxed.Offset;
023    import org.vmmagic.unboxed.Word;
024    
025    import java.nio.charset.CharacterCodingException;
026    import java.nio.charset.Charset;
027    import java.nio.charset.CharsetDecoder;
028    import java.nio.charset.CharsetEncoder;
029    import java.nio.charset.CodingErrorAction;
030    import java.nio.ByteBuffer;
031    import java.nio.CharBuffer;
032    
033    /**
034     * Platform independent utility functions called from JNIFunctions
035     * (cannot be placed in JNIFunctions because methods
036     * there are specially compiled to be called from native).
037     *
038     * @see JNIFunctions
039     */
040    public abstract class JNIGenericHelpers {
041    
042      /**
043       * Compute the length of the given null-terminated string
044       *
045       * @param ptr address of string in memory
046       * @return the length of the string in bytes
047       */
048      public static int strlen(Address ptr) {
049        int length=0;
050        // align address to size of machine
051        while (!ptr.toWord().and(Word.fromIntZeroExtend(BYTES_IN_ADDRESS - 1)).isZero()) {
052          byte bits = ptr.loadByte(Offset.fromIntZeroExtend(length));
053          if (bits == 0) {
054            return length;
055          }
056          length++;
057        }
058        // Ascii characters are normally in the range 1 to 128, if we subtract 1
059        // from each byte and look if the top bit of the byte is set then if it is
060        // the chances are the byte's value is 0. Loop over words doing this quick
061        // test and then do byte by byte tests when we think we have the 0
062        Word onesToSubtract;
063        Word maskToTestHighBits;
064        if (VM.BuildFor32Addr) {
065          onesToSubtract     = Word.fromIntZeroExtend(0x01010101);
066          maskToTestHighBits = Word.fromIntZeroExtend(0x80808080);
067        } else {
068          onesToSubtract     = Word.fromLong(0x0101010101010101L);
069          maskToTestHighBits = Word.fromLong(0x8080808080808080L);
070        }
071        while (true) {
072          Word bytes = ptr.loadWord(Offset.fromIntZeroExtend(length));
073          if(!bytes.minus(onesToSubtract).and(maskToTestHighBits).isZero()) {
074            if (VM.LittleEndian) {
075              for(int byteOff=0; byteOff < BYTES_IN_ADDRESS; byteOff++) {
076                if(bytes.and(Word.fromIntZeroExtend(0xFF)).isZero()) {
077                  return length + byteOff;
078                }
079                bytes = bytes.rshl(8);
080              }
081            } else {
082              for(int byteOff=BYTES_IN_ADDRESS-1; byteOff >= 0; byteOff--) {
083                if(bytes.rshl(byteOff*8).and(Word.fromIntZeroExtend(0xFF)).isZero()) {
084                  return length + (BYTES_IN_ADDRESS - 1 - byteOff);
085                }
086              }
087            }
088          }
089          length += BYTES_IN_ADDRESS;
090        }
091      }
092      /**
093       * Given an address in C that points to a null-terminated string,
094       * create a new Java byte[] with a copy of the string.
095       *
096       * @param stringAddress an address in C space for a string
097       * @return a new Java byte[]
098       */
099      public static byte[] createByteArrayFromC(Address stringAddress) {
100    
101        int length = strlen(stringAddress);
102        byte[] contents = new byte[length];
103        Memory.memcopy(Magic.objectAsAddress(contents), stringAddress, length);
104    
105        return contents;
106      }
107    
108      /**
109       * Create a string from the given charset decoder and bytebuffer
110       */
111      private static String createString(CharsetDecoder csd, ByteBuffer bbuf) throws CharacterCodingException {
112        char[] v;
113        int o;
114        int c;
115        CharBuffer cbuf = csd.decode(bbuf);
116        if(cbuf.hasArray()) {
117          v = cbuf.array();
118          o = cbuf.position();
119          c = cbuf.remaining();
120        } else {
121          // Doubt this will happen. But just in case.
122          v = new char[cbuf.remaining()];
123          cbuf.get(v);
124          o = 0;
125          c = v.length;
126        }
127        return java.lang.JikesRVMSupport.newStringWithoutCopy(v, o, c);
128      }
129      /**
130       * Given an address in C that points to a null-terminated string,
131       * create a new Java String with a copy of the string.
132       *
133       * @param stringAddress an address in C space for a string
134       * @return a new Java String
135       */
136      public static String createStringFromC(Address stringAddress) {
137        if (VM.fullyBooted) {
138          try {
139            String encoding = System.getProperty("file.encoding");
140            CharsetDecoder csd = Charset.forName(encoding).newDecoder();
141            csd.onMalformedInput(CodingErrorAction.REPLACE);
142            csd.onUnmappableCharacter(CodingErrorAction.REPLACE);
143            ByteBuffer bbuf =
144              java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress,
145                                                           strlen(stringAddress));
146            return createString(csd, bbuf);
147          } catch(Exception ex){
148            // Any problems fall through to default encoding
149          }
150        }
151        // Can't do real Char encoding until VM is fully booted.
152        // All Strings encountered during booting must be ascii
153        byte[] tmp = createByteArrayFromC(stringAddress);
154        return StringUtilities.asciiBytesToString(tmp);
155      }
156      /**
157       * Given an address in C that points to a null-terminated string,
158       * create a new UTF encoded Java String with a copy of the string.
159       *
160       * @param stringAddress an address in C space for a string
161       * @return a new Java String
162       */
163      public static String createUTFStringFromC(Address stringAddress) {
164        final boolean USE_LIBRARY_CODEC = false;
165        byte[] tmp;
166        ByteBuffer bbuf;
167        if (VM.fullyBooted) {
168          try {
169            bbuf = java.nio.JikesRVMSupport.newDirectByteBuffer(stringAddress,
170                                                                strlen(stringAddress));
171            if (USE_LIBRARY_CODEC) {
172              CharsetDecoder csd = Charset.forName("UTF8").newDecoder();
173              return createString(csd, bbuf);
174            } else {
175              return UTF8Convert.fromUTF8(bbuf);
176            }
177          } catch(Exception ex){
178            // Any problems fall through to default encoding
179          }
180        }
181        // Can't do real Char encoding until VM is fully booted.
182        // All Strings encountered during booting must be ascii
183        tmp = createByteArrayFromC(stringAddress);
184        return StringUtilities.asciiBytesToString(tmp);
185      }
186    
187    
188      /**
189       * Convert a String into a a malloced region
190       */
191      public static void createUTFForCFromString(String str, Address copyBuffer, int len) {
192        ByteBuffer bbuf =
193          java.nio.JikesRVMSupport.newDirectByteBuffer(copyBuffer, len);
194    
195        final boolean USE_LIBRARY_CODEC = false;
196        if (USE_LIBRARY_CODEC) {
197          char[] strChars = java.lang.JikesRVMSupport.getBackingCharArray(str);
198          int strOffset = java.lang.JikesRVMSupport.getStringOffset(str);
199          int strLen = java.lang.JikesRVMSupport.getStringLength(str);
200          CharBuffer cbuf = CharBuffer.wrap(strChars, strOffset, strLen);
201          CharsetEncoder cse = Charset.forName("UTF8").newEncoder();
202          cse.encode(cbuf, bbuf, true);
203        } else {
204          UTF8Convert.toUTF8(str, bbuf);
205        }
206        // store terminating zero
207        copyBuffer.store((byte)0, Offset.fromIntZeroExtend(len-1));
208      }
209    
210      /**
211       * A JNI helper function, to set the value pointed to by a C pointer
212       * of type (jboolean *).
213       * @param boolPtr Native pointer to a jboolean variable to be set.   May be
214       *            the NULL pointer, in which case we do nothing.
215       * @param val Value to set it to (usually TRUE)
216       *
217       */
218      static void setBoolStar(Address boolPtr, boolean val) {
219        if (boolPtr.isZero()) {
220          return;
221        }
222        if (val) {
223          boolPtr.store((byte)1);
224        } else {
225          boolPtr.store((byte)0);
226        }
227      }
228    }