2 * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
3 * Copyright (C) 2008, Google Inc.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
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
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
;
59 * Single tree record from the 'TREE' {@link DirCache} extension.
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.
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
;
83 for (cPos
= 0; cPos
< aLen
&& cPos
< bLen
; cPos
++) {
84 final int cmp
= (a
[cPos
] & 0xff) - (b
[cPos
] & 0xff);
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. */
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
;
115 encodedName
= NO_NAME
;
116 children
= NO_CHILDREN
;
121 private DirCacheTree(final DirCacheTree myParent
, final byte[] path
,
122 final int pathOff
, final int pathLen
) {
124 encodedName
= new byte[pathLen
];
125 System
.arraycopy(path
, pathOff
, encodedName
, 0, pathLen
);
126 children
= NO_CHILDREN
;
131 DirCacheTree(final byte[] in
, final MutableInteger off
,
132 final DirCacheTree myParent
) {
135 int ptr
= RawParseUtils
.next(in
, off
.value
, '\0');
136 final int nameLen
= ptr
- off
.value
- 1;
138 encodedName
= new byte[nameLen
];
139 System
.arraycopy(in
, off
.value
, encodedName
, 0, nameLen
);
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
;
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;
171 Arrays
.sort(children
, 0, subcnt
, TREE_CMP
);
173 // Leaf level trees have no children, only (file) entries.
175 children
= NO_CHILDREN
;
180 void write(final byte[] tmp
, final OutputStream os
) throws IOException
{
181 int ptr
= tmp
.length
;
183 ptr
= RawParseUtils
.formatBase10(tmp
, ptr
, childCnt
);
185 ptr
= RawParseUtils
.formatBase10(tmp
, ptr
, isValid() ? entrySpan
: -1);
188 os
.write(encodedName
);
189 os
.write(tmp
, ptr
, tmp
.length
- ptr
);
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.
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() {
214 * Get the number of entries this tree spans within the DirCache.
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
220 * @return total number of entries (recursively) contained within this tree.
222 public int getEntrySpan() {
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() {
236 * Get the i-th child cache tree.
239 * index of the child to obtain.
240 * @return the child tree.
242 public DirCacheTree
getChild(final int i
) {
246 ObjectId
getObjectId() {
251 * Get the tree's name within its parent.
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.
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();
284 * Write (if necessary) this tree to the object store.
287 * the complete cache from DirCache.
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.
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.
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
{
311 final int endIdx
= cIdx
+ entrySpan
;
312 final int size
= computeSize(cache
, cIdx
, pathOffset
, ow
);
313 final ByteArrayOutputStream out
= new ByteArrayOutputStream(size
);
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
);
325 out
.write(st
.encodedName
);
327 st
.id
.copyRawTo(out
);
329 entryIdx
+= st
.entrySpan
;
335 e
.getFileMode().copyTo(out
);
337 out
.write(ep
, pathOffset
, ep
.length
- pathOffset
);
339 out
.write(e
.idBuffer(), e
.idOffset(), OBJECT_ID_LENGTH
);
343 id
= ow
.writeCanonicalTree(out
.toByteArray());
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
;
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
;
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;
391 private void appendName(final StringBuilder r
) {
392 if (parent
!= null) {
393 parent
.appendName(r
);
394 r
.append(getNameString());
396 } else if (nameLength() > 0) {
397 r
.append(getNameString());
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
])
414 return a
[aOff
] == '/';
418 * Update (if necessary) this tree's entrySpan.
421 * the complete cache from DirCache.
423 * number of entries in <code>cache</code> that are valid for
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.
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
,
438 if (entrySpan
>= 0) {
439 // If we are valid, our children are also valid.
440 // We have no need to validate them.
447 // Special case of an empty index, and we are the root tree.
452 final byte[] firstPath
= cache
[cIdx
].path
;
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.
463 DirCacheTree st
= stIdx
< childCnt ? children
[stIdx
] : null;
464 final int cc
= namecmp(currPath
, pathOff
, st
);
466 // This subtree is now empty.
473 final int p
= slash(currPath
, pathOff
);
475 // The entry has no '/' and thus is directly in this
476 // tree. Count it as one of our own.
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
;
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
);
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
);
517 final int n
= c
.length
;
518 final DirCacheTree
[] a
= new DirCacheTree
[n
+ 1];
520 System
.arraycopy(c
, 0, a
, 0, stIdx
);
523 System
.arraycopy(c
, stIdx
, a
, stIdx
+ 1, n
- stIdx
);
528 private void removeChild(final int stIdx
) {
529 final int n
= --childCnt
;
531 System
.arraycopy(children
, stIdx
+ 1, children
, stIdx
, n
- stIdx
);
535 static boolean peq(final byte[] a
, final byte[] b
, int aLen
) {
538 for (aLen
--; aLen
>= 0; aLen
--)
539 if (a
[aLen
] != b
[aLen
])
544 private static int namecmp(final byte[] a
, int aPos
, final DirCacheTree ct
) {
547 final byte[] b
= ct
.encodedName
;
548 final int aLen
= a
.length
;
549 final int bLen
= b
.length
;
551 for (; aPos
< aLen
&& bPos
< bLen
; aPos
++, bPos
++) {
552 final int cmp
= (a
[aPos
] & 0xff) - (b
[bPos
] & 0xff);
557 return a
[aPos
] == '/' ?
0 : -1;
561 private static int slash(final byte[] a
, int aPos
) {
562 final int aLen
= a
.length
;
563 for (; aPos
< aLen
; aPos
++)