mirror of
https://gitee.com/mirrors_PX4/PX4-Autopilot.git
synced 2026-10-14 05:33:34 +08:00
Merge pull request #73 from UAVCAN/footprint_reduction
Footprint reduction
This commit is contained in:
@@ -167,6 +167,22 @@
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef UAVCAN_LIKELY
|
||||
# if __GNUC__
|
||||
# define UAVCAN_LIKELY(x) __builtin_expect(!!(x), true)
|
||||
# else
|
||||
# define UAVCAN_LIKELY(x) (x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
#ifndef UAVCAN_UNLIKELY
|
||||
# if __GNUC__
|
||||
# define UAVCAN_UNLIKELY(x) __builtin_expect(!!(x), false)
|
||||
# else
|
||||
# define UAVCAN_UNLIKELY(x) (x)
|
||||
# endif
|
||||
#endif
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
/**
|
||||
@@ -239,24 +255,6 @@ static const unsigned MaxCanAcceptanceFilters = UAVCAN_MAX_CAN_ACCEPTANCE_FILTER
|
||||
static const unsigned MaxCanAcceptanceFilters = 32;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This constant defines how many receiver objects will be statically pre-allocated by classes that listen to messages
|
||||
* of type uavcan.protocol.NodeStatus from other nodes. If the number of publishers exceeds this value, extra
|
||||
* listeners will be allocated in the dynamic memory (one block per listener).
|
||||
*
|
||||
* The default value is safe to use in any application (since extra memory can always be retrieved from the pool).
|
||||
* Applications that are extremely memory sensitive and are not expected to operate in large networks can override
|
||||
* the default with a lower value.
|
||||
*
|
||||
* Note that nodes that aren't using classes that monitor the network (e.g. NodeStatusMonitor, dynamic node ID
|
||||
* allocation servers) do not depend on this configuration parameter in any way.
|
||||
*/
|
||||
#ifdef UAVCAN_MAX_NETWORK_SIZE_HINT
|
||||
static const unsigned MaxNetworkSizeHint = UAVCAN_MAX_NETWORK_SIZE_HINT;
|
||||
#else
|
||||
static const unsigned MaxNetworkSizeHint = 64; ///< Rest will go into the dynamic memory
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
#endif // UAVCAN_BUILD_CONFIG_HPP_INCLUDED
|
||||
|
||||
@@ -26,14 +26,30 @@ public:
|
||||
virtual void* allocate(std::size_t size) = 0;
|
||||
virtual void deallocate(const void* ptr) = 0;
|
||||
|
||||
virtual uint16_t getNumBlocks() const = 0;
|
||||
/**
|
||||
* Returns the maximum number of blocks this allocator can allocate.
|
||||
*/
|
||||
virtual uint16_t getBlockCapacity() const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Classic implementation of a pool allocator (Meyers).
|
||||
*
|
||||
* The allocator can be made thread-safe (optional) by means of providing a RAII-lock type via the second template
|
||||
* argument. The allocator uses the lock only to access the shared state, therefore critical sections are only a few
|
||||
* cycles long, which implies that it should be acceptable to use hardware IRQ disabling instead of a mutex for
|
||||
* performance reasons. For example, an IRQ-based RAII-lock type can be implemented as follows:
|
||||
* struct RaiiSynchronizer
|
||||
* {
|
||||
* RaiiSynchronizer() { __disable_irq(); }
|
||||
* ~RaiiSynchronizer() { __enable_irq(); }
|
||||
* };
|
||||
*/
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator, Noncopyable
|
||||
template <std::size_t PoolSize,
|
||||
uint8_t BlockSize,
|
||||
typename RaiiSynchronizer = char>
|
||||
class UAVCAN_EXPORT PoolAllocator : public IPoolAllocator,
|
||||
Noncopyable
|
||||
{
|
||||
union Node
|
||||
{
|
||||
@@ -61,18 +77,33 @@ public:
|
||||
virtual void* allocate(std::size_t size);
|
||||
virtual void deallocate(const void* ptr);
|
||||
|
||||
virtual uint16_t getNumBlocks() const { return NumBlocks; }
|
||||
virtual uint16_t getBlockCapacity() const { return NumBlocks; }
|
||||
|
||||
/**
|
||||
* Return the number of blocks that are currently allocated/unallocated.
|
||||
*/
|
||||
uint16_t getNumUsedBlocks() const { return used_; }
|
||||
uint16_t getNumFreeBlocks() const { return static_cast<uint16_t>(NumBlocks - used_); }
|
||||
uint16_t getNumUsedBlocks() const
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
return used_;
|
||||
}
|
||||
uint16_t getNumFreeBlocks() const
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
return static_cast<uint16_t>(NumBlocks - used_);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum number of blocks that were ever allocated at the same time.
|
||||
*/
|
||||
uint16_t getPeakNumUsedBlocks() const { return max_used_; }
|
||||
uint16_t getPeakNumUsedBlocks() const
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
return max_used_;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -96,7 +127,7 @@ public:
|
||||
virtual void* allocate(std::size_t size);
|
||||
virtual void deallocate(const void* ptr);
|
||||
|
||||
virtual uint16_t getNumBlocks() const;
|
||||
virtual uint16_t getBlockCapacity() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -104,11 +135,11 @@ public:
|
||||
/*
|
||||
* PoolAllocator<>
|
||||
*/
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
const uint16_t PoolAllocator<PoolSize, BlockSize>::NumBlocks;
|
||||
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
|
||||
const uint16_t PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::NumBlocks;
|
||||
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
PoolAllocator<PoolSize, BlockSize>::PoolAllocator() :
|
||||
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
|
||||
PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::PoolAllocator() :
|
||||
free_list_(reinterpret_cast<Node*>(pool_.bytes)),
|
||||
used_(0),
|
||||
max_used_(0)
|
||||
@@ -125,14 +156,17 @@ PoolAllocator<PoolSize, BlockSize>::PoolAllocator() :
|
||||
free_list_[NumBlocks - 1].next = NULL;
|
||||
}
|
||||
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
void* PoolAllocator<PoolSize, BlockSize>::allocate(std::size_t size)
|
||||
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
|
||||
void* PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::allocate(std::size_t size)
|
||||
{
|
||||
if (free_list_ == NULL || size > BlockSize)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
|
||||
void* pmem = free_list_;
|
||||
free_list_ = free_list_->next;
|
||||
|
||||
@@ -147,14 +181,17 @@ void* PoolAllocator<PoolSize, BlockSize>::allocate(std::size_t size)
|
||||
return pmem;
|
||||
}
|
||||
|
||||
template <std::size_t PoolSize, uint8_t BlockSize>
|
||||
void PoolAllocator<PoolSize, BlockSize>::deallocate(const void* ptr)
|
||||
template <std::size_t PoolSize, uint8_t BlockSize, typename RaiiSynchronizer>
|
||||
void PoolAllocator<PoolSize, BlockSize, RaiiSynchronizer>::deallocate(const void* ptr)
|
||||
{
|
||||
if (ptr == NULL)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
|
||||
Node* p = static_cast<Node*>(const_cast<void*>(ptr));
|
||||
p->next = free_list_;
|
||||
free_list_ = p;
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef UAVCAN_HELPERS_HEAP_BASED_POOL_ALLOCATOR_HPP_INCLUDED
|
||||
#define UAVCAN_HELPERS_HEAP_BASED_POOL_ALLOCATOR_HPP_INCLUDED
|
||||
|
||||
#include <cstdlib>
|
||||
#include <uavcan/build_config.hpp>
|
||||
#include <uavcan/debug.hpp>
|
||||
#include <uavcan/dynamic_memory.hpp>
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
/**
|
||||
* A special-purpose implementation of a pool allocator that keeps the pool in the heap using malloc()/free().
|
||||
* The pool grows dynamically, ad-hoc, thus using as little memory as possible.
|
||||
*
|
||||
* Allocated blocks will not be freed back automatically, but there are two ways to force their deallocation:
|
||||
* - Call @ref shrink() - this method frees all blocks that are unused at the moment.
|
||||
* - Destroy the object - the desctructor calls @ref shrink().
|
||||
*
|
||||
* The pool can be limited in growth with hard and soft limits.
|
||||
* The soft limit defines the value that will be reported via @ref IPoolAllocator::getBlockCapacity().
|
||||
* The hard limit defines the maximum number of blocks that can be allocated from heap.
|
||||
* Typically, the hard limit should be equal or greater than the soft limit.
|
||||
*
|
||||
* The allocator can be made thread-safe (optional) by means of providing a RAII-lock type via the second template
|
||||
* argument. The allocator uses the lock only to access the shared state, therefore critical sections are only a few
|
||||
* cycles long, which implies that it should be acceptable to use hardware IRQ disabling instead of a mutex for
|
||||
* performance reasons. For example, an IRQ-based RAII-lock type can be implemented as follows:
|
||||
* struct RaiiSynchronizer
|
||||
* {
|
||||
* RaiiSynchronizer() { __disable_irq(); }
|
||||
* ~RaiiSynchronizer() { __enable_irq(); }
|
||||
* };
|
||||
*/
|
||||
template <std::size_t BlockSize,
|
||||
typename RaiiSynchronizer = char>
|
||||
class UAVCAN_EXPORT HeapBasedPoolAllocator : public IPoolAllocator,
|
||||
Noncopyable
|
||||
{
|
||||
union Node
|
||||
{
|
||||
Node* next;
|
||||
private:
|
||||
uint8_t data[BlockSize];
|
||||
long double _aligner1;
|
||||
long long _aligner2;
|
||||
};
|
||||
|
||||
const uint16_t capacity_soft_limit_;
|
||||
const uint16_t capacity_hard_limit_;
|
||||
|
||||
uint16_t num_reserved_blocks_;
|
||||
uint16_t num_allocated_blocks_;
|
||||
|
||||
Node* reserve_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* The allocator initializes with empty reserve, so first allocations will be served from heap.
|
||||
*
|
||||
* @param block_capacity_soft_limit Block capacity that will be reported via @ref getBlockCapacity().
|
||||
*
|
||||
* @param block_capacity_hard_limit Real block capacity limit; the number of allocated blocks will never
|
||||
* exceed this value. Hard limit should be higher than soft limit.
|
||||
* Default value is two times the soft limit.
|
||||
*/
|
||||
HeapBasedPoolAllocator(uint16_t block_capacity_soft_limit,
|
||||
uint16_t block_capacity_hard_limit = 0) :
|
||||
capacity_soft_limit_(block_capacity_soft_limit),
|
||||
capacity_hard_limit_((block_capacity_hard_limit > 0) ? block_capacity_hard_limit :
|
||||
static_cast<uint16_t>(min(static_cast<uint32_t>(block_capacity_soft_limit) * 2U,
|
||||
static_cast<uint32_t>(NumericTraits<uint16_t>::max())))),
|
||||
num_reserved_blocks_(0),
|
||||
num_allocated_blocks_(0),
|
||||
reserve_(NULL)
|
||||
{ }
|
||||
|
||||
/**
|
||||
* The destructor de-allocates all blocks that are currently in the reserve.
|
||||
* BLOCKS THAT ARE CURRENTLY HELD BY THE APPLICATION WILL LEAK.
|
||||
*/
|
||||
~HeapBasedPoolAllocator()
|
||||
{
|
||||
shrink();
|
||||
#if UAVCAN_DEBUG
|
||||
if (num_allocated_blocks_ > 0)
|
||||
{
|
||||
UAVCAN_TRACE("HeapBasedPoolAllocator", "%u BLOCKS LEAKED", num_allocated_blocks_);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes a block from the reserve, unless it's empty.
|
||||
* In the latter case, allocates a new block in the heap.
|
||||
*/
|
||||
virtual void* allocate(std::size_t size)
|
||||
{
|
||||
if (size > BlockSize)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
|
||||
Node* const p = reserve_;
|
||||
|
||||
if (UAVCAN_LIKELY(p != NULL))
|
||||
{
|
||||
reserve_ = reserve_->next;
|
||||
num_allocated_blocks_++;
|
||||
return p;
|
||||
}
|
||||
|
||||
if (num_reserved_blocks_ >= capacity_hard_limit_) // Hard limit reached, no further allocations
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Unlikely branch
|
||||
void* const m = std::malloc(sizeof(Node));
|
||||
if (m != NULL)
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
|
||||
num_reserved_blocks_++;
|
||||
num_allocated_blocks_++;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the block back to reserve.
|
||||
* The block will not be free()d automatically; see @ref shrink().
|
||||
*/
|
||||
virtual void deallocate(const void* ptr)
|
||||
{
|
||||
if (ptr != NULL)
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
|
||||
Node* const node = static_cast<Node*>(const_cast<void*>(ptr));
|
||||
node->next = reserve_;
|
||||
reserve_ = node;
|
||||
|
||||
num_allocated_blocks_--;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The soft limit.
|
||||
*/
|
||||
virtual uint16_t getBlockCapacity() const { return capacity_soft_limit_; }
|
||||
|
||||
/**
|
||||
* The hard limit.
|
||||
*/
|
||||
uint16_t getBlockCapacityHardLimit() const { return capacity_hard_limit_; }
|
||||
|
||||
/**
|
||||
* Frees all blocks that are not in use at the moment.
|
||||
* Post-condition is getNumAllocatedBlocks() == getNumReservedBlocks().
|
||||
*/
|
||||
void shrink()
|
||||
{
|
||||
Node* p = NULL;
|
||||
for (;;)
|
||||
{
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
// Removing from reserve and updating the counter.
|
||||
p = reserve_;
|
||||
if (p != NULL)
|
||||
{
|
||||
reserve_ = reserve_->next;
|
||||
num_reserved_blocks_--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Then freeing, having left the critical section.
|
||||
std::free(p);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of blocks that are currently in use by the application.
|
||||
*/
|
||||
uint16_t getNumAllocatedBlocks() const
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
return num_allocated_blocks_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of blocks that are acquired from the heap.
|
||||
*/
|
||||
uint16_t getNumReservedBlocks() const
|
||||
{
|
||||
RaiiSynchronizer lock;
|
||||
(void)lock;
|
||||
return num_reserved_blocks_;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -13,8 +13,6 @@
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
|
||||
#if UAVCAN_TINY
|
||||
/**
|
||||
* This function implements fast copy of unaligned bit arrays. It isn't part of the library API, so it is not exported.
|
||||
* @param src_org Source array
|
||||
@@ -25,16 +23,6 @@ namespace uavcan
|
||||
*/
|
||||
void bitarrayCopy(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
|
||||
unsigned char* dst_org, unsigned dst_offset);
|
||||
#else
|
||||
/**
|
||||
* Special cases of @ref bitarrayCopy() - either source or destination must be aligned.
|
||||
* These functions aren't part of the library API, so they are not exported.
|
||||
*/
|
||||
void bitarrayCopyAlignedToUnaligned(const unsigned char* src_org, unsigned src_len,
|
||||
unsigned char* dst_org, unsigned dst_offset);
|
||||
void bitarrayCopyUnalignedToAligned(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
|
||||
unsigned char* dst_org);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This class treats a chunk of memory as an array of bits.
|
||||
@@ -50,7 +38,6 @@ class UAVCAN_EXPORT BitStream
|
||||
|
||||
static inline unsigned bitlenToBytelen(unsigned bits) { return (bits + 7) / 8; }
|
||||
|
||||
#if UAVCAN_TINY
|
||||
static inline void copyBitArrayAlignedToUnaligned(const uint8_t* src_org, unsigned src_len,
|
||||
uint8_t* dst_org, unsigned dst_offset)
|
||||
{
|
||||
@@ -64,21 +51,6 @@ class UAVCAN_EXPORT BitStream
|
||||
bitarrayCopy(reinterpret_cast<const unsigned char*>(src_org), src_offset, src_len,
|
||||
reinterpret_cast<unsigned char*>(dst_org), 0);
|
||||
}
|
||||
#else
|
||||
static inline void copyBitArrayAlignedToUnaligned(const uint8_t* src_org, unsigned src_len,
|
||||
uint8_t* dst_org, unsigned dst_offset)
|
||||
{
|
||||
bitarrayCopyAlignedToUnaligned(reinterpret_cast<const unsigned char*>(src_org), src_len,
|
||||
reinterpret_cast<unsigned char*>(dst_org), dst_offset);
|
||||
}
|
||||
|
||||
static inline void copyBitArrayUnalignedToAligned(const uint8_t* src_org, unsigned src_offset, unsigned src_len,
|
||||
uint8_t* dst_org)
|
||||
{
|
||||
bitarrayCopyUnalignedToAligned(reinterpret_cast<const unsigned char*>(src_org), src_offset, src_len,
|
||||
reinterpret_cast<unsigned char*>(dst_org));
|
||||
}
|
||||
#endif
|
||||
|
||||
public:
|
||||
static const unsigned MaxBitsPerRW = MaxBytesPerRW * 8;
|
||||
|
||||
@@ -100,9 +100,9 @@ protected:
|
||||
|
||||
~GenericSubscriberBase() { }
|
||||
|
||||
int genericStart(TransferListenerBase* listener, bool (Dispatcher::*registration_method)(TransferListenerBase*));
|
||||
int genericStart(TransferListener* listener, bool (Dispatcher::*registration_method)(TransferListener*));
|
||||
|
||||
void stop(TransferListenerBase* listener);
|
||||
void stop(TransferListener* listener);
|
||||
|
||||
public:
|
||||
/**
|
||||
@@ -115,31 +115,6 @@ public:
|
||||
INode& getNode() const { return node_; }
|
||||
};
|
||||
|
||||
/**
|
||||
* This helper class does some compile-time magic on the transport layer machinery. For authorized personnel only.
|
||||
*/
|
||||
template <typename DataStruct_,
|
||||
unsigned NumStaticReceivers_,
|
||||
unsigned NumStaticBufs_,
|
||||
template<unsigned, unsigned, unsigned> class TransferListenerTemplate = TransferListener
|
||||
>
|
||||
class UAVCAN_EXPORT TransferListenerInstantiationHelper
|
||||
{
|
||||
enum { DataTypeMaxByteLen = BitLenToByteLen<DataStruct_::MaxBitLen>::Result };
|
||||
enum { NeedsBuffer = int(DataTypeMaxByteLen) > int(GuaranteedPayloadLenPerFrame) };
|
||||
enum { BufferSize = NeedsBuffer ? DataTypeMaxByteLen : 0 };
|
||||
#if UAVCAN_TINY
|
||||
enum { NumStaticBufs = 0 };
|
||||
enum { NumStaticReceivers = 0 };
|
||||
#else
|
||||
enum { NumStaticBufs = NeedsBuffer ? NumStaticBufs_: 0 };
|
||||
enum { NumStaticReceivers = NumStaticReceivers_ };
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef TransferListenerTemplate<BufferSize, NumStaticBufs, NumStaticReceivers> Type;
|
||||
};
|
||||
|
||||
/**
|
||||
* Please note that the reference passed to the RX callback points to a stack-allocated object, which means
|
||||
* that it gets invalidated shortly after the callback returns.
|
||||
@@ -160,9 +135,15 @@ class UAVCAN_EXPORT GenericSubscriber : public GenericSubscriberBase
|
||||
}
|
||||
|
||||
public:
|
||||
TransferForwarder(SelfType& obj, const DataTypeDescriptor& data_type, IPoolAllocator& allocator)
|
||||
: TransferListenerType(obj.node_.getDispatcher().getTransferPerfCounter(), data_type, allocator)
|
||||
, obj_(obj)
|
||||
TransferForwarder(SelfType& obj,
|
||||
const DataTypeDescriptor& data_type,
|
||||
uint16_t max_buffer_size,
|
||||
IPoolAllocator& allocator) :
|
||||
TransferListenerType(obj.node_.getDispatcher().getTransferPerfCounter(),
|
||||
data_type,
|
||||
max_buffer_size,
|
||||
allocator),
|
||||
obj_(obj)
|
||||
{ }
|
||||
};
|
||||
|
||||
@@ -172,20 +153,19 @@ class UAVCAN_EXPORT GenericSubscriber : public GenericSubscriberBase
|
||||
|
||||
void handleIncomingTransfer(IncomingTransfer& transfer);
|
||||
|
||||
int genericStart(bool (Dispatcher::*registration_method)(TransferListenerBase*));
|
||||
int genericStart(bool (Dispatcher::*registration_method)(TransferListener*));
|
||||
|
||||
protected:
|
||||
struct ReceivedDataStructureSpec : public ReceivedDataStructure<DataStruct>
|
||||
{
|
||||
ReceivedDataStructureSpec() { }
|
||||
|
||||
ReceivedDataStructureSpec(const IncomingTransfer* arg_transfer)
|
||||
: ReceivedDataStructure<DataStruct>(arg_transfer)
|
||||
ReceivedDataStructureSpec(const IncomingTransfer* arg_transfer) :
|
||||
ReceivedDataStructure<DataStruct>(arg_transfer)
|
||||
{ }
|
||||
};
|
||||
|
||||
explicit GenericSubscriber(INode& node)
|
||||
: GenericSubscriberBase(node)
|
||||
explicit GenericSubscriber(INode& node) : GenericSubscriberBase(node)
|
||||
{ }
|
||||
|
||||
virtual ~GenericSubscriber() { stop(); }
|
||||
@@ -256,8 +236,10 @@ int GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::checkInit()
|
||||
return -ErrUnknownDataType;
|
||||
}
|
||||
|
||||
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, IPoolAllocator&>
|
||||
(*this, *descr, node_.getAllocator());
|
||||
static const uint16_t MaxBufferSize = BitLenToByteLen<DataStruct::MaxBitLen>::Result;
|
||||
|
||||
forwarder_.template construct<SelfType&, const DataTypeDescriptor&, uint16_t, IPoolAllocator&>
|
||||
(*this, *descr, MaxBufferSize, node_.getAllocator());
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -295,7 +277,7 @@ void GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::handleIncomi
|
||||
|
||||
template <typename DataSpec, typename DataStruct, typename TransferListenerType>
|
||||
int GenericSubscriber<DataSpec, DataStruct, TransferListenerType>::
|
||||
genericStart(bool (Dispatcher::*registration_method)(TransferListenerBase*))
|
||||
genericStart(bool (Dispatcher::*registration_method)(TransferListener*))
|
||||
{
|
||||
const int res = checkInit();
|
||||
if (res < 0)
|
||||
|
||||
@@ -30,38 +30,20 @@ namespace uavcan
|
||||
* This is the top-level node API.
|
||||
* A custom node class can be implemented if needed, in which case it shall inherit INode.
|
||||
*
|
||||
* @tparam MemPoolSize_ Size of memory pool for this node, in bytes.
|
||||
* Minimum recommended size is 4K * (number of CAN ifaces + 1).
|
||||
* For simple nodes this number can be reduced.
|
||||
* For high-traffic nodes the recommended minimum is
|
||||
* like 16K * (number of CAN ifaces + 1).
|
||||
*
|
||||
* @tparam OutgoingTransferRegistryStaticEntries Number of statically allocated objects
|
||||
* to track Transfer ID for outgoing transfers.
|
||||
* Normally it should be equal to expected number of
|
||||
* publishers and service callers, but it's not necessary.
|
||||
* Additional objects for Transfer ID tracking will
|
||||
* be allocated in the memory pool if needed.
|
||||
* Default value is acceptable for any use case.
|
||||
* @tparam MemPoolSize Size of memory pool for this node, in bytes.
|
||||
* Please refer to the documentation for details.
|
||||
* If this value is zero, the constructor will accept a reference to user-provided allocator.
|
||||
*/
|
||||
template <std::size_t MemPoolSize_,
|
||||
#if UAVCAN_TINY
|
||||
unsigned OutgoingTransferRegistryStaticEntries = 0
|
||||
#else
|
||||
unsigned OutgoingTransferRegistryStaticEntries = 10
|
||||
#endif
|
||||
>
|
||||
template <std::size_t MemPoolSize = 0>
|
||||
class UAVCAN_EXPORT Node : public INode
|
||||
{
|
||||
enum
|
||||
{
|
||||
MemPoolSize = (MemPoolSize_ < std::size_t(MemPoolBlockSize)) ? std::size_t(MemPoolBlockSize) : MemPoolSize_
|
||||
};
|
||||
|
||||
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
|
||||
typedef typename
|
||||
Select<(MemPoolSize > 0),
|
||||
PoolAllocator<MemPoolSize, MemPoolBlockSize>, // If pool size is specified, use default allocator
|
||||
IPoolAllocator& // Otherwise use reference to user-provided allocator
|
||||
>::Result Allocator;
|
||||
|
||||
Allocator pool_allocator_;
|
||||
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
|
||||
Scheduler scheduler_;
|
||||
|
||||
NodeStatusProvider proto_nsp_;
|
||||
@@ -75,6 +57,12 @@ class UAVCAN_EXPORT Node : public INode
|
||||
uint64_t internal_failure_cnt_;
|
||||
bool started_;
|
||||
|
||||
void commonInit()
|
||||
{
|
||||
internal_failure_cnt_ = 0;
|
||||
started_ = false;
|
||||
}
|
||||
|
||||
protected:
|
||||
virtual void registerInternalFailure(const char* msg)
|
||||
{
|
||||
@@ -88,21 +76,43 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
Node(ICanDriver& can_driver, ISystemClock& system_clock)
|
||||
: outgoing_trans_reg_(pool_allocator_)
|
||||
, scheduler_(can_driver, pool_allocator_, system_clock, outgoing_trans_reg_)
|
||||
, proto_nsp_(*this)
|
||||
/**
|
||||
* This overload is only valid if MemPoolSize > 0.
|
||||
*/
|
||||
Node(ICanDriver& can_driver,
|
||||
ISystemClock& system_clock) :
|
||||
scheduler_(can_driver, pool_allocator_, system_clock),
|
||||
proto_nsp_(*this)
|
||||
#if !UAVCAN_TINY
|
||||
, proto_dtp_(*this)
|
||||
, proto_logger_(*this)
|
||||
, proto_rrs_(*this)
|
||||
, proto_tsp_(*this)
|
||||
#endif
|
||||
, internal_failure_cnt_(0)
|
||||
, started_(false)
|
||||
{ }
|
||||
{
|
||||
commonInit();
|
||||
}
|
||||
|
||||
virtual Allocator& getAllocator() { return pool_allocator_; }
|
||||
/**
|
||||
* This overload is only valid if MemPoolSize == 0.
|
||||
*/
|
||||
Node(ICanDriver& can_driver,
|
||||
ISystemClock& system_clock,
|
||||
IPoolAllocator& allocator) :
|
||||
pool_allocator_(allocator),
|
||||
scheduler_(can_driver, pool_allocator_, system_clock),
|
||||
proto_nsp_(*this)
|
||||
#if !UAVCAN_TINY
|
||||
, proto_dtp_(*this)
|
||||
, proto_logger_(*this)
|
||||
, proto_rrs_(*this)
|
||||
, proto_tsp_(*this)
|
||||
#endif
|
||||
{
|
||||
commonInit();
|
||||
}
|
||||
|
||||
virtual typename RemoveReference<Allocator>::Type& getAllocator() { return pool_allocator_; }
|
||||
|
||||
virtual Scheduler& getScheduler() { return scheduler_; }
|
||||
virtual const Scheduler& getScheduler() const { return scheduler_; }
|
||||
@@ -260,9 +270,8 @@ public:
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <std::size_t MemPoolSize_, unsigned OutgoingTransferRegistryStaticEntries>
|
||||
int Node<MemPoolSize_, OutgoingTransferRegistryStaticEntries>::start(
|
||||
const TransferPriority priority)
|
||||
template <std::size_t MemPoolSize_>
|
||||
int Node<MemPoolSize_>::start(const TransferPriority priority)
|
||||
{
|
||||
if (started_)
|
||||
{
|
||||
|
||||
@@ -92,8 +92,8 @@ class UAVCAN_EXPORT Scheduler : Noncopyable
|
||||
void pollCleanup(MonotonicTime mono_ts, uint32_t num_frames_processed_with_last_spin);
|
||||
|
||||
public:
|
||||
Scheduler(ICanDriver& can_driver, IPoolAllocator& allocator, ISystemClock& sysclock, IOutgoingTransferRegistry& otr)
|
||||
: dispatcher_(can_driver, allocator, sysclock, otr)
|
||||
Scheduler(ICanDriver& can_driver, IPoolAllocator& allocator, ISystemClock& sysclock)
|
||||
: dispatcher_(can_driver, allocator, sysclock)
|
||||
, prev_cleanup_ts_(sysclock.getMonotonic())
|
||||
, deadline_resolution_(MonotonicDuration::fromMSec(DefaultDeadlineResolutionMs))
|
||||
, cleanup_period_(MonotonicDuration::fromMSec(DefaultCleanupPeriodMs))
|
||||
|
||||
@@ -20,17 +20,6 @@
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
|
||||
template <typename ServiceDataType, unsigned NumStaticReceiversAndBuffers>
|
||||
class UAVCAN_EXPORT ServiceResponseTransferListenerInstantiationHelper
|
||||
{
|
||||
public: // so much templating it hurts
|
||||
typedef typename TransferListenerInstantiationHelper<typename ServiceDataType::Response,
|
||||
NumStaticReceiversAndBuffers,
|
||||
NumStaticReceiversAndBuffers,
|
||||
TransferListenerWithFilter>::Type Type;
|
||||
};
|
||||
|
||||
/**
|
||||
* This struct describes a pending service call.
|
||||
* Refer to @ref ServiceClient to learn more about service calls.
|
||||
@@ -217,24 +206,17 @@ public:
|
||||
* In C++11 mode this type defaults to std::function<>.
|
||||
* In C++03 mode this type defaults to a plain function pointer; use binder to
|
||||
* call member functions as callbacks.
|
||||
*
|
||||
* @tparam NumStaticCalls_ Number of concurrent calls that the class will be able to handle without using the
|
||||
* memory pool. Note that this is NOT the maximum possible number of concurrent calls,
|
||||
* there's no such limit. Defaults to one.
|
||||
*/
|
||||
template <typename DataType_,
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>,
|
||||
typename Callback_ = std::function<void (const ServiceCallResult<DataType_>&)>
|
||||
#else
|
||||
typename Callback_ = void (*)(const ServiceCallResult<DataType_>&),
|
||||
typename Callback_ = void (*)(const ServiceCallResult<DataType_>&)
|
||||
#endif
|
||||
unsigned NumStaticCalls_ = 1
|
||||
>
|
||||
class UAVCAN_EXPORT ServiceClient
|
||||
: public GenericSubscriber<DataType_,
|
||||
typename DataType_::Response,
|
||||
typename ServiceResponseTransferListenerInstantiationHelper<DataType_,
|
||||
NumStaticCalls_>::Type>
|
||||
typename DataType_::Response, TransferListenerWithFilter>
|
||||
, public ServiceClientBase
|
||||
{
|
||||
public:
|
||||
@@ -244,16 +226,12 @@ public:
|
||||
typedef ServiceCallResult<DataType> ServiceCallResultType;
|
||||
typedef Callback_ Callback;
|
||||
|
||||
enum { NumStaticCalls = NumStaticCalls_ };
|
||||
|
||||
private:
|
||||
typedef ServiceClient<DataType, Callback> SelfType;
|
||||
typedef GenericPublisher<DataType, RequestType> PublisherType;
|
||||
typedef typename ServiceResponseTransferListenerInstantiationHelper<DataType, NumStaticCalls>::Type
|
||||
TransferListenerType;
|
||||
typedef GenericSubscriber<DataType, ResponseType, TransferListenerType> SubscriberType;
|
||||
typedef GenericSubscriber<DataType, ResponseType, TransferListenerWithFilter> SubscriberType;
|
||||
|
||||
typedef Multiset<CallState, NumStaticCalls> CallRegistry;
|
||||
typedef Multiset<CallState> CallRegistry;
|
||||
|
||||
struct TimeoutCallbackCaller
|
||||
{
|
||||
@@ -424,8 +402,8 @@ public:
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(ServiceCallResultType& result)
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::invokeCallback(ServiceCallResultType& result)
|
||||
{
|
||||
if (coerceOrFallback<bool>(callback_, true))
|
||||
{
|
||||
@@ -437,8 +415,8 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::invokeCallback(Servic
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(const RxFrame& frame) const
|
||||
template <typename DataType_, typename Callback_>
|
||||
bool ServiceClient<DataType_, Callback_>::shouldAcceptFrame(const RxFrame& frame) const
|
||||
{
|
||||
UAVCAN_ASSERT(frame.getTransferType() == TransferTypeServiceResponse); // Other types filtered out by dispatcher
|
||||
|
||||
@@ -447,9 +425,8 @@ bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::shouldAcceptFrame(con
|
||||
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::
|
||||
handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
|
||||
{
|
||||
UAVCAN_ASSERT(response.getTransferType() == TransferTypeServiceResponse);
|
||||
|
||||
@@ -460,8 +437,8 @@ handleReceivedDataStruct(ReceivedDataStructure<ResponseType>& response)
|
||||
}
|
||||
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleDeadline(MonotonicTime)
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::handleDeadline(MonotonicTime)
|
||||
{
|
||||
UAVCAN_TRACE("ServiceClient", "Shared deadline event received");
|
||||
/*
|
||||
@@ -484,8 +461,8 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::handleDeadline(Monoto
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCallID call_id)
|
||||
template <typename DataType_, typename Callback_>
|
||||
int ServiceClient<DataType_, Callback_>::addCallState(ServiceCallID call_id)
|
||||
{
|
||||
if (call_registry_.isEmpty())
|
||||
{
|
||||
@@ -507,16 +484,16 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::addCallState(ServiceCa
|
||||
return 0;
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request)
|
||||
template <typename DataType_, typename Callback_>
|
||||
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request)
|
||||
{
|
||||
ServiceCallID dummy;
|
||||
return call(server_node_id, request, dummy);
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_node_id, const RequestType& request,
|
||||
ServiceCallID& out_call_id)
|
||||
template <typename DataType_, typename Callback_>
|
||||
int ServiceClient<DataType_, Callback_>::call(NodeID server_node_id, const RequestType& request,
|
||||
ServiceCallID& out_call_id)
|
||||
{
|
||||
if (!coerceOrFallback<bool>(callback_, true))
|
||||
{
|
||||
@@ -549,7 +526,7 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_nod
|
||||
* Configuring the listener so it will accept only the matching responses
|
||||
* TODO move to init(), but this requires to somewhat refactor GenericSubscriber<> (remove TransferForwarder)
|
||||
*/
|
||||
TransferListenerType* const tl = SubscriberType::getTransferListener();
|
||||
TransferListenerWithFilter* const tl = SubscriberType::getTransferListener();
|
||||
if (tl == NULL)
|
||||
{
|
||||
UAVCAN_ASSERT(0); // Must have been created
|
||||
@@ -573,8 +550,8 @@ int ServiceClient<DataType_, Callback_, NumStaticCalls_>::call(NodeID server_nod
|
||||
return publisher_res;
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelCall(ServiceCallID call_id)
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::cancelCall(ServiceCallID call_id)
|
||||
{
|
||||
call_registry_.removeFirstWhere(CallStateMatchingPredicate(call_id));
|
||||
if (call_registry_.isEmpty())
|
||||
@@ -583,21 +560,21 @@ void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelCall(ServiceCal
|
||||
}
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
void ServiceClient<DataType_, Callback_, NumStaticCalls_>::cancelAllCalls()
|
||||
template <typename DataType_, typename Callback_>
|
||||
void ServiceClient<DataType_, Callback_>::cancelAllCalls()
|
||||
{
|
||||
call_registry_.clear();
|
||||
SubscriberType::stop();
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
bool ServiceClient<DataType_, Callback_, NumStaticCalls_>::hasPendingCallToServer(NodeID server_node_id) const
|
||||
template <typename DataType_, typename Callback_>
|
||||
bool ServiceClient<DataType_, Callback_>::hasPendingCallToServer(NodeID server_node_id) const
|
||||
{
|
||||
return NULL != call_registry_.find(ServerSearchPredicate(server_node_id));
|
||||
}
|
||||
|
||||
template <typename DataType_, typename Callback_, unsigned NumStaticCalls_>
|
||||
ServiceCallID ServiceClient<DataType_, Callback_, NumStaticCalls_>::getCallIDByIndex(unsigned index) const
|
||||
template <typename DataType_, typename Callback_>
|
||||
ServiceCallID ServiceClient<DataType_, Callback_>::getCallIDByIndex(unsigned index) const
|
||||
{
|
||||
const CallState* const id = call_registry_.getByIndex(index);
|
||||
return (id == NULL) ? ServiceCallID() : id->getCallID();
|
||||
|
||||
@@ -77,33 +77,18 @@ public:
|
||||
* In C++11 mode this type defaults to std::function<>.
|
||||
* In C++03 mode this type defaults to a plain function pointer; use binder to
|
||||
* call member functions as callbacks.
|
||||
*
|
||||
* @tparam NumStaticReceivers Number of statically allocated receiver objects. If there's more service
|
||||
* clients for this service, extra receivers will be allocated in the memory pool.
|
||||
*
|
||||
* @tparam NumStaticBufs Number of statically allocated receiver buffers. If there's more concurrent
|
||||
* incoming transfers, extra buffers will be allocated in the memory pool.
|
||||
*/
|
||||
template <typename DataType_,
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
typename Callback_ = std::function<void (const ReceivedDataStructure<typename DataType_::Request>&,
|
||||
ServiceResponseDataStructure<typename DataType_::Response>&)>,
|
||||
ServiceResponseDataStructure<typename DataType_::Response>&)>
|
||||
#else
|
||||
typename Callback_ = void (*)(const ReceivedDataStructure<typename DataType_::Request>&,
|
||||
ServiceResponseDataStructure<typename DataType_::Response>&),
|
||||
#endif
|
||||
#if UAVCAN_TINY
|
||||
unsigned NumStaticReceivers = 0,
|
||||
unsigned NumStaticBufs = 0
|
||||
#else
|
||||
unsigned NumStaticReceivers = 2,
|
||||
unsigned NumStaticBufs = 1
|
||||
ServiceResponseDataStructure<typename DataType_::Response>&)
|
||||
#endif
|
||||
>
|
||||
class UAVCAN_EXPORT ServiceServer
|
||||
: public GenericSubscriber<DataType_, typename DataType_::Request,
|
||||
typename TransferListenerInstantiationHelper<typename DataType_::Request,
|
||||
NumStaticReceivers, NumStaticBufs>::Type>
|
||||
: public GenericSubscriber<DataType_, typename DataType_::Request, TransferListener>
|
||||
{
|
||||
public:
|
||||
typedef DataType_ DataType;
|
||||
@@ -112,9 +97,7 @@ public:
|
||||
typedef Callback_ Callback;
|
||||
|
||||
private:
|
||||
typedef typename TransferListenerInstantiationHelper<RequestType, NumStaticReceivers, NumStaticBufs>::Type
|
||||
TransferListenerType;
|
||||
typedef GenericSubscriber<DataType, RequestType, TransferListenerType> SubscriberType;
|
||||
typedef GenericSubscriber<DataType, RequestType, TransferListener> SubscriberType;
|
||||
typedef GenericPublisher<DataType, ResponseType> PublisherType;
|
||||
|
||||
PublisherType publisher_;
|
||||
|
||||
@@ -20,20 +20,16 @@ namespace uavcan
|
||||
* Please refer to the @ref Node<> for documentation concerning the template arguments; refer to the tutorials
|
||||
* to lean how to use libuavcan in multiprocess applications.
|
||||
*/
|
||||
template <std::size_t MemPoolSize_,
|
||||
unsigned OutgoingTransferRegistryStaticEntries = 10
|
||||
>
|
||||
template <std::size_t MemPoolSize = 0>
|
||||
class UAVCAN_EXPORT SubNode : public INode
|
||||
{
|
||||
enum
|
||||
{
|
||||
MemPoolSize = (MemPoolSize_ < std::size_t(MemPoolBlockSize)) ? std::size_t(MemPoolBlockSize) : MemPoolSize_
|
||||
};
|
||||
|
||||
typedef PoolAllocator<MemPoolSize, MemPoolBlockSize> Allocator;
|
||||
typedef typename
|
||||
Select<(MemPoolSize > 0),
|
||||
PoolAllocator<MemPoolSize, MemPoolBlockSize>, // If pool size is specified, use default allocator
|
||||
IPoolAllocator& // Otherwise use reference to user-provided allocator
|
||||
>::Result Allocator;
|
||||
|
||||
Allocator pool_allocator_;
|
||||
OutgoingTransferRegistry<OutgoingTransferRegistryStaticEntries> outgoing_trans_reg_;
|
||||
Scheduler scheduler_;
|
||||
|
||||
uint64_t internal_failure_cnt_;
|
||||
@@ -47,13 +43,27 @@ protected:
|
||||
}
|
||||
|
||||
public:
|
||||
SubNode(ICanDriver& can_driver, ISystemClock& system_clock) :
|
||||
outgoing_trans_reg_(pool_allocator_),
|
||||
scheduler_(can_driver, pool_allocator_, system_clock, outgoing_trans_reg_),
|
||||
/**
|
||||
* This overload is only valid if MemPoolSize > 0.
|
||||
*/
|
||||
SubNode(ICanDriver& can_driver,
|
||||
ISystemClock& system_clock) :
|
||||
scheduler_(can_driver, pool_allocator_, system_clock),
|
||||
internal_failure_cnt_(0)
|
||||
{ }
|
||||
|
||||
virtual Allocator& getAllocator() { return pool_allocator_; }
|
||||
/**
|
||||
* This overload is only valid if MemPoolSize == 0.
|
||||
*/
|
||||
SubNode(ICanDriver& can_driver,
|
||||
ISystemClock& system_clock,
|
||||
IPoolAllocator& allocator) :
|
||||
pool_allocator_(allocator),
|
||||
scheduler_(can_driver, pool_allocator_, system_clock),
|
||||
internal_failure_cnt_(0)
|
||||
{ }
|
||||
|
||||
virtual typename RemoveReference<Allocator>::Type& getAllocator() { return pool_allocator_; }
|
||||
|
||||
virtual Scheduler& getScheduler() { return scheduler_; }
|
||||
virtual const Scheduler& getScheduler() const { return scheduler_; }
|
||||
|
||||
@@ -34,39 +34,22 @@ namespace uavcan
|
||||
* In C++11 mode this type defaults to std::function<>.
|
||||
* In C++03 mode this type defaults to a plain function pointer; use binder to
|
||||
* call member functions as callbacks.
|
||||
*
|
||||
* @tparam NumStaticReceivers Number of statically allocated receiver objects. If there's more publishers
|
||||
* of this message, extra receivers will be allocated in the memory pool.
|
||||
*
|
||||
* @tparam NumStaticBufs Number of statically allocated receiver buffers. If there's more concurrent
|
||||
* incoming transfers, extra buffers will be allocated in the memory pool.
|
||||
*/
|
||||
template <typename DataType_,
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
typename Callback_ = std::function<void (const ReceivedDataStructure<DataType_>&)>,
|
||||
typename Callback_ = std::function<void (const ReceivedDataStructure<DataType_>&)>
|
||||
#else
|
||||
typename Callback_ = void (*)(const ReceivedDataStructure<DataType_>&),
|
||||
#endif
|
||||
#if UAVCAN_TINY
|
||||
unsigned NumStaticReceivers = 0,
|
||||
unsigned NumStaticBufs = 0
|
||||
#else
|
||||
unsigned NumStaticReceivers = 2,
|
||||
unsigned NumStaticBufs = 1
|
||||
typename Callback_ = void (*)(const ReceivedDataStructure<DataType_>&)
|
||||
#endif
|
||||
>
|
||||
class UAVCAN_EXPORT Subscriber
|
||||
: public GenericSubscriber<DataType_, DataType_,
|
||||
typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers,
|
||||
NumStaticBufs>::Type>
|
||||
: public GenericSubscriber<DataType_, DataType_, TransferListener>
|
||||
{
|
||||
public:
|
||||
typedef Callback_ Callback;
|
||||
|
||||
private:
|
||||
typedef typename TransferListenerInstantiationHelper<DataType_, NumStaticReceivers, NumStaticBufs>::Type
|
||||
TransferListenerType;
|
||||
typedef GenericSubscriber<DataType_, DataType_, TransferListenerType> BaseType;
|
||||
typedef GenericSubscriber<DataType_, DataType_, TransferListener> BaseType;
|
||||
|
||||
Callback callback_;
|
||||
|
||||
|
||||
@@ -126,8 +126,8 @@ private:
|
||||
*/
|
||||
ServiceServer<AppendEntries, AppendEntriesCallback> append_entries_srv_;
|
||||
ServiceClient<AppendEntries, AppendEntriesResponseCallback> append_entries_client_;
|
||||
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
|
||||
ServiceClient<RequestVote, RequestVoteResponseCallback, MaxNumFollowers> request_vote_client_;
|
||||
ServiceServer<RequestVote, RequestVoteCallback> request_vote_srv_;
|
||||
ServiceClient<RequestVote, RequestVoteResponseCallback> request_vote_client_;
|
||||
|
||||
/*
|
||||
* Methods
|
||||
|
||||
@@ -84,7 +84,7 @@ class NodeDiscoverer : TimerBase
|
||||
{ }
|
||||
};
|
||||
|
||||
typedef Map<NodeID, NodeData, 10> NodeMap;
|
||||
typedef Map<NodeID, NodeData> NodeMap;
|
||||
|
||||
/**
|
||||
* When this number of attempts has been made, the discoverer will give up and assume that the node
|
||||
@@ -101,10 +101,10 @@ class NodeDiscoverer : TimerBase
|
||||
IEventTracer& tracer_;
|
||||
|
||||
BitSet<NodeID::Max + 1> committed_node_mask_; ///< Nodes that are marked will not be queried
|
||||
NodeMap node_map_; ///< Will not work in UAVCAN_TINY
|
||||
NodeMap node_map_;
|
||||
|
||||
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback> get_node_info_client_;
|
||||
Subscriber<protocol::NodeStatus, NodeStatusCallback, MaxNetworkSizeHint, 0> node_status_sub_;
|
||||
Subscriber<protocol::NodeStatus, NodeStatusCallback> node_status_sub_;
|
||||
|
||||
/*
|
||||
* Methods
|
||||
|
||||
@@ -31,8 +31,7 @@ class UAVCAN_EXPORT GlobalTimeSyncSlave : Noncopyable
|
||||
void (GlobalTimeSyncSlave::*)(const ReceivedDataStructure<protocol::GlobalTimeSync>&)>
|
||||
GlobalTimeSyncCallback;
|
||||
|
||||
// Static buffers are explicitly disabled because time should never be unicasted.
|
||||
Subscriber<protocol::GlobalTimeSync, GlobalTimeSyncCallback, 2, 0> sub_;
|
||||
Subscriber<protocol::GlobalTimeSync, GlobalTimeSyncCallback> sub_;
|
||||
|
||||
UtcTime prev_ts_utc_;
|
||||
MonotonicTime prev_ts_mono_;
|
||||
|
||||
@@ -156,7 +156,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
enum { NumStaticCalls = 2 };
|
||||
enum { DefaultNumRequestAttempts = 16 };
|
||||
enum { DefaultTimerIntervalMSec = 40 }; ///< Read explanation in the class documentation
|
||||
|
||||
@@ -165,9 +164,9 @@ private:
|
||||
*/
|
||||
Entry entries_[NodeID::Max]; // [1, NodeID::Max]
|
||||
|
||||
Multiset<INodeInfoListener*, 2> listeners_;
|
||||
Multiset<INodeInfoListener*> listeners_;
|
||||
|
||||
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback, NumStaticCalls> get_node_info_client_;
|
||||
ServiceClient<protocol::GetNodeInfo, GetNodeInfoResponseCallback> get_node_info_client_;
|
||||
|
||||
MonotonicDuration request_interval_;
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ private:
|
||||
|
||||
typedef MethodBinder<NodeStatusMonitor*, void (NodeStatusMonitor::*)(const TimerEvent&)> TimerCallback;
|
||||
|
||||
Subscriber<protocol::NodeStatus, NodeStatusCallback, MaxNetworkSizeHint, 0> sub_;
|
||||
Subscriber<protocol::NodeStatus, NodeStatusCallback> sub_;
|
||||
|
||||
TimerEventForwarder<TimerCallback> timer_;
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ private:
|
||||
static const unsigned DefaultAnonMsgMask = 0xFF;
|
||||
static const unsigned DefaultAnonMsgID = 0x0;
|
||||
|
||||
typedef uavcan::Multiset<CanFilterConfig, 1> MultisetConfigContainer;
|
||||
typedef uavcan::Multiset<CanFilterConfig> MultisetConfigContainer;
|
||||
|
||||
static CanFilterConfig mergeFilters(CanFilterConfig &a_, CanFilterConfig &b_);
|
||||
static uint8_t countBits(uint32_t n_);
|
||||
|
||||
@@ -81,19 +81,19 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
|
||||
{
|
||||
CanIOManager canio_;
|
||||
ISystemClock& sysclock_;
|
||||
IOutgoingTransferRegistry& outgoing_transfer_reg_;
|
||||
OutgoingTransferRegistry outgoing_transfer_reg_;
|
||||
TransferPerfCounter perf_;
|
||||
|
||||
class ListenerRegistry
|
||||
{
|
||||
LinkedListRoot<TransferListenerBase> list_;
|
||||
LinkedListRoot<TransferListener> list_;
|
||||
|
||||
class DataTypeIDInsertionComparator
|
||||
{
|
||||
const DataTypeID id_;
|
||||
public:
|
||||
explicit DataTypeIDInsertionComparator(DataTypeID id) : id_(id) { }
|
||||
bool operator()(const TransferListenerBase* listener) const
|
||||
bool operator()(const TransferListener* listener) const
|
||||
{
|
||||
UAVCAN_ASSERT(listener);
|
||||
return id_ > listener->getDataTypeDescriptor().getID();
|
||||
@@ -103,15 +103,15 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
|
||||
public:
|
||||
enum Mode { UniqueListener, ManyListeners };
|
||||
|
||||
bool add(TransferListenerBase* listener, Mode mode);
|
||||
void remove(TransferListenerBase* listener);
|
||||
bool add(TransferListener* listener, Mode mode);
|
||||
void remove(TransferListener* listener);
|
||||
bool exists(DataTypeID dtid) const;
|
||||
void cleanup(MonotonicTime ts);
|
||||
void handleFrame(const RxFrame& frame);
|
||||
|
||||
unsigned getNumEntries() const { return list_.getLength(); }
|
||||
|
||||
const LinkedListRoot<TransferListenerBase>& getList() const { return list_; }
|
||||
const LinkedListRoot<TransferListener>& getList() const { return list_; }
|
||||
};
|
||||
|
||||
ListenerRegistry lmsg_;
|
||||
@@ -133,10 +133,10 @@ class UAVCAN_EXPORT Dispatcher : Noncopyable
|
||||
void notifyRxFrameListener(const CanRxFrame& can_frame, CanIOFlags flags);
|
||||
|
||||
public:
|
||||
Dispatcher(ICanDriver& driver, IPoolAllocator& allocator, ISystemClock& sysclock, IOutgoingTransferRegistry& otr)
|
||||
Dispatcher(ICanDriver& driver, IPoolAllocator& allocator, ISystemClock& sysclock)
|
||||
: canio_(driver, allocator, sysclock)
|
||||
, sysclock_(sysclock)
|
||||
, outgoing_transfer_reg_(otr)
|
||||
, outgoing_transfer_reg_(allocator)
|
||||
#if !UAVCAN_TINY
|
||||
, rx_listener_(NULL)
|
||||
#endif
|
||||
@@ -162,13 +162,13 @@ public:
|
||||
|
||||
void cleanup(MonotonicTime ts);
|
||||
|
||||
bool registerMessageListener(TransferListenerBase* listener);
|
||||
bool registerServiceRequestListener(TransferListenerBase* listener);
|
||||
bool registerServiceResponseListener(TransferListenerBase* listener);
|
||||
bool registerMessageListener(TransferListener* listener);
|
||||
bool registerServiceRequestListener(TransferListener* listener);
|
||||
bool registerServiceResponseListener(TransferListener* listener);
|
||||
|
||||
void unregisterMessageListener(TransferListenerBase* listener);
|
||||
void unregisterServiceRequestListener(TransferListenerBase* listener);
|
||||
void unregisterServiceResponseListener(TransferListenerBase* listener);
|
||||
void unregisterMessageListener(TransferListener* listener);
|
||||
void unregisterServiceRequestListener(TransferListener* listener);
|
||||
void unregisterServiceResponseListener(TransferListener* listener);
|
||||
|
||||
bool hasSubscriber(DataTypeID dtid) const;
|
||||
bool hasPublisher(DataTypeID dtid) const;
|
||||
@@ -185,15 +185,15 @@ public:
|
||||
* removed from this list as soon as the corresponding service call is complete.
|
||||
* @{
|
||||
*/
|
||||
const LinkedListRoot<TransferListenerBase>& getListOfMessageListeners() const
|
||||
const LinkedListRoot<TransferListener>& getListOfMessageListeners() const
|
||||
{
|
||||
return lmsg_.getList();
|
||||
}
|
||||
const LinkedListRoot<TransferListenerBase>& getListOfServiceRequestListeners() const
|
||||
const LinkedListRoot<TransferListener>& getListOfServiceRequestListeners() const
|
||||
{
|
||||
return lsrv_req_.getList();
|
||||
}
|
||||
const LinkedListRoot<TransferListenerBase>& getListOfServiceResponseListeners() const
|
||||
const LinkedListRoot<TransferListener>& getListOfServiceResponseListeners() const
|
||||
{
|
||||
return lsrv_resp_.getList();
|
||||
}
|
||||
@@ -201,7 +201,7 @@ public:
|
||||
* @}
|
||||
*/
|
||||
|
||||
IOutgoingTransferRegistry& getOutgoingTransferRegistry() { return outgoing_transfer_reg_; }
|
||||
OutgoingTransferRegistry& getOutgoingTransferRegistry() { return outgoing_transfer_reg_; }
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
LoopbackFrameListenerRegistry& getLoopbackFrameListenerRegistry() { return loopback_listeners_; }
|
||||
|
||||
@@ -61,21 +61,7 @@ public:
|
||||
* If a local transfer sender was inactive for a sufficiently long time, the outgoing transfer registry will
|
||||
* remove the respective Transfer ID tracking object.
|
||||
*/
|
||||
class UAVCAN_EXPORT IOutgoingTransferRegistry
|
||||
{
|
||||
public:
|
||||
static const MonotonicDuration MinEntryLifetime;
|
||||
|
||||
virtual ~IOutgoingTransferRegistry() { }
|
||||
|
||||
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline) = 0;
|
||||
virtual bool exists(DataTypeID dtid, TransferType tt) const = 0;
|
||||
virtual void cleanup(MonotonicTime deadline) = 0;
|
||||
};
|
||||
|
||||
|
||||
template <int NumStaticEntries>
|
||||
class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry, Noncopyable
|
||||
class UAVCAN_EXPORT OutgoingTransferRegistry : Noncopyable
|
||||
{
|
||||
struct Value
|
||||
{
|
||||
@@ -123,56 +109,22 @@ class UAVCAN_EXPORT OutgoingTransferRegistry : public IOutgoingTransferRegistry,
|
||||
}
|
||||
};
|
||||
|
||||
Map<OutgoingTransferRegistryKey, Value, NumStaticEntries> map_;
|
||||
Map<OutgoingTransferRegistryKey, Value> map_;
|
||||
|
||||
public:
|
||||
static const MonotonicDuration MinEntryLifetime;
|
||||
|
||||
explicit OutgoingTransferRegistry(IPoolAllocator& allocator)
|
||||
: map_(allocator)
|
||||
{ }
|
||||
|
||||
virtual TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline);
|
||||
TransferID* accessOrCreate(const OutgoingTransferRegistryKey& key, MonotonicTime new_deadline);
|
||||
|
||||
virtual bool exists(DataTypeID dtid, TransferType tt) const;
|
||||
bool exists(DataTypeID dtid, TransferType tt) const;
|
||||
|
||||
virtual void cleanup(MonotonicTime ts);
|
||||
void cleanup(MonotonicTime ts);
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* OutgoingTransferRegistry<>
|
||||
*/
|
||||
template <int NumStaticEntries>
|
||||
TransferID* OutgoingTransferRegistry<NumStaticEntries>::accessOrCreate(const OutgoingTransferRegistryKey& key,
|
||||
MonotonicTime new_deadline)
|
||||
{
|
||||
UAVCAN_ASSERT(!new_deadline.isZero());
|
||||
Value* p = map_.access(key);
|
||||
if (p == NULL)
|
||||
{
|
||||
p = map_.insert(key, Value());
|
||||
if (p == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
UAVCAN_TRACE("OutgoingTransferRegistry", "Created %s", key.toString().c_str());
|
||||
}
|
||||
p->deadline = new_deadline;
|
||||
return &p->tid;
|
||||
}
|
||||
|
||||
template <int NumStaticEntries>
|
||||
bool OutgoingTransferRegistry<NumStaticEntries>::exists(DataTypeID dtid, TransferType tt) const
|
||||
{
|
||||
return NULL != map_.find(ExistenceCheckingPredicate(dtid, tt));
|
||||
}
|
||||
|
||||
template <int NumStaticEntries>
|
||||
void OutgoingTransferRegistry<NumStaticEntries>::cleanup(MonotonicTime ts)
|
||||
{
|
||||
map_.removeAllWhere(DeadlineExpiredPredicate(ts));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // UAVCAN_TRANSPORT_OUTGOING_TRANSFER_REGISTRY_HPP_INCLUDED
|
||||
|
||||
@@ -16,6 +16,47 @@
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
/**
|
||||
* Standalone static buffer
|
||||
*/
|
||||
class StaticTransferBufferImpl : public ITransferBuffer
|
||||
{
|
||||
uint8_t* const data_;
|
||||
const uint16_t size_;
|
||||
uint16_t max_write_pos_;
|
||||
|
||||
public:
|
||||
StaticTransferBufferImpl(uint8_t* buf, uint16_t buf_size) :
|
||||
data_(buf),
|
||||
size_(buf_size),
|
||||
max_write_pos_(0)
|
||||
{ }
|
||||
|
||||
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
|
||||
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
|
||||
|
||||
void reset();
|
||||
|
||||
uint16_t getSize() const { return size_; }
|
||||
|
||||
uint8_t* getRawPtr() { return data_; }
|
||||
const uint8_t* getRawPtr() const { return data_; }
|
||||
|
||||
uint16_t getMaxWritePos() const { return max_write_pos_; }
|
||||
void setMaxWritePos(uint16_t value) { max_write_pos_ = value; }
|
||||
};
|
||||
|
||||
template <uint16_t Size>
|
||||
class UAVCAN_EXPORT StaticTransferBuffer : public StaticTransferBufferImpl
|
||||
{
|
||||
uint8_t buffer_[Size];
|
||||
public:
|
||||
StaticTransferBuffer() : StaticTransferBufferImpl(buffer_, Size)
|
||||
{
|
||||
StaticAssert<(Size > 0)>::check();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal for TransferBufferManager
|
||||
*/
|
||||
@@ -53,41 +94,13 @@ public:
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* Internal for TransferBufferManager
|
||||
*/
|
||||
class UAVCAN_EXPORT TransferBufferManagerEntry : public ITransferBuffer, Noncopyable
|
||||
{
|
||||
TransferBufferManagerKey key_;
|
||||
|
||||
protected:
|
||||
virtual void resetImpl() = 0;
|
||||
|
||||
public:
|
||||
TransferBufferManagerEntry() { }
|
||||
|
||||
explicit TransferBufferManagerEntry(const TransferBufferManagerKey& key)
|
||||
: key_(key)
|
||||
{ }
|
||||
|
||||
const TransferBufferManagerKey& getKey() const { return key_; }
|
||||
bool isEmpty() const { return key_.isEmpty(); }
|
||||
|
||||
void reset(const TransferBufferManagerKey& key = TransferBufferManagerKey())
|
||||
{
|
||||
key_ = key;
|
||||
resetImpl();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Resizable gather/scatter storage.
|
||||
* reset() call releases all memory blocks.
|
||||
* Supports unordered write operations - from higher to lower offsets
|
||||
*/
|
||||
class UAVCAN_EXPORT DynamicTransferBufferManagerEntry
|
||||
: public TransferBufferManagerEntry
|
||||
, public LinkedListNode<DynamicTransferBufferManagerEntry>
|
||||
class UAVCAN_EXPORT TransferBufferManagerEntry : public ITransferBuffer
|
||||
, public LinkedListNode<TransferBufferManagerEntry>
|
||||
{
|
||||
struct Block : LinkedListNode<Block>
|
||||
{
|
||||
@@ -107,118 +120,58 @@ class UAVCAN_EXPORT DynamicTransferBufferManagerEntry
|
||||
LinkedListRoot<Block> blocks_; // Blocks are ordered from lower to higher buffer offset
|
||||
uint16_t max_write_pos_;
|
||||
const uint16_t max_size_;
|
||||
|
||||
/// Reset functionality must be implemented in a non-virtual method to call it safely from the destructor.
|
||||
void doReset();
|
||||
|
||||
virtual void resetImpl();
|
||||
TransferBufferManagerKey key_;
|
||||
|
||||
public:
|
||||
DynamicTransferBufferManagerEntry(IPoolAllocator& allocator, uint16_t max_size)
|
||||
: allocator_(allocator)
|
||||
, max_write_pos_(0)
|
||||
, max_size_(max_size)
|
||||
TransferBufferManagerEntry(IPoolAllocator& allocator, uint16_t max_size) :
|
||||
allocator_(allocator),
|
||||
max_write_pos_(0),
|
||||
max_size_(max_size)
|
||||
{
|
||||
StaticAssert<(Block::Size > 8)>::check();
|
||||
IsDynamicallyAllocatable<Block>::check();
|
||||
IsDynamicallyAllocatable<DynamicTransferBufferManagerEntry>::check();
|
||||
IsDynamicallyAllocatable<TransferBufferManagerEntry>::check();
|
||||
}
|
||||
|
||||
virtual ~DynamicTransferBufferManagerEntry()
|
||||
{
|
||||
doReset();
|
||||
}
|
||||
virtual ~TransferBufferManagerEntry() { reset(); }
|
||||
|
||||
static DynamicTransferBufferManagerEntry* instantiate(IPoolAllocator& allocator, uint16_t max_size);
|
||||
static void destroy(DynamicTransferBufferManagerEntry*& obj, IPoolAllocator& allocator);
|
||||
|
||||
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
|
||||
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
|
||||
};
|
||||
|
||||
/**
|
||||
* Standalone static buffer
|
||||
*/
|
||||
class StaticTransferBufferImpl : public ITransferBuffer
|
||||
{
|
||||
uint8_t* const data_;
|
||||
const uint16_t size_;
|
||||
uint16_t max_write_pos_;
|
||||
|
||||
public:
|
||||
StaticTransferBufferImpl(uint8_t* buf, uint16_t buf_size)
|
||||
: data_(buf)
|
||||
, size_(buf_size)
|
||||
, max_write_pos_(0)
|
||||
{ }
|
||||
static TransferBufferManagerEntry* instantiate(IPoolAllocator& allocator, uint16_t max_size);
|
||||
static void destroy(TransferBufferManagerEntry*& obj, IPoolAllocator& allocator);
|
||||
|
||||
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
|
||||
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
|
||||
|
||||
void reset();
|
||||
void reset(const TransferBufferManagerKey& key = TransferBufferManagerKey());
|
||||
|
||||
uint16_t getSize() const { return size_; }
|
||||
|
||||
uint8_t* getRawPtr() { return data_; }
|
||||
const uint8_t* getRawPtr() const { return data_; }
|
||||
|
||||
uint16_t getMaxWritePos() const { return max_write_pos_; }
|
||||
void setMaxWritePos(uint16_t value) { max_write_pos_ = value; }
|
||||
};
|
||||
|
||||
template <uint16_t Size>
|
||||
class UAVCAN_EXPORT StaticTransferBuffer : public StaticTransferBufferImpl
|
||||
{
|
||||
uint8_t buffer_[Size];
|
||||
public:
|
||||
StaticTransferBuffer()
|
||||
: StaticTransferBufferImpl(buffer_, Size)
|
||||
{
|
||||
StaticAssert<(Size > 0)>::check();
|
||||
}
|
||||
const TransferBufferManagerKey& getKey() const { return key_; }
|
||||
bool isEmpty() const { return key_.isEmpty(); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Statically allocated storage for the buffer manager
|
||||
* Buffer manager implementation.
|
||||
*/
|
||||
class StaticTransferBufferManagerEntryImpl : public TransferBufferManagerEntry
|
||||
class TransferBufferManager : public Noncopyable
|
||||
{
|
||||
StaticTransferBufferImpl buf_;
|
||||
LinkedListRoot<TransferBufferManagerEntry> buffers_;
|
||||
IPoolAllocator& allocator_;
|
||||
const uint16_t max_buf_size_;
|
||||
|
||||
virtual void resetImpl();
|
||||
TransferBufferManagerEntry* findFirst(const TransferBufferManagerKey& key);
|
||||
|
||||
public:
|
||||
StaticTransferBufferManagerEntryImpl(uint8_t* buf, uint16_t buf_size)
|
||||
: buf_(buf, buf_size)
|
||||
TransferBufferManager(uint16_t max_buf_size, IPoolAllocator& allocator) :
|
||||
allocator_(allocator),
|
||||
max_buf_size_(max_buf_size)
|
||||
{ }
|
||||
|
||||
virtual int read(unsigned offset, uint8_t* data, unsigned len) const;
|
||||
virtual int write(unsigned offset, const uint8_t* data, unsigned len);
|
||||
~TransferBufferManager();
|
||||
|
||||
bool migrateFrom(const TransferBufferManagerEntry* tbme);
|
||||
};
|
||||
ITransferBuffer* access(const TransferBufferManagerKey& key);
|
||||
ITransferBuffer* create(const TransferBufferManagerKey& key);
|
||||
void remove(const TransferBufferManagerKey& key);
|
||||
bool isEmpty() const;
|
||||
|
||||
template <uint16_t Size>
|
||||
class UAVCAN_EXPORT StaticTransferBufferManagerEntry : public StaticTransferBufferManagerEntryImpl
|
||||
{
|
||||
uint8_t buffer_[Size];
|
||||
public:
|
||||
StaticTransferBufferManagerEntry()
|
||||
: StaticTransferBufferManagerEntryImpl(buffer_, Size)
|
||||
{ }
|
||||
};
|
||||
|
||||
/**
|
||||
* Manages different storage types (static/dynamic) for transfer reception logic.
|
||||
*/
|
||||
class UAVCAN_EXPORT ITransferBufferManager
|
||||
{
|
||||
public:
|
||||
virtual ~ITransferBufferManager() { }
|
||||
virtual ITransferBuffer* access(const TransferBufferManagerKey& key) = 0;
|
||||
virtual ITransferBuffer* create(const TransferBufferManagerKey& key) = 0;
|
||||
virtual void remove(const TransferBufferManagerKey& key) = 0;
|
||||
virtual bool isEmpty() const = 0;
|
||||
unsigned getNumBuffers() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -226,13 +179,13 @@ public:
|
||||
*/
|
||||
class UAVCAN_EXPORT TransferBufferAccessor
|
||||
{
|
||||
ITransferBufferManager& bufmgr_;
|
||||
TransferBufferManager& bufmgr_;
|
||||
const TransferBufferManagerKey key_;
|
||||
|
||||
public:
|
||||
TransferBufferAccessor(ITransferBufferManager& bufmgr, TransferBufferManagerKey key)
|
||||
: bufmgr_(bufmgr)
|
||||
, key_(key)
|
||||
TransferBufferAccessor(TransferBufferManager& bufmgr, TransferBufferManagerKey key) :
|
||||
bufmgr_(bufmgr),
|
||||
key_(key)
|
||||
{
|
||||
UAVCAN_ASSERT(!key.isEmpty());
|
||||
}
|
||||
@@ -241,87 +194,6 @@ public:
|
||||
void remove() { bufmgr_.remove(key_); }
|
||||
};
|
||||
|
||||
/**
|
||||
* Buffer manager implementation.
|
||||
*/
|
||||
class TransferBufferManagerImpl : public ITransferBufferManager, Noncopyable
|
||||
{
|
||||
LinkedListRoot<DynamicTransferBufferManagerEntry> dynamic_buffers_;
|
||||
IPoolAllocator& allocator_;
|
||||
const uint16_t max_buf_size_;
|
||||
|
||||
virtual StaticTransferBufferManagerEntryImpl* getStaticByIndex(uint16_t index) const = 0;
|
||||
|
||||
StaticTransferBufferManagerEntryImpl* findFirstStatic(const TransferBufferManagerKey& key);
|
||||
DynamicTransferBufferManagerEntry* findFirstDynamic(const TransferBufferManagerKey& key);
|
||||
void optimizeStorage();
|
||||
|
||||
public:
|
||||
TransferBufferManagerImpl(uint16_t max_buf_size, IPoolAllocator& allocator)
|
||||
: allocator_(allocator)
|
||||
, max_buf_size_(max_buf_size)
|
||||
{ }
|
||||
|
||||
virtual ~TransferBufferManagerImpl();
|
||||
|
||||
virtual ITransferBuffer* access(const TransferBufferManagerKey& key);
|
||||
virtual ITransferBuffer* create(const TransferBufferManagerKey& key);
|
||||
virtual void remove(const TransferBufferManagerKey& key);
|
||||
virtual bool isEmpty() const;
|
||||
|
||||
unsigned getNumDynamicBuffers() const;
|
||||
unsigned getNumStaticBuffers() const;
|
||||
};
|
||||
|
||||
template <uint16_t MaxBufSize, uint8_t NumStaticBufs>
|
||||
class UAVCAN_EXPORT TransferBufferManager : public TransferBufferManagerImpl
|
||||
{
|
||||
mutable StaticTransferBufferManagerEntry<MaxBufSize> static_buffers_[NumStaticBufs];
|
||||
|
||||
virtual StaticTransferBufferManagerEntry<MaxBufSize>* getStaticByIndex(uint16_t index) const
|
||||
{
|
||||
return (index < NumStaticBufs) ? &static_buffers_[index] : NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit TransferBufferManager(IPoolAllocator& allocator)
|
||||
: TransferBufferManagerImpl(MaxBufSize, allocator)
|
||||
{
|
||||
#if UAVCAN_TINY
|
||||
StaticAssert<(NumStaticBufs == 0)>::check(); // Static buffers in UAVCAN_TINY mode are not allowed
|
||||
#endif
|
||||
StaticAssert<(MaxBufSize > 0)>::check();
|
||||
}
|
||||
};
|
||||
|
||||
template <uint16_t MaxBufSize>
|
||||
class UAVCAN_EXPORT TransferBufferManager<MaxBufSize, 0> : public TransferBufferManagerImpl
|
||||
{
|
||||
virtual StaticTransferBufferManagerEntry<MaxBufSize>* getStaticByIndex(uint16_t) const
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit TransferBufferManager(IPoolAllocator& allocator)
|
||||
: TransferBufferManagerImpl(MaxBufSize, allocator)
|
||||
{
|
||||
StaticAssert<(MaxBufSize > 0)>::check();
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
class UAVCAN_EXPORT TransferBufferManager<0, 0> : public ITransferBufferManager
|
||||
{
|
||||
public:
|
||||
TransferBufferManager() { }
|
||||
TransferBufferManager(IPoolAllocator&) { }
|
||||
virtual ITransferBuffer* access(const TransferBufferManagerKey&) { return NULL; }
|
||||
virtual ITransferBuffer* create(const TransferBufferManagerKey&) { return NULL; }
|
||||
virtual void remove(const TransferBufferManagerKey&) { }
|
||||
virtual bool isEmpty() const { return true; }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // UAVCAN_TRANSPORT_TRANSFER_BUFFER_HPP_INCLUDED
|
||||
|
||||
@@ -96,11 +96,11 @@ public:
|
||||
/**
|
||||
* Internal, refer to the transport dispatcher class.
|
||||
*/
|
||||
class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListenerBase>, Noncopyable
|
||||
class UAVCAN_EXPORT TransferListener : public LinkedListNode<TransferListener>, Noncopyable
|
||||
{
|
||||
const DataTypeDescriptor& data_type_;
|
||||
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers_;
|
||||
ITransferBufferManager& bufmgr_;
|
||||
TransferBufferManager bufmgr_;
|
||||
Map<TransferBufferManagerKey, TransferReceiver> receivers_;
|
||||
TransferPerfCounter& perf_;
|
||||
const TransferCRC crc_base_; ///< Pre-initialized with data type hash, thus constant
|
||||
bool allow_anonymous_transfers_;
|
||||
@@ -108,10 +108,10 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
|
||||
class TimedOutReceiverPredicate
|
||||
{
|
||||
const MonotonicTime ts_;
|
||||
ITransferBufferManager& parent_bufmgr_;
|
||||
TransferBufferManager& parent_bufmgr_;
|
||||
|
||||
public:
|
||||
TimedOutReceiverPredicate(MonotonicTime arg_ts, ITransferBufferManager& arg_bufmgr)
|
||||
TimedOutReceiverPredicate(MonotonicTime arg_ts, TransferBufferManager& arg_bufmgr)
|
||||
: ts_(arg_ts)
|
||||
, parent_bufmgr_(arg_bufmgr)
|
||||
{ }
|
||||
@@ -122,25 +122,24 @@ class UAVCAN_EXPORT TransferListenerBase : public LinkedListNode<TransferListene
|
||||
bool checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const;
|
||||
|
||||
protected:
|
||||
TransferListenerBase(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
|
||||
MapBase<TransferBufferManagerKey, TransferReceiver>& receivers,
|
||||
ITransferBufferManager& bufmgr)
|
||||
: data_type_(data_type)
|
||||
, receivers_(receivers)
|
||||
, bufmgr_(bufmgr)
|
||||
, perf_(perf)
|
||||
, crc_base_(data_type.getSignature().toTransferCRC())
|
||||
, allow_anonymous_transfers_(false)
|
||||
{ }
|
||||
|
||||
virtual ~TransferListenerBase() { }
|
||||
|
||||
void handleReception(TransferReceiver& receiver, const RxFrame& frame, TransferBufferAccessor& tba);
|
||||
void handleAnonymousTransferReception(const RxFrame& frame);
|
||||
|
||||
virtual void handleIncomingTransfer(IncomingTransfer& transfer) = 0;
|
||||
|
||||
public:
|
||||
TransferListener(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
|
||||
uint16_t max_buffer_size, IPoolAllocator& allocator)
|
||||
: data_type_(data_type)
|
||||
, bufmgr_(max_buffer_size, allocator)
|
||||
, receivers_(allocator)
|
||||
, perf_(perf)
|
||||
, crc_base_(data_type.getSignature().toTransferCRC())
|
||||
, allow_anonymous_transfers_(false)
|
||||
{ }
|
||||
|
||||
virtual ~TransferListener();
|
||||
|
||||
const DataTypeDescriptor& getDataTypeDescriptor() const { return data_type_; }
|
||||
|
||||
/**
|
||||
@@ -154,35 +153,6 @@ public:
|
||||
virtual void handleFrame(const RxFrame& frame);
|
||||
};
|
||||
|
||||
/**
|
||||
* This class should be derived by transfer receivers (subscribers, servers).
|
||||
*/
|
||||
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
|
||||
class UAVCAN_EXPORT TransferListener : public TransferListenerBase
|
||||
{
|
||||
TransferBufferManager<MaxBufSize, NumStaticBufs> bufmgr_;
|
||||
Map<TransferBufferManagerKey, TransferReceiver, NumStaticReceivers> receivers_;
|
||||
|
||||
public:
|
||||
TransferListener(TransferPerfCounter& perf, const DataTypeDescriptor& data_type, IPoolAllocator& allocator)
|
||||
: TransferListenerBase(perf, data_type, receivers_, bufmgr_)
|
||||
, bufmgr_(allocator)
|
||||
, receivers_(allocator)
|
||||
{
|
||||
#if UAVCAN_TINY
|
||||
StaticAssert<NumStaticBufs == 0>::check();
|
||||
StaticAssert<NumStaticReceivers == 0>::check();
|
||||
#endif
|
||||
StaticAssert<(NumStaticReceivers >= NumStaticBufs)>::check(); // Otherwise it would be meaningless
|
||||
}
|
||||
|
||||
virtual ~TransferListener()
|
||||
{
|
||||
// Map must be cleared before bufmgr is destroyed
|
||||
receivers_.clear();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is used by transfer listener to decide if the frame should be accepted or ignored.
|
||||
*/
|
||||
@@ -200,19 +170,16 @@ public:
|
||||
/**
|
||||
* This class should be derived by callers.
|
||||
*/
|
||||
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
|
||||
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers>
|
||||
class UAVCAN_EXPORT TransferListenerWithFilter : public TransferListener
|
||||
{
|
||||
const ITransferAcceptanceFilter* filter_;
|
||||
|
||||
virtual void handleFrame(const RxFrame& frame);
|
||||
|
||||
public:
|
||||
typedef TransferListener<MaxBufSize, NumStaticBufs, NumStaticReceivers> BaseType;
|
||||
|
||||
TransferListenerWithFilter(TransferPerfCounter& perf, const DataTypeDescriptor& data_type,
|
||||
IPoolAllocator& allocator)
|
||||
: BaseType(perf, data_type, allocator)
|
||||
uint16_t max_buffer_size, IPoolAllocator& allocator)
|
||||
: TransferListener(perf, data_type, max_buffer_size, allocator)
|
||||
, filter_(NULL)
|
||||
{ }
|
||||
|
||||
@@ -222,27 +189,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* TransferListenerWithFilter<>
|
||||
*/
|
||||
template <unsigned MaxBufSize, unsigned NumStaticBufs, unsigned NumStaticReceivers>
|
||||
void TransferListenerWithFilter<MaxBufSize, NumStaticBufs, NumStaticReceivers>::handleFrame(const RxFrame& frame)
|
||||
{
|
||||
if (filter_ != NULL)
|
||||
{
|
||||
if (filter_->shouldAcceptFrame(frame))
|
||||
{
|
||||
BaseType::handleFrame(frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif // UAVCAN_TRANSPORT_TRANSFER_LISTENER_HPP_INCLUDED
|
||||
|
||||
@@ -18,9 +18,7 @@ namespace uavcan
|
||||
/**
|
||||
* Slow but memory efficient KV container.
|
||||
*
|
||||
* KV pairs can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
|
||||
* When a KV pair is deleted from the static buffer, one pair from the memory pool will be moved in the free
|
||||
* slot of the static buffer, so the use of the memory pool is minimized.
|
||||
* KV pairs will be allocated in the node's memory pool.
|
||||
*
|
||||
* Please be aware that this container does not perform any speed optimizations to minimize memory footprint,
|
||||
* so the complexity of most operations is O(N).
|
||||
@@ -32,10 +30,8 @@ namespace uavcan
|
||||
* Size of Key + Value + padding must not exceed MemPoolBlockSize.
|
||||
*/
|
||||
template <typename Key, typename Value>
|
||||
class UAVCAN_EXPORT MapBase : Noncopyable
|
||||
class UAVCAN_EXPORT Map : Noncopyable
|
||||
{
|
||||
template <typename, typename, unsigned> friend class Map;
|
||||
|
||||
public:
|
||||
struct KVPair
|
||||
{
|
||||
@@ -102,16 +98,9 @@ private:
|
||||
|
||||
LinkedListRoot<KVGroup> list_;
|
||||
IPoolAllocator& allocator_;
|
||||
#if !UAVCAN_TINY
|
||||
KVPair* const static_;
|
||||
const unsigned num_static_entries_;
|
||||
#endif
|
||||
|
||||
KVPair* findKey(const Key& key);
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
void optimizeStorage();
|
||||
#endif
|
||||
void compact();
|
||||
|
||||
struct YesPredicate
|
||||
@@ -119,30 +108,18 @@ private:
|
||||
bool operator()(const Key&, const Value&) const { return true; }
|
||||
};
|
||||
|
||||
protected:
|
||||
#if UAVCAN_TINY
|
||||
MapBase(IPoolAllocator& allocator)
|
||||
: allocator_(allocator)
|
||||
{
|
||||
UAVCAN_ASSERT(Key() == Key());
|
||||
}
|
||||
#else
|
||||
MapBase(KVPair* static_buf, unsigned num_static_entries, IPoolAllocator& allocator)
|
||||
: allocator_(allocator)
|
||||
, static_(static_buf)
|
||||
, num_static_entries_(num_static_entries)
|
||||
{
|
||||
UAVCAN_ASSERT(Key() == Key());
|
||||
}
|
||||
#endif
|
||||
|
||||
/// Derived class destructor must call clear();
|
||||
~MapBase()
|
||||
{
|
||||
UAVCAN_ASSERT(getSize() == 0);
|
||||
}
|
||||
|
||||
public:
|
||||
Map(IPoolAllocator& allocator) :
|
||||
allocator_(allocator)
|
||||
{
|
||||
UAVCAN_ASSERT(Key() == Key());
|
||||
}
|
||||
|
||||
~Map()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns null pointer if there's no such entry.
|
||||
*/
|
||||
@@ -192,69 +169,20 @@ public:
|
||||
*/
|
||||
bool isEmpty() const { return find(YesPredicate()) == NULL; }
|
||||
|
||||
unsigned getSize() const;
|
||||
|
||||
/**
|
||||
* For testing, do not use directly.
|
||||
* Complexity is O(N).
|
||||
*/
|
||||
unsigned getNumStaticPairs() const;
|
||||
unsigned getNumDynamicPairs() const;
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename Value, unsigned NumStaticEntries = 0>
|
||||
class UAVCAN_EXPORT Map : public MapBase<Key, Value>
|
||||
{
|
||||
typename MapBase<Key, Value>::KVPair static_[NumStaticEntries];
|
||||
|
||||
public:
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
|
||||
// This instantiation will not be valid in UAVCAN_TINY mode
|
||||
explicit Map(IPoolAllocator& allocator)
|
||||
: MapBase<Key, Value>(static_, NumStaticEntries, allocator)
|
||||
{ }
|
||||
|
||||
~Map() { this->clear(); }
|
||||
|
||||
#endif // !UAVCAN_TINY
|
||||
};
|
||||
|
||||
|
||||
template <typename Key, typename Value>
|
||||
class UAVCAN_EXPORT Map<Key, Value, 0> : public MapBase<Key, Value>
|
||||
{
|
||||
public:
|
||||
explicit Map(IPoolAllocator& allocator)
|
||||
#if UAVCAN_TINY
|
||||
: MapBase<Key, Value>(allocator)
|
||||
#else
|
||||
: MapBase<Key, Value>(NULL, 0, allocator)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
~Map() { this->clear(); }
|
||||
unsigned getSize() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
* MapBase<>
|
||||
* Map<>
|
||||
*/
|
||||
template <typename Key, typename Value>
|
||||
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& key)
|
||||
typename Map<Key, Value>::KVPair* Map<Key, Value>::findKey(const Key& key)
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (static_[i].match(key))
|
||||
{
|
||||
return static_ + i;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
KVGroup* p = list_.get();
|
||||
while (p)
|
||||
{
|
||||
@@ -268,64 +196,8 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::findKey(const Key& ke
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void MapBase<Key, Value>::optimizeStorage()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
// Looking for first EMPTY static entry
|
||||
KVPair* stat = NULL;
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (static_[i].match(Key()))
|
||||
{
|
||||
stat = static_ + i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stat == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Looking for the first NON-EMPTY dynamic entry, erasing immediately
|
||||
KVGroup* p = list_.get();
|
||||
KVPair dyn;
|
||||
while (p)
|
||||
{
|
||||
bool stop = false;
|
||||
for (int i = 0; i < KVGroup::NumKV; i++)
|
||||
{
|
||||
if (!p->kvs[i].match(Key())) // Non empty
|
||||
{
|
||||
dyn = p->kvs[i]; // Copy by value
|
||||
p->kvs[i] = KVPair(); // Erase immediately
|
||||
stop = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (stop)
|
||||
{
|
||||
break;
|
||||
}
|
||||
p = p->getNextListNode();
|
||||
}
|
||||
if (dyn.match(Key()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// Migrating
|
||||
*stat = dyn;
|
||||
}
|
||||
}
|
||||
|
||||
#endif // !UAVCAN_TINY
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void MapBase<Key, Value>::compact()
|
||||
void Map<Key, Value>::compact()
|
||||
{
|
||||
KVGroup* p = list_.get();
|
||||
while (p)
|
||||
@@ -350,7 +222,7 @@ void MapBase<Key, Value>::compact()
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
Value* MapBase<Key, Value>::access(const Key& key)
|
||||
Value* Map<Key, Value>::access(const Key& key)
|
||||
{
|
||||
UAVCAN_ASSERT(!(key == Key()));
|
||||
KVPair* const kv = findKey(key);
|
||||
@@ -358,7 +230,7 @@ Value* MapBase<Key, Value>::access(const Key& key)
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
|
||||
Value* Map<Key, Value>::insert(const Key& key, const Value& value)
|
||||
{
|
||||
UAVCAN_ASSERT(!(key == Key()));
|
||||
remove(key);
|
||||
@@ -381,40 +253,23 @@ Value* MapBase<Key, Value>::insert(const Key& key, const Value& value)
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void MapBase<Key, Value>::remove(const Key& key)
|
||||
void Map<Key, Value>::remove(const Key& key)
|
||||
{
|
||||
UAVCAN_ASSERT(!(key == Key()));
|
||||
KVPair* const kv = findKey(key);
|
||||
if (kv)
|
||||
{
|
||||
*kv = KVPair();
|
||||
#if !UAVCAN_TINY
|
||||
optimizeStorage();
|
||||
#endif
|
||||
compact();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Predicate>
|
||||
void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
|
||||
void Map<Key, Value>::removeAllWhere(Predicate predicate)
|
||||
{
|
||||
unsigned num_removed = 0;
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (!static_[i].match(Key()))
|
||||
{
|
||||
if (predicate(static_[i].key, static_[i].value))
|
||||
{
|
||||
num_removed++;
|
||||
static_[i] = KVPair();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
KVGroup* p = list_.get();
|
||||
while (p != NULL)
|
||||
{
|
||||
@@ -438,30 +293,14 @@ void MapBase<Key, Value>::removeAllWhere(Predicate predicate)
|
||||
|
||||
if (num_removed > 0)
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
optimizeStorage();
|
||||
#endif
|
||||
compact();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
template <typename Predicate>
|
||||
const Key* MapBase<Key, Value>::find(Predicate predicate) const
|
||||
const Key* Map<Key, Value>::find(Predicate predicate) const
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (!static_[i].match(Key()))
|
||||
{
|
||||
if (predicate(static_[i].key, static_[i].value))
|
||||
{
|
||||
return &static_[i].key;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
KVGroup* p = list_.get();
|
||||
while (p != NULL)
|
||||
{
|
||||
@@ -485,29 +324,14 @@ const Key* MapBase<Key, Value>::find(Predicate predicate) const
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
void MapBase<Key, Value>::clear()
|
||||
void Map<Key, Value>::clear()
|
||||
{
|
||||
removeAllWhere(YesPredicate());
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index)
|
||||
typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index)
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
// Checking the static storage
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (!static_[i].match(Key()))
|
||||
{
|
||||
if (index == 0)
|
||||
{
|
||||
return static_ + i;
|
||||
}
|
||||
index--;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Slowly crawling through the dynamic storage
|
||||
KVGroup* p = list_.get();
|
||||
while (p != NULL)
|
||||
@@ -534,35 +358,13 @@ typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned i
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
const typename MapBase<Key, Value>::KVPair* MapBase<Key, Value>::getByIndex(unsigned index) const
|
||||
const typename Map<Key, Value>::KVPair* Map<Key, Value>::getByIndex(unsigned index) const
|
||||
{
|
||||
return const_cast<MapBase<Key, Value>*>(this)->getByIndex(index);
|
||||
return const_cast<Map<Key, Value>*>(this)->getByIndex(index);
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
unsigned MapBase<Key, Value>::getSize() const
|
||||
{
|
||||
return getNumStaticPairs() + getNumDynamicPairs();
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
unsigned MapBase<Key, Value>::getNumStaticPairs() const
|
||||
{
|
||||
unsigned num = 0;
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < num_static_entries_; i++)
|
||||
{
|
||||
if (!static_[i].match(Key()))
|
||||
{
|
||||
num++;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return num;
|
||||
}
|
||||
|
||||
template <typename Key, typename Value>
|
||||
unsigned MapBase<Key, Value>::getNumDynamicPairs() const
|
||||
unsigned Map<Key, Value>::getSize() const
|
||||
{
|
||||
unsigned num = 0;
|
||||
KVGroup* p = list_.get();
|
||||
|
||||
@@ -23,11 +23,9 @@ namespace uavcan
|
||||
* Slow but memory efficient unordered multiset. Unlike Map<>, this container does not move objects, so
|
||||
* they don't have to be copyable.
|
||||
*
|
||||
* Items can be allocated in a static buffer or in the node's memory pool if the static buffer is exhausted.
|
||||
*
|
||||
* Number of static entries must not be less than 1.
|
||||
* Items will be allocated in the node's memory pool.
|
||||
*/
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
template <typename T>
|
||||
class UAVCAN_EXPORT Multiset : Noncopyable
|
||||
{
|
||||
struct Item : ::uavcan::Noncopyable
|
||||
@@ -122,7 +120,6 @@ private:
|
||||
*/
|
||||
LinkedListRoot<Chunk> list_;
|
||||
IPoolAllocator& allocator_;
|
||||
Item static_[NumStaticEntries];
|
||||
|
||||
/*
|
||||
* Methods
|
||||
@@ -333,13 +330,7 @@ public:
|
||||
* Counts number of items stored.
|
||||
* Best case complexity is O(N).
|
||||
*/
|
||||
unsigned getSize() const { return getNumStaticItems() + getNumDynamicItems(); }
|
||||
|
||||
/**
|
||||
* For testing, do not use directly.
|
||||
*/
|
||||
unsigned getNumStaticItems() const;
|
||||
unsigned getNumDynamicItems() const;
|
||||
unsigned getSize() const;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
@@ -347,21 +338,10 @@ public:
|
||||
/*
|
||||
* Multiset<>
|
||||
*/
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::findOrCreateFreeSlot()
|
||||
template <typename T>
|
||||
typename Multiset<T>::Item* Multiset<T>::findOrCreateFreeSlot()
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
// Search in static pool
|
||||
for (unsigned i = 0; i < NumStaticEntries; i++)
|
||||
{
|
||||
if (!static_[i].isConstructed())
|
||||
{
|
||||
return &static_[i];
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Search in dynamic pool
|
||||
// Search
|
||||
{
|
||||
Chunk* p = list_.get();
|
||||
while (p)
|
||||
@@ -375,7 +355,7 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
|
||||
}
|
||||
}
|
||||
|
||||
// Create new dynamic chunk
|
||||
// Create new chunk
|
||||
Chunk* const chunk = Chunk::instantiate(allocator_);
|
||||
if (chunk == NULL)
|
||||
{
|
||||
@@ -385,8 +365,8 @@ typename Multiset<T, NumStaticEntries>::Item* Multiset<T, NumStaticEntries>::fin
|
||||
return &chunk->items[0];
|
||||
}
|
||||
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
void Multiset<T, NumStaticEntries>::compact()
|
||||
template <typename T>
|
||||
void Multiset<T>::compact()
|
||||
{
|
||||
Chunk* p = list_.get();
|
||||
while (p)
|
||||
@@ -410,30 +390,12 @@ void Multiset<T, NumStaticEntries>::compact()
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
template <typename T>
|
||||
template <typename Predicate>
|
||||
void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
|
||||
void Multiset<T>::removeWhere(Predicate predicate, const RemoveStrategy strategy)
|
||||
{
|
||||
unsigned num_removed = 0;
|
||||
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < NumStaticEntries; i++)
|
||||
{
|
||||
if (static_[i].isConstructed())
|
||||
{
|
||||
if (predicate(*static_[i].ptr))
|
||||
{
|
||||
num_removed++;
|
||||
static_[i].destroy();
|
||||
if (strategy == RemoveOne)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Chunk* p = list_.get();
|
||||
while (p != NULL)
|
||||
{
|
||||
@@ -470,23 +432,10 @@ void Multiset<T, NumStaticEntries>::removeWhere(Predicate predicate, const Remov
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
template <typename T>
|
||||
template <typename Predicate>
|
||||
T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
|
||||
T* Multiset<T>::find(Predicate predicate)
|
||||
{
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < NumStaticEntries; i++)
|
||||
{
|
||||
if (static_[i].isConstructed())
|
||||
{
|
||||
if (predicate(*static_[i].ptr))
|
||||
{
|
||||
return static_[i].ptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
Chunk* p = list_.get();
|
||||
while (p != NULL)
|
||||
{
|
||||
@@ -508,21 +457,8 @@ T* Multiset<T, NumStaticEntries>::find(Predicate predicate)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
unsigned Multiset<T, NumStaticEntries>::getNumStaticItems() const
|
||||
{
|
||||
unsigned num = 0;
|
||||
#if !UAVCAN_TINY
|
||||
for (unsigned i = 0; i < NumStaticEntries; i++)
|
||||
{
|
||||
num += static_[i].isConstructed() ? 1U : 0U;
|
||||
}
|
||||
#endif
|
||||
return num;
|
||||
}
|
||||
|
||||
template <typename T, unsigned NumStaticEntries>
|
||||
unsigned Multiset<T, NumStaticEntries>::getNumDynamicItems() const
|
||||
template <typename T>
|
||||
unsigned Multiset<T>::getSize() const
|
||||
{
|
||||
unsigned num = 0;
|
||||
Chunk* p = list_.get();
|
||||
|
||||
@@ -14,8 +14,6 @@ namespace uavcan
|
||||
static const unsigned char reverse_mask[] = { 0x00U, 0x80U, 0xC0U, 0xE0U, 0xF0U, 0xF8U, 0xFCU, 0xFEU, 0xFFU };
|
||||
static const unsigned char reverse_mask_xor[] = { 0xFFU, 0x7FU, 0x3FU, 0x1FU, 0x0FU, 0x07U, 0x03U, 0x01U, 0x00U };
|
||||
|
||||
#if UAVCAN_TINY
|
||||
|
||||
#define PREPARE_FIRST_COPY() \
|
||||
do { \
|
||||
if (src_len >= (CHAR_BIT - dst_offset_modulo)) { \
|
||||
@@ -120,150 +118,4 @@ void bitarrayCopy(const unsigned char* src_org, unsigned src_offset, unsigned sr
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Functions below were manually optimized in the most horrible way.
|
||||
*/
|
||||
|
||||
void bitarrayCopyAlignedToUnaligned(const unsigned char* src_org, unsigned src_len,
|
||||
unsigned char* dst_org, unsigned dst_offset)
|
||||
{
|
||||
if (src_len > 0U)
|
||||
{
|
||||
unsigned char* dst = dst_org + (dst_offset / CHAR_BIT);
|
||||
const unsigned dst_offset_modulo = dst_offset % CHAR_BIT;
|
||||
|
||||
if (0U == dst_offset_modulo)
|
||||
{
|
||||
const unsigned byte_len = src_len / CHAR_BIT;
|
||||
const unsigned src_len_modulo = src_len % CHAR_BIT;
|
||||
|
||||
if (byte_len > 0U)
|
||||
{
|
||||
(void)std::memcpy(dst, src_org, byte_len);
|
||||
src_org += byte_len;
|
||||
dst += byte_len;
|
||||
}
|
||||
if (src_len_modulo > 0U)
|
||||
{
|
||||
*dst &= reverse_mask_xor[src_len_modulo];
|
||||
*dst |= reverse_mask[src_len_modulo] & *src_org;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned bit_diff_ls = CHAR_BIT - dst_offset_modulo;
|
||||
unsigned char c =
|
||||
static_cast<unsigned char>(*src_org >> dst_offset_modulo & reverse_mask_xor[dst_offset_modulo]);
|
||||
|
||||
if (src_len >= (CHAR_BIT - dst_offset_modulo))
|
||||
{
|
||||
*dst &= reverse_mask[dst_offset_modulo];
|
||||
src_len -= CHAR_BIT - dst_offset_modulo;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dst &= reverse_mask[dst_offset_modulo] | reverse_mask_xor[dst_offset_modulo + src_len + 1];
|
||||
c &= reverse_mask[dst_offset_modulo + src_len];
|
||||
src_len = 0;
|
||||
}
|
||||
|
||||
*dst++ |= c;
|
||||
|
||||
int byte_len = int(src_len / CHAR_BIT);
|
||||
|
||||
while (--byte_len >= 0)
|
||||
{
|
||||
c = static_cast<unsigned char>(*src_org++ << bit_diff_ls);
|
||||
c = static_cast<unsigned char>(c | (*src_org >> dst_offset_modulo));
|
||||
*dst++ = c;
|
||||
}
|
||||
|
||||
const unsigned src_len_modulo = src_len % CHAR_BIT;
|
||||
if (src_len_modulo > 0U)
|
||||
{
|
||||
c = static_cast<unsigned char>(*src_org++ << bit_diff_ls);
|
||||
c = static_cast<unsigned char>(c | (*src_org >> dst_offset_modulo));
|
||||
c &= reverse_mask[src_len_modulo];
|
||||
|
||||
*dst &= reverse_mask_xor[src_len_modulo];
|
||||
*dst |= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void bitarrayCopyUnalignedToAligned(const unsigned char* src_org, unsigned src_offset, unsigned src_len,
|
||||
unsigned char* dst_org)
|
||||
{
|
||||
if (src_len > 0U)
|
||||
{
|
||||
const unsigned char* src = src_org + (src_offset / CHAR_BIT);
|
||||
|
||||
const unsigned src_offset_modulo = src_offset % CHAR_BIT;
|
||||
|
||||
if (src_offset_modulo == 0U)
|
||||
{
|
||||
const unsigned byte_len = src_len / CHAR_BIT;
|
||||
const unsigned src_len_modulo = src_len % CHAR_BIT;
|
||||
|
||||
if (byte_len > 0U)
|
||||
{
|
||||
(void)std::memcpy(dst_org, src, byte_len);
|
||||
src += byte_len;
|
||||
dst_org += byte_len;
|
||||
}
|
||||
if (src_len_modulo > 0U)
|
||||
{
|
||||
*dst_org &= reverse_mask_xor[src_len_modulo];
|
||||
*dst_org |= reverse_mask[src_len_modulo] & *src;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
const unsigned bit_diff_rs = CHAR_BIT - src_offset_modulo;
|
||||
|
||||
unsigned char c = static_cast<unsigned char>(*src++ << src_offset_modulo);
|
||||
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
|
||||
|
||||
if (src_len >= CHAR_BIT)
|
||||
{
|
||||
*dst_org &= 0x55U;
|
||||
src_len -= CHAR_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
*dst_org &= 0x55U | reverse_mask_xor[src_len + 1];
|
||||
c &= reverse_mask[src_len];
|
||||
src_len = 0;
|
||||
}
|
||||
|
||||
*dst_org++ |= c;
|
||||
|
||||
int byte_len = int(src_len / CHAR_BIT);
|
||||
|
||||
while (--byte_len >= 0)
|
||||
{
|
||||
c = static_cast<unsigned char>(*src++ << src_offset_modulo);
|
||||
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
|
||||
*dst_org++ = c;
|
||||
}
|
||||
|
||||
const unsigned src_len_modulo = src_len % CHAR_BIT;
|
||||
if (src_len_modulo > 0U)
|
||||
{
|
||||
c = static_cast<unsigned char>(*src++ << src_offset_modulo);
|
||||
c = static_cast<unsigned char>(c | (*src >> bit_diff_rs));
|
||||
c &= reverse_mask[src_len_modulo];
|
||||
|
||||
*dst_org &= reverse_mask_xor[src_len_modulo];
|
||||
*dst_org |= c;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
namespace uavcan
|
||||
{
|
||||
|
||||
int GenericSubscriberBase::genericStart(TransferListenerBase* listener,
|
||||
bool (Dispatcher::*registration_method)(TransferListenerBase*))
|
||||
int GenericSubscriberBase::genericStart(TransferListener* listener,
|
||||
bool (Dispatcher::*registration_method)(TransferListener*))
|
||||
{
|
||||
if (listener == NULL)
|
||||
{
|
||||
@@ -24,7 +24,7 @@ int GenericSubscriberBase::genericStart(TransferListenerBase* listener,
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GenericSubscriberBase::stop(TransferListenerBase* listener)
|
||||
void GenericSubscriberBase::stop(TransferListener* listener)
|
||||
{
|
||||
if (listener != NULL)
|
||||
{
|
||||
|
||||
@@ -38,8 +38,8 @@ int16_t CanAcceptanceFilterConfigurator::loadInputConfiguration(AnonymousMessage
|
||||
return -ErrMemory;
|
||||
}
|
||||
|
||||
const TransferListenerBase* p = node_.getDispatcher().getListOfMessageListeners().get();
|
||||
while (p)
|
||||
const TransferListener* p = node_.getDispatcher().getListOfMessageListeners().get();
|
||||
while (p != NULL)
|
||||
{
|
||||
CanFilterConfig cfg;
|
||||
cfg.id = static_cast<uint32_t>(p->getDataTypeDescriptor().getID().get()) << 8;
|
||||
|
||||
@@ -295,10 +295,10 @@ CanIOManager::CanIOManager(ICanDriver& driver, IPoolAllocator& allocator, ISyste
|
||||
|
||||
if (mem_blocks_per_iface == 0)
|
||||
{
|
||||
mem_blocks_per_iface = allocator.getNumBlocks() / (num_ifaces_ + 1U) + 1U;
|
||||
mem_blocks_per_iface = allocator.getBlockCapacity() / (num_ifaces_ + 1U) + 1U;
|
||||
}
|
||||
UAVCAN_TRACE("CanIOManager", "Memory blocks per iface: %u, total: %u",
|
||||
unsigned(mem_blocks_per_iface), unsigned(allocator.getNumBlocks()));
|
||||
unsigned(mem_blocks_per_iface), unsigned(allocator.getBlockCapacity()));
|
||||
|
||||
for (int i = 0; i < num_ifaces_; i++)
|
||||
{
|
||||
|
||||
@@ -72,11 +72,11 @@ void LoopbackFrameListenerRegistry::invokeListeners(RxFrame& frame)
|
||||
/*
|
||||
* Dispatcher::ListenerRegister
|
||||
*/
|
||||
bool Dispatcher::ListenerRegistry::add(TransferListenerBase* listener, Mode mode)
|
||||
bool Dispatcher::ListenerRegistry::add(TransferListener* listener, Mode mode)
|
||||
{
|
||||
if (mode == UniqueListener)
|
||||
{
|
||||
TransferListenerBase* p = list_.get();
|
||||
TransferListener* p = list_.get();
|
||||
while (p)
|
||||
{
|
||||
if (p->getDataTypeDescriptor().getID() == listener->getDataTypeDescriptor().getID())
|
||||
@@ -91,14 +91,14 @@ bool Dispatcher::ListenerRegistry::add(TransferListenerBase* listener, Mode mode
|
||||
return true;
|
||||
}
|
||||
|
||||
void Dispatcher::ListenerRegistry::remove(TransferListenerBase* listener)
|
||||
void Dispatcher::ListenerRegistry::remove(TransferListener* listener)
|
||||
{
|
||||
list_.remove(listener);
|
||||
}
|
||||
|
||||
bool Dispatcher::ListenerRegistry::exists(DataTypeID dtid) const
|
||||
{
|
||||
TransferListenerBase* p = list_.get();
|
||||
TransferListener* p = list_.get();
|
||||
while (p)
|
||||
{
|
||||
if (p->getDataTypeDescriptor().getID() == dtid)
|
||||
@@ -112,10 +112,10 @@ bool Dispatcher::ListenerRegistry::exists(DataTypeID dtid) const
|
||||
|
||||
void Dispatcher::ListenerRegistry::cleanup(MonotonicTime ts)
|
||||
{
|
||||
TransferListenerBase* p = list_.get();
|
||||
TransferListener* p = list_.get();
|
||||
while (p)
|
||||
{
|
||||
TransferListenerBase* const next = p->getNextListNode();
|
||||
TransferListener* const next = p->getNextListNode();
|
||||
p->cleanup(ts); // p may be modified
|
||||
p = next;
|
||||
}
|
||||
@@ -123,10 +123,10 @@ void Dispatcher::ListenerRegistry::cleanup(MonotonicTime ts)
|
||||
|
||||
void Dispatcher::ListenerRegistry::handleFrame(const RxFrame& frame)
|
||||
{
|
||||
TransferListenerBase* p = list_.get();
|
||||
TransferListener* p = list_.get();
|
||||
while (p)
|
||||
{
|
||||
TransferListenerBase* const next = p->getNextListNode();
|
||||
TransferListener* const next = p->getNextListNode();
|
||||
if (p->getDataTypeDescriptor().getID() == frame.getDataTypeID())
|
||||
{
|
||||
p->handleFrame(frame); // p may be modified
|
||||
@@ -311,7 +311,7 @@ void Dispatcher::cleanup(MonotonicTime ts)
|
||||
lsrv_resp_.cleanup(ts);
|
||||
}
|
||||
|
||||
bool Dispatcher::registerMessageListener(TransferListenerBase* listener)
|
||||
bool Dispatcher::registerMessageListener(TransferListener* listener)
|
||||
{
|
||||
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindMessage)
|
||||
{
|
||||
@@ -321,7 +321,7 @@ bool Dispatcher::registerMessageListener(TransferListenerBase* listener)
|
||||
return lmsg_.add(listener, ListenerRegistry::ManyListeners); // Multiple subscribers are OK
|
||||
}
|
||||
|
||||
bool Dispatcher::registerServiceRequestListener(TransferListenerBase* listener)
|
||||
bool Dispatcher::registerServiceRequestListener(TransferListener* listener)
|
||||
{
|
||||
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindService)
|
||||
{
|
||||
@@ -331,7 +331,7 @@ bool Dispatcher::registerServiceRequestListener(TransferListenerBase* listener)
|
||||
return lsrv_req_.add(listener, ListenerRegistry::UniqueListener); // Only one server per data type
|
||||
}
|
||||
|
||||
bool Dispatcher::registerServiceResponseListener(TransferListenerBase* listener)
|
||||
bool Dispatcher::registerServiceResponseListener(TransferListener* listener)
|
||||
{
|
||||
if (listener->getDataTypeDescriptor().getKind() != DataTypeKindService)
|
||||
{
|
||||
@@ -341,17 +341,17 @@ bool Dispatcher::registerServiceResponseListener(TransferListenerBase* listener)
|
||||
return lsrv_resp_.add(listener, ListenerRegistry::ManyListeners); // Multiple callers may call same srv
|
||||
}
|
||||
|
||||
void Dispatcher::unregisterMessageListener(TransferListenerBase* listener)
|
||||
void Dispatcher::unregisterMessageListener(TransferListener* listener)
|
||||
{
|
||||
lmsg_.remove(listener);
|
||||
}
|
||||
|
||||
void Dispatcher::unregisterServiceRequestListener(TransferListenerBase* listener)
|
||||
void Dispatcher::unregisterServiceRequestListener(TransferListener* listener)
|
||||
{
|
||||
lsrv_req_.remove(listener);
|
||||
}
|
||||
|
||||
void Dispatcher::unregisterServiceResponseListener(TransferListenerBase* listener)
|
||||
void Dispatcher::unregisterServiceResponseListener(TransferListener* listener)
|
||||
{
|
||||
lsrv_resp_.remove(listener);
|
||||
}
|
||||
|
||||
@@ -20,8 +20,36 @@ std::string OutgoingTransferRegistryKey::toString() const
|
||||
#endif
|
||||
|
||||
/*
|
||||
* IOutgoingTransferRegistry
|
||||
* OutgoingTransferRegistry
|
||||
*/
|
||||
const MonotonicDuration IOutgoingTransferRegistry::MinEntryLifetime = MonotonicDuration::fromMSec(2000);
|
||||
const MonotonicDuration OutgoingTransferRegistry::MinEntryLifetime = MonotonicDuration::fromMSec(2000);
|
||||
|
||||
TransferID* OutgoingTransferRegistry::accessOrCreate(const OutgoingTransferRegistryKey& key,
|
||||
MonotonicTime new_deadline)
|
||||
{
|
||||
UAVCAN_ASSERT(!new_deadline.isZero());
|
||||
Value* p = map_.access(key);
|
||||
if (p == NULL)
|
||||
{
|
||||
p = map_.insert(key, Value());
|
||||
if (p == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
UAVCAN_TRACE("OutgoingTransferRegistry", "Created %s", key.toString().c_str());
|
||||
}
|
||||
p->deadline = new_deadline;
|
||||
return &p->tid;
|
||||
}
|
||||
|
||||
bool OutgoingTransferRegistry::exists(DataTypeID dtid, TransferType tt) const
|
||||
{
|
||||
return NULL != map_.find(ExistenceCheckingPredicate(dtid, tt));
|
||||
}
|
||||
|
||||
void OutgoingTransferRegistry::cleanup(MonotonicTime ts)
|
||||
{
|
||||
map_.removeAllWhere(DeadlineExpiredPredicate(ts));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,58 @@
|
||||
|
||||
namespace uavcan
|
||||
{
|
||||
/*
|
||||
* StaticTransferBufferImpl
|
||||
*/
|
||||
int StaticTransferBufferImpl::read(unsigned offset, uint8_t* data, unsigned len) const
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
if (offset >= max_write_pos_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((offset + len) > max_write_pos_)
|
||||
{
|
||||
len = max_write_pos_ - offset;
|
||||
}
|
||||
UAVCAN_ASSERT((offset + len) <= max_write_pos_);
|
||||
(void)copy(data_ + offset, data_ + offset + len, data);
|
||||
return int(len);
|
||||
}
|
||||
|
||||
int StaticTransferBufferImpl::write(unsigned offset, const uint8_t* data, unsigned len)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
if (offset >= size_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((offset + len) > size_)
|
||||
{
|
||||
len = size_ - offset;
|
||||
}
|
||||
UAVCAN_ASSERT((offset + len) <= size_);
|
||||
(void)copy(data, data + len, data_ + offset);
|
||||
max_write_pos_ = max(uint16_t(offset + len), uint16_t(max_write_pos_));
|
||||
return int(len);
|
||||
}
|
||||
|
||||
void StaticTransferBufferImpl::reset()
|
||||
{
|
||||
max_write_pos_ = 0;
|
||||
#if UAVCAN_DEBUG
|
||||
fill(data_, data_ + size_, uint8_t(0));
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* TransferBufferManagerKey
|
||||
*/
|
||||
@@ -23,8 +75,8 @@ std::string TransferBufferManagerKey::toString() const
|
||||
/*
|
||||
* DynamicTransferBuffer::Block
|
||||
*/
|
||||
DynamicTransferBufferManagerEntry::Block*
|
||||
DynamicTransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
|
||||
TransferBufferManagerEntry::Block*
|
||||
TransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
|
||||
{
|
||||
void* const praw = allocator.allocate(sizeof(Block));
|
||||
if (praw == NULL)
|
||||
@@ -34,7 +86,7 @@ DynamicTransferBufferManagerEntry::Block::instantiate(IPoolAllocator& allocator)
|
||||
return new (praw) Block;
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocator& allocator)
|
||||
void TransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocator& allocator)
|
||||
{
|
||||
if (obj != NULL)
|
||||
{
|
||||
@@ -44,8 +96,8 @@ void DynamicTransferBufferManagerEntry::Block::destroy(Block*& obj, IPoolAllocat
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned target_offset,
|
||||
unsigned& total_offset, unsigned& left_to_read)
|
||||
void TransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned target_offset,
|
||||
unsigned& total_offset, unsigned& left_to_read)
|
||||
{
|
||||
UAVCAN_ASSERT(outptr);
|
||||
for (unsigned i = 0; (i < Block::Size) && (left_to_read > 0); i++, total_offset++)
|
||||
@@ -58,7 +110,7 @@ void DynamicTransferBufferManagerEntry::Block::read(uint8_t*& outptr, unsigned t
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsigned target_offset,
|
||||
void TransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsigned target_offset,
|
||||
unsigned& total_offset, unsigned& left_to_write)
|
||||
{
|
||||
UAVCAN_ASSERT(inptr);
|
||||
@@ -75,46 +127,28 @@ void DynamicTransferBufferManagerEntry::Block::write(const uint8_t*& inptr, unsi
|
||||
/*
|
||||
* DynamicTransferBuffer
|
||||
*/
|
||||
DynamicTransferBufferManagerEntry* DynamicTransferBufferManagerEntry::instantiate(IPoolAllocator& allocator,
|
||||
TransferBufferManagerEntry* TransferBufferManagerEntry::instantiate(IPoolAllocator& allocator,
|
||||
uint16_t max_size)
|
||||
{
|
||||
void* const praw = allocator.allocate(sizeof(DynamicTransferBufferManagerEntry));
|
||||
void* const praw = allocator.allocate(sizeof(TransferBufferManagerEntry));
|
||||
if (praw == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return new (praw) DynamicTransferBufferManagerEntry(allocator, max_size);
|
||||
return new (praw) TransferBufferManagerEntry(allocator, max_size);
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::destroy(DynamicTransferBufferManagerEntry*& obj, IPoolAllocator& allocator)
|
||||
void TransferBufferManagerEntry::destroy(TransferBufferManagerEntry*& obj, IPoolAllocator& allocator)
|
||||
{
|
||||
if (obj != NULL)
|
||||
{
|
||||
obj->~DynamicTransferBufferManagerEntry();
|
||||
obj->~TransferBufferManagerEntry();
|
||||
allocator.deallocate(obj);
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::doReset()
|
||||
{
|
||||
max_write_pos_ = 0;
|
||||
Block* p = blocks_.get();
|
||||
while (p)
|
||||
{
|
||||
Block* const next = p->getNextListNode();
|
||||
blocks_.remove(p);
|
||||
Block::destroy(p, allocator_);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
void DynamicTransferBufferManagerEntry::resetImpl()
|
||||
{
|
||||
doReset();
|
||||
}
|
||||
|
||||
int DynamicTransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsigned len) const
|
||||
int TransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsigned len) const
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
@@ -150,7 +184,7 @@ int DynamicTransferBufferManagerEntry::read(unsigned offset, uint8_t* data, unsi
|
||||
return int(len);
|
||||
}
|
||||
|
||||
int DynamicTransferBufferManagerEntry::write(unsigned offset, const uint8_t* data, unsigned len)
|
||||
int TransferBufferManagerEntry::write(unsigned offset, const uint8_t* data, unsigned len)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
@@ -221,131 +255,26 @@ int DynamicTransferBufferManagerEntry::write(unsigned offset, const uint8_t* dat
|
||||
return int(actually_written);
|
||||
}
|
||||
|
||||
/*
|
||||
* StaticTransferBufferImpl
|
||||
*/
|
||||
int StaticTransferBufferImpl::read(unsigned offset, uint8_t* data, unsigned len) const
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
if (offset >= max_write_pos_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((offset + len) > max_write_pos_)
|
||||
{
|
||||
len = max_write_pos_ - offset;
|
||||
}
|
||||
UAVCAN_ASSERT((offset + len) <= max_write_pos_);
|
||||
(void)copy(data_ + offset, data_ + offset + len, data);
|
||||
return int(len);
|
||||
}
|
||||
|
||||
int StaticTransferBufferImpl::write(unsigned offset, const uint8_t* data, unsigned len)
|
||||
{
|
||||
if (!data)
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return -ErrInvalidParam;
|
||||
}
|
||||
if (offset >= size_)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if ((offset + len) > size_)
|
||||
{
|
||||
len = size_ - offset;
|
||||
}
|
||||
UAVCAN_ASSERT((offset + len) <= size_);
|
||||
(void)copy(data, data + len, data_ + offset);
|
||||
max_write_pos_ = max(uint16_t(offset + len), uint16_t(max_write_pos_));
|
||||
return int(len);
|
||||
}
|
||||
|
||||
void StaticTransferBufferImpl::reset()
|
||||
void TransferBufferManagerEntry::reset(const TransferBufferManagerKey& key)
|
||||
{
|
||||
key_ = key;
|
||||
max_write_pos_ = 0;
|
||||
#if UAVCAN_DEBUG
|
||||
fill(data_, data_ + size_, uint8_t(0));
|
||||
#endif
|
||||
Block* p = blocks_.get();
|
||||
while (p)
|
||||
{
|
||||
Block* const next = p->getNextListNode();
|
||||
blocks_.remove(p);
|
||||
Block::destroy(p, allocator_);
|
||||
p = next;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* StaticTransferBufferManagerEntryImpl
|
||||
* TransferBufferManager
|
||||
*/
|
||||
void StaticTransferBufferManagerEntryImpl::resetImpl()
|
||||
TransferBufferManagerEntry* TransferBufferManager::findFirst(const TransferBufferManagerKey& key)
|
||||
{
|
||||
buf_.reset();
|
||||
}
|
||||
|
||||
int StaticTransferBufferManagerEntryImpl::read(unsigned offset, uint8_t* data, unsigned len) const
|
||||
{
|
||||
return buf_.read(offset, data, len);
|
||||
}
|
||||
|
||||
int StaticTransferBufferManagerEntryImpl::write(unsigned offset, const uint8_t* data, unsigned len)
|
||||
{
|
||||
return buf_.write(offset, data, len);
|
||||
}
|
||||
|
||||
bool StaticTransferBufferManagerEntryImpl::migrateFrom(const TransferBufferManagerEntry* tbme)
|
||||
{
|
||||
if (tbme == NULL || tbme->isEmpty())
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Resetting self and moving all data from the source
|
||||
TransferBufferManagerEntry::reset(tbme->getKey());
|
||||
const int res = tbme->read(0, buf_.getRawPtr(), buf_.getSize());
|
||||
if (res < 0)
|
||||
{
|
||||
TransferBufferManagerEntry::reset();
|
||||
return false;
|
||||
}
|
||||
buf_.setMaxWritePos(uint16_t(res));
|
||||
if (res < int(buf_.getSize()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Now we need to make sure that all data can fit the storage
|
||||
uint8_t dummy = 0;
|
||||
if (tbme->read(buf_.getSize(), &dummy, 1) > 0)
|
||||
{
|
||||
TransferBufferManagerEntry::reset(); // Damn, the buffer was too large
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* TransferBufferManagerImpl
|
||||
*/
|
||||
StaticTransferBufferManagerEntryImpl* TransferBufferManagerImpl::findFirstStatic(const TransferBufferManagerKey& key)
|
||||
{
|
||||
for (unsigned i = 0; true; i++)
|
||||
{
|
||||
StaticTransferBufferManagerEntryImpl* const sb = getStaticByIndex(uint16_t(i));
|
||||
if (sb == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (sb->getKey() == key)
|
||||
{
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
DynamicTransferBufferManagerEntry* TransferBufferManagerImpl::findFirstDynamic(const TransferBufferManagerKey& key)
|
||||
{
|
||||
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
|
||||
TransferBufferManagerEntry* dyn = buffers_.get();
|
||||
while (dyn)
|
||||
{
|
||||
UAVCAN_ASSERT(!dyn->isEmpty());
|
||||
@@ -358,68 +287,29 @@ DynamicTransferBufferManagerEntry* TransferBufferManagerImpl::findFirstDynamic(c
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void TransferBufferManagerImpl::optimizeStorage()
|
||||
TransferBufferManager::~TransferBufferManager()
|
||||
{
|
||||
while (!dynamic_buffers_.isEmpty())
|
||||
{
|
||||
StaticTransferBufferManagerEntryImpl* const sb = findFirstStatic(TransferBufferManagerKey());
|
||||
if (sb == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
|
||||
UAVCAN_ASSERT(dyn);
|
||||
UAVCAN_ASSERT(!dyn->isEmpty());
|
||||
if (sb->migrateFrom(dyn))
|
||||
{
|
||||
UAVCAN_ASSERT(!dyn->isEmpty());
|
||||
UAVCAN_TRACE("TransferBufferManager", "Storage optimization: Migrated %s",
|
||||
dyn->getKey().toString().c_str());
|
||||
dynamic_buffers_.remove(dyn);
|
||||
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Migration can fail if a dynamic buffer contains more data than a static buffer can accomodate.
|
||||
* This should never happen during normal operation because dynamic buffers are limited in growth.
|
||||
*/
|
||||
UAVCAN_TRACE("TransferBufferManager", "Storage optimization: MIGRATION FAILURE %s MAXSIZE %u",
|
||||
dyn->getKey().toString().c_str(), max_buf_size_);
|
||||
UAVCAN_ASSERT(0);
|
||||
sb->reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TransferBufferManagerImpl::~TransferBufferManagerImpl()
|
||||
{
|
||||
DynamicTransferBufferManagerEntry* dyn = dynamic_buffers_.get();
|
||||
TransferBufferManagerEntry* dyn = buffers_.get();
|
||||
while (dyn)
|
||||
{
|
||||
DynamicTransferBufferManagerEntry* const next = dyn->getNextListNode();
|
||||
dynamic_buffers_.remove(dyn);
|
||||
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
|
||||
TransferBufferManagerEntry* const next = dyn->getNextListNode();
|
||||
buffers_.remove(dyn);
|
||||
TransferBufferManagerEntry::destroy(dyn, allocator_);
|
||||
dyn = next;
|
||||
}
|
||||
}
|
||||
|
||||
ITransferBuffer* TransferBufferManagerImpl::access(const TransferBufferManagerKey& key)
|
||||
ITransferBuffer* TransferBufferManager::access(const TransferBufferManagerKey& key)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
return NULL;
|
||||
}
|
||||
TransferBufferManagerEntry* tbme = findFirstStatic(key);
|
||||
if (tbme)
|
||||
{
|
||||
return tbme;
|
||||
}
|
||||
return findFirstDynamic(key);
|
||||
return findFirst(key);
|
||||
}
|
||||
|
||||
ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKey& key)
|
||||
ITransferBuffer* TransferBufferManager::create(const TransferBufferManagerKey& key)
|
||||
{
|
||||
if (key.isEmpty())
|
||||
{
|
||||
@@ -428,27 +318,17 @@ ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKe
|
||||
}
|
||||
remove(key);
|
||||
|
||||
TransferBufferManagerEntry* tbme = findFirstStatic(TransferBufferManagerKey());
|
||||
TransferBufferManagerEntry* tbme = TransferBufferManagerEntry::instantiate(allocator_, max_buf_size_);
|
||||
if (tbme == NULL)
|
||||
{
|
||||
DynamicTransferBufferManagerEntry* dyn =
|
||||
DynamicTransferBufferManagerEntry::instantiate(allocator_, max_buf_size_);
|
||||
tbme = dyn;
|
||||
if (dyn == NULL)
|
||||
{
|
||||
return NULL; // Epic fail.
|
||||
}
|
||||
dynamic_buffers_.insert(dyn);
|
||||
UAVCAN_TRACE("TransferBufferManager", "Dynamic buffer created [st=%u, dyn=%u], %s",
|
||||
getNumStaticBuffers(), getNumDynamicBuffers(), key.toString().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
UAVCAN_TRACE("TransferBufferManager", "Static buffer created [st=%u, dyn=%u], %s",
|
||||
getNumStaticBuffers(), getNumDynamicBuffers(), key.toString().c_str());
|
||||
return NULL; // Epic fail.
|
||||
}
|
||||
|
||||
if (tbme)
|
||||
buffers_.insert(tbme);
|
||||
|
||||
UAVCAN_TRACE("TransferBufferManager", "Buffer created [num=%u], %s", getNumBuffers(), key.toString().c_str());
|
||||
|
||||
if (tbme != NULL)
|
||||
{
|
||||
UAVCAN_ASSERT(tbme->isEmpty());
|
||||
tbme->reset(key);
|
||||
@@ -456,54 +336,27 @@ ITransferBuffer* TransferBufferManagerImpl::create(const TransferBufferManagerKe
|
||||
return tbme;
|
||||
}
|
||||
|
||||
void TransferBufferManagerImpl::remove(const TransferBufferManagerKey& key)
|
||||
void TransferBufferManager::remove(const TransferBufferManagerKey& key)
|
||||
{
|
||||
UAVCAN_ASSERT(!key.isEmpty());
|
||||
|
||||
TransferBufferManagerEntry* const tbme = findFirstStatic(key);
|
||||
if (tbme)
|
||||
TransferBufferManagerEntry* dyn = findFirst(key);
|
||||
if (dyn != NULL)
|
||||
{
|
||||
UAVCAN_TRACE("TransferBufferManager", "Static buffer deleted, %s", key.toString().c_str());
|
||||
tbme->reset();
|
||||
optimizeStorage();
|
||||
return;
|
||||
}
|
||||
|
||||
DynamicTransferBufferManagerEntry* dyn = findFirstDynamic(key);
|
||||
if (dyn)
|
||||
{
|
||||
UAVCAN_TRACE("TransferBufferManager", "Dynamic buffer deleted, %s", key.toString().c_str());
|
||||
dynamic_buffers_.remove(dyn);
|
||||
DynamicTransferBufferManagerEntry::destroy(dyn, allocator_);
|
||||
UAVCAN_TRACE("TransferBufferManager", "Buffer deleted, %s", key.toString().c_str());
|
||||
buffers_.remove(dyn);
|
||||
TransferBufferManagerEntry::destroy(dyn, allocator_);
|
||||
}
|
||||
}
|
||||
|
||||
bool TransferBufferManagerImpl::isEmpty() const
|
||||
bool TransferBufferManager::isEmpty() const
|
||||
{
|
||||
return (getNumStaticBuffers() == 0) && (getNumDynamicBuffers() == 0);
|
||||
return getNumBuffers() == 0;
|
||||
}
|
||||
|
||||
unsigned TransferBufferManagerImpl::getNumDynamicBuffers() const
|
||||
unsigned TransferBufferManager::getNumBuffers() const
|
||||
{
|
||||
return dynamic_buffers_.getLength();
|
||||
}
|
||||
|
||||
unsigned TransferBufferManagerImpl::getNumStaticBuffers() const
|
||||
{
|
||||
unsigned res = 0;
|
||||
for (unsigned i = 0; true; i++)
|
||||
{
|
||||
StaticTransferBufferManagerEntryImpl* const sb = getStaticByIndex(uint16_t(i));
|
||||
if (sb == NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (!sb->isEmpty())
|
||||
{
|
||||
res++;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
return buffers_.getLength();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -80,10 +80,10 @@ int MultiFrameIncomingTransfer::read(unsigned offset, uint8_t* data, unsigned le
|
||||
}
|
||||
|
||||
/*
|
||||
* TransferListenerBase::TimedOutReceiverPredicate
|
||||
* TransferListener::TimedOutReceiverPredicate
|
||||
*/
|
||||
bool TransferListenerBase::TimedOutReceiverPredicate::operator()(const TransferBufferManagerKey& key,
|
||||
const TransferReceiver& value) const
|
||||
bool TransferListener::TimedOutReceiverPredicate::operator()(const TransferBufferManagerKey& key,
|
||||
const TransferReceiver& value) const
|
||||
{
|
||||
if (value.isTimedOut(ts_))
|
||||
{
|
||||
@@ -101,9 +101,9 @@ bool TransferListenerBase::TimedOutReceiverPredicate::operator()(const TransferB
|
||||
}
|
||||
|
||||
/*
|
||||
* TransferListenerBase
|
||||
* TransferListener
|
||||
*/
|
||||
bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const
|
||||
bool TransferListener::checkPayloadCrc(const uint16_t compare_with, const ITransferBuffer& tbb) const
|
||||
{
|
||||
TransferCRC crc = crc_base_;
|
||||
unsigned offset = 0;
|
||||
@@ -113,7 +113,7 @@ bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const IT
|
||||
const int res = tbb.read(offset, buf, sizeof(buf));
|
||||
if (res < 0)
|
||||
{
|
||||
UAVCAN_TRACE("TransferListenerBase", "Failed to check CRC: Buffer read failure %i", res);
|
||||
UAVCAN_TRACE("TransferListener", "Failed to check CRC: Buffer read failure %i", res);
|
||||
return false;
|
||||
}
|
||||
if (res == 0)
|
||||
@@ -125,14 +125,14 @@ bool TransferListenerBase::checkPayloadCrc(const uint16_t compare_with, const IT
|
||||
}
|
||||
if (crc.get() != compare_with)
|
||||
{
|
||||
UAVCAN_TRACE("TransferListenerBase", "CRC mismatch, expected=0x%04x, got=0x%04x",
|
||||
UAVCAN_TRACE("TransferListener", "CRC mismatch, expected=0x%04x, got=0x%04x",
|
||||
int(compare_with), int(crc.get()));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxFrame& frame,
|
||||
void TransferListener::handleReception(TransferReceiver& receiver, const RxFrame& frame,
|
||||
TransferBufferAccessor& tba)
|
||||
{
|
||||
switch (receiver.addFrame(frame, tba))
|
||||
@@ -155,12 +155,12 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF
|
||||
const ITransferBuffer* tbb = tba.access();
|
||||
if (tbb == NULL)
|
||||
{
|
||||
UAVCAN_TRACE("TransferListenerBase", "Buffer access failure, last frame: %s", frame.toString().c_str());
|
||||
UAVCAN_TRACE("TransferListener", "Buffer access failure, last frame: %s", frame.toString().c_str());
|
||||
break;
|
||||
}
|
||||
if (!checkPayloadCrc(receiver.getLastTransferCrc(), *tbb))
|
||||
{
|
||||
UAVCAN_TRACE("TransferListenerBase", "CRC error, last frame: %s", frame.toString().c_str());
|
||||
UAVCAN_TRACE("TransferListener", "CRC error, last frame: %s", frame.toString().c_str());
|
||||
break;
|
||||
}
|
||||
MultiFrameIncomingTransfer it(receiver.getLastTransferTimestampMonotonic(),
|
||||
@@ -177,7 +177,7 @@ void TransferListenerBase::handleReception(TransferReceiver& receiver, const RxF
|
||||
}
|
||||
}
|
||||
|
||||
void TransferListenerBase::handleAnonymousTransferReception(const RxFrame& frame)
|
||||
void TransferListener::handleAnonymousTransferReception(const RxFrame& frame)
|
||||
{
|
||||
if (allow_anonymous_transfers_)
|
||||
{
|
||||
@@ -187,13 +187,19 @@ void TransferListenerBase::handleAnonymousTransferReception(const RxFrame& frame
|
||||
}
|
||||
}
|
||||
|
||||
void TransferListenerBase::cleanup(MonotonicTime ts)
|
||||
TransferListener::~TransferListener()
|
||||
{
|
||||
// Map must be cleared before bufmgr is destroyed
|
||||
receivers_.clear();
|
||||
}
|
||||
|
||||
void TransferListener::cleanup(MonotonicTime ts)
|
||||
{
|
||||
receivers_.removeAllWhere(TimedOutReceiverPredicate(ts, bufmgr_));
|
||||
UAVCAN_ASSERT(receivers_.isEmpty() ? bufmgr_.isEmpty() : 1);
|
||||
}
|
||||
|
||||
void TransferListenerBase::handleFrame(const RxFrame& frame)
|
||||
void TransferListener::handleFrame(const RxFrame& frame)
|
||||
{
|
||||
if (frame.getSrcNodeID().isUnicast()) // Normal transfer
|
||||
{
|
||||
@@ -227,7 +233,25 @@ void TransferListenerBase::handleFrame(const RxFrame& frame)
|
||||
}
|
||||
else
|
||||
{
|
||||
UAVCAN_TRACE("TransferListenerBase", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame
|
||||
UAVCAN_TRACE("TransferListener", "Invalid frame: %s", frame.toString().c_str()); // Invalid frame
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* TransferListenerWithFilter
|
||||
*/
|
||||
void TransferListenerWithFilter::handleFrame(const RxFrame& frame)
|
||||
{
|
||||
if (filter_ != NULL)
|
||||
{
|
||||
if (filter_->shouldAcceptFrame(frame))
|
||||
{
|
||||
TransferListener::handleFrame(frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
UAVCAN_ASSERT(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ int TransferSender::send(const uint8_t* payload, unsigned payload_len, Monotonic
|
||||
|
||||
UAVCAN_ASSERT(!tx_deadline.isZero());
|
||||
const MonotonicTime otr_deadline = tx_deadline + max(max_transfer_interval_ * 2,
|
||||
IOutgoingTransferRegistry::MinEntryLifetime);
|
||||
OutgoingTransferRegistry::MinEntryLifetime);
|
||||
|
||||
TransferID* const tid = dispatcher_.getOutgoingTransferRegistry().accessOrCreate(otr_key, otr_deadline);
|
||||
if (tid == NULL)
|
||||
|
||||
@@ -33,9 +33,9 @@ void LimitedPoolAllocator::deallocate(const void* ptr)
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t LimitedPoolAllocator::getNumBlocks() const
|
||||
uint16_t LimitedPoolAllocator::getBlockCapacity() const
|
||||
{
|
||||
return min(max_blocks_, allocator_.getNumBlocks());
|
||||
return min(max_blocks_, allocator_.getBlockCapacity());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ TEST(DynamicMemory, LimitedPoolAllocator)
|
||||
uavcan::PoolAllocator<128, 32> pool32;
|
||||
uavcan::LimitedPoolAllocator lim(pool32, 2);
|
||||
|
||||
EXPECT_EQ(2, lim.getNumBlocks());
|
||||
EXPECT_EQ(2, lim.getBlockCapacity());
|
||||
EXPECT_EQ(0, pool32.getPeakNumUsedBlocks());
|
||||
|
||||
const void* ptr1 = lim.allocate(1);
|
||||
|
||||
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
|
||||
#include <malloc.h>
|
||||
|
||||
|
||||
TEST(HeapBasedPoolAllocator, Basic)
|
||||
{
|
||||
std::cout << ">>> HEAP BEFORE:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> al(0xEEEE);
|
||||
|
||||
ASSERT_EQ(0, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(0, al.getNumAllocatedBlocks());
|
||||
|
||||
ASSERT_EQ(0xEEEE, al.getBlockCapacity());
|
||||
ASSERT_EQ(0xFFFF, al.getBlockCapacityHardLimit());
|
||||
|
||||
void* a = al.allocate(10);
|
||||
void* b = al.allocate(10);
|
||||
void* c = al.allocate(10);
|
||||
void* d = al.allocate(10);
|
||||
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(4, al.getNumAllocatedBlocks());
|
||||
|
||||
al.deallocate(a);
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(3, al.getNumAllocatedBlocks());
|
||||
|
||||
al.deallocate(b);
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(2, al.getNumAllocatedBlocks());
|
||||
|
||||
al.deallocate(c);
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(1, al.getNumAllocatedBlocks());
|
||||
|
||||
a = al.allocate(10);
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(2, al.getNumAllocatedBlocks());
|
||||
ASSERT_EQ(c, a);
|
||||
|
||||
al.deallocate(a);
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(1, al.getNumAllocatedBlocks());
|
||||
|
||||
al.shrink();
|
||||
ASSERT_EQ(1, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(1, al.getNumAllocatedBlocks());
|
||||
|
||||
al.deallocate(d);
|
||||
ASSERT_EQ(1, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(0, al.getNumAllocatedBlocks());
|
||||
|
||||
al.shrink();
|
||||
ASSERT_EQ(0, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(0, al.getNumAllocatedBlocks());
|
||||
|
||||
std::cout << ">>> HEAP AFTER:" << std::endl;
|
||||
malloc_stats();
|
||||
}
|
||||
|
||||
|
||||
TEST(HeapBasedPoolAllocator, Limits)
|
||||
{
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> al(2);
|
||||
|
||||
ASSERT_EQ(2, al.getBlockCapacity());
|
||||
ASSERT_EQ(4, al.getBlockCapacityHardLimit());
|
||||
|
||||
ASSERT_EQ(0, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(0, al.getNumAllocatedBlocks());
|
||||
|
||||
void* a = al.allocate(10);
|
||||
void* b = al.allocate(10);
|
||||
void* c = al.allocate(10);
|
||||
void* d = al.allocate(10);
|
||||
|
||||
ASSERT_TRUE(a);
|
||||
ASSERT_TRUE(b);
|
||||
ASSERT_TRUE(c);
|
||||
ASSERT_TRUE(d);
|
||||
|
||||
ASSERT_FALSE(al.allocate(10));
|
||||
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(4, al.getNumAllocatedBlocks());
|
||||
|
||||
al.deallocate(a);
|
||||
al.deallocate(b);
|
||||
al.deallocate(c);
|
||||
al.deallocate(d);
|
||||
|
||||
ASSERT_EQ(4, al.getNumReservedBlocks());
|
||||
ASSERT_EQ(0, al.getNumAllocatedBlocks());
|
||||
}
|
||||
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
|
||||
#include <thread>
|
||||
#include <mutex>
|
||||
|
||||
struct RaiiSynchronizer
|
||||
{
|
||||
static std::mutex mutex;
|
||||
std::lock_guard<std::mutex> guard{mutex};
|
||||
};
|
||||
|
||||
std::mutex RaiiSynchronizer::mutex;
|
||||
|
||||
TEST(HeapBasedPoolAllocator, Concurrency)
|
||||
{
|
||||
std::cout << ">>> HEAP BEFORE:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize, RaiiSynchronizer> al(1000);
|
||||
|
||||
ASSERT_EQ(1000, al.getBlockCapacity());
|
||||
ASSERT_EQ(2000, al.getBlockCapacityHardLimit());
|
||||
|
||||
volatile bool terminate = false;
|
||||
|
||||
/*
|
||||
* Starting the testing threads
|
||||
*/
|
||||
std::thread threads[3];
|
||||
|
||||
for (auto& x : threads)
|
||||
{
|
||||
x = std::thread([&al, &terminate]()
|
||||
{
|
||||
while (!terminate)
|
||||
{
|
||||
auto a = al.allocate(1);
|
||||
auto b = al.allocate(1);
|
||||
auto c = al.allocate(1);
|
||||
al.deallocate(al.allocate(1));
|
||||
al.deallocate(a);
|
||||
al.deallocate(b);
|
||||
al.deallocate(c);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
* Running the threads for some time, then terminating
|
||||
*/
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
|
||||
terminate = true;
|
||||
std::cout << "Terminating workers..." << std::endl;
|
||||
|
||||
for (auto& x : threads)
|
||||
{
|
||||
x.join();
|
||||
}
|
||||
std::cout << "All workers joined" << std::endl;
|
||||
|
||||
/*
|
||||
* Now, there must not be any leaked memory, because the worker threads deallocate everything before completion.
|
||||
*/
|
||||
std::cout << "Allocated: " << al.getNumAllocatedBlocks() << std::endl;
|
||||
std::cout << "Reserved: " << al.getNumReservedBlocks() << std::endl;
|
||||
|
||||
std::cout << ">>> HEAP BEFORE SHRINK:" << std::endl;
|
||||
malloc_stats();
|
||||
|
||||
al.shrink();
|
||||
|
||||
std::cout << ">>> HEAP AFTER SHRINK:" << std::endl;
|
||||
malloc_stats();
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -31,11 +31,12 @@ TEST(Node, Basic)
|
||||
swver.minor = 1;
|
||||
swver.vcs_commit = 0xDEADBEEF;
|
||||
|
||||
std::cout << "sizeof(uavcan::Node<0>): " << sizeof(uavcan::Node<0>) << std::endl;
|
||||
|
||||
/*
|
||||
* uavcan::Node
|
||||
*/
|
||||
uavcan::Node<0> node1(nodes.can_a, nodes.clock_a);
|
||||
std::cout << "sizeof(uavcan::Node<0>): " << sizeof(uavcan::Node<0>) << std::endl;
|
||||
uavcan::Node<1024> node1(nodes.can_a, nodes.clock_a);
|
||||
node1.setName("com.example");
|
||||
node1.setNodeID(1);
|
||||
node1.setSoftwareVersion(swver);
|
||||
@@ -43,7 +44,7 @@ TEST(Node, Basic)
|
||||
/*
|
||||
* Companion test node
|
||||
*/
|
||||
uavcan::Node<0> node2(nodes.can_b, nodes.clock_b);
|
||||
uavcan::Node<1024> node2(nodes.can_b, nodes.clock_b);
|
||||
node2.setName("foobar");
|
||||
node2.setNodeID(2);
|
||||
node2.setSoftwareVersion(swver);
|
||||
|
||||
@@ -31,10 +31,12 @@ TEST(SubNode, Basic)
|
||||
swver.minor = 1;
|
||||
swver.vcs_commit = 0xDEADBEEF;
|
||||
|
||||
std::cout << "sizeof(uavcan::SubNode<0>): " << sizeof(uavcan::SubNode<0>) << std::endl;
|
||||
|
||||
/*
|
||||
* uavcan::Node
|
||||
*/
|
||||
uavcan::Node<0> node1(nodes.can_a, nodes.clock_a);
|
||||
uavcan::Node<1024> node1(nodes.can_a, nodes.clock_a);
|
||||
node1.setName("com.example");
|
||||
node1.setNodeID(1);
|
||||
node1.setSoftwareVersion(swver);
|
||||
@@ -42,8 +44,7 @@ TEST(SubNode, Basic)
|
||||
/*
|
||||
* uavcan::SubNode
|
||||
*/
|
||||
uavcan::SubNode<0> node2(nodes.can_b, nodes.clock_b);
|
||||
std::cout << "sizeof(uavcan::SubNode<0>): " << sizeof(uavcan::SubNode<0>) << std::endl;
|
||||
uavcan::SubNode<1024> node2(nodes.can_b, nodes.clock_b);
|
||||
|
||||
BackgroundSpinner bgspinner(node2, node1);
|
||||
bgspinner.startPeriodic(uavcan::MonotonicDuration::fromMSec(10));
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#endif
|
||||
|
||||
#include <uavcan/node/abstract_node.hpp>
|
||||
#include <uavcan/helpers/heap_based_pool_allocator.hpp>
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <queue>
|
||||
@@ -19,15 +20,20 @@
|
||||
|
||||
struct TestNode : public uavcan::INode
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::OutgoingTransferRegistry<8> otr;
|
||||
/*
|
||||
* This class used to use the simple pool allocator instead:
|
||||
* uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 1024, uavcan::MemPoolBlockSize> pool;
|
||||
* It has been replaced because unlike the simple allocator, heap-based one is not tested as extensively.
|
||||
* Moreover, heap based allocator prints and error message upon destruction if some memory has not been freed.
|
||||
*/
|
||||
uavcan::HeapBasedPoolAllocator<uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::Scheduler scheduler;
|
||||
uint64_t internal_failure_count;
|
||||
|
||||
TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id)
|
||||
: otr(pool)
|
||||
, scheduler(can_driver, pool, clock_driver, otr)
|
||||
, internal_failure_count(0)
|
||||
TestNode(uavcan::ICanDriver& can_driver, uavcan::ISystemClock& clock_driver, uavcan::NodeID self_node_id) :
|
||||
pool(1024),
|
||||
scheduler(can_driver, pool, clock_driver),
|
||||
internal_failure_count(0)
|
||||
{
|
||||
setNodeID(self_node_id);
|
||||
}
|
||||
|
||||
@@ -190,6 +190,5 @@ TEST(dynamic_node_id_server, ObjectSizes)
|
||||
std::cout << "ServiceServer<AppendEntries>: " << sizeof(ServiceServer<AppendEntries>) << std::endl;
|
||||
std::cout << "ServiceClient<AppendEntries>: " << sizeof(ServiceClient<AppendEntries>) << std::endl;
|
||||
std::cout << "ServiceServer<RequestVote>: " << sizeof(ServiceServer<RequestVote>) << std::endl;
|
||||
std::cout << "ServiceClient<RequestVote,~,5>:"
|
||||
<< sizeof(ServiceClient<RequestVote, void (*)(const ServiceCallResult<RequestVote>&), 5>) << std::endl;
|
||||
std::cout << "ServiceClient<RequestVote>: " << sizeof(ServiceClient<RequestVote>) << std::endl;
|
||||
}
|
||||
|
||||
@@ -281,7 +281,5 @@ TEST(dynamic_node_id_server_NodeDiscoverer, Sizes)
|
||||
std::cout << "BitSet<NodeID::Max + 1>: " << sizeof(BitSet<NodeID::Max + 1>) << std::endl;
|
||||
std::cout << "ServiceClient<protocol::GetNodeInfo>: " << sizeof(ServiceClient<protocol::GetNodeInfo>) << std::endl;
|
||||
std::cout << "protocol::GetNodeInfo::Response: " << sizeof(protocol::GetNodeInfo::Response) << std::endl;
|
||||
std::cout << "Subscriber<protocol::NodeStatus, ~, 64, 0>: "
|
||||
<< sizeof(Subscriber<protocol::NodeStatus,
|
||||
void (*)(const ReceivedDataStructure<protocol::NodeStatus>&), 64, 0>) << std::endl;
|
||||
std::cout << "Subscriber<protocol::NodeStatus>: " << sizeof(Subscriber<protocol::NodeStatus>) << std::endl;
|
||||
}
|
||||
|
||||
@@ -145,10 +145,10 @@ TEST(CanAcceptanceFilter, Basic_test)
|
||||
std::cout << "config.MK [" << i << "]= " << configure_array.getByIndex(i)->mask << std::endl;
|
||||
}
|
||||
|
||||
ASSERT_EQ(configure_array.getByIndex(0)->id, 0);
|
||||
ASSERT_EQ(configure_array.getByIndex(0)->mask, 255);
|
||||
ASSERT_EQ(configure_array.getByIndex(1)->id, 256000);
|
||||
ASSERT_EQ(configure_array.getByIndex(1)->mask, 16771968);
|
||||
ASSERT_EQ(configure_array.getByIndex(0)->id, 256000);
|
||||
ASSERT_EQ(configure_array.getByIndex(0)->mask, 16771968);
|
||||
ASSERT_EQ(configure_array.getByIndex(1)->id, 0);
|
||||
ASSERT_EQ(configure_array.getByIndex(1)->mask, 255);
|
||||
ASSERT_EQ(configure_array.getByIndex(2)->id, 6272);
|
||||
ASSERT_EQ(configure_array.getByIndex(2)->mask, 32640);
|
||||
ASSERT_EQ(configure_array.getByIndex(3)->id, 262144);
|
||||
@@ -170,16 +170,16 @@ TEST(CanAcceptanceFilter, Basic_test)
|
||||
|
||||
for (uint16_t i = 0; i<configure_array_size; i++)
|
||||
{
|
||||
std::cout << "config.ID [" << i << "]= " << configure_array.getByIndex(i)->id << std::endl;
|
||||
std::cout << "config.MK [" << i << "]= " << configure_array.getByIndex(i)->mask << std::endl;
|
||||
std::cout << "config.ID [" << i << "]= " << configure_array_2.getByIndex(i)->id << std::endl;
|
||||
std::cout << "config.MK [" << i << "]= " << configure_array_2.getByIndex(i)->mask << std::endl;
|
||||
}
|
||||
|
||||
ASSERT_EQ(configure_array_2.getByIndex(0)->id, 6272);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(0)->mask, 32640);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(0)->id, 256000);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(0)->mask, 16771968);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(1)->id, 262144);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(1)->mask, 16776320);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(2)->id, 256000);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(2)->mask, 16771968);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(2)->id, 6272);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(2)->mask, 32640);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(3)->id, 262144);
|
||||
ASSERT_EQ(configure_array_2.getByIndex(3)->mask, 16771968);
|
||||
}
|
||||
|
||||
@@ -52,14 +52,12 @@ static const uavcan::NodeID SELF_NODE_ID(64);
|
||||
|
||||
TEST(Dispatcher, Reception)
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(pool);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, pool, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, pool, clockmock);
|
||||
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
|
||||
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
|
||||
ASSERT_EQ(SELF_NODE_ID, dispatcher.getNodeID());
|
||||
@@ -86,17 +84,17 @@ TEST(Dispatcher, Reception)
|
||||
makeDataType(uavcan::DataTypeKindService, 1)
|
||||
};
|
||||
|
||||
typedef TestListener<512, 2, 2> Subscriber;
|
||||
typedef std::auto_ptr<Subscriber> SubscriberPtr;
|
||||
static const int NUM_SUBSCRIBERS = 6;
|
||||
SubscriberPtr subscribers[NUM_SUBSCRIBERS] =
|
||||
typedef std::auto_ptr<TestListener> TestListenerPtr;
|
||||
static const int MaxBufSize = 512;
|
||||
static const int NumSubscribers = 6;
|
||||
TestListenerPtr subscribers[NumSubscribers] =
|
||||
{
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[0], pool)), // msg
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[0], pool)), // msg // Two similar
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[1], pool)), // msg
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[2], pool)), // srv
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[3], pool)), // srv
|
||||
SubscriberPtr(new Subscriber(dispatcher.getTransferPerfCounter(), TYPES[3], pool)) // srv // Repeat again
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[0], MaxBufSize, pool)), // msg
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[0], MaxBufSize, pool)), // msg // Two similar
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[1], MaxBufSize, pool)), // msg
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[2], MaxBufSize, pool)), // srv
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[3], MaxBufSize, pool)), // srv
|
||||
TestListenerPtr(new TestListener(dispatcher.getTransferPerfCounter(), TYPES[3], MaxBufSize, pool)) // srv // Repeat again
|
||||
};
|
||||
|
||||
static const std::string DATA[6] =
|
||||
@@ -139,7 +137,7 @@ TEST(Dispatcher, Reception)
|
||||
/*
|
||||
* Registration
|
||||
*/
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
for (int i = 0; i < NumSubscribers; i++)
|
||||
{
|
||||
ASSERT_FALSE(dispatcher.hasSubscriber(subscribers[i]->getDataTypeDescriptor().getID()));
|
||||
ASSERT_FALSE(dispatcher.hasPublisher(subscribers[i]->getDataTypeDescriptor().getID()));
|
||||
@@ -153,7 +151,7 @@ TEST(Dispatcher, Reception)
|
||||
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[4].get()));
|
||||
ASSERT_TRUE(dispatcher.registerServiceResponseListener(subscribers[5].get()));
|
||||
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
for (int i = 0; i < NumSubscribers; i++)
|
||||
{
|
||||
ASSERT_FALSE(dispatcher.hasPublisher(subscribers[i]->getDataTypeDescriptor().getID()));
|
||||
}
|
||||
@@ -177,7 +175,7 @@ TEST(Dispatcher, Reception)
|
||||
ASSERT_EQ(1, dispatcher.getNumServiceRequestListeners());
|
||||
ASSERT_EQ(2, dispatcher.getNumServiceResponseListeners());
|
||||
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
for (int i = 0; i < NumSubscribers; i++)
|
||||
{
|
||||
ASSERT_TRUE(subscribers[i]->isEmpty());
|
||||
}
|
||||
@@ -214,7 +212,7 @@ TEST(Dispatcher, Reception)
|
||||
|
||||
ASSERT_TRUE(subscribers[5]->matchAndPop(transfers[3]));
|
||||
|
||||
for (int i = 0; i < NUM_SUBSCRIBERS; i++)
|
||||
for (int i = 0; i < NumSubscribers; i++)
|
||||
{
|
||||
ASSERT_TRUE(subscribers[i]->isEmpty());
|
||||
}
|
||||
@@ -255,9 +253,7 @@ TEST(Dispatcher, Transmission)
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(pool);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, pool, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, pool, clockmock);
|
||||
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
|
||||
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
|
||||
|
||||
@@ -280,7 +276,8 @@ TEST(Dispatcher, Transmission)
|
||||
ASSERT_FALSE(dispatcher.hasPublisher(123));
|
||||
ASSERT_FALSE(dispatcher.hasPublisher(456));
|
||||
const uavcan::OutgoingTransferRegistryKey otr_key(123, uavcan::TransferTypeMessageBroadcast, 0);
|
||||
ASSERT_TRUE(out_trans_reg.accessOrCreate(otr_key, uavcan::MonotonicTime::fromMSec(1000000)));
|
||||
ASSERT_TRUE(dispatcher.getOutgoingTransferRegistry().accessOrCreate(otr_key,
|
||||
uavcan::MonotonicTime::fromMSec(1000000)));
|
||||
ASSERT_TRUE(dispatcher.hasPublisher(123));
|
||||
ASSERT_FALSE(dispatcher.hasPublisher(456));
|
||||
|
||||
@@ -319,9 +316,7 @@ TEST(Dispatcher, Spin)
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
|
||||
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID)); // Can be set only once
|
||||
ASSERT_FALSE(dispatcher.setNodeID(SELF_NODE_ID));
|
||||
|
||||
@@ -365,9 +360,7 @@ TEST(Dispatcher, Loopback)
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
|
||||
ASSERT_TRUE(dispatcher.setNodeID(SELF_NODE_ID));
|
||||
|
||||
{
|
||||
|
||||
@@ -72,8 +72,8 @@ TEST(MultiFrameIncomingTransfer, Basic)
|
||||
using uavcan::RxFrame;
|
||||
using uavcan::MultiFrameIncomingTransfer;
|
||||
|
||||
NullAllocator poolmgr; // We don't need dynamic memory
|
||||
uavcan::TransferBufferManager<256, 1> bufmgr(poolmgr);
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::TransferBufferManager bufmgr(256, poolmgr);
|
||||
|
||||
const RxFrame frame = makeFrame();
|
||||
uavcan::TransferBufferManagerKey bufmgr_key(frame.getSrcNodeID(), frame.getTransferType());
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
TEST(OutgoingTransferRegistry, Basic)
|
||||
{
|
||||
using uavcan::OutgoingTransferRegistryKey;
|
||||
NullAllocator poolmgr; // Empty
|
||||
uavcan::OutgoingTransferRegistry<4> otr(poolmgr);
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 2, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::OutgoingTransferRegistry otr(poolmgr);
|
||||
|
||||
otr.cleanup(tsMono(1000));
|
||||
|
||||
|
||||
@@ -83,8 +83,8 @@ static const int TEST_BUFFER_SIZE = 200;
|
||||
|
||||
TEST(StaticTransferBuffer, Basic)
|
||||
{
|
||||
using uavcan::StaticTransferBufferManagerEntry;
|
||||
StaticTransferBufferManagerEntry<TEST_BUFFER_SIZE> buf;
|
||||
using uavcan::StaticTransferBuffer;
|
||||
StaticTransferBuffer<TEST_BUFFER_SIZE> buf;
|
||||
|
||||
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
|
||||
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
|
||||
@@ -126,15 +126,15 @@ TEST(StaticTransferBuffer, Basic)
|
||||
}
|
||||
|
||||
|
||||
TEST(DynamicTransferBufferManagerEntry, Basic)
|
||||
TEST(TransferBufferManagerEntry, Basic)
|
||||
{
|
||||
using uavcan::DynamicTransferBufferManagerEntry;
|
||||
using uavcan::TransferBufferManagerEntry;
|
||||
|
||||
static const int MAX_SIZE = TEST_BUFFER_SIZE;
|
||||
static const int POOL_BLOCKS = 8;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
DynamicTransferBufferManagerEntry buf(pool, MAX_SIZE);
|
||||
TransferBufferManagerEntry buf(pool, MAX_SIZE);
|
||||
|
||||
uint8_t local_buffer[TEST_BUFFER_SIZE * 2];
|
||||
const uint8_t* const test_data_ptr = reinterpret_cast<const uint8_t*>(TEST_DATA.c_str());
|
||||
@@ -186,7 +186,7 @@ TEST(DynamicTransferBufferManagerEntry, Basic)
|
||||
|
||||
// Destroying the object; memory should be released
|
||||
ASSERT_LT(0, pool.getNumUsedBlocks());
|
||||
buf.~DynamicTransferBufferManagerEntry();
|
||||
buf.~TransferBufferManagerEntry();
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
}
|
||||
|
||||
@@ -229,11 +229,10 @@ TEST(TransferBufferManager, Basic)
|
||||
using uavcan::TransferBufferManagerKey;
|
||||
using uavcan::ITransferBuffer;
|
||||
|
||||
static const int POOL_BLOCKS = 6;
|
||||
static const int POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef TransferBufferManager<MGR_MAX_BUFFER_SIZE, 2> TransferBufferManagerType;
|
||||
std::auto_ptr<TransferBufferManagerType> mgr(new TransferBufferManagerType(pool));
|
||||
std::auto_ptr<TransferBufferManager> mgr(new TransferBufferManager(MGR_MAX_BUFFER_SIZE, pool));
|
||||
|
||||
// Empty
|
||||
ASSERT_FALSE(mgr->access(TransferBufferManagerKey(0, uavcan::TransferTypeMessageBroadcast)));
|
||||
@@ -250,40 +249,25 @@ TEST(TransferBufferManager, Basic)
|
||||
TransferBufferManagerKey(64, uavcan::TransferTypeMessageBroadcast)
|
||||
};
|
||||
|
||||
// Static 0
|
||||
ASSERT_TRUE((tbb = mgr->create(keys[0])));
|
||||
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[0], tbb));
|
||||
ASSERT_EQ(1, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(1, mgr->getNumBuffers());
|
||||
|
||||
// Static 1
|
||||
ASSERT_TRUE((tbb = mgr->create(keys[1])));
|
||||
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[1], tbb));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(0, mgr->getNumDynamicBuffers());
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(2, mgr->getNumBuffers());
|
||||
ASSERT_LT(2, pool.getNumUsedBlocks());
|
||||
|
||||
// Dynamic 0
|
||||
ASSERT_TRUE((tbb = mgr->create(keys[2])));
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks()); // Empty dynamic buffer occupies one block
|
||||
ASSERT_EQ(MGR_MAX_BUFFER_SIZE, fillTestData(MGR_TEST_DATA[2], tbb));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(1, mgr->getNumDynamicBuffers());
|
||||
ASSERT_LT(1, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(3, mgr->getNumBuffers());
|
||||
|
||||
std::cout << "TransferBufferManager - Basic: Pool usage: " << pool.getNumUsedBlocks() << std::endl;
|
||||
|
||||
// Dynamic 2
|
||||
ASSERT_TRUE((tbb = mgr->create(keys[3])));
|
||||
ASSERT_LT(0, pool.getNumUsedBlocks());
|
||||
|
||||
ASSERT_LT(0, fillTestData(MGR_TEST_DATA[3], tbb));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(2, mgr->getNumDynamicBuffers());
|
||||
|
||||
// Dynamic 3 - will fail due to OOM
|
||||
ASSERT_FALSE((tbb = mgr->create(keys[4])));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(2, mgr->getNumDynamicBuffers());
|
||||
ASSERT_EQ(4, mgr->getNumBuffers());
|
||||
|
||||
// Making sure all buffers contain proper data
|
||||
ASSERT_TRUE((tbb = mgr->access(keys[0])));
|
||||
@@ -298,18 +282,14 @@ TEST(TransferBufferManager, Basic)
|
||||
ASSERT_TRUE((tbb = mgr->access(keys[3])));
|
||||
ASSERT_TRUE(matchAgainst(MGR_TEST_DATA[3], *tbb));
|
||||
|
||||
// Freeing one static buffer; one dynamic must migrate
|
||||
mgr->remove(keys[1]);
|
||||
ASSERT_FALSE(mgr->access(keys[1]));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(1, mgr->getNumDynamicBuffers()); // One migrated to the static
|
||||
ASSERT_EQ(3, mgr->getNumBuffers());
|
||||
ASSERT_LT(0, pool.getNumFreeBlocks());
|
||||
|
||||
// Removing NodeID 0; one dynamic must migrate
|
||||
mgr->remove(keys[0]);
|
||||
ASSERT_FALSE(mgr->access(keys[0]));
|
||||
ASSERT_EQ(2, mgr->getNumStaticBuffers());
|
||||
ASSERT_EQ(0, mgr->getNumDynamicBuffers());
|
||||
ASSERT_EQ(2, mgr->getNumBuffers());
|
||||
|
||||
// At this time we have the following NodeID: 2, 127
|
||||
ASSERT_TRUE((tbb = mgr->access(keys[2])));
|
||||
@@ -332,11 +312,3 @@ TEST(TransferBufferManager, Basic)
|
||||
mgr.reset();
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
}
|
||||
|
||||
|
||||
TEST(TransferBufferManager, EmptySpecialization)
|
||||
{
|
||||
uavcan::TransferBufferManager<0, 0> mgr;
|
||||
(void)mgr;
|
||||
ASSERT_GE(sizeof(void*), sizeof(mgr));
|
||||
}
|
||||
|
||||
@@ -9,11 +9,11 @@
|
||||
|
||||
class TransferListenerEmulator : public IncomingTransferEmulatorBase
|
||||
{
|
||||
uavcan::TransferListenerBase& target_;
|
||||
uavcan::TransferListener& target_;
|
||||
const uavcan::DataTypeDescriptor data_type_;
|
||||
|
||||
public:
|
||||
TransferListenerEmulator(uavcan::TransferListenerBase& target, const uavcan::DataTypeDescriptor& type,
|
||||
TransferListenerEmulator(uavcan::TransferListener& target, const uavcan::DataTypeDescriptor& type,
|
||||
uavcan::NodeID dst_node_id = 127)
|
||||
: IncomingTransferEmulatorBase(dst_node_id)
|
||||
, target_(target)
|
||||
@@ -34,11 +34,11 @@ TEST(TransferListener, BasicMFT)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
static const int NUM_POOL_BLOCKS = 12; // This number is just enough to pass the test
|
||||
static const int NUM_POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<256, 1, 1> subscriber(perf, type, pool);
|
||||
TestListener subscriber(perf, type, 256, pool);
|
||||
|
||||
/*
|
||||
* Test data
|
||||
@@ -93,9 +93,10 @@ TEST(TransferListener, CrcFailure)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
NullAllocator poolmgr; // No dynamic memory
|
||||
static const int NUM_POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<256, 2, 2> subscriber(perf, type, poolmgr); // Static buffer only, 2 entries
|
||||
TestListener subscriber(perf, type, 256, poolmgr); // Static buffer only, 2 entries
|
||||
|
||||
/*
|
||||
* Generating transfers with damaged payload (CRC is not valid)
|
||||
@@ -136,9 +137,10 @@ TEST(TransferListener, BasicSFT)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
NullAllocator poolmgr; // No dynamic memory. At all.
|
||||
static const int NUM_POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<0, 0, 5> subscriber(perf, type, poolmgr); // Max buf size is 0, i.e. SFT-only
|
||||
TestListener subscriber(perf, type, 0, poolmgr); // Max buf size is 0, i.e. SFT-only
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
@@ -148,7 +150,6 @@ TEST(TransferListener, BasicSFT)
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, ""),
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 3, "abc"),
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceResponse, 4, ""),
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceResponse, 2, ""), // New TT, ignored due to OOM
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "foo"), // Same as 2, not ignored
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "123456789abc"), // Same as 2, not SFT - ignore
|
||||
emulator.makeTransfer(16, uavcan::TransferTypeServiceRequest, 2, "bar"), // Same as 2, not ignored
|
||||
@@ -161,8 +162,8 @@ TEST(TransferListener, BasicSFT)
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[2]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[3]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[4]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[6]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[8]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[5]));
|
||||
ASSERT_TRUE(subscriber.matchAndPop(transfers[7]));
|
||||
|
||||
ASSERT_TRUE(subscriber.isEmpty());
|
||||
}
|
||||
@@ -172,9 +173,10 @@ TEST(TransferListener, Cleanup)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
NullAllocator poolmgr; // No dynamic memory
|
||||
static const int NUM_POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<256, 1, 2> subscriber(perf, type, poolmgr); // Static buffer only, 1 entry
|
||||
TestListener subscriber(perf, type, 256, poolmgr);
|
||||
|
||||
/*
|
||||
* Generating transfers
|
||||
@@ -206,7 +208,7 @@ TEST(TransferListener, Cleanup)
|
||||
/*
|
||||
* Cleanup with huge timestamp value will remove all entries
|
||||
*/
|
||||
static_cast<uavcan::TransferListenerBase&>(subscriber).cleanup(tsMono(100000000));
|
||||
static_cast<uavcan::TransferListener&>(subscriber).cleanup(tsMono(100000000));
|
||||
|
||||
/*
|
||||
* Sending the same transfers again - they will be accepted since registres were cleared
|
||||
@@ -227,9 +229,10 @@ TEST(TransferListener, AnonymousTransfers)
|
||||
{
|
||||
const uavcan::DataTypeDescriptor type(uavcan::DataTypeKindMessage, 123, uavcan::DataTypeSignature(123456789), "A");
|
||||
|
||||
NullAllocator poolmgr;
|
||||
static const int NUM_POOL_BLOCKS = 100;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * NUM_POOL_BLOCKS, uavcan::MemPoolBlockSize> poolmgr;
|
||||
uavcan::TransferPerfCounter perf;
|
||||
TestListener<0, 0, 0> subscriber(perf, type, poolmgr);
|
||||
TestListener subscriber(perf, type, 0, poolmgr);
|
||||
|
||||
TransferListenerEmulator emulator(subscriber, type);
|
||||
const Transfer transfers[] =
|
||||
@@ -261,5 +264,5 @@ TEST(TransferListener, Sizes)
|
||||
{
|
||||
using namespace uavcan;
|
||||
|
||||
std::cout << "sizeof(TransferListener<64, 1, 2>): " << sizeof(TransferListener<64, 1, 2>) << std::endl;
|
||||
std::cout << "sizeof(TransferListener): " << sizeof(TransferListener) << std::endl;
|
||||
}
|
||||
|
||||
@@ -74,15 +74,13 @@ const uavcan::TransferBufferManagerKey RxFrameGenerator::DEFAULT_KEY(42, uavcan:
|
||||
template <unsigned BufSize>
|
||||
struct Context
|
||||
{
|
||||
NullAllocator pool; // We don't need dynamic memory for this test
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> pool;
|
||||
uavcan::TransferReceiver receiver; // Must be default constructible and copyable
|
||||
uavcan::TransferBufferManager<BufSize, 1> bufmgr;
|
||||
uavcan::TransferBufferManager bufmgr;
|
||||
|
||||
Context() :
|
||||
bufmgr(pool)
|
||||
{
|
||||
assert(pool.allocate(1) == NULL);
|
||||
}
|
||||
bufmgr(BufSize, pool)
|
||||
{ }
|
||||
|
||||
~Context()
|
||||
{
|
||||
@@ -124,7 +122,7 @@ TEST(TransferReceiver, Basic)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
std::cout << "sizeof(TransferReceiver): " << sizeof(TransferReceiver) << std::endl;
|
||||
@@ -263,7 +261,7 @@ TEST(TransferReceiver, OutOfBufferSpace_32bytes)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
@@ -301,7 +299,7 @@ TEST(TransferReceiver, OutOfOrderFrames)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
CHECK_NOT_COMPLETE(rcv.addFrame(gen(1, "1234567", SET100, 7, 100000000), bk));
|
||||
@@ -325,7 +323,7 @@ TEST(TransferReceiver, IntervalMeasurement)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
static const int INTERVAL = 1000;
|
||||
@@ -355,7 +353,7 @@ TEST(TransferReceiver, Restart)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
@@ -403,7 +401,7 @@ TEST(TransferReceiver, UtcTransferTimestamping)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(789);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferAccessor bk(context.bufmgr, RxFrameGenerator::DEFAULT_KEY);
|
||||
|
||||
/*
|
||||
@@ -462,7 +460,7 @@ TEST(TransferReceiver, HeaderParsing)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(123);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
|
||||
/*
|
||||
* MFT, message broadcasting
|
||||
@@ -509,7 +507,7 @@ TEST(TransferReceiver, HeaderParsing)
|
||||
Context<32> context;
|
||||
RxFrameGenerator gen(123);
|
||||
uavcan::TransferReceiver& rcv = context.receiver;
|
||||
uavcan::ITransferBufferManager& bufmgr = context.bufmgr;
|
||||
uavcan::TransferBufferManager& bufmgr = context.bufmgr;
|
||||
|
||||
static const uavcan::TransferType ADDRESSED_TRANSFER_TYPES[2] =
|
||||
{
|
||||
|
||||
@@ -29,17 +29,15 @@ static int sendOne(uavcan::TransferSender& sender, const std::string& data,
|
||||
|
||||
TEST(TransferSender, Basic)
|
||||
{
|
||||
NullAllocator poolmgr;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
static const uavcan::NodeID TX_NODE_ID(64);
|
||||
static const uavcan::NodeID RX_NODE_ID(65);
|
||||
uavcan::Dispatcher dispatcher_tx(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher_rx(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher_tx(driver, poolmgr, clockmock);
|
||||
uavcan::Dispatcher dispatcher_rx(driver, poolmgr, clockmock);
|
||||
ASSERT_TRUE(dispatcher_tx.setNodeID(TX_NODE_ID));
|
||||
ASSERT_TRUE(dispatcher_rx.setNodeID(RX_NODE_ID));
|
||||
|
||||
@@ -123,9 +121,9 @@ TEST(TransferSender, Basic)
|
||||
}
|
||||
}
|
||||
|
||||
TestListener<512, 2, 2> sub_msg(dispatcher_rx.getTransferPerfCounter(), TYPES[0], poolmgr);
|
||||
TestListener<512, 2, 2> sub_srv_req(dispatcher_rx.getTransferPerfCounter(), TYPES[1], poolmgr);
|
||||
TestListener<512, 2, 2> sub_srv_resp(dispatcher_rx.getTransferPerfCounter(), TYPES[1], poolmgr);
|
||||
TestListener sub_msg(dispatcher_rx.getTransferPerfCounter(), TYPES[0], 512, poolmgr);
|
||||
TestListener sub_srv_req(dispatcher_rx.getTransferPerfCounter(), TYPES[1], 512, poolmgr);
|
||||
TestListener sub_srv_resp(dispatcher_rx.getTransferPerfCounter(), TYPES[1], 512, poolmgr);
|
||||
|
||||
dispatcher_rx.registerMessageListener(&sub_msg);
|
||||
dispatcher_rx.registerServiceRequestListener(&sub_srv_req);
|
||||
@@ -190,15 +188,13 @@ struct TransferSenderTestLoopbackFrameListener : public uavcan::LoopbackFrameLis
|
||||
|
||||
TEST(TransferSender, Loopback)
|
||||
{
|
||||
NullAllocator poolmgr;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
static const uavcan::NodeID TX_NODE_ID(64);
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
|
||||
ASSERT_TRUE(dispatcher.setNodeID(TX_NODE_ID));
|
||||
|
||||
uavcan::DataTypeDescriptor desc = makeDataType(uavcan::DataTypeKindMessage, 1, "Foobar");
|
||||
@@ -231,14 +227,12 @@ TEST(TransferSender, Loopback)
|
||||
|
||||
TEST(TransferSender, PassiveMode)
|
||||
{
|
||||
NullAllocator poolmgr;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 100, uavcan::MemPoolBlockSize> poolmgr;
|
||||
|
||||
SystemClockMock clockmock(100);
|
||||
CanDriverMock driver(2, clockmock);
|
||||
|
||||
uavcan::OutgoingTransferRegistry<8> out_trans_reg(poolmgr);
|
||||
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock, out_trans_reg);
|
||||
uavcan::Dispatcher dispatcher(driver, poolmgr, clockmock);
|
||||
|
||||
uavcan::TransferSender sender(dispatcher, makeDataType(uavcan::DataTypeKindMessage, 123),
|
||||
uavcan::CanTxQueue::Volatile);
|
||||
|
||||
@@ -11,7 +11,7 @@ TEST(TransferTestHelpers, Transfer)
|
||||
{
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * 8, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
uavcan::TransferBufferManager<128, 1> mgr(pool);
|
||||
uavcan::TransferBufferManager mgr(128, pool);
|
||||
uavcan::TransferBufferAccessor tba(mgr, uavcan::TransferBufferManagerKey(0, uavcan::TransferTypeMessageBroadcast));
|
||||
|
||||
uavcan::RxFrame frame(uavcan::Frame(123, uavcan::TransferTypeMessageBroadcast, 1, 0, 0),
|
||||
|
||||
@@ -117,17 +117,16 @@ struct Transfer
|
||||
* In reality, uavcan::TransferListener should accept only specific transfer types
|
||||
* which are dispatched/filtered by uavcan::Dispatcher.
|
||||
*/
|
||||
template <unsigned MAX_BUF_SIZE, unsigned NUM_STATIC_BUFS, unsigned NUM_STATIC_RECEIVERS>
|
||||
class TestListener : public uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS>
|
||||
class TestListener : public uavcan::TransferListener
|
||||
{
|
||||
typedef uavcan::TransferListener<MAX_BUF_SIZE, NUM_STATIC_BUFS, NUM_STATIC_RECEIVERS> Base;
|
||||
typedef uavcan::TransferListener Base;
|
||||
|
||||
std::queue<Transfer> transfers_;
|
||||
|
||||
public:
|
||||
TestListener(uavcan::TransferPerfCounter& perf, const uavcan::DataTypeDescriptor& data_type,
|
||||
uavcan::IPoolAllocator& allocator)
|
||||
: Base(perf, data_type, allocator)
|
||||
uavcan::uint16_t max_buffer_size, uavcan::IPoolAllocator& allocator)
|
||||
: Base(perf, data_type, max_buffer_size, allocator)
|
||||
{ }
|
||||
|
||||
void handleIncomingTransfer(uavcan::IncomingTransfer& transfer)
|
||||
@@ -166,7 +165,7 @@ public:
|
||||
return res;
|
||||
}
|
||||
|
||||
int getNumReceivedTransfers() const { return transfers_.size(); }
|
||||
unsigned getNumReceivedTransfers() const { return static_cast<unsigned>(transfers_.size()); }
|
||||
bool isEmpty() const { return transfers_.empty(); }
|
||||
};
|
||||
|
||||
@@ -317,5 +316,5 @@ class NullAllocator : public uavcan::IPoolAllocator
|
||||
public:
|
||||
virtual void* allocate(std::size_t) { return NULL; }
|
||||
virtual void deallocate(const void*) { }
|
||||
virtual uint16_t getNumBlocks() const { return 0; }
|
||||
virtual uint16_t getBlockCapacity() const { return 0; }
|
||||
};
|
||||
|
||||
+20
-69
@@ -2,6 +2,11 @@
|
||||
* Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
|
||||
*/
|
||||
|
||||
#if __GNUC__
|
||||
// We need auto_ptr for compatibility reasons
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
#include <string>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
@@ -46,7 +51,7 @@ TEST(Map, Basic)
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Map<std::string, std::string, 2> MapType;
|
||||
typedef Map<std::string, std::string> MapType;
|
||||
std::auto_ptr<MapType> map(new MapType(pool));
|
||||
|
||||
// Empty
|
||||
@@ -57,25 +62,23 @@ TEST(Map, Basic)
|
||||
ASSERT_FALSE(map->getByIndex(1));
|
||||
ASSERT_FALSE(map->getByIndex(10000));
|
||||
|
||||
// Static insertion
|
||||
// Insertion
|
||||
ASSERT_EQ("a", *map->insert("1", "a"));
|
||||
ASSERT_EQ("b", *map->insert("2", "b"));
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
ASSERT_EQ(0, map->getNumDynamicPairs());
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(2, map->getSize());
|
||||
|
||||
// Ordering
|
||||
ASSERT_TRUE(map->getByIndex(0)->match("1"));
|
||||
ASSERT_TRUE(map->getByIndex(1)->match("2"));
|
||||
|
||||
// Dynamic insertion
|
||||
// Insertion
|
||||
ASSERT_EQ("c", *map->insert("3", "c"));
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks());
|
||||
|
||||
ASSERT_EQ("d", *map->insert("4", "d"));
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks()); // Assuming that at least 2 items fit one block
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
ASSERT_EQ(2, map->getNumDynamicPairs());
|
||||
ASSERT_EQ(2, pool.getNumUsedBlocks()); // Assuming that at least 2 items fit one block
|
||||
ASSERT_EQ(4, map->getSize());
|
||||
|
||||
// Making sure everything is here
|
||||
ASSERT_EQ("a", *map->access("1"));
|
||||
@@ -84,14 +87,6 @@ TEST(Map, Basic)
|
||||
ASSERT_EQ("d", *map->access("4"));
|
||||
ASSERT_FALSE(map->access("hi"));
|
||||
|
||||
// Ordering
|
||||
ASSERT_TRUE(map->getByIndex(0)->match("1"));
|
||||
ASSERT_TRUE(map->getByIndex(1)->match("2"));
|
||||
ASSERT_TRUE(map->getByIndex(2)->match("3"));
|
||||
ASSERT_TRUE(map->getByIndex(3)->match("4"));
|
||||
ASSERT_FALSE(map->getByIndex(4));
|
||||
ASSERT_FALSE(map->getByIndex(1000));
|
||||
|
||||
// Modifying existing entries
|
||||
*map->access("1") = "A";
|
||||
*map->access("2") = "B";
|
||||
@@ -116,35 +111,28 @@ TEST(Map, Basic)
|
||||
ASSERT_EQ("4", *map->find(ValueFindPredicate("D")));
|
||||
ASSERT_FALSE(map->find(KeyFindPredicate("nonexistent_value")));
|
||||
|
||||
// Removing one static
|
||||
// Removing one
|
||||
map->remove("1"); // One of dynamics now migrates to the static storage
|
||||
map->remove("foo"); // There's no such thing anyway
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
ASSERT_EQ(1, map->getNumDynamicPairs());
|
||||
ASSERT_EQ(2, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(3, map->getSize());
|
||||
|
||||
ASSERT_FALSE(map->access("1"));
|
||||
ASSERT_EQ("B", *map->access("2"));
|
||||
ASSERT_EQ("C", *map->access("3"));
|
||||
ASSERT_EQ("D", *map->access("4"));
|
||||
|
||||
// Ordering has not changed - first dynamic entry has moved to the first static slot
|
||||
ASSERT_TRUE(map->getByIndex(0)->match("3"));
|
||||
ASSERT_TRUE(map->getByIndex(1)->match("2"));
|
||||
ASSERT_TRUE(map->getByIndex(2)->match("4"));
|
||||
|
||||
// Removing another static
|
||||
// Removing another
|
||||
map->remove("2");
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
ASSERT_EQ(0, map->getNumDynamicPairs());
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks()); // No dynamic entries left
|
||||
ASSERT_EQ(2, map->getSize());
|
||||
ASSERT_EQ(2, pool.getNumUsedBlocks());
|
||||
|
||||
ASSERT_FALSE(map->access("1"));
|
||||
ASSERT_FALSE(map->access("2"));
|
||||
ASSERT_EQ("C", *map->access("3"));
|
||||
ASSERT_EQ("D", *map->access("4"));
|
||||
|
||||
// Adding some new dynamics
|
||||
// Adding some new
|
||||
unsigned max_key_integer = 0;
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
@@ -172,13 +160,7 @@ TEST(Map, Basic)
|
||||
ASSERT_FALSE(map->access("value"));
|
||||
|
||||
// Removing odd values - nearly half of them
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
const unsigned num_dynamics_old = map->getNumDynamicPairs();
|
||||
map->removeAllWhere(oddValuePredicate);
|
||||
ASSERT_EQ(2, map->getNumStaticPairs());
|
||||
const unsigned num_dynamics_new = map->getNumDynamicPairs();
|
||||
std::cout << "Num of dynamic pairs reduced from " << num_dynamics_old << " to " << num_dynamics_new << std::endl;
|
||||
ASSERT_LT(num_dynamics_new, num_dynamics_old);
|
||||
|
||||
// Making sure there's no odd values left
|
||||
for (unsigned kv_int = 0; kv_int <= max_key_integer; kv_int++)
|
||||
@@ -200,37 +182,6 @@ TEST(Map, Basic)
|
||||
}
|
||||
|
||||
|
||||
TEST(Map, NoStatic)
|
||||
{
|
||||
using uavcan::Map;
|
||||
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Map<std::string, std::string> MapType;
|
||||
std::auto_ptr<MapType> map(new MapType(pool));
|
||||
|
||||
// Empty
|
||||
ASSERT_FALSE(map->access("hi"));
|
||||
map->remove("foo");
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
ASSERT_FALSE(map->getByIndex(0));
|
||||
|
||||
// Insertion
|
||||
ASSERT_EQ("a", *map->insert("1", "a"));
|
||||
ASSERT_EQ("b", *map->insert("2", "b"));
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(0, map->getNumStaticPairs());
|
||||
ASSERT_EQ(2, map->getNumDynamicPairs());
|
||||
|
||||
// Ordering
|
||||
ASSERT_TRUE(map->getByIndex(0)->match("1"));
|
||||
ASSERT_TRUE(map->getByIndex(1)->match("2"));
|
||||
ASSERT_FALSE(map->getByIndex(3));
|
||||
ASSERT_FALSE(map->getByIndex(1000));
|
||||
}
|
||||
|
||||
|
||||
TEST(Map, PrimitiveKey)
|
||||
{
|
||||
using uavcan::Map;
|
||||
@@ -238,7 +189,7 @@ TEST(Map, PrimitiveKey)
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Map<short, short, 2> MapType;
|
||||
typedef Map<short, short> MapType;
|
||||
std::auto_ptr<MapType> map(new MapType(pool));
|
||||
|
||||
// Empty
|
||||
|
||||
@@ -71,7 +71,7 @@ TEST(Multiset, Basic)
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Multiset<std::string, 2> MultisetType;
|
||||
typedef Multiset<std::string> MultisetType;
|
||||
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
|
||||
|
||||
typedef SummationOperator<std::string> StringConcatenationOperator;
|
||||
@@ -86,9 +86,8 @@ TEST(Multiset, Basic)
|
||||
// Static addion
|
||||
ASSERT_EQ("1", *mset->emplace("1"));
|
||||
ASSERT_EQ("2", *mset->emplace("2"));
|
||||
ASSERT_EQ(0, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(2, mset->getNumStaticItems());
|
||||
ASSERT_EQ(0, mset->getNumDynamicItems());
|
||||
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
|
||||
ASSERT_EQ(2, mset->getSize());
|
||||
|
||||
// Ordering
|
||||
ASSERT_TRUE(*mset->getByIndex(0) == "1");
|
||||
@@ -103,23 +102,15 @@ TEST(Multiset, Basic)
|
||||
// Dynamic addition
|
||||
ASSERT_EQ("3", *mset->emplace("3"));
|
||||
ASSERT_EQ("3", *mset->getByIndex(2));
|
||||
ASSERT_EQ(1, pool.getNumUsedBlocks());
|
||||
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
|
||||
|
||||
ASSERT_EQ("4", *mset->emplace("4"));
|
||||
ASSERT_LE(1, pool.getNumUsedBlocks()); // One or more
|
||||
ASSERT_EQ(2, mset->getNumStaticItems());
|
||||
ASSERT_EQ(2, mset->getNumDynamicItems());
|
||||
ASSERT_EQ(4, mset->getSize());
|
||||
|
||||
// Making sure everything is here
|
||||
ASSERT_EQ("1", *mset->getByIndex(0));
|
||||
ASSERT_EQ("2", *mset->getByIndex(1));
|
||||
// 2 and 3 are not tested because their placement depends on number of items per dynamic block
|
||||
ASSERT_FALSE(mset->getByIndex(100));
|
||||
ASSERT_FALSE(mset->getByIndex(4));
|
||||
|
||||
const std::string data_at_pos2 = *mset->getByIndex(2);
|
||||
const std::string data_at_pos3 = *mset->getByIndex(3);
|
||||
|
||||
// Finding some items
|
||||
ASSERT_EQ("1", *mset->find(FindPredicate("1")));
|
||||
ASSERT_EQ("2", *mset->find(FindPredicate("2")));
|
||||
@@ -134,23 +125,10 @@ TEST(Multiset, Basic)
|
||||
ASSERT_EQ(4, op.accumulator.size());
|
||||
}
|
||||
|
||||
// Removing one static; ordering will be preserved
|
||||
// Removing some
|
||||
mset->removeFirst("1");
|
||||
mset->removeFirst("foo"); // There's no such thing anyway
|
||||
ASSERT_LE(1, pool.getNumUsedBlocks());
|
||||
ASSERT_EQ(1, mset->getNumStaticItems());
|
||||
ASSERT_EQ(2, mset->getNumDynamicItems()); // This container does not move items
|
||||
|
||||
// Ordering has not changed
|
||||
ASSERT_EQ("2", *mset->getByIndex(0)); // Entry "1" was here
|
||||
ASSERT_EQ(data_at_pos2, *mset->getByIndex(1));
|
||||
ASSERT_EQ(data_at_pos3, *mset->getByIndex(2));
|
||||
|
||||
// Removing another static
|
||||
mset->removeFirst("2");
|
||||
ASSERT_EQ(0, mset->getNumStaticItems());
|
||||
ASSERT_EQ(2, mset->getNumDynamicItems());
|
||||
ASSERT_LE(1, pool.getNumUsedBlocks());
|
||||
|
||||
// Adding some new items
|
||||
unsigned max_value_integer = 0;
|
||||
@@ -219,7 +197,7 @@ TEST(Multiset, PrimitiveKey)
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Multiset<int, 2> MultisetType;
|
||||
typedef Multiset<int> MultisetType;
|
||||
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
|
||||
|
||||
// Empty
|
||||
@@ -237,16 +215,6 @@ TEST(Multiset, PrimitiveKey)
|
||||
ASSERT_EQ(4, *mset->emplace(4));
|
||||
ASSERT_EQ(4, mset->getSize());
|
||||
|
||||
#if UAVCAN_CPP_VERSION >= UAVCAN_CPP11
|
||||
// Only C++11 because C++03 uses one entry per pool block which breaks ordering
|
||||
ASSERT_EQ(1, *mset->getByIndex(0));
|
||||
ASSERT_EQ(2, *mset->getByIndex(1));
|
||||
ASSERT_EQ(3, *mset->getByIndex(2));
|
||||
ASSERT_EQ(4, *mset->getByIndex(3));
|
||||
ASSERT_FALSE(mset->getByIndex(5));
|
||||
ASSERT_FALSE(mset->getByIndex(1000));
|
||||
#endif
|
||||
|
||||
// Summation and clearing
|
||||
{
|
||||
SummationOperator<int> summation_operator;
|
||||
@@ -269,7 +237,7 @@ TEST(Multiset, NoncopyableWithCounter)
|
||||
static const int POOL_BLOCKS = 3;
|
||||
uavcan::PoolAllocator<uavcan::MemPoolBlockSize * POOL_BLOCKS, uavcan::MemPoolBlockSize> pool;
|
||||
|
||||
typedef Multiset<NoncopyableWithCounter, 2> MultisetType;
|
||||
typedef Multiset<NoncopyableWithCounter> MultisetType;
|
||||
std::auto_ptr<MultisetType> mset(new MultisetType(pool));
|
||||
|
||||
ASSERT_EQ(0, NoncopyableWithCounter::num_objects);
|
||||
@@ -286,11 +254,9 @@ TEST(Multiset, NoncopyableWithCounter)
|
||||
|
||||
mset->removeFirst(NoncopyableWithCounter(0));
|
||||
ASSERT_EQ(4, NoncopyableWithCounter::num_objects);
|
||||
ASSERT_EQ(123, mset->getByIndex(0)->value);
|
||||
|
||||
mset->removeFirstWhere(&NoncopyableWithCounter::isNegative);
|
||||
ASSERT_EQ(3, NoncopyableWithCounter::num_objects);
|
||||
ASSERT_EQ(456, mset->getByIndex(1)->value); // -456 is now removed
|
||||
|
||||
mset->removeAllWhere(&NoncopyableWithCounter::isNegative);
|
||||
ASSERT_EQ(2, NoncopyableWithCounter::num_objects); // Only 1 and 2 are left
|
||||
|
||||
@@ -373,11 +373,11 @@ public:
|
||||
{
|
||||
assert(num_ifaces_ > 0 && num_ifaces_ <= uavcan::MaxCanIfaces);
|
||||
|
||||
const unsigned quota_per_iface = allocator_.getNumBlocks() / num_ifaces_;
|
||||
const unsigned quota_per_iface = allocator_.getBlockCapacity() / num_ifaces_;
|
||||
const unsigned quota_per_queue = quota_per_iface; // 2x overcommit
|
||||
|
||||
UAVCAN_TRACE("VirtualCanDriver", "Total blocks: %u, quota per queue: %u",
|
||||
unsigned(allocator_.getNumBlocks()), unsigned(quota_per_queue));
|
||||
unsigned(allocator_.getBlockCapacity()), unsigned(quota_per_queue));
|
||||
|
||||
for (unsigned i = 0; i < num_ifaces_; i++)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user