You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

DirCacheTree.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
  4. * and other copyright owners as documented in the project's IP log.
  5. *
  6. * This program and the accompanying materials are made available
  7. * under the terms of the Eclipse Distribution License v1.0 which
  8. * accompanies this distribution, is reproduced below, and is
  9. * available at http://www.eclipse.org/org/documents/edl-v10.php
  10. *
  11. * All rights reserved.
  12. *
  13. * Redistribution and use in source and binary forms, with or
  14. * without modification, are permitted provided that the following
  15. * conditions are met:
  16. *
  17. * - Redistributions of source code must retain the above copyright
  18. * notice, this list of conditions and the following disclaimer.
  19. *
  20. * - Redistributions in binary form must reproduce the above
  21. * copyright notice, this list of conditions and the following
  22. * disclaimer in the documentation and/or other materials provided
  23. * with the distribution.
  24. *
  25. * - Neither the name of the Eclipse Foundation, Inc. nor the
  26. * names of its contributors may be used to endorse or promote
  27. * products derived from this software without specific prior
  28. * written permission.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  31. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  32. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  33. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  34. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  35. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  36. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  37. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  38. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  39. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  40. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  41. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  42. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  43. */
  44. package org.eclipse.jgit.dircache;
  45. import static java.nio.charset.StandardCharsets.UTF_8;
  46. import static org.eclipse.jgit.lib.FileMode.TREE;
  47. import static org.eclipse.jgit.lib.TreeFormatter.entrySize;
  48. import java.io.IOException;
  49. import java.io.OutputStream;
  50. import java.nio.ByteBuffer;
  51. import java.util.Arrays;
  52. import java.util.Comparator;
  53. import org.eclipse.jgit.errors.UnmergedPathException;
  54. import org.eclipse.jgit.lib.Constants;
  55. import org.eclipse.jgit.lib.ObjectId;
  56. import org.eclipse.jgit.lib.ObjectInserter;
  57. import org.eclipse.jgit.lib.TreeFormatter;
  58. import org.eclipse.jgit.util.MutableInteger;
  59. import org.eclipse.jgit.util.RawParseUtils;
  60. /**
  61. * Single tree record from the 'TREE' {@link org.eclipse.jgit.dircache.DirCache}
  62. * extension.
  63. * <p>
  64. * A valid cache tree record contains the object id of a tree object and the
  65. * total number of {@link org.eclipse.jgit.dircache.DirCacheEntry} instances
  66. * (counted recursively) from the DirCache contained within the tree. This
  67. * information facilitates faster traversal of the index and quicker generation
  68. * of tree objects prior to creating a new commit.
  69. * <p>
  70. * An invalid cache tree record indicates a known subtree whose file entries
  71. * have changed in ways that cause the tree to no longer have a known object id.
  72. * Invalid cache tree records must be revalidated prior to use.
  73. */
  74. public class DirCacheTree {
  75. private static final byte[] NO_NAME = {};
  76. private static final DirCacheTree[] NO_CHILDREN = {};
  77. private static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() {
  78. @Override
  79. public int compare(DirCacheTree o1, DirCacheTree o2) {
  80. final byte[] a = o1.encodedName;
  81. final byte[] b = o2.encodedName;
  82. final int aLen = a.length;
  83. final int bLen = b.length;
  84. int cPos;
  85. for (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {
  86. final int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);
  87. if (cmp != 0)
  88. return cmp;
  89. }
  90. if (aLen == bLen)
  91. return 0;
  92. if (aLen < bLen)
  93. return '/' - (b[cPos] & 0xff);
  94. return (a[cPos] & 0xff) - '/';
  95. }
  96. };
  97. /** Tree this tree resides in; null if we are the root. */
  98. private DirCacheTree parent;
  99. /** Name of this tree within its parent. */
  100. byte[] encodedName;
  101. /** Number of {@link DirCacheEntry} records that belong to this tree. */
  102. private int entrySpan;
  103. /** Unique SHA-1 of this tree; null if invalid. */
  104. private ObjectId id;
  105. /** Child trees, if any, sorted by {@link #encodedName}. */
  106. private DirCacheTree[] children;
  107. /** Number of valid children in {@link #children}. */
  108. private int childCnt;
  109. DirCacheTree() {
  110. encodedName = NO_NAME;
  111. children = NO_CHILDREN;
  112. childCnt = 0;
  113. entrySpan = -1;
  114. }
  115. private DirCacheTree(final DirCacheTree myParent, final byte[] path,
  116. final int pathOff, final int pathLen) {
  117. parent = myParent;
  118. encodedName = new byte[pathLen];
  119. System.arraycopy(path, pathOff, encodedName, 0, pathLen);
  120. children = NO_CHILDREN;
  121. childCnt = 0;
  122. entrySpan = -1;
  123. }
  124. DirCacheTree(final byte[] in, final MutableInteger off,
  125. final DirCacheTree myParent) {
  126. parent = myParent;
  127. int ptr = RawParseUtils.next(in, off.value, '\0');
  128. final int nameLen = ptr - off.value - 1;
  129. if (nameLen > 0) {
  130. encodedName = new byte[nameLen];
  131. System.arraycopy(in, off.value, encodedName, 0, nameLen);
  132. } else
  133. encodedName = NO_NAME;
  134. entrySpan = RawParseUtils.parseBase10(in, ptr, off);
  135. final int subcnt = RawParseUtils.parseBase10(in, off.value, off);
  136. off.value = RawParseUtils.next(in, off.value, '\n');
  137. if (entrySpan >= 0) {
  138. // Valid trees have a positive entry count and an id of a
  139. // tree object that should exist in the object database.
  140. //
  141. id = ObjectId.fromRaw(in, off.value);
  142. off.value += Constants.OBJECT_ID_LENGTH;
  143. }
  144. if (subcnt > 0) {
  145. boolean alreadySorted = true;
  146. children = new DirCacheTree[subcnt];
  147. for (int i = 0; i < subcnt; i++) {
  148. children[i] = new DirCacheTree(in, off, this);
  149. // C Git's ordering differs from our own; it prefers to
  150. // sort by length first. This sometimes produces a sort
  151. // we do not desire. On the other hand it may have been
  152. // created by us, and be sorted the way we want.
  153. //
  154. if (alreadySorted && i > 0
  155. && TREE_CMP.compare(children[i - 1], children[i]) > 0)
  156. alreadySorted = false;
  157. }
  158. if (!alreadySorted)
  159. Arrays.sort(children, 0, subcnt, TREE_CMP);
  160. } else {
  161. // Leaf level trees have no children, only (file) entries.
  162. //
  163. children = NO_CHILDREN;
  164. }
  165. childCnt = subcnt;
  166. }
  167. void write(byte[] tmp, OutputStream os) throws IOException {
  168. int ptr = tmp.length;
  169. tmp[--ptr] = '\n';
  170. ptr = RawParseUtils.formatBase10(tmp, ptr, childCnt);
  171. tmp[--ptr] = ' ';
  172. ptr = RawParseUtils.formatBase10(tmp, ptr, isValid() ? entrySpan : -1);
  173. tmp[--ptr] = 0;
  174. os.write(encodedName);
  175. os.write(tmp, ptr, tmp.length - ptr);
  176. if (isValid()) {
  177. id.copyRawTo(tmp, 0);
  178. os.write(tmp, 0, Constants.OBJECT_ID_LENGTH);
  179. }
  180. for (int i = 0; i < childCnt; i++)
  181. children[i].write(tmp, os);
  182. }
  183. /**
  184. * Determine if this cache is currently valid.
  185. * <p>
  186. * A valid cache tree knows how many
  187. * {@link org.eclipse.jgit.dircache.DirCacheEntry} instances from the parent
  188. * {@link org.eclipse.jgit.dircache.DirCache} reside within this tree
  189. * (recursively enumerated). It also knows the object id of the tree, as the
  190. * tree should be readily available from the repository's object database.
  191. *
  192. * @return true if this tree is knows key details about itself; false if the
  193. * tree needs to be regenerated.
  194. */
  195. public boolean isValid() {
  196. return id != null;
  197. }
  198. /**
  199. * Get the number of entries this tree spans within the DirCache.
  200. * <p>
  201. * If this tree is not valid (see {@link #isValid()}) this method's return
  202. * value is always strictly negative (less than 0) but is otherwise an
  203. * undefined result.
  204. *
  205. * @return total number of entries (recursively) contained within this tree.
  206. */
  207. public int getEntrySpan() {
  208. return entrySpan;
  209. }
  210. /**
  211. * Get the number of cached subtrees contained within this tree.
  212. *
  213. * @return number of child trees available through this tree.
  214. */
  215. public int getChildCount() {
  216. return childCnt;
  217. }
  218. /**
  219. * Get the i-th child cache tree.
  220. *
  221. * @param i
  222. * index of the child to obtain.
  223. * @return the child tree.
  224. */
  225. public DirCacheTree getChild(int i) {
  226. return children[i];
  227. }
  228. /**
  229. * Get the tree's ObjectId.
  230. * <p>
  231. * If {@link #isValid()} returns false this method will return null.
  232. *
  233. * @return ObjectId of this tree or null.
  234. * @since 4.3
  235. */
  236. public ObjectId getObjectId() {
  237. return id;
  238. }
  239. /**
  240. * Get the tree's name within its parent.
  241. * <p>
  242. * This method is not very efficient and is primarily meant for debugging
  243. * and final output generation. Applications should try to avoid calling it,
  244. * and if invoked do so only once per interesting entry, where the name is
  245. * absolutely required for correct function.
  246. *
  247. * @return name of the tree. This does not contain any '/' characters.
  248. */
  249. public String getNameString() {
  250. final ByteBuffer bb = ByteBuffer.wrap(encodedName);
  251. return UTF_8.decode(bb).toString();
  252. }
  253. /**
  254. * Get the tree's path within the repository.
  255. * <p>
  256. * This method is not very efficient and is primarily meant for debugging
  257. * and final output generation. Applications should try to avoid calling it,
  258. * and if invoked do so only once per interesting entry, where the name is
  259. * absolutely required for correct function.
  260. *
  261. * @return path of the tree, relative to the repository root. If this is not
  262. * the root tree the path ends with '/'. The root tree's path string
  263. * is the empty string ("").
  264. */
  265. public String getPathString() {
  266. final StringBuilder r = new StringBuilder();
  267. appendName(r);
  268. return r.toString();
  269. }
  270. /**
  271. * Write (if necessary) this tree to the object store.
  272. *
  273. * @param cache
  274. * the complete cache from DirCache.
  275. * @param cIdx
  276. * first position of <code>cache</code> that is a member of this
  277. * tree. The path of <code>cache[cacheIdx].path</code> for the
  278. * range <code>[0,pathOff-1)</code> matches the complete path of
  279. * this tree, from the root of the repository.
  280. * @param pathOffset
  281. * number of bytes of <code>cache[cacheIdx].path</code> that
  282. * matches this tree's path. The value at array position
  283. * <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
  284. * <code>pathOff</code> is > 0.
  285. * @param ow
  286. * the writer to use when serializing to the store.
  287. * @return identity of this tree.
  288. * @throws UnmergedPathException
  289. * one or more paths contain higher-order stages (stage > 0),
  290. * which cannot be stored in a tree object.
  291. * @throws IOException
  292. * an unexpected error occurred writing to the object store.
  293. */
  294. ObjectId writeTree(final DirCacheEntry[] cache, int cIdx,
  295. final int pathOffset, final ObjectInserter ow)
  296. throws UnmergedPathException, IOException {
  297. if (id == null) {
  298. final int endIdx = cIdx + entrySpan;
  299. final TreeFormatter fmt = new TreeFormatter(computeSize(cache,
  300. cIdx, pathOffset, ow));
  301. int childIdx = 0;
  302. int entryIdx = cIdx;
  303. while (entryIdx < endIdx) {
  304. final DirCacheEntry e = cache[entryIdx];
  305. final byte[] ep = e.path;
  306. if (childIdx < childCnt) {
  307. final DirCacheTree st = children[childIdx];
  308. if (st.contains(ep, pathOffset, ep.length)) {
  309. fmt.append(st.encodedName, TREE, st.id);
  310. entryIdx += st.entrySpan;
  311. childIdx++;
  312. continue;
  313. }
  314. }
  315. fmt.append(ep, pathOffset, ep.length - pathOffset, e
  316. .getFileMode(), e.idBuffer(), e.idOffset());
  317. entryIdx++;
  318. }
  319. id = ow.insert(fmt);
  320. }
  321. return id;
  322. }
  323. private int computeSize(final DirCacheEntry[] cache, int cIdx,
  324. final int pathOffset, final ObjectInserter ow)
  325. throws UnmergedPathException, IOException {
  326. final int endIdx = cIdx + entrySpan;
  327. int childIdx = 0;
  328. int entryIdx = cIdx;
  329. int size = 0;
  330. while (entryIdx < endIdx) {
  331. final DirCacheEntry e = cache[entryIdx];
  332. if (e.getStage() != 0)
  333. throw new UnmergedPathException(e);
  334. final byte[] ep = e.path;
  335. if (childIdx < childCnt) {
  336. final DirCacheTree st = children[childIdx];
  337. if (st.contains(ep, pathOffset, ep.length)) {
  338. final int stOffset = pathOffset + st.nameLength() + 1;
  339. st.writeTree(cache, entryIdx, stOffset, ow);
  340. size += entrySize(TREE, st.nameLength());
  341. entryIdx += st.entrySpan;
  342. childIdx++;
  343. continue;
  344. }
  345. }
  346. size += entrySize(e.getFileMode(), ep.length - pathOffset);
  347. entryIdx++;
  348. }
  349. return size;
  350. }
  351. private void appendName(StringBuilder r) {
  352. if (parent != null) {
  353. parent.appendName(r);
  354. r.append(getNameString());
  355. r.append('/');
  356. } else if (nameLength() > 0) {
  357. r.append(getNameString());
  358. r.append('/');
  359. }
  360. }
  361. final int nameLength() {
  362. return encodedName.length;
  363. }
  364. final boolean contains(byte[] a, int aOff, int aLen) {
  365. final byte[] e = encodedName;
  366. final int eLen = e.length;
  367. for (int eOff = 0; eOff < eLen && aOff < aLen; eOff++, aOff++)
  368. if (e[eOff] != a[aOff])
  369. return false;
  370. if (aOff >= aLen)
  371. return false;
  372. return a[aOff] == '/';
  373. }
  374. /**
  375. * Update (if necessary) this tree's entrySpan.
  376. *
  377. * @param cache
  378. * the complete cache from DirCache.
  379. * @param cCnt
  380. * number of entries in <code>cache</code> that are valid for
  381. * iteration.
  382. * @param cIdx
  383. * first position of <code>cache</code> that is a member of this
  384. * tree. The path of <code>cache[cacheIdx].path</code> for the
  385. * range <code>[0,pathOff-1)</code> matches the complete path of
  386. * this tree, from the root of the repository.
  387. * @param pathOff
  388. * number of bytes of <code>cache[cacheIdx].path</code> that
  389. * matches this tree's path. The value at array position
  390. * <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if
  391. * <code>pathOff</code> is > 0.
  392. */
  393. void validate(final DirCacheEntry[] cache, final int cCnt, int cIdx,
  394. final int pathOff) {
  395. if (entrySpan >= 0 && cIdx + entrySpan <= cCnt) {
  396. // If we are valid, our children are also valid.
  397. // We have no need to validate them.
  398. //
  399. return;
  400. }
  401. entrySpan = 0;
  402. if (cCnt == 0) {
  403. // Special case of an empty index, and we are the root tree.
  404. //
  405. return;
  406. }
  407. final byte[] firstPath = cache[cIdx].path;
  408. int stIdx = 0;
  409. while (cIdx < cCnt) {
  410. final byte[] currPath = cache[cIdx].path;
  411. if (pathOff > 0 && !peq(firstPath, currPath, pathOff)) {
  412. // The current entry is no longer in this tree. Our
  413. // span is updated and the remainder goes elsewhere.
  414. //
  415. break;
  416. }
  417. DirCacheTree st = stIdx < childCnt ? children[stIdx] : null;
  418. final int cc = namecmp(currPath, pathOff, st);
  419. if (cc > 0) {
  420. // This subtree is now empty.
  421. //
  422. removeChild(stIdx);
  423. continue;
  424. }
  425. if (cc < 0) {
  426. final int p = slash(currPath, pathOff);
  427. if (p < 0) {
  428. // The entry has no '/' and thus is directly in this
  429. // tree. Count it as one of our own.
  430. //
  431. cIdx++;
  432. entrySpan++;
  433. continue;
  434. }
  435. // Build a new subtree for this entry.
  436. //
  437. st = new DirCacheTree(this, currPath, pathOff, p - pathOff);
  438. insertChild(stIdx, st);
  439. }
  440. // The entry is contained in this subtree.
  441. //
  442. assert(st != null);
  443. st.validate(cache, cCnt, cIdx, pathOff + st.nameLength() + 1);
  444. cIdx += st.entrySpan;
  445. entrySpan += st.entrySpan;
  446. stIdx++;
  447. }
  448. // None of our remaining children can be in this tree
  449. // as the current cache entry is after our own name.
  450. //
  451. while (stIdx < childCnt)
  452. removeChild(childCnt - 1);
  453. }
  454. private void insertChild(int stIdx, DirCacheTree st) {
  455. final DirCacheTree[] c = children;
  456. if (childCnt + 1 <= c.length) {
  457. if (stIdx < childCnt)
  458. System.arraycopy(c, stIdx, c, stIdx + 1, childCnt - stIdx);
  459. c[stIdx] = st;
  460. childCnt++;
  461. return;
  462. }
  463. final int n = c.length;
  464. final DirCacheTree[] a = new DirCacheTree[n + 1];
  465. if (stIdx > 0)
  466. System.arraycopy(c, 0, a, 0, stIdx);
  467. a[stIdx] = st;
  468. if (stIdx < n)
  469. System.arraycopy(c, stIdx, a, stIdx + 1, n - stIdx);
  470. children = a;
  471. childCnt++;
  472. }
  473. private void removeChild(int stIdx) {
  474. final int n = --childCnt;
  475. if (stIdx < n)
  476. System.arraycopy(children, stIdx + 1, children, stIdx, n - stIdx);
  477. children[n] = null;
  478. }
  479. static boolean peq(byte[] a, byte[] b, int aLen) {
  480. if (b.length < aLen)
  481. return false;
  482. for (aLen--; aLen >= 0; aLen--)
  483. if (a[aLen] != b[aLen])
  484. return false;
  485. return true;
  486. }
  487. private static int namecmp(byte[] a, int aPos, DirCacheTree ct) {
  488. if (ct == null)
  489. return -1;
  490. final byte[] b = ct.encodedName;
  491. final int aLen = a.length;
  492. final int bLen = b.length;
  493. int bPos = 0;
  494. for (; aPos < aLen && bPos < bLen; aPos++, bPos++) {
  495. final int cmp = (a[aPos] & 0xff) - (b[bPos] & 0xff);
  496. if (cmp != 0)
  497. return cmp;
  498. }
  499. if (bPos == bLen)
  500. return a[aPos] == '/' ? 0 : -1;
  501. return aLen - bLen;
  502. }
  503. private static int slash(byte[] a, int aPos) {
  504. final int aLen = a.length;
  505. for (; aPos < aLen; aPos++)
  506. if (a[aPos] == '/')
  507. return aPos;
  508. return -1;
  509. }
  510. /** {@inheritDoc} */
  511. @Override
  512. public String toString() {
  513. return getNameString();
  514. }
  515. }