<commit_before>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/foreach.hh>

#include "kernel/userver.hh"

#include "object/urbi-exception.hh"

#include "scheduler/scheduler.hh"
#include "scheduler/job.hh"

namespace scheduler
{

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job (void* job)
  {
    static_cast<Job*>(job)->run();
  }

  void
  Scheduler::add_job (Job* job)
  {
    assert (job);
    assert (!libport::has (jobs_, job));
    jobs_.push_back (job);
    jobs_to_start_ = true;
  }

  libport::utime_t
  Scheduler::work ()
  {
#ifdef ENABLE_DEBUG_TRACES
    static int cycle = 0;
#endif
    ECHO ("======================================================== cycle "
	  << ++cycle);

    libport::utime_t deadline = execute_round (false);

    // If some jobs need to be stopped, do it as soon as possible.
    deadline = std::min (deadline, check_for_stopped_tags (deadline));

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO ("Scheduler asking to be woken up in "
	    << (deadline - ::urbiserver->getTime ()) / 1000000L << " seconds");
    else
      ECHO ("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  libport::utime_t
  Scheduler::execute_round (bool blocked_only)
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear ();
    std::swap (pending_, jobs_);

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t deadline = ::urbiserver->getTime () +  3600000000LL;
    jobs_to_start_ = false;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;

    ECHO (pending_.size() << " jobs in the queue for this round");
    foreach (Job* job, pending_)
    {
      // Kill a job if needed. See explanation in job.hh.
      to_kill_ = 0;

      assert (job);
      assert (!job->terminated ());

      // Should the job be started?
      bool start = false;

      ECHO ("Considering " << *job << " in state " << state_name (job->state_get ()));

      switch (job->state_get ())
      {
      case to_start:
	// New job. Start its coroutine but do not start the job as it would be queued
	// twice otherwise. It will start doing real work at the next cycle, so set
	// deadline to 0. Note that we use "continue" here to avoid having the job
	// requeued because it hasn't been started by setting "start".
	ECHO ("Starting job " << *job);
	current_job_ = job;
	Coro_startCoro_ (self_, job->coro_get(), job, run_job);
	current_job_ = 0;
	ECHO ("Job " << *job << " has been started");
	assert (job->state_get () != to_start);
	deadline = 0;
	continue;
      case zombie:
	assert (false);
	break;
      case running:
	start = !blocked_only || job->blocked ();
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get ();
	  if (job_deadline <= ::urbiserver->getTime ())
	    start = true;
	  else
	    deadline = std::min (deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting | possible_side_effect_;
	break;
      case joining:
	break;
      }

      if (start)
      {
	ECHO ("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get ();
	assert (!current_job_);
	Coro_switchTo_ (self_, job->coro_get ());
	assert (!current_job_);
	possible_side_effect_ |= !job->side_effect_free_get ();
	ECHO ("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get ())
	{
	case running:
	  deadline = 0;
	  break;
	case sleeping:
	  deadline = std::min (deadline, job->deadline_get ());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back (job);   // Job not started, keep it in queue
    }

    // Kill a job if needed. See explanation in job.hh.
    to_kill_ = 0;

    /// If during this cycle a new job has been created by an existing job,
    /// start it.
    if (jobs_to_start_)
      deadline = 0;

    return deadline;
  }

  libport::utime_t
  Scheduler::check_for_stopped_tags (libport::utime_t old_deadline)
  {
    bool blocked_job = false;

    // If we have had no stopped tag, return immediately.
    if (stopped_tags_.empty ())
      return old_deadline;

    // If some jobs have been blocked, mark them as running so that they will
    // handle the condition when they are resumed.
    foreach (Job* job, jobs_)
      if (job->blocked ())
      {
	job->state_set (running);
	blocked_job = true;
      }

    // Wake up blocked jobs.
    libport::utime_t deadline = execute_round (true);

    // Reset tags to their real blocked value and reset the list.
    foreach (tag_state_type t, stopped_tags_)
      t.first->set_blocked (t.second);
    stopped_tags_.clear ();

    return deadline;
  }

  void
  Scheduler::switch_back (Job* job)
  {
    // Switch back to the scheduler.
    assert (current_job_ == job);
    current_job_ = 0;
    Coro_switchTo_ (job->coro_get (), self_);
    // We regained control, we are again in the context of the job.
    assert (!current_job_);
    current_job_ = job;
    ECHO ("job " << *job << " resumed");
    // Check that we are not near exhausting the stack space.
    job->check_stack_space ();
    // Execute a deferred exception if any
    job->check_for_pending_exception ();
    // If we are in frozen state, let's requeue ourselves waiting
    // for something to change. And let's mark us side-effect
    // free during this time so that we won't cause other jobs
    // to be scheduled. Of course, we have to check for pending
    // exceptions each time we are woken up.
    if (job->frozen ())
    {
      bool side_effect_free_save = job->side_effect_free_get ();
      do {
	job->side_effect_free_set (true);
	job->yield_until_things_changed ();
	job->side_effect_free_set (side_effect_free_save);
	// Execute a deferred exception if any
	job->check_for_pending_exception ();
      } while (job->frozen ());
    }
  }

  void
  Scheduler::resume_scheduler (Job* job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    if (job->state_get () == running && job->side_effect_free_get ())
      return;

    if (!job->terminated ())
      jobs_.push_back (job);

    ECHO (*job << " has " << (job->terminated () ? "" : "not ") << "terminated\n\t"
	  << "state: " << state_name (job->state_get ()));
    switch_back (job);
  }

  void
  Scheduler::killall_jobs ()
  {
    ECHO ("killing all jobs!");

    foreach (Job* job, jobs_)
      kill_job (job);

    foreach (Job* job, pending_)
      kill_job (job);
  }

  void
  Scheduler::unschedule_job (Job* job)
  {
    assert (job);
    assert (job != current_job_);

    ECHO ("unscheduling job " << *job);

    // Remove the job from the queue.
    jobs_.remove (job);

    // Remove it from live queues as well if the job is destroyed.
    pending_.remove (job);
  }

  void
  Scheduler::kill_job (Job* job)
  {
    KillException ke;
    job->async_throw (ke);
  }

  void Scheduler::signal_stop (rTag t)
  {
    bool previous_state = t->own_blocked ();
    t->set_blocked (true);
    stopped_tags_.push_back (std::make_pair(t, previous_state));
  }

} // namespace scheduler
<commit_msg>Do not resume a terminated job in any case<commit_after>/**
 ** \file scheduler/scheduler.cc
 ** \brief Implementation of scheduler::Scheduler.
 */

//#define ENABLE_DEBUG_TRACES

#include <cassert>
#include <cstdlib>

#include <libport/compiler.hh>
#include <libport/containers.hh>
#include <libport/foreach.hh>

#include "kernel/userver.hh"

#include "object/urbi-exception.hh"

#include "scheduler/scheduler.hh"
#include "scheduler/job.hh"

namespace scheduler
{

  // This function is required to start a new job using the libcoroutine.
  // Its only purpose is to create the context and start the execution
  // of the new job.
  static void
  run_job (void* job)
  {
    static_cast<Job*>(job)->run();
  }

  void
  Scheduler::add_job (Job* job)
  {
    assert (job);
    assert (!libport::has (jobs_, job));
    jobs_.push_back (job);
    jobs_to_start_ = true;
  }

  libport::utime_t
  Scheduler::work ()
  {
#ifdef ENABLE_DEBUG_TRACES
    static int cycle = 0;
#endif
    ECHO ("======================================================== cycle "
	  << ++cycle);

    libport::utime_t deadline = execute_round (false);

    // If some jobs need to be stopped, do it as soon as possible.
    deadline = std::min (deadline, check_for_stopped_tags (deadline));

#ifdef ENABLE_DEBUG_TRACES
    if (deadline)
      ECHO ("Scheduler asking to be woken up in "
	    << (deadline - ::urbiserver->getTime ()) / 1000000L << " seconds");
    else
      ECHO ("Scheduler asking to be woken up ASAP");
#endif

    return deadline;
  }

  libport::utime_t
  Scheduler::execute_round (bool blocked_only)
  {
    // Run all the jobs in the run queue once. If any job declares upon entry or
    // return that it is not side-effect free, we remember that for the next
    // cycle.
    pending_.clear ();
    std::swap (pending_, jobs_);

    // By default, wake us up after one hour and consider that we have no
    // new job to start. Also, run waiting jobs only if the previous round
    // may have add a side effect and reset this indication for the current
    // job.
    libport::utime_t deadline = ::urbiserver->getTime () +  3600000000LL;
    jobs_to_start_ = false;
    bool start_waiting = possible_side_effect_;
    possible_side_effect_ = false;

    ECHO (pending_.size() << " jobs in the queue for this round");
    foreach (Job* job, pending_)
    {
      // Kill a job if needed. See explanation in job.hh.
      to_kill_ = 0;

      assert (job);
      assert (!job->terminated ());

      // Should the job be started?
      bool start = false;

      ECHO ("Considering " << *job << " in state " << state_name (job->state_get ()));

      switch (job->state_get ())
      {
      case to_start:
	// New job. Start its coroutine but do not start the job as it would be queued
	// twice otherwise. It will start doing real work at the next cycle, so set
	// deadline to 0. Note that we use "continue" here to avoid having the job
	// requeued because it hasn't been started by setting "start".
	ECHO ("Starting job " << *job);
	current_job_ = job;
	Coro_startCoro_ (self_, job->coro_get(), job, run_job);
	current_job_ = 0;
	ECHO ("Job " << *job << " has been started");
	assert (job->state_get () != to_start);
	deadline = 0;
	continue;
      case zombie:
	assert (false);
	break;
      case running:
	start = !blocked_only || job->blocked ();
	break;
      case sleeping:
	{
	  libport::utime_t job_deadline = job->deadline_get ();
	  if (job_deadline <= ::urbiserver->getTime ())
	    start = true;
	  else
	    deadline = std::min (deadline, job_deadline);
	}
	break;
      case waiting:
	// Since jobs keep their orders in the queue, start waiting jobs if
	// previous jobs in the run have had a possible side effect or if
	// the previous run may have had some. Without it, we may miss some
	// changes if the watching job is after the modifying job in the queue
	// and the watched condition gets true for only one cycle.
	start = start_waiting | possible_side_effect_;
	break;
      case joining:
	break;
      }

      if (start)
      {
	ECHO ("will resume job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	possible_side_effect_ |= !job->side_effect_free_get ();
	assert (!current_job_);
	Coro_switchTo_ (self_, job->coro_get ());
	assert (!current_job_);
	possible_side_effect_ |= !job->side_effect_free_get ();
	ECHO ("back from job " << *job
	      << (job->side_effect_free_get() ? " (side-effect free)" : ""));
	switch (job->state_get ())
	{
	case running:
	  deadline = 0;
	  break;
	case sleeping:
	  deadline = std::min (deadline, job->deadline_get ());
	  break;
	default:
	  break;
	}
      }
      else
	jobs_.push_back (job);   // Job not started, keep it in queue
    }

    // Kill a job if needed. See explanation in job.hh.
    to_kill_ = 0;

    /// If during this cycle a new job has been created by an existing job,
    /// start it.
    if (jobs_to_start_)
      deadline = 0;

    return deadline;
  }

  libport::utime_t
  Scheduler::check_for_stopped_tags (libport::utime_t old_deadline)
  {
    bool blocked_job = false;

    // If we have had no stopped tag, return immediately.
    if (stopped_tags_.empty ())
      return old_deadline;

    // If some jobs have been blocked, mark them as running so that they will
    // handle the condition when they are resumed.
    foreach (Job* job, jobs_)
      if (job->blocked ())
      {
	job->state_set (running);
	blocked_job = true;
      }

    // Wake up blocked jobs.
    libport::utime_t deadline = execute_round (true);

    // Reset tags to their real blocked value and reset the list.
    foreach (tag_state_type t, stopped_tags_)
      t.first->set_blocked (t.second);
    stopped_tags_.clear ();

    return deadline;
  }

  void
  Scheduler::switch_back (Job* job)
  {
    // Switch back to the scheduler.
    assert (current_job_ == job);
    current_job_ = 0;
    Coro_switchTo_ (job->coro_get (), self_);
    // We regained control, we are again in the context of the job.
    assert (!current_job_);
    current_job_ = job;
    ECHO ("job " << *job << " resumed");
    // Check that we are not near exhausting the stack space.
    job->check_stack_space ();
    // Execute a deferred exception if any
    job->check_for_pending_exception ();
    // If we are in frozen state, let's requeue ourselves waiting
    // for something to change. And let's mark us side-effect
    // free during this time so that we won't cause other jobs
    // to be scheduled. Of course, we have to check for pending
    // exceptions each time we are woken up.
    if (job->frozen ())
    {
      bool side_effect_free_save = job->side_effect_free_get ();
      do {
	job->side_effect_free_set (true);
	job->yield_until_things_changed ();
	job->side_effect_free_set (side_effect_free_save);
	// Execute a deferred exception if any
	job->check_for_pending_exception ();
      } while (job->frozen ());
    }
  }

  void
  Scheduler::resume_scheduler (Job* job)
  {
    // If the job has not terminated and is side-effect free, then we
    // assume it will not take a long time as we are probably evaluating
    // a condition. In order to reduce the number of cycles spent to evaluate
    // the condition, continue until it asks to be suspended in another
    // way or until it is no longer side-effect free.

    if (!job->terminated ())
      {
	if (job->state_get () == running && job->side_effect_free_get ())
	  return;
	else
	  jobs_.push_back (job);
      }

    ECHO (*job << " has " << (job->terminated () ? "" : "not ") << "terminated\n\t"
	  << "state: " << state_name (job->state_get ()));
    switch_back (job);
  }

  void
  Scheduler::killall_jobs ()
  {
    ECHO ("killing all jobs!");

    foreach (Job* job, jobs_)
      kill_job (job);

    foreach (Job* job, pending_)
      kill_job (job);
  }

  void
  Scheduler::unschedule_job (Job* job)
  {
    assert (job);
    assert (job != current_job_);

    ECHO ("unscheduling job " << *job);

    // Remove the job from the queue.
    jobs_.remove (job);

    // Remove it from live queues as well if the job is destroyed.
    pending_.remove (job);
  }

  void
  Scheduler::kill_job (Job* job)
  {
    KillException ke;
    job->async_throw (ke);
  }

  void Scheduler::signal_stop (rTag t)
  {
    bool previous_state = t->own_blocked ();
    t->set_blocked (true);
    stopped_tags_.push_back (std::make_pair(t, previous_state));
  }

} // namespace scheduler
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// HEADER
#include <dali-adaptor-version.h>

// EXTERNAL INCLUDES
#ifdef DEBUG_ENABLED
#include <iostream>
#endif

namespace Dali
{

const unsigned int ADAPTOR_MAJOR_VERSION = 1;
const unsigned int ADAPTOR_MINOR_VERSION = 1;
const unsigned int ADAPTOR_MICRO_VERSION = 17;
const char * const ADAPTOR_BUILD_DATE    = __DATE__ " " __TIME__;

#ifdef DEBUG_ENABLED
namespace
{
/// Allows the printing of the version number ONLY when debug is enabled
struct PrintVersion
{
  PrintVersion()
  {
    std::cout << "DALi Adaptor:   " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << " (" << ADAPTOR_BUILD_DATE << ")" << std::endl;
  }
};
PrintVersion ADAPTOR_VERSION;
} // unnamed namespace
#endif

} // namespace Dali
<commit_msg>Reporting dali version on stderr instead of stdout<commit_after>/*
 * Copyright (c) 2015 Samsung Electronics Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

// HEADER
#include <dali-adaptor-version.h>

// EXTERNAL INCLUDES
#ifdef DEBUG_ENABLED
#include <iostream>
#endif

namespace Dali
{

const unsigned int ADAPTOR_MAJOR_VERSION = 1;
const unsigned int ADAPTOR_MINOR_VERSION = 1;
const unsigned int ADAPTOR_MICRO_VERSION = 17;
const char * const ADAPTOR_BUILD_DATE    = __DATE__ " " __TIME__;

#ifdef DEBUG_ENABLED
namespace
{
/// Allows the printing of the version number ONLY when debug is enabled
struct PrintVersion
{
  PrintVersion()
  {
    std::cerr << "DALi Adaptor:   " << ADAPTOR_MAJOR_VERSION << "." << ADAPTOR_MINOR_VERSION << "." << ADAPTOR_MICRO_VERSION << " (" << ADAPTOR_BUILD_DATE << ")" << std::endl;
  }
};
PrintVersion ADAPTOR_VERSION;
} // unnamed namespace
#endif

} // namespace Dali
<|endoftext|>


<commit_before>#include <catch.hpp>

#include <regex>
#include "test_data.hpp"
#include "libslic3r.h"

using namespace Slic3r::Test;

std::regex perimeters_regex("^G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter[ ]*$");
std::regex infill_regex("^G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill[ ]*$");
std::regex skirt_regex("^G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt[ ]*$");

SCENARIO( "PrintGCode basic functionality", "[!mayfail]") {
    GIVEN("A default configuration and a print test object") {
        auto config {Slic3r::Config::new_from_defaults()};
        auto gcode {std::stringstream("")};

        WHEN("the output is executed with no support material") {
            config->set("first_layer_extrusion_width", 0);
            config->set("gcode_comments", true);
            Slic3r::Model model;
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            print->process();
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains slic3r version") {
                REQUIRE(exported.find(SLIC3R_VERSION) != std::string::npos);
            }
            THEN("Exported text contains git commit id") {
                REQUIRE(exported.find("; Git Commit") != std::string::npos);
                REQUIRE(exported.find(BUILD_COMMIT) != std::string::npos);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
            }

            THEN("GCode preamble is emitted.") {
                REQUIRE(exported.find("G21 ; set units to millimeters") != std::string::npos);
            }

            THEN("Config options emitted for print config, default region config, default object config") {
                REQUIRE(exported.find("; first_layer_temperature") != std::string::npos);
                REQUIRE(exported.find("; layer_height") != std::string::npos);
                REQUIRE(exported.find("; fill_density") != std::string::npos);
            }
            THEN("Infill is emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, infill_regex));
            }
            THEN("Perimeters are emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, perimeters_regex));
            }
            THEN("Skirt is emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, skirt_regex));
            }
        }
        WHEN("the output is executed with support material") {
            Slic3r::Model model;
            config->set("first_layer_extrusion_width", 0);
            config->set("support_material", true);
            config->set("raft_layers", 3);
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") != std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
            }
        }
        WHEN("the output is executed with a separate first layer extrusion width") {
            Slic3r::Model model;
            config->set("first_layer_extrusion_width", 0.5);
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") != std::string::npos);
            }
        }
        WHEN("Cooling is enabled and the fan is disabled.") {
            config->set("cooling", true);
            config->set("disable_fan_first_layers", 5);
            Slic3r::Model model;
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("GCode to disable fan is emitted."){
                REQUIRE(exported.find("M107") != std::string::npos);
            }
        }

        gcode.clear();
    }
}
<commit_msg>Fixed regex to match lines.<commit_after>#include <catch.hpp>

#include <regex>
#include "test_data.hpp"
#include "libslic3r.h"

using namespace Slic3r::Test;

std::regex perimeters_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; perimeter");
std::regex infill_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; infill");
std::regex skirt_regex("G1 X[-0-9.]* Y[-0-9.]* E[-0-9.]* ; skirt");

SCENARIO( "PrintGCode basic functionality", "[!mayfail]") {
    GIVEN("A default configuration and a print test object") {
        auto config {Slic3r::Config::new_from_defaults()};
        auto gcode {std::stringstream("")};

        WHEN("the output is executed with no support material") {
            config->set("first_layer_extrusion_width", 0);
            config->set("gcode_comments", true);
            Slic3r::Model model;
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            print->process();
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains slic3r version") {
                REQUIRE(exported.find(SLIC3R_VERSION) != std::string::npos);
            }
            THEN("Exported text contains git commit id") {
                REQUIRE(exported.find("; Git Commit") != std::string::npos);
                REQUIRE(exported.find(BUILD_COMMIT) != std::string::npos);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
            }

            THEN("GCode preamble is emitted.") {
                REQUIRE(exported.find("G21 ; set units to millimeters") != std::string::npos);
            }

            THEN("Config options emitted for print config, default region config, default object config") {
                REQUIRE(exported.find("; first_layer_temperature") != std::string::npos);
                REQUIRE(exported.find("; layer_height") != std::string::npos);
                REQUIRE(exported.find("; fill_density") != std::string::npos);
            }
            THEN("Infill is emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, infill_regex));
            }
            THEN("Perimeters are emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, perimeters_regex));
            }
            THEN("Skirt is emitted.") {
                std::smatch has_match;
                REQUIRE(std::regex_search(exported, has_match, skirt_regex));
            }
        }
        WHEN("the output is executed with support material") {
            Slic3r::Model model;
            config->set("first_layer_extrusion_width", 0);
            config->set("support_material", true);
            config->set("raft_layers", 3);
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") != std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") == std::string::npos);
            }
        }
        WHEN("the output is executed with a separate first layer extrusion width") {
            Slic3r::Model model;
            config->set("first_layer_extrusion_width", 0.5);
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("Some text output is generated.") {
                REQUIRE(exported.size() > 0);
            }
            THEN("Exported text contains extrusion statistics.") {
                REQUIRE(exported.find("; external perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; perimeters extrusion width") != std::string::npos);
                REQUIRE(exported.find("; infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; top solid infill extrusion width") != std::string::npos);
                REQUIRE(exported.find("; support material extrusion width") == std::string::npos);
                REQUIRE(exported.find("; first layer extrusion width") != std::string::npos);
            }
        }
        WHEN("Cooling is enabled and the fan is disabled.") {
            config->set("cooling", true);
            config->set("disable_fan_first_layers", 5);
            Slic3r::Model model;
            auto print {Slic3r::Test::init_print({TestMesh::cube_20x20x20}, model, config)};
            Slic3r::Test::gcode(gcode, print);
            auto exported {gcode.str()};
            THEN("GCode to disable fan is emitted."){
                REQUIRE(exported.find("M107") != std::string::npos);
            }
        }

        gcode.clear();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018 The Project Oak Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "oak/server/oak_node.h"

#include <openssl/sha.h>

#include <iostream>
#include <utility>

#include "absl/memory/memory.h"
#include "asylo/util/logging.h"
#include "oak/server/buffer_channel.h"
#include "oak/server/logging_channel.h"
#include "oak/server/wabt_output.h"
#include "src/binary-reader.h"
#include "src/error-formatter.h"
#include "src/error.h"
#include "src/interp/binary-reader-interp.h"

namespace oak {

// From https://github.com/WebAssembly/wabt/blob/master/src/tools/wasm-interp.cc .

static std::unique_ptr<wabt::FileStream> s_log_stream = wabt::FileStream::CreateStdout();
static std::unique_ptr<wabt::FileStream> s_stdout_stream = wabt::FileStream::CreateStdout();

static absl::Span<const char> ReadMemory(wabt::interp::Environment* env, const uint32_t offset,
                                         const uint32_t size) {
  return absl::MakeConstSpan(env->GetMemory(0)->data).subspan(offset, size);
}

static const std::string ReadString(wabt::interp::Environment* env, const uint32_t offset,
                                    const uint32_t size) {
  absl::Span<const char> memory = ReadMemory(env, offset, size);
  return std::string(memory.cbegin(), memory.cend());
}

static void WriteMemory(wabt::interp::Environment* env, const uint32_t offset,
                        const absl::Span<const char> data) {
  std::copy(data.cbegin(), data.cend(), env->GetMemory(0)->data.begin() + offset);
}

// Creates a TypedValue of type i64 with the specified inner value.
static wabt::interp::TypedValue MakeI64(uint64_t v) {
  wabt::interp::TypedValue tv(wabt::Type::I64);
  tv.set_i64(v);
  return tv;
}

static void LogHostFunctionCall(const wabt::interp::HostFunc* func,
                                const wabt::interp::TypedValues& args) {
  LOG(INFO) << "Called host function: " << func->module_name << "." << func->field_name;
  for (auto const& arg : args) {
    LOG(INFO) << "Arg: " << wabt::interp::TypedValueToString(arg);
  }
}

static wabt::Result ReadModule(const std::string module_bytes, wabt::interp::Environment* env,
                               wabt::Errors* errors, wabt::interp::DefinedModule** out_module) {
  LOG(INFO) << "Reading module";
  wabt::Result result;

  *out_module = nullptr;

  wabt::Features s_features;
  const bool kReadDebugNames = true;
  const bool kStopOnFirstError = true;
  const bool kFailOnCustomSectionError = true;
  wabt::Stream* log_stream = nullptr;
  wabt::ReadBinaryOptions options(s_features, log_stream, kReadDebugNames, kStopOnFirstError,
                                  kFailOnCustomSectionError);

  result = wabt::ReadBinaryInterp(env, module_bytes.data(), module_bytes.size(), options, errors,
                                  out_module);

  if (Succeeded(result)) {
    // env->DisassembleModule(s_stdout_stream.get(), *out_module);
  }

  return result;
}

// Describes an expected export from an Oak module.
struct RequiredExport {
  std::string name_;
  bool mandatory_;
  wabt::interp::FuncSignature sig_;
};

}  // namespace oak

std::ostream& operator<<(std::ostream& os, const oak::RequiredExport& r) {
  return os << (r.mandatory_ ? "required" : "optional") << " export '" << r.name_
            << "' with signature " << &r.sig_;
}

namespace oak {

const RequiredExport kRequiredExports[] = {
    {"oak_initialize", true, {}},
    {"oak_handle_grpc_call", true, {}},
    {"oak_finalize", false, {}},
};

// Check module exports all required functions with the correct signatures,
// returning true if so.
static bool CheckModuleExport(wabt::interp::Environment* env, wabt::interp::DefinedModule* module,
                              const RequiredExport& req) {
  LOG(INFO) << "check for " << req;
  wabt::interp::Export* exp = module->GetExport(req.name_);
  if (exp == nullptr) {
    if (req.mandatory_) {
      LOG(WARNING) << "Could not find required export '" << req.name_ << "' in module";
      return false;
    }
    LOG(INFO) << "optional import '" << req.name_ << "' missing";
    return true;
  }
  if (exp->kind != wabt::ExternalKind::Func) {
    LOG(WARNING) << "Required export of kind " << exp->kind << " not func in module";
    return false;
  }
  LOG(INFO) << "check signature of function #" << exp->index;
  wabt::interp::Func* func = env->GetFunc(exp->index);
  if (func == nullptr) {
    LOG(WARNING) << "failed to retrieve function #" << exp->index;
    return false;
  }
  if (func->sig_index >= env->GetFuncSignatureCount()) {
    LOG(WARNING) << "Function #" << func->sig_index << " beyond range of signature types";
    return false;
  }
  wabt::interp::FuncSignature* sig = env->GetFuncSignature(func->sig_index);
  if (sig == nullptr) {
    LOG(WARNING) << "Could not find signature for function #" << exp->index;
    return false;
  }
  LOG(INFO) << "function #" << exp->index << " has type #" << func->sig_index << " with signature "
            << *sig;
  if ((sig->param_types != req.sig_.param_types) || (sig->result_types != req.sig_.result_types)) {
    LOG(WARNING) << "Function signature mismatch for " << req.name_ << ": got " << *sig << ", want "
                 << req.sig_;
    return false;
  }
  return true;
}
static bool CheckModuleExports(wabt::interp::Environment* env,
                               wabt::interp::DefinedModule* module) {
  bool rc = true;
  for (const RequiredExport& req : kRequiredExports) {
    if (!CheckModuleExport(env, module, req)) {
      rc = false;
    }
  }
  return rc;
}

std::string Sha256Hash(const std::string& data) {
  SHA256_CTX context;
  SHA256_Init(&context);
  SHA256_Update(&context, data.data(), data.size());
  std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH);
  SHA256_Final(hash.data(), &context);
  return std::string(hash.cbegin(), hash.cend());
}

OakNode::OakNode(const std::string& node_id, const std::string& module)
    : Service(), node_id_(node_id), module_hash_sha_256_(Sha256Hash(module)) {}

std::unique_ptr<OakNode> OakNode::Create(const std::string& node_id, const std::string& module) {
  LOG(INFO) << "Creating Oak Node";

  std::unique_ptr<OakNode> node = absl::WrapUnique(new OakNode(node_id, module));
  node->InitEnvironment(&node->env_);
  LOG(INFO) << "Host func count: " << node->env_.GetFuncCount();

  wabt::Errors errors;
  LOG(INFO) << "Reading module";
  wabt::Result result = ReadModule(module, &node->env_, &errors, &node->module_);
  if (!wabt::Succeeded(result)) {
    LOG(WARNING) << "Could not read module: " << result;
    LOG(WARNING) << "Errors: " << wabt::FormatErrorsToString(errors, wabt::Location::Type::Binary);
    return nullptr;
  }

  LOG(INFO) << "Reading module done";
  if (!CheckModuleExports(&node->env_, node->module_)) {
    LOG(WARNING) << "Failed to validate module";
    return nullptr;
  }

  wabt::interp::Thread::Options thread_options;
  // wabt::Stream* trace_stream = s_stdout_stream.get();
  wabt::Stream* trace_stream = nullptr;
  wabt::interp::Executor executor(&node->env_, trace_stream, thread_options);
  LOG(INFO) << "Executing module";

  // Create a logging channel to allow the module to log during initialization.
  std::unique_ptr<Channel> logging_channel = absl::make_unique<LoggingChannel>();
  node->channels_[LOGGING_CHANNEL_HANDLE] = std::move(logging_channel);
  LOG(INFO) << "Created logging channel";

  wabt::interp::TypedValues args;
  wabt::interp::ExecResult exec_result =
      executor.RunExportByName(node->module_, "oak_initialize", args);

  // Drop all channels used in the current invocation.
  node->channels_ = std::unordered_map<Handle, std::unique_ptr<Channel>>();

  if (exec_result.result != wabt::interp::Result::Ok) {
    LOG(WARNING) << "Could not execute module";
    wabt::interp::WriteResult(s_stdout_stream.get(), "error", exec_result.result);
    // TODO: Print error.
    return nullptr;
  }

  LOG(INFO) << "Executed module";
  return node;
}

// Register all available host functions so that they are available to the Oak Module at runtime.
void OakNode::InitEnvironment(wabt::interp::Environment* env) {
  wabt::interp::HostModule* oak_module = env->AppendHostModule("oak");
  oak_module->AppendFuncExport(
      "channel_read",
      wabt::interp::FuncSignature(
          std::vector<wabt::Type>{wabt::Type::I64, wabt::Type::I32, wabt::Type::I32},
          std::vector<wabt::Type>{wabt::Type::I32}),
      this->OakChannelRead(env));
  oak_module->AppendFuncExport(
      "channel_write",
      wabt::interp::FuncSignature(
          std::vector<wabt::Type>{wabt::Type::I64, wabt::Type::I32, wabt::Type::I32},
          std::vector<wabt::Type>{wabt::Type::I32}),
      this->OakChannelWrite(env));
}

wabt::interp::HostFunc::Callback OakNode::OakChannelRead(wabt::interp::Environment* env) {
  return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature* sig,
                     const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) {
    LogHostFunctionCall(func, args);

    Handle channel_handle = args[0].get_i64();
    uint32_t offset = args[1].get_i32();
    uint32_t size = args[2].get_i32();

    if (channels_.count(channel_handle) == 0) {
      LOG(WARNING) << "Invalid channel handle: " << channel_handle;
      return wabt::interp::Result::Ok;
    }
    std::unique_ptr<Channel>& channel = channels_.at(channel_handle);

    absl::Span<const char> data = channel->Read(size);
    WriteMemory(env, offset, data);
    results[0].set_i32(data.size());

    return wabt::interp::Result::Ok;
  };
}

wabt::interp::HostFunc::Callback OakNode::OakChannelWrite(wabt::interp::Environment* env) {
  return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature* sig,
                     const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) {
    LogHostFunctionCall(func, args);

    Handle channel_handle = args[0].get_i64();
    uint32_t offset = args[1].get_i32();
    uint32_t size = args[2].get_i32();

    if (channels_.count(channel_handle) == 0) {
      LOG(WARNING) << "Invalid channel handle: " << channel_handle;
      return wabt::interp::Result::Ok;
    }
    std::unique_ptr<Channel>& channel = channels_.at(channel_handle);

    absl::Span<const char> data = ReadMemory(env, offset, size);
    channel->Write(data);
    results[0].set_i32(data.size());

    return wabt::interp::Result::Ok;
  };
}

grpc::Status OakNode::ProcessModuleInvocation(grpc::GenericServerContext* context,
                                              const std::vector<char>& request_data,
                                              std::vector<char>* response_data) {
  LOG(INFO) << "Handling gRPC call: " << context->method();
  server_context_ = context;

  // Store a copy of the gRPC method name so that we can refer to it via a Span when the Module
  // tries to read it from a channel; otherwise we would need to allocate a new string every time
  // and ensure it can outlive the Span reference.
  grpc_method_name_ = context->method();

  // Create the gRPC channel, used by the module to perform basic input and output.
  std::unique_ptr<Channel> grpc_channel =
      absl::make_unique<BufferChannel>(request_data, response_data);
  channels_[GRPC_CHANNEL_HANDLE] = std::move(grpc_channel);
  LOG(INFO) << "Created gRPC channel";

  // Create the gRPC method name channel, used by the module to read the gRPC method name from the
  // current context.
  std::unique_ptr<Channel> grpc_method_name_channel =
      absl::make_unique<BufferChannel>(grpc_method_name_, nullptr);
  channels_[GRPC_METHOD_NAME_CHANNEL_HANDLE] = std::move(grpc_method_name_channel);
  LOG(INFO) << "Created gRPC method name channel";

  // Create the logging channel, used by the module to log statements for debugging.
  std::unique_ptr<Channel> logging_channel = absl::make_unique<LoggingChannel>();
  channels_[LOGGING_CHANNEL_HANDLE] = std::move(logging_channel);
  LOG(INFO) << "Created logging channel";

  wabt::Stream* trace_stream = nullptr;
  wabt::interp::Thread::Options thread_options;
  wabt::interp::Executor executor(&env_, trace_stream, thread_options);

  wabt::interp::TypedValues args = {};
  wabt::interp::ExecResult exec_result =
      executor.RunExportByName(module_, "oak_handle_grpc_call", args);

  // Drop all the channels used in the current invocation.
  channels_ = std::unordered_map<Handle, std::unique_ptr<Channel>>();

  if (exec_result.result != wabt::interp::Result::Ok) {
    std::string err = wabt::interp::ResultToString(exec_result.result);
    LOG(ERROR) << "Could not handle gRPC call: " << err;
    return grpc::Status(grpc::StatusCode::INTERNAL, err);
  }
  return grpc::Status::OK;
}

grpc::Status OakNode::GetAttestation(grpc::ServerContext* context,
                                     const GetAttestationRequest* request,
                                     GetAttestationResponse* response) {
  response->set_module_hash_sha_256(module_hash_sha_256_);
  return ::grpc::Status::OK;
}

}  // namespace oak
<commit_msg>Drop unused C++ function oak::ReadString<commit_after>/*
 * Copyright 2018 The Project Oak Authors
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "oak/server/oak_node.h"

#include <openssl/sha.h>

#include <iostream>
#include <utility>

#include "absl/memory/memory.h"
#include "asylo/util/logging.h"
#include "oak/server/buffer_channel.h"
#include "oak/server/logging_channel.h"
#include "oak/server/wabt_output.h"
#include "src/binary-reader.h"
#include "src/error-formatter.h"
#include "src/error.h"
#include "src/interp/binary-reader-interp.h"

namespace oak {

// From https://github.com/WebAssembly/wabt/blob/master/src/tools/wasm-interp.cc .

static std::unique_ptr<wabt::FileStream> s_log_stream = wabt::FileStream::CreateStdout();
static std::unique_ptr<wabt::FileStream> s_stdout_stream = wabt::FileStream::CreateStdout();

static absl::Span<const char> ReadMemory(wabt::interp::Environment* env, const uint32_t offset,
                                         const uint32_t size) {
  return absl::MakeConstSpan(env->GetMemory(0)->data).subspan(offset, size);
}

static void WriteMemory(wabt::interp::Environment* env, const uint32_t offset,
                        const absl::Span<const char> data) {
  std::copy(data.cbegin(), data.cend(), env->GetMemory(0)->data.begin() + offset);
}

// Creates a TypedValue of type i64 with the specified inner value.
static wabt::interp::TypedValue MakeI64(uint64_t v) {
  wabt::interp::TypedValue tv(wabt::Type::I64);
  tv.set_i64(v);
  return tv;
}

static void LogHostFunctionCall(const wabt::interp::HostFunc* func,
                                const wabt::interp::TypedValues& args) {
  LOG(INFO) << "Called host function: " << func->module_name << "." << func->field_name;
  for (auto const& arg : args) {
    LOG(INFO) << "Arg: " << wabt::interp::TypedValueToString(arg);
  }
}

static wabt::Result ReadModule(const std::string module_bytes, wabt::interp::Environment* env,
                               wabt::Errors* errors, wabt::interp::DefinedModule** out_module) {
  LOG(INFO) << "Reading module";
  wabt::Result result;

  *out_module = nullptr;

  wabt::Features s_features;
  const bool kReadDebugNames = true;
  const bool kStopOnFirstError = true;
  const bool kFailOnCustomSectionError = true;
  wabt::Stream* log_stream = nullptr;
  wabt::ReadBinaryOptions options(s_features, log_stream, kReadDebugNames, kStopOnFirstError,
                                  kFailOnCustomSectionError);

  result = wabt::ReadBinaryInterp(env, module_bytes.data(), module_bytes.size(), options, errors,
                                  out_module);

  if (Succeeded(result)) {
    // env->DisassembleModule(s_stdout_stream.get(), *out_module);
  }

  return result;
}

// Describes an expected export from an Oak module.
struct RequiredExport {
  std::string name_;
  bool mandatory_;
  wabt::interp::FuncSignature sig_;
};

}  // namespace oak

std::ostream& operator<<(std::ostream& os, const oak::RequiredExport& r) {
  return os << (r.mandatory_ ? "required" : "optional") << " export '" << r.name_
            << "' with signature " << &r.sig_;
}

namespace oak {

const RequiredExport kRequiredExports[] = {
    {"oak_initialize", true, {}},
    {"oak_handle_grpc_call", true, {}},
    {"oak_finalize", false, {}},
};

// Check module exports all required functions with the correct signatures,
// returning true if so.
static bool CheckModuleExport(wabt::interp::Environment* env, wabt::interp::DefinedModule* module,
                              const RequiredExport& req) {
  LOG(INFO) << "check for " << req;
  wabt::interp::Export* exp = module->GetExport(req.name_);
  if (exp == nullptr) {
    if (req.mandatory_) {
      LOG(WARNING) << "Could not find required export '" << req.name_ << "' in module";
      return false;
    }
    LOG(INFO) << "optional import '" << req.name_ << "' missing";
    return true;
  }
  if (exp->kind != wabt::ExternalKind::Func) {
    LOG(WARNING) << "Required export of kind " << exp->kind << " not func in module";
    return false;
  }
  LOG(INFO) << "check signature of function #" << exp->index;
  wabt::interp::Func* func = env->GetFunc(exp->index);
  if (func == nullptr) {
    LOG(WARNING) << "failed to retrieve function #" << exp->index;
    return false;
  }
  if (func->sig_index >= env->GetFuncSignatureCount()) {
    LOG(WARNING) << "Function #" << func->sig_index << " beyond range of signature types";
    return false;
  }
  wabt::interp::FuncSignature* sig = env->GetFuncSignature(func->sig_index);
  if (sig == nullptr) {
    LOG(WARNING) << "Could not find signature for function #" << exp->index;
    return false;
  }
  LOG(INFO) << "function #" << exp->index << " has type #" << func->sig_index << " with signature "
            << *sig;
  if ((sig->param_types != req.sig_.param_types) || (sig->result_types != req.sig_.result_types)) {
    LOG(WARNING) << "Function signature mismatch for " << req.name_ << ": got " << *sig << ", want "
                 << req.sig_;
    return false;
  }
  return true;
}
static bool CheckModuleExports(wabt::interp::Environment* env,
                               wabt::interp::DefinedModule* module) {
  bool rc = true;
  for (const RequiredExport& req : kRequiredExports) {
    if (!CheckModuleExport(env, module, req)) {
      rc = false;
    }
  }
  return rc;
}

std::string Sha256Hash(const std::string& data) {
  SHA256_CTX context;
  SHA256_Init(&context);
  SHA256_Update(&context, data.data(), data.size());
  std::vector<uint8_t> hash(SHA256_DIGEST_LENGTH);
  SHA256_Final(hash.data(), &context);
  return std::string(hash.cbegin(), hash.cend());
}

OakNode::OakNode(const std::string& node_id, const std::string& module)
    : Service(), node_id_(node_id), module_hash_sha_256_(Sha256Hash(module)) {}

std::unique_ptr<OakNode> OakNode::Create(const std::string& node_id, const std::string& module) {
  LOG(INFO) << "Creating Oak Node";

  std::unique_ptr<OakNode> node = absl::WrapUnique(new OakNode(node_id, module));
  node->InitEnvironment(&node->env_);
  LOG(INFO) << "Host func count: " << node->env_.GetFuncCount();

  wabt::Errors errors;
  LOG(INFO) << "Reading module";
  wabt::Result result = ReadModule(module, &node->env_, &errors, &node->module_);
  if (!wabt::Succeeded(result)) {
    LOG(WARNING) << "Could not read module: " << result;
    LOG(WARNING) << "Errors: " << wabt::FormatErrorsToString(errors, wabt::Location::Type::Binary);
    return nullptr;
  }

  LOG(INFO) << "Reading module done";
  if (!CheckModuleExports(&node->env_, node->module_)) {
    LOG(WARNING) << "Failed to validate module";
    return nullptr;
  }

  wabt::interp::Thread::Options thread_options;
  // wabt::Stream* trace_stream = s_stdout_stream.get();
  wabt::Stream* trace_stream = nullptr;
  wabt::interp::Executor executor(&node->env_, trace_stream, thread_options);
  LOG(INFO) << "Executing module";

  // Create a logging channel to allow the module to log during initialization.
  std::unique_ptr<Channel> logging_channel = absl::make_unique<LoggingChannel>();
  node->channels_[LOGGING_CHANNEL_HANDLE] = std::move(logging_channel);
  LOG(INFO) << "Created logging channel";

  wabt::interp::TypedValues args;
  wabt::interp::ExecResult exec_result =
      executor.RunExportByName(node->module_, "oak_initialize", args);

  // Drop all channels used in the current invocation.
  node->channels_ = std::unordered_map<Handle, std::unique_ptr<Channel>>();

  if (exec_result.result != wabt::interp::Result::Ok) {
    LOG(WARNING) << "Could not execute module";
    wabt::interp::WriteResult(s_stdout_stream.get(), "error", exec_result.result);
    // TODO: Print error.
    return nullptr;
  }

  LOG(INFO) << "Executed module";
  return node;
}

// Register all available host functions so that they are available to the Oak Module at runtime.
void OakNode::InitEnvironment(wabt::interp::Environment* env) {
  wabt::interp::HostModule* oak_module = env->AppendHostModule("oak");
  oak_module->AppendFuncExport(
      "channel_read",
      wabt::interp::FuncSignature(
          std::vector<wabt::Type>{wabt::Type::I64, wabt::Type::I32, wabt::Type::I32},
          std::vector<wabt::Type>{wabt::Type::I32}),
      this->OakChannelRead(env));
  oak_module->AppendFuncExport(
      "channel_write",
      wabt::interp::FuncSignature(
          std::vector<wabt::Type>{wabt::Type::I64, wabt::Type::I32, wabt::Type::I32},
          std::vector<wabt::Type>{wabt::Type::I32}),
      this->OakChannelWrite(env));
}

wabt::interp::HostFunc::Callback OakNode::OakChannelRead(wabt::interp::Environment* env) {
  return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature* sig,
                     const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) {
    LogHostFunctionCall(func, args);

    Handle channel_handle = args[0].get_i64();
    uint32_t offset = args[1].get_i32();
    uint32_t size = args[2].get_i32();

    if (channels_.count(channel_handle) == 0) {
      LOG(WARNING) << "Invalid channel handle: " << channel_handle;
      return wabt::interp::Result::Ok;
    }
    std::unique_ptr<Channel>& channel = channels_.at(channel_handle);

    absl::Span<const char> data = channel->Read(size);
    WriteMemory(env, offset, data);
    results[0].set_i32(data.size());

    return wabt::interp::Result::Ok;
  };
}

wabt::interp::HostFunc::Callback OakNode::OakChannelWrite(wabt::interp::Environment* env) {
  return [this, env](const wabt::interp::HostFunc* func, const wabt::interp::FuncSignature* sig,
                     const wabt::interp::TypedValues& args, wabt::interp::TypedValues& results) {
    LogHostFunctionCall(func, args);

    Handle channel_handle = args[0].get_i64();
    uint32_t offset = args[1].get_i32();
    uint32_t size = args[2].get_i32();

    if (channels_.count(channel_handle) == 0) {
      LOG(WARNING) << "Invalid channel handle: " << channel_handle;
      return wabt::interp::Result::Ok;
    }
    std::unique_ptr<Channel>& channel = channels_.at(channel_handle);

    absl::Span<const char> data = ReadMemory(env, offset, size);
    channel->Write(data);
    results[0].set_i32(data.size());

    return wabt::interp::Result::Ok;
  };
}

grpc::Status OakNode::ProcessModuleInvocation(grpc::GenericServerContext* context,
                                              const std::vector<char>& request_data,
                                              std::vector<char>* response_data) {
  LOG(INFO) << "Handling gRPC call: " << context->method();
  server_context_ = context;

  // Store a copy of the gRPC method name so that we can refer to it via a Span when the Module
  // tries to read it from a channel; otherwise we would need to allocate a new string every time
  // and ensure it can outlive the Span reference.
  grpc_method_name_ = context->method();

  // Create the gRPC channel, used by the module to perform basic input and output.
  std::unique_ptr<Channel> grpc_channel =
      absl::make_unique<BufferChannel>(request_data, response_data);
  channels_[GRPC_CHANNEL_HANDLE] = std::move(grpc_channel);
  LOG(INFO) << "Created gRPC channel";

  // Create the gRPC method name channel, used by the module to read the gRPC method name from the
  // current context.
  std::unique_ptr<Channel> grpc_method_name_channel =
      absl::make_unique<BufferChannel>(grpc_method_name_, nullptr);
  channels_[GRPC_METHOD_NAME_CHANNEL_HANDLE] = std::move(grpc_method_name_channel);
  LOG(INFO) << "Created gRPC method name channel";

  // Create the logging channel, used by the module to log statements for debugging.
  std::unique_ptr<Channel> logging_channel = absl::make_unique<LoggingChannel>();
  channels_[LOGGING_CHANNEL_HANDLE] = std::move(logging_channel);
  LOG(INFO) << "Created logging channel";

  wabt::Stream* trace_stream = nullptr;
  wabt::interp::Thread::Options thread_options;
  wabt::interp::Executor executor(&env_, trace_stream, thread_options);

  wabt::interp::TypedValues args = {};
  wabt::interp::ExecResult exec_result =
      executor.RunExportByName(module_, "oak_handle_grpc_call", args);

  // Drop all the channels used in the current invocation.
  channels_ = std::unordered_map<Handle, std::unique_ptr<Channel>>();

  if (exec_result.result != wabt::interp::Result::Ok) {
    std::string err = wabt::interp::ResultToString(exec_result.result);
    LOG(ERROR) << "Could not handle gRPC call: " << err;
    return grpc::Status(grpc::StatusCode::INTERNAL, err);
  }
  return grpc::Status::OK;
}

grpc::Status OakNode::GetAttestation(grpc::ServerContext* context,
                                     const GetAttestationRequest* request,
                                     GetAttestationResponse* response) {
  response->set_module_hash_sha_256(module_hash_sha_256_);
  return ::grpc::Status::OK;
}

}  // namespace oak
<|endoftext|>


<commit_before>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: atomicContactEnergy.C,v 1.4 2003/05/06 20:27:38 oliver Exp $
//

#include <BALL/ENERGY/atomicContactEnergy.h>

#include <BALL/common.h>
#include <BALL/SYSTEM/file.h>
#include <BALL/SYSTEM/path.h>
#include <BALL/DATATYPE/hashGrid.h>
#include <BALL/DATATYPE/string.h>
#include <BALL/DATATYPE/stringHashMap.h>
#include <BALL/MATHS/vector3.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/atomContainer.h>

#define ACE_FILENAME "energy/AtomicContactEnergy.dat"
#define ACE_TYPES_FILENAME "energy/ACE_types.dat"

// #define BALL_DEBUG_ACE

using namespace std;

namespace BALL
{

	StringHashMap<Atom::Type> buildTable_()
	{
		File				datafile;
		String			residue;
		String			atom_name;
		Atom::Type	atom_type;

		// determine the path to the data file
		Path path;
		String filename = path.find(ACE_TYPES_FILENAME);
		if (filename == "")
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, ACE_TYPES_FILENAME);
		}

		datafile.open(filename);

		// create a new StringHashMap
		StringHashMap<Atom::Type> table;

		while (datafile.good())
		{
			datafile >> residue >> atom_name >> atom_type;
			String key(residue);
			key.append(":");
			key.append(atom_name);
			table[key] = atom_type;
		}

		datafile.close();

		return table;
	}

	typedef struct
	{
		Index					type;
		Vector3			v;
		unsigned		index;
		String			name;
		const Atom*	atom;
	} ACEFastAtomType_;

	double calculateACE(AtomContainer& atoms)
	{

		Vector3										bounding_box_lower;
		Vector3										bounding_box_upper;
		Vector3										size;

		StringHashMap<Atom::Type>	table;
		float											ACE[18][18];
		FILE*											ACE_file;
		Index												i;
		Index												j;

		table = buildTable_();

		Path path;
		String filename = path.find(ACE_FILENAME);
		if (filename == "")
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, ACE_FILENAME);
		}
		ACE_file = fopen(filename.c_str(), "r");

		for (i = 0; i < 18; i++)
		{
			for (j = 0; j < 18; j++)
			{
				fscanf(ACE_file, "%f", &(ACE[i][j]));
			}
		}
		fclose(ACE_file);

		// perform consistency check: matrix has to be symmetric
		for (i = 0; i < 18; i++)
		{
			for (j = i; j < 18; j++)
			{
				if (ACE[i][j] != ACE[j][i])
				{
					Log.error() << "calculateACE: inconsistent values in contact matrix: ACE["
											<< i << "][" << j << "] != ACE[" << j << "][" << i << "]!" << endl;
				}
			}
		}

		BoundingBoxProcessor	bounding_box;

		atoms.apply(bounding_box);
		bounding_box_lower = bounding_box.getLower();
		bounding_box_upper = bounding_box.getUpper();

		size = bounding_box_upper - bounding_box_lower;

		HashGrid3<ACEFastAtomType_*>*	grid;
		grid = new HashGrid3<ACEFastAtomType_*>(bounding_box_lower - Vector3(7.0), size + Vector3(14.0), 6.0);

		ACEFastAtomType_*		atom;
		String							atom_name;
		Index									atom_type;
		unsigned						index = 0;

		AtomConstIterator atom_it(atoms.beginAtom());
		for (atom_it = atoms.beginAtom(); +atom_it; ++atom_it)
		{
			// construct correct name, <RESNAME>:<ATOMNAME>
			atom_name = atom_it->getFragment()->getName().trim();
			atom_name.append(":");
			atom_name.append(atom_it->getName().trim());

			// get the atom type from hash table
			//    atom type = -1 for hydrogen (not to be considered)
			//    atom type = -2 for atoms that are not parametrized
			//    atom type = -3 for unknown atom names
			//
			if (table.has(atom_name))
			{
				atom_type = table[atom_name];
			}
			else
			{
				atom_type = -3;
			}

			// insert all neccessary atoms into the grid
			if (atom_type >= 0)
			{
				atom = new ACEFastAtomType_;
				atom->index = index++;
				atom->v = atom_it->getPosition();
				atom->type = atom_type;
				atom->name = atom_name;
				atom->atom = &(*atom_it);
				grid->insert(atom->v, atom);
				// Log.error() << atom_name << ": " << atom_type << endl;
			}
			else
			{
				// some types are known but not parametrized
				if (atom_type == -2)
				{
					Log.warn() << "calculateACE: ACE not parametrized for " << atom_name << endl;
				}
				// hydrogens are just to be ignored, unknown types are a
				// real reason for concern
				else if (atom_type == -3)
				{
					Log.warn() << "calculateACE: did not find an ACE type for " << atom_name << endl;
				}
			}
		}

		float contact_energy = 0;
		float	sq_distance;
		Vector3 v;

		HashGrid3<ACEFastAtomType_*>::BoxIterator					box_it;
		HashGridBox3<ACEFastAtomType_*>::DataIterator			data_it;

		HashGridBox3<ACEFastAtomType_*>::BoxIterator			box_it2;
		HashGridBox3<ACEFastAtomType_*>::DataIterator			data_it2;

#ifdef BALL_DEBUG_ACE
		Size count = 0;
		AtomIterator debug_atom_it1 = atoms.beginAtom();
		AtomIterator debug_atom_it2;
		for (; +debug_atom_it1; ++debug_atom_it1)
		{
			for (debug_atom_it2 = debug_atom_it1; +debug_atom_it2; ++debug_atom_it2)
			{
				if ((debug_atom_it1 != debug_atom_it2)
						&& (debug_atom_it1->getPosition().getDistance(debug_atom_it2->getPosition()) <= 6.0))
				{
					count++;
				}
			}
		}
		Log.error() << "count(BF) = " << count << endl;
		count = 0;
#endif

		// iterate over all (non empty) grid boxes
		for (box_it = grid->beginBox(); +box_it; ++box_it)
		{
			// iterate over all items in the box
			for (data_it = (*box_it).beginData(); +data_it; ++data_it)
			{
				v = (*data_it)->v;

				// iterate over all neighbouring boxes (includig the box itself!)
				for (box_it2 = (*box_it).beginBox(); +box_it2; ++box_it2)
				{
					// iterate over all items in the box
					for (data_it2 = (*box_it2).beginData(); +data_it2; ++data_it2)
					{
						// consider only pairs of different atoms and consider each pair only once
						if ((*data_it2)->index > (*data_it)->index)
						{
							// check whether the distance between the atoms is at most 6.0 Angstrom
							sq_distance = v.getSquareDistance((*data_it2)->v);
							if (sq_distance <= 36.0)
							{
								if (!(*data_it)->atom->isBoundTo(*(*data_it2)->atom))
								{
									contact_energy += ACE[(*data_it)->type][(*data_it2)->type];
#ifdef BALL_DEBUG_ACE
									count++;
#endif
								}
							}
						}
					}
				}
			}
		}

#ifdef BALL_DEBUG_ACE
		Log.error() << "count(BF) = " << count << endl;
#endif

		// scale by a factor of 1 / 21.0 to get energy in kcal/mol
		// and another factor of 4.184 to get kJ/mol
		// return the energy in units of kJ/mol
		return contact_energy / (21.0 / 4.184);
	}

} // namespace BALL
<commit_msg>fixed: modified implementation -- memory leaks removed<commit_after>// -*- Mode: C++; tab-width: 2; -*-
// vi: set ts=2:
//
// $Id: atomicContactEnergy.C,v 1.5 2003/05/07 16:41:44 oliver Exp $
//

#include <BALL/ENERGY/atomicContactEnergy.h>

#include <BALL/common.h>
#include <BALL/SYSTEM/file.h>
#include <BALL/SYSTEM/path.h>
#include <BALL/DATATYPE/hashGrid.h>
#include <BALL/DATATYPE/string.h>
#include <BALL/DATATYPE/stringHashMap.h>
#include <BALL/MATHS/vector3.h>
#include <BALL/STRUCTURE/geometricProperties.h>
#include <BALL/KERNEL/atom.h>
#include <BALL/KERNEL/atomContainer.h>

#define ACE_FILENAME "energy/AtomicContactEnergy.dat"
#define ACE_TYPES_FILENAME "energy/ACE_types.dat"

// #define BALL_DEBUG_ACE

using namespace std;

namespace BALL
{
	typedef StringHashMap<Atom::Type>								ACETypeTable;
	typedef std::vector<std::vector<double> >				ACEParameter;
	typedef std::pair<ACETypeTable, ACEParameter>		ACEData;

	 const ACEData& ACEBuildTable
		(const String& type_filename, const String& parameter_filename)
	{
		// A static string hash map holds the types.
		static ACEData data;
		static String last_type_filename;
		static String last_parameter_filename;

		// Check whether the last files read are identical with the
		// filenames specified. We are fine if they are identical
		// and reread everything otherwise.
		if (last_type_filename == type_filename && last_parameter_filename == parameter_filename)
		{
			return data;
		}

		// Determine the path to the type file.
		Path path;
		String filename = path.find(type_filename);
		if (filename == "")
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, type_filename);
		}

		// Read the file.
		data.first.clear();
		File datafile(filename);
		String key;
		String atom_name;
		Index atom_type;
		while (datafile.good())
		{
			datafile >> key >> atom_name >> atom_type;
			key.append(":");
			key.append(atom_name);
			data.first[key] = atom_type;
		}
		datafile.close();

		filename = path.find(parameter_filename);
		if (filename == "")
		{
			throw Exception::FileNotFound(__FILE__, __LINE__, filename);
		}

		// Read the parameter table.
		datafile.open(filename);
		Size number_of_entries;
		datafile >> number_of_entries;

		// Read the matrix of ACE parameters (symmetric, number_of_entries x number_of_entries)
		data.second.resize(number_of_entries);
		for (Position i = 0; i < number_of_entries; i++)
		{
			data.second[i].resize(number_of_entries);

			for (Position j = 0; j < number_of_entries; j++)
			{
				datafile >> data.second[i][j];
			}
		}

		// Consistency check in the matrix
		for (Position i = 0; i < number_of_entries; i++)
		{
			for (Position j = 0; j < number_of_entries; j++)
			{
				if (data.second[i][j] != data.second[j][i])
				{
					throw Exception::ParseError(__FILE__, __LINE__,
																			String("ACE data from ") + filename,
																			String("inconsistent table entry at position ") + String(i) + "/" + String(j));
				}
			}
		}

		// Remember we read the files
		last_type_filename = type_filename;
		last_parameter_filename = parameter_filename;

		return data;
	}

	typedef struct
	{
		Index				type;
		Vector3			v;
		Position		index;
		String			name;
		const Atom*	atom;
	}
	ACEFastAtomType;

	double calculateACE
		(const AtomContainer& atom_container,
		 const string& type_filename, const string& parameter_filename)
	{
		// Retrieve the ACE types and parameters.
		// We do this just once, unless different filenames are
		// being specified.
		const ACEData& data = ACEBuildTable(type_filename, parameter_filename);
		const ACETypeTable& table = data.first;
		const ACEParameter& params = data.second;

		// Compute the bounding box of the atom container.
		BoundingBoxProcessor bounding_box;
		const_cast<AtomContainer&>(atom_container).apply(bounding_box);
		Vector3 size = bounding_box.getUpper() - bounding_box.getLower();

		// Create a three-dimensional hash grid. The border has to
		// extend by at least one box size (the max distance used by ACE) over the bounding box.
		HashGrid3<ACEFastAtomType*> grid(bounding_box.getLower() - Vector3(7.0), size + Vector3(14.0), 6.0);

		// Create a vector of ACEFastAtom entries. We will stick pointers
		// to those into the hash grid. It will never shrink again, but then
		// we won't have to reallocate all the time. This seems like a good
		// trade-off for now.
		static std::vector<ACEFastAtomType> atoms;

		// Remove old stuff
		atoms.clear();

		String atom_name;
		Index atom_type;
		AtomConstIterator atom_it(atom_container.beginAtom());
		for (; +atom_it; ++atom_it)
		{
			// construct correct name, <RESNAME>:<ATOMNAME>
			atom_name = atom_it->getFullName(Atom::NO_VARIANT_EXTENSIONS);

			// get the atom type from hash table
			//    atom type = -1 for hydrogen (not to be considered)
			//    atom type = -2 for atoms that are not parametrized
			//    atom type = -3 for unknown atom names
			//
			if (table.has(atom_name))
			{
				atom_type = table[atom_name];
			}
			else
			{
				atom_type = -3;
			}

			// insert all neccessary atoms into the grid
			if (atom_type >= 0)
			{
				static ACEFastAtomType atom;
				atom.v = atom_it->getPosition();
				atom.type = atom_type;
				atom.name = atom_name;
				atom.index = atoms.size();
				atom.atom = &*atom_it;

				atoms.push_back(atom);
			}
			else
			{
				// some types are known but not parametrized
				if (atom_type == -2)
				{
					Log.warn() << "calculateACE: ACE not parametrized for " << atom_name << endl;
				}
				else if (atom_type == -3)
				{
					// hydrogens are just to be ignored, unknown types are a
					// real reason for concern
					Log.warn() << "calculateACE: did not find an ACE type for " << atom_name << endl;
				}
			}
		}

		std::vector<ACEFastAtomType>::iterator it(atoms.begin());
		for (; it != atoms.end(); ++it)
		{
			grid.insert(it->v, &*it);
		}

		double contact_energy = 0;
		double sq_distance;
		Vector3 v;

		#ifdef BALL_DEBUG_ACE
			Size count = 0;
			AtomIterator debug_atom_it1 = atoms.beginAtom();
			AtomIterator debug_atom_it2;
			for (; +debug_atom_it1; ++debug_atom_it1)
			{
				for (debug_atom_it2 = debug_atom_it1; +debug_atom_it2; ++debug_atom_it2)
				{
					if ((debug_atom_it1 != debug_atom_it2)
							&& (debug_atom_it1->getPosition().getDistance(debug_atom_it2->getPosition()) <= 6.0))
					{
						count++;
					}
				}
			}
			Log.error() << "count(BF) = " << count << endl;
			count = 0;
		#endif

		// iterate over all (non empty) grid boxes
		HashGrid3<ACEFastAtomType*>::BoxIterator box_it(grid.beginBox());
		for (; +box_it; ++box_it)
		{
			HashGridBox3<ACEFastAtomType*>::DataIterator			data_it;
			HashGridBox3<ACEFastAtomType*>::BoxIterator				box_it2;
			HashGridBox3<ACEFastAtomType*>::DataIterator			data_it2;

			// iterate over all items in the box
			for (data_it = box_it->beginData(); +data_it; ++data_it)
			{
				v = (*data_it)->v;

				// iterate over all neighbouring boxes (includig the box itself!)
				for (box_it2 = box_it->beginBox(); +box_it2; ++box_it2)
				{
					// iterate over all items in the box
					for (data_it2 = box_it2->beginData(); +data_it2; ++data_it2)
					{
						// consider only pairs of different atoms and consider each pair only once
						if ((*data_it2)->index > (*data_it)->index)
						{
							// check whether the distance between the atoms is at most 6.0 Angstrom
							sq_distance = v.getSquareDistance((*data_it2)->v);
							if (sq_distance <= 36.0)
							{
								if (!(*data_it)->atom->isBoundTo(*(*data_it2)->atom))
								{
									contact_energy += params[(*data_it)->type][(*data_it2)->type];
									#ifdef BALL_DEBUG_ACE
										count++;
									#endif
								}
							}
						}
					}
				}
			}
		}

	#ifdef BALL_DEBUG_ACE
		Log.error() << "count(BF) = " << count << endl;
	#endif

		// scale by a factor of 1 / 21.0 to get energy in kcal/mol
		// and another factor of 4.184 to get kJ/mol
		// return the energy in units of kJ/mol
		return contact_energy / (21.0 / 4.184);
	}

} // namespace BALL
<|endoftext|>


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

#include <assert.h>
#include <stdint.h>

#include "image.hpp"

namespace Image {

/**
 * http://en.wikipedia.org/wiki/Netpbm_format
 * http://netpbm.sourceforge.net/doc/ppm.html
 */
void
Image::writePNM(std::ostream &os, const char *comment) const {
    assert(channels == 1 || channels >= 3);

    os << (channels == 1 ? "P5" : "P6") << "\n";
    if (comment) {
        os << "#" << comment << "\n";
    }
    os << width << " " << height << "\n";
    os << "255" << "\n";

    const unsigned char *row;

    if (channels == 1 || channels == 3) {
        for (row = start(); row != end(); row += stride()) {
            os.write((const char *)row, width*channels);
        }
    } else {
        unsigned char pixel[3] = {0, 0, 0};
        for (row = start(); row != end(); row += stride()) {
            for (unsigned x = 0; x < width; ++x) {
                for (unsigned channel = 0; channel < channels; ++channel) {
                    pixel[channel] = row[x*channels + channel];
                }
                os.write((const char *)pixel, sizeof pixel);
            }
        }
    }
}

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

#include <assert.h>
#include <string.h>

#include "image.hpp"

namespace Image {

/**
 * http://en.wikipedia.org/wiki/Netpbm_format
 * http://netpbm.sourceforge.net/doc/ppm.html
 */
void
Image::writePNM(std::ostream &os, const char *comment) const {
    assert(channels == 1 || channels >= 3);

    os << (channels == 1 ? "P5" : "P6") << "\n";
    if (comment) {
        os << "#" << comment << "\n";
    }
    os << width << " " << height << "\n";
    os << "255" << "\n";

    const unsigned char *row;

    if (channels == 1 || channels == 3) {
        for (row = start(); row != end(); row += stride()) {
            os.write((const char *)row, width*channels);
        }
    } else {
        unsigned char *pixels = new unsigned char[width*3];
        if (channels == 4) {
            for (row = start(); row != end(); row += stride()) {
                for (unsigned x = 0; x < width; ++x) {
                    pixels[x*3 + 0] = row[x*4 + 0];
                    pixels[x*3 + 1] = row[x*4 + 1];
                    pixels[x*3 + 2] = row[x*4 + 2];
                }
                os.write((const char *)pixels, width*3);
            }
        } else if (channels == 2) {
            for (row = start(); row != end(); row += stride()) {
                for (unsigned x = 0; x < width; ++x) {
                    pixels[x*3 + 0] = row[x*2 + 0];
                    pixels[x*3 + 1] = row[x*2 + 1];
                    pixels[x*3 + 2] = 0;
                }
                os.write((const char *)pixels, width*3);
            }
        } else {
            assert(0);
        }
        delete [] pixels;
    }
}

} /* namespace Image */
<|endoftext|>


<commit_before>////////////////////////////////////////////////////////////////////////////////
///
/// MMX optimized routines. All MMX optimized functions have been gathered into
/// this single source code file, regardless to their class or original source
/// code file, in order to ease porting the library to other compiler and
/// processor platforms.
///
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
/// should compile with both toolsets.
///
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
/// is available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed  : $Date$
// File revision : $Revision: 4 $
//
// $Id$
//
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#include "STTypes.h"

#ifdef ALLOW_MMX
// MMX routines available only with integer sample type

#if !(WIN32 || __i386__ || __x86_64__)
#error "wrong platform - this source code file is exclusively for x86 platforms"
#endif

using namespace soundtouch;

//////////////////////////////////////////////////////////////////////////////
//
// implementation of MMX optimized functions of class 'TDStretchMMX'
//
//////////////////////////////////////////////////////////////////////////////

#include "TDStretch.h"
#include <mmintrin.h>
#include <limits.h>

// Calculates cross correlation of two buffers
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
{
    const __m64 *pVec1, *pVec2;
    __m64 shifter;
    __m64 accu;
    long corr;
    uint i;

    pVec1 = (__m64*)pV1;
    pVec2 = (__m64*)pV2;

    shifter = _m_from_int(overlapDividerBits);
    accu = _mm_setzero_si64();

    // Process 4 parallel sets of 2 * stereo samples each during each
    // round to improve CPU-level parallellization.
    for (i = 0; i < overlapLength / 8; i ++)
    {
        __m64 temp;

        // dictionary of instructions:
        // _m_pmaddwd   : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
        // _mm_add_pi32 : 2*32bit add
        // _m_psrad     : 32bit right-shift

        temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
                            _mm_madd_pi16(pVec1[1], pVec2[1]));
        accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));

        temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
                            _mm_madd_pi16(pVec1[3], pVec2[3]));
        accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));

        pVec1 += 4;
        pVec2 += 4;
    }

    // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
    // and finally store the result into the variable "corr"

    accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
    corr = _m_to_int(accu);

    // Clear MMS state
    _m_empty();

    return corr;
    // Note: Warning about the missing EMMS instruction is harmless
    // as it'll be called elsewhere.
}

void TDStretchMMX::clearCrossCorrState()
{
    // Clear MMS state
    _m_empty();
    //_asm EMMS;
}

// MMX-optimized version of the function overlapStereo
void TDStretchMMX::overlapStereo(short *output, const short *input) const
{
    const __m64 *pVinput, *pVMidBuf;
    __m64 *pVdest;
    __m64 mix1, mix2, adder, shifter;
    uint i;

    pVinput  = (const __m64*)input;
    pVMidBuf = (const __m64*)pMidBuffer;
    pVdest   = (__m64*)output;

    // mix1  = mixer values for 1st stereo sample
    // mix1  = mixer values for 2nd stereo sample
    // adder = adder for updating mixer values after each round

    mix1  = _mm_set_pi16(0, overlapLength,   0, overlapLength);
    adder = _mm_set_pi16(1, -1, 1, -1);
    mix2  = _mm_add_pi16(mix1, adder);
    adder = _mm_add_pi16(adder, adder);

    shifter = _m_from_int(overlapDividerBits);

    for (i = 0; i < overlapLength / 4; i ++)
    {
        __m64 temp1, temp2;

        // load & shuffle data so that input & mixbuffer data samples are paired
        temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]);     // = i0l m0l i0r m0r
        temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]);     // = i1l m1l i1r m1r

        // temp = (temp .* mix) >> shifter
        temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
        temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
        pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit

        // update mix += adder
        mix1 = _mm_add_pi16(mix1, adder);
        mix2 = _mm_add_pi16(mix2, adder);

        // --- second round begins here ---

        // load & shuffle data so that input & mixbuffer data samples are paired
        temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]);       // = i2l m2l i2r m2r
        temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]);       // = i3l m3l i3r m3r

        // temp = (temp .* mix) >> shifter
        temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
        temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
        pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit

        // update mix += adder
        mix1 = _mm_add_pi16(mix1, adder);
        mix2 = _mm_add_pi16(mix2, adder);

        pVinput  += 2;
        pVMidBuf += 2;
        pVdest   += 2;
    }

    _m_empty(); // clear MMS state
}

//////////////////////////////////////////////////////////////////////////////
//
// implementation of MMX optimized functions of class 'FIRFilter'
//
//////////////////////////////////////////////////////////////////////////////

#include "FIRFilter.h"

FIRFilterMMX::FIRFilterMMX() : FIRFilter()
{
    filterCoeffsUnalign = NULL;
}

FIRFilterMMX::~FIRFilterMMX()
{
    delete[] filterCoeffsUnalign;
}

// (overloaded) Calculates filter coefficients for MMX routine
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
{
    uint i;
    FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);

    // Ensure that filter coeffs array is aligned to 16-byte boundary
    delete[] filterCoeffsUnalign;
    filterCoeffsUnalign = new short[2 * newLength + 8];
    filterCoeffsAlign = (short *)(((uint)filterCoeffsUnalign + 15) & -16);

    // rearrange the filter coefficients for mmx routines
    for (i = 0;i < length; i += 4)
    {
        filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
        filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
        filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
        filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];

        filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
        filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
        filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
        filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
    }
}

// mmx-optimized version of the filter routine for stereo sound
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
{
    // Create stack copies of the needed member variables for asm routines :
    uint i, j;
    __m64 *pVdest = (__m64*)dest;

    if (length < 2) return 0;

    for (i = 0; i < numSamples / 2; i ++)
    {
        __m64 accu1;
        __m64 accu2;
        const __m64 *pVsrc = (const __m64*)src;
        const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;

        accu1 = accu2 = _mm_setzero_si64();
        for (j = 0; j < lengthDiv8 * 2; j ++)
        {
            __m64 temp1, temp2;

            temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]);  // = l2 l0 r2 r0
            temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]);  // = l3 l1 r3 r1

            accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0]));  // += l2*f2+l0*f0 r2*f2+r0*f0
            accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1]));  // += l3*f3+l1*f1 r3*f3+r1*f1

            temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]);  // = l4 l2 r4 r2

            accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0]));  // += l3*f2+l1*f0 r3*f2+r1*f0
            accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1]));  // += l4*f3+l2*f1 r4*f3+r2*f1

            // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
            //       += l3*f3+l1*f1 r3*f3+r1*f1

            // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
            //          l4*f3+l2*f1 r4*f3+r2*f1

            pVfilter += 2;
            pVsrc += 2;
        }
        // accu >>= resultDivFactor
        accu1 = _mm_srai_pi32(accu1, resultDivFactor);
        accu2 = _mm_srai_pi32(accu2, resultDivFactor);

        // pack 2*2*32bits => 4*16 bits
        pVdest[0] = _mm_packs_pi32(accu1, accu2);
        src += 4;
        pVdest ++;
    }

   _m_empty();  // clear emms state

    return (numSamples & 0xfffffffe) - length;
}

#endif  // ALLOW_MMX
<commit_msg>Bugfix: Use 'ulong' datatype for pointer typecast for 64bit compatibility<commit_after>////////////////////////////////////////////////////////////////////////////////
///
/// MMX optimized routines. All MMX optimized functions have been gathered into
/// this single source code file, regardless to their class or original source
/// code file, in order to ease porting the library to other compiler and
/// processor platforms.
///
/// The MMX-optimizations are programmed using MMX compiler intrinsics that
/// are supported both by Microsoft Visual C++ and GCC compilers, so this file
/// should compile with both toolsets.
///
/// NOTICE: If using Visual Studio 6.0, you'll need to install the "Visual C++
/// 6.0 processor pack" update to support compiler intrinsic syntax. The update
/// is available for download at Microsoft Developers Network, see here:
/// http://msdn.microsoft.com/vstudio/downloads/tools/ppack/default.aspx
///
/// Author        : Copyright (c) Olli Parviainen
/// Author e-mail : oparviai 'at' iki.fi
/// SoundTouch WWW: http://www.surina.net/soundtouch
///
////////////////////////////////////////////////////////////////////////////////
//
// Last changed  : $Date$
// File revision : $Revision: 4 $
//
// $Id$
//
////////////////////////////////////////////////////////////////////////////////
//
// License :
//
//  SoundTouch audio processing library
//  Copyright (c) Olli Parviainen
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
////////////////////////////////////////////////////////////////////////////////

#include "STTypes.h"

#ifdef ALLOW_MMX
// MMX routines available only with integer sample type

#if !(WIN32 || __i386__ || __x86_64__)
#error "wrong platform - this source code file is exclusively for x86 platforms"
#endif

using namespace soundtouch;

//////////////////////////////////////////////////////////////////////////////
//
// implementation of MMX optimized functions of class 'TDStretchMMX'
//
//////////////////////////////////////////////////////////////////////////////

#include "TDStretch.h"
#include <mmintrin.h>
#include <limits.h>

// Calculates cross correlation of two buffers
long TDStretchMMX::calcCrossCorrStereo(const short *pV1, const short *pV2) const
{
    const __m64 *pVec1, *pVec2;
    __m64 shifter;
    __m64 accu;
    long corr;
    uint i;

    pVec1 = (__m64*)pV1;
    pVec2 = (__m64*)pV2;

    shifter = _m_from_int(overlapDividerBits);
    accu = _mm_setzero_si64();

    // Process 4 parallel sets of 2 * stereo samples each during each
    // round to improve CPU-level parallellization.
    for (i = 0; i < overlapLength / 8; i ++)
    {
        __m64 temp;

        // dictionary of instructions:
        // _m_pmaddwd   : 4*16bit multiply-add, resulting two 32bits = [a0*b0+a1*b1 ; a2*b2+a3*b3]
        // _mm_add_pi32 : 2*32bit add
        // _m_psrad     : 32bit right-shift

        temp = _mm_add_pi32(_mm_madd_pi16(pVec1[0], pVec2[0]),
                            _mm_madd_pi16(pVec1[1], pVec2[1]));
        accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));

        temp = _mm_add_pi32(_mm_madd_pi16(pVec1[2], pVec2[2]),
                            _mm_madd_pi16(pVec1[3], pVec2[3]));
        accu = _mm_add_pi32(accu, _mm_sra_pi32(temp, shifter));

        pVec1 += 4;
        pVec2 += 4;
    }

    // copy hi-dword of mm0 to lo-dword of mm1, then sum mmo+mm1
    // and finally store the result into the variable "corr"

    accu = _mm_add_pi32(accu, _mm_srli_si64(accu, 32));
    corr = _m_to_int(accu);

    // Clear MMS state
    _m_empty();

    return corr;
    // Note: Warning about the missing EMMS instruction is harmless
    // as it'll be called elsewhere.
}

void TDStretchMMX::clearCrossCorrState()
{
    // Clear MMS state
    _m_empty();
    //_asm EMMS;
}

// MMX-optimized version of the function overlapStereo
void TDStretchMMX::overlapStereo(short *output, const short *input) const
{
    const __m64 *pVinput, *pVMidBuf;
    __m64 *pVdest;
    __m64 mix1, mix2, adder, shifter;
    uint i;

    pVinput  = (const __m64*)input;
    pVMidBuf = (const __m64*)pMidBuffer;
    pVdest   = (__m64*)output;

    // mix1  = mixer values for 1st stereo sample
    // mix1  = mixer values for 2nd stereo sample
    // adder = adder for updating mixer values after each round

    mix1  = _mm_set_pi16(0, overlapLength,   0, overlapLength);
    adder = _mm_set_pi16(1, -1, 1, -1);
    mix2  = _mm_add_pi16(mix1, adder);
    adder = _mm_add_pi16(adder, adder);

    shifter = _m_from_int(overlapDividerBits);

    for (i = 0; i < overlapLength / 4; i ++)
    {
        __m64 temp1, temp2;

        // load & shuffle data so that input & mixbuffer data samples are paired
        temp1 = _mm_unpacklo_pi16(pVMidBuf[0], pVinput[0]);     // = i0l m0l i0r m0r
        temp2 = _mm_unpackhi_pi16(pVMidBuf[0], pVinput[0]);     // = i1l m1l i1r m1r

        // temp = (temp .* mix) >> shifter
        temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
        temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
        pVdest[0] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit

        // update mix += adder
        mix1 = _mm_add_pi16(mix1, adder);
        mix2 = _mm_add_pi16(mix2, adder);

        // --- second round begins here ---

        // load & shuffle data so that input & mixbuffer data samples are paired
        temp1 = _mm_unpacklo_pi16(pVMidBuf[1], pVinput[1]);       // = i2l m2l i2r m2r
        temp2 = _mm_unpackhi_pi16(pVMidBuf[1], pVinput[1]);       // = i3l m3l i3r m3r

        // temp = (temp .* mix) >> shifter
        temp1 = _mm_sra_pi32(_mm_madd_pi16(temp1, mix1), shifter);
        temp2 = _mm_sra_pi32(_mm_madd_pi16(temp2, mix2), shifter);
        pVdest[1] = _mm_packs_pi32(temp1, temp2); // pack 2*2*32bit => 4*16bit

        // update mix += adder
        mix1 = _mm_add_pi16(mix1, adder);
        mix2 = _mm_add_pi16(mix2, adder);

        pVinput  += 2;
        pVMidBuf += 2;
        pVdest   += 2;
    }

    _m_empty(); // clear MMS state
}

//////////////////////////////////////////////////////////////////////////////
//
// implementation of MMX optimized functions of class 'FIRFilter'
//
//////////////////////////////////////////////////////////////////////////////

#include "FIRFilter.h"

FIRFilterMMX::FIRFilterMMX() : FIRFilter()
{
    filterCoeffsUnalign = NULL;
}

FIRFilterMMX::~FIRFilterMMX()
{
    delete[] filterCoeffsUnalign;
}

// (overloaded) Calculates filter coefficients for MMX routine
void FIRFilterMMX::setCoefficients(const short *coeffs, uint newLength, uint uResultDivFactor)
{
    uint i;
    FIRFilter::setCoefficients(coeffs, newLength, uResultDivFactor);

    // Ensure that filter coeffs array is aligned to 16-byte boundary
    delete[] filterCoeffsUnalign;
    filterCoeffsUnalign = new short[2 * newLength + 8];
    filterCoeffsAlign = (short *)(((ulong)filterCoeffsUnalign + 15) & -16);

    // rearrange the filter coefficients for mmx routines
    for (i = 0;i < length; i += 4)
    {
        filterCoeffsAlign[2 * i + 0] = coeffs[i + 0];
        filterCoeffsAlign[2 * i + 1] = coeffs[i + 2];
        filterCoeffsAlign[2 * i + 2] = coeffs[i + 0];
        filterCoeffsAlign[2 * i + 3] = coeffs[i + 2];

        filterCoeffsAlign[2 * i + 4] = coeffs[i + 1];
        filterCoeffsAlign[2 * i + 5] = coeffs[i + 3];
        filterCoeffsAlign[2 * i + 6] = coeffs[i + 1];
        filterCoeffsAlign[2 * i + 7] = coeffs[i + 3];
    }
}

// mmx-optimized version of the filter routine for stereo sound
uint FIRFilterMMX::evaluateFilterStereo(short *dest, const short *src, const uint numSamples) const
{
    // Create stack copies of the needed member variables for asm routines :
    uint i, j;
    __m64 *pVdest = (__m64*)dest;

    if (length < 2) return 0;

    for (i = 0; i < numSamples / 2; i ++)
    {
        __m64 accu1;
        __m64 accu2;
        const __m64 *pVsrc = (const __m64*)src;
        const __m64 *pVfilter = (const __m64*)filterCoeffsAlign;

        accu1 = accu2 = _mm_setzero_si64();
        for (j = 0; j < lengthDiv8 * 2; j ++)
        {
            __m64 temp1, temp2;

            temp1 = _mm_unpacklo_pi16(pVsrc[0], pVsrc[1]);  // = l2 l0 r2 r0
            temp2 = _mm_unpackhi_pi16(pVsrc[0], pVsrc[1]);  // = l3 l1 r3 r1

            accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp1, pVfilter[0]));  // += l2*f2+l0*f0 r2*f2+r0*f0
            accu1 = _mm_add_pi32(accu1, _mm_madd_pi16(temp2, pVfilter[1]));  // += l3*f3+l1*f1 r3*f3+r1*f1

            temp1 = _mm_unpacklo_pi16(pVsrc[1], pVsrc[2]);  // = l4 l2 r4 r2

            accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp2, pVfilter[0]));  // += l3*f2+l1*f0 r3*f2+r1*f0
            accu2 = _mm_add_pi32(accu2, _mm_madd_pi16(temp1, pVfilter[1]));  // += l4*f3+l2*f1 r4*f3+r2*f1

            // accu1 += l2*f2+l0*f0 r2*f2+r0*f0
            //       += l3*f3+l1*f1 r3*f3+r1*f1

            // accu2 += l3*f2+l1*f0 r3*f2+r1*f0
            //          l4*f3+l2*f1 r4*f3+r2*f1

            pVfilter += 2;
            pVsrc += 2;
        }
        // accu >>= resultDivFactor
        accu1 = _mm_srai_pi32(accu1, resultDivFactor);
        accu2 = _mm_srai_pi32(accu2, resultDivFactor);

        // pack 2*2*32bits => 4*16 bits
        pVdest[0] = _mm_packs_pi32(accu1, accu2);
        src += 4;
        pVdest ++;
    }

   _m_empty();  // clear emms state

    return (numSamples & 0xfffffffe) - length;
}

#endif  // ALLOW_MMX
<|endoftext|>