libuav original

Dependents:   UAVCAN UAVCAN_Subscriber

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers array.hpp Source File

array.hpp

00001 /*
00002  * Copyright (C) 2014 Pavel Kirienko <pavel.kirienko@gmail.com>
00003  */
00004 
00005 #ifndef UAVCAN_MARSHAL_ARRAY_HPP_INCLUDED
00006 #define UAVCAN_MARSHAL_ARRAY_HPP_INCLUDED
00007 
00008 #include <cassert>
00009 #include <cstdio>
00010 #include <cstring>
00011 #include <cmath>
00012 #include <uavcan/error.hpp>
00013 #include <uavcan/util/bitset.hpp>
00014 #include <uavcan/util/templates.hpp>
00015 #include <uavcan/build_config.hpp>
00016 #include <uavcan/marshal/type_util.hpp>
00017 #include <uavcan/marshal/integer_spec.hpp>
00018 #include <uavcan/std.hpp>
00019 
00020 #ifndef UAVCAN_CPP_VERSION
00021 # error UAVCAN_CPP_VERSION
00022 #endif
00023 
00024 #ifndef UAVCAN_EXCEPTIONS
00025 # error UAVCAN_EXCEPTIONS
00026 #endif
00027 
00028 #if UAVCAN_EXCEPTIONS
00029 # include <stdexcept>
00030 #endif
00031 
00032 namespace uavcan
00033 {
00034 
00035 enum ArrayMode { ArrayModeStatic, ArrayModeDynamic };
00036 
00037 /**
00038  * Properties of a square matrix; assuming row-major representation.
00039  */
00040 template <unsigned NumElements_>
00041 struct SquareMatrixTraits
00042 {
00043     enum { NumElements = NumElements_ };
00044 
00045     enum { NumRowsCols = CompileTimeIntSqrt<NumElements>::Result };
00046 
00047     enum { NumElementsInTriangle = ((1 + NumRowsCols) * NumRowsCols) / 2 };
00048 
00049     static inline bool isIndexOnDiagonal(unsigned index) { return (index / NumRowsCols) == (index % NumRowsCols); }
00050 
00051     static inline int computeElementIndexAtRowCol(int row, int col) { return row * NumRowsCols + col; }
00052 };
00053 
00054 /**
00055  * This class can be used to detect properties of square matrices.
00056  * Element iterator is a random access forward constant iterator.
00057  */
00058 template <typename ElementIterator, unsigned NumElements>
00059 class SquareMatrixAnalyzer : public SquareMatrixTraits<NumElements>
00060 {
00061     typedef SquareMatrixTraits<NumElements>  Traits ;
00062 
00063     const ElementIterator first_;
00064 
00065 public:
00066     enum PackingMode
00067     {
00068         PackingModeEmpty,
00069         PackingModeScalar,
00070         PackingModeDiagonal,
00071         PackingModeSymmetric,
00072         PackingModeFull
00073     };
00074 
00075     SquareMatrixAnalyzer(ElementIterator first_element_iterator)
00076         : first_(first_element_iterator)
00077     {
00078         StaticAssert<(NumElements > 0)>::check();
00079     }
00080 
00081     ElementIterator accessElementAtRowCol(int row, int col) const
00082     {
00083         return first_ + Traits::computeElementIndexAtRowCol(row, col);
00084     }
00085 
00086     bool areAllElementsNan() const
00087     {
00088         unsigned index = 0;
00089         for (ElementIterator it = first_; index < NumElements; ++it, ++index)
00090         {
00091             if (!isNaN(*it))
00092             {
00093                 return false;
00094             }
00095         }
00096         return true;
00097     }
00098 
00099     bool isScalar() const
00100     {
00101         unsigned index = 0;
00102         for (ElementIterator it = first_; index < NumElements; ++it, ++index)
00103         {
00104             if (!Traits::isIndexOnDiagonal(index) && !isCloseToZero(*it))
00105             {
00106                 return false;
00107             }
00108             if (Traits::isIndexOnDiagonal(index) && !areClose(*it, *first_))
00109             {
00110                 return false;
00111             }
00112         }
00113         return true;
00114     }
00115 
00116     bool isDiagonal() const
00117     {
00118         unsigned index = 0;
00119         for (ElementIterator it = first_; index < NumElements; ++it, ++index)
00120         {
00121             if (!Traits::isIndexOnDiagonal(index) && !isCloseToZero(*it))
00122             {
00123                 return false;
00124             }
00125         }
00126         return true;
00127     }
00128 
00129     bool isSymmetric() const
00130     {
00131         for (int i = 0; i < Traits::NumRowsCols; ++i)
00132         {
00133             for (int k = 0; k < Traits::NumRowsCols; ++k)
00134             {
00135                 // On diagonal comparison is pointless
00136                 if ((i != k) &&
00137                     !areClose(*accessElementAtRowCol(i, k),
00138                               *accessElementAtRowCol(k, i)))
00139                 {
00140                     return false;
00141                 }
00142             }
00143         }
00144         return true;
00145     }
00146 
00147     PackingMode detectOptimalPackingMode() const
00148     {
00149         if (areAllElementsNan())
00150         {
00151             return PackingModeEmpty;
00152         }
00153         if (isScalar())
00154         {
00155             return PackingModeScalar;
00156         }
00157         if (isDiagonal())
00158         {
00159             return PackingModeDiagonal;
00160         }
00161         if (isSymmetric())
00162         {
00163             return PackingModeSymmetric;
00164         }
00165         return PackingModeFull;
00166     }
00167 };
00168 
00169 
00170 template <unsigned Size>
00171 class UAVCAN_EXPORT StaticArrayBase
00172 {
00173 protected:
00174     typedef IntegerSpec<IntegerBitLen<Size>::Result, SignednessUnsigned, CastModeSaturate> RawEncodedSizeType;
00175 
00176 public:
00177     enum { SizeBitLen = 0 };
00178 
00179     typedef typename StorageType<IntegerSpec<IntegerBitLen<EnumMax<Size, 2>::Result>::Result,
00180                                              SignednessUnsigned, CastModeSaturate> >::Type SizeType;
00181 
00182     SizeType size()     const { return SizeType(Size); }
00183     SizeType capacity() const { return SizeType(Size); }
00184 
00185 protected:
00186     StaticArrayBase() { }
00187     ~StaticArrayBase() { }
00188 
00189     SizeType validateRange(SizeType pos) const
00190     {
00191         if (pos < SizeType(Size))
00192         {
00193             return pos;
00194         }
00195 #if UAVCAN_EXCEPTIONS
00196         throw std::out_of_range("uavcan::Array");
00197 #else
00198         UAVCAN_ASSERT(0);
00199         return SizeType(Size - 1U);  // Ha ha
00200 #endif
00201     }
00202 };
00203 
00204 
00205 template <unsigned MaxSize>
00206 class UAVCAN_EXPORT DynamicArrayBase
00207 {
00208 protected:
00209     typedef IntegerSpec<IntegerBitLen<MaxSize>::Result, SignednessUnsigned, CastModeSaturate> RawEncodedSizeType;
00210 public:
00211     typedef typename StorageType<IntegerSpec<IntegerBitLen<EnumMax<MaxSize, 2>::Result>::Result,
00212                                              SignednessUnsigned, CastModeSaturate> >::Type SizeType;
00213 
00214 private:
00215     SizeType size_;
00216 
00217 protected:
00218     DynamicArrayBase() : size_(0) { }
00219     ~DynamicArrayBase() { }
00220 
00221     SizeType validateRange(SizeType pos) const
00222     {
00223         if (pos < size_)
00224         {
00225             return pos;
00226         }
00227 #if UAVCAN_EXCEPTIONS
00228         throw std::out_of_range("uavcan::Array");
00229 #else
00230         UAVCAN_ASSERT(0);
00231         return SizeType((size_ == 0U) ? 0U : (size_ - 1U));
00232 #endif
00233     }
00234 
00235     void grow()
00236     {
00237         if (size_ >= MaxSize)
00238         {
00239             (void)validateRange(MaxSize);  // Will throw, UAVCAN_ASSERT() or do nothing
00240         }
00241         else
00242         {
00243             size_++;
00244         }
00245     }
00246 
00247     void shrink()
00248     {
00249         if (size_ > 0)
00250         {
00251             size_--;
00252         }
00253     }
00254 
00255 public:
00256     enum { SizeBitLen = RawEncodedSizeType::BitLen };
00257 
00258     SizeType size() const
00259     {
00260         UAVCAN_ASSERT(size_ ? ((size_ - 1u) <= (MaxSize - 1u)) : 1); // -Werror=type-limits
00261         return size_;
00262     }
00263 
00264     SizeType capacity() const { return MaxSize; }
00265 
00266     void clear() { size_ = 0; }
00267 };
00268 
00269 /**
00270  * Common functionality for both static and dynamic arrays.
00271  * Static arrays are of fixed size; methods that can alter the size (e.g. push_back() and such) will fail to compile.
00272  * Dynamic arrays contain a fixed-size buffer (it's size is enough to fit maximum number of elements) plus the
00273  * currently allocated number of elements.
00274  */
00275 template <typename T, ArrayMode ArrayMode, unsigned MaxSize>
00276 class UAVCAN_EXPORT ArrayImpl : public Select<ArrayMode == ArrayModeDynamic,
00277                                               DynamicArrayBase<MaxSize>, StaticArrayBase<MaxSize> >::Result
00278 {
00279     typedef ArrayImpl<T, ArrayMode, MaxSize> SelfType;
00280     typedef typename Select<ArrayMode == ArrayModeDynamic,
00281                             DynamicArrayBase<MaxSize>, StaticArrayBase<MaxSize> >::Result Base;
00282 
00283 public:
00284     enum
00285     {
00286         /// True if the array contents can be interpreted as a 8-bit string (ASCII or UTF8).
00287         IsStringLike = IsIntegerSpec<T>::Result && (T::MaxBitLen == 8 || T::MaxBitLen == 7) &&
00288                        (ArrayMode == ArrayModeDynamic)
00289     };
00290 
00291 private:
00292     typedef typename StorageType<T>::Type BufferType[MaxSize + (IsStringLike ? 1 : 0)];
00293     BufferType data_;
00294 
00295     template <typename U>
00296     typename EnableIf<sizeof(U(0) >= U())>::Type initialize(int)
00297     {
00298         if (ArrayMode != ArrayModeDynamic)
00299         {
00300             ::uavcan::fill(data_, data_ + MaxSize, U());
00301         }
00302     }
00303     template <typename> void initialize(...) { }
00304 
00305 protected:
00306     ~ArrayImpl() { }
00307 
00308 public:
00309     typedef typename StorageType<T>::Type ValueType;
00310     typedef typename Base::SizeType SizeType;
00311 
00312     using Base::size;
00313     using Base::capacity;
00314 
00315     ArrayImpl() { initialize<ValueType>(0); }
00316 
00317     /**
00318      * Returns zero-terminated string, same as std::string::c_str().
00319      * This method will compile only if the array can be interpreted as 8-bit string (ASCII of UTF8).
00320      */
00321     const char* c_str() const
00322     {
00323         StaticAssert<IsStringLike>::check();
00324         UAVCAN_ASSERT(size() < (MaxSize + 1));
00325         const_cast<BufferType&>(data_)[size()] = 0;  // Ad-hoc string termination
00326         return reinterpret_cast<const char*>(data_);
00327     }
00328 
00329     /**
00330      * Range-checking subscript.
00331      * If the index is out of range:
00332      * - if exceptions are enabled, std::out_of_range will be thrown.
00333      * - if exceptions are disabled and UAVCAN_ASSERT() is enabled, execution will be aborted.
00334      * - if exceptions are disabled and UAVCAN_ASSERT() is disabled, index will be constrained to
00335      *   the closest valid value.
00336      */
00337     ValueType& at(SizeType pos)             { return data_[Base::validateRange(pos)]; }
00338     const ValueType& at(SizeType pos) const { return data_[Base::validateRange(pos)]; }
00339 
00340     /**
00341      * Range-checking subscript. @ref at()
00342      */
00343     ValueType& operator[](SizeType pos)             { return at(pos); }
00344     const ValueType& operator[](SizeType pos) const { return at(pos); }
00345 
00346     /**
00347      * Standard container methods. Applicable to both dynamic and static arrays.
00348      */
00349     ValueType* begin()             { return data_; }
00350     const ValueType* begin() const { return data_; }
00351     ValueType* end()               { return data_ + Base::size(); }
00352     const ValueType* end()   const { return data_ + Base::size(); }
00353     ValueType& front()             { return at(0U); }
00354     const ValueType& front() const { return at(0U); }
00355     ValueType& back()              { return at((Base::size() == 0U) ? 0U : SizeType(Base::size() - 1U)); }
00356     const ValueType& back()  const { return at((Base::size() == 0U) ? 0U : SizeType(Base::size() - 1U)); }
00357 
00358     /**
00359      * Performs standard lexicographical compare of the elements.
00360      */
00361     template <typename R>
00362     bool operator<(const R& rhs) const
00363     {
00364         return ::uavcan::lexicographical_compare(begin(), end(), rhs.begin(), rhs.end());
00365     }
00366 
00367     /**
00368      * Aliases for compatibility with standard containers.
00369      */
00370     typedef ValueType* iterator;
00371     typedef const ValueType* const_iterator;
00372 };
00373 
00374 /**
00375  * Memory-efficient specialization for bit arrays (each element maps to a single bit rather than single byte).
00376  * This should be compatible with std::bitset.
00377  */
00378 template <unsigned MaxSize, ArrayMode ArrayMode, CastMode CastMode>
00379 class UAVCAN_EXPORT ArrayImpl<IntegerSpec<1, SignednessUnsigned, CastMode>, ArrayMode, MaxSize>
00380     : public BitSet<MaxSize>
00381     , public Select<ArrayMode == ArrayModeDynamic, DynamicArrayBase<MaxSize>, StaticArrayBase<MaxSize> >::Result
00382 {
00383     typedef typename Select<ArrayMode == ArrayModeDynamic,
00384                             DynamicArrayBase<MaxSize>, StaticArrayBase<MaxSize> >::Result ArrayBase;
00385 
00386 public:
00387     enum { IsStringLike = 0 };
00388 
00389     typedef typename BitSet<MaxSize>::Reference Reference;
00390     typedef typename ArrayBase::SizeType SizeType;
00391 
00392     using ArrayBase::size;
00393     using ArrayBase::capacity;
00394 
00395     /**
00396      * Range-checking subscript. Throws if enabled; UAVCAN_ASSERT() if enabled; else constraints the position.
00397      */
00398     Reference at(SizeType pos)  { return BitSet<MaxSize>::operator[](ArrayBase::validateRange(pos)); }
00399     bool at(SizeType pos) const { return BitSet<MaxSize>::operator[](ArrayBase::validateRange(pos)); }
00400 
00401     /**
00402      * @ref at()
00403      */
00404     Reference operator[](SizeType pos)  { return at(pos); }
00405     bool operator[](SizeType pos) const { return at(pos); }
00406 };
00407 
00408 /**
00409  * Zero length arrays are not allowed
00410  */
00411 template <typename T, ArrayMode ArrayMode> class ArrayImpl<T, ArrayMode, 0>;
00412 
00413 /**
00414  * Generic array implementation.
00415  * This class is compatible with most standard library functions operating on containers (e.g. std::sort(),
00416  * std::lexicographical_compare(), etc.).
00417  * No dynamic memory is used.
00418  * All functions that can modify the array or access elements are range checking. If the range error occurs:
00419  * - if exceptions are enabled, std::out_of_range will be thrown;
00420  * - if UAVCAN_ASSERT() is enabled, program will be terminated on UAVCAN_ASSERT(0);
00421  * - otherwise the index value will be constrained to the closest valid value.
00422  */
00423 template <typename T, ArrayMode ArrayMode, unsigned MaxSize_>
00424 class UAVCAN_EXPORT Array : public ArrayImpl<T, ArrayMode, MaxSize_>
00425 {
00426     typedef ArrayImpl<T, ArrayMode, MaxSize_>  Base;
00427     typedef Array<T, ArrayMode, MaxSize_> SelfType;
00428 
00429     static bool isOptimizedTailArray(TailArrayOptimizationMode tao_mode)
00430     {
00431         return (T::MinBitLen >= 8) && (tao_mode == TailArrayOptEnabled);
00432     }
00433 
00434     int encodeImpl(ScalarCodec& codec, const TailArrayOptimizationMode tao_mode, FalseType) const  /// Static
00435     {
00436         UAVCAN_ASSERT(size() > 0);
00437         for (SizeType i = 0; i < size(); i++)
00438         {
00439             const bool last_item = i == (size() - 1);
00440             const int res = RawValueType::encode(Base::at(i), codec, last_item ? tao_mode : TailArrayOptDisabled);
00441             if (res <= 0)
00442             {
00443                 return res;
00444             }
00445         }
00446         return 1;
00447     }
00448 
00449     int encodeImpl(ScalarCodec& codec, const TailArrayOptimizationMode tao_mode, TrueType) const   /// Dynamic
00450     {
00451         StaticAssert<IsDynamic>::check();
00452         const bool self_tao_enabled = isOptimizedTailArray(tao_mode);
00453         if (!self_tao_enabled)
00454         {
00455             const int res_sz =
00456                 Base::RawEncodedSizeType::encode(typename StorageType<typename Base::RawEncodedSizeType>::Type(size()),
00457                                                  codec, TailArrayOptDisabled);
00458             if (res_sz <= 0)
00459             {
00460                 return res_sz;
00461             }
00462         }
00463         if (size() == 0)
00464         {
00465             return 1;
00466         }
00467         return encodeImpl(codec, self_tao_enabled ? TailArrayOptDisabled : tao_mode, FalseType());
00468     }
00469 
00470     int decodeImpl(ScalarCodec& codec, const TailArrayOptimizationMode tao_mode, FalseType)  /// Static
00471     {
00472         UAVCAN_ASSERT(size() > 0);
00473         for (SizeType i = 0; i < size(); i++)
00474         {
00475             const bool last_item = i == (size() - 1);
00476             ValueType value = ValueType();                          // TODO: avoid extra copy
00477             const int res = RawValueType::decode(value, codec, last_item ? tao_mode : TailArrayOptDisabled);
00478             if (res <= 0)
00479             {
00480                 return res;
00481             }
00482             Base::at(i) = value;
00483         }
00484         return 1;
00485     }
00486 
00487 #if __GNUC__
00488 # pragma GCC diagnostic push
00489 # pragma GCC diagnostic ignored "-Wtype-limits"
00490 #endif
00491     int decodeImpl(ScalarCodec& codec, const TailArrayOptimizationMode tao_mode, TrueType)   /// Dynamic
00492     {
00493         StaticAssert<IsDynamic>::check();
00494         Base::clear();
00495         if (isOptimizedTailArray(tao_mode))
00496         {
00497             while (true)
00498             {
00499                 ValueType value = ValueType();
00500                 const int res = RawValueType::decode(value, codec, TailArrayOptDisabled);
00501                 if (res < 0)
00502                 {
00503                     return res;
00504                 }
00505                 if (res == 0)             // Success: End of stream reached (even if zero items were read)
00506                 {
00507                     return 1;
00508                 }
00509                 if (size() == MaxSize_)   // Error: Max array length reached, but the end of stream is not
00510                 {
00511                     return -ErrInvalidMarshalData;
00512                 }
00513                 push_back(value);
00514             }
00515         }
00516         else
00517         {
00518             typename StorageType<typename Base::RawEncodedSizeType>::Type sz = 0;
00519             const int res_sz = Base::RawEncodedSizeType::decode(sz, codec, TailArrayOptDisabled);
00520             if (res_sz <= 0)
00521             {
00522                 return res_sz;
00523             }
00524             // coverity[result_independent_of_operands]
00525             if (static_cast<unsigned>(sz) > MaxSize_)   // False 'type-limits' warning occurs here
00526             {
00527                 return -ErrInvalidMarshalData;
00528             }
00529             resize(sz);
00530             if (sz == 0)
00531             {
00532                 return 1;
00533             }
00534             return decodeImpl(codec, tao_mode, FalseType());
00535         }
00536         UAVCAN_ASSERT(0); // Unreachable
00537         return -ErrLogic;
00538     }
00539 #if __GNUC__
00540 # pragma GCC diagnostic pop
00541 #endif
00542 
00543     template <typename InputIter>
00544     void packSquareMatrixImpl(const InputIter src_row_major)
00545     {
00546         StaticAssert<IsDynamic>::check();
00547 
00548         this->clear();
00549 
00550         typedef SquareMatrixAnalyzer<InputIter, MaxSize> Analyzer;
00551         const Analyzer analyzer(src_row_major);
00552 
00553         switch (analyzer.detectOptimalPackingMode())
00554         {
00555         case Analyzer::PackingModeEmpty:
00556         {
00557             break; // Nothing to insert
00558         }
00559         case Analyzer::PackingModeScalar:
00560         {
00561             this->push_back(ValueType(*src_row_major));
00562             break;
00563         }
00564         case Analyzer::PackingModeDiagonal:
00565         {
00566             for (int i = 0; i < Analyzer::NumRowsCols; i++)
00567             {
00568                 this->push_back(ValueType(*analyzer.accessElementAtRowCol(i, i)));
00569             }
00570             break;
00571         }
00572         case Analyzer::PackingModeSymmetric:
00573         {
00574             for (int row = 0; row < Analyzer::NumRowsCols; row++)
00575             {
00576                 for (int col = row; col < Analyzer::NumRowsCols; col++)
00577                 {
00578                     this->push_back(ValueType(*analyzer.accessElementAtRowCol(row, col)));
00579                 }
00580             }
00581             UAVCAN_ASSERT(this->size() == Analyzer::NumElementsInTriangle);
00582             break;
00583         }
00584         case Analyzer::PackingModeFull:
00585         {
00586             InputIter it = src_row_major;
00587             for (unsigned index = 0; index < MaxSize; index++, it++)
00588             {
00589                 this->push_back(ValueType(*it));
00590             }
00591             break;
00592         }
00593         default:
00594         {
00595             UAVCAN_ASSERT(0);
00596             break;
00597         }
00598         }
00599     }
00600 
00601     template <typename ScalarType, typename OutputIter>
00602     void unpackSquareMatrixImpl(const OutputIter dst_row_major) const
00603     {
00604         StaticAssert<IsDynamic>::check();
00605         typedef SquareMatrixTraits<MaxSize> Traits;
00606 
00607         if (this->size() == Traits::NumRowsCols || this->size() == 1)   // Scalar or diagonal
00608         {
00609             OutputIter it = dst_row_major;
00610             for (unsigned index = 0; index < MaxSize; index++)
00611             {
00612                 if (Traits::isIndexOnDiagonal(index))
00613                 {
00614                     const SizeType source_index = SizeType((this->size() == 1) ? 0 : (index / Traits::NumRowsCols));
00615                     *it++ = ScalarType(this->at(source_index));
00616                 }
00617                 else
00618                 {
00619                     *it++ = ScalarType(0);
00620                 }
00621             }
00622         }
00623         else if (this->size() == Traits::NumElementsInTriangle)         // Symmetric
00624         {
00625             OutputIter it = dst_row_major;
00626             SizeType source_index = 0;
00627             for (int row = 0; row < Traits::NumRowsCols; row++)
00628             {
00629                 for (int col = 0; col < Traits::NumRowsCols; col++)
00630                 {
00631                     if (col >= row)     // Diagonal or upper-right triangle
00632                     {
00633                         *it++ = ScalarType(this->at(source_index));
00634                         source_index++;
00635                     }
00636                     else                // Lower-left triangle
00637                     {
00638                         // Transposing one element, argument swapping is intentional
00639                         // coverity[swapped_arguments]
00640                         *it++ = *(dst_row_major + Traits::computeElementIndexAtRowCol(col, row));
00641                     }
00642                 }
00643             }
00644             UAVCAN_ASSERT(source_index == Traits::NumElementsInTriangle);
00645         }
00646         else if (this->size() == MaxSize)                               // Full - no packing whatsoever
00647         {
00648             OutputIter it = dst_row_major;
00649             for (SizeType index = 0; index < MaxSize; index++)
00650             {
00651                 *it++ = ScalarType(this->at(index));
00652             }
00653         }
00654         else                                                            // Everything else
00655         {
00656             // coverity[suspicious_sizeof : FALSE]
00657             ::uavcan::fill_n(dst_row_major, MaxSize, ScalarType(0));
00658         }
00659     }
00660 
00661 public:
00662     typedef T RawValueType;                           ///< This may be not the same as the element type.
00663     typedef typename StorageType<T>::Type ValueType;  ///< This is the actual stored element type.
00664     typedef typename Base::SizeType SizeType;         ///< Minimal width size type.
00665 
00666     using Base::size;
00667     using Base::capacity;
00668 
00669     enum { IsDynamic = ArrayMode == ArrayModeDynamic };
00670     enum { MaxSize = MaxSize_ };
00671     enum
00672     {
00673         MinBitLen = (IsDynamic == 0)
00674                     ? (static_cast<unsigned>(RawValueType::MinBitLen) * static_cast<unsigned>(MaxSize))
00675                     : 0
00676     };
00677     enum
00678     {
00679         MaxBitLen = static_cast<unsigned>(Base::SizeBitLen) +
00680                     static_cast<unsigned>(RawValueType::MaxBitLen) * static_cast<unsigned>(MaxSize)
00681     };
00682 
00683     /**
00684      * Default constructor zero-initializes the storage even if it consists of primitive types.
00685      */
00686     Array() { }
00687 
00688     /**
00689      * String constructor - only for string-like arrays.
00690      * Refer to @ref operator+=(const char*) for details.
00691      */
00692     Array(const char* str)      // Implicit
00693     {
00694         operator+=(str);
00695     }
00696 
00697     static int encode(const SelfType& array, ScalarCodec& codec, const TailArrayOptimizationMode tao_mode)
00698     {
00699         return array.encodeImpl(codec, tao_mode, BooleanType<IsDynamic>());
00700     }
00701 
00702     static int decode(SelfType& array, ScalarCodec& codec, const TailArrayOptimizationMode tao_mode)
00703     {
00704         return array.decodeImpl(codec, tao_mode, BooleanType<IsDynamic>());
00705     }
00706 
00707     static void extendDataTypeSignature(DataTypeSignature& signature)
00708     {
00709         RawValueType::extendDataTypeSignature(signature);
00710     }
00711 
00712     bool empty() const { return size() == 0; }
00713 
00714     /**
00715      * Only for dynamic arrays. Range checking.
00716      */
00717     void pop_back() { Base::shrink(); }
00718     void push_back(const ValueType& value)
00719     {
00720         Base::grow();
00721         Base::at(SizeType(size() - 1)) = value;
00722     }
00723 
00724     /**
00725      * Only for dynamic arrays. Range checking.
00726      */
00727     void resize(SizeType new_size, const ValueType& filler)
00728     {
00729         if (new_size > size())
00730         {
00731             SizeType cnt = SizeType(new_size - size());
00732             while (cnt-- > 0)
00733             {
00734                 push_back(filler);
00735             }
00736         }
00737         else if (new_size < size())
00738         {
00739             SizeType cnt = SizeType(size() - new_size);
00740             while (cnt-- > 0)
00741             {
00742                 pop_back();
00743             }
00744         }
00745         else
00746         {
00747             ; // Exact size
00748         }
00749     }
00750 
00751     /**
00752      * Only for dynamic arrays. Range checking.
00753      */
00754     void resize(SizeType new_size)
00755     {
00756         resize(new_size, ValueType());
00757     }
00758 
00759     /**
00760      * This operator accepts any container with size() and [].
00761      * Members must be comparable via operator ==.
00762      */
00763     template <typename R>
00764     typename EnableIf<sizeof((reinterpret_cast<const R*>(0))->size()) &&
00765                       sizeof((*(reinterpret_cast<const R*>(0)))[0]), bool>::Type
00766     operator==(const R& rhs) const
00767     {
00768         if (size() != rhs.size())
00769         {
00770             return false;
00771         }
00772         for (SizeType i = 0; i < size(); i++)  // Bitset does not have iterators
00773         {
00774             if (!(Base::at(i) == rhs[i]))
00775             {
00776                 return false;
00777             }
00778         }
00779         return true;
00780     }
00781 
00782     /**
00783      * This method compares two arrays using @ref areClose(), which ensures proper comparison of
00784      * floating point values, or DSDL data structures which contain floating point fields at any depth.
00785      * Please refer to the documentation of @ref areClose() to learn more about how it works and how to
00786      * define custom fuzzy comparison behavior.
00787      * Any container with size() and [] is acceptable.
00788      */
00789     template <typename R>
00790     typename EnableIf<sizeof((reinterpret_cast<const R*>(0))->size()) &&
00791                       sizeof((*(reinterpret_cast<const R*>(0)))[0]), bool>::Type
00792     isClose(const R& rhs) const
00793     {
00794         if (size() != rhs.size())
00795         {
00796             return false;
00797         }
00798         for (SizeType i = 0; i < size(); i++)  // Bitset does not have iterators
00799         {
00800             if (!areClose(Base::at(i), rhs[i]))
00801             {
00802                 return false;
00803             }
00804         }
00805         return true;
00806     }
00807 
00808     /**
00809      * This operator can only be used with string-like arrays; otherwise it will fail to compile.
00810      * @ref c_str()
00811      */
00812     bool operator==(const char* chr) const
00813     {
00814         if (chr == UAVCAN_NULLPTR)
00815         {
00816             return false;
00817         }
00818         return std::strncmp(Base::c_str(), chr, MaxSize) == 0;
00819     }
00820 
00821     /**
00822      * @ref operator==()
00823      */
00824     template <typename R> bool operator!=(const R& rhs) const { return !operator==(rhs); }
00825 
00826     /**
00827      * This operator can only be used with string-like arrays; otherwise it will fail to compile.
00828      * @ref c_str()
00829      */
00830     SelfType& operator=(const char* chr)
00831     {
00832         StaticAssert<Base::IsStringLike>::check();
00833         StaticAssert<IsDynamic>::check();
00834         Base::clear();
00835         if (chr == UAVCAN_NULLPTR)
00836         {
00837             handleFatalError("Array::operator=(const char*)");
00838         }
00839         while (*chr)
00840         {
00841             push_back(ValueType(*chr++));  // Value type is likely to be unsigned char, so conversion may be required.
00842         }
00843         return *this;
00844     }
00845 
00846     /**
00847      * This operator can only be used with string-like arrays; otherwise it will fail to compile.
00848      * @ref c_str()
00849      */
00850     SelfType& operator+=(const char* chr)
00851     {
00852         StaticAssert<Base::IsStringLike>::check();
00853         StaticAssert<IsDynamic>::check();
00854         if (chr == UAVCAN_NULLPTR)
00855         {
00856             handleFatalError("Array::operator+=(const char*)");
00857         }
00858         while (*chr)
00859         {
00860             push_back(ValueType(*chr++));
00861         }
00862         return *this;
00863     }
00864 
00865     /**
00866      * Appends another Array<> with the same element type. Mode and max size can be different.
00867      */
00868     template <uavcan::ArrayMode RhsArrayMode, unsigned RhsMaxSize>
00869     SelfType& operator+=(const Array<T, RhsArrayMode, RhsMaxSize>& rhs)
00870     {
00871         typedef Array<T, RhsArrayMode, RhsMaxSize> Rhs;
00872         StaticAssert<IsDynamic>::check();
00873         for (typename Rhs::SizeType i = 0; i < rhs.size(); i++)
00874         {
00875             push_back(rhs[i]);
00876         }
00877         return *this;
00878     }
00879 
00880     /**
00881      * Formatting appender.
00882      * This method doesn't raise an overflow error; instead it silently truncates the data to fit the array capacity.
00883      * Works only with string-like arrays, otherwise fails to compile.
00884      * @param format    Format string for std::snprintf(), e.g. "%08x", "%f"
00885      * @param value     Arbitrary value of a primitive type (should fail to compile if there's a non-primitive type)
00886      */
00887     template <typename A>
00888     void appendFormatted(const char* const format, const A value)
00889     {
00890         StaticAssert<Base::IsStringLike>::check();
00891         StaticAssert<IsDynamic>::check();
00892 
00893         StaticAssert<sizeof(A() >= A(0))>::check();              // This check allows to weed out most compound types
00894         StaticAssert<(sizeof(A) <= sizeof(long double)) ||
00895                      (sizeof(A) <= sizeof(long long))>::check(); // Another stupid check to catch non-primitive types
00896 
00897         if (!format)
00898         {
00899             UAVCAN_ASSERT(0);
00900             return;
00901         }
00902         // Add some hardcore runtime checks for the format string correctness?
00903 
00904         ValueType* const ptr = Base::end();
00905         UAVCAN_ASSERT(capacity() >= size());
00906         const SizeType max_size = SizeType(capacity() - size());
00907 
00908         // We have one extra byte for the null terminator, hence +1
00909         const int ret = snprintf(reinterpret_cast<char*>(ptr), SizeType(max_size + 1U), format, value);
00910 
00911         for (int i = 0; i < min(ret, int(max_size)); i++)
00912         {
00913             Base::grow();
00914         }
00915         if (ret < 0)
00916         {
00917             UAVCAN_ASSERT(0);    // Likely an invalid format string
00918             (*this) += format;   // So we print it as is in release builds
00919         }
00920     }
00921 
00922     /**
00923      * Converts the string to upper/lower case in place, assuming that encoding is ASCII.
00924      * These methods can only be used with string-like arrays; otherwise compilation will fail.
00925      */
00926     void convertToUpperCaseASCII()
00927     {
00928         StaticAssert<Base::IsStringLike>::check();
00929 
00930         for (SizeType i = 0; i < size(); i++)
00931         {
00932             const int x = Base::at(i);
00933             if ((x <= 'z') && (x >= 'a'))
00934             {
00935                 Base::at(i) = static_cast<ValueType>(x + ('Z' - 'z'));
00936             }
00937         }
00938     }
00939 
00940     void convertToLowerCaseASCII()
00941     {
00942         StaticAssert<Base::IsStringLike>::check();
00943 
00944         for (SizeType i = 0; i < size(); i++)
00945         {
00946             const int x = Base::at(i);
00947             if ((x <= 'Z') && (x >= 'A'))
00948             {
00949                 Base::at(i) = static_cast<ValueType>(x - ('Z' - 'z'));
00950             }
00951         }
00952     }
00953 
00954     /**
00955      * Fills this array as a packed square matrix from a static array.
00956      * Please refer to the specification to learn more about matrix packing.
00957      * Note that matrix packing code uses @ref areClose() for comparison.
00958      */
00959     template <typename ScalarType>
00960     void packSquareMatrix(const ScalarType (&src_row_major)[MaxSize])
00961     {
00962         packSquareMatrixImpl<const ScalarType*>(src_row_major);
00963     }
00964 
00965     /**
00966      * Fills this array as a packed square matrix in place.
00967      * Please refer to the specification to learn more about matrix packing.
00968      * Note that matrix packing code uses @ref areClose() for comparison.
00969      */
00970     void packSquareMatrix()
00971     {
00972         if (this->size() == MaxSize)
00973         {
00974             ValueType matrix[MaxSize];
00975             for (SizeType i = 0; i < MaxSize; i++)
00976             {
00977                 matrix[i] = this->at(i);
00978             }
00979             packSquareMatrix(matrix);
00980         }
00981         else if (this->size() == 0)
00982         {
00983             ; // Nothing to do - leave the matrix empty
00984         }
00985         else
00986         {
00987 #if UAVCAN_EXCEPTIONS
00988             throw std::out_of_range("uavcan::Array::packSquareMatrix()");
00989 #else
00990             UAVCAN_ASSERT(0);
00991             this->clear();
00992 #endif
00993         }
00994 
00995     }
00996 
00997     /**
00998      * Fills this array as a packed square matrix from any container that has the following public entities:
00999      *  - method begin()
01000      *  - method size()
01001      *  - only for C++03: type value_type
01002      * Please refer to the specification to learn more about matrix packing.
01003      * Note that matrix packing code uses @ref areClose() for comparison.
01004      */
01005     template <typename R>
01006     typename EnableIf<sizeof((reinterpret_cast<const R*>(0))->begin()) &&
01007                       sizeof((reinterpret_cast<const R*>(0))->size())>::Type
01008     packSquareMatrix(const R& src_row_major)
01009     {
01010         if (src_row_major.size() == MaxSize)
01011         {
01012             packSquareMatrixImpl(src_row_major.begin());
01013         }
01014         else if (src_row_major.size() == 0)
01015         {
01016             this->clear();
01017         }
01018         else
01019         {
01020 #if UAVCAN_EXCEPTIONS
01021             throw std::out_of_range("uavcan::Array::packSquareMatrix()");
01022 #else
01023             UAVCAN_ASSERT(0);
01024             this->clear();
01025 #endif
01026         }
01027     }
01028 
01029     /**
01030      * Reconstructs full matrix, result will be saved into a static array.
01031      * Please refer to the specification to learn more about matrix packing.
01032      */
01033     template <typename ScalarType>
01034     void unpackSquareMatrix(ScalarType (&dst_row_major)[MaxSize]) const
01035     {
01036         unpackSquareMatrixImpl<ScalarType, ScalarType*>(dst_row_major);
01037     }
01038 
01039     /**
01040      * Reconstructs full matrix in place.
01041      * Please refer to the specification to learn more about matrix packing.
01042      */
01043     void unpackSquareMatrix()
01044     {
01045         ValueType matrix[MaxSize];
01046         unpackSquareMatrix(matrix);
01047 
01048         this->clear();
01049         for (unsigned i = 0; i < MaxSize; i++)
01050         {
01051             this->push_back(matrix[i]);
01052         }
01053     }
01054 
01055     /**
01056      * Reconstructs full matrix, result will be saved into container that has the following public entities:
01057      *  - method begin()
01058      *  - method size()
01059      *  - only for C++03: type value_type
01060      * Please refer to the specification to learn more about matrix packing.
01061      */
01062     template <typename R>
01063     typename EnableIf<sizeof((reinterpret_cast<const R*>(0))->begin()) &&
01064                       sizeof((reinterpret_cast<const R*>(0))->size())>::Type
01065     unpackSquareMatrix(R& dst_row_major) const
01066     {
01067         if (dst_row_major.size() == MaxSize)
01068         {
01069 #if UAVCAN_CPP_VERSION > UAVCAN_CPP03
01070             typedef typename RemoveReference<decltype(*dst_row_major.begin())>::Type RhsValueType;
01071             unpackSquareMatrixImpl<RhsValueType>(dst_row_major.begin());
01072 #else
01073             unpackSquareMatrixImpl<typename R::value_type>(dst_row_major.begin());
01074 #endif
01075         }
01076         else
01077         {
01078 #if UAVCAN_EXCEPTIONS
01079             throw std::out_of_range("uavcan::Array::unpackSquareMatrix()");
01080 #else
01081             UAVCAN_ASSERT(0);
01082 #endif
01083         }
01084     }
01085 
01086     /**
01087      * Aliases for compatibility with standard containers.
01088      */
01089     typedef ValueType value_type;
01090     typedef SizeType size_type;
01091 };
01092 
01093 /**
01094  * These operators will only be enabled if rhs and lhs are different types. This precondition allows to work-around
01095  * the ambiguity arising from the scope containing two definitions: one here and the others in Array<>.
01096  * Refer to https://github.com/UAVCAN/libuavcan/issues/55 for more info.
01097  */
01098 template <typename R, typename T, ArrayMode ArrayMode, unsigned MaxSize>
01099 UAVCAN_EXPORT
01100 inline typename EnableIf<!IsSameType<R, Array<T, ArrayMode, MaxSize> >::Result, bool>::Type
01101 operator==(const R& rhs, const Array<T, ArrayMode, MaxSize>& lhs)
01102 {
01103     return lhs.operator==(rhs);
01104 }
01105 
01106 template <typename R, typename T, ArrayMode ArrayMode, unsigned MaxSize>
01107 UAVCAN_EXPORT
01108 inline typename EnableIf<!IsSameType<R, Array<T, ArrayMode, MaxSize> >::Result, bool>::Type
01109 operator!=(const R& rhs, const Array<T, ArrayMode, MaxSize>& lhs)
01110 {
01111     return lhs.operator!=(rhs);
01112 }
01113 
01114 /**
01115  * Shortcut for string-like array type instantiation.
01116  * The proper way of doing this is actually "template<> using ... = ...", but this feature is not available in
01117  * older C++ revisions which the library has to support.
01118  */
01119 template <unsigned MaxSize>
01120 class MakeString
01121 {
01122     MakeString();       // This class is not instantiatable.
01123 public:
01124     typedef Array<IntegerSpec<8, SignednessUnsigned, CastModeSaturate>, ArrayModeDynamic, MaxSize> Type;
01125 };
01126 
01127 /**
01128  * YAML streamer specification for any Array<>
01129  */
01130 template <typename T, ArrayMode ArrayMode, unsigned MaxSize>
01131 class UAVCAN_EXPORT YamlStreamer<Array<T, ArrayMode, MaxSize> >
01132 {
01133     typedef Array<T, ArrayMode, MaxSize> ArrayType;
01134 
01135     static bool isNiceCharacter(int c)
01136     {
01137         if (c >= 32 && c <= 126)
01138         {
01139             return true;
01140         }
01141         static const char Good[] = {'\n', '\r', '\t'};
01142         for (unsigned i = 0; i < sizeof(Good) / sizeof(Good[0]); i++)
01143         {
01144             if (Good[i] == c)
01145             {
01146                 return true;
01147             }
01148         }
01149         return false;
01150     }
01151 
01152     template <typename Stream>
01153     static void streamPrimitives(Stream& s, const ArrayType& array)
01154     {
01155         s << '[';
01156         for (typename ArrayType::SizeType i = 0; i < array.size(); i++)
01157         {
01158             YamlStreamer<T>::stream(s, array.at(i), 0);
01159             if ((i + 1) < array.size())
01160             {
01161                 s << ", ";
01162             }
01163         }
01164         s << ']';
01165     }
01166 
01167     template <typename Stream>
01168     static void streamCharacters(Stream& s, const ArrayType& array)
01169     {
01170         s << '"';
01171         for (typename ArrayType::SizeType i = 0; i < array.size(); i++)
01172         {
01173             const int c = array.at(i);
01174             if (c < 32 || c > 126)
01175             {
01176                 char nibbles[2] = {char((c >> 4) & 0xF), char(c & 0xF)};
01177                 for (int k = 0; k < 2; k++)
01178                 {
01179                     nibbles[k] = char(nibbles[k] + '0');
01180                     if (nibbles[k] > '9')
01181                     {
01182                         nibbles[k] = char(nibbles[k] + 'A' - '9' - 1);
01183                     }
01184                 }
01185                 s << "\\x" << nibbles[0] << nibbles[1];
01186             }
01187             else
01188             {
01189                 if (c == '"' || c == '\\')      // YAML requires to escape these two
01190                 {
01191                     s << '\\';
01192                 }
01193                 s << char(c);
01194             }
01195         }
01196         s << '"';
01197     }
01198 
01199     struct SelectorStringLike { };
01200     struct SelectorPrimitives { };
01201     struct SelectorObjects { };
01202 
01203     template <typename Stream>
01204     static void genericStreamImpl(Stream& s, const ArrayType& array, int, SelectorStringLike)
01205     {
01206         bool printable_only = true;
01207         for (typename ArrayType::SizeType i = 0; i < array.size(); i++)
01208         {
01209             if (!isNiceCharacter(array[i]))
01210             {
01211                 printable_only = false;
01212                 break;
01213             }
01214         }
01215         if (printable_only)
01216         {
01217             streamCharacters(s, array);
01218         }
01219         else
01220         {
01221             streamPrimitives(s, array);
01222             s << " # ";
01223             streamCharacters(s, array);
01224         }
01225     }
01226 
01227     template <typename Stream>
01228     static void genericStreamImpl(Stream& s, const ArrayType& array, int, SelectorPrimitives)
01229     {
01230         streamPrimitives(s, array);
01231     }
01232 
01233     template <typename Stream>
01234     static void genericStreamImpl(Stream& s, const ArrayType& array, int level, SelectorObjects)
01235     {
01236         if (array.empty())
01237         {
01238             s << "[]";
01239             return;
01240         }
01241         for (typename ArrayType::SizeType i = 0; i < array.size(); i++)
01242         {
01243             s << '\n';
01244             for (int pos = 0; pos < level; pos++)
01245             {
01246                 s << "  ";
01247             }
01248             s << "- ";
01249             YamlStreamer<T>::stream(s, array.at(i), level + 1);
01250         }
01251     }
01252 
01253 public:
01254     /**
01255      * Prints Array<> into the stream in YAML format.
01256      */
01257     template <typename Stream>
01258     static void stream(Stream& s, const ArrayType& array, int level)
01259     {
01260         typedef typename Select<ArrayType::IsStringLike, SelectorStringLike,
01261                                 typename Select<IsPrimitiveType<typename ArrayType::RawValueType>::Result,
01262                                                 SelectorPrimitives,
01263                                                 SelectorObjects>::Result >::Result Type;
01264         genericStreamImpl(s, array, level, Type());
01265     }
01266 };
01267 
01268 }
01269 
01270 #endif // UAVCAN_MARSHAL_ARRAY_HPP_INCLUDED