<commit_before>#include "Catch.hpp"

#include "RaZ/Math/MathUtils.hpp"

TEST_CASE("MathUtils linear interpolation") {
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.25f) == 0.25f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.75f) == 0.75f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 1.f) == 1.f);

  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.f) == -5.f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.25f) == -2.5f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.5f) == 0.f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.75f) == 2.5f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 1.f) == 5.f);

  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.f) == -1.43f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.25f) == 0.9575f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.5f) == 3.345f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.75f) == 5.7325001f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 1.f) == 8.12f);
}

TEST_CASE("MathUtils smoothstep value") {
  CHECK(Raz::MathUtils::smoothstep(0.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(0.25f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(0.75f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(1.f) == 1.f);
}

TEST_CASE("MathUtils smoothstep range") {
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, -1.f) == 0.f); // If value < minThresh, returns 0
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.25f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.75f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 1.f) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 2.f) == 1.f); // If value > maxThresh, returns 1

  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -10.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -5.f - std::numeric_limits<float>::epsilon()) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -5.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -4.5f) == 0.00725000072f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -2.5f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -0.5f) == 0.42524996f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 0.f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 0.5f) == 0.57475001f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 2.5f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 4.5f) == 0.99274999f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 5.f) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 5.f + std::numeric_limits<float>::epsilon()) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 10.f) == 1.f);
}

TEST_CASE("MathUtils smootherstep value") {
  CHECK(Raz::MathUtils::smootherstep(0.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(0.25f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(0.75f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(1.f) == 1.f);
}

TEST_CASE("MathUtils smootherstep range") {
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, -1.f) == 0.f); // If value < minThresh, returns 0
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.25f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.75f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 1.f) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 2.f) == 1.f); // If value > maxThresh, returns 1

  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -10.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -5.f - std::numeric_limits<float>::epsilon()) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -5.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -4.5f) == 0.0011581251f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -2.5f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -0.5f) == 0.40687308f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 0.f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 0.5f) == 0.59312695f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 2.5f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 4.5f) == 0.99884182f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 5.f) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 5.f + std::numeric_limits<float>::epsilon()) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 10.f) == 1.f);
}
<commit_msg>[Math/MathUtils] Added unit tests to check lerp()'s behavior with vectors<commit_after>#include "Catch.hpp"

#include "RaZ/Math/MathUtils.hpp"
#include "RaZ/Math/Vector.hpp"

TEST_CASE("MathUtils lerp arithmetic types") {
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.25f) == 0.25f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 0.75f) == 0.75f);
  CHECK(Raz::MathUtils::lerp(0.f, 1.f, 1.f) == 1.f);

  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.f) == -5.f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.25f) == -2.5f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.5f) == 0.f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 0.75f) == 2.5f);
  CHECK(Raz::MathUtils::lerp(-5.f, 5.f, 1.f) == 5.f);

  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.f) == -1.43f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.25f) == 0.9575f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.5f) == 3.345f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 0.75f) == 5.7325001f);
  CHECK(Raz::MathUtils::lerp(-1.43f, 8.12f, 1.f) == 8.12f);

  CHECK(Raz::MathUtils::lerp(457, 5324, 0.f) == 457);
  CHECK(Raz::MathUtils::lerp(457, 5324, 0.25f) == 1673);
  CHECK(Raz::MathUtils::lerp(457, 5324, 0.5f) == 2890);
  CHECK(Raz::MathUtils::lerp(457, 5324, 0.75f) == 4107);
  CHECK(Raz::MathUtils::lerp(457, 5324, 1.f) == 5324);
}

TEST_CASE("MathUtils lerp vector") {
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(0.f), Raz::Vec3f(1.f), 0.f) == Raz::Vec3f(0.f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(0.f), Raz::Vec3f(1.f), 0.25f) == Raz::Vec3f(0.25f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(0.f), Raz::Vec3f(1.f), 0.5f) == Raz::Vec3f(0.5f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(0.f), Raz::Vec3f(1.f), 0.75f) == Raz::Vec3f(0.75f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(0.f), Raz::Vec3f(1.f), 1.f) == Raz::Vec3f(1.f));

  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(1.f, 2.f, 3.f), Raz::Vec3f(3.f, 2.f, 1.f), 0.f) == Raz::Vec3f(1.f, 2.f, 3.f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(1.f, 2.f, 3.f), Raz::Vec3f(3.f, 2.f, 1.f), 0.25f) == Raz::Vec3f(1.5f, 2.f, 2.5f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(1.f, 2.f, 3.f), Raz::Vec3f(3.f, 2.f, 1.f), 0.5f) == Raz::Vec3f(2.f, 2.f, 2.f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(1.f, 2.f, 3.f), Raz::Vec3f(3.f, 2.f, 1.f), 0.75f) == Raz::Vec3f(2.5f, 2.f, 1.5f));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3f(1.f, 2.f, 3.f), Raz::Vec3f(3.f, 2.f, 1.f), 1.f) == Raz::Vec3f(3.f, 2.f, 1.f));

  CHECK(Raz::MathUtils::lerp(Raz::Vec3i(140, 874, 3541), Raz::Vec3i(841, 53, 4712), 0.f) == Raz::Vec3i(140, 874, 3541));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3i(140, 874, 3541), Raz::Vec3i(841, 53, 4712), 0.25f) == Raz::Vec3i(315, 668, 3833));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3i(140, 874, 3541), Raz::Vec3i(841, 53, 4712), 0.5f) == Raz::Vec3i(490, 463, 4126));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3i(140, 874, 3541), Raz::Vec3i(841, 53, 4712), 0.75f) == Raz::Vec3i(665, 257, 4419));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3i(140, 874, 3541), Raz::Vec3i(841, 53, 4712), 1.f) == Raz::Vec3i(841, 53, 4712));

  CHECK(Raz::MathUtils::lerp(Raz::Vec3b(255, 140, 27), Raz::Vec3b(75, 241, 173), 0.f) == Raz::Vec3b(255, 140, 27));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3b(255, 140, 27), Raz::Vec3b(75, 241, 173), 0.25f) == Raz::Vec3b(209, 165, 63));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3b(255, 140, 27), Raz::Vec3b(75, 241, 173), 0.5f) == Raz::Vec3b(164, 190, 99));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3b(255, 140, 27), Raz::Vec3b(75, 241, 173), 0.75f) == Raz::Vec3b(119, 215, 135));
  CHECK(Raz::MathUtils::lerp(Raz::Vec3b(255, 140, 27), Raz::Vec3b(75, 241, 173), 1.f) == Raz::Vec3b(75, 241, 173));
}

TEST_CASE("MathUtils smoothstep value") {
  CHECK(Raz::MathUtils::smoothstep(0.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(0.25f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(0.75f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(1.f) == 1.f);
}

TEST_CASE("MathUtils smoothstep range") {
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, -1.f) == 0.f); // If value < minThresh, returns 0
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.25f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 0.75f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 1.f) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(0.f, 1.f, 2.f) == 1.f); // If value > maxThresh, returns 1

  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -10.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -5.f - std::numeric_limits<float>::epsilon()) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -5.f) == 0.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -4.5f) == 0.00725000072f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -2.5f) == 0.15625f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, -0.5f) == 0.42524996f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 0.f) == 0.5f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 0.5f) == 0.57475001f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 2.5f) == 0.84375f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 4.5f) == 0.99274999f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 5.f) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 5.f + std::numeric_limits<float>::epsilon()) == 1.f);
  CHECK(Raz::MathUtils::smoothstep(-5.f, 5.f, 10.f) == 1.f);
}

TEST_CASE("MathUtils smootherstep value") {
  CHECK(Raz::MathUtils::smootherstep(0.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(0.25f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(0.75f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(1.f) == 1.f);
}

TEST_CASE("MathUtils smootherstep range") {
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, -1.f) == 0.f); // If value < minThresh, returns 0
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.25f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.5f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 0.75f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 1.f) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(0.f, 1.f, 2.f) == 1.f); // If value > maxThresh, returns 1

  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -10.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -5.f - std::numeric_limits<float>::epsilon()) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -5.f) == 0.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -4.5f) == 0.0011581251f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -2.5f) == 0.103515625f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, -0.5f) == 0.40687308f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 0.f) == 0.5f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 0.5f) == 0.59312695f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 2.5f) == 0.89648438f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 4.5f) == 0.99884182f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 5.f) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 5.f + std::numeric_limits<float>::epsilon()) == 1.f);
  CHECK(Raz::MathUtils::smootherstep(-5.f, 5.f, 10.f) == 1.f);
}
<|endoftext|>


<commit_before>//===--------------------- SummaryView.cpp -------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements the functionalities used by the SummaryView to print
/// the report information.
///
//===----------------------------------------------------------------------===//

#include "SummaryView.h"
#include "Support.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Format.h"

namespace mca {

#define DEBUG_TYPE "llvm-mca"

using namespace llvm;

void SummaryView::onInstructionEvent(const HWInstructionEvent &Event) {
  // We are only interested in the "instruction dispatched" events generated by
  // the dispatch stage for instructions that are part of iteration #0.
  if (Event.Type != HWInstructionEvent::Dispatched)
    return;

  if (Event.IR.getSourceIndex() >= Source.size())
    return;

  // Update the cumulative number of resource cycles based on the processor
  // resource usage information available from the instruction descriptor. We need to
  // compute the cumulative number of resource cycles for every processor
  // resource which is consumed by an instruction of the block.
  const Instruction &Inst = *Event.IR.getInstruction();
  const InstrDesc &Desc = Inst.getDesc();
  NumMicroOps += Desc.NumMicroOps;
  for (const std::pair<uint64_t, const ResourceUsage> &RU : Desc.Resources) {
    if (!RU.second.size())
      continue;

    assert(RU.second.NumUnits && "Expected more than one unit used!");
    if (ProcResourceUsage.find(RU.first) == ProcResourceUsage.end()) {
      ProcResourceUsage[RU.first] = RU.second.size();
      continue;
    }

    ProcResourceUsage[RU.first] += RU.second.size();
  }
}

double SummaryView::getBlockRThroughput() const {
  assert(NumMicroOps && "Expected at least one micro opcode!");

  SmallVector<uint64_t, 8> Masks(SM.getNumProcResourceKinds());
  computeProcResourceMasks(SM, Masks);

  // The block throughput is bounded from above by the hardware dispatch
  // throughput. That is because the DispatchWidth is an upper bound on the
  // number of opcodes that can be part of a single dispatch group.
  double Max = static_cast<double>(NumMicroOps) / DispatchWidth;

  // The block throughput is also limited by the amount of hardware parallelism.
  // The number of available resource units affects the resource pressure
  // distributed, as well as how many blocks can be executed every cycle.
  for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
    uint64_t Mask = Masks[I];
    const auto It = ProcResourceUsage.find_as(Mask);
    if (It != ProcResourceUsage.end()) {
      const MCProcResourceDesc &MCDesc = *SM.getProcResource(I);
      unsigned NumUnits = MCDesc.NumUnits;
      double Throughput = static_cast<double>(It->second) / NumUnits;
      Max = std::max(Max, Throughput);
    }
  }

  // The block reciprocal throughput is computed as the MAX of:
  //  -  (#uOps / DispatchWidth)
  //  -  (#units / resource cycles) for every consumed processor resource.
  return Max;
}

void SummaryView::printView(raw_ostream &OS) const {
  unsigned Iterations = Source.getNumIterations();
  unsigned Instructions = Source.size();
  unsigned TotalInstructions = Instructions * Iterations;
  double IPC = (double)TotalInstructions / TotalCycles;
  double BlockRThroughput = getBlockRThroughput();

  std::string Buffer;
  raw_string_ostream TempStream(Buffer);
  TempStream << "Iterations:        " << Iterations;
  TempStream << "\nInstructions:      " << TotalInstructions;
  TempStream << "\nTotal Cycles:      " << TotalCycles;
  TempStream << "\nDispatch Width:    " << DispatchWidth;
  TempStream << "\nIPC:               " << format("%.2f", IPC);
  TempStream << "\nBlock RThroughput: " << format("%.1f", BlockRThroughput)
             << '\n';
  TempStream.flush();
  OS << Buffer;
}
} // namespace mca.
<commit_msg>[llvm-mca] Fix a rounding problem in SummaryView.cpp exposed by r333204.<commit_after>//===--------------------- SummaryView.cpp -------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements the functionalities used by the SummaryView to print
/// the report information.
///
//===----------------------------------------------------------------------===//

#include "SummaryView.h"
#include "Support.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Format.h"

namespace mca {

#define DEBUG_TYPE "llvm-mca"

using namespace llvm;

void SummaryView::onInstructionEvent(const HWInstructionEvent &Event) {
  // We are only interested in the "instruction dispatched" events generated by
  // the dispatch stage for instructions that are part of iteration #0.
  if (Event.Type != HWInstructionEvent::Dispatched)
    return;

  if (Event.IR.getSourceIndex() >= Source.size())
    return;

  // Update the cumulative number of resource cycles based on the processor
  // resource usage information available from the instruction descriptor. We
  // need to compute the cumulative number of resource cycles for every
  // processor resource which is consumed by an instruction of the block.
  const Instruction &Inst = *Event.IR.getInstruction();
  const InstrDesc &Desc = Inst.getDesc();
  NumMicroOps += Desc.NumMicroOps;
  for (const std::pair<uint64_t, const ResourceUsage> &RU : Desc.Resources) {
    if (!RU.second.size())
      continue;

    assert(RU.second.NumUnits && "Expected more than one unit used!");
    if (ProcResourceUsage.find(RU.first) == ProcResourceUsage.end()) {
      ProcResourceUsage[RU.first] = RU.second.size();
      continue;
    }

    ProcResourceUsage[RU.first] += RU.second.size();
  }
}

double SummaryView::getBlockRThroughput() const {
  assert(NumMicroOps && "Expected at least one micro opcode!");

  SmallVector<uint64_t, 8> Masks(SM.getNumProcResourceKinds());
  computeProcResourceMasks(SM, Masks);

  // The block throughput is bounded from above by the hardware dispatch
  // throughput. That is because the DispatchWidth is an upper bound on the
  // number of opcodes that can be part of a single dispatch group.
  double Max = static_cast<double>(NumMicroOps) / DispatchWidth;

  // The block throughput is also limited by the amount of hardware parallelism.
  // The number of available resource units affects the resource pressure
  // distributed, as well as how many blocks can be executed every cycle.
  for (unsigned I = 0, E = SM.getNumProcResourceKinds(); I < E; ++I) {
    uint64_t Mask = Masks[I];
    const auto It = ProcResourceUsage.find_as(Mask);
    if (It != ProcResourceUsage.end()) {
      const MCProcResourceDesc &MCDesc = *SM.getProcResource(I);
      unsigned NumUnits = MCDesc.NumUnits;
      double Throughput = static_cast<double>(It->second) / NumUnits;
      Max = std::max(Max, Throughput);
    }
  }

  // The block reciprocal throughput is computed as the MAX of:
  //  -  (#uOps / DispatchWidth)
  //  -  (#units / resource cycles) for every consumed processor resource.
  return Max;
}

void SummaryView::printView(raw_ostream &OS) const {
  unsigned Iterations = Source.getNumIterations();
  unsigned Instructions = Source.size();
  unsigned TotalInstructions = Instructions * Iterations;
  double IPC = (double)TotalInstructions / TotalCycles;
  double BlockRThroughput = getBlockRThroughput();

  std::string Buffer;
  raw_string_ostream TempStream(Buffer);
  TempStream << "Iterations:        " << Iterations;
  TempStream << "\nInstructions:      " << TotalInstructions;
  TempStream << "\nTotal Cycles:      " << TotalCycles;
  TempStream << "\nDispatch Width:    " << DispatchWidth;
  TempStream << "\nIPC:               " << format("%.2f", IPC);

  // Round to the block reciprocal throughput to the nearest tenth.
  TempStream << "\nBlock RThroughput: "
             << format("%.1f", floor((BlockRThroughput * 10) + 0.5) / 10)
             << '\n';
  TempStream.flush();
  OS << Buffer;
}
} // namespace mca.
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * 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 <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -t EVENT_TYPES Fully-qualified probe names to show,
       joined by a comma, e.g. "quicly:accept,quicly:free"
    -s RESPONSE_HEADER_NAMES  Response header names to show,
       joined by a comma, e.g. "content-type" (case-insensitive)
    -h Print this help and exit
    -d Print debugging information (-dd shows more)
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept,quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header,h2o:h3s_accept,h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static const std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static const std::vector<std::string> split_str(char delim, const std::string &s)
{
    std::vector<std::string> tokens;
    std::size_t from = 0;
    for (std::size_t i = 0; i < s.size(); ++i) {
        if (s[i] == delim) {
            tokens.push_back(s.substr(from, i - from));
            from = i + 1;
        }
    }
    if (from <= s.size()) {
        tokens.push_back(s.substr(from, s.size()));
    }
    return tokens;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    int debug = 0;
    FILE *outfp = stdout;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            for (const auto &item : split_str(',', optarg)) {
                event_type_filters.push_back(item);
            }
            break;
        case 's':
            for (const auto &item : split_str(',', optarg)) {
                response_header_filters.push_back(item);
            }
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (debug >= 2) {
        fprintf(stderr, "event_type_filters=");
        for (size_t i = 0; i < event_type_filters.size(); i++) {
            if (i > 0) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", event_type_filters[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;
    for (const auto &probe_def : tracer->usdt_probes()) {
        if (event_type_filters.empty() || std::find(event_type_filters.cbegin(), event_type_filters.cend(),
                                                    probe_def.provider + ":" + probe_def.name) != event_type_filters.cend()) {
            probes.push_back(ebpf::USDT(h2o_pid, probe_def.provider, probe_def.name, probe_def.probe_func));
        }
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<commit_msg>h2olog: add -l to list available USDT names<commit_after>/*
 * Copyright (c) 2019-2020 Fastly, Inc., Toru Maesaka, Goro Fuji
 *
 * 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 <memory>
#include <vector>
#include <algorithm>
#include <bcc/BPF.h>
extern "C" {
#include <unistd.h>
#include <stdarg.h>
#include <sys/time.h>
#include "h2o/memory.h"
#include "h2o/version.h"
}
#include "h2olog.h"

#define POLL_TIMEOUT (1000)
#define PERF_BUFFER_PAGE_COUNT 256

static void usage(void)
{
    printf(R"(h2olog (h2o v%s)
Usage: h2olog -p PID
       h2olog quic -p PID
Optional arguments:
    -d Print debugging information (-dd shows more)
    -h Print this help and exit
    -l Print the list of handled USDTs and exit
    -s RESPONSE_HEADER_NAMES  Response header names to show,
       joined by a comma, e.g. "content-type" (case-insensitive)
    -t EVENT_TYPES Fully-qualified probe names to show,
       joined by a comma, e.g. "quicly:accept,quicly:free"
    -w Path to write the output (default: stdout)

Examples:
    h2olog quic -p $(pgrep -o h2o)
    h2olog quic -p $(pgrep -o h2o) -t quicly:accept,quicly:free
    h2olog quic -p $(pgrep -o h2o) -t h2o:send_response_header,h2o:h3s_accept,h2o:h3s_destroy -s alt-svc
)",
           H2O_VERSION);
    return;
}

static void make_timestamp(char *buf, size_t buf_len)
{
    time_t t = time(NULL);
    struct tm tms;
    localtime_r(&t, &tms);
    const char *iso8601format = "%FT%TZ";
    strftime(buf, buf_len, iso8601format, &tms);
}

static void infof(const char *fmt, ...) __attribute__((format(printf, 1, 2)));

static void infof(const char *fmt, ...)
{
    char buf[1024];
    va_list args;
    va_start(args, fmt);
    vsnprintf(buf, sizeof(buf), fmt, args);
    va_end(args);

    char timestamp[128];
    make_timestamp(timestamp, sizeof(timestamp));

    fprintf(stderr, "%s %s\n", timestamp, buf);
}

uint64_t h2o_tracer::time_milliseconds()
{
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return (int64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
}

void h2o_tracer::show_event_per_sec(time_t *t0)
{
    time_t t1 = time(NULL);
    int64_t d = t1 - *t0;
    if (d > 10) {
        uint64_t c = stats_.num_events / d;
        if (c > 0) {
            if (stats_.num_lost > 0) {
                infof("%20" PRIu64 " events/s (possibly lost %" PRIu64 " events)", c, stats_.num_lost);
                stats_.num_lost = 0;
            } else {
                infof("%20" PRIu64 " events/s", c);
            }
            stats_.num_events = 0;
        }
        *t0 = t1;
    }
}

static void show_process(pid_t pid)
{
    char cmdline[256];
    char proc_file[256];
    snprintf(proc_file, sizeof(proc_file), "/proc/%d/cmdline", pid);
    FILE *f = fopen(proc_file, "r");
    if (f == nullptr) {
        fprintf(stderr, "Error: failed to open %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    size_t nread = fread(cmdline, 1, sizeof(cmdline), f);
    fclose(f);
    if (nread == 0) {
        fprintf(stderr, "Error: failed to read from %s: %s\n", proc_file, strerror(errno));
        exit(EXIT_FAILURE);
    }
    nread--; // skip trailing nul
    for (size_t i = 0; i < nread; i++) {
        if (cmdline[i] == '\0') {
            cmdline[i] = ' ';
        }
    }
    infof("Attaching pid=%d (%s)", pid, cmdline);
}

static const std::string join_str(const std::string &sep, const std::vector<std::string> &strs)
{
    std::string s;
    for (auto iter = strs.cbegin(); iter != strs.cend(); ++iter) {
        if (iter != strs.cbegin()) {
            s += sep;
        }
        s += *iter;
    }
    return s;
}

static const std::vector<std::string> split_str(char delim, const std::string &s)
{
    std::vector<std::string> tokens;
    std::size_t from = 0;
    for (std::size_t i = 0; i < s.size(); ++i) {
        if (s[i] == delim) {
            tokens.push_back(s.substr(from, i - from));
            from = i + 1;
        }
    }
    if (from <= s.size()) {
        tokens.push_back(s.substr(from, s.size()));
    }
    return tokens;
}

static std::string generate_header_filter_cflag(const std::vector<std::string> &tokens)
{
    std::vector<std::string> conditions;

    for (auto &token : tokens) {
        char buf[256];
        snprintf(buf, sizeof(buf), "/* %s */ (slen) == %zu", token.c_str(), token.size());
        std::vector<std::string> exprs = {buf};

        for (size_t i = 0; i < token.size(); ++i) {
            snprintf(buf, sizeof(buf), "(s)[%zu] == '%c'", i, token[i]);
            exprs.push_back(buf);
        }
        conditions.push_back("(" + join_str(" && ", exprs) + ")");
    }

    std::string cflag("-DCHECK_ALLOWED_RES_HEADER_NAME(s,slen)=(");
    cflag += join_str(" || ", conditions);
    cflag += ")";
    return cflag;
}

static std::string make_pid_cflag(const char *macro_name, pid_t pid)
{
    char buf[256];
    snprintf(buf, sizeof(buf), "-D%s=%d", macro_name, pid);
    return std::string(buf);
}

static void event_cb(void *context, void *data, int len)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_event(data, len);
}

static void lost_cb(void *context, uint64_t lost)
{
    h2o_tracer *tracer = (h2o_tracer *)context;
    tracer->handle_lost(lost);
}

int main(int argc, char **argv)
{
    std::unique_ptr<h2o_tracer> tracer;
    if (argc > 1 && strcmp(argv[1], "quic") == 0) {
        tracer.reset(create_quic_tracer());
        --argc;
        ++argv;
    } else {
        tracer.reset(create_http_tracer());
    }

    int debug = 0;
    int list_and_exit = 0;
    FILE *outfp = stdout;
    std::vector<std::string> event_type_filters;
    std::vector<std::string> response_header_filters;
    int c;
    pid_t h2o_pid = -1;
    while ((c = getopt(argc, argv, "hdp:t:s:w:l")) != -1) {
        switch (c) {
        case 'p':
            h2o_pid = atoi(optarg);
            break;
        case 't':
            for (const auto &item : split_str(',', optarg)) {
                event_type_filters.push_back(item);
            }
            break;
        case 's':
            for (const auto &item : split_str(',', optarg)) {
                response_header_filters.push_back(item);
            }
            break;
        case 'w':
            if ((outfp = fopen(optarg, "w")) == nullptr) {
                fprintf(stderr, "Error: failed to open %s: %s", optarg, strerror(errno));
                exit(EXIT_FAILURE);
            }
            break;
        case 'd':
            debug++;
            break;
        case 'l':
            list_and_exit++;
            break;
        case 'h':
            usage();
            exit(EXIT_SUCCESS);
        default:
            usage();
            exit(EXIT_FAILURE);
        }
    }

    if (argc > optind) {
        fprintf(stderr, "Error: too many aruments\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (list_and_exit) {
        for (const auto &usdt : tracer->usdt_probes()) {
            fprintf(stderr, "%s:%s\n", usdt.provider.c_str(), usdt.name.c_str());
        }
        exit(EXIT_SUCCESS);
    }

    if (h2o_pid == -1) {
        fprintf(stderr, "Error: -p option is missing\n");
        usage();
        exit(EXIT_FAILURE);
    }

    if (geteuid() != 0) {
        fprintf(stderr, "Error: root privilege is required\n");
        exit(EXIT_FAILURE);
    }

    tracer->init(outfp);

    std::vector<std::string> cflags({
        make_pid_cflag("H2OLOG_H2O_PID", h2o_pid),
    });

    if (!response_header_filters.empty()) {
        cflags.push_back(generate_header_filter_cflag(response_header_filters));
    }

    if (debug >= 2) {
        fprintf(stderr, "event_type_filters=");
        for (size_t i = 0; i < event_type_filters.size(); i++) {
            if (i > 0) {
                fprintf(stderr, ",");
            }
            fprintf(stderr, "%s", event_type_filters[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "cflags=");
        for (size_t i = 0; i < cflags.size(); i++) {
            if (i > 0) {
                fprintf(stderr, " ");
            }
            fprintf(stderr, "%s", cflags[i].c_str());
        }
        fprintf(stderr, "\n");
        fprintf(stderr, "<BPF>\n%s\n</BPF>\n", tracer->bpf_text().c_str());
    }

    ebpf::BPF *bpf = new ebpf::BPF();
    std::vector<ebpf::USDT> probes;
    for (const auto &probe_def : tracer->usdt_probes()) {
        if (event_type_filters.empty() || std::find(event_type_filters.cbegin(), event_type_filters.cend(),
                                                    probe_def.provider + ":" + probe_def.name) != event_type_filters.cend()) {
            probes.push_back(ebpf::USDT(h2o_pid, probe_def.provider, probe_def.name, probe_def.probe_func));
        }
    }

    ebpf::StatusTuple ret = bpf->init(tracer->bpf_text(), cflags, probes);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: init: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    bpf->attach_tracepoint("sched:sched_process_exit", "trace_sched_process_exit");

    for (auto &probe : probes) {
        ret = bpf->attach_usdt(probe);
        if (ret.code() != 0) {
            fprintf(stderr, "Error: attach_usdt: %s\n", ret.msg().c_str());
            return EXIT_FAILURE;
        }
    }

    ret = bpf->open_perf_buffer("events", event_cb, lost_cb, tracer.get(), PERF_BUFFER_PAGE_COUNT);
    if (ret.code() != 0) {
        fprintf(stderr, "Error: open_perf_buffer: %s\n", ret.msg().c_str());
        return EXIT_FAILURE;
    }

    if (debug) {
        show_process(h2o_pid);
    }

    ebpf::BPFPerfBuffer *perf_buffer = bpf->get_perf_buffer("events");
    if (perf_buffer) {
        time_t t0 = time(NULL);

        while (true) {
            perf_buffer->poll(POLL_TIMEOUT);
            tracer->flush();

            if (debug) {
                tracer->show_event_per_sec(&t0);
            }
        }
    }

    fprintf(stderr, "Error: failed to get_perf_buffer()\n");
    return EXIT_FAILURE;
}
<|endoftext|>


<commit_before>/*
 * Copyright 2017, Flávio Keglevich
 *
 * 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.
 */

/**
 * Native implementation of the DngNegative class
 *
 * Created by Flávio Keglevich on 01/11/2018.
 */

#include <jni.h>
#include "dng_sdk/source/dng_host.h"
#include "dng_sdk/source/dng_simple_image.h"
#include "dng_sdk/source/dng_tag_values.h"
#include "dng_sdk/source/dng_matrix.h"
#include "dng_sdk/source/dng_exif.h"
#include "dng_sdk/source/dng_orientation.h"
#include "dng_sdk/source/dng_preview.h"
#include "dng_sdk/source/dng_render.h"
#include "dng_sdk/source/dng_color_space.h"
#include "dng_sdk/source/dng_image_writer.h"
#include "dng_sdk/source/dng_file_stream.h"
#include "dng_sdk/source/dng_tone_curve.h"
#include "dng_sdk/source/dng_camera_profile.h"
#include "dng_sdk/source/dng_point.h"
#include "dng_sdk/source/dng_gain_map.h"
#include "dng_sdk/source/dng_opcodes.h"
#include "dng_sdk/source/dng_memory_stream.h"

dng_matrix_3by3 get3x3Matrix(JNIEnv *env, jfloatArray matrix3x3)
{
    jfloat *raw = env->GetFloatArrayElements(matrix3x3, NULL);
    dng_matrix_3by3 dngMatrix(raw[0], raw[1], raw[2],
                              raw[3], raw[4], raw[5],
                              raw[6], raw[7], raw[8]);
    env->ReleaseFloatArrayElements(matrix3x3, raw, 0);
    return dngMatrix;
}

dng_tone_curve getToneCurve(JNIEnv *env, jfloatArray toneCurve)
{
    dng_tone_curve result; result.SetInvalid();
    int len = env->GetArrayLength(toneCurve);
    if (len % 2 != 0)
    {
        result.SetNull();
        return result;
    }

    jfloat *raw = env->GetFloatArrayElements(toneCurve, NULL);

    for (int i = 0; i < len; i += 2)
    {
        dng_point_real64 point(raw[i + 1], raw[i]);
        result.fCoord.push_back(point);
    }

    env->ReleaseFloatArrayElements(toneCurve, raw, 0);
    return result;
}

extern "C"
{
    dng_host globalHost;
    bool hostWasInitialized = false;

    void initializeHost()
    {
        if (!hostWasInitialized)
        {
            globalHost.SetSaveLinearDNG(false);
            hostWasInitialized = true;
        }
    }

    void setDefaultValues(dng_negative* negative)
    {
        negative->SetColorChannels(3);
        negative->SetColorKeys(colorKeyRed, colorKeyGreen, colorKeyBlue);
        negative->SetDefaultScale(dng_urational(1, 1), dng_urational(1, 1));
        negative->SetNoiseReductionApplied(dng_urational(0, 1));
        negative->SetBaselineExposure(0.0);
        negative->SetBaselineNoise(1.0);
        negative->SetBaselineSharpness(1.0);
        negative->SetAntiAliasStrength(dng_urational(100, 100));
        negative->SetLinearResponseLimit(1.0);
        negative->SetAnalogBalance(dng_vector_3(1.0, 1.0, 1.0));
        negative->SetShadowScale(dng_urational(1, 1));
    }

    JNIEXPORT jlong JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_nativeConstructor(JNIEnv *env, jobject instance)
    {
        initializeHost();
        dng_negative *negative = globalHost.Make_dng_negative();
        setDefaultValues(negative);
        return (jlong) negative;
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_nativeDispose(JNIEnv *env, jobject instance,
                                                                       jlong pointer)
    {
        delete ((dng_negative*) pointer);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setModelNative(JNIEnv *env, jobject instance,
                                                                        jlong pointer, jstring model_)
    {
        const char *model = env->GetStringUTFChars(model_, 0);
        ((dng_negative*) pointer)->SetModelName(model);
        env->ReleaseStringUTFChars(model_, model);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setOriginalRawFileNameNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jstring fileName_)
    {
        const char *fileName = env->GetStringUTFChars(fileName_, 0);
        ((dng_negative*) pointer)->SetOriginalRawFileName(fileName);
        env->ReleaseStringUTFChars(fileName_, fileName);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setSensorInfoNative(JNIEnv *env, jobject instance,
                                                                             jlong pointer,
                                                                             jint whiteLevel,
                                                                             jfloatArray blackLevels_,
                                                                             jint bayerPhase)
    {
        jfloat *blackLevels = env->GetFloatArrayElements(blackLevels_, NULL);
        ((dng_negative*) pointer)->SetWhiteLevel((uint32) whiteLevel);
        ((dng_negative*) pointer)->SetQuadBlacks(blackLevels[0], blackLevels[1], blackLevels[2], blackLevels[3]);
        ((dng_negative*) pointer)->SetBayerMosaic((uint32) bayerPhase);
        env->ReleaseFloatArrayElements(blackLevels_, blackLevels, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setCameraNeutralNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jfloatArray cameraNeutral_)
    {
        jfloat *cameraNeutral = env->GetFloatArrayElements(cameraNeutral_, NULL);
        dng_vector_3 vector3(cameraNeutral[0], cameraNeutral[1], cameraNeutral[2]);
        ((dng_negative*) pointer)->SetCameraNeutral(vector3);
        env->ReleaseFloatArrayElements(cameraNeutral_, cameraNeutral, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setImageSizeAndOrientationNative(JNIEnv *env, jobject instance,
                                                                              jlong pointer,
                                                                              jint width,
                                                                              jint height,
                                                                              jint orientationExifCode)
    {
        dng_rect rect((uint32) height, (uint32) width);
        dng_orientation orientation; orientation.SetTIFF((uint32) orientationExifCode);

        ((dng_negative*) pointer)->SetDefaultCropOrigin(0, 0);
        ((dng_negative*) pointer)->SetDefaultCropSize((uint32) width, (uint32) height);
        ((dng_negative*) pointer)->SetActiveArea(rect);
        ((dng_negative*) pointer)->SetBaseOrientation(orientation);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setCameraCalibrationNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jfloatArray cameraCalibration1_,
                                                                                jfloatArray cameraCalibration2_)
    {
        if (cameraCalibration1_ != NULL)
            ((dng_negative*) pointer)->SetCameraCalibration1(get3x3Matrix(env, cameraCalibration1_));
        if (cameraCalibration2_ != NULL)
            ((dng_negative*) pointer)->SetCameraCalibration2(get3x3Matrix(env, cameraCalibration2_));
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_addColorProfileNative(JNIEnv *env, jobject instance,
                                                                               jlong pointer,
                                                                               jstring name_,
                                                                               jfloatArray colorMatrix1_,
                                                                               jfloatArray colorMatrix2_,
                                                                               jfloatArray forwardMatrix1_,
                                                                               jfloatArray forwardMatrix2_,
                                                                               jint calibrationIlluminant1,
                                                                               jint calibrationIlluminant2,
                                                                               jfloatArray toneCurve_)
    {
        AutoPtr<dng_camera_profile> profile(new dng_camera_profile);

        const char *name = env->GetStringUTFChars(name_, 0);
        profile->SetName(name);
        env->ReleaseStringUTFChars(name_, name);

        if (colorMatrix1_ != NULL)
            profile->SetColorMatrix1(get3x3Matrix(env, colorMatrix1_));

        if (colorMatrix2_ != NULL)
            profile->SetColorMatrix2(get3x3Matrix(env, colorMatrix2_));

        if (forwardMatrix1_ != NULL)
            profile->SetForwardMatrix1(get3x3Matrix(env, forwardMatrix1_));

        if (forwardMatrix2_ != NULL)
            profile->SetForwardMatrix2(get3x3Matrix(env, forwardMatrix2_));

        if (calibrationIlluminant1 != lsUnknown)
            profile->SetCalibrationIlluminant1((uint32) calibrationIlluminant1);

        if (calibrationIlluminant2 != lsUnknown)
            profile->SetCalibrationIlluminant2((uint32) calibrationIlluminant2);

        if (toneCurve_ != NULL)
            profile->SetToneCurve(getToneCurve(env, toneCurve_));

        profile->SetWasReadFromDNG(true);

        ((dng_negative*) pointer)->AddProfile(profile);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setOpcodeListNative(JNIEnv *env, jobject instance,
                                                                              jlong pointer,
                                                                              jbyteArray bytes_,
                                                                              jint listType)
    {
        int numBytes = env->GetArrayLength(bytes_);

        jbyte *bytes = env->GetByteArrayElements(bytes_, NULL);
        dng_memory_stream opcodeListStream(globalHost.Allocator());
        opcodeListStream.Put(bytes, (uint32) numBytes);
        env->ReleaseByteArrayElements(bytes_, bytes, 0);

        switch (listType)
        {
            case 1:
                ((dng_negative*) pointer)->OpcodeList1().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
                break;
            case 2:
                ((dng_negative*) pointer)->OpcodeList2().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
                break;
            default:
                ((dng_negative*) pointer)->OpcodeList3().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
        }
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_setNoiseProfileNative(JNIEnv *env, jobject instance,
                                                                               jlong pointer,
                                                                               jdoubleArray noiseProfile)
    {
        dng_std_vector<dng_noise_function> noiseFunctions;
        int len = env->GetArrayLength(noiseProfile);
        if (len % 2 != 0)
            return;

        jdouble *raw = env->GetDoubleArrayElements(noiseProfile, NULL);

        for (int i = 0; i < len; i += 2)
        {
            dng_noise_function function(raw[i], raw[i + 1]);
            noiseFunctions.push_back(function);
        }

        env->ReleaseDoubleArrayElements(noiseProfile, raw, 0);

        ((dng_negative*) pointer)->SetNoiseProfile(dng_noise_profile(noiseFunctions));
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_exif_DngExifTagWriter_writeMakerNoteTagNative(JNIEnv *env, jobject instance,
                                                                                jlong nativeHandle,
                                                                                jbyteArray makerNote_)
    {
        int numBytes = env->GetArrayLength(makerNote_);
        jbyte *makerNote = env->GetByteArrayElements(makerNote_, NULL);

        AutoPtr<dng_memory_stream> mknStream(new dng_memory_stream(globalHost.Allocator()));
        mknStream->SetBigEndian();                      //The stream should be big endian
        mknStream->Put("Adobe\0MakN", 10);              //These chars mark the makernotes
        mknStream->Put_uint32((uint32) numBytes);       //Write the original length of the makernotes
        mknStream->Put("II", 2);                        //Assuming the makernote is always small endian
        mknStream->Put_uint32(0);                       //Assuming the makernote has no offset
        mknStream->Put(makerNote, (uint32) numBytes);   //Put the actual makernote data

        AutoPtr<dng_memory_block> mknBlock(mknStream->AsMemoryBlock(globalHost.Allocator()));
        ((dng_negative*) nativeHandle)->SetPrivateData(mknBlock);

        env->ReleaseByteArrayElements(makerNote_, makerNote, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_writeImageToFileNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jstring fileName_,
                                                                                jint width,
                                                                                jint height,
                                                                                jbyteArray imageData_,
                                                                                jboolean uncompressed)
    {

        jbyte *imageData = env->GetByteArrayElements(imageData_, NULL);

        dng_rect imageRect((uint32) height, ((uint32) width));
        dng_simple_image* image = new dng_simple_image(imageRect, 1, ttShort, globalHost.Allocator());

        dng_pixel_buffer buffer; image->GetPixelBuffer(buffer);
        memcpy(buffer.fData, imageData, width * height * sizeof(uint16));
        env->ReleaseByteArrayElements(imageData_, imageData, 0);

        AutoPtr<dng_image> castImage(dynamic_cast<dng_image*>(image));
        ((dng_negative*) pointer)->SetStage1Image(castImage);

        ((dng_negative*) pointer)->SynchronizeMetadata();

        const char *fileName = env->GetStringUTFChars(fileName_, 0);
        dng_image_writer writer;
        dng_file_stream stream(fileName, true);
        writer.WriteDNG(globalHost, stream, *((dng_negative*) pointer) , NULL, dngVersion_SaveDefault, uncompressed);
        env->ReleaseStringUTFChars(fileName_, fileName);

    }

    JNIEXPORT jlong JNICALL
    Java_com_fkeglevich_rawdumper_dng_dngsdk_DngNegative_getExifHandleNative(JNIEnv *env, jobject instance,
                                                                             jlong pointer)
    {
        return (jlong ) ((dng_negative*) pointer)->GetExif();
    }
}<commit_msg>Forgot to rename the native methods as well<commit_after>/*
 * Copyright 2017, Flávio Keglevich
 *
 * 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.
 */

/**
 * Native implementation of the DngNegative class
 *
 * Created by Flávio Keglevich on 01/11/2018.
 */

#include <jni.h>
#include "dng_sdk/source/dng_host.h"
#include "dng_sdk/source/dng_simple_image.h"
#include "dng_sdk/source/dng_tag_values.h"
#include "dng_sdk/source/dng_matrix.h"
#include "dng_sdk/source/dng_exif.h"
#include "dng_sdk/source/dng_orientation.h"
#include "dng_sdk/source/dng_preview.h"
#include "dng_sdk/source/dng_render.h"
#include "dng_sdk/source/dng_color_space.h"
#include "dng_sdk/source/dng_image_writer.h"
#include "dng_sdk/source/dng_file_stream.h"
#include "dng_sdk/source/dng_tone_curve.h"
#include "dng_sdk/source/dng_camera_profile.h"
#include "dng_sdk/source/dng_point.h"
#include "dng_sdk/source/dng_gain_map.h"
#include "dng_sdk/source/dng_opcodes.h"
#include "dng_sdk/source/dng_memory_stream.h"

dng_matrix_3by3 get3x3Matrix(JNIEnv *env, jfloatArray matrix3x3)
{
    jfloat *raw = env->GetFloatArrayElements(matrix3x3, NULL);
    dng_matrix_3by3 dngMatrix(raw[0], raw[1], raw[2],
                              raw[3], raw[4], raw[5],
                              raw[6], raw[7], raw[8]);
    env->ReleaseFloatArrayElements(matrix3x3, raw, 0);
    return dngMatrix;
}

dng_tone_curve getToneCurve(JNIEnv *env, jfloatArray toneCurve)
{
    dng_tone_curve result; result.SetInvalid();
    int len = env->GetArrayLength(toneCurve);
    if (len % 2 != 0)
    {
        result.SetNull();
        return result;
    }

    jfloat *raw = env->GetFloatArrayElements(toneCurve, NULL);

    for (int i = 0; i < len; i += 2)
    {
        dng_point_real64 point(raw[i + 1], raw[i]);
        result.fCoord.push_back(point);
    }

    env->ReleaseFloatArrayElements(toneCurve, raw, 0);
    return result;
}

extern "C"
{
    dng_host globalHost;
    bool hostWasInitialized = false;

    void initializeHost()
    {
        if (!hostWasInitialized)
        {
            globalHost.SetSaveLinearDNG(false);
            hostWasInitialized = true;
        }
    }

    void setDefaultValues(dng_negative* negative)
    {
        negative->SetColorChannels(3);
        negative->SetColorKeys(colorKeyRed, colorKeyGreen, colorKeyBlue);
        negative->SetDefaultScale(dng_urational(1, 1), dng_urational(1, 1));
        negative->SetNoiseReductionApplied(dng_urational(0, 1));
        negative->SetBaselineExposure(0.0);
        negative->SetBaselineNoise(1.0);
        negative->SetBaselineSharpness(1.0);
        negative->SetAntiAliasStrength(dng_urational(100, 100));
        negative->SetLinearResponseLimit(1.0);
        negative->SetAnalogBalance(dng_vector_3(1.0, 1.0, 1.0));
        negative->SetShadowScale(dng_urational(1, 1));
    }

    JNIEXPORT jlong JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_nativeConstructor(JNIEnv *env, jobject instance)
    {
        initializeHost();
        dng_negative *negative = globalHost.Make_dng_negative();
        setDefaultValues(negative);
        return (jlong) negative;
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_nativeDispose(JNIEnv *env, jobject instance,
                                                                       jlong pointer)
    {
        delete ((dng_negative*) pointer);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setModelNative(JNIEnv *env, jobject instance,
                                                                        jlong pointer, jstring model_)
    {
        const char *model = env->GetStringUTFChars(model_, 0);
        ((dng_negative*) pointer)->SetModelName(model);
        env->ReleaseStringUTFChars(model_, model);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setOriginalRawFileNameNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jstring fileName_)
    {
        const char *fileName = env->GetStringUTFChars(fileName_, 0);
        ((dng_negative*) pointer)->SetOriginalRawFileName(fileName);
        env->ReleaseStringUTFChars(fileName_, fileName);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setSensorInfoNative(JNIEnv *env, jobject instance,
                                                                             jlong pointer,
                                                                             jint whiteLevel,
                                                                             jfloatArray blackLevels_,
                                                                             jint bayerPhase)
    {
        jfloat *blackLevels = env->GetFloatArrayElements(blackLevels_, NULL);
        ((dng_negative*) pointer)->SetWhiteLevel((uint32) whiteLevel);
        ((dng_negative*) pointer)->SetQuadBlacks(blackLevels[0], blackLevels[1], blackLevels[2], blackLevels[3]);
        ((dng_negative*) pointer)->SetBayerMosaic((uint32) bayerPhase);
        env->ReleaseFloatArrayElements(blackLevels_, blackLevels, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setCameraNeutralNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jfloatArray cameraNeutral_)
    {
        jfloat *cameraNeutral = env->GetFloatArrayElements(cameraNeutral_, NULL);
        dng_vector_3 vector3(cameraNeutral[0], cameraNeutral[1], cameraNeutral[2]);
        ((dng_negative*) pointer)->SetCameraNeutral(vector3);
        env->ReleaseFloatArrayElements(cameraNeutral_, cameraNeutral, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setImageSizeAndOrientationNative(JNIEnv *env, jobject instance,
                                                                              jlong pointer,
                                                                              jint width,
                                                                              jint height,
                                                                              jint orientationExifCode)
    {
        dng_rect rect((uint32) height, (uint32) width);
        dng_orientation orientation; orientation.SetTIFF((uint32) orientationExifCode);

        ((dng_negative*) pointer)->SetDefaultCropOrigin(0, 0);
        ((dng_negative*) pointer)->SetDefaultCropSize((uint32) width, (uint32) height);
        ((dng_negative*) pointer)->SetActiveArea(rect);
        ((dng_negative*) pointer)->SetBaseOrientation(orientation);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setCameraCalibrationNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jfloatArray cameraCalibration1_,
                                                                                jfloatArray cameraCalibration2_)
    {
        if (cameraCalibration1_ != NULL)
            ((dng_negative*) pointer)->SetCameraCalibration1(get3x3Matrix(env, cameraCalibration1_));
        if (cameraCalibration2_ != NULL)
            ((dng_negative*) pointer)->SetCameraCalibration2(get3x3Matrix(env, cameraCalibration2_));
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_addColorProfileNative(JNIEnv *env, jobject instance,
                                                                               jlong pointer,
                                                                               jstring name_,
                                                                               jfloatArray colorMatrix1_,
                                                                               jfloatArray colorMatrix2_,
                                                                               jfloatArray forwardMatrix1_,
                                                                               jfloatArray forwardMatrix2_,
                                                                               jint calibrationIlluminant1,
                                                                               jint calibrationIlluminant2,
                                                                               jfloatArray toneCurve_)
    {
        AutoPtr<dng_camera_profile> profile(new dng_camera_profile);

        const char *name = env->GetStringUTFChars(name_, 0);
        profile->SetName(name);
        env->ReleaseStringUTFChars(name_, name);

        if (colorMatrix1_ != NULL)
            profile->SetColorMatrix1(get3x3Matrix(env, colorMatrix1_));

        if (colorMatrix2_ != NULL)
            profile->SetColorMatrix2(get3x3Matrix(env, colorMatrix2_));

        if (forwardMatrix1_ != NULL)
            profile->SetForwardMatrix1(get3x3Matrix(env, forwardMatrix1_));

        if (forwardMatrix2_ != NULL)
            profile->SetForwardMatrix2(get3x3Matrix(env, forwardMatrix2_));

        if (calibrationIlluminant1 != lsUnknown)
            profile->SetCalibrationIlluminant1((uint32) calibrationIlluminant1);

        if (calibrationIlluminant2 != lsUnknown)
            profile->SetCalibrationIlluminant2((uint32) calibrationIlluminant2);

        if (toneCurve_ != NULL)
            profile->SetToneCurve(getToneCurve(env, toneCurve_));

        profile->SetWasReadFromDNG(true);

        ((dng_negative*) pointer)->AddProfile(profile);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setOpcodeListNative(JNIEnv *env, jobject instance,
                                                                              jlong pointer,
                                                                              jbyteArray bytes_,
                                                                              jint listType)
    {
        int numBytes = env->GetArrayLength(bytes_);

        jbyte *bytes = env->GetByteArrayElements(bytes_, NULL);
        dng_memory_stream opcodeListStream(globalHost.Allocator());
        opcodeListStream.Put(bytes, (uint32) numBytes);
        env->ReleaseByteArrayElements(bytes_, bytes, 0);

        switch (listType)
        {
            case 1:
                ((dng_negative*) pointer)->OpcodeList1().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
                break;
            case 2:
                ((dng_negative*) pointer)->OpcodeList2().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
                break;
            default:
                ((dng_negative*) pointer)->OpcodeList3().Parse(globalHost, opcodeListStream, (uint32) numBytes, 0);
        }
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_setNoiseProfileNative(JNIEnv *env, jobject instance,
                                                                               jlong pointer,
                                                                               jdoubleArray noiseProfile)
    {
        dng_std_vector<dng_noise_function> noiseFunctions;
        int len = env->GetArrayLength(noiseProfile);
        if (len % 2 != 0)
            return;

        jdouble *raw = env->GetDoubleArrayElements(noiseProfile, NULL);

        for (int i = 0; i < len; i += 2)
        {
            dng_noise_function function(raw[i], raw[i + 1]);
            noiseFunctions.push_back(function);
        }

        env->ReleaseDoubleArrayElements(noiseProfile, raw, 0);

        ((dng_negative*) pointer)->SetNoiseProfile(dng_noise_profile(noiseFunctions));
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_exif_DngExifTagWriter_writeMakerNoteTagNative(JNIEnv *env, jobject instance,
                                                                                jlong nativeHandle,
                                                                                jbyteArray makerNote_)
    {
        int numBytes = env->GetArrayLength(makerNote_);
        jbyte *makerNote = env->GetByteArrayElements(makerNote_, NULL);

        AutoPtr<dng_memory_stream> mknStream(new dng_memory_stream(globalHost.Allocator()));
        mknStream->SetBigEndian();                      //The stream should be big endian
        mknStream->Put("Adobe\0MakN", 10);              //These chars mark the makernotes
        mknStream->Put_uint32((uint32) numBytes);       //Write the original length of the makernotes
        mknStream->Put("II", 2);                        //Assuming the makernote is always small endian
        mknStream->Put_uint32(0);                       //Assuming the makernote has no offset
        mknStream->Put(makerNote, (uint32) numBytes);   //Put the actual makernote data

        AutoPtr<dng_memory_block> mknBlock(mknStream->AsMemoryBlock(globalHost.Allocator()));
        ((dng_negative*) nativeHandle)->SetPrivateData(mknBlock);

        env->ReleaseByteArrayElements(makerNote_, makerNote, 0);
    }

    JNIEXPORT void JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_writeImageToFileNative(JNIEnv *env, jobject instance,
                                                                                jlong pointer,
                                                                                jstring fileName_,
                                                                                jint width,
                                                                                jint height,
                                                                                jbyteArray imageData_,
                                                                                jboolean uncompressed)
    {

        jbyte *imageData = env->GetByteArrayElements(imageData_, NULL);

        dng_rect imageRect((uint32) height, ((uint32) width));
        dng_simple_image* image = new dng_simple_image(imageRect, 1, ttShort, globalHost.Allocator());

        dng_pixel_buffer buffer; image->GetPixelBuffer(buffer);
        memcpy(buffer.fData, imageData, width * height * sizeof(uint16));
        env->ReleaseByteArrayElements(imageData_, imageData, 0);

        AutoPtr<dng_image> castImage(dynamic_cast<dng_image*>(image));
        ((dng_negative*) pointer)->SetStage1Image(castImage);

        ((dng_negative*) pointer)->SynchronizeMetadata();

        const char *fileName = env->GetStringUTFChars(fileName_, 0);
        dng_image_writer writer;
        dng_file_stream stream(fileName, true);
        writer.WriteDNG(globalHost, stream, *((dng_negative*) pointer) , NULL, dngVersion_SaveDefault, uncompressed);
        env->ReleaseStringUTFChars(fileName_, fileName);

    }

    JNIEXPORT jlong JNICALL
    Java_com_fkeglevich_rawdumper_dng_writer_DngNegative_getExifHandleNative(JNIEnv *env, jobject instance,
                                                                             jlong pointer)
    {
        return (jlong ) ((dng_negative*) pointer)->GetExif();
    }
}<|endoftext|>


<commit_before>#pragma once

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define error(x...) do \
{ \
	fprintf(stderr, "Error: "); \
	fprintf(stderr, x); \
} while(0)

#define warning(x...) do \
{ \
	fprintf(stderr, "Warning: "); \
	fprintf(stderr, x); \
} while(0)

#define panic(x...) do \
{ \
	error(x); \
	exit(1); \
} while(0)

#define panic_if(cond, x...) \
		do { if ((cond)) panic(x); } while(0)

static inline char *xstrdup(const char *s)
{
	char *out = strdup(s);

	panic_if(!out, "strdup failed");

	return out;
}

static inline void *xmalloc(size_t sz)
{
  void *out = malloc(sz);

  panic_if(!out, "malloc failed");
  memset(out, 0, sz);

  return out;
}
<commit_msg>utils: newline on error/warning<commit_after>#pragma once

#include <stdlib.h>
#include <string.h>
#include <stdio.h>

#define error(x...) do \
{ \
	fprintf(stderr, "Error: "); \
	fprintf(stderr, x); \
	fprintf(stderr, "\n"); \
} while(0)

#define warning(x...) do \
{ \
	fprintf(stderr, "Warning: "); \
	fprintf(stderr, x); \
	fprintf(stderr, "\n"); \
} while(0)

#define panic(x...) do \
{ \
	error(x); \
	exit(1); \
} while(0)

#define panic_if(cond, x...) \
		do { if ((cond)) panic(x); } while(0)

static inline char *xstrdup(const char *s)
{
	char *out = strdup(s);

	panic_if(!out, "strdup failed");

	return out;
}

static inline void *xmalloc(size_t sz)
{
  void *out = malloc(sz);

  panic_if(!out, "malloc failed");
  memset(out, 0, sz);

  return out;
}
<|endoftext|>


<commit_before>/*
 * This istream filter passes one byte at a time.  This is useful for
 * testing and debugging istream handler implementations.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_byte.hxx"
#include "istream_internal.hxx"
#include "istream_forward.hxx"
#include "util/Cast.hxx"
#include "pool.hxx"

#include <assert.h>

struct istream_byte {
    struct istream output;
    struct istream *input;

    istream_byte(struct pool &p, const struct istream_class &cls)
        :output(p, cls) {}
};

/*
 * istream handler
 *
 */

static size_t
byte_input_data(const void *data, gcc_unused size_t length, void *ctx)
{
    auto *byte = (struct istream_byte *)ctx;

    return istream_invoke_data(&byte->output, data, 1);
}

static ssize_t
byte_input_direct(enum istream_direct type, int fd,
                  gcc_unused size_t max_length, void *ctx)
{
    auto *byte = (struct istream_byte *)ctx;

    return istream_invoke_direct(&byte->output, type, fd, 1);
}

static const struct istream_handler byte_input_handler = {
    .data = byte_input_data,
    .direct = byte_input_direct,
    .eof = istream_forward_eof,
    .abort = istream_forward_abort,
};

/*
 * istream implementation
 *
 */

static inline struct istream_byte *
istream_to_byte(struct istream *istream)
{
    return &ContainerCast2(*istream, &istream_byte::output);
}

static void
istream_byte_read(struct istream *istream)
{
    struct istream_byte *byte = istream_to_byte(istream);

    istream_handler_set_direct(byte->input, byte->output.handler_direct);

    istream_read(byte->input);
}

static void
istream_byte_close(struct istream *istream)
{
    struct istream_byte *byte = istream_to_byte(istream);

    assert(byte->input != nullptr);

    istream_close_handler(byte->input);
    istream_deinit(&byte->output);
}

static const struct istream_class istream_byte = {
    .read = istream_byte_read,
    .close = istream_byte_close,
};

/*
 * constructor
 *
 */

struct istream *
istream_byte_new(struct pool *pool, struct istream *input)
{
    auto *byte = NewFromPool<struct istream_byte>(*pool, *pool, istream_byte);

    assert(input != nullptr);
    assert(!istream_has_handler(input));

    istream_assign_handler(&byte->input, input,
                           &byte_input_handler, byte,
                           0);

    return &byte->output;
}
<commit_msg>istream_byte: rename the struct using CamelCase<commit_after>/*
 * This istream filter passes one byte at a time.  This is useful for
 * testing and debugging istream handler implementations.
 *
 * author: Max Kellermann <mk@cm4all.com>
 */

#include "istream_byte.hxx"
#include "istream_internal.hxx"
#include "istream_forward.hxx"
#include "util/Cast.hxx"
#include "pool.hxx"

#include <assert.h>

struct ByteIstream {
    struct istream output;
    struct istream *input;

    ByteIstream(struct pool &p, const struct istream_class &cls)
        :output(p, cls) {}
};

/*
 * istream handler
 *
 */

static size_t
byte_input_data(const void *data, gcc_unused size_t length, void *ctx)
{
    auto *byte = (ByteIstream *)ctx;

    return istream_invoke_data(&byte->output, data, 1);
}

static ssize_t
byte_input_direct(enum istream_direct type, int fd,
                  gcc_unused size_t max_length, void *ctx)
{
    auto *byte = (ByteIstream *)ctx;

    return istream_invoke_direct(&byte->output, type, fd, 1);
}

static const struct istream_handler byte_input_handler = {
    .data = byte_input_data,
    .direct = byte_input_direct,
    .eof = istream_forward_eof,
    .abort = istream_forward_abort,
};

/*
 * istream implementation
 *
 */

static inline ByteIstream *
istream_to_byte(struct istream *istream)
{
    return &ContainerCast2(*istream, &ByteIstream::output);
}

static void
istream_byte_read(struct istream *istream)
{
    ByteIstream *byte = istream_to_byte(istream);

    istream_handler_set_direct(byte->input, byte->output.handler_direct);

    istream_read(byte->input);
}

static void
istream_byte_close(struct istream *istream)
{
    ByteIstream *byte = istream_to_byte(istream);

    assert(byte->input != nullptr);

    istream_close_handler(byte->input);
    istream_deinit(&byte->output);
}

static const struct istream_class istream_byte = {
    .read = istream_byte_read,
    .close = istream_byte_close,
};

/*
 * constructor
 *
 */

struct istream *
istream_byte_new(struct pool *pool, struct istream *input)
{
    auto *byte = NewFromPool<ByteIstream>(*pool, *pool, istream_byte);

    assert(input != nullptr);
    assert(!istream_has_handler(input));

    istream_assign_handler(&byte->input, input,
                           &byte_input_handler, byte,
                           0);

    return &byte->output;
}
<|endoftext|>


<commit_before>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2013-2014 Vaclav Slavik
 *
 *  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 "languagectrl.h"

#include <wx/config.h>
#include <wx/sizer.h>
#include <wx/stattext.h>

IMPLEMENT_DYNAMIC_CLASS(LanguageCtrl, wxComboBox)

LanguageCtrl::LanguageCtrl(wxWindow *parent, wxWindowID winid, Language lang)
    : wxComboBox(parent, winid)
{
    Init(lang);
}

void LanguageCtrl::Init(Language lang)
{
    SetHint(_("Language Code or Name (e.g. en_GB)"));

    // wxGTK must have the value set before autocompletion to avoid annoying
    // popups in some (hard to determine) cases.
#ifdef __WXGTK__
    if (lang.IsValid())
        SetValue(lang.FormatForRoundtrip());
#endif

#ifdef __WXOSX__
    for (auto x: Language::AllFormattedNames())
        Append(x);
    NSComboBox *cb = (NSComboBox*) GetHandle();
    [cb setCompletes:YES];
#else
    static wxArrayString choices;
    if (choices.empty())
    {
        for (auto x: Language::AllFormattedNames())
            choices.push_back(x);
    }
    Set(choices);
    AutoComplete(choices);
#endif

    m_inited = true;

    // ...but wxMSW requires the opposite, otherwise the text wouldn't appear.
#ifndef __WXGTK__
    if (lang.IsValid())
        SetValue(lang.FormatForRoundtrip());
#endif
}

void LanguageCtrl::SetLang(const Language& lang)
{
    if (!m_inited)
        Init(lang);
    else
        SetValue(lang.FormatForRoundtrip());
}

Language LanguageCtrl::GetLang() const
{
    return Language::TryParse(GetValue());
}

#ifdef __WXMSW__
wxSize LanguageCtrl::DoGetBestSize() const
{
    // wxComboBox's implementation is insanely slow, at least on MSW.
    // Hardcode a value instead, it doesn't matter for Poedit's use anyway,
    // this control's best size is not the determining factor.
    return GetSizeFromTextSize(100);
}
#endif

LanguageDialog::LanguageDialog(wxWindow *parent)
    : wxDialog(parent, wxID_ANY, _("Translation Language")),
      m_validatedLang(-1)
{
    wxString langcode = wxConfigBase::Get()->Read("/last_translation_lang", "");
    Language lang;
    if (!langcode.empty())
        lang = Language::TryParse(langcode);

    auto sizer = new wxBoxSizer(wxVERTICAL);

    auto label = new wxStaticText(this, wxID_ANY, _("Language of the translation:"));
    m_language = new LanguageCtrl(this, wxID_ANY, lang);
    m_language->SetMinSize(wxSize(300,-1));
    auto buttons = CreateButtonSizer(wxOK | wxCANCEL);

#ifdef __WXOSX__
    sizer->AddSpacer(10);
    sizer->Add(label, wxSizerFlags().Border());
    sizer->Add(m_language, wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(buttons, wxSizerFlags().Expand());
#else
    sizer->AddSpacer(10);
    sizer->Add(label, wxSizerFlags().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(m_language, wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(buttons, wxSizerFlags().Expand().Border());
#endif

    m_language->Bind(wxEVT_TEXT,     [=](wxCommandEvent& e){ m_validatedLang = -1; e.Skip(); });
    m_language->Bind(wxEVT_COMBOBOX, [=](wxCommandEvent& e){ m_validatedLang = -1; e.Skip(); });

    Bind(wxEVT_UPDATE_UI,
        [=](wxUpdateUIEvent& e){ e.Enable(Validate()); },
        wxID_OK);

    SetSizerAndFit(sizer);
    CenterOnParent();

    m_language->SetFocus();
#ifdef __WXOSX__
    // Workaround wx bug: http://trac.wxwidgets.org/ticket/9521
    m_language->SelectAll();
#endif
}

bool LanguageDialog::Validate()
{
    if (m_validatedLang == -1)
    {
        m_validatedLang = m_language->IsValid() ? 1 : 0;
    }

    return m_validatedLang == 1;
}

void LanguageDialog::EndModal(int retval)
{
    if (retval == wxID_OK)
    {
        wxConfigBase::Get()->Write("/last_translation_lang", GetLang().Code().c_str());
    }
    wxDialog::EndModal(retval);
}

void LanguageDialog::SetLang(const Language& lang)
{
    m_validatedLang = -1;
    m_language->SetLang(lang);
}
<commit_msg>OS X: Complete languages case-insensitively.<commit_after>/*
 *  This file is part of Poedit (http://poedit.net)
 *
 *  Copyright (C) 2013-2014 Vaclav Slavik
 *
 *  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 "languagectrl.h"

#include <wx/config.h>
#include <wx/sizer.h>
#include <wx/stattext.h>

#ifdef __WXOSX__
extern "C" {
#import "NSObject+REResponder.h"
}
#endif

IMPLEMENT_DYNAMIC_CLASS(LanguageCtrl, wxComboBox)

LanguageCtrl::LanguageCtrl(wxWindow *parent, wxWindowID winid, Language lang)
    : wxComboBox(parent, winid)
{
    Init(lang);
}

void LanguageCtrl::Init(Language lang)
{
    SetHint(_("Language Code or Name (e.g. en_GB)"));

    // wxGTK must have the value set before autocompletion to avoid annoying
    // popups in some (hard to determine) cases.
#ifdef __WXGTK__
    if (lang.IsValid())
        SetValue(lang.FormatForRoundtrip());
#endif

#ifdef __WXOSX__
    for (auto x: Language::AllFormattedNames())
        Append(x);
    NSComboBox *cb = (NSComboBox*) GetHandle();
    [cb setCompletes:YES];
    // default completion is case-sensitive, we'd rather be case-insensitive, so plug in
    // customized completedString: implementation
    RESetBlock(cb.cell, @selector(completedString:), NO, nil, ^(NSComboBox *receiver, NSString *string) {
        for (NSString *item in receiver.objectValues) {
            if ([item compare:string
                      options:NSCaseInsensitiveSearch|NSDiacriticInsensitiveSearch
                        range:NSMakeRange(0, std::min([item length], [string length]))
                       locale:[NSLocale currentLocale]] == NSOrderedSame)
                return item;
        }
        return (NSString*)nil;
    });
#else
    static wxArrayString choices;
    if (choices.empty())
    {
        for (auto x: Language::AllFormattedNames())
            choices.push_back(x);
    }
    Set(choices);
    AutoComplete(choices);
#endif

    m_inited = true;

    // ...but wxMSW requires the opposite, otherwise the text wouldn't appear.
#ifndef __WXGTK__
    if (lang.IsValid())
        SetValue(lang.FormatForRoundtrip());
#endif
}

void LanguageCtrl::SetLang(const Language& lang)
{
    if (!m_inited)
        Init(lang);
    else
        SetValue(lang.FormatForRoundtrip());
}

Language LanguageCtrl::GetLang() const
{
    return Language::TryParse(GetValue());
}

#ifdef __WXMSW__
wxSize LanguageCtrl::DoGetBestSize() const
{
    // wxComboBox's implementation is insanely slow, at least on MSW.
    // Hardcode a value instead, it doesn't matter for Poedit's use anyway,
    // this control's best size is not the determining factor.
    return GetSizeFromTextSize(100);
}
#endif

LanguageDialog::LanguageDialog(wxWindow *parent)
    : wxDialog(parent, wxID_ANY, _("Translation Language")),
      m_validatedLang(-1)
{
    wxString langcode = wxConfigBase::Get()->Read("/last_translation_lang", "");
    Language lang;
    if (!langcode.empty())
        lang = Language::TryParse(langcode);

    auto sizer = new wxBoxSizer(wxVERTICAL);

    auto label = new wxStaticText(this, wxID_ANY, _("Language of the translation:"));
    m_language = new LanguageCtrl(this, wxID_ANY, lang);
    m_language->SetMinSize(wxSize(300,-1));
    auto buttons = CreateButtonSizer(wxOK | wxCANCEL);

#ifdef __WXOSX__
    sizer->AddSpacer(10);
    sizer->Add(label, wxSizerFlags().Border());
    sizer->Add(m_language, wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(buttons, wxSizerFlags().Expand());
#else
    sizer->AddSpacer(10);
    sizer->Add(label, wxSizerFlags().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(m_language, wxSizerFlags().Expand().DoubleBorder(wxLEFT|wxRIGHT));
    sizer->Add(buttons, wxSizerFlags().Expand().Border());
#endif

    m_language->Bind(wxEVT_TEXT,     [=](wxCommandEvent& e){ m_validatedLang = -1; e.Skip(); });
    m_language->Bind(wxEVT_COMBOBOX, [=](wxCommandEvent& e){ m_validatedLang = -1; e.Skip(); });

    Bind(wxEVT_UPDATE_UI,
        [=](wxUpdateUIEvent& e){ e.Enable(Validate()); },
        wxID_OK);

    SetSizerAndFit(sizer);
    CenterOnParent();

    m_language->SetFocus();
#ifdef __WXOSX__
    // Workaround wx bug: http://trac.wxwidgets.org/ticket/9521
    m_language->SelectAll();
#endif
}

bool LanguageDialog::Validate()
{
    if (m_validatedLang == -1)
    {
        m_validatedLang = m_language->IsValid() ? 1 : 0;
    }

    return m_validatedLang == 1;
}

void LanguageDialog::EndModal(int retval)
{
    if (retval == wxID_OK)
    {
        wxConfigBase::Get()->Write("/last_translation_lang", GetLang().Code().c_str());
    }
    wxDialog::EndModal(retval);
}

void LanguageDialog::SetLang(const Language& lang)
{
    m_validatedLang = -1;
    m_language->SetLang(lang);
}
<|endoftext|>