<commit_before>
#include "include/types.h"
#include "msg/Message.h"
#include "osd/OSD.h"
#include "messages/MClass.h"
#include "ClassHandler.h"
#include "common/arch.h"

#include <dlfcn.h>

#include <map>

#include "config.h"

#define DOUT_SUBSYS osd
#undef dout_prefix
#define dout_prefix *_dout << dbeginl

static ClassHandler::ClassData null_cls_data;

void ClassHandler::_load_class(ClassData &cls)
{
  dout(10) << "load_class " << cls.name << dendl;

  char *fname=strdup("/tmp/class-XXXXXX");
  int fd = mkstemp(fname);
  cls_deps_t *(*cls_deps)();

  for (list<bufferptr>::const_iterator it = cls.impl.binary.buffers().begin();
       it != cls.impl.binary.buffers().end(); it++)
    write(fd, it->c_str(), it->length());

  close(fd);

  cls.handle = dlopen(fname, RTLD_NOW);

  if (!cls.handle) {
    dout(0) << "could not open class (dlopen failed)" << dendl;
    goto done;
  }
  cls_deps = (cls_deps_t *(*)())dlsym(cls.handle, "class_deps");
  if (cls_deps) {
    cls_deps_t *deps = cls_deps();
    while (deps) {
      if (!deps->name)
        break;
      cls._add_dependency(deps);
      deps++;
    }
  }
  cls.load();
done:
  unlink(fname);
  free(fname);

  return;
}

void ClassHandler::load_class(const string& cname)
{
  ClassData& cls = get_obj(cname);
  if (&cls == &null_cls_data) {
    dout(0) << "ERROR: can't load null class data" << dendl;
    return;
  }
  cls.mutex->Lock();
  _load_class(cls);
  cls.mutex->Unlock();
}

ClassHandler::ClassData& ClassHandler::get_obj(const string& cname)
{
  Mutex::Locker locker(mutex);
  map<string, ClassData>::iterator iter = classes.find(cname);
  if (iter == classes.end()) {
    ClassData& cls = classes[cname];
    cls.mutex = new Mutex("ClassData");
    if (!cls.mutex) {
      classes[cname] = null_cls_data;
      return null_cls_data;
    }
    dout(0) << "get_obj: adding new class name=" << cname << " ptr=" << &cls << dendl;
    cls.name = cname;
    cls.osd = osd;
    cls.handler = this;
    return cls;
  }

  return iter->second;
}

ClassHandler::ClassData *ClassHandler::get_class(const string& cname, ClassVersion& version)
{
  ClassHandler::ClassData *ret = NULL;
  ClassData *class_data = &get_obj(cname);
  if (class_data == &null_cls_data)
    return NULL;

  Mutex::Locker lock(*class_data->mutex);

  switch (class_data->status) {
  case ClassData::CLASS_INVALID:
  case ClassData::CLASS_LOADED:
    if (class_data->cache_timed_out()) {
      dout(0) << "class timed out going to send request for " << cname.c_str() << " v" << version << dendl;
      ret = class_data;
      goto send;
    }
    return class_data;
  case ClassData::CLASS_REQUESTED:
    return NULL;
    break;

  case ClassData::CLASS_UNKNOWN:
    class_data->set_status(ClassData::CLASS_REQUESTED);
    break;

  default:
    assert(0);
  }

  class_data->version = version;
send:
  osd->send_class_request(cname.c_str(), version);
  return ret;
}

void ClassHandler::handle_class(MClass *m)
{
  deque<ClassInfo>::iterator info_iter;
  deque<ClassImpl>::iterator impl_iter;
  deque<bool>::iterator add_iter;

  for (info_iter = m->info.begin(), add_iter = m->add.begin(), impl_iter = m->impl.begin();
       info_iter != m->info.end();
       ++info_iter, ++add_iter) {
    ClassData& data = get_obj(info_iter->name);
    if (&data == &null_cls_data) {
      dout(0) << "couldn't get class, out of memory? continuing" << dendl;
      continue;
    }
    dout(0) << "handle_class " << info_iter->name << dendl;
    data.mutex->Lock();

    if (*add_iter) {
        data.set_status(ClassData::CLASS_REQUESTED);
	dout(10) << "added class '" << info_iter->name << "'" << dendl;
	data.impl = *impl_iter;
	++impl_iter;
	_load_class(data);
    } else {
	dout(10) << "response of an invalid class '" << info_iter->name << "'" << dendl;
        data.set_status(ClassData::CLASS_INVALID);
        osd->got_class(info_iter->name);
    }
    data.mutex->Unlock();
  }
}

void ClassHandler::resend_class_requests()
{
  for (map<string,ClassData>::iterator p = classes.begin(); p != classes.end(); p++) {
    dout(20) << "resending class request "<< p->first.c_str() << " v" << p->second.version << dendl;
    osd->send_class_request(p->first.c_str(), p->second.version);
  }
}

ClassHandler::ClassData *ClassHandler::register_class(const char *cname)
{
  ClassData& class_data = get_obj(cname);

  if (&class_data == &null_cls_data) {
    dout(0) << "couldn't get class object, out of memory?" << dendl;
    return NULL;
  }

  dout(0) << "&class_data=" << (void *)&class_data << " status=" << class_data.status << dendl;

  if (class_data.status != ClassData::CLASS_LOADED) {
    dout(0) << "class " << cname << " can't be loaded" << dendl;
    return NULL;
  }

  if (class_data.registered) {
    dout(0) << "class " << cname << " already registered" << dendl;
  }

  class_data.registered = true;

  return &class_data;
}

void ClassHandler::unregister_class(ClassHandler::ClassData *cls)
{
  /* FIXME: do we really need this one? */
}

void ClassHandler::ClassData::load()
{
  if (status == CLASS_INVALID) {
    /* if we're invalid, we should just notify osd */
    osd->got_class(name);
    return;
  }

  if (!has_missing_deps()) {
    set_status(CLASS_LOADED);
    dout(0) << "setting class " << name << " status to CLASS_LOADED" << dendl;
    init();
    osd->got_class(name);
  }

  list<ClassData *>::iterator iter;
  for (iter = dependents.begin(); iter != dependents.end(); ++iter) {
    ClassData *cls = *iter;
    cls->satisfy_dependency(this);
  }
}

void ClassHandler::ClassData::init()
{
  void (*cls_init)() = (void (*)())dlsym(handle, "__cls_init");

  if (cls_init)
    cls_init();
}

bool ClassHandler::ClassData::_add_dependency(cls_deps_t *dep)
{
  if (!dep->name)
    return false;

  ClassData& cls_dep = handler->get_obj(dep->name);
  if (&cls_dep == &null_cls_data) {
    dout(0) << "couldn't get class dep object, out of memory?" << dendl;
    return false;
  }
  map<string, ClassData *>::iterator iter = missing_dependencies.find(dep->name);
  dependencies[dep->name] = &cls_dep;
  dout(0) << "adding dependency " << dep->name << dendl;

  if (cls_dep.status != CLASS_LOADED) {
    missing_dependencies[dep->name] = &cls_dep;

    if(cls_dep.status == CLASS_UNKNOWN) {
      ClassVersion version;
      version.set_arch(get_arch());
      handler->get_class(dep->name, version);
    }
    dout(0) << "adding missing dependency " << dep->name << dendl;
  }
  cls_dep._add_dependent(*this);

  if (cls_dep.status == CLASS_INVALID) {
    dout(0) << "ouch! depending on invalid class" << dendl;
    set_status(CLASS_INVALID); /* we have an invalid dependency, we're invalid */
  }

  return true;
}

void ClassHandler::ClassData::satisfy_dependency(ClassData *cls)
{
  Mutex::Locker lock(*mutex);
  map<string, ClassData *>::iterator iter = missing_dependencies.find(cls->name);

  if (iter != missing_dependencies.end()) {
    dout(0) << "satisfied dependency name=" << name << " dep=" << cls->name << dendl;
    missing_dependencies.erase(iter);
    if (missing_dependencies.size() == 0) {
      dout(0) << "all dependencies are satisfied! initializing, notifying osd" << dendl;
      set_status(CLASS_LOADED);
  dout(0) << "this=" << (void *)this << " status=" << status << dendl;
      init();
      osd->got_class(name);
    }
  }
}

void ClassHandler::ClassData::_add_dependent(ClassData& dependent)
{
  Mutex::Locker lock(*mutex);
  dout(0) << "class " << name << " has dependet: " << dependent.name << dendl;
  dependents.push_back(&dependent);
}

ClassHandler::ClassMethod *ClassHandler::ClassData::register_method(const char *mname,
                                                                    int flags,
								    cls_method_call_t func)
{
  /* no need for locking, called under the class_init mutex */
  ClassMethod& method = methods_map[mname];
  method.func = func;
  method.name = mname;
  method.flags = flags;
  method.cls = this;

  return &method;
}

ClassHandler::ClassMethod *ClassHandler::ClassData::register_cxx_method(const char *mname,
                                                                        int flags,
									cls_method_cxx_call_t func)
{
  /* no need for locking, called under the class_init mutex */
  ClassMethod& method = methods_map[mname];
  method.cxx_func = func;
  method.name = mname;
  method.flags = flags;
  method.cls = this;

  return &method;
}

ClassHandler::ClassMethod *ClassHandler::ClassData::get_method(const char *mname)
{
  Mutex::Locker lock(*mutex);
  map<string, ClassHandler::ClassMethod>::iterator iter = methods_map.find(mname);

  if (iter == methods_map.end())
    return NULL;

  return &(iter->second);
}

void ClassHandler::ClassData::unregister_method(ClassHandler::ClassMethod *method)
{
 /* no need for locking, called under the class_init mutex */
   map<string, ClassMethod>::iterator iter;

   iter = methods_map.find(method->name);
   if (iter == methods_map.end())
     return;

   methods_map.erase(iter);
}

void ClassHandler::ClassData::set_status(ClassHandler::ClassData::Status _status)
{
  status = _status;

  switch (status) {
    case CLASS_INVALID:
    case CLASS_LOADED:
      set_timeout();
      break;
    default:
      break;
  }
}

void ClassHandler::ClassData::set_timeout()
{
  timeout = g_clock.now();
  timeout += g_conf.osd_class_timeout;
}

bool ClassHandler::ClassData::cache_timed_out()
{
  dout(0) << "timeout=" << timeout << " g_clock.now()=" << g_clock.now() << dendl;
  return (timeout && g_clock.now() >= timeout);
}

void ClassHandler::ClassMethod::unregister()
{
  cls->unregister_method(this);
}

int ClassHandler::ClassMethod::exec(cls_method_context_t ctx, bufferlist& indata, bufferlist& outdata)
{
  int ret;
  if (cxx_func) {
    // C++ call version
    ret = cxx_func(ctx, &indata, &outdata);
  } else {
    // C version
    char *out = NULL;
    int olen = 0;
    ret = func(ctx, indata.c_str(), indata.length(), &out, &olen);
    if (out) {
      // assume *out was allocated via cls_alloc (which calls malloc!)
      buffer::ptr bp = buffer::claim_malloc(olen, out);
      outdata.push_back(bp);
    }
  }
  return ret;
}

int ClassHandler::get_method_flags(const string& cname, const string& mname)
{
  ClassData& cls = get_obj(cname);
  if (&cls == &null_cls_data)
    return 0;

  ClassMethod *method = cls.get_method(mname.c_str());
  if (!method)
    return 0;

  return method->flags;
}
<commit_msg>classhandler: print error when dlopen fails<commit_after>
#include "include/types.h"
#include "msg/Message.h"
#include "osd/OSD.h"
#include "messages/MClass.h"
#include "ClassHandler.h"
#include "common/arch.h"

#include <dlfcn.h>

#include <map>

#include "config.h"

#define DOUT_SUBSYS osd
#undef dout_prefix
#define dout_prefix *_dout << dbeginl

static ClassHandler::ClassData null_cls_data;

void ClassHandler::_load_class(ClassData &cls)
{
  dout(10) << "load_class " << cls.name << dendl;

  char *fname=strdup("/tmp/class-XXXXXX");
  int fd = mkstemp(fname);
  cls_deps_t *(*cls_deps)();

  for (list<bufferptr>::const_iterator it = cls.impl.binary.buffers().begin();
       it != cls.impl.binary.buffers().end(); it++)
    write(fd, it->c_str(), it->length());

  close(fd);

  cls.handle = dlopen(fname, RTLD_NOW);

  if (!cls.handle) {
    char buf[80];
    dout(0) << "could not open class (dlopen failed) " << strerror_r(errno, buf, sizeof(buf)) << dendl;
    goto done;
  }
  cls_deps = (cls_deps_t *(*)())dlsym(cls.handle, "class_deps");
  if (cls_deps) {
    cls_deps_t *deps = cls_deps();
    while (deps) {
      if (!deps->name)
        break;
      cls._add_dependency(deps);
      deps++;
    }
  }
  cls.load();
done:
  unlink(fname);
  free(fname);

  return;
}

void ClassHandler::load_class(const string& cname)
{
  ClassData& cls = get_obj(cname);
  if (&cls == &null_cls_data) {
    dout(0) << "ERROR: can't load null class data" << dendl;
    return;
  }
  cls.mutex->Lock();
  _load_class(cls);
  cls.mutex->Unlock();
}

ClassHandler::ClassData& ClassHandler::get_obj(const string& cname)
{
  Mutex::Locker locker(mutex);
  map<string, ClassData>::iterator iter = classes.find(cname);
  if (iter == classes.end()) {
    ClassData& cls = classes[cname];
    cls.mutex = new Mutex("ClassData");
    if (!cls.mutex) {
      classes[cname] = null_cls_data;
      return null_cls_data;
    }
    dout(0) << "get_obj: adding new class name=" << cname << " ptr=" << &cls << dendl;
    cls.name = cname;
    cls.osd = osd;
    cls.handler = this;
    return cls;
  }

  return iter->second;
}

ClassHandler::ClassData *ClassHandler::get_class(const string& cname, ClassVersion& version)
{
  ClassHandler::ClassData *ret = NULL;
  ClassData *class_data = &get_obj(cname);
  if (class_data == &null_cls_data)
    return NULL;

  Mutex::Locker lock(*class_data->mutex);

  switch (class_data->status) {
  case ClassData::CLASS_INVALID:
  case ClassData::CLASS_LOADED:
    if (class_data->cache_timed_out()) {
      dout(0) << "class timed out going to send request for " << cname.c_str() << " v" << version << dendl;
      ret = class_data;
      goto send;
    }
    return class_data;
  case ClassData::CLASS_REQUESTED:
    return NULL;
    break;

  case ClassData::CLASS_UNKNOWN:
    class_data->set_status(ClassData::CLASS_REQUESTED);
    break;

  default:
    assert(0);
  }

  class_data->version = version;
send:
  osd->send_class_request(cname.c_str(), version);
  return ret;
}

void ClassHandler::handle_class(MClass *m)
{
  deque<ClassInfo>::iterator info_iter;
  deque<ClassImpl>::iterator impl_iter;
  deque<bool>::iterator add_iter;

  for (info_iter = m->info.begin(), add_iter = m->add.begin(), impl_iter = m->impl.begin();
       info_iter != m->info.end();
       ++info_iter, ++add_iter) {
    ClassData& data = get_obj(info_iter->name);
    if (&data == &null_cls_data) {
      dout(0) << "couldn't get class, out of memory? continuing" << dendl;
      continue;
    }
    dout(0) << "handle_class " << info_iter->name << dendl;
    data.mutex->Lock();

    if (*add_iter) {
        data.set_status(ClassData::CLASS_REQUESTED);
	dout(10) << "added class '" << info_iter->name << "'" << dendl;
	data.impl = *impl_iter;
	++impl_iter;
	_load_class(data);
    } else {
	dout(10) << "response of an invalid class '" << info_iter->name << "'" << dendl;
        data.set_status(ClassData::CLASS_INVALID);
        osd->got_class(info_iter->name);
    }
    data.mutex->Unlock();
  }
}

void ClassHandler::resend_class_requests()
{
  for (map<string,ClassData>::iterator p = classes.begin(); p != classes.end(); p++) {
    dout(20) << "resending class request "<< p->first.c_str() << " v" << p->second.version << dendl;
    osd->send_class_request(p->first.c_str(), p->second.version);
  }
}

ClassHandler::ClassData *ClassHandler::register_class(const char *cname)
{
  ClassData& class_data = get_obj(cname);

  if (&class_data == &null_cls_data) {
    dout(0) << "couldn't get class object, out of memory?" << dendl;
    return NULL;
  }

  dout(0) << "&class_data=" << (void *)&class_data << " status=" << class_data.status << dendl;

  if (class_data.status != ClassData::CLASS_LOADED) {
    dout(0) << "class " << cname << " can't be loaded" << dendl;
    return NULL;
  }

  if (class_data.registered) {
    dout(0) << "class " << cname << " already registered" << dendl;
  }

  class_data.registered = true;

  return &class_data;
}

void ClassHandler::unregister_class(ClassHandler::ClassData *cls)
{
  /* FIXME: do we really need this one? */
}

void ClassHandler::ClassData::load()
{
  if (status == CLASS_INVALID) {
    /* if we're invalid, we should just notify osd */
    osd->got_class(name);
    return;
  }

  if (!has_missing_deps()) {
    set_status(CLASS_LOADED);
    dout(0) << "setting class " << name << " status to CLASS_LOADED" << dendl;
    init();
    osd->got_class(name);
  }

  list<ClassData *>::iterator iter;
  for (iter = dependents.begin(); iter != dependents.end(); ++iter) {
    ClassData *cls = *iter;
    cls->satisfy_dependency(this);
  }
}

void ClassHandler::ClassData::init()
{
  void (*cls_init)() = (void (*)())dlsym(handle, "__cls_init");

  if (cls_init)
    cls_init();
}

bool ClassHandler::ClassData::_add_dependency(cls_deps_t *dep)
{
  if (!dep->name)
    return false;

  ClassData& cls_dep = handler->get_obj(dep->name);
  if (&cls_dep == &null_cls_data) {
    dout(0) << "couldn't get class dep object, out of memory?" << dendl;
    return false;
  }
  map<string, ClassData *>::iterator iter = missing_dependencies.find(dep->name);
  dependencies[dep->name] = &cls_dep;
  dout(0) << "adding dependency " << dep->name << dendl;

  if (cls_dep.status != CLASS_LOADED) {
    missing_dependencies[dep->name] = &cls_dep;

    if(cls_dep.status == CLASS_UNKNOWN) {
      ClassVersion version;
      version.set_arch(get_arch());
      handler->get_class(dep->name, version);
    }
    dout(0) << "adding missing dependency " << dep->name << dendl;
  }
  cls_dep._add_dependent(*this);

  if (cls_dep.status == CLASS_INVALID) {
    dout(0) << "ouch! depending on invalid class" << dendl;
    set_status(CLASS_INVALID); /* we have an invalid dependency, we're invalid */
  }

  return true;
}

void ClassHandler::ClassData::satisfy_dependency(ClassData *cls)
{
  Mutex::Locker lock(*mutex);
  map<string, ClassData *>::iterator iter = missing_dependencies.find(cls->name);

  if (iter != missing_dependencies.end()) {
    dout(0) << "satisfied dependency name=" << name << " dep=" << cls->name << dendl;
    missing_dependencies.erase(iter);
    if (missing_dependencies.size() == 0) {
      dout(0) << "all dependencies are satisfied! initializing, notifying osd" << dendl;
      set_status(CLASS_LOADED);
  dout(0) << "this=" << (void *)this << " status=" << status << dendl;
      init();
      osd->got_class(name);
    }
  }
}

void ClassHandler::ClassData::_add_dependent(ClassData& dependent)
{
  Mutex::Locker lock(*mutex);
  dout(0) << "class " << name << " has dependet: " << dependent.name << dendl;
  dependents.push_back(&dependent);
}

ClassHandler::ClassMethod *ClassHandler::ClassData::register_method(const char *mname,
                                                                    int flags,
								    cls_method_call_t func)
{
  /* no need for locking, called under the class_init mutex */
  ClassMethod& method = methods_map[mname];
  method.func = func;
  method.name = mname;
  method.flags = flags;
  method.cls = this;

  return &method;
}

ClassHandler::ClassMethod *ClassHandler::ClassData::register_cxx_method(const char *mname,
                                                                        int flags,
									cls_method_cxx_call_t func)
{
  /* no need for locking, called under the class_init mutex */
  ClassMethod& method = methods_map[mname];
  method.cxx_func = func;
  method.name = mname;
  method.flags = flags;
  method.cls = this;

  return &method;
}

ClassHandler::ClassMethod *ClassHandler::ClassData::get_method(const char *mname)
{
  Mutex::Locker lock(*mutex);
  map<string, ClassHandler::ClassMethod>::iterator iter = methods_map.find(mname);

  if (iter == methods_map.end())
    return NULL;

  return &(iter->second);
}

void ClassHandler::ClassData::unregister_method(ClassHandler::ClassMethod *method)
{
 /* no need for locking, called under the class_init mutex */
   map<string, ClassMethod>::iterator iter;

   iter = methods_map.find(method->name);
   if (iter == methods_map.end())
     return;

   methods_map.erase(iter);
}

void ClassHandler::ClassData::set_status(ClassHandler::ClassData::Status _status)
{
  status = _status;

  switch (status) {
    case CLASS_INVALID:
    case CLASS_LOADED:
      set_timeout();
      break;
    default:
      break;
  }
}

void ClassHandler::ClassData::set_timeout()
{
  timeout = g_clock.now();
  timeout += g_conf.osd_class_timeout;
}

bool ClassHandler::ClassData::cache_timed_out()
{
  dout(0) << "timeout=" << timeout << " g_clock.now()=" << g_clock.now() << dendl;
  return (timeout && g_clock.now() >= timeout);
}

void ClassHandler::ClassMethod::unregister()
{
  cls->unregister_method(this);
}

int ClassHandler::ClassMethod::exec(cls_method_context_t ctx, bufferlist& indata, bufferlist& outdata)
{
  int ret;
  if (cxx_func) {
    // C++ call version
    ret = cxx_func(ctx, &indata, &outdata);
  } else {
    // C version
    char *out = NULL;
    int olen = 0;
    ret = func(ctx, indata.c_str(), indata.length(), &out, &olen);
    if (out) {
      // assume *out was allocated via cls_alloc (which calls malloc!)
      buffer::ptr bp = buffer::claim_malloc(olen, out);
      outdata.push_back(bp);
    }
  }
  return ret;
}

int ClassHandler::get_method_flags(const string& cname, const string& mname)
{
  ClassData& cls = get_obj(cname);
  if (&cls == &null_cls_data)
    return 0;

  ClassMethod *method = cls.get_method(mname.c_str());
  if (!method)
    return 0;

  return method->flags;
}
<|endoftext|>


<commit_before>// -*- c++ -*-

/*

 Copyright 2019-2022 Alain Dargelas

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

/*
 * File:   SynthSubset.cpp
 * Author: alaindargelas
 *
 * Created on Feb 16, 2022, 9:03 PM
 */
#include <string.h>
#include <uhdm/SynthSubset.h>
#include <uhdm/clone_tree.h>
#include <uhdm/uhdm.h>
#include <uhdm/vpi_visitor.h>

namespace UHDM {

SynthSubset::SynthSubset(Serializer* serializer,
                         std::set<const any*>& nonSynthesizableObjects,
                         bool reportErrors)
    : serializer_(serializer),
      nonSynthesizableObjects_(nonSynthesizableObjects),
      reportErrors_(reportErrors) {
  const std::string kDollar("$");
  for (auto s :
       {// "display",
        "write", "strobe", "monitor", "monitoron", "monitoroff", "displayb",
        "writeb", "strobeb", "monitorb", "displayo", "writeo", "strobeo",
        "monitoro", "displayh", "writeh", "strobeh", "monitorh", "fopen",
        "fclose", "frewind", "fflush", "fseek", "ftell", "fdisplay", "fwrite",
        "swrite", "fstrobe", "fmonitor", "fread", "fscanf", "fdisplayb",
        "fwriteb", "swriteb", "fstrobeb", "fmonitorb", "fdisplayo", "fwriteo",
        "swriteo", "fstrobeo", "fmonitoro", "fdisplayh", "fwriteh", "swriteh",
        "fstrobeh", "fmonitorh", "sscanf", "sdf_annotate", "sformat",
        // "cast",
        "assertkill", "assertoff", "asserton",
        // "bits",
        // "bitstoshortreal",
        "countones", "coverage_control", "coverage_merge", "coverage_save",
        // "dimensions",
        // "error",
        "exit",
        // "fatal",
        "fell", "get_coverage", "coverage_get", "coverage_get_max",
        // "high",
        //"increment",
        "info", "isunbounded", "isunknown",
        // "left",
        "load_coverage_db",
        // "low",
        "onehot", "past",
        // "readmemb",
        // "readmemh",
        //"right",
        "root", "rose", "sampled", "set_coverage_db_name",
        // "shortrealtobits",
        // "size",
        "stable",
        // "typename",
        // "typeof",
        "unit", "urandom", "srandom", "urandom_range", "set_randstate",
        "get_randstate", "dist_uniform", "dist_normal", "dist_exponential",
        "dist_poisson", "dist_chi_square", "dist_t", "dist_erlang",
        // "warning",
        // "writememb",
        // "writememh",
        "value$plusargs"}) {
    nonSynthSysCalls_.emplace(kDollar + s);
  }
}

void SynthSubset::report(std::ostream& out) {
  for (auto object : nonSynthesizableObjects_) {
    VisitedContainer visited;

    vpiHandle dh =
        object->GetSerializer()->MakeUhdmHandle(object->UhdmType(), object);

    visit_object(dh, 0, "", &visited, out, true);
    vpi_release_handle(dh);
  }
}

void SynthSubset::reportError(const any* object) {
  const any* tmp = object;
  while (tmp && tmp->VpiFile().empty()) {
    tmp = tmp->VpiParent();
  }
  if (tmp) object = tmp;
  if (reportErrors_ && !reportedParent(object)) {
    if (!object->VpiFile().empty())
      serializer_->GetErrorHandler()(ErrorType::UHDM_NON_SYNTHESIZABLE,
                                     object->VpiName(), object, nullptr);
  }
  mark(object);
}

void SynthSubset::leaveAny(const any* object, vpiHandle handle) {
  switch (object->UhdmType()) {
    case uhdmfinal_stmt:
    case uhdmdelay_control:
    case uhdmdelay_term:
    case uhdmthread_obj:
    case uhdmwait_stmt:
    case uhdmwait_fork:
    case uhdmordered_wait:
    case uhdmdisable:
    case uhdmdisable_fork:
    case uhdmforce:
    case uhdmdeassign:
    case uhdmrelease:
    case uhdmexpect_stmt:
    case uhdmcover:
    case uhdmassume:
    case uhdmrestrict:
    case uhdmimmediate_assume:
    case uhdmimmediate_cover:
    case uhdmsequence_inst:
    case uhdmseq_formal_decl:
    case uhdmsequence_decl:
    case uhdmprop_formal_decl:
    case uhdmproperty_inst:
    case uhdmproperty_spec:
    case uhdmproperty_decl:
    case uhdmclocked_property:
    case uhdmcase_property_item:
    case uhdmcase_property:
    case uhdmmulticlock_sequence_expr:
    case uhdmclocked_seq:
    case uhdmreal_var:
    case uhdmtime_var:
    case uhdmchandle_var:
    case uhdmchecker_port:
    case uhdmchecker_inst_port:
    case uhdmswitch_tran:
    case uhdmudp:
    case uhdmmod_path:
    case uhdmtchk:
    case uhdmudp_defn:
    case uhdmtable_entry:
    case uhdmclocking_block:
    case uhdmclocking_io_decl:
    case uhdmprogram_array:
    case uhdmswitch_array:
    case uhdmudp_array:
    case uhdmtchk_term:
    case uhdmtime_net:
    case uhdmnamed_event:
    case uhdmvirtual_interface_var:
    case uhdmextends:
    case uhdmclass_defn:
    case uhdmclass_obj:
    case uhdmprogram:
    case uhdmchecker_decl:
    case uhdmchecker_inst:
    case uhdmshort_real_typespec:
    case uhdmreal_typespec:
    case uhdmtime_typespec:
    case uhdmchandle_typespec:
    case uhdmsequence_typespec:
    case uhdmproperty_typespec:
    case uhdmuser_systf:
    case uhdmmethod_func_call:
    case uhdmmethod_task_call:
    case uhdmconstraint_ordering:
    case uhdmconstraint:
    case uhdmdistribution:
    case uhdmdist_item:
    case uhdmimplication:
    case uhdmconstr_if:
    case uhdmconstr_if_else:
    case uhdmconstr_foreach:
    case uhdmsoft_disable:
    case uhdmfork_stmt:
    case uhdmnamed_fork:
    case uhdmevent_stmt:
    case uhdmevent_typespec:
      reportError(object);
      break;
    default:
      break;
  }
}

void SynthSubset::leaveTask(const task* topobject, vpiHandle handle) {
  // Give specific error for non-synthesizable tasks
  std::function<void(const any*, const any*)> inst_visit =
      [&inst_visit, this](const any* stmt, const any* top) {
        UHDM_OBJECT_TYPE type = stmt->UhdmType();
        UHDM::VectorOfany* stmts = nullptr;
        if (type == uhdmbegin) {
          begin* b = (begin*)stmt;
          stmts = b->Stmts();
        } else if (type == uhdmnamed_begin) {
          named_begin* b = (named_begin*)stmt;
          stmts = b->Stmts();
        }
        if (stmts) {
          for (auto st : *stmts) {
            UHDM_OBJECT_TYPE sttype = st->UhdmType();
            switch (sttype) {
              case uhdmwait_stmt:
              case uhdmwait_fork:
              case uhdmordered_wait:
              case uhdmdisable:
              case uhdmdisable_fork:
              case uhdmforce:
              case uhdmdeassign:
              case uhdmrelease:
              case uhdmsoft_disable:
              case uhdmfork_stmt:
              case uhdmnamed_fork:
              case uhdmevent_stmt: {
                reportError(top);
              }
              default: {
              }
            }
            inst_visit(st, top);
          }
        }
      };

  if (const any* stmt = topobject->Stmt()) {
    inst_visit(stmt, topobject);
  }
}

void SynthSubset::leaveSys_task_call(const sys_task_call* object,
                                     vpiHandle handle) {
  const std::string& name = object->VpiName();
  if (nonSynthSysCalls_.find(name) != nonSynthSysCalls_.end()) {
    reportError(object);
  }
}

void SynthSubset::leaveSys_func_call(const sys_func_call* object,
                                     vpiHandle handle) {
  const std::string& name = object->VpiName();
  if (nonSynthSysCalls_.find(name) != nonSynthSysCalls_.end()) {
    reportError(object);
  }
}

void SynthSubset::leaveClass_typespec(const class_typespec* object,
                                      vpiHandle handle) {
  if (const any* def = object->Class_defn())
    reportError(def);
  else
    reportError(object);
}

void SynthSubset::leaveClass_var(const class_var* object, vpiHandle handle) {
  if (const class_typespec* spec = (class_typespec*)object->Typespec()) {
    if (const class_defn* def = spec->Class_defn()) {
      if (reportedParent(def)) {
        mark(object);
        return;
      }
    }
  }
  reportError(object);
}

void SynthSubset::mark(const any* object) {
  nonSynthesizableObjects_.insert(object);
}

bool SynthSubset::reportedParent(const any* object) {
  if (object->UhdmType() == uhdmpackage) {
    if (object->VpiName() == "builtin") return true;
  } else if (object->UhdmType() == uhdmclass_defn) {
    if (object->VpiName() == "work@semaphore" ||
        object->VpiName() == "work@process" ||
        object->VpiName() == "work@mailbox")
      return true;
  }
  if (nonSynthesizableObjects_.find(object) != nonSynthesizableObjects_.end()) {
    return true;
  }
  if (const any* parent = object->VpiParent()) {
    return reportedParent(parent);
  }
  return false;
}

}  // namespace UHDM
<commit_msg>Add missing break in switch statement.<commit_after>// -*- c++ -*-

/*

 Copyright 2019-2022 Alain Dargelas

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

 http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 */

/*
 * File:   SynthSubset.cpp
 * Author: alaindargelas
 *
 * Created on Feb 16, 2022, 9:03 PM
 */
#include <string.h>
#include <uhdm/SynthSubset.h>
#include <uhdm/clone_tree.h>
#include <uhdm/uhdm.h>
#include <uhdm/vpi_visitor.h>

namespace UHDM {

SynthSubset::SynthSubset(Serializer* serializer,
                         std::set<const any*>& nonSynthesizableObjects,
                         bool reportErrors)
    : serializer_(serializer),
      nonSynthesizableObjects_(nonSynthesizableObjects),
      reportErrors_(reportErrors) {
  const std::string kDollar("$");
  for (auto s :
       {// "display",
        "write", "strobe", "monitor", "monitoron", "monitoroff", "displayb",
        "writeb", "strobeb", "monitorb", "displayo", "writeo", "strobeo",
        "monitoro", "displayh", "writeh", "strobeh", "monitorh", "fopen",
        "fclose", "frewind", "fflush", "fseek", "ftell", "fdisplay", "fwrite",
        "swrite", "fstrobe", "fmonitor", "fread", "fscanf", "fdisplayb",
        "fwriteb", "swriteb", "fstrobeb", "fmonitorb", "fdisplayo", "fwriteo",
        "swriteo", "fstrobeo", "fmonitoro", "fdisplayh", "fwriteh", "swriteh",
        "fstrobeh", "fmonitorh", "sscanf", "sdf_annotate", "sformat",
        // "cast",
        "assertkill", "assertoff", "asserton",
        // "bits",
        // "bitstoshortreal",
        "countones", "coverage_control", "coverage_merge", "coverage_save",
        // "dimensions",
        // "error",
        "exit",
        // "fatal",
        "fell", "get_coverage", "coverage_get", "coverage_get_max",
        // "high",
        //"increment",
        "info", "isunbounded", "isunknown",
        // "left",
        "load_coverage_db",
        // "low",
        "onehot", "past",
        // "readmemb",
        // "readmemh",
        //"right",
        "root", "rose", "sampled", "set_coverage_db_name",
        // "shortrealtobits",
        // "size",
        "stable",
        // "typename",
        // "typeof",
        "unit", "urandom", "srandom", "urandom_range", "set_randstate",
        "get_randstate", "dist_uniform", "dist_normal", "dist_exponential",
        "dist_poisson", "dist_chi_square", "dist_t", "dist_erlang",
        // "warning",
        // "writememb",
        // "writememh",
        "value$plusargs"}) {
    nonSynthSysCalls_.emplace(kDollar + s);
  }
}

void SynthSubset::report(std::ostream& out) {
  for (auto object : nonSynthesizableObjects_) {
    VisitedContainer visited;

    vpiHandle dh =
        object->GetSerializer()->MakeUhdmHandle(object->UhdmType(), object);

    visit_object(dh, 0, "", &visited, out, true);
    vpi_release_handle(dh);
  }
}

void SynthSubset::reportError(const any* object) {
  const any* tmp = object;
  while (tmp && tmp->VpiFile().empty()) {
    tmp = tmp->VpiParent();
  }
  if (tmp) object = tmp;
  if (reportErrors_ && !reportedParent(object)) {
    if (!object->VpiFile().empty())
      serializer_->GetErrorHandler()(ErrorType::UHDM_NON_SYNTHESIZABLE,
                                     object->VpiName(), object, nullptr);
  }
  mark(object);
}

void SynthSubset::leaveAny(const any* object, vpiHandle handle) {
  switch (object->UhdmType()) {
    case uhdmfinal_stmt:
    case uhdmdelay_control:
    case uhdmdelay_term:
    case uhdmthread_obj:
    case uhdmwait_stmt:
    case uhdmwait_fork:
    case uhdmordered_wait:
    case uhdmdisable:
    case uhdmdisable_fork:
    case uhdmforce:
    case uhdmdeassign:
    case uhdmrelease:
    case uhdmexpect_stmt:
    case uhdmcover:
    case uhdmassume:
    case uhdmrestrict:
    case uhdmimmediate_assume:
    case uhdmimmediate_cover:
    case uhdmsequence_inst:
    case uhdmseq_formal_decl:
    case uhdmsequence_decl:
    case uhdmprop_formal_decl:
    case uhdmproperty_inst:
    case uhdmproperty_spec:
    case uhdmproperty_decl:
    case uhdmclocked_property:
    case uhdmcase_property_item:
    case uhdmcase_property:
    case uhdmmulticlock_sequence_expr:
    case uhdmclocked_seq:
    case uhdmreal_var:
    case uhdmtime_var:
    case uhdmchandle_var:
    case uhdmchecker_port:
    case uhdmchecker_inst_port:
    case uhdmswitch_tran:
    case uhdmudp:
    case uhdmmod_path:
    case uhdmtchk:
    case uhdmudp_defn:
    case uhdmtable_entry:
    case uhdmclocking_block:
    case uhdmclocking_io_decl:
    case uhdmprogram_array:
    case uhdmswitch_array:
    case uhdmudp_array:
    case uhdmtchk_term:
    case uhdmtime_net:
    case uhdmnamed_event:
    case uhdmvirtual_interface_var:
    case uhdmextends:
    case uhdmclass_defn:
    case uhdmclass_obj:
    case uhdmprogram:
    case uhdmchecker_decl:
    case uhdmchecker_inst:
    case uhdmshort_real_typespec:
    case uhdmreal_typespec:
    case uhdmtime_typespec:
    case uhdmchandle_typespec:
    case uhdmsequence_typespec:
    case uhdmproperty_typespec:
    case uhdmuser_systf:
    case uhdmmethod_func_call:
    case uhdmmethod_task_call:
    case uhdmconstraint_ordering:
    case uhdmconstraint:
    case uhdmdistribution:
    case uhdmdist_item:
    case uhdmimplication:
    case uhdmconstr_if:
    case uhdmconstr_if_else:
    case uhdmconstr_foreach:
    case uhdmsoft_disable:
    case uhdmfork_stmt:
    case uhdmnamed_fork:
    case uhdmevent_stmt:
    case uhdmevent_typespec:
      reportError(object);
      break;
    default:
      break;
  }
}

void SynthSubset::leaveTask(const task* topobject, vpiHandle handle) {
  // Give specific error for non-synthesizable tasks
  std::function<void(const any*, const any*)> inst_visit =
      [&inst_visit, this](const any* stmt, const any* top) {
        UHDM_OBJECT_TYPE type = stmt->UhdmType();
        UHDM::VectorOfany* stmts = nullptr;
        if (type == uhdmbegin) {
          begin* b = (begin*)stmt;
          stmts = b->Stmts();
        } else if (type == uhdmnamed_begin) {
          named_begin* b = (named_begin*)stmt;
          stmts = b->Stmts();
        }
        if (stmts) {
          for (auto st : *stmts) {
            UHDM_OBJECT_TYPE sttype = st->UhdmType();
            switch (sttype) {
              case uhdmwait_stmt:
              case uhdmwait_fork:
              case uhdmordered_wait:
              case uhdmdisable:
              case uhdmdisable_fork:
              case uhdmforce:
              case uhdmdeassign:
              case uhdmrelease:
              case uhdmsoft_disable:
              case uhdmfork_stmt:
              case uhdmnamed_fork:
              case uhdmevent_stmt: {
                reportError(top);
                break;
              }
              default: {
              }
            }
            inst_visit(st, top);
          }
        }
      };

  if (const any* stmt = topobject->Stmt()) {
    inst_visit(stmt, topobject);
  }
}

void SynthSubset::leaveSys_task_call(const sys_task_call* object,
                                     vpiHandle handle) {
  const std::string& name = object->VpiName();
  if (nonSynthSysCalls_.find(name) != nonSynthSysCalls_.end()) {
    reportError(object);
  }
}

void SynthSubset::leaveSys_func_call(const sys_func_call* object,
                                     vpiHandle handle) {
  const std::string& name = object->VpiName();
  if (nonSynthSysCalls_.find(name) != nonSynthSysCalls_.end()) {
    reportError(object);
  }
}

void SynthSubset::leaveClass_typespec(const class_typespec* object,
                                      vpiHandle handle) {
  if (const any* def = object->Class_defn())
    reportError(def);
  else
    reportError(object);
}

void SynthSubset::leaveClass_var(const class_var* object, vpiHandle handle) {
  if (const class_typespec* spec = (class_typespec*)object->Typespec()) {
    if (const class_defn* def = spec->Class_defn()) {
      if (reportedParent(def)) {
        mark(object);
        return;
      }
    }
  }
  reportError(object);
}

void SynthSubset::mark(const any* object) {
  nonSynthesizableObjects_.insert(object);
}

bool SynthSubset::reportedParent(const any* object) {
  if (object->UhdmType() == uhdmpackage) {
    if (object->VpiName() == "builtin") return true;
  } else if (object->UhdmType() == uhdmclass_defn) {
    if (object->VpiName() == "work@semaphore" ||
        object->VpiName() == "work@process" ||
        object->VpiName() == "work@mailbox")
      return true;
  }
  if (nonSynthesizableObjects_.find(object) != nonSynthesizableObjects_.end()) {
    return true;
  }
  if (const any* parent = object->VpiParent()) {
    return reportedParent(parent);
  }
  return false;
}

}  // namespace UHDM
<|endoftext|>


<commit_before>#ifndef CONCURRENCY_SIGNAL_HPP_
#define CONCURRENCY_SIGNAL_HPP_

#include "errors.hpp"
#include <boost/function.hpp>

#include "concurrency/pubsub.hpp"
#include "utils.hpp"

/* A `signal_t` is a boolean variable, combined with a way to be notified if
that boolean variable becomes true. Typically you will construct a concrete
subclass of `signal_t`, then pass a pointer to the underlying `signal_t` to
another object which will read from or listen to it.

To check if a `signal_t` has already been pulsed, call `is_pulsed()` on it. To
be notified when it gets pulsed, construct a `signal_t::subscription_t` and pass
a callback function and the signal to watch to its constructor. The callback
will be called when the signal is pulsed. If the signal is already pulsed at the
time you construct the `signal_t::subscription_t`, then the callback will be
called immediately.

`signal_t` is not thread-safe.

Although you may be tempted to, please do not add a method that "unpulses" a
`signal_t`. Part of the definition of a `signal_t` is that it does not return to
the unpulsed state after being pulsed, and some things may depend on that
property. If you want something like that, maybe you should look at something
other than `signal_t`. */

class signal_t :
    public home_thread_mixin_t
{
public:
    /* True if somebody has called `pulse()`. */
    bool is_pulsed() const {
        assert_thread();
        return pulsed;
    }

    /* Wrapper around a `publisher_t<boost::function<void()> >::subscription_t`
    */
    class subscription_t : public home_thread_mixin_t {
    public:
        explicit subscription_t(boost::function<void()> cb) : subs(cb) { }
        subscription_t(boost::function<void()> cb, signal_t *s) : subs(cb) {
            reset(s);
        }
        void reset(signal_t *s = NULL) {
            if (s) {
                mutex_assertion_t::acq_t acq(&s->lock);
                if (s->is_pulsed()) {
                    (subs.subscriber)();
                } else {
                    subs.reset(s->publisher_controller.get_publisher());
                }
            } else {
                subs.reset(NULL);
            }
        }
    private:
        publisher_t<boost::function<void()> >::subscription_t subs;
        DISABLE_COPYING(subscription_t);
    };

    /* The coro that calls `wait_lazily_ordered()` will be pushed onto the event
    queue when the signal is pulsed, but will not wake up immediately. Unless
    you really need the ordering guarantee, you should call
    `wait_lazily_unordered()`. */
    void wait_lazily_ordered();

    /* The coro that calls `wait_lazily_unordered()` will be notified soon after
    the signal has been pulsed, but not immediately. */
    void wait_lazily_unordered();

    /* `wait()` is a deprecated synonym for `wait_lazily_ordered()`. */
    void wait() {
        wait_lazily_ordered();
    }

    void rethread(int new_thread) {
        real_home_thread = new_thread;
        publisher_controller.rethread(new_thread);
        lock.rethread(new_thread);
    }

protected:
    signal_t() : pulsed(false) { }
    ~signal_t() { }

    void pulse() THROWS_NOTHING {
        mutex_assertion_t::acq_t acq(&lock);
        rassert(!is_pulsed());
        pulsed = true;
        publisher_controller.publish(&signal_t::call);
    }

private:
    static void call(boost::function<void()>& fun) THROWS_NOTHING {
        fun();
    }

    bool pulsed;
    publisher_controller_t<boost::function<void()> > publisher_controller;
    mutex_assertion_t lock;
    DISABLE_COPYING(signal_t);
};

#endif /* CONCURRENCY_SIGNAL_HPP_ */
<commit_msg>An incremental change to signal_t::subscription_t that might be thrown out or might be weird somehow.<commit_after>#ifndef CONCURRENCY_SIGNAL_HPP_
#define CONCURRENCY_SIGNAL_HPP_

#include "errors.hpp"
#include <boost/function.hpp>

#include "concurrency/pubsub.hpp"
#include "utils.hpp"

/* A `signal_t` is a boolean variable, combined with a way to be notified if
that boolean variable becomes true. Typically you will construct a concrete
subclass of `signal_t`, then pass a pointer to the underlying `signal_t` to
another object which will read from or listen to it.

To check if a `signal_t` has already been pulsed, call `is_pulsed()` on it. To
be notified when it gets pulsed, construct a `signal_t::subscription_t` and pass
a callback function and the signal to watch to its constructor. The callback
will be called when the signal is pulsed. If the signal is already pulsed at the
time you construct the `signal_t::subscription_t`, then the callback will be
called immediately.

`signal_t` is not thread-safe.

Although you may be tempted to, please do not add a method that "unpulses" a
`signal_t`. Part of the definition of a `signal_t` is that it does not return to
the unpulsed state after being pulsed, and some things may depend on that
property. If you want something like that, maybe you should look at something
other than `signal_t`. */

class signal_t :
    public home_thread_mixin_t
{
public:
    /* True if somebody has called `pulse()`. */
    bool is_pulsed() const {
        assert_thread();
        return pulsed;
    }

    /* Wrapper around a `publisher_t<boost::function<void()> >::subscription_t`
    */
    class subscription_t : public home_thread_mixin_t {
    public:
        explicit subscription_t(boost::function<void()> cb) : cb_(cb), subs(this) { }
        subscription_t(boost::function<void()> cb, signal_t *s) : cb_(cb), subs(this) {
            reset(s);
        }
        void reset(signal_t *s = NULL) {
            if (s) {
                mutex_assertion_t::acq_t acq(&s->lock);
                if (s->is_pulsed()) {
                    (subs.subscriber->cb_)();
                } else {
                    subs.reset(s->publisher_controller.get_publisher());
                }
            } else {
                subs.reset(NULL);
            }
        }
	friend class signal_t;
    private:
	boost::function<void()> cb_;
        publisher_t<subscription_t *>::subscription_t subs;
        DISABLE_COPYING(subscription_t);
    };

    /* The coro that calls `wait_lazily_ordered()` will be pushed onto the event
    queue when the signal is pulsed, but will not wake up immediately. Unless
    you really need the ordering guarantee, you should call
    `wait_lazily_unordered()`. */
    void wait_lazily_ordered();

    /* The coro that calls `wait_lazily_unordered()` will be notified soon after
    the signal has been pulsed, but not immediately. */
    void wait_lazily_unordered();

    /* `wait()` is a deprecated synonym for `wait_lazily_ordered()`. */
    void wait() {
        wait_lazily_ordered();
    }

    void rethread(int new_thread) {
        real_home_thread = new_thread;
        publisher_controller.rethread(new_thread);
        lock.rethread(new_thread);
    }

protected:
    signal_t() : pulsed(false) { }
    ~signal_t() { }

    void pulse() THROWS_NOTHING {
        mutex_assertion_t::acq_t acq(&lock);
        rassert(!is_pulsed());
        pulsed = true;
        publisher_controller.publish(&signal_t::call);
    }

private:
    static void call(subscription_t *subscription) THROWS_NOTHING {
        (subscription->cb_)();
    }

    bool pulsed;
    publisher_controller_t<subscription_t *> publisher_controller;
    mutex_assertion_t lock;
    DISABLE_COPYING(signal_t);
};

#endif /* CONCURRENCY_SIGNAL_HPP_ */
<|endoftext|>


<commit_before>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtMultimedia module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "player.h"

#include "playlistmodel.h"

#include <qmediaplayer.h>
#include <qmediaplaylist.h>
#include <qmediametadata.h>

#include <QtGui>

Player::Player(QWidget *parent)
    : QWidget(parent)
    , slider(0)
{
    player = new QMediaPlayer;
    metaData = new QMediaMetadata(player);

    connect(player, SIGNAL(durationChanged(qint64)), this, SLOT(durationChanged(qint64)));
    connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
    connect(metaData, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));

    QWidget *videoWidget = 0; /*service->createWidget();

    if (videoWidget)
        service->setVideoOutput(videoWidget);
    */
    PlaylistModel *playlistModel = new PlaylistModel(this);
    playlistModel->setPlaylist(player->mediaPlaylist());

    QTableView *playlistView = new QTableView;
    playlistView->setModel(playlistModel);

    slider = new QSlider(Qt::Horizontal);
    slider->setRange(0, 0);

    QPushButton *openButton = new QPushButton(tr("Open"));
    connect(openButton, SIGNAL(clicked()), this, SLOT(open()));

    QPushButton *playButton = new QPushButton(tr("Play"));
    connect(playButton, SIGNAL(clicked()), player, SLOT(play()));

    QPushButton *pauseButton = new QPushButton(tr("Pause"));
    connect(pauseButton, SIGNAL(clicked()), player, SLOT(pause()));

    QPushButton *stopButton = new QPushButton(tr("Stop"));
    connect(stopButton, SIGNAL(clicked()), player, SLOT(stop()));

    QLabel *volumeLabel = new QLabel(tr("Volume"));

    QSlider *volumeSlider = new QSlider(Qt::Horizontal);
    volumeSlider->setRange(0, 100);
    volumeSlider->setValue(player->volume());
    connect(volumeSlider, SIGNAL(valueChanged(int)), player, SLOT(setVolume(int)));

    QPushButton *muteButton = new QPushButton(tr("Mute"));
    muteButton->setCheckable(true);
    muteButton->setChecked(player->isMuted());
    connect(muteButton, SIGNAL(clicked(bool)), player, SLOT(setMuted(bool)));

    QBoxLayout *controlLayout = new QHBoxLayout;
    controlLayout->setMargin(0);
    controlLayout->addWidget(openButton);
    controlLayout->addWidget(playButton);
    controlLayout->addWidget(pauseButton);
    controlLayout->addWidget(stopButton);
    controlLayout->addWidget(volumeLabel);
    controlLayout->addWidget(volumeSlider);
    controlLayout->addWidget(muteButton);

    QBoxLayout *layout = new QVBoxLayout;
    if (videoWidget) {
        QSplitter *splitter = new QSplitter(Qt::Vertical);

        splitter->addWidget(videoWidget);
        splitter->addWidget(playlistView);

        layout->addWidget(splitter);
    } else {
        layout->addWidget(playlistView);
    }
    layout->addWidget(slider);
    layout->addLayout(controlLayout);

    setLayout(layout);
}

Player::~Player()
{
    delete player;
}

void Player::open()
{
    QString fileName = QFileDialog::getOpenFileName();

    if (!fileName.isNull()) {
        player->mediaPlaylist()->append(QMediaSource("", QLatin1String("file:///") + fileName));
    }
}

void Player::durationChanged(qint64 duration)
{
    slider->setMaximum(duration / 1000);
}

void Player::positionChanged(qint64 progress)
{
    slider->setValue(progress / 1000);
}

void Player::metaDataChanged()
{
    setWindowTitle(metaData->valueFor(QLatin1String("Title")).toString());
}
<commit_msg>Method name change on QMediaMetadata<commit_after>/****************************************************************************
**
** Copyright (c) 2008-2009 Nokia Corporation and/or its subsidiary(-ies).
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of the QtMultimedia module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** No Commercial Usage
** This file contains pre-release code and may not be distributed.
** You may use this file in accordance with the terms and conditions
** contained in the Technology Preview License Agreement accompanying
** this package.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Nokia gives you certain
** additional rights. These rights are described in the Nokia Qt LGPL
** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
** package.
**
** If you have questions regarding the use of this file, please contact
** Nokia at http://www.qtsoftware.com/contact.
** $QT_END_LICENSE$
**
****************************************************************************/

#include "player.h"

#include "playlistmodel.h"

#include <qmediaplayer.h>
#include <qmediaplaylist.h>
#include <qmediametadata.h>

#include <QtGui>

Player::Player(QWidget *parent)
    : QWidget(parent)
    , slider(0)
{
    player = new QMediaPlayer;
    metaData = new QMediaMetadata(player);

    connect(player, SIGNAL(durationChanged(qint64)), this, SLOT(durationChanged(qint64)));
    connect(player, SIGNAL(positionChanged(qint64)), this, SLOT(positionChanged(qint64)));
    connect(metaData, SIGNAL(metaDataChanged()), this, SLOT(metaDataChanged()));

    QWidget *videoWidget = 0; /*service->createWidget();

    if (videoWidget)
        service->setVideoOutput(videoWidget);
    */
    PlaylistModel *playlistModel = new PlaylistModel(this);
    playlistModel->setPlaylist(player->mediaPlaylist());

    QTableView *playlistView = new QTableView;
    playlistView->setModel(playlistModel);

    slider = new QSlider(Qt::Horizontal);
    slider->setRange(0, 0);

    QPushButton *openButton = new QPushButton(tr("Open"));
    connect(openButton, SIGNAL(clicked()), this, SLOT(open()));

    QPushButton *playButton = new QPushButton(tr("Play"));
    connect(playButton, SIGNAL(clicked()), player, SLOT(play()));

    QPushButton *pauseButton = new QPushButton(tr("Pause"));
    connect(pauseButton, SIGNAL(clicked()), player, SLOT(pause()));

    QPushButton *stopButton = new QPushButton(tr("Stop"));
    connect(stopButton, SIGNAL(clicked()), player, SLOT(stop()));

    QLabel *volumeLabel = new QLabel(tr("Volume"));

    QSlider *volumeSlider = new QSlider(Qt::Horizontal);
    volumeSlider->setRange(0, 100);
    volumeSlider->setValue(player->volume());
    connect(volumeSlider, SIGNAL(valueChanged(int)), player, SLOT(setVolume(int)));

    QPushButton *muteButton = new QPushButton(tr("Mute"));
    muteButton->setCheckable(true);
    muteButton->setChecked(player->isMuted());
    connect(muteButton, SIGNAL(clicked(bool)), player, SLOT(setMuted(bool)));

    QBoxLayout *controlLayout = new QHBoxLayout;
    controlLayout->setMargin(0);
    controlLayout->addWidget(openButton);
    controlLayout->addWidget(playButton);
    controlLayout->addWidget(pauseButton);
    controlLayout->addWidget(stopButton);
    controlLayout->addWidget(volumeLabel);
    controlLayout->addWidget(volumeSlider);
    controlLayout->addWidget(muteButton);

    QBoxLayout *layout = new QVBoxLayout;
    if (videoWidget) {
        QSplitter *splitter = new QSplitter(Qt::Vertical);

        splitter->addWidget(videoWidget);
        splitter->addWidget(playlistView);

        layout->addWidget(splitter);
    } else {
        layout->addWidget(playlistView);
    }
    layout->addWidget(slider);
    layout->addLayout(controlLayout);

    setLayout(layout);
}

Player::~Player()
{
    delete player;
}

void Player::open()
{
    QString fileName = QFileDialog::getOpenFileName();

    if (!fileName.isNull()) {
        player->mediaPlaylist()->append(QMediaSource("", QLatin1String("file:///") + fileName));
    }
}

void Player::durationChanged(qint64 duration)
{
    slider->setMaximum(duration / 1000);
}

void Player::positionChanged(qint64 progress)
{
    slider->setValue(progress / 1000);
}

void Player::metaDataChanged()
{
    setWindowTitle(metaData->metadata(QLatin1String("Title")).toString());
}
<|endoftext|>


<commit_before>#include <cmd_classbook_add_record_handler.h>
#include <QJsonArray>
#include <QSqlError>
#include <QUuid>

CmdClassbookAddRecordHandler::CmdClassbookAddRecordHandler(){
    m_vInputs.push_back(CmdInputDef("parentid").required().integer_().description("pareintid for classbook article"));
    m_vInputs.push_back(CmdInputDef("name").required().string_().description("name of article"));
    m_vInputs.push_back(CmdInputDef("content").required().string_().description("content of article"));
    m_vInputs.push_back(CmdInputDef("uuid").optional().uuid_().description("uuid of article"));
    m_vInputs.push_back(CmdInputDef("order").optional().integer_().description("order of article"));
}

QString CmdClassbookAddRecordHandler::cmd(){
    return "classbook_add_record";
}

bool CmdClassbookAddRecordHandler::accessUnauthorized(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessUser(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessTester(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessAdmin(){
    return true;
}

const QVector<CmdInputDef> &CmdClassbookAddRecordHandler::inputs(){
    return m_vInputs;
};

QString CmdClassbookAddRecordHandler::description(){
    return "Adds a new article with the specified name, content, and id.";
}

QStringList CmdClassbookAddRecordHandler::errors(){
    QStringList	list;
    return list;
}

void CmdClassbookAddRecordHandler::handle(QWebSocket *pClient, IWebSocketServer *pWebSocketServer, QString m, QJsonObject obj){

    QSqlDatabase db = *(pWebSocketServer->database());

    int parentid = obj["parentid"].toInt();

    //Check parentid in database
    QSqlQuery query(db);
    if(parentid !=0){
        query.prepare("SELECT name FROM classbook WHERE id=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        if (!query.next()){
            pWebSocketServer->sendMessageError(pClient, cmd(), m, Errors::NotFound("article with this id"));
            return;
        }
    }

    QString name = obj["name"].toString().trimmed().toHtmlEscaped();
    QString content = obj["content"].toString().trimmed().toHtmlEscaped();

    //Set uuid from request if free, else generate uuid
    QString uuid;
    if(obj.contains("uuid")){
        query.prepare("SELECT uuid FROM classbook WHERE uuid=:uuid");
        query.bindValue(":uuid", obj["uuid"].toString());
        query.exec();
        if (!query.next()){
            uuid = obj["uuid"].toString();
        } else {
            pWebSocketServer->sendMessageError(pClient, cmd(), m, Errors::UuidAlreadyExist(obj["uuid"].toString()));
            return;
        }
    } else {
        uuid = QUuid::createUuid().toString();
    }

    //Find parentuuid from database
    QString parentuuid = "00000000-0000-0000-0000-000000000000";
    if(parentid != 0){
        query.prepare("SELECT uuid FROM classbook WHERE id=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        query.next();
        QSqlRecord record = query.record();
        parentuuid = record.value("uuid").toString();
    }

    //Set ordered of article: increment max of child's ordered
    int ordered;
    if (obj.contains("order")){
        ordered = obj["order"].toInt();
    } else {
        query.prepare("SELECT MAX(ordered) AS max FROM classbook WHERE parentid=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        if (query.next()){
            QSqlRecord record = query.record();
            ordered = record.value("max").toInt() + 1;
        } else {
            query.prepare("SELECT ordered FROM classbook WHERE id=:parentid");
            query.bindValue(":parentid", parentid);
            query.exec();
            QSqlRecord record = query.record();
            ordered = record.value("ordered").toInt() + 1;
        }
    }

    //Set Datetime
    QDateTime created, updated;
    created = QDateTime::currentDateTime();
    updated = created;

    //Insert article into classbook
    query.prepare("INSERT INTO classbook("
                  "parentid,"
                  "ordered,"
                  "uuid,"
                  "parentuuid,"
                  "name,"
                  "content,"
                  "created,"
                  "updated"
                  ")"
                  "VALUES("
                  ":parentid,"
                  ":ordered,"
                  ":uuid,"
                  ":parentuuid,"
                  ":name,"
                  ":content,"
                  ":created,"
                  ":updated"
                  ")");
    query.bindValue(":parentid", parentid);
    query.bindValue(":ordered", ordered);
    query.bindValue(":uuid", uuid);
    query.bindValue(":parentuuid", parentuuid);
    query.bindValue(":name", name);
    query.bindValue(":content", content);
    query.bindValue(":created", created);
    query.bindValue(":updated", updated);
    if (!query.exec()){
        pWebSocketServer->sendMessageError(pClient, cmd(), m, Error(500, query.lastError().text()));
        return;
    }

    QJsonObject data;
    int rowid = query.lastInsertId().toInt();
    data["classbookid"] = QJsonValue(rowid);
    data["parentid"] = parentid;
    data["name"] = name;
    data["content"] = content;

    QJsonObject jsonResponse;
    jsonResponse["cmd"] = QJsonValue(cmd());
    jsonResponse["m"] = QJsonValue(m);
    jsonResponse["result"] = QJsonValue("DONE");
    jsonResponse["data"] = data;
    pWebSocketServer->sendMessage(pClient, jsonResponse);
}

<commit_msg>Small fix<commit_after>#include <cmd_classbook_add_record_handler.h>
#include <QJsonArray>
#include <QSqlError>
#include <QUuid>

CmdClassbookAddRecordHandler::CmdClassbookAddRecordHandler(){
    m_vInputs.push_back(CmdInputDef("parentid").required().integer_().description("pareintid for classbook article"));
    m_vInputs.push_back(CmdInputDef("name").required().string_().description("name of article"));
    m_vInputs.push_back(CmdInputDef("content").required().string_().description("content of article"));
    m_vInputs.push_back(CmdInputDef("uuid").optional().uuid_().description("uuid of article"));
    m_vInputs.push_back(CmdInputDef("order").optional().integer_().description("order of article"));
}

QString CmdClassbookAddRecordHandler::cmd(){
    return "classbook_add_record";
}

bool CmdClassbookAddRecordHandler::accessUnauthorized(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessUser(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessTester(){
    return false;
}

bool CmdClassbookAddRecordHandler::accessAdmin(){
    return true;
}

const QVector<CmdInputDef> &CmdClassbookAddRecordHandler::inputs(){
    return m_vInputs;
}

QString CmdClassbookAddRecordHandler::description(){
    return "Adds a new article with the specified name, content, and id.";
}

QStringList CmdClassbookAddRecordHandler::errors(){
    QStringList	list;
    return list;
}

void CmdClassbookAddRecordHandler::handle(QWebSocket *pClient, IWebSocketServer *pWebSocketServer, QString m, QJsonObject obj){

    QSqlDatabase db = *(pWebSocketServer->database());

    int parentid = obj["parentid"].toInt();

    //Check parentid in database
    QSqlQuery query(db);
    if(parentid !=0){
        query.prepare("SELECT name FROM classbook WHERE id=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        if (!query.next()){
            pWebSocketServer->sendMessageError(pClient, cmd(), m, Errors::NotFound("article with this id"));
            return;
        }
    }

    QString name = obj["name"].toString().trimmed().toHtmlEscaped();
    QString content = obj["content"].toString().trimmed().toHtmlEscaped();

    //Set uuid from request if free, else generate uuid
    QString uuid;
    if(obj.contains("uuid")){
        query.prepare("SELECT uuid FROM classbook WHERE uuid=:uuid");
        query.bindValue(":uuid", obj["uuid"].toString());
        query.exec();
        if (!query.next()){
            uuid = obj["uuid"].toString();
        } else {
            pWebSocketServer->sendMessageError(pClient, cmd(), m, Errors::UuidAlreadyExist(obj["uuid"].toString()));
            return;
        }
    } else {
        uuid = QUuid::createUuid().toString();
    }

    //Find parentuuid from database
    QString parentuuid = "00000000-0000-0000-0000-000000000000";
    if(parentid != 0){
        query.prepare("SELECT uuid FROM classbook WHERE id=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        query.next();
        QSqlRecord record = query.record();
        parentuuid = record.value("uuid").toString();
    }

    //Set ordered of article: increment max of child's ordered
    int ordered;
    if (obj.contains("order")){
        ordered = obj["order"].toInt();
    } else {
        query.prepare("SELECT MAX(ordered) AS max FROM classbook WHERE parentid=:parentid");
        query.bindValue(":parentid", parentid);
        query.exec();
        if (query.next()){
            QSqlRecord record = query.record();
            ordered = record.value("max").toInt() + 1;
        } else {
            query.prepare("SELECT ordered FROM classbook WHERE id=:parentid");
            query.bindValue(":parentid", parentid);
            query.exec();
            QSqlRecord record = query.record();
            ordered = record.value("ordered").toInt() + 1;
        }
    }

    //Set Datetime
    QDateTime created, updated;
    created = QDateTime::currentDateTime();
    updated = created;

    //Insert article into classbook
    query.prepare("INSERT INTO classbook("
                  "parentid,"
                  "ordered,"
                  "uuid,"
                  "parentuuid,"
                  "name,"
                  "content,"
                  "created,"
                  "updated"
                  ")"
                  "VALUES("
                  ":parentid,"
                  ":ordered,"
                  ":uuid,"
                  ":parentuuid,"
                  ":name,"
                  ":content,"
                  ":created,"
                  ":updated"
                  ")");
    query.bindValue(":parentid", parentid);
    query.bindValue(":ordered", ordered);
    query.bindValue(":uuid", uuid);
    query.bindValue(":parentuuid", parentuuid);
    query.bindValue(":name", name);
    query.bindValue(":content", content);
    query.bindValue(":created", created);
    query.bindValue(":updated", updated);
    if (!query.exec()){
        pWebSocketServer->sendMessageError(pClient, cmd(), m, Error(500, query.lastError().text()));
        return;
    }

    QJsonObject data;
    int rowid = query.lastInsertId().toInt();
    data["classbookid"] = QJsonValue(rowid);
    data["parentid"] = parentid;
    data["name"] = name;
    data["content"] = content;

    QJsonObject jsonResponse;
    jsonResponse["cmd"] = QJsonValue(cmd());
    jsonResponse["m"] = QJsonValue(m);
    jsonResponse["result"] = QJsonValue("DONE");
    jsonResponse["data"] = data;
    pWebSocketServer->sendMessage(pClient, jsonResponse);
}

<|endoftext|>


<commit_before>#include <list>
#include <algorithm>
#include "rtmidi/RtMidi.h"
#include "core.hpp"
#include "MidiInterface.hpp"

using namespace rack;

/**
 * MidiIO implements the shared functionality of all midi modules, namely:
 * + Channel Selection (including helper for storing json)
 * + Interface Selection (including helper for storing json)
 * + rtMidi initialisation (input or output)
 */
MidiIO::MidiIO(bool isOut) {
	channel = -1;
	this->isOut = isOut;

	if (isOut) {
		fprintf(stderr, "Midi Out is currently not supported (will be added soon)");
	}
};

void MidiIO::setChannel(int channel) {
	this->channel = channel;
}

std::unordered_map<std::string, MidiInWrapper*> MidiIO::midiInMap = {};

json_t *MidiIO::addBaseJson(json_t *rootJ) {
	if (deviceName != "") {
		json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str()));
		json_object_set_new(rootJ, "channel", json_integer(channel));
	}
	return rootJ;
}

void MidiIO::baseFromJson(json_t *rootJ) {
	json_t *portNameJ = json_object_get(rootJ, "interfaceName");
	if (portNameJ) {
		deviceName = json_string_value(portNameJ);
		openDevice(deviceName);
	}

	json_t *channelJ = json_object_get(rootJ, "channel");
	if (channelJ) {
		setChannel(json_integer_value(channelJ));
	}
}

std::vector<std::string> MidiIO::getDevices() {
	/* Note: we could also use an existing interface if one exists */
	static RtMidiIn * m = new RtMidiIn();

	std::vector<std::string> names = {};

	for (int i = 0; i < m->getPortCount(); i++) {
		names.push_back(m->getPortName(i));
	}

	return names;
}

void MidiIO::openDevice(std::string deviceName) {
	MidiInWrapper *mw = midiInMap[deviceName];

	if (id > 0 || deviceName != "") {
		close();
	}

	if (!mw) {
		try {
			mw = new MidiInWrapper();
			midiInMap[deviceName] = mw;

			for (int i = 0; i < mw->getPortCount(); i++) {
				if (deviceName == mw->getPortName(i)) {
					mw->openPort(i);
					break;
				}
			}
		}
		catch (RtMidiError &error) {
			fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
			return;
		}
	}

	this->deviceName = deviceName;

	midiInMap[deviceName]->ignoreTypes(ignore_midiSysex, ignore_midiTime, ignore_midiSense);

	id = midiInMap[deviceName]->add();
}

std::string MidiIO::getDeviceName() {
	return deviceName;
}

double MidiIO::getMessage(std::vector<unsigned char> *msg) {
	std::vector<unsigned char> next_msg;

	MidiInWrapper *mw = midiInMap[deviceName];

	if (!mw) {
		fprintf(stderr, "Device not opened!: %s\n", deviceName.c_str());
		return 0;
	}

	double stamp = midiInMap[deviceName]->getMessage(&next_msg);

	if (next_msg.size() > 0) {
		for (auto kv : mw->idMessagesMap) {
			mw->idMessagesMap[kv.first].push_back(next_msg);
			mw->idStampsMap[kv.first].push_back(stamp);
		}
	}

	if (mw->idMessagesMap[id].size() <= 0) {
		*msg = next_msg;
		return stamp;
	}

	*msg = mw->idMessagesMap[id].front();
	stamp = mw->idStampsMap[id].front();
	mw->idMessagesMap[id].pop_front();
	return stamp;
}

bool MidiIO::isPortOpen() {
	return id > 0;
}

void MidiIO::close() {
	MidiInWrapper * mw = midiInMap[deviceName];
	if (!mw){
		return;
	}

	mw->erase(id);

	if (mw->subscribers == 0) {
		mw->closePort();
		midiInMap.erase(deviceName);
	}

	id = -1;
}

void MidiItem::onAction() {
	midiModule->resetMidi(); // reset Midi values
	midiModule->openDevice(text);
}

void MidiChoice::onAction() {
	Menu *menu = gScene->createMenu();
	menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
	menu->box.size.x = box.size.x;

	{
		MidiItem *midiItem = new MidiItem();
		midiItem->midiModule = midiModule;
		midiItem->text = "";
		menu->pushChild(midiItem);
	}

	std::vector<std::string> deviceNames = midiModule->getDevices();
	for (int i = 0; i < deviceNames.size(); i++) {
		MidiItem *midiItem = new MidiItem();
		midiItem->midiModule = midiModule;
		midiItem->text = deviceNames[i];
		menu->pushChild(midiItem);
	}
}

void MidiChoice::step() {
	if (midiModule->getDeviceName() == "") {
		text = "No Device";
		return;
	}
	std::string name = midiModule->getDeviceName();
	text = ellipsize(name, 15);
}

void ChannelItem::onAction() {
	midiModule->resetMidi(); // reset Midi values
	midiModule->setChannel(channel);
}

void ChannelChoice::onAction() {
	Menu *menu = gScene->createMenu();
	menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
	menu->box.size.x = box.size.x;

	{
		ChannelItem *channelItem = new ChannelItem();
		channelItem->midiModule = midiModule;
		channelItem->channel = -1;
		channelItem->text = "All";
		menu->pushChild(channelItem);
	}
	for (int channel = 0; channel < 16; channel++) {
		ChannelItem *channelItem = new ChannelItem();
		channelItem->midiModule = midiModule;
		channelItem->channel = channel;
		channelItem->text = stringf("%d", channel + 1);
		menu->pushChild(channelItem);
	}
}

void ChannelChoice::step() {
	text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
}<commit_msg>Fix minor issues<commit_after>#include <list>
#include <algorithm>
#include "rtmidi/RtMidi.h"
#include "core.hpp"
#include "MidiInterface.hpp"

using namespace rack;

/**
 * MidiIO implements the shared functionality of all midi modules, namely:
 * + Channel Selection (including helper for storing json)
 * + Interface Selection (including helper for storing json)
 * + rtMidi initialisation (input or output)
 */
MidiIO::MidiIO(bool isOut) {
	channel = -1;
	this->isOut = isOut;

	if (isOut) {
		fprintf(stderr, "Midi Out is currently not supported (will be added soon)");
	}
};

void MidiIO::setChannel(int channel) {
	this->channel = channel;
}

std::unordered_map<std::string, MidiInWrapper*> MidiIO::midiInMap = {};

json_t *MidiIO::addBaseJson(json_t *rootJ) {
	if (deviceName != "") {
		json_object_set_new(rootJ, "interfaceName", json_string(deviceName.c_str()));
		json_object_set_new(rootJ, "channel", json_integer(channel));
	}
	return rootJ;
}

void MidiIO::baseFromJson(json_t *rootJ) {
	json_t *portNameJ = json_object_get(rootJ, "interfaceName");
	if (portNameJ) {
		openDevice(json_string_value(portNameJ));
	}

	json_t *channelJ = json_object_get(rootJ, "channel");
	if (channelJ) {
		setChannel(json_integer_value(channelJ));
	}
}

std::vector<std::string> MidiIO::getDevices() {
	/* Note: we could also use an existing interface if one exists */
	static RtMidiIn * m = new RtMidiIn();

	std::vector<std::string> names = {};

	for (int i = 0; i < m->getPortCount(); i++) {
		names.push_back(m->getPortName(i));
	}

	return names;
}

void MidiIO::openDevice(std::string deviceName) {

	MidiInWrapper *mw = midiInMap[deviceName];

	if (this->id > 0 || this->deviceName != "") {
		close();
	}

	if (!mw) {
		try {
			mw = new MidiInWrapper();
			midiInMap[deviceName] = mw;

			for (int i = 0; i < mw->getPortCount(); i++) {
				if (deviceName == mw->getPortName(i)) {
					mw->openPort(i);
					break;
				}
			}
		}
		catch (RtMidiError &error) {
			fprintf(stderr, "Failed to create RtMidiIn: %s\n", error.getMessage().c_str());
			return;
		}
	}

	this->deviceName = deviceName;

	midiInMap[deviceName]->ignoreTypes(ignore_midiSysex, ignore_midiTime, ignore_midiSense);

	id = midiInMap[deviceName]->add();
}

std::string MidiIO::getDeviceName() {
	return deviceName;
}

double MidiIO::getMessage(std::vector<unsigned char> *msg) {
	std::vector<unsigned char> next_msg;

	MidiInWrapper *mw = midiInMap[deviceName];

	if (!mw) {
		fprintf(stderr, "Device not opened!: %s\n", deviceName.c_str());
		return 0;
	}

	double stamp = midiInMap[deviceName]->getMessage(&next_msg);

	if (next_msg.size() > 0) {
		for (auto kv : mw->idMessagesMap) {
			mw->idMessagesMap[kv.first].push_back(next_msg);
			mw->idStampsMap[kv.first].push_back(stamp);
		}
	}

	if (mw->idMessagesMap[id].size() <= 0) {
		*msg = next_msg;
		return stamp;
	}

	*msg = mw->idMessagesMap[id].front();
	stamp = mw->idStampsMap[id].front();
	mw->idMessagesMap[id].pop_front();
	return stamp;
}

bool MidiIO::isPortOpen() {
	return id > 0;
}

void MidiIO::close() {

	MidiInWrapper * mw = midiInMap[deviceName];

	if (!mw || id < 0) {
		fprintf(stderr, "Trying to close already closed device!\n");
		return;
	}

	mw->erase(id);

	if (mw->subscribers == 0) {
		mw->closePort();
		midiInMap.erase(deviceName);
	}

	id = -1;
	deviceName = "";
}

void MidiItem::onAction() {
	midiModule->resetMidi(); // reset Midi values
	midiModule->openDevice(text);
}

void MidiChoice::onAction() {
	Menu *menu = gScene->createMenu();
	menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
	menu->box.size.x = box.size.x;

	{
		MidiItem *midiItem = new MidiItem();
		midiItem->midiModule = midiModule;
		midiItem->text = "";
		menu->pushChild(midiItem);
	}

	std::vector<std::string> deviceNames = midiModule->getDevices();
	for (int i = 0; i < deviceNames.size(); i++) {
		MidiItem *midiItem = new MidiItem();
		midiItem->midiModule = midiModule;
		midiItem->text = deviceNames[i];
		menu->pushChild(midiItem);
	}
}

void MidiChoice::step() {
	if (midiModule->getDeviceName() == "") {
		text = "No Device";
		return;
	}
	std::string name = midiModule->getDeviceName();
	text = ellipsize(name, 15);
}

void ChannelItem::onAction() {
	midiModule->resetMidi(); // reset Midi values
	midiModule->setChannel(channel);
}

void ChannelChoice::onAction() {
	Menu *menu = gScene->createMenu();
	menu->box.pos = getAbsolutePos().plus(Vec(0, box.size.y));
	menu->box.size.x = box.size.x;

	{
		ChannelItem *channelItem = new ChannelItem();
		channelItem->midiModule = midiModule;
		channelItem->channel = -1;
		channelItem->text = "All";
		menu->pushChild(channelItem);
	}
	for (int channel = 0; channel < 16; channel++) {
		ChannelItem *channelItem = new ChannelItem();
		channelItem->midiModule = midiModule;
		channelItem->channel = channel;
		channelItem->text = stringf("%d", channel + 1);
		menu->pushChild(channelItem);
	}
}

void ChannelChoice::step() {
	text = (midiModule->channel >= 0) ? stringf("%d", midiModule->channel + 1) : "All";
}<|endoftext|>