<commit_before>#include "clientmodel-events.h"

/**
 * Handles all the currently queued change events, returning when the
 * ClientModel change list is exhausted.
 */
void ClientModelEvents::handle_queued_changes()
{
    m_should_relayer = false;

    for (ClientModel::change_iter change_iter = m_clients.changes_begin();
            change_iter != m_clients.changes_end();
            change_iter++)
    {
        m_change = *change_iter;

        if (m_change->is_layer_change())
            handle_layer_change();
        else if (m_change->is_focus_change())
            handle_focus_change();
        else if (m_change->is_client_desktop_change())
            handle_client_desktop_change();
        else if (m_change->is_current_desktop_change())
            handle_current_desktop_change();
        else if (m_change->is_location_change())
            handle_location_change();
        else if (m_change->is_size_change())
            handle_size_change();
    }

    if (m_should_relayer)
        do_relayer();
}

/**
 * Sets a flag so that relayering occurs later - this avoid relayering on
 * every ChangeLayer event.
 */
void ClientModelEvents::handle_layer_change()
{
    m_should_relayer = true;
}

/**
 * Changes the focus from one window to another.
 *
 * The focus model used by SmallWM is click-to-focus, so clicks must be
 * captured on unfocused clients, while focused clients should not be captured.
 */
void ClientModelEvents::handle_focus_change()
{
    ChangeFocus const *change_event = dynamic_cast<ChangeFocus const*>(m_change);

    // First, unfocus whatever the model says is unfoucsed. Note that the
    // client which is being unfocused may not exist anymore.
    Window unfocused_client = change_event->prev_focus;
    if (m_clients.is_client(unfocused_client))
    {
        // Since this window will possibly be focused later, capture the clicks
        // going to it so we know when it needs to be focused again
        m_xdata.set_border_color(unfocused_client, X_WHITE);
        m_xdata.grab_mouse(unfocused_client);
    }

    Window focused_client = change_event->next_focus;
    if (focused_client == None)
    {
        // We can't set the border color of a nonexistent window, so just set
        // the focus to None
        m_xdata.set_input_focus(None);
    }
    else
    {
        // Since this is now focused, let the client process events in by
        // ungrabbing the mouse and setting the keyboard focus
        if (m_xdata.set_input_focus(focused_client))
        {
            m_xdata.set_border_color(unfocused_client, X_BLACK);
            m_xdata.ungrab_mouse(focused_client);
        }
        else
        {
            // If we failed to actually do the focus, then we have to
            // update the client model to keep it in sync with what our idea
            // of the focus is
            m_clients.unfocus();
        }
    }
}

/**
 * This changes the currently visible desktop, which involves figuring out
 * which windows are visible on the current desktop, which are not, and then
 * showing those that are visible and hiding those that are not.
 */
void ClientModelEvents::handle_current_desktop_change()
{
    ChangeCurrentDesktop const *change =
        dynamic_cast<ChangeCurrentDesktop const*>(m_change);

    std::vector<Window> old_desktop_list;
    std::vector<Window> new_desktop_list;

    // The output size will have, at most, the size of its largest input
    size_t max_output_size = std::max(old_desktop_list.size(),
        new_desktop_list.size());

    // std::set_difference requires sorted inputs
    std::sort(old_desktop_list.begin(), old_desktop_list.end());
    std::sort(new_desktop_list.begin(), new_desktop_list.end());

    std::vector<Window> to_make_invisible;
    std::vector<Window> to_make_visible;

    // Fill out all of the output vectors to the length of the input iterators,
    // so that std::set_difference has somewhere to put the data
    to_make_invisible.resize(max_output_size, None);
    to_make_visible.resize(max_output_size, None);

    // The old -> new set difference will produce the list of windows
    // which are on the old desktop, but not this one - these need to be
    // hidden
    std::set_difference(
        old_desktop_list.begin(), old_desktop_list.end(),
        new_desktop_list.begin(), new_desktop_list.end(),
        to_make_invisible.begin());

    for (std::vector<Window>::iterator to_hide = to_make_invisible.begin();
            /* The extra check that the iterator is not None is necessary,
             * since the vector was padded with None when it was resized.
             */
            to_hide != to_make_invisible.end() && *to_hide != None;
            to_hide++)
        m_xdata.unmap_win(*to_hide);

    // The new -> old set difference will produce the list of windows which
    // are on the new desktop, but not on the old - these need to be made
    // visible
    std::set_difference(
        new_desktop_list.begin(), new_desktop_list.end(),
        old_desktop_list.begin(), old_desktop_list.end(),
        to_make_invisible.begin());

    for (std::vector<Window>::iterator to_show = to_make_visible.begin();
            to_show != to_make_visible.end() && *to_show != None;
            to_show++)
        m_xdata.map_win(*to_show);

    // Since we've made some windows visible and some others invisible, we've
    // invalidated the previous stacking order, so restack everything according
    // to what is now visible
    do_relayer();
}

/**
 * Actually does the relayering.
 *
 * This involves sorting the clients, and then sticking the icons and
 * move/resize placeholder on the top.
 */
void ClientModelEvents::do_relayer()
{
    std::vector<Window> ordered_windows;
    m_clients.get_visible_in_layer_order(ordered_windows);

    // Figure out the currently focused client, and where it's at. We'll need
    // this information in order to place it above its peers.
    Window focused_client = m_clients.get_focused();
    Layer focused_layer;
    if (focused_client != None)
        focused_layer = m_clients.find_layer(focused_client);

    for (std::vector<Window>::iterator client_iter = ordered_windows.begin();
            client_iter != ordered_windows.end();
            client_iter++)
    {
        Window current_client = *client_iter;
        Layer current_layer = m_clients.find_layer(current_client);

        // We have to check if we're at the point where we can put up the
        // focused window - this happens when we've passed the layer that the
        // focused window is on. We want to put the focused window above all of
        // its peers, so before putting up the first client on the next layer,
        // put up the focused window
        if (focused_client != None &&
            current_layer == focused_layer + 1)
        {
            m_xdata.raise(focused_client);

            // Make sure to erase the focused client, so that we don't raise
            // it more than once
            focused_client = None;
        }

        if (current_client != focused_client)
            m_xdata.raise(current_client);
    }

    // If we haven't cleared the focused window, then we need to raise it before
    // moving on
    if (focused_client != None)
        m_xdata.raise(focused_client);

    // Now, raise all the clients since they should always be above all other
    // windows so they aren't obscured
    std::vector<Icon*> icon_list;
    m_xmodel.get_icons(icon_list);

    for (std::vector<Icon*>::iterator icon = icon_list.begin();
            icon != icon_list.end();
            icon++)
    {
        Window icon_win = (*icon)->icon;
        m_xdata.raise(icon_win);
    }

    // Finally, raise the placeholder, if there is one
    Window placeholder_win = m_xmodel.get_move_resize_placeholder();
    if (placeholder_win != None)
        m_xdata.raise(placeholder_win);
}
<commit_msg>Added location and size change handlers<commit_after>#include "clientmodel-events.h"

/**
 * Handles all the currently queued change events, returning when the
 * ClientModel change list is exhausted.
 */
void ClientModelEvents::handle_queued_changes()
{
    m_should_relayer = false;

    for (ClientModel::change_iter change_iter = m_clients.changes_begin();
            change_iter != m_clients.changes_end();
            change_iter++)
    {
        m_change = *change_iter;

        if (m_change->is_layer_change())
            handle_layer_change();
        else if (m_change->is_focus_change())
            handle_focus_change();
        else if (m_change->is_client_desktop_change())
            handle_client_desktop_change();
        else if (m_change->is_current_desktop_change())
            handle_current_desktop_change();
        else if (m_change->is_location_change())
            handle_location_change();
        else if (m_change->is_size_change())
            handle_size_change();
    }

    if (m_should_relayer)
        do_relayer();
}

/**
 * Sets a flag so that relayering occurs later - this avoid relayering on
 * every ChangeLayer event.
 */
void ClientModelEvents::handle_layer_change()
{
    m_should_relayer = true;
}

/**
 * Changes the focus from one window to another.
 *
 * The focus model used by SmallWM is click-to-focus, so clicks must be
 * captured on unfocused clients, while focused clients should not be captured.
 */
void ClientModelEvents::handle_focus_change()
{
    ChangeFocus const *change_event = dynamic_cast<ChangeFocus const*>(m_change);

    // First, unfocus whatever the model says is unfoucsed. Note that the
    // client which is being unfocused may not exist anymore.
    Window unfocused_client = change_event->prev_focus;
    if (m_clients.is_client(unfocused_client))
    {
        // Since this window will possibly be focused later, capture the clicks
        // going to it so we know when it needs to be focused again
        m_xdata.set_border_color(unfocused_client, X_WHITE);
        m_xdata.grab_mouse(unfocused_client);
    }

    Window focused_client = change_event->next_focus;
    if (focused_client == None)
    {
        // We can't set the border color of a nonexistent window, so just set
        // the focus to None
        m_xdata.set_input_focus(None);
    }
    else
    {
        // Since this is now focused, let the client process events in by
        // ungrabbing the mouse and setting the keyboard focus
        if (m_xdata.set_input_focus(focused_client))
        {
            m_xdata.set_border_color(unfocused_client, X_BLACK);
            m_xdata.ungrab_mouse(focused_client);
        }
        else
        {
            // If we failed to actually do the focus, then we have to
            // update the client model to keep it in sync with what our idea
            // of the focus is
            m_clients.unfocus();
        }
    }
}

/**
 * This changes the currently visible desktop, which involves figuring out
 * which windows are visible on the current desktop, which are not, and then
 * showing those that are visible and hiding those that are not.
 */
void ClientModelEvents::handle_current_desktop_change()
{
    ChangeCurrentDesktop const *change =
        dynamic_cast<ChangeCurrentDesktop const*>(m_change);

    std::vector<Window> old_desktop_list;
    std::vector<Window> new_desktop_list;

    // The output size will have, at most, the size of its largest input
    size_t max_output_size = std::max(old_desktop_list.size(),
        new_desktop_list.size());

    // std::set_difference requires sorted inputs
    std::sort(old_desktop_list.begin(), old_desktop_list.end());
    std::sort(new_desktop_list.begin(), new_desktop_list.end());

    std::vector<Window> to_make_invisible;
    std::vector<Window> to_make_visible;

    // Fill out all of the output vectors to the length of the input iterators,
    // so that std::set_difference has somewhere to put the data
    to_make_invisible.resize(max_output_size, None);
    to_make_visible.resize(max_output_size, None);

    // The old -> new set difference will produce the list of windows
    // which are on the old desktop, but not this one - these need to be
    // hidden
    std::set_difference(
        old_desktop_list.begin(), old_desktop_list.end(),
        new_desktop_list.begin(), new_desktop_list.end(),
        to_make_invisible.begin());

    for (std::vector<Window>::iterator to_hide = to_make_invisible.begin();
            /* The extra check that the iterator is not None is necessary,
             * since the vector was padded with None when it was resized.
             */
            to_hide != to_make_invisible.end() && *to_hide != None;
            to_hide++)
        m_xdata.unmap_win(*to_hide);

    // The new -> old set difference will produce the list of windows which
    // are on the new desktop, but not on the old - these need to be made
    // visible
    std::set_difference(
        new_desktop_list.begin(), new_desktop_list.end(),
        old_desktop_list.begin(), old_desktop_list.end(),
        to_make_invisible.begin());

    for (std::vector<Window>::iterator to_show = to_make_visible.begin();
            to_show != to_make_visible.end() && *to_show != None;
            to_show++)
        m_xdata.map_win(*to_show);

    // Since we've made some windows visible and some others invisible, we've
    // invalidated the previous stacking order, so restack everything according
    // to what is now visible
    do_relayer();
}

/**
 * Handles a change in location for a particular window.
 */
void ClientModelEvents::handle_location_change()
{
    ChangeLocation const *change =
        dynamic_cast<ChangeLocation const*>(m_change);

    XMoveWindow(change->window, change->x, change->y);
}

/**
 * Handles a change in size for a particular window.
 */
void ClientModelEvents::handle_size_change()
{
    ChangeSize const *change =
        dynamic_cast<ChangeSize const*>(m_change);

    XResizeWIndow(change->window, change->w, change->h);
}

/**
 * Actually does the relayering.
 *
 * This involves sorting the clients, and then sticking the icons and
 * move/resize placeholder on the top.
 */
void ClientModelEvents::do_relayer()
{
    std::vector<Window> ordered_windows;
    m_clients.get_visible_in_layer_order(ordered_windows);

    // Figure out the currently focused client, and where it's at. We'll need
    // this information in order to place it above its peers.
    Window focused_client = m_clients.get_focused();
    Layer focused_layer;
    if (focused_client != None)
        focused_layer = m_clients.find_layer(focused_client);

    for (std::vector<Window>::iterator client_iter = ordered_windows.begin();
            client_iter != ordered_windows.end();
            client_iter++)
    {
        Window current_client = *client_iter;
        Layer current_layer = m_clients.find_layer(current_client);

        // We have to check if we're at the point where we can put up the
        // focused window - this happens when we've passed the layer that the
        // focused window is on. We want to put the focused window above all of
        // its peers, so before putting up the first client on the next layer,
        // put up the focused window
        if (focused_client != None &&
            current_layer == focused_layer + 1)
        {
            m_xdata.raise(focused_client);

            // Make sure to erase the focused client, so that we don't raise
            // it more than once
            focused_client = None;
        }

        if (current_client != focused_client)
            m_xdata.raise(current_client);
    }

    // If we haven't cleared the focused window, then we need to raise it before
    // moving on
    if (focused_client != None)
        m_xdata.raise(focused_client);

    // Now, raise all the clients since they should always be above all other
    // windows so they aren't obscured
    std::vector<Icon*> icon_list;
    m_xmodel.get_icons(icon_list);

    for (std::vector<Icon*>::iterator icon = icon_list.begin();
            icon != icon_list.end();
            icon++)
    {
        Window icon_win = (*icon)->icon;
        m_xdata.raise(icon_win);
    }

    // Finally, raise the placeholder, if there is one
    Window placeholder_win = m_xmodel.get_move_resize_placeholder();
    if (placeholder_win != None)
        m_xdata.raise(placeholder_win);
}
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/queue.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-eventdb/TableRepository.h"
#include "fnord-eventdb/LogTableTail.h"
#include "fnord-msg/MessageEncoder.h"
#include "fnord-msg/MessagePrinter.h"
#include "logjoin/LogJoinBackfill.h"
#include "IndexReader.h"

#include "common.h"
#include "schemas.h"

using namespace cm;
using namespace fnord;

fnord::thread::EventLoop ev;
std::atomic<bool> cm_logjoin_shutdown;

void quit(int n) {
  cm_logjoin_shutdown = true;
}

struct BackfillData {
  Option<String> shop_name;
  Option<String> shop_id;
  Option<uint64_t> category1;
  Option<uint64_t> category2;
  Option<uint64_t> category3;
};

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  cm_logjoin_shutdown = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir",
      "<path>");

  flags.defineFlag(
      "feedserver_addr",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "http://localhost:8000",
      "feedserver addr",
      "<addr>");

  flags.defineFlag(
      "batch_size",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "2048",
      "batch_size",
      "<num>");

  flags.defineFlag(
      "worker_threads",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "4",
      "threads",
      "<num>");

  flags.defineFlag(
      "upload_threads",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "1",
      "threads",
      "<num>");

  flags.defineFlag(
      "no_dryrun",
      fnord::cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "no dryrun",
      "<bool>");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "no_dryrun",
      fnord::cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "no dryrun",
      "<bool>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* args */
  auto index_path = flags.getString("index");
  auto batch_size = flags.getInt("batch_size");
  auto datadir = flags.getString("datadir");
  auto dry_run = !flags.isSet("no_dryrun");
  if (!FileUtil::isDirectory(datadir)) {
    RAISEF(kIOError, "no such directory: $0", datadir);
  }

  URI target_uri("http://localhost:8000/eventdb/insert?table=joined_sessions-dawanda");

  /* event loop, http */
  http::HTTPConnectionPool http(&ev);
  auto evloop_thread = std::thread([] {
    ev.run();
  });

  /* open index */
  auto index = cm::IndexReader::openIndex(index_path);

  /* open table */
  auto schema = joinedSessionsSchema();
  auto table = eventdb::TableReader::open(
      "dawanda_joined_sessions",
      flags.getString("replica"),
      datadir,
      schema);

  auto queries_fid = schema.id("queries");
  auto queryitems_fid = schema.id("queries.items");
  auto qi_id_fid = schema.id("queries.items.item_id");
  auto qi_sid_fid = schema.id("queries.items.shop_id");
  auto qi_sname_fid = schema.id("queries.items.shop_name");
  auto qi_c1_fid = schema.id("queries.items.category1");
  auto qi_c2_fid = schema.id("queries.items.category2");
  auto qi_c3_fid = schema.id("queries.items.category3");

  HashMap<String, BackfillData> cache;

  /* backfill fn */
  auto get_backfill_data = [&cache, &index] (const String& item_id) -> BackfillData {
    auto cached = cache.find(item_id);
    if (!(cached == cache.end())) {
      return cached->second;
    }

    BackfillData data;

    DocID docid { .docid = item_id };
    data.shop_id = index->docIndex()->getField(docid, "shop_id");
    data.shop_name = index->docIndex()->getField(docid, "shop_name");

    auto category1 = index->docIndex()->getField(docid, "category1");
    if (!category1.isEmpty()) {
      data.category1 = Some(std::stoull(category1.get()));
    }

    auto category2 = index->docIndex()->getField(docid, "category2");
    if (!category2.isEmpty()) {
      data.category2 = Some(std::stoull(category2.get()));
    }

    auto category3 = index->docIndex()->getField(docid, "category3");
    if (!category3.isEmpty()) {
      data.category3 = Some(std::stoull(category3.get()));
    }

    cache[item_id] = data;
    return data;
  };

  auto backfill_fn = [
    &schema,
    &queries_fid,
    &queryitems_fid,
    &qi_id_fid,
    &qi_sid_fid,
    &qi_sname_fid,
    &qi_c1_fid,
    &qi_c2_fid,
    &qi_c3_fid,
    &get_backfill_data
  ] (msg::MessageObject* record) {
    auto msg = msg::MessagePrinter::print(*record, schema);

    for (auto& q : record->asObject()) {
      if (q.id != queries_fid) {
        continue;
      }

      for (auto& qi : q.asObject()) {
        if (qi.id != queryitems_fid) {
          continue;
        }

        String item_id;
        for (auto cur = qi.asObject().begin(); cur != qi.asObject().end(); ) {
          auto id = cur->id;

          if (id == qi_id_fid) {
            item_id = cur->asString();
            ++cur;
          } else if (
              id == qi_sid_fid ||
              id == qi_sname_fid ||
              id == qi_c1_fid ||
              id == qi_c2_fid ||
              id == qi_c3_fid) {
            cur = qi.asObject().erase(cur);
          } else {
            ++cur;
          }
        }

        auto bdata = get_backfill_data(item_id);

        if (!bdata.shop_id.isEmpty()) {
          qi.addChild(qi_sid_fid, bdata.shop_id.get());
        }

        if (!bdata.shop_name.isEmpty()) {
          qi.addChild(qi_sname_fid, bdata.shop_name.get());
        }

        if (!bdata.category1.isEmpty()) {
          qi.addChild(qi_c1_fid, bdata.category1.get());
        }

        if (!bdata.category2.isEmpty()) {
          qi.addChild(qi_c2_fid, bdata.category2.get());
        }

        if (!bdata.category3.isEmpty()) {
          qi.addChild(qi_c3_fid, bdata.category3.get());
        }
      }
    }

    fnord::iputs("backfill: $0", msg);
  };

  /* run backfill */
  cm::LogJoinBackfill backfill(
      table,
      backfill_fn,
      "/tmp/logjoin-backfill-state",
      dry_run,
      target_uri,
      &http);

  backfill.start();

  while (backfill.process(batch_size)) {
    if (cm_logjoin_shutdown.load()) {
      break;
    }
  }

  backfill.shutdown();
  ev.shutdown();
  evloop_thread.join();

  return 0;
}

<commit_msg>conversion fix<commit_after>/**
 * Copyright (c) 2015 - The CM Authors <legal@clickmatcher.com>
 *   All Rights Reserved.
 *
 * This file is CONFIDENTIAL -- Distribution or duplication of this material or
 * the information contained herein is strictly forbidden unless prior written
 * permission is obtained.
 */
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "fnord-base/io/filerepository.h"
#include "fnord-base/io/fileutil.h"
#include "fnord-base/application.h"
#include "fnord-base/logging.h"
#include "fnord-base/random.h"
#include "fnord-base/thread/eventloop.h"
#include "fnord-base/thread/threadpool.h"
#include "fnord-base/thread/queue.h"
#include "fnord-base/wallclock.h"
#include "fnord-base/cli/flagparser.h"
#include "fnord-eventdb/TableRepository.h"
#include "fnord-eventdb/LogTableTail.h"
#include "fnord-msg/MessageEncoder.h"
#include "fnord-msg/MessagePrinter.h"
#include "logjoin/LogJoinBackfill.h"
#include "IndexReader.h"

#include "common.h"
#include "schemas.h"

using namespace cm;
using namespace fnord;

fnord::thread::EventLoop ev;
std::atomic<bool> cm_logjoin_shutdown;

void quit(int n) {
  cm_logjoin_shutdown = true;
}

struct BackfillData {
  Option<String> shop_name;
  Option<String> shop_id;
  Option<uint64_t> category1;
  Option<uint64_t> category2;
  Option<uint64_t> category3;
};

int main(int argc, const char** argv) {
  fnord::Application::init();
  fnord::Application::logToStderr();

  cm_logjoin_shutdown = false;
  struct sigaction sa;
  memset(&sa, 0, sizeof(struct sigaction));
  sa.sa_handler = quit;
  sigaction(SIGTERM, &sa, NULL);
  sigaction(SIGQUIT, &sa, NULL);
  sigaction(SIGINT, &sa, NULL);

  fnord::cli::FlagParser flags;

  flags.defineFlag(
      "datadir",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "datadir",
      "<path>");

  flags.defineFlag(
      "feedserver_addr",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "http://localhost:8000",
      "feedserver addr",
      "<addr>");

  flags.defineFlag(
      "batch_size",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "2048",
      "batch_size",
      "<num>");

  flags.defineFlag(
      "worker_threads",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "4",
      "threads",
      "<num>");

  flags.defineFlag(
      "upload_threads",
      fnord::cli::FlagParser::T_INTEGER,
      false,
      NULL,
      "1",
      "threads",
      "<num>");

  flags.defineFlag(
      "no_dryrun",
      fnord::cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "no dryrun",
      "<bool>");

  flags.defineFlag(
      "replica",
      cli::FlagParser::T_STRING,
      true,
      NULL,
      NULL,
      "replica id",
      "<id>");

  flags.defineFlag(
      "no_dryrun",
      fnord::cli::FlagParser::T_SWITCH,
      false,
      NULL,
      NULL,
      "no dryrun",
      "<bool>");

  flags.defineFlag(
      "loglevel",
      fnord::cli::FlagParser::T_STRING,
      false,
      NULL,
      "INFO",
      "loglevel",
      "<level>");

  flags.parseArgv(argc, argv);

  Logger::get()->setMinimumLogLevel(
      strToLogLevel(flags.getString("loglevel")));

  /* args */
  auto index_path = flags.getString("index");
  auto batch_size = flags.getInt("batch_size");
  auto datadir = flags.getString("datadir");
  auto dry_run = !flags.isSet("no_dryrun");
  if (!FileUtil::isDirectory(datadir)) {
    RAISEF(kIOError, "no such directory: $0", datadir);
  }

  URI target_uri("http://localhost:8000/eventdb/insert?table=joined_sessions-dawanda");

  /* event loop, http */
  http::HTTPConnectionPool http(&ev);
  auto evloop_thread = std::thread([] {
    ev.run();
  });

  /* open index */
  auto index = cm::IndexReader::openIndex(index_path);

  /* open table */
  auto schema = joinedSessionsSchema();
  auto table = eventdb::TableReader::open(
      "dawanda_joined_sessions",
      flags.getString("replica"),
      datadir,
      schema);

  auto queries_fid = schema.id("queries");
  auto queryitems_fid = schema.id("queries.items");
  auto qi_id_fid = schema.id("queries.items.item_id");
  auto qi_sid_fid = schema.id("queries.items.shop_id");
  auto qi_sname_fid = schema.id("queries.items.shop_name");
  auto qi_c1_fid = schema.id("queries.items.category1");
  auto qi_c2_fid = schema.id("queries.items.category2");
  auto qi_c3_fid = schema.id("queries.items.category3");

  HashMap<String, BackfillData> cache;

  /* backfill fn */
  auto get_backfill_data = [&cache, &index] (const String& item_id) -> BackfillData {
    auto cached = cache.find(item_id);
    if (!(cached == cache.end())) {
      return cached->second;
    }

    BackfillData data;

    DocID docid { .docid = item_id };
    data.shop_id = index->docIndex()->getField(docid, "shop_id");
    data.shop_name = index->docIndex()->getField(docid, "shop_name");

    auto category1 = index->docIndex()->getField(docid, "category1");
    if (!category1.isEmpty()) {
      data.category1 = Some((uint64_t) std::stoull(category1.get()));
    }

    auto category2 = index->docIndex()->getField(docid, "category2");
    if (!category2.isEmpty()) {
      data.category2 = Some((uint64_t) std::stoull(category2.get()));
    }

    auto category3 = index->docIndex()->getField(docid, "category3");
    if (!category3.isEmpty()) {
      data.category3 = Some((uint64_t) std::stoull(category3.get()));
    }

    cache[item_id] = data;
    return data;
  };

  auto backfill_fn = [
    &schema,
    &queries_fid,
    &queryitems_fid,
    &qi_id_fid,
    &qi_sid_fid,
    &qi_sname_fid,
    &qi_c1_fid,
    &qi_c2_fid,
    &qi_c3_fid,
    &get_backfill_data
  ] (msg::MessageObject* record) {
    auto msg = msg::MessagePrinter::print(*record, schema);

    for (auto& q : record->asObject()) {
      if (q.id != queries_fid) {
        continue;
      }

      for (auto& qi : q.asObject()) {
        if (qi.id != queryitems_fid) {
          continue;
        }

        String item_id;
        for (auto cur = qi.asObject().begin(); cur != qi.asObject().end(); ) {
          auto id = cur->id;

          if (id == qi_id_fid) {
            item_id = cur->asString();
            ++cur;
          } else if (
              id == qi_sid_fid ||
              id == qi_sname_fid ||
              id == qi_c1_fid ||
              id == qi_c2_fid ||
              id == qi_c3_fid) {
            cur = qi.asObject().erase(cur);
          } else {
            ++cur;
          }
        }

        auto bdata = get_backfill_data(item_id);

        if (!bdata.shop_id.isEmpty()) {
          qi.addChild(qi_sid_fid, bdata.shop_id.get());
        }

        if (!bdata.shop_name.isEmpty()) {
          qi.addChild(qi_sname_fid, bdata.shop_name.get());
        }

        if (!bdata.category1.isEmpty()) {
          qi.addChild(qi_c1_fid, bdata.category1.get());
        }

        if (!bdata.category2.isEmpty()) {
          qi.addChild(qi_c2_fid, bdata.category2.get());
        }

        if (!bdata.category3.isEmpty()) {
          qi.addChild(qi_c3_fid, bdata.category3.get());
        }
      }
    }

    fnord::iputs("backfill: $0", msg);
  };

  /* run backfill */
  cm::LogJoinBackfill backfill(
      table,
      backfill_fn,
      "/tmp/logjoin-backfill-state",
      dry_run,
      target_uri,
      &http);

  backfill.start();

  while (backfill.process(batch_size)) {
    if (cm_logjoin_shutdown.load()) {
      break;
    }
  }

  backfill.shutdown();
  ev.shutdown();
  evloop_thread.join();

  return 0;
}

<|endoftext|>


<commit_before>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "itkExceptionObject.h"

#include "otbImageFileReader.h"
#include "otbList.h"
#include "otbImage.h"

int otbList(int argc, char * argv[])
{
  try
    {
      const char * inputFilename1 = argv[1];
      const char * inputFilename2 = argv[2];
      const char * inputFilename3 = argv[3];
      const unsigned int Dimension = 2;

      typedef unsigned char InputPixelType;
      typedef otb::Image<InputPixelType,Dimension> InputImageType;
      typedef otb::ImageFileReader<InputImageType> ReaderType;
      typedef otb::List<InputImageType> ImageListType;

      // Reading image 1
      ReaderType::Pointer reader1 = ReaderType::New();
      reader1->SetFileName(inputFilename1);
      reader1->Update();

      // Reading image 2
      ReaderType::Pointer reader2 = ReaderType::New();
      reader2->SetFileName(inputFilename2);
      reader2->Update();

      // Reading image 3
      ReaderType::Pointer reader3 = ReaderType::New();
      reader3->SetFileName(inputFilename3);
      reader3->Update();

      // Instantiating the tested object
      ImageListType::Pointer imageList = ImageListType::New();

      // Testing reserve/capacity
      imageList->Reserve(2);

      otbControlConditionTestMacro(imageList->Capacity()!=2,"Reserve/Capacity()");

      // Testing Size/Element accessor
      imageList->PushBack(reader1->GetOutput());
      imageList->PushBack(reader2->GetOutput());

      otbControlConditionTestMacro(imageList->Size()!=2,"PushBack/Size()");
      otbControlConditionTestMacro(imageList->GetNthElement(0)!=reader1->GetOutput(),"PushBack/GetNthElement(0)");
      otbControlConditionTestMacro(imageList->GetNthElement(1)!=reader2->GetOutput(),"PushBack/GetNthElement(1)");
      otbControlConditionTestMacro(imageList->Front()!=reader1->GetOutput(),"PushBack/Front()");
      otbControlConditionTestMacro(imageList->Back()!=reader2->GetOutput(),"PushBack/Back()");

      // Testing resizing and related method
      imageList->Resize(3);
       otbControlConditionTestMacro(imageList->Size()!=3,"Resize/Size()");

      // Testing explicit setter
      imageList->SetNthElement(2,reader3->GetOutput());

      otbControlConditionTestMacro(imageList->Size()!=3,"SetNthElement/Size()");
      otbControlConditionTestMacro(imageList->GetNthElement(2)!=reader3->GetOutput(),"SetNthElement/GetNthElement(2)");

      // Testing erase operation
      imageList->Erase(2);
      otbControlConditionTestMacro((imageList->Size()!=2)
	 ||(imageList->GetNthElement(0)!=reader1->GetOutput())
	 ||(imageList->GetNthElement(1)!=reader2->GetOutput()),"Erase(3)");

      // Testing iterator
      ImageListType::Iterator iter = imageList->Begin();

      otbControlConditionTestMacro(!(iter!=imageList->End()),"Iterator/Begin()!=Iterator/End()");
      unsigned int index = 0;
      while(iter!=imageList->End())
	{
	  otbControlConditionTestMacro((index==0)&&(reader1->GetOutput()!=iter.Get()),"Iterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader2->GetOutput()!=iter.Get()),"Iterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"Iterator/OutOfBound/iter.Get()");
	  ++index;
	  ++iter;
	}

      // Testing const iterator
      ImageListType::ConstIterator constIter = imageList->Begin();
      index = 0;
      while(constIter!=imageList->End())
	{
	  otbControlConditionTestMacro((index==0)&&(reader1->GetOutput()!=constIter.Get()),"ConstIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader2->GetOutput()!=constIter.Get()),"ConstIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ConstIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++constIter;
	}

      //Testing reverse iterator
      ImageListType::ReverseIterator revIter = imageList->ReverseBegin();
      otbControlConditionTestMacro(!(revIter!=imageList->ReverseEnd()),"ReverseIterator/ReverseBegin()!=ReverseIterator/ReverseEnd()");

      index = 0;
      while(revIter!=imageList->ReverseEnd())
	{
	  otbControlConditionTestMacro((index==0)&&(reader2->GetOutput()!=revIter.Get()),"ReverseIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader1->GetOutput()!=revIter.Get()),"ReverseIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ReverseIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++revIter;
	}

      //Testing const reverse iterator
      ImageListType::ReverseConstIterator revConstIter = imageList->ReverseBegin();
      index = 0;
      while(revConstIter!=imageList->ReverseEnd())
	{
	  otbControlConditionTestMacro((index==0)&&(reader2->GetOutput()!=revConstIter.Get()),"ReverseConstIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader1->GetOutput()!=revConstIter.Get()),"ReverseConstIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ReverseConstIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++revConstIter;
	}

      // Testing clear
      imageList->Clear();

      otbControlConditionTestMacro(imageList->Size()!=0,"Clear()");

      // Testing erase with iterators
      imageList->PushBack(reader1->GetOutput());
      imageList->PushBack(reader2->GetOutput());
      imageList->PushBack(reader3->GetOutput());

      ImageListType::Iterator begin = imageList->Begin()+1;
      ImageListType::Iterator end = imageList->End();
      imageList->Erase(begin,end);

      otbControlConditionTestMacro(imageList->Size()!=1,"Erase(Iterator,Iterator)/Size()");
      otbControlConditionTestMacro(imageList->Back()!=reader1->GetOutput(),"Erase(Iterator,Iterator)/Back()");
    }

  catch( itk::ExceptionObject & err )
    {
    std::cout << "Exception itk::ExceptionObject thrown !" << std::endl;
    std::cout << err << std::endl;
    return EXIT_FAILURE;
    }

  catch( ... )
    {
    std::cout << "Unknown exception thrown !" << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

<commit_msg>Ajout test sur operator+ et operator- pour iterator<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "itkExceptionObject.h"

#include "otbImageFileReader.h"
#include "otbList.h"
#include "otbImage.h"

int otbList(int argc, char * argv[])
{
  try
    {
      const char * inputFilename1 = argv[1];
      const char * inputFilename2 = argv[2];
      const char * inputFilename3 = argv[3];
      const unsigned int Dimension = 2;

      typedef unsigned char InputPixelType;
      typedef otb::Image<InputPixelType,Dimension> InputImageType;
      typedef otb::ImageFileReader<InputImageType> ReaderType;
      typedef otb::List<InputImageType> ImageListType;

      // Reading image 1
      ReaderType::Pointer reader1 = ReaderType::New();
      reader1->SetFileName(inputFilename1);
      reader1->Update();

      // Reading image 2
      ReaderType::Pointer reader2 = ReaderType::New();
      reader2->SetFileName(inputFilename2);
      reader2->Update();

      // Reading image 3
      ReaderType::Pointer reader3 = ReaderType::New();
      reader3->SetFileName(inputFilename3);
      reader3->Update();

      // Instantiating the tested object
      ImageListType::Pointer imageList = ImageListType::New();

      // Testing reserve/capacity
      imageList->Reserve(2);

      otbControlConditionTestMacro(imageList->Capacity()!=2,"Reserve/Capacity()");

      // Testing Size/Element accessor
      imageList->PushBack(reader1->GetOutput());
      imageList->PushBack(reader2->GetOutput());

      otbControlConditionTestMacro(imageList->Size()!=2,"PushBack/Size()");
      otbControlConditionTestMacro(imageList->GetNthElement(0)!=reader1->GetOutput(),"PushBack/GetNthElement(0)");
      otbControlConditionTestMacro(imageList->GetNthElement(1)!=reader2->GetOutput(),"PushBack/GetNthElement(1)");
      otbControlConditionTestMacro(imageList->Front()!=reader1->GetOutput(),"PushBack/Front()");
      otbControlConditionTestMacro(imageList->Back()!=reader2->GetOutput(),"PushBack/Back()");

      // Testing resizing and related method
      imageList->Resize(3);
       otbControlConditionTestMacro(imageList->Size()!=3,"Resize/Size()");

      // Testing explicit setter
      imageList->SetNthElement(2,reader3->GetOutput());

      otbControlConditionTestMacro(imageList->Size()!=3,"SetNthElement/Size()");
      otbControlConditionTestMacro(imageList->GetNthElement(2)!=reader3->GetOutput(),"SetNthElement/GetNthElement(2)");

      // Testing erase operation
      imageList->Erase(2);
      otbControlConditionTestMacro((imageList->Size()!=2)
	 ||(imageList->GetNthElement(0)!=reader1->GetOutput())
	 ||(imageList->GetNthElement(1)!=reader2->GetOutput()),"Erase(3)");

      // Testing iterator
      ImageListType::Iterator iter = imageList->Begin();

      otbControlConditionTestMacro(!(iter!=imageList->End()),"Iterator/Begin()!=Iterator/End()");
      unsigned int index = 0;
      while(iter!=imageList->End())
	{
	  otbControlConditionTestMacro((index==0)&&(reader1->GetOutput()!=iter.Get()),"Iterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader2->GetOutput()!=iter.Get()),"Iterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"Iterator/OutOfBound/iter.Get()");
	  ++index;
	  ++iter;
	}

      // Testing operator+
      iter = imageList->Begin();
      index=0;
      otbControlConditionTestMacro(imageList->GetNthElement(0) != iter.Get(),
				   "Iterator != GetNthElement(0)");
      otbControlConditionTestMacro(imageList->GetNthElement(1) != (iter+1).Get(),
				   "Iterator+1 != GetNthElement(1)");
      ++iter;
      otbControlConditionTestMacro(imageList->GetNthElement(1) != iter.Get(),
				   "Iterator != GetNthElement(1)");
      otbControlConditionTestMacro(imageList->GetNthElement(0) != (iter-1).Get(),
				   "Iterator-1 != GetNthElement(0)");

      // Testing const iterator
      ImageListType::ConstIterator constIter = imageList->Begin();
      index = 0;
      while(constIter!=imageList->End())
	{
	  otbControlConditionTestMacro((index==0)&&(reader1->GetOutput()!=constIter.Get()),"ConstIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader2->GetOutput()!=constIter.Get()),"ConstIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ConstIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++constIter;
	}

      //Testing reverse iterator
      ImageListType::ReverseIterator revIter = imageList->ReverseBegin();
      otbControlConditionTestMacro(!(revIter!=imageList->ReverseEnd()),"ReverseIterator/ReverseBegin()!=ReverseIterator/ReverseEnd()");

      index = 0;
      while(revIter!=imageList->ReverseEnd())
	{
	  otbControlConditionTestMacro((index==0)&&(reader2->GetOutput()!=revIter.Get()),"ReverseIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader1->GetOutput()!=revIter.Get()),"ReverseIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ReverseIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++revIter;
	}

      //Testing const reverse iterator
      ImageListType::ReverseConstIterator revConstIter = imageList->ReverseBegin();
      index = 0;
      while(revConstIter!=imageList->ReverseEnd())
	{
	  otbControlConditionTestMacro((index==0)&&(reader2->GetOutput()!=revConstIter.Get()),"ReverseConstIterator/1/iter.Get()");
	  otbControlConditionTestMacro((index==1)&&(reader1->GetOutput()!=revConstIter.Get()),"ReverseConstIterator/2/iter.Get()");
	  otbControlConditionTestMacro(index>1,"ReverseConstIterator/OutOfBound/iter.Get()");
	  ++index;
	  ++revConstIter;
	}

      // Testing clear
      imageList->Clear();

      otbControlConditionTestMacro(imageList->Size()!=0,"Clear()");

      // Testing erase with iterators
      imageList->PushBack(reader1->GetOutput());
      imageList->PushBack(reader2->GetOutput());
      imageList->PushBack(reader3->GetOutput());

      ImageListType::Iterator begin = imageList->Begin()+1;
      ImageListType::Iterator end = imageList->End();
      imageList->Erase(begin,end);

      otbControlConditionTestMacro(imageList->Size()!=1,"Erase(Iterator,Iterator)/Size()");
      otbControlConditionTestMacro(imageList->Back()!=reader1->GetOutput(),"Erase(Iterator,Iterator)/Back()");
    }

  catch( itk::ExceptionObject & err )
    {
    std::cout << "Exception itk::ExceptionObject thrown !" << std::endl;
    std::cout << err << std::endl;
    return EXIT_FAILURE;
    }

  catch( ... )
    {
    std::cout << "Unknown exception thrown !" << std::endl;
    return EXIT_FAILURE;
    }

  return EXIT_SUCCESS;
}

<|endoftext|>


<commit_before>#ifndef INCLUDE_AL_BUFFER_HPP
#define INCLUDE_AL_BUFFER_HPP

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2012. The Regents of the University of California.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

		Redistributions of source code must retain the above copyright notice,
		this list of conditions and the following disclaimer.

		Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.

		Neither the name of the University of California nor the names of its
		contributors may be used to endorse or promote products derived from
		this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

	File description:
	Variably sized one-dimensional array

	File author(s):
	Lance Putnam, 2010, putnam.lance@gmail.com
*/

#include <algorithm>
#include <vector>

namespace al{

/// Buffer

/// This buffer automatically expands itself as new elements are added.
/// Additionally, its logical size can be reduced without triggering memory
/// deallocations.
///
/// @ingroup allocore
template <class T, class Alloc=std::allocator<T> >
class Buffer : protected Alloc{
	typedef Alloc super;
public:

	/// @param[in] size			Initial size
	explicit Buffer(int size=0)
	:	mElems(size), mSize(size)
	{}

	/// @param[in] size			Initial size
	/// @param[in] capacity		Initial capacity
	Buffer(int size, int capacity)
	:	mElems(capacity), mSize(size)
	{}

	~Buffer(){}

	int capacity() const { return mElems.size(); }		///< Returns total capacity
	int size() const { return mSize; }					///< Returns size
	const T * elems() const { return &mElems[0]; }		///< Returns C pointer to elements
	T * elems(){ return &mElems[0]; }					///< Returns C pointer to elements

	T * begin(){ return elems(); }
	const T * begin() const { return elems(); }
	T * end(){ return elems() + size(); }
	const T * end() const { return elems() + size(); }

	/// Get element at index
	T& operator[](int i){ return mElems[i]; }

	/// Get element at index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Assign value to elements

	/// This function fills a Buffer with n copies of the given value. Note that
	/// the assignment completely changes the buffer and that the resulting size
	/// is the same as the number of elements assigned. Old data may be lost.
	void assign(int n, const T& v){ mElems.assign(n,v); }

	/// Get last element
	T& last(){ return mElems[size()-1]; }
	const T& last() const { return mElems[size()-1]; }

	/// Resets size to zero without deallocating allocated memory
	void reset(){ mSize=0; }

	/// Resize buffer

	/// This will set both the size and capacity of the buffer to the requested
	/// size. If the number is smaller than the current size the buffer is
	/// truncated, otherwise the buffer is extended and new elements are
	/// default-constructed.
	void resize(int n){
		mElems.resize(n);
		setSize(n);
	}

	/// Set size of buffer

	/// If the requested size is larger than the current capacity, then the
	/// buffer will be resized.
	void size(int n){
		if(capacity() < n) resize(n);
		else setSize(n);
	}

	/// Appends element to end of buffer growing its size if necessary
	void append(const T& v, double growFactor=2){

		// Grow array if too small
		if(size() >= capacity()){
			// Copy argument since it may be an element in current memory range
			// which may become invalid after the resize.
			const T vsafecopy = v;
			mElems.resize((size() ? size() : 4)*growFactor);
			super::construct(elems()+size(), vsafecopy);
		}
		else{
			super::construct(elems()+size(), v);
		}
		++mSize;
	}
	/// synonym for append():
	void push_back(const T& v, double growFactor=2) { append(v, growFactor); }

	/// Append elements of another Buffer

	/// Note: not safe to apply this to itself
	///
	void append(const Buffer<T>& src){
		append(src.elems(), src.size());
	}

	/// Append elements of an array
	void append(const T * src, int len){
		int oldsize = size();
		size(size() + len);
		std::copy(src, src + len, mElems.begin() + oldsize);
	}

	/// Repeat last element
	void repeatLast(){ append(last()); }

	/// Insert new elements after each existing element

	/// @tparam n		Expansion factor; new size is n times old size
	/// @tparam dup		If true, new elements are duplicates of existing elements.
	///					If false, new elements are default constructed.
	template <int n, bool dup>
	void expand(){
		size(size()*n);
		const int Nd = dup ? n : 1;
		for(int i=size()/n-1; i>=0; --i){
			const T& v = (*this)[i];
			for(int j=0; j<Nd; ++j) Alloc::construct(elems()+n*i+j, v);
		}
	}

private:
	std::vector<T, Alloc> mElems;
	int mSize;		// logical size array

	void setSize(int n){ mSize=n; }
};

/// Ring buffer

/// This buffer allows potentially large amounts of data to be buffered without
/// moving memory. This is accomplished by use of a moving write tap.
///
/// @ingroup allocore
template <class T, class Alloc=std::allocator<T> >
class RingBuffer : protected Alloc {
public:

	/// Default constructor; does not allocate memory
	RingBuffer(): mPos(-1), mFill(0){}

	/// @param[in] size		number of elements
	/// @param[in] v		value to initialize elements to
	explicit RingBuffer(unsigned size, const T& v=T())
	:	mPos(size), mFill(0)
	{
		resize(size,v);
	}

	/// Get number of elements
	int size() const { return mElems.size(); }

	/// Get absolute index of most recently written element
	int pos() const { return mPos; }

	/// Get fill amount of buffer
	int fill() const { return mFill; }

	/// Get element at absolute index
	T& operator[](int i){ return mElems[i]; }

	/// Get element at absolute index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Obtain next element in buffer

	/// This method returns a reference to the next available element in the
	/// buffer. This is an alternative to calling write() that does not require
	/// constructing a new object, but instead returns the oldest element in
	/// the buffer. The returned reference should be assumed to be in an unknown
	/// state, thus should be initialized properly.
	T& next(){
		if(mFill < size()) ++mFill;
		++mPos; if(pos() == size()){ mPos=0; }
		return mElems[pos()];
	}

	/// Write new element
	void write(const T& v){
		Alloc::construct(&next(), v);
	}

	/// Get reference to element relative to newest element
	T& read(int i){ return mElems[wrapOnce(pos()-i, size())]; }

	/// Get reference to element relative to newest element (read-only)
	const T& read(int i) const { return readFrom(pos(), i); }

	/// Get reference to older element relative to some newer element (read-only)

	/// @param[in] from		absolute index the read is relative to
	/// @param[in] dist		distance into past relative to 'from' of the returned element
	const T& readFrom(int from, int dist) const {
		return mElems[wrapOnce(from-dist, size())];
	}

	/// \returns reference to newest element
	T& newest(){ return mElems[pos()]; }

	/// \returns reference to newest element (read-only)
	const T& newest() const { return mElems[pos()]; }

	/// Set write position to start of array and zero fill amount
	void reset(){
		mPos = size()-1;
		mFill = 0;
	}

	/// Resize buffer

	/// @param[in] n	number of elements
	/// @param[in] v	initialization value of newly allocated elements
	void resize(int n, const T& v=T()){
		mElems.resize(n,v);
		if(mPos >=n) mPos = n-1;
	}

protected:
	std::vector<T, Alloc> mElems;
	int mPos;
	int mFill;

	// Moves value one period closer to interval [0, max)
	static int wrapOnce(int v, int max){
		if(v <  0)   return v+max;
		if(v >= max) return v-max;
		return v;
	}
};

/// Constant size shift buffer

/// This is a first-in, first-out buffer with a constant number of elements.
/// Adding new elements to the buffer physically moves existing elements. The
/// advantage of moving memory like this is that elements stay logically ordered
/// making access faster and operating on the history easier.
///
/// @ingroup allocore
template <int N, class T>
class ShiftBuffer{
public:

	/// @param[in] v	Value to initialize all elements to
	ShiftBuffer(const T& v=T()){ assign(v); }

	/// Get number of elements
	static int size(){ return N; }

	/// Get pointer to elements (read-only)
	const T * elems() const { return &mElems[0]; }

	/// Get pointer to elements
	T * elems(){ return &mElems[0]; }

	/// Get reference to element at index
	T& operator[](int i){ return mElems[i];}

	/// Get reference to element at index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Push new element onto buffer. Newest element is at index 0.
	void operator()(const T& v){
		for(int i=N-1; i>0; --i) mElems[i] = mElems[i-1];
		mElems[0]=v;
	}

	/// Set all elements to argument
	void assign(const T& v){ for(int i=0;i<N;++i) mElems[i]=v; }

	/// Zero bytes of all elements
	void zero(){ memset(mElems, 0, N * sizeof(T)); }

protected:
	T mElems[N];
};

} // al::

#endif
<commit_msg>RingBuffer: add oldest function<commit_after>#ifndef INCLUDE_AL_BUFFER_HPP
#define INCLUDE_AL_BUFFER_HPP

/*	Allocore --
	Multimedia / virtual environment application class library

	Copyright (C) 2009. AlloSphere Research Group, Media Arts & Technology, UCSB.
	Copyright (C) 2012. The Regents of the University of California.
	All rights reserved.

	Redistribution and use in source and binary forms, with or without
	modification, are permitted provided that the following conditions are met:

		Redistributions of source code must retain the above copyright notice,
		this list of conditions and the following disclaimer.

		Redistributions in binary form must reproduce the above copyright
		notice, this list of conditions and the following disclaimer in the
		documentation and/or other materials provided with the distribution.

		Neither the name of the University of California nor the names of its
		contributors may be used to endorse or promote products derived from
		this software without specific prior written permission.

	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
	AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
	ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
	LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
	CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
	POSSIBILITY OF SUCH DAMAGE.

	File description:
	Variably sized one-dimensional array

	File author(s):
	Lance Putnam, 2010, putnam.lance@gmail.com
*/

#include <algorithm>
#include <vector>

namespace al{

/// Buffer

/// This buffer automatically expands itself as new elements are added.
/// Additionally, its logical size can be reduced without triggering memory
/// deallocations.
///
/// @ingroup allocore
template <class T, class Alloc=std::allocator<T> >
class Buffer : protected Alloc{
	typedef Alloc super;
public:

	/// @param[in] size			Initial size
	explicit Buffer(int size=0)
	:	mElems(size), mSize(size)
	{}

	/// @param[in] size			Initial size
	/// @param[in] capacity		Initial capacity
	Buffer(int size, int capacity)
	:	mElems(capacity), mSize(size)
	{}

	~Buffer(){}

	int capacity() const { return mElems.size(); }		///< Returns total capacity
	int size() const { return mSize; }					///< Returns size
	const T * elems() const { return &mElems[0]; }		///< Returns C pointer to elements
	T * elems(){ return &mElems[0]; }					///< Returns C pointer to elements

	T * begin(){ return elems(); }
	const T * begin() const { return elems(); }
	T * end(){ return elems() + size(); }
	const T * end() const { return elems() + size(); }

	/// Get element at index
	T& operator[](int i){ return mElems[i]; }

	/// Get element at index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Assign value to elements

	/// This function fills a Buffer with n copies of the given value. Note that
	/// the assignment completely changes the buffer and that the resulting size
	/// is the same as the number of elements assigned. Old data may be lost.
	void assign(int n, const T& v){ mElems.assign(n,v); }

	/// Get last element
	T& last(){ return mElems[size()-1]; }
	const T& last() const { return mElems[size()-1]; }

	/// Resets size to zero without deallocating allocated memory
	void reset(){ mSize=0; }

	/// Resize buffer

	/// This will set both the size and capacity of the buffer to the requested
	/// size. If the number is smaller than the current size the buffer is
	/// truncated, otherwise the buffer is extended and new elements are
	/// default-constructed.
	void resize(int n){
		mElems.resize(n);
		setSize(n);
	}

	/// Set size of buffer

	/// If the requested size is larger than the current capacity, then the
	/// buffer will be resized.
	void size(int n){
		if(capacity() < n) resize(n);
		else setSize(n);
	}

	/// Appends element to end of buffer growing its size if necessary
	void append(const T& v, double growFactor=2){

		// Grow array if too small
		if(size() >= capacity()){
			// Copy argument since it may be an element in current memory range
			// which may become invalid after the resize.
			const T vsafecopy = v;
			mElems.resize((size() ? size() : 4)*growFactor);
			super::construct(elems()+size(), vsafecopy);
		}
		else{
			super::construct(elems()+size(), v);
		}
		++mSize;
	}
	/// synonym for append():
	void push_back(const T& v, double growFactor=2) { append(v, growFactor); }

	/// Append elements of another Buffer

	/// Note: not safe to apply this to itself
	///
	void append(const Buffer<T>& src){
		append(src.elems(), src.size());
	}

	/// Append elements of an array
	void append(const T * src, int len){
		int oldsize = size();
		size(size() + len);
		std::copy(src, src + len, mElems.begin() + oldsize);
	}

	/// Repeat last element
	void repeatLast(){ append(last()); }

	/// Insert new elements after each existing element

	/// @tparam n		Expansion factor; new size is n times old size
	/// @tparam dup		If true, new elements are duplicates of existing elements.
	///					If false, new elements are default constructed.
	template <int n, bool dup>
	void expand(){
		size(size()*n);
		const int Nd = dup ? n : 1;
		for(int i=size()/n-1; i>=0; --i){
			const T& v = (*this)[i];
			for(int j=0; j<Nd; ++j) Alloc::construct(elems()+n*i+j, v);
		}
	}

private:
	std::vector<T, Alloc> mElems;
	int mSize;		// logical size array

	void setSize(int n){ mSize=n; }
};

/// Ring buffer

/// This buffer allows potentially large amounts of data to be buffered without
/// moving memory. This is accomplished by use of a moving write tap.
///
/// @ingroup allocore
template <class T, class Alloc=std::allocator<T> >
class RingBuffer : protected Alloc {
public:

	/// Default constructor; does not allocate memory
	RingBuffer(): mPos(-1){}

	/// @param[in] size		number of elements
	/// @param[in] v		value to initialize elements to
	explicit RingBuffer(unsigned size, const T& v=T())
	:	mPos(size)
	{
		resize(size,v);
	}

	/// Get number of elements
	int size() const { return mElems.size(); }

	/// Get absolute index of most recently written element
	int pos() const { return mPos; }

	/// Get fill amount of buffer
	int fill() const { return mFill; }

	/// Get element at absolute index
	T& operator[](int i){ return mElems[i]; }

	/// Get element at absolute index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Obtain next element in buffer

	/// This method returns a reference to the next available element in the
	/// buffer. This is an alternative to calling write() that does not require
	/// constructing a new object, but instead returns the oldest element in
	/// the buffer. The returned reference should be assumed to be in an unknown
	/// state, thus should be initialized properly.
	T& next(){
		if(mFill < size()) ++mFill;
		++mPos; if(pos() == size()){ mPos=0; }
		return newest();
	}

	/// Write new element
	void write(const T& v){
		Alloc::construct(&next(), v);
	}

	/// Get reference to element relative to newest element
	const T& read(int i) const { return readFrom(pos(), i); }
	T& read(int i){ return const_cast<T&>(const_cast<const RingBuffer<T,Alloc>*>(this)->read(i)); }

	/// Get reference to older element relative to some newer element

	/// @param[in] from		absolute index the read is relative to
	/// @param[in] dist		distance into past relative to 'from' of the returned element
	const T& readFrom(int from, int dist) const {
		return mElems[wrapOnce(from-dist, size())];
	}
	T& readFrom(int from, int dist){
		return const_cast<T&>(const_cast<const RingBuffer<T,Alloc>*>(this)->readFrom(from,dist));
	}

	/// \returns reference to newest (last in) element
	const T& newest() const { return mElems[pos()]; }
	T& newest(){ return const_cast<T&>(const_cast<const RingBuffer<T,Alloc>*>(this)->newest()); }

	/// \returns reference to oldest (first in) element
	const T& oldest() const { return read(fill()-1); }
	T& oldest(){ return const_cast<T&>(const_cast<const RingBuffer<T,Alloc>*>(this)->oldest()); }

	/// Set write position to start of array and zero fill amount
	void reset(){
		mPos = size()-1;
		mFill = 0;
	}

	/// Resize buffer

	/// @param[in] n	number of elements
	/// @param[in] v	initialization value of newly allocated elements
	void resize(int n, const T& v=T()){
		mElems.resize(n,v);
		if(mPos >=n) mPos = n-1;
	}

protected:
	std::vector<T, Alloc> mElems;
	int mPos;
	int mFill = 0;

	// Moves value one period closer to interval [0, max)
	static int wrapOnce(int v, int max){
		if(v <  0)   return v+max;
		if(v >= max) return v-max;
		return v;
	}
};

/// Constant size shift buffer

/// This is a first-in, first-out buffer with a constant number of elements.
/// Adding new elements to the buffer physically moves existing elements. The
/// advantage of moving memory like this is that elements stay logically ordered
/// making access faster and operating on the history easier.
///
/// @ingroup allocore
template <int N, class T>
class ShiftBuffer{
public:

	/// @param[in] v	Value to initialize all elements to
	ShiftBuffer(const T& v=T()){ assign(v); }

	/// Get number of elements
	static int size(){ return N; }

	/// Get pointer to elements (read-only)
	const T * elems() const { return &mElems[0]; }

	/// Get pointer to elements
	T * elems(){ return &mElems[0]; }

	/// Get reference to element at index
	T& operator[](int i){ return mElems[i];}

	/// Get reference to element at index (read-only)
	const T& operator[](int i) const { return mElems[i]; }

	/// Push new element onto buffer. Newest element is at index 0.
	void operator()(const T& v){
		for(int i=N-1; i>0; --i) mElems[i] = mElems[i-1];
		mElems[0]=v;
	}

	/// Set all elements to argument
	void assign(const T& v){ for(int i=0;i<N;++i) mElems[i]=v; }

	/// Zero bytes of all elements
	void zero(){ memset(mElems, 0, N * sizeof(T)); }

protected:
	T mElems[N];
};

} // al::

#endif
<|endoftext|>


<commit_before>#include <iostream>
#include "CUDADenseGraphBFSearcher.h"
#include "Device.h"

using namespace sqaod_cuda;

template<class real>
void runBFSearcher() {
    typedef sq::MatrixType<real> Matrix;

    Device device;
    device.initialize();

    int N = 100;
    CUDADenseGraphBFSearcher<real> solver;
    solver.assignDevice(device);
    Matrix W = Matrix::eye(N);
    solver.setProblem(W, sq::optMinimize);
    solver.search();

    device.finalize();

}

int main() {
    runBFSearcher<float>();
    runBFSearcher<double>();
}
<commit_msg>setProblem() -> setQUBO().<commit_after>#include <iostream>
#include "CUDADenseGraphBFSearcher.h"
#include "Device.h"

using namespace sqaod_cuda;

template<class real>
void runBFSearcher() {
    typedef sq::MatrixType<real> Matrix;

    Device device;
    device.initialize();

    int N = 100;
    CUDADenseGraphBFSearcher<real> solver;
    solver.assignDevice(device);
    Matrix W = Matrix::eye(N);
    solver.setQUBO(W, sq::optMinimize);
    solver.search();

    device.finalize();

}

int main() {
    runBFSearcher<float>();
    runBFSearcher<double>();
}
<|endoftext|>


<commit_before>// Copyright 2012-2013 Samplecount S.L.
//
// 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 "Methcla/Audio/Engine.hpp"
#include "Methcla/Audio/Group.hpp"
#include "Methcla/Audio/Synth.hpp"
#include "Methcla/LV2/Atom.hpp"

#include <boost/current_function.hpp>
#include <cstdlib>
#include <iostream>

#include "lv2/lv2plug.in/ns/ext/atom/util.h"

using namespace Methcla;
using namespace Methcla::Audio;
using namespace Methcla::Memory;
using namespace std;

void NodeMap::insert(Node* node)
{
    NodeId id = node->id();
    if (m_nodes[id] != 0)
        BOOST_THROW_EXCEPTION(DuplicateNodeId() << ErrorInfoNodeId(id));
    m_nodes[id] = node;
}

void NodeMap::release(const NodeId& nodeId)
{
    if (m_nodes[nodeId] == 0)
        BOOST_THROW_EXCEPTION(InvalidNodeId() << ErrorInfoNodeId(nodeId));
    m_nodes[nodeId] = 0;
}

Environment::Environment(PluginManager& pluginManager, const Options& options)
    : m_sampleRate(options.sampleRate)
    , m_blockSize(options.blockSize)
    , m_plugins(pluginManager)
    , m_audioBuses    (options.numHardwareInputChannels+options.numHardwareOutputChannels+options.maxNumAudioBuses)
    , m_freeAudioBuses(options.numHardwareInputChannels+options.numHardwareOutputChannels+options.maxNumAudioBuses)
    , m_nodes(options.maxNumNodes)
    , m_rootNode(Group::construct(*this, nullptr, Node::kAddToTail))
    , m_epoch(0)
    , m_worker(uriMap())
    , m_uris(uriMap())
    , m_parser(uriMap().lv2Map())
    , m_printer(uriMap().lv2Map(), uriMap().lv2Unmap())
{
    const Epoch prevEpoch = epoch() - 1;

    m_audioInputChannels.reserve(options.numHardwareInputChannels);
    for (uint32_t i=0; i < options.numHardwareInputChannels; i++) {
        ExternalAudioBus* bus = new ExternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_audioBuses.insert(bus->id(), bus);
        m_audioInputChannels.push_back(bus);
    }

    m_audioOutputChannels.reserve(options.numHardwareOutputChannels);
    for (uint32_t i=0; i < options.numHardwareOutputChannels; i++) {
        ExternalAudioBus* bus = new ExternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_audioBuses.insert(bus->id(), bus);
        m_audioOutputChannels.push_back(bus);
    }

    for (uint32_t i=options.numHardwareInputChannels+options.numHardwareOutputChannels; i < m_freeAudioBuses.size(); i++) {
        AudioBus* bus = new InternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_freeAudioBuses.insert(bus->id(), bus);
    }
}

Environment::~Environment()
{
}

AudioBus* Environment::audioBus(const AudioBusId& id)
{
    return m_audioBuses.lookup(id);
}

AudioBus& Environment::externalAudioOutput(size_t index)
{
    return *m_audioOutputChannels[index];
}

AudioBus& Environment::externalAudioInput(size_t index)
{
    return *m_audioInputChannels[index];
}

void Environment::request(MessageQueue::Respond respond, void* data, const LV2_Atom* msg)
{
    m_requests.send(respond, data, msg);
}

void Environment::process(size_t numFrames, sample_t** inputs, sample_t** outputs)
{
    BOOST_ASSERT_MSG( numFrames <= blockSize(), "numFrames exceeds blockSize()" );

    // Process external requests
    processRequests();

    // Process non-realtime commands
    m_worker.perform();

    const size_t numInputs = m_audioInputChannels.size();
    const size_t numOutputs = m_audioOutputChannels.size();

    // Connect input and output buses
    for (size_t i=0; i < numInputs; i++) {
        m_audioInputChannels[i]->setData(inputs[i]);
        m_audioInputChannels[i]->setEpoch(epoch());
    }
    for (size_t i=0; i < numOutputs; i++) {
        m_audioOutputChannels[i]->setData(outputs[i]);
    }

    // Run DSP graph
    m_rootNode->process(numFrames);

    // Zero outputs that haven't been written to
    for (size_t i=0; i < numOutputs; i++) {
        if (m_audioOutputChannels[i]->epoch() != epoch()) {
            memset(outputs[i], 0, numFrames * sizeof(sample_t));
        }
    }

    m_epoch++;
}

static void forgeReturnEnvelope(::LV2::Forge& forge, const Uris& uris, const Environment::MessageQueue::Message& msg)
{
    forge.atom(sizeof(msg), uris.atom_Chunk);
    forge.write(&msg, sizeof(msg));
}

static void forgeError(::LV2::Forge& forge, const Uris& uris, const char* errorMessage)
{
    ::LV2::ObjectFrame frame(forge, 0, uris.patch_Error);
    forge << ::LV2::Property(uris.methcla_errorMessage)
          << errorMessage;
}

static void forgeException(::LV2::Forge& forge, const Uris& uris, const Exception& e)
{
    const std::string* errorInfo = boost::get_error_info<ErrorInfoString>(e);
    const char* errorMessage = errorInfo == nullptr ? "Unknown error" : errorInfo->c_str();
    forgeError(forge, uris, errorMessage);
}

static void sendReply(void* data, const LV2_Atom* payload, Environment::Worker::Writer& /* writer */)
{
    const LV2::Parser& parser = *static_cast<const LV2::Parser*>(data);
    auto tuple = parser.cast<const LV2_Atom_Tuple*>(payload);
    auto iter = lv2_atom_tuple_begin(tuple);
    auto msg = parser.cast<const Environment::MessageQueue::Message*>(iter);
    auto response = lv2_atom_tuple_next(iter);
    msg->respond(response);
}

void Environment::processRequests()
{
    MessageQueue::Message msg;
    while (m_requests.next(msg)) {
        try {
            handleRequest(msg);
        } catch(Exception& e) {
            // Send Error response
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), msg);
                forgeException(frame, uris(), e);
            }
            commit();
        } catch(std::exception& e) {
            // Send Error response
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), msg);
                forgeError(frame, uris(), e.what());
            }
            commit();
        }
    }
}

/*
*/
void Environment::handleRequest(MessageQueue::Message& request)
{
    const LV2_Atom* atom = request.payload();
    std::cout << BOOST_CURRENT_FUNCTION << std::endl;
    m_printer.print(std::cout, atom, 4);
    //cout << "Message: " << atom << endl
         //<< "    atom size: " << atom->size << endl
         //<< "    atom type: " << atom->type << endl
         //<< "    atom uri:  " << unmapUri(atom->type) << endl;
    if (m_parser.isObject(atom))
        handleMessageRequest(request, m_parser.cast<const LV2_Atom_Object*>(atom));
    else if (m_parser.isSequence(atom))
        handleSequenceRequest(request, m_parser.cast<const LV2_Atom_Sequence*>(atom));
    else
        BOOST_THROW_EXCEPTION(Exception() << ErrorInfoString("Invalid request type"));
}

void Environment::handleMessageRequest(MessageQueue::Message& request, const LV2_Atom_Object* msg)
{
    const LV2_Atom* subjectAtom = nullptr;
    const LV2_Atom* bodyAtom = nullptr;
    const LV2_URID requestType = msg->body.otype;

    int matches = lv2_atom_object_get(
                    msg
                  , uris().patch_subject, &subjectAtom
                  , uris().patch_body, &bodyAtom
                  , nullptr );

    if (subjectAtom == nullptr)
        BOOST_THROW_EXCEPTION( Exception() << ErrorInfoString("Message must have subject property") );

    const LV2_Atom_Object* subject = m_parser.cast<const LV2_Atom_Object*>(subjectAtom);
    const LV2_Atom_Object* body = m_parser.cast<const LV2_Atom_Object*>(bodyAtom);

    if (subject->body.otype == uris().methcla_Node) {
        const LV2_Atom* targetAtom = nullptr;
        lv2_atom_object_get(subject, uris().methcla_id, &targetAtom, nullptr);
        BOOST_ASSERT_MSG( targetAtom != nullptr, "methcla:id property not found" );

        NodeId targetId(m_parser.cast<int32_t>(targetAtom));
        Node* targetNode = m_nodes.lookup(targetId);
        BOOST_ASSERT_MSG( targetNode != nullptr, "target node not found" );

        Synth* targetSynth = dynamic_cast<Synth*>(targetNode);
        Group* targetGroup = targetSynth == nullptr ? dynamic_cast<Group*>(targetNode) : targetSynth->parent();

        if (requestType == uris().patch_Insert) {
            // get add target specification

            // get plugin URI
            const LV2_Atom* pluginAtom = nullptr;
            lv2_atom_object_get(body, uris().methcla_plugin, &pluginAtom, nullptr);
            BOOST_ASSERT_MSG( pluginAtom != nullptr, "methcla:plugin property not found" );
            LV2_URID pluginURID = m_parser.cast<LV2_URID>(pluginAtom);

            // get params from body

            // uris().methcla_plugin
            const std::shared_ptr<Plugin> def = plugins().lookup(pluginURID);
            Synth* synth = Synth::construct(*this, targetGroup, Node::kAddToTail, *def);

            // Send reply with synth ID (from NRT thread)
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), request);
                {
                    ::LV2::ObjectFrame frame(forge, 0, uris().patch_Ack);
                    forge << ::LV2::Property(uris().methcla_id)
                          << (int32_t)synth->id();
                }
            }
            commit();
        } else if (requestType == uris().patch_Delete) {

        } else if (requestType == uris().patch_Set) {

        }
    }
}

void Environment::handleSequenceRequest(MessageQueue::Message& request, const LV2_Atom_Sequence* bdl)
{
    std::cerr << "Sequence requests not supported yet\n";
}

Engine::Engine(PluginManager& pluginManager, const boost::filesystem::path& lv2Directory)
{
    m_driver = IO::defaultPlatformDriver();
    m_driver->setProcessCallback(processCallback, this);

    Environment::Options options;
    options.sampleRate = m_driver->sampleRate();
    options.blockSize = m_driver->bufferSize();
    options.numHardwareInputChannels = m_driver->numInputs();
    options.numHardwareOutputChannels = m_driver->numOutputs();
    m_env = new Environment(pluginManager, options);

    pluginManager.loadPlugins(lv2Directory);
}

Engine::~Engine()
{
    stop();
    delete m_env;
    delete m_driver;
}

void Engine::start()
{
    m_driver->start();
}

void Engine::stop()
{
    m_driver->stop();
}

void Engine::processCallback(void* data, size_t numFrames, sample_t** inputs, sample_t** outputs)
{
    static_cast<Engine*>(data)->m_env->process(numFrames, inputs, outputs);
}
<commit_msg>Remove stale code<commit_after>// Copyright 2012-2013 Samplecount S.L.
//
// 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 "Methcla/Audio/Engine.hpp"
#include "Methcla/Audio/Group.hpp"
#include "Methcla/Audio/Synth.hpp"
#include "Methcla/LV2/Atom.hpp"

#include <boost/current_function.hpp>
#include <cstdlib>
#include <iostream>

#include "lv2/lv2plug.in/ns/ext/atom/util.h"

using namespace Methcla;
using namespace Methcla::Audio;
using namespace Methcla::Memory;
using namespace std;

void NodeMap::insert(Node* node)
{
    NodeId id = node->id();
    if (m_nodes[id] != 0)
        BOOST_THROW_EXCEPTION(DuplicateNodeId() << ErrorInfoNodeId(id));
    m_nodes[id] = node;
}

void NodeMap::release(const NodeId& nodeId)
{
    if (m_nodes[nodeId] == 0)
        BOOST_THROW_EXCEPTION(InvalidNodeId() << ErrorInfoNodeId(nodeId));
    m_nodes[nodeId] = 0;
}

Environment::Environment(PluginManager& pluginManager, const Options& options)
    : m_sampleRate(options.sampleRate)
    , m_blockSize(options.blockSize)
    , m_plugins(pluginManager)
    , m_audioBuses    (options.numHardwareInputChannels+options.numHardwareOutputChannels+options.maxNumAudioBuses)
    , m_freeAudioBuses(options.numHardwareInputChannels+options.numHardwareOutputChannels+options.maxNumAudioBuses)
    , m_nodes(options.maxNumNodes)
    , m_rootNode(Group::construct(*this, nullptr, Node::kAddToTail))
    , m_epoch(0)
    , m_worker(uriMap())
    , m_uris(uriMap())
    , m_parser(uriMap().lv2Map())
    , m_printer(uriMap().lv2Map(), uriMap().lv2Unmap())
{
    const Epoch prevEpoch = epoch() - 1;

    m_audioInputChannels.reserve(options.numHardwareInputChannels);
    for (uint32_t i=0; i < options.numHardwareInputChannels; i++) {
        ExternalAudioBus* bus = new ExternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_audioBuses.insert(bus->id(), bus);
        m_audioInputChannels.push_back(bus);
    }

    m_audioOutputChannels.reserve(options.numHardwareOutputChannels);
    for (uint32_t i=0; i < options.numHardwareOutputChannels; i++) {
        ExternalAudioBus* bus = new ExternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_audioBuses.insert(bus->id(), bus);
        m_audioOutputChannels.push_back(bus);
    }

    for (uint32_t i=options.numHardwareInputChannels+options.numHardwareOutputChannels; i < m_freeAudioBuses.size(); i++) {
        AudioBus* bus = new InternalAudioBus(*this, AudioBusId(i), blockSize(), prevEpoch);
        m_freeAudioBuses.insert(bus->id(), bus);
    }
}

Environment::~Environment()
{
}

AudioBus* Environment::audioBus(const AudioBusId& id)
{
    return m_audioBuses.lookup(id);
}

AudioBus& Environment::externalAudioOutput(size_t index)
{
    return *m_audioOutputChannels[index];
}

AudioBus& Environment::externalAudioInput(size_t index)
{
    return *m_audioInputChannels[index];
}

void Environment::request(MessageQueue::Respond respond, void* data, const LV2_Atom* msg)
{
    m_requests.send(respond, data, msg);
}

void Environment::process(size_t numFrames, sample_t** inputs, sample_t** outputs)
{
    BOOST_ASSERT_MSG( numFrames <= blockSize(), "numFrames exceeds blockSize()" );

    // Process external requests
    processRequests();

    // Process non-realtime commands
    m_worker.perform();

    const size_t numInputs = m_audioInputChannels.size();
    const size_t numOutputs = m_audioOutputChannels.size();

    // Connect input and output buses
    for (size_t i=0; i < numInputs; i++) {
        m_audioInputChannels[i]->setData(inputs[i]);
        m_audioInputChannels[i]->setEpoch(epoch());
    }
    for (size_t i=0; i < numOutputs; i++) {
        m_audioOutputChannels[i]->setData(outputs[i]);
    }

    // Run DSP graph
    m_rootNode->process(numFrames);

    // Zero outputs that haven't been written to
    for (size_t i=0; i < numOutputs; i++) {
        if (m_audioOutputChannels[i]->epoch() != epoch()) {
            memset(outputs[i], 0, numFrames * sizeof(sample_t));
        }
    }

    m_epoch++;
}

static void forgeReturnEnvelope(::LV2::Forge& forge, const Uris& uris, const Environment::MessageQueue::Message& msg)
{
    forge.atom(sizeof(msg), uris.atom_Chunk);
    forge.write(&msg, sizeof(msg));
}

static void forgeError(::LV2::Forge& forge, const Uris& uris, const char* errorMessage)
{
    ::LV2::ObjectFrame frame(forge, 0, uris.patch_Error);
    forge << ::LV2::Property(uris.methcla_errorMessage)
          << errorMessage;
}

static void forgeException(::LV2::Forge& forge, const Uris& uris, const Exception& e)
{
    const std::string* errorInfo = boost::get_error_info<ErrorInfoString>(e);
    const char* errorMessage = errorInfo == nullptr ? "Unknown error" : errorInfo->c_str();
    forgeError(forge, uris, errorMessage);
}

static void sendReply(void* data, const LV2_Atom* payload, Environment::Worker::Writer& /* writer */)
{
    const LV2::Parser& parser = *static_cast<const LV2::Parser*>(data);
    auto tuple = parser.cast<const LV2_Atom_Tuple*>(payload);
    auto iter = lv2_atom_tuple_begin(tuple);
    auto msg = parser.cast<const Environment::MessageQueue::Message*>(iter);
    auto response = lv2_atom_tuple_next(iter);
    msg->respond(response);
}

void Environment::processRequests()
{
    MessageQueue::Message msg;
    while (m_requests.next(msg)) {
        try {
            handleRequest(msg);
        } catch(Exception& e) {
            // Send Error response
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), msg);
                forgeException(frame, uris(), e);
            }
            commit();
        } catch(std::exception& e) {
            // Send Error response
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), msg);
                forgeError(frame, uris(), e.what());
            }
            commit();
        }
    }
}

/*
*/
void Environment::handleRequest(MessageQueue::Message& request)
{
    const LV2_Atom* atom = request.payload();

    std::cout << BOOST_CURRENT_FUNCTION << std::endl;
    m_printer.print(std::cout, atom, 4);

    if (m_parser.isObject(atom))
        handleMessageRequest(request, m_parser.cast<const LV2_Atom_Object*>(atom));
    else if (m_parser.isSequence(atom))
        handleSequenceRequest(request, m_parser.cast<const LV2_Atom_Sequence*>(atom));
    else
        BOOST_THROW_EXCEPTION(Exception() << ErrorInfoString("Invalid request type"));
}

void Environment::handleMessageRequest(MessageQueue::Message& request, const LV2_Atom_Object* msg)
{
    const LV2_Atom* subjectAtom = nullptr;
    const LV2_Atom* bodyAtom = nullptr;
    const LV2_URID requestType = msg->body.otype;

    int matches = lv2_atom_object_get(
                    msg
                  , uris().patch_subject, &subjectAtom
                  , uris().patch_body, &bodyAtom
                  , nullptr );

    if (subjectAtom == nullptr)
        BOOST_THROW_EXCEPTION( Exception() << ErrorInfoString("Message must have subject property") );

    const LV2_Atom_Object* subject = m_parser.cast<const LV2_Atom_Object*>(subjectAtom);
    const LV2_Atom_Object* body = m_parser.cast<const LV2_Atom_Object*>(bodyAtom);

    if (subject->body.otype == uris().methcla_Node) {
        const LV2_Atom* targetAtom = nullptr;
        lv2_atom_object_get(subject, uris().methcla_id, &targetAtom, nullptr);
        BOOST_ASSERT_MSG( targetAtom != nullptr, "methcla:id property not found" );

        NodeId targetId(m_parser.cast<int32_t>(targetAtom));
        Node* targetNode = m_nodes.lookup(targetId);
        BOOST_ASSERT_MSG( targetNode != nullptr, "target node not found" );

        Synth* targetSynth = dynamic_cast<Synth*>(targetNode);
        Group* targetGroup = targetSynth == nullptr ? dynamic_cast<Group*>(targetNode) : targetSynth->parent();

        if (requestType == uris().patch_Insert) {
            // get add target specification

            // get plugin URI
            const LV2_Atom* pluginAtom = nullptr;
            lv2_atom_object_get(body, uris().methcla_plugin, &pluginAtom, nullptr);
            BOOST_ASSERT_MSG( pluginAtom != nullptr, "methcla:plugin property not found" );
            LV2_URID pluginURID = m_parser.cast<LV2_URID>(pluginAtom);

            // get params from body

            // uris().methcla_plugin
            const std::shared_ptr<Plugin> def = plugins().lookup(pluginURID);
            Synth* synth = Synth::construct(*this, targetGroup, Node::kAddToTail, *def);

            // Send reply with synth ID (from NRT thread)
            ::LV2::Forge forge(*prepare(sendReply, &m_parser));
            {
                ::LV2::TupleFrame frame(forge);
                forgeReturnEnvelope(frame, uris(), request);
                {
                    ::LV2::ObjectFrame frame(forge, 0, uris().patch_Ack);
                    forge << ::LV2::Property(uris().methcla_id)
                          << (int32_t)synth->id();
                }
            }
            commit();
        } else if (requestType == uris().patch_Delete) {

        } else if (requestType == uris().patch_Set) {

        }
    }
}

void Environment::handleSequenceRequest(MessageQueue::Message& request, const LV2_Atom_Sequence* bdl)
{
    std::cerr << "Sequence requests not supported yet\n";
}

Engine::Engine(PluginManager& pluginManager, const boost::filesystem::path& lv2Directory)
{
    m_driver = IO::defaultPlatformDriver();
    m_driver->setProcessCallback(processCallback, this);

    Environment::Options options;
    options.sampleRate = m_driver->sampleRate();
    options.blockSize = m_driver->bufferSize();
    options.numHardwareInputChannels = m_driver->numInputs();
    options.numHardwareOutputChannels = m_driver->numOutputs();
    m_env = new Environment(pluginManager, options);

    pluginManager.loadPlugins(lv2Directory);
}

Engine::~Engine()
{
    stop();
    delete m_env;
    delete m_driver;
}

void Engine::start()
{
    m_driver->start();
}

void Engine::stop()
{
    m_driver->stop();
}

void Engine::processCallback(void* data, size_t numFrames, sample_t** inputs, sample_t** outputs)
{
    static_cast<Engine*>(data)->m_env->process(numFrames, inputs, outputs);
}
<|endoftext|>