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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484
  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 com.googlecode.javaewah.EWAHCompressedBitmap;
  48. import com.googlecode.javaewah.IntIterator;
  49. import org.eclipse.jgit.internal.JGitText;
  50. import org.eclipse.jgit.lib.AnyObjectId;
  51. import org.eclipse.jgit.lib.BitmapIndex;
  52. import org.eclipse.jgit.lib.BitmapObject;
  53. import org.eclipse.jgit.lib.Constants;
  54. import org.eclipse.jgit.lib.ObjectId;
  55. import org.eclipse.jgit.lib.ObjectIdOwnerMap;
  56. import org.eclipse.jgit.util.BlockList;
  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. private final PackBitmapIndex packIndex;
  61. private final MutableBitmapIndex mutableIndex;
  62. private 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. public CompressedBitmap getBitmap(AnyObjectId objectId) {
  78. EWAHCompressedBitmap compressed = packIndex.getBitmap(objectId);
  79. if (compressed == null)
  80. return null;
  81. return new CompressedBitmap(compressed);
  82. }
  83. public CompressedBitmapBuilder newBitmapBuilder() {
  84. return new CompressedBitmapBuilder();
  85. }
  86. private int findPosition(AnyObjectId objectId) {
  87. int position = packIndex.findPosition(objectId);
  88. if (position < 0) {
  89. position = mutableIndex.findPosition(objectId);
  90. if (position >= 0)
  91. position += indexObjectCount;
  92. }
  93. return position;
  94. }
  95. private int addObject(AnyObjectId objectId, int type) {
  96. int position = findPosition(objectId);
  97. if (position < 0) {
  98. position = mutableIndex.addObject(objectId, type);
  99. position += indexObjectCount;
  100. }
  101. return position;
  102. }
  103. private static final class ComboBitset {
  104. private InflatingBitSet inflatingBitmap;
  105. private BitSet toAdd;
  106. private BitSet toRemove;
  107. private ComboBitset() {
  108. this(new EWAHCompressedBitmap());
  109. }
  110. private ComboBitset(EWAHCompressedBitmap bitmap) {
  111. this.inflatingBitmap = new InflatingBitSet(bitmap);
  112. }
  113. EWAHCompressedBitmap combine() {
  114. EWAHCompressedBitmap toAddCompressed = null;
  115. if (toAdd != null) {
  116. toAddCompressed = toAdd.toEWAHCompressedBitmap();
  117. toAdd = null;
  118. }
  119. EWAHCompressedBitmap toRemoveCompressed = null;
  120. if (toRemove != null) {
  121. toRemoveCompressed = toRemove.toEWAHCompressedBitmap();
  122. toRemove = null;
  123. }
  124. if (toAddCompressed != null)
  125. or(toAddCompressed);
  126. if (toRemoveCompressed != null)
  127. andNot(toRemoveCompressed);
  128. return inflatingBitmap.getBitmap();
  129. }
  130. void or(EWAHCompressedBitmap inbits) {
  131. if (toRemove != null)
  132. combine();
  133. inflatingBitmap = inflatingBitmap.or(inbits);
  134. }
  135. void andNot(EWAHCompressedBitmap inbits) {
  136. if (toAdd != null || toRemove != null)
  137. combine();
  138. inflatingBitmap = inflatingBitmap.andNot(inbits);
  139. }
  140. void xor(EWAHCompressedBitmap inbits) {
  141. if (toAdd != null || toRemove != null)
  142. combine();
  143. inflatingBitmap = inflatingBitmap.xor(inbits);
  144. }
  145. boolean contains(int position) {
  146. if (toRemove != null && toRemove.get(position))
  147. return false;
  148. if (toAdd != null && toAdd.get(position))
  149. return true;
  150. return inflatingBitmap.contains(position);
  151. }
  152. void remove(int position) {
  153. if (toAdd != null)
  154. toAdd.clear(position);
  155. if (inflatingBitmap.maybeContains(position)) {
  156. if (toRemove == null)
  157. toRemove = new BitSet(position + EXTRA_BITS);
  158. toRemove.set(position);
  159. }
  160. }
  161. void set(int position) {
  162. if (toRemove != null)
  163. toRemove.clear(position);
  164. if (toAdd == null)
  165. toAdd = new BitSet(position + EXTRA_BITS);
  166. toAdd.set(position);
  167. }
  168. }
  169. private final class CompressedBitmapBuilder implements BitmapBuilder {
  170. private ComboBitset bitset = new ComboBitset();
  171. public boolean add(AnyObjectId objectId, int type) {
  172. int position = addObject(objectId, type);
  173. if (bitset.contains(position))
  174. return false;
  175. Bitmap entry = getBitmap(objectId);
  176. if (entry != null) {
  177. or(entry);
  178. return false;
  179. }
  180. bitset.set(position);
  181. return true;
  182. }
  183. public boolean contains(AnyObjectId objectId) {
  184. int position = findPosition(objectId);
  185. return 0 <= position && bitset.contains(position);
  186. }
  187. public void remove(AnyObjectId objectId) {
  188. int position = findPosition(objectId);
  189. if (0 <= position)
  190. bitset.remove(position);
  191. }
  192. public CompressedBitmapBuilder or(Bitmap other) {
  193. if (isSameCompressedBitmap(other)) {
  194. bitset.or(((CompressedBitmap) other).bitmap);
  195. } else if (isSameCompressedBitmapBuilder(other)) {
  196. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  197. bitset.or(b.bitset.combine());
  198. } else {
  199. throw new IllegalArgumentException();
  200. }
  201. return this;
  202. }
  203. public CompressedBitmapBuilder andNot(Bitmap other) {
  204. if (isSameCompressedBitmap(other)) {
  205. bitset.andNot(((CompressedBitmap) other).bitmap);
  206. } else if (isSameCompressedBitmapBuilder(other)) {
  207. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  208. bitset.andNot(b.bitset.combine());
  209. } else {
  210. throw new IllegalArgumentException();
  211. }
  212. return this;
  213. }
  214. public CompressedBitmapBuilder xor(Bitmap other) {
  215. if (isSameCompressedBitmap(other)) {
  216. bitset.xor(((CompressedBitmap) other).bitmap);
  217. } else if (isSameCompressedBitmapBuilder(other)) {
  218. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  219. bitset.xor(b.bitset.combine());
  220. } else {
  221. throw new IllegalArgumentException();
  222. }
  223. return this;
  224. }
  225. /** @return the fully built immutable bitmap */
  226. public CompressedBitmap build() {
  227. return new CompressedBitmap(bitset.combine());
  228. }
  229. public Iterator<BitmapObject> iterator() {
  230. return build().iterator();
  231. }
  232. public int cardinality() {
  233. return bitset.combine().cardinality();
  234. }
  235. public boolean removeAllOrNone(PackBitmapIndex index) {
  236. if (!packIndex.equals(index))
  237. return false;
  238. EWAHCompressedBitmap curr = bitset.combine()
  239. .xor(ones(indexObjectCount));
  240. IntIterator ii = curr.intIterator();
  241. if (ii.hasNext() && ii.next() < indexObjectCount)
  242. return false;
  243. bitset = new ComboBitset(curr);
  244. return true;
  245. }
  246. private BitmapIndexImpl getBitmapIndex() {
  247. return BitmapIndexImpl.this;
  248. }
  249. }
  250. final class CompressedBitmap implements Bitmap {
  251. private final EWAHCompressedBitmap bitmap;
  252. private CompressedBitmap(EWAHCompressedBitmap bitmap) {
  253. this.bitmap = bitmap;
  254. }
  255. public CompressedBitmap or(Bitmap other) {
  256. return new CompressedBitmap(bitmap.or(bitmapOf(other)));
  257. }
  258. public CompressedBitmap andNot(Bitmap other) {
  259. return new CompressedBitmap(bitmap.andNot(bitmapOf(other)));
  260. }
  261. public CompressedBitmap xor(Bitmap other) {
  262. return new CompressedBitmap(bitmap.xor(bitmapOf(other)));
  263. }
  264. private EWAHCompressedBitmap bitmapOf(Bitmap other) {
  265. if (isSameCompressedBitmap(other))
  266. return ((CompressedBitmap) other).bitmap;
  267. if (isSameCompressedBitmapBuilder(other))
  268. return ((CompressedBitmapBuilder) other).build().bitmap;
  269. CompressedBitmapBuilder builder = newBitmapBuilder();
  270. builder.or(other);
  271. return builder.build().bitmap;
  272. }
  273. private final IntIterator ofObjectType(int type) {
  274. return packIndex.ofObjectType(bitmap, type).intIterator();
  275. }
  276. public Iterator<BitmapObject> iterator() {
  277. final IntIterator dynamic = bitmap.andNot(ones(indexObjectCount))
  278. .intIterator();
  279. final IntIterator commits = ofObjectType(Constants.OBJ_COMMIT);
  280. final IntIterator trees = ofObjectType(Constants.OBJ_TREE);
  281. final IntIterator blobs = ofObjectType(Constants.OBJ_BLOB);
  282. final IntIterator tags = ofObjectType(Constants.OBJ_TAG);
  283. return new Iterator<BitmapObject>() {
  284. private final BitmapObjectImpl out = new BitmapObjectImpl();
  285. private int type;
  286. private IntIterator cached = dynamic;
  287. public boolean hasNext() {
  288. if (!cached.hasNext()) {
  289. if (commits.hasNext()) {
  290. type = Constants.OBJ_COMMIT;
  291. cached = commits;
  292. } else if (trees.hasNext()) {
  293. type = Constants.OBJ_TREE;
  294. cached = trees;
  295. } else if (blobs.hasNext()) {
  296. type = Constants.OBJ_BLOB;
  297. cached = blobs;
  298. } else if (tags.hasNext()) {
  299. type = Constants.OBJ_TAG;
  300. cached = tags;
  301. } else {
  302. return false;
  303. }
  304. }
  305. return true;
  306. }
  307. public BitmapObject next() {
  308. if (!hasNext())
  309. throw new NoSuchElementException();
  310. int position = cached.next();
  311. if (position < indexObjectCount) {
  312. out.type = type;
  313. out.objectId = packIndex.getObject(position);
  314. } else {
  315. position -= indexObjectCount;
  316. MutableEntry entry = mutableIndex.getObject(position);
  317. out.type = entry.type;
  318. out.objectId = entry;
  319. }
  320. return out;
  321. }
  322. public void remove() {
  323. throw new UnsupportedOperationException();
  324. }
  325. };
  326. }
  327. EWAHCompressedBitmap getEwahCompressedBitmap() {
  328. return bitmap;
  329. }
  330. private BitmapIndexImpl getPackBitmapIndex() {
  331. return BitmapIndexImpl.this;
  332. }
  333. }
  334. private static final class MutableBitmapIndex {
  335. private final ObjectIdOwnerMap<MutableEntry>
  336. revMap = new ObjectIdOwnerMap<MutableEntry>();
  337. private final BlockList<MutableEntry>
  338. revList = new BlockList<MutableEntry>();
  339. int findPosition(AnyObjectId objectId) {
  340. MutableEntry entry = revMap.get(objectId);
  341. if (entry == null)
  342. return -1;
  343. return entry.position;
  344. }
  345. MutableEntry getObject(int position) {
  346. try {
  347. MutableEntry entry = revList.get(position);
  348. if (entry == null)
  349. throw new IllegalArgumentException(MessageFormat.format(
  350. JGitText.get().objectNotFound,
  351. String.valueOf(position)));
  352. return entry;
  353. } catch (IndexOutOfBoundsException ex) {
  354. throw new IllegalArgumentException(ex);
  355. }
  356. }
  357. int addObject(AnyObjectId objectId, int type) {
  358. MutableEntry entry = new MutableEntry(
  359. objectId, type, revList.size());
  360. revList.add(entry);
  361. revMap.add(entry);
  362. return entry.position;
  363. }
  364. }
  365. private static final class MutableEntry extends ObjectIdOwnerMap.Entry {
  366. private final int type;
  367. private final int position;
  368. MutableEntry(AnyObjectId objectId, int type, int position) {
  369. super(objectId);
  370. this.type = type;
  371. this.position = position;
  372. }
  373. }
  374. private static final class BitmapObjectImpl extends BitmapObject {
  375. private ObjectId objectId;
  376. private int type;
  377. @Override
  378. public ObjectId getObjectId() {
  379. return objectId;
  380. }
  381. @Override
  382. public int getType() {
  383. return type;
  384. }
  385. }
  386. private boolean isSameCompressedBitmap(Bitmap other) {
  387. if (other instanceof CompressedBitmap) {
  388. CompressedBitmap b = (CompressedBitmap) other;
  389. return this == b.getPackBitmapIndex();
  390. }
  391. return false;
  392. }
  393. private boolean isSameCompressedBitmapBuilder(Bitmap other) {
  394. if (other instanceof CompressedBitmapBuilder) {
  395. CompressedBitmapBuilder b = (CompressedBitmapBuilder) other;
  396. return this == b.getBitmapIndex();
  397. }
  398. return false;
  399. }
  400. private static final EWAHCompressedBitmap ones(int sizeInBits) {
  401. EWAHCompressedBitmap mask = new EWAHCompressedBitmap();
  402. mask.addStreamOfEmptyWords(
  403. true, sizeInBits / EWAHCompressedBitmap.wordinbits);
  404. int remaining = sizeInBits % EWAHCompressedBitmap.wordinbits;
  405. if (remaining > 0)
  406. mask.add((1L << remaining) - 1, remaining);
  407. return mask;
  408. }
  409. }