blob: fafaa39815390366f052268c37a67a7f062abc9f [file] [log] [blame]
/* 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;
}
}
}