| // 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. |
| // |
| // Implements a Decoder for the format described in |
| // RFC 3284 - The VCDIFF Generic Differencing and Compression Data Format. |
| // The RFC text can be found at http://www.faqs.org/rfcs/rfc3284.html |
| // |
| // The RFC describes the possibility of using a secondary compressor |
| // to further reduce the size of each section of the VCDIFF output. |
| // That feature is not supported in this implementation of the encoder |
| // and decoder. |
| // No secondary compressor types have been publicly registered with |
| // the IANA at http://www.iana.org/assignments/vcdiff-comp-ids |
| // in the more than five years since the registry was created, so there |
| // is no standard set of compressor IDs which would be generated by other |
| // encoders or accepted by other decoders. |
| |
| #include <config.h> |
| #include "google/vcdecoder.h" |
| #include <stddef.h> // size_t, ptrdiff_t |
| #include <stdint.h> // int32_t |
| #include <string.h> // memcpy, memset |
| #include <memory> // auto_ptr |
| #include <string> |
| #include "addrcache.h" |
| #include "checksum.h" |
| #include "codetable.h" |
| #include "decodetable.h" |
| #include "headerparser.h" |
| #include "logging.h" |
| #include "google/output_string.h" |
| #include "varint_bigendian.h" |
| #include "vcdiff_defs.h" |
| |
| namespace open_vcdiff { |
| |
| // This class is used to parse delta file windows as described |
| // in RFC sections 4.2 and 4.3. Its methods are not thread-safe. |
| // |
| // Here is the window format copied from the RFC: |
| // |
| // Window1 |
| // Win_Indicator - byte |
| // [Source segment size] - integer |
| // [Source segment position] - integer |
| // The delta encoding of the target window |
| // Length of the delta encoding - integer |
| // The delta encoding |
| // Size of the target window - integer |
| // Delta_Indicator - byte |
| // Length of data for ADDs and RUNs - integer |
| // Length of instructions and sizes - integer |
| // Length of addresses for COPYs - integer |
| // Data section for ADDs and RUNs - array of bytes |
| // Instructions and sizes section - array of bytes |
| // Addresses section for COPYs - array of bytes |
| // Window2 |
| // ... |
| // |
| // Sample usage: |
| // |
| // VCDiffDeltaFileWindow delta_window_; |
| // delta_window_.Init(parent); |
| // ParseableChunk parseable_chunk(input_buffer, |
| // input_size, |
| // leftover_unencoded_bytes); |
| // switch (delta_window_.DecodeWindows(&parseable_chunk)) { |
| // case RESULT_END_OF_DATA: |
| // <Read more input and retry DecodeWindows later.> |
| // case RESULT_ERROR: |
| // <Handle error case. An error log message has already been generated.> |
| // } |
| // |
| // DecodeWindows consumes as many windows from the input as it can. It only |
| // needs to be placed within a loop if the loop is used to obtain more input |
| // (delta file) data. |
| // |
| class VCDiffDeltaFileWindow { |
| public: |
| VCDiffDeltaFileWindow(); |
| ~VCDiffDeltaFileWindow(); |
| |
| // Init() should be called immediately after constructing the |
| // VCDiffDeltaFileWindow(). It must be called before DecodeWindows() can be |
| // invoked, or an error will occur. |
| void Init(VCDiffStreamingDecoderImpl* parent); |
| |
| // Resets the pointers to the data sections in the current window. |
| void Reset(); |
| |
| bool UseCodeTable(const VCDiffCodeTableData& code_table_data, |
| unsigned char max_mode) { |
| return reader_.UseCodeTable(code_table_data, max_mode); |
| } |
| |
| // Decodes as many delta windows as possible using the input data from |
| // *parseable_chunk. Appends the decoded target windows to |
| // parent_->decoded_target(). Returns RESULT_SUCCESS on success, or |
| // RESULT_END_OF_DATA if the end of input was reached before the entire window |
| // could be decoded and more input is expected (only possible if |
| // IsInterleaved() is true), or RESULT_ERROR if an error occurred during |
| // decoding. In the RESULT_ERROR case, the value of parseable_chunk->pointer_ |
| // is undefined; otherwise, parseable_chunk->Advance() is called to point to |
| // the input data position just after the data that has been decoded. |
| // |
| // If planned_target_file_size is not set to kUnlimitedBytes, then the decoder |
| // expects *exactly* this number of target bytes to be decoded from one or |
| // more delta file windows. If this number is met exactly after finishing a |
| // delta window, this function will return RESULT_SUCCESS without processing |
| // any more bytes from data_pointer. If this number is exceeded while |
| // decoding a window, but was not met before starting that window, |
| // then RESULT_ERROR will be returned. |
| // |
| VCDiffResult DecodeWindows(ParseableChunk* parseable_chunk); |
| |
| bool FoundWindowHeader() const { |
| return found_header_; |
| } |
| |
| bool MoreDataExpected() const { |
| // When parsing an interleaved-format delta file, |
| // every time DecodeBody() exits, interleaved_bytes_expected_ |
| // will be decremented by the number of bytes parsed. If it |
| // reaches zero, then there is no more data expected because |
| // the size of the interleaved section (given in the window |
| // header) has been reached. |
| return IsInterleaved() && (interleaved_bytes_expected_ > 0); |
| } |
| |
| size_t target_window_start_pos() const { return target_window_start_pos_; } |
| |
| void set_target_window_start_pos(size_t new_start_pos) { |
| target_window_start_pos_ = new_start_pos; |
| } |
| |
| // Returns the number of bytes remaining to be decoded in the target window. |
| // If not in the process of decoding a window, returns 0. |
| size_t TargetBytesRemaining(); |
| |
| private: |
| // Reads the header of the window section as described in RFC sections 4.2 and |
| // 4.3, up to and including the value "Length of addresses for COPYs". If the |
| // entire header is found, this function sets up the DeltaWindowSections |
| // instructions_and_sizes_, data_for_add_and_run_, and addresses_for_copy_ so |
| // that the decoder can begin decoding the opcodes in these sections. Returns |
| // RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA if the end of |
| // available data was reached before the entire header could be read. (The |
| // latter may be an error condition if there is no more data available.) |
| // Otherwise, returns RESULT_SUCCESS and advances parseable_chunk past the |
| // parsed header. |
| // |
| VCDiffResult ReadHeader(ParseableChunk* parseable_chunk); |
| |
| // After the window header has been parsed as far as the Delta_Indicator, |
| // this function is called to parse the following delta window header fields: |
| // |
| // Length of data for ADDs and RUNs - integer (VarintBE format) |
| // Length of instructions and sizes - integer (VarintBE format) |
| // Length of addresses for COPYs - integer (VarintBE format) |
| // |
| // If has_checksum_ is true, it also looks for the following element: |
| // |
| // Adler32 checksum - unsigned 32-bit integer (VarintBE format) |
| // |
| // It sets up the DeltaWindowSections instructions_and_sizes_, |
| // data_for_add_and_run_, and addresses_for_copy_. If the interleaved format |
| // is being used, all three sections will include the entire window body; if |
| // the standard format is used, three non-overlapping window sections will be |
| // defined. Returns RESULT_ERROR if an error occurred, or RESULT_END_OF_DATA |
| // if standard format is being used and there is not enough input data to read |
| // the entire window body. Otherwise, returns RESULT_SUCCESS. |
| VCDiffResult SetUpWindowSections(VCDiffHeaderParser* header_parser); |
| |
| // Decodes the body of the window section as described in RFC sections 4.3, |
| // including the sections "Data section for ADDs and RUNs", "Instructions |
| // and sizes section", and "Addresses section for COPYs". These sections |
| // must already have been set up by ReadWindowHeader(). Returns a |
| // non-negative value on success, or RESULT_END_OF_DATA if the end of input |
| // was reached before the entire window could be decoded (only possible if |
| // IsInterleaved() is true), or RESULT_ERROR if an error occurred during |
| // decoding. Appends as much of the decoded target window as possible to |
| // parent->decoded_target(). |
| // |
| int DecodeBody(ParseableChunk* parseable_chunk); |
| |
| // Returns the number of bytes already decoded into the target window. |
| size_t TargetBytesDecoded(); |
| |
| // Decodes a single ADD instruction, updating parent_->decoded_target_. |
| VCDiffResult DecodeAdd(size_t size); |
| |
| // Decodes a single RUN instruction, updating parent_->decoded_target_. |
| VCDiffResult DecodeRun(size_t size); |
| |
| // Decodes a single COPY instruction, updating parent_->decoded_target_. |
| VCDiffResult DecodeCopy(size_t size, unsigned char mode); |
| |
| // When using the interleaved format, this function is called both on parsing |
| // the header and on resuming after a RESULT_END_OF_DATA was returned from a |
| // previous call to DecodeBody(). It sets up all three section pointers to |
| // reference the same interleaved stream of instructions, sizes, addresses, |
| // and data. These pointers must be reset every time that work resumes on a |
| // delta window, because the input data string may have been changed or |
| // resized since DecodeBody() last returned. |
| void UpdateInterleavedSectionPointers(const char* data_pos, |
| const char* data_end) { |
| const ptrdiff_t available_data = data_end - data_pos; |
| // Don't read past the end of currently-available data |
| if (available_data > interleaved_bytes_expected_) { |
| instructions_and_sizes_.Init(data_pos, interleaved_bytes_expected_); |
| } else { |
| instructions_and_sizes_.Init(data_pos, available_data); |
| } |
| data_for_add_and_run_.Init(&instructions_and_sizes_); |
| addresses_for_copy_.Init(&instructions_and_sizes_); |
| } |
| |
| // If true, the interleaved format described in AllowInterleaved() is used |
| // for the current delta file. Only valid after ReadWindowHeader() has been |
| // called and returned a positive number (i.e., the whole header was parsed), |
| // but before the window has finished decoding. |
| // |
| bool IsInterleaved() const { |
| // If the sections are interleaved, both addresses_for_copy_ and |
| // data_for_add_and_run_ should point at instructions_and_sizes_. |
| return !addresses_for_copy_.IsOwned(); |
| } |
| |
| // Executes a single COPY or ADD instruction, appending data to |
| // parent_->decoded_target(). |
| void CopyBytes(const char* data, size_t size); |
| |
| // Executes a single RUN instruction, appending data to |
| // parent_->decoded_target(). |
| void RunByte(unsigned char byte, size_t size); |
| |
| // Advance *parseable_chunk to point to the current position in the |
| // instructions/sizes section. If interleaved format is used, then |
| // decrement the number of expected bytes in the instructions/sizes section |
| // by the number of instruction/size bytes parsed. |
| void UpdateInstructionPointer(ParseableChunk* parseable_chunk); |
| |
| // The parent object which was passed to Init(). |
| VCDiffStreamingDecoderImpl* parent_; |
| |
| // This value will be true if VCDiffDeltaFileWindow::ReadDeltaWindowHeader() |
| // has been called and succeeded in parsing the delta window header, but the |
| // entire window has not yet been decoded. |
| bool found_header_; |
| |
| // Contents and length of the current source window. source_segment_ptr_ |
| // will be non-NULL if (a) the window section header for the current window |
| // has been read, but the window has not yet finished decoding; or |
| // (b) the window did not specify a source segment. |
| const char* source_segment_ptr_; |
| size_t source_segment_length_; |
| |
| // The delta encoding window sections as defined in RFC section 4.3. |
| // The pointer for each section will be incremented as data is consumed and |
| // decoded from that section. If the interleaved format is used, |
| // data_for_add_and_run_ and addresses_for_copy_ will both point to |
| // instructions_and_sizes_; otherwise, they will be separate data sections. |
| // |
| DeltaWindowSection instructions_and_sizes_; |
| DeltaWindowSection data_for_add_and_run_; |
| DeltaWindowSection addresses_for_copy_; |
| |
| // The expected bytes left to decode in instructions_and_sizes_. Only used |
| // for the interleaved format. |
| int interleaved_bytes_expected_; |
| |
| // The expected length of the target window once it has been decoded. |
| size_t target_window_length_; |
| |
| // The index in decoded_target at which the first byte of the current |
| // target window was/will be written. |
| size_t target_window_start_pos_; |
| |
| // If has_checksum_ is true, then expected_checksum_ contains an Adler32 |
| // checksum of the target window data. This is an extension included in the |
| // VCDIFF 'S' (SDCH) format, but is not part of the RFC 3284 draft standard. |
| bool has_checksum_; |
| VCDChecksum expected_checksum_; |
| |
| VCDiffCodeTableReader reader_; |
| |
| // Making these private avoids implicit copy constructor & assignment operator |
| VCDiffDeltaFileWindow(const VCDiffDeltaFileWindow&); // NOLINT |
| void operator=(const VCDiffDeltaFileWindow&); |
| }; |
| |
| // *** Inline methods for VCDiffDeltaFileWindow |
| |
| inline VCDiffDeltaFileWindow::VCDiffDeltaFileWindow() : parent_(NULL) { |
| Reset(); |
| } |
| |
| inline VCDiffDeltaFileWindow::~VCDiffDeltaFileWindow() { } |
| |
| inline void VCDiffDeltaFileWindow::Init(VCDiffStreamingDecoderImpl* parent) { |
| parent_ = parent; |
| } |
| |
| class VCDiffStreamingDecoderImpl { |
| public: |
| typedef std::string string; |
| |
| // The default maximum target file size (and target window size) if |
| // SetMaximumTargetFileSize() is not called. |
| static const size_t kDefaultMaximumTargetFileSize = 67108864U; // 64 MB |
| |
| // The largest value that can be passed to SetMaximumTargetWindowSize(). |
| // Using a larger value will result in an error. |
| static const size_t kTargetSizeLimit = 2147483647U; // INT32_MAX |
| |
| // A constant that is the default value for planned_target_file_size_, |
| // indicating that the decoder does not have an expected length |
| // for the target data. |
| static const size_t kUnlimitedBytes = static_cast<size_t>(-3); |
| |
| VCDiffStreamingDecoderImpl(); |
| ~VCDiffStreamingDecoderImpl(); |
| |
| // Resets all member variables to their initial states. |
| void Reset(); |
| |
| // These functions are identical to their counterparts |
| // in VCDiffStreamingDecoder. |
| // |
| void StartDecoding(const char* dictionary_ptr, size_t dictionary_size); |
| |
| bool DecodeChunk(const char* data, |
| size_t len, |
| OutputStringInterface* output_string); |
| |
| bool FinishDecoding(); |
| |
| // If true, the version of VCDIFF used in the current delta file allows |
| // for the interleaved format, in which instructions, addresses and data |
| // are all sent interleaved in the instructions section of each window |
| // rather than being sent in separate sections. This is not part of |
| // the VCDIFF draft standard, so we've defined a special version code |
| // 'S' which implies that this feature is available. Even if interleaving |
| // is supported, it is not mandatory; interleaved format will be implied |
| // if the address and data sections are both zero-length. |
| // |
| bool AllowInterleaved() const { return vcdiff_version_code_ == 'S'; } |
| |
| // If true, the version of VCDIFF used in the current delta file allows |
| // each delta window to contain an Adler32 checksum of the target window data. |
| // If the bit 0x08 (VCD_CHECKSUM) is set in the Win_Indicator flags, then |
| // this checksum will appear as a variable-length integer, just after the |
| // "length of addresses for COPYs" value and before the window data sections. |
| // It is possible for some windows in a delta file to use the checksum feature |
| // and for others not to use it (and leave the flag bit set to 0.) |
| // Just as with AllowInterleaved(), this extension is not part of the draft |
| // standard and is only available when the version code 'S' is specified. |
| // |
| bool AllowChecksum() const { return vcdiff_version_code_ == 'S'; } |
| |
| bool SetMaximumTargetFileSize(size_t new_maximum_target_file_size) { |
| maximum_target_file_size_ = new_maximum_target_file_size; |
| return true; |
| } |
| |
| bool SetMaximumTargetWindowSize(size_t new_maximum_target_window_size) { |
| if (new_maximum_target_window_size > kTargetSizeLimit) { |
| LOG(ERROR) << "Specified maximum target window size " |
| << new_maximum_target_window_size << " exceeds limit of " |
| << kTargetSizeLimit << " bytes" << LOG_ENDL; |
| return false; |
| } |
| maximum_target_window_size_ = new_maximum_target_window_size; |
| return true; |
| } |
| |
| // See description of planned_target_file_size_, below. |
| bool HasPlannedTargetFileSize() const { |
| return planned_target_file_size_ != kUnlimitedBytes; |
| } |
| |
| void SetPlannedTargetFileSize(size_t planned_target_file_size) { |
| planned_target_file_size_ = planned_target_file_size; |
| } |
| |
| void AddToTotalTargetWindowSize(size_t window_size) { |
| total_of_target_window_sizes_ += window_size; |
| } |
| |
| // Checks to see whether the decoded target data has reached its planned size. |
| bool ReachedPlannedTargetFileSize() const { |
| if (!HasPlannedTargetFileSize()) { |
| return false; |
| } |
| // The planned target file size should not have been exceeded. |
| // TargetWindowWouldExceedSizeLimits() ensures that the advertised size of |
| // each target window would not make the target file exceed that limit, and |
| // DecodeBody() will return RESULT_ERROR if the actual decoded output ever |
| // exceeds the advertised target window size. |
| if (total_of_target_window_sizes_ > planned_target_file_size_) { |
| LOG(DFATAL) << "Internal error: Decoded data size " |
| << total_of_target_window_sizes_ |
| << " exceeds planned target file size " |
| << planned_target_file_size_ << LOG_ENDL; |
| return true; |
| } |
| return total_of_target_window_sizes_ == planned_target_file_size_; |
| } |
| |
| // Checks to see whether adding a new target window of the specified size |
| // would exceed the planned target file size, the maximum target file size, |
| // or the maximum target window size. If so, logs an error and returns true; |
| // otherwise, returns false. |
| bool TargetWindowWouldExceedSizeLimits(size_t window_size) const; |
| |
| // Returns the amount of input data passed to the last DecodeChunk() |
| // that was not consumed by the decoder. This is essential if |
| // SetPlannedTargetFileSize() is being used, in order to preserve the |
| // remaining input data stream once the planned target file has been decoded. |
| size_t GetUnconsumedDataSize() const { |
| return unparsed_bytes_.size(); |
| } |
| |
| // This function will return true if the decoder has parsed a complete delta |
| // file header plus zero or more delta file windows, with no data left over. |
| // It will also return true if no delta data at all was decoded. If these |
| // conditions are not met, then FinishDecoding() should not be called. |
| bool IsDecodingComplete() const { |
| if (!FoundFileHeader()) { |
| // No complete delta file header has been parsed yet. DecodeChunk() |
| // may have received some data that it hasn't yet parsed, in which case |
| // decoding is incomplete. |
| return unparsed_bytes_.empty(); |
| } else if (custom_code_table_decoder_.get()) { |
| // The decoder is in the middle of parsing a custom code table. |
| return false; |
| } else if (delta_window_.FoundWindowHeader()) { |
| // The decoder is in the middle of parsing an interleaved format delta |
| // window. |
| return false; |
| } else if (ReachedPlannedTargetFileSize()) { |
| // The decoder found exactly the planned number of bytes. In this case |
| // it is OK for unparsed_bytes_ to be non-empty; it contains the leftover |
| // data after the end of the delta file. |
| return true; |
| } else { |
| // No complete delta file window has been parsed yet. DecodeChunk() |
| // may have received some data that it hasn't yet parsed, in which case |
| // decoding is incomplete. |
| return unparsed_bytes_.empty(); |
| } |
| } |
| |
| const char* dictionary_ptr() const { return dictionary_ptr_; } |
| |
| size_t dictionary_size() const { return dictionary_size_; } |
| |
| VCDiffAddressCache* addr_cache() { return addr_cache_.get(); } |
| |
| string* decoded_target() { return &decoded_target_; } |
| |
| bool allow_vcd_target() const { return allow_vcd_target_; } |
| |
| void SetAllowVcdTarget(bool allow_vcd_target) { |
| if (start_decoding_was_called_) { |
| LOG(DFATAL) << "SetAllowVcdTarget() called after StartDecoding()" |
| << LOG_ENDL; |
| return; |
| } |
| allow_vcd_target_ = allow_vcd_target; |
| } |
| |
| // Removes the contents of decoded_target_ that precede the beginning of the |
| // current window. |
| void TruncateToBeginningOfWindow(); |
| |
| private: |
| // Reads the VCDiff delta file header section as described in RFC section 4.1, |
| // except the custom code table data. Returns RESULT_ERROR if an error |
| // occurred, or RESULT_END_OF_DATA if the end of available data was reached |
| // before the entire header could be read. (The latter may be an error |
| // condition if there is no more data available.) Otherwise, advances |
| // data->position_ past the header and returns RESULT_SUCCESS. |
| // |
| VCDiffResult ReadDeltaFileHeader(ParseableChunk* data); |
| |
| // Indicates whether or not the header has already been read. |
| bool FoundFileHeader() const { return addr_cache_.get() != NULL; } |
| |
| // If ReadDeltaFileHeader() finds the VCD_CODETABLE flag set within the delta |
| // file header, this function parses the custom cache sizes and initializes |
| // a nested VCDiffStreamingDecoderImpl object that will be used to parse the |
| // custom code table in ReadCustomCodeTable(). Returns RESULT_ERROR if an |
| // error occurred, or RESULT_END_OF_DATA if the end of available data was |
| // reached before the custom cache sizes could be read. Otherwise, returns |
| // the number of bytes read. |
| // |
| int InitCustomCodeTable(const char* data_start, const char* data_end); |
| |
| // If a custom code table was specified in the header section that was parsed |
| // by ReadDeltaFileHeader(), this function makes a recursive call to another |
| // VCDiffStreamingDecoderImpl object (custom_code_table_decoder_), since the |
| // custom code table is expected to be supplied as an embedded VCDIFF |
| // encoding that uses the standard code table. Returns RESULT_ERROR if an |
| // error occurs, or RESULT_END_OF_DATA if the end of available data was |
| // reached before the entire custom code table could be read. Otherwise, |
| // returns RESULT_SUCCESS and sets *data_ptr to the position after the encoded |
| // custom code table. If the function returns RESULT_SUCCESS or |
| // RESULT_END_OF_DATA, it advances data->position_ past the parsed bytes. |
| // |
| VCDiffResult ReadCustomCodeTable(ParseableChunk* data); |
| |
| // Contents and length of the source (dictionary) data. |
| const char* dictionary_ptr_; |
| size_t dictionary_size_; |
| |
| // This string will be used to store any unparsed bytes left over when |
| // DecodeChunk() reaches the end of its input and returns RESULT_END_OF_DATA. |
| // It will also be used to concatenate those unparsed bytes with the data |
| // supplied to the next call to DecodeChunk(), so that they appear in |
| // contiguous memory. |
| string unparsed_bytes_; |
| |
| // The portion of the target file that has been decoded so far. This will be |
| // used to fill the output string for DecodeChunk(), and will also be used to |
| // execute COPY instructions that reference target data. Since the source |
| // window can come from a range of addresses in the previously decoded target |
| // data, the entire target file needs to be available to the decoder, not just |
| // the current target window. |
| string decoded_target_; |
| |
| // The VCDIFF version byte (also known as "header4") from the |
| // delta file header. |
| unsigned char vcdiff_version_code_; |
| |
| VCDiffDeltaFileWindow delta_window_; |
| |
| std::auto_ptr<VCDiffAddressCache> addr_cache_; |
| |
| // Will be NULL unless a custom code table has been defined. |
| std::auto_ptr<VCDiffCodeTableData> custom_code_table_; |
| |
| // Used to receive the decoded custom code table. |
| string custom_code_table_string_; |
| |
| // If a custom code table is specified, it will be expressed |
| // as an embedded VCDIFF delta file which uses the default code table |
| // as the source file (dictionary). Use a child decoder object |
| // to decode that delta file. |
| std::auto_ptr<VCDiffStreamingDecoderImpl> custom_code_table_decoder_; |
| |
| // If set, then the decoder is expecting *exactly* this number of |
| // target bytes to be decoded from one or more delta file windows. |
| // If this number is exceeded while decoding a window, but was not met |
| // before starting on that window, an error will be reported. |
| // If FinishDecoding() is called before this number is met, an error |
| // will also be reported. This feature is used for decoding the |
| // embedded code table data within a VCDIFF delta file; we want to |
| // stop processing the embedded data once the entire code table has |
| // been decoded, and treat the rest of the available data as part |
| // of the enclosing delta file. |
| size_t planned_target_file_size_; |
| |
| size_t maximum_target_file_size_; |
| |
| size_t maximum_target_window_size_; |
| |
| // Contains the sum of the decoded sizes of all target windows seen so far, |
| // including the expected total size of the current target window in progress |
| // (even if some of the current target window has not yet been decoded.) |
| size_t total_of_target_window_sizes_; |
| |
| // This value is used to ensure the correct order of calls to the interface |
| // functions, i.e., a single call to StartDecoding(), followed by zero or |
| // more calls to DecodeChunk(), followed by a single call to |
| // FinishDecoding(). |
| bool start_decoding_was_called_; |
| |
| // If this value is true then the VCD_TARGET flag can be specified to allow |
| // the source segment to be chosen from the previously-decoded target data. |
| // (This is the default behavior.) If it is false, then specifying the |
| // VCD_TARGET flag is considered an error, and the decoder does not need to |
| // keep in memory any decoded target data prior to the current window. |
| bool allow_vcd_target_; |
| |
| // Making these private avoids implicit copy constructor & assignment operator |
| VCDiffStreamingDecoderImpl(const VCDiffStreamingDecoderImpl&); // NOLINT |
| void operator=(const VCDiffStreamingDecoderImpl&); |
| }; |
| |
| // *** Methods for VCDiffStreamingDecoderImpl |
| |
| const size_t VCDiffStreamingDecoderImpl::kDefaultMaximumTargetFileSize; |
| const size_t VCDiffStreamingDecoderImpl::kUnlimitedBytes; |
| |
| VCDiffStreamingDecoderImpl::VCDiffStreamingDecoderImpl() |
| : maximum_target_file_size_(kDefaultMaximumTargetFileSize), |
| maximum_target_window_size_(kDefaultMaximumTargetFileSize), |
| allow_vcd_target_(true) { |
| delta_window_.Init(this); |
| Reset(); |
| } |
| |
| // Reset() will delete the component objects without reallocating them. |
| VCDiffStreamingDecoderImpl::~VCDiffStreamingDecoderImpl() { Reset(); } |
| |
| void VCDiffStreamingDecoderImpl::Reset() { |
| start_decoding_was_called_ = false; |
| dictionary_ptr_ = NULL; |
| dictionary_size_ = 0; |
| vcdiff_version_code_ = '\0'; |
| planned_target_file_size_ = kUnlimitedBytes; |
| total_of_target_window_sizes_ = 0; |
| addr_cache_.reset(); |
| custom_code_table_.reset(); |
| custom_code_table_decoder_.reset(); |
| delta_window_.Reset(); |
| } |
| |
| void VCDiffStreamingDecoderImpl::TruncateToBeginningOfWindow() { |
| // Conserve the data for the current window that has been partially decoded. |
| decoded_target_.erase(0, delta_window_.target_window_start_pos()); |
| delta_window_.set_target_window_start_pos(0); |
| } |
| |
| void VCDiffStreamingDecoderImpl::StartDecoding(const char* dictionary_ptr, |
| size_t dictionary_size) { |
| if (start_decoding_was_called_) { |
| LOG(DFATAL) << "StartDecoding() called twice without FinishDecoding()" |
| << LOG_ENDL; |
| return; |
| } |
| unparsed_bytes_.clear(); |
| decoded_target_.clear(); // delta_window_.Reset() depends on this |
| Reset(); |
| dictionary_ptr_ = dictionary_ptr; |
| dictionary_size_ = dictionary_size; |
| start_decoding_was_called_ = true; |
| } |
| |
| // Reads the VCDiff delta file header section as described in RFC section 4.1: |
| // |
| // Header1 - byte = 0xD6 (ASCII 'V' | 0x80) |
| // Header2 - byte = 0xC3 (ASCII 'C' | 0x80) |
| // Header3 - byte = 0xC4 (ASCII 'D' | 0x80) |
| // Header4 - byte |
| // Hdr_Indicator - byte |
| // [Secondary compressor ID] - byte |
| // [Length of code table data] - integer |
| // [Code table data] |
| // |
| // Initializes the code table and address cache objects. Returns RESULT_ERROR |
| // if an error occurred, and RESULT_END_OF_DATA if the end of available data was |
| // reached before the entire header could be read. (The latter may be an error |
| // condition if there is no more data available.) Otherwise, returns |
| // RESULT_SUCCESS, and removes the header bytes from the data string. |
| // |
| // It's relatively inefficient to expect this function to parse any number of |
| // input bytes available, down to 1 byte, but it is necessary in case the input |
| // is not a properly formatted VCDIFF delta file. If the entire input consists |
| // of two bytes "12", then we should recognize that it does not match the |
| // initial VCDIFF magic number "VCD" and report an error, rather than waiting |
| // indefinitely for more input that will never arrive. |
| // |
| VCDiffResult VCDiffStreamingDecoderImpl::ReadDeltaFileHeader( |
| ParseableChunk* data) { |
| if (FoundFileHeader()) { |
| return RESULT_SUCCESS; |
| } |
| size_t data_size = data->UnparsedSize(); |
| const DeltaFileHeader* header = |
| reinterpret_cast<const DeltaFileHeader*>(data->UnparsedData()); |
| bool wrong_magic_number = false; |
| switch (data_size) { |
| // Verify only the bytes that are available. |
| default: |
| // Found header contents up to and including VCDIFF version |
| vcdiff_version_code_ = header->header4; |
| if ((vcdiff_version_code_ != 0x00) && // Draft standard VCDIFF (RFC 3284) |
| (vcdiff_version_code_ != 'S')) { // Enhancements for SDCH protocol |
| LOG(ERROR) << "Unrecognized VCDIFF format version" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| // fall through |
| case 3: |
| if (header->header3 != 0xC4) { // magic value 'D' | 0x80 |
| wrong_magic_number = true; |
| } |
| // fall through |
| case 2: |
| if (header->header2 != 0xC3) { // magic value 'C' | 0x80 |
| wrong_magic_number = true; |
| } |
| // fall through |
| case 1: |
| if (header->header1 != 0xD6) { // magic value 'V' | 0x80 |
| wrong_magic_number = true; |
| } |
| // fall through |
| case 0: |
| if (wrong_magic_number) { |
| LOG(ERROR) << "Did not find VCDIFF header bytes; " |
| "input is not a VCDIFF delta file" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| if (data_size < sizeof(DeltaFileHeader)) return RESULT_END_OF_DATA; |
| } |
| // Secondary compressor not supported. |
| if (header->hdr_indicator & VCD_DECOMPRESS) { |
| LOG(ERROR) << "Secondary compression is not supported" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| if (header->hdr_indicator & VCD_CODETABLE) { |
| int bytes_parsed = InitCustomCodeTable( |
| data->UnparsedData() + sizeof(DeltaFileHeader), |
| data->End()); |
| switch (bytes_parsed) { |
| case RESULT_ERROR: |
| return RESULT_ERROR; |
| case RESULT_END_OF_DATA: |
| return RESULT_END_OF_DATA; |
| default: |
| data->Advance(sizeof(DeltaFileHeader) + bytes_parsed); |
| } |
| } else { |
| addr_cache_.reset(new VCDiffAddressCache); |
| // addr_cache_->Init() will be called |
| // from VCDiffStreamingDecoderImpl::DecodeChunk() |
| data->Advance(sizeof(DeltaFileHeader)); |
| } |
| return RESULT_SUCCESS; |
| } |
| |
| int VCDiffStreamingDecoderImpl::InitCustomCodeTable(const char* data_start, |
| const char* data_end) { |
| // A custom code table is being specified. Parse the variable-length |
| // cache sizes and begin parsing the encoded custom code table. |
| int32_t near_cache_size = 0, same_cache_size = 0; |
| VCDiffHeaderParser header_parser(data_start, data_end); |
| if (!header_parser.ParseInt32("size of near cache", &near_cache_size)) { |
| return header_parser.GetResult(); |
| } |
| if (!header_parser.ParseInt32("size of same cache", &same_cache_size)) { |
| return header_parser.GetResult(); |
| } |
| custom_code_table_.reset(new struct VCDiffCodeTableData); |
| memset(custom_code_table_.get(), 0, sizeof(struct VCDiffCodeTableData)); |
| custom_code_table_string_.clear(); |
| addr_cache_.reset(new VCDiffAddressCache(near_cache_size, same_cache_size)); |
| // addr_cache_->Init() will be called |
| // from VCDiffStreamingDecoderImpl::DecodeChunk() |
| |
| // If we reach this point (the start of the custom code table) |
| // without encountering a RESULT_END_OF_DATA condition, then we won't call |
| // ReadDeltaFileHeader() again for this delta file. |
| // |
| // Instantiate a recursive decoder to interpret the custom code table |
| // as a VCDIFF encoding of the default code table. |
| custom_code_table_decoder_.reset(new VCDiffStreamingDecoderImpl); |
| custom_code_table_decoder_->StartDecoding( |
| reinterpret_cast<const char*>( |
| &VCDiffCodeTableData::kDefaultCodeTableData), |
| sizeof(VCDiffCodeTableData::kDefaultCodeTableData)); |
| custom_code_table_decoder_->SetPlannedTargetFileSize( |
| sizeof(*custom_code_table_)); |
| return static_cast<int>(header_parser.ParsedSize()); |
| } |
| |
| VCDiffResult VCDiffStreamingDecoderImpl::ReadCustomCodeTable( |
| ParseableChunk* data) { |
| if (!custom_code_table_decoder_.get()) { |
| return RESULT_SUCCESS; |
| } |
| if (!custom_code_table_.get()) { |
| LOG(DFATAL) << "Internal error: custom_code_table_decoder_ is set," |
| " but custom_code_table_ is NULL" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| OutputString<string> output_string(&custom_code_table_string_); |
| if (!custom_code_table_decoder_->DecodeChunk(data->UnparsedData(), |
| data->UnparsedSize(), |
| &output_string)) { |
| return RESULT_ERROR; |
| } |
| if (custom_code_table_string_.length() < sizeof(*custom_code_table_)) { |
| // Skip over the consumed data. |
| data->Finish(); |
| return RESULT_END_OF_DATA; |
| } |
| if (!custom_code_table_decoder_->FinishDecoding()) { |
| return RESULT_ERROR; |
| } |
| if (custom_code_table_string_.length() != sizeof(*custom_code_table_)) { |
| LOG(DFATAL) << "Decoded custom code table size (" |
| << custom_code_table_string_.length() |
| << ") does not match size of a code table (" |
| << sizeof(*custom_code_table_) << ")" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| memcpy(custom_code_table_.get(), |
| custom_code_table_string_.data(), |
| sizeof(*custom_code_table_)); |
| custom_code_table_string_.clear(); |
| // Skip over the consumed data. |
| data->FinishExcept(custom_code_table_decoder_->GetUnconsumedDataSize()); |
| custom_code_table_decoder_.reset(); |
| delta_window_.UseCodeTable(*custom_code_table_, addr_cache_->LastMode()); |
| return RESULT_SUCCESS; |
| } |
| |
| namespace { |
| |
| class TrackNewOutputText { |
| public: |
| typedef std::string string; |
| |
| explicit TrackNewOutputText(const string& decoded_target) |
| : decoded_target_(decoded_target), |
| initial_decoded_target_size_(decoded_target.size()) { } |
| |
| void AppendNewOutputText(size_t target_bytes_remaining, |
| OutputStringInterface* output_string) { |
| const size_t bytes_decoded_this_chunk = |
| decoded_target_.size() - initial_decoded_target_size_; |
| if (bytes_decoded_this_chunk > 0) { |
| if (target_bytes_remaining > 0) { |
| // The decoder is midway through decoding a target window. Resize |
| // output_string to match the expected length. The interface guarantees |
| // not to resize the output_string more than once per target window |
| // decoded. |
| output_string->ReserveAdditionalBytes(bytes_decoded_this_chunk |
| + target_bytes_remaining); |
| } |
| output_string->append( |
| decoded_target_.data() + initial_decoded_target_size_, |
| bytes_decoded_this_chunk); |
| } |
| } |
| |
| private: |
| const string& decoded_target_; |
| size_t initial_decoded_target_size_; |
| }; |
| |
| } // anonymous namespace |
| |
| bool VCDiffStreamingDecoderImpl::DecodeChunk( |
| const char* data, |
| size_t len, |
| OutputStringInterface* output_string) { |
| if (!start_decoding_was_called_) { |
| LOG(DFATAL) << "DecodeChunk() called without StartDecoding()" << LOG_ENDL; |
| Reset(); |
| return false; |
| } |
| ParseableChunk parseable_chunk(data, len); |
| if (!unparsed_bytes_.empty()) { |
| unparsed_bytes_.append(data, len); |
| parseable_chunk.SetDataBuffer(unparsed_bytes_.data(), |
| unparsed_bytes_.size()); |
| } |
| TrackNewOutputText output_tracker(decoded_target_); |
| VCDiffResult result = ReadDeltaFileHeader(&parseable_chunk); |
| if (RESULT_SUCCESS == result) { |
| result = ReadCustomCodeTable(&parseable_chunk); |
| } |
| if (RESULT_SUCCESS == result) { |
| result = delta_window_.DecodeWindows(&parseable_chunk); |
| } |
| if (RESULT_ERROR == result) { |
| Reset(); // Don't allow further DecodeChunk calls |
| return false; |
| } |
| unparsed_bytes_.assign(parseable_chunk.UnparsedData(), |
| parseable_chunk.UnparsedSize()); |
| output_tracker.AppendNewOutputText(delta_window_.TargetBytesRemaining(), |
| output_string); |
| if (!allow_vcd_target()) { |
| // VCD_TARGET will never be used to reference target data beyond the start |
| // of the current window, so throw away any earlier target data. |
| TruncateToBeginningOfWindow(); |
| } |
| return true; |
| } |
| |
| // Finishes decoding after all data has been received. Returns true |
| // if decoding of the entire stream was successful. |
| bool VCDiffStreamingDecoderImpl::FinishDecoding() { |
| bool success = true; |
| if (!start_decoding_was_called_) { |
| LOG(WARNING) << "FinishDecoding() called before StartDecoding()," |
| " or called after DecodeChunk() returned false" |
| << LOG_ENDL; |
| success = false; |
| } else if (!IsDecodingComplete()) { |
| LOG(ERROR) << "FinishDecoding() called before parsing entire" |
| " delta file window" << LOG_ENDL; |
| success = false; |
| } |
| // Reset the object state for the next decode operation |
| Reset(); |
| return success; |
| } |
| |
| bool VCDiffStreamingDecoderImpl::TargetWindowWouldExceedSizeLimits( |
| size_t window_size) const { |
| if (window_size > maximum_target_window_size_) { |
| LOG(ERROR) << "Length of target window (" << window_size |
| << ") exceeds limit of " << maximum_target_window_size_ |
| << " bytes" << LOG_ENDL; |
| return true; |
| } |
| if (HasPlannedTargetFileSize()) { |
| // The logical expression to check would be: |
| // |
| // total_of_target_window_sizes_ + window_size > planned_target_file_size_ |
| // |
| // but the addition might cause an integer overflow if target_bytes_to_add |
| // is very large. So it is better to check target_bytes_to_add against |
| // the remaining planned target bytes. |
| size_t remaining_planned_target_file_size = |
| planned_target_file_size_ - total_of_target_window_sizes_; |
| if (window_size > remaining_planned_target_file_size) { |
| LOG(ERROR) << "Length of target window (" << window_size |
| << " bytes) plus previous windows (" |
| << total_of_target_window_sizes_ |
| << " bytes) would exceed planned size of " |
| << planned_target_file_size_ << " bytes" << LOG_ENDL; |
| return true; |
| } |
| } |
| size_t remaining_maximum_target_bytes = |
| maximum_target_file_size_ - total_of_target_window_sizes_; |
| if (window_size > remaining_maximum_target_bytes) { |
| LOG(ERROR) << "Length of target window (" << window_size |
| << " bytes) plus previous windows (" |
| << total_of_target_window_sizes_ |
| << " bytes) would exceed maximum target file size of " |
| << maximum_target_file_size_ << " bytes" << LOG_ENDL; |
| return true; |
| } |
| return false; |
| } |
| |
| // *** Methods for VCDiffDeltaFileWindow |
| |
| void VCDiffDeltaFileWindow::Reset() { |
| found_header_ = false; |
| |
| // Mark the start of the current target window. |
| target_window_start_pos_ = parent_ ? parent_->decoded_target()->size() : 0U; |
| target_window_length_ = 0; |
| |
| source_segment_ptr_ = NULL; |
| source_segment_length_ = 0; |
| |
| instructions_and_sizes_.Invalidate(); |
| data_for_add_and_run_.Invalidate(); |
| addresses_for_copy_.Invalidate(); |
| |
| interleaved_bytes_expected_ = 0; |
| |
| has_checksum_ = false; |
| expected_checksum_ = 0; |
| } |
| |
| VCDiffResult VCDiffDeltaFileWindow::SetUpWindowSections( |
| VCDiffHeaderParser* header_parser) { |
| size_t add_and_run_data_length = 0; |
| size_t instructions_and_sizes_length = 0; |
| size_t addresses_length = 0; |
| if (!header_parser->ParseSectionLengths(has_checksum_, |
| &add_and_run_data_length, |
| &instructions_and_sizes_length, |
| &addresses_length, |
| &expected_checksum_)) { |
| return header_parser->GetResult(); |
| } |
| if (parent_->AllowInterleaved() && |
| (add_and_run_data_length == 0) && |
| (addresses_length == 0)) { |
| // The interleaved format is being used. |
| interleaved_bytes_expected_ = |
| static_cast<int>(instructions_and_sizes_length); |
| UpdateInterleavedSectionPointers(header_parser->UnparsedData(), |
| header_parser->End()); |
| } else { |
| // If interleaved format is not used, then the whole window contents |
| // must be available before decoding can begin. If only part of |
| // the current window is available, then report end of data |
| // and re-parse the whole header when DecodeChunk() is called again. |
| if (header_parser->UnparsedSize() < (add_and_run_data_length + |
| instructions_and_sizes_length + |
| addresses_length)) { |
| return RESULT_END_OF_DATA; |
| } |
| data_for_add_and_run_.Init(header_parser->UnparsedData(), |
| add_and_run_data_length); |
| instructions_and_sizes_.Init(data_for_add_and_run_.End(), |
| instructions_and_sizes_length); |
| addresses_for_copy_.Init(instructions_and_sizes_.End(), addresses_length); |
| if (addresses_for_copy_.End() != header_parser->EndOfDeltaWindow()) { |
| LOG(ERROR) << "The end of the instructions section " |
| "does not match the end of the delta window" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| } |
| reader_.Init(instructions_and_sizes_.UnparsedDataAddr(), |
| instructions_and_sizes_.End()); |
| return RESULT_SUCCESS; |
| } |
| |
| // Here are the elements of the delta window header to be parsed, |
| // from section 4 of the RFC: |
| // |
| // Window1 |
| // Win_Indicator - byte |
| // [Source segment size] - integer |
| // [Source segment position] - integer |
| // The delta encoding of the target window |
| // Length of the delta encoding - integer |
| // The delta encoding |
| // Size of the target window - integer |
| // Delta_Indicator - byte |
| // Length of data for ADDs and RUNs - integer |
| // Length of instructions and sizes - integer |
| // Length of addresses for COPYs - integer |
| // Data section for ADDs and RUNs - array of bytes |
| // Instructions and sizes section - array of bytes |
| // Addresses section for COPYs - array of bytes |
| // |
| VCDiffResult VCDiffDeltaFileWindow::ReadHeader( |
| ParseableChunk* parseable_chunk) { |
| std::string* decoded_target = parent_->decoded_target(); |
| VCDiffHeaderParser header_parser(parseable_chunk->UnparsedData(), |
| parseable_chunk->End()); |
| size_t source_segment_position = 0; |
| unsigned char win_indicator = 0; |
| if (!header_parser.ParseWinIndicatorAndSourceSegment( |
| parent_->dictionary_size(), |
| decoded_target->size(), |
| parent_->allow_vcd_target(), |
| &win_indicator, |
| &source_segment_length_, |
| &source_segment_position)) { |
| return header_parser.GetResult(); |
| } |
| has_checksum_ = parent_->AllowChecksum() && (win_indicator & VCD_CHECKSUM); |
| if (!header_parser.ParseWindowLengths(&target_window_length_)) { |
| return header_parser.GetResult(); |
| } |
| if (parent_->TargetWindowWouldExceedSizeLimits(target_window_length_)) { |
| // An error has been logged by TargetWindowWouldExceedSizeLimits(). |
| return RESULT_ERROR; |
| } |
| header_parser.ParseDeltaIndicator(); |
| VCDiffResult setup_return_code = SetUpWindowSections(&header_parser); |
| if (RESULT_SUCCESS != setup_return_code) { |
| return setup_return_code; |
| } |
| // Reserve enough space in the output string for the current target window. |
| decoded_target->reserve(target_window_start_pos_ + target_window_length_); |
| // Get a pointer to the start of the source segment. |
| if (win_indicator & VCD_SOURCE) { |
| source_segment_ptr_ = parent_->dictionary_ptr() + source_segment_position; |
| } else if (win_indicator & VCD_TARGET) { |
| // This assignment must happen after the reserve(). |
| // decoded_target should not be resized again while processing this window, |
| // so source_segment_ptr_ should remain valid. |
| source_segment_ptr_ = decoded_target->data() + source_segment_position; |
| } |
| // The whole window header was found and parsed successfully. |
| found_header_ = true; |
| parseable_chunk->Advance(header_parser.ParsedSize()); |
| parent_->AddToTotalTargetWindowSize(target_window_length_); |
| return RESULT_SUCCESS; |
| } |
| |
| void VCDiffDeltaFileWindow::UpdateInstructionPointer( |
| ParseableChunk* parseable_chunk) { |
| if (IsInterleaved()) { |
| size_t bytes_parsed = instructions_and_sizes_.ParsedSize(); |
| // Reduce expected instruction segment length by bytes parsed |
| interleaved_bytes_expected_ -= static_cast<int>(bytes_parsed); |
| parseable_chunk->Advance(bytes_parsed); |
| } |
| } |
| |
| inline size_t VCDiffDeltaFileWindow::TargetBytesDecoded() { |
| return parent_->decoded_target()->size() - target_window_start_pos_; |
| } |
| |
| size_t VCDiffDeltaFileWindow::TargetBytesRemaining() { |
| if (target_window_length_ == 0) { |
| // There is no window being decoded at present |
| return 0; |
| } else { |
| return target_window_length_ - TargetBytesDecoded(); |
| } |
| } |
| |
| inline void VCDiffDeltaFileWindow::CopyBytes(const char* data, size_t size) { |
| parent_->decoded_target()->append(data, size); |
| } |
| |
| inline void VCDiffDeltaFileWindow::RunByte(unsigned char byte, size_t size) { |
| parent_->decoded_target()->append(size, byte); |
| } |
| |
| VCDiffResult VCDiffDeltaFileWindow::DecodeAdd(size_t size) { |
| if (size > data_for_add_and_run_.UnparsedSize()) { |
| return RESULT_END_OF_DATA; |
| } |
| // Write the next "size" data bytes |
| CopyBytes(data_for_add_and_run_.UnparsedData(), size); |
| data_for_add_and_run_.Advance(size); |
| return RESULT_SUCCESS; |
| } |
| |
| VCDiffResult VCDiffDeltaFileWindow::DecodeRun(size_t size) { |
| if (data_for_add_and_run_.Empty()) { |
| return RESULT_END_OF_DATA; |
| } |
| // Write "size" copies of the next data byte |
| RunByte(*data_for_add_and_run_.UnparsedData(), size); |
| data_for_add_and_run_.Advance(1); |
| return RESULT_SUCCESS; |
| } |
| |
| VCDiffResult VCDiffDeltaFileWindow::DecodeCopy(size_t size, |
| unsigned char mode) { |
| // Keep track of the number of target bytes decoded as a local variable |
| // to avoid recalculating it each time it is needed. |
| size_t target_bytes_decoded = TargetBytesDecoded(); |
| const VCDAddress here_address = |
| static_cast<VCDAddress>(source_segment_length_ + target_bytes_decoded); |
| const VCDAddress decoded_address = parent_->addr_cache()->DecodeAddress( |
| here_address, |
| mode, |
| addresses_for_copy_.UnparsedDataAddr(), |
| addresses_for_copy_.End()); |
| switch (decoded_address) { |
| case RESULT_ERROR: |
| LOG(ERROR) << "Unable to decode address for COPY" << LOG_ENDL; |
| return RESULT_ERROR; |
| case RESULT_END_OF_DATA: |
| return RESULT_END_OF_DATA; |
| default: |
| if ((decoded_address < 0) || (decoded_address > here_address)) { |
| LOG(DFATAL) << "Internal error: unexpected address " << decoded_address |
| << " returned from DecodeAddress, with here_address = " |
| << here_address << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| break; |
| } |
| size_t address = static_cast<size_t>(decoded_address); |
| if ((address + size) <= source_segment_length_) { |
| // Copy all data from source segment |
| CopyBytes(&source_segment_ptr_[address], size); |
| return RESULT_SUCCESS; |
| } |
| // Copy some data from target window... |
| if (address < source_segment_length_) { |
| // ... plus some data from source segment |
| const size_t partial_copy_size = source_segment_length_ - address; |
| CopyBytes(&source_segment_ptr_[address], partial_copy_size); |
| target_bytes_decoded += partial_copy_size; |
| address += partial_copy_size; |
| size -= partial_copy_size; |
| } |
| address -= source_segment_length_; |
| // address is now based at start of target window |
| const char* const target_segment_ptr = parent_->decoded_target()->data() + |
| target_window_start_pos_; |
| while (size > (target_bytes_decoded - address)) { |
| // Recursive copy that extends into the yet-to-be-copied target data |
| const size_t partial_copy_size = target_bytes_decoded - address; |
| CopyBytes(&target_segment_ptr[address], partial_copy_size); |
| target_bytes_decoded += partial_copy_size; |
| address += partial_copy_size; |
| size -= partial_copy_size; |
| } |
| CopyBytes(&target_segment_ptr[address], size); |
| return RESULT_SUCCESS; |
| } |
| |
| int VCDiffDeltaFileWindow::DecodeBody(ParseableChunk* parseable_chunk) { |
| if (IsInterleaved() && (instructions_and_sizes_.UnparsedData() |
| != parseable_chunk->UnparsedData())) { |
| LOG(DFATAL) << "Internal error: interleaved format is used, but the" |
| " input pointer does not point to the instructions section" |
| << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| while (TargetBytesDecoded() < target_window_length_) { |
| int32_t decoded_size = VCD_INSTRUCTION_ERROR; |
| unsigned char mode = 0; |
| VCDiffInstructionType instruction = |
| reader_.GetNextInstruction(&decoded_size, &mode); |
| switch (instruction) { |
| case VCD_INSTRUCTION_END_OF_DATA: |
| UpdateInstructionPointer(parseable_chunk); |
| return RESULT_END_OF_DATA; |
| case VCD_INSTRUCTION_ERROR: |
| return RESULT_ERROR; |
| default: |
| break; |
| } |
| const size_t size = static_cast<size_t>(decoded_size); |
| // The value of "size" itself could be enormous (say, INT32_MAX) |
| // so check it individually against the limit to protect against |
| // overflow when adding it to something else. |
| if ((size > target_window_length_) || |
| ((size + TargetBytesDecoded()) > target_window_length_)) { |
| LOG(ERROR) << VCDiffInstructionName(instruction) |
| << " with size " << size |
| << " plus existing " << TargetBytesDecoded() |
| << " bytes of target data exceeds length of target" |
| " window (" << target_window_length_ << " bytes)" |
| << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| VCDiffResult result = RESULT_SUCCESS; |
| switch (instruction) { |
| case VCD_ADD: |
| result = DecodeAdd(size); |
| break; |
| case VCD_RUN: |
| result = DecodeRun(size); |
| break; |
| case VCD_COPY: |
| result = DecodeCopy(size, mode); |
| break; |
| default: |
| LOG(DFATAL) << "Unexpected instruction type " << instruction |
| << "in opcode stream" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| switch (result) { |
| case RESULT_END_OF_DATA: |
| reader_.UnGetInstruction(); |
| UpdateInstructionPointer(parseable_chunk); |
| return RESULT_END_OF_DATA; |
| case RESULT_ERROR: |
| return RESULT_ERROR; |
| case RESULT_SUCCESS: |
| break; |
| } |
| } |
| if (TargetBytesDecoded() != target_window_length_) { |
| LOG(ERROR) << "Decoded target window size (" << TargetBytesDecoded() |
| << " bytes) does not match expected size (" |
| << target_window_length_ << " bytes)" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| const char* const target_window_start = |
| parent_->decoded_target()->data() + target_window_start_pos_; |
| if (has_checksum_ && |
| (ComputeAdler32(target_window_start, target_window_length_) |
| != expected_checksum_)) { |
| LOG(ERROR) << "Target data does not match checksum; this could mean " |
| "that the wrong dictionary was used" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| if (!instructions_and_sizes_.Empty()) { |
| LOG(ERROR) << "Excess instructions and sizes left over " |
| "after decoding target window" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| if (!IsInterleaved()) { |
| // Standard format is being used, with three separate sections for the |
| // instructions, data, and addresses. |
| if (!data_for_add_and_run_.Empty()) { |
| LOG(ERROR) << "Excess ADD/RUN data left over " |
| "after decoding target window" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| if (!addresses_for_copy_.Empty()) { |
| LOG(ERROR) << "Excess COPY addresses left over " |
| "after decoding target window" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| // Reached the end of the window. Update the ParseableChunk to point to the |
| // end of the addresses section, which is the last section in the window. |
| parseable_chunk->SetPosition(addresses_for_copy_.End()); |
| } else { |
| // Interleaved format is being used. |
| UpdateInstructionPointer(parseable_chunk); |
| } |
| return RESULT_SUCCESS; |
| } |
| |
| VCDiffResult VCDiffDeltaFileWindow::DecodeWindows( |
| ParseableChunk* parseable_chunk) { |
| if (!parent_) { |
| LOG(DFATAL) << "Internal error: VCDiffDeltaFileWindow::DecodeWindows() " |
| "called before VCDiffDeltaFileWindow::Init()" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| while (!parseable_chunk->Empty()) { |
| if (!found_header_) { |
| switch (ReadHeader(parseable_chunk)) { |
| case RESULT_END_OF_DATA: |
| return RESULT_END_OF_DATA; |
| case RESULT_ERROR: |
| return RESULT_ERROR; |
| default: |
| // Reset address cache between windows (RFC section 5.1) |
| if (!parent_->addr_cache()->Init()) { |
| LOG(DFATAL) << "Error initializing address cache" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| } |
| } else { |
| // We are resuming a window that was partially decoded before a |
| // RESULT_END_OF_DATA was returned. This can only happen on the first |
| // loop iteration, and only if the interleaved format is enabled and used. |
| if (!IsInterleaved()) { |
| LOG(DFATAL) << "Internal error: Resumed decoding of a delta file window" |
| " when interleaved format is not being used" << LOG_ENDL; |
| return RESULT_ERROR; |
| } |
| UpdateInterleavedSectionPointers(parseable_chunk->UnparsedData(), |
| parseable_chunk->End()); |
| reader_.UpdatePointers(instructions_and_sizes_.UnparsedDataAddr(), |
| instructions_and_sizes_.End()); |
| } |
| switch (DecodeBody(parseable_chunk)) { |
| case RESULT_END_OF_DATA: |
| if (MoreDataExpected()) { |
| return RESULT_END_OF_DATA; |
| } else { |
| LOG(ERROR) << "End of data reached while decoding VCDIFF delta file" |
| << LOG_ENDL; |
| // fall through to RESULT_ERROR case |
| } |
| case RESULT_ERROR: |
| return RESULT_ERROR; |
| default: |
| break; // DecodeBody succeeded |
| } |
| // Get ready to read a new delta window |
| Reset(); |
| if (parent_->ReachedPlannedTargetFileSize()) { |
| // Found exactly the length we expected. Stop decoding. |
| return RESULT_SUCCESS; |
| } |
| } |
| return RESULT_SUCCESS; |
| } |
| |
| // *** Methods for VCDiffStreamingDecoder |
| |
| VCDiffStreamingDecoder::VCDiffStreamingDecoder() |
| : impl_(new VCDiffStreamingDecoderImpl) { } |
| |
| VCDiffStreamingDecoder::~VCDiffStreamingDecoder() { delete impl_; } |
| |
| void VCDiffStreamingDecoder::StartDecoding(const char* source, size_t len) { |
| impl_->StartDecoding(source, len); |
| } |
| |
| bool VCDiffStreamingDecoder::DecodeChunkToInterface( |
| const char* data, |
| size_t len, |
| OutputStringInterface* output_string) { |
| return impl_->DecodeChunk(data, len, output_string); |
| } |
| |
| bool VCDiffStreamingDecoder::FinishDecoding() { |
| return impl_->FinishDecoding(); |
| } |
| |
| bool VCDiffStreamingDecoder::SetMaximumTargetFileSize( |
| size_t new_maximum_target_file_size) { |
| return impl_->SetMaximumTargetFileSize(new_maximum_target_file_size); |
| } |
| |
| bool VCDiffStreamingDecoder::SetMaximumTargetWindowSize( |
| size_t new_maximum_target_window_size) { |
| return impl_->SetMaximumTargetWindowSize(new_maximum_target_window_size); |
| } |
| |
| void VCDiffStreamingDecoder::SetAllowVcdTarget(bool allow_vcd_target) { |
| impl_->SetAllowVcdTarget(allow_vcd_target); |
| } |
| |
| bool VCDiffDecoder::DecodeToInterface(const char* dictionary_ptr, |
| size_t dictionary_size, |
| const string& encoding, |
| OutputStringInterface* target) { |
| target->clear(); |
| decoder_.StartDecoding(dictionary_ptr, dictionary_size); |
| if (!decoder_.DecodeChunkToInterface(encoding.data(), |
| encoding.size(), |
| target)) { |
| return false; |
| } |
| return decoder_.FinishDecoding(); |
| } |
| |
| } // namespace open_vcdiff |