open-vcdiff-sys 0.1.1

Low-level bindings to open-vcdiff, a codec for the VCDIFF binary delta format.
Documentation
// 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 <config.h>
#include <stdio.h>
#include <string.h>
#include <sstream>
#include <string>
#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<unsigned char>(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<unsigned char>(data[i]) >= 128) {
      return false;
    }
  }
  return true;
}

}  // namespace open_vcdiff