LCOV - code coverage report
Current view: top level - server/patch - PatchListEntry.java (source / functions) Hit Total Coverage
Test: _coverage_report.dat Lines: 24 156 15.4 %
Date: 2022-11-19 15:00:39 Functions: 10 29 34.5 %

          Line data    Source code
       1             : // Copyright (C) 2009 The Android Open Source Project
       2             : //
       3             : // Licensed under the Apache License, Version 2.0 (the "License");
       4             : // you may not use this file except in compliance with the License.
       5             : // You may obtain a copy of the License at
       6             : //
       7             : // http://www.apache.org/licenses/LICENSE-2.0
       8             : //
       9             : // Unless required by applicable law or agreed to in writing, software
      10             : // distributed under the License is distributed on an "AS IS" BASIS,
      11             : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12             : // See the License for the specific language governing permissions and
      13             : // limitations under the License.
      14             : 
      15             : package com.google.gerrit.server.patch;
      16             : 
      17             : import static com.google.common.collect.ImmutableList.toImmutableList;
      18             : import static com.google.common.collect.ImmutableSet.toImmutableSet;
      19             : import static com.google.gerrit.server.ioutil.BasicSerialization.readBytes;
      20             : import static com.google.gerrit.server.ioutil.BasicSerialization.readEnum;
      21             : import static com.google.gerrit.server.ioutil.BasicSerialization.readFixInt64;
      22             : import static com.google.gerrit.server.ioutil.BasicSerialization.readString;
      23             : import static com.google.gerrit.server.ioutil.BasicSerialization.readVarInt32;
      24             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeBytes;
      25             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeEnum;
      26             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeFixInt64;
      27             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeString;
      28             : import static com.google.gerrit.server.ioutil.BasicSerialization.writeVarInt32;
      29             : import static java.nio.charset.StandardCharsets.UTF_8;
      30             : 
      31             : import com.google.common.collect.ImmutableList;
      32             : import com.google.common.collect.ImmutableSet;
      33             : import com.google.gerrit.entities.Patch;
      34             : import com.google.gerrit.entities.Patch.ChangeType;
      35             : import com.google.gerrit.entities.Patch.PatchType;
      36             : import java.io.IOException;
      37             : import java.io.InputStream;
      38             : import java.io.OutputStream;
      39             : import java.util.Collection;
      40             : import java.util.List;
      41             : import java.util.Set;
      42             : import org.eclipse.jgit.diff.Edit;
      43             : import org.eclipse.jgit.patch.CombinedFileHeader;
      44             : import org.eclipse.jgit.patch.FileHeader;
      45             : import org.eclipse.jgit.util.IntList;
      46             : import org.eclipse.jgit.util.RawParseUtils;
      47             : 
      48             : public class PatchListEntry {
      49           1 :   private static final byte[] EMPTY_HEADER = {};
      50             : 
      51             :   static PatchListEntry empty(String fileName) {
      52           1 :     return empty(fileName, ChangeType.MODIFIED);
      53             :   }
      54             : 
      55             :   static PatchListEntry empty(String fileName, ChangeType changeType) {
      56           1 :     return new PatchListEntry(
      57             :         changeType,
      58             :         PatchType.UNIFIED,
      59             :         null,
      60             :         fileName,
      61             :         EMPTY_HEADER,
      62           1 :         ImmutableList.of(),
      63           1 :         ImmutableSet.of(),
      64             :         0,
      65             :         0,
      66             :         0,
      67             :         0);
      68             :   }
      69             : 
      70             :   private final ChangeType changeType;
      71             :   private final PatchType patchType;
      72             :   private final String oldName;
      73             :   private final String newName;
      74             :   private final byte[] header;
      75             :   private final ImmutableList<Edit> edits;
      76             :   private final ImmutableSet<Edit> editsDueToRebase;
      77             :   private final int insertions;
      78             :   private final int deletions;
      79             :   private final long size;
      80             :   private final long sizeDelta;
      81             :   // Note: When adding new fields, the serialVersionUID in PatchListKey must be
      82             :   // incremented so that entries from the cache are automatically invalidated.
      83             : 
      84             :   public PatchListEntry(
      85           0 :       FileHeader hdr, List<Edit> editList, Set<Edit> editsDueToRebase, long size, long sizeDelta) {
      86           0 :     changeType = toChangeType(hdr);
      87           0 :     patchType = toPatchType(hdr);
      88             : 
      89           0 :     switch (changeType) {
      90             :       case DELETED:
      91           0 :         oldName = null;
      92           0 :         newName = hdr.getOldPath();
      93           0 :         break;
      94             : 
      95             :       case ADDED:
      96             :       case MODIFIED:
      97             :       case REWRITE:
      98           0 :         oldName = null;
      99           0 :         newName = hdr.getNewPath();
     100           0 :         break;
     101             : 
     102             :       case COPIED:
     103             :       case RENAMED:
     104           0 :         oldName = hdr.getOldPath();
     105           0 :         newName = hdr.getNewPath();
     106           0 :         break;
     107             : 
     108             :       default:
     109           0 :         throw new IllegalArgumentException("Unsupported type " + changeType);
     110             :     }
     111             : 
     112           0 :     header = compact(hdr);
     113             : 
     114           0 :     if (hdr instanceof CombinedFileHeader || hdr.getHunks().isEmpty()) {
     115           0 :       edits = ImmutableList.of();
     116             :     } else {
     117           0 :       edits = ImmutableList.copyOf(editList);
     118             :     }
     119           0 :     this.editsDueToRebase = ImmutableSet.copyOf(editsDueToRebase);
     120             : 
     121           0 :     int ins = 0;
     122           0 :     int del = 0;
     123           0 :     for (Edit e : editList) {
     124           0 :       if (!editsDueToRebase.contains(e)) {
     125           0 :         del += e.getEndA() - e.getBeginA();
     126           0 :         ins += e.getEndB() - e.getBeginB();
     127             :       }
     128           0 :     }
     129           0 :     insertions = ins;
     130           0 :     deletions = del;
     131           0 :     this.size = size;
     132           0 :     this.sizeDelta = sizeDelta;
     133           0 :   }
     134             : 
     135             :   private PatchListEntry(
     136             :       ChangeType changeType,
     137             :       PatchType patchType,
     138             :       String oldName,
     139             :       String newName,
     140             :       byte[] header,
     141             :       ImmutableList<Edit> edits,
     142             :       ImmutableSet<Edit> editsDueToRebase,
     143             :       int insertions,
     144             :       int deletions,
     145             :       long size,
     146           1 :       long sizeDelta) {
     147           1 :     this.changeType = changeType;
     148           1 :     this.patchType = patchType;
     149           1 :     this.oldName = oldName;
     150           1 :     this.newName = newName;
     151           1 :     this.header = header;
     152           1 :     this.edits = edits;
     153           1 :     this.editsDueToRebase = editsDueToRebase;
     154           1 :     this.insertions = insertions;
     155           1 :     this.deletions = deletions;
     156           1 :     this.size = size;
     157           1 :     this.sizeDelta = sizeDelta;
     158           1 :   }
     159             : 
     160             :   int weigh() {
     161           0 :     int size = 16 + 6 * 8 + 2 * 4 + 20 + 16 + 8 + 4 + 20;
     162           0 :     size += stringSize(oldName);
     163           0 :     size += stringSize(newName);
     164           0 :     size += header.length;
     165           0 :     size += (8 + 16 + 4 * 4) * edits.size();
     166           0 :     size += (8 + 16 + 4 * 4) * editsDueToRebase.size();
     167           0 :     return size;
     168             :   }
     169             : 
     170             :   private static int stringSize(String str) {
     171           0 :     if (str != null) {
     172           0 :       return 16 + 3 * 4 + 16 + str.length() * 2;
     173             :     }
     174           0 :     return 0;
     175             :   }
     176             : 
     177             :   public ChangeType getChangeType() {
     178           1 :     return changeType;
     179             :   }
     180             : 
     181             :   public PatchType getPatchType() {
     182           1 :     return patchType;
     183             :   }
     184             : 
     185             :   public String getOldName() {
     186           1 :     return oldName;
     187             :   }
     188             : 
     189             :   public String getNewName() {
     190           1 :     return newName;
     191             :   }
     192             : 
     193             :   public ImmutableList<Edit> getEdits() {
     194             :     // Edits are mutable objects. As we serialize PatchListEntry asynchronously in H2CacheImpl, we
     195             :     // must ensure that its state isn't modified until it was properly stored in the cache.
     196           1 :     return deepCopyEdits(edits);
     197             :   }
     198             : 
     199             :   public ImmutableSet<Edit> getEditsDueToRebase() {
     200           0 :     return deepCopyEdits(editsDueToRebase);
     201             :   }
     202             : 
     203             :   public int getInsertions() {
     204           0 :     return insertions;
     205             :   }
     206             : 
     207             :   public int getDeletions() {
     208           0 :     return deletions;
     209             :   }
     210             : 
     211             :   public long getSize() {
     212           0 :     return size;
     213             :   }
     214             : 
     215             :   public long getSizeDelta() {
     216           0 :     return sizeDelta;
     217             :   }
     218             : 
     219             :   public ImmutableList<String> getHeaderLines() {
     220           0 :     final IntList m = RawParseUtils.lineMap(header, 0, header.length);
     221           0 :     final ImmutableList.Builder<String> headerLines =
     222           0 :         ImmutableList.builderWithExpectedSize(m.size() - 1);
     223           0 :     for (int i = 1; i < m.size() - 1; i++) {
     224           0 :       final int b = m.get(i);
     225           0 :       int e = m.get(i + 1);
     226           0 :       if (header[e - 1] == '\n') {
     227           0 :         e--;
     228             :       }
     229           0 :       headerLines.add(RawParseUtils.decode(UTF_8, header, b, e));
     230             :     }
     231           0 :     return headerLines.build();
     232             :   }
     233             : 
     234             :   private static ImmutableList<Edit> deepCopyEdits(List<Edit> edits) {
     235           1 :     return edits.stream().map(PatchListEntry::copy).collect(toImmutableList());
     236             :   }
     237             : 
     238             :   private static ImmutableSet<Edit> deepCopyEdits(Set<Edit> edits) {
     239           0 :     return edits.stream().map(PatchListEntry::copy).collect(toImmutableSet());
     240             :   }
     241             : 
     242             :   private static Edit copy(Edit edit) {
     243           0 :     return new Edit(edit.getBeginA(), edit.getEndA(), edit.getBeginB(), edit.getEndB());
     244             :   }
     245             : 
     246             :   void writeTo(OutputStream out) throws IOException {
     247           0 :     writeEnum(out, changeType);
     248           0 :     writeEnum(out, patchType);
     249           0 :     writeString(out, oldName);
     250           0 :     writeString(out, newName);
     251           0 :     writeBytes(out, header);
     252           0 :     writeVarInt32(out, insertions);
     253           0 :     writeVarInt32(out, deletions);
     254           0 :     writeFixInt64(out, size);
     255           0 :     writeFixInt64(out, sizeDelta);
     256             : 
     257           0 :     writeEditArray(out, edits);
     258           0 :     writeEditArray(out, editsDueToRebase);
     259           0 :   }
     260             : 
     261             :   private static void writeEditArray(OutputStream out, Collection<Edit> edits) throws IOException {
     262           0 :     writeVarInt32(out, edits.size());
     263           0 :     for (Edit edit : edits) {
     264           0 :       writeVarInt32(out, edit.getBeginA());
     265           0 :       writeVarInt32(out, edit.getEndA());
     266           0 :       writeVarInt32(out, edit.getBeginB());
     267           0 :       writeVarInt32(out, edit.getEndB());
     268           0 :     }
     269           0 :   }
     270             : 
     271             :   static PatchListEntry readFrom(InputStream in) throws IOException {
     272           0 :     ChangeType changeType = readEnum(in, ChangeType.values());
     273           0 :     PatchType patchType = readEnum(in, PatchType.values());
     274           0 :     String oldName = readString(in);
     275           0 :     String newName = readString(in);
     276           0 :     byte[] hdr = readBytes(in);
     277           0 :     int ins = readVarInt32(in);
     278           0 :     int del = readVarInt32(in);
     279           0 :     long size = readFixInt64(in);
     280           0 :     long sizeDelta = readFixInt64(in);
     281             : 
     282           0 :     Edit[] editArray = readEditArray(in);
     283           0 :     Edit[] editsDueToRebase = readEditArray(in);
     284             : 
     285           0 :     return new PatchListEntry(
     286             :         changeType,
     287             :         patchType,
     288             :         oldName,
     289             :         newName,
     290             :         hdr,
     291           0 :         ImmutableList.copyOf(editArray),
     292           0 :         ImmutableSet.copyOf(editsDueToRebase),
     293             :         ins,
     294             :         del,
     295             :         size,
     296             :         sizeDelta);
     297             :   }
     298             : 
     299             :   private static Edit[] readEditArray(InputStream in) throws IOException {
     300           0 :     int numEdits = readVarInt32(in);
     301           0 :     Edit[] edits = new Edit[numEdits];
     302           0 :     for (int i = 0; i < numEdits; i++) {
     303           0 :       int beginA = readVarInt32(in);
     304           0 :       int endA = readVarInt32(in);
     305           0 :       int beginB = readVarInt32(in);
     306           0 :       int endB = readVarInt32(in);
     307           0 :       edits[i] = new Edit(beginA, endA, beginB, endB);
     308             :     }
     309           0 :     return edits;
     310             :   }
     311             : 
     312             :   private static byte[] compact(FileHeader h) {
     313           0 :     final int end = end(h);
     314           0 :     if (h.getStartOffset() == 0 && end == h.getBuffer().length) {
     315           0 :       return h.getBuffer();
     316             :     }
     317             : 
     318           0 :     final byte[] buf = new byte[end - h.getStartOffset()];
     319           0 :     System.arraycopy(h.getBuffer(), h.getStartOffset(), buf, 0, buf.length);
     320           0 :     return buf;
     321             :   }
     322             : 
     323             :   private static int end(FileHeader h) {
     324           0 :     if (h instanceof CombinedFileHeader) {
     325           0 :       return h.getEndOffset();
     326             :     }
     327           0 :     if (!h.getHunks().isEmpty()) {
     328           0 :       return h.getHunks().get(0).getStartOffset();
     329             :     }
     330           0 :     return h.getEndOffset();
     331             :   }
     332             : 
     333             :   private static ChangeType toChangeType(FileHeader hdr) {
     334           0 :     switch (hdr.getChangeType()) {
     335             :       case ADD:
     336           0 :         return Patch.ChangeType.ADDED;
     337             :       case MODIFY:
     338           0 :         return Patch.ChangeType.MODIFIED;
     339             :       case DELETE:
     340           0 :         return Patch.ChangeType.DELETED;
     341             :       case RENAME:
     342           0 :         return Patch.ChangeType.RENAMED;
     343             :       case COPY:
     344           0 :         return Patch.ChangeType.COPIED;
     345             :       default:
     346           0 :         throw new IllegalArgumentException("Unsupported type " + hdr.getChangeType());
     347             :     }
     348             :   }
     349             : 
     350             :   private static PatchType toPatchType(FileHeader hdr) {
     351             :     PatchType pt;
     352             : 
     353           0 :     switch (hdr.getPatchType()) {
     354             :       case UNIFIED:
     355           0 :         pt = Patch.PatchType.UNIFIED;
     356           0 :         break;
     357             :       case GIT_BINARY:
     358             :       case BINARY:
     359           0 :         pt = Patch.PatchType.BINARY;
     360           0 :         break;
     361             :       default:
     362           0 :         throw new IllegalArgumentException("Unsupported type " + hdr.getPatchType());
     363             :     }
     364             : 
     365           0 :     if (pt != PatchType.BINARY) {
     366           0 :       final byte[] buf = hdr.getBuffer();
     367           0 :       for (int ptr = hdr.getStartOffset(); ptr < hdr.getEndOffset(); ptr++) {
     368           0 :         if (buf[ptr] == '\0') {
     369             :           // Its really binary, but Git couldn't see the nul early enough
     370             :           // to realize its binary, and instead produced the diff.
     371             :           //
     372             :           // Force it to be a binary; it really should have been that.
     373             :           //
     374           0 :           pt = PatchType.BINARY;
     375           0 :           break;
     376             :         }
     377             :       }
     378             :     }
     379             : 
     380           0 :     return pt;
     381             :   }
     382             : }

Generated by: LCOV version 1.16+git.20220603.dfeb750