<commit_before>/* Copyright 2017 The TensorFlow 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 "tensorflow/lite/tools/verifier.h"
#include <climits>
#include "absl/container/flat_hash_set.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/string_util.h"
#include "tensorflow/lite/version.h"

namespace tflite {

namespace {

// Reports error message when the reporter is set.
void ReportError(ErrorReporter* error_reporter, const char* format, ...) {
  if (error_reporter) {
    va_list args;
    va_start(args, format);
    error_reporter->Report(format, args);
    va_end(args);
  }
}

// Returns the int32_t value pointed by ptr.
const uint32_t* GetIntPtr(const char* ptr) {
  return reinterpret_cast<const uint32_t*>(ptr);
}

// Verifies flatbuffer format of the model contents and returns the in-memory
// model.
const Model* VerifyFlatbufferAndGetModel(const void* buf, size_t len) {
  ::flatbuffers::Verifier verifier(static_cast<const uint8_t*>(buf), len);
  if (VerifyModelBuffer(verifier)) {
    return ::tflite::GetModel(buf);
  } else {
    return nullptr;
  }
}

const uint32_t kMaxNumString = UINT_MAX / sizeof(int32_t) - 2;

// Verifies string tensor has legit buffer contents that follow the schema
// defined in lite/string_util.h
bool VerifyStringTensorBuffer(const Tensor& tensor, const Buffer& buffer,
                              ErrorReporter* error_reporter) {
  uint32_t buffer_size = buffer.data()->size();
  const char* buffer_ptr = reinterpret_cast<const char*>(buffer.data()->data());

  uint32_t num_strings = *GetIntPtr(buffer_ptr);
  if (num_strings > kMaxNumString) {
    ReportError(error_reporter,
                "String tensor %s has invalid num of string set: %d",
                tensor.name()->c_str(), num_strings);
    return false;
  }
  uint32_t header_offsets =
      static_cast<uint32_t>(num_strings + 2) * sizeof(int32_t);

  if (buffer_size < header_offsets) {
    ReportError(error_reporter,
                "String tensor %s buffer requires at least %d bytes, but is "
                "allocated with %d bytes",
                tensor.name()->c_str(), header_offsets, buffer_size);
    return false;
  }

  uint32_t prev_ptr = header_offsets;
  uint32_t offset = sizeof(int32_t);

  if (*GetIntPtr(buffer_ptr + offset) != header_offsets) {
    ReportError(error_reporter,
                "String tensor %s buffer initial offset must be: %d",
                tensor.name()->c_str(), header_offsets);
    return false;
  }
  offset += sizeof(int32_t);
  for (int i = 1; i <= num_strings; i++, offset += sizeof(int32_t)) {
    int string_offset = *GetIntPtr(buffer_ptr + offset);
    if (string_offset < prev_ptr || string_offset > buffer_size) {
      ReportError(error_reporter,
                  "String tensor %s buffer is invalid: index %d",
                  tensor.name()->c_str(), i);
      return false;
    }
  }
  if (*GetIntPtr(buffer_ptr + offset - sizeof(int32_t)) != buffer_size) {
    ReportError(error_reporter,
                "String tensor %s buffer last offset must be %d",
                tensor.name()->c_str(), buffer_size);
    return false;
  }
  return true;
}

// Verifies numeric tensor has legit buffer.
bool VerifyNumericTensorBuffer(const Tensor& tensor, const Buffer& buffer,
                               ErrorReporter* error_reporter) {
  uint64_t bytes_required = 1;
  if (!tensor.shape()) {
    ReportError(error_reporter, "Tensor %s shape is empty",
                tensor.name()->c_str());
    return false;
  }
  for (int dim : *tensor.shape()) {
    bytes_required *= dim;
    if (bytes_required > UINT_MAX) {
      ReportError(error_reporter, "Tensor %s dimension overflow",
                  tensor.name()->c_str());
      return false;
    }
  }
  switch (tensor.type()) {
    case TensorType_FLOAT32:
      bytes_required *= sizeof(float);
      break;
    case TensorType_INT32:
      bytes_required *= sizeof(int32_t);
      break;
    case TensorType_UINT8:
      bytes_required *= sizeof(uint8_t);
      break;
    case TensorType_INT64:
      bytes_required *= sizeof(int64_t);
      break;
    case TensorType_FLOAT16:
      // FALLTHROUGH_INTENDED;
    default:
      ReportError(error_reporter, "Tensor %s invalid type: %d",
                  tensor.name()->c_str(), tensor.type());
      return false;
  }
  if (bytes_required > UINT_MAX) {
    ReportError(error_reporter, "Tensor %s dimension overflow",
                tensor.name()->c_str());
    return false;
  }

  if (bytes_required != buffer.data()->size()) {
    ReportError(
        error_reporter,
        "Tensor %s requires %d bytes, but is allocated with %d bytes buffer",
        tensor.name()->c_str(), bytes_required, buffer.data()->size());
    return false;
  }
  return true;

  // TODO(yichengfan): verify quantized tensors.
}

using flatbuffers::Offset;
using flatbuffers::Vector;

bool VerifyOperators(const Vector<Offset<Operator>>& operators,
                     ErrorReporter* error_reporter) {
  for (const auto& op : operators) {
    if (!op->inputs()) {
      ReportError(error_reporter, "Missing 'inputs' for operator.");
      return false;
    }
    if (!op->outputs()) {
      ReportError(error_reporter, "Missing 'outputs' for operator.");
      return false;
    }
  }
  return true;
}

bool IsConstantTensor(const Tensor& tensor, const Model& model) {
  if (!tensor.buffer() || !model.buffers()) return false;
  if (tensor.buffer() > 0 && tensor.buffer() < model.buffers()->size()) {
    auto* buffer = model.buffers()->Get(tensor.buffer());
    if (buffer && buffer->data()) {
      return true;
    }
  }
  return false;
}

// Performs basic consistency checks on a sub-graph.
bool VerifySubGraphConsistency(const Model& model, const SubGraph& subgraph,
                               ErrorReporter* error_reporter) {
  absl::flat_hash_set<int> subgraph_input_tensors, constant_tensors,
      variable_tensors, output_tensors;
  for (int i = 0; i < subgraph.tensors()->Length(); ++i) {
    const auto* tensor = subgraph.tensors()->Get(i);
    bool is_constant_tensor = false;
    if (model.buffers() && tensor->buffer() > 0 &&
        tensor->buffer() < model.buffers()->size()) {
      auto* buffer = model.buffers()->Get(tensor->buffer());
      if (buffer && buffer->data()) {
        is_constant_tensor = true;
      }
    }
    if (IsConstantTensor(*tensor, model)) {
      constant_tensors.insert(i);
    } else if (tensor->is_variable()) {
      variable_tensors.insert(i);
    }
  }
  for (const int tensor_idx : *subgraph.inputs()) {
    subgraph_input_tensors.insert(tensor_idx);
  }

  for (int op_idx = 0; op_idx < subgraph.operators()->Length(); ++op_idx) {
    const auto* op = subgraph.operators()->Get(op_idx);
    const auto& opcode = model.operator_codes()->Get(op->opcode_index());
    // Check for invalid inputs by ensuring all exist in produced_tensors.
    for (const int input_idx : *op->inputs()) {
      if (constant_tensors.find(input_idx) == constant_tensors.end() &&
          variable_tensors.find(input_idx) == variable_tensors.end() &&
          subgraph_input_tensors.find(input_idx) ==
              subgraph_input_tensors.end() &&
          output_tensors.find(input_idx) == output_tensors.end()) {
        ReportError(error_reporter,
                    "Input tensor %d to op %d (%s) is not produced", input_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      }
    }
    // Check for cycles/invalid outputs by ensuring that none exist in
    // produced_tensors.
    for (const int output_idx : *op->outputs()) {
      if (constant_tensors.find(output_idx) != constant_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a constant", output_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (variable_tensors.find(output_idx) != variable_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a variable", output_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (subgraph_input_tensors.find(output_idx) !=
                 subgraph_input_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a subgraph input",
                    output_idx, op_idx,
                    EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (output_tensors.find(output_idx) != output_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is an output from "
                    "another op. There is a cycle in the graph",
                    output_idx, op_idx,
                    EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      }
      // This can be an input to a subsequent op.
      output_tensors.insert(output_idx);
    }
  }
  return true;
}

bool VerifySubGraphs(const Model& model, ErrorReporter* error_reporter) {
  if (!model.subgraphs()) {
    ReportError(error_reporter, "Missing 'subgraphs' section.");
    return false;
  }
  for (const auto& subgraph : *model.subgraphs()) {
    if (!subgraph->operators()) {
      ReportError(error_reporter, "Missing 'operators' section in subgraph.");
      return false;
    }

    if (!VerifyOperators(*subgraph->operators(), error_reporter)) {
      return false;
    }

    if (!VerifySubGraphConsistency(model, *subgraph, error_reporter)) {
      return false;
    }
  }
  return true;
}

// Verifies tensors have valid properties and legit buffer if set.
bool VerifyTensors(const Model& model, ErrorReporter* error_reporter) {
  if (!model.subgraphs()) {
    return true;
  }
  if (!model.buffers()) {
    ReportError(error_reporter, "Missing 'buffers' section.");
    return false;
  }

  for (const auto& subgraph : *model.subgraphs()) {
    if (!subgraph->tensors()) {
      continue;
    }
    for (const auto& tensor : *subgraph->tensors()) {
      if (!tensor->buffer()) {
        continue;
      }
      if (tensor->buffer() >= model.buffers()->size()) {
        ReportError(error_reporter, "Tensor %s invalid buffer index: %d",
                    tensor->name(), tensor->buffer());
        return false;
      }
      auto* buffer = model.buffers()->Get(tensor->buffer());
      if (!buffer) {
        ReportError(error_reporter, "Tensor %s buffer %d not set",
                    tensor->name(), tensor->buffer());
        return false;
      }

      // Many transient tensors don't have data in the flatbuffer. Their
      // buffers will be allocated by the interpreter at run-time.
      if (buffer->data()) {
        if (tensor->type() == TensorType_STRING) {
          if (!VerifyStringTensorBuffer(*tensor, *buffer, error_reporter)) {
            return false;
          }
        } else {
          if (!VerifyNumericTensorBuffer(*tensor, *buffer, error_reporter)) {
            return false;
          }
        }
      }
    }
  }
  return true;
}

bool VerifyOps(const Model& model, const OpResolver& resolver,
               ErrorReporter* error_reporter) {
  if (!model.operator_codes()) {
    return true;
  }
  for (const auto& opcode : *model.operator_codes()) {
    if (opcode->builtin_code() < BuiltinOperator_MIN ||
        opcode->builtin_code() > BuiltinOperator_MAX) {
      ReportError(error_reporter, "Operator id '%d' is out of range.",
                  opcode->builtin_code());
      return false;
    }

    if (opcode->builtin_code() == BuiltinOperator_CUSTOM) {
      if (!resolver.FindOp(opcode->custom_code()->c_str(), opcode->version())) {
        ReportError(error_reporter, "Unsupported custom op: %s, version: %d",
                    opcode->custom_code()->c_str(), opcode->version());
        return false;
      }
    } else {
      if (!resolver.FindOp(opcode->builtin_code(), opcode->version())) {
        ReportError(error_reporter, "Unsupported builtin op: %s, version: %d",
                    EnumNameBuiltinOperator(opcode->builtin_code()),
                    opcode->version());
        return false;
      }
    }
  }
  return true;
}

}  // namespace

bool Verify(const void* buf, size_t len, const OpResolver& resolver,
            ErrorReporter* error_reporter) {
  const Model* model = VerifyFlatbufferAndGetModel(buf, len);
  if (model == nullptr) {
    ReportError(error_reporter, "Invalid flatbuffer format");
    return false;
  }
  if (model->version() != TFLITE_SCHEMA_VERSION) {
    ReportError(error_reporter, "Invalid model version %d", model->version());
    return false;
  }
  if (!VerifySubGraphs(*model, error_reporter)) {
    return false;
  }
  if (!VerifyTensors(*model, error_reporter)) {
    return false;
  }
  if (!VerifyOps(*model, resolver, error_reporter)) {
    return false;
  }
  return true;
}
}  // namespace tflite
<commit_msg>Allow tflite verifier to accept numeric tensors of type TensorType_INT8.<commit_after>/* Copyright 2017 The TensorFlow 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 "tensorflow/lite/tools/verifier.h"
#include <climits>
#include "absl/container/flat_hash_set.h"
#include "tensorflow/lite/schema/schema_generated.h"
#include "tensorflow/lite/string_util.h"
#include "tensorflow/lite/version.h"

namespace tflite {

namespace {

// Reports error message when the reporter is set.
void ReportError(ErrorReporter* error_reporter, const char* format, ...) {
  if (error_reporter) {
    va_list args;
    va_start(args, format);
    error_reporter->Report(format, args);
    va_end(args);
  }
}

// Returns the int32_t value pointed by ptr.
const uint32_t* GetIntPtr(const char* ptr) {
  return reinterpret_cast<const uint32_t*>(ptr);
}

// Verifies flatbuffer format of the model contents and returns the in-memory
// model.
const Model* VerifyFlatbufferAndGetModel(const void* buf, size_t len) {
  ::flatbuffers::Verifier verifier(static_cast<const uint8_t*>(buf), len);
  if (VerifyModelBuffer(verifier)) {
    return ::tflite::GetModel(buf);
  } else {
    return nullptr;
  }
}

const uint32_t kMaxNumString = UINT_MAX / sizeof(int32_t) - 2;

// Verifies string tensor has legit buffer contents that follow the schema
// defined in lite/string_util.h
bool VerifyStringTensorBuffer(const Tensor& tensor, const Buffer& buffer,
                              ErrorReporter* error_reporter) {
  uint32_t buffer_size = buffer.data()->size();
  const char* buffer_ptr = reinterpret_cast<const char*>(buffer.data()->data());

  uint32_t num_strings = *GetIntPtr(buffer_ptr);
  if (num_strings > kMaxNumString) {
    ReportError(error_reporter,
                "String tensor %s has invalid num of string set: %d",
                tensor.name()->c_str(), num_strings);
    return false;
  }
  uint32_t header_offsets =
      static_cast<uint32_t>(num_strings + 2) * sizeof(int32_t);

  if (buffer_size < header_offsets) {
    ReportError(error_reporter,
                "String tensor %s buffer requires at least %d bytes, but is "
                "allocated with %d bytes",
                tensor.name()->c_str(), header_offsets, buffer_size);
    return false;
  }

  uint32_t prev_ptr = header_offsets;
  uint32_t offset = sizeof(int32_t);

  if (*GetIntPtr(buffer_ptr + offset) != header_offsets) {
    ReportError(error_reporter,
                "String tensor %s buffer initial offset must be: %d",
                tensor.name()->c_str(), header_offsets);
    return false;
  }
  offset += sizeof(int32_t);
  for (int i = 1; i <= num_strings; i++, offset += sizeof(int32_t)) {
    int string_offset = *GetIntPtr(buffer_ptr + offset);
    if (string_offset < prev_ptr || string_offset > buffer_size) {
      ReportError(error_reporter,
                  "String tensor %s buffer is invalid: index %d",
                  tensor.name()->c_str(), i);
      return false;
    }
  }
  if (*GetIntPtr(buffer_ptr + offset - sizeof(int32_t)) != buffer_size) {
    ReportError(error_reporter,
                "String tensor %s buffer last offset must be %d",
                tensor.name()->c_str(), buffer_size);
    return false;
  }
  return true;
}

// Verifies numeric tensor has legit buffer.
bool VerifyNumericTensorBuffer(const Tensor& tensor, const Buffer& buffer,
                               ErrorReporter* error_reporter) {
  uint64_t bytes_required = 1;
  if (!tensor.shape()) {
    ReportError(error_reporter, "Tensor %s shape is empty",
                tensor.name()->c_str());
    return false;
  }
  for (int dim : *tensor.shape()) {
    bytes_required *= dim;
    if (bytes_required > UINT_MAX) {
      ReportError(error_reporter, "Tensor %s dimension overflow",
                  tensor.name()->c_str());
      return false;
    }
  }
  switch (tensor.type()) {
    case TensorType_FLOAT32:
      bytes_required *= sizeof(float);
      break;
    case TensorType_INT8:
      bytes_required *= sizeof(int8_t);
      break;
    case TensorType_UINT8:
      bytes_required *= sizeof(uint8_t);
      break;
    case TensorType_INT32:
      bytes_required *= sizeof(int32_t);
      break;
    case TensorType_INT64:
      bytes_required *= sizeof(int64_t);
      break;
    case TensorType_FLOAT16:
      // FALLTHROUGH_INTENDED;
    default:
      ReportError(error_reporter, "Tensor %s invalid type: %d",
                  tensor.name()->c_str(), tensor.type());
      return false;
  }
  if (bytes_required > UINT_MAX) {
    ReportError(error_reporter, "Tensor %s dimension overflow",
                tensor.name()->c_str());
    return false;
  }

  if (bytes_required != buffer.data()->size()) {
    ReportError(
        error_reporter,
        "Tensor %s requires %d bytes, but is allocated with %d bytes buffer",
        tensor.name()->c_str(), bytes_required, buffer.data()->size());
    return false;
  }
  return true;

  // TODO(yichengfan): verify quantized tensors.
}

using flatbuffers::Offset;
using flatbuffers::Vector;

bool VerifyOperators(const Vector<Offset<Operator>>& operators,
                     ErrorReporter* error_reporter) {
  for (const auto& op : operators) {
    if (!op->inputs()) {
      ReportError(error_reporter, "Missing 'inputs' for operator.");
      return false;
    }
    if (!op->outputs()) {
      ReportError(error_reporter, "Missing 'outputs' for operator.");
      return false;
    }
  }
  return true;
}

bool IsConstantTensor(const Tensor& tensor, const Model& model) {
  if (!tensor.buffer() || !model.buffers()) return false;
  if (tensor.buffer() > 0 && tensor.buffer() < model.buffers()->size()) {
    auto* buffer = model.buffers()->Get(tensor.buffer());
    if (buffer && buffer->data()) {
      return true;
    }
  }
  return false;
}

// Performs basic consistency checks on a sub-graph.
bool VerifySubGraphConsistency(const Model& model, const SubGraph& subgraph,
                               ErrorReporter* error_reporter) {
  absl::flat_hash_set<int> subgraph_input_tensors, constant_tensors,
      variable_tensors, output_tensors;
  for (int i = 0; i < subgraph.tensors()->Length(); ++i) {
    const auto* tensor = subgraph.tensors()->Get(i);
    bool is_constant_tensor = false;
    if (model.buffers() && tensor->buffer() > 0 &&
        tensor->buffer() < model.buffers()->size()) {
      auto* buffer = model.buffers()->Get(tensor->buffer());
      if (buffer && buffer->data()) {
        is_constant_tensor = true;
      }
    }
    if (IsConstantTensor(*tensor, model)) {
      constant_tensors.insert(i);
    } else if (tensor->is_variable()) {
      variable_tensors.insert(i);
    }
  }
  for (const int tensor_idx : *subgraph.inputs()) {
    subgraph_input_tensors.insert(tensor_idx);
  }

  for (int op_idx = 0; op_idx < subgraph.operators()->Length(); ++op_idx) {
    const auto* op = subgraph.operators()->Get(op_idx);
    const auto& opcode = model.operator_codes()->Get(op->opcode_index());
    // Check for invalid inputs by ensuring all exist in produced_tensors.
    for (const int input_idx : *op->inputs()) {
      if (constant_tensors.find(input_idx) == constant_tensors.end() &&
          variable_tensors.find(input_idx) == variable_tensors.end() &&
          subgraph_input_tensors.find(input_idx) ==
              subgraph_input_tensors.end() &&
          output_tensors.find(input_idx) == output_tensors.end()) {
        ReportError(error_reporter,
                    "Input tensor %d to op %d (%s) is not produced", input_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      }
    }
    // Check for cycles/invalid outputs by ensuring that none exist in
    // produced_tensors.
    for (const int output_idx : *op->outputs()) {
      if (constant_tensors.find(output_idx) != constant_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a constant", output_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (variable_tensors.find(output_idx) != variable_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a variable", output_idx,
                    op_idx, EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (subgraph_input_tensors.find(output_idx) !=
                 subgraph_input_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is a subgraph input",
                    output_idx, op_idx,
                    EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      } else if (output_tensors.find(output_idx) != output_tensors.end()) {
        ReportError(error_reporter,
                    "Output tensor %d to op %d (%s) is an output from "
                    "another op. There is a cycle in the graph",
                    output_idx, op_idx,
                    EnumNameBuiltinOperator(opcode->builtin_code()));
        return false;
      }
      // This can be an input to a subsequent op.
      output_tensors.insert(output_idx);
    }
  }
  return true;
}

bool VerifySubGraphs(const Model& model, ErrorReporter* error_reporter) {
  if (!model.subgraphs()) {
    ReportError(error_reporter, "Missing 'subgraphs' section.");
    return false;
  }
  for (const auto& subgraph : *model.subgraphs()) {
    if (!subgraph->operators()) {
      ReportError(error_reporter, "Missing 'operators' section in subgraph.");
      return false;
    }

    if (!VerifyOperators(*subgraph->operators(), error_reporter)) {
      return false;
    }

    if (!VerifySubGraphConsistency(model, *subgraph, error_reporter)) {
      return false;
    }
  }
  return true;
}

// Verifies tensors have valid properties and legit buffer if set.
bool VerifyTensors(const Model& model, ErrorReporter* error_reporter) {
  if (!model.subgraphs()) {
    return true;
  }
  if (!model.buffers()) {
    ReportError(error_reporter, "Missing 'buffers' section.");
    return false;
  }

  for (const auto& subgraph : *model.subgraphs()) {
    if (!subgraph->tensors()) {
      continue;
    }
    for (const auto& tensor : *subgraph->tensors()) {
      if (!tensor->buffer()) {
        continue;
      }
      if (tensor->buffer() >= model.buffers()->size()) {
        ReportError(error_reporter, "Tensor %s invalid buffer index: %d",
                    tensor->name(), tensor->buffer());
        return false;
      }
      auto* buffer = model.buffers()->Get(tensor->buffer());
      if (!buffer) {
        ReportError(error_reporter, "Tensor %s buffer %d not set",
                    tensor->name(), tensor->buffer());
        return false;
      }

      // Many transient tensors don't have data in the flatbuffer. Their
      // buffers will be allocated by the interpreter at run-time.
      if (buffer->data()) {
        if (tensor->type() == TensorType_STRING) {
          if (!VerifyStringTensorBuffer(*tensor, *buffer, error_reporter)) {
            return false;
          }
        } else {
          if (!VerifyNumericTensorBuffer(*tensor, *buffer, error_reporter)) {
            return false;
          }
        }
      }
    }
  }
  return true;
}

bool VerifyOps(const Model& model, const OpResolver& resolver,
               ErrorReporter* error_reporter) {
  if (!model.operator_codes()) {
    return true;
  }
  for (const auto& opcode : *model.operator_codes()) {
    if (opcode->builtin_code() < BuiltinOperator_MIN ||
        opcode->builtin_code() > BuiltinOperator_MAX) {
      ReportError(error_reporter, "Operator id '%d' is out of range.",
                  opcode->builtin_code());
      return false;
    }

    if (opcode->builtin_code() == BuiltinOperator_CUSTOM) {
      if (!resolver.FindOp(opcode->custom_code()->c_str(), opcode->version())) {
        ReportError(error_reporter, "Unsupported custom op: %s, version: %d",
                    opcode->custom_code()->c_str(), opcode->version());
        return false;
      }
    } else {
      if (!resolver.FindOp(opcode->builtin_code(), opcode->version())) {
        ReportError(error_reporter, "Unsupported builtin op: %s, version: %d",
                    EnumNameBuiltinOperator(opcode->builtin_code()),
                    opcode->version());
        return false;
      }
    }
  }
  return true;
}

}  // namespace

bool Verify(const void* buf, size_t len, const OpResolver& resolver,
            ErrorReporter* error_reporter) {
  const Model* model = VerifyFlatbufferAndGetModel(buf, len);
  if (model == nullptr) {
    ReportError(error_reporter, "Invalid flatbuffer format");
    return false;
  }
  if (model->version() != TFLITE_SCHEMA_VERSION) {
    ReportError(error_reporter, "Invalid model version %d", model->version());
    return false;
  }
  if (!VerifySubGraphs(*model, error_reporter)) {
    return false;
  }
  if (!VerifyTensors(*model, error_reporter)) {
    return false;
  }
  if (!VerifyOps(*model, resolver, error_reporter)) {
    return false;
  }
  return true;
}
}  // namespace tflite
<|endoftext|>


<commit_before>/*
// ts_mruby.hpp - ts_mruby internal functions
//
*/

#ifndef TS_MRUBY_INTERNAL_H
#define TS_MRUBY_INTERNAL_H

#include <iostream>
#include <map>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include <atscppapi/InterceptPlugin.h>
#include <atscppapi/Transaction.h>
#include <atscppapi/TransformationPlugin.h>
#include <mruby.h>
#include <mruby/value.h>

// Enable to unit test
#ifdef MOCKING
#define MOCKABLE_ATTR virtual
#else
#define MOCKABLE_ATTR
#endif // MOCKING

#define TS_MRUBY_PLUGIN_NAME "ts_mruby"
const static char *TS_MRUBY_PLUGIN_VERSION = "0.1";
const static char *TS_MRUBY_PLUGIN_AUTHOR = "Ryo Okubo";
const static char *TS_MRUBY_PLUGIN_EMAIL = "";
const int FILTER_RESERVED_BUFFER_SIZE = 1024;

bool judge_tls(const std::string &scheme);

std::pair<std::string, uint16_t>
get_authority_pair(const std::string &authority, bool is_tls = false);

namespace ts_mruby {

/*
 * Thread local mrb_state and RProc*'s
 */
class ThreadLocalMRubyStates {
public:
  ThreadLocalMRubyStates();
  ~ThreadLocalMRubyStates();

  mrb_state *getMrb() { return state_; }

  RProc *getRProc(const std::string &key);

private:
  mrb_state *state_;
  std::map<std::string, RProc *> procCache_;
};

/*
 * Getter for thread-shared mruby script cache
 */
class MrubyScriptsCache;
MrubyScriptsCache* getInitializedGlobalScriptCache(const std::string& filepath);

/*
 * Getter for thread-local mrb_state*
 */
ThreadLocalMRubyStates *getThreadLocalMrubyStates();

} // ts_mruby namespace

/*
 * rputs/echo intercept plugin
 *
 * It enables users specify response header/body.
 * If the plugin is used, ATS doesn't fetch to origin server.
 *
 */
class RputsPlugin : public atscppapi::InterceptPlugin {
private:
  int status_code_;
  std::vector<std::pair<std::string, std::string>> headers_;
  std::string message_;

public:
  RputsPlugin(atscppapi::Transaction &transaction, int code = 200)
      : atscppapi::InterceptPlugin(
            transaction, atscppapi::InterceptPlugin::TRANSACTION_INTERCEPT),
        status_code_(code), message_("") {}

  void consume(const std::string &data,
               atscppapi::InterceptPlugin::RequestDataType type){};

  void setStatusCode(int code);

  void appendMessage(const std::string &msg);

  void appendHeader(const std::pair<std::string, std::string> &entry);

  void appendHeaders(const std::vector<std::pair<std::string, std::string>> &h);

  void handleInputComplete();
};

/*
 * HeaderRewrite plugin
 *
 * It enables users specify response headers with low cost.
 *
 */
class HeaderRewritePlugin : public atscppapi::TransactionPlugin {
public:
  HeaderRewritePlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransactionPlugin(transaction) {
    atscppapi::TransactionPlugin::registerHook(HOOK_SEND_RESPONSE_HEADERS);
  }

  enum class Operator : int { ASSIGN, DELETE };

  void addRewriteRule(const std::string &key, const std::string &value,
                      Operator op);
  void handleSendResponseHeaders(atscppapi::Transaction &transaction);

private:
  using Modifiers = std::vector<std::tuple<std::string, std::string, Operator>>;
  Modifiers modifiers_;
};

/*
 * Output Filter plugin
 *
 * It enables users modify response body with their mruby block.
 * NOTE It does buffering response body.
 *
 */
class FilterPlugin : public atscppapi::TransformationPlugin {
private:
  std::string origBuffer_;
  std::string transformedBuffer_;
  mrb_value block_;

public:
  FilterPlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransformationPlugin(transaction, RESPONSE_TRANSFORMATION) {
    registerHook(HOOK_SEND_RESPONSE_HEADERS);

    origBuffer_.reserve(FILTER_RESERVED_BUFFER_SIZE);
    transformedBuffer_.reserve(FILTER_RESERVED_BUFFER_SIZE);
    block_ = mrb_nil_value();
  }

  void appendBody(const std::string &data);
  void appendBlock(const mrb_value block);

  void consume(const std::string &data);
  void handleInputComplete();
};

struct TSMrubyResult {
  bool isRemapped = false;
};

/*
 * Current Transaction state.
 * It nearly equal atscppapi::Plugin::HookType
 *
 */
enum class TransactionStateTag : int {
  READ_REQUEST_HEADERS,
  SEND_REQUEST_HEADERS,
  READ_RESPONSE_HEADERS,
  SEND_RESPONSE_HEADERS,
};

/*
 * ts_mruby context
 *
 * It has a lifecycle similar to TS txn.
 * And some pointer member will be released by atscppapi
 *
 */
class TSMrubyContext {
private:
  TransactionStateTag state_tag_;
  TSMrubyResult result_;

  // NOTE: these resource's owner is atscppai
  atscppapi::Transaction *transaction_;
  RputsPlugin *rputs_;
  HeaderRewritePlugin *header_rewrite_;
  FilterPlugin *filter_;

  template <typename T> void createAndAddPlugin_(T **ptr) {
    if (transaction_ && !*ptr) {
      *ptr = new T(*transaction_);
      transaction_->addPlugin(*ptr);
    }
  }

public:
  TSMrubyContext()
    : state_tag_(TransactionStateTag::READ_REQUEST_HEADERS) {}

  atscppapi::Transaction *getTransaction() { return transaction_; }
  void setTransaction(atscppapi::Transaction *transaction) {
    transaction_ = transaction;
  }

  const TSMrubyResult getResult() const { return result_; }
  void setResult(TSMrubyResult r) { result_ = r; }

  RputsPlugin *getRputsPlugin() { return rputs_; }
  HeaderRewritePlugin *getHeaderRewritePlugin() { return header_rewrite_; }
  FilterPlugin *getFilterPlugin() { return filter_; }

  void registerRputsPlugin() { createAndAddPlugin_<RputsPlugin>(&rputs_); }
  void registerHeaderRewritePlugin() {
    createAndAddPlugin_<HeaderRewritePlugin>(&header_rewrite_);
  }
  void registerFilterPlugin() { createAndAddPlugin_<FilterPlugin>(&filter_); }

  TransactionStateTag getStateTag() const { return state_tag_; }
  void setStateTag(TransactionStateTag tag) { state_tag_ = tag; }
};

/**
 * mrb_value RAII
 *
 * Manage mrb_gc_register() / mrb_gc_unregister() to keep an mrb_value from mruby GC.
 * Recommend to use it with shared_ptr to avoid leak.
 *
 * This constractor requires that the 2nd argument is rvalue
 *
 */
class TSMrubyValue {
private:
  mrb_state* mrb_;
  mrb_value value_;

public:
  TSMrubyValue(mrb_state* mrb, mrb_value&& value)
    : mrb_(mrb), value_(value) {
    // keep mrb_value from GC.
    mrb_gc_register(mrb_, value_);
  }

  ~TSMrubyValue() {
    // unmark mrb_value to release.
    // TODO Ensure thread-safety
    // mrb_gc_unregister(mrb_, value_);
  }

  mrb_value getValue() { return value_; }

};

#endif // TS_MRUBY_INTERNAL_H
<commit_msg>Fix missing ptr initializations<commit_after>/*
// ts_mruby.hpp - ts_mruby internal functions
//
*/

#ifndef TS_MRUBY_INTERNAL_H
#define TS_MRUBY_INTERNAL_H

#include <iostream>
#include <map>
#include <string>
#include <tuple>
#include <utility>
#include <vector>

#include <atscppapi/InterceptPlugin.h>
#include <atscppapi/Transaction.h>
#include <atscppapi/TransformationPlugin.h>
#include <mruby.h>
#include <mruby/value.h>

// Enable to unit test
#ifdef MOCKING
#define MOCKABLE_ATTR virtual
#else
#define MOCKABLE_ATTR
#endif // MOCKING

#define TS_MRUBY_PLUGIN_NAME "ts_mruby"
const static char *TS_MRUBY_PLUGIN_VERSION = "0.1";
const static char *TS_MRUBY_PLUGIN_AUTHOR = "Ryo Okubo";
const static char *TS_MRUBY_PLUGIN_EMAIL = "";
const int FILTER_RESERVED_BUFFER_SIZE = 1024;

bool judge_tls(const std::string &scheme);

std::pair<std::string, uint16_t>
get_authority_pair(const std::string &authority, bool is_tls = false);

namespace ts_mruby {

/*
 * Thread local mrb_state and RProc*'s
 */
class ThreadLocalMRubyStates {
public:
  ThreadLocalMRubyStates();
  ~ThreadLocalMRubyStates();

  mrb_state *getMrb() { return state_; }

  RProc *getRProc(const std::string &key);

private:
  mrb_state *state_;
  std::map<std::string, RProc *> procCache_;
};

/*
 * Getter for thread-shared mruby script cache
 */
class MrubyScriptsCache;
MrubyScriptsCache* getInitializedGlobalScriptCache(const std::string& filepath);

/*
 * Getter for thread-local mrb_state*
 */
ThreadLocalMRubyStates *getThreadLocalMrubyStates();

} // ts_mruby namespace

/*
 * rputs/echo intercept plugin
 *
 * It enables users specify response header/body.
 * If the plugin is used, ATS doesn't fetch to origin server.
 *
 */
class RputsPlugin : public atscppapi::InterceptPlugin {
private:
  int status_code_;
  std::vector<std::pair<std::string, std::string>> headers_;
  std::string message_;

public:
  RputsPlugin(atscppapi::Transaction &transaction, int code = 200)
      : atscppapi::InterceptPlugin(
            transaction, atscppapi::InterceptPlugin::TRANSACTION_INTERCEPT),
        status_code_(code), message_("") {}

  void consume(const std::string &data,
               atscppapi::InterceptPlugin::RequestDataType type){};

  void setStatusCode(int code);

  void appendMessage(const std::string &msg);

  void appendHeader(const std::pair<std::string, std::string> &entry);

  void appendHeaders(const std::vector<std::pair<std::string, std::string>> &h);

  void handleInputComplete();
};

/*
 * HeaderRewrite plugin
 *
 * It enables users specify response headers with low cost.
 *
 */
class HeaderRewritePlugin : public atscppapi::TransactionPlugin {
public:
  HeaderRewritePlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransactionPlugin(transaction) {
    atscppapi::TransactionPlugin::registerHook(HOOK_SEND_RESPONSE_HEADERS);
  }

  enum class Operator : int { ASSIGN, DELETE };

  void addRewriteRule(const std::string &key, const std::string &value,
                      Operator op);
  void handleSendResponseHeaders(atscppapi::Transaction &transaction);

private:
  using Modifiers = std::vector<std::tuple<std::string, std::string, Operator>>;
  Modifiers modifiers_;
};

/*
 * Output Filter plugin
 *
 * It enables users modify response body with their mruby block.
 * NOTE It does buffering response body.
 *
 */
class FilterPlugin : public atscppapi::TransformationPlugin {
private:
  std::string origBuffer_;
  std::string transformedBuffer_;
  mrb_value block_;

public:
  FilterPlugin(atscppapi::Transaction &transaction)
      : atscppapi::TransformationPlugin(transaction, RESPONSE_TRANSFORMATION) {
    registerHook(HOOK_SEND_RESPONSE_HEADERS);

    origBuffer_.reserve(FILTER_RESERVED_BUFFER_SIZE);
    transformedBuffer_.reserve(FILTER_RESERVED_BUFFER_SIZE);
    block_ = mrb_nil_value();
  }

  void appendBody(const std::string &data);
  void appendBlock(const mrb_value block);

  void consume(const std::string &data);
  void handleInputComplete();
};

struct TSMrubyResult {
  bool isRemapped = false;
};

/*
 * Current Transaction state.
 * It nearly equal atscppapi::Plugin::HookType
 *
 */
enum class TransactionStateTag : int {
  READ_REQUEST_HEADERS,
  SEND_REQUEST_HEADERS,
  READ_RESPONSE_HEADERS,
  SEND_RESPONSE_HEADERS,
};

/*
 * ts_mruby context
 *
 * It has a lifecycle similar to TS txn.
 * And some pointer member will be released by atscppapi
 *
 */
class TSMrubyContext {
private:
  TransactionStateTag state_tag_;
  TSMrubyResult result_;

  // NOTE: these resource's owner is atscppai
  atscppapi::Transaction *transaction_;
  RputsPlugin *rputs_;
  HeaderRewritePlugin *header_rewrite_;
  FilterPlugin *filter_;

  template <typename T> void createAndAddPlugin_(T **ptr) {
    if (transaction_ && !*ptr) {
      *ptr = new T(*transaction_);
      transaction_->addPlugin(*ptr);
    }
  }

public:
  TSMrubyContext()
    : state_tag_(TransactionStateTag::READ_REQUEST_HEADERS),
      transaction_(nullptr), rputs_(nullptr), header_rewrite_(nullptr),
      filter_(nullptr) {}

  atscppapi::Transaction *getTransaction() { return transaction_; }
  void setTransaction(atscppapi::Transaction *transaction) {
    transaction_ = transaction;
  }

  const TSMrubyResult getResult() const { return result_; }
  void setResult(TSMrubyResult r) { result_ = r; }

  RputsPlugin *getRputsPlugin() { return rputs_; }
  HeaderRewritePlugin *getHeaderRewritePlugin() { return header_rewrite_; }
  FilterPlugin *getFilterPlugin() { return filter_; }

  void registerRputsPlugin() { createAndAddPlugin_<RputsPlugin>(&rputs_); }
  void registerHeaderRewritePlugin() {
    createAndAddPlugin_<HeaderRewritePlugin>(&header_rewrite_);
  }
  void registerFilterPlugin() { createAndAddPlugin_<FilterPlugin>(&filter_); }

  TransactionStateTag getStateTag() const { return state_tag_; }
  void setStateTag(TransactionStateTag tag) { state_tag_ = tag; }
};

/**
 * mrb_value RAII
 *
 * Manage mrb_gc_register() / mrb_gc_unregister() to keep an mrb_value from mruby GC.
 * Recommend to use it with shared_ptr to avoid leak.
 *
 * This constractor requires that the 2nd argument is rvalue
 *
 */
class TSMrubyValue {
private:
  mrb_state* mrb_;
  mrb_value value_;

public:
  TSMrubyValue(mrb_state* mrb, mrb_value&& value)
    : mrb_(mrb), value_(value) {
    // keep mrb_value from GC.
    mrb_gc_register(mrb_, value_);
  }

  ~TSMrubyValue() {
    // unmark mrb_value to release.
    // TODO Ensure thread-safety
    // mrb_gc_unregister(mrb_, value_);
  }

  mrb_value getValue() { return value_; }

};

#endif // TS_MRUBY_INTERNAL_H
<|endoftext|>


<commit_before>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2022                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <openspace/engine/moduleengine.h>

#include <openspace/documentation/documentation.h>
#include <openspace/engine/globals.h>
#include <openspace/moduleregistration.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/util/openspacemodule.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/profiling.h>

#include "moduleengine_lua.inl"

namespace {
    constexpr const char* _loggerCat = "ModuleEngine";
} // namespace

namespace openspace {

ModuleEngine::ModuleEngine() : properties::PropertyOwner({ "Modules" }) {}

void ModuleEngine::initialize(
                     const std::map<std::string, ghoul::Dictionary>& moduleConfigurations)
{
    ZoneScoped

    std::vector<OpenSpaceModule*> modules = AllModules();

    for (OpenSpaceModule* m : modules) {
        registerModule(std::unique_ptr<OpenSpaceModule>(m));
    }

    for (OpenSpaceModule* m : modules) {
        const std::string& identifier = m->identifier();
        auto it = moduleConfigurations.find(identifier);
        ghoul::Dictionary configuration;
        if (it != moduleConfigurations.end()) {
            configuration = it->second;
        }
        try {
            m->initialize(configuration);
        }
        catch (const documentation::SpecificationError& e) {
            for (const documentation::TestResult::Offense& o : e.result.offenses) {
                LERRORC(e.component, o.offender + ": " + ghoul::to_string(o.reason));
            }
            for (const documentation::TestResult::Warning& w : e.result.warnings) {
                LWARNINGC(e.component, w.offender + ": " + ghoul::to_string(w.reason));
            }
            throw;
        }

        addPropertySubOwner(m);

    }
}

void ModuleEngine::initializeGL() {
    ZoneScoped

    LDEBUG("Initializing OpenGL of modules");
    for (std::unique_ptr<OpenSpaceModule>& m : _modules) {
        LDEBUG(fmt::format("Initializing OpenGL of module '{}'", m->identifier()));
        m->initializeGL();
    }
    LDEBUG("Finished initializing OpenGL of modules");
}

void ModuleEngine::deinitialize() {
    ZoneScoped

    LDEBUG("Deinitializing modules");
    for (std::unique_ptr<OpenSpaceModule>& m : _modules) {
        LDEBUG(fmt::format("Deinitializing module '{}'", m->identifier()));
        m->deinitialize();
    }

    LDEBUG("Finished deinitializing modules");

    for (std::unique_ptr<OpenSpaceModule>& m : _modules) {
        LDEBUG(fmt::format("Destroying module '{}'", m->identifier()));
        m = nullptr;
    }

    LDEBUG("Finished destroying modules");
    _modules.clear();
}

void ModuleEngine::deinitializeGL() {
    ZoneScoped

    LDEBUG("Deinitializing OpenGL of modules");
    for (std::unique_ptr<OpenSpaceModule>& m : _modules) {
        LDEBUG(fmt::format("Deinitializing OpenGL of module '{}'", m->identifier()));
        m->deinitializeGL();

    }
    LDEBUG("Finished deinitializing OpenGL of modules");
}

void ModuleEngine::registerModule(std::unique_ptr<OpenSpaceModule> mod) {
    ZoneScoped

    ghoul_assert(mod, "Module must not be nullptr");

    auto it = std::find_if(
        _modules.begin(),
        _modules.end(),
        [&mod](std::unique_ptr<OpenSpaceModule>& rhs) {
            return rhs->identifier() == mod->identifier();
        }
    );
    if (it != _modules.end()) {
        throw ghoul::RuntimeError(
            "Module name '" + mod->identifier() + "' was registered before",
            "ModuleEngine"
        );
    }

    LDEBUG(fmt::format("Registering module '{}'", mod->identifier()));
    LDEBUG(fmt::format("Registered module '{}'", mod->identifier()));
    _modules.push_back(std::move(mod));
}

std::vector<OpenSpaceModule*> ModuleEngine::modules() const {
    std::vector<OpenSpaceModule*> result;
    result.reserve(_modules.size());
    for (const std::unique_ptr<OpenSpaceModule>& m : _modules) {
        result.push_back(m.get());
    }
    return result;
}

ghoul::systemcapabilities::Version ModuleEngine::requiredOpenGLVersion() const {
    ghoul::systemcapabilities::Version version = { 0, 0, 0 };

    for (const std::unique_ptr<OpenSpaceModule>& m : _modules) {
        version = std::max(version, m->requiredOpenGLVersion());
    }

    return version;
}

scripting::LuaLibrary ModuleEngine::luaLibrary() {
    return {
        "modules",
        {
            codegen::lua::IsLoaded
        }
    };
}

} // namespace openspace
<commit_msg>Reverse order of module deinitialization<commit_after>/*****************************************************************************************
 *                                                                                *
 * OpenSpace                                                                             *
 *                                                                                *
 * Copyright (c) 2014-2022                                                               *
 *                                                                                *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this  *
 * software and associated documentation files (the "Software"), to deal in the Software *
 * without restriction, including without limitation the rights to use, copy, modify,    *
 * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to    *
 * permit persons to whom the Software is furnished to do so, subject to the following   *
 * conditions:                                                                           *
 *                                                                                *
 * The above copyright notice and this permission notice shall be included in all copies *
 * or substantial portions of the Software.                                              *
 *                                                                                *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,   *
 * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A         *
 * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT    *
 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF  *
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE  *
 * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                                         *
 ****************************************************************************************/

#include <openspace/engine/moduleengine.h>

#include <openspace/documentation/documentation.h>
#include <openspace/engine/globals.h>
#include <openspace/moduleregistration.h>
#include <openspace/scripting/lualibrary.h>
#include <openspace/scripting/scriptengine.h>
#include <openspace/util/openspacemodule.h>
#include <ghoul/logging/logmanager.h>
#include <ghoul/misc/profiling.h>

#include "moduleengine_lua.inl"

namespace {
    constexpr const char* _loggerCat = "ModuleEngine";
} // namespace

namespace openspace {

ModuleEngine::ModuleEngine() : properties::PropertyOwner({ "Modules" }) {}

void ModuleEngine::initialize(
                     const std::map<std::string, ghoul::Dictionary>& moduleConfigurations)
{
    ZoneScoped

    std::vector<OpenSpaceModule*> modules = AllModules();

    for (OpenSpaceModule* m : modules) {
        registerModule(std::unique_ptr<OpenSpaceModule>(m));
    }

    for (OpenSpaceModule* m : modules) {
        const std::string& identifier = m->identifier();
        auto it = moduleConfigurations.find(identifier);
        ghoul::Dictionary configuration;
        if (it != moduleConfigurations.end()) {
            configuration = it->second;
        }
        try {
            m->initialize(configuration);
        }
        catch (const documentation::SpecificationError& e) {
            for (const documentation::TestResult::Offense& o : e.result.offenses) {
                LERRORC(e.component, o.offender + ": " + ghoul::to_string(o.reason));
            }
            for (const documentation::TestResult::Warning& w : e.result.warnings) {
                LWARNINGC(e.component, w.offender + ": " + ghoul::to_string(w.reason));
            }
            throw;
        }

        addPropertySubOwner(m);

    }
}

void ModuleEngine::initializeGL() {
    ZoneScoped

    LDEBUG("Initializing OpenGL of modules");
    for (std::unique_ptr<OpenSpaceModule>& m : _modules) {
        LDEBUG(fmt::format("Initializing OpenGL of module '{}'", m->identifier()));
        m->initializeGL();
    }
    LDEBUG("Finished initializing OpenGL of modules");
}

void ModuleEngine::deinitialize() {
    ZoneScoped

    LDEBUG("Deinitializing modules");

    for (auto mIt = _modules.rbegin(); mIt != _modules.rend(); ++mIt) {
        LDEBUG(fmt::format("Deinitializing module '{}'", (*mIt)->identifier()));
        (*mIt)->deinitialize();
    }
    LDEBUG("Finished deinitializing modules");

    for (auto mIt = _modules.rbegin(); mIt != _modules.rend(); ++mIt) {
        LDEBUG(fmt::format("Destroying module '{}'", (*mIt)->identifier()));
        (*mIt) = nullptr;
    }
    LDEBUG("Finished destroying modules");

    _modules.clear();
}

void ModuleEngine::deinitializeGL() {
    ZoneScoped

    LDEBUG("Deinitializing OpenGL of modules");
    for (auto mIt = _modules.rbegin(); mIt != _modules.rend(); ++mIt) {
        LDEBUG(fmt::format("Deinitializing OpenGL of module '{}'", (*mIt)->identifier()));
        (*mIt)->deinitializeGL();

    }
    LDEBUG("Finished deinitializing OpenGL of modules");
}

void ModuleEngine::registerModule(std::unique_ptr<OpenSpaceModule> mod) {
    ZoneScoped

    ghoul_assert(mod, "Module must not be nullptr");

    auto it = std::find_if(
        _modules.begin(),
        _modules.end(),
        [&mod](std::unique_ptr<OpenSpaceModule>& rhs) {
            return rhs->identifier() == mod->identifier();
        }
    );
    if (it != _modules.end()) {
        throw ghoul::RuntimeError(
            "Module name '" + mod->identifier() + "' was registered before",
            "ModuleEngine"
        );
    }

    LDEBUG(fmt::format("Registering module '{}'", mod->identifier()));
    LDEBUG(fmt::format("Registered module '{}'", mod->identifier()));
    _modules.push_back(std::move(mod));
}

std::vector<OpenSpaceModule*> ModuleEngine::modules() const {
    std::vector<OpenSpaceModule*> result;
    result.reserve(_modules.size());
    for (const std::unique_ptr<OpenSpaceModule>& m : _modules) {
        result.push_back(m.get());
    }
    return result;
}

ghoul::systemcapabilities::Version ModuleEngine::requiredOpenGLVersion() const {
    ghoul::systemcapabilities::Version version = { 0, 0, 0 };

    for (const std::unique_ptr<OpenSpaceModule>& m : _modules) {
        version = std::max(version, m->requiredOpenGLVersion());
    }

    return version;
}

scripting::LuaLibrary ModuleEngine::luaLibrary() {
    return {
        "modules",
        {
            codegen::lua::IsLoaded
        }
    };
}

} // namespace openspace
<|endoftext|>


<commit_before>// Copyright 2011 libmv authors
// Initial revision by Matthias Fauconneau.
//
#include "tracker.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/tools/tool.h"
#include "libmv/image/image.h"
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/tracking/pyramid_region_tracker.h"

#include <QFileDialog>
#include <QDesktopWidget>
#include <QGraphicsPixmapItem>
#include <QTime>
#include <QDebug>

using libmv::FloatImage;
using libmv::Marker;
using std::vector;

int main(int argc, char *argv[]) {
  libmv::Init("", &argc, &argv);
  QApplication app(argc, argv);
  Tracker window;
  window.show();
  return app.exec();
}

// Minimal scope profiling tool.
struct Profile {
  QTime time;
  Profile(const char* msg,const char* file,const int line) {
    fprintf(stderr,"%s:%d: %s... ",file,line,msg); fflush(stderr);
    time.start();
  }
  ~Profile() { fprintf(stderr,"%d ms\n",time.elapsed()); fflush(stderr); }
#define profile(msg) Profile p(msg,__FILE__, __LINE__)
};

// Only supports bags-of-files type movies for now; use QMovie later.
// TODO(keir): This doesn't belong in the header.
class Clip {
 public:
  Clip() {}
  Clip(QStringList paths) {
    Open(paths);
  }

  void Open(QStringList paths) {
    if (paths.isEmpty()) {
      return;
    }
    image_filenames_.clear();
    foreach(QString path, paths) {
      if (QFileInfo(path).isFile()) {
        image_filenames_ << path;
      } else {
        foreach(QString file,
                QDir(path).entryList(QStringList("*.jpg") <<"*.png",
                                     QDir::Files,
                                     QDir::Name)) {
          image_filenames_ << QDir(path).filePath(file);
        }
      }
    }
  }

  QImage Frame(int frame) {
    return QImage(image_filenames_[frame]);
  }

  int size() const { return image_filenames_.size(); }

 private:
  QList<QString> image_filenames_;
};

// Copy the region starting at *x0, *y0 with width w, h into region. If the
// region asked for is outside the image border, clipping is done and the
// returned region is smaller than requested. At return, *x0 and *y0 contain
// the top left pixel of *region in the original image (which may not match
// what was passed in). Returns true if any values were copied.
bool CopyRegionFromQImage(QImage image,
                          int w, int h,
                          int *x0, int *y0,
                          libmv::FloatImage *region) {
  const unsigned char *data = image.bits();
  int width = image.width();
  int height = image.height();

  // Clip the region on the upper right.
  if (*x0 < 0) {
    w += *x0;
    *x0 = 0;
  }
  if (*y0 < 0) {
    h += *y0;
    *y0 = 0;
  }

  // Clip the region on the lower left.
  w = std::min(w, width - *x0);
  h = std::min(h, height - *y0);

  // The region is entirely outside the given image.
  if (w <= 0 || h <= 0) {
    return false;
  }

  // Now that clipping is done, do the blit.
  region->resize(w, h);
  for (int y = *y0; y < *y0 + h; ++y) {
    for (int x = *x0; x < *x0 + w; ++x) {
      // This assumes BGR row-major ordering for the QImage's raw bytes.
      // TODO(keir): Check that that is true!
      const unsigned char *pixel = data + (y * width + x) * 4;
      (*region)(y, x, 0) = (0.2126 * pixel[2] +
                            0.7152 * pixel[1] +
                            0.0722 * pixel[0]) / 255;
    }
  }
  return true;
}

libmv::RegionTracker *CreateRegionTracker() {
  return new libmv::PyramidRegionTracker(new libmv::KltRegionTracker, 3);
}

// TODO(keir): Leaks clip and tracks.
Tracker::Tracker()
    : current_(-1),
      clip_(new Clip),
      tracks_(new libmv::Tracks),
      region_tracker_(CreateRegionTracker()) {
      max_track_(-1) {
  setWindowTitle("LibMV Simple Tracker");
  setMaximumSize(qApp->desktop()->availableGeometry().size());

  // Create the toolbar.
  QToolBar* tool_bar = addToolBar("Main Toolbar");
  tool_bar->setObjectName("mainToolbar");
  tool_bar->addAction(QIcon::fromTheme("document-open"), "Open...",
                     this, SLOT(open()));
  tool_bar->addAction(QIcon::fromTheme("media-skip-backward"), "First Frame",
                     this, SLOT(first()));
  tool_bar->addAction(QIcon::fromTheme("media-seek-backward"),"Previous Frame",
                     this, SLOT(previous()))->setShortcut(QKeySequence("Left"));
  tool_bar->addWidget(&frameNumber);
  connect(&frameNumber, SIGNAL(valueChanged(int)), SLOT(seek(int)));
  playAction = tool_bar->addAction(QIcon::fromTheme("media-playback-start"),
                                  QKeySequence("Play"));
  playAction->setCheckable(true);
  playAction->setShortcut(QKeySequence("Space"));
  connect(playAction, SIGNAL(triggered(bool)), SLOT(togglePlay(bool)));
  tool_bar->addWidget(&slider);
  slider.setOrientation(Qt::Horizontal);
  connect(&slider, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
  tool_bar->addAction(QIcon::fromTheme("media-seek-forward"), "Next Frame",
                     this, SLOT(next()))
          ->setShortcut(QKeySequence("Right"));
  tool_bar->addAction(QIcon::fromTheme("media-skip-forward"), "Last Frame",
                     this, SLOT(last()));

  // Set up the scene.
  view.setScene(&scene);
  view.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
  view.setFrameShape(QFrame::NoFrame);
  view.setDragMode(QGraphicsView::ScrollHandDrag);
  view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  setCentralWidget(&view);
  connect(&playTimer, SIGNAL(timeout()), SLOT(next()));

  QStringList args = qApp->arguments();
  args.removeFirst();
  if (args.isEmpty()) {
    open();
  } else {
    open(args);
  }
}

void Tracker::open() {
  open(QFileDialog::getOpenFileNames(
      this,
      tr("Select Images"),
      "",
      "Images (*.png *.jpg);;All Files (*.*)"));
}

void Tracker::open(QStringList paths) {
  scene.clear();
  pixmap = 0;
  clip_->Open(paths);
  frameNumber.setMaximum(clip_->size() - 1);
  slider.setMaximum(clip_->size() - 1);
  first();
}

void Tracker::seek(int frame) {
  // Bail out if there's nothing to do.
  if (frame == current_) {
    return;
  }
  if (frame < 0 || frame >= clip_->size()) {
    stop();
    return;
  }

  // Get and display the image.
  QImage image = clip_->Frame(frame);
  if (!pixmap) {
    pixmap = scene.addPixmap(QPixmap::fromImage(image));
    view.fitInView(pixmap);
  } else {
    pixmap->setPixmap(QPixmap::fromImage(image));
  }

  // If the shift is between consecutive frames, track the active trackers
  // from the previous frame into this one.
  vector<Marker> marker_in_previous_frame;
  tracks_->TracksInImage(current_, &marker_in_previous_frame);
  for (int i = 0; i < marker_in_previous_frame.size(); ++i) {
    const Marker &marker = marker_in_previous_frame[i];
    //if (active_tracks_.find(marker.track) == active_tracks_.end()) {
    //  continue
    //}
    // TODO(keir): For now this uses a fixed 32x32 region. What's needed is
    // an extension to use custom sized boxes around the tracked region.
    int size = 33;
    int half_size = size / 2;

    int x0 = marker.x - half_size;
    int y0 = marker.y - half_size;
    FloatImage old_patch;
    if (!CopyRegionFromQImage(current_image_, 32, 32,
                              &x0, &y0,
                              &old_patch)) {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }

    int x1 = marker.x - half_size;
    int y1 = marker.y - half_size;
    FloatImage new_patch;
    if (!CopyRegionFromQImage(image, 32, 32,
                              &x1, &y1,
                              &new_patch)) {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }

    double xx0 = marker.x - x0;
    double yy0 = marker.y - y0;
    double xx1 = marker.x - x1;
    double yy1 = marker.y - y1;
    if (region_tracker_->Track(old_patch, new_patch,
                               xx0, yy0,
                               &xx1, &yy1)) {
      tracks_->Insert(frame, marker.track, xx1, yy1);
    } else {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }
  }

  current_ = frame;
  current_image_ = image;
  slider.setValue(frame);
  frameNumber.setValue(frame);
}

void Tracker::first() {
  seek(0);
}
void Tracker::previous() {
  seek(current_ - 1);
}
void Tracker::next() {
  seek(current_ - 1);
}
void Tracker::last() {
  seek(clip_->size() - 1);
}

void Tracker::start() {
  playAction->setChecked(true);
  playTimer.start(100);
}
void Tracker::stop() {
  playAction->setChecked(false);
  playTimer.stop();
}
void Tracker::togglePlay(bool play) {
  if (play) {
    start();
  } else {
    stop();
  }
}

void Tracker::resizeEvent(QResizeEvent *) {
  view.fitInView(pixmap);
}
<commit_msg>Fix broken qt-tracker compile.<commit_after>// Copyright 2011 libmv authors
// Initial revision by Matthias Fauconneau.
//
#include "tracker.h"
#include "libmv/simple_pipeline/tracks.h"
#include "libmv/tools/tool.h"
#include "libmv/image/image.h"
#include "libmv/tracking/klt_region_tracker.h"
#include "libmv/tracking/pyramid_region_tracker.h"

#include <QFileDialog>
#include <QDesktopWidget>
#include <QGraphicsPixmapItem>
#include <QTime>
#include <QDebug>

using libmv::FloatImage;
using libmv::Marker;
using std::vector;

int main(int argc, char *argv[]) {
  libmv::Init("", &argc, &argv);
  QApplication app(argc, argv);
  Tracker window;
  window.show();
  return app.exec();
}

// Minimal scope profiling tool.
struct Profile {
  QTime time;
  Profile(const char* msg,const char* file,const int line) {
    fprintf(stderr,"%s:%d: %s... ",file,line,msg); fflush(stderr);
    time.start();
  }
  ~Profile() { fprintf(stderr,"%d ms\n",time.elapsed()); fflush(stderr); }
#define profile(msg) Profile p(msg,__FILE__, __LINE__)
};

// Only supports bags-of-files type movies for now; use QMovie later.
// TODO(keir): This doesn't belong in the header.
class Clip {
 public:
  Clip() {}
  Clip(QStringList paths) {
    Open(paths);
  }

  void Open(QStringList paths) {
    if (paths.isEmpty()) {
      return;
    }
    image_filenames_.clear();
    foreach(QString path, paths) {
      if (QFileInfo(path).isFile()) {
        image_filenames_ << path;
      } else {
        foreach(QString file,
                QDir(path).entryList(QStringList("*.jpg") <<"*.png",
                                     QDir::Files,
                                     QDir::Name)) {
          image_filenames_ << QDir(path).filePath(file);
        }
      }
    }
  }

  QImage Frame(int frame) {
    return QImage(image_filenames_[frame]);
  }

  int size() const { return image_filenames_.size(); }

 private:
  QList<QString> image_filenames_;
};

// Copy the region starting at *x0, *y0 with width w, h into region. If the
// region asked for is outside the image border, clipping is done and the
// returned region is smaller than requested. At return, *x0 and *y0 contain
// the top left pixel of *region in the original image (which may not match
// what was passed in). Returns true if any values were copied.
bool CopyRegionFromQImage(QImage image,
                          int w, int h,
                          int *x0, int *y0,
                          libmv::FloatImage *region) {
  const unsigned char *data = image.bits();
  int width = image.width();
  int height = image.height();

  // Clip the region on the upper right.
  if (*x0 < 0) {
    w += *x0;
    *x0 = 0;
  }
  if (*y0 < 0) {
    h += *y0;
    *y0 = 0;
  }

  // Clip the region on the lower left.
  w = std::min(w, width - *x0);
  h = std::min(h, height - *y0);

  // The region is entirely outside the given image.
  if (w <= 0 || h <= 0) {
    return false;
  }

  // Now that clipping is done, do the blit.
  region->resize(w, h);
  for (int y = *y0; y < *y0 + h; ++y) {
    for (int x = *x0; x < *x0 + w; ++x) {
      // This assumes BGR row-major ordering for the QImage's raw bytes.
      // TODO(keir): Check that that is true!
      const unsigned char *pixel = data + (y * width + x) * 4;
      (*region)(y, x, 0) = (0.2126 * pixel[2] +
                            0.7152 * pixel[1] +
                            0.0722 * pixel[0]) / 255;
    }
  }
  return true;
}

libmv::RegionTracker *CreateRegionTracker() {
  return new libmv::PyramidRegionTracker(new libmv::KltRegionTracker, 3);
}

// TODO(keir): Leaks clip and tracks.
Tracker::Tracker()
    : current_(-1),
      clip_(new Clip),
      tracks_(new libmv::Tracks),
      region_tracker_(CreateRegionTracker()),
      max_track_(-1) {
  setWindowTitle("LibMV Simple Tracker");
  setMaximumSize(qApp->desktop()->availableGeometry().size());

  // Create the toolbar.
  QToolBar* tool_bar = addToolBar("Main Toolbar");
  tool_bar->setObjectName("mainToolbar");
  tool_bar->addAction(QIcon::fromTheme("document-open"), "Open...",
                     this, SLOT(open()));
  tool_bar->addAction(QIcon::fromTheme("media-skip-backward"), "First Frame",
                     this, SLOT(first()));
  tool_bar->addAction(QIcon::fromTheme("media-seek-backward"),"Previous Frame",
                     this, SLOT(previous()))->setShortcut(QKeySequence("Left"));
  tool_bar->addWidget(&frameNumber);
  connect(&frameNumber, SIGNAL(valueChanged(int)), SLOT(seek(int)));
  playAction = tool_bar->addAction(QIcon::fromTheme("media-playback-start"),
                                  QKeySequence("Play"));
  playAction->setCheckable(true);
  playAction->setShortcut(QKeySequence("Space"));
  connect(playAction, SIGNAL(triggered(bool)), SLOT(togglePlay(bool)));
  tool_bar->addWidget(&slider);
  slider.setOrientation(Qt::Horizontal);
  connect(&slider, SIGNAL(sliderMoved(int)), SLOT(seek(int)));
  tool_bar->addAction(QIcon::fromTheme("media-seek-forward"), "Next Frame",
                     this, SLOT(next()))
          ->setShortcut(QKeySequence("Right"));
  tool_bar->addAction(QIcon::fromTheme("media-skip-forward"), "Last Frame",
                     this, SLOT(last()));

  // Set up the scene.
  view.setScene(&scene);
  view.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
  view.setFrameShape(QFrame::NoFrame);
  view.setDragMode(QGraphicsView::ScrollHandDrag);
  view.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  view.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
  setCentralWidget(&view);
  connect(&playTimer, SIGNAL(timeout()), SLOT(next()));

  QStringList args = qApp->arguments();
  args.removeFirst();
  if (args.isEmpty()) {
    open();
  } else {
    open(args);
  }
}

void Tracker::open() {
  open(QFileDialog::getOpenFileNames(
      this,
      tr("Select Images"),
      "",
      "Images (*.png *.jpg);;All Files (*.*)"));
}

void Tracker::open(QStringList paths) {
  scene.clear();
  pixmap = 0;
  clip_->Open(paths);
  frameNumber.setMaximum(clip_->size() - 1);
  slider.setMaximum(clip_->size() - 1);
  first();
}

void Tracker::seek(int frame) {
  // Bail out if there's nothing to do.
  if (frame == current_) {
    return;
  }
  if (frame < 0 || frame >= clip_->size()) {
    stop();
    return;
  }

  // Get and display the image.
  QImage image = clip_->Frame(frame);
  if (!pixmap) {
    pixmap = scene.addPixmap(QPixmap::fromImage(image));
    view.fitInView(pixmap);
  } else {
    pixmap->setPixmap(QPixmap::fromImage(image));
  }

  // If the shift is between consecutive frames, track the active trackers
  // from the previous frame into this one.
  vector<Marker> marker_in_previous_frame;
  tracks_->TracksInImage(current_, &marker_in_previous_frame);
  for (int i = 0; i < marker_in_previous_frame.size(); ++i) {
    const Marker &marker = marker_in_previous_frame[i];
    //if (active_tracks_.find(marker.track) == active_tracks_.end()) {
    //  continue
    //}
    // TODO(keir): For now this uses a fixed 32x32 region. What's needed is
    // an extension to use custom sized boxes around the tracked region.
    int size = 33;
    int half_size = size / 2;

    int x0 = marker.x - half_size;
    int y0 = marker.y - half_size;
    FloatImage old_patch;
    if (!CopyRegionFromQImage(current_image_, 32, 32,
                              &x0, &y0,
                              &old_patch)) {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }

    int x1 = marker.x - half_size;
    int y1 = marker.y - half_size;
    FloatImage new_patch;
    if (!CopyRegionFromQImage(image, 32, 32,
                              &x1, &y1,
                              &new_patch)) {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }

    double xx0 = marker.x - x0;
    double yy0 = marker.y - y0;
    double xx1 = marker.x - x1;
    double yy1 = marker.y - y1;
    if (region_tracker_->Track(old_patch, new_patch,
                               xx0, yy0,
                               &xx1, &yy1)) {
      tracks_->Insert(frame, marker.track, xx1, yy1);
    } else {
      // TODO(keir): Must handle this case! Currently no marker delete.
    }
  }

  current_ = frame;
  current_image_ = image;
  slider.setValue(frame);
  frameNumber.setValue(frame);
}

void Tracker::first() {
  seek(0);
}
void Tracker::previous() {
  seek(current_ - 1);
}
void Tracker::next() {
  seek(current_ - 1);
}
void Tracker::last() {
  seek(clip_->size() - 1);
}

void Tracker::start() {
  playAction->setChecked(true);
  playTimer.start(100);
}
void Tracker::stop() {
  playAction->setChecked(false);
  playTimer.stop();
}
void Tracker::togglePlay(bool play) {
  if (play) {
    start();
  } else {
    stop();
  }
}

void Tracker::resizeEvent(QResizeEvent *) {
  view.fitInView(pixmap);
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file SimpleQueue.hxx
 * A simple fast single-linked queue class with non-virtual methods.
 *
 * @author Balazs Racz
 * @date 6 Apr 2015
 */

#ifndef _UTILS_SIMPLEQUEUE_HXX_
#define _UTILS_SIMPLEQUEUE_HXX_

#include "utils/QMember.hxx"

/// A simple fast single-linked queue class with non-virtual methods.
///
/// This structure allows putting QMember descendants (including buffers,
/// stateflows, exeutables etc) into a queue without incurring the overhead of
/// the virtual methods and allocation semantics of BufferQueue.
///
/// SimpleQueue does not support asynchronous access, allocation/deallocation
/// and notification, but supports strongly typed access (via @ref TypedQueue)
/// and queueing types other than Buffers. Almost all functions on SimpleQueue
/// compile into just a few machine instructions.
class SimpleQueue {
public:
    SimpleQueue() : head_(nullptr) {}

    bool empty() {
        return !head_;
    }

    void push_front(QMember* member) {
        HASSERT(!member->next);
        member->next = head_;
        head_ = member;
    }

    QMember* pop_front() {
        HASSERT(head_);
        QMember* f = head_;
        head_ = f->next;
        f->next = nullptr;
        return f;
    }

    QMember* front() const {
        return head_;
    }

    class end_iterator {};

    /// STL-compatible iterator for SimpleQueue.
    class iterator {
    public:
        iterator(QMember** link): link_(link) {
            HASSERT(link_);
        }

        bool operator==(const iterator& o) const {
            return *link_ == *o.link_;
        }

        bool operator==(const end_iterator& o) const {
            return *link_ == nullptr;
        }

        bool operator!=(const iterator& o) const {
            return *link_ != *o.link_;
        }

        bool operator!=(const end_iterator& o) const {
            return *link_ != nullptr;
        }

        iterator& operator++() {
            HASSERT(*link_);
            link_ = &(*link_)->next;
            return *this;
        }

        QMember* operator->() {
            return *link_;
        }

        QMember& operator*() {
            return **link_;
        }

    private:
        friend class SimpleQueue;

        QMember** link_;
    };

    /// STL-compatible iterator for TypedQueue.
    template<class T> class typed_iterator : public iterator {
    public:
        typed_iterator(QMember** link): iterator(link) {}
        T* operator->() {
            return static_cast<T*>(*link_);
        }
        T& operator*() {
            return static_cast<T&>(**link_);
        }
    };

    iterator begin() {
        return iterator(&head_);
    }

    /** Returns a sentinel to compare against for determining when an iteration
     * is done. This sentinel cannot be used to insert entries at the end of
     * the queue. */
    end_iterator end() {
        return end_iterator{};
    }

    /** Inserts the element entry before the position.  The iterator will point
     * to the new member.
     */
    void insert(const iterator& position, QMember* entry) {
        HASSERT(!entry->next);
        entry->next = *position.link_;
        *position.link_ = entry;
    }

    /** Removes the entry pointed to by the iterator. */
    void erase(const iterator& position) {
        QMember* m = *position.link_;
        HASSERT(m);
        *position.link_ = m->next;
        m->next = nullptr;
    }

protected:
    /** Used as a guard for comparing against for the end of the queue. */
    static QMember* const PTR_END;

    QMember* head_;
};

/// A simple, fast, type-safe single-linked queue class with non-virtual
/// methods.
///
/// see @ref SimpleQueue for details.
template<class T>
class TypedQueue : public SimpleQueue {
public:
    void push_front(T* entry) {
        SimpleQueue::push_front(entry);
    }

    T* pop_front() {
        return static_cast<T*>(SimpleQueue::pop_front());
    }

    T* front() const {
        return static_cast<T*>(SimpleQueue::front());
    }

    typedef typed_iterator<T> iterator;

    iterator begin() {
        return iterator(&head_);
    }
};

#endif // _UTILS_SIMPLEQUEUE_HXX_
<commit_msg>Clarifies deletion iterator semantics.<commit_after>/** \copyright
 * Copyright (c) 2015, Balazs Racz
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file SimpleQueue.hxx
 * A simple fast single-linked queue class with non-virtual methods.
 *
 * @author Balazs Racz
 * @date 6 Apr 2015
 */

#ifndef _UTILS_SIMPLEQUEUE_HXX_
#define _UTILS_SIMPLEQUEUE_HXX_

#include "utils/QMember.hxx"

/// A simple fast single-linked queue class with non-virtual methods.
///
/// This structure allows putting QMember descendants (including buffers,
/// stateflows, exeutables etc) into a queue without incurring the overhead of
/// the virtual methods and allocation semantics of BufferQueue.
///
/// SimpleQueue does not support asynchronous access, allocation/deallocation
/// and notification, but supports strongly typed access (via @ref TypedQueue)
/// and queueing types other than Buffers. Almost all functions on SimpleQueue
/// compile into just a few machine instructions.
class SimpleQueue {
public:
    SimpleQueue() : head_(nullptr) {}

    bool empty() {
        return !head_;
    }

    void push_front(QMember* member) {
        HASSERT(!member->next);
        member->next = head_;
        head_ = member;
    }

    QMember* pop_front() {
        HASSERT(head_);
        QMember* f = head_;
        head_ = f->next;
        f->next = nullptr;
        return f;
    }

    QMember* front() const {
        return head_;
    }

    class end_iterator {};

    /// STL-compatible iterator for SimpleQueue.
    class iterator {
    public:
        iterator(QMember** link): link_(link) {
            HASSERT(link_);
        }

        bool operator==(const iterator& o) const {
            return *link_ == *o.link_;
        }

        bool operator==(const end_iterator& o) const {
            return *link_ == nullptr;
        }

        bool operator!=(const iterator& o) const {
            return *link_ != *o.link_;
        }

        bool operator!=(const end_iterator& o) const {
            return *link_ != nullptr;
        }

        iterator& operator++() {
            HASSERT(*link_);
            link_ = &(*link_)->next;
            return *this;
        }

        QMember* operator->() {
            return *link_;
        }

        QMember& operator*() {
            return **link_;
        }

    private:
        friend class SimpleQueue;

        QMember** link_;
    };

    /// STL-compatible iterator for TypedQueue.
    template<class T> class typed_iterator : public iterator {
    public:
        typed_iterator(QMember** link): iterator(link) {}
        T* operator->() {
            return static_cast<T*>(*link_);
        }
        T& operator*() {
            return static_cast<T&>(**link_);
        }
    };

    iterator begin() {
        return iterator(&head_);
    }

    /** Returns a sentinel to compare against for determining when an iteration
     * is done. This sentinel cannot be used to insert entries at the end of
     * the queue. */
    end_iterator end() {
        return end_iterator{};
    }

    /** Inserts the element entry before the position.  The iterator will point
     * to the new member.
     */
    void insert(const iterator& position, QMember* entry) {
        HASSERT(!entry->next);
        entry->next = *position.link_;
        *position.link_ = entry;
    }

    /** Removes the entry pointed to by the iterator. The iterator will
     * afterwards point to the next member after the removed one (or end() if
     * this was the last member). Invalidates any other iterator pointing just
     * behind the marked position (but not iterators pointing elsewhere). */
    void erase(const iterator& position) {
        QMember* m = *position.link_;
        HASSERT(m);
        *position.link_ = m->next;
        m->next = nullptr;
    }

protected:
    /** Used as a guard for comparing against for the end of the queue. */
    static QMember* const PTR_END;

    QMember* head_;
};

/// A simple, fast, type-safe single-linked queue class with non-virtual
/// methods.
///
/// see @ref SimpleQueue for details.
template<class T>
class TypedQueue : public SimpleQueue {
public:
    void push_front(T* entry) {
        SimpleQueue::push_front(entry);
    }

    T* pop_front() {
        return static_cast<T*>(SimpleQueue::pop_front());
    }

    T* front() const {
        return static_cast<T*>(SimpleQueue::front());
    }

    typedef typed_iterator<T> iterator;

    iterator begin() {
        return iterator(&head_);
    }
};

#endif // _UTILS_SIMPLEQUEUE_HXX_
<|endoftext|>


<commit_before>#include <utils/json/Object.hpp>
//#include <utils/json/Value.hpp>
#include <stdexcept>

namespace utils
{
    namespace json
    {

        Object::Object()
        {
        }

        std::ostream& operator<<(std::ostream& stream, const Object& self)
        {
            self._printIdent(stream,0);
            return stream;
        }

        void Object::_printIdent(std::ostream& stream, int i)const
        {
            /*if(i >0)
                stream<<"\n";
            ident(stream,i);*/
            stream<<"{\n";
            ++i;

            if(_values.size()>0)
            {
                auto begin = _values.begin();
                auto end = _values.end();

                _ident(stream,i);
                stream<<"\""<<begin->first<<"\" : ";
                begin->second._printIdent(stream,i);

                ++begin;
                while(begin!=end)
                {
                    stream<<",\n";
                    _ident(stream,i);
                    stream<<"\""<<begin->first<<"\" : ";
                    begin->second._printIdent(stream,i);
                    ++begin;
                }
            }
            --i;
            stream<<"\n";
            _ident(stream,i);
            stream<<"}";
        }

        Value& Object::operator[] (const std::string& key)
        {
            return _values[key];
        }

        const Value& Object::operator[] (const std::string& key) const
        {
           auto it = _values.find(key);
			if(it != _values.end())
			{
				return it->second;
            }

			throw std::out_of_range(std::string("Index '")+key+"' is out of range");
        }

        int Object::count(const std::string& key) const
        {
            return _values.count(key);
        }

        std::unordered_map<std::string, Value>::const_iterator Object::begin() const
        {
            return _values.begin();
        }

        std::unordered_map<std::string, Value>::const_iterator Object::end() const
        {
            return _values.end();
        }

        std::unordered_map<std::string, Value>::iterator Object::begin()
        {
            return _values.begin();
        }

        std::unordered_map<std::string, Value>::iterator Object::end()
        {
            return _values.end();
        }

        size_t Object::size() const
        {
            return _values.size();
        }

        void Object::merge(const Object& other, bool recursive, bool replace_old)
        {
            auto end = other._values.end();
            for (const auto& val : other._values)
            {
                auto it = this->_values.find(val.first);
                if (it != this->_values.end()) //has key
                {
                    if (replace_old) //replace existing value
                    {
                        if (recursive && it->second.isObject() && val.second.isObject()) //merge if both are object
                        {
                            it->second.asObject().merge(val.second.asObject(), recursive, replace_old);
                        }
                        else //else change value
                        {
                            it->second = val.second;
                        }
                    }
                }
                else //add value
                {
                    this->_values.insert(val);
                }
            }
        }

        void Object::_ident(std::ostream& out,int max)
        {
            for(int i=0;i<max;++i)
                out<<"    ";
        }

    }
}
<commit_msg>remove unused value<commit_after>#include <utils/json/Object.hpp>
//#include <utils/json/Value.hpp>
#include <stdexcept>

namespace utils
{
    namespace json
    {

        Object::Object()
        {
        }

        std::ostream& operator<<(std::ostream& stream, const Object& self)
        {
            self._printIdent(stream,0);
            return stream;
        }

        void Object::_printIdent(std::ostream& stream, int i)const
        {
            /*if(i >0)
                stream<<"\n";
            ident(stream,i);*/
            stream<<"{\n";
            ++i;

            if(_values.size()>0)
            {
                auto begin = _values.begin();
                auto end = _values.end();

                _ident(stream,i);
                stream<<"\""<<begin->first<<"\" : ";
                begin->second._printIdent(stream,i);

                ++begin;
                while(begin!=end)
                {
                    stream<<",\n";
                    _ident(stream,i);
                    stream<<"\""<<begin->first<<"\" : ";
                    begin->second._printIdent(stream,i);
                    ++begin;
                }
            }
            --i;
            stream<<"\n";
            _ident(stream,i);
            stream<<"}";
        }

        Value& Object::operator[] (const std::string& key)
        {
            return _values[key];
        }

        const Value& Object::operator[] (const std::string& key) const
        {
           auto it = _values.find(key);
			if(it != _values.end())
			{
				return it->second;
            }

			throw std::out_of_range(std::string("Index '")+key+"' is out of range");
        }

        int Object::count(const std::string& key) const
        {
            return _values.count(key);
        }

        std::unordered_map<std::string, Value>::const_iterator Object::begin() const
        {
            return _values.begin();
        }

        std::unordered_map<std::string, Value>::const_iterator Object::end() const
        {
            return _values.end();
        }

        std::unordered_map<std::string, Value>::iterator Object::begin()
        {
            return _values.begin();
        }

        std::unordered_map<std::string, Value>::iterator Object::end()
        {
            return _values.end();
        }

        size_t Object::size() const
        {
            return _values.size();
        }

        void Object::merge(const Object& other, bool recursive, bool replace_old)
        {
            for (const auto& val : other._values)
            {
                auto it = this->_values.find(val.first);
                if (it != this->_values.end()) //has key
                {
                    if (replace_old) //replace existing value
                    {
                        if (recursive && it->second.isObject() && val.second.isObject()) //merge if both are object
                        {
                            it->second.asObject().merge(val.second.asObject(), recursive, replace_old);
                        }
                        else //else change value
                        {
                            it->second = val.second;
                        }
                    }
                }
                else //add value
                {
                    this->_values.insert(val);
                }
            }
        }

        void Object::_ident(std::ostream& out,int max)
        {
            for(int i=0;i<max;++i)
                out<<"    ";
        }

    }
}
<|endoftext|>