<commit_before>// Copyright 2013 Google Inc. All Rights Reserved.
/**
 * @fileoverview NaCl loader for libchewing.
 * @author hungte@google.com (Hung-Te Lin)
 *
 * Accept messages:
 *  key:<KEY NAME>
 *
 * Output messages:
 *  debug:<DEBUG MESSAGE>
 *  context:<IM context>
 */

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

#include <pthread.h>  // for nacl_io
#include <stdlib.h>
#include <unistd.h>

#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "nacl_io/nacl_io.h"
#include "json/writer.h"

#include "chewing.h"
#include "chewing-private.h"

using std::string;

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))

extern "C" size_t getpagesize() {
  return sysconf(_SC_PAGESIZE);
}

////////////////////////////////////////////////////////////////////////
// Module Initialization.
////////////////////////////////////////////////////////////////////////

typedef struct {
  const char *keyname;
  int (*handler)(ChewingContext *ctx);
} ChewingKeyMapping;

ChewingKeyMapping special_key_mappings[] = {
  { "Backspace", chewing_handle_Backspace, },
  { "Tab", chewing_handle_Tab, },
  { "Enter", chewing_handle_Enter, },
  { "Shift", chewing_handle_ShiftLeft, },
  { "CapsLock", chewing_handle_Capslock, },
  { "Esc", chewing_handle_Esc, },
  { " ", chewing_handle_Space, },
  { "PageUp", chewing_handle_PageUp, },
  { "PageDown", chewing_handle_PageDown, },
  { "End", chewing_handle_End, },
  { "Home", chewing_handle_Home, },
  { "Left", chewing_handle_Left, },
  { "Up", chewing_handle_Up, },
  { "Right", chewing_handle_Right, },
  { "Down", chewing_handle_Down, },
  { "Delete", chewing_handle_Del, },
};

void *chewing_init_context(void *arg);

class ChewingInstance: public pp::Instance {
 protected:
  const string kMsgDebugPrefix;
  const string kMsgContextPrefix;
  const string kMsgKeyPrefix;

 public:
  ChewingContext *ctx;

  explicit ChewingInstance(PP_Instance instance): pp::Instance(instance),
      kMsgDebugPrefix("debug:"), kMsgContextPrefix("context:"),
      kMsgKeyPrefix("key:"), ctx(NULL) {

    const char *data_dir = "/data", *user_data_dir = "/user_data";
    nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());
    if (mount("libchewing/data", data_dir, "httpfs", 0, "") != 0) {
      PostMessage(pp::Var("can't mount data"));
      return;
    }
    // TODO(hungte) change memfs to html5fs.
    if (mount("", user_data_dir, "html5fs", 0,
              "type=PERSISTENT,expected_size=1048576") != 0) {
      PostMessage(pp::Var("can't mount user data"));
      return;
    }
    // Note chewing library does not really take path on its Init...
    // So we always need to do putenv.
    char chewing_path[] = "CHEWING_PATH=/data";
    char chewing_user_path[] = "CHEWING_USER_PATH=/user_data";
    putenv(chewing_path);
    putenv(chewing_user_path);
    chewing_Init(data_dir, ".");
    pthread_t main_thread;
    pthread_create(&main_thread, NULL, chewing_init_context, (void*)this);
  }

  virtual ~ChewingInstance() {
    if (ctx)
      chewing_delete(ctx);
    chewing_Terminate();
  }

  virtual void Debug(const string &message, const string &detail="") {
    PostMessage(pp::Var(kMsgDebugPrefix + message + (
        detail.empty() ? ", " : "") + detail));
  }

  virtual void ReturnContext() {
    char *s;
    Json::FastWriter writer;
    Json::Value value(Json::objectValue);

    // TODO(hungte) Probably just access context internal buffer so we don't
    // need to waste time doing calloc/free... reading ChewingOutput directly.

    // chewing_cand_CheckDone does not do what we expect...
    if (chewing_cand_TotalChoice(ctx) > 0) {
      chewing_cand_Enumerate(ctx);
      Json::Value cand = Json::Value(Json::arrayValue);
      int i, len = chewing_cand_ChoicePerPage(ctx);
      for (i = 0; i < len && chewing_cand_hasNext(ctx); i++) {
        s = chewing_cand_String(ctx);
        cand.append(Json::Value(s));
        chewing_free(s);
      }
      value["cand"] = cand;
      value["cand_ChoicePerPage"] = Json::Value(len);
      value["cand_TotalPage"] = Json::Value(chewing_cand_TotalPage(ctx));
      value["cand_CurrentPage"] = Json::Value(chewing_cand_CurrentPage(ctx));
    }

    if (chewing_buffer_Check(ctx)) {
      s = chewing_buffer_String(ctx);
      value["buffer"] = Json::Value(s);
      chewing_free(s);
    }

    {
      IntervalType it;
      Json::Value intervals = Json::Value(Json::arrayValue);
      Json::Value lcch = Json::Value(Json::arrayValue);
      chewing_interval_Enumerate(ctx);
      while (chewing_interval_hasNext(ctx)) {
        chewing_interval_Get(ctx, &it);
        Json::Value itv = Json::Value(Json::objectValue);
        itv["from"] = Json::Value(it.from);
        itv["to"] = Json::Value(it.to);
        char *text = (char*)calloc(it.to - it.from + 1, MAX_UTF8_SIZE);
        text[0] = 0;
        for (int i = it.from; i < it.to; i++) {
          strcat(text, (char *)ctx->output->chiSymbolBuf[i].s);
        }
        itv["text"] = Json::Value(text);
        lcch.append(Json::Value(text));
        free(text);
        intervals.append(itv);
      }
      if (intervals.size() > 0) {
        value["interval"] = intervals;
        value["lcch"] = lcch;
      }
    }

    if (chewing_bopomofo_Check(ctx)) {
      s = chewing_bopomofo_String(ctx);
      value["bopomofo"] = Json::Value(s);
      chewing_free(s);
    }
    if (chewing_aux_Check(ctx)) {
      s = chewing_aux_String(ctx);
      value["aux"] = Json::Value(s);
      chewing_free(s);
    }
    if (chewing_commit_Check(ctx)) {
      s = chewing_commit_String(ctx);
      value["commit"] = Json::Value(s);
      chewing_free(s);
    }
    value["cursor"] = Json::Value(chewing_cursor_Current(ctx));
    if (chewing_keystroke_CheckIgnore(ctx))
      value["ignore"] = Json::Value(true);
    if (chewing_keystroke_CheckAbsorb(ctx))
      value["absorb"] = Json::Value(true);

    // XCIN compatible fields
    value["keystroke"] = value["bopomofo"];
    value["mcch"] = value["cand"];
    value["cch"] = value["commit"];
    value["edit_pos"] = value["cursor"];
    // lcch should be already handled when building interval.

    PostMessage(pp::Var(kMsgContextPrefix + writer.write(value)));
  }

  virtual void HandleMessage(const pp::Var &var_message) {
    // Due to current PPAPI limitation, we need to serialize anything to simple
    // string before sending to native client.
    if (!ctx || !var_message.is_string())
      return;

    // Check message type.
    string msg(var_message.AsString());
    if (msg.find_first_of(kMsgKeyPrefix) != 0)
      return;
    msg = msg.substr(kMsgKeyPrefix.size());

    bool handled = false;
    for (int i = 0; i < ARRAYSIZE(special_key_mappings); i++) {
      ChewingKeyMapping *map = &special_key_mappings[i];
      if (msg == map->keyname) {
        map->handler(ctx);
        handled = true;
        break;
      }
    }
    // Some special keys, like Ctrl, should not be mis-handled.
    if (!handled && msg.size() == 1) {
        chewing_handle_Default(ctx, msg[0]);
    }
    ReturnContext();
  }
};

void *chewing_init_context(void *arg) {
  int selkeys[MAX_SELKEY] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};

  ChewingInstance *instance = (ChewingInstance*)arg;
  /* chewing_new will do fopen/fread so we must call it inside a dedicated
   * thread. */
  ChewingContext *ctx = chewing_new();

  // Set keyboard type
  chewing_set_KBType(ctx, chewing_KBStr2Num((char*)"KB_DEFAULT"));

  chewing_set_maxChiSymbolLen(ctx, 16);
  chewing_set_addPhraseDirection(ctx, 1);
  chewing_set_spaceAsSelection(ctx, 1);
  // chewing_set_selKey does not really take the len arg and takes a hard-coded
  // value for memcpy size. How amazing!
  int nSelKeys = ARRAYSIZE(selkeys);
  assert(nSelKeys >= MAX_SELKEY);
  chewing_set_selKey(ctx, selkeys, nSelKeys);
  chewing_set_candPerPage(ctx, std::min(nSelKeys, MAX_SELKEY));
  instance->ctx = ctx;
  return NULL;
}

class ChewingModule: public pp::Module {
 public:
  ChewingModule(): pp::Module() {}
  virtual ~ChewingModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new ChewingInstance(instance);
  }
};

namespace pp {
  Module* CreateModule() {
    return new ChewingModule();
  }
}  // namespace pp
<commit_msg>nacl/libchewing: Return complete buffer.<commit_after>// Copyright 2013 Google Inc. All Rights Reserved.
/**
 * @fileoverview NaCl loader for libchewing.
 * @author hungte@google.com (Hung-Te Lin)
 *
 * Accept messages:
 *  key:<KEY NAME>
 *
 * Output messages:
 *  debug:<DEBUG MESSAGE>
 *  context:<IM context>
 */

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

#include <pthread.h>  // for nacl_io
#include <stdlib.h>
#include <unistd.h>

#include "ppapi/cpp/instance.h"
#include "ppapi/cpp/module.h"
#include "ppapi/cpp/var.h"
#include "nacl_io/nacl_io.h"
#include "json/writer.h"

#include "chewing.h"
#include "chewing-private.h"

using std::string;

#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))

extern "C" size_t getpagesize() {
  return sysconf(_SC_PAGESIZE);
}

////////////////////////////////////////////////////////////////////////
// Module Initialization.
////////////////////////////////////////////////////////////////////////

typedef struct {
  const char *keyname;
  int (*handler)(ChewingContext *ctx);
} ChewingKeyMapping;

ChewingKeyMapping special_key_mappings[] = {
  { "Backspace", chewing_handle_Backspace, },
  { "Tab", chewing_handle_Tab, },
  { "Enter", chewing_handle_Enter, },
  { "Shift", chewing_handle_ShiftLeft, },
  { "CapsLock", chewing_handle_Capslock, },
  { "Esc", chewing_handle_Esc, },
  { " ", chewing_handle_Space, },
  { "PageUp", chewing_handle_PageUp, },
  { "PageDown", chewing_handle_PageDown, },
  { "End", chewing_handle_End, },
  { "Home", chewing_handle_Home, },
  { "Left", chewing_handle_Left, },
  { "Up", chewing_handle_Up, },
  { "Right", chewing_handle_Right, },
  { "Down", chewing_handle_Down, },
  { "Delete", chewing_handle_Del, },
};

void *chewing_init_context(void *arg);

class ChewingInstance: public pp::Instance {
 protected:
  const string kMsgDebugPrefix;
  const string kMsgContextPrefix;
  const string kMsgKeyPrefix;

 public:
  ChewingContext *ctx;

  explicit ChewingInstance(PP_Instance instance): pp::Instance(instance),
      kMsgDebugPrefix("debug:"), kMsgContextPrefix("context:"),
      kMsgKeyPrefix("key:"), ctx(NULL) {

    const char *data_dir = "/data", *user_data_dir = "/user_data";
    nacl_io_init_ppapi(instance, pp::Module::Get()->get_browser_interface());
    if (mount("libchewing/data", data_dir, "httpfs", 0, "") != 0) {
      PostMessage(pp::Var("can't mount data"));
      return;
    }
    // TODO(hungte) change memfs to html5fs.
    if (mount("", user_data_dir, "html5fs", 0,
              "type=PERSISTENT,expected_size=1048576") != 0) {
      PostMessage(pp::Var("can't mount user data"));
      return;
    }
    // Note chewing library does not really take path on its Init...
    // So we always need to do putenv.
    char chewing_path[] = "CHEWING_PATH=/data";
    char chewing_user_path[] = "CHEWING_USER_PATH=/user_data";
    putenv(chewing_path);
    putenv(chewing_user_path);
    chewing_Init(data_dir, ".");
    pthread_t main_thread;
    pthread_create(&main_thread, NULL, chewing_init_context, (void*)this);
  }

  virtual ~ChewingInstance() {
    if (ctx)
      chewing_delete(ctx);
    chewing_Terminate();
  }

  virtual void Debug(const string &message, const string &detail="") {
    PostMessage(pp::Var(kMsgDebugPrefix + message + (
        detail.empty() ? ", " : "") + detail));
  }

  virtual void AppendChewingBuffer(Json::Value &array, int from, int to) {
    char *text = (char*)calloc(to - from + 1, MAX_UTF8_SIZE);
    for (int i = from; i < to; i++) {
      strcat(text, (char *)ctx->output->chiSymbolBuf[i].s);
    }
    array.append(Json::Value(text));
    free(text);
  }

  virtual void ReturnContext() {
    char *s;
    Json::FastWriter writer;
    Json::Value value(Json::objectValue);

    // TODO(hungte) Probably just access context internal buffer so we don't
    // need to waste time doing calloc/free... reading ChewingOutput directly.

    // chewing_cand_CheckDone does not do what we expect...
    if (chewing_cand_TotalChoice(ctx) > 0) {
      chewing_cand_Enumerate(ctx);
      Json::Value cand = Json::Value(Json::arrayValue);
      int i, len = chewing_cand_ChoicePerPage(ctx);
      for (i = 0; i < len && chewing_cand_hasNext(ctx); i++) {
        s = chewing_cand_String(ctx);
        cand.append(Json::Value(s));
        chewing_free(s);
      }
      value["cand"] = cand;
      value["cand_ChoicePerPage"] = Json::Value(len);
      value["cand_TotalPage"] = Json::Value(chewing_cand_TotalPage(ctx));
      value["cand_CurrentPage"] = Json::Value(chewing_cand_CurrentPage(ctx));
    }

    if (chewing_buffer_Check(ctx)) {
      s = chewing_buffer_String(ctx);
      value["buffer"] = Json::Value(s);
      chewing_free(s);
    }

    {
      IntervalType it;
      Json::Value intervals = Json::Value(Json::arrayValue);
      Json::Value lcch = Json::Value(Json::arrayValue);
      chewing_interval_Enumerate(ctx);
      int start = 0, end = chewing_buffer_Len(ctx);
      // TODO(hungte) It is possible to have groups without buffer.
      // i.e., lcch>0, intervals=0
      while (chewing_interval_hasNext(ctx)) {
        chewing_interval_Get(ctx, &it);
        Json::Value itv = Json::Value(Json::objectValue);
        itv["from"] = Json::Value(it.from);
        itv["to"] = Json::Value(it.to);
        intervals.append(itv);

        if (start != it.from)
          AppendChewingBuffer(lcch, start, it.from);
        AppendChewingBuffer(lcch, it.from, it.to);
        start = it.to;
      }
      if (start < end)
        AppendChewingBuffer(lcch, start, end);
      if (intervals.size() > 0)
        value["interval"] = intervals;
      if (lcch.size() > 0)
        value["lcch"] = lcch;
    }

    if (chewing_bopomofo_Check(ctx)) {
      s = chewing_bopomofo_String(ctx);
      value["bopomofo"] = Json::Value(s);
      chewing_free(s);
    }
    if (chewing_aux_Check(ctx)) {
      s = chewing_aux_String(ctx);
      value["aux"] = Json::Value(s);
      chewing_free(s);
    }
    if (chewing_commit_Check(ctx)) {
      s = chewing_commit_String(ctx);
      value["commit"] = Json::Value(s);
      chewing_free(s);
    }
    value["cursor"] = Json::Value(chewing_cursor_Current(ctx));
    if (chewing_keystroke_CheckIgnore(ctx))
      value["ignore"] = Json::Value(true);
    if (chewing_keystroke_CheckAbsorb(ctx))
      value["absorb"] = Json::Value(true);

    // XCIN compatible fields
    value["keystroke"] = value["bopomofo"];
    value["mcch"] = value["cand"];
    value["cch"] = value["commit"];
    value["edit_pos"] = value["cursor"];
    // lcch should be already handled when building interval.

    PostMessage(pp::Var(kMsgContextPrefix + writer.write(value)));
  }

  virtual void HandleMessage(const pp::Var &var_message) {
    // Due to current PPAPI limitation, we need to serialize anything to simple
    // string before sending to native client.
    if (!ctx || !var_message.is_string())
      return;

    // Check message type.
    string msg(var_message.AsString());
    if (msg.find_first_of(kMsgKeyPrefix) != 0)
      return;
    msg = msg.substr(kMsgKeyPrefix.size());

    bool handled = false;
    for (int i = 0; i < ARRAYSIZE(special_key_mappings); i++) {
      ChewingKeyMapping *map = &special_key_mappings[i];
      if (msg == map->keyname) {
        map->handler(ctx);
        handled = true;
        break;
      }
    }
    // Some special keys, like Ctrl, should not be mis-handled.
    if (!handled && msg.size() == 1) {
        chewing_handle_Default(ctx, msg[0]);
    }
    ReturnContext();
  }
};

void *chewing_init_context(void *arg) {
  int selkeys[MAX_SELKEY] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};

  ChewingInstance *instance = (ChewingInstance*)arg;
  /* chewing_new will do fopen/fread so we must call it inside a dedicated
   * thread. */
  ChewingContext *ctx = chewing_new();

  // Set keyboard type
  chewing_set_KBType(ctx, chewing_KBStr2Num((char*)"KB_DEFAULT"));

  chewing_set_maxChiSymbolLen(ctx, 16);
  chewing_set_addPhraseDirection(ctx, 1);
  chewing_set_spaceAsSelection(ctx, 1);
  // chewing_set_selKey does not really take the len arg and takes a hard-coded
  // value for memcpy size. How amazing!
  int nSelKeys = ARRAYSIZE(selkeys);
  assert(nSelKeys >= MAX_SELKEY);
  chewing_set_selKey(ctx, selkeys, nSelKeys);
  chewing_set_candPerPage(ctx, std::min(nSelKeys, MAX_SELKEY));
  instance->ctx = ctx;
  return NULL;
}

class ChewingModule: public pp::Module {
 public:
  ChewingModule(): pp::Module() {}
  virtual ~ChewingModule() {}

  virtual pp::Instance* CreateInstance(PP_Instance instance) {
    return new ChewingInstance(instance);
  }
};

namespace pp {
  Module* CreateModule() {
    return new ChewingModule();
  }
}  // namespace pp
<|endoftext|>


<commit_before>#include "zp_data_command.h"

#include <google/protobuf/text_format.h>
#include <glog/logging.h>
#include "include/db_nemo_impl.h"
#include "zp_data_server.h"

#include "slash_string.h"

extern ZPDataServer *zp_data_server;

void SetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  // User raw pointer instead of shared_ptr, since it is surely be existed during this caller
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  response->set_type(client::Type::SET);

  rocksdb::Status s;
  if (request->set().has_expire()) {
    int base = 0, ttl = request->set().expire().ttl();
    if (request->set().expire().has_base()) {
      // Come from sync conn
      base = request->set().expire().base();
      ttl -= (time(NULL) - base);
      if (ttl <= 0) {
        // Already expire
        DLOG(INFO) << "Set key(" << request->set().key() << ") at "
          << ptr->table_name() << "_"
          << ptr->partition_id() << " already expired";
        response->set_code(client::StatusCode::kOk);
        return;
      }
    }
    s = ptr->db()->Put(rocksdb::WriteOptions(),
        request->set().key(),
        request->set().value(),
        ttl);
  } else {
    s = ptr->db()->Put(rocksdb::WriteOptions(),
        request->set().key(),
        request->set().value());
  }

  if (!s.ok()) {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Set, caz " << s.ToString();
  } else {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "Set key(" << request->set().key() << ") at "
      << ptr->table_name() << "_" << ptr->partition_id() << " ok";
  }
}

bool SetCmd::GenerateLog(const google::protobuf::Message *req,
    std::string* log_raw) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  if (request->set().has_expire()) {
    client::CmdRequest log_req(*request);
    log_req.mutable_set()->mutable_expire()->set_base(time(NULL));
    return log_req.SerializeToString(log_raw);
  }
  return request->SerializeToString(log_raw);
}

void GetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  client::CmdResponse_Get* get_res = response->mutable_get();
  response->set_type(client::Type::GET);

  std::string value;
  rocksdb::Status s = ptr->db()->Get(rocksdb::ReadOptions(),
      request->get().key(),
      &value);
  if (s.ok()) {
    response->set_code(client::StatusCode::kOk);
    get_res->set_value(value);
    DLOG(INFO) << "Get key(" << request->get().key()
      << ") at " << ptr->table_name() << "_" << ptr->partition_id()
      << " ok, value is (" << value << ")";
  } else if (s.IsNotFound()) {
    response->set_code(client::StatusCode::kNotFound);
    DLOG(INFO) << "Get key(" << request->get().key() <<
      ") at " << ptr->table_name() << "_" << ptr->partition_id() << " not found!";
  } else {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Get at "
      << ptr->table_name() << "_"  << ptr->partition_id()
      << ", caz " << s.ToString();
  }
}

void DelCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  response->set_type(client::Type::DEL);

  rocksdb::Status s = ptr->db()->Delete(rocksdb::WriteOptions(),
      request->del().key());
  if (!s.ok()) {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Del, caz " << s.ToString();
  } else {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "Del key(" << request->del().key()
      << ") at Partition: " << ptr->partition_id() << " ok";
  }
}

void MgetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* ptr) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  response->Clear();
  response->set_type(client::Type::MGET);

  // One error all error
  Cmd* sub_cmd = zp_data_server->CmdGet(client::Type::GET);
  client::CmdRequest sub_req;
  sub_req.set_type(client::Type::GET);
  client::CmdResponse sub_res;
  sub_res.set_type(client::Type::GET);
  for (auto& key : request->mget().keys()) {
    std::shared_ptr<Partition> partition = zp_data_server->GetTablePartition(
        request->mget().table_name(), key);
    if (partition == NULL) {
      LOG(WARNING) << "command failed: Mget, no partition for key:" << key;
      response->set_code(client::StatusCode::kError);
      response->set_msg("no partition" + key);
      return;
    }

    // convert to multi sub command, then execute
    sub_req.Clear();
    sub_res.Clear();
    client::CmdRequest_Get* get = sub_req.mutable_get();
    get->set_table_name(request->mget().table_name());
    get->set_key(key);
    partition->DoCommand(sub_cmd, sub_req, sub_res);
    if (sub_res.code() != client::StatusCode::kOk) {
      LOG(WARNING) << "command failed: Mget, key:" << key << ", error:" << sub_res.msg();
      response->set_code(sub_res.code());
      response->set_msg(sub_res.msg());
      return;
    }
    client::CmdResponse_Mget* mget = response->add_mget();
    mget->set_key(key);
    mget->set_value(sub_res.get().value());
  }
  response->set_code(client::StatusCode::kOk);
}

void InfoCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* p) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);

  response->Clear();
  std::string table_name;
  if (request->has_info() && request->info().has_table_name()) {
    table_name = request->info().table_name();
  }

  switch (request->type()) {
    case client::Type::INFOSTATS: {
      response->set_type(client::Type::INFOSTATS);

      std::vector<Statistic> stats;
      zp_data_server->GetTableStat(table_name, stats);
      DLOG(INFO) << "InfoStat with " << stats.size() << " tables total";

      for (auto it = stats.begin(); it != stats.end(); it++) {
        // TODO anan debug;
        //DLOG(INFO) << "InfoStats ---->";
        //it->Dump();
        client::CmdResponse_InfoStats* info_stat = response->add_info_stats();
        info_stat->set_table_name(it->table_name);
        info_stat->set_total_querys(it->querys);
        info_stat->set_qps(it->last_qps);
      }
      break;
    }
    case client::Type::INFOCAPACITY: {
      response->set_type(client::Type::INFOCAPACITY);
      std::vector<Statistic> stats;
      zp_data_server->GetTableCapacity(table_name, stats);
      DLOG(INFO) << "InfoCapacity with " << stats.size() << " tables total";

      for (auto it = stats.begin(); it != stats.end(); it++) {
        // TODO anan debug;
        //DLOG(INFO) << "InfoCapacity ---->";
        //it->Dump();
        client::CmdResponse_InfoCapacity* info_cpct = response->add_info_capacity();
        info_cpct->set_table_name(it->table_name);
        info_cpct->set_used(it->used_disk);
        info_cpct->set_remain(it->free_disk);
      }
      break;
    }
    case client::Type::INFOREPL: {
      response->set_type(client::Type::INFOREPL);
      std::unordered_map<std::string, client::CmdResponse_InfoRepl> info_repls;
      if (!zp_data_server->GetTableReplInfo(table_name, &info_repls)) {
        response->set_code(client::StatusCode::kError);
        response->set_msg("unknown table name");
        LOG(WARNING) << "Failed to GetTableReplInfo: " << table_name;
        return;
      }

      for (auto& info_repl : info_repls) {
        response->add_info_repl()->CopyFrom(info_repl.second);
      }
      break;
    }
    case client::Type::INFOSERVER: {
      response->set_type(client::Type::INFOSERVER);
      client::CmdResponse_InfoServer info_server;
      if (!zp_data_server->GetServerInfo(&info_server)) {
        LOG(WARNING) << "Failed to GetTableReplInfo: " << table_name;
      }
      response->mutable_info_server()->CopyFrom(info_server);
      break;
    }
    default: {
      response->set_code(client::StatusCode::kError);
      response->set_msg("unsupported cmd type");
      LOG(ERROR) << "unsupported cmd type" << static_cast<int>(request->type());
      return;
    }
  }

  response->set_code(client::StatusCode::kOk);
}

// Sync between nodes
void SyncCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  client::CmdRequest_Sync sync_req = request->sync();

  slash::Status s;
  Node node(sync_req.node().ip(), sync_req.node().port());
  response->set_type(client::Type::SYNC);

  uint32_t s_filenum = sync_req.sync_offset().filenum();
  uint64_t s_offset = sync_req.sync_offset().offset();
  LOG(INFO) << "SyncCmd with a new node ("
    << ptr->table_name() << "_"  << ptr->partition_id()
    << "_" << node.ip << ":" << node.port << ", "
    << s_filenum << ", " << s_offset << ")";
  s = ptr->SlaveAskSync(node, s_filenum, s_offset);

  if (s.ok()) {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "SyncCmd add node ok";
  } else if (s.IsEndFile() || s.IsInvalidArgument()) {
    // Need send fallback offset
    response->set_code(client::StatusCode::kFallback);
    client::CmdResponse_Sync *sync_res = response->mutable_sync();
    sync_res->set_table_name(sync_req.table_name());
    client::SyncOffset *offset = sync_res->mutable_sync_offset();
    if (s.IsEndFile()) {
      // Peer's offset is larger than me, send fallback offset
      ptr->GetWinBinlogOffset(&s_filenum, &s_offset);
      DLOG(INFO) << "SyncCmd with offset larger than me, node:"
        << sync_req.node().ip() << ":" << sync_req.node().port();
    } else {
      // Invalid filenum an offset, sen fallback offset
      DLOG(INFO) << "SyncCmd with offset invalid, node:"
        << sync_req.node().ip() << ":" << sync_req.node().port();
    }
    offset->set_filenum(s_filenum);
    s_offset = BinlogBlockStart(s_offset);
    offset->set_offset(s_offset);
    DLOG(INFO) << "Send back fallback binlog point: "
      << s_filenum << ", " << s_offset << " To: "
      << sync_req.node().ip() << ":" << sync_req.node().port();
  } else if (s.IsIncomplete()) {
    // Slave should wait for db sync
    response->set_code(client::StatusCode::kWait);
    DLOG(INFO) << "SyncCmd add node incomplete";
  } else {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Sync, caz " << s.ToString();
  }
}
<commit_msg>mget set value empty when not found<commit_after>#include "zp_data_command.h"

#include <google/protobuf/text_format.h>
#include <glog/logging.h>
#include "include/db_nemo_impl.h"
#include "zp_data_server.h"

#include "slash_string.h"

extern ZPDataServer *zp_data_server;

void SetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  // User raw pointer instead of shared_ptr, since it is surely be existed during this caller
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  response->set_type(client::Type::SET);

  rocksdb::Status s;
  if (request->set().has_expire()) {
    int base = 0, ttl = request->set().expire().ttl();
    if (request->set().expire().has_base()) {
      // Come from sync conn
      base = request->set().expire().base();
      ttl -= (time(NULL) - base);
      if (ttl <= 0) {
        // Already expire
        DLOG(INFO) << "Set key(" << request->set().key() << ") at "
          << ptr->table_name() << "_"
          << ptr->partition_id() << " already expired";
        response->set_code(client::StatusCode::kOk);
        return;
      }
    }
    s = ptr->db()->Put(rocksdb::WriteOptions(),
        request->set().key(),
        request->set().value(),
        ttl);
  } else {
    s = ptr->db()->Put(rocksdb::WriteOptions(),
        request->set().key(),
        request->set().value());
  }

  if (!s.ok()) {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Set, caz " << s.ToString();
  } else {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "Set key(" << request->set().key() << ") at "
      << ptr->table_name() << "_" << ptr->partition_id() << " ok";
  }
}

bool SetCmd::GenerateLog(const google::protobuf::Message *req,
    std::string* log_raw) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  if (request->set().has_expire()) {
    client::CmdRequest log_req(*request);
    log_req.mutable_set()->mutable_expire()->set_base(time(NULL));
    return log_req.SerializeToString(log_raw);
  }
  return request->SerializeToString(log_raw);
}

void GetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  client::CmdResponse_Get* get_res = response->mutable_get();
  response->set_type(client::Type::GET);

  std::string value;
  rocksdb::Status s = ptr->db()->Get(rocksdb::ReadOptions(),
      request->get().key(),
      &value);
  if (s.ok()) {
    response->set_code(client::StatusCode::kOk);
    get_res->set_value(value);
    DLOG(INFO) << "Get key(" << request->get().key()
      << ") at " << ptr->table_name() << "_" << ptr->partition_id()
      << " ok, value is (" << value << ")";
  } else if (s.IsNotFound()) {
    response->set_code(client::StatusCode::kNotFound);
    DLOG(INFO) << "Get key(" << request->get().key() <<
      ") at " << ptr->table_name() << "_" << ptr->partition_id() << " not found!";
  } else {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Get at "
      << ptr->table_name() << "_"  << ptr->partition_id()
      << ", caz " << s.ToString();
  }
}

void DelCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  response->set_type(client::Type::DEL);

  rocksdb::Status s = ptr->db()->Delete(rocksdb::WriteOptions(),
      request->del().key());
  if (!s.ok()) {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Del, caz " << s.ToString();
  } else {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "Del key(" << request->del().key()
      << ") at Partition: " << ptr->partition_id() << " ok";
  }
}

void MgetCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* ptr) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  response->Clear();
  response->set_type(client::Type::MGET);

  // One error all error
  Cmd* sub_cmd = zp_data_server->CmdGet(client::Type::GET);
  client::CmdRequest sub_req;
  sub_req.set_type(client::Type::GET);
  client::CmdResponse sub_res;
  sub_res.set_type(client::Type::GET);
  for (auto& key : request->mget().keys()) {
    std::shared_ptr<Partition> partition = zp_data_server->GetTablePartition(
        request->mget().table_name(), key);
    if (partition == NULL) {
      LOG(WARNING) << "command failed: Mget, no partition for key:" << key;
      response->set_code(client::StatusCode::kError);
      response->set_msg("no partition" + key);
      return;
    }

    // convert to multi sub command, then execute
    sub_req.Clear();
    sub_res.Clear();
    client::CmdRequest_Get* get = sub_req.mutable_get();
    get->set_table_name(request->mget().table_name());
    get->set_key(key);
    partition->DoCommand(sub_cmd, sub_req, sub_res);
    if (sub_res.code() != client::StatusCode::kOk
        && sub_res.code() != client::StatusCode::kNotFound) {
      LOG(WARNING) << "command failed: Mget, key:" << key << ", error:" << sub_res.msg();
      response->set_code(sub_res.code());
      response->set_msg(sub_res.msg());
      return;
    }
    client::CmdResponse_Mget* mget = response->add_mget();
    mget->set_key(key);
    if (sub_res.code() == client::StatusCode::kOk) {
      mget->set_value(sub_res.get().value());
    } else {
      mget->set_value("");
    }
  }
  response->set_code(client::StatusCode::kOk);
}

void InfoCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* p) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);

  response->Clear();
  std::string table_name;
  if (request->has_info() && request->info().has_table_name()) {
    table_name = request->info().table_name();
  }

  switch (request->type()) {
    case client::Type::INFOSTATS: {
      response->set_type(client::Type::INFOSTATS);

      std::vector<Statistic> stats;
      zp_data_server->GetTableStat(table_name, stats);
      DLOG(INFO) << "InfoStat with " << stats.size() << " tables total";

      for (auto it = stats.begin(); it != stats.end(); it++) {
        // TODO anan debug;
        //DLOG(INFO) << "InfoStats ---->";
        //it->Dump();
        client::CmdResponse_InfoStats* info_stat = response->add_info_stats();
        info_stat->set_table_name(it->table_name);
        info_stat->set_total_querys(it->querys);
        info_stat->set_qps(it->last_qps);
      }
      break;
    }
    case client::Type::INFOCAPACITY: {
      response->set_type(client::Type::INFOCAPACITY);
      std::vector<Statistic> stats;
      zp_data_server->GetTableCapacity(table_name, stats);
      DLOG(INFO) << "InfoCapacity with " << stats.size() << " tables total";

      for (auto it = stats.begin(); it != stats.end(); it++) {
        // TODO anan debug;
        //DLOG(INFO) << "InfoCapacity ---->";
        //it->Dump();
        client::CmdResponse_InfoCapacity* info_cpct = response->add_info_capacity();
        info_cpct->set_table_name(it->table_name);
        info_cpct->set_used(it->used_disk);
        info_cpct->set_remain(it->free_disk);
      }
      break;
    }
    case client::Type::INFOREPL: {
      response->set_type(client::Type::INFOREPL);
      std::unordered_map<std::string, client::CmdResponse_InfoRepl> info_repls;
      if (!zp_data_server->GetTableReplInfo(table_name, &info_repls)) {
        response->set_code(client::StatusCode::kError);
        response->set_msg("unknown table name");
        LOG(WARNING) << "Failed to GetTableReplInfo: " << table_name;
        return;
      }

      for (auto& info_repl : info_repls) {
        response->add_info_repl()->CopyFrom(info_repl.second);
      }
      break;
    }
    case client::Type::INFOSERVER: {
      response->set_type(client::Type::INFOSERVER);
      client::CmdResponse_InfoServer info_server;
      if (!zp_data_server->GetServerInfo(&info_server)) {
        LOG(WARNING) << "Failed to GetTableReplInfo: " << table_name;
      }
      response->mutable_info_server()->CopyFrom(info_server);
      break;
    }
    default: {
      response->set_code(client::StatusCode::kError);
      response->set_msg("unsupported cmd type");
      LOG(ERROR) << "unsupported cmd type" << static_cast<int>(request->type());
      return;
    }
  }

  response->set_code(client::StatusCode::kOk);
}

// Sync between nodes
void SyncCmd::Do(const google::protobuf::Message *req,
    google::protobuf::Message *res, void* partition) const {
  const client::CmdRequest* request = static_cast<const client::CmdRequest*>(req);
  client::CmdResponse* response = static_cast<client::CmdResponse*>(res);
  Partition* ptr = static_cast<Partition*>(partition);

  response->Clear();
  client::CmdRequest_Sync sync_req = request->sync();

  slash::Status s;
  Node node(sync_req.node().ip(), sync_req.node().port());
  response->set_type(client::Type::SYNC);

  uint32_t s_filenum = sync_req.sync_offset().filenum();
  uint64_t s_offset = sync_req.sync_offset().offset();
  LOG(INFO) << "SyncCmd with a new node ("
    << ptr->table_name() << "_"  << ptr->partition_id()
    << "_" << node.ip << ":" << node.port << ", "
    << s_filenum << ", " << s_offset << ")";
  s = ptr->SlaveAskSync(node, s_filenum, s_offset);

  if (s.ok()) {
    response->set_code(client::StatusCode::kOk);
    DLOG(INFO) << "SyncCmd add node ok";
  } else if (s.IsEndFile() || s.IsInvalidArgument()) {
    // Need send fallback offset
    response->set_code(client::StatusCode::kFallback);
    client::CmdResponse_Sync *sync_res = response->mutable_sync();
    sync_res->set_table_name(sync_req.table_name());
    client::SyncOffset *offset = sync_res->mutable_sync_offset();
    if (s.IsEndFile()) {
      // Peer's offset is larger than me, send fallback offset
      ptr->GetWinBinlogOffset(&s_filenum, &s_offset);
      DLOG(INFO) << "SyncCmd with offset larger than me, node:"
        << sync_req.node().ip() << ":" << sync_req.node().port();
    } else {
      // Invalid filenum an offset, sen fallback offset
      DLOG(INFO) << "SyncCmd with offset invalid, node:"
        << sync_req.node().ip() << ":" << sync_req.node().port();
    }
    offset->set_filenum(s_filenum);
    s_offset = BinlogBlockStart(s_offset);
    offset->set_offset(s_offset);
    DLOG(INFO) << "Send back fallback binlog point: "
      << s_filenum << ", " << s_offset << " To: "
      << sync_req.node().ip() << ":" << sync_req.node().port();
  } else if (s.IsIncomplete()) {
    // Slave should wait for db sync
    response->set_code(client::StatusCode::kWait);
    DLOG(INFO) << "SyncCmd add node incomplete";
  } else {
    response->set_code(client::StatusCode::kError);
    response->set_msg(s.ToString());
    LOG(ERROR) << "command failed: Sync, caz " << s.ToString();
  }
}
<|endoftext|>


<commit_before>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// 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

// C++ includes
#include "libmesh_config.h"

#ifdef LIBMESH_HAVE_PETSC

// Local includes
#include "petsc_matrix.h"
#include "dof_map.h"
#include "dense_matrix.h"
#include "petsc_vector.h"

//-----------------------------------------------------------------------
// PetscMatrix members
template <typename T>
void PetscMatrix<T>::init (const unsigned int m,
			   const unsigned int n,
			   const unsigned int m_l,
			   const unsigned int n_l,
			   const unsigned int nnz,
			   const unsigned int noz)
{
  // We allow 0x0 matrices now
  //if ((m==0) || (n==0))
  //  return;

  // Clear initialized matrices
  if (this->initialized())
    this->clear();

  this->_is_initialized = true;

  int ierr     = 0;
  int m_global = static_cast<int>(m);
  int n_global = static_cast<int>(n);
  int m_local  = static_cast<int>(m_l);
  int n_local  = static_cast<int>(n_l);
  int n_nz     = static_cast<int>(nnz);
  int n_oz     = static_cast<int>(noz);

  // create a sequential matrix on one processor
  if (libMesh::n_processors() == 1)
    {
      libmesh_assert ((m_l == m) && (n_l == n));

      // Create matrix.  Revisit later to do preallocation and make more efficient
      ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			      n_nz, PETSC_NULL, &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  else
    {
      parallel_only();

      ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD, m_local, n_local, m_global, n_global,
			      n_nz, PETSC_NULL, n_oz, PETSC_NULL, &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  this->zero ();
}

template <typename T>
void PetscMatrix<T>::init ()
{
  libmesh_assert (this->_dof_map != NULL);

  // Clear initialized matrices
  if (this->initialized())
    this->clear();

  this->_is_initialized = true;

  int proc_id = 0;

  MPI_Comm_rank (libMesh::COMM_WORLD, &proc_id);

  const unsigned int m   = this->_dof_map->n_dofs();
  const unsigned int n   = m;
  const unsigned int n_l = this->_dof_map->n_dofs_on_processor(proc_id);
  const unsigned int m_l = n_l;

  const std::vector<unsigned int>& n_nz = this->_dof_map->get_n_nz();
  const std::vector<unsigned int>& n_oz = this->_dof_map->get_n_oz();

  // Make sure the sparsity pattern isn't empty unless the matrix is 0x0
  libmesh_assert (n_nz.size() == n_l);
  libmesh_assert (n_oz.size() == n_l);

  // We allow 0x0 matrices now
  //if (m==0)
  //  return;

  int ierr     = 0;
  int m_global = static_cast<int>(m);
  int n_global = static_cast<int>(n);
  int m_local  = static_cast<int>(m_l);
  int n_local  = static_cast<int>(n_l);

  // create a sequential matrix on one processor
  if (libMesh::n_processors() == 1)
    {
      libmesh_assert ((m_l == m) && (n_l == n));
      if (n_nz.empty())
        ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			        PETSC_NULL, (int*) PETSC_NULL, &_mat);
      else
        ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			        PETSC_NULL, (int*) &n_nz[0], &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  else
    {
      parallel_only();

      if (n_nz.empty())
        ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD,
			        m_local, n_local,
			        m_global, n_global,
			        PETSC_NULL, (int*) PETSC_NULL,
			        PETSC_NULL, (int*) PETSC_NULL, &_mat);
      else
        ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD,
			        m_local, n_local,
			        m_global, n_global,
			        PETSC_NULL, (int*) &n_nz[0],
			        PETSC_NULL, (int*) &n_oz[0], &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  this->zero();
}

template <typename T>
void PetscMatrix<T>::zero ()
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;

  ierr = MatZeroEntries(_mat);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::zero_rows (std::vector<int> & rows, T diag_value)
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;

  if(!rows.empty())
    ierr = MatZeroRows(_mat, rows.size(), &rows[0], diag_value);
  else
    ierr = MatZeroRows(_mat, 0, PETSC_NULL, diag_value);

  CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::clear ()
{
  int ierr=0;

  if ((this->initialized()) && (this->_destroy_mat_on_exit))
    {
      semiparallel_only();

      ierr = MatDestroy (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      this->_is_initialized = false;
    }
}

template <typename T>
Real PetscMatrix<T>::l1_norm () const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;
  PetscReal petsc_value;
  Real value;

  libmesh_assert (this->closed());

  ierr = MatNorm(_mat, NORM_1, &petsc_value);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  value = static_cast<Real>(petsc_value);

  return value;
}

template <typename T>
Real PetscMatrix<T>::linfty_norm () const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;
  PetscReal petsc_value;
  Real value;

  libmesh_assert (this->closed());

  ierr = MatNorm(_mat, NORM_INFINITY, &petsc_value);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  value = static_cast<Real>(petsc_value);

  return value;
}

template <typename T>
void PetscMatrix<T>::print_matlab (const std::string name) const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  // libmesh_assert (this->closed());
  this->close();

  int ierr=0;
  PetscViewer petsc_viewer;

  ierr = PetscViewerCreate (libMesh::COMM_WORLD,
			    &petsc_viewer);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  /**
   * Create an ASCII file containing the matrix
   * if a filename was provided.
   */
  if (name != "NULL")
    {
      ierr = PetscViewerASCIIOpen( libMesh::COMM_WORLD,
				   name.c_str(),
				   &petsc_viewer);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = PetscViewerSetFormat (petsc_viewer,
				   PETSC_VIEWER_ASCII_MATLAB);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatView (_mat, petsc_viewer);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  /**
   * Otherwise the matrix will be dumped to the screen.
   */
  else
    {
      ierr = PetscViewerSetFormat (PETSC_VIEWER_STDOUT_WORLD,
				   PETSC_VIEWER_ASCII_MATLAB);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatView (_mat, PETSC_VIEWER_STDOUT_WORLD);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  /**
   * Destroy the viewer.
   */
  ierr = PetscViewerDestroy (petsc_viewer);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::add_matrix(const DenseMatrix<T>& dm,
				const std::vector<unsigned int>& rows,
				const std::vector<unsigned int>& cols)
{
  libmesh_assert (this->initialized());

  const unsigned int m = dm.m();
  const unsigned int n = dm.n();

  libmesh_assert (rows.size() == m);
  libmesh_assert (cols.size() == n);

  int ierr=0;

  // These casts are required for PETSc <= 2.1.5
  ierr = MatSetValues(_mat,
		      m, (int*) &rows[0],
		      n, (int*) &cols[0],
		      (PetscScalar*) &dm.get_values()[0],
		      ADD_VALUES);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::_get_submatrix(SparseMatrix<T>& submatrix,
				    const std::vector<unsigned int> &rows,
				    const std::vector<unsigned int> &cols,
				    const bool reuse_submatrix) const
{
  // Can only extract submatrices from closed matrices
  this->close();

  // Make sure the SparseMatrix passed in is really a PetscMatrix
  PetscMatrix<T>* petsc_submatrix = libmesh_cast_ptr<PetscMatrix<T>*>(&submatrix);

  // Construct row and column index sets.
  int ierr=0;
  IS isrow, iscol;

  ierr = ISCreateGeneral(libMesh::COMM_WORLD,
			 rows.size(),
			 (int*) &rows[0],
			 &isrow); CHKERRABORT(libMesh::COMM_WORLD,ierr);

  ierr = ISCreateGeneral(libMesh::COMM_WORLD,
			 cols.size(),
			 (int*) &cols[0],
			 &iscol); CHKERRABORT(libMesh::COMM_WORLD,ierr);

  // Extract submatrix
  ierr = MatGetSubMatrix(_mat,
			 isrow,
			 iscol,
			 PETSC_DECIDE,
			 (reuse_submatrix ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX),
			 &(petsc_submatrix->_mat));  CHKERRABORT(libMesh::COMM_WORLD,ierr);

  // Specify that the new submatrix is initialized and close it.
  petsc_submatrix->_is_initialized = true;
  petsc_submatrix->close();

  // Clean up PETSc data structures
  ierr = ISDestroy(isrow); CHKERRABORT(libMesh::COMM_WORLD,ierr);
  ierr = ISDestroy(iscol); CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::get_diagonal (NumericVector<T>& dest) const
{
  // Make sure the NumericVector passed in is really a PetscVector
  PetscVector<T>& petsc_dest = libmesh_cast_ref<PetscVector<T>&>(dest);

  // Call PETSc function.

#if PETSC_VERSION_LESS_THAN(2,3,1)

  std::cout << "This method has been developed with PETSc 2.3.1.  "
	    << "No one has made it backwards compatible with older "
	    << "versions of PETSc so far; however, it might work "
	    << "without any change with some older version." << std::endl;
  libmesh_error();

#else

  // Needs a const_cast since PETSc does not work with const.
  int ierr =
    MatGetDiagonal(const_cast<PetscMatrix<T>*>(this)->mat(),petsc_dest.vec()); CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

}

template <typename T>
void PetscMatrix<T>::get_transpose (SparseMatrix<T>& dest) const
{
  // Make sure the SparseMatrix passed in is really a PetscMatrix
  PetscMatrix<T>& petsc_dest = libmesh_cast_ref<PetscMatrix<T>&>(dest);

  int ierr;
#if PETSC_VERSION_LESS_THAN(3,0,0)
  if (&petsc_dest == this)
    ierr = MatTranspose(_mat,PETSC_NULL);
  else
    ierr = MatTranspose(_mat,&petsc_dest._mat);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);
#else
  // FIXME - we can probably use MAT_REUSE_MATRIX in more situations
  if (&petsc_dest == this)
    ierr = MatTranspose(_mat,MAT_REUSE_MATRIX,&petsc_dest._mat);
  else
    ierr = MatTranspose(_mat,MAT_INITIAL_MATRIX,&petsc_dest._mat);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);
#endif
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscMatrix<Number>;

#endif // #ifdef LIBMESH_HAVE_PETSC
<commit_msg>Fixed potential memory leak in petsc_matrix.C, need to clear submatrix if we are not reusing the memory allocated for submatrix.<commit_after>// $Id$

// The libMesh Finite Element Library.
// Copyright (C) 2002-2008 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner

// 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

// C++ includes
#include "libmesh_config.h"

#ifdef LIBMESH_HAVE_PETSC

// Local includes
#include "petsc_matrix.h"
#include "dof_map.h"
#include "dense_matrix.h"
#include "petsc_vector.h"

//-----------------------------------------------------------------------
// PetscMatrix members
template <typename T>
void PetscMatrix<T>::init (const unsigned int m,
			   const unsigned int n,
			   const unsigned int m_l,
			   const unsigned int n_l,
			   const unsigned int nnz,
			   const unsigned int noz)
{
  // We allow 0x0 matrices now
  //if ((m==0) || (n==0))
  //  return;

  // Clear initialized matrices
  if (this->initialized())
    this->clear();

  this->_is_initialized = true;

  int ierr     = 0;
  int m_global = static_cast<int>(m);
  int n_global = static_cast<int>(n);
  int m_local  = static_cast<int>(m_l);
  int n_local  = static_cast<int>(n_l);
  int n_nz     = static_cast<int>(nnz);
  int n_oz     = static_cast<int>(noz);

  // create a sequential matrix on one processor
  if (libMesh::n_processors() == 1)
    {
      libmesh_assert ((m_l == m) && (n_l == n));

      // Create matrix.  Revisit later to do preallocation and make more efficient
      ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			      n_nz, PETSC_NULL, &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  else
    {
      parallel_only();

      ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD, m_local, n_local, m_global, n_global,
			      n_nz, PETSC_NULL, n_oz, PETSC_NULL, &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  this->zero ();
}

template <typename T>
void PetscMatrix<T>::init ()
{
  libmesh_assert (this->_dof_map != NULL);

  // Clear initialized matrices
  if (this->initialized())
    this->clear();

  this->_is_initialized = true;

  int proc_id = 0;

  MPI_Comm_rank (libMesh::COMM_WORLD, &proc_id);

  const unsigned int m   = this->_dof_map->n_dofs();
  const unsigned int n   = m;
  const unsigned int n_l = this->_dof_map->n_dofs_on_processor(proc_id);
  const unsigned int m_l = n_l;

  const std::vector<unsigned int>& n_nz = this->_dof_map->get_n_nz();
  const std::vector<unsigned int>& n_oz = this->_dof_map->get_n_oz();

  // Make sure the sparsity pattern isn't empty unless the matrix is 0x0
  libmesh_assert (n_nz.size() == n_l);
  libmesh_assert (n_oz.size() == n_l);

  // We allow 0x0 matrices now
  //if (m==0)
  //  return;

  int ierr     = 0;
  int m_global = static_cast<int>(m);
  int n_global = static_cast<int>(n);
  int m_local  = static_cast<int>(m_l);
  int n_local  = static_cast<int>(n_l);

  // create a sequential matrix on one processor
  if (libMesh::n_processors() == 1)
    {
      libmesh_assert ((m_l == m) && (n_l == n));
      if (n_nz.empty())
        ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			        PETSC_NULL, (int*) PETSC_NULL, &_mat);
      else
        ierr = MatCreateSeqAIJ (libMesh::COMM_WORLD, n_global, n_global,
			        PETSC_NULL, (int*) &n_nz[0], &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  else
    {
      parallel_only();

      if (n_nz.empty())
        ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD,
			        m_local, n_local,
			        m_global, n_global,
			        PETSC_NULL, (int*) PETSC_NULL,
			        PETSC_NULL, (int*) PETSC_NULL, &_mat);
      else
        ierr = MatCreateMPIAIJ (libMesh::COMM_WORLD,
			        m_local, n_local,
			        m_global, n_global,
			        PETSC_NULL, (int*) &n_nz[0],
			        PETSC_NULL, (int*) &n_oz[0], &_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatSetFromOptions (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  this->zero();
}

template <typename T>
void PetscMatrix<T>::zero ()
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;

  ierr = MatZeroEntries(_mat);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::zero_rows (std::vector<int> & rows, T diag_value)
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;

  if(!rows.empty())
    ierr = MatZeroRows(_mat, rows.size(), &rows[0], diag_value);
  else
    ierr = MatZeroRows(_mat, 0, PETSC_NULL, diag_value);

  CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::clear ()
{
  int ierr=0;

  if ((this->initialized()) && (this->_destroy_mat_on_exit))
    {
      semiparallel_only();

      ierr = MatDestroy (_mat);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      this->_is_initialized = false;
    }
}

template <typename T>
Real PetscMatrix<T>::l1_norm () const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;
  PetscReal petsc_value;
  Real value;

  libmesh_assert (this->closed());

  ierr = MatNorm(_mat, NORM_1, &petsc_value);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  value = static_cast<Real>(petsc_value);

  return value;
}

template <typename T>
Real PetscMatrix<T>::linfty_norm () const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  int ierr=0;
  PetscReal petsc_value;
  Real value;

  libmesh_assert (this->closed());

  ierr = MatNorm(_mat, NORM_INFINITY, &petsc_value);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  value = static_cast<Real>(petsc_value);

  return value;
}

template <typename T>
void PetscMatrix<T>::print_matlab (const std::string name) const
{
  libmesh_assert (this->initialized());

  semiparallel_only();

  // libmesh_assert (this->closed());
  this->close();

  int ierr=0;
  PetscViewer petsc_viewer;

  ierr = PetscViewerCreate (libMesh::COMM_WORLD,
			    &petsc_viewer);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);

  /**
   * Create an ASCII file containing the matrix
   * if a filename was provided.
   */
  if (name != "NULL")
    {
      ierr = PetscViewerASCIIOpen( libMesh::COMM_WORLD,
				   name.c_str(),
				   &petsc_viewer);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = PetscViewerSetFormat (petsc_viewer,
				   PETSC_VIEWER_ASCII_MATLAB);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatView (_mat, petsc_viewer);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  /**
   * Otherwise the matrix will be dumped to the screen.
   */
  else
    {
      ierr = PetscViewerSetFormat (PETSC_VIEWER_STDOUT_WORLD,
				   PETSC_VIEWER_ASCII_MATLAB);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);

      ierr = MatView (_mat, PETSC_VIEWER_STDOUT_WORLD);
             CHKERRABORT(libMesh::COMM_WORLD,ierr);
    }

  /**
   * Destroy the viewer.
   */
  ierr = PetscViewerDestroy (petsc_viewer);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::add_matrix(const DenseMatrix<T>& dm,
				const std::vector<unsigned int>& rows,
				const std::vector<unsigned int>& cols)
{
  libmesh_assert (this->initialized());

  const unsigned int m = dm.m();
  const unsigned int n = dm.n();

  libmesh_assert (rows.size() == m);
  libmesh_assert (cols.size() == n);

  int ierr=0;

  // These casts are required for PETSc <= 2.1.5
  ierr = MatSetValues(_mat,
		      m, (int*) &rows[0],
		      n, (int*) &cols[0],
		      (PetscScalar*) &dm.get_values()[0],
		      ADD_VALUES);
         CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::_get_submatrix(SparseMatrix<T>& submatrix,
				    const std::vector<unsigned int> &rows,
				    const std::vector<unsigned int> &cols,
				    const bool reuse_submatrix) const
{
  // Can only extract submatrices from closed matrices
  this->close();

  // Make sure the SparseMatrix passed in is really a PetscMatrix
  PetscMatrix<T>* petsc_submatrix = libmesh_cast_ptr<PetscMatrix<T>*>(&submatrix);

  // If we're not reusing submatrix and submatrix is already initialized
  // then we need to clear it, otherwise we get a memory leak.
  if( !reuse_submatrix && submatrix.initialized() )
    submatrix.clear();

  // Construct row and column index sets.
  int ierr=0;
  IS isrow, iscol;

  ierr = ISCreateGeneral(libMesh::COMM_WORLD,
			 rows.size(),
			 (int*) &rows[0],
			 &isrow); CHKERRABORT(libMesh::COMM_WORLD,ierr);

  ierr = ISCreateGeneral(libMesh::COMM_WORLD,
			 cols.size(),
			 (int*) &cols[0],
			 &iscol); CHKERRABORT(libMesh::COMM_WORLD,ierr);

  // Extract submatrix
  ierr = MatGetSubMatrix(_mat,
			 isrow,
			 iscol,
			 PETSC_DECIDE,
			 (reuse_submatrix ? MAT_REUSE_MATRIX : MAT_INITIAL_MATRIX),
			 &(petsc_submatrix->_mat));  CHKERRABORT(libMesh::COMM_WORLD,ierr);

  // Specify that the new submatrix is initialized and close it.
  petsc_submatrix->_is_initialized = true;
  petsc_submatrix->close();

  // Clean up PETSc data structures
  ierr = ISDestroy(isrow); CHKERRABORT(libMesh::COMM_WORLD,ierr);
  ierr = ISDestroy(iscol); CHKERRABORT(libMesh::COMM_WORLD,ierr);
}

template <typename T>
void PetscMatrix<T>::get_diagonal (NumericVector<T>& dest) const
{
  // Make sure the NumericVector passed in is really a PetscVector
  PetscVector<T>& petsc_dest = libmesh_cast_ref<PetscVector<T>&>(dest);

  // Call PETSc function.

#if PETSC_VERSION_LESS_THAN(2,3,1)

  std::cout << "This method has been developed with PETSc 2.3.1.  "
	    << "No one has made it backwards compatible with older "
	    << "versions of PETSc so far; however, it might work "
	    << "without any change with some older version." << std::endl;
  libmesh_error();

#else

  // Needs a const_cast since PETSc does not work with const.
  int ierr =
    MatGetDiagonal(const_cast<PetscMatrix<T>*>(this)->mat(),petsc_dest.vec()); CHKERRABORT(libMesh::COMM_WORLD,ierr);

#endif

}

template <typename T>
void PetscMatrix<T>::get_transpose (SparseMatrix<T>& dest) const
{
  // Make sure the SparseMatrix passed in is really a PetscMatrix
  PetscMatrix<T>& petsc_dest = libmesh_cast_ref<PetscMatrix<T>&>(dest);

  int ierr;
#if PETSC_VERSION_LESS_THAN(3,0,0)
  if (&petsc_dest == this)
    ierr = MatTranspose(_mat,PETSC_NULL);
  else
    ierr = MatTranspose(_mat,&petsc_dest._mat);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);
#else
  // FIXME - we can probably use MAT_REUSE_MATRIX in more situations
  if (&petsc_dest == this)
    ierr = MatTranspose(_mat,MAT_REUSE_MATRIX,&petsc_dest._mat);
  else
    ierr = MatTranspose(_mat,MAT_INITIAL_MATRIX,&petsc_dest._mat);
  CHKERRABORT(libMesh::COMM_WORLD,ierr);
#endif
}

//------------------------------------------------------------------
// Explicit instantiations
template class PetscMatrix<Number>;

#endif // #ifdef LIBMESH_HAVE_PETSC
<|endoftext|>


<commit_before>// Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.com>
//
// 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 <nvcore/Debug.h>
#include <nvcore/Library.h>
#include "CudaUtils.h"

#if defined HAVE_CUDA
#include <cuda_runtime.h>
#endif

using namespace nv;
using namespace cuda;

#if NV_OS_WIN32

#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>

static bool isWindowsVista()
{
	OSVERSIONINFO osvi;
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	::GetVersionEx(&osvi);
	return osvi.dwMajorVersion >= 6;
}

typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);

static bool isWow32()
{
	LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");

	BOOL bIsWow64 = FALSE;

	if (NULL != fnIsWow64Process)
	{
		if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
		{
			// Assume 32 bits.
			return true;
		}
	}

	return !bIsWow64;
}

#endif

static bool isCudaDriverAvailable(uint version)
{
#if NV_OS_WIN32
	Library nvcuda("nvcuda.dll");
#else
	Library nvcuda(NV_LIBRARY_NAME("cuda"));
#endif

	if (!nvcuda.isValid())
	{
		return false;
	}

	if (version > 2000)
	{
		void * address = nvcuda.bindSymbol("cuStreamCreate");
		if (address == NULL) return false;
	}

	if (version > 2010)
	{
		void * address = nvcuda.bindSymbol("cuLoadDataEx");
		if (address == NULL) return false;
	}

	return true;
}

/// Determine if CUDA is available.
bool nv::cuda::isHardwarePresent()
{
#if defined HAVE_CUDA
#if NV_OS_WIN32
	//if (isWindowsVista()) return false;
	//if (isWindowsVista() || !isWow32()) return false;
#endif
	int count = deviceCount();
	if (count == 1)
	{
		// Make sure it's not an emulation device.
		cudaDeviceProp deviceProp;
		cudaGetDeviceProperties(&deviceProp, 0);

		// deviceProp.name != Device Emulation (CPU)
		if (deviceProp.major == -1 || deviceProp.minor == -1)
		{
			return false;
		}

		// Make sure that CUDA driver matches CUDA runtime.
		if (!isCudaDriverAvailable(CUDART_VERSION))
		{
			return false;
		}

		// @@ Make sure that warp size == 32
	}

	return count > 0;
#else
	return false;
#endif
}

/// Get number of CUDA enabled devices.
int nv::cuda::deviceCount()
{
#if defined HAVE_CUDA
	int gpuCount = 0;

	cudaError_t result = cudaGetDeviceCount(&gpuCount);

	if (result == cudaSuccess)
	{
		return gpuCount;
	}
#endif
	return 0;
}

int nv::cuda::getFastestDevice()
{
	const int device_count = deviceCount();

	int max_gflops_device = 0;
	int max_gflops = 0;

	int current_device = 0;
	while (current_device < device_count)
	{
		cudaDeviceProp device_properties;
		cudaGetDeviceProperties(&device_properties, current_device);
		int gflops = device_properties.multiProcessorCount * device_properties.clockRate;

		if (device_properties.major != -1 && device_properties.minor != -1)
		{
			if( gflops > max_gflops )
			{
				max_gflops = gflops;
				max_gflops_device = current_device;
			}
		}

		current_device++;
	}

	return max_gflops_device;
}

/// Activate the given devices.
bool nv::cuda::setDevice(int i)
{
	nvCheck(i < deviceCount());
#if defined HAVE_CUDA
	cudaError_t result = cudaSetDevice(i);
	return result == cudaSuccess;
#else
	return false;
#endif
}
<commit_msg>Do not use cuda API when CUDA not found. Fix end of lines.<commit_after>// Copyright NVIDIA Corporation 2007 -- Ignacio Castano <icastano@nvidia.com>
//
// 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 <nvcore/Debug.h>
#include <nvcore/Library.h>
#include "CudaUtils.h"

#if defined HAVE_CUDA
#include <cuda_runtime.h>
#endif

using namespace nv;
using namespace cuda;

#if NV_OS_WIN32

#define WINDOWS_LEAN_AND_MEAN
#include <windows.h>

static bool isWindowsVista()
{
	OSVERSIONINFO osvi;
	osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

	::GetVersionEx(&osvi);
	return osvi.dwMajorVersion >= 6;
}

typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL);

static bool isWow32()
{
	LPFN_ISWOW64PROCESS fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), "IsWow64Process");

	BOOL bIsWow64 = FALSE;

	if (NULL != fnIsWow64Process)
	{
		if (!fnIsWow64Process(GetCurrentProcess(), &bIsWow64))
		{
			// Assume 32 bits.
			return true;
		}
	}

	return !bIsWow64;
}

#endif

static bool isCudaDriverAvailable(uint version)
{
#if NV_OS_WIN32
	Library nvcuda("nvcuda.dll");
#else
	Library nvcuda(NV_LIBRARY_NAME("cuda"));
#endif

	if (!nvcuda.isValid())
	{
		return false;
	}

	if (version > 2000)
	{
		void * address = nvcuda.bindSymbol("cuStreamCreate");
		if (address == NULL) return false;
	}

	if (version > 2010)
	{
		void * address = nvcuda.bindSymbol("cuLoadDataEx");
		if (address == NULL) return false;
	}

	return true;
}

/// Determine if CUDA is available.
bool nv::cuda::isHardwarePresent()
{
#if defined HAVE_CUDA
#if NV_OS_WIN32
	//if (isWindowsVista()) return false;
	//if (isWindowsVista() || !isWow32()) return false;
#endif
	int count = deviceCount();
	if (count == 1)
	{
		// Make sure it's not an emulation device.
		cudaDeviceProp deviceProp;
		cudaGetDeviceProperties(&deviceProp, 0);

		// deviceProp.name != Device Emulation (CPU)
		if (deviceProp.major == -1 || deviceProp.minor == -1)
		{
			return false;
		}

		// Make sure that CUDA driver matches CUDA runtime.
		if (!isCudaDriverAvailable(CUDART_VERSION))
		{
			return false;
		}

		// @@ Make sure that warp size == 32
	}

	return count > 0;
#else
	return false;
#endif
}

/// Get number of CUDA enabled devices.
int nv::cuda::deviceCount()
{
#if defined HAVE_CUDA
	int gpuCount = 0;

	cudaError_t result = cudaGetDeviceCount(&gpuCount);

	if (result == cudaSuccess)
	{
		return gpuCount;
	}
#endif
	return 0;
}

int nv::cuda::getFastestDevice()
{
	int max_gflops_device = 0;
#if defined HAVE_CUDA
	int max_gflops = 0;

	const int device_count = deviceCount();
	int current_device = 0;
	while (current_device < device_count)
	{
		cudaDeviceProp device_properties;
		cudaGetDeviceProperties(&device_properties, current_device);
		int gflops = device_properties.multiProcessorCount * device_properties.clockRate;

		if (device_properties.major != -1 && device_properties.minor != -1)
		{
			if( gflops > max_gflops )
			{
				max_gflops = gflops;
				max_gflops_device = current_device;
			}
		}

		current_device++;
	}
#endif
	return max_gflops_device;
}

/// Activate the given devices.
bool nv::cuda::setDevice(int i)
{
	nvCheck(i < deviceCount());
#if defined HAVE_CUDA
	cudaError_t result = cudaSetDevice(i);
	return result == cudaSuccess;
#else
	return false;
#endif
}
<|endoftext|>


<commit_before>#include "output_https.h"
#include <mist/procs.h>

namespace Mist{
  mbedtls_entropy_context OutHTTPS::entropy;
  mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg;
  mbedtls_ssl_config OutHTTPS::sslConf;
  mbedtls_x509_crt OutHTTPS::srvcert;
  mbedtls_pk_context OutHTTPS::pkey;

  void OutHTTPS::init(Util::Config *cfg){
    Output::init(cfg);
    capa["provides"] = "HTTP";
    capa["protocol"] = "https://";
    capa["required"]["cert"]["name"] = "Certificate";
    capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain";
    capa["required"]["cert"]["option"] = "--cert";
    capa["required"]["cert"]["short"] = "C";
    capa["required"]["cert"]["default"] = "";
    capa["required"]["cert"]["type"] = "str";
    capa["required"]["key"]["name"] = "Key";
    capa["required"]["key"]["help"] = "Private key for SSL";
    capa["required"]["key"]["option"] = "--key";
    capa["required"]["key"]["short"] = "K";
    capa["required"]["key"]["default"] = "";
    capa["required"]["key"]["type"] = "str";
    cfg->addConnectorOptions(4433, capa);
    config = cfg;
  }

  OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){
    int ret;
    mbedtls_net_init(&client_fd);
    client_fd.fd = C.getSocket();
    mbedtls_ssl_init(&ssl);
    if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){
      FAIL_MSG("Could not reseed");
      C.close();
      return;
    }

    // Set up the SSL connection
    if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){
      FAIL_MSG("Could not set up SSL connection");
      C.close();
      return;
    }

    // Inform mbedtls how we'd like to use the connection (uses default bio handlers)
    // We tell it to use non-blocking IO here
    mbedtls_net_set_nonblock(&client_fd);
    mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
    // do the SSL handshake
    while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        FAIL_MSG("Could not handshake, SSL error: %d", ret);
        C.close();
        return;
      }else{
        Util::sleep(100);
      }
    }
    HIGH_MSG("Started SSL connection handler");
  }

  int OutHTTPS::run(){
    unsigned char buf[1024 * 4]; // 4k internal buffer
    int ret;

    // Start a MistOutHTTP process, connected to this SSL connection
    int fderr = 2;
    int fd[2];
    if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){
      FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!");
      return 1;
    }
    std::deque<std::string> args;
    args.push_back(Util::getMyPath() + "MistOutHTTP");
    args.push_back("--ip");
    args.push_back(myConn.getHost());
    args.push_back("");
    Util::Procs::socketList.insert(fd[0]);
    pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr);
    close(fd[1]);
    if (http_proc < 2){
      FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!");
      return 1;
    }
    Socket::Connection http(fd[0]);
    http.setBlocking(false);
    Socket::Buffer &http_buf = http.Received();

    // pass data back and forth between the SSL connection and HTTP process while connected
    while (config->is_active && http){
      bool activity = false;
      // attempt to read SSL data, pass to HTTP
      ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        if (ret <= 0){
          HIGH_MSG("SSL disconnect!");
          break;
        }
        // we received ret bytes of data to pass on. Do so.
        activity = true;
        http.SendNow((const char *)buf, ret);
      }

      // attempt to read HTTP data, pass to SSL
      if (http.spool() || http_buf.size()){
        // We have data - pass it on
        activity = true;
        while (http_buf.size() && http){
          int todo = http_buf.get().size();
          int done = 0;
          while (done < todo){
            ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done);
            if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){
              HIGH_MSG("SSL disconnect!");
              http.close();
              break;
            }
            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
              done += ret;
            }else{
              Util::sleep(50);
            }
          }
          http_buf.get().clear();
        }
      }
      if (!activity){
        Util::sleep(50);
      }
    }
    // close the HTTP process (close stdio, kill its PID)
    http.close();
    Util::Procs::Stop(http_proc);
    uint16_t waiting = 0;
    while (++waiting < 100){
      if (!Util::Procs::isRunning(http_proc)){break;}
      Util::sleep(100);
    }
    return 0;
  }

  OutHTTPS::~OutHTTPS(){
    HIGH_MSG("Ending SSL connection handler");
    // close when we're done
    mbedtls_ssl_close_notify(&ssl);
    mbedtls_ssl_free(&ssl);
    mbedtls_net_free(&client_fd);
    myConn.close();
  }

  /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket
  void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){
    if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){
      FAIL_MSG("The cert/key required options were not passed!");
      return;
    }

    //Declare and set up all required mbedtls structures
    int ret;
    mbedtls_ssl_config_init(&sslConf);
    mbedtls_entropy_init(&entropy);
    mbedtls_pk_init(&pkey);
    mbedtls_x509_crt_init(&srvcert);
    mbedtls_ctr_drbg_init(&ctr_drbg);

    // seed the rng
    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){
      FAIL_MSG("Could not seed the random number generator!");
    }

    //Read certificate chain(s) from cmdline option(s)
    JSON::Value certs = config->getOption("cert", true);
    jsonForEach(certs, it){
      if (it->asStringRef().size()){//Ignore empty entries (default is empty)
        ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str());
        if (ret != 0){
          WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str());
        }
      }
    }

    //Read key from cmdline option
    ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0);
    if (ret != 0){
      FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str());
      return;
    }

    if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){
      FAIL_MSG("SSL config defaults failed");
      return;
    }
    mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg);
    mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL);
    if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){
      FAIL_MSG("SSL config own certificate failed");
      return;
    }

    Output::listener(conf, callback);

    //Free all the mbedtls structures
    mbedtls_x509_crt_free(&srvcert);
    mbedtls_pk_free(&pkey);
    mbedtls_ssl_config_free(&sslConf);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
  }
}

<commit_msg>Changed SSL handshake error to level MEDIUM instead of FAIL<commit_after>#include "output_https.h"
#include <mist/procs.h>

namespace Mist{
  mbedtls_entropy_context OutHTTPS::entropy;
  mbedtls_ctr_drbg_context OutHTTPS::ctr_drbg;
  mbedtls_ssl_config OutHTTPS::sslConf;
  mbedtls_x509_crt OutHTTPS::srvcert;
  mbedtls_pk_context OutHTTPS::pkey;

  void OutHTTPS::init(Util::Config *cfg){
    Output::init(cfg);
    capa["provides"] = "HTTP";
    capa["protocol"] = "https://";
    capa["required"]["cert"]["name"] = "Certificate";
    capa["required"]["cert"]["help"] = "(Root) certificate(s) file(s) to append to chain";
    capa["required"]["cert"]["option"] = "--cert";
    capa["required"]["cert"]["short"] = "C";
    capa["required"]["cert"]["default"] = "";
    capa["required"]["cert"]["type"] = "str";
    capa["required"]["key"]["name"] = "Key";
    capa["required"]["key"]["help"] = "Private key for SSL";
    capa["required"]["key"]["option"] = "--key";
    capa["required"]["key"]["short"] = "K";
    capa["required"]["key"]["default"] = "";
    capa["required"]["key"]["type"] = "str";
    cfg->addConnectorOptions(4433, capa);
    config = cfg;
  }

  OutHTTPS::OutHTTPS(Socket::Connection &C) : Output(C){
    int ret;
    mbedtls_net_init(&client_fd);
    client_fd.fd = C.getSocket();
    mbedtls_ssl_init(&ssl);
    if ((ret = mbedtls_ctr_drbg_reseed(&ctr_drbg, (const unsigned char *)"child", 5)) != 0){
      FAIL_MSG("Could not reseed");
      C.close();
      return;
    }

    // Set up the SSL connection
    if ((ret = mbedtls_ssl_setup(&ssl, &sslConf)) != 0){
      FAIL_MSG("Could not set up SSL connection");
      C.close();
      return;
    }

    // Inform mbedtls how we'd like to use the connection (uses default bio handlers)
    // We tell it to use non-blocking IO here
    mbedtls_net_set_nonblock(&client_fd);
    mbedtls_ssl_set_bio(&ssl, &client_fd, mbedtls_net_send, mbedtls_net_recv, NULL);
    // do the SSL handshake
    while ((ret = mbedtls_ssl_handshake(&ssl)) != 0){
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        MEDIUM_MSG("Could not handshake, SSL error: %d", ret);
        C.close();
        return;
      }else{
        Util::sleep(100);
      }
    }
    HIGH_MSG("Started SSL connection handler");
  }

  int OutHTTPS::run(){
    unsigned char buf[1024 * 4]; // 4k internal buffer
    int ret;

    // Start a MistOutHTTP process, connected to this SSL connection
    int fderr = 2;
    int fd[2];
    if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fd) != 0){
      FAIL_MSG("Could not open anonymous socket for SSL<->HTTP connection!");
      return 1;
    }
    std::deque<std::string> args;
    args.push_back(Util::getMyPath() + "MistOutHTTP");
    args.push_back("--ip");
    args.push_back(myConn.getHost());
    args.push_back("");
    Util::Procs::socketList.insert(fd[0]);
    pid_t http_proc = Util::Procs::StartPiped(args, &(fd[1]), &(fd[1]), &fderr);
    close(fd[1]);
    if (http_proc < 2){
      FAIL_MSG("Could not spawn MistOutHTTP process for SSL connection!");
      return 1;
    }
    Socket::Connection http(fd[0]);
    http.setBlocking(false);
    Socket::Buffer &http_buf = http.Received();

    // pass data back and forth between the SSL connection and HTTP process while connected
    while (config->is_active && http){
      bool activity = false;
      // attempt to read SSL data, pass to HTTP
      ret = mbedtls_ssl_read(&ssl, buf, sizeof(buf));
      if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
        if (ret <= 0){
          HIGH_MSG("SSL disconnect!");
          break;
        }
        // we received ret bytes of data to pass on. Do so.
        activity = true;
        http.SendNow((const char *)buf, ret);
      }

      // attempt to read HTTP data, pass to SSL
      if (http.spool() || http_buf.size()){
        // We have data - pass it on
        activity = true;
        while (http_buf.size() && http){
          int todo = http_buf.get().size();
          int done = 0;
          while (done < todo){
            ret = mbedtls_ssl_write(&ssl, (const unsigned char*)http_buf.get().data() + done, todo - done);
            if (ret == MBEDTLS_ERR_NET_CONN_RESET || ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT){
              HIGH_MSG("SSL disconnect!");
              http.close();
              break;
            }
            if (ret != MBEDTLS_ERR_SSL_WANT_READ && ret != MBEDTLS_ERR_SSL_WANT_WRITE){
              done += ret;
            }else{
              Util::sleep(50);
            }
          }
          http_buf.get().clear();
        }
      }
      if (!activity){
        Util::sleep(50);
      }
    }
    // close the HTTP process (close stdio, kill its PID)
    http.close();
    Util::Procs::Stop(http_proc);
    uint16_t waiting = 0;
    while (++waiting < 100){
      if (!Util::Procs::isRunning(http_proc)){break;}
      Util::sleep(100);
    }
    return 0;
  }

  OutHTTPS::~OutHTTPS(){
    HIGH_MSG("Ending SSL connection handler");
    // close when we're done
    mbedtls_ssl_close_notify(&ssl);
    mbedtls_ssl_free(&ssl);
    mbedtls_net_free(&client_fd);
    myConn.close();
  }

  /// Listens for HTTPS requests, accepting them and connecting them to a HTTP socket
  void OutHTTPS::listener(Util::Config &conf, int (*callback)(Socket::Connection &S)){
    if (config->getOption("cert", true).size() < 2 || config->getOption("key", true).size() < 2){
      FAIL_MSG("The cert/key required options were not passed!");
      return;
    }

    //Declare and set up all required mbedtls structures
    int ret;
    mbedtls_ssl_config_init(&sslConf);
    mbedtls_entropy_init(&entropy);
    mbedtls_pk_init(&pkey);
    mbedtls_x509_crt_init(&srvcert);
    mbedtls_ctr_drbg_init(&ctr_drbg);

    // seed the rng
    if ((ret = mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const unsigned char *)"MistServer", 10)) != 0){
      FAIL_MSG("Could not seed the random number generator!");
    }

    //Read certificate chain(s) from cmdline option(s)
    JSON::Value certs = config->getOption("cert", true);
    jsonForEach(certs, it){
      if (it->asStringRef().size()){//Ignore empty entries (default is empty)
        ret = mbedtls_x509_crt_parse_file(&srvcert, it->asStringRef().c_str());
        if (ret != 0){
          WARN_MSG("Could not load any certificates from file: %s", it->asStringRef().c_str());
        }
      }
    }

    //Read key from cmdline option
    ret = mbedtls_pk_parse_keyfile(&pkey, config->getString("key").c_str(), 0);
    if (ret != 0){
      FAIL_MSG("Could not load any keys from file: %s", config->getString("key").c_str());
      return;
    }

    if ((ret = mbedtls_ssl_config_defaults(&sslConf, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT)) != 0){
      FAIL_MSG("SSL config defaults failed");
      return;
    }
    mbedtls_ssl_conf_rng(&sslConf, mbedtls_ctr_drbg_random, &ctr_drbg);
    mbedtls_ssl_conf_ca_chain(&sslConf, srvcert.next, NULL);
    if ((ret = mbedtls_ssl_conf_own_cert(&sslConf, &srvcert, &pkey)) != 0){
      FAIL_MSG("SSL config own certificate failed");
      return;
    }

    Output::listener(conf, callback);

    //Free all the mbedtls structures
    mbedtls_x509_crt_free(&srvcert);
    mbedtls_pk_free(&pkey);
    mbedtls_ssl_config_free(&sslConf);
    mbedtls_ctr_drbg_free(&ctr_drbg);
    mbedtls_entropy_free(&entropy);
  }
}

<|endoftext|>


<commit_before>/*
 * This file is part of telepathy-accounts-kcm
 *
 * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "protocol-list-model.h"

#include "connection-manager-item.h"
#include "protocol-item.h"

#include <KDebug>

ProtocolListModel::ProtocolListModel(QObject *parent)
 : QAbstractListModel(parent)
{
    kDebug();

    m_connectionManagerItems.clear();
    m_protocolItems.clear();
}

ProtocolListModel::~ProtocolListModel()
{
    kDebug();

    // TODO: Implement me!
}

int ProtocolListModel::rowCount(const QModelIndex &index) const
{
    // If the index is the root item, then return the row count.
    if (index == QModelIndex()) {
       return m_protocolItems.size();
    }

    // Otherwise, return 0 (as this is a list model, so all items
    // are children of the root item).
    return 0;
}

QVariant ProtocolListModel::data(const QModelIndex &index, int role) const
{
    // FIXME: This is a basic implementation just so I can see what's going
    // on while developing this code further. Needs expanding.
    QVariant data;

    switch(role)
    {
    case Qt::DisplayRole:
        data = QVariant(m_protocolItems.at(index.row())->protocol());
        break;
    default:
        break;
    }

    return data;
}

void ProtocolListModel::addConnectionManager(Tp::ConnectionManagerPtr connectionManager)
{
    kDebug() << "Creating a new ConnectionManagerItem from connection manager:"
             << connectionManager.data();

    // Check if the cm is already in the list.
    foreach (const ConnectionManagerItem* cmi, m_connectionManagerItems) {
        if (cmi->connectionManager() == connectionManager) {
            kWarning() << "Connection Manager: "
                       << cmi->connectionManager()->name()
                       << " is already in the list.";
            return;
        }
    }

    kDebug() << "Connection Manager not already in list. Create new ConnectionManagerItem from cm:"
             << connectionManager.data();

    ConnectionManagerItem *item = new ConnectionManagerItem(connectionManager, this);
    m_connectionManagerItems.append(item);

    connect(item, SIGNAL(newProtocol(const QString&)),
            this, SLOT(onNewProtocol(const QString&)));
}

void ProtocolListModel::onNewProtocol(const QString& protocol)
{
    kDebug();

    // New protocol supported by a connection manager. Get the ConnectionManagerItem for it.
    ConnectionManagerItem *cmItem = qobject_cast<ConnectionManagerItem*>(sender());
    if (!cmItem) {
        kWarning() << "Slot was called by a non-ConnectionManagerItem object.";
        return;
    }

    // Check that the protocol/cm pair are not already in the model.
    foreach (ProtocolItem *pi, m_protocolItems) {
        if ((pi->parent() == cmItem) && (pi->protocol() == protocol)) {
            kWarning() << "Protocol/CM pair already in the model.";
            return;
        }
    }

    // Add the Protocol/CM pair to the model.
    beginInsertRows(QModelIndex(), m_protocolItems.size(), m_protocolItems.size());
    ProtocolItem *item = new ProtocolItem(protocol, cmItem);
    m_protocolItems.append(item);
    endInsertRows();
}

ProtocolItem *ProtocolListModel::itemForIndex(const QModelIndex &index) const
{
    return m_protocolItems.at(index.row());
}

#include "protocol-list-model.moc"

<commit_msg>Show the icons for the protocols in the Add Account Assistant's first page. The icon names follow the same naming scheme as Empathy. We'll need to get them moved out of Kopete's protocol folders at some point so that they can be installed accessible for all Telepathy enabled apps.<commit_after>/*
 * This file is part of telepathy-accounts-kcm
 *
 * Copyright (C) 2009 Collabora Ltd. <http://www.collabora.co.uk/>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */

#include "protocol-list-model.h"

#include "connection-manager-item.h"
#include "protocol-item.h"

#include <KIcon>
#include <KDebug>

ProtocolListModel::ProtocolListModel(QObject *parent)
 : QAbstractListModel(parent)
{
    kDebug();

    m_connectionManagerItems.clear();
    m_protocolItems.clear();
}

ProtocolListModel::~ProtocolListModel()
{
    kDebug();

    // TODO: Implement me!
}

int ProtocolListModel::rowCount(const QModelIndex &index) const
{
    // If the index is the root item, then return the row count.
    if (index == QModelIndex()) {
       return m_protocolItems.size();
    }

    // Otherwise, return 0 (as this is a list model, so all items
    // are children of the root item).
    return 0;
}

QVariant ProtocolListModel::data(const QModelIndex &index, int role) const
{
    // FIXME: This is a basic implementation just so I can see what's going
    // on while developing this code further. Needs expanding.
    QVariant data;

    switch(role)
    {
    case Qt::DisplayRole:
        data = QVariant(m_protocolItems.at(index.row())->protocol());
        break;
    case Qt::DecorationRole:
        // Look for an icon named im-<protocolname>
        data = QVariant(KIcon((QString("im-%1").arg(m_protocolItems.at(index.row())->protocol()))));
        break;
    default:
        break;
    }

    return data;
}

void ProtocolListModel::addConnectionManager(Tp::ConnectionManagerPtr connectionManager)
{
    kDebug() << "Creating a new ConnectionManagerItem from connection manager:"
             << connectionManager.data();

    // Check if the cm is already in the list.
    foreach (const ConnectionManagerItem* cmi, m_connectionManagerItems) {
        if (cmi->connectionManager() == connectionManager) {
            kWarning() << "Connection Manager: "
                       << cmi->connectionManager()->name()
                       << " is already in the list.";
            return;
        }
    }

    kDebug() << "Connection Manager not already in list. Create new ConnectionManagerItem from cm:"
             << connectionManager.data();

    ConnectionManagerItem *item = new ConnectionManagerItem(connectionManager, this);
    m_connectionManagerItems.append(item);

    connect(item, SIGNAL(newProtocol(const QString&)),
            this, SLOT(onNewProtocol(const QString&)));
}

void ProtocolListModel::onNewProtocol(const QString& protocol)
{
    kDebug();

    // New protocol supported by a connection manager. Get the ConnectionManagerItem for it.
    ConnectionManagerItem *cmItem = qobject_cast<ConnectionManagerItem*>(sender());
    if (!cmItem) {
        kWarning() << "Slot was called by a non-ConnectionManagerItem object.";
        return;
    }

    // Check that the protocol/cm pair are not already in the model.
    foreach (ProtocolItem *pi, m_protocolItems) {
        if ((pi->parent() == cmItem) && (pi->protocol() == protocol)) {
            kWarning() << "Protocol/CM pair already in the model.";
            return;
        }
    }

    // Add the Protocol/CM pair to the model.
    beginInsertRows(QModelIndex(), m_protocolItems.size(), m_protocolItems.size());
    ProtocolItem *item = new ProtocolItem(protocol, cmItem);
    m_protocolItems.append(item);
    endInsertRows();
}

ProtocolItem *ProtocolListModel::itemForIndex(const QModelIndex &index) const
{
    return m_protocolItems.at(index.row());
}

#include "protocol-list-model.moc"

<|endoftext|>