| // Copyright 2008 Google Inc. |
| // Author: Lincoln Smith |
| // |
| // 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 <config.h> |
| #include "vcdecoder_test.h" |
| #include <string.h> // strlen |
| #include "checksum.h" |
| #include "codetable.h" |
| #include "testing.h" |
| #include "varint_bigendian.h" |
| #include "vcdiff_defs.h" |
| |
| namespace open_vcdiff { |
| |
| const char VCDiffDecoderTest::kStandardFileHeader[] = { |
| 0xD6, // 'V' | 0x80 |
| 0xC3, // 'C' | 0x80 |
| 0xC4, // 'D' | 0x80 |
| 0x00, // Draft standard version number |
| 0x00 // Hdr_Indicator: no custom code table, no compression |
| }; |
| |
| const char VCDiffDecoderTest::kInterleavedFileHeader[] = { |
| 0xD6, // 'V' | 0x80 |
| 0xC3, // 'C' | 0x80 |
| 0xC4, // 'D' | 0x80 |
| 'S', // SDCH version code |
| 0x00 // Hdr_Indicator: no custom code table, no compression |
| }; |
| |
| const char VCDiffDecoderTest::kDictionary[] = |
| "\"Just the place for a Snark!\" the Bellman cried,\n" |
| "As he landed his crew with care;\n" |
| "Supporting each man on the top of the tide\n" |
| "By a finger entwined in his hair.\n"; |
| |
| const char VCDiffDecoderTest::kExpectedTarget[] = |
| "\"Just the place for a Snark! I have said it twice:\n" |
| "That alone should encourage the crew.\n" |
| "Just the place for a Snark! I have said it thrice:\n" |
| "What I tell you three times is true.\"\n"; |
| |
| VCDiffDecoderTest::VCDiffDecoderTest() : fuzzer_(0), fuzzed_byte_position_(0) { |
| dictionary_ = kDictionary; |
| expected_target_ = kExpectedTarget; |
| } |
| |
| void VCDiffDecoderTest::SetUp() { |
| InitializeDeltaFile(); |
| } |
| |
| void VCDiffDecoderTest::UseStandardFileHeader() { |
| delta_file_header_.assign(kStandardFileHeader, |
| sizeof(kStandardFileHeader)); |
| } |
| |
| void VCDiffDecoderTest::UseInterleavedFileHeader() { |
| delta_file_header_.assign(kInterleavedFileHeader, |
| sizeof(kInterleavedFileHeader)); |
| } |
| |
| void VCDiffDecoderTest::InitializeDeltaFile() { |
| delta_file_ = delta_file_header_ + delta_window_header_ + delta_window_body_; |
| } |
| |
| char VCDiffDecoderTest::GetByteFromStringLength(const char* s, |
| int which_byte) { |
| char varint_buf[VarintBE<int32_t>::kMaxBytes]; |
| VarintBE<int32_t>::Encode(static_cast<int32_t>(strlen(s)), varint_buf); |
| return varint_buf[which_byte]; |
| } |
| |
| void VCDiffDecoderTest::AddChecksum(VCDChecksum checksum) { |
| int32_t checksum_as_int32 = static_cast<int32_t>(checksum); |
| delta_window_header_[0] |= VCD_CHECKSUM; |
| VarintBE<int32_t>::AppendToString(checksum_as_int32, &delta_window_header_); |
| // Adjust delta window size to include checksum. |
| // This method wouldn't work if adding to the length caused the VarintBE |
| // value to spill over into another byte. Luckily, this test data happens |
| // not to cause such an overflow. |
| delta_window_header_[4] += VarintBE<int32_t>::Length(checksum_as_int32); |
| } |
| |
| void VCDiffDecoderTest::ComputeAndAddChecksum() { |
| AddChecksum(ComputeAdler32(expected_target_.data(), |
| expected_target_.size())); |
| } |
| |
| // Write the maximum expressible positive 32-bit VarintBE |
| // (0x7FFFFFFF) at the given offset in the delta window. |
| void VCDiffDecoderTest::WriteMaxVarintAtOffset(int offset, |
| int bytes_to_replace) { |
| static const char kMaxVarint[] = { 0x87, 0xFF, 0xFF, 0xFF, 0x7F }; |
| delta_file_.replace(delta_file_header_.size() + offset, |
| bytes_to_replace, |
| kMaxVarint, |
| sizeof(kMaxVarint)); |
| } |
| |
| // Write a negative 32-bit VarintBE (0x80000000) at the given offset |
| // in the delta window. |
| void VCDiffDecoderTest::WriteNegativeVarintAtOffset(int offset, |
| int bytes_to_replace) { |
| static const char kNegativeVarint[] = { 0x88, 0x80, 0x80, 0x80, 0x00 }; |
| delta_file_.replace(delta_file_header_.size() + offset, |
| bytes_to_replace, |
| kNegativeVarint, |
| sizeof(kNegativeVarint)); |
| } |
| |
| // Write a VarintBE that has too many continuation bytes |
| // at the given offset in the delta window. |
| void VCDiffDecoderTest::WriteInvalidVarintAtOffset(int offset, |
| int bytes_to_replace) { |
| static const char kInvalidVarint[] = { 0x87, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F }; |
| delta_file_.replace(delta_file_header_.size() + offset, |
| bytes_to_replace, |
| kInvalidVarint, |
| sizeof(kInvalidVarint)); |
| } |
| |
| bool VCDiffDecoderTest::FuzzOneByteInDeltaFile() { |
| static const struct Fuzzer { |
| char _and; |
| char _or; |
| char _xor; |
| } fuzzers[] = { |
| { 0xff, 0x80, 0x00 }, |
| { 0xff, 0xff, 0x00 }, |
| { 0xff, 0x00, 0x80 }, |
| { 0xff, 0x00, 0xff }, |
| { 0xff, 0x01, 0x00 }, |
| { 0x7f, 0x00, 0x00 }, |
| }; |
| |
| for (; fuzzer_ < (sizeof(fuzzers) / sizeof(fuzzers[0])); ++fuzzer_) { |
| for (; fuzzed_byte_position_ < delta_file_.size(); |
| ++fuzzed_byte_position_) { |
| char fuzzed_byte = (((delta_file_[fuzzed_byte_position_] |
| & fuzzers[fuzzer_]._and) |
| | fuzzers[fuzzer_]._or) |
| ^ fuzzers[fuzzer_]._xor); |
| if (fuzzed_byte != delta_file_[fuzzed_byte_position_]) { |
| delta_file_[fuzzed_byte_position_] = fuzzed_byte; |
| ++fuzzed_byte_position_; |
| return true; |
| } |
| } |
| fuzzed_byte_position_ = 0; |
| } |
| return false; |
| } |
| |
| const char VCDiffStandardDecoderTest::kWindowHeader[] = { |
| VCD_SOURCE, // Win_Indicator: take source from dictionary |
| FirstByteOfStringLength(kDictionary), // Source segment size |
| SecondByteOfStringLength(kDictionary), |
| 0x00, // Source segment position: start of dictionary |
| 0x79, // Length of the delta encoding |
| FirstByteOfStringLength(kExpectedTarget), // Size of the target window |
| SecondByteOfStringLength(kExpectedTarget), |
| 0x00, // Delta_indicator (no compression) |
| 0x64, // length of data for ADDs and RUNs |
| 0x0C, // length of instructions section |
| 0x03 // length of addresses for COPYs |
| }; |
| |
| const char VCDiffStandardDecoderTest::kWindowBody[] = { |
| // Data for ADDs: 1st section (length 61) |
| ' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ', |
| 'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n', |
| 'T', 'h', 'a', 't', ' ', |
| 'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', |
| 'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ', |
| 't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n', |
| // Data for ADDs: 2nd section (length 2) |
| 'h', 'r', |
| // Data for ADDs: 3rd section (length 9) |
| 'W', 'h', 'a', 't', ' ', |
| 'I', ' ', 't', 'e', |
| // Data for RUN: 4th section (length 1) |
| 'l', |
| // Data for ADD: 4th section (length 27) |
| ' ', 'y', 'o', 'u', ' ', |
| 't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ', |
| 't', 'r', 'u', 'e', '.', '\"', '\n', |
| // Instructions and sizes (length 13) |
| 0x13, // VCD_COPY mode VCD_SELF, size 0 |
| 0x1C, // Size of COPY (28) |
| 0x01, // VCD_ADD size 0 |
| 0x3D, // Size of ADD (61) |
| 0x23, // VCD_COPY mode VCD_HERE, size 0 |
| 0x2C, // Size of COPY (44) |
| 0xCB, // VCD_ADD size 2 + VCD_COPY mode NEAR(1), size 5 |
| 0x0A, // VCD_ADD size 9 |
| 0x00, // VCD_RUN size 0 |
| 0x02, // Size of RUN (2) |
| 0x01, // VCD_ADD size 0 |
| 0x1B, // Size of ADD (27) |
| // Addresses for COPYs (length 3) |
| 0x00, // Start of dictionary |
| 0x58, // HERE mode address for 2nd copy (27+61 back from here_address) |
| 0x2D // NEAR(1) mode address for 2nd copy (45 after prior address) |
| }; |
| |
| VCDiffStandardDecoderTest::VCDiffStandardDecoderTest() { |
| UseStandardFileHeader(); |
| delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader)); |
| delta_window_body_.assign(kWindowBody, sizeof(kWindowBody)); |
| } |
| |
| const char VCDiffInterleavedDecoderTest::kWindowHeader[] = { |
| VCD_SOURCE, // Win_Indicator: take source from dictionary |
| FirstByteOfStringLength(kDictionary), // Source segment size |
| SecondByteOfStringLength(kDictionary), |
| 0x00, // Source segment position: start of dictionary |
| 0x79, // Length of the delta encoding |
| FirstByteOfStringLength(kExpectedTarget), // Size of the target window |
| SecondByteOfStringLength(kExpectedTarget), |
| 0x00, // Delta_indicator (no compression) |
| 0x00, // length of data for ADDs and RUNs (unused) |
| 0x73, // length of interleaved section |
| 0x00 // length of addresses for COPYs (unused) |
| }; |
| |
| const char VCDiffInterleavedDecoderTest::kWindowBody[] = { |
| 0x13, // VCD_COPY mode VCD_SELF, size 0 |
| 0x1C, // Size of COPY (28) |
| 0x00, // Address of COPY: Start of dictionary |
| 0x01, // VCD_ADD size 0 |
| 0x3D, // Size of ADD (61) |
| // Data for ADD (length 61) |
| ' ', 'I', ' ', 'h', 'a', 'v', 'e', ' ', 's', 'a', 'i', 'd', ' ', |
| 'i', 't', ' ', 't', 'w', 'i', 'c', 'e', ':', '\n', |
| 'T', 'h', 'a', 't', ' ', |
| 'a', 'l', 'o', 'n', 'e', ' ', 's', 'h', 'o', 'u', 'l', 'd', ' ', |
| 'e', 'n', 'c', 'o', 'u', 'r', 'a', 'g', 'e', ' ', |
| 't', 'h', 'e', ' ', 'c', 'r', 'e', 'w', '.', '\n', |
| 0x23, // VCD_COPY mode VCD_HERE, size 0 |
| 0x2C, // Size of COPY (44) |
| 0x58, // HERE mode address (27+61 back from here_address) |
| 0xCB, // VCD_ADD size 2 + VCD_COPY mode NEAR(1), size 5 |
| // Data for ADDs: 2nd section (length 2) |
| 'h', 'r', |
| 0x2D, // NEAR(1) mode address (45 after prior address) |
| 0x0A, // VCD_ADD size 9 |
| // Data for ADDs: 3rd section (length 9) |
| 'W', 'h', 'a', 't', ' ', |
| 'I', ' ', 't', 'e', |
| 0x00, // VCD_RUN size 0 |
| 0x02, // Size of RUN (2) |
| // Data for RUN: 4th section (length 1) |
| 'l', |
| 0x01, // VCD_ADD size 0 |
| 0x1B, // Size of ADD (27) |
| // Data for ADD: 4th section (length 27) |
| ' ', 'y', 'o', 'u', ' ', |
| 't', 'h', 'r', 'e', 'e', ' ', 't', 'i', 'm', 'e', 's', ' ', 'i', 's', ' ', |
| 't', 'r', 'u', 'e', '.', '\"', '\n' |
| }; |
| |
| VCDiffInterleavedDecoderTest::VCDiffInterleavedDecoderTest() { |
| UseInterleavedFileHeader(); |
| delta_window_header_.assign(kWindowHeader, sizeof(kWindowHeader)); |
| delta_window_body_.assign(kWindowBody, sizeof(kWindowBody)); |
| } |
| |
| } // namespace open_vcdiff |