regtest.cc (xdelta3-3.0.10) | : | regtest.cc (xdelta3-3.0.11) | ||
---|---|---|---|---|
skipping to change at line 95 | skipping to change at line 95 | |||
} | } | |||
BlockIterator source_iterator(source_file, options.block_size); | BlockIterator source_iterator(source_file, options.block_size); | |||
BlockIterator target_iterator(target_file, Constants::WINDOW_SIZE); | BlockIterator target_iterator(target_file, Constants::WINDOW_SIZE); | |||
Block encode_source_block, decode_source_block; | Block encode_source_block, decode_source_block; | |||
Block decoded_block, target_block; | Block decoded_block, target_block; | |||
bool encoding = true; | bool encoding = true; | |||
bool done = false; | bool done = false; | |||
bool done_after_input = false; | bool done_after_input = false; | |||
IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u winsize %lu\n", | IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u winsize %"Z"u\n", | |||
source_file.Size(), options.block_size, | source_file.Size(), options.block_size, | |||
target_file.Size(), | target_file.Size(), | |||
Constants::WINDOW_SIZE)); | Constants::WINDOW_SIZE)); | |||
while (!done) { | while (!done) { | |||
target_iterator.Get(&target_block); | target_iterator.Get(&target_block); | |||
xoff_t blks = target_iterator.Blocks(); | xoff_t blks = target_iterator.Blocks(); | |||
IF_DEBUG2(XPR(NTR "target in %s: %"Q"u..%"Q"u %"Q"u(%"Q"u) " | IF_DEBUG2(XPR(NTR "target in %s: %"Q"u..%"Q"u %"Q"u(%"Q"u) " | |||
skipping to change at line 156 | skipping to change at line 156 | |||
decoded_block.Append(decode_stream.next_out, | decoded_block.Append(decode_stream.next_out, | |||
decode_stream.avail_out); | decode_stream.avail_out); | |||
xd3_consume_output(&decode_stream); | xd3_consume_output(&decode_stream); | |||
} | } | |||
goto process; | goto process; | |||
case XD3_GETSRCBLK: { | case XD3_GETSRCBLK: { | |||
xd3_source *src = (encoding ? &encode_source : &decode_source); | xd3_source *src = (encoding ? &encode_source : &decode_source); | |||
Block *block = (encoding ? &encode_source_block : &decode_source_block); | Block *block = (encoding ? &encode_source_block : &decode_source_block); | |||
if (encoding) { | if (encoding) { | |||
IF_DEBUG1(XPR(NTR "[srcblock] %"Q"u last srcpos %"Q"u " | IF_DEBUG2(XPR(NTR "[srcblock] %"Q"u last srcpos %"Q"u " | |||
"encodepos %"Q"u\n", | "encodepos %"Q"u\n", | |||
encode_source.getblkno, | encode_source.getblkno, | |||
encode_stream.match_last_srcpos, | encode_stream.match_last_srcpos, | |||
encode_stream.input_position + encode_stream.total_in)); | encode_stream.input_position + encode_stream.total_in)); | |||
} | } | |||
source_iterator.SetBlock(src->getblkno); | source_iterator.SetBlock(src->getblkno); | |||
source_iterator.Get(block); | source_iterator.Get(block); | |||
src->curblkno = src->getblkno; | src->curblkno = src->getblkno; | |||
src->onblk = block->Size(); | src->onblk = block->Size(); | |||
skipping to change at line 793 | skipping to change at line 793 | |||
Delta delta(block); | Delta delta(block); | |||
CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows()); | CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows()); | |||
} | } | |||
void TestSmallStride() { | void TestSmallStride() { | |||
MTRandom rand; | MTRandom rand; | |||
FileSpec spec0(&rand); | FileSpec spec0(&rand); | |||
usize_t size = Constants::BLOCK_SIZE * 4; | usize_t size = Constants::BLOCK_SIZE * 4; | |||
spec0.GenerateFixedSize(size); | spec0.GenerateFixedSize(size); | |||
/* TODO Need to study the actual causes of missed adds for tests | // Note: Not very good performance due to hash collisions, note 3x | |||
* less than 30 bytes. */ | // multiplier below. | |||
const int s = 30; | for (int s = 15; s < 101; s++) { | |||
usize_t adds = 0; | usize_t changes = 0; | |||
ChangeList cl; | ChangeList cl; | |||
for (usize_t j = s; j < size; j += s, ++adds) | for (usize_t j = s; j < size; j += s, ++changes) | |||
{ | { | |||
cl.push_back(Change(Change::MODIFY, 1, j)); | cl.push_back(Change(Change::MODIFY, 1, j)); | |||
} | } | |||
FileSpec spec1(&rand); | ||||
spec0.ModifyTo(ChangeListMutator(cl), &spec1); | ||||
Options options; | FileSpec spec1(&rand); | |||
options.encode_srcwin_maxsz = size; | spec0.ModifyTo(ChangeListMutator(cl), &spec1); | |||
options.iopt_size = 128; | ||||
options.smatch_cfg = XD3_SMATCH_SLOW; | ||||
options.size_known = false; | ||||
Block block; | Options options; | |||
InMemoryEncodeDecode(spec0, spec1, &block, options); | options.encode_srcwin_maxsz = size; | |||
Delta delta(block); | options.iopt_size = 128; | |||
options.smatch_cfg = XD3_SMATCH_SLOW; | ||||
options.size_known = false; | ||||
// Allow an additional two byte of add per window | Block block; | |||
usize_t allowance = 2 * size / Constants::WINDOW_SIZE; | InMemoryEncodeDecode(spec0, spec1, &block, options); | |||
CHECK_GE(adds + allowance, delta.AddedBytes()); | Delta delta(block); | |||
IF_DEBUG1(DP(RINT "[stride=%d] changes=%u adds=%"Q"u\n", | ||||
s, changes, delta.AddedBytes())); | ||||
double allowance = Constants::BLOCK_SIZE < 8192 || s < 30 ? 3.0 : 1.1; | ||||
CHECK_GE(allowance * changes, (double)delta.AddedBytes()); | ||||
} | ||||
} | } | |||
void TestCopyWindow() { | void TestCopyWindow() { | |||
// Construct an input that has many copies, to fill the IOPT buffer | // Construct an input that has many copies, to fill the IOPT buffer | |||
// and force a source window decision. "srclen" may be set to a | // and force a source window decision. "srclen" may be set to a | |||
// value that goes beyond the end-of-source. | // value that goes beyond the end-of-source. | |||
const int clen = 16; | const int clen = 16; | |||
const int size = 4096; | const int size = 4096; | |||
const int nmov = size / clen; | const int nmov = size / clen; | |||
const int iters = 16; | const int iters = 16; | |||
skipping to change at line 930 | skipping to change at line 932 | |||
Block block3; | Block block3; | |||
Block block4; | Block block4; | |||
EncodeDecodeAPI(spec0, spec1, &block3, options); | EncodeDecodeAPI(spec0, spec1, &block3, options); | |||
EncodeDecodeAPI(spec1, spec0, &block4, options); | EncodeDecodeAPI(spec1, spec0, &block4, options); | |||
} | } | |||
CHECK_GE(2000 * iters, added_01); | CHECK_GE(2000 * iters, added_01); | |||
CHECK_LE(2000 * iters, added_10); | CHECK_LE(2000 * iters, added_10); | |||
} | } | |||
void TestHalfBlockCopy() { | void TestHalfBlockCopy() { | |||
MTRandom rand; | // Create a half-block copy, 7.5 blocks apart, in a pair of files: | |||
FileSpec spec0(&rand); | // 0 1 ... 6 7 | |||
FileSpec spec1(&rand); | // spec0 [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb][ccccc][bbbb_] | |||
// spec1 [aaaaa][ccccc][aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa_] | ||||
spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 4); | // where stage= | |||
// 0: the final block is full | ||||
// a. (source)spec1->(target)spec0 copies block C: reads 8 source | ||||
// blocks during target block 0. | ||||
// b. (source)spec0->(target)spec1 does not copy block C b/c attempt | ||||
// to read past EOF empties block 0 from (virtual) block cache | ||||
// 1: the final block is less than full. | ||||
// a. (same) copies block C | ||||
// b. (same) copies block C, unlike 0a, no attempt to read past EOF | ||||
// | ||||
// "virtual" above refers to XD3_TOOFARBACK, since there is no caching | ||||
// in the API, there is simply a promise not to request blocks that are | ||||
// beyond source->max_winsize from the last known source file position. | ||||
for (int stage = 0; stage < 2; stage++) | ||||
{ | ||||
IF_DEBUG1 (DP(RINT "half_block_copy stage %d\n", stage)); | ||||
// Create a half-block copy, 2.5 blocks apart, from the second half | MTRandom rand; | |||
// of the source version to the first half of the target version. | FileSpec spec0(&rand); | |||
// 0 1 2 3 | FileSpec spec1(&rand); | |||
// spec0 [bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb][ccccc][bbbbb] | ||||
// spec1 [aaaaa][ccccc][aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa] | spec0.GenerateFixedSize(Constants::BLOCK_SIZE * 8 - stage); | |||
ChangeList cl1; | ||||
cl1.push_back(Change(Change::MODIFY, | ChangeList cl1; | |||
Constants::BLOCK_SIZE / 2, // size | cl1.push_back(Change(Change::MODIFY, | |||
0)); | Constants::BLOCK_SIZE / 2, // size | |||
cl1.push_back(Change(Change::COPYOVER, | 0)); | |||
Constants::BLOCK_SIZE / 2, // size | cl1.push_back(Change(Change::COPYOVER, | |||
Constants::BLOCK_SIZE * 3, // offset | Constants::BLOCK_SIZE / 2, // size | |||
Constants::BLOCK_SIZE / 2)); | Constants::BLOCK_SIZE * 7, // offset | |||
cl1.push_back(Change(Change::MODIFY, | Constants::BLOCK_SIZE / 2)); | |||
Constants::BLOCK_SIZE * 3, | cl1.push_back(Change(Change::MODIFY, | |||
Constants::BLOCK_SIZE)); | Constants::BLOCK_SIZE * 7, | |||
spec0.ModifyTo(ChangeListMutator(cl1), &spec1); | Constants::BLOCK_SIZE - stage)); | |||
spec0.ModifyTo(ChangeListMutator(cl1), &spec1); | ||||
const int onecopy_adds = | ||||
4 * Constants::BLOCK_SIZE - Constants::BLOCK_SIZE / 2; | ||||
const int nocopy_adds = 4 * Constants::BLOCK_SIZE; | ||||
// Note the case b=4 is contrived: the caller should use a single block | ||||
// containing the entire source, if possible. | ||||
for (int b = 1; b <= 4; b++) | ||||
{ | ||||
Options options; | Options options; | |||
options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * b; | options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * 8; | |||
Block block0; | Block block0; | |||
Block block1; | Block block1; | |||
InMemoryEncodeDecode(spec0, spec1, &block0, options); | InMemoryEncodeDecode(spec0, spec1, &block0, options); | |||
InMemoryEncodeDecode(spec1, spec0, &block1, options); | InMemoryEncodeDecode(spec1, spec0, &block1, options); | |||
Delta delta0(block0); | Delta delta0(block0); | |||
Delta delta1(block1); | Delta delta1(block1); | |||
// The first block never copies from the last source block, by | const int yes = | |||
// design, because if the last source block is available when | Constants::BLOCK_SIZE * 8 - Constants::BLOCK_SIZE / 2; | |||
// the first target block is ready, the caller is expected to | const int no = | |||
// use a single block. | Constants::BLOCK_SIZE * 8 - Constants::BLOCK_SIZE / 2; | |||
CHECK_EQ(nocopy_adds, delta0.AddedBytes()); | ||||
if (Constants::BLOCK_SIZE < 8192 || b > 2) | if (stage == 0) | |||
{ | { | |||
// For small-block inputs, the entire file is read into one | CHECK_EQ(yes, delta0.AddedBytes()); | |||
// block (the min source window size is 16kB). | CHECK_EQ(no, delta1.AddedBytes()); | |||
// | } | |||
// For large blocks, at least 3 blocks of source window are | ||||
// needed. | ||||
CHECK_EQ(onecopy_adds, delta1.AddedBytes()); | ||||
} | ||||
else | else | |||
{ | { | |||
// When there are fewer than 3 source blocks. | CHECK_EQ(yes, delta0.AddedBytes()); | |||
CHECK_EQ(nocopy_adds, delta1.AddedBytes()); | CHECK_EQ(yes, delta1.AddedBytes()); | |||
} | } | |||
} | } | |||
Options options; | ||||
options.encode_srcwin_maxsz = Constants::BLOCK_SIZE * 4; | ||||
options.block_size = Constants::BLOCK_SIZE * 4; | ||||
// Test the whole-buffer case. | ||||
Block block0; | ||||
Block block1; | ||||
InMemoryEncodeDecode(spec0, spec1, &block0, options); | ||||
InMemoryEncodeDecode(spec1, spec0, &block1, options); | ||||
Delta delta0(block0); | ||||
Delta delta1(block1); | ||||
// This <= >= are only for blocksize = 512, which has irregular readsize. | ||||
CHECK_LE(onecopy_adds, delta0.AddedBytes()); | ||||
CHECK_GE(onecopy_adds + 1, delta0.AddedBytes()); | ||||
CHECK_EQ(onecopy_adds, delta1.AddedBytes()); | ||||
} | } | |||
void FourWayMergeTest(const FileSpec &spec0, | void FourWayMergeTest(const FileSpec &spec0, | |||
const FileSpec &spec1, | const FileSpec &spec1, | |||
const FileSpec &spec2, | const FileSpec &spec2, | |||
const FileSpec &spec3) { | const FileSpec &spec3) { | |||
TmpFile f0, f1, f2, f3; | TmpFile f0, f1, f2, f3; | |||
ExtFile d01, d12, d23; | ExtFile d01, d12, d23; | |||
Options options; | Options options; | |||
options.encode_srcwin_maxsz = | options.encode_srcwin_maxsz = | |||
End of changes. 14 change blocks. | ||||
91 lines changed or deleted | 81 lines changed or added |