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.

BlockWriter.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572
  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.reftable;
  11. import static java.nio.charset.StandardCharsets.UTF_8;
  12. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.FILE_HEADER_LEN;
  13. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.INDEX_BLOCK_TYPE;
  14. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_BLOCK_TYPE;
  15. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_DATA;
  16. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.LOG_NONE;
  17. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.MAX_RESTARTS;
  18. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.OBJ_BLOCK_TYPE;
  19. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.REF_BLOCK_TYPE;
  20. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_1ID;
  21. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_2ID;
  22. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_NONE;
  23. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_SYMREF;
  24. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.VALUE_TYPE_MASK;
  25. import static org.eclipse.jgit.internal.storage.reftable.ReftableConstants.reverseUpdateIndex;
  26. import static org.eclipse.jgit.internal.storage.reftable.ReftableOutputStream.computeVarintSize;
  27. import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
  28. import static org.eclipse.jgit.lib.Ref.Storage.NEW;
  29. import java.io.IOException;
  30. import java.util.ArrayList;
  31. import java.util.Arrays;
  32. import java.util.List;
  33. import org.eclipse.jgit.internal.JGitText;
  34. import org.eclipse.jgit.lib.ObjectId;
  35. import org.eclipse.jgit.lib.PersonIdent;
  36. import org.eclipse.jgit.lib.Ref;
  37. import org.eclipse.jgit.util.IntList;
  38. import org.eclipse.jgit.util.LongList;
  39. import org.eclipse.jgit.util.NB;
  40. /** Formats and writes blocks for {@link ReftableWriter}. */
  41. class BlockWriter {
  42. private final byte blockType;
  43. private final byte keyType;
  44. private final List<Entry> entries;
  45. private final int blockLimitBytes;
  46. private final int restartInterval;
  47. private int entriesSumBytes;
  48. private int restartCnt;
  49. BlockWriter(byte type, byte kt, int bs, int ri) {
  50. blockType = type;
  51. keyType = kt;
  52. blockLimitBytes = bs;
  53. restartInterval = ri;
  54. entries = new ArrayList<>(estimateEntryCount(type, kt, bs));
  55. }
  56. private static int estimateEntryCount(byte blockType, byte keyType,
  57. int blockLimitBytes) {
  58. double avgBytesPerEntry;
  59. switch (blockType) {
  60. case REF_BLOCK_TYPE:
  61. default:
  62. avgBytesPerEntry = 35.31;
  63. break;
  64. case OBJ_BLOCK_TYPE:
  65. avgBytesPerEntry = 4.19;
  66. break;
  67. case LOG_BLOCK_TYPE:
  68. avgBytesPerEntry = 101.14;
  69. break;
  70. case INDEX_BLOCK_TYPE:
  71. switch (keyType) {
  72. case REF_BLOCK_TYPE:
  73. case LOG_BLOCK_TYPE:
  74. default:
  75. avgBytesPerEntry = 27.44;
  76. break;
  77. case OBJ_BLOCK_TYPE:
  78. avgBytesPerEntry = 11.57;
  79. break;
  80. }
  81. }
  82. int cnt = (int) (Math.ceil(blockLimitBytes / avgBytesPerEntry));
  83. return Math.min(cnt, 4096);
  84. }
  85. byte blockType() {
  86. return blockType;
  87. }
  88. boolean padBetweenBlocks() {
  89. return padBetweenBlocks(blockType)
  90. || (blockType == INDEX_BLOCK_TYPE && padBetweenBlocks(keyType));
  91. }
  92. static boolean padBetweenBlocks(byte type) {
  93. return type == REF_BLOCK_TYPE || type == OBJ_BLOCK_TYPE;
  94. }
  95. byte[] lastKey() {
  96. return entries.get(entries.size() - 1).key;
  97. }
  98. int currentSize() {
  99. return computeBlockBytes(0, false);
  100. }
  101. void mustAdd(Entry entry) throws BlockSizeTooSmallException {
  102. if (!tryAdd(entry, true)) {
  103. // Insanely long names need a larger block size.
  104. throw blockSizeTooSmall(entry);
  105. }
  106. }
  107. boolean tryAdd(Entry entry) {
  108. if (entry instanceof ObjEntry
  109. && computeBlockBytes(entry.sizeBytes(), 1) > blockLimitBytes) {
  110. // If the ObjEntry has so many ref block pointers that its
  111. // encoding overflows any block, reconfigure it to tell readers to
  112. // instead scan all refs for this ObjectId. That significantly
  113. // shrinks the entry to a very small size, which may now fit into
  114. // this block.
  115. ((ObjEntry) entry).markScanRequired();
  116. }
  117. if (tryAdd(entry, true)) {
  118. return true;
  119. } else if (nextShouldBeRestart()) {
  120. // It was time for another restart, but the entry doesn't fit
  121. // with its complete key, as the block is nearly full. Try to
  122. // force it to fit with prefix compression rather than waste
  123. // the tail of the block with padding.
  124. return tryAdd(entry, false);
  125. }
  126. return false;
  127. }
  128. private boolean tryAdd(Entry entry, boolean tryRestart) {
  129. byte[] key = entry.key;
  130. int prefixLen = 0;
  131. boolean restart = tryRestart && nextShouldBeRestart();
  132. if (!restart) {
  133. Entry priorEntry = entries.get(entries.size() - 1);
  134. byte[] prior = priorEntry.key;
  135. prefixLen = commonPrefix(prior, prior.length, key);
  136. if (prefixLen <= 5 /* "refs/" */ && keyType == REF_BLOCK_TYPE) {
  137. // Force restart points at transitions between namespaces
  138. // such as "refs/heads/" to "refs/tags/".
  139. restart = true;
  140. prefixLen = 0;
  141. } else if (prefixLen == 0) {
  142. restart = true;
  143. }
  144. }
  145. entry.restart = restart;
  146. entry.prefixLen = prefixLen;
  147. int entryBytes = entry.sizeBytes();
  148. if (computeBlockBytes(entryBytes, restart) > blockLimitBytes) {
  149. return false;
  150. }
  151. entriesSumBytes += entryBytes;
  152. entries.add(entry);
  153. if (restart) {
  154. restartCnt++;
  155. }
  156. return true;
  157. }
  158. private boolean nextShouldBeRestart() {
  159. int cnt = entries.size();
  160. return (cnt == 0 || ((cnt + 1) % restartInterval) == 0)
  161. && restartCnt < MAX_RESTARTS;
  162. }
  163. private int computeBlockBytes(int entryBytes, boolean restart) {
  164. return computeBlockBytes(
  165. entriesSumBytes + entryBytes,
  166. restartCnt + (restart ? 1 : 0));
  167. }
  168. private static int computeBlockBytes(int entryBytes, int restartCnt) {
  169. return 4 // 4-byte block header
  170. + entryBytes
  171. + restartCnt * 3 // restart_offset
  172. + 2; // 2-byte restart_count
  173. }
  174. void writeTo(ReftableOutputStream os) throws IOException {
  175. os.beginBlock(blockType);
  176. IntList restarts = new IntList(restartCnt);
  177. for (Entry entry : entries) {
  178. if (entry.restart) {
  179. restarts.add(os.bytesWrittenInBlock());
  180. }
  181. entry.writeKey(os);
  182. entry.writeValue(os);
  183. }
  184. if (restarts.size() == 0 || restarts.size() > MAX_RESTARTS) {
  185. throw new IllegalStateException();
  186. }
  187. for (int i = 0; i < restarts.size(); i++) {
  188. os.writeInt24(restarts.get(i));
  189. }
  190. os.writeInt16(restarts.size());
  191. os.flushBlock();
  192. }
  193. private BlockSizeTooSmallException blockSizeTooSmall(Entry entry) {
  194. // Compute size required to fit this entry by itself.
  195. int min = FILE_HEADER_LEN + computeBlockBytes(entry.sizeBytes(), 1);
  196. return new BlockSizeTooSmallException(min);
  197. }
  198. static int commonPrefix(byte[] a, int n, byte[] b) {
  199. int len = Math.min(n, Math.min(a.length, b.length));
  200. for (int i = 0; i < len; i++) {
  201. if (a[i] != b[i]) {
  202. return i;
  203. }
  204. }
  205. return len;
  206. }
  207. static int encodeSuffixAndType(int sfx, int valueType) {
  208. return (sfx << 3) | valueType;
  209. }
  210. static int compare(
  211. byte[] a, int ai, int aLen,
  212. byte[] b, int bi, int bLen) {
  213. int aEnd = ai + aLen;
  214. int bEnd = bi + bLen;
  215. while (ai < aEnd && bi < bEnd) {
  216. int c = (a[ai++] & 0xff) - (b[bi++] & 0xff);
  217. if (c != 0) {
  218. return c;
  219. }
  220. }
  221. return aLen - bLen;
  222. }
  223. abstract static class Entry {
  224. static int compare(Entry ea, Entry eb) {
  225. byte[] a = ea.key;
  226. byte[] b = eb.key;
  227. return BlockWriter.compare(a, 0, a.length, b, 0, b.length);
  228. }
  229. final byte[] key;
  230. int prefixLen;
  231. boolean restart;
  232. Entry(byte[] key) {
  233. this.key = key;
  234. }
  235. void writeKey(ReftableOutputStream os) {
  236. int sfxLen = key.length - prefixLen;
  237. os.writeVarint(prefixLen);
  238. os.writeVarint(encodeSuffixAndType(sfxLen, valueType()));
  239. os.write(key, prefixLen, sfxLen);
  240. }
  241. int sizeBytes() {
  242. int sfxLen = key.length - prefixLen;
  243. int sfx = encodeSuffixAndType(sfxLen, valueType());
  244. return computeVarintSize(prefixLen)
  245. + computeVarintSize(sfx)
  246. + sfxLen
  247. + valueSize();
  248. }
  249. abstract byte blockType();
  250. abstract int valueType();
  251. abstract int valueSize();
  252. abstract void writeValue(ReftableOutputStream os) throws IOException;
  253. }
  254. static class IndexEntry extends Entry {
  255. private final long blockPosition;
  256. IndexEntry(byte[] key, long blockPosition) {
  257. super(key);
  258. this.blockPosition = blockPosition;
  259. }
  260. @Override
  261. byte blockType() {
  262. return INDEX_BLOCK_TYPE;
  263. }
  264. @Override
  265. int valueType() {
  266. return 0;
  267. }
  268. @Override
  269. int valueSize() {
  270. return computeVarintSize(blockPosition);
  271. }
  272. @Override
  273. void writeValue(ReftableOutputStream os) {
  274. os.writeVarint(blockPosition);
  275. }
  276. }
  277. static class RefEntry extends Entry {
  278. final Ref ref;
  279. final long updateIndexDelta;
  280. RefEntry(Ref ref, long updateIndexDelta) {
  281. super(nameUtf8(ref));
  282. this.ref = ref;
  283. this.updateIndexDelta = updateIndexDelta;
  284. }
  285. @Override
  286. byte blockType() {
  287. return REF_BLOCK_TYPE;
  288. }
  289. @Override
  290. int valueType() {
  291. if (ref.isSymbolic()) {
  292. return VALUE_SYMREF;
  293. } else if (ref.getStorage() == NEW && ref.getObjectId() == null) {
  294. return VALUE_NONE;
  295. } else if (ref.getPeeledObjectId() != null) {
  296. return VALUE_2ID;
  297. } else {
  298. return VALUE_1ID;
  299. }
  300. }
  301. @Override
  302. int valueSize() {
  303. int n = computeVarintSize(updateIndexDelta);
  304. switch (valueType()) {
  305. case VALUE_NONE:
  306. return n;
  307. case VALUE_1ID:
  308. return n + OBJECT_ID_LENGTH;
  309. case VALUE_2ID:
  310. return n + 2 * OBJECT_ID_LENGTH;
  311. case VALUE_SYMREF:
  312. if (ref.isSymbolic()) {
  313. int nameLen = nameUtf8(ref.getTarget()).length;
  314. return n + computeVarintSize(nameLen) + nameLen;
  315. }
  316. }
  317. throw new IllegalStateException();
  318. }
  319. @Override
  320. void writeValue(ReftableOutputStream os) throws IOException {
  321. os.writeVarint(updateIndexDelta);
  322. switch (valueType()) {
  323. case VALUE_NONE:
  324. return;
  325. case VALUE_1ID: {
  326. ObjectId id1 = ref.getObjectId();
  327. if (!ref.isPeeled()) {
  328. throw new IOException(JGitText.get().peeledRefIsRequired);
  329. } else if (id1 == null) {
  330. throw new IOException(JGitText.get().invalidId0);
  331. }
  332. os.writeId(id1);
  333. return;
  334. }
  335. case VALUE_2ID: {
  336. ObjectId id1 = ref.getObjectId();
  337. ObjectId id2 = ref.getPeeledObjectId();
  338. if (!ref.isPeeled()) {
  339. throw new IOException(JGitText.get().peeledRefIsRequired);
  340. } else if (id1 == null || id2 == null) {
  341. throw new IOException(JGitText.get().invalidId0);
  342. }
  343. os.writeId(id1);
  344. os.writeId(id2);
  345. return;
  346. }
  347. case VALUE_SYMREF:
  348. if (ref.isSymbolic()) {
  349. os.writeVarintString(ref.getTarget().getName());
  350. return;
  351. }
  352. }
  353. throw new IllegalStateException();
  354. }
  355. private static byte[] nameUtf8(Ref ref) {
  356. return ref.getName().getBytes(UTF_8);
  357. }
  358. }
  359. static class ObjEntry extends Entry {
  360. final LongList blockPos;
  361. ObjEntry(int idLen, ObjectId id, LongList blockPos) {
  362. super(key(idLen, id));
  363. this.blockPos = blockPos;
  364. }
  365. private static byte[] key(int idLen, ObjectId id) {
  366. byte[] key = new byte[OBJECT_ID_LENGTH];
  367. id.copyRawTo(key, 0);
  368. if (idLen < OBJECT_ID_LENGTH) {
  369. return Arrays.copyOf(key, idLen);
  370. }
  371. return key;
  372. }
  373. void markScanRequired() {
  374. blockPos.clear();
  375. }
  376. @Override
  377. byte blockType() {
  378. return OBJ_BLOCK_TYPE;
  379. }
  380. @Override
  381. int valueType() {
  382. int cnt = blockPos.size();
  383. return cnt != 0 && cnt <= VALUE_TYPE_MASK ? cnt : 0;
  384. }
  385. @Override
  386. int valueSize() {
  387. int cnt = blockPos.size();
  388. if (cnt == 0) {
  389. return computeVarintSize(0);
  390. }
  391. int n = 0;
  392. if (cnt > VALUE_TYPE_MASK) {
  393. n += computeVarintSize(cnt);
  394. }
  395. n += computeVarintSize(blockPos.get(0));
  396. for (int j = 1; j < cnt; j++) {
  397. long prior = blockPos.get(j - 1);
  398. long b = blockPos.get(j);
  399. n += computeVarintSize(b - prior);
  400. }
  401. return n;
  402. }
  403. @Override
  404. void writeValue(ReftableOutputStream os) throws IOException {
  405. int cnt = blockPos.size();
  406. if (cnt == 0) {
  407. os.writeVarint(0);
  408. return;
  409. }
  410. if (cnt > VALUE_TYPE_MASK) {
  411. os.writeVarint(cnt);
  412. }
  413. os.writeVarint(blockPos.get(0));
  414. for (int j = 1; j < cnt; j++) {
  415. long prior = blockPos.get(j - 1);
  416. long b = blockPos.get(j);
  417. os.writeVarint(b - prior);
  418. }
  419. }
  420. }
  421. static class DeleteLogEntry extends Entry {
  422. DeleteLogEntry(String refName, long updateIndex) {
  423. super(LogEntry.key(refName, updateIndex));
  424. }
  425. @Override
  426. byte blockType() {
  427. return LOG_BLOCK_TYPE;
  428. }
  429. @Override
  430. int valueType() {
  431. return LOG_NONE;
  432. }
  433. @Override
  434. int valueSize() {
  435. return 0;
  436. }
  437. @Override
  438. void writeValue(ReftableOutputStream os) {
  439. // Nothing in a delete log record.
  440. }
  441. }
  442. static class LogEntry extends Entry {
  443. final ObjectId oldId;
  444. final ObjectId newId;
  445. final long timeSecs;
  446. final short tz;
  447. final byte[] name;
  448. final byte[] email;
  449. final byte[] msg;
  450. LogEntry(String refName, long updateIndex, PersonIdent who,
  451. ObjectId oldId, ObjectId newId, String message) {
  452. super(key(refName, updateIndex));
  453. this.oldId = oldId;
  454. this.newId = newId;
  455. this.timeSecs = who.getWhen().getTime() / 1000L;
  456. this.tz = (short) who.getTimeZoneOffset();
  457. this.name = who.getName().getBytes(UTF_8);
  458. this.email = who.getEmailAddress().getBytes(UTF_8);
  459. this.msg = message.getBytes(UTF_8);
  460. }
  461. static byte[] key(String ref, long index) {
  462. byte[] name = ref.getBytes(UTF_8);
  463. byte[] key = Arrays.copyOf(name, name.length + 1 + 8);
  464. NB.encodeInt64(key, key.length - 8, reverseUpdateIndex(index));
  465. return key;
  466. }
  467. @Override
  468. byte blockType() {
  469. return LOG_BLOCK_TYPE;
  470. }
  471. @Override
  472. int valueType() {
  473. return LOG_DATA;
  474. }
  475. @Override
  476. int valueSize() {
  477. return 2 * OBJECT_ID_LENGTH
  478. + computeVarintSize(name.length) + name.length
  479. + computeVarintSize(email.length) + email.length
  480. + computeVarintSize(timeSecs)
  481. + 2 // tz
  482. + computeVarintSize(msg.length) + msg.length;
  483. }
  484. @Override
  485. void writeValue(ReftableOutputStream os) {
  486. os.writeId(oldId);
  487. os.writeId(newId);
  488. os.writeVarintString(name);
  489. os.writeVarintString(email);
  490. os.writeVarint(timeSecs);
  491. os.writeInt16(tz);
  492. os.writeVarintString(msg);
  493. }
  494. }
  495. }