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.

ReftableReader.java 18KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707
  1. /*
  2. * Copyright (C) 2017, 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.reftable;
  44. import static java.nio.charset.StandardCharsets.UTF_8;
  45. import static org.eclipse.jgit.internal.storage.reftable.BlockReader.decodeBlockLen;
  46. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_BLOCK_TYPE;
  47. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_FOOTER_LEN;
  48. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
  49. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
  50. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
  51. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
  52. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VERSION_1;
  53. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.isFileHeaderMagic;
  54. import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
  55. import java.io.IOException;
  56. import java.nio.ByteBuffer;
  57. import java.text.MessageFormat;
  58. import java.util.Arrays;
  59. import java.util.zip.CRC32;
  60. import org.eclipse.jgit.internal.JGitText;
  61. import org.eclipse.jgit.internal.storage.io.BlockSource;
  62. import org.eclipse.jgit.internal.storage.reftable.BlockWriter.LogEntry;
  63. import org.eclipse.jgit.lib.AnyObjectId;
  64. import org.eclipse.jgit.lib.ObjectId;
  65. import org.eclipse.jgit.lib.Ref;
  66. import org.eclipse.jgit.lib.ReflogEntry;
  67. import org.eclipse.jgit.util.LongList;
  68. import org.eclipse.jgit.util.LongMap;
  69. import org.eclipse.jgit.util.NB;
  70. /**
  71. * Reads a reftable formatted file.
  72. * <p>
  73. * {@code ReftableReader} is not thread-safe. Concurrent readers need their own
  74. * instance to read from the same file.
  75. */
  76. public class ReftableReader extends Reftable {
  77. private final BlockSource src;
  78. private int blockSize = -1;
  79. private long minUpdateIndex;
  80. private long maxUpdateIndex;
  81. private long refEnd;
  82. private long objPosition;
  83. private long objEnd;
  84. private long logPosition;
  85. private long logEnd;
  86. private int objIdLen;
  87. private long refIndexPosition = -1;
  88. private long objIndexPosition = -1;
  89. private long logIndexPosition = -1;
  90. private BlockReader refIndex;
  91. private BlockReader objIndex;
  92. private BlockReader logIndex;
  93. private LongMap<BlockReader> indexCache;
  94. /**
  95. * Initialize a new reftable reader.
  96. *
  97. * @param src
  98. * the file content to read.
  99. */
  100. public ReftableReader(BlockSource src) {
  101. this.src = src;
  102. }
  103. /**
  104. * Get the block size in bytes chosen for this file by the writer.
  105. *
  106. * @return the block size in bytes chosen for this file by the writer. Most
  107. * reads from the
  108. * {@link org.eclipse.jgit.internal.storage.io.BlockSource} will be
  109. * aligned to the block size.
  110. * @throws java.io.IOException
  111. * file cannot be read.
  112. */
  113. public int blockSize() throws IOException {
  114. if (blockSize == -1) {
  115. readFileHeader();
  116. }
  117. return blockSize;
  118. }
  119. /**
  120. * Get the minimum update index for log entries that appear in this
  121. * reftable.
  122. *
  123. * @return the minimum update index for log entries that appear in this
  124. * reftable. This should be 1 higher than the prior reftable's
  125. * {@code maxUpdateIndex} if this table is used in a stack.
  126. * @throws java.io.IOException
  127. * file cannot be read.
  128. */
  129. public long minUpdateIndex() throws IOException {
  130. if (blockSize == -1) {
  131. readFileHeader();
  132. }
  133. return minUpdateIndex;
  134. }
  135. /**
  136. * Get the maximum update index for log entries that appear in this
  137. * reftable.
  138. *
  139. * @return the maximum update index for log entries that appear in this
  140. * reftable. This should be 1 higher than the prior reftable's
  141. * {@code maxUpdateIndex} if this table is used in a stack.
  142. * @throws java.io.IOException
  143. * file cannot be read.
  144. */
  145. public long maxUpdateIndex() throws IOException {
  146. if (blockSize == -1) {
  147. readFileHeader();
  148. }
  149. return maxUpdateIndex;
  150. }
  151. /** {@inheritDoc} */
  152. @Override
  153. public RefCursor allRefs() throws IOException {
  154. if (blockSize == -1) {
  155. readFileHeader();
  156. }
  157. long end = refEnd > 0 ? refEnd : (src.size() - FILE_FOOTER_LEN);
  158. src.adviseSequentialRead(0, end);
  159. RefCursorImpl i = new RefCursorImpl(end, null, false);
  160. i.block = readBlock(0, end);
  161. return i;
  162. }
  163. /** {@inheritDoc} */
  164. @Override
  165. public RefCursor seekRef(String refName) throws IOException {
  166. initRefIndex();
  167. byte[] key = refName.getBytes(UTF_8);
  168. RefCursorImpl i = new RefCursorImpl(refEnd, key, false);
  169. i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
  170. return i;
  171. }
  172. /** {@inheritDoc} */
  173. @Override
  174. public RefCursor seekRefsWithPrefix(String prefix) throws IOException {
  175. initRefIndex();
  176. byte[] key = prefix.getBytes(UTF_8);
  177. RefCursorImpl i = new RefCursorImpl(refEnd, key, true);
  178. i.block = seek(REF_BLOCK_TYPE, key, refIndex, 0, refEnd);
  179. return i;
  180. }
  181. /** {@inheritDoc} */
  182. @Override
  183. public RefCursor byObjectId(AnyObjectId id) throws IOException {
  184. initObjIndex();
  185. ObjCursorImpl i = new ObjCursorImpl(refEnd, id);
  186. if (objIndex != null) {
  187. i.initSeek();
  188. } else {
  189. i.initScan();
  190. }
  191. return i;
  192. }
  193. /** {@inheritDoc} */
  194. @Override
  195. public LogCursor allLogs() throws IOException {
  196. initLogIndex();
  197. if (logPosition > 0) {
  198. src.adviseSequentialRead(logPosition, logEnd);
  199. LogCursorImpl i = new LogCursorImpl(logEnd, null);
  200. i.block = readBlock(logPosition, logEnd);
  201. return i;
  202. }
  203. return new EmptyLogCursor();
  204. }
  205. /** {@inheritDoc} */
  206. @Override
  207. public LogCursor seekLog(String refName, long updateIndex)
  208. throws IOException {
  209. initLogIndex();
  210. if (logPosition > 0) {
  211. byte[] key = LogEntry.key(refName, updateIndex);
  212. byte[] match = refName.getBytes(UTF_8);
  213. LogCursorImpl i = new LogCursorImpl(logEnd, match);
  214. i.block = seek(LOG_BLOCK_TYPE, key, logIndex, logPosition, logEnd);
  215. return i;
  216. }
  217. return new EmptyLogCursor();
  218. }
  219. private BlockReader seek(byte blockType, byte[] key, BlockReader idx,
  220. long startPos, long endPos) throws IOException {
  221. if (idx != null) {
  222. // Walk through a possibly multi-level index to a leaf block.
  223. BlockReader block = idx;
  224. do {
  225. if (block.seekKey(key) > 0) {
  226. return null;
  227. }
  228. long pos = block.readPositionFromIndex();
  229. block = readBlock(pos, endPos);
  230. } while (block.type() == INDEX_BLOCK_TYPE);
  231. block.seekKey(key);
  232. return block;
  233. }
  234. return binarySearch(blockType, key, startPos, endPos);
  235. }
  236. private BlockReader binarySearch(byte blockType, byte[] key,
  237. long startPos, long endPos) throws IOException {
  238. if (blockSize == 0) {
  239. BlockReader b = readBlock(startPos, endPos);
  240. if (blockType != b.type()) {
  241. return null;
  242. }
  243. b.seekKey(key);
  244. return b;
  245. }
  246. int low = (int) (startPos / blockSize);
  247. int end = blocksIn(startPos, endPos);
  248. BlockReader block = null;
  249. do {
  250. int mid = (low + end) >>> 1;
  251. block = readBlock(((long) mid) * blockSize, endPos);
  252. if (blockType != block.type()) {
  253. return null;
  254. }
  255. int cmp = block.seekKey(key);
  256. if (cmp < 0) {
  257. end = mid;
  258. } else if (cmp == 0) {
  259. break;
  260. } else /* if (cmp > 0) */ {
  261. low = mid + 1;
  262. }
  263. } while (low < end);
  264. return block;
  265. }
  266. private void readFileHeader() throws IOException {
  267. readHeaderOrFooter(0, FILE_HEADER_LEN);
  268. }
  269. private void readFileFooter() throws IOException {
  270. int ftrLen = FILE_FOOTER_LEN;
  271. byte[] ftr = readHeaderOrFooter(src.size() - ftrLen, ftrLen);
  272. CRC32 crc = new CRC32();
  273. crc.update(ftr, 0, ftrLen - 4);
  274. if (crc.getValue() != NB.decodeUInt32(ftr, ftrLen - 4)) {
  275. throw new IOException(JGitText.get().invalidReftableCRC);
  276. }
  277. refIndexPosition = NB.decodeInt64(ftr, 24);
  278. long p = NB.decodeInt64(ftr, 32);
  279. objPosition = p >>> 5;
  280. objIdLen = (int) (p & 0x1f);
  281. objIndexPosition = NB.decodeInt64(ftr, 40);
  282. logPosition = NB.decodeInt64(ftr, 48);
  283. logIndexPosition = NB.decodeInt64(ftr, 56);
  284. if (refIndexPosition > 0) {
  285. refEnd = refIndexPosition;
  286. } else if (objPosition > 0) {
  287. refEnd = objPosition;
  288. } else if (logPosition > 0) {
  289. refEnd = logPosition;
  290. } else {
  291. refEnd = src.size() - ftrLen;
  292. }
  293. if (objPosition > 0) {
  294. if (objIndexPosition > 0) {
  295. objEnd = objIndexPosition;
  296. } else if (logPosition > 0) {
  297. objEnd = logPosition;
  298. } else {
  299. objEnd = src.size() - ftrLen;
  300. }
  301. }
  302. if (logPosition > 0) {
  303. if (logIndexPosition > 0) {
  304. logEnd = logIndexPosition;
  305. } else {
  306. logEnd = src.size() - ftrLen;
  307. }
  308. }
  309. }
  310. private byte[] readHeaderOrFooter(long pos, int len) throws IOException {
  311. ByteBuffer buf = src.read(pos, len);
  312. if (buf.position() != len) {
  313. throw new IOException(JGitText.get().shortReadOfBlock);
  314. }
  315. byte[] tmp = new byte[len];
  316. buf.flip();
  317. buf.get(tmp);
  318. if (!isFileHeaderMagic(tmp, 0, len)) {
  319. throw new IOException(JGitText.get().invalidReftableFile);
  320. }
  321. int v = NB.decodeInt32(tmp, 4);
  322. int version = v >>> 24;
  323. if (VERSION_1 != version) {
  324. throw new IOException(MessageFormat.format(
  325. JGitText.get().unsupportedReftableVersion,
  326. Integer.valueOf(version)));
  327. }
  328. if (blockSize == -1) {
  329. blockSize = v & 0xffffff;
  330. }
  331. minUpdateIndex = NB.decodeInt64(tmp, 8);
  332. maxUpdateIndex = NB.decodeInt64(tmp, 16);
  333. return tmp;
  334. }
  335. private void initRefIndex() throws IOException {
  336. if (refIndexPosition < 0) {
  337. readFileFooter();
  338. }
  339. if (refIndex == null && refIndexPosition > 0) {
  340. refIndex = readIndex(refIndexPosition);
  341. }
  342. }
  343. private void initObjIndex() throws IOException {
  344. if (objIndexPosition < 0) {
  345. readFileFooter();
  346. }
  347. if (objIndex == null && objIndexPosition > 0) {
  348. objIndex = readIndex(objIndexPosition);
  349. }
  350. }
  351. private void initLogIndex() throws IOException {
  352. if (logIndexPosition < 0) {
  353. readFileFooter();
  354. }
  355. if (logIndex == null && logIndexPosition > 0) {
  356. logIndex = readIndex(logIndexPosition);
  357. }
  358. }
  359. private BlockReader readIndex(long pos) throws IOException {
  360. int sz = readBlockLen(pos);
  361. BlockReader i = new BlockReader();
  362. i.readBlock(src, pos, sz);
  363. i.verifyIndex();
  364. return i;
  365. }
  366. private int readBlockLen(long pos) throws IOException {
  367. int sz = pos == 0 ? FILE_HEADER_LEN + 4 : 4;
  368. ByteBuffer tmp = src.read(pos, sz);
  369. if (tmp.position() < sz) {
  370. throw new IOException(JGitText.get().invalidReftableFile);
  371. }
  372. byte[] buf;
  373. if (tmp.hasArray() && tmp.arrayOffset() == 0) {
  374. buf = tmp.array();
  375. } else {
  376. buf = new byte[sz];
  377. tmp.flip();
  378. tmp.get(buf);
  379. }
  380. if (pos == 0 && buf[FILE_HEADER_LEN] == FILE_BLOCK_TYPE) {
  381. return FILE_HEADER_LEN;
  382. }
  383. int p = pos == 0 ? FILE_HEADER_LEN : 0;
  384. return decodeBlockLen(NB.decodeInt32(buf, p));
  385. }
  386. private BlockReader readBlock(long pos, long end) throws IOException {
  387. if (indexCache != null) {
  388. BlockReader b = indexCache.get(pos);
  389. if (b != null) {
  390. return b;
  391. }
  392. }
  393. int sz = blockSize;
  394. if (sz == 0) {
  395. sz = readBlockLen(pos);
  396. } else if (pos + sz > end) {
  397. sz = (int) (end - pos); // last block may omit padding.
  398. }
  399. BlockReader b = new BlockReader();
  400. b.readBlock(src, pos, sz);
  401. if (b.type() == INDEX_BLOCK_TYPE && !b.truncated()) {
  402. if (indexCache == null) {
  403. indexCache = new LongMap<>();
  404. }
  405. indexCache.put(pos, b);
  406. }
  407. return b;
  408. }
  409. private int blocksIn(long pos, long end) {
  410. int blocks = (int) ((end - pos) / blockSize);
  411. return end % blockSize == 0 ? blocks : (blocks + 1);
  412. }
  413. /**
  414. * Get size of the reftable, in bytes.
  415. *
  416. * @return size of the reftable, in bytes.
  417. * @throws java.io.IOException
  418. * size cannot be obtained.
  419. */
  420. public long size() throws IOException {
  421. return src.size();
  422. }
  423. /** {@inheritDoc} */
  424. @Override
  425. public void close() throws IOException {
  426. src.close();
  427. }
  428. private class RefCursorImpl extends RefCursor {
  429. private final long scanEnd;
  430. private final byte[] match;
  431. private final boolean prefix;
  432. private Ref ref;
  433. private long updateIndex;
  434. BlockReader block;
  435. RefCursorImpl(long scanEnd, byte[] match, boolean prefix) {
  436. this.scanEnd = scanEnd;
  437. this.match = match;
  438. this.prefix = prefix;
  439. }
  440. @Override
  441. public boolean next() throws IOException {
  442. for (;;) {
  443. if (block == null || block.type() != REF_BLOCK_TYPE) {
  444. return false;
  445. } else if (!block.next()) {
  446. long pos = block.endPosition();
  447. if (pos >= scanEnd) {
  448. return false;
  449. }
  450. block = readBlock(pos, scanEnd);
  451. continue;
  452. }
  453. block.parseKey();
  454. if (match != null && !block.match(match, prefix)) {
  455. block.skipValue();
  456. return false;
  457. }
  458. updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
  459. ref = block.readRef();
  460. if (!includeDeletes && wasDeleted()) {
  461. continue;
  462. }
  463. return true;
  464. }
  465. }
  466. @Override
  467. public Ref getRef() {
  468. return ref;
  469. }
  470. @Override
  471. public long getUpdateIndex() {
  472. return updateIndex;
  473. }
  474. @Override
  475. public void close() {
  476. // Do nothing.
  477. }
  478. }
  479. private class LogCursorImpl extends LogCursor {
  480. private final long scanEnd;
  481. private final byte[] match;
  482. private String refName;
  483. private long updateIndex;
  484. private ReflogEntry entry;
  485. BlockReader block;
  486. LogCursorImpl(long scanEnd, byte[] match) {
  487. this.scanEnd = scanEnd;
  488. this.match = match;
  489. }
  490. @Override
  491. public boolean next() throws IOException {
  492. for (;;) {
  493. if (block == null || block.type() != LOG_BLOCK_TYPE) {
  494. return false;
  495. } else if (!block.next()) {
  496. long pos = block.endPosition();
  497. if (pos >= scanEnd) {
  498. return false;
  499. }
  500. block = readBlock(pos, scanEnd);
  501. continue;
  502. }
  503. block.parseKey();
  504. if (match != null && !block.match(match, false)) {
  505. block.skipValue();
  506. return false;
  507. }
  508. refName = block.name();
  509. updateIndex = block.readLogUpdateIndex();
  510. entry = block.readLogEntry();
  511. if (entry == null && !includeDeletes) {
  512. continue;
  513. }
  514. return true;
  515. }
  516. }
  517. @Override
  518. public String getRefName() {
  519. return refName;
  520. }
  521. @Override
  522. public long getUpdateIndex() {
  523. return updateIndex;
  524. }
  525. @Override
  526. public ReflogEntry getReflogEntry() {
  527. return entry;
  528. }
  529. @Override
  530. public void close() {
  531. // Do nothing.
  532. }
  533. }
  534. static final LongList EMPTY_LONG_LIST = new LongList(0);
  535. private class ObjCursorImpl extends RefCursor {
  536. private final long scanEnd;
  537. private final ObjectId match;
  538. private Ref ref;
  539. private long updateIndex;
  540. private int listIdx;
  541. private LongList blockPos;
  542. private BlockReader block;
  543. ObjCursorImpl(long scanEnd, AnyObjectId id) {
  544. this.scanEnd = scanEnd;
  545. this.match = id.copy();
  546. }
  547. void initSeek() throws IOException {
  548. byte[] rawId = new byte[OBJECT_ID_LENGTH];
  549. match.copyRawTo(rawId, 0);
  550. byte[] key = Arrays.copyOf(rawId, objIdLen);
  551. BlockReader b = objIndex;
  552. do {
  553. if (b.seekKey(key) > 0) {
  554. blockPos = EMPTY_LONG_LIST;
  555. return;
  556. }
  557. long pos = b.readPositionFromIndex();
  558. b = readBlock(pos, objEnd);
  559. } while (b.type() == INDEX_BLOCK_TYPE);
  560. b.seekKey(key);
  561. while (b.next()) {
  562. b.parseKey();
  563. if (b.match(key, false)) {
  564. blockPos = b.readBlockPositionList();
  565. if (blockPos == null) {
  566. initScan();
  567. return;
  568. }
  569. break;
  570. }
  571. b.skipValue();
  572. }
  573. if (blockPos == null) {
  574. blockPos = EMPTY_LONG_LIST;
  575. }
  576. if (blockPos.size() > 0) {
  577. long pos = blockPos.get(listIdx++);
  578. block = readBlock(pos, scanEnd);
  579. }
  580. }
  581. void initScan() throws IOException {
  582. block = readBlock(0, scanEnd);
  583. }
  584. @Override
  585. public boolean next() throws IOException {
  586. for (;;) {
  587. if (block == null || block.type() != REF_BLOCK_TYPE) {
  588. return false;
  589. } else if (!block.next()) {
  590. long pos;
  591. if (blockPos != null) {
  592. if (listIdx >= blockPos.size()) {
  593. return false;
  594. }
  595. pos = blockPos.get(listIdx++);
  596. } else {
  597. pos = block.endPosition();
  598. }
  599. if (pos >= scanEnd) {
  600. return false;
  601. }
  602. block = readBlock(pos, scanEnd);
  603. continue;
  604. }
  605. block.parseKey();
  606. updateIndex = minUpdateIndex + block.readUpdateIndexDelta();
  607. ref = block.readRef();
  608. ObjectId id = ref.getObjectId();
  609. if (id != null && match.equals(id)
  610. && (includeDeletes || !wasDeleted())) {
  611. return true;
  612. }
  613. }
  614. }
  615. @Override
  616. public Ref getRef() {
  617. return ref;
  618. }
  619. @Override
  620. public long getUpdateIndex() {
  621. return updateIndex;
  622. }
  623. @Override
  624. public void close() {
  625. // Do nothing.
  626. }
  627. }
  628. }