Mercurial > hg4j
view src/org/tmatesoft/hg/internal/Patch.java @ 329:694ebabb5cb3
Refactor revlog patch mechanism, towards patch merging
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Thu, 13 Oct 2011 03:30:50 +0200 |
parents | |
children | 9747a786a34d |
line wrap: on
line source
/* * Copyright (c) 2011 TMate Software Ltd * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * For information on how to redistribute this software under * the terms of a license other than GNU General Public License * contact TMate Software at support@hg4j.com */ package org.tmatesoft.hg.internal; import java.io.IOException; import java.util.ArrayList; /** * @see http://mercurial.selenic.com/wiki/BundleFormat, in Changelog group description * * range [start..end] in original source gets replaced with data of length (do not keep, use data.length instead) * range [end(i)..start(i+1)] is copied from the source * * @author Artem Tikhomirov * @author TMate Software Ltd. */ public final class Patch { private final IntVector starts, ends; private final ArrayList<byte[]> data; public Patch() { starts = new IntVector(); ends = new IntVector(); data = new ArrayList<byte[]>(); } public int count() { return data.size(); } // number of bytes this patch will add (or remove, if negative) from the base revision private int patchSizeDelta() { int rv = 0; int prevEnd = 0; for (int i = 0, x = data.size(); i < x; i++) { final int start = starts.get(i); final int len = data.get(i).length; rv += start - prevEnd; // would copy from original rv += len; // and add new prevEnd = ends.get(i); } rv -= prevEnd; return rv; } public byte[] apply(DataAccess baseRevisionContent, int outcomeLen) throws IOException { if (outcomeLen == -1) { outcomeLen = baseRevisionContent.length() + patchSizeDelta(); } int prevEnd = 0, destIndex = 0; byte[] rv = new byte[outcomeLen]; for (int i = 0, x = data.size(); i < x; i++) { final int start = starts.get(i); baseRevisionContent.seek(prevEnd); // copy source bytes that were not modified (up to start of the record) baseRevisionContent.readBytes(rv, destIndex, start - prevEnd); destIndex += start - prevEnd; // insert new data from the patch, if any byte[] d = data.get(i); System.arraycopy(d, 0, rv, destIndex, d.length); destIndex += d.length; prevEnd = ends.get(i); } baseRevisionContent.seek(prevEnd); // copy everything in the source past last record's end baseRevisionContent.readBytes(rv, destIndex, (int) (baseRevisionContent.length() - prevEnd)); return rv; } public void clear() { starts.clear(); ends.clear(); data.clear(); } /** * Initialize instance from stream. Any previous patch information (i.e. if instance if reused) is cleared first. * Read up to the end of DataAccess and interpret data as patch records. */ public void read(DataAccess da) throws IOException { clear(); while (!da.isEmpty()) { readOne(da); } } /** * Caller is responsible to ensure stream got some data to read */ public void readOne(DataAccess da) throws IOException { int s = da.readInt(); int e = da.readInt(); int len = da.readInt(); byte[] src = new byte[len]; da.readBytes(src, 0, len); starts.add(s); ends.add(e); data.add(src); } /* private void add(Patch another, int index) { starts.add(another.starts.get(index)); ends.add(another.ends.get(index)); data.add(another.data.get(index)); } /** * Modify this patch with subsequent patch * / public void apply(Patch another) { Patch r = new Patch(); int p1AppliedPos = 0; int p1PrevEnd = 0; for (int i = 0, j = 0, iMax = another.count(), jMax = this.count(); i < iMax; i++) { int newerPatchEntryStart = another.starts.get(i); int olderPatchEntryEnd; while (j < jMax) { if (starts.get(j) < newerPatchEntryStart) { if (starts.get(j)+data.get(j).length <= newerPatchEntryStart) { r.add(this, j); } else { int newLen = newerPatchEntryStart - starts.get(j); int newEnd = ends.get(j) <= newerPatchEntryStart ? ends.get(j) : newerPatchEntryStart; r.add(starts.get(j), newEnd, data.get(j), newLen); break; } } p1AppliedPos += starts.get(j) - p1PrevEnd; p1AppliedPos += data.get(j).length; p1PrevEnd = ends.get(j); j++; } r.add(newerPatchEntryStart, another.ends.get(i), another.data.get(i)); p1AppliedPos += newerPatchEntryStart + p1PrevEnd - another.data.get(i).length; // either j == jMax and another(i, i+1, ..., iMax) need to be just copied // or new patch entry starts before end of one of original patch entries if (olderPatchEntryEnd > (destPosition + newerPatchEntryStart)) { destPosition += starts.get(j) - prevEnd; // count those in the original stream up to old patch start int newLen = newerPatchEntryStart - destPosition; } } } */ }