Index: third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java |
diff --git a/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java b/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java |
new file mode 100644 |
index 0000000000000000000000000000000000000000..fafaa39815390366f052268c37a67a7f062abc9f |
--- /dev/null |
+++ b/third_party/jmake/src/org/pantsbuild/jmake/BinaryProjectDatabaseWriter.java |
@@ -0,0 +1,363 @@ |
+/* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved |
+ * |
+ * This program is distributed under the terms of |
+ * the GNU General Public License Version 2. See the LICENSE file |
+ * at the top of the source tree. |
+ */ |
+package org.pantsbuild.jmake; |
+ |
+import java.io.File; |
+import java.io.FileOutputStream; |
+import java.io.IOException; |
+import java.util.Map; |
+ |
+/** |
+ * This class implements writing into a byte array representing a project database |
+ * |
+ * @author Misha Dmitriev |
+ * 2 March 2005 |
+ */ |
+public class BinaryProjectDatabaseWriter extends BinaryFileWriter { |
+ |
+ private Map<String, PCDEntry> pcd = null; |
+ private int nOfEntries; |
+ private byte[] stringBuf; |
+ private int curStringBufPos, stringBufInc, curStringBufWatermark, stringCount; |
+ private StringHashTable stringHashTable = null; |
+ |
+ public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> pcd) { |
+ try { |
+ byte[] buf = new BinaryProjectDatabaseWriter().writeProjectDatabase(pcd); |
+ FileOutputStream out = new FileOutputStream(outfile); |
+ out.write(buf); |
+ out.close(); |
+ } catch (IOException e) { |
+ throw new PrivateException(e); |
+ } |
+ } |
+ |
+ public byte[] writeProjectDatabase(Map<String, PCDEntry> pcd) { |
+ this.pcd = pcd; |
+ nOfEntries = pcd.size(); |
+ |
+ // So far the constant here is chosen rather arbitrarily |
+ initBuf(nOfEntries * 1000); |
+ |
+ stringBuf = new byte[nOfEntries * 300]; |
+ stringBufInc = stringBuf.length / 5; |
+ curStringBufWatermark = stringBuf.length - 20; |
+ stringHashTable = new StringHashTable(stringBuf.length / 8); |
+ |
+ for (PCDEntry entry : pcd.values()) { |
+ writePCDEntry(entry); |
+ } |
+ |
+ // Now we have the string buffer and the main buffer. Write the end result |
+ byte[] mainBuf = buf; |
+ int mainBufSize = curBufPos; |
+ int preambleSize = Utils.MAGIC.length + 8; |
+ int stringBufSize = curStringBufPos; |
+ int pdbSize = stringBufSize + mainBufSize + 8; // 8 is for nOfEntries and string table size |
+ initBuf(preambleSize + pdbSize); |
+ setBufferIncreaseMode(false); |
+ |
+ writePreamble(pdbSize); |
+ writeStringTable(stringBufSize); |
+ System.arraycopy(mainBuf, 0, buf, curBufPos, mainBufSize); |
+ return buf; |
+ } |
+ |
+ private void writePreamble(int pdbSize) { |
+ System.arraycopy(Utils.MAGIC, 0, buf, 0, Utils.MAGIC.length); |
+ curBufPos += Utils.MAGIC.length; |
+ |
+ writeInt(Utils.PDB_FORMAT_CODE_LATEST); // Version number |
+ writeInt(pdbSize); |
+ writeInt(pcd.size()); |
+ } |
+ |
+ private void writeStringTable(int stringBufSize) { |
+ writeInt(stringCount); |
+ System.arraycopy(stringBuf, 0, buf, curBufPos, stringBufSize); |
+ curBufPos += stringBufSize; |
+ } |
+ |
+ private void writePCDEntry(PCDEntry entry) { |
+ writeStringRef(entry.className); |
+ writeStringRef(entry.javaFileFullPath); |
+ writeLong(entry.oldClassFileLastModified); |
+ writeLong(entry.oldClassFileFingerprint); |
+ writeClassInfo(entry.oldClassInfo); |
+ } |
+ |
+ private void writeClassInfo(ClassInfo ci) { |
+ int i, j, len; |
+ |
+ writeStringRef(ci.name); |
+ writeInt(ci.javacTargetRelease); |
+ |
+ len = ci.cpoolRefsToClasses != null ? ci.cpoolRefsToClasses.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String cpoolRefsToClasses[] = ci.cpoolRefsToClasses; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToClasses[i]); |
+ } |
+ boolean isRefClassArray[] = ci.isRefClassArray; |
+ for (i = 0; i < len; i++) { |
+ byte b = isRefClassArray[i] ? (byte) 1 : (byte) 0; |
+ writeByte(b); |
+ } |
+ } |
+ |
+ len = ci.cpoolRefsToFieldClasses != null ? ci.cpoolRefsToFieldClasses.length |
+ : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String cpoolRefsToFieldClasses[] = ci.cpoolRefsToFieldClasses; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToFieldClasses[i]); |
+ } |
+ String cpoolRefsToFieldNames[] = ci.cpoolRefsToFieldNames; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToFieldNames[i]); |
+ } |
+ String cpoolRefsToFieldSignatures[] = ci.cpoolRefsToFieldSignatures; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToFieldSignatures[i]); |
+ } |
+ } |
+ |
+ len = ci.cpoolRefsToMethodClasses != null ? ci.cpoolRefsToMethodClasses.length |
+ : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String cpoolRefsToMethodClasses[] = ci.cpoolRefsToMethodClasses; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToMethodClasses[i]); |
+ } |
+ String cpoolRefsToMethodNames[] = ci.cpoolRefsToMethodNames; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToMethodNames[i]); |
+ } |
+ String cpoolRefsToMethodSignatures[] = |
+ ci.cpoolRefsToMethodSignatures; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(cpoolRefsToMethodSignatures[i]); |
+ } |
+ } |
+ |
+ writeChar(ci.accessFlags); |
+ byte b = ci.isNonMemberNestedClass ? (byte) 1 : (byte) 0; |
+ writeByte(b); |
+ if (!"java/lang/Object".equals(ci.name)) { |
+ writeStringRef(ci.superName); |
+ } |
+ |
+ len = ci.interfaces != null ? ci.interfaces.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String interfaces[] = ci.interfaces; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(interfaces[i]); |
+ } |
+ } |
+ |
+ len = ci.fieldNames != null ? ci.fieldNames.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String fieldNames[] = ci.fieldNames; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(fieldNames[i]); |
+ } |
+ String fieldSignatures[] = ci.fieldSignatures; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(fieldSignatures[i]); |
+ } |
+ char fieldAccessFlags[] = ci.fieldAccessFlags; |
+ for (i = 0; i < len; i++) { |
+ writeChar(fieldAccessFlags[i]); |
+ } |
+ } |
+ |
+ len = ci.primitiveConstantInitValues != null ? ci.primitiveConstantInitValues.length |
+ : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ Object primitiveConstantInitValues[] = |
+ ci.primitiveConstantInitValues; |
+ for (i = 0; i < len; i++) { |
+ Object pc = primitiveConstantInitValues[i]; |
+ if (pc != null) { |
+ if (pc instanceof String) { |
+ writeByte((byte)1); |
+ writeStringRef((String) pc); |
+ } else if (pc instanceof Integer) { |
+ writeByte((byte)2); |
+ writeInt(((Integer) pc).intValue()); |
+ } else if (pc instanceof Long) { |
+ writeByte((byte)3); |
+ writeLong(((Long) pc).longValue()); |
+ } else if (pc instanceof Float) { |
+ writeByte((byte)4); |
+ writeFloat(((Float) pc).floatValue()); |
+ } else if (pc instanceof Double) { |
+ writeByte((byte)5); |
+ writeDouble(((Double) pc).doubleValue()); |
+ } |
+ } else { |
+ writeByte((byte)0); |
+ } |
+ } |
+ } |
+ |
+ len = ci.methodNames != null ? ci.methodNames.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String methodNames[] = ci.methodNames; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(methodNames[i]); |
+ } |
+ String methodSignatures[] = ci.methodSignatures; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(methodSignatures[i]); |
+ } |
+ char methodAccessFlags[] = ci.methodAccessFlags; |
+ for (i = 0; i < len; i++) { |
+ writeChar(methodAccessFlags[i]); |
+ } |
+ } |
+ |
+ len = ci.checkedExceptions != null ? ci.checkedExceptions.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String checkedExceptions[][] = ci.checkedExceptions; |
+ for (i = 0; i < len; i++) { |
+ int lenl = checkedExceptions[i] != null ? checkedExceptions[i].length |
+ : 0; |
+ writeChar(lenl); |
+ if (lenl > 0) { |
+ for (j = 0; j < lenl; j++) { |
+ writeStringRef(checkedExceptions[i][j]); |
+ } |
+ } |
+ } |
+ } |
+ |
+ len = ci.nestedClasses != null ? ci.nestedClasses.length : 0; |
+ writeChar(len); |
+ if (len > 0) { |
+ String nestedClasses[] = ci.nestedClasses; |
+ for (i = 0; i < len; i++) { |
+ writeStringRef(nestedClasses[i]); |
+ } |
+ } |
+ } |
+ |
+ private void writeString(String s) { |
+ byte sb[] = s.getBytes(); |
+ int len = sb.length; |
+ if (curStringBufPos + len > curStringBufWatermark) { |
+ // May need to adapt stringBufInc |
+ if (len >= stringBufInc) { |
+ stringBufInc = (stringBufInc + len) * 2; |
+ } else { |
+ stringBufInc = (stringBufInc * 5) / 4; // Still increase a little - observations show that otherwise we usually get here 20 more times |
+ } |
+ byte newStringBuf[] = new byte[stringBuf.length + stringBufInc]; |
+ System.arraycopy(stringBuf, 0, newStringBuf, 0, curStringBufPos); |
+ stringBuf = newStringBuf; |
+ curStringBufWatermark = stringBuf.length - 20; |
+ } |
+ stringBuf[curStringBufPos++] = (byte) ((len >> 8) & 255); |
+ stringBuf[curStringBufPos++] = (byte) (len & 255); |
+ System.arraycopy(sb, 0, stringBuf, curStringBufPos, len); |
+ curStringBufPos += len; |
+ } |
+ |
+ private void writeStringRef(String s) { |
+ int stringRef = stringHashTable.get(s); |
+ if (stringRef == -1) { |
+ stringHashTable.add(s, stringCount); |
+ stringRef = stringCount; |
+ writeString(s); |
+ stringCount++; |
+ } |
+ writeInt(stringRef); |
+ } |
+ |
+ /** Maps Strings to integer numbers (their positions in String table) */ |
+ static class StringHashTable { |
+ |
+ String keys[]; |
+ int values[]; |
+ int size, nOfElements, watermark; |
+ |
+ StringHashTable(int size) { |
+ size = makeLikePrimeNumber(size); |
+ this.size = size; |
+ keys = new String[size]; |
+ values = new int[size]; |
+ nOfElements = 0; |
+ watermark = size * 3 / 4; |
+ } |
+ |
+ final int get(String key) { |
+ int pos = (key.hashCode() & 0x7FFFFFFF) % size; |
+ |
+ while (keys[pos] != null && !keys[pos].equals(key)) { |
+ pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0 |
+ } |
+ if (key.equals(keys[pos])) { |
+ return values[pos]; |
+ } else { |
+ return -1; |
+ } |
+ } |
+ |
+ final void add(String key, int value) { |
+ if (nOfElements > watermark) { |
+ rehash(); |
+ } |
+ |
+ int pos = (key.hashCode() & 0x7FFFFFFF) % size; |
+ while (keys[pos] != null) { |
+ pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0 |
+ } |
+ keys[pos] = key; |
+ values[pos] = value; |
+ nOfElements++; |
+ } |
+ |
+ private final void rehash() { |
+ String oldKeys[] = keys; |
+ int oldValues[] = values; |
+ int oldSize = size; |
+ size = makeLikePrimeNumber(size * 3 / 2); |
+ keys = new String[size]; |
+ values = new int[size]; |
+ nOfElements = 0; |
+ watermark = size * 3 / 4; |
+ |
+ for (int i = 0; i < oldSize; i++) { |
+ if (oldKeys[i] != null) { |
+ add(oldKeys[i], oldValues[i]); |
+ } |
+ } |
+ } |
+ |
+ private final int makeLikePrimeNumber(int no) { |
+ no = (no / 2) * 2 + 1; // Make it an odd number |
+ // Find the nearest "approximately prime" number |
+ boolean prime = false; |
+ do { |
+ no += 2; |
+ prime = |
+ (no % 3 != 0 && no % 5 != 0 && no % 7 != 0 && no % 11 != 0 && |
+ no % 13 != 0 && no % 17 != 0 && no % 19 != 0 && no % 23 != 0 && |
+ no % 29 != 0 && no % 31 != 0 && no % 37 != 0 && no % 41 != 0); |
+ } while (!prime); |
+ return no; |
+ } |
+ } |
+} |