// Copyright 2009 The open-vcdiff Authors. All Rights Reserved. // // 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 #include #include #include #include #include "logging.h" #include "google/jsonwriter.h" #include "google/output_string.h" namespace open_vcdiff { JSONCodeTableWriter::JSONCodeTableWriter() : output_called_(false), opcode_added_(false) {} JSONCodeTableWriter::~JSONCodeTableWriter() {} bool JSONCodeTableWriter::Init(size_t /*dictionary_size*/) { output_ = "["; opcode_added_ = false; return true; } void JSONCodeTableWriter::Output(OutputStringInterface* out) { output_called_ = true; out->append(output_.data(), output_.size()); output_ = ""; } void JSONCodeTableWriter::FinishEncoding(OutputStringInterface* out) { if (output_called_) { out->append("]", 1); } } void JSONCodeTableWriter::JSONEscape(const char* data, size_t size, string* out) { for (size_t i = 0; i < size; ++i) { const unsigned char c = static_cast(data[i]); switch (c) { case '"': out->append("\\\"", 2); break; case '\\': out->append("\\\\", 2); break; case '\b': out->append("\\b", 2); break; case '\f': out->append("\\f", 2); break; case '\n': out->append("\\n", 2); break; case '\r': out->append("\\r", 2); break; case '\t': out->append("\\t", 2); break; default: // encode zero as unicode, also all control codes. if (c < 32 || c >= 127) { char unicode_code[8] = ""; snprintf(unicode_code, sizeof(unicode_code), "\\u%04x", c); out->append(unicode_code, strlen(unicode_code)); } else { out->push_back(data[i]); } } } } void JSONCodeTableWriter::Add(const char* data, size_t size) { // Add leading comma if this is not the first opcode. if (opcode_added_) { output_.push_back(','); } output_.push_back('\"'); JSONEscape(data, size, &output_); output_.push_back('\"'); opcode_added_ = true; } void JSONCodeTableWriter::Copy(int32_t offset, size_t size) { // Add leading comma if this is not the first opcode. if (opcode_added_) { output_.push_back(','); } std::ostringstream copy_code; copy_code << offset << "," << size; output_.append(copy_code.str()); opcode_added_ = true; } void JSONCodeTableWriter::Run(size_t size, unsigned char byte) { // Add leading comma if this is not the first opcode. if (opcode_added_) { output_.push_back(','); } output_.push_back('\"'); output_.append(string(size, byte).data(), size); output_.push_back('\"'); opcode_added_ = true; } void JSONCodeTableWriter::WriteHeader(OutputStringInterface *, VCDiffFormatExtensionFlags) { // The JSON format does not need a header. } bool JSONCodeTableWriter::VerifyDictionary(const char *dictionary, size_t size) const { // Indexes in the JSON encoding are interpreted as character indexes // in a unicode string while the delta is constructed using byte // indexes. To avoid an indexing mismatch and corrupt deltas all // characters in the dictionary and target must be single-byte // characters, ie 7-bit ASCII. VCD_ERROR << "JSON writer does not allow non-ASCII characters" " in dictionary" << VCD_ENDL; return IsAscii(dictionary, size); } bool JSONCodeTableWriter::VerifyChunk(const char *chunk, size_t size) const { // See comment in VerifyDictionary for explanation of ASCII // restriction. VCD_ERROR << "JSON writer does not allow non-ASCII characters" " in target" << VCD_ENDL; return IsAscii(chunk, size); } bool JSONCodeTableWriter::IsAscii(const char *data, size_t len) { for (size_t i = 0; i < len; i++) { if (static_cast(data[i]) >= 128) { return false; } } return true; } } // namespace open_vcdiff