/* * Copyright (C) 2014 Pavel Kirienko */ #pragma once #include #include #include #include #include #include namespace uavcan { enum ArrayMode { ArrayModeStatic, ArrayModeDynamic }; template class StaticArrayBase { public: typedef unsigned int SizeType; SizeType size() const { return Size; } SizeType capacity() const { return Size; } protected: StaticArrayBase() { } ~StaticArrayBase() { } SizeType validateRange(SizeType pos) const { if (pos < Size) return pos; #if UAVCAN_EXCEPTIONS throw std::out_of_range(typeid(*this).name()); #else assert(0); return Size - 1; // Ha ha #endif } }; template class DynamicArrayBase { protected: typedef IntegerSpec::Result, SignednessUnsigned, CastModeSaturate> RawSizeType; public: typedef typename StorageType::Type SizeType; private: SizeType size_; protected: DynamicArrayBase() : size_(0) { } ~DynamicArrayBase() { } SizeType validateRange(SizeType pos) const { if (pos < size_) return pos; #if UAVCAN_EXCEPTIONS throw std::out_of_range(typeid(*this).name()); #else assert(0); return (size_ == 0) ? 0 : (size_ - 1); #endif } void grow() { if (size_ >= MaxSize) validateRange(MaxSize); // Will throw, assert() or do nothing else size_++; } void shrink() { if (size_ > 0) size_--; } public: SizeType size() const { assert(size_ <= MaxSize); return size_; } SizeType capacity() const { return MaxSize; } }; /** * Statically allocated array with optional dynamic-like behavior */ template class ArrayImpl : public StaticIf, StaticArrayBase >::Result { typedef ArrayImpl SelfType; typedef typename StaticIf, StaticArrayBase >::Result Base; typename StorageType::Type data_[MaxSize]; template typename EnableIf::Type initialize(int) { std::fill(data_, data_ + MaxSize, U()); } template void initialize(...) { } public: typedef typename StorageType::Type ValueType; typedef typename Base::SizeType SizeType; using Base::size; using Base::capacity; ArrayImpl() { initialize(0); } ValueType& at(SizeType pos) { return data_[validateRange(pos)]; } const ValueType& at(SizeType pos) const { return data_[Base::validateRange(pos)]; } ValueType& operator[](SizeType pos) { return at(pos); } const ValueType& operator[](SizeType pos) const { return at(pos); } ValueType* begin() { return data_; } const ValueType* begin() const { return data_; } ValueType* end() { return data_ + Base::size(); } const ValueType* end() const { return data_ + Base::size(); } ValueType& front() { return at(0); } const ValueType& front() const { return at(0); } ValueType& back() { return at(Base::size() - 1); } const ValueType& back() const { return at(Base::size() - 1); } template bool operator<(const R& rhs) const { return std::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end()); } typedef ValueType* iterator; typedef const ValueType* const_iterator; }; /** * Bit array specialization */ template class ArrayImpl, ArrayMode, MaxSize> : public std::bitset , public StaticIf, StaticArrayBase >::Result { typedef typename StaticIf, StaticArrayBase >::Result ArrayBase; public: typedef typename std::bitset::reference Reference; typedef typename ArrayBase::SizeType SizeType; using ArrayBase::size; using ArrayBase::capacity; Reference at(SizeType pos) { return std::bitset::operator[](ArrayBase::validateRange(pos)); } bool at(SizeType pos) const { return std::bitset::operator[](ArrayBase::validateRange(pos)); } Reference operator[](SizeType pos) { return at(pos); } bool operator[](SizeType pos) const { return at(pos); } }; /** * Zero length arrays are not allowed */ template class ArrayImpl; template class Array : public ArrayImpl { typedef ArrayImpl Base; typedef Array SelfType; int encodeSize(ScalarCodec&, FalseType) const { return 1; } int encodeSize(ScalarCodec& codec, TrueType) const // TODO: Tail array optimization { return Base::RawSizeType::encode(size(), codec); } int decodeSize(ScalarCodec&, FalseType) { return 1; } int decodeSize(ScalarCodec& codec, TrueType) { typename StorageType::Type sz = 0; const int res = Base::RawSizeType::decode(sz, codec); if (res > 0) resize(sz); return res; } public: typedef T RawValueType; typedef typename StorageType::Type ValueType; typedef typename Base::SizeType SizeType; using Base::size; enum { IsDynamic = ArrayMode == ArrayModeDynamic }; enum { MaxSize = MaxSize_ }; enum { MinBitLen = IsDynamic ? 0 : (RawValueType::MinBitLen * MaxSize) }; static int encode(const SelfType& array, ScalarCodec& codec) { const int res_sz = array.encodeSize(codec, BooleanType()); if (res_sz <= 0) return res_sz; for (SizeType i = 0; i < array.size(); i++) { const int res = RawValueType::encode(array[i], codec); if (res <= 0) return res; } return 1; } static int decode(SelfType& array, ScalarCodec& codec) { const int res_sz = array.decodeSize(codec, BooleanType()); if (res_sz <= 0) return res_sz; for (SizeType i = 0; i < array.size(); i++) { // TODO: avoid excessive copy ValueType value; const int res = RawValueType::decode(value, codec); array[i] = value; if (res <= 0) return res; } return 1; } bool empty() const { return size() == 0; } void pop_back() { Base::shrink(); } void push_back(const ValueType& value) { Base::grow(); Base::at(size() - 1) = value; } void resize(SizeType new_size, ValueType filler = ValueType()) { if (new_size > size()) { unsigned int cnt = new_size - size(); while (cnt--) push_back(filler); } else if (new_size < size()) { unsigned int cnt = size() - new_size; while (cnt--) pop_back(); } } void clear() { resize(0); } template bool operator==(const R& rhs) const { if (size() != rhs.size()) return false; for (SizeType i = 0; i < size(); i++) // Bitset does not have iterators if (!(Base::at(i) == rhs[i])) return false; return true; } template bool operator!=(const R& rhs) const { return !operator==(rhs); } typedef ValueType value_type; typedef SizeType size_type; }; }