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.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. /*
  2. * Copyright (C) 2008, Google Inc.
  3. * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org> and others
  4. *
  5. * This program and the accompanying materials are made available under the
  6. * terms of the Eclipse Distribution License v. 1.0 which is available at
  7. * https://www.eclipse.org/org/documents/edl-v10.php.
  8. *
  9. * SPDX-License-Identifier: BSD-3-Clause
  10. */
  11. package org.eclipse.jgit.dircache;
  12. import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
  13. import static org.eclipse.jgit.util.Paths.compareSameName;
  14. import java.io.IOException;
  15. import org.eclipse.jgit.errors.DirCacheNameConflictException;
  16. /**
  17. * Generic update/editing support for {@link DirCache}.
  18. * <p>
  19. * The different update strategies extend this class to provide their own unique
  20. * services to applications.
  21. */
  22. abstract class BaseDirCacheEditor {
  23. /** The cache instance this editor updates during {@link #finish()}. */
  24. protected DirCache cache;
  25. /**
  26. * Entry table this builder will eventually replace into {@link #cache}.
  27. * <p>
  28. * Use {@link #fastAdd(DirCacheEntry)} or {@link #fastKeep(int, int)} to
  29. * make additions to this table. The table is automatically expanded if it
  30. * is too small for a new addition.
  31. * <p>
  32. * Typically the entries in here are sorted by their path names, just like
  33. * they are in the DirCache instance.
  34. */
  35. protected DirCacheEntry[] entries;
  36. /** Total number of valid entries in {@link #entries}. */
  37. protected int entryCnt;
  38. /**
  39. * Construct a new editor.
  40. *
  41. * @param dc
  42. * the cache this editor will eventually update.
  43. * @param ecnt
  44. * estimated number of entries the editor will have upon
  45. * completion. This sizes the initial entry table.
  46. */
  47. protected BaseDirCacheEditor(DirCache dc, int ecnt) {
  48. cache = dc;
  49. entries = new DirCacheEntry[ecnt];
  50. }
  51. /**
  52. * Get the {@code DirCache}
  53. *
  54. * @return the cache we will update on {@link #finish()}.
  55. */
  56. public DirCache getDirCache() {
  57. return cache;
  58. }
  59. /**
  60. * Append one entry into the resulting entry list.
  61. * <p>
  62. * The entry is placed at the end of the entry list. The caller is
  63. * responsible for making sure the final table is correctly sorted.
  64. * <p>
  65. * The {@link #entries} table is automatically expanded if there is
  66. * insufficient space for the new addition.
  67. *
  68. * @param newEntry
  69. * the new entry to add.
  70. */
  71. protected void fastAdd(DirCacheEntry newEntry) {
  72. if (entries.length == entryCnt) {
  73. final DirCacheEntry[] n = new DirCacheEntry[(entryCnt + 16) * 3 / 2];
  74. System.arraycopy(entries, 0, n, 0, entryCnt);
  75. entries = n;
  76. }
  77. entries[entryCnt++] = newEntry;
  78. }
  79. /**
  80. * Add a range of existing entries from the destination cache.
  81. * <p>
  82. * The entries are placed at the end of the entry list, preserving their
  83. * current order. The caller is responsible for making sure the final table
  84. * is correctly sorted.
  85. * <p>
  86. * This method copies from the destination cache, which has not yet been
  87. * updated with this editor's new table. So all offsets into the destination
  88. * cache are not affected by any updates that may be currently taking place
  89. * in this editor.
  90. * <p>
  91. * The {@link #entries} table is automatically expanded if there is
  92. * insufficient space for the new additions.
  93. *
  94. * @param pos
  95. * first entry to copy from the destination cache.
  96. * @param cnt
  97. * number of entries to copy.
  98. */
  99. protected void fastKeep(int pos, int cnt) {
  100. if (entryCnt + cnt > entries.length) {
  101. final int m1 = (entryCnt + 16) * 3 / 2;
  102. final int m2 = entryCnt + cnt;
  103. final DirCacheEntry[] n = new DirCacheEntry[Math.max(m1, m2)];
  104. System.arraycopy(entries, 0, n, 0, entryCnt);
  105. entries = n;
  106. }
  107. cache.toArray(pos, entries, entryCnt, cnt);
  108. entryCnt += cnt;
  109. }
  110. /**
  111. * Finish this builder and update the destination
  112. * {@link org.eclipse.jgit.dircache.DirCache}.
  113. * <p>
  114. * When this method completes this builder instance is no longer usable by
  115. * the calling application. A new builder must be created to make additional
  116. * changes to the index entries.
  117. * <p>
  118. * After completion the DirCache returned by {@link #getDirCache()} will
  119. * contain all modifications.
  120. * <p>
  121. * <i>Note to implementors:</i> Make sure {@link #entries} is fully sorted
  122. * then invoke {@link #replace()} to update the DirCache with the new table.
  123. */
  124. public abstract void finish();
  125. /**
  126. * Update the DirCache with the contents of {@link #entries}.
  127. * <p>
  128. * This method should be invoked only during an implementation of
  129. * {@link #finish()}, and only after {@link #entries} is sorted.
  130. */
  131. protected void replace() {
  132. checkNameConflicts();
  133. if (entryCnt < entries.length / 2) {
  134. final DirCacheEntry[] n = new DirCacheEntry[entryCnt];
  135. System.arraycopy(entries, 0, n, 0, entryCnt);
  136. entries = n;
  137. }
  138. cache.replace(entries, entryCnt);
  139. }
  140. private void checkNameConflicts() {
  141. int end = entryCnt - 1;
  142. for (int eIdx = 0; eIdx < end; eIdx++) {
  143. DirCacheEntry e = entries[eIdx];
  144. if (e.getStage() != 0) {
  145. continue;
  146. }
  147. byte[] ePath = e.path;
  148. int prefixLen = lastSlash(ePath) + 1;
  149. for (int nIdx = eIdx + 1; nIdx < entryCnt; nIdx++) {
  150. DirCacheEntry n = entries[nIdx];
  151. if (n.getStage() != 0) {
  152. continue;
  153. }
  154. byte[] nPath = n.path;
  155. if (!startsWith(ePath, nPath, prefixLen)) {
  156. // Different prefix; this entry is in another directory.
  157. break;
  158. }
  159. int s = nextSlash(nPath, prefixLen);
  160. int m = s < nPath.length ? TYPE_TREE : n.getRawMode();
  161. int cmp = compareSameName(
  162. ePath, prefixLen, ePath.length,
  163. nPath, prefixLen, s, m);
  164. if (cmp < 0) {
  165. break;
  166. } else if (cmp == 0) {
  167. throw new DirCacheNameConflictException(
  168. e.getPathString(),
  169. n.getPathString());
  170. }
  171. }
  172. }
  173. }
  174. private static int lastSlash(byte[] path) {
  175. for (int i = path.length - 1; i >= 0; i--) {
  176. if (path[i] == '/') {
  177. return i;
  178. }
  179. }
  180. return -1;
  181. }
  182. private static int nextSlash(byte[] b, int p) {
  183. final int n = b.length;
  184. for (; p < n; p++) {
  185. if (b[p] == '/') {
  186. return p;
  187. }
  188. }
  189. return n;
  190. }
  191. private static boolean startsWith(byte[] a, byte[] b, int n) {
  192. if (b.length < n) {
  193. return false;
  194. }
  195. for (n--; n >= 0; n--) {
  196. if (a[n] != b[n]) {
  197. return false;
  198. }
  199. }
  200. return true;
  201. }
  202. /**
  203. * Finish, write, commit this change, and release the index lock.
  204. * <p>
  205. * If this method fails (returns false) the lock is still released.
  206. * <p>
  207. * This is a utility method for applications as the finish-write-commit
  208. * pattern is very common after using a builder to update entries.
  209. *
  210. * @return true if the commit was successful and the file contains the new
  211. * data; false if the commit failed and the file remains with the
  212. * old data.
  213. * @throws java.lang.IllegalStateException
  214. * the lock is not held.
  215. * @throws java.io.IOException
  216. * the output file could not be created. The caller no longer
  217. * holds the lock.
  218. */
  219. public boolean commit() throws IOException {
  220. finish();
  221. cache.write();
  222. return cache.commit();
  223. }
  224. }