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.

NoteParser.java 5.1KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. /*
  2. * Copyright (C) 2010, 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.notes;
  11. import static org.eclipse.jgit.lib.Constants.OBJECT_ID_STRING_LENGTH;
  12. import static org.eclipse.jgit.lib.Constants.encodeASCII;
  13. import static org.eclipse.jgit.lib.FileMode.TREE;
  14. import static org.eclipse.jgit.util.RawParseUtils.parseHexInt4;
  15. import java.io.IOException;
  16. import org.eclipse.jgit.errors.IncorrectObjectTypeException;
  17. import org.eclipse.jgit.lib.AbbreviatedObjectId;
  18. import org.eclipse.jgit.lib.FileMode;
  19. import org.eclipse.jgit.lib.MutableObjectId;
  20. import org.eclipse.jgit.lib.ObjectId;
  21. import org.eclipse.jgit.lib.ObjectReader;
  22. import org.eclipse.jgit.treewalk.CanonicalTreeParser;
  23. /** Custom tree parser to select note bucket type and load it. */
  24. final class NoteParser extends CanonicalTreeParser {
  25. /**
  26. * Parse a tree object into a {@link NoteBucket} instance.
  27. *
  28. * The type of note tree is automatically detected by examining the items
  29. * within the tree, and allocating the proper storage type based on the
  30. * first note-like entry encountered. Since the method parses by guessing
  31. * the type on the first element, malformed note trees can be read as the
  32. * wrong type of tree.
  33. *
  34. * This method is not recursive, it parses the one tree given to it and
  35. * returns the bucket. If there are subtrees for note storage, they are
  36. * setup as lazy pointers that will be resolved at a later time.
  37. *
  38. * @param prefix
  39. * common hex digits that all notes within this tree share. The
  40. * root tree has {@code prefix.length() == 0}, the first-level
  41. * subtrees should be {@code prefix.length()==2}, etc.
  42. * @param treeId
  43. * the tree to read from the repository.
  44. * @param reader
  45. * reader to access the tree object.
  46. * @return bucket to holding the notes of the specified tree.
  47. * @throws IOException
  48. * {@code treeId} cannot be accessed.
  49. */
  50. static InMemoryNoteBucket parse(AbbreviatedObjectId prefix,
  51. final ObjectId treeId, final ObjectReader reader)
  52. throws IOException {
  53. return new NoteParser(prefix, reader, treeId).parse();
  54. }
  55. private final int prefixLen;
  56. private final int pathPadding;
  57. private NonNoteEntry firstNonNote;
  58. private NonNoteEntry lastNonNote;
  59. private NoteParser(AbbreviatedObjectId prefix, ObjectReader r, ObjectId t)
  60. throws IncorrectObjectTypeException, IOException {
  61. super(encodeASCII(prefix.name()), r, t);
  62. prefixLen = prefix.length();
  63. // Our path buffer has a '/' that we don't want after the prefix.
  64. // Drop it by shifting the path down one position.
  65. pathPadding = 0 < prefixLen ? 1 : 0;
  66. if (0 < pathPadding)
  67. System.arraycopy(path, 0, path, pathPadding, prefixLen);
  68. }
  69. private InMemoryNoteBucket parse() {
  70. InMemoryNoteBucket r = parseTree();
  71. r.nonNotes = firstNonNote;
  72. return r;
  73. }
  74. private InMemoryNoteBucket parseTree() {
  75. for (; !eof(); next(1)) {
  76. if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH && isHex())
  77. return parseLeafTree();
  78. else if (getNameLength() == 2 && isHex() && isTree())
  79. return parseFanoutTree();
  80. else
  81. storeNonNote();
  82. }
  83. // If we cannot determine the style used, assume its a leaf.
  84. return new LeafBucket(prefixLen);
  85. }
  86. private LeafBucket parseLeafTree() {
  87. final LeafBucket leaf = new LeafBucket(prefixLen);
  88. final MutableObjectId idBuf = new MutableObjectId();
  89. for (; !eof(); next(1)) {
  90. if (parseObjectId(idBuf))
  91. leaf.parseOneEntry(idBuf, getEntryObjectId());
  92. else
  93. storeNonNote();
  94. }
  95. return leaf;
  96. }
  97. private boolean parseObjectId(MutableObjectId id) {
  98. if (pathLen == pathPadding + OBJECT_ID_STRING_LENGTH) {
  99. try {
  100. id.fromString(path, pathPadding);
  101. return true;
  102. } catch (ArrayIndexOutOfBoundsException notHex) {
  103. return false;
  104. }
  105. }
  106. return false;
  107. }
  108. private FanoutBucket parseFanoutTree() {
  109. final FanoutBucket fanout = new FanoutBucket(prefixLen);
  110. for (; !eof(); next(1)) {
  111. final int cell = parseFanoutCell();
  112. if (0 <= cell)
  113. fanout.setBucket(cell, getEntryObjectId());
  114. else
  115. storeNonNote();
  116. }
  117. return fanout;
  118. }
  119. private int parseFanoutCell() {
  120. if (getNameLength() == 2 && isTree()) {
  121. try {
  122. return (parseHexInt4(path[pathOffset + 0]) << 4)
  123. | parseHexInt4(path[pathOffset + 1]);
  124. } catch (ArrayIndexOutOfBoundsException notHex) {
  125. return -1;
  126. }
  127. }
  128. return -1;
  129. }
  130. private void storeNonNote() {
  131. ObjectId id = getEntryObjectId();
  132. FileMode fileMode = getEntryFileMode();
  133. byte[] name = new byte[getNameLength()];
  134. getName(name, 0);
  135. NonNoteEntry ent = new NonNoteEntry(name, fileMode, id);
  136. if (firstNonNote == null)
  137. firstNonNote = ent;
  138. if (lastNonNote != null)
  139. lastNonNote.next = ent;
  140. lastNonNote = ent;
  141. }
  142. private boolean isTree() {
  143. return TREE.equals(mode);
  144. }
  145. private boolean isHex() {
  146. try {
  147. for (int i = pathOffset; i < pathLen; i++)
  148. parseHexInt4(path[i]);
  149. return true;
  150. } catch (ArrayIndexOutOfBoundsException fail) {
  151. return false;
  152. }
  153. }
  154. }