##// END OF EJS Templates
mpatch: split mpatch into two files
mpatch: split mpatch into two files

File last commit:

r29693:b9b9f9a9 default
r29693:b9b9f9a9 default
Show More
mpatch.c
310 lines | 6.5 KiB | text/x-c | CLexer
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 /*
mpatch.c - efficient binary patching for Mercurial
This implements a patch algorithm that's O(m + nlog n) where m is the
size of the output and n is the number of patches.
Given a list of binary patches, it unpacks each into a hunk list,
then combines the hunk lists with a treewise recursion to form a
single hunk list. This hunk list is then applied to the original
text.
The text (or binary) fragments are copied directly from their source
Python objects into a preallocated output string to avoid the
allocation of intermediate Python objects. Working memory is about 2x
the total number of hunks.
Vadim Gelfer
update copyrights.
r2859 Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.
*/
Adrian Buehlmann
mpatch: use Py_ssize_t for string length
r16758 #define PY_SSIZE_T_CLEAN
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 #include <Python.h>
#include <stdlib.h>
#include <string.h>
Vadim Gelfer
mac os x: fixes for 10.2 from chris monson <monpublic@gmail.com>
r2468
Renato Cunha
mpatch.c: Added preliminary support for py3k....
r11360 #include "util.h"
Maciej Fijalkowski
internals: move the bitmanipulation routines into its own file...
r29444 #include "bitmanipulation.h"
Maciej Fijalkowski
mpatch: change Py_ssize_t to ssize_t in places that will be later copied
r29691 #include "compat.h"
Renato Cunha
mpatch.c: Added preliminary support for py3k....
r11360
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 static char mpatch_doc[] = "Efficient binary patching.";
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 static PyObject *mpatch_Error;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_frag {
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 int start, end, len;
Matt Mackall
mpatch: allow buffer objects for input
r5444 const char *data;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 };
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_flist {
struct mpatch_frag *base, *head, *tail;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 };
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 struct mpatch_flist *lalloc(ssize_t size)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_flist *a = NULL;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
Matt Mackall
mpatch: Fix for malloc corner case on AIX
r3138 if (size < 1)
size = 1;
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 a = (struct mpatch_flist *)malloc(sizeof(struct mpatch_flist));
mpm@selenic.com
Add safety checking to mpatch
r128 if (a) {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 a->base = (struct mpatch_frag *)malloc(sizeof(struct mpatch_frag) * size);
Thomas Arendsen Hein
Set correct exception for another possible malloc error in mpatch.c
r2048 if (a->base) {
mpm@selenic.com
Add safety checking to mpatch
r128 a->head = a->tail = a->base;
Thomas Arendsen Hein
Set correct exception for another possible malloc error in mpatch.c
r2048 return a;
}
free(a);
a = NULL;
mpm@selenic.com
Add safety checking to mpatch
r128 }
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 if (!PyErr_Occurred())
PyErr_NoMemory();
return NULL;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 void mpatch_lfree(struct mpatch_flist *a)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
mpm@selenic.com
Add safety checking to mpatch
r128 if (a) {
free(a->base);
free(a);
}
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 static ssize_t lsize(struct mpatch_flist *a)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
return a->tail - a->head;
}
/* move hunks in source that are less cut to dest, compensating
for changes in offset. the last hunk may be split if necessary.
*/
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 static int gather(struct mpatch_flist *dest, struct mpatch_flist *src, int cut,
int offset)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_frag *d = dest->tail, *s = src->head;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 int postend, c, l;
while (s != src->tail) {
if (s->start + offset >= cut)
mpm@selenic.com
Gotos are embarrassing.
r82 break; /* we've gone far enough */
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
postend = offset + s->start + s->len;
if (postend <= cut) {
/* save this hunk */
offset += s->start + s->len - s->end;
*d++ = *s++;
}
else {
/* break up this hunk */
c = cut - offset;
if (s->end < c)
c = s->end;
l = cut - offset - s->start;
if (s->len < l)
l = s->len;
offset += s->start + l - c;
d->start = s->start;
d->end = c;
d->len = l;
d->data = s->data;
d++;
s->start = c;
s->len = s->len - l;
s->data = s->data + l;
mpm@selenic.com
Gotos are embarrassing.
r82 break;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
}
dest->tail = d;
src->head = s;
return offset;
}
/* like gather, but with no output list */
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 static int discard(struct mpatch_flist *src, int cut, int offset)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_frag *s = src->head;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 int postend, c, l;
while (s != src->tail) {
if (s->start + offset >= cut)
mpm@selenic.com
Gotos are embarrassing.
r82 break;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
postend = offset + s->start + s->len;
if (postend <= cut) {
offset += s->start + s->len - s->end;
s++;
}
else {
c = cut - offset;
if (s->end < c)
c = s->end;
l = cut - offset - s->start;
if (s->len < l)
l = s->len;
offset += s->start + l - c;
s->start = c;
s->len = s->len - l;
s->data = s->data + l;
mpm@selenic.com
Gotos are embarrassing.
r82 break;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
}
src->head = s;
return offset;
}
/* combine hunk lists a and b, while adjusting b for offset changes in a/
this deletes a and b and returns the resultant list. */
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 static struct mpatch_flist *combine(struct mpatch_flist *a,
struct mpatch_flist *b)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_flist *c = NULL;
struct mpatch_frag *bh, *ct;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 int offset = 0, post;
mpm@selenic.com
Add safety checking to mpatch
r128 if (a && b)
c = lalloc((lsize(a) + lsize(b)) * 2);
if (c) {
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
mpm@selenic.com
Add safety checking to mpatch
r128 for (bh = b->head; bh != b->tail; bh++) {
/* save old hunks */
offset = gather(c, a, bh->start, offset);
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
mpm@selenic.com
Add safety checking to mpatch
r128 /* discard replaced hunks */
post = discard(a, bh->end, offset);
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
mpm@selenic.com
Add safety checking to mpatch
r128 /* insert new hunk */
ct = c->tail;
ct->start = bh->start - offset;
ct->end = bh->end - post;
ct->len = bh->len;
ct->data = bh->data;
c->tail++;
offset = post;
}
/* hold on to tail from a */
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 memcpy(c->tail, a->head, sizeof(struct mpatch_frag) * lsize(a));
mpm@selenic.com
Add safety checking to mpatch
r128 c->tail += lsize(a);
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 mpatch_lfree(a);
mpatch_lfree(b);
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 return c;
}
/* decode a binary patch into a hunk list */
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 struct mpatch_flist *mpatch_decode(const char *bin, ssize_t len)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_flist *l;
struct mpatch_frag *lt;
Matt Mackall
mpatch: rewrite pointer overflow checks
r20167 int pos = 0;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
/* assume worst case size, we won't have many of these lists */
Matt Mackall
parsers: fix list sizing rounding error (SEC)...
r28656 l = lalloc(len / 12 + 1);
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 if (!l)
return NULL;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 lt = l->tail;
Matt Mackall
mpatch: rewrite pointer overflow checks
r20167 while (pos >= 0 && pos < len) {
lt->start = getbe32(bin + pos);
lt->end = getbe32(bin + pos + 4);
lt->len = getbe32(bin + pos + 8);
lt->data = bin + pos + 12;
pos += 12 + lt->len;
Matt Mackall
parsers: detect short records (SEC)...
r28657 if (lt->start > lt->end || lt->len < 0)
break; /* sanity check */
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 lt++;
}
Matt Mackall
mpatch: rewrite pointer overflow checks
r20167 if (pos != len) {
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 if (!PyErr_Occurred())
PyErr_SetString(mpatch_Error, "patch cannot be decoded");
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 mpatch_lfree(l);
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 return NULL;
}
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 l->tail = lt;
return l;
}
/* calculate the size of resultant text */
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 ssize_t mpatch_calcsize(ssize_t len, struct mpatch_flist *l)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: change Py_ssize_t to ssize_t in places that will be later copied
r29691 ssize_t outlen = 0, last = 0;
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_frag *f = l->head;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
while (f != l->tail) {
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 if (f->start < last || f->end > len) {
if (!PyErr_Occurred())
PyErr_SetString(mpatch_Error,
"invalid patch");
return -1;
}
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 outlen += f->start - last;
last = f->end;
outlen += f->len;
f++;
}
outlen += len - last;
return outlen;
}
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 int mpatch_apply(char *buf, const char *orig, ssize_t len,
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_flist *l)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 struct mpatch_frag *f = l->head;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 int last = 0;
char *p = buf;
while (f != l->tail) {
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 if (f->start < last || f->end > len) {
if (!PyErr_Occurred())
PyErr_SetString(mpatch_Error,
"invalid patch");
return 0;
}
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 memcpy(p, orig + last, f->start - last);
p += f->start - last;
memcpy(p, f->data, f->len);
last = f->end;
p += f->len;
f++;
}
memcpy(p, orig + last, len - last);
Benoit Boissinot
catch errors and throw exception with invalid binary patch data
r1722 return 1;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
/* recursively generate a patch of all bins between start and end */
Maciej Fijalkowski
mpatch: split mpatch into two files
r29693 struct mpatch_flist *mpatch_fold(PyObject *bins, ssize_t start,
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 ssize_t end)
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 {
Maciej Fijalkowski
mpatch: change Py_ssize_t to ssize_t in places that will be later copied
r29691 ssize_t len, blen;
Matt Mackall
mpatch: allow buffer objects for input
r5444 const char *buffer;
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72
if (start + 1 == end) {
/* trivial case, output a decoded list */
PyObject *tmp = PyList_GetItem(bins, start);
mpm@selenic.com
Add safety checking to mpatch
r128 if (!tmp)
return NULL;
Matt Mackall
mpatch: allow buffer objects for input
r5444 if (PyObject_AsCharBuffer(tmp, &buffer, &blen))
return NULL;
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 return mpatch_decode(buffer, blen);
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }
/* divide and conquer, memory management is elsewhere */
len = (end - start) / 2;
Maciej Fijalkowski
mpatch: provide things that will be exported later with a prefixed name...
r29692 return combine(mpatch_fold(bins, start, start + len),
mpatch_fold(bins, start + len, end));
mpm@selenic.com
Add an O(m + nlog n) patching extension
r72 }