/* * @(#)CVMStringTable.java 1.38 06/10/22 * * Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. * */ package runtime; import java.util.Enumeration; import consts.Const; import components.*; import vm.*; import util.*; public class CVMStringTable extends vm.StringTable { CVMStringIntern internTable; private CVMInitInfo initInfo; CVMStringTable(CVMInitInfo initInfo) { this.initInfo = initInfo; } final boolean writeStringData( String dataName, String charArrayClassBlockName, CCodeWriter out ){ int n = arrangeStringData(); if ( n == 0 ) return false; // nothing to do here. char v[] = new char[n]; this.fillInChars( 0, n, v, 0 ); // // first typedef the structure we're about to create /* * The following structure has to reflect CVMArrayOfChar which * has a padding field for 64 bit platforms to be sure * that the membler elems has the same offset for all * CVMArrayOf<type> structures. */ out.print("const struct { CVMObjectHeader hdr;\n CVMJavaInt length;\n#ifdef CVM_64\n CVMUint32 pad;\n#endif\n CVMJavaChar stringData["+n+"]; } "); out.print( dataName ); out.println( " = {" ); // // fill in the header information // hdr.various = {{0}} ?? int hashCode = 0; out.println(" CVM_ROM_OBJECT_HDR_INIT0("+charArrayClassBlockName+ ","+hashCode+"),"); out.println(" "+n+",\n#ifdef CVM_64\n 0,\n#endif\n {"); // // finally write the char array. int mod = 0; for (int i = 0; i < n; i++) { if (mod == 0) out.write('\t'); out.printHexInt(v[i]); out.write(','); if (++mod == 12) { out.write('\n'); mod = 0; } } if (mod != 0) out.write('\n'); out.print(" } };\n\n"); return true; } private final static int maxCom = 20; private static byte cbuf[] = new byte[maxCom + 3]; private void commentary( String s, CCodeWriter out ){ int m = Math.min( maxCom, s.length() ); for ( int i = 0; i < m; i++ ){ char c = s.charAt(i); if ( ' ' <= c && c <= '~' ) { if (c == '*') { /* * Prevent damage to C comment by replacing * with ?. */ c = '?'; } cbuf[i] = (byte) c; } else { cbuf[i] = (byte)'?'; } } out.write( out.commentLeader, 0, out.commentLeader.length ); out.write( cbuf, 0, m ); if ( m < s.length() ){ out.write('.'); out.write('.'); out.write('.'); } out.write( out.commentTrailer, 0, out.commentTrailer.length ); } public int writeStrings(CCodeWriter out, String tableName ) { String dataName = tableName+"_data"; ClassInfo charArrayClass = ClassTable.lookupClass("[C"); if ( charArrayClass == null ){ System.err.println(Localizer.getString("javacodecompact.cannot_find_array_of_char")); return 0; } if ( ! writeStringData( dataName, ((CVMClass)(charArrayClass.vmClass)).getNativeName()+"_Classblock" , out ) ) return 0; out.println("const CVMUint16 " + tableName + "[] = {"); StringConstant stringTable[] = allStrings(); internTable = new CVMStringIntern( initInfo, stringTable.length ); int currentLength = -1; int currentLengthCount = -1; int numCompressedEntries = 0; for ( int i = 0; i < stringTable.length; i++ ){ StringConstant s = stringTable[i]; String string = s.str.string; int hashCode = 0; if (currentLength != string.length()) { if (currentLengthCount >= 0) { out.println("/* COUNTS: "+ currentLengthCount+ " instances of "+ currentLength+ "-character-long strings */"); out.println("\t"+currentLengthCount+", "+ currentLength+","); numCompressedEntries++; } currentLength = string.length(); currentLengthCount = 0; } currentLengthCount++; out.print("\t"); commentary(string, out); internTable.internStringConstant( s ); } if (currentLengthCount >= 0) { out.println("/* COUNTS: "+ currentLengthCount+ " instances of "+ currentLength+ "-character-long strings */"); out.println("\t"+currentLengthCount+", "+ currentLength+","); numCompressedEntries++; } out.println("};\n\n"); out.println("const int CVM_nROMStringsCompressedEntries = "+numCompressedEntries+";"); return stringTable.length; } /* * Moved global variables to CVMROMGlobals * That makes it necessary to pass the global header out file into writeInternTable. */ public void writeStringInternTable( CCodeWriter out, CCodeWriter globalHeaderOut, String tableName, String stringArrayName ){ internTable.writeInternTable( out, globalHeaderOut, tableName, stringArrayName ); } } class CVMInternSegment { int capacity; int content = 0; StringConstant data[]; private static final int primeFactors[] = { 3, 5, 7, 11, 13, 37 }; private static final int nPrimeFactors = primeFactors.length; CVMInternSegment( int size ){ /* * apply our usual emptyness parameter. * then make sure its relatively prime to * the secondary hash h1, where 1 <= h1 <= 16 * and also to 37. This means testing against * 2, 3, 5, 7, 11, 13, and 37. * This loop WILL terminate, because eventually * we will hit either a relative prime to all these * factors, or an actual prime. */ size = ( size*100 ) / 65; size |= 1; /* not a multiple of 2 */ boolean changed = true; while ( changed ) { changed = false; for ( int i = 0; i < nPrimeFactors; i++ ){ if (size%primeFactors[i] == 0 ){ size += 2; changed = true; break; /* out of for loop */ } } } capacity = size; data = new StringConstant[ size ]; } void writeSegment( String segmentName, String stringArrayName, String link, CCodeWriter out ) { // // first typedef the array we're about to create out.print("const struct { CVM_INTERN_SEGMENT_HEADER\n\tCVMStringICell data["); out.print( capacity ); out.print( "];\n\tCVMUint8 refCount[" ); out.print( capacity ); out.print( "]; } " ); out.print( segmentName ); out.print(" = {\n &" ); /* * Moved globals to CVMROMGlobals. */ out.print("CVMROMGlobals." + link); out.print(", "); out.print(capacity); out.print(", "); out.print(content); out.println(", 1, {"); // // write the String reference array. for (int i = 0; i < capacity; i++) { StringConstant sc = data[i]; if ( sc == null ){ out.println(" {0},"); } else { /* * Moved globals to CVMROMGlobals. */ out.println(" {(CVMObject*)&CVMROMGlobals."+stringArrayName+"["+sc.unicodeIndex+"]},"); } } out.print("}, {\n "); // now the reference counts: all either unused or sticky int perLine = 0; for (int i = 0; i < capacity; i++) { StringConstant sc = data[i]; if ( sc == null ){ out.print("StrU, "); } else { out.print("StrS, "); } if (++perLine > 8 ){ out.print("\n "); perLine = 0; } } out.println("\n}};"); } } class CVMStringIntern{ CVMInternSegment internTable; CVMInitInfo initInfo; CVMStringIntern( CVMInitInfo initInfo, int maxSize ){ this.initInfo = initInfo; internTable = new CVMInternSegment( maxSize ); } StringConstant internStringConstant( StringConstant val ){ String s = val.str.string; CVMInternSegment curseg = internTable; int h = 0, n; // Hash function. // Identical to the one in the runtime interning code. n = Math.min( s.length(), 16 ); for ( int i = 0; i < n; i++ ){ h = (h*37) + s.charAt(i); } int h1 = (h&0x7fffffff) % curseg.capacity; int h2 = (h&15)+1; int i = h1; StringConstant candidate; while ( (candidate=curseg.data[i]) != null ){ if ( val.equals( candidate ) ){ return candidate; } i += h2; if ( i >= curseg.capacity ){ i -= curseg.capacity; } } // not found in this table. // insert at current location. curseg.data[i] = val; curseg.content += 1; return val; } /* * Moved global variables to CVMROMGlobals * That makes it necessary to pass the global header out file into writeInternTable. */ public void writeInternTable( CCodeWriter out, CCodeWriter globalHeaderOut, String tableName, String stringArrayName ) { /* the StringIntern class has no EMV equivalent -- it becomes * part of java.lang.String. Only the InternSegment exists. * Go write it out. */ String linkname; // Note : define more compact representation of // CVMInternUnused and CVMInternSticky to reduce our // output volumn out.print("#undef StrS\n#undef StrU\n"); out.print("#define StrS CVMInternSticky\n"); out.print("#define StrU CVMInternUnused\n"); // // write out the indirect next cell, and see that it gets // initialized. linkname = tableName+"NextCell"; /* * Moved global variables to CVMROMGlobals */ globalHeaderOut.println(" struct CVMInternSegment * "+linkname+";"); initInfo.addInfo("NULL", "&CVMROMGlobals."+linkname, "sizeof (CVMROMGlobals."+linkname+")" ); internTable.writeSegment( tableName, stringArrayName, linkname, out ); } }