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.

BlockBasedFile.java 5.4KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. * Copyright (C) 2017, 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.dfs;
  11. import java.io.EOFException;
  12. import java.io.IOException;
  13. import java.nio.ByteBuffer;
  14. import java.text.MessageFormat;
  15. import org.eclipse.jgit.errors.PackInvalidException;
  16. import org.eclipse.jgit.internal.storage.pack.PackExt;
  17. /** Block based file stored in {@link DfsBlockCache}. */
  18. abstract class BlockBasedFile {
  19. /** Cache that owns this file and its data. */
  20. final DfsBlockCache cache;
  21. /** Unique identity of this file while in-memory. */
  22. final DfsStreamKey key;
  23. /** Description of the associated pack file's storage. */
  24. final DfsPackDescription desc;
  25. final PackExt ext;
  26. /**
  27. * Preferred alignment for loading blocks from the backing file.
  28. * <p>
  29. * It is initialized to 0 and filled in on the first read made from the
  30. * file. Block sizes may be odd, e.g. 4091, caused by the underling DFS
  31. * storing 4091 user bytes and 5 bytes block metadata into a lower level
  32. * 4096 byte block on disk.
  33. */
  34. volatile int blockSize;
  35. /**
  36. * Total number of bytes in this pack file.
  37. * <p>
  38. * This field initializes to -1 and gets populated when a block is loaded.
  39. */
  40. volatile long length;
  41. /** True once corruption has been detected that cannot be worked around. */
  42. volatile boolean invalid;
  43. /** Exception that caused the packfile to be flagged as invalid */
  44. protected volatile Exception invalidatingCause;
  45. BlockBasedFile(DfsBlockCache cache, DfsPackDescription desc, PackExt ext) {
  46. this.cache = cache;
  47. this.key = desc.getStreamKey(ext);
  48. this.desc = desc;
  49. this.ext = ext;
  50. }
  51. String getFileName() {
  52. return desc.getFileName(ext);
  53. }
  54. boolean invalid() {
  55. return invalid;
  56. }
  57. void setInvalid() {
  58. invalid = true;
  59. }
  60. void setBlockSize(int newSize) {
  61. blockSize = newSize;
  62. }
  63. long alignToBlock(long pos) {
  64. int size = blockSize;
  65. if (size == 0)
  66. size = cache.getBlockSize();
  67. return (pos / size) * size;
  68. }
  69. int blockSize(ReadableChannel rc) {
  70. // If the block alignment is not yet known, discover it. Prefer the
  71. // larger size from either the cache or the file itself.
  72. int size = blockSize;
  73. if (size == 0) {
  74. size = rc.blockSize();
  75. if (size <= 0)
  76. size = cache.getBlockSize();
  77. else if (size < cache.getBlockSize())
  78. size = (cache.getBlockSize() / size) * size;
  79. blockSize = size;
  80. }
  81. return size;
  82. }
  83. DfsBlock getOrLoadBlock(long pos, DfsReader ctx) throws IOException {
  84. try (LazyChannel c = new LazyChannel(ctx, desc, ext)) {
  85. return cache.getOrLoad(this, pos, ctx, c);
  86. }
  87. }
  88. DfsBlock readOneBlock(long pos, DfsReader ctx, ReadableChannel rc)
  89. throws IOException {
  90. if (invalid) {
  91. throw new PackInvalidException(getFileName(), invalidatingCause);
  92. }
  93. ctx.stats.readBlock++;
  94. long start = System.nanoTime();
  95. try {
  96. int size = blockSize(rc);
  97. pos = (pos / size) * size;
  98. // If the size of the file is not yet known, try to discover it.
  99. // Channels may choose to return -1 to indicate they don't
  100. // know the length yet, in this case read up to the size unit
  101. // given by the caller, then recheck the length.
  102. long len = length;
  103. if (len < 0) {
  104. len = rc.size();
  105. if (0 <= len)
  106. length = len;
  107. }
  108. if (0 <= len && len < pos + size)
  109. size = (int) (len - pos);
  110. if (size <= 0)
  111. throw new EOFException(MessageFormat.format(
  112. DfsText.get().shortReadOfBlock, Long.valueOf(pos),
  113. getFileName(), Long.valueOf(0), Long.valueOf(0)));
  114. byte[] buf = new byte[size];
  115. rc.position(pos);
  116. int cnt = read(rc, ByteBuffer.wrap(buf, 0, size));
  117. ctx.stats.readBlockBytes += cnt;
  118. if (cnt != size) {
  119. if (0 <= len) {
  120. throw new EOFException(MessageFormat.format(
  121. DfsText.get().shortReadOfBlock, Long.valueOf(pos),
  122. getFileName(), Integer.valueOf(size),
  123. Integer.valueOf(cnt)));
  124. }
  125. // Assume the entire thing was read in a single shot, compact
  126. // the buffer to only the space required.
  127. byte[] n = new byte[cnt];
  128. System.arraycopy(buf, 0, n, 0, n.length);
  129. buf = n;
  130. } else if (len < 0) {
  131. // With no length at the start of the read, the channel should
  132. // have the length available at the end.
  133. length = len = rc.size();
  134. }
  135. return new DfsBlock(key, pos, buf);
  136. } finally {
  137. ctx.stats.readBlockMicros += elapsedMicros(start);
  138. }
  139. }
  140. static int read(ReadableChannel rc, ByteBuffer buf) throws IOException {
  141. int n;
  142. do {
  143. n = rc.read(buf);
  144. } while (0 < n && buf.hasRemaining());
  145. return buf.position();
  146. }
  147. static long elapsedMicros(long start) {
  148. return (System.nanoTime() - start) / 1000L;
  149. }
  150. /**
  151. * A supplier of readable channel that opens the channel lazily.
  152. */
  153. private static class LazyChannel
  154. implements AutoCloseable, DfsBlockCache.ReadableChannelSupplier {
  155. private final DfsReader ctx;
  156. private final DfsPackDescription desc;
  157. private final PackExt ext;
  158. private ReadableChannel rc;
  159. LazyChannel(DfsReader ctx, DfsPackDescription desc, PackExt ext) {
  160. this.ctx = ctx;
  161. this.desc = desc;
  162. this.ext = ext;
  163. }
  164. @Override
  165. public ReadableChannel get() throws IOException {
  166. if (rc == null) {
  167. rc = ctx.db.openFile(desc, ext);
  168. }
  169. return rc;
  170. }
  171. @Override
  172. public void close() throws IOException {
  173. if (rc != null) {
  174. rc.close();
  175. }
  176. }
  177. }
  178. }