/// <summary> /// Check an annotated tag for errors. /// </summary> /// <param name="raw">The tag data. The array is never modified.</param> /// <exception cref="CorruptObjectException">If any error was detected.</exception> public void checkTag(byte[] raw) { int ptr = 0; if ((ptr = RawParseUtils.match(raw, ptr, @object)) < 0) { throw new CorruptObjectException("no object header"); } if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid object"); } if ((ptr = RawParseUtils.match(raw, ptr, type)) < 0) { throw new CorruptObjectException("no type header"); } ptr = RawParseUtils.nextLF(raw, ptr); if ((ptr = RawParseUtils.match(raw, ptr, tag)) < 0) { throw new CorruptObjectException("no tag header"); } ptr = RawParseUtils.nextLF(raw, ptr); if ((ptr = RawParseUtils.match(raw, ptr, tagger)) > 0) { if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid tagger"); } } }
public int parseTraditionalHeaders(int ptr, int end) { while (ptr < end) { int eol = RawParseUtils.nextLF(Buffer, ptr); if (isHunkHdr(Buffer, ptr, eol) >= 1) { // First hunk header; break out and parse them later. break; } if (RawParseUtils.match(Buffer, ptr, OLD_NAME) >= 0) { ParseOldName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, NEW_NAME) >= 0) { ParseNewName(ptr, eol); } else { // Possibly an empty patch. break; } ptr = eol; } return(ptr); }
public void testMatch_Prefix() { byte[] src = Constants.encodeASCII("author "); byte[] dst = Constants.encodeASCII("author A. U. Thor"); Assert.IsTrue(RawParseUtils.match(dst, 0, src) == src.Length); Assert.IsTrue(RawParseUtils.match(dst, 1, src) < 0); }
private static int SkipFile(byte[] buf, int ptr) { ptr = RawParseUtils.nextLF(buf, ptr); if (RawParseUtils.match(buf, ptr, FileHeader.OLD_NAME) >= 0) { ptr = RawParseUtils.nextLF(buf, ptr); } return(ptr); }
private static bool MatchAny(byte[] buf, int c, IEnumerable <byte[]> srcs) { foreach (byte[] s in srcs) { if (RawParseUtils.match(buf, c, s) >= 0) { return(true); } } return(false); }
public override int parseGitHeaders(int ptr, int end) { while (ptr < end) { int eol = RawParseUtils.nextLF(Buffer, ptr); if (isHunkHdr(Buffer, ptr, end) >= 1) { // First hunk header; break out and parse them later. break; } if (RawParseUtils.match(Buffer, ptr, OLD_NAME) >= 0) { ParseOldName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, NEW_NAME) >= 0) { ParseNewName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, Index) >= 0) { ParseIndexLine(ptr + Index.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, Mode) >= 0) { parseModeLine(ptr + Mode.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, NewFileMode) >= 0) { ParseNewFileMode(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, DeletedFileMode) >= 0) { parseDeletedFileMode(ptr + DeletedFileMode.Length, eol); } else { // Probably an empty patch (stat dirty). break; } ptr = eol; } return(ptr); }
/// <summary> /// Check a commit for errors. /// </summary> /// <param name="raw">The commit data. The array is never modified.</param> /// <exception cref="CorruptObjectException">If any error was detected.</exception> public void checkCommit(byte[] raw) { int ptr = 0; if ((ptr = RawParseUtils.match(raw, ptr, tree)) < 0) { throw new CorruptObjectException("no tree header"); } if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid tree"); } while (RawParseUtils.match(raw, ptr, parent) >= 0) { ptr += parent.Length; if ((ptr = id(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid parent"); } } if ((ptr = RawParseUtils.match(raw, ptr, author)) < 0) { throw new CorruptObjectException("no author"); } if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid author"); } if ((ptr = RawParseUtils.match(raw, ptr, committer)) < 0) { throw new CorruptObjectException("no committer"); } if ((ptr = personIdent(raw, ptr)) < 0 || raw[ptr++] != '\n') { throw new CorruptObjectException("invalid committer"); } }
public int parseHunk(int ptr, int end) { byte[] buf = file.Buffer; if (RawParseUtils.match(buf, ptr, LITERAL) >= 0) { type = Type.LITERAL_DEFLATED; length = RawParseUtils.parseBase10(buf, ptr + LITERAL.Length, null); } else if (RawParseUtils.match(buf, ptr, DELTA) >= 0) { type = Type.DELTA_DEFLATED; length = RawParseUtils.parseBase10(buf, ptr + DELTA.Length, null); } else { // Not a valid binary hunk. Signal to the caller that // we cannot parse any further and that this line should // be treated otherwise. // return(-1); } ptr = RawParseUtils.nextLF(buf, ptr); // Skip until the first blank line; that is the end of the binary // encoded information in this hunk. To save time we don't do a // validation of the binary data at this point. // while (ptr < end) { bool empty = buf[ptr] == '\n'; ptr = RawParseUtils.nextLF(buf, ptr); if (empty) { break; } } return(ptr); }
private void OnOpenPack() { PackIndex idx = LoadPackIndex(); var buf = new byte[20]; IO.ReadFully(_fd, 0, buf, 0, 12); if (RawParseUtils.match(buf, 0, Constants.PACK_SIGNATURE) != 4) { throw new IOException("Not a PACK file."); } long vers = NB.decodeUInt32(buf, 4); long packCnt = NB.decodeUInt32(buf, 8); if (vers != 2 && vers != 3) { throw new IOException("Unsupported pack version " + vers + "."); } if (packCnt != idx.ObjectCount) { throw new PackMismatchException("Pack object count mismatch:" + " pack " + packCnt + " index " + idx.ObjectCount + ": " + File.FullName); } IO.ReadFully(_fd, Length - 20, buf, 0, 20); if (!buf.SequenceEqual(_packChecksum)) { throw new PackMismatchException("Pack checksum mismatch:" + " pack " + ObjectId.FromRaw(buf) + " index " + ObjectId.FromRaw(idx.PackChecksum) + ": " + File.FullName); } }
private int ParseHunks(FileHeader fh, int c, int end) { byte[] buf = fh.Buffer; while (c < end) { // If we see a file header at this point, we have all of the // hunks for our current file. We should stop and report back // with this position so it can be parsed again later. // if (RawParseUtils.match(buf, c, DiffGit) >= 0) { break; } if (RawParseUtils.match(buf, c, DiffCc) >= 0) { break; } if (RawParseUtils.match(buf, c, DiffCombined) >= 0) { break; } if (RawParseUtils.match(buf, c, FileHeader.OLD_NAME) >= 0) { break; } if (RawParseUtils.match(buf, c, FileHeader.NEW_NAME) >= 0) { break; } if (FileHeader.isHunkHdr(buf, c, end) == fh.ParentCount) { HunkHeader h = fh.newHunkHeader(c); h.parseHeader(); c = h.parseBody(this, end); h.EndOffset = c; fh.addHunk(h); if (c < end) { switch (buf[c]) { case (byte)'@': case (byte)'d': case (byte)'\n': break; default: if (RawParseUtils.match(buf, c, SigFooter) < 0) { warn(buf, c, "Unexpected hunk trailer"); } break; } } continue; } int eol = RawParseUtils.nextLF(buf, c); if (fh.Hunks.isEmpty() && RawParseUtils.match(buf, c, GitBinary) >= 0) { fh.PatchType = FileHeader.PatchTypeEnum.GIT_BINARY; return(ParseGitBinary(fh, eol, end)); } if (fh.Hunks.isEmpty() && BinTrailer.Length < eol - c && RawParseUtils.match(buf, eol - BinTrailer.Length, BinTrailer) >= 0 && MatchAny(buf, c, BinHeaders)) { // The patch is a binary file diff, with no deltas. // fh.PatchType = FileHeader.PatchTypeEnum.BINARY; return(eol); } // Skip this line and move to the next. Its probably garbage // After the last hunk of a file. // c = eol; } if (fh.Hunks.isEmpty() && fh.getPatchType() == FileHeader.PatchTypeEnum.UNIFIED && !fh.hasMetaDataChanges()) { // Hmm, an empty patch? If there is no metadata here we // really have a binary patch that we didn't notice above. // fh.PatchType = FileHeader.PatchTypeEnum.BINARY; } return(c); }
private int ParseFile(byte[] buf, int c, int end) { while (c < end) { if (FileHeader.isHunkHdr(buf, c, end) >= 1) { // If we find a disconnected hunk header we might // have missed a file header previously. The hunk // isn't valid without knowing where it comes from. // error(buf, c, "Hunk disconnected from file"); c = RawParseUtils.nextLF(buf, c); continue; } // Valid git style patch? // if (RawParseUtils.match(buf, c, DiffGit) >= 0) { return(ParseDiffGit(buf, c, end)); } if (RawParseUtils.match(buf, c, DiffCc) >= 0) { return(ParseDiffCombined(DiffCc, buf, c, end)); } if (RawParseUtils.match(buf, c, DiffCombined) >= 0) { return(ParseDiffCombined(DiffCombined, buf, c, end)); } // Junk between files? Leading junk? Traditional // (non-git generated) patch? // int n = RawParseUtils.nextLF(buf, c); if (n >= end) { // Patches cannot be only one line long. This must be // trailing junk that we should ignore. // return(end); } if (n - c < 6) { // A valid header must be at least 6 bytes on the // first line, e.g. "--- a/b\n". // c = n; continue; } if (RawParseUtils.match(buf, c, FileHeader.OLD_NAME) >= 0 && RawParseUtils.match(buf, n, FileHeader.NEW_NAME) >= 0) { // Probably a traditional patch. Ensure we have at least // a "@@ -0,0" smelling line next. We only check the "@@ -". // int f = RawParseUtils.nextLF(buf, n); if (f >= end) { return(end); } if (FileHeader.isHunkHdr(buf, f, end) == 1) { return(ParseTraditionalPatch(buf, c, end)); } } c = n; } return(c); }
public virtual int parseGitHeaders(int ptr, int end) { while (ptr < end) { int eol = RawParseUtils.nextLF(Buffer, ptr); if (isHunkHdr(Buffer, ptr, eol) >= 1) { // First hunk header; break out and parse them later. break; } if (RawParseUtils.match(Buffer, ptr, OLD_NAME) >= 0) { ParseOldName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, NEW_NAME) >= 0) { ParseNewName(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, OldModeString) >= 0) { _oldMode = ParseFileMode(ptr + OldModeString.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, NewModeString) >= 0) { _newMode = ParseFileMode(ptr + NewModeString.Length, eol); } else if (RawParseUtils.match(Buffer, ptr, DeletedFileMode) >= 0) { _oldMode = ParseFileMode(ptr + DeletedFileMode.Length, eol); _newMode = FileMode.Missing; ChangeType = ChangeTypeEnum.DELETE; } else if (RawParseUtils.match(Buffer, ptr, NewFileMode) >= 0) { ParseNewFileMode(ptr, eol); } else if (RawParseUtils.match(Buffer, ptr, CopyFrom) >= 0) { oldName = ParseName(oldName, ptr + CopyFrom.Length, eol); ChangeType = ChangeTypeEnum.COPY; } else if (RawParseUtils.match(Buffer, ptr, CopyTo) >= 0) { newName = ParseName(newName, ptr + CopyTo.Length, eol); ChangeType = ChangeTypeEnum.COPY; } else if (RawParseUtils.match(Buffer, ptr, RenameOld) >= 0) { oldName = ParseName(oldName, ptr + RenameOld.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameNew) >= 0) { newName = ParseName(newName, ptr + RenameNew.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameFrom) >= 0) { oldName = ParseName(oldName, ptr + RenameFrom.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, RenameTo) >= 0) { newName = ParseName(newName, ptr + RenameTo.Length, eol); ChangeType = ChangeTypeEnum.RENAME; } else if (RawParseUtils.match(Buffer, ptr, SimilarityIndex) >= 0) { _score = RawParseUtils.parseBase10(Buffer, ptr + SimilarityIndex.Length, null); } else if (RawParseUtils.match(Buffer, ptr, DissimilarityIndex) >= 0) { _score = RawParseUtils.parseBase10(Buffer, ptr + DissimilarityIndex.Length, null); } else if (RawParseUtils.match(Buffer, ptr, Index) >= 0) { ParseIndexLine(ptr + Index.Length, eol); } else { // Probably an empty patch (stat dirty). break; } ptr = eol; } return(ptr); }
public void testMatch_TooSmall() { byte[] src = Constants.encodeASCII("author "); byte[] dst = Constants.encodeASCII("author autho"); Assert.IsTrue(RawParseUtils.match(dst, src.Length + 1, src) < 0); }
public void testMatch_NotEqual() { byte[] src = Constants.encodeASCII(" differ\n"); byte[] dst = Constants.encodeASCII("a differ\n"); Assert.IsTrue(RawParseUtils.match(dst, 2, src) < 0); }
public void testMatch_Equal() { byte[] src = Constants.encodeASCII(" differ\n"); byte[] dst = Constants.encodeASCII("foo differ\n"); Assert.IsTrue(RawParseUtils.match(dst, 3, src) == 3 + src.Length); }
public virtual int parseBody(Patch script, int end) { byte[] buf = _file.Buffer; int c = RawParseUtils.nextLF(buf, _startOffset), last = c; _oldImage.LinesDeleted = 0; _oldImage.LinesAdded = 0; for (; c < end; last = c, c = RawParseUtils.nextLF(buf, c)) { bool breakScan; switch (buf[c]) { case (byte)' ': case (byte)'\n': LinesContext++; continue; case (byte)'-': _oldImage.LinesDeleted++; continue; case (byte)'+': _oldImage.LinesAdded++; continue; case (byte)'\\': // Matches "\ No newline at end of file" continue; default: breakScan = true; break; } if (breakScan) { break; } } if (last < end && LinesContext + _oldImage.LinesDeleted - 1 == _oldImage.LineCount && LinesContext + _oldImage.LinesAdded == NewLineCount && RawParseUtils.match(buf, last, Patch.SigFooter) >= 0) { // This is an extremely common occurrence of "corruption". // Users add footers with their signatures After this mark, // and git diff adds the git executable version number. // Let it slide; the hunk otherwise looked sound. // _oldImage.LinesDeleted--; return(last); } if (LinesContext + _oldImage.LinesDeleted < _oldImage.LineCount) { int missingCount = _oldImage.LineCount - (LinesContext + _oldImage.LinesDeleted); script.error(buf, _startOffset, "Truncated hunk, at least " + missingCount + " old lines is missing"); } else if (LinesContext + _oldImage.LinesAdded < NewLineCount) { int missingCount = NewLineCount - (LinesContext + _oldImage.LinesAdded); script.error(buf, _startOffset, "Truncated hunk, at least " + missingCount + " new lines is missing"); } else if (LinesContext + _oldImage.LinesDeleted > _oldImage.LineCount || LinesContext + _oldImage.LinesAdded > NewLineCount) { string oldcnt = _oldImage.LineCount + ":" + NewLineCount; string newcnt = (LinesContext + _oldImage.LinesDeleted) + ":" + (LinesContext + _oldImage.LinesAdded); script.warn(buf, _startOffset, "Hunk header " + oldcnt + " does not match body line count of " + newcnt); } return(c); }