🏗 Add xdelta3 dependency
This commit is contained in:
parent
80f2439239
commit
ae59ce2256
97 changed files with 45332 additions and 1 deletions
8
lib/xdelta3/testing/Makefile
Normal file
8
lib/xdelta3/testing/Makefile
Normal file
|
@ -0,0 +1,8 @@
|
|||
all:
|
||||
(cd .. && make all)
|
||||
|
||||
xdelta3regtest:
|
||||
(cd .. && make xdelta3regtest)
|
||||
|
||||
xdelta3checksum:
|
||||
(cd .. && make xdelta3checksum)
|
770
lib/xdelta3/testing/checksum_test.cc
Normal file
770
lib/xdelta3/testing/checksum_test.cc
Normal file
|
@ -0,0 +1,770 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
#include <assert.h>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include "../cpp-btree/btree_map.h"
|
||||
|
||||
extern "C" {
|
||||
uint32_t xd3_large32_cksum_old (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look);
|
||||
uint32_t xd3_large32_cksum_update_old (xd3_hash_cfg *cfg, uint32_t cksum,
|
||||
const uint8_t *base, const usize_t look);
|
||||
|
||||
uint64_t xd3_large64_cksum_old (xd3_hash_cfg *cfg, const uint8_t *base, const usize_t look);
|
||||
uint64_t xd3_large64_cksum_update_old (xd3_hash_cfg *cfg, uint64_t cksum,
|
||||
const uint8_t *base, const usize_t look);
|
||||
}
|
||||
|
||||
using btree::btree_map;
|
||||
using std::list;
|
||||
using std::vector;
|
||||
|
||||
// MLCG parameters
|
||||
// a, a*
|
||||
uint32_t good_32bit_values[] = {
|
||||
1597334677U, // ...
|
||||
741103597U, 887987685U,
|
||||
};
|
||||
|
||||
// a, a*
|
||||
uint64_t good_64bit_values[] = {
|
||||
1181783497276652981ULL, 4292484099903637661ULL,
|
||||
7664345821815920749ULL, // ...
|
||||
};
|
||||
|
||||
void print_header() {
|
||||
static int hdr_cnt = 0;
|
||||
if (hdr_cnt++ % 20 == 0) {
|
||||
printf("%-32sConf\t\tCount\tUniq\tFull\tCover\tColls"
|
||||
"\tMB/s\tIters\t#Colls\n", "Name");
|
||||
}
|
||||
}
|
||||
|
||||
struct true_type { };
|
||||
struct false_type { };
|
||||
|
||||
template <typename Word>
|
||||
usize_t bitsof();
|
||||
|
||||
template<>
|
||||
usize_t bitsof<unsigned int>() {
|
||||
return sizeof(unsigned int) * 8;
|
||||
}
|
||||
|
||||
template<>
|
||||
usize_t bitsof<unsigned long>() {
|
||||
return sizeof(unsigned long) * 8;
|
||||
}
|
||||
|
||||
template<>
|
||||
usize_t bitsof<unsigned long long>() {
|
||||
return sizeof(unsigned long long) * 8;
|
||||
}
|
||||
|
||||
template <typename Word>
|
||||
struct hhash { // shift "s" bits leaving the high bits as a hash value for
|
||||
// this checksum, which are the most "distant" in terms of the
|
||||
// spectral test for the rabin_karp MLCG. For short windows,
|
||||
// the high bits aren't enough, XOR "mask" worth of these in.
|
||||
Word operator()(const Word t, const Word s, const Word mask) {
|
||||
return (t >> s) ^ (t & mask);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Word>
|
||||
Word good_word();
|
||||
|
||||
template<>
|
||||
uint32_t good_word<uint32_t>() {
|
||||
return good_32bit_values[0];
|
||||
}
|
||||
|
||||
template<>
|
||||
uint64_t good_word<uint64_t>() {
|
||||
return good_64bit_values[0];
|
||||
}
|
||||
|
||||
// CLASSES
|
||||
|
||||
#define SELF Word, CksumSize, CksumSkip, Hash, Compaction
|
||||
#define MEMBER template <typename Word, \
|
||||
int CksumSize, \
|
||||
int CksumSkip, \
|
||||
typename Hash, \
|
||||
int Compaction>
|
||||
|
||||
MEMBER
|
||||
struct cksum_params {
|
||||
typedef Word word_type;
|
||||
typedef Hash hash_type;
|
||||
|
||||
static const int cksum_size = CksumSize;
|
||||
static const int cksum_skip = CksumSkip;
|
||||
static const int compaction = Compaction;
|
||||
};
|
||||
|
||||
MEMBER
|
||||
struct rabin_karp : public cksum_params<SELF> {
|
||||
// (a^cksum_size-1 c_0) + (a^cksum_size-2 c_1) ...
|
||||
rabin_karp()
|
||||
: powers(make_powers()),
|
||||
product(powers[0] * good_word<Word>()),
|
||||
incr_state(0) { }
|
||||
|
||||
static Word* make_powers() {
|
||||
Word *p = new Word[CksumSize];
|
||||
p[CksumSize - 1] = 1;
|
||||
for (int i = CksumSize - 2; i >= 0; i--) {
|
||||
p[i] = p[i + 1] * good_word<Word>();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
~rabin_karp() {
|
||||
delete [] powers;
|
||||
}
|
||||
|
||||
Word step(const uint8_t *ptr) {
|
||||
Word h = 0;
|
||||
for (int i = 0; i < CksumSize; i++) {
|
||||
h += (ptr[i]) * powers[i];
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
Word state0(const uint8_t *ptr) {
|
||||
incr_state = step(ptr);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
Word incr(const uint8_t *ptr) {
|
||||
incr_state = good_word<Word>() * incr_state -
|
||||
product * (ptr[-1]) + (ptr[CksumSize - 1]);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
const Word *const powers;
|
||||
const Word product;
|
||||
Word incr_state;
|
||||
};
|
||||
|
||||
MEMBER
|
||||
struct with_stream : public cksum_params<SELF> {
|
||||
xd3_stream stream;
|
||||
|
||||
with_stream()
|
||||
{
|
||||
xd3_config cfg;
|
||||
memset (&stream, 0, sizeof (stream));
|
||||
xd3_init_config (&cfg, 0);
|
||||
cfg.smatch_cfg = XD3_SMATCH_SOFT;
|
||||
cfg.smatcher_soft.large_look = CksumSize;
|
||||
cfg.smatcher_soft.large_step = CksumSkip;
|
||||
cfg.smatcher_soft.small_look = 4;
|
||||
cfg.smatcher_soft.small_chain = 4;
|
||||
cfg.smatcher_soft.small_lchain = 4;
|
||||
cfg.smatcher_soft.max_lazy = 4;
|
||||
cfg.smatcher_soft.long_enough = 4;
|
||||
CHECK_EQ(0, xd3_config_stream (&stream, &cfg));
|
||||
|
||||
CHECK_EQ(0, xd3_size_hashtable (&stream,
|
||||
1<<10 /* ignored */,
|
||||
stream.smatcher.large_look,
|
||||
& stream.large_hash));
|
||||
}
|
||||
~with_stream()
|
||||
{
|
||||
xd3_free_stream (&stream);
|
||||
}
|
||||
};
|
||||
|
||||
MEMBER
|
||||
struct large_cksum : public with_stream<SELF> {
|
||||
Word step(const uint8_t *ptr) {
|
||||
return xd3_large_cksum (&this->stream.large_hash, ptr, CksumSize);
|
||||
}
|
||||
|
||||
Word state0(const uint8_t *ptr) {
|
||||
incr_state = step(ptr);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
Word incr(const uint8_t *ptr) {
|
||||
incr_state = xd3_large_cksum_update (&this->stream.large_hash,
|
||||
incr_state, ptr - 1, CksumSize);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
Word incr_state;
|
||||
};
|
||||
|
||||
#if SIZEOF_USIZE_T == 4
|
||||
#define xd3_large_cksum_old xd3_large32_cksum_old
|
||||
#define xd3_large_cksum_update_old xd3_large32_cksum_update_old
|
||||
#elif SIZEOF_USIZE_T == 8
|
||||
#define xd3_large_cksum_old xd3_large64_cksum_old
|
||||
#define xd3_large_cksum_update_old xd3_large64_cksum_update_old
|
||||
#endif
|
||||
|
||||
MEMBER
|
||||
struct large_cksum_old : public with_stream<SELF> {
|
||||
Word step(const uint8_t *ptr) {
|
||||
return xd3_large_cksum_old (&this->stream.large_hash, ptr, CksumSize);
|
||||
}
|
||||
|
||||
Word state0(const uint8_t *ptr) {
|
||||
incr_state = step(ptr);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
Word incr(const uint8_t *ptr) {
|
||||
incr_state = xd3_large_cksum_update_old (&this->stream.large_hash,
|
||||
incr_state, ptr - 1, CksumSize);
|
||||
return incr_state;
|
||||
}
|
||||
|
||||
Word incr_state;
|
||||
};
|
||||
|
||||
// TESTS
|
||||
|
||||
template <typename Word>
|
||||
struct file_stats {
|
||||
typedef const uint8_t* ptr_type;
|
||||
typedef Word word_type;
|
||||
typedef btree::btree_multimap<word_type, ptr_type> table_type;
|
||||
typedef typename table_type::iterator table_iterator;
|
||||
|
||||
usize_t cksum_size;
|
||||
usize_t cksum_skip;
|
||||
usize_t unique;
|
||||
usize_t unique_values;
|
||||
usize_t count;
|
||||
table_type table;
|
||||
|
||||
file_stats(usize_t size, usize_t skip)
|
||||
: cksum_size(size),
|
||||
cksum_skip(skip),
|
||||
unique(0),
|
||||
unique_values(0),
|
||||
count(0) {
|
||||
}
|
||||
|
||||
void reset() {
|
||||
unique = 0;
|
||||
unique_values = 0;
|
||||
count = 0;
|
||||
table.clear();
|
||||
}
|
||||
|
||||
void update(word_type word, ptr_type ptr) {
|
||||
table_iterator t_i = table.find(word);
|
||||
|
||||
count++;
|
||||
if (t_i != table.end()) {
|
||||
int collisions = 0;
|
||||
for (table_iterator p_i = t_i;
|
||||
p_i != table.end() && p_i->first == word;
|
||||
++p_i) {
|
||||
if (memcmp(p_i->second, ptr, cksum_size) == 0) {
|
||||
return;
|
||||
}
|
||||
collisions++;
|
||||
}
|
||||
if (collisions >= 1000) {
|
||||
fprintf(stderr, "Something is not right, lots of collisions=%d\n",
|
||||
collisions);
|
||||
abort();
|
||||
}
|
||||
} else {
|
||||
unique_values++;
|
||||
}
|
||||
unique++;
|
||||
table.insert(std::make_pair(word, ptr));
|
||||
return;
|
||||
}
|
||||
|
||||
void freeze() {
|
||||
table.clear();
|
||||
}
|
||||
};
|
||||
|
||||
struct test_result_base;
|
||||
|
||||
static vector<test_result_base*> all_tests;
|
||||
|
||||
struct test_result_base {
|
||||
virtual ~test_result_base() {
|
||||
}
|
||||
virtual void reset() = 0;
|
||||
virtual void print() = 0;
|
||||
virtual void get(const uint8_t* buf, const size_t buf_size,
|
||||
usize_t iters) = 0;
|
||||
virtual void stat() = 0;
|
||||
virtual usize_t count() = 0;
|
||||
virtual usize_t dups() = 0;
|
||||
virtual double uniqueness() = 0;
|
||||
virtual double fullness() = 0;
|
||||
virtual double collisions() = 0;
|
||||
virtual double coverage() = 0;
|
||||
virtual double compression() = 0;
|
||||
virtual double time() = 0;
|
||||
virtual double total_time() = 0;
|
||||
virtual usize_t total_count() = 0;
|
||||
virtual usize_t total_dups() = 0;
|
||||
};
|
||||
|
||||
template <typename Checksum>
|
||||
struct test_result : public test_result_base {
|
||||
Checksum cksum;
|
||||
const char *test_name;
|
||||
file_stats<typename Checksum::word_type> fstats;
|
||||
usize_t test_size;
|
||||
usize_t n_steps;
|
||||
usize_t n_incrs;
|
||||
typename Checksum::word_type s_bits;
|
||||
typename Checksum::word_type s_mask;
|
||||
usize_t t_entries;
|
||||
usize_t h_bits;
|
||||
usize_t h_buckets_full;
|
||||
char *hash_table;
|
||||
long accum_millis;
|
||||
usize_t accum_iters;
|
||||
|
||||
// These are not reset
|
||||
double accum_time;
|
||||
usize_t accum_count;
|
||||
usize_t accum_dups;
|
||||
usize_t accum_colls;
|
||||
size_t accum_size;
|
||||
|
||||
test_result(const char *name)
|
||||
: test_name(name),
|
||||
fstats(Checksum::cksum_size, Checksum::cksum_skip),
|
||||
hash_table(NULL),
|
||||
accum_millis(0),
|
||||
accum_iters(0),
|
||||
accum_time(0.0),
|
||||
accum_count(0),
|
||||
accum_dups(0),
|
||||
accum_colls(0),
|
||||
accum_size(0) {
|
||||
all_tests.push_back(this);
|
||||
}
|
||||
|
||||
~test_result() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
// size of file
|
||||
test_size = 0;
|
||||
|
||||
// count
|
||||
n_steps = 0;
|
||||
n_incrs = 0;
|
||||
|
||||
// four values used by new_table()/summarize_table()
|
||||
s_bits = 0;
|
||||
s_mask = 0;
|
||||
t_entries = 0;
|
||||
h_bits = 0;
|
||||
h_buckets_full = 0;
|
||||
|
||||
accum_millis = 0;
|
||||
accum_iters = 0;
|
||||
|
||||
fstats.reset();
|
||||
|
||||
// temporary
|
||||
if (hash_table) {
|
||||
delete(hash_table);
|
||||
hash_table = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
usize_t count() {
|
||||
if (Checksum::cksum_skip == 1) {
|
||||
return n_incrs;
|
||||
} else {
|
||||
return n_steps;
|
||||
}
|
||||
}
|
||||
|
||||
usize_t dups() {
|
||||
return fstats.count - fstats.unique;
|
||||
}
|
||||
|
||||
/* Fraction of distinct strings of length cksum_size which are not
|
||||
* represented in the hash table. */
|
||||
double collisions() {
|
||||
return (fstats.unique - fstats.unique_values) / (double) fstats.unique;
|
||||
}
|
||||
usize_t colls() {
|
||||
return (fstats.unique - fstats.unique_values);
|
||||
}
|
||||
|
||||
double uniqueness() {
|
||||
return 1.0 - (double) dups() / count();
|
||||
}
|
||||
|
||||
double fullness() {
|
||||
return (double) h_buckets_full / (1 << h_bits);
|
||||
}
|
||||
|
||||
double coverage() {
|
||||
return (double) h_buckets_full / uniqueness() / count();
|
||||
}
|
||||
|
||||
double compression() {
|
||||
return 1.0 - coverage();
|
||||
}
|
||||
|
||||
double time() {
|
||||
return (double) accum_millis / accum_iters;
|
||||
}
|
||||
|
||||
double total_time() {
|
||||
return accum_time;
|
||||
}
|
||||
|
||||
usize_t total_count() {
|
||||
return accum_count;
|
||||
}
|
||||
|
||||
usize_t total_dups() {
|
||||
return accum_dups;
|
||||
}
|
||||
|
||||
usize_t total_colls() {
|
||||
return accum_dups;
|
||||
}
|
||||
|
||||
void stat() {
|
||||
accum_time += time();
|
||||
accum_count += count();
|
||||
accum_dups += dups();
|
||||
accum_colls += colls();
|
||||
accum_size += test_size;
|
||||
}
|
||||
|
||||
void print() {
|
||||
if (fstats.count != count()) {
|
||||
fprintf(stderr, "internal error: %" W "d != %" W "d\n", fstats.count, count());
|
||||
abort();
|
||||
}
|
||||
print_header();
|
||||
printf("%-32s%d/%d 2^%" W "u\t%" W "u\t%0.4f\t%.4f\t%.4f\t%.1e\t%.2f\t"
|
||||
"%" W "u\t%" W "u\n",
|
||||
test_name,
|
||||
Checksum::cksum_size,
|
||||
Checksum::cksum_skip,
|
||||
h_bits,
|
||||
count(),
|
||||
uniqueness(),
|
||||
fullness(),
|
||||
coverage(),
|
||||
collisions(),
|
||||
0.001 * accum_iters * test_size / accum_millis,
|
||||
accum_iters,
|
||||
colls());
|
||||
}
|
||||
|
||||
usize_t size_log2 (usize_t slots) {
|
||||
usize_t bits = bitsof<typename Checksum::word_type>() - 1;
|
||||
usize_t i;
|
||||
|
||||
for (i = 3; i <= bits; i += 1) {
|
||||
if (slots <= (1U << i)) {
|
||||
return i - Checksum::compaction;
|
||||
}
|
||||
}
|
||||
|
||||
return bits;
|
||||
}
|
||||
|
||||
void new_table(usize_t entries) {
|
||||
t_entries = entries;
|
||||
h_bits = size_log2(entries);
|
||||
|
||||
usize_t n = 1 << h_bits;
|
||||
|
||||
s_bits = bitsof<typename Checksum::word_type>() - h_bits;
|
||||
s_mask = n - 1U;
|
||||
|
||||
hash_table = new char[n / 8];
|
||||
memset(hash_table, 0, n / 8);
|
||||
}
|
||||
|
||||
int get_table_bit(usize_t i) {
|
||||
return hash_table[i/8] & (1 << i%8);
|
||||
}
|
||||
|
||||
int set_table_bit(usize_t i) {
|
||||
return hash_table[i/8] |= (1 << i%8);
|
||||
}
|
||||
|
||||
void summarize_table() {
|
||||
usize_t n = 1 << h_bits;
|
||||
usize_t f = 0;
|
||||
for (usize_t i = 0; i < n; i++) {
|
||||
if (get_table_bit(i)) {
|
||||
f++;
|
||||
}
|
||||
}
|
||||
h_buckets_full = f;
|
||||
}
|
||||
|
||||
void get(const uint8_t* buf, const size_t buf_size, usize_t test_iters) {
|
||||
typename Checksum::hash_type hash;
|
||||
const uint8_t *ptr;
|
||||
const uint8_t *end;
|
||||
usize_t periods;
|
||||
int64_t last_offset;
|
||||
int64_t stop;
|
||||
|
||||
test_size = buf_size;
|
||||
last_offset = buf_size - Checksum::cksum_size;
|
||||
|
||||
if (last_offset < 0) {
|
||||
periods = 0;
|
||||
n_steps = 0;
|
||||
n_incrs = 0;
|
||||
stop = -Checksum::cksum_size;
|
||||
} else {
|
||||
periods = last_offset / Checksum::cksum_skip;
|
||||
n_steps = periods + 1;
|
||||
n_incrs = last_offset + 1;
|
||||
stop = last_offset - (periods + 1) * Checksum::cksum_skip;
|
||||
}
|
||||
|
||||
// Compute file stats once.
|
||||
if (fstats.unique_values == 0) {
|
||||
if (Checksum::cksum_skip == 1) {
|
||||
for (size_t i = 0; i <= buf_size - Checksum::cksum_size; i++) {
|
||||
fstats.update(hash(cksum.step(buf + i), s_bits, s_mask), buf + i);
|
||||
}
|
||||
} else {
|
||||
ptr = buf + last_offset;
|
||||
end = buf + stop;
|
||||
|
||||
for (; ptr != end; ptr -= Checksum::cksum_skip) {
|
||||
fstats.update(hash(cksum.step(ptr), s_bits, s_mask), ptr);
|
||||
}
|
||||
}
|
||||
fstats.freeze();
|
||||
}
|
||||
|
||||
long start_test = get_millisecs_now();
|
||||
|
||||
if (Checksum::cksum_skip != 1) {
|
||||
new_table(n_steps);
|
||||
|
||||
for (usize_t i = 0; i < test_iters; i++) {
|
||||
ptr = buf + last_offset;
|
||||
end = buf + stop;
|
||||
|
||||
for (; ptr != end; ptr -= Checksum::cksum_skip) {
|
||||
set_table_bit(hash(cksum.step(ptr), s_bits, s_mask));
|
||||
}
|
||||
}
|
||||
|
||||
summarize_table();
|
||||
}
|
||||
|
||||
stop = buf_size - Checksum::cksum_size + 1;
|
||||
if (stop < 0) {
|
||||
stop = 0;
|
||||
}
|
||||
|
||||
if (Checksum::cksum_skip == 1) {
|
||||
new_table(n_incrs);
|
||||
|
||||
for (usize_t i = 0; i < test_iters; i++) {
|
||||
ptr = buf;
|
||||
end = buf + stop;
|
||||
|
||||
if (ptr != end) {
|
||||
set_table_bit(hash(cksum.state0(ptr++), s_bits, s_mask));
|
||||
}
|
||||
|
||||
for (; ptr != end; ptr++) {
|
||||
typename Checksum::word_type w = cksum.incr(ptr);
|
||||
CHECK_EQ(w, cksum.step(ptr));
|
||||
set_table_bit(hash(w, s_bits, s_mask));
|
||||
}
|
||||
}
|
||||
|
||||
summarize_table();
|
||||
}
|
||||
|
||||
accum_iters += test_iters;
|
||||
accum_millis += get_millisecs_now() - start_test;
|
||||
}
|
||||
};
|
||||
|
||||
static int read_whole_file(const char *name,
|
||||
uint8_t **buf_ptr,
|
||||
size_t *buf_len) {
|
||||
main_file file;
|
||||
int ret;
|
||||
xoff_t len;
|
||||
size_t nread;
|
||||
main_file_init(&file);
|
||||
file.filename = name;
|
||||
ret = main_file_open(&file, name, XO_READ);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "open failed\n");
|
||||
goto exit;
|
||||
}
|
||||
ret = main_file_stat(&file, &len);
|
||||
if (ret != 0) {
|
||||
fprintf(stderr, "stat failed\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
(*buf_len) = (size_t)len;
|
||||
(*buf_ptr) = (uint8_t*) main_malloc(*buf_len);
|
||||
ret = main_file_read(&file, *buf_ptr, *buf_len, &nread,
|
||||
"read failed");
|
||||
if (ret == 0 && *buf_len == nread) {
|
||||
ret = 0;
|
||||
} else {
|
||||
fprintf(stderr, "invalid read\n");
|
||||
ret = XD3_INTERNAL;
|
||||
}
|
||||
exit:
|
||||
main_file_cleanup(&file);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int i;
|
||||
uint8_t *buf = NULL;
|
||||
size_t buf_len = 0;
|
||||
int ret;
|
||||
|
||||
if (argc <= 1) {
|
||||
fprintf(stderr, "usage: %s file ...\n", argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// TODO: The xdelta3-hash.h code is identical now; add sameness test.
|
||||
// using rabin_karp<> template.
|
||||
#define TEST(T,Z,S,C) \
|
||||
test_result<large_cksum<T,Z,S,hhash<T>,C>> \
|
||||
_xck_ ## T ## _ ## Z ## _ ## S ## _ ## C \
|
||||
("xck_" #T "_" #Z "_" #S "_" #C); \
|
||||
test_result<large_cksum_old<T,Z,S,hhash<T>,C>> \
|
||||
_old_ ## T ## _ ## Z ## _ ## S ## _ ## C \
|
||||
("old_" #T "_" #Z "_" #S "_" #C)
|
||||
|
||||
#define TESTS(SIZE, SKIP) \
|
||||
TEST(usize_t, SIZE, SKIP, 1); \
|
||||
TEST(usize_t, SIZE, SKIP, 2)
|
||||
|
||||
TESTS(5, 1);
|
||||
TESTS(6, 1);
|
||||
TESTS(7, 1);
|
||||
TESTS(8, 1);
|
||||
TESTS(9, 1);
|
||||
TESTS(10, 1);
|
||||
TESTS(11, 1);
|
||||
TESTS(12, 1);
|
||||
TESTS(13, 1);
|
||||
TESTS(14, 1);
|
||||
TESTS(15, 1);
|
||||
TESTS(16, 1);
|
||||
TESTS(17, 1);
|
||||
TESTS(18, 1);
|
||||
TESTS(19, 1);
|
||||
TESTS(20, 1);
|
||||
TESTS(21, 1);
|
||||
TESTS(22, 1);
|
||||
TESTS(23, 1);
|
||||
TESTS(24, 1);
|
||||
TESTS(25, 1);
|
||||
TESTS(26, 1);
|
||||
TESTS(27, 1);
|
||||
TESTS(28, 1);
|
||||
TESTS(29, 1);
|
||||
TESTS(30, 1);
|
||||
TESTS(31, 1);
|
||||
TESTS(32, 1);
|
||||
TESTS(33, 1);
|
||||
TESTS(34, 1);
|
||||
TESTS(35, 1);
|
||||
TESTS(36, 1);
|
||||
TESTS(37, 1);
|
||||
TESTS(38, 1);
|
||||
TESTS(39, 1);
|
||||
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if ((ret = read_whole_file(argv[i],
|
||||
& buf,
|
||||
& buf_len))) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
fprintf(stderr, "file %s is %zu bytes\n",
|
||||
argv[i], buf_len);
|
||||
|
||||
double min_time = -1.0;
|
||||
double min_compression = 0.0;
|
||||
|
||||
for (vector<test_result_base*>::iterator iter = all_tests.begin();
|
||||
iter != all_tests.end(); ++iter) {
|
||||
test_result_base *test = *iter;
|
||||
test->reset();
|
||||
|
||||
usize_t iters = 1;
|
||||
long start_test = get_millisecs_now();
|
||||
|
||||
do {
|
||||
test->get(buf, buf_len, iters);
|
||||
iters *= 3;
|
||||
iters /= 2;
|
||||
} while (get_millisecs_now() - start_test < 2000);
|
||||
|
||||
test->stat();
|
||||
|
||||
if (min_time < 0.0) {
|
||||
min_compression = test->compression();
|
||||
min_time = test->time();
|
||||
}
|
||||
|
||||
if (min_time > test->time()) {
|
||||
min_time = test->time();
|
||||
}
|
||||
|
||||
if (min_compression > test->compression()) {
|
||||
min_compression = test->compression();
|
||||
}
|
||||
|
||||
test->print();
|
||||
}
|
||||
|
||||
main_free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
189
lib/xdelta3/testing/checksum_test_c.c
Normal file
189
lib/xdelta3/testing/checksum_test_c.c
Normal file
|
@ -0,0 +1,189 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
#include "../xdelta3.c"
|
||||
|
||||
// OLD CHECKSUM CODE
|
||||
|
||||
#define PERMUTE32(x) (__single_hash32[x])
|
||||
#define PERMUTE64(x) (__single_hash64[x])
|
||||
|
||||
const uint16_t __single_hash32[256] =
|
||||
{
|
||||
/* This hashes the input alphabet (Scheme SLIB pseudo-random). */
|
||||
0xbcd1, 0xbb65, 0x42c2, 0xdffe, 0x9666, 0x431b, 0x8504, 0xeb46,
|
||||
0x6379, 0xd460, 0xcf14, 0x53cf, 0xdb51, 0xdb08, 0x12c8, 0xf602,
|
||||
0xe766, 0x2394, 0x250d, 0xdcbb, 0xa678, 0x02af, 0xa5c6, 0x7ea6,
|
||||
0xb645, 0xcb4d, 0xc44b, 0xe5dc, 0x9fe6, 0x5b5c, 0x35f5, 0x701a,
|
||||
0x220f, 0x6c38, 0x1a56, 0x4ca3, 0xffc6, 0xb152, 0x8d61, 0x7a58,
|
||||
0x9025, 0x8b3d, 0xbf0f, 0x95a3, 0xe5f4, 0xc127, 0x3bed, 0x320b,
|
||||
0xb7f3, 0x6054, 0x333c, 0xd383, 0x8154, 0x5242, 0x4e0d, 0x0a94,
|
||||
0x7028, 0x8689, 0x3a22, 0x0980, 0x1847, 0xb0f1, 0x9b5c, 0x4176,
|
||||
0xb858, 0xd542, 0x1f6c, 0x2497, 0x6a5a, 0x9fa9, 0x8c5a, 0x7743,
|
||||
0xa8a9, 0x9a02, 0x4918, 0x438c, 0xc388, 0x9e2b, 0x4cad, 0x01b6,
|
||||
0xab19, 0xf777, 0x365f, 0x1eb2, 0x091e, 0x7bf8, 0x7a8e, 0x5227,
|
||||
0xeab1, 0x2074, 0x4523, 0xe781, 0x01a3, 0x163d, 0x3b2e, 0x287d,
|
||||
0x5e7f, 0xa063, 0xb134, 0x8fae, 0x5e8e, 0xb7b7, 0x4548, 0x1f5a,
|
||||
0xfa56, 0x7a24, 0x900f, 0x42dc, 0xcc69, 0x02a0, 0x0b22, 0xdb31,
|
||||
0x71fe, 0x0c7d, 0x1732, 0x1159, 0xcb09, 0xe1d2, 0x1351, 0x52e9,
|
||||
0xf536, 0x5a4f, 0xc316, 0x6bf9, 0x8994, 0xb774, 0x5f3e, 0xf6d6,
|
||||
0x3a61, 0xf82c, 0xcc22, 0x9d06, 0x299c, 0x09e5, 0x1eec, 0x514f,
|
||||
0x8d53, 0xa650, 0x5c6e, 0xc577, 0x7958, 0x71ac, 0x8916, 0x9b4f,
|
||||
0x2c09, 0x5211, 0xf6d8, 0xcaaa, 0xf7ef, 0x287f, 0x7a94, 0xab49,
|
||||
0xfa2c, 0x7222, 0xe457, 0xd71a, 0x00c3, 0x1a76, 0xe98c, 0xc037,
|
||||
0x8208, 0x5c2d, 0xdfda, 0xe5f5, 0x0b45, 0x15ce, 0x8a7e, 0xfcad,
|
||||
0xaa2d, 0x4b5c, 0xd42e, 0xb251, 0x907e, 0x9a47, 0xc9a6, 0xd93f,
|
||||
0x085e, 0x35ce, 0xa153, 0x7e7b, 0x9f0b, 0x25aa, 0x5d9f, 0xc04d,
|
||||
0x8a0e, 0x2875, 0x4a1c, 0x295f, 0x1393, 0xf760, 0x9178, 0x0f5b,
|
||||
0xfa7d, 0x83b4, 0x2082, 0x721d, 0x6462, 0x0368, 0x67e2, 0x8624,
|
||||
0x194d, 0x22f6, 0x78fb, 0x6791, 0xb238, 0xb332, 0x7276, 0xf272,
|
||||
0x47ec, 0x4504, 0xa961, 0x9fc8, 0x3fdc, 0xb413, 0x007a, 0x0806,
|
||||
0x7458, 0x95c6, 0xccaa, 0x18d6, 0xe2ae, 0x1b06, 0xf3f6, 0x5050,
|
||||
0xc8e8, 0xf4ac, 0xc04c, 0xf41c, 0x992f, 0xae44, 0x5f1b, 0x1113,
|
||||
0x1738, 0xd9a8, 0x19ea, 0x2d33, 0x9698, 0x2fe9, 0x323f, 0xcde2,
|
||||
0x6d71, 0xe37d, 0xb697, 0x2c4f, 0x4373, 0x9102, 0x075d, 0x8e25,
|
||||
0x1672, 0xec28, 0x6acb, 0x86cc, 0x186e, 0x9414, 0xd674, 0xd1a5
|
||||
};
|
||||
|
||||
const uint32_t __single_hash64[256] =
|
||||
{
|
||||
/* http://random.org 2014.10.24 */
|
||||
0xd25e9f0a, 0xb1af9d5e, 0xb753dfa2, 0x157050f7, /* 0 */
|
||||
0xc84b072c, 0xdd14fe7c, 0xf92208c3, 0xdf08a0c0,
|
||||
0x63a5c118, 0x76f5d90f, 0xa2f8b93e, 0xb6c12d22,
|
||||
0xaf074957, 0x966fb7d9, 0x62f7b785, 0xb40e8a09,
|
||||
0x0a811d5d, 0x323a6daa, 0xb62f7c5b, 0xfdcb9a53,
|
||||
0xf25a9067, 0x4506bc7a, 0xff58a74b, 0x5ae62817,
|
||||
0x74097675, 0x722c0fd9, 0x116a2a66, 0x65f76728,
|
||||
0x72c79651, 0xe043cf9d, 0x64b867c7, 0x6604834f,
|
||||
0xcdca58a6, 0x0f164e2d, 0x24515f05, 0x632cdbf8,
|
||||
0x18091d4a, 0x3eff4128, 0x673d1c33, 0xd8e10c71,
|
||||
0x1a3edf11, 0xba52892f, 0xa56949e0, 0xf3e1dd77, /* 10 */
|
||||
0x86fcbe3e, 0x138d66d0, 0x4fc98359, 0xc22e5dd6,
|
||||
0xc59f2267, 0x6c6dd739, 0xe03da190, 0x07e8469c,
|
||||
0xadcfb02c, 0x00d3b0d9, 0xa1f44918, 0x8bd84d87,
|
||||
0x08ec9ec1, 0xbbcd156f, 0xb57718e3, 0x3177e752,
|
||||
0xf52a4d70, 0xde7aaad9, 0x075f1da0, 0x21ba00c6,
|
||||
0xb9469a5c, 0xcf08d5ba, 0x91ac9edc, 0xc6167b63,
|
||||
0xc1974919, 0xc8c8d195, 0x4b1996dd, 0xeff8991c,
|
||||
0xf7f66c6b, 0x25b012e2, 0x59d12a98, 0xea40d3cc,
|
||||
0x41f9970b, 0xec48101a, 0xa3bdcf90, 0x99f16905,
|
||||
0x27af6c97, 0xc849af37, 0x49cad89b, 0xf48c2278, /* 20 */
|
||||
0x5529c3d8, 0x9e7d6dce, 0x16feb52d, 0xf1b0aca1,
|
||||
0xaf28fccb, 0x48e4ce3c, 0xc4436617, 0x64524e3e,
|
||||
0x61806681, 0x6384f2d7, 0x1172880f, 0x34a5ef5f,
|
||||
0xcc8cc0a8, 0x66e8f100, 0x2866085f, 0xba9b1b2d,
|
||||
0x51285949, 0x2be4b574, 0x889b1ef5, 0x3dbe920d,
|
||||
0x9277a62f, 0x0584a9f6, 0x085d8fc4, 0x4b5d403d,
|
||||
0x4e46ca78, 0x3294c2f9, 0x29313e70, 0xe4f09b24,
|
||||
0xe73b331c, 0x072f5552, 0x2e390b78, 0xea0021ca,
|
||||
0xd8f40320, 0xed0e16fd, 0x7de9cf7a, 0xf17e3d6c,
|
||||
0x8df1bd85, 0x052cae67, 0x3486e512, 0x3a1c09b8, /* 30 */
|
||||
0x6c2a7b4e, 0x83455753, 0xbc0353ac, 0x0ffe20b6,
|
||||
0x5fdcef85, 0x010f506c, 0x595ce972, 0xe28680d0,
|
||||
0xa7e216b2, 0xa392ee0f, 0x25b73faa, 0x2b1f4983,
|
||||
0xeeaefe98, 0x1d3d9cbc, 0x6aebe97b, 0x8b7b3584,
|
||||
0x9e6a9a07, 0xd37f1e99, 0x4ac2a441, 0x8ae9a213,
|
||||
0x7d0e27d7, 0x5de54b9a, 0x8621de1f, 0xf0f2f866,
|
||||
0xcb08d275, 0x49c3f87e, 0xd5ee68c1, 0x9802fc77,
|
||||
0x68be6c5e, 0x65aa8c27, 0xf423d5f7, 0x10ec5502,
|
||||
0x9909bce1, 0x509cdf1b, 0x338fea72, 0x2733e9bf,
|
||||
0xf92f4fd7, 0x87738ea2, 0x931a8bbc, 0x0a5c9155, /* 40 */
|
||||
0xbe5edd9b, 0xadbf5838, 0x0338f8d2, 0x290da210,
|
||||
0x390c37d8, 0xe7cffae8, 0x20617ebe, 0x464322dd,
|
||||
0x7b3c4e78, 0xac142dcb, 0x2d5cef76, 0xd8fe49fc,
|
||||
0x60f4e9a9, 0x7473816f, 0x0dc35f39, 0x5eed80c1,
|
||||
0x0cb55ab6, 0x1d3ac541, 0x13c7f529, 0x7bffdf4a,
|
||||
0xe334785b, 0x85263ec1, 0xd132ae56, 0x7c868b9e,
|
||||
0x47f60638, 0x1012b979, 0x81c31dd3, 0x1af868c8,
|
||||
0x0c5d0742, 0xd1b3e1a2, 0x5873200a, 0xf848465c,
|
||||
0x0fc4d596, 0x609c18af, 0xc9f5a480, 0xd1a94a84,
|
||||
0xa1431a3f, 0x7de8bb1a, 0x25f1256b, 0x1dcc732c, /* 50 */
|
||||
0x6aa1549a, 0xa2367281, 0x32f2a77e, 0x82e62a0f,
|
||||
0x045cbb56, 0x74b2027c, 0xd71a32d9, 0x022e7cb5,
|
||||
0xe99be177, 0x60222fdf, 0xd69681ca, 0x9008ee2c,
|
||||
0x32923db4, 0xcf82bf97, 0x38960a5b, 0xb3503d5b,
|
||||
0x9bd4c7f2, 0x33c029c8, 0x1ef504a3, 0xdb249d3b,
|
||||
0x91e89676, 0x4ca43b36, 0x9191433c, 0x465d5dc4,
|
||||
0xf4dcb118, 0x9d11dd00, 0xb592f058, 0xdbe5ce30,
|
||||
0x74790d92, 0x779850a8, 0x7180d25b, 0xfa951d99,
|
||||
0x5990935a, 0x921cb022, 0x3b7c39bc, 0x6a38a7c7,
|
||||
0xdc22703b, 0x142bab3b, 0x4e3d9479, 0x44bb8482, /* 60 */
|
||||
0x8043abce, 0xfebe832a, 0x8e6a2f98, 0x4d43c4fe,
|
||||
0xd192a70a, 0x802f3c3a, 0x5d11bbab, 0x2665d241,
|
||||
0xb3f3a680, 0x3a8d223f, 0xcf82cdb4, 0x4ed28743,
|
||||
};
|
||||
|
||||
uint64_t
|
||||
xd3_large64_cksum_old (xd3_hash_cfg *ignore, const uint8_t *base, const usize_t look)
|
||||
{
|
||||
static const uint64_t kBits = 32;
|
||||
static const uint64_t kMask = 0xffffffff;
|
||||
usize_t i = 0;
|
||||
uint64_t low = 0;
|
||||
uint64_t high = 0;
|
||||
|
||||
for (; i < look; i += 1)
|
||||
{
|
||||
low += PERMUTE64(*base++);
|
||||
high += low;
|
||||
}
|
||||
|
||||
return ((high & kMask) << kBits) | (low & kMask);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
xd3_large64_cksum_update_old (xd3_hash_cfg *ignore, const uint64_t cksum,
|
||||
const uint8_t *base, const usize_t look)
|
||||
{
|
||||
static const uint64_t kBits = 32;
|
||||
static const uint64_t kMask = 0xffffffff;
|
||||
uint64_t old_c = PERMUTE64(base[0]);
|
||||
uint64_t new_c = PERMUTE64(base[look]);
|
||||
uint64_t low = ((cksum & kMask) - old_c + new_c) & kMask;
|
||||
uint64_t high = ((cksum >> kBits) - (old_c * look) + low) & kMask;
|
||||
return (high << kBits) | low;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
xd3_large32_cksum_old (xd3_hash_cfg *ignore, const uint8_t *base, const usize_t look)
|
||||
{
|
||||
static const uint32_t kBits = 16;
|
||||
static const uint32_t kMask = 0xffff;
|
||||
usize_t i = 0;
|
||||
uint32_t low = 0;
|
||||
uint32_t high = 0;
|
||||
|
||||
for (; i < look; i += 1)
|
||||
{
|
||||
low += PERMUTE32(*base++);
|
||||
high += low;
|
||||
}
|
||||
|
||||
return ((high & kMask) << kBits) | (low & kMask);
|
||||
}
|
||||
|
||||
uint32_t
|
||||
xd3_large32_cksum_update_old (xd3_hash_cfg *ignore, const uint32_t cksum,
|
||||
const uint8_t *base, const usize_t look)
|
||||
{
|
||||
static const uint32_t kBits = 16;
|
||||
static const uint32_t kMask = 0xffff;
|
||||
uint32_t old_c = PERMUTE32(base[0]);
|
||||
uint32_t new_c = PERMUTE32(base[look]);
|
||||
uint32_t low = ((cksum & kMask) - old_c + new_c) & kMask;
|
||||
uint32_t high = ((cksum >> kBits) - (old_c * look) + low) & kMask;
|
||||
return (high << kBits) | low;
|
||||
}
|
67
lib/xdelta3/testing/cmp.h
Normal file
67
lib/xdelta3/testing/cmp.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
static size_t CmpDifferentBlockBytes(const Block &a, const Block &b) {
|
||||
size_t total = 0;
|
||||
size_t i = 0;
|
||||
size_t m = min(a.Size(), b.Size());
|
||||
|
||||
for (; i < m; i++) {
|
||||
if (a[i] != b[i]) {
|
||||
total++;
|
||||
}
|
||||
}
|
||||
|
||||
total += a.Size() - i;
|
||||
total += b.Size() - i;
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static xoff_t CmpDifferentBytes(const FileSpec &a, const FileSpec &b) {
|
||||
Block block_a, block_b;
|
||||
xoff_t total = 0;
|
||||
typename FileSpec::iterator a_i(a), b_i(b);
|
||||
|
||||
for (; !a_i.Done() && !b_i.Done(); a_i.Next(), b_i.Next()) {
|
||||
|
||||
a_i.Get(&block_a);
|
||||
b_i.Get(&block_b);
|
||||
|
||||
total += CmpDifferentBlockBytes(block_a, block_b);
|
||||
}
|
||||
|
||||
for (; !a_i.Done(); a_i.Next()) {
|
||||
total += a_i.BytesOnBlock();
|
||||
}
|
||||
for (; !b_i.Done(); b_i.Next()) {
|
||||
total += b_i.BytesOnBlock();
|
||||
}
|
||||
|
||||
return total;
|
||||
}
|
||||
|
||||
static size_t CmpDifferentBlockBytesAtOffset(const Block &a,
|
||||
const FileSpec &b_spec,
|
||||
xoff_t offset) {
|
||||
Block b;
|
||||
size_t size = a.Size();
|
||||
CHECK_LE(offset, b_spec.Size());
|
||||
if (b_spec.Size() < offset + size) {
|
||||
size = b_spec.Size() - offset;
|
||||
}
|
||||
b_spec.Get(&b, offset, size);
|
||||
return CmpDifferentBlockBytes(a, b);
|
||||
}
|
87
lib/xdelta3/testing/delta.h
Normal file
87
lib/xdelta3/testing/delta.h
Normal file
|
@ -0,0 +1,87 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
class Delta {
|
||||
public:
|
||||
Delta(const Block &block) {
|
||||
int ret;
|
||||
xd3_config config;
|
||||
memset(&stream_, 0, sizeof (stream_));
|
||||
memset(&config, 0, sizeof (config));
|
||||
|
||||
xd3_init_config(&config, XD3_SKIP_EMIT | XD3_ADLER32_NOVER);
|
||||
|
||||
CHECK_EQ(0, xd3_config_stream (&stream_, &config));
|
||||
|
||||
xd3_avail_input (&stream_, block.Data(), block.Size());
|
||||
|
||||
bool done = false;
|
||||
while (!done) {
|
||||
ret = xd3_decode_input(&stream_);
|
||||
|
||||
switch (ret) {
|
||||
case XD3_INPUT:
|
||||
done = true;
|
||||
break;
|
||||
case XD3_OUTPUT:
|
||||
CHECK_EQ(0, xd3_whole_append_window (&stream_));
|
||||
break;
|
||||
case XD3_GOTHEADER:
|
||||
case XD3_WINSTART:
|
||||
case XD3_WINFINISH:
|
||||
break;
|
||||
default:
|
||||
cerr << "decode: " << done;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~Delta() {
|
||||
xd3_free_stream(&stream_);
|
||||
}
|
||||
|
||||
xoff_t AddedBytes() const {
|
||||
return stream_.whole_target.addslen;
|
||||
}
|
||||
|
||||
xoff_t Windows() const {
|
||||
return stream_.whole_target.wininfolen;
|
||||
}
|
||||
|
||||
// Note: This does not benefit from -Wformat= checking, due to the
|
||||
// enclosing template. Further, it was not used.
|
||||
// void Print() const {
|
||||
// for (size_t i = 0; i < stream_.whole_target.instlen; i++) {
|
||||
// xd3_winst &winst = stream_.whole_target.inst[i];
|
||||
// switch (winst.type) {
|
||||
// case XD3_RUN:
|
||||
// DP(RINT, "%" Q "u run %" W "u\n", winst.position, winst.size);
|
||||
// break;
|
||||
// case XD3_ADD:
|
||||
// DP(RINT "%" Q "u add %" W "u\n", winst.position, winst.size);
|
||||
// break;
|
||||
// default:
|
||||
// DP(RINT "%" Q "u copy %" W "u @ %" Q "u (mode %u)\n",
|
||||
// winst.position, winst.size, winst.addr, winst.mode);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
private:
|
||||
xd3_stream stream_;
|
||||
};
|
399
lib/xdelta3/testing/file.h
Normal file
399
lib/xdelta3/testing/file.h
Normal file
|
@ -0,0 +1,399 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
class Block;
|
||||
class BlockIterator;
|
||||
class TmpFile;
|
||||
|
||||
class Block {
|
||||
public:
|
||||
Block()
|
||||
: data_(NULL),
|
||||
data_size_(0),
|
||||
size_(0) { }
|
||||
|
||||
~Block() {
|
||||
if (data_) {
|
||||
delete [] data_;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
uint8_t operator[](size_t i) const {
|
||||
CHECK_LT(i, size_);
|
||||
return data_[i];
|
||||
}
|
||||
|
||||
uint8_t* Data() const {
|
||||
if (data_ == NULL) {
|
||||
CHECK_EQ(0, size_);
|
||||
data_size_ = 1;
|
||||
data_ = new uint8_t[1];
|
||||
}
|
||||
return data_;
|
||||
}
|
||||
|
||||
// For writing to blocks
|
||||
void Append(const uint8_t *data, size_t size) {
|
||||
if (data_ == NULL) {
|
||||
CHECK_EQ(0, size_);
|
||||
CHECK_EQ(0, data_size_);
|
||||
data_ = new uint8_t[Constants::BLOCK_SIZE];
|
||||
data_size_ = Constants::BLOCK_SIZE;
|
||||
}
|
||||
|
||||
if (size_ + size > data_size_) {
|
||||
uint8_t *tmp = data_;
|
||||
while (size_ + size > data_size_) {
|
||||
data_size_ *= 2;
|
||||
}
|
||||
data_ = new uint8_t[data_size_];
|
||||
memcpy(data_, tmp, size_);
|
||||
delete [] tmp;
|
||||
}
|
||||
|
||||
memcpy(data_ + size_, data, size);
|
||||
size_ += size;
|
||||
}
|
||||
|
||||
// For cleaing a block
|
||||
void Reset() {
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
// Note: This does not benefit from -Wformat= checking, due to the
|
||||
// enclosing template. Further, it was not used.
|
||||
// void Print() const {
|
||||
// xoff_t pos = 0;
|
||||
// for (size_t i = 0; i < Size(); i++) {
|
||||
// if (pos % 16 == 0) {
|
||||
// DP(RINT "%5" Q "x: ", pos);
|
||||
// }
|
||||
// DP(RINT "%02x ", (*this)[i]);
|
||||
// if (pos % 16 == 15) {
|
||||
// DP(RINT "\n");
|
||||
// }
|
||||
// pos++;
|
||||
// }
|
||||
// DP(RINT "\n");
|
||||
// }
|
||||
|
||||
void WriteTmpFile(TmpFile *f) const {
|
||||
f->Append(this);
|
||||
}
|
||||
|
||||
void SetSize(size_t size) {
|
||||
uint8_t *t = NULL;
|
||||
if (data_size_ < size) {
|
||||
if (data_) {
|
||||
t = data_;
|
||||
}
|
||||
data_ = new uint8_t[size];
|
||||
data_size_ = size;
|
||||
}
|
||||
if (t && size < size_) {
|
||||
memcpy(data_, t, size);
|
||||
}
|
||||
delete [] t;
|
||||
size_ = size;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class BlockIterator;
|
||||
|
||||
mutable uint8_t *data_;
|
||||
mutable size_t data_size_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
class FileSpec {
|
||||
public:
|
||||
FileSpec(MTRandom *rand)
|
||||
: rand_(rand) {
|
||||
}
|
||||
|
||||
// Generates a file with a known size
|
||||
void GenerateFixedSize(xoff_t size) {
|
||||
Reset();
|
||||
|
||||
for (xoff_t p = 0; p < size; ) {
|
||||
xoff_t t = min(Constants::BLOCK_SIZE, size - p);
|
||||
table_.insert(make_pair(p, Segment(t, rand_)));
|
||||
p += t;
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a file with exponential-random distributed size
|
||||
void GenerateRandomSize(xoff_t mean) {
|
||||
GenerateFixedSize(rand_->ExpRand(mean));
|
||||
}
|
||||
|
||||
// Returns the size of the file
|
||||
xoff_t Size() const {
|
||||
if (table_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
ConstSegmentMapIterator i = --table_.end();
|
||||
return i->first + i->second.Size();
|
||||
}
|
||||
|
||||
// Returns the number of blocks
|
||||
xoff_t Blocks(size_t blksize = Constants::BLOCK_SIZE) const {
|
||||
if (table_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
return ((Size() - 1) / blksize) + 1;
|
||||
}
|
||||
|
||||
// Returns the number of segments
|
||||
xoff_t Segments() const {
|
||||
return table_.size();
|
||||
}
|
||||
|
||||
// Create a mutation according to "what".
|
||||
void ModifyTo(const Mutator &mutator,
|
||||
FileSpec *modify) const {
|
||||
modify->Reset();
|
||||
mutator.Mutate(&modify->table_, &table_, rand_);
|
||||
modify->CheckSegments();
|
||||
}
|
||||
|
||||
void CheckSegments() const {
|
||||
for (ConstSegmentMapIterator iter(table_.begin());
|
||||
iter != table_.end(); ) {
|
||||
ConstSegmentMapIterator iter0(iter++);
|
||||
if (iter == table_.end()) {
|
||||
break;
|
||||
}
|
||||
CHECK_EQ(iter0->first + iter0->second.Size(), iter->first);
|
||||
}
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
table_.clear();
|
||||
}
|
||||
|
||||
void Print() const {
|
||||
for (ConstSegmentMapIterator iter(table_.begin());
|
||||
iter != table_.end();
|
||||
++iter) {
|
||||
const Segment &seg = iter->second;
|
||||
cerr << "Segment at " << iter->first
|
||||
<< " (" << seg.ToString() << ")" << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void PrintData() const {
|
||||
Block block;
|
||||
for (BlockIterator iter(*this); !iter.Done(); iter.Next()) {
|
||||
iter.Get(&block);
|
||||
block.Print();
|
||||
}
|
||||
}
|
||||
|
||||
void WriteTmpFile(TmpFile *f) const {
|
||||
Block block;
|
||||
for (BlockIterator iter(*this); !iter.Done(); iter.Next()) {
|
||||
iter.Get(&block);
|
||||
f->Append(&block);
|
||||
}
|
||||
}
|
||||
|
||||
void Get(Block *block, xoff_t offset, size_t size) const {
|
||||
size_t got = 0;
|
||||
block->SetSize(size);
|
||||
|
||||
ConstSegmentMapIterator pos = table_.upper_bound(offset);
|
||||
if (pos == table_.begin()) {
|
||||
CHECK_EQ(0, Size());
|
||||
return;
|
||||
}
|
||||
--pos;
|
||||
|
||||
while (got < size) {
|
||||
CHECK(pos != table_.end());
|
||||
CHECK_GE(offset, pos->first);
|
||||
|
||||
const Segment &seg = pos->second;
|
||||
|
||||
// The position of this segment may start before this block starts,
|
||||
// and then the position of the data may be offset from the seeding
|
||||
// position.
|
||||
size_t seg_offset = offset - pos->first;
|
||||
size_t advance = min(seg.Size() - seg_offset,
|
||||
size - got);
|
||||
|
||||
seg.Fill(seg_offset, advance, block->Data() + got);
|
||||
|
||||
got += advance;
|
||||
offset += advance;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
typedef BlockIterator iterator;
|
||||
|
||||
private:
|
||||
friend class BlockIterator;
|
||||
|
||||
MTRandom *rand_;
|
||||
SegmentMap table_;
|
||||
};
|
||||
|
||||
class BlockIterator {
|
||||
public:
|
||||
explicit BlockIterator(const FileSpec& spec)
|
||||
: spec_(spec),
|
||||
blkno_(0),
|
||||
blksize_(Constants::BLOCK_SIZE) { }
|
||||
|
||||
BlockIterator(const FileSpec& spec,
|
||||
size_t blksize)
|
||||
: spec_(spec),
|
||||
blkno_(0),
|
||||
blksize_(blksize) { }
|
||||
|
||||
bool Done() const {
|
||||
return blkno_ >= spec_.Blocks(blksize_);
|
||||
}
|
||||
|
||||
void Next() {
|
||||
blkno_++;
|
||||
}
|
||||
|
||||
xoff_t Blkno() const {
|
||||
return blkno_;
|
||||
}
|
||||
|
||||
xoff_t Blocks() const {
|
||||
return spec_.Blocks(blksize_);
|
||||
}
|
||||
|
||||
xoff_t Offset() const {
|
||||
return blkno_ * blksize_;
|
||||
}
|
||||
|
||||
void SetBlock(xoff_t blkno) {
|
||||
CHECK_LE(blkno, Blocks());
|
||||
blkno_ = blkno;
|
||||
}
|
||||
|
||||
void Get(Block *block) const {
|
||||
spec_.Get(block, blkno_ * blksize_, BytesOnBlock());
|
||||
}
|
||||
|
||||
size_t BytesOnBlock() const {
|
||||
xoff_t blocks = spec_.Blocks(blksize_);
|
||||
xoff_t size = spec_.Size();
|
||||
|
||||
DCHECK((blkno_ < blocks) ||
|
||||
(blkno_ == blocks && size % blksize_ == 0));
|
||||
|
||||
if (blkno_ == blocks) {
|
||||
return 0;
|
||||
}
|
||||
if (blkno_ + 1 == blocks) {
|
||||
return ((size - 1) % blksize_) + 1;
|
||||
}
|
||||
return blksize_;
|
||||
}
|
||||
|
||||
size_t BlockSize() const {
|
||||
return blksize_;
|
||||
}
|
||||
|
||||
private:
|
||||
const FileSpec& spec_;
|
||||
xoff_t blkno_;
|
||||
size_t blksize_;
|
||||
};
|
||||
|
||||
class ExtFile {
|
||||
public:
|
||||
ExtFile() {
|
||||
static int static_counter = 0;
|
||||
pid_t pid = getpid();
|
||||
char buf[64];
|
||||
xoff_t xpid = pid;
|
||||
snprintf(buf, 64, "/tmp/regtest.%" Q "u.%d", xpid, static_counter++);
|
||||
filename_.append(buf);
|
||||
unlink(filename_.c_str());
|
||||
}
|
||||
|
||||
~ExtFile() {
|
||||
unlink(filename_.c_str());
|
||||
}
|
||||
|
||||
const char* Name() const {
|
||||
return filename_.c_str();
|
||||
}
|
||||
|
||||
// Check whether a real file matches a file spec.
|
||||
bool EqualsSpec(const FileSpec &spec) const {
|
||||
main_file t;
|
||||
main_file_init(&t);
|
||||
CHECK_EQ(0, main_file_open(&t, Name(), XO_READ));
|
||||
|
||||
Block tblock;
|
||||
Block sblock;
|
||||
for (BlockIterator iter(spec); !iter.Done(); iter.Next()) {
|
||||
iter.Get(&sblock);
|
||||
tblock.SetSize(sblock.Size());
|
||||
size_t tread;
|
||||
CHECK_EQ(0, main_file_read(&t,
|
||||
tblock.Data(),
|
||||
tblock.Size(), &tread, "read failed"));
|
||||
CHECK_EQ(0, CmpDifferentBlockBytes(tblock, sblock));
|
||||
}
|
||||
|
||||
CHECK_EQ(0, main_file_close(&t));
|
||||
main_file_cleanup(&t);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
string filename_;
|
||||
};
|
||||
|
||||
class TmpFile : public ExtFile {
|
||||
public:
|
||||
TmpFile() {
|
||||
main_file_init(&file_);
|
||||
CHECK_EQ(0, main_file_open(&file_, Name(), XO_WRITE));
|
||||
}
|
||||
|
||||
~TmpFile() {
|
||||
main_file_cleanup(&file_);
|
||||
}
|
||||
|
||||
void Append(const Block *block) {
|
||||
CHECK_EQ(0, main_file_write(&file_,
|
||||
block->Data(), block->Size(),
|
||||
"tmpfile write failed"));
|
||||
}
|
||||
|
||||
const char* Name() const {
|
||||
if (main_file_isopen(&file_)) {
|
||||
CHECK_EQ(0, main_file_close(&file_));
|
||||
}
|
||||
return ExtFile::Name();
|
||||
}
|
||||
|
||||
private:
|
||||
mutable main_file file_;
|
||||
};
|
400
lib/xdelta3/testing/modify.h
Normal file
400
lib/xdelta3/testing/modify.h
Normal file
|
@ -0,0 +1,400 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
class Mutator {
|
||||
public:
|
||||
virtual ~Mutator() { }
|
||||
virtual void Mutate(SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) const = 0;
|
||||
};
|
||||
|
||||
class Change {
|
||||
public:
|
||||
enum Kind {
|
||||
MODIFY = 1, // Mutate a certain range w/ random or supplied data
|
||||
ADD = 2, // Insert random or supplied data
|
||||
DELRANGE = 3, // Delete a specified range of data
|
||||
COPY = 4, // Copy from one region, inserting elsewhere
|
||||
MOVE = 5, // Copy then delete copied-from range
|
||||
COPYOVER = 6 // Copy then delete copied-to range
|
||||
|
||||
// ADD, DELRANGE, and COPY change the file size
|
||||
// MODIFY, MOVE, COPYOVER preserve the file size
|
||||
};
|
||||
|
||||
// Constructor for modify, add, delete.
|
||||
Change(Kind kind0, xoff_t size0, xoff_t addr1_0)
|
||||
: kind(kind0),
|
||||
size(size0),
|
||||
addr1(addr1_0),
|
||||
addr2(0),
|
||||
insert(NULL) {
|
||||
CHECK(kind != MOVE && kind != COPY && kind != COPYOVER);
|
||||
}
|
||||
|
||||
// Constructor for modify, add w/ provided data.
|
||||
Change(Kind kind0, xoff_t size0, xoff_t addr1_0, Segment *insert0)
|
||||
: kind(kind0),
|
||||
size(size0),
|
||||
addr1(addr1_0),
|
||||
addr2(0),
|
||||
insert(insert0) {
|
||||
CHECK(kind != MOVE && kind != COPY && kind != COPYOVER);
|
||||
}
|
||||
|
||||
// Constructor for move, copy, overwrite
|
||||
Change(Kind kind0, xoff_t size0, xoff_t addr1_0, xoff_t addr2_0)
|
||||
: kind(kind0),
|
||||
size(size0),
|
||||
addr1(addr1_0),
|
||||
addr2(addr2_0),
|
||||
insert(NULL) {
|
||||
CHECK(kind == MOVE || kind == COPY || kind == COPYOVER);
|
||||
}
|
||||
|
||||
Kind kind;
|
||||
xoff_t size;
|
||||
xoff_t addr1;
|
||||
xoff_t addr2;
|
||||
Segment *insert; // For modify and/or add
|
||||
};
|
||||
|
||||
typedef list<Change> ChangeList;
|
||||
typedef typename ChangeList::const_iterator ConstChangeListIterator;
|
||||
typedef typename ChangeList::iterator ChangeListIterator;
|
||||
|
||||
class ChangeListMutator : public Mutator {
|
||||
public:
|
||||
ChangeListMutator(const ChangeList &cl)
|
||||
: cl_(cl) { }
|
||||
|
||||
ChangeListMutator() { }
|
||||
|
||||
void Mutate(SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) const {
|
||||
// The speed of processing gigabytes of data is so slow compared with
|
||||
// these table-copy operations, no attempt to make this fast.
|
||||
SegmentMap tmp;
|
||||
|
||||
for (ConstChangeListIterator iter(cl_.begin());
|
||||
iter != cl_.end(); ++iter) {
|
||||
const Change &ch = *iter;
|
||||
tmp.clear();
|
||||
Mutate(ch, &tmp, source_table, rand);
|
||||
tmp.swap(*table);
|
||||
source_table = table;
|
||||
}
|
||||
}
|
||||
|
||||
static void Mutate(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
switch (ch.kind) {
|
||||
case Change::ADD:
|
||||
AddChange(ch, table, source_table, rand);
|
||||
break;
|
||||
case Change::MODIFY:
|
||||
ModifyChange(ch, table, source_table, rand);
|
||||
break;
|
||||
case Change::DELRANGE:
|
||||
DeleteChange(ch, table, source_table, rand);
|
||||
break;
|
||||
case Change::COPY:
|
||||
CopyChange(ch, table, source_table, rand);
|
||||
break;
|
||||
case Change::MOVE:
|
||||
MoveChange(ch, table, source_table, rand);
|
||||
break;
|
||||
case Change::COPYOVER:
|
||||
OverwriteChange(ch, table, source_table, rand);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void ModifyChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
xoff_t m_start = ch.addr1;
|
||||
xoff_t m_end = m_start + ch.size;
|
||||
xoff_t i_start = 0;
|
||||
xoff_t i_end = 0;
|
||||
|
||||
for (ConstSegmentMapIterator iter(source_table->begin());
|
||||
iter != source_table->end();
|
||||
++iter) {
|
||||
const Segment &seg = iter->second;
|
||||
i_start = iter->first;
|
||||
i_end = i_start + seg.Size();
|
||||
|
||||
if (i_end <= m_start || i_start >= m_end) {
|
||||
table->insert(table->end(), make_pair(i_start, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start < m_start) {
|
||||
table->insert(table->end(),
|
||||
make_pair(i_start,
|
||||
seg.Subseg(0, m_start - i_start)));
|
||||
}
|
||||
|
||||
// Insert the entire segment, even though it may extend into later
|
||||
// segments. This condition avoids inserting it during later
|
||||
// segments.
|
||||
if (m_start >= i_start) {
|
||||
if (ch.insert != NULL) {
|
||||
table->insert(table->end(), make_pair(m_start, *ch.insert));
|
||||
} else {
|
||||
Segment part(m_end - m_start, rand);
|
||||
table->insert(table->end(), make_pair(m_start, part));
|
||||
}
|
||||
}
|
||||
|
||||
if (i_end > m_end) {
|
||||
table->insert(table->end(),
|
||||
make_pair(m_end,
|
||||
seg.Subseg(m_end - i_start, i_end - m_end)));
|
||||
}
|
||||
}
|
||||
|
||||
// This check verifies that the modify does not extend past the
|
||||
// source_table EOF.
|
||||
CHECK_LE(m_end, i_end);
|
||||
}
|
||||
|
||||
static void AddChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
xoff_t m_start = ch.addr1;
|
||||
xoff_t i_start = 0;
|
||||
xoff_t i_end = 0;
|
||||
|
||||
for (ConstSegmentMapIterator iter(source_table->begin());
|
||||
iter != source_table->end();
|
||||
++iter) {
|
||||
const Segment &seg = iter->second;
|
||||
i_start = iter->first;
|
||||
i_end = i_start + seg.Size();
|
||||
|
||||
if (i_end <= m_start) {
|
||||
table->insert(table->end(), make_pair(i_start, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start > m_start) {
|
||||
table->insert(table->end(), make_pair(i_start + ch.size, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start < m_start) {
|
||||
table->insert(table->end(),
|
||||
make_pair(i_start,
|
||||
seg.Subseg(0, m_start - i_start)));
|
||||
}
|
||||
|
||||
if (ch.insert != NULL) {
|
||||
table->insert(table->end(), make_pair(m_start, *ch.insert));
|
||||
} else {
|
||||
Segment addseg(ch.size, rand);
|
||||
table->insert(table->end(), make_pair(m_start, addseg));
|
||||
}
|
||||
|
||||
if (m_start < i_end) {
|
||||
table->insert(table->end(),
|
||||
make_pair(m_start + ch.size,
|
||||
seg.Subseg(m_start - i_start,
|
||||
i_end - m_start)));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_LE(m_start, i_end);
|
||||
|
||||
// Special case for add at end-of-input.
|
||||
if (m_start == i_end) {
|
||||
Segment addseg(ch.size, rand);
|
||||
table->insert(table->end(), make_pair(m_start, addseg));
|
||||
}
|
||||
}
|
||||
|
||||
static void DeleteChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
xoff_t m_start = ch.addr1;
|
||||
xoff_t m_end = m_start + ch.size;
|
||||
xoff_t i_start = 0;
|
||||
xoff_t i_end = 0;
|
||||
|
||||
for (ConstSegmentMapIterator iter(source_table->begin());
|
||||
iter != source_table->end();
|
||||
++iter) {
|
||||
const Segment &seg = iter->second;
|
||||
i_start = iter->first;
|
||||
i_end = i_start + seg.Size();
|
||||
|
||||
if (i_end <= m_start) {
|
||||
table->insert(table->end(), make_pair(i_start, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start >= m_end) {
|
||||
table->insert(table->end(), make_pair(i_start - ch.size, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start < m_start) {
|
||||
table->insert(table->end(),
|
||||
make_pair(i_start,
|
||||
seg.Subseg(0, m_start - i_start)));
|
||||
}
|
||||
|
||||
if (i_end > m_end) {
|
||||
table->insert(table->end(),
|
||||
make_pair(m_end - ch.size,
|
||||
seg.Subseg(m_end - i_start, i_end - m_end)));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_LT(m_start, i_end);
|
||||
CHECK_LE(m_end, i_end);
|
||||
}
|
||||
|
||||
// A move is a copy followed by delete of the copied-from range.
|
||||
static void MoveChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
SegmentMap tmp;
|
||||
CHECK_NE(ch.addr1, ch.addr2);
|
||||
CopyChange(ch, &tmp, source_table, rand);
|
||||
Change d(Change::DELRANGE, ch.size,
|
||||
ch.addr1 < ch.addr2 ? ch.addr1 : ch.addr1 + ch.size);
|
||||
DeleteChange(d, table, &tmp, rand);
|
||||
}
|
||||
|
||||
// An overwrite is a copy followed by a delete of the copied-to range.
|
||||
static void OverwriteChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) {
|
||||
SegmentMap tmp;
|
||||
CHECK_NE(ch.addr1, ch.addr2);
|
||||
CopyChange(ch, &tmp, source_table, rand);
|
||||
Change d(Change::DELRANGE, ch.size, ch.addr2 + ch.size);
|
||||
DeleteChange(d, table, &tmp, rand);
|
||||
}
|
||||
|
||||
static void CopyChange(const Change &ch,
|
||||
SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *ignore) {
|
||||
xoff_t m_start = ch.addr2;
|
||||
xoff_t c_start = ch.addr1;
|
||||
xoff_t i_start = 0;
|
||||
xoff_t i_end = 0;
|
||||
|
||||
// Like AddChange() with AppendCopy instead of a random segment.
|
||||
for (ConstSegmentMapIterator iter(source_table->begin());
|
||||
iter != source_table->end();
|
||||
++iter) {
|
||||
const Segment &seg = iter->second;
|
||||
i_start = iter->first;
|
||||
i_end = i_start + seg.Size();
|
||||
|
||||
if (i_end <= m_start) {
|
||||
table->insert(table->end(), make_pair(i_start, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start > m_start) {
|
||||
table->insert(table->end(), make_pair(i_start + ch.size, seg));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i_start < m_start) {
|
||||
table->insert(table->end(),
|
||||
make_pair(i_start,
|
||||
seg.Subseg(0, m_start - i_start)));
|
||||
}
|
||||
|
||||
AppendCopy(table, source_table, c_start, m_start, ch.size);
|
||||
|
||||
if (m_start < i_end) {
|
||||
table->insert(table->end(),
|
||||
make_pair(m_start + ch.size,
|
||||
seg.Subseg(m_start - i_start, i_end - m_start)));
|
||||
}
|
||||
}
|
||||
|
||||
CHECK_LE(m_start, i_end);
|
||||
|
||||
// Special case for copy to end-of-input.
|
||||
if (m_start == i_end) {
|
||||
AppendCopy(table, source_table, c_start, m_start, ch.size);
|
||||
}
|
||||
}
|
||||
|
||||
static void AppendCopy(SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
xoff_t copy_offset,
|
||||
xoff_t append_offset,
|
||||
xoff_t length) {
|
||||
ConstSegmentMapIterator pos(source_table->upper_bound(copy_offset));
|
||||
--pos;
|
||||
xoff_t got = 0;
|
||||
|
||||
while (got < length) {
|
||||
size_t seg_offset = copy_offset - pos->first;
|
||||
size_t advance = min(pos->second.Size() - seg_offset,
|
||||
(size_t)(length - got));
|
||||
|
||||
table->insert(table->end(),
|
||||
make_pair(append_offset,
|
||||
pos->second.Subseg(seg_offset,
|
||||
advance)));
|
||||
|
||||
got += advance;
|
||||
copy_offset += advance;
|
||||
append_offset += advance;
|
||||
++pos;
|
||||
}
|
||||
}
|
||||
|
||||
ChangeList* Changes() {
|
||||
return &cl_;
|
||||
}
|
||||
|
||||
const ChangeList* Changes() const {
|
||||
return &cl_;
|
||||
}
|
||||
|
||||
private:
|
||||
ChangeList cl_;
|
||||
};
|
||||
|
||||
class Modify1stByte : public Mutator {
|
||||
public:
|
||||
void Mutate(SegmentMap *table,
|
||||
const SegmentMap *source_table,
|
||||
MTRandom *rand) const {
|
||||
ChangeListMutator::Mutate(Change(Change::MODIFY, 1, 0),
|
||||
table, source_table, rand);
|
||||
}
|
||||
};
|
157
lib/xdelta3/testing/random.h
Normal file
157
lib/xdelta3/testing/random.h
Normal file
|
@ -0,0 +1,157 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#undef MT_LEN
|
||||
#undef MT_IA
|
||||
class MTRandom {
|
||||
public:
|
||||
enum Constants {
|
||||
MT_LEN = 624,
|
||||
MT_IA = 397
|
||||
};
|
||||
|
||||
static const uint32_t TEST_SEED1;
|
||||
static const uint32_t UPPER_MASK;
|
||||
static const uint32_t LOWER_MASK;
|
||||
static const uint32_t MATRIX_A;
|
||||
|
||||
MTRandom() {
|
||||
Init(TEST_SEED1);
|
||||
}
|
||||
|
||||
explicit MTRandom(uint32_t seed) {
|
||||
Init(seed);
|
||||
}
|
||||
|
||||
/* This Mersenne Twister code is attributed to Michael Brundage. Thanks!
|
||||
* http://www.qbrundage.com/michaelb/pubs/essays/random_number_generation.html
|
||||
*/
|
||||
uint32_t Rand32 () {
|
||||
uint32_t y;
|
||||
static unsigned long mag01[2] = {
|
||||
0 , MATRIX_A
|
||||
};
|
||||
|
||||
if (mt_index_ >= MT_LEN) {
|
||||
int kk;
|
||||
|
||||
for (kk = 0; kk < MT_LEN - MT_IA; kk++) {
|
||||
y = (mt_buffer_[kk] & UPPER_MASK) | (mt_buffer_[kk + 1] & LOWER_MASK);
|
||||
mt_buffer_[kk] = mt_buffer_[kk + MT_IA] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
for (;kk < MT_LEN - 1; kk++) {
|
||||
y = (mt_buffer_[kk] & UPPER_MASK) | (mt_buffer_[kk + 1] & LOWER_MASK);
|
||||
mt_buffer_[kk] = mt_buffer_[kk + (MT_IA - MT_LEN)] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
}
|
||||
y = (mt_buffer_[MT_LEN - 1] & UPPER_MASK) | (mt_buffer_[0] & LOWER_MASK);
|
||||
mt_buffer_[MT_LEN - 1] = mt_buffer_[MT_IA - 1] ^ (y >> 1) ^ mag01[y & 0x1UL];
|
||||
|
||||
mt_index_ = 0;
|
||||
}
|
||||
|
||||
y = mt_buffer_[mt_index_++];
|
||||
|
||||
y ^= (y >> 11);
|
||||
y ^= (y << 7) & 0x9d2c5680UL;
|
||||
y ^= (y << 15) & 0xefc60000UL;
|
||||
y ^= (y >> 18);
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
uint32_t ExpRand32(uint32_t mean) {
|
||||
double mean_d = mean;
|
||||
double erand = log (1.0 / (Rand32() / (double)UINT32_MAX));
|
||||
uint32_t x = (uint32_t) (mean_d * erand + 0.5);
|
||||
return x;
|
||||
}
|
||||
|
||||
uint64_t Rand64() {
|
||||
return ((uint64_t)Rand32() << 32) | Rand32();
|
||||
}
|
||||
|
||||
uint64_t ExpRand64(uint64_t mean) {
|
||||
double mean_d = mean;
|
||||
double erand = log (1.0 / (Rand64() / (double)UINT32_MAX));
|
||||
uint64_t x = (uint64_t) (mean_d * erand + 0.5);
|
||||
return x;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T Rand() {
|
||||
switch (sizeof(T)) {
|
||||
case sizeof(uint32_t):
|
||||
return Rand32();
|
||||
case sizeof(uint64_t):
|
||||
return Rand64();
|
||||
default:
|
||||
cerr << "Invalid sizeof T" << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T ExpRand(T mean) {
|
||||
switch (sizeof(T)) {
|
||||
case sizeof(uint32_t):
|
||||
return ExpRand32(mean);
|
||||
case sizeof(uint64_t):
|
||||
return ExpRand64(mean);
|
||||
default:
|
||||
cerr << "Invalid sizeof T" << endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void Init(uint32_t seed) {
|
||||
mt_buffer_[0] = seed;
|
||||
mt_index_ = MT_LEN;
|
||||
for (int i = 1; i < MT_LEN; i++) {
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array mt[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
mt_buffer_[i] =
|
||||
(1812433253UL * (mt_buffer_[i-1] ^ (mt_buffer_[i-1] >> 30)) + i);
|
||||
}
|
||||
}
|
||||
|
||||
int mt_index_;
|
||||
uint32_t mt_buffer_[MT_LEN];
|
||||
};
|
||||
|
||||
const uint32_t MTRandom::TEST_SEED1 = 5489UL;
|
||||
const uint32_t MTRandom::UPPER_MASK = 0x80000000;
|
||||
const uint32_t MTRandom::LOWER_MASK = 0x7FFFFFFF;
|
||||
const uint32_t MTRandom::MATRIX_A = 0x9908B0DF;
|
||||
|
||||
class MTRandom8 {
|
||||
public:
|
||||
MTRandom8(MTRandom *rand)
|
||||
: rand_(rand) {
|
||||
}
|
||||
|
||||
uint8_t Rand8() {
|
||||
uint32_t r = rand_->Rand32();
|
||||
|
||||
// TODO: make this use a single byte at a time?
|
||||
return (r & 0xff) ^ (r >> 7) ^ (r >> 15) ^ (r >> 21);
|
||||
}
|
||||
|
||||
private:
|
||||
MTRandom *rand_;
|
||||
};
|
1321
lib/xdelta3/testing/regtest.cc
Normal file
1321
lib/xdelta3/testing/regtest.cc
Normal file
File diff suppressed because it is too large
Load diff
17
lib/xdelta3/testing/regtest_c.c
Normal file
17
lib/xdelta3/testing/regtest_c.c
Normal file
|
@ -0,0 +1,17 @@
|
|||
/* xdelta3 - delta compression tools and library
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
#include "../xdelta3.c"
|
2
lib/xdelta3/testing/run_release.sh
Normal file
2
lib/xdelta3/testing/run_release.sh
Normal file
|
@ -0,0 +1,2 @@
|
|||
#!/bin/sh
|
||||
(cd .. && ./run_release.sh)
|
112
lib/xdelta3/testing/segment.h
Normal file
112
lib/xdelta3/testing/segment.h
Normal file
|
@ -0,0 +1,112 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
class Segment {
|
||||
public:
|
||||
Segment(size_t size, MTRandom *rand)
|
||||
: size_(size),
|
||||
seed_(rand->Rand32()),
|
||||
seed_offset_(0),
|
||||
data_(NULL) {
|
||||
CHECK_GT(size_, 0);
|
||||
}
|
||||
|
||||
Segment(size_t size, uint32_t seed)
|
||||
: size_(size),
|
||||
seed_(seed),
|
||||
seed_offset_(0),
|
||||
data_(NULL) {
|
||||
CHECK_GT(size_, 0);
|
||||
}
|
||||
|
||||
Segment(size_t size, uint8_t *data)
|
||||
: size_(size),
|
||||
seed_(0),
|
||||
seed_offset_(0),
|
||||
data_(data) {
|
||||
CHECK_GT(size_, 0);
|
||||
}
|
||||
|
||||
size_t Size() const {
|
||||
return size_;
|
||||
}
|
||||
|
||||
Segment Subseg(size_t start, size_t size) const {
|
||||
CHECK_LE(start + size, size_);
|
||||
if (data_) {
|
||||
return Segment(size, data_ + start);
|
||||
} else {
|
||||
return Segment(size, seed_, seed_offset_ + start);
|
||||
}
|
||||
}
|
||||
|
||||
void Fill(size_t seg_offset, size_t size, uint8_t *data) const {
|
||||
CHECK_LE(seg_offset + size, size_);
|
||||
if (data_) {
|
||||
memcpy(data, data_ + seg_offset, size);
|
||||
} else {
|
||||
size_t skip = seg_offset + seed_offset_;
|
||||
MTRandom gen(seed_);
|
||||
MTRandom8 gen8(&gen);
|
||||
while (skip--) {
|
||||
gen8.Rand8();
|
||||
}
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
data[i] = gen8.Rand8();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string ToString() const {
|
||||
string r;
|
||||
if (data_) {
|
||||
for (size_t i = 0; i < size_; i++) {
|
||||
char buf[10];
|
||||
sprintf(buf, "%02x ", data_[i]);
|
||||
r.append(buf);
|
||||
}
|
||||
} else {
|
||||
char buf[256];
|
||||
sprintf(buf, "size=%ld,seed=%ud,skip=%ld", size_, seed_, seed_offset_);
|
||||
r.append(buf);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
private:
|
||||
// Used by Subseg()
|
||||
Segment(size_t size, uint32_t seed, size_t seed_offset)
|
||||
: size_(size),
|
||||
seed_(seed),
|
||||
seed_offset_(seed_offset),
|
||||
data_(NULL) {
|
||||
CHECK_GT(size_, 0);
|
||||
}
|
||||
|
||||
size_t size_; // Size of this segment
|
||||
|
||||
// For random segments
|
||||
uint32_t seed_; // Seed used for generating byte sequence
|
||||
size_t seed_offset_; // Seed positions the sequence this many bytes
|
||||
// before its beginning.
|
||||
|
||||
// For literal segments (data is not owned)
|
||||
uint8_t *data_;
|
||||
};
|
||||
|
||||
typedef map<xoff_t, Segment> SegmentMap;
|
||||
typedef typename SegmentMap::const_iterator ConstSegmentMapIterator;
|
||||
typedef typename SegmentMap::iterator SegmentMapIterator;
|
126
lib/xdelta3/testing/sizes.h
Normal file
126
lib/xdelta3/testing/sizes.h
Normal file
|
@ -0,0 +1,126 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
template <typename T, typename U>
|
||||
class SizeIterator {
|
||||
public:
|
||||
SizeIterator(MTRandom *rand, size_t howmany)
|
||||
: rand_(rand),
|
||||
count_(0),
|
||||
fixed_(U::sizes),
|
||||
fixed_size_(SIZEOF_ARRAY(U::sizes)),
|
||||
howmany_(howmany) { }
|
||||
|
||||
T Get() {
|
||||
if (count_ < fixed_size_) {
|
||||
return fixed_[count_];
|
||||
}
|
||||
return rand_->Rand<T>() % U::max_value;
|
||||
}
|
||||
|
||||
bool Done() {
|
||||
return count_ >= fixed_size_ && count_ >= howmany_;
|
||||
}
|
||||
|
||||
void Next() {
|
||||
count_++;
|
||||
}
|
||||
|
||||
private:
|
||||
MTRandom *rand_;
|
||||
size_t count_;
|
||||
T* fixed_;
|
||||
size_t fixed_size_;
|
||||
size_t howmany_;
|
||||
};
|
||||
|
||||
// Small sizes
|
||||
class SmallSizes {
|
||||
public:
|
||||
static size_t sizes[];
|
||||
static size_t max_value;
|
||||
};
|
||||
|
||||
size_t SmallSizes::sizes[] = {
|
||||
0, 1, 128 / 4, 3333,
|
||||
128 - (128 / 3),
|
||||
128,
|
||||
128 + (128 / 3),
|
||||
2 * 128 - (128 / 3),
|
||||
2 * 128,
|
||||
2 * 128 + (128 / 3),
|
||||
};
|
||||
|
||||
size_t SmallSizes::max_value = 128 * 3;
|
||||
|
||||
// Large sizes
|
||||
class LargeSizes {
|
||||
public:
|
||||
static size_t sizes[];
|
||||
static size_t max_value;
|
||||
};
|
||||
|
||||
size_t LargeSizes::sizes[] = {
|
||||
1 << 20,
|
||||
1 << 18,
|
||||
1 << 16,
|
||||
};
|
||||
|
||||
size_t LargeSizes::max_value = 1<<20;
|
||||
|
||||
// Base constants
|
||||
struct BaseConstants {
|
||||
static const size_t TEST_ROUNDS;
|
||||
};
|
||||
|
||||
const size_t BaseConstants::TEST_ROUNDS = 10;
|
||||
|
||||
// Regtest<> arguments
|
||||
struct SmallBlock : public BaseConstants {
|
||||
static const xoff_t BLOCK_SIZE;
|
||||
static const size_t WINDOW_SIZE;
|
||||
typedef SmallSizes Sizes;
|
||||
};
|
||||
|
||||
const xoff_t SmallBlock::BLOCK_SIZE = 1<<7;
|
||||
const size_t SmallBlock::WINDOW_SIZE = 1<<7;
|
||||
|
||||
struct LargeBlock : public BaseConstants {
|
||||
static const xoff_t BLOCK_SIZE;
|
||||
static const size_t WINDOW_SIZE;
|
||||
typedef LargeSizes Sizes;
|
||||
};
|
||||
|
||||
const xoff_t LargeBlock::BLOCK_SIZE = (1 << 13);
|
||||
const size_t LargeBlock::WINDOW_SIZE = (1 << 13);
|
||||
|
||||
struct MixedBlock : public BaseConstants {
|
||||
static const xoff_t BLOCK_SIZE;
|
||||
static const size_t WINDOW_SIZE;
|
||||
typedef SmallSizes Sizes;
|
||||
};
|
||||
|
||||
const xoff_t MixedBlock::BLOCK_SIZE = 1<<7;
|
||||
const size_t MixedBlock::WINDOW_SIZE = 1<<8;
|
||||
|
||||
struct OversizeBlock : public BaseConstants {
|
||||
static const xoff_t BLOCK_SIZE;
|
||||
static const size_t WINDOW_SIZE;
|
||||
typedef SmallSizes Sizes;
|
||||
};
|
||||
|
||||
const xoff_t OversizeBlock::BLOCK_SIZE = 1<<8;
|
||||
const size_t OversizeBlock::WINDOW_SIZE = 1<<7;
|
84
lib/xdelta3/testing/test.h
Normal file
84
lib/xdelta3/testing/test.h
Normal file
|
@ -0,0 +1,84 @@
|
|||
/* xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
Copyright 2016 Joshua MacDonald
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include "../xdelta3.h"
|
||||
#include "../xdelta3-internal.h"
|
||||
}
|
||||
|
||||
#include <unistd.h>
|
||||
#include <math.h>
|
||||
#include <string>
|
||||
|
||||
#define CHECK_EQ(x,y) CHECK_OP(x,y,==)
|
||||
#define CHECK_NE(x,y) CHECK_OP(x,y,!=)
|
||||
#define CHECK_LT(x,y) CHECK_OP(x,y,<)
|
||||
#define CHECK_GT(x,y) CHECK_OP(x,y,>)
|
||||
#define CHECK_LE(x,y) CHECK_OP(x,y,<=)
|
||||
#define CHECK_GE(x,y) CHECK_OP(x,y,>=)
|
||||
|
||||
#define CHECK_OP(x,y,OP) \
|
||||
do { \
|
||||
__typeof__(x) _x(x); \
|
||||
__typeof__(x) _y(y); \
|
||||
if (!(_x OP _y)) { \
|
||||
cerr << __FILE__ << ":" << __LINE__ << " Check failed: " << #x " " #OP " " #y << endl; \
|
||||
cerr << __FILE__ << ":" << __LINE__ << " {0} " << _x << endl; \
|
||||
cerr << __FILE__ << ":" << __LINE__ << " {1} " << _y << endl; \
|
||||
abort(); \
|
||||
} } while (false)
|
||||
#undef CHECK
|
||||
#define CHECK(x) \
|
||||
do {if (!(x)) { \
|
||||
cerr << __FILE__ << ":" << __LINE__ << " Check failed: " << #x << endl; \
|
||||
abort(); \
|
||||
} } while (false)
|
||||
|
||||
#define DCHECK(x)
|
||||
|
||||
using std::string;
|
||||
|
||||
#include <vector>
|
||||
using std::vector;
|
||||
|
||||
inline string CommandToString(const vector<const char*> &v) {
|
||||
string s(v[0]);
|
||||
for (size_t i = 1; i < v.size() && v[i] != NULL; i++) {
|
||||
s.append(" ");
|
||||
s.append(v[i]);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
#include <iostream>
|
||||
using std::cerr;
|
||||
using std::endl;
|
||||
using std::ostream;
|
||||
|
||||
#include <map>
|
||||
using std::map;
|
||||
using std::pair;
|
||||
|
||||
#include <list>
|
||||
using std::list;
|
||||
|
||||
template <typename T, typename U>
|
||||
pair<T, U> make_pair(const T& t, const U& u) {
|
||||
return pair<T, U>(t, u);
|
||||
}
|
||||
|
||||
using std::min;
|
||||
using std::max;
|
1264
lib/xdelta3/testing/xdelta3-regtest.py
Normal file
1264
lib/xdelta3/testing/xdelta3-regtest.py
Normal file
File diff suppressed because it is too large
Load diff
153
lib/xdelta3/testing/xdelta3-test.py
Normal file
153
lib/xdelta3/testing/xdelta3-test.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
#!/usr/bin/python2.7
|
||||
# xdelta3 - delta compression tools and library -*- Mode: C++ -*-
|
||||
# Copyright 2016 Joshua MacDonald
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import xdelta3
|
||||
|
||||
# the test data section is expected to be len('target')
|
||||
source = 'source source input0 source source'
|
||||
target = 'source source target source source'
|
||||
|
||||
#
|
||||
#
|
||||
|
||||
print 'encode: basic ...'
|
||||
result, patch = xdelta3.xd3_encode_memory(target, source, 50)
|
||||
|
||||
assert result == 0
|
||||
assert len(patch) < len(source)
|
||||
|
||||
print 'encode: adler32 ...'
|
||||
result, patch_adler32 = xdelta3.xd3_encode_memory(target, source, 50,
|
||||
xdelta3.XD3_ADLER32)
|
||||
|
||||
assert result == 0
|
||||
assert len(patch_adler32) < len(source)
|
||||
assert len(patch_adler32) > len(patch)
|
||||
|
||||
print 'encode: secondary ...'
|
||||
result, patch_djw = xdelta3.xd3_encode_memory(target, source, 50,
|
||||
xdelta3.XD3_SEC_DJW)
|
||||
|
||||
assert result == 0
|
||||
# secondary compression doesn't help
|
||||
assert len(patch_djw) > len(patch)
|
||||
|
||||
print 'encode: exact ...'
|
||||
result, ignore = xdelta3.xd3_encode_memory(target, source, len(patch))
|
||||
|
||||
assert result == 0
|
||||
assert len(ignore) < len(source)
|
||||
|
||||
print 'encode: out of space ...'
|
||||
result, ignore = xdelta3.xd3_encode_memory(target, source, len(patch) - 1)
|
||||
|
||||
assert result == 28
|
||||
assert ignore == None
|
||||
|
||||
print 'encode: zero space ...'
|
||||
result, ignore = xdelta3.xd3_encode_memory(target, source, 0)
|
||||
|
||||
assert result == 28
|
||||
assert ignore == None
|
||||
|
||||
print 'encode: no source ...'
|
||||
result, zdata = xdelta3.xd3_encode_memory(target, None, 50)
|
||||
|
||||
assert result == 0
|
||||
assert len(zdata) > len(patch)
|
||||
|
||||
print 'encode: no input ...'
|
||||
result, ignore = xdelta3.xd3_encode_memory(None, None, 50)
|
||||
|
||||
assert result != 0
|
||||
|
||||
print 'decode: basic ...'
|
||||
result, target1 = xdelta3.xd3_decode_memory(patch, source, len(target))
|
||||
|
||||
assert result == 0
|
||||
assert len(target1) == len(target)
|
||||
assert target1 == target
|
||||
|
||||
print 'decode: out of space ...'
|
||||
result, ignore = xdelta3.xd3_decode_memory(patch, source, len(target) - 1)
|
||||
|
||||
assert result == 28
|
||||
assert ignore == None
|
||||
|
||||
print 'decode: zero space ...'
|
||||
result, ignore = xdelta3.xd3_decode_memory(patch, source, 0)
|
||||
|
||||
assert result == 28
|
||||
assert ignore == None
|
||||
|
||||
print 'decode: single byte error ...'
|
||||
# a few expected single-byte errors, e.g., unused address cache bits, see
|
||||
# xdelta3-test.h's single-bit error tests
|
||||
extra_count = 4
|
||||
noverify_count = 0
|
||||
for corrupt_pos in range(len(patch_adler32)):
|
||||
input = ''.join([j == corrupt_pos and '\xff' or patch_adler32[j]
|
||||
for j in range(len(patch_adler32))])
|
||||
|
||||
result, ignore = xdelta3.xd3_decode_memory(input, source, len(target), 0)
|
||||
assert result == -17712
|
||||
assert ignore == None
|
||||
|
||||
# without adler32 verification, the error may be in the data section which
|
||||
# in this case is 6 bytes 'target'
|
||||
result, corrupt = xdelta3.xd3_decode_memory(input, source, len(target),
|
||||
xdelta3.XD3_ADLER32_NOVER)
|
||||
if result == 0:
|
||||
noverify_count = noverify_count + 1
|
||||
#print "got %s" % corrupt
|
||||
#end
|
||||
#end
|
||||
assert noverify_count == len('target') + extra_count
|
||||
|
||||
print 'decode: no source ...'
|
||||
result, target2 = xdelta3.xd3_decode_memory(zdata, None, len(target))
|
||||
|
||||
assert result == 0
|
||||
assert target == target2
|
||||
|
||||
# Test compression level setting via flags. assumes a 9 byte checksum
|
||||
# and that level 9 steps 2, level 1 steps 15:
|
||||
# 01234567890123456789012345678901
|
||||
# level 1 only indexes 2 checksums "abcdefghi" and "ABCDEFGHI"
|
||||
# outputs 43 vs. 23 bytes
|
||||
print 'encode: compression level ...'
|
||||
|
||||
source = '_la_la_abcdefghi_la_la_ABCDEFGHI'
|
||||
target = 'la_la_ABCDEFGH__la_la_abcdefgh__'
|
||||
|
||||
result1, level1 = xdelta3.xd3_encode_memory(target, source, 50, xdelta3.XD3_COMPLEVEL_1)
|
||||
result9, level9 = xdelta3.xd3_encode_memory(target, source, 50, xdelta3.XD3_COMPLEVEL_9)
|
||||
|
||||
assert result1 == 0 and result9 == 0
|
||||
assert len(level1) > len(level9)
|
||||
|
||||
#
|
||||
# Issue 65
|
||||
print 'encode: 65 ...'
|
||||
source = 'Hello World'
|
||||
target = 'Hello everyone'
|
||||
result, patch = xdelta3.xd3_encode_memory(target, source, len(target))
|
||||
assert result != 0
|
||||
|
||||
result, patch = xdelta3.xd3_encode_memory(target, source, 2 * len(target))
|
||||
assert result == 0
|
||||
|
||||
print 'PASS'
|
Loading…
Add table
Add a link
Reference in a new issue