Computation of average could overflow
[egit/chris.git] / org.spearce.jgit / src / org / spearce / jgit / dircache / DirCache.java
blobfa906fa41bcc1c6d602731f3566f6c16df10230e
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or
7 * without modification, are permitted provided that the following
8 * conditions are met:
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above
14 * copyright notice, this list of conditions and the following
15 * disclaimer in the documentation and/or other materials provided
16 * with the distribution.
18 * - Neither the name of the Git Development Community nor the
19 * names of its contributors may be used to endorse or promote
20 * products derived from this software without specific prior
21 * written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
24 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
25 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
28 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
29 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
30 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
33 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
34 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
35 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
38 package org.spearce.jgit.dircache;
40 import java.io.BufferedInputStream;
41 import java.io.BufferedOutputStream;
42 import java.io.File;
43 import java.io.FileInputStream;
44 import java.io.FileNotFoundException;
45 import java.io.IOException;
46 import java.io.OutputStream;
47 import java.nio.ByteBuffer;
48 import java.security.DigestOutputStream;
49 import java.security.MessageDigest;
50 import java.util.Arrays;
51 import java.util.Comparator;
53 import org.spearce.jgit.errors.CorruptObjectException;
54 import org.spearce.jgit.errors.UnmergedPathException;
55 import org.spearce.jgit.lib.Constants;
56 import org.spearce.jgit.lib.LockFile;
57 import org.spearce.jgit.lib.ObjectId;
58 import org.spearce.jgit.lib.ObjectWriter;
59 import org.spearce.jgit.lib.Repository;
60 import org.spearce.jgit.util.MutableInteger;
61 import org.spearce.jgit.util.NB;
62 import org.spearce.jgit.util.TemporaryBuffer;
64 /**
65 * Support for the Git dircache (aka index file).
66 * <p>
67 * The index file keeps track of which objects are currently checked out in the
68 * working directory, and the last modified time of those working files. Changes
69 * in the working directory can be detected by comparing the modification times
70 * to the cached modification time within the index file.
71 * <p>
72 * Index files are also used during merges, where the merge happens within the
73 * index file first, and the working directory is updated as a post-merge step.
74 * Conflicts are stored in the index file to allow tool (and human) based
75 * resolutions to be easily performed.
77 public class DirCache {
78 private static final byte[] SIG_DIRC = { 'D', 'I', 'R', 'C' };
80 private static final int EXT_TREE = 0x54524545 /* 'TREE' */;
82 private static final int INFO_LEN = DirCacheEntry.INFO_LEN;
84 private static final DirCacheEntry[] NO_ENTRIES = {};
86 static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
87 public int compare(final DirCacheEntry o1, final DirCacheEntry o2) {
88 final int cr = cmp(o1, o2);
89 if (cr != 0)
90 return cr;
91 return o1.getStage() - o2.getStage();
95 static int cmp(final DirCacheEntry a, final DirCacheEntry b) {
96 return cmp(a.path, a.path.length, b);
99 static int cmp(final byte[] aPath, final int aLen, final DirCacheEntry b) {
100 return cmp(aPath, aLen, b.path, b.path.length);
103 static int cmp(final byte[] aPath, final int aLen, final byte[] bPath,
104 final int bLen) {
105 for (int cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
106 final int cmp = (aPath[cPos] & 0xff) - (bPath[cPos] & 0xff);
107 if (cmp != 0)
108 return cmp;
110 return aLen - bLen;
114 * Create a new empty index which is never stored on disk.
116 * @return an empty cache which has no backing store file. The cache may not
117 * be read or written, but it may be queried and updated (in
118 * memory).
120 public static DirCache newInCore() {
121 return new DirCache(null);
125 * Create a new in-core index representation and read an index from disk.
126 * <p>
127 * The new index will be read before it is returned to the caller. Read
128 * failures are reported as exceptions and therefore prevent the method from
129 * returning a partially populated index.
131 * @param indexLocation
132 * location of the index file on disk.
133 * @return a cache representing the contents of the specified index file (if
134 * it exists) or an empty cache if the file does not exist.
135 * @throws IOException
136 * the index file is present but could not be read.
137 * @throws CorruptObjectException
138 * the index file is using a format or extension that this
139 * library does not support.
141 public static DirCache read(final File indexLocation)
142 throws CorruptObjectException, IOException {
143 final DirCache c = new DirCache(indexLocation);
144 c.read();
145 return c;
149 * Create a new in-core index representation and read an index from disk.
150 * <p>
151 * The new index will be read before it is returned to the caller. Read
152 * failures are reported as exceptions and therefore prevent the method from
153 * returning a partially populated index.
155 * @param db
156 * repository the caller wants to read the default index of.
157 * @return a cache representing the contents of the specified index file (if
158 * it exists) or an empty cache if the file does not exist.
159 * @throws IOException
160 * the index file is present but could not be read.
161 * @throws CorruptObjectException
162 * the index file is using a format or extension that this
163 * library does not support.
165 public static DirCache read(final Repository db)
166 throws CorruptObjectException, IOException {
167 return read(new File(db.getDirectory(), "index"));
171 * Create a new in-core index representation, lock it, and read from disk.
172 * <p>
173 * The new index will be locked and then read before it is returned to the
174 * caller. Read failures are reported as exceptions and therefore prevent
175 * the method from returning a partially populated index. On read failure,
176 * the lock is released.
178 * @param indexLocation
179 * location of the index file on disk.
180 * @return a cache representing the contents of the specified index file (if
181 * it exists) or an empty cache if the file does not exist.
182 * @throws IOException
183 * the index file is present but could not be read, or the lock
184 * could not be obtained.
185 * @throws CorruptObjectException
186 * the index file is using a format or extension that this
187 * library does not support.
189 public static DirCache lock(final File indexLocation)
190 throws CorruptObjectException, IOException {
191 final DirCache c = new DirCache(indexLocation);
192 if (!c.lock())
193 throw new IOException("Cannot lock " + indexLocation);
195 try {
196 c.read();
197 } catch (IOException e) {
198 c.unlock();
199 throw e;
200 } catch (RuntimeException e) {
201 c.unlock();
202 throw e;
203 } catch (Error e) {
204 c.unlock();
205 throw e;
208 return c;
212 * Create a new in-core index representation, lock it, and read from disk.
213 * <p>
214 * The new index will be locked and then read before it is returned to the
215 * caller. Read failures are reported as exceptions and therefore prevent
216 * the method from returning a partially populated index.
218 * @param db
219 * repository the caller wants to read the default index of.
220 * @return a cache representing the contents of the specified index file (if
221 * it exists) or an empty cache if the file does not exist.
222 * @throws IOException
223 * the index file is present but could not be read, or the lock
224 * could not be obtained.
225 * @throws CorruptObjectException
226 * the index file is using a format or extension that this
227 * library does not support.
229 public static DirCache lock(final Repository db)
230 throws CorruptObjectException, IOException {
231 return lock(new File(db.getDirectory(), "index"));
234 /** Location of the current version of the index file. */
235 private final File liveFile;
237 /** Modification time of the file at the last read/write we did. */
238 private long lastModified;
240 /** Individual file index entries, sorted by path name. */
241 private DirCacheEntry[] sortedEntries;
243 /** Number of positions within {@link #sortedEntries} that are valid. */
244 private int entryCnt;
246 /** Cache tree for this index; null if the cache tree is not available. */
247 private DirCacheTree tree;
249 /** Our active lock (if we hold it); null if we don't have it locked. */
250 private LockFile myLock;
253 * Create a new in-core index representation.
254 * <p>
255 * The new index will be empty. Callers may wish to read from the on disk
256 * file first with {@link #read()}.
258 * @param indexLocation
259 * location of the index file on disk.
261 public DirCache(final File indexLocation) {
262 liveFile = indexLocation;
263 clear();
267 * Create a new builder to update this cache.
268 * <p>
269 * Callers should add all entries to the builder, then use
270 * {@link DirCacheBuilder#finish()} to update this instance.
272 * @return a new builder instance for this cache.
274 public DirCacheBuilder builder() {
275 return new DirCacheBuilder(this, entryCnt + 16);
279 * Create a new editor to recreate this cache.
280 * <p>
281 * Callers should add commands to the editor, then use
282 * {@link DirCacheEditor#finish()} to update this instance.
284 * @return a new builder instance for this cache.
286 public DirCacheEditor editor() {
287 return new DirCacheEditor(this, entryCnt + 16);
290 void replace(final DirCacheEntry[] e, final int cnt) {
291 sortedEntries = e;
292 entryCnt = cnt;
293 tree = null;
297 * Read the index from disk, if it has changed on disk.
298 * <p>
299 * This method tries to avoid loading the index if it has not changed since
300 * the last time we consulted it. A missing index file will be treated as
301 * though it were present but had no file entries in it.
303 * @throws IOException
304 * the index file is present but could not be read. This
305 * DirCache instance may not be populated correctly.
306 * @throws CorruptObjectException
307 * the index file is using a format or extension that this
308 * library does not support.
310 public void read() throws IOException, CorruptObjectException {
311 if (liveFile == null)
312 throw new IOException("DirCache does not have a backing file");
313 if (!liveFile.exists())
314 clear();
315 else if (liveFile.lastModified() != lastModified) {
316 try {
317 final FileInputStream inStream = new FileInputStream(liveFile);
318 try {
319 clear();
320 readFrom(inStream);
321 } finally {
322 try {
323 inStream.close();
324 } catch (IOException err2) {
325 // Ignore any close failures.
328 } catch (FileNotFoundException fnfe) {
329 // Someone must have deleted it between our exists test
330 // and actually opening the path. That's fine, its empty.
332 clear();
337 /** Empty this index, removing all entries. */
338 public void clear() {
339 lastModified = 0;
340 sortedEntries = NO_ENTRIES;
341 entryCnt = 0;
342 tree = null;
345 private void readFrom(final FileInputStream inStream) throws IOException,
346 CorruptObjectException {
347 final BufferedInputStream in = new BufferedInputStream(inStream);
348 final MessageDigest md = Constants.newMessageDigest();
350 // Read the index header and verify we understand it.
352 final byte[] hdr = new byte[20];
353 NB.readFully(in, hdr, 0, 12);
354 md.update(hdr, 0, 12);
355 if (!is_DIRC(hdr))
356 throw new CorruptObjectException("Not a DIRC file.");
357 final int ver = NB.decodeInt32(hdr, 4);
358 if (ver != 2)
359 throw new CorruptObjectException("Unknown DIRC version " + ver);
360 entryCnt = NB.decodeInt32(hdr, 8);
361 if (entryCnt < 0)
362 throw new CorruptObjectException("DIRC has too many entries.");
364 // Load the individual file entries.
366 final byte[] infos = new byte[INFO_LEN * entryCnt];
367 sortedEntries = new DirCacheEntry[entryCnt];
368 for (int i = 0; i < entryCnt; i++)
369 sortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in, md);
370 lastModified = liveFile.lastModified();
372 // After the file entries are index extensions, and then a footer.
374 for (;;) {
375 in.mark(21);
376 NB.readFully(in, hdr, 0, 20);
377 if (in.read() < 0) {
378 // No extensions present; the file ended where we expected.
380 break;
382 in.reset();
384 switch (NB.decodeInt32(hdr, 0)) {
385 case EXT_TREE: {
386 final byte[] raw = new byte[NB.decodeInt32(hdr, 4)];
387 md.update(hdr, 0, 8);
388 NB.skipFully(in, 8);
389 NB.readFully(in, raw, 0, raw.length);
390 md.update(raw, 0, raw.length);
391 tree = new DirCacheTree(raw, new MutableInteger(), null);
392 break;
394 default:
395 if (hdr[0] >= 'A' && hdr[0] <= 'Z') {
396 // The extension is optional and is here only as
397 // a performance optimization. Since we do not
398 // understand it, we can safely skip past it.
400 NB.skipFully(in, NB.decodeUInt32(hdr, 4));
401 } else {
402 // The extension is not an optimization and is
403 // _required_ to understand this index format.
404 // Since we did not trap it above we must abort.
406 throw new CorruptObjectException("DIRC extension '"
407 + Constants.CHARSET.decode(
408 ByteBuffer.wrap(hdr, 0, 4)).toString()
409 + "' not supported by this version.");
414 final byte[] exp = md.digest();
415 if (!Arrays.equals(exp, hdr)) {
416 throw new CorruptObjectException("DIRC checksum mismatch");
420 private static boolean is_DIRC(final byte[] hdr) {
421 if (hdr.length < SIG_DIRC.length)
422 return false;
423 for (int i = 0; i < SIG_DIRC.length; i++)
424 if (hdr[i] != SIG_DIRC[i])
425 return false;
426 return true;
430 * Try to establish an update lock on the cache file.
432 * @return true if the lock is now held by the caller; false if it is held
433 * by someone else.
434 * @throws IOException
435 * the output file could not be created. The caller does not
436 * hold the lock.
438 public boolean lock() throws IOException {
439 if (liveFile == null)
440 throw new IOException("DirCache does not have a backing file");
441 final LockFile tmp = new LockFile(liveFile);
442 if (tmp.lock()) {
443 tmp.setNeedStatInformation(true);
444 myLock = tmp;
445 return true;
447 return false;
451 * Write the entry records from memory to disk.
452 * <p>
453 * The cache must be locked first by calling {@link #lock()} and receiving
454 * true as the return value. Applications are encouraged to lock the index,
455 * then invoke {@link #read()} to ensure the in-memory data is current,
456 * prior to updating the in-memory entries.
457 * <p>
458 * Once written the lock is closed and must be either committed with
459 * {@link #commit()} or rolled back with {@link #unlock()}.
461 * @throws IOException
462 * the output file could not be created. The caller no longer
463 * holds the lock.
465 public void write() throws IOException {
466 final LockFile tmp = myLock;
467 requireLocked(tmp);
468 try {
469 writeTo(new BufferedOutputStream(tmp.getOutputStream()));
470 } catch (IOException err) {
471 tmp.unlock();
472 throw err;
473 } catch (RuntimeException err) {
474 tmp.unlock();
475 throw err;
476 } catch (Error err) {
477 tmp.unlock();
478 throw err;
482 private void writeTo(final OutputStream os) throws IOException {
483 final MessageDigest foot = Constants.newMessageDigest();
484 final DigestOutputStream dos = new DigestOutputStream(os, foot);
486 // Write the header.
488 final byte[] tmp = new byte[128];
489 System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
490 NB.encodeInt32(tmp, 4, /* version */2);
491 NB.encodeInt32(tmp, 8, entryCnt);
492 dos.write(tmp, 0, 12);
494 // Write the individual file entries.
496 if (lastModified <= 0) {
497 // Write a new index, as no entries require smudging.
499 for (int i = 0; i < entryCnt; i++)
500 sortedEntries[i].write(dos);
501 } else {
502 final int smudge_s = (int) (lastModified / 1000);
503 final int smudge_ns = ((int) (lastModified % 1000)) * 1000000;
504 for (int i = 0; i < entryCnt; i++) {
505 final DirCacheEntry e = sortedEntries[i];
506 if (e.mightBeRacilyClean(smudge_s, smudge_ns))
507 e.smudgeRacilyClean();
508 e.write(dos);
512 if (tree != null) {
513 final TemporaryBuffer bb = new TemporaryBuffer();
514 tree.write(tmp, bb);
515 bb.close();
517 NB.encodeInt32(tmp, 0, EXT_TREE);
518 NB.encodeInt32(tmp, 4, (int) bb.length());
519 dos.write(tmp, 0, 8);
520 bb.writeTo(dos, null);
523 os.write(foot.digest());
524 os.close();
528 * Commit this change and release the lock.
529 * <p>
530 * If this method fails (returns false) the lock is still released.
532 * @return true if the commit was successful and the file contains the new
533 * data; false if the commit failed and the file remains with the
534 * old data.
535 * @throws IllegalStateException
536 * the lock is not held.
538 public boolean commit() {
539 final LockFile tmp = myLock;
540 requireLocked(tmp);
541 myLock = null;
542 if (!tmp.commit())
543 return false;
544 lastModified = tmp.getCommitLastModified();
545 return true;
548 private void requireLocked(final LockFile tmp) {
549 if (liveFile == null)
550 throw new IllegalStateException("DirCache is not locked");
551 if (tmp == null)
552 throw new IllegalStateException("DirCache "
553 + liveFile.getAbsolutePath() + " not locked.");
557 * Unlock this file and abort this change.
558 * <p>
559 * The temporary file (if created) is deleted before returning.
561 public void unlock() {
562 final LockFile tmp = myLock;
563 if (tmp != null) {
564 myLock = null;
565 tmp.unlock();
570 * Locate the position a path's entry is at in the index.
571 * <p>
572 * If there is at least one entry in the index for this path the position of
573 * the lowest stage is returned. Subsequent stages can be identified by
574 * testing consecutive entries until the path differs.
575 * <p>
576 * If no path matches the entry -(position+1) is returned, where position is
577 * the location it would have gone within the index.
579 * @param path
580 * the path to search for.
581 * @return if >= 0 then the return value is the position of the entry in the
582 * index; pass to {@link #getEntry(int)} to obtain the entry
583 * information. If < 0 the entry does not exist in the index.
585 public int findEntry(final String path) {
586 if (entryCnt == 0)
587 return -1;
588 final byte[] p = Constants.encode(path);
589 return findEntry(p, p.length);
592 int findEntry(final byte[] p, final int pLen) {
593 int low = 0;
594 int high = entryCnt;
595 do {
596 int mid = (low + high) >>> 1;
597 final int cmp = cmp(p, pLen, sortedEntries[mid]);
598 if (cmp < 0)
599 high = mid;
600 else if (cmp == 0) {
601 while (mid > 0 && cmp(p, pLen, sortedEntries[mid - 1]) == 0)
602 mid--;
603 return mid;
604 } else
605 low = mid + 1;
606 } while (low < high);
607 return -(low + 1);
611 * Determine the next index position past all entries with the same name.
612 * <p>
613 * As index entries are sorted by path name, then stage number, this method
614 * advances the supplied position to the first position in the index whose
615 * path name does not match the path name of the supplied position's entry.
617 * @param position
618 * entry position of the path that should be skipped.
619 * @return position of the next entry whose path is after the input.
621 public int nextEntry(final int position) {
622 DirCacheEntry last = sortedEntries[position];
623 int nextIdx = position + 1;
624 while (nextIdx < entryCnt) {
625 final DirCacheEntry next = sortedEntries[nextIdx];
626 if (cmp(last, next) != 0)
627 break;
628 last = next;
629 nextIdx++;
631 return nextIdx;
634 int nextEntry(final byte[] p, final int pLen, int nextIdx) {
635 while (nextIdx < entryCnt) {
636 final DirCacheEntry next = sortedEntries[nextIdx];
637 if (!DirCacheTree.peq(p, next.path, pLen))
638 break;
639 nextIdx++;
641 return nextIdx;
645 * Total number of file entries stored in the index.
646 * <p>
647 * This count includes unmerged stages for a file entry if the file is
648 * currently conflicted in a merge. This means the total number of entries
649 * in the index may be up to 3 times larger than the number of files in the
650 * working directory.
651 * <p>
652 * Note that this value counts only <i>files</i>.
654 * @return number of entries available.
655 * @see #getEntry(int)
657 public int getEntryCount() {
658 return entryCnt;
662 * Get a specific entry.
664 * @param i
665 * position of the entry to get.
666 * @return the entry at position <code>i</code>.
668 public DirCacheEntry getEntry(final int i) {
669 return sortedEntries[i];
673 * Get a specific entry.
675 * @param path
676 * the path to search for.
677 * @return the entry at position <code>i</code>.
679 public DirCacheEntry getEntry(final String path) {
680 final int i = findEntry(path);
681 return i < 0 ? null : sortedEntries[i];
685 * Recursively get all entries within a subtree.
687 * @param path
688 * the subtree path to get all entries within.
689 * @return all entries recursively contained within the subtree.
691 public DirCacheEntry[] getEntriesWithin(String path) {
692 if (!path.endsWith("/"))
693 path += "/";
694 final byte[] p = Constants.encode(path);
695 final int pLen = p.length;
697 int eIdx = findEntry(p, pLen);
698 if (eIdx < 0)
699 eIdx = -(eIdx + 1);
700 final int lastIdx = nextEntry(p, pLen, eIdx);
701 final DirCacheEntry[] r = new DirCacheEntry[lastIdx - eIdx];
702 System.arraycopy(sortedEntries, eIdx, r, 0, r.length);
703 return r;
706 void toArray(final int i, final DirCacheEntry[] dst, final int off,
707 final int cnt) {
708 System.arraycopy(sortedEntries, i, dst, off, cnt);
712 * Obtain (or build) the current cache tree structure.
713 * <p>
714 * This method can optionally recreate the cache tree, without flushing the
715 * tree objects themselves to disk.
717 * @param build
718 * if true and the cache tree is not present in the index it will
719 * be generated and returned to the caller.
720 * @return the cache tree; null if there is no current cache tree available
721 * and <code>build</code> was false.
723 public DirCacheTree getCacheTree(final boolean build) {
724 if (build) {
725 if (tree == null)
726 tree = new DirCacheTree();
727 tree.validate(sortedEntries, entryCnt, 0, 0);
729 return tree;
733 * Write all index trees to the object store, returning the root tree.
735 * @param ow
736 * the writer to use when serializing to the store.
737 * @return identity for the root tree.
738 * @throws UnmergedPathException
739 * one or more paths contain higher-order stages (stage > 0),
740 * which cannot be stored in a tree object.
741 * @throws IOException
742 * an unexpected error occurred writing to the object store.
744 public ObjectId writeTree(final ObjectWriter ow)
745 throws UnmergedPathException, IOException {
746 return getCacheTree(true).writeTree(sortedEntries, 0, 0, ow);