Mercurial > hg4j
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/org/tmatesoft/hg/internal/Patch.java Thu Oct 13 03:30:50 2011 +0200 @@ -0,0 +1,160 @@ +/* + * 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; + } + } + } +*/ +} \ No newline at end of file