<commit_before>/*
Copyright (c) 2017, Vadim Malyshev, lboss75@gmail.com
All rights reserved
*/

#include "stdafx.h"
#include "private/udp_transport.h"
#include "messages/dht_route_messages.h"
#include "private/dht_session.h"
#include "logger.h"
#include "dht_network_client.h"
#include "dht_network_client_p.h"

vds::dht::network::udp_transport::udp_transport(){
}

vds::dht::network::udp_transport::~udp_transport() {
}

void vds::dht::network::udp_transport::start(
  const service_provider * sp,
  const std::shared_ptr<certificate> & node_cert,
  const std::shared_ptr<asymmetric_private_key> & node_key,
  uint16_t port) {
  this->send_thread_ = std::make_shared<thread_apartment>(sp);
  this->sp_ = sp;
  this->this_node_id_ = node_cert->fingerprint(hash::sha256());
  this->node_cert_ = node_cert;
  this->node_key_ = node_key;

  try {
    auto [reader, writer] = this->server_.start(sp, network_address::any_ip6(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }
  catch (...) {
    auto[reader, writer] = this->server_.start(sp, network_address::any_ip4(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }

      this->continue_read().detach();
}

void vds::dht::network::udp_transport::stop() {
  this->server_.stop();
}

vds::async_task<void>
vds::dht::network::udp_transport::write_async( const udp_datagram& datagram) {
  auto result = std::make_shared<vds::async_result<void>>();
  this->send_thread_->schedule([result, this, datagram]() {
    try{
      this->writer_->write_async(datagram).get();
    }
    catch (...){
      result->set_exception(std::current_exception());
      return;
    }
    result->set_value();
  });

  return result->get_future();

  //  std::unique_lock<std::debug_mutex> lock(this->write_mutex_);
  //  while(this->write_in_progress_) {
  //    this->write_cond_.wait(*reinterpret_cast<std::unique_lock<std::mutex> *>(&lock));
  //  }
  //  this->write_in_progress_ = true;
  //#ifdef _DEBUG
  //#ifndef _WIN32
  //  this->owner_id_ = syscall(SYS_gettid);
  //#else
  //  this->owner_id_ = GetCurrentThreadId();
  //#endif
  //#endif//_DEBUG
  //co_await this->writer_->write_async(sp, datagram);
}

vds::async_task<void> vds::dht::network::udp_transport::try_handshake(
                                                                  const std::string& address) {

  resizable_data_buffer out_message;
  out_message.add((uint8_t)protocol_message_type_t::HandshakeBroadcast);
  out_message.add(MAGIC_LABEL >> 24);
  out_message.add(MAGIC_LABEL >> 16);
  out_message.add(MAGIC_LABEL >> 8);
  out_message.add(MAGIC_LABEL);
  out_message.add(PROTOCOL_VERSION);

  binary_serializer bs;
  bs << this->node_cert_->der();

  out_message += bs.move_data();

  co_await this->write_async(udp_datagram(
    network_address::parse(this->server_.address().family(), address),
    out_message.move_data(),
    false));
}

vds::async_task<void> vds::dht::network::udp_transport::on_timer() {
  std::list<std::shared_ptr<dht_session>> sessions;

  this->sessions_mutex_.lock_shared();
  for(auto & p : this->sessions_) {
    if (p.second.session_) {
      sessions.push_back(p.second.session_);
    }
  }
  this->sessions_mutex_.unlock_shared();

  for(auto & s : sessions) {
    co_await s->on_timer(this->shared_from_this());
  }
}

void vds::dht::network::udp_transport::get_session_statistics(session_statistic& session_statistic) {
  session_statistic.output_size_ = this->send_thread_->size();

  std::shared_lock<std::shared_mutex> lock(this->sessions_mutex_);
  for (const auto& p : this->sessions_) {
    const auto & session = p.second;
    if (session.session_) {
      session_statistic.items_.push_back(session.session_->get_statistic());
    }
  }
}

vds::async_task<void> vds::dht::network::udp_transport::continue_read(
  ) {
  for (;;) {
    udp_datagram datagram;
    try {
      datagram = co_await this->reader_->read_async();
    }
    catch(const std::system_error & ex) {
      continue;
    }

    if (this->sp_->get_shutdown_event().is_shuting_down()) {
      co_return;
    }

    this->sessions_mutex_.lock();
    auto & session_info = this->sessions_[datagram.address()];
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)) {
        session_info.blocked_ = false;
      }
      else {
        session_info.session_mutex_.unlock();
        if (*datagram.data() != (uint8_t)protocol_message_type_t::Failed
          && *datagram.data() != (uint8_t)protocol_message_type_t::Handshake
          && *datagram.data() != (uint8_t)protocol_message_type_t::HandshakeBroadcast) {
          uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
          try {
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
          catch (...) {
          }
        }
        continue;
      }
    }

    switch ((protocol_message_type_t)datagram.data()[0]) {
    case protocol_message_type_t::HandshakeBroadcast:
    case protocol_message_type_t::Handshake: {

      if (session_info.session_) {
        session_info.session_mutex_.unlock();
        continue;
      }

      if (
        (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]
        && PROTOCOL_VERSION == datagram.data()[5]) {
        binary_deserializer bd(datagram.data() + 6, datagram.data_size() - 6);
        const_data_buffer partner_node_cert_der;
        bd >> partner_node_cert_der;
        auto partner_node_cert = certificate::parse_der(partner_node_cert_der);
        auto partner_node_id = partner_node_cert.fingerprint(hash::sha256());
        if (partner_node_id == this->this_node_id_) {
          session_info.session_mutex_.unlock();
          break;
        }

        if (!session_info.session_key_ || (std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(10)) {
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_key_.resize(32);
          crypto_service::rand_bytes(session_info.session_key_.data(), session_info.session_key_.size());
        }

        session_info.session_ = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_node_id,
          session_info.session_key_);

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session_info.session_, 0);

        resizable_data_buffer out_message;
        out_message.add(static_cast<uint8_t>(protocol_message_type_t::Welcome));
        out_message.add(MAGIC_LABEL >> 24);
        out_message.add(MAGIC_LABEL >> 16);
        out_message.add(MAGIC_LABEL >> 8);
        out_message.add(MAGIC_LABEL);

        binary_serializer bs;
        bs
          << this->node_cert_->der()
          << partner_node_cert.public_key().encrypt(session_info.session_key_);

        session_info.session_mutex_.unlock();

        out_message += bs.move_data();
        co_await this->write_async(udp_datagram(datagram.address(), out_message.move_data(), false));
        co_await (*this->sp_->get<client>())->send_neighbors(
          message_create<messages::dht_find_node_response>(
            std::list<messages::dht_find_node_response::target_node>({
          messages::dht_find_node_response::target_node(partner_node_id, datagram.address().to_string(), 0) })));

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_node_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }
      break;
    }
    case protocol_message_type_t::Welcome: {
      if (datagram.data_size() > 5
        && (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]) {

        const_data_buffer cert_buffer;
        const_data_buffer key_buffer;
        binary_deserializer bd(datagram.data() + 5, datagram.data_size() - 5);
        bd
          >> cert_buffer
          >> key_buffer;

        auto cert = certificate::parse_der(cert_buffer);
        auto key = this->node_key_->decrypt(key_buffer);

        auto partner_id = cert.fingerprint(hash::sha256());

        //TODO: validate cert
        auto session = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_id,
          key);

        session_info.session_ = session;
        session_info.session_mutex_.unlock();

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session, 0);

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }

      break;
    }
    case protocol_message_type_t::Failed: {
      session_info.blocked_ = true;
      (*this->sp_->get<client>())->remove_session(session_info.session_);
      session_info.session_.reset();
      session_info.update_time_ = std::chrono::steady_clock::now();
      session_info.session_mutex_.unlock();
      break;
    }
    default: {
      if (session_info.session_) {
        try {
          auto session = session_info.session_;
          session_info.session_mutex_.unlock();

          bool failed = false;
          try {
            co_await session->process_datagram(
              this->shared_from_this(),
              const_data_buffer(datagram.data(), datagram.data_size()));
          }
          catch (const std::exception & ex) {
            logger::get(this->sp_)->debug(ThisModule, "%s at process message from %s",
              ex.what(),
              datagram.address().to_string().c_str());
            failed = true;
          }

          if(failed) {
            session_info.session_mutex_.lock();
            session_info.blocked_ = true;
            (*this->sp_->get<client>())->remove_session(session_info.session_);
            session_info.session_.reset();
            session_info.update_time_ = std::chrono::steady_clock::now();
            session_info.session_mutex_.unlock();

            uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
        }
        catch (...) {
          session_info.session_mutex_.lock();
          session_info.blocked_ = true;
          (*this->sp_->get<client>())->remove_session(session_info.session_);
          session_info.session_.reset();
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_mutex_.unlock();
        }
      }
      else {
        session_info.blocked_ = true;
        (*this->sp_->get<client>())->remove_session(session_info.session_);
        session_info.session_.reset();
        session_info.update_time_ = std::chrono::steady_clock::now();
        session_info.session_mutex_.unlock();

        uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
        try {
          co_await this->write_async(udp_datagram(datagram.address(),
            const_data_buffer(out_message, sizeof(out_message))));
        }
        catch (...) {
        }
      }
      break;
    }
    }
  }
}

<commit_msg>change UDP MTU size<commit_after>/*
Copyright (c) 2017, Vadim Malyshev, lboss75@gmail.com
All rights reserved
*/

#include "stdafx.h"
#include "private/udp_transport.h"
#include "messages/dht_route_messages.h"
#include "private/dht_session.h"
#include "logger.h"
#include "dht_network_client.h"
#include "dht_network_client_p.h"

vds::dht::network::udp_transport::udp_transport(){
}

vds::dht::network::udp_transport::~udp_transport() {
}

void vds::dht::network::udp_transport::start(
  const service_provider * sp,
  const std::shared_ptr<certificate> & node_cert,
  const std::shared_ptr<asymmetric_private_key> & node_key,
  uint16_t port) {
  this->send_thread_ = std::make_shared<thread_apartment>(sp);
  this->sp_ = sp;
  this->this_node_id_ = node_cert->fingerprint(hash::sha256());
  this->node_cert_ = node_cert;
  this->node_key_ = node_key;

  try {
    auto [reader, writer] = this->server_.start(sp, network_address::any_ip6(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }
  catch (...) {
    auto[reader, writer] = this->server_.start(sp, network_address::any_ip4(port));
    this->reader_ = reader;
    this->writer_ = writer;
  }

      this->continue_read().detach();
}

void vds::dht::network::udp_transport::stop() {
  this->server_.stop();
}

vds::async_task<void>
vds::dht::network::udp_transport::write_async( const udp_datagram& datagram) {
  auto result = std::make_shared<vds::async_result<void>>();
  this->send_thread_->schedule([result, this, datagram]() {
    try{
      this->writer_->write_async(datagram).get();
    }
    catch (...){
      result->set_exception(std::current_exception());
      return;
    }
    result->set_value();
  });

  return result->get_future();

  //  std::unique_lock<std::debug_mutex> lock(this->write_mutex_);
  //  while(this->write_in_progress_) {
  //    this->write_cond_.wait(*reinterpret_cast<std::unique_lock<std::mutex> *>(&lock));
  //  }
  //  this->write_in_progress_ = true;
  //#ifdef _DEBUG
  //#ifndef _WIN32
  //  this->owner_id_ = syscall(SYS_gettid);
  //#else
  //  this->owner_id_ = GetCurrentThreadId();
  //#endif
  //#endif//_DEBUG
  //co_await this->writer_->write_async(sp, datagram);
}

vds::async_task<void> vds::dht::network::udp_transport::try_handshake(
                                                                  const std::string& address_str) {

  auto address = network_address::parse(this->server_.address().family(), address_str);

  this->sessions_mutex_.lock();
  auto p = this->sessions_.find(address);
  if (this->sessions_.end() == p) {
    this->sessions_mutex_.unlock();
  }
  else {
    auto & session_info = *p;
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)) {
        session_info.blocked_ = false;
      }
      else {
        co_return;
      }
    }
    else if(session_info.session_) {
      co_return;
    }
  }

  resizable_data_buffer out_message;
  out_message.add((uint8_t)protocol_message_type_t::HandshakeBroadcast);
  out_message.add(MAGIC_LABEL >> 24);
  out_message.add(MAGIC_LABEL >> 16);
  out_message.add(MAGIC_LABEL >> 8);
  out_message.add(MAGIC_LABEL);
  out_message.add(PROTOCOL_VERSION);

  binary_serializer bs;
  bs << this->node_cert_->der();

  out_message += bs.move_data();

  co_await this->write_async(udp_datagram(
    address,
    out_message.move_data(),
    false));
}

vds::async_task<void> vds::dht::network::udp_transport::on_timer() {
  std::list<std::shared_ptr<dht_session>> sessions;

  this->sessions_mutex_.lock_shared();
  for(auto & p : this->sessions_) {
    if (p.second.session_) {
      sessions.push_back(p.second.session_);
    }
  }
  this->sessions_mutex_.unlock_shared();

  for(auto & s : sessions) {
    co_await s->on_timer(this->shared_from_this());
  }
}

void vds::dht::network::udp_transport::get_session_statistics(session_statistic& session_statistic) {
  session_statistic.output_size_ = this->send_thread_->size();

  std::shared_lock<std::shared_mutex> lock(this->sessions_mutex_);
  for (const auto& p : this->sessions_) {
    const auto & session = p.second;
    if (session.session_) {
      session_statistic.items_.push_back(session.session_->get_statistic());
    }
  }
}

vds::async_task<void> vds::dht::network::udp_transport::continue_read(
  ) {
  for (;;) {
    udp_datagram datagram;
    try {
      datagram = co_await this->reader_->read_async();
    }
    catch(const std::system_error & ex) {
      continue;
    }

    if (this->sp_->get_shutdown_event().is_shuting_down()) {
      co_return;
    }

    this->sessions_mutex_.lock();
    auto & session_info = this->sessions_[datagram.address()];
    this->sessions_mutex_.unlock();

    session_info.session_mutex_.lock();
    if (session_info.blocked_) {
      if ((std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(1)) {
        session_info.blocked_ = false;
      }
      else {
        session_info.session_mutex_.unlock();
        if (*datagram.data() != (uint8_t)protocol_message_type_t::Failed
          && *datagram.data() != (uint8_t)protocol_message_type_t::Handshake
          && *datagram.data() != (uint8_t)protocol_message_type_t::HandshakeBroadcast
          && *datagram.data() != (uint8_t)protocol_message_type_t::Welcome) {
          uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
          try {
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
          catch (...) {
          }
        }
        continue;
      }
    }

    switch ((protocol_message_type_t)datagram.data()[0]) {
    case protocol_message_type_t::HandshakeBroadcast:
    case protocol_message_type_t::Handshake: {

      if (session_info.session_) {
        session_info.session_mutex_.unlock();
        continue;
      }

      if (
        (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]
        && PROTOCOL_VERSION == datagram.data()[5]) {
        binary_deserializer bd(datagram.data() + 6, datagram.data_size() - 6);
        const_data_buffer partner_node_cert_der;
        bd >> partner_node_cert_der;
        auto partner_node_cert = certificate::parse_der(partner_node_cert_der);
        auto partner_node_id = partner_node_cert.fingerprint(hash::sha256());
        if (partner_node_id == this->this_node_id_) {
          session_info.session_mutex_.unlock();
          break;
        }

        if (!session_info.session_key_ || (std::chrono::steady_clock::now() - session_info.update_time_) > std::chrono::minutes(10)) {
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_key_.resize(32);
          crypto_service::rand_bytes(session_info.session_key_.data(), session_info.session_key_.size());
        }

        session_info.session_ = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_node_id,
          session_info.session_key_);

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session_info.session_, 0);

        resizable_data_buffer out_message;
        out_message.add(static_cast<uint8_t>(protocol_message_type_t::Welcome));
        out_message.add(MAGIC_LABEL >> 24);
        out_message.add(MAGIC_LABEL >> 16);
        out_message.add(MAGIC_LABEL >> 8);
        out_message.add(MAGIC_LABEL);

        binary_serializer bs;
        bs
          << this->node_cert_->der()
          << partner_node_cert.public_key().encrypt(session_info.session_key_);

        session_info.session_mutex_.unlock();

        out_message += bs.move_data();
        co_await this->write_async(udp_datagram(datagram.address(), out_message.move_data(), false));
        co_await (*this->sp_->get<client>())->send_neighbors(
          message_create<messages::dht_find_node_response>(
            std::list<messages::dht_find_node_response::target_node>({
          messages::dht_find_node_response::target_node(partner_node_id, datagram.address().to_string(), 0) })));

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_node_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }
      break;
    }
    case protocol_message_type_t::Welcome: {
      if (datagram.data_size() > 5
        && (uint8_t)(MAGIC_LABEL >> 24) == datagram.data()[1]
        && (uint8_t)(MAGIC_LABEL >> 16) == datagram.data()[2]
        && (uint8_t)(MAGIC_LABEL >> 8) == datagram.data()[3]
        && (uint8_t)(MAGIC_LABEL) == datagram.data()[4]) {

        const_data_buffer cert_buffer;
        const_data_buffer key_buffer;
        binary_deserializer bd(datagram.data() + 5, datagram.data_size() - 5);
        bd
          >> cert_buffer
          >> key_buffer;

        auto cert = certificate::parse_der(cert_buffer);
        auto key = this->node_key_->decrypt(key_buffer);

        auto partner_id = cert.fingerprint(hash::sha256());

        //TODO: validate cert
        auto session = std::make_shared<dht_session>(
          this->sp_,
          datagram.address(),
          this->this_node_id_,
          partner_id,
          key);

        session_info.session_ = session;
        session_info.session_mutex_.unlock();

        this->sp_->get<logger>()->debug(ThisModule, "Add session %s", datagram.address().to_string().c_str());
        (*this->sp_->get<client>())->add_session(session, 0);

        co_await this->sp_->get<imessage_map>()->on_new_session(
          partner_id);
      }
      else {
        session_info.session_mutex_.unlock();
        throw std::runtime_error("Invalid protocol");
      }

      break;
    }
    case protocol_message_type_t::Failed: {
      session_info.blocked_ = true;
      (*this->sp_->get<client>())->remove_session(session_info.session_);
      session_info.session_.reset();
      session_info.update_time_ = std::chrono::steady_clock::now();
      session_info.session_mutex_.unlock();
      break;
    }
    default: {
      if (session_info.session_) {
        try {
          auto session = session_info.session_;
          session_info.session_mutex_.unlock();

          bool failed = false;
          try {
            co_await session->process_datagram(
              this->shared_from_this(),
              const_data_buffer(datagram.data(), datagram.data_size()));
          }
          catch (const std::exception & ex) {
            logger::get(this->sp_)->debug(ThisModule, "%s at process message from %s",
              ex.what(),
              datagram.address().to_string().c_str());
            failed = true;
          }

          if(failed) {
            session_info.session_mutex_.lock();
            session_info.blocked_ = true;
            (*this->sp_->get<client>())->remove_session(session_info.session_);
            session_info.session_.reset();
            session_info.update_time_ = std::chrono::steady_clock::now();
            session_info.session_mutex_.unlock();

            uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
            co_await this->write_async(udp_datagram(datagram.address(),
              const_data_buffer(out_message, sizeof(out_message))));
          }
        }
        catch (...) {
          session_info.session_mutex_.lock();
          session_info.blocked_ = true;
          (*this->sp_->get<client>())->remove_session(session_info.session_);
          session_info.session_.reset();
          session_info.update_time_ = std::chrono::steady_clock::now();
          session_info.session_mutex_.unlock();
        }
      }
      else {
        session_info.blocked_ = true;
        (*this->sp_->get<client>())->remove_session(session_info.session_);
        session_info.session_.reset();
        session_info.update_time_ = std::chrono::steady_clock::now();
        session_info.session_mutex_.unlock();

        uint8_t out_message[] = { (uint8_t)protocol_message_type_t::Failed };
        try {
          co_await this->write_async(udp_datagram(datagram.address(),
            const_data_buffer(out_message, sizeof(out_message))));
        }
        catch (...) {
        }
      }
      break;
    }
    }
  }
}

<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#if defined(WNT)
#include <windows.h>
#endif

#include <osl/thread.h>
#include <osl/file.hxx>
#include <tools/debug.hxx>
#include <tools/urlobj.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <unotools/lingucfg.hxx>
#include <unotools/pathoptions.hxx>
#include <rtl/ustring.hxx>
#include <rtl/string.hxx>
#include <rtl/tencinfo.h>
#include <linguistic/misc.hxx>

#include <set>
#include <vector>
#include <string.h>

#include <lingutil.hxx>

#include <sal/macros.h>

using ::com::sun::star::lang::Locale;
using namespace ::com::sun::star;

#if defined(WNT)
OString Win_GetShortPathName( const OUString &rLongPathName )
{
    OString aRes;

    sal_Unicode aShortBuffer[1024] = {0};
    sal_Int32   nShortBufSize = SAL_N_ELEMENTS( aShortBuffer );

    // use the version of 'GetShortPathName' that can deal with Unicode...
    sal_Int32 nShortLen = GetShortPathNameW(
            reinterpret_cast<LPCWSTR>( rLongPathName.getStr() ),
            reinterpret_cast<LPWSTR>( aShortBuffer ),
            nShortBufSize );

    if (nShortLen < nShortBufSize) // conversion successful?
        aRes = OString( OU2ENC( OUString( aShortBuffer, nShortLen ), osl_getThreadTextEncoding()) );
    else
        OSL_FAIL( "Win_GetShortPathName: buffer to short" );

    return aRes;
}
#endif //defined(WNT)

//////////////////////////////////////////////////////////////////////

// build list of old style diuctionaries (not as extensions) to use.
// User installed dictionaries (the ones residing in the user paths)
// will get precedence over system installed ones for the same language.
std::vector< SvtLinguConfigDictionaryEntry > GetOldStyleDics( const char *pDicType )
{
    std::vector< SvtLinguConfigDictionaryEntry > aRes;

    if (!pDicType)
        return aRes;

    OUString aFormatName;
    String aDicExtension;
#ifdef SYSTEM_DICTS
    OUString aSystemDir;
    OUString aSystemPrefix;
    OUString aSystemSuffix;
#endif
    if (strcmp( pDicType, "DICT" ) == 0)
    {
        aFormatName     = "DICT_SPELL";
        aDicExtension   = ".dic";
#ifdef SYSTEM_DICTS
        aSystemDir      = DICT_SYSTEM_DIR;
        aSystemSuffix   = aDicExtension;
#endif
    }
    else if (strcmp( pDicType, "HYPH" ) == 0)
    {
        aFormatName     = "DICT_HYPH";
        aDicExtension   = ".dic";
#ifdef SYSTEM_DICTS
        aSystemDir      = HYPH_SYSTEM_DIR;
        aSystemPrefix   = "hyph_";
        aSystemSuffix   = aDicExtension;
#endif
    }
    else if (strcmp( pDicType, "THES" ) == 0)
    {
        aFormatName     = "DICT_THES";
        aDicExtension   = ".dat";
#ifdef SYSTEM_DICTS
        aSystemDir      = THES_SYSTEM_DIR;
        aSystemPrefix   = "th_";
        aSystemSuffix   = "_v2.dat";
#endif
    }

    if (aFormatName.isEmpty() || aDicExtension.Len() == 0)
        return aRes;

#ifdef SYSTEM_DICTS
    osl::Directory aSystemDicts(aSystemDir);
    if (aSystemDicts.open() == osl::FileBase::E_None)
    {
        // set of languages to remember the language where it is already
        // decided to make use of the dictionary.
        std::set< OUString > aDicLangInUse;

        osl::DirectoryItem aItem;
        osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
        while (aSystemDicts.getNextItem(aItem) == osl::FileBase::E_None)
        {
            aItem.getFileStatus(aFileStatus);
            OUString sPath = aFileStatus.getFileURL();
            if (sPath.lastIndexOf(aSystemSuffix) == sPath.getLength()-aSystemSuffix.getLength())
            {
                sal_Int32 nStartIndex = sPath.lastIndexOf(sal_Unicode('/')) + 1;
                if (!sPath.match(aSystemPrefix, nStartIndex))
                    continue;
                OUString sChunk = sPath.copy(nStartIndex + aSystemPrefix.getLength(),
                    sPath.getLength() - aSystemSuffix.getLength() -
                    nStartIndex - aSystemPrefix.getLength());
                if (sChunk.isEmpty())
                    continue;
                // We prefer (now) to use language tags.
                // Avoid feeding in the older LANG_REGION scheme to the BCP47
                // ctor as that triggers use of liblangtag and initializes its
                // database which we do not want during startup. Convert
                // instead.
                sChunk = sChunk.replace( '_', '-');
                LanguageTag aLangTag(sChunk, true);
                if (!aLangTag.isValidBcp47())
                    continue;

                // Thus we first get the language of the dictionary
                OUString aLocaleName(aLangTag.getBcp47());

                if (aDicLangInUse.insert(aLocaleName).second)
                {
                    // add the dictionary to the resulting vector
                    SvtLinguConfigDictionaryEntry aDicEntry;
                    aDicEntry.aLocations.realloc(1);
                    aDicEntry.aLocaleNames.realloc(1);
                    aDicEntry.aLocations[0] = sPath;
                    aDicEntry.aFormatName = aFormatName;
                    aDicEntry.aLocaleNames[0] = aLocaleName;
                    aRes.push_back( aDicEntry );
                }
            }
        }
    }
#endif

    return aRes;
}

void MergeNewStyleDicsAndOldStyleDics(
    std::list< SvtLinguConfigDictionaryEntry > &rNewStyleDics,
    const std::vector< SvtLinguConfigDictionaryEntry > &rOldStyleDics )
{
    // get list of languages supported by new style dictionaries
    std::set< LanguageType > aNewStyleLanguages;
    std::list< SvtLinguConfigDictionaryEntry >::const_iterator aIt;
    for (aIt = rNewStyleDics.begin() ;  aIt != rNewStyleDics.end();  ++aIt)
    {
        const uno::Sequence< OUString > aLocaleNames( aIt->aLocaleNames );
        sal_Int32 nLocaleNames = aLocaleNames.getLength();
        for (sal_Int32 k = 0;  k < nLocaleNames; ++k)
        {
            LanguageType nLang = LanguageTag::convertToLanguageTypeWithFallback( aLocaleNames[k] );
            aNewStyleLanguages.insert( nLang );
        }
    }

    // now check all old style dictionaries if they will add a not yet
    // added language. If so add them to the resulting vector
    std::vector< SvtLinguConfigDictionaryEntry >::const_iterator aIt2;
    for (aIt2 = rOldStyleDics.begin();  aIt2 != rOldStyleDics.end();  ++aIt2)
    {
        sal_Int32 nOldStyleDics = aIt2->aLocaleNames.getLength();

        // old style dics should only have one language listed...
        DBG_ASSERT( nOldStyleDics, "old style dictionary with more then one language found!");
        if (nOldStyleDics > 0)
        {
            LanguageType nLang = LanguageTag::convertToLanguageTypeWithFallback( aIt2->aLocaleNames[0] );

            if (nLang == LANGUAGE_DONTKNOW || linguistic::LinguIsUnspecified( nLang))
            {
                OSL_FAIL( "old style dictionary with invalid language found!" );
                continue;
            }

            // language not yet added?
            if (aNewStyleLanguages.count( nLang ) == 0)
                rNewStyleDics.push_back( *aIt2 );
        }
        else
        {
            OSL_FAIL( "old style dictionary with no language found!" );
        }
    }
}

rtl_TextEncoding getTextEncodingFromCharset(const sal_Char* pCharset)
{
    // default result: used to indicate that we failed to get the proper encoding
    rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;

    if (pCharset)
    {
        eRet = rtl_getTextEncodingFromMimeCharset(pCharset);
        if (eRet == RTL_TEXTENCODING_DONTKNOW)
            eRet = rtl_getTextEncodingFromUnixCharset(pCharset);
        if (eRet == RTL_TEXTENCODING_DONTKNOW)
        {
            if (strcmp("ISCII-DEVANAGARI", pCharset) == 0)
                eRet = RTL_TEXTENCODING_ISCII_DEVANAGARI;
        }
    }
    return eRet;
}

//////////////////////////////////////////////////////////////////////

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>no need to count, just find<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */

#if defined(WNT)
#include <windows.h>
#endif

#include <osl/thread.h>
#include <osl/file.hxx>
#include <tools/debug.hxx>
#include <tools/urlobj.hxx>
#include <i18nlangtag/languagetag.hxx>
#include <i18nlangtag/mslangid.hxx>
#include <unotools/lingucfg.hxx>
#include <unotools/pathoptions.hxx>
#include <rtl/ustring.hxx>
#include <rtl/string.hxx>
#include <rtl/tencinfo.h>
#include <linguistic/misc.hxx>

#include <set>
#include <vector>
#include <string.h>

#include <lingutil.hxx>

#include <sal/macros.h>

using ::com::sun::star::lang::Locale;
using namespace ::com::sun::star;

#if defined(WNT)
OString Win_GetShortPathName( const OUString &rLongPathName )
{
    OString aRes;

    sal_Unicode aShortBuffer[1024] = {0};
    sal_Int32   nShortBufSize = SAL_N_ELEMENTS( aShortBuffer );

    // use the version of 'GetShortPathName' that can deal with Unicode...
    sal_Int32 nShortLen = GetShortPathNameW(
            reinterpret_cast<LPCWSTR>( rLongPathName.getStr() ),
            reinterpret_cast<LPWSTR>( aShortBuffer ),
            nShortBufSize );

    if (nShortLen < nShortBufSize) // conversion successful?
        aRes = OString( OU2ENC( OUString( aShortBuffer, nShortLen ), osl_getThreadTextEncoding()) );
    else
        OSL_FAIL( "Win_GetShortPathName: buffer to short" );

    return aRes;
}
#endif //defined(WNT)

//////////////////////////////////////////////////////////////////////

// build list of old style diuctionaries (not as extensions) to use.
// User installed dictionaries (the ones residing in the user paths)
// will get precedence over system installed ones for the same language.
std::vector< SvtLinguConfigDictionaryEntry > GetOldStyleDics( const char *pDicType )
{
    std::vector< SvtLinguConfigDictionaryEntry > aRes;

    if (!pDicType)
        return aRes;

    OUString aFormatName;
    String aDicExtension;
#ifdef SYSTEM_DICTS
    OUString aSystemDir;
    OUString aSystemPrefix;
    OUString aSystemSuffix;
#endif
    if (strcmp( pDicType, "DICT" ) == 0)
    {
        aFormatName     = "DICT_SPELL";
        aDicExtension   = ".dic";
#ifdef SYSTEM_DICTS
        aSystemDir      = DICT_SYSTEM_DIR;
        aSystemSuffix   = aDicExtension;
#endif
    }
    else if (strcmp( pDicType, "HYPH" ) == 0)
    {
        aFormatName     = "DICT_HYPH";
        aDicExtension   = ".dic";
#ifdef SYSTEM_DICTS
        aSystemDir      = HYPH_SYSTEM_DIR;
        aSystemPrefix   = "hyph_";
        aSystemSuffix   = aDicExtension;
#endif
    }
    else if (strcmp( pDicType, "THES" ) == 0)
    {
        aFormatName     = "DICT_THES";
        aDicExtension   = ".dat";
#ifdef SYSTEM_DICTS
        aSystemDir      = THES_SYSTEM_DIR;
        aSystemPrefix   = "th_";
        aSystemSuffix   = "_v2.dat";
#endif
    }

    if (aFormatName.isEmpty() || aDicExtension.Len() == 0)
        return aRes;

#ifdef SYSTEM_DICTS
    osl::Directory aSystemDicts(aSystemDir);
    if (aSystemDicts.open() == osl::FileBase::E_None)
    {
        // set of languages to remember the language where it is already
        // decided to make use of the dictionary.
        std::set< OUString > aDicLangInUse;

        osl::DirectoryItem aItem;
        osl::FileStatus aFileStatus(osl_FileStatus_Mask_FileURL);
        while (aSystemDicts.getNextItem(aItem) == osl::FileBase::E_None)
        {
            aItem.getFileStatus(aFileStatus);
            OUString sPath = aFileStatus.getFileURL();
            if (sPath.lastIndexOf(aSystemSuffix) == sPath.getLength()-aSystemSuffix.getLength())
            {
                sal_Int32 nStartIndex = sPath.lastIndexOf(sal_Unicode('/')) + 1;
                if (!sPath.match(aSystemPrefix, nStartIndex))
                    continue;
                OUString sChunk = sPath.copy(nStartIndex + aSystemPrefix.getLength(),
                    sPath.getLength() - aSystemSuffix.getLength() -
                    nStartIndex - aSystemPrefix.getLength());
                if (sChunk.isEmpty())
                    continue;
                // We prefer (now) to use language tags.
                // Avoid feeding in the older LANG_REGION scheme to the BCP47
                // ctor as that triggers use of liblangtag and initializes its
                // database which we do not want during startup. Convert
                // instead.
                sChunk = sChunk.replace( '_', '-');
                LanguageTag aLangTag(sChunk, true);
                if (!aLangTag.isValidBcp47())
                    continue;

                // Thus we first get the language of the dictionary
                OUString aLocaleName(aLangTag.getBcp47());

                if (aDicLangInUse.insert(aLocaleName).second)
                {
                    // add the dictionary to the resulting vector
                    SvtLinguConfigDictionaryEntry aDicEntry;
                    aDicEntry.aLocations.realloc(1);
                    aDicEntry.aLocaleNames.realloc(1);
                    aDicEntry.aLocations[0] = sPath;
                    aDicEntry.aFormatName = aFormatName;
                    aDicEntry.aLocaleNames[0] = aLocaleName;
                    aRes.push_back( aDicEntry );
                }
            }
        }
    }
#endif

    return aRes;
}

void MergeNewStyleDicsAndOldStyleDics(
    std::list< SvtLinguConfigDictionaryEntry > &rNewStyleDics,
    const std::vector< SvtLinguConfigDictionaryEntry > &rOldStyleDics )
{
    // get list of languages supported by new style dictionaries
    std::set< LanguageType > aNewStyleLanguages;
    std::list< SvtLinguConfigDictionaryEntry >::const_iterator aIt;
    for (aIt = rNewStyleDics.begin() ;  aIt != rNewStyleDics.end();  ++aIt)
    {
        const uno::Sequence< OUString > aLocaleNames( aIt->aLocaleNames );
        sal_Int32 nLocaleNames = aLocaleNames.getLength();
        for (sal_Int32 k = 0;  k < nLocaleNames; ++k)
        {
            LanguageType nLang = LanguageTag::convertToLanguageTypeWithFallback( aLocaleNames[k] );
            aNewStyleLanguages.insert( nLang );
        }
    }

    // now check all old style dictionaries if they will add a not yet
    // added language. If so add them to the resulting vector
    std::vector< SvtLinguConfigDictionaryEntry >::const_iterator aIt2;
    for (aIt2 = rOldStyleDics.begin();  aIt2 != rOldStyleDics.end();  ++aIt2)
    {
        sal_Int32 nOldStyleDics = aIt2->aLocaleNames.getLength();

        // old style dics should only have one language listed...
        DBG_ASSERT( nOldStyleDics, "old style dictionary with more then one language found!");
        if (nOldStyleDics > 0)
        {
            LanguageType nLang = LanguageTag::convertToLanguageTypeWithFallback( aIt2->aLocaleNames[0] );

            if (nLang == LANGUAGE_DONTKNOW || linguistic::LinguIsUnspecified( nLang))
            {
                OSL_FAIL( "old style dictionary with invalid language found!" );
                continue;
            }

            // language not yet added?
            if (aNewStyleLanguages.find( nLang ) == aNewStyleLanguages.end())
                rNewStyleDics.push_back( *aIt2 );
        }
        else
        {
            OSL_FAIL( "old style dictionary with no language found!" );
        }
    }
}

rtl_TextEncoding getTextEncodingFromCharset(const sal_Char* pCharset)
{
    // default result: used to indicate that we failed to get the proper encoding
    rtl_TextEncoding eRet = RTL_TEXTENCODING_DONTKNOW;

    if (pCharset)
    {
        eRet = rtl_getTextEncodingFromMimeCharset(pCharset);
        if (eRet == RTL_TEXTENCODING_DONTKNOW)
            eRet = rtl_getTextEncodingFromUnixCharset(pCharset);
        if (eRet == RTL_TEXTENCODING_DONTKNOW)
        {
            if (strcmp("ISCII-DEVANAGARI", pCharset) == 0)
                eRet = RTL_TEXTENCODING_ISCII_DEVANAGARI;
        }
    }
    return eRet;
}

//////////////////////////////////////////////////////////////////////

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>


<commit_before>/*
 * lookup-atomese.cc
 *
 * Implement the word-lookup callbacks
 *
 * Copyright (c) 2022 Linas Vepstas <linasvepstas@gmail.com>
 */
#ifdef HAVE_ATOMESE

#include <cstdlib>
#include <opencog/atoms/value/StringValue.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/persist/api/StorageNode.h>
#include <opencog/persist/cog-storage/CogStorage.h>
#include <opencog/nlp/types/atom_types.h>
#undef STRINGIFY

extern "C" {
#include "../link-includes.h"            // For Dictionary
#include "../dict-common/dict-common.h"  // for Dictionary_s
#include "../dict-ram/dict-ram.h"
#include "lookup-atomese.h"
};

using namespace opencog;

class Local
{
public:
	const char* url;
	AtomSpacePtr asp;
	StorageNodePtr stnp;
	Handle linkp;
};

/// Open a connection to a CogServer located at url.
void as_open(Dictionary dict, const char* url)
{
	Local* local = new Local;

	local->url = string_set_add(url, dict->string_set);

	local->asp = createAtomSpace();

	// Create the connector predicate.
	local->linkp = local->asp->add_node(PREDICATE_NODE,
		"*-LG connector string-*");

	/* The cast below forces the shared lib constructor to run. */
	/* That's needed to force the factory to get installed. */
	Handle hsn = local->asp->add_node(COG_STORAGE_NODE, url);
	local->stnp = CogStorageNodeCast(hsn);

	local->stnp->open();

	// XXX FIXME -- if we cannot connect, then should hard-fail.
	if (local->stnp->connected())
		printf("Connected to %s\n", url);
	else
		printf("Failed to connect to %s\n", url);

	dict->as_server = (void*) local;
}

/// Close the connection to the cogserver.
void as_close(Dictionary dict)
{
	if (nullptr == dict->as_server) return;
	Local* local = (Local*) (dict->as_server);
	local->stnp->close();
	delete local;
	dict->as_server = nullptr;
}

/// Return true if the given word can be found in the dictionary,
/// else return false.
bool as_boolean_lookup(Dictionary dict, const char *s)
{
	Local* local = (Local*) (dict->as_server);

printf("duuude called as_boolean_lookup for >>%s<<\n", s);
	bool found = dict_node_exists_lookup(dict, s);
	if (found) return true;

	if (0 == strcmp(s, LEFT_WALL_WORD))
		s = "###LEFT-WALL###";

	Handle wrd = local->asp->get_node(WORD_NODE, s);
	if (nullptr == wrd)
		wrd = local->asp->add_node(WORD_NODE, s);

	// Are there any Sections in the local atomspace?
	size_t nsects = wrd->getIncomingSetSizeByType(SECTION);
	if (0 == nsects)
	{
		local->stnp->fetch_incoming_by_type(wrd, SECTION);
		local->stnp->barrier();
	}

	nsects = wrd->getIncomingSetSizeByType(SECTION);
printf("duuude as_boolean_lookup for >>%s<< sects=%lu\n", s, nsects);
	return 0 != nsects;
}

/// int to base-26 capital letters.
static std::string idtostr(uint64_t aid)
{
	std::string s;
	do
	{
		char c = (aid % 26) + 'A';
		s.push_back(c);
	}
	while (0 < (aid /= 26));

	return s;
}

// Return a cached LG-compatible link string.
// Assisgn a new name, if one does not exist.
static std::string get_linkname(Local* local, const Handle& lnk)
{
	// If we've already cached a connector string for this,
	// just return it.  Else build and cache a string.
	StringValuePtr vname = StringValueCast(lnk->getValue(local->linkp));
	if (vname)
		return vname->value()[0];

	static uint64_t lid = 0;
	std::string slnk = idtostr(lid++);
	vname = createStringValue(slnk);
	lnk->setValue(local->linkp, ValueCast(vname));
	return slnk;
}

// Cheap hack until c++20 ranges are generally available.
template<typename T>
class reverse {
private:
  T& iterable_;
public:
  explicit reverse(T& iterable) : iterable_{iterable} {}
  auto begin() const { return std::rbegin(iterable_); }
  auto end() const { return std::rend(iterable_); }
};

Dict_node * as_lookup_list(Dictionary dict, const char *s)
{
	// Do we already have this word cached? If so, pull from
	// the cache.
	Dict_node * dn = dict_node_lookup(dict, s);

printf("duuude called as_lookup_list for >>%s<< dn=%p\n", s, dn);
	if (dn) return dn;

	const char* ssc = string_set_add(s, dict->string_set);
	Local* local = (Local*) (dict->as_server);

	if (0 == strcmp(s, LEFT_WALL_WORD))
		s = "###LEFT-WALL###";

	Handle wrd = local->asp->get_node(WORD_NODE, s);
	if (nullptr == wrd) return nullptr;

	// Loop over all Sections on the word.
	HandleSeq sects = wrd->getIncomingSetByType(SECTION);
	for (const Handle& sect: sects)
	{
		Exp* exp = nullptr;

		// The germ is the first Atom.
		const Handle& germ = sect->getOutgoingAtom(0);

		// The connector sequence the second Atom.
		// Loop over the connectors in the connector sequence.
		const Handle& conseq = sect->getOutgoingAtom(1);
		for (const Handle& ctcr : reverse(conseq->getOutgoingSet()))
		{
			// The connection target is the first Atom in the connector
			const Handle& tgt = ctcr->getOutgoingAtom(0);
			const Handle& dir = ctcr->getOutgoingAtom(1);

			// The link is the connection of both of these.
			const Handle& lnk = local->asp->add_link(SET_LINK, germ, tgt);

			// Assign an upper-case name to the link.
			std::string slnk = get_linkname(local, lnk);
			const std::string& sdir = dir->get_name();

			Exp* e = make_connector_node(dict, dict->Exp_pool,
			                 slnk.c_str(), sdir.c_str()[0], false);
			if (nullptr == exp)
			{
				exp = e;
				continue;
			}

			exp = make_and_node(dict->Exp_pool, e, exp);
		}
		// printf("Word %s expression %s\n", ssc, lg_exp_stringify(exp));

		Dict_node *sdn = (Dict_node*) malloc(sizeof(Dict_node));
		memset(sdn, 0, sizeof(Dict_node));
		sdn->string = ssc;
		sdn->exp = exp;
		sdn->right = dn;
		sdn->left = NULL;
		dn = sdn;
	}

	// Cache the result; avoid repeated lookups.
	dict->root = dict_node_insert(dict, dict->root, dn);
	dict->num_entries++;

	// Rebalance the tree every now and then.
	if (0 == dict->num_entries % 30)
	{
		dict->root = dsw_tree_to_vine(dict->root);
		dict->root = dsw_vine_to_tree(dict->root, dict->num_entries);
	}

	// Perform the lookup. We cannot return the dn above, as the
	// as_free_llist() below will delete it, leading to mem corruption.
	dn = dict_node_lookup(dict, ssc);
	return dn;
}

Dict_node * as_lookup_wild(Dictionary dict, const char *s)
{
printf("duuude called as_lookup_wild for %s\n", s);
	return NULL;
}

// Zap all the Dict_nodes that we've added earlier.
// This clears out everything hanging on dict->root
// as well as the expression pool.
// And also the local AtomSpace.
//
void as_clear_cache(Dictionary dict)
{
	free_dictionary_root(dict);
	dict->num_entries = 0;
	dict->Exp_pool = pool_new(__func__, "Exp", /*num_elements*/4096,
	                             sizeof(Exp), /*zero_out*/false,
	                             /*align*/false, /*exact*/false);

	// Clear the local AtomSpace too.
	// Easiest way to do this is to just close and reopen
	// the connection.
	Local* local = (Local*) (dict->as_server);
	const char* url = local->url;
	as_close(dict);
	as_open(dict, url);
	as_boolean_lookup(dict, LEFT_WALL_WORD);
}
#endif /* HAVE_ATOMESE */
<commit_msg>Revised debug printfs<commit_after>/*
 * lookup-atomese.cc
 *
 * Implement the word-lookup callbacks
 *
 * Copyright (c) 2022 Linas Vepstas <linasvepstas@gmail.com>
 */
#ifdef HAVE_ATOMESE

#include <cstdlib>
#include <opencog/atoms/value/StringValue.h>
#include <opencog/atomspace/AtomSpace.h>
#include <opencog/persist/api/StorageNode.h>
#include <opencog/persist/cog-storage/CogStorage.h>
#include <opencog/nlp/types/atom_types.h>
#undef STRINGIFY

extern "C" {
#include "../link-includes.h"            // For Dictionary
#include "../dict-common/dict-common.h"  // for Dictionary_s
#include "../dict-ram/dict-ram.h"
#include "lookup-atomese.h"
};

using namespace opencog;

class Local
{
public:
	const char* url;
	AtomSpacePtr asp;
	StorageNodePtr stnp;
	Handle linkp;
};

/// Open a connection to a CogServer located at url.
void as_open(Dictionary dict, const char* url)
{
	Local* local = new Local;

	local->url = string_set_add(url, dict->string_set);

	local->asp = createAtomSpace();

	// Create the connector predicate.
	local->linkp = local->asp->add_node(PREDICATE_NODE,
		"*-LG connector string-*");

	/* The cast below forces the shared lib constructor to run. */
	/* That's needed to force the factory to get installed. */
	Handle hsn = local->asp->add_node(COG_STORAGE_NODE, url);
	local->stnp = CogStorageNodeCast(hsn);

	local->stnp->open();

	// XXX FIXME -- if we cannot connect, then should hard-fail.
	if (local->stnp->connected())
		printf("Connected to %s\n", url);
	else
		printf("Failed to connect to %s\n", url);

	dict->as_server = (void*) local;
}

/// Close the connection to the cogserver.
void as_close(Dictionary dict)
{
	if (nullptr == dict->as_server) return;
	Local* local = (Local*) (dict->as_server);
	local->stnp->close();
	delete local;
	dict->as_server = nullptr;
}

/// Return true if the given word can be found in the dictionary,
/// else return false.
bool as_boolean_lookup(Dictionary dict, const char *s)
{
	bool found = dict_node_exists_lookup(dict, s);
	if (found) return true;

	if (0 == strcmp(s, LEFT_WALL_WORD))
		s = "###LEFT-WALL###";

	Local* local = (Local*) (dict->as_server);
	Handle wrd = local->asp->get_node(WORD_NODE, s);
	if (nullptr == wrd)
		wrd = local->asp->add_node(WORD_NODE, s);

	// Are there any Sections in the local atomspace?
	size_t nsects = wrd->getIncomingSetSizeByType(SECTION);
	if (0 == nsects)
	{
		local->stnp->fetch_incoming_by_type(wrd, SECTION);
		local->stnp->barrier();
	}

	nsects = wrd->getIncomingSetSizeByType(SECTION);
printf("duuude as_boolean_lookup for >>%s<< found sects=%lu\n", s, nsects);
	return 0 != nsects;
}

/// int to base-26 capital letters.
static std::string idtostr(uint64_t aid)
{
	std::string s;
	do
	{
		char c = (aid % 26) + 'A';
		s.push_back(c);
	}
	while (0 < (aid /= 26));

	return s;
}

// Return a cached LG-compatible link string.
// Assisgn a new name, if one does not exist.
static std::string get_linkname(Local* local, const Handle& lnk)
{
	// If we've already cached a connector string for this,
	// just return it.  Else build and cache a string.
	StringValuePtr vname = StringValueCast(lnk->getValue(local->linkp));
	if (vname)
		return vname->value()[0];

	static uint64_t lid = 0;
	std::string slnk = idtostr(lid++);
	vname = createStringValue(slnk);
	lnk->setValue(local->linkp, ValueCast(vname));
	return slnk;
}

// Cheap hack until c++20 ranges are generally available.
template<typename T>
class reverse {
private:
  T& iterable_;
public:
  explicit reverse(T& iterable) : iterable_{iterable} {}
  auto begin() const { return std::rbegin(iterable_); }
  auto end() const { return std::rend(iterable_); }
};

Dict_node * as_lookup_list(Dictionary dict, const char *s)
{
	// Do we already have this word cached? If so, pull from
	// the cache.
	Dict_node * dn = dict_node_lookup(dict, s);

	if (dn) return dn;

	const char* ssc = string_set_add(s, dict->string_set);
	Local* local = (Local*) (dict->as_server);

	if (0 == strcmp(s, LEFT_WALL_WORD))
		s = "###LEFT-WALL###";

	Handle wrd = local->asp->get_node(WORD_NODE, s);
	if (nullptr == wrd) return nullptr;

	// Loop over all Sections on the word.
	HandleSeq sects = wrd->getIncomingSetByType(SECTION);
	for (const Handle& sect: sects)
	{
		Exp* exp = nullptr;

		// The germ is the first Atom.
		const Handle& germ = sect->getOutgoingAtom(0);

		// The connector sequence the second Atom.
		// Loop over the connectors in the connector sequence.
		const Handle& conseq = sect->getOutgoingAtom(1);
		for (const Handle& ctcr : reverse(conseq->getOutgoingSet()))
		{
			// The connection target is the first Atom in the connector
			const Handle& tgt = ctcr->getOutgoingAtom(0);
			const Handle& dir = ctcr->getOutgoingAtom(1);

			// The link is the connection of both of these.
			const Handle& lnk = local->asp->add_link(SET_LINK, germ, tgt);

			// Assign an upper-case name to the link.
			std::string slnk = get_linkname(local, lnk);
			const std::string& sdir = dir->get_name();

			Exp* e = make_connector_node(dict, dict->Exp_pool,
			                 slnk.c_str(), sdir.c_str()[0], false);
			if (nullptr == exp)
			{
				exp = e;
				continue;
			}

			exp = make_and_node(dict->Exp_pool, e, exp);
		}
		// printf("Word %s expression %s\n", ssc, lg_exp_stringify(exp));

		Dict_node *sdn = (Dict_node*) malloc(sizeof(Dict_node));
		memset(sdn, 0, sizeof(Dict_node));
		sdn->string = ssc;
		sdn->exp = exp;
		sdn->right = dn;
		sdn->left = NULL;
		dn = sdn;
	}

	// Cache the result; avoid repeated lookups.
	dict->root = dict_node_insert(dict, dict->root, dn);
	dict->num_entries++;
printf("duuude as_lookup_list %d for >>%s<< had=%lu\n",
dict->num_entries, ssc, sects.size());

	// Rebalance the tree every now and then.
	if (0 == dict->num_entries% 30)
	{
printf("duuude rebalance\n");
		dict->root = dsw_tree_to_vine(dict->root);
		dict->root = dsw_vine_to_tree(dict->root, dict->num_entries);
	}

	// Perform the lookup. We cannot return the dn above, as the
	// as_free_llist() below will delete it, leading to mem corruption.
	dn = dict_node_lookup(dict, ssc);
	return dn;
}

Dict_node * as_lookup_wild(Dictionary dict, const char *s)
{
printf("duuude called as_lookup_wild for %s\n", s);
	return NULL;
}

// Zap all the Dict_nodes that we've added earlier.
// This clears out everything hanging on dict->root
// as well as the expression pool.
// And also the local AtomSpace.
//
void as_clear_cache(Dictionary dict)
{
	Local* local = (Local*) (dict->as_server);
	printf("Prior to clear, dict has %d entries, Atomspace has %lu Atoms\n",
		dict->num_entries, local->asp->size());

	free_dictionary_root(dict);
	dict->num_entries = 0;
	dict->Exp_pool = pool_new(__func__, "Exp", /*num_elements*/4096,
	                             sizeof(Exp), /*zero_out*/false,
	                             /*align*/false, /*exact*/false);

	// Clear the local AtomSpace too.
	// Easiest way to do this is to just close and reopen
	// the connection.
	const char* url = local->url;
	as_close(dict);
	as_open(dict, url);
	as_boolean_lookup(dict, LEFT_WALL_WORD);
}
#endif /* HAVE_ATOMESE */
<|endoftext|>


<commit_before>// Copyright (c) 2009, Object Computing, Inc.
// All rights reserved.
// See the file license.txt for licensing information.
#include <Common/QuickFASTPch.h>
#include "BitMap.h"
#include <Common/Exceptions.h>

using namespace ::QuickFAST;

BitMap::BitMap(const uchar * buffer, size_t length)
{
  int todo;
}

BitMap::BitMap(const BitMap & rhs)
{
  int todo;
}

BitMap::BitMap()
{
  int todo;
}

BitMap::~BitMap()
{
}

void
BitMap::toString(std::string & value)const
{
  int todo;
}

BitMap &
BitMap::operator = (const BitMap & rhs)
{
  BitMap temp(rhs);
  swap(temp);
  return *this;
}

void
BitMap::swap(BitMap & rhs)
{
  int todo;
}

<commit_msg>Renaming Bitmap.cpp<commit_after><|endoftext|>


<commit_before>#include <math.h>
#include <stdio.h>
#include "HalideRuntime.h"
#include "HalideBuffer.h"
#include <assert.h>

#include "tiled_blur.h"

using namespace Halide::Runtime;

const int W = 80, H = 80;

int my_halide_trace(void *user_context, const halide_trace_event_t *ev) {
    if (ev->event == halide_trace_begin_realization) {
        assert(ev->dimensions == 6);
        int min_x = ev->coordinates[0], width = ev->coordinates[1];
        int min_y = ev->coordinates[2], height = ev->coordinates[3];
        int max_x = min_x + width - 1;
        int max_y = min_y + height - 1;
        printf("Using %d x %d input tile over [%d - %d] x [%d - %d]\n", width, height, min_x, max_x,
               min_y, max_y);
        assert(min_x >= 0 && min_y >= 0 && max_x < W && max_y < H);

        // The input is large enough that the boundary condition could
        // only ever apply on one side.
        assert(width == 33 || width == 34);
        assert(height == 33 || height == 34);
    }
    return 0;
}

Buffer<> buffer_factory_planar(halide_type_t t, int w, int h, int c) {
    return Buffer<>(t, w, h, c);
}

Buffer<> buffer_factory_interleaved(halide_type_t t, int w, int h, int c) {
    return Buffer<>::make_interleaved(t, w, h, c);
}

void test(Buffer<> (*factory)(halide_type_t, int w, int h, int c)) {
    Buffer<uint8_t> input = factory(halide_type_of<uint8_t>(), W, H, 3);
    input.for_each_element([&](int x, int y, int c) {
        // Just an arbitrary color pattern with enough variation to notice the brighten + blur
        if (c == 0) {
            input(x, y, c) = (uint8_t)((x % 7) + (y % 3));
        } else if (c == 1) {
            input(x, y, c) = (uint8_t)(x + y);
        } else {
            input(x, y, c) = (uint8_t)((x * 5) + (y * 2));
        }
    });
    Buffer<uint8_t> output = factory(halide_type_of<uint8_t>(), W, H, 3);

    printf("Evaluating output over %d x %d in tiles of size 32 x 32\n", W, H);
    tiled_blur(input, output);
}

int main(int argc, char **argv) {
    halide_set_custom_trace(&my_halide_trace);

    printf("Testing planar buffer...\n");
    test(buffer_factory_planar);

    printf("Testing interleaved buffer...\n");
    test(buffer_factory_interleaved);

    printf("Success!\n");
    return 0;
}
<commit_msg>tiled blur test stuff<commit_after>#include <math.h>
#include <stdio.h>
#include "HalideRuntime.h"
#include "HalideBuffer.h"
#include <assert.h>

#include "tiled_blur.h"

// defined away to avoid requiring libpng, libjpeg everywhere;
// left in because useful for debugging and profiling.
#define SAVE_IMAGES 0

#if SAVE_IMAGES
#include "halide_image_io.h"
#endif

using namespace Halide::Runtime;

const int W = 80, H = 80;

int my_halide_trace(void *user_context, const halide_trace_event_t *ev) {
    if (ev->event == halide_trace_begin_realization) {
        assert(ev->dimensions == 6);
        int min_x = ev->coordinates[0], width = ev->coordinates[1];
        int min_y = ev->coordinates[2], height = ev->coordinates[3];
        int max_x = min_x + width - 1;
        int max_y = min_y + height - 1;
        printf("Using %d x %d input tile over [%d - %d] x [%d - %d]\n", width, height, min_x, max_x,
               min_y, max_y);
        assert(min_x >= 0 && min_y >= 0 && max_x < W && max_y < H);

        // The input is large enough that the boundary condition could
        // only ever apply on one side.
        assert(width == 33 || width == 34);
        assert(height == 33 || height == 34);
    }
    return 0;
}

Buffer<uint8_t> buffer_factory_planar(int w, int h, int c) {
    return Buffer<uint8_t>(w, h, c);
}

Buffer<uint8_t> buffer_factory_interleaved(int w, int h, int c) {
    return Buffer<uint8_t>::make_interleaved(w, h, c);
}

void test(Buffer<uint8_t> (*factory)(int w, int h, int c)) {
    Buffer<uint8_t> input = factory(W, H, 3);
    input.for_each_element([&](int x, int y, int c) {
        // Just an arbitrary color pattern with enough variation to notice the brighten + blur
        if (c == 0) {
            input(x, y, c) = (uint8_t)((x % 7) + (y % 3));
        } else if (c == 1) {
            input(x, y, c) = (uint8_t)(x + y);
        } else {
            input(x, y, c) = (uint8_t)((x * 5) + (y * 2));
        }
    });
    Buffer<uint8_t> output = factory(W, H, 3);

    printf("Evaluating output over %d x %d in tiles of size 32 x 32\n", W, H);
    tiled_blur(input, output);

#if SAVE_IMAGES
    static int x = 0;
    Halide::Tools::save_image(input, "/tmp/tiled_input" + std::to_string(x) + ".png");
    Halide::Tools::save_image(output, "/tmp/tiled_output" + std::to_string(x) + ".png");
    ++x;
#endif
}

int main(int argc, char **argv) {
    halide_set_custom_trace(&my_halide_trace);

    printf("Testing planar buffer...\n");
    test(buffer_factory_planar);

    printf("Testing interleaved buffer...\n");
    test(buffer_factory_interleaved);

    printf("Success!\n");
    return 0;
}
<|endoftext|>


<commit_before>#include <ComputeWAXPBY.hpp>
#include <KernelWrappers.h>
#include "VectorOptimizationDataTx.hpp"

int ComputeWAXPBY(const local_int_t n, const double alpha, const Vector & x,
    const double beta, const Vector & y, Vector & w, bool & isOptimized,
    bool copyIn, bool copyOut)
{
  isOptimized = true;
  const double* x_d;
  const double* y_d;
  double* w_d = ((VectorOptimizationDataTx*)w.optimizationData)->devicePtr;
  if (copyIn) {
    x_d = transferDataToGPU(x);
    y_d = transferDataToGPU(y);
  } else {
    x_d = ((VectorOptimizationDataTx*)x.optimizationData)->devicePtr;
    y_d = ((VectorOptimizationDataTx*)y.optimizationData)->devicePtr;
  }
  launchComputeWAXPBY(n, alpha, x_d, beta, y_d, w_d);
  if (copyOut) {
    transferDataFromGPU(w);
  } else {
    cudaError_t cerr = cudaDeviceSynchronize();
    CHKCUDAERR(cerr);
  }
  return 0;
}

<commit_msg>Another missing include.<commit_after>#include <ComputeWAXPBY.hpp>
#include <KernelWrappers.h>
#include "VectorOptimizationDataTx.hpp"
#include "chkcudaerror.hpp"

int ComputeWAXPBY(const local_int_t n, const double alpha, const Vector & x,
    const double beta, const Vector & y, Vector & w, bool & isOptimized,
    bool copyIn, bool copyOut)
{
  isOptimized = true;
  const double* x_d;
  const double* y_d;
  double* w_d = ((VectorOptimizationDataTx*)w.optimizationData)->devicePtr;
  if (copyIn) {
    x_d = transferDataToGPU(x);
    y_d = transferDataToGPU(y);
  } else {
    x_d = ((VectorOptimizationDataTx*)x.optimizationData)->devicePtr;
    y_d = ((VectorOptimizationDataTx*)y.optimizationData)->devicePtr;
  }
  launchComputeWAXPBY(n, alpha, x_d, beta, y_d, w_d);
  if (copyOut) {
    transferDataFromGPU(w);
  } else {
    cudaError_t cerr = cudaDeviceSynchronize();
    CHKCUDAERR(cerr);
  }
  return 0;
}

<|endoftext|>


<commit_before>//
//  Video.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 31/10/2020.
//  Copyright © 2020 Thomas Harte. All rights reserved.
//

#include "Video.hpp"

using namespace Apple::IIgs::Video;

namespace {

constexpr int CyclesPerLine = 910;
constexpr int Lines = 263;
constexpr int FinalPixelLine = 192;

}

VideoBase::VideoBase() :
	VideoSwitches<Cycles>(Cycles(2), [] (Cycles) {}) {
}

void VideoBase::set_internal_ram(const uint8_t *ram) {
	ram_ = ram;
}

void VideoBase::did_set_annunciator_3(bool) {}
void VideoBase::did_set_alternative_character_set(bool) {}

void VideoBase::run_for(Cycles cycles) {
	// TODO: everything else!
	const auto old = cycles_into_frame_;
	cycles_into_frame_ = (cycles_into_frame_ + cycles.as<int>()) % (CyclesPerLine * Lines);

	// DEBUGGING HACK!!
	// Scan the output buffer, assuming this is 40-column text mode, and print anything found.
	if(cycles_into_frame_ < old) {
		for(int line = 0; line < 192; line += 8) {
			const uint16_t address = get_row_address(line);

			bool did_print_line = false;
			for(int column = 0; column < 40; column++) {
				const char c = char(ram_[address + column]);
				if(c > 0) {
					printf("%c", c);
					did_print_line = true;
				}
			}
			if(did_print_line) printf("\n");
		}
	}
}

bool VideoBase::get_is_vertical_blank() {
	return cycles_into_frame_ >= FinalPixelLine * CyclesPerLine;
}

void VideoBase::set_new_video(uint8_t new_video) {
	new_video_ = new_video;
}

uint8_t VideoBase::get_new_video() {
	return new_video_;
}

void VideoBase::clear_interrupts(uint8_t mask) {
	set_interrupts(interrupts_ & ~(mask & 0x60));
}

void VideoBase::set_interrupt_register(uint8_t mask) {
	set_interrupts(interrupts_ | (mask & 0x6));
}

uint8_t VideoBase::get_interrupt_register() {
	return interrupts_;
}

void VideoBase::notify_clock_tick() {
	set_interrupts(interrupts_ | 0x40);
}

void VideoBase::set_interrupts(uint8_t new_value) {
	interrupts_ = new_value & 0x7f;
	if((interrupts_ >> 4) & 0x6)
		interrupts_ |= 0x80;
}
<commit_msg>Resolves specious interrupts.dic<commit_after>//
//  Video.cpp
//  Clock Signal
//
//  Created by Thomas Harte on 31/10/2020.
//  Copyright © 2020 Thomas Harte. All rights reserved.
//

#include "Video.hpp"

using namespace Apple::IIgs::Video;

namespace {

constexpr int CyclesPerLine = 910;
constexpr int Lines = 263;
constexpr int FinalPixelLine = 192;

}

VideoBase::VideoBase() :
	VideoSwitches<Cycles>(Cycles(2), [] (Cycles) {}) {
}

void VideoBase::set_internal_ram(const uint8_t *ram) {
	ram_ = ram;
}

void VideoBase::did_set_annunciator_3(bool) {}
void VideoBase::did_set_alternative_character_set(bool) {}

void VideoBase::run_for(Cycles cycles) {
	// TODO: everything else!
	const auto old = cycles_into_frame_;
	cycles_into_frame_ = (cycles_into_frame_ + cycles.as<int>()) % (CyclesPerLine * Lines);

	// DEBUGGING HACK!!
	// Scan the output buffer, assuming this is 40-column text mode, and print anything found.
	if(cycles_into_frame_ < old) {
		for(int line = 0; line < 192; line += 8) {
			const uint16_t address = get_row_address(line);

			bool did_print_line = false;
			for(int column = 0; column < 40; column++) {
				const char c = char(ram_[address + column]);
				if(c > 0) {
					printf("%c", c);
					did_print_line = true;
				}
			}
			if(did_print_line) printf("\n");
		}
	}
}

bool VideoBase::get_is_vertical_blank() {
	return cycles_into_frame_ >= FinalPixelLine * CyclesPerLine;
}

void VideoBase::set_new_video(uint8_t new_video) {
	new_video_ = new_video;
}

uint8_t VideoBase::get_new_video() {
	return new_video_;
}

void VideoBase::clear_interrupts(uint8_t mask) {
	set_interrupts(interrupts_ & ~(mask & 0x60));
}

void VideoBase::set_interrupt_register(uint8_t mask) {
	set_interrupts(interrupts_ | (mask & 0x6));
}

uint8_t VideoBase::get_interrupt_register() {
	return interrupts_;
}

void VideoBase::notify_clock_tick() {
	set_interrupts(interrupts_ | 0x40);
}

void VideoBase::set_interrupts(uint8_t new_value) {
	interrupts_ = new_value & 0x7f;
	if((interrupts_ >> 4) & interrupts_ & 0x6)
		interrupts_ |= 0x80;
}
<|endoftext|>


<commit_before>#pragma once

#include <boost/spirit/home/lex/lexer/lexer.hpp>

#include <ostream>

namespace perseus
{
  namespace detail
  {
    namespace token_id
    {
      enum token_id
      {
        whitespace,
        comment,

        // constants
        string,
        decimal_integer,
        hexadecimal_integer,
        binary_integer,

        identifier,
        operator_identifier,

        //    keywords; using a _ suffix for conistency, even for those that are not c++ keywords
        // control flow
        if_,
        else_,
        while_,
        return_,
        // values
        true_,
        false_,
        // other
        let_,
        function_,
        mutable_,
        impure_,

        colon,
        semicolon,
        dot,
        comma,
        equals,
        backtick, // `
        arrow_right, // ->

        // ()
        paren_open,
        paren_close,
        // {}
        brace_open,
        brace_close,
        // []
        square_bracket_open,
        square_bracket_close,

        any
      };

      std::ostream& operator<<( std::ostream& os, token_id token );
    }
  }
}<commit_msg>token 0 is apparently a bad idea?<commit_after>#pragma once

#include <boost/spirit/home/lex/lexer/lexer.hpp>

#include <ostream>

namespace perseus
{
  namespace detail
  {
    namespace token_id
    {
      enum token_id
      {
        whitespace = 1,
        comment,

        // constants
        string,
        decimal_integer,
        hexadecimal_integer,
        binary_integer,

        identifier,
        operator_identifier,

        //    keywords; using a _ suffix for conistency, even for those that are not c++ keywords
        // control flow
        if_,
        else_,
        while_,
        return_,
        // values
        true_,
        false_,
        // other
        let_,
        function_,
        mutable_,
        impure_,

        colon,
        semicolon,
        dot,
        comma,
        equals,
        backtick, // `
        arrow_right, // ->

        // ()
        paren_open,
        paren_close,
        // {}
        brace_open,
        brace_close,
        // []
        square_bracket_open,
        square_bracket_close,

        any
      };

      std::ostream& operator<<( std::ostream& os, token_id token );
    }
  }
}<|endoftext|>


<commit_before>#ifndef MJOLNIR_READ_UNIT_SYSTEM_HPP
#define MJOLNIR_READ_UNIT_SYSTEM_HPP
#include <extlib/toml/toml.hpp>
#include <mjolnir/math/constants.hpp>
#include <mjolnir/core/constants.hpp>
#include <mjolnir/core/Unit.hpp>
#include <mjolnir/util/get_toml_value.hpp>
#include <mjolnir/util/logger.hpp>
#include <mjolnir/input/read_simulator.hpp>

namespace mjolnir
{

template<typename traitsT>
std::unique_ptr<SimulatorBase> read_units(const toml::Table& data)
{
    MJOLNIR_GET_DEFAULT_LOGGER();
    MJOLNIR_SCOPE(read_units(const toml::Table& data), 0);
    using real_type = typename traitsT::real_type;
    using phys_type = physics::constants<real_type>;
    using math_type = math::constants<real_type>;
    using unit_type = unit::constants<real_type>;

    const auto& units = get_toml_value<toml::Table>(data, "units", "<root>");

    const auto& energy = get_toml_value<std::string>(units, "energy", "[units]");
    const auto& length = get_toml_value<std::string>(units, "length", "[units]");

    MJOLNIR_LOG_INFO("unit of energy : [", energy, ']');
    MJOLNIR_LOG_INFO("unit of length : [", length, ']');

    if(energy == "kcal/mol")
    {
        // kB [J/K] -> [kcal/mol/K] by * (J to cal) * 1e-3 * (/mol)
        phys_type::kB   *= (unit_type::J_to_cal / 1000.0) *
                           unit_type::avogadro_constant;
        // eps0 [F/m] == [C^2/J/m] -> [C^2/(kcal/mol)/m]
        phys_type::eps0 *= (1000.0 / unit_type::J_to_cal) /
                           unit_type::avogadro_constant;
    }
    else if(energy == "kJ/mol")
    {
        // kB [J/K] -> [kJ/mol/K]
        phys_type::kB   *= 1e-3 * unit_type::avogadro_constant;
        // eps0 [F/m] == [C^2/J/m] -> [C^2/kJ/mol/m]
        phys_type::eps0 *= 1e+3 / unit_type::avogadro_constant;
    }
    else
    {
        throw_exception<std::runtime_error>("mjolnir::read_units: unknown unit "
            "for energy: `", energy, "`. `kcal/mol`, `kJ/mol` are allowed");
    }

    // until here, SI `m` are used as length unit.

    if(length == "angstrom" || length == "Å")
    {
        // eps0 [C^2/Energy/m] -> [C^2/Energy/Angstrom]
        phys_type::eps0 *= (1.0 / unit_type::m_to_angstrom);
    }
    else if(length == "nm")
    {
        // eps0 [C^2/Energy/m] -> [C^2/Energy/nm]
        phys_type::eps0 *= (1.0 / unit_type::m_to_nm);
    }
    else
    {
        throw_exception<std::runtime_error>("mjolnir::read_units: unknown unit "
            "for length: `", length, "`. `angstrom`, `nm` are allowed");
    }

    MJOLNIR_LOG_INFO(u8"phys::kB = ", phys_type::kB,   " [", energy, "]");
    MJOLNIR_LOG_INFO(u8"phys::NA = ", phys_type::NA,   " [1/mol]");
    MJOLNIR_LOG_INFO(u8"phys::e  = ", phys_type::e,    " [C]");
    MJOLNIR_LOG_INFO(u8"phys::ε0 = ", phys_type::eps0,
                       " [C^2 / (", energy, '*', length, ")]");

    return read_simulator<traitsT>(data);
}

} // mjolnir
#endif// MJOLNIR_READ_UNIT_SYSTEM_HPP
<commit_msg>add missing u8 prefix<commit_after>#ifndef MJOLNIR_READ_UNIT_SYSTEM_HPP
#define MJOLNIR_READ_UNIT_SYSTEM_HPP
#include <extlib/toml/toml.hpp>
#include <mjolnir/math/constants.hpp>
#include <mjolnir/core/constants.hpp>
#include <mjolnir/core/Unit.hpp>
#include <mjolnir/util/get_toml_value.hpp>
#include <mjolnir/util/logger.hpp>
#include <mjolnir/input/read_simulator.hpp>

namespace mjolnir
{

template<typename traitsT>
std::unique_ptr<SimulatorBase> read_units(const toml::Table& data)
{
    MJOLNIR_GET_DEFAULT_LOGGER();
    MJOLNIR_SCOPE(read_units(const toml::Table& data), 0);
    using real_type = typename traitsT::real_type;
    using phys_type = physics::constants<real_type>;
    using math_type = math::constants<real_type>;
    using unit_type = unit::constants<real_type>;

    const auto& units = get_toml_value<toml::Table>(data, "units", "<root>");

    const auto& energy = get_toml_value<std::string>(units, "energy", "[units]");
    const auto& length = get_toml_value<std::string>(units, "length", "[units]");

    MJOLNIR_LOG_INFO("unit of energy : [", energy, ']');
    MJOLNIR_LOG_INFO("unit of length : [", length, ']');

    if(energy == "kcal/mol")
    {
        // kB [J/K] -> [kcal/mol/K] by * (J to cal) * 1e-3 * (/mol)
        phys_type::kB   *= (unit_type::J_to_cal / 1000.0) *
                           unit_type::avogadro_constant;
        // eps0 [F/m] == [C^2/J/m] -> [C^2/(kcal/mol)/m]
        phys_type::eps0 *= (1000.0 / unit_type::J_to_cal) /
                           unit_type::avogadro_constant;
    }
    else if(energy == "kJ/mol")
    {
        // kB [J/K] -> [kJ/mol/K]
        phys_type::kB   *= 1e-3 * unit_type::avogadro_constant;
        // eps0 [F/m] == [C^2/J/m] -> [C^2/kJ/mol/m]
        phys_type::eps0 *= 1e+3 / unit_type::avogadro_constant;
    }
    else
    {
        throw_exception<std::runtime_error>("mjolnir::read_units: unknown unit "
            "for energy: `", energy, "`. `kcal/mol`, `kJ/mol` are allowed");
    }

    // until here, SI `m` are used as length unit.

    if(length == "angstrom" || length == u8"Å")
    {
        // eps0 [C^2/Energy/m] -> [C^2/Energy/Angstrom]
        phys_type::eps0 *= (1.0 / unit_type::m_to_angstrom);
    }
    else if(length == "nm")
    {
        // eps0 [C^2/Energy/m] -> [C^2/Energy/nm]
        phys_type::eps0 *= (1.0 / unit_type::m_to_nm);
    }
    else
    {
        throw_exception<std::runtime_error>("mjolnir::read_units: unknown unit "
            "for length: `", length, "`. `angstrom`, `nm` are allowed");
    }

    MJOLNIR_LOG_INFO(u8"phys::kB = ", phys_type::kB,   " [", energy, "]");
    MJOLNIR_LOG_INFO(u8"phys::NA = ", phys_type::NA,   " [1/mol]");
    MJOLNIR_LOG_INFO(u8"phys::e  = ", phys_type::e,    " [C]");
    MJOLNIR_LOG_INFO(u8"phys::ε0 = ", phys_type::eps0,
                       " [C^2 / (", energy, '*', length, ")]");

    return read_simulator<traitsT>(data);
}

} // mjolnir
#endif// MJOLNIR_READ_UNIT_SYSTEM_HPP
<|endoftext|>


<commit_before><commit_msg>Removed unused `#include` lines.<commit_after><|endoftext|>


<commit_before>/*
 * Copyright (C) 2011 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "x-messenger-oauth2-prompt.h"

#include <KUrl>
#include <KIcon>
#include <KToolInvocation>
#include <KDebug>
#include <KWebView>
#include <KWebPage>

#include <QtWebKit/QWebSettings>
#include <QtGui/QProgressBar>
#include <QtGui/QBoxLayout>
#include <QtGui/QLayout>
#include <QtNetwork/QNetworkReply>

const QLatin1String msnClientID("000000004C070A47");
const QLatin1String redirectUri("https://oauth.live.com/desktop");
const QLatin1String request("https://oauth.live.com/authorize?response_type=token&redirect_uri=%1&client_id=%2&scope=wl.messenger");
const QLatin1String tokenParameter("access_token=");

XMessengerOAuth2Prompt::XMessengerOAuth2Prompt(QWidget* parent) :
    KDialog(parent),
    m_webView(new KWebView()),
    m_ProgressBar(new QProgressBar()),
    m_loginPageLoaded(false)
{
    // TODO Use .ui file
    m_webView->setContextMenuPolicy(Qt::NoContextMenu);
//    QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    m_ProgressBar->setRange(0, 100);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(m_webView);
    layout->addWidget(m_ProgressBar);

    QWidget *widget = new QWidget(this);
    widget->setLayout(layout);
    widget->setContentsMargins(0, 0, 0, 0);

    setMainWidget(widget);
    setWindowIcon(KIcon(QLatin1String("telepathy-kde")));
    setButtons(Cancel);

    // connect progress bar
    connect(m_webView,
            SIGNAL(loadFinished(bool)),
            m_ProgressBar,
            SLOT(hide()));
    connect(m_webView,
            SIGNAL(loadStarted()),
            m_ProgressBar,
            SLOT(show()));
    connect(m_webView,
            SIGNAL(loadProgress(int)),
            m_ProgressBar,
            SLOT(setValue(int)));

    connect(m_webView,
            SIGNAL(urlChanged(QUrl)),
            SLOT(onUrlChanged(QUrl)));
    connect(m_webView,
            SIGNAL(loadFinished(bool)),
            SLOT(onLoadFinished(bool)));

    connect(m_webView,
            SIGNAL(linkClicked(QUrl)),
            SLOT(onLinkClicked(QUrl)));

    KWebPage *page = qobject_cast<KWebPage*>(m_webView->page());

    page->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
    page->setForwardUnsupportedContent(true);

    connect(page,
            SIGNAL(unsupportedContent(QNetworkReply*)),
            SLOT(onUnsupportedContent(QNetworkReply*)));

    // start loading the login URL
    KUrl url(QString(request).arg(redirectUri).arg(msnClientID));
    m_webView->load(url.url());
}

XMessengerOAuth2Prompt::~XMessengerOAuth2Prompt()
{
}

QByteArray XMessengerOAuth2Prompt::accessToken() const
{
    return m_accessToken;
}

QSize XMessengerOAuth2Prompt::sizeHint() const
{
    return QSize(500, 600);
}

void XMessengerOAuth2Prompt::onUrlChanged(const QUrl &url)
{
    kDebug() << url;
    if (url.toString().indexOf(redirectUri) != 0) {
        // This is not the url containing the token
        return;
    }
    extractToken(url);
}

void XMessengerOAuth2Prompt::onLinkClicked(const QUrl& url)
{
    kDebug() << url;
    KToolInvocation::invokeBrowser(url.toString());
}

void XMessengerOAuth2Prompt::onLoadFinished(bool ok)
{
    if (!m_loginPageLoaded) {
        m_loginPageLoaded = true;
        Q_EMIT loginPageLoaded(ok);
    }
}

void XMessengerOAuth2Prompt::onUnsupportedContent(QNetworkReply* reply)
{
    // With QtWebkit < 2.2, for some reason the request for the token is
    // unsupported, but we can extract the token from the url of the failing
    // request
    // FIXME: In the future this might want to remove this
    QUrl url =  reply->url();
    if (url.toString().indexOf(redirectUri) != 0) {
        // This is not the url containing the token
        return;
    }
    extractToken(url);
}

void XMessengerOAuth2Prompt::extractToken(const QUrl &url)
{
    QString accessToken;
    Q_FOREACH (const QString &token, QString::fromLatin1(url.encodedFragment()).split(QLatin1Char('&'))) {
        // Get the URL fragment part and iterate over the parameters of the request
        if (token.indexOf(tokenParameter) == 0) {
            // This is the token that we are looking for (we are not interested
            // in the "expires_in" part, etc.)
            accessToken = token;
            break;
        }
    }
    if (accessToken.isEmpty()) {
        // Could not find the token
        kWarning() << "Token not found";
        return;
    }

    accessToken = accessToken.split(QLatin1Char('=')).at(1);    // Split by "access_token=..." and take latter part

    // Wocky will base64 encode, but token actually already is base64, so we
    // decode now and it will be re-encoded.
    m_accessToken = QByteArray::fromBase64(QByteArray::fromPercentEncoding(accessToken.toUtf8()));
    Q_EMIT accessTokenTaken(m_accessToken);
    accept();
}

#include "x-messenger-oauth2-prompt.moc"
<commit_msg>blocks QWebView functionality which allows you to change page by dragging a URL onto it<commit_after>/*
 * Copyright (C) 2011 Daniele E. Domenichelli <daniele.domenichelli@gmail.com>
 *
 * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "x-messenger-oauth2-prompt.h"

#include <KUrl>
#include <KIcon>
#include <KToolInvocation>
#include <KDebug>
#include <KWebView>
#include <KWebPage>

#include <QtWebKit/QWebSettings>
#include <QtGui/QProgressBar>
#include <QtGui/QBoxLayout>
#include <QtGui/QLayout>
#include <QtNetwork/QNetworkReply>

const QLatin1String msnClientID("000000004C070A47");
const QLatin1String redirectUri("https://oauth.live.com/desktop");
const QLatin1String request("https://oauth.live.com/authorize?response_type=token&redirect_uri=%1&client_id=%2&scope=wl.messenger");
const QLatin1String tokenParameter("access_token=");

XMessengerOAuth2Prompt::XMessengerOAuth2Prompt(QWidget* parent) :
    KDialog(parent),
    m_webView(new KWebView()),
    m_ProgressBar(new QProgressBar()),
    m_loginPageLoaded(false)
{
    // TODO Use .ui file
    m_webView->setContextMenuPolicy(Qt::NoContextMenu);
    m_webView->setAcceptDrops(false);
//    QWebSettings::globalSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled, true);
    m_ProgressBar->setRange(0, 100);

    QVBoxLayout *layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->addWidget(m_webView);
    layout->addWidget(m_ProgressBar);

    QWidget *widget = new QWidget(this);
    widget->setLayout(layout);
    widget->setContentsMargins(0, 0, 0, 0);

    setMainWidget(widget);
    setWindowIcon(KIcon(QLatin1String("telepathy-kde")));
    setButtons(Cancel);

    // connect progress bar
    connect(m_webView,
            SIGNAL(loadFinished(bool)),
            m_ProgressBar,
            SLOT(hide()));
    connect(m_webView,
            SIGNAL(loadStarted()),
            m_ProgressBar,
            SLOT(show()));
    connect(m_webView,
            SIGNAL(loadProgress(int)),
            m_ProgressBar,
            SLOT(setValue(int)));

    connect(m_webView,
            SIGNAL(urlChanged(QUrl)),
            SLOT(onUrlChanged(QUrl)));
    connect(m_webView,
            SIGNAL(loadFinished(bool)),
            SLOT(onLoadFinished(bool)));

    connect(m_webView,
            SIGNAL(linkClicked(QUrl)),
            SLOT(onLinkClicked(QUrl)));

    KWebPage *page = qobject_cast<KWebPage*>(m_webView->page());

    page->setLinkDelegationPolicy(QWebPage::DontDelegateLinks);
    page->setForwardUnsupportedContent(true);

    connect(page,
            SIGNAL(unsupportedContent(QNetworkReply*)),
            SLOT(onUnsupportedContent(QNetworkReply*)));

    // start loading the login URL
    KUrl url(QString(request).arg(redirectUri).arg(msnClientID));
    m_webView->load(url.url());
}

XMessengerOAuth2Prompt::~XMessengerOAuth2Prompt()
{
}

QByteArray XMessengerOAuth2Prompt::accessToken() const
{
    return m_accessToken;
}

QSize XMessengerOAuth2Prompt::sizeHint() const
{
    return QSize(500, 600);
}

void XMessengerOAuth2Prompt::onUrlChanged(const QUrl &url)
{
    kDebug() << url;
    if (url.toString().indexOf(redirectUri) != 0) {
        // This is not the url containing the token
        return;
    }
    extractToken(url);
}

void XMessengerOAuth2Prompt::onLinkClicked(const QUrl& url)
{
    kDebug() << url;
    KToolInvocation::invokeBrowser(url.toString());
}

void XMessengerOAuth2Prompt::onLoadFinished(bool ok)
{
    if (!m_loginPageLoaded) {
        m_loginPageLoaded = true;
        Q_EMIT loginPageLoaded(ok);
    }
}

void XMessengerOAuth2Prompt::onUnsupportedContent(QNetworkReply* reply)
{
    // With QtWebkit < 2.2, for some reason the request for the token is
    // unsupported, but we can extract the token from the url of the failing
    // request
    // FIXME: In the future this might want to remove this
    QUrl url =  reply->url();
    if (url.toString().indexOf(redirectUri) != 0) {
        // This is not the url containing the token
        return;
    }
    extractToken(url);
}

void XMessengerOAuth2Prompt::extractToken(const QUrl &url)
{
    QString accessToken;
    Q_FOREACH (const QString &token, QString::fromLatin1(url.encodedFragment()).split(QLatin1Char('&'))) {
        // Get the URL fragment part and iterate over the parameters of the request
        if (token.indexOf(tokenParameter) == 0) {
            // This is the token that we are looking for (we are not interested
            // in the "expires_in" part, etc.)
            accessToken = token;
            break;
        }
    }
    if (accessToken.isEmpty()) {
        // Could not find the token
        kWarning() << "Token not found";
        return;
    }

    accessToken = accessToken.split(QLatin1Char('=')).at(1);    // Split by "access_token=..." and take latter part

    // Wocky will base64 encode, but token actually already is base64, so we
    // decode now and it will be re-encoded.
    m_accessToken = QByteArray::fromBase64(QByteArray::fromPercentEncoding(accessToken.toUtf8()));
    Q_EMIT accessTokenTaken(m_accessToken);
    accept();
}

#include "x-messenger-oauth2-prompt.moc"
<|endoftext|>


<commit_before>#include <common/buffer.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/pipe.h>

#include <xcodec/xcodec.h>
#include <xcodec/xcodec_encoder.h>
#include <xcodec/xcodec_encoder_pipe.h>

XCodecEncoderPipe::XCodecEncoderPipe(XCodec *codec)
: encoder_(codec),
  input_buffer_(),
  input_eos_(false),
  output_action_(NULL),
  output_callback_(NULL)
{
	ASSERT(output_callback_ == NULL || output_action_ == NULL);

	encoder_.set_pipe(this);
}

XCodecEncoderPipe::~XCodecEncoderPipe()
{
	ASSERT(output_action_ == NULL);
	ASSERT(output_callback_ == NULL);

	encoder_.set_pipe(NULL);
}

Action *
XCodecEncoderPipe::input(Buffer *buf, EventCallback *cb)
{
	ASSERT(output_callback_ == NULL || output_action_ == NULL);
	if (output_callback_ != NULL) {
		ASSERT(input_buffer_.empty());
		ASSERT(output_action_ == NULL);

		if (!buf->empty()) {
			Buffer tmp;

			encoder_.encode(&tmp, buf);
			ASSERT(buf->empty());

			output_callback_->event(Event(Event::Done, 0, tmp));
		} else {
			input_eos_ = true;
			output_callback_->event(Event(Event::EOS, 0));
		}
		output_action_ = EventSystem::instance()->schedule(output_callback_);
		output_callback_ = NULL;
		ASSERT(output_callback_ == NULL || output_action_ == NULL);
	} else {
		if (!buf->empty()) {
			encoder_.encode(&input_buffer_, buf);
			ASSERT(buf->empty());
		} else {
			input_eos_ = true;
		}
	}
	ASSERT(output_callback_ == NULL || output_action_ == NULL);

	cb->event(Event(Event::Done, 0));
	return (EventSystem::instance()->schedule(cb));
}

Action *
XCodecEncoderPipe::output(EventCallback *cb)
{
	ASSERT(output_action_ == NULL);
	ASSERT(output_callback_ == NULL);

	ASSERT(output_callback_ == NULL || output_action_ == NULL);

	if (!input_buffer_.empty() || input_eos_) {
		if (input_eos_) {
			cb->event(Event(Event::EOS, 0, input_buffer_));
			if (!input_buffer_.empty())
				input_buffer_.clear();
		} else {
			cb->event(Event(Event::Done, 0, input_buffer_));
			input_buffer_.clear();
		}

		return (EventSystem::instance()->schedule(cb));
	}

	ASSERT(output_callback_ == NULL || output_action_ == NULL);
	output_callback_ = cb;
	ASSERT(output_callback_ == NULL || output_action_ == NULL);

	return (cancellation(this, &XCodecEncoderPipe::output_cancel));
}

void
XCodecEncoderPipe::output_ready(void)
{
	if (input_eos_) {
		if (input_buffer_.empty()) {
			ERROR("/xcodec/encoder/pipe") << "Ignoring spontaneous output after EOS.";
			return;
		}
		DEBUG("/xcodec/encoder/pipe") << "Retrieving spontaneous data along with buffered data at EOS.";
	}

	Buffer empty;

	encoder_.encode(&input_buffer_, &empty);
	ASSERT(!input_buffer_.empty());

	if (output_callback_ != NULL) {
		/*
		 * If EOS has come, we can only get here if it is being serviced
		 * and there is data pending to be pushed before EOS.  Make sure
		 * that's the case.
		 */
		ASSERT(!input_eos_);

		ASSERT(output_callback_ == NULL || output_action_ == NULL);
		ASSERT(output_action_ == NULL);
		ASSERT(output_callback_ == NULL || output_action_ == NULL);

		output_callback_->event(Event(Event::Done, 0, input_buffer_));
		input_buffer_.clear();
		output_action_ = EventSystem::instance()->schedule(output_callback_);
		output_callback_ = NULL;
		ASSERT(output_callback_ == NULL || output_action_ == NULL);
	}
}

void
XCodecEncoderPipe::output_cancel(void)
{
	ASSERT(output_callback_ == NULL || output_action_ == NULL);
	if (output_action_ != NULL) {
		ASSERT(output_callback_ == NULL);

		output_action_->cancel();
		output_action_ = NULL;
	}

	ASSERT(output_callback_ == NULL || output_action_ == NULL);
	if (output_callback_ != NULL) {
		delete output_callback_;
		output_callback_ = NULL;
	}
	ASSERT(output_callback_ == NULL || output_action_ == NULL);
}
<commit_msg>Remove overly-enthusiastic assertions, bug was tracked down.<commit_after>#include <common/buffer.h>

#include <event/action.h>
#include <event/callback.h>
#include <event/event_system.h>

#include <io/pipe.h>

#include <xcodec/xcodec.h>
#include <xcodec/xcodec_encoder.h>
#include <xcodec/xcodec_encoder_pipe.h>

XCodecEncoderPipe::XCodecEncoderPipe(XCodec *codec)
: encoder_(codec),
  input_buffer_(),
  input_eos_(false),
  output_action_(NULL),
  output_callback_(NULL)
{
	encoder_.set_pipe(this);
}

XCodecEncoderPipe::~XCodecEncoderPipe()
{
	ASSERT(output_action_ == NULL);
	ASSERT(output_callback_ == NULL);

	encoder_.set_pipe(NULL);
}

Action *
XCodecEncoderPipe::input(Buffer *buf, EventCallback *cb)
{
	if (output_callback_ != NULL) {
		ASSERT(input_buffer_.empty());
		ASSERT(output_action_ == NULL);

		if (!buf->empty()) {
			Buffer tmp;

			encoder_.encode(&tmp, buf);
			ASSERT(buf->empty());

			output_callback_->event(Event(Event::Done, 0, tmp));
		} else {
			input_eos_ = true;
			output_callback_->event(Event(Event::EOS, 0));
		}
		output_action_ = EventSystem::instance()->schedule(output_callback_);
		output_callback_ = NULL;
	} else {
		if (!buf->empty()) {
			encoder_.encode(&input_buffer_, buf);
			ASSERT(buf->empty());
		} else {
			input_eos_ = true;
		}
	}

	cb->event(Event(Event::Done, 0));
	return (EventSystem::instance()->schedule(cb));
}

Action *
XCodecEncoderPipe::output(EventCallback *cb)
{
	ASSERT(output_action_ == NULL);
	ASSERT(output_callback_ == NULL);

	if (!input_buffer_.empty() || input_eos_) {
		if (input_eos_) {
			cb->event(Event(Event::EOS, 0, input_buffer_));
			if (!input_buffer_.empty())
				input_buffer_.clear();
		} else {
			cb->event(Event(Event::Done, 0, input_buffer_));
			input_buffer_.clear();
		}

		return (EventSystem::instance()->schedule(cb));
	}

	output_callback_ = cb;

	return (cancellation(this, &XCodecEncoderPipe::output_cancel));
}

void
XCodecEncoderPipe::output_ready(void)
{
	if (input_eos_) {
		if (input_buffer_.empty()) {
			ERROR("/xcodec/encoder/pipe") << "Ignoring spontaneous output after EOS.";
			return;
		}
		DEBUG("/xcodec/encoder/pipe") << "Retrieving spontaneous data along with buffered data at EOS.";
	}

	Buffer empty;

	encoder_.encode(&input_buffer_, &empty);
	ASSERT(!input_buffer_.empty());

	if (output_callback_ != NULL) {
		/*
		 * If EOS has come, we can only get here if it is being serviced
		 * and there is data pending to be pushed before EOS.  Make sure
		 * that's the case.
		 */
		ASSERT(!input_eos_);

		ASSERT(output_action_ == NULL);

		output_callback_->event(Event(Event::Done, 0, input_buffer_));
		input_buffer_.clear();
		output_action_ = EventSystem::instance()->schedule(output_callback_);
		output_callback_ = NULL;
	}
}

void
XCodecEncoderPipe::output_cancel(void)
{
	if (output_action_ != NULL) {
		ASSERT(output_callback_ == NULL);

		output_action_->cancel();
		output_action_ = NULL;
	}

	if (output_callback_ != NULL) {
		delete output_callback_;
		output_callback_ = NULL;
	}
}
<|endoftext|>