OLD | NEW |
(Empty) | |
| 1 /* Copyright (c) 2002-2008 Sun Microsystems, Inc. All rights reserved |
| 2 * |
| 3 * This program is distributed under the terms of |
| 4 * the GNU General Public License Version 2. See the LICENSE file |
| 5 * at the top of the source tree. |
| 6 */ |
| 7 package org.pantsbuild.jmake; |
| 8 |
| 9 import java.io.File; |
| 10 import java.io.FileOutputStream; |
| 11 import java.io.IOException; |
| 12 import java.util.Map; |
| 13 |
| 14 /** |
| 15 * This class implements writing into a byte array representing a project databa
se |
| 16 * |
| 17 * @author Misha Dmitriev |
| 18 * 2 March 2005 |
| 19 */ |
| 20 public class BinaryProjectDatabaseWriter extends BinaryFileWriter { |
| 21 |
| 22 private Map<String, PCDEntry> pcd = null; |
| 23 private int nOfEntries; |
| 24 private byte[] stringBuf; |
| 25 private int curStringBufPos, stringBufInc, curStringBufWatermark, stringC
ount; |
| 26 private StringHashTable stringHashTable = null; |
| 27 |
| 28 public void writeProjectDatabaseToFile(File outfile, Map<String, PCDEntry> p
cd) { |
| 29 try { |
| 30 byte[] buf = new BinaryProjectDatabaseWriter().writeProjectDatabase(
pcd); |
| 31 FileOutputStream out = new FileOutputStream(outfile); |
| 32 out.write(buf); |
| 33 out.close(); |
| 34 } catch (IOException e) { |
| 35 throw new PrivateException(e); |
| 36 } |
| 37 } |
| 38 |
| 39 public byte[] writeProjectDatabase(Map<String, PCDEntry> pcd) { |
| 40 this.pcd = pcd; |
| 41 nOfEntries = pcd.size(); |
| 42 |
| 43 // So far the constant here is chosen rather arbitrarily |
| 44 initBuf(nOfEntries * 1000); |
| 45 |
| 46 stringBuf = new byte[nOfEntries * 300]; |
| 47 stringBufInc = stringBuf.length / 5; |
| 48 curStringBufWatermark = stringBuf.length - 20; |
| 49 stringHashTable = new StringHashTable(stringBuf.length / 8); |
| 50 |
| 51 for (PCDEntry entry : pcd.values()) { |
| 52 writePCDEntry(entry); |
| 53 } |
| 54 |
| 55 // Now we have the string buffer and the main buffer. Write the end resu
lt |
| 56 byte[] mainBuf = buf; |
| 57 int mainBufSize = curBufPos; |
| 58 int preambleSize = Utils.MAGIC.length + 8; |
| 59 int stringBufSize = curStringBufPos; |
| 60 int pdbSize = stringBufSize + mainBufSize + 8; // 8 is for nOfEntries a
nd string table size |
| 61 initBuf(preambleSize + pdbSize); |
| 62 setBufferIncreaseMode(false); |
| 63 |
| 64 writePreamble(pdbSize); |
| 65 writeStringTable(stringBufSize); |
| 66 System.arraycopy(mainBuf, 0, buf, curBufPos, mainBufSize); |
| 67 return buf; |
| 68 } |
| 69 |
| 70 private void writePreamble(int pdbSize) { |
| 71 System.arraycopy(Utils.MAGIC, 0, buf, 0, Utils.MAGIC.length); |
| 72 curBufPos += Utils.MAGIC.length; |
| 73 |
| 74 writeInt(Utils.PDB_FORMAT_CODE_LATEST); // Version number |
| 75 writeInt(pdbSize); |
| 76 writeInt(pcd.size()); |
| 77 } |
| 78 |
| 79 private void writeStringTable(int stringBufSize) { |
| 80 writeInt(stringCount); |
| 81 System.arraycopy(stringBuf, 0, buf, curBufPos, stringBufSize); |
| 82 curBufPos += stringBufSize; |
| 83 } |
| 84 |
| 85 private void writePCDEntry(PCDEntry entry) { |
| 86 writeStringRef(entry.className); |
| 87 writeStringRef(entry.javaFileFullPath); |
| 88 writeLong(entry.oldClassFileLastModified); |
| 89 writeLong(entry.oldClassFileFingerprint); |
| 90 writeClassInfo(entry.oldClassInfo); |
| 91 } |
| 92 |
| 93 private void writeClassInfo(ClassInfo ci) { |
| 94 int i, j, len; |
| 95 |
| 96 writeStringRef(ci.name); |
| 97 writeInt(ci.javacTargetRelease); |
| 98 |
| 99 len = ci.cpoolRefsToClasses != null ? ci.cpoolRefsToClasses.length : 0; |
| 100 writeChar(len); |
| 101 if (len > 0) { |
| 102 String cpoolRefsToClasses[] = ci.cpoolRefsToClasses; |
| 103 for (i = 0; i < len; i++) { |
| 104 writeStringRef(cpoolRefsToClasses[i]); |
| 105 } |
| 106 boolean isRefClassArray[] = ci.isRefClassArray; |
| 107 for (i = 0; i < len; i++) { |
| 108 byte b = isRefClassArray[i] ? (byte) 1 : (byte) 0; |
| 109 writeByte(b); |
| 110 } |
| 111 } |
| 112 |
| 113 len = ci.cpoolRefsToFieldClasses != null ? ci.cpoolRefsToFieldClasses.le
ngth |
| 114 : 0; |
| 115 writeChar(len); |
| 116 if (len > 0) { |
| 117 String cpoolRefsToFieldClasses[] = ci.cpoolRefsToFieldClasses; |
| 118 for (i = 0; i < len; i++) { |
| 119 writeStringRef(cpoolRefsToFieldClasses[i]); |
| 120 } |
| 121 String cpoolRefsToFieldNames[] = ci.cpoolRefsToFieldNames; |
| 122 for (i = 0; i < len; i++) { |
| 123 writeStringRef(cpoolRefsToFieldNames[i]); |
| 124 } |
| 125 String cpoolRefsToFieldSignatures[] = ci.cpoolRefsToFieldSignatures; |
| 126 for (i = 0; i < len; i++) { |
| 127 writeStringRef(cpoolRefsToFieldSignatures[i]); |
| 128 } |
| 129 } |
| 130 |
| 131 len = ci.cpoolRefsToMethodClasses != null ? ci.cpoolRefsToMethodClasses.
length |
| 132 : 0; |
| 133 writeChar(len); |
| 134 if (len > 0) { |
| 135 String cpoolRefsToMethodClasses[] = ci.cpoolRefsToMethodClasses; |
| 136 for (i = 0; i < len; i++) { |
| 137 writeStringRef(cpoolRefsToMethodClasses[i]); |
| 138 } |
| 139 String cpoolRefsToMethodNames[] = ci.cpoolRefsToMethodNames; |
| 140 for (i = 0; i < len; i++) { |
| 141 writeStringRef(cpoolRefsToMethodNames[i]); |
| 142 } |
| 143 String cpoolRefsToMethodSignatures[] = |
| 144 ci.cpoolRefsToMethodSignatures; |
| 145 for (i = 0; i < len; i++) { |
| 146 writeStringRef(cpoolRefsToMethodSignatures[i]); |
| 147 } |
| 148 } |
| 149 |
| 150 writeChar(ci.accessFlags); |
| 151 byte b = ci.isNonMemberNestedClass ? (byte) 1 : (byte) 0; |
| 152 writeByte(b); |
| 153 if (!"java/lang/Object".equals(ci.name)) { |
| 154 writeStringRef(ci.superName); |
| 155 } |
| 156 |
| 157 len = ci.interfaces != null ? ci.interfaces.length : 0; |
| 158 writeChar(len); |
| 159 if (len > 0) { |
| 160 String interfaces[] = ci.interfaces; |
| 161 for (i = 0; i < len; i++) { |
| 162 writeStringRef(interfaces[i]); |
| 163 } |
| 164 } |
| 165 |
| 166 len = ci.fieldNames != null ? ci.fieldNames.length : 0; |
| 167 writeChar(len); |
| 168 if (len > 0) { |
| 169 String fieldNames[] = ci.fieldNames; |
| 170 for (i = 0; i < len; i++) { |
| 171 writeStringRef(fieldNames[i]); |
| 172 } |
| 173 String fieldSignatures[] = ci.fieldSignatures; |
| 174 for (i = 0; i < len; i++) { |
| 175 writeStringRef(fieldSignatures[i]); |
| 176 } |
| 177 char fieldAccessFlags[] = ci.fieldAccessFlags; |
| 178 for (i = 0; i < len; i++) { |
| 179 writeChar(fieldAccessFlags[i]); |
| 180 } |
| 181 } |
| 182 |
| 183 len = ci.primitiveConstantInitValues != null ? ci.primitiveConstantInitV
alues.length |
| 184 : 0; |
| 185 writeChar(len); |
| 186 if (len > 0) { |
| 187 Object primitiveConstantInitValues[] = |
| 188 ci.primitiveConstantInitValues; |
| 189 for (i = 0; i < len; i++) { |
| 190 Object pc = primitiveConstantInitValues[i]; |
| 191 if (pc != null) { |
| 192 if (pc instanceof String) { |
| 193 writeByte((byte)1); |
| 194 writeStringRef((String) pc); |
| 195 } else if (pc instanceof Integer) { |
| 196 writeByte((byte)2); |
| 197 writeInt(((Integer) pc).intValue()); |
| 198 } else if (pc instanceof Long) { |
| 199 writeByte((byte)3); |
| 200 writeLong(((Long) pc).longValue()); |
| 201 } else if (pc instanceof Float) { |
| 202 writeByte((byte)4); |
| 203 writeFloat(((Float) pc).floatValue()); |
| 204 } else if (pc instanceof Double) { |
| 205 writeByte((byte)5); |
| 206 writeDouble(((Double) pc).doubleValue()); |
| 207 } |
| 208 } else { |
| 209 writeByte((byte)0); |
| 210 } |
| 211 } |
| 212 } |
| 213 |
| 214 len = ci.methodNames != null ? ci.methodNames.length : 0; |
| 215 writeChar(len); |
| 216 if (len > 0) { |
| 217 String methodNames[] = ci.methodNames; |
| 218 for (i = 0; i < len; i++) { |
| 219 writeStringRef(methodNames[i]); |
| 220 } |
| 221 String methodSignatures[] = ci.methodSignatures; |
| 222 for (i = 0; i < len; i++) { |
| 223 writeStringRef(methodSignatures[i]); |
| 224 } |
| 225 char methodAccessFlags[] = ci.methodAccessFlags; |
| 226 for (i = 0; i < len; i++) { |
| 227 writeChar(methodAccessFlags[i]); |
| 228 } |
| 229 } |
| 230 |
| 231 len = ci.checkedExceptions != null ? ci.checkedExceptions.length : 0; |
| 232 writeChar(len); |
| 233 if (len > 0) { |
| 234 String checkedExceptions[][] = ci.checkedExceptions; |
| 235 for (i = 0; i < len; i++) { |
| 236 int lenl = checkedExceptions[i] != null ? checkedExceptions[i].l
ength |
| 237 : 0; |
| 238 writeChar(lenl); |
| 239 if (lenl > 0) { |
| 240 for (j = 0; j < lenl; j++) { |
| 241 writeStringRef(checkedExceptions[i][j]); |
| 242 } |
| 243 } |
| 244 } |
| 245 } |
| 246 |
| 247 len = ci.nestedClasses != null ? ci.nestedClasses.length : 0; |
| 248 writeChar(len); |
| 249 if (len > 0) { |
| 250 String nestedClasses[] = ci.nestedClasses; |
| 251 for (i = 0; i < len; i++) { |
| 252 writeStringRef(nestedClasses[i]); |
| 253 } |
| 254 } |
| 255 } |
| 256 |
| 257 private void writeString(String s) { |
| 258 byte sb[] = s.getBytes(); |
| 259 int len = sb.length; |
| 260 if (curStringBufPos + len > curStringBufWatermark) { |
| 261 // May need to adapt stringBufInc |
| 262 if (len >= stringBufInc) { |
| 263 stringBufInc = (stringBufInc + len) * 2; |
| 264 } else { |
| 265 stringBufInc = (stringBufInc * 5) / 4; // Still increase a litt
le - observations show that otherwise we usually get here 20 more times |
| 266 } |
| 267 byte newStringBuf[] = new byte[stringBuf.length + stringBufInc]; |
| 268 System.arraycopy(stringBuf, 0, newStringBuf, 0, curStringBufPos); |
| 269 stringBuf = newStringBuf; |
| 270 curStringBufWatermark = stringBuf.length - 20; |
| 271 } |
| 272 stringBuf[curStringBufPos++] = (byte) ((len >> 8) & 255); |
| 273 stringBuf[curStringBufPos++] = (byte) (len & 255); |
| 274 System.arraycopy(sb, 0, stringBuf, curStringBufPos, len); |
| 275 curStringBufPos += len; |
| 276 } |
| 277 |
| 278 private void writeStringRef(String s) { |
| 279 int stringRef = stringHashTable.get(s); |
| 280 if (stringRef == -1) { |
| 281 stringHashTable.add(s, stringCount); |
| 282 stringRef = stringCount; |
| 283 writeString(s); |
| 284 stringCount++; |
| 285 } |
| 286 writeInt(stringRef); |
| 287 } |
| 288 |
| 289 /** Maps Strings to integer numbers (their positions in String table) */ |
| 290 static class StringHashTable { |
| 291 |
| 292 String keys[]; |
| 293 int values[]; |
| 294 int size, nOfElements, watermark; |
| 295 |
| 296 StringHashTable(int size) { |
| 297 size = makeLikePrimeNumber(size); |
| 298 this.size = size; |
| 299 keys = new String[size]; |
| 300 values = new int[size]; |
| 301 nOfElements = 0; |
| 302 watermark = size * 3 / 4; |
| 303 } |
| 304 |
| 305 final int get(String key) { |
| 306 int pos = (key.hashCode() & 0x7FFFFFFF) % size; |
| 307 |
| 308 while (keys[pos] != null && !keys[pos].equals(key)) { |
| 309 pos = (pos + 3) % size; // Relies on the fact that size % 3 != 0 |
| 310 } |
| 311 if (key.equals(keys[pos])) { |
| 312 return values[pos]; |
| 313 } else { |
| 314 return -1; |
| 315 } |
| 316 } |
| 317 |
| 318 final void add(String key, int value) { |
| 319 if (nOfElements > watermark) { |
| 320 rehash(); |
| 321 } |
| 322 |
| 323 int pos = (key.hashCode() & 0x7FFFFFFF) % size; |
| 324 while (keys[pos] != null) { |
| 325 pos = (pos + 3) % size; // Relies on the fact that size % 3 !=
0 |
| 326 } |
| 327 keys[pos] = key; |
| 328 values[pos] = value; |
| 329 nOfElements++; |
| 330 } |
| 331 |
| 332 private final void rehash() { |
| 333 String oldKeys[] = keys; |
| 334 int oldValues[] = values; |
| 335 int oldSize = size; |
| 336 size = makeLikePrimeNumber(size * 3 / 2); |
| 337 keys = new String[size]; |
| 338 values = new int[size]; |
| 339 nOfElements = 0; |
| 340 watermark = size * 3 / 4; |
| 341 |
| 342 for (int i = 0; i < oldSize; i++) { |
| 343 if (oldKeys[i] != null) { |
| 344 add(oldKeys[i], oldValues[i]); |
| 345 } |
| 346 } |
| 347 } |
| 348 |
| 349 private final int makeLikePrimeNumber(int no) { |
| 350 no = (no / 2) * 2 + 1; // Make it an odd number |
| 351 // Find the nearest "approximately prime" number |
| 352 boolean prime = false; |
| 353 do { |
| 354 no += 2; |
| 355 prime = |
| 356 (no % 3 != 0 && no % 5 != 0 && no % 7 != 0 && no % 11 !=
0 && |
| 357 no % 13 != 0 && no % 17 != 0 && no % 19 != 0 && no % 23
!= 0 && |
| 358 no % 29 != 0 && no % 31 != 0 && no % 37 != 0 && no % 41
!= 0); |
| 359 } while (!prime); |
| 360 return no; |
| 361 } |
| 362 } |
| 363 } |
OLD | NEW |