/****************************************************************************
**
**
** This file is part of Qt Creator.
**
**
****************************************************************************/

#include "syssocket.h" // this should be the first header included

#include "servicebrowser_p.h"

#include <QCoreApplication>
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QString>
#include <QStringList>
#include <QProcess>

#ifdef Q_OS_LINUX
#define EMBEDDED_LIB
#endif

#ifdef Q_OS_WIN32
#define EMBEDDED_LIB
#endif

#ifdef EMBEDDED_LIB
#define PID_FILE "/tmp/mdnsd.pid"
#define MDNS_UDS_SERVERPATH "/tmp/mdnsd"

#include "embed/dnssd_ipc.c"
#include "embed/dnssd_clientlib.c"
#include "embed/dnssd_clientstub.c"
#ifdef Q_OS_WIN
#include "embed/DebugServices.c"
#endif

namespace ZeroConf {
namespace Internal {
// represents a zero conf library exposing the dns-sd interface
class EmbeddedZConfLib : public ZConfLib
{
public:
    QString daemonPath;

    EmbeddedZConfLib(const QString &daemonPath, ZConfLib::Ptr fallBack) : ZConfLib(fallBack),
        daemonPath(daemonPath)
    {
        if (daemonPath.isEmpty())
            m_maxErrors = 0;
        if (!daemonPath.isEmpty() && daemonPath.at(0) != '/' && daemonPath.at(0) != '.')
            this->daemonPath = QCoreApplication::applicationDirPath() + QChar('/') + daemonPath;
    }

    ~EmbeddedZConfLib()
    { }

    QString name()
    {
        return QString::fromLatin1("Embedded Dns_sd Library");
    }

    bool tryStartDaemon(ErrorMessage::ErrorLogger *logger)
    {
        if (!daemonPath.isEmpty()) {
            QFileInfo dPath(daemonPath);
            QProcess killall;
            bool killAllFailed = false;
#ifdef Q_OS_WIN
            QString cmd = QLatin1String("taskill /im ") + dPath.fileName()
                    + QLatin1String(" /f /t");
#else
            QString cmd = QLatin1String("killall ") + dPath.fileName()
                    + QLatin1String(" 2> /dev/null");
#endif
            killall.start(cmd);
            if (!killall.waitForStarted()) {
                killAllFailed = true;
            } else {
                killall.closeWriteChannel();
                killall.waitForFinished();
            }
            if (killAllFailed) {
                if (logger)
                    logger->appendError(ErrorMessage::WarningLevel,
                                        ZConfLib::tr("%1 failed to kill other daemons with '%2'.")
                                        .arg(name()).arg(cmd));
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " had an error trying to kill other daemons with " << cmd;
            }
            QString daemonCmd = daemonPath;
            QStringList daemonArgs;
#ifdef Q_OS_LINUX
            if (QFile::exists("/tmp/mdnsd") && logger)
                logger->appendError(ErrorMessage::WarningLevel,
                                    ZConfLib::tr("%1 detected a file at /tmp/mdnsd, daemon startup will probably fail.")
                                    .arg(name()));
            QString logFile = QString::fromLatin1("/tmp/mdnssd.log");
            static int didFail = 0;
            QFile oldLog(logFile);
            if (didFail > 1 && oldLog.exists()) {
                oldLog.open(QIODevice::ReadOnly);
                if (logger) {
                    QByteArray logBA = oldLog.readAll();
                    logger->appendError(ErrorMessage::NoteLevel,
                                        ZConfLib::tr("%1: log of previous daemon run is: '%2'.\n")
                                        .arg(name())
                                        .arg(QString::fromLatin1(logBA.constData(), logBA.size())));
                    qDebug()<<logBA.size()<<oldLog.error()<<oldLog.errorString();
                }
                oldLog.close();
            }
            if (++didFail > 1)
                daemonArgs << QString::fromLatin1("-debug");
#endif
            if (QProcess::startDetached(daemonCmd, daemonArgs)) {
                QThread::yieldCurrentThread();
                // sleep a bit?
                if (DEBUG_ZEROCONF)
                    qDebug() << name() << " started " << daemonCmd << daemonArgs;
                return true;
            } else {
                this->setError(true, ZConfLib::tr("%1 failed starting embedded daemon at %2.")
                               .arg(name()).arg(daemonPath));
            }
        }
        return false;
    }

    void refDeallocate(DNSServiceRef sdRef)
    {
        embeddedLib::DNSServiceRefDeallocate(sdRef);
    }

    void browserDeallocate(BrowserRef *bRef)
    {
        if (bRef){
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(bRef));
            *bRef = 0;
        }
    }

    void stopConnection(ConnectionRef cRef)
    {
        int sock = refSoc

        if (sock>0)
            shutdown(sock, SHUT_RDWR);
    }

    void destroyConnection(ConnectionRef *sdRef)
    {
        if (sdRef) {
            embeddedLib::DNSServiceRefDeallocate(*reinterpret_cast<DNSServiceRef*>(sdRef));
            *sdRef = 0;
        }
    }

    DNSServiceErrorType resolve(ConnectionRef                       cRef,
                                DNSServiceRef                       *sdRef,
                                uint32_t                            interfaceIndex,
                                ZK_IP_Protocol                      /* protocol */,
                                const char                          *name,
                                const char                          *regtype,
                                const char                          *domain,
                                ServiceGatherer                     *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceResolve(sdRef, kDNSServiceFlagsShareConnection
                                              | kDNSServiceFlagsTimeout,
                                              interfaceIndex, name, regtype, domain,
                                              &cServiceResolveReply, gatherer);
    }

    DNSServiceErrorType queryRecord(ConnectionRef                       cRef,
                                    DNSServiceRef                       *sdRef,
                                    uint32_t                            interfaceIndex,
                                    const char                          *fullname,
                                    ServiceGatherer                     *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceQueryRecord(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, fullname,
                                                  kDNSServiceType_TXT, kDNSServiceClass_IN,
                                                  &cTxtRecordReply , gatherer);
    }

    DNSServiceErrorType getAddrInfo(ConnectionRef                    cRef,
                                    DNSServiceRef                    *sdRef,
                                    uint32_t                         interfaceIndex,
                                    DNSServiceProtocol               protocol,
                                    const char                       *hostname,
                                    ServiceGatherer                  *gatherer)
    {
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceGetAddrInfo(sdRef, kDNSServiceFlagsShareConnection
                                                  | kDNSServiceFlagsTimeout,
                                                  interfaceIndex, protocol,
                                                  hostname, &cAddrReply, gatherer);
    }

    DNSServiceErrorType reconfirmRecord(ConnectionRef /*cRef*/, uint32_t /*interfaceIndex*/,
                                        const char * /*name*/, const char * /*type*/,
                                        const char * /*domain*/, const char * /*fullname*/)
    {
        // reload and force update with in the callback with
        return kDNSServiceErr_Unsupported;
    }

    DNSServiceErrorType browse(ConnectionRef                       cRef,
                               BrowserRef                          *bRef,
                               uint32_t                            interfaceIndex,
                               const char                          *regtype,
                               const char                          *domain,    /* may be NULL */
                               ServiceBrowserPrivate               *browser)
    {
        DNSServiceRef *sdRef = reinterpret_cast<DNSServiceRef *>(bRef);
        *sdRef = reinterpret_cast<DNSServiceRef>(cRef);
        return embeddedLib::DNSServiceBrowse(sdRef, kDNSServiceFlagsShareConnection
                                             /* | kDNSServiceFlagsSuppressUnusable */,
                                             interfaceIndex, regtype, domain, &cBrowseReply,
                                             browser);
    }

    DNSServiceErrorType getProperty(const char *property, void *result, uint32_t *size)
    {
        return embeddedLib::DNSServiceGetProperty(property, result, size);
    }

    RunLoopStatus processOneEventBlock(ConnectionRef cRef)
    {
        if (embeddedLib::DNSServiceProcessResult(reinterpret_cast<DNSServiceRef>(cRef)) != kDNSServiceErr_NoError)
            return ProcessedError;
        return ProcessedOk;
    }

    DNSServiceErrorType createConnection(MainConnection *, ConnectionRef *sdRef)
    {
        return embeddedLib::DNSServiceCreateConnection(reinterpret_cast<DNSServiceRef*>(sdRef));
    }

    int refSockFD(ConnectionRef sdRef)
    {
        return embeddedLib::DNSServiceRefSockFD(reinterpret_cast<DNSServiceRef>(sdRef));
    }
};

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &daemonPath, const ZConfLib::Ptr &fallback)
{
    return ZConfLib::Ptr(new EmbeddedZConfLib(daemonPath, fallback));
}
} // namespace Internal
} // namespace ZeroConf

#else // no embedded lib

namespace ZeroConf {
namespace Internal {

ZConfLib::Ptr ZConfLib::createEmbeddedLib(const QString &, const ZConfLib::Ptr &fallback)
{
    return fallback;
}

} // namespace Internal
} // namespace ZeroConf
#endif


/**
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://example.com
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <gtest/gtest.h>
#include <vector>
#include "lib/coro/co_thread.h"
#include "lib/coro/co_thread_mgr.h"

using namespace oceanbase::lib;
using namespace oceanbase::common;
using namespace std;

class Add1 : public CoThread {
  void run() override
  {
    ATOMIC_INC(&v_);
    while (!has_set_stop()) {
      ::usleep(100 * 1000L);
    }
  }

public:
  Add1(int& v) : v_(v)
  {}
  int& v_;
};

{
  int v = 0;
  vector<Add1*> thes;
  for (int i = 0; i < 100; i++) {
    thes.push_back(new Add1(v));
  }

  CoThreadMgr ctm;
  for (auto th : thes) {
    ctm.add_thread(*th);
  }
  ctm.start();
  ctm.stop();
  ctm.wait();
  ASSERT_EQ(100, v);
  for (auto th : thes) {
    delete th;
  }
}

int main(int argc, char* argv[])
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}

/**
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://example.com
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <gtest/gtest.h>
#include <vector>
#include "lib/coro/co_thread.h"
#include "lib/coro/co_thread_mgr.h"

using namespace oceanbase::lib;
using namespace oceanbase::common;
using namespace std;

class Add1 : public CoThread {
  void run() override
  {
    ATOMIC_INC(&v_);
    while (!has_set_stop()) {
      ::usleep(100 * 1000L);
    }
  }

public:
  Add1(int& v) : v_(v)
  {}
  int& v_;
};

TEST(TestCoThreadMgr, Add)
{
  int v = 0;
  vector<Add1*> thes;
  for (int i = 0; i < 100; i++) {
    thes.push_back(new Add1(v));
  }

  CoThreadMgr ctm;
  for (auto th : thes) {
    ctm.add_thread(*th);
  }
  ctm.start();
  ctm.stop();
  ctm.wait();
  ASSERT_EQ(100, v);
  for (auto th : thes) {
    delete th;
  }
}

int main(int argc, char* argv[])


/**
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://example.com
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <gtest/gtest.h>
#include <vector>
#include "lib/coro/co_thread.h"
#include "lib/coro/co_thread_mgr.h"

using namespace oceanbase::lib;
using namespace oceanbase::common;
using namespace std;

class Add1 : public CoThread {
  void run() override
  {
    ATOMIC_INC(&v_);
    while (!has_set_stop()) {
      ::usleep(100 * 1000L);
    }
  }

public:
  Add1(int& v) : v_(v)
  {}
  int& v_;
};

TEST(TestCoThreadMgr, Add)
{
  int v = 0;
  vector<Add1*> thes;
  for (int i = 0; i < 100; i++) {
    thes.push_back(new Add1(v));
  }

  Co

  for (auto th : thes) {
    ctm.add_thread(*th);
  }
  ctm.start();
  ctm.stop();
  ctm.wait();
  ASSERT_EQ(100, v);
  for (auto th : thes) {
    delete th;
  }
}

int main(int argc, char* argv[])
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


while (!has_set_stop()) {
      ::usleep(100 * 1000L);
    }

/**
 * You can use this software according to the terms and conditions of the Mulan PubL v2.
 * You may obtain a copy of Mulan PubL v2 at:
 *          http://example.com
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PubL v2 for more details.
 */

#include <gtest/gtest.h>
#include <vector>
#include "lib/coro/co_thread.h"
#include "lib/coro/co_thread_mgr.h"

using namespace oceanbase::lib;
using namespace oceanbase::common;
using namespace std;

class Add1 : public CoThread {
  void run() override
  {
    ATOMIC_INC(&v_);

  }

public:
  Add1(int& v) : v_(v)
  {}
  int& v_;
};

TEST(TestCoThreadMgr, Add)
{
  int v = 0;
  vector<Add1*> thes;
  for (int i = 0; i < 100; i++) {
    thes.push_back(new Add1(v));
  }

  CoThreadMgr ctm;
  for (auto th : thes) {
    ctm.add_thread(*th);
  }
  ctm.start();
  ctm.stop();
  ctm.wait();
  ASSERT_EQ(100, v);
  for (auto th : thes) {
    delete th;
  }
}

int main(int argc, char* argv[])
{
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}


const HexagonDYLDRendezvous::ThreadInfo &
HexagonDYLDRendezvous::GetThreadInfo()

//                     The LLVM Compiler Infrastructure

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "HexagonDYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t ResolveRendezvousAddress(Process *process) {
  addr_t info_location;
  addr_t info_addr;
  Error error;

  info_location = process->GetImageInfoAddress();

  if (info_location == LLDB_INVALID_ADDRESS)
    return LLDB_INVALID_ADDRESS;

  info_addr = process->ReadPointerFromMemory(info_location, error);
  if (error.Fail())
    return LLDB_INVALID_ADDRESS;

  if (info_addr == 0)
    return LLDB_INVALID_ADDRESS;

  return info_addr;
}

HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process)
    : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
      m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() {
  m_thread_info.valid = false;

  // Cache a copy of the executable path
  if (m_process) {
    Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
    if (exe_mod)
      exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
  }
}

bool HexagonDYLDRendezvous::Resolve() {
  const size_t word_size = 4;
  Rendezvous info;
  size_t address_size;
  size_t padding;
  addr_t info_addr;
  addr_t cursor;

  address_size = m_process->GetAddressByteSize();
  padding = address_size - word_size;

  if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
    cursor = info_addr = ResolveRendezvousAddress(m_process);
  else
    cursor = info_addr = m_rendezvous_addr;

  if (cursor == LLDB_INVALID_ADDRESS)
    return false;

  if (!(cursor = ReadWord(cursor, &info.version, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
    return false;

  if (!(cursor = ReadPointer(cursor, &info.brk)))
    return false;

  if (!(cursor = ReadWord(cursor, &info.state, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
    return false;

  // The rendezvous was successfully read.  Update our internal state.
  m_rendezvous_addr = info_addr;
  m_previous = m_current;
  m_current = info;

  return UpdateSOEntries();
}

void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
  m_rendezvous_addr = addr;
}

bool HexagonDYLDRendezvous::IsValid() {
  return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
}

bool HexagonDYLDRendezvous::UpdateSOEntries() {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  // When the previous and current states are consistent this is the first
  // time we have been asked to update.  Just take a snapshot of the currently
  // loaded modules.
  if (m_previous.state == eConsistent && m_current.state == eConsistent)
    return TakeSnapshot(m_soentries);

  // If we are about to add or remove a shared object clear out the current
  // state and take a snapshot of the currently loaded images.
  if (m_current.state == eAdd || m_current.state == eDelete) {
    // this is a fudge so that we can clear the assert below.
    m_previous.state = eConsistent;
    // We hit this assert on the 2nd run of this function after running the calc
    // example
    assert(m_previous.state == eConsistent);
    m_soentries.clear();
    m_added_soentries.clear();
    m_removed_soentries.clear();
    return TakeSnapshot(m_soentries);
  }
  assert(m_current.state == eConsistent);

  // Otherwise check the previous state to determine what to expect and update
  if (m_previous.state == eAdd)
    return UpdateSOEntriesForAddition();
  else if (m_previous.state == eDelete)
    return UpdateSOEntriesForDeletion();

  return false;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() {
  SOEntry entry;
  iterator pos;

  assert(m_previous.state == eAdd);

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
    if (pos == m_soentries.end()) {
      m_soentries.push_back(entry);
      m_added_soentries.push_back(entry);
    }
  }

  return true;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
  SOEntryList entry_list;
  iterator pos;

  assert(m_previous.state == eDelete);

  if (!TakeSnapshot(entry_list))
    return false;

  for (iterator I = begin(); I != end(); ++I) {
    pos = std::find(entry_list.begin(), entry_list.end(), *I);
    if (pos == entry_list.end())
      m_removed_soentries.push_back(*I);
  }

  m_soentries = entry_list;
  return true;
}

bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    entry_list.push_back(entry);
  }

  return true;
}

addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
                                       size_t size) {
  Error error;

  *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
  if (error.Fail())
    return 0;

  return addr + size;
}

addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
  Error error;

  *dst = m_process->ReadPointerFromMemory(addr, error);
  if (error.Fail())
    return 0;

  return addr + m_process->GetAddressByteSize();
}

std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
  std::string str;
  Error error;
  size_t size;
  char c;

  if (addr == LLDB_INVALID_ADDRESS)
    return std::string();

  for (;;) {
    size = m_process->DoReadMemory(addr, &c, 1, error);
    if (size != 1 || error.Fail())
      return std::string();
    if (c == 0)
      break;
    else {
      str.push_back(c);
      addr++;
    }
  }

  return str;
}

bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
                                                  SOEntry &entry) {
  entry.clear();
  entry.link_addr = addr;

  if (!(addr = ReadPointer(addr, &entry.base_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.path_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.next)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.prev)))
    return false;

  entry.path = ReadStringFromMemory(entry.path_addr);

  return true;
}

bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
                                         uint32_t &value) {
  Target &target = m_process->GetTarget();

  SymbolContextList list;
  if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
                                                     eSymbolTypeAny, list))
    return false;

  Address address = list[0].symbol->GetAddress();
  addr_t addr = address.GetLoadAddress(&target);
  if (addr == LLDB_INVALID_ADDRESS)
    return false;

  Error error;
  value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
      addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
  if (error.Fail())
    return false;

  if (field == eSize)
    value /= 8; // convert bits to bytes

  return true;
}

{
  if (!m_thread_info.valid) {
    bool ok = true;

    ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
                       m_thread_info.dtv_offset);
    ok &=
        FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
    ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
                       m_thread_info.modid_offset);
    ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
                       m_thread_info.tls_offset);

    if (ok)
      m_thread_info.valid = true;
  }

  return m_thread_info;
}

void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
  int state = GetState();

  if (!log)
    return;

  log->PutCString("HexagonDYLDRendezvous:");
  log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
  log->Printf("   Version: %" PRIu64, GetVersion());
  log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
  log->Printf("   Break  : %" PRIx64, GetBreakAddress());
  log->Printf("   LDBase : %" PRIx64, GetLDBase());
  log->Printf("   State  : %s",
              (state == eConsistent)
                  ? "consistent"
                  : (state == eAdd)
                        ? "add"
                        : (state == eDelete) ? "delete" : "unknown");

  iterator I = begin();
  iterator E = end();

  if (I != E)
    log->PutCString("HexagonDYLDRendezvous SOEntries:");

  for (int i = 1; I != E; ++I, ++i) {
    log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
    log->Printf("      Base : %" PRIx64, I->base_addr);
    log->Printf("      Path : %" PRIx64, I->path_addr);
    log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
    log->Printf("      Next : %" PRIx64, I->next);
    log->Printf("      Prev : %" PRIx64, I->prev);
  }
}


{
  addr_t info_location;
  addr_t info_addr;
  Error error;

  info_location = process->GetImageInfoAddress();

  if (info_location == LLDB_INVALID_ADDRESS)
    return LLDB_INVALID_ADDRESS;

  info_addr = process->ReadPointerFromMemory(info_location, error);
  if (error.Fail())
    return LLDB_INVALID_ADDRESS;

  if (info_addr == 0)
    return LLDB_INVALID_ADDRESS;

  return info_addr;
}

//                     The LLVM Compiler Infrastructure

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "HexagonDYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t ResolveRendezvousAddress(Process *process)

HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process)
    : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
      m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() {
  m_thread_info.valid = false;

  // Cache a copy of the executable path
  if (m_process) {
    Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
    if (exe_mod)
      exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
  }
}

bool HexagonDYLDRendezvous::Resolve() {
  const size_t word_size = 4;
  Rendezvous info;
  size_t address_size;
  size_t padding;
  addr_t info_addr;
  addr_t cursor;

  address_size = m_process->GetAddressByteSize();
  padding = address_size - word_size;

  if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
    cursor = info_addr = ResolveRendezvousAddress(m_process);
  else
    cursor = info_addr = m_rendezvous_addr;

  if (cursor == LLDB_INVALID_ADDRESS)
    return false;

  if (!(cursor = ReadWord(cursor, &info.version, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
    return false;

  if (!(cursor = ReadPointer(cursor, &info.brk)))
    return false;

  if (!(cursor = ReadWord(cursor, &info.state, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
    return false;

  // The rendezvous was successfully read.  Update our internal state.
  m_rendezvous_addr = info_addr;
  m_previous = m_current;
  m_current = info;

  return UpdateSOEntries();
}

void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
  m_rendezvous_addr = addr;
}

bool HexagonDYLDRendezvous::IsValid() {
  return m_rendezvous_addr != LLDB_INVALID_ADDRESS;
}

bool HexagonDYLDRendezvous::UpdateSOEntries() {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  // When the previous and current states are consistent this is the first
  // time we have been asked to update.  Just take a snapshot of the currently
  // loaded modules.
  if (m_previous.state == eConsistent && m_current.state == eConsistent)
    return TakeSnapshot(m_soentries);

  // If we are about to add or remove a shared object clear out the current
  // state and take a snapshot of the currently loaded images.
  if (m_current.state == eAdd || m_current.state == eDelete) {
    // this is a fudge so that we can clear the assert below.
    m_previous.state = eConsistent;
    // We hit this assert on the 2nd run of this function after running the calc
    // example
    assert(m_previous.state == eConsistent);
    m_soentries.clear();
    m_added_soentries.clear();
    m_removed_soentries.clear();
    return TakeSnapshot(m_soentries);
  }
  assert(m_current.state == eConsistent);

  // Otherwise check the previous state to determine what to expect and update
  if (m_previous.state == eAdd)
    return UpdateSOEntriesForAddition();
  else if (m_previous.state == eDelete)
    return UpdateSOEntriesForDeletion();

  return false;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() {
  SOEntry entry;
  iterator pos;

  assert(m_previous.state == eAdd);

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
    if (pos == m_soentries.end()) {
      m_soentries.push_back(entry);
      m_added_soentries.push_back(entry);
    }
  }

  return true;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
  SOEntryList entry_list;
  iterator pos;

  assert(m_previous.state == eDelete);

  if (!TakeSnapshot(entry_list))
    return false;

  for (iterator I = begin(); I != end(); ++I) {
    pos = std::find(entry_list.begin(), entry_list.end(), *I);
    if (pos == entry_list.end())
      m_removed_soentries.push_back(*I);
  }

  m_soentries = entry_list;
  return true;
}

bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    entry_list.push_back(entry);
  }

  return true;
}

addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
                                       size_t size) {
  Error error;

  *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
  if (error.Fail())
    return 0;

  return addr + size;
}

addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
  Error error;

  *dst = m_process->ReadPointerFromMemory(addr, error);
  if (error.Fail())
    return 0;

  return addr + m_process->GetAddressByteSize();
}

std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
  std::string str;
  Error error;
  size_t size;
  char c;

  if (addr == LLDB_INVALID_ADDRESS)
    return std::string();

  for (;;) {
    size = m_process->DoReadMemory(addr, &c, 1, error);
    if (size != 1 || error.Fail())
      return std::string();
    if (c == 0)
      break;
    else {
      str.push_back(c);
      addr++;
    }
  }

  return str;
}

bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
                                                  SOEntry &entry) {
  entry.clear();
  entry.link_addr = addr;

  if (!(addr = ReadPointer(addr, &entry.base_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.path_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.next)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.prev)))
    return false;

  entry.path = ReadStringFromMemory(entry.path_addr);

  return true;
}

bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
                                         uint32_t &value) {
  Target &target = m_process->GetTarget();

  SymbolContextList list;
  if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
                                                     eSymbolTypeAny, list))
    return false;

  Address address = list[0].symbol->GetAddress();
  addr_t addr = address.GetLoadAddress(&target);
  if (addr == LLDB_INVALID_ADDRESS)
    return false;

  Error error;
  value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
      addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
  if (error.Fail())
    return false;

  if (field == eSize)
    value /= 8; // convert bits to bytes

  return true;
}

const HexagonDYLDRendezvous::ThreadInfo &
HexagonDYLDRendezvous::GetThreadInfo() {
  if (!m_thread_info.valid) {
    bool ok = true;

    ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
                       m_thread_info.dtv_offset);
    ok &=
        FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
    ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
                       m_thread_info.modid_offset);
    ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
                       m_thread_info.tls_offset);

    if (ok)
      m_thread_info.valid = true;
  }

  return m_thread_info;
}

void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
  int state = GetState();

  if (!log)
    return;

  log->PutCString("HexagonDYLDRendezvous:");
  log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
  log->Printf("   Version: %" PRIu64, GetVersion());
  log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
  log->Printf("   Break  : %" PRIx64, GetBreakAddress());
  log->Printf("   LDBase : %" PRIx64, GetLDBase());
  log->Printf("   State  : %s",
              (state == eConsistent)
                  ? "consistent"
                  : (state == eAdd)
                        ? "add"
                        : (state == eDelete) ? "delete" : "unknown");

  iterator I = begin();
  iterator E = end();

  if (I != E)
    log->PutCString("HexagonDYLDRendezvous SOEntries:");

  for (int i = 1; I != E; ++I, ++i) {
    log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
    log->Printf("      Base : %" PRIx64, I->base_addr);
    log->Printf("      Path : %" PRIx64, I->path_addr);
    log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
    log->Printf("      Next : %" PRIx64, I->next);
    log->Printf("      Prev : %" PRIx64, I->prev);
  }
}


//                     The LLVM Compiler Infrastructure

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "HexagonDYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.
static addr_t ResolveRendezvousAddress(Process *process) {
  addr_t info_location;
  addr_t info_addr;
  Error error;

  info_location = process->GetImageInfoAddress();

  if (info_location == LLDB_INVALID_ADDRESS)
    return LLDB_INVALID_ADDRESS;

  info_addr = process->ReadPointerFromMemory(info_location, error);
  if (error.Fail())
    return LLDB_INVALID_ADDRESS;

  if (info_addr == 0)
    return LLDB_INVALID_ADDRESS;

  return info_addr;
}

HexagonDYLDRendezvous::HexagonDYLDRendezvous(Process *process)
    : m_process(process), m_rendezvous_addr(LLDB_INVALID_ADDRESS), m_current(),
      m_previous(), m_soentries(), m_added_soentries(), m_removed_soentries() {
  m_thread_info.valid = false;

  // Cache a copy of the executable path
  if (m_process) {
    Module *exe_mod = m_process->GetTarget().GetExecutableModulePointer();
    if (exe_mod)
      exe_mod->GetFileSpec().GetPath(m_exe_path, PATH_MAX);
  }
}

bool HexagonDYLDRendezvous::Resolve() {
  const size_t word_size = 4;
  Rendezvous info;
  size_t address_size;
  size_t padding;
  addr_t info_addr;
  addr_t cursor;

  address_size = m_process->GetAddressByteSize();
  padding = address_size - word_size;

  if (m_rendezvous_addr == LLDB_INVALID_ADDRESS)
    cursor = info_addr = ResolveRendezvousAddress(m_process);
  else
    cursor = info_addr = m_rendezvous_addr;

  if (cursor == LLDB_INVALID_ADDRESS)
    return false;

  if (!(cursor = ReadWord(cursor, &info.version, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.map_addr)))
    return false;

  if (!(cursor = ReadPointer(cursor, &info.brk)))
    return false;

  if (!(cursor = ReadWord(cursor, &info.state, word_size)))
    return false;

  if (!(cursor = ReadPointer(cursor + padding, &info.ldbase)))
    return false;

  // The rendezvous was successfully read.  Update our internal state.
  m_rendezvous_addr = info_addr;
  m_previous = m_current;
  m_current = info;

  return UpdateSOEntries();
}

void HexagonDYLDRendezvous::SetRendezvousAddress(lldb::addr_t addr) {
  m_rendezvous_addr = addr;
}

bool HexagonDYLDRendezvous::IsValid() {
  return

}

bool HexagonDYLDRendezvous::UpdateSOEntries() {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  // When the previous and current states are consistent this is the first
  // time we have been asked to update.  Just take a snapshot of the currently
  // loaded modules.
  if (m_previous.state == eConsistent && m_current.state == eConsistent)
    return TakeSnapshot(m_soentries);

  // If we are about to add or remove a shared object clear out the current
  // state and take a snapshot of the currently loaded images.
  if (m_current.state == eAdd || m_current.state == eDelete) {
    // this is a fudge so that we can clear the assert below.
    m_previous.state = eConsistent;
    // We hit this assert on the 2nd run of this function after running the calc
    // example
    assert(m_previous.state == eConsistent);
    m_soentries.clear();
    m_added_soentries.clear();
    m_removed_soentries.clear();
    return TakeSnapshot(m_soentries);
  }
  assert(m_current.state == eConsistent);

  // Otherwise check the previous state to determine what to expect and update
  if (m_previous.state == eAdd)
    return UpdateSOEntriesForAddition();
  else if (m_previous.state == eDelete)
    return UpdateSOEntriesForDeletion();

  return false;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForAddition() {
  SOEntry entry;
  iterator pos;

  assert(m_previous.state == eAdd);

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    pos = std::find(m_soentries.begin(), m_soentries.end(), entry);
    if (pos == m_soentries.end()) {
      m_soentries.push_back(entry);
      m_added_soentries.push_back(entry);
    }
  }

  return true;
}

bool HexagonDYLDRendezvous::UpdateSOEntriesForDeletion() {
  SOEntryList entry_list;
  iterator pos;

  assert(m_previous.state == eDelete);

  if (!TakeSnapshot(entry_list))
    return false;

  for (iterator I = begin(); I != end(); ++I) {
    pos = std::find(entry_list.begin(), entry_list.end(), *I);
    if (pos == entry_list.end())
      m_removed_soentries.push_back(*I);
  }

  m_soentries = entry_list;
  return true;
}

bool HexagonDYLDRendezvous::TakeSnapshot(SOEntryList &entry_list) {
  SOEntry entry;

  if (m_current.map_addr == 0)
    return false;

  for (addr_t cursor = m_current.map_addr; cursor != 0; cursor = entry.next) {
    if (!ReadSOEntryFromMemory(cursor, entry))
      return false;

    // Only add shared libraries and not the executable.
    // On Linux this is indicated by an empty path in the entry.
    // On FreeBSD it is the name of the executable.
    if (entry.path.empty() || ::strcmp(entry.path.c_str(), m_exe_path) == 0)
      continue;

    entry_list.push_back(entry);
  }

  return true;
}

addr_t HexagonDYLDRendezvous::ReadWord(addr_t addr, uint64_t *dst,
                                       size_t size) {
  Error error;

  *dst = m_process->ReadUnsignedIntegerFromMemory(addr, size, 0, error);
  if (error.Fail())
    return 0;

  return addr + size;
}

addr_t HexagonDYLDRendezvous::ReadPointer(addr_t addr, addr_t *dst) {
  Error error;

  *dst = m_process->ReadPointerFromMemory(addr, error);
  if (error.Fail())
    return 0;

  return addr + m_process->GetAddressByteSize();
}

std::string HexagonDYLDRendezvous::ReadStringFromMemory(addr_t addr) {
  std::string str;
  Error error;
  size_t size;
  char c;

  if (addr == LLDB_INVALID_ADDRESS)
    return std::string();

  for (;;) {
    size = m_process->DoReadMemory(addr, &c, 1, error);
    if (size != 1 || error.Fail())
      return std::string();
    if (c == 0)
      break;
    else {
      str.push_back(c);
      addr++;
    }
  }

  return str;
}

bool HexagonDYLDRendezvous::ReadSOEntryFromMemory(lldb::addr_t addr,
                                                  SOEntry &entry) {
  entry.clear();
  entry.link_addr = addr;

  if (!(addr = ReadPointer(addr, &entry.base_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.path_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.dyn_addr)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.next)))
    return false;

  if (!(addr = ReadPointer(addr, &entry.prev)))
    return false;

  entry.path = ReadStringFromMemory(entry.path_addr);

  return true;
}

bool HexagonDYLDRendezvous::FindMetadata(const char *name, PThreadField field,
                                         uint32_t &value) {
  Target &target = m_process->GetTarget();

  SymbolContextList list;
  if (!target.GetImages().FindSymbolsWithNameAndType(ConstString(name),
                                                     eSymbolTypeAny, list))
    return false;

  Address address = list[0].symbol->GetAddress();
  addr_t addr = address.GetLoadAddress(&target);
  if (addr == LLDB_INVALID_ADDRESS)
    return false;

  Error error;
  value = (uint32_t)m_process->ReadUnsignedIntegerFromMemory(
      addr + field * sizeof(uint32_t), sizeof(uint32_t), 0, error);
  if (error.Fail())
    return false;

  if (field == eSize)
    value /= 8; // convert bits to bytes

  return true;
}

const HexagonDYLDRendezvous::ThreadInfo &
HexagonDYLDRendezvous::GetThreadInfo() {
  if (!m_thread_info.valid) {
    bool ok = true;

    ok &= FindMetadata("_thread_db_pthread_dtvp", eOffset,
                       m_thread_info.dtv_offset);
    ok &=
        FindMetadata("_thread_db_dtv_dtv", eSize, m_thread_info.dtv_slot_size);
    ok &= FindMetadata("_thread_db_link_map_l_tls_modid", eOffset,
                       m_thread_info.modid_offset);
    ok &= FindMetadata("_thread_db_dtv_t_pointer_val", eOffset,
                       m_thread_info.tls_offset);

    if (ok)
      m_thread_info.valid = true;
  }

  return m_thread_info;
}

void HexagonDYLDRendezvous::DumpToLog(Log *log) const {
  int state = GetState();

  if (!log)
    return;

  log->PutCString("HexagonDYLDRendezvous:");
  log->Printf("   Address: %" PRIx64, GetRendezvousAddress());
  log->Printf("   Version: %" PRIu64, GetVersion());
  log->Printf("   Link   : %" PRIx64, GetLinkMapAddress());
  log->Printf("   Break  : %" PRIx64, GetBreakAddress());
  log->Printf("   LDBase : %" PRIx64, GetLDBase());
  log->Printf("   State  : %s",
              (state == eConsistent)
                  ? "consistent"
                  : (state == eAdd)
                        ? "add"
                        : (state == eDelete) ? "delete" : "unknown");

  iterator I = begin();
  iterator E = end();

  if (I != E)
    log->PutCString("HexagonDYLDRendezvous SOEntries:");

  for (int i = 1; I != E; ++I, ++i) {
    log->Printf("\n   SOEntry [%d] %s", i, I->path.c_str());
    log->Printf("      Base : %" PRIx64, I->base_addr);
    log->Printf("      Path : %" PRIx64, I->path_addr);
    log->Printf("      Dyn  : %" PRIx64, I->dyn_addr);
    log->Printf("      Next : %" PRIx64, I->next);
    log->Printf("      Prev : %" PRIx64, I->prev);
  }
}


static addr_t ResolveRendezvousAddress(Process *process) {
  addr_t info_location;
  addr_t info_addr;
  Error error;

  info_location = process->GetImageInfoAddress();

  if (info_location == LLDB_INVALID_ADDRESS)
    return LLDB_INVALID_ADDRESS;

  info_addr = process->ReadPointerFromMemory(info_location, error);
  if (error.Fail())
    return LLDB_INVALID_ADDRESS;

  if (info_addr == 0)
    return LLDB_INVALID_ADDRESS;

  return info_addr;
}

//                     The LLVM Compiler Infrastructure

// C Includes
// C++ Includes
// Other libraries and framework includes
#include "lldb/Core/ArchSpec.h"
#include "lldb/Core/Error.h"
#include "lldb/Core/Log.h"
#include "lldb/Core/Module.h"
#include "lldb/Symbol/Symbol.h"
#include "lldb/Symbol/SymbolContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "lldb/Symbol/ObjectFile.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/Target.h"

#include "HexagonDYLDRendezvous.h"

using namespace lldb;
using namespace lldb_private;

/// Locates the address of the rendezvous structure.  Returns the address on
/// success and LLDB_INVALID_ADDRESS on failure.


#include "windows.h"
#include <stdio.h>
BYTE    b       =   0x12;
WORD    w       =   0x1234;
DWORD   dw      =   0x12345678;
char    str[]   =   "abcde";

{
    BYTE    lb  =   b;
    WORD    lw  =   w;
    DWORD   ldw =   dw;
    char    *lstr  =   str;

	printf("address: %02X\n", &(*lstr));

    return 0;
}


{
    BYTE    lb  =   b;
    WORD    lw  =   w;
    DWORD   ldw =   dw;
    char    *lstr  =   str;

	printf("address: %02X\n", &(*lstr));

    return 0;
}

#include "windows.h"
#include <stdio.h>
BYTE    b       =   0x12;
WORD    w       =   0x1234;
DWORD   dw      =   0x12345678;
char    str[]   =   "abcde";

int main(int argc, char *argv[])


#include "windows.h"
#include <stdio.h>
BYTE    b       =   0x12;
WORD    w       =   0x1234;
DWORD   dw      =   0x12345678;
char    str[]   =   "abcde";

int main(int argc, char *argv[])
{
    BYTE    lb  =   b;
    WORD    lw  =   w;
    DWORD   ldw =   dw;
    char    *lstr  =   str;


void RimEclipseFaultColors::setReservoirView( RimEclipseView* ownerReservoirView )

#include "RimEclipseFaultColors.h"

#include "RimEclipseCase.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimFaultInViewCollection.h"

#include "RiuMainWindow.h"

#include "cafPdmUiTreeOrdering.h"

CAF_PDM_SOURCE_INIT( RimEclipseFaultColors, "RimFaultResultSlot" );

RimEclipseFaultColors::RimEclipseFaultColors()
{
    CAF_PDM_InitObject( "Fault Result", ":/draw_style_faults_24x24.png", "", "" );

    CAF_PDM_InitField( &showCustomFaultResult, "ShowCustomFaultResult", false, "Show Custom Fault Result", "", "", "" );
    showCustomFaultResult.uiCapability()->setUiHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_customFaultResultColors,
                                "CustomResultSlot",
                                "Custom Fault Result",
                                ":/CellResult.png",
                                "",
                                "" );
    m_customFaultResultColors = new RimEclipseCellColors();
    m_customFaultResultColors.uiCapability()->setUiTreeHidden( true );
    m_customFaultResultColors->enableDeltaResults( true );
}

RimEclipseFaultColors::~RimEclipseFaultColors()
{
    delete m_customFaultResultColors;
    m_customFaultResultColors = nullptr;
}

{
    m_reservoirView = ownerReservoirView;
    m_customFaultResultColors->setReservoirView( ownerReservoirView );
}

void RimEclipseFaultColors::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
                                              const QVariant&            oldValue,
                                              const QVariant&            newValue )
{
    this->updateUiIconFromToggleField();

    if ( m_reservoirView ) m_reservoirView->scheduleCreateDisplayModelAndRedraw();
}

void RimEclipseFaultColors::initAfterRead()
{
    m_customFaultResultColors->initAfterRead();

    this->updateUiIconFromToggleField();
}

RimEclipseCellColors* RimEclipseFaultColors::customFaultResult()
{
    return this->m_customFaultResultColors();
}

void RimEclipseFaultColors::updateUiFieldsFromActiveResult()
{
    m_customFaultResultColors->updateUiFieldsFromActiveResult();
}

caf::PdmFieldHandle* RimEclipseFaultColors::objectToggleField()
{
    return &showCustomFaultResult;
}

void RimEclipseFaultColors::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    caf::PdmUiGroup* group1 = uiOrdering.addNewGroup( "Result" );
    m_customFaultResultColors->uiOrdering( uiConfigName, *group1 );

    RimEclipseView* eclipseView = nullptr;
    this->firstAncestorOfType( eclipseView );
    if ( eclipseView )
    {
        eclipseView->faultCollection()->uiOrderingFaults( uiConfigName, uiOrdering );
    }
}

bool RimEclipseFaultColors::hasValidCustomResult()
{
    if ( this->showCustomFaultResult() )
    {
        if ( m_customFaultResultColors->hasResult() || m_customFaultResultColors->isTernarySaturationSelected() )
        {
            return true;
        }
    }

    return false;
}

void RimEclipseFaultColors::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
    m_customFaultResultColors()->defineUiTreeOrdering( uiTreeOrdering, uiConfigName );
}


{
    m_reservoirView = ownerReservoirView;
    m_customFaultResultColors->setReservoirView( ownerReservoirView );
}

#include "RimEclipseFaultColors.h"

#include "RimEclipseCase.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimFaultInViewCollection.h"

#include "RiuMainWindow.h"

#include "cafPdmUiTreeOrdering.h"

CAF_PDM_SOURCE_INIT( RimEclipseFaultColors, "RimFaultResultSlot" );

RimEclipseFaultColors::RimEclipseFaultColors()
{
    CAF_PDM_InitObject( "Fault Result", ":/draw_style_faults_24x24.png", "", "" );

    CAF_PDM_InitField( &showCustomFaultResult, "ShowCustomFaultResult", false, "Show Custom Fault Result", "", "", "" );
    showCustomFaultResult.uiCapability()->setUiHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_customFaultResultColors,
                                "CustomResultSlot",
                                "Custom Fault Result",
                                ":/CellResult.png",
                                "",
                                "" );
    m_customFaultResultColors = new RimEclipseCellColors();
    m_customFaultResultColors.uiCapability()->setUiTreeHidden( true );
    m_customFaultResultColors->enableDeltaResults( true );
}

RimEclipseFaultColors::~RimEclipseFaultColors()
{
    delete m_customFaultResultColors;
    m_customFaultResultColors = nullptr;
}

void RimEclipseFaultColors::setReservoirView( RimEclipseView* ownerReservoirView )

void RimEclipseFaultColors::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
                                              const QVariant&            oldValue,
                                              const QVariant&            newValue )
{
    this->updateUiIconFromToggleField();

    if ( m_reservoirView ) m_reservoirView->scheduleCreateDisplayModelAndRedraw();
}

void RimEclipseFaultColors::initAfterRead()
{
    m_customFaultResultColors->initAfterRead();

    this->updateUiIconFromToggleField();
}

RimEclipseCellColors* RimEclipseFaultColors::customFaultResult()
{
    return this->m_customFaultResultColors();
}

void RimEclipseFaultColors::updateUiFieldsFromActiveResult()
{
    m_customFaultResultColors->updateUiFieldsFromActiveResult();
}

caf::PdmFieldHandle* RimEclipseFaultColors::objectToggleField()
{
    return &showCustomFaultResult;
}

void RimEclipseFaultColors::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    caf::PdmUiGroup* group1 = uiOrdering.addNewGroup( "Result" );
    m_customFaultResultColors->uiOrdering( uiConfigName, *group1 );

    RimEclipseView* eclipseView = nullptr;
    this->firstAncestorOfType( eclipseView );
    if ( eclipseView )
    {
        eclipseView->faultCollection()->uiOrderingFaults( uiConfigName, uiOrdering );
    }
}

bool RimEclipseFaultColors::hasValidCustomResult()
{
    if ( this->showCustomFaultResult() )
    {
        if ( m_customFaultResultColors->hasResult() || m_customFaultResultColors->isTernarySaturationSelected() )
        {
            return true;
        }
    }

    return false;
}

void RimEclipseFaultColors::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
    m_customFaultResultColors()->defineUiTreeOrdering( uiTreeOrdering, uiConfigName );
}


rs->uiOrdering( uiConfigName, *group1 );

#include "RimEclipseFaultColors.h"

#include "RimEclipseCase.h"
#include "RimEclipseCellColors.h"
#include "RimEclipseView.h"
#include "RimFaultInViewCollection.h"

#include "RiuMainWindow.h"

#include "cafPdmUiTreeOrdering.h"

CAF_PDM_SOURCE_INIT( RimEclipseFaultColors, "RimFaultResultSlot" );

RimEclipseFaultColors::RimEclipseFaultColors()
{
    CAF_PDM_InitObject( "Fault Result", ":/draw_style_faults_24x24.png", "", "" );

    CAF_PDM_InitField( &showCustomFaultResult, "ShowCustomFaultResult", false, "Show Custom Fault Result", "", "", "" );
    showCustomFaultResult.uiCapability()->setUiHidden( true );

    CAF_PDM_InitFieldNoDefault( &m_customFaultResultColors,
                                "CustomResultSlot",
                                "Custom Fault Result",
                                ":/CellResult.png",
                                "",
                                "" );
    m_customFaultResultColors = new RimEclipseCellColors();
    m_customFaultResultColors.uiCapability()->setUiTreeHidden( true );
    m_customFaultResultColors->enableDeltaResults( true );
}

RimEclipseFaultColors::~RimEclipseFaultColors()
{
    delete m_customFaultResultColors;
    m_customFaultResultColors = nullptr;
}

void RimEclipseFaultColors::setReservoirView( RimEclipseView* ownerReservoirView )
{
    m_reservoirView = ownerReservoirView;
    m_customFaultResultColors->setReservoirView( ownerReservoirView );
}

void RimEclipseFaultColors::fieldChangedByUi( const caf::PdmFieldHandle* changedField,
                                              const QVariant&            oldValue,
                                              const QVariant&            newValue )
{
    this->updateUiIconFromToggleField();

    if ( m_reservoirView ) m_reservoirView->scheduleCreateDisplayModelAndRedraw();
}

void RimEclipseFaultColors::initAfterRead()
{
    m_customFaultResultColors->initAfterRead();

    this->updateUiIconFromToggleField();
}

RimEclipseCellColors* RimEclipseFaultColors::customFaultResult()
{
    return this->m_customFaultResultColors();
}

void RimEclipseFaultColors::updateUiFieldsFromActiveResult()
{
    m_customFaultResultColors->updateUiFieldsFromActiveResult();
}

caf::PdmFieldHandle* RimEclipseFaultColors::objectToggleField()
{
    return &showCustomFaultResult;
}

void RimEclipseFaultColors::defineUiOrdering( QString uiConfigName, caf::PdmUiOrdering& uiOrdering )
{
    caf::PdmUiGroup* group1 = uiOrdering.addNewGroup( "Result" );
    m_customFaultResultColo

    RimEclipseView* eclipseView = nullptr;
    this->firstAncestorOfType( eclipseView );
    if ( eclipseView )
    {
        eclipseView->faultCollection()->uiOrderingFaults( uiConfigName, uiOrdering );
    }
}

bool RimEclipseFaultColors::hasValidCustomResult()
{
    if ( this->showCustomFaultResult() )
    {
        if ( m_customFaultResultColors->hasResult() || m_customFaultResultColors->isTernarySaturationSelected() )
        {
            return true;
        }
    }

    return false;
}

void RimEclipseFaultColors::defineUiTreeOrdering( caf::PdmUiTreeOrdering& uiTreeOrdering, QString uiConfigName /*= ""*/ )
{
    m_customFaultResultColors()->defineUiTreeOrdering( uiTreeOrdering, uiConfigName );
}


#include "mutable_data_inst.h"
#include "primitive_base.hpp"
#include "impls/implementation_map.hpp"

namespace cldnn {
namespace ocl {

struct mutable_data_impl : public typed_primitive_impl_ocl<mutable_data> {
    using parent = typed_primitive_impl_ocl<mutable_data>;
    using parent::parent;

    std::unique_ptr<primitive_impl> clone() const override {
        return make_unique<mutable_data_impl>(*this);
    }

public:
    static primitive_impl* create(mutable_data_node const& arg) { return new mutable_data_impl(arg, {}); }
};

namespace detail {

{
    implementation_map<mutable_data>::add(impl_types::ocl, mutable_data_impl::create, {});
}

}  // namespace detail
}  // namespace ocl
}  // namespace cldnn


{ return new mutable_data_impl(arg, {}); }

#include "mutable_data_inst.h"
#include "primitive_base.hpp"
#include "impls/implementation_map.hpp"

namespace cldnn {
namespace ocl {

struct mutable_data_impl : public typed_primitive_impl_ocl<mutable_data> {
    using parent = typed_primitive_impl_ocl<mutable_data>;
    using parent::parent;

    std::unique_ptr<primitive_impl> clone() const override {
        return make_unique<mutable_data_impl>(*this);
    }

public:
    static primitive_impl* create(mutable_data_node const& arg)

};

namespace detail {

attach_mutable_data_impl::attach_mutable_data_impl() {
    implementation_map<mutable_data>::add(impl_types::ocl, mutable_data_impl::create, {});
}

}  // namespace detail
}  // namespace ocl
}  // namespace cldnn


#include "mutable_data_inst.h"
#include "primitive_base.hpp"
#include "impls/implementation_map.hpp"

namespace cldnn {
namespace ocl {

struct mutable_data_impl : public typed_primitive_impl_ocl<mutable_data> {
    using parent = typed_primitive_impl_ocl<mutable_data>;
    using parent::parent;

    std::unique_ptr<primitive_impl> clone() const override {
        return make_unique<

    }

public:
    static primitive_impl* create(mutable_data_node const& arg) { return new mutable_data_impl(arg, {}); }
};

namespace detail {

attach_mutable_data_impl::attach_mutable_data_impl() {
    implementation_map<mutable_data>::add(impl_types::ocl, mutable_data_impl::create, {});
}

}  // namespace detail
}  // namespace ocl
}  // namespace cldnn


#include <cstdio>
#include <set>
#include <utility>
#define swap(x, y) {int _x = x; x = y; y = _x;}
using namespace std;

typedef pair<int, int> ii;

{
	int k, x, y;
	set<ii> ans;
	set<ii>::iterator it;
	while(scanf("%d", &k)!=EOF){
		ans.clear();
		for(x=k+1; x<=2*k; x++){
			if ((k*x)%(x-k)==0){
				y = (k*x)/(x-k);
				if (x>y) swap(x,y);
				ans.insert(ii(x,y));
			}
		}
		printf("%d\n", (int)ans.size());
		for(it = ans.begin(); it!=ans.end(); it++){
			printf("1/%d = 1/%d + 1/%d\n", k, it->second, it->first);
		}
	}
	return 0;
}


{
	int k, x, y;
	set<ii> ans;
	set<ii>::iterator it;
	while(scanf("%d", &k)!=EOF){
		ans.clear();
		for(x=k+1; x<=2*k; x++){
			if ((k*x)%(x-k)==0){
				y = (k*x)/(x-k);
				if (x>y) swap(x,y);
				ans.insert(ii(x,y));
			}
		}
		printf("%d\n", (int)ans.size());
		for(it = ans.begin(); it!=ans.end(); it++){
			printf("1/%d = 1/%d + 1/%d\n", k, it->second, it->first);
		}
	}
	return 0;
}

#include <cstdio>
#include <set>
#include <utility>
#define swap(x, y) {int _x = x; x = y; y = _x;}
using namespace std;

typedef pair<int, int> ii;

int main()


#include <cstdio>
#include <set>
#include <utility>
#define swap(x, y) {int _x = x; x = y; y = _x;}
using namespace std;

typedef pair<int, int> ii;

int main()
{
	int k, x

	set<ii> ans;
	set<ii>::iterator it;
	while(scanf("%d", &k)!=EOF){
		ans.clear();
		for(x=k+1; x<=2*k; x++){
			if ((k*x)%(x-k)==0){
				y = (k*x)/(x-k);
				if (x>y) swap(x,y);
				ans.insert(ii(x,y));
			}
		}
		printf("%d\n", (int)ans.size());
		for(it = ans.begin(); it!=ans.end(); it++){
			printf("1/%d = 1/%d + 1/%d\n", k, it->second, it->first);
		}
	}
	return 0;
}


while(scanf("%d", &k)!=EOF){
		ans.clear();
		for(x=k+1; x<=2*k; x++){
			if ((k*x)%(x-k)==0){
				y = (k*x)/(x-k);
				if (x>y) swap(x,y);
				ans.insert(ii(x,y));
			}
		}
		printf("%d\n", (int)ans.size());
		for(it = ans.begin(); it!=ans.end(); it++){
			printf("1/%d = 1/%d + 1/%d\n", k, it->second, it->first);
		}
	}

#include <cstdio>
#include <set>
#include <utility>
#define swap(x, y) {int _x = x; x = y; y = _x;}
using namespace std;

typedef pair<int, int> ii;

int main()
{
	int k, x, y;
	set<ii> ans;
	set<ii>::iterator it;


/*Header-MicMac-eLiSe-25/06/2007

    MicMac : Multi Image Correspondances par Methodes Automatiques de Correlation
    eLiSe  : ELements of an Image Software Environnement

    www.micmac.ign.fr

    Author : Marc Pierrot Deseilligny
    Contributors : Gregoire Maillet, Didier Boldo.

[1] M. Pierrot-Deseilligny, N. Paparoditis.
    "A multiresolution and optimization-based image matching approach:
    An application to surface reconstruction from SPOT5-HRS stereo imagery."
    In IAPRS vol XXXVI-1/W41 in ISPRS Workshop On Topographic Mapping From Space
    (With Special Emphasis on Small Satellites), Ankara, Turquie, 02-2006.

[2] M. Pierrot-Deseilligny, "MicMac, un lociel de mise en correspondance
    d'images, adapte au contexte geograhique" to appears in
    Bulletin d'information de l'Institut Geographique National, 2007.

Francais :

   MicMac est un logiciel de mise en correspondance d'image adapte
   au contexte de recherche en information geographique. Il s'appuie sur
   la bibliotheque de manipulation d'image eLiSe. Il est distibue sous la

English :

    MicMac is an open source software specialized in image matching
    for research in geographic information. MicMac is built on the

Header-MicMac-eLiSe-25/06/2007*/
#include "general/all.h"
#include "private/all.h"
#include <algorithm>

{
    std::string aName;
    int aTMin = 20;
    int aTMax = -1;
    int aSzEl = 1;
    ElInitArgMain
    (
         argc,argv,
         LArgMain() 	<< EAM(aName) ,
         LArgMain()  << EAM(aTMin,"TMin",true)
	             << EAM(aTMax,"TMax",true)
   );

   ELISE_fp aFile(aName.c_str(),ELISE_fp::READ);

   aFile.seek(0,ELISE_fp::send);
   int aNbOct = aFile.tell();
   aFile.seek(0,ELISE_fp::sbegin);
   int aNbEl = aNbOct / aSzEl;

   std::cout << "Sz = " << aNbEl << "\n";

   if (aTMax==-1)
   {
      aTMax = round_ni(10.0*sqrt(aNbEl));
   }
   aTMax = Pow_of_2_sup(aTMax);

   std::cout << "TMax = " << aTMax << "\n";

   int aSz2[2];
   aSz2[0] = aNbEl;
   aSz2[1] = 1;

   Elise_File_Im  aFIm
                  (
                       aName.c_str(),
                       2,
                       aSz2,
                       GenIm::u_int1,
                       1,
		       0
		  );

   Im2D_REAL8 aI1(aTMax,1);
   Im2D_REAL8 aI2(aTMax,1);

   ELISE_COPY
   (
       aI1.all_pts(),
       trans(aFIm.in(),Pt2di((aNbEl-aTMax)/2,0)),
       (aI1.out()|aI2.out())
   );

   ElFFTCorrelCirc(aI1,aI2);

   int aPMax[2];
   ELISE_COPY
   (
       rectangle
       (
          Pt2di(aTMin,0),
          Pt2di(aTMax-aTMin,1)
       ),
       aI1.in(),
       WhichMax(aPMax,2)
   );

   std::cout << "SzX ?= " << aPMax[0] << "\n";

   return 0;
}

/*Footer-MicMac-eLiSe-25/06/2007

Ce logiciel est un programme informatique servant à la mise en
correspondances d'images pour la reconstruction du relief.

respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
termes.
Footer-MicMac-eLiSe-25/06/2007*/


/*Header-MicMac-eLiSe-25/06/2007

    MicMac : Multi Image Correspondances par Methodes Automatiques de Correlation
    eLiSe  : ELements of an Image Software Environnement

    www.micmac.ign.fr

    Author : Marc Pierrot Deseilligny
    Contributors : Gregoire Maillet, Didier Boldo.

[1] M. Pierrot-Deseilligny, N. Paparoditis.
    "A multiresolution and optimization-based image matching approach:
    An application to surface reconstruction from SPOT5-HRS stereo imagery."
    In IAPRS vol XXXVI-1/W41 in ISPRS Workshop On Topographic Mapping From Space
    (With Special Emphasis on Small Satellites), Ankara, Turquie, 02-2006.

[2] M. Pierrot-Deseilligny, "MicMac, un lociel de mise en correspondance
    d'images, adapte au contexte geograhique" to appears in
    Bulletin d'information de l'Institut Geographique National, 2007.

Francais :

   MicMac est un logiciel de mise en correspondance d'image adapte
   au contexte de recherche en information geographique. Il s'appuie sur
   la bibliotheque de manipulation d'image eLiSe. Il est distibue sous la

English :

    MicMac is an open source software specialized in image matching
    for research in geographic information. MicMac is built on the

Header-MicMac-eLiSe-25/06/2007*/
#include "general/all.h"
#include "private/all.h"
#include <algorithm>

int main(int argc,char ** argv)
{
    std::string aName;
    int aTMin = 20;
    int aTMax = -1;
    int aSzEl = 1;
    ElInitArgMain
    (
         argc,argv,
         LArgMain() 	<< EAM(aName) ,
         LArgMain()  << EAM(aTMin,"TMin",true)
	             << EAM(aTMax,"TMax",true)
   );

   ELISE_fp aFile(aName.c_str(),ELISE_fp::READ);

   aFile.seek(0,ELISE_fp::send);
   int aNbOct = aFile.tell();
   aFile.seek(0,ELISE_fp::sbegin);
   int aNbEl = aNbOct / aSzEl;

   std::cout << "Sz = " << aNbEl << "\n";

   if (aTMax==-1)
   {
      aTMax = round_ni(10.0*sqrt(aNbEl));
   }
   aTMax = Pow_of_2_sup(aTMax);

   std::cout << "TMax = " << aTMax << "\n";

   int aSz2[2];
   aSz2[0] = aNbEl;

   Elise_File_Im  aFIm
                  (
                       aName.c_str(),
                       2,
                       aSz2,
                       GenIm::u_int1,
                       1,
		       0
		  );

   Im2D_REAL8 aI1(aTMax,1);
   Im2D_REAL8 aI2(aTMax,1);

   ELISE_COPY
   (
       aI1.all_pts(),
       trans(aFIm.in(),Pt2di((aNbEl-aTMax)/2,0)),
       (aI1.out()|aI2.out())
   );

   ElFFTCorrelCirc(aI1,aI2);

   int aPMax[2];
   ELISE_COPY
   (
       rectangle
       (
          Pt2di(aTMin,0),
          Pt2di(aTMax-aTMin,1)
       ),
       aI1.in(),
       WhichMax(aPMax,2)
   );

   std::cout << "SzX ?= " << aPMax[0] << "\n";

   return 0;
}

/*Footer-MicMac-eLiSe-25/06/2007

Ce logiciel est un programme informatique servant à la mise en
correspondances d'images pour la reconstruction du relief.

respectant les principes de diffusion des logiciels libres. Vous pouvez
utiliser, modifier et/ou redistribuer ce programme sous les conditions

En contrepartie de l'accessibilité au code source et des droits de copie,
de modification et de redistribution accordés par cette licence, il n'est
offert aux utilisateurs qu'une garantie limitée.  Pour les mêmes raisons,
seule une responsabilité restreinte pèse sur l'auteur du programme,  le
titulaire des droits patrimoniaux et les concédants successifs.

A cet égard  l'attention de l'utilisateur est attirée sur les risques
associés au chargement,  à l'utilisation,  à la modification et/ou au
développement et à la reproduction du logiciel par l'utilisateur étant
donné sa spécificité de logiciel libre, qui peut le rendre complexe à
manipuler et qui le réserve donc à des développeurs et des professionnels
avertis possédant  des  connaissances  informatiques approfondies.  Les
utilisateurs sont donc invités à charger  et  tester  l'adéquation  du
logiciel à leurs besoins dans des conditions permettant d'assurer la
sécurité de leurs systèmes et ou de leurs données et, plus généralement,
à l'utiliser et l'exploiter dans les mêmes conditions de sécurité.

Le fait que vous puissiez accéder à cet en-tête signifie que vous avez
termes.
Footer-MicMac-eLiSe-25/06/2007*/


void worker( shared_ptr< Service > service, shared_ptr< Settings > settings )

//System Includes
#include <thread>
#include <chrono>
#include <memory>
#include <functional>

//Project Includes
#include <restbed>

//External Includes
#include <asio.hpp>
#include <catch.hpp>

//System Namespaces
using std::thread;
using std::shared_ptr;
using std::make_shared;
using std::chrono::seconds;

//Project Namespaces
using namespace restbed;

//External Namespaces
using asio::ip::tcp;
using asio::connect;
using asio::io_service;
using asio::system_error;

bool exception_was_thrown = false;

{
    try
    {
        service->start( settings );
    }
    catch ( const system_error& se )
    {
        if ( se.code( ) == asio::error::eof )
        {
            exception_was_thrown = true;
        }
    }
}

void wait_for_service_initialisation( void )
{
    std::this_thread::sleep_for( seconds( 1 ) );
}

TEST_CASE( "peer closes connection without sending data", "[service]" )
{
    auto resource = make_shared< Resource >( );
    resource->set_path( "test" );

    auto settings = make_shared< Settings >( );
    settings->set_port( 1984 );

    auto service = make_shared< Service >( );
    service->publish( resource );

    thread restbed_thread( worker, service, settings );

    wait_for_service_initialisation( );

    io_service io_service;
    tcp::socket socket( io_service );
    tcp::resolver resolver( io_service );
    connect( socket, resolver.resolve( { "localhost", "1984" } ) );

    socket.close( );

    service->stop( );

    restbed_thread.join( );

    REQUIRE_FALSE( exception_was_thrown );
}


{
    try
    {
        service->start( settings );
    }
    catch ( const system_error& se )
    {
        if ( se.code( ) == asio::error::eof )
        {
            exception_was_thrown = true;
        }
    }
}

//System Includes
#include <thread>
#include <chrono>
#include <memory>
#include <functional>

//Project Includes
#include <restbed>

//External Includes
#include <asio.hpp>
#include <catch.hpp>

//System Namespaces
using std::thread;
using std::shared_ptr;
using std::make_shared;
using std::chrono::seconds;

//Project Namespaces
using namespace restbed;

//External Namespaces
using asio::ip::tcp;
using asio::connect;
using asio::io_service;
using asio::system_error;

bool exception_was_thrown = false;

void worker( shared_ptr< Service > service, shared_ptr< Settings > settings )

void wait_for_service_initialisation( void )
{
    std::this_thread::sleep_for( seconds( 1 ) );
}

TEST_CASE( "peer closes connection without sending data", "[service]" )
{
    auto resource = make_shared< Resource >( );
    resource->set_path( "test" );

    auto settings = make_shared< Settings >( );
    settings->set_port( 1984 );

    auto service = make_shared< Service >( );
    service->publish( resource );

    thread restbed_thread( worker, service, settings );

    wait_for_service_initialisation( );

    io_service io_service;
    tcp::socket socket( io_service );
    tcp::resolver resolver( io_service );
    connect( socket, resolver.resolve( { "localhost", "1984" } ) );

    socket.close( );

    service->stop( );

    restbed_thread.join( );

    REQUIRE_FALSE( exception_was_thrown );
}


//System Includes
#include <thread>
#include <chrono>
#include <memory>
#include <functional>

//Project Includes
#include <restbed>

//External Includes
#include <asio.hpp>
#include <catch.hpp>

//System Namespaces
using std::thread;
using std::shared_ptr;
using std::make_shared;
using std::chrono::seconds;

//Project Namespaces
using namespace restbed;

//External Namespaces
using asio::ip::tcp;
using asio::connect;
using asio::io_service;
using asio::system_error;

bool exception_was_thrown = false;

void worker( shared_ptr< Service > service, shared_ptr< Settings > settings )
{
    try
    {
        service->start( settings );
    }
    catch ( const system_error& se )
    {
        if ( se.code( ) == asio::error::eof )
        {
            exception_was_thrown = true;
        }
    }
}

void wait_for_service_initialisation( void )
{
    std::this_thread::sleep_for( seconds(

}

TEST_CASE( "peer closes connection without sending data", "[service]" )
{
    auto resource = make_shared< Resource >( );
    resource->set_path( "test" );

    auto settings = make_shared< Settings >( );
    settings->set_port( 1984 );

    auto service = make_shared< Service >( );
    service->publish( resource );

    thread restbed_thread( worker, service, settings );

    wait_for_service_initialisation( );

    io_service io_service;
    tcp::socket socket( io_service );
    tcp::resolver resolver( io_service );
    connect( socket, resolver.resolve( { "localhost", "1984" } ) );

    socket.close( );

    service->stop( );

    restbed_thread.join( );

    REQUIRE_FALSE( exception_was_thrown );
}


try
    {
        service->start( settings );
    }
    catch ( const system_error& se )
    {
        if ( se.code( ) == asio::error::eof )
        {
            exception_was_thrown = true;
        }
    }

//System Includes
#include <thread>
#include <chrono>
#include <memory>
#include <functional>

//Project Includes
#include <restbed>

//External Includes
#include <asio.hpp>
#include <catch.hpp>

//System Namespaces
using std::thread;
using std::shared_ptr;
using std::make_shared;
using std::chrono::seconds;

//Project Namespaces
using namespace restbed;

//External Namespaces
using asio::ip::tcp;
using asio::connect;
using asio::io_service;
using asio::system_error;

bool exception_was_thrown = false;

void worker( shared_ptr< Service > service, shared_ptr< Settings > settings )
{

}

void wait_for_service_initialisation( void )
{
    std::this_thread::sleep_for( seconds( 1 ) );
}

TEST_CASE( "peer closes connection without sending data", "[service]" )
{
    auto resource = make_shared< Resource >( );
    resource->set_path( "test" );

    auto settings = make_shared< Settings >( );
    settings->set_port( 1984 );

    auto service = make_shared< Service >( );
    service->publish( resource );

    thread restbed_thread( worker, service, settings );

    wait_for_service_initialisation( );

    io_service io_service;
    tcp::socket socket( io_service );
    tcp::resolver resolver( io_service );
    connect( socket, resolver.resolve( { "localhost", "1984" } ) );

    socket.close( );

    service->stop( );

    restbed_thread.join( );

    REQUIRE_FALSE( exception_was_thrown );
}


FORCEFEEDBACK::FORCEFEEDBACK( string device, std::ostream & error_output, std::ostream & info_output )
	: device_name(device), enabled(true), stop_and_play(false), lastforce(0)

#include "pch.h"

#include "forcefeedback.h"

#ifdef ENABLE_FORCE_FEEDBACK

#include <cstring>
#include <string>

using std::string;
using std::endl;

/* Number of bits for 1 unsigned char */
#define nBitsPerUchar          (sizeof(unsigned char) * 8)

/* Number of unsigned chars to contain a given number of bits */
#define nUcharsForNBits(nBits) ((((nBits)-1)/nBitsPerUchar)+1)

/* Index=Offset of given bit in 1 unsigned char */
#define bitOffsetInUchar(bit)  ((bit)%nBitsPerUchar)

/* Index=Offset of the unsigned char associated to the bit
   at the given index=offset */
#define ucharIndexForBit(bit)  ((bit)/nBitsPerUchar)

/* Value of an unsigned char with bit set at given index=offset */
#define ucharValueForBit(bit)  (((unsigned char)(1))<<bitOffsetInUchar(bit))

/* Test the bit with given index=offset in an unsigned char array */
#define testBit(bit, array)    ((array[ucharIndexForBit(bit)] >> bitOffsetInUchar(bit)) & 1)

{
	unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
	unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
	unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];

	struct input_event event;
	int valbuf[16];

	// Open event device with write permission
	device_handle = open(device_name.c_str(),O_RDWR|O_NONBLOCK);
	if (device_handle<0)
	{
		error_output << "force feedback: can not open " << device_name << " (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which buttons has the device?
	memset(key_bits,0,sizeof(key_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_KEY,sizeof(key_bits)),key_bits)<0)
	{
		error_output << "force feedback: can not get key bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axes has the device?
	memset(abs_bits,0,sizeof(abs_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_ABS,sizeof(abs_bits)),abs_bits)<0)
	{
		error_output << "force feedback: can not get abs bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Now get some information about force feedback
	memset(ff_bits,0,sizeof(ff_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_FF ,sizeof(ff_bits)),ff_bits)<0)
	{
		error_output << "force feedback: can not get ff bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axis is the x-axis?
	if      (testBit(ABS_X    ,abs_bits)) axis_code=ABS_X;
	else if (testBit(ABS_RX   ,abs_bits)) axis_code=ABS_RX;
	else if (testBit(ABS_WHEEL,abs_bits)) axis_code=ABS_WHEEL;
	else
	{
		error_output << "force feedback: no suitable x-axis found [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// get axis value range
	if (ioctl(device_handle,EVIOCGABS(axis_code),valbuf)<0)
	{
		error_output << "force feedback: can not get axis value range (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}
	axis_min=valbuf[1];
	axis_max=valbuf[2];
	if (axis_min>=axis_max)
	{
		error_output << "force feedback: bad axis value range (" << axis_min << "," << axis_max << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// force feedback supported?
	if (!testBit(FF_CONSTANT,ff_bits))
	{
		error_output << "force feedback: device (or driver) has no force feedback support [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Switch off auto centering
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=FF_AUTOCENTER;
	event.value=0;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: failed to disable auto centering (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Initialize constant force effect
	memset(&effect,0,sizeof(effect));
	effect.type=FF_CONSTANT;
	effect.id=-1;
	effect.trigger.button=0;
	effect.trigger.interval=0;
	effect.replay.length=0xffff;
	effect.replay.delay=0;
	effect.u.constant.level=0;
	effect.direction=0xC000;
	effect.u.constant.envelope.attack_length=0;
	effect.u.constant.envelope.attack_level=0;
	effect.u.constant.envelope.fade_length=0;
	effect.u.constant.envelope.fade_level=0;

	// Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
		error_output << "force feedback: uploading effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Start effect */
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=effect.id;
	event.value=1;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	info_output << "Force feedback initialized successfully" << std::endl;
}

void FORCEFEEDBACK::update(double force, double * position, double dt, std::ostream & error_output)
{
	if ( !enabled )
		return;

	struct input_event event;

        // Delete effect
	if (stop_and_play && effect.id!=-1)
	{
		if (ioctl(device_handle,EVIOCRMFF,effect.id)==-1) {
			error_output << "force feedback: removing effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
		effect.id=-1;
	}

        // Set force
	if (force>1.0) force=1.0;
	if (force<-1.0) force=-1.0;
	effect.direction=0xC000;
	effect.u.constant.level=(short)(force*32767.0); /* only to be safe */
	effect.u.constant.envelope.attack_level=effect.u.constant.level;
	effect.u.constant.envelope.fade_level=effect.u.constant.level;
	/*effect.u.constant.envelope.attack_level = (short)(lastforce*32767.0);
	effect.u.constant.envelope.attack_length = (short)(dt*1000.0);
	effect.u.constant.envelope.fade_length = (short)(dt*1000.0);

	lastforce = force;

        // Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
              //error_output << "error uploading effect" << endl;
                /* We do not exit here. Indeed, too frequent updates may be
				* refused, but that is not a fatal error */
	}
	else if (effect.id!=-1)
        // Start effect
	{
		memset(&event,0,sizeof(event));
		event.type=EV_FF;
		event.code=effect.id;
		event.value=1;
		if (write(device_handle,&event,sizeof(event))!=sizeof(event))
		{
			error_output << "force feedback: re-starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
	}

        // Get events
	while (read(device_handle,&event,sizeof(event))==sizeof(event))
	{
		if (event.type==EV_ABS && event.code==axis_code)
		{
			*position=((double)(((short)event.value)-axis_min))*2.0/(axis_max-axis_min)-1.0;
			if (*position>1.0) *position=1.0;
			else if (*position<-1.0) *position=-1.0;
		}
	}
}

#endif // ENABLE_FORCE_FEEDBACK


#include "pch.h"

#include "forcefeedback.h"

#ifdef ENABLE_FORCE_FEEDBACK

#include <cstring>
#include <string>

using std::string;
using std::endl;

/* Number of bits for 1 unsigned char */
#define nBitsPerUchar          (sizeof(unsigned char) * 8)

/* Number of unsigned chars to contain a given number of bits */
#define nUcharsForNBits(nBits) ((((nBits)-1)/nBitsPerUchar)+1)

/* Index=Offset of given bit in 1 unsigned char */
#define bitOffsetInUchar(bit)  ((bit)%nBitsPerUchar)

/* Index=Offset of the unsigned char associated to the bit
   at the given index=offset */
#define ucharIndexForBit(bit)  ((bit)/nBitsPerUchar)

/* Value of an unsigned char with bit set at given index=offset */
#define ucharValueForBit(bit)  (((unsigned char)(1))<<bitOffsetInUchar(bit))

/* Test the bit with given index=offset in an unsigned char array */
#define testBit(bit, array)    ((array[ucharIndexForBit(bit)] >> bitOffsetInUchar(bit)) & 1)

FORCEFEEDBACK::FORCEFEEDBACK( string device, std::ostream & error_output, std::ostream & info_output )
	: device_name(device), enabled(true), stop_and_play(false), lastforce(0)
{
	unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
	unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
	unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];

	struct input_event event;
	int valbuf[16];

	// Open event device with write permission
	device_handle = open(device_name.c_str(),O_RDWR|O_NONBLOCK);
	if (device_handle<0)
	{
		error_output << "force feedback: can not open " << device_name << " (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which buttons has the device?
	memset(key_bits,0,sizeof(key_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_KEY,sizeof(key_bits)),key_bits)<0)
	{
		error_output << "force feedback: can not get key bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axes has the device?
	memset(abs_bits,0,sizeof(abs_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_ABS,sizeof(abs_bits)),abs_bits)<0)
	{
		error_output << "force feedback: can not get abs bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Now get some information about force feedback
	memset(ff_bits,0,sizeof(ff_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_FF ,sizeof(ff_bits)),ff_bits)<0)
	{
		error_output << "force feedback: can not get ff bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axis is the x-axis?
	if      (testBit(ABS_X    ,abs_bits)) axis_code=ABS_X;
	else if (testBit(ABS_RX   ,abs_bits)) axis_code=ABS_RX;
	else if (testBit(ABS_WHEEL,abs_bits)) axis_code=ABS_WHEEL;
	else
	{
		error_output << "force feedback: no suitable x-axis found [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// get axis value range
	if (ioctl(device_handle,EVIOCGABS(axis_code),valbuf)<0)
	{
		error_output << "force feedback: can not get axis value range (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}
	axis_min=valbuf[1];
	axis_max=valbuf[2];
	if (axis_min>=axis_max)
	{
		error_output << "force feedback: bad axis value range (" << axis_min << "," << axis_max << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// force feedback supported?
	if (!testBit(FF_CONSTANT,ff_bits))
	{
		error_output << "force feedback: device (or driver) has no force feedback support [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Switch off auto centering
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=FF_AUTOCENTER;
	event.value=0;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: failed to disable auto centering (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Initialize constant force effect
	memset(&effect,0,sizeof(effect));
	effect.type=FF_CONSTANT;
	effect.id=-1;
	effect.trigger.button=0;
	effect.trigger.interval=0;
	effect.replay.length=0xffff;
	effect.replay.delay=0;
	effect.u.constant.level=0;
	effect.direction=0xC000;
	effect.u.constant.envelope.attack_length=0;
	effect.u.constant.envelope.attack_level=0;
	effect.u.constant.envelope.fade_length=0;
	effect.u.constant.envelope.fade_level=0;

	// Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
		error_output << "force feedback: uploading effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Start effect */
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=effect.id;
	event.value=1;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	info_output << "Force feedback initialized successfully" << std::endl;
}

void FORCEFEEDBACK::update(double force, double * position, double dt, std::ostream & error_output)
{
	if ( !enabled )
		return;

	struct input_event event;

        // Delete effect
	if (stop_and_play && effect.id!=-1)
	{
		if (ioctl(device_handle,EVIOCRMFF,effect.id)==-1) {
			error_output << "force feedback: removing effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
		effect.id=-1;
	}

        // Set force
	if (force>1.0) force=1.0;
	if (force<-1.0) force=-1.0;
	effect.direction=0xC000;
	effect.u.constant.level=(s

 /* only to be safe */
	effect.u.constant.envelope.attack_level=effect.u.constant.level;
	effect.u.constant.envelope.fade_level=effect.u.constant.level;
	/*effect.u.constant.envelope.attack_level = (short)(lastforce*32767.0);
	effect.u.constant.envelope.attack_length = (short)(dt*1000.0);
	effect.u.constant.envelope.fade_length = (short)(dt*1000.0);

	lastforce = force;

        // Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
              //error_output << "error uploading effect" << endl;
                /* We do not exit here. Indeed, too frequent updates may be
				* refused, but that is not a fatal error */
	}
	else if (effect.id!=-1)
        // Start effect
	{
		memset(&event,0,sizeof(event));
		event.type=EV_FF;
		event.code=effect.id;
		event.value=1;
		if (write(device_handle,&event,sizeof(event))!=sizeof(event))
		{
			error_output << "force feedback: re-starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
	}

        // Get events
	while (read(device_handle,&event,sizeof(event))==sizeof(event))
	{
		if (event.type==EV_ABS && event.code==axis_code)
		{
			*position=((double)(((short)event.value)-axis_min))*2.0/(axis_max-axis_min)-1.0;
			if (*position>1.0) *position=1.0;
			else if (*position<-1.0) *position=-1.0;
		}
	}
}

#endif // ENABLE_FORCE_FEEDBACK


while (read(device_handle,&event,sizeof(event))==sizeof(event))
	{
		if (event.type==EV_ABS && event.code==axis_code)
		{
			*position=((double)(((short)event.value)-axis_min))*2.0/(axis_max-axis_min)-1.0;
			if (*position>1.0) *position=1.0;
			else if (*position<-1.0) *position=-1.0;
		}
	}

#include "pch.h"

#include "forcefeedback.h"

#ifdef ENABLE_FORCE_FEEDBACK

#include <cstring>
#include <string>

using std::string;
using std::endl;

/* Number of bits for 1 unsigned char */
#define nBitsPerUchar          (sizeof(unsigned char) * 8)

/* Number of unsigned chars to contain a given number of bits */
#define nUcharsForNBits(nBits) ((((nBits)-1)/nBitsPerUchar)+1)

/* Index=Offset of given bit in 1 unsigned char */
#define bitOffsetInUchar(bit)  ((bit)%nBitsPerUchar)

/* Index=Offset of the unsigned char associated to the bit
   at the given index=offset */
#define ucharIndexForBit(bit)  ((bit)/nBitsPerUchar)

/* Value of an unsigned char with bit set at given index=offset */
#define ucharValueForBit(bit)  (((unsigned char)(1))<<bitOffsetInUchar(bit))

/* Test the bit with given index=offset in an unsigned char array */
#define testBit(bit, array)    ((array[ucharIndexForBit(bit)] >> bitOffsetInUchar(bit)) & 1)

FORCEFEEDBACK::FORCEFEEDBACK( string device, std::ostream & error_output, std::ostream & info_output )
	: device_name(device), enabled(true), stop_and_play(false), lastforce(0)
{
	unsigned char key_bits[1 + KEY_MAX/8/sizeof(unsigned char)];
	unsigned char abs_bits[1 + ABS_MAX/8/sizeof(unsigned char)];
	unsigned char ff_bits[1 + FF_MAX/8/sizeof(unsigned char)];

	struct input_event event;
	int valbuf[16];

	// Open event device with write permission
	device_handle = open(device_name.c_str(),O_RDWR|O_NONBLOCK);
	if (device_handle<0)
	{
		error_output << "force feedback: can not open " << device_name << " (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which buttons has the device?
	memset(key_bits,0,sizeof(key_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_KEY,sizeof(key_bits)),key_bits)<0)
	{
		error_output << "force feedback: can not get key bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axes has the device?
	memset(abs_bits,0,sizeof(abs_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_ABS,sizeof(abs_bits)),abs_bits)<0)
	{
		error_output << "force feedback: can not get abs bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Now get some information about force feedback
	memset(ff_bits,0,sizeof(ff_bits));
	if (ioctl(device_handle,EVIOCGBIT(EV_FF ,sizeof(ff_bits)),ff_bits)<0)
	{
		error_output << "force feedback: can not get ff bits (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Which axis is the x-axis?
	if      (testBit(ABS_X    ,abs_bits)) axis_code=ABS_X;
	else if (testBit(ABS_RX   ,abs_bits)) axis_code=ABS_RX;
	else if (testBit(ABS_WHEEL,abs_bits)) axis_code=ABS_WHEEL;
	else
	{
		error_output << "force feedback: no suitable x-axis found [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// get axis value range
	if (ioctl(device_handle,EVIOCGABS(axis_code),valbuf)<0)
	{
		error_output << "force feedback: can not get axis value range (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}
	axis_min=valbuf[1];
	axis_max=valbuf[2];
	if (axis_min>=axis_max)
	{
		error_output << "force feedback: bad axis value range (" << axis_min << "," << axis_max << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// force feedback supported?
	if (!testBit(FF_CONSTANT,ff_bits))
	{
		error_output << "force feedback: device (or driver) has no force feedback support [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Switch off auto centering
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=FF_AUTOCENTER;
	event.value=0;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: failed to disable auto centering (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	// Initialize constant force effect
	memset(&effect,0,sizeof(effect));
	effect.type=FF_CONSTANT;
	effect.id=-1;
	effect.trigger.button=0;
	effect.trigger.interval=0;
	effect.replay.length=0xffff;
	effect.replay.delay=0;
	effect.u.constant.level=0;
	effect.direction=0xC000;
	effect.u.constant.envelope.attack_length=0;
	effect.u.constant.envelope.attack_level=0;
	effect.u.constant.envelope.fade_length=0;
	effect.u.constant.envelope.fade_level=0;

	// Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
		error_output << "force feedback: uploading effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	/* Start effect */
	memset(&event,0,sizeof(event));
	event.type=EV_FF;
	event.code=effect.id;
	event.value=1;
	if (write(device_handle,&event,sizeof(event))!=sizeof(event))
	{
		error_output << "force feedback: starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
		disable();
		return;
	}

	info_output << "Force feedback initialized successfully" << std::endl;
}

void FORCEFEEDBACK::update(double force, double * position, double dt, std::ostream & error_output)
{
	if ( !enabled )
		return;

	struct input_event event;

        // Delete effect
	if (stop_and_play && effect.id!=-1)
	{
		if (ioctl(device_handle,EVIOCRMFF,effect.id)==-1) {
			error_output << "force feedback: removing effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
		effect.id=-1;
	}

        // Set force
	if (force>1.0) force=1.0;
	if (force<-1.0) force=-1.0;
	effect.direction=0xC000;
	effect.u.constant.level=(short)(force*32767.0); /* only to be safe */
	effect.u.constant.envelope.attack_level=effect.u.constant.level;
	effect.u.constant.envelope.fade_level=effect.u.constant.level;
	/*effect.u.constant.envelope.attack_level = (short)(lastforce*32767.0);
	effect.u.constant.envelope.attack_length = (short)(dt*1000.0);
	effect.u.constant.envelope.fade_length = (short)(dt*1000.0);

	lastforce = force;

        // Upload effect
	if (ioctl(device_handle,EVIOCSFF,&effect)==-1)
	{
              //error_output << "error uploading effect" << endl;
                /* We do not exit here. Indeed, too frequent updates may be
				* refused, but that is not a fatal error */
	}
	else if (effect.id!=-1)
        // Start effect
	{
		memset(&event,0,sizeof(event));
		event.type=EV_FF;
		event.code=effect.id;
		event.value=1;
		if (write(device_handle,&event,sizeof(event))!=sizeof(event))
		{
			error_output << "force feedback: re-starting effect failed (" << strerror(errno) << ") [" << __FILE__ << ":" << __LINE__ << "]" << endl;
			disable();
			return;
		}
	}

        // Get events