<commit_before>#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iterator>

#include <dariadb.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <limits>
#include <random>
#include <thread>
#include <utils/fs.h>

class BenchCallback : public dariadb::storage::ReaderClb {
public:
  BenchCallback() = default;
  void call(const dariadb::Meas &m) {
    if (m.flag != dariadb::Flags::_NO_DATA) {
      count++;
    } else {
      count_ig++;
    }
  }
  size_t count;
  size_t count_ig;
};

void writer_1(dariadb::storage::MeasStorage_ptr ms) {
  auto m = dariadb::Meas::empty();
  dariadb::Time t = dariadb::timeutil::current_time();
  for (dariadb::Id i = 0; i < 32768; i += 1) {
    m.id = i;
    m.flag = dariadb::Flag(0);
    m.time = t;
    m.value = dariadb::Value(i);
    ms->append(m);
    t++;
  }
}

std::atomic_long writen{0};
dariadb::IdSet _all_id;
std::mutex _id_lock;
void writer_2(dariadb::Id id_from, size_t id_per_thread,
              dariadb::storage::MeasStorage_ptr ms) {
  auto m = dariadb::Meas::empty();
  std::random_device r;
  std::default_random_engine e1(r());
  std::uniform_int_distribution<int> uniform_dist(10, 64);

  size_t max_id = (id_from + id_per_thread);

  for (dariadb::Id i = id_from; i < max_id; i += 1) {
    _id_lock.lock();
    _all_id.insert(i);
    _id_lock.unlock();
    dariadb::Value v = 1.0;
    m.id = i;
    m.flag = dariadb::Flag(0);
    auto max_rnd = uniform_dist(e1);
    m.time = 0; // dariadb::timeutil::current_time();
    for (dariadb::Time p = 0; p < dariadb::Time(max_rnd); p++) {
      m.time += 10;
      m.value = v;
      ms->append(m);
      writen++;
      auto rnd = rand() / double(RAND_MAX);

      v += rnd;
    }
  }
}

int main(int argc, char *argv[]) {
  (void)argc;
  (void)argv;
  srand(static_cast<unsigned int>(time(NULL)));

  const std::string storage_path = "testStorage";
  const size_t chunk_per_storage = 1024 * 1024;
  const size_t chunk_size = 256;
  const size_t cap_B = 10;
  const size_t max_mem_chunks = 0;

  { // 1.
    if (dariadb::utils::fs::path_exists(storage_path)) {
      dariadb::utils::fs::rm(storage_path);
    }

    auto raw_ptr = new dariadb::storage::Engine(
        dariadb::storage::PageManager::Params(storage_path, chunk_per_storage,
                                              chunk_size),
        dariadb::storage::Capacitor::Params(cap_B, storage_path),
        dariadb::storage::Engine::Limits(max_mem_chunks));
    dariadb::storage::MeasStorage_ptr ms{raw_ptr};
    auto start = clock();

    writer_1(ms);

    auto elapsed = ((float)clock() - start) / CLOCKS_PER_SEC;
    std::cout << "1. insert : " << elapsed << std::endl;
  }

  if (dariadb::utils::fs::path_exists(storage_path)) {
    dariadb::utils::fs::rm(storage_path);
  }

  auto raw_ptr_ds = new dariadb::storage::Engine(
      dariadb::storage::PageManager::Params(storage_path, chunk_per_storage, chunk_size),
      dariadb::storage::Capacitor::Params(cap_B, storage_path),
      dariadb::storage::Engine::Limits(max_mem_chunks));
  dariadb::storage::MeasStorage_ptr ms{raw_ptr_ds};

  { // 2.
    const size_t threads_count = 1;
    const size_t id_per_thread = size_t(32768 / threads_count);

    auto start = clock();
    std::vector<std::thread> writers(threads_count);
    size_t pos = 0;
    for (size_t i = 0; i < threads_count; i++) {
      std::thread t{writer_2, id_per_thread * i + 1, id_per_thread, ms};
      writers[pos++] = std::move(t);
    }

    pos = 0;
    for (size_t i = 0; i < threads_count; i++) {
      std::thread t = std::move(writers[pos++]);
      t.join();
    }

    auto elapsed = ((float)clock() - start) / CLOCKS_PER_SEC;
    std::cout << "2. insert : " << elapsed << std::endl;
    raw_ptr_ds->flush();
  }
  auto queue_sizes = raw_ptr_ds->queue_size();
  std::cout << "\r"
            << " in queue: (p:" << queue_sizes.page << " cap:" << queue_sizes.cap << ")"
            << std::endl;
  //{
  //   /* auto ids=ms->getIds();
  //    std::cout << "ids.size:"<<ids.size() << std::endl;*/
  //    std::cout << "read all..." << std::endl;
  //    std::shared_ptr<BenchCallback> clbk{ new BenchCallback() };
  //    auto start = clock();
  // dariadb::storage::QueryInterval qi(
  //  dariadb::IdArray{ _all_id.begin(),_all_id.end() },
  //  0, ms->minTime(), ms->maxTime());
  //    ms->readInterval(qi)->readAll(clbk.get());

  //    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC);
  //    std::cout << "readed: " << clbk->count << std::endl;
  //    std::cout << "time: " << elapsed << std::endl;
  //}
  { // 3

    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Id> uniform_dist(1, 32767);
    dariadb::IdArray ids;
    ids.resize(1);

    const size_t queries_count = 32768;

    dariadb::IdArray rnd_ids(queries_count);
    std::vector<dariadb::Time> rnd_time(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_ids[i] = uniform_dist(e1);
      dariadb::Time minT, maxT;
      bool exists = raw_ptr_ds->minMaxTime(rnd_ids[i], &minT, &maxT);
      if (!exists) {
        throw MAKE_EXCEPTION("!exists");
      }
      std::uniform_int_distribution<dariadb::Time> uniform_dist_tmp(minT, maxT);
      rnd_time[i] = uniform_dist_tmp(e1);
    }
    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      ids[0] = rnd_ids[i];
      auto t = rnd_time[i];
      auto rdr = ms->readInTimePoint(dariadb::storage::QueryTimePoint(ids, 0, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "3. time point: " << elapsed << " readed: " << raw_ptr->count
              << " ignored: " << raw_ptr->count_ig << std::endl;
  }
  { // 4
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Id> uniform_dist(dariadb::Time(10),
                                                            dariadb::Time(32));

    const size_t queries_count = 1; // 32;

    dariadb::IdArray ids(_all_id.begin(), _all_id.end());
    std::vector<dariadb::Time> rnd_time(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time[i] = uniform_dist(e1);
    }
    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      auto t = rnd_time[i];
      auto rdr = ms->readInTimePoint(dariadb::storage::QueryTimePoint(ids, 0, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "4. time point: " << elapsed << " readed: " << raw_ptr->count
              << std::endl;
  }

  { // 5
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Time> uniform_dist(dariadb::Time(10),
                                                              dariadb::Time(32));

    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};
    const size_t queries_count = 1;

    std::vector<dariadb::Time> rnd_time_from(queries_count), rnd_time_to(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time_from[i] = uniform_dist(e1);
      rnd_time_to[i] = uniform_dist(e1);
    }

    dariadb::IdArray all_ids(_all_id.begin(), _all_id.end());

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      auto f = std::min(rnd_time_from[i], rnd_time_to[i]);
      auto t = std::max(rnd_time_from[i], rnd_time_to[i]);
      auto rdr = ms->readInterval(dariadb::storage::QueryInterval(all_ids, 0, f, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "5. interval: " << elapsed << " readed: " << raw_ptr->count << std::endl;
  }

  { // 6
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Time> uniform_dist(
        raw_ptr_ds->minTime(), dariadb::timeutil::current_time());
    std::uniform_int_distribution<dariadb::Id> uniform_dist_id(1, 32767);

    const size_t ids_count = size_t(32768 * 0.1);
    dariadb::IdArray ids;
    ids.resize(ids_count);

    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};
    const size_t queries_count = 2; // 32;

    std::vector<dariadb::Time> rnd_time_from(queries_count), rnd_time_to(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time_from[i] = uniform_dist(e1);
      rnd_time_to[i] = uniform_dist(e1);
    }

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      for (size_t j = 0; j < ids_count; j++) {
        ids[j] = uniform_dist_id(e1);
      }
      auto f = std::min(rnd_time_from[i], rnd_time_to[i]);
      auto t = std::max(rnd_time_from[i], rnd_time_to[i]);
      auto rdr = ms->readInterval(dariadb::storage::QueryInterval(ids, 0, f, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "6. interval: " << elapsed << " readed: " << raw_ptr->count << std::endl;
  }

  ms = nullptr;

  if (dariadb::utils::fs::path_exists(storage_path)) {
    dariadb::utils::fs::rm(storage_path);
  }
}
<commit_msg>hard_benchmark: read params.<commit_after>#include <cstdlib>
#include <ctime>
#include <iostream>
#include <iterator>

#include <dariadb.h>
#include <algorithm>
#include <atomic>
#include <cassert>
#include <chrono>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <limits>
#include <random>
#include <thread>
#include <utils/fs.h>

class BenchCallback : public dariadb::storage::ReaderClb {
public:
  BenchCallback() = default;
  void call(const dariadb::Meas &m) {
    if (m.flag != dariadb::Flags::_NO_DATA) {
      count++;
    } else {
      count_ig++;
    }
  }
  size_t count;
  size_t count_ig;
};

void writer_1(dariadb::storage::MeasStorage_ptr ms) {
  auto m = dariadb::Meas::empty();
  dariadb::Time t = dariadb::timeutil::current_time();
  for (dariadb::Id i = 0; i < 32768; i += 1) {
    m.id = i;
    m.flag = dariadb::Flag(0);
    m.time = t;
    m.value = dariadb::Value(i);
    ms->append(m);
    t++;
  }
}

std::atomic_long writen{0};
dariadb::IdSet _all_id;
std::mutex _id_lock;
void writer_2(dariadb::Id id_from, size_t id_per_thread,
              dariadb::storage::MeasStorage_ptr ms) {
  auto m = dariadb::Meas::empty();
  std::random_device r;
  std::default_random_engine e1(r());
  std::uniform_int_distribution<int> uniform_dist(10, 64);

  size_t max_id = (id_from + id_per_thread);

  for (dariadb::Id i = id_from; i < max_id; i += 1) {
    _id_lock.lock();
    _all_id.insert(i);
    _id_lock.unlock();
    dariadb::Value v = 1.0;
    m.id = i;
    m.flag = dariadb::Flag(0);
    auto max_rnd = uniform_dist(e1);
    m.time = 0; // dariadb::timeutil::current_time();
    for (dariadb::Time p = 0; p < dariadb::Time(max_rnd); p++) {
      m.time += 10;
      m.value = v;
      ms->append(m);
      writen++;
      auto rnd = rand() / double(RAND_MAX);

      v += rnd;
    }
  }
}

int main(int argc, char *argv[]) {
  (void)argc;
  (void)argv;
  srand(static_cast<unsigned int>(time(NULL)));

  const std::string storage_path = "testStorage";
  const size_t chunk_per_storage = 1024 * 1024;
  const size_t chunk_size = 256;
  const size_t cap_B = 10;
  const size_t max_mem_chunks = 0;

  { // 1.
    if (dariadb::utils::fs::path_exists(storage_path)) {
      dariadb::utils::fs::rm(storage_path);
    }

    auto raw_ptr = new dariadb::storage::Engine(
        dariadb::storage::PageManager::Params(storage_path, chunk_per_storage,
                                              chunk_size),
        dariadb::storage::Capacitor::Params(cap_B, storage_path),
        dariadb::storage::Engine::Limits(max_mem_chunks));
    dariadb::storage::MeasStorage_ptr ms{raw_ptr};
    auto start = clock();

    writer_1(ms);

    auto elapsed = ((float)clock() - start) / CLOCKS_PER_SEC;
    std::cout << "1. insert : " << elapsed << std::endl;
  }

  if (dariadb::utils::fs::path_exists(storage_path)) {
    dariadb::utils::fs::rm(storage_path);
  }

  auto raw_ptr_ds = new dariadb::storage::Engine(
      dariadb::storage::PageManager::Params(storage_path, chunk_per_storage, chunk_size),
      dariadb::storage::Capacitor::Params(cap_B, storage_path),
      dariadb::storage::Engine::Limits(max_mem_chunks));
  dariadb::storage::MeasStorage_ptr ms{raw_ptr_ds};

  { // 2.
    const size_t threads_count = 1;
    const size_t id_per_thread = size_t(32768 / threads_count);

    auto start = clock();
    std::vector<std::thread> writers(threads_count);
    size_t pos = 0;
    for (size_t i = 0; i < threads_count; i++) {
      std::thread t{writer_2, id_per_thread * i + 1, id_per_thread, ms};
      writers[pos++] = std::move(t);
    }

    pos = 0;
    for (size_t i = 0; i < threads_count; i++) {
      std::thread t = std::move(writers[pos++]);
      t.join();
    }

    auto elapsed = ((float)clock() - start) / CLOCKS_PER_SEC;
    std::cout << "2. insert : " << elapsed << std::endl;
    raw_ptr_ds->flush();
  }
  auto queue_sizes = raw_ptr_ds->queue_size();
  std::cout << "\r"
            << " in queue: (p:" << queue_sizes.page << " cap:" << queue_sizes.cap << ")"
            << std::endl;
  //{
  //   /* auto ids=ms->getIds();
  //    std::cout << "ids.size:"<<ids.size() << std::endl;*/
  //    std::cout << "read all..." << std::endl;
  //    std::shared_ptr<BenchCallback> clbk{ new BenchCallback() };
  //    auto start = clock();
  // dariadb::storage::QueryInterval qi(
  //  dariadb::IdArray{ _all_id.begin(),_all_id.end() },
  //  0, ms->minTime(), ms->maxTime());
  //    ms->readInterval(qi)->readAll(clbk.get());

  //    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC);
  //    std::cout << "readed: " << clbk->count << std::endl;
  //    std::cout << "time: " << elapsed << std::endl;
  //}
  { // 3

    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Id> uniform_dist(1, 32767);
    dariadb::IdArray ids;
    ids.resize(1);

    const size_t queries_count = 32768;

    dariadb::IdArray rnd_ids(queries_count);
    std::vector<dariadb::Time> rnd_time(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_ids[i] = uniform_dist(e1);
      /*dariadb::Time minT, maxT;
      bool exists = raw_ptr_ds->minMaxTime(rnd_ids[i], &minT, &maxT);
      if (!exists) {
        throw MAKE_EXCEPTION("!exists");
      }*/
      std::uniform_int_distribution<dariadb::Time> uniform_dist_tmp(10, 32);
      rnd_time[i] = uniform_dist_tmp(e1);
    }
    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      ids[0] = rnd_ids[i];
      auto t = rnd_time[i];
      auto rdr = ms->readInTimePoint(dariadb::storage::QueryTimePoint(ids, 0, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "3. time point: " << elapsed << " readed: " << raw_ptr->count
              << " ignored: " << raw_ptr->count_ig << std::endl;
  }
  { // 4
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Id> uniform_dist(dariadb::Time(10),
                                                            dariadb::Time(32));

    const size_t queries_count = 1; // 32;

    dariadb::IdArray ids(_all_id.begin(), _all_id.end());
    std::vector<dariadb::Time> rnd_time(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time[i] = uniform_dist(e1);
    }
    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      auto t = rnd_time[i];
      auto rdr = ms->readInTimePoint(dariadb::storage::QueryTimePoint(ids, 0, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "4. time point: " << elapsed << " readed: " << raw_ptr->count
              << std::endl;
  }

  { // 5
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Time> uniform_dist(dariadb::Time(10),
                                                              dariadb::Time(32));

    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};
    const size_t queries_count = 1;

    std::vector<dariadb::Time> rnd_time_from(queries_count), rnd_time_to(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time_from[i] = uniform_dist(e1);
      rnd_time_to[i] = uniform_dist(e1);
    }

    dariadb::IdArray all_ids(_all_id.begin(), _all_id.end());

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      auto f = std::min(rnd_time_from[i], rnd_time_to[i]);
      auto t = std::max(rnd_time_from[i], rnd_time_to[i]);
      auto rdr = ms->readInterval(dariadb::storage::QueryInterval(all_ids, 0, f, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "5. interval: " << elapsed << " readed: " << raw_ptr->count << std::endl;
  }

  { // 6
    std::random_device r;
    std::default_random_engine e1(r());
    std::uniform_int_distribution<dariadb::Time> uniform_dist(
        raw_ptr_ds->minTime(), dariadb::timeutil::current_time());
    std::uniform_int_distribution<dariadb::Id> uniform_dist_id(1, 32767);

    const size_t ids_count = size_t(32768 * 0.1);
    dariadb::IdArray ids;
    ids.resize(ids_count);

    auto raw_ptr = new BenchCallback();
    dariadb::storage::ReaderClb_ptr clbk{raw_ptr};
    const size_t queries_count = 2; // 32;

    std::vector<dariadb::Time> rnd_time_from(queries_count), rnd_time_to(queries_count);
    for (size_t i = 0; i < queries_count; i++) {
      rnd_time_from[i] = uniform_dist(e1);
      rnd_time_to[i] = uniform_dist(e1);
    }

    auto start = clock();

    for (size_t i = 0; i < queries_count; i++) {
      for (size_t j = 0; j < ids_count; j++) {
        ids[j] = uniform_dist_id(e1);
      }
      auto f = std::min(rnd_time_from[i], rnd_time_to[i]);
      auto t = std::max(rnd_time_from[i], rnd_time_to[i]);
      auto rdr = ms->readInterval(dariadb::storage::QueryInterval(ids, 0, f, t));
      rdr->readAll(clbk.get());
    }

    auto elapsed = (((float)clock() - start) / CLOCKS_PER_SEC) / queries_count;
    std::cout << "6. interval: " << elapsed << " readed: " << raw_ptr->count << std::endl;
  }

  ms = nullptr;

  if (dariadb::utils::fs::path_exists(storage_path)) {
    dariadb::utils::fs::rm(storage_path);
  }
}
<|endoftext|>


<commit_before>
#pragma once

#include "quantities/parser.hpp"

#include <array>
#include <string>

#include "quantities/named_quantities.hpp"
#include "quantities/si.hpp"

namespace principia {
namespace quantities {
namespace internal_parser {

using si::AstronomicalUnit;
using si::Day;
using si::Degree;
using si::Kilo;
using si::Metre;
using si::Second;

template<typename Q>
struct ExtractDimensions {};

template<std::int64_t LengthExponent,
         std::int64_t MassExponent,
         std::int64_t TimeExponent,
         std::int64_t CurrentExponent,
         std::int64_t TemperatureExponent,
         std::int64_t AmountExponent,
         std::int64_t LuminousIntensityExponent,
         std::int64_t AngleExponent>
struct ExtractDimensions<
    Quantity<internal_quantities::Dimensions<LengthExponent,
                                             MassExponent,
                                             TimeExponent,
                                             CurrentExponent,
                                             TemperatureExponent,
                                             AmountExponent,
                                             LuminousIntensityExponent,
                                             AngleExponent>>> {
  constexpr static std::array<std::int64_t, 8> dimensions = {
      LengthExponent,
      MassExponent,
      TimeExponent,
      CurrentExponent,
      TemperatureExponent,
      AmountExponent,
      LuminousIntensityExponent,
      AngleExponent};
};

struct ParsedUnit {
  std::array<std::int64_t, 8> dimensions;
  double multiplier;
};

ParsedUnit ParseUnit(std::string const& s) {
  // Units of length.
  if (s == "m") {
    return {ExtractDimensions<Length>::dimensions, 1};
  } else if (s == "km") {
    return {ExtractDimensions<Length>::dimensions, Kilo(Metre) / Metre};
  } else if (s == "au") {
    return {ExtractDimensions<Length>::dimensions, AstronomicalUnit / Metre};
  // Units of time.
  } else if (s == "s") {
    return {ExtractDimensions<Time>::dimensions, 1};
  } else if (s == "d") {
    return {ExtractDimensions<Time>::dimensions, Day / Second};
  // Units of angle.
  } else if (s == "deg" || s == u8"°") {
    return {ExtractDimensions<Angle>::dimensions, Degree / Radian};
  } else if (s == "rad") {
    return {ExtractDimensions<Angle>::dimensions, 1};
  } else {
    LOG(FATAL) << "Unsupported unit " << s;
    base::noreturn();
  }
}

// The patterns that we parse here have the form:
//    U^n/V^m
//  when U and V are (final) unit names and n and m are integers.

template<typename T>
using ParseUnitFunction = T(*)(std::string const& s);

template<typename T, int exponent>
Exponentiation<T, exponent> ParseExponentiationUnit(std::string const& s) {
  int const first_carret = s.find('^');
  int const last_nonblank = s.find_last_not_of(' ', first_carret - 1);
  CHECK_NE(std::string::npos, last_nonblank);

  char* interpreted_end;
  const char* interpreted_begin = s.c_str() + first_carret + 1;
  int const actual_exponent = std::strtol(interpreted_begin,
                                          &interpreted_end,
                                          10);
  int const interpreted = interpreted_end - interpreted_begin;
  CHECK_LT(0, interpreted) << "invalid exponent " << s;
  CHECK_EQ(exponent, actual_exponent);

  return Pow<exponent>(ParseUnit<T>(s.substr(0, last_nonblank + 1)));
}

ParsedUnit ParseQuotientUnit(std::string const& s) {
  int first_blank;
  int first_nonblank;
  int last_nonblank;
  do {
    first_blank = s.find(' ');
    if (first_blank == std::string::npos) {
      return ParseExponentiationUnit(s);
    } else {
      first_nonblank = s.find_first_not_of(' ', first_blank + 1);
      last_nonblank = s.find_last_not_of(' ', first_blank + 1);
      if (first_nonblank != std::string::npos)
    }
  } while (first_blank != std::string::npos && (s[first_nonblank] == '^' ||
                                                s[last_nonblank == '^'))
  CHECK_NE(std::string::npos, first_nonblank);
  int const last_nonblank = s.find_last_not_of(' ', last_slash - 1);
  CHECK_NE(std::string::npos, last_nonblank);
  if (first_blank == std::string::npos)
}

ParsedUnit ParseQuotientUnit(std::string const& s) {
  // Look for the slash from the back to achieve proper associativity.
  int const last_slash = s.rfind('/');
  if (last_slash == std::string::npos) {
    // Not a quotient.
  } else {
    // A quotient.  Parse each half.
    int const first_nonblank = s.find_first_not_of(' ', last_slash + 1);
    CHECK_NE(std::string::npos, first_nonblank);
    int const last_nonblank = s.find_last_not_of(' ', last_slash - 1);
    CHECK_NE(std::string::npos, last_nonblank);
    return parse_numerator_unit(s.substr(0, last_nonblank + 1)) /
           parse_denominator_unit(s.substr(first_nonblank));
  }
}

template<typename T>
T ParseQuantity(std::string const& s) {
  // Parse a double.
  char* interpreted_end;
  char const* const c_string = s.c_str();
  double const magnitude = std::strtod(c_string, &interpreted_end);
  int const interpreted = interpreted_end - c_string;
  CHECK_LT(0, interpreted) << "invalid floating-point number " << s;

  // Locate the unit.  It may be empty for a double.
  int const first_nonblank = s.find_first_not_of(' ', interpreted);
  int const last_nonblank = s.find_last_not_of(' ');
  std::string unit_string;
  if (first_nonblank != std::string::npos) {
    unit_string = s.substr(first_nonblank, last_nonblank - first_nonblank + 1);
  }

  ParsedUnit const unit = ParseQuotientUnit(unit_string);
  return magnitude * unit;
}

template<>
inline Length ParseUnit(std::string const& s) {
  if (s == "m") {
    return si::Metre;
  } else if (s == "km") {
    return si::Kilo(si::Metre);
  } else if (s == "au") {
    return si::AstronomicalUnit;
  } else {
    LOG(FATAL) << "Unsupported unit of length " << s;
    base::noreturn();
  }
}

template<>
inline Time ParseUnit(std::string const& s) {
  if (s == "s") {
    return si::Second;
  } else if (s == "d") {
    return si::Day;
  } else {
    LOG(FATAL) << "Unsupported unit of time " << s;
    base::noreturn();
  }
}

template<>
inline Angle ParseUnit(std::string const& s) {
  if (s == "deg" || s == u8"°") {
    return si::Degree;
  } else if (s == "rad") {
    return si::Radian;
  } else {
    LOG(FATAL) << "Unsupported unit of angle " << s;
    base::noreturn();
  }
}

template<>
inline AngularFrequency ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s, &ParseUnit<Angle>, &ParseUnit<Time>);
}

template<>
inline Speed ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s, &ParseUnit<Length>, &ParseUnit<Time>);
}

template<>
inline GravitationalParameter ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s,
                           &ParseExponentiationUnit<Length, 3>,
                           &ParseExponentiationUnit<Time, 2>);
}

template<>
inline double ParseUnit<double>(std::string const& s) {
  CHECK(s.empty()) << s;
  return 1;
}

}  // namespace internal_parser
}  // namespace quantities
}  // namespace principia
<commit_msg>Recursive parsing.<commit_after>
#pragma once

#include "quantities/parser.hpp"

#include <array>
#include <string>

#include "quantities/named_quantities.hpp"
#include "quantities/si.hpp"

namespace principia {
namespace quantities {
namespace internal_parser {

using si::AstronomicalUnit;
using si::Day;
using si::Degree;
using si::Kilo;
using si::Metre;
using si::Second;

template<typename Q>
struct ExtractDimensions {};

template<std::int64_t LengthExponent,
         std::int64_t MassExponent,
         std::int64_t TimeExponent,
         std::int64_t CurrentExponent,
         std::int64_t TemperatureExponent,
         std::int64_t AmountExponent,
         std::int64_t LuminousIntensityExponent,
         std::int64_t AngleExponent>
struct ExtractDimensions<
    Quantity<internal_quantities::Dimensions<LengthExponent,
                                             MassExponent,
                                             TimeExponent,
                                             CurrentExponent,
                                             TemperatureExponent,
                                             AmountExponent,
                                             LuminousIntensityExponent,
                                             AngleExponent>>> {
  constexpr static std::array<std::int64_t, 8> dimensions = {
      LengthExponent,
      MassExponent,
      TimeExponent,
      CurrentExponent,
      TemperatureExponent,
      AmountExponent,
      LuminousIntensityExponent,
      AngleExponent};
};

struct ParsedUnit {
  std::array<std::int64_t, 8> dimensions;
  double multiplier;
};

ParsedUnit ParseUnit(std::string const& s) {
  // Units of length.
  if (s == "m") {
    return {ExtractDimensions<Length>::dimensions, 1};
  } else if (s == "km") {
    return {ExtractDimensions<Length>::dimensions, Kilo(Metre) / Metre};
  } else if (s == "au") {
    return {ExtractDimensions<Length>::dimensions, AstronomicalUnit / Metre};
  // Units of time.
  } else if (s == "s") {
    return {ExtractDimensions<Time>::dimensions, 1};
  } else if (s == "d") {
    return {ExtractDimensions<Time>::dimensions, Day / Second};
  // Units of angle.
  } else if (s == "deg" || s == u8"°") {
    return {ExtractDimensions<Angle>::dimensions, Degree / Radian};
  } else if (s == "rad") {
    return {ExtractDimensions<Angle>::dimensions, 1};
  } else {
    LOG(FATAL) << "Unsupported unit " << s;
    base::noreturn();
  }
}

int ParseExponent(std::string const& s) {
  // Parse an int.
  char* interpreted_end;
  char const* const c_string = s.c_str();
  double const exponent = std::strtol(c_string, &interpreted_end, /*base=*/10);
  int const interpreted = interpreted_end - c_string;
  CHECK_LT(0, interpreted) << "invalid integer number " << s;
  return exponent;
}

ParsedUnit ParseExponentiationUnit(std::string const& s) {
  int const first_caret = s.find('^');
  if (first_caret == std::string::npos) {
    return ParseUnit(s);
  } else {
    int const first_nonblank = s.find_first_not_of(' ', last_slash + 1);
    CHECK_NE(std::string::npos, first_nonblank);
    int const last_nonblank = s.find_last_not_of(' ', last_slash - 1);
    CHECK_NE(std::string::npos, last_nonblank);
    auto const left = ParseUnit(s.substr(0, last_nonblank + 1));
    auto const right = ParseExponent(s.substr(first_nonblank));
  }
}

ParsedUnit ParseProductUnit(std::string const& s) {
  // For a product we are looking for a blank character that is not next to a
  // carret.
  int first_blank;
  int first_nonblank;
  int last_nonblank;
  for (;;) {
    first_blank = s.find(' ');
    if (first_blank == std::string::npos) {
      return ParseExponentiationUnit(s);
    } else {
      first_nonblank = s.find_first_not_of(' ', first_blank + 1);
      last_nonblank = s.find_last_not_of(' ', first_blank + 1);
      if ((first_nonblank == std::string::npos || s[first_nonblank] != '^') &&
          (last_nonblank == std::string::npos || s[last_nonblank] != '^')) {
        break;
      }
    }
  }
  auto const left = ParseProductUnit(s.substr(0, last_nonblank + 1));
  auto const right = ParseProductUnit(s.substr(first_nonblank));
}

ParsedUnit ParseQuotientUnit(std::string const& s) {
  // Look for the slash from the back to achieve proper associativity.
  int const last_slash = s.rfind('/');
  if (last_slash == std::string::npos) {
    // Not a quotient.
    return ParseProductUnit(s);
  } else {
    // A quotient.  Parse each half.
    int const first_nonblank = s.find_first_not_of(' ', last_slash + 1);
    CHECK_NE(std::string::npos, first_nonblank);
    int const last_nonblank = s.find_last_not_of(' ', last_slash - 1);
    CHECK_NE(std::string::npos, last_nonblank);
    auto const left = ParseProductUnit(s.substr(0, last_nonblank + 1));
    auto const right = ParseProductUnit(s.substr(first_nonblank));
  }
}

template<typename T>
T ParseQuantity(std::string const& s) {
  // Parse a double.
  char* interpreted_end;
  char const* const c_string = s.c_str();
  double const magnitude = std::strtod(c_string, &interpreted_end);
  int const interpreted = interpreted_end - c_string;
  CHECK_LT(0, interpreted) << "invalid floating-point number " << s;

  // Locate the unit.  It may be empty for a double.
  int const first_nonblank = s.find_first_not_of(' ', interpreted);
  int const last_nonblank = s.find_last_not_of(' ');
  std::string unit_string;
  if (first_nonblank != std::string::npos) {
    unit_string = s.substr(first_nonblank, last_nonblank - first_nonblank + 1);
  }

  ParsedUnit const unit = ParseQuotientUnit(unit_string);
  return magnitude * unit;
}

template<>
inline Length ParseUnit(std::string const& s) {
  if (s == "m") {
    return si::Metre;
  } else if (s == "km") {
    return si::Kilo(si::Metre);
  } else if (s == "au") {
    return si::AstronomicalUnit;
  } else {
    LOG(FATAL) << "Unsupported unit of length " << s;
    base::noreturn();
  }
}

template<>
inline Time ParseUnit(std::string const& s) {
  if (s == "s") {
    return si::Second;
  } else if (s == "d") {
    return si::Day;
  } else {
    LOG(FATAL) << "Unsupported unit of time " << s;
    base::noreturn();
  }
}

template<>
inline Angle ParseUnit(std::string const& s) {
  if (s == "deg" || s == u8"°") {
    return si::Degree;
  } else if (s == "rad") {
    return si::Radian;
  } else {
    LOG(FATAL) << "Unsupported unit of angle " << s;
    base::noreturn();
  }
}

template<>
inline AngularFrequency ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s, &ParseUnit<Angle>, &ParseUnit<Time>);
}

template<>
inline Speed ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s, &ParseUnit<Length>, &ParseUnit<Time>);
}

template<>
inline GravitationalParameter ParseUnit(std::string const& s) {
  return ParseQuotientUnit(s,
                           &ParseExponentiationUnit<Length, 3>,
                           &ParseExponentiationUnit<Time, 2>);
}

template<>
inline double ParseUnit<double>(std::string const& s) {
  CHECK(s.empty()) << s;
  return 1;
}

}  // namespace internal_parser
}  // namespace quantities
}  // namespace principia
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2018-2019 Baldur Karlsson
 *
 * 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 "jdwp.h"
#include <functional>
#include "core/core.h"
#include "strings/string_utils.h"
#include "android.h"
#include "android_utils.h"

namespace JDWP
{
void InjectVulkanLayerSearchPath(Connection &conn, threadID thread, int32_t slotIdx,
                                 const std::string &libPath)
{
  referenceTypeID stringClass = conn.GetType("Ljava/lang/String;");
  methodID stringConcat = conn.GetMethod(stringClass, "concat");

  if(conn.IsErrored())
    return;

  if(!stringClass || !stringConcat)
  {
    RDCERR("Couldn't find java.lang.String (%llu) or java.lang.String.concat() (%llu)", stringClass,
           stringConcat);
    return;
  }

  // get the callstack frames
  std::vector<StackFrame> stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return;
  }

  // get the local in the top (current) frame
  value librarySearchPath = conn.GetLocalValue(thread, stack[0].id, slotIdx, Tag::Object);

  if(librarySearchPath.tag != Tag::String || librarySearchPath.String == 0)
  {
    RDCERR("Couldn't get 'String librarySearchPath' local parameter!");
    return;
  }

  RDCDEBUG("librarySearchPath is %s", conn.GetString(librarySearchPath.String).c_str());

  value appendSearch = conn.NewString(thread, ":" + libPath);

  // temp = librarySearchPath.concat(appendSearch);
  value temp = conn.InvokeInstance(thread, stringClass, stringConcat, librarySearchPath.String,
                                   {appendSearch});

  if(temp.tag != Tag::String || temp.String == 0)
  {
    RDCERR("Failed to concat search path!");
    return;
  }

  RDCDEBUG("librarySearchPath is now %s", conn.GetString(temp.String).c_str());

  // we will have resume the thread above to call concat, invalidating our frames.
  // Re-fetch the callstack
  stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return;
  }

  // replace the search path with our modified one
  // librarySearchPath = temp;
  conn.SetLocalValue(thread, stack[0].id, slotIdx, temp);
}

bool InjectLibraries(const std::string &deviceID, Network::Socket *sock)
{
  Connection conn(sock);

  // check that the handshake completed successfully
  if(conn.IsErrored())
    return false;

  // immediately re-suspend, as connecting will have woken it up
  conn.Suspend();

  conn.SetupIDSizes();

  if(conn.IsErrored())
    return false;

  // default to arm as a safe bet
  Android::ABI abi = Android::ABI::armeabi_v7a;

  // determine the CPU ABI from android.os.Build.CPU_ABI
  referenceTypeID buildClass = conn.GetType("Landroid/os/Build;");
  if(buildClass)
  {
    fieldID CPU_ABI = conn.GetField(buildClass, "CPU_ABI");

    if(CPU_ABI)
    {
      value val = conn.GetFieldValue(buildClass, CPU_ABI);

      if(val.tag == Tag::String)
        abi = Android::GetABI(conn.GetString(val.String));
      else
        RDCERR("CPU_ABI value was type %u, not string!", (uint32_t)val.tag);
    }
    else
    {
      RDCERR("Couldn't find CPU_ABI field in android.os.Build");
    }
  }
  else
  {
    RDCERR("Couldn't find android.os.Build");
  }

  if(abi == Android::ABI::unknown)
  {
    RDCERR("Unrecognised running ABI, falling back to armeabi-v7a");
    abi = Android::ABI::armeabi_v7a;
  }

  std::string libPath = Android::GetPathForPackage(deviceID, Android::GetRenderDocPackageForABI(abi));

  switch(abi)
  {
    case Android::ABI::unknown:
    case Android::ABI::armeabi_v7a: libPath += "lib/arm"; break;
    case Android::ABI::arm64_v8a: libPath += "lib/arm64"; break;
    case Android::ABI::x86_64: libPath += "lib/x86_64"; break;
    case Android::ABI::x86: libPath += "lib/x86"; break;
  }

  RDCLOG("Injecting RenderDoc from library in %s", libPath.c_str());

  if(conn.IsErrored())
    return false;

  // try to find the vulkan loader class and patch the search path when getClassLoader is called.
  // This is an optional step as some devices may not support vulkan and may not have this class, so
  // in that case we just skip it.
  referenceTypeID vulkanLoaderClass = conn.GetType("Landroid/app/ApplicationLoaders;");

  if(vulkanLoaderClass)
  {
    // See:
    // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ApplicationLoaders.java
    // for the public getClassLoader.

    // look for both signatures in this order, as it goes from most recent to least recent. In some
    // cases (e.g. with List<ClassLoader> sharedLibraries) the older function is still around as an
    // overload that forwards on - so may not be called. This would cause us to wait for a function
    // to be hit that was never hit.

    const char *getClassLoaderSignatures[] = {
        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent, String cacheKey,
        //                            String classLoaderName, List<ClassLoader> sharedLibraries);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;"
        "Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)Ljava/lang/ClassLoader;",

        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent, String classLoaderName);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;"
        "Ljava/lang/String;)Ljava/lang/ClassLoader;",

        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)"
        "Ljava/lang/ClassLoader;",
    };

    methodID vulkanLoaderMethod = 0;
    for(const char *sig : getClassLoaderSignatures)
    {
      vulkanLoaderMethod = conn.GetMethod(vulkanLoaderClass, "getClassLoader", sig);

      if(vulkanLoaderMethod)
      {
        RDCLOG("Got android.app.ApplicationLoaders.getClassLoader signature %s", sig);
        break;
      }
    }

    if(vulkanLoaderMethod)
    {
      std::vector<VariableSlot> slots = conn.GetLocalVariables(vulkanLoaderClass, vulkanLoaderMethod);

      int32_t slotIdx = -1;

      for(const VariableSlot &s : slots)
      {
        if(s.name == "librarySearchPath")
        {
          slotIdx = s.slot;
          break;
        }
      }

      // Android 9 doesn't return names and changed how slots are indexed, try an offset from this
      if(slotIdx == -1)
      {
        for(const VariableSlot &s : slots)
        {
          if(s.name == "this")
          {
              slotIdx = s.slot + 4;
              break;
          }
        }
      }

      // as a default, use the 4th slot as it's the 4th argument argument (0 is this), if symbols
      // weren't available we can't identify the variable by name
      if(slotIdx == -1)
        slotIdx = 4;

      // wait for the method to get hit - WaitForEvent will resume, watch events, and return
      // (re-suspended) when the first event occurs that matches the filter function
      Event evData =
          conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, vulkanLoaderClass}},
                            [vulkanLoaderMethod](const Event &evData) {
                              return evData.MethodEntry.location.meth == vulkanLoaderMethod;
                            });

      // if we successfully hit the event, try to inject
      if(evData.eventKind == EventKind::MethodEntry)
        InjectVulkanLayerSearchPath(conn, evData.MethodEntry.thread, slotIdx, libPath);
    }
    else
    {
      // we expect if we can get the class, we should find the method.
      RDCERR("Couldn't find getClassLoader method in android.app.ApplicationLoaders");
    }
  }
  else
  {
    // warning only - it's not a problem if we're capturing GLES
    RDCWARN("Couldn't find class android.app.ApplicationLoaders. Vulkan won't be hooked.");
  }

  // we get here whether we processed vulkan or not. Now we need to wait for the application to hit
  // onCreate() and load our library

  referenceTypeID androidApp = conn.GetType("Landroid/app/Application;");

  if(androidApp == 0)
  {
    RDCERR("Couldn't find android.app.Application");
    return false;
  }

  methodID appConstruct = conn.GetMethod(androidApp, "<init>", "()V");

  if(appConstruct == 0)
  {
    RDCERR("Couldn't find android.app.Application constructor");
    return false;
  }

  threadID thread;

  // wait until we hit the constructor of android.app.Application
  {
    Event evData = conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, androidApp}},
                                     [appConstruct](const Event &evData) {
                                       return evData.MethodEntry.location.meth == appConstruct;
                                     });

    if(evData.eventKind == EventKind::MethodEntry)
      thread = evData.MethodEntry.thread;
  }

  if(thread == 0)
  {
    RDCERR("Didn't hit android.app.Application constructor");
    return false;
  }

  // get the callstack frames
  std::vector<StackFrame> stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return false;
  }

  // get this on the top frame
  objectID thisPtr = conn.GetThis(thread, stack[0].id);

  if(thisPtr == 0)
  {
    RDCERR("Couldn't find this");
    return false;
  }

  // get the type for the this object
  referenceTypeID thisType = conn.GetType(thisPtr);

  if(thisType == 0)
  {
    RDCERR("Couldn't find this's class");
    return false;
  }

  // call getClass, this will give us the information for the most derived class
  methodID getClass = conn.GetMethod(thisType, "getClass", "()Ljava/lang/Class;");

  if(getClass == 0)
  {
    RDCERR("Couldn't find this.getClass()");
    return false;
  }

  value thisClass = conn.InvokeInstance(thread, thisType, getClass, thisPtr, {});

  if(thisClass.tag != Tag::ClassObject || thisClass.Object == 0)
  {
    RDCERR("Failed to call this.getClass()!");
    return false;
  }

  // look up onCreate in the most derived class - since we can't guarantee that the base
  // application.app.onCreate() will get called.
  //
  // Note that because we're filtering on both classID and methodID, we need to return back the
  // exact class in the inheritance hierarchy matching the methodID, otherwise we could filter on
  // the derived class but a parent method, and have no hits.
  //
  // This can happen if the most derived class doesn't have an onCreate, and we have to search to a
  // superclass
  referenceTypeID onCreateClass = thisClass.RefType;
  methodID onCreateMethod = conn.GetMethod(thisClass.RefType, "onCreate", "()V", &onCreateClass);

  if(onCreateMethod == 0)
  {
    RDCERR("Couldn't find this.getClass().onCreate()");
    return false;
  }

  // wait until we hit the derived onCreate
  {
    thread = 0;

    Event evData =
        conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, onCreateClass}},
                          [onCreateMethod](const Event &evData) {
                            return evData.MethodEntry.location.meth == onCreateMethod;
                          });

    if(evData.eventKind == EventKind::MethodEntry)
      thread = evData.MethodEntry.thread;
  }

  if(thread == 0)
  {
    RDCERR("Didn't hit android.app.Application.onCreate()");
    return false;
  }

  // find java.lang.Runtime
  referenceTypeID runtime = conn.GetType("Ljava/lang/Runtime;");

  if(runtime == 0)
  {
    RDCERR("Couldn't find java.lang.Runtime");
    return false;
  }

  // find both the static Runtime.getRuntime() as well as the instance Runtime.load()
  methodID getRuntime = conn.GetMethod(runtime, "getRuntime", "()Ljava/lang/Runtime;");
  methodID load = conn.GetMethod(runtime, "load", "(Ljava/lang/String;)V");

  if(getRuntime == 0 || load == 0)
  {
    RDCERR("Couldn't find java.lang.Runtime.getRuntime() %llu or java.lang.Runtime.load() %llu",
           getRuntime, load);
    return false;
  }

  // get the Runtime object via java.lang.Runtime.getRuntime()
  value runtimeObject = conn.InvokeStatic(thread, runtime, getRuntime, {});

  if(runtimeObject.tag != Tag::Object || runtimeObject.Object == 0)
  {
    RDCERR("Failed to call getClass!");
    return false;
  }

  // call Runtime.load() on our library. This will load the library and from then on it's
  // responsible for injecting its hooks into GLES on its own. See android_hook.cpp for more
  // information on the implementation
  value ret = conn.InvokeInstance(thread, runtime, load, runtimeObject.Object,
                                  {conn.NewString(thread, libPath + "/" RENDERDOC_ANDROID_LIBRARY)});

  if(ret.tag != Tag::Void)
  {
    RDCERR("Failed to call load(%s/%s)!", libPath.c_str(), RENDERDOC_ANDROID_LIBRARY);
    return false;
  }

  return true;
}
};    // namespace JDWP

namespace Android
{
bool InjectWithJDWP(const std::string &deviceID, uint16_t jdwpport)
{
  Network::Socket *sock = Network::CreateClientSocket("localhost", jdwpport, 500);

  if(sock)
  {
    bool ret = JDWP::InjectLibraries(deviceID, sock);
    delete sock;

    return ret;
  }
  else
  {
    RDCERR("Couldn't make JDWP connection");
  }

  return false;
}
};    // namespace Android
<commit_msg>Only use this + 4 slot if there is no slot 4<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2018-2019 Baldur Karlsson
 *
 * 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 "jdwp.h"
#include <functional>
#include "core/core.h"
#include "strings/string_utils.h"
#include "android.h"
#include "android_utils.h"

namespace JDWP
{
void InjectVulkanLayerSearchPath(Connection &conn, threadID thread, int32_t slotIdx,
                                 const std::string &libPath)
{
  referenceTypeID stringClass = conn.GetType("Ljava/lang/String;");
  methodID stringConcat = conn.GetMethod(stringClass, "concat");

  if(conn.IsErrored())
    return;

  if(!stringClass || !stringConcat)
  {
    RDCERR("Couldn't find java.lang.String (%llu) or java.lang.String.concat() (%llu)", stringClass,
           stringConcat);
    return;
  }

  // get the callstack frames
  std::vector<StackFrame> stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return;
  }

  // get the local in the top (current) frame
  value librarySearchPath = conn.GetLocalValue(thread, stack[0].id, slotIdx, Tag::Object);

  if(librarySearchPath.tag != Tag::String || librarySearchPath.String == 0)
  {
    RDCERR("Couldn't get 'String librarySearchPath' local parameter!");
    return;
  }

  RDCDEBUG("librarySearchPath is %s", conn.GetString(librarySearchPath.String).c_str());

  value appendSearch = conn.NewString(thread, ":" + libPath);

  // temp = librarySearchPath.concat(appendSearch);
  value temp = conn.InvokeInstance(thread, stringClass, stringConcat, librarySearchPath.String,
                                   {appendSearch});

  if(temp.tag != Tag::String || temp.String == 0)
  {
    RDCERR("Failed to concat search path!");
    return;
  }

  RDCDEBUG("librarySearchPath is now %s", conn.GetString(temp.String).c_str());

  // we will have resume the thread above to call concat, invalidating our frames.
  // Re-fetch the callstack
  stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return;
  }

  // replace the search path with our modified one
  // librarySearchPath = temp;
  conn.SetLocalValue(thread, stack[0].id, slotIdx, temp);
}

bool InjectLibraries(const std::string &deviceID, Network::Socket *sock)
{
  Connection conn(sock);

  // check that the handshake completed successfully
  if(conn.IsErrored())
    return false;

  // immediately re-suspend, as connecting will have woken it up
  conn.Suspend();

  conn.SetupIDSizes();

  if(conn.IsErrored())
    return false;

  // default to arm as a safe bet
  Android::ABI abi = Android::ABI::armeabi_v7a;

  // determine the CPU ABI from android.os.Build.CPU_ABI
  referenceTypeID buildClass = conn.GetType("Landroid/os/Build;");
  if(buildClass)
  {
    fieldID CPU_ABI = conn.GetField(buildClass, "CPU_ABI");

    if(CPU_ABI)
    {
      value val = conn.GetFieldValue(buildClass, CPU_ABI);

      if(val.tag == Tag::String)
        abi = Android::GetABI(conn.GetString(val.String));
      else
        RDCERR("CPU_ABI value was type %u, not string!", (uint32_t)val.tag);
    }
    else
    {
      RDCERR("Couldn't find CPU_ABI field in android.os.Build");
    }
  }
  else
  {
    RDCERR("Couldn't find android.os.Build");
  }

  if(abi == Android::ABI::unknown)
  {
    RDCERR("Unrecognised running ABI, falling back to armeabi-v7a");
    abi = Android::ABI::armeabi_v7a;
  }

  std::string libPath = Android::GetPathForPackage(deviceID, Android::GetRenderDocPackageForABI(abi));

  switch(abi)
  {
    case Android::ABI::unknown:
    case Android::ABI::armeabi_v7a: libPath += "lib/arm"; break;
    case Android::ABI::arm64_v8a: libPath += "lib/arm64"; break;
    case Android::ABI::x86_64: libPath += "lib/x86_64"; break;
    case Android::ABI::x86: libPath += "lib/x86"; break;
  }

  RDCLOG("Injecting RenderDoc from library in %s", libPath.c_str());

  if(conn.IsErrored())
    return false;

  // try to find the vulkan loader class and patch the search path when getClassLoader is called.
  // This is an optional step as some devices may not support vulkan and may not have this class, so
  // in that case we just skip it.
  referenceTypeID vulkanLoaderClass = conn.GetType("Landroid/app/ApplicationLoaders;");

  if(vulkanLoaderClass)
  {
    // See:
    // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/app/ApplicationLoaders.java
    // for the public getClassLoader.

    // look for both signatures in this order, as it goes from most recent to least recent. In some
    // cases (e.g. with List<ClassLoader> sharedLibraries) the older function is still around as an
    // overload that forwards on - so may not be called. This would cause us to wait for a function
    // to be hit that was never hit.

    const char *getClassLoaderSignatures[] = {
        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent, String cacheKey,
        //                            String classLoaderName, List<ClassLoader> sharedLibraries);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;"
        "Ljava/lang/String;Ljava/lang/String;Ljava/util/List;)Ljava/lang/ClassLoader;",

        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent, String classLoaderName);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;"
        "Ljava/lang/String;)Ljava/lang/ClassLoader;",

        // ClassLoader getClassLoader(String zip, int targetSdkVersion, boolean isBundled,
        //                            String librarySearchPath, String libraryPermittedPath,
        //                            ClassLoader parent);
        "(Ljava/lang/String;IZLjava/lang/String;Ljava/lang/String;Ljava/lang/ClassLoader;)"
        "Ljava/lang/ClassLoader;",
    };

    methodID vulkanLoaderMethod = 0;
    for(const char *sig : getClassLoaderSignatures)
    {
      vulkanLoaderMethod = conn.GetMethod(vulkanLoaderClass, "getClassLoader", sig);

      if(vulkanLoaderMethod)
      {
        RDCLOG("Got android.app.ApplicationLoaders.getClassLoader signature %s", sig);
        break;
      }
    }

    if(vulkanLoaderMethod)
    {
      std::vector<VariableSlot> slots = conn.GetLocalVariables(vulkanLoaderClass, vulkanLoaderMethod);

      int32_t slotIdx = -1, thisSlotIdx = -1;
      bool slot4Exists = false;

      for(const VariableSlot &s : slots)
      {
        if(s.name == "librarySearchPath")
        {
          slotIdx = s.slot;
          break;
        }
        else if(s.name == "this")
        {
          thisSlotIdx = s.slot;
        }

        if(s.slot == 4)
          slot4Exists = true;
      }

      // on some newer devices slots are not 0-based, try an offset from this if there is no slot 4
      if(slotIdx == -1 && thisSlotIdx != -1 && !slot4Exists)
        slotIdx = thisSlotIdx + 4;

      // as a default, use the 4th slot as it's the 4th argument argument (0 is this), if symbols
      // weren't available we can't identify the variable by name
      if(slotIdx == -1)
        slotIdx = 4;

      // wait for the method to get hit - WaitForEvent will resume, watch events, and return
      // (re-suspended) when the first event occurs that matches the filter function
      Event evData =
          conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, vulkanLoaderClass}},
                            [vulkanLoaderMethod](const Event &evData) {
                              return evData.MethodEntry.location.meth == vulkanLoaderMethod;
                            });

      // if we successfully hit the event, try to inject
      if(evData.eventKind == EventKind::MethodEntry)
        InjectVulkanLayerSearchPath(conn, evData.MethodEntry.thread, slotIdx, libPath);
    }
    else
    {
      // we expect if we can get the class, we should find the method.
      RDCERR("Couldn't find getClassLoader method in android.app.ApplicationLoaders");
    }
  }
  else
  {
    // warning only - it's not a problem if we're capturing GLES
    RDCWARN("Couldn't find class android.app.ApplicationLoaders. Vulkan won't be hooked.");
  }

  // we get here whether we processed vulkan or not. Now we need to wait for the application to hit
  // onCreate() and load our library

  referenceTypeID androidApp = conn.GetType("Landroid/app/Application;");

  if(androidApp == 0)
  {
    RDCERR("Couldn't find android.app.Application");
    return false;
  }

  methodID appConstruct = conn.GetMethod(androidApp, "<init>", "()V");

  if(appConstruct == 0)
  {
    RDCERR("Couldn't find android.app.Application constructor");
    return false;
  }

  threadID thread;

  // wait until we hit the constructor of android.app.Application
  {
    Event evData = conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, androidApp}},
                                     [appConstruct](const Event &evData) {
                                       return evData.MethodEntry.location.meth == appConstruct;
                                     });

    if(evData.eventKind == EventKind::MethodEntry)
      thread = evData.MethodEntry.thread;
  }

  if(thread == 0)
  {
    RDCERR("Didn't hit android.app.Application constructor");
    return false;
  }

  // get the callstack frames
  std::vector<StackFrame> stack = conn.GetCallStack(thread);

  if(stack.empty())
  {
    RDCERR("Couldn't get callstack!");
    return false;
  }

  // get this on the top frame
  objectID thisPtr = conn.GetThis(thread, stack[0].id);

  if(thisPtr == 0)
  {
    RDCERR("Couldn't find this");
    return false;
  }

  // get the type for the this object
  referenceTypeID thisType = conn.GetType(thisPtr);

  if(thisType == 0)
  {
    RDCERR("Couldn't find this's class");
    return false;
  }

  // call getClass, this will give us the information for the most derived class
  methodID getClass = conn.GetMethod(thisType, "getClass", "()Ljava/lang/Class;");

  if(getClass == 0)
  {
    RDCERR("Couldn't find this.getClass()");
    return false;
  }

  value thisClass = conn.InvokeInstance(thread, thisType, getClass, thisPtr, {});

  if(thisClass.tag != Tag::ClassObject || thisClass.Object == 0)
  {
    RDCERR("Failed to call this.getClass()!");
    return false;
  }

  // look up onCreate in the most derived class - since we can't guarantee that the base
  // application.app.onCreate() will get called.
  //
  // Note that because we're filtering on both classID and methodID, we need to return back the
  // exact class in the inheritance hierarchy matching the methodID, otherwise we could filter on
  // the derived class but a parent method, and have no hits.
  //
  // This can happen if the most derived class doesn't have an onCreate, and we have to search to a
  // superclass
  referenceTypeID onCreateClass = thisClass.RefType;
  methodID onCreateMethod = conn.GetMethod(thisClass.RefType, "onCreate", "()V", &onCreateClass);

  if(onCreateMethod == 0)
  {
    RDCERR("Couldn't find this.getClass().onCreate()");
    return false;
  }

  // wait until we hit the derived onCreate
  {
    thread = 0;

    Event evData =
        conn.WaitForEvent(EventKind::MethodEntry, {{ModifierKind::ClassOnly, onCreateClass}},
                          [onCreateMethod](const Event &evData) {
                            return evData.MethodEntry.location.meth == onCreateMethod;
                          });

    if(evData.eventKind == EventKind::MethodEntry)
      thread = evData.MethodEntry.thread;
  }

  if(thread == 0)
  {
    RDCERR("Didn't hit android.app.Application.onCreate()");
    return false;
  }

  // find java.lang.Runtime
  referenceTypeID runtime = conn.GetType("Ljava/lang/Runtime;");

  if(runtime == 0)
  {
    RDCERR("Couldn't find java.lang.Runtime");
    return false;
  }

  // find both the static Runtime.getRuntime() as well as the instance Runtime.load()
  methodID getRuntime = conn.GetMethod(runtime, "getRuntime", "()Ljava/lang/Runtime;");
  methodID load = conn.GetMethod(runtime, "load", "(Ljava/lang/String;)V");

  if(getRuntime == 0 || load == 0)
  {
    RDCERR("Couldn't find java.lang.Runtime.getRuntime() %llu or java.lang.Runtime.load() %llu",
           getRuntime, load);
    return false;
  }

  // get the Runtime object via java.lang.Runtime.getRuntime()
  value runtimeObject = conn.InvokeStatic(thread, runtime, getRuntime, {});

  if(runtimeObject.tag != Tag::Object || runtimeObject.Object == 0)
  {
    RDCERR("Failed to call getClass!");
    return false;
  }

  // call Runtime.load() on our library. This will load the library and from then on it's
  // responsible for injecting its hooks into GLES on its own. See android_hook.cpp for more
  // information on the implementation
  value ret = conn.InvokeInstance(thread, runtime, load, runtimeObject.Object,
                                  {conn.NewString(thread, libPath + "/" RENDERDOC_ANDROID_LIBRARY)});

  if(ret.tag != Tag::Void)
  {
    RDCERR("Failed to call load(%s/%s)!", libPath.c_str(), RENDERDOC_ANDROID_LIBRARY);
    return false;
  }

  return true;
}
};    // namespace JDWP

namespace Android
{
bool InjectWithJDWP(const std::string &deviceID, uint16_t jdwpport)
{
  Network::Socket *sock = Network::CreateClientSocket("localhost", jdwpport, 500);

  if(sock)
  {
    bool ret = JDWP::InjectLibraries(deviceID, sock);
    delete sock;

    return ret;
  }
  else
  {
    RDCERR("Couldn't make JDWP connection");
  }

  return false;
}
};    // namespace Android
<|endoftext|>


<commit_before>//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <vector>

// class vector

// size_type size() const noexcept;

#include <vector>
#include <cassert>

#include "test_macros.h"
#include "min_allocator.h"

int main()
{
    {
    typedef std::vector<bool> C;
    C c;
    ASSERT_NOEXCEPT(c.size());
    assert(c.size() == 0);
    c.push_back(false);
    assert(c.size() == 1);
    c.push_back(true);
    assert(c.size() == 2);
    c.push_back(C::value_type(3));
    assert(c.size() == 3);
    c.erase(c.begin());
    assert(c.size() == 2);
    c.erase(c.begin());
    assert(c.size() == 1);
    c.erase(c.begin());
    assert(c.size() == 0);
    }
#if TEST_STD_VER >= 11
    {
    typedef std::vector<bool, min_allocator<bool>> C;
    C c;
    ASSERT_NOEXCEPT(c.size());
    assert(c.size() == 0);
    c.push_back(false);
    assert(c.size() == 1);
    c.push_back(true);
    assert(c.size() == 2);
    c.push_back(C::value_type(3));
    assert(c.size() == 3);
    c.erase(c.begin());
    assert(c.size() == 2);
    c.erase(c.begin());
    assert(c.size() == 1);
    c.erase(c.begin());
    assert(c.size() == 0);
    }
#endif
}
<commit_msg>Fix copy/paste bug in test where we were putting a '3' into a vector<bool>. NFC.<commit_after>//===----------------------------------------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is dual licensed under the MIT and the University of Illinois Open
// Source Licenses. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

// <vector>

// class vector

// size_type size() const noexcept;

#include <vector>
#include <cassert>

#include "test_macros.h"
#include "min_allocator.h"

int main()
{
    {
    typedef std::vector<bool> C;
    C c;
    ASSERT_NOEXCEPT(c.size());
    assert(c.size() == 0);
    c.push_back(false);
    assert(c.size() == 1);
    c.push_back(true);
    assert(c.size() == 2);
    c.push_back(false);
    assert(c.size() == 3);
    c.erase(c.begin());
    assert(c.size() == 2);
    c.erase(c.begin());
    assert(c.size() == 1);
    c.erase(c.begin());
    assert(c.size() == 0);
    }
#if TEST_STD_VER >= 11
    {
    typedef std::vector<bool, min_allocator<bool>> C;
    C c;
    ASSERT_NOEXCEPT(c.size());
    assert(c.size() == 0);
    c.push_back(false);
    assert(c.size() == 1);
    c.push_back(true);
    assert(c.size() == 2);
    c.push_back(false);
    assert(c.size() == 3);
    c.erase(c.begin());
    assert(c.size() == 2);
    c.erase(c.begin());
    assert(c.size() == 1);
    c.erase(c.begin());
    assert(c.size() == 0);
    }
#endif
}
<|endoftext|>


<commit_before>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * 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 <dlfcn.h>
#include <stdio.h>
#include "driver/gl/gl_common.h"

// we need to export the whole of the EGL API, since we will have redirected any dlopen()
// for libEGL.so to ourselves, and dlsym() for any of these entry points must return a valid
// function. We don't need to intercept them, so we just pass it along

extern void *libGLdlsymHandle;

/*
  in bash:

    function EGLHook()
    {
        N=$1;
        echo -n "#define EGL_PASSTHRU_$N(ret, function";
            for I in `seq 1 $N`; do echo -n ", t$I, p$I"; done;
        echo ") \\";

        echo -en "\ttypedef ret (*CONCAT(function, _hooktype)) (";
            for I in `seq 1 $N`; do echo -n "t$I"; if [ $I -ne $N ]; then echo -n ", "; fi; done;
        echo "); \\";

        echo -e "\textern \"C\" __attribute__ ((visibility (\"default\"))) \\";

        echo -en "\tret function(";
            for I in `seq 1 $N`; do echo -n "t$I p$I"; if [ $I -ne $N ]; then echo -n ", "; fi;
  done;
        echo ") \\";

        echo -en "\t{ CONCAT(function, _hooktype) real = (CONCAT(function, _hooktype))";
        echo "dlsym(libGLdlsymHandle, #function); \\";
        echo -en "\treturn real(";
            for I in `seq 1 $N`; do echo -n "p$I"; if [ $I -ne $N ]; then echo -n ", "; fi; done;
        echo "); }";
    }

  for I in `seq 0 5`; do EGLHook $I; echo; done

*/

#define EGL_PASSTHRU_0(ret, function)                                       \
  typedef ret (*CONCAT(function, _hooktype))();                             \
  extern "C" __attribute__((visibility("default"))) ret function()          \
  {                                                                         \
    CONCAT(function, _hooktype)                                             \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function); \
    return real();                                                          \
  }

#define EGL_PASSTHRU_1(ret, function, t1, p1)                               \
  typedef ret (*CONCAT(function, _hooktype))(t1);                           \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1)     \
  {                                                                         \
    CONCAT(function, _hooktype)                                             \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function); \
    return real(p1);                                                        \
  }

#define EGL_PASSTHRU_2(ret, function, t1, p1, t2, p2)                          \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2);                          \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2) \
  {                                                                            \
    CONCAT(function, _hooktype)                                                \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);    \
    return real(p1, p2);                                                       \
  }

#define EGL_PASSTHRU_3(ret, function, t1, p1, t2, p2, t3, p3)                         \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3);                             \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                       \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);           \
    return real(p1, p2, p3);                                                          \
  }

#define EGL_PASSTHRU_4(ret, function, t1, p1, t2, p2, t3, p3, t4, p4)                        \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4);                                \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                              \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);                  \
    return real(p1, p2, p3, p4);                                                             \
  }

#define EGL_PASSTHRU_5(ret, function, t1, p1, t2, p2, t3, p3, t4, p4, t5, p5)                       \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4, t5);                                   \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                                     \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);                         \
    return real(p1, p2, p3, p4, p5);                                                                \
  }

/* EGL 1.0 */

EGL_PASSTHRU_5(EGLBoolean, eglChooseConfig, EGLDisplay, dpy, const EGLint *, attrib_list,
               EGLConfig *, configs, EGLint, config_size, EGLint *, num_config)
EGL_PASSTHRU_3(EGLBoolean, eglCopyBuffers, EGLDisplay, dpy, EGLSurface, surface,
               EGLNativePixmapType, target)
EGL_PASSTHRU_3(EGLSurface, eglCreatePbufferSurface, EGLDisplay, dpy, EGLConfig, config,
               const EGLint *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreatePixmapSurface, EGLDisplay, dpy, EGLConfig, config,
               EGLNativePixmapType, pixmap, const EGLint *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreateWindowSurface, EGLDisplay, dpy, EGLConfig, config,
               EGLNativeWindowType, win, const EGLint *, attrib_list)
EGL_PASSTHRU_2(EGLBoolean, eglDestroySurface, EGLDisplay, dpy, EGLSurface, surface)
EGL_PASSTHRU_4(EGLBoolean, eglGetConfigAttrib, EGLDisplay, dpy, EGLConfig, config, EGLint,
               attribute, EGLint *, value)
EGL_PASSTHRU_4(EGLBoolean, eglGetConfigs, EGLDisplay, dpy, EGLConfig *, configs, EGLint,
               config_size, EGLint *, num_config)
EGL_PASSTHRU_0(EGLDisplay, eglGetCurrentDisplay)
EGL_PASSTHRU_1(EGLSurface, eglGetCurrentSurface, EGLint, readdraw)
EGL_PASSTHRU_0(EGLint, eglGetError)
EGL_PASSTHRU_3(EGLBoolean, eglInitialize, EGLDisplay, dpy, EGLint *, major, EGLint *, minor)
EGL_PASSTHRU_4(EGLBoolean, eglQueryContext, EGLDisplay, dpy, EGLContext, ctx, EGLint, attribute,
               EGLint *, value)
EGL_PASSTHRU_2(const char *, eglQueryString, EGLDisplay, dpy, EGLint, name)
EGL_PASSTHRU_4(EGLBoolean, eglQuerySurface, EGLDisplay, dpy, EGLSurface, surface, EGLint, attribute,
               EGLint *, value)
EGL_PASSTHRU_1(EGLBoolean, eglTerminate, EGLDisplay, dpy)
EGL_PASSTHRU_0(EGLBoolean, eglWaitGL)
EGL_PASSTHRU_1(EGLBoolean, eglWaitNative, EGLint, engine)

/* EGL 1.1 */

EGL_PASSTHRU_3(EGLBoolean, eglBindTexImage, EGLDisplay, dpy, EGLSurface, surface, EGLint, buffer)
EGL_PASSTHRU_3(EGLBoolean, eglReleaseTexImage, EGLDisplay, dpy, EGLSurface, surface, EGLint, buffer)
EGL_PASSTHRU_4(EGLBoolean, eglSurfaceAttrib, EGLDisplay, dpy, EGLSurface, surface, EGLint,
               attribute, EGLint, value)
EGL_PASSTHRU_2(EGLBoolean, glSwapInterval, EGLDisplay, dpy, EGLint, interval)

/* EGL 1.2 */

EGL_PASSTHRU_1(EGLBoolean, eglBindAPI, EGLenum, api)
EGL_PASSTHRU_0(EGLenum, eglQueryAPI)
EGL_PASSTHRU_5(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, dpy, EGLenum, buftype,
               EGLClientBuffer, buffer, EGLConfig, config, const EGLint *, attrib_list)
EGL_PASSTHRU_0(EGLBoolean, eglReleaseThread)
EGL_PASSTHRU_0(EGLBoolean, eglWaitClient)

/* EGL 1.4 */
EGL_PASSTHRU_0(EGLContext, eglGetCurrentContext)
<commit_msg>Fix typo on eglSwapInterval passthru.<commit_after>/******************************************************************************
 * The MIT License (MIT)
 *
 * Copyright (c) 2017 Baldur Karlsson
 *
 * 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 <dlfcn.h>
#include <stdio.h>
#include "driver/gl/gl_common.h"

// we need to export the whole of the EGL API, since we will have redirected any dlopen()
// for libEGL.so to ourselves, and dlsym() for any of these entry points must return a valid
// function. We don't need to intercept them, so we just pass it along

extern void *libGLdlsymHandle;

/*
  in bash:

    function EGLHook()
    {
        N=$1;
        echo -n "#define EGL_PASSTHRU_$N(ret, function";
            for I in `seq 1 $N`; do echo -n ", t$I, p$I"; done;
        echo ") \\";

        echo -en "\ttypedef ret (*CONCAT(function, _hooktype)) (";
            for I in `seq 1 $N`; do echo -n "t$I"; if [ $I -ne $N ]; then echo -n ", "; fi; done;
        echo "); \\";

        echo -e "\textern \"C\" __attribute__ ((visibility (\"default\"))) \\";

        echo -en "\tret function(";
            for I in `seq 1 $N`; do echo -n "t$I p$I"; if [ $I -ne $N ]; then echo -n ", "; fi;
  done;
        echo ") \\";

        echo -en "\t{ CONCAT(function, _hooktype) real = (CONCAT(function, _hooktype))";
        echo "dlsym(libGLdlsymHandle, #function); \\";
        echo -en "\treturn real(";
            for I in `seq 1 $N`; do echo -n "p$I"; if [ $I -ne $N ]; then echo -n ", "; fi; done;
        echo "); }";
    }

  for I in `seq 0 5`; do EGLHook $I; echo; done

*/

#define EGL_PASSTHRU_0(ret, function)                                       \
  typedef ret (*CONCAT(function, _hooktype))();                             \
  extern "C" __attribute__((visibility("default"))) ret function()          \
  {                                                                         \
    CONCAT(function, _hooktype)                                             \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function); \
    return real();                                                          \
  }

#define EGL_PASSTHRU_1(ret, function, t1, p1)                               \
  typedef ret (*CONCAT(function, _hooktype))(t1);                           \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1)     \
  {                                                                         \
    CONCAT(function, _hooktype)                                             \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function); \
    return real(p1);                                                        \
  }

#define EGL_PASSTHRU_2(ret, function, t1, p1, t2, p2)                          \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2);                          \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2) \
  {                                                                            \
    CONCAT(function, _hooktype)                                                \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);    \
    return real(p1, p2);                                                       \
  }

#define EGL_PASSTHRU_3(ret, function, t1, p1, t2, p2, t3, p3)                         \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3);                             \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                       \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);           \
    return real(p1, p2, p3);                                                          \
  }

#define EGL_PASSTHRU_4(ret, function, t1, p1, t2, p2, t3, p3, t4, p4)                        \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4);                                \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                              \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);                  \
    return real(p1, p2, p3, p4);                                                             \
  }

#define EGL_PASSTHRU_5(ret, function, t1, p1, t2, p2, t3, p3, t4, p4, t5, p5)                       \
  typedef ret (*CONCAT(function, _hooktype))(t1, t2, t3, t4, t5);                                   \
  extern "C" __attribute__((visibility("default"))) ret function(t1 p1, t2 p2, t3 p3, t4 p4, t5 p5) \
  {                                                                                \
    CONCAT(function, _hooktype)                                                                     \
    real = (CONCAT(function, _hooktype))dlsym(libGLdlsymHandle, #function);                         \
    return real(p1, p2, p3, p4, p5);                                                                \
  }

/* EGL 1.0 */

EGL_PASSTHRU_5(EGLBoolean, eglChooseConfig, EGLDisplay, dpy, const EGLint *, attrib_list,
               EGLConfig *, configs, EGLint, config_size, EGLint *, num_config)
EGL_PASSTHRU_3(EGLBoolean, eglCopyBuffers, EGLDisplay, dpy, EGLSurface, surface,
               EGLNativePixmapType, target)
EGL_PASSTHRU_3(EGLSurface, eglCreatePbufferSurface, EGLDisplay, dpy, EGLConfig, config,
               const EGLint *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreatePixmapSurface, EGLDisplay, dpy, EGLConfig, config,
               EGLNativePixmapType, pixmap, const EGLint *, attrib_list)
EGL_PASSTHRU_4(EGLSurface, eglCreateWindowSurface, EGLDisplay, dpy, EGLConfig, config,
               EGLNativeWindowType, win, const EGLint *, attrib_list)
EGL_PASSTHRU_2(EGLBoolean, eglDestroySurface, EGLDisplay, dpy, EGLSurface, surface)
EGL_PASSTHRU_4(EGLBoolean, eglGetConfigAttrib, EGLDisplay, dpy, EGLConfig, config, EGLint,
               attribute, EGLint *, value)
EGL_PASSTHRU_4(EGLBoolean, eglGetConfigs, EGLDisplay, dpy, EGLConfig *, configs, EGLint,
               config_size, EGLint *, num_config)
EGL_PASSTHRU_0(EGLDisplay, eglGetCurrentDisplay)
EGL_PASSTHRU_1(EGLSurface, eglGetCurrentSurface, EGLint, readdraw)
EGL_PASSTHRU_0(EGLint, eglGetError)
EGL_PASSTHRU_3(EGLBoolean, eglInitialize, EGLDisplay, dpy, EGLint *, major, EGLint *, minor)
EGL_PASSTHRU_4(EGLBoolean, eglQueryContext, EGLDisplay, dpy, EGLContext, ctx, EGLint, attribute,
               EGLint *, value)
EGL_PASSTHRU_2(const char *, eglQueryString, EGLDisplay, dpy, EGLint, name)
EGL_PASSTHRU_4(EGLBoolean, eglQuerySurface, EGLDisplay, dpy, EGLSurface, surface, EGLint, attribute,
               EGLint *, value)
EGL_PASSTHRU_1(EGLBoolean, eglTerminate, EGLDisplay, dpy)
EGL_PASSTHRU_0(EGLBoolean, eglWaitGL)
EGL_PASSTHRU_1(EGLBoolean, eglWaitNative, EGLint, engine)

/* EGL 1.1 */

EGL_PASSTHRU_3(EGLBoolean, eglBindTexImage, EGLDisplay, dpy, EGLSurface, surface, EGLint, buffer)
EGL_PASSTHRU_3(EGLBoolean, eglReleaseTexImage, EGLDisplay, dpy, EGLSurface, surface, EGLint, buffer)
EGL_PASSTHRU_4(EGLBoolean, eglSurfaceAttrib, EGLDisplay, dpy, EGLSurface, surface, EGLint,
               attribute, EGLint, value)
EGL_PASSTHRU_2(EGLBoolean, eglSwapInterval, EGLDisplay, dpy, EGLint, interval)

/* EGL 1.2 */

EGL_PASSTHRU_1(EGLBoolean, eglBindAPI, EGLenum, api)
EGL_PASSTHRU_0(EGLenum, eglQueryAPI)
EGL_PASSTHRU_5(EGLSurface, eglCreatePbufferFromClientBuffer, EGLDisplay, dpy, EGLenum, buftype,
               EGLClientBuffer, buffer, EGLConfig, config, const EGLint *, attrib_list)
EGL_PASSTHRU_0(EGLBoolean, eglReleaseThread)
EGL_PASSTHRU_0(EGLBoolean, eglWaitClient)

/* EGL 1.4 */
EGL_PASSTHRU_0(EGLContext, eglGetCurrentContext)
<|endoftext|>


<commit_before>/**
 * @file Cosa/PinChangeInterrupt.cpp
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2012-2013, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 * This file is part of the Arduino Che Cosa project.
 */

#include "Cosa/PinChangeInterrupt.hh"

PinChangeInterrupt* PinChangeInterrupt::pin[Board::PIN_MAX] = { 0 };
uint8_t PinChangeInterrupt::state[Board::PCINT_MAX] = { 0 };

void
PinChangeInterrupt::enable()
{
  synchronized {
    *PCIMR() |= m_mask;
#if !defined(__ARDUINO_MEGA__)
    pin[m_pin] = this;
#else
    uint8_t ix = m_pin - (m_pin < 24 ? 24 : 48);
    pin[ix] = this;
#endif
  }
}

void
PinChangeInterrupt::disable()
{
  synchronized {
    *PCIMR() &= ~m_mask;
#if !defined(__ARDUINO_MEGA__)
    pin[m_pin] = 0;
#else
    uint8_t ix = m_pin - (m_pin < 24 ? 24 : 48);
    pin[ix] = 0;
#endif
  }
}

void
PinChangeInterrupt::begin()
{
#if defined(__ARDUINO_MEGA__)
  state[0] = *Pin::PIN(16);
  state[1] = 0;
  state[2] = *Pin::PIN(64);
#else
  for (uint8_t i = 0; i < Board::PCINT_MAX; i++)
    state[i] = *Pin::PIN(i << 3);
#endif
  synchronized {
#if defined(__ARDUINO_TINYX5__)
    bit_set(GIMSK, PCIE);
#elif defined(__ARDUINO_TINYX4__) || defined(__ARDUINO_TINYX61__)
    bit_mask_set(GIMSK, _BV(PCIE1) | _BV(PCIE0));
#elif defined(__ARDUINO_STANDARD_USB__)
    bit_mask_set(PCICR, _BV(PCIE0));
#elif defined(__ARDUINO_MIGHTY__)
    bit_mask_set(PCICR, _BV(PCIE3) | _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#else
    bit_mask_set(PCICR, _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#endif
  }
}

void
PinChangeInterrupt::end()
{
  synchronized {
#if defined(__ARDUINO_TINYX5__)
    bit_clear(GIMSK, PCIE);
#elif defined(__ARDUINO_TINYX4__) || defined(__ARDUINO_TINYX61__)
    bit_mask_clear(GIMSK, _BV(PCIE1) | _BV(PCIE0));
#elif defined(__ARDUINO_STANDARD_USB__)
    bit_mask_clear(PCICR, _BV(PCIE0));
#elif defined(__ARDUINO_MIGHTY__)
    bit_mask_clear(PCICR, _BV(PCIE3) | _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#else
    bit_mask_clear(PCICR, _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#endif
  }
}

#if defined(__ARDUINO_TINYX5__)

ISR(PCINT0_vect)
{
  uint8_t mask = PCMSK0;
  uint8_t state = *Pin::PIN(0);
  uint8_t changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_TINYX4__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

#elif defined(__ARDUINO_TINYX61__)

ISR(PCINT0_vect)
{
  uint8_t changed;
  uint8_t state;
  uint8_t mask;
  if (GIFR & _BV(INTF0)) {
    mask = PCMSK0;
    state = *Pin::PIN(0);
    changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  }
  else {
    mask = PCMSK1;
    state = *Pin::PIN(8);
    changed = (state ^ PinChangeInterrupt::state[1]) & mask;
  }
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_STANDARD__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3) - (ix < 2 ? 0 : 2);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK2);
}

#elif defined(__ARDUINO_STANDARD_USB__)

ISR(PCINT0_vect)
{
  uint8_t mask = PCMSK0;
  uint8_t state = *Pin::PIN(0);
  uint8_t changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_MEGA__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t rx = (ix == 0 ? 16 : 64);
  uint8_t state = *Pin::PIN(rx);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK2);
}

#elif defined(__ARDUINO_MIGHTY__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK2);
}

ISR(PCINT3_vect)
{
  PinChangeInterrupt::on_interrupt(3, PCMSK3);
}
#endif
<commit_msg>__ARDUINO_MEGA__ PinChangeInterrupt broken<commit_after>/**
 * @file Cosa/PinChangeInterrupt.cpp
 * @version 1.0
 *
 * @section License
 * Copyright (C) 2012-2013, Mikael Patel
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General
 * Public License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
 * Boston, MA  02111-1307  USA
 *
 * This file is part of the Arduino Che Cosa project.
 */

#include "Cosa/PinChangeInterrupt.hh"

PinChangeInterrupt* PinChangeInterrupt::pin[Board::PIN_MAX] = { 0 };
uint8_t PinChangeInterrupt::state[Board::PCINT_MAX] = { 0 };

void
PinChangeInterrupt::enable()
{
  synchronized {
    *PCIMR() |= m_mask;
#if !defined(__ARDUINO_MEGA__)
    pin[m_pin] = this;
#else
    uint8_t ix = m_pin - (m_pin < 24 ? 16 : 48);
    pin[ix] = this;
#endif
  }
}

void
PinChangeInterrupt::disable()
{
  synchronized {
    *PCIMR() &= ~m_mask;
#if !defined(__ARDUINO_MEGA__)
    pin[m_pin] = 0;
#else
    uint8_t ix = m_pin - (m_pin < 24 ? 16 : 48);
    pin[ix] = 0;
#endif
  }
}

void
PinChangeInterrupt::begin()
{
#if defined(__ARDUINO_MEGA__)
  state[0] = *Pin::PIN(16);
  state[1] = 0;
  state[2] = *Pin::PIN(64);
#else
  for (uint8_t i = 0; i < Board::PCINT_MAX; i++)
    state[i] = *Pin::PIN(i << 3);
#endif
  synchronized {
#if defined(__ARDUINO_TINYX5__)
    bit_set(GIMSK, PCIE);
#elif defined(__ARDUINO_TINYX4__) || defined(__ARDUINO_TINYX61__)
    bit_mask_set(GIMSK, _BV(PCIE1) | _BV(PCIE0));
#elif defined(__ARDUINO_STANDARD_USB__)
    bit_mask_set(PCICR, _BV(PCIE0));
#elif defined(__ARDUINO_MIGHTY__)
    bit_mask_set(PCICR, _BV(PCIE3) | _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#else
    bit_mask_set(PCICR, _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#endif
  }
}

void
PinChangeInterrupt::end()
{
  synchronized {
#if defined(__ARDUINO_TINYX5__)
    bit_clear(GIMSK, PCIE);
#elif defined(__ARDUINO_TINYX4__) || defined(__ARDUINO_TINYX61__)
    bit_mask_clear(GIMSK, _BV(PCIE1) | _BV(PCIE0));
#elif defined(__ARDUINO_STANDARD_USB__)
    bit_mask_clear(PCICR, _BV(PCIE0));
#elif defined(__ARDUINO_MIGHTY__)
    bit_mask_clear(PCICR, _BV(PCIE3) | _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#else
    bit_mask_clear(PCICR, _BV(PCIE2) | _BV(PCIE1) | _BV(PCIE0));
#endif
  }
}

#if defined(__ARDUINO_TINYX5__)

ISR(PCINT0_vect)
{
  uint8_t mask = PCMSK0;
  uint8_t state = *Pin::PIN(0);
  uint8_t changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_TINYX4__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

#elif defined(__ARDUINO_TINYX61__)

ISR(PCINT0_vect)
{
  uint8_t changed;
  uint8_t state;
  uint8_t mask;
  if (GIFR & _BV(INTF0)) {
    mask = PCMSK0;
    state = *Pin::PIN(0);
    changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  }
  else {
    mask = PCMSK1;
    state = *Pin::PIN(8);
    changed = (state ^ PinChangeInterrupt::state[1]) & mask;
  }
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_STANDARD__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3) - (ix < 2 ? 0 : 2);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK2);
}

#elif defined(__ARDUINO_STANDARD_USB__)

ISR(PCINT0_vect)
{
  uint8_t mask = PCMSK0;
  uint8_t state = *Pin::PIN(0);
  uint8_t changed = (state ^ PinChangeInterrupt::state[0]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i] != NULL)) {
      PinChangeInterrupt::pin[i]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[0] = state;
}

#elif defined(__ARDUINO_MEGA__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t rx = (ix == 0 ? 16 : 64);
  uint8_t state = *Pin::PIN(rx);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK2);
}

#elif defined(__ARDUINO_MIGHTY__)

void
PinChangeInterrupt::on_interrupt(uint8_t ix, uint8_t mask)
{
  uint8_t px = (ix << 3);
  uint8_t state = *Pin::PIN(px);
  uint8_t changed = (state ^ PinChangeInterrupt::state[ix]) & mask;
  for (uint8_t i = 0; i < CHARBITS; i++) {
    if ((changed & 1) && (PinChangeInterrupt::pin[i + px] != NULL)) {
      PinChangeInterrupt::pin[i + px]->on_interrupt();
    }
    changed >>= 1;
  }
  PinChangeInterrupt::state[ix] = state;
}

ISR(PCINT0_vect)
{
  PinChangeInterrupt::on_interrupt(0, PCMSK0);
}

ISR(PCINT1_vect)
{
  PinChangeInterrupt::on_interrupt(1, PCMSK1);
}

ISR(PCINT2_vect)
{
  PinChangeInterrupt::on_interrupt(2, PCMSK2);
}

ISR(PCINT3_vect)
{
  PinChangeInterrupt::on_interrupt(3, PCMSK3);
}
#endif
<|endoftext|>