<commit_before>/*
 * Copyright (c) 2015 Mikhail Baranov
 * Copyright (c) 2015 Victor Gaydov
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <CppUTest/TestHarness.h>

#include <stdio.h>
#include <unistd.h>

#include "roc_core/buffer_pool.h"
#include "roc_core/heap_allocator.h"
#include "roc_core/log.h"
#include "roc_core/random.h"
#include "roc_core/stddefs.h"
#include "roc_core/thread.h"
#include "roc_netio/transceiver.h"
#include "roc_packet/address_to_str.h"
#include "roc_packet/packet_pool.h"
#include "roc_packet/parse_address.h"

#include "roc/receiver.h"
#include "roc/sender.h"

namespace roc {

namespace {

enum { MaxBufSize = 4096 };

core::HeapAllocator allocator;
packet::PacketPool packet_pool(allocator, 1);
core::BufferPool<uint8_t> byte_buffer_pool(allocator, MaxBufSize, 1);

class Sender : public core::Thread {
public:
    Sender(roc_sender_config& config,
           packet::Address dst_source_addr,
           packet::Address dst_repair_addr,
           float* samples,
           size_t len,
           size_t frame_size)
        : samples_(samples)
        , sz_(len)
        , frame_size_(frame_size) {
        packet::Address addr;
        CHECK(packet::parse_address("127.0.0.1:0", addr));
        sndr_ = roc_sender_new(&config);
        CHECK(sndr_);
        CHECK(roc_sender_bind(sndr_, addr.saddr()) == 0);
        CHECK(
            roc_sender_connect(sndr_, ROC_PROTO_RTP_RSM8_SOURCE, dst_source_addr.saddr())
            == 0);
        CHECK(roc_sender_connect(sndr_, ROC_PROTO_RSM8_REPAIR, dst_repair_addr.saddr())
              == 0);
        CHECK(roc_sender_start(sndr_) == 0);
    }

    ~Sender() {
        roc_sender_stop(sndr_);
        roc_sender_delete(sndr_);
    }

private:
    virtual void run() {
        for (size_t off = 0; off < sz_; off += frame_size_) {
            if (off + frame_size_ > sz_) {
                off = sz_ - frame_size_;
            }
            const ssize_t ret = roc_sender_write(sndr_, samples_ + off, frame_size_);
            LONGS_EQUAL(frame_size_, ret);
        }
    }

    roc_sender* sndr_;
    float* samples_;
    const size_t sz_;
    const size_t frame_size_;
};

class Receiver {
public:
    Receiver(roc_receiver_config& config) {
        CHECK(packet::parse_address("127.0.0.1:0", source_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", repair_addr_));
        recv_ = roc_receiver_new(&config);
        CHECK(recv_);
        CHECK(roc_receiver_bind(recv_, ROC_PROTO_RTP_RSM8_SOURCE, source_addr_.saddr())
              == 0);
        CHECK(roc_receiver_bind(recv_, ROC_PROTO_RSM8_REPAIR, repair_addr_.saddr()) == 0);
        CHECK(roc_receiver_start(recv_) == 0);
    }

    ~Receiver() {
        roc_receiver_stop(recv_);
        roc_receiver_delete(recv_);
    }

    packet::Address source_addr() {
        return source_addr_;
    }

    packet::Address repair_addr() {
        return repair_addr_;
    }

    ssize_t read(float* samples, const size_t n_samples) {
        return roc_receiver_read(recv_, samples, n_samples);
    }

private:
    roc_receiver* recv_;

    packet::Address source_addr_;
    packet::Address repair_addr_;
};

class Proxy : private packet::IWriter {
public:
    Proxy(const packet::Address& dst_source_addr,
          const packet::Address& dst_repair_addr,
          const size_t block_size)
        : trx_(packet_pool, byte_buffer_pool, allocator)
        , dst_source_addr_(dst_source_addr)
        , dst_repair_addr_(dst_repair_addr)
        , block_size_(block_size)
        , num_(0) {
        CHECK(packet::parse_address("127.0.0.1:0", send_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", recv_source_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", recv_repair_addr_));
        writer_ = trx_.add_udp_sender(send_addr_);
        CHECK(writer_);
        CHECK(trx_.add_udp_receiver(recv_source_addr_, *this));
        CHECK(trx_.add_udp_receiver(recv_repair_addr_, *this));
    }

    packet::Address source_addr() {
        return recv_source_addr_;
    }

    packet::Address repair_addr() {
        return recv_repair_addr_;
    }

    void start() {
        trx_.start();
    }

    void stop() {
        trx_.stop();
        trx_.join();
    }

private:
    virtual void write(const packet::PacketPtr& ptr) {
        if (num_++ % block_size_ == 1) {
            return;
        }
        ptr->udp()->src_addr = send_addr_;
        if (ptr->udp()->dst_addr == recv_source_addr_) {
            ptr->udp()->dst_addr = dst_source_addr_;
        } else {
            ptr->udp()->dst_addr = dst_repair_addr_;
        }
        writer_->write(ptr);
    }

    netio::Transceiver trx_;

    packet::Address send_addr_;

    packet::Address recv_source_addr_;
    packet::Address recv_repair_addr_;

    packet::Address dst_source_addr_;
    packet::Address dst_repair_addr_;

    packet::IWriter* writer_;

    const size_t block_size_;
    size_t num_;
};

} // namespace

TEST_GROUP(sender_receiver) {
    static const size_t n_channels = 2;

    static const size_t n_source_packets = 10;
    static const size_t n_repair_packets = 5;

    static const size_t packet_len = 100;
    static const size_t packet_num = n_source_packets * 5;

    static const size_t frame_size = packet_len * 2;

    roc_sender_config sender_conf;
    roc_receiver_config receiver_conf;

    static const size_t total_sz = packet_len * packet_num;
    float s2send[total_sz];
    float s2recv[total_sz];

    void setup() {
        memset(&sender_conf, 0, sizeof(sender_conf));
        sender_conf.flags |= ROC_FLAG_DISABLE_INTERLEAVER;
        sender_conf.flags |= ROC_FLAG_ENABLE_TIMER;
        sender_conf.samples_per_packet = (unsigned int)packet_len / n_channels;
        sender_conf.fec_scheme = ROC_FEC_RS8M;
        sender_conf.n_source_packets = n_source_packets;
        sender_conf.n_repair_packets = n_repair_packets;

        memset(&receiver_conf, 0, sizeof(receiver_conf));
        receiver_conf.flags |= ROC_FLAG_DISABLE_RESAMPLER;
        receiver_conf.flags |= ROC_FLAG_ENABLE_TIMER;
        receiver_conf.samples_per_packet = (unsigned int)packet_len / n_channels;
        receiver_conf.fec_scheme = ROC_FEC_RS8M;
        receiver_conf.n_source_packets = n_source_packets;
        receiver_conf.n_repair_packets = n_repair_packets;
        receiver_conf.latency = packet_len * 20;
        receiver_conf.timeout = packet_len * 300;

        const float sstep = 1. / 32768.;
        float sval = -1 + sstep;
        for (size_t i = 0; i < total_sz; ++i) {
            s2send[i] = sval;
            sval += sstep;
            if (sval >= 1) {
                sval = -1 + sstep;
            }
        }
    }

    void check_sample_arrays(Receiver & recv, float* original, const size_t len) {
        float rx_buff[packet_len];
        size_t s_first = 0;
        size_t inner_cntr = 0;
        bool seek_first = true;
        size_t s_last = 0;

        size_t ipacket = 0;
        while (s_last == 0) {
            size_t i = 0;
            ipacket++;
            LONGS_EQUAL(packet_len, recv.read(rx_buff, packet_len));
            if (seek_first) {
                for (; i < packet_len && fabs(double(rx_buff[i])) < 1e-9;
                     i++, s_first++) {
                }
                CHECK(s_first < len);
                if (i < packet_len) {
                    seek_first = false;
                }
            }
            if (!seek_first) {
                for (; i < packet_len; i++) {
                    if (inner_cntr >= len) {
                        CHECK(fabs(double(rx_buff[i])) < 1e-9);
                        s_last = inner_cntr + s_first;
                        roc_log(LogInfo,
                                "finish: s_first: %lu, s_last: %lu, inner_cntr: %lu",
                                (unsigned long)s_first, (unsigned long)s_last,
                                (unsigned long)inner_cntr);
                        break;
                    } else if (fabs(double(original[inner_cntr] - rx_buff[i])) > 1e-9) {
                        char sbuff[256];
                        int sbuff_i =
                            snprintf(sbuff, sizeof(sbuff),
                                     "failed comparing samples #%lu\n\npacket_num: %lu\n",
                                     (unsigned long)inner_cntr, (unsigned long)ipacket);
                        snprintf(&sbuff[sbuff_i], sizeof(sbuff) - (size_t)sbuff_i,
                                 "original: %f,\treceived: %f\n",
                                 (double)original[inner_cntr], (double)rx_buff[i]);
                        FAIL(sbuff);
                    } else {
                        inner_cntr++;
                    }
                }
            }
        }
    }
};

TEST(sender_receiver, simple) {
    Receiver recv(receiver_conf);

    Sender sndr(sender_conf, recv.source_addr(), recv.repair_addr(), s2send, total_sz,
                frame_size);

    sndr.start();
    check_sample_arrays(recv, s2send, total_sz);
    sndr.join();
}

#ifdef ROC_TARGET_OPENFEC
TEST(sender_receiver, losses) {
    Receiver recv(receiver_conf);

    Proxy proxy(recv.source_addr(), recv.repair_addr(),
                n_source_packets + n_repair_packets);

    Sender sndr(sender_conf, proxy.source_addr(), proxy.repair_addr(), s2send, total_sz,
                frame_size);

    proxy.start();

    sndr.start();
    check_sample_arrays(recv, s2send, total_sz);
    sndr.join();

    proxy.stop();
}
#endif // ROC_TARGET_OPENFEC

} // namespace roc
<commit_msg>roc_lib: refactor test<commit_after>/*
 * Copyright (c) 2015 Mikhail Baranov
 * Copyright (c) 2015 Victor Gaydov
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <CppUTest/TestHarness.h>

#include <stdio.h>
#include <unistd.h>

#include "roc_core/buffer_pool.h"
#include "roc_core/heap_allocator.h"
#include "roc_core/log.h"
#include "roc_core/random.h"
#include "roc_core/stddefs.h"
#include "roc_core/thread.h"
#include "roc_netio/transceiver.h"
#include "roc_packet/address_to_str.h"
#include "roc_packet/packet_pool.h"
#include "roc_packet/parse_address.h"

#include "roc/receiver.h"
#include "roc/sender.h"

namespace roc {

namespace {

enum {
    MaxBufSize = 4096,

    NumChans = 2,

    SourcePackets = 10,
    RepairPackets = 5,

    NumPackets = SourcePackets * 5,

    PacketSamples = 100,
    FrameSamples = PacketSamples * 2,
    TotalSamples = PacketSamples * NumPackets
};

core::HeapAllocator allocator;
packet::PacketPool packet_pool(allocator, 1);
core::BufferPool<uint8_t> byte_buffer_pool(allocator, MaxBufSize, 1);

class Sender : public core::Thread {
public:
    Sender(roc_sender_config& config,
           packet::Address dst_source_addr,
           packet::Address dst_repair_addr,
           float* samples,
           size_t len,
           size_t frame_size)
        : samples_(samples)
        , sz_(len)
        , frame_size_(frame_size) {
        packet::Address addr;
        CHECK(packet::parse_address("127.0.0.1:0", addr));
        sndr_ = roc_sender_new(&config);
        CHECK(sndr_);
        CHECK(roc_sender_bind(sndr_, addr.saddr()) == 0);
        CHECK(
            roc_sender_connect(sndr_, ROC_PROTO_RTP_RSM8_SOURCE, dst_source_addr.saddr())
            == 0);
        CHECK(roc_sender_connect(sndr_, ROC_PROTO_RSM8_REPAIR, dst_repair_addr.saddr())
              == 0);
        CHECK(roc_sender_start(sndr_) == 0);
    }

    ~Sender() {
        roc_sender_stop(sndr_);
        roc_sender_delete(sndr_);
    }

private:
    virtual void run() {
        for (size_t off = 0; off < sz_; off += frame_size_) {
            if (off + frame_size_ > sz_) {
                off = sz_ - frame_size_;
            }
            const ssize_t ret = roc_sender_write(sndr_, samples_ + off, frame_size_);
            LONGS_EQUAL(frame_size_, ret);
        }
    }

    roc_sender* sndr_;
    float* samples_;
    const size_t sz_;
    const size_t frame_size_;
};

class Receiver {
public:
    Receiver(roc_receiver_config& config,
             const float* samples,
             size_t len,
             size_t frame_size)
        : samples_(samples)
        , sz_(len)
        , frame_size_(frame_size) {
        CHECK(packet::parse_address("127.0.0.1:0", source_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", repair_addr_));
        recv_ = roc_receiver_new(&config);
        CHECK(recv_);
        CHECK(roc_receiver_bind(recv_, ROC_PROTO_RTP_RSM8_SOURCE, source_addr_.saddr())
              == 0);
        CHECK(roc_receiver_bind(recv_, ROC_PROTO_RSM8_REPAIR, repair_addr_.saddr()) == 0);
        CHECK(roc_receiver_start(recv_) == 0);
    }

    ~Receiver() {
        roc_receiver_stop(recv_);
        roc_receiver_delete(recv_);
    }

    packet::Address source_addr() {
        return source_addr_;
    }

    packet::Address repair_addr() {
        return repair_addr_;
    }

    void run() {
        float rx_buff[MaxBufSize];
        size_t s_first = 0;
        size_t inner_cntr = 0;
        bool seek_first = true;
        size_t s_last = 0;

        size_t ipacket = 0;
        while (s_last == 0) {
            size_t i = 0;
            ipacket++;
            LONGS_EQUAL(frame_size_, roc_receiver_read(recv_, rx_buff, frame_size_));
            if (seek_first) {
                for (; i < frame_size_ && is_zero_(rx_buff[i]); i++, s_first++) {
                }
                CHECK(s_first < sz_);
                if (i < frame_size_) {
                    seek_first = false;
                }
            }
            if (!seek_first) {
                for (; i < frame_size_; i++) {
                    if (inner_cntr >= sz_) {
                        CHECK(is_zero_(rx_buff[i]));
                        s_last = inner_cntr + s_first;
                        roc_log(LogInfo,
                                "finish: s_first: %lu, s_last: %lu, inner_cntr: %lu",
                                (unsigned long)s_first, (unsigned long)s_last,
                                (unsigned long)inner_cntr);
                        break;
                    } else if (!is_zero_(samples_[inner_cntr] - rx_buff[i])) {
                        char sbuff[256];
                        int sbuff_i =
                            snprintf(sbuff, sizeof(sbuff),
                                     "failed comparing sample #%lu\n\npacket_num: %lu\n",
                                     (unsigned long)inner_cntr, (unsigned long)ipacket);
                        snprintf(&sbuff[sbuff_i], sizeof(sbuff) - (size_t)sbuff_i,
                                 "original: %f,\treceived: %f\n",
                                 (double)samples_[inner_cntr], (double)rx_buff[i]);
                        FAIL(sbuff);
                    } else {
                        inner_cntr++;
                    }
                }
            }
        }
    }

private:
    static inline bool is_zero_(float s) {
        return fabs(double(s)) < 1e-9;
    }

    roc_receiver* recv_;

    packet::Address source_addr_;
    packet::Address repair_addr_;

    const float* samples_;
    const size_t sz_;
    const size_t frame_size_;
};

class Proxy : private packet::IWriter {
public:
    Proxy(const packet::Address& dst_source_addr,
          const packet::Address& dst_repair_addr,
          const size_t block_size)
        : trx_(packet_pool, byte_buffer_pool, allocator)
        , dst_source_addr_(dst_source_addr)
        , dst_repair_addr_(dst_repair_addr)
        , block_size_(block_size)
        , num_(0) {
        CHECK(packet::parse_address("127.0.0.1:0", send_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", recv_source_addr_));
        CHECK(packet::parse_address("127.0.0.1:0", recv_repair_addr_));
        writer_ = trx_.add_udp_sender(send_addr_);
        CHECK(writer_);
        CHECK(trx_.add_udp_receiver(recv_source_addr_, *this));
        CHECK(trx_.add_udp_receiver(recv_repair_addr_, *this));
    }

    packet::Address source_addr() {
        return recv_source_addr_;
    }

    packet::Address repair_addr() {
        return recv_repair_addr_;
    }

    void start() {
        trx_.start();
    }

    void stop() {
        trx_.stop();
        trx_.join();
    }

private:
    virtual void write(const packet::PacketPtr& ptr) {
        if (num_++ % block_size_ == 1) {
            // packet loss
            return;
        }
        ptr->udp()->src_addr = send_addr_;
        if (ptr->udp()->dst_addr == recv_source_addr_) {
            ptr->udp()->dst_addr = dst_source_addr_;
        } else {
            ptr->udp()->dst_addr = dst_repair_addr_;
        }
        writer_->write(ptr);
    }

    netio::Transceiver trx_;

    packet::Address send_addr_;

    packet::Address recv_source_addr_;
    packet::Address recv_repair_addr_;

    packet::Address dst_source_addr_;
    packet::Address dst_repair_addr_;

    packet::IWriter* writer_;

    const size_t block_size_;
    size_t num_;
};

} // namespace

TEST_GROUP(sender_receiver) {
    roc_sender_config sender_conf;
    roc_receiver_config receiver_conf;

    float samples[TotalSamples];

    void setup() {
        memset(&sender_conf, 0, sizeof(sender_conf));
        sender_conf.flags |= ROC_FLAG_DISABLE_INTERLEAVER;
        sender_conf.flags |= ROC_FLAG_ENABLE_TIMER;
        sender_conf.samples_per_packet = (unsigned int)PacketSamples / NumChans;
        sender_conf.fec_scheme = ROC_FEC_RS8M;
        sender_conf.n_source_packets = SourcePackets;
        sender_conf.n_repair_packets = RepairPackets;

        memset(&receiver_conf, 0, sizeof(receiver_conf));
        receiver_conf.flags |= ROC_FLAG_DISABLE_RESAMPLER;
        receiver_conf.flags |= ROC_FLAG_ENABLE_TIMER;
        receiver_conf.samples_per_packet = (unsigned int)PacketSamples / NumChans;
        receiver_conf.fec_scheme = ROC_FEC_RS8M;
        receiver_conf.n_source_packets = SourcePackets;
        receiver_conf.n_repair_packets = RepairPackets;
        receiver_conf.latency = PacketSamples * 20;
        receiver_conf.timeout = PacketSamples * 300;

        init_samples();
    }

    void init_samples() {
        const float sstep = 1. / 32768.;
        float sval = -1 + sstep;
        for (size_t i = 0; i < TotalSamples; ++i) {
            samples[i] = sval;
            sval += sstep;
            if (sval >= 1) {
                sval = -1 + sstep;
            }
        }
    }
};

TEST(sender_receiver, simple) {
    Receiver receiver(receiver_conf, samples, TotalSamples, FrameSamples);

    Sender sender(sender_conf, receiver.source_addr(), receiver.repair_addr(), samples,
                  TotalSamples, FrameSamples);

    sender.start();
    receiver.run();
    sender.join();
}

#ifdef ROC_TARGET_OPENFEC
TEST(sender_receiver, losses) {
    Receiver receiver(receiver_conf, samples, TotalSamples, FrameSamples);

    Proxy proxy(receiver.source_addr(), receiver.repair_addr(),
                SourcePackets + RepairPackets);

    Sender sender(sender_conf, proxy.source_addr(), proxy.repair_addr(), samples,
                  TotalSamples, FrameSamples);

    proxy.start();

    sender.start();
    receiver.run();
    sender.join();

    proxy.stop();
}
#endif // ROC_TARGET_OPENFEC

} // namespace roc
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.socketcache");

namespace qi {

  TransportSocketCache::~TransportSocketCache() {
    close();
  }

  void TransportSocketCache::init() {
    boost::mutex::scoped_lock sl(_socketsMutex);
    _dying = false;
  }

  void TransportSocketCache::close() {
    {
      _dying = true;
      MachineConnectionMap socketsCopy;
      {
        // Do not hold _socketsMutex while iterating or deadlock may occurr
        // between disconnect() that waits for callback handler
        // and callback handler that tries to acquire _socketsMutex
        boost::mutex::scoped_lock sl(_socketsMutex);
        socketsCopy = _sockets;
      }
      MachineConnectionMap::iterator mcmIt;
      for (mcmIt = socketsCopy.begin(); mcmIt != socketsCopy.end(); ++mcmIt) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        for (tscmIt = tscm.begin(); tscmIt != tscm.end(); ++tscmIt) {
          TransportSocketConnection& tsc = tscmIt->second;
          tsc.socket->disconnected.disconnect(tscmIt->second.disconnectLink);
          tsc.socket->connected.disconnect(tscmIt->second.connectLink);
          //remove callback before calling disconnect. (we dont need them)
          if (tscmIt->second.socket->isConnected())
            tscmIt->second.socket->disconnect();
          tscmIt->second.promise.reset();
          tscmIt->second.promise.setError("session closed");
        }
      }
    }
  }

  qi::Future<qi::TransportSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo,
                                                                  const std::string protocol) {
    qi::UrlVector endpoints;
    qi::UrlVector::const_iterator urlIt;

    bool local = servInfo.machineId() == qi::os::getMachineId();
    qiLogDebug() << "local check " << servInfo.machineId() << " " <<  qi::os::getMachineId() << " " << local;
    // RFC 3330 - http://tools.ietf.org/html/rfc3330
    //   -> 127.0.0.0/8 is assigned to loopback address.
    //
    // This filters endpoints. If we are on the same machine, we just try to
    // connect on the loopback address, else we will try on all endpoints we
    // have that are not loopback.
    for (urlIt = servInfo.endpoints().begin(); urlIt != servInfo.endpoints().end(); ++urlIt) {
      qi::Url url = *urlIt;
      qiLogDebug() << "testing url " << url.str();
      if (!url.isValid())
        continue;
      if (url.host().substr(0, 4) == "127." || url.host() == "localhost") {
        if (local && (protocol == "" || url.protocol() == protocol)) {
          endpoints.push_back(url);
          break;
        }
      } else if (!local) {
        endpoints.push_back(url);
      }
    }
    if (endpoints.empty() && local && !servInfo.endpoints().empty())
    { // We are local, but localhost is not listed in endpoints.
      // Just take any entry, it has to be one of our public IP addresses
      endpoints.push_back(servInfo.endpoints().front());
    }
    if (endpoints.empty())
      qiLogWarning() << "No more endpoints available after filtering.";
    {
      boost::mutex::scoped_lock sl(_socketsMutex);
      if (_dying)
        return qi::makeFutureError<qi::TransportSocketPtr>("TransportSocketCache is closed.");

      // From here, we will see if we have a pending/established connection to
      // machineId on one of the endpoints (they all share the same promise
      // anyway). If it is the case, we return its future.
      MachineConnectionMap::iterator mcmIt;
      if ((mcmIt = _sockets.find(servInfo.machineId())) != _sockets.end()) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        for (urlIt = endpoints.begin(); urlIt != endpoints.end(); ++urlIt) {
          if ((tscmIt = tscm.find((*urlIt).str())) != tscm.end()) {
            TransportSocketConnection& tsc = tscmIt->second;
            if (tsc.promise.future().hasError()) {
              // When we have a socket with an error, we will try to connect to
              // all endpoints in case the old one is completely down.
              continue;
            }
            qiLogVerbose() << "A connection is pending or already"
                                       << " established.";
            return tsc.promise.future();
          }
        }
      }

      // Launching connections to all endpoints at the same time. They all share
      // the same promise.
      qi::Promise<qi::TransportSocketPtr> prom;
      if (endpoints.empty())
      {
        prom.setError("No endpoint available.");
        return prom.future();
      }
      // We will need this to report error (to know if all sockets didn't
      // connect).
      TransportSocketConnectionAttempt& tsca = _attempts[servInfo.machineId()];
      tsca.promise = prom;
      tsca.socket_count = 0;
      tsca.successful = false;

      // This part launches all the socket connections on the same promise. The
      // first socket to connect is the winner.
      TransportSocketConnectionMap& tscm = _sockets[servInfo.machineId()];
      for (urlIt = endpoints.begin(); urlIt != endpoints.end(); ++urlIt) {
        qi::Url url = *urlIt;
        if (protocol != "" && protocol != url.protocol())
        {
          continue;
        }

        qi::TransportSocketPtr socket = makeTransportSocket(url.protocol());
        TransportSocketConnection& tsc = tscm[url.str()];
        qiLogVerbose() << "Attempting connection to " << url.str()
                                   << " of machine id " << servInfo.machineId();
        tsc.socket = socket;
        tsc.promise = prom;
        tsc.url = url;
        tsc.connectLink = socket->connected.connect(boost::bind(&TransportSocketCache::onSocketConnected, this, socket, servInfo, url));
        tsc.disconnectLink = socket->disconnected.connect(boost::bind(&TransportSocketCache::onSocketDisconnected, this, _1, socket, servInfo, url));
        socket->connect(url).async();
        tsca.socket_count++;
      }
      return prom.future();
    } // ! boost::mutex::scoped_lock
  }

  void TransportSocketCache::onSocketDisconnected(std::string error, TransportSocketPtr socket, const qi::ServiceInfo& servInfo, const qi::Url& url) {
    {
      boost::mutex::scoped_lock sl(_socketsMutex);

      // First, we get the attempts of the machineId. It is used to know if we
      // have pending connections to other endpoints.
      MachineAttemptsMap::iterator mamIt;
      if ((mamIt = _attempts.find(servInfo.machineId())) == _attempts.end()) {
        // Unknown error. This shouldn't happen...
        return;
      }
      TransportSocketConnectionAttempt& tsca = mamIt->second;

      if (_dying) {
        tsca.promise.setError("TransportSocketCache is closed.");
        return;
      }

      tsca.socket_count--;
      if (tsca.socket_count != 0) {
        // We still have some sockets attempting to connect to the service, so
        // we just ignore this disconnection.
        return;
      }

      // No socket can be created, we just return an error.
      std::stringstream ss;
      ss << "Failed to connect to service " << servInfo.name() << " on "
         << "machine " << servInfo.machineId() << ". All endpoints are "
         << "unavailable.";
      tsca.promise.setError(ss.str());
    } // ! boost::mutex::scoped_lock
  }

  /*
   * Corner case to manage (TODO):
   *
   * You are connecting to machineId foo, you are machineId bar. foo and bar are
   * on different sub-networks with the same netmask. They sadly got the same IP
   * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
   * will try to connect its endpoints, basically:
   *   - tcp://1.2.3.4:1333 (public IP)
   *   - tcp://192.168.1.42:1333 (subnet public IP)
   * If bar is listening on port 1333, we may connect to it instead of foo (our
   * real target).
   */
  void TransportSocketCache::onSocketConnected(TransportSocketPtr socket, const qi::ServiceInfo& servInfo, const qi::Url& url) {
    {
      boost::mutex::scoped_lock sl(_socketsMutex);

      MachineAttemptsMap::iterator mamIt;
      if ((mamIt = _attempts.find(servInfo.machineId())) == _attempts.end()) {
        // Unknown error. This shouldn't happen...
        return;
      }
      TransportSocketConnectionAttempt& tsca = mamIt->second;

      if (_dying) {
        tsca.promise.setError("TransportSocketCache is closed.");
        return;
      }

      if (tsca.successful) {
        // If we are already connected to this service, disconnect this socket.
        socket->disconnect();
        return;
      }

      // Else, we set promise to this socket. We have a winner.
      MachineConnectionMap::iterator mcmIt;
      if ((mcmIt = _sockets.find(servInfo.machineId())) != _sockets.end()) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        if ((tscmIt = tscm.find(url.str())) != tscm.end()) {
          tscmIt->second.promise.setValue(socket);
          tsca.successful = true;
        }
      }
    } // ! boost::mutex::scoped_lock
  }
}
<commit_msg>Add missing timeout on future error<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.socketcache");

namespace qi {

  TransportSocketCache::~TransportSocketCache() {
    close();
  }

  void TransportSocketCache::init() {
    boost::mutex::scoped_lock sl(_socketsMutex);
    _dying = false;
  }

  void TransportSocketCache::close() {
    {
      _dying = true;
      MachineConnectionMap socketsCopy;
      {
        // Do not hold _socketsMutex while iterating or deadlock may occurr
        // between disconnect() that waits for callback handler
        // and callback handler that tries to acquire _socketsMutex
        boost::mutex::scoped_lock sl(_socketsMutex);
        socketsCopy = _sockets;
      }
      MachineConnectionMap::iterator mcmIt;
      for (mcmIt = socketsCopy.begin(); mcmIt != socketsCopy.end(); ++mcmIt) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        for (tscmIt = tscm.begin(); tscmIt != tscm.end(); ++tscmIt) {
          TransportSocketConnection& tsc = tscmIt->second;
          tsc.socket->disconnected.disconnect(tscmIt->second.disconnectLink);
          tsc.socket->connected.disconnect(tscmIt->second.connectLink);
          //remove callback before calling disconnect. (we dont need them)
          if (tscmIt->second.socket->isConnected())
            tscmIt->second.socket->disconnect();
          tscmIt->second.promise.reset();
          tscmIt->second.promise.setError("session closed");
        }
      }
    }
  }

  qi::Future<qi::TransportSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo,
                                                                  const std::string protocol) {
    qi::UrlVector endpoints;
    qi::UrlVector::const_iterator urlIt;

    bool local = servInfo.machineId() == qi::os::getMachineId();
    qiLogDebug() << "local check " << servInfo.machineId() << " " <<  qi::os::getMachineId() << " " << local;
    // RFC 3330 - http://tools.ietf.org/html/rfc3330
    //   -> 127.0.0.0/8 is assigned to loopback address.
    //
    // This filters endpoints. If we are on the same machine, we just try to
    // connect on the loopback address, else we will try on all endpoints we
    // have that are not loopback.
    for (urlIt = servInfo.endpoints().begin(); urlIt != servInfo.endpoints().end(); ++urlIt) {
      qi::Url url = *urlIt;
      qiLogDebug() << "testing url " << url.str();
      if (!url.isValid())
        continue;
      if (url.host().substr(0, 4) == "127." || url.host() == "localhost") {
        if (local && (protocol == "" || url.protocol() == protocol)) {
          endpoints.push_back(url);
          break;
        }
      } else if (!local) {
        endpoints.push_back(url);
      }
    }
    if (endpoints.empty() && local && !servInfo.endpoints().empty())
    { // We are local, but localhost is not listed in endpoints.
      // Just take any entry, it has to be one of our public IP addresses
      endpoints.push_back(servInfo.endpoints().front());
    }
    if (endpoints.empty())
      qiLogWarning() << "No more endpoints available after filtering.";
    {
      boost::mutex::scoped_lock sl(_socketsMutex);
      if (_dying)
        return qi::makeFutureError<qi::TransportSocketPtr>("TransportSocketCache is closed.");

      // From here, we will see if we have a pending/established connection to
      // machineId on one of the endpoints (they all share the same promise
      // anyway). If it is the case, we return its future.
      MachineConnectionMap::iterator mcmIt;
      if ((mcmIt = _sockets.find(servInfo.machineId())) != _sockets.end()) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        for (urlIt = endpoints.begin(); urlIt != endpoints.end(); ++urlIt) {
          if ((tscmIt = tscm.find((*urlIt).str())) != tscm.end()) {
            TransportSocketConnection& tsc = tscmIt->second;
            try
            {
              if (tsc.promise.future().isFinished() &&
                  tsc.promise.future().hasError(0)) {
                // When we have a socket with an error, we will try to connect to
                // all endpoints in case the old one is completely down.
                continue;
              }
            }
            catch (...)
            {
              continue;
            }

            qiLogVerbose() << "A connection is pending or already"
                                       << " established.";
            return tsc.promise.future();
          }
        }
      }

      // Launching connections to all endpoints at the same time. They all share
      // the same promise.
      qi::Promise<qi::TransportSocketPtr> prom;
      if (endpoints.empty())
      {
        prom.setError("No endpoint available.");
        return prom.future();
      }
      // We will need this to report error (to know if all sockets didn't
      // connect).
      TransportSocketConnectionAttempt& tsca = _attempts[servInfo.machineId()];
      tsca.promise = prom;
      tsca.socket_count = 0;
      tsca.successful = false;

      // This part launches all the socket connections on the same promise. The
      // first socket to connect is the winner.
      TransportSocketConnectionMap& tscm = _sockets[servInfo.machineId()];
      for (urlIt = endpoints.begin(); urlIt != endpoints.end(); ++urlIt) {
        qi::Url url = *urlIt;
        if (protocol != "" && protocol != url.protocol())
        {
          continue;
        }

        qi::TransportSocketPtr socket = makeTransportSocket(url.protocol());
        TransportSocketConnection& tsc = tscm[url.str()];
        qiLogVerbose() << "Attempting connection to " << url.str()
                                   << " of machine id " << servInfo.machineId();
        tsc.socket = socket;
        tsc.promise = prom;
        tsc.url = url;
        tsc.connectLink = socket->connected.connect(boost::bind(&TransportSocketCache::onSocketConnected, this, socket, servInfo, url));
        tsc.disconnectLink = socket->disconnected.connect(boost::bind(&TransportSocketCache::onSocketDisconnected, this, _1, socket, servInfo, url));
        socket->connect(url).async();
        tsca.socket_count++;
      }
      return prom.future();
    } // ! boost::mutex::scoped_lock
  }

  void TransportSocketCache::onSocketDisconnected(std::string error, TransportSocketPtr socket, const qi::ServiceInfo& servInfo, const qi::Url& url) {
    {
      boost::mutex::scoped_lock sl(_socketsMutex);

      // First, we get the attempts of the machineId. It is used to know if we
      // have pending connections to other endpoints.
      MachineAttemptsMap::iterator mamIt;
      if ((mamIt = _attempts.find(servInfo.machineId())) == _attempts.end()) {
        // Unknown error. This shouldn't happen...
        return;
      }
      TransportSocketConnectionAttempt& tsca = mamIt->second;

      if (_dying) {
        tsca.promise.setError("TransportSocketCache is closed.");
        return;
      }

      tsca.socket_count--;
      if (tsca.socket_count != 0) {
        // We still have some sockets attempting to connect to the service, so
        // we just ignore this disconnection.
        return;
      }

      // No socket can be created, we just return an error.
      std::stringstream ss;
      ss << "Failed to connect to service " << servInfo.name() << " on "
         << "machine " << servInfo.machineId() << ". All endpoints are "
         << "unavailable.";
      tsca.promise.setError(ss.str());
    } // ! boost::mutex::scoped_lock
  }

  /*
   * Corner case to manage (TODO):
   *
   * You are connecting to machineId foo, you are machineId bar. foo and bar are
   * on different sub-networks with the same netmask. They sadly got the same IP
   * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
   * will try to connect its endpoints, basically:
   *   - tcp://1.2.3.4:1333 (public IP)
   *   - tcp://192.168.1.42:1333 (subnet public IP)
   * If bar is listening on port 1333, we may connect to it instead of foo (our
   * real target).
   */
  void TransportSocketCache::onSocketConnected(TransportSocketPtr socket, const qi::ServiceInfo& servInfo, const qi::Url& url) {
    {
      boost::mutex::scoped_lock sl(_socketsMutex);

      MachineAttemptsMap::iterator mamIt;
      if ((mamIt = _attempts.find(servInfo.machineId())) == _attempts.end()) {
        // Unknown error. This shouldn't happen...
        return;
      }
      TransportSocketConnectionAttempt& tsca = mamIt->second;

      if (_dying) {
        tsca.promise.setError("TransportSocketCache is closed.");
        return;
      }

      if (tsca.successful) {
        // If we are already connected to this service, disconnect this socket.
        socket->disconnect();
        return;
      }

      // Else, we set promise to this socket. We have a winner.
      MachineConnectionMap::iterator mcmIt;
      if ((mcmIt = _sockets.find(servInfo.machineId())) != _sockets.end()) {
        TransportSocketConnectionMap& tscm = mcmIt->second;
        TransportSocketConnectionMap::iterator tscmIt;
        if ((tscmIt = tscm.find(url.str())) != tscm.end()) {
          tscmIt->second.promise.setValue(socket);
          tsca.successful = true;
        }
      }
    } // ! boost::mutex::scoped_lock
  }
}
<|endoftext|>


<commit_before>/*
 *
 */
#ifndef UNIX_CONDITION_QUEUE_HPP
#define UNIX_CONDITION_QUEUE_HPP

#include <condition_variable>
#include <mutex>
#include <optional>

#include "condition-queue-detail.hpp"
#include "move-queue.hpp"

namespace unix
{

  // mutex_t is either std::mutex or std::ref(std::mutex)
  // similar for condition_t
  template <typename record_t, typename mutex_t, typename condition_t>
  class condition_queue_t
  {

  public:

    condition_queue_t () = default;
    condition_queue_t (mutex_t mutex, condition_t condition);
    ~condition_queue_t () = default;

    void push (record_t record);

    template <bool wait>
    std::optional<record_t> pop ();

    template <bool lock>
    bool empty ();

    template <bool lock>
    void notify_one ();

  private:

    mutex_t m_mutex;
    condition_t m_condition;

    move_queue_t<record_t> m_queue;
  };

  template <typename record_t, typename mutex_t, typename condition_t>
  condition_queue_t<record_t, mutex_t, condition_t>::
  condition_queue_t (mutex_t mutex, condition_t condition)
    : m_mutex (mutex),
      m_condition (condition)
  {
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  void condition_queue_t<record_t, mutex_t, condition_t>::push (record_t record)
  {
    // take a reference
    std::mutex &mutex (m_mutex);
    std::lock_guard lock (mutex);

    bool empty = m_queue.empty ();

    m_queue.push (std::move (record));

    if (empty == true) {
      std::condition_variable &condition (m_condition);
      condition.notify_one ();
    }
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_wait>
  std::optional<record_t>
  condition_queue_t<record_t, mutex_t, condition_t>::pop ()
  {
    std::mutex &mutex (m_mutex);
    std::unique_lock lock (mutex);
    std::condition_variable &condition (m_condition);

    if (m_queue.empty () == true)
      condition_queue_detail::wait<really_wait>(lock, condition);

    if (m_queue.empty ())
      return {};

    return std::optional<record_t>(m_queue.pop ());
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_lock>
  bool condition_queue_t<record_t, mutex_t, condition_t>::empty ()
  {
    std::mutex &mutex (m_mutex);
    typename condition_queue_detail::guard<really_lock>::lock lock (mutex);

    return m_queue.empty ();
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_lock>
  void condition_queue_t<record_t, mutex_t, condition_t>::notify_one ()
  {
    std::mutex &mutex (m_mutex);
    typename condition_queue_detail::unique<really_lock>::lock lock (mutex);

    std::condition_variable &condition (m_condition);
    condition.notify_one ();
  }

} // unix

#endif
<commit_msg>pi: Simplify condition-queue, use if constexpr<commit_after>/*
 *
 */
#ifndef UNIX_CONDITION_QUEUE_HPP
#define UNIX_CONDITION_QUEUE_HPP

#include <condition_variable>
#include <mutex>
#include <optional>

// #include "condition-queue-detail.hpp"
#include "move-queue.hpp"

namespace unix
{

  // mutex_t is either std::mutex or std::ref(std::mutex)
  // similar for condition_t
  template <typename record_t, typename mutex_t, typename condition_t>
  class condition_queue_t
  {

  public:

    condition_queue_t () = default;
    condition_queue_t (mutex_t mutex, condition_t condition);
    ~condition_queue_t () = default;

    void push (record_t record);

    template <bool wait>
    std::optional<record_t> pop ();

    template <bool lock>
    bool empty ();

    template <bool lock>
    void notify_one ();

  private:

    mutex_t m_mutex;
    condition_t m_condition;

    move_queue_t<record_t> m_queue;
  };

  template <typename record_t, typename mutex_t, typename condition_t>
  condition_queue_t<record_t, mutex_t, condition_t>::
  condition_queue_t (mutex_t mutex, condition_t condition)
    : m_mutex (mutex),
      m_condition (condition)
  {
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  void condition_queue_t<record_t, mutex_t, condition_t>::push (record_t record)
  {
    // take a reference
    std::mutex &mutex (m_mutex);
    std::lock_guard lock (mutex);

    bool empty = m_queue.empty ();

    m_queue.push (std::move (record));

    if (empty == true) {
      std::condition_variable &condition (m_condition);
      condition.notify_one ();
    }
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_wait>
  std::optional<record_t>
  condition_queue_t<record_t, mutex_t, condition_t>::pop ()
  {
    std::mutex &mutex (m_mutex);
    std::unique_lock lock (mutex);
    std::condition_variable &condition (m_condition);

    if constexpr (really_wait)
      condition.wait (lock);

    //   }
    // a;
    // if (m_queue.empty () == true)
    //   condition_queue_detail::wait<really_wait>(lock, condition);

    if (m_queue.empty ())
      return {};

    return std::optional<record_t>(m_queue.pop ());
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_lock>
  bool condition_queue_t<record_t, mutex_t, condition_t>::empty ()
  {
    // std::mutex &mutex (m_mutex);
    if constexpr (really_lock)
      std::lock_guard lock (m_mutex);
    // typename condition_queue_detail::guard<really_lock>::lock lock (mutex);

    return m_queue.empty ();
  }

  template <typename record_t, typename mutex_t, typename condition_t>
  template <bool really_lock>
  void condition_queue_t<record_t, mutex_t, condition_t>::notify_one ()
  {
    // std::mutex &mutex (m_mutex);
    // typename condition_queue_detail::unique<really_lock>::lock lock (mutex);

    if constexpr (really_lock)
      std::lock_guard lock (m_mutex);

    // std::condition_variable &condition (m_condition);
    m_condition.notify_one ();
  }

} // unix

#endif
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: node.cxx,v $
 *
 *  $Revision: 1.7 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 23:29:34 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include "node.hxx"

#ifndef INCLUDED_SHARABLE_ANYDATA_HXX
#include "anydata.hxx"
#endif
#ifndef INCLUDED_SHARABLE_TREEFRAGMENT_HXX
#include "treefragment.hxx"
#endif
#ifndef CONFIGMGR_ACCESSOR_HXX
#include "accessor.hxx"
#endif
#ifndef CONFIGMGR_UPDATEACCESSOR_HXX
#include "updateaccessor.hxx"
#endif

#ifndef CONFIGMGR_CONFIGURATION_ATTRIBUTES_HXX_
#include "attributes.hxx"
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_ANY_HXX_
#include <com/sun/star/uno/Any.hxx>
#endif

#ifndef INCLUDED_CSTDDEF
#include <cstddef>
#define INCLUDED_CSTDDEF
#endif

namespace configmgr
{
//-----------------------------------------------------------------------------
    namespace sharable
    {
//-----------------------------------------------------------------------------

//        Name            name;
  //          Offset          parent; // always counts backwards
    //        Flags::Field    flags;
      //      Type ::Field    type;   // contains discriminator for union

rtl::OUString NodeInfo::getName(memory::Accessor const & _aAccessor) const
{
    return readString(_aAccessor,this->name);
}
//-----------------------------------------------------------------------------

configmgr::node::Attributes NodeInfo::getNodeInfoAttributes() const
{
    configmgr::node::Attributes aResult;

    bool help = !!(flags & Flags::readonly);
    aResult.setAccess( help,!!(flags & Flags::finalized) );
//    aResult.setAccess( !!(flags & Flags::readonly),!!(flags & Flags::finalized) );

    aResult.setNullable(!!(flags & Flags::nullable));
    aResult.setLocalized(!!(flags & Flags::localized));

    configmgr::node::State state = (flags & Flags::defaulted)   ? configmgr::node::isDefault :
                        (flags & Flags::defaultable) ? configmgr::node::isMerged  :
                                                       configmgr::node::isReplaced;
    aResult.setState(state);

    return aResult;
}
//-----------------------------------------------------------------------------

bool NodeInfo::isDefault() const
{
    return !!(this->flags & Flags::defaulted);
}
//-----------------------------------------------------------------------------

bool NodeInfo::isLocalized() const
{
    return !!(this->flags & Flags::localized);
}
//-----------------------------------------------------------------------------

void NodeInfo::markAsDefault(bool bDefault)
{
    if (bDefault)
    {
        OSL_ENSURE(flags & Flags::defaultable,"Marking a non-defaultable node as default");
        this->flags |= Flags::defaulted;
    }
    else
        this->flags &= ~Flags::defaulted;
}
//-----------------------------------------------------------------------------

bool GroupNode::hasDefaultsAvailable() const
{
    if (this->info.isDefault())
        return true;

    if (node(this)->getTreeFragment()->hasDefaultsAvailable())
        return true;

#if 0 // extended check for default state
    for (Node const * pChild = getFirstChild(); pChild != NULL; pChild = getNextChild(pChild))
        if (! pChild->isDefault() )
            return false;

    return true;
#endif

    return false;
}
//-----------------------------------------------------------------------------

Node * GroupNode::getFirstChild()
{
    OSL_ENSURE(numDescendants, "Groups MUST have at least one child");
    return node(this) + 1;
}
//-----------------------------------------------------------------------------

Node const * GroupNode::getFirstChild()  const
{
    OSL_ENSURE(numDescendants, "Groups MUST have at least one child");
    return node(this) + 1;
}
//-----------------------------------------------------------------------------

static
Offset implGetNextChildOffset(GroupNode const * _pParent, Node const * _pChild)
{
    OSL_PRECOND(_pChild, "getNextChild: previous child must not be NULL");
    OSL_PRECOND(_pChild->getParentNode() == node(_pParent), "getNextChild: not a child of this node");

    OSL_ENSURE( node(_pParent) < _pChild && _pChild <= node(_pParent) + _pParent->numDescendants,
                "getNextChild: child out of descendants range");

    // offset to child's next sibling
    Offset next = 1;
    if ( _pChild->isGroup())
    {
        next = next + _pChild->group.numDescendants;
    }

    if (_pChild->node.info.parent + next > _pParent->numDescendants)
    {
        OSL_ENSURE(_pChild->node.info.parent + next == _pParent->numDescendants+1, "Next child candidate should match next sibling here");
        return 0;
    }

    OSL_POSTCOND( (_pChild+next)->getParentNode() == node(_pParent), "getNextChild: not a child of this node");
    return next;
}
//-----------------------------------------------------------------------------

Node * GroupNode::getNextChild(Node * _pChild)
{
    if (Offset next = implGetNextChildOffset(this, _pChild))
        return _pChild + next;

    else
        return NULL;
}
//-----------------------------------------------------------------------------

Node const * GroupNode::getNextChild(Node const * _pChild) const
{
    if (Offset next = implGetNextChildOffset(this, _pChild))
        return _pChild + next;

    else
        return NULL;
}
//-----------------------------------------------------------------------------

bool SetNode::isLocalizedValue() const
{
    return info.isLocalized();
}
//-----------------------------------------------------------------------------

// TODO: optimize this - keep a list of such structs ....
struct SetNodeTemplateData
{
    Name name;
    Name module;
};
//-----------------------------------------------------------------------------
static inline
SetNodeTemplateData * readTemplateData(memory::Allocator const & _anAllocator, Address _aTemplateData)
{
    return static_cast<SetNodeTemplateData *>( _anAllocator.access(_aTemplateData) );
}
//-----------------------------------------------------------------------------
static inline
SetNodeTemplateData const * readTemplateData(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    return static_cast<SetNodeTemplateData const*>( _anAccessor.access(memory::Pointer(_aTemplateData)) );
}
//-----------------------------------------------------------------------------

Address SetNode::allocTemplateData(memory::Allocator const & _anAllocator, NameChar const * pName, NameChar const * pModule)
{
    rtl::OUString aName(pName), aModule(pModule);

    Address aData = _anAllocator.allocate(sizeof(SetNodeTemplateData));

    if (aData)
    {
        SetNodeTemplateData * pData = readTemplateData(_anAllocator,aData);

        OSL_ENSURE(pData, "Creating template data: unexpected NULL data");

        pData->name     = allocName(_anAllocator,aName);
        pData->module   = allocName(_anAllocator,aModule);
    }
    return aData;
}
//-----------------------------------------------------------------------------

void SetNode::releaseTemplateData(memory::Allocator const & _anAllocator, Address _aTemplateData)
{
    if (!_aTemplateData) return;

    SetNodeTemplateData const * pData = readTemplateData(_anAllocator,_aTemplateData);

    OSL_ENSURE(pData, "Freeing template data: unexpected NULL data");

    freeName(_anAllocator,pData->name);
    freeName(_anAllocator,pData->module);

    _anAllocator.deallocate(_aTemplateData);
}
//-----------------------------------------------------------------------------

NameChar const * SetNode::getTemplateDataName(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    OSL_PRECOND(_aTemplateData, "Reading template data: unexpected NULL pointer");

    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,_aTemplateData);

    OSL_ENSURE(pData, "Reading template data: unexpected NULL data");

    return accessName(_anAccessor,pData->name);
}
//-----------------------------------------------------------------------------

NameChar const * SetNode::getTemplateDataModule(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    OSL_PRECOND(_aTemplateData, "Reading template data: unexpected NULL pointer");

    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,_aTemplateData);

    OSL_ENSURE(pData, "Reading template data: unexpected NULL data");

    return accessName(_anAccessor,pData->module);
}
//-----------------------------------------------------------------------------

rtl::OUString SetNode::getElementTemplateName(memory::Accessor const & _anAccessor)   const
{
    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,this->elementType);

    OSL_ENSURE(pData, "ERROR: No template data found for set");

    return readName(_anAccessor,pData->name);
}
//-----------------------------------------------------------------------------

rtl::OUString SetNode::getElementTemplateModule(memory::Accessor const & _anAccessor) const
{
    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,this->elementType);

    OSL_ENSURE(pData, "ERROR: No template data found for set");

    return readName(_anAccessor,pData->module);
}
//-----------------------------------------------------------------------------

static inline
TreeFragment const * implGetFragmentFromList(memory::Accessor const & _anAccessor, List _aListEntry)
{
    return static_cast<TreeFragment const *>(_anAccessor.access(memory::Pointer(_aListEntry)));
}
//-----------------------------------------------------------------------------

TreeFragment const  * SetNode::getFirstElement(memory::Accessor const & _anAccessor) const
{
    return implGetFragmentFromList(_anAccessor, this->elements);
}
//-----------------------------------------------------------------------------

TreeFragment const  * SetNode::getNextElement(memory::Accessor const & _anAccessor, TreeFragment const * _pElement) const
{
    OSL_PRECOND(_pElement, "getNextElement: previous element must not be NULL");
    OSL_PRECOND(_pElement->header.parent == _anAccessor.address(this).value(),
                "getNextElement: not an element of this node");

    return implGetFragmentFromList(_anAccessor, _pElement->header.next);
}
//-----------------------------------------------------------------------------

bool ValueNode::isEmpty() const
{
    Type::Field const empty_value_type = Type::value_any | Type::nodetype_value;

    return info.type == empty_value_type;
}
//-----------------------------------------------------------------------------

bool ValueNode::isNull() const
{
    Flags::Type availmask = (info.flags & Flags::defaulted) ?
                                Flags::defaultAvailable :
                                Flags::valueAvailable;

    return !(info.flags & availmask);
}
//-----------------------------------------------------------------------------

bool ValueNode::hasUsableDefault() const
{
    return (info.flags & Flags::defaultable) &&
           (info.flags & (Flags::defaultAvailable| Flags::nullable));
}
//-----------------------------------------------------------------------------

uno::Type   ValueNode::getValueType()  const
{
    AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

    return getUnoType(aType);
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getValue(memory::Accessor const & _aAccessor)      const
{
    if (info.flags & Flags::defaulted)
        return getDefaultValue(_aAccessor);

    else
        return getUserValue(_aAccessor);
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getUserValue(memory::Accessor const & _aAccessor)      const
{
    if (info.flags & Flags::valueAvailable)
    {
        AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

        return readData(_aAccessor,aType,this->value);
    }
    else
        return uno::Any();
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getDefaultValue(memory::Accessor const & _aAccessor)    const
{
    if (info.flags & Flags::defaultAvailable)
    {
        AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

        return readData(_aAccessor,aType,this->defaultValue);
    }
    else
        return uno::Any();
}
//-----------------------------------------------------------------------------

bool Node::isNamed(rtl::OUString const & _aName, memory::Accessor const & _aAccessor) const
{
    return 0 == rtl_ustr_compare(_aName.getStr(),accessString(_aAccessor,node.info.name));
}
//-----------------------------------------------------------------------------

rtl::OUString Node::getName(memory::Accessor const & _aAccessor) const
{
    return node.info.getName(_aAccessor);
}
//-----------------------------------------------------------------------------

configmgr::node::Attributes Node::getAttributes() const
{
    if(this->isFragmentRoot())
    {
        return this->getTreeFragment()->getAttributes();
    }
    else
    {
        return node.info.getNodeInfoAttributes();
    }
}
//-----------------------------------------------------------------------------

bool Node::isDefault() const
{
    return node.info.isDefault();
}
//-----------------------------------------------------------------------------

bool Node::isLocalized() const
{
    return node.info.isLocalized();
}
//-----------------------------------------------------------------------------

bool Node::isGroup()  const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_group;
}
//-----------------------------------------------------------------------------

bool Node::isSet()    const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_set;
}
//-----------------------------------------------------------------------------

bool Node::isValue()  const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_value;
}
//-----------------------------------------------------------------------------

GroupNode       * Node::groupData()
{
    return isGroup() ? &this->group : NULL;
}
//-----------------------------------------------------------------------------

GroupNode const * Node::groupData() const
{
    return isGroup() ? &this->group : NULL;
}
//-----------------------------------------------------------------------------

SetNode         * Node::setData()
{
    return isSet() ? &this->set : NULL;
}
//-----------------------------------------------------------------------------
SetNode   const * Node::setData()   const
{
    return isSet() ? &this->set : NULL;
}
//-----------------------------------------------------------------------------
ValueNode       * Node::valueData()
{
    return isValue() ? &this->value : NULL;
}
//-----------------------------------------------------------------------------
ValueNode const * Node::valueData() const
{
    return isValue() ? &this->value : NULL;
}
//-----------------------------------------------------------------------------

bool Node::isFragmentRoot() const
{
    return ! node.info.parent;
}
//-----------------------------------------------------------------------------

Node * Node::getParentNode()
{
    return node.info.parent ? this - node.info.parent : NULL;
}
//-----------------------------------------------------------------------------

Node const * Node::getParentNode() const
{
    return node.info.parent ? this - node.info.parent : NULL;
}
//-----------------------------------------------------------------------------

static Offset getFragmentIndex(Node const * pNode)
{
    Offset result = 0;
    while (Offset step = pNode->node.info.parent)
    {
        result = result + step;
        pNode  -= step;
    }
    return result;
}
//-----------------------------------------------------------------------------

TreeFragment * Node::getTreeFragment()
{
    void * pRoot = this - getFragmentIndex(this);

    void * pFrag = static_cast<char*>(pRoot) - offsetof(TreeFragment,nodes);

    return static_cast<TreeFragment *>(pFrag);
}
//-----------------------------------------------------------------------------

TreeFragment const * Node::getTreeFragment() const
{
    void const * pRoot = this - getFragmentIndex(this);

    void const * pFrag = static_cast<char const*>(pRoot) - offsetof(TreeFragment,nodes);

    return static_cast<TreeFragment const *>(pFrag);
}

//-----------------------------------------------------------------------------
    } // namespace sharable
//-----------------------------------------------------------------------------
} // namespace configmgr

<commit_msg>INTEGRATION: CWS pchfix02 (1.7.18); FILE MERGED 2006/09/01 17:20:43 kaib 1.7.18.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: node.cxx,v $
 *
 *  $Revision: 1.8 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-16 15:21:33 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_configmgr.hxx"

#include "node.hxx"

#ifndef INCLUDED_SHARABLE_ANYDATA_HXX
#include "anydata.hxx"
#endif
#ifndef INCLUDED_SHARABLE_TREEFRAGMENT_HXX
#include "treefragment.hxx"
#endif
#ifndef CONFIGMGR_ACCESSOR_HXX
#include "accessor.hxx"
#endif
#ifndef CONFIGMGR_UPDATEACCESSOR_HXX
#include "updateaccessor.hxx"
#endif

#ifndef CONFIGMGR_CONFIGURATION_ATTRIBUTES_HXX_
#include "attributes.hxx"
#endif

#ifndef _RTL_USTRING_HXX_
#include <rtl/ustring.hxx>
#endif
#ifndef _COM_SUN_STAR_UNO_ANY_HXX_
#include <com/sun/star/uno/Any.hxx>
#endif

#ifndef INCLUDED_CSTDDEF
#include <cstddef>
#define INCLUDED_CSTDDEF
#endif

namespace configmgr
{
//-----------------------------------------------------------------------------
    namespace sharable
    {
//-----------------------------------------------------------------------------

//        Name            name;
  //          Offset          parent; // always counts backwards
    //        Flags::Field    flags;
      //      Type ::Field    type;   // contains discriminator for union

rtl::OUString NodeInfo::getName(memory::Accessor const & _aAccessor) const
{
    return readString(_aAccessor,this->name);
}
//-----------------------------------------------------------------------------

configmgr::node::Attributes NodeInfo::getNodeInfoAttributes() const
{
    configmgr::node::Attributes aResult;

    bool help = !!(flags & Flags::readonly);
    aResult.setAccess( help,!!(flags & Flags::finalized) );
//    aResult.setAccess( !!(flags & Flags::readonly),!!(flags & Flags::finalized) );

    aResult.setNullable(!!(flags & Flags::nullable));
    aResult.setLocalized(!!(flags & Flags::localized));

    configmgr::node::State state = (flags & Flags::defaulted)   ? configmgr::node::isDefault :
                        (flags & Flags::defaultable) ? configmgr::node::isMerged  :
                                                       configmgr::node::isReplaced;
    aResult.setState(state);

    return aResult;
}
//-----------------------------------------------------------------------------

bool NodeInfo::isDefault() const
{
    return !!(this->flags & Flags::defaulted);
}
//-----------------------------------------------------------------------------

bool NodeInfo::isLocalized() const
{
    return !!(this->flags & Flags::localized);
}
//-----------------------------------------------------------------------------

void NodeInfo::markAsDefault(bool bDefault)
{
    if (bDefault)
    {
        OSL_ENSURE(flags & Flags::defaultable,"Marking a non-defaultable node as default");
        this->flags |= Flags::defaulted;
    }
    else
        this->flags &= ~Flags::defaulted;
}
//-----------------------------------------------------------------------------

bool GroupNode::hasDefaultsAvailable() const
{
    if (this->info.isDefault())
        return true;

    if (node(this)->getTreeFragment()->hasDefaultsAvailable())
        return true;

#if 0 // extended check for default state
    for (Node const * pChild = getFirstChild(); pChild != NULL; pChild = getNextChild(pChild))
        if (! pChild->isDefault() )
            return false;

    return true;
#endif

    return false;
}
//-----------------------------------------------------------------------------

Node * GroupNode::getFirstChild()
{
    OSL_ENSURE(numDescendants, "Groups MUST have at least one child");
    return node(this) + 1;
}
//-----------------------------------------------------------------------------

Node const * GroupNode::getFirstChild()  const
{
    OSL_ENSURE(numDescendants, "Groups MUST have at least one child");
    return node(this) + 1;
}
//-----------------------------------------------------------------------------

static
Offset implGetNextChildOffset(GroupNode const * _pParent, Node const * _pChild)
{
    OSL_PRECOND(_pChild, "getNextChild: previous child must not be NULL");
    OSL_PRECOND(_pChild->getParentNode() == node(_pParent), "getNextChild: not a child of this node");

    OSL_ENSURE( node(_pParent) < _pChild && _pChild <= node(_pParent) + _pParent->numDescendants,
                "getNextChild: child out of descendants range");

    // offset to child's next sibling
    Offset next = 1;
    if ( _pChild->isGroup())
    {
        next = next + _pChild->group.numDescendants;
    }

    if (_pChild->node.info.parent + next > _pParent->numDescendants)
    {
        OSL_ENSURE(_pChild->node.info.parent + next == _pParent->numDescendants+1, "Next child candidate should match next sibling here");
        return 0;
    }

    OSL_POSTCOND( (_pChild+next)->getParentNode() == node(_pParent), "getNextChild: not a child of this node");
    return next;
}
//-----------------------------------------------------------------------------

Node * GroupNode::getNextChild(Node * _pChild)
{
    if (Offset next = implGetNextChildOffset(this, _pChild))
        return _pChild + next;

    else
        return NULL;
}
//-----------------------------------------------------------------------------

Node const * GroupNode::getNextChild(Node const * _pChild) const
{
    if (Offset next = implGetNextChildOffset(this, _pChild))
        return _pChild + next;

    else
        return NULL;
}
//-----------------------------------------------------------------------------

bool SetNode::isLocalizedValue() const
{
    return info.isLocalized();
}
//-----------------------------------------------------------------------------

// TODO: optimize this - keep a list of such structs ....
struct SetNodeTemplateData
{
    Name name;
    Name module;
};
//-----------------------------------------------------------------------------
static inline
SetNodeTemplateData * readTemplateData(memory::Allocator const & _anAllocator, Address _aTemplateData)
{
    return static_cast<SetNodeTemplateData *>( _anAllocator.access(_aTemplateData) );
}
//-----------------------------------------------------------------------------
static inline
SetNodeTemplateData const * readTemplateData(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    return static_cast<SetNodeTemplateData const*>( _anAccessor.access(memory::Pointer(_aTemplateData)) );
}
//-----------------------------------------------------------------------------

Address SetNode::allocTemplateData(memory::Allocator const & _anAllocator, NameChar const * pName, NameChar const * pModule)
{
    rtl::OUString aName(pName), aModule(pModule);

    Address aData = _anAllocator.allocate(sizeof(SetNodeTemplateData));

    if (aData)
    {
        SetNodeTemplateData * pData = readTemplateData(_anAllocator,aData);

        OSL_ENSURE(pData, "Creating template data: unexpected NULL data");

        pData->name     = allocName(_anAllocator,aName);
        pData->module   = allocName(_anAllocator,aModule);
    }
    return aData;
}
//-----------------------------------------------------------------------------

void SetNode::releaseTemplateData(memory::Allocator const & _anAllocator, Address _aTemplateData)
{
    if (!_aTemplateData) return;

    SetNodeTemplateData const * pData = readTemplateData(_anAllocator,_aTemplateData);

    OSL_ENSURE(pData, "Freeing template data: unexpected NULL data");

    freeName(_anAllocator,pData->name);
    freeName(_anAllocator,pData->module);

    _anAllocator.deallocate(_aTemplateData);
}
//-----------------------------------------------------------------------------

NameChar const * SetNode::getTemplateDataName(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    OSL_PRECOND(_aTemplateData, "Reading template data: unexpected NULL pointer");

    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,_aTemplateData);

    OSL_ENSURE(pData, "Reading template data: unexpected NULL data");

    return accessName(_anAccessor,pData->name);
}
//-----------------------------------------------------------------------------

NameChar const * SetNode::getTemplateDataModule(memory::Accessor const & _anAccessor, Address _aTemplateData)
{
    OSL_PRECOND(_aTemplateData, "Reading template data: unexpected NULL pointer");

    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,_aTemplateData);

    OSL_ENSURE(pData, "Reading template data: unexpected NULL data");

    return accessName(_anAccessor,pData->module);
}
//-----------------------------------------------------------------------------

rtl::OUString SetNode::getElementTemplateName(memory::Accessor const & _anAccessor)   const
{
    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,this->elementType);

    OSL_ENSURE(pData, "ERROR: No template data found for set");

    return readName(_anAccessor,pData->name);
}
//-----------------------------------------------------------------------------

rtl::OUString SetNode::getElementTemplateModule(memory::Accessor const & _anAccessor) const
{
    SetNodeTemplateData const * pData = readTemplateData(_anAccessor,this->elementType);

    OSL_ENSURE(pData, "ERROR: No template data found for set");

    return readName(_anAccessor,pData->module);
}
//-----------------------------------------------------------------------------

static inline
TreeFragment const * implGetFragmentFromList(memory::Accessor const & _anAccessor, List _aListEntry)
{
    return static_cast<TreeFragment const *>(_anAccessor.access(memory::Pointer(_aListEntry)));
}
//-----------------------------------------------------------------------------

TreeFragment const  * SetNode::getFirstElement(memory::Accessor const & _anAccessor) const
{
    return implGetFragmentFromList(_anAccessor, this->elements);
}
//-----------------------------------------------------------------------------

TreeFragment const  * SetNode::getNextElement(memory::Accessor const & _anAccessor, TreeFragment const * _pElement) const
{
    OSL_PRECOND(_pElement, "getNextElement: previous element must not be NULL");
    OSL_PRECOND(_pElement->header.parent == _anAccessor.address(this).value(),
                "getNextElement: not an element of this node");

    return implGetFragmentFromList(_anAccessor, _pElement->header.next);
}
//-----------------------------------------------------------------------------

bool ValueNode::isEmpty() const
{
    Type::Field const empty_value_type = Type::value_any | Type::nodetype_value;

    return info.type == empty_value_type;
}
//-----------------------------------------------------------------------------

bool ValueNode::isNull() const
{
    Flags::Type availmask = (info.flags & Flags::defaulted) ?
                                Flags::defaultAvailable :
                                Flags::valueAvailable;

    return !(info.flags & availmask);
}
//-----------------------------------------------------------------------------

bool ValueNode::hasUsableDefault() const
{
    return (info.flags & Flags::defaultable) &&
           (info.flags & (Flags::defaultAvailable| Flags::nullable));
}
//-----------------------------------------------------------------------------

uno::Type   ValueNode::getValueType()  const
{
    AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

    return getUnoType(aType);
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getValue(memory::Accessor const & _aAccessor)      const
{
    if (info.flags & Flags::defaulted)
        return getDefaultValue(_aAccessor);

    else
        return getUserValue(_aAccessor);
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getUserValue(memory::Accessor const & _aAccessor)      const
{
    if (info.flags & Flags::valueAvailable)
    {
        AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

        return readData(_aAccessor,aType,this->value);
    }
    else
        return uno::Any();
}
//-----------------------------------------------------------------------------

uno::Any    ValueNode::getDefaultValue(memory::Accessor const & _aAccessor)    const
{
    if (info.flags & Flags::defaultAvailable)
    {
        AnyData::TypeCode aType = AnyData::TypeCode( info.type & Type::mask_valuetype );

        return readData(_aAccessor,aType,this->defaultValue);
    }
    else
        return uno::Any();
}
//-----------------------------------------------------------------------------

bool Node::isNamed(rtl::OUString const & _aName, memory::Accessor const & _aAccessor) const
{
    return 0 == rtl_ustr_compare(_aName.getStr(),accessString(_aAccessor,node.info.name));
}
//-----------------------------------------------------------------------------

rtl::OUString Node::getName(memory::Accessor const & _aAccessor) const
{
    return node.info.getName(_aAccessor);
}
//-----------------------------------------------------------------------------

configmgr::node::Attributes Node::getAttributes() const
{
    if(this->isFragmentRoot())
    {
        return this->getTreeFragment()->getAttributes();
    }
    else
    {
        return node.info.getNodeInfoAttributes();
    }
}
//-----------------------------------------------------------------------------

bool Node::isDefault() const
{
    return node.info.isDefault();
}
//-----------------------------------------------------------------------------

bool Node::isLocalized() const
{
    return node.info.isLocalized();
}
//-----------------------------------------------------------------------------

bool Node::isGroup()  const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_group;
}
//-----------------------------------------------------------------------------

bool Node::isSet()    const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_set;
}
//-----------------------------------------------------------------------------

bool Node::isValue()  const
{
    return (node.info.type & Type::mask_nodetype) == Type::nodetype_value;
}
//-----------------------------------------------------------------------------

GroupNode       * Node::groupData()
{
    return isGroup() ? &this->group : NULL;
}
//-----------------------------------------------------------------------------

GroupNode const * Node::groupData() const
{
    return isGroup() ? &this->group : NULL;
}
//-----------------------------------------------------------------------------

SetNode         * Node::setData()
{
    return isSet() ? &this->set : NULL;
}
//-----------------------------------------------------------------------------
SetNode   const * Node::setData()   const
{
    return isSet() ? &this->set : NULL;
}
//-----------------------------------------------------------------------------
ValueNode       * Node::valueData()
{
    return isValue() ? &this->value : NULL;
}
//-----------------------------------------------------------------------------
ValueNode const * Node::valueData() const
{
    return isValue() ? &this->value : NULL;
}
//-----------------------------------------------------------------------------

bool Node::isFragmentRoot() const
{
    return ! node.info.parent;
}
//-----------------------------------------------------------------------------

Node * Node::getParentNode()
{
    return node.info.parent ? this - node.info.parent : NULL;
}
//-----------------------------------------------------------------------------

Node const * Node::getParentNode() const
{
    return node.info.parent ? this - node.info.parent : NULL;
}
//-----------------------------------------------------------------------------

static Offset getFragmentIndex(Node const * pNode)
{
    Offset result = 0;
    while (Offset step = pNode->node.info.parent)
    {
        result = result + step;
        pNode  -= step;
    }
    return result;
}
//-----------------------------------------------------------------------------

TreeFragment * Node::getTreeFragment()
{
    void * pRoot = this - getFragmentIndex(this);

    void * pFrag = static_cast<char*>(pRoot) - offsetof(TreeFragment,nodes);

    return static_cast<TreeFragment *>(pFrag);
}
//-----------------------------------------------------------------------------

TreeFragment const * Node::getTreeFragment() const
{
    void const * pRoot = this - getFragmentIndex(this);

    void const * pFrag = static_cast<char const*>(pRoot) - offsetof(TreeFragment,nodes);

    return static_cast<TreeFragment const *>(pFrag);
}

//-----------------------------------------------------------------------------
    } // namespace sharable
//-----------------------------------------------------------------------------
} // namespace configmgr

<|endoftext|>


<commit_before>#include "vlc_video_source.h"
#include <cstdio>
#include <cstdlib>
#include <chrono>
#include <thread>

namespace gg
{

VideoSourceVLC::VideoSourceVLC(const std::string path)
    : _vlc_inst(nullptr)
    , _vlc_mp(nullptr)
    , _running(false)
    , _video_buffer(nullptr)
    , _data_length(0)
    , _cols(0)
    , _rows(0)
    , _sub(nullptr)
    , _path(path)
{
    init_vlc();
    run_vlc();
    determine_full();
}

VideoSourceVLC::~VideoSourceVLC()
{
    stop_vlc();
    release_vlc();
    clear();
}

bool VideoSourceVLC::get_frame_dimensions(int & width, int & height)
{
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if(this->_cols == 0 || this->_rows == 0)
        return false;

    width = this->_cols;
    height = this->_rows;
    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame & frame)
{
    if (frame.colour() != ColourSpace::I420)
        return false;

    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_data_length > 0)
    {
        frame.init_from_specs(_video_buffer, _data_length, _cols, _rows);
        return true;
    }
    else
        return false;

    // TODO #86
}

double VideoSourceVLC::get_frame_rate()
{
    return libvlc_media_player_get_fps(_vlc_mp);
}

void VideoSourceVLC::set_sub_frame(int x, int y, int width, int height)
{
    if (x >= _full.x and x + width <= _full.x + _full.width and
        y >= _full.y and y + height <= _full.y + _full.height)
    {
        stop_vlc();
        set_crop(x, y, width, height);
        run_vlc();
    }
}

void VideoSourceVLC::get_full_frame()
{
    stop_vlc();
    reset_crop();
    run_vlc();
}

void VideoSourceVLC::init_vlc()
{
    // VLC global options (valid for the lifetime of the program)
    // see `vlc --help` for the difference between `--option` and `:option`
    const char * const vlc_args[] = {
        "-I", "dummy", // Don't use any interface
        "--ignore-config", // Don't use VLC's config
        "--file-logging",
        //"--verbose=2", // Be much more verbose then normal for debugging purpose
        "--no-audio",
        "--no-video-title-show"
    };

    // We launch VLC
    _vlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if (_vlc_inst == nullptr)
        throw VideoSourceError("Could not create VLC engine");

    // create VLC media player
    _vlc_mp = libvlc_media_player_new(_vlc_inst);
    if (_vlc_mp == nullptr)
        throw VideoSourceError("Could not create VLC media player");
}

void VideoSourceVLC::run_vlc()
{
    libvlc_media_t * vlc_media = nullptr;

    // If path contains a colon (:), it will be treated as a
    // URL. Else, it will be considered as a local path.
    if (_path.find(":") == std::string::npos)
        vlc_media = libvlc_media_new_path(_vlc_inst, _path.c_str());
    else
        vlc_media = libvlc_media_new_location(_vlc_inst, _path.c_str());
    if (vlc_media == nullptr)
        throw VideoSourceError(std::string("Could not open ").append(_path));

    // compose the processing pipeline description
    char pipeline[512];
    // open transcode step
    sprintf(pipeline, "#transcode{");
    // cropping sub-frame
    unsigned int croptop = 0, cropbottom = 0, cropleft = 0, cropright = 0;
    if (_sub != nullptr) // cropping video?
    {
        croptop = std::max(_sub->y, croptop),
        cropbottom = std::max(_full.height - (_sub->y + _sub->height), cropbottom),
        cropleft = std::max(_sub->x, cropleft),
        cropright = std::max(_full.width - (_sub->x + _sub->width), cropright);
    }
    sprintf(pipeline, "%svfilter=croppadd{", pipeline);
    sprintf(pipeline, "%scroptop=%u,", pipeline, croptop);
    sprintf(pipeline, "%scropbottom=%u,", pipeline, cropbottom);
    sprintf(pipeline, "%scropleft=%u,", pipeline, cropleft);
    sprintf(pipeline, "%scropright=%u", pipeline, cropright);
    sprintf(pipeline, "%s}", pipeline);
    // colour space specification
    sprintf(pipeline, "%s,vcodec=I420", pipeline);
    // close transcode step
    sprintf(pipeline, "%s}:", pipeline);
    // callbacks
    sprintf(pipeline,
            "%ssmem{video-data=%lld,video-prerender-callback=%lld,video-postrender-callback=%lld}",
            pipeline,
            (long long int)(intptr_t)(void*) this,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::prepareRender,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::handleStream );
    // activate pipeline in VLC media
    char sout_options[1024];
    sprintf(sout_options, ":sout=%s", pipeline);
    libvlc_media_add_option(vlc_media, sout_options);
    // set VLC media player's media
    libvlc_media_player_set_media(_vlc_mp, vlc_media);
    // release VLC media
    libvlc_media_release(vlc_media);

    { // artificial scope for the mutex guard below
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        // play the media_player
        if (libvlc_media_player_play(_vlc_mp) != 0)
            throw VideoSourceError("Could not start VLC media player");
        _running = true;
    }

    // empirically determined value that allows for initialisation
    // to succeed before any API functions are called on this object
    std::this_thread::sleep_for(std::chrono::milliseconds(350));
}

void VideoSourceVLC::stop_vlc()
{
    // stop playing
    libvlc_media_player_stop(_vlc_mp);

    { // artificial scope for mutex guard below
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }
}

void VideoSourceVLC::release_vlc()
{
    // free media player
    libvlc_media_player_release(_vlc_mp);

    // free engine
    libvlc_release(_vlc_inst);
}

void VideoSourceVLC::clear()
{
    // free buffer
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_video_buffer != nullptr)
    {
        delete[] _video_buffer;
        _video_buffer = nullptr;
    }
    _data_length = 0;
    _cols = 0;
    _rows = 0;

    reset_crop();
}

void VideoSourceVLC::determine_full()
{
    unsigned int width, height;
    if (libvlc_video_get_size(_vlc_mp, 0, &width, &height) != 0)
        throw VideoSourceError("Could not get video dimensions");

    _full.x = 0;
    _full.y = 0;
    _full.width = width;
    _full.height = height;
}

void VideoSourceVLC::set_crop(unsigned int x, unsigned int y,
                              unsigned int width, unsigned int height)
{
    if (_sub == nullptr) _sub = new FrameBox;
    _sub->x = x;
    _sub->y = y;
    _sub->width = width;
    _sub->height = height;
}

void VideoSourceVLC::reset_crop()
{
    // free sub-frame
    if (_sub != nullptr)
    {
        delete _sub;
        _sub = nullptr;
    }
}

void VideoSourceVLC::prepareRender(VideoSourceVLC * p_video_data,
                                   uint8_t ** pp_pixel_buffer,
                                   size_t size)
{
    std::lock_guard<std::mutex> data_lock_guard(p_video_data->_data_lock);
    if (p_video_data->_running)
    {
        if (size > p_video_data->_data_length)
        {
            if (p_video_data->_data_length == 0)
                p_video_data->_video_buffer = reinterpret_cast<uint8_t *>(
                            malloc(size * sizeof(uint8_t))
                            );
            else
                p_video_data->_video_buffer = reinterpret_cast<uint8_t *>(
                            realloc(p_video_data->_video_buffer, size * sizeof(uint8_t))
                            );
        }
        p_video_data->_data_length = size;

        *pp_pixel_buffer = p_video_data->_video_buffer;
    }
}

void VideoSourceVLC::handleStream(VideoSourceVLC * p_video_data,
                                  uint8_t * p_pixel_buffer,
                                  size_t cols,
                                  size_t rows,
                                  size_t colour_depth,
                                  size_t size)
{
    std::lock_guard<std::mutex> data_lock_guard(p_video_data->_data_lock);
    // TODO: explain how data should be handled (see #86)
    if (p_video_data->_running)
    {
        p_video_data->_cols = cols;
        p_video_data->_rows = rows;
    }
}

}
<commit_msg>Issue #101: fixed determine_full: using get_frame_dimensions inside it<commit_after>#include "vlc_video_source.h"
#include <cstdio>
#include <cstdlib>
#include <chrono>
#include <thread>

namespace gg
{

VideoSourceVLC::VideoSourceVLC(const std::string path)
    : _vlc_inst(nullptr)
    , _vlc_mp(nullptr)
    , _running(false)
    , _video_buffer(nullptr)
    , _data_length(0)
    , _cols(0)
    , _rows(0)
    , _sub(nullptr)
    , _path(path)
{
    init_vlc();
    run_vlc();
    determine_full();
}

VideoSourceVLC::~VideoSourceVLC()
{
    stop_vlc();
    release_vlc();
    clear();
}

bool VideoSourceVLC::get_frame_dimensions(int & width, int & height)
{
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if(this->_cols == 0 || this->_rows == 0)
        return false;

    width = this->_cols;
    height = this->_rows;
    return true;
}

bool VideoSourceVLC::get_frame(VideoFrame & frame)
{
    if (frame.colour() != ColourSpace::I420)
        return false;

    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_data_length > 0)
    {
        frame.init_from_specs(_video_buffer, _data_length, _cols, _rows);
        return true;
    }
    else
        return false;

    // TODO #86
}

double VideoSourceVLC::get_frame_rate()
{
    return libvlc_media_player_get_fps(_vlc_mp);
}

void VideoSourceVLC::set_sub_frame(int x, int y, int width, int height)
{
    if (x >= _full.x and x + width <= _full.x + _full.width and
        y >= _full.y and y + height <= _full.y + _full.height)
    {
        stop_vlc();
        set_crop(x, y, width, height);
        run_vlc();
    }
}

void VideoSourceVLC::get_full_frame()
{
    stop_vlc();
    reset_crop();
    run_vlc();
}

void VideoSourceVLC::init_vlc()
{
    // VLC global options (valid for the lifetime of the program)
    // see `vlc --help` for the difference between `--option` and `:option`
    const char * const vlc_args[] = {
        "-I", "dummy", // Don't use any interface
        "--ignore-config", // Don't use VLC's config
        "--file-logging",
        //"--verbose=2", // Be much more verbose then normal for debugging purpose
        "--no-audio",
        "--no-video-title-show"
    };

    // We launch VLC
    _vlc_inst = libvlc_new(sizeof(vlc_args) / sizeof(vlc_args[0]), vlc_args);
    if (_vlc_inst == nullptr)
        throw VideoSourceError("Could not create VLC engine");

    // create VLC media player
    _vlc_mp = libvlc_media_player_new(_vlc_inst);
    if (_vlc_mp == nullptr)
        throw VideoSourceError("Could not create VLC media player");
}

void VideoSourceVLC::run_vlc()
{
    libvlc_media_t * vlc_media = nullptr;

    // If path contains a colon (:), it will be treated as a
    // URL. Else, it will be considered as a local path.
    if (_path.find(":") == std::string::npos)
        vlc_media = libvlc_media_new_path(_vlc_inst, _path.c_str());
    else
        vlc_media = libvlc_media_new_location(_vlc_inst, _path.c_str());
    if (vlc_media == nullptr)
        throw VideoSourceError(std::string("Could not open ").append(_path));

    // compose the processing pipeline description
    char pipeline[512];
    // open transcode step
    sprintf(pipeline, "#transcode{");
    // cropping sub-frame
    unsigned int croptop = 0, cropbottom = 0, cropleft = 0, cropright = 0;
    if (_sub != nullptr) // cropping video?
    {
        croptop = std::max(_sub->y, croptop),
        cropbottom = std::max(_full.height - (_sub->y + _sub->height), cropbottom),
        cropleft = std::max(_sub->x, cropleft),
        cropright = std::max(_full.width - (_sub->x + _sub->width), cropright);
    }
    sprintf(pipeline, "%svfilter=croppadd{", pipeline);
    sprintf(pipeline, "%scroptop=%u,", pipeline, croptop);
    sprintf(pipeline, "%scropbottom=%u,", pipeline, cropbottom);
    sprintf(pipeline, "%scropleft=%u,", pipeline, cropleft);
    sprintf(pipeline, "%scropright=%u", pipeline, cropright);
    sprintf(pipeline, "%s}", pipeline);
    // colour space specification
    sprintf(pipeline, "%s,vcodec=I420", pipeline);
    // close transcode step
    sprintf(pipeline, "%s}:", pipeline);
    // callbacks
    sprintf(pipeline,
            "%ssmem{video-data=%lld,video-prerender-callback=%lld,video-postrender-callback=%lld}",
            pipeline,
            (long long int)(intptr_t)(void*) this,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::prepareRender,
            (long long int)(intptr_t)(void*) &VideoSourceVLC::handleStream );
    // activate pipeline in VLC media
    char sout_options[1024];
    sprintf(sout_options, ":sout=%s", pipeline);
    libvlc_media_add_option(vlc_media, sout_options);
    // set VLC media player's media
    libvlc_media_player_set_media(_vlc_mp, vlc_media);
    // release VLC media
    libvlc_media_release(vlc_media);

    { // artificial scope for the mutex guard below
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        // play the media_player
        if (libvlc_media_player_play(_vlc_mp) != 0)
            throw VideoSourceError("Could not start VLC media player");
        _running = true;
    }

    // empirically determined value that allows for initialisation
    // to succeed before any API functions are called on this object
    std::this_thread::sleep_for(std::chrono::milliseconds(350));
}

void VideoSourceVLC::stop_vlc()
{
    // stop playing
    libvlc_media_player_stop(_vlc_mp);

    { // artificial scope for mutex guard below
        std::lock_guard<std::mutex> data_lock_guard(_data_lock);
        _running = false;
    }
}

void VideoSourceVLC::release_vlc()
{
    // free media player
    libvlc_media_player_release(_vlc_mp);

    // free engine
    libvlc_release(_vlc_inst);
}

void VideoSourceVLC::clear()
{
    // free buffer
    std::lock_guard<std::mutex> data_lock_guard(_data_lock);
    if (_video_buffer != nullptr)
    {
        delete[] _video_buffer;
        _video_buffer = nullptr;
    }
    _data_length = 0;
    _cols = 0;
    _rows = 0;

    reset_crop();
}

void VideoSourceVLC::determine_full()
{
    get_frame_dimensions((int&)_full.width, (int&)_full.height);
    _full.x = 0;
    _full.y = 0;
}

void VideoSourceVLC::set_crop(unsigned int x, unsigned int y,
                              unsigned int width, unsigned int height)
{
    if (_sub == nullptr) _sub = new FrameBox;
    _sub->x = x;
    _sub->y = y;
    _sub->width = width;
    _sub->height = height;
}

void VideoSourceVLC::reset_crop()
{
    // free sub-frame
    if (_sub != nullptr)
    {
        delete _sub;
        _sub = nullptr;
    }
}

void VideoSourceVLC::prepareRender(VideoSourceVLC * p_video_data,
                                   uint8_t ** pp_pixel_buffer,
                                   size_t size)
{
    std::lock_guard<std::mutex> data_lock_guard(p_video_data->_data_lock);
    if (p_video_data->_running)
    {
        if (size > p_video_data->_data_length)
        {
            if (p_video_data->_data_length == 0)
                p_video_data->_video_buffer = reinterpret_cast<uint8_t *>(
                            malloc(size * sizeof(uint8_t))
                            );
            else
                p_video_data->_video_buffer = reinterpret_cast<uint8_t *>(
                            realloc(p_video_data->_video_buffer, size * sizeof(uint8_t))
                            );
        }
        p_video_data->_data_length = size;

        *pp_pixel_buffer = p_video_data->_video_buffer;
    }
}

void VideoSourceVLC::handleStream(VideoSourceVLC * p_video_data,
                                  uint8_t * p_pixel_buffer,
                                  size_t cols,
                                  size_t rows,
                                  size_t colour_depth,
                                  size_t size)
{
    std::lock_guard<std::mutex> data_lock_guard(p_video_data->_data_lock);
    // TODO: explain how data should be handled (see #86)
    if (p_video_data->_running)
    {
        p_video_data->_cols = cols;
        p_video_data->_rows = rows;
    }
}

}
<|endoftext|>