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.

BitmapIndexImpl.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513
  1. /*
  2. * Copyright (C) 2012, Google Inc.
  3. * and other copyright owners as documented in the project's IP log.
  4. *
  5. * This program and the accompanying materials are made available
  6. * under the terms of the Eclipse Distribution License v1.0 which
  7. * accompanies this distribution, is reproduced below, and is
  8. * available at http://www.eclipse.org/org/documents/edl-v10.php
  9. *
  10. * All rights reserved.
  11. *
  12. * Redistribution and use in source and binary forms, with or
  13. * without modification, are permitted provided that the following
  14. * conditions are met:
  15. *
  16. * - Redistributions of source code must retain the above copyright
  17. * notice, this list of conditions and the following disclaimer.
  18. *
  19. * - Redistributions in binary form must reproduce the above
  20. * copyright notice, this list of conditions and the following
  21. * disclaimer in the documentation and/or other materials provided
  22. * with the distribution.
  23. *
  24. * - Neither the name of the Eclipse Foundation, Inc. nor the
  25. * names of its contributors may be used to endorse or promote
  26. * products derived from this software without specific prior
  27. * written permission.
  28. *
  29. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
  30. * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  31. * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  32. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
  34. * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  38. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39. * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40. * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
  41. * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  42. */
  43. package org.eclipse.jgit.internal.storage.file;
  44. import java.text.MessageFormat;
  45. import java.util.Iterator;
  46. import java.util.NoSuchElementException;
  47. import org.eclipse.jgit.internal.JGitText;
  48. import org.eclipse.jgit.lib.AnyObjectId;
  49. import org.eclipse.jgit.lib.BitmapIndex;
  50. import org.eclipse.jgit.lib.BitmapObject;
  51. import org.eclipse.jgit.lib.Constants;
  52. import org.eclipse.jgit.lib.ObjectId;
  53. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  54. import org.eclipse.jgit.util.BlockList;
  55. import com.googlecode.javaewah.EWAHCompressedBitmap;
  56. import com.googlecode.javaewah.IntIterator;
  57. /** A compressed bitmap representation of the entire object graph. */
  58. public class BitmapIndexImpl implements BitmapIndex {
  59. private static final int EXTRA_BITS = 10 * 1024;
  60. final PackBitmapIndex packIndex;
  61. final MutableBitmapIndex mutableIndex;
  62. final int indexObjectCount;
  63. /**
  64. * Creates a BitmapIndex that is back by Compressed bitmaps.
  65. *
  66. * @param packIndex
  67. * the bitmap index for the pack.
  68. */
  69. public BitmapIndexImpl(PackBitmapIndex packIndex) {
  70. this.packIndex = packIndex;
  71. mutableIndex = new MutableBitmapIndex();
  72. indexObjectCount = packIndex.getObjectCount();
  73. }
  74. PackBitmapIndex getPackBitmapIndex() {
  75. return packIndex;
  76. }
  77. @Override
  78. public CompressedBitmap getBitmap(AnyObjectId objectId) {
  79. EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
  80. if (compressed == null)
  81. return null;
  82. return new CompressedBitmap(compressed, this);
  83. }
  84. @Override
  85. public CompressedBitmapBuilder newBitmapBuilder() {
  86. return new CompressedBitmapBuilder(this);
  87. }
  88. int findPosition(AnyObjectId objectId) {
  89. int position = packIndex.findPosition(objectId);
  90. if (position < 0) {
  91. position = mutableIndex.findPosition(objectId);
  92. if (position >= 0)
  93. position += indexObjectCount;
  94. }
  95. return position;
  96. }
  97. int findOrInsert(AnyObjectId objectId, int type) {
  98. int position = findPosition(objectId);
  99. if (position < 0) {
  100. position = mutableIndex.findOrInsert(objectId, type);
  101. position += indexObjectCount;
  102. }
  103. return position;
  104. }
  105. private static final class ComboBitset {
  106. private InflatingBitSet inflatingBitmap;
  107. private BitSet toAdd;
  108. private BitSet toRemove;
  109. ComboBitset() {
  110. this(new EWAHCompressedBitmap());
  111. }
  112. ComboBitset(EWAHCompressedBitmap bitmap) {
  113. this.inflatingBitmap = new InflatingBitSet(bitmap);
  114. }
  115. EWAHCompressedBitmap combine() {
  116. EWAHCompressedBitmap toAddCompressed = null;
  117. if (toAdd != null) {
  118. toAddCompressed = toAdd.toEWAHCompressedBitmap();
  119. toAdd = null;
  120. }
  121. EWAHCompressedBitmap toRemoveCompressed = null;
  122. if (toRemove != null) {
  123. toRemoveCompressed = toRemove.toEWAHCompressedBitmap();
  124. toRemove = null;
  125. }
  126. if (toAddCompressed != null)
  127. or(toAddCompressed);
  128. if (toRemoveCompressed != null)
  129. andNot(toRemoveCompressed);
  130. return inflatingBitmap.getBitmap();
  131. }
  132. void or(EWAHCompressedBitmap inbits) {
  133. if (toRemove != null)
  134. combine();
  135. inflatingBitmap = inflatingBitmap.or(inbits);
  136. }
  137. void andNot(EWAHCompressedBitmap inbits) {
  138. if (toAdd != null || toRemove != null)
  139. combine();
  140. inflatingBitmap = inflatingBitmap.andNot(inbits);
  141. }
  142. void xor(EWAHCompressedBitmap inbits) {
  143. if (toAdd != null || toRemove != null)
  144. combine();
  145. inflatingBitmap = inflatingBitmap.xor(inbits);
  146. }
  147. boolean contains(int position) {
  148. if (toRemove != null && toRemove.get(position))
  149. return false;
  150. if (toAdd != null && toAdd.get(position))
  151. return true;
  152. return inflatingBitmap.contains(position);
  153. }
  154. void remove(int position) {
  155. if (toAdd != null)
  156. toAdd.clear(position);
  157. if (inflatingBitmap.maybeContains(position)) {
  158. if (toRemove == null)
  159. toRemove = new BitSet(position + EXTRA_BITS);
  160. toRemove.set(position);
  161. }
  162. }
  163. void set(int position) {
  164. if (toRemove != null)
  165. toRemove.clear(position);
  166. if (toAdd == null)
  167. toAdd = new BitSet(position + EXTRA_BITS);
  168. toAdd.set(position);
  169. }
  170. }
  171. private static final class CompressedBitmapBuilder implements BitmapBuilder {
  172. private ComboBitset bitset;
  173. private final BitmapIndexImpl bitmapIndex;
  174. CompressedBitmapBuilder(BitmapIndexImpl bitmapIndex) {
  175. this.bitset = new ComboBitset();
  176. this.bitmapIndex = bitmapIndex;
  177. }
  178. @Override
  179. public boolean add(AnyObjectId objectId, int type) {
  180. int position = bitmapIndex.findOrInsert(objectId, type);
  181. if (bitset.contains(position))
  182. return false;
  183. Bitmap entry = bitmapIndex.getBitmap(objectId);
  184. if (entry != null) {
  185. or(entry);
  186. return false;
  187. }
  188. bitset.set(position);
  189. return true;
  190. }
  191. @Override
  192. public boolean contains(AnyObjectId objectId) {
  193. int position = bitmapIndex.findPosition(objectId);
  194. return 0 <= position && bitset.contains(position);
  195. }
  196. @Override
  197. public BitmapBuilder addObject(AnyObjectId objectId, int type) {
  198. bitset.set(bitmapIndex.findOrInsert(objectId, type));
  199. return this;
  200. }
  201. @Override
  202. public void remove(AnyObjectId objectId) {
  203. int position = bitmapIndex.findPosition(objectId);
  204. if (0 <= position)
  205. bitset.remove(position);
  206. }
  207. @Override
  208. public CompressedBitmapBuilder or(Bitmap other) {
  209. bitset.or(ewahBitmap(other));
  210. return this;
  211. }
  212. @Override
  213. public CompressedBitmapBuilder andNot(Bitmap other) {
  214. bitset.andNot(ewahBitmap(other));
  215. return this;
  216. }
  217. @Override
  218. public CompressedBitmapBuilder xor(Bitmap other) {
  219. bitset.xor(ewahBitmap(other));
  220. return this;
  221. }
  222. /** @return the fully built immutable bitmap */
  223. @Override
  224. public CompressedBitmap build() {
  225. return new CompressedBitmap(bitset.combine(), bitmapIndex);
  226. }
  227. @Override
  228. public Iterator<BitmapObject> iterator() {
  229. return build().iterator();
  230. }
  231. @Override
  232. public int cardinality() {
  233. return bitset.combine().cardinality();
  234. }
  235. @Override
  236. public boolean removeAllOrNone(PackBitmapIndex index) {
  237. if (!bitmapIndex.packIndex.equals(index))
  238. return false;
  239. EWAHCompressedBitmap curr = bitset.combine()
  240. .xor(ones(bitmapIndex.indexObjectCount));
  241. IntIterator ii = curr.intIterator();
  242. if (ii.hasNext() && ii.next() < bitmapIndex.indexObjectCount)
  243. return false;
  244. bitset = new ComboBitset(curr);
  245. return true;
  246. }
  247. @Override
  248. public BitmapIndexImpl getBitmapIndex() {
  249. return bitmapIndex;
  250. }
  251. private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  252. if (other instanceof CompressedBitmap) {
  253. CompressedBitmap b = (CompressedBitmap) other;
  254. if (b.bitmapIndex != bitmapIndex) {
  255. throw new IllegalArgumentException();
  256. }
  257. return b.bitmap;
  258. }
  259. if (other instanceof CompressedBitmapBuilder) {
  260. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  261. if (b.bitmapIndex != bitmapIndex) {
  262. throw new IllegalArgumentException();
  263. }
  264. return b.bitset.combine();
  265. }
  266. throw new IllegalArgumentException();
  267. }
  268. }
  269. /**
  270. * Wrapper for a {@link EWAHCompressedBitmap} and {@link PackBitmapIndex}.
  271. * <p>
  272. * For a EWAHCompressedBitmap {@code bitmap} representing a vector of
  273. * bits, {@code new CompressedBitmap(bitmap, bitmapIndex)} represents the
  274. * objects at those positions in {@code bitmapIndex.packIndex}.
  275. */
  276. public static final class CompressedBitmap implements Bitmap {
  277. final EWAHCompressedBitmap bitmap;
  278. final BitmapIndexImpl bitmapIndex;
  279. /**
  280. * Construct compressed bitmap for given bitmap and bitmap index
  281. *
  282. * @param bitmap
  283. * @param bitmapIndex
  284. */
  285. public CompressedBitmap(EWAHCompressedBitmap bitmap, BitmapIndexImpl bitmapIndex) {
  286. this.bitmap = bitmap;
  287. this.bitmapIndex = bitmapIndex;
  288. }
  289. @Override
  290. public CompressedBitmap or(Bitmap other) {
  291. return new CompressedBitmap(bitmap.or(ewahBitmap(other)), bitmapIndex);
  292. }
  293. @Override
  294. public CompressedBitmap andNot(Bitmap other) {
  295. return new CompressedBitmap(bitmap.andNot(ewahBitmap(other)), bitmapIndex);
  296. }
  297. @Override
  298. public CompressedBitmap xor(Bitmap other) {
  299. return new CompressedBitmap(bitmap.xor(ewahBitmap(other)), bitmapIndex);
  300. }
  301. private final IntIterator ofObjectType(int type) {
  302. return bitmapIndex.packIndex.ofObjectType(bitmap, type).intIterator();
  303. }
  304. @Override
  305. public Iterator<BitmapObject> iterator() {
  306. final IntIterator dynamic = bitmap.andNot(ones(bitmapIndex.indexObjectCount))
  307. .intIterator();
  308. final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
  309. final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
  310. final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB);
  311. final IntIterator tags = ofObjectType(Constants.OBJ_TAG);
  312. return new Iterator<BitmapObject>() {
  313. private final BitmapObjectImpl out = new BitmapObjectImpl();
  314. private int type;
  315. private IntIterator cached = dynamic;
  316. public boolean hasNext() {
  317. if (!cached.hasNext()) {
  318. if (commits.hasNext()) {
  319. type = Constants.OBJ_COMMIT;
  320. cached = commits;
  321. } else if (trees.hasNext()) {
  322. type = Constants.OBJ_TREE;
  323. cached = trees;
  324. } else if (blobs.hasNext()) {
  325. type = Constants.OBJ_BLOB;
  326. cached = blobs;
  327. } else if (tags.hasNext()) {
  328. type = Constants.OBJ_TAG;
  329. cached = tags;
  330. } else {
  331. return false;
  332. }
  333. }
  334. return true;
  335. }
  336. public BitmapObject next() {
  337. if (!hasNext())
  338. throw new NoSuchElementException();
  339. int position = cached.next();
  340. if (position < bitmapIndex.indexObjectCount) {
  341. out.type = type;
  342. out.objectId = bitmapIndex.packIndex.getObject(position);
  343. } else {
  344. position -= bitmapIndex.indexObjectCount;
  345. MutableEntry entry = bitmapIndex.mutableIndex.getObject(position);
  346. out.type = entry.type;
  347. out.objectId = entry;
  348. }
  349. return out;
  350. }
  351. public void remove() {
  352. throw new UnsupportedOperationException();
  353. }
  354. };
  355. }
  356. EWAHCompressedBitmap getEwahCompressedBitmap() {
  357. return bitmap;
  358. }
  359. private EWAHCompressedBitmap ewahBitmap(Bitmap other) {
  360. if (other instanceof CompressedBitmap) {
  361. CompressedBitmap b = (CompressedBitmap) other;
  362. if (b.bitmapIndex != bitmapIndex) {
  363. throw new IllegalArgumentException();
  364. }
  365. return b.bitmap;
  366. }
  367. if (other instanceof CompressedBitmapBuilder) {
  368. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  369. if (b.bitmapIndex != bitmapIndex) {
  370. throw new IllegalArgumentException();
  371. }
  372. return b.bitset.combine();
  373. }
  374. throw new IllegalArgumentException();
  375. }
  376. }
  377. private static final class MutableBitmapIndex {
  378. private final ObjectIdOwnerMap<MutableEntry>
  379. revMap = new ObjectIdOwnerMap<MutableEntry>();
  380. private final BlockList<MutableEntry>
  381. revList = new BlockList<MutableEntry>();
  382. int findPosition(AnyObjectId objectId) {
  383. MutableEntry entry = revMap.get(objectId);
  384. if (entry == null)
  385. return -1;
  386. return entry.position;
  387. }
  388. MutableEntry getObject(int position) {
  389. try {
  390. MutableEntry entry = revList.get(position);
  391. if (entry == null)
  392. throw new IllegalArgumentException(MessageFormat.format(
  393. JGitText.get().objectNotFound,
  394. String.valueOf(position)));
  395. return entry;
  396. } catch (IndexOutOfBoundsException ex) {
  397. throw new IllegalArgumentException(ex);
  398. }
  399. }
  400. int findOrInsert(AnyObjectId objectId, int type) {
  401. MutableEntry entry = new MutableEntry(
  402. objectId, type, revList.size());
  403. revList.add(entry);
  404. revMap.add(entry);
  405. return entry.position;
  406. }
  407. }
  408. private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
  409. final int type;
  410. final int position;
  411. MutableEntry(AnyObjectId objectId, int type, int position) {
  412. super(objectId);
  413. this.type = type;
  414. this.position = position;
  415. }
  416. }
  417. private static final class BitmapObjectImpl extends BitmapObject {
  418. private ObjectId objectId;
  419. private int type;
  420. @Override
  421. public ObjectId getObjectId() {
  422. return objectId;
  423. }
  424. @Override
  425. public int getType() {
  426. return type;
  427. }
  428. }
  429. static final EWAHCompressedBitmap ones(int sizeInBits) {
  430. EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
  431. mask.addStreamOfEmptyWords(
  432. true, sizeInBits / EWAHCompressedBitmap.WORD_IN_BITS);
  433. int remaining = sizeInBits % EWAHCompressedBitmap.WORD_IN_BITS;
  434. if (remaining > 0)
  435. mask.add((1L << remaining) - 1, remaining);
  436. return mask;
  437. }
  438. }