summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjosh.macdonald <jmacd@users.noreply.github.com>2014-03-09 05:44:20 +0000
committerjosh.macdonald <jmacd@users.noreply.github.com>2014-03-09 05:44:20 +0000
commit81498dea08759883b4bfdb3335a95da92c9278dd (patch)
tree0955937f902a9a9db168cb1283b697092f7690d8
parent9e98593f4cbaa59b8fdef989983a4fc727685b4c (diff)
Fix issue in xd3_encode_memory causing error when IOPT buffer capacity reached and a copy reaches the end-of-source, causing unexpected request for second source block. New tests to cover this case. Removed the IOPT limit in xd3_encode_memory (improves compression). Doubles sizing of the large-checksum hash table (fewer collisions, better compression).
-rw-r--r--xdelta3/testing/Makefile3
-rw-r--r--xdelta3/testing/file.h13
-rw-r--r--xdelta3/testing/modify.h2
-rw-r--r--xdelta3/testing/regtest.cc322
-rw-r--r--xdelta3/testing/sizes.h19
-rw-r--r--xdelta3/xdelta3-djw.h2
-rw-r--r--xdelta3/xdelta3-internal.h11
-rw-r--r--xdelta3/xdelta3.c54
8 files changed, 333 insertions, 93 deletions
diff --git a/xdelta3/testing/Makefile b/xdelta3/testing/Makefile
index 77cc11d..f859f94 100644
--- a/xdelta3/testing/Makefile
+++ b/xdelta3/testing/Makefile
@@ -1,2 +1,5 @@
1all: 1all:
2 (cd .. && make all) 2 (cd .. && make all)
3
4xdelta3regtest:
5 (cd .. && make xdelta3regtest)
diff --git a/xdelta3/testing/file.h b/xdelta3/testing/file.h
index a204d74..67309ac 100644
--- a/xdelta3/testing/file.h
+++ b/xdelta3/testing/file.h
@@ -82,16 +82,21 @@ public:
82 } 82 }
83 83
84 void SetSize(size_t size) { 84 void SetSize(size_t size) {
85 size_ = size; 85 uint8_t *t = NULL;
86
87 if (data_size_ < size) { 86 if (data_size_ < size) {
88 if (data_) { 87 if (data_) {
89 delete [] data_; 88 t = data_;
90 } 89 }
91 data_ = new uint8_t[size]; 90 data_ = new uint8_t[size];
92 data_size_ = size; 91 data_size_ = size;
93 } 92 }
93 if (t && size < size_) {
94 memcpy(data_, t, size);
95 }
96 delete [] t;
97 size_ = size;
94 } 98 }
99
95private: 100private:
96 friend class BlockIterator; 101 friend class BlockIterator;
97 102
@@ -268,6 +273,7 @@ public:
268 } 273 }
269 274
270 void SetBlock(xoff_t blkno) { 275 void SetBlock(xoff_t blkno) {
276 CHECK_LE(blkno, Blocks());
271 blkno_ = blkno; 277 blkno_ = blkno;
272 } 278 }
273 279
@@ -373,4 +379,3 @@ public:
373private: 379private:
374 mutable main_file file_; 380 mutable main_file file_;
375}; 381};
376
diff --git a/xdelta3/testing/modify.h b/xdelta3/testing/modify.h
index 5c2e316..023e6ce 100644
--- a/xdelta3/testing/modify.h
+++ b/xdelta3/testing/modify.h
@@ -15,7 +15,7 @@ public:
15 DELETE = 3, // Delete a specified range of data 15 DELETE = 3, // Delete a specified range of data
16 COPY = 4, // Copy from one region, inserting elsewhere 16 COPY = 4, // Copy from one region, inserting elsewhere
17 MOVE = 5, // Copy then delete copied-from range 17 MOVE = 5, // Copy then delete copied-from range
18 COPYOVER = 6 // Copy then delete copied-to range 18 COPYOVER = 6 // Copy then delete copied-to range
19 19
20 // ADD, DELETE, and COPY change the file size 20 // ADD, DELETE, and COPY change the file size
21 // MODIFY, MOVE, COPYOVER preserve the file size 21 // MODIFY, MOVE, COPYOVER preserve the file size
diff --git a/xdelta3/testing/regtest.cc b/xdelta3/testing/regtest.cc
index 12b712e..7f9ba52 100644
--- a/xdelta3/testing/regtest.cc
+++ b/xdelta3/testing/regtest.cc
@@ -9,12 +9,18 @@ public:
9 typedef typename Constants::Sizes Sizes; 9 typedef typename Constants::Sizes Sizes;
10 10
11 struct Options { 11 struct Options {
12 Options() : encode_srcwin_maxsz(1<<20), 12 Options()
13 block_size(Constants::BLOCK_SIZE), 13 : encode_srcwin_maxsz(1<<20),
14 size_known(false) { } 14 block_size(Constants::BLOCK_SIZE),
15 size_known(false),
16 iopt_size(XD3_DEFAULT_IOPT_SIZE),
17 smatch_cfg(XD3_SMATCH_DEFAULT) { }
18
15 size_t encode_srcwin_maxsz; 19 size_t encode_srcwin_maxsz;
16 size_t block_size; 20 size_t block_size;
17 bool size_known; 21 bool size_known;
22 usize_t iopt_size;
23 xd3_smatch_cfg smatch_cfg;
18 }; 24 };
19 25
20#include "segment.h" 26#include "segment.h"
@@ -26,7 +32,7 @@ public:
26 void InMemoryEncodeDecode(const FileSpec &source_file, 32 void InMemoryEncodeDecode(const FileSpec &source_file,
27 const FileSpec &target_file, 33 const FileSpec &target_file,
28 Block *coded_data, 34 Block *coded_data,
29 const Options &options = Options()) { 35 const Options &options) {
30 xd3_stream encode_stream; 36 xd3_stream encode_stream;
31 xd3_config encode_config; 37 xd3_config encode_config;
32 xd3_source encode_source; 38 xd3_source encode_source;
@@ -51,16 +57,8 @@ public:
51 xd3_init_config(&decode_config, XD3_ADLER32); 57 xd3_init_config(&decode_config, XD3_ADLER32);
52 58
53 encode_config.winsize = Constants::WINDOW_SIZE; 59 encode_config.winsize = Constants::WINDOW_SIZE;
54 60 encode_config.iopt_size = options.iopt_size;
55 // TODO! the smatcher setup isn't working, 61 encode_config.smatch_cfg = options.smatch_cfg;
56 // if (options.large_cksum_step) {
57 // encode_config.smatch_cfg = XD3_SMATCH_SOFT;
58 // encode_config.smatcher_soft.large_step = options.large_cksum_step;
59 // }
60 // if (options.large_cksum_size) {
61 // encode_config.smatch_cfg = XD3_SMATCH_SOFT;
62 // encode_config.smatcher_soft.large_look = options.large_cksum_size;
63 // }
64 62
65 CHECK_EQ(0, xd3_config_stream (&encode_stream, &encode_config)); 63 CHECK_EQ(0, xd3_config_stream (&encode_stream, &encode_config));
66 CHECK_EQ(0, xd3_config_stream (&decode_stream, &decode_config)); 64 CHECK_EQ(0, xd3_config_stream (&decode_stream, &decode_config));
@@ -71,30 +69,30 @@ public:
71 encode_source.max_winsize = options.encode_srcwin_maxsz; 69 encode_source.max_winsize = options.encode_srcwin_maxsz;
72 decode_source.max_winsize = options.encode_srcwin_maxsz; 70 decode_source.max_winsize = options.encode_srcwin_maxsz;
73 71
74 if (!options.size_known) 72 if (!options.size_known)
75 { 73 {
76 xd3_set_source (&encode_stream, &encode_source); 74 xd3_set_source (&encode_stream, &encode_source);
77 xd3_set_source (&decode_stream, &decode_source); 75 xd3_set_source (&decode_stream, &decode_source);
78 } 76 }
79 else 77 else
80 { 78 {
81 xd3_set_source_and_size (&encode_stream, &encode_source, 79 xd3_set_source_and_size (&encode_stream, &encode_source,
82 source_file.Size()); 80 source_file.Size());
83 xd3_set_source_and_size (&decode_stream, &decode_source, 81 xd3_set_source_and_size (&decode_stream, &decode_source,
84 source_file.Size()); 82 source_file.Size());
85 } 83 }
86 84
87 BlockIterator source_iterator(source_file, options.block_size); 85 BlockIterator source_iterator(source_file, options.block_size);
88 BlockIterator target_iterator(target_file, Constants::READ_SIZE); 86 BlockIterator target_iterator(target_file, Constants::WINDOW_SIZE);
89 Block encode_source_block, decode_source_block; 87 Block encode_source_block, decode_source_block;
90 Block decoded_block, target_block; 88 Block decoded_block, target_block;
91 bool encoding = true; 89 bool encoding = true;
92 bool done = false; 90 bool done = false;
93 bool done_after_input = false; 91 bool done_after_input = false;
94 92
95 IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u[%lu] winsize %lu\n", 93 IF_DEBUG1 (XPR(NTR "source %"Q"u[%"Q"u] target %"Q"u winsize %lu\n",
96 source_file.Size(), options.block_size, 94 source_file.Size(), options.block_size,
97 target_file.Size(), Constants::READ_SIZE, 95 target_file.Size(),
98 Constants::WINDOW_SIZE)); 96 Constants::WINDOW_SIZE));
99 97
100 while (!done) { 98 while (!done) {
@@ -102,7 +100,8 @@ public:
102 100
103 xoff_t blks = target_iterator.Blocks(); 101 xoff_t blks = target_iterator.Blocks();
104 102
105 IF_DEBUG2(XPR(NTR "target in %s: %llu..%llu %"Q"u(%"Q"u) verified %"Q"u\n", 103 IF_DEBUG2(XPR(NTR "target in %s: %"Q"u..%"Q"u %"Q"u(%"Q"u) "
104 "verified %"Q"u\n",
106 encoding ? "encoding" : "decoding", 105 encoding ? "encoding" : "decoding",
107 target_iterator.Offset(), 106 target_iterator.Offset(),
108 target_iterator.Offset() + target_block.Size(), 107 target_iterator.Offset() + target_block.Size(),
@@ -229,10 +228,10 @@ public:
229 ExtFile *coded_data, 228 ExtFile *coded_data,
230 const Options &options) { 229 const Options &options) {
231 vector<const char*> ecmd; 230 vector<const char*> ecmd;
232 char buf[16]; 231 char wbuf[16];
233 snprintf(buf, sizeof(buf), "-B%"Q"u", options.encode_srcwin_maxsz); 232 snprintf(wbuf, sizeof(wbuf), "-B%"Q"u", options.encode_srcwin_maxsz);
234 ecmd.push_back("xdelta3"); 233 ecmd.push_back("xdelta3");
235 ecmd.push_back(buf); 234 ecmd.push_back(wbuf);
236 ecmd.push_back("-s"); 235 ecmd.push_back("-s");
237 ecmd.push_back(source_file.Name()); 236 ecmd.push_back(source_file.Name());
238 ecmd.push_back(target_file.Name()); 237 ecmd.push_back(target_file.Name());
@@ -245,7 +244,7 @@ public:
245 vector<const char*> dcmd; 244 vector<const char*> dcmd;
246 ExtFile recon_file; 245 ExtFile recon_file;
247 dcmd.push_back("xdelta3"); 246 dcmd.push_back("xdelta3");
248 ecmd.push_back(buf); 247 ecmd.push_back(wbuf);
249 dcmd.push_back("-d"); 248 dcmd.push_back("-d");
250 dcmd.push_back("-s"); 249 dcmd.push_back("-s");
251 dcmd.push_back(source_file.Name()); 250 dcmd.push_back(source_file.Name());
@@ -256,10 +255,115 @@ public:
256 CHECK_EQ(0, xd3_main_cmdline(dcmd.size() - 1, 255 CHECK_EQ(0, xd3_main_cmdline(dcmd.size() - 1,
257 const_cast<char**>(&dcmd[0]))); 256 const_cast<char**>(&dcmd[0])));
258 257
259 CHECK_EQ(0, test_compare_files(recon_file.Name(), 258 CHECK_EQ(0, test_compare_files(recon_file.Name(),
260 target_file.Name())); 259 target_file.Name()));
261 } 260 }
262 261
262 // Similar to xd3_process_memory, with support for test Options.
263 // Exercises xd3_process_stream.
264 int TestProcessMemory (int is_encode,
265 int (*func) (xd3_stream *),
266 const uint8_t *input,
267 usize_t input_size,
268 const uint8_t *source,
269 usize_t source_size,
270 uint8_t *output,
271 usize_t *output_size,
272 usize_t output_size_max,
273 const Options &options) {
274 xd3_stream stream;
275 xd3_config config;
276 xd3_source src;
277 int ret;
278
279 memset (& stream, 0, sizeof (stream));
280 memset (& config, 0, sizeof (config));
281
282 if (is_encode)
283 {
284 config.winsize = input_size;
285 config.iopt_size = options.iopt_size;
286 config.sprevsz = xd3_pow2_roundup (config.winsize);
287 }
288
289 if ((ret = xd3_config_stream (&stream, &config)) != 0)
290 {
291 goto exit;
292 }
293
294 if (source != NULL)
295 {
296 memset (& src, 0, sizeof (src));
297
298 src.blksize = source_size;
299 src.onblk = source_size;
300 src.curblk = source;
301 src.curblkno = 0;
302 src.max_winsize = source_size;
303
304 if ((ret = xd3_set_source_and_size (&stream, &src, source_size)) != 0)
305 {
306 goto exit;
307 }
308 }
309
310 if ((ret = xd3_process_stream (is_encode,
311 & stream,
312 func, 1,
313 input, input_size,
314 output,
315 output_size,
316 output_size_max)) != 0)
317 {
318 goto exit;
319 }
320
321 exit:
322 if (ret != 0)
323 {
324 IF_DEBUG2 (DP(RINT "test_process_memory: %d: %s\n", ret, stream.msg));
325 }
326 xd3_free_stream(&stream);
327 return ret;
328 }
329
330 void EncodeDecodeAPI(const FileSpec &spec0, const FileSpec &spec1,
331 Block *delta, const Options &options) {
332 Block from;
333 Block to;
334 spec0.Get(&from, 0, spec0.Size());
335 spec1.Get(&to, 0, spec1.Size());
336
337 delta->SetSize(to.Size() * 1.5);
338 usize_t out_size;
339 int enc_ret = TestProcessMemory(true,
340 &xd3_encode_input,
341 to.Data(),
342 to.Size(),
343 from.Data(),
344 from.Size(),
345 delta->Data(),
346 &out_size,
347 delta->Size(),
348 options);
349 CHECK_EQ(0, enc_ret);
350 delta->SetSize(out_size);
351
352 Block recon;
353 recon.SetSize(to.Size());
354 usize_t recon_size;
355 int dec_ret = xd3_decode_memory(delta->Data(),
356 delta->Size(),
357 from.Data(),
358 from.Size(),
359 recon.Data(),
360 &recon_size,
361 recon.Size(),
362 0);
363 CHECK_EQ(0, dec_ret);
364 CHECK_EQ(0, CmpDifferentBlockBytes(to, recon));
365 }
366
263 ////////////////////////////////////////////////////////////////////// 367 //////////////////////////////////////////////////////////////////////
264 368
265 void TestRandomNumbers() { 369 void TestRandomNumbers() {
@@ -370,7 +474,7 @@ void TestFirstByte() {
370 } 474 }
371 spec0.GenerateFixedSize(size); 475 spec0.GenerateFixedSize(size);
372 spec0.ModifyTo(Modify1stByte(), &spec1); 476 spec0.ModifyTo(Modify1stByte(), &spec1);
373 InMemoryEncodeDecode(spec0, spec1, NULL); 477 InMemoryEncodeDecode(spec0, spec1, NULL, Options());
374 } 478 }
375} 479}
376 480
@@ -406,7 +510,7 @@ void TestModifyMutator() {
406 // pass. 510 // pass.
407 CHECK_GE(diff, test_cases[i].size - (2 * test_cases[i].size / 256)); 511 CHECK_GE(diff, test_cases[i].size - (2 * test_cases[i].size / 256));
408 512
409 InMemoryEncodeDecode(spec0, spec1, NULL); 513 InMemoryEncodeDecode(spec0, spec1, NULL, Options());
410 } 514 }
411} 515}
412 516
@@ -439,7 +543,7 @@ void TestAddMutator() {
439 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size()); 543 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
440 544
441 Block coded; 545 Block coded;
442 InMemoryEncodeDecode(spec0, spec1, &coded); 546 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
443 547
444 Delta delta(coded); 548 Delta delta(coded);
445 CHECK_EQ(test_cases[i].expected_adds, 549 CHECK_EQ(test_cases[i].expected_adds,
@@ -476,7 +580,7 @@ void TestDeleteMutator() {
476 CHECK_EQ(spec0.Size() - test_cases[i].size, spec1.Size()); 580 CHECK_EQ(spec0.Size() - test_cases[i].size, spec1.Size());
477 581
478 Block coded; 582 Block coded;
479 InMemoryEncodeDecode(spec0, spec1, &coded); 583 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
480 584
481 Delta delta(coded); 585 Delta delta(coded);
482 CHECK_EQ(0, delta.AddedBytes()); 586 CHECK_EQ(0, delta.AddedBytes());
@@ -511,7 +615,7 @@ void TestCopyMutator() {
511 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size()); 615 CHECK_EQ(spec0.Size() + test_cases[i].size, spec1.Size());
512 616
513 Block coded; 617 Block coded;
514 InMemoryEncodeDecode(spec0, spec1, &coded); 618 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
515 619
516 Delta delta(coded); 620 Delta delta(coded);
517 CHECK_EQ(0, delta.AddedBytes()); 621 CHECK_EQ(0, delta.AddedBytes());
@@ -553,7 +657,7 @@ void TestMoveMutator() {
553 CHECK_EQ(spec0.Size(), spec1.Size()); 657 CHECK_EQ(spec0.Size(), spec1.Size());
554 658
555 Block coded; 659 Block coded;
556 InMemoryEncodeDecode(spec0, spec1, &coded); 660 InMemoryEncodeDecode(spec0, spec1, &coded, Options());
557 661
558 Delta delta(coded); 662 Delta delta(coded);
559 CHECK_EQ(0, delta.AddedBytes()); 663 CHECK_EQ(0, delta.AddedBytes());
@@ -596,7 +700,7 @@ void TestOverwriteMutator() {
596 700
597// Note: this test is written to expose a problem, but the problem was 701// Note: this test is written to expose a problem, but the problem was
598// only exposed with BLOCK_SIZE = 128. 702// only exposed with BLOCK_SIZE = 128.
599void TestNonBlockingProgress() { 703void TestNonBlocking() {
600 MTRandom rand; 704 MTRandom rand;
601 FileSpec spec0(&rand); 705 FileSpec spec0(&rand);
602 FileSpec spec1(&rand); 706 FileSpec spec1(&rand);
@@ -635,7 +739,7 @@ void TestNonBlockingProgress() {
635 739
636 spec0.ModifyTo(ChangeListMutator(ctl), &spec2); 740 spec0.ModifyTo(ChangeListMutator(ctl), &spec2);
637 741
638 InMemoryEncodeDecode(spec1, spec2, NULL); 742 InMemoryEncodeDecode(spec1, spec2, NULL, Options());
639} 743}
640 744
641void TestEmptyInMemory() { 745void TestEmptyInMemory() {
@@ -647,7 +751,7 @@ void TestEmptyInMemory() {
647 spec0.GenerateFixedSize(0); 751 spec0.GenerateFixedSize(0);
648 spec1.GenerateFixedSize(0); 752 spec1.GenerateFixedSize(0);
649 753
650 InMemoryEncodeDecode(spec0, spec1, &block); 754 InMemoryEncodeDecode(spec0, spec1, &block, Options());
651 755
652 Delta delta(block); 756 Delta delta(block);
653 CHECK_LT(0, block.Size()); 757 CHECK_LT(0, block.Size());
@@ -663,12 +767,138 @@ void TestBlockInMemory() {
663 spec0.GenerateFixedSize(Constants::BLOCK_SIZE); 767 spec0.GenerateFixedSize(Constants::BLOCK_SIZE);
664 spec1.GenerateFixedSize(Constants::BLOCK_SIZE); 768 spec1.GenerateFixedSize(Constants::BLOCK_SIZE);
665 769
666 InMemoryEncodeDecode(spec0, spec1, &block); 770 InMemoryEncodeDecode(spec0, spec1, &block, Options());
667 771
668 Delta delta(block); 772 Delta delta(block);
669 CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows()); 773 CHECK_EQ(spec1.Blocks(Constants::WINDOW_SIZE), delta.Windows());
670} 774}
671 775
776void TestSmallStride() {
777 MTRandom rand;
778 FileSpec spec0(&rand);
779 usize_t size = Constants::BLOCK_SIZE * 4;
780 spec0.GenerateFixedSize(size);
781
782 /* TODO Need to study the actual causes of missed adds for tests
783 * less than 30 bytes. */
784 const int s = 30;
785 usize_t adds = 0;
786 ChangeList cl;
787 for (usize_t j = s; j < size; j += s, ++adds)
788 {
789 cl.push_back(Change(Change::MODIFY, 1, j));
790 }
791
792 FileSpec spec1(&rand);
793 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
794
795 Options options;
796 options.encode_srcwin_maxsz = size;
797 options.iopt_size = 128;
798 options.smatch_cfg = XD3_SMATCH_SLOW;
799 options.size_known = false;
800
801 Block block;
802 InMemoryEncodeDecode(spec0, spec1, &block, options);
803 Delta delta(block);
804
805 // Allow an additional two byte of add per window
806 usize_t allowance = 2 * size / Constants::WINDOW_SIZE;
807 CHECK_GE(adds + allowance, delta.AddedBytes());
808}
809
810void TestCopyWindow() {
811 // Construct an input that has many copies, to fill the IOPT buffer
812 // and force a source window decision. "srclen" may be set to a
813 // value that goes beyond the end-of-source.
814 const int clen = 16;
815 const int size = 4096;
816 const int nmov = size / clen;
817 MTRandom rand;
818 FileSpec spec0(&rand);
819 ChangeList cl;
820
821 spec0.GenerateFixedSize(size);
822
823 for (int j = 0; j < nmov; j += 2)
824 {
825 cl.push_back(Change(Change::MOVE,
826 clen, (j + 1) * clen, j * clen));
827 }
828
829 FileSpec spec1(&rand);
830 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
831
832 Options options;
833 options.encode_srcwin_maxsz = size;
834 options.iopt_size = 128;
835 options.smatch_cfg = XD3_SMATCH_SLOW;
836 options.size_known = false;
837
838 Block block1;
839 InMemoryEncodeDecode(spec0, spec1, &block1, options);
840 Delta delta1(block1);
841 CHECK_EQ(0, delta1.AddedBytes());
842
843 Block block2;
844 InMemoryEncodeDecode(spec1, spec0, &block2, options);
845 Delta delta2(block2);
846 CHECK_EQ(0, delta2.AddedBytes());
847
848 Block block3;
849 Block block4;
850 EncodeDecodeAPI(spec0, spec1, &block3, options);
851 EncodeDecodeAPI(spec1, spec0, &block4, options);
852}
853
854void TestCopyFromEnd() {
855 // Copies from the end of the source buffer, which reach a block
856 // boundary end-of-file.
857 const int size = 4096;
858 const int clen = 16;
859 const int nmov = (size / 2) / clen;
860 MTRandom rand;
861 FileSpec spec0(&rand);
862 ChangeList cl;
863
864 spec0.GenerateFixedSize(size);
865
866 cl.push_back(Change(Change::MODIFY, 2012, 2048));
867
868 for (int j = 0; j < nmov; j += 2)
869 {
870 cl.push_back(Change(Change::MOVE,
871 clen, (j + 1) * clen, j * clen));
872 }
873
874 cl.push_back(Change(Change::COPYOVER, 28, 4068, 3000));
875 cl.push_back(Change(Change::COPYOVER, 30, 4066, 3100));
876 cl.push_back(Change(Change::COPYOVER, 32, 4064, 3200));
877
878 FileSpec spec1(&rand);
879 spec0.ModifyTo(ChangeListMutator(cl), &spec1);
880
881 Options options;
882 options.encode_srcwin_maxsz = size;
883 options.iopt_size = 128;
884 options.smatch_cfg = XD3_SMATCH_SLOW;
885
886 Block block1;
887 InMemoryEncodeDecode(spec0, spec1, &block1, options);
888 Delta delta1(block1);
889 CHECK_GE(2000, delta1.AddedBytes());
890
891 Block block2;
892 InMemoryEncodeDecode(spec1, spec0, &block2, options);
893 Delta delta2(block2);
894 CHECK_LE(2000, delta2.AddedBytes());
895
896 Block block3;
897 Block block4;
898 EncodeDecodeAPI(spec0, spec1, &block3, options);
899 EncodeDecodeAPI(spec1, spec0, &block4, options);
900}
901
672void TestHalfBlockCopy() { 902void TestHalfBlockCopy() {
673 MTRandom rand; 903 MTRandom rand;
674 FileSpec spec0(&rand); 904 FileSpec spec0(&rand);
@@ -694,11 +924,11 @@ void TestHalfBlockCopy() {
694 Constants::BLOCK_SIZE)); 924 Constants::BLOCK_SIZE));
695 spec0.ModifyTo(ChangeListMutator(cl1), &spec1); 925 spec0.ModifyTo(ChangeListMutator(cl1), &spec1);
696 926
697 const int onecopy_adds = 927 const int onecopy_adds =
698 4 * Constants::BLOCK_SIZE - Constants::BLOCK_SIZE / 2; 928 4 * Constants::BLOCK_SIZE - Constants::BLOCK_SIZE / 2;
699 const int nocopy_adds = 4 * Constants::BLOCK_SIZE; 929 const int nocopy_adds = 4 * Constants::BLOCK_SIZE;
700 930
701 // Note the case b=4 is contrived: the caller should use a single block 931 // Note the case b=4 is contrived: the caller should use a single block
702 // containing the entire source, if possible. 932 // containing the entire source, if possible.
703 for (int b = 1; b <= 4; b++) 933 for (int b = 1; b <= 4; b++)
704 { 934 {
@@ -721,7 +951,7 @@ void TestHalfBlockCopy() {
721 { 951 {
722 // For small-block inputs, the entire file is read into one 952 // For small-block inputs, the entire file is read into one
723 // block (the min source window size is 16kB). 953 // block (the min source window size is 16kB).
724 // 954 //
725 // For large blocks, at least 3 blocks of source window are 955 // For large blocks, at least 3 blocks of source window are
726 // needed. 956 // needed.
727 CHECK_EQ(onecopy_adds, delta1.AddedBytes()); 957 CHECK_EQ(onecopy_adds, delta1.AddedBytes());
@@ -760,7 +990,7 @@ void FourWayMergeTest(const FileSpec &spec0,
760 TmpFile f0, f1, f2, f3; 990 TmpFile f0, f1, f2, f3;
761 ExtFile d01, d12, d23; 991 ExtFile d01, d12, d23;
762 Options options; 992 Options options;
763 options.encode_srcwin_maxsz = 993 options.encode_srcwin_maxsz =
764 std::max(spec0.Size(), options.encode_srcwin_maxsz); 994 std::max(spec0.Size(), options.encode_srcwin_maxsz);
765 995
766 spec0.WriteTmpFile(&f0); 996 spec0.WriteTmpFile(&f0);
@@ -975,12 +1205,15 @@ void UnitTest() {
975// These are Xdelta tests. 1205// These are Xdelta tests.
976template <class T> 1206template <class T>
977void MainTest() { 1207void MainTest() {
978 XPR(NT "Blocksize %"Q"u readsize %"Q"u windowsize %"Q"u\n", 1208 XPR(NT "Blocksize %"Q"u windowsize %"Q"u\n",
979 T::BLOCK_SIZE, T::READ_SIZE, T::WINDOW_SIZE); 1209 T::BLOCK_SIZE, T::WINDOW_SIZE);
980 Regtest<T> regtest; 1210 Regtest<T> regtest;
981 TEST(TestEmptyInMemory); 1211 TEST(TestEmptyInMemory);
982 TEST(TestBlockInMemory); 1212 TEST(TestBlockInMemory);
983 TEST(TestNonBlockingProgress); 1213 TEST(TestSmallStride);
1214 TEST(TestCopyWindow);
1215 TEST(TestCopyFromEnd);
1216 TEST(TestNonBlocking);
984 TEST(TestHalfBlockCopy); 1217 TEST(TestHalfBlockCopy);
985 TEST(TestMergeCommand1); 1218 TEST(TestMergeCommand1);
986 TEST(TestMergeCommand2); 1219 TEST(TestMergeCommand2);
@@ -988,7 +1221,7 @@ void MainTest() {
988 1221
989#undef TEST 1222#undef TEST
990 1223
991int main(int argc, char **argv) 1224int main(int argc, char **argv)
992{ 1225{
993 vector<const char*> mcmd; 1226 vector<const char*> mcmd;
994 string pn; 1227 string pn;
@@ -1004,7 +1237,6 @@ int main(int argc, char **argv)
1004 UnitTest<SmallBlock>(); 1237 UnitTest<SmallBlock>();
1005 MainTest<SmallBlock>(); 1238 MainTest<SmallBlock>();
1006 MainTest<MixedBlock>(); 1239 MainTest<MixedBlock>();
1007 MainTest<PrimeBlock>();
1008 MainTest<OversizeBlock>(); 1240 MainTest<OversizeBlock>();
1009 MainTest<LargeBlock>(); 1241 MainTest<LargeBlock>();
1010 1242
diff --git a/xdelta3/testing/sizes.h b/xdelta3/testing/sizes.h
index 18f46e9..223d359 100644
--- a/xdelta3/testing/sizes.h
+++ b/xdelta3/testing/sizes.h
@@ -77,54 +77,35 @@ const size_t BaseConstants::TEST_ROUNDS = 10;
77struct SmallBlock : public BaseConstants { 77struct SmallBlock : public BaseConstants {
78 static const xoff_t BLOCK_SIZE; 78 static const xoff_t BLOCK_SIZE;
79 static const size_t WINDOW_SIZE; 79 static const size_t WINDOW_SIZE;
80 static const size_t READ_SIZE;
81 typedef SmallSizes Sizes; 80 typedef SmallSizes Sizes;
82}; 81};
83 82
84const size_t SmallBlock::READ_SIZE = 1<<7;
85const xoff_t SmallBlock::BLOCK_SIZE = 1<<7; 83const xoff_t SmallBlock::BLOCK_SIZE = 1<<7;
86const size_t SmallBlock::WINDOW_SIZE = 1<<7; 84const size_t SmallBlock::WINDOW_SIZE = 1<<7;
87 85
88struct LargeBlock : public BaseConstants { 86struct LargeBlock : public BaseConstants {
89 static const xoff_t BLOCK_SIZE; 87 static const xoff_t BLOCK_SIZE;
90 static const size_t WINDOW_SIZE; 88 static const size_t WINDOW_SIZE;
91 static const size_t READ_SIZE;
92 typedef LargeSizes Sizes; 89 typedef LargeSizes Sizes;
93}; 90};
94 91
95const size_t LargeBlock::READ_SIZE = (1 << 13);
96const xoff_t LargeBlock::BLOCK_SIZE = (1 << 13); 92const xoff_t LargeBlock::BLOCK_SIZE = (1 << 13);
97const size_t LargeBlock::WINDOW_SIZE = (1 << 13); 93const size_t LargeBlock::WINDOW_SIZE = (1 << 13);
98 94
99struct MixedBlock : public BaseConstants { 95struct MixedBlock : public BaseConstants {
100 static const xoff_t BLOCK_SIZE; 96 static const xoff_t BLOCK_SIZE;
101 static const size_t WINDOW_SIZE; 97 static const size_t WINDOW_SIZE;
102 static const size_t READ_SIZE;
103 typedef SmallSizes Sizes; 98 typedef SmallSizes Sizes;
104}; 99};
105 100
106const size_t MixedBlock::READ_SIZE = 1<<6;
107const xoff_t MixedBlock::BLOCK_SIZE = 1<<7; 101const xoff_t MixedBlock::BLOCK_SIZE = 1<<7;
108const size_t MixedBlock::WINDOW_SIZE = 1<<8; 102const size_t MixedBlock::WINDOW_SIZE = 1<<8;
109 103
110struct OversizeBlock : public BaseConstants { 104struct OversizeBlock : public BaseConstants {
111 static const xoff_t BLOCK_SIZE; 105 static const xoff_t BLOCK_SIZE;
112 static const size_t WINDOW_SIZE; 106 static const size_t WINDOW_SIZE;
113 static const size_t READ_SIZE;
114 typedef SmallSizes Sizes; 107 typedef SmallSizes Sizes;
115}; 108};
116 109
117const size_t OversizeBlock::READ_SIZE = (1<<6) + (1<<7);
118const xoff_t OversizeBlock::BLOCK_SIZE = 1<<8; 110const xoff_t OversizeBlock::BLOCK_SIZE = 1<<8;
119const size_t OversizeBlock::WINDOW_SIZE = 1<<7; 111const size_t OversizeBlock::WINDOW_SIZE = 1<<7;
120
121struct PrimeBlock : public BaseConstants {
122 static const xoff_t BLOCK_SIZE;
123 static const size_t WINDOW_SIZE;
124 static const size_t READ_SIZE;
125 typedef SmallSizes Sizes;
126};
127
128const size_t PrimeBlock::READ_SIZE = 71;
129const xoff_t PrimeBlock::BLOCK_SIZE = 512; // Must be a power-of-2
130const size_t PrimeBlock::WINDOW_SIZE = 73;
diff --git a/xdelta3/xdelta3-djw.h b/xdelta3/xdelta3-djw.h
index d6e2881..422e06a 100644
--- a/xdelta3/xdelta3-djw.h
+++ b/xdelta3/xdelta3-djw.h
@@ -869,7 +869,7 @@ xd3_encode_huff (xd3_stream *stream,
869 usize_t groups, sector_size; 869 usize_t groups, sector_size;
870 bit_state bstate = BIT_STATE_ENCODE_INIT; 870 bit_state bstate = BIT_STATE_ENCODE_INIT;
871 xd3_output *in; 871 xd3_output *in;
872 int output_bits; 872 usize_t output_bits;
873 usize_t input_bits; 873 usize_t input_bits;
874 usize_t input_bytes; 874 usize_t input_bytes;
875 usize_t initial_offset = output->next; 875 usize_t initial_offset = output->next;
diff --git a/xdelta3/xdelta3-internal.h b/xdelta3/xdelta3-internal.h
index 8e9e25b..35de56b 100644
--- a/xdelta3/xdelta3-internal.h
+++ b/xdelta3/xdelta3-internal.h
@@ -47,6 +47,17 @@ uint32_t xd3_large_cksum_update (uint32_t cksum,
47 const uint8_t *base, 47 const uint8_t *base,
48 usize_t look); 48 usize_t look);
49int xd3_encode_init_full (xd3_stream *stream); 49int xd3_encode_init_full (xd3_stream *stream);
50size_t xd3_pow2_roundup (size_t x);
51int xd3_process_stream (int is_encode,
52 xd3_stream *stream,
53 int (*func) (xd3_stream *),
54 int close_stream,
55 const uint8_t *input,
56 usize_t input_size,
57 uint8_t *output,
58 usize_t *output_size,
59 usize_t output_size_max);
60
50#if PYTHON_MODULE || SWIG_MODULE || NOT_MAIN 61#if PYTHON_MODULE || SWIG_MODULE || NOT_MAIN
51int xd3_main_cmdline (int argc, char **argv); 62int xd3_main_cmdline (int argc, char **argv);
52#endif 63#endif
diff --git a/xdelta3/xdelta3.c b/xdelta3/xdelta3.c
index 1367d99..c2c8d3e 100644
--- a/xdelta3/xdelta3.c
+++ b/xdelta3/xdelta3.c
@@ -1551,7 +1551,7 @@ xd3_check_pow2 (xoff_t value, usize_t *logof)
1551 return XD3_INTERNAL; 1551 return XD3_INTERNAL;
1552} 1552}
1553 1553
1554static size_t 1554size_t
1555xd3_pow2_roundup (size_t x) 1555xd3_pow2_roundup (size_t x)
1556{ 1556{
1557 size_t i = 1; 1557 size_t i = 1;
@@ -2594,6 +2594,8 @@ xd3_getblk (xd3_stream *stream, xoff_t blkno)
2594 blkno, xd3_strerror (ret))); 2594 blkno, xd3_strerror (ret)));
2595 return ret; 2595 return ret;
2596 } 2596 }
2597 IF_DEBUG2 (DP(RINT "[getblk] read source block %"Q"u onblk "
2598 "%u blksize %u\n", blkno, source->onblk, source->blksize));
2597 } 2599 }
2598 2600
2599 if (blkno >= source->frontier_blkno) 2601 if (blkno >= source->frontier_blkno)
@@ -2629,8 +2631,6 @@ xd3_getblk (xd3_stream *stream, xoff_t blkno)
2629 } 2631 }
2630 2632
2631 XD3_ASSERT (source->curblk != NULL); 2633 XD3_ASSERT (source->curblk != NULL);
2632 IF_DEBUG2 (DP(RINT "[getblk] read source block %"Q"u onblk %u blksize %u\n",
2633 blkno, source->onblk, source->blksize));
2634 2634
2635 if (blkno == source->max_blkno) 2635 if (blkno == source->max_blkno)
2636 { 2636 {
@@ -3697,8 +3697,8 @@ xd3_encode_init (xd3_stream *stream, int full_init)
3697 * identical or short inputs require no table allocation. */ 3697 * identical or short inputs require no table allocation. */
3698 if (large_comp) 3698 if (large_comp)
3699 { 3699 {
3700 usize_t hash_values = (stream->src->max_winsize / 3700 usize_t hash_values = (2 * stream->src->max_winsize) /
3701 stream->smatcher.large_step); 3701 stream->smatcher.large_step;
3702 3702
3703 xd3_size_hashtable (stream, 3703 xd3_size_hashtable (stream,
3704 hash_values, 3704 hash_values,
@@ -4036,7 +4036,7 @@ xd3_encode_input (xd3_stream *stream)
4036 Client convenience functions 4036 Client convenience functions
4037 ******************************************************************/ 4037 ******************************************************************/
4038 4038
4039static int 4039int
4040xd3_process_stream (int is_encode, 4040xd3_process_stream (int is_encode,
4041 xd3_stream *stream, 4041 xd3_stream *stream,
4042 int (*func) (xd3_stream *), 4042 int (*func) (xd3_stream *),
@@ -4060,14 +4060,15 @@ xd3_process_stream (int is_encode,
4060 for (;;) 4060 for (;;)
4061 { 4061 {
4062 int ret; 4062 int ret;
4063 switch((ret = func (stream))) 4063 switch ((ret = func (stream)))
4064 { 4064 {
4065 case XD3_OUTPUT: { /* memcpy below */ break; } 4065 case XD3_OUTPUT: { /* memcpy below */ break; }
4066 case XD3_INPUT: { 4066 case XD3_INPUT: {
4067 n = min(stream->winsize, input_size - ipos); 4067 n = min(stream->winsize, input_size - ipos);
4068 if (n == 0) { 4068 if (n == 0)
4069 goto done; 4069 {
4070 } 4070 goto done;
4071 }
4071 xd3_avail_input (stream, input + ipos, n); 4072 xd3_avail_input (stream, input + ipos, n);
4072 ipos += n; 4073 ipos += n;
4073 continue; 4074 continue;
@@ -4077,7 +4078,11 @@ xd3_process_stream (int is_encode,
4077 case XD3_WINFINISH: { /* ignore */ continue; } 4078 case XD3_WINFINISH: { /* ignore */ continue; }
4078 case XD3_GETSRCBLK: 4079 case XD3_GETSRCBLK:
4079 { 4080 {
4080 stream->msg = "stream requires source input"; 4081 /* When the getblk function is NULL, it is necessary to
4082 * provide the complete source as a single block using
4083 * xd3_set_source_and_size, otherwise this error. The
4084 * library should never ask for another source block. */
4085 stream->msg = "library requested source block";
4081 return XD3_INTERNAL; 4086 return XD3_INTERNAL;
4082 } 4087 }
4083 case 0: 4088 case 0:
@@ -4109,7 +4114,6 @@ xd3_process_stream (int is_encode,
4109static int 4114static int
4110xd3_process_memory (int is_encode, 4115xd3_process_memory (int is_encode,
4111 int (*func) (xd3_stream *), 4116 int (*func) (xd3_stream *),
4112 int close_stream,
4113 const uint8_t *input, 4117 const uint8_t *input,
4114 usize_t input_size, 4118 usize_t input_size,
4115 const uint8_t *source, 4119 const uint8_t *source,
@@ -4137,8 +4141,6 @@ xd3_process_memory (int is_encode,
4137 if (is_encode) 4141 if (is_encode)
4138 { 4142 {
4139 config.winsize = min(input_size, (usize_t) XD3_DEFAULT_WINSIZE); 4143 config.winsize = min(input_size, (usize_t) XD3_DEFAULT_WINSIZE);
4140 config.iopt_size = min(input_size / 32, XD3_DEFAULT_IOPT_SIZE);
4141 config.iopt_size = max(config.iopt_size, 128U);
4142 config.sprevsz = xd3_pow2_roundup (config.winsize); 4144 config.sprevsz = xd3_pow2_roundup (config.winsize);
4143 } 4145 }
4144 4146
@@ -4205,7 +4207,7 @@ xd3_decode_memory (const uint8_t *input,
4205 usize_t *output_size, 4207 usize_t *output_size,
4206 usize_t output_size_max, 4208 usize_t output_size_max,
4207 int flags) { 4209 int flags) {
4208 return xd3_process_memory (0, & xd3_decode_input, 1, 4210 return xd3_process_memory (0, & xd3_decode_input,
4209 input, input_size, 4211 input, input_size,
4210 source, source_size, 4212 source, source_size,
4211 output, output_size, output_size_max, 4213 output, output_size, output_size_max,
@@ -4236,7 +4238,7 @@ xd3_encode_memory (const uint8_t *input,
4236 usize_t *output_size, 4238 usize_t *output_size,
4237 usize_t output_size_max, 4239 usize_t output_size_max,
4238 int flags) { 4240 int flags) {
4239 return xd3_process_memory (1, & xd3_encode_input, 1, 4241 return xd3_process_memory (1, & xd3_encode_input,
4240 input, input_size, 4242 input, input_size,
4241 source, source_size, 4243 source, source_size,
4242 output, output_size, output_size_max, 4244 output, output_size, output_size_max,
@@ -4396,8 +4398,18 @@ xd3_srcwin_setup (xd3_stream *stream)
4396 src->srclen = max ((usize_t) length, 4398 src->srclen = max ((usize_t) length,
4397 stream->avail_in + (stream->avail_in >> 2)); 4399 stream->avail_in + (stream->avail_in >> 2));
4398 4400
4399 /* OPT: If we know the source size, it might be possible to reduce 4401 if (src->eof_known)
4400 * srclen. */ 4402 {
4403 /* Note: if the source size is known, we must reduce srclen or
4404 * code that expects to pass a single block w/ getblk == NULL
4405 * will not function, as the code will return GETSRCBLK asking
4406 * for the second block. */
4407 src->srclen = min (src->srclen, xd3_source_eof(src) - src->srcbase);
4408 }
4409
4410 IF_DEBUG1 (DP(RINT "[srcwin_setup_constrained] base %llu len %llu\n",
4411 src->srcbase, src->srclen));
4412
4401 XD3_ASSERT (src->srclen); 4413 XD3_ASSERT (src->srclen);
4402 done: 4414 done:
4403 /* Set the taroff. This convenience variable is used even when 4415 /* Set the taroff. This convenience variable is used even when
@@ -4695,11 +4707,7 @@ xd3_source_extend_match (xd3_stream *stream)
4695 4707
4696 if ((ret = xd3_getblk (stream, tryblk))) 4708 if ((ret = xd3_getblk (stream, tryblk)))
4697 { 4709 {
4698 /* if search went too far back, continue forward. */ 4710 XD3_ASSERT (ret != XD3_TOOFARBACK);
4699 if (ret == XD3_TOOFARBACK)
4700 {
4701 break;
4702 }
4703 4711
4704 /* could be a XD3_GETSRCBLK failure. */ 4712 /* could be a XD3_GETSRCBLK failure. */
4705 return ret; 4713 return ret;