<commit_before>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "MDSMap.h"

#include <sstream>
using std::stringstream;

// features

const struct CompatSet::Feature feature_compat[] = {
  END_FEATURE
};
const struct CompatSet::Feature feature_incompat[] = {
  MDS_FEATURE_INCOMPAT_BASE,
  MDS_FEATURE_INCOMPAT_CLIENTRANGES,
  MDS_FEATURE_INCOMPAT_FILELAYOUT,
  MDS_FEATURE_INCOMPAT_DIRINODE,
  END_FEATURE
};
const struct CompatSet::Feature feature_ro_compat[] = {
  END_FEATURE
};

CompatSet mdsmap_compat(feature_compat,
			feature_ro_compat,
			feature_incompat);

// base (pre v0.20)
const struct CompatSet::Feature feature_compat_base[] = {
  END_FEATURE
};
const struct CompatSet::Feature feature_incompat_base[] = {
  MDS_FEATURE_INCOMPAT_BASE,
  END_FEATURE
};
const struct CompatSet::Feature feature_ro_compat_base[] = {
  END_FEATURE
};

CompatSet mdsmap_compat_base(feature_compat_base,
			     feature_ro_compat_base,
			     feature_incompat_base);

// ----

void MDSMap::print(ostream& out)
{
  out << "epoch " << epoch << "\n";
  out << "flags " << hex << flags << dec << "\n";
  out << "created " << created << std::endl;
  out << "modified " << modified << std::endl;
  out << "tableserver " << tableserver << std::endl;
  out << "root " << root << std::endl;
  out << "session_timeout " << session_timeout << "\n"
      << "session_autoclose " << session_autoclose << "\n";
  out << "last_failure " << last_failure << "\n"
      << "last_failure_osd_epoch" << last_failure_osd_epoch << "\n";

  out << "\ncompat\t" << compat << std::endl;
  out << "\nmax_mds\t" << max_mds << std::endl;
  out << "in\t" << in << "\n"
      << "up\t" << up << "\n"
      << "failed\t" << failed << "\n"
      << "stopped\t" << stopped << "\n";

  multimap< pair<unsigned,unsigned>, uint64_t > foo;
  for (map<uint64_t,mds_info_t>::iterator p = mds_info.begin();
       p != mds_info.end();
       p++)
    foo.insert(pair<pair<unsigned,unsigned>,uint64_t>(pair<unsigned,unsigned>(p->second.rank, p->second.inc-1), p->first));

  for (multimap< pair<unsigned,unsigned>, uint64_t >::iterator p = foo.begin();
       p != foo.end();
       p++) {
    mds_info_t& info = mds_info[p->second];

    out << p->second << ":\t"
	<< info.addr
	<< " '" << info.name << "'"
	<< " mds" << info.rank
	<< "." << info.inc
	<< " " << ceph_mds_state_name(info.state)
	<< " seq " << info.state_seq;
    if (info.laggy())
      out << " laggy since " << info.laggy_since;
    if (info.standby_for_rank != -1 ||
	!info.standby_for_name.empty()) {
      out << " (standby for";
      //if (info.standby_for_rank >= 0)
	out << " rank " << info.standby_for_rank;
      if (!info.standby_for_name.empty())
	out << " '" << info.standby_for_name << "'";
      out << ")";
    }
    if (info.export_targets.size())
      out << " export_targets=" << info.export_targets;
    out << "\n";
  }

}

void MDSMap::print_summary(ostream& out)
{
  map<int,string> by_rank;
  map<string,int> by_state;

  for (map<uint64_t,mds_info_t>::iterator p = mds_info.begin();
       p != mds_info.end();
       p++) {
    string s = ceph_mds_state_name(p->second.state);
    if (p->second.laggy())
      s += "(laggy or crashed)";

    if (p->second.rank >= 0)
      by_rank[p->second.rank] = s;
    else
      by_state[s]++;
  }

  out << "e" << get_epoch() << ": " << up.size() << "/" << in.size() << "/" << max_mds << " up";

  if (by_rank.size())
    out << " " << by_rank;

  for (map<string,int>::reverse_iterator p = by_state.rbegin(); p != by_state.rend(); p++)
    out << ", " << p->second << " " << p->first;

  if (failed.size())
    out << ", " << failed.size() << " failed";
  if (stopped.size())
    out << ", " << stopped.size() << " stopped";
}

enum health_status_t MDSMap::
get_health(std::ostream &ss) const
{
  health_status_t ret(HEALTH_OK);
  std::ostringstream oss;

  if (!failed.empty()) {
    oss << " There are failed MDSes: ";
    string sep("");
    for (set<int32_t>::const_iterator f = failed.begin();
	 f != failed.end(); ++f) {
      oss << sep << "rank " << *f;
      sep = ", ";
    }
    oss << ".";
    if (ret > HEALTH_ERR)
      ret = HEALTH_ERR;
  }

  map<int32_t,uint64_t>::const_iterator u = up.begin();
  map<int32_t,uint64_t>::const_iterator u_end = up.end();
  map<uint64_t,mds_info_t>::const_iterator m_end = mds_info.end();
  string prefix(" There are lagging MDSes: ");
  for (; u != u_end; ++u) {
    map<uint64_t,mds_info_t>::const_iterator m = mds_info.find(u->second);
    assert(m != m_end);
    const mds_info_t &mds_info(m->second);
    if (mds_info.laggy()) {
      oss << prefix << mds_info.name << "(rank " << mds_info.rank << ")" ;
      prefix = ", ";
      if (ret > HEALTH_WARN)
	ret = HEALTH_WARN;
    }
  }
  dout(0) << "get_health: " << oss.str() << " (ret = " << ret << ")" << dendl;
  ss << oss.str();
  return ret;
}
<commit_msg>mds: remove "N stopped" from short mdsmap summary<commit_after>// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph - scalable distributed file system
 *
 * Copyright (C) 2004-2006 Sage Weil <sage@newdream.net>
 *
 * This is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License version 2.1, as published by the Free Software
 * Foundation.  See file COPYING.
 *
 */

#include "MDSMap.h"

#include <sstream>
using std::stringstream;

// features

const struct CompatSet::Feature feature_compat[] = {
  END_FEATURE
};
const struct CompatSet::Feature feature_incompat[] = {
  MDS_FEATURE_INCOMPAT_BASE,
  MDS_FEATURE_INCOMPAT_CLIENTRANGES,
  MDS_FEATURE_INCOMPAT_FILELAYOUT,
  MDS_FEATURE_INCOMPAT_DIRINODE,
  END_FEATURE
};
const struct CompatSet::Feature feature_ro_compat[] = {
  END_FEATURE
};

CompatSet mdsmap_compat(feature_compat,
			feature_ro_compat,
			feature_incompat);

// base (pre v0.20)
const struct CompatSet::Feature feature_compat_base[] = {
  END_FEATURE
};
const struct CompatSet::Feature feature_incompat_base[] = {
  MDS_FEATURE_INCOMPAT_BASE,
  END_FEATURE
};
const struct CompatSet::Feature feature_ro_compat_base[] = {
  END_FEATURE
};

CompatSet mdsmap_compat_base(feature_compat_base,
			     feature_ro_compat_base,
			     feature_incompat_base);

// ----

void MDSMap::print(ostream& out)
{
  out << "epoch " << epoch << "\n";
  out << "flags " << hex << flags << dec << "\n";
  out << "created " << created << std::endl;
  out << "modified " << modified << std::endl;
  out << "tableserver " << tableserver << std::endl;
  out << "root " << root << std::endl;
  out << "session_timeout " << session_timeout << "\n"
      << "session_autoclose " << session_autoclose << "\n";
  out << "last_failure " << last_failure << "\n"
      << "last_failure_osd_epoch" << last_failure_osd_epoch << "\n";

  out << "\ncompat\t" << compat << std::endl;
  out << "\nmax_mds\t" << max_mds << std::endl;
  out << "in\t" << in << "\n"
      << "up\t" << up << "\n"
      << "failed\t" << failed << "\n"
      << "stopped\t" << stopped << "\n";

  multimap< pair<unsigned,unsigned>, uint64_t > foo;
  for (map<uint64_t,mds_info_t>::iterator p = mds_info.begin();
       p != mds_info.end();
       p++)
    foo.insert(pair<pair<unsigned,unsigned>,uint64_t>(pair<unsigned,unsigned>(p->second.rank, p->second.inc-1), p->first));

  for (multimap< pair<unsigned,unsigned>, uint64_t >::iterator p = foo.begin();
       p != foo.end();
       p++) {
    mds_info_t& info = mds_info[p->second];

    out << p->second << ":\t"
	<< info.addr
	<< " '" << info.name << "'"
	<< " mds" << info.rank
	<< "." << info.inc
	<< " " << ceph_mds_state_name(info.state)
	<< " seq " << info.state_seq;
    if (info.laggy())
      out << " laggy since " << info.laggy_since;
    if (info.standby_for_rank != -1 ||
	!info.standby_for_name.empty()) {
      out << " (standby for";
      //if (info.standby_for_rank >= 0)
	out << " rank " << info.standby_for_rank;
      if (!info.standby_for_name.empty())
	out << " '" << info.standby_for_name << "'";
      out << ")";
    }
    if (info.export_targets.size())
      out << " export_targets=" << info.export_targets;
    out << "\n";
  }

}

void MDSMap::print_summary(ostream& out)
{
  map<int,string> by_rank;
  map<string,int> by_state;

  for (map<uint64_t,mds_info_t>::iterator p = mds_info.begin();
       p != mds_info.end();
       p++) {
    string s = ceph_mds_state_name(p->second.state);
    if (p->second.laggy())
      s += "(laggy or crashed)";

    if (p->second.rank >= 0)
      by_rank[p->second.rank] = s;
    else
      by_state[s]++;
  }

  out << "e" << get_epoch() << ": " << up.size() << "/" << in.size() << "/" << max_mds << " up";

  if (by_rank.size())
    out << " " << by_rank;

  for (map<string,int>::reverse_iterator p = by_state.rbegin(); p != by_state.rend(); p++)
    out << ", " << p->second << " " << p->first;

  if (failed.size())
    out << ", " << failed.size() << " failed";
  //if (stopped.size())
  //out << ", " << stopped.size() << " stopped";
}

enum health_status_t MDSMap::
get_health(std::ostream &ss) const
{
  health_status_t ret(HEALTH_OK);
  std::ostringstream oss;

  if (!failed.empty()) {
    oss << " There are failed MDSes: ";
    string sep("");
    for (set<int32_t>::const_iterator f = failed.begin();
	 f != failed.end(); ++f) {
      oss << sep << "rank " << *f;
      sep = ", ";
    }
    oss << ".";
    if (ret > HEALTH_ERR)
      ret = HEALTH_ERR;
  }

  map<int32_t,uint64_t>::const_iterator u = up.begin();
  map<int32_t,uint64_t>::const_iterator u_end = up.end();
  map<uint64_t,mds_info_t>::const_iterator m_end = mds_info.end();
  string prefix(" There are lagging MDSes: ");
  for (; u != u_end; ++u) {
    map<uint64_t,mds_info_t>::const_iterator m = mds_info.find(u->second);
    assert(m != m_end);
    const mds_info_t &mds_info(m->second);
    if (mds_info.laggy()) {
      oss << prefix << mds_info.name << "(rank " << mds_info.rank << ")" ;
      prefix = ", ";
      if (ret > HEALTH_WARN)
	ret = HEALTH_WARN;
    }
  }
  dout(0) << "get_health: " << oss.str() << " (ret = " << ret << ")" << dendl;
  ss << oss.str();
  return ret;
}
<|endoftext|>


<commit_before>/// @file signal_handler.cpp
///
/// @brief Implementation of signal_handler.hpp
///
/// @author Thomas Vanderbruggen <thomas@koheron.com>
/// @date 11/09/2015
///
/// (c) Koheron 2014-2015

#include "signal_handler.hpp"

#include <iostream>
#include <cxxabi.h>

extern "C" {
  #include <signal.h>
  #include <execinfo.h>
}

#include "kserver.hpp"

namespace kserver {

KServer* SignalHandler::kserver = nullptr;

SignalHandler::SignalHandler()
{}

SignalHandler::~SignalHandler()
{
    // TODO Restore disabled signals
}

int SignalHandler::Init(KServer *kserver_)
{
    kserver = kserver_;

    if(set_interrup_signals() < 0 ||
       set_ignore_signals()   < 0 ||
       set_crash_signals()    < 0)
        return -1;

    return 0;
}

// Interrupt signals

int volatile SignalHandler::s_interrupted = 0;

void exit_signal_handler(int s)
{
    SignalHandler::s_interrupted = 1;
}

int SignalHandler::set_interrup_signals()
{
    struct sigaction sig_int_handler;

    sig_int_handler.sa_handler = exit_signal_handler;
    sigemptyset(&sig_int_handler.sa_mask);
    sig_int_handler.sa_flags = 0;

    if(sigaction(SIGINT, &sig_int_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGINT handler\n");
        return -1;
    }

    if(sigaction(SIGTERM, &sig_int_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGTERM handler\n");
        return -1;
    }

    return 0;
}

// Ignored signals

int SignalHandler::set_ignore_signals()
{
    struct sigaction sig_ign_handler;

    sig_ign_handler.sa_handler = SIG_IGN;
    sig_ign_handler.sa_flags = 0;
    sigemptyset(&sig_ign_handler.sa_mask);

    // Disable SIGPIPE which is call by the socket write function
    // when client closes its connection during writing.
    // Results in an unwanted server shutdown
    if(sigaction(SIGPIPE, &sig_ign_handler, 0) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot disable SIGPIPE\n");
        return -1;
    }

    // XXX TV
    // According to this:
    // http://stackoverflow.com/questions/7296923/different-signal-handler-for-thread-and-process-is-it-possible
    //
    // SIGPIPE is delivered to the thread generating it.
    // It might thus be possible to stop the session emitting it.

    if(sigaction(SIGTSTP, &sig_ign_handler, 0) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot disable SIGTSTP\n");
        return -1;
    }

    return 0;
}

// Crashed signals
//
// Write backtrace to the syslog. See:
// http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes

#define BACKTRACE_BUFF_SIZE 100

void crash_signal_handler(int sig)
{
    // The signal handler is called several times
    // on a segmentation fault (WHY ?).
    // So only display the backtrace the first time
    if(SignalHandler::s_interrupted)
        return;

    const char *sig_name;

    switch(sig) {
      case SIGBUS:
        sig_name = "(Bus Error)";
        break;
      case SIGSEGV:
        sig_name = "(Segmentation Fault)";
        break;
      default:
        sig_name = "(Unidentify signal)";
    }

    SignalHandler::kserver->syslog.print(SysLog::CRITICAL,
                              "CRASH: signal %d %s\n", sig, sig_name);

    void *buffer[BACKTRACE_BUFF_SIZE];
    size_t size = backtrace(buffer, BACKTRACE_BUFF_SIZE);
    char **messages = backtrace_symbols(buffer, size);

    if (messages == NULL) {
        SignalHandler::kserver->syslog.print(SysLog::ERROR,
                                             "No backtrace_symbols");
        goto exit;
    }

    for(unsigned int i = 0; i < size && messages != NULL; i++) {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // Find parantheses and +address offset surrounding mangled name
        for(char *p = messages[i]; *p; ++p) {
            if(*p == '(') {
                mangled_name = p;
            }
            else if(*p == '+') {
                offset_begin = p;
            }
            else if(*p == ')') {
                offset_end = p;
                break;
            }
        }

        // If the line could be processed, attempt to demangle the symbol
        if(mangled_name && offset_begin && offset_end
           && mangled_name < offset_begin) {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // If demangling is successful, output the demangled function name
            if(status == 0) {
                SignalHandler::kserver->syslog.print(SysLog::INFO,
                        "[bt]: (%d) %s : %s+%s%s\n",
                        i, messages[i], real_name, offset_begin, offset_end);
            } else { // Otherwise, output the mangled function name
                SignalHandler::kserver->syslog.print(SysLog::INFO,
                        "[bt]: (%d) %s : %s+%s%s\n",
                        i, messages[i], mangled_name, offset_begin, offset_end);
            }

            free(real_name);
        }
    }

    free(messages);

exit:
    // Exit KServer
    SignalHandler::s_interrupted = 1;
}

int SignalHandler::set_crash_signals()
{
    struct sigaction sig_crash_handler;

    sig_crash_handler.sa_handler = crash_signal_handler;
    sigemptyset(&sig_crash_handler.sa_mask);
    sig_crash_handler.sa_flags = SA_RESTART | SA_SIGINFO;

    if(sigaction(SIGSEGV, &sig_crash_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGSEGV handler\n");
        return -1;
    }

    if(sigaction(SIGBUS, &sig_crash_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGBUSs handler\n");
        return -1;
    }

    return 0;
}

} // namespace kserver
<commit_msg>Style<commit_after>/// @file signal_handler.cpp
///
/// @brief Implementation of signal_handler.hpp
///
/// @author Thomas Vanderbruggen <thomas@koheron.com>
/// @date 11/09/2015
///
/// (c) Koheron 2014-2015

#include "signal_handler.hpp"

#include <iostream>
#include <cxxabi.h>

extern "C" {
  #include <signal.h>
  #include <execinfo.h>
}

#include "kserver.hpp"

namespace kserver {

KServer* SignalHandler::kserver = nullptr;

SignalHandler::SignalHandler()
{}

SignalHandler::~SignalHandler()
{
    // TODO Restore disabled signals
}

int SignalHandler::Init(KServer *kserver_)
{
    kserver = kserver_;

    if (set_interrup_signals() < 0 ||
        set_ignore_signals()   < 0 ||
        set_crash_signals()    < 0)
        return -1;

    return 0;
}

// Interrupt signals

int volatile SignalHandler::s_interrupted = 0;

void exit_signal_handler(int s)
{
    SignalHandler::s_interrupted = 1;
}

int SignalHandler::set_interrup_signals()
{
    struct sigaction sig_int_handler;

    sig_int_handler.sa_handler = exit_signal_handler;
    sigemptyset(&sig_int_handler.sa_mask);
    sig_int_handler.sa_flags = 0;

    if (sigaction(SIGINT, &sig_int_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGINT handler\n");
        return -1;
    }

    if (sigaction(SIGTERM, &sig_int_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGTERM handler\n");
        return -1;
    }

    return 0;
}

// Ignored signals

int SignalHandler::set_ignore_signals()
{
    struct sigaction sig_ign_handler;

    sig_ign_handler.sa_handler = SIG_IGN;
    sig_ign_handler.sa_flags = 0;
    sigemptyset(&sig_ign_handler.sa_mask);

    // Disable SIGPIPE which is call by the socket write function
    // when client closes its connection during writing.
    // Results in an unwanted server shutdown
    if (sigaction(SIGPIPE, &sig_ign_handler, 0) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot disable SIGPIPE\n");
        return -1;
    }

    // XXX TV
    // According to this:
    // http://stackoverflow.com/questions/7296923/different-signal-handler-for-thread-and-process-is-it-possible
    //
    // SIGPIPE is delivered to the thread generating it.
    // It might thus be possible to stop the session emitting it.

    if (sigaction(SIGTSTP, &sig_ign_handler, 0) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot disable SIGTSTP\n");
        return -1;
    }

    return 0;
}

// Crashed signals
//
// Write backtrace to the syslog. See:
// http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes

#define BACKTRACE_BUFF_SIZE 100

void crash_signal_handler(int sig)
{
    // The signal handler is called several times
    // on a segmentation fault (WHY ?).
    // So only display the backtrace the first time
    if (SignalHandler::s_interrupted)
        return;

    const char *sig_name;

    switch (sig) {
      case SIGBUS:
        sig_name = "(Bus Error)";
        break;
      case SIGSEGV:
        sig_name = "(Segmentation Fault)";
        break;
      default:
        sig_name = "(Unidentify signal)";
    }

    SignalHandler::kserver->syslog.print(SysLog::CRITICAL,
                              "CRASH: signal %d %s\n", sig, sig_name);

    void *buffer[BACKTRACE_BUFF_SIZE];
    size_t size = backtrace(buffer, BACKTRACE_BUFF_SIZE);
    char **messages = backtrace_symbols(buffer, size);

    if (messages == NULL) {
        SignalHandler::kserver->syslog.print(SysLog::ERROR,
                                             "No backtrace_symbols");
        goto exit;
    }

    for(unsigned int i = 0; i < size && messages != NULL; i++) {
        char *mangled_name = 0, *offset_begin = 0, *offset_end = 0;

        // Find parantheses and +address offset surrounding mangled name
        for (char *p = messages[i]; *p; ++p) {
            if (*p == '(') {
                mangled_name = p;
            }
            else if (*p == '+') {
                offset_begin = p;
            }
            else if (*p == ')') {
                offset_end = p;
                break;
            }
        }

        // If the line could be processed, attempt to demangle the symbol
        if (mangled_name && offset_begin && offset_end
            && mangled_name < offset_begin) {
            *mangled_name++ = '\0';
            *offset_begin++ = '\0';
            *offset_end++ = '\0';

            int status;
            char *real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);

            // If demangling is successful, output the demangled function name
            if (status == 0) {
                SignalHandler::kserver->syslog.print(SysLog::INFO,
                        "[bt]: (%d) %s : %s+%s%s\n",
                        i, messages[i], real_name, offset_begin, offset_end);
            } else { // Otherwise, output the mangled function name
                SignalHandler::kserver->syslog.print(SysLog::INFO,
                        "[bt]: (%d) %s : %s+%s%s\n",
                        i, messages[i], mangled_name, offset_begin, offset_end);
            }

            free(real_name);
        }
    }

    free(messages);

exit: // Exit Server
    SignalHandler::s_interrupted = 1;
}

int SignalHandler::set_crash_signals()
{
    struct sigaction sig_crash_handler;

    sig_crash_handler.sa_handler = crash_signal_handler;
    sigemptyset(&sig_crash_handler.sa_mask);
    sig_crash_handler.sa_flags = SA_RESTART | SA_SIGINFO;

    if (sigaction(SIGSEGV, &sig_crash_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGSEGV handler\n");
        return -1;
    }

    if (sigaction(SIGBUS, &sig_crash_handler, NULL) < 0) {
        kserver->syslog.print(SysLog::CRITICAL, "Cannot set SIGBUSs handler\n");
        return -1;
    }

    return 0;
}

} // namespace kserver
<|endoftext|>


<commit_before>
/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 *          Steve Reinhardt
 */

/**
 * @file
 * Definition of a simple bus bridge without buffering.
 */

#include <algorithm>

#include "base/range_ops.hh"
#include "base/trace.hh"
#include "mem/bridge.hh"
#include "params/Bridge.hh"

Bridge::BridgePort::BridgePort(const std::string &_name,
                               Bridge *_bridge, BridgePort *_otherPort,
                               int _delay, int _nack_delay, int _req_limit,
                               int _resp_limit,
                               std::vector<Range<Addr> > filter_ranges)
    : Port(_name, _bridge), bridge(_bridge), otherPort(_otherPort),
      delay(_delay), nackDelay(_nack_delay), filterRanges(filter_ranges),
      outstandingResponses(0), queuedRequests(0), inRetry(false),
      reqQueueLimit(_req_limit), respQueueLimit(_resp_limit), sendEvent(this)
{
}

Bridge::Bridge(Params *p)
    : MemObject(p),
      portA(p->name + "-portA", this, &portB, p->delay, p->nack_delay,
              p->req_size_a, p->resp_size_a, p->filter_ranges_a),
      portB(p->name + "-portB", this, &portA, p->delay, p->nack_delay,
              p->req_size_b, p->resp_size_b, p->filter_ranges_b),
      ackWrites(p->write_ack), _params(p)
{
    if (ackWrites)
        panic("No support for acknowledging writes\n");
}

Port *
Bridge::getPort(const std::string &if_name, int idx)
{
    BridgePort *port;

    if (if_name == "side_a")
        port = &portA;
    else if (if_name == "side_b")
        port = &portB;
    else
        return NULL;

    if (port->getPeer() != NULL && !port->getPeer()->isDefaultPort())
        panic("bridge side %s already connected to %s.",
                if_name, port->getPeer()->name());
    return port;
}

void
Bridge::init()
{
    // Make sure that both sides are connected to.
    if (!portA.isConnected() || !portB.isConnected())
        fatal("Both ports of bus bridge are not connected to a bus.\n");

    if (portA.peerBlockSize() != portB.peerBlockSize())
        fatal("Busses don't have the same block size... Not supported.\n");
}

bool
Bridge::BridgePort::respQueueFull()
{
    assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit);
    return outstandingResponses >= respQueueLimit;
}

bool
Bridge::BridgePort::reqQueueFull()
{
    assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit);
    return queuedRequests >= reqQueueLimit;
}

/** Function called by the port when the bus is receiving a Timing
 * transaction.*/
bool
Bridge::BridgePort::recvTiming(PacketPtr pkt)
{
    DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n",
                pkt->getSrc(), pkt->getDest(), pkt->getAddr());

    DPRINTF(BusBridge, "Local queue size: %d outreq: %d outresp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
    DPRINTF(BusBridge, "Remote queue size: %d outreq: %d outresp: %d\n",
                    otherPort->sendQueue.size(), otherPort->queuedRequests,
                    otherPort->outstandingResponses);

    if (pkt->isRequest() && otherPort->reqQueueFull()) {
        DPRINTF(BusBridge, "Remote queue full, nacking\n");
        nackRequest(pkt);
        return true;
    }

    if (pkt->needsResponse()) {
        if (respQueueFull()) {
            DPRINTF(BusBridge, "Local queue full, no space for response, nacking\n");
            DPRINTF(BusBridge, "queue size: %d outreq: %d outstanding resp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
            nackRequest(pkt);
            return true;
        } else {
            DPRINTF(BusBridge, "Request Needs response, reserving space\n");
            ++outstandingResponses;
        }
    }

    otherPort->queueForSendTiming(pkt);

    return true;
}

void
Bridge::BridgePort::nackRequest(PacketPtr pkt)
{
    // Nack the packet
    pkt->makeTimingResponse();
    pkt->setNacked();

    //put it on the list to send
    Tick readyTime = curTick + nackDelay;
    PacketBuffer *buf = new PacketBuffer(pkt, readyTime, true);

    // nothing on the list, add it and we're done
    if (sendQueue.empty()) {
        assert(!sendEvent.scheduled());
        schedule(sendEvent, readyTime);
        sendQueue.push_back(buf);
        return;
    }

    assert(sendEvent.scheduled() || inRetry);

    // does it go at the end?
    if (readyTime >= sendQueue.back()->ready) {
        sendQueue.push_back(buf);
        return;
    }

    // ok, somewhere in the middle, fun
    std::list<PacketBuffer*>::iterator i = sendQueue.begin();
    std::list<PacketBuffer*>::iterator end = sendQueue.end();
    std::list<PacketBuffer*>::iterator begin = sendQueue.begin();
    bool done = false;

    while (i != end && !done) {
        if (readyTime < (*i)->ready) {
            if (i == begin)
                reschedule(sendEvent, readyTime);
            sendQueue.insert(i,buf);
            done = true;
        }
        i++;
    }
    assert(done);
}

void
Bridge::BridgePort::queueForSendTiming(PacketPtr pkt)
{
    if (pkt->isResponse()) {
        // This is a response for a request we forwarded earlier.  The
        // corresponding PacketBuffer should be stored in the packet's
        // senderState field.

        PacketBuffer *buf = dynamic_cast<PacketBuffer*>(pkt->senderState);
        assert(buf != NULL);
        // set up new packet dest & senderState based on values saved
        // from original request
        buf->fixResponse(pkt);

        DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest());
        delete buf;
    }

    if (pkt->isRequest()) {
        ++queuedRequests;
    }

    Tick readyTime = curTick + delay;
    PacketBuffer *buf = new PacketBuffer(pkt, readyTime);

    // If we're about to put this packet at the head of the queue, we
    // need to schedule an event to do the transmit.  Otherwise there
    // should already be an event scheduled for sending the head
    // packet.
    if (sendQueue.empty()) {
        schedule(sendEvent, readyTime);
    }
    sendQueue.push_back(buf);
}

void
Bridge::BridgePort::trySend()
{
    assert(!sendQueue.empty());

    PacketBuffer *buf = sendQueue.front();

    assert(buf->ready <= curTick);

    PacketPtr pkt = buf->pkt;

    DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n",
            buf->origSrc, pkt->getDest(), pkt->getAddr());

    bool wasReq = pkt->isRequest();
    bool was_nacked_here = buf->nackedHere;

    // If the send was successful, make sure sender state was set to NULL
    // otherwise we could get a NACK back of a packet that didn't expect a
    // response and we would try to use freed memory.

    Packet::SenderState *old_sender_state = pkt->senderState;
    if (pkt->isRequest() && !buf->expectResponse)
        pkt->senderState = NULL;

    if (sendTiming(pkt)) {
        // send successful
        sendQueue.pop_front();
        buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it

        if (buf->expectResponse) {
            // Must wait for response
            DPRINTF(BusBridge, "  successful: awaiting response (%d)\n",
                    outstandingResponses);
        } else {
            // no response expected... deallocate packet buffer now.
            DPRINTF(BusBridge, "  successful: no response expected\n");
            delete buf;
        }

        if (wasReq)
            --queuedRequests;
        else if (!was_nacked_here)
            --outstandingResponses;

        // If there are more packets to send, schedule event to try again.
        if (!sendQueue.empty()) {
            buf = sendQueue.front();
            DPRINTF(BusBridge, "Scheduling next send\n");
            schedule(sendEvent, std::max(buf->ready, curTick + 1));
        }
    } else {
        DPRINTF(BusBridge, "  unsuccessful\n");
        pkt->senderState = old_sender_state;
        inRetry = true;
    }

    DPRINTF(BusBridge, "trySend: queue size: %d outreq: %d outstanding resp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
}

void
Bridge::BridgePort::recvRetry()
{
    inRetry = false;
    Tick nextReady = sendQueue.front()->ready;
    if (nextReady <= curTick)
        trySend();
    else
        schedule(sendEvent, nextReady);
}

/** Function called by the port when the bus is receiving a Atomic
 * transaction.*/
Tick
Bridge::BridgePort::recvAtomic(PacketPtr pkt)
{
    return delay + otherPort->sendAtomic(pkt);
}

/** Function called by the port when the bus is receiving a Functional
 * transaction.*/
void
Bridge::BridgePort::recvFunctional(PacketPtr pkt)
{
    std::list<PacketBuffer*>::iterator i;

    pkt->pushLabel(name());

    for (i = sendQueue.begin();  i != sendQueue.end(); ++i) {
        if (pkt->checkFunctional((*i)->pkt))
            return;
    }

    pkt->popLabel();

    // fall through if pkt still not satisfied
    otherPort->sendFunctional(pkt);
}

/** Function called by the port when the bus is receiving a status change.*/
void
Bridge::BridgePort::recvStatusChange(Port::Status status)
{
    otherPort->sendStatusChange(status);
}

void
Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp,
                                           bool &snoop)
{
    otherPort->getPeerAddressRanges(resp, snoop);
    FilterRangeList(filterRanges, resp);
    // we don't allow snooping across bridges
    snoop = false;
}

Bridge *
BridgeParams::create()
{
    return new Bridge(this);
}
<commit_msg>ruby: Added more info to bridge error message<commit_after>
/*
 * Copyright (c) 2006 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Ali Saidi
 *          Steve Reinhardt
 */

/**
 * @file
 * Definition of a simple bus bridge without buffering.
 */

#include <algorithm>

#include "base/range_ops.hh"
#include "base/trace.hh"
#include "mem/bridge.hh"
#include "params/Bridge.hh"

Bridge::BridgePort::BridgePort(const std::string &_name,
                               Bridge *_bridge, BridgePort *_otherPort,
                               int _delay, int _nack_delay, int _req_limit,
                               int _resp_limit,
                               std::vector<Range<Addr> > filter_ranges)
    : Port(_name, _bridge), bridge(_bridge), otherPort(_otherPort),
      delay(_delay), nackDelay(_nack_delay), filterRanges(filter_ranges),
      outstandingResponses(0), queuedRequests(0), inRetry(false),
      reqQueueLimit(_req_limit), respQueueLimit(_resp_limit), sendEvent(this)
{
}

Bridge::Bridge(Params *p)
    : MemObject(p),
      portA(p->name + "-portA", this, &portB, p->delay, p->nack_delay,
              p->req_size_a, p->resp_size_a, p->filter_ranges_a),
      portB(p->name + "-portB", this, &portA, p->delay, p->nack_delay,
              p->req_size_b, p->resp_size_b, p->filter_ranges_b),
      ackWrites(p->write_ack), _params(p)
{
    if (ackWrites)
        panic("No support for acknowledging writes\n");
}

Port *
Bridge::getPort(const std::string &if_name, int idx)
{
    BridgePort *port;

    if (if_name == "side_a")
        port = &portA;
    else if (if_name == "side_b")
        port = &portB;
    else
        return NULL;

    if (port->getPeer() != NULL && !port->getPeer()->isDefaultPort())
        panic("bridge side %s already connected to %s.",
                if_name, port->getPeer()->name());
    return port;
}

void
Bridge::init()
{
    // Make sure that both sides are connected to.
    if (!portA.isConnected() || !portB.isConnected())
        fatal("Both ports of bus bridge are not connected to a bus.\n");

    if (portA.peerBlockSize() != portB.peerBlockSize())
        fatal("port A size %d, port B size %d \n " \
              "Busses don't have the same block size... Not supported.\n",
              portA.peerBlockSize(), portB.peerBlockSize());
}

bool
Bridge::BridgePort::respQueueFull()
{
    assert(outstandingResponses >= 0 && outstandingResponses <= respQueueLimit);
    return outstandingResponses >= respQueueLimit;
}

bool
Bridge::BridgePort::reqQueueFull()
{
    assert(queuedRequests >= 0 && queuedRequests <= reqQueueLimit);
    return queuedRequests >= reqQueueLimit;
}

/** Function called by the port when the bus is receiving a Timing
 * transaction.*/
bool
Bridge::BridgePort::recvTiming(PacketPtr pkt)
{
    DPRINTF(BusBridge, "recvTiming: src %d dest %d addr 0x%x\n",
                pkt->getSrc(), pkt->getDest(), pkt->getAddr());

    DPRINTF(BusBridge, "Local queue size: %d outreq: %d outresp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
    DPRINTF(BusBridge, "Remote queue size: %d outreq: %d outresp: %d\n",
                    otherPort->sendQueue.size(), otherPort->queuedRequests,
                    otherPort->outstandingResponses);

    if (pkt->isRequest() && otherPort->reqQueueFull()) {
        DPRINTF(BusBridge, "Remote queue full, nacking\n");
        nackRequest(pkt);
        return true;
    }

    if (pkt->needsResponse()) {
        if (respQueueFull()) {
            DPRINTF(BusBridge, "Local queue full, no space for response, nacking\n");
            DPRINTF(BusBridge, "queue size: %d outreq: %d outstanding resp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
            nackRequest(pkt);
            return true;
        } else {
            DPRINTF(BusBridge, "Request Needs response, reserving space\n");
            ++outstandingResponses;
        }
    }

    otherPort->queueForSendTiming(pkt);

    return true;
}

void
Bridge::BridgePort::nackRequest(PacketPtr pkt)
{
    // Nack the packet
    pkt->makeTimingResponse();
    pkt->setNacked();

    //put it on the list to send
    Tick readyTime = curTick + nackDelay;
    PacketBuffer *buf = new PacketBuffer(pkt, readyTime, true);

    // nothing on the list, add it and we're done
    if (sendQueue.empty()) {
        assert(!sendEvent.scheduled());
        schedule(sendEvent, readyTime);
        sendQueue.push_back(buf);
        return;
    }

    assert(sendEvent.scheduled() || inRetry);

    // does it go at the end?
    if (readyTime >= sendQueue.back()->ready) {
        sendQueue.push_back(buf);
        return;
    }

    // ok, somewhere in the middle, fun
    std::list<PacketBuffer*>::iterator i = sendQueue.begin();
    std::list<PacketBuffer*>::iterator end = sendQueue.end();
    std::list<PacketBuffer*>::iterator begin = sendQueue.begin();
    bool done = false;

    while (i != end && !done) {
        if (readyTime < (*i)->ready) {
            if (i == begin)
                reschedule(sendEvent, readyTime);
            sendQueue.insert(i,buf);
            done = true;
        }
        i++;
    }
    assert(done);
}

void
Bridge::BridgePort::queueForSendTiming(PacketPtr pkt)
{
    if (pkt->isResponse()) {
        // This is a response for a request we forwarded earlier.  The
        // corresponding PacketBuffer should be stored in the packet's
        // senderState field.

        PacketBuffer *buf = dynamic_cast<PacketBuffer*>(pkt->senderState);
        assert(buf != NULL);
        // set up new packet dest & senderState based on values saved
        // from original request
        buf->fixResponse(pkt);

        DPRINTF(BusBridge, "response, new dest %d\n", pkt->getDest());
        delete buf;
    }

    if (pkt->isRequest()) {
        ++queuedRequests;
    }

    Tick readyTime = curTick + delay;
    PacketBuffer *buf = new PacketBuffer(pkt, readyTime);

    // If we're about to put this packet at the head of the queue, we
    // need to schedule an event to do the transmit.  Otherwise there
    // should already be an event scheduled for sending the head
    // packet.
    if (sendQueue.empty()) {
        schedule(sendEvent, readyTime);
    }
    sendQueue.push_back(buf);
}

void
Bridge::BridgePort::trySend()
{
    assert(!sendQueue.empty());

    PacketBuffer *buf = sendQueue.front();

    assert(buf->ready <= curTick);

    PacketPtr pkt = buf->pkt;

    DPRINTF(BusBridge, "trySend: origSrc %d dest %d addr 0x%x\n",
            buf->origSrc, pkt->getDest(), pkt->getAddr());

    bool wasReq = pkt->isRequest();
    bool was_nacked_here = buf->nackedHere;

    // If the send was successful, make sure sender state was set to NULL
    // otherwise we could get a NACK back of a packet that didn't expect a
    // response and we would try to use freed memory.

    Packet::SenderState *old_sender_state = pkt->senderState;
    if (pkt->isRequest() && !buf->expectResponse)
        pkt->senderState = NULL;

    if (sendTiming(pkt)) {
        // send successful
        sendQueue.pop_front();
        buf->pkt = NULL; // we no longer own packet, so it's not safe to look at it

        if (buf->expectResponse) {
            // Must wait for response
            DPRINTF(BusBridge, "  successful: awaiting response (%d)\n",
                    outstandingResponses);
        } else {
            // no response expected... deallocate packet buffer now.
            DPRINTF(BusBridge, "  successful: no response expected\n");
            delete buf;
        }

        if (wasReq)
            --queuedRequests;
        else if (!was_nacked_here)
            --outstandingResponses;

        // If there are more packets to send, schedule event to try again.
        if (!sendQueue.empty()) {
            buf = sendQueue.front();
            DPRINTF(BusBridge, "Scheduling next send\n");
            schedule(sendEvent, std::max(buf->ready, curTick + 1));
        }
    } else {
        DPRINTF(BusBridge, "  unsuccessful\n");
        pkt->senderState = old_sender_state;
        inRetry = true;
    }

    DPRINTF(BusBridge, "trySend: queue size: %d outreq: %d outstanding resp: %d\n",
                    sendQueue.size(), queuedRequests, outstandingResponses);
}

void
Bridge::BridgePort::recvRetry()
{
    inRetry = false;
    Tick nextReady = sendQueue.front()->ready;
    if (nextReady <= curTick)
        trySend();
    else
        schedule(sendEvent, nextReady);
}

/** Function called by the port when the bus is receiving a Atomic
 * transaction.*/
Tick
Bridge::BridgePort::recvAtomic(PacketPtr pkt)
{
    return delay + otherPort->sendAtomic(pkt);
}

/** Function called by the port when the bus is receiving a Functional
 * transaction.*/
void
Bridge::BridgePort::recvFunctional(PacketPtr pkt)
{
    std::list<PacketBuffer*>::iterator i;

    pkt->pushLabel(name());

    for (i = sendQueue.begin();  i != sendQueue.end(); ++i) {
        if (pkt->checkFunctional((*i)->pkt))
            return;
    }

    pkt->popLabel();

    // fall through if pkt still not satisfied
    otherPort->sendFunctional(pkt);
}

/** Function called by the port when the bus is receiving a status change.*/
void
Bridge::BridgePort::recvStatusChange(Port::Status status)
{
    otherPort->sendStatusChange(status);
}

void
Bridge::BridgePort::getDeviceAddressRanges(AddrRangeList &resp,
                                           bool &snoop)
{
    otherPort->getPeerAddressRanges(resp, snoop);
    FilterRangeList(filterRanges, resp);
    // we don't allow snooping across bridges
    snoop = false;
}

Bridge *
BridgeParams::create()
{
    return new Bridge(this);
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2015, Robert Escriva
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Replicant nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// POSIX
#include <poll.h>

// STL
#include <memory>

// po6
#include <po6/time.h>

// Replicant
#include "common/bootstrap.h"
#include "common/configuration.h"
#include "common/network_msgtype.h"

using replicant::bootstrap;

bool
bootstrap :: parse_hosts(const char* conn_str,
                         std::vector<po6::net::hostname>* hosts)
{
    const size_t conn_str_sz = strlen(conn_str);
    std::vector<char> cs(conn_str, conn_str + conn_str_sz + 1);
    char* ptr = &cs[0];
    char* const end = ptr + conn_str_sz;

    while (ptr < end)
    {
        char* eoh = strchr(ptr, ',');
        eoh = eoh ? eoh : end;
        *eoh = '\0';
        char* colon = strrchr(ptr, ':');

        if (colon == NULL)
        {
            hosts->push_back(po6::net::hostname(ptr, 1982));
            ptr = eoh + 1;
            continue;
        }

        char* tmp = NULL;
        errno = 0;
        unsigned long port = strtoul(colon + 1, &tmp, 10);

        if (errno != 0)
        {
            return false;
        }

        std::string host;

        if (*ptr == '[' && colon > ptr && *(colon - 1) == ']')
        {
            host.assign(ptr + 1, colon - 1);
        }
        else
        {
            host.assign(ptr, colon);
        }

        hosts->push_back(po6::net::hostname(host.c_str(), port));
        ptr = eoh + 1;
    }

    return true;
}

std::string
bootstrap :: conn_str(const po6::net::hostname* hns, size_t hns_sz)
{
    std::ostringstream ostr;

    for (size_t i = 0; i < hns_sz; ++i)
    {
        if (i > 0)
        {
            ostr << ",";
        }

        ostr << hns[i];
    }

    return ostr.str();
}

bootstrap :: bootstrap()
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
}

bootstrap :: bootstrap(const char* host, uint16_t port)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_hosts.push_back(po6::net::hostname(host, port));
}

bootstrap :: bootstrap(const char* cs)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_valid = parse_hosts(cs, &m_hosts);
}

bootstrap :: bootstrap(const char* host, uint16_t port, const char* cs)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_valid = parse_hosts(cs, &m_hosts);
    m_hosts.push_back(po6::net::hostname(host, port));
}

bootstrap :: bootstrap(const std::vector<po6::net::hostname>& h)
    : m_hosts(h)
    , m_conns()
    , m_valid(true)
{
}

bootstrap :: bootstrap(const bootstrap& other)
    : m_hosts(other.m_hosts)
    , m_conns()
    , m_valid(other.m_valid)
{
}

bootstrap :: ~bootstrap() throw ()
{
}

bool
bootstrap :: valid() const
{
    if (!m_valid)
    {
        return false;
    }

    for (size_t i = 0; i < m_hosts.size(); ++i)
    {
        if (m_hosts[i].port <= 0 || m_hosts[i].port >= (1 << 16))
        {
            return false;
        }
    }

    return true;
}

replicant_returncode
bootstrap :: do_it(int timeout, configuration* config, e::error* err)
{
    if (!m_valid)
    {
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "invalid bootstrap connection string";
        return REPLICANT_COMM_FAILED;
    }

    if (m_hosts.empty())
    {
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "no hosts to bootstrap from";
        return REPLICANT_COMM_FAILED;
    }

    m_conns.resize(m_hosts.size());
    replicant_returncode rc = REPLICANT_TIMEOUT;
    err->set_loc(__FILE__, __LINE__);
    err->set_msg() << "timed out connecting to the cluster";
    std::vector<pollfd> pfds;
    pfds.resize(m_conns.size());

    for (size_t i = 0; i < m_hosts.size(); ++i)
    {
        if (!m_conns[i].get())
        {
            m_conns[i].reset(busybee_single::create(m_hosts[i]));
        }
    }

    while (true)
    {
        for (size_t i = 0; i < m_conns.size(); ++i)
        {
            rc = send_bootstrap(i, err);
            pfds[i].fd = m_conns[i]->poll_fd();
            pfds[i].events = POLLIN|POLLOUT|POLLHUP|POLLERR;
            pfds[i].revents = 0;
        }

        rc = REPLICANT_TIMEOUT;
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "timed out connecting to the cluster";
        int ret = poll(&pfds[0], pfds.size(), timeout);

        if (ret < 0)
        {
            int e = errno;
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "poll failed: " << po6::strerror(e) << " [" << po6::strerrno(e) << "]";
            errno = e;
            return REPLICANT_SEE_ERRNO;
        }
        else if (ret == 0)
        {
            return rc;
        }

        for (size_t i = 0; i < m_conns.size(); ++i)
        {
            if (pfds[i].fd < 0 || pfds[i].revents == 0)
            {
                continue;
            }

            std::auto_ptr<e::buffer> msg;

            switch (m_conns[i]->recv(timeout, &msg))
            {
                case BUSYBEE_SUCCESS:
                    break;
                case BUSYBEE_TIMEOUT:
                    break;
                case BUSYBEE_SHUTDOWN:
                case BUSYBEE_DISRUPTED:
                case BUSYBEE_EXTERNAL:
                case BUSYBEE_INTERRUPTED:
                case BUSYBEE_SEE_ERRNO:
                    err->set_loc(__FILE__, __LINE__);
                    err->set_msg() << "communication error with " << m_hosts[i];
                    rc = REPLICANT_COMM_FAILED;
                    continue;
                default:
                    abort();
            }

            if (!msg.get())
            {
                continue;
            }

            network_msgtype mt = REPLNET_NOP;
            e::unpacker up = msg->unpack_from(BUSYBEE_HEADER_SIZE);
            up >> mt >> *config;

            if (up.error() || mt != REPLNET_BOOTSTRAP || !config->validate())
            {
                err->set_loc(__FILE__, __LINE__);
                err->set_msg() << "received a malformed bootstrap message from " << m_hosts[i];
                rc = REPLICANT_COMM_FAILED;
                continue;
            }

            return REPLICANT_SUCCESS;
        }
    }
}

std::string
bootstrap :: conn_str() const
{
    return conn_str(&m_hosts[0], m_hosts.size());
}

bootstrap&
bootstrap :: operator = (const bootstrap& rhs)
{
    if (this != &rhs)
    {
        m_hosts = rhs.m_hosts;
        m_valid = rhs.m_valid;
    }

    return *this;
}

replicant_returncode
bootstrap :: send_bootstrap(size_t i, e::error* err)
{
    replicant_returncode rc = REPLICANT_SUCCESS;
    const size_t sz = BUSYBEE_HEADER_SIZE
                    + pack_size(REPLNET_BOOTSTRAP);
    std::auto_ptr<e::buffer> msg(e::buffer::create(sz));
    msg->pack_at(BUSYBEE_HEADER_SIZE) << REPLNET_BOOTSTRAP;

    switch (m_conns[i]->send(msg))
    {
        case BUSYBEE_SUCCESS:
            break;
        case BUSYBEE_TIMEOUT:
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "timed out connecting to " << m_hosts[i];
            rc = REPLICANT_TIMEOUT;
            break;
        case BUSYBEE_SHUTDOWN:
        case BUSYBEE_DISRUPTED:
        case BUSYBEE_EXTERNAL:
        case BUSYBEE_INTERRUPTED:
        case BUSYBEE_SEE_ERRNO:
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "communication error with " << m_hosts[i];
            rc = REPLICANT_COMM_FAILED;
            break;
        default:
            abort();
    }

    return rc;
}

std::ostream&
replicant :: operator << (std::ostream& lhs, const bootstrap& rhs)
{
    return lhs << rhs.conn_str();
}
<commit_msg>Clear bootstrap connections on success<commit_after>// Copyright (c) 2012-2015, Robert Escriva
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice,
//       this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright
//       notice, this list of conditions and the following disclaimer in the
//       documentation and/or other materials provided with the distribution.
//     * Neither the name of Replicant nor the names of its contributors may be
//       used to endorse or promote products derived from this software without
//       specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.

// POSIX
#include <poll.h>

// STL
#include <memory>

// po6
#include <po6/time.h>

// Replicant
#include "common/bootstrap.h"
#include "common/configuration.h"
#include "common/network_msgtype.h"

using replicant::bootstrap;

bool
bootstrap :: parse_hosts(const char* conn_str,
                         std::vector<po6::net::hostname>* hosts)
{
    const size_t conn_str_sz = strlen(conn_str);
    std::vector<char> cs(conn_str, conn_str + conn_str_sz + 1);
    char* ptr = &cs[0];
    char* const end = ptr + conn_str_sz;

    while (ptr < end)
    {
        char* eoh = strchr(ptr, ',');
        eoh = eoh ? eoh : end;
        *eoh = '\0';
        char* colon = strrchr(ptr, ':');

        if (colon == NULL)
        {
            hosts->push_back(po6::net::hostname(ptr, 1982));
            ptr = eoh + 1;
            continue;
        }

        char* tmp = NULL;
        errno = 0;
        unsigned long port = strtoul(colon + 1, &tmp, 10);

        if (errno != 0)
        {
            return false;
        }

        std::string host;

        if (*ptr == '[' && colon > ptr && *(colon - 1) == ']')
        {
            host.assign(ptr + 1, colon - 1);
        }
        else
        {
            host.assign(ptr, colon);
        }

        hosts->push_back(po6::net::hostname(host.c_str(), port));
        ptr = eoh + 1;
    }

    return true;
}

std::string
bootstrap :: conn_str(const po6::net::hostname* hns, size_t hns_sz)
{
    std::ostringstream ostr;

    for (size_t i = 0; i < hns_sz; ++i)
    {
        if (i > 0)
        {
            ostr << ",";
        }

        ostr << hns[i];
    }

    return ostr.str();
}

bootstrap :: bootstrap()
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
}

bootstrap :: bootstrap(const char* host, uint16_t port)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_hosts.push_back(po6::net::hostname(host, port));
}

bootstrap :: bootstrap(const char* cs)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_valid = parse_hosts(cs, &m_hosts);
}

bootstrap :: bootstrap(const char* host, uint16_t port, const char* cs)
    : m_hosts()
    , m_conns()
    , m_valid(true)
{
    m_valid = parse_hosts(cs, &m_hosts);
    m_hosts.push_back(po6::net::hostname(host, port));
}

bootstrap :: bootstrap(const std::vector<po6::net::hostname>& h)
    : m_hosts(h)
    , m_conns()
    , m_valid(true)
{
}

bootstrap :: bootstrap(const bootstrap& other)
    : m_hosts(other.m_hosts)
    , m_conns()
    , m_valid(other.m_valid)
{
}

bootstrap :: ~bootstrap() throw ()
{
}

bool
bootstrap :: valid() const
{
    if (!m_valid)
    {
        return false;
    }

    for (size_t i = 0; i < m_hosts.size(); ++i)
    {
        if (m_hosts[i].port <= 0 || m_hosts[i].port >= (1 << 16))
        {
            return false;
        }
    }

    return true;
}

replicant_returncode
bootstrap :: do_it(int timeout, configuration* config, e::error* err)
{
    if (!m_valid)
    {
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "invalid bootstrap connection string";
        return REPLICANT_COMM_FAILED;
    }

    if (m_hosts.empty())
    {
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "no hosts to bootstrap from";
        return REPLICANT_COMM_FAILED;
    }

    m_conns.resize(m_hosts.size());
    replicant_returncode rc = REPLICANT_TIMEOUT;
    err->set_loc(__FILE__, __LINE__);
    err->set_msg() << "timed out connecting to the cluster";
    std::vector<pollfd> pfds;
    pfds.resize(m_conns.size());

    for (size_t i = 0; i < m_hosts.size(); ++i)
    {
        if (!m_conns[i].get())
        {
            m_conns[i].reset(busybee_single::create(m_hosts[i]));
        }
    }

    while (true)
    {
        for (size_t i = 0; i < m_conns.size(); ++i)
        {
            rc = send_bootstrap(i, err);
            pfds[i].fd = m_conns[i]->poll_fd();
            pfds[i].events = POLLIN|POLLOUT|POLLHUP|POLLERR;
            pfds[i].revents = 0;
        }

        rc = REPLICANT_TIMEOUT;
        err->set_loc(__FILE__, __LINE__);
        err->set_msg() << "timed out connecting to the cluster";
        int ret = poll(&pfds[0], pfds.size(), timeout);

        if (ret < 0)
        {
            int e = errno;
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "poll failed: " << po6::strerror(e) << " [" << po6::strerrno(e) << "]";
            errno = e;
            return REPLICANT_SEE_ERRNO;
        }
        else if (ret == 0)
        {
            return rc;
        }

        for (size_t i = 0; i < m_conns.size(); ++i)
        {
            if (pfds[i].fd < 0 || pfds[i].revents == 0)
            {
                continue;
            }

            std::auto_ptr<e::buffer> msg;

            switch (m_conns[i]->recv(timeout, &msg))
            {
                case BUSYBEE_SUCCESS:
                    break;
                case BUSYBEE_TIMEOUT:
                    break;
                case BUSYBEE_SHUTDOWN:
                case BUSYBEE_DISRUPTED:
                case BUSYBEE_EXTERNAL:
                case BUSYBEE_INTERRUPTED:
                case BUSYBEE_SEE_ERRNO:
                    err->set_loc(__FILE__, __LINE__);
                    err->set_msg() << "communication error with " << m_hosts[i];
                    rc = REPLICANT_COMM_FAILED;
                    continue;
                default:
                    abort();
            }

            if (!msg.get())
            {
                continue;
            }

            network_msgtype mt = REPLNET_NOP;
            e::unpacker up = msg->unpack_from(BUSYBEE_HEADER_SIZE);
            up >> mt >> *config;

            if (up.error() || mt != REPLNET_BOOTSTRAP || !config->validate())
            {
                err->set_loc(__FILE__, __LINE__);
                err->set_msg() << "received a malformed bootstrap message from " << m_hosts[i];
                rc = REPLICANT_COMM_FAILED;
                continue;
            }

            m_conns.clear();
            return REPLICANT_SUCCESS;
        }
    }
}

std::string
bootstrap :: conn_str() const
{
    return conn_str(&m_hosts[0], m_hosts.size());
}

bootstrap&
bootstrap :: operator = (const bootstrap& rhs)
{
    if (this != &rhs)
    {
        m_hosts = rhs.m_hosts;
        m_valid = rhs.m_valid;
    }

    return *this;
}

replicant_returncode
bootstrap :: send_bootstrap(size_t i, e::error* err)
{
    replicant_returncode rc = REPLICANT_SUCCESS;
    const size_t sz = BUSYBEE_HEADER_SIZE
                    + pack_size(REPLNET_BOOTSTRAP);
    std::auto_ptr<e::buffer> msg(e::buffer::create(sz));
    msg->pack_at(BUSYBEE_HEADER_SIZE) << REPLNET_BOOTSTRAP;

    switch (m_conns[i]->send(msg))
    {
        case BUSYBEE_SUCCESS:
            break;
        case BUSYBEE_TIMEOUT:
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "timed out connecting to " << m_hosts[i];
            rc = REPLICANT_TIMEOUT;
            break;
        case BUSYBEE_SHUTDOWN:
        case BUSYBEE_DISRUPTED:
        case BUSYBEE_EXTERNAL:
        case BUSYBEE_INTERRUPTED:
        case BUSYBEE_SEE_ERRNO:
            err->set_loc(__FILE__, __LINE__);
            err->set_msg() << "communication error with " << m_hosts[i];
            rc = REPLICANT_COMM_FAILED;
            break;
        default:
            abort();
    }

    return rc;
}

std::ostream&
replicant :: operator << (std::ostream& lhs, const bootstrap& rhs)
{
    return lhs << rhs.conn_str();
}
<|endoftext|>


<commit_before>#pragma once
//=========================================================================//
/*!	@file
	@brief	DMAC マネージャー @n
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=========================================================================//
#include "common/renesas.hpp"
#include "common/intr_utils.hpp"
#include "common/vect.h"

/// RX24T には DMAC が無い為、エラーにする。
#ifdef SIG_RX24T
#  error "dmac_man.hpp not support for RX24T"
#endif

namespace device {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  DMAC マネージャー・クラス
		@param[in]	DMAC	DMA コントローラー
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template<class DMAC, class TASK = utils::null_task>
	class dmac_man {
	public:

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief  タイプ
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class type : uint8_t {
		};

	private:
		static TASK	task_;

		uint32_t	src_;

		static INTERRUPT_FUNC void dmac_task_() noexcept
		{
			task_();
			DMAC::DMSTS.DTIF = 0;
		}

		void set_vector_(uint32_t lvl, ICU::VECTOR vec) const noexcept
		{
			if(lvl) {
				set_interrupt_task(dmac_task_, static_cast<uint32_t>(vec));
			} else {
				set_interrupt_task(nullptr, static_cast<uint32_t>(vec));
			}
			icu_mgr::set_level(DMAC::get_peripheral(), lvl);
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		 */
		//-----------------------------------------------------------------//
		dmac_man() noexcept : src_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	開始
			@param[in]	lvl		割り込みレベル
		 */
		//-----------------------------------------------------------------//
		void start(uint32_t lvl = 0) const noexcept
		{
			power_cfg::turn(DMAC::get_peripheral());

			set_vector_(lvl, DMAC::get_vec());

			if(lvl) {
				DMAC::DMINT.DTIE = 1;
			} else {
				DMAC::DMINT.DTIE = 0;
			}

			DMAST.DMST = 1;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	動作中か検査
			@return 動作中なら「true」
		 */
		//-----------------------------------------------------------------//
		bool probe() const noexcept {
			return DMAC::DMSTS.ACT();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	メモリー・コピー
			@param[in]	src		コピー元
			@param[in]	dst		コピー先
			@param[in]	len		コピー数（バイト）
			@return コピーが出来ない場合「false」
		 */
		//-----------------------------------------------------------------//
		bool fill(uint32_t src, void* dst, uint32_t len) noexcept
		{
			if(len == 0 || len >= 0x04000000) return false;

			src_ = src;

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	コピー・メモリー
			@param[in]	src		コピー元
			@param[in]	dst		コピー先
			@param[in]	len		コピー数（バイト単位）@n
								※アドレスが 32 ビット単位の場合、最大 65535 x4 バイト
								※アドレスが 16 ビット単位の場合、最大 65535 x2 バイト
								※アドレスが 8  ビット単位の場合、最大 65535 x1 バイト
			@return コピーが出来ない場合「false」（パラメーターが異常）
		 */
		//-----------------------------------------------------------------//
		bool copy(const void* src, void* dst, uint32_t len) const noexcept
		{
			if(len == 0) return false;

			DMAC::DMCNT.DTE = 0;

			uint32_t src_adr = reinterpret_cast<uint32_t>(src);
			uint32_t dst_adr = reinterpret_cast<uint32_t>(dst);
			uint8_t sz = 0;
			if(len < 4) ;
			else if((src_adr & 3) == 0 && (dst_adr & 3) == 0 && (len & 3) == 0) {  // 32 bits DMA
				sz = 2;
				len >>= 2;
			} else if((src_adr & 1) == 0 && (dst_adr & 1) == 0 && (len & 1) == 0) {  // 16 bits DMA
				sz = 1;
				len >>= 1;
			}

			// 転送元 (+1)、転送先 (+1)
			DMAC::DMAMD = DMAC::DMAMD.DM.b(0b10) | DMAC::DMAMD.SM.b(0b10);

			DMAC::DMTMD = DMAC::DMTMD.DCTG.b(0b00) | DMAC::DMTMD.SZ.b(sz) |
						  DMAC::DMTMD.DTS.b(0b10) | DMAC::DMTMD.MD.b(0b00);

			DMAC::DMSAR = src_adr;
			DMAC::DMDAR = dst_adr;
			DMAC::DMCRA = len;

			DMAC::DMCNT.DTE = 1;

			// CLRS=0 の場合、１回の転送で終了
			DMAC::DMREQ = DMAC::DMREQ.SWREQ.b() | DMAC::DMREQ.CLRS.b();

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  TASK クラスの参照
			@return TASK クラス
		*/
		//-----------------------------------------------------------------//
		static TASK& at_task() noexcept { return task_; }
	};

	template <class DMAC, class TASK> TASK dmac_man<DMAC, TASK>::task_;
}
<commit_msg>update: last<commit_after>#pragma once
//=========================================================================//
/*!	@file
	@brief	DMAC マネージャー @n
    @author 平松邦仁 (hira@rvf-rc45.net)
	@copyright	Copyright (C) 2018 Kunihito Hiramatsu @n
				Released under the MIT license @n
				https://github.com/hirakuni45/RX/blob/master/LICENSE
*/
//=========================================================================//
#include "common/renesas.hpp"
#include "common/intr_utils.hpp"
#include "common/vect.h"

/// RX24T には DMAC が無い為、エラーにする。
#ifdef SIG_RX24T
#  error "dmac_man.hpp not support for RX24T"
#endif

namespace device {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  DMAC マネージャー・クラス
		@param[in]	DMAC	DMA コントローラー
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	template<class DMAC, class TASK = utils::null_task>
	class dmac_man {
	public:

		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		/*!
			@brief  転送タイプ
		*/
		//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
		enum class trans_type : uint8_t {
			SN_DP_8,		///< ソース固定、ディストネーション＋（8 bits）
			SP_DN_8,		///< ソース＋、ディストネーション固定（8 bits）
			SN_DP_16,		///< ソース固定、ディストネーション＋（16 bits）
			SP_DN_16,		///< ソース＋、ディストネーション固定（16 bits）
			SN_DP_32,		///< ソース固定、ディストネーション＋（32 bits）
			SP_DN_32,		///< ソース＋、ディストネーション固定（32 bits）
		};

	private:
		static TASK	task_;
		uint8_t		level_;

		static INTERRUPT_FUNC void dmac_task_() noexcept
		{
			task_();
			DMAC::DMSTS.DTIF = 0;
		}

		void set_vector_(uint32_t lvl, ICU::VECTOR vec) const noexcept
		{
			if(lvl) {
				set_interrupt_task(dmac_task_, static_cast<uint32_t>(vec));
			} else {
				set_interrupt_task(nullptr, static_cast<uint32_t>(vec));
			}
			icu_mgr::set_level(DMAC::get_peripheral(), lvl);
		}

		static bool trans_inc_(uint32_t src_adr, uint32_t dst_adr, uint32_t len) noexcept
		{
			uint8_t sz = 0;
			if(len < 4) ;
			else if((src_adr & 3) == 0 && (dst_adr & 3) == 0 && (len & 3) == 0) {  // 32 bits DMA
				sz = 2;
				len >>= 2;
			} else if((src_adr & 1) == 0 && (dst_adr & 1) == 0 && (len & 1) == 0) {  // 16 bits DMA
				sz = 1;
				len >>= 1;
			}
			if(len > 65535) return false;

			// 転送元 (+1)、転送先 (+1)
			DMAC::DMAMD = DMAC::DMAMD.DM.b(0b10) | DMAC::DMAMD.SM.b(0b10);

			DMAC::DMTMD = DMAC::DMTMD.DCTG.b(0b00) | DMAC::DMTMD.SZ.b(sz) |
						  DMAC::DMTMD.DTS.b(0b10) | DMAC::DMTMD.MD.b(0b00);

			DMAC::DMSAR = src_adr;
			DMAC::DMDAR = dst_adr;
			DMAC::DMCRA = len;

			return true;
		}

		static bool trans_dec_(uint32_t src_adr, uint32_t dst_adr, uint32_t len) noexcept
		{
			uint8_t sz = 0;
			if(len < 4) ;
			else if((src_adr & 3) == 0 && (dst_adr & 3) == 0 && (len & 3) == 0) {  // 32 bits DMA
				sz = 2;
				len >>= 2;
			} else if((src_adr & 1) == 0 && (dst_adr & 1) == 0 && (len & 1) == 0) {  // 16 bits DMA
				sz = 1;
				len >>= 1;
			}
			if(len > 65535) return false;

			// 転送元 (-1)、転送先 (-1)
			DMAC::DMAMD = DMAC::DMAMD.DM.b(0b11) | DMAC::DMAMD.SM.b(0b11);

			DMAC::DMTMD = DMAC::DMTMD.DCTG.b(0b00) | DMAC::DMTMD.SZ.b(sz) |
						  DMAC::DMTMD.DTS.b(0b10) | DMAC::DMTMD.MD.b(0b00);

			DMAC::DMSAR = src_adr + len - sz;
			DMAC::DMDAR = dst_adr + len - sz;
			DMAC::DMCRA = len;

			return true;
		}

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief	コンストラクター
		 */
		//-----------------------------------------------------------------//
		dmac_man() noexcept : level_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief	開始（メモリー操作関係）
			@param[in]	lvl		割り込みレベル（０以上）@n
								※無指定（０）なら割り込みを起動しない。
		 */
		//-----------------------------------------------------------------//
		void start(uint32_t lvl = 0) noexcept
		{
			power_cfg::turn(DMAC::get_peripheral());

			set_vector_(lvl, DMAC::get_vec());

			level_ = lvl;

			DMAST.DMST = 1;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	割り込み要因による開始
			@param[in]	tft		転送タイプ
			@param[in]	src		元アドレス
			@param[in]	dst		先アドレス
			@param[in]	lim		転送リミット
			@return 成功なら「true」
		 */
		//-----------------------------------------------------------------//
		bool start(trans_type tft, uint32_t src, uint32_t dst, uint32_t lim) noexcept
		{
			power_cfg::turn(DMAC::get_peripheral());

#if 0
			// 転送元 (+1)、転送先 (+1)
			DMAC::DMAMD = DMAC::DMAMD.DM.b(0b10) | DMAC::DMAMD.SM.b(0b10);

			DMAC::DMTMD = DMAC::DMTMD.DCTG.b(0b00) | DMAC::DMTMD.SZ.b(sz) |
						  DMAC::DMTMD.DTS.b(0b10) | DMAC::DMTMD.MD.b(0b00);

#endif

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	動作中か検査
			@return 動作中なら「true」
		 */
		//-----------------------------------------------------------------//
		bool probe() const noexcept {
			return DMAC::DMSTS.ACT();
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	定数で埋める
			@param[out]	org		定数先頭 @n
								定数をあらかじめ先頭に書き込んでおく
			@param[in]	frz		定数サイズ（バイト）
			@param[in]	len		埋める数（バイト）
			@param[in]	tae		終了時タスクを起動する場合「true」
			@return 出来ない場合「false」
		 */
		//-----------------------------------------------------------------//
		bool fill(void* org, uint32_t frz, uint32_t len, bool tae = false) noexcept
		{
			if(org == nullptr || frz == 0 || len == 0 || frz > len) return false;

			DMAC::DMCNT.DTE = 0;  // 念のため停止させる。

			uint32_t src_adr = reinterpret_cast<uint32_t>(org);
			uint32_t dst_adr = src_adr + frz;
			len -= frz;

			if(!trans_inc_(src_adr, dst_adr, len)) {
				return false;
			}

			if(tae && level_ > 0) {
				DMAC::DMINT.DTIE = 1;
			} else {
				DMAC::DMINT.DTIE = 0;
			}

			DMAC::DMCNT.DTE = 1;

			// CLRS=0 の場合、１回の転送で終了
			DMAC::DMREQ = DMAC::DMREQ.SWREQ.b() | DMAC::DMREQ.CLRS.b();

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	定数で埋める
			@param[out]	org		定数先頭 @n
			@param[in]	val		定数
			@param[in]	len		埋める数（バイト）
			@param[in]	tae		終了時タスクを起動する場合「true」
			@return 出来ない場合「false」
		 */
		//-----------------------------------------------------------------//
		bool memset(void* org, uint8_t val, uint32_t len, bool tae = false) noexcept
		{
			if(org == nullptr || len == 0) return false;

			uint8_t* dst = static_cast<uint8_t*>(org);
			*dst++ = val;
			uint32_t frz = 1;
			uint32_t adr = reinterpret_cast<uint32_t>(org);
			if((adr & 1) == 0 && (len & 1) == 0 && len > 2) {
				++frz;
				*dst++ = val;
			}
			if((adr & 3) == 0 && (len & 3) == 0 && len > 4) {
				frz += 2;
				*dst++ = val;
				*dst++ = val;
			}
			return fill(org, frz, len, tae);
		}

		//-----------------------------------------------------------------//
		/*!
			@brief	コピー・メモリー @n
					オーバーラップする場合は、コピーする順番を自動で変更 @n
					※特定の値で埋める場合は「fill」を使う。
			@param[in]	src		コピー元
			@param[in]	dst		コピー先
			@param[in]	len		コピー数（バイト単位）@n
								※アドレスが 32 ビット単位の場合、最大 65535 x4 バイト
								※アドレスが 16 ビット単位の場合、最大 65535 x2 バイト
								※アドレスが 8  ビット単位の場合、最大 65535 x1 バイト
			@param[in]	tae		終了時タスクを起動する場合「true」
			@return コピーが出来ない場合「false」（パラメーターが異常）
		 */
		//-----------------------------------------------------------------//
		bool copy(const void* src, void* dst, uint32_t len, bool tae = false) const noexcept
		{
			if(len == 0 || src == nullptr || dst == nullptr) return false;

			DMAC::DMCNT.DTE = 0;  // 念のため停止させる。

			uint32_t src_adr = reinterpret_cast<uint32_t>(src);
			uint32_t dst_adr = reinterpret_cast<uint32_t>(dst);

			bool ret = false;
			if(dst_adr < (src_adr + len)) {
				ret = trans_dec_(src_adr, dst_adr, len);
			} else {
				ret = trans_inc_(src_adr, dst_adr, len);
			}
			if(!ret) {
				return false;
			}

			if(tae && level_ > 0) {
				DMAC::DMINT.DTIE = 1;
			} else {
				DMAC::DMINT.DTIE = 0;
			}

			DMAC::DMCNT.DTE = 1;

			// CLRS=0 の場合、１回の転送で終了
			DMAC::DMREQ = DMAC::DMREQ.SWREQ.b() | DMAC::DMREQ.CLRS.b();

			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  TASK クラスの参照
			@return TASK クラス
		*/
		//-----------------------------------------------------------------//
		static TASK& at_task() noexcept { return task_; }
	};

	template <class DMAC, class TASK> TASK dmac_man<DMAC, TASK>::task_;
}
<|endoftext|>


<commit_before>#include <stdio.h>
#include <ctype.h>
#include <iostream>
#include <fstream>
#include <string>

#include <metaUtils.h>
#include <metaObject.h>
#include <metaScene.h>
#include <metaTube.h>
#include <metaEllipse.h>
#include <metaGaussian.h>
#include <metaImage.h>
#include <metaBlob.h>
#include <metaLandmark.h>
#include <metaLine.h>
#include <metaGroup.h>
#include <metaSurface.h>
#include <metaLandmark.h>
#include <metaMesh.h>
//
// MetaScene Constructors
//
MetaScene::
MetaScene()
:MetaObject()
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
}

//
MetaScene::
MetaScene(const MetaScene *_scene)
:MetaObject()
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
  CopyInfo(_scene);
}

//
MetaScene::
MetaScene(unsigned int dim)
:MetaObject(dim)
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
}

/** Destructor */
MetaScene::
~MetaScene()
{
  Clear();
  M_Destroy();
}

//
void MetaScene::
PrintInfo() const
{
  MetaObject::PrintInfo();
  std::cout << "Number of Objects = " << m_NObjects << std::endl;
}

void MetaScene::
CopyInfo(const MetaScene * _tube)
{
  MetaObject::CopyInfo(_tube);
}

void MetaScene::
NObjects(int nobjects)
{
  m_NObjects = nobjects;
}

int MetaScene::
NObjects(void) const
{
  return m_NObjects;
}

void MetaScene::
AddObject(MetaObject* object)
{
  m_ObjectList.push_back(object);
}

bool MetaScene::
Read(const char *_headerName)
{
  if(META_DEBUG) std::cout << "MetaScene: Read" << std::endl;

  int i = 0;
  char suf[80];
  suf[0] = '\0';
  if(MET_GetFileSuffixPtr(_headerName, &i))
    {
    strcpy(suf, &_headerName[i]);
    }

  M_Destroy();

  Clear();

  M_SetupReadFields();

  if(_headerName != NULL)
  {
    strcpy(m_FileName, _headerName);
  }

  if(META_DEBUG) std::cout << "MetaScene: Read: Opening stream" << std::endl;

  M_PrepareNewReadStream();

  m_ReadStream->open(m_FileName, std::ios::binary | std::ios::in);

  if(!m_ReadStream->is_open())
  {
    std::cout << "MetaScene: Read: Cannot open file" << std::endl;
    return false;
  }

  if(!M_Read())
  {
    std::cout << "MetaScene: Read: Cannot parse file" << std::endl;
    m_ReadStream->close();
    return false;
  }

  if(_headerName != NULL)
  {
    strcpy(m_FileName, _headerName);
  }

  if(m_Event)
   {
   m_Event->StartReading(m_NObjects);
   }

  /** Objects should be added here */
  for(i=0;i<m_NObjects;i++)
  {
    if(META_DEBUG) std::cout << MET_ReadType(*m_ReadStream) << std::endl;

    if(m_Event)
      {
      m_Event->SetCurrentIteration(i+1);
      }

    if(!strncmp(MET_ReadType(*m_ReadStream),"Tube",4) ||
       (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "tre")))
    {
      MetaTube* tube = new MetaTube();
      tube->SetEvent(m_Event);
      tube->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(tube);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Ellipse",7) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "elp")))
    {
      MetaEllipse* ellipse = new MetaEllipse();
      ellipse->SetEvent(m_Event);
      ellipse->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(ellipse);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Gaussian",8) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "gau")))
    {
      MetaGaussian* gaussian = new MetaGaussian();
      gaussian->SetEvent(m_Event);
      gaussian->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(gaussian);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Image",5) ||
            (MET_ReadType(*m_ReadStream) == NULL &&
             (!strcmp(suf, "mhd") || !strcmp(suf, "mha"))))
    {
      MetaImage* image = new MetaImage();
      image->SetEvent(m_Event);
      image->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(image);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Blob",4) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "blb")))
    {
      MetaBlob* blob = new MetaBlob();
      blob->SetEvent(m_Event);
      blob->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(blob);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Landmark",8) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "ldm")))
    {
      MetaLandmark* landmark = new MetaLandmark();
      landmark->SetEvent(m_Event);
      landmark->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(landmark);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Surface",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "suf")))
    {
      MetaSurface* surface = new MetaSurface();
      surface->SetEvent(m_Event);
      surface->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(surface);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Line",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "lin")))
    {
      MetaLine* line = new MetaLine();
      line->SetEvent(m_Event);
      line->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(line);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Group",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "grp")))
    {
      MetaGroup* group = new MetaGroup();
      group->SetEvent(m_Event);
      group->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(group);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"AffineTransform",15) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "trn")))
    {
      MetaGroup* group = new MetaGroup();
      group->SetEvent(m_Event);
      group->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(group);
    }
    else if(!strncmp(MET_ReadType(*m_ReadStream),"Mesh",4) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "msh")))
    {
      MetaMesh* mesh = new MetaMesh();
      mesh->SetEvent(m_Event);
      mesh->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(mesh);
    }
  }

  if(m_Event)
    {
     m_Event->StopReading();
    }

  m_ReadStream->close();

  return true;
}

//
//
//
bool MetaScene::
Write(const char *_headName)
{
  if(META_DEBUG) std::cout << "MetaScene: Write" << std::endl;

  if(_headName != NULL)
  {
    FileName(_headName);
  }

  // Set the number of objects based on the net list
  //ObjectListType::const_iterator itNet = m_ObjectList.begin();
  m_NObjects = m_ObjectList.size();

  M_SetupWriteFields();

  if(!m_WriteStream)
  {
    m_WriteStream = new std::ofstream;
  }

#ifdef __sgi
  // Create the file. This is required on some older sgi's
  std::ofstream tFile(m_FileName,std::ios::out);
  tFile.close();
#endif

  m_WriteStream->open(m_FileName, std::ios::binary | std::ios::out);
  if(!m_WriteStream->is_open())
    {
    return false;
    delete m_WriteStream;
    m_WriteStream = 0;
    }

  M_Write();

  m_WriteStream->close();
  delete m_WriteStream;
  m_WriteStream = 0;

  /** Then we write all the objects in the scene */
  ObjectListType::iterator it = m_ObjectList.begin();
  while(it != m_ObjectList.end())
  {
    (*it)->BinaryData(this->BinaryData());
    (*it)->Append(_headName);
    it++;
  }

  return true;
}

/** Clear tube information */
void MetaScene::
Clear(void)
{
  if(META_DEBUG) std::cout << "MetaScene: Clear" << std::endl;
  MetaObject::Clear();
  // Delete the list of pointers to objects in the scene.
  ObjectListType::iterator it = m_ObjectList.begin();
  while(it != m_ObjectList.end())
  {
    MetaObject* object = *it;
    it++;
    delete object;
  }

  m_ObjectList.clear();

}

/** Destroy tube information */
void MetaScene::
M_Destroy(void)
{
  MetaObject::M_Destroy();
}

/** Set Read fields */
void MetaScene::
M_SetupReadFields(void)
{
  if(META_DEBUG) std::cout << "MetaScene: M_SetupReadFields" << std::endl;

  MetaObject::M_SetupReadFields();

  MET_FieldRecordType * mF;

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "NObjects", MET_INT, false);
  mF->required = true;
  mF->terminateRead = true;
  m_Fields.push_back(mF);

  mF = MET_GetFieldRecord("ElementSpacing", &m_Fields);
  mF->required = false;
}

void MetaScene::
M_SetupWriteFields(void)
{
  strcpy(m_ObjectTypeName,"Scene");
  MetaObject::M_SetupWriteFields();

  MET_FieldRecordType * mF;

  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "NObjects", MET_INT, m_NObjects);
  m_Fields.push_back(mF);
}

bool MetaScene::
M_Read(void)
{
  if(META_DEBUG) std::cout<<"MetaScene: M_Read: Loading Header"<<std::endl;
  if(strncmp(MET_ReadType(*m_ReadStream),"Scene",5))
    {
    m_NObjects = 1;
    return true;
    }

  if(!MetaObject::M_Read())
    {
    std::cout << "MetaScene: M_Read: Error parsing file" << std::endl;
    return false;
    }

  if(META_DEBUG) std::cout << "MetaScene: M_Read: Parsing Header" << std::endl;

  MET_FieldRecordType * mF;

  mF = MET_GetFieldRecord("NObjects", &m_Fields);
  if(mF->defined)
  {
    m_NObjects= (int)mF->value[0];
  }

  return true;
}

bool MetaScene::
M_Write(void)
{
  if(!MetaObject::M_Write())
  {
    std::cout << "MetaScene: M_Write: Error parsing file" << std::endl;
    return false;
  }

  return true;
}
<commit_msg>ENH: Scene does not contain a transform, color, etc.   Default write fields updated accordingly.<commit_after>#include <stdio.h>
#include <ctype.h>
#include <iostream>
#include <fstream>
#include <string>

#include <metaUtils.h>
#include <metaObject.h>
#include <metaScene.h>
#include <metaTube.h>
#include <metaEllipse.h>
#include <metaGaussian.h>
#include <metaImage.h>
#include <metaBlob.h>
#include <metaLandmark.h>
#include <metaLine.h>
#include <metaGroup.h>
#include <metaSurface.h>
#include <metaLandmark.h>
#include <metaMesh.h>
//
// MetaScene Constructors
//
MetaScene::
MetaScene()
:MetaObject()
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
}

//
MetaScene::
MetaScene(const MetaScene *_scene)
:MetaObject()
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
  CopyInfo(_scene);
}

//
MetaScene::
MetaScene(unsigned int dim)
:MetaObject(dim)
{
  if(META_DEBUG) std::cout << "MetaScene()" << std::endl;
  Clear();
}

/** Destructor */
MetaScene::
~MetaScene()
{
  Clear();
  M_Destroy();
}

//
void MetaScene::
PrintInfo() const
{
  MetaObject::PrintInfo();
  std::cout << "Number of Objects = " << m_NObjects << std::endl;
}

void MetaScene::
CopyInfo(const MetaScene * _tube)
{
  MetaObject::CopyInfo(_tube);
}

void MetaScene::
NObjects(int nobjects)
{
  m_NObjects = nobjects;
}

int MetaScene::
NObjects(void) const
{
  return m_NObjects;
}

void MetaScene::
AddObject(MetaObject* object)
{
  m_ObjectList.push_back(object);
}

bool MetaScene::
Read(const char *_headerName)
{
  if(META_DEBUG) std::cout << "MetaScene: Read" << std::endl;

  int i = 0;
  char suf[80];
  suf[0] = '\0';
  if(MET_GetFileSuffixPtr(_headerName, &i))
    {
    strcpy(suf, &_headerName[i]);
    }

  M_Destroy();

  Clear();

  M_SetupReadFields();

  if(_headerName != NULL)
  {
    strcpy(m_FileName, _headerName);
  }

  if(META_DEBUG) std::cout << "MetaScene: Read: Opening stream" << std::endl;

  M_PrepareNewReadStream();

  m_ReadStream->open(m_FileName, std::ios::binary | std::ios::in);

  if(!m_ReadStream->is_open())
  {
    std::cout << "MetaScene: Read: Cannot open file" << std::endl;
    return false;
  }

  if(!M_Read())
  {
    std::cout << "MetaScene: Read: Cannot parse file" << std::endl;
    m_ReadStream->close();
    return false;
  }

  if(_headerName != NULL)
  {
    strcpy(m_FileName, _headerName);
  }

  if(m_Event)
   {
   m_Event->StartReading(m_NObjects);
   }

  /** Objects should be added here */
  for(i=0;i<m_NObjects;i++)
  {
    if(META_DEBUG) std::cout << MET_ReadType(*m_ReadStream) << std::endl;

    if(m_Event)
      {
      m_Event->SetCurrentIteration(i+1);
      }

    if(!strncmp(MET_ReadType(*m_ReadStream),"Tube",4) ||
       (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "tre")))
    {
      MetaTube* tube = new MetaTube();
      tube->SetEvent(m_Event);
      tube->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(tube);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Ellipse",7) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "elp")))
    {
      MetaEllipse* ellipse = new MetaEllipse();
      ellipse->SetEvent(m_Event);
      ellipse->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(ellipse);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Gaussian",8) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "gau")))
    {
      MetaGaussian* gaussian = new MetaGaussian();
      gaussian->SetEvent(m_Event);
      gaussian->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(gaussian);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Image",5) ||
            (MET_ReadType(*m_ReadStream) == NULL &&
             (!strcmp(suf, "mhd") || !strcmp(suf, "mha"))))
    {
      MetaImage* image = new MetaImage();
      image->SetEvent(m_Event);
      image->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(image);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Blob",4) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "blb")))
    {
      MetaBlob* blob = new MetaBlob();
      blob->SetEvent(m_Event);
      blob->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(blob);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Landmark",8) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "ldm")))
    {
      MetaLandmark* landmark = new MetaLandmark();
      landmark->SetEvent(m_Event);
      landmark->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(landmark);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Surface",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "suf")))
    {
      MetaSurface* surface = new MetaSurface();
      surface->SetEvent(m_Event);
      surface->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(surface);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Line",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "lin")))
    {
      MetaLine* line = new MetaLine();
      line->SetEvent(m_Event);
      line->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(line);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"Group",5) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "grp")))
    {
      MetaGroup* group = new MetaGroup();
      group->SetEvent(m_Event);
      group->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(group);
    }

    else if(!strncmp(MET_ReadType(*m_ReadStream),"AffineTransform",15) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "trn")))
    {
      MetaGroup* group = new MetaGroup();
      group->SetEvent(m_Event);
      group->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(group);
    }
    else if(!strncmp(MET_ReadType(*m_ReadStream),"Mesh",4) ||
            (MET_ReadType(*m_ReadStream) == NULL && !strcmp(suf, "msh")))
    {
      MetaMesh* mesh = new MetaMesh();
      mesh->SetEvent(m_Event);
      mesh->ReadStream(m_NDims,m_ReadStream);
      m_ObjectList.push_back(mesh);
    }
  }

  if(m_Event)
    {
     m_Event->StopReading();
    }

  m_ReadStream->close();

  return true;
}

//
//
//
bool MetaScene::
Write(const char *_headName)
{
  if(META_DEBUG) std::cout << "MetaScene: Write" << std::endl;

  if(_headName != NULL)
  {
    FileName(_headName);
  }

  // Set the number of objects based on the net list
  //ObjectListType::const_iterator itNet = m_ObjectList.begin();
  m_NObjects = m_ObjectList.size();

  M_SetupWriteFields();

  if(!m_WriteStream)
  {
    m_WriteStream = new std::ofstream;
  }

#ifdef __sgi
  // Create the file. This is required on some older sgi's
  std::ofstream tFile(m_FileName,std::ios::out);
  tFile.close();
#endif

  m_WriteStream->open(m_FileName, std::ios::binary | std::ios::out);
  if(!m_WriteStream->is_open())
    {
    return false;
    delete m_WriteStream;
    m_WriteStream = 0;
    }

  M_Write();

  m_WriteStream->close();
  delete m_WriteStream;
  m_WriteStream = 0;

  /** Then we write all the objects in the scene */
  ObjectListType::iterator it = m_ObjectList.begin();
  while(it != m_ObjectList.end())
  {
    (*it)->BinaryData(this->BinaryData());
    (*it)->Append(_headName);
    it++;
  }

  return true;
}

/** Clear tube information */
void MetaScene::
Clear(void)
{
  if(META_DEBUG) std::cout << "MetaScene: Clear" << std::endl;
  MetaObject::Clear();
  // Delete the list of pointers to objects in the scene.
  ObjectListType::iterator it = m_ObjectList.begin();
  while(it != m_ObjectList.end())
  {
    MetaObject* object = *it;
    it++;
    delete object;
  }

  m_ObjectList.clear();

}

/** Destroy tube information */
void MetaScene::
M_Destroy(void)
{
  MetaObject::M_Destroy();
}

/** Set Read fields */
void MetaScene::
M_SetupReadFields(void)
{
  if(META_DEBUG) std::cout << "MetaScene: M_SetupReadFields" << std::endl;

  MetaObject::M_SetupReadFields();

  MET_FieldRecordType * mF;

  mF = new MET_FieldRecordType;
  MET_InitReadField(mF, "NObjects", MET_INT, false);
  mF->required = true;
  mF->terminateRead = true;
  m_Fields.push_back(mF);

  mF = MET_GetFieldRecord("ElementSpacing", &m_Fields);
  mF->required = false;
}

void MetaScene::
M_SetupWriteFields(void)
{
  this->ClearFields();

  MET_FieldRecordType * mF;

  if(strlen(m_Comment)>0)
    {
    mF = new MET_FieldRecordType;
    MET_InitWriteField(mF, "Comment", MET_STRING, strlen(m_Comment), m_Comment);
    m_Fields.push_back(mF);
    }

  strcpy(m_ObjectTypeName,"Scene");
  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "ObjectType", MET_STRING, strlen(m_ObjectTypeName),
                    m_ObjectTypeName);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "NDims", MET_INT, m_NDims);
  m_Fields.push_back(mF);

  mF = new MET_FieldRecordType;
  MET_InitWriteField(mF, "NObjects", MET_INT, m_NObjects);
  m_Fields.push_back(mF);
}

bool MetaScene::
M_Read(void)
{
  if(META_DEBUG) std::cout<<"MetaScene: M_Read: Loading Header"<<std::endl;
  if(strncmp(MET_ReadType(*m_ReadStream),"Scene",5))
    {
    m_NObjects = 1;
    return true;
    }

  if(!MetaObject::M_Read())
    {
    std::cout << "MetaScene: M_Read: Error parsing file" << std::endl;
    return false;
    }

  if(META_DEBUG) std::cout << "MetaScene: M_Read: Parsing Header" << std::endl;

  MET_FieldRecordType * mF;

  mF = MET_GetFieldRecord("NObjects", &m_Fields);
  if(mF->defined)
  {
    m_NObjects= (int)mF->value[0];
  }

  return true;
}

bool MetaScene::
M_Write(void)
{
  if(!MetaObject::M_Write())
  {
    std::cout << "MetaScene: M_Write: Error parsing file" << std::endl;
    return false;
  }

  return true;
}
<|endoftext|>