<commit_before>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QIMESSAGING_DETAILS_GENERICVALUE_HXX_
#define _QIMESSAGING_DETAILS_GENERICVALUE_HXX_

#include <boost/type_traits/remove_const.hpp>

#include <qimessaging/typeint.hpp>

namespace qi {

class ValueClone
{
public:
  void* clone(void* src)
  {
    return new GenericValue(((GenericValue*)src)->clone());
  }

  void destroy(void* ptr)
  {
    ((GenericValue*)ptr)->destroy();
    delete (GenericValue*)ptr;
  }
};

class ValueValue
{
public:

  bool toValue(const void* ptr, qi::detail::DynamicValue& val)
  {
    GenericValue* m = (GenericValue*)ptr;
    return m->type->toValue(m->value, val);
  }
  void* fromValue(const qi::detail::DynamicValue& val)
  {
    GenericValue v = ::qi::toValue(val);
    return new GenericValue(v.clone());
  }
};

template<> class TypeImpl<GenericValue>:
  public DefaultTypeImpl<
    GenericValue,
    TypeDefaultAccess<GenericValue>,
    ValueClone,
    ValueValue,
    TypeDefaultSerialize<TypeDefaultAccess<GenericValue> >
    > {};

namespace detail
{
  template<typename T> struct TypeCopy
  {
    void operator()(T &dst, const T &src)
    {
      dst = src;
    }
  };

  template<int I> struct TypeCopy<char[I] >
  {
    void operator() (char* dst, const char* src)
    {
      memcpy(dst, src, I);
    }
  };
}

template<typename T>
GenericValue toValue(const T& v)
{
  GenericValue res;
  res.type = typeOf<typename boost::remove_const<T>::type>();
  res.value = res.type->initializeStorage(const_cast<void*>((const void*)&v));
  return res;
}

inline
GenericValue GenericValue::clone() const
{
  GenericValue res;
  res.type = type;
  res.value = type?res.type->clone(value):0;
  return res;
}

template<typename T>
std::pair<const T*, bool> GenericValue::to() const
{
  Type* targetType =  typeOf<T>();
  if (type->info() == targetType->info())
    return std::make_pair((const T*)type->ptrFromStorage((void**)&value), false);
  else
  {
    std::pair<GenericValue, bool> mv = convert(targetType);
    // NOTE: delete theResult.first will not do, destroy must be called,
    return std::make_pair((const T*)mv.first.type->ptrFromStorage(&mv.first.value), mv.second);
  }
}

inline AutoGenericValue::AutoGenericValue(const AutoGenericValue& b)
{
  value = b.value;
  type = b.type;
}

template<typename T> AutoGenericValue::AutoGenericValue(const T& ptr)
{
  *(GenericValue*)this = toValue(ptr);
}

inline AutoGenericValue::AutoGenericValue()
{
  value = type = 0;
}

inline std::string GenericValue::signature() const
{
  if (!type)
      return "";
    else
      return type->signature();
}

inline void GenericValue::destroy()
{
  if (type && value)
    type->destroy(value);
}

inline void GenericValue::serialize(ODataStream& os) const
{
  if (type)
    type->serialize(os, value);
}

inline GenericValue::GenericValue()
: value(0)
, type(0)
{
}

inline Type::Kind GenericValue::kind() const
{
 if (!type)
   return Type::Void;
 else
   return type->kind();
}

// Kind -> handler Type (TypeInt, TypeList...)  accessor

class KindNotConvertible;

template<Type::Kind T> struct TypeOfKind
{
  typedef KindNotConvertible type;
};

#define TYPE_OF_KIND(k, t) template<> struct TypeOfKind<k> { typedef t type;}

TYPE_OF_KIND(Type::Int, TypeInt);
TYPE_OF_KIND(Type::Float,  TypeFloat);
//TYPE_OF_KIND(Type::String, TypeString);

#undef TYPE_OF_KIND

// Type -> Kind  accessor

namespace detail
{
  struct Nothing {};
  template<Type::Kind k> struct MakeKind
  {
    static const Type::Kind value = k;
  };

  template<typename C, typename T, typename F> struct IfElse
  {
  };

  template<typename T, typename F> struct IfElse<boost::true_type, T, F>
  {
    typedef T type;
  };

  template<typename T, typename F> struct IfElse<boost::false_type, T, F>
  {
    typedef F type;
  };

}

#define IF(cond, type) \
public detail::IfElse<cond, typename detail::MakeKind<type>, detail::Nothing>::type

template<typename T> struct KindOfType
: IF(typename boost::is_integral<T>::type, Type::Int)
, IF(typename boost::is_floating_point<T>::type, Type::Float)
{
};

#undef IF

template<typename T, Type::Kind k>
inline T GenericValue::as() const
{
  if (kind() != k)
  {
    qiLogWarning("qi.GenericValue") << "as: type " << kind() <<" not convertible to kind " << k;
    return T();
  }
  return dynamic_cast<const typename TypeOfKind<k>::type*>(type)->get(value);
}

template<typename T>
inline T GenericValue::as() const
{
  return as<T, KindOfType<T>::value>();
}

inline int64_t GenericValue::asInt() const
{
  return as<int64_t, Type::Int>();
}

inline float GenericValue::asFloat() const
{
  return as<float, Type::Float>();
}

inline double GenericValue::asDouble() const
{
  return as<double, Type::Float>();
}

inline std::string GenericValue::asString() const
{
  return ""; // as<std::string, Type::String>();
}

namespace detail
{
  /** This class can be used to convert the return value of an arbitrary function
  * into a GenericValue. It handles functions returning void.
  *
  *  Usage:
  *    ValueCopy val;
  *    val(), functionCall(arg);
  *
  *  in val(), parenthesis are useful to avoid compiler warning "val not used" when handling void.
  */
  class GenericValueCopy: public GenericValue
  {
  public:
    template<typename T> void operator,(const T& any);
    GenericValueCopy &operator()() { return *this; }
  };

  template<typename T> void GenericValueCopy::operator,(const T& any)
  {
    *(GenericValue*)this = toValue(any);
    *(GenericValue*)this = clone();
  }
}

}

#endif  // _QIMESSAGING_DETAILS_GENERICVALUE_HXX_
<commit_msg>toValue(T): Cache Type* of T.<commit_after>#pragma once
/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/

#ifndef _QIMESSAGING_DETAILS_GENERICVALUE_HXX_
#define _QIMESSAGING_DETAILS_GENERICVALUE_HXX_

#include <boost/type_traits/remove_const.hpp>

#include <qimessaging/typeint.hpp>

namespace qi {

class ValueClone
{
public:
  void* clone(void* src)
  {
    return new GenericValue(((GenericValue*)src)->clone());
  }

  void destroy(void* ptr)
  {
    ((GenericValue*)ptr)->destroy();
    delete (GenericValue*)ptr;
  }
};

class ValueValue
{
public:

  bool toValue(const void* ptr, qi::detail::DynamicValue& val)
  {
    GenericValue* m = (GenericValue*)ptr;
    return m->type->toValue(m->value, val);
  }
  void* fromValue(const qi::detail::DynamicValue& val)
  {
    GenericValue v = ::qi::toValue(val);
    return new GenericValue(v.clone());
  }
};

template<> class TypeImpl<GenericValue>:
  public DefaultTypeImpl<
    GenericValue,
    TypeDefaultAccess<GenericValue>,
    ValueClone,
    ValueValue,
    TypeDefaultSerialize<TypeDefaultAccess<GenericValue> >
    > {};

namespace detail
{
  template<typename T> struct TypeCopy
  {
    void operator()(T &dst, const T &src)
    {
      dst = src;
    }
  };

  template<int I> struct TypeCopy<char[I] >
  {
    void operator() (char* dst, const char* src)
    {
      memcpy(dst, src, I);
    }
  };
}

template<typename T>
GenericValue toValue(const T& v)
{
  static Type* type = 0;
  if (!type)
    type = typeOf<typename boost::remove_const<T>::type>();
  GenericValue res;
  res.type = type;
  res.value = res.type->initializeStorage(const_cast<void*>((const void*)&v));
  return res;
}

inline
GenericValue GenericValue::clone() const
{
  GenericValue res;
  res.type = type;
  res.value = type?res.type->clone(value):0;
  return res;
}

template<typename T>
std::pair<const T*, bool> GenericValue::to() const
{
  Type* targetType =  typeOf<T>();
  if (type->info() == targetType->info())
    return std::make_pair((const T*)type->ptrFromStorage((void**)&value), false);
  else
  {
    std::pair<GenericValue, bool> mv = convert(targetType);
    // NOTE: delete theResult.first will not do, destroy must be called,
    return std::make_pair((const T*)mv.first.type->ptrFromStorage(&mv.first.value), mv.second);
  }
}

inline AutoGenericValue::AutoGenericValue(const AutoGenericValue& b)
{
  value = b.value;
  type = b.type;
}

template<typename T> AutoGenericValue::AutoGenericValue(const T& ptr)
{
  *(GenericValue*)this = toValue(ptr);
}

inline AutoGenericValue::AutoGenericValue()
{
  value = type = 0;
}

inline std::string GenericValue::signature() const
{
  if (!type)
      return "";
    else
      return type->signature();
}

inline void GenericValue::destroy()
{
  if (type && value)
    type->destroy(value);
}

inline void GenericValue::serialize(ODataStream& os) const
{
  if (type)
    type->serialize(os, value);
}

inline GenericValue::GenericValue()
: value(0)
, type(0)
{
}

inline Type::Kind GenericValue::kind() const
{
 if (!type)
   return Type::Void;
 else
   return type->kind();
}

// Kind -> handler Type (TypeInt, TypeList...)  accessor

class KindNotConvertible;

template<Type::Kind T> struct TypeOfKind
{
  typedef KindNotConvertible type;
};

#define TYPE_OF_KIND(k, t) template<> struct TypeOfKind<k> { typedef t type;}

TYPE_OF_KIND(Type::Int, TypeInt);
TYPE_OF_KIND(Type::Float,  TypeFloat);
//TYPE_OF_KIND(Type::String, TypeString);

#undef TYPE_OF_KIND

// Type -> Kind  accessor

namespace detail
{
  struct Nothing {};
  template<Type::Kind k> struct MakeKind
  {
    static const Type::Kind value = k;
  };

  template<typename C, typename T, typename F> struct IfElse
  {
  };

  template<typename T, typename F> struct IfElse<boost::true_type, T, F>
  {
    typedef T type;
  };

  template<typename T, typename F> struct IfElse<boost::false_type, T, F>
  {
    typedef F type;
  };

}

#define IF(cond, type) \
public detail::IfElse<cond, typename detail::MakeKind<type>, detail::Nothing>::type

template<typename T> struct KindOfType
: IF(typename boost::is_integral<T>::type, Type::Int)
, IF(typename boost::is_floating_point<T>::type, Type::Float)
{
};

#undef IF

template<typename T, Type::Kind k>
inline T GenericValue::as() const
{
  if (kind() != k)
  {
    qiLogWarning("qi.GenericValue") << "as: type " << kind() <<" not convertible to kind " << k;
    return T();
  }
  return dynamic_cast<const typename TypeOfKind<k>::type*>(type)->get(value);
}

template<typename T>
inline T GenericValue::as() const
{
  return as<T, KindOfType<T>::value>();
}

inline int64_t GenericValue::asInt() const
{
  return as<int64_t, Type::Int>();
}

inline float GenericValue::asFloat() const
{
  return as<float, Type::Float>();
}

inline double GenericValue::asDouble() const
{
  return as<double, Type::Float>();
}

inline std::string GenericValue::asString() const
{
  return ""; // as<std::string, Type::String>();
}

namespace detail
{
  /** This class can be used to convert the return value of an arbitrary function
  * into a GenericValue. It handles functions returning void.
  *
  *  Usage:
  *    ValueCopy val;
  *    val(), functionCall(arg);
  *
  *  in val(), parenthesis are useful to avoid compiler warning "val not used" when handling void.
  */
  class GenericValueCopy: public GenericValue
  {
  public:
    template<typename T> void operator,(const T& any);
    GenericValueCopy &operator()() { return *this; }
  };

  template<typename T> void GenericValueCopy::operator,(const T& any)
  {
    *(GenericValue*)this = toValue(any);
    *(GenericValue*)this = clone();
  }
}

}

#endif  // _QIMESSAGING_DETAILS_GENERICVALUE_HXX_
<|endoftext|>


<commit_before>#pragma once

#include <eosio/chain/action.hpp>
#include <numeric>

namespace eosio { namespace chain {

   struct deferred_transaction_generation_context : fc::reflect_init {
      static constexpr uint16_t extension_id() { return 0; }
      static constexpr bool     enforce_unique() { return true; }

      deferred_transaction_generation_context() = default;

      deferred_transaction_generation_context( const transaction_id_type& sender_trx_id, uint128_t sender_id, account_name sender )
      :sender_trx_id( sender_trx_id )
      ,sender_id( sender_id )
      ,sender( sender )
      {}

      void reflector_init();

      transaction_id_type sender_trx_id;
      uint128_t           sender_id;
      account_name        sender;
   };

   namespace detail {
      template<typename... Ts>
      struct transaction_extension_types {
         using transaction_extension_t = fc::static_variant< Ts... >;
         using decompose_t = decompose< Ts... >;
      };
   }

   using transaction_extension_types = detail::transaction_extension_types<
      deferred_transaction_generation_context
   >;

   using transaction_extension = transaction_extension_types::transaction_extension_t;

   /**
    *  The transaction header contains the fixed-sized data
    *  associated with each transaction. It is separated from
    *  the transaction body to facilitate partial parsing of
    *  transactions without requiring dynamic memory allocation.
    *
    *  All transactions have an expiration time after which they
    *  may no longer be included in the blockchain. Once a block
    *  with a block_header::timestamp greater than expiration is
    *  deemed irreversible, then a user can safely trust the transaction
    *  will never be included.
    *

    *  Each region is an independent blockchain, it is included as routing
    *  information for inter-blockchain communication. A contract in this
    *  region might generate or authorize a transaction intended for a foreign
    *  region.
    */
   struct transaction_header {
      time_point_sec         expiration;   ///< the time at which a transaction expires
      uint16_t               ref_block_num       = 0U; ///< specifies a block num in the last 2^16 blocks.
      uint32_t               ref_block_prefix    = 0UL; ///< specifies the lower 32 bits of the blockid at get_ref_blocknum
      fc::unsigned_int       max_net_usage_words = 0UL; /// upper limit on total network bandwidth (in 8 byte words) billed for this transaction
      uint8_t                max_cpu_usage_ms    = 0; /// upper limit on the total CPU time billed for this transaction
      fc::unsigned_int       delay_sec           = 0UL; /// number of seconds to delay this transaction for during which it may be canceled.

      /**
       * @return the absolute block number given the relative ref_block_num
       */
      block_num_type get_ref_blocknum( block_num_type head_blocknum )const {
         return ((head_blocknum/0xffff)*0xffff) + head_blocknum%0xffff;
      }
      void set_reference_block( const block_id_type& reference_block );
      bool verify_reference_block( const block_id_type& reference_block )const;
      void validate()const;
   };

   /**
    *  A transaction consits of a set of messages which must all be applied or
    *  all are rejected. These messages have access to data within the given
    *  read and write scopes.
    */
   struct transaction : public transaction_header {
      vector<action>         context_free_actions;
      vector<action>         actions;
      extensions_type        transaction_extensions;

      transaction_id_type        id()const;
      digest_type                sig_digest( const chain_id_type& chain_id, const vector<bytes>& cfd = vector<bytes>() )const;
      fc::microseconds           get_signature_keys( const vector<signature_type>& signatures,
                                                     const chain_id_type& chain_id,
                                                     fc::time_point deadline,
                                                     const vector<bytes>& cfd,
                                                     flat_set<public_key_type>& recovered_pub_keys,
                                                     bool allow_duplicate_keys = false) const;

      uint32_t total_actions()const { return context_free_actions.size() + actions.size(); }

      account_name first_authorizer()const {
         for( const auto& a : actions ) {
            for( const auto& u : a.authorization )
               return u.actor;
         }
         return account_name();
      }

      flat_multimap<uint16_t, transaction_extension> validate_and_extract_extensions()const;
   };

   struct signed_transaction : public transaction
   {
      signed_transaction() = default;
//      signed_transaction( const signed_transaction& ) = default;
//      signed_transaction( signed_transaction&& ) = default;
      signed_transaction( transaction&& trx, const vector<signature_type>& signatures, const vector<bytes>& context_free_data)
      : transaction(std::move(trx))
      , signatures(signatures)
      , context_free_data(context_free_data)
      {}
      signed_transaction( transaction&& trx, const vector<signature_type>& signatures, vector<bytes>&& context_free_data)
      : transaction(std::move(trx))
      , signatures(signatures)
      , context_free_data(std::move(context_free_data))
      {}

      vector<signature_type>    signatures;
      vector<bytes>             context_free_data; ///< for each context-free action, there is an entry here

      const signature_type&     sign(const private_key_type& key, const chain_id_type& chain_id);
      signature_type            sign(const private_key_type& key, const chain_id_type& chain_id)const;
      fc::microseconds          get_signature_keys( const chain_id_type& chain_id, fc::time_point deadline,
                                                    flat_set<public_key_type>& recovered_pub_keys,
                                                    bool allow_duplicate_keys = false )const;
   };

   struct packed_transaction : fc::reflect_init {
      enum class compression_type {
         none = 0,
         zlib = 1
      };

      packed_transaction() = default;
      packed_transaction(packed_transaction&&) = default;
      explicit packed_transaction(const packed_transaction&) = default;
      packed_transaction& operator=(const packed_transaction&) = delete;
      packed_transaction& operator=(packed_transaction&&) = default;

      explicit packed_transaction(const signed_transaction& t, compression_type _compression = compression_type::none)
      :signatures(t.signatures), compression(_compression), unpacked_trx(t), trx_id(unpacked_trx.id())
      {
         local_pack_transaction();
         local_pack_context_free_data();
      }

      explicit packed_transaction(signed_transaction&& t, compression_type _compression = compression_type::none)
      :signatures(t.signatures), compression(_compression), unpacked_trx(std::move(t)), trx_id(unpacked_trx.id())
      {
         local_pack_transaction();
         local_pack_context_free_data();
      }

      // used by abi_serializer
      packed_transaction( bytes&& packed_txn, vector<signature_type>&& sigs, bytes&& packed_cfd, compression_type _compression );
      packed_transaction( bytes&& packed_txn, vector<signature_type>&& sigs, vector<bytes>&& cfd, compression_type _compression );
      packed_transaction( transaction&& t, vector<signature_type>&& sigs, bytes&& packed_cfd, compression_type _compression );

      uint32_t get_unprunable_size()const;
      uint32_t get_prunable_size()const;

      digest_type packed_digest()const;

      const transaction_id_type& id()const { return trx_id; }
      bytes               get_raw_transaction()const;

      time_point_sec                expiration()const { return unpacked_trx.expiration; }
      const vector<bytes>&          get_context_free_data()const { return unpacked_trx.context_free_data; }
      const transaction&            get_transaction()const { return unpacked_trx; }
      const signed_transaction&     get_signed_transaction()const { return unpacked_trx; }
      const vector<signature_type>& get_signatures()const { return signatures; }
      const fc::enum_type<uint8_t,compression_type>& get_compression()const { return compression; }
      const bytes&                  get_packed_context_free_data()const { return packed_context_free_data; }
      const bytes&                  get_packed_transaction()const { return packed_trx; }

   private:
      void local_unpack_transaction(vector<bytes>&& context_free_data);
      void local_unpack_context_free_data();
      void local_pack_transaction();
      void local_pack_context_free_data();

      friend struct fc::reflector<packed_transaction>;
      friend struct fc::reflector_init_visitor<packed_transaction>;
      friend struct fc::has_reflector_init<packed_transaction>;
      void reflector_init();
   private:
      vector<signature_type>                  signatures;
      fc::enum_type<uint8_t,compression_type> compression;
      bytes                                   packed_context_free_data;
      bytes                                   packed_trx;

   private:
      // cache unpacked trx, for thread safety do not modify after construction
      signed_transaction                      unpacked_trx;
      transaction_id_type                     trx_id;
   };

   using packed_transaction_ptr = std::shared_ptr<packed_transaction>;

   uint128_t transaction_id_to_sender_id( const transaction_id_type& tid );

} } /// namespace eosio::chain

FC_REFLECT(eosio::chain::deferred_transaction_generation_context, (sender_trx_id)(sender_id)(sender) )
FC_REFLECT( eosio::chain::transaction_header, (expiration)(ref_block_num)(ref_block_prefix)
                                              (max_net_usage_words)(max_cpu_usage_ms)(delay_sec) )
FC_REFLECT_DERIVED( eosio::chain::transaction, (eosio::chain::transaction_header), (context_free_actions)(actions)(transaction_extensions) )
FC_REFLECT_DERIVED( eosio::chain::signed_transaction, (eosio::chain::transaction), (signatures)(context_free_data) )
FC_REFLECT_ENUM( eosio::chain::packed_transaction::compression_type, (none)(zlib))

// @ignore unpacked_trx trx_id
FC_REFLECT( eosio::chain::packed_transaction, (signatures)(compression)(packed_context_free_data)(packed_trx) )
<commit_msg>took out ignore<commit_after>#pragma once

#include <eosio/chain/action.hpp>
#include <numeric>

namespace eosio { namespace chain {

   struct deferred_transaction_generation_context : fc::reflect_init {
      static constexpr uint16_t extension_id() { return 0; }
      static constexpr bool     enforce_unique() { return true; }

      deferred_transaction_generation_context() = default;

      deferred_transaction_generation_context( const transaction_id_type& sender_trx_id, uint128_t sender_id, account_name sender )
      :sender_trx_id( sender_trx_id )
      ,sender_id( sender_id )
      ,sender( sender )
      {}

      void reflector_init();

      transaction_id_type sender_trx_id;
      uint128_t           sender_id;
      account_name        sender;
   };

   namespace detail {
      template<typename... Ts>
      struct transaction_extension_types {
         using transaction_extension_t = fc::static_variant< Ts... >;
         using decompose_t = decompose< Ts... >;
      };
   }

   using transaction_extension_types = detail::transaction_extension_types<
      deferred_transaction_generation_context
   >;

   using transaction_extension = transaction_extension_types::transaction_extension_t;

   /**
    *  The transaction header contains the fixed-sized data
    *  associated with each transaction. It is separated from
    *  the transaction body to facilitate partial parsing of
    *  transactions without requiring dynamic memory allocation.
    *
    *  All transactions have an expiration time after which they
    *  may no longer be included in the blockchain. Once a block
    *  with a block_header::timestamp greater than expiration is
    *  deemed irreversible, then a user can safely trust the transaction
    *  will never be included.
    *

    *  Each region is an independent blockchain, it is included as routing
    *  information for inter-blockchain communication. A contract in this
    *  region might generate or authorize a transaction intended for a foreign
    *  region.
    */
   struct transaction_header {
      time_point_sec         expiration;   ///< the time at which a transaction expires
      uint16_t               ref_block_num       = 0U; ///< specifies a block num in the last 2^16 blocks.
      uint32_t               ref_block_prefix    = 0UL; ///< specifies the lower 32 bits of the blockid at get_ref_blocknum
      fc::unsigned_int       max_net_usage_words = 0UL; /// upper limit on total network bandwidth (in 8 byte words) billed for this transaction
      uint8_t                max_cpu_usage_ms    = 0; /// upper limit on the total CPU time billed for this transaction
      fc::unsigned_int       delay_sec           = 0UL; /// number of seconds to delay this transaction for during which it may be canceled.

      /**
       * @return the absolute block number given the relative ref_block_num
       */
      block_num_type get_ref_blocknum( block_num_type head_blocknum )const {
         return ((head_blocknum/0xffff)*0xffff) + head_blocknum%0xffff;
      }
      void set_reference_block( const block_id_type& reference_block );
      bool verify_reference_block( const block_id_type& reference_block )const;
      void validate()const;
   };

   /**
    *  A transaction consits of a set of messages which must all be applied or
    *  all are rejected. These messages have access to data within the given
    *  read and write scopes.
    */
   struct transaction : public transaction_header {
      vector<action>         context_free_actions;
      vector<action>         actions;
      extensions_type        transaction_extensions;

      transaction_id_type        id()const;
      digest_type                sig_digest( const chain_id_type& chain_id, const vector<bytes>& cfd = vector<bytes>() )const;
      fc::microseconds           get_signature_keys( const vector<signature_type>& signatures,
                                                     const chain_id_type& chain_id,
                                                     fc::time_point deadline,
                                                     const vector<bytes>& cfd,
                                                     flat_set<public_key_type>& recovered_pub_keys,
                                                     bool allow_duplicate_keys = false) const;

      uint32_t total_actions()const { return context_free_actions.size() + actions.size(); }

      account_name first_authorizer()const {
         for( const auto& a : actions ) {
            for( const auto& u : a.authorization )
               return u.actor;
         }
         return account_name();
      }

      flat_multimap<uint16_t, transaction_extension> validate_and_extract_extensions()const;
   };

   struct signed_transaction : public transaction
   {
      signed_transaction() = default;
//      signed_transaction( const signed_transaction& ) = default;
//      signed_transaction( signed_transaction&& ) = default;
      signed_transaction( transaction&& trx, const vector<signature_type>& signatures, const vector<bytes>& context_free_data)
      : transaction(std::move(trx))
      , signatures(signatures)
      , context_free_data(context_free_data)
      {}
      signed_transaction( transaction&& trx, const vector<signature_type>& signatures, vector<bytes>&& context_free_data)
      : transaction(std::move(trx))
      , signatures(signatures)
      , context_free_data(std::move(context_free_data))
      {}

      vector<signature_type>    signatures;
      vector<bytes>             context_free_data; ///< for each context-free action, there is an entry here

      const signature_type&     sign(const private_key_type& key, const chain_id_type& chain_id);
      signature_type            sign(const private_key_type& key, const chain_id_type& chain_id)const;
      fc::microseconds          get_signature_keys( const chain_id_type& chain_id, fc::time_point deadline,
                                                    flat_set<public_key_type>& recovered_pub_keys,
                                                    bool allow_duplicate_keys = false )const;
   };

   struct packed_transaction : fc::reflect_init {
      enum class compression_type {
         none = 0,
         zlib = 1
      };

      packed_transaction() = default;
      packed_transaction(packed_transaction&&) = default;
      explicit packed_transaction(const packed_transaction&) = default;
      packed_transaction& operator=(const packed_transaction&) = delete;
      packed_transaction& operator=(packed_transaction&&) = default;

      explicit packed_transaction(const signed_transaction& t, compression_type _compression = compression_type::none)
      :signatures(t.signatures), compression(_compression), unpacked_trx(t), trx_id(unpacked_trx.id())
      {
         local_pack_transaction();
         local_pack_context_free_data();
      }

      explicit packed_transaction(signed_transaction&& t, compression_type _compression = compression_type::none)
      :signatures(t.signatures), compression(_compression), unpacked_trx(std::move(t)), trx_id(unpacked_trx.id())
      {
         local_pack_transaction();
         local_pack_context_free_data();
      }

      // used by abi_serializer
      packed_transaction( bytes&& packed_txn, vector<signature_type>&& sigs, bytes&& packed_cfd, compression_type _compression );
      packed_transaction( bytes&& packed_txn, vector<signature_type>&& sigs, vector<bytes>&& cfd, compression_type _compression );
      packed_transaction( transaction&& t, vector<signature_type>&& sigs, bytes&& packed_cfd, compression_type _compression );

      uint32_t get_unprunable_size()const;
      uint32_t get_prunable_size()const;

      digest_type packed_digest()const;

      const transaction_id_type& id()const { return trx_id; }
      bytes               get_raw_transaction()const;

      time_point_sec                expiration()const { return unpacked_trx.expiration; }
      const vector<bytes>&          get_context_free_data()const { return unpacked_trx.context_free_data; }
      const transaction&            get_transaction()const { return unpacked_trx; }
      const signed_transaction&     get_signed_transaction()const { return unpacked_trx; }
      const vector<signature_type>& get_signatures()const { return signatures; }
      const fc::enum_type<uint8_t,compression_type>& get_compression()const { return compression; }
      const bytes&                  get_packed_context_free_data()const { return packed_context_free_data; }
      const bytes&                  get_packed_transaction()const { return packed_trx; }

   private:
      void local_unpack_transaction(vector<bytes>&& context_free_data);
      void local_unpack_context_free_data();
      void local_pack_transaction();
      void local_pack_context_free_data();

      friend struct fc::reflector<packed_transaction>;
      friend struct fc::reflector_init_visitor<packed_transaction>;
      friend struct fc::has_reflector_init<packed_transaction>;
      void reflector_init();
   private:
      vector<signature_type>                  signatures;
      fc::enum_type<uint8_t,compression_type> compression;
      bytes                                   packed_context_free_data;
      bytes                                   packed_trx;

   private:
      // cache unpacked trx, for thread safety do not modify after construction
      signed_transaction                      unpacked_trx;
      transaction_id_type                     trx_id;
   };

   using packed_transaction_ptr = std::shared_ptr<packed_transaction>;

   uint128_t transaction_id_to_sender_id( const transaction_id_type& tid );

} } /// namespace eosio::chain

FC_REFLECT(eosio::chain::deferred_transaction_generation_context, (sender_trx_id)(sender_id)(sender) )
FC_REFLECT( eosio::chain::transaction_header, (expiration)(ref_block_num)(ref_block_prefix)
                                              (max_net_usage_words)(max_cpu_usage_ms)(delay_sec) )
FC_REFLECT_DERIVED( eosio::chain::transaction, (eosio::chain::transaction_header), (context_free_actions)(actions)(transaction_extensions) )
FC_REFLECT_DERIVED( eosio::chain::signed_transaction, (eosio::chain::transaction), (signatures)(context_free_data) )
FC_REFLECT_ENUM( eosio::chain::packed_transaction::compression_type, (none)(zlib))

FC_REFLECT(eosio::chain::packed_transaction, (signatures)(compression)(packed_context_free_data)(packed_trx))

<|endoftext|>


<commit_before>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include <unistd.h>
#include <zubax_chibios/sys/sys.h>
#include <uavcan_stm32/uavcan_stm32.hpp>
#include <uavcan/protocol/global_time_sync_slave.hpp>
#include <uavcan/protocol/dynamic_node_id_client.hpp>
#include "board/board.hpp"

namespace app
{
namespace
{

uavcan_stm32::CanInitHelper<128> can;

constexpr unsigned NodePoolSize = 16384;

uavcan::Node<NodePoolSize>& getNode()
{
    static uavcan::Node<NodePoolSize> node(can.driver, uavcan_stm32::SystemClock::instance());
    return node;
}

void init()
{
    board::init();

    /*
     * CAN auto bit rate detection.
     * Automatic bit rate detection requires that the bus has at least one other CAN node publishing some
     * frames periodically.
     * Auto bit rate detection can be bypassed byif the desired bit rate is passed directly to can.init(), e.g.:
     *   can.init(1000000);
     */
    int res = 0;
    do
    {
        ::sleep(1);
        ::syslog("CAN auto bit rate detection...\n");

        std::uint32_t bitrate = 0;
        res = can.init([]() { ::usleep(can.getRecommendedListeningDelay().toUSec()); }, bitrate);
        if (res >= 0)
        {
            ::syslog("CAN inited at %u bps\n", unsigned(bitrate));
        }
    }
    while (res < 0);
}

class : public chibios_rt::BaseStaticThread<8192>
{
    void configureNodeInfo()
    {
        getNode().setName("org.uavcan.stm32_test_stm32f107");

        /*
         * Software version
         * TODO: Fill other fields too
         */
        uavcan::protocol::SoftwareVersion swver;

        swver.vcs_commit = GIT_HASH;
        swver.optional_field_flags = swver.OPTIONAL_FIELD_FLAG_VCS_COMMIT;

        getNode().setSoftwareVersion(swver);

        syslog("Git commit hash: 0x%08x\n", GIT_HASH);

        /*
         * Hardware version
         * TODO: Fill other fields too
         */
        uavcan::protocol::HardwareVersion hwver;

        std::uint8_t uid[board::UniqueIDSize] = {};
        board::readUniqueID(uid);
        std::copy(std::begin(uid), std::end(uid), std::begin(hwver.unique_id));

        getNode().setHardwareVersion(hwver);

        syslog("UDID:");
        for (auto b : hwver.unique_id)
        {
            syslog(" %02x", unsigned(b));
        }
        syslog("\n");
    }

    void performDynamicNodeIDAllocation()
    {
        uavcan::DynamicNodeIDClient client(getNode());

        const int client_start_res = client.start(getNode().getHardwareVersion().unique_id);
        if (client_start_res < 0)
        {
            board::die(client_start_res);
        }

        syslog("Waiting for dynamic node ID allocation...\n");
        while (!client.isAllocationComplete())
        {
            const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(100));
            if (spin_res < 0)
            {
                syslog("Spin failure: %i\n", spin_res);
            }
        }

        syslog("Dynamic node ID %d allocated by %d\n",
                  int(client.getAllocatedNodeID().get()),
                  int(client.getAllocatorNodeID().get()));

        getNode().setNodeID(client.getAllocatedNodeID());
    }

public:
    msg_t main()
    {
        /*
         * Setting up the node parameters
         */
        configureNodeInfo();

        /*
         * Initializing the UAVCAN node
         */
        const int node_init_res = getNode().start();
        if (node_init_res < 0)
        {
            board::die(node_init_res);
        }

        /*
         * Waiting for a dynamic node ID allocation
         */
        performDynamicNodeIDAllocation();

        /*
         * Time synchronizer
         */
        static uavcan::GlobalTimeSyncSlave time_sync_slave(getNode());
        {
            const int res = time_sync_slave.start();
            if (res < 0)
            {
                board::die(res);
            }
        }

        /*
         * Main loop
         */
        syslog("UAVCAN node started\n");
        getNode().setModeOperational();
        while (true)
        {
            const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(5000));
            if (spin_res < 0)
            {
                syslog("Spin failure: %i\n", spin_res);
            }

            syslog("Time sync master: %u\n", unsigned(time_sync_slave.getMasterNodeID().get()));

            syslog("Memory usage: free=%u used=%u worst=%u\n",
                      getNode().getAllocator().getNumFreeBlocks(),
                      getNode().getAllocator().getNumUsedBlocks(),
                      getNode().getAllocator().getPeakNumUsedBlocks());

            syslog("CAN errors: %lu %lu\n",
                      static_cast<unsigned long>(can.driver.getIface(0)->getErrorCount()),
                      static_cast<unsigned long>(can.driver.getIface(1)->getErrorCount()));

#if !UAVCAN_TINY
            node.getLogger().setLevel(uavcan::protocol::debug::LogLevel::INFO);
            node.logInfo("app", "UTC %* sec, %* corr, %* jumps",
                         uavcan_stm32::clock::getUtc().toMSec() / 1000,
                         uavcan_stm32::clock::getUtcSpeedCorrectionPPM(),
                         uavcan_stm32::clock::getUtcAjdustmentJumpCount());
#endif
        }
        return msg_t();
    }
} uavcan_node_thread;

}
}

int main()
{
    app::init();

    syslog("Starting the UAVCAN thread\n");
    app::uavcan_node_thread.start(LOWPRIO);

    while (true)
    {
        for (int i = 0; i < 200; i++)
        {
            board::setLed(app::can.driver.hadActivity());
            ::usleep(25000);
        }

        const uavcan::UtcTime utc = uavcan_stm32::clock::getUtc();
        syslog("UTC %lu sec   Rate corr: %fPPM   Jumps: %lu   Locked: %i\n",
                  static_cast<unsigned long>(utc.toMSec() / 1000),
                  uavcan_stm32::clock::getUtcRateCorrectionPPM(),
                  uavcan_stm32::clock::getUtcJumpCount(),
                  int(uavcan_stm32::clock::isUtcLocked()));
    }
}
<commit_msg>Back out upstream changes from test_stm32f107/src/main.cpp<commit_after>/*
 * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
 */

#include <unistd.h>
#include <zubax_chibios/sys/sys.h>
#include <uavcan_stm32/uavcan_stm32.hpp>
#include <uavcan/protocol/global_time_sync_slave.hpp>
#include <uavcan/protocol/dynamic_node_id_client.hpp>
#include "board/board.hpp"

namespace app
{
namespace
{

uavcan_stm32::CanInitHelper<128> can;

constexpr unsigned NodePoolSize = 16384;

uavcan::Node<NodePoolSize>& getNode()
{
    static uavcan::Node<NodePoolSize> node(can.driver, uavcan_stm32::SystemClock::instance());
    return node;
}

void init()
{
    board::init();

    /*
     * CAN auto bit rate detection.
     * Automatic bit rate detection requires that the bus has at least one other CAN node publishing some
     * frames periodically.
     * Auto bit rate detection can be bypassed byif the desired bit rate is passed directly to can.init(), e.g.:
     *   can.init(1000000);
     */
    int res = 0;
    do
    {
        ::sleep(1);
        ::lowsyslog("CAN auto bit rate detection...\n");

        std::uint32_t bitrate = 0;
        res = can.init([]() { ::usleep(can.getRecommendedListeningDelay().toUSec()); }, bitrate);
        if (res >= 0)
        {
            ::lowsyslog("CAN inited at %u bps\n", unsigned(bitrate));
        }
    }
    while (res < 0);
}

class : public chibios_rt::BaseStaticThread<8192>
{
    void configureNodeInfo()
    {
        getNode().setName("org.uavcan.stm32_test_stm32f107");

        /*
         * Software version
         * TODO: Fill other fields too
         */
        uavcan::protocol::SoftwareVersion swver;

        swver.vcs_commit = GIT_HASH;
        swver.optional_field_flags = swver.OPTIONAL_FIELD_FLAG_VCS_COMMIT;

        getNode().setSoftwareVersion(swver);

        lowsyslog("Git commit hash: 0x%08x\n", GIT_HASH);

        /*
         * Hardware version
         * TODO: Fill other fields too
         */
        uavcan::protocol::HardwareVersion hwver;

        std::uint8_t uid[board::UniqueIDSize] = {};
        board::readUniqueID(uid);
        std::copy(std::begin(uid), std::end(uid), std::begin(hwver.unique_id));

        getNode().setHardwareVersion(hwver);

        lowsyslog("UDID:");
        for (auto b : hwver.unique_id)
        {
            lowsyslog(" %02x", unsigned(b));
        }
        lowsyslog("\n");
    }

    void performDynamicNodeIDAllocation()
    {
        uavcan::DynamicNodeIDClient client(getNode());

        const int client_start_res = client.start(getNode().getHardwareVersion().unique_id);
        if (client_start_res < 0)
        {
            board::die(client_start_res);
        }

        lowsyslog("Waiting for dynamic node ID allocation...\n");
        while (!client.isAllocationComplete())
        {
            const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(100));
            if (spin_res < 0)
            {
                lowsyslog("Spin failure: %i\n", spin_res);
            }
        }

        lowsyslog("Dynamic node ID %d allocated by %d\n",
                  int(client.getAllocatedNodeID().get()),
                  int(client.getAllocatorNodeID().get()));

        getNode().setNodeID(client.getAllocatedNodeID());
    }

public:
    msg_t main()
    {
        /*
         * Setting up the node parameters
         */
        configureNodeInfo();

        /*
         * Initializing the UAVCAN node
         */
        const int node_init_res = getNode().start();
        if (node_init_res < 0)
        {
            board::die(node_init_res);
        }

        /*
         * Waiting for a dynamic node ID allocation
         */
        performDynamicNodeIDAllocation();

        /*
         * Time synchronizer
         */
        static uavcan::GlobalTimeSyncSlave time_sync_slave(getNode());
        {
            const int res = time_sync_slave.start();
            if (res < 0)
            {
                board::die(res);
            }
        }

        /*
         * Main loop
         */
        lowsyslog("UAVCAN node started\n");
        getNode().setModeOperational();
        while (true)
        {
            const int spin_res = getNode().spin(uavcan::MonotonicDuration::fromMSec(5000));
            if (spin_res < 0)
            {
                lowsyslog("Spin failure: %i\n", spin_res);
            }

            lowsyslog("Time sync master: %u\n", unsigned(time_sync_slave.getMasterNodeID().get()));

            lowsyslog("Memory usage: free=%u used=%u worst=%u\n",
                      getNode().getAllocator().getNumFreeBlocks(),
                      getNode().getAllocator().getNumUsedBlocks(),
                      getNode().getAllocator().getPeakNumUsedBlocks());

            lowsyslog("CAN errors: %lu %lu\n",
                      static_cast<unsigned long>(can.driver.getIface(0)->getErrorCount()),
                      static_cast<unsigned long>(can.driver.getIface(1)->getErrorCount()));

#if !UAVCAN_TINY
            node.getLogger().setLevel(uavcan::protocol::debug::LogLevel::INFO);
            node.logInfo("app", "UTC %* sec, %* corr, %* jumps",
                         uavcan_stm32::clock::getUtc().toMSec() / 1000,
                         uavcan_stm32::clock::getUtcSpeedCorrectionPPM(),
                         uavcan_stm32::clock::getUtcAjdustmentJumpCount());
#endif
        }
        return msg_t();
    }
} uavcan_node_thread;

}
}

int main()
{
    app::init();

    lowsyslog("Starting the UAVCAN thread\n");
    app::uavcan_node_thread.start(LOWPRIO);

    while (true)
    {
        for (int i = 0; i < 200; i++)
        {
            board::setLed(app::can.driver.hadActivity());
            ::usleep(25000);
        }

        const uavcan::UtcTime utc = uavcan_stm32::clock::getUtc();
        lowsyslog("UTC %lu sec   Rate corr: %fPPM   Jumps: %lu   Locked: %i\n",
                  static_cast<unsigned long>(utc.toMSec() / 1000),
                  uavcan_stm32::clock::getUtcRateCorrectionPPM(),
                  uavcan_stm32::clock::getUtcJumpCount(),
                  int(uavcan_stm32::clock::isUtcLocked()));
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2011 Sirikata Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.

#include "EmersonMessagingManager.hpp"
#include "JSLogging.hpp"
#include <sirikata/core/network/Frame.hpp>
#include "Protocol_Frame.pbj.hpp"

namespace Sirikata{
namespace JS{

#define EMERSON_RELIABLE_COMMUNICATION_PORT 5

EmersonMessagingManager::EmersonMessagingManager(ObjectHostContext* ctx)
 : mMainContext(ctx)
{
}

EmersonMessagingManager::~EmersonMessagingManager()
{
    // Close all streams, clear out listeners
    for(PresenceStreamMap::iterator pres_it = mStreams.begin(); pres_it != mStreams.end(); pres_it++) {
        // Stop listening for new streams
        const SpaceObjectReference& pres_id = pres_it->first;
        mMainContext->sstConnMgr()->unlisten(
            SST::EndPoint<SpaceObjectReference>(pres_id,EMERSON_RELIABLE_COMMUNICATION_PORT)
        );

        // Stop listening for substreams on the streams we have, close them
        StreamMap& pres_streams = pres_it->second;
        for(StreamMap::iterator it = pres_streams.begin(); it != pres_streams.end(); it++) {
            SSTStreamPtr stream = it->second;
            stream->unlistenSubstream(EMERSON_RELIABLE_COMMUNICATION_PORT);
            stream->close(false);
        }
    }

    mStreams.clear();
}

void EmersonMessagingManager::presenceConnected(const SpaceObjectReference& connPresSporef)
{
    allPres[connPresSporef] = true;

    //create a listener for scripting messages to presence with sporef connPresSporef.
    mMainContext->sstConnMgr()->listen(
        std::tr1::bind(&EmersonMessagingManager::createScriptCommListenerStreamCB,this,
            livenessToken(), connPresSporef,_1,_2
        ),
        SST::EndPoint<SpaceObjectReference>(connPresSporef,EMERSON_RELIABLE_COMMUNICATION_PORT)
    );
}

void EmersonMessagingManager::presenceDisconnected(const SpaceObjectReference& disconnPresSporef)
{
    std::map<SpaceObjectReference,bool>::iterator allPresFinder = allPres.find(disconnPresSporef);
    if (allPresFinder == allPres.end())
    {
        JSLOG(error,"Error in messaging manager: requested a disconnect for a presence that was not already connected.");
        return;
    }
    allPres.erase(allPresFinder);
    clearStreams(disconnPresSporef);
}

void EmersonMessagingManager::setupNewStream(
    SSTStreamPtr sstStream,bool closePrevious)
{
    // Regardless of whether we want to hold on to this or not, make sure we set
    // it up in case it gets used

    // Listen for substreams, which is where we'll actually receive data. This
    // top level stream goes unused.
    sstStream->listenSubstream(
        EMERSON_RELIABLE_COMMUNICATION_PORT,
        std::tr1::bind(
            &EmersonMessagingManager::handleIncomingSubstream, this,
            livenessToken(), _1, _2
        )
    );

    // Then figure out whether to save it for ourselves
    SpaceObjectReference pres = sstStream->localEndPoint().endPoint;
    SpaceObjectReference sender = sstStream->remoteEndPoint().endPoint;

    // Simple rule for resolving which stream to save.
    bool save_mine = pres < sender;

    //doing this for the case where a previous stream wasn't responding to
    //messages, and we want to close it and replace it.  (should really only
    //happen in call scriptCommWriteStreamConnectedCB, which only gets called if
    //previous stream didn't exist or if it did exist, but couldn't get messages
    //to other end after numerous retries.)
    if (closePrevious)
    {
        if (mStreams.find(pres) != mStreams.end())
        {
            JSLOG(detailed,"Deleting existing stream map to make way for new one");
            StreamMap& smap = mStreams[pres];
            StreamMap::iterator particularStreamIt  = smap.find(sender);
            if (particularStreamIt != smap.end())
            {
                particularStreamIt->second->close(false);
                smap.erase(particularStreamIt);
            }
        }
    }

    StreamMap& pres_streams = mStreams[pres];
    StreamMap::iterator it = pres_streams.find(sender);
    if (it == pres_streams.end() || !it->second->connected() || save_mine) {
        pres_streams[sender] = sstStream;
    }
}

SSTStreamPtr EmersonMessagingManager::getStream(const SpaceObjectReference& pres, const SpaceObjectReference& remote) {
    PresenceStreamMap::iterator pres_it = mStreams.find(pres);
    if (pres_it == mStreams.end()) return SSTStreamPtr();
    StreamMap& pres_streams = pres_it->second;

    StreamMap::iterator it = pres_streams.find(remote);
    if (it != pres_streams.end()) {
        return it->second;
    }
    return SSTStreamPtr();
}

void EmersonMessagingManager::clearStreams(const SpaceObjectReference& pres) {
    PresenceStreamMap::iterator pres_it = mStreams.find(pres);
    if (pres_it != mStreams.end())
        mStreams.erase(pres_it);
}

//Gets executed whenever a new stream connects to presence with sporef toListenFrom.
void EmersonMessagingManager::createScriptCommListenerStreamCB(Liveness::Token alive, const SpaceObjectReference& toListenFrom, int err, SSTStreamPtr sstStream)
{
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS)
    {
        JSLOG(error,"Error in createScriptCommListenerStreamCB.  Stream could not be created.");
        return;
    }
    setupNewStream(sstStream);
}

void EmersonMessagingManager::handleIncomingSubstream(Liveness::Token alive, int err, SSTStreamPtr streamPtr) {
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS) return;

    String* msgBuf = new String();
    streamPtr->registerReadCallback(
        std::tr1::bind(&EmersonMessagingManager::handleScriptCommStreamRead, this,
            livenessToken(), streamPtr, msgBuf, _1, _2)
    );
}

//Gets executed whenever have additional data to read.
void EmersonMessagingManager::handleScriptCommStreamRead(Liveness::Token alive, SSTStreamPtr sstptr, String* prevdata, uint8* buffer, int length)
{
    if (!alive) {
        delete prevdata;
        sstptr->registerReadCallback(0);
        sstptr->close(false);
        return;
    }

    prevdata->append((const char*)buffer, length);

    // We should only ever get one message per substream, so there's no need to
    // loop and we can clean up as soon as we parse a message successfully.
    std::string msg = Network::Frame::parse(*prevdata);

    // If we don't have a full message, just wait for more
    if (msg.empty())
        return;

    //otherwise, try to handle it.
    handleScriptCommRead(sstptr->remoteEndPoint().endPoint, sstptr->localEndPoint().endPoint, msg);

    delete prevdata;
    sstptr->registerReadCallback(0);
    sstptr->close(false);
}

////////////////writing functions.

bool EmersonMessagingManager::sendScriptCommMessageReliable(const SpaceObjectReference& sender, const SpaceObjectReference& receiver, const String& msg) {
    // arbitrary default # of retries
    return sendScriptCommMessageReliable(sender, receiver, msg, 10,5);
}

void EmersonMessagingManager::removeStream(
    const SpaceObjectReference& sender, const SpaceObjectReference& receiver)
{
    if (mStreams.find(sender) != mStreams.end())
    {
        StreamMap& smap = mStreams[sender];
        for(StreamMap::iterator it = smap.begin(); it != smap.end(); it++)
            it->second->close(false);
        mStreams.erase(sender);
    }
}

bool EmersonMessagingManager::sendScriptCommMessageReliable(const SpaceObjectReference& sender, const SpaceObjectReference& receiver, const String& msg, int8 retriesSameStream,int8 retriesNewStream,bool isRetry)
{
    bool returner = false;

    //if we have tried to create a bunch of new streams, and that has still
    //failed, then remove the existing stream (if it's there), and drop message.
    if (retriesNewStream == 0)
    {
        JSLOG(error, "Cannot send message from sender "<< sender<<\
              " to "<<receiver<<".  Aborting.");
        removeStream(sender,receiver);
        return false;
    }

    if (retriesSameStream > 0)
    {
        // If we have a stream
        SSTStreamPtr streamPtr = getStream(sender, receiver);
        if (streamPtr)
        {
            writeMessage(
                livenessToken(), streamPtr, msg, sender,
                receiver, retriesSameStream,--retriesNewStream);
            return true;
        }
    }

    //what to do when the connection is created.
    CommWriteStreamConnectedCBRetryData retryData;
    retryData.retriesSameStream = retriesSameStream;
    retryData.retriesNewStream = --retriesNewStream;
    retryData.isRetry = isRetry;

    // Otherwise, start the process of connecting
    // connection will overwrite existing streams if they are there.
    returner = mMainContext->sstConnMgr()->connectStream(
        SST::EndPoint<SpaceObjectReference>(sender,0), //local port is random

        //send to receiver's script comm port
        SST::EndPoint<SpaceObjectReference>(receiver,EMERSON_RELIABLE_COMMUNICATION_PORT),

        std::tr1::bind(
            &EmersonMessagingManager::scriptCommWriteStreamConnectedCB, this,
            livenessToken(),
            msg, sender,receiver, _1, _2, retryData
        )
    );

    return returner;
}

void EmersonMessagingManager::scriptCommWriteStreamConnectedCB(
    Liveness::Token alive, const String& msg,
    const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int err, SSTStreamPtr streamPtr,
    CommWriteStreamConnectedCBRetryData retryData)
{
    if (!alive) return;

    int8 retriesSameStream = retryData.retriesSameStream;
    int8 retriesNewStream = retryData.retriesNewStream;
    bool isRetry = retryData.isRetry;

    //if connection failure, just try to re-connect.
    if (err != SST_IMPL_SUCCESS)
    {
        if (retriesSameStream > 0)
        {
            sendScriptCommMessageReliable(
                sender, receiver, msg, --retriesSameStream,
                retriesNewStream,true);
        }
        return;
    }
    //will overwrite any existing streams if they existed before.
    setupNewStream(streamPtr,isRetry);

    writeMessage(
        livenessToken(), streamPtr, msg, sender,
        receiver, retriesSameStream,retriesNewStream);
}

void EmersonMessagingManager::writeMessage(
    Liveness::Token alive, SSTStreamPtr streamPtr,
    const String& msg, const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int8 retriesSameStream,
    int8 retriesNewStream)
{
    if (!alive) return;

    // FIXME we're not pushing data directly here because it doesn't seem like
    // you can actually find out how much data was successfully pushed in. what
    // if we have a very large message?
    streamPtr->createChildStream(
        std::tr1::bind(&EmersonMessagingManager::writeMessageSubstream,
            this, alive, _1, _2, msg, sender, receiver,
            retriesSameStream,retriesNewStream),
        NULL, 0,
        EMERSON_RELIABLE_COMMUNICATION_PORT, EMERSON_RELIABLE_COMMUNICATION_PORT
    );
}

void EmersonMessagingManager::writeMessageSubstream(
    Liveness::Token alive, int err, SSTStreamPtr subStreamPtr,
    const String& msg, const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int8 retriesSameStream,
    int8 retriesNewStream)
{
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS)
    {
        // Try the whole process over, starting from scratch in case the stream
        // changed
        if (retriesSameStream > 0)
        {
            sendScriptCommMessageReliable(
                sender, receiver, msg, --retriesSameStream,retriesNewStream,true);
        }
        return;
    }

    writeData(livenessToken(), subStreamPtr, Network::Frame::write(msg), sender, receiver);
}

void EmersonMessagingManager::writeData(Liveness::Token alive, SSTStreamPtr streamPtr, const String& msg, const SpaceObjectReference& sender, const SpaceObjectReference& receiver) {
    if (!alive) return;

    int bytesWritten = streamPtr->write((uint8*) msg.data(), msg.size());
    //errored out: log message and abort.
    if (bytesWritten == -1)
    {
        JSLOG(error,"Error sending script communication message in scriptCommStreamCallback.  Not sending message.");
        return;
    }

    //check if full message was written.
    if(bytesWritten < (int)msg.size())
    {
        //retry write infinitely
        String restToWrite = msg.substr(bytesWritten);
        mMainContext->mainStrand->post(
            Duration::milliseconds((int64)20),
            std::tr1::bind(&EmersonMessagingManager::writeData, this, livenessToken(), streamPtr, restToWrite, sender, receiver),
            "EmersonMessagingManager::writeData"
        );
        JSLOG(detailed,"More sript data to write to stream.  Queueing future write operation.");
    }
    else
        streamPtr->close(false);
}

} //end namespace js
} //end namespace sirikata
<commit_msg>createChildStream call can fail if the connection had already been closed.  This change handles case where creation fails.  Essentially, we force new stream creation through the retry mechanism already in place.<commit_after>// Copyright (c) 2011 Sirikata Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can
// be found in the LICENSE file.

#include "EmersonMessagingManager.hpp"
#include "JSLogging.hpp"
#include <sirikata/core/network/Frame.hpp>
#include "Protocol_Frame.pbj.hpp"

namespace Sirikata{
namespace JS{

#define EMERSON_RELIABLE_COMMUNICATION_PORT 5

EmersonMessagingManager::EmersonMessagingManager(ObjectHostContext* ctx)
 : mMainContext(ctx)
{
}

EmersonMessagingManager::~EmersonMessagingManager()
{
    // Close all streams, clear out listeners
    for(PresenceStreamMap::iterator pres_it = mStreams.begin(); pres_it != mStreams.end(); pres_it++) {
        // Stop listening for new streams
        const SpaceObjectReference& pres_id = pres_it->first;
        mMainContext->sstConnMgr()->unlisten(
            SST::EndPoint<SpaceObjectReference>(pres_id,EMERSON_RELIABLE_COMMUNICATION_PORT)
        );

        // Stop listening for substreams on the streams we have, close them
        StreamMap& pres_streams = pres_it->second;
        for(StreamMap::iterator it = pres_streams.begin(); it != pres_streams.end(); it++) {
            SSTStreamPtr stream = it->second;
            stream->unlistenSubstream(EMERSON_RELIABLE_COMMUNICATION_PORT);
            stream->close(false);
        }
    }

    mStreams.clear();
}

void EmersonMessagingManager::presenceConnected(const SpaceObjectReference& connPresSporef)
{
    allPres[connPresSporef] = true;

    //create a listener for scripting messages to presence with sporef connPresSporef.
    mMainContext->sstConnMgr()->listen(
        std::tr1::bind(&EmersonMessagingManager::createScriptCommListenerStreamCB,this,
            livenessToken(), connPresSporef,_1,_2
        ),
        SST::EndPoint<SpaceObjectReference>(connPresSporef,EMERSON_RELIABLE_COMMUNICATION_PORT)
    );
}

void EmersonMessagingManager::presenceDisconnected(const SpaceObjectReference& disconnPresSporef)
{
    std::map<SpaceObjectReference,bool>::iterator allPresFinder = allPres.find(disconnPresSporef);
    if (allPresFinder == allPres.end())
    {
        JSLOG(error,"Error in messaging manager: requested a disconnect for a presence that was not already connected.");
        return;
    }
    allPres.erase(allPresFinder);
    clearStreams(disconnPresSporef);
}

void EmersonMessagingManager::setupNewStream(
    SSTStreamPtr sstStream,bool closePrevious)
{
    // Regardless of whether we want to hold on to this or not, make sure we set
    // it up in case it gets used

    // Listen for substreams, which is where we'll actually receive data. This
    // top level stream goes unused.
    sstStream->listenSubstream(
        EMERSON_RELIABLE_COMMUNICATION_PORT,
        std::tr1::bind(
            &EmersonMessagingManager::handleIncomingSubstream, this,
            livenessToken(), _1, _2
        )
    );

    // Then figure out whether to save it for ourselves
    SpaceObjectReference pres = sstStream->localEndPoint().endPoint;
    SpaceObjectReference sender = sstStream->remoteEndPoint().endPoint;

    // Simple rule for resolving which stream to save.
    bool save_mine = pres < sender;

    //doing this for the case where a previous stream wasn't responding to
    //messages, and we want to close it and replace it.  (should really only
    //happen in call scriptCommWriteStreamConnectedCB, which only gets called if
    //previous stream didn't exist or if it did exist, but couldn't get messages
    //to other end after numerous retries.)
    if (closePrevious)
    {
        if (mStreams.find(pres) != mStreams.end())
        {
            JSLOG(detailed,"Deleting existing stream map to make way for new one");
            StreamMap& smap = mStreams[pres];
            StreamMap::iterator particularStreamIt  = smap.find(sender);
            if (particularStreamIt != smap.end())
            {
                particularStreamIt->second->close(false);
                smap.erase(particularStreamIt);
            }
        }
    }

    StreamMap& pres_streams = mStreams[pres];
    StreamMap::iterator it = pres_streams.find(sender);
    if (it == pres_streams.end() || !it->second->connected() || save_mine) {
        pres_streams[sender] = sstStream;
    }
}

SSTStreamPtr EmersonMessagingManager::getStream(const SpaceObjectReference& pres, const SpaceObjectReference& remote) {
    PresenceStreamMap::iterator pres_it = mStreams.find(pres);
    if (pres_it == mStreams.end()) return SSTStreamPtr();
    StreamMap& pres_streams = pres_it->second;

    StreamMap::iterator it = pres_streams.find(remote);
    if (it != pres_streams.end()) {
        return it->second;
    }
    return SSTStreamPtr();
}

void EmersonMessagingManager::clearStreams(const SpaceObjectReference& pres) {
    PresenceStreamMap::iterator pres_it = mStreams.find(pres);
    if (pres_it != mStreams.end())
        mStreams.erase(pres_it);
}

//Gets executed whenever a new stream connects to presence with sporef toListenFrom.
void EmersonMessagingManager::createScriptCommListenerStreamCB(Liveness::Token alive, const SpaceObjectReference& toListenFrom, int err, SSTStreamPtr sstStream)
{
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS)
    {
        JSLOG(error,"Error in createScriptCommListenerStreamCB.  Stream could not be created.");
        return;
    }
    setupNewStream(sstStream);
}

void EmersonMessagingManager::handleIncomingSubstream(Liveness::Token alive, int err, SSTStreamPtr streamPtr) {
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS) return;

    String* msgBuf = new String();
    streamPtr->registerReadCallback(
        std::tr1::bind(&EmersonMessagingManager::handleScriptCommStreamRead, this,
            livenessToken(), streamPtr, msgBuf, _1, _2)
    );
}

//Gets executed whenever have additional data to read.
void EmersonMessagingManager::handleScriptCommStreamRead(Liveness::Token alive, SSTStreamPtr sstptr, String* prevdata, uint8* buffer, int length)
{
    if (!alive) {
        delete prevdata;
        sstptr->registerReadCallback(0);
        sstptr->close(false);
        return;
    }

    prevdata->append((const char*)buffer, length);

    // We should only ever get one message per substream, so there's no need to
    // loop and we can clean up as soon as we parse a message successfully.
    std::string msg = Network::Frame::parse(*prevdata);

    // If we don't have a full message, just wait for more
    if (msg.empty())
        return;

    //otherwise, try to handle it.
    handleScriptCommRead(sstptr->remoteEndPoint().endPoint, sstptr->localEndPoint().endPoint, msg);

    delete prevdata;
    sstptr->registerReadCallback(0);
    sstptr->close(false);
}

////////////////writing functions.

bool EmersonMessagingManager::sendScriptCommMessageReliable(const SpaceObjectReference& sender, const SpaceObjectReference& receiver, const String& msg) {
    // arbitrary default # of retries
    return sendScriptCommMessageReliable(sender, receiver, msg, 10,5);
}

void EmersonMessagingManager::removeStream(
    const SpaceObjectReference& sender, const SpaceObjectReference& receiver)
{
    if (mStreams.find(sender) != mStreams.end())
    {
        StreamMap& smap = mStreams[sender];
        for(StreamMap::iterator it = smap.begin(); it != smap.end(); it++)
            it->second->close(false);
        mStreams.erase(sender);
    }
}

bool EmersonMessagingManager::sendScriptCommMessageReliable(const SpaceObjectReference& sender, const SpaceObjectReference& receiver, const String& msg, int8 retriesSameStream,int8 retriesNewStream,bool isRetry)
{
    bool returner = false;

    //if we have tried to create a bunch of new streams, and that has still
    //failed, then remove the existing stream (if it's there), and drop message.
    if (retriesNewStream == 0)
    {
        JSLOG(error, "Cannot send message from sender "<< sender<<\
              " to "<<receiver<<".  Aborting.");
        removeStream(sender,receiver);
        return false;
    }

    if (retriesSameStream > 0)
    {
        // If we have a stream
        SSTStreamPtr streamPtr = getStream(sender, receiver);
        if (streamPtr)
        {
            writeMessage(
                livenessToken(), streamPtr, msg, sender,
                receiver, retriesSameStream,--retriesNewStream);
            return true;
        }
    }

    //what to do when the connection is created.
    CommWriteStreamConnectedCBRetryData retryData;
    retryData.retriesSameStream = retriesSameStream;
    retryData.retriesNewStream = --retriesNewStream;
    retryData.isRetry = isRetry;

    // Otherwise, start the process of connecting
    // connection will overwrite existing streams if they are there.
    returner = mMainContext->sstConnMgr()->connectStream(
        SST::EndPoint<SpaceObjectReference>(sender,0), //local port is random

        //send to receiver's script comm port
        SST::EndPoint<SpaceObjectReference>(receiver,EMERSON_RELIABLE_COMMUNICATION_PORT),

        std::tr1::bind(
            &EmersonMessagingManager::scriptCommWriteStreamConnectedCB, this,
            livenessToken(),
            msg, sender,receiver, _1, _2, retryData
        )
    );

    return returner;
}

void EmersonMessagingManager::scriptCommWriteStreamConnectedCB(
    Liveness::Token alive, const String& msg,
    const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int err, SSTStreamPtr streamPtr,
    CommWriteStreamConnectedCBRetryData retryData)
{
    if (!alive) return;

    int8 retriesSameStream = retryData.retriesSameStream;
    int8 retriesNewStream = retryData.retriesNewStream;
    bool isRetry = retryData.isRetry;

    //if connection failure, just try to re-connect.
    if (err != SST_IMPL_SUCCESS)
    {
        if (retriesSameStream > 0)
        {
            sendScriptCommMessageReliable(
                sender, receiver, msg, --retriesSameStream,
                retriesNewStream,true);
        }
        return;
    }
    //will overwrite any existing streams if they existed before.
    setupNewStream(streamPtr,isRetry);

    writeMessage(
        livenessToken(), streamPtr, msg, sender,
        receiver, retriesSameStream,retriesNewStream);
}

void EmersonMessagingManager::writeMessage(
    Liveness::Token alive, SSTStreamPtr streamPtr,
    const String& msg, const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int8 retriesSameStream,
    int8 retriesNewStream)
{
    if (!alive) return;

    // FIXME we're not pushing data directly here because it doesn't seem like
    // you can actually find out how much data was successfully pushed in. what
    // if we have a very large message?
    int retCode = streamPtr->createChildStream(
        std::tr1::bind(&EmersonMessagingManager::writeMessageSubstream,
            this, alive, _1, _2, msg, sender, receiver,
            retriesSameStream,retriesNewStream),
        NULL, 0,
        EMERSON_RELIABLE_COMMUNICATION_PORT, EMERSON_RELIABLE_COMMUNICATION_PORT
    );

    if (retCode == -1)
    {
        //createChildStream failed.  close streamPtr and create a new stream to
        //try to send the message.  (Setting the retriesSameStream to zero and
        //isRetry to true should cause this to happen.)
        sendScriptCommMessageReliable(
            sender, receiver, msg, 0,--retriesNewStream,true);
    }

}

void EmersonMessagingManager::writeMessageSubstream(
    Liveness::Token alive, int err, SSTStreamPtr subStreamPtr,
    const String& msg, const SpaceObjectReference& sender,
    const SpaceObjectReference& receiver, int8 retriesSameStream,
    int8 retriesNewStream)
{
    if (!alive) return;

    if (err != SST_IMPL_SUCCESS)
    {
        // Try the whole process over, starting from scratch in case the stream
        // changed
        if (retriesSameStream > 0)
        {
            sendScriptCommMessageReliable(
                sender, receiver, msg, --retriesSameStream,retriesNewStream,true);
        }
        return;
    }

    writeData(livenessToken(), subStreamPtr, Network::Frame::write(msg), sender, receiver);
}

void EmersonMessagingManager::writeData(Liveness::Token alive, SSTStreamPtr streamPtr, const String& msg, const SpaceObjectReference& sender, const SpaceObjectReference& receiver) {
    if (!alive) return;

    int bytesWritten = streamPtr->write((uint8*) msg.data(), msg.size());
    //errored out: log message and abort.
    if (bytesWritten == -1)
    {
        JSLOG(error,"Error sending script communication message in scriptCommStreamCallback.  Not sending message.");
        return;
    }

    //check if full message was written.
    if(bytesWritten < (int)msg.size())
    {
        //retry write infinitely
        String restToWrite = msg.substr(bytesWritten);
        mMainContext->mainStrand->post(
            Duration::milliseconds((int64)20),
            std::tr1::bind(&EmersonMessagingManager::writeData, this, livenessToken(), streamPtr, restToWrite, sender, receiver),
            "EmersonMessagingManager::writeData"
        );
        JSLOG(detailed,"More sript data to write to stream.  Queueing future write operation.");
    }
    else
        streamPtr->close(false);
}

} //end namespace js
} //end namespace sirikata
<|endoftext|>


<commit_before>/*****************************************************************************
 * SegmentTemplate.cpp: Implement the UrlTemplate element.
 *****************************************************************************
 * Copyright (C) 1998-2007 VLC authors and VideoLAN
 * $Id$
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "SegmentTemplate.h"
#include "SegmentTimeline.h"
#include "SegmentInformation.hpp"
#include "AbstractPlaylist.hpp"

using namespace adaptive::playlist;

BaseSegmentTemplate::BaseSegmentTemplate( ICanonicalUrl *parent ) :
    Segment( parent )
{
}

MediaSegmentTemplate::MediaSegmentTemplate( SegmentInformation *parent ) :
    BaseSegmentTemplate( parent ), Timelineable(), TimescaleAble( parent )
{
    debugName = "SegmentTemplate";
    classId = Segment::CLASSID_SEGMENT;
    startNumber.Set( 1 );
    initialisationSegment.Set( NULL );
    templated = true;
    parentSegmentInformation = parent;
}

void MediaSegmentTemplate::mergeWith(MediaSegmentTemplate *updated, mtime_t prunebarrier)
{
    SegmentTimeline *timeline = segmentTimeline.Get();
    if(timeline && updated->segmentTimeline.Get())
    {
        timeline->mergeWith(*updated->segmentTimeline.Get());
        if(prunebarrier)
        {
            const Timescale timescale = timeline->inheritTimescale();
            const uint64_t number =
                    timeline->getElementNumberByScaledPlaybackTime(timescale.ToScaled(prunebarrier));
            timeline->pruneBySequenceNumber(number);
        }
    }
}

void MediaSegmentTemplate::pruneByPlaybackTime(mtime_t time)
{
    if(segmentTimeline.Get())
        return segmentTimeline.Get()->pruneByPlaybackTime(time);
}

size_t MediaSegmentTemplate::pruneBySequenceNumber(uint64_t number)
{
    if(segmentTimeline.Get())
        return segmentTimeline.Get()->pruneBySequenceNumber(number);
    return 0;
}

uint64_t MediaSegmentTemplate::getCurrentLiveTemplateNumber() const
{
    uint64_t number = startNumber.Get();
    /* live streams / templated */
    const stime_t dur = duration.Get();
    if(dur)
    {
        /* compute, based on current time */
        const time_t playbacktime = time(NULL);
        const Timescale timescale = inheritTimescale();
        time_t streamstart = parentSegmentInformation->getPlaylist()->availabilityStartTime.Get();
        streamstart += parentSegmentInformation->getPeriodStart();
        stime_t elapsed = timescale.ToScaled(CLOCK_FREQ * (playbacktime - streamstart));
        number += elapsed / dur - 2;
    }

    return number;
}

stime_t MediaSegmentTemplate::getMinAheadScaledTime(uint64_t number) const
{
    if( segmentTimeline.Get() )
        return segmentTimeline.Get()->getMinAheadScaledTime(number);

    return duration.Get(); /* FIXME: use stream end time */
}

uint64_t MediaSegmentTemplate::getSequenceNumber() const
{
    return startNumber.Get();
}

void MediaSegmentTemplate::setSourceUrl(const std::string &url)
{
    sourceUrl = Url(Url::Component(url, this));
}

void MediaSegmentTemplate::debug(vlc_object_t *obj, int indent) const
{
    Segment::debug(obj, indent);
    if(segmentTimeline.Get())
        segmentTimeline.Get()->debug(obj, indent + 1);
}

InitSegmentTemplate::InitSegmentTemplate( ICanonicalUrl *parent ) :
    BaseSegmentTemplate(parent)
{
    debugName = "InitSegmentTemplate";
    classId = InitSegment::CLASSID_INITSEGMENT;
}
<commit_msg>demux: adaptive: fix computing ahead time for live templates<commit_after>/*****************************************************************************
 * SegmentTemplate.cpp: Implement the UrlTemplate element.
 *****************************************************************************
 * Copyright (C) 1998-2007 VLC authors and VideoLAN
 * $Id$
 *
 * Authors: Hugo Beauzée-Luyssen <hugo@beauzee.fr>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program 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 program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
 *****************************************************************************/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#include "SegmentTemplate.h"
#include "SegmentTimeline.h"
#include "SegmentInformation.hpp"
#include "AbstractPlaylist.hpp"

using namespace adaptive::playlist;

BaseSegmentTemplate::BaseSegmentTemplate( ICanonicalUrl *parent ) :
    Segment( parent )
{
}

MediaSegmentTemplate::MediaSegmentTemplate( SegmentInformation *parent ) :
    BaseSegmentTemplate( parent ), Timelineable(), TimescaleAble( parent )
{
    debugName = "SegmentTemplate";
    classId = Segment::CLASSID_SEGMENT;
    startNumber.Set( 1 );
    initialisationSegment.Set( NULL );
    templated = true;
    parentSegmentInformation = parent;
}

void MediaSegmentTemplate::mergeWith(MediaSegmentTemplate *updated, mtime_t prunebarrier)
{
    SegmentTimeline *timeline = segmentTimeline.Get();
    if(timeline && updated->segmentTimeline.Get())
    {
        timeline->mergeWith(*updated->segmentTimeline.Get());
        if(prunebarrier)
        {
            const Timescale timescale = timeline->inheritTimescale();
            const uint64_t number =
                    timeline->getElementNumberByScaledPlaybackTime(timescale.ToScaled(prunebarrier));
            timeline->pruneBySequenceNumber(number);
        }
    }
}

void MediaSegmentTemplate::pruneByPlaybackTime(mtime_t time)
{
    if(segmentTimeline.Get())
        return segmentTimeline.Get()->pruneByPlaybackTime(time);
}

size_t MediaSegmentTemplate::pruneBySequenceNumber(uint64_t number)
{
    if(segmentTimeline.Get())
        return segmentTimeline.Get()->pruneBySequenceNumber(number);
    return 0;
}

uint64_t MediaSegmentTemplate::getCurrentLiveTemplateNumber() const
{
    uint64_t number = startNumber.Get();
    /* live streams / templated */
    const stime_t dur = duration.Get();
    if(dur)
    {
        /* compute, based on current time */
        const time_t playbacktime = time(NULL);
        const Timescale timescale = inheritTimescale();
        time_t streamstart = parentSegmentInformation->getPlaylist()->availabilityStartTime.Get();
        streamstart += parentSegmentInformation->getPeriodStart();
        stime_t elapsed = timescale.ToScaled(CLOCK_FREQ * (playbacktime - streamstart));
        number += elapsed / dur - 2;
    }

    return number;
}

stime_t MediaSegmentTemplate::getMinAheadScaledTime(uint64_t number) const
{
    if( segmentTimeline.Get() )
        return segmentTimeline.Get()->getMinAheadScaledTime(number);

    uint64_t current = getCurrentLiveTemplateNumber();
    return (current - number) * duration.Get();
}

uint64_t MediaSegmentTemplate::getSequenceNumber() const
{
    return startNumber.Get();
}

void MediaSegmentTemplate::setSourceUrl(const std::string &url)
{
    sourceUrl = Url(Url::Component(url, this));
}

void MediaSegmentTemplate::debug(vlc_object_t *obj, int indent) const
{
    Segment::debug(obj, indent);
    if(segmentTimeline.Get())
        segmentTimeline.Get()->debug(obj, indent + 1);
}

InitSegmentTemplate::InitSegmentTemplate( ICanonicalUrl *parent ) :
    BaseSegmentTemplate(parent)
{
    debugName = "InitSegmentTemplate";
    classId = InitSegment::CLASSID_INITSEGMENT;
}
<|endoftext|>