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.

PackBitmapIndexBuilder.java 9.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  1. /*
  2. * Copyright (C) 2012, Google Inc. and others
  3. *
  4. * This program and the accompanying materials are made available under the
  5. * terms of the Eclipse Distribution License v. 1.0 which is available at
  6. * https://www.eclipse.org/org/documents/edl-v10.php.
  7. *
  8. * SPDX-License-Identifier: BSD-3-Clause
  9. */
  10. package org.eclipse.jgit.internal.storage.file;
  11. import java.text.MessageFormat;
  12. import java.util.ArrayList;
  13. import java.util.Collections;
  14. import java.util.LinkedList;
  15. import java.util.List;
  16. import org.eclipse.jgit.internal.JGitText;
  17. import org.eclipse.jgit.internal.storage.pack.BitmapCommit;
  18. import org.eclipse.jgit.internal.storage.pack.ObjectToPack;
  19. import org.eclipse.jgit.lib.AnyObjectId;
  20. import org.eclipse.jgit.lib.BitmapIndex.Bitmap;
  21. import org.eclipse.jgit.lib.Constants;
  22. import org.eclipse.jgit.lib.ObjectId;
  23. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  24. import org.eclipse.jgit.util.BlockList;
  25. import com.googlecode.javaewah.EWAHCompressedBitmap;
  26. /**
  27. * Helper for constructing
  28. * {@link org.eclipse.jgit.internal.storage.file.PackBitmapIndex}es.
  29. */
  30. public class PackBitmapIndexBuilder extends BasePackBitmapIndex {
  31. private static final int MAX_XOR_OFFSET_SEARCH = 10;
  32. private final EWAHCompressedBitmap commits;
  33. private final EWAHCompressedBitmap trees;
  34. private final EWAHCompressedBitmap blobs;
  35. private final EWAHCompressedBitmap tags;
  36. private final BlockList<PositionEntry> byOffset;
  37. private final LinkedList<StoredBitmap>
  38. bitmapsToWriteXorBuffer = new LinkedList<>();
  39. private List<StoredEntry> bitmapsToWrite = new ArrayList<>();
  40. final ObjectIdOwnerMap<PositionEntry>
  41. positionEntries = new ObjectIdOwnerMap<>();
  42. /**
  43. * Creates a PackBitmapIndex used for building the contents of an index
  44. * file.
  45. *
  46. * @param objects
  47. * objects sorted by name. The list must be initially sorted by
  48. * ObjectId (name); it will be resorted in place.
  49. */
  50. public PackBitmapIndexBuilder(List<ObjectToPack> objects) {
  51. super(new ObjectIdOwnerMap<StoredBitmap>());
  52. byOffset = new BlockList<>(objects.size());
  53. sortByOffsetAndIndex(byOffset, positionEntries, objects);
  54. // 64 objects fit in a single long word (64 bits).
  55. // On average a repository is 30% commits, 30% trees, 30% blobs.
  56. // Initialize bitmap capacity for worst case to minimize growing.
  57. int sizeInWords = Math.max(4, byOffset.size() / 64 / 3);
  58. commits = new EWAHCompressedBitmap(sizeInWords);
  59. trees = new EWAHCompressedBitmap(sizeInWords);
  60. blobs = new EWAHCompressedBitmap(sizeInWords);
  61. tags = new EWAHCompressedBitmap(sizeInWords);
  62. for (int i = 0; i < objects.size(); i++) {
  63. int type = objects.get(i).getType();
  64. switch (type) {
  65. case Constants.OBJ_COMMIT:
  66. commits.set(i);
  67. break;
  68. case Constants.OBJ_TREE:
  69. trees.set(i);
  70. break;
  71. case Constants.OBJ_BLOB:
  72. blobs.set(i);
  73. break;
  74. case Constants.OBJ_TAG:
  75. tags.set(i);
  76. break;
  77. default:
  78. throw new IllegalArgumentException(MessageFormat.format(
  79. JGitText.get().badObjectType, String.valueOf(type)));
  80. }
  81. }
  82. commits.trim();
  83. trees.trim();
  84. blobs.trim();
  85. tags.trim();
  86. }
  87. private static void sortByOffsetAndIndex(BlockList<PositionEntry> byOffset,
  88. ObjectIdOwnerMap<PositionEntry> positionEntries,
  89. List<ObjectToPack> entries) {
  90. for (int i = 0; i < entries.size(); i++) {
  91. positionEntries.add(new PositionEntry(entries.get(i), i));
  92. }
  93. Collections.sort(entries, (ObjectToPack a, ObjectToPack b) -> Long
  94. .signum(a.getOffset() - b.getOffset()));
  95. for (int i = 0; i < entries.size(); i++) {
  96. PositionEntry e = positionEntries.get(entries.get(i));
  97. e.offsetPosition = i;
  98. byOffset.add(e);
  99. }
  100. }
  101. /**
  102. * Get set of objects included in the pack.
  103. *
  104. * @return set of objects included in the pack.
  105. */
  106. public ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> getObjectSet() {
  107. ObjectIdOwnerMap<ObjectIdOwnerMap.Entry> r = new ObjectIdOwnerMap<>();
  108. for (PositionEntry e : byOffset) {
  109. r.add(new ObjectIdOwnerMap.Entry(e) {
  110. // A new entry that copies the ObjectId
  111. });
  112. }
  113. return r;
  114. }
  115. /**
  116. * Stores the bitmap for the objectId.
  117. *
  118. * @param objectId
  119. * the object id key for the bitmap.
  120. * @param bitmap
  121. * the bitmap
  122. * @param flags
  123. * the flags to be stored with the bitmap
  124. */
  125. public void addBitmap(AnyObjectId objectId, Bitmap bitmap, int flags) {
  126. addBitmap(objectId, bitmap.retrieveCompressed(), flags);
  127. }
  128. /**
  129. * Processes a commit and prepares its bitmap to write to the bitmap index
  130. * file.
  131. *
  132. * @param c
  133. * the commit corresponds to the bitmap.
  134. * @param bitmap
  135. * the bitmap to be written.
  136. * @param flags
  137. * the flags of the commit.
  138. */
  139. public void processBitmapForWrite(BitmapCommit c, Bitmap bitmap,
  140. int flags) {
  141. EWAHCompressedBitmap compressed = bitmap.retrieveCompressed();
  142. compressed.trim();
  143. StoredBitmap newest = new StoredBitmap(c, compressed, null, flags);
  144. bitmapsToWriteXorBuffer.add(newest);
  145. if (bitmapsToWriteXorBuffer.size() > MAX_XOR_OFFSET_SEARCH) {
  146. bitmapsToWrite.add(
  147. generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
  148. }
  149. if (c.isAddToIndex()) {
  150. // The Bitmap map in the base class is used to make revwalks
  151. // efficient, so only add bitmaps that keep it efficient without
  152. // bloating memory.
  153. addBitmap(c, bitmap, flags);
  154. }
  155. }
  156. private StoredEntry generateStoredEntry(StoredBitmap bitmapToWrite) {
  157. int bestXorOffset = 0;
  158. EWAHCompressedBitmap bestBitmap = bitmapToWrite.getBitmap();
  159. int offset = 1;
  160. for (StoredBitmap curr : bitmapsToWriteXorBuffer) {
  161. EWAHCompressedBitmap bitmap = curr.getBitmap()
  162. .xor(bitmapToWrite.getBitmap());
  163. if (bitmap.sizeInBytes() < bestBitmap.sizeInBytes()) {
  164. bestBitmap = bitmap;
  165. bestXorOffset = offset;
  166. }
  167. offset++;
  168. }
  169. PositionEntry entry = positionEntries.get(bitmapToWrite);
  170. if (entry == null) {
  171. throw new IllegalStateException();
  172. }
  173. bestBitmap.trim();
  174. StoredEntry result = new StoredEntry(entry.namePosition, bestBitmap,
  175. bestXorOffset, bitmapToWrite.getFlags());
  176. return result;
  177. }
  178. /**
  179. * Stores the bitmap for the objectId.
  180. *
  181. * @param objectId
  182. * the object id key for the bitmap.
  183. * @param bitmap
  184. * the bitmap
  185. * @param flags
  186. * the flags to be stored with the bitmap
  187. */
  188. public void addBitmap(
  189. AnyObjectId objectId, EWAHCompressedBitmap bitmap, int flags) {
  190. bitmap.trim();
  191. StoredBitmap result = new StoredBitmap(objectId, bitmap, null, flags);
  192. getBitmaps().add(result);
  193. }
  194. /** {@inheritDoc} */
  195. @Override
  196. public EWAHCompressedBitmap ofObjectType(
  197. EWAHCompressedBitmap bitmap, int type) {
  198. switch (type) {
  199. case Constants.OBJ_BLOB:
  200. return getBlobs().and(bitmap);
  201. case Constants.OBJ_TREE:
  202. return getTrees().and(bitmap);
  203. case Constants.OBJ_COMMIT:
  204. return getCommits().and(bitmap);
  205. case Constants.OBJ_TAG:
  206. return getTags().and(bitmap);
  207. }
  208. throw new IllegalArgumentException();
  209. }
  210. /** {@inheritDoc} */
  211. @Override
  212. public int findPosition(AnyObjectId objectId) {
  213. PositionEntry entry = positionEntries.get(objectId);
  214. if (entry == null)
  215. return -1;
  216. return entry.offsetPosition;
  217. }
  218. /** {@inheritDoc} */
  219. @Override
  220. public ObjectId getObject(int position) throws IllegalArgumentException {
  221. ObjectId objectId = byOffset.get(position);
  222. if (objectId == null)
  223. throw new IllegalArgumentException();
  224. return objectId;
  225. }
  226. /**
  227. * Get the commit object bitmap.
  228. *
  229. * @return the commit object bitmap.
  230. */
  231. public EWAHCompressedBitmap getCommits() {
  232. return commits;
  233. }
  234. /**
  235. * Get the tree object bitmap.
  236. *
  237. * @return the tree object bitmap.
  238. */
  239. public EWAHCompressedBitmap getTrees() {
  240. return trees;
  241. }
  242. /**
  243. * Get the blob object bitmap.
  244. *
  245. * @return the blob object bitmap.
  246. */
  247. public EWAHCompressedBitmap getBlobs() {
  248. return blobs;
  249. }
  250. /**
  251. * Get the tag object bitmap.
  252. *
  253. * @return the tag object bitmap.
  254. */
  255. public EWAHCompressedBitmap getTags() {
  256. return tags;
  257. }
  258. /**
  259. * Get the index storage options.
  260. *
  261. * @return the index storage options.
  262. */
  263. public int getOptions() {
  264. return PackBitmapIndexV1.OPT_FULL;
  265. }
  266. /** {@inheritDoc} */
  267. @Override
  268. public int getBitmapCount() {
  269. return bitmapsToWriteXorBuffer.size() + bitmapsToWrite.size();
  270. }
  271. /**
  272. * Remove all the bitmaps entries added.
  273. *
  274. * @param size
  275. * the expected number of bitmap entries to be written.
  276. */
  277. public void resetBitmaps(int size) {
  278. getBitmaps().clear();
  279. bitmapsToWrite = new ArrayList<>(size);
  280. }
  281. /** {@inheritDoc} */
  282. @Override
  283. public int getObjectCount() {
  284. return byOffset.size();
  285. }
  286. /**
  287. * Get list of xor compressed entries that need to be written.
  288. *
  289. * @return a list of the xor compressed entries.
  290. */
  291. public List<StoredEntry> getCompressedBitmaps() {
  292. while (!bitmapsToWriteXorBuffer.isEmpty()) {
  293. bitmapsToWrite.add(
  294. generateStoredEntry(bitmapsToWriteXorBuffer.pollFirst()));
  295. }
  296. Collections.reverse(bitmapsToWrite);
  297. return bitmapsToWrite;
  298. }
  299. /** Data object for the on disk representation of a bitmap entry. */
  300. public static final class StoredEntry {
  301. private final long objectId;
  302. private final EWAHCompressedBitmap bitmap;
  303. private final int xorOffset;
  304. private final int flags;
  305. StoredEntry(long objectId, EWAHCompressedBitmap bitmap,
  306. int xorOffset, int flags) {
  307. this.objectId = objectId;
  308. this.bitmap = bitmap;
  309. this.xorOffset = xorOffset;
  310. this.flags = flags;
  311. }
  312. /** @return the bitmap */
  313. public EWAHCompressedBitmap getBitmap() {
  314. return bitmap;
  315. }
  316. /** @return the xorOffset */
  317. public int getXorOffset() {
  318. return xorOffset;
  319. }
  320. /** @return the flags */
  321. public int getFlags() {
  322. return flags;
  323. }
  324. /** @return the ObjectId */
  325. public long getObjectId() {
  326. return objectId;
  327. }
  328. }
  329. private static final class PositionEntry extends ObjectIdOwnerMap.Entry {
  330. final int namePosition;
  331. int offsetPosition;
  332. PositionEntry(AnyObjectId objectId, int namePosition) {
  333. super(objectId);
  334. this.namePosition = namePosition;
  335. }
  336. }
  337. }