1 11 package org.eclipse.core.internal.localstore; 12 13 import java.io.*; 14 import java.util.Arrays ; 15 import java.util.Comparator ; 16 import org.eclipse.core.internal.utils.UniversalUniqueIdentifier; 17 import org.eclipse.core.runtime.IPath; 18 19 public class HistoryBucket extends Bucket { 20 21 31 public static final class HistoryEntry extends Bucket.Entry { 32 33 final static Comparator COMPARATOR = new Comparator () { 34 public int compare(Object o1, Object o2) { 35 byte[] state1 = (byte[]) o1; 36 byte[] state2 = (byte[]) o2; 37 return compareStates(state1, state2); 38 } 39 }; 40 41 private final static byte[][] EMPTY_DATA = new byte[0][]; 43 private final static int LONG_LENGTH = 8; 45 private final static int UUID_LENGTH = UniversalUniqueIdentifier.BYTES_SIZE; 47 public final static int DATA_LENGTH = UUID_LENGTH + LONG_LENGTH; 48 49 53 private byte[][] data; 54 55 60 private static int compareStates(byte[] state1, byte[] state2) { 61 long timestamp1 = getTimestamp(state1); 62 long timestamp2 = getTimestamp(state2); 63 if (timestamp1 == timestamp2) 64 return -UniversalUniqueIdentifier.compareTime(state1, state2); 65 return timestamp1 < timestamp2 ? +1 : -1; 66 } 67 68 71 private static byte[] getState(UniversalUniqueIdentifier uuid, long timestamp) { 72 byte[] uuidBytes = uuid.toBytes(); 73 byte[] state = new byte[DATA_LENGTH]; 74 System.arraycopy(uuidBytes, 0, state, 0, uuidBytes.length); 75 for (int j = 0; j < LONG_LENGTH; j++) { 76 state[UUID_LENGTH + j] = (byte) (0xFF & timestamp); 77 timestamp >>>= 8; 78 } 79 return state; 80 } 81 82 private static long getTimestamp(byte[] state) { 83 long timestamp = 0; 84 for (int j = 0; j < LONG_LENGTH; j++) 85 timestamp += (state[UUID_LENGTH + j] & 0xFFL) << j * 8; 86 return timestamp; 87 } 88 89 93 private static byte[][] insert(byte[][] existing, byte[] toAdd) { 94 int index = search(existing, toAdd); 96 if (index >= 0) 97 return null; 99 int insertPosition = -index - 1; 101 byte[][] newValue = new byte[existing.length + 1][]; 102 if (insertPosition > 0) 103 System.arraycopy(existing, 0, newValue, 0, insertPosition); 104 newValue[insertPosition] = toAdd; 105 if (insertPosition < existing.length) 106 System.arraycopy(existing, insertPosition, newValue, insertPosition + 1, existing.length - insertPosition); 107 return newValue; 108 } 109 110 113 private static byte[][] merge(byte[][] base, byte[][] additions) { 114 int additionPointer = 0; 115 int basePointer = 0; 116 int added = 0; 117 byte[][] result = new byte[base.length + additions.length][]; 118 while (basePointer < base.length && additionPointer < additions.length) { 119 int comparison = compareStates(base[basePointer], additions[additionPointer]); 120 if (comparison == 0) { 121 result[added++] = base[basePointer++]; 122 additionPointer++; 124 } else if (comparison < 0) 125 result[added++] = base[basePointer++]; 126 else 127 result[added++] = additions[additionPointer++]; 128 } 129 byte[][] remaining = basePointer == base.length ? additions : base; 131 int remainingPointer = basePointer == base.length ? additionPointer : basePointer; 132 int remainingCount = remaining.length - remainingPointer; 133 System.arraycopy(remaining, remainingPointer, result, added, remainingCount); 134 added += remainingCount; 135 if (added == base.length + additions.length) 136 return result; 138 byte[][] finalResult = new byte[added][]; 140 System.arraycopy(result, 0, finalResult, 0, finalResult.length); 141 return finalResult; 142 } 143 144 private static int search(byte[][] existing, byte[] element) { 145 return Arrays.binarySearch(existing, element, COMPARATOR); 146 } 147 148 public HistoryEntry(IPath path, byte[][] data) { 149 super(path); 150 this.data = data; 151 } 152 153 public HistoryEntry(IPath path, HistoryEntry base) { 154 super(path); 155 this.data = new byte[base.data.length][]; 156 System.arraycopy(base.data, 0, this.data, 0, this.data.length); 157 } 158 159 163 private void compact() { 164 if (!isDirty()) 165 return; 166 int occurrences = 0; 167 for (int i = 0; i < data.length; i++) 168 if (data[i] != null) 169 data[occurrences++] = data[i]; 170 if (occurrences == data.length) 171 return; 173 if (occurrences == 0) { 174 data = EMPTY_DATA; 176 delete(); 177 return; 178 } 179 byte[][] result = new byte[occurrences][]; 180 System.arraycopy(data, 0, result, 0, occurrences); 181 data = result; 182 } 183 184 public void deleteOccurrence(int i) { 185 markDirty(); 186 data[i] = null; 187 } 188 189 byte[][] getData() { 190 return data; 191 } 192 193 public int getOccurrences() { 194 return data.length; 195 } 196 197 public long getTimestamp(int i) { 198 return getTimestamp(data[i]); 199 } 200 201 public UniversalUniqueIdentifier getUUID(int i) { 202 return new UniversalUniqueIdentifier(data[i]); 203 } 204 205 public Object getValue() { 206 return data; 207 } 208 209 public boolean isEmpty() { 210 return data.length == 0; 211 } 212 213 public void visited() { 214 compact(); 215 } 216 217 } 218 219 246 public final static byte VERSION = 2; 247 248 public HistoryBucket() { 249 super(); 250 } 251 252 public void addBlob(IPath path, UniversalUniqueIdentifier uuid, long lastModified) { 253 byte[] state = HistoryEntry.getState(uuid, lastModified); 254 String pathAsString = path.toString(); 255 byte[][] existing = (byte[][]) getEntryValue(pathAsString); 256 if (existing == null) { 257 setEntryValue(pathAsString, new byte[][] {state}); 258 return; 259 } 260 byte[][] newValue = HistoryEntry.insert(existing, state); 261 if (newValue == null) 262 return; 263 setEntryValue(pathAsString, newValue); 264 } 265 266 public void addBlobs(HistoryEntry fileEntry) { 267 IPath path = fileEntry.getPath(); 268 byte[][] additions = fileEntry.getData(); 269 String pathAsString = path.toString(); 270 byte[][] existing = (byte[][]) getEntryValue(pathAsString); 271 if (existing == null) { 272 setEntryValue(pathAsString, additions); 273 return; 274 } 275 setEntryValue(pathAsString, HistoryEntry.merge(existing, additions)); 276 } 277 278 protected Bucket.Entry createEntry(IPath path, Object value) { 279 return new HistoryEntry(path, (byte[][]) value); 280 } 281 282 public HistoryEntry getEntry(IPath path) { 283 String pathAsString = path.toString(); 284 byte[][] existing = (byte[][]) getEntryValue(pathAsString); 285 if (existing == null) 286 return null; 287 return new HistoryEntry(path, existing); 288 } 289 290 protected String getIndexFileName() { 291 return "history.index"; } 293 294 protected byte getVersion() { 295 return VERSION; 296 } 297 298 protected String getVersionFileName() { 299 return "history.version"; } 301 302 protected Object readEntryValue(DataInputStream source) throws IOException { 303 int length = source.readUnsignedShort(); 304 byte[][] uuids = new byte[length][HistoryEntry.DATA_LENGTH]; 305 for (int j = 0; j < uuids.length; j++) 306 source.read(uuids[j]); 307 return uuids; 308 } 309 310 protected void writeEntryValue(DataOutputStream destination, Object entryValue) throws IOException { 311 byte[][] uuids = (byte[][]) entryValue; 312 destination.writeShort(uuids.length); 313 for (int j = 0; j < uuids.length; j++) 314 destination.write(uuids[j]); 315 } 316 } 317 | Popular Tags |