Remove System.out.println from RevWalkFilterTest
[egit/chris.git] / org.spearce.jgit / src / org / spearce / jgit / dircache / DirCacheTree.java
blob9d5af7083f08d87e75c9099da8ff7e8877a3ce19
1 /*
2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3 * Copyright (C) 2008, Google Inc.
5 * All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
9 * conditions are met:
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
14 * - Redistributions in binary form must reproduce the above
15 * copyright notice, this list of conditions and the following
16 * disclaimer in the documentation and/or other materials provided
17 * with the distribution.
19 * - Neither the name of the Git Development Community nor the
20 * names of its contributors may be used to endorse or promote
21 * products derived from this software without specific prior
22 * written permission.
24 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
25 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
26 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
27 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
28 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
29 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
32 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
33 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
34 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
39 package org.spearce.jgit.dircache;
41 import static org.spearce.jgit.lib.Constants.OBJECT_ID_LENGTH;
43 import java.io.ByteArrayOutputStream;
44 import java.io.IOException;
45 import java.io.OutputStream;
46 import java.nio.ByteBuffer;
47 import java.util.Arrays;
48 import java.util.Comparator;
50 import org.spearce.jgit.errors.UnmergedPathException;
51 import org.spearce.jgit.lib.Constants;
52 import org.spearce.jgit.lib.FileMode;
53 import org.spearce.jgit.lib.ObjectId;
54 import org.spearce.jgit.lib.ObjectWriter;
55 import org.spearce.jgit.util.MutableInteger;
56 import org.spearce.jgit.util.RawParseUtils;
58 /**
59 * Single tree record from the 'TREE' {@link DirCache} extension.
60 * <p>
61 * A valid cache tree record contains the object id of a tree object and the
62 * total number of {@link DirCacheEntry} instances (counted recursively) from
63 * the DirCache contained within the tree. This information facilitates faster
64 * traversal of the index and quicker generation of tree objects prior to
65 * creating a new commit.
66 * <p>
67 * An invalid cache tree record indicates a known subtree whose file entries
68 * have changed in ways that cause the tree to no longer have a known object id.
69 * Invalid cache tree records must be revalidated prior to use.
71 public class DirCacheTree {
72 private static final byte[] NO_NAME = {};
74 private static final DirCacheTree[] NO_CHILDREN = {};
76 private static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() {
77 public int compare(final DirCacheTree o1, final DirCacheTree o2) {
78 final byte[] a = o1.encodedName;
79 final byte[] b = o2.encodedName;
80 final int aLen = a.length;
81 final int bLen = b.length;
82 int cPos;
83 for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
84 final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);
85 if (cmp != 0)
86 return cmp;
88 if (aLen == bLen)
89 return 0;
90 if (aLen < bLen)
91 return '/' - (b[cPos] & 0xff);
92 return (a[cPos] & 0xff) - '/';
96 /** Tree this tree resides in; null if we are the root. */
97 private DirCacheTree parent;
99 /** Name of this tree within its parent. */
100 private byte[] encodedName;
102 /** Number of {@link DirCacheEntry} records that belong to this tree. */
103 private int entrySpan;
105 /** Unique SHA-1 of this tree; null if invalid. */
106 private ObjectId id;
108 /** Child trees, if any, sorted by {@link #encodedName}. */
109 private DirCacheTree[] children;
111 /** Number of valid children in {@link #children}. */
112 private int childCnt;
114 DirCacheTree() {
115 encodedName = NO_NAME;
116 children = NO_CHILDREN;
117 childCnt = 0;
118 entrySpan = -1;
121 private DirCacheTree(final DirCacheTree myParent, final byte[] path,
122 final int pathOff, final int pathLen) {
123 parent = myParent;
124 encodedName = new byte[pathLen];
125 System.arraycopy(path, pathOff, encodedName, 0, pathLen);
126 children = NO_CHILDREN;
127 childCnt = 0;
128 entrySpan = -1;
131 DirCacheTree(final byte[] in, final MutableInteger off,
132 final DirCacheTree myParent) {
133 parent = myParent;
135 int ptr = RawParseUtils.next(in, off.value, '\0');
136 final int nameLen = ptr - off.value - 1;
137 if (nameLen > 0) {
138 encodedName = new byte[nameLen];
139 System.arraycopy(in, off.value, encodedName, 0, nameLen);
140 } else
141 encodedName = NO_NAME;
143 entrySpan = RawParseUtils.parseBase10(in, ptr, off);
144 final int subcnt = RawParseUtils.parseBase10(in, off.value, off);
145 off.value = RawParseUtils.next(in, off.value, '\n');
147 if (entrySpan >= 0) {
148 // Valid trees have a positive entry count and an id of a
149 // tree object that should exist in the object database.
151 id = ObjectId.fromRaw(in, off.value);
152 off.value += Constants.OBJECT_ID_LENGTH;
155 if (subcnt > 0) {
156 boolean alreadySorted = true;
157 children = new DirCacheTree[subcnt];
158 for (int i = 0; i < subcnt; i++) {
159 children[i] = new DirCacheTree(in, off, this);
161 // C Git's ordering differs from our own; it prefers to
162 // sort by length first. This sometimes produces a sort
163 // we do not desire. On the other hand it may have been
164 // created by us, and be sorted the way we want.
166 if (alreadySorted && i > 0
167 && TREE_CMP.compare(children[i - 1], children[i]) > 0)
168 alreadySorted = false;
170 if (!alreadySorted)
171 Arrays.sort(children, 0, subcnt, TREE_CMP);
172 } else {
173 // Leaf level trees have no children, only (file) entries.
175 children = NO_CHILDREN;
177 childCnt = subcnt;
180 void write(final byte[] tmp, final OutputStream os) throws IOException {
181 int ptr = tmp.length;
182 tmp[--ptr] = '\n';
183 ptr = RawParseUtils.formatBase10(tmp, ptr, childCnt);
184 tmp[--ptr] = ' ';
185 ptr = RawParseUtils.formatBase10(tmp, ptr, isValid() ? entrySpan : -1);
186 tmp[--ptr] = 0;
188 os.write(encodedName);
189 os.write(tmp, ptr, tmp.length - ptr);
190 if (isValid()) {
191 id.copyRawTo(tmp, 0);
192 os.write(tmp, 0, Constants.OBJECT_ID_LENGTH);
194 for (int i = 0; i < childCnt; i++)
195 children[i].write(tmp, os);
199 * Determine if this cache is currently valid.
200 * <p>
201 * A valid cache tree knows how many {@link DirCacheEntry} instances from
202 * the parent {@link DirCache} reside within this tree (recursively
203 * enumerated). It also knows the object id of the tree, as the tree should
204 * be readily available from the repository's object database.
206 * @return true if this tree is knows key details about itself; false if the
207 * tree needs to be regenerated.
209 public boolean isValid() {
210 return id != null;
214 * Get the number of entries this tree spans within the DirCache.
215 * <p>
216 * If this tree is not valid (see {@link #isValid()}) this method's return
217 * value is always strictly negative (less than 0) but is otherwise an
218 * undefined result.
220 * @return total number of entries (recursively) contained within this tree.
222 public int getEntrySpan() {
223 return entrySpan;
227 * Get the number of cached subtrees contained within this tree.
229 * @return number of child trees available through this tree.
231 public int getChildCount() {
232 return childCnt;
236 * Get the i-th child cache tree.
238 * @param i
239 * index of the child to obtain.
240 * @return the child tree.
242 public DirCacheTree getChild(final int i) {
243 return children[i];
246 ObjectId getObjectId() {
247 return id;
251 * Get the tree's name within its parent.
252 * <p>
253 * This method is not very efficient and is primarily meant for debugging
254 * and final output generation. Applications should try to avoid calling it,
255 * and if invoked do so only once per interesting entry, where the name is
256 * absolutely required for correct function.
258 * @return name of the tree. This does not contain any '/' characters.
260 public String getNameString() {
261 final ByteBuffer bb = ByteBuffer.wrap(encodedName);
262 return Constants.CHARSET.decode(bb).toString();
266 * Get the tree's path within the repository.
267 * <p>
268 * This method is not very efficient and is primarily meant for debugging
269 * and final output generation. Applications should try to avoid calling it,
270 * and if invoked do so only once per interesting entry, where the name is
271 * absolutely required for correct function.
273 * @return path of the tree, relative to the repository root. If this is not
274 * the root tree the path ends with '/'. The root tree's path string
275 * is the empty string ("").
277 public String getPathString() {
278 final StringBuilder r = new StringBuilder();
279 appendName(r);
280 return r.toString();
284 * Write (if necessary) this tree to the object store.
286 * @param cache
287 * the complete cache from DirCache.
288 * @param cIdx
289 * first position of <code>cache</code> that is a member of this
290 * tree. The path of <code>cache[cacheIdx].path</code> for the
291 * range <code>[0,pathOff-1)</code> matches the complete path of
292 * this tree, from the root of the repository.
293 * @param pathOffset
294 * number of bytes of <code>cache[cacheIdx].path</code> that
295 * matches this tree's path. The value at array position
296 * <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
297 * <code>pathOff</code> is > 0.
298 * @param ow
299 * the writer to use when serializing to the store.
300 * @return identity of this tree.
301 * @throws UnmergedPathException
302 * one or more paths contain higher-order stages (stage > 0),
303 * which cannot be stored in a tree object.
304 * @throws IOException
305 * an unexpected error occurred writing to the object store.
307 ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
308 final int pathOffset, final ObjectWriter ow)
309 throws UnmergedPathException, IOException {
310 if (id == null) {
311 final int endIdx = cIdx + entrySpan;
312 final int size = computeSize(cache, cIdx, pathOffset, ow);
313 final ByteArrayOutputStream out = new ByteArrayOutputStream(size);
314 int childIdx = 0;
315 int entryIdx = cIdx;
317 while (entryIdx < endIdx) {
318 final DirCacheEntry e = cache[entryIdx];
319 final byte[] ep = e.path;
320 if (childIdx < childCnt) {
321 final DirCacheTree st = children[childIdx];
322 if (st.contains(ep, pathOffset, ep.length)) {
323 FileMode.TREE.copyTo(out);
324 out.write(' ');
325 out.write(st.encodedName);
326 out.write(0);
327 st.id.copyRawTo(out);
329 entryIdx += st.entrySpan;
330 childIdx++;
331 continue;
335 e.getFileMode().copyTo(out);
336 out.write(' ');
337 out.write(ep, pathOffset, ep.length - pathOffset);
338 out.write(0);
339 out.write(e.idBuffer(), e.idOffset(), OBJECT_ID_LENGTH);
340 entryIdx++;
343 id = ow.writeCanonicalTree(out.toByteArray());
345 return id;
348 private int computeSize(final DirCacheEntry[] cache, int cIdx,
349 final int pathOffset, final ObjectWriter ow)
350 throws UnmergedPathException, IOException {
351 final int endIdx = cIdx + entrySpan;
352 int childIdx = 0;
353 int entryIdx = cIdx;
354 int size = 0;
356 while (entryIdx < endIdx) {
357 final DirCacheEntry e = cache[entryIdx];
358 if (e.getStage() != 0)
359 throw new UnmergedPathException(e);
361 final byte[] ep = e.path;
362 if (childIdx < childCnt) {
363 final DirCacheTree st = children[childIdx];
364 if (st.contains(ep, pathOffset, ep.length)) {
365 final int stOffset = pathOffset + st.nameLength() + 1;
366 st.writeTree(cache, entryIdx, stOffset, ow);
368 size += FileMode.TREE.copyToLength();
369 size += st.nameLength();
370 size += OBJECT_ID_LENGTH + 2;
372 entryIdx += st.entrySpan;
373 childIdx++;
374 continue;
378 final FileMode mode = e.getFileMode();
379 if (mode.getObjectType() == Constants.OBJ_BAD)
380 throw new UnmergedPathException(e);
382 size += mode.copyToLength();
383 size += ep.length - pathOffset;
384 size += OBJECT_ID_LENGTH + 2;
385 entryIdx++;
388 return size;
391 private void appendName(final StringBuilder r) {
392 if (parent != null) {
393 parent.appendName(r);
394 r.append(getNameString());
395 r.append('/');
396 } else if (nameLength() > 0) {
397 r.append(getNameString());
398 r.append('/');
402 final int nameLength() {
403 return encodedName.length;
406 final boolean contains(final byte[] a, int aOff, final int aLen) {
407 final byte[] e = encodedName;
408 final int eLen = e.length;
409 for (int eOff = 0; eOff < eLen && aOff < aLen; eOff++, aOff++)
410 if (e[eOff] != a[aOff])
411 return false;
412 if (aOff == aLen)
413 return false;
414 return a[aOff] == '/';
418 * Update (if necessary) this tree's entrySpan.
420 * @param cache
421 * the complete cache from DirCache.
422 * @param cCnt
423 * number of entries in <code>cache</code> that are valid for
424 * iteration.
425 * @param cIdx
426 * first position of <code>cache</code> that is a member of this
427 * tree. The path of <code>cache[cacheIdx].path</code> for the
428 * range <code>[0,pathOff-1)</code> matches the complete path of
429 * this tree, from the root of the repository.
430 * @param pathOff
431 * number of bytes of <code>cache[cacheIdx].path</code> that
432 * matches this tree's path. The value at array position
433 * <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
434 * <code>pathOff</code> is > 0.
436 void validate(final DirCacheEntry[] cache, final int cCnt, int cIdx,
437 final int pathOff) {
438 if (entrySpan >= 0) {
439 // If we are valid, our children are also valid.
440 // We have no need to validate them.
442 return;
445 entrySpan = 0;
446 if (cCnt == 0) {
447 // Special case of an empty index, and we are the root tree.
449 return;
452 final byte[] firstPath = cache[cIdx].path;
453 int stIdx = 0;
454 while (cIdx < cCnt) {
455 final byte[] currPath = cache[cIdx].path;
456 if (pathOff > 0 && !peq(firstPath, currPath, pathOff)) {
457 // The current entry is no longer in this tree. Our
458 // span is updated and the remainder goes elsewhere.
460 break;
463 DirCacheTree st = stIdx < childCnt ? children[stIdx] : null;
464 final int cc = namecmp(currPath, pathOff, st);
465 if (cc > 0) {
466 // This subtree is now empty.
468 removeChild(stIdx);
469 continue;
472 if (cc < 0) {
473 final int p = slash(currPath, pathOff);
474 if (p < 0) {
475 // The entry has no '/' and thus is directly in this
476 // tree. Count it as one of our own.
478 cIdx++;
479 entrySpan++;
480 continue;
483 // Build a new subtree for this entry.
485 st = new DirCacheTree(this, currPath, pathOff, p - pathOff);
486 insertChild(stIdx, st);
489 // The entry is contained in this subtree.
491 st.validate(cache, cCnt, cIdx, pathOff + st.nameLength() + 1);
492 cIdx += st.entrySpan;
493 entrySpan += st.entrySpan;
494 stIdx++;
497 if (stIdx < childCnt) {
498 // None of our remaining children can be in this tree
499 // as the current cache entry is after our own name.
501 final DirCacheTree[] dct = new DirCacheTree[stIdx];
502 System.arraycopy(children, 0, dct, 0, stIdx);
503 children = dct;
507 private void insertChild(final int stIdx, final DirCacheTree st) {
508 final DirCacheTree[] c = children;
509 if (childCnt + 1 <= c.length) {
510 if (stIdx < childCnt)
511 System.arraycopy(c, stIdx, c, stIdx + 1, childCnt - stIdx);
512 c[stIdx] = st;
513 childCnt++;
514 return;
517 final int n = c.length;
518 final DirCacheTree[] a = new DirCacheTree[n + 1];
519 if (stIdx > 0)
520 System.arraycopy(c, 0, a, 0, stIdx);
521 a[stIdx] = st;
522 if (stIdx < n)
523 System.arraycopy(c, stIdx, a, stIdx + 1, n - stIdx);
524 children = a;
525 childCnt++;
528 private void removeChild(final int stIdx) {
529 final int n = --childCnt;
530 if (stIdx < n)
531 System.arraycopy(children, stIdx + 1, children, stIdx, n - stIdx);
532 children[n] = null;
535 static boolean peq(final byte[] a, final byte[] b, int aLen) {
536 if (b.length < aLen)
537 return false;
538 for (aLen--; aLen >= 0; aLen--)
539 if (a[aLen] != b[aLen])
540 return false;
541 return true;
544 private static int namecmp(final byte[] a, int aPos, final DirCacheTree ct) {
545 if (ct == null)
546 return -1;
547 final byte[] b = ct.encodedName;
548 final int aLen = a.length;
549 final int bLen = b.length;
550 int bPos = 0;
551 for (; aPos < aLen && bPos < bLen; aPos++, bPos++) {
552 final int cmp = (a[aPos] & 0xff) - (b[bPos] & 0xff);
553 if (cmp != 0)
554 return cmp;
556 if (bPos == bLen)
557 return a[aPos] == '/' ? 0 : -1;
558 return aLen - bLen;
561 private static int slash(final byte[] a, int aPos) {
562 final int aLen = a.length;
563 for (; aPos < aLen; aPos++)
564 if (a[aPos] == '/')
565 return aPos;
566 return -1;