<commit_before>#include <pxp-agent/util/daemonize.hpp>
#include <pxp-agent/configuration.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.pxp_agent.util.daemonize"
#include <leatherman/logging/logging.hpp>

#include <sys/types.h>
#include <stdlib.h>         // exit()
#include <unistd.h>         // _exit(), getpid(), fork(), setsid(), chdir()
#include <sys/wait.h>       // waitpid()
#include <sys/stat.h>       // umask()
#include <signal.h>
#include <stdio.h>

#include <memory>
#include <vector>

namespace PXPAgent {
namespace Util {

const mode_t UMASK_FLAGS { 002 };
const std::string DEFAULT_DAEMON_WORKING_DIR = "/";

static void sigHandler(int sig) {
    // NOTE(ale): if issued by the init process, between SIGTERM and
    // the successive SIGKILL there are only 5 s - must be fast
    auto pidfile = Configuration::Instance().get<std::string>("pidfile");
    LOG_INFO("Caught signal %1% - removing PID file '%2%'",
             std::to_string(sig), pidfile);
    PIDFile pidf { pidfile };
    pidf.cleanup();
    exit(EXIT_SUCCESS);
}

static void dumbSigHandler(int sig) {}

std::unique_ptr<PIDFile> daemonize() {
    // Check if we're already a daemon
    if (getppid() == 1) {
        // Parent is init process (PID 1)
        LOG_INFO("Already a daemon with PID=%1%", std::to_string(getpid()));
        return nullptr;
    }

    // Set umask; child processes will inherit
    umask(UMASK_FLAGS);

    // Check PID file; get read lock; ensure we can obtain write lock

    auto pidfile = Configuration::Instance().get<std::string>("pidfile");
    std::unique_ptr<PIDFile> pidf_ptr { new PIDFile(pidfile) };

    auto removeLockAndExit = [&pidf_ptr] () {
                                 pidf_ptr->cleanupWhenDone();
                                 exit(EXIT_FAILURE);
                             };

    if (pidf_ptr->isExecuting()) {
        auto pid = pidf_ptr->read();
        LOG_ERROR("Already running with PID=%1%", pid);
        removeLockAndExit();
    }

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained a read lock for the PID file; no other pxp-agent "
                  "daemon should be executing");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock for the PID file: %1%", e.what());
        removeLockAndExit();
    }

    try {
        if (pidf_ptr->canLockWrite()) {
            LOG_DEBUG("It is possible to get a write lock for the PID file; no "
                      "other pxp-agent daemonization should be in progress");
        } else {
            LOG_ERROR("Cannot acquire the write lock for the PID file; please "
                      "ensure that there is no other pxp-agent instance executing");
            removeLockAndExit();
        }
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed to check if we can lock the PID file: %1%", e.what());
        removeLockAndExit();
    }

    // First fork - run in background

    auto first_child_pid = fork();

    switch (first_child_pid) {
        case -1:
            LOG_ERROR("Failed to perform the first fork; errno=%1%",
                      std::to_string(errno));
            removeLockAndExit();
        case 0:
            // CHILD - will fork and exit soon
            LOG_DEBUG("First child spawned, with PID=%1%",
                      std::to_string(getpid()));
            break;
        default:
            // PARENT - wait for the child, to avoid a zombie process
            // and don't unlock the PID file
            waitpid(first_child_pid, nullptr, 0);

            // Exit with _exit(); note that after a fork(), only one
            // of parent and child should terminate with exit()
            _exit(EXIT_SUCCESS);
    }

    // Get the read lock

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained the read lock after first fork");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock after first fork: %1%", e.what());
        removeLockAndExit();
    }

    // Create a new group session and become leader in order to
    // disassociate from a possible controlling terminal later

    if (setsid() == -1) {
        LOG_ERROR("Failed to create new session for first child process; "
                  "errno=%1%", std::to_string(errno));
        removeLockAndExit();
    }

    // Prepare signal mask for the second fork in order to catch the
    // child signal: block the child signal for now in order to avoid
    // races and change signal disposition for SIGUSR1

    sigset_t block_mask, orig_mask, empty_mask;
    struct sigaction sa;

    sigemptyset(&block_mask);
    sigaddset(&block_mask, SIGUSR1);

    if (sigprocmask(SIG_BLOCK, &block_mask, &orig_mask) == -1) {
        LOG_ERROR("Failed to set the signal mask after first fork");
        removeLockAndExit();
    }

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = dumbSigHandler;

    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        LOG_ERROR("Failed to set SIGUSR1 disposition after first fork");
        removeLockAndExit();
    }

    // Second fork - the child won't be a session leader and won't be
    // associated with any controlling terminal

    auto second_child_pid = fork();

    switch (second_child_pid) {
        case -1:
            LOG_ERROR("Failed to perform the second fork; errno=%1%",
                      std::to_string(errno));
            removeLockAndExit();
        case 0:
            // CHILD - will be the pxp-agent!
            break;
        default:
            // PARENT - wait for the child signal to avoid unlocking
            // the PID file
            sigemptyset(&empty_mask);

            if (sigsuspend(&empty_mask) == -1 && errno != EINTR) {
                LOG_ERROR("Unexpected error while waiting for pending signals "
                          "after second fork; errno=%1%", errno);
            }

            _exit(EXIT_SUCCESS);
    }

    // Get read lock, signal the parent, and restore signal mask

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained the read lock after second fork");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock after second fork: %1%", e.what());
        kill(getppid(), SIGUSR1);
        removeLockAndExit();
    }

    kill(getppid(), SIGUSR1);

    if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) == -1) {
        LOG_ERROR("Failed to reset signal mask after second fork; "
                  "errno=%1%", errno);
        removeLockAndExit();
    }

    auto agent_pid = getpid();
    LOG_DEBUG("Second child spawned, with PID=%1%", agent_pid);

    // Convert the read lock to a write lock and write PID to file

    LOG_DEBUG("About to convert the read lock to write lock after second fork");

    try {
        pidf_ptr->lockWrite(true);  // blocking call
        LOG_DEBUG("Successfully converted read lock to write lock");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed to convert to write lock after second fork: %1%",
                  e.what());
        removeLockAndExit();
    }

    pidf_ptr->write(agent_pid);

    // Change work directory

    if (chdir(DEFAULT_DAEMON_WORKING_DIR.data())) {
        LOG_ERROR("Failed to change work directory to '%1%'; errno=%2%",
                  DEFAULT_DAEMON_WORKING_DIR, std::to_string(errno));
        removeLockAndExit();
    } else {
        LOG_DEBUG("Changed working directory to '%1%'", DEFAULT_DAEMON_WORKING_DIR);
    }

    // Change signal dispositions
    // HERE(ale): don't touch SIGUSR2; it's used to reopen the logfile

    for (auto s : std::vector<int> { SIGINT, SIGTERM, SIGQUIT }) {
        if (signal(s, sigHandler) == SIG_ERR) {
            LOG_ERROR("Failed to set signal handler for sig %1%", s);
            removeLockAndExit();
        }
    }

    signal(SIGCHLD, SIG_DFL);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    // Redirect standard files; we always use boost::log anyway

    stdin = freopen("/dev/null", "r", stdin);
    stdout = freopen("/dev/null", "w", stdout);
    stderr = freopen("/dev/null", "w", stderr);

    LOG_INFO("Daemonization completed; pxp-agent PID=%1%, PID lock file in '%2%'",
             agent_pid, pidfile);

    // Set PIDFile dtor to clean itself up and return pointer for RAII
    pidf_ptr->cleanupWhenDone();

    return std::move(pidf_ptr);
}

}  // namespace Util
}  // namespace PXPAgent
<commit_msg>(maint) Fix compiling on Solaris<commit_after>#include <pxp-agent/util/daemonize.hpp>
#include <pxp-agent/configuration.hpp>

#define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.pxp_agent.util.daemonize"
#include <leatherman/logging/logging.hpp>

#include <sys/types.h>
#include <stdlib.h>         // exit()
#include <unistd.h>         // _exit(), getpid(), fork(), setsid(), chdir()
#include <sys/wait.h>       // waitpid()
#include <sys/stat.h>       // umask()
#include <signal.h>
#include <stdio.h>

#include <memory>
#include <vector>

namespace PXPAgent {
namespace Util {

const mode_t UMASK_FLAGS { 002 };
const std::string DEFAULT_DAEMON_WORKING_DIR = "/";

static void sigHandler(int sig) {
    // NOTE(ale): if issued by the init process, between SIGTERM and
    // the successive SIGKILL there are only 5 s - must be fast
    auto pidfile = Configuration::Instance().get<std::string>("pidfile");
    LOG_INFO("Caught signal %1% - removing PID file '%2%'",
             std::to_string(sig), pidfile);
    PIDFile pidf { pidfile };
    pidf.cleanup();
    exit(EXIT_SUCCESS);
}

static void dumbSigHandler(int sig) {}

std::unique_ptr<PIDFile> daemonize() {
    // Check if we're already a daemon
    if (getppid() == 1) {
        // Parent is init process (PID 1)
        LOG_INFO("Already a daemon with PID=%1%", std::to_string(getpid()));
        return nullptr;
    }

    // Set umask; child processes will inherit
    umask(UMASK_FLAGS);

    // Check PID file; get read lock; ensure we can obtain write lock

    auto pidfile = Configuration::Instance().get<std::string>("pidfile");
    std::unique_ptr<PIDFile> pidf_ptr { new PIDFile(pidfile) };

    auto removeLockAndExit = [&pidf_ptr] () {
                                 pidf_ptr->cleanupWhenDone();
                                 exit(EXIT_FAILURE);
                             };

    if (pidf_ptr->isExecuting()) {
        auto pid = pidf_ptr->read();
        LOG_ERROR("Already running with PID=%1%", pid);
        removeLockAndExit();
    }

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained a read lock for the PID file; no other pxp-agent "
                  "daemon should be executing");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock for the PID file: %1%", e.what());
        removeLockAndExit();
    }

    try {
        if (pidf_ptr->canLockWrite()) {
            LOG_DEBUG("It is possible to get a write lock for the PID file; no "
                      "other pxp-agent daemonization should be in progress");
        } else {
            LOG_ERROR("Cannot acquire the write lock for the PID file; please "
                      "ensure that there is no other pxp-agent instance executing");
            removeLockAndExit();
        }
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed to check if we can lock the PID file: %1%", e.what());
        removeLockAndExit();
    }

    // First fork - run in background

    auto first_child_pid = fork();

    switch (first_child_pid) {
        case -1:
            LOG_ERROR("Failed to perform the first fork; errno=%1%",
                      std::to_string(errno));
            removeLockAndExit();
        case 0:
            // CHILD - will fork and exit soon
            LOG_DEBUG("First child spawned, with PID=%1%",
                      std::to_string(getpid()));
            break;
        default:
            // PARENT - wait for the child, to avoid a zombie process
            // and don't unlock the PID file
            waitpid(first_child_pid, nullptr, 0);

            // Exit with _exit(); note that after a fork(), only one
            // of parent and child should terminate with exit()
            _exit(EXIT_SUCCESS);
    }

    // Get the read lock

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained the read lock after first fork");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock after first fork: %1%", e.what());
        removeLockAndExit();
    }

    // Create a new group session and become leader in order to
    // disassociate from a possible controlling terminal later

    if (setsid() == -1) {
        LOG_ERROR("Failed to create new session for first child process; "
                  "errno=%1%", std::to_string(errno));
        removeLockAndExit();
    }

    // Prepare signal mask for the second fork in order to catch the
    // child signal: block the child signal for now in order to avoid
    // races and change signal disposition for SIGUSR1

    sigset_t block_mask, orig_mask, empty_mask;
    struct sigaction sa;

    sigemptyset(&block_mask);
    sigaddset(&block_mask, SIGUSR1);

    if (sigprocmask(SIG_BLOCK, &block_mask, &orig_mask) == -1) {
        LOG_ERROR("Failed to set the signal mask after first fork");
        removeLockAndExit();
    }

    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_RESTART;
    sa.sa_handler = dumbSigHandler;

    if (sigaction(SIGUSR1, &sa, NULL) == -1) {
        LOG_ERROR("Failed to set SIGUSR1 disposition after first fork");
        removeLockAndExit();
    }

    // Second fork - the child won't be a session leader and won't be
    // associated with any controlling terminal

    auto second_child_pid = fork();

    switch (second_child_pid) {
        case -1:
            LOG_ERROR("Failed to perform the second fork; errno=%1%",
                      std::to_string(errno));
            removeLockAndExit();
        case 0:
            // CHILD - will be the pxp-agent!
            break;
        default:
            // PARENT - wait for the child signal to avoid unlocking
            // the PID file
            sigemptyset(&empty_mask);

            if (sigsuspend(&empty_mask) == -1 && errno != EINTR) {
                LOG_ERROR("Unexpected error while waiting for pending signals "
                          "after second fork; errno=%1%", errno);
            }

            _exit(EXIT_SUCCESS);
    }

    // Get read lock, signal the parent, and restore signal mask

    try {
        pidf_ptr->lockRead();
        LOG_DEBUG("Obtained the read lock after second fork");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed get a read lock after second fork: %1%", e.what());
        kill(getppid(), SIGUSR1);
        removeLockAndExit();
    }

    kill(getppid(), SIGUSR1);

    if (sigprocmask(SIG_SETMASK, &orig_mask, NULL) == -1) {
        LOG_ERROR("Failed to reset signal mask after second fork; "
                  "errno=%1%", errno);
        removeLockAndExit();
    }

    auto agent_pid = getpid();
    LOG_DEBUG("Second child spawned, with PID=%1%", agent_pid);

    // Convert the read lock to a write lock and write PID to file

    LOG_DEBUG("About to convert the read lock to write lock after second fork");

    try {
        pidf_ptr->lockWrite(true);  // blocking call
        LOG_DEBUG("Successfully converted read lock to write lock");
    } catch (const PIDFile::Error& e) {
        LOG_ERROR("Failed to convert to write lock after second fork: %1%",
                  e.what());
        removeLockAndExit();
    }

    pidf_ptr->write(agent_pid);

    // Change work directory

    if (chdir(DEFAULT_DAEMON_WORKING_DIR.data())) {
        LOG_ERROR("Failed to change work directory to '%1%'; errno=%2%",
                  DEFAULT_DAEMON_WORKING_DIR, std::to_string(errno));
        removeLockAndExit();
    } else {
        LOG_DEBUG("Changed working directory to '%1%'", DEFAULT_DAEMON_WORKING_DIR);
    }

    // Change signal dispositions
    // HERE(ale): don't touch SIGUSR2; it's used to reopen the logfile

    for (auto s : std::vector<int> { SIGINT, SIGTERM, SIGQUIT }) {
        if (signal(s, sigHandler) == SIG_ERR) {
            LOG_ERROR("Failed to set signal handler for sig %1%", s);
            removeLockAndExit();
        }
    }

    signal(SIGCHLD, SIG_DFL);
    signal(SIGTSTP, SIG_IGN);
    signal(SIGTTOU, SIG_IGN);
    signal(SIGTTIN, SIG_IGN);
    signal(SIGHUP, SIG_IGN);

    // Redirect standard files; we always use boost::log anyway

    if (freopen("/dev/null", "r", stdin) == nullptr) {
        LOG_WARNING("Failed to redirect stdin to /dev/null; errno=%1%", std::to_string(errno));
    }
    if (freopen("/dev/null", "w", stdout) == nullptr) {
        LOG_WARNING("Failed to redirect stdout to /dev/null; errno=%1%", std::to_string(errno));
    }
    if (freopen("/dev/null", "w", stderr) == nullptr) {
        LOG_WARNING("Failed to redirect stderr to /dev/null; errno=%1%", std::to_string(errno));
    }

    LOG_INFO("Daemonization completed; pxp-agent PID=%1%, PID lock file in '%2%'",
             agent_pid, pidfile);

    // Set PIDFile dtor to clean itself up and return pointer for RAII
    pidf_ptr->cleanupWhenDone();

    return std::move(pidf_ptr);
}

}  // namespace Util
}  // namespace PXPAgent
<|endoftext|>


<commit_before>// @(#)root/test:$Name$:$Id$
// Author: Fons Rademakers   19/08/96

#include <stdlib.h>
#include <iostream.h>

#include "TROOT.h"
#include "TString.h"
#include "TObjString.h"
#include "TSortedList.h"
#include "TObjArray.h"
#include "TOrdCollection.h"
#include "THashTable.h"
#include "TBtree.h"
#include "TStopwatch.h"

// To focus on basic collection protocol, this sample program uses
// simple classes inheriting from TObject. One class, TObjString, is a
// collectable string class (a TString wrapped in a TObject) provided
// by the ROOT system. The other class we define below, is an integer
// wrapped in a TObject, just like TObjString.

// TObjNum is a simple container for an integer.
class TObjNum : public TObject {
private:
   int  num;

public:
   TObjNum(int i = 0) : num(i) { }
   ~TObjNum() { Printf("~TObjNum = %d", num); }
   void    SetNum(int i) { num = i; }
   int     GetNum() { return num; }
   void    Print(Option_t *) { Printf("TObjNum = %d", num); }
   ULong_t  Hash() { return num; }
   Bool_t IsEqual(TObject *obj) { return num == ((TObjNum*)obj)->num; }
   Bool_t  IsSortable() const { return kTRUE; }
   Int_t   Compare(TObject *obj) { if (num > ((TObjNum*)obj)->num)
                                      return 1;
                                   else if (num < ((TObjNum*)obj)->num)
                                      return -1;
                                   else
                                      return 0; }
};

void Test_TObjArray()
{

   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TObjArray                                          //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Array of capacity 10, Add() will automatically expand the array if necessary.
   TObjArray  a(10);

   Printf("Filling TObjArray");
   a.Add(new TObjNum(1));            // add at next free slot, pos 0
   a[1] = new TObjNum(2);            // use operator[], put at pos 1
   TObjNum *n3 = new TObjNum(3);
   a.AddAt(n3,2);                    // add at position 2
   a.Add(new TObjNum(4));            // add at next free slot, pos 3
   a.AddLast(new TObjNum(10));       // add at pos 4
   TObjNum n6(6);                    // stack based TObjNum
   a.AddAt(&n6,5);                   // add at pos 5
   a[6] = new TObjNum(5);            // add at respective positions
   a[7] = new TObjNum(8);
   a[8] = new TObjNum(7);
//   a[10] = &n6;                    // gives out-of-bound error

   Printf("Print array");
   a.Print();                        // invoke Print() of all objects

   Printf("Sort array");
   a.Sort();
   for (int i = 0; i < a.Capacity(); i++)  // typical way of iterating over array
      if (a[i])
         a[i]->Print();      // can also use operator[] to access elements
      else
         Printf("%d empty slot", i);

   Printf("Use binary search to get position of number 6");
   Printf("6 is at position %d", a.BinarySearch(&n6));

   Printf("Find number before 6");
   a.Before(&n6)->Print();

   Printf("Find number after 3");
   a.After(n3)->Print();

   Printf("Remove 3 and print list again");
   a.Remove(n3);
   delete n3;
   a.Print();

   Printf("Iterate forward over list and remove 4 and 7");

   // TIter encapsulates the actual class iterator. The type of iterator
   // used depends on the type of the collection.
   TIter next(&a);

   TObjNum *obj;
   while ((obj = (TObjNum*)next()))     // iterator skips empty slots
      if (obj->GetNum() == 4) {
         a.Remove(obj);
         delete obj;
      }

   // Reset the iterator and loop again
   next.Reset();
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 7) {
         a.Remove(obj);
         delete obj;
      }

   Printf("Iterate backward over list and remove 2");
   TIter next1(&a, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) {
         a.Remove(obj);
         delete obj;
      }

   Printf("Delete remainder of list: 1,5,8,10 (6 not deleted since not on heap)");

   // Delete heap objects and clear list. Attention: do this only when you
   // own all objects stored in the collection. When you stored aliases to
   // the actual objects (i.e. you did not create the objects) use Clear()
   // instead.
   a.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_TOrdCollection()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TOrdCollection                                     //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create collection with default size, Add() will automatically expand
   // the collection if necessary.
   TOrdCollection  c;

   Printf("Filling TOrdCollection");
   c.Add(new TObjString("anton"));      // add at next free slot, pos 0
   c.AddFirst(new TObjString("bobo"));  // put at pos 0, bump anton to pos 1
   TObjString *s3 = new TObjString("damon");
   c.AddAt(s3,1);                       // add at position 1, bump anton to pos 2
   c.Add(new TObjString("cassius"));    // add at next free slot, pos 3
   c.AddLast(new TObjString("enigma")); // add at pos 4
   TObjString s6("fons");               // stack based TObjString
   c.AddBefore(s3,&s6);                 // add at pos 1
   c.AddAfter(s3, new TObjString("gaia"));

   Printf("Print collection");
   c.Print();                           // invoke Print() of all objects

   Printf("Sort collection");
   c.Sort();
   c.Print();

   Printf("Use binary search to get position of string damon");
   Printf("damon is at position %d", c.BinarySearch(s3));

   Printf("Find str before fons");
   c.Before(&s6)->Print();

   Printf("Find string after damon");
   c.After(s3)->Print();

   Printf("Remove damon and print list again");
   c.Remove(s3);
   delete s3;
   c.Print();

   Printf("Iterate forward over list and remove cassius");
   TObjString *objs;
   TIter next(&c);
   while ((objs = (TObjString*)next()))     // iterator skips empty slots
      if (objs->String() == "cassius") {
         c.Remove(objs);
         delete objs;
      }

   Printf("Iterate backward over list and remove gaia");
   TIter next1(&c, kIterBackward);
   while ((objs = (TObjString*)next1()))
      if (objs->String() == "gaia") {
         c.Remove(objs);
         delete objs;
      }

   Printf("Delete remainder of list: anton,bobo,enigma (fons not deleted since not on heap)");
   c.Delete();                        // delete heap objects and clear list

   Printf("Delete stack based objects (fons)");
}

void Test_TList()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TList                                              //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create a doubly linked list.
   TList l;

   Printf("Filling TList");
   TObjNum *n3 = new TObjNum(3);
   l.Add(n3);
   l.AddBefore(n3, new TObjNum(5));
   l.AddAfter(n3, new TObjNum(2));
   l.Add(new TObjNum(1));
   l.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   l.AddFirst(&n6);

   Printf("Print list");
   l.Print();

   Printf("Remove 3 and print list again");
   l.Remove(n3);
   delete n3;
   l.Print();

   Printf("Iterate forward over list and remove 4");
   TObjNum *obj;
   TIter next(&l);
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 4) l.Remove(obj);

   Printf("Iterate backward over list and remove 2");
   TIter next1(&l, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) {
         l.Remove(obj);
         delete obj;
      }

   Printf("Delete remainder of list: 1, 5 (6 not deleted since not on heap)");
   l.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_TSortedList()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TSortedList                                        //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create a sorted doubly linked list.
   TSortedList sl;

   Printf("Filling TSortedList");
   TObjNum *n3 = new TObjNum(3);
   sl.Add(n3);
   sl.AddBefore(n3,new TObjNum(5));
   sl.AddAfter(n3, new TObjNum(2));
   sl.Add(new TObjNum(1));
   sl.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   sl.AddFirst(&n6);

   Printf("Print list");
   sl.Print();

   Printf("Delete all heap based objects (6 not deleted since not on heap)");
   sl.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_THashTable()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of THashTable                                         //\n"
   "////////////////////////////////////////////////////////////////"
   );

   int i;

   // Create a hash table with an initial size of 20 (actually the next prime
   // above 20). No automatic rehashing.
   THashTable ht(20);

   Printf("Filling THashTable");
   Printf("Number of slots before filling: %d", ht.Capacity());
   for (i = 0; i < 1000; i++)
      ht.Add(new TObject);

   Printf("Average collisions: %f", ht.AverageCollisions());

   // rehash the hash table to reduce the collission rate
   ht.Rehash(ht.GetSize());

   Printf("Number of slots after rehash: %d", ht.Capacity());
   Printf("Average collisions after rehash: %f", ht.AverageCollisions());

   ht.Delete();

   // Create a hash table and trigger automatic rehashing when average
   // collision rate becomes larger than 5.
   THashTable ht2(20,5);

   Printf("Filling THashTable with automatic rehash when AverageCollisions>5");
   Printf("Number of slots before filling: %d", ht2.Capacity());
   for (i = 0; i < 1000; i++)
      ht2.Add(new TObject);

   Printf("Number of slots after filling: %d", ht2.Capacity());
   Printf("Average collisions: %f", ht2.AverageCollisions());

   Printf("\nDelete all heap based objects");
   ht2.Delete();
}

void Test_TBtree()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TBtree                                             //\n"
   "////////////////////////////////////////////////////////////////"
   );
   TStopwatch timer;      // create a timer
   TBtree     l;          // btree of order 3

   Printf("Filling TBtree");

   TObjNum *n3 = new TObjNum(3);
   l.Add(n3);
   l.AddBefore(n3,new TObjNum(5));
   l.AddAfter(n3, new TObjNum(2));
   l.Add(new TObjNum(1));
   l.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   l.AddFirst(&n6);

   timer.Start();
   for (int i = 0; i < 50; i++)
      l.Add(new TObjNum(i));
   timer.Print();

   Printf("Print TBtree");
   l.Print();

   Printf("Remove 3 and print TBtree again");
   l.Remove(n3);
   l.Print();

   Printf("Iterate forward over TBtree and remove 4 from tree");
   TIter next(&l);
   TObjNum *obj;
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 4) l.Remove(obj);

   Printf("Iterate backward over TBtree and remove 2 from tree");
   TIter next1(&l, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) l.Remove(obj);

   Printf("\nDelete all heap based objects");
   l.Delete();

   Printf("Delete stack based objects (6)");
}

int main()
{
   // Initialize the ROOT framework
   TROOT tcollex("Collection", "Test collection classes");

   Test_TObjArray();
   Test_TOrdCollection();
   Test_TList();
   Test_TSortedList();
   Test_THashTable();
   Test_TBtree();

   return 0;
}
<commit_msg>Fix const in Hash, Print and Compare functions<commit_after>// @(#)root/test:$Name:  $:$Id: tcollex.cxx,v 1.2 2000/07/11 18:05:26 rdm Exp $
// Author: Fons Rademakers   19/08/96

#include <stdlib.h>
#include <iostream.h>

#include "TROOT.h"
#include "TString.h"
#include "TObjString.h"
#include "TSortedList.h"
#include "TObjArray.h"
#include "TOrdCollection.h"
#include "THashTable.h"
#include "TBtree.h"
#include "TStopwatch.h"

// To focus on basic collection protocol, this sample program uses
// simple classes inheriting from TObject. One class, TObjString, is a
// collectable string class (a TString wrapped in a TObject) provided
// by the ROOT system. The other class we define below, is an integer
// wrapped in a TObject, just like TObjString.

// TObjNum is a simple container for an integer.
class TObjNum : public TObject {
private:
   int  num;

public:
   TObjNum(int i = 0) : num(i) { }
   ~TObjNum() { Printf("~TObjNum = %d", num); }
   void    SetNum(int i) { num = i; }
   int     GetNum() { return num; }
   void    Print(Option_t *) const { Printf("TObjNum = %d", num); }
   ULong_t  Hash() const { return num; }
   Bool_t IsEqual(const TObject *obj) const { return num == ((TObjNum*)obj)->num; }
   Bool_t  IsSortable() const { return kTRUE; }
   Int_t   Compare(const TObject *obj) const { if (num > ((TObjNum*)obj)->num)
                                      return 1;
                                   else if (num < ((TObjNum*)obj)->num)
                                      return -1;
                                   else
                                      return 0; }
};

void Test_TObjArray()
{

   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TObjArray                                          //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Array of capacity 10, Add() will automatically expand the array if necessary.
   TObjArray  a(10);

   Printf("Filling TObjArray");
   a.Add(new TObjNum(1));            // add at next free slot, pos 0
   a[1] = new TObjNum(2);            // use operator[], put at pos 1
   TObjNum *n3 = new TObjNum(3);
   a.AddAt(n3,2);                    // add at position 2
   a.Add(new TObjNum(4));            // add at next free slot, pos 3
   a.AddLast(new TObjNum(10));       // add at pos 4
   TObjNum n6(6);                    // stack based TObjNum
   a.AddAt(&n6,5);                   // add at pos 5
   a[6] = new TObjNum(5);            // add at respective positions
   a[7] = new TObjNum(8);
   a[8] = new TObjNum(7);
//   a[10] = &n6;                    // gives out-of-bound error

   Printf("Print array");
   a.Print();                        // invoke Print() of all objects

   Printf("Sort array");
   a.Sort();
   for (int i = 0; i < a.Capacity(); i++)  // typical way of iterating over array
      if (a[i])
         a[i]->Print();      // can also use operator[] to access elements
      else
         Printf("%d empty slot", i);

   Printf("Use binary search to get position of number 6");
   Printf("6 is at position %d", a.BinarySearch(&n6));

   Printf("Find number before 6");
   a.Before(&n6)->Print();

   Printf("Find number after 3");
   a.After(n3)->Print();

   Printf("Remove 3 and print list again");
   a.Remove(n3);
   delete n3;
   a.Print();

   Printf("Iterate forward over list and remove 4 and 7");

   // TIter encapsulates the actual class iterator. The type of iterator
   // used depends on the type of the collection.
   TIter next(&a);

   TObjNum *obj;
   while ((obj = (TObjNum*)next()))     // iterator skips empty slots
      if (obj->GetNum() == 4) {
         a.Remove(obj);
         delete obj;
      }

   // Reset the iterator and loop again
   next.Reset();
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 7) {
         a.Remove(obj);
         delete obj;
      }

   Printf("Iterate backward over list and remove 2");
   TIter next1(&a, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) {
         a.Remove(obj);
         delete obj;
      }

   Printf("Delete remainder of list: 1,5,8,10 (6 not deleted since not on heap)");

   // Delete heap objects and clear list. Attention: do this only when you
   // own all objects stored in the collection. When you stored aliases to
   // the actual objects (i.e. you did not create the objects) use Clear()
   // instead.
   a.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_TOrdCollection()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TOrdCollection                                     //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create collection with default size, Add() will automatically expand
   // the collection if necessary.
   TOrdCollection  c;

   Printf("Filling TOrdCollection");
   c.Add(new TObjString("anton"));      // add at next free slot, pos 0
   c.AddFirst(new TObjString("bobo"));  // put at pos 0, bump anton to pos 1
   TObjString *s3 = new TObjString("damon");
   c.AddAt(s3,1);                       // add at position 1, bump anton to pos 2
   c.Add(new TObjString("cassius"));    // add at next free slot, pos 3
   c.AddLast(new TObjString("enigma")); // add at pos 4
   TObjString s6("fons");               // stack based TObjString
   c.AddBefore(s3,&s6);                 // add at pos 1
   c.AddAfter(s3, new TObjString("gaia"));

   Printf("Print collection");
   c.Print();                           // invoke Print() of all objects

   Printf("Sort collection");
   c.Sort();
   c.Print();

   Printf("Use binary search to get position of string damon");
   Printf("damon is at position %d", c.BinarySearch(s3));

   Printf("Find str before fons");
   c.Before(&s6)->Print();

   Printf("Find string after damon");
   c.After(s3)->Print();

   Printf("Remove damon and print list again");
   c.Remove(s3);
   delete s3;
   c.Print();

   Printf("Iterate forward over list and remove cassius");
   TObjString *objs;
   TIter next(&c);
   while ((objs = (TObjString*)next()))     // iterator skips empty slots
      if (objs->String() == "cassius") {
         c.Remove(objs);
         delete objs;
      }

   Printf("Iterate backward over list and remove gaia");
   TIter next1(&c, kIterBackward);
   while ((objs = (TObjString*)next1()))
      if (objs->String() == "gaia") {
         c.Remove(objs);
         delete objs;
      }

   Printf("Delete remainder of list: anton,bobo,enigma (fons not deleted since not on heap)");
   c.Delete();                        // delete heap objects and clear list

   Printf("Delete stack based objects (fons)");
}

void Test_TList()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TList                                              //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create a doubly linked list.
   TList l;

   Printf("Filling TList");
   TObjNum *n3 = new TObjNum(3);
   l.Add(n3);
   l.AddBefore(n3, new TObjNum(5));
   l.AddAfter(n3, new TObjNum(2));
   l.Add(new TObjNum(1));
   l.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   l.AddFirst(&n6);

   Printf("Print list");
   l.Print();

   Printf("Remove 3 and print list again");
   l.Remove(n3);
   delete n3;
   l.Print();

   Printf("Iterate forward over list and remove 4");
   TObjNum *obj;
   TIter next(&l);
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 4) l.Remove(obj);

   Printf("Iterate backward over list and remove 2");
   TIter next1(&l, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) {
         l.Remove(obj);
         delete obj;
      }

   Printf("Delete remainder of list: 1, 5 (6 not deleted since not on heap)");
   l.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_TSortedList()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TSortedList                                        //\n"
   "////////////////////////////////////////////////////////////////"
   );

   // Create a sorted doubly linked list.
   TSortedList sl;

   Printf("Filling TSortedList");
   TObjNum *n3 = new TObjNum(3);
   sl.Add(n3);
   sl.AddBefore(n3,new TObjNum(5));
   sl.AddAfter(n3, new TObjNum(2));
   sl.Add(new TObjNum(1));
   sl.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   sl.AddFirst(&n6);

   Printf("Print list");
   sl.Print();

   Printf("Delete all heap based objects (6 not deleted since not on heap)");
   sl.Delete();

   Printf("Delete stack based objects (6)");
}

void Test_THashTable()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of THashTable                                         //\n"
   "////////////////////////////////////////////////////////////////"
   );

   int i;

   // Create a hash table with an initial size of 20 (actually the next prime
   // above 20). No automatic rehashing.
   THashTable ht(20);

   Printf("Filling THashTable");
   Printf("Number of slots before filling: %d", ht.Capacity());
   for (i = 0; i < 1000; i++)
      ht.Add(new TObject);

   Printf("Average collisions: %f", ht.AverageCollisions());

   // rehash the hash table to reduce the collission rate
   ht.Rehash(ht.GetSize());

   Printf("Number of slots after rehash: %d", ht.Capacity());
   Printf("Average collisions after rehash: %f", ht.AverageCollisions());

   ht.Delete();

   // Create a hash table and trigger automatic rehashing when average
   // collision rate becomes larger than 5.
   THashTable ht2(20,5);

   Printf("Filling THashTable with automatic rehash when AverageCollisions>5");
   Printf("Number of slots before filling: %d", ht2.Capacity());
   for (i = 0; i < 1000; i++)
      ht2.Add(new TObject);

   Printf("Number of slots after filling: %d", ht2.Capacity());
   Printf("Average collisions: %f", ht2.AverageCollisions());

   Printf("\nDelete all heap based objects");
   ht2.Delete();
}

void Test_TBtree()
{
   Printf(
   "////////////////////////////////////////////////////////////////\n"
   "// Test of TBtree                                             //\n"
   "////////////////////////////////////////////////////////////////"
   );
   TStopwatch timer;      // create a timer
   TBtree     l;          // btree of order 3

   Printf("Filling TBtree");

   TObjNum *n3 = new TObjNum(3);
   l.Add(n3);
   l.AddBefore(n3,new TObjNum(5));
   l.AddAfter(n3, new TObjNum(2));
   l.Add(new TObjNum(1));
   l.AddBefore(n3, new TObjNum(4));
   TObjNum n6(6);                     // stack based TObjNum
   l.AddFirst(&n6);

   timer.Start();
   for (int i = 0; i < 50; i++)
      l.Add(new TObjNum(i));
   timer.Print();

   Printf("Print TBtree");
   l.Print();

   Printf("Remove 3 and print TBtree again");
   l.Remove(n3);
   l.Print();

   Printf("Iterate forward over TBtree and remove 4 from tree");
   TIter next(&l);
   TObjNum *obj;
   while ((obj = (TObjNum*)next()))
      if (obj->GetNum() == 4) l.Remove(obj);

   Printf("Iterate backward over TBtree and remove 2 from tree");
   TIter next1(&l, kIterBackward);
   while ((obj = (TObjNum*)next1()))
      if (obj->GetNum() == 2) l.Remove(obj);

   Printf("\nDelete all heap based objects");
   l.Delete();

   Printf("Delete stack based objects (6)");
}

int main()
{
   // Initialize the ROOT framework
   TROOT tcollex("Collection", "Test collection classes");

   Test_TObjArray();
   Test_TOrdCollection();
   Test_TList();
   Test_TSortedList();
   Test_THashTable();
   Test_TBtree();

   return 0;
}
<|endoftext|>


<commit_before>#include "stdafx.h"
#include "bindings.h"
#include "helper.h"
#include <game_sa\CPed.h>
#include <injector\calling.hpp>

using namespace Bindings;

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

void Bindings::Ped(JsValueRef ped, Container& container) {
	void* data;
	JsGetExternalData(ped, &data);
	CPed* pedNative = (CPed*)data;

	Js::DefineProperty(ped, L"health", getFloat, setFloat, &pedNative->m_fHealth);
	Js::DefineProperty(ped, L"armor", getFloat, setFloat, &pedNative->m_fArmour);
	Js::DefineProperty(ped, L"isDriving", getBool, nullptr, container.create_pointer(pedNative, 0x46C, 8));
	Js::DefineProperty(ped, L"isArrested", getBool, nullptr, container.create_pointer(pedNative, 0x474, 6));
	Js::DefineProperty(ped, L"isInvisible", getBool, setBool, container.create_pointer(pedNative, 0x474, 1));
	Js::DefineProperty(ped, L"model", getInt, nullptr, &pedNative->m_wModelIndex);
}

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	JsValueRef numberValue;
	JsDoubleToNumber(*floatValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	double doubleValue;
	JsNumberToDouble(arguments[1], &doubleValue);

	*floatValue = doubleValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	JsValueRef numberValue;
	JsIntToNumber(*intValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	int tempIntValue;
	JsNumberToInt(arguments[1], &tempIntValue);

	*intValue = tempIntValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;
	bool boolValue = get(ptr->pointer, ptr->offset, ptr->shift);

	JsValueRef booleanValue;
	JsBoolToBoolean(boolValue, &booleanValue);

	return booleanValue;
}

JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;

	bool tempBoolValue;
	JsBooleanToBool(arguments[1], &tempBoolValue);

	set(ptr->pointer, ptr->offset, ptr->shift, tempBoolValue);

	return JS_INVALID_REFERENCE;
}

Bindings::fake_ptr::fake_ptr(void* pointer, int offset, int shift)
{
	this->pointer = pointer;
	this->offset = offset;
	this->shift = shift;
}

Bindings::Container::Container()
{
}

Bindings::Container::~Container()
{
	while (!pointers.empty())
	{
		delete pointers.front();
		pointers.pop();
	}
}

fake_ptr* Bindings::Container::create_pointer(void* base, int offset, int shift)
{
	auto pointer = new fake_ptr(base, offset, shift);
	pointers.push(pointer);
	return pointer;
}
<commit_msg>Added position<commit_after>#include "stdafx.h"
#include "bindings.h"
#include "helper.h"
#include <game_sa\CPed.h>
#include <injector\calling.hpp>

using namespace Bindings;

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

JsValueRef CALLBACK getVector(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);
JsValueRef CALLBACK setVector(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState);

void Bindings::Ped(JsValueRef ped, Container& container) {
	void* data;
	JsGetExternalData(ped, &data);
	CPed* pedNative = (CPed*)data;

	Js::DefineProperty(ped, L"position", getVector, setVector, &pedNative->m_pCoords->pos);
	Js::DefineProperty(ped, L"health", getFloat, setFloat, &pedNative->m_fHealth);
	Js::DefineProperty(ped, L"armor", getFloat, setFloat, &pedNative->m_fArmour);
	Js::DefineProperty(ped, L"isDriving", getBool, nullptr, container.create_pointer(pedNative, 0x46C, 8));
	Js::DefineProperty(ped, L"isArrested", getBool, nullptr, container.create_pointer(pedNative, 0x474, 6));
	Js::DefineProperty(ped, L"isInvisible", getBool, setBool, container.create_pointer(pedNative, 0x474, 1));
	Js::DefineProperty(ped, L"model", getInt, nullptr, &pedNative->m_wModelIndex);
}

JsValueRef CALLBACK getFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	JsValueRef numberValue;
	JsDoubleToNumber(*floatValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setFloat(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	float* floatValue = (float*)callbackState;

	double doubleValue;
	JsNumberToDouble(arguments[1], &doubleValue);

	*floatValue = doubleValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	JsValueRef numberValue;
	JsIntToNumber(*intValue, &numberValue);

	return numberValue;
}

JsValueRef CALLBACK setInt(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	int* intValue = (int*)callbackState;

	int tempIntValue;
	JsNumberToInt(arguments[1], &tempIntValue);

	*intValue = tempIntValue;

	return JS_INVALID_REFERENCE;
}

JsValueRef CALLBACK getBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;
	bool boolValue = get(ptr->pointer, ptr->offset, ptr->shift);

	JsValueRef booleanValue;
	JsBoolToBoolean(boolValue, &booleanValue);

	return booleanValue;
}

JsValueRef CALLBACK setBool(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	fake_ptr* ptr = (fake_ptr*)callbackState;

	bool tempBoolValue;
	JsBooleanToBool(arguments[1], &tempBoolValue);

	set(ptr->pointer, ptr->offset, ptr->shift, tempBoolValue);

	return JS_INVALID_REFERENCE;
}

Bindings::fake_ptr::fake_ptr(void* pointer, int offset, int shift)
{
	this->pointer = pointer;
	this->offset = offset;
	this->shift = shift;
}

Bindings::Container::Container()
{
}

Bindings::Container::~Container()
{
	while (!pointers.empty())
	{
		delete pointers.front();
		pointers.pop();
	}
}

fake_ptr* Bindings::Container::create_pointer(void* base, int offset, int shift)
{
	auto pointer = new fake_ptr(base, offset, shift);
	pointers.push(pointer);
	return pointer;
}

JsValueRef CALLBACK getVector(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	CVector* vectorValue = (CVector*)callbackState;

	JsValueRef vectorObject;
	JsCreateExternalObject(vectorValue, nullptr, &vectorObject);
	Js::DefineProperty(vectorObject, L"x", getFloat, setFloat, &vectorValue->x);
	Js::DefineProperty(vectorObject, L"y", getFloat, setFloat, &vectorValue->y);
	Js::DefineProperty(vectorObject, L"z", getFloat, setFloat, &vectorValue->z);

	return vectorObject;
}

JsValueRef CALLBACK setVector(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
{
	CVector* vectorValue = (CVector*)callbackState;

	JsValueRef x, y, z;
	Js::GetPropertyFromObject(arguments[1], L"x", &x);
	Js::GetPropertyFromObject(arguments[1], L"y", &y);
	Js::GetPropertyFromObject(arguments[1], L"z", &z);

	double tempX, tempY, tempZ;
	JsNumberToDouble(x, &tempX);
	vectorValue->x = tempX;
	JsNumberToDouble(y, &tempY);
	vectorValue->y = tempY;
	JsNumberToDouble(z, &tempZ);
	vectorValue->z = tempZ;

	return JS_INVALID_REFERENCE;
}
<|endoftext|>


<commit_before>// Copyright (c) 2012-2013 Plenluno All rights reserved.

#include <gtest/gtest.h>
#include <float.h>
#include <libj/array_list.h>
#include <libj/error.h>
#include <libj/json.h>
#include <libj/js_array.h>
#include <libj/js_object.h>
#include <libj/map.h>
#include <libj/string_builder.h>

namespace libj {

TEST(GTestJson, TestStringify) {
    ASSERT_FALSE(json::stringify(UNDEFINED));

    ASSERT_TRUE(json::stringify(static_cast<Byte>(3))
        ->equals(String::create("3")));

    ASSERT_TRUE(json::stringify(static_cast<Short>(4))
        ->equals(String::create("4")));

    ASSERT_TRUE(json::stringify(static_cast<Int>(5))
        ->equals(String::create("5")));

    ASSERT_TRUE(json::stringify(static_cast<Long>(6))
        ->equals(String::create("6")));

    ASSERT_TRUE(json::stringify(static_cast<Float>(2.5))
        ->equals(String::create("2.5")));

    ASSERT_TRUE(json::stringify(static_cast<Double>(3.5))
        ->equals(String::create("3.5")));

    ASSERT_TRUE(json::stringify(DBL_MAX)
        ->equals(String::create("1.7976931348623157e+308")));

    ASSERT_TRUE(json::stringify(-DBL_MIN)
        ->equals(String::create("-2.2250738585072014e-308")));

    ASSERT_TRUE(json::stringify(true)
        ->equals(String::create("true")));

    ASSERT_TRUE(json::stringify(false)
        ->equals(String::create("false")));

    ASSERT_TRUE(json::stringify(Object::null())
        ->equals(String::create("null")));

    ASSERT_TRUE(json::stringify(String::create("456"))
        ->equals(String::create("\"456\"")));

    ASSERT_TRUE(json::stringify(Map::create())
        ->equals(String::create("{}")));

    ASSERT_TRUE(json::stringify(ArrayList::create())
        ->equals(String::create("[]")));

    Map::Ptr m = Map::create();
    m->put(3, false);
    m->put(String::create("w"), UNDEFINED);
    m->put(String::create("x"), 123);
    m->put(String::create("y"), String::create("456"));
    m->put(String::create("z"), String::null());
    ASSERT_TRUE(json::stringify(m)
        ->equals(String::create("{\"x\":123,\"y\":\"456\",\"z\":null}")));

    ArrayList::Ptr a = ArrayList::create();
    a->add(3);
    a->add(UNDEFINED);
    a->add(false);
    ASSERT_TRUE(json::stringify(a)
        ->equals(String::create("[3,null,false]")));
}

TEST(GTestJson, TestRecursiveStringify) {
    Map::Ptr m = Map::create();
    m->put(String::create("x"), 123);
    m->put(String::create("y"), ArrayList::create());

    JsObject::Ptr jo = JsObject::create();
    jo->put(3, false);
    jo->put(Object::null(), m);
    ASSERT_TRUE(json::stringify(jo)->equals(
        String::create("{\"3\":false,\"null\":{\"x\":123,\"y\":[]}}")));

    ArrayList::Ptr a = ArrayList::create();
    a->add(3);
    a->add(m);

    JsArray::Ptr ja = JsArray::create();
    ja->add(UNDEFINED);
    ja->add(Object::null());
    ja->add(a);
    ASSERT_TRUE(json::stringify(ja)->equals(
        String::create("[null,null,[3,{\"x\":123,\"y\":[]}]]")));
}

TEST(GTestJson, TestParse) {
    String::CPtr json =
        String::create("{\"x\":123,\"y\":[3.5,false],\"z\":null}");
    Value v = json::parse(json);
    ASSERT_TRUE(v.instanceof(Type<Map>::id()));
    ASSERT_TRUE(v.instanceof(Type<JsObject>::id()));

    JsObject::CPtr m = toCPtr<JsObject>(v);
    ASSERT_EQ(3, m->size());

    Value xv = m->get(String::create("x"));
    ASSERT_EQ(Type<Long>::id(), xv.type());

    Long l;
    to<Long>(xv, &l);
    ASSERT_EQ(123, l);

    Value yv = m->get(String::create("y"));
    ASSERT_TRUE(yv.instanceof(Type<ArrayList>::id()));
    ASSERT_TRUE(yv.instanceof(Type<JsArray>::id()));

    JsArray::CPtr a = toCPtr<JsArray>(yv);
    Value a0 = a->get(0);
    ASSERT_EQ(Type<Double>::id(), a0.type());

    Double d;
    to<Double>(a0, &d);
    ASSERT_EQ(3.5, d);

    Value a1 = a->get(1);
    ASSERT_EQ(Type<Boolean>::id(), a1.type());

    Boolean b;
    to<Boolean>(a1, &b);
    ASSERT_FALSE(b);

    Value zv = m->get(String::create("z"));
    ASSERT_TRUE(zv.equals(Object::null()));
}

TEST(GTestJson, TestParseString) {
    String::CPtr s = String::create("\"\\u0026\"");
    ASSERT_TRUE(json::parse(s).equals(String::create("&")));

    String::CPtr s1 = String::create("\"\\ud84c\\udfd0\"");
#ifdef LIBJ_USE_UTF32
    String::CPtr s2 = String::create(0x233d0);
#else
    std::u16string s16;
    s16 += static_cast<char16_t>(0xd84c);
    s16 += static_cast<char16_t>(0xdfd0);
    String::CPtr s2 = String::create(s16);
#endif
    ASSERT_TRUE(json::parse(s1).equals(s2));

    std::u32string s32;
    s32 += 0x3042;
    s32 += 0x2000b;
    s = String::create(s32);
    StringBuilder::Ptr sb = StringBuilder::create();
    sb->appendCStr("\"");
    sb->append(s);
    sb->appendCStr("\"");
    ASSERT_TRUE(json::parse(sb->toString()).equals(s));
}

TEST(GTestJson, TestParseNumber) {
    String::CPtr s1 = String::create("1.0");
    Value v1 = json::parse(s1);
    ASSERT_EQ(Type<Long>::id(), v1.type());
    ASSERT_TRUE(v1.equals(static_cast<Long>(1)));

    String::CPtr s2 = String::create("9007199254740990");
    Value v2 = json::parse(s2);
    ASSERT_TRUE(v2.equals(static_cast<Long>(9007199254740990)));

    String::CPtr s3 = String::create("9007199254740991");
    Value v3 = json::parse(s3);
    ASSERT_TRUE(v3.equals(static_cast<Long>(9007199254740991)));

    String::CPtr s4 = String::create("9007199254740992");
    Value v4 = json::parse(s4);
    ASSERT_TRUE(v4.equals(9007199254740992.0));

    String::CPtr s5 = String::create("9007199254740993");
    Value v5 = json::parse(s5);
    ASSERT_TRUE(v5.equals(9007199254740992.0));

    String::CPtr s6 = String::create("-9007199254740990");
    Value v6 = json::parse(s6);
    ASSERT_TRUE(v6.equals(-9007199254740990LL));

    String::CPtr s7 = String::create("-9007199254740991");
    Value v7 = json::parse(s7);
    ASSERT_TRUE(v7.equals(-9007199254740991LL));

    String::CPtr s8 = String::create("-9007199254740992");
    Value v8 = json::parse(s8);
    ASSERT_TRUE(v8.equals(-9007199254740992.0));

    String::CPtr s9 = String::create("-9007199254740993");
    Value v9 = json::parse(s9);
    ASSERT_TRUE(v9.equals(-9007199254740992.0));

    String::CPtr s10 = String::create("1.7976931348623157e+308");
    Value v10 = json::parse(s10);
    ASSERT_TRUE(v10.equals(1.7976931348623157e+308));
}

TEST(GTestJson, TestParseError) {
    Value v1 = json::parse(String::null());
    ASSERT_TRUE(v1.instanceof(Type<Error>::id()));

    Value v2 = json::parse(String::create("\""));
    ASSERT_TRUE(v2.instanceof(Type<Error>::id()));
}

TEST(GTestJson, TestEscape) {
    String::CPtr str = String::create("\b\f\n\r\t'\"\\");
    String::CPtr json = String::create("\"\\b\\f\\n\\r\\t'\\\"\\\\\"");
    String::CPtr s = toCPtr<String>(json::parse(json));
    ASSERT_TRUE(s->equals(str));
    s = json::stringify(str);
    ASSERT_TRUE(s->equals(json));
}

}  // namespace libj
<commit_msg>fix travis errors<commit_after>// Copyright (c) 2012-2013 Plenluno All rights reserved.

#include <gtest/gtest.h>
#include <float.h>
#include <libj/array_list.h>
#include <libj/error.h>
#include <libj/json.h>
#include <libj/js_array.h>
#include <libj/js_object.h>
#include <libj/map.h>
#include <libj/string_builder.h>

namespace libj {

TEST(GTestJson, TestStringify) {
    ASSERT_FALSE(json::stringify(UNDEFINED));

    ASSERT_TRUE(json::stringify(static_cast<Byte>(3))
        ->equals(String::create("3")));

    ASSERT_TRUE(json::stringify(static_cast<Short>(4))
        ->equals(String::create("4")));

    ASSERT_TRUE(json::stringify(static_cast<Int>(5))
        ->equals(String::create("5")));

    ASSERT_TRUE(json::stringify(static_cast<Long>(6))
        ->equals(String::create("6")));

    ASSERT_TRUE(json::stringify(static_cast<Float>(2.5))
        ->equals(String::create("2.5")));

    ASSERT_TRUE(json::stringify(static_cast<Double>(3.5))
        ->equals(String::create("3.5")));

    ASSERT_TRUE(json::stringify(DBL_MAX)
        ->equals(String::create("1.7976931348623157e+308")));

    ASSERT_TRUE(json::stringify(-DBL_MIN)
        ->equals(String::create("-2.2250738585072014e-308")));

    ASSERT_TRUE(json::stringify(true)
        ->equals(String::create("true")));

    ASSERT_TRUE(json::stringify(false)
        ->equals(String::create("false")));

    ASSERT_TRUE(json::stringify(Object::null())
        ->equals(String::create("null")));

    ASSERT_TRUE(json::stringify(String::create("456"))
        ->equals(String::create("\"456\"")));

    ASSERT_TRUE(json::stringify(Map::create())
        ->equals(String::create("{}")));

    ASSERT_TRUE(json::stringify(ArrayList::create())
        ->equals(String::create("[]")));

    Map::Ptr m = Map::create();
    m->put(3, false);
    m->put(String::create("w"), UNDEFINED);
    m->put(String::create("x"), 123);
    m->put(String::create("y"), String::create("456"));
    m->put(String::create("z"), String::null());
    ASSERT_TRUE(json::stringify(m)
        ->equals(String::create("{\"x\":123,\"y\":\"456\",\"z\":null}")));

    ArrayList::Ptr a = ArrayList::create();
    a->add(3);
    a->add(UNDEFINED);
    a->add(false);
    ASSERT_TRUE(json::stringify(a)
        ->equals(String::create("[3,null,false]")));
}

TEST(GTestJson, TestRecursiveStringify) {
    Map::Ptr m = Map::create();
    m->put(String::create("x"), 123);
    m->put(String::create("y"), ArrayList::create());

    JsObject::Ptr jo = JsObject::create();
    jo->put(3, false);
    jo->put(Object::null(), m);
    ASSERT_TRUE(json::stringify(jo)->equals(
        String::create("{\"3\":false,\"null\":{\"x\":123,\"y\":[]}}")));

    ArrayList::Ptr a = ArrayList::create();
    a->add(3);
    a->add(m);

    JsArray::Ptr ja = JsArray::create();
    ja->add(UNDEFINED);
    ja->add(Object::null());
    ja->add(a);
    ASSERT_TRUE(json::stringify(ja)->equals(
        String::create("[null,null,[3,{\"x\":123,\"y\":[]}]]")));
}

TEST(GTestJson, TestParse) {
    String::CPtr json =
        String::create("{\"x\":123,\"y\":[3.5,false],\"z\":null}");
    Value v = json::parse(json);
    ASSERT_TRUE(v.instanceof(Type<Map>::id()));
    ASSERT_TRUE(v.instanceof(Type<JsObject>::id()));

    JsObject::CPtr m = toCPtr<JsObject>(v);
    ASSERT_EQ(3, m->size());

    Value xv = m->get(String::create("x"));
    ASSERT_EQ(Type<Long>::id(), xv.type());

    Long l;
    to<Long>(xv, &l);
    ASSERT_EQ(123, l);

    Value yv = m->get(String::create("y"));
    ASSERT_TRUE(yv.instanceof(Type<ArrayList>::id()));
    ASSERT_TRUE(yv.instanceof(Type<JsArray>::id()));

    JsArray::CPtr a = toCPtr<JsArray>(yv);
    Value a0 = a->get(0);
    ASSERT_EQ(Type<Double>::id(), a0.type());

    Double d;
    to<Double>(a0, &d);
    ASSERT_EQ(3.5, d);

    Value a1 = a->get(1);
    ASSERT_EQ(Type<Boolean>::id(), a1.type());

    Boolean b;
    to<Boolean>(a1, &b);
    ASSERT_FALSE(b);

    Value zv = m->get(String::create("z"));
    ASSERT_TRUE(zv.equals(Object::null()));
}

TEST(GTestJson, TestParseString) {
    String::CPtr s = String::create("\"\\u0026\"");
    ASSERT_TRUE(json::parse(s).equals(String::create("&")));

    String::CPtr s1 = String::create("\"\\ud84c\\udfd0\"");
#ifdef LIBJ_USE_UTF32
    String::CPtr s2 = String::create(0x233d0);
#else
    std::u16string s16;
    s16 += static_cast<char16_t>(0xd84c);
    s16 += static_cast<char16_t>(0xdfd0);
    String::CPtr s2 = String::create(s16);
#endif
    ASSERT_TRUE(json::parse(s1).equals(s2));

    std::u32string s32;
    s32 += 0x3042;
    s32 += 0x2000b;
    s = String::create(s32);
    StringBuilder::Ptr sb = StringBuilder::create();
    sb->appendCStr("\"");
    sb->append(s);
    sb->appendCStr("\"");
    ASSERT_TRUE(json::parse(sb->toString()).equals(s));
}

TEST(GTestJson, TestParseNumber) {
    String::CPtr s1 = String::create("1.0");
    Value v1 = json::parse(s1);
    ASSERT_EQ(Type<Long>::id(), v1.type());
    ASSERT_TRUE(v1.equals(static_cast<Long>(1)));

    String::CPtr s2 = String::create("9007199254740990");
    Value v2 = json::parse(s2);
    ASSERT_TRUE(v2.equals(static_cast<Long>(9007199254740990)));

    String::CPtr s3 = String::create("9007199254740991");
    Value v3 = json::parse(s3);
    ASSERT_TRUE(v3.equals(static_cast<Long>(9007199254740991)));

    String::CPtr s4 = String::create("9007199254740992");
    Value v4 = json::parse(s4);
    ASSERT_TRUE(v4.equals(9007199254740992.0));

    String::CPtr s5 = String::create("9007199254740993");
    Value v5 = json::parse(s5);
    ASSERT_TRUE(v5.equals(9007199254740992.0));

    String::CPtr s6 = String::create("-9007199254740990");
    Value v6 = json::parse(s6);
    ASSERT_TRUE(v6.equals(static_cast<Long>(-9007199254740990)));

    String::CPtr s7 = String::create("-9007199254740991");
    Value v7 = json::parse(s7);
    ASSERT_TRUE(v7.equals(static_cast<Long>(-9007199254740991)));

    String::CPtr s8 = String::create("-9007199254740992");
    Value v8 = json::parse(s8);
    ASSERT_TRUE(v8.equals(-9007199254740992.0));

    String::CPtr s9 = String::create("-9007199254740993");
    Value v9 = json::parse(s9);
    ASSERT_TRUE(v9.equals(-9007199254740992.0));

    String::CPtr s10 = String::create("1.7976931348623157e+308");
    Value v10 = json::parse(s10);
    ASSERT_TRUE(v10.equals(1.7976931348623157e+308));
}

TEST(GTestJson, TestParseError) {
    Value v1 = json::parse(String::null());
    ASSERT_TRUE(v1.instanceof(Type<Error>::id()));

    Value v2 = json::parse(String::create("\""));
    ASSERT_TRUE(v2.instanceof(Type<Error>::id()));
}

TEST(GTestJson, TestEscape) {
    String::CPtr str = String::create("\b\f\n\r\t'\"\\");
    String::CPtr json = String::create("\"\\b\\f\\n\\r\\t'\\\"\\\\\"");
    String::CPtr s = toCPtr<String>(json::parse(json));
    ASSERT_TRUE(s->equals(str));
    s = json::stringify(str);
    ASSERT_TRUE(s->equals(json));
}

}  // namespace libj
<|endoftext|>


<commit_before>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <algorithm>
#include <type_traits>

#include "caf/config.hpp"
#include "caf/fwd.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/telemetry/counter.hpp"
#include "caf/telemetry/gauge.hpp"
#include "caf/telemetry/label.hpp"
#include "caf/telemetry/metric_type.hpp"

namespace caf::telemetry {

/// Represent aggregatable distributions of events.
template <class ValueType>
class histogram {
public:
  // -- member types -----------------------------------------------------------

  using value_type = ValueType;

  using gauge_type = gauge<value_type>;

  using counter_type = counter<value_type>;

  using family_setting = std::vector<value_type>;

  struct bucket_type {
    value_type upper_bound;
    counter_type count;
  };

  // -- constants --------------------------------------------------------------

  static constexpr metric_type runtime_type
    = std::is_same<value_type, double>::value ? metric_type::dbl_histogram
                                              : metric_type::int_histogram;

  // -- constructors, destructors, and assignment operators --------------------

  histogram(span<const label> labels, const settings* cfg,
            span<const value_type> upper_bounds) {
    if (!init_buckets_from_config(labels, cfg))
      init_buckets(upper_bounds);
  }

  explicit histogram(std::initializer_list<value_type> upper_bounds)
    : histogram({}, nullptr,
                make_span(upper_bounds.begin(), upper_bounds.size())) {
    // nop
  }

  histogram(const histogram&) = delete;

  histogram& operator=(const histogram&) = delete;

  ~histogram() {
    delete[] buckets_;
  }

  // -- modifiers --------------------------------------------------------------

  /// Increments the bucket where the observed value falls into and increments
  /// the sum of all observed values.
  void observe(value_type value) {
    // The last bucket has an upper bound of +inf or int_max, so we'll always
    // find a bucket and increment the counters.
    for (size_t index = 0;; ++index) {
      auto& [upper_bound, count] = buckets_[index];
      if (value <= upper_bound) {
        count.inc();
        sum_.inc(value);
        return;
      }
    }
  }

  // -- observers --------------------------------------------------------------

  /// Returns the ``counter`` objects with the configured upper bounds.
  span<const bucket_type> buckets() const noexcept {
    return {buckets_, num_buckets_};
  }

  /// Returns the sum of all observed values.
  value_type sum() const noexcept {
    return sum_.value();
  }

private:
  void init_buckets(span<const value_type> upper_bounds) {
    CAF_ASSERT(std::is_sorted(upper_bounds.begin(), upper_bounds.end()));
    using limits = std::numeric_limits<value_type>;
    num_buckets_ = upper_bounds.size() + 1;
    buckets_ = new bucket_type[num_buckets_];
    size_t index = 0;
    for (; index < upper_bounds.size(); ++index)
      buckets_[index].upper_bound = upper_bounds[index];
    if constexpr (limits::has_infinity)
      buckets_[index].upper_bound = limits::infinity();
    else
      buckets_[index].upper_bound = limits::max();
  }

  bool init_buckets_from_config(span<const label> labels, const settings* cfg) {
    if (cfg == nullptr || labels.empty())
      return false;
    for (const auto& lbl : labels) {
      if (auto ptr = get_if<settings>(cfg, lbl.str())) {
        if (auto bounds = get_as<std::vector<value_type>>(*ptr, "buckets")) {
          std::sort(bounds->begin(), bounds->end());
          bounds->erase(std::unique(bounds->begin(), bounds->end()),
                        bounds->end());
          if (bounds->empty())
            return false;
          init_buckets(*bounds);
          return true;
        }
      }
    }
    return false;
  }

  size_t num_buckets_;
  bucket_type* buckets_;
  gauge_type sum_;
};

/// Convenience alias for a histogram with value type `double`.
using dbl_histogram = histogram<double>;

/// Convenience alias for a histogram with value type `int64_t`.
using int_histogram = histogram<int64_t>;

} // namespace caf::telemetry
<commit_msg>Fix value type of histogram bucket counters<commit_after>// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.

#pragma once

#include <algorithm>
#include <type_traits>

#include "caf/config.hpp"
#include "caf/fwd.hpp"
#include "caf/settings.hpp"
#include "caf/span.hpp"
#include "caf/telemetry/counter.hpp"
#include "caf/telemetry/gauge.hpp"
#include "caf/telemetry/label.hpp"
#include "caf/telemetry/metric_type.hpp"

namespace caf::telemetry {

/// Represent aggregatable distributions of events.
template <class ValueType>
class histogram {
public:
  // -- member types -----------------------------------------------------------

  using value_type = ValueType;

  using gauge_type = gauge<value_type>;

  using family_setting = std::vector<value_type>;

  struct bucket_type {
    value_type upper_bound;
    int_counter count;
  };

  // -- constants --------------------------------------------------------------

  static constexpr metric_type runtime_type
    = std::is_same<value_type, double>::value ? metric_type::dbl_histogram
                                              : metric_type::int_histogram;

  // -- constructors, destructors, and assignment operators --------------------

  histogram(span<const label> labels, const settings* cfg,
            span<const value_type> upper_bounds) {
    if (!init_buckets_from_config(labels, cfg))
      init_buckets(upper_bounds);
  }

  explicit histogram(std::initializer_list<value_type> upper_bounds)
    : histogram({}, nullptr,
                make_span(upper_bounds.begin(), upper_bounds.size())) {
    // nop
  }

  histogram(const histogram&) = delete;

  histogram& operator=(const histogram&) = delete;

  ~histogram() {
    delete[] buckets_;
  }

  // -- modifiers --------------------------------------------------------------

  /// Increments the bucket where the observed value falls into and increments
  /// the sum of all observed values.
  void observe(value_type value) {
    // The last bucket has an upper bound of +inf or int_max, so we'll always
    // find a bucket and increment the counters.
    for (size_t index = 0;; ++index) {
      auto& [upper_bound, count] = buckets_[index];
      if (value <= upper_bound) {
        count.inc();
        sum_.inc(value);
        return;
      }
    }
  }

  // -- observers --------------------------------------------------------------

  /// Returns the ``counter`` objects with the configured upper bounds.
  span<const bucket_type> buckets() const noexcept {
    return {buckets_, num_buckets_};
  }

  /// Returns the sum of all observed values.
  value_type sum() const noexcept {
    return sum_.value();
  }

private:
  void init_buckets(span<const value_type> upper_bounds) {
    CAF_ASSERT(std::is_sorted(upper_bounds.begin(), upper_bounds.end()));
    using limits = std::numeric_limits<value_type>;
    num_buckets_ = upper_bounds.size() + 1;
    buckets_ = new bucket_type[num_buckets_];
    size_t index = 0;
    for (; index < upper_bounds.size(); ++index)
      buckets_[index].upper_bound = upper_bounds[index];
    if constexpr (limits::has_infinity)
      buckets_[index].upper_bound = limits::infinity();
    else
      buckets_[index].upper_bound = limits::max();
  }

  bool init_buckets_from_config(span<const label> labels, const settings* cfg) {
    if (cfg == nullptr || labels.empty())
      return false;
    for (const auto& lbl : labels) {
      if (auto ptr = get_if<settings>(cfg, lbl.str())) {
        if (auto bounds = get_as<std::vector<value_type>>(*ptr, "buckets")) {
          std::sort(bounds->begin(), bounds->end());
          bounds->erase(std::unique(bounds->begin(), bounds->end()),
                        bounds->end());
          if (bounds->empty())
            return false;
          init_buckets(*bounds);
          return true;
        }
      }
    }
    return false;
  }

  size_t num_buckets_;
  bucket_type* buckets_;
  gauge_type sum_;
};

/// Convenience alias for a histogram with value type `double`.
using dbl_histogram = histogram<double>;

/// Convenience alias for a histogram with value type `int64_t`.
using int_histogram = histogram<int64_t>;

} // namespace caf::telemetry
<|endoftext|>


<commit_before>/**
 * @file edge.cpp
 * @brief Simple test for OpenCV
 * @author Denis Deryugin <deryugin.denis@gmail.com>
 * @version
 * @date 03.06.2019
 */

#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include <stdio.h>
#include <time.h>

#include <drivers/video/fb.h>

using namespace cv;
using namespace std;

static void help(void) {
	printf("\nThis sample demonstrates Canny edge detection\n"
			"Call:\n"
			"	./edge [image [threshold]] \n\n");
}

static const char* keys = {
	"{help h||}"
	"{@image        | \"fruits.png\" | source image   }"
	"{@repeat |1     | number}"
};

static void imdrawfb(Mat& img) {
	struct fb_info *fbi;
	int w, h;

	fbi = fb_lookup(0);
	if (!fbi) {
		printf("fb0 not found\n");
		return;
	}

	printf("Framebuffer: %dx%d %dbpp\n", fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);

	h = min((int) fbi->var.yres, img.rows);
	w = min((int) (fbi->var.bits_per_pixel * fbi->var.xres) / 8, 3 * img.cols);

	for (int y = 0; y < h; y++) {
		const uchar *row = &img.at<uchar>(y, 0);
		for (int x = 0; x < w; x += 3) {
			unsigned rgb888	=
				0xFF000000 |
				unsigned(row[x]) |
				(unsigned(row[x + 1]) << 8) |
				(unsigned(row[x + 2]) << 16);

			((uint32_t *) fbi->screen_base)[fbi->var.xres * y + x / 3] = rgb888;
		}
	}
}

int main(int argc, const char** argv) {
	struct timeval tv_start, tv_end, tv_res;
	int edgeThresh = 2;
	Mat image, gray, edge, cedge;

	CommandLineParser parser(argc, argv, keys);
	if (parser.has("help"))  {
		help();
		return 0;
	}

	edgeThresh = parser.get<int>(1);
	string filename = parser.get<String>("@image");
	image = imread(filename, 1);
	if(image.empty()) {
		printf("Cannot read image file: %s\n", filename.c_str());
		help();
		return -1;
	}

	cedge.create(image.size(), image.type());
	cvtColor(image, gray, COLOR_BGR2GRAY);

	gettimeofday(&tv_start, NULL);
	{
		blur(gray, edge, Size(3,3));
		Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
		cedge = Scalar::all(0);
	}
	gettimeofday(&tv_end, NULL);
	timersub(&tv_end, &tv_start, &tv_res);

	image.copyTo(cedge, edge);

	printf("Image: %dx%d; Threshold=%d\n", cedge.cols, cedge.rows, edgeThresh);
	printf("Detection time: %d s %d ms\n", tv_res.tv_sec, tv_res.tv_usec / 1000);

	imdrawfb(cedge);

	return 0;
}
<commit_msg>opencv: Fix edges sample build<commit_after>/**
 * @file edge.cpp
 * @brief Simple test for OpenCV
 * @author Denis Deryugin <deryugin.denis@gmail.com>
 * @version
 * @date 03.06.2019
 */

#include "opencv2/core/utility.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/imgcodecs.hpp"
#include <stdio.h>
#include <time.h>

#include <drivers/video/fb.h>

using namespace cv;
using namespace std;

static void help(void) {
	printf("\nThis sample demonstrates Canny edge detection\n"
			"Call:\n"
			"	./edge [image [threshold]] \n\n");
}

static const char* keys = {
	"{help h||}"
	"{@image        | \"fruits.png\" | source image   }"
	"{@repeat |1     | number}"
};

static void imdrawfb(Mat& img) {
	struct fb_info *fbi;
	int w, h;

	fbi = fb_lookup(0);
	if (!fbi) {
		printf("fb0 not found\n");
		return;
	}

	printf("Framebuffer: %dx%d %dbpp\n", fbi->var.xres, fbi->var.yres, fbi->var.bits_per_pixel);

	h = min((int) fbi->var.yres, img.rows);
	w = min((int) (fbi->var.bits_per_pixel * fbi->var.xres) / 8, 3 * img.cols);

	for (int y = 0; y < h; y++) {
		const uchar *row = &img.at<uchar>(y, 0);
		for (int x = 0; x < w; x += 3) {
			unsigned rgb888	=
				0xFF000000 |
				unsigned(row[x]) |
				(unsigned(row[x + 1]) << 8) |
				(unsigned(row[x + 2]) << 16);

			((uint32_t *) fbi->screen_base)[fbi->var.xres * y + x / 3] = rgb888;
		}
	}
}

int main(int argc, const char** argv) {
	struct timeval tv_start, tv_end, tv_res;
	int edgeThresh = 2;
	Mat image, gray, edge, cedge;

	CommandLineParser parser(argc, argv, keys);
	if (parser.has("help"))  {
		help();
		return 0;
	}

	edgeThresh = parser.get<int>(1);
	string filename = parser.get<String>("@image");
	image = imread(filename, 1);
	if(image.empty()) {
		printf("Cannot read image file: %s\n", filename.c_str());
		help();
		return -1;
	}

	cedge.create(image.size(), image.type());
	cvtColor(image, gray, COLOR_BGR2GRAY);

	gettimeofday(&tv_start, NULL);
	{
		blur(gray, edge, Size(3,3));
		Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
		cedge = Scalar::all(0);
	}
	gettimeofday(&tv_end, NULL);
	timersub(&tv_end, &tv_start, &tv_res);

	image.copyTo(cedge, edge);

	printf("Image: %dx%d; Threshold=%d\n", cedge.cols, cedge.rows, edgeThresh);
	printf("Detection time: %d s %d ms\n", (int) tv_res.tv_sec, (int) tv_res.tv_usec / 1000);

	imdrawfb(cedge);

	return 0;
}
<|endoftext|>


<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Nathan Osman
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <QSignalSpy>
#include <QTest>

#include <nitroshare/deviceenumerator.h>
#include <nitroshare/devicemodel.h>

const QString TestUuid = "1234";
const QString TestName = "Test";
const QString TestAddress1 = "127.0.0.1";
const QString TestAddress2 = "::1";

class TestDeviceModel : public QObject
{
    Q_OBJECT

private Q_SLOTS:

    void testAddDevice();
    void testUpdateDevice();
    void testRemoveDevice();

private:

    void addDevice();

    DeviceEnumerator enumerator;
};

void TestDeviceModel::testAddDevice()
{
    DeviceModel model;
    model.addEnumerator(&enumerator);

    // Watch for rows being inserted
    QSignalSpy spy(&model, &DeviceModel::rowsInserted);
    addDevice();

    // Ensure one row was inserted at the expected point
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(1).toInt(), 0);
    QCOMPARE(spy.at(0).at(2).toInt(), 0);

    // Ensure the row contains the expected values
    QModelIndex index = model.index(0, 0);
    QCOMPARE(model.data(index, DeviceModel::UuidRole).toString(), TestUuid);
    QCOMPARE(model.data(index, DeviceModel::NameRole).toString(), TestName);
    QStringList addresses = model.data(index, DeviceModel::AddressesRole).toStringList();
    QCOMPARE(addresses.count(), 2);
    QVERIFY(addresses.contains(TestAddress1));
    QVERIFY(addresses.contains(TestAddress2));
}

void TestDeviceModel::testUpdateDevice()
{
    //...
}

void TestDeviceModel::testRemoveDevice()
{
    //...
}

void TestDeviceModel::addDevice()
{
    // Emit a signal for a new device that has been discovered
    emit enumerator.deviceUpdated(TestUuid, {
        { "name", TestName },
        { "addresses", QStringList({ TestAddress1, TestAddress2 }) }
    });
}

QTEST_MAIN(TestDeviceModel)
#include "TestDeviceModel.moc"
<commit_msg>Finish writing tests for DeviceModel.<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2016 Nathan Osman
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <QSignalSpy>
#include <QTest>

#include <nitroshare/deviceenumerator.h>
#include <nitroshare/devicemodel.h>

const QString TestUuid = "1234";
const QString TestName = "Test";
const QString TestAddress1 = "127.0.0.1";
const QString TestAddress2 = "::1";

class TestDeviceModel : public QObject
{
    Q_OBJECT

private Q_SLOTS:

    void testAddDevice();
    void testUpdateDevice();
    void testRemoveDevice();
    void testRemoveEnumerator();

private:

    void addDevice();

    DeviceEnumerator enumerator;
};

void TestDeviceModel::testAddDevice()
{
    DeviceModel model;
    model.addEnumerator(&enumerator);

    // Watch for rows being inserted
    QSignalSpy spy(&model, &DeviceModel::rowsInserted);
    addDevice();

    // Ensure one row was inserted at the expected point
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(1).toInt(), 0);
    QCOMPARE(spy.at(0).at(2).toInt(), 0);

    // Ensure the row contains the expected values
    QCOMPARE(model.rowCount(QModelIndex()), 1);
    QModelIndex index = model.index(0, 0);
    QCOMPARE(model.data(index, DeviceModel::UuidRole).toString(), TestUuid);
    QCOMPARE(model.data(index, DeviceModel::NameRole).toString(), TestName);
    QStringList addresses = model.data(index, DeviceModel::AddressesRole).toStringList();
    QCOMPARE(addresses.count(), 2);
    QVERIFY(addresses.contains(TestAddress1));
    QVERIFY(addresses.contains(TestAddress2));
}

void TestDeviceModel::testUpdateDevice()
{
    DeviceModel model;
    model.addEnumerator(&enumerator);
    addDevice();

    // Update the device with identical data
    QSignalSpy spy(&model, &DeviceModel::dataChanged);
    addDevice();

    // No signal should be emitted
    QCOMPARE(spy.count(), 0);

    // Make a change - remove one of the addresses
    emit enumerator.deviceUpdated(TestUuid, {
        { "addresses", QStringList({ TestAddress1 }) }
    });

    // Ensure the dataChanged signal was emitted
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(0).toModelIndex().row(), 0);
    QCOMPARE(spy.at(0).at(1).toModelIndex().row(), 0);

    // Ensure the row was updated
    QModelIndex index = model.index(0, 0);
    QStringList addresses = model.data(index, DeviceModel::AddressesRole).toStringList();
    QCOMPARE(addresses.count(), 1);
    QVERIFY(addresses.contains(TestAddress1));
}

void TestDeviceModel::testRemoveDevice()
{
    DeviceModel model;
    model.addEnumerator(&enumerator);
    addDevice();

    // Watch for removal of the device
    QSignalSpy spy(&model, &DeviceModel::rowsRemoved);
    emit enumerator.deviceRemoved(TestUuid);

    // Ensure one row was removed
    QCOMPARE(spy.count(), 1);
    QCOMPARE(spy.at(0).at(1).toInt(), 0);
    QCOMPARE(spy.at(0).at(2).toInt(), 0);

    // Ensure there is nothing left in the model
    QCOMPARE(model.rowCount(QModelIndex()), 0);
}

void TestDeviceModel::testRemoveEnumerator()
{
    DeviceModel model;
    model.addEnumerator(&enumerator);
    addDevice();

    // Watch for removal of the device once the enumerator is removed
    QSignalSpy spy(&model, &DeviceModel::rowsRemoved);
    model.removeEnumerator(&enumerator);

    // Ensure a row was removed
    QCOMPARE(spy.count(), 1);
    QCOMPARE(model.rowCount(QModelIndex()), 0);
}

void TestDeviceModel::addDevice()
{
    // Emit a signal for a new device that has been discovered
    emit enumerator.deviceUpdated(TestUuid, {
        { "name", TestName },
        { "addresses", QStringList({ TestAddress1, TestAddress2 }) }
    });
}

QTEST_MAIN(TestDeviceModel)
#include "TestDeviceModel.moc"
<|endoftext|>


<commit_before>/*
** transport-client.cpp
** Login : <hcuche@hcuche-de>
** Started on  Thu Jan  5 15:21:13 2012 Herve Cuche
** $Id$
**
** Author(s):
**  - Herve Cuche <hcuche@aldebaran-robotics.com>
**
** Copyright (C) 2012 Herve Cuche
*/

#include <iostream>
#include <cstring>
#include <map>

#include <qi/log.hpp>

#include <event2/util.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <qimessaging/transport_socket.hpp>
#include "src/network_thread.hpp"
#include <qimessaging/message.hpp>

#define MAX_LINE 16384

namespace qi {

struct TransportSocketPrivate
{
  TransportSocketPrivate()
    : tcd(NULL)
    , bev(NULL)
    , connected(false)
    , fd(-1)
  {
  }

  TransportSocketPrivate(int fileDesc)
    : tcd(NULL)
    , bev(NULL)
    , connected(false)
    , fd(fileDesc)
  {
  }

  ~TransportSocketPrivate()
  {
  }

  TransportSocketInterface            *tcd;
  struct bufferevent                  *bev;
  bool                                 connected;
  std::map<unsigned int, qi::Message*> msgSend;
  boost::mutex                         mtx;
  boost::condition_variable            cond;
  int                                  fd;
};

static void readcb(struct bufferevent *bev,
                   void *context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->readcb(bev, context);
}

static void writecb(struct bufferevent* bev,
                    void* context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->writecb(bev, context);
}

static void eventcb(struct bufferevent *bev,
                    short error,
                    void *context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->eventcb(bev, error, context);
}

void TransportSocket::readcb(struct bufferevent *bev,
                             void *context)
{
  // FIXME for multiple message & optimization
  qi::Buffer      *buf = new qi::Buffer();
  qi::Message     *msg = new qi::Message(buf);
  struct evbuffer *input = bufferevent_get_input(bev);
  struct evbuffer *tmp = evbuffer_new();

  // get the header
  evbuffer_remove(input, msg->_header, sizeof(qi::Message::MessageHeader));
  // get the data
  //TODO check the return value
  evbuffer_remove_buffer(input, tmp, msg->_header->size);
  // set the data
  buf->setData(reinterpret_cast<void*>(tmp));

  boost::mutex::scoped_lock l(_p->mtx);
  {
    _p->msgSend[msg->id()] = msg;
    _p->tcd->onReadyRead(this, *msg);
    _p->cond.notify_all();
  }
}

void TransportSocket::writecb(struct bufferevent* bev,
                              void* context)
{
  _p->tcd->onWriteDone(this);
}

void TransportSocket::eventcb(struct bufferevent *bev,
                              short events,
                              void *context)
{
  if (events & BEV_EVENT_CONNECTED)
  {
    qi::Message msg;
    _p->connected = true;
    _p->tcd->onConnected(this);
  }
  else if (events & BEV_EVENT_EOF)
  {
    qi::Message msg;
    _p->tcd->onDisconnected(this);
    _p->connected = false;
    // connection has been closed, do any clean up here
    qiLogInfo("qimessaging.TransportSocket") << "connection has been closed, do any clean up here" << std::endl;
  }
  else if (events & BEV_EVENT_ERROR)
  {
    bufferevent_free(_p->bev);
    // check errno to see what error occurred
    qiLogError("qimessaging.TransportSocket")  << "Cannnot connect" << std::endl;
  }
  else if (events & BEV_EVENT_TIMEOUT)
  {
    // must be a timeout event handle, handle it
    qiLogError("qimessaging.TransportSocket")  << "must be a timeout event handle, handle it" << std::endl;
  }
}

TransportSocket::TransportSocket()
{
  _p = new TransportSocketPrivate();
}

TransportSocket::TransportSocket(int fd,
                                 struct event_base *base)
{
  _p = new TransportSocketPrivate(fd);

  _p->bev = bufferevent_socket_new(base, _p->fd, BEV_OPT_CLOSE_ON_FREE);
  bufferevent_setcb(_p->bev, ::qi::readcb, ::qi::writecb, ::qi::eventcb, this);
  bufferevent_setwatermark(_p->bev, EV_WRITE, 0, MAX_LINE);
  bufferevent_enable(_p->bev, EV_READ|EV_WRITE);

  _p->connected = true;
}

TransportSocket::~TransportSocket()
{
  disconnect();
  delete _p;
}

bool TransportSocket::connect(const qi::Url     &url,
                              struct event_base *base)
{
  const std::string &address = url.host();
  unsigned short port = url.port();

  if (!_p->connected)
  {
    _p->bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(_p->bev, ::qi::readcb, ::qi::writecb, ::qi::eventcb, this);
    bufferevent_setwatermark(_p->bev, EV_WRITE, 0, MAX_LINE);
    bufferevent_enable(_p->bev, EV_READ|EV_WRITE);

    bufferevent_socket_connect_hostname(_p->bev, NULL, AF_INET, address.c_str(), port);

    if (_p->connected)
      return true;
  }

  return false;
}

bool TransportSocket::waitForConnected(int msecs)
{
  // no timeout
  if (msecs < 0)
  {
    while (!_p->connected)
      ;

    return true;
  }

  while (!_p->connected && msecs > 0)
  {
    qi::os::msleep(1);
    msecs--;
  }

  // timeout
  if (msecs == 0)
    return false;

  return true;
}

void TransportSocket::disconnect()
{
  if (_p->connected)
  {
    bufferevent_free(_p->bev);
    _p->bev = NULL;
    _p->connected = false;
  }
}

bool TransportSocket::waitForDisconnected(int msecs)
{
  // no timeout
  if (msecs < 0)
  {
    while (_p->connected)
      ;

    return true;
  }

  while (_p->connected && msecs > 0)
  {
    qi::os::msleep(1);
    msecs--;
  }

  // timeout
  if (msecs == 0)
    return false;

  return true;
}

bool TransportSocket::waitForId(int id, int msecs)
{
  std::map<unsigned int, qi::Message*>::iterator it;
  {
    boost::mutex::scoped_lock l(_p->mtx);
    {
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
        return true;
      if (msecs > 0)
        _p->cond.timed_wait(l, boost::posix_time::milliseconds(msecs));
      else
        _p->cond.wait(l);
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
        return true;
    }
  }
  return false;
}

void TransportSocket::read(int id, qi::Message *msg)
{
  std::map<unsigned int, qi::Message*>::iterator it;
  {
    boost::mutex::scoped_lock l(_p->mtx);
    {
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
      {
        qi::Message ans = *(it->second);
        *msg = ans;
        _p->msgSend.erase(it);
      }
    }
  }
}

bool TransportSocket::send(qi::Message &msg)
{
  msg.complete();

  struct evbuffer *output = bufferevent_get_output(_p->bev);
  if (_p->connected && !evbuffer_add_buffer(output, reinterpret_cast<struct evbuffer*>(msg.buffer()->data())))
  {
    return true;
  }

  return false;
}

void TransportSocket::setDelegate(TransportSocketInterface *delegate)
{
  _p->tcd = delegate;
}

bool TransportSocket::isConnected()
{
  return _p->connected;
}

}
<commit_msg>use bufferevent instead of evbuffer to send a message<commit_after>/*
** transport-client.cpp
** Login : <hcuche@hcuche-de>
** Started on  Thu Jan  5 15:21:13 2012 Herve Cuche
** $Id$
**
** Author(s):
**  - Herve Cuche <hcuche@aldebaran-robotics.com>
**
** Copyright (C) 2012 Herve Cuche
*/

#include <iostream>
#include <cstring>
#include <map>

#include <qi/log.hpp>

#include <event2/util.h>
#include <event2/event.h>
#include <event2/buffer.h>
#include <event2/bufferevent.h>
#include <boost/thread.hpp>
#include <boost/date_time/posix_time/posix_time.hpp>

#include <qimessaging/transport_socket.hpp>
#include "src/network_thread.hpp"
#include <qimessaging/message.hpp>

#define MAX_LINE 16384

namespace qi {

struct TransportSocketPrivate
{
  TransportSocketPrivate()
    : tcd(NULL)
    , bev(NULL)
    , connected(false)
    , fd(-1)
  {
  }

  TransportSocketPrivate(int fileDesc)
    : tcd(NULL)
    , bev(NULL)
    , connected(false)
    , fd(fileDesc)
  {
  }

  ~TransportSocketPrivate()
  {
  }

  TransportSocketInterface            *tcd;
  struct bufferevent                  *bev;
  bool                                 connected;
  std::map<unsigned int, qi::Message*> msgSend;
  boost::mutex                         mtx;
  boost::condition_variable            cond;
  int                                  fd;
};

static void readcb(struct bufferevent *bev,
                   void *context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->readcb(bev, context);
}

static void writecb(struct bufferevent* bev,
                    void* context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->writecb(bev, context);
}

static void eventcb(struct bufferevent *bev,
                    short error,
                    void *context)
{
  TransportSocket *tc = static_cast<TransportSocket*>(context);
  tc->eventcb(bev, error, context);
}

void TransportSocket::readcb(struct bufferevent *bev,
                             void *context)
{
  // FIXME for multiple message & optimization
  qi::Buffer      *buf = new qi::Buffer();
  qi::Message     *msg = new qi::Message(buf);
  struct evbuffer *input = bufferevent_get_input(bev);
  struct evbuffer *tmp = evbuffer_new();

  // get the header
  evbuffer_remove(input, msg->_header, sizeof(qi::Message::MessageHeader));
  // get the data
  //TODO check the return value
  evbuffer_remove_buffer(input, tmp, msg->_header->size);
  // set the data
  buf->setData(reinterpret_cast<void*>(tmp));

  boost::mutex::scoped_lock l(_p->mtx);
  {
    _p->msgSend[msg->id()] = msg;
    _p->tcd->onReadyRead(this, *msg);
    _p->cond.notify_all();
  }
}

void TransportSocket::writecb(struct bufferevent* bev,
                              void* context)
{
  _p->tcd->onWriteDone(this);
}

void TransportSocket::eventcb(struct bufferevent *bev,
                              short events,
                              void *context)
{
  if (events & BEV_EVENT_CONNECTED)
  {
    qi::Message msg;
    _p->connected = true;
    _p->tcd->onConnected(this);
  }
  else if (events & BEV_EVENT_EOF)
  {
    qi::Message msg;
    _p->tcd->onDisconnected(this);
    _p->connected = false;
    // connection has been closed, do any clean up here
    qiLogInfo("qimessaging.TransportSocket") << "connection has been closed, do any clean up here" << std::endl;
  }
  else if (events & BEV_EVENT_ERROR)
  {
    bufferevent_free(_p->bev);
    // check errno to see what error occurred
    qiLogError("qimessaging.TransportSocket")  << "Cannnot connect" << std::endl;
  }
  else if (events & BEV_EVENT_TIMEOUT)
  {
    // must be a timeout event handle, handle it
    qiLogError("qimessaging.TransportSocket")  << "must be a timeout event handle, handle it" << std::endl;
  }
}

TransportSocket::TransportSocket()
{
  _p = new TransportSocketPrivate();
}

TransportSocket::TransportSocket(int fd,
                                 struct event_base *base)
{
  _p = new TransportSocketPrivate(fd);

  _p->bev = bufferevent_socket_new(base, _p->fd, BEV_OPT_CLOSE_ON_FREE);
  bufferevent_setcb(_p->bev, ::qi::readcb, ::qi::writecb, ::qi::eventcb, this);
  bufferevent_setwatermark(_p->bev, EV_WRITE, 0, MAX_LINE);
  bufferevent_enable(_p->bev, EV_READ|EV_WRITE);

  _p->connected = true;
}

TransportSocket::~TransportSocket()
{
  disconnect();
  delete _p;
}

bool TransportSocket::connect(const qi::Url     &url,
                              struct event_base *base)
{
  const std::string &address = url.host();
  unsigned short port = url.port();

  if (!_p->connected)
  {
    _p->bev = bufferevent_socket_new(base, -1, BEV_OPT_CLOSE_ON_FREE);
    bufferevent_setcb(_p->bev, ::qi::readcb, ::qi::writecb, ::qi::eventcb, this);
    bufferevent_setwatermark(_p->bev, EV_WRITE, 0, MAX_LINE);
    bufferevent_enable(_p->bev, EV_READ|EV_WRITE);

    bufferevent_socket_connect_hostname(_p->bev, NULL, AF_INET, address.c_str(), port);

    if (_p->connected)
      return true;
  }

  return false;
}

bool TransportSocket::waitForConnected(int msecs)
{
  // no timeout
  if (msecs < 0)
  {
    while (!_p->connected)
      ;

    return true;
  }

  while (!_p->connected && msecs > 0)
  {
    qi::os::msleep(1);
    msecs--;
  }

  // timeout
  if (msecs == 0)
    return false;

  return true;
}

void TransportSocket::disconnect()
{
  if (_p->connected)
  {
    bufferevent_free(_p->bev);
    _p->bev = NULL;
    _p->connected = false;
  }
}

bool TransportSocket::waitForDisconnected(int msecs)
{
  // no timeout
  if (msecs < 0)
  {
    while (_p->connected)
      ;

    return true;
  }

  while (_p->connected && msecs > 0)
  {
    qi::os::msleep(1);
    msecs--;
  }

  // timeout
  if (msecs == 0)
    return false;

  return true;
}

bool TransportSocket::waitForId(int id, int msecs)
{
  std::map<unsigned int, qi::Message*>::iterator it;
  {
    boost::mutex::scoped_lock l(_p->mtx);
    {
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
        return true;
      if (msecs > 0)
        _p->cond.timed_wait(l, boost::posix_time::milliseconds(msecs));
      else
        _p->cond.wait(l);
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
        return true;
    }
  }
  return false;
}

void TransportSocket::read(int id, qi::Message *msg)
{
  std::map<unsigned int, qi::Message*>::iterator it;
  {
    boost::mutex::scoped_lock l(_p->mtx);
    {
      it = _p->msgSend.find(id);
      if (it != _p->msgSend.end())
      {
        qi::Message ans = *(it->second);
        *msg = ans;
        _p->msgSend.erase(it);
      }
    }
  }
}

bool TransportSocket::send(qi::Message &msg)
{
  msg.complete();

  struct evbuffer *buf = reinterpret_cast<struct evbuffer*>(msg.buffer()->data());
  if (_p->connected && (bufferevent_write_buffer(_p->bev, buf) == 0))
    return true;

  return false;
}

void TransportSocket::setDelegate(TransportSocketInterface *delegate)
{
  _p->tcd = delegate;
}

bool TransportSocket::isConnected()
{
  return _p->connected;
}

}
<|endoftext|>