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.

PackIndexV1.java 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. /*
  2. * Copyright (C) 2008-2009, Google Inc.
  3. * Copyright (C) 2008, Marek Zawirski <marek.zawirski@gmail.com>
  4. * Copyright (C) 2007-2009, Robin Rosenberg <robin.rosenberg@dewire.com>
  5. * Copyright (C) 2006-2008, Shawn O. Pearce <spearce@spearce.org> and others
  6. *
  7. * This program and the accompanying materials are made available under the
  8. * terms of the Eclipse Distribution License v. 1.0 which is available at
  9. * https://www.eclipse.org/org/documents/edl-v10.php.
  10. *
  11. * SPDX-License-Identifier: BSD-3-Clause
  12. */
  13. package org.eclipse.jgit.internal.storage.file;
  14. import java.io.IOException;
  15. import java.io.InputStream;
  16. import java.util.Arrays;
  17. import java.util.Iterator;
  18. import java.util.NoSuchElementException;
  19. import java.util.Set;
  20. import org.eclipse.jgit.errors.CorruptObjectException;
  21. import org.eclipse.jgit.internal.JGitText;
  22. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  23. import org.eclipse.jgit.lib.AnyObjectId;
  24. import org.eclipse.jgit.lib.Constants;
  25. import org.eclipse.jgit.lib.ObjectId;
  26. import org.eclipse.jgit.util.IO;
  27. import org.eclipse.jgit.util.NB;
  28. class PackIndexV1 extends PackIndex {
  29. private static final int IDX_HDR_LEN = 256 * 4;
  30. private final long[] idxHeader;
  31. byte[][] idxdata;
  32. private long objectCnt;
  33. PackIndexV1(final InputStream fd, final byte[] hdr)
  34. throws CorruptObjectException, IOException {
  35. final byte[] fanoutTable = new byte[IDX_HDR_LEN];
  36. System.arraycopy(hdr, 0, fanoutTable, 0, hdr.length);
  37. IO.readFully(fd, fanoutTable, hdr.length, IDX_HDR_LEN - hdr.length);
  38. idxHeader = new long[256]; // really unsigned 32-bit...
  39. for (int k = 0; k < idxHeader.length; k++)
  40. idxHeader[k] = NB.decodeUInt32(fanoutTable, k * 4);
  41. idxdata = new byte[idxHeader.length][];
  42. for (int k = 0; k < idxHeader.length; k++) {
  43. int n;
  44. if (k == 0) {
  45. n = (int) (idxHeader[k]);
  46. } else {
  47. n = (int) (idxHeader[k] - idxHeader[k - 1]);
  48. }
  49. if (n > 0) {
  50. final long len = n * (Constants.OBJECT_ID_LENGTH + 4);
  51. if (len > Integer.MAX_VALUE - 8) // http://stackoverflow.com/a/8381338
  52. throw new IOException(JGitText.get().indexFileIsTooLargeForJgit);
  53. idxdata[k] = new byte[(int) len];
  54. IO.readFully(fd, idxdata[k], 0, idxdata[k].length);
  55. }
  56. }
  57. objectCnt = idxHeader[255];
  58. packChecksum = new byte[20];
  59. IO.readFully(fd, packChecksum, 0, packChecksum.length);
  60. }
  61. /** {@inheritDoc} */
  62. @Override
  63. public long getObjectCount() {
  64. return objectCnt;
  65. }
  66. /** {@inheritDoc} */
  67. @Override
  68. public long getOffset64Count() {
  69. long n64 = 0;
  70. for (MutableEntry e : this) {
  71. if (e.getOffset() >= Integer.MAX_VALUE)
  72. n64++;
  73. }
  74. return n64;
  75. }
  76. private int findLevelOne(long nthPosition) {
  77. int levelOne = Arrays.binarySearch(idxHeader, nthPosition + 1);
  78. if (levelOne >= 0) {
  79. // If we hit the bucket exactly the item is in the bucket, or
  80. // any bucket before it which has the same object count.
  81. //
  82. long base = idxHeader[levelOne];
  83. while (levelOne > 0 && base == idxHeader[levelOne - 1])
  84. levelOne--;
  85. } else {
  86. // The item is in the bucket we would insert it into.
  87. //
  88. levelOne = -(levelOne + 1);
  89. }
  90. return levelOne;
  91. }
  92. private int getLevelTwo(long nthPosition, int levelOne) {
  93. final long base = levelOne > 0 ? idxHeader[levelOne - 1] : 0;
  94. return (int) (nthPosition - base);
  95. }
  96. /** {@inheritDoc} */
  97. @Override
  98. public ObjectId getObjectId(long nthPosition) {
  99. final int levelOne = findLevelOne(nthPosition);
  100. final int p = getLevelTwo(nthPosition, levelOne);
  101. final int dataIdx = idOffset(p);
  102. return ObjectId.fromRaw(idxdata[levelOne], dataIdx);
  103. }
  104. @Override
  105. long getOffset(long nthPosition) {
  106. final int levelOne = findLevelOne(nthPosition);
  107. final int levelTwo = getLevelTwo(nthPosition, levelOne);
  108. final int p = (4 + Constants.OBJECT_ID_LENGTH) * levelTwo;
  109. return NB.decodeUInt32(idxdata[levelOne], p);
  110. }
  111. /** {@inheritDoc} */
  112. @Override
  113. public long findOffset(AnyObjectId objId) {
  114. final int levelOne = objId.getFirstByte();
  115. byte[] data = idxdata[levelOne];
  116. if (data == null)
  117. return -1;
  118. int high = data.length / (4 + Constants.OBJECT_ID_LENGTH);
  119. int low = 0;
  120. do {
  121. final int mid = (low + high) >>> 1;
  122. final int pos = idOffset(mid);
  123. final int cmp = objId.compareTo(data, pos);
  124. if (cmp < 0)
  125. high = mid;
  126. else if (cmp == 0) {
  127. int b0 = data[pos - 4] & 0xff;
  128. int b1 = data[pos - 3] & 0xff;
  129. int b2 = data[pos - 2] & 0xff;
  130. int b3 = data[pos - 1] & 0xff;
  131. return (((long) b0) << 24) | (b1 << 16) | (b2 << 8) | (b3);
  132. } else
  133. low = mid + 1;
  134. } while (low < high);
  135. return -1;
  136. }
  137. /** {@inheritDoc} */
  138. @Override
  139. public long findCRC32(AnyObjectId objId) {
  140. throw new UnsupportedOperationException();
  141. }
  142. /** {@inheritDoc} */
  143. @Override
  144. public boolean hasCRC32Support() {
  145. return false;
  146. }
  147. /** {@inheritDoc} */
  148. @Override
  149. public Iterator<MutableEntry> iterator() {
  150. return new IndexV1Iterator();
  151. }
  152. /** {@inheritDoc} */
  153. @Override
  154. public void resolve(Set<ObjectId> matches, AbbreviatedObjectId id,
  155. int matchLimit) throws IOException {
  156. byte[] data = idxdata[id.getFirstByte()];
  157. if (data == null)
  158. return;
  159. int max = data.length / (4 + Constants.OBJECT_ID_LENGTH);
  160. int high = max;
  161. int low = 0;
  162. do {
  163. int p = (low + high) >>> 1;
  164. final int cmp = id.prefixCompare(data, idOffset(p));
  165. if (cmp < 0)
  166. high = p;
  167. else if (cmp == 0) {
  168. // We may have landed in the middle of the matches. Move
  169. // backwards to the start of matches, then walk forwards.
  170. //
  171. while (0 < p && id.prefixCompare(data, idOffset(p - 1)) == 0)
  172. p--;
  173. for (; p < max && id.prefixCompare(data, idOffset(p)) == 0; p++) {
  174. matches.add(ObjectId.fromRaw(data, idOffset(p)));
  175. if (matches.size() > matchLimit)
  176. break;
  177. }
  178. return;
  179. } else
  180. low = p + 1;
  181. } while (low < high);
  182. }
  183. private static int idOffset(int mid) {
  184. return ((4 + Constants.OBJECT_ID_LENGTH) * mid) + 4;
  185. }
  186. private class IndexV1Iterator extends EntriesIterator {
  187. int levelOne;
  188. int levelTwo;
  189. @Override
  190. protected MutableEntry initEntry() {
  191. return new MutableEntry() {
  192. @Override
  193. protected void ensureId() {
  194. idBuffer.fromRaw(idxdata[levelOne], levelTwo
  195. - Constants.OBJECT_ID_LENGTH);
  196. }
  197. };
  198. }
  199. @Override
  200. public MutableEntry next() {
  201. for (; levelOne < idxdata.length; levelOne++) {
  202. if (idxdata[levelOne] == null)
  203. continue;
  204. if (levelTwo < idxdata[levelOne].length) {
  205. entry.offset = NB.decodeUInt32(idxdata[levelOne], levelTwo);
  206. levelTwo += Constants.OBJECT_ID_LENGTH + 4;
  207. returnedNumber++;
  208. return entry;
  209. }
  210. levelTwo = 0;
  211. }
  212. throw new NoSuchElementException();
  213. }
  214. }
  215. }