20 Commits
Author SHA1 Message Date
Patrick McCarty e0e8bcda58 Release v1.0.4
This release fixes an issue with libbsdiff thread safety. This was a
regression introduced in v1.0.3.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-06-14 14:50:54 -07:00
Patrick McCarty 2e5b80929c Make revised search algorithm thread safe
In commit 85b1c5345b, I introduced a new "max_len" variable, used by the
search() function to store the maximum match length found in course of
the binary search.

However, I overlooked the fact that the search() function then lost
thread safety, as "max_len" would be shared by all threads of a process
using libbsdiff.

Fix the issue by not using a global variable, instead updating "max_len"
via another pointer, just like the "pos" variable is handled already.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-06-14 14:46:52 -07:00
Patrick McCarty 6127bd6095 Release v1.0.3
This release fixes a bug that results in significantly improved diff
creation times in certain cases.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-02-22 18:04:24 -08:00
Patrick McCarty 85b1c5345b Find the longest match in the suffix-sorted array
Fixes #2

The current implementation performs a binary search through the
suffix-sorted array to find byte sequence matches between old and new
files. However, the algorithm is not optimal when it repeatedly matches
very short strings, leading to performance issues as reported in issue
 #2.

This commit changes the algorithm to consider *all* matches encountered
during the binary search and choose the longest of these matches.

The overall performance impact is yet to be determined, but it appears
to yield a small percentage increase in diff creation time (expected)
and a large percentage *decrease* for the case of diffing the files from
issue #2. In my testing, the creation time there decreases from approx
64 minutes to 4.5 seconds.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2018-02-22 18:01:17 -08:00
Patrick McCarty d039492824 Fix code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:33:09 -07:00
Patrick McCarty 43a817a2e5 build: add 'compliant' makefile target for fixing code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:32:33 -07:00
Patrick McCarty 4e868b4772 Split suffix sort code into a separate source file
To prepare for the possibility of testing other suffix sort algorithms
in the future, split this code into a separate source file for clarity.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2017-06-12 11:24:09 -07:00
Patrick McCarty 71b9d7e78a Allow for skipping valgrind use in tests
In case the system valgrind is not functioning properly, allow the test
suite to run without using the tool by setting SKIP_VALGRIND=1 for `make
check`.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-13 10:07:22 -08:00
Patrick McCarty b46a4e2c0f Run clang-format to fix code style
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-12-13 09:49:33 -08:00
Patrick McCarty 5342235446 Release version 1.0.2
This release enables the functional test suite by default. To disable
the ability to run the test suite and its dependency checks, pass
--disable-tests to configure for the build.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 13:25:41 -08:00
Patrick McCarty 8ed6fb38aa Fix distcheck
The remaining test files need to be distributed in order to run the test
suite, and export abs_builddir to the test script to point to the
binaries.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 13:25:41 -08:00
Patrick McCarty f23f25a43c Enable travis-ci integration
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 10:50:39 -08:00
Patrick McCarty 8654e611cf Use 'readlink -f' instead of 'realpath'
Some distros do not ship the coreutils 'realpath', so use 'readlink -f'
instead, which is equivalent for the usage here.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-11-21 10:48:23 -08:00
Patrick McCarty fb5ced7c2c configure: fix libcheck minimum version
TAP support was added in libcheck version 0.9.12.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-10-28 17:50:36 -07:00
Patrick McCarty 150cc28bbe configure: disable libcheck test if test build is disabled
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-10-28 17:48:22 -07:00
Patrick McCarty 8c0a87b7c9 Convert functional test suite to TAP
Automake provides a nice test harness that can parse TAP results, so
switch to using that.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:46:30 -08:00
Patrick McCarty 253dcbbbc0 Provide configure option to disable tests
Building the tests is enabled by default, but because the test suite
requires root privileges to run at the moment, people should have to
option to disable the tests entirely.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:44:21 -08:00
Patrick McCarty a11afb9d3b Cleanups for building with test coverage
Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 15:42:59 -08:00
Patrick McCarty 33273c0f93 Release version 1.0.1
This release fixes a small issue with permissions on the target files
created by both bsdiff and bspatch, namely that there is no reason for
them not to be group and world readable.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 12:59:53 -08:00
Patrick McCarty 7525374a10 Open target files write-only and loosen their permissions
Because the target files are only written to, not read from, by either
bsdiff or bspatch, we can open them write-only.

Also, I don't see a reason for the file permissions to be 600, so loosen
the permissions to be 644.

Signed-off-by: Patrick McCarty <patrick.mccarty@intel.com>
2016-02-26 11:21:38 -08:00
10 changed files with 459 additions and 267 deletions
+7
View File
@@ -13,6 +13,7 @@ bsdump
bspatch
compile
configure
coverage/
depcomp
install-sh
libbsdiff.la
@@ -21,7 +22,13 @@ ltmain.sh
m4/
missing
src/.dirstamp
src/*.gcda
src/*.gcno
stamp-h1
bsdiff-*.tar.xz
test/*.diff
test/*.out
test/*.log
test/*.trs
tap-driver.sh
test-suite.log
+20
View File
@@ -0,0 +1,20 @@
sudo: required
dist: trusty
language: c
# Pre-install missing build dependencies:
# - valgrind (from the repo)
# - libcheck 0.9.10 is slightly too old, since 0.9.12 adds TAP support
before_install:
- sudo apt-get -qq update
- sudo apt-get install -y valgrind
install:
- wget http://downloads.sourceforge.net/project/check/check/0.10.0/check-0.10.0.tar.gz
- tar -xvf check-0.10.0.tar.gz
- pushd check-0.10.0 && ./configure --prefix=/usr && make -j48 && sudo make install && popd
# Ubuntu's default umask is 0002, but tests are written with the expectation of a 0022 default.
script:
- autoreconf --verbose --warnings=none --install --force && ./configure && make -j48 && sudo sh -c 'umask 0022 && make -j48 check'
after_failure: cat test-suite.log
+72 -4
View File
@@ -28,11 +28,15 @@ EXTRA_DIST = \
AUTOMAKE_OPTIONS = color-tests parallel-tests
if COVERAGE
coverage:
AM_CFLAGS += --coverage
coverage: coverage-clean
mkdir -p coverage
lcov --compat-libtool --directory . --capture --output-file coverage/report
genhtml -o coverage/ coverage/report
AM_CFLAGS += --coverage
coverage-clean:
rm -rf coverage
endif
bin_PROGRAMS = \
@@ -63,7 +67,8 @@ lib_LTLIBRARIES = \
libbsdiff_la_SOURCES = \
src/diff.c \
src/patch.c
src/patch.c \
src/sufsort.c
libbsdiff_la_LIBADD = \
$(zlib_LIBS)
@@ -98,7 +103,70 @@ distclean-local:
install-exec-hook:
perl findstatic.pl */*.o | grep -v Checking ||:
check_PROGRAMS =
TEST_EXTENSIONS = .sh
EXTRA_DIST += \
test/data/5.bspatch.diff \
test/data/5.bspatch.original \
test/data/6.bspatch.diff \
test/data/6.bspatch.original \
test/data/7.bspatch.diff \
test/data/7.bspatch.original \
test/data/8.bspatch.diff \
test/data/8.bspatch.original \
test/data/9.bspatch.diff \
test/data/9.bspatch.modified \
test/data/9.bspatch.original \
test/data/10.bspatch.diff \
test/data/10.bspatch.modified \
test/data/10.bspatch.original \
test/data/11.bspatch.diff \
test/data/12.bspatch.diff \
test/data/12.bspatch.modified \
test/data/12.bspatch.original \
test/data/13.bspatch.modified \
test/data/13.bspatch.original \
test/data/14.bspatch.modified \
test/data/14.bspatch.original \
test/data/15.bspatch.modified \
test/data/15.bspatch.original \
test/data/16.bspatch.diff \
test/data/16.bspatch.original
if ENABLE_TESTS
AM_TESTS_ENVIRONMENT = \
abs_builddir=$(abs_builddir); export abs_builddir;
tap_driver = env AM_TAP_AWK='$(AWK)' $(SHELL) \
$(top_srcdir)/tap-driver.sh
LOG_DRIVER = $(tap_driver)
SH_LOG_DRIVER = $(tap_driver)
TESTS = $(dist_check_SCRIPTS)
dist_check_SCRIPTS = \
test/run.sh
endif
compliant:
@git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Error: can only check code style when include/ and src/ are clean."; \
echo "Stash or commit your changes and try again."; \
exit $$ret; \
elif [ $$ret -gt 1 ]; then \
exit $$ret; \
fi; \
clang-format -i -style=file include/*.h src/*.c; ret=$$?; \
if [ $$ret -ne 0 ]; then \
exit $$ret; \
fi; \
git diff --quiet --exit-code include src; ret=$$?; \
if [ $$ret -eq 1 ]; then \
echo "Code style issues found. Run 'git diff' to view issues."; \
elif [ $$ret -eq 0 ]; then \
echo "No code style issues found."; \
fi; \
exit $$ret
release:
@git rev-parse v$(PACKAGE_VERSION) &> /dev/null; \
+15 -2
View File
@@ -1,5 +1,5 @@
AC_PREREQ([2.66])
AC_INIT([bsdiff], [1.0.0], [patrick.mccarty@intel.com])
AC_INIT([bsdiff], [1.0.4], [patrick.mccarty@intel.com])
AC_CONFIG_MACRO_DIR([m4])
AC_PROG_CC
AC_PROG_CC_STDC
@@ -14,7 +14,6 @@ AM_SILENT_RULES([yes])
LT_INIT
PKG_CHECK_MODULES([zlib], [zlib])
PKG_CHECK_MODULES([CHECK], [check >= 0.9])
AC_ARG_ENABLE([bzip2],
[AS_HELP_STRING([--disable-bzip2],[Do not use bzip2 compression (uses bzip2 by default)])])
@@ -33,6 +32,11 @@ AS_IF([test "$enable_lzma" != "no"], [
])
AM_CONDITIONAL([ENABLE_LZMA], [test "$enable_lzma" != "no"])
AC_ARG_ENABLE(
[tests],
[AS_HELP_STRING([--disable-tests], [Do not enable functional tests (enabled by default)])]
)
have_coverage=no
AC_ARG_ENABLE(coverage, AS_HELP_STRING([--enable-coverage], [enable test coverage]))
if test "x$enable_coverage" = "xyes" ; then
@@ -52,8 +56,17 @@ if test "x$enable_coverage" = "xyes" ; then
fi
AM_CONDITIONAL([COVERAGE], [test "$have_coverage" = "yes"])
AS_IF([test "$enable_tests" != "no"], [
PKG_CHECK_MODULES([CHECK], [check >= 0.9.12])
AC_PATH_PROG([have_valgrind], [valgrind])
AS_IF([test -z "${have_valgrind}"], [
AC_MSG_ERROR([Must have valgrind installed to run functional tests])
])
])
AM_CONDITIONAL([ENABLE_TESTS], [test "$enable_tests" != "no"])
AC_CONFIG_FILES([Makefile data/bsdiff.pc])
AC_REQUIRE_AUX_FILE([tap-driver.sh])
AC_OUTPUT
AC_MSG_RESULT([
+4 -1
View File
@@ -2,6 +2,7 @@
#define __INCLUDE_GUARD_BSHEADER_H
#include <stdint.h>
#include <sys/types.h> // for u_char
#include "bsdiff.h"
@@ -58,7 +59,7 @@ struct header_v20 {
uint64_t extra_length;
uint64_t old_file_length;
uint64_t new_file_length;
uint64_t mtime; /* unused */
uint64_t mtime; /* unused */
uint32_t file_mode;
uint32_t file_owner;
uint32_t file_group;
@@ -177,4 +178,6 @@ static inline int eblock_get_enc(enc_flags_t enc)
}
}
int qsufsort(int64_t *, int64_t *, u_char *, int64_t);
#endif
+57 -196
View File
@@ -32,8 +32,6 @@ __FBSDID
#define _GNU_SOURCE
#include "config.h"
#include <sys/types.h>
#ifdef BSDIFF_WITH_BZIP2
#include <bzlib.h>
#endif
@@ -45,20 +43,20 @@ __FBSDID
#include <lzma.h>
#endif
#include <assert.h>
#include <endian.h>
#include <grp.h>
#include <pthread.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>
#include <endian.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <pthread.h>
#include <assert.h>
#include <sys/mman.h>
#include "bsheader.h"
@@ -76,172 +74,6 @@ static int bsdiff_fulldl;
#undef MIN
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
/* NOTES:
* I and V are chunks of memory (arrays) with length = (oldfile size +1) * sizeof(int64_t).
* Additionally, we pass in arraylen now. The parent function qsufsort receives it, so it
* should be available here as well for error checking.
* start: is actually the point in the array sent in during the suffix sort, which sorts by
* small blocks/chunks.
* len: refers to the length of the current chunk being processed - NOT the array length(s).
* h: will never be more than 8, and increases by *2 during suffix sort (h += h) */
static void split(int64_t *I, int64_t *V, int64_t arraylen, int64_t start, int64_t len,
int64_t h)
{
int64_t i, j, k, x, tmp, jj, kk;
if (len < 16) {
for (k = start; k < start + len; k += j) {
j = 1;
x = V[I[k] + h];
for (i = 1; k + i < start + len; i++) {
if (V[I[k + i] + h] < x) {
x = V[I[k + i] + h];
j = 0;
}
if (V[I[k + i] + h] == x) {
tmp = I[k + j];
I[k + j] = I[k + i];
I[k + i] = tmp;
j++;
}
}
for (i = 0; i < j; i++) {
V[I[k + i]] = k + j - 1;
}
if (j == 1) {
I[k] = -1;
}
}
return;
}
x = V[I[start + len / 2] + h];
jj = 0;
kk = 0;
for (i = start; i < start + len; i++) {
if (V[I[i] + h] < x) {
jj++;
}
if (V[I[i] + h] == x) {
kk++;
}
}
jj += start;
kk += jj;
i = start;
j = 0;
k = 0;
while (i < jj) {
if (V[I[i] + h] < x) {
i++;
} else if (V[I[i] + h] == x) {
tmp = I[i];
I[i] = I[jj + j];
I[jj + j] = tmp;
j++;
} else {
tmp = I[i];
I[i] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
while (jj + j < kk) {
if (V[I[jj + j] + h] == x) {
j++;
} else {
tmp = I[jj + j];
I[jj + j] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
if (jj > start) {
split(I, V, arraylen, start, jj - start, h);
}
for (i = 0; i < kk - jj; i++) {
V[I[jj + i]] = kk - 1;
}
if (jj == kk - 1) {
I[jj] = -1;
}
if (start + len > kk) {
split(I, V, arraylen, kk, start + len - kk, h);
}
}
/* The old_data (previous file data) is passed into this suffix sort and sorted
* accordingly using the I and V arrays, which are both of length oldsize +1. */
static int qsufsort(int64_t *I, int64_t *V, u_char *old, int64_t oldsize)
{
int64_t buckets[QSUF_BUCKET_SIZE];
int64_t i, h, len;
for (i = 0; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] = 0;
}
for (i = 0; i < oldsize; i++) {
buckets[old[i]]++;
}
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] += buckets[i - 1];
}
for (i = QSUF_BUCKET_SIZE - 1; i > 0; i--) {
buckets[i] = buckets[i - 1];
}
buckets[0] = 0;
for (i = 0; i < oldsize; i++) {
if (buckets[old[i]] > oldsize + 1) {
return -1;
}
I[++buckets[old[i]]] = i;
}
for (i = 0; i < oldsize; i++) {
V[i] = buckets[old[i]];
}
V[oldsize] = 0;
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
if (buckets[i] == buckets[i - 1] + 1) {
I[buckets[i]] = -1;
}
}
I[0] = -1;
for (h = 1; I[0] != -(oldsize + 1); h += h) {
len = 0;
for (i = 0; i < oldsize + 1;) {
if (I[i] < 0) {
len -= I[i];
i -= I[i];
} else {
if (len) {
I[i - len] = -len;
}
len = V[I[i]] + 1 - i;
split(I, V, oldsize, i, len, h);
i += len;
len = 0;
}
}
if (len) {
I[i - len] = -len;
}
}
for (i = 0; i < oldsize + 1; i++) {
I[V[i]] = i;
}
return 0;
}
static int64_t matchlen(u_char *old, int64_t oldsize, u_char *new,
int64_t newsize)
{
@@ -256,30 +88,60 @@ static int64_t matchlen(u_char *old, int64_t oldsize, u_char *new,
return i;
}
static int64_t search(int64_t *I, u_char *old, int64_t oldsize,
u_char *new, int64_t newsize, int64_t st, int64_t en,
int64_t *pos)
/**
* Finds the longest matching array of bytes between the OLD and NEW file. The
* old file is suffix-sorted; the suffix-sorted array is stored at I, and
* indices to search between are indicated by ST (start) and EN (end). The
* function does not return a value, but once a match is determined, POS is
* updated to the position of the match within OLD, and MAX_LEN is set to the
* match length.
*/
static void search(int64_t *I, u_char *old, int64_t oldsize,
u_char *new, int64_t newsize, int64_t st, int64_t en,
int64_t *pos, int64_t *max_len)
{
int64_t x, y;
/* Initialize max_len for the binary search */
if (st == 0 && en == oldsize) {
*max_len = matchlen(old, oldsize, new, newsize);
*pos = I[st];
}
/* The binary search terminates here when "en" and "st" are adjacent
* indices in the suffix-sorted array. */
if (en - st < 2) {
x = matchlen(old + I[st], oldsize - I[st], new, newsize);
y = matchlen(old + I[en], oldsize - I[en], new, newsize);
if (x > y) {
if (x > *max_len) {
*max_len = x;
*pos = I[st];
return x;
} else {
*pos = I[en];
return y;
}
y = matchlen(old + I[en], oldsize - I[en], new, newsize);
if (y > *max_len) {
*max_len = y;
*pos = I[en];
}
return;
}
x = st + (en - st) / 2;
if (memcmp(old + I[x], new, MIN(oldsize - I[x], newsize)) < 0) {
return search(I, old, oldsize, new, newsize, x, en, pos);
int64_t length = MIN(oldsize - I[x], newsize);
u_char *oldoffset = old + I[x];
/* This match *could* be the longest one, so check for that here */
int64_t tmp = matchlen(oldoffset, length, new, length);
if (tmp > *max_len) {
*max_len = tmp;
*pos = I[x];
}
/* Determine how to continue the binary search */
if (memcmp(oldoffset, new, length) < 0) {
return search(I, old, oldsize, new, newsize, x, en, pos, max_len);
} else {
return search(I, old, oldsize, new, newsize, st, x, pos);
return search(I, old, oldsize, new, newsize, st, x, pos, max_len);
}
}
@@ -586,7 +448,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
memset(&small_header, 0, sizeof(struct header_v21));
memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8);
efd = open(delta_filename, O_CREAT | O_EXCL | O_RDWR, 00600);
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
close(fd);
return -1;
@@ -668,7 +530,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
memset(&small_header, 0, sizeof(struct header_v21));
memcpy(&small_header.magic, BSDIFF_HDR_FULLDL, 8);
efd = open(delta_filename, O_CREAT | O_EXCL | O_RDWR, 00600);
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
close(fd);
munmap(old_data, oldsize);
@@ -754,9 +616,8 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
oldscore = 0;
for (scsc = scan += len; scan < newsize; scan++) {
len =
search(I, old_data, oldsize, new_data + scan, newsize - scan,
0, oldsize, &pos);
search(I, old_data, oldsize, new_data + scan, newsize - scan,
0, oldsize, &pos, &len);
for (; scsc < scan + len; scsc++) {
if ((scsc + lastoffset < oldsize) &&
@@ -883,7 +744,7 @@ int make_bsdiff_delta(char *old_filename, char *new_filename, char *delta_filena
/* Create the patch file */
efd = open(delta_filename, O_CREAT | O_EXCL | O_RDWR, 00600);
efd = open(delta_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (efd < 0) {
ret = -1;
goto fulldl_free;
+3 -3
View File
@@ -30,12 +30,12 @@
*/
#define _GNU_SOURCE
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
#include <time.h>
#include <unistd.h>
#include "bsdiff.h"
#include "bsheader.h"
@@ -110,7 +110,7 @@ static void print_v20_header(struct header_v20 *h, FILE *f)
if (h->mtime == 0) {
printf("Mtime:\t(not set, as expected)\n");
} else {
printf("Mtime:\t%s (probably means there is a bug)\n", ctime((const time_t*)&h->mtime));
printf("Mtime:\t%s (probably means there is a bug)\n", ctime((const time_t *)&h->mtime));
}
printf("Mode:\t%4o\n", h->file_mode);
printf("Uid:\t%d\n", h->file_owner);
+15 -15
View File
@@ -44,23 +44,23 @@ __FBSDID
#include <lzma.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <zlib.h>
#include <stdint.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#include <limits.h>
#include <linux/fs.h>
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <fcntl.h>
#include <grp.h>
#include <limits.h>
#include <linux/fs.h>
#include <pwd.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <zlib.h>
#include "bsheader.h"
@@ -259,7 +259,7 @@ typedef struct {
#ifdef BSDIFF_WITH_BZIP2
BZFILE *bz2; /* method = BZIP2 */
#endif
gzFile gz; /* method = GZIP */
gzFile gz; /* method = GZIP */
#ifdef BSDIFF_WITH_LZMA
xzfile *xz; /* method = XZ */
#endif
@@ -690,7 +690,7 @@ static int apply_delta_v2(int subver, FILE *f,
cfclose(&ef);
/* Write the new file */
fd = open(new_filename, O_CREAT | O_EXCL | O_WRONLY, 0600);
fd = open(new_filename, O_CREAT | O_EXCL | O_WRONLY, 00644);
if (fd < 0) {
ret = -1;
goto writeerror;
+193
View File
@@ -0,0 +1,193 @@
/*-
* Copyright 2003-2005 Colin Percival
* All rights reserved
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted providing that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "bsheader.h"
/* NOTES:
* I and V are chunks of memory (arrays) with length = (oldfile size +1) * sizeof(int64_t).
* Additionally, we pass in arraylen now. The parent function qsufsort receives it, so it
* should be available here as well for error checking.
* start: is actually the point in the array sent in during the suffix sort, which sorts by
* small blocks/chunks.
* len: refers to the length of the current chunk being processed - NOT the array length(s).
* h: will never be more than 8, and increases by *2 during suffix sort (h += h) */
static void split(int64_t *I, int64_t *V, int64_t arraylen, int64_t start, int64_t len,
int64_t h)
{
int64_t i, j, k, x, tmp, jj, kk;
if (len < 16) {
for (k = start; k < start + len; k += j) {
j = 1;
x = V[I[k] + h];
for (i = 1; k + i < start + len; i++) {
if (V[I[k + i] + h] < x) {
x = V[I[k + i] + h];
j = 0;
}
if (V[I[k + i] + h] == x) {
tmp = I[k + j];
I[k + j] = I[k + i];
I[k + i] = tmp;
j++;
}
}
for (i = 0; i < j; i++) {
V[I[k + i]] = k + j - 1;
}
if (j == 1) {
I[k] = -1;
}
}
return;
}
x = V[I[start + len / 2] + h];
jj = 0;
kk = 0;
for (i = start; i < start + len; i++) {
if (V[I[i] + h] < x) {
jj++;
}
if (V[I[i] + h] == x) {
kk++;
}
}
jj += start;
kk += jj;
i = start;
j = 0;
k = 0;
while (i < jj) {
if (V[I[i] + h] < x) {
i++;
} else if (V[I[i] + h] == x) {
tmp = I[i];
I[i] = I[jj + j];
I[jj + j] = tmp;
j++;
} else {
tmp = I[i];
I[i] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
while (jj + j < kk) {
if (V[I[jj + j] + h] == x) {
j++;
} else {
tmp = I[jj + j];
I[jj + j] = I[kk + k];
I[kk + k] = tmp;
k++;
}
}
if (jj > start) {
split(I, V, arraylen, start, jj - start, h);
}
for (i = 0; i < kk - jj; i++) {
V[I[jj + i]] = kk - 1;
}
if (jj == kk - 1) {
I[jj] = -1;
}
if (start + len > kk) {
split(I, V, arraylen, kk, start + len - kk, h);
}
}
/* The old_data (previous file data) is passed into this suffix sort and sorted
* accordingly using the I and V arrays, which are both of length oldsize +1. */
int qsufsort(int64_t *I, int64_t *V, u_char *old, int64_t oldsize)
{
int64_t buckets[QSUF_BUCKET_SIZE];
int64_t i, h, len;
for (i = 0; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] = 0;
}
for (i = 0; i < oldsize; i++) {
buckets[old[i]]++;
}
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
buckets[i] += buckets[i - 1];
}
for (i = QSUF_BUCKET_SIZE - 1; i > 0; i--) {
buckets[i] = buckets[i - 1];
}
buckets[0] = 0;
for (i = 0; i < oldsize; i++) {
if (buckets[old[i]] > oldsize + 1) {
return -1;
}
I[++buckets[old[i]]] = i;
}
for (i = 0; i < oldsize; i++) {
V[i] = buckets[old[i]];
}
V[oldsize] = 0;
for (i = 1; i < QSUF_BUCKET_SIZE; i++) {
if (buckets[i] == buckets[i - 1] + 1) {
I[buckets[i]] = -1;
}
}
I[0] = -1;
for (h = 1; I[0] != -(oldsize + 1); h += h) {
len = 0;
for (i = 0; i < oldsize + 1;) {
if (I[i] < 0) {
len -= I[i];
i -= I[i];
} else {
if (len) {
I[i - len] = -len;
}
len = V[I[i]] + 1 - i;
split(I, V, oldsize, i, len, h);
i += len;
len = 0;
}
}
if (len) {
I[i - len] = -len;
}
}
for (i = 0; i < oldsize + 1; i++) {
I[V[i]] = i;
}
return 0;
}
+73 -46
View File
@@ -1,57 +1,89 @@
#!/bin/bash
# $srcdir variable is set by automake environment
cd $srcdir/test
# number is incremented after running every test
testnum=0
sudo rm -f *.diff *.out
libdir="$(realpath "../.libs")"
ldpath="LD_LIBRARY_PATH=$libdir"
BSDIFF="sudo $ldpath valgrind -q $libdir/bsdiff"
BSPATCH="sudo $ldpath valgrind -q $libdir/bspatch"
VALGRIND="valgrind -q"
if [ -n "$SKIP_VALGRIND" ]; then
VALGRIND=""
fi
echo -n "5.."
libdir="$abs_builddir/.libs"
ldpath="LD_LIBRARY_PATH=$libdir"
BSDIFF="sudo $ldpath $VALGRIND $libdir/bsdiff"
BSPATCH="sudo $ldpath $VALGRIND $libdir/bspatch"
# If exit status is 0, the test succeeded. Else it failed.
check_success() {
res=$?
[ -n "$1" ] && msg="$1" || msg=""
testnum=$(expr $testnum + 1)
if [ $res -ne 0 ]; then
echo "not ok $testnum - $msg"
else
echo "ok $testnum"
fi
}
# If exit status is 255, the test succeeded. Else it failed.
check_failure() {
res=$?
[ -n "$1" ] && msg="$1" || msg=""
testnum=$(expr $testnum + 1)
if [ $res -ne 255 ]; then
echo "not ok $testnum - $msg"
else
echo "ok $testnum"
fi
}
echo "Running test #5 ..."
$BSPATCH data/5.bspatch.original 5.out data/5.bspatch.diff
echo -n "6.."
check_success
echo "Running test #6 ..."
$BSPATCH data/6.bspatch.original 6.out data/6.bspatch.diff
echo -n "7.."
check_success
echo "Running test #7 ..."
$BSPATCH data/7.bspatch.original 7.out data/7.bspatch.diff
echo -n "8.."
check_success
echo "Running test #8 ..."
$BSPATCH data/8.bspatch.original 8.out data/8.bspatch.diff
echo -n "9.."
check_success
echo "Running test #9 ..."
$BSPATCH data/9.bspatch.original 9.out data/9.bspatch.diff
diff data/9.bspatch.modified 9.out
if [ $? -ne 0 ]
then
echo "bspatch 9 output does not match expected!!"
fi
echo -n "10.."
check_success "output does not match expected!!"
echo "Running test #10 ..."
$BSPATCH data/10.bspatch.original 10.out data/10.bspatch.diff
diff data/10.bspatch.modified 10.out
if [ $? -ne 0 ]
then
echo "bspatch 10 output does not match expected!!"
fi
check_success "output does not match expected!!"
#same as 9 but with zeros encoding
echo -n "11.."
echo "Running test #11 ..."
$BSPATCH data/9.bspatch.original 11.out data/11.bspatch.diff
diff data/9.bspatch.modified 11.out
if [ $? -ne 0 ]
then
echo "bspatch 11 output does not match expected!!"
fi
echo -n "12.."
check_success "output does not match expected!!"
echo "Running test #12 ..."
$BSPATCH data/12.bspatch.original 12.out data/12.bspatch.diff
diff data/12.bspatch.modified 12.out
if [ $? -ne 0 ]
then
echo "bspatch 12 output does not match expected!!"
fi
echo -n "13.."
check_success "output does not match expected!!"
echo "Running test #13 ..."
$BSDIFF data/13.bspatch.original data/13.bspatch.modified 13.diff any
$BSPATCH data/13.bspatch.original 13.out 13.diff
diff data/13.bspatch.modified 13.out
if [ $? -ne 0 ]
then
echo "bspatch 13 output does not match expected!!"
fi
check_success "output does not match expected!!"
# Next a very loooong running test, but one which successfully condenses the 2MB
# original file pair into a 26kB bsdiff. The bsdiff computation alone (ie:
@@ -62,26 +94,21 @@ fi
# used in a regression test run at every check-in of code changes to the bsdiff
# implementation.
#
#echo -n "14.."
#echo "Running test #14 ..."
#$BSDIFF data/14.bspatch.original data/14.bspatch.modified 14.diff any
#$BSPATCH data/14.bspatch.original 14.out 14.diff
#diff data/14.bspatch.modified 14.out
#if [ $? -ne 0 ]
#then
# echo "bspatch 14 output does not match expected!!"
#fi
#check_success "output does not match expected!!"
echo -n "15.."
echo "Running test #15 ..."
$BSDIFF data/15.bspatch.original data/15.bspatch.modified 15.diff any
# expected output: "Failed to create delta (-1)"
if [ $? -ne 255 ]
then
echo "bspatch 15 creation has memory management issue!"
fi
check_failure "patch creation has memory management issue!"
echo -n "16.."
echo "Running test #16 ..."
# any valgrind errors may indicate a buffer overflow
$BSPATCH data/16.bspatch.original 16.out data/16.bspatch.diff
check_success
# add final newline
echo ""
# For TAP support, output the plan
echo "1..${testnum}"